• 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 "effects/GrCustomXfermode.h"
9 
10 #include "GrCoordTransform.h"
11 #include "GrContext.h"
12 #include "GrFragmentProcessor.h"
13 #include "GrInvariantOutput.h"
14 #include "GrPipeline.h"
15 #include "GrProcessor.h"
16 #include "GrTexture.h"
17 #include "GrTextureAccess.h"
18 #include "SkXfermode.h"
19 #include "glsl/GrGLSLBlend.h"
20 #include "glsl/GrGLSLCaps.h"
21 #include "glsl/GrGLSLFragmentProcessor.h"
22 #include "glsl/GrGLSLFragmentShaderBuilder.h"
23 #include "glsl/GrGLSLProgramDataManager.h"
24 #include "glsl/GrGLSLUniformHandler.h"
25 #include "glsl/GrGLSLXferProcessor.h"
26 
IsSupportedMode(SkXfermode::Mode mode)27 bool GrCustomXfermode::IsSupportedMode(SkXfermode::Mode mode) {
28     return mode > SkXfermode::kLastCoeffMode && mode <= SkXfermode::kLastMode;
29 }
30 
31 ///////////////////////////////////////////////////////////////////////////////
32 // Static helpers
33 ///////////////////////////////////////////////////////////////////////////////
34 
hw_blend_equation(SkXfermode::Mode mode)35 static GrBlendEquation hw_blend_equation(SkXfermode::Mode mode) {
36     enum { kOffset = kOverlay_GrBlendEquation - SkXfermode::kOverlay_Mode };
37     return static_cast<GrBlendEquation>(mode + kOffset);
38 
39     GR_STATIC_ASSERT(kOverlay_GrBlendEquation == SkXfermode::kOverlay_Mode + kOffset);
40     GR_STATIC_ASSERT(kDarken_GrBlendEquation == SkXfermode::kDarken_Mode + kOffset);
41     GR_STATIC_ASSERT(kLighten_GrBlendEquation == SkXfermode::kLighten_Mode + kOffset);
42     GR_STATIC_ASSERT(kColorDodge_GrBlendEquation == SkXfermode::kColorDodge_Mode + kOffset);
43     GR_STATIC_ASSERT(kColorBurn_GrBlendEquation == SkXfermode::kColorBurn_Mode + kOffset);
44     GR_STATIC_ASSERT(kHardLight_GrBlendEquation == SkXfermode::kHardLight_Mode + kOffset);
45     GR_STATIC_ASSERT(kSoftLight_GrBlendEquation == SkXfermode::kSoftLight_Mode + kOffset);
46     GR_STATIC_ASSERT(kDifference_GrBlendEquation == SkXfermode::kDifference_Mode + kOffset);
47     GR_STATIC_ASSERT(kExclusion_GrBlendEquation == SkXfermode::kExclusion_Mode + kOffset);
48     GR_STATIC_ASSERT(kMultiply_GrBlendEquation == SkXfermode::kMultiply_Mode + kOffset);
49     GR_STATIC_ASSERT(kHSLHue_GrBlendEquation == SkXfermode::kHue_Mode + kOffset);
50     GR_STATIC_ASSERT(kHSLSaturation_GrBlendEquation == SkXfermode::kSaturation_Mode + kOffset);
51     GR_STATIC_ASSERT(kHSLColor_GrBlendEquation == SkXfermode::kColor_Mode + kOffset);
52     GR_STATIC_ASSERT(kHSLLuminosity_GrBlendEquation == SkXfermode::kLuminosity_Mode + kOffset);
53     GR_STATIC_ASSERT(kGrBlendEquationCnt == SkXfermode::kLastMode + 1 + kOffset);
54 }
55 
can_use_hw_blend_equation(GrBlendEquation equation,const GrPipelineOptimizations & opt,const GrCaps & caps)56 static bool can_use_hw_blend_equation(GrBlendEquation equation,
57                                       const GrPipelineOptimizations& opt,
58                                       const GrCaps& caps) {
59     if (!caps.advancedBlendEquationSupport()) {
60         return false;
61     }
62     if (opt.fOverrides.fUsePLSDstRead) {
63         return false;
64     }
65     if (opt.fCoveragePOI.isFourChannelOutput()) {
66         return false; // LCD coverage must be applied after the blend equation.
67     }
68     if (caps.canUseAdvancedBlendEquation(equation)) {
69         return false;
70     }
71     return true;
72 }
73 
74 ///////////////////////////////////////////////////////////////////////////////
75 // Xfer Processor
76 ///////////////////////////////////////////////////////////////////////////////
77 
78 class CustomXP : public GrXferProcessor {
79 public:
CustomXP(SkXfermode::Mode mode,GrBlendEquation hwBlendEquation)80     CustomXP(SkXfermode::Mode mode, GrBlendEquation hwBlendEquation)
81         : fMode(mode),
82           fHWBlendEquation(hwBlendEquation) {
83         this->initClassID<CustomXP>();
84     }
85 
CustomXP(const DstTexture * dstTexture,bool hasMixedSamples,SkXfermode::Mode mode)86     CustomXP(const DstTexture* dstTexture, bool hasMixedSamples, SkXfermode::Mode mode)
87         : INHERITED(dstTexture, true, hasMixedSamples),
88           fMode(mode),
89           fHWBlendEquation(static_cast<GrBlendEquation>(-1)) {
90         this->initClassID<CustomXP>();
91     }
92 
name() const93     const char* name() const override { return "Custom Xfermode"; }
94 
95     GrGLSLXferProcessor* createGLSLInstance() const override;
96 
mode() const97     SkXfermode::Mode mode() const { return fMode; }
hasHWBlendEquation() const98     bool hasHWBlendEquation() const { return -1 != static_cast<int>(fHWBlendEquation); }
99 
hwBlendEquation() const100     GrBlendEquation hwBlendEquation() const {
101         SkASSERT(this->hasHWBlendEquation());
102         return fHWBlendEquation;
103     }
104 
105 private:
106     GrXferProcessor::OptFlags onGetOptimizations(const GrPipelineOptimizations& optimizations,
107                                                  bool doesStencilWrite,
108                                                  GrColor* overrideColor,
109                                                  const GrCaps& caps) const override;
110 
111     void onGetGLSLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override;
112 
113     GrXferBarrierType onXferBarrier(const GrRenderTarget*, const GrCaps&) const override;
114 
115     void onGetBlendInfo(BlendInfo*) const override;
116 
117     bool onIsEqual(const GrXferProcessor& xpBase) const override;
118 
119     const SkXfermode::Mode fMode;
120     const GrBlendEquation  fHWBlendEquation;
121 
122     typedef GrXferProcessor INHERITED;
123 };
124 
125 ///////////////////////////////////////////////////////////////////////////////
126 
127 class GLCustomXP : public GrGLSLXferProcessor {
128 public:
GLCustomXP(const GrXferProcessor &)129     GLCustomXP(const GrXferProcessor&) {}
~GLCustomXP()130     ~GLCustomXP() override {}
131 
GenKey(const GrXferProcessor & p,const GrGLSLCaps & caps,GrProcessorKeyBuilder * b)132     static void GenKey(const GrXferProcessor& p, const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) {
133         const CustomXP& xp = p.cast<CustomXP>();
134         uint32_t key = 0;
135         if (xp.hasHWBlendEquation()) {
136             SkASSERT(caps.advBlendEqInteraction() > 0);  // 0 will mean !xp.hasHWBlendEquation().
137             key |= caps.advBlendEqInteraction();
138             GR_STATIC_ASSERT(GrGLSLCaps::kLast_AdvBlendEqInteraction < 4);
139         }
140         if (!xp.hasHWBlendEquation() || caps.mustEnableSpecificAdvBlendEqs()) {
141             key |= xp.mode() << 3;
142         }
143         b->add32(key);
144     }
145 
146 private:
emitOutputsForBlendState(const EmitArgs & args)147     void emitOutputsForBlendState(const EmitArgs& args) override {
148         const CustomXP& xp = args.fXP.cast<CustomXP>();
149         SkASSERT(xp.hasHWBlendEquation());
150 
151         GrGLSLXPFragmentBuilder* fragBuilder = args.fXPFragBuilder;
152         fragBuilder->enableAdvancedBlendEquationIfNeeded(xp.hwBlendEquation());
153 
154         // Apply coverage by multiplying it into the src color before blending. Mixed samples will
155         // "just work" automatically. (See onGetOptimizations())
156         if (args.fInputCoverage) {
157             fragBuilder->codeAppendf("%s = %s * %s;",
158                                      args.fOutputPrimary, args.fInputCoverage, args.fInputColor);
159         } else {
160             fragBuilder->codeAppendf("%s = %s;", args.fOutputPrimary, args.fInputColor);
161         }
162     }
163 
emitBlendCodeForDstRead(GrGLSLXPFragmentBuilder * fragBuilder,GrGLSLUniformHandler * uniformHandler,const char * srcColor,const char * srcCoverage,const char * dstColor,const char * outColor,const char * outColorSecondary,const GrXferProcessor & proc)164     void emitBlendCodeForDstRead(GrGLSLXPFragmentBuilder* fragBuilder,
165                                  GrGLSLUniformHandler* uniformHandler,
166                                  const char* srcColor,
167                                  const char* srcCoverage,
168                                  const char* dstColor,
169                                  const char* outColor,
170                                  const char* outColorSecondary,
171                                  const GrXferProcessor& proc) override {
172         const CustomXP& xp = proc.cast<CustomXP>();
173         SkASSERT(!xp.hasHWBlendEquation());
174 
175         GrGLSLBlend::AppendMode(fragBuilder, srcColor, dstColor, outColor, xp.mode());
176 
177         // Apply coverage.
178         INHERITED::DefaultCoverageModulation(fragBuilder, srcCoverage, dstColor, outColor,
179                                              outColorSecondary, xp);
180     }
181 
onSetData(const GrGLSLProgramDataManager &,const GrXferProcessor &)182     void onSetData(const GrGLSLProgramDataManager&, const GrXferProcessor&) override {}
183 
184     typedef GrGLSLXferProcessor INHERITED;
185 };
186 
187 ///////////////////////////////////////////////////////////////////////////////
188 
onGetGLSLProcessorKey(const GrGLSLCaps & caps,GrProcessorKeyBuilder * b) const189 void CustomXP::onGetGLSLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const {
190     GLCustomXP::GenKey(*this, caps, b);
191 }
192 
createGLSLInstance() const193 GrGLSLXferProcessor* CustomXP::createGLSLInstance() const {
194     SkASSERT(this->willReadDstColor() != this->hasHWBlendEquation());
195     return new GLCustomXP(*this);
196 }
197 
onIsEqual(const GrXferProcessor & other) const198 bool CustomXP::onIsEqual(const GrXferProcessor& other) const {
199     const CustomXP& s = other.cast<CustomXP>();
200     return fMode == s.fMode && fHWBlendEquation == s.fHWBlendEquation;
201 }
202 
onGetOptimizations(const GrPipelineOptimizations & optimizations,bool doesStencilWrite,GrColor * overrideColor,const GrCaps & caps) const203 GrXferProcessor::OptFlags CustomXP::onGetOptimizations(const GrPipelineOptimizations& optimizations,
204                                                        bool doesStencilWrite,
205                                                        GrColor* overrideColor,
206                                                        const GrCaps& caps) const {
207   /*
208     Most the optimizations we do here are based on tweaking alpha for coverage.
209 
210     The general SVG blend equation is defined in the spec as follows:
211 
212       Dca' = B(Sc, Dc) * Sa * Da + Y * Sca * (1-Da) + Z * Dca * (1-Sa)
213       Da'  = X * Sa * Da + Y * Sa * (1-Da) + Z * Da * (1-Sa)
214 
215     (Note that Sca, Dca indicate RGB vectors that are premultiplied by alpha,
216      and that B(Sc, Dc) is a mode-specific function that accepts non-multiplied
217      RGB colors.)
218 
219     For every blend mode supported by this class, i.e. the "advanced" blend
220     modes, X=Y=Z=1 and this equation reduces to the PDF blend equation.
221 
222     It can be shown that when X=Y=Z=1, these equations can modulate alpha for
223     coverage.
224 
225 
226     == Color ==
227 
228     We substitute Y=Z=1 and define a blend() function that calculates Dca' in
229     terms of premultiplied alpha only:
230 
231       blend(Sca, Dca, Sa, Da) = {Dca : if Sa == 0,
232                                  Sca : if Da == 0,
233                                  B(Sca/Sa, Dca/Da) * Sa * Da + Sca * (1-Da) + Dca * (1-Sa) : if Sa,Da != 0}
234 
235     And for coverage modulation, we use a post blend src-over model:
236 
237       Dca'' = f * blend(Sca, Dca, Sa, Da) + (1-f) * Dca
238 
239     (Where f is the fractional coverage.)
240 
241     Next we show that canTweakAlphaForCoverage() is true by proving the
242     following relationship:
243 
244       blend(f*Sca, Dca, f*Sa, Da) == f * blend(Sca, Dca, Sa, Da) + (1-f) * Dca
245 
246     General case (f,Sa,Da != 0):
247 
248       f * blend(Sca, Dca, Sa, Da) + (1-f) * Dca
249         = f * (B(Sca/Sa, Dca/Da) * Sa * Da + Sca * (1-Da) + Dca * (1-Sa)) + (1-f) * Dca  [Sa,Da != 0, definition of blend()]
250         = B(Sca/Sa, Dca/Da) * f*Sa * Da + f*Sca * (1-Da) + f*Dca * (1-Sa) + Dca - f*Dca
251         = B(Sca/Sa, Dca/Da) * f*Sa * Da + f*Sca - f*Sca * Da + f*Dca - f*Dca * Sa + Dca - f*Dca
252         = B(Sca/Sa, Dca/Da) * f*Sa * Da + f*Sca - f*Sca * Da - f*Dca * Sa + Dca
253         = B(Sca/Sa, Dca/Da) * f*Sa * Da + f*Sca * (1-Da) - f*Dca * Sa + Dca
254         = B(Sca/Sa, Dca/Da) * f*Sa * Da + f*Sca * (1-Da) + Dca * (1 - f*Sa)
255         = B(f*Sca/f*Sa, Dca/Da) * f*Sa * Da + f*Sca * (1-Da) + Dca * (1 - f*Sa)  [f!=0]
256         = blend(f*Sca, Dca, f*Sa, Da)  [definition of blend()]
257 
258     Corner cases (Sa=0, Da=0, and f=0):
259 
260       Sa=0: f * blend(Sca, Dca, Sa, Da) + (1-f) * Dca
261               = f * Dca + (1-f) * Dca  [Sa=0, definition of blend()]
262               = Dca
263               = blend(0, Dca, 0, Da)  [definition of blend()]
264               = blend(f*Sca, Dca, f*Sa, Da)  [Sa=0]
265 
266       Da=0: f * blend(Sca, Dca, Sa, Da) + (1-f) * Dca
267               = f * Sca + (1-f) * Dca  [Da=0, definition of blend()]
268               = f * Sca  [Da=0]
269               = blend(f*Sca, 0, f*Sa, 0)  [definition of blend()]
270               = blend(f*Sca, Dca, f*Sa, Da)  [Da=0]
271 
272       f=0: f * blend(Sca, Dca, Sa, Da) + (1-f) * Dca
273              = Dca  [f=0]
274              = blend(0, Dca, 0, Da)  [definition of blend()]
275              = blend(f*Sca, Dca, f*Sa, Da)  [f=0]
276 
277     == Alpha ==
278 
279     We substitute X=Y=Z=1 and define a blend() function that calculates Da':
280 
281       blend(Sa, Da) = Sa * Da + Sa * (1-Da) + Da * (1-Sa)
282                     = Sa * Da + Sa - Sa * Da + Da - Da * Sa
283                     = Sa + Da - Sa * Da
284 
285     We use the same model for coverage modulation as we did with color:
286 
287       Da'' = f * blend(Sa, Da) + (1-f) * Da
288 
289     And show that canTweakAlphaForCoverage() is true by proving the following
290     relationship:
291 
292       blend(f*Sa, Da) == f * blend(Sa, Da) + (1-f) * Da
293 
294 
295       f * blend(Sa, Da) + (1-f) * Da
296         = f * (Sa + Da - Sa * Da) + (1-f) * Da
297         = f*Sa + f*Da - f*Sa * Da + Da - f*Da
298         = f*Sa - f*Sa * Da + Da
299         = f*Sa + Da - f*Sa * Da
300         = blend(f*Sa, Da)
301    */
302 
303     OptFlags flags = kNone_OptFlags;
304     if (optimizations.fColorPOI.allStagesMultiplyInput()) {
305         flags |= kCanTweakAlphaForCoverage_OptFlag;
306     }
307     if (this->hasHWBlendEquation() && optimizations.fCoveragePOI.isSolidWhite()) {
308         flags |= kIgnoreCoverage_OptFlag;
309     }
310     return flags;
311 }
312 
onXferBarrier(const GrRenderTarget * rt,const GrCaps & caps) const313 GrXferBarrierType CustomXP::onXferBarrier(const GrRenderTarget* rt, const GrCaps& caps) const {
314     if (this->hasHWBlendEquation() && !caps.advancedCoherentBlendEquationSupport()) {
315         return kBlend_GrXferBarrierType;
316     }
317     return kNone_GrXferBarrierType;
318 }
319 
onGetBlendInfo(BlendInfo * blendInfo) const320 void CustomXP::onGetBlendInfo(BlendInfo* blendInfo) const {
321     if (this->hasHWBlendEquation()) {
322         blendInfo->fEquation = this->hwBlendEquation();
323     }
324 }
325 
326 ///////////////////////////////////////////////////////////////////////////////
327 class CustomXPFactory : public GrXPFactory {
328 public:
329     CustomXPFactory(SkXfermode::Mode mode);
330 
331     void getInvariantBlendedColor(const GrProcOptInfo& colorPOI,
332                                   GrXPFactory::InvariantBlendedColor*) const override;
333 
334 private:
335     GrXferProcessor* onCreateXferProcessor(const GrCaps& caps,
336                                            const GrPipelineOptimizations& optimizations,
337                                            bool hasMixedSamples,
338                                            const DstTexture*) const override;
339 
340     bool onWillReadDstColor(const GrCaps& caps,
341                           const GrPipelineOptimizations& optimizations,
342                           bool hasMixedSamples) const override;
343 
onIsEqual(const GrXPFactory & xpfBase) const344     bool onIsEqual(const GrXPFactory& xpfBase) const override {
345         const CustomXPFactory& xpf = xpfBase.cast<CustomXPFactory>();
346         return fMode == xpf.fMode;
347     }
348 
349     GR_DECLARE_XP_FACTORY_TEST;
350 
351     SkXfermode::Mode fMode;
352     GrBlendEquation  fHWBlendEquation;
353 
354     typedef GrXPFactory INHERITED;
355 };
356 
CustomXPFactory(SkXfermode::Mode mode)357 CustomXPFactory::CustomXPFactory(SkXfermode::Mode mode)
358     : fMode(mode),
359       fHWBlendEquation(hw_blend_equation(mode)) {
360     SkASSERT(GrCustomXfermode::IsSupportedMode(fMode));
361     this->initClassID<CustomXPFactory>();
362 }
363 
onCreateXferProcessor(const GrCaps & caps,const GrPipelineOptimizations & opt,bool hasMixedSamples,const DstTexture * dstTexture) const364 GrXferProcessor* CustomXPFactory::onCreateXferProcessor(const GrCaps& caps,
365                                                         const GrPipelineOptimizations& opt,
366                                                         bool hasMixedSamples,
367                                                         const DstTexture* dstTexture) const {
368     if (can_use_hw_blend_equation(fHWBlendEquation, opt, caps)) {
369         SkASSERT(!dstTexture || !dstTexture->texture());
370         return new CustomXP(fMode, fHWBlendEquation);
371     }
372     return new CustomXP(dstTexture, hasMixedSamples, fMode);
373 }
374 
onWillReadDstColor(const GrCaps & caps,const GrPipelineOptimizations & optimizations,bool hasMixedSamples) const375 bool CustomXPFactory::onWillReadDstColor(const GrCaps& caps,
376                                          const GrPipelineOptimizations& optimizations,
377                                          bool hasMixedSamples) const {
378     return !can_use_hw_blend_equation(fHWBlendEquation, optimizations, caps);
379 }
380 
getInvariantBlendedColor(const GrProcOptInfo & colorPOI,InvariantBlendedColor * blendedColor) const381 void CustomXPFactory::getInvariantBlendedColor(const GrProcOptInfo& colorPOI,
382                                                InvariantBlendedColor* blendedColor) const {
383     blendedColor->fWillBlendWithDst = true;
384     blendedColor->fKnownColorFlags = kNone_GrColorComponentFlags;
385 }
386 
387 GR_DEFINE_XP_FACTORY_TEST(CustomXPFactory);
TestCreate(GrProcessorTestData * d)388 const GrXPFactory* CustomXPFactory::TestCreate(GrProcessorTestData* d) {
389     int mode = d->fRandom->nextRangeU(SkXfermode::kLastCoeffMode + 1,
390                                       SkXfermode::kLastSeparableMode);
391 
392     return new CustomXPFactory(static_cast<SkXfermode::Mode>(mode));
393 }
394 
395 ///////////////////////////////////////////////////////////////////////////////
396 
CreateXPFactory(SkXfermode::Mode mode)397 GrXPFactory* GrCustomXfermode::CreateXPFactory(SkXfermode::Mode mode) {
398     if (!GrCustomXfermode::IsSupportedMode(mode)) {
399         return nullptr;
400     } else {
401         return new CustomXPFactory(mode);
402     }
403 }
404