Gpu blend optimizations, handle more xfer modes with fractional pixel coverage

Review URL: http://codereview.appspot.com/5237062/



git-svn-id: http://skia.googlecode.com/svn/trunk@2464 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/gpu/src/GrDrawTarget.cpp b/gpu/src/GrDrawTarget.cpp
index 2169fc7..0ba7ead 100644
--- a/gpu/src/GrDrawTarget.cpp
+++ b/gpu/src/GrDrawTarget.cpp
@@ -825,7 +825,7 @@
 
 // Some blend modes allow folding a partial coverage value into the color's
 // alpha channel, while others will blend incorrectly.
-bool GrDrawTarget::CanTweakAlphaForCoverage(GrBlendCoeff dstCoeff) {
+bool GrDrawTarget::canTweakAlphaForCoverage() const {
     /**
      * The fractional coverage is f
      * The src and dst coeffs are Cs and Cd
@@ -837,89 +837,186 @@
      * for Cd we find that only 1, ISA, and ISC produce the correct depth
      * coeffecient in terms of S' and D.
      */
-    return kOne_BlendCoeff == dstCoeff ||
-           kISA_BlendCoeff == dstCoeff ||
-           kISC_BlendCoeff == dstCoeff;
+    return kOne_BlendCoeff == fCurrDrawState.fDstBlend||
+           kISA_BlendCoeff == fCurrDrawState.fDstBlend ||
+           kISC_BlendCoeff == fCurrDrawState.fDstBlend;
 }
 
-bool GrDrawTarget::CanDisableBlend(GrVertexLayout layout, const DrState& state) {
-    // If we compute a coverage value (using edge AA or a coverage stage) then
-    // we can't force blending off.
-    if (state.fEdgeAANumEdges > 0 ||
-        (layout & kEdge_VertexLayoutBit) ||
-        (layout & kCoverage_VertexLayoutBit)) {
-        return false;
-    }
-    for (int s = state.fFirstCoverageStage; s < kNumStages; ++s) {
-        if (StageWillBeUsed(s, layout, state)) {
-            return false;
-        }
-    }
 
-    if ((kOne_BlendCoeff == state.fSrcBlend) &&
-        (kZero_BlendCoeff == state.fDstBlend)) {
-            return true;
-    }
+bool GrDrawTarget::srcAlphaWillBeOne() const {
+    const GrVertexLayout& layout = this->getGeomSrc().fVertexLayout;
 
-    // If we have vertex color without alpha then we can't force blend off
+    // Check if per-vertex or constant color may have partial alpha
     if ((layout & kColor_VertexLayoutBit) ||
-         0xff != GrColorUnpackA(state.fColor)) {
+        0xff != GrColorUnpackA(fCurrDrawState.fColor)) {
         return false;
     }
-
-    // If the src coef will always be 1...
-    if (kSA_BlendCoeff != state.fSrcBlend &&
-        kOne_BlendCoeff != state.fSrcBlend) {
+    // Check if color filter could introduce an alpha
+    // (TODO: Consider being more aggressive with regards to detecting 0xff
+    // final alpha from color filter).
+    if (SkXfermode::kDst_Mode != fCurrDrawState.fColorFilterXfermode) {
         return false;
     }
-
-    // ...and the dst coef is always 0...
-    if (kISA_BlendCoeff != state.fDstBlend &&
-        kZero_BlendCoeff != state.fDstBlend) {
-        return false;
-    }
-
-    // ...and there isn't a texture stage with an alpha channel...
-    for (int s = 0; s < state.fFirstCoverageStage; ++s) {
-        if (StageWillBeUsed(s, layout, state)) {
-            GrAssert(NULL != state.fTextures[s]);
-
-            GrPixelConfig config = state.fTextures[s]->config();
-
+    // Check if a color stage could create a partial alpha
+    for (int s = 0; s < fCurrDrawState.fFirstCoverageStage; ++s) {
+        if (StageWillBeUsed(s, layout, fCurrDrawState)) {
+            GrAssert(NULL != fCurrDrawState.fTextures[s]);
+            GrPixelConfig config = fCurrDrawState.fTextures[s]->config();
             if (!GrPixelConfigIsOpaque(config)) {
                 return false;
             }
         }
     }
-
-    // ...and there isn't an interesting color filter...
-    // TODO: Consider being more aggressive with regards to disabling
-    // blending when a color filter is used.
-    if (SkXfermode::kDst_Mode != state.fColorFilterXfermode) {
-        return false;
-    }
-
-    // ...then we disable blend.
     return true;
 }
 
-bool GrDrawTarget::CanUseHWAALines(GrVertexLayout layout, const DrState& state) {
+GrDrawTarget::BlendOptFlags
+GrDrawTarget::getBlendOpts(bool forceCoverage,
+                           GrBlendCoeff* srcCoeff,
+                           GrBlendCoeff* dstCoeff) const {
+
+    const GrVertexLayout& layout = this->getGeomSrc().fVertexLayout;
+
+    GrBlendCoeff bogusSrcCoeff, bogusDstCoeff;
+    if (NULL == srcCoeff) {
+        srcCoeff = &bogusSrcCoeff;
+    }
+    *srcCoeff = fCurrDrawState.fSrcBlend;
+
+    if (NULL == dstCoeff) {
+        dstCoeff = &bogusDstCoeff;
+    }
+    *dstCoeff = fCurrDrawState.fDstBlend;
+
+    // We don't ever expect source coeffecients to reference the source
+    GrAssert(kSA_BlendCoeff != *srcCoeff &&
+             kISA_BlendCoeff != *srcCoeff &&
+             kSC_BlendCoeff != *srcCoeff &&
+             kISC_BlendCoeff != *srcCoeff);
+    // same for dst
+    GrAssert(kDA_BlendCoeff != *dstCoeff &&
+             kIDA_BlendCoeff != *dstCoeff &&
+             kDC_BlendCoeff != *dstCoeff &&
+             kIDC_BlendCoeff != *dstCoeff);
+
+    if (SkToBool(kNoColorWrites_StateBit & fCurrDrawState.fFlagBits)) {
+        *srcCoeff = kZero_BlendCoeff;
+        *dstCoeff = kOne_BlendCoeff;
+    }
+
+    bool srcAIsOne = this->srcAlphaWillBeOne();
+    bool dstCoeffIsOne = kOne_BlendCoeff == *dstCoeff ||
+                         (kSA_BlendCoeff == *dstCoeff && srcAIsOne);
+    bool dstCoeffIsZero = kZero_BlendCoeff == *dstCoeff ||
+                         (kISA_BlendCoeff == *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_BlendCoeff == *srcCoeff && dstCoeffIsOne)) {
+        if (fCurrDrawState.fStencilSettings.doesWrite()) {
+            if (fCaps.fShaderSupport) {
+                return kDisableBlend_BlendOptFlag |
+                       kEmitTransBlack_BlendOptFlag;
+            } else {
+                return kDisableBlend_BlendOptFlag;
+            }
+        } else {
+            return kSkipDraw_BlendOptFlag;
+        }
+    }
+
+    // check for coverage due to edge aa or coverage texture stage
+    bool hasCoverage = forceCoverage ||
+                       fCurrDrawState.fEdgeAANumEdges > 0 ||
+                       (layout & kCoverage_VertexLayoutBit) ||
+                       (layout & kEdge_VertexLayoutBit);
+    for (int s = fCurrDrawState.fFirstCoverageStage;
+         !hasCoverage && s < kNumStages;
+         ++s) {
+        if (StageWillBeUsed(s, layout, fCurrDrawState)) {
+            hasCoverage = true;
+        }
+    }
+
+    // if we don't have coverage we can check whether the dst
+    // has to read at all. If not, we'll disable blending.
+    if (!hasCoverage) {
+        if (dstCoeffIsZero) {
+            if (kOne_BlendCoeff == *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
+                return kDisableBlend_BlendOptFlag;
+            } else if (kZero_BlendCoeff == *srcCoeff &&
+                       fCaps.fShaderSupport) {
+                // 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_BlendCoeff;
+                *dstCoeff = kZero_BlendCoeff;
+                return kDisableBlend_BlendOptFlag |
+                       kEmitTransBlack_BlendOptFlag;
+            }
+        }
+    } 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_BlendOptFlag;
+        }
+        // We haven't implemented support for these optimizations in the
+        // fixed pipe (which is on its deathbed)
+        if (fCaps.fShaderSupport) {
+            if (dstCoeffIsZero) {
+                if (kZero_BlendCoeff == *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_BlendCoeff;
+                    return  kEmitCoverage_BlendOptFlag;
+                } 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_BlendCoeff;
+                    return  kCoverageAsAlpha_BlendOptFlag;
+                }
+            } 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_BlendCoeff;
+                return  kCoverageAsAlpha_BlendOptFlag;
+            }
+        }
+    }
+    return kNone_BlendOpt;
+}
+
+bool GrDrawTarget::willUseHWAALines() const {
     // there is a conflict between using smooth lines and our use of
     // premultiplied alpha. Smooth lines tweak the incoming alpha value
     // but not in a premul-alpha way. So we only use them when our alpha
     // is 0xff and tweaking the color for partial coverage is OK
-    return (kAntialias_StateBit & state.fFlagBits) &&
-           CanDisableBlend(layout, state) &&
-           CanTweakAlphaForCoverage(state.fDstBlend);
+    if (!fCaps.fHWAALineSupport ||
+        !(kAntialias_StateBit & fCurrDrawState.fFlagBits)) {
+        return false;
+    }
+    BlendOptFlags opts = this->getBlendOpts();
+    return (kDisableBlend_BlendOptFlag & opts) &&
+           (kCoverageAsAlpha_BlendOptFlag & opts);
 }
 
 bool GrDrawTarget::canApplyCoverage() const {
+    // we can correctly apply coverage if a) we have dual source blending
+    // or b) one of our blend optimizations applies.
     return this->getCaps().fDualSourceBlendingSupport ||
-           CanTweakAlphaForCoverage(fCurrDrawState.fDstBlend);
+           kNone_BlendOpt != this->getBlendOpts(true);
 }
 
-bool GrDrawTarget::canDisableBlend() const {
-    return CanDisableBlend(this->getGeomSrc().fVertexLayout, fCurrDrawState);
+bool GrDrawTarget::drawWillReadDst() const {
+    return SkToBool((kDisableBlend_BlendOptFlag | kSkipDraw_BlendOptFlag) &
+                    this->getBlendOpts());
 }
 
 ///////////////////////////////////////////////////////////////////////////////