Index: src/gpu/effects/GrPorterDuffXferProcessor.cpp |
diff --git a/src/gpu/effects/GrPorterDuffXferProcessor.cpp b/src/gpu/effects/GrPorterDuffXferProcessor.cpp |
index 55e0c93b23ad03d355ea39969e31c5a7c0cb2eb2..2b4b1334f89828b96cc142a688f8492437f7dcfa 100644 |
--- a/src/gpu/effects/GrPorterDuffXferProcessor.cpp |
+++ b/src/gpu/effects/GrPorterDuffXferProcessor.cpp |
@@ -7,6 +7,7 @@ |
#include "effects/GrPorterDuffXferProcessor.h" |
+#include "GrBlend.h" |
#include "GrDrawState.h" |
#include "GrInvariantOutput.h" |
#include "GrProcessor.h" |
@@ -16,6 +17,25 @@ |
#include "gl/builders/GrGLFragmentShaderBuilder.h" |
#include "gl/builders/GrGLProgramBuilder.h" |
+static bool can_tweak_alpha_for_coverage(GrBlendCoeff dstCoeff, bool isCoverageDrawing) { |
+ /* |
+ 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. |
+ Also, if we're directly rendering coverage (isCoverageDrawing) then coverage is treated as |
+ color by definition. |
+ */ |
+ // TODO: Once we have a CoverageDrawing XP, we don't need to check is CoverageDrawing here |
+ return kOne_GrBlendCoeff == dstCoeff || |
+ kISA_GrBlendCoeff == dstCoeff || |
+ kISC_GrBlendCoeff == dstCoeff || |
+ isCoverageDrawing; |
+} |
+ |
class GrGLPorterDuffXferProcessor : public GrGLXferProcessor { |
public: |
GrGLPorterDuffXferProcessor(const GrProcessor&) {} |
@@ -42,8 +62,9 @@ private: |
/////////////////////////////////////////////////////////////////////////////// |
-GrPorterDuffXferProcessor::GrPorterDuffXferProcessor(GrBlendCoeff srcBlend, GrBlendCoeff dstBlend) |
- : fSrcBlend(srcBlend), fDstBlend(dstBlend) { |
+GrPorterDuffXferProcessor::GrPorterDuffXferProcessor(GrBlendCoeff srcBlend, GrBlendCoeff dstBlend, |
+ GrColor constant) |
+ : fSrcBlend(srcBlend), fDstBlend(dstBlend), fBlendConstant(constant) { |
this->initClassID<GrPorterDuffXferProcessor>(); |
} |
@@ -60,13 +81,121 @@ GrGLFragmentProcessor* GrPorterDuffXferProcessor::createGLInstance() const { |
} |
void GrPorterDuffXferProcessor::onComputeInvariantOutput(GrInvariantOutput* inout) const { |
- inout->setToUnknown(GrInvariantOutput::kWillNot_ReadInput); |
+ inout->setToUnknown(GrInvariantOutput::kWill_ReadInput); |
} |
+GrXferProcessor::OptFlags |
+GrPorterDuffXferProcessor::getOptimizations(const GrProcOptInfo& colorPOI, |
+ const GrProcOptInfo& coveragePOI, |
+ bool isCoverageDrawing, |
+ bool colorWriteDisabled, |
+ bool doesStencilWrite, |
+ GrColor* color, uint8_t* coverage) { |
+ if (colorWriteDisabled) { |
+ fSrcBlend = kZero_GrBlendCoeff; |
+ fDstBlend = kOne_GrBlendCoeff; |
+ } |
+ |
+ bool srcAIsOne; |
+ bool hasCoverage; |
+ if (isCoverageDrawing) { |
+ srcAIsOne = colorPOI.isOpaque() && coveragePOI.isOpaque(); |
+ hasCoverage = false; |
+ } else { |
+ srcAIsOne = colorPOI.isOpaque(); |
+ hasCoverage = !coveragePOI.isSolidWhite(); |
+ } |
+ |
+ bool dstCoeffIsOne = kOne_GrBlendCoeff == fDstBlend || |
+ (kSA_GrBlendCoeff == fDstBlend && srcAIsOne); |
+ bool dstCoeffIsZero = kZero_GrBlendCoeff == fDstBlend || |
+ (kISA_GrBlendCoeff == fDstBlend && srcAIsOne); |
+ |
+ // Optimizations when doing RGB Coverage |
+ if (coveragePOI.isFourChannelOutput()) { |
+ // We want to force our primary output to be alpha * Coverage, where alpha is the alpha |
+ // value of the blend the constant. We should already have valid blend coeff's if we are at |
+ // a point where we have RGB coverage. We don't need any color stages since the known color |
+ // output is already baked into the blendConstant. |
+ uint8_t alpha = GrColorUnpackA(fBlendConstant); |
+ *color = GrColorPackRGBA(alpha, alpha, alpha, alpha); |
+ return GrXferProcessor::kClearColorStages_OptFlag; |
+ } |
+ |
+ // 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) { |
+ *color = 0xffffffff; |
+ return GrXferProcessor::kClearColorStages_OptFlag | |
+ GrXferProcessor::kSetCoverageDrawing_OptFlag; |
+ } else { |
+ fDstBlend = kOne_GrBlendCoeff; |
+ return GrXferProcessor::kSkipDraw_OptFlag; |
+ } |
+ } |
+ |
+ // 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; |
+ } 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; |
+ *color = 0; |
+ *coverage = 0xff; |
+ return GrXferProcessor::kClearColorStages_OptFlag | |
+ GrXferProcessor::kClearCoverageStages_OptFlag; |
+ } |
+ } |
+ } else if (isCoverageDrawing) { |
+ // we have coverage but we aren't distinguishing it from alpha by request. |
+ return GrXferProcessor::kSetCoverageDrawing_OptFlag; |
+ } else { |
+ // 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, isCoverageDrawing)) { |
+ return GrXferProcessor::kSetCoverageDrawing_OptFlag; |
+ } |
+ 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; |
+ *color = 0xffffffff; |
+ return GrXferProcessor::kClearColorStages_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; |
+ 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; |
+ return GrXferProcessor::kSetCoverageDrawing_OptFlag; |
+ } |
+ } |
+ |
+ return GrXferProcessor::kNone_Opt; |
+} |
/////////////////////////////////////////////////////////////////////////////// |
GrPorterDuffXPFactory::GrPorterDuffXPFactory(GrBlendCoeff src, GrBlendCoeff dst) |
- : fSrc(src), fDst(dst) { |
+ : fSrcCoeff(src), fDstCoeff(dst) { |
this->initClassID<GrPorterDuffXPFactory>(); |
} |
@@ -152,16 +281,173 @@ GrXPFactory* GrPorterDuffXPFactory::Create(SkXfermode::Mode mode) { |
} |
} |
-const GrXferProcessor* GrPorterDuffXPFactory::createXferProcessor() const { |
- return GrPorterDuffXferProcessor::Create(fSrc, fDst); |
+GrXferProcessor* GrPorterDuffXPFactory::createXferProcessor(const GrProcOptInfo& colorPOI, |
+ const GrProcOptInfo& covPOI) const { |
+ if (!covPOI.isFourChannelOutput()) { |
+ return GrPorterDuffXferProcessor::Create(fSrcCoeff, fDstCoeff); |
+ } else { |
+ if (this->supportsRGBCoverage(colorPOI.color(), colorPOI.validFlags())) { |
+ SkASSERT(kRGBA_GrColorComponentFlags == colorPOI.validFlags()); |
+ GrColor blendConstant = GrUnPreMulColor(colorPOI.color()); |
+ return GrPorterDuffXferProcessor::Create(kConstC_GrBlendCoeff, kISC_GrBlendCoeff, |
+ blendConstant); |
+ } else { |
+ return NULL; |
+ } |
+ } |
} |
bool GrPorterDuffXPFactory::supportsRGBCoverage(GrColor /*knownColor*/, |
uint32_t knownColorFlags) const { |
- if (kOne_GrBlendCoeff == fSrc && kISA_GrBlendCoeff == fDst && |
+ if (kOne_GrBlendCoeff == fSrcCoeff && kISA_GrBlendCoeff == fDstCoeff && |
kRGBA_GrColorComponentFlags == knownColorFlags) { |
return true; |
} |
return false; |
} |
+bool GrPorterDuffXPFactory::canApplyCoverage(const GrProcOptInfo& colorPOI, |
+ const GrProcOptInfo& coveragePOI, |
+ bool isCoverageDrawing, |
+ bool colorWriteDisabled) const { |
+ bool srcAIsOne = colorPOI.isOpaque() && (!isCoverageDrawing || coveragePOI.isOpaque()); |
+ |
+ if (colorWriteDisabled) { |
+ return true; |
+ } |
+ |
+ bool dstCoeffIsOne = kOne_GrBlendCoeff == fDstCoeff || |
+ (kSA_GrBlendCoeff == fDstCoeff && srcAIsOne); |
+ bool dstCoeffIsZero = kZero_GrBlendCoeff == fDstCoeff || |
+ (kISA_GrBlendCoeff == fDstCoeff && srcAIsOne); |
+ |
+ if ((kZero_GrBlendCoeff == fSrcCoeff && dstCoeffIsOne)) { |
+ return true; |
+ } |
+ |
+ // if we don't have coverage we can check whether the dst |
+ // has to read at all. |
+ if (isCoverageDrawing) { |
+ // we have coverage but we aren't distinguishing it from alpha by request. |
+ return true; |
+ } else { |
+ // check whether coverage can be safely rolled into alpha |
+ // of if we can skip color computation and just emit coverage |
+ if (this->canTweakAlphaForCoverage(isCoverageDrawing)) { |
+ return true; |
+ } |
+ if (dstCoeffIsZero) { |
+ if (kZero_GrBlendCoeff == fSrcCoeff) { |
+ return true; |
+ } else if (srcAIsOne) { |
+ return true; |
+ } |
+ } else if (dstCoeffIsOne) { |
+ return true; |
+ } |
+ } |
+ |
+ // TODO: once all SkXferEffects are XP's then we will never reads dst here since only XP's |
+ // will readDst and PD XP's don't read dst. |
+ if ((colorPOI.readsDst() || coveragePOI.readsDst()) && |
+ kOne_GrBlendCoeff == fSrcCoeff && kZero_GrBlendCoeff == fDstCoeff) { |
+ return true; |
+ } |
+ |
+ return false; |
+} |
+ |
+bool GrPorterDuffXPFactory::willBlendWithDst(const GrProcOptInfo& colorPOI, |
+ const GrProcOptInfo& coveragePOI, |
+ bool isCoverageDrawing, |
+ bool colorWriteDisabled) const { |
+ if (!(isCoverageDrawing || coveragePOI.isSolidWhite())) { |
+ return true; |
+ } |
+ |
+ // TODO: once all SkXferEffects are XP's then we will never reads dst here since only XP's |
+ // will readDst and PD XP's don't read dst. |
+ if ((!colorWriteDisabled && colorPOI.readsDst()) || coveragePOI.readsDst()) { |
+ return true; |
+ } |
+ |
+ if (GrBlendCoeffRefsDst(fSrcCoeff)) { |
+ return true; |
+ } |
+ |
+ bool srcAIsOne = colorPOI.isOpaque() && (!isCoverageDrawing || coveragePOI.isOpaque()); |
+ |
+ if (!(kZero_GrBlendCoeff == fDstCoeff || |
+ (kISA_GrBlendCoeff == fDstCoeff && srcAIsOne))) { |
+ return true; |
+ } |
+ |
+ return false; |
+} |
+ |
+bool GrPorterDuffXPFactory::canTweakAlphaForCoverage(bool isCoverageDrawing) const { |
+ return can_tweak_alpha_for_coverage(fDstCoeff, isCoverageDrawing); |
+} |
+ |
+bool GrPorterDuffXPFactory::getOpaqueAndKnownColor(const GrProcOptInfo& colorPOI, |
+ const GrProcOptInfo& coveragePOI, |
+ GrColor* solidColor, |
+ uint32_t* solidColorKnownComponents) const { |
+ if (!coveragePOI.isSolidWhite()) { |
+ return false; |
+ } |
+ |
+ SkASSERT((NULL == solidColor) == (NULL == solidColorKnownComponents)); |
+ |
+ 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); |
+ |
+ bool opaque = kZero_GrBlendCoeff == dstCoeff && !GrBlendCoeffRefsDst(srcCoeff); |
+ if (solidColor) { |
+ if (opaque) { |
+ switch (srcCoeff) { |
+ case kZero_GrBlendCoeff: |
+ *solidColor = 0; |
+ *solidColorKnownComponents = kRGBA_GrColorComponentFlags; |
+ break; |
+ |
+ case kOne_GrBlendCoeff: |
+ *solidColor = colorPOI.color(); |
+ *solidColorKnownComponents = 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; |
+ |
+ // TODO: update this once GrPaint actually has a const color. |
+ case kConstC_GrBlendCoeff: |
+ case kIConstC_GrBlendCoeff: |
+ case kConstA_GrBlendCoeff: |
+ case kIConstA_GrBlendCoeff: |
+ *solidColorKnownComponents = 0; |
+ break; |
+ } |
+ } else { |
+ solidColorKnownComponents = 0; |
+ } |
+ } |
+ return opaque; |
+} |
+ |
+ |