• Home
  • History
  • Annotate
  • 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 "effects/GrCustomXfermode.h"
9 
10 #include "GrCaps.h"
11 #include "GrCoordTransform.h"
12 #include "GrFragmentProcessor.h"
13 #include "GrPipeline.h"
14 #include "GrProcessor.h"
15 #include "GrShaderCaps.h"
16 #include "GrTexture.h"
17 #include "glsl/GrGLSLBlend.h"
18 #include "glsl/GrGLSLFragmentProcessor.h"
19 #include "glsl/GrGLSLFragmentShaderBuilder.h"
20 #include "glsl/GrGLSLProgramDataManager.h"
21 #include "glsl/GrGLSLUniformHandler.h"
22 #include "glsl/GrGLSLXferProcessor.h"
23 
IsSupportedMode(SkBlendMode mode)24 bool GrCustomXfermode::IsSupportedMode(SkBlendMode mode) {
25     return (int)mode  > (int)SkBlendMode::kLastCoeffMode &&
26            (int)mode <= (int)SkBlendMode::kLastMode;
27 }
28 
29 ///////////////////////////////////////////////////////////////////////////////
30 // Static helpers
31 ///////////////////////////////////////////////////////////////////////////////
32 
hw_blend_equation(SkBlendMode mode)33 static constexpr GrBlendEquation hw_blend_equation(SkBlendMode mode) {
34 // In C++14 this could be a constexpr int variable.
35 #define EQ_OFFSET (kOverlay_GrBlendEquation - (int)SkBlendMode::kOverlay)
36     GR_STATIC_ASSERT(kOverlay_GrBlendEquation == (int)SkBlendMode::kOverlay + EQ_OFFSET);
37     GR_STATIC_ASSERT(kDarken_GrBlendEquation == (int)SkBlendMode::kDarken + EQ_OFFSET);
38     GR_STATIC_ASSERT(kLighten_GrBlendEquation == (int)SkBlendMode::kLighten + EQ_OFFSET);
39     GR_STATIC_ASSERT(kColorDodge_GrBlendEquation == (int)SkBlendMode::kColorDodge + EQ_OFFSET);
40     GR_STATIC_ASSERT(kColorBurn_GrBlendEquation == (int)SkBlendMode::kColorBurn + EQ_OFFSET);
41     GR_STATIC_ASSERT(kHardLight_GrBlendEquation == (int)SkBlendMode::kHardLight + EQ_OFFSET);
42     GR_STATIC_ASSERT(kSoftLight_GrBlendEquation == (int)SkBlendMode::kSoftLight + EQ_OFFSET);
43     GR_STATIC_ASSERT(kDifference_GrBlendEquation == (int)SkBlendMode::kDifference + EQ_OFFSET);
44     GR_STATIC_ASSERT(kExclusion_GrBlendEquation == (int)SkBlendMode::kExclusion + EQ_OFFSET);
45     GR_STATIC_ASSERT(kMultiply_GrBlendEquation == (int)SkBlendMode::kMultiply + EQ_OFFSET);
46     GR_STATIC_ASSERT(kHSLHue_GrBlendEquation == (int)SkBlendMode::kHue + EQ_OFFSET);
47     GR_STATIC_ASSERT(kHSLSaturation_GrBlendEquation == (int)SkBlendMode::kSaturation + EQ_OFFSET);
48     GR_STATIC_ASSERT(kHSLColor_GrBlendEquation == (int)SkBlendMode::kColor + EQ_OFFSET);
49     GR_STATIC_ASSERT(kHSLLuminosity_GrBlendEquation == (int)SkBlendMode::kLuminosity + EQ_OFFSET);
50     GR_STATIC_ASSERT(kGrBlendEquationCnt == (int)SkBlendMode::kLastMode + 1 + EQ_OFFSET);
51     return static_cast<GrBlendEquation>((int)mode + EQ_OFFSET);
52 #undef EQ_OFFSET
53 }
54 
can_use_hw_blend_equation(GrBlendEquation equation,GrPipelineAnalysisCoverage coverage,const GrCaps & caps)55 static bool can_use_hw_blend_equation(GrBlendEquation equation, GrPipelineAnalysisCoverage coverage,
56                                       const GrCaps& caps) {
57     if (!caps.advancedBlendEquationSupport()) {
58         return false;
59     }
60     if (GrPipelineAnalysisCoverage::kLCD == coverage) {
61         return false; // LCD coverage must be applied after the blend equation.
62     }
63     if (caps.canUseAdvancedBlendEquation(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         : fMode(mode),
77           fHWBlendEquation(hwBlendEquation) {
78         this->initClassID<CustomXP>();
79     }
80 
CustomXP(const DstTexture * dstTexture,bool hasMixedSamples,SkBlendMode mode)81     CustomXP(const DstTexture* dstTexture, bool hasMixedSamples, SkBlendMode mode)
82         : INHERITED(dstTexture, true, hasMixedSamples),
83           fMode(mode),
84           fHWBlendEquation(static_cast<GrBlendEquation>(-1)) {
85         this->initClassID<CustomXP>();
86     }
87 
name() const88     const char* name() const override { return "Custom Xfermode"; }
89 
90     GrGLSLXferProcessor* createGLSLInstance() const override;
91 
mode() const92     SkBlendMode mode() const { return fMode; }
hasHWBlendEquation() const93     bool hasHWBlendEquation() const { return -1 != static_cast<int>(fHWBlendEquation); }
94 
hwBlendEquation() const95     GrBlendEquation hwBlendEquation() const {
96         SkASSERT(this->hasHWBlendEquation());
97         return fHWBlendEquation;
98     }
99 
100 private:
101     GrXferProcessor::OptFlags onGetOptimizations(const FragmentProcessorAnalysis&) const override;
102 
103     void onGetGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override;
104 
105     GrXferBarrierType onXferBarrier(const GrRenderTarget*, const GrCaps&) const override;
106 
107     void onGetBlendInfo(BlendInfo*) const override;
108 
109     bool onIsEqual(const GrXferProcessor& xpBase) const override;
110 
111     const SkBlendMode      fMode;
112     const GrBlendEquation  fHWBlendEquation;
113 
114     typedef GrXferProcessor INHERITED;
115 };
116 
117 ///////////////////////////////////////////////////////////////////////////////
118 
119 class GLCustomXP : public GrGLSLXferProcessor {
120 public:
GLCustomXP(const GrXferProcessor &)121     GLCustomXP(const GrXferProcessor&) {}
~GLCustomXP()122     ~GLCustomXP() override {}
123 
GenKey(const GrXferProcessor & p,const GrShaderCaps & caps,GrProcessorKeyBuilder * b)124     static void GenKey(const GrXferProcessor& p, const GrShaderCaps& caps,
125                        GrProcessorKeyBuilder* b) {
126         const CustomXP& xp = p.cast<CustomXP>();
127         uint32_t key = 0;
128         if (xp.hasHWBlendEquation()) {
129             SkASSERT(caps.advBlendEqInteraction() > 0);  // 0 will mean !xp.hasHWBlendEquation().
130             key |= caps.advBlendEqInteraction();
131             GR_STATIC_ASSERT(GrShaderCaps::kLast_AdvBlendEqInteraction < 4);
132         }
133         if (!xp.hasHWBlendEquation() || caps.mustEnableSpecificAdvBlendEqs()) {
134             key |= (int)xp.mode() << 3;
135         }
136         b->add32(key);
137     }
138 
139 private:
emitOutputsForBlendState(const EmitArgs & args)140     void emitOutputsForBlendState(const EmitArgs& args) override {
141         const CustomXP& xp = args.fXP.cast<CustomXP>();
142         SkASSERT(xp.hasHWBlendEquation());
143 
144         GrGLSLXPFragmentBuilder* fragBuilder = args.fXPFragBuilder;
145         fragBuilder->enableAdvancedBlendEquationIfNeeded(xp.hwBlendEquation());
146 
147         // Apply coverage by multiplying it into the src color before blending. Mixed samples will
148         // "just work" automatically. (See onGetOptimizations())
149         fragBuilder->codeAppendf("%s = %s * %s;", args.fOutputPrimary, args.fInputCoverage,
150                                  args.fInputColor);
151     }
152 
emitBlendCodeForDstRead(GrGLSLXPFragmentBuilder * fragBuilder,GrGLSLUniformHandler * uniformHandler,const char * srcColor,const char * srcCoverage,const char * dstColor,const char * outColor,const char * outColorSecondary,const GrXferProcessor & proc)153     void emitBlendCodeForDstRead(GrGLSLXPFragmentBuilder* fragBuilder,
154                                  GrGLSLUniformHandler* uniformHandler,
155                                  const char* srcColor,
156                                  const char* srcCoverage,
157                                  const char* dstColor,
158                                  const char* outColor,
159                                  const char* outColorSecondary,
160                                  const GrXferProcessor& proc) override {
161         const CustomXP& xp = proc.cast<CustomXP>();
162         SkASSERT(!xp.hasHWBlendEquation());
163 
164         GrGLSLBlend::AppendMode(fragBuilder, srcColor, dstColor, outColor, xp.mode());
165 
166         // Apply coverage.
167         INHERITED::DefaultCoverageModulation(fragBuilder, srcCoverage, dstColor, outColor,
168                                              outColorSecondary, xp);
169     }
170 
onSetData(const GrGLSLProgramDataManager &,const GrXferProcessor &)171     void onSetData(const GrGLSLProgramDataManager&, const GrXferProcessor&) override {}
172 
173     typedef GrGLSLXferProcessor INHERITED;
174 };
175 
176 ///////////////////////////////////////////////////////////////////////////////
177 
onGetGLSLProcessorKey(const GrShaderCaps & caps,GrProcessorKeyBuilder * b) const178 void CustomXP::onGetGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const {
179     GLCustomXP::GenKey(*this, caps, b);
180 }
181 
createGLSLInstance() const182 GrGLSLXferProcessor* CustomXP::createGLSLInstance() const {
183     SkASSERT(this->willReadDstColor() != this->hasHWBlendEquation());
184     return new GLCustomXP(*this);
185 }
186 
onIsEqual(const GrXferProcessor & other) const187 bool CustomXP::onIsEqual(const GrXferProcessor& other) const {
188     const CustomXP& s = other.cast<CustomXP>();
189     return fMode == s.fMode && fHWBlendEquation == s.fHWBlendEquation;
190 }
191 
onGetOptimizations(const FragmentProcessorAnalysis & analysis) const192 GrXferProcessor::OptFlags CustomXP::onGetOptimizations(
193         const FragmentProcessorAnalysis& analysis) const {
194     /*
195       Most the optimizations we do here are based on tweaking alpha for coverage.
196 
197       The general SVG blend equation is defined in the spec as follows:
198 
199         Dca' = B(Sc, Dc) * Sa * Da + Y * Sca * (1-Da) + Z * Dca * (1-Sa)
200         Da'  = X * Sa * Da + Y * Sa * (1-Da) + Z * Da * (1-Sa)
201 
202       (Note that Sca, Dca indicate RGB vectors that are premultiplied by alpha,
203        and that B(Sc, Dc) is a mode-specific function that accepts non-multiplied
204        RGB colors.)
205 
206       For every blend mode supported by this class, i.e. the "advanced" blend
207       modes, X=Y=Z=1 and this equation reduces to the PDF blend equation.
208 
209       It can be shown that when X=Y=Z=1, these equations can modulate alpha for
210       coverage.
211 
212 
213       == Color ==
214 
215       We substitute Y=Z=1 and define a blend() function that calculates Dca' in
216       terms of premultiplied alpha only:
217 
218         blend(Sca, Dca, Sa, Da) = {Dca : if Sa == 0,
219                                    Sca : if Da == 0,
220                                    B(Sca/Sa, Dca/Da) * Sa * Da + Sca * (1-Da) + Dca * (1-Sa) : if
221       Sa,Da != 0}
222 
223       And for coverage modulation, we use a post blend src-over model:
224 
225         Dca'' = f * blend(Sca, Dca, Sa, Da) + (1-f) * Dca
226 
227       (Where f is the fractional coverage.)
228 
229       Next we show that canTweakAlphaForCoverage() is true by proving the
230       following relationship:
231 
232         blend(f*Sca, Dca, f*Sa, Da) == f * blend(Sca, Dca, Sa, Da) + (1-f) * Dca
233 
234       General case (f,Sa,Da != 0):
235 
236         f * blend(Sca, Dca, Sa, Da) + (1-f) * Dca
237           = f * (B(Sca/Sa, Dca/Da) * Sa * Da + Sca * (1-Da) + Dca * (1-Sa)) + (1-f) * Dca  [Sa,Da !=
238       0, definition of blend()]
239           = B(Sca/Sa, Dca/Da) * f*Sa * Da + f*Sca * (1-Da) + f*Dca * (1-Sa) + Dca - f*Dca
240           = B(Sca/Sa, Dca/Da) * f*Sa * Da + f*Sca - f*Sca * Da + f*Dca - f*Dca * Sa + Dca - f*Dca
241           = B(Sca/Sa, Dca/Da) * f*Sa * Da + f*Sca - f*Sca * Da - f*Dca * Sa + Dca
242           = B(Sca/Sa, Dca/Da) * f*Sa * Da + f*Sca * (1-Da) - f*Dca * Sa + Dca
243           = B(Sca/Sa, Dca/Da) * f*Sa * Da + f*Sca * (1-Da) + Dca * (1 - f*Sa)
244           = B(f*Sca/f*Sa, Dca/Da) * f*Sa * Da + f*Sca * (1-Da) + Dca * (1 - f*Sa)  [f!=0]
245           = blend(f*Sca, Dca, f*Sa, Da)  [definition of blend()]
246 
247       Corner cases (Sa=0, Da=0, and f=0):
248 
249         Sa=0: f * blend(Sca, Dca, Sa, Da) + (1-f) * Dca
250                 = f * Dca + (1-f) * Dca  [Sa=0, definition of blend()]
251                 = Dca
252                 = blend(0, Dca, 0, Da)  [definition of blend()]
253                 = blend(f*Sca, Dca, f*Sa, Da)  [Sa=0]
254 
255         Da=0: f * blend(Sca, Dca, Sa, Da) + (1-f) * Dca
256                 = f * Sca + (1-f) * Dca  [Da=0, definition of blend()]
257                 = f * Sca  [Da=0]
258                 = blend(f*Sca, 0, f*Sa, 0)  [definition of blend()]
259                 = blend(f*Sca, Dca, f*Sa, Da)  [Da=0]
260 
261         f=0: f * blend(Sca, Dca, Sa, Da) + (1-f) * Dca
262                = Dca  [f=0]
263                = blend(0, Dca, 0, Da)  [definition of blend()]
264                = blend(f*Sca, Dca, f*Sa, Da)  [f=0]
265 
266       == Alpha ==
267 
268       We substitute X=Y=Z=1 and define a blend() function that calculates Da':
269 
270         blend(Sa, Da) = Sa * Da + Sa * (1-Da) + Da * (1-Sa)
271                       = Sa * Da + Sa - Sa * Da + Da - Da * Sa
272                       = Sa + Da - Sa * Da
273 
274       We use the same model for coverage modulation as we did with color:
275 
276         Da'' = f * blend(Sa, Da) + (1-f) * Da
277 
278       And show that canTweakAlphaForCoverage() is true by proving the following
279       relationship:
280 
281         blend(f*Sa, Da) == f * blend(Sa, Da) + (1-f) * Da
282 
283 
284         f * blend(Sa, Da) + (1-f) * Da
285           = f * (Sa + Da - Sa * Da) + (1-f) * Da
286           = f*Sa + f*Da - f*Sa * Da + Da - f*Da
287           = f*Sa - f*Sa * Da + Da
288           = f*Sa + Da - f*Sa * Da
289           = blend(f*Sa, Da)
290      */
291 
292     OptFlags flags = kNone_OptFlags;
293     if (analysis.isCompatibleWithCoverageAsAlpha()) {
294         flags |= kCanTweakAlphaForCoverage_OptFlag;
295     }
296     return flags;
297 }
298 
onXferBarrier(const GrRenderTarget * rt,const GrCaps & caps) const299 GrXferBarrierType CustomXP::onXferBarrier(const GrRenderTarget* rt, const GrCaps& caps) const {
300     if (this->hasHWBlendEquation() && !caps.advancedCoherentBlendEquationSupport()) {
301         return kBlend_GrXferBarrierType;
302     }
303     return kNone_GrXferBarrierType;
304 }
305 
onGetBlendInfo(BlendInfo * blendInfo) const306 void CustomXP::onGetBlendInfo(BlendInfo* blendInfo) const {
307     if (this->hasHWBlendEquation()) {
308         blendInfo->fEquation = this->hwBlendEquation();
309     }
310 }
311 
312 ///////////////////////////////////////////////////////////////////////////////
313 
314 // See the comment above GrXPFactory's definition about this warning suppression.
315 #if defined(__GNUC__) || defined(__clang)
316 #pragma GCC diagnostic push
317 #pragma GCC diagnostic ignored "-Wnon-virtual-dtor"
318 #endif
319 class CustomXPFactory : public GrXPFactory {
320 public:
CustomXPFactory(SkBlendMode mode)321     constexpr CustomXPFactory(SkBlendMode mode)
322             : fMode(mode), fHWBlendEquation(hw_blend_equation(mode)) {}
323 
324 private:
325     GrXferProcessor* onCreateXferProcessor(const GrCaps& caps,
326                                            const FragmentProcessorAnalysis&,
327                                            bool hasMixedSamples,
328                                            const DstTexture*) const override;
329 
330     bool willReadDstInShader(const GrCaps&, const FragmentProcessorAnalysis&) const override;
331 
compatibleWithCoverageAsAlpha(bool colorIsOpaque) const332     bool compatibleWithCoverageAsAlpha(bool colorIsOpaque) const override { return true; }
333 
334     GR_DECLARE_XP_FACTORY_TEST;
335 
336     SkBlendMode     fMode;
337     GrBlendEquation fHWBlendEquation;
338 
339     typedef GrXPFactory INHERITED;
340 };
341 #if defined(__GNUC__) || defined(__clang)
342 #pragma GCC diagnostic pop
343 #endif
344 
onCreateXferProcessor(const GrCaps & caps,const FragmentProcessorAnalysis & analysis,bool hasMixedSamples,const DstTexture * dstTexture) const345 GrXferProcessor* CustomXPFactory::onCreateXferProcessor(const GrCaps& caps,
346                                                         const FragmentProcessorAnalysis& analysis,
347                                                         bool hasMixedSamples,
348                                                         const DstTexture* dstTexture) const {
349     SkASSERT(GrCustomXfermode::IsSupportedMode(fMode));
350     if (can_use_hw_blend_equation(fHWBlendEquation, analysis.outputCoverageType(), caps)) {
351         SkASSERT(!dstTexture || !dstTexture->texture());
352         return new CustomXP(fMode, fHWBlendEquation);
353     }
354     return new CustomXP(dstTexture, hasMixedSamples, fMode);
355 }
356 
willReadDstInShader(const GrCaps & caps,const FragmentProcessorAnalysis & analysis) const357 bool CustomXPFactory::willReadDstInShader(const GrCaps& caps,
358                                           const FragmentProcessorAnalysis& analysis) const {
359     return !can_use_hw_blend_equation(fHWBlendEquation, analysis.outputCoverageType(), caps);
360 }
361 
362 GR_DEFINE_XP_FACTORY_TEST(CustomXPFactory);
363 #if GR_TEST_UTILS
TestGet(GrProcessorTestData * d)364 const GrXPFactory* CustomXPFactory::TestGet(GrProcessorTestData* d) {
365     int mode = d->fRandom->nextRangeU((int)SkBlendMode::kLastCoeffMode + 1,
366                                       (int)SkBlendMode::kLastSeparableMode);
367 
368     return GrCustomXfermode::Get((SkBlendMode)mode);
369 }
370 #endif
371 
372 ///////////////////////////////////////////////////////////////////////////////
373 
Get(SkBlendMode mode)374 const GrXPFactory* GrCustomXfermode::Get(SkBlendMode mode) {
375     // If these objects are constructed as static constexpr by cl.exe (2015 SP2) the vtables are
376     // null.
377 #ifdef SK_BUILD_FOR_WIN
378 #define _CONSTEXPR_
379 #else
380 #define _CONSTEXPR_ constexpr
381 #endif
382     static _CONSTEXPR_ const CustomXPFactory gOverlay(SkBlendMode::kOverlay);
383     static _CONSTEXPR_ const CustomXPFactory gDarken(SkBlendMode::kDarken);
384     static _CONSTEXPR_ const CustomXPFactory gLighten(SkBlendMode::kLighten);
385     static _CONSTEXPR_ const CustomXPFactory gColorDodge(SkBlendMode::kColorDodge);
386     static _CONSTEXPR_ const CustomXPFactory gColorBurn(SkBlendMode::kColorBurn);
387     static _CONSTEXPR_ const CustomXPFactory gHardLight(SkBlendMode::kHardLight);
388     static _CONSTEXPR_ const CustomXPFactory gSoftLight(SkBlendMode::kSoftLight);
389     static _CONSTEXPR_ const CustomXPFactory gDifference(SkBlendMode::kDifference);
390     static _CONSTEXPR_ const CustomXPFactory gExclusion(SkBlendMode::kExclusion);
391     static _CONSTEXPR_ const CustomXPFactory gMultiply(SkBlendMode::kMultiply);
392     static _CONSTEXPR_ const CustomXPFactory gHue(SkBlendMode::kHue);
393     static _CONSTEXPR_ const CustomXPFactory gSaturation(SkBlendMode::kSaturation);
394     static _CONSTEXPR_ const CustomXPFactory gColor(SkBlendMode::kColor);
395     static _CONSTEXPR_ const CustomXPFactory gLuminosity(SkBlendMode::kLuminosity);
396 #undef _CONSTEXPR_
397     switch (mode) {
398         case SkBlendMode::kOverlay:
399             return &gOverlay;
400         case SkBlendMode::kDarken:
401             return &gDarken;
402         case SkBlendMode::kLighten:
403             return &gLighten;
404         case SkBlendMode::kColorDodge:
405             return &gColorDodge;
406         case SkBlendMode::kColorBurn:
407             return &gColorBurn;
408         case SkBlendMode::kHardLight:
409             return &gHardLight;
410         case SkBlendMode::kSoftLight:
411             return &gSoftLight;
412         case SkBlendMode::kDifference:
413             return &gDifference;
414         case SkBlendMode::kExclusion:
415             return &gExclusion;
416         case SkBlendMode::kMultiply:
417             return &gMultiply;
418         case SkBlendMode::kHue:
419             return &gHue;
420         case SkBlendMode::kSaturation:
421             return &gSaturation;
422         case SkBlendMode::kColor:
423             return &gColor;
424         case SkBlendMode::kLuminosity:
425             return &gLuminosity;
426         default:
427             SkASSERT(!GrCustomXfermode::IsSupportedMode(mode));
428             return nullptr;
429     }
430 }
431