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