Use dst copies in porter duffer XP to correctly render certain blends.
authoregdaniel <egdaniel@google.com>
Fri, 13 Feb 2015 18:23:19 +0000 (10:23 -0800)
committerCommit bot <commit-bot@chromium.org>
Fri, 13 Feb 2015 18:23:19 +0000 (10:23 -0800)
BUG=skia:

Review URL: https://codereview.chromium.org/914003003

12 files changed:
include/gpu/GrXferProcessor.h
include/gpu/effects/GrPorterDuffXferProcessor.h
src/effects/SkArithmeticMode_gpu.cpp
src/effects/SkArithmeticMode_gpu.h
src/gpu/GrXferProcessor.cpp
src/gpu/effects/GrCoverageSetOpXP.cpp
src/gpu/effects/GrCoverageSetOpXP.h
src/gpu/effects/GrCustomXfermode.cpp
src/gpu/effects/GrCustomXfermodePriv.h
src/gpu/effects/GrDisableColorXP.cpp
src/gpu/effects/GrDisableColorXP.h
src/gpu/effects/GrPorterDuffXferProcessor.cpp

index f748797..301390d 100644 (file)
@@ -261,14 +261,16 @@ protected:
     uint32_t fClassID;
 
 private:
-    virtual GrXferProcessor* onCreateXferProcessor(const GrProcOptInfo& colorPOI,
+    virtual GrXferProcessor* onCreateXferProcessor(const GrDrawTargetCaps& caps,
+                                                   const GrProcOptInfo& colorPOI,
                                                    const GrProcOptInfo& coveragePOI,
                                                    const GrDeviceCoordTexture* dstCopy) const = 0;
     /**
      *  Returns true if the XP generated by this factory will explicitly read dst in the fragment
      *  shader.
      */
-    virtual bool willReadDstColor(const GrProcOptInfo& colorPOI,
+    virtual bool willReadDstColor(const GrDrawTargetCaps& caps,
+                                  const GrProcOptInfo& colorPOI,
                                   const GrProcOptInfo& coveragePOI) const = 0;
 
     virtual bool onIsEqual(const GrXPFactory&) const = 0;
index 1bd1993..8d9e71f 100644 (file)
@@ -28,8 +28,10 @@ public:
 
     bool supportsRGBCoverage(GrColor knownColor, uint32_t knownColorFlags) const SK_OVERRIDE;
 
-    bool canApplyCoverage(const GrProcOptInfo& colorPOI,
-                          const GrProcOptInfo& coveragePOI) const SK_OVERRIDE;
+    bool canApplyCoverage(const GrProcOptInfo& /*colorPOI*/,
+                          const GrProcOptInfo& /*coveragePOI*/) const SK_OVERRIDE {
+        return true;
+    }
 
     bool canTweakAlphaForCoverage() const SK_OVERRIDE;
 
@@ -39,11 +41,13 @@ public:
 private:
     GrPorterDuffXPFactory(GrBlendCoeff src, GrBlendCoeff dst); 
 
-    GrXferProcessor* onCreateXferProcessor(const GrProcOptInfo& colorPOI,
+    GrXferProcessor* onCreateXferProcessor(const GrDrawTargetCaps& caps,
+                                           const GrProcOptInfo& colorPOI,
                                            const GrProcOptInfo& coveragePOI,
                                            const GrDeviceCoordTexture* dstCopy) const SK_OVERRIDE;
 
-    bool willReadDstColor(const GrProcOptInfo& colorPOI,
+    bool willReadDstColor(const GrDrawTargetCaps& caps,
+                          const GrProcOptInfo& colorPOI,
                           const GrProcOptInfo& coveragePOI) const SK_OVERRIDE;
 
     bool onIsEqual(const GrXPFactory& xpfBase) const SK_OVERRIDE {
index 469f5b1..e068c9d 100644 (file)
@@ -304,11 +304,12 @@ GrArithmeticXPFactory::GrArithmeticXPFactory(float k1, float k2, float k3, float
 }
 
 GrXferProcessor*
-GrArithmeticXPFactory::onCreateXferProcessor(const GrProcOptInfo& colorPOI,
+GrArithmeticXPFactory::onCreateXferProcessor(const GrDrawTargetCaps& caps,
+                                             const GrProcOptInfo& colorPOI,
                                              const GrProcOptInfo& coveragePOI,
                                              const GrDeviceCoordTexture* dstCopy) const {
     return ArithmeticXP::Create(fK1, fK2, fK3, fK4, fEnforcePMColor, dstCopy,
-                                this->willReadDstColor(colorPOI, coveragePOI));
+                                this->willReadDstColor(caps, colorPOI, coveragePOI));
 }
 
 
index 822e7bd..546902d 100644 (file)
@@ -96,11 +96,13 @@ public:
 private:
     GrArithmeticXPFactory(float k1, float k2, float k3, float k4, bool enforcePMColor); 
 
-    GrXferProcessor* onCreateXferProcessor(const GrProcOptInfo& colorPOI,
+    GrXferProcessor* onCreateXferProcessor(const GrDrawTargetCaps& caps,
+                                           const GrProcOptInfo& colorPOI,
                                            const GrProcOptInfo& coveragePOI,
                                            const GrDeviceCoordTexture* dstCopy) const SK_OVERRIDE; 
 
-    bool willReadDstColor(const GrProcOptInfo& colorPOI,
+    bool willReadDstColor(const GrDrawTargetCaps& caps,
+                          const GrProcOptInfo& colorPOI,
                           const GrProcOptInfo& coveragePOI) const SK_OVERRIDE {
         return true;
     }
index 321dcfd..358a249 100644 (file)
@@ -38,7 +38,7 @@ GrXferProcessor* GrXPFactory::createXferProcessor(const GrProcOptInfo& colorPOI,
                                                   const GrDeviceCoordTexture* dstCopy,
                                                   const GrDrawTargetCaps& caps) const {
 #ifdef SK_DEBUG
-    if (this->willReadDstColor(colorPOI, coveragePOI)) {
+    if (this->willReadDstColor(caps, colorPOI, coveragePOI)) {
         if (!caps.dstReadInShaderSupport()) {
             SkASSERT(dstCopy && dstCopy->texture());
         } else {
@@ -46,14 +46,13 @@ GrXferProcessor* GrXPFactory::createXferProcessor(const GrProcOptInfo& colorPOI,
         }
     } else {
         SkASSERT(!dstCopy || !dstCopy->texture()); 
-
     }
 #endif
-    return this->onCreateXferProcessor(colorPOI, coveragePOI, dstCopy);
+    return this->onCreateXferProcessor(caps, colorPOI, coveragePOI, dstCopy);
 }
 
 bool GrXPFactory::willNeedDstCopy(const GrDrawTargetCaps& caps, const GrProcOptInfo& colorPOI,
                                   const GrProcOptInfo& coveragePOI) const {
-    return (this->willReadDstColor(colorPOI, coveragePOI) && !caps.dstReadInShaderSupport());
+    return (this->willReadDstColor(caps, colorPOI, coveragePOI) && !caps.dstReadInShaderSupport());
 }
 
index f0ec1f9..40998d5 100644 (file)
@@ -228,7 +228,8 @@ GrXPFactory* GrCoverageSetOpXPFactory::Create(SkRegion::Op regionOp, bool invert
 }
 
 GrXferProcessor*
-GrCoverageSetOpXPFactory::onCreateXferProcessor(const GrProcOptInfo& colorPOI,
+GrCoverageSetOpXPFactory::onCreateXferProcessor(const GrDrawTargetCaps& caps,
+                                                const GrProcOptInfo& colorPOI,
                                                 const GrProcOptInfo& covPOI,
                                                 const GrDeviceCoordTexture* dstCopy) const {
     return CoverageSetOpXP::Create(fRegionOp, fInvertCoverage);
index 01dce95..e8900b3 100644 (file)
@@ -35,11 +35,13 @@ public:
 private:
     GrCoverageSetOpXPFactory(SkRegion::Op regionOp, bool invertCoverage);
 
-    GrXferProcessor* onCreateXferProcessor(const GrProcOptInfo& colorPOI,
+    GrXferProcessor* onCreateXferProcessor(const GrDrawTargetCaps& caps,
+                                           const GrProcOptInfo& colorPOI,
                                            const GrProcOptInfo& coveragePOI,
                                            const GrDeviceCoordTexture* dstCopy) const SK_OVERRIDE;
 
-    bool willReadDstColor(const GrProcOptInfo& colorPOI,
+    bool willReadDstColor(const GrDrawTargetCaps& caps,
+                          const GrProcOptInfo& colorPOI,
                           const GrProcOptInfo& coveragePOI) const SK_OVERRIDE {
         return false;
     }
index 8d8f9f2..e4d65e4 100644 (file)
@@ -608,10 +608,11 @@ GrCustomXPFactory::GrCustomXPFactory(SkXfermode::Mode mode)
 }
 
 GrXferProcessor*
-GrCustomXPFactory::onCreateXferProcessor(const GrProcOptInfo& colorPOI,
+GrCustomXPFactory::onCreateXferProcessor(const GrDrawTargetCaps& caps,
+                                         const GrProcOptInfo& colorPOI,
                                          const GrProcOptInfo& coveragePOI,
                                          const GrDeviceCoordTexture* dstCopy) const {
-    return CustomXP::Create(fMode, dstCopy, this->willReadDstColor(colorPOI, coveragePOI));
+    return CustomXP::Create(fMode, dstCopy, this->willReadDstColor(caps, colorPOI, coveragePOI));
 }
 
 
index 959b079..8672529 100644 (file)
@@ -77,11 +77,13 @@ public:
                             GrXPFactory::InvariantOutput*) const SK_OVERRIDE;
 
 private:
-    GrXferProcessor* onCreateXferProcessor(const GrProcOptInfo& colorPOI,
+    GrXferProcessor* onCreateXferProcessor(const GrDrawTargetCaps& caps,
+                                           const GrProcOptInfo& colorPOI,
                                            const GrProcOptInfo& coveragePOI,
                                            const GrDeviceCoordTexture* dstCopy) const SK_OVERRIDE; 
 
-    bool willReadDstColor(const GrProcOptInfo& colorPOI,
+    bool willReadDstColor(const GrDrawTargetCaps& caps,
+                          const GrProcOptInfo& colorPOI,
                           const GrProcOptInfo& coveragePOI) const SK_OVERRIDE {
         return true;
     }
index 637f99b..d97589d 100644 (file)
@@ -100,7 +100,8 @@ GrDisableColorXPFactory::GrDisableColorXPFactory() {
 }
 
 GrXferProcessor*
-GrDisableColorXPFactory::onCreateXferProcessor(const GrProcOptInfo& colorPOI,
+GrDisableColorXPFactory::onCreateXferProcessor(const GrDrawTargetCaps& caps,
+                                               const GrProcOptInfo& colorPOI,
                                                const GrProcOptInfo& covPOI,
                                                const GrDeviceCoordTexture* dstCopy) const {
     return DisableColorXP::Create();
index fe60b59..0067588 100644 (file)
@@ -39,11 +39,13 @@ public:
 private:
     GrDisableColorXPFactory();
 
-    GrXferProcessor* onCreateXferProcessor(const GrProcOptInfo& colorPOI,
+    GrXferProcessor* onCreateXferProcessor(const GrDrawTargetCaps& caps,
+                                           const GrProcOptInfo& colorPOI,
                                            const GrProcOptInfo& coveragePOI,
                                            const GrDeviceCoordTexture* dstCopy) const SK_OVERRIDE;
 
-    bool willReadDstColor(const GrProcOptInfo& colorPOI,
+    bool willReadDstColor(const GrDrawTargetCaps& caps,
+                          const GrProcOptInfo& colorPOI,
                           const GrProcOptInfo& coveragePOI) const SK_OVERRIDE {
         return false;
     }
index 6f2b63f..22029e1 100644 (file)
@@ -59,6 +59,10 @@ public:
         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
     };
 
     enum SecondaryOutputType {
@@ -87,11 +91,19 @@ public:
                                                const GrDrawTargetCaps& caps) SK_OVERRIDE;
 
     void getBlendInfo(GrXferProcessor::BlendInfo* blendInfo) const SK_OVERRIDE {
-        blendInfo->fSrcBlend = fSrcBlend;
-        blendInfo->fDstBlend = fDstBlend;
+        if (!this->willReadDstColor()) {
+            blendInfo->fSrcBlend = fSrcBlend;
+            blendInfo->fDstBlend = fDstBlend;
+        } else {
+            blendInfo->fSrcBlend = kOne_GrBlendCoeff;
+            blendInfo->fDstBlend = kZero_GrBlendCoeff;
+        }
         blendInfo->fBlendConstant = fBlendConstant;
     }
 
+    GrBlendCoeff getSrcBlend() const { return fSrcBlend; }
+    GrBlendCoeff getDstBlend() const { return fDstBlend; }
+
 private:
     PorterDuffXferProcessor(GrBlendCoeff srcBlend, GrBlendCoeff dstBlend, GrColor constant,
                             const GrDeviceCoordTexture* dstCopy, bool willReadDstColor);
@@ -128,6 +140,50 @@ private:
 
 ///////////////////////////////////////////////////////////////////////////////
 
+bool append_porterduff_term(GrGLFPFragmentBuilder* 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;
+    }
+}
+
 class GLPorterDuffXferProcessor : public GrGLXferProcessor {
 public:
     GLPorterDuffXferProcessor(const GrProcessor&) {}
@@ -139,16 +195,24 @@ public:
         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());
+        }
     };
 
 private:
     void onEmitCode(const EmitArgs& args) SK_OVERRIDE {
         const PorterDuffXferProcessor& xp = args.fXP.cast<PorterDuffXferProcessor>();
         GrGLFPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
-        if (xp.hasSecondaryOutput()) {
+        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);
+                    fsBuilder->codeAppendf("%s = %s;", args.fOutputSecondary,
+                                           args.fInputCoverage);
                     break;
                 case PorterDuffXferProcessor::kCoverageISA_SecondaryOutputType:
                     fsBuilder->codeAppendf("%s = (1.0 - %s.a) * %s;",
@@ -163,24 +227,43 @@ private:
                 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");
+
+            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);
         }
     }
 
@@ -196,7 +279,8 @@ PorterDuffXferProcessor::PorterDuffXferProcessor(GrBlendCoeff srcBlend,
                                                  GrColor constant,
                                                  const GrDeviceCoordTexture* dstCopy,
                                                  bool willReadDstColor)
-    : fSrcBlend(srcBlend)
+    : INHERITED(dstCopy, willReadDstColor)
+    , fSrcBlend(srcBlend)
     , fDstBlend(dstBlend)
     , fBlendConstant(constant)
     , fPrimaryOutputType(kModulate_PrimaryOutputType) 
@@ -244,6 +328,11 @@ PorterDuffXferProcessor::getOptimizations(const GrProcOptInfo& colorPOI,
 void PorterDuffXferProcessor::calcOutputTypes(GrXferProcessor::OptFlags optFlags,
                                               const GrDrawTargetCaps& caps,
                                               bool hasSolidCoverage) {
+    if (this->willReadDstColor()) {
+        fPrimaryOutputType = kCustom_PrimaryOutputType;
+        return;
+    }
+
     if (optFlags & kIgnoreColor_OptFlag) {
         if (optFlags & kIgnoreCoverage_OptFlag) {
             fPrimaryOutputType = kNone_PrimaryOutputType;
@@ -284,11 +373,12 @@ GrXferProcessor::OptFlags
 PorterDuffXferProcessor::internalGetOptimizations(const GrProcOptInfo& colorPOI,
                                                   const GrProcOptInfo& coveragePOI,
                                                   bool doesStencilWrite) {
-    bool srcAIsOne;
-    bool hasCoverage;
+    if (this->willReadDstColor()) {
+        return GrXferProcessor::kNone_Opt;
+    }
 
-    srcAIsOne = colorPOI.isOpaque();
-    hasCoverage = !coveragePOI.isSolidWhite();
+    bool srcAIsOne = colorPOI.isOpaque();
+    bool hasCoverage = !coveragePOI.isSolidWhite();
 
     bool dstCoeffIsOne = kOne_GrBlendCoeff == fDstBlend ||
                          (kSA_GrBlendCoeff == fDstBlend && srcAIsOne);
@@ -453,19 +543,20 @@ GrXPFactory* GrPorterDuffXPFactory::Create(SkXfermode::Mode mode) {
 }
 
 GrXferProcessor*
-GrPorterDuffXPFactory::onCreateXferProcessor(const GrProcOptInfo& colorPOI,
+GrPorterDuffXPFactory::onCreateXferProcessor(const GrDrawTargetCaps& caps,
+                                             const GrProcOptInfo& colorPOI,
                                              const GrProcOptInfo& covPOI,
                                              const GrDeviceCoordTexture* dstCopy) const {
     if (!covPOI.isFourChannelOutput()) {
         return PorterDuffXferProcessor::Create(fSrcCoeff, fDstCoeff, 0, dstCopy,
-                                               this->willReadDstColor(colorPOI, covPOI));
+                                               this->willReadDstColor(caps, colorPOI, covPOI));
     } else {
         if (this->supportsRGBCoverage(colorPOI.color(), colorPOI.validFlags())) {
             SkASSERT(kRGBA_GrColorComponentFlags == colorPOI.validFlags());
             GrColor blendConstant = GrUnPreMulColor(colorPOI.color());
             return PorterDuffXferProcessor::Create(kConstC_GrBlendCoeff, kISC_GrBlendCoeff,
                                                    blendConstant, dstCopy,
-                                                   this->willReadDstColor(colorPOI, covPOI));
+                                                   this->willReadDstColor(caps, colorPOI, covPOI));
         } else {
             return NULL;
         }
@@ -481,39 +572,6 @@ bool GrPorterDuffXPFactory::supportsRGBCoverage(GrColor /*knownColor*/,
     return false;
 }
 
-bool GrPorterDuffXPFactory::canApplyCoverage(const GrProcOptInfo& colorPOI,
-                                             const GrProcOptInfo& coveragePOI) const {
-    bool srcAIsOne = colorPOI.isOpaque();
-
-    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.
-    // check whether coverage can be safely rolled into alpha
-    // of if we can skip color computation and just emit coverage
-    if (this->canTweakAlphaForCoverage()) {
-        return true;
-    }
-    if (dstCoeffIsZero) {
-        if (kZero_GrBlendCoeff == fSrcCoeff) {
-            return true;
-        } else if (srcAIsOne) {
-            return  true;
-        }
-    } else if (dstCoeffIsOne) {
-        return true;
-    }
-
-    return false;
-}
-
 bool GrPorterDuffXPFactory::canTweakAlphaForCoverage() const {
     return can_tweak_alpha_for_coverage(fDstCoeff);
 }
@@ -587,9 +645,37 @@ void GrPorterDuffXPFactory::getInvariantOutput(const GrProcOptInfo& colorPOI,
     output->fWillBlendWithDst = false;
 }
 
-bool GrPorterDuffXPFactory::willReadDstColor(const GrProcOptInfo& colorPOI,
+bool GrPorterDuffXPFactory::willReadDstColor(const GrDrawTargetCaps& caps,
+                                             const GrProcOptInfo& colorPOI,
                                              const GrProcOptInfo& coveragePOI) const {
-    return false;
+    // We can always blend correctly if we have dual source blending.
+    if (caps.dualSourceBlendingSupport()) {
+        return false;
+    }
+
+    if (this->canTweakAlphaForCoverage()) {
+        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;
 }
 
 GR_DEFINE_XP_FACTORY_TEST(GrPorterDuffXPFactory);