Index: src/gpu/effects/GrPorterDuffXferProcessor.cpp |
diff --git a/src/gpu/effects/GrPorterDuffXferProcessor.cpp b/src/gpu/effects/GrPorterDuffXferProcessor.cpp |
index d1e02d2c2b28b8f586c414018f1deff1e58940a9..812cfeaaa4472e4bef2a4a170e7bdd4a22b19265 100644 |
--- a/src/gpu/effects/GrPorterDuffXferProcessor.cpp |
+++ b/src/gpu/effects/GrPorterDuffXferProcessor.cpp |
@@ -17,79 +17,305 @@ |
#include "gl/builders/GrGLFragmentShaderBuilder.h" |
#include "gl/builders/GrGLProgramBuilder.h" |
-static bool can_tweak_alpha_for_coverage(GrBlendCoeff dstCoeff) { |
- /* |
- The fractional coverage is f. |
- The src and dst coeffs are Cs and Cd. |
- The dst and src colors are S and D. |
- We want the blend to compute: f*Cs*S + (f*Cd + (1-f))D. By tweaking the source color's alpha |
- we're replacing S with S'=fS. It's obvious that that first term will always be ok. The second |
- term can be rearranged as [1-(1-Cd)f]D. By substituting in the various possibilities for Cd we |
- find that only 1, ISA, and ISC produce the correct destination when applied to S' and D. |
+/** |
+ * Wraps the shader outputs and HW blend state that comprise a Porter Duff blend mode with coverage. |
+ */ |
+struct BlendFormula { |
+public: |
+ /** |
+ * Values the shader can write to primary and secondary outputs. These must all be modulated by |
+ * coverage to support mixed samples. The XP will ignore the multiplies when not using coverage. |
*/ |
- return kOne_GrBlendCoeff == dstCoeff || |
- kISA_GrBlendCoeff == dstCoeff || |
- kISC_GrBlendCoeff == dstCoeff; |
-} |
+ enum OutputType { |
+ kNone_OutputType, //<! 0 |
+ kCoverage_OutputType, //<! inputCoverage |
+ kModulate_OutputType, //<! inputColor * inputCoverage |
+ kISAModulate_OutputType, //<! (1 - inputColor.a) * inputCoverage |
+ kISCModulate_OutputType, //<! (1 - inputColor) * inputCoverage |
+ |
+ kLast_OutputType = kISCModulate_OutputType |
+ }; |
-class PorterDuffXferProcessor : public GrXferProcessor { |
-public: |
- static GrXferProcessor* Create(GrBlendCoeff srcBlend, GrBlendCoeff dstBlend, |
- GrColor constant, const DstTexture* dstTexture, |
- bool willReadDstColor) { |
- return SkNEW_ARGS(PorterDuffXferProcessor, (srcBlend, dstBlend, constant, dstTexture, |
- willReadDstColor)); |
- } |
+ enum Properties { |
+ kModifiesDst_Property = 1, |
+ kUsesDstColor_Property = 1 << 1, |
+ kUsesInputColor_Property = 1 << 2, |
+ kCanTweakAlphaForCoverage_Property = 1 << 3, |
+ |
+ kLast_Property = kCanTweakAlphaForCoverage_Property |
+ }; |
- ~PorterDuffXferProcessor() override; |
+ BlendFormula& operator =(const BlendFormula& other) { |
+ fData = other.fData; |
+ return *this; |
+ } |
- const char* name() const override { return "Porter Duff"; } |
+ bool operator ==(const BlendFormula& other) const { |
+ return fData == other.fData; |
+ } |
- GrGLXferProcessor* createGLInstance() const override; |
+ bool hasSecondaryOutput() const { return kNone_OutputType != fSecondaryOutputType; } |
+ bool modifiesDst() const { return SkToBool(fProps & kModifiesDst_Property); } |
+ bool usesDstColor() const { return SkToBool(fProps & kUsesDstColor_Property); } |
+ bool usesInputColor() const { return SkToBool(fProps & kUsesInputColor_Property); } |
+ bool canTweakAlphaForCoverage() const { |
+ return SkToBool(fProps & kCanTweakAlphaForCoverage_Property); |
+ } |
- bool hasSecondaryOutput() const override; |
- |
- /////////////////////////////////////////////////////////////////////////// |
- /// @name Stage Output Types |
- //// |
- |
- enum PrimaryOutputType { |
- kNone_PrimaryOutputType, |
- kColor_PrimaryOutputType, |
- kCoverage_PrimaryOutputType, |
- // Modulate color and coverage, write result as the color output. |
- kModulate_PrimaryOutputType, |
- // Custom Porter-Duff output, used for when we explictly are reading the dst and blending |
- // in the shader. Secondary Output must be none if you use this. The custom blend uses the |
- // equation: cov * (coeffS * S + coeffD * D) + (1 - cov) * D |
- kCustom_PrimaryOutputType |
+ /** |
+ * Deduce the properties of a compile-time constant BlendFormula. |
+ */ |
+ template<OutputType PrimaryOut, OutputType SecondaryOut, |
+ GrBlendEquation BlendEquation, GrBlendCoeff SrcCoeff, GrBlendCoeff DstCoeff> |
+ struct get_properties : SkTIntegralConstant<Properties, static_cast<Properties>( |
+ |
+ (GR_BLEND_MODIFIES_DST(BlendEquation, SrcCoeff, DstCoeff) ? |
+ kModifiesDst_Property : 0) | |
+ |
+ (GR_BLEND_COEFFS_USE_DST_COLOR(SrcCoeff, DstCoeff) ? |
+ kUsesDstColor_Property : 0) | |
+ |
+ ((PrimaryOut >= kModulate_OutputType && GR_BLEND_COEFFS_USE_SRC_COLOR(SrcCoeff,DstCoeff)) || |
+ (SecondaryOut >= kModulate_OutputType && GR_BLEND_COEFF_REFS_SRC2(DstCoeff)) ? |
+ kUsesInputColor_Property : 0) | // We assert later that SrcCoeff doesn't ref src2. |
+ |
+ (kModulate_OutputType == PrimaryOut && |
+ kNone_OutputType == SecondaryOut && |
+ GR_BLEND_CAN_TWEAK_ALPHA_FOR_COVERAGE(BlendEquation, SrcCoeff, DstCoeff) ? |
+ kCanTweakAlphaForCoverage_Property : 0))> { |
+ |
+ // The provided formula should already be optimized. |
+ GR_STATIC_ASSERT((kNone_OutputType == PrimaryOut) == |
+ !GR_BLEND_COEFFS_USE_SRC_COLOR(SrcCoeff, DstCoeff)); |
+ GR_STATIC_ASSERT(!GR_BLEND_COEFF_REFS_SRC2(SrcCoeff)); |
+ GR_STATIC_ASSERT((kNone_OutputType == SecondaryOut) == |
+ !GR_BLEND_COEFF_REFS_SRC2(DstCoeff)); |
+ GR_STATIC_ASSERT(PrimaryOut != SecondaryOut || kNone_OutputType == PrimaryOut); |
+ GR_STATIC_ASSERT(kNone_OutputType != PrimaryOut || kNone_OutputType == SecondaryOut); |
}; |
- enum SecondaryOutputType { |
- // There is no secondary output |
- kNone_SecondaryOutputType, |
- // Writes coverage as the secondary output. Only set if dual source blending is supported |
- // and primary output is kModulate. |
- kCoverage_SecondaryOutputType, |
- // Writes coverage * (1 - colorA) as the secondary output. Only set if dual source blending |
- // is supported and primary output is kModulate. |
- kCoverageISA_SecondaryOutputType, |
- // Writes coverage * (1 - colorRGBA) as the secondary output. Only set if dual source |
- // blending is supported and primary output is kModulate. |
- kCoverageISC_SecondaryOutputType, |
- |
- kSecondaryOutputTypeCnt, |
+ union { |
+ struct { |
+ // We allot the enums one more bit than they require because MSVC seems to sign-extend |
+ // them when the top bit is set. (This is in violation of the C++03 standard 9.6/4) |
+ OutputType fPrimaryOutputType : 4; |
+ OutputType fSecondaryOutputType : 4; |
+ GrBlendEquation fBlendEquation : 6; |
+ GrBlendCoeff fSrcCoeff : 6; |
+ GrBlendCoeff fDstCoeff : 6; |
+ Properties fProps : 32 - (4 + 4 + 6 + 6 + 6); |
+ }; |
+ uint32_t fData; |
}; |
- PrimaryOutputType primaryOutputType() const { return fPrimaryOutputType; } |
- SecondaryOutputType secondaryOutputType() const { return fSecondaryOutputType; } |
+ GR_STATIC_ASSERT(kLast_OutputType < (1 << 3)); |
+ GR_STATIC_ASSERT(kLast_GrBlendEquation < (1 << 5)); |
+ GR_STATIC_ASSERT(kLast_GrBlendCoeff < (1 << 5)); |
+ GR_STATIC_ASSERT(kLast_Property < (1 << 6)); |
+}; |
+ |
+GR_STATIC_ASSERT(4 == sizeof(BlendFormula)); |
+ |
+GR_MAKE_BITFIELD_OPS(BlendFormula::Properties); |
+ |
+/** |
+ * Initialize a compile-time constant BlendFormula and automatically deduce fProps. |
+ */ |
+#define INIT_BLEND_FORMULA(PRIMARY_OUT, SECONDARY_OUT, BLEND_EQUATION, SRC_COEFF, DST_COEFF) \ |
+ {{{PRIMARY_OUT, \ |
+ SECONDARY_OUT, \ |
+ BLEND_EQUATION, SRC_COEFF, DST_COEFF, \ |
+ BlendFormula::get_properties<PRIMARY_OUT, SECONDARY_OUT, \ |
+ BLEND_EQUATION, SRC_COEFF, DST_COEFF>::value}}} |
+ |
+/** |
+ * When there is no coverage, or the blend mode can tweak alpha for coverage, we use the standard |
+ * Porter Duff formula. |
+ */ |
+#define COEFF_FORMULA(SRC_COEFF, DST_COEFF) \ |
+ INIT_BLEND_FORMULA(BlendFormula::kModulate_OutputType, \ |
+ BlendFormula::kNone_OutputType, \ |
+ kAdd_GrBlendEquation, SRC_COEFF, DST_COEFF) |
+ |
+/** |
+ * When the coeffs are (Zero, Zero), we clear the dst. This formula has its own macro so we can set |
+ * the primary output type to none. |
+ */ |
+#define DST_CLEAR_FORMULA \ |
+ INIT_BLEND_FORMULA(BlendFormula::kNone_OutputType, \ |
+ BlendFormula::kNone_OutputType, \ |
+ kAdd_GrBlendEquation, kZero_GrBlendCoeff, kZero_GrBlendCoeff) |
+ |
+/** |
+ * When the coeffs are (Zero, One), we don't write to the dst at all. This formula has its own macro |
+ * so we can set the primary output type to none. |
+ */ |
+#define NO_DST_WRITE_FORMULA \ |
+ INIT_BLEND_FORMULA(BlendFormula::kNone_OutputType, \ |
+ BlendFormula::kNone_OutputType, \ |
+ kAdd_GrBlendEquation, kZero_GrBlendCoeff, kOne_GrBlendCoeff) |
- GrBlendCoeff getSrcBlend() const { return fSrcBlend; } |
- GrBlendCoeff getDstBlend() const { return fDstBlend; } |
+/** |
+ * When there is coverage, the equation with f=coverage is: |
+ * |
+ * D' = f * (S * srcCoeff + D * dstCoeff) + (1-f) * D |
+ * |
+ * This can be rewritten as: |
+ * |
+ * D' = f * S * srcCoeff + D * (1 - [f * (1 - dstCoeff)]) |
+ * |
+ * To implement this formula, we output [f * (1 - dstCoeff)] for the secondary color and replace the |
+ * HW dst coeff with IS2C. |
+ * |
+ * Xfer modes: dst-atop (Sa!=1) |
+ */ |
+#define COVERAGE_FORMULA(ONE_MINUS_DST_COEFF_MODULATE_OUTPUT, SRC_COEFF) \ |
+ INIT_BLEND_FORMULA(BlendFormula::kModulate_OutputType, \ |
+ ONE_MINUS_DST_COEFF_MODULATE_OUTPUT, \ |
+ kAdd_GrBlendEquation, SRC_COEFF, kIS2C_GrBlendCoeff) |
+ |
+/** |
+ * When there is coverage and the src coeff is Zero, the equation with f=coverage becomes: |
+ * |
+ * D' = f * D * dstCoeff + (1-f) * D |
+ * |
+ * This can be rewritten as: |
+ * |
+ * D' = D - D * [f * (1 - dstCoeff)] |
+ * |
+ * To implement this formula, we output [f * (1 - dstCoeff)] for the primary color and use a reverse |
+ * subtract HW blend equation with coeffs of (DC, One). |
+ * |
+ * Xfer modes: clear, dst-out (Sa=1), dst-in (Sa!=1), modulate (Sc!=1) |
+ */ |
+#define COVERAGE_SRC_COEFF_ZERO_FORMULA(ONE_MINUS_DST_COEFF_MODULATE_OUTPUT) \ |
+ INIT_BLEND_FORMULA(ONE_MINUS_DST_COEFF_MODULATE_OUTPUT, \ |
+ BlendFormula::kNone_OutputType, \ |
+ kReverseSubtract_GrBlendEquation, kDC_GrBlendCoeff, kOne_GrBlendCoeff) |
+ |
+/** |
+ * When there is coverage and the dst coeff is Zero, the equation with f=coverage becomes: |
+ * |
+ * D' = f * S * srcCoeff + (1-f) * D |
+ * |
+ * To implement this formula, we output [f] for the secondary color and replace the HW dst coeff |
+ * with IS2A. (Note that we can avoid dual source blending when Sa=1 by using ISA.) |
+ * |
+ * Xfer modes (Sa!=1): src, src-in, src-out |
+ */ |
+#define COVERAGE_DST_COEFF_ZERO_FORMULA(SRC_COEFF) \ |
+ INIT_BLEND_FORMULA(BlendFormula::kModulate_OutputType, \ |
+ BlendFormula::kCoverage_OutputType, \ |
+ kAdd_GrBlendEquation, SRC_COEFF, kIS2A_GrBlendCoeff) |
+ |
+/** |
+ * This table outlines the blend formulas we will use with each xfermode, with and without coverage, |
+ * with and without an opaque input color. Optimization properties are deduced at compile time so we |
+ * can make runtime decisions quickly. RGB coverage is not supported. |
+ */ |
+static const BlendFormula gBlendTable[2][2][SkXfermode::kLastCoeffMode + 1] = { |
+ |
+ /*>> No coverage, input color unknown <<*/ {{ |
+ |
+ /* clear */ DST_CLEAR_FORMULA, |
+ /* src */ COEFF_FORMULA( kOne_GrBlendCoeff, kZero_GrBlendCoeff), |
+ /* dst */ NO_DST_WRITE_FORMULA, |
+ /* src-over */ COEFF_FORMULA( kOne_GrBlendCoeff, kISA_GrBlendCoeff), |
+ /* dst-over */ COEFF_FORMULA( kIDA_GrBlendCoeff, kOne_GrBlendCoeff), |
+ /* src-in */ COEFF_FORMULA( kDA_GrBlendCoeff, kZero_GrBlendCoeff), |
+ /* dst-in */ COEFF_FORMULA( kZero_GrBlendCoeff, kSA_GrBlendCoeff), |
+ /* src-out */ COEFF_FORMULA( kIDA_GrBlendCoeff, kZero_GrBlendCoeff), |
+ /* dst-out */ COEFF_FORMULA( kZero_GrBlendCoeff, kISA_GrBlendCoeff), |
+ /* src-atop */ COEFF_FORMULA( kDA_GrBlendCoeff, kISA_GrBlendCoeff), |
+ /* dst-atop */ COEFF_FORMULA( kIDA_GrBlendCoeff, kSA_GrBlendCoeff), |
+ /* xor */ COEFF_FORMULA( kIDA_GrBlendCoeff, kISA_GrBlendCoeff), |
+ /* plus */ COEFF_FORMULA( kOne_GrBlendCoeff, kOne_GrBlendCoeff), |
+ /* modulate */ COEFF_FORMULA( kZero_GrBlendCoeff, kSC_GrBlendCoeff), |
+ /* screen */ COEFF_FORMULA( kOne_GrBlendCoeff, kISC_GrBlendCoeff), |
+ |
+ }, /*>> Has coverage, input color unknown <<*/ { |
+ |
+ /* clear */ COVERAGE_SRC_COEFF_ZERO_FORMULA(BlendFormula::kCoverage_OutputType), |
+ /* src */ COVERAGE_DST_COEFF_ZERO_FORMULA(kOne_GrBlendCoeff), |
+ /* dst */ NO_DST_WRITE_FORMULA, |
+ /* src-over */ COEFF_FORMULA( kOne_GrBlendCoeff, kISA_GrBlendCoeff), |
+ /* dst-over */ COEFF_FORMULA( kIDA_GrBlendCoeff, kOne_GrBlendCoeff), |
+ /* src-in */ COVERAGE_DST_COEFF_ZERO_FORMULA(kDA_GrBlendCoeff), |
+ /* dst-in */ COVERAGE_SRC_COEFF_ZERO_FORMULA(BlendFormula::kISAModulate_OutputType), |
+ /* src-out */ COVERAGE_DST_COEFF_ZERO_FORMULA(kIDA_GrBlendCoeff), |
+ /* dst-out */ COEFF_FORMULA( kZero_GrBlendCoeff, kISA_GrBlendCoeff), |
+ /* src-atop */ COEFF_FORMULA( kDA_GrBlendCoeff, kISA_GrBlendCoeff), |
+ /* dst-atop */ COVERAGE_FORMULA(BlendFormula::kISAModulate_OutputType, kIDA_GrBlendCoeff), |
+ /* xor */ COEFF_FORMULA( kIDA_GrBlendCoeff, kISA_GrBlendCoeff), |
+ /* plus */ COEFF_FORMULA( kOne_GrBlendCoeff, kOne_GrBlendCoeff), |
+ /* modulate */ COVERAGE_SRC_COEFF_ZERO_FORMULA(BlendFormula::kISCModulate_OutputType), |
+ /* screen */ COEFF_FORMULA( kOne_GrBlendCoeff, kISC_GrBlendCoeff), |
+ |
+ }}, /*>> No coverage, input color opaque <<*/ {{ |
+ |
+ /* clear */ DST_CLEAR_FORMULA, |
+ /* src */ COEFF_FORMULA( kOne_GrBlendCoeff, kZero_GrBlendCoeff), |
+ /* dst */ NO_DST_WRITE_FORMULA, |
+ /* src-over */ COEFF_FORMULA( kOne_GrBlendCoeff, kZero_GrBlendCoeff), |
+ /* dst-over */ COEFF_FORMULA( kIDA_GrBlendCoeff, kOne_GrBlendCoeff), |
+ /* src-in */ COEFF_FORMULA( kDA_GrBlendCoeff, kZero_GrBlendCoeff), |
+ /* dst-in */ NO_DST_WRITE_FORMULA, |
+ /* src-out */ COEFF_FORMULA( kIDA_GrBlendCoeff, kZero_GrBlendCoeff), |
+ /* dst-out */ DST_CLEAR_FORMULA, |
+ /* src-atop */ COEFF_FORMULA( kDA_GrBlendCoeff, kZero_GrBlendCoeff), |
+ /* dst-atop */ COEFF_FORMULA( kIDA_GrBlendCoeff, kOne_GrBlendCoeff), |
+ /* xor */ COEFF_FORMULA( kIDA_GrBlendCoeff, kZero_GrBlendCoeff), |
+ /* plus */ COEFF_FORMULA( kOne_GrBlendCoeff, kOne_GrBlendCoeff), |
+ /* modulate */ COEFF_FORMULA( kZero_GrBlendCoeff, kSC_GrBlendCoeff), |
+ /* screen */ COEFF_FORMULA( kOne_GrBlendCoeff, kISC_GrBlendCoeff), |
+ |
+ }, /*>> Has coverage, input color opaque <<*/ { |
+ |
+ /* clear */ COVERAGE_SRC_COEFF_ZERO_FORMULA(BlendFormula::kCoverage_OutputType), |
+ /* src */ COEFF_FORMULA( kOne_GrBlendCoeff, kISA_GrBlendCoeff), |
+ /* dst */ NO_DST_WRITE_FORMULA, |
+ /* src-over */ COEFF_FORMULA( kOne_GrBlendCoeff, kISA_GrBlendCoeff), |
+ /* dst-over */ COEFF_FORMULA( kIDA_GrBlendCoeff, kOne_GrBlendCoeff), |
+ /* src-in */ COEFF_FORMULA( kDA_GrBlendCoeff, kISA_GrBlendCoeff), |
+ /* dst-in */ NO_DST_WRITE_FORMULA, |
+ /* src-out */ COEFF_FORMULA( kIDA_GrBlendCoeff, kISA_GrBlendCoeff), |
+ /* dst-out */ COVERAGE_SRC_COEFF_ZERO_FORMULA(BlendFormula::kCoverage_OutputType), |
+ /* src-atop */ COEFF_FORMULA( kDA_GrBlendCoeff, kISA_GrBlendCoeff), |
+ /* dst-atop */ COEFF_FORMULA( kIDA_GrBlendCoeff, kOne_GrBlendCoeff), |
+ /* xor */ COEFF_FORMULA( kIDA_GrBlendCoeff, kISA_GrBlendCoeff), |
+ /* plus */ COEFF_FORMULA( kOne_GrBlendCoeff, kOne_GrBlendCoeff), |
+ /* modulate */ COVERAGE_SRC_COEFF_ZERO_FORMULA(BlendFormula::kISCModulate_OutputType), |
+ /* screen */ COEFF_FORMULA( kOne_GrBlendCoeff, kISC_GrBlendCoeff), |
+}}}; |
+ |
+static BlendFormula get_blend_formula(SkXfermode::Mode xfermode, |
+ const GrProcOptInfo& colorPOI, |
+ const GrProcOptInfo& coveragePOI) { |
+ SkASSERT(xfermode >= 0 && xfermode <= SkXfermode::kLastCoeffMode); |
+ SkASSERT(!coveragePOI.isFourChannelOutput()); |
+ |
+ return gBlendTable[colorPOI.isOpaque()][!coveragePOI.isSolidWhite()][xfermode]; |
+} |
+ |
+/////////////////////////////////////////////////////////////////////////////// |
+ |
+class PorterDuffXferProcessor : public GrXferProcessor { |
+public: |
+ static GrXferProcessor* Create(BlendFormula blendFormula) { |
+ return SkNEW_ARGS(PorterDuffXferProcessor, (blendFormula)); |
+ } |
+ |
+ const char* name() const override { return "Porter Duff"; } |
+ bool hasSecondaryOutput() const override { return fBlendFormula.hasSecondaryOutput(); } |
+ |
+ GrGLXferProcessor* createGLInstance() const override; |
+ |
+ BlendFormula getBlendFormula() const { return fBlendFormula; } |
private: |
- PorterDuffXferProcessor(GrBlendCoeff srcBlend, GrBlendCoeff dstBlend, GrColor constant, |
- const DstTexture*, bool willReadDstColor); |
+ PorterDuffXferProcessor(BlendFormula blendFormula) : fBlendFormula(blendFormula) { |
+ this->initClassID<PorterDuffXferProcessor>(); |
+ } |
GrXferProcessor::OptFlags onGetOptimizations(const GrProcOptInfo& colorPOI, |
const GrProcOptInfo& coveragePOI, |
@@ -100,204 +326,100 @@ private: |
void onGetGLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override; |
void onGetBlendInfo(GrXferProcessor::BlendInfo* blendInfo) const override { |
- if (!this->willReadDstColor()) { |
- blendInfo->fSrcBlend = fSrcBlend; |
- blendInfo->fDstBlend = fDstBlend; |
- } else { |
- blendInfo->fSrcBlend = kOne_GrBlendCoeff; |
- blendInfo->fDstBlend = kZero_GrBlendCoeff; |
- } |
- blendInfo->fBlendConstant = fBlendConstant; |
+ blendInfo->fEquation = fBlendFormula.fBlendEquation; |
+ blendInfo->fSrcBlend = fBlendFormula.fSrcCoeff; |
+ blendInfo->fDstBlend = fBlendFormula.fDstCoeff; |
+ blendInfo->fWriteColor = fBlendFormula.modifiesDst(); |
} |
bool onIsEqual(const GrXferProcessor& xpBase) const override { |
const PorterDuffXferProcessor& xp = xpBase.cast<PorterDuffXferProcessor>(); |
- if (fSrcBlend != xp.fSrcBlend || |
- fDstBlend != xp.fDstBlend || |
- fBlendConstant != xp.fBlendConstant || |
- fPrimaryOutputType != xp.fPrimaryOutputType || |
- fSecondaryOutputType != xp.fSecondaryOutputType) { |
- return false; |
- } |
- return true; |
+ return fBlendFormula == xp.fBlendFormula; |
} |
- GrXferProcessor::OptFlags internalGetOptimizations(const GrProcOptInfo& colorPOI, |
- const GrProcOptInfo& coveragePOI, |
- bool doesStencilWrite); |
- |
- void calcOutputTypes(GrXferProcessor::OptFlags blendOpts, const GrCaps& caps, |
- bool hasSolidCoverage); |
- |
- GrBlendCoeff fSrcBlend; |
- GrBlendCoeff fDstBlend; |
- GrColor fBlendConstant; |
- PrimaryOutputType fPrimaryOutputType; |
- SecondaryOutputType fSecondaryOutputType; |
+ const BlendFormula fBlendFormula; |
typedef GrXferProcessor INHERITED; |
}; |
/////////////////////////////////////////////////////////////////////////////// |
-bool append_porterduff_term(GrGLXPFragmentBuilder* fsBuilder, GrBlendCoeff coeff, |
- const char* colorName, const char* srcColorName, |
- const char* dstColorName, bool hasPrevious) { |
- if (kZero_GrBlendCoeff == coeff) { |
- return hasPrevious; |
- } else { |
- if (hasPrevious) { |
- fsBuilder->codeAppend(" + "); |
- } |
- fsBuilder->codeAppendf("%s", colorName); |
- switch (coeff) { |
- case kOne_GrBlendCoeff: |
- break; |
- case kSC_GrBlendCoeff: |
- fsBuilder->codeAppendf(" * %s", srcColorName); |
- break; |
- case kISC_GrBlendCoeff: |
- fsBuilder->codeAppendf(" * (vec4(1.0) - %s)", srcColorName); |
- break; |
- case kDC_GrBlendCoeff: |
- fsBuilder->codeAppendf(" * %s", dstColorName); |
- break; |
- case kIDC_GrBlendCoeff: |
- fsBuilder->codeAppendf(" * (vec4(1.0) - %s)", dstColorName); |
- break; |
- case kSA_GrBlendCoeff: |
- fsBuilder->codeAppendf(" * %s.a", srcColorName); |
- break; |
- case kISA_GrBlendCoeff: |
- fsBuilder->codeAppendf(" * (1.0 - %s.a)", srcColorName); |
- break; |
- case kDA_GrBlendCoeff: |
- fsBuilder->codeAppendf(" * %s.a", dstColorName); |
- break; |
- case kIDA_GrBlendCoeff: |
- fsBuilder->codeAppendf(" * (1.0 - %s.a)", dstColorName); |
- break; |
- default: |
- SkFAIL("Unsupported Blend Coeff"); |
- } |
- return true; |
+static void append_color_output(const PorterDuffXferProcessor& xp, GrGLXPFragmentBuilder* fsBuilder, |
+ BlendFormula::OutputType outputType, const char* output, |
+ const char* inColor, const char* inCoverage) { |
+ switch (outputType) { |
+ case BlendFormula::kNone_OutputType: |
+ fsBuilder->codeAppendf("%s = vec4(0.0);", output); |
+ break; |
+ case BlendFormula::kCoverage_OutputType: |
+ fsBuilder->codeAppendf("%s = %s;", |
+ output, xp.readsCoverage() ? inCoverage : "vec4(1.0)"); |
+ break; |
+ case BlendFormula::kModulate_OutputType: |
+ if (xp.readsCoverage()) { |
+ fsBuilder->codeAppendf("%s = %s * %s;", output, inColor, inCoverage); |
+ } else { |
+ fsBuilder->codeAppendf("%s = %s;", output, inColor); |
+ } |
+ break; |
+ case BlendFormula::kISAModulate_OutputType: |
+ if (xp.readsCoverage()) { |
+ fsBuilder->codeAppendf("%s = (1.0 - %s.a) * %s;", output, inColor, inCoverage); |
+ } else { |
+ fsBuilder->codeAppendf("%s = vec4(1.0 - %s.a);", output, inColor); |
+ } |
+ break; |
+ case BlendFormula::kISCModulate_OutputType: |
+ if (xp.readsCoverage()) { |
+ fsBuilder->codeAppendf("%s = (vec4(1.0) - %s) * %s;", output, inColor, inCoverage); |
+ } else { |
+ fsBuilder->codeAppendf("%s = vec4(1.0) - %s;", output, inColor); |
+ } |
+ break; |
+ default: |
+ SkFAIL("Unsupported output type."); |
+ break; |
} |
} |
class GLPorterDuffXferProcessor : public GrGLXferProcessor { |
public: |
- GLPorterDuffXferProcessor(const GrProcessor&) {} |
- |
- virtual ~GLPorterDuffXferProcessor() {} |
- |
- static void GenKey(const GrProcessor& processor, const GrGLSLCaps& caps, |
- GrProcessorKeyBuilder* b) { |
+ static void GenKey(const GrProcessor& processor, GrProcessorKeyBuilder* b) { |
const PorterDuffXferProcessor& xp = processor.cast<PorterDuffXferProcessor>(); |
- b->add32(xp.primaryOutputType()); |
- b->add32(xp.secondaryOutputType()); |
- if (xp.willReadDstColor()) { |
- b->add32(xp.getSrcBlend()); |
- b->add32(xp.getDstBlend()); |
- } |
+ b->add32(SkToInt(xp.readsCoverage()) | |
+ (xp.getBlendFormula().fPrimaryOutputType << 1) | |
+ (xp.getBlendFormula().fSecondaryOutputType << 4)); |
+ GR_STATIC_ASSERT(BlendFormula::kLast_OutputType < 8); |
}; |
private: |
void onEmitCode(const EmitArgs& args) override { |
const PorterDuffXferProcessor& xp = args.fXP.cast<PorterDuffXferProcessor>(); |
GrGLXPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder(); |
- if (PorterDuffXferProcessor::kCustom_PrimaryOutputType != xp.primaryOutputType()) { |
- SkASSERT(!xp.willReadDstColor()); |
- switch(xp.secondaryOutputType()) { |
- case PorterDuffXferProcessor::kNone_SecondaryOutputType: |
- break; |
- case PorterDuffXferProcessor::kCoverage_SecondaryOutputType: |
- fsBuilder->codeAppendf("%s = %s;", args.fOutputSecondary, |
- args.fInputCoverage); |
- break; |
- case PorterDuffXferProcessor::kCoverageISA_SecondaryOutputType: |
- fsBuilder->codeAppendf("%s = (1.0 - %s.a) * %s;", |
- args.fOutputSecondary, args.fInputColor, |
- args.fInputCoverage); |
- break; |
- case PorterDuffXferProcessor::kCoverageISC_SecondaryOutputType: |
- fsBuilder->codeAppendf("%s = (vec4(1.0) - %s) * %s;", |
- args.fOutputSecondary, args.fInputColor, |
- args.fInputCoverage); |
- break; |
- default: |
- SkFAIL("Unexpected Secondary Output"); |
- } |
- switch (xp.primaryOutputType()) { |
- case PorterDuffXferProcessor::kNone_PrimaryOutputType: |
- fsBuilder->codeAppendf("%s = vec4(0);", args.fOutputPrimary); |
- break; |
- case PorterDuffXferProcessor::kColor_PrimaryOutputType: |
- fsBuilder->codeAppendf("%s = %s;", args.fOutputPrimary, args.fInputColor); |
- break; |
- case PorterDuffXferProcessor::kCoverage_PrimaryOutputType: |
- fsBuilder->codeAppendf("%s = %s;", args.fOutputPrimary, args.fInputCoverage); |
- break; |
- case PorterDuffXferProcessor::kModulate_PrimaryOutputType: |
- fsBuilder->codeAppendf("%s = %s * %s;", args.fOutputPrimary, args.fInputColor, |
- args.fInputCoverage); |
- break; |
- default: |
- SkFAIL("Unexpected Primary Output"); |
- } |
- } else { |
- SkASSERT(xp.willReadDstColor()); |
- |
- const char* dstColor = fsBuilder->dstColor(); |
- |
- fsBuilder->codeAppend("vec4 colorBlend ="); |
- // append src blend |
- bool didAppend = append_porterduff_term(fsBuilder, xp.getSrcBlend(), |
- args.fInputColor, args.fInputColor, |
- dstColor, false); |
- // append dst blend |
- SkAssertResult(append_porterduff_term(fsBuilder, xp.getDstBlend(), |
- dstColor, args.fInputColor, |
- dstColor, didAppend)); |
- fsBuilder->codeAppend(";"); |
- |
- fsBuilder->codeAppendf("%s = %s * colorBlend + (vec4(1.0) - %s) * %s;", |
- args.fOutputPrimary, args.fInputCoverage, args.fInputCoverage, |
- dstColor); |
+ BlendFormula blendFormula = xp.getBlendFormula(); |
+ if (blendFormula.hasSecondaryOutput()) { |
+ append_color_output(xp, fsBuilder, blendFormula.fSecondaryOutputType, |
+ args.fOutputSecondary, args.fInputColor, args.fInputCoverage); |
} |
+ append_color_output(xp, fsBuilder, blendFormula.fPrimaryOutputType, |
+ args.fOutputPrimary, args.fInputColor, args.fInputCoverage); |
} |
- void onSetData(const GrGLProgramDataManager&, const GrXferProcessor&) override {}; |
+ void onSetData(const GrGLProgramDataManager&, const GrXferProcessor&) override {} |
typedef GrGLXferProcessor INHERITED; |
}; |
/////////////////////////////////////////////////////////////////////////////// |
-PorterDuffXferProcessor::PorterDuffXferProcessor(GrBlendCoeff srcBlend, |
- GrBlendCoeff dstBlend, |
- GrColor constant, |
- const DstTexture* dstTexture, |
- bool willReadDstColor) |
- : INHERITED(dstTexture, willReadDstColor) |
- , fSrcBlend(srcBlend) |
- , fDstBlend(dstBlend) |
- , fBlendConstant(constant) |
- , fPrimaryOutputType(kModulate_PrimaryOutputType) |
- , fSecondaryOutputType(kNone_SecondaryOutputType) { |
- this->initClassID<PorterDuffXferProcessor>(); |
-} |
- |
-PorterDuffXferProcessor::~PorterDuffXferProcessor() { |
-} |
- |
-void PorterDuffXferProcessor::onGetGLProcessorKey(const GrGLSLCaps& caps, |
+void PorterDuffXferProcessor::onGetGLProcessorKey(const GrGLSLCaps&, |
GrProcessorKeyBuilder* b) const { |
- GLPorterDuffXferProcessor::GenKey(*this, caps, b); |
+ GLPorterDuffXferProcessor::GenKey(*this, b); |
} |
GrGLXferProcessor* PorterDuffXferProcessor::createGLInstance() const { |
- return SkNEW_ARGS(GLPorterDuffXferProcessor, (*this)); |
+ return SkNEW(GLPorterDuffXferProcessor); |
} |
GrXferProcessor::OptFlags |
@@ -306,168 +428,167 @@ PorterDuffXferProcessor::onGetOptimizations(const GrProcOptInfo& colorPOI, |
bool doesStencilWrite, |
GrColor* overrideColor, |
const GrCaps& caps) { |
- GrXferProcessor::OptFlags optFlags = this->internalGetOptimizations(colorPOI, |
- coveragePOI, |
- doesStencilWrite); |
- this->calcOutputTypes(optFlags, caps, coveragePOI.isSolidWhite()); |
+ GrXferProcessor::OptFlags optFlags = GrXferProcessor::kNone_Opt; |
+ if (!fBlendFormula.modifiesDst()) { |
+ if (!doesStencilWrite) { |
+ optFlags |= GrXferProcessor::kSkipDraw_OptFlag; |
+ } |
+ optFlags |= (GrXferProcessor::kIgnoreColor_OptFlag | |
+ GrXferProcessor::kIgnoreCoverage_OptFlag | |
+ GrXferProcessor::kCanTweakAlphaForCoverage_OptFlag); |
+ } else { |
+ if (!fBlendFormula.usesInputColor()) { |
+ optFlags |= GrXferProcessor::kIgnoreColor_OptFlag; |
+ } |
+ if (coveragePOI.isSolidWhite()) { |
+ optFlags |= GrXferProcessor::kIgnoreCoverage_OptFlag; |
+ } |
+ if (colorPOI.allStagesMultiplyInput() && fBlendFormula.canTweakAlphaForCoverage()) { |
+ optFlags |= GrXferProcessor::kCanTweakAlphaForCoverage_OptFlag; |
+ } |
+ } |
return optFlags; |
} |
-void PorterDuffXferProcessor::calcOutputTypes(GrXferProcessor::OptFlags optFlags, |
- const GrCaps& caps, |
- bool hasSolidCoverage) { |
- if (this->willReadDstColor()) { |
- fPrimaryOutputType = kCustom_PrimaryOutputType; |
- return; |
+/////////////////////////////////////////////////////////////////////////////// |
+ |
+class ShaderPDXferProcessor : public GrXferProcessor { |
+public: |
+ static GrXferProcessor* Create(SkXfermode::Mode xfermode, const DstTexture* dstTexture) { |
+ return SkNEW_ARGS(ShaderPDXferProcessor, (xfermode, dstTexture)); |
} |
- if (optFlags & kIgnoreColor_OptFlag) { |
- if (optFlags & kIgnoreCoverage_OptFlag) { |
- fPrimaryOutputType = kNone_PrimaryOutputType; |
- return; |
- } else { |
- fPrimaryOutputType = kCoverage_PrimaryOutputType; |
- return; |
- } |
- } else if (optFlags & kIgnoreCoverage_OptFlag) { |
- fPrimaryOutputType = kColor_PrimaryOutputType; |
- return; |
+ const char* name() const override { return "Porter Duff Shader"; } |
+ bool hasSecondaryOutput() const override { return false; } |
+ |
+ GrGLXferProcessor* createGLInstance() const override; |
+ |
+ SkXfermode::Mode getXfermode() const { return fXfermode; } |
+ |
+private: |
+ ShaderPDXferProcessor(SkXfermode::Mode xfermode, const DstTexture* dstTexture) |
+ : INHERITED(dstTexture, true) |
+ , fXfermode(xfermode) { |
+ this->initClassID<ShaderPDXferProcessor>(); |
} |
- // If we do have coverage determine whether it matters. Dual source blending is expensive so |
- // we don't do it if we are doing coverage drawing. If we aren't then We always do dual source |
- // blending if we have any effective coverage stages OR the geometry processor doesn't emits |
- // solid coverage. |
- if (!(optFlags & kSetCoverageDrawing_OptFlag) && !hasSolidCoverage) { |
- if (caps.shaderCaps()->dualSourceBlendingSupport()) { |
- if (kZero_GrBlendCoeff == fDstBlend) { |
- // write the coverage value to second color |
- fSecondaryOutputType = kCoverage_SecondaryOutputType; |
- fDstBlend = kIS2C_GrBlendCoeff; |
- } else if (kSA_GrBlendCoeff == fDstBlend) { |
- // SA dst coeff becomes 1-(1-SA)*coverage when dst is partially covered. |
- fSecondaryOutputType = kCoverageISA_SecondaryOutputType; |
- fDstBlend = kIS2C_GrBlendCoeff; |
- } else if (kSC_GrBlendCoeff == fDstBlend) { |
- // SA dst coeff becomes 1-(1-SA)*coverage when dst is partially covered. |
- fSecondaryOutputType = kCoverageISC_SecondaryOutputType; |
- fDstBlend = kIS2C_GrBlendCoeff; |
- } |
- } |
+ GrXferProcessor::OptFlags onGetOptimizations(const GrProcOptInfo&, const GrProcOptInfo&, |
+ bool, GrColor*, const GrCaps&) override { |
+ return kNone_Opt; |
} |
-} |
-GrXferProcessor::OptFlags |
-PorterDuffXferProcessor::internalGetOptimizations(const GrProcOptInfo& colorPOI, |
- const GrProcOptInfo& coveragePOI, |
- bool doesStencilWrite) { |
- if (this->willReadDstColor()) { |
- return GrXferProcessor::kNone_Opt; |
+ void onGetGLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override; |
+ |
+ bool onIsEqual(const GrXferProcessor& xpBase) const override { |
+ const ShaderPDXferProcessor& xp = xpBase.cast<ShaderPDXferProcessor>(); |
+ return fXfermode == xp.fXfermode; |
} |
- bool srcAIsOne = colorPOI.isOpaque(); |
- bool hasCoverage = !coveragePOI.isSolidWhite(); |
- |
- bool dstCoeffIsOne = kOne_GrBlendCoeff == fDstBlend || |
- (kSA_GrBlendCoeff == fDstBlend && srcAIsOne); |
- bool dstCoeffIsZero = kZero_GrBlendCoeff == fDstBlend || |
- (kISA_GrBlendCoeff == fDstBlend && srcAIsOne); |
- |
- // When coeffs are (0,1) there is no reason to draw at all, unless |
- // stenciling is enabled. Having color writes disabled is effectively |
- // (0,1). |
- if ((kZero_GrBlendCoeff == fSrcBlend && dstCoeffIsOne)) { |
- if (doesStencilWrite) { |
- return GrXferProcessor::kIgnoreColor_OptFlag | |
- GrXferProcessor::kSetCoverageDrawing_OptFlag; |
- } else { |
- fDstBlend = kOne_GrBlendCoeff; |
- return GrXferProcessor::kSkipDraw_OptFlag; |
+ const SkXfermode::Mode fXfermode; |
+ |
+ typedef GrXferProcessor INHERITED; |
+}; |
+ |
+/////////////////////////////////////////////////////////////////////////////// |
+ |
+static bool append_porterduff_term(GrGLXPFragmentBuilder* fsBuilder, SkXfermode::Coeff coeff, |
+ const char* colorName, const char* srcColorName, |
+ const char* dstColorName, bool hasPrevious) { |
+ if (SkXfermode::kZero_Coeff == coeff) { |
+ return hasPrevious; |
+ } else { |
+ if (hasPrevious) { |
+ fsBuilder->codeAppend(" + "); |
+ } |
+ fsBuilder->codeAppendf("%s", colorName); |
+ switch (coeff) { |
+ case SkXfermode::kOne_Coeff: |
+ break; |
+ case SkXfermode::kSC_Coeff: |
+ fsBuilder->codeAppendf(" * %s", srcColorName); |
+ break; |
+ case SkXfermode::kISC_Coeff: |
+ fsBuilder->codeAppendf(" * (vec4(1.0) - %s)", srcColorName); |
+ break; |
+ case SkXfermode::kDC_Coeff: |
+ fsBuilder->codeAppendf(" * %s", dstColorName); |
+ break; |
+ case SkXfermode::kIDC_Coeff: |
+ fsBuilder->codeAppendf(" * (vec4(1.0) - %s)", dstColorName); |
+ break; |
+ case SkXfermode::kSA_Coeff: |
+ fsBuilder->codeAppendf(" * %s.a", srcColorName); |
+ break; |
+ case SkXfermode::kISA_Coeff: |
+ fsBuilder->codeAppendf(" * (1.0 - %s.a)", srcColorName); |
+ break; |
+ case SkXfermode::kDA_Coeff: |
+ fsBuilder->codeAppendf(" * %s.a", dstColorName); |
+ break; |
+ case SkXfermode::kIDA_Coeff: |
+ fsBuilder->codeAppendf(" * (1.0 - %s.a)", dstColorName); |
+ break; |
+ default: |
+ SkFAIL("Unsupported Blend Coeff"); |
} |
+ return true; |
} |
+} |
- // if we don't have coverage we can check whether the dst |
- // has to read at all. If not, we'll disable blending. |
- if (!hasCoverage) { |
- if (dstCoeffIsZero) { |
- if (kOne_GrBlendCoeff == fSrcBlend) { |
- // if there is no coverage and coeffs are (1,0) then we |
- // won't need to read the dst at all, it gets replaced by src |
- fDstBlend = kZero_GrBlendCoeff; |
- return GrXferProcessor::kNone_Opt | |
- GrXferProcessor::kIgnoreCoverage_OptFlag; |
- } else if (kZero_GrBlendCoeff == fSrcBlend) { |
- // if the op is "clear" then we don't need to emit a color |
- // or blend, just write transparent black into the dst. |
- fSrcBlend = kOne_GrBlendCoeff; |
- fDstBlend = kZero_GrBlendCoeff; |
- return GrXferProcessor::kIgnoreColor_OptFlag | |
- GrXferProcessor::kIgnoreCoverage_OptFlag; |
- } |
- } |
- return GrXferProcessor::kIgnoreCoverage_OptFlag; |
+class GLShaderPDXferProcessor : public GrGLXferProcessor { |
+public: |
+ static void GenKey(const GrProcessor& processor, GrProcessorKeyBuilder* b) { |
+ const ShaderPDXferProcessor& xp = processor.cast<ShaderPDXferProcessor>(); |
+ b->add32(xp.getXfermode()); |
} |
- // check whether coverage can be safely rolled into alpha |
- // of if we can skip color computation and just emit coverage |
- if (can_tweak_alpha_for_coverage(fDstBlend)) { |
- if (colorPOI.allStagesMultiplyInput()) { |
- return GrXferProcessor::kSetCoverageDrawing_OptFlag | |
- GrXferProcessor::kCanTweakAlphaForCoverage_OptFlag; |
- } else { |
- return GrXferProcessor::kSetCoverageDrawing_OptFlag; |
+private: |
+ void onEmitCode(const EmitArgs& args) override { |
+ const ShaderPDXferProcessor& xp = args.fXP.cast<ShaderPDXferProcessor>(); |
+ GrGLXPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder(); |
- } |
+ SkXfermode::Coeff srcCoeff, dstCoeff; |
+ SkXfermode::ModeAsCoeff(xp.getXfermode(), &srcCoeff, &dstCoeff); |
+ |
+ const char* dstColor = fsBuilder->dstColor(); |
+ |
+ fsBuilder->codeAppend("vec4 colorBlend ="); |
+ // append src blend |
+ bool didAppend = append_porterduff_term(fsBuilder, srcCoeff, |
+ args.fInputColor, args.fInputColor, |
+ dstColor, false); |
+ // append dst blend |
+ SkAssertResult(append_porterduff_term(fsBuilder, dstCoeff, |
+ dstColor, args.fInputColor, |
+ dstColor, didAppend)); |
+ fsBuilder->codeAppend(";"); |
+ |
+ fsBuilder->codeAppendf("%s = %s * colorBlend + (vec4(1.0) - %s) * %s;", |
+ args.fOutputPrimary, args.fInputCoverage, args.fInputCoverage, |
+ dstColor); |
} |
- if (dstCoeffIsZero) { |
- if (kZero_GrBlendCoeff == fSrcBlend) { |
- // the source color is not included in the blend |
- // the dst coeff is effectively zero so blend works out to: |
- // (c)(0)D + (1-c)D = (1-c)D. |
- fDstBlend = kISA_GrBlendCoeff; |
- return GrXferProcessor::kIgnoreColor_OptFlag | |
- GrXferProcessor::kSetCoverageDrawing_OptFlag; |
- } else if (srcAIsOne) { |
- // the dst coeff is effectively zero so blend works out to: |
- // cS + (c)(0)D + (1-c)D = cS + (1-c)D. |
- // If Sa is 1 then we can replace Sa with c |
- // and set dst coeff to 1-Sa. |
- fDstBlend = kISA_GrBlendCoeff; |
- if (colorPOI.allStagesMultiplyInput()) { |
- return GrXferProcessor::kSetCoverageDrawing_OptFlag | |
- GrXferProcessor::kCanTweakAlphaForCoverage_OptFlag; |
- } else { |
- return GrXferProcessor::kSetCoverageDrawing_OptFlag; |
- } |
- } |
- } else if (dstCoeffIsOne) { |
- // the dst coeff is effectively one so blend works out to: |
- // cS + (c)(1)D + (1-c)D = cS + D. |
- fDstBlend = kOne_GrBlendCoeff; |
- if (colorPOI.allStagesMultiplyInput()) { |
- return GrXferProcessor::kSetCoverageDrawing_OptFlag | |
- GrXferProcessor::kCanTweakAlphaForCoverage_OptFlag; |
- } else { |
- return GrXferProcessor::kSetCoverageDrawing_OptFlag; |
+ void onSetData(const GrGLProgramDataManager&, const GrXferProcessor&) override {} |
- } |
- return GrXferProcessor::kSetCoverageDrawing_OptFlag; |
- } |
+ typedef GrGLXferProcessor INHERITED; |
+}; |
+ |
+/////////////////////////////////////////////////////////////////////////////// |
- return GrXferProcessor::kNone_Opt; |
+void ShaderPDXferProcessor::onGetGLProcessorKey(const GrGLSLCaps&, |
+ GrProcessorKeyBuilder* b) const { |
+ GLShaderPDXferProcessor::GenKey(*this, b); |
} |
-bool PorterDuffXferProcessor::hasSecondaryOutput() const { |
- return kNone_SecondaryOutputType != fSecondaryOutputType; |
+GrGLXferProcessor* ShaderPDXferProcessor::createGLInstance() const { |
+ return SkNEW(GLShaderPDXferProcessor); |
} |
/////////////////////////////////////////////////////////////////////////////// |
class PDLCDXferProcessor : public GrXferProcessor { |
public: |
- static GrXferProcessor* Create(GrBlendCoeff srcBlend, GrBlendCoeff dstBlend, |
- const GrProcOptInfo& colorPOI); |
+ static GrXferProcessor* Create(SkXfermode::Mode xfermode, const GrProcOptInfo& colorPOI); |
~PDLCDXferProcessor() override; |
@@ -541,9 +662,9 @@ PDLCDXferProcessor::PDLCDXferProcessor(GrColor blendConstant, uint8_t alpha) |
this->initClassID<PDLCDXferProcessor>(); |
} |
-GrXferProcessor* PDLCDXferProcessor::Create(GrBlendCoeff srcBlend, GrBlendCoeff dstBlend, |
+GrXferProcessor* PDLCDXferProcessor::Create(SkXfermode::Mode xfermode, |
const GrProcOptInfo& colorPOI) { |
- if (kOne_GrBlendCoeff != srcBlend || kISA_GrBlendCoeff != dstBlend) { |
+ if (SkXfermode::kSrcOver_Mode != xfermode) { |
return NULL; |
} |
@@ -585,91 +706,40 @@ PDLCDXferProcessor::onGetOptimizations(const GrProcOptInfo& colorPOI, |
} |
/////////////////////////////////////////////////////////////////////////////// |
-GrPorterDuffXPFactory::GrPorterDuffXPFactory(GrBlendCoeff src, GrBlendCoeff dst) |
- : fSrcCoeff(src), fDstCoeff(dst) { |
+ |
+GrPorterDuffXPFactory::GrPorterDuffXPFactory(SkXfermode::Mode xfermode) |
+ : fXfermode(xfermode) { |
this->initClassID<GrPorterDuffXPFactory>(); |
} |
-GrXPFactory* GrPorterDuffXPFactory::Create(SkXfermode::Mode mode) { |
- switch (mode) { |
- case SkXfermode::kClear_Mode: { |
- static GrPorterDuffXPFactory gClearPDXPF(kZero_GrBlendCoeff, kZero_GrBlendCoeff); |
- return SkRef(&gClearPDXPF); |
- break; |
- } |
- case SkXfermode::kSrc_Mode: { |
- static GrPorterDuffXPFactory gSrcPDXPF(kOne_GrBlendCoeff, kZero_GrBlendCoeff); |
- return SkRef(&gSrcPDXPF); |
- break; |
- } |
- case SkXfermode::kDst_Mode: { |
- static GrPorterDuffXPFactory gDstPDXPF(kZero_GrBlendCoeff, kOne_GrBlendCoeff); |
- return SkRef(&gDstPDXPF); |
- break; |
- } |
- case SkXfermode::kSrcOver_Mode: { |
- static GrPorterDuffXPFactory gSrcOverPDXPF(kOne_GrBlendCoeff, kISA_GrBlendCoeff); |
- return SkRef(&gSrcOverPDXPF); |
- break; |
- } |
- case SkXfermode::kDstOver_Mode: { |
- static GrPorterDuffXPFactory gDstOverPDXPF(kIDA_GrBlendCoeff, kOne_GrBlendCoeff); |
- return SkRef(&gDstOverPDXPF); |
- break; |
- } |
- case SkXfermode::kSrcIn_Mode: { |
- static GrPorterDuffXPFactory gSrcInPDXPF(kDA_GrBlendCoeff, kZero_GrBlendCoeff); |
- return SkRef(&gSrcInPDXPF); |
- break; |
- } |
- case SkXfermode::kDstIn_Mode: { |
- static GrPorterDuffXPFactory gDstInPDXPF(kZero_GrBlendCoeff, kSA_GrBlendCoeff); |
- return SkRef(&gDstInPDXPF); |
- break; |
- } |
- case SkXfermode::kSrcOut_Mode: { |
- static GrPorterDuffXPFactory gSrcOutPDXPF(kIDA_GrBlendCoeff, kZero_GrBlendCoeff); |
- return SkRef(&gSrcOutPDXPF); |
- break; |
- } |
- case SkXfermode::kDstOut_Mode: { |
- static GrPorterDuffXPFactory gDstOutPDXPF(kZero_GrBlendCoeff, kISA_GrBlendCoeff); |
- return SkRef(&gDstOutPDXPF); |
- break; |
- } |
- case SkXfermode::kSrcATop_Mode: { |
- static GrPorterDuffXPFactory gSrcATopPDXPF(kDA_GrBlendCoeff, kISA_GrBlendCoeff); |
- return SkRef(&gSrcATopPDXPF); |
- break; |
- } |
- case SkXfermode::kDstATop_Mode: { |
- static GrPorterDuffXPFactory gDstATopPDXPF(kIDA_GrBlendCoeff, kSA_GrBlendCoeff); |
- return SkRef(&gDstATopPDXPF); |
- break; |
- } |
- case SkXfermode::kXor_Mode: { |
- static GrPorterDuffXPFactory gXorPDXPF(kIDA_GrBlendCoeff, kISA_GrBlendCoeff); |
- return SkRef(&gXorPDXPF); |
- break; |
- } |
- case SkXfermode::kPlus_Mode: { |
- static GrPorterDuffXPFactory gPlusPDXPF(kOne_GrBlendCoeff, kOne_GrBlendCoeff); |
- return SkRef(&gPlusPDXPF); |
- break; |
- } |
- case SkXfermode::kModulate_Mode: { |
- static GrPorterDuffXPFactory gModulatePDXPF(kZero_GrBlendCoeff, kSC_GrBlendCoeff); |
- return SkRef(&gModulatePDXPF); |
- break; |
- } |
- case SkXfermode::kScreen_Mode: { |
- static GrPorterDuffXPFactory gScreenPDXPF(kOne_GrBlendCoeff, kISC_GrBlendCoeff); |
- return SkRef(&gScreenPDXPF); |
- break; |
- } |
- default: |
- return NULL; |
+GrXPFactory* GrPorterDuffXPFactory::Create(SkXfermode::Mode xfermode) { |
+ static GrPorterDuffXPFactory gClearPDXPF(SkXfermode::kClear_Mode); |
+ static GrPorterDuffXPFactory gSrcPDXPF(SkXfermode::kSrc_Mode); |
+ static GrPorterDuffXPFactory gDstPDXPF(SkXfermode::kDst_Mode); |
+ static GrPorterDuffXPFactory gSrcOverPDXPF(SkXfermode::kSrcOver_Mode); |
+ static GrPorterDuffXPFactory gDstOverPDXPF(SkXfermode::kDstOver_Mode); |
+ static GrPorterDuffXPFactory gSrcInPDXPF(SkXfermode::kSrcIn_Mode); |
+ static GrPorterDuffXPFactory gDstInPDXPF(SkXfermode::kDstIn_Mode); |
+ static GrPorterDuffXPFactory gSrcOutPDXPF(SkXfermode::kSrcOut_Mode); |
+ static GrPorterDuffXPFactory gDstOutPDXPF(SkXfermode::kDstOut_Mode); |
+ static GrPorterDuffXPFactory gSrcATopPDXPF(SkXfermode::kSrcATop_Mode); |
+ static GrPorterDuffXPFactory gDstATopPDXPF(SkXfermode::kDstATop_Mode); |
+ static GrPorterDuffXPFactory gXorPDXPF(SkXfermode::kXor_Mode); |
+ static GrPorterDuffXPFactory gPlusPDXPF(SkXfermode::kPlus_Mode); |
+ static GrPorterDuffXPFactory gModulatePDXPF(SkXfermode::kModulate_Mode); |
+ static GrPorterDuffXPFactory gScreenPDXPF(SkXfermode::kScreen_Mode); |
+ |
+ static GrPorterDuffXPFactory* gFactories[] = { |
+ &gClearPDXPF, &gSrcPDXPF, &gDstPDXPF, &gSrcOverPDXPF, &gDstOverPDXPF, &gSrcInPDXPF, |
+ &gDstInPDXPF, &gSrcOutPDXPF, &gDstOutPDXPF, &gSrcATopPDXPF, &gDstATopPDXPF, &gXorPDXPF, |
+ &gPlusPDXPF, &gModulatePDXPF, &gScreenPDXPF |
+ }; |
+ GR_STATIC_ASSERT(SK_ARRAY_COUNT(gFactories) == SkXfermode::kLastCoeffMode + 1); |
+ |
+ if (xfermode < 0 || xfermode > SkXfermode::kLastCoeffMode) { |
+ return NULL; |
} |
+ return SkRef(gFactories[xfermode]); |
} |
GrXferProcessor* |
@@ -678,16 +748,22 @@ GrPorterDuffXPFactory::onCreateXferProcessor(const GrCaps& caps, |
const GrProcOptInfo& covPOI, |
const DstTexture* dstTexture) const { |
if (covPOI.isFourChannelOutput()) { |
- return PDLCDXferProcessor::Create(fSrcCoeff, fDstCoeff, colorPOI); |
- } else { |
- return PorterDuffXferProcessor::Create(fSrcCoeff, fDstCoeff, 0, dstTexture, |
- this->willReadDstColor(caps, colorPOI, covPOI)); |
+ SkASSERT(!dstTexture || !dstTexture->texture()); |
+ return PDLCDXferProcessor::Create(fXfermode, colorPOI); |
+ } |
+ |
+ BlendFormula blendFormula = get_blend_formula(fXfermode, colorPOI, covPOI); |
+ if (blendFormula.hasSecondaryOutput() && !caps.shaderCaps()->dualSourceBlendingSupport()) { |
+ return ShaderPDXferProcessor::Create(fXfermode, dstTexture); |
} |
+ |
+ SkASSERT(!dstTexture || !dstTexture->texture()); |
+ return PorterDuffXferProcessor::Create(blendFormula); |
} |
bool GrPorterDuffXPFactory::supportsRGBCoverage(GrColor /*knownColor*/, |
uint32_t knownColorFlags) const { |
- if (kOne_GrBlendCoeff == fSrcCoeff && kISA_GrBlendCoeff == fDstCoeff && |
+ if (SkXfermode::kSrcOver_Mode == fXfermode && |
kRGBA_GrColorComponentFlags == knownColorFlags) { |
return true; |
} |
@@ -697,103 +773,50 @@ bool GrPorterDuffXPFactory::supportsRGBCoverage(GrColor /*knownColor*/, |
void GrPorterDuffXPFactory::getInvariantOutput(const GrProcOptInfo& colorPOI, |
const GrProcOptInfo& coveragePOI, |
GrXPFactory::InvariantOutput* output) const { |
- if (!coveragePOI.isSolidWhite()) { |
- output->fWillBlendWithDst = true; |
- output->fBlendedColorFlags = 0; |
- return; |
- } |
+ output->fWillBlendWithDst = true; |
+ output->fBlendedColorFlags = kNone_GrColorComponentFlags; |
- GrBlendCoeff srcCoeff = fSrcCoeff; |
- GrBlendCoeff dstCoeff = fDstCoeff; |
- |
- // TODO: figure out to merge this simplify with other current optimization code paths and |
- // eventually remove from GrBlend |
- GrSimplifyBlend(&srcCoeff, &dstCoeff, colorPOI.color(), colorPOI.validFlags(), |
- 0, 0, 0); |
- |
- if (GrBlendCoeffRefsDst(srcCoeff)) { |
- output->fWillBlendWithDst = true; |
- output->fBlendedColorFlags = 0; |
+ // The blend table doesn't support LCD coverage, but these formulas never have invariant output. |
+ if (coveragePOI.isFourChannelOutput()) { |
return; |
} |
- if (kZero_GrBlendCoeff != dstCoeff) { |
- bool srcAIsOne = colorPOI.isOpaque(); |
- if (kISA_GrBlendCoeff != dstCoeff || !srcAIsOne) { |
- output->fWillBlendWithDst = true; |
- } |
- output->fBlendedColorFlags = 0; |
+ const BlendFormula& blendFormula = get_blend_formula(fXfermode, colorPOI, coveragePOI); |
+ if (blendFormula.usesDstColor()) { |
return; |
} |
- switch (srcCoeff) { |
+ SkASSERT(coveragePOI.isSolidWhite()); |
+ SkASSERT(kAdd_GrBlendEquation == blendFormula.fBlendEquation); |
+ |
+ output->fWillBlendWithDst = false; |
+ |
+ switch (blendFormula.fSrcCoeff) { |
case kZero_GrBlendCoeff: |
output->fBlendedColor = 0; |
output->fBlendedColorFlags = kRGBA_GrColorComponentFlags; |
- break; |
+ return; |
case kOne_GrBlendCoeff: |
output->fBlendedColor = colorPOI.color(); |
output->fBlendedColorFlags = colorPOI.validFlags(); |
- break; |
- |
- // The src coeff should never refer to the src and if it refers to dst then opaque |
- // should have been false. |
- case kSC_GrBlendCoeff: |
- case kISC_GrBlendCoeff: |
- case kDC_GrBlendCoeff: |
- case kIDC_GrBlendCoeff: |
- case kSA_GrBlendCoeff: |
- case kISA_GrBlendCoeff: |
- case kDA_GrBlendCoeff: |
- case kIDA_GrBlendCoeff: |
- default: |
- SkFAIL("srcCoeff should not refer to src or dst."); |
- break; |
+ return; |
- // TODO: update this once GrPaint actually has a const color. |
- case kConstC_GrBlendCoeff: |
- case kIConstC_GrBlendCoeff: |
- case kConstA_GrBlendCoeff: |
- case kIConstA_GrBlendCoeff: |
- output->fBlendedColorFlags = 0; |
- break; |
+ default: return; |
} |
- |
- output->fWillBlendWithDst = false; |
} |
bool GrPorterDuffXPFactory::willReadDstColor(const GrCaps& caps, |
const GrProcOptInfo& colorPOI, |
const GrProcOptInfo& coveragePOI) const { |
- // We can always blend correctly if we have dual source blending. |
- if (caps.shaderCaps()->dualSourceBlendingSupport()) { |
- return false; |
+ if (coveragePOI.isFourChannelOutput()) { |
+ return false; // The LCD XP never does a dst read. |
} |
- if (can_tweak_alpha_for_coverage(fDstCoeff)) { |
- return false; |
- } |
- |
- bool srcAIsOne = colorPOI.isOpaque(); |
- |
- if (kZero_GrBlendCoeff == fDstCoeff) { |
- if (kZero_GrBlendCoeff == fSrcCoeff || srcAIsOne) { |
- return false; |
- } |
- } |
- |
- // Reduces to: coeffS * (Cov*S) + D |
- if (kSA_GrBlendCoeff == fDstCoeff && srcAIsOne) { |
- return false; |
- } |
- |
- // We can always blend correctly if we have solid coverage. |
- if (coveragePOI.isSolidWhite()) { |
- return false; |
- } |
- |
- return true; |
+ // We fallback on the shader XP when the blend formula would use dual source blending but we |
+ // don't have support for it. |
+ return !caps.shaderCaps()->dualSourceBlendingSupport() && |
+ get_blend_formula(fXfermode, colorPOI, coveragePOI).hasSecondaryOutput(); |
} |
GR_DEFINE_XP_FACTORY_TEST(GrPorterDuffXPFactory); |
@@ -806,3 +829,15 @@ GrXPFactory* GrPorterDuffXPFactory::TestCreate(SkRandom* random, |
return GrPorterDuffXPFactory::Create(mode); |
} |
+void GrPorterDuffXPFactory::TestGetXPOutputTypes(const GrXferProcessor* xp, |
+ int* outPrimary, |
+ int* outSecondary) { |
+ if (!!strcmp(xp->name(), "Porter Duff")) { |
+ *outPrimary = *outSecondary = -1; |
+ return; |
+ } |
+ BlendFormula blendFormula = static_cast<const PorterDuffXferProcessor*>(xp)->getBlendFormula(); |
+ *outPrimary = blendFormula.fPrimaryOutputType; |
+ *outSecondary = blendFormula.fSecondaryOutputType; |
+} |
+ |