OptState owns program descriptor

BUG=skia:

Review URL: https://codereview.chromium.org/674543004
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,