Replace fixed-size array of effect stages in GrDrawState with two appendable arrays, one for color, one for coverage.

R=robertphillips@google.com

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

git-svn-id: http://skia.googlecode.com/svn/trunk@9592 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/gpu/GrAAConvexPathRenderer.cpp b/src/gpu/GrAAConvexPathRenderer.cpp
index 77980e4..5d461ab 100644
--- a/src/gpu/GrAAConvexPathRenderer.cpp
+++ b/src/gpu/GrAAConvexPathRenderer.cpp
@@ -645,7 +645,7 @@
     };
     static const int kEdgeAttrIndex = 1;
     GrEffectRef* quadEffect = QuadEdgeEffect::Create();
-    drawState->setEffect(kEdgeEffectStage, quadEffect, kEdgeAttrIndex)->unref();
+    drawState->addCoverageEffect(quadEffect, kEdgeAttrIndex)->unref();
 
     GrDrawTarget::AutoReleaseGeometry arg(target, vCount, iCount);
     if (!arg.succeeded()) {
diff --git a/src/gpu/GrAAHairLinePathRenderer.cpp b/src/gpu/GrAAHairLinePathRenderer.cpp
index bed931c..51bff1e 100644
--- a/src/gpu/GrAAHairLinePathRenderer.cpp
+++ b/src/gpu/GrAAHairLinePathRenderer.cpp
@@ -871,24 +871,27 @@
     }
 #endif
 
-    target->setIndexSourceToBuffer(fLinesIndexBuffer);
-    int lines = 0;
-    int nBufLines = fLinesIndexBuffer->maxQuads();
-    drawState->setEffect(kEdgeEffectStage, hairLineEffect, kEdgeAttrIndex)->unref();
-    while (lines < lineCnt) {
-        int n = GrMin(lineCnt - lines, nBufLines);
-        target->drawIndexed(kTriangles_GrPrimitiveType,
-                            kVertsPerLineSeg*lines,    // startV
-                            0,                         // startI
-                            kVertsPerLineSeg*n,        // vCount
-                            kIdxsPerLineSeg*n,
-                            &devBounds);        // iCount
-        lines += n;
+    {
+        GrDrawState::AutoRestoreEffects are(drawState);
+        target->setIndexSourceToBuffer(fLinesIndexBuffer);
+        int lines = 0;
+        int nBufLines = fLinesIndexBuffer->maxQuads();
+        drawState->addCoverageEffect(hairLineEffect, kEdgeAttrIndex)->unref();
+        while (lines < lineCnt) {
+            int n = GrMin(lineCnt - lines, nBufLines);
+            target->drawIndexed(kTriangles_GrPrimitiveType,
+                                kVertsPerLineSeg*lines,     // startV
+                                0,                          // startI
+                                kVertsPerLineSeg*n,         // vCount
+                                kIdxsPerLineSeg*n,
+                                &devBounds);                // iCount
+            lines += n;
+        }
     }
 
     target->setIndexSourceToBuffer(fQuadsIndexBuffer);
     int quads = 0;
-    drawState->setEffect(kEdgeEffectStage, hairQuadEffect, kEdgeAttrIndex)->unref();
+    drawState->addCoverageEffect(hairQuadEffect, kEdgeAttrIndex)->unref();
     while (quads < quadCnt) {
         int n = GrMin(quadCnt - quads, kNumQuadsInIdxBuffer);
         target->drawIndexed(kTriangles_GrPrimitiveType,
diff --git a/src/gpu/GrAARectRenderer.cpp b/src/gpu/GrAARectRenderer.cpp
index f28d914..7fe0517 100644
--- a/src/gpu/GrAARectRenderer.cpp
+++ b/src/gpu/GrAARectRenderer.cpp
@@ -542,17 +542,10 @@
 
     RectVertex* verts = reinterpret_cast<RectVertex*>(geo.vertices());
 
-    enum {
-        // the edge effects share this stage with glyph rendering
-        // (kGlyphMaskStage in GrTextContext) && SW path rendering
-        // (kPathMaskStage in GrSWMaskHelper)
-        kEdgeEffectStage = GrPaint::kTotalStages,
-    };
-
     GrEffectRef* effect = GrRectEffect::Create();
     static const int kRectAttrIndex = 1;
     static const int kWidthIndex = 2;
-    drawState->setEffect(kEdgeEffectStage, effect, kRectAttrIndex, kWidthIndex)->unref();
+    drawState->addCoverageEffect(effect, kRectAttrIndex, kWidthIndex)->unref();
 
     for (int i = 0; i < 4; ++i) {
         verts[i].fCenter = center;
@@ -599,16 +592,9 @@
 
     AARectVertex* verts = reinterpret_cast<AARectVertex*>(geo.vertices());
 
-    enum {
-        // the edge effects share this stage with glyph rendering
-        // (kGlyphMaskStage in GrTextContext) && SW path rendering
-        // (kPathMaskStage in GrSWMaskHelper)
-        kEdgeEffectStage = GrPaint::kTotalStages,
-    };
-
     GrEffectRef* effect = GrAlignedRectEffect::Create();
     static const int kOffsetIndex = 1;
-    drawState->setEffect(kEdgeEffectStage, effect, kOffsetIndex)->unref();
+    drawState->addCoverageEffect(effect, kOffsetIndex)->unref();
 
     SkRect devRect;
     combinedMatrix.mapRect(&devRect, rect);
diff --git a/src/gpu/GrClipMaskManager.cpp b/src/gpu/GrClipMaskManager.cpp
index 1362179..bc20c25 100644
--- a/src/gpu/GrClipMaskManager.cpp
+++ b/src/gpu/GrClipMaskManager.cpp
@@ -37,8 +37,6 @@
     GrDrawState* drawState = gpu->drawState();
     GrAssert(drawState);
 
-    static const int kMaskStage = GrPaint::kTotalStages+1;
-
     SkMatrix mat;
     // We want to use device coords to compute the texture coordinates. We set our matrix to be
     // equal to the view matrix followed by an offset to the devBound, and then a scaling matrix to
@@ -50,8 +48,8 @@
 
     SkIRect domainTexels = SkIRect::MakeWH(devBound.width(), devBound.height());
     // This could be a long-lived effect that is cached with the alpha-mask.
-    drawState->setEffect(kMaskStage,
-                         GrTextureDomainEffect::Create(result,
+    drawState->addCoverageEffect(
+        GrTextureDomainEffect::Create(result,
                                       mat,
                                       GrTextureDomainEffect::MakeTexelDomain(result, domainTexels),
                                       GrTextureDomainEffect::kDecal_WrapMode,
@@ -109,7 +107,8 @@
 ////////////////////////////////////////////////////////////////////////////////
 // sort out what kind of clip mask needs to be created: alpha, stencil,
 // scissor, or entirely software
-bool GrClipMaskManager::setupClipping(const GrClipData* clipDataIn) {
+bool GrClipMaskManager::setupClipping(const GrClipData* clipDataIn,
+                                      GrDrawState::AutoRestoreEffects* are) {
     fCurrClipMaskType = kNone_ClipMaskType;
 
     ElementList elements(16);
@@ -178,6 +177,7 @@
             // clipSpace bounds. We determine the mask's position WRT to the render target here.
             SkIRect rtSpaceMaskBounds = clipSpaceIBounds;
             rtSpaceMaskBounds.offset(-clipDataIn->fOrigin);
+            are->set(fGpu->drawState());
             setup_drawstate_aaclip(fGpu, result, rtSpaceMaskBounds);
             fGpu->disableScissor();
             this->setGpuStencil();
@@ -350,6 +350,7 @@
     GrDrawState::AutoViewMatrixRestore avmr;
     GrDrawState* drawState = fGpu->drawState();
     SkAssertResult(avmr.setIdentity(drawState));
+    GrDrawState::AutoRestoreEffects are(drawState);
 
     drawState->setRenderTarget(dstMask->asRenderTarget());
 
@@ -357,15 +358,13 @@
 
     SkMatrix sampleM;
     sampleM.setIDiv(srcMask->width(), srcMask->height());
-    drawState->setEffect(0,
+    drawState->addColorEffect(
         GrTextureDomainEffect::Create(srcMask,
                                       sampleM,
                                       GrTextureDomainEffect::MakeTexelDomain(srcMask, srcBound),
                                       GrTextureDomainEffect::kDecal_WrapMode,
                                       false))->unref();
     fGpu->drawSimpleRect(SkRect::MakeFromIRect(dstBound), NULL);
-
-    drawState->disableStage(0);
 }
 
 // get a texture to act as a temporary buffer for AA clip boolean operations
diff --git a/src/gpu/GrClipMaskManager.h b/src/gpu/GrClipMaskManager.h
index 6d9d6f6..121737b 100644
--- a/src/gpu/GrClipMaskManager.h
+++ b/src/gpu/GrClipMaskManager.h
@@ -10,6 +10,7 @@
 #define GrClipMaskManager_DEFINED
 
 #include "GrContext.h"
+#include "GrDrawState.h"
 #include "GrNoncopyable.h"
 #include "GrRect.h"
 #include "GrReducedClip.h"
@@ -29,7 +30,6 @@
 class GrPathRendererChain;
 class SkPath;
 class GrTexture;
-class GrDrawState;
 
 /**
  * The clip mask creator handles the generation of the clip mask. If anti
@@ -51,7 +51,7 @@
      * and sets the GrGpu's scissor and stencil state. If the return is false
      * then the draw can be skipped.
      */
-    bool setupClipping(const GrClipData* clipDataIn);
+    bool setupClipping(const GrClipData* clipDataIn, GrDrawState::AutoRestoreEffects*);
 
     void releaseResources();
 
diff --git a/src/gpu/GrContext.cpp b/src/gpu/GrContext.cpp
index a28f1bc..c94c1a9 100644
--- a/src/gpu/GrContext.cpp
+++ b/src/gpu/GrContext.cpp
@@ -66,6 +66,9 @@
 
 #define ASSERT_OWNED_RESOURCE(R) GrAssert(!(R) || (R)->getContext() == this)
 
+// Glorified typedef to avoid including GrDrawState.h in GrContext.h
+class GrContext::AutoRestoreEffects : public GrDrawState::AutoRestoreEffects {};
+
 GrContext* GrContext::Create(GrBackend backend, GrBackendContext backendContext) {
     GrContext* context = SkNEW(GrContext);
     if (context->init(backend, backendContext)) {
@@ -259,7 +262,7 @@
                                                                        direction,
                                                                        radius,
                                                                        sigma));
-    drawState->setEffect(0, conv);
+    drawState->addColorEffect(conv);
     target->drawSimpleRect(rect, NULL);
 }
 
@@ -372,7 +375,7 @@
         // texels in the resampled image are copies of texels from
         // the original.
         GrTextureParams params(SkShader::kClamp_TileMode, needsFiltering);
-        drawState->createTextureEffect(0, clampedTexture, SkMatrix::I(), params);
+        drawState->addColorTextureEffect(clampedTexture, SkMatrix::I(), params);
 
         drawState->setVertexAttribs<gVertexAttribs>(SK_ARRAY_COUNT(gVertexAttribs));
 
@@ -602,7 +605,8 @@
 void GrContext::clear(const GrIRect* rect,
                       const GrColor color,
                       GrRenderTarget* target) {
-    this->prepareToDraw(NULL, BUFFERED_DRAW)->clear(rect, color, target);
+    AutoRestoreEffects are;
+    this->prepareToDraw(NULL, BUFFERED_DRAW, &are)->clear(rect, color, target);
 }
 
 void GrContext::drawPaint(const GrPaint& origPaint) {
@@ -765,8 +769,8 @@
                          const SkMatrix* matrix) {
     SK_TRACE_EVENT0("GrContext::drawRect");
 
-    GrDrawTarget* target = this->prepareToDraw(&paint, BUFFERED_DRAW);
-    GrDrawState::AutoStageDisable atr(fDrawState);
+    AutoRestoreEffects are;
+    GrDrawTarget* target = this->prepareToDraw(&paint, BUFFERED_DRAW, &are);
 
     GrRect devRect;
     SkMatrix combinedMatrix;
@@ -846,8 +850,8 @@
                                const SkMatrix* dstMatrix,
                                const SkMatrix* localMatrix) {
     SK_TRACE_EVENT0("GrContext::drawRectToRect");
-    GrDrawTarget* target = this->prepareToDraw(&paint, BUFFERED_DRAW);
-    GrDrawState::AutoStageDisable atr(fDrawState);
+    AutoRestoreEffects are;
+    GrDrawTarget* target = this->prepareToDraw(&paint, BUFFERED_DRAW, &are);
 
     target->drawRect(dstRect, dstMatrix, &localRect, localMatrix);
 }
@@ -902,8 +906,8 @@
 
     GrDrawTarget::AutoReleaseGeometry geo;
 
-    GrDrawTarget* target = this->prepareToDraw(&paint, BUFFERED_DRAW);
-    GrDrawState::AutoStageDisable atr(fDrawState);
+    AutoRestoreEffects are;
+    GrDrawTarget* target = this->prepareToDraw(&paint, BUFFERED_DRAW, &are);
 
     GrDrawState* drawState = target->drawState();
 
@@ -951,8 +955,8 @@
                           const SkRRect& rect,
                           const SkStrokeRec& stroke) {
 
-    GrDrawTarget* target = this->prepareToDraw(&paint, BUFFERED_DRAW);
-    GrDrawState::AutoStageDisable atr(fDrawState);
+    AutoRestoreEffects are;
+    GrDrawTarget* target = this->prepareToDraw(&paint, BUFFERED_DRAW, &are);
 
     bool useAA = paint.isAntiAlias() &&
                  !target->getDrawState().getRenderTarget()->isMultisampled() &&
@@ -971,8 +975,8 @@
                          const GrRect& oval,
                          const SkStrokeRec& stroke) {
 
-    GrDrawTarget* target = this->prepareToDraw(&paint, BUFFERED_DRAW);
-    GrDrawState::AutoStageDisable atr(fDrawState);
+    AutoRestoreEffects are;
+    GrDrawTarget* target = this->prepareToDraw(&paint, BUFFERED_DRAW, &are);
 
     bool useAA = paint.isAntiAlias() &&
                  !target->getDrawState().getRenderTarget()->isMultisampled() &&
@@ -1045,8 +1049,8 @@
     // cache. This presents a potential hazard for buffered drawing. However,
     // the writePixels that uploads to the scratch will perform a flush so we're
     // OK.
-    GrDrawTarget* target = this->prepareToDraw(&paint, BUFFERED_DRAW);
-    GrDrawState::AutoStageDisable atr(fDrawState);
+    AutoRestoreEffects are;
+    GrDrawTarget* target = this->prepareToDraw(&paint, BUFFERED_DRAW, &are);
 
     bool useAA = paint.isAntiAlias() && !target->getDrawState().getRenderTarget()->isMultisampled();
     if (useAA && stroke.getWidth() < 0 && !path.isConvex()) {
@@ -1346,7 +1350,7 @@
                 GrDrawTarget::AutoGeometryAndStatePush agasp(fGpu, GrDrawTarget::kReset_ASRInit);
                 GrDrawState* drawState = fGpu->drawState();
                 GrAssert(effect);
-                drawState->setEffect(0, effect);
+                drawState->addColorEffect(effect);
 
                 drawState->setRenderTarget(texture->asRenderTarget());
                 GrRect rect = GrRect::MakeWH(SkIntToScalar(width), SkIntToScalar(height));
@@ -1421,7 +1425,7 @@
         return;
     }
     sampleM.preTranslate(SkIntToScalar(srcRect.fLeft), SkIntToScalar(srcRect.fTop));
-    drawState->createTextureEffect(0, src, sampleM);
+    drawState->addColorTextureEffect(src, sampleM);
     SkRect dstR = SkRect::MakeWH(SkIntToScalar(srcRect.width()), SkIntToScalar(srcRect.height()));
     fGpu->drawSimpleRect(dstR, NULL);
 }
@@ -1539,7 +1543,7 @@
     GrDrawTarget::AutoGeometryAndStatePush agasp(fGpu, GrDrawTarget::kReset_ASRInit, &matrix);
     GrDrawState* drawState = fGpu->drawState();
     GrAssert(effect);
-    drawState->setEffect(0, effect);
+    drawState->addColorEffect(effect);
 
     drawState->setRenderTarget(target);
 
@@ -1548,14 +1552,21 @@
 }
 ////////////////////////////////////////////////////////////////////////////////
 
-GrDrawTarget* GrContext::prepareToDraw(const GrPaint* paint, BufferedDraw buffered) {
+GrDrawTarget* GrContext::prepareToDraw(const GrPaint* paint,
+                                       BufferedDraw buffered,
+                                       AutoRestoreEffects* are) {
+    // All users of this draw state should be freeing up all effects when they're done.
+    // Otherwise effects that own resources may keep those resources alive indefinitely.
+    GrAssert(0 == fDrawState->numColorStages() && 0 == fDrawState->numCoverageStages());
+
     if (kNo_BufferedDraw == buffered && kYes_BufferedDraw == fLastDrawWasBuffered) {
         this->flushDrawBuffer();
         fLastDrawWasBuffered = kNo_BufferedDraw;
     }
     ASSERT_OWNED_RESOURCE(fRenderTarget.get());
     if (NULL != paint) {
-        GrAssert(fDrawState->stagesDisabled());
+        GrAssert(NULL != are);
+        are->set(fDrawState);
         fDrawState->setFromPaint(*paint, fViewMatrix, fRenderTarget.get());
 #if GR_DEBUG_PARTIAL_COVERAGE_CHECK
         if ((paint->hasMask() || 0xff != paint->fCoverage) &&
@@ -1655,7 +1666,7 @@
 }
 
 GrDrawTarget* GrContext::getTextTarget() {
-    return this->prepareToDraw(NULL, BUFFERED_DRAW);
+    return this->prepareToDraw(NULL, BUFFERED_DRAW, NULL);
 }
 
 const GrIndexBuffer* GrContext::getQuadIndexBuffer() const {
@@ -1779,7 +1790,8 @@
         }
 
         this->setRenderTarget(dstTexture->asRenderTarget());
-        GrDrawTarget* target = this->prepareToDraw(NULL, BUFFERED_DRAW);
+        AutoRestoreEffects are;
+        GrDrawTarget* target = this->prepareToDraw(&paint, BUFFERED_DRAW, &are);
         convolve_gaussian(target, srcTexture, srcRect, sigmaX, radiusX,
                           Gr1DKernelEffect::kX_Direction);
         srcTexture = dstTexture;
@@ -1796,7 +1808,8 @@
         }
 
         this->setRenderTarget(dstTexture->asRenderTarget());
-        GrDrawTarget* target = this->prepareToDraw(NULL, BUFFERED_DRAW);
+        AutoRestoreEffects are;
+        GrDrawTarget* target = this->prepareToDraw(&paint, BUFFERED_DRAW, &are);
         convolve_gaussian(target, srcTexture, srcRect, sigmaY, radiusY,
                           Gr1DKernelEffect::kY_Direction);
         srcTexture = dstTexture;
diff --git a/src/gpu/GrDrawState.cpp b/src/gpu/GrDrawState.cpp
index 2a81e26..7c68543 100644
--- a/src/gpu/GrDrawState.cpp
+++ b/src/gpu/GrDrawState.cpp
@@ -9,18 +9,17 @@
 #include "GrPaint.h"
 
 bool GrDrawState::setIdentityViewMatrix()  {
-    SkMatrix invVM;
-    bool inverted = false;
-    for (int s = 0; s < GrDrawState::kNumStages; ++s) {
-        if (this->isStageEnabled(s)) {
-            if (!inverted) {
-                if (!fCommon.fViewMatrix.invert(&invVM)) {
-                    // sad trombone sound
-                    return false;
-                }
-                inverted = true;
-            }
-            fStages[s].localCoordChange(invVM);
+    if (fColorStages.count() || fCoverageStages.count()) {
+        SkMatrix invVM;
+        if (!fCommon.fViewMatrix.invert(&invVM)) {
+            // sad trombone sound
+            return false;
+        }
+        for (int s = 0; s < fColorStages.count(); ++s) {
+            fColorStages[s].localCoordChange(invVM);
+        }
+        for (int s = 0; s < fCoverageStages.count(); ++s) {
+            fCoverageStages[s].localCoordChange(invVM);
         }
     }
     fCommon.fViewMatrix.reset();
@@ -28,31 +27,23 @@
 }
 
 void GrDrawState::setFromPaint(const GrPaint& paint, const SkMatrix& vm, GrRenderTarget* rt) {
+    GrAssert(0 == fBlockEffectRemovalCnt || 0 == this->numTotalStages());
+
+    fColorStages.reset();
+    fCoverageStages.reset();
+
     for (int i = 0; i < GrPaint::kMaxColorStages; ++i) {
-        int s = i + GrPaint::kFirstColorStage;
         if (paint.isColorStageEnabled(i)) {
-            fStages[s] = paint.getColorStage(i);
-        } else {
-            fStages[s].setEffect(NULL);
+            fColorStages.push_back(paint.getColorStage(i));
         }
     }
 
-    this->setFirstCoverageStage(GrPaint::kFirstCoverageStage);
-
     for (int i = 0; i < GrPaint::kMaxCoverageStages; ++i) {
-        int s = i + GrPaint::kFirstCoverageStage;
         if (paint.isCoverageStageEnabled(i)) {
-            fStages[s] = paint.getCoverageStage(i);
-        } else {
-            fStages[s].setEffect(NULL);
+            fCoverageStages.push_back(paint.getCoverageStage(i));
         }
     }
 
-    // disable all stages not accessible via the paint
-    for (int s = GrPaint::kTotalStages; s < GrDrawState::kNumStages; ++s) {
-        this->disableStage(s);
-    }
-
     this->setRenderTarget(rt);
 
     fCommon.fViewMatrix = vm;
@@ -162,33 +153,34 @@
     for (int i = 0; i < kMaxVertexAttribCnt; ++i) {
         slTypes[i] = static_cast<GrSLType>(-1);
     }
-    for (int s = 0; s < kNumStages; ++s) {
-        if (this->isStageEnabled(s)) {
-            const GrEffectStage& stage = fStages[s];
-            const GrEffectRef* effect = stage.getEffect();
-            // make sure that any attribute indices have the correct binding type, that the attrib
-            // type and effect's shader lang type are compatible, and that attributes shared by
-            // multiple effects use the same shader lang type.
-            const int* attributeIndices = stage.getVertexAttribIndices();
-            int numAttributes = stage.getVertexAttribIndexCount();
-            for (int i = 0; i < numAttributes; ++i) {
-                int attribIndex = attributeIndices[i];
-                if (attribIndex >= fCommon.fVACount ||
-                    kEffect_GrVertexAttribBinding != fCommon.fVAPtr[attribIndex].fBinding) {
-                    return false;
-                }
-
-                GrSLType effectSLType = (*effect)->vertexAttribType(i);
-                GrVertexAttribType attribType = fCommon.fVAPtr[attribIndex].fType;
-                int slVecCount = GrSLTypeVectorCount(effectSLType);
-                int attribVecCount = GrVertexAttribTypeVectorCount(attribType);
-                if (slVecCount != attribVecCount ||
-                    (static_cast<GrSLType>(-1) != slTypes[attribIndex] &&
-                     slTypes[attribIndex] != effectSLType)) {
-                    return false;
-                }
-                slTypes[attribIndex] = effectSLType;
+    int totalStages = fColorStages.count() + fCoverageStages.count();
+    for (int s = 0; s < totalStages; ++s) {
+        int covIdx = s - fColorStages.count();
+        const GrEffectStage& stage = covIdx < 0 ? fColorStages[s] : fCoverageStages[covIdx];
+        const GrEffectRef* effect = stage.getEffect();
+        GrAssert(NULL != effect);
+        // make sure that any attribute indices have the correct binding type, that the attrib
+        // type and effect's shader lang type are compatible, and that attributes shared by
+        // multiple effects use the same shader lang type.
+        const int* attributeIndices = stage.getVertexAttribIndices();
+        int numAttributes = stage.getVertexAttribIndexCount();
+        for (int i = 0; i < numAttributes; ++i) {
+            int attribIndex = attributeIndices[i];
+            if (attribIndex >= fCommon.fVACount ||
+                kEffect_GrVertexAttribBinding != fCommon.fVAPtr[attribIndex].fBinding) {
+                return false;
             }
+
+            GrSLType effectSLType = (*effect)->vertexAttribType(i);
+            GrVertexAttribType attribType = fCommon.fVAPtr[attribIndex].fType;
+            int slVecCount = GrSLTypeVectorCount(effectSLType);
+            int attribVecCount = GrVertexAttribTypeVectorCount(attribType);
+            if (slVecCount != attribVecCount ||
+                (static_cast<GrSLType>(-1) != slTypes[attribIndex] &&
+                    slTypes[attribIndex] != effectSLType)) {
+                return false;
+            }
+            slTypes[attribIndex] = effectSLType;
         }
     }
 
@@ -196,9 +188,15 @@
 }
 
 bool GrDrawState::willEffectReadDstColor() const {
-    int startStage = this->isColorWriteDisabled() ? this->getFirstCoverageStage() : 0;
-    for (int s = startStage; s < kNumStages; ++s) {
-        if (this->isStageEnabled(s) && (*this->getStage(s).getEffect())->willReadDstColor()) {
+    if (!this->isColorWriteDisabled()) {
+        for (int s = 0; s < fColorStages.count(); ++s) {
+            if ((*fColorStages[s].getEffect())->willReadDstColor()) {
+                return true;
+            }
+        }
+    }
+    for (int s = 0; s < fCoverageStages.count(); ++s) {
+        if ((*fCoverageStages[s].getEffect())->willReadDstColor()) {
             return true;
         }
     }
@@ -220,12 +218,9 @@
     }
 
     // Run through the color stages
-    int stageCnt = getFirstCoverageStage();
-    for (int s = 0; s < stageCnt; ++s) {
-        const GrEffectRef* effect = this->getStage(s).getEffect();
-        if (NULL != effect) {
-            (*effect)->getConstantColorComponents(&color, &validComponentFlags);
-        }
+    for (int s = 0; s < fColorStages.count(); ++s) {
+        const GrEffectRef* effect = fColorStages[s].getEffect();
+        (*effect)->getConstantColorComponents(&color, &validComponentFlags);
     }
 
     // Check if the color filter could introduce an alpha.
@@ -247,11 +242,9 @@
                 color |= (SkMulDiv255Round(a, b) << (c * 8));
             }
         }
-        for (int s = this->getFirstCoverageStage(); s < GrDrawState::kNumStages; ++s) {
-            const GrEffectRef* effect = this->getStage(s).getEffect();
-            if (NULL != effect) {
-                (*effect)->getConstantColorComponents(&color, &validComponentFlags);
-            }
+        for (int s = 0; s < fCoverageStages.count(); ++s) {
+            const GrEffectRef* effect = fCoverageStages[s].getEffect();
+            (*effect)->getConstantColorComponents(&color, &validComponentFlags);
         }
     }
     return (kA_GrColorComponentFlag & validComponentFlags) && 0xff == GrColorUnpackA(color);
@@ -274,13 +267,11 @@
     }
 
     // Run through the coverage stages and see if the coverage will be all ones at the end.
-    for (int s = this->getFirstCoverageStage(); s < GrDrawState::kNumStages; ++s) {
-        const GrEffectRef* effect = this->getStage(s).getEffect();
-        if (NULL != effect) {
-            (*effect)->getConstantColorComponents(&coverage, &validComponentFlags);
-        }
+    for (int s = 0; s < fCoverageStages.count(); ++s) {
+        const GrEffectRef* effect = fCoverageStages[s].getEffect();
+        (*effect)->getConstantColorComponents(&coverage, &validComponentFlags);
     }
-    return (kRGBA_GrColorComponentFlags == validComponentFlags)  && (0xffffffff == coverage);
+    return (kRGBA_GrColorComponentFlags == validComponentFlags) && (0xffffffff == coverage);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -349,12 +340,8 @@
     // check for coverage due to constant coverage, per-vertex coverage, or coverage stage
     bool hasCoverage = forceCoverage ||
                        0xffffffff != this->getCoverage() ||
-                       this->hasCoverageVertexAttribute();
-    for (int s = this->getFirstCoverageStage(); !hasCoverage && s < GrDrawState::kNumStages; ++s) {
-        if (this->isStageEnabled(s)) {
-            hasCoverage = true;
-        }
-    }
+                       this->hasCoverageVertexAttribute() ||
+                       fCoverageStages.count() > 0;
 
     // if we don't have coverage we can check whether the dst
     // has to read at all. If not, we'll disable blending.
@@ -417,11 +404,18 @@
 
 void GrDrawState::AutoViewMatrixRestore::restore() {
     if (NULL != fDrawState) {
+        GR_DEBUGCODE(--fDrawState->fBlockEffectRemovalCnt;)
         fDrawState->fCommon.fViewMatrix = fViewMatrix;
-        for (int s = 0; s < GrDrawState::kNumStages; ++s) {
-            if (fRestoreMask & (1 << s)) {
-                fDrawState->fStages[s].restoreCoordChange(fSavedCoordChanges[s]);
-            }
+        GrAssert(fDrawState->numColorStages() >= fNumColorStages);
+        int numCoverageStages = fSavedCoordChanges.count() - fNumColorStages;
+        GrAssert(fDrawState->numCoverageStages() >= numCoverageStages);
+
+        int i = 0;
+        for (int s = 0; s < fNumColorStages; ++s, ++i) {
+            fDrawState->fColorStages[s].restoreCoordChange(fSavedCoordChanges[i]);
+        }
+        for (int s = 0; s < numCoverageStages; ++s, ++i) {
+            fDrawState->fCoverageStages[s].restoreCoordChange(fSavedCoordChanges[i]);
         }
         fDrawState = NULL;
     }
@@ -431,21 +425,17 @@
                                              const SkMatrix& preconcatMatrix) {
     this->restore();
 
+    GrAssert(NULL == fDrawState);
     if (NULL == drawState || preconcatMatrix.isIdentity()) {
         return;
     }
     fDrawState = drawState;
 
-    fRestoreMask = 0;
     fViewMatrix = drawState->getViewMatrix();
     drawState->fCommon.fViewMatrix.preConcat(preconcatMatrix);
-    for (int s = 0; s < GrDrawState::kNumStages; ++s) {
-        if (drawState->isStageEnabled(s)) {
-            fRestoreMask |= (1 << s);
-            drawState->fStages[s].saveCoordChange(&fSavedCoordChanges[s]);
-            drawState->fStages[s].localCoordChange(preconcatMatrix);
-        }
-    }
+
+    this->doEffectCoordChanges(preconcatMatrix);
+    GR_DEBUGCODE(++fDrawState->fBlockEffectRemovalCnt;)
 }
 
 bool GrDrawState::AutoViewMatrixRestore::setIdentity(GrDrawState* drawState) {
@@ -460,16 +450,39 @@
     }
 
     fViewMatrix = drawState->getViewMatrix();
-    fRestoreMask = 0;
-    for (int s = 0; s < GrDrawState::kNumStages; ++s) {
-        if (drawState->isStageEnabled(s)) {
-            fRestoreMask |= (1 << s);
-            drawState->fStages[s].saveCoordChange(&fSavedCoordChanges[s]);
+    if (0 == drawState->numTotalStages()) {
+        drawState->fCommon.fViewMatrix.reset();
+        fDrawState = drawState;
+        fNumColorStages = 0;
+        fSavedCoordChanges.reset(0);
+        GR_DEBUGCODE(++fDrawState->fBlockEffectRemovalCnt;)
+        return true;
+    } else {
+        SkMatrix inv;
+        if (!fViewMatrix.invert(&inv)) {
+            return false;
         }
+        drawState->fCommon.fViewMatrix.reset();
+        fDrawState = drawState;
+        this->doEffectCoordChanges(inv);
+        GR_DEBUGCODE(++fDrawState->fBlockEffectRemovalCnt;)
+        return true;
     }
-    if (!drawState->setIdentityViewMatrix()) {
-        return false;
+}
+
+void GrDrawState::AutoViewMatrixRestore::doEffectCoordChanges(const SkMatrix& coordChangeMatrix) {
+    fSavedCoordChanges.reset(fDrawState->numTotalStages());
+    int i = 0;
+
+    fNumColorStages = fDrawState->numColorStages();
+    for (int s = 0; s < fNumColorStages; ++s, ++i) {
+        fDrawState->fColorStages[s].saveCoordChange(&fSavedCoordChanges[i]);
+        fDrawState->fColorStages[s].localCoordChange(coordChangeMatrix);
     }
-    fDrawState = drawState;
-    return true;
+
+    int numCoverageStages = fDrawState->numCoverageStages();
+    for (int s = 0; s < numCoverageStages; ++s, ++i) {
+        fDrawState->fCoverageStages[s].saveCoordChange(&fSavedCoordChanges[i]);
+        fDrawState->fCoverageStages[s].localCoordChange(coordChangeMatrix);
+    }
 }
diff --git a/src/gpu/GrDrawState.h b/src/gpu/GrDrawState.h
index 5b1194d..9d1e64d 100644
--- a/src/gpu/GrDrawState.h
+++ b/src/gpu/GrDrawState.h
@@ -27,43 +27,21 @@
 public:
     SK_DECLARE_INST_COUNT(GrDrawState)
 
-    /**
-     * Total number of effect stages. Each stage can host a GrEffect. A stage is enabled if it has a
-     * GrEffect. The effect produces an output color in the fragment shader. It's inputs are the
-     * output from the previous enabled stage and a position. The position is either derived from
-     * the interpolated vertex positions or explicit per-vertex coords, depending upon the
-     * GrAttribBindings used to draw.
-     *
-     * The stages are divided into two sets, color-computing and coverage-computing. The final color
-     * stage produces the final pixel color. The coverage-computing stages function exactly as the
-     * color-computing but the output of the final coverage stage is treated as a fractional pixel
-     * coverage rather than as input to the src/dst color blend step.
-     *
-     * The input color to the first enabled color-stage is either the constant color or interpolated
-     * per-vertex colors. The input to the first coverage stage is either a constant coverage
-     * (usually full-coverage) or interpolated per-vertex coverage.
-     *
-     * See the documentation of kCoverageDrawing_StateBit for information about disabling the
-     * the color / coverage distinction.
-     *
-     * Stages 0 through GrPaint::kTotalStages-1 are reserved for stages copied from the client's
-     * GrPaint. Stage GrPaint::kTotalStages is earmarked for use by GrTextContext, GrPathRenderer-
-     * derived classes, and the rect/oval helper classes. GrPaint::kTotalStages+1 is earmarked for
-     * clipping by GrClipMaskManager. TODO: replace fixed size array of stages with variable size
-     * arrays of color and coverage stages.
-     */
-    enum {
-        kNumStages = GrPaint::kTotalStages + 2,
-    };
+    GrDrawState() {
+        GR_DEBUGCODE(fBlockEffectRemovalCnt = 0;)
+        this->reset();
+    }
 
-    GrDrawState() { this->reset(); }
-
-    GrDrawState(const SkMatrix& initialViewMatrix) { this->reset(initialViewMatrix); }
+    GrDrawState(const SkMatrix& initialViewMatrix) {
+        GR_DEBUGCODE(fBlockEffectRemovalCnt = 0;)
+        this->reset(initialViewMatrix);
+    }
 
     /**
      * Copies another draw state.
      **/
     GrDrawState(const GrDrawState& state) {
+        GR_DEBUGCODE(fBlockEffectRemovalCnt = 0;)
         *this = state;
     }
 
@@ -71,17 +49,19 @@
      * Copies another draw state with a preconcat to the view matrix.
      **/
     GrDrawState(const GrDrawState& state, const SkMatrix& preConcatMatrix) {
+        GR_DEBUGCODE(fBlockEffectRemovalCnt = 0;)
         *this = state;
         if (!preConcatMatrix.isIdentity()) {
-            for (int i = 0; i < kNumStages; ++i) {
-                if (this->isStageEnabled(i)) {
-                    fStages[i].localCoordChange(preConcatMatrix);
-                }
+            for (int i = 0; i < fColorStages.count(); ++i) {
+                fColorStages[i].localCoordChange(preConcatMatrix);
+            }
+            for (int i = 0; i < fCoverageStages.count(); ++i) {
+                fCoverageStages[i].localCoordChange(preConcatMatrix);
             }
         }
     }
 
-    virtual ~GrDrawState() { this->disableStages(); }
+    virtual ~GrDrawState() { GrAssert(0 == fBlockEffectRemovalCnt); }
 
     /**
      * Resets to the default state. GrEffects will be removed from all stages.
@@ -93,8 +73,7 @@
     /**
      * Initializes the GrDrawState based on a GrPaint, view matrix and render target. Note that
      * GrDrawState encompasses more than GrPaint. Aspects of GrDrawState that have no GrPaint
-     * equivalents are set to default values. GrPaint has fewer stages than GrDrawState. The extra
-     * GrDrawState stages are disabled. Clipping will be enabled.
+     * equivalents are set to default values. Clipping will be enabled.
      */
     void setFromPaint(const GrPaint& , const SkMatrix& viewMatrix, GrRenderTarget*);
 
@@ -359,90 +338,105 @@
 
     ///////////////////////////////////////////////////////////////////////////
     /// @name Effect Stages
+    /// Each stage hosts a GrEffect. The effect produces an output color or coverage in the fragment
+    /// shader. Its inputs are the output from the previous stage as well as some variables
+    /// available to it in the fragment and vertex shader (e.g. the vertex position, the dst color,
+    /// the fragment position, local coordinates).
+    ///
+    /// The stages are divided into two sets, color-computing and coverage-computing. The final
+    /// color stage produces the final pixel color. The coverage-computing stages function exactly
+    /// as the color-computing but the output of the final coverage stage is treated as a fractional
+    /// pixel coverage rather than as input to the src/dst color blend step.
+    ///
+    /// The input color to the first color-stage is either the constant color or interpolated
+    /// per-vertex colors. The input to the first coverage stage is either a constant coverage
+    /// (usually full-coverage) or interpolated per-vertex coverage.
+    ///
+    /// See the documentation of kCoverageDrawing_StateBit for information about disabling the
+    /// the color / coverage distinction.
     ////
 
-    const GrEffectRef* setEffect(int stageIdx, const GrEffectRef* effect) {
-        fStages[stageIdx].setEffect(effect);
+    const GrEffectRef* addColorEffect(const GrEffectRef* effect, int attr0 = -1, int attr1 = -1) {
+        GrAssert(NULL != effect);
+        SkNEW_APPEND_TO_TARRAY(&fColorStages, GrEffectStage, (effect, attr0, attr1));
         return effect;
     }
 
-    const GrEffectRef* setEffect(int stageIdx, const GrEffectRef* effect,
-                                 int attr0, int attr1 = -1) {
-        fStages[stageIdx].setEffect(effect, attr0, attr1);
+    const GrEffectRef* addCoverageEffect(const GrEffectRef* effect, int attr0 = -1, int attr1 = -1) {
+        GrAssert(NULL != effect);
+        SkNEW_APPEND_TO_TARRAY(&fCoverageStages, GrEffectStage, (effect, attr0, attr1));
         return effect;
     }
 
     /**
      * Creates a GrSimpleTextureEffect that uses local coords as texture coordinates.
      */
-    void createTextureEffect(int stageIdx, GrTexture* texture, const SkMatrix& matrix) {
-        GrAssert(!this->getStage(stageIdx).getEffect());
+    void addColorTextureEffect(GrTexture* texture, const SkMatrix& matrix) {
         GrEffectRef* effect = GrSimpleTextureEffect::Create(texture, matrix);
-        this->setEffect(stageIdx, effect)->unref();
+        this->addColorEffect(effect)->unref();
     }
-    void createTextureEffect(int stageIdx,
-                             GrTexture* texture,
-                             const SkMatrix& matrix,
-                             const GrTextureParams& params) {
-        GrAssert(!this->getStage(stageIdx).getEffect());
+
+    void addCoverageTextureEffect(GrTexture* texture, const SkMatrix& matrix) {
+        GrEffectRef* effect = GrSimpleTextureEffect::Create(texture, matrix);
+        this->addCoverageEffect(effect)->unref();
+    }
+
+    void addColorTextureEffect(GrTexture* texture,
+                               const SkMatrix& matrix,
+                               const GrTextureParams& params) {
         GrEffectRef* effect = GrSimpleTextureEffect::Create(texture, matrix, params);
-        this->setEffect(stageIdx, effect)->unref();
+        this->addColorEffect(effect)->unref();
     }
 
-    bool stagesDisabled() {
-        for (int i = 0; i < kNumStages; ++i) {
-            if (NULL != fStages[i].getEffect()) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    void disableStage(int stageIdx) {
-        this->setEffect(stageIdx, NULL);
+    void addCoverageTextureEffect(GrTexture* texture,
+                                  const SkMatrix& matrix,
+                                  const GrTextureParams& params) {
+        GrEffectRef* effect = GrSimpleTextureEffect::Create(texture, matrix, params);
+        this->addCoverageEffect(effect)->unref();
     }
 
     /**
-     * Release all the GrEffects referred to by this draw state.
+     * When this object is destroyed it will remove any effects from the draw state that were added
+     * after its constructor.
      */
-    void disableStages() {
-        for (int i = 0; i < kNumStages; ++i) {
-            this->disableStage(i);
-        }
-    }
-
-    class AutoStageDisable : public ::GrNoncopyable {
+    class AutoRestoreEffects : public ::GrNoncopyable {
     public:
-        AutoStageDisable(GrDrawState* ds) : fDrawState(ds) {}
-        ~AutoStageDisable() {
+        AutoRestoreEffects() : fDrawState(NULL) {}
+
+        AutoRestoreEffects(GrDrawState* ds) : fDrawState(NULL) { this->set(ds); }
+
+        ~AutoRestoreEffects() { this->set(NULL); }
+
+        void set(GrDrawState* ds) {
             if (NULL != fDrawState) {
-                fDrawState->disableStages();
+                int n = fDrawState->fColorStages.count() - fColorEffectCnt;
+                GrAssert(n >= 0);
+                fDrawState->fColorStages.pop_back_n(n);
+                n = fDrawState->fCoverageStages.count() - fCoverageEffectCnt;
+                GrAssert(n >= 0);
+                fDrawState->fCoverageStages.pop_back_n(n);
+                GR_DEBUGCODE(--fDrawState->fBlockEffectRemovalCnt;)
+            }
+            fDrawState = ds;
+            if (NULL != ds) {
+                fColorEffectCnt = ds->fColorStages.count();
+                fCoverageEffectCnt = ds->fCoverageStages.count();
+                GR_DEBUGCODE(++ds->fBlockEffectRemovalCnt;)
             }
         }
+
     private:
         GrDrawState* fDrawState;
+        int fColorEffectCnt;
+        int fCoverageEffectCnt;
     };
 
-    /**
-     * Returns the current stage by index.
-     */
-    const GrEffectStage& getStage(int stageIdx) const {
-        GrAssert((unsigned)stageIdx < kNumStages);
-        return fStages[stageIdx];
-    }
+    int numColorStages() const { return fColorStages.count(); }
+    int numCoverageStages() const { return fCoverageStages.count(); }
+    int numTotalStages() const { return this->numColorStages() + this->numCoverageStages(); }
 
-    /**
-     * Called when the source coord system is changing. This ensures that effects will see the
-     * correct local coordinates. oldToNew gives the transformation from the old coord system in
-     * which the geometry was specified to the new coordinate system from which it will be rendered.
-     */
-    void localCoordChange(const SkMatrix& oldToNew) {
-        for (int i = 0; i < kNumStages; ++i) {
-            if (this->isStageEnabled(i)) {
-                fStages[i].localCoordChange(oldToNew);
-            }
-        }
-    }
+    const GrEffectStage& getColorStage(int stageIdx) const { return fColorStages[stageIdx]; }
+    const GrEffectStage& getCoverageStage(int stageIdx) const { return fCoverageStages[stageIdx]; }
 
     /**
      * Checks whether any of the effects will read the dst pixel color.
@@ -452,33 +446,6 @@
     /// @}
 
     ///////////////////////////////////////////////////////////////////////////
-    /// @name Coverage / Color Stages
-    ////
-
-    /**
-     * A common pattern is to compute a color with the initial stages and then
-     * modulate that color by a coverage value in later stage(s) (AA, mask-
-     * filters, glyph mask, etc). Color-filters, xfermodes, etc should be
-     * computed based on the pre-coverage-modulated color. The division of
-     * stages between color-computing and coverage-computing is specified by
-     * this method. Initially this is kNumStages (all stages
-     * are color-computing).
-     */
-    void setFirstCoverageStage(int firstCoverageStage) {
-        GrAssert((unsigned)firstCoverageStage <= kNumStages);
-        fCommon.fFirstCoverageStage = firstCoverageStage;
-    }
-
-    /**
-     * Gets the index of the first coverage-computing stage.
-     */
-    int getFirstCoverageStage() const {
-        return fCommon.fFirstCoverageStage;
-    }
-
-    ///@}
-
-    ///////////////////////////////////////////////////////////////////////////
     /// @name Blending
     ////
 
@@ -674,10 +641,12 @@
         bool setIdentity(GrDrawState* drawState);
 
     private:
-        GrDrawState*                        fDrawState;
-        SkMatrix                            fViewMatrix;
-        GrEffectStage::SavedCoordChange     fSavedCoordChanges[GrDrawState::kNumStages];
-        uint32_t                            fRestoreMask;
+        void doEffectCoordChanges(const SkMatrix& coordChangeMatrix);
+
+        GrDrawState*                                        fDrawState;
+        SkMatrix                                            fViewMatrix;
+        int                                                 fNumColorStages;
+        SkAutoSTArray<8, GrEffectStage::SavedCoordChange>   fSavedCoordChanges;
     };
 
     /// @}
@@ -905,21 +874,20 @@
 
     ///////////////////////////////////////////////////////////////////////////
 
-    bool isStageEnabled(int s) const {
-        GrAssert((unsigned)s < kNumStages);
-        return (NULL != fStages[s].getEffect());
-    }
-
     bool operator ==(const GrDrawState& s) const {
-        if (fRenderTarget.get() != s.fRenderTarget.get() || fCommon != s.fCommon) {
+        if (fRenderTarget.get() != s.fRenderTarget.get() ||
+            fColorStages.count() != s.fColorStages.count() ||
+            fCoverageStages.count() != s.fCoverageStages.count() ||
+            fCommon != s.fCommon) {
             return false;
         }
-        for (int i = 0; i < kNumStages; i++) {
-            bool enabled = this->isStageEnabled(i);
-            if (enabled != s.isStageEnabled(i)) {
+        for (int i = 0; i < fColorStages.count(); i++) {
+            if (fColorStages[i] != s.fColorStages[i]) {
                 return false;
             }
-            if (enabled && this->fStages[i] != s.fStages[i]) {
+        }
+        for (int i = 0; i < fCoverageStages.count(); i++) {
+            if (fCoverageStages[i] != s.fCoverageStages[i]) {
                 return false;
             }
         }
@@ -928,21 +896,20 @@
     bool operator !=(const GrDrawState& s) const { return !(*this == s); }
 
     GrDrawState& operator= (const GrDrawState& s) {
+        GrAssert(0 == fBlockEffectRemovalCnt || 0 == this->numTotalStages());
         this->setRenderTarget(s.fRenderTarget.get());
         fCommon = s.fCommon;
-        for (int i = 0; i < kNumStages; i++) {
-            if (s.isStageEnabled(i)) {
-                this->fStages[i] = s.fStages[i];
-            }
-        }
+        fColorStages = s.fColorStages;
+        fCoverageStages = s.fCoverageStages;
         return *this;
     }
 
 private:
 
     void onReset(const SkMatrix* initialViewMatrix) {
-
-        this->disableStages();
+        GrAssert(0 == fBlockEffectRemovalCnt || 0 == this->numTotalStages());
+        fColorStages.reset();
+        fCoverageStages.reset();
 
         fRenderTarget.reset(NULL);
 
@@ -959,7 +926,6 @@
         fCommon.fBlendConstant = 0x0;
         fCommon.fFlagBits = 0x0;
         fCommon.fStencilSettings.setDisabled();
-        fCommon.fFirstCoverageStage = kNumStages;
         fCommon.fCoverage = 0xffffffff;
         fCommon.fColorFilterMode = SkXfermode::kDst_Mode;
         fCommon.fColorFilterColor = 0x0;
@@ -978,7 +944,6 @@
         const GrVertexAttrib* fVAPtr;
         int                   fVACount;
         GrStencilSettings     fStencilSettings;
-        int                   fFirstCoverageStage;
         GrColor               fCoverage;
         SkXfermode::Mode      fColorFilterMode;
         GrColor               fColorFilterColor;
@@ -998,7 +963,6 @@
                           fVACount == other.fVACount &&
                           !memcmp(fVAPtr, other.fVAPtr, fVACount * sizeof(GrVertexAttrib)) &&
                           fStencilSettings == other.fStencilSettings &&
-                          fFirstCoverageStage == other.fFirstCoverageStage &&
                           fCoverage == other.fCoverage &&
                           fColorFilterMode == other.fColorFilterMode &&
                           fColorFilterColor == other.fColorFilterColor &&
@@ -1042,8 +1006,13 @@
             // Here we ref the effects directly rather than the effect-refs. TODO: When the effect-
             // ref gets fully unref'ed it will cause the underlying effect to unref its resources
             // and recycle them to the cache (if no one else is holding a ref to the resources).
-            for (int i = 0; i < kNumStages; ++i) {
-                fStages[i].saveFrom(drawState.fStages[i]);
+            fStages.reset(drawState.fColorStages.count() + drawState.fCoverageStages.count());
+            fColorStageCnt = drawState.fColorStages.count();
+            for (int i = 0; i < fColorStageCnt; ++i) {
+                fStages[i].saveFrom(drawState.fColorStages[i]);
+            }
+            for (int i = 0; i < drawState.fCoverageStages.count(); ++i) {
+                fStages[i + fColorStageCnt].saveFrom(drawState.fCoverageStages[i]);
             }
             GR_DEBUGCODE(fInitialized = true;)
         }
@@ -1052,17 +1021,35 @@
             GrAssert(fInitialized);
             drawState->fCommon = fCommon;
             drawState->setRenderTarget(fRenderTarget);
-            for (int i = 0; i < kNumStages; ++i) {
-                fStages[i].restoreTo(&drawState->fStages[i]);
+            // reinflate color/cov stage arrays.
+            drawState->fColorStages.reset(fColorStageCnt);
+            for (int i = 0; i < fColorStageCnt; ++i) {
+                fStages[i].restoreTo(&drawState->fColorStages[i]);
+            }
+            int coverageStageCnt = fStages.count() - fColorStageCnt;
+            drawState->fCoverageStages.reset(coverageStageCnt);
+            for (int i = 0; i < coverageStageCnt; ++i) {
+                fStages[fColorStageCnt + i].restoreTo(&drawState->fCoverageStages[i]);
             }
         }
 
         bool isEqual(const GrDrawState& state) const {
-            if (fRenderTarget != state.fRenderTarget.get() || fCommon != state.fCommon) {
+            int numCoverageStages = fStages.count() - fColorStageCnt;
+            if (fRenderTarget != state.fRenderTarget.get() ||
+                fColorStageCnt != state.fColorStages.count() ||
+                numCoverageStages != state.fCoverageStages.count() ||
+                fCommon != state.fCommon) {
                 return false;
             }
-            for (int i = 0; i < kNumStages; ++i) {
-                if (!fStages[i].isEqual(state.fStages[i], state.hasLocalCoordAttribute())) {
+            bool explicitLocalCoords = state.hasLocalCoordAttribute();
+            for (int i = 0; i < fColorStageCnt; ++i) {
+                if (!fStages[i].isEqual(state.fColorStages[i], explicitLocalCoords)) {
+                    return false;
+                }
+            }
+            for (int i = 0; i < numCoverageStages; ++i) {
+                int s = fColorStageCnt + i;
+                if (!fStages[s].isEqual(state.fCoverageStages[i], explicitLocalCoords)) {
                     return false;
                 }
             }
@@ -1070,18 +1057,28 @@
         }
 
     private:
+        typedef SkAutoSTArray<8, GrEffectStage::DeferredStage> DeferredStageArray;
+
         GrRenderTarget*                       fRenderTarget;
         CommonState                           fCommon;
-        GrEffectStage::DeferredStage          fStages[kNumStages];
+        int                                   fColorStageCnt;
+        DeferredStageArray                    fStages;
 
         GR_DEBUGCODE(bool fInitialized;)
     };
 
 private:
 
-    SkAutoTUnref<GrRenderTarget>           fRenderTarget;
-    CommonState                            fCommon;
-    GrEffectStage                          fStages[kNumStages];
+    SkAutoTUnref<GrRenderTarget>        fRenderTarget;
+    CommonState                         fCommon;
+
+    typedef SkSTArray<4, GrEffectStage> EffectStageArray;
+    EffectStageArray                    fColorStages;
+    EffectStageArray                    fCoverageStages;
+
+    // Some of the auto restore objects assume that no effects are removed during their lifetime.
+    // This is used to assert that this condition holds.
+    GR_DEBUGCODE(int fBlockEffectRemovalCnt;)
 
     /**
      *  Sets vertex attributes for next draw.
diff --git a/src/gpu/GrDrawTarget.cpp b/src/gpu/GrDrawTarget.cpp
index 02bc8d7..1f3b630 100644
--- a/src/gpu/GrDrawTarget.cpp
+++ b/src/gpu/GrDrawTarget.cpp
@@ -387,14 +387,21 @@
     }
 
     GrAssert(NULL != drawState.getRenderTarget());
-    for (int s = 0; s < GrDrawState::kNumStages; ++s) {
-        if (drawState.isStageEnabled(s)) {
-            const GrEffectRef& effect = *drawState.getStage(s).getEffect();
-            int numTextures = effect->numTextures();
-            for (int t = 0; t < numTextures; ++t) {
-                GrTexture* texture = effect->texture(t);
-                GrAssert(texture->asRenderTarget() != drawState.getRenderTarget());
-            }
+
+    for (int s = 0; s < drawState.numColorStages(); ++s) {
+        const GrEffectRef& effect = *drawState.getColorStage(s).getEffect();
+        int numTextures = effect->numTextures();
+        for (int t = 0; t < numTextures; ++t) {
+            GrTexture* texture = effect->texture(t);
+            GrAssert(texture->asRenderTarget() != drawState.getRenderTarget());
+        }
+    }
+    for (int s = 0; s < drawState.numCoverageStages(); ++s) {
+        const GrEffectRef& effect = *drawState.getCoverageStage(s).getEffect();
+        int numTextures = effect->numTextures();
+        for (int t = 0; t < numTextures; ++t) {
+            GrTexture* texture = effect->texture(t);
+            GrAssert(texture->asRenderTarget() != drawState.getRenderTarget());
         }
     }
 
@@ -912,7 +919,7 @@
     matrix.setTranslate(SkIntToScalar(srcRect.fLeft - dstPoint.fX),
                         SkIntToScalar(srcRect.fTop - dstPoint.fY));
     matrix.postIDiv(tex->width(), tex->height());
-    this->drawState()->createTextureEffect(0, tex, matrix);
+    this->drawState()->addColorTextureEffect(tex, matrix);
     SkIRect dstRect = SkIRect::MakeXYWH(dstPoint.fX,
                                         dstPoint.fY,
                                         srcRect.width(),
diff --git a/src/gpu/GrDrawTarget.h b/src/gpu/GrDrawTarget.h
index e876136..f31dd6c 100644
--- a/src/gpu/GrDrawTarget.h
+++ b/src/gpu/GrDrawTarget.h
@@ -355,7 +355,7 @@
      * Helper for drawRect when the caller doesn't need separate local rects or matrices.
      */
     void drawSimpleRect(const GrRect& rect, const SkMatrix* matrix = NULL) {
-        drawRect(rect, matrix, NULL, NULL);
+        this->drawRect(rect, matrix, NULL, NULL);
     }
     void drawSimpleRect(const GrIRect& irect, const SkMatrix* matrix = NULL) {
         SkRect rect = SkRect::MakeFromIRect(irect);
diff --git a/src/gpu/GrGpu.cpp b/src/gpu/GrGpu.cpp
index 0c58430..ae98b54 100644
--- a/src/gpu/GrGpu.cpp
+++ b/src/gpu/GrGpu.cpp
@@ -292,9 +292,9 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
-bool GrGpu::setupClipAndFlushState(DrawType type, const GrDeviceCoordTexture* dstCopy) {
-
-    if (!fClipMaskManager.setupClipping(this->getClip())) {
+bool GrGpu::setupClipAndFlushState(DrawType type, const GrDeviceCoordTexture* dstCopy,
+                                   GrDrawState::AutoRestoreEffects* are) {
+    if (!fClipMaskManager.setupClipping(this->getClip(), are)) {
         return false;
     }
 
@@ -336,8 +336,10 @@
 
 void GrGpu::onDraw(const DrawInfo& info) {
     this->handleDirtyContext();
+    GrDrawState::AutoRestoreEffects are;
     if (!this->setupClipAndFlushState(PrimTypeToDrawType(info.primitiveType()),
-                                      info.getDstCopy())) {
+                                      info.getDstCopy(),
+                                      &are)) {
         return;
     }
     this->onGpuDraw(info);
@@ -350,7 +352,8 @@
     GrAutoTRestore<GrStencilSettings> asr(this->drawState()->stencil());
 
     this->setStencilPathSettings(*path, fill, this->drawState()->stencil());
-    if (!this->setupClipAndFlushState(kStencilPath_DrawType, NULL)) {
+    GrDrawState::AutoRestoreEffects are;
+    if (!this->setupClipAndFlushState(kStencilPath_DrawType, NULL, &are)) {
         return;
     }
 
diff --git a/src/gpu/GrGpu.h b/src/gpu/GrGpu.h
index 5b556c5..3acfac2 100644
--- a/src/gpu/GrGpu.h
+++ b/src/gpu/GrGpu.h
@@ -365,7 +365,9 @@
     }
 
     // prepares clip flushes gpu state before a draw
-    bool setupClipAndFlushState(DrawType, const GrDeviceCoordTexture* dstCopy);
+    bool setupClipAndFlushState(DrawType,
+                                const GrDeviceCoordTexture* dstCopy,
+                                GrDrawState::AutoRestoreEffects* are);
 
     // Functions used to map clip-respecting stencil tests into normal
     // stencil funcs supported by GPUs.
diff --git a/src/gpu/GrOvalRenderer.cpp b/src/gpu/GrOvalRenderer.cpp
index 70b5c77..465dd2b 100644
--- a/src/gpu/GrOvalRenderer.cpp
+++ b/src/gpu/GrOvalRenderer.cpp
@@ -349,16 +349,10 @@
 
     SkStrokeRec::Style style = stroke.getStyle();
     bool isStroked = (SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style);
-    enum {
-        // the edge effects share this stage with glyph rendering
-        // (kGlyphMaskStage in GrTextContext) && SW path rendering
-        // (kPathMaskStage in GrSWMaskHelper)
-        kEdgeEffectStage = GrPaint::kTotalStages,
-    };
 
     GrEffectRef* effect = CircleEdgeEffect::Create(isStroked);
     static const int kCircleEdgeAttrIndex = 1;
-    drawState->setEffect(kEdgeEffectStage, effect, kCircleEdgeAttrIndex)->unref();
+    drawState->addCoverageEffect(effect, kCircleEdgeAttrIndex)->unref();
 
     SkScalar innerRadius = 0.0f;
     SkScalar outerRadius = radius;
@@ -509,18 +503,11 @@
 
     EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(geo.vertices());
 
-    enum {
-        // the edge effects share this stage with glyph rendering
-        // (kGlyphMaskStage in GrTextContext) && SW path rendering
-        // (kPathMaskStage in GrSWMaskHelper)
-        kEdgeEffectStage = GrPaint::kTotalStages,
-    };
     GrEffectRef* effect = EllipseEdgeEffect::Create(isStroked);
 
     static const int kEllipseCenterAttrIndex = 1;
     static const int kEllipseEdgeAttrIndex = 2;
-    drawState->setEffect(kEdgeEffectStage, effect,
-                         kEllipseCenterAttrIndex, kEllipseEdgeAttrIndex)->unref();
+    drawState->addCoverageEffect(effect, kEllipseCenterAttrIndex, kEllipseEdgeAttrIndex)->unref();
 
     // Compute the reciprocals of the radii here to save time in the shader
     SkScalar xRadRecip = SkScalarInvert(xRadius);
@@ -657,13 +644,6 @@
 
     bool isStroked = (SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style);
 
-    enum {
-        // the edge effects share this stage with glyph rendering
-        // (kGlyphMaskStage in GrTextContext) && SW path rendering
-        // (kPathMaskStage in GrSWMaskHelper)
-        kEdgeEffectStage = GrPaint::kTotalStages,
-    };
-
     GrIndexBuffer* indexBuffer = this->rRectIndexBuffer(context->getGpu());
     if (NULL == indexBuffer) {
         GrPrintf("Failed to create index buffer!\n");
@@ -702,7 +682,7 @@
 
         GrEffectRef* effect = CircleEdgeEffect::Create(isStroked);
         static const int kCircleEdgeAttrIndex = 1;
-        drawState->setEffect(kEdgeEffectStage, effect, kCircleEdgeAttrIndex)->unref();
+        drawState->addCoverageEffect(effect, kCircleEdgeAttrIndex)->unref();
 
         // The radii are outset for two reasons. First, it allows the shader to simply perform
         // clamp(distance-to-center - radius, 0, 1). Second, the outer radius is used to compute the
@@ -805,8 +785,8 @@
         GrEffectRef* effect = EllipseEdgeEffect::Create(isStroked);
         static const int kEllipseOffsetAttrIndex = 1;
         static const int kEllipseRadiiAttrIndex = 2;
-        drawState->setEffect(kEdgeEffectStage, effect,
-                             kEllipseOffsetAttrIndex, kEllipseRadiiAttrIndex)->unref();
+        drawState->addCoverageEffect(effect,
+                                     kEllipseOffsetAttrIndex, kEllipseRadiiAttrIndex)->unref();
 
         // Compute the reciprocals of the radii here to save time in the shader
         SkScalar xRadRecip = SkScalarInvert(xRadius);
diff --git a/src/gpu/GrSWMaskHelper.cpp b/src/gpu/GrSWMaskHelper.cpp
index fc34591..0fab55c 100644
--- a/src/gpu/GrSWMaskHelper.cpp
+++ b/src/gpu/GrSWMaskHelper.cpp
@@ -191,6 +191,7 @@
     if (!avmr.setIdentity(drawState)) {
         return;
     }
+    GrDrawState::AutoRestoreEffects are(drawState);
     enum {
         // the SW path renderer shares this stage with glyph
         // rendering (kGlyphMaskStage in GrTextContext)
@@ -213,13 +214,11 @@
     maskMatrix.preTranslate(SkIntToScalar(-rect.fLeft), SkIntToScalar(-rect.fTop));
     maskMatrix.preConcat(drawState->getViewMatrix());
 
-    GrAssert(!drawState->isStageEnabled(kPathMaskStage));
-    drawState->setEffect(kPathMaskStage,
+    drawState->addCoverageEffect(
                          GrSimpleTextureEffect::Create(texture,
                                                        maskMatrix,
                                                        false,
                                                        GrEffect::kPosition_CoordsType))->unref();
 
     target->drawSimpleRect(dstRect);
-    drawState->disableStage(kPathMaskStage);
 }
diff --git a/src/gpu/GrTextContext.cpp b/src/gpu/GrTextContext.cpp
index 19f18b5..65c5c7a 100644
--- a/src/gpu/GrTextContext.cpp
+++ b/src/gpu/GrTextContext.cpp
@@ -18,17 +18,15 @@
 #include "SkPath.h"
 #include "SkStrokeRec.h"
 
-// glyph rendering shares this stage with edge rendering (kEdgeEffectStage in GrContext) && SW path
-// rendering (kPathMaskStage in GrSWMaskHelper)
-static const int kGlyphMaskStage = GrPaint::kTotalStages;
 static const int kGlyphCoordsAttributeIndex = 1;
 
 void GrTextContext::flushGlyphs() {
     if (NULL == fDrawTarget) {
         return;
     }
-    GrDrawTarget::AutoStateRestore asr(fDrawTarget, GrDrawTarget::kPreserve_ASRInit);
+
     GrDrawState* drawState = fDrawTarget->drawState();
+    GrDrawState::AutoRestoreEffects are(drawState);
     drawState->setFromPaint(fPaint, SkMatrix::I(), fContext->getRenderTarget());
 
     if (fCurrVertex > 0) {
@@ -38,9 +36,9 @@
         GrTextureParams params(SkShader::kRepeat_TileMode, false);
 
         // This effect could be stored with one of the cache objects (atlas?)
-        drawState->setEffect(kGlyphMaskStage,
-                             GrSimpleTextureEffect::CreateWithCustomCoords(fCurrTexture, params),
-                             kGlyphCoordsAttributeIndex)->unref();
+        drawState->addCoverageEffect(
+                                GrSimpleTextureEffect::CreateWithCustomCoords(fCurrTexture, params),
+                                kGlyphCoordsAttributeIndex)->unref();
 
         if (!GrPixelConfigIsAlphaOnly(fCurrTexture->config())) {
             if (kOne_GrBlendCoeff != fPaint.getSrcBlendCoeff() ||
diff --git a/src/gpu/gl/GrGLProgramDesc.cpp b/src/gpu/gl/GrGLProgramDesc.cpp
index 4f00118..6b3008b 100644
--- a/src/gpu/gl/GrGLProgramDesc.cpp
+++ b/src/gpu/gl/GrGLProgramDesc.cpp
@@ -14,6 +14,24 @@
 
 #include "SkChecksum.h"
 
+namespace {
+inline GrGLEffect::EffectKey get_key_and_update_stats(const GrEffectStage& stage,
+                                                      const GrGLCaps& caps,
+                                                      bool useExplicitLocalCoords,
+                                                      bool* setTrueIfReadsDst,
+                                                      bool* setTrueIfReadsPos) {
+    const GrEffectRef& effect = *stage.getEffect();
+    const GrBackendEffectFactory& factory = effect->getFactory();
+    GrDrawEffect drawEffect(stage, useExplicitLocalCoords);
+    if (effect->willReadDstColor()) {
+        *setTrueIfReadsDst = true;
+    }
+    if (effect->willReadFragmentPosition()) {
+        *setTrueIfReadsPos = true;
+    }
+    return factory.glEffectKey(drawEffect, caps);
+}
+}
 void GrGLProgramDesc::Build(const GrDrawState& drawState,
                             bool isPoints,
                             GrDrawState::BlendOptFlags blendOpts,
@@ -50,27 +68,10 @@
     bool colorIsSolidWhite = (blendOpts & GrDrawState::kEmitCoverage_BlendOptFlag) ||
                              (!requiresColorAttrib && 0xffffffff == drawState.getColor());
 
-    // Do an initial loop over the stages to count them. We count the color and coverage effects
-    // separately here. Later we may decide the distinction doesn't matter and will count all
-    // effects as color in desc. Two things will allow simplication of this mess: GrDrawState will
-    // have tight lists of color and coverage stages rather than a fixed size array with NULLS and
-    // the xfermode-color filter will be removed.
-    if (!skipColor) {
-        for (int s = 0; s < drawState.getFirstCoverageStage(); ++s) {
-            if (drawState.isStageEnabled(s)) {
-                colorStages->push_back(&drawState.getStage(s));
-            }
-        }
-    }
-    if (!skipCoverage) {
-        for (int s = drawState.getFirstCoverageStage(); s < GrDrawState::kNumStages; ++s) {
-            if (drawState.isStageEnabled(s)) {
-                coverageStages->push_back(&drawState.getStage(s));
-            }
-        }
-    }
+    int numEffects = (skipColor ? 0 : drawState.numColorStages()) +
+                     (skipCoverage ? 0 : drawState.numCoverageStages());
 
-    size_t newKeyLength = KeyLength(colorStages->count() + coverageStages->count());
+    size_t newKeyLength = KeyLength(numEffects);
     bool allocChanged;
     desc->fKey.reset(newKeyLength, SkAutoMalloc::kAlloc_OnShrink, &allocChanged);
     if (allocChanged || !desc->fInitialized) {
@@ -86,20 +87,18 @@
     int currEffectKey = 0;
     bool readsDst = false;
     bool readFragPosition = false;
-    for (int s = 0; s < GrDrawState::kNumStages; ++s) {
-        bool skip = s < drawState.getFirstCoverageStage() ? skipColor : skipCoverage;
-        if (!skip && drawState.isStageEnabled(s)) {
-            const GrEffectRef& effect = *drawState.getStage(s).getEffect();
-            const GrBackendEffectFactory& factory = effect->getFactory();
-            GrDrawEffect drawEffect(drawState.getStage(s), requiresLocalCoordAttrib);
-            effectKeys[currEffectKey] = factory.glEffectKey(drawEffect, gpu->glCaps());
-            ++currEffectKey;
-            if (effect->willReadDstColor()) {
-                readsDst = true;
-            }
-            if (effect->willReadFragmentPosition()) {
-                readFragPosition = true;
-            }
+    if (!skipColor) {
+        for (int s = 0; s < drawState.numColorStages(); ++s) {
+            effectKeys[currEffectKey++] = 
+                get_key_and_update_stats(drawState.getColorStage(s), gpu->glCaps(),
+                                         requiresLocalCoordAttrib, &readsDst, &readFragPosition);
+        }
+    }
+    if (!skipCoverage) {
+        for (int s = 0; s < drawState.numCoverageStages(); ++s) {
+            effectKeys[currEffectKey++] = 
+                get_key_and_update_stats(drawState.getCoverageStage(s), gpu->glCaps(),
+                                         requiresLocalCoordAttrib, &readsDst, &readFragPosition);
         }
     }
 
@@ -189,7 +188,8 @@
 
     // If we do have coverage determine whether it matters.
     bool separateCoverageFromColor = false;
-    if (!drawState.isCoverageDrawing() && (coverageStages->count() > 0 || requiresCoverageAttrib)) {
+    if (!drawState.isCoverageDrawing() && !skipCoverage &&
+        (drawState.numCoverageStages() > 0 || requiresCoverageAttrib)) {
         // color filter is applied between color/coverage computation
         if (SkXfermode::kDst_Mode != header->fColorFilterXfermode) {
             separateCoverageFromColor = true;
@@ -224,14 +224,24 @@
             separateCoverageFromColor = true;
         }
     }
-    if (separateCoverageFromColor) {
-        header->fColorEffectCnt = colorStages->count();
-        header->fCoverageEffectCnt = coverageStages->count();
-    } else {
-        header->fColorEffectCnt = colorStages->count() + coverageStages->count();
-        header->fCoverageEffectCnt = 0;
-        colorStages->push_back_n(coverageStages->count(), coverageStages->begin());
-        coverageStages->reset();
+    if (!skipColor) {
+        for (int s = 0; s < drawState.numColorStages(); ++s) {
+            colorStages->push_back(&drawState.getColorStage(s));
+        }
+        header->fColorEffectCnt = drawState.numColorStages();
+    }
+    if (!skipCoverage) {
+        SkTArray<const GrEffectStage*, true>* array;
+        if (separateCoverageFromColor) {
+            array = coverageStages;
+            header->fCoverageEffectCnt = drawState.numCoverageStages();
+        } else {
+            array = colorStages;
+            header->fColorEffectCnt += drawState.numCoverageStages();
+        }
+        for (int s = 0; s < drawState.numCoverageStages(); ++s) {
+            array->push_back(&drawState.getCoverageStage(s));
+        }
     }
 
     *desc->checksum() = 0;
diff --git a/src/gpu/gl/debug/GrFrameBufferObj.cpp b/src/gpu/gl/debug/GrFrameBufferObj.cpp
index 24ab120..7dc12ac 100644
--- a/src/gpu/gl/debug/GrFrameBufferObj.cpp
+++ b/src/gpu/gl/debug/GrFrameBufferObj.cpp
@@ -53,7 +53,7 @@
         GrAlwaysAssert(fStencilBuffer->getStencilBound(this));
         fStencilBuffer->resetStencilBound(this);
 
-        GrAlwaysAssert(!fStencilBuffer->getDeleted());
+        //GrAlwaysAssert(!fStencilBuffer->getDeleted());
         fStencilBuffer->unref();
     }
     fStencilBuffer = buffer;