Opt state takes a GP instead of a GeometryStage

BUG=skia:

Committed: https://skia.googlesource.com/skia/+/71856d520461ae025a0332aa0ce9735a096d9baf

Review URL: https://codereview.chromium.org/637003003
diff --git a/include/gpu/GrProcessor.h b/include/gpu/GrProcessor.h
index c1755a8..a5c8341 100644
--- a/include/gpu/GrProcessor.h
+++ b/include/gpu/GrProcessor.h
@@ -124,12 +124,6 @@
         in generated shader code. */
     const char* name() const;
 
-    int numTransforms() const { return fCoordTransforms.count(); }
-
-    /** Returns the coordinate transformation at index. index must be valid according to
-        numTransforms(). */
-    const GrCoordTransform& coordTransform(int index) const { return *fCoordTransforms[index]; }
-
     int numTextures() const { return fTextureAccesses.count(); }
 
     /** Returns the access pattern for the texture at index. index must be valid according to
@@ -159,16 +153,6 @@
 
 protected:
     /**
-     * Subclasses call this from their constructor to register coordinate transformations. The
-     * effect subclass manages the lifetime of the transformations (this function only stores a
-     * pointer). The GrCoordTransform is typically a member field of the GrProcessor subclass. When
-     * the matrix has perspective, the transformed coordinates will have 3 components. Otherwise
-     * they'll have 2. This must only be called from the constructor because GrProcessors are
-     * immutable.
-     */
-    void addCoordTransform(const GrCoordTransform* coordTransform);
-
-    /**
      * Subclasses call this from their constructor to register GrTextureAccesses. The effect
      * subclass manages the lifetime of the accesses (this function only stores a pointer). The
      * GrTextureAccess is typically a member field of the GrProcessor subclass. This must only be
@@ -198,9 +182,7 @@
      * Subclass implements this to support getConstantColorComponents(...).
      */
     virtual void onComputeInvariantOutput(InvariantOutput* inout) const = 0;
-    friend class GrGeometryProcessor; // to set fRequiresVertexShader and build fVertexAttribTypes.
 
-    SkSTArray<4, const GrCoordTransform*, true>  fCoordTransforms;
     SkSTArray<4, const GrTextureAccess*, true>   fTextureAccesses;
     bool                                         fWillReadFragmentPosition;
 
@@ -216,6 +198,12 @@
 
     virtual const GrBackendFragmentProcessorFactory& getFactory() const = 0;
 
+    int numTransforms() const { return fCoordTransforms.count(); }
+
+    /** Returns the coordinate transformation at index. index must be valid according to
+        numTransforms(). */
+    const GrCoordTransform& coordTransform(int index) const { return *fCoordTransforms[index]; }
+
     /** Will this effect read the destination pixel value? */
     bool willReadDstColor() const { return fWillReadDstColor; }
 
@@ -224,6 +212,16 @@
 
 protected:
     /**
+     * Fragment Processor subclasses call this from their constructor to register coordinate
+     * transformations. The processor subclass manages the lifetime of the transformations (this
+     * function only stores a pointer). The GrCoordTransform is typically a member field of the
+     * GrProcessor subclass. When the matrix has perspective, the transformed coordinates will have
+     * 3 components. Otherwise they'll have 2. This must only be called from the constructor because
+     * GrProcessors are immutable.
+     */
+    void addCoordTransform(const GrCoordTransform*);
+
+    /**
      * If the effect subclass will read the destination pixel value then it must call this function
      * from its constructor. Otherwise, when its generated backend-specific effect class attempts
      * to generate code that reads the destination pixel it will fail.
@@ -238,6 +236,7 @@
     void setWillNotUseInputColor() { fWillUseInputColor = false; }
 
 private:
+    SkSTArray<4, const GrCoordTransform*, true>  fCoordTransforms;
     bool                                         fWillReadDstColor;
     bool                                         fWillUseInputColor;
 
diff --git a/include/gpu/GrProcessorStage.h b/include/gpu/GrProcessorStage.h
index 5cc06e7..c961572 100644
--- a/include/gpu/GrProcessorStage.h
+++ b/include/gpu/GrProcessorStage.h
@@ -24,16 +24,14 @@
 // is immutable, and only owns pending execution refs. This requries removing the common base
 // class from GrDrawState and GrOptDrawState called GrRODrawState and converting to GrOptDrawState
 // when draws are enqueued in the GrInOrderDrawBuffer.
-class GrProcessorStage {
+class GrFragmentStage {
 public:
-    explicit GrProcessorStage(const GrProcessor* proc)
+    explicit GrFragmentStage(const GrFragmentProcessor* proc)
     : fProc(SkRef(proc)) {
         fCoordChangeMatrixSet = false;
     }
 
-    virtual ~GrProcessorStage() {}
-
-    GrProcessorStage(const GrProcessorStage& other) {
+    GrFragmentStage(const GrFragmentStage& other) {
         fCoordChangeMatrixSet = other.fCoordChangeMatrixSet;
         if (other.fCoordChangeMatrixSet) {
             fCoordChangeMatrix = other.fCoordChangeMatrix;
@@ -41,7 +39,7 @@
         fProc.initAndRef(other.fProc);
     }
     
-    static bool AreCompatible(const GrProcessorStage& a, const GrProcessorStage& b,
+    static bool AreCompatible(const GrFragmentStage& a, const GrFragmentStage& b,
                               bool usingExplicitLocalCoords) {
         SkASSERT(a.fProc.get());
         SkASSERT(b.fProc.get());
@@ -90,7 +88,7 @@
         SkMatrix fCoordChangeMatrix;
         SkDEBUGCODE(mutable uint32_t fEffectUniqueID;)
 
-        friend class GrProcessorStage;
+        friend class GrFragmentStage;
     };
 
     /**
@@ -149,38 +147,14 @@
         }
     }
 
-    virtual const GrProcessor* getProcessor() const = 0;
+    const GrFragmentProcessor* getProcessor() const { return fProc.get(); }
 
     void convertToPendingExec() { fProc.convertToPendingExec(); }
 
 protected:
-    bool                                   fCoordChangeMatrixSet;
-    SkMatrix                               fCoordChangeMatrix;
-    GrProgramElementRef<const GrProcessor> fProc;
-};
-
-class GrFragmentStage : public GrProcessorStage {
-public:
-    GrFragmentStage(const GrFragmentProcessor* fp) : GrProcessorStage(fp) {}
-
-    virtual const GrFragmentProcessor* getProcessor() const {
-        return static_cast<const GrFragmentProcessor*>(fProc.get());
-    }
-
-    typedef GrFragmentProcessor   Processor;
-    typedef GrGLFragmentProcessor GLProcessor;
-};
-
-class GrGeometryStage : public GrProcessorStage {
-public:
-    GrGeometryStage(const GrGeometryProcessor* gp) : GrProcessorStage(gp) {}
-
-    virtual const GrGeometryProcessor* getProcessor() const {
-        return static_cast<const GrGeometryProcessor*>(fProc.get());
-    }
-
-    typedef GrGeometryProcessor   Processor;
-    typedef GrGLGeometryProcessor GLProcessor;
+    bool                                           fCoordChangeMatrixSet;
+    SkMatrix                                       fCoordChangeMatrix;
+    GrProgramElementRef<const GrFragmentProcessor> fProc;
 };
 
 #endif
diff --git a/src/effects/gradients/SkGradientShaderPriv.h b/src/effects/gradients/SkGradientShaderPriv.h
index d1ac77b..b3ad6f4 100644
--- a/src/effects/gradients/SkGradientShaderPriv.h
+++ b/src/effects/gradients/SkGradientShaderPriv.h
@@ -300,7 +300,7 @@
 #include "GrCoordTransform.h"
 #include "gl/GrGLProcessor.h"
 
-class GrProcessorStage;
+class GrFragmentStage;
 class GrBackendProcessorFactory;
 
 /*
diff --git a/src/gpu/GrDrawState.cpp b/src/gpu/GrDrawState.cpp
index 288fa12..e1fa6da 100644
--- a/src/gpu/GrDrawState.cpp
+++ b/src/gpu/GrDrawState.cpp
@@ -43,9 +43,7 @@
     if (this->hasGeometryProcessor()) {
         if (!that.hasGeometryProcessor()) {
             return false;
-        } else if (!GrProcessorStage::AreCompatible(*this->getGeometryProcessor(),
-                                                    *that.getGeometryProcessor(),
-                                                    explicitLocalCoords)) {
+        } else if (!this->getGeometryProcessor()->isEqual(*that.getGeometryProcessor())) {
             return false;
         }
     } else if (that.hasGeometryProcessor()) {
@@ -53,13 +51,13 @@
     }
 
     for (int i = 0; i < this->numColorStages(); i++) {
-        if (!GrProcessorStage::AreCompatible(this->getColorStage(i), that.getColorStage(i),
+        if (!GrFragmentStage::AreCompatible(this->getColorStage(i), that.getColorStage(i),
                                              explicitLocalCoords)) {
             return false;
         }
     }
     for (int i = 0; i < this->numCoverageStages(); i++) {
-        if (!GrProcessorStage::AreCompatible(this->getCoverageStage(i), that.getCoverageStage(i),
+        if (!GrFragmentStage::AreCompatible(this->getCoverageStage(i), that.getCoverageStage(i),
                                              explicitLocalCoords)) {
             return false;
         }
@@ -116,9 +114,6 @@
     SkDEBUGCODE(fBlockEffectRemovalCnt = 0;)
     *this = state;
     if (!preConcatMatrix.isIdentity()) {
-        if (this->hasGeometryProcessor()) {
-            fGeometryProcessor->localCoordChange(preConcatMatrix);
-        }
         for (int i = 0; i < this->numColorStages(); ++i) {
             fColorStages[i].localCoordChange(preConcatMatrix);
         }
@@ -147,7 +142,7 @@
     fCoverage = that.fCoverage;
     fDrawFace = that.fDrawFace;
     if (that.hasGeometryProcessor()) {
-        fGeometryProcessor.reset(SkNEW_ARGS(GrGeometryStage, (*that.fGeometryProcessor.get())));
+        fGeometryProcessor.initAndRef(that.fGeometryProcessor);
     } else {
         fGeometryProcessor.reset(NULL);
     }
@@ -202,9 +197,6 @@
             // sad trombone sound
             return false;
         }
-        if (this->hasGeometryProcessor()) {
-            fGeometryProcessor->localCoordChange(invVM);
-        }
         for (int s = 0; s < this->numColorStages(); ++s) {
             fColorStages[s].localCoordChange(invVM);
         }
@@ -265,9 +257,7 @@
     }
 
     if (this->hasGeometryProcessor()) {
-        const GrGeometryStage& stage = *this->getGeometryProcessor();
-        const GrGeometryProcessor* gp = stage.getProcessor();
-        SkASSERT(gp);
+        const GrGeometryProcessor* gp = this->getGeometryProcessor();
         // 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.
@@ -410,8 +400,7 @@
 
     // Run through the coverage stages and see if the coverage will be all ones at the end.
     if (this->hasGeometryProcessor()) {
-        const GrGeometryProcessor* gp = fGeometryProcessor->getProcessor();
-        gp->computeInvariantOutput(&inout);
+        fGeometryProcessor->computeInvariantOutput(&inout);
     }
 
     for (int s = 0; s < this->numCoverageStages(); ++s) {
@@ -456,7 +445,7 @@
         if (SK_InvalidUniqueID == fOriginalGPID) {
             fDrawState->fGeometryProcessor.reset(NULL);
         } else {
-            SkASSERT(fDrawState->getGeometryProcessor()->getProcessor()->getUniqueID() ==
+            SkASSERT(fDrawState->getGeometryProcessor()->getUniqueID() ==
                      fOriginalGPID);
             fOriginalGPID = SK_InvalidUniqueID;
         }
@@ -477,7 +466,7 @@
     if (NULL != ds) {
         SkASSERT(SK_InvalidUniqueID == fOriginalGPID);
         if (NULL != ds->getGeometryProcessor()) {
-            fOriginalGPID = ds->getGeometryProcessor()->getProcessor()->getUniqueID();
+            fOriginalGPID = ds->getGeometryProcessor()->getUniqueID();
         }
         fColorEffectCnt = ds->numColorStages();
         fCoverageEffectCnt = ds->numCoverageStages();
@@ -515,14 +504,9 @@
         fDrawState->fViewMatrix = fViewMatrix;
         SkASSERT(fDrawState->numColorStages() >= fNumColorStages);
         int numCoverageStages = fSavedCoordChanges.count() - fNumColorStages;
-        numCoverageStages -= fHasGeometryProcessor ? 1 : 0;
         SkASSERT(fDrawState->numCoverageStages() >= numCoverageStages);
 
         int i = 0;
-        if (fHasGeometryProcessor) {
-            SkASSERT(fDrawState->hasGeometryProcessor());
-            fDrawState->fGeometryProcessor->restoreCoordChange(fSavedCoordChanges[i++]);
-        }
         for (int s = 0; s < fNumColorStages; ++s, ++i) {
             fDrawState->fColorStages[s].restoreCoordChange(fSavedCoordChanges[i]);
         }
@@ -568,7 +552,6 @@
     if (0 == drawState->numTotalStages()) {
         drawState->fViewMatrix.reset();
         fDrawState = drawState;
-        fHasGeometryProcessor = false;
         fNumColorStages = 0;
         fSavedCoordChanges.reset(0);
         SkDEBUGCODE(++fDrawState->fBlockEffectRemovalCnt;)
@@ -590,13 +573,6 @@
     fSavedCoordChanges.reset(fDrawState->numTotalStages());
     int i = 0;
 
-    fHasGeometryProcessor = false;
-    if (fDrawState->hasGeometryProcessor()) {
-        fDrawState->fGeometryProcessor->saveCoordChange(&fSavedCoordChanges[i++]);
-        fDrawState->fGeometryProcessor->localCoordChange(coordChangeMatrix);
-        fHasGeometryProcessor = true;
-    }
-
     fNumColorStages = fDrawState->numColorStages();
     for (int s = 0; s < fNumColorStages; ++s, ++i) {
         fDrawState->getColorStage(s).saveCoordChange(&fSavedCoordChanges[i]);
@@ -619,7 +595,7 @@
         fColorStages[i].convertToPendingExec();
     }
     if (fGeometryProcessor) {
-        fGeometryProcessor->convertToPendingExec();
+        fGeometryProcessor.convertToPendingExec();
     }
     for (int i = 0; i < fCoverageStages.count(); ++i) {
         fCoverageStages[i].convertToPendingExec();
diff --git a/src/gpu/GrDrawState.h b/src/gpu/GrDrawState.h
index 03af7b5..d58d8c6 100644
--- a/src/gpu/GrDrawState.h
+++ b/src/gpu/GrDrawState.h
@@ -221,7 +221,7 @@
     const GrGeometryProcessor* setGeometryProcessor(const GrGeometryProcessor* geometryProcessor) {
         SkASSERT(geometryProcessor);
         SkASSERT(!this->hasGeometryProcessor());
-        fGeometryProcessor.reset(new GrGeometryStage(geometryProcessor));
+        fGeometryProcessor.reset(SkRef(geometryProcessor));
         this->invalidateOptState();
         return geometryProcessor;
     }
@@ -254,7 +254,7 @@
     }
 
     bool hasGeometryProcessor() const { return SkToBool(fGeometryProcessor.get()); }
-    const GrGeometryStage* getGeometryProcessor() const { return fGeometryProcessor.get(); }
+    const GrGeometryProcessor* getGeometryProcessor() const { return fGeometryProcessor.get(); }
     const GrFragmentStage& getColorStage(int idx) const { return fColorStages[idx]; }
     const GrFragmentStage& getCoverageStage(int idx) const { return fCoverageStages[idx]; }
 
@@ -485,8 +485,7 @@
         GrDrawState*                                           fDrawState;
         SkMatrix                                               fViewMatrix;
         int                                                    fNumColorStages;
-        bool                                                   fHasGeometryProcessor;
-        SkAutoSTArray<8, GrProcessorStage::SavedCoordChange>   fSavedCoordChanges;
+        SkAutoSTArray<8, GrFragmentStage::SavedCoordChange>    fSavedCoordChanges;
     };
 
     /// @}
@@ -810,10 +809,11 @@
     GrBlendCoeff                        fSrcBlend;
     GrBlendCoeff                        fDstBlend;
 
-    typedef SkSTArray<4, GrFragmentStage>   FragmentStageArray;
-    SkAutoTDelete<GrGeometryStage>          fGeometryProcessor;
-    FragmentStageArray                      fColorStages;
-    FragmentStageArray                      fCoverageStages;
+    typedef SkSTArray<4, GrFragmentStage> FragmentStageArray;
+    typedef GrProgramElementRef<const GrGeometryProcessor> ProgramGeometryProcessor;
+    ProgramGeometryProcessor            fGeometryProcessor;
+    FragmentStageArray                  fColorStages;
+    FragmentStageArray                  fCoverageStages;
 
     uint32_t                            fHints;
 
diff --git a/src/gpu/GrDrawTarget.cpp b/src/gpu/GrDrawTarget.cpp
index 7265f4d..b144caf 100644
--- a/src/gpu/GrDrawTarget.cpp
+++ b/src/gpu/GrDrawTarget.cpp
@@ -391,7 +391,7 @@
     SkASSERT(drawState.getRenderTarget());
 
     if (drawState.hasGeometryProcessor()) {
-        const GrGeometryProcessor* gp = drawState.getGeometryProcessor()->getProcessor();
+        const GrGeometryProcessor* gp = drawState.getGeometryProcessor();
         int numTextures = gp->numTextures();
         for (int t = 0; t < numTextures; ++t) {
             GrTexture* texture = gp->texture(t);
diff --git a/src/gpu/GrOptDrawState.cpp b/src/gpu/GrOptDrawState.cpp
index b3d90f7..7c8710f 100644
--- a/src/gpu/GrOptDrawState.cpp
+++ b/src/gpu/GrOptDrawState.cpp
@@ -54,7 +54,7 @@
 
     // Copy GeometryProcesssor from DS or ODS
     if (drawState.hasGeometryProcessor()) {
-        fGeometryProcessor.reset(SkNEW_ARGS(GrGeometryStage, (*drawState.getGeometryProcessor())));
+        fGeometryProcessor.initAndRef(drawState.fGeometryProcessor);
     } else {
         fGeometryProcessor.reset(NULL);
     }
@@ -305,8 +305,8 @@
         get_stage_stats(stage, &fReadsDst, &fReadsFragPosition);
     }
     if (ds.hasGeometryProcessor()) {
-        const GrGeometryStage& stage = *ds.getGeometryProcessor();
-        fReadsFragPosition = fReadsFragPosition || stage.getProcessor()->willReadFragmentPosition();
+        const GrGeometryProcessor& gp = *ds.getGeometryProcessor();
+        fReadsFragPosition = fReadsFragPosition || gp.willReadFragmentPosition();
     }
 }
 
@@ -354,9 +354,7 @@
     if (this->hasGeometryProcessor()) {
         if (!that.hasGeometryProcessor()) {
             return false;
-        } else if (!GrProcessorStage::AreCompatible(*this->getGeometryProcessor(),
-                                                    *that.getGeometryProcessor(),
-                                                    explicitLocalCoords)) {
+        } else if (!this->getGeometryProcessor()->isEqual(*that.getGeometryProcessor())) {
             return false;
         }
     } else if (that.hasGeometryProcessor()) {
@@ -364,8 +362,8 @@
     }
 
     for (int i = 0; i < this->numFragmentStages(); i++) {
-        if (!GrProcessorStage::AreCompatible(this->getFragmentStage(i), that.getFragmentStage(i),
-                                             explicitLocalCoords)) {
+        if (!GrFragmentStage::AreCompatible(this->getFragmentStage(i), that.getFragmentStage(i),
+                                            explicitLocalCoords)) {
             return false;
         }
     }
diff --git a/src/gpu/GrOptDrawState.h b/src/gpu/GrOptDrawState.h
index 1c439bb..f47913a 100644
--- a/src/gpu/GrOptDrawState.h
+++ b/src/gpu/GrOptDrawState.h
@@ -127,7 +127,7 @@
     }
 
     bool hasGeometryProcessor() const { return SkToBool(fGeometryProcessor.get()); }
-    const GrGeometryStage* getGeometryProcessor() const { return fGeometryProcessor.get(); }
+    const GrGeometryProcessor* getGeometryProcessor() const { return fGeometryProcessor.get(); }
     const GrFragmentStage& getColorStage(int idx) const {
         SkASSERT(idx < this->numColorStages());
         return fFragmentStages[idx];
@@ -441,11 +441,12 @@
     GrBlendCoeff                        fDstBlend;
 
     typedef SkSTArray<8, GrFragmentStage> FragmentStageArray;
-    SkAutoTDelete<GrGeometryStage>        fGeometryProcessor;
-    FragmentStageArray                    fFragmentStages;
+    typedef GrProgramElementRef<const GrGeometryProcessor> ProgramGeometryProcessor;
+    ProgramGeometryProcessor            fGeometryProcessor;
+    FragmentStageArray                  fFragmentStages;
 
     // This function is equivalent to the offset into fFragmentStages where coverage stages begin.
-    int                                   fNumColorStages;
+    int                                 fNumColorStages;
 
     // This is simply a different representation of info in fVertexAttribs and thus does
     // not need to be compared in op==.
diff --git a/src/gpu/GrProcessor.cpp b/src/gpu/GrProcessor.cpp
index 71dfaae..1732e3a 100644
--- a/src/gpu/GrProcessor.cpp
+++ b/src/gpu/GrProcessor.cpp
@@ -103,11 +103,6 @@
     return this->getFactory().name();
 }
 
-void GrProcessor::addCoordTransform(const GrCoordTransform* transform) {
-    fCoordTransforms.push_back(transform);
-    SkDEBUGCODE(transform->setInEffect();)
-}
-
 void GrProcessor::addTextureAccess(const GrTextureAccess* access) {
     fTextureAccesses.push_back(access);
     this->addGpuResource(access->getProgramTexture());
@@ -123,10 +118,6 @@
 
 #ifdef SK_DEBUG
 void GrProcessor::assertEquality(const GrProcessor& other) const {
-    SkASSERT(this->numTransforms() == other.numTransforms());
-    for (int i = 0; i < this->numTransforms(); ++i) {
-        SkASSERT(this->coordTransform(i) == other.coordTransform(i));
-    }
     SkASSERT(this->numTextures() == other.numTextures());
     for (int i = 0; i < this->numTextures(); ++i) {
         SkASSERT(this->textureAccess(i) == other.textureAccess(i));
@@ -173,5 +164,11 @@
     }
     return true;
 }
-#endif
+#endif // end DEBUG
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+void GrFragmentProcessor::addCoordTransform(const GrCoordTransform* transform) {
+    fCoordTransforms.push_back(transform);
+    SkDEBUGCODE(transform->setInEffect();)
+}
diff --git a/src/gpu/effects/GrConfigConversionEffect.h b/src/gpu/effects/GrConfigConversionEffect.h
index aa2c874..8c65dce 100644
--- a/src/gpu/effects/GrConfigConversionEffect.h
+++ b/src/gpu/effects/GrConfigConversionEffect.h
@@ -10,7 +10,7 @@
 
 #include "GrSingleTextureEffect.h"
 
-class GrProcessorStage;
+class GrFragmentStage;
 class GrGLConfigConversionEffect;
 
 /**
diff --git a/src/gpu/gl/GrGLGeometryProcessor.h b/src/gpu/gl/GrGLGeometryProcessor.h
index ce1c7c9..e50f598 100644
--- a/src/gpu/gl/GrGLGeometryProcessor.h
+++ b/src/gpu/gl/GrGLGeometryProcessor.h
@@ -10,6 +10,8 @@
 
 #include "GrGLProcessor.h"
 
+class GrGLGPBuilder;
+
 /**
  * If a GL effect needs a GrGLFullShaderBuilder* object to emit vertex code, then it must inherit
  * from this class. Since paths don't have vertices, this class is only meant to be used internally
diff --git a/src/gpu/gl/GrGLProcessor.h b/src/gpu/gl/GrGLProcessor.h
index 5f69832..0487077 100644
--- a/src/gpu/gl/GrGLProcessor.h
+++ b/src/gpu/gl/GrGLProcessor.h
@@ -114,6 +114,7 @@
         @param samplers     Contains one entry for each GrTextureAccess of the GrProcessor. These
                             can be passed to the builder to emit texture reads in the generated
                             code.
+        TODO this should take a struct
         */
     virtual void emitCode(GrGLFPBuilder* builder,
                           const GrFragmentProcessor& effect,
diff --git a/src/gpu/gl/GrGLProgram.cpp b/src/gpu/gl/GrGLProgram.cpp
index e0d4939..2d9b569 100644
--- a/src/gpu/gl/GrGLProgram.cpp
+++ b/src/gpu/gl/GrGLProgram.cpp
@@ -10,6 +10,7 @@
 #include "GrAllocator.h"
 #include "GrProcessor.h"
 #include "GrCoordTransform.h"
+#include "GrGLGeometryProcessor.h"
 #include "GrGLProcessor.h"
 #include "GrGpuGL.h"
 #include "GrGLPathRendering.h"
@@ -24,7 +25,7 @@
 /**
  * Retrieves the final matrix that a transform needs to apply to its source coords.
  */
-static SkMatrix get_transform_matrix(const GrProcessorStage& processorStage,
+static SkMatrix get_transform_matrix(const GrFragmentStage& processorStage,
                                      bool useExplicitLocalCoords,
                                      int transformIdx) {
     const GrCoordTransform& coordTransform =
@@ -59,17 +60,15 @@
                          const BuiltinUniformHandles& builtinUniforms,
                          GrGLuint programID,
                          const UniformInfoArray& uniforms,
-                         GrGLInstalledProcessors* geometryProcessor,
-                         GrGLInstalledProcessors* colorProcessors,
-                         GrGLInstalledProcessors* coverageProcessors)
+                         GrGLInstalledGeoProc* geometryProcessor,
+                         GrGLInstalledFragProcs* fragmentProcessors)
     : fColor(GrColor_ILLEGAL)
     , fCoverage(GrColor_ILLEGAL)
     , fDstCopyTexUnit(-1)
     , fBuiltinUniformHandles(builtinUniforms)
     , fProgramID(programID)
-    , fGeometryProcessor(SkSafeRef(geometryProcessor))
-    , fColorEffects(SkRef(colorProcessors))
-    , fCoverageEffects(SkRef(coverageProcessors))
+    , fGeometryProcessor(geometryProcessor)
+    , fFragmentProcessors(SkRef(fragmentProcessors))
     , fDesc(desc)
     , fGpu(gpu)
     , fProgramDataManager(gpu, uniforms) {
@@ -96,28 +95,24 @@
     if (fGeometryProcessor.get()) {
         this->initSamplers(fGeometryProcessor.get(), &texUnitIdx);
     }
-    this->initSamplers(fColorEffects.get(), &texUnitIdx);
-    this->initSamplers(fCoverageEffects.get(), &texUnitIdx);
-}
-
-void GrGLProgram::initSamplers(GrGLInstalledProcessors* ip, int* texUnitIdx) {
-    int numEffects = ip->fGLProcessors.count();
-    SkASSERT(numEffects == ip->fSamplers.count());
-    for (int e = 0; e < numEffects; ++e) {
-        SkTArray<GrGLInstalledProcessors::Sampler, true>& samplers = ip->fSamplers[e];
-        int numSamplers = samplers.count();
-        for (int s = 0; s < numSamplers; ++s) {
-            SkASSERT(samplers[s].fUniform.isValid());
-            fProgramDataManager.setSampler(samplers[s].fUniform, *texUnitIdx);
-            samplers[s].fTextureUnit = (*texUnitIdx)++;
-        }
+    int numProcs = fFragmentProcessors->fProcs.count();
+    for (int i = 0; i < numProcs; i++) {
+        this->initSamplers(fFragmentProcessors->fProcs[i], &texUnitIdx);
     }
 }
 
-void GrGLProgram::bindTextures(const GrGLInstalledProcessors* ip,
-                               const GrProcessor& processor,
-                               int effectIdx) {
-    const SkTArray<GrGLInstalledProcessors::Sampler, true>& samplers = ip->fSamplers[effectIdx];
+void GrGLProgram::initSamplers(GrGLInstalledProc* ip, int* texUnitIdx) {
+    SkTArray<GrGLInstalledProc::Sampler, true>& samplers = ip->fSamplers;
+    int numSamplers = samplers.count();
+    for (int s = 0; s < numSamplers; ++s) {
+        SkASSERT(samplers[s].fUniform.isValid());
+        fProgramDataManager.setSampler(samplers[s].fUniform, *texUnitIdx);
+        samplers[s].fTextureUnit = (*texUnitIdx)++;
+    }
+}
+
+void GrGLProgram::bindTextures(const GrGLInstalledProc* ip, const GrProcessor& processor) {
+    const SkTArray<GrGLInstalledProc::Sampler, true>& samplers = ip->fSamplers;
     int numSamplers = samplers.count();
     SkASSERT(numSamplers == processor.numTextures());
     for (int s = 0; s < numSamplers; ++s) {
@@ -134,9 +129,6 @@
 
 void GrGLProgram::setData(const GrOptDrawState& optState,
                           GrGpu::DrawType drawType,
-                          const GrGeometryStage* geometryProcessor,
-                          const GrFragmentStage* colorStages[],
-                          const GrFragmentStage* coverageStages[],
                           const GrDeviceCoordTexture* dstCopy,
                           SharedGLState* sharedState) {
     GrColor color = optState.getColor();
@@ -170,25 +162,34 @@
     // we set the textures, and uniforms for installed processors in a generic way, but subclasses
     // of GLProgram determine how to set coord transforms
     if (fGeometryProcessor.get()) {
-        SkASSERT(geometryProcessor);
-        this->setData<GrGeometryStage>(&geometryProcessor, fGeometryProcessor.get());
+        SkASSERT(optState.hasGeometryProcessor());
+        const GrGeometryProcessor& gp = *optState.getGeometryProcessor();
+        fGeometryProcessor->fGLProc->setData(fProgramDataManager, gp);
+        this->bindTextures(fGeometryProcessor, gp);
     }
-    this->setData<GrFragmentStage>(colorStages, fColorEffects.get());
-    this->setData<GrFragmentStage>(coverageStages, fCoverageEffects.get());
+    this->setFragmentData(optState);
 
     // Some of GrGLProgram subclasses need to update state here
     this->didSetData(drawType);
 }
 
-void GrGLProgram::setTransformData(const GrProcessorStage& processor,
-                                   int effectIdx,
-                                   GrGLInstalledProcessors* ip) {
-    SkTArray<GrGLInstalledProcessors::Transform, true>& transforms = ip->fTransforms[effectIdx];
+void GrGLProgram::setFragmentData(const GrOptDrawState& optState) {
+    int numProcessors = fFragmentProcessors->fProcs.count();
+    for (int e = 0; e < numProcessors; ++e) {
+        const GrFragmentStage& stage = optState.getFragmentStage(e);
+        const GrProcessor& processor = *stage.getProcessor();
+        fFragmentProcessors->fProcs[e]->fGLProc->setData(fProgramDataManager, processor);
+        this->setTransformData(stage, fFragmentProcessors->fProcs[e]);
+        this->bindTextures(fFragmentProcessors->fProcs[e], processor);
+    }
+}
+void GrGLProgram::setTransformData(const GrFragmentStage& processor, GrGLInstalledFragProc* ip) {
+    SkTArray<GrGLInstalledFragProc::Transform, true>& transforms = ip->fTransforms;
     int numTransforms = transforms.count();
     SkASSERT(numTransforms == processor.getProcessor()->numTransforms());
     for (int t = 0; t < numTransforms; ++t) {
         SkASSERT(transforms[t].fHandle.isValid());
-        const SkMatrix& matrix = get_transform_matrix(processor, ip->fHasExplicitLocalCoords, t);
+        const SkMatrix& matrix = get_transform_matrix(processor, ip->fLocalCoordAttrib, t);
         if (!transforms[t].fCurrentValue.cheapEqualTo(matrix)) {
             fProgramDataManager.setSkMatrix(transforms[t].fHandle.convertToUniformHandle(), matrix);
             transforms[t].fCurrentValue = matrix;
@@ -321,10 +322,8 @@
                                          const BuiltinUniformHandles& builtinUniforms,
                                          GrGLuint programID,
                                          const UniformInfoArray& uniforms,
-                                         GrGLInstalledProcessors* colorProcessors,
-                                         GrGLInstalledProcessors* coverageProcessors)
-    : INHERITED(gpu, desc, builtinUniforms, programID, uniforms, NULL, colorProcessors,
-                coverageProcessors) {
+                                         GrGLInstalledFragProcs* fragmentProcessors)
+    : INHERITED(gpu, desc, builtinUniforms, programID, uniforms, NULL, fragmentProcessors) {
 }
 
 void GrGLNvprProgramBase::onSetMatrixAndRenderTargetHeight(GrGpu::DrawType drawType,
@@ -343,11 +342,9 @@
                                  const BuiltinUniformHandles& builtinUniforms,
                                  GrGLuint programID,
                                  const UniformInfoArray& uniforms,
-                                 GrGLInstalledProcessors* colorProcessors,
-                                 GrGLInstalledProcessors* coverageProcessors,
+                                 GrGLInstalledFragProcs* fragmentProcessors,
                                  const SeparableVaryingInfoArray& separableVaryings)
-    : INHERITED(gpu, desc, builtinUniforms, programID, uniforms, colorProcessors,
-                coverageProcessors) {
+    : INHERITED(gpu, desc, builtinUniforms, programID, uniforms, fragmentProcessors) {
     int count = separableVaryings.count();
     fVaryings.push_back_n(count);
     for (int i = 0; i < count; i++) {
@@ -365,15 +362,13 @@
     SkASSERT(GrGpu::IsPathRenderingDrawType(drawType));
 }
 
-void GrGLNvprProgram::setTransformData(const GrProcessorStage& processor,
-                                       int effectIdx,
-                                       GrGLInstalledProcessors* ip) {
-    SkTArray<GrGLInstalledProcessors::Transform, true>& transforms = ip->fTransforms[effectIdx];
+void GrGLNvprProgram::setTransformData(const GrFragmentStage& proc, GrGLInstalledFragProc* ip) {
+    SkTArray<GrGLInstalledFragProc::Transform, true>& transforms = ip->fTransforms;
     int numTransforms = transforms.count();
-    SkASSERT(numTransforms == processor.getProcessor()->numTransforms());
+    SkASSERT(numTransforms == proc.getProcessor()->numTransforms());
     for (int t = 0; t < numTransforms; ++t) {
         SkASSERT(transforms[t].fHandle.isValid());
-        const SkMatrix& transform = get_transform_matrix(processor, ip->fHasExplicitLocalCoords, t);
+        const SkMatrix& transform = get_transform_matrix(proc, false, t);
         if (transforms[t].fCurrentValue.cheapEqualTo(transform)) {
             continue;
         }
@@ -392,15 +387,13 @@
 //////////////////////////////////////////////////////////////////////////////////////
 
 GrGLLegacyNvprProgram::GrGLLegacyNvprProgram(GrGpuGL* gpu,
-                                 const GrGLProgramDesc& desc,
-                                 const BuiltinUniformHandles& builtinUniforms,
-                                 GrGLuint programID,
-                                 const UniformInfoArray& uniforms,
-                                 GrGLInstalledProcessors* colorProcessors,
-                                 GrGLInstalledProcessors* coverageProcessors,
-                                 int texCoordSetCnt)
-    : INHERITED(gpu, desc, builtinUniforms, programID, uniforms, colorProcessors,
-                coverageProcessors)
+                                             const GrGLProgramDesc& desc,
+                                             const BuiltinUniformHandles& builtinUniforms,
+                                             GrGLuint programID,
+                                             const UniformInfoArray& uniforms,
+                                             GrGLInstalledFragProcs* fps,
+                                             int texCoordSetCnt)
+    : INHERITED(gpu, desc, builtinUniforms, programID, uniforms, fps)
     , fTexCoordSetCnt(texCoordSetCnt) {
 }
 
@@ -409,17 +402,16 @@
     fGpu->glPathRendering()->flushPathTexGenSettings(fTexCoordSetCnt);
 }
 
-void GrGLLegacyNvprProgram::setTransformData(const GrProcessorStage& processorStage,
-                                       int effectIdx,
-                                       GrGLInstalledProcessors* ip) {
+void
+GrGLLegacyNvprProgram::setTransformData(const GrFragmentStage& proc, GrGLInstalledFragProc* ip) {
     // We've hidden the texcoord index in the first entry of the transforms array for each effect
-    int texCoordIndex = ip->fTransforms[effectIdx][0].fHandle.handle();
-    int numTransforms = processorStage.getProcessor()->numTransforms();
+    int texCoordIndex = ip->fTransforms[0].fHandle.handle();
+    int numTransforms = proc.getProcessor()->numTransforms();
     for (int t = 0; t < numTransforms; ++t) {
-        const SkMatrix& transform = get_transform_matrix(processorStage, false, t);
+        const SkMatrix& transform = get_transform_matrix(proc, false, t);
         GrGLPathRendering::PathTexGenComponents components =
                 GrGLPathRendering::kST_PathTexGenComponents;
-        if (processorStage.isPerspectiveCoordTransform(t, false)) {
+        if (proc.isPerspectiveCoordTransform(t, false)) {
             components = GrGLPathRendering::kSTR_PathTexGenComponents;
         }
         fGpu->glPathRendering()->enablePathTexGen(texCoordIndex++, components, transform);
diff --git a/src/gpu/gl/GrGLProgram.h b/src/gpu/gl/GrGLProgram.h
index fc441e5..e8aef35 100644
--- a/src/gpu/gl/GrGLProgram.h
+++ b/src/gpu/gl/GrGLProgram.h
@@ -152,9 +152,6 @@
      */
     void setData(const GrOptDrawState&,
                  GrGpu::DrawType,
-                 const GrGeometryStage* geometryProcessor,
-                 const GrFragmentStage* colorStages[],
-                 const GrFragmentStage* coverageStages[],
                  const GrDeviceCoordTexture* dstCopy, // can be NULL
                  SharedGLState*);
 
@@ -167,13 +164,12 @@
                 const BuiltinUniformHandles&,
                 GrGLuint programID,
                 const UniformInfoArray&,
-                GrGLInstalledProcessors* geometryProcessor,
-                GrGLInstalledProcessors* colorProcessors,
-                GrGLInstalledProcessors* coverageProcessors);
+                GrGLInstalledGeoProc* geometryProcessor,
+                GrGLInstalledFragProcs* fragmentProcessors);
 
     // Sets the texture units for samplers.
     void initSamplerUniforms();
-    void initSamplers(GrGLInstalledProcessors* processors, int* texUnitIdx);
+    void initSamplers(GrGLInstalledProc*, int* texUnitIdx);
 
     // Helper for setData(). Makes GL calls to specify the initial color when there is not
     // per-vertex colors.
@@ -184,23 +180,9 @@
     void setCoverage(const GrOptDrawState&, GrColor coverage, SharedGLState*);
 
     // A templated helper to loop over effects, set the transforms(via subclass) and bind textures
-    template <class ProcessorStage>
-    void setData(const ProcessorStage* effectStages[],
-                 GrGLInstalledProcessors* installedProcessors) {
-        int numEffects = installedProcessors->fGLProcessors.count();
-        SkASSERT(numEffects == installedProcessors->fTransforms.count());
-        SkASSERT(numEffects == installedProcessors->fSamplers.count());
-        for (int e = 0; e < numEffects; ++e) {
-            const GrProcessor& effect = *effectStages[e]->getProcessor();
-            installedProcessors->fGLProcessors[e]->setData(fProgramDataManager, effect);
-            this->setTransformData(*effectStages[e], e, installedProcessors);
-            this->bindTextures(installedProcessors, effect, e);
-        }
-    }
-    virtual void setTransformData(const GrProcessorStage& effectStage,
-                                  int effectIdx,
-                                  GrGLInstalledProcessors* pe);
-    void bindTextures(const GrGLInstalledProcessors*, const GrProcessor&, int effectIdx);
+    void setFragmentData(const GrOptDrawState&);
+    virtual void setTransformData(const GrFragmentStage& effectStage, GrGLInstalledFragProc* pe);
+    void bindTextures(const GrGLInstalledProc*, const GrProcessor&);
 
     /*
      * Legacy NVPR needs a hook here to flush path tex gen settings.
@@ -221,9 +203,8 @@
     GrGLuint fProgramID;
 
     // the installed effects
-    SkAutoTUnref<GrGLInstalledProcessors> fGeometryProcessor;
-    SkAutoTUnref<GrGLInstalledProcessors> fColorEffects;
-    SkAutoTUnref<GrGLInstalledProcessors> fCoverageEffects;
+    SkAutoTDelete<GrGLInstalledGeoProc> fGeometryProcessor;
+    SkAutoTUnref<GrGLInstalledFragProcs> fFragmentProcessors;
 
     GrGLProgramDesc fDesc;
     GrGpuGL* fGpu;
@@ -248,8 +229,7 @@
                         const BuiltinUniformHandles&,
                         GrGLuint programID,
                         const UniformInfoArray&,
-                        GrGLInstalledProcessors* colorProcessors,
-                        GrGLInstalledProcessors* coverageProcessors);
+                        GrGLInstalledFragProcs* fragmentProcessors);
     virtual void onSetMatrixAndRenderTargetHeight(GrGpu::DrawType, const GrOptDrawState&);
 
     typedef GrGLProgram INHERITED;
@@ -267,13 +247,10 @@
                     const BuiltinUniformHandles&,
                     GrGLuint programID,
                     const UniformInfoArray&,
-                    GrGLInstalledProcessors* colorProcessors,
-                    GrGLInstalledProcessors* coverageProcessors,
+                    GrGLInstalledFragProcs* fragmentProcessors,
                     const SeparableVaryingInfoArray& separableVaryings);
     virtual void didSetData(GrGpu::DrawType) SK_OVERRIDE;
-    virtual void setTransformData(const GrProcessorStage&,
-                                  int effectIdx,
-                                  GrGLInstalledProcessors*) SK_OVERRIDE;
+    virtual void setTransformData(const GrFragmentStage&, GrGLInstalledFragProc*) SK_OVERRIDE;
 
     struct Varying {
         GrGLint     fLocation;
@@ -298,13 +275,10 @@
                           const BuiltinUniformHandles&,
                           GrGLuint programID,
                           const UniformInfoArray&,
-                          GrGLInstalledProcessors* colorProcessors,
-                          GrGLInstalledProcessors* coverageProcessors,
+                          GrGLInstalledFragProcs* fragmentProcessors,
                           int texCoordSetCnt);
     virtual void didSetData(GrGpu::DrawType) SK_OVERRIDE;
-    virtual void setTransformData(const GrProcessorStage&,
-                                  int effectIdx,
-                                  GrGLInstalledProcessors*) SK_OVERRIDE;
+    virtual void setTransformData(const GrFragmentStage&, GrGLInstalledFragProc*) SK_OVERRIDE;
 
     int fTexCoordSetCnt;
 
diff --git a/src/gpu/gl/GrGLProgramDesc.cpp b/src/gpu/gl/GrGLProgramDesc.cpp
index 0c85c99..224411e 100644
--- a/src/gpu/gl/GrGLProgramDesc.cpp
+++ b/src/gpu/gl/GrGLProgramDesc.cpp
@@ -60,10 +60,10 @@
     return false;
 }
 
-static uint32_t gen_attrib_key(const GrGeometryProcessor* effect) {
+static uint32_t gen_attrib_key(const GrGeometryProcessor& proc) {
     uint32_t key = 0;
 
-    const GrGeometryProcessor::VertexAttribArray& vars = effect->getVertexAttribs();
+    const GrGeometryProcessor::VertexAttribArray& vars = proc.getVertexAttribs();
     int numAttributes = vars.count();
     SkASSERT(numAttributes <= 2);
     for (int a = 0; a < numAttributes; ++a) {
@@ -73,7 +73,7 @@
     return key;
 }
 
-static uint32_t gen_transform_key(const GrProcessorStage& effectStage,
+static uint32_t gen_transform_key(const GrFragmentStage& effectStage,
                                   bool useExplicitLocalCoords) {
     uint32_t totalKey = 0;
     int numTransforms = effectStage.getProcessor()->numTransforms();
@@ -96,11 +96,11 @@
     return totalKey;
 }
 
-static uint32_t gen_texture_key(const GrProcessor* effect, const GrGLCaps& caps) {
+static uint32_t gen_texture_key(const GrProcessor& proc, const GrGLCaps& caps) {
     uint32_t key = 0;
-    int numTextures = effect->numTextures();
+    int numTextures = proc.numTextures();
     for (int t = 0; t < numTextures; ++t) {
-        const GrTextureAccess& access = effect->textureAccess(t);
+        const GrTextureAccess& access = proc.textureAccess(t);
         uint32_t configComponentMask = GrPixelConfigComponentMask(access.getTexture()->config());
         if (swizzle_requires_alpha_remapping(caps, configComponentMask, access.swizzleMask())) {
             key |= 1 << t;
@@ -115,101 +115,61 @@
  * in its key (e.g. the pixel format of textures used). So we create a meta-key for
  * every effect using this function. It is also responsible for inserting the effect's class ID
  * which must be different for every GrProcessor subclass. It can fail if an effect uses too many
- * textures, transforms, etc, for the space allotted in the meta-key.
+ * textures, transforms, etc, for the space allotted in the meta-key.  NOTE, both FPs and GPs share
+ * this function because it is hairy, though FPs do not have attribs, and GPs do not have transforms
  */
-
-static uint32_t* get_processor_meta_key(const GrProcessorStage& processorStage,
-                                        bool useExplicitLocalCoords,
-                                        const GrGLCaps& caps,
-                                        GrProcessorKeyBuilder* b) {
-
-    uint32_t textureKey = gen_texture_key(processorStage.getProcessor(), caps);
-    uint32_t transformKey = gen_transform_key(processorStage,useExplicitLocalCoords);
-    uint32_t classID = processorStage.getProcessor()->getFactory().effectClassID();
+static bool get_meta_key(const GrProcessor& proc,
+                         const GrGLCaps& caps,
+                         uint32_t transformKey,
+                         uint32_t attribKey,
+                         GrProcessorKeyBuilder* b,
+                         uint16_t* processorKeySize) {
+    const GrBackendProcessorFactory& factory = proc.getFactory();
+    factory.getGLProcessorKey(proc, caps, b);
+    size_t size = b->size();
+    if (size > SK_MaxU16) {
+        *processorKeySize = 0; // suppresses a warning.
+        return false;
+    }
+    *processorKeySize = SkToU16(size);
+    uint32_t textureKey = gen_texture_key(proc, caps);
+    uint32_t classID = proc.getFactory().effectClassID();
 
     // Currently we allow 16 bits for each of the above portions of the meta-key. Fail if they
     // don't fit.
     static const uint32_t kMetaKeyInvalidMask = ~((uint32_t) SK_MaxU16);
     if ((textureKey | transformKey | classID) & kMetaKeyInvalidMask) {
-        return NULL;
+        return false;
     }
 
     uint32_t* key = b->add32n(2);
     key[0] = (textureKey << 16 | transformKey);
     key[1] = (classID << 16);
-    return key;
-}
-
-static bool get_fp_key(const GrProcessorStage& stage,
-                       const GrGLCaps& caps,
-                       bool useExplicitLocalCoords,
-                       GrProcessorKeyBuilder* b,
-                       uint16_t* processorKeySize) {
-    const GrProcessor& effect = *stage.getProcessor();
-    const GrBackendProcessorFactory& factory = effect.getFactory();
-    factory.getGLProcessorKey(effect, caps, b);
-    size_t size = b->size();
-    if (size > SK_MaxU16) {
-        *processorKeySize = 0; // suppresses a warning.
-        return false;
-    }
-    *processorKeySize = SkToU16(size);
-    if (NULL == get_processor_meta_key(stage, useExplicitLocalCoords, caps, b)) {
-        return false;
-    }
-    return true;
-}
-
-static bool get_gp_key(const GrGeometryStage& stage,
-                       const GrGLCaps& caps,
-                       bool useExplicitLocalCoords,
-                       GrProcessorKeyBuilder* b,
-                       uint16_t* processorKeySize) {
-    const GrProcessor& effect = *stage.getProcessor();
-    const GrBackendProcessorFactory& factory = effect.getFactory();
-    factory.getGLProcessorKey(effect, caps, b);
-    size_t size = b->size();
-    if (size > SK_MaxU16) {
-        *processorKeySize = 0; // suppresses a warning.
-        return false;
-    }
-    *processorKeySize = SkToU16(size);
-    uint32_t* key = get_processor_meta_key(stage, useExplicitLocalCoords, caps, b);
-    if (NULL == key) {
-        return false;
-    }
-    uint32_t attribKey = gen_attrib_key(stage.getProcessor());
-
-    // Currently we allow 16 bits for each of the above portions of the meta-key. Fail if they
-    // don't fit.
-    static const uint32_t kMetaKeyInvalidMask = ~((uint32_t) SK_MaxU16);
-    if ((attribKey) & kMetaKeyInvalidMask) {
-       return false;
-    }
-
-    key[1] |= attribKey;
     return true;
 }
 
 struct GeometryProcessorKeyBuilder {
-    typedef GrGeometryStage StagedProcessor;
-    static bool GetProcessorKey(const GrGeometryStage& gpStage,
+    typedef GrGeometryProcessor StagedProcessor;
+    static bool GetProcessorKey(const GrGeometryProcessor& gp,
                                 const GrGLCaps& caps,
-                                bool requiresLocalCoordAttrib,
+                                bool,
                                 GrProcessorKeyBuilder* b,
-                                uint16_t* processorKeySize) {
-        return get_gp_key(gpStage, caps, requiresLocalCoordAttrib, b, processorKeySize);
+                                uint16_t* keySize) {
+        /* 0 because no transforms on a GP */
+        return get_meta_key(gp, caps, 0, gen_attrib_key(gp), b, keySize);
     }
 };
 
 struct FragmentProcessorKeyBuilder {
     typedef GrFragmentStage StagedProcessor;
-    static bool GetProcessorKey(const GrFragmentStage& fpStage,
+    static bool GetProcessorKey(const GrFragmentStage& fps,
                                 const GrGLCaps& caps,
-                                bool requiresLocalCoordAttrib,
+                                bool useLocalCoords,
                                 GrProcessorKeyBuilder* b,
-                                uint16_t* processorKeySize) {
-        return get_fp_key(fpStage, caps, requiresLocalCoordAttrib, b, processorKeySize);
+                                uint16_t* keySize) {
+        /* 0 because no attribs on a fP */
+        return get_meta_key(*fps.getProcessor(), caps, gen_transform_key(fps, useLocalCoords), 0,
+                            b, keySize);
     }
 };
 
@@ -242,17 +202,9 @@
 
 bool GrGLProgramDesc::Build(const GrOptDrawState& optState,
                             GrGpu::DrawType drawType,
-                            GrBlendCoeff srcCoeff,
-                            GrBlendCoeff dstCoeff,
                             GrGpuGL* gpu,
                             const GrDeviceCoordTexture* dstCopy,
-                            const GrGeometryStage** geometryProcessor,
-                            SkTArray<const GrFragmentStage*, true>* colorStages,
-                            SkTArray<const GrFragmentStage*, true>* coverageStages,
                             GrGLProgramDesc* desc) {
-    colorStages->reset();
-    coverageStages->reset();
-
     bool inputColorIsUsed = optState.inputColorIsUsed();
     bool inputCoverageIsUsed = optState.inputCoverageIsUsed();
 
@@ -274,29 +226,17 @@
 
     // We can only have one effect which touches the vertex shader
     if (optState.hasGeometryProcessor()) {
-        const GrGeometryStage& gpStage = *optState.getGeometryProcessor();
-        if (!BuildStagedProcessorKey<GeometryProcessorKeyBuilder>(gpStage,
+        if (!BuildStagedProcessorKey<GeometryProcessorKeyBuilder>(*optState.getGeometryProcessor(),
                                                                   gpu->glCaps(),
-                                                                  requiresLocalCoordAttrib,
-                                                                  desc,
-                                                                  &offsetAndSizeIndex)) {
-            return false;
-        }
-        *geometryProcessor = &gpStage;
-    }
-
-    for (int s = 0; s < optState.numColorStages(); ++s) {
-        if (!BuildStagedProcessorKey<FragmentProcessorKeyBuilder>(optState.getColorStage(s),
-                                                                  gpu->glCaps(),
-                                                                  requiresLocalCoordAttrib,
+                                                                  false,
                                                                   desc,
                                                                   &offsetAndSizeIndex)) {
             return false;
         }
     }
 
-    for (int s = 0; s < optState.numCoverageStages(); ++s) {
-        if (!BuildStagedProcessorKey<FragmentProcessorKeyBuilder>(optState.getCoverageStage(s),
+    for (int s = 0; s < optState.numFragmentStages(); ++s) {
+        if (!BuildStagedProcessorKey<FragmentProcessorKeyBuilder>(optState.getFragmentStage(s),
                                                                   gpu->glCaps(),
                                                                   requiresLocalCoordAttrib,
                                                                   desc,
@@ -399,15 +339,8 @@
     header->fPrimaryOutputType = optState.getPrimaryOutputType();
     header->fSecondaryOutputType = optState.getSecondaryOutputType();
 
-    for (int s = 0; s < optState.numColorStages(); ++s) {
-        colorStages->push_back(&optState.getColorStage(s));
-    }
-    for (int s = 0; s < optState.numCoverageStages(); ++s) {
-        coverageStages->push_back(&optState.getCoverageStage(s));
-    }
-
-    header->fColorEffectCnt = colorStages->count();
-    header->fCoverageEffectCnt = coverageStages->count();
+    header->fColorEffectCnt = optState.numColorStages();
+    header->fCoverageEffectCnt = optState.numCoverageStages();
     desc->finalize();
     return true;
 }
diff --git a/src/gpu/gl/GrGLProgramDesc.h b/src/gpu/gl/GrGLProgramDesc.h
index 9bf7553..4e1be5b 100644
--- a/src/gpu/gl/GrGLProgramDesc.h
+++ b/src/gpu/gl/GrGLProgramDesc.h
@@ -43,13 +43,8 @@
      */
     static bool Build(const GrOptDrawState&,
                       GrGpu::DrawType,
-                      GrBlendCoeff srcCoeff,
-                      GrBlendCoeff dstCoeff,
                       GrGpuGL*,
-                      const GrDeviceCoordTexture* dstCopy,
-                      const GrGeometryStage** geometryProcessor,
-                      SkTArray<const GrFragmentStage*, true>* colorStages,
-                      SkTArray<const GrFragmentStage*, true>* coverageStages,
+                      const GrDeviceCoordTexture*,
                       GrGLProgramDesc*);
 
     bool hasGeometryProcessor() const {
@@ -160,26 +155,23 @@
     const KeyHeader& getHeader() const { return *this->atOffset<KeyHeader, kHeaderOffset>(); }
 
     /** Used to provide effects' keys to their emitCode() function. */
-    class EffectKeyProvider {
+    class ProcKeyProvider {
     public:
-        enum EffectType {
-            kGeometryProcessor_EffectType,
-            kColor_EffectType,
-            kCoverage_EffectType,
+        enum ProcessorType {
+            kGeometry_ProcessorType,
+            kFragment_ProcessorType,
         };
 
-        EffectKeyProvider(const GrGLProgramDesc* desc, EffectType type) : fDesc(desc) {
+        ProcKeyProvider(const GrGLProgramDesc* desc, ProcessorType type)
+            : fDesc(desc), fBaseIndex(0) {
             switch (type) {
-                case kGeometryProcessor_EffectType:
+                case kGeometry_ProcessorType:
                     // there can be only one
                     fBaseIndex = 0;
                     break;
-                case kColor_EffectType:
+                case kFragment_ProcessorType:
                     fBaseIndex = desc->hasGeometryProcessor() ? 1 : 0;
                     break;
-                case kCoverage_EffectType:
-                    fBaseIndex = desc->numColorEffects() + (desc->hasGeometryProcessor() ? 1 : 0);
-                    break;
             }
         }
 
diff --git a/src/gpu/gl/GrGpuGL.h b/src/gpu/gl/GrGpuGL.h
index 056f093..24ab4ec 100644
--- a/src/gpu/gl/GrGpuGL.h
+++ b/src/gpu/gl/GrGpuGL.h
@@ -181,10 +181,7 @@
         void abandon();
         GrGLProgram* getProgram(const GrOptDrawState&,
                                 const GrGLProgramDesc&,
-                                DrawType,
-                                const GrGeometryStage* geometryProcessor,
-                                const GrFragmentStage* colorStages[],
-                                const GrFragmentStage* coverageStages[]);
+                                DrawType);
 
     private:
         enum {
diff --git a/src/gpu/gl/GrGpuGL_program.cpp b/src/gpu/gl/GrGpuGL_program.cpp
index 7dba531..6a09ebf 100644
--- a/src/gpu/gl/GrGpuGL_program.cpp
+++ b/src/gpu/gl/GrGpuGL_program.cpp
@@ -93,10 +93,7 @@
 
 GrGLProgram* GrGpuGL::ProgramCache::getProgram(const GrOptDrawState& optState,
                                                const GrGLProgramDesc& desc,
-                                               DrawType type,
-                                               const GrGeometryStage* geometryProcessor,
-                                               const GrFragmentStage* colorStages[],
-                                               const GrFragmentStage* coverageStages[]) {
+                                               DrawType type) {
 #ifdef PROGRAM_CACHE_STATS
     ++fTotalRequests;
 #endif
@@ -131,9 +128,7 @@
 #ifdef PROGRAM_CACHE_STATS
         ++fCacheMisses;
 #endif
-        GrGLProgram* program = GrGLProgramBuilder::CreateProgram(optState, desc, type,
-                                                                 geometryProcessor, colorStages,
-                                                                 coverageStages, fGpu);
+        GrGLProgram* program = GrGLProgramBuilder::CreateProgram(optState, desc, type, fGpu);
         if (NULL == program) {
             return NULL;
         }
@@ -237,30 +232,13 @@
             return false;
         }
 
-        const GrGeometryStage* geometryProcessor = NULL;
-        SkSTArray<8, const GrFragmentStage*, true> colorStages;
-        SkSTArray<8, const GrFragmentStage*, true> coverageStages;
         GrGLProgramDesc desc;
-        if (!GrGLProgramDesc::Build(*optState.get(),
-                                    type,
-                                    srcCoeff,
-                                    dstCoeff,
-                                    this,
-                                    dstCopy,
-                                    &geometryProcessor,
-                                    &colorStages,
-                                    &coverageStages,
-                                    &desc)) {
+        if (!GrGLProgramDesc::Build(*optState.get(), type, this, dstCopy, &desc)) {
             SkDEBUGFAIL("Failed to generate GL program descriptor");
             return false;
         }
 
-        fCurrentProgram.reset(fProgramCache->getProgram(*optState.get(),
-                                                        desc,
-                                                        type,
-                                                        geometryProcessor,
-                                                        colorStages.begin(),
-                                                        coverageStages.begin()));
+        fCurrentProgram.reset(fProgramCache->getProgram(*optState.get(), desc, type));
         if (NULL == fCurrentProgram.get()) {
             SkDEBUGFAIL("Failed to create program!");
             return false;
@@ -276,13 +254,7 @@
 
         this->flushBlend(*optState.get(), kDrawLines_DrawType == type, srcCoeff, dstCoeff);
 
-        fCurrentProgram->setData(*optState.get(),
-                                 type,
-                                 geometryProcessor,
-                                 colorStages.begin(),
-                                 coverageStages.begin(),
-                                 dstCopy,
-                                 &fSharedGLProgramState);
+        fCurrentProgram->setData(*optState.get(), type, dstCopy, &fSharedGLProgramState);
     }
 
     GrGLRenderTarget* glRT = static_cast<GrGLRenderTarget*>(optState->getRenderTarget());
diff --git a/src/gpu/gl/builders/GrGLLegacyNvprProgramBuilder.cpp b/src/gpu/gl/builders/GrGLLegacyNvprProgramBuilder.cpp
index acb4af1..1c1cb42 100644
--- a/src/gpu/gl/builders/GrGLLegacyNvprProgramBuilder.cpp
+++ b/src/gpu/gl/builders/GrGLLegacyNvprProgramBuilder.cpp
@@ -24,20 +24,17 @@
     return firstFreeCoordSet;
 }
 
-void GrGLLegacyNvprProgramBuilder::emitTransforms(const GrProcessorStage& processorStage,
+void GrGLLegacyNvprProgramBuilder::emitTransforms(const GrFragmentStage& processorStage,
                                             GrGLProcessor::TransformedCoordsArray* outCoords,
-                                            GrGLInstalledProcessors* installedProcessors) {
+                                            GrGLInstalledFragProc* ifp) {
     int numTransforms = processorStage.getProcessor()->numTransforms();
     int texCoordIndex = this->addTexCoordSets(numTransforms);
 
-    SkTArray<GrGLInstalledProcessors::Transform, true>& transforms =
-            installedProcessors->addTransforms();
-
     // Use the first uniform location as the texcoord index.  This may seem a bit hacky but it
     // allows us to use one program effects object for all of our programs which really simplifies
     // the code overall
-    transforms.push_back_n(1);
-    transforms[0].fHandle = GrGLInstalledProcessors::ShaderVarHandle(texCoordIndex);
+    ifp->fTransforms.push_back_n(1);
+    ifp->fTransforms[0].fHandle = GrGLInstalledFragProc::ShaderVarHandle(texCoordIndex);
 
     SkString name;
     for (int t = 0; t < numTransforms; ++t) {
@@ -51,5 +48,5 @@
 
 GrGLProgram* GrGLLegacyNvprProgramBuilder::createProgram(GrGLuint programID) {
     return SkNEW_ARGS(GrGLLegacyNvprProgram, (fGpu, fDesc, fUniformHandles, programID, fUniforms,
-                                        fColorEffects, fCoverageEffects,  fTexCoordSetCnt));
+                                              fFragmentProcessors.get(),  fTexCoordSetCnt));
 }
diff --git a/src/gpu/gl/builders/GrGLLegacyNvprProgramBuilder.h b/src/gpu/gl/builders/GrGLLegacyNvprProgramBuilder.h
index 496fbd8..dabec08 100644
--- a/src/gpu/gl/builders/GrGLLegacyNvprProgramBuilder.h
+++ b/src/gpu/gl/builders/GrGLLegacyNvprProgramBuilder.h
@@ -18,9 +18,9 @@
 
 private:
     int addTexCoordSets(int count);
-    void emitTransforms(const GrProcessorStage&,
+    void emitTransforms(const GrFragmentStage&,
                         GrGLProcessor::TransformedCoordsArray* outCoords,
-                        GrGLInstalledProcessors*);
+                        GrGLInstalledFragProc*);
 
     int fTexCoordSetCnt;
 
diff --git a/src/gpu/gl/builders/GrGLNvprProgramBuilder.cpp b/src/gpu/gl/builders/GrGLNvprProgramBuilder.cpp
index e5eae9d..a20b0d6 100644
--- a/src/gpu/gl/builders/GrGLNvprProgramBuilder.cpp
+++ b/src/gpu/gl/builders/GrGLNvprProgramBuilder.cpp
@@ -18,15 +18,13 @@
         , fSeparableVaryingInfos(kVarsPerBlock) {
 }
 
-void GrGLNvprProgramBuilder::emitTransforms(const GrProcessorStage& processorStage,
+void GrGLNvprProgramBuilder::emitTransforms(const GrFragmentStage& processorStage,
                                             GrGLProcessor::TransformedCoordsArray* outCoords,
-                                            GrGLInstalledProcessors* installedProcessors) {
-    const GrProcessor* effect = processorStage.getProcessor();
+                                            GrGLInstalledFragProc* ifp) {
+    const GrFragmentProcessor* effect = processorStage.getProcessor();
     int numTransforms = effect->numTransforms();
 
-    SkTArray<GrGLInstalledProcessors::Transform, true>& transforms =
-            installedProcessors->addTransforms();
-    transforms.push_back_n(numTransforms);
+    ifp->fTransforms.push_back_n(numTransforms);
 
     for (int t = 0; t < numTransforms; t++) {
         GrSLType varyingType =
@@ -43,24 +41,24 @@
         }
         const char* vsVaryingName;
         const char* fsVaryingName;
-        transforms[t].fHandle = this->addSeparableVarying(varyingType, varyingName,
-                                                          &vsVaryingName, &fsVaryingName);
-        transforms[t].fType = varyingType;
+        ifp->fTransforms[t].fHandle = this->addSeparableVarying(varyingType, varyingName,
+                                                                &vsVaryingName, &fsVaryingName);
+        ifp->fTransforms[t].fType = varyingType;
 
         SkNEW_APPEND_TO_TARRAY(outCoords, GrGLProcessor::TransformedCoords,
                                (SkString(fsVaryingName), varyingType));
     }
 }
 
-GrGLInstalledProcessors::ShaderVarHandle
+GrGLInstalledFragProc::ShaderVarHandle
 GrGLNvprProgramBuilder::addSeparableVarying(GrSLType type,
-                                              const char* name,
-                                              const char** vsOutName,
-                                              const char** fsInName) {
+                                            const char* name,
+                                            const char** vsOutName,
+                                            const char** fsInName) {
     addVarying(type, name, vsOutName, fsInName);
     SeparableVaryingInfo& varying = fSeparableVaryingInfos.push_back();
     varying.fVariable = fFS.fInputs.back();
-    return GrGLInstalledProcessors::ShaderVarHandle(fSeparableVaryingInfos.count() - 1);
+    return GrGLInstalledFragProc::ShaderVarHandle(fSeparableVaryingInfos.count() - 1);
 }
 
 void GrGLNvprProgramBuilder::resolveSeparableVaryings(GrGLuint programId) {
@@ -80,5 +78,5 @@
     // building
     this->resolveSeparableVaryings(programID);
     return SkNEW_ARGS(GrGLNvprProgram, (fGpu, fDesc, fUniformHandles, programID, fUniforms,
-                                        fColorEffects, fCoverageEffects, fSeparableVaryingInfos));
+                                        fFragmentProcessors.get(), fSeparableVaryingInfos));
 }
diff --git a/src/gpu/gl/builders/GrGLNvprProgramBuilder.h b/src/gpu/gl/builders/GrGLNvprProgramBuilder.h
index cb13375..4bf7e02 100644
--- a/src/gpu/gl/builders/GrGLNvprProgramBuilder.h
+++ b/src/gpu/gl/builders/GrGLNvprProgramBuilder.h
@@ -28,11 +28,11 @@
     virtual GrGLProgram* createProgram(GrGLuint programID);
 
 private:
-    virtual void emitTransforms(const GrProcessorStage&,
+    virtual void emitTransforms(const GrFragmentStage&,
                                 GrGLProcessor::TransformedCoordsArray* outCoords,
-                                GrGLInstalledProcessors*) SK_OVERRIDE;
+                                GrGLInstalledFragProc*) SK_OVERRIDE;
 
-    typedef GrGLInstalledProcessors::ShaderVarHandle ShaderVarHandle;
+    typedef GrGLInstalledFragProc::ShaderVarHandle ShaderVarHandle;
 
     /**
      * Add a separable varying input variable to the current program.
diff --git a/src/gpu/gl/builders/GrGLProgramBuilder.cpp b/src/gpu/gl/builders/GrGLProgramBuilder.cpp
index fbf78d7..7af7e8c 100644
--- a/src/gpu/gl/builders/GrGLProgramBuilder.cpp
+++ b/src/gpu/gl/builders/GrGLProgramBuilder.cpp
@@ -6,7 +6,6 @@
  */
 
 #include "GrGLProgramBuilder.h"
-#include "gl/GrGLGeometryProcessor.h"
 #include "gl/GrGLProgram.h"
 #include "gl/GrGLSLPrettyPrint.h"
 #include "gl/GrGLUniformHandle.h"
@@ -32,16 +31,13 @@
 GrGLProgram* GrGLProgramBuilder::CreateProgram(const GrOptDrawState& optState,
                                                const GrGLProgramDesc& desc,
                                                GrGpu::DrawType drawType,
-                                               const GrGeometryStage* geometryProcessor,
-                                               const GrFragmentStage* colorStages[],
-                                               const GrFragmentStage* coverageStages[],
                                                GrGpuGL* gpu) {
     // create a builder.  This will be handed off to effects so they can use it to add
     // uniforms, varyings, textures, etc
     SkAutoTDelete<GrGLProgramBuilder> builder(CreateProgramBuilder(desc,
                                                                    optState,
                                                                    drawType,
-                                                                   SkToBool(geometryProcessor),
+                                                                   optState.hasGeometryProcessor(),
                                                                    gpu));
 
     GrGLProgramBuilder* pb = builder.get();
@@ -74,8 +70,7 @@
         }
     }
 
-    pb->createAndEmitProcessors(geometryProcessor, colorStages, coverageStages, &inputColor,
-                                &inputCoverage);
+    pb->emitAndInstallProcs(optState, &inputColor, &inputCoverage);
 
     if (hasVertexShader) {
         pb->fVS.transformSkiaToGLCoords();
@@ -116,13 +111,15 @@
 
 /////////////////////////////////////////////////////////////////////////////
 
-GrGLProgramBuilder::GrGLProgramBuilder(GrGpuGL* gpu, const GrOptDrawState& optState,
+GrGLProgramBuilder::GrGLProgramBuilder(GrGpuGL* gpu,
+                                       const GrOptDrawState& optState,
                                        const GrGLProgramDesc& desc)
     : fVS(this)
     , fGS(this)
     , fFS(this, desc)
     , fOutOfStage(true)
     , fStageIndex(-1)
+    , fGeometryProcessor(NULL)
     , fOptState(optState)
     , fDesc(desc)
     , fGpu(gpu)
@@ -225,104 +222,106 @@
     }
 }
 
-void GrGLProgramBuilder::createAndEmitProcessors(const GrGeometryStage* geometryProcessor,
-                                                 const GrFragmentStage* colorStages[],
-                                                 const GrFragmentStage* coverageStages[],
-                                                 GrGLSLExpr4* inputColor,
-                                                 GrGLSLExpr4* inputCoverage) {
-    bool useLocalCoords = fVS.hasExplicitLocalCoords();
-
-    EffectKeyProvider colorKeyProvider(&fDesc, EffectKeyProvider::kColor_EffectType);
-    int numColorEffects = fDesc.numColorEffects();
-    GrGLInstalledProcessors* ip = SkNEW_ARGS(GrGLInstalledProcessors, (numColorEffects,
-                                                                       useLocalCoords));
-    this->createAndEmitProcessors<GrFragmentStage>(colorStages, numColorEffects, colorKeyProvider,
-                                                   inputColor, ip);
-    fColorEffects.reset(ip);
-
-    if (geometryProcessor) {
-        fVS.emitAttributes(*geometryProcessor->getProcessor());
-        EffectKeyProvider gpKeyProvider(&fDesc, EffectKeyProvider::kGeometryProcessor_EffectType);
-        ip = SkNEW_ARGS(GrGLInstalledProcessors, (1, useLocalCoords));
-        this->createAndEmitProcessors<GrGeometryStage>(&geometryProcessor, 1, gpKeyProvider,
-                                                       inputCoverage, ip);
-        fGeometryProcessor.reset(ip);
+void GrGLProgramBuilder::emitAndInstallProcs(const GrOptDrawState& optState,
+                                             GrGLSLExpr4* inputColor,
+                                             GrGLSLExpr4* inputCoverage) {
+    fFragmentProcessors.reset(SkNEW(GrGLInstalledFragProcs));
+    int numProcs = optState.numFragmentStages();
+    this->emitAndInstallFragProcs(0, optState.numColorStages(), inputColor);
+    if (optState.hasGeometryProcessor()) {
+        const GrGeometryProcessor& gp = *optState.getGeometryProcessor();
+        fVS.emitAttributes(gp);
+        ProcKeyProvider keyProvider(&fDesc, ProcKeyProvider::kGeometry_ProcessorType);
+        GrGLSLExpr4 output;
+        this->emitAndInstallProc<GrGeometryProcessor>(gp, 0, keyProvider, *inputCoverage, &output);
+        *inputCoverage = output;
     }
-
-    EffectKeyProvider coverageKeyProvider(&fDesc, EffectKeyProvider::kCoverage_EffectType);
-    int numCoverageEffects = fDesc.numCoverageEffects();
-    ip = SkNEW_ARGS(GrGLInstalledProcessors, (numCoverageEffects, useLocalCoords));
-    this->createAndEmitProcessors<GrFragmentStage>(coverageStages, numCoverageEffects,
-                                                   coverageKeyProvider, inputCoverage, ip);
-    fCoverageEffects.reset(ip);
+    this->emitAndInstallFragProcs(optState.numColorStages(), numProcs,  inputCoverage);
 }
 
-template <class ProcessorStage>
-void GrGLProgramBuilder::createAndEmitProcessors(const ProcessorStage* processStages[],
-                                                 int effectCnt,
-                                                 const EffectKeyProvider& keyProvider,
-                                                 GrGLSLExpr4* fsInOutColor,
-                                                 GrGLInstalledProcessors* installedProcessors) {
-    bool effectEmitted = false;
-
-    GrGLSLExpr4 inColor = *fsInOutColor;
-    GrGLSLExpr4 outColor;
-
-    for (int e = 0; e < effectCnt; ++e) {
-        // Program builders have a bit of state we need to clear with each effect
-        AutoStageAdvance adv(this);
-        const ProcessorStage& stage = *processStages[e];
-        SkASSERT(stage.getProcessor());
-
-        if (inColor.isZeros()) {
-            SkString inColorName;
-
-            // Effects have no way to communicate zeros, they treat an empty string as ones.
-            this->nameVariable(&inColorName, '\0', "input");
-            fFS.codeAppendf("vec4 %s = %s;", inColorName.c_str(), inColor.c_str());
-            inColor = inColorName;
-        }
-
-        // create var to hold stage result
-        SkString outColorName;
-        this->nameVariable(&outColorName, '\0', "output");
-        fFS.codeAppendf("vec4 %s;", outColorName.c_str());
-        outColor = outColorName;
-
-        SkASSERT(installedProcessors);
-        const typename ProcessorStage::Processor& processor = *stage.getProcessor();
-        SkSTArray<2, GrGLProcessor::TransformedCoords> coords(processor.numTransforms());
-        SkSTArray<4, GrGLProcessor::TextureSampler> samplers(processor.numTextures());
-
-        this->emitTransforms(stage, &coords, installedProcessors);
-        this->emitSamplers(processor, &samplers, installedProcessors);
-
-        typename ProcessorStage::GLProcessor* glEffect =
-                processor.getFactory().createGLInstance(processor);
-        installedProcessors->addEffect(glEffect);
-
-        // Enclose custom code in a block to avoid namespace conflicts
-        SkString openBrace;
-        openBrace.printf("{ // Stage %d: %s\n", fStageIndex, glEffect->name());
-        fFS.codeAppend(openBrace.c_str());
-        fVS.codeAppend(openBrace.c_str());
-
-        glEffect->emitCode(this, processor, keyProvider.get(e), outColor.c_str(),
-                           inColor.isOnes() ? NULL : inColor.c_str(), coords, samplers);
-
-        // We have to check that effects and the code they emit are consistent, ie if an effect
-        // asks for dst color, then the emit code needs to follow suit
-        verify(processor);
-        fFS.codeAppend("}");
-        fVS.codeAppend("}");
-
-        inColor = outColor;
-        effectEmitted = true;
+void GrGLProgramBuilder::emitAndInstallFragProcs(int procOffset, int numProcs, GrGLSLExpr4* inOut) {
+    ProcKeyProvider keyProvider(&fDesc, ProcKeyProvider::kFragment_ProcessorType);
+    for (int e = procOffset; e < numProcs; ++e) {
+        GrGLSLExpr4 output;
+        const GrFragmentStage& stage = fOptState.getFragmentStage(e);
+        this->emitAndInstallProc<GrFragmentStage>(stage, e, keyProvider, *inOut, &output);
+        *inOut = output;
     }
+}
 
-    if (effectEmitted) {
-        *fsInOutColor = outColor;
-    }
+// TODO Processors cannot output zeros because an empty string is all 1s
+// the fix is to allow effects to take the GrGLSLExpr4 directly
+template <class Proc>
+void GrGLProgramBuilder::emitAndInstallProc(const Proc& proc,
+                                            int index,
+                                            const ProcKeyProvider keyProvider,
+                                            const GrGLSLExpr4& input,
+                                            GrGLSLExpr4* output) {
+    // Program builders have a bit of state we need to clear with each effect
+    AutoStageAdvance adv(this);
+
+    // create var to hold stage result
+    SkString outColorName;
+    this->nameVariable(&outColorName, '\0', "output");
+    fFS.codeAppendf("vec4 %s;", outColorName.c_str());
+    *output = outColorName;
+
+    // Enclose custom code in a block to avoid namespace conflicts
+    SkString openBrace;
+    openBrace.printf("{ // Stage %d\n", fStageIndex);
+    fFS.codeAppend(openBrace.c_str());
+
+    this->emitAndInstallProc(proc, keyProvider.get(index), output->c_str(),
+                             input.isOnes() ? NULL : input.c_str());
+
+    fFS.codeAppend("}");
+}
+
+void GrGLProgramBuilder::emitAndInstallProc(const GrFragmentStage& fs,
+                                            const GrProcessorKey& key,
+                                            const char* outColor,
+                                            const char* inColor) {
+    GrGLInstalledFragProc* ifp = SkNEW_ARGS(GrGLInstalledFragProc, (fVS.hasLocalCoords()));
+
+    const GrFragmentProcessor& fp = *fs.getProcessor();
+    ifp->fGLProc.reset(fp.getFactory().createGLInstance(fp));
+
+    SkSTArray<4, GrGLProcessor::TextureSampler> samplers(fp.numTextures());
+    this->emitSamplers(fp, &samplers, ifp);
+
+    // Fragment processors can have coord transforms
+    SkSTArray<2, GrGLProcessor::TransformedCoords> coords(fp.numTransforms());
+    this->emitTransforms(fs, &coords, ifp);
+
+    ifp->fGLProc->emitCode(this, fp, key, outColor, inColor, coords, samplers);
+
+    // We have to check that effects and the code they emit are consistent, ie if an effect
+    // asks for dst color, then the emit code needs to follow suit
+    verify(fp);
+    fFragmentProcessors->fProcs.push_back(ifp);
+}
+
+void GrGLProgramBuilder::emitAndInstallProc(const GrGeometryProcessor& gp,
+                                            const GrProcessorKey& key,
+                                            const char* outColor,
+                                            const char* inColor) {
+    SkASSERT(!fGeometryProcessor);
+    fGeometryProcessor = SkNEW(GrGLInstalledGeoProc);
+
+    fGeometryProcessor->fGLProc.reset(gp.getFactory().createGLInstance(gp));
+
+    SkSTArray<4, GrGLProcessor::TextureSampler> samplers(gp.numTextures());
+    this->emitSamplers(gp, &samplers, fGeometryProcessor);
+
+    SkSTArray<2, GrGLProcessor::TransformedCoords> coords;
+
+    // TODO remove coords from emit code signature, probably best to use a struct here so these
+    // updates are less painful
+    fGeometryProcessor->fGLProc->emitCode(this, gp, key, outColor, inColor, coords, samplers);
+
+    // We have to check that effects and the code they emit are consistent, ie if an effect
+    // asks for dst color, then the emit code needs to follow suit
+    verify(gp);
 }
 
 void GrGLProgramBuilder::verify(const GrGeometryProcessor& gp) {
@@ -334,19 +333,17 @@
     SkASSERT(fFS.hasReadDstColor() == fp.willReadDstColor());
 }
 
-void GrGLProgramBuilder::emitTransforms(const GrProcessorStage& effectStage,
+void GrGLProgramBuilder::emitTransforms(const GrFragmentStage& effectStage,
                                         GrGLProcessor::TransformedCoordsArray* outCoords,
-                                        GrGLInstalledProcessors* installedProcessors) {
-    SkTArray<GrGLInstalledProcessors::Transform, true>& transforms =
-            installedProcessors->addTransforms();
-    const GrProcessor* effect = effectStage.getProcessor();
+                                        GrGLInstalledFragProc* ifp) {
+    const GrFragmentProcessor* effect = effectStage.getProcessor();
     int numTransforms = effect->numTransforms();
-    transforms.push_back_n(numTransforms);
+    ifp->fTransforms.push_back_n(numTransforms);
 
     for (int t = 0; t < numTransforms; t++) {
         const char* uniName = "StageMatrix";
         GrSLType varyingType =
-                effectStage.isPerspectiveCoordTransform(t, fVS.hasExplicitLocalCoords()) ?
+                effectStage.isPerspectiveCoordTransform(t, fVS.hasLocalCoords()) ?
                         kVec3f_GrSLType :
                         kVec2f_GrSLType;
 
@@ -356,10 +353,10 @@
             suffixedUniName.appendf("_%i", t);
             uniName = suffixedUniName.c_str();
         }
-        transforms[t].fHandle = this->addUniform(GrGLProgramBuilder::kVertex_Visibility,
-                                                 kMat33f_GrSLType,
-                                                 uniName,
-                                                 &uniName).toShaderBuilderIndex();
+        ifp->fTransforms[t].fHandle = this->addUniform(GrGLProgramBuilder::kVertex_Visibility,
+                                                       kMat33f_GrSLType,
+                                                       uniName,
+                                                       &uniName).toShaderBuilderIndex();
 
         const char* varyingName = "MatrixCoord";
         SkString suffixedVaryingName;
@@ -393,18 +390,17 @@
 
 void GrGLProgramBuilder::emitSamplers(const GrProcessor& processor,
                                       GrGLProcessor::TextureSamplerArray* outSamplers,
-                                      GrGLInstalledProcessors* installedProcessors) {
-    SkTArray<GrGLInstalledProcessors::Sampler, true>& samplers = installedProcessors->addSamplers();
+                                      GrGLInstalledProc* ip) {
     int numTextures = processor.numTextures();
-    samplers.push_back_n(numTextures);
+    ip->fSamplers.push_back_n(numTextures);
     SkString name;
     for (int t = 0; t < numTextures; ++t) {
         name.printf("Sampler%d", t);
-        samplers[t].fUniform = this->addUniform(GrGLProgramBuilder::kFragment_Visibility,
-                                                kSampler2D_GrSLType,
-                                                name.c_str());
+        ip->fSamplers[t].fUniform = this->addUniform(GrGLProgramBuilder::kFragment_Visibility,
+                                                     kSampler2D_GrSLType,
+                                                     name.c_str());
         SkNEW_APPEND_TO_TARRAY(outSamplers, GrGLProcessor::TextureSampler,
-                               (samplers[t].fUniform, processor.textureAccess(t)));
+                               (ip->fSamplers[t].fUniform, processor.textureAccess(t)));
     }
 }
 
@@ -506,14 +502,14 @@
 
 GrGLProgram* GrGLProgramBuilder::createProgram(GrGLuint programID) {
     return SkNEW_ARGS(GrGLProgram, (fGpu, fDesc, fUniformHandles, programID, fUniforms,
-                                    fGeometryProcessor, fColorEffects, fCoverageEffects));
+                                    fGeometryProcessor, fFragmentProcessors.get()));
 }
 
-////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////////////////////////
 
-GrGLInstalledProcessors::~GrGLInstalledProcessors() {
-    int numEffects = fGLProcessors.count();
-    for (int e = 0; e < numEffects; ++e) {
-        SkDELETE(fGLProcessors[e]);
+GrGLInstalledFragProcs::~GrGLInstalledFragProcs() {
+    int numProcs = fProcs.count();
+    for (int e = 0; e < numProcs; ++e) {
+        SkDELETE(fProcs[e]);
     }
 }
diff --git a/src/gpu/gl/builders/GrGLProgramBuilder.h b/src/gpu/gl/builders/GrGLProgramBuilder.h
index 6141058..2afdd09 100644
--- a/src/gpu/gl/builders/GrGLProgramBuilder.h
+++ b/src/gpu/gl/builders/GrGLProgramBuilder.h
@@ -13,8 +13,7 @@
 #include "GrGLVertexShaderBuilder.h"
 #include "../GrGLProgramDataManager.h"
 #include "../GrGLUniformHandle.h"
-
-class GrGLInstalledProcessors;
+#include "../GrGLGeometryProcessor.h"
 
 /*
  * This is the base class for a series of interfaces.  This base class *MUST* remain abstract with
@@ -98,6 +97,11 @@
      */
 };
 
+struct GrGLInstalledProc;
+struct GrGLInstalledGeoProc;
+struct GrGLInstalledFragProc;
+struct GrGLInstalledFragProcs;
+
 /*
  * Please note - no diamond problems because of virtual inheritance.  Also, both base classes
  * are pure virtual with no data members.  This is the base class for program building.
@@ -118,9 +122,6 @@
     static GrGLProgram* CreateProgram(const GrOptDrawState&,
                                       const GrGLProgramDesc&,
                                       GrGpu::DrawType,
-                                      const GrGeometryStage* inGeometryProcessor,
-                                      const GrFragmentStage* inColorStages[],
-                                      const GrFragmentStage* inCoverageStages[],
                                       GrGpuGL* gpu);
 
     virtual UniformHandle addUniform(uint32_t visibility,
@@ -150,11 +151,12 @@
     virtual GrGLFPFragmentBuilder* getFragmentShaderBuilder() SK_OVERRIDE { return &fFS; }
     virtual GrGLVertexBuilder* getVertexShaderBuilder() SK_OVERRIDE { return &fVS; }
 
-    virtual void addVarying(GrSLType type,
-                            const char* name,
-                            const char** vsOutName = NULL,
-                            const char** fsInName = NULL,
-                            GrGLShaderVar::Precision fsPrecision=GrGLShaderVar::kDefault_Precision);
+    virtual void addVarying(
+            GrSLType type,
+            const char* name,
+            const char** vsOutName = NULL,
+            const char** fsInName = NULL,
+            GrGLShaderVar::Precision fsPrecision=GrGLShaderVar::kDefault_Precision) SK_OVERRIDE;
 
     // Handles for program uniforms (other than per-effect uniforms)
     struct BuiltinUniformHandles {
@@ -174,6 +176,10 @@
     };
 
 protected:
+    typedef GrGLProgramDesc::ProcKeyProvider ProcKeyProvider;
+    typedef GrGLProgramDataManager::UniformInfo UniformInfo;
+    typedef GrGLProgramDataManager::UniformInfoArray UniformInfoArray;
+
     static GrGLProgramBuilder* CreateProgramBuilder(const GrGLProgramDesc&,
                                                     const GrOptDrawState&,
                                                     GrGpu::DrawType,
@@ -191,32 +197,40 @@
     // generating stage code.
     void nameVariable(SkString* out, char prefix, const char* name);
     void setupUniformColorAndCoverageIfNeeded(GrGLSLExpr4* inputColor, GrGLSLExpr4* inputCoverage);
-    void createAndEmitProcessors(const GrGeometryStage* geometryProcessor,
-                                 const GrFragmentStage* colorStages[],
-                                 const GrFragmentStage* coverageStages[],
-                                 GrGLSLExpr4* inputColor,
-                                 GrGLSLExpr4* inputCoverage);
-    template <class ProcessorStage>
-    void createAndEmitProcessors(const ProcessorStage*[],
-                                 int effectCnt,
-                                 const GrGLProgramDesc::EffectKeyProvider&,
-                                 GrGLSLExpr4* fsInOutColor,
-                                 GrGLInstalledProcessors*);
+    void emitAndInstallProcs(const GrOptDrawState& optState,
+                             GrGLSLExpr4* inputColor,
+                             GrGLSLExpr4* inputCoverage);
+    void emitAndInstallFragProcs(int procOffset, int numProcs, GrGLSLExpr4* inOut);
+    template <class Proc>
+    void emitAndInstallProc(const Proc&,
+                            int index,
+                            const ProcKeyProvider,
+                            const GrGLSLExpr4& input,
+                            GrGLSLExpr4* output);
+
+    // these emit functions help to keep the createAndEmitProcessors template general
+    void emitAndInstallProc(const GrFragmentStage&,
+                            const GrProcessorKey&,
+                            const char* outColor,
+                            const char* inColor);
+    void emitAndInstallProc(const GrGeometryProcessor&,
+                            const GrProcessorKey&,
+                            const char* outColor,
+                            const char* inColor);
     void verify(const GrGeometryProcessor&);
     void verify(const GrFragmentProcessor&);
     void emitSamplers(const GrProcessor&,
                       GrGLProcessor::TextureSamplerArray* outSamplers,
-                      GrGLInstalledProcessors*);
+                      GrGLInstalledProc*);
 
     // each specific program builder has a distinct transform and must override this function
-    virtual void emitTransforms(const GrProcessorStage&,
+    virtual void emitTransforms(const GrFragmentStage&,
                                 GrGLProcessor::TransformedCoordsArray* outCoords,
-                                GrGLInstalledProcessors*);
+                                GrGLInstalledFragProc*);
     GrGLProgram* finalize();
     void bindUniformLocations(GrGLuint programID);
     bool checkLinkStatus(GrGLuint programID);
     void resolveUniformLocations(GrGLuint programID);
-
     void cleanupProgram(GrGLuint programID, const SkTDArray<GrGLuint>& shaderIDs);
     void cleanupShaders(const SkTDArray<GrGLuint>& shaderIDs);
 
@@ -256,10 +270,6 @@
     void enterStage() { fOutOfStage = false; }
     int stageIndex() const { return fStageIndex; }
 
-    typedef GrGLProgramDesc::EffectKeyProvider EffectKeyProvider;
-    typedef GrGLProgramDataManager::UniformInfo UniformInfo;
-    typedef GrGLProgramDataManager::UniformInfoArray UniformInfoArray;
-
     // number of each input/output type in a single allocation block, used by many builders
     static const int kVarsPerBlock;
 
@@ -270,9 +280,8 @@
     bool fOutOfStage;
     int fStageIndex;
 
-    SkAutoTUnref<GrGLInstalledProcessors> fGeometryProcessor;
-    SkAutoTUnref<GrGLInstalledProcessors> fColorEffects;
-    SkAutoTUnref<GrGLInstalledProcessors> fCoverageEffects;
+    GrGLInstalledGeoProc* fGeometryProcessor;
+    SkAutoTUnref<GrGLInstalledFragProcs> fFragmentProcessors;
 
     const GrOptDrawState& fOptState;
     const GrGLProgramDesc& fDesc;
@@ -286,32 +295,26 @@
 };
 
 /**
- * This class encapsulates an array of GrGLProcessors and their supporting data (coord transforms
- * and textures). It is built by GrGLProgramBuilder, then used to manage the necessary GL
- * state and shader uniforms in GLPrograms.  Its just Plain old data, and as such is entirely public
- *
- * TODO We really don't need this class to have an array of processors.  It makes sense for it
- * to just have one, also break out the transforms
+ * The below structs represent processors installed in programs.  All processors can have texture
+ * samplers, but only frag processors have coord transforms, hence the need for different structs
  */
-class GrGLInstalledProcessors : public SkRefCnt {
-public:
-    GrGLInstalledProcessors(int reserveCount, bool hasExplicitLocalCoords = false)
-        : fGLProcessors(reserveCount)
-        , fSamplers(reserveCount)
-        , fTransforms(reserveCount)
-        , fHasExplicitLocalCoords(hasExplicitLocalCoords) {
-    }
+struct GrGLInstalledProc {
+     typedef GrGLProgramDataManager::UniformHandle UniformHandle;
 
-    virtual ~GrGLInstalledProcessors();
+     struct Sampler {
+         SkDEBUGCODE(Sampler() : fTextureUnit(-1) {})
+         UniformHandle  fUniform;
+         int            fTextureUnit;
+     };
+     SkSTArray<4, Sampler, true> fSamplers;
+};
 
-    typedef GrGLProgramDataManager::UniformHandle UniformHandle;
+struct GrGLInstalledGeoProc : public GrGLInstalledProc {
+    SkAutoTDelete<GrGLGeometryProcessor> fGLProc;
+};
 
-    struct Sampler {
-        SkDEBUGCODE(Sampler() : fTextureUnit(-1) {})
-        UniformHandle  fUniform;
-        int            fTextureUnit;
-    };
-
+struct GrGLInstalledFragProc : public GrGLInstalledProc {
+    GrGLInstalledFragProc(bool useLocalCoords) : fGLProc(NULL), fLocalCoordAttrib(useLocalCoords) {}
     class ShaderVarHandle {
     public:
         bool isValid() const { return fHandle > -1; }
@@ -334,19 +337,14 @@
         GrSLType       fType;
     };
 
-    void addEffect(GrGLProcessor* effect) { fGLProcessors.push_back(effect); }
-    SkTArray<Sampler, true>& addSamplers() { return fSamplers.push_back(); }
-    SkTArray<Transform, true>& addTransforms() { return fTransforms.push_back(); }
+    SkAutoTDelete<GrGLFragmentProcessor> fGLProc;
+    SkSTArray<2, Transform, true>        fTransforms;
+    bool                                 fLocalCoordAttrib;
+};
 
-    SkTArray<GrGLProcessor*>                 fGLProcessors;
-    SkTArray<SkSTArray<4, Sampler, true> >   fSamplers;
-    SkTArray<SkSTArray<2, Transform, true> > fTransforms;
-    bool                                     fHasExplicitLocalCoords;
-
-    friend class GrGLShaderBuilder;
-    friend class GrGLVertexShaderBuilder;
-    friend class GrGLFragmentShaderBuilder;
-    friend class GrGLGeometryShaderBuilder;
+struct GrGLInstalledFragProcs : public SkRefCnt {
+    virtual ~GrGLInstalledFragProcs();
+    SkSTArray<8, GrGLInstalledFragProc*, true> fProcs;
 };
 
 #endif
diff --git a/src/gpu/gl/builders/GrGLVertexShaderBuilder.h b/src/gpu/gl/builders/GrGLVertexShaderBuilder.h
index 0b85b84..6e1495a 100644
--- a/src/gpu/gl/builders/GrGLVertexShaderBuilder.h
+++ b/src/gpu/gl/builders/GrGLVertexShaderBuilder.h
@@ -21,7 +21,7 @@
     /**
      * Are explicit local coordinates provided as input to the vertex shader.
      */
-    bool hasExplicitLocalCoords() const { return (fLocalCoordsVar != fPositionVar); }
+    bool hasLocalCoords() const { return (fLocalCoordsVar != fPositionVar); }
 
     /** Returns a vertex attribute that represents the local coords in the VS. This may be the same
         as positionAttribute() or it may not be. It depends upon whether the rendering code
diff --git a/tests/GLProgramsTest.cpp b/tests/GLProgramsTest.cpp
index 5ab35b7..75032bd 100644
--- a/tests/GLProgramsTest.cpp
+++ b/tests/GLProgramsTest.cpp
@@ -456,9 +456,6 @@
             ds->reset();
             continue;
         }
-        const GrGeometryStage*                     geometryProcessor = NULL;
-        SkSTArray<8, const GrFragmentStage*, true> colorStages;
-        SkSTArray<8, const GrFragmentStage*, true> coverageStages;
         GrGLProgramDesc desc;
         GrDeviceCoordTexture dstCopy;
 
@@ -468,24 +465,14 @@
         }
         if (!GrGLProgramDesc::Build(*ods,
                                     drawType,
-                                    ods->getSrcBlendCoeff(),
-                                    ods->getDstBlendCoeff(),
                                     this,
                                     dstCopy.texture() ? &dstCopy : NULL,
-                                    &geometryProcessor,
-                                    &colorStages,
-                                    &coverageStages,
                                     &desc)) {
             SkDebugf("Failed to generate GL program descriptor");
             return false;
         }
-        SkAutoTUnref<GrGLProgram> program(GrGLProgramBuilder::CreateProgram(*ods,
-                                                                            desc,
-                                                                            drawType,
-                                                                            geometryProcessor,
-                                                                            colorStages.begin(),
-                                                                            coverageStages.begin(),
-                                                                            this));
+        SkAutoTUnref<GrGLProgram> program(
+                GrGLProgramBuilder::CreateProgram(*ods, desc, drawType, this));
         if (NULL == program.get()) {
             SkDebugf("Failed to create program!");
             return false;