1 /*
2 * Copyright 2015 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8 #include "src/gpu/ganesh/effects/GrBlendFragmentProcessor.h"
9
10 #include "src/core/SkBlendModePriv.h"
11 #include "src/gpu/Blend.h"
12 #include "src/gpu/KeyBuilder.h"
13 #include "src/gpu/ganesh/GrFragmentProcessor.h"
14 #include "src/gpu/ganesh/GrProcessorUnitTest.h"
15 #include "src/gpu/ganesh/SkGr.h"
16 #include "src/gpu/ganesh/glsl/GrGLSLBlend.h"
17 #include "src/gpu/ganesh/glsl/GrGLSLFragmentShaderBuilder.h"
18
19 // Some of the CPU implementations of blend modes differ from the GPU enough that
20 // we can't use the CPU implementation to implement constantOutputForConstantInput.
does_cpu_blend_impl_match_gpu(SkBlendMode mode)21 static inline bool does_cpu_blend_impl_match_gpu(SkBlendMode mode) {
22 // The non-separable modes differ too much. So does SoftLight. ColorBurn differs too much on our
23 // test iOS device, but we just disable it across the board since it might differ on untested
24 // GPUs.
25 return mode <= SkBlendMode::kLastSeparableMode && mode != SkBlendMode::kSoftLight &&
26 mode != SkBlendMode::kColorBurn;
27 }
28
29 //////////////////////////////////////////////////////////////////////////////
30
31 class BlendFragmentProcessor : public GrFragmentProcessor {
32 public:
Make(std::unique_ptr<GrFragmentProcessor> src,std::unique_ptr<GrFragmentProcessor> dst,SkBlendMode mode,bool shareBlendLogic)33 static std::unique_ptr<GrFragmentProcessor> Make(std::unique_ptr<GrFragmentProcessor> src,
34 std::unique_ptr<GrFragmentProcessor> dst,
35 SkBlendMode mode,
36 bool shareBlendLogic) {
37 return std::unique_ptr<GrFragmentProcessor>(
38 new BlendFragmentProcessor(std::move(src), std::move(dst), mode, shareBlendLogic));
39 }
40
name() const41 const char* name() const override { return "Blend"; }
42
43 std::unique_ptr<GrFragmentProcessor> clone() const override;
44
45 private:
BlendFragmentProcessor(std::unique_ptr<GrFragmentProcessor> src,std::unique_ptr<GrFragmentProcessor> dst,SkBlendMode mode,bool shareBlendLogic)46 BlendFragmentProcessor(std::unique_ptr<GrFragmentProcessor> src,
47 std::unique_ptr<GrFragmentProcessor> dst,
48 SkBlendMode mode,
49 bool shareBlendLogic)
50 : INHERITED(kBlendFragmentProcessor_ClassID, OptFlags(src.get(), dst.get(), mode))
51 , fMode(mode)
52 , fShareBlendLogic(shareBlendLogic) {
53 this->setIsBlendFunction();
54 this->registerChild(std::move(src));
55 this->registerChild(std::move(dst));
56 }
57
BlendFragmentProcessor(const BlendFragmentProcessor & that)58 BlendFragmentProcessor(const BlendFragmentProcessor& that)
59 : INHERITED(that)
60 , fMode(that.fMode)
61 , fShareBlendLogic(that.fShareBlendLogic) {}
62
63 #if GR_TEST_UTILS
onDumpInfo() const64 SkString onDumpInfo() const override {
65 return SkStringPrintf("(fMode=%s)", SkBlendMode_Name(fMode));
66 }
67 #endif
68
OptFlags(const GrFragmentProcessor * src,const GrFragmentProcessor * dst,SkBlendMode mode)69 static OptimizationFlags OptFlags(const GrFragmentProcessor* src,
70 const GrFragmentProcessor* dst, SkBlendMode mode) {
71 OptimizationFlags flags;
72 switch (mode) {
73 case SkBlendMode::kClear:
74 flags = kNone_OptimizationFlags;
75 break;
76
77 // Just propagates src, and its flags:
78 case SkBlendMode::kSrc:
79 flags = ProcessorOptimizationFlags(src) &
80 ~kConstantOutputForConstantInput_OptimizationFlag;
81 break;
82
83 // Just propagates dst, and its flags:
84 case SkBlendMode::kDst:
85 flags = ProcessorOptimizationFlags(dst) &
86 ~kConstantOutputForConstantInput_OptimizationFlag;
87 break;
88
89 // Produces opaque if both src and dst are opaque. These also will modulate the child's
90 // output by either the input color or alpha. However, if the child is not compatible
91 // with the coverage as alpha then it may produce a color that is not valid premul.
92 case SkBlendMode::kSrcIn:
93 case SkBlendMode::kDstIn:
94 case SkBlendMode::kModulate:
95 if (src && dst) {
96 flags = ProcessorOptimizationFlags(src) & ProcessorOptimizationFlags(dst) &
97 kPreservesOpaqueInput_OptimizationFlag;
98 } else if (src) {
99 flags = ProcessorOptimizationFlags(src) &
100 ~kConstantOutputForConstantInput_OptimizationFlag;
101 } else if (dst) {
102 flags = ProcessorOptimizationFlags(dst) &
103 ~kConstantOutputForConstantInput_OptimizationFlag;
104 } else {
105 flags = kNone_OptimizationFlags;
106 }
107 break;
108
109 // Produces zero when both are opaque, indeterminate if one is opaque.
110 case SkBlendMode::kSrcOut:
111 case SkBlendMode::kDstOut:
112 case SkBlendMode::kXor:
113 flags = kNone_OptimizationFlags;
114 break;
115
116 // Is opaque if the dst is opaque.
117 case SkBlendMode::kSrcATop:
118 flags = ProcessorOptimizationFlags(dst) & kPreservesOpaqueInput_OptimizationFlag;
119 break;
120
121 // DstATop is the converse of kSrcATop. Screen is also opaque if the src is a opaque.
122 case SkBlendMode::kDstATop:
123 case SkBlendMode::kScreen:
124 flags = ProcessorOptimizationFlags(src) & kPreservesOpaqueInput_OptimizationFlag;
125 break;
126
127 // These modes are all opaque if either src or dst is opaque. All the advanced modes
128 // compute alpha as src-over.
129 case SkBlendMode::kSrcOver:
130 case SkBlendMode::kDstOver:
131 case SkBlendMode::kPlus:
132 case SkBlendMode::kOverlay:
133 case SkBlendMode::kDarken:
134 case SkBlendMode::kLighten:
135 case SkBlendMode::kColorDodge:
136 case SkBlendMode::kColorBurn:
137 case SkBlendMode::kHardLight:
138 case SkBlendMode::kSoftLight:
139 case SkBlendMode::kDifference:
140 case SkBlendMode::kExclusion:
141 case SkBlendMode::kMultiply:
142 case SkBlendMode::kHue:
143 case SkBlendMode::kSaturation:
144 case SkBlendMode::kColor:
145 case SkBlendMode::kLuminosity:
146 flags = (ProcessorOptimizationFlags(src) | ProcessorOptimizationFlags(dst)) &
147 kPreservesOpaqueInput_OptimizationFlag;
148 break;
149 }
150 if (does_cpu_blend_impl_match_gpu(mode) &&
151 (!src || src->hasConstantOutputForConstantInput()) &&
152 (!dst || dst->hasConstantOutputForConstantInput())) {
153 flags |= kConstantOutputForConstantInput_OptimizationFlag;
154 }
155 return flags;
156 }
157
onAddToKey(const GrShaderCaps & caps,skgpu::KeyBuilder * b) const158 void onAddToKey(const GrShaderCaps& caps, skgpu::KeyBuilder* b) const override {
159 if (fShareBlendLogic) {
160 b->add32(GrGLSLBlend::BlendKey(fMode));
161 } else {
162 b->add32((int)fMode);
163 }
164 }
165
onIsEqual(const GrFragmentProcessor & other) const166 bool onIsEqual(const GrFragmentProcessor& other) const override {
167 const BlendFragmentProcessor& cs = other.cast<BlendFragmentProcessor>();
168 return fMode == cs.fMode;
169 }
170
constantOutputForConstantInput(const SkPMColor4f & input) const171 SkPMColor4f constantOutputForConstantInput(const SkPMColor4f& input) const override {
172 const auto* src = this->childProcessor(0);
173 const auto* dst = this->childProcessor(1);
174
175 SkPMColor4f srcColor = ConstantOutputForConstantInput(src, input);
176 SkPMColor4f dstColor = ConstantOutputForConstantInput(dst, input);
177
178 return SkBlendMode_Apply(fMode, srcColor, dstColor);
179 }
180
181 std::unique_ptr<ProgramImpl> onMakeProgramImpl() const override;
182
183 SkBlendMode fMode;
184 bool fShareBlendLogic;
185
186 GR_DECLARE_FRAGMENT_PROCESSOR_TEST
187
188 using INHERITED = GrFragmentProcessor;
189 };
190
191 /////////////////////////////////////////////////////////////////////
192
193
GR_DEFINE_FRAGMENT_PROCESSOR_TEST(BlendFragmentProcessor)194 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(BlendFragmentProcessor)
195
196 #if GR_TEST_UTILS
197 std::unique_ptr<GrFragmentProcessor> BlendFragmentProcessor::TestCreate(GrProcessorTestData* d) {
198 // Create one or two random fragment processors.
199 std::unique_ptr<GrFragmentProcessor> src(GrProcessorUnitTest::MakeOptionalChildFP(d));
200 std::unique_ptr<GrFragmentProcessor> dst(GrProcessorUnitTest::MakeChildFP(d));
201 if (d->fRandom->nextBool()) {
202 std::swap(src, dst);
203 }
204 bool shareLogic = d->fRandom->nextBool();
205
206 SkBlendMode mode =
207 static_cast<SkBlendMode>(d->fRandom->nextRangeU(0, (int)SkBlendMode::kLastMode));
208 return std::unique_ptr<GrFragmentProcessor>(
209 new BlendFragmentProcessor(std::move(src), std::move(dst), mode, shareLogic));
210 }
211 #endif
212
clone() const213 std::unique_ptr<GrFragmentProcessor> BlendFragmentProcessor::clone() const {
214 return std::unique_ptr<GrFragmentProcessor>(new BlendFragmentProcessor(*this));
215 }
216
onMakeProgramImpl() const217 std::unique_ptr<GrFragmentProcessor::ProgramImpl> BlendFragmentProcessor::onMakeProgramImpl()
218 const {
219 class Impl : public ProgramImpl {
220 public:
221 void emitCode(EmitArgs& args) override {
222 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
223 const BlendFragmentProcessor& bfp = args.fFp.cast<BlendFragmentProcessor>();
224 const SkBlendMode mode = bfp.fMode;
225
226 // Invoke src/dst with our input color (or substitute input color if no child FP)
227 SkString srcColor = this->invokeChild(0, args);
228 SkString dstColor = this->invokeChild(1, args);
229
230 if (bfp.fShareBlendLogic) {
231 // Use a blend expression that may rely on uniforms.
232 std::string blendExpr = GrGLSLBlend::BlendExpression(&args.fFp,
233 args.fUniformHandler,
234 &fBlendUniform,
235 srcColor.c_str(),
236 dstColor.c_str(),
237 mode);
238 fragBuilder->codeAppendf("return %s;", blendExpr.c_str());
239 } else {
240 // Blend src and dst colors together using a hardwired built-in blend function.
241 fragBuilder->codeAppendf("return %s(%s, %s);",
242 skgpu::BlendFuncName(mode),
243 srcColor.c_str(),
244 dstColor.c_str());
245 }
246 }
247
248 void onSetData(const GrGLSLProgramDataManager& pdman,
249 const GrFragmentProcessor& fp) override {
250 const BlendFragmentProcessor& bfp = fp.cast<BlendFragmentProcessor>();
251 if (bfp.fShareBlendLogic) {
252 GrGLSLBlend::SetBlendModeUniformData(pdman, fBlendUniform, bfp.fMode);
253 }
254 }
255
256 UniformHandle fBlendUniform;
257 };
258
259 return std::make_unique<Impl>();
260 }
261
262 //////////////////////////////////////////////////////////////////////////////
263
Make(std::unique_ptr<GrFragmentProcessor> src,std::unique_ptr<GrFragmentProcessor> dst,SkBlendMode mode,bool shareBlendLogic)264 std::unique_ptr<GrFragmentProcessor> GrBlendFragmentProcessor::Make(
265 std::unique_ptr<GrFragmentProcessor> src,
266 std::unique_ptr<GrFragmentProcessor> dst,
267 SkBlendMode mode,
268 bool shareBlendLogic) {
269 // These modes simplify dramatically in the shader, but only if we bypass the shared logic:
270 if (mode == SkBlendMode::kClear || mode == SkBlendMode::kSrc || mode == SkBlendMode::kDst) {
271 shareBlendLogic = false;
272 }
273
274 return BlendFragmentProcessor::Make(std::move(src), std::move(dst), mode, shareBlendLogic);
275 }
276