Chromium Code Reviews| Index: src/gpu/effects/GrAdvancedEquationXferProcessor.cpp |
| diff --git a/src/gpu/effects/GrAdvancedEquationXferProcessor.cpp b/src/gpu/effects/GrAdvancedEquationXferProcessor.cpp |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..cc8f6e7b615ae29b647c133fdd7fec7035c3be50 |
| --- /dev/null |
| +++ b/src/gpu/effects/GrAdvancedEquationXferProcessor.cpp |
| @@ -0,0 +1,316 @@ |
| +/* |
| + * Copyright 2015 Google Inc. |
| + * |
| + * Use of this source code is governed by a BSD-style license that can be |
| + * found in the LICENSE file. |
| + */ |
| + |
| +#include "effects/GrAdvancedEquationXferProcessor.h" |
| + |
| +#include "GrDrawTargetCaps.h" |
| +#include "GrGpu.h" |
| +#include "gl/GrGLXferProcessor.h" |
| +#include "gl/builders/GrGLFragmentShaderBuilder.h" |
| +#include "gl/builders/GrGLProgramBuilder.h" |
| + |
| +class GrAdvancedEquationXPBase : public GrXferProcessor { |
| +public: |
| + GrAdvancedEquationXPBase(GrBlendEquation equation) : fEquation(equation) {} |
| + |
| + const char* name() const override { return "Blend Equation Advanced"; } |
| + bool hasSecondaryOutput() const override { return false; } |
| + |
| + GrXferProcessor::OptFlags getOptimizations(const GrProcOptInfo&, const GrProcOptInfo&, bool, |
| + GrColor*, const GrDrawTargetCaps&) override { |
| + return GrXferProcessor::kNone_Opt; |
| + } |
| + |
| + bool willNeedXferBarrier(const GrRenderTarget* rt, const GrDrawTargetCaps&, |
| + GrXferBarrierType* outBarrierType) const override; |
| + |
| +protected: |
| + void onGetGLProcessorKey(const GrGLCaps&, GrProcessorKeyBuilder*) const override; |
| + |
| + void onGetBlendInfo(BlendInfo*) const override; |
| + |
| + bool onIsEqual(const GrXferProcessor& xpBase) const override { |
| + const GrAdvancedEquationXPBase& xp = xpBase.cast<GrAdvancedEquationXPBase>(); |
| + return fEquation == xp.fEquation; |
| + } |
| + |
| + const GrBlendEquation fEquation; |
| + |
| + typedef GrXferProcessor INHERITED; |
| +}; |
| + |
| +template<bool HasCoverage> class GrAdvancedEquationXferProcessor : public GrAdvancedEquationXPBase { |
| +public: |
| + GrAdvancedEquationXferProcessor(GrBlendEquation equation) : INHERITED(equation) { |
| + this->initClassID<GrAdvancedEquationXferProcessor>(); |
| + } |
| + |
| + GrGLXferProcessor* createGLInstance() const override; |
| + |
| +private: |
| + typedef GrAdvancedEquationXPBase INHERITED; |
| +}; |
| + |
| + |
| +bool GrAdvancedEquationXPFactory::IsSupported(const GrContext* context, SkXfermode::Mode mode) { |
| + if (!context->getGpu()->caps()->advancedBlendEquationSupport()) { |
| + return false; |
| + } |
| + if (mode > SkXfermode::kLastMode) { |
| + return false; |
| + } |
| + return mode == SkXfermode::kScreen_Mode || mode > SkXfermode::kLastCoeffMode; |
| +} |
| + |
| +GrXPFactory* GrAdvancedEquationXPFactory::Create(const GrContext* context, SkXfermode::Mode mode) { |
| + if (!context->getGpu()->caps()->advancedBlendEquationSupport()) { |
| + return NULL; |
| + } |
| + |
| + switch (mode) { |
| + default: break; |
| + case SkXfermode::kScreen_Mode: return RefFactory<kScreen_GrBlendEquation>(); |
| + case SkXfermode::kOverlay_Mode: return RefFactory<kOverlay_GrBlendEquation>(); |
| + case SkXfermode::kDarken_Mode: return RefFactory<kDarken_GrBlendEquation>(); |
| + case SkXfermode::kLighten_Mode: return RefFactory<kLighten_GrBlendEquation>(); |
| + case SkXfermode::kColorDodge_Mode: return RefFactory<kColorDodge_GrBlendEquation>(); |
| + case SkXfermode::kColorBurn_Mode: return RefFactory<kColorBurn_GrBlendEquation>(); |
| + case SkXfermode::kHardLight_Mode: return RefFactory<kHardLight_GrBlendEquation>(); |
| + case SkXfermode::kSoftLight_Mode: return RefFactory<kSoftLight_GrBlendEquation>(); |
| + case SkXfermode::kDifference_Mode: return RefFactory<kDifference_GrBlendEquation>(); |
| + case SkXfermode::kExclusion_Mode: return RefFactory<kExclusion_GrBlendEquation>(); |
| + case SkXfermode::kMultiply_Mode: return RefFactory<kMultiply_GrBlendEquation>(); |
| + case SkXfermode::kHue_Mode: return RefFactory<kHSLHue_GrBlendEquation>(); |
| + case SkXfermode::kSaturation_Mode: return RefFactory<kHSLSaturation_GrBlendEquation>(); |
| + case SkXfermode::kColor_Mode: return RefFactory<kHSLColor_GrBlendEquation>(); |
| + case SkXfermode::kLuminosity_Mode: return RefFactory<kHSLLuminosity_GrBlendEquation>(); |
| + } |
| + |
| + return NULL; |
| +} |
| + |
| +template<GrBlendEquation Equation> GrXPFactory* GrAdvancedEquationXPFactory::RefFactory() { |
| + GR_STATIC_ASSERT(GrBlendEquationIsAdvanced(Equation)); |
| + static GrAdvancedEquationXPFactory* factory; |
| + if (!factory) { |
| + factory = SkNEW_ARGS(GrAdvancedEquationXPFactory, (Equation)); |
| + } |
| + return SkRef(factory); |
| +} |
| + |
| +GrAdvancedEquationXPFactory::GrAdvancedEquationXPFactory(GrBlendEquation equation) |
| + : fEquation(equation), |
| + fXferProcessor(SkNEW_ARGS(GrAdvancedEquationXferProcessor<false>, (fEquation))), |
| + fXferProcessorWithCoverage(SkNEW_ARGS(GrAdvancedEquationXferProcessor<true>, (fEquation))) { |
| +} |
| + |
| +GrAdvancedEquationXPFactory::~GrAdvancedEquationXPFactory() { |
| +} |
| + |
| +bool GrAdvancedEquationXPFactory::canTweakAlphaForCoverage() const { |
| + /* |
| + The general SVG blend equation is defined in the spec as follows: |
| + |
| + Dca' = B(Sc, Dc) * Sa * Da + Y * Sca * (1-Da) + Z * Dca * (1-Sa) |
| + Da' = X * Sa * Da + Y * Sa * (1-Da) + Z * Da * (1-Sa) |
| + |
| + (Note that Sca, Dca indicate RGB vectors that are premultiplied by alpha, |
| + and that B(Sc, Dc) is a mode-specific function that accepts non-multiplied |
| + RGB colors.) |
| + |
| + For every "advanced" blend mode, X=Y=Z=1 and this equation reduces to the |
| + PDF blend equation. |
| + |
| + It can be shown that when X=Y=Z=1, canTweakAlphaForCoverage() is true. |
| + |
| + |
| + == Color == |
| + |
| + We substitute Y=Z=1 and define a blend() function that calculates Dca' in |
| + terms of premultiplied alpha only: |
| + |
| + blend(Sca, Dca, Sa, Da) = {Dca : if Sa == 0, |
| + Sca : if Da == 0, |
| + B(Sca/Sa, Dca/Da) * Sa * Da + Sca * (1-Da) + Dca * (1-Sa) : if Sa,Da != 0} |
| + |
| + And for coverage modulation, we use a post blend src-over model: |
| + |
| + Dca'' = f * blend(Sca, Dca, Sa, Da) + (1-f) * Dca |
| + |
| + (Where f is the fractional coverage.) |
| + |
| + Next we show that canTweakAlphaForCoverage() is true by proving the |
| + following relationship: |
| + |
| + blend(f*Sca, Dca, f*Sa, Da) == f * blend(Sca, Dca, Sa, Da) + (1-f) * Dca |
| + |
| + General case (f,Sa,Da != 0): |
| + |
| + f * blend(Sca, Dca, Sa, Da) + (1-f) * Dca |
| + = f * (B(Sca/Sa, Dca/Da) * Sa * Da + Sca * (1-Da) + Dca * (1-Sa)) + (1-f) * Dca [Sa,Da != 0, definition of blend()] |
| + = B(Sca/Sa, Dca/Da) * f*Sa * Da + f*Sca * (1-Da) + f*Dca * (1-Sa) + Dca - f*Dca |
| + = B(Sca/Sa, Dca/Da) * f*Sa * Da + f*Sca - f*Sca * Da + f*Dca - f*Dca * Sa + Dca - f*Dca |
| + = B(Sca/Sa, Dca/Da) * f*Sa * Da + f*Sca - f*Sca * Da - f*Dca * Sa + Dca |
| + = B(Sca/Sa, Dca/Da) * f*Sa * Da + f*Sca * (1-Da) - f*Dca * Sa + Dca |
| + = B(Sca/Sa, Dca/Da) * f*Sa * Da + f*Sca * (1-Da) + Dca * (1 - f*Sa) |
| + = B(f*Sca/f*Sa, Dca/Da) * f*Sa * Da + f*Sca * (1-Da) + Dca * (1 - f*Sa) [f!=0] |
| + = blend(f*Sca, Dca, f*Sa, Da) [definition of blend()] |
| + |
| + Corner cases (Sa=0, Da=0, and f=0): |
| + |
| + Sa=0: f * blend(Sca, Dca, Sa, Da) + (1-f) * Dca |
| + = f * Dca + (1-f) * Dca [Sa=0, definition of blend()] |
| + = Dca |
| + = blend(0, Dca, 0, Da) [definition of blend()] |
| + = blend(f*Sca, Dca, f*Sa, Da) [Sa=0] |
| + |
| + Da=0: f * blend(Sca, Dca, Sa, Da) + (1-f) * Dca |
| + = f * Sca + (1-f) * Dca [Da=0, definition of blend()] |
| + = f * Sca [Da=0] |
| + = blend(f*Sca, 0, f*Sa, 0) [definition of blend()] |
| + = blend(f*Sca, Dca, f*Sa, Da) [Da=0] |
| + |
| + f=0: f * blend(Sca, Dca, Sa, Da) + (1-f) * Dca |
| + = Dca [f=0] |
| + = blend(0, Dca, 0, Da) [definition of blend()] |
| + = blend(f*Sca, Dca, f*Sa, Da) [f=0] |
| + |
| + == Alpha == |
| + |
| + We substitute X=Y=Z=1 and define a blend() function that calculates Da': |
| + |
| + blend(Sa, Da) = Sa * Da + Sa * (1-Da) + Da * (1-Sa) |
| + = Sa * Da + Sa - Sa * Da + Da - Da * Sa |
| + = Sa + Da - Sa * Da |
| + |
| + We use the same model for coverage modulation as we did with color: |
| + |
| + Da'' = f * blend(Sa, Da) + (1-f) * Da |
| + |
| + And show that canTweakAlphaForCoverage() is true by proving the following |
| + relationship: |
| + |
| + blend(f*Sa, Da) == f * blend(Sa, Da) + (1-f) * Da |
| + |
| + |
| + f * blend(Sa, Da) + (1-f) * Da |
| + = f * (Sa + Da - Sa * Da) + (1-f) * Da |
| + = f*Sa + f*Da - f*Sa * Da + Da - f*Da |
| + = f*Sa - f*Sa * Da + Da |
| + = f*Sa + Da - f*Sa * Da |
| + = blend(f*Sa, Da) |
| + */ |
| + |
| + return true; |
| +} |
| + |
| +bool GrAdvancedEquationXPFactory::supportsRGBCoverage(GrColor, uint32_t) const { |
| + /* |
| + We do coverage modulation by multiplying it into the src color before |
| + blending. The GPU runs the blend equation *after* the fragment shader, so |
| + it is not within our control to apply coverage at that point. This model |
| + is mathematically correct, but it only works with scalar coverage. It's easy |
| + to see why it won't work when coverage is unique per component: |
| + |
| + blend(fc*Sca, Dca, fa*Sa, Da) |
| + = B((fc*Sca)/(fa*Sa), Dca/Da) * ... |
| + != B(Sc, Dc) * ... |
| + |
| + The arguments for B() no longer reduce to (Sc, Dc). And when Sc == 0, the |
| + arguments for B() work, but the equation still doesn't: |
| + |
| + fc * blend(0, Dca, Sa, Da) + (1-fc) * Dca |
| + = ... |
| + = B(0, Dca/Da) * fc*Sa * Da + fc*0 * (1-Da) + Dca * (1 - fc*Sa) |
| + = blend(fc*0, Dca, fc*Sa, Da) |
| + != blend(fc*0, Dca, fa*Sa, Da) |
| + |
| + (Note that this could work if we knew the dst was transparent black.) |
| + */ |
| + |
| + return false; |
| +} |
| + |
| +GrXferProcessor* GrAdvancedEquationXPFactory::onCreateXferProcessor( |
| + const GrDrawTargetCaps& caps, |
| + const GrProcOptInfo& colorPOI, |
| + const GrProcOptInfo& coveragePOI, |
| + const GrDeviceCoordTexture* dstCopy) const { |
| + SkASSERT(caps.advancedBlendEquationSupport()); |
| + SkASSERT(!dstCopy || !dstCopy->texture()); |
| + |
| + if (coveragePOI.isFourChannelOutput()) { |
| + // RGB coverage is not supported. (See supportsRGBCoverage()) |
| + return NULL; |
| + } |
| + |
| + return SkRef(coveragePOI.isSolidWhite() ? fXferProcessor.get() |
| + : fXferProcessorWithCoverage.get()); |
|
Chris Dalton
2015/04/17 08:37:19
TODO: This causes an assert:
../../src/gpu/GrMe
|
| +} |
| + |
| +bool GrAdvancedEquationXPBase::willNeedXferBarrier(const GrRenderTarget*, |
| + const GrDrawTargetCaps& caps, |
| + GrXferBarrierType* outBarrierType) const { |
| + if (GrDrawTargetCaps::kAdvancedCoherent_BlendEquationSupport != caps.blendEquationSupport()) { |
| + *outBarrierType = kBlend_GrXferBarrierType; |
| + return true; |
| + } |
| + return false; |
| +} |
| + |
| +void GrAdvancedEquationXPBase::onGetGLProcessorKey(const GrGLCaps&, GrProcessorKeyBuilder*) const { |
| + // Our XP's don't emit variable fragment code. (HasCoverage is accounted for by the class ID.) |
| + return; |
| +} |
| + |
| +template</*HasCoverage = false*/> |
| +GrGLXferProcessor* GrAdvancedEquationXferProcessor<false>::createGLInstance() const { |
| + class GLAdvancedEquationXP : public GrGLXferProcessor { |
| + private: |
| + void onEmitCode(const EmitArgs& args) override { |
| + GrGLFPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder(); |
| + fsBuilder->codeAppendf("%s = %s;", args.fOutputPrimary, args.fInputColor); |
| + } |
| + void onSetData(const GrGLProgramDataManager&, const GrXferProcessor&) override {} |
| + }; |
| + return SkNEW(GLAdvancedEquationXP); |
| +} |
| + |
| +template</*HasCoverage = true*/> |
| +GrGLXferProcessor* GrAdvancedEquationXferProcessor<true>::createGLInstance() const { |
| + class GLAdvancedEquationXPWithCoverage : public GrGLXferProcessor { |
| + private: |
| + void onEmitCode(const EmitArgs& args) override { |
| + // We do coverage modulation by multiplying it into the src color before blending. |
| + // (See canTweakAlphaForCoverage()) |
| + GrGLFPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder(); |
| + fsBuilder->codeAppendf("%s = %s * %s;", |
| + args.fOutputPrimary, args.fInputColor, args.fInputCoverage); |
| + } |
| + void onSetData(const GrGLProgramDataManager&, const GrXferProcessor&) override {} |
| + }; |
| + return SkNEW(GLAdvancedEquationXPWithCoverage); |
| +} |
| + |
| +void GrAdvancedEquationXPBase::onGetBlendInfo(BlendInfo* blendInfo) const { |
| + SkASSERT(GrBlendEquationIsAdvanced(fEquation)); |
| + blendInfo->fEquation = fEquation; |
| +} |
| + |
| + |
| +GR_DEFINE_XP_FACTORY_TEST(GrAdvancedEquationXPFactory); |
| + |
| +GrXPFactory* GrAdvancedEquationXPFactory::TestCreate(SkRandom* random, |
| + GrContext* context, |
| + const GrDrawTargetCaps& caps, |
| + GrTexture*[]) { |
| + int mode = random->nextRangeU(SkXfermode::kLastCoeffMode, SkXfermode::kLastMode); |
| + if (SkXfermode::kLastCoeffMode == mode) { |
| + mode = SkXfermode::kScreen_Mode; |
| + } |
| + return GrAdvancedEquationXPFactory::Create(context, static_cast<SkXfermode::Mode>(mode)); |
| +} |