diff options
-rw-r--r-- | gm/texdata.cpp | 6 | ||||
-rw-r--r-- | include/core/SkXfermode.h | 4 | ||||
-rw-r--r-- | include/gpu/GrPaint.h | 32 | ||||
-rw-r--r-- | include/gpu/GrXferProcessor.h | 111 | ||||
-rw-r--r-- | include/gpu/effects/GrPorterDuffXferProcessor.h | 50 | ||||
-rw-r--r-- | src/core/SkXfermode.cpp | 19 | ||||
-rw-r--r-- | src/effects/SkAlphaThresholdFilter.cpp | 3 | ||||
-rw-r--r-- | src/effects/SkBlurMaskFilter.cpp | 7 | ||||
-rwxr-xr-x | src/gpu/GrBitmapTextContext.cpp | 13 | ||||
-rw-r--r-- | src/gpu/GrClipMaskManager.cpp | 16 | ||||
-rwxr-xr-x | src/gpu/GrDistanceFieldTextContext.cpp | 14 | ||||
-rw-r--r-- | src/gpu/GrDrawState.cpp | 171 | ||||
-rw-r--r-- | src/gpu/GrDrawState.h | 109 | ||||
-rw-r--r-- | src/gpu/GrOptDrawState.cpp | 144 | ||||
-rw-r--r-- | src/gpu/GrOptDrawState.h | 18 | ||||
-rw-r--r-- | src/gpu/GrPaint.cpp | 60 | ||||
-rw-r--r-- | src/gpu/GrProcOptInfo.cpp | 20 | ||||
-rw-r--r-- | src/gpu/GrProcOptInfo.h | 11 | ||||
-rw-r--r-- | src/gpu/SkGpuDevice.cpp | 3 | ||||
-rw-r--r-- | src/gpu/SkGr.cpp | 11 | ||||
-rw-r--r-- | src/gpu/effects/GrPorterDuffXferProcessor.cpp | 300 | ||||
-rw-r--r-- | tests/GLProgramsTest.cpp | 5 |
22 files changed, 629 insertions, 498 deletions
diff --git a/gm/texdata.cpp b/gm/texdata.cpp index efe8c4d730..56dfd24ed0 100644 --- a/gm/texdata.cpp +++ b/gm/texdata.cpp @@ -12,8 +12,9 @@ #if SK_SUPPORT_GPU #include "GrContext.h" -#include "effects/GrSimpleTextureEffect.h" #include "SkColorPriv.h" +#include "effects/GrPorterDuffXferProcessor.h" +#include "effects/GrSimpleTextureEffect.h" namespace skiagm { @@ -98,7 +99,8 @@ protected: ctx->setRenderTarget(target); GrPaint paint; - paint.setBlendFunc(kOne_GrBlendCoeff, kISA_GrBlendCoeff); + paint.setPorterDuffXPFactory(SkXfermode::kSrcOver_Mode); + SkMatrix vm; if (i) { vm.setRotate(90 * SK_Scalar1, diff --git a/include/core/SkXfermode.h b/include/core/SkXfermode.h index 35e9837483..05fd81152a 100644 --- a/include/core/SkXfermode.h +++ b/include/core/SkXfermode.h @@ -217,10 +217,10 @@ public: or a fragment processor. This helper calls the asCoeff(), asXPFactory(), and asFragmentProcessor() virtuals. If the xfermode is NULL, it is treated as kSrcOver_Mode. It is legal to call this with all params NULL to simply test the return value. - fp, xpf, src, and dst must all be NULL or all non-NULL. + fp and xpf must both be NULL or all non-NULL. */ static bool AsFragmentProcessorOrXPFactory(SkXfermode*, GrFragmentProcessor**, - GrXPFactory**, Coeff* src, Coeff* dst); + GrXPFactory**); SK_TO_STRING_PUREVIRT() SK_DECLARE_FLATTENABLE_REGISTRAR_GROUP() diff --git a/include/gpu/GrPaint.h b/include/gpu/GrPaint.h index f31830bb2b..49b95b8449 100644 --- a/include/gpu/GrPaint.h +++ b/include/gpu/GrPaint.h @@ -13,6 +13,7 @@ #include "GrColor.h" #include "GrFragmentStage.h" #include "GrXferProcessor.h" +#include "effects/GrPorterDuffXferProcessor.h" #include "SkXfermode.h" @@ -51,17 +52,6 @@ public: ~GrPaint() {} /** - * Sets the blending coefficients to use to blend the final primitive color with the - * destination color. Defaults to kOne for src and kZero for dst (i.e. src mode). - */ - void setBlendFunc(GrBlendCoeff srcCoeff, GrBlendCoeff dstCoeff) { - fSrcBlendCoeff = srcCoeff; - fDstBlendCoeff = dstCoeff; - } - GrBlendCoeff getSrcBlendCoeff() const { return fSrcBlendCoeff; } - GrBlendCoeff getDstBlendCoeff() const { return fDstBlendCoeff; } - - /** * The initial color of the drawn primitive. Defaults to solid white. */ void setColor(GrColor color) { fColor = color; } @@ -84,6 +74,14 @@ public: return xpFactory; } + void setPorterDuffXPFactory(SkXfermode::Mode mode) { + fXPFactory.reset(GrPorterDuffXPFactory::Create(mode)); + } + + void setPorterDuffXPFactory(GrBlendCoeff src, GrBlendCoeff dst) { + fXPFactory.reset(GrPorterDuffXPFactory::Create(src, dst)); + } + /** * Appends an additional color processor to the color computation. */ @@ -121,8 +119,6 @@ public: const GrFragmentStage& getCoverageStage(int s) const { return fCoverageStages[s]; } GrPaint& operator=(const GrPaint& paint) { - fSrcBlendCoeff = paint.fSrcBlendCoeff; - fDstBlendCoeff = paint.fDstBlendCoeff; fAntiAlias = paint.fAntiAlias; fDither = paint.fDither; @@ -140,7 +136,6 @@ public: * Resets the paint to the defaults. */ void reset() { - this->resetBlend(); this->resetOptions(); this->resetColor(); this->resetStages(); @@ -211,18 +206,11 @@ private: SkSTArray<4, GrFragmentStage> fColorStages; SkSTArray<2, GrFragmentStage> fCoverageStages; - GrBlendCoeff fSrcBlendCoeff; - GrBlendCoeff fDstBlendCoeff; bool fAntiAlias; bool fDither; GrColor fColor; - void resetBlend() { - fSrcBlendCoeff = kOne_GrBlendCoeff; - fDstBlendCoeff = kZero_GrBlendCoeff; - } - void resetOptions() { fAntiAlias = false; fDither = false; @@ -232,7 +220,7 @@ private: fColor = GrColorPackRGBA(0xff, 0xff, 0xff, 0xff); } - void resetStages(); + void resetStages(); }; #endif diff --git a/include/gpu/GrXferProcessor.h b/include/gpu/GrXferProcessor.h index 10ecf54c75..b7d0bdd9fa 100644 --- a/include/gpu/GrXferProcessor.h +++ b/include/gpu/GrXferProcessor.h @@ -13,6 +13,8 @@ #include "GrTypes.h" #include "SkXfermode.h" +class GrProcOptInfo; + /** * GrXferProcessor is responsible for implementing the xfer mode that blends the src color and dst * color. It does this by emitting fragment shader code and controlling the fixed-function blend @@ -27,11 +29,83 @@ * GrXPFactory once we have finalized the state of our draw. */ class GrXferProcessor : public GrFragmentProcessor { +public: + /** + * Optimizations for blending / coverage that an OptDrawState should apply to itself. + */ + enum OptFlags { + /** + * No optimizations needed + */ + kNone_Opt = 0, + /** + * The draw can be skipped completely. + */ + kSkipDraw_OptFlag = 0x1, + /** + * Clear color stages, remove color vertex attribs, and use input color + */ + kClearColorStages_OptFlag = 0x2, + /** + * Clear coverage stages, remove coverage vertex attribs, and use input coverage + */ + kClearCoverageStages_OptFlag = 0x4, + /** + * Set CoverageDrawing_StateBit + */ + kSetCoverageDrawing_OptFlag = 0x8, + }; + + GR_DECL_BITFIELD_OPS_FRIENDS(OptFlags); + + /** + * Determines which optimizations (as described by the ptFlags above) can be performed by + * the draw with this xfer processor. If this function is called, the xfer processor may change + * its state to reflected the given blend optimizations. It will also set the output parameters, + * color and coverage, to specific values if it decides to remove all color or coverage stages. + * A caller who calls this function on a XP is required to honor the returned OptFlags + * and color/coverage values for its draw. + */ + // TODO: remove need for isCoverageDrawing once coverageDrawing is its own XP. + // TODO: remove need for colorWriteDisabled once colorWriteDisabled is its own XP. + virtual OptFlags getOptimizations(const GrProcOptInfo& colorPOI, + const GrProcOptInfo& coveragePOI, + bool isCoverageDrawing, + bool colorWriteDisabled, + bool doesStencilWrite, + GrColor* color, + uint8_t* coverage) = 0; + + struct BlendInfo { + GrBlendCoeff fSrcBlend; + GrBlendCoeff fDstBlend; + GrColor fBlendConstant; + }; + + virtual void getBlendInfo(BlendInfo* blendInfo) const = 0; + + /** Will this prceossor read the destination pixel value? */ + bool willReadDstColor() const { return fWillReadDstColor; } + +protected: + GrXferProcessor() : fWillReadDstColor(false) {} + + /** + * If the prceossor subclass will read the destination pixel value then it must call this + * function from its constructor. Otherwise, when its generated backend-specific prceossor class + * attempts to generate code that reads the destination pixel it will fail. + */ + void setWillReadDstColor() { fWillReadDstColor = true; } + private: + bool fWillReadDstColor; + typedef GrFragmentProcessor INHERITED; }; +GR_MAKE_BITFIELD_OPS(GrXferProcessor::OptFlags); + /** * We install a GrXPFactory (XPF) early on in the pipeline before all the final draw information is * known (e.g. whether there is fractional pixel coverage, will coverage be 1 or 4 channel, is the @@ -45,7 +119,8 @@ private: */ class GrXPFactory : public SkRefCnt { public: - virtual const GrXferProcessor* createXferProcessor() const = 0; + virtual GrXferProcessor* createXferProcessor(const GrProcOptInfo& colorPOI, + const GrProcOptInfo& coveragePOI) const = 0; /** * This function returns true if the GrXferProcessor generated from this factory will be able to @@ -54,6 +129,40 @@ public: */ virtual bool supportsRGBCoverage(GrColor knownColor, uint32_t knownColorFlags) const = 0; + /** + * Depending on color blend mode requested it may or may not be possible to correctly blend with + * fractional pixel coverage generated by the fragment shader. + * + * This function considers the known color and coverage input into the xfer processor and + * certain state information (isCoverageDrawing and colorWriteDisabled) to determine whether + * coverage can be handled correctly. + */ + // TODO: remove need for isCoverageDrawing once coverageDrawing is its own XP. + // TODO: remove need for colorWriteDisabled once colorWriteDisabled is its own XP. + virtual bool canApplyCoverage(const GrProcOptInfo& colorPOI, const GrProcOptInfo& coveragePOI, + bool isCoverageDrawing, bool colorWriteDisabled) const = 0; + + /** + * This function returns true if the destination pixel values will be read for blending during + * draw. + */ + // TODO: remove need for isCoverageDrawing once coverageDrawing is its own XP. + // TODO: remove need for colorWriteDisabled once only XP can read dst. + virtual bool willBlendWithDst(const GrProcOptInfo& colorPOI, const GrProcOptInfo& coveragePOI, + bool isCoverageDrawing, bool colorWriteDisabled) const = 0; + + /** + * Determines whether multiplying the computed per-pixel color by the pixel's fractional + * coverage before the blend will give the correct final destination color. In general it + * will not as coverage is applied after blending. + */ + // TODO: remove need for isCoverageDrawing once coverageDrawing is its own XP. + virtual bool canTweakAlphaForCoverage(bool isCoverageDrawing) const = 0; + + virtual bool getOpaqueAndKnownColor(const GrProcOptInfo& colorPOI, + const GrProcOptInfo& coveragePOI, GrColor* solidColor, + uint32_t* solidColorKnownComponents) const = 0; + bool isEqual(const GrXPFactory& that) const { if (this->classID() != that.classID()) { return false; diff --git a/include/gpu/effects/GrPorterDuffXferProcessor.h b/include/gpu/effects/GrPorterDuffXferProcessor.h index 9ff3bea2ed..0ddfcdcd2a 100644 --- a/include/gpu/effects/GrPorterDuffXferProcessor.h +++ b/include/gpu/effects/GrPorterDuffXferProcessor.h @@ -17,8 +17,9 @@ class GrInvariantOutput; class GrPorterDuffXferProcessor : public GrXferProcessor { public: - static GrXferProcessor* Create(GrBlendCoeff srcBlend, GrBlendCoeff dstBlend) { - return SkNEW_ARGS(GrPorterDuffXferProcessor, (srcBlend, dstBlend)); + static GrXferProcessor* Create(GrBlendCoeff srcBlend, GrBlendCoeff dstBlend, + GrColor constant = 0) { + return SkNEW_ARGS(GrPorterDuffXferProcessor, (srcBlend, dstBlend, constant)); } virtual ~GrPorterDuffXferProcessor(); @@ -30,12 +31,28 @@ public: virtual GrGLFragmentProcessor* createGLInstance() const SK_OVERRIDE; + virtual GrXferProcessor::OptFlags getOptimizations(const GrProcOptInfo& colorPOI, + const GrProcOptInfo& coveragePOI, + bool isCoverageDrawing, + bool colorWriteDisabled, + bool doesStencilWrite, + GrColor* color, + uint8_t* coverage) SK_OVERRIDE; + + virtual void getBlendInfo(GrXferProcessor::BlendInfo* blendInfo) const SK_OVERRIDE { + blendInfo->fSrcBlend = fSrcBlend; + blendInfo->fDstBlend = fDstBlend; + blendInfo->fBlendConstant = fBlendConstant; + } + private: - GrPorterDuffXferProcessor(GrBlendCoeff srcBlend, GrBlendCoeff dstBlend); + GrPorterDuffXferProcessor(GrBlendCoeff srcBlend, GrBlendCoeff dstBlend, GrColor constant); virtual bool onIsEqual(const GrFragmentProcessor& fpBase) const SK_OVERRIDE { const GrPorterDuffXferProcessor& xp = fpBase.cast<GrPorterDuffXferProcessor>(); - if (fSrcBlend != xp.fSrcBlend || fDstBlend != xp.fDstBlend) { + if (fSrcBlend != xp.fSrcBlend || + fDstBlend != xp.fDstBlend || + fBlendConstant != xp.fBlendConstant) { return false; } return true; @@ -45,7 +62,8 @@ private: GrBlendCoeff fSrcBlend; GrBlendCoeff fDstBlend; - + GrColor fBlendConstant; + typedef GrXferProcessor INHERITED; }; @@ -63,20 +81,34 @@ public: return SkNEW_ARGS(GrPorterDuffXPFactory, (src, dst)); } - const GrXferProcessor* createXferProcessor() const SK_OVERRIDE; + GrXferProcessor* createXferProcessor(const GrProcOptInfo& colorPOI, + const GrProcOptInfo& coveragePOI) const SK_OVERRIDE; bool supportsRGBCoverage(GrColor knownColor, uint32_t knownColorFlags) const SK_OVERRIDE; + bool canApplyCoverage(const GrProcOptInfo& colorPOI, const GrProcOptInfo& coveragePOI, + bool isCoverageDrawing, bool colorWriteDisabled) const SK_OVERRIDE; + + bool willBlendWithDst(const GrProcOptInfo& colorPOI, const GrProcOptInfo& coveragePOI, + bool isCoverageDrawing, bool colorWriteDisabled) const SK_OVERRIDE; + + bool canTweakAlphaForCoverage(bool isCoverageDrawing) const SK_OVERRIDE; + + bool getOpaqueAndKnownColor(const GrProcOptInfo& colorPOI, + const GrProcOptInfo& coveragePOI, + GrColor* solidColor, + uint32_t* solidColorKnownComponents) const SK_OVERRIDE; + private: GrPorterDuffXPFactory(GrBlendCoeff src, GrBlendCoeff dst); bool onIsEqual(const GrXPFactory& xpfBase) const SK_OVERRIDE { const GrPorterDuffXPFactory& xpf = xpfBase.cast<GrPorterDuffXPFactory>(); - return (fSrc == xpf.fSrc && fDst == xpf.fDst); + return (fSrcCoeff == xpf.fSrcCoeff && fDstCoeff == xpf.fDstCoeff); } - GrBlendCoeff fSrc; - GrBlendCoeff fDst; + GrBlendCoeff fSrcCoeff; + GrBlendCoeff fDstCoeff; typedef GrXPFactory INHERITED; }; diff --git a/src/core/SkXfermode.cpp b/src/core/SkXfermode.cpp index 268d41ec1b..004aa93afd 100644 --- a/src/core/SkXfermode.cpp +++ b/src/core/SkXfermode.cpp @@ -689,25 +689,19 @@ bool SkXfermode::asXPFactory(GrXPFactory**) const { bool SkXfermode::AsFragmentProcessorOrXPFactory(SkXfermode* xfermode, GrFragmentProcessor** fp, - GrXPFactory** xpf, - Coeff* src, Coeff* dst) { + GrXPFactory** xpf) { + Coeff src, dst; Mode mode; if (NULL == xfermode) { - SkAssertResult(ModeAsCoeff(kSrcOver_Mode, src, dst)); - *xpf = GrPorterDuffXPFactory::Create(*src, *dst); + *xpf = GrPorterDuffXPFactory::Create(kSrcOver_Mode); return true; } else if (xfermode->asMode(&mode) && mode <= kLastCoeffMode) { *xpf = GrPorterDuffXPFactory::Create(mode); - // TODO: This Line will be removed in follow up cl that handles blending and thus we won't - // have to set coeffs here. - SkAssertResult(ModeAsCoeff(mode, src, dst)); return true; - } else if (xfermode->asCoeff(src, dst)) { - *xpf = GrPorterDuffXPFactory::Create(*src, *dst); + } else if (xfermode->asCoeff(&src, &dst)) { + *xpf = GrPorterDuffXPFactory::Create(src, dst); return true; } else if (xfermode->asXPFactory(xpf)) { - *src = SkXfermode::kOne_Coeff; - *dst = SkXfermode::kZero_Coeff; return true; } else { return xfermode->asFragmentProcessor(fp); @@ -716,8 +710,7 @@ bool SkXfermode::AsFragmentProcessorOrXPFactory(SkXfermode* xfermode, #else bool SkXfermode::AsFragmentProcessorOrXPFactory(SkXfermode* xfermode, GrFragmentProcessor** fp, - GrXPFactory** xpf, - Coeff* src, Coeff* dst) { + GrXPFactory** xpf) { return false; } #endif diff --git a/src/effects/SkAlphaThresholdFilter.cpp b/src/effects/SkAlphaThresholdFilter.cpp index 6f9fefce1b..13a745efe0 100644 --- a/src/effects/SkAlphaThresholdFilter.cpp +++ b/src/effects/SkAlphaThresholdFilter.cpp @@ -48,6 +48,7 @@ SkImageFilter* SkAlphaThresholdFilter::Create(const SkRegion& region, #include "GrFragmentProcessor.h" #include "GrInvariantOutput.h" #include "GrTextureAccess.h" +#include "effects/GrPorterDuffXferProcessor.h" #include "SkGr.h" @@ -281,7 +282,7 @@ bool SkAlphaThresholdFilterImpl::asFragmentProcessor(GrFragmentProcessor** fp, { GrContext::AutoRenderTarget art(context, maskTexture->asRenderTarget()); GrPaint grPaint; - grPaint.setBlendFunc(kOne_GrBlendCoeff, kZero_GrBlendCoeff); + grPaint.setPorterDuffXPFactory(SkXfermode::kSrc_Mode); SkRegion::Iterator iter(fRegion); context->clear(NULL, 0x0, true, maskTexture->asRenderTarget()); diff --git a/src/effects/SkBlurMaskFilter.cpp b/src/effects/SkBlurMaskFilter.cpp index f7174e921f..52f99e112a 100644 --- a/src/effects/SkBlurMaskFilter.cpp +++ b/src/effects/SkBlurMaskFilter.cpp @@ -24,6 +24,7 @@ #include "GrInvariantOutput.h" #include "SkGrPixelRef.h" #include "SkDraw.h" +#include "effects/GrPorterDuffXferProcessor.h" #include "effects/GrSimpleTextureEffect.h" #include "gl/GrGLProcessor.h" #include "gl/builders/GrGLProgramBuilder.h" @@ -1218,15 +1219,15 @@ bool SkBlurMaskFilterImpl::filterMaskGPU(GrTexture* src, paint.addColorProcessor(GrSimpleTextureEffect::Create(src, matrix))->unref(); if (kInner_SkBlurStyle == fBlurStyle) { // inner: dst = dst * src - paint.setBlendFunc(kDC_GrBlendCoeff, kZero_GrBlendCoeff); + paint.setPorterDuffXPFactory(kDC_GrBlendCoeff, kZero_GrBlendCoeff); } else if (kSolid_SkBlurStyle == fBlurStyle) { // solid: dst = src + dst - src * dst // = (1 - dst) * src + 1 * dst - paint.setBlendFunc(kIDC_GrBlendCoeff, kOne_GrBlendCoeff); + paint.setPorterDuffXPFactory(kIDC_GrBlendCoeff, kOne_GrBlendCoeff); } else if (kOuter_SkBlurStyle == fBlurStyle) { // outer: dst = dst * (1 - src) // = 0 * src + (1 - src) * dst - paint.setBlendFunc(kZero_GrBlendCoeff, kISC_GrBlendCoeff); + paint.setPorterDuffXPFactory(kZero_GrBlendCoeff, kISC_GrBlendCoeff); } context->drawRect(paint, clipRect); } diff --git a/src/gpu/GrBitmapTextContext.cpp b/src/gpu/GrBitmapTextContext.cpp index 48054f4eac..e509e02437 100755 --- a/src/gpu/GrBitmapTextContext.cpp +++ b/src/gpu/GrBitmapTextContext.cpp @@ -561,7 +561,6 @@ void GrBitmapTextContext::flush() { // Color bitmap text case kARGB_GrMaskFormat: SkASSERT(!drawState.hasColorVertexAttribute()); - drawState.setBlendFunc(fPaint.getSrcBlendCoeff(), fPaint.getDstBlendCoeff()); drawState.setAlpha(fSkPaint.getAlpha()); break; // LCD text @@ -573,24 +572,12 @@ void GrBitmapTextContext::flush() { SkDebugf("LCD Text will not draw correctly.\n"); } SkASSERT(!drawState.hasColorVertexAttribute()); - // We don't use the GrPaint's color in this case because it's been premultiplied by - // alpha. Instead we feed in a non-premultiplied color, and multiply its alpha by - // the mask texture color. The end result is that we get - // mask*paintAlpha*paintColor + (1-mask*paintAlpha)*dstColor - int a = SkColorGetA(fSkPaint.getColor()); - // paintAlpha - drawState.setColor(SkColorSetARGB(a, a, a, a)); - // paintColor - drawState.setBlendConstant(skcolor_to_grcolor_nopremultiply(fSkPaint.getColor())); - drawState.setBlendFunc(kConstC_GrBlendCoeff, kISC_GrBlendCoeff); break; } // Grayscale/BW text case kA8_GrMaskFormat: drawState.setHint(GrDrawState::kVertexColorsAreOpaque_Hint, 0xFF == GrColorUnpackA(fPaint.getColor())); - // set back to normal in case we took LCD path previously. - drawState.setBlendFunc(fPaint.getSrcBlendCoeff(), fPaint.getDstBlendCoeff()); // We're using per-vertex color. SkASSERT(drawState.hasColorVertexAttribute()); break; diff --git a/src/gpu/GrClipMaskManager.cpp b/src/gpu/GrClipMaskManager.cpp index f86aa9de29..e87c7e1bc5 100644 --- a/src/gpu/GrClipMaskManager.cpp +++ b/src/gpu/GrClipMaskManager.cpp @@ -18,9 +18,10 @@ #include "SkRasterClip.h" #include "SkStrokeRec.h" #include "SkTLazy.h" -#include "effects/GrTextureDomain.h" #include "effects/GrConvexPolyEffect.h" +#include "effects/GrPorterDuffXferProcessor.h" #include "effects/GrRRectEffect.h" +#include "effects/GrTextureDomain.h" #define GR_AA_CLIP 1 typedef SkClipStack::Element Element; @@ -332,24 +333,25 @@ namespace { // set up the OpenGL blend function to perform the specified // boolean operation for alpha clip mask creation void setup_boolean_blendcoeffs(SkRegion::Op op, GrDrawState* drawState) { + // TODO: once we have a coverageDrawing XP this will all use that instead of PD switch (op) { case SkRegion::kReplace_Op: - drawState->setBlendFunc(kOne_GrBlendCoeff, kZero_GrBlendCoeff); + drawState->setPorterDuffXPFactory(kOne_GrBlendCoeff, kZero_GrBlendCoeff); break; case SkRegion::kIntersect_Op: - drawState->setBlendFunc(kDC_GrBlendCoeff, kZero_GrBlendCoeff); + drawState->setPorterDuffXPFactory(kDC_GrBlendCoeff, kZero_GrBlendCoeff); break; case SkRegion::kUnion_Op: - drawState->setBlendFunc(kOne_GrBlendCoeff, kISC_GrBlendCoeff); + drawState->setPorterDuffXPFactory(kOne_GrBlendCoeff, kISC_GrBlendCoeff); break; case SkRegion::kXOR_Op: - drawState->setBlendFunc(kIDC_GrBlendCoeff, kISC_GrBlendCoeff); + drawState->setPorterDuffXPFactory(kIDC_GrBlendCoeff, kISC_GrBlendCoeff); break; case SkRegion::kDifference_Op: - drawState->setBlendFunc(kZero_GrBlendCoeff, kISC_GrBlendCoeff); + drawState->setPorterDuffXPFactory(kZero_GrBlendCoeff, kISC_GrBlendCoeff); break; case SkRegion::kReverseDifference_Op: - drawState->setBlendFunc(kIDC_GrBlendCoeff, kZero_GrBlendCoeff); + drawState->setPorterDuffXPFactory(kIDC_GrBlendCoeff, kZero_GrBlendCoeff); break; default: SkASSERT(false); diff --git a/src/gpu/GrDistanceFieldTextContext.cpp b/src/gpu/GrDistanceFieldTextContext.cpp index 5b679d1c80..1f4070f4f5 100755 --- a/src/gpu/GrDistanceFieldTextContext.cpp +++ b/src/gpu/GrDistanceFieldTextContext.cpp @@ -637,8 +637,6 @@ void GrDistanceFieldTextContext::flush() { // Set draw state if (fUseLCDText) { - GrColor colorNoPreMul = skcolor_to_grcolor_nopremultiply(filteredColor); - // TODO: move supportsRGBCoverage check to setupCoverageEffect and only add LCD // processor if the xp can support it. For now we will simply assume that if // fUseLCDText is true, then we have a known color output. @@ -646,22 +644,10 @@ void GrDistanceFieldTextContext::flush() { SkDebugf("LCD Text will not draw correctly.\n"); } SkASSERT(!drawState.hasColorVertexAttribute()); - // We don't use the GrPaint's color in this case because it's been premultiplied by - // alpha. Instead we feed in a non-premultiplied color, and multiply its alpha by - // the mask texture color. The end result is that we get - // mask*paintAlpha*paintColor + (1-mask*paintAlpha)*dstColor - int a = SkColorGetA(fSkPaint.getColor()); - // paintAlpha - drawState.setColor(SkColorSetARGB(a, a, a, a)); - // paintColor - drawState.setBlendConstant(colorNoPreMul); - drawState.setBlendFunc(kConstC_GrBlendCoeff, kISC_GrBlendCoeff); } else { if (0xFF == GrColorUnpackA(fPaint.getColor())) { drawState.setHint(GrDrawState::kVertexColorsAreOpaque_Hint, true); } - // set back to normal in case we took LCD path previously. - drawState.setBlendFunc(fPaint.getSrcBlendCoeff(), fPaint.getDstBlendCoeff()); // We're using per-vertex color. SkASSERT(drawState.hasColorVertexAttribute()); } diff --git a/src/gpu/GrDrawState.cpp b/src/gpu/GrDrawState.cpp index 8c2d75f0b5..51ef6cf5d3 100644 --- a/src/gpu/GrDrawState.cpp +++ b/src/gpu/GrDrawState.cpp @@ -14,8 +14,6 @@ #include "GrXferProcessor.h" #include "effects/GrPorterDuffXferProcessor.h" -/////////////////////////////////////////////////////////////////////////////// - bool GrDrawState::isEqual(const GrDrawState& that) const { bool usingVertexColors = this->hasColorVertexAttribute(); if (!usingVertexColors && this->fColor != that.fColor) { @@ -26,9 +24,6 @@ bool GrDrawState::isEqual(const GrDrawState& that) const { this->fColorStages.count() != that.fColorStages.count() || this->fCoverageStages.count() != that.fCoverageStages.count() || !this->fViewMatrix.cheapEqualTo(that.fViewMatrix) || - this->fSrcBlend != that.fSrcBlend || - this->fDstBlend != that.fDstBlend || - this->fBlendConstant != that.fBlendConstant || this->fFlagBits != that.fFlagBits || this->fStencilSettings != that.fStencilSettings || this->fDrawFace != that.fDrawFace) { @@ -90,9 +85,6 @@ GrDrawState& GrDrawState::operator=(const GrDrawState& that) { fRenderTarget.reset(SkSafeRef(that.fRenderTarget.get())); fColor = that.fColor; fViewMatrix = that.fViewMatrix; - fSrcBlend = that.fSrcBlend; - fDstBlend = that.fDstBlend; - fBlendConstant = that.fBlendConstant; fFlagBits = that.fFlagBits; fStencilSettings = that.fStencilSettings; fCoverage = that.fCoverage; @@ -130,9 +122,6 @@ void GrDrawState::onReset(const SkMatrix* initialViewMatrix) { } else { fViewMatrix = *initialViewMatrix; } - fSrcBlend = kOne_GrBlendCoeff; - fDstBlend = kZero_GrBlendCoeff; - fBlendConstant = 0x0; fFlagBits = 0x0; fStencilSettings.setDisabled(); fCoverage = 0xff; @@ -179,13 +168,11 @@ void GrDrawState::setFromPaint(const GrPaint& paint, const SkMatrix& vm, GrRende fXPFactory.reset(SkRef(paint.getXPFactory())); - this->setBlendFunc(paint.getSrcBlendCoeff(), paint.getDstBlendCoeff()); this->setRenderTarget(rt); fViewMatrix = vm; // These have no equivalent in GrPaint, set them to defaults - fBlendConstant = 0x0; fDrawFace = kBoth_DrawFace; fStencilSettings.setDisabled(); fFlagBits = 0; @@ -209,15 +196,11 @@ bool GrDrawState::couldApplyCoverage(const GrDrawTargetCaps& caps) const { if (caps.dualSourceBlendingSupport()) { return true; } - // we can correctly apply coverage if a) we have dual source blending - // or b) one of our blend optimizations applies - // or c) the src, dst blend coeffs are 1,0 and we will read Dst Color - GrBlendCoeff srcCoeff; - GrBlendCoeff dstCoeff; - BlendOpt opt = this->getBlendOpt(true, &srcCoeff, &dstCoeff); - return GrDrawState::kNone_BlendOpt != opt || - (this->willEffectReadDstColor() && - kOne_GrBlendCoeff == srcCoeff && kZero_GrBlendCoeff == dstCoeff); + + this->calcColorInvariantOutput(); + this->calcCoverageInvariantOutput(); + return fXPFactory->canApplyCoverage(fColorProcInfo, fCoverageProcInfo, + this->isCoverageDrawing(), this->isColorWriteDisabled()); } bool GrDrawState::hasSolidCoverage() const { @@ -237,13 +220,22 @@ bool GrDrawState::hasSolidCoverage() const { //////////////////////////////////////////////////////////////////////////////s bool GrDrawState::willEffectReadDstColor() const { + this->calcColorInvariantOutput(); + this->calcCoverageInvariantOutput(); + // TODO: Remove need to create the XP here. + // Also once all custom blends are turned into XPs we can remove the need + // to check other stages since only xp's will be able to read dst + SkAutoTUnref<GrXferProcessor> xferProcessor(fXPFactory->createXferProcessor(fColorProcInfo, + fCoverageProcInfo)); + if (xferProcessor && xferProcessor->willReadDstColor()) { + return true; + } + if (!this->isColorWriteDisabled()) { - this->calcColorInvariantOutput(); if (fColorProcInfo.readsDst()) { return true; } } - this->calcCoverageInvariantOutput(); return fCoverageProcInfo.readsDst(); } @@ -288,21 +280,7 @@ void GrDrawState::AutoRestoreEffects::set(GrDrawState* ds) { // Some blend modes allow folding a fractional coverage value into the color's alpha channel, while // others will blend incorrectly. bool GrDrawState::canTweakAlphaForCoverage() const { - /* - 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. - */ - return kOne_GrBlendCoeff == fDstBlend || - kISA_GrBlendCoeff == fDstBlend || - kISC_GrBlendCoeff == fDstBlend || - this->isCoverageDrawing(); + return fXPFactory->canTweakAlphaForCoverage(this->isCoverageDrawing()); } //////////////////////////////////////////////////////////////////////////////// @@ -400,97 +378,6 @@ GrDrawState::~GrDrawState() { //////////////////////////////////////////////////////////////////////////////// -GrDrawState::BlendOpt GrDrawState::getBlendOpt(bool forceCoverage, - GrBlendCoeff* srcCoeff, - GrBlendCoeff* dstCoeff) const { - GrBlendCoeff bogusSrcCoeff, bogusDstCoeff; - if (NULL == srcCoeff) { - srcCoeff = &bogusSrcCoeff; - } - if (NULL == dstCoeff) { - dstCoeff = &bogusDstCoeff; - } - - *srcCoeff = this->getSrcBlendCoeff(); - *dstCoeff = this->getDstBlendCoeff(); - - if (this->isColorWriteDisabled()) { - *srcCoeff = kZero_GrBlendCoeff; - *dstCoeff = kOne_GrBlendCoeff; - } - - bool srcAIsOne = this->srcAlphaWillBeOne(); - bool dstCoeffIsOne = kOne_GrBlendCoeff == *dstCoeff || - (kSA_GrBlendCoeff == *dstCoeff && srcAIsOne); - bool dstCoeffIsZero = kZero_GrBlendCoeff == *dstCoeff || - (kISA_GrBlendCoeff == *dstCoeff && 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 == *srcCoeff && dstCoeffIsOne)) { - if (this->getStencil().doesWrite()) { - return kEmitCoverage_BlendOpt; - } else { - *dstCoeff = kOne_GrBlendCoeff; - return kSkipDraw_BlendOpt; - } - } - - bool hasCoverage = forceCoverage || !this->hasSolidCoverage(); - - // 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 == *srcCoeff) { - // 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 - *dstCoeff = kZero_GrBlendCoeff; - return kNone_BlendOpt; - } else if (kZero_GrBlendCoeff == *srcCoeff) { - // if the op is "clear" then we don't need to emit a color - // or blend, just write transparent black into the dst. - *srcCoeff = kOne_GrBlendCoeff; - *dstCoeff = kZero_GrBlendCoeff; - return kEmitTransBlack_BlendOpt; - } - } - } else if (this->isCoverageDrawing()) { - // we have coverage but we aren't distinguishing it from alpha by request. - return kCoverageAsAlpha_BlendOpt; - } else { - // check whether coverage can be safely rolled into alpha - // of if we can skip color computation and just emit coverage - if (this->canTweakAlphaForCoverage()) { - return kCoverageAsAlpha_BlendOpt; - } - if (dstCoeffIsZero) { - if (kZero_GrBlendCoeff == *srcCoeff) { - // 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. - *dstCoeff = kISA_GrBlendCoeff; - return kEmitCoverage_BlendOpt; - } 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. - *dstCoeff = kISA_GrBlendCoeff; - return kCoverageAsAlpha_BlendOpt; - } - } else if (dstCoeffIsOne) { - // the dst coeff is effectively one so blend works out to: - // cS + (c)(1)D + (1-c)D = cS + D. - *dstCoeff = kOne_GrBlendCoeff; - return kCoverageAsAlpha_BlendOpt; - } - } - - return kNone_BlendOpt; -} - bool GrDrawState::srcAlphaWillBeOne() const { this->calcColorInvariantOutput(); if (this->isCoverageDrawing()) { @@ -501,25 +388,10 @@ bool GrDrawState::srcAlphaWillBeOne() const { } bool GrDrawState::willBlendWithDst() const { - if (!this->hasSolidCoverage()) { - return true; - } - - if (this->willEffectReadDstColor()) { - return true; - } - - if (GrBlendCoeffRefsDst(this->getSrcBlendCoeff())) { - return true; - } - - GrBlendCoeff dstCoeff = this->getDstBlendCoeff(); - if (!(kZero_GrBlendCoeff == dstCoeff || - (kISA_GrBlendCoeff == dstCoeff && this->srcAlphaWillBeOne()))) { - return true; - } - - return false; + this->calcColorInvariantOutput(); + this->calcCoverageInvariantOutput(); + return fXPFactory->willBlendWithDst(fColorProcInfo, fCoverageProcInfo, + this->isCoverageDrawing(), this->isColorWriteDisabled()); } void GrDrawState::calcColorInvariantOutput() const { @@ -561,4 +433,3 @@ void GrDrawState::calcCoverageInvariantOutput() const { fCoverageProcInfoValid = true; } } - diff --git a/src/gpu/GrDrawState.h b/src/gpu/GrDrawState.h index 39163ff5ee..68c71f020a 100644 --- a/src/gpu/GrDrawState.h +++ b/src/gpu/GrDrawState.h @@ -17,7 +17,9 @@ #include "GrProcOptInfo.h" #include "GrRenderTarget.h" #include "GrStencil.h" +#include "GrXferProcessor.h" #include "SkMatrix.h" +#include "effects/GrPorterDuffXferProcessor.h" #include "effects/GrSimpleTextureEffect.h" class GrDrawTargetCaps; @@ -214,6 +216,22 @@ public: */ bool willEffectReadDstColor() const; + /** + * The xfer processor factory. + */ + const GrXPFactory* setXPFactory(const GrXPFactory* xpFactory) { + fXPFactory.reset(SkRef(xpFactory)); + return xpFactory; + } + + void setPorterDuffXPFactory(SkXfermode::Mode mode) { + fXPFactory.reset(GrPorterDuffXPFactory::Create(mode)); + } + + void setPorterDuffXPFactory(GrBlendCoeff src, GrBlendCoeff dst) { + fXPFactory.reset(GrPorterDuffXPFactory::Create(src, dst)); + } + const GrFragmentProcessor* addColorProcessor(const GrFragmentProcessor* effect) { SkASSERT(effect); SkNEW_APPEND_TO_TARRAY(&fColorStages, GrFragmentStage, (effect)); @@ -332,15 +350,6 @@ public: /// @name Blending //// - GrBlendCoeff getSrcBlendCoeff() const { return fSrcBlend; } - GrBlendCoeff getDstBlendCoeff() const { return fDstBlend; } - - /** - * Retrieves the last value set by setBlendConstant() - * @return the blending constant value - */ - GrColor getBlendConstant() const { return fBlendConstant; } - /** * Determines whether multiplying the computed per-pixel color by the pixel's fractional * coverage before the blend will give the correct final destination color. In general it @@ -348,44 +357,6 @@ public: */ bool canTweakAlphaForCoverage() const; - /** - * Sets the blending function coefficients. - * - * The blend function will be: - * D' = sat(S*srcCoef + D*dstCoef) - * - * where D is the existing destination color, S is the incoming source - * color, and D' is the new destination color that will be written. sat() - * is the saturation function. - * - * @param srcCoef coefficient applied to the src color. - * @param dstCoef coefficient applied to the dst color. - */ - void setBlendFunc(GrBlendCoeff srcCoeff, GrBlendCoeff dstCoeff) { - fSrcBlend = srcCoeff; - fDstBlend = dstCoeff; - #ifdef SK_DEBUG - if (GrBlendCoeffRefsDst(dstCoeff)) { - SkDebugf("Unexpected dst blend coeff. Won't work correctly with coverage stages.\n"); - } - if (GrBlendCoeffRefsSrc(srcCoeff)) { - SkDebugf("Unexpected src blend coeff. Won't work correctly with coverage stages.\n"); - } - #endif - } - - /** - * Sets the blending function constant referenced by the following blending - * coefficients: - * kConstC_GrBlendCoeff - * kIConstC_GrBlendCoeff - * kConstA_GrBlendCoeff - * kIConstA_GrBlendCoeff - * - * @param constant the constant to set - */ - void setBlendConstant(GrColor constant) { fBlendConstant = constant; } - /// @} /////////////////////////////////////////////////////////////////////////// @@ -637,47 +608,6 @@ public: private: bool isEqual(const GrDrawState& that) const; - /** - * Optimizations for blending / coverage to that can be applied based on the current state. - */ - enum BlendOpt { - /** - * No optimization - */ - kNone_BlendOpt, - /** - * Don't draw at all - */ - kSkipDraw_BlendOpt, - /** - * The coverage value does not have to be computed separately from alpha, the the output - * color can be the modulation of the two. - */ - kCoverageAsAlpha_BlendOpt, - /** - * Instead of emitting a src color, emit coverage in the alpha channel and r,g,b are - * "don't cares". - */ - kEmitCoverage_BlendOpt, - /** - * Emit transparent black instead of the src color, no need to compute coverage. - */ - kEmitTransBlack_BlendOpt - }; - - /** - * Determines what optimizations can be applied based on the blend. The coefficients may have - * to be tweaked in order for the optimization to work. srcCoeff and dstCoeff are optional - * params that receive the tweaked coefficients. Normally the function looks at the current - * state to see if coverage is enabled. By setting forceCoverage the caller can speculatively - * determine the blend optimizations that would be used if there was partial pixel coverage. - * - * This is used internally and when constructing a GrOptDrawState. - */ - BlendOpt getBlendOpt(bool forceCoverage = false, - GrBlendCoeff* srcCoeff = NULL, - GrBlendCoeff* dstCoeff = NULL) const; - const GrProcOptInfo& colorProcInfo() const { this->calcColorInvariantOutput(); return fColorProcInfo; @@ -716,13 +646,10 @@ private: SkAutoTUnref<GrRenderTarget> fRenderTarget; GrColor fColor; SkMatrix fViewMatrix; - GrColor fBlendConstant; uint32_t fFlagBits; GrStencilSettings fStencilSettings; uint8_t fCoverage; DrawFace fDrawFace; - GrBlendCoeff fSrcBlend; - GrBlendCoeff fDstBlend; SkAutoTUnref<const GrGeometryProcessor> fGeometryProcessor; SkAutoTUnref<const GrXPFactory> fXPFactory; FragmentStageArray fColorStages; diff --git a/src/gpu/GrOptDrawState.cpp b/src/gpu/GrOptDrawState.cpp index d95bd3b7c4..d3172010ed 100644 --- a/src/gpu/GrOptDrawState.cpp +++ b/src/gpu/GrOptDrawState.cpp @@ -11,6 +11,7 @@ #include "GrDrawTargetCaps.h" #include "GrGpu.h" #include "GrProcOptInfo.h" +#include "GrXferProcessor.h" GrOptDrawState::GrOptDrawState(const GrDrawState& drawState, const GrDrawTargetCaps& caps, @@ -19,14 +20,35 @@ GrOptDrawState::GrOptDrawState(const GrDrawState& drawState, GrGpu::DrawType drawType) : fFinalized(false) { fDrawType = drawType; - GrBlendCoeff optSrcCoeff; - GrBlendCoeff optDstCoeff; - GrDrawState::BlendOpt blendOpt = drawState.getBlendOpt(false, &optSrcCoeff, &optDstCoeff); + + const GrProcOptInfo& colorPOI = drawState.colorProcInfo(); + const GrProcOptInfo& coveragePOI = drawState.coverageProcInfo(); + + fColor = colorPOI.inputColorToEffectiveStage(); + fCoverage = drawState.getCoverage(); + + // Create XferProcessor from DS's XPFactory + SkAutoTUnref<GrXferProcessor> xferProcessor( + drawState.getXPFactory()->createXferProcessor(colorPOI, coveragePOI)); + + GrXferProcessor::OptFlags optFlags; + if (xferProcessor) { + fXferProcessor.reset(xferProcessor.get()); + + optFlags = xferProcessor->getOptimizations(colorPOI, + coveragePOI, + drawState.isCoverageDrawing(), + drawState.isColorWriteDisabled(), + drawState.getStencil().doesWrite(), + &fColor, + &fCoverage); + } // When path rendering the stencil settings are not always set on the draw state // so we must check the draw type. In cases where we will skip drawing we simply return a // null GrOptDrawState. - if (GrDrawState::kSkipDraw_BlendOpt == blendOpt && GrGpu::kStencilPath_DrawType != drawType) { + if (!xferProcessor || ((GrXferProcessor::kSkipDraw_OptFlag & optFlags) && + GrGpu::kStencilPath_DrawType != drawType)) { // Set the fields that don't default init and return. The lack of a render target will // indicate that this can be skipped. fFlags = 0; @@ -42,12 +64,8 @@ GrOptDrawState::GrOptDrawState(const GrDrawState& drawState, SkASSERT(fRenderTarget); fScissorState = scissorState; fViewMatrix = drawState.getViewMatrix(); - fBlendConstant = drawState.getBlendConstant(); fStencilSettings = drawState.getStencil(); fDrawFace = drawState.getDrawFace(); - fSrcBlend = optSrcCoeff; - fDstBlend = optDstCoeff; - // TODO move this out of optDrawState if (dstCopy) { fDstCopy = *dstCopy; @@ -73,10 +91,8 @@ GrOptDrawState::GrOptDrawState(const GrDrawState& drawState, bool hasLocalCoords = drawState.hasGeometryProcessor() && drawState.getGeometryProcessor()->hasLocalCoords(); - const GrProcOptInfo& colorPOI = drawState.colorProcInfo(); int firstColorStageIdx = colorPOI.firstEffectiveStageIndex(); fDescInfo.fInputColorIsUsed = colorPOI.inputColorIsUsed(); - fColor = colorPOI.inputColorToEffectiveStage(); if (colorPOI.removeVertexAttrib()) { fDescInfo.fHasVertexColor = false; } @@ -85,12 +101,18 @@ GrOptDrawState::GrOptDrawState(const GrDrawState& drawState, // drawState's coverageProcInfo (like color above) to set this initial information. int firstCoverageStageIdx = 0; fDescInfo.fInputCoverageIsUsed = true; - fCoverage = drawState.getCoverage(); - this->adjustProgramForBlendOpt(drawState, blendOpt, &firstColorStageIdx, - &firstCoverageStageIdx); - this->getStageStats(drawState, firstColorStageIdx, firstCoverageStageIdx, hasLocalCoords); + GrXferProcessor::BlendInfo blendInfo; + fXferProcessor->getBlendInfo(&blendInfo); + fSrcBlend = blendInfo.fSrcBlend; + fDstBlend = blendInfo.fDstBlend; + fBlendConstant = blendInfo.fBlendConstant; + + this->adjustProgramFromOptimizations(drawState, optFlags, colorPOI, coveragePOI, + &firstColorStageIdx, &firstCoverageStageIdx); + + fDescInfo.fRequiresLocalCoordAttrib = hasLocalCoords; // Copy GeometryProcesssor from DS or ODS SkASSERT(GrGpu::IsPathRenderingDrawType(drawType) || @@ -98,17 +120,13 @@ GrOptDrawState::GrOptDrawState(const GrDrawState& drawState, drawState.hasGeometryProcessor()); fGeometryProcessor.reset(drawState.getGeometryProcessor()); - // Create XferProcessor from DS's XPFactory - const GrXferProcessor* xpProcessor = drawState.getXPFactory()->createXferProcessor(); - fXferProcessor.reset(xpProcessor); - xpProcessor->unref(); - // Copy Stages from DS to ODS for (int i = firstColorStageIdx; i < drawState.numColorStages(); ++i) { SkNEW_APPEND_TO_TARRAY(&fFragmentStages, GrPendingFragmentStage, (drawState.fColorStages[i], hasLocalCoords)); } + fNumColorStages = fFragmentStages.count(); for (int i = firstCoverageStageIdx; i < drawState.numCoverageStages(); ++i) { SkNEW_APPEND_TO_TARRAY(&fFragmentStages, @@ -116,8 +134,6 @@ GrOptDrawState::GrOptDrawState(const GrDrawState& drawState, (drawState.fCoverageStages[i], hasLocalCoords)); } - this->setOutputStateInfo(drawState, blendOpt, caps); - // let the GP init the batch tracker if (drawState.hasGeometryProcessor()) { GrGeometryProcessor::InitBT init; @@ -127,10 +143,12 @@ GrOptDrawState::GrOptDrawState(const GrDrawState& drawState, init.fCoverage = this->getCoverage(); fGeometryProcessor->initBatchTracker(&fBatchTracker, init); } + + this->setOutputStateInfo(drawState, optFlags, caps); } void GrOptDrawState::setOutputStateInfo(const GrDrawState& ds, - GrDrawState::BlendOpt blendOpt, + GrXferProcessor::OptFlags optFlags, const GrDrawTargetCaps& caps) { // Set this default and then possibly change our mind if there is coverage. fDescInfo.fPrimaryOutputType = GrProgramDesc::kModulate_PrimaryOutputType; @@ -138,8 +156,7 @@ void GrOptDrawState::setOutputStateInfo(const GrDrawState& ds, // Determine whether we should use dual source blending or shader code to keep coverage // separate from color. - bool keepCoverageSeparate = !(GrDrawState::kCoverageAsAlpha_BlendOpt == blendOpt || - GrDrawState::kEmitCoverage_BlendOpt == blendOpt); + bool keepCoverageSeparate = !(optFlags & GrXferProcessor::kSetCoverageDrawing_OptFlag); if (keepCoverageSeparate && !ds.hasSolidCoverage()) { if (caps.dualSourceBlendingSupport()) { if (kZero_GrBlendCoeff == fDstBlend) { @@ -163,64 +180,35 @@ void GrOptDrawState::setOutputStateInfo(const GrDrawState& ds, } } -void GrOptDrawState::adjustProgramForBlendOpt(const GrDrawState& ds, - GrDrawState::BlendOpt blendOpt, - int* firstColorStageIdx, - int* firstCoverageStageIdx) { - switch (blendOpt) { - case GrDrawState::kNone_BlendOpt: - case GrDrawState::kSkipDraw_BlendOpt: - case GrDrawState::kCoverageAsAlpha_BlendOpt: - break; - case GrDrawState::kEmitCoverage_BlendOpt: - fColor = 0xffffffff; - fDescInfo.fInputColorIsUsed = true; - *firstColorStageIdx = ds.numColorStages(); - fDescInfo.fHasVertexColor = false; - break; - case GrDrawState::kEmitTransBlack_BlendOpt: - fColor = 0; - fCoverage = 0xff; - fDescInfo.fInputColorIsUsed = true; - fDescInfo.fInputCoverageIsUsed = true; - *firstColorStageIdx = ds.numColorStages(); - *firstCoverageStageIdx = ds.numCoverageStages(); - fDescInfo.fHasVertexColor = false; - fDescInfo.fHasVertexCoverage = false; - break; - } -} - -static void get_stage_stats(const GrFragmentStage& stage, bool* readsDst, bool* readsFragPosition) { - if (stage.getProcessor()->willReadDstColor()) { - *readsDst = true; - } - if (stage.getProcessor()->willReadFragmentPosition()) { - *readsFragPosition = true; - } -} - -void GrOptDrawState::getStageStats(const GrDrawState& ds, int firstColorStageIdx, - int firstCoverageStageIdx, bool hasLocalCoords) { - // We will need a local coord attrib if there is one currently set on the optState and we are - // actually generating some effect code - fDescInfo.fRequiresLocalCoordAttrib = hasLocalCoords && - ds.numTotalStages() - firstColorStageIdx - firstCoverageStageIdx > 0; - +void GrOptDrawState::adjustProgramFromOptimizations(const GrDrawState& ds, + GrXferProcessor::OptFlags flags, + const GrProcOptInfo& colorPOI, + const GrProcOptInfo& coveragePOI, + int* firstColorStageIdx, + int* firstCoverageStageIdx) { fDescInfo.fReadsDst = false; fDescInfo.fReadsFragPosition = false; - for (int s = firstColorStageIdx; s < ds.numColorStages(); ++s) { - const GrFragmentStage& stage = ds.getColorStage(s); - get_stage_stats(stage, &fDescInfo.fReadsDst, &fDescInfo.fReadsFragPosition); - } - for (int s = firstCoverageStageIdx; s < ds.numCoverageStages(); ++s) { - const GrFragmentStage& stage = ds.getCoverageStage(s); - get_stage_stats(stage, &fDescInfo.fReadsDst, &fDescInfo.fReadsFragPosition); + if (flags & GrXferProcessor::kClearColorStages_OptFlag) { + fDescInfo.fInputColorIsUsed = true; + *firstColorStageIdx = ds.numColorStages(); + fDescInfo.fHasVertexColor = false; + } else { + fDescInfo.fReadsDst = colorPOI.readsDst(); + fDescInfo.fReadsFragPosition = colorPOI.readsFragPosition(); } - if (ds.hasGeometryProcessor()) { - const GrGeometryProcessor& gp = *ds.getGeometryProcessor(); - fDescInfo.fReadsFragPosition = fDescInfo.fReadsFragPosition || gp.willReadFragmentPosition(); + + if (flags & GrXferProcessor::kClearCoverageStages_OptFlag) { + fDescInfo.fInputCoverageIsUsed = true; + *firstCoverageStageIdx = ds.numCoverageStages(); + fDescInfo.fHasVertexCoverage = false; + } else { + if (coveragePOI.readsDst()) { + fDescInfo.fReadsDst = true; + } + if (coveragePOI.readsFragPosition()) { + fDescInfo.fReadsFragPosition = true; + } } } diff --git a/src/gpu/GrOptDrawState.h b/src/gpu/GrOptDrawState.h index 45501b49dd..1878d16af3 100644 --- a/src/gpu/GrOptDrawState.h +++ b/src/gpu/GrOptDrawState.h @@ -194,22 +194,20 @@ private: /** * Alter the program desc and inputs (attribs and processors) based on the blend optimization. */ - void adjustProgramForBlendOpt(const GrDrawState& ds, GrDrawState::BlendOpt, - int* firstColorStageIdx, int* firstCoverageStageIdx); - - /** - * Loop over the effect stages to determine various info like what data they will read and what - * shaders they require. - */ - void getStageStats(const GrDrawState& ds, int firstColorStageIdx, int firstCoverageStageIdx, - bool hasLocalCoords); + void adjustProgramFromOptimizations(const GrDrawState& ds, + GrXferProcessor::OptFlags, + const GrProcOptInfo& colorPOI, + const GrProcOptInfo& coveragePOI, + int* firstColorStageIdx, + int* firstCoverageStageIdx); /** * Calculates the primary and secondary output types of the shader. For certain output types * the function may adjust the blend coefficients. After this function is called the src and dst * blend coeffs will represent those used by backend API. */ - void setOutputStateInfo(const GrDrawState& ds, GrDrawState::BlendOpt, const GrDrawTargetCaps&); + void setOutputStateInfo(const GrDrawState& ds, GrXferProcessor::OptFlags, + const GrDrawTargetCaps&); enum Flags { kDither_Flag = 0x1, diff --git a/src/gpu/GrPaint.cpp b/src/gpu/GrPaint.cpp index 443e1c0abf..5ed573a88b 100644 --- a/src/gpu/GrPaint.cpp +++ b/src/gpu/GrPaint.cpp @@ -8,7 +8,6 @@ #include "GrPaint.h" -#include "GrBlend.h" #include "GrProcOptInfo.h" #include "effects/GrPorterDuffXferProcessor.h" #include "effects/GrSimpleTextureEffect.h" @@ -52,73 +51,20 @@ bool GrPaint::isOpaqueAndConstantColor(GrColor* color) const { void GrPaint::resetStages() { fColorStages.reset(); fCoverageStages.reset(); - fXPFactory.reset(GrPorterDuffXPFactory::Create(SkXfermode::kSrcOver_Mode)); + fXPFactory.reset(GrPorterDuffXPFactory::Create(SkXfermode::kSrc_Mode)); } bool GrPaint::getOpaqueAndKnownColor(GrColor* solidColor, uint32_t* solidColorKnownComponents) const { - // TODO: Share this implementation with GrDrawState - GrProcOptInfo coverageProcInfo; coverageProcInfo.calcWithInitialValues(fCoverageStages.begin(), this->numCoverageStages(), 0xFFFFFFFF, kRGBA_GrColorComponentFlags, true); - - if (!coverageProcInfo.isSolidWhite()) { - return false; - } - GrProcOptInfo colorProcInfo; colorProcInfo.calcWithInitialValues(fColorStages.begin(), this->numColorStages(), fColor, kRGBA_GrColorComponentFlags, false); - SkASSERT((NULL == solidColor) == (NULL == solidColorKnownComponents)); - - GrBlendCoeff srcCoeff = fSrcBlendCoeff; - GrBlendCoeff dstCoeff = fDstBlendCoeff; - GrSimplifyBlend(&srcCoeff, &dstCoeff, colorProcInfo.color(), colorProcInfo.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 = colorProcInfo.color(); - *solidColorKnownComponents = colorProcInfo.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; + return fXPFactory->getOpaqueAndKnownColor(colorProcInfo, coverageProcInfo, solidColor, + solidColorKnownComponents); } diff --git a/src/gpu/GrProcOptInfo.cpp b/src/gpu/GrProcOptInfo.cpp index 18a32020d4..a84ac2e17f 100644 --- a/src/gpu/GrProcOptInfo.cpp +++ b/src/gpu/GrProcOptInfo.cpp @@ -7,8 +7,9 @@ #include "GrProcOptInfo.h" -#include "GrGeometryProcessor.h" +#include "GrFragmentProcessor.h" #include "GrFragmentStage.h" +#include "GrGeometryProcessor.h" void GrProcOptInfo::calcWithInitialValues(const GrFragmentStage* stages, int stageCount, @@ -22,6 +23,7 @@ void GrProcOptInfo::calcWithInitialValues(const GrFragmentStage* stages, fInputColor = startColor; fRemoveVertexAttrib = false; fReadsDst = false; + fReadsFragPosition = false; if (areCoverageStages && gp) { gp->computeInvariantOutput(&fInOut); @@ -31,17 +33,22 @@ void GrProcOptInfo::calcWithInitialValues(const GrFragmentStage* stages, const GrFragmentProcessor* processor = stages[i].getProcessor(); fInOut.resetWillUseInputColor(); processor->computeInvariantOutput(&fInOut); - #ifdef SK_DEBUG +#ifdef SK_DEBUG fInOut.validate(); - #endif +#endif if (!fInOut.willUseInputColor()) { fFirstEffectStageIndex = i; fInputColorIsUsed = false; - fReadsDst = false; // Reset this since we don't care if previous stages read dst + // Reset these since we don't care if previous stages read these values + fReadsDst = false; + fReadsFragPosition = false; } if (processor->willReadDstColor()) { fReadsDst = true; } + if (processor->willReadFragmentPosition()) { + fReadsFragPosition = true; + } if (kRGBA_GrColorComponentFlags == fInOut.validFlags()) { fFirstEffectStageIndex = i + 1; fInputColor = fInOut.color(); @@ -50,7 +57,10 @@ void GrProcOptInfo::calcWithInitialValues(const GrFragmentStage* stages, // Since we are clearing all previous color stages we are in a state where we have found // zero stages that don't multiply the inputColor. fInOut.resetNonMulStageFound(); - fReadsDst = false; // Reset this since we don't care if previous stages read dst + // Reset these since we don't care if previous stages read these values + fReadsDst = false; + fReadsFragPosition = false; } } } + diff --git a/src/gpu/GrProcOptInfo.h b/src/gpu/GrProcOptInfo.h index bb657d27c0..5778c4c343 100644 --- a/src/gpu/GrProcOptInfo.h +++ b/src/gpu/GrProcOptInfo.h @@ -12,7 +12,9 @@ #include "GrInvariantOutput.h" class GrFragmentStage; +class GrFragmentProcessor; class GrGeometryProcessor; +class GrProcessor; /** * GrProcOptInfo gathers invariant data from a set of processor stages.It is used to recognize @@ -27,7 +29,8 @@ public: , fInputColorIsUsed(true) , fInputColor(0) , fRemoveVertexAttrib(false) - , fReadsDst(false) {} + , fReadsDst(false) + , fReadsFragPosition(false) {} void calcWithInitialValues(const GrFragmentStage*, int stageCount, GrColor startColor, GrColorComponentFlags flags, bool areCoverageStages, @@ -74,6 +77,11 @@ public: */ bool readsDst() const { return fReadsDst; } + /** + * Returns true if any of the stages preserved by GrProcOptInfo read the frag position. + */ + bool readsFragPosition() const { return fReadsFragPosition; } + private: GrInvariantOutput fInOut; int fFirstEffectStageIndex; @@ -81,6 +89,7 @@ private: GrColor fInputColor; bool fRemoveVertexAttrib; bool fReadsDst; + bool fReadsFragPosition; }; #endif diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp index b4b24d9086..c0439f5e89 100644 --- a/src/gpu/SkGpuDevice.cpp +++ b/src/gpu/SkGpuDevice.cpp @@ -9,6 +9,7 @@ #include "effects/GrBicubicEffect.h" #include "effects/GrDashingEffect.h" +#include "effects/GrPorterDuffXferProcessor.h" #include "effects/GrTextureDomain.h" #include "effects/GrSimpleTextureEffect.h" @@ -673,7 +674,7 @@ GrTexture* create_mask_GPU(GrContext* context, // code path may not be taken. So we use a dst blend coeff of ISA. We // could special case AA draws to a dst surface with known alpha=0 to // use a zero dst coeff when dual source blending isn't available. - tempPaint.setBlendFunc(kOne_GrBlendCoeff, kISC_GrBlendCoeff); + tempPaint.setPorterDuffXPFactory(kOne_GrBlendCoeff, kISC_GrBlendCoeff); } GrContext::AutoMatrix am; diff --git a/src/gpu/SkGr.cpp b/src/gpu/SkGr.cpp index 9059f55c7a..82b1ce4b87 100644 --- a/src/gpu/SkGr.cpp +++ b/src/gpu/SkGr.cpp @@ -464,30 +464,21 @@ void SkPaint2GrPaintNoShader(GrContext* context, const SkPaint& skPaint, GrColor grPaint->setDither(skPaint.isDither()); grPaint->setAntiAlias(skPaint.isAntiAlias()); - SkXfermode::Coeff sm; - SkXfermode::Coeff dm; - SkXfermode* mode = skPaint.getXfermode(); GrFragmentProcessor* fragmentProcessor = NULL; GrXPFactory* xpFactory = NULL; - if (SkXfermode::AsFragmentProcessorOrXPFactory(mode, &fragmentProcessor, &xpFactory, - &sm, &dm)) { + if (SkXfermode::AsFragmentProcessorOrXPFactory(mode, &fragmentProcessor, &xpFactory)) { if (fragmentProcessor) { SkASSERT(NULL == xpFactory); grPaint->addColorProcessor(fragmentProcessor)->unref(); xpFactory = GrPorterDuffXPFactory::Create(SkXfermode::kSrc_Mode); - sm = SkXfermode::kOne_Coeff; - dm = SkXfermode::kZero_Coeff; } } else { // Fall back to src-over xpFactory = GrPorterDuffXPFactory::Create(SkXfermode::kSrcOver_Mode); - sm = SkXfermode::kOne_Coeff; - dm = SkXfermode::kISA_Coeff; } SkASSERT(xpFactory); grPaint->setXPFactory(xpFactory)->unref(); - grPaint->setBlendFunc(sk_blend_to_grblend(sm), sk_blend_to_grblend(dm)); //set the color of the paint to the one of the parameter grPaint->setColor(paintColor); diff --git a/src/gpu/effects/GrPorterDuffXferProcessor.cpp b/src/gpu/effects/GrPorterDuffXferProcessor.cpp index 55e0c93b23..52ef75572d 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.isSingleComponent()) { + // 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.isSingleComponent()) { + 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; +} + + diff --git a/tests/GLProgramsTest.cpp b/tests/GLProgramsTest.cpp index 18c14c45be..56707a0414 100644 --- a/tests/GLProgramsTest.cpp +++ b/tests/GLProgramsTest.cpp @@ -16,10 +16,12 @@ #include "GrInvariantOutput.h" #include "GrOptDrawState.h" #include "GrTest.h" +#include "GrXferProcessor.h" #include "SkChecksum.h" #include "SkRandom.h" #include "Test.h" #include "effects/GrConfigConversionEffect.h" +#include "effects/GrPorterDuffXferProcessor.h" #include "gl/GrGLPathRendering.h" #include "gl/GrGpuGL.h" #include "gl/builders/GrGLProgramBuilder.h" @@ -267,7 +269,8 @@ static void set_random_blend_func(GrDrawState* ds, SkRandom* random) { dst = GrBlendCoeff(random->nextRangeU(kFirstPublicGrBlendCoeff, kLastPublicGrBlendCoeff)); } while (GrBlendCoeffRefsDst(dst)); - ds->setBlendFunc(src, dst); + GrXPFactory* xpFactory = GrPorterDuffXPFactory::Create(src, dst); + ds->setXPFactory(xpFactory)->unref(); } // right now, the only thing we seem to care about in drawState's stencil is 'doesWrite()' |