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