Create an optimized draw state but not hooked in yet to gpu pipeline

BUG=skia:
R=bsalomon@google.com

Author: egdaniel@google.com

Review URL: https://codereview.chromium.org/508663002
diff --git a/src/gpu/GrDrawState.cpp b/src/gpu/GrDrawState.cpp
index ffec662..642ec26 100644
--- a/src/gpu/GrDrawState.cpp
+++ b/src/gpu/GrDrawState.cpp
@@ -6,8 +6,22 @@
  */
 
 #include "GrDrawState.h"
-#include "GrPaint.h"
+
 #include "GrDrawTargetCaps.h"
+#include "GrOptDrawState.h"
+#include "GrPaint.h"
+
+//////////////////////////////////////////////////////////////////////////////s
+
+GrOptDrawState* GrDrawState::createOptState() const {
+    if (NULL == fCachedOptState) {
+        fCachedOptState = SkNEW_ARGS(GrOptDrawState, (*this));
+    } else {
+        SkASSERT(GrOptDrawState(*this) == *fCachedOptState);
+    }
+    fCachedOptState->ref();
+    return fCachedOptState;
+}
 
 //////////////////////////////////////////////////////////////////////////////s
 
@@ -50,7 +64,8 @@
 
 //////////////////////////////////////////////////////////////////////////////s
 
-GrDrawState::GrDrawState(const GrDrawState& state, const SkMatrix& preConcatMatrix) {
+GrDrawState::GrDrawState(const GrDrawState& state, const SkMatrix& preConcatMatrix)
+    : fCachedOptState(NULL) {
     SkDEBUGCODE(fBlockEffectRemovalCnt = 0;)
     *this = state;
     if (!preConcatMatrix.isIdentity()) {
@@ -63,7 +78,7 @@
         for (int i = 0; i < this->numCoverageStages(); ++i) {
             fCoverageStages[i].localCoordChange(preConcatMatrix);
         }
-        this->invalidateBlendOptFlags();
+        this->invalidateOptState();
     }
 }
 
@@ -97,6 +112,8 @@
 
     fHints = that.fHints;
 
+    SkRefCnt_SafeAssign(fCachedOptState, that.fCachedOptState);
+
     memcpy(fFixedFunctionVertexAttribIndices,
             that.fFixedFunctionVertexAttribIndices,
             sizeof(fFixedFunctionVertexAttribIndices));
@@ -131,7 +148,7 @@
 
     fHints = 0;
 
-    this->invalidateBlendOptFlags();
+    this->invalidateOptState();
 }
 
 bool GrDrawState::setIdentityViewMatrix()  {
@@ -151,6 +168,7 @@
             fCoverageStages[s].localCoordChange(invVM);
         }
     }
+    this->invalidateOptState();
     fViewMatrix.reset();
     return true;
 }
@@ -190,7 +208,7 @@
 
     this->setBlendFunc(paint.getSrcBlendCoeff(), paint.getDstBlendCoeff());
     this->setCoverage(paint.getCoverage());
-    this->invalidateBlendOptFlags();
+    this->invalidateOptState();
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -247,7 +265,7 @@
         overlapCheck |= (mask << offsetShift);
 #endif
     }
-    this->invalidateBlendOptFlags();
+    this->invalidateOptState();
     // Positions must be specified.
     SkASSERT(-1 != fFixedFunctionVertexAttribIndices[kPosition_GrVertexAttribBinding]);
 }
@@ -267,7 +285,7 @@
            0xff,
            sizeof(fFixedFunctionVertexAttribIndices));
     fFixedFunctionVertexAttribIndices[kPosition_GrVertexAttribBinding] = 0;
-    this->invalidateBlendOptFlags();
+    this->invalidateOptState();
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -289,8 +307,7 @@
 
 //////////////////////////////////////////////////////////////////////////////
 
-GrDrawState::AutoVertexAttribRestore::AutoVertexAttribRestore(
-    GrDrawState* drawState) {
+GrDrawState::AutoVertexAttribRestore::AutoVertexAttribRestore(GrDrawState* drawState) {
     SkASSERT(drawState);
     fDrawState = drawState;
     fVAPtr = drawState->fVAPtr;
@@ -320,7 +337,7 @@
         SkASSERT(n >= 0);
         fDrawState->fCoverageStages.pop_back_n(n);
         if (m + n > 0) {
-            fDrawState->invalidateBlendOptFlags();
+            fDrawState->invalidateOptState();
         }
         SkDEBUGCODE(--fDrawState->fBlockEffectRemovalCnt;)
     }
@@ -338,118 +355,6 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
-GrRODrawState::BlendOptFlags GrDrawState::getBlendOpts(bool forceCoverage,
-                                                       GrBlendCoeff* srcCoeff,
-                                                       GrBlendCoeff* dstCoeff) const {
-    GrBlendCoeff bogusSrcCoeff, bogusDstCoeff;
-    if (NULL == srcCoeff) {
-        srcCoeff = &bogusSrcCoeff;
-    }
-    if (NULL == dstCoeff) {
-        dstCoeff = &bogusDstCoeff;
-    }
-
-    if (forceCoverage) {
-        return this->calcBlendOpts(true, srcCoeff, dstCoeff);
-    }
-
-    if (0 == (fBlendOptFlags & kInvalid_BlendOptFlag)) {
-        *srcCoeff = fOptSrcBlend;
-        *dstCoeff = fOptDstBlend;
-        return fBlendOptFlags;
-    }
-
-    fBlendOptFlags = this->calcBlendOpts(forceCoverage, srcCoeff, dstCoeff);
-    fOptSrcBlend = *srcCoeff;
-    fOptDstBlend = *dstCoeff;
-
-    return fBlendOptFlags;
-}
-
-GrRODrawState::BlendOptFlags GrDrawState::calcBlendOpts(bool forceCoverage,
-                                                          GrBlendCoeff* srcCoeff,
-                                                          GrBlendCoeff* dstCoeff) const {
-    *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_BlendOptFlag;
-        } else {
-            return kSkipDraw_BlendOptFlag;
-        }
-    }
-
-    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_BlendOptFlag;
-            }
-        }
-    } else if (this->isCoverageDrawing()) {
-        // we have coverage but we aren't distinguishing it from alpha by request.
-        return kCoverageAsAlpha_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;
-        }
-        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_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_GrBlendCoeff;
-                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_GrBlendCoeff;
-            return  kCoverageAsAlpha_BlendOptFlag;
-        }
-    }
-
-    return kNone_BlendOpt;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-
 void GrDrawState::AutoViewMatrixRestore::restore() {
     if (fDrawState) {
         SkDEBUGCODE(--fDrawState->fBlockEffectRemovalCnt;)
@@ -469,6 +374,7 @@
         for (int s = 0; s < numCoverageStages; ++s, ++i) {
             fDrawState->fCoverageStages[s].restoreCoordChange(fSavedCoordChanges[i]);
         }
+        fDrawState->invalidateOptState();
         fDrawState = NULL;
     }
 }
@@ -488,6 +394,7 @@
 
     this->doEffectCoordChanges(preconcatMatrix);
     SkDEBUGCODE(++fDrawState->fBlockEffectRemovalCnt;)
+    drawState->invalidateOptState();
 }
 
 bool GrDrawState::AutoViewMatrixRestore::setIdentity(GrDrawState* drawState) {
@@ -501,6 +408,7 @@
         return true;
     }
 
+    drawState->invalidateOptState();
     fViewMatrix = drawState->getViewMatrix();
     if (0 == drawState->numTotalStages()) {
         drawState->fViewMatrix.reset();
@@ -547,69 +455,3 @@
     }
 }
 
-bool GrDrawState::srcAlphaWillBeOne() const {
-    uint32_t validComponentFlags;
-    GrColor color;
-    // Check if per-vertex or constant color may have partial alpha
-    if (this->hasColorVertexAttribute()) {
-        if (fHints & kVertexColorsAreOpaque_Hint) {
-            validComponentFlags = kA_GrColorComponentFlag;
-            color = 0xFF << GrColor_SHIFT_A;
-        } else {
-            validComponentFlags = 0;
-            color = 0; // not strictly necessary but we get false alarms from tools about uninit.
-        }
-    } else {
-        validComponentFlags = kRGBA_GrColorComponentFlags;
-        color = this->getColor();
-    }
-
-    // Run through the color stages
-    for (int s = 0; s < this->numColorStages(); ++s) {
-        const GrEffect* effect = this->getColorStage(s).getEffect();
-        effect->getConstantColorComponents(&color, &validComponentFlags);
-    }
-
-    // Check whether coverage is treated as color. If so we run through the coverage computation.
-    if (this->isCoverageDrawing()) {
-        // The shader generated for coverage drawing runs the full coverage computation and then
-        // makes the shader output be the multiplication of color and coverage. We mirror that here.
-        GrColor coverage;
-        uint32_t coverageComponentFlags;
-        if (this->hasCoverageVertexAttribute()) {
-            coverageComponentFlags = 0;
-            coverage = 0; // suppresses any warnings.
-        } else {
-            coverageComponentFlags = kRGBA_GrColorComponentFlags;
-            coverage = this->getCoverageColor();
-        }
-
-        // Run through the coverage stages
-        if (this->hasGeometryProcessor()) {
-            const GrEffect* effect = fGeometryProcessor->getEffect();
-            effect->getConstantColorComponents(&coverage, &coverageComponentFlags);
-        }
-        for (int s = 0; s < this->numCoverageStages(); ++s) {
-            const GrEffect* effect = this->getCoverageStage(s).getEffect();
-            effect->getConstantColorComponents(&coverage, &coverageComponentFlags);
-        }
-
-        // Since the shader will multiply coverage and color, the only way the final A==1 is if
-        // coverage and color both have A==1.
-        return (kA_GrColorComponentFlag & validComponentFlags & coverageComponentFlags) &&
-                0xFF == GrColorUnpackA(color) && 0xFF == GrColorUnpackA(coverage);
-
-    }
-
-    return (kA_GrColorComponentFlag & validComponentFlags) && 0xFF == GrColorUnpackA(color);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-
-bool GrDrawState::canIgnoreColorAttribute() const {
-    if (fBlendOptFlags & kInvalid_BlendOptFlag) {
-        this->getBlendOpts();
-    }
-    return SkToBool(fBlendOptFlags & (GrRODrawState::kEmitTransBlack_BlendOptFlag |
-                                      GrRODrawState::kEmitCoverage_BlendOptFlag));
-}
diff --git a/src/gpu/GrDrawState.h b/src/gpu/GrDrawState.h
index 2fe56a3..4850fc9 100644
--- a/src/gpu/GrDrawState.h
+++ b/src/gpu/GrDrawState.h
@@ -9,6 +9,7 @@
 #define GrDrawState_DEFINED
 
 #include "GrBlend.h"
+#include "GrOptDrawState.h"
 #include "GrProgramResource.h"
 #include "GrRODrawState.h"
 #include "effects/GrSimpleTextureEffect.h"
@@ -23,12 +24,12 @@
 public:
     SK_DECLARE_INST_COUNT(GrDrawState)
 
-    GrDrawState() {
+    GrDrawState() : fCachedOptState(NULL) {
         SkDEBUGCODE(fBlockEffectRemovalCnt = 0;)
         this->reset();
     }
 
-    GrDrawState(const SkMatrix& initialViewMatrix) {
+    GrDrawState(const SkMatrix& initialViewMatrix) : fCachedOptState(NULL) {
         SkDEBUGCODE(fBlockEffectRemovalCnt = 0;)
         this->reset(initialViewMatrix);
     }
@@ -36,7 +37,7 @@
     /**
      * Copies another draw state.
      **/
-    GrDrawState(const GrDrawState& state) : INHERITED() {
+    GrDrawState(const GrDrawState& state) : INHERITED(), fCachedOptState(NULL) {
         SkDEBUGCODE(fBlockEffectRemovalCnt = 0;)
         *this = state;
     }
@@ -46,7 +47,10 @@
      **/
     GrDrawState(const GrDrawState& state, const SkMatrix& preConcatMatrix);
 
-    virtual ~GrDrawState() { SkASSERT(0 == fBlockEffectRemovalCnt); }
+    virtual ~GrDrawState() {
+        SkSafeUnref(fCachedOptState);
+        SkASSERT(0 == fBlockEffectRemovalCnt);
+    }
 
     /**
      * Resets to the default state. GrEffects will be removed from all stages.
@@ -100,7 +104,8 @@
      public:
          AutoVertexAttribRestore(GrDrawState* drawState);
 
-         ~AutoVertexAttribRestore() { fDrawState->internalSetVertexAttribs(fVAPtr, fVACount, fVAStride); }
+         ~AutoVertexAttribRestore() { fDrawState->internalSetVertexAttribs(fVAPtr, fVACount,
+                                                                           fVAStride); }
 
      private:
          GrDrawState*          fDrawState;
@@ -135,8 +140,10 @@
      *  @param color    the color to set.
      */
     void setColor(GrColor color) {
-        fColor = color;
-        this->invalidateBlendOptFlags();
+        if (color != fColor) {
+            fColor = color;
+            this->invalidateOptState();
+        }
     }
 
     /**
@@ -159,8 +166,10 @@
      * coverage is ignored when per-vertex coverage is provided.
      */
     void setCoverage(uint8_t coverage) {
-        fCoverage = coverage;
-        this->invalidateBlendOptFlags();
+        if (coverage != fCoverage) {
+            fCoverage = coverage;
+            this->invalidateOptState();
+        }
     }
 
     /// @}
@@ -175,7 +184,7 @@
         SkASSERT(effect);
         SkASSERT(!this->hasGeometryProcessor());
         fGeometryProcessor.reset(new GrEffectStage(effect, attr0, attr1));
-        this->invalidateBlendOptFlags();
+        this->invalidateOptState();
         return effect;
     }
 
@@ -202,14 +211,14 @@
     const GrEffect* addColorEffect(const GrEffect* effect, int attr0 = -1, int attr1 = -1) {
         SkASSERT(effect);
         SkNEW_APPEND_TO_TARRAY(&fColorStages, GrEffectStage, (effect, attr0, attr1));
-        this->invalidateBlendOptFlags();
+        this->invalidateOptState();
         return effect;
     }
 
     const GrEffect* addCoverageEffect(const GrEffect* effect, int attr0 = -1, int attr1 = -1) {
         SkASSERT(effect);
         SkNEW_APPEND_TO_TARRAY(&fCoverageStages, GrEffectStage, (effect, attr0, attr1));
-        this->invalidateBlendOptFlags();
+        this->invalidateOptState();
         return effect;
     }
 
@@ -301,9 +310,11 @@
      * @param dstCoef coefficient applied to the dst color.
      */
     void setBlendFunc(GrBlendCoeff srcCoeff, GrBlendCoeff dstCoeff) {
-        fSrcBlend = srcCoeff;
-        fDstBlend = dstCoeff;
-        this->invalidateBlendOptFlags();
+        if (srcCoeff != fSrcBlend || dstCoeff != fDstBlend) {
+            fSrcBlend = srcCoeff;
+            fDstBlend = dstCoeff;
+            this->invalidateOptState();
+        }
     #ifdef SK_DEBUG
         if (GrBlendCoeffRefsDst(dstCoeff)) {
             GrPrintf("Unexpected dst blend coeff. Won't work correctly with coverage stages.\n");
@@ -325,34 +336,12 @@
      * @param constant the constant to set
      */
     void setBlendConstant(GrColor constant) {
-        fBlendConstant = constant;
-        this->invalidateBlendOptFlags();
+        if (constant != fBlendConstant) {
+            fBlendConstant = constant;
+            this->invalidateOptState();
+        }
     }
 
-    /**
-     * 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.
-     *
-     * Subclasses of GrDrawTarget that actually draw (as opposed to those that just buffer for
-     * playback) must call this function and respect the flags that replace the output color.
-     *
-     * If the cached BlendOptFlags does not have the invalidate bit set, then getBlendOpts will
-     * simply returned the cached flags and coefficients. Otherwise it will calculate the values. 
-     */
-    BlendOptFlags getBlendOpts(bool forceCoverage = false,
-                               GrBlendCoeff* srcCoeff = NULL,
-                               GrBlendCoeff* dstCoeff = NULL) const;
-
-    /**
-     * We don't use suplied vertex color attributes if our blend mode is EmitCoverage or
-     * EmitTransBlack
-     */
-    bool canIgnoreColorAttribute() const;
-
-
     /// @}
 
     ///////////////////////////////////////////////////////////////////////////
@@ -416,6 +405,7 @@
      */
     void setRenderTarget(GrRenderTarget* target) {
         fRenderTarget.setResource(SkSafeRef(target), GrProgramResource::kWrite_IOType);
+        this->invalidateOptState();
     }
 
     /// @}
@@ -432,16 +422,20 @@
      * @param settings  the stencil settings to use.
      */
     void setStencil(const GrStencilSettings& settings) {
-        fStencilSettings = settings;
-        this->invalidateBlendOptFlags();
+        if (settings != fStencilSettings) {
+            fStencilSettings = settings;
+            this->invalidateOptState();
+        }
     }
 
     /**
      * Shortcut to disable stencil testing and ops.
      */
     void disableStencil() {
-        fStencilSettings.setDisabled();
-        this->invalidateBlendOptFlags();
+        if (!fStencilSettings.isDisabled()) {
+            fStencilSettings.setDisabled();
+            this->invalidateOptState();
+        }
     }
 
     GrStencilSettings* stencil() { return &fStencilSettings; }
@@ -453,8 +447,10 @@
     ////
 
     void resetStateFlags() {
-        fFlagBits = 0;
-        this->invalidateBlendOptFlags();
+        if (0 != fFlagBits) {
+            fFlagBits = 0;
+            this->invalidateOptState();
+        }
     }
 
     /**
@@ -463,8 +459,10 @@
      * @param stateBits bitfield of StateBits specifying the states to enable
      */
     void enableState(uint32_t stateBits) {
-        fFlagBits |= stateBits;
-        this->invalidateBlendOptFlags();
+        if (stateBits & ~fFlagBits) {
+            fFlagBits |= stateBits;
+            this->invalidateOptState();
+        }
     }
 
     /**
@@ -473,8 +471,10 @@
      * @param stateBits bitfield of StateBits specifying the states to disable
      */
     void disableState(uint32_t stateBits) {
-        fFlagBits &= ~(stateBits);
-        this->invalidateBlendOptFlags();
+        if (stateBits & fFlagBits) {
+            fFlagBits &= ~(stateBits);
+            this->invalidateOptState();
+        }
     }
 
     /**
@@ -513,8 +513,6 @@
     /// Hints that when provided can enable optimizations.
     ////
 
-    enum Hints { kVertexColorsAreOpaque_Hint = 0x1, };
-
     void setHint(Hints hint, bool value) { fHints = value ? (fHints | hint) : (fHints & ~hint); }
 
     /// @}
@@ -542,33 +540,34 @@
 
     GrDrawState& operator= (const GrDrawState& that);
 
+    /**
+     * Returns a snapshot of the current optimized state. If the current drawState has a valid
+     * cached optimiezed state it will simply return a pointer to it otherwise it will create a new
+     * GrOptDrawState. In all cases the GrOptDrawState is reffed and ownership is given to the
+     * caller.
+     */
+    GrOptDrawState* createOptState() const;
+
 private:
+    void invalidateOptState() const {
+        SkSafeSetNull(fCachedOptState);
+        fBlendOptFlags = kInvalid_BlendOptFlag;
+    }
+
     void onReset(const SkMatrix* initialViewMatrix);
 
-    /**
-     * Determines whether src alpha is guaranteed to be one for all src pixels
-     */
-    bool srcAlphaWillBeOne() const;
-
-    /**
-     * Helper function for getBlendOpts.
-     */
-    BlendOptFlags calcBlendOpts(bool forceCoverage = false,
-                                GrBlendCoeff* srcCoeff = NULL,
-                                GrBlendCoeff* dstCoeff = NULL) const;
-
     void invalidateBlendOptFlags() {
         fBlendOptFlags = kInvalid_BlendOptFlag;
     }
 
-    uint32_t                            fHints;
-
     // Some of the auto restore objects assume that no effects are removed during their lifetime.
     // This is used to assert that this condition holds.
     SkDEBUGCODE(int fBlockEffectRemovalCnt;)
 
     void internalSetVertexAttribs(const GrVertexAttrib attribs[], int count, size_t stride);
 
+    mutable GrOptDrawState* fCachedOptState;
+
     typedef GrRODrawState INHERITED;
 };
 
diff --git a/src/gpu/GrOptDrawState.cpp b/src/gpu/GrOptDrawState.cpp
new file mode 100644
index 0000000..83546ba
--- /dev/null
+++ b/src/gpu/GrOptDrawState.cpp
@@ -0,0 +1,147 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrOptDrawState.h"
+
+#include "GrDrawState.h"
+
+GrOptDrawState::GrOptDrawState(const GrDrawState& drawState) : INHERITED(drawState) {
+    fColor = drawState.getColor();
+    fCoverage = drawState.getCoverage();
+    fViewMatrix = drawState.getViewMatrix();
+    fBlendConstant = drawState.getBlendConstant();
+    fFlagBits = drawState.getFlagBits();
+    fVAPtr = drawState.getVertexAttribs();
+    fVACount = drawState.getVertexAttribCount();
+    fVAStride = drawState.getVertexStride();
+    fStencilSettings = drawState.getStencil();
+    fDrawFace = drawState.getDrawFace();
+
+    fBlendOptFlags = drawState.getBlendOpts(false, &fSrcBlend, &fDstBlend);
+
+    memcpy(fFixedFunctionVertexAttribIndices,
+            drawState.getFixedFunctionVertexAttribIndices(),
+            sizeof(fFixedFunctionVertexAttribIndices));
+
+    fInputColorIsUsed = true;
+    fInputCoverageIsUsed = true;
+
+    if (drawState.hasGeometryProcessor()) {
+        fGeometryProcessor.reset(SkNEW_ARGS(GrEffectStage, (*drawState.getGeometryProcessor())));
+    } else {
+        fGeometryProcessor.reset(NULL);
+    }
+
+    this->copyEffectiveColorStages(drawState);
+    this->copyEffectiveCoverageStages(drawState);
+};
+
+void GrOptDrawState::removeFixedFunctionVertexAttribs(uint8_t removeVAFlag) {
+    int numToRemove = 0;
+    uint8_t maskCheck = 0x1;
+    // Count the number of vertex attributes that we will actually remove
+    for (int i = 0; i < kGrFixedFunctionVertexAttribBindingCnt; ++i) {
+        if ((maskCheck & removeVAFlag) && -1 != fFixedFunctionVertexAttribIndices[i]) {
+            ++numToRemove;
+        }
+        maskCheck <<= 1;
+    }
+    fOptVA.reset(fVACount - numToRemove);
+
+    GrVertexAttrib* dst = fOptVA.get();
+    const GrVertexAttrib* src = fVAPtr;
+
+    for (int i = 0, newIdx = 0; i < fVACount; ++i, ++src) {
+        const GrVertexAttrib& currAttrib = *src;
+        if (currAttrib.fBinding < kGrFixedFunctionVertexAttribBindingCnt) {
+            uint8_t maskCheck = 0x1 << currAttrib.fBinding;
+            if (maskCheck & removeVAFlag) {
+                SkASSERT(-1 != fFixedFunctionVertexAttribIndices[currAttrib.fBinding]);
+                fFixedFunctionVertexAttribIndices[currAttrib.fBinding] = -1;
+                continue;
+            }
+        }
+        memcpy(dst, src, sizeof(GrVertexAttrib));
+        fFixedFunctionVertexAttribIndices[currAttrib.fBinding] = newIdx;
+        ++newIdx;
+        ++dst;
+    }
+    fVACount -= numToRemove;
+    fVAPtr = fOptVA.get();
+}
+
+void GrOptDrawState::copyEffectiveColorStages(const GrDrawState& ds) {
+    int firstColorStage = 0;
+
+    // Set up color and flags for ConstantColorComponent checks
+    GrColor color;
+    uint32_t validComponentFlags;
+    if (!this->hasColorVertexAttribute()) {
+        color = ds.getColor();
+        validComponentFlags = kRGBA_GrColorComponentFlags;
+    } else {
+        if (ds.vertexColorsAreOpaque()) {
+            color = 0xFF << GrColor_SHIFT_A;
+            validComponentFlags = kA_GrColorComponentFlag;
+        } else {
+            validComponentFlags = 0;
+            color = 0; // not strictly necessary but we get false alarms from tools about uninit.
+        }
+    }
+
+    for (int i = 0; i < ds.numColorStages(); ++i) {
+        const GrEffect* effect = ds.getColorStage(i).getEffect();
+        if (!effect->willUseInputColor()) {
+            firstColorStage = i;
+            fInputColorIsUsed = false;
+        }
+        effect->getConstantColorComponents(&color, &validComponentFlags);
+        if (kRGBA_GrColorComponentFlags == validComponentFlags) {
+            firstColorStage = i + 1;
+            fColor = color;
+            fInputColorIsUsed = true;
+            this->removeFixedFunctionVertexAttribs(0x1 << kColor_GrVertexAttribBinding);
+        }
+    }
+    if (firstColorStage < ds.numColorStages()) {
+        fColorStages.reset(&ds.getColorStage(firstColorStage),
+                           ds.numColorStages() - firstColorStage);
+    } else {
+        fColorStages.reset();
+    }
+}
+
+void GrOptDrawState::copyEffectiveCoverageStages(const GrDrawState& ds) {
+    int firstCoverageStage = 0;
+
+    // We do not try to optimize out constantColor coverage effects here. It is extremely rare
+    // to have a coverage effect that returns a constant value for all four channels. Thus we
+    // save having to make extra virtual calls by not checking for it.
+
+    // Don't do any optimizations on coverage stages. It should not be the case where we do not use
+    // input coverage in an effect
+#ifdef OptCoverageStages
+    for (int i = 0; i < ds.numCoverageStages(); ++i) {
+        const GrEffect* effect = ds.getCoverageStage(i).getEffect();
+        if (!effect->willUseInputColor()) {
+            firstCoverageStage = i;
+            fInputCoverageIsUsed = false;
+        }
+    }
+#endif
+    if (ds.numCoverageStages() > 0) {
+        fCoverageStages.reset(&ds.getCoverageStage(firstCoverageStage),
+                              ds.numCoverageStages() - firstCoverageStage);
+    } else {
+        fCoverageStages.reset();
+    }
+}
+
+bool GrOptDrawState::operator== (const GrOptDrawState& that) const {
+    return this->isEqual(that);
+}
+
diff --git a/src/gpu/GrOptDrawState.h b/src/gpu/GrOptDrawState.h
new file mode 100644
index 0000000..a4edc01
--- /dev/null
+++ b/src/gpu/GrOptDrawState.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrOptDrawState_DEFINED
+#define GrOptDrawState_DEFINED
+
+#include "GrRODrawState.h"
+
+class GrDrawState;
+
+/**
+ * Subclass of GrRODrawState that holds an optimized version of a GrDrawState. Like it's parent
+ * it is meant to be an immutable class, and simply adds a few helpful data members not in the
+ * base class.
+ */
+class GrOptDrawState : public GrRODrawState {
+public:
+    /**
+     * Constructs and optimized drawState out of a GrRODrawState.
+     */
+    explicit GrOptDrawState(const GrDrawState& drawState);
+
+    bool operator== (const GrOptDrawState& that) const;
+
+private:
+    /*
+     * Loops through all the color stage effects to check if the stage will ignore color input or
+     * always output a constant color. In the ignore color input case we can ignore all previous
+     * stages. In the constant color case, we can ignore all previous stages and
+     * the current one and set the state color to the constant color. Once we determine the so
+     * called first effective stage, we copy all the effective stages into our optimized
+     * state.
+     */
+    void copyEffectiveColorStages(const GrDrawState& ds);
+
+    /*
+     * Loops through all the coverage stage effects to check if the stage will ignore color input.
+     * If a coverage stage will ignore input, then we can ignore all coverage stages before it. We
+     * loop to determine the first effective coverage stage, and then copy all of our effective
+     * coverage stages into our optimized state.
+     */
+    void copyEffectiveCoverageStages(const GrDrawState& ds);
+
+    /*
+     * This function takes in a flag and removes the corresponding fixed function vertex attributes.
+     * The flags are in the same order as GrVertexAttribBinding array. If bit i of removeVAFlags is
+     * set, then vertex attributes with binding (GrVertexAttribute)i will be removed.
+     */
+    void removeFixedFunctionVertexAttribs(uint8_t removeVAFlags);
+
+    void removeColorVertexAttrib();
+
+    // These flags are needed to protect the code from creating an unused uniform color/coverage
+    // which will cause shader compiler errors.
+    bool            fInputColorIsUsed;
+    bool            fInputCoverageIsUsed;
+
+    SkAutoSTArray<4, GrVertexAttrib> fOptVA;
+
+    typedef GrRODrawState INHERITED;
+};
+
+#endif
diff --git a/src/gpu/GrRODrawState.cpp b/src/gpu/GrRODrawState.cpp
index 8d6c283..b79e8fc 100644
--- a/src/gpu/GrRODrawState.cpp
+++ b/src/gpu/GrRODrawState.cpp
@@ -6,10 +6,17 @@
  */
 
 #include "GrRODrawState.h"
+
 #include "GrDrawTargetCaps.h"
+#include "GrRenderTarget.h"
 
 ////////////////////////////////////////////////////////////////////////////////
 
+GrRODrawState::GrRODrawState(const GrRODrawState& drawState) : INHERITED() {
+    fRenderTarget.setResource(SkSafeRef(drawState.fRenderTarget.getResource()),
+                              GrProgramResource::kWrite_IOType);
+}
+
 bool GrRODrawState::isEqual(const GrRODrawState& that) const {
     bool usingVertexColors = this->hasColorVertexAttribute();
     if (!usingVertexColors && this->fColor != that.fColor) {
@@ -164,6 +171,118 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
+GrRODrawState::BlendOptFlags GrRODrawState::getBlendOpts(bool forceCoverage,
+                                                         GrBlendCoeff* srcCoeff,
+                                                         GrBlendCoeff* dstCoeff) const {
+    GrBlendCoeff bogusSrcCoeff, bogusDstCoeff;
+    if (NULL == srcCoeff) {
+        srcCoeff = &bogusSrcCoeff;
+    }
+    if (NULL == dstCoeff) {
+        dstCoeff = &bogusDstCoeff;
+    }
+
+    if (forceCoverage) {
+        return this->calcBlendOpts(true, srcCoeff, dstCoeff);
+    }
+
+    if (0 == (fBlendOptFlags & kInvalid_BlendOptFlag)) {
+        *srcCoeff = fOptSrcBlend;
+        *dstCoeff = fOptDstBlend;
+        return fBlendOptFlags;
+    }
+
+    fBlendOptFlags = this->calcBlendOpts(forceCoverage, srcCoeff, dstCoeff);
+    fOptSrcBlend = *srcCoeff;
+    fOptDstBlend = *dstCoeff;
+
+    return fBlendOptFlags;
+}
+
+GrRODrawState::BlendOptFlags GrRODrawState::calcBlendOpts(bool forceCoverage,
+                                                          GrBlendCoeff* srcCoeff,
+                                                          GrBlendCoeff* dstCoeff) const {
+    *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_BlendOptFlag;
+        } else {
+            return kSkipDraw_BlendOptFlag;
+        }
+    }
+
+    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_BlendOptFlag;
+            }
+        }
+    } else if (this->isCoverageDrawing()) {
+        // we have coverage but we aren't distinguishing it from alpha by request.
+        return kCoverageAsAlpha_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;
+        }
+        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_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_GrBlendCoeff;
+                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_GrBlendCoeff;
+            return  kCoverageAsAlpha_BlendOptFlag;
+        }
+    }
+
+    return kNone_BlendOpt;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
 // Some blend modes allow folding a fractional coverage value into the color's alpha channel, while
 // others will blend incorrectly.
 bool GrRODrawState::canTweakAlphaForCoverage() const {
@@ -197,3 +316,67 @@
         fCoverageStages[i].convertToPendingExec();
     }
 }
+
+bool GrRODrawState::srcAlphaWillBeOne() const {
+    uint32_t validComponentFlags;
+    GrColor color;
+    // Check if per-vertex or constant color may have partial alpha
+    if (this->hasColorVertexAttribute()) {
+        if (fHints & kVertexColorsAreOpaque_Hint) {
+            validComponentFlags = kA_GrColorComponentFlag;
+            color = 0xFF << GrColor_SHIFT_A;
+        } else {
+            validComponentFlags = 0;
+            color = 0; // not strictly necessary but we get false alarms from tools about uninit.
+        }
+    } else {
+        validComponentFlags = kRGBA_GrColorComponentFlags;
+        color = this->getColor();
+    }
+
+    // Run through the color stages
+    for (int s = 0; s < this->numColorStages(); ++s) {
+        const GrEffect* effect = this->getColorStage(s).getEffect();
+        effect->getConstantColorComponents(&color, &validComponentFlags);
+    }
+
+    // Check whether coverage is treated as color. If so we run through the coverage computation.
+    if (this->isCoverageDrawing()) {
+        // The shader generated for coverage drawing runs the full coverage computation and then
+        // makes the shader output be the multiplication of color and coverage. We mirror that here.
+        GrColor coverage;
+        uint32_t coverageComponentFlags;
+        if (this->hasCoverageVertexAttribute()) {
+            coverageComponentFlags = 0;
+            coverage = 0; // suppresses any warnings.
+        } else {
+            coverageComponentFlags = kRGBA_GrColorComponentFlags;
+            coverage = this->getCoverageColor();
+        }
+
+        // Run through the coverage stages
+        for (int s = 0; s < this->numCoverageStages(); ++s) {
+            const GrEffect* effect = this->getCoverageStage(s).getEffect();
+            effect->getConstantColorComponents(&coverage, &coverageComponentFlags);
+        }
+
+        // Since the shader will multiply coverage and color, the only way the final A==1 is if
+        // coverage and color both have A==1.
+        return (kA_GrColorComponentFlag & validComponentFlags & coverageComponentFlags) &&
+                0xFF == GrColorUnpackA(color) && 0xFF == GrColorUnpackA(coverage);
+
+    }
+
+    return (kA_GrColorComponentFlag & validComponentFlags) && 0xFF == GrColorUnpackA(color);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+bool GrRODrawState::canIgnoreColorAttribute() const {
+    if (fBlendOptFlags & kInvalid_BlendOptFlag) {
+        this->getBlendOpts();
+    }
+    return SkToBool(fBlendOptFlags & (GrRODrawState::kEmitTransBlack_BlendOptFlag |
+                                      GrRODrawState::kEmitCoverage_BlendOptFlag));
+}
+
diff --git a/src/gpu/GrRODrawState.h b/src/gpu/GrRODrawState.h
index 718f017..d748e7d 100644
--- a/src/gpu/GrRODrawState.h
+++ b/src/gpu/GrRODrawState.h
@@ -13,6 +13,7 @@
 #include "GrStencil.h"
 #include "SkMatrix.h"
 
+class GrDrawState;
 class GrDrawTargetCaps;
 class GrPaint;
 class GrTexture;
@@ -26,6 +27,10 @@
 public:
     SK_DECLARE_INST_COUNT(GrRODrawState)
 
+    GrRODrawState() {}
+
+    GrRODrawState& operator= (const GrRODrawState& that);
+
     ///////////////////////////////////////////////////////////////////////////
     /// @name Vertex Attributes
     ////
@@ -68,6 +73,10 @@
         return -1 != fFixedFunctionVertexAttribIndices[kCoverage_GrVertexAttribBinding];
     }
 
+    const int* getFixedFunctionVertexAttribIndices() const {
+        return fFixedFunctionVertexAttribIndices;
+    }
+
     bool validateVertexAttribs() const;
 
     /// @}
@@ -158,6 +167,12 @@
     GrColor getBlendConstant() const { return fBlendConstant; }
 
     /**
+     * We don't use supplied vertex color attributes if our blend mode is EmitCoverage or
+     * EmitTransBlack
+     */
+    bool canIgnoreColorAttribute() const;
+
+    /**
      * 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.
@@ -198,6 +213,22 @@
     };
     GR_DECL_BITFIELD_OPS_FRIENDS(BlendOptFlags);
 
+    /**
+     * 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.
+     *
+     * Subclasses of GrDrawTarget that actually draw (as opposed to those that just buffer for
+     * playback) must call this function and respect the flags that replace the output color.
+     *
+     * If the cached BlendOptFlags does not have the invalidate bit set, then getBlendOpts will
+     * simply returned the cached flags and coefficients. Otherwise it will calculate the values. 
+     */
+    BlendOptFlags getBlendOpts(bool forceCoverage = false,
+                               GrBlendCoeff* srcCoeff = NULL,
+                               GrBlendCoeff* dstCoeff = NULL) const;
     /// @}
 
     ///////////////////////////////////////////////////////////////////////////
@@ -301,6 +332,8 @@
         kLastPublicStateBit = kDummyStateBit-1,
     };
 
+    uint32_t getFlagBits() const { return fFlagBits; }
+
     bool isStateFlagEnabled(uint32_t stateBit) const { return 0 != (stateBit & fFlagBits); }
 
     bool isDitherState() const { return 0 != (fFlagBits & kDither_StateBit); }
@@ -333,6 +366,17 @@
     /// @}
 
     ///////////////////////////////////////////////////////////////////////////
+    /// @name Hints
+    /// Hints that when provided can enable optimizations.
+    ////
+
+    enum Hints { kVertexColorsAreOpaque_Hint = 0x1, };
+
+    bool vertexColorsAreOpaque() const { return kVertexColorsAreOpaque_Hint & fHints; }
+
+    /// @}
+
+    ///////////////////////////////////////////////////////////////////////////
 
     /** Return type for CombineIfPossible. */
     enum CombinedState {
@@ -359,6 +403,8 @@
 
     friend class GrDrawTarget;
 
+    explicit GrRODrawState(const GrRODrawState& drawState);
+
     bool isEqual(const GrRODrawState& that) const;
 
     // These fields are roughly sorted by decreasing likelihood of being different in op==
@@ -381,6 +427,8 @@
     EffectStageArray                    fColorStages;
     EffectStageArray                    fCoverageStages;
 
+    uint32_t                            fHints;
+
     mutable GrBlendCoeff                fOptSrcBlend;
     mutable GrBlendCoeff                fOptDstBlend;
     mutable BlendOptFlags               fBlendOptFlags;
@@ -390,6 +438,18 @@
     int fFixedFunctionVertexAttribIndices[kGrFixedFunctionVertexAttribBindingCnt];
 
 private:
+    /**
+     * Determines whether src alpha is guaranteed to be one for all src pixels
+     */
+    bool srcAlphaWillBeOne() const;
+
+    /**
+     * Helper function for getBlendOpts.
+     */
+    BlendOptFlags calcBlendOpts(bool forceCoverage = false,
+                                GrBlendCoeff* srcCoeff = NULL,
+                                GrBlendCoeff* dstCoeff = NULL) const;
+
     typedef SkRefCnt INHERITED;
 };