xref: /aosp_15_r20/external/skia/src/gpu/ganesh/effects/GrCustomXfermode.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/GrCustomXfermode.h"
9 
10 #include "include/core/SkBlendMode.h"
11 #include "include/core/SkRefCnt.h"
12 #include "include/private/base/SkAssert.h"
13 #include "src/base/SkRandom.h"
14 #include "src/gpu/Blend.h"
15 #include "src/gpu/KeyBuilder.h"
16 #include "src/gpu/ganesh/GrCaps.h"
17 #include "src/gpu/ganesh/GrProcessorAnalysis.h"
18 #include "src/gpu/ganesh/GrProcessorUnitTest.h"
19 #include "src/gpu/ganesh/GrShaderCaps.h"
20 #include "src/gpu/ganesh/GrXferProcessor.h"
21 #include "src/gpu/ganesh/glsl/GrGLSLBlend.h"
22 #include "src/gpu/ganesh/glsl/GrGLSLFragmentShaderBuilder.h"
23 #include "src/gpu/ganesh/glsl/GrGLSLUniformHandler.h"
24 
25 #include <memory>
26 #include <string>
27 
28 class GrGLSLProgramDataManager;
29 enum class GrClampType;
30 
IsSupportedMode(SkBlendMode mode)31 bool GrCustomXfermode::IsSupportedMode(SkBlendMode mode) {
32     return (int)mode  > (int)SkBlendMode::kLastCoeffMode &&
33            (int)mode <= (int)SkBlendMode::kLastMode;
34 }
35 
36 ///////////////////////////////////////////////////////////////////////////////
37 // Static helpers
38 ///////////////////////////////////////////////////////////////////////////////
39 
hw_blend_equation(SkBlendMode mode)40 static constexpr skgpu::BlendEquation hw_blend_equation(SkBlendMode mode) {
41     constexpr int kEqOffset = ((int)skgpu::BlendEquation::kOverlay - (int)SkBlendMode::kOverlay);
42     static_assert((int)skgpu::BlendEquation::kOverlay == (int)SkBlendMode::kOverlay + kEqOffset);
43     static_assert((int)skgpu::BlendEquation::kDarken == (int)SkBlendMode::kDarken + kEqOffset);
44     static_assert((int)skgpu::BlendEquation::kLighten == (int)SkBlendMode::kLighten + kEqOffset);
45     static_assert((int)skgpu::BlendEquation::kColorDodge == (int)SkBlendMode::kColorDodge + kEqOffset);
46     static_assert((int)skgpu::BlendEquation::kColorBurn == (int)SkBlendMode::kColorBurn + kEqOffset);
47     static_assert((int)skgpu::BlendEquation::kHardLight == (int)SkBlendMode::kHardLight + kEqOffset);
48     static_assert((int)skgpu::BlendEquation::kSoftLight == (int)SkBlendMode::kSoftLight + kEqOffset);
49     static_assert((int)skgpu::BlendEquation::kDifference == (int)SkBlendMode::kDifference + kEqOffset);
50     static_assert((int)skgpu::BlendEquation::kExclusion == (int)SkBlendMode::kExclusion + kEqOffset);
51     static_assert((int)skgpu::BlendEquation::kMultiply == (int)SkBlendMode::kMultiply + kEqOffset);
52     static_assert((int)skgpu::BlendEquation::kHSLHue == (int)SkBlendMode::kHue + kEqOffset);
53     static_assert((int)skgpu::BlendEquation::kHSLSaturation == (int)SkBlendMode::kSaturation + kEqOffset);
54     static_assert((int)skgpu::BlendEquation::kHSLColor == (int)SkBlendMode::kColor + kEqOffset);
55     static_assert((int)skgpu::BlendEquation::kHSLLuminosity == (int)SkBlendMode::kLuminosity + kEqOffset);
56 
57     // There's an illegal BlendEquation that corresponds to no SkBlendMode, hence the extra +1.
58     static_assert(skgpu::kBlendEquationCnt == (int)SkBlendMode::kLastMode + 1 + 1 + kEqOffset);
59 
60     return static_cast<skgpu::BlendEquation>((int)mode + kEqOffset);
61 #undef EQ_OFFSET
62 }
63 
can_use_hw_blend_equation(skgpu::BlendEquation equation,GrProcessorAnalysisCoverage coverage,const GrCaps & caps)64 static bool can_use_hw_blend_equation(skgpu::BlendEquation equation,
65                                       GrProcessorAnalysisCoverage coverage, const GrCaps& caps) {
66     if (!caps.advancedBlendEquationSupport()) {
67         return false;
68     }
69     if (GrProcessorAnalysisCoverage::kLCD == coverage) {
70         return false; // LCD coverage must be applied after the blend equation.
71     }
72     if (caps.isAdvancedBlendEquationDisabled(equation)) {
73         return false;
74     }
75     return true;
76 }
77 
78 ///////////////////////////////////////////////////////////////////////////////
79 // Xfer Processor
80 ///////////////////////////////////////////////////////////////////////////////
81 
82 class CustomXP : public GrXferProcessor {
83 public:
CustomXP(SkBlendMode mode,skgpu::BlendEquation hwBlendEquation)84     CustomXP(SkBlendMode mode, skgpu::BlendEquation hwBlendEquation)
85         : INHERITED(kCustomXP_ClassID)
86         , fMode(mode)
87         , fHWBlendEquation(hwBlendEquation) {}
88 
CustomXP(SkBlendMode mode,GrProcessorAnalysisCoverage coverage)89     CustomXP(SkBlendMode mode, GrProcessorAnalysisCoverage coverage)
90             : INHERITED(kCustomXP_ClassID, /*willReadDstColor=*/true, coverage)
91             , fMode(mode)
92             , fHWBlendEquation(skgpu::BlendEquation::kIllegal) {
93     }
94 
name() const95     const char* name() const override { return "Custom Xfermode"; }
96 
97     std::unique_ptr<ProgramImpl> makeProgramImpl() const override;
98 
99     GrXferBarrierType xferBarrierType(const GrCaps&) const override;
100 
101 private:
hasHWBlendEquation() const102     bool hasHWBlendEquation() const { return skgpu::BlendEquation::kIllegal != fHWBlendEquation; }
103 
104     void onAddToKey(const GrShaderCaps&, skgpu::KeyBuilder*) const override;
105 
106     void onGetBlendInfo(skgpu::BlendInfo*) const override;
107 
108     bool onIsEqual(const GrXferProcessor& xpBase) const override;
109 
110     const SkBlendMode          fMode;
111     const skgpu::BlendEquation fHWBlendEquation;
112 
113     using INHERITED = GrXferProcessor;
114 };
115 
onAddToKey(const GrShaderCaps & caps,skgpu::KeyBuilder * b) const116 void CustomXP::onAddToKey(const GrShaderCaps& caps, skgpu::KeyBuilder* b) const {
117     if (this->hasHWBlendEquation()) {
118         SkASSERT(caps.fAdvBlendEqInteraction > 0);  // 0 will mean !xp.hasHWBlendEquation().
119         b->addBool(true, "has hardware blend equation");
120         b->add32(caps.fAdvBlendEqInteraction);
121     } else {
122         b->addBool(false, "has hardware blend equation");
123         b->add32(GrGLSLBlend::BlendKey(fMode));
124     }
125 }
126 
makeProgramImpl() const127 std::unique_ptr<GrXferProcessor::ProgramImpl> CustomXP::makeProgramImpl() const {
128     SkASSERT(this->willReadDstColor() != this->hasHWBlendEquation());
129 
130     class Impl : public ProgramImpl {
131     private:
132         void emitOutputsForBlendState(const EmitArgs& args) override {
133             const CustomXP& xp = args.fXP.cast<CustomXP>();
134             SkASSERT(xp.hasHWBlendEquation());
135 
136             GrGLSLXPFragmentBuilder* fragBuilder = args.fXPFragBuilder;
137             fragBuilder->enableAdvancedBlendEquationIfNeeded(xp.fHWBlendEquation);
138 
139             // Apply coverage by multiplying it into the src color before blending. This will "just
140             // work" automatically. (See analysisProperties())
141             fragBuilder->codeAppendf("%s = %s * %s;",
142                                      args.fOutputPrimary,
143                                      args.fInputCoverage,
144                                      args.fInputColor);
145         }
146 
147         void emitBlendCodeForDstRead(GrGLSLXPFragmentBuilder* fragBuilder,
148                                      GrGLSLUniformHandler* uniformHandler,
149                                      const char* srcColor,
150                                      const char* srcCoverage,
151                                      const char* dstColor,
152                                      const char* outColor,
153                                      const char* outColorSecondary,
154                                      const GrXferProcessor& proc) override {
155             const CustomXP& xp = proc.cast<CustomXP>();
156             SkASSERT(!xp.hasHWBlendEquation());
157 
158             std::string blendExpr = GrGLSLBlend::BlendExpression(
159                     &xp, uniformHandler, &fBlendUniform, srcColor, dstColor, xp.fMode);
160             fragBuilder->codeAppendf("%s = %s;", outColor, blendExpr.c_str());
161 
162             // Apply coverage.
163             DefaultCoverageModulation(fragBuilder,
164                                       srcCoverage,
165                                       dstColor,
166                                       outColor,
167                                       outColorSecondary,
168                                       xp);
169         }
170 
171         void onSetData(const GrGLSLProgramDataManager& pdman,
172                        const GrXferProcessor& proc) override {
173             if (fBlendUniform.isValid()) {
174                 const CustomXP& xp = proc.cast<CustomXP>();
175                 GrGLSLBlend::SetBlendModeUniformData(pdman, fBlendUniform, xp.fMode);
176             }
177         }
178 
179         GrGLSLUniformHandler::UniformHandle fBlendUniform;
180     };
181 
182     return std::make_unique<Impl>();
183 }
184 
onIsEqual(const GrXferProcessor & other) const185 bool CustomXP::onIsEqual(const GrXferProcessor& other) const {
186     const CustomXP& s = other.cast<CustomXP>();
187     return fMode == s.fMode && fHWBlendEquation == s.fHWBlendEquation;
188 }
189 
xferBarrierType(const GrCaps & caps) const190 GrXferBarrierType CustomXP::xferBarrierType(const GrCaps& caps) const {
191     if (this->hasHWBlendEquation() && !caps.advancedCoherentBlendEquationSupport()) {
192         return kBlend_GrXferBarrierType;
193     }
194     return kNone_GrXferBarrierType;
195 }
196 
onGetBlendInfo(skgpu::BlendInfo * blendInfo) const197 void CustomXP::onGetBlendInfo(skgpu::BlendInfo* blendInfo) const {
198     if (this->hasHWBlendEquation()) {
199         blendInfo->fEquation = fHWBlendEquation;
200     }
201 }
202 
203 ///////////////////////////////////////////////////////////////////////////////
204 
205 // See the comment above GrXPFactory's definition about this warning suppression.
206 #if defined(__GNUC__)
207 #pragma GCC diagnostic push
208 #pragma GCC diagnostic ignored "-Wnon-virtual-dtor"
209 #endif
210 #if defined(__clang__)
211 #pragma clang diagnostic push
212 #pragma clang diagnostic ignored "-Wnon-virtual-dtor"
213 #endif
214 class CustomXPFactory : public GrXPFactory {
215 public:
CustomXPFactory(SkBlendMode mode)216     constexpr CustomXPFactory(SkBlendMode mode)
217             : fMode(mode), fHWBlendEquation(hw_blend_equation(mode)) {}
218 
219 private:
220     sk_sp<const GrXferProcessor> makeXferProcessor(const GrProcessorAnalysisColor&,
221                                                    GrProcessorAnalysisCoverage,
222                                                    const GrCaps&,
223                                                    GrClampType) const override;
224 
225     AnalysisProperties analysisProperties(const GrProcessorAnalysisColor&,
226                                           const GrProcessorAnalysisCoverage&,
227                                           const GrCaps&,
228                                           GrClampType) const override;
229 
230     GR_DECLARE_XP_FACTORY_TEST
231 
232     SkBlendMode fMode;
233     skgpu::BlendEquation fHWBlendEquation;
234 
235     using INHERITED = GrXPFactory;
236 };
237 #if defined(__GNUC__)
238 #pragma GCC diagnostic pop
239 #endif
240 #if defined(__clang__)
241 #pragma clang diagnostic pop
242 #endif
243 
makeXferProcessor(const GrProcessorAnalysisColor &,GrProcessorAnalysisCoverage coverage,const GrCaps & caps,GrClampType clampType) const244 sk_sp<const GrXferProcessor> CustomXPFactory::makeXferProcessor(
245         const GrProcessorAnalysisColor&,
246         GrProcessorAnalysisCoverage coverage,
247         const GrCaps& caps,
248         GrClampType clampType) const {
249     SkASSERT(GrCustomXfermode::IsSupportedMode(fMode));
250     if (can_use_hw_blend_equation(fHWBlendEquation, coverage, caps)) {
251         return sk_sp<GrXferProcessor>(new CustomXP(fMode, fHWBlendEquation));
252     }
253     return sk_sp<GrXferProcessor>(new CustomXP(fMode, coverage));
254 }
255 
analysisProperties(const GrProcessorAnalysisColor &,const GrProcessorAnalysisCoverage & coverage,const GrCaps & caps,GrClampType clampType) const256 GrXPFactory::AnalysisProperties CustomXPFactory::analysisProperties(
257         const GrProcessorAnalysisColor&, const GrProcessorAnalysisCoverage& coverage,
258         const GrCaps& caps, GrClampType clampType) const {
259     /*
260       The general SVG blend equation is defined in the spec as follows:
261 
262         Dca' = B(Sc, Dc) * Sa * Da + Y * Sca * (1-Da) + Z * Dca * (1-Sa)
263         Da'  = X * Sa * Da + Y * Sa * (1-Da) + Z * Da * (1-Sa)
264 
265       (Note that Sca, Dca indicate RGB vectors that are premultiplied by alpha,
266        and that B(Sc, Dc) is a mode-specific function that accepts non-multiplied
267        RGB colors.)
268 
269       For every blend mode supported by this class, i.e. the "advanced" blend
270       modes, X=Y=Z=1 and this equation reduces to the PDF blend equation.
271 
272       It can be shown that when X=Y=Z=1, these equations can modulate alpha for
273       coverage.
274 
275 
276       == Color ==
277 
278       We substitute Y=Z=1 and define a blend() function that calculates Dca' in
279       terms of premultiplied alpha only:
280 
281         blend(Sca, Dca, Sa, Da) = {Dca : if Sa == 0,
282                                    Sca : if Da == 0,
283                                    B(Sca/Sa, Dca/Da) * Sa * Da + Sca * (1-Da) + Dca * (1-Sa) : if
284       Sa,Da != 0}
285 
286       And for coverage modulation, we use a post blend src-over model:
287 
288         Dca'' = f * blend(Sca, Dca, Sa, Da) + (1-f) * Dca
289 
290       (Where f is the fractional coverage.)
291 
292       Next we show that canTweakAlphaForCoverage() is true by proving the
293       following relationship:
294 
295         blend(f*Sca, Dca, f*Sa, Da) == f * blend(Sca, Dca, Sa, Da) + (1-f) * Dca
296 
297       General case (f,Sa,Da != 0):
298 
299         f * blend(Sca, Dca, Sa, Da) + (1-f) * Dca
300           = f * (B(Sca/Sa, Dca/Da) * Sa * Da + Sca * (1-Da) + Dca * (1-Sa)) + (1-f) * Dca  [Sa,Da !=
301       0, definition of blend()]
302           = B(Sca/Sa, Dca/Da) * f*Sa * Da + f*Sca * (1-Da) + f*Dca * (1-Sa) + Dca - f*Dca
303           = B(Sca/Sa, Dca/Da) * f*Sa * Da + f*Sca - f*Sca * Da + f*Dca - f*Dca * Sa + Dca - f*Dca
304           = B(Sca/Sa, Dca/Da) * f*Sa * Da + f*Sca - f*Sca * Da - f*Dca * Sa + Dca
305           = B(Sca/Sa, Dca/Da) * f*Sa * Da + f*Sca * (1-Da) - f*Dca * Sa + Dca
306           = B(Sca/Sa, Dca/Da) * f*Sa * Da + f*Sca * (1-Da) + Dca * (1 - f*Sa)
307           = B(f*Sca/f*Sa, Dca/Da) * f*Sa * Da + f*Sca * (1-Da) + Dca * (1 - f*Sa)  [f!=0]
308           = blend(f*Sca, Dca, f*Sa, Da)  [definition of blend()]
309 
310       Corner cases (Sa=0, Da=0, and f=0):
311 
312         Sa=0: f * blend(Sca, Dca, Sa, Da) + (1-f) * Dca
313                 = f * Dca + (1-f) * Dca  [Sa=0, definition of blend()]
314                 = Dca
315                 = blend(0, Dca, 0, Da)  [definition of blend()]
316                 = blend(f*Sca, Dca, f*Sa, Da)  [Sa=0]
317 
318         Da=0: f * blend(Sca, Dca, Sa, Da) + (1-f) * Dca
319                 = f * Sca + (1-f) * Dca  [Da=0, definition of blend()]
320                 = f * Sca  [Da=0]
321                 = blend(f*Sca, 0, f*Sa, 0)  [definition of blend()]
322                 = blend(f*Sca, Dca, f*Sa, Da)  [Da=0]
323 
324         f=0: f * blend(Sca, Dca, Sa, Da) + (1-f) * Dca
325                = Dca  [f=0]
326                = blend(0, Dca, 0, Da)  [definition of blend()]
327                = blend(f*Sca, Dca, f*Sa, Da)  [f=0]
328 
329       == Alpha ==
330 
331       We substitute X=Y=Z=1 and define a blend() function that calculates Da':
332 
333         blend(Sa, Da) = Sa * Da + Sa * (1-Da) + Da * (1-Sa)
334                       = Sa * Da + Sa - Sa * Da + Da - Da * Sa
335                       = Sa + Da - Sa * Da
336 
337       We use the same model for coverage modulation as we did with color:
338 
339         Da'' = f * blend(Sa, Da) + (1-f) * Da
340 
341       And show that canTweakAlphaForCoverage() is true by proving the following
342       relationship:
343 
344         blend(f*Sa, Da) == f * blend(Sa, Da) + (1-f) * Da
345 
346 
347         f * blend(Sa, Da) + (1-f) * Da
348           = f * (Sa + Da - Sa * Da) + (1-f) * Da
349           = f*Sa + f*Da - f*Sa * Da + Da - f*Da
350           = f*Sa - f*Sa * Da + Da
351           = f*Sa + Da - f*Sa * Da
352           = blend(f*Sa, Da)
353     */
354     if (can_use_hw_blend_equation(fHWBlendEquation, coverage, caps)) {
355         if (caps.blendEquationSupport() == GrCaps::kAdvancedCoherent_BlendEquationSupport) {
356             return AnalysisProperties::kCompatibleWithCoverageAsAlpha;
357         } else {
358             return AnalysisProperties::kCompatibleWithCoverageAsAlpha |
359                    AnalysisProperties::kRequiresNonOverlappingDraws |
360                    AnalysisProperties::kUsesNonCoherentHWBlending;
361         }
362     }
363     return AnalysisProperties::kCompatibleWithCoverageAsAlpha |
364            AnalysisProperties::kReadsDstInShader;
365 }
366 
GR_DEFINE_XP_FACTORY_TEST(CustomXPFactory) const367 GR_DEFINE_XP_FACTORY_TEST(CustomXPFactory)
368 #if defined(GPU_TEST_UTILS)
369 const GrXPFactory* CustomXPFactory::TestGet(GrProcessorTestData* d) {
370     int mode = d->fRandom->nextRangeU((int)SkBlendMode::kLastCoeffMode + 1,
371                                       (int)SkBlendMode::kLastSeparableMode);
372 
373     return GrCustomXfermode::Get((SkBlendMode)mode);
374 }
375 #endif
376 
377 ///////////////////////////////////////////////////////////////////////////////
378 
Get(SkBlendMode mode)379 const GrXPFactory* GrCustomXfermode::Get(SkBlendMode mode) {
380     static constexpr const CustomXPFactory gOverlay(SkBlendMode::kOverlay);
381     static constexpr const CustomXPFactory gDarken(SkBlendMode::kDarken);
382     static constexpr const CustomXPFactory gLighten(SkBlendMode::kLighten);
383     static constexpr const CustomXPFactory gColorDodge(SkBlendMode::kColorDodge);
384     static constexpr const CustomXPFactory gColorBurn(SkBlendMode::kColorBurn);
385     static constexpr const CustomXPFactory gHardLight(SkBlendMode::kHardLight);
386     static constexpr const CustomXPFactory gSoftLight(SkBlendMode::kSoftLight);
387     static constexpr const CustomXPFactory gDifference(SkBlendMode::kDifference);
388     static constexpr const CustomXPFactory gExclusion(SkBlendMode::kExclusion);
389     static constexpr const CustomXPFactory gMultiply(SkBlendMode::kMultiply);
390     static constexpr const CustomXPFactory gHue(SkBlendMode::kHue);
391     static constexpr const CustomXPFactory gSaturation(SkBlendMode::kSaturation);
392     static constexpr const CustomXPFactory gColor(SkBlendMode::kColor);
393     static constexpr const CustomXPFactory gLuminosity(SkBlendMode::kLuminosity);
394     switch (mode) {
395         case SkBlendMode::kOverlay:
396             return &gOverlay;
397         case SkBlendMode::kDarken:
398             return &gDarken;
399         case SkBlendMode::kLighten:
400             return &gLighten;
401         case SkBlendMode::kColorDodge:
402             return &gColorDodge;
403         case SkBlendMode::kColorBurn:
404             return &gColorBurn;
405         case SkBlendMode::kHardLight:
406             return &gHardLight;
407         case SkBlendMode::kSoftLight:
408             return &gSoftLight;
409         case SkBlendMode::kDifference:
410             return &gDifference;
411         case SkBlendMode::kExclusion:
412             return &gExclusion;
413         case SkBlendMode::kMultiply:
414             return &gMultiply;
415         case SkBlendMode::kHue:
416             return &gHue;
417         case SkBlendMode::kSaturation:
418             return &gSaturation;
419         case SkBlendMode::kColor:
420             return &gColor;
421         case SkBlendMode::kLuminosity:
422             return &gLuminosity;
423         default:
424             SkASSERT(!GrCustomXfermode::IsSupportedMode(mode));
425             return nullptr;
426     }
427 }
428