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/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.