• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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