aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/gpu/effects
diff options
context:
space:
mode:
authorGravatar cdalton <cdalton@nvidia.com>2015-05-06 13:40:21 -0700
committerGravatar Commit bot <commit-bot@chromium.org>2015-05-06 13:40:21 -0700
commit8917d62ef4d9bde9ec4f879dc42b309b03a0ad98 (patch)
tree155b2ec06eecc8aadee64f9152b6ea95515778ce /src/gpu/effects
parente0cab96599764f7611de015f558a6b22162c3eda (diff)
Implement support for KHR_blend_equation_advanced
Uses KHR(or NV)_blend_equation_advanced to implement custom Xfer modes in hardware. BUG=skia: Review URL: https://codereview.chromium.org/1037123003
Diffstat (limited to 'src/gpu/effects')
-rw-r--r--src/gpu/effects/GrCustomXfermode.cpp230
-rw-r--r--src/gpu/effects/GrCustomXfermodePriv.h4
2 files changed, 218 insertions, 16 deletions
diff --git a/src/gpu/effects/GrCustomXfermode.cpp b/src/gpu/effects/GrCustomXfermode.cpp
index 2f8a970290..b15e2c2b9a 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,48 @@ 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;
+ if (xp.hasHWBlendEquation()) {
+ SkASSERT(caps.advBlendEqInteraction() > 0); // 0 will mean !xp.hasHWBlendEquation().
+ key |= caps.advBlendEqInteraction() << 2;
+ }
+ if (!xp.hasHWBlendEquation() || caps.mustEnableSpecificAdvBlendEqs()) {
+ GR_STATIC_ASSERT(GrGLSLCaps::kLast_AdvBlendEqInteraction < 4);
+ key |= xp.mode() << 4;
+ }
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 +630,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 +642,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 +658,131 @@ 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() && !caps.advancedCoherentBlendEquationSupport()) {
+ *outBarrierType = kBlend_GrXferBarrierType;
+ return true;
+ }
+ return false;
+}
+
+void CustomXP::onGetBlendInfo(BlendInfo* blendInfo) const {
+ if (this->hasHWBlendEquation()) {
+ blendInfo->fEquation = this->hwBlendEquation();
+ }
}
///////////////////////////////////////////////////////////////////////////////
@@ -609,6 +800,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,
diff --git a/src/gpu/effects/GrCustomXfermodePriv.h b/src/gpu/effects/GrCustomXfermodePriv.h
index edcdcc0a6e..43aaeabc54 100644
--- a/src/gpu/effects/GrCustomXfermodePriv.h
+++ b/src/gpu/effects/GrCustomXfermodePriv.h
@@ -75,9 +75,7 @@ private:
bool willReadDstColor(const GrDrawTargetCaps& caps,
const GrProcOptInfo& colorPOI,
- const GrProcOptInfo& coveragePOI) const override {
- return true;
- }
+ const GrProcOptInfo& coveragePOI) const override;
bool onIsEqual(const GrXPFactory& xpfBase) const override {
const GrCustomXPFactory& xpf = xpfBase.cast<GrCustomXPFactory>();