Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(313)

Unified Diff: src/gpu/effects/GrAdvancedEquationXferProcessor.cpp

Issue 1037123003: Implement support for KHR_blend_equation_advanced (Closed) Base URL: https://skia.googlesource.com/skia.git@upload_zzz2_barriers
Patch Set: Properly track the previous patch Created 5 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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,
egdaniel 2015/04/21 18:59:12 Can we move this to the derived class and then ret
Chris Dalton 2015/04/21 20:39:31 Ok, that's a good idea.
+ 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()
egdaniel 2015/04/21 18:59:12 As per your comment on previous patch, there reall
Chris Dalton 2015/04/21 20:39:31 I'm not sure I completely follow option #1, how do
egdaniel 2015/04/21 20:53:49 Sorry wasn't the most clear typing. You can't get
+ : fXferProcessorWithCoverage.get());
+}
+
+bool GrAdvancedEquationXPBase::willNeedXferBarrier(const GrRenderTarget*,
+ const GrDrawTargetCaps& caps,
+ GrXferBarrierType* outBarrierType) const {
+ if (GrDrawTargetCaps::kAdvancedCoherent_BlendEquationSupport != caps.blendEquationSupport()) {
egdaniel 2015/04/21 18:59:12 Just since I don't know these extensions very well
Chris Dalton 2015/04/21 20:39:31 Yes, that's the difference. For "coherent" mode,
+ *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));
+}

Powered by Google App Engine
This is Rietveld 408576698