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