xref: /aosp_15_r20/external/skia/src/gpu/ganesh/effects/GrBlendFragmentProcessor.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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