Chromium Code Reviews| Index: src/gpu/effects/GrCustomXfermode.cpp |
| diff --git a/src/gpu/effects/GrCustomXfermode.cpp b/src/gpu/effects/GrCustomXfermode.cpp |
| index 2f8a970290fc02aecad7c8167ecbffc249f1e7aa..bbc3c26e16d54f3a142ca317dc420a7933bd9b95 100644 |
| --- a/src/gpu/effects/GrCustomXfermode.cpp |
| +++ b/src/gpu/effects/GrCustomXfermode.cpp |
| @@ -17,6 +17,7 @@ |
| #include "GrTextureAccess.h" |
| #include "SkXfermode.h" |
| #include "gl/GrGLCaps.h" |
| +#include "gl/GrGLGpu.h" |
| #include "gl/GrGLProcessor.h" |
| #include "gl/GrGLProgramDataManager.h" |
| #include "gl/builders/GrGLProgramBuilder.h" |
| @@ -29,6 +30,27 @@ bool GrCustomXfermode::IsSupportedMode(SkXfermode::Mode mode) { |
| // Static helpers |
| /////////////////////////////////////////////////////////////////////////////// |
| +static GrBlendEquation hw_blend_equation(SkXfermode::Mode mode) { |
| + enum { kOffset = kOverlay_GrBlendEquation - SkXfermode::kOverlay_Mode }; |
| + return static_cast<GrBlendEquation>(mode + kOffset); |
| + |
| + GR_STATIC_ASSERT(kOverlay_GrBlendEquation == SkXfermode::kOverlay_Mode + kOffset); |
| + GR_STATIC_ASSERT(kDarken_GrBlendEquation == SkXfermode::kDarken_Mode + kOffset); |
| + GR_STATIC_ASSERT(kLighten_GrBlendEquation == SkXfermode::kLighten_Mode + kOffset); |
| + GR_STATIC_ASSERT(kColorDodge_GrBlendEquation == SkXfermode::kColorDodge_Mode + kOffset); |
| + GR_STATIC_ASSERT(kColorBurn_GrBlendEquation == SkXfermode::kColorBurn_Mode + kOffset); |
| + GR_STATIC_ASSERT(kHardLight_GrBlendEquation == SkXfermode::kHardLight_Mode + kOffset); |
| + GR_STATIC_ASSERT(kSoftLight_GrBlendEquation == SkXfermode::kSoftLight_Mode + kOffset); |
| + GR_STATIC_ASSERT(kDifference_GrBlendEquation == SkXfermode::kDifference_Mode + kOffset); |
| + GR_STATIC_ASSERT(kExclusion_GrBlendEquation == SkXfermode::kExclusion_Mode + kOffset); |
| + GR_STATIC_ASSERT(kMultiply_GrBlendEquation == SkXfermode::kMultiply_Mode + kOffset); |
| + GR_STATIC_ASSERT(kHSLHue_GrBlendEquation == SkXfermode::kHue_Mode + kOffset); |
| + GR_STATIC_ASSERT(kHSLSaturation_GrBlendEquation == SkXfermode::kSaturation_Mode + kOffset); |
| + GR_STATIC_ASSERT(kHSLColor_GrBlendEquation == SkXfermode::kColor_Mode + kOffset); |
| + GR_STATIC_ASSERT(kHSLLuminosity_GrBlendEquation == SkXfermode::kLuminosity_Mode + kOffset); |
| + GR_STATIC_ASSERT(kTotalGrBlendEquationCount == SkXfermode::kLastMode + 1 + kOffset); |
| +} |
| + |
| static void hard_light(GrGLFragmentBuilder* fsBuilder, |
| const char* final, |
| const char* src, |
| @@ -510,15 +532,30 @@ public: |
| const GrDrawTargetCaps& caps) override; |
| SkXfermode::Mode mode() const { return fMode; } |
| + bool hasCoverage() const { return fHasCoverage; } |
| + bool hasHWBlendEquation() const { return kInvalid_GrBlendEquation != fHWBlendEquation; } |
| + |
| + GrBlendEquation hwBlendEquation() const { |
| + SkASSERT(this->hasHWBlendEquation()); |
| + return fHWBlendEquation; |
| + } |
| private: |
| CustomXP(SkXfermode::Mode mode, const GrDeviceCoordTexture* dstCopy, bool willReadDstColor); |
| void onGetGLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override; |
| + bool onWillNeedXferBarrier(const GrRenderTarget* rt, |
| + const GrDrawTargetCaps& caps, |
| + GrXferBarrierType* outBarrierType) const override; |
| + |
| + void onGetBlendInfo(BlendInfo*) const override; |
| + |
| bool onIsEqual(const GrXferProcessor& xpBase) const override; |
| SkXfermode::Mode fMode; |
| + bool fHasCoverage; |
| + GrBlendEquation fHWBlendEquation; |
| typedef GrXferProcessor INHERITED; |
| }; |
| @@ -540,24 +577,51 @@ public: |
| GLCustomXP(const GrXferProcessor&) {} |
| ~GLCustomXP() override {} |
| - static void GenKey(const GrXferProcessor& proc, const GrGLSLCaps&, GrProcessorKeyBuilder* b) { |
| - uint32_t key = proc.numTextures(); |
| + static void GenKey(const GrXferProcessor& p, const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) { |
| + const CustomXP& xp = p.cast<CustomXP>(); |
| + uint32_t key = xp.numTextures(); |
| SkASSERT(key <= 1); |
| - key |= proc.cast<CustomXP>().mode() << 1; |
| + key |= xp.hasCoverage() << 1; |
| + key |= xp.hasHWBlendEquation() << 2; |
|
egdaniel
2015/05/05 21:20:01
I'm trying to think if we can simplify this logic
Chris Dalton
2015/05/06 16:06:27
Good idea. I like it a lot better now.
|
| + if (xp.hasHWBlendEquation()) { |
| + key |= caps.advancedBlendEqnInteraction() << 3; |
| + if (GrGLSLCaps::kMustEnableSeparately_AdvancedBlendEqnInteraction == |
| + caps.advancedBlendEqnInteraction()) { |
| + GR_STATIC_ASSERT(GrGLSLCaps::kLast_AdvancedBlendEqnInteraction <= 3); |
| + key |= xp.hwBlendEquation() << 5; |
| + } |
| + } else { |
| + key |= xp.mode() << 3; |
| + } |
| b->add32(key); |
| } |
| private: |
| void onEmitCode(const EmitArgs& args) override { |
| - SkXfermode::Mode mode = args.fXP.cast<CustomXP>().mode(); |
| + const CustomXP& xp = args.fXP.cast<CustomXP>(); |
| GrGLXPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder(); |
| - const char* dstColor = fsBuilder->dstColor(); |
| - emit_custom_xfermode_code(mode, fsBuilder, args.fOutputPrimary, args.fInputColor, dstColor); |
| - |
| - fsBuilder->codeAppendf("%s = %s * %s + (vec4(1.0) - %s) * %s;", |
| - args.fOutputPrimary, args.fOutputPrimary, args.fInputCoverage, |
| - args.fInputCoverage, dstColor); |
| + if (xp.hasHWBlendEquation()) { |
| + // The blend mode will be implemented in hardware; only output the src color. |
| + fsBuilder->enableAdvancedBlendEquationIfNeeded(xp.hwBlendEquation()); |
| + if (xp.hasCoverage()) { |
| + // Do coverage modulation by multiplying it into the src color before blending. |
| + // (See getOptimizations()) |
| + fsBuilder->codeAppendf("%s = %s * %s;", |
| + args.fOutputPrimary, args.fInputCoverage, args.fInputColor); |
| + } else { |
| + fsBuilder->codeAppendf("%s = %s;", args.fOutputPrimary, args.fInputColor); |
| + } |
| + } else { |
| + const char* dstColor = fsBuilder->dstColor(); |
| + emit_custom_xfermode_code(xp.mode(), fsBuilder, args.fOutputPrimary, args.fInputColor, |
| + dstColor); |
| + if (xp.hasCoverage()) { |
| + fsBuilder->codeAppendf("%s = %s * %s + (vec4(1.0) - %s) * %s;", |
| + args.fOutputPrimary, args.fOutputPrimary, |
| + args.fInputCoverage, args.fInputCoverage, dstColor); |
| + } |
| + } |
| } |
| void onSetData(const GrGLProgramDataManager&, const GrXferProcessor&) override {} |
| @@ -569,7 +633,10 @@ private: |
| CustomXP::CustomXP(SkXfermode::Mode mode, const GrDeviceCoordTexture* dstCopy, |
| bool willReadDstColor) |
| - : INHERITED(dstCopy, willReadDstColor), fMode(mode) { |
| + : INHERITED(dstCopy, willReadDstColor), |
| + fMode(mode), |
| + fHasCoverage(true), |
| + fHWBlendEquation(kInvalid_GrBlendEquation) { |
| this->initClassID<CustomXP>(); |
| } |
| @@ -578,12 +645,15 @@ void CustomXP::onGetGLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder |
| } |
| GrGLXferProcessor* CustomXP::createGLInstance() const { |
| + SkASSERT(this->willReadDstColor() != this->hasHWBlendEquation()); |
| return SkNEW_ARGS(GLCustomXP, (*this)); |
| } |
| bool CustomXP::onIsEqual(const GrXferProcessor& other) const { |
| const CustomXP& s = other.cast<CustomXP>(); |
| - return fMode == s.fMode; |
| + return fMode == s.fMode && |
| + fHasCoverage == s.fHasCoverage && |
| + fHWBlendEquation == s.fHWBlendEquation; |
| } |
| GrXferProcessor::OptFlags CustomXP::getOptimizations(const GrProcOptInfo& colorPOI, |
| @@ -591,7 +661,132 @@ GrXferProcessor::OptFlags CustomXP::getOptimizations(const GrProcOptInfo& colorP |
| bool doesStencilWrite, |
| GrColor* overrideColor, |
| const GrDrawTargetCaps& caps) { |
| - return GrXferProcessor::kNone_Opt; |
| + /* |
| + Most the optimizations we do here are based on tweaking alpha for coverage. |
| + |
| + 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 blend mode supported by this class, i.e. the "advanced" blend |
| + modes, X=Y=Z=1 and this equation reduces to the PDF blend equation. |
| + |
| + It can be shown that when X=Y=Z=1, these equations can modulate alpha for |
| + coverage. |
| + |
| + |
| + == 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) |
| + */ |
| + |
| + OptFlags flags = kNone_Opt; |
| + if (colorPOI.allStagesMultiplyInput()) { |
| + flags = flags | kCanTweakAlphaForCoverage_OptFlag; |
| + } |
| + if (coveragePOI.isSolidWhite()) { |
| + flags = flags | kIgnoreCoverage_OptFlag; |
| + fHasCoverage = false; |
| + } |
| + if (caps.advancedBlendEquationSupport() && !coveragePOI.isFourChannelOutput()) { |
| + // This blend mode can be implemented in hardware. |
| + fHWBlendEquation = hw_blend_equation(fMode); |
| + } |
| + return flags; |
| +} |
| + |
| +bool CustomXP::onWillNeedXferBarrier(const GrRenderTarget* rt, |
| + const GrDrawTargetCaps& caps, |
| + GrXferBarrierType* outBarrierType) const { |
| + if (this->hasHWBlendEquation() && |
| + GrDrawTargetCaps::kAdvancedCoherent_BlendEquationSupport != caps.blendEquationSupport()) { |
| + *outBarrierType = kBlend_GrXferBarrierType; |
| + return true; |
| + } |
| + return false; |
| +} |
| + |
| +void CustomXP::onGetBlendInfo(BlendInfo* blendInfo) const { |
| + if (this->hasHWBlendEquation()) { |
| + blendInfo->fEquation = this->hwBlendEquation(); |
| + } |
| } |
| /////////////////////////////////////////////////////////////////////////////// |
| @@ -609,6 +804,19 @@ GrCustomXPFactory::onCreateXferProcessor(const GrDrawTargetCaps& caps, |
| return CustomXP::Create(fMode, dstCopy, this->willReadDstColor(caps, colorPOI, coveragePOI)); |
| } |
| +bool GrCustomXPFactory::willReadDstColor(const GrDrawTargetCaps& caps, |
| + const GrProcOptInfo& colorPOI, |
| + const GrProcOptInfo& coveragePOI) const { |
| + if (!caps.advancedBlendEquationSupport()) { |
| + // No hardware support for advanced blend equations; we will need to do it in the shader. |
| + return true; |
| + } |
| + if (coveragePOI.isFourChannelOutput()) { |
| + // Advanced blend equations can't tweak alpha for RGB coverage. |
| + return true; |
| + } |
| + return false; |
| +} |
| void GrCustomXPFactory::getInvariantOutput(const GrProcOptInfo& colorPOI, |
| const GrProcOptInfo& coveragePOI, |