OptState owns program descriptor

BUG=skia:

Review URL: https://codereview.chromium.org/674543004
diff --git a/src/gpu/GrGpu.h b/src/gpu/GrGpu.h
index 9478530..6814ae9 100644
--- a/src/gpu/GrGpu.h
+++ b/src/gpu/GrGpu.h
@@ -11,6 +11,7 @@
 #include "GrDrawTarget.h"
 #include "GrClipMaskManager.h"
 #include "GrPathRendering.h"
+#include "GrProgramDesc.h"
 #include "SkPath.h"
 
 class GrContext;
@@ -324,6 +325,12 @@
 
     GrContext::GPUStats* gpuStats() { return &fGPUStats; }
 
+    virtual void buildProgramDesc(const GrOptDrawState&,
+                                  const GrProgramDesc::DescInfo&,
+                                  GrGpu::DrawType,
+                                  const GrDeviceCoordTexture* dstCopy,
+                                  GrProgramDesc*) = 0;
+
 protected:
     DrawType PrimTypeToDrawType(GrPrimitiveType type) {
         switch (type) {
diff --git a/src/gpu/GrOptDrawState.cpp b/src/gpu/GrOptDrawState.cpp
index 77a9fc8..529e9ed 100644
--- a/src/gpu/GrOptDrawState.cpp
+++ b/src/gpu/GrOptDrawState.cpp
@@ -9,12 +9,15 @@
 
 #include "GrDrawState.h"
 #include "GrDrawTargetCaps.h"
+#include "gl/GrGpuGL.h"
 
 GrOptDrawState::GrOptDrawState(const GrDrawState& drawState,
                                BlendOptFlags blendOptFlags,
                                GrBlendCoeff optSrcCoeff,
                                GrBlendCoeff optDstCoeff,
-                               const GrDrawTargetCaps& caps) {
+                               GrGpu* gpu,
+                               const GrDeviceCoordTexture* dstCopy,
+                               GrGpu::DrawType drawType) {
     fRenderTarget.set(SkSafeRef(drawState.getRenderTarget()), kWrite_GrIOType);
     fColor = drawState.getColor();
     fCoverage = drawState.getCoverage();
@@ -29,13 +32,14 @@
     fBlendOptFlags = blendOptFlags;
     fSrcBlend = optSrcCoeff;
     fDstBlend = optDstCoeff;
+    GrProgramDesc::DescInfo descInfo;
 
-    memcpy(fFixedFunctionVertexAttribIndices,
+    memcpy(descInfo.fFixedFunctionVertexAttribIndices,
            drawState.getFixedFunctionVertexAttribIndices(),
-           sizeof(fFixedFunctionVertexAttribIndices));
+           sizeof(descInfo.fFixedFunctionVertexAttribIndices));
 
-    fInputColorIsUsed = true;
-    fInputCoverageIsUsed = true;
+    descInfo.fInputColorIsUsed = true;
+    descInfo.fInputCoverageIsUsed = true;
 
     int firstColorStageIdx = 0;
     int firstCoverageStageIdx = 0;
@@ -43,16 +47,18 @@
 
     uint8_t fixedFunctionVAToRemove = 0;
 
-    this->computeEffectiveColorStages(drawState, &firstColorStageIdx, &fixedFunctionVAToRemove);
-    this->computeEffectiveCoverageStages(drawState, &firstCoverageStageIdx);
-    this->adjustFromBlendOpts(drawState, &firstColorStageIdx, &firstCoverageStageIdx,
+    this->computeEffectiveColorStages(drawState, &descInfo, &firstColorStageIdx,
+                                      &fixedFunctionVAToRemove);
+    this->computeEffectiveCoverageStages(drawState, &descInfo, &firstCoverageStageIdx);
+    this->adjustFromBlendOpts(drawState, &descInfo, &firstColorStageIdx, &firstCoverageStageIdx,
                               &fixedFunctionVAToRemove);
     // Should not be setting any more FFVA to be removed at this point
     if (0 != fixedFunctionVAToRemove) {
-        this->removeFixedFunctionVertexAttribs(fixedFunctionVAToRemove);
+        this->removeFixedFunctionVertexAttribs(fixedFunctionVAToRemove, &descInfo);
     }
-    this->getStageStats(drawState, firstColorStageIdx, firstCoverageStageIdx);
-    this->setOutputStateInfo(drawState, caps, firstCoverageStageIdx, &separateCoverageFromColor);
+    this->getStageStats(drawState, firstColorStageIdx, firstCoverageStageIdx, &descInfo);
+    this->setOutputStateInfo(drawState, *gpu->caps(), firstCoverageStageIdx, &descInfo,
+                             &separateCoverageFromColor);
 
     // Copy GeometryProcesssor from DS or ODS
     if (drawState.hasGeometryProcessor()) {
@@ -79,10 +85,16 @@
             fNumColorStages = fFragmentStages.count();
         }
     }
+
+    // now create a key
+    gpu->buildProgramDesc(*this, descInfo, drawType, dstCopy, &fDesc);
 };
 
-GrOptDrawState* GrOptDrawState::Create(const GrDrawState& drawState, const GrDrawTargetCaps& caps,
+GrOptDrawState* GrOptDrawState::Create(const GrDrawState& drawState,
+                                       GrGpu* gpu,
+                                       const GrDeviceCoordTexture* dstCopy,
                                        GrGpu::DrawType drawType) {
+    const GrDrawTargetCaps& caps = *gpu->caps();
     if (NULL == drawState.fCachedOptState || caps.getUniqueID() != drawState.fCachedCapsID) {
         GrBlendCoeff srcCoeff;
         GrBlendCoeff dstCoeff;
@@ -100,7 +112,7 @@
         }
 
         drawState.fCachedOptState = SkNEW_ARGS(GrOptDrawState, (drawState, blendFlags, srcCoeff,
-                                                                dstCoeff, caps));
+                                                                dstCoeff, gpu, dstCopy, drawType));
         drawState.fCachedCapsID = caps.getUniqueID();
     } else {
 #ifdef SK_DEBUG
@@ -109,8 +121,8 @@
         BlendOptFlags blendFlags = (BlendOptFlags) drawState.getBlendOpts(false,
                                                                           &srcCoeff,
                                                                           &dstCoeff);
-        SkASSERT(GrOptDrawState(drawState, blendFlags, srcCoeff, dstCoeff, caps) ==
-                 *drawState.fCachedOptState);
+        SkASSERT(GrOptDrawState(drawState, blendFlags, srcCoeff, dstCoeff, gpu, dstCopy,
+                                drawType) == *drawState.fCachedOptState);
 #endif
     }
     drawState.fCachedOptState->ref();
@@ -120,45 +132,47 @@
 void GrOptDrawState::setOutputStateInfo(const GrDrawState& ds,
                                         const GrDrawTargetCaps& caps,
                                         int firstCoverageStageIdx,
+                                        GrProgramDesc::DescInfo* descInfo,
                                         bool* separateCoverageFromColor) {
     // Set this default and then possibly change our mind if there is coverage.
-    fPrimaryOutputType = kModulate_PrimaryOutputType;
-    fSecondaryOutputType = kNone_SecondaryOutputType;
+    descInfo->fPrimaryOutputType = GrProgramDesc::kModulate_PrimaryOutputType;
+    descInfo->fSecondaryOutputType = GrProgramDesc::kNone_SecondaryOutputType;
 
     // If we do have coverage determine whether it matters.
     *separateCoverageFromColor = this->hasGeometryProcessor();
     if (!this->isCoverageDrawing() &&
         (ds.numCoverageStages() - firstCoverageStageIdx > 0 ||
          ds.hasGeometryProcessor() ||
-         this->hasCoverageVertexAttribute())) {
+         descInfo->hasCoverageVertexAttribute())) {
 
         if (caps.dualSourceBlendingSupport()) {
             if (kZero_GrBlendCoeff == fDstBlend) {
                 // write the coverage value to second color
-                fSecondaryOutputType =  kCoverage_SecondaryOutputType;
+                descInfo->fSecondaryOutputType = GrProgramDesc::kCoverage_SecondaryOutputType;
                 *separateCoverageFromColor = true;
                 fDstBlend = (GrBlendCoeff)GrGpu::kIS2C_GrBlendCoeff;
             } else if (kSA_GrBlendCoeff == fDstBlend) {
                 // SA dst coeff becomes 1-(1-SA)*coverage when dst is partially covered.
-                fSecondaryOutputType = kCoverageISA_SecondaryOutputType;
+                descInfo->fSecondaryOutputType = GrProgramDesc::kCoverageISA_SecondaryOutputType;
                 *separateCoverageFromColor = true;
                 fDstBlend = (GrBlendCoeff)GrGpu::kIS2C_GrBlendCoeff;
             } else if (kSC_GrBlendCoeff == fDstBlend) {
                 // SA dst coeff becomes 1-(1-SA)*coverage when dst is partially covered.
-                fSecondaryOutputType = kCoverageISC_SecondaryOutputType;
+                descInfo->fSecondaryOutputType = GrProgramDesc::kCoverageISC_SecondaryOutputType;
                 *separateCoverageFromColor = true;
                 fDstBlend = (GrBlendCoeff)GrGpu::kIS2C_GrBlendCoeff;
             }
-        } else if (fReadsDst &&
+        } else if (descInfo->fReadsDst &&
                    kOne_GrBlendCoeff == fSrcBlend &&
                    kZero_GrBlendCoeff == fDstBlend) {
-            fPrimaryOutputType = kCombineWithDst_PrimaryOutputType;
+            descInfo->fPrimaryOutputType = GrProgramDesc::kCombineWithDst_PrimaryOutputType;
             *separateCoverageFromColor = true;
         }
     }
 }
 
 void GrOptDrawState::adjustFromBlendOpts(const GrDrawState& ds,
+                                         GrProgramDesc::DescInfo* descInfo,
                                          int* firstColorStageIdx,
                                          int* firstCoverageStageIdx,
                                          uint8_t* fixedFunctionVAToRemove) {
@@ -171,15 +185,15 @@
             break;
         case kEmitCoverage_BlendOptFlag:
             fColor = 0xffffffff;
-            fInputColorIsUsed = true;
+            descInfo->fInputColorIsUsed = true;
             *firstColorStageIdx = ds.numColorStages();
             *fixedFunctionVAToRemove |= 0x1 << kColor_GrVertexAttribBinding;
             break;
         case kEmitTransBlack_BlendOptFlag:
             fColor = 0;
             fCoverage = 0xff;
-            fInputColorIsUsed = true;
-            fInputCoverageIsUsed = true;
+            descInfo->fInputColorIsUsed = true;
+            descInfo->fInputCoverageIsUsed = true;
             *firstColorStageIdx = ds.numColorStages();
             *firstCoverageStageIdx = ds.numCoverageStages();
             *fixedFunctionVAToRemove |= (0x1 << kColor_GrVertexAttribBinding |
@@ -190,12 +204,13 @@
     }
 }
 
-void GrOptDrawState::removeFixedFunctionVertexAttribs(uint8_t removeVAFlag) {
+void GrOptDrawState::removeFixedFunctionVertexAttribs(uint8_t removeVAFlag,
+                                                      GrProgramDesc::DescInfo* descInfo) {
     int numToRemove = 0;
     uint8_t maskCheck = 0x1;
     // Count the number of vertex attributes that we will actually remove
     for (int i = 0; i < kGrFixedFunctionVertexAttribBindingCnt; ++i) {
-        if ((maskCheck & removeVAFlag) && -1 != fFixedFunctionVertexAttribIndices[i]) {
+        if ((maskCheck & removeVAFlag) && -1 != descInfo->fFixedFunctionVertexAttribIndices[i]) {
             ++numToRemove;
         }
         maskCheck <<= 1;
@@ -211,11 +226,11 @@
         if (currAttrib.fBinding < kGrFixedFunctionVertexAttribBindingCnt) {
             uint8_t maskCheck = 0x1 << currAttrib.fBinding;
             if (maskCheck & removeVAFlag) {
-                SkASSERT(-1 != fFixedFunctionVertexAttribIndices[currAttrib.fBinding]);
-                fFixedFunctionVertexAttribIndices[currAttrib.fBinding] = -1;
+                SkASSERT(-1 != descInfo->fFixedFunctionVertexAttribIndices[currAttrib.fBinding]);
+                descInfo->fFixedFunctionVertexAttribIndices[currAttrib.fBinding] = -1;
                 continue;
             }
-            fFixedFunctionVertexAttribIndices[currAttrib.fBinding] = newIdx;
+            descInfo->fFixedFunctionVertexAttribIndices[currAttrib.fBinding] = newIdx;
         }
         memcpy(dst, src, sizeof(GrVertexAttrib));
         ++newIdx;
@@ -225,12 +240,14 @@
     fVAPtr = fOptVA.get();
 }
 
-void GrOptDrawState::computeEffectiveColorStages(const GrDrawState& ds, int* firstColorStageIdx,
+void GrOptDrawState::computeEffectiveColorStages(const GrDrawState& ds,
+                                                 GrProgramDesc::DescInfo* descInfo,
+                                                 int* firstColorStageIdx,
                                                  uint8_t* fixedFunctionVAToRemove) {
     // Set up color and flags for ConstantColorComponent checks
     GrProcessor::InvariantOutput inout;
     inout.fIsSingleComponent = false;
-    if (!this->hasColorVertexAttribute()) {
+    if (!descInfo->hasColorVertexAttribute()) {
         inout.fColor = ds.getColor();
         inout.fValidFlags = kRGBA_GrColorComponentFlags;
     } else {
@@ -249,12 +266,12 @@
         fp->computeInvariantOutput(&inout);
         if (!inout.fWillUseInputColor) {
             *firstColorStageIdx = i;
-            fInputColorIsUsed = false;
+            descInfo->fInputColorIsUsed = false;
         }
         if (kRGBA_GrColorComponentFlags == inout.fValidFlags) {
             *firstColorStageIdx = i + 1;
             fColor = inout.fColor;
-            fInputColorIsUsed = true;
+            descInfo->fInputColorIsUsed = true;
             *fixedFunctionVAToRemove |= 0x1 << kColor_GrVertexAttribBinding;
             // Since we are clearing all previous color stages we are in a state where we have found
             // zero stages that don't multiply the inputColor.
@@ -264,6 +281,7 @@
 }
 
 void GrOptDrawState::computeEffectiveCoverageStages(const GrDrawState& ds,
+                                                    GrProgramDesc::DescInfo* descInfo,
                                                     int* firstCoverageStageIdx) {
     // We do not try to optimize out constantColor coverage effects here. It is extremely rare
     // to have a coverage effect that returns a constant value for all four channels. Thus we
@@ -278,7 +296,7 @@
         fp->computeInvariantOutput(&inout);
         if (!inout.fWillUseInputColor) {
             *firstCoverageStageIdx = i;
-            fInputCoverageIsUsed = false;
+            descInfo->fInputCoverageIsUsed = false;
         }
     }
 #endif
@@ -294,26 +312,26 @@
 }
 
 void GrOptDrawState::getStageStats(const GrDrawState& ds, int firstColorStageIdx,
-                                   int firstCoverageStageIdx) {
+                                   int firstCoverageStageIdx, GrProgramDesc::DescInfo* descInfo) {
     // We will need a local coord attrib if there is one currently set on the optState and we are
     // actually generating some effect code
-    fRequiresLocalCoordAttrib = this->hasLocalCoordAttribute() &&
+    descInfo->fRequiresLocalCoordAttrib = descInfo->hasLocalCoordAttribute() &&
         ds.numTotalStages() - firstColorStageIdx - firstCoverageStageIdx > 0;
 
-    fReadsDst = false;
-    fReadsFragPosition = false;
+    descInfo->fReadsDst = false;
+    descInfo->fReadsFragPosition = false;
 
     for (int s = firstColorStageIdx; s < ds.numColorStages(); ++s) {
         const GrFragmentStage& stage = ds.getColorStage(s);
-        get_stage_stats(stage, &fReadsDst, &fReadsFragPosition);
+        get_stage_stats(stage, &descInfo->fReadsDst, &descInfo->fReadsFragPosition);
     }
     for (int s = firstCoverageStageIdx; s < ds.numCoverageStages(); ++s) {
         const GrFragmentStage& stage = ds.getCoverageStage(s);
-        get_stage_stats(stage, &fReadsDst, &fReadsFragPosition);
+        get_stage_stats(stage, &descInfo->fReadsDst, &descInfo->fReadsFragPosition);
     }
     if (ds.hasGeometryProcessor()) {
         const GrGeometryProcessor& gp = *ds.getGeometryProcessor();
-        fReadsFragPosition = fReadsFragPosition || gp.willReadFragmentPosition();
+        descInfo->fReadsFragPosition = descInfo->fReadsFragPosition || gp.willReadFragmentPosition();
     }
 }
 
@@ -324,14 +342,15 @@
 }
 
 bool GrOptDrawState::isEqual(const GrOptDrawState& that) const {
-    bool usingVertexColors = this->hasColorVertexAttribute();
+    if (this->fDesc != that.fDesc) {
+        return false;
+    }
+    bool usingVertexColors = that.fDesc.header().fColorAttributeIndex != -1;
     if (!usingVertexColors && this->fColor != that.fColor) {
         return false;
     }
 
     if (this->getRenderTarget() != that.getRenderTarget() ||
-        this->fFragmentStages.count() != that.fFragmentStages.count() ||
-        this->fNumColorStages != that.fNumColorStages ||
         !this->fViewMatrix.cheapEqualTo(that.fViewMatrix) ||
         this->fSrcBlend != that.fSrcBlend ||
         this->fDstBlend != that.fDstBlend ||
@@ -341,23 +360,15 @@
         this->fVAStride != that.fVAStride ||
         memcmp(this->fVAPtr, that.fVAPtr, this->fVACount * sizeof(GrVertexAttrib)) ||
         this->fStencilSettings != that.fStencilSettings ||
-        this->fDrawFace != that.fDrawFace ||
-        this->fInputColorIsUsed != that.fInputColorIsUsed ||
-        this->fInputCoverageIsUsed != that.fInputCoverageIsUsed ||
-        this->fReadsDst != that.fReadsDst ||
-        this->fReadsFragPosition != that.fReadsFragPosition ||
-        this->fRequiresLocalCoordAttrib != that.fRequiresLocalCoordAttrib ||
-        this->fPrimaryOutputType != that.fPrimaryOutputType ||
-        this->fSecondaryOutputType != that.fSecondaryOutputType) {
+        this->fDrawFace != that.fDrawFace) {
         return false;
     }
 
-    bool usingVertexCoverage = this->hasCoverageVertexAttribute();
+    bool usingVertexCoverage = this->fDesc.header().fCoverageAttributeIndex != -1;
     if (!usingVertexCoverage && this->fCoverage != that.fCoverage) {
         return false;
     }
 
-    bool explicitLocalCoords = this->hasLocalCoordAttribute();
     if (this->hasGeometryProcessor()) {
         if (!that.hasGeometryProcessor()) {
             return false;
@@ -368,17 +379,13 @@
         return false;
     }
 
+    bool explicitLocalCoords = this->fDesc.header().fLocalCoordAttributeIndex != -1;
     for (int i = 0; i < this->numFragmentStages(); i++) {
         if (!GrFragmentStage::AreCompatible(this->getFragmentStage(i), that.getFragmentStage(i),
                                             explicitLocalCoords)) {
             return false;
         }
     }
-
-    SkASSERT(0 == memcmp(this->fFixedFunctionVertexAttribIndices,
-                         that.fFixedFunctionVertexAttribIndices,
-                         sizeof(this->fFixedFunctionVertexAttribIndices)));
-
     return true;
 }
 
diff --git a/src/gpu/GrOptDrawState.h b/src/gpu/GrOptDrawState.h
index f47913a..f495b89 100644
--- a/src/gpu/GrOptDrawState.h
+++ b/src/gpu/GrOptDrawState.h
@@ -11,11 +11,13 @@
 #include "GrColor.h"
 #include "GrGpu.h"
 #include "GrProcessorStage.h"
+#include "GrProgramDesc.h"
 #include "GrStencil.h"
 #include "GrTypesPriv.h"
 #include "SkMatrix.h"
 #include "SkRefCnt.h"
 
+class GrDeviceCoordTexture;
 class GrDrawState;
 
 /**
@@ -30,8 +32,8 @@
      * GrOptDrawState. In all cases the GrOptDrawState is reffed and ownership is given to the
      * caller.
      */
-    static GrOptDrawState* Create(const GrDrawState& drawState, const GrDrawTargetCaps& caps,
-                                  GrGpu::DrawType drawType);
+    static GrOptDrawState* Create(const GrDrawState& drawState, GrGpu*,
+                                  const GrDeviceCoordTexture* dstCopy, GrGpu::DrawType drawType);
 
     bool operator== (const GrOptDrawState& that) const;
 
@@ -48,35 +50,6 @@
 
     size_t getVertexStride() const { return fVAStride; }
 
-    /**
-     * Getters for index into getVertexAttribs() for particular bindings. -1 is returned if the
-     * binding does not appear in the current attribs. These bindings should appear only once in
-     * the attrib array.
-     */
-
-    int positionAttributeIndex() const {
-        return fFixedFunctionVertexAttribIndices[kPosition_GrVertexAttribBinding];
-    }
-    int localCoordAttributeIndex() const {
-        return fFixedFunctionVertexAttribIndices[kLocalCoord_GrVertexAttribBinding];
-    }
-    int colorVertexAttributeIndex() const {
-        return fFixedFunctionVertexAttribIndices[kColor_GrVertexAttribBinding];
-    }
-    int coverageVertexAttributeIndex() const {
-        return fFixedFunctionVertexAttribIndices[kCoverage_GrVertexAttribBinding];
-    }
-
-    bool hasLocalCoordAttribute() const {
-        return -1 != fFixedFunctionVertexAttribIndices[kLocalCoord_GrVertexAttribBinding];
-    }
-    bool hasColorVertexAttribute() const {
-        return -1 != fFixedFunctionVertexAttribIndices[kColor_GrVertexAttribBinding];
-    }
-    bool hasCoverageVertexAttribute() const {
-        return -1 != fFixedFunctionVertexAttribIndices[kCoverage_GrVertexAttribBinding];
-    }
-
     /// @}
 
     ///////////////////////////////////////////////////////////////////////////
@@ -299,48 +272,10 @@
         kB_CombinedState,
     };
 
-    bool inputColorIsUsed() const { return fInputColorIsUsed; }
-    bool inputCoverageIsUsed() const { return fInputCoverageIsUsed; }
-
-    bool readsDst() const { return fReadsDst; }
-    bool readsFragPosition() const { return fReadsFragPosition; }
-    bool requiresLocalCoordAttrib() const { return fRequiresLocalCoordAttrib; }
-
-    ///////////////////////////////////////////////////////////////////////////
-    /// @name Stage Output Types
-    ////
-
-    enum PrimaryOutputType {
-        // Modulate color and coverage, write result as the color output.
-        kModulate_PrimaryOutputType,
-        // Combines the coverage, dst, and color as coverage * color + (1 - coverage) * dst. This
-        // can only be set if fDstReadKey is non-zero.
-        kCombineWithDst_PrimaryOutputType,
-
-        kPrimaryOutputTypeCnt,
-    };
-
-    enum SecondaryOutputType {
-        // There is no secondary output
-        kNone_SecondaryOutputType,
-        // Writes coverage as the secondary output. Only set if dual source blending is supported
-        // and primary output is kModulate.
-        kCoverage_SecondaryOutputType,
-        // Writes coverage * (1 - colorA) as the secondary output. Only set if dual source blending
-        // is supported and primary output is kModulate.
-        kCoverageISA_SecondaryOutputType,
-        // Writes coverage * (1 - colorRGBA) as the secondary output. Only set if dual source
-        // blending is supported and primary output is kModulate.
-        kCoverageISC_SecondaryOutputType,
-
-        kSecondaryOutputTypeCnt,
-    };
-
-    PrimaryOutputType getPrimaryOutputType() const { return fPrimaryOutputType; }
-    SecondaryOutputType getSecondaryOutputType() const { return fSecondaryOutputType; }
-
     /// @}
 
+    const GrProgramDesc& programDesc() const { return fDesc; }
+
 private:
     /**
      * Optimizations for blending / coverage to that can be applied based on the current state.
@@ -376,7 +311,7 @@
      */
     GrOptDrawState(const GrDrawState& drawState, BlendOptFlags blendOptFlags,
                    GrBlendCoeff optSrcCoeff, GrBlendCoeff optDstCoeff,
-                   const GrDrawTargetCaps& caps);
+                   GrGpu*, const GrDeviceCoordTexture* dstCopy, GrGpu::DrawType);
 
     /**
      * Loops through all the color stage effects to check if the stage will ignore color input or
@@ -384,35 +319,38 @@
      * stages. In the constant color case, we can ignore all previous stages and
      * the current one and set the state color to the constant color.
      */
-    void computeEffectiveColorStages(const GrDrawState& ds, int* firstColorStageIdx,
-                                     uint8_t* fixFunctionVAToRemove);
+    void computeEffectiveColorStages(const GrDrawState& ds, GrProgramDesc::DescInfo*,
+                                     int* firstColorStageIdx, uint8_t* fixFunctionVAToRemove);
 
     /**
      * Loops through all the coverage stage effects to check if the stage will ignore color input.
      * If a coverage stage will ignore input, then we can ignore all coverage stages before it. We
      * loop to determine the first effective coverage stage.
      */
-    void computeEffectiveCoverageStages(const GrDrawState& ds, int* firstCoverageStageIdx);
+    void computeEffectiveCoverageStages(const GrDrawState& ds, GrProgramDesc::DescInfo* descInfo,
+                                        int* firstCoverageStageIdx);
 
     /**
      * This function takes in a flag and removes the corresponding fixed function vertex attributes.
      * The flags are in the same order as GrVertexAttribBinding array. If bit i of removeVAFlags is
      * set, then vertex attributes with binding (GrVertexAttribute)i will be removed.
      */
-    void removeFixedFunctionVertexAttribs(uint8_t removeVAFlags);
+    void removeFixedFunctionVertexAttribs(uint8_t removeVAFlags, GrProgramDesc::DescInfo*);
 
     /**
      * Alter the OptDrawState (adjusting stages, vertex attribs, flags, etc.) based on the
      * BlendOptFlags.
      */
-    void adjustFromBlendOpts(const GrDrawState& ds, int* firstColorStageIdx,
-                             int* firstCoverageStageIdx, uint8_t* fixedFunctionVAToRemove);
+    void adjustFromBlendOpts(const GrDrawState& ds, GrProgramDesc::DescInfo*,
+                             int* firstColorStageIdx, int* firstCoverageStageIdx,
+                             uint8_t* fixedFunctionVAToRemove);
 
     /**
      * Loop over the effect stages to determine various info like what data they will read and what
      * shaders they require.
      */
-    void getStageStats(const GrDrawState& ds, int firstColorStageIdx, int firstCoverageStageIdx);
+    void getStageStats(const GrDrawState& ds, int firstColorStageIdx, int firstCoverageStageIdx,
+                       GrProgramDesc::DescInfo*);
 
     /**
      * Calculates the primary and secondary output types of the shader. For certain output types
@@ -420,7 +358,8 @@
      * blend coeffs will represent those used by backend API.
      */
     void setOutputStateInfo(const GrDrawState& ds, const GrDrawTargetCaps&,
-                            int firstCoverageStageIdx, bool* separateCoverageFromColor);
+                            int firstCoverageStageIdx, GrProgramDesc::DescInfo*,
+                            bool* separateCoverageFromColor);
 
     bool isEqual(const GrOptDrawState& that) const;
 
@@ -448,27 +387,11 @@
     // This function is equivalent to the offset into fFragmentStages where coverage stages begin.
     int                                 fNumColorStages;
 
-    // This is simply a different representation of info in fVertexAttribs and thus does
-    // not need to be compared in op==.
-    int fFixedFunctionVertexAttribIndices[kGrFixedFunctionVertexAttribBindingCnt];
-
-    // These flags are needed to protect the code from creating an unused uniform color/coverage
-    // which will cause shader compiler errors.
-    bool            fInputColorIsUsed;
-    bool            fInputCoverageIsUsed;
-
-    // These flags give aggregated info on the effect stages that are used when building programs.
-    bool            fReadsDst;
-    bool            fReadsFragPosition;
-    bool            fRequiresLocalCoordAttrib;
-
     SkAutoSTArray<4, GrVertexAttrib> fOptVA;
 
     BlendOptFlags   fBlendOptFlags;
 
-    // Fragment shader color outputs
-    PrimaryOutputType  fPrimaryOutputType : 8;
-    SecondaryOutputType  fSecondaryOutputType : 8;
+    GrProgramDesc fDesc;
 
     typedef SkRefCnt INHERITED;
 };
diff --git a/src/gpu/GrProgramDesc.h b/src/gpu/GrProgramDesc.h
new file mode 100644
index 0000000..cc8a38a
--- /dev/null
+++ b/src/gpu/GrProgramDesc.h
@@ -0,0 +1,269 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrProgramDesc_DEFINED
+#define GrProgramDesc_DEFINED
+
+#include "GrBackendProcessorFactory.h"
+#include "GrColor.h"
+#include "GrTypesPriv.h"
+#include "SkChecksum.h"
+
+class GrGpuGL;
+
+/** This class describes a program to generate. It also serves as a program cache key. Very little
+    of this is GL-specific. The GL-specific parts could be factored out into a subclass. */
+class GrProgramDesc {
+public:
+    // Creates an uninitialized key that must be populated by GrGpu::buildProgramDesc()
+    GrProgramDesc() {}
+
+    // Returns this as a uint32_t array to be used as a key in the program cache.
+    const uint32_t* asKey() const {
+        return reinterpret_cast<const uint32_t*>(fKey.begin());
+    }
+
+    // Gets the number of bytes in asKey(). It will be a 4-byte aligned value. When comparing two
+    // keys the size of either key can be used with memcmp() since the lengths themselves begin the
+    // keys and thus the memcmp will exit early if the keys are of different lengths.
+    uint32_t keyLength() const { return *this->atOffset<uint32_t, kLengthOffset>(); }
+
+    // Gets the a checksum of the key. Can be used as a hash value for a fast lookup in a cache.
+    uint32_t getChecksum() const { return *this->atOffset<uint32_t, kChecksumOffset>(); }
+
+    GrProgramDesc& operator= (const GrProgramDesc& other) {
+        size_t keyLength = other.keyLength();
+        fKey.reset(keyLength);
+        memcpy(fKey.begin(), other.fKey.begin(), keyLength);
+        return *this;
+    }
+
+    bool operator== (const GrProgramDesc& other) const {
+        // The length is masked as a hint to the compiler that the address will be 4 byte aligned.
+        return 0 == memcmp(this->asKey(), other.asKey(), this->keyLength() & ~0x3);
+    }
+
+    bool operator!= (const GrProgramDesc& other) const {
+        return !(*this == other);
+    }
+
+    static bool Less(const GrProgramDesc& a, const GrProgramDesc& b) {
+        return memcmp(a.asKey(), b.asKey(), a.keyLength() & ~0x3) < 0;
+    }
+
+
+    ///////////////////////////////////////////////////////////////////////////
+    /// @name Stage Output Types
+    ////
+
+    enum PrimaryOutputType {
+        // Modulate color and coverage, write result as the color output.
+        kModulate_PrimaryOutputType,
+        // Combines the coverage, dst, and color as coverage * color + (1 - coverage) * dst. This
+        // can only be set if fDstReadKey is non-zero.
+        kCombineWithDst_PrimaryOutputType,
+
+        kPrimaryOutputTypeCnt,
+    };
+
+    enum SecondaryOutputType {
+        // There is no secondary output
+        kNone_SecondaryOutputType,
+        // Writes coverage as the secondary output. Only set if dual source blending is supported
+        // and primary output is kModulate.
+        kCoverage_SecondaryOutputType,
+        // Writes coverage * (1 - colorA) as the secondary output. Only set if dual source blending
+        // is supported and primary output is kModulate.
+        kCoverageISA_SecondaryOutputType,
+        // Writes coverage * (1 - colorRGBA) as the secondary output. Only set if dual source
+        // blending is supported and primary output is kModulate.
+        kCoverageISC_SecondaryOutputType,
+
+        kSecondaryOutputTypeCnt,
+    };
+
+    // Specifies where the initial color comes from before the stages are applied.
+    enum ColorInput {
+        kAllOnes_ColorInput,
+        kAttribute_ColorInput,
+        kUniform_ColorInput,
+
+        kColorInputCnt
+    };
+
+    struct KeyHeader {
+        uint8_t                     fDstReadKey;   // set by GrGLShaderBuilder if there
+                                                   // are effects that must read the dst.
+                                                   // Otherwise, 0.
+        uint8_t                     fFragPosKey;   // set by GrGLShaderBuilder if there are
+                                                   // effects that read the fragment position.
+                                                   // Otherwise, 0.
+
+        SkBool8                     fEmitsPointSize;
+
+        ColorInput                  fColorInput : 8;
+        ColorInput                  fCoverageInput : 8;
+
+        PrimaryOutputType           fPrimaryOutputType : 8;
+        SecondaryOutputType         fSecondaryOutputType : 8;
+
+        int8_t                      fPositionAttributeIndex;
+        int8_t                      fLocalCoordAttributeIndex;
+        int8_t                      fColorAttributeIndex;
+        int8_t                      fCoverageAttributeIndex;
+
+        SkBool8                     fHasGeometryProcessor;
+        int8_t                      fColorEffectCnt;
+        int8_t                      fCoverageEffectCnt;
+    };
+
+
+    bool hasGeometryProcessor() const {
+        return SkToBool(this->header().fHasGeometryProcessor);
+    }
+
+    int numColorEffects() const {
+        return this->header().fColorEffectCnt;
+    }
+
+    int numCoverageEffects() const {
+        return this->header().fCoverageEffectCnt;
+    }
+
+    int numTotalEffects() const { return this->numColorEffects() + this->numCoverageEffects(); }
+
+    // This should really only be used internally, base classes should return their own headers
+    const KeyHeader& header() const { return *this->atOffset<KeyHeader, kHeaderOffset>(); }
+
+    /** Used to provide effects' keys to their emitCode() function. */
+    class ProcKeyProvider {
+    public:
+        enum ProcessorType {
+            kGeometry_ProcessorType,
+            kFragment_ProcessorType,
+        };
+
+        ProcKeyProvider(const GrProgramDesc* desc, ProcessorType type, int effectOffset)
+            : fDesc(desc), fBaseIndex(0), fEffectOffset(effectOffset) {
+            switch (type) {
+                case kGeometry_ProcessorType:
+                    // there can be only one
+                    fBaseIndex = 0;
+                    break;
+                case kFragment_ProcessorType:
+                    fBaseIndex = desc->hasGeometryProcessor() ? 1 : 0;
+                    break;
+            }
+        }
+
+        GrProcessorKey get(int index) const {
+            const uint16_t* offsetsAndLengths = reinterpret_cast<const uint16_t*>(
+                fDesc->fKey.begin() + fEffectOffset);
+            // We store two uint16_ts per effect, one for the offset to the effect's key and one for
+            // its length. Here we just need the offset.
+            uint16_t offset = offsetsAndLengths[2 * (fBaseIndex + index) + 0];
+            uint16_t length = offsetsAndLengths[2 * (fBaseIndex + index) + 1];
+            // Currently effects must add to the key in units of uint32_t.
+            SkASSERT(0 == (length % sizeof(uint32_t)));
+            return GrProcessorKey(reinterpret_cast<const uint32_t*>(fDesc->fKey.begin() + offset),
+                               length / sizeof(uint32_t));
+        }
+    private:
+        const GrProgramDesc*  fDesc;
+        int                   fBaseIndex;
+        int                   fEffectOffset;
+    };
+
+    // A struct to communicate descriptor information to the program descriptor builder
+    struct DescInfo {
+        int positionAttributeIndex() const {
+            return fFixedFunctionVertexAttribIndices[kPosition_GrVertexAttribBinding];
+        }
+        int localCoordAttributeIndex() const {
+            return fFixedFunctionVertexAttribIndices[kLocalCoord_GrVertexAttribBinding];
+        }
+        int colorVertexAttributeIndex() const {
+            return fFixedFunctionVertexAttribIndices[kColor_GrVertexAttribBinding];
+        }
+        int coverageVertexAttributeIndex() const {
+            return fFixedFunctionVertexAttribIndices[kCoverage_GrVertexAttribBinding];
+        }
+        bool hasLocalCoordAttribute() const {
+            return -1 != fFixedFunctionVertexAttribIndices[kLocalCoord_GrVertexAttribBinding];
+        }
+        bool hasColorVertexAttribute() const {
+            return -1 != fFixedFunctionVertexAttribIndices[kColor_GrVertexAttribBinding];
+        }
+        bool hasCoverageVertexAttribute() const {
+            return -1 != fFixedFunctionVertexAttribIndices[kCoverage_GrVertexAttribBinding];
+        }
+
+        int fFixedFunctionVertexAttribIndices[kGrFixedFunctionVertexAttribBindingCnt];
+
+        // These flags are needed to protect the code from creating an unused uniform color/coverage
+        // which will cause shader compiler errors.
+        bool            fInputColorIsUsed;
+        bool            fInputCoverageIsUsed;
+
+        // These flags give aggregated info on the processor stages that are used when building
+        // programs.
+        bool            fReadsDst;
+        bool            fReadsFragPosition;
+        bool            fRequiresLocalCoordAttrib;
+
+        // Fragment shader color outputs
+        GrProgramDesc::PrimaryOutputType  fPrimaryOutputType : 8;
+        GrProgramDesc::SecondaryOutputType  fSecondaryOutputType : 8;
+    };
+
+private:
+    template<typename T, size_t OFFSET> T* atOffset() {
+        return reinterpret_cast<T*>(reinterpret_cast<intptr_t>(fKey.begin()) + OFFSET);
+    }
+
+    template<typename T, size_t OFFSET> const T* atOffset() const {
+        return reinterpret_cast<const T*>(reinterpret_cast<intptr_t>(fKey.begin()) + OFFSET);
+    }
+
+    void finalize() {
+        int keyLength = fKey.count();
+        SkASSERT(0 == (keyLength % 4));
+        *(this->atOffset<uint32_t, GrProgramDesc::kLengthOffset>()) = SkToU32(keyLength);
+
+        uint32_t* checksum = this->atOffset<uint32_t, GrProgramDesc::kChecksumOffset>();
+        *checksum = 0;
+        *checksum = SkChecksum::Compute(reinterpret_cast<uint32_t*>(fKey.begin()), keyLength);
+    }
+
+    // The key, stored in fKey, is composed of four parts:
+    // 1. uint32_t for total key length.
+    // 2. uint32_t for a checksum.
+    // 3. Header struct defined above.  Also room for extensions to the header
+    // 4. A Backend specific payload.  Room is preallocated for this
+    enum KeyOffsets {
+        // Part 1.
+        kLengthOffset = 0,
+        // Part 2.
+        kChecksumOffset = kLengthOffset + sizeof(uint32_t),
+        // Part 3.
+        kHeaderOffset = kChecksumOffset + sizeof(uint32_t),
+        kHeaderSize = SkAlign4(2 * sizeof(KeyHeader)),
+    };
+
+    enum {
+        kMaxPreallocProcessors = 8,
+        kIntsPerProcessor      = 4,    // This is an overestimate of the average effect key size.
+        kPreAllocSize = kHeaderOffset + kHeaderSize +
+                        kMaxPreallocProcessors * sizeof(uint32_t) * kIntsPerProcessor,
+    };
+
+    SkSTArray<kPreAllocSize, uint8_t, true> fKey;
+
+    friend class GrGLProgramDescBuilder;
+};
+
+#endif
diff --git a/src/gpu/gl/GrGLProgram.cpp b/src/gpu/gl/GrGLProgram.cpp
index bfa5f3c..f152173 100644
--- a/src/gpu/gl/GrGLProgram.cpp
+++ b/src/gpu/gl/GrGLProgram.cpp
@@ -56,7 +56,7 @@
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
 GrGLProgram::GrGLProgram(GrGpuGL* gpu,
-                         const GrGLProgramDesc& desc,
+                         const GrProgramDesc& desc,
                          const BuiltinUniformHandles& builtinUniforms,
                          GrGLuint programID,
                          const UniformInfoArray& uniforms,
@@ -201,12 +201,12 @@
 }
 
 void GrGLProgram::setColor(const GrOptDrawState& optState, GrColor color) {
-    const GrGLProgramDesc::KeyHeader& header = fDesc.getHeader();
+    const GrProgramDesc::KeyHeader& header = fDesc.header();
     switch (header.fColorInput) {
-        case GrGLProgramDesc::kAttribute_ColorInput:
+        case GrProgramDesc::kAttribute_ColorInput:
             // Attribute case is handled in GrGpuGL::setupGeometry
             break;
-        case GrGLProgramDesc::kUniform_ColorInput:
+        case GrProgramDesc::kUniform_ColorInput:
             if (fColor != color && fBuiltinUniformHandles.fColorUni.isValid()) {
                 // OpenGL ES doesn't support unsigned byte varieties of glUniform
                 GrGLfloat c[4];
@@ -215,7 +215,7 @@
                 fColor = color;
             }
             break;
-        case GrGLProgramDesc::kAllOnes_ColorInput:
+        case GrProgramDesc::kAllOnes_ColorInput:
             // Handled by shader creation
             break;
         default:
@@ -224,12 +224,12 @@
 }
 
 void GrGLProgram::setCoverage(const GrOptDrawState& optState, GrColor coverage) {
-    const GrGLProgramDesc::KeyHeader& header = fDesc.getHeader();
+    const GrProgramDesc::KeyHeader& header = fDesc.header();
     switch (header.fCoverageInput) {
-        case GrGLProgramDesc::kAttribute_ColorInput:
+        case GrProgramDesc::kAttribute_ColorInput:
             // Attribute case is handled in GrGpuGL::setupGeometry
             break;
-        case GrGLProgramDesc::kUniform_ColorInput:
+        case GrProgramDesc::kUniform_ColorInput:
             if (fCoverage != coverage) {
                 // OpenGL ES doesn't support unsigned byte varieties of glUniform
                 GrGLfloat c[4];
@@ -238,7 +238,7 @@
                 fCoverage = coverage;
             }
             break;
-        case GrGLProgramDesc::kAllOnes_ColorInput:
+        case GrProgramDesc::kAllOnes_ColorInput:
             // Handled by shader creation
             break;
         default:
@@ -286,7 +286,7 @@
 /////////////////////////////////////////////////////////////////////////////////////////
 
 GrGLNvprProgramBase::GrGLNvprProgramBase(GrGpuGL* gpu,
-                                         const GrGLProgramDesc& desc,
+                                         const GrProgramDesc& desc,
                                          const BuiltinUniformHandles& builtinUniforms,
                                          GrGLuint programID,
                                          const UniformInfoArray& uniforms,
@@ -306,7 +306,7 @@
 /////////////////////////////////////////////////////////////////////////////////////////
 
 GrGLNvprProgram::GrGLNvprProgram(GrGpuGL* gpu,
-                                 const GrGLProgramDesc& desc,
+                                 const GrProgramDesc& desc,
                                  const BuiltinUniformHandles& builtinUniforms,
                                  GrGLuint programID,
                                  const UniformInfoArray& uniforms,
@@ -355,7 +355,7 @@
 //////////////////////////////////////////////////////////////////////////////////////
 
 GrGLLegacyNvprProgram::GrGLLegacyNvprProgram(GrGpuGL* gpu,
-                                             const GrGLProgramDesc& desc,
+                                             const GrProgramDesc& desc,
                                              const BuiltinUniformHandles& builtinUniforms,
                                              GrGLuint programID,
                                              const UniformInfoArray& uniforms,
diff --git a/src/gpu/gl/GrGLProgram.h b/src/gpu/gl/GrGLProgram.h
index ca75e20..d7a7cf8 100644
--- a/src/gpu/gl/GrGLProgram.h
+++ b/src/gpu/gl/GrGLProgram.h
@@ -47,7 +47,7 @@
      */
     void abandon();
 
-    const GrGLProgramDesc& getDesc() { return fDesc; }
+    const GrProgramDesc& getDesc() { return fDesc; }
 
     /**
      * Gets the GL program ID for this program.
@@ -138,7 +138,7 @@
     typedef GrGLProgramDataManager::UniformInfoArray UniformInfoArray;
 
     GrGLProgram(GrGpuGL*,
-                const GrGLProgramDesc&,
+                const GrProgramDesc&,
                 const BuiltinUniformHandles&,
                 GrGLuint programID,
                 const UniformInfoArray&,
@@ -184,7 +184,7 @@
     SkAutoTDelete<GrGLInstalledGeoProc> fGeometryProcessor;
     SkAutoTUnref<GrGLInstalledFragProcs> fFragmentProcessors;
 
-    GrGLProgramDesc fDesc;
+    GrProgramDesc fDesc;
     GrGpuGL* fGpu;
     GrGLProgramDataManager fProgramDataManager;
 
@@ -203,7 +203,7 @@
 class GrGLNvprProgramBase : public GrGLProgram {
 protected:
     GrGLNvprProgramBase(GrGpuGL*,
-                        const GrGLProgramDesc&,
+                        const GrProgramDesc&,
                         const BuiltinUniformHandles&,
                         GrGLuint programID,
                         const UniformInfoArray&,
@@ -221,7 +221,7 @@
     typedef GrGLNvprProgramBuilder::SeparableVaryingInfo SeparableVaryingInfo;
     typedef GrGLNvprProgramBuilder::SeparableVaryingInfoArray SeparableVaryingInfoArray;
     GrGLNvprProgram(GrGpuGL*,
-                    const GrGLProgramDesc&,
+                    const GrProgramDesc&,
                     const BuiltinUniformHandles&,
                     GrGLuint programID,
                     const UniformInfoArray&,
@@ -249,7 +249,7 @@
 
 private:
     GrGLLegacyNvprProgram(GrGpuGL* gpu,
-                          const GrGLProgramDesc& desc,
+                          const GrProgramDesc& desc,
                           const BuiltinUniformHandles&,
                           GrGLuint programID,
                           const UniformInfoArray&,
diff --git a/src/gpu/gl/GrGLProgramDesc.cpp b/src/gpu/gl/GrGLProgramDesc.cpp
index 4a274c6..4a182b3 100644
--- a/src/gpu/gl/GrGLProgramDesc.cpp
+++ b/src/gpu/gl/GrGLProgramDesc.cpp
@@ -4,15 +4,15 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
-
-#include "gl/builders/GrGLFragmentShaderBuilder.h"
 #include "GrGLProgramDesc.h"
+
+#include "GrGLProcessor.h"
 #include "GrBackendProcessorFactory.h"
 #include "GrProcessor.h"
 #include "GrGpuGL.h"
 #include "GrOptDrawState.h"
-
 #include "SkChecksum.h"
+#include "gl/builders/GrGLFragmentShaderBuilder.h"
 
 /**
  * The key for an individual coord transform is made up of a matrix type and a bit that
@@ -176,10 +176,10 @@
 
 template <class ProcessorKeyBuilder>
 bool
-GrGLProgramDesc::BuildStagedProcessorKey(const typename ProcessorKeyBuilder::StagedProcessor& stage,
+GrGLProgramDescBuilder::BuildStagedProcessorKey(const typename ProcessorKeyBuilder::StagedProcessor& stage,
                                          const GrGLCaps& caps,
                                          bool requiresLocalCoordAttrib,
-                                         GrGLProgramDesc* desc,
+                                         GrProgramDesc* desc,
                                          int* offsetAndSizeIndex) {
     GrProcessorKeyBuilder b(&desc->fKey);
     uint16_t processorKeySize;
@@ -192,7 +192,7 @@
     }
 
     uint16_t* offsetAndSize =
-            reinterpret_cast<uint16_t*>(desc->fKey.begin() + kEffectKeyOffsetsAndLengthOffset +
+            reinterpret_cast<uint16_t*>(desc->fKey.begin() + kProcessorKeyOffsetsAndLengthOffset +
                                         *offsetAndSizeIndex * 2 * sizeof(uint16_t));
     offsetAndSize[0] = SkToU16(processorOffset);
     offsetAndSize[1] = processorKeySize;
@@ -200,27 +200,28 @@
     return true;
 }
 
-bool GrGLProgramDesc::Build(const GrOptDrawState& optState,
+bool GrGLProgramDescBuilder::Build(const GrOptDrawState& optState,
+                            const GrProgramDesc::DescInfo& descInfo,
                             GrGpu::DrawType drawType,
                             GrGpuGL* gpu,
                             const GrDeviceCoordTexture* dstCopy,
-                            GrGLProgramDesc* desc) {
-    bool inputColorIsUsed = optState.inputColorIsUsed();
-    bool inputCoverageIsUsed = optState.inputCoverageIsUsed();
+                            GrProgramDesc* desc) {
+    bool inputColorIsUsed = descInfo.fInputColorIsUsed;
+    bool inputCoverageIsUsed = descInfo.fInputCoverageIsUsed;
 
     // The descriptor is used as a cache key. Thus when a field of the
     // descriptor will not affect program generation (because of the attribute
     // bindings in use or other descriptor field settings) it should be set
     // to a canonical value to avoid duplicate programs with different keys.
 
-    bool requiresLocalCoordAttrib = optState.requiresLocalCoordAttrib();
+    bool requiresLocalCoordAttrib = descInfo.fRequiresLocalCoordAttrib;
 
     int numStages = optState.numTotalStages();
 
-    GR_STATIC_ASSERT(0 == kEffectKeyOffsetsAndLengthOffset % sizeof(uint32_t));
+    GR_STATIC_ASSERT(0 == kProcessorKeyOffsetsAndLengthOffset % sizeof(uint32_t));
     // Make room for everything up to and including the array of offsets to effect keys.
     desc->fKey.reset();
-    desc->fKey.push_back_n(kEffectKeyOffsetsAndLengthOffset + 2 * sizeof(uint16_t) * numStages);
+    desc->fKey.push_back_n(kProcessorKeyOffsetsAndLengthOffset + 2 * sizeof(uint16_t) * numStages);
 
     int offsetAndSizeIndex = 0;
 
@@ -248,7 +249,7 @@
     // --------DO NOT MOVE HEADER ABOVE THIS LINE--------------------------------------------------
     // Because header is a pointer into the dynamic array, we can't push any new data into the key
     // below here.
-    KeyHeader* header = desc->header();
+    GLKeyHeader* header = desc->atOffset<GLKeyHeader, kHeaderOffset>();
 
     // make sure any padding in the header is zeroed.
     memset(header, 0, kHeaderSize);
@@ -266,33 +267,33 @@
     }
 
     bool hasUniformColor = inputColorIsUsed &&
-                           (isPathRendering || !optState.hasColorVertexAttribute());
+                           (isPathRendering || !descInfo.hasColorVertexAttribute());
 
     bool hasUniformCoverage = inputCoverageIsUsed &&
-                              (isPathRendering || !optState.hasCoverageVertexAttribute());
+                              (isPathRendering || !descInfo.hasCoverageVertexAttribute());
 
     if (!inputColorIsUsed) {
-        header->fColorInput = kAllOnes_ColorInput;
+        header->fColorInput = GrProgramDesc::kAllOnes_ColorInput;
     } else if (hasUniformColor) {
-        header->fColorInput = kUniform_ColorInput;
+        header->fColorInput = GrProgramDesc::kUniform_ColorInput;
     } else {
-        header->fColorInput = kAttribute_ColorInput;
+        header->fColorInput = GrProgramDesc::kAttribute_ColorInput;
         SkASSERT(!header->fUseNvpr);
     }
 
-    bool covIsSolidWhite = !optState.hasCoverageVertexAttribute() &&
+    bool covIsSolidWhite = !descInfo.hasCoverageVertexAttribute() &&
                            0xffffffff == optState.getCoverageColor();
 
     if (covIsSolidWhite || !inputCoverageIsUsed) {
-        header->fCoverageInput = kAllOnes_ColorInput;
+        header->fCoverageInput = GrProgramDesc::kAllOnes_ColorInput;
     } else if (hasUniformCoverage) {
-        header->fCoverageInput = kUniform_ColorInput;
+        header->fCoverageInput = GrProgramDesc::kUniform_ColorInput;
     } else {
-        header->fCoverageInput = kAttribute_ColorInput;
+        header->fCoverageInput = GrProgramDesc::kAttribute_ColorInput;
         SkASSERT(!header->fUseNvpr);
     }
 
-    if (optState.readsDst()) {
+    if (descInfo.fReadsDst) {
         SkASSERT(dstCopy || gpu->caps()->dstReadInShaderSupport());
         const GrTexture* dstCopyTexture = NULL;
         if (dstCopy) {
@@ -305,7 +306,7 @@
         header->fDstReadKey = 0;
     }
 
-    if (optState.readsFragPosition()) {
+    if (descInfo.fReadsFragPosition) {
         header->fFragPosKey =
                 GrGLFragmentShaderBuilder::KeyForFragmentPosition(optState.getRenderTarget(),
                                                                   gpu->glCaps());
@@ -314,14 +315,14 @@
     }
 
     // Record attribute indices
-    header->fPositionAttributeIndex = optState.positionAttributeIndex();
-    header->fLocalCoordAttributeIndex = optState.localCoordAttributeIndex();
+    header->fPositionAttributeIndex = descInfo.positionAttributeIndex();
+    header->fLocalCoordAttributeIndex = descInfo.localCoordAttributeIndex();
 
     // For constant color and coverage we need an attribute with an index beyond those already set
     int availableAttributeIndex = optState.getVertexAttribCount();
-    if (optState.hasColorVertexAttribute()) {
-        header->fColorAttributeIndex = optState.colorVertexAttributeIndex();
-    } else if (GrGLProgramDesc::kAttribute_ColorInput == header->fColorInput) {
+    if (descInfo.hasColorVertexAttribute()) {
+        header->fColorAttributeIndex = descInfo.colorVertexAttributeIndex();
+    } else if (GrProgramDesc::kAttribute_ColorInput == header->fColorInput) {
         SkASSERT(availableAttributeIndex < GrDrawState::kMaxVertexAttribCnt);
         header->fColorAttributeIndex = availableAttributeIndex;
         availableAttributeIndex++;
@@ -329,37 +330,20 @@
         header->fColorAttributeIndex = -1;
     }
 
-    if (optState.hasCoverageVertexAttribute()) {
-        header->fCoverageAttributeIndex = optState.coverageVertexAttributeIndex();
-    } else if (GrGLProgramDesc::kAttribute_ColorInput == header->fCoverageInput) {
+    if (descInfo.hasCoverageVertexAttribute()) {
+        header->fCoverageAttributeIndex = descInfo.coverageVertexAttributeIndex();
+    } else if (GrProgramDesc::kAttribute_ColorInput == header->fCoverageInput) {
         SkASSERT(availableAttributeIndex < GrDrawState::kMaxVertexAttribCnt);
         header->fCoverageAttributeIndex = availableAttributeIndex;
     } else {
         header->fCoverageAttributeIndex = -1;
     }
 
-    header->fPrimaryOutputType = optState.getPrimaryOutputType();
-    header->fSecondaryOutputType = optState.getSecondaryOutputType();
+    header->fPrimaryOutputType = descInfo.fPrimaryOutputType;
+    header->fSecondaryOutputType = descInfo.fSecondaryOutputType;
 
     header->fColorEffectCnt = optState.numColorStages();
     header->fCoverageEffectCnt = optState.numCoverageStages();
     desc->finalize();
     return true;
 }
-
-void GrGLProgramDesc::finalize() {
-    int keyLength = fKey.count();
-    SkASSERT(0 == (keyLength % 4));
-    *this->atOffset<uint32_t, kLengthOffset>() = SkToU32(keyLength);
-
-    uint32_t* checksum = this->atOffset<uint32_t, kChecksumOffset>();
-    *checksum = 0;
-    *checksum = SkChecksum::Compute(reinterpret_cast<uint32_t*>(fKey.begin()), keyLength);
-}
-
-GrGLProgramDesc& GrGLProgramDesc::operator= (const GrGLProgramDesc& other) {
-    size_t keyLength = other.keyLength();
-    fKey.reset(keyLength);
-    memcpy(fKey.begin(), other.fKey.begin(), keyLength);
-    return *this;
-}
diff --git a/src/gpu/gl/GrGLProgramDesc.h b/src/gpu/gl/GrGLProgramDesc.h
index d97bdfd..8bfd506 100644
--- a/src/gpu/gl/GrGLProgramDesc.h
+++ b/src/gpu/gl/GrGLProgramDesc.h
@@ -8,112 +8,24 @@
 #ifndef GrGLProgramDesc_DEFINED
 #define GrGLProgramDesc_DEFINED
 
-#include "GrGLProcessor.h"
-#include "GrDrawState.h"
+#include "GrColor.h"
+#include "GrProgramDesc.h"
 #include "GrGpu.h"
-#include "GrOptDrawState.h"
+#include "GrTypesPriv.h"
 
 class GrGpuGL;
 
-/** This class describes a program to generate. It also serves as a program cache key. Very little
-    of this is GL-specific. The GL-specific parts could be factored out into a subclass. */
-class GrGLProgramDesc {
+/**
+ * This class can be used to build a GrProgramDesc.  It also provides helpers for accessing
+ * GL specific info in the header.
+ */
+class GrGLProgramDescBuilder {
 public:
-    GrGLProgramDesc() {}
-    GrGLProgramDesc(const GrGLProgramDesc& desc) { *this = desc; }
-
-    // Returns this as a uint32_t array to be used as a key in the program cache.
-    const uint32_t* asKey() const {
-        return reinterpret_cast<const uint32_t*>(fKey.begin());
-    }
-
-    // Gets the number of bytes in asKey(). It will be a 4-byte aligned value. When comparing two
-    // keys the size of either key can be used with memcmp() since the lengths themselves begin the
-    // keys and thus the memcmp will exit early if the keys are of different lengths.
-    uint32_t keyLength() const { return *this->atOffset<uint32_t, kLengthOffset>(); }
-
-    // Gets the a checksum of the key. Can be used as a hash value for a fast lookup in a cache.
-    uint32_t getChecksum() const { return *this->atOffset<uint32_t, kChecksumOffset>(); }
-
-    /**
-     * Builds a program descriptor from a GrOptDrawState. Whether the primitive type is points, and
-     * the caps of the GrGpuGL are also inputs. It also outputs the color and coverage stages
-     * referenced by the generated descriptor. Coverage stages from the drawState may be treated as
-     * color stages in the output.
-     */
-    static bool Build(const GrOptDrawState&,
-                      GrGpu::DrawType,
-                      GrGpuGL*,
-                      const GrDeviceCoordTexture*,
-                      GrGLProgramDesc*);
-
-    bool hasGeometryProcessor() const {
-        return SkToBool(this->getHeader().fHasGeometryProcessor);
-    }
-
-    int numColorEffects() const {
-        return this->getHeader().fColorEffectCnt;
-    }
-
-    int numCoverageEffects() const {
-        return this->getHeader().fCoverageEffectCnt;
-    }
-
-    int numTotalEffects() const { return this->numColorEffects() + this->numCoverageEffects(); }
-
-    GrGLProgramDesc& operator= (const GrGLProgramDesc& other);
-
-    bool operator== (const GrGLProgramDesc& other) const {
-        // The length is masked as a hint to the compiler that the address will be 4 byte aligned.
-        return 0 == memcmp(this->asKey(), other.asKey(), this->keyLength() & ~0x3);
-    }
-
-    bool operator!= (const GrGLProgramDesc& other) const {
-        return !(*this == other);
-    }
-
-    static bool Less(const GrGLProgramDesc& a, const GrGLProgramDesc& b) {
-        return memcmp(a.asKey(), b.asKey(), a.keyLength() & ~0x3) < 0;
-    }
-
-private:
-    // Specifies where the initial color comes from before the stages are applied.
-    enum ColorInput {
-        kAllOnes_ColorInput,
-        kAttribute_ColorInput,
-        kUniform_ColorInput,
-
-        kColorInputCnt
+    struct GLKeyHeader : public GrProgramDesc::KeyHeader {
+        SkBool8 fUseNvpr;
     };
 
-    struct KeyHeader {
-        uint8_t                          fDstReadKey;   // set by GrGLShaderBuilder if there
-                                                        // are effects that must read the dst.
-                                                        // Otherwise, 0.
-        uint8_t                          fFragPosKey;   // set by GrGLShaderBuilder if there are
-                                                        // effects that read the fragment position.
-                                                        // Otherwise, 0.
-
-        SkBool8                     fUseNvpr;
-        SkBool8                     fEmitsPointSize;
-
-        ColorInput                       fColorInput : 8;
-        ColorInput                       fCoverageInput : 8;
-
-        GrOptDrawState::PrimaryOutputType    fPrimaryOutputType : 8;
-        GrOptDrawState::SecondaryOutputType  fSecondaryOutputType : 8;
-
-        int8_t                      fPositionAttributeIndex;
-        int8_t                      fLocalCoordAttributeIndex;
-        int8_t                      fColorAttributeIndex;
-        int8_t                      fCoverageAttributeIndex;
-
-        SkBool8                     fHasGeometryProcessor;
-        int8_t                      fColorEffectCnt;
-        int8_t                      fCoverageEffectCnt;
-    };
-
-    // The key, stored in fKey, is composed of five parts:
+    // The key, stored in fKey, is composed of five parts(first 2 are defined in the key itself):
     // 1. uint32_t for total key length.
     // 2. uint32_t for a checksum.
     // 3. Header struct defined above.
@@ -121,95 +33,49 @@
     //    offset and size.
     // 5. per-effect keys. Each effect's key is a variable length array of uint32_t.
     enum {
-        // Part 1.
-        kLengthOffset = 0,
-        // Part 2.
-        kChecksumOffset = kLengthOffset + sizeof(uint32_t),
         // Part 3.
-        kHeaderOffset = kChecksumOffset + sizeof(uint32_t),
-        kHeaderSize = SkAlign4(sizeof(KeyHeader)),
+        kHeaderOffset = GrProgramDesc::kHeaderOffset,
+        kHeaderSize = SkAlign4(sizeof(GLKeyHeader)),
         // Part 4.
         // This is the offset in the overall key to the array of per-effect offset,length pairs.
-        kEffectKeyOffsetsAndLengthOffset = kHeaderOffset + kHeaderSize,
+        kProcessorKeyOffsetsAndLengthOffset = kHeaderOffset + kHeaderSize,
     };
 
-    template<typename T, size_t OFFSET> T* atOffset() {
-        return reinterpret_cast<T*>(reinterpret_cast<intptr_t>(fKey.begin()) + OFFSET);
+    /**
+     * Builds a GL specific program descriptor
+     *
+     * @param GrOptDrawState  The optimized drawstate.  The descriptor will represent a program
+     *                        which this optstate can use to draw with.  The optstate contains
+     *                        general draw information, as well as the specific color, geometry,
+     *                        and coverage stages which will be used to generate the GL Program for
+     *                        this optstate.
+     * @param DescInfo  A descriptor info struct, generated by the optstate, which contains a number
+     *                  of important facts about the program the built descriptor will represent
+     * @param DrawType
+     * @param GrGpuGL  A GL Gpu, the caps and Gpu object are used to output processor specific
+     *                 parts of the descriptor.
+     * @param GrDeviceCoordTexture  A dstCopy texture, which may be null if frame buffer fetch is
+     *                              supported
+     * @param GrProgramDesc  The built and finalized descriptor
+     **/
+    static bool Build(const GrOptDrawState&,
+                      const GrProgramDesc::DescInfo&,
+                      GrGpu::DrawType,
+                      GrGpuGL*,
+                      const GrDeviceCoordTexture*,
+                      GrProgramDesc*);
+
+    static const GLKeyHeader& GetHeader(const GrProgramDesc& desc) {
+        return *desc.atOffset<GLKeyHeader, kHeaderOffset>();
     }
 
-    template<typename T, size_t OFFSET> const T* atOffset() const {
-        return reinterpret_cast<const T*>(reinterpret_cast<intptr_t>(fKey.begin()) + OFFSET);
-    }
-
-    KeyHeader* header() { return this->atOffset<KeyHeader, kHeaderOffset>(); }
-
-    // a helper class to handle getting an individual processor's key
+private:
     template <class ProcessorKeyBuilder>
     static bool BuildStagedProcessorKey(const typename ProcessorKeyBuilder::StagedProcessor& stage,
                                         const GrGLCaps& caps,
                                         bool requiresLocalCoordAttrib,
-                                        GrGLProgramDesc* desc,
+                                        GrProgramDesc* desc,
                                         int* offsetAndSizeIndex);
-    void finalize();
-
-    const KeyHeader& getHeader() const { return *this->atOffset<KeyHeader, kHeaderOffset>(); }
-
-    /** Used to provide effects' keys to their emitCode() function. */
-    class ProcKeyProvider {
-    public:
-        enum ProcessorType {
-            kGeometry_ProcessorType,
-            kFragment_ProcessorType,
-        };
-
-        ProcKeyProvider(const GrGLProgramDesc* desc, ProcessorType type)
-            : fDesc(desc), fBaseIndex(0) {
-            switch (type) {
-                case kGeometry_ProcessorType:
-                    // there can be only one
-                    fBaseIndex = 0;
-                    break;
-                case kFragment_ProcessorType:
-                    fBaseIndex = desc->hasGeometryProcessor() ? 1 : 0;
-                    break;
-            }
-        }
-
-        GrProcessorKey get(int index) const {
-            const uint16_t* offsetsAndLengths = reinterpret_cast<const uint16_t*>(
-                fDesc->fKey.begin() + kEffectKeyOffsetsAndLengthOffset);
-            // We store two uint16_ts per effect, one for the offset to the effect's key and one for
-            // its length. Here we just need the offset.
-            uint16_t offset = offsetsAndLengths[2 * (fBaseIndex + index) + 0];
-            uint16_t length = offsetsAndLengths[2 * (fBaseIndex + index) + 1];
-            // Currently effects must add to the key in units of uint32_t.
-            SkASSERT(0 == (length % sizeof(uint32_t)));
-            return GrProcessorKey(reinterpret_cast<const uint32_t*>(fDesc->fKey.begin() + offset),
-                               length / sizeof(uint32_t));
-        }
-    private:
-        const GrGLProgramDesc*  fDesc;
-        int                     fBaseIndex;
-    };
-
-    enum {
-        kMaxPreallocEffects = 8,
-        kIntsPerEffect      = 4,    // This is an overestimate of the average effect key size.
-        kPreAllocSize = kEffectKeyOffsetsAndLengthOffset +
-                        kMaxPreallocEffects * sizeof(uint32_t) * kIntsPerEffect,
-    };
-
-    SkSTArray<kPreAllocSize, uint8_t, true> fKey;
-
-    // GrGLProgram and GrGLShaderBuilder read the private fields to generate code. TODO: Split out
-    // part of GrGLShaderBuilder that is used by effects so that this header doesn't need to be
-    // visible to GrGLProcessors. Then make public accessors as necessary and remove friends.
-    friend class GrGLProgram;
-    friend class GrGLProgramBuilder;
-    friend class GrGLLegacyNvprProgramBuilder;
-    friend class GrGLVertexBuilder;
-    friend class GrGLFragmentShaderBuilder;
-    friend class GrGLGeometryBuilder;
 };
 
 #endif
diff --git a/src/gpu/gl/GrGpuGL.cpp b/src/gpu/gl/GrGpuGL.cpp
index b9cb2da..88e7be0 100644
--- a/src/gpu/gl/GrGpuGL.cpp
+++ b/src/gpu/gl/GrGpuGL.cpp
@@ -2583,6 +2583,7 @@
         GL_CALL(PopGroupMarker());
     }
 }
+
 ///////////////////////////////////////////////////////////////////////////////
 
 GrGLAttribArrayState* GrGpuGL::HWGeometryState::bindArrayAndBuffersToDraw(
diff --git a/src/gpu/gl/GrGpuGL.h b/src/gpu/gl/GrGpuGL.h
index 808f97a..46d9700 100644
--- a/src/gpu/gl/GrGpuGL.h
+++ b/src/gpu/gl/GrGpuGL.h
@@ -19,6 +19,7 @@
 #include "GrGLVertexArray.h"
 #include "GrGLVertexBuffer.h"
 #include "GrGpu.h"
+#include "GrOptDrawState.h"
 #include "SkTypes.h"
 
 #ifdef SK_DEVELOPER
@@ -105,6 +106,12 @@
                                   const SkIRect& srcRect,
                                   const SkIPoint& dstPoint) SK_OVERRIDE;
 
+    virtual void buildProgramDesc(const GrOptDrawState&,
+                                  const GrProgramDesc::DescInfo&,
+                                  GrGpu::DrawType,
+                                  const GrDeviceCoordTexture* dstCopy,
+                                  GrProgramDesc*) SK_OVERRIDE;
+
 private:
     // GrGpu overrides
     virtual void onResetContext(uint32_t resetBits) SK_OVERRIDE;
@@ -181,9 +188,7 @@
         ~ProgramCache();
 
         void abandon();
-        GrGLProgram* getProgram(const GrOptDrawState&,
-                                const GrGLProgramDesc&,
-                                DrawType);
+        GrGLProgram* getProgram(const GrOptDrawState&, DrawType);
 
     private:
         enum {
@@ -199,7 +204,7 @@
 
         // binary search for entry matching desc. returns index into fEntries that matches desc or ~
         // of the index of where it should be inserted.
-        int search(const GrGLProgramDesc& desc) const;
+        int search(const GrProgramDesc& desc) const;
 
         // sorted array of all the entries
         Entry*                      fEntries[kMaxEntries];
diff --git a/src/gpu/gl/GrGpuGL_program.cpp b/src/gpu/gl/GrGpuGL_program.cpp
index a1259c9..23de7a8 100644
--- a/src/gpu/gl/GrGpuGL_program.cpp
+++ b/src/gpu/gl/GrGpuGL_program.cpp
@@ -31,14 +31,14 @@
 };
 
 struct GrGpuGL::ProgramCache::ProgDescLess {
-    bool operator() (const GrGLProgramDesc& desc, const Entry* entry) {
+    bool operator() (const GrProgramDesc& desc, const Entry* entry) {
         SkASSERT(entry->fProgram.get());
-        return GrGLProgramDesc::Less(desc, entry->fProgram->getDesc());
+        return GrProgramDesc::Less(desc, entry->fProgram->getDesc());
     }
 
-    bool operator() (const Entry* entry, const GrGLProgramDesc& desc) {
+    bool operator() (const Entry* entry, const GrProgramDesc& desc) {
         SkASSERT(entry->fProgram.get());
-        return GrGLProgramDesc::Less(entry->fProgram->getDesc(), desc);
+        return GrProgramDesc::Less(entry->fProgram->getDesc(), desc);
     }
 };
 
@@ -86,35 +86,33 @@
     fCount = 0;
 }
 
-int GrGpuGL::ProgramCache::search(const GrGLProgramDesc& desc) const {
+int GrGpuGL::ProgramCache::search(const GrProgramDesc& desc) const {
     ProgDescLess less;
     return SkTSearch(fEntries, fCount, desc, sizeof(Entry*), less);
 }
 
-GrGLProgram* GrGpuGL::ProgramCache::getProgram(const GrOptDrawState& optState,
-                                               const GrGLProgramDesc& desc,
-                                               DrawType type) {
+GrGLProgram* GrGpuGL::ProgramCache::getProgram(const GrOptDrawState& optState, DrawType type) {
 #ifdef PROGRAM_CACHE_STATS
     ++fTotalRequests;
 #endif
 
     Entry* entry = NULL;
 
-    uint32_t hashIdx = desc.getChecksum();
+    uint32_t hashIdx = optState.programDesc().getChecksum();
     hashIdx ^= hashIdx >> 16;
     if (kHashBits <= 8) {
         hashIdx ^= hashIdx >> 8;
     }
     hashIdx &=((1 << kHashBits) - 1);
     Entry* hashedEntry = fHashTable[hashIdx];
-    if (hashedEntry && hashedEntry->fProgram->getDesc() == desc) {
+    if (hashedEntry && hashedEntry->fProgram->getDesc() == optState.programDesc()) {
         SkASSERT(hashedEntry->fProgram);
         entry = hashedEntry;
     }
 
     int entryIdx;
     if (NULL == entry) {
-        entryIdx = this->search(desc);
+        entryIdx = this->search(optState.programDesc());
         if (entryIdx >= 0) {
             entry = fEntries[entryIdx];
 #ifdef PROGRAM_CACHE_STATS
@@ -128,7 +126,7 @@
 #ifdef PROGRAM_CACHE_STATS
         ++fCacheMisses;
 #endif
-        GrGLProgram* program = GrGLProgramBuilder::CreateProgram(optState, desc, type, fGpu);
+        GrGLProgram* program = GrGLProgramBuilder::CreateProgram(optState, type, fGpu);
         if (NULL == program) {
             return NULL;
         }
@@ -178,10 +176,10 @@
         SkASSERT(fEntries[0]->fProgram.get());
         for (int i = 0; i < fCount - 1; ++i) {
             SkASSERT(fEntries[i + 1]->fProgram.get());
-            const GrGLProgramDesc& a = fEntries[i]->fProgram->getDesc();
-            const GrGLProgramDesc& b = fEntries[i + 1]->fProgram->getDesc();
-            SkASSERT(GrGLProgramDesc::Less(a, b));
-            SkASSERT(!GrGLProgramDesc::Less(b, a));
+            const GrProgramDesc& a = fEntries[i]->fProgram->getDesc();
+            const GrProgramDesc& b = fEntries[i + 1]->fProgram->getDesc();
+            SkASSERT(GrProgramDesc::Less(a, b));
+            SkASSERT(!GrProgramDesc::Less(b, a));
         }
 #endif
     }
@@ -207,7 +205,8 @@
                                  const ScissorState& scissorState,
                                  const GrDeviceCoordTexture* dstCopy) {
     SkAutoTUnref<GrOptDrawState> optState(GrOptDrawState::Create(this->getDrawState(),
-                                                                 *this->caps(),
+                                                                 this,
+                                                                 dstCopy,
                                                                  type));
 
     if (!optState) {
@@ -234,13 +233,7 @@
             return false;
         }
 
-        GrGLProgramDesc 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));
+        fCurrentProgram.reset(fProgramCache->getProgram(*optState.get(), type));
         if (NULL == fCurrentProgram.get()) {
             SkDEBUGFAIL("Failed to create program!");
             return false;
@@ -279,7 +272,7 @@
 
 void GrGpuGL::setupGeometry(const DrawInfo& info, size_t* indexOffsetInBytes) {
     SkAutoTUnref<GrOptDrawState> optState(
-        GrOptDrawState::Create(this->getDrawState(), *this->caps(),
+        GrOptDrawState::Create(this->getDrawState(), this, info.getDstCopy(),
                                PrimTypeToDrawType(info.primitiveType())));
 
     // If the optState would is NULL it should have been caught in flushGraphicsState before getting
@@ -361,3 +354,13 @@
         attribState->disableUnusedArrays(this, usedAttribArraysMask);
     }
 }
+
+void GrGpuGL::buildProgramDesc(const GrOptDrawState& optState,
+                               const GrProgramDesc::DescInfo& descInfo,
+                               GrGpu::DrawType drawType,
+                               const GrDeviceCoordTexture* dstCopy,
+                               GrProgramDesc* desc) {
+    if (!GrGLProgramDescBuilder::Build(optState, descInfo, drawType, this, dstCopy, desc)) {
+        SkDEBUGFAIL("Failed to generate GL program descriptor");
+    }
+}
diff --git a/src/gpu/gl/builders/GrGLFragmentShaderBuilder.cpp b/src/gpu/gl/builders/GrGLFragmentShaderBuilder.cpp
index ff3989b..6aeba7a 100644
--- a/src/gpu/gl/builders/GrGLFragmentShaderBuilder.cpp
+++ b/src/gpu/gl/builders/GrGLFragmentShaderBuilder.cpp
@@ -69,12 +69,12 @@
 }
 
 GrGLFragmentShaderBuilder::GrGLFragmentShaderBuilder(GrGLProgramBuilder* program,
-                                                     const GrGLProgramDesc& desc)
+                                                     uint8_t fragPosKey)
     : INHERITED(program)
     , fHasCustomColorOutput(false)
     , fHasSecondaryOutput(false)
     , fSetupFragPosition(false)
-    , fTopLeftFragPosRead(kTopLeftFragPosRead_FragPosKey == desc.getHeader().fFragPosKey)
+    , fTopLeftFragPosRead(kTopLeftFragPosRead_FragPosKey == fragPosKey)
     , fCustomColorOutputIndex(-1)
     , fHasReadDstColor(false)
     , fHasReadFragmentPosition(false) {
@@ -262,13 +262,13 @@
     const char* secondaryOutputName = this->getSecondaryColorOutputName();
     GrGLSLExpr4 coeff(1);
     switch (fProgramBuilder->header().fSecondaryOutputType) {
-        case GrOptDrawState::kCoverage_SecondaryOutputType:
+        case GrProgramDesc::kCoverage_SecondaryOutputType:
             break;
-        case GrOptDrawState::kCoverageISA_SecondaryOutputType:
+        case GrProgramDesc::kCoverageISA_SecondaryOutputType:
             // Get (1-A) into coeff
             coeff = GrGLSLExpr4::VectorCast(GrGLSLExpr1(1) - inputColor.a());
             break;
-        case GrOptDrawState::kCoverageISC_SecondaryOutputType:
+        case GrProgramDesc::kCoverageISC_SecondaryOutputType:
             // Get (1-RGBA) into coeff
             coeff = GrGLSLExpr4(1) - inputColor;
             break;
@@ -283,9 +283,9 @@
                                                         const GrGLSLExpr4& inputCoverage) {
     GrGLSLExpr4 fragColor = inputColor * inputCoverage;
     switch (fProgramBuilder->header().fPrimaryOutputType) {
-        case GrOptDrawState::kModulate_PrimaryOutputType:
+        case GrProgramDesc::kModulate_PrimaryOutputType:
             break;
-        case GrOptDrawState::kCombineWithDst_PrimaryOutputType:
+        case GrProgramDesc::kCombineWithDst_PrimaryOutputType:
             {
                 // Tack on "+(1-coverage)dst onto the frag color.
                 GrGLSLExpr4 dstCoeff = GrGLSLExpr4(1) - inputCoverage;
diff --git a/src/gpu/gl/builders/GrGLFragmentShaderBuilder.h b/src/gpu/gl/builders/GrGLFragmentShaderBuilder.h
index b5f83d4..226be51 100644
--- a/src/gpu/gl/builders/GrGLFragmentShaderBuilder.h
+++ b/src/gpu/gl/builders/GrGLFragmentShaderBuilder.h
@@ -84,7 +84,7 @@
        the key is 0. */
     static FragPosKey KeyForFragmentPosition(const GrRenderTarget* dst, const GrGLCaps&);
 
-    GrGLFragmentShaderBuilder(GrGLProgramBuilder* program, const GrGLProgramDesc& desc);
+    GrGLFragmentShaderBuilder(GrGLProgramBuilder* program, uint8_t fragPosKey);
 
     // true public interface, defined explicitly in the abstract interfaces above
     virtual bool enableFeature(GLSLFeature) SK_OVERRIDE;
diff --git a/src/gpu/gl/builders/GrGLGeometryShaderBuilder.cpp b/src/gpu/gl/builders/GrGLGeometryShaderBuilder.cpp
index af95f56..f35c9ba 100644
--- a/src/gpu/gl/builders/GrGLGeometryShaderBuilder.cpp
+++ b/src/gpu/gl/builders/GrGLGeometryShaderBuilder.cpp
@@ -48,7 +48,7 @@
     geomShaderSrc.append("void main() {\n");
     geomShaderSrc.append("\tfor (int i = 0; i < 3; ++i) {\n"
                          "\t\tgl_Position = gl_in[i].gl_Position;\n");
-    if (fProgramBuilder->desc().getHeader().fEmitsPointSize) {
+    if (fProgramBuilder->desc().header().fEmitsPointSize) {
         geomShaderSrc.append("\t\tgl_PointSize = 1.0;\n");
     }
     SkASSERT(fInputs.count() == fOutputs.count());
diff --git a/src/gpu/gl/builders/GrGLLegacyNvprProgramBuilder.cpp b/src/gpu/gl/builders/GrGLLegacyNvprProgramBuilder.cpp
index 1c1cb42..c0c4fbb 100644
--- a/src/gpu/gl/builders/GrGLLegacyNvprProgramBuilder.cpp
+++ b/src/gpu/gl/builders/GrGLLegacyNvprProgramBuilder.cpp
@@ -9,12 +9,9 @@
 #include "../GrGpuGL.h"
 
 GrGLLegacyNvprProgramBuilder::GrGLLegacyNvprProgramBuilder(GrGpuGL* gpu,
-                                                           const GrOptDrawState& optState,
-                                                           const GrGLProgramDesc& desc)
-    : INHERITED(gpu, optState, desc)
+                                                           const GrOptDrawState& optState)
+    : INHERITED(gpu, optState)
     , fTexCoordSetCnt(0) {
-    SkASSERT(GrGLProgramDesc::kAttribute_ColorInput != desc.getHeader().fColorInput);
-    SkASSERT(GrGLProgramDesc::kAttribute_ColorInput != desc.getHeader().fCoverageInput);
 }
 
 int GrGLLegacyNvprProgramBuilder::addTexCoordSets(int count) {
diff --git a/src/gpu/gl/builders/GrGLLegacyNvprProgramBuilder.h b/src/gpu/gl/builders/GrGLLegacyNvprProgramBuilder.h
index dabec08..cd2cfb7 100644
--- a/src/gpu/gl/builders/GrGLLegacyNvprProgramBuilder.h
+++ b/src/gpu/gl/builders/GrGLLegacyNvprProgramBuilder.h
@@ -12,7 +12,7 @@
 
 class GrGLLegacyNvprProgramBuilder : public GrGLProgramBuilder {
 public:
-    GrGLLegacyNvprProgramBuilder(GrGpuGL*, const GrOptDrawState&, const GrGLProgramDesc&);
+    GrGLLegacyNvprProgramBuilder(GrGpuGL*, const GrOptDrawState&);
 
     virtual GrGLProgram* createProgram(GrGLuint programID);
 
diff --git a/src/gpu/gl/builders/GrGLNvprProgramBuilder.cpp b/src/gpu/gl/builders/GrGLNvprProgramBuilder.cpp
index 3d991a2..5488252 100644
--- a/src/gpu/gl/builders/GrGLNvprProgramBuilder.cpp
+++ b/src/gpu/gl/builders/GrGLNvprProgramBuilder.cpp
@@ -12,9 +12,8 @@
 #define GL_CALL_RET(R, X) GR_GL_CALL_RET(this->gpu()->glInterface(), R, X)
 
 GrGLNvprProgramBuilder::GrGLNvprProgramBuilder(GrGpuGL* gpu,
-                                               const GrOptDrawState& optState,
-                                               const GrGLProgramDesc& desc)
-        : INHERITED(gpu, optState, desc)
+                                               const GrOptDrawState& optState)
+        : INHERITED(gpu, optState)
         , fSeparableVaryingInfos(kVarsPerBlock) {
 }
 
diff --git a/src/gpu/gl/builders/GrGLNvprProgramBuilder.h b/src/gpu/gl/builders/GrGLNvprProgramBuilder.h
index 008aff7..e9f6b3b 100644
--- a/src/gpu/gl/builders/GrGLNvprProgramBuilder.h
+++ b/src/gpu/gl/builders/GrGLNvprProgramBuilder.h
@@ -12,7 +12,7 @@
 
 class GrGLNvprProgramBuilder : public GrGLProgramBuilder {
 public:
-    GrGLNvprProgramBuilder(GrGpuGL*, const GrOptDrawState&, const GrGLProgramDesc&);
+    GrGLNvprProgramBuilder(GrGpuGL*, const GrOptDrawState&);
 
     /*
      * The separable varying info must be passed to GrGLProgram so this must
diff --git a/src/gpu/gl/builders/GrGLProgramBuilder.cpp b/src/gpu/gl/builders/GrGLProgramBuilder.cpp
index 65a7cda..6df086c 100644
--- a/src/gpu/gl/builders/GrGLProgramBuilder.cpp
+++ b/src/gpu/gl/builders/GrGLProgramBuilder.cpp
@@ -29,19 +29,17 @@
 const int GrGLProgramBuilder::kVarsPerBlock = 8;
 
 GrGLProgram* GrGLProgramBuilder::CreateProgram(const GrOptDrawState& optState,
-                                               const GrGLProgramDesc& desc,
                                                GrGpu::DrawType drawType,
                                                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,
+    SkAutoTDelete<GrGLProgramBuilder> builder(CreateProgramBuilder(optState,
                                                                    drawType,
                                                                    optState.hasGeometryProcessor(),
                                                                    gpu));
 
     GrGLProgramBuilder* pb = builder.get();
-    const GrGLProgramDesc::KeyHeader& header = pb->header();
+    const GrGLProgramDescBuilder::GLKeyHeader& header = GrGLProgramDescBuilder::GetHeader(pb->desc());
 
     // emit code to read the dst copy texture, if necessary
     if (GrGLFragmentShaderBuilder::kNoDstRead_DstReadKey != header.fDstReadKey
@@ -64,10 +62,10 @@
         if (header.fEmitsPointSize) {
             pb->fVS.codeAppend("gl_PointSize = 1.0;");
         }
-        if (GrGLProgramDesc::kAttribute_ColorInput == header.fColorInput) {
+        if (GrProgramDesc::kAttribute_ColorInput == header.fColorInput) {
             pb->fVS.setupBuiltinVertexAttribute("Color", &inputColor);
         }
-        if (GrGLProgramDesc::kAttribute_ColorInput == header.fCoverageInput) {
+        if (GrProgramDesc::kAttribute_ColorInput == header.fCoverageInput) {
             pb->fVS.setupBuiltinVertexAttribute("Coverage", &inputCoverage);
         }
     }
@@ -79,7 +77,7 @@
     }
 
     // write the secondary color output if necessary
-    if (GrOptDrawState::kNone_SecondaryOutputType != header.fSecondaryOutputType) {
+    if (GrProgramDesc::kNone_SecondaryOutputType != header.fSecondaryOutputType) {
         pb->fFS.enableSecondaryOutput(inputColor, inputCoverage);
     }
 
@@ -89,38 +87,38 @@
 }
 
 GrGLProgramBuilder*
-GrGLProgramBuilder::CreateProgramBuilder(const GrGLProgramDesc& desc,
-                                         const GrOptDrawState& optState,
+GrGLProgramBuilder::CreateProgramBuilder(const GrOptDrawState& optState,
                                          GrGpu::DrawType drawType,
                                          bool hasGeometryProcessor,
                                          GrGpuGL* gpu) {
-    if (desc.getHeader().fUseNvpr) {
+    const GrProgramDesc& desc = optState.programDesc();
+    if (GrGLProgramDescBuilder::GetHeader(desc).fUseNvpr) {
         SkASSERT(gpu->glCaps().pathRenderingSupport());
+        SkASSERT(GrProgramDesc::kAttribute_ColorInput != desc.header().fColorInput);
+        SkASSERT(GrProgramDesc::kAttribute_ColorInput != desc.header().fCoverageInput);
         SkASSERT(!hasGeometryProcessor);
         if (gpu->glPathRendering()->texturingMode() ==
             GrGLPathRendering::FixedFunction_TexturingMode) {
-            return SkNEW_ARGS(GrGLLegacyNvprProgramBuilder, (gpu, optState, desc));
+            return SkNEW_ARGS(GrGLLegacyNvprProgramBuilder, (gpu, optState));
         } else {
-            return SkNEW_ARGS(GrGLNvprProgramBuilder, (gpu, optState, desc));
+            return SkNEW_ARGS(GrGLNvprProgramBuilder, (gpu, optState));
         }
     } else {
-        return SkNEW_ARGS(GrGLProgramBuilder, (gpu, optState, desc));
+        return SkNEW_ARGS(GrGLProgramBuilder, (gpu, optState));
     }
 }
 
 /////////////////////////////////////////////////////////////////////////////
 
-GrGLProgramBuilder::GrGLProgramBuilder(GrGpuGL* gpu,
-                                       const GrOptDrawState& optState,
-                                       const GrGLProgramDesc& desc)
+GrGLProgramBuilder::GrGLProgramBuilder(GrGpuGL* gpu, const GrOptDrawState& optState)
     : fVS(this)
     , fGS(this)
-    , fFS(this, desc)
+    , fFS(this, optState.programDesc().header().fFragPosKey)
     , fOutOfStage(true)
     , fStageIndex(-1)
     , fGeometryProcessor(NULL)
     , fOptState(optState)
-    , fDesc(desc)
+    , fDesc(optState.programDesc())
     , fGpu(gpu)
     , fUniforms(kVarsPerBlock) {
 }
@@ -202,8 +200,8 @@
 
 void GrGLProgramBuilder::setupUniformColorAndCoverageIfNeeded(GrGLSLExpr4* inputColor,
                                                               GrGLSLExpr4* inputCoverage) {
-    const GrGLProgramDesc::KeyHeader& header = this->header();
-    if (GrGLProgramDesc::kUniform_ColorInput == header.fColorInput) {
+    const GrProgramDesc::KeyHeader& header = this->header();
+    if (GrProgramDesc::kUniform_ColorInput == header.fColorInput) {
         const char* name;
         fUniformHandles.fColorUni =
             this->addUniform(GrGLProgramBuilder::kFragment_Visibility,
@@ -211,10 +209,10 @@
                              "Color",
                              &name);
         *inputColor = GrGLSLExpr4(name);
-    } else if (GrGLProgramDesc::kAllOnes_ColorInput == header.fColorInput) {
+    } else if (GrProgramDesc::kAllOnes_ColorInput == header.fColorInput) {
         *inputColor = GrGLSLExpr4(1);
     }
-    if (GrGLProgramDesc::kUniform_ColorInput == header.fCoverageInput) {
+    if (GrProgramDesc::kUniform_ColorInput == header.fCoverageInput) {
         const char* name;
         fUniformHandles.fCoverageUni =
             this->addUniform(GrGLProgramBuilder::kFragment_Visibility,
@@ -222,7 +220,7 @@
                              "Coverage",
                              &name);
         *inputCoverage = GrGLSLExpr4(name);
-    } else if (GrGLProgramDesc::kAllOnes_ColorInput == header.fCoverageInput) {
+    } else if (GrProgramDesc::kAllOnes_ColorInput == header.fCoverageInput) {
         *inputCoverage = GrGLSLExpr4(1);
     }
 }
@@ -236,7 +234,9 @@
     if (optState.hasGeometryProcessor()) {
         const GrGeometryProcessor& gp = *optState.getGeometryProcessor();
         fVS.emitAttributes(gp);
-        ProcKeyProvider keyProvider(&fDesc, ProcKeyProvider::kGeometry_ProcessorType);
+        ProcKeyProvider keyProvider(&fDesc,
+                                    ProcKeyProvider::kGeometry_ProcessorType,
+                                    GrGLProgramDescBuilder::kProcessorKeyOffsetsAndLengthOffset);
         GrGLSLExpr4 output;
         this->emitAndInstallProc<GrGeometryProcessor>(gp, 0, keyProvider, *inputCoverage, &output);
         *inputCoverage = output;
@@ -245,7 +245,9 @@
 }
 
 void GrGLProgramBuilder::emitAndInstallFragProcs(int procOffset, int numProcs, GrGLSLExpr4* inOut) {
-    ProcKeyProvider keyProvider(&fDesc, ProcKeyProvider::kFragment_ProcessorType);
+    ProcKeyProvider keyProvider(&fDesc,
+                                ProcKeyProvider::kFragment_ProcessorType,
+                                GrGLProgramDescBuilder::kProcessorKeyOffsetsAndLengthOffset);
     for (int e = procOffset; e < numProcs; ++e) {
         GrGLSLExpr4 output;
         const GrFragmentStage& stage = fOptState.getFragmentStage(e);
@@ -259,7 +261,7 @@
 template <class Proc>
 void GrGLProgramBuilder::emitAndInstallProc(const Proc& proc,
                                             int index,
-                                            const ProcKeyProvider keyProvider,
+                                            const ProcKeyProvider& keyProvider,
                                             const GrGLSLExpr4& input,
                                             GrGLSLExpr4* output) {
     // Program builders have a bit of state we need to clear with each effect
@@ -419,7 +421,7 @@
         this->cleanupProgram(programID, shadersToDelete);
         return NULL;
     }
-    if (!(this->header().fUseNvpr &&
+    if (!(GrGLProgramDescBuilder::GetHeader(fDesc).fUseNvpr &&
           fGpu->glPathRendering()->texturingMode() ==
           GrGLPathRendering::FixedFunction_TexturingMode)) {
         if (!fVS.compileAndAttachShaders(programID, &shadersToDelete)) {
diff --git a/src/gpu/gl/builders/GrGLProgramBuilder.h b/src/gpu/gl/builders/GrGLProgramBuilder.h
index 9d8e7e0..0abdf0a 100644
--- a/src/gpu/gl/builders/GrGLProgramBuilder.h
+++ b/src/gpu/gl/builders/GrGLProgramBuilder.h
@@ -169,10 +169,7 @@
      * to be used.
      * @return true if generation was successful.
      */
-    static GrGLProgram* CreateProgram(const GrOptDrawState&,
-                                      const GrGLProgramDesc&,
-                                      GrGpu::DrawType,
-                                      GrGpuGL* gpu);
+    static GrGLProgram* CreateProgram(const GrOptDrawState&, GrGpu::DrawType, GrGpuGL*);
 
     virtual UniformHandle addUniform(uint32_t visibility,
                                      GrSLType type,
@@ -224,21 +221,20 @@
     };
 
 protected:
-    typedef GrGLProgramDesc::ProcKeyProvider ProcKeyProvider;
+    typedef GrProgramDesc::ProcKeyProvider ProcKeyProvider;
     typedef GrGLProgramDataManager::UniformInfo UniformInfo;
     typedef GrGLProgramDataManager::UniformInfoArray UniformInfoArray;
 
-    static GrGLProgramBuilder* CreateProgramBuilder(const GrGLProgramDesc&,
-                                                    const GrOptDrawState&,
+    static GrGLProgramBuilder* CreateProgramBuilder(const GrOptDrawState&,
                                                     GrGpu::DrawType,
                                                     bool hasGeometryProcessor,
                                                     GrGpuGL*);
 
-    GrGLProgramBuilder(GrGpuGL*, const GrOptDrawState&, const GrGLProgramDesc&);
+    GrGLProgramBuilder(GrGpuGL*, const GrOptDrawState&);
 
     const GrOptDrawState& optState() const { return fOptState; }
-    const GrGLProgramDesc& desc() const { return fDesc; }
-    const GrGLProgramDesc::KeyHeader& header() const { return fDesc.getHeader(); }
+    const GrProgramDesc& desc() const { return fDesc; }
+    const GrProgramDesc::KeyHeader& header() const { return fDesc.header(); }
 
     // Generates a name for a variable. The generated string will be name prefixed by the prefix
     // char (unless the prefix is '\0'). It also mangles the name to be stage-specific if we're
@@ -252,7 +248,7 @@
     template <class Proc>
     void emitAndInstallProc(const Proc&,
                             int index,
-                            const ProcKeyProvider,
+                            const ProcKeyProvider&,
                             const GrGLSLExpr4& input,
                             GrGLSLExpr4* output);
 
@@ -332,7 +328,7 @@
     SkAutoTUnref<GrGLInstalledFragProcs> fFragmentProcessors;
 
     const GrOptDrawState& fOptState;
-    const GrGLProgramDesc& fDesc;
+    const GrProgramDesc& fDesc;
     GrGpuGL* fGpu;
     UniformInfoArray fUniforms;
 
diff --git a/src/gpu/gl/builders/GrGLShaderBuilder.h b/src/gpu/gl/builders/GrGLShaderBuilder.h
index cf7ae9b..8b7b9f9 100644
--- a/src/gpu/gl/builders/GrGLShaderBuilder.h
+++ b/src/gpu/gl/builders/GrGLShaderBuilder.h
@@ -8,6 +8,7 @@
 #ifndef GrGLShaderBuilder_DEFINED
 #define GrGLShaderBuilder_DEFINED
 
+#include "gl/GrGLProcessor.h"
 #include "gl/GrGLProgramDesc.h"
 #include "gl/GrGLProgramDataManager.h"
 
diff --git a/src/gpu/gl/builders/GrGLVertexShaderBuilder.cpp b/src/gpu/gl/builders/GrGLVertexShaderBuilder.cpp
index 1fa9467..5a65f71 100644
--- a/src/gpu/gl/builders/GrGLVertexShaderBuilder.cpp
+++ b/src/gpu/gl/builders/GrGLVertexShaderBuilder.cpp
@@ -93,7 +93,7 @@
 
 void GrGLVertexBuilder::bindVertexAttributes(GrGLuint programID) {
     // Bind the attrib locations to same values for all shaders
-    const GrGLProgramDesc::KeyHeader& header = fProgramBuilder->header();
+    const GrProgramDesc::KeyHeader& header = fProgramBuilder->header();
     SkASSERT(-1 != header.fPositionAttributeIndex);
     GL_CALL(BindAttribLocation(programID,
                                header.fPositionAttributeIndex,