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

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

Issue 1037123003: Implement support for KHR_blend_equation_advanced (Closed) Base URL: https://skia.googlesource.com/skia.git@upload_zzz2_barriers
Patch Set: Created 5 years, 9 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/GrAdvancedEquationXPFactory.cpp
diff --git a/src/gpu/effects/GrAdvancedEquationXPFactory.cpp b/src/gpu/effects/GrAdvancedEquationXPFactory.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..def4cca2ceeeb866e99f7c01c587a577c583f94f
--- /dev/null
+++ b/src/gpu/effects/GrAdvancedEquationXPFactory.cpp
@@ -0,0 +1,259 @@
+/*
+ * 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/GrAdvancedEquationXPFactory.h"
+
+#include "GrDrawTargetCaps.h"
+#include "GrGpu.h"
+#include "gl/GrGLXferProcessor.h"
+#include "gl/builders/GrGLFragmentShaderBuilder.h"
+#include "gl/builders/GrGLProgramBuilder.h"
+
+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;
+}
+
+template<GrBlendEquation Equation> GrXPFactory* GrAdvancedEquationXPFactory::TakeFactoryRef() {
+ GR_STATIC_ASSERT(GrBlendEquationIsAdvanced(Equation));
+ static GrAdvancedEquationXPFactory* factory;
+ if (!factory) {
+ factory = SkNEW_ARGS(GrAdvancedEquationXPFactory, (Equation));
+ }
+ return SkRef(factory);
+}
+
+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 TakeFactoryRef<kScreen_GrBlendEquation>();
+ case SkXfermode::kOverlay_Mode: return TakeFactoryRef<kOverlay_GrBlendEquation>();
+ case SkXfermode::kDarken_Mode: return TakeFactoryRef<kDarken_GrBlendEquation>();
+ case SkXfermode::kLighten_Mode: return TakeFactoryRef<kLighten_GrBlendEquation>();
+ case SkXfermode::kColorDodge_Mode: return TakeFactoryRef<kColorDodge_GrBlendEquation>();
+ case SkXfermode::kColorBurn_Mode: return TakeFactoryRef<kColorBurn_GrBlendEquation>();
+ case SkXfermode::kHardLight_Mode: return TakeFactoryRef<kHardLight_GrBlendEquation>();
+ case SkXfermode::kSoftLight_Mode: return TakeFactoryRef<kSoftLight_GrBlendEquation>();
+ case SkXfermode::kDifference_Mode: return TakeFactoryRef<kDifference_GrBlendEquation>();
+ case SkXfermode::kExclusion_Mode: return TakeFactoryRef<kExclusion_GrBlendEquation>();
+ case SkXfermode::kMultiply_Mode: return TakeFactoryRef<kMultiply_GrBlendEquation>();
+ case SkXfermode::kHue_Mode: return TakeFactoryRef<kHSLHue_GrBlendEquation>();
+ case SkXfermode::kSaturation_Mode: return TakeFactoryRef<kHSLSaturation_GrBlendEquation>();
+ case SkXfermode::kColor_Mode: return TakeFactoryRef<kHSLColor_GrBlendEquation>();
+ case SkXfermode::kLuminosity_Mode: return TakeFactoryRef<kHSLLuminosity_GrBlendEquation>();
+ }
+
+ return NULL;
+}
+
+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.
Mark Kilgard 2015/04/03 18:30:03 great to see this analysis
+
+
+ == 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, Skia uses 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
+
+ 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]
+
+ 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()]
Mark Kilgard 2015/04/03 18:30:04 great analysis I think you should motivate the "C
Chris Dalton 2015/04/17 08:37:18 Done.
+
+
+ == 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 knownColor,
+ uint32_t knownColorFlags) 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 the proof lies in the following property:
+
+ f*Sca / f*Sa == Sc
+
+ (That is, the general case, f,Sa,Da != 0. See canTweakAlphaForCoverage().)
+
+ But when coverage is unique per channel, this property no longer holds:
Mark Kilgard 2015/04/03 18:30:03 "when coverage is unique per channel" ?? huh? Wh
Chris Dalton 2015/04/17 08:37:19 Yeah, "RGB coverage" in the context of GrXferProce
+
+ fc*Sca / fa*Sa == fc/fa * Sc != Sc [fc!=fa]
+
+ The only time it is still true is when Sc == 0.
Chris Dalton 2015/03/31 10:51:49 This is most likely wrong. I'll update the comment
+
+ (It would also work out if we could know the dst color was trans black.)
Mark Kilgard 2015/04/03 18:30:03 I'm assume by "trans black" you mean transparent b
+ */
+
+ return kRGB_GrColorComponentFlags == (knownColorFlags & kRGB_GrColorComponentFlags) &&
+ 0 == (knownColor & ~(0xffu << GrColor_SHIFT_A)); // RGB=0
Mark Kilgard 2015/04/03 18:30:03 Is there not some constant to use instead of 0xffu
Chris Dalton 2015/04/17 08:37:19 That code is completely gone now.
+}
+
+bool GrAdvancedEquationXPFactory::willBlendCoherently(const GrDrawTargetCaps& caps) const {
+ return GrDrawTargetCaps::kAdvancedCoherent_BlendEquationSupport == caps.blendEquationSupport();
+}
+
+GrXferProcessor* GrAdvancedEquationXPFactory::onCreateXferProcessor(
+ const GrDrawTargetCaps& caps,
+ const GrProcOptInfo& colorPOI,
+ const GrProcOptInfo& coveragePOI,
Mark Kilgard 2015/04/03 18:30:03 wish I knew what "XP" stands for can I assume POI
Chris Dalton 2015/04/17 08:37:19 XP = Xfer Processor = An object that controls the
+ const GrDeviceCoordTexture* dstCopy) const {
+ SkASSERT(caps.advancedBlendEquationSupport());
+ SkASSERT(!dstCopy || !dstCopy->texture());
+
+ if (coveragePOI.isFourChannelOutput() && !this->supportsRGBCoverage(colorPOI.color(),
+ colorPOI.validFlags())) {
+ return NULL;
+ }
+ if (!coveragePOI.isSolidWhite()) {
+ return SkRef(&fXferProcessorWithCoverage);
+ }
+ return SkRef(&fXferProcessor);
+}
+
+void GrAdvancedEquationXPFactory::XferProcessorBase::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 = true*/>
+GrGLXferProcessor* GrAdvancedEquationXPFactory::XferProcessor<true>::createGLInstance() const {
+ class GLXferProcessorWithCoverage : 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(GLXferProcessorWithCoverage);
+}
+
+template</*HasCoverage = false*/>
+GrGLXferProcessor* GrAdvancedEquationXPFactory::XferProcessor<false>::createGLInstance() const {
+ class GLXferProcessor : 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(GLXferProcessor);
+}
+
+void GrAdvancedEquationXPFactory::XferProcessorBase::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 (mode == SkXfermode::kLastCoeffMode) {
+ mode = SkXfermode::kScreen_Mode;
+ }
+ return GrAdvancedEquationXPFactory::Create(context, static_cast<SkXfermode::Mode>(mode));
+}
« no previous file with comments | « src/gpu/SkGr.cpp ('k') | src/gpu/gl/GrGLCaps.cpp » ('j') | src/gpu/gl/GrGLCaps.cpp » ('J')

Powered by Google App Engine
This is Rietveld 408576698