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/GrDefaultPathRenderer.cpp b/gpu/src/GrDefaultPathRenderer.cpp
index fd56c07..14c032b 100644
--- a/gpu/src/GrDefaultPathRenderer.cpp
+++ b/gpu/src/GrDefaultPathRenderer.cpp
@@ -179,7 +179,7 @@
         return hint == kConvex_ConvexHint ||
                hint == kNonOverlappingConvexPieces_ConvexHint ||
                (hint == kSameWindingConvexPieces_ConvexHint &&
-                target.canDisableBlend() && !target.isDitherState());
+                !target.drawWillReadDst() && !target.isDitherState());
 
     }
     return false;
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());
 }
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/gpu/src/GrDrawTarget.h b/gpu/src/GrDrawTarget.h
index e411338..10633f5 100644
--- a/gpu/src/GrDrawTarget.h
+++ b/gpu/src/GrDrawTarget.h
@@ -131,7 +131,6 @@
                                         //   pairs for convexity while 
                                         //   rasterizing.  Set this if the
                                         //   source polygon is non-convex.
-
         // subclass may use additional bits internally
         kDummyStateBit,
         kLastPublicStateBit = kDummyStateBit-1
@@ -524,13 +523,13 @@
     GrColor getBlendConstant() const { return fCurrDrawState.fBlendConstant; }
 
     /**
-     * Determines if blend is effectively disabled.
+     * Determines if blending will require a read of a dst given the current
+     * state set on the draw target
      *
-     * @return true if blend can be disabled without changing the rendering
-     *  result given the current state including the vertex layout specified
-     *  with the vertex source.
+     * @return true if the dst surface will be read at each pixel hit by the
+     *         a draw operation.
      */
-    bool canDisableBlend() const;
+    bool drawWillReadDst() const;
 
     /**
      * Color alpha and coverage are two inputs to the drawing pipeline. For some
@@ -550,9 +549,7 @@
      * color specified by setColor or per-vertex colors will give the right
      * blending result.
      */
-    bool canTweakAlphaForCoverage() const {
-        return CanTweakAlphaForCoverage(fCurrDrawState.fDstBlend);
-    }
+    bool canTweakAlphaForCoverage() const;
 
      /**
       * Determines the interpretation per-vertex edge data when the
@@ -568,7 +565,7 @@
      * lines be used (if line primitive type is drawn)? (Note that lines are
      * always 1 pixel wide)
      */
-    virtual bool willUseHWAALines() const = 0;
+     bool willUseHWAALines() const;
 
     /**
      * Sets the edge data required for edge antialiasing.
@@ -653,6 +650,7 @@
 
 private:
     static const int TEX_COORD_BIT_CNT = kNumStages*kMaxTexCoords;
+
 public:
     /**
      * Generates a bit indicating that a texture stage uses the position
@@ -1299,15 +1297,57 @@
 
 protected:
 
-    // Determines whether it is correct to apply partial pixel coverage
-    // by multiplying the src color by the fractional coverage.
-    static bool CanTweakAlphaForCoverage(GrBlendCoeff dstCoeff);
-    
-    // determines whether HW blending can be disabled or not
-    static bool CanDisableBlend(GrVertexLayout layout, const DrState& state);
+    /**
+     * Optimizations for blending / coverage to be applied based on the current
+     * state.
+     * Subclasses that actually draw (as opposed to those that just buffer for
+     * playback) must implement the flags that replace the output color.
+     */
+    enum BlendOptFlags {
+        /**
+         * No optimization
+         */
+        kNone_BlendOpt = 0,
+        /**
+         * Don't draw at all
+         */
+        kSkipDraw_BlendOptFlag = 0x2,
+        /**
+         * Emit the src color, disable HW blending (replace dst with src)
+         */
+        kDisableBlend_BlendOptFlag = 0x4,
+        /**
+         * The coverage value does not have to be computed separately from
+         * alpha, the the output color can be the modulation of the two.
+         */
+        kCoverageAsAlpha_BlendOptFlag = 0x1,
+        /**
+         * Instead of emitting a src color, emit coverage in the alpha channel
+         * and r,g,b are "don't cares".
+         */
+        kEmitCoverage_BlendOptFlag = 0x10,
+        /**
+         * Emit transparent black instead of the src color, no need to compute
+         * coverage.
+         */
+        kEmitTransBlack_BlendOptFlag = 0x8,
+    };
+    GR_DECL_BITFIELD_OPS_FRIENDS(BlendOptFlags);
 
-    // determines whether HW AA lines can be used or not
-    static bool CanUseHWAALines(GrVertexLayout layout, const DrState& state);
+    // Determines what optimizations can be applied based on the blend.
+    // The coeffecients may have to be tweaked in order for the optimization
+    // to work. srcCoeff and dstCoeff are optional params that receive the
+    // tweaked coeffecients.
+    // 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
+    BlendOptFlags getBlendOpts(bool forceCoverage = false,
+                               GrBlendCoeff* srcCoeff = NULL,
+                               GrBlendCoeff* dstCoeff = NULL) const;
+
+    // determine if src alpha is guaranteed to be one for all src pixels
+    bool srcAlphaWillBeOne() const;
 
     enum GeometrySrcType {
         kNone_GeometrySrcType,     //<! src has not been specified
@@ -1431,4 +1471,6 @@
     
 };
 
+GR_MAKE_BITFIELD_OPS(GrDrawTarget::BlendOptFlags);
+
 #endif
diff --git a/gpu/src/GrGLProgram.cpp b/gpu/src/GrGLProgram.cpp
index 4b97fab..4b4140d 100644
--- a/gpu/src/GrGLProgram.cpp
+++ b/gpu/src/GrGLProgram.cpp
@@ -598,6 +598,20 @@
         uniformCoeff = SkXfermode::kZero_Coeff;
     }
 
+    // If we know the final color is going to be all zeros then we can
+    // simplify the color filter coeffecients. needComputedColor will then
+    // come out false below.
+    if (ProgramDesc::kTransBlack_ColorType == fProgramDesc.fColorType) {
+        colorCoeff = SkXfermode::kZero_Coeff;
+        if (SkXfermode::kDC_Coeff == uniformCoeff ||
+            SkXfermode::kDA_Coeff == uniformCoeff) {
+            uniformCoeff = SkXfermode::kZero_Coeff;
+        } else if (SkXfermode::kIDC_Coeff == uniformCoeff ||
+                   SkXfermode::kIDA_Coeff == uniformCoeff) {
+            uniformCoeff = SkXfermode::kOne_Coeff;
+        }
+    }
+
     bool needColorFilterUniform;
     bool needComputedColor;
     needBlendInputs(uniformCoeff, colorCoeff,
@@ -645,8 +659,13 @@
                 programData->fUniLocations.fColorUni = kUseUniform;
                 inColor = COL_UNI_NAME;
                 break;
+            case ProgramDesc::kTransBlack_ColorType:
+                GrAssert(!"needComputedColor should be false.");
+                break;
+            case ProgramDesc::kSolidWhite_ColorType:
+                break;
             default:
-                GrAssert(ProgramDesc::kNone_ColorType == fProgramDesc.fColorType);
+                GrCrash("Unknown color type.");
                 break;
         }
     }
@@ -708,15 +727,18 @@
         }
     }
 
-    // if have all ones for the "dst" input to the color filter then we can make
-    // additional optimizations.
-    if (needColorFilterUniform && !inColor.size() &&
-        (SkXfermode::kIDC_Coeff == uniformCoeff ||
-         SkXfermode::kIDA_Coeff == uniformCoeff)) {
-          uniformCoeff = SkXfermode::kZero_Coeff;
-          bool bogus;
-          needBlendInputs(SkXfermode::kZero_Coeff, colorCoeff,
-                          &needColorFilterUniform, &bogus);
+    // if have all ones or zeros for the "dst" input to the color filter then we
+    // may be able to make additional optimizations.
+    if (needColorFilterUniform && needComputedColor && !inColor.size()) {
+        GrAssert(ProgramDesc::kSolidWhite_ColorType == fProgramDesc.fColorType);
+        bool uniformCoeffIsZero = SkXfermode::kIDC_Coeff == uniformCoeff ||
+                                  SkXfermode::kIDA_Coeff == uniformCoeff;
+        if (uniformCoeffIsZero) {
+            uniformCoeff = SkXfermode::kZero_Coeff;
+            bool bogus;
+            needBlendInputs(SkXfermode::kZero_Coeff, colorCoeff,
+                            &needColorFilterUniform, &bogus);
+        }
     }
     if (needColorFilterUniform) {
         segments.fFSUnis.push_back().set(GrGLShaderVar::kVec4f_Type,
@@ -733,7 +755,16 @@
         wroteFragColorZero = true;
     } else if (SkXfermode::kDst_Mode != fProgramDesc.fColorFilterXfermode) {
         segments.fFSCode.appendf("\tvec4 filteredColor;\n");
-        const char* color = inColor.size() ? inColor.c_str() : all_ones_vec(4);
+        const char* color;
+        if (inColor.size()) {
+            color = inColor.c_str();
+        } else {
+            if (ProgramDesc::kSolidWhite_ColorType == fProgramDesc.fColorType) {
+                color = all_ones_vec(4);
+            } else {
+                color = all_zeros_vec(4);
+            }
+        }
         addColorFilter(&segments.fFSCode, "filteredColor", uniformCoeff,
                        colorCoeff, color);
         inColor = "filteredColor";
diff --git a/gpu/src/GrGLProgram.h b/gpu/src/GrGLProgram.h
index 197da5d..dafa79d 100644
--- a/gpu/src/GrGLProgram.h
+++ b/gpu/src/GrGLProgram.h
@@ -139,10 +139,15 @@
             }
         };
 
+        // Specifies where the intitial color comes from before the stages are
+        // applied.
         enum ColorType {
-            kNone_ColorType         = 0,
-            kAttribute_ColorType    = 1,
-            kUniform_ColorType      = 2,
+            kSolidWhite_ColorType,
+            kTransBlack_ColorType,
+            kAttribute_ColorType,
+            kUniform_ColorType,
+
+            kColorTypeCnt
         };
         // Dual-src blending makes use of a secondary output color that can be
         // used as a per-pixel blend coeffecient. This controls whether a
diff --git a/gpu/src/GrGpu.cpp b/gpu/src/GrGpu.cpp
index 850fa60..b05f2c0 100644
--- a/gpu/src/GrGpu.cpp
+++ b/gpu/src/GrGpu.cpp
@@ -140,13 +140,6 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
-bool GrGpu::willUseHWAALines() const {
-    return (kAntialias_StateBit & fCurrDrawState.fFlagBits) &&
-           CanUseHWAALines(this->getGeomSrc().fVertexLayout, fCurrDrawState);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-
 GrTexture* GrGpu::createTexture(const GrTextureDesc& desc,
                                 const void* srcData, size_t rowBytes) {
     this->handleDirtyContext();
diff --git a/gpu/src/GrGpu.h b/gpu/src/GrGpu.h
index 3193a56..9107554 100644
--- a/gpu/src/GrGpu.h
+++ b/gpu/src/GrGpu.h
@@ -219,7 +219,6 @@
     void removeResource(GrResource* resource);
 
     // GrDrawTarget overrides
-    virtual bool willUseHWAALines() const;
     virtual void clear(const GrIRect* rect, GrColor color);
 
 protected:
diff --git a/gpu/src/GrGpuGL.cpp b/gpu/src/GrGpuGL.cpp
index ffbb557..b27815b 100644
--- a/gpu/src/GrGpuGL.cpp
+++ b/gpu/src/GrGpuGL.cpp
@@ -1811,8 +1811,8 @@
     }
 }
 
-void GrGpuGL::flushBlend(GrPrimitiveType type, 
-                         GrBlendCoeff srcCoeff, 
+void GrGpuGL::flushBlend(GrPrimitiveType type,
+                         GrBlendCoeff srcCoeff,
                          GrBlendCoeff dstCoeff) {
     if (GrIsPrimTypeLines(type) && this->willUseHWAALines()) {
         if (fHWBlendDisabled) {
@@ -1827,7 +1827,11 @@
             fHWDrawState.fDstBlend = kISA_BlendCoeff;
         }
     } else {
-        bool blendOff = canDisableBlend();
+        // any optimization to disable blending should
+        // have already been applied and tweaked the coeffs
+        // to (1, 0).
+        bool blendOff = kOne_BlendCoeff == srcCoeff &&
+                        kZero_BlendCoeff == dstCoeff;
         if (fHWBlendDisabled != blendOff) {
             if (blendOff) {
                 GL_CALL(Disable(GR_GL_BLEND));
diff --git a/gpu/src/GrGpuGL.h b/gpu/src/GrGpuGL.h
index 353394f..a2630c8 100644
--- a/gpu/src/GrGpuGL.h
+++ b/gpu/src/GrGpuGL.h
@@ -123,7 +123,10 @@
     // line width
     bool flushGLStateCommon(GrPrimitiveType type);
 
-    // subclass should call this to flush the blend state
+    // Subclasses should call this to flush the blend state.
+    // The params should be the final coeffecients to apply
+    // (after any blending optimizations or dual source blending considerations
+    // have been accounted for).
     void flushBlend(GrPrimitiveType type,
                     GrBlendCoeff srcCoeff,
                     GrBlendCoeff dstCoeff);
diff --git a/gpu/src/GrGpuGLFixed.cpp b/gpu/src/GrGpuGLFixed.cpp
index c9d5844..336b687 100644
--- a/gpu/src/GrGpuGLFixed.cpp
+++ b/gpu/src/GrGpuGLFixed.cpp
@@ -172,7 +172,13 @@
         return false;
     }
 
-    this->flushBlend(type, fCurrDrawState.fSrcBlend, fCurrDrawState.fDstBlend);
+    GrBlendCoeff srcCoeff, dstCoeff;
+    if (kSkipDraw_BlendOptFlag & 
+        this->getBlendOpts(false, &srcCoeff, &dstCoeff)) {
+        return false;
+    }
+
+    this->flushBlend(type, srcCoeff, dstCoeff);
 
     if (fDirtyFlags.fRenderTargetChanged) {
         flushProjectionMatrix();
diff --git a/gpu/src/GrGpuGLShaders.cpp b/gpu/src/GrGpuGLShaders.cpp
index 9c0c9cc..f1bc5ec 100644
--- a/gpu/src/GrGpuGLShaders.cpp
+++ b/gpu/src/GrGpuGLShaders.cpp
@@ -204,14 +204,8 @@
 
         pdesc.fVertexLayout = 0;
         pdesc.fEmitsPointSize = random.nextF() > .5f;
-        float colorType = random.nextF();
-        if (colorType < 1.f / 3.f) {
-            pdesc.fColorType = ProgramDesc::kAttribute_ColorType;
-        } else if (colorType < 2.f / 3.f) {
-            pdesc.fColorType = ProgramDesc::kUniform_ColorType;
-        } else {
-            pdesc.fColorType = ProgramDesc::kNone_ColorType;
-        }
+        pdesc.fColorType = static_cast<int>(random.nextF() *
+                                            ProgramDesc::kColorTypeCnt);
 
         int idx = (int)(random.nextF() * (SkXfermode::kCoeffModesCnt));
         pdesc.fColorFilterXfermode = (SkXfermode::Mode)idx;
@@ -589,7 +583,7 @@
     GrColorUnpackA(color) * ONE_OVER_255 \
 }
 
-void GrGpuGLShaders::flushColor() {
+void GrGpuGLShaders::flushColor(GrColor color) {
     const ProgramDesc& desc = fCurrentProgram.getDesc();
     if (this->getGeomSrc().fVertexLayout & kColor_VertexLayoutBit) {
         // color will be specified per-vertex as an attribute
@@ -598,27 +592,27 @@
     } else {
         switch (desc.fColorType) {
             case ProgramDesc::kAttribute_ColorType:
-                if (fHWDrawState.fColor != fCurrDrawState.fColor) {
+                if (fHWDrawState.fColor != color) {
                     // OpenGL ES only supports the float varities of glVertexAttrib
-                    float c[] = GR_COLOR_TO_VEC4(fCurrDrawState.fColor);
+                    float c[] = GR_COLOR_TO_VEC4(color);
                     GL_CALL(VertexAttrib4fv(GrGLProgram::ColorAttributeIdx(), 
                                             c));
-                    fHWDrawState.fColor = fCurrDrawState.fColor;
+                    fHWDrawState.fColor = color;
                 }
                 break;
             case ProgramDesc::kUniform_ColorType:
-                if (fProgramData->fColor != fCurrDrawState.fColor) {
+                if (fProgramData->fColor != color) {
                     // OpenGL ES only supports the float varities of glVertexAttrib
-                    float c[] = GR_COLOR_TO_VEC4(fCurrDrawState.fColor);
+                    float c[] = GR_COLOR_TO_VEC4(color);
                     GrAssert(GrGLProgram::kUnusedUniform != 
                              fProgramData->fUniLocations.fColorUni);
                     GL_CALL(Uniform4fv(fProgramData->fUniLocations.fColorUni,
                                         1, c));
-                    fProgramData->fColor = fCurrDrawState.fColor;
+                    fProgramData->fColor = color;
                 }
                 break;
-            case ProgramDesc::kNone_ColorType:
-                GrAssert(0xffffffff == fCurrDrawState.fColor);
+            case ProgramDesc::kSolidWhite_ColorType:
+            case ProgramDesc::kTransBlack_ColorType:
                 break;
             default:
                 GrCrash("Unknown color type.");
@@ -648,7 +642,14 @@
         fProgramCache->invalidateViewMatrices();
     }
 
-    buildProgram(type);
+    GrBlendCoeff srcCoeff;
+    GrBlendCoeff dstCoeff;
+    BlendOptFlags blendOpts = this->getBlendOpts(false, &srcCoeff, &dstCoeff);
+    if (kSkipDraw_BlendOptFlag & blendOpts) {
+        return false;
+    }
+
+    this->buildProgram(type, blendOpts, dstCoeff);
     fProgramData = fProgramCache->getProgramData(fCurrentProgram);
     if (NULL == fProgramData) {
         GrAssert(!"Failed to create program!");
@@ -659,13 +660,18 @@
         GL_CALL(UseProgram(fProgramData->fProgramID));
         fHWProgramID = fProgramData->fProgramID;
     }
-    GrBlendCoeff srcCoeff = fCurrDrawState.fSrcBlend;
-    GrBlendCoeff dstCoeff = fCurrDrawState.fDstBlend;
-    
     fCurrentProgram.overrideBlend(&srcCoeff, &dstCoeff);
     this->flushBlend(type, srcCoeff, dstCoeff);
 
-    this->flushColor();
+    GrColor color;
+    if (blendOpts & kEmitTransBlack_BlendOptFlag) {
+        color = 0;
+    } else if (blendOpts & kEmitCoverage_BlendOptFlag) {
+        color = 0xffffffff;
+    } else {
+        color = fCurrDrawState.fColor;
+    }
+    this->flushColor(color);
 
     GrMatrix* currViewMatrix;
     if (GrGLProgram::kSetAsAttribute == 
@@ -836,9 +842,19 @@
     fHWGeometryState.fArrayPtrsDirty = false;
 }
 
-void GrGpuGLShaders::buildProgram(GrPrimitiveType type) {
+void GrGpuGLShaders::buildProgram(GrPrimitiveType type,
+                                  BlendOptFlags blendOpts,
+                                  GrBlendCoeff dstCoeff) {
     ProgramDesc& desc = fCurrentProgram.fProgramDesc;
 
+    // This should already have been caught
+    GrAssert(!(kSkipDraw_BlendOptFlag & blendOpts));
+
+    bool skipCoverage = SkToBool(blendOpts & kEmitTransBlack_BlendOptFlag);
+
+    bool skipColor = SkToBool(blendOpts & (kEmitTransBlack_BlendOptFlag |
+                                           kEmitCoverage_BlendOptFlag));
+
     // The descriptor is used as a cache key. Thus when a field of the
     // descriptor will not affect program generation (because of the vertex
     // layout in use or other descriptor field settings) it should be set
@@ -849,35 +865,47 @@
 
     desc.fEmitsPointSize = kPoints_PrimitiveType == type;
 
-    bool requiresAttributeColors = 0 != (desc.fVertexLayout & kColor_VertexLayoutBit);
+    bool requiresAttributeColors = 
+        !skipColor && SkToBool(desc.fVertexLayout & kColor_VertexLayoutBit);
     // fColorType records how colors are specified for the program. Strip
     // the bit from the layout to avoid false negatives when searching for an
     // existing program in the cache.
     desc.fVertexLayout &= ~(kColor_VertexLayoutBit);
 
-    desc.fColorFilterXfermode = fCurrDrawState.fColorFilterXfermode;
+    desc.fColorFilterXfermode = skipColor ?
+                                SkXfermode::kDst_Mode :
+                                fCurrDrawState.fColorFilterXfermode;
 
-#if GR_AGGRESSIVE_SHADER_OPTS
-    if (!requiresAttributeColors && (0xffffffff == fCurrDrawState.fColor)) {
-        desc.fColorType = ProgramDesc::kNone_ColorType;
-    } else
-#endif
-#if GR_GL_NO_CONSTANT_ATTRIBUTES
-    if (!requiresAttributeColors) {
+    // no reason to do edge aa or look at per-vertex coverage if coverage is
+    // ignored
+    if (skipCoverage) {
+        desc.fVertexLayout &= ~(kEdge_VertexLayoutBit |
+                                kCoverage_VertexLayoutBit);
+    }
+
+    bool colorIsTransBlack = SkToBool(blendOpts & kEmitTransBlack_BlendOptFlag);
+    bool colorIsSolidWhite = (blendOpts & kEmitCoverage_BlendOptFlag) ||
+                             (!requiresAttributeColors &&
+                              0xffffffff == fCurrDrawState.fColor);
+    if (GR_AGGRESSIVE_SHADER_OPTS && colorIsTransBlack) {
+        desc.fColorType = ProgramDesc::kTransBlack_ColorType;
+    } else if (GR_AGGRESSIVE_SHADER_OPTS && colorIsSolidWhite) {
+        desc.fColorType = ProgramDesc::kSolidWhite_ColorType;
+    } else if (GR_GL_NO_CONSTANT_ATTRIBUTES && !requiresAttributeColors) {
         desc.fColorType = ProgramDesc::kUniform_ColorType;
-    } else
-#endif
-    {
-        if (requiresAttributeColors) {} // suppress unused var warning
+    } else {
         desc.fColorType = ProgramDesc::kAttribute_ColorType;
     }
 
-    desc.fEdgeAANumEdges = fCurrDrawState.fEdgeAANumEdges;
-    desc.fEdgeAAConcave = desc.fEdgeAANumEdges > 0 && SkToBool(fCurrDrawState.fFlagBits & kEdgeAAConcave_StateBit);
+    desc.fEdgeAANumEdges = skipCoverage ? 0 : fCurrDrawState.fEdgeAANumEdges;
+    desc.fEdgeAAConcave = desc.fEdgeAANumEdges > 0 &&
+                          SkToBool(fCurrDrawState.fFlagBits &
+                                   kEdgeAAConcave_StateBit);
 
     int lastEnabledStage = -1;
 
-    if (desc.fVertexLayout & GrDrawTarget::kEdge_VertexLayoutBit) {
+    if (!skipCoverage && (desc.fVertexLayout &
+                          GrDrawTarget::kEdge_VertexLayoutBit)) {
         desc.fVertexEdgeType = fCurrDrawState.fVertexEdgeType;
     } else {
         // use canonical value when not set to avoid cache misses
@@ -890,7 +918,10 @@
         stage.fOptFlags = 0;
         stage.setEnabled(this->isStageEnabled(s));
 
-        if (stage.isEnabled()) {
+        bool skip = s < fCurrDrawState.fFirstCoverageStage ? skipColor :
+                                                             skipCoverage;
+
+        if (!skip && stage.isEnabled()) {
             lastEnabledStage = s;
             GrGLTexture* texture = (GrGLTexture*) fCurrDrawState.fTextures[s];
             GrAssert(NULL != texture);
@@ -1005,22 +1036,19 @@
             desc.fFirstCoverageStage = firstCoverageStage;
         }
 
-        // We could consider cases where the final color is solid (0xff alpha)
-        // and the dst coeff can correctly be set to a non-dualsrc gl value.
-        // (e.g. solid draw, and dst coeff is kZero. It's correct to make
-        // the dst coeff be kISA. Or solid draw with kSA can be tweaked to be
-        // kOne).
-        if (this->getCaps().fDualSourceBlendingSupport) {
-            if (kZero_BlendCoeff == fCurrDrawState.fDstBlend) {
+        if (this->getCaps().fDualSourceBlendingSupport &&
+            !(blendOpts & (kEmitCoverage_BlendOptFlag |
+                           kCoverageAsAlpha_BlendOptFlag))) {
+            if (kZero_BlendCoeff == dstCoeff) {
                 // write the coverage value to second color
                 desc.fDualSrcOutput =  ProgramDesc::kCoverage_DualSrcOutput;
                 desc.fFirstCoverageStage = firstCoverageStage;
-            } else if (kSA_BlendCoeff == fCurrDrawState.fDstBlend) {
+            } else if (kSA_BlendCoeff == dstCoeff) {
                 // SA dst coeff becomes 1-(1-SA)*coverage when dst is partially 
                 // cover
                 desc.fDualSrcOutput = ProgramDesc::kCoverageISA_DualSrcOutput;
                 desc.fFirstCoverageStage = firstCoverageStage;
-            } else if (kSC_BlendCoeff == fCurrDrawState.fDstBlend) {
+            } else if (kSC_BlendCoeff == dstCoeff) {
                 // SA dst coeff becomes 1-(1-SA)*coverage when dst is partially
                 // cover
                 desc.fDualSrcOutput = ProgramDesc::kCoverageISC_DualSrcOutput;
diff --git a/gpu/src/GrGpuGLShaders.h b/gpu/src/GrGpuGLShaders.h
index 6d9cccd..0c8322b 100644
--- a/gpu/src/GrGpuGLShaders.h
+++ b/gpu/src/GrGpuGLShaders.h
@@ -57,7 +57,7 @@
     void flushTextureDomain(int stage);
 
     // sets the color specified by GrDrawTarget::setColor()
-    void flushColor();
+    void flushColor(GrColor color);
 
     // sets the MVP matrix uniform for currently bound program
     void flushViewMatrix();
@@ -77,7 +77,9 @@
     static void DeleteProgram(const GrGLInterface* gl,
                               CachedData* programData);
 
-    void buildProgram(GrPrimitiveType type);
+    void buildProgram(GrPrimitiveType typeBlend,
+                      BlendOptFlags blendOpts,
+                      GrBlendCoeff dstCoeff);
 
     ProgramCache*               fProgramCache;
     CachedData*                 fProgramData;
diff --git a/gpu/src/GrInOrderDrawBuffer.cpp b/gpu/src/GrInOrderDrawBuffer.cpp
index 946d31c..5e3c769 100644
--- a/gpu/src/GrInOrderDrawBuffer.cpp
+++ b/gpu/src/GrInOrderDrawBuffer.cpp
@@ -616,9 +616,3 @@
     INHERITED::clipWillBeSet(newClip);
     fClipSet = true;
 }
-
-bool GrInOrderDrawBuffer::willUseHWAALines() const {
-    return this->getCaps().fHWAALineSupport &&
-           CanUseHWAALines(this->getGeomSrc().fVertexLayout, fCurrDrawState);
-}
-
diff --git a/gpu/src/GrInOrderDrawBuffer.h b/gpu/src/GrInOrderDrawBuffer.h
index 3d1ec8c..3273525 100644
--- a/gpu/src/GrInOrderDrawBuffer.h
+++ b/gpu/src/GrInOrderDrawBuffer.h
@@ -91,8 +91,6 @@
 
     virtual void clear(const GrIRect* rect, GrColor color);
 
-    virtual bool willUseHWAALines() const;
-
 private:
 
     struct Draw {