Remove GrGLProgram::CachedData, make GrGLProgram represent the program

Review URL: http://codereview.appspot.com/6409043/



git-svn-id: http://skia.googlecode.com/svn/trunk@4627 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/gpu/GrBinHashKey.h b/src/gpu/GrBinHashKey.h
index 028bb91..d2194e9 100644
--- a/src/gpu/GrBinHashKey.h
+++ b/src/gpu/GrBinHashKey.h
@@ -13,30 +13,40 @@
 #include "GrTypes.h"
 
 /**
- *  Hash function class that can take a data chunk of any predetermined
- *  length. The hash function used is the One-at-a-Time Hash
- *  (http://burtleburtle.net/bob/hash/doobs.html).
+ *  Hash function class that can take a data chunk of any predetermined length. The hash function
+ *  used is the One-at-a-Time Hash (http://burtleburtle.net/bob/hash/doobs.html).
+ *
+ *  Keys are computed from Entry objects. Entry must be fully ordered by a member:
+ *      int compare(const GrTBinHashKey<Entry, ..>& k);
+ *  which returns negative if the Entry < k, 0 if it equals k, and positive if k < the Entry.
+ *  Additionally, Entry must be flattenable into the key using setKeyData.
+ *
+ *  This class satisfies the requirements to be a key for a GrTHashTable.
  */
 template<typename Entry, size_t KeySize>
-class GrBinHashKey {
+class GrTBinHashKey {
 public:
-    GrBinHashKey()
-        : fHash(0)
-#if GR_DEBUG
-        , fIsValid(false)
-#endif
-    {}
+    GrTBinHashKey() {
+        this->reset();
+    }
 
-    GrBinHashKey(const GrBinHashKey<Entry, KeySize>& other) {
+    GrTBinHashKey(const GrTBinHashKey<Entry, KeySize>& other) {
         *this = other;
     }
-    GrBinHashKey<Entry, KeySize>& operator=(const GrBinHashKey<Entry,
-        KeySize>& other) {
+
+    GrTBinHashKey<Entry, KeySize>& operator=(const GrTBinHashKey<Entry, KeySize>& other) {
         memcpy(this, &other, sizeof(*this));
         return *this;
     }
 
-    ~GrBinHashKey() {
+    ~GrTBinHashKey() {
+    }
+
+    void reset() {
+        fHash = 0;
+#if GR_DEBUG
+        fIsValid = false;
+#endif
     }
 
     void setKeyData(const uint32_t* SK_RESTRICT data) {
@@ -60,19 +70,17 @@
         fHash = hash;
     }
 
-    int compare(const GrBinHashKey<Entry, KeySize>& key) const {
+    int compare(const GrTBinHashKey<Entry, KeySize>& key) const {
         GrAssert(fIsValid && key.fIsValid);
         return memcmp(fData, key.fData, KeySize);
     }
 
-    static bool
-    EQ(const Entry& entry, const GrBinHashKey<Entry, KeySize>& key) {
+    static bool EQ(const Entry& entry, const GrTBinHashKey<Entry, KeySize>& key) {
         GrAssert(key.fIsValid);
         return 0 == entry.compare(key);
     }
 
-    static bool
-    LT(const Entry& entry, const GrBinHashKey<Entry, KeySize>& key) {
+    static bool LT(const Entry& entry, const GrTBinHashKey<Entry, KeySize>& key) {
         GrAssert(key.fIsValid);
         return entry.compare(key) < 0;
     }
@@ -84,7 +92,7 @@
 
 private:
     uint32_t            fHash;
-    uint8_t             fData[KeySize];  //Buffer for key storage
+    uint8_t             fData[KeySize];  // Buffer for key storage
 
 #if GR_DEBUG
 public:
diff --git a/src/gpu/gl/GrGLProgram.cpp b/src/gpu/gl/GrGLProgram.cpp
index d13620c..b2b26fa 100644
--- a/src/gpu/gl/GrGLProgram.cpp
+++ b/src/gpu/gl/GrGLProgram.cpp
@@ -16,6 +16,8 @@
 #include "SkTrace.h"
 #include "SkXfermode.h"
 
+SK_DEFINE_INST_COUNT(GrGLProgram)
+
 namespace {
 
 enum {
@@ -28,7 +30,7 @@
 
 #define PRINT_SHADERS 0
 
-typedef GrGLProgram::ProgramDesc::StageDesc StageDesc;
+typedef GrGLProgram::Desc::StageDesc StageDesc;
 
 #define VIEW_MATRIX_NAME "uViewM"
 
@@ -95,18 +97,21 @@
 }
 
 GrGLProgram::~GrGLProgram() {
+    for (int i = 0; i < GrDrawState::kNumStages; ++i) {
+        delete fProgramStage[i];
+    }
 }
 
 void GrGLProgram::overrideBlend(GrBlendCoeff* srcCoeff,
                                 GrBlendCoeff* dstCoeff) const {
-    switch (fProgramDesc.fDualSrcOutput) {
-        case ProgramDesc::kNone_DualSrcOutput:
+    switch (fDesc.fDualSrcOutput) {
+        case Desc::kNone_DualSrcOutput:
             break;
         // the prog will write a coverage value to the secondary
         // output and the dst is blended by one minus that value.
-        case ProgramDesc::kCoverage_DualSrcOutput:
-        case ProgramDesc::kCoverageISA_DualSrcOutput:
-        case ProgramDesc::kCoverageISC_DualSrcOutput:
+        case Desc::kCoverage_DualSrcOutput:
+        case Desc::kCoverageISA_DualSrcOutput:
+        case Desc::kCoverageISC_DualSrcOutput:
         *dstCoeff = (GrBlendCoeff)GrGpu::kIS2C_GrBlendCoeff;
         break;
         default:
@@ -281,17 +286,15 @@
 }
 
 void GrGLProgram::genEdgeCoverage(const GrGLContextInfo& gl,
-                                  GrVertexLayout layout,
-                                  CachedData* programData,
                                   SkString* coverageVar,
                                   GrGLShaderBuilder* segments) const {
-    if (layout & GrDrawTarget::kEdge_VertexLayoutBit) {
+    if (fDesc.fVertexLayout & GrDrawTarget::kEdge_VertexLayoutBit) {
         const char *vsName, *fsName;
         segments->addVarying(kVec4f_GrSLType, "Edge", &vsName, &fsName);
         segments->fVSAttrs.push_back().set(kVec4f_GrSLType,
             GrGLShaderVar::kAttribute_TypeModifier, EDGE_ATTR_NAME);
         segments->fVSCode.appendf("\t%s = " EDGE_ATTR_NAME ";\n", vsName);
-        switch (fProgramDesc.fVertexEdgeType) {
+        switch (fDesc.fVertexEdgeType) {
         case GrDrawState::kHairLine_EdgeType:
             segments->fFSCode.appendf("\tfloat edgeAlpha = abs(dot(vec3(gl_FragCoord.xy,1), %s.xyz));\n", fsName);
             segments->fFSCode.append("\tedgeAlpha = max(1.0 - edgeAlpha, 0.0);\n");
@@ -345,32 +348,27 @@
     }
 }
 
-namespace {
-
-void genInputColor(GrGLProgram::ProgramDesc::ColorInput colorInput,
-                   GrGLProgram::CachedData* programData,
-                   GrGLShaderBuilder* segments,
-                   SkString* inColor) {
-    switch (colorInput) {
-        case GrGLProgram::ProgramDesc::kAttribute_ColorInput: {
-            segments->fVSAttrs.push_back().set(kVec4f_GrSLType,
+void GrGLProgram::genInputColor(GrGLShaderBuilder* builder, SkString* inColor) {
+    switch (fDesc.fColorInput) {
+        case GrGLProgram::Desc::kAttribute_ColorInput: {
+            builder->fVSAttrs.push_back().set(kVec4f_GrSLType,
                 GrGLShaderVar::kAttribute_TypeModifier,
                 COL_ATTR_NAME);
             const char *vsName, *fsName;
-            segments->addVarying(kVec4f_GrSLType, "Color", &vsName, &fsName);
-            segments->fVSCode.appendf("\t%s = " COL_ATTR_NAME ";\n", vsName);
+            builder->addVarying(kVec4f_GrSLType, "Color", &vsName, &fsName);
+            builder->fVSCode.appendf("\t%s = " COL_ATTR_NAME ";\n", vsName);
             *inColor = fsName;
             } break;
-        case GrGLProgram::ProgramDesc::kUniform_ColorInput:
-            segments->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
+        case GrGLProgram::Desc::kUniform_ColorInput:
+            builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
                                  kVec4f_GrSLType, COL_UNI_NAME);
-            programData->fUniLocations.fColorUni = kUseUniform;
+            fUniLocations.fColorUni = kUseUniform;
             *inColor = COL_UNI_NAME;
             break;
-        case GrGLProgram::ProgramDesc::kTransBlack_ColorInput:
+        case GrGLProgram::Desc::kTransBlack_ColorInput:
             GrAssert(!"needComputedColor should be false.");
             break;
-        case GrGLProgram::ProgramDesc::kSolidWhite_ColorInput:
+        case GrGLProgram::Desc::kSolidWhite_ColorInput:
             break;
         default:
             GrCrash("Unknown color type.");
@@ -378,8 +376,22 @@
     }
 }
 
-void genAttributeCoverage(GrGLShaderBuilder* segments,
-                          SkString* inOutCoverage) {
+void GrGLProgram::genUniformCoverage(GrGLShaderBuilder* builder, SkString* inOutCoverage) {
+    builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
+                        kVec4f_GrSLType, COV_UNI_NAME);
+    fUniLocations.fCoverageUni = kUseUniform;
+    if (inOutCoverage->size()) {
+        builder->fFSCode.appendf("\tvec4 uniCoverage = %s * %s;\n",
+                                  COV_UNI_NAME, inOutCoverage->c_str());
+        *inOutCoverage = "uniCoverage";
+    } else {
+        *inOutCoverage = COV_UNI_NAME;
+    }
+}
+
+namespace {
+void gen_attribute_coverage(GrGLShaderBuilder* segments,
+                            SkString* inOutCoverage) {
     segments->fVSAttrs.push_back().set(kVec4f_GrSLType,
                                        GrGLShaderVar::kAttribute_TypeModifier,
                                        COV_ATTR_NAME);
@@ -394,35 +406,19 @@
         *inOutCoverage = fsName;
     }
 }
-    
-void genUniformCoverage(GrGLShaderBuilder* segments,
-                        GrGLProgram::CachedData* programData,
-                        SkString* inOutCoverage) {
-    segments->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
-                         kVec4f_GrSLType, COV_UNI_NAME);
-    programData->fUniLocations.fCoverageUni = kUseUniform;
-    if (inOutCoverage->size()) {
-        segments->fFSCode.appendf("\tvec4 uniCoverage = %s * %s;\n",
-                                  COV_UNI_NAME, inOutCoverage->c_str());
-        *inOutCoverage = "uniCoverage";
-    } else {
-        *inOutCoverage = COV_UNI_NAME;
-    }
-}
-
 }
 
 void GrGLProgram::genGeometryShader(const GrGLContextInfo& gl,
                                     GrGLShaderBuilder* segments) const {
 #if GR_GL_EXPERIMENTAL_GS
-    if (fProgramDesc.fExperimentalGS) {
+    if (fDesc.fExperimentalGS) {
         GrAssert(gl.glslGeneration() >= k150_GrGLSLGeneration);
         segments->fGSHeader.append("layout(triangles) in;\n"
                                    "layout(triangle_strip, max_vertices = 6) out;\n");
         segments->fGSCode.append("void main() {\n"
                                  "\tfor (int i = 0; i < 3; ++i) {\n"
                                   "\t\tgl_Position = gl_in[i].gl_Position;\n");
-        if (this->fProgramDesc.fEmitsPointSize) {
+        if (fDesc.fEmitsPointSize) {
             segments->fGSCode.append("\t\tgl_PointSize = 1.0;\n");
         }
         GrAssert(segments->fGSInputs.count() == segments->fGSOutputs.count());
@@ -444,7 +440,7 @@
     if (inColor.size()) {
           return inColor.c_str();
     } else {
-        if (ProgramDesc::kSolidWhite_ColorInput == fProgramDesc.fColorInput) {
+        if (Desc::kSolidWhite_ColorInput == fDesc.fColorInput) {
             return all_ones_vec(4);
         } else {
             return all_zeros_vec(4);
@@ -452,14 +448,6 @@
     }
 }
 
-// If this destructor is in the header file, we must include GrGLProgramStage
-// instead of just forward-declaring it.
-GrGLProgram::CachedData::~CachedData() {
-    for (int i = 0; i < GrDrawState::kNumStages; ++i) {
-        delete fCustomStage[i];
-    }
-}
-
 namespace {
 #define GL_CALL(X) GR_GL_CALL(gl.interface(), X)
 #define GL_CALL_RET(R, X) GR_GL_CALL_RET(gl.interface(), R, X)
@@ -524,10 +512,11 @@
     return compile_shader(gl, type, 1, &str, &length);
 }
 
-// compiles all the shaders from builder and stores the shader IDs in programData.
-bool compile_shaders(const GrGLContextInfo& gl,
-                     const GrGLShaderBuilder& builder,
-                     GrGLProgram::CachedData* programData) {
+}
+
+// compiles all the shaders from builder and stores the shader IDs
+bool GrGLProgram::compileShaders(const GrGLContextInfo& gl,
+                                 const GrGLShaderBuilder& builder) {
 
     SkString shader;
 
@@ -536,7 +525,7 @@
     GrPrintf(shader.c_str());
     GrPrintf("\n");
 #endif
-    if (!(programData->fVShaderID = compile_shader(gl, GR_GL_VERTEX_SHADER, shader))) {
+    if (!(fVShaderID = compile_shader(gl, GR_GL_VERTEX_SHADER, shader))) {
         return false;
     }
 
@@ -546,11 +535,11 @@
         GrPrintf(shader.c_str());
         GrPrintf("\n");
 #endif
-        if (!(programData->fGShaderID = compile_shader(gl, GR_GL_GEOMETRY_SHADER, shader))) {
+        if (!(fGShaderID = compile_shader(gl, GR_GL_GEOMETRY_SHADER, shader))) {
             return false;
         }
     } else {
-        programData->fGShaderID = 0;
+        fGShaderID = 0;
     }
 
     builder.getShader(GrGLShaderBuilder::kFragment_ShaderType, &shader);
@@ -558,34 +547,34 @@
     GrPrintf(shader.c_str());
     GrPrintf("\n");
 #endif
-    if (!(programData->fFShaderID = compile_shader(gl, GR_GL_FRAGMENT_SHADER, shader))) {
+    if (!(fFShaderID = compile_shader(gl, GR_GL_FRAGMENT_SHADER, shader))) {
         return false;
     }
 
     return true;
 }
 
-}
 
 bool GrGLProgram::genProgram(const GrGLContextInfo& gl,
-                             GrCustomStage** customStages,
-                             GrGLProgram::CachedData* programData) const {
+                             const Desc& desc,
+                             GrCustomStage** customStages) {
+    fDesc = desc;
     GrGLShaderBuilder builder(gl);
-    const uint32_t& layout = fProgramDesc.fVertexLayout;
+    const uint32_t& layout = fDesc.fVertexLayout;
 
-    programData->fUniLocations.reset();
+    fUniLocations.reset();
 
 #if GR_GL_EXPERIMENTAL_GS
-    builder.fUsesGS = fProgramDesc.fExperimentalGS;
+    builder.fUsesGS = fDesc.fExperimentalGS;
 #endif
 
     SkXfermode::Coeff colorCoeff, uniformCoeff;
-    bool applyColorMatrix = SkToBool(fProgramDesc.fColorMatrixEnabled);
+    bool applyColorMatrix = SkToBool(fDesc.fColorMatrixEnabled);
     // The rest of transfer mode color filters have not been implemented
-    if (fProgramDesc.fColorFilterXfermode < SkXfermode::kCoeffModesCnt) {
+    if (fDesc.fColorFilterXfermode < SkXfermode::kCoeffModesCnt) {
         GR_DEBUGCODE(bool success =)
             SkXfermode::ModeAsCoeff(static_cast<SkXfermode::Mode>
-                                    (fProgramDesc.fColorFilterXfermode),
+                                    (fDesc.fColorFilterXfermode),
                                     &uniformCoeff, &colorCoeff);
         GR_DEBUGASSERT(success);
     } else {
@@ -596,7 +585,7 @@
     // no need to do the color filter / matrix at all if coverage is 0. The
     // output color is scaled by the coverage. All the dual source outputs are
     // scaled by the coverage as well.
-    if (ProgramDesc::kTransBlack_ColorInput == fProgramDesc.fCoverageInput) {
+    if (Desc::kTransBlack_ColorInput == fDesc.fCoverageInput) {
         colorCoeff = SkXfermode::kZero_Coeff;
         uniformCoeff = SkXfermode::kZero_Coeff;
         applyColorMatrix = false;
@@ -605,7 +594,7 @@
     // If we know the final color is going to be all zeros then we can
     // simplify the color filter coeffecients. needComputedColor will then
     // come out false below.
-    if (ProgramDesc::kTransBlack_ColorInput == fProgramDesc.fColorInput) {
+    if (Desc::kTransBlack_ColorInput == fDesc.fColorInput) {
         colorCoeff = SkXfermode::kZero_Coeff;
         if (SkXfermode::kDC_Coeff == uniformCoeff ||
             SkXfermode::kDA_Coeff == uniformCoeff) {
@@ -637,7 +626,7 @@
 
     builder.addUniform(GrGLShaderBuilder::kVertex_ShaderType,
                        kMat33f_GrSLType, VIEW_MATRIX_NAME);
-    programData->fUniLocations.fViewMatrixUni = kUseUniform;
+    fUniLocations.fViewMatrixUni = kUseUniform;
 
     builder.fVSAttrs.push_back().set(kVec2f_GrSLType,
                                      GrGLShaderVar::kAttribute_TypeModifier,
@@ -651,12 +640,11 @@
     SkString inColor;
 
     if (needComputedColor) {
-        genInputColor((ProgramDesc::ColorInput) fProgramDesc.fColorInput,
-                      programData, &builder, &inColor);
+        this->genInputColor(&builder, &inColor);
     }
 
     // we output point size in the GS if present
-    if (fProgramDesc.fEmitsPointSize && !builder.fUsesGS){
+    if (fDesc.fEmitsPointSize && !builder.fUsesGS){
         builder.fVSCode.append("\tgl_PointSize = 1.0;\n");
     }
 
@@ -679,7 +667,7 @@
     // uses of programData, but it's safest to do so below when we're *sure*
     // we need them.
     for (int s = 0; s < GrDrawState::kNumStages; ++s) {
-        programData->fCustomStage[s] = NULL;
+        fProgramStage[s] = NULL;
     }
 
     ///////////////////////////////////////////////////////////////////////////
@@ -689,8 +677,8 @@
     // of each to the next and generating code for each stage.
     if (needComputedColor) {
         SkString outColor;
-        for (int s = 0; s < fProgramDesc.fFirstCoverageStage; ++s) {
-            if (fProgramDesc.fStages[s].isEnabled()) {
+        for (int s = 0; s < fDesc.fFirstCoverageStage; ++s) {
+            if (fDesc.fStages[s].isEnabled()) {
                 // create var to hold stage result
                 outColor = "color";
                 outColor.appendS32(s);
@@ -708,20 +696,15 @@
                 }
 
                 if (NULL != customStages[s]) {
-                    const GrProgramStageFactory& factory =
-                        customStages[s]->getFactory();
-                    programData->fCustomStage[s] =
-                        factory.createGLInstance(*customStages[s]);
+                    const GrProgramStageFactory& factory = customStages[s]->getFactory();
+                    fProgramStage[s] = factory.createGLInstance(*customStages[s]);
                 }
                 this->genStageCode(gl,
                                    s,
-                                   fProgramDesc.fStages[s],
                                    inColor.size() ? inColor.c_str() : NULL,
                                    outColor.c_str(),
                                    inCoords,
-                                   &builder,
-                                   &programData->fUniLocations.fStages[s],
-                                   programData->fCustomStage[s]);
+                                   &builder);
                 inColor = outColor;
             }
         }
@@ -730,7 +713,7 @@
     // if have all ones or zeros for the "dst" input to the color filter then we
     // may be able to make additional optimizations.
     if (needColorFilterUniform && needComputedColor && !inColor.size()) {
-        GrAssert(ProgramDesc::kSolidWhite_ColorInput == fProgramDesc.fColorInput);
+        GrAssert(Desc::kSolidWhite_ColorInput == fDesc.fColorInput);
         bool uniformCoeffIsZero = SkXfermode::kIDC_Coeff == uniformCoeff ||
                                   SkXfermode::kIDA_Coeff == uniformCoeff;
         if (uniformCoeffIsZero) {
@@ -743,7 +726,7 @@
     if (needColorFilterUniform) {
         builder.addUniform(GrGLShaderBuilder::kFragment_ShaderType,
                            kVec4f_GrSLType, COL_FILTER_UNI_NAME);
-        programData->fUniLocations.fColorFilterUni = kUseUniform;
+        fUniLocations.fColorFilterUni = kUseUniform;
     }
     bool wroteFragColorZero = false;
     if (SkXfermode::kZero_Coeff == uniformCoeff &&
@@ -753,7 +736,7 @@
                                 colorOutput.getName().c_str(),
                                 all_zeros_vec(4));
         wroteFragColorZero = true;
-    } else if (SkXfermode::kDst_Mode != fProgramDesc.fColorFilterXfermode) {
+    } else if (SkXfermode::kDst_Mode != fDesc.fColorFilterXfermode) {
         builder.fFSCode.append("\tvec4 filteredColor;\n");
         const char* color = adjustInColor(inColor);
         addColorFilter(&builder.fFSCode, "filteredColor", uniformCoeff,
@@ -765,8 +748,8 @@
                            kMat44f_GrSLType, COL_MATRIX_UNI_NAME);
         builder.addUniform(GrGLShaderBuilder::kFragment_ShaderType,
                            kVec4f_GrSLType, COL_MATRIX_VEC_UNI_NAME);
-        programData->fUniLocations.fColorMatrixUni = kUseUniform;
-        programData->fUniLocations.fColorMatrixVecUni = kUseUniform;
+        fUniLocations.fColorMatrixUni = kUseUniform;
+        fUniLocations.fColorMatrixVecUni = kUseUniform;
         builder.fFSCode.append("\tvec4 matrixedColor;\n");
         const char* color = adjustInColor(inColor);
         addColorMatrix(&builder.fFSCode, "matrixedColor", color);
@@ -777,34 +760,32 @@
     // compute the partial coverage (coverage stages and edge aa)
 
     SkString inCoverage;
-    bool coverageIsZero = ProgramDesc::kTransBlack_ColorInput ==
-                          fProgramDesc.fCoverageInput;
+    bool coverageIsZero = Desc::kTransBlack_ColorInput == fDesc.fCoverageInput;
     // we don't need to compute coverage at all if we know the final shader
     // output will be zero and we don't have a dual src blend output.
-    if (!wroteFragColorZero ||
-        ProgramDesc::kNone_DualSrcOutput != fProgramDesc.fDualSrcOutput) {
+    if (!wroteFragColorZero || Desc::kNone_DualSrcOutput != fDesc.fDualSrcOutput) {
 
         if (!coverageIsZero) {
-            this->genEdgeCoverage(gl, layout, programData, &inCoverage, &builder);
+            this->genEdgeCoverage(gl, &inCoverage, &builder);
 
-            switch (fProgramDesc.fCoverageInput) {
-                case ProgramDesc::kSolidWhite_ColorInput:
+            switch (fDesc.fCoverageInput) {
+                case Desc::kSolidWhite_ColorInput:
                     // empty string implies solid white
                     break;
-                case ProgramDesc::kAttribute_ColorInput:
-                    genAttributeCoverage(&builder, &inCoverage);
+                case Desc::kAttribute_ColorInput:
+                    gen_attribute_coverage(&builder, &inCoverage);
                     break;
-                case ProgramDesc::kUniform_ColorInput:
-                    genUniformCoverage(&builder, programData, &inCoverage);
+                case Desc::kUniform_ColorInput:
+                    this->genUniformCoverage(&builder, &inCoverage);
                     break;
                 default:
                     GrCrash("Unexpected input coverage.");
             }
 
             SkString outCoverage;
-            const int& startStage = fProgramDesc.fFirstCoverageStage;
+            const int& startStage = fDesc.fFirstCoverageStage;
             for (int s = startStage; s < GrDrawState::kNumStages; ++s) {
-                if (fProgramDesc.fStages[s].isEnabled()) {
+                if (fDesc.fStages[s].isEnabled()) {
                     // create var to hold stage output
                     outCoverage = "coverage";
                     outCoverage.appendS32(s);
@@ -825,37 +806,31 @@
                     }
 
                     if (NULL != customStages[s]) {
-                        const GrProgramStageFactory& factory =
-                            customStages[s]->getFactory();
-                        programData->fCustomStage[s] =
-                            factory.createGLInstance(*customStages[s]);
+                        const GrProgramStageFactory& factory = customStages[s]->getFactory();
+                        fProgramStage[s] = factory.createGLInstance(*customStages[s]);
                     }
-                    this->genStageCode(gl, s,
-                        fProgramDesc.fStages[s],
-                        inCoverage.size() ? inCoverage.c_str() : NULL,
-                        outCoverage.c_str(),
-                        inCoords,
-                        &builder,
-                        &programData->fUniLocations.fStages[s],
-                        programData->fCustomStage[s]);
+                    this->genStageCode(gl,
+                                       s,
+                                       inCoverage.size() ? inCoverage.c_str() : NULL,
+                                       outCoverage.c_str(),
+                                       inCoords,
+                                       &builder);
                     inCoverage = outCoverage;
                 }
             }
         }
-        if (ProgramDesc::kNone_DualSrcOutput != fProgramDesc.fDualSrcOutput) {
+        if (Desc::kNone_DualSrcOutput != fDesc.fDualSrcOutput) {
             builder.fFSOutputs.push_back().set(kVec4f_GrSLType,
                                                GrGLShaderVar::kOut_TypeModifier,
                                                dual_source_output_name());
             bool outputIsZero = coverageIsZero;
             SkString coeff;
             if (!outputIsZero &&
-                ProgramDesc::kCoverage_DualSrcOutput !=
-                fProgramDesc.fDualSrcOutput && !wroteFragColorZero) {
+                Desc::kCoverage_DualSrcOutput != fDesc.fDualSrcOutput && !wroteFragColorZero) {
                 if (!inColor.size()) {
                     outputIsZero = true;
                 } else {
-                    if (fProgramDesc.fDualSrcOutput ==
-                        ProgramDesc::kCoverageISA_DualSrcOutput) {
+                    if (Desc::kCoverageISA_DualSrcOutput == fDesc.fDualSrcOutput) {
                         coeff.printf("(1 - %s.a)", inColor.c_str());
                     } else {
                         coeff.printf("(vec4(1,1,1,1) - %s)", inColor.c_str());
@@ -890,16 +865,14 @@
                             inCoverage.c_str(),
                             &builder.fFSCode);
         }
-        if (ProgramDesc::kUnpremultiplied_RoundDown_OutputConfig ==
-            fProgramDesc.fOutputConfig) {
+        if (Desc::kUnpremultiplied_RoundDown_OutputConfig == fDesc.fOutputConfig) {
             builder.fFSCode.appendf("\t%s = %s.a <= 0.0 ? vec4(0,0,0,0) : vec4(floor(%s.rgb / %s.a * 255.0)/255.0, %s.a);\n",
                                     colorOutput.getName().c_str(),
                                     colorOutput.getName().c_str(),
                                     colorOutput.getName().c_str(),
                                     colorOutput.getName().c_str(),
                                     colorOutput.getName().c_str());
-        } else if (ProgramDesc::kUnpremultiplied_RoundUp_OutputConfig ==
-                   fProgramDesc.fOutputConfig) {
+        } else if (Desc::kUnpremultiplied_RoundUp_OutputConfig == fDesc.fOutputConfig) {
             builder.fFSCode.appendf("\t%s = %s.a <= 0.0 ? vec4(0,0,0,0) : vec4(ceil(%s.rgb / %s.a * 255.0)/255.0, %s.a);\n",
                                     colorOutput.getName().c_str(),
                                     colorOutput.getName().c_str(),
@@ -921,132 +894,124 @@
     ///////////////////////////////////////////////////////////////////////////
     // compile and setup attribs and unis
 
-    if (!compile_shaders(gl, builder, programData)) {
+    if (!this->compileShaders(gl, builder)) {
         return false;
     }
 
-    if (!this->bindOutputsAttribsAndLinkProgram(gl, texCoordAttrs,
+    if (!this->bindOutputsAttribsAndLinkProgram(gl,
+                                                texCoordAttrs,
                                                 isColorDeclared,
-                                                dualSourceOutputWritten,
-                                                programData)) {
+                                                dualSourceOutputWritten)) {
         return false;
     }
 
-    this->getUniformLocationsAndInitCache(builder, gl, programData);
+    this->getUniformLocationsAndInitCache(gl, builder);
 
     return true;
 }
 
 bool GrGLProgram::bindOutputsAttribsAndLinkProgram(const GrGLContextInfo& gl,
-                                        SkString texCoordAttrNames[],
-                                        bool bindColorOut,
-                                        bool bindDualSrcOut,
-                                        CachedData* programData) const {
-    GL_CALL_RET(programData->fProgramID, CreateProgram());
-    if (!programData->fProgramID) {
+                                                   SkString texCoordAttrNames[],
+                                                   bool bindColorOut,
+                                                   bool bindDualSrcOut) {
+    GL_CALL_RET(fProgramID, CreateProgram());
+    if (!fProgramID) {
         return false;
     }
-    const GrGLint& progID = programData->fProgramID;
 
-    GL_CALL(AttachShader(progID, programData->fVShaderID));
-    if (programData->fGShaderID) {
-        GL_CALL(AttachShader(progID, programData->fGShaderID));
+    GL_CALL(AttachShader(fProgramID, fVShaderID));
+    if (fGShaderID) {
+        GL_CALL(AttachShader(fProgramID, fGShaderID));
     }
-    GL_CALL(AttachShader(progID, programData->fFShaderID));
+    GL_CALL(AttachShader(fProgramID, fFShaderID));
 
     if (bindColorOut) {
-        GL_CALL(BindFragDataLocation(programData->fProgramID,
-                                     0, declared_color_output_name()));
+        GL_CALL(BindFragDataLocation(fProgramID, 0, declared_color_output_name()));
     }
     if (bindDualSrcOut) {
-        GL_CALL(BindFragDataLocationIndexed(programData->fProgramID,
-                                            0, 1, dual_source_output_name()));
+        GL_CALL(BindFragDataLocationIndexed(fProgramID, 0, 1, dual_source_output_name()));
     }
 
     // Bind the attrib locations to same values for all shaders
-    GL_CALL(BindAttribLocation(progID, PositionAttributeIdx(), POS_ATTR_NAME));
+    GL_CALL(BindAttribLocation(fProgramID, PositionAttributeIdx(), POS_ATTR_NAME));
     for (int t = 0; t < GrDrawState::kMaxTexCoords; ++t) {
         if (texCoordAttrNames[t].size()) {
-            GL_CALL(BindAttribLocation(progID,
+            GL_CALL(BindAttribLocation(fProgramID,
                                        TexCoordAttributeIdx(t),
                                        texCoordAttrNames[t].c_str()));
         }
     }
 
-    GL_CALL(BindAttribLocation(progID, ColorAttributeIdx(), COL_ATTR_NAME));
-    GL_CALL(BindAttribLocation(progID, CoverageAttributeIdx(), COV_ATTR_NAME));
-    GL_CALL(BindAttribLocation(progID, EdgeAttributeIdx(), EDGE_ATTR_NAME));
+    GL_CALL(BindAttribLocation(fProgramID, ColorAttributeIdx(), COL_ATTR_NAME));
+    GL_CALL(BindAttribLocation(fProgramID, CoverageAttributeIdx(), COV_ATTR_NAME));
+    GL_CALL(BindAttribLocation(fProgramID, EdgeAttributeIdx(), EDGE_ATTR_NAME));
 
-    GL_CALL(LinkProgram(progID));
+    GL_CALL(LinkProgram(fProgramID));
 
     GrGLint linked = GR_GL_INIT_ZERO;
-    GL_CALL(GetProgramiv(progID, GR_GL_LINK_STATUS, &linked));
+    GL_CALL(GetProgramiv(fProgramID, GR_GL_LINK_STATUS, &linked));
     if (!linked) {
         GrGLint infoLen = GR_GL_INIT_ZERO;
-        GL_CALL(GetProgramiv(progID, GR_GL_INFO_LOG_LENGTH, &infoLen));
+        GL_CALL(GetProgramiv(fProgramID, GR_GL_INFO_LOG_LENGTH, &infoLen));
         SkAutoMalloc log(sizeof(char)*(infoLen+1));  // outside if for debugger
         if (infoLen > 0) {
             // retrieve length even though we don't need it to workaround
             // bug in chrome cmd buffer param validation.
             GrGLsizei length = GR_GL_INIT_ZERO;
-            GL_CALL(GetProgramInfoLog(progID,
+            GL_CALL(GetProgramInfoLog(fProgramID,
                                       infoLen+1,
                                       &length,
                                       (char*)log.get()));
             GrPrintf((char*)log.get());
         }
         GrAssert(!"Error linking program");
-        GL_CALL(DeleteProgram(progID));
-        programData->fProgramID = 0;
+        GL_CALL(DeleteProgram(fProgramID));
+        fProgramID = 0;
         return false;
     }
     return true;
 }
 
-void GrGLProgram::getUniformLocationsAndInitCache(const GrGLShaderBuilder& builder,
-                                                  const GrGLContextInfo& gl,
-                                                  CachedData* programData) const {
-    const GrGLint& progID = programData->fProgramID;
+void GrGLProgram::getUniformLocationsAndInitCache(const GrGLContextInfo& gl,
+                                                  const GrGLShaderBuilder& builder) {
 
-    if (kUseUniform == programData->fUniLocations.fViewMatrixUni) {
-        GL_CALL_RET(programData->fUniLocations.fViewMatrixUni,
-                    GetUniformLocation(progID, VIEW_MATRIX_NAME));
-        GrAssert(kUnusedUniform != programData->fUniLocations.fViewMatrixUni);
+    if (kUseUniform == fUniLocations.fViewMatrixUni) {
+        GL_CALL_RET(fUniLocations.fViewMatrixUni, GetUniformLocation(fProgramID, VIEW_MATRIX_NAME));
+        GrAssert(kUnusedUniform != fUniLocations.fViewMatrixUni);
     }
-    if (kUseUniform == programData->fUniLocations.fColorUni) {
-        GL_CALL_RET(programData->fUniLocations.fColorUni,
-                    GetUniformLocation(progID, COL_UNI_NAME));
-        GrAssert(kUnusedUniform != programData->fUniLocations.fColorUni);
+    if (kUseUniform == fUniLocations.fColorUni) {
+        GL_CALL_RET(fUniLocations.fColorUni, GetUniformLocation(fProgramID, COL_UNI_NAME));
+        GrAssert(kUnusedUniform != fUniLocations.fColorUni);
     }
-    if (kUseUniform == programData->fUniLocations.fColorFilterUni) {
-        GL_CALL_RET(programData->fUniLocations.fColorFilterUni, 
-                    GetUniformLocation(progID, COL_FILTER_UNI_NAME));
-        GrAssert(kUnusedUniform != programData->fUniLocations.fColorFilterUni);
+    if (kUseUniform == fUniLocations.fColorFilterUni) {
+        GL_CALL_RET(fUniLocations.fColorFilterUni,
+                    GetUniformLocation(fProgramID, COL_FILTER_UNI_NAME));
+        GrAssert(kUnusedUniform != fUniLocations.fColorFilterUni);
     }
 
-    if (kUseUniform == programData->fUniLocations.fColorMatrixUni) {
-        GL_CALL_RET(programData->fUniLocations.fColorMatrixUni,
-                    GetUniformLocation(progID, COL_MATRIX_UNI_NAME));
+    if (kUseUniform == fUniLocations.fColorMatrixUni) {
+        GL_CALL_RET(fUniLocations.fColorMatrixUni,
+                    GetUniformLocation(fProgramID, COL_MATRIX_UNI_NAME));
+        GrAssert(kUnusedUniform != fUniLocations.fColorMatrixUni);
     }
 
-    if (kUseUniform == programData->fUniLocations.fColorMatrixVecUni) {
-        GL_CALL_RET(programData->fUniLocations.fColorMatrixVecUni,
-                    GetUniformLocation(progID, COL_MATRIX_VEC_UNI_NAME));
+    if (kUseUniform == fUniLocations.fColorMatrixVecUni) {
+        GL_CALL_RET(fUniLocations.fColorMatrixVecUni,
+                    GetUniformLocation(fProgramID, COL_MATRIX_VEC_UNI_NAME));
+        GrAssert(kUnusedUniform != fUniLocations.fColorMatrixVecUni);
     }
-    if (kUseUniform == programData->fUniLocations.fCoverageUni) {
-        GL_CALL_RET(programData->fUniLocations.fCoverageUni,
-                    GetUniformLocation(progID, COV_UNI_NAME));
-        GrAssert(kUnusedUniform != programData->fUniLocations.fCoverageUni);
+    if (kUseUniform == fUniLocations.fCoverageUni) {
+        GrAssert(kUnusedUniform != fUniLocations.fCoverageUni);
     }
 
     for (int s = 0; s < GrDrawState::kNumStages; ++s) {
-        StageUniLocations& locations = programData->fUniLocations.fStages[s];
-        if (fProgramDesc.fStages[s].isEnabled()) {
+        StageUniLocations& locations = fUniLocations.fStages[s];
+        if (fDesc.fStages[s].isEnabled()) {
             if (kUseUniform == locations.fTextureMatrixUni) {
                 SkString texMName;
                 tex_matrix_name(s, &texMName);
                 GL_CALL_RET(locations.fTextureMatrixUni,
-                            GetUniformLocation(progID, texMName.c_str()));
+                            GetUniformLocation(fProgramID, texMName.c_str()));
                 GrAssert(kUnusedUniform != locations.fTextureMatrixUni);
             }
 
@@ -1054,7 +1019,7 @@
                 SkString samplerName;
                 sampler_name(s, &samplerName);
                 GL_CALL_RET(locations.fSamplerUni,
-                            GetUniformLocation(progID,samplerName.c_str()));
+                            GetUniformLocation(fProgramID,samplerName.c_str()));
                 GrAssert(kUnusedUniform != locations.fSamplerUni);
             }
 
@@ -1062,33 +1027,32 @@
                 SkString texDomName;
                 tex_domain_name(s, &texDomName);
                 GL_CALL_RET(locations.fTexDomUni,
-                            GetUniformLocation(progID, texDomName.c_str()));
+                            GetUniformLocation(fProgramID, texDomName.c_str()));
                 GrAssert(kUnusedUniform != locations.fTexDomUni);
             }
 
-            if (NULL != programData->fCustomStage[s]) {
-                programData->fCustomStage[s]->initUniforms(&builder, gl.interface(), progID);
+            if (NULL != fProgramStage[s]) {
+                fProgramStage[s]->initUniforms(&builder, gl.interface(), fProgramID);
             }
         }
     }
-    GL_CALL(UseProgram(progID));
+    GL_CALL(UseProgram(fProgramID));
 
     // init sampler unis and set bogus values for state tracking
     for (int s = 0; s < GrDrawState::kNumStages; ++s) {
-        if (kUnusedUniform != programData->fUniLocations.fStages[s].fSamplerUni) {
-            GL_CALL(Uniform1i(programData->fUniLocations.fStages[s].fSamplerUni, s));
+        if (kUnusedUniform != fUniLocations.fStages[s].fSamplerUni) {
+            GL_CALL(Uniform1i(fUniLocations.fStages[s].fSamplerUni, s));
         }
-        programData->fTextureMatrices[s] = GrMatrix::InvalidMatrix();
-        programData->fTextureDomain[s].setEmpty();
+        fTextureMatrices[s] = GrMatrix::InvalidMatrix();
+        fTextureDomain[s].setEmpty();
         // this is arbitrary, just initialize to something
-        programData->fTextureOrientation[s] =
-            GrGLTexture::kBottomUp_Orientation;
+        fTextureOrientation[s] = GrGLTexture::kBottomUp_Orientation;
         // Must not reset fStageOverride[] here.
     }
-    programData->fViewMatrix = GrMatrix::InvalidMatrix();
-    programData->fViewportSize.set(-1, -1);
-    programData->fColor = GrColor_ILLEGAL;
-    programData->fColorFilterColor = GrColor_ILLEGAL;
+    fViewMatrix = GrMatrix::InvalidMatrix();
+    fViewportSize.set(-1, -1);
+    fColor = GrColor_ILLEGAL;
+    fColorFilterColor = GrColor_ILLEGAL;
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -1096,17 +1060,17 @@
 
 void GrGLProgram::genStageCode(const GrGLContextInfo& gl,
                                int stageNum,
-                               const GrGLProgram::StageDesc& desc,
                                const char* fsInColor, // NULL means no incoming color
                                const char* fsOutColor,
                                const char* vsInCoord,
-                               GrGLShaderBuilder* segments,
-                               StageUniLocations* locations,
-                               GrGLProgramStage* customStage) const {
-
+                               GrGLShaderBuilder* segments) {
     GrAssert(stageNum >= 0 && stageNum <= GrDrawState::kNumStages);
-    GrAssert((desc.fInConfigFlags & StageDesc::kInConfigBitMask) ==
-             desc.fInConfigFlags);
+
+    const GrGLProgram::StageDesc& desc = fDesc.fStages[stageNum];
+    StageUniLocations& locations = fUniLocations.fStages[stageNum];
+    GrGLProgramStage* customStage = fProgramStage[stageNum];
+
+    GrAssert((desc.fInConfigFlags & StageDesc::kInConfigBitMask) == desc.fInConfigFlags);
 
     /// Vertex Shader Stuff
 
@@ -1124,7 +1088,7 @@
         const GrGLShaderVar& mat = segments->getUniformVariable(m);
         // Can't use texMatName.c_str() because it's on the stack!
         matName = mat.getName().c_str();
-        locations->fTextureMatrixUni = kUseUniform;
+        locations.fTextureMatrixUni = kUseUniform;
 
         if (desc.fOptFlags & StageDesc::kNoPerspective_OptFlagBit) {
             segments->fVaryingDims = segments->fCoordDims;
@@ -1143,14 +1107,14 @@
     sampler_name(stageNum, &samplerName);
     segments->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
                          kSampler2D_GrSLType, samplerName.c_str());
-    locations->fSamplerUni = kUseUniform;
+    locations.fSamplerUni = kUseUniform;
 
     const char *varyingVSName, *varyingFSName;
     segments->addVarying(GrSLFloatVectorType(segments->fVaryingDims),
                          "Stage",
-                        stageNum,
-                        &varyingVSName,
-                        &varyingFSName);
+                         stageNum,
+                         &varyingVSName,
+                         &varyingFSName);
 
     if (!matName) {
         GrAssert(segments->fVaryingDims == segments->fCoordDims);
@@ -1203,7 +1167,7 @@
                                   texDomainName.c_str(),
                                   texDomainName.c_str());
         segments->fSampleCoords = coordVar;
-        locations->fTexDomUni = kUseUniform;
+        locations.fTexDomUni = kUseUniform;
     }
 
     // NOTE: GrGLProgramStages are now responsible for fetching
diff --git a/src/gpu/gl/GrGLProgram.h b/src/gpu/gl/GrGLProgram.h
index bebaa58..c397d74 100644
--- a/src/gpu/gl/GrGLProgram.h
+++ b/src/gpu/gl/GrGLProgram.h
@@ -13,7 +13,7 @@
 #include "GrGLContextInfo.h"
 #include "GrGLSL.h"
 #include "GrGLTexture.h"
-#include "GrGpu.h"
+//#include "GrGpu.h"
 
 #include "SkString.h"
 #include "SkXfermode.h"
@@ -35,28 +35,29 @@
  * Uniforms are program-local so we can't rely on fHWState to hold the
  * previous uniform state after a program change.
  */
-class GrGLProgram {
+class GrGLProgram : public GrRefCnt {
 public:
+    SK_DECLARE_INST_COUNT(GrGLProgram)
 
-    class CachedData;
+    struct Desc;
 
     GrGLProgram();
-    ~GrGLProgram();
+    virtual ~GrGLProgram();
 
     /**
      *  This is the heavy initilization routine for building a GLProgram.
-     *  The result of heavy init is not stored in datamembers of GrGLProgam,
-     *  but in a separate cacheable container.
      */
     bool genProgram(const GrGLContextInfo& gl,
-                    GrCustomStage** customStages,
-                    CachedData* programData) const;
+                    const Desc& desc,
+                    GrCustomStage** customStages);
 
      /**
       * The shader may modify the blend coeffecients. Params are in/out
       */
      void overrideBlend(GrBlendCoeff* srcCoeff, GrBlendCoeff* dstCoeff) const;
 
+     const Desc& getDesc() { return fDesc; }
+
     /**
      * Attribute indices. These should not overlap. Matrices consume 3 slots.
      */
@@ -75,16 +76,23 @@
         return 7 + GrDrawState::kMaxTexCoords + 3 * stage;
     }
 
-public:
+    enum {
+        kUnusedUniform = -1,
+    };
 
     // Parameters that affect code generation
     // These structs should be kept compact; they are the input to an
     // expensive hash key generator.
-    struct ProgramDesc {
-        ProgramDesc() {
+    struct Desc {
+        Desc() {
             // since we use this as part of a key we can't have any unitialized
             // padding
-            memset(this, 0, sizeof(ProgramDesc));
+            memset(this, 0, sizeof(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*>(this);
         }
 
         enum OutputConfig {
@@ -224,23 +232,46 @@
 
         uint8_t fColorFilterXfermode;  // casts to enum SkXfermode::Mode
         int8_t fPadding[1];
-
-    } fProgramDesc;
-    GR_STATIC_ASSERT(!(sizeof(ProgramDesc) % 4));
+    };
+    GR_STATIC_ASSERT(!(sizeof(Desc) % 4));
 
     // for code readability
-    typedef ProgramDesc::StageDesc StageDesc;
+    typedef Desc::StageDesc StageDesc;
 
 private:
+    void genInputColor(GrGLShaderBuilder* builder, SkString* inColor);
 
-    const ProgramDesc& getDesc() { return fProgramDesc; }
+    // Determines which uniforms will need to be bound.
+    void genStageCode(const GrGLContextInfo& gl,
+                      int stageNum,
+                      const char* fsInColor, // NULL means no incoming color
+                      const char* fsOutColor,
+                      const char* vsInCoord,
+                      GrGLShaderBuilder* builder);
+
+    void genGeometryShader(const GrGLContextInfo& gl, GrGLShaderBuilder* segments) const;
+
+    void genUniformCoverage(GrGLShaderBuilder* segments, SkString* inOutCoverage);
+
+    // generates code to compute coverage based on edge AA.
+    void genEdgeCoverage(const GrGLContextInfo& gl,
+                         SkString* coverageVar,
+                         GrGLShaderBuilder* builder) const;
+
+    // Creates a GL program ID, binds shader attributes to GL vertex attrs, and links the program
+    bool bindOutputsAttribsAndLinkProgram(const GrGLContextInfo& gl,
+                                          SkString texCoordAttrNames[GrDrawState::kMaxTexCoords],
+                                          bool bindColorOut,
+                                          bool bindDualSrcOut);
+
+    // Binds uniforms; initializes cache to invalid values.
+    void getUniformLocationsAndInitCache(const GrGLContextInfo& gl,
+                                         const GrGLShaderBuilder& builder);
+
+    bool compileShaders(const GrGLContextInfo& gl, const GrGLShaderBuilder& builder);
+
     const char* adjustInColor(const SkString& inColor) const;
 
-public:
-    enum {
-        kUnusedUniform = -1,
-    };
-
     struct StageUniLocations {
         GrGLint fTextureMatrixUni;
         GrGLint fSamplerUni;
@@ -273,104 +304,37 @@
         }
     };
 
-    class CachedData : public ::GrNoncopyable {
-    public:
-        CachedData() {
-            for (int i = 0; i < GrDrawState::kNumStages; ++i) {
-                fCustomStage[i] = NULL;
-            }
-        }
+    // IDs
+    GrGLuint    fVShaderID;
+    GrGLuint    fGShaderID;
+    GrGLuint    fFShaderID;
+    GrGLuint    fProgramID;
+    // shader uniform locations (-1 if shader doesn't use them)
+    UniLocations fUniLocations;
 
-        ~CachedData();
+    // The matrix sent to GL is determined by both the client's matrix and
+    // the size of the viewport.
+    GrMatrix  fViewMatrix;
+    SkISize   fViewportSize;
 
-        void copyAndTakeOwnership(CachedData& other) {
-            memcpy(this, &other, sizeof(*this));
-            for (int i = 0; i < GrDrawState::kNumStages; ++i) {
-                other.fCustomStage[i] = NULL;
-            }
-        }
+    // these reflect the current values of uniforms
+    // (GL uniform values travel with program)
+    GrColor                     fColor;
+    GrColor                     fCoverage;
+    GrColor                     fColorFilterColor;
+    GrMatrix                    fTextureMatrices[GrDrawState::kNumStages];
+    GrRect                      fTextureDomain[GrDrawState::kNumStages];
+    // The texture domain and texture matrix sent to GL depend upon the
+    // orientation.
+    GrGLTexture::Orientation    fTextureOrientation[GrDrawState::kNumStages];
 
-    public:
+    GrGLProgramStage*           fProgramStage[GrDrawState::kNumStages];
 
-        // IDs
-        GrGLuint    fVShaderID;
-        GrGLuint    fGShaderID;
-        GrGLuint    fFShaderID;
-        GrGLuint    fProgramID;
-        // shader uniform locations (-1 if shader doesn't use them)
-        UniLocations fUniLocations;
+    Desc fDesc;
 
-        // The matrix sent to GL is determined by both the client's matrix and
-        // the size of the viewport.
-        GrMatrix  fViewMatrix;
-        SkISize   fViewportSize;
+    friend class GrGpuGL; // TODO: remove this by adding getters and moving functionality.
 
-        // these reflect the current values of uniforms
-        // (GL uniform values travel with program)
-        GrColor                     fColor;
-        GrColor                     fCoverage;
-        GrColor                     fColorFilterColor;
-        GrMatrix                    fTextureMatrices[GrDrawState::kNumStages];
-        GrRect                      fTextureDomain[GrDrawState::kNumStages];
-        // The texture domain and texture matrix sent to GL depend upon the
-        // orientation.
-        GrGLTexture::Orientation    fTextureOrientation[GrDrawState::kNumStages];
-
-        GrGLProgramStage*           fCustomStage[GrDrawState::kNumStages];
-
-    private:
-        enum Constants {
-            kUniLocationPreAllocSize = 8
-        };
-
-    }; // CachedData
-
-    enum Constants {
-        kProgramKeySize = sizeof(ProgramDesc)
-    };
-
-    // Provide an opaque ProgramDesc
-    const uint32_t* keyData() const{
-        return reinterpret_cast<const uint32_t*>(&fProgramDesc);
-    }
-
-private:
-
-    // Determines which uniforms will need to be bound.
-    void genStageCode(const GrGLContextInfo& gl,
-                      int stageNum,
-                      const ProgramDesc::StageDesc& desc,
-                      const char* fsInColor, // NULL means no incoming color
-                      const char* fsOutColor,
-                      const char* vsInCoord,
-                      GrGLShaderBuilder* segments,
-                      StageUniLocations* locations,
-                      GrGLProgramStage* override) const;
-
-    void genGeometryShader(const GrGLContextInfo& gl,
-                           GrGLShaderBuilder* segments) const;
-
-    // generates code to compute coverage based on edge AA.
-    void genEdgeCoverage(const GrGLContextInfo& gl,
-                         GrVertexLayout layout,
-                         CachedData* programData,
-                         SkString* coverageVar,
-                         GrGLShaderBuilder* segments) const;
-
-    // Creates a GL program ID, binds shader attributes to GL vertex attrs, and links the program
-    bool bindOutputsAttribsAndLinkProgram(
-                const GrGLContextInfo& gl,
-                SkString texCoordAttrNames[GrDrawState::kMaxTexCoords],
-                bool bindColorOut,
-                bool bindDualSrcOut,
-                CachedData* programData) const;
-
-    // Binds uniforms; initializes cache to invalid values.
-    void getUniformLocationsAndInitCache(const GrGLShaderBuilder& builder,
-                                         const GrGLContextInfo& gl,
-                                         CachedData* programData) const;
-
-    friend class GrGpuGL;
+    typedef GrRefCnt INHERITED;
 };
 
 #endif
diff --git a/src/gpu/gl/GrGpuGL.cpp b/src/gpu/gl/GrGpuGL.cpp
index 4a63b33..21cf3b8 100644
--- a/src/gpu/gl/GrGpuGL.cpp
+++ b/src/gpu/gl/GrGpuGL.cpp
@@ -175,7 +175,6 @@
 
     this->initCaps();
 
-    fProgramData = NULL;
     fProgramCache = SkNEW_ARGS(ProgramCache, (this->glContextInfo()));
 
     fLastSuccessfulStencilFmtIdx = 0;
@@ -186,16 +185,14 @@
 }
 
 GrGpuGL::~GrGpuGL() {
-    if (fProgramData && 0 != fHWProgramID) {
+    if (0 != fHWProgramID) {
         // detach the current program so there is no confusion on OpenGL's part
         // that we want it to be deleted
-        GrAssert(fHWProgramID == fProgramData->fProgramID);
+        GrAssert(fHWProgramID == fCurrentProgram->fProgramID);
         GL_CALL(UseProgram(0));
     }
 
     delete fProgramCache;
-    fProgramCache = NULL;
-    fProgramData = NULL;
 
     // This must be called by before the GrDrawTarget destructor
     this->releaseGeometry();
diff --git a/src/gpu/gl/GrGpuGL.h b/src/gpu/gl/GrGpuGL.h
index 0871a57..5b33e0e 100644
--- a/src/gpu/gl/GrGpuGL.h
+++ b/src/gpu/gl/GrGpuGL.h
@@ -162,9 +162,8 @@
     static bool BlendCoeffReferencesConstant(GrBlendCoeff coeff);
 
     // for readability of function impls
-    typedef GrGLProgram::ProgramDesc ProgramDesc;
+    typedef GrGLProgram::Desc        ProgramDesc;
     typedef ProgramDesc::StageDesc   StageDesc;
-    typedef GrGLProgram::CachedData  CachedData;
 
     class ProgramCache : public ::GrNoncopyable {
     public:
@@ -172,31 +171,37 @@
         ~ProgramCache();
 
         void abandon();
-        CachedData* getProgramData(const GrGLProgram& desc,
-                                   GrCustomStage** stages);
+        GrGLProgram* getProgram(const GrGLProgram::Desc& desc, GrCustomStage** stages);
     private:
         enum {
-            kKeySize = GrGLProgram::kProgramKeySize,
-            // We may actually have kMaxEntries+1 shaders in the GL context
-            // because we create a new shader before evicting from the cache.
+            kKeySize = sizeof(ProgramDesc),
+            // We may actually have kMaxEntries+1 shaders in the GL context because we create a new
+            // shader before evicting from the cache.
             kMaxEntries = 32
         };
 
         class Entry;
-        typedef GrBinHashKey<Entry, kKeySize> ProgramHashKey;
+        // The value of the hash key is based on the ProgramDesc.
+        typedef GrTBinHashKey<Entry, kKeySize> ProgramHashKey;
 
         class Entry : public ::GrNoncopyable {
         public:
-            Entry() {}
-            void copyAndTakeOwnership(Entry& entry);
+            Entry() : fProgram(NULL), fLRUStamp(0) {}
+            Entry& operator = (const Entry& entry) {
+                GrSafeRef(entry.fProgram.get());
+                fProgram.reset(entry.fProgram.get());
+                fKey = entry.fKey;
+                fLRUStamp = entry.fLRUStamp;
+                return *this;
+            }
             int compare(const ProgramHashKey& key) const {
                 return fKey.compare(key);
             }
 
         public:
-            CachedData      fProgramData;
-            ProgramHashKey  fKey;
-            unsigned int    fLRUStamp;
+            SkAutoTUnref<GrGLProgram>   fProgram;
+            ProgramHashKey              fKey;
+            unsigned int                fLRUStamp; // Move outside entry?
         };
 
         GrTHashTable<Entry, ProgramHashKey, 8> fHashCache;
@@ -243,13 +248,13 @@
     // flushing the scissor after that function is called.
     void flushScissor();
 
-    static void DeleteProgram(const GrGLInterface* gl,
-                              CachedData* programData);
+    static void DeleteProgram(const GrGLInterface* gl, GrGLProgram* programData);
 
     void buildProgram(bool isPoints,
                       BlendOptFlags blendOpts,
                       GrBlendCoeff dstCoeff,
-                      GrCustomStage** customStages);
+                      GrCustomStage** customStages,
+                      ProgramDesc* desc);
 
     // Inits GrDrawTarget::Caps, sublcass may enable additional caps.
     void initCaps();
@@ -304,8 +309,7 @@
 
     // GL program-related state
     ProgramCache*               fProgramCache;
-    CachedData*                 fProgramData;
-    GrGLProgram                 fCurrentProgram;
+    SkAutoTUnref<GrGLProgram>   fCurrentProgram;
 
     ///////////////////////////////////////////////////////////////////////////
     ///@name Caching of GL State
diff --git a/src/gpu/gl/GrGpuGL_program.cpp b/src/gpu/gl/GrGpuGL_program.cpp
index af9bb05..077f807 100644
--- a/src/gpu/gl/GrGpuGL_program.cpp
+++ b/src/gpu/gl/GrGpuGL_program.cpp
@@ -14,12 +14,6 @@
 #define SKIP_CACHE_CHECK    true
 #define GR_UINT32_MAX   static_cast<uint32_t>(-1)
 
-void GrGpuGL::ProgramCache::Entry::copyAndTakeOwnership(Entry& entry) {
-    fProgramData.copyAndTakeOwnership(entry.fProgramData);
-    fKey = entry.fKey; // ownership transfer
-    fLRUStamp = entry.fLRUStamp;
-}
-
 GrGpuGL::ProgramCache::ProgramCache(const GrGLContextInfo& gl)
     : fCount(0)
     , fCurrLRUStamp(0)
@@ -28,8 +22,7 @@
 
 GrGpuGL::ProgramCache::~ProgramCache() {
     for (int i = 0; i < fCount; ++i) {
-        GrGpuGL::DeleteProgram(fGL.interface(),
-                                        &fEntries[i].fProgramData);
+        GrGpuGL::DeleteProgram(fGL.interface(), fEntries[i].fProgram);
     }
 }
 
@@ -37,15 +30,14 @@
     fCount = 0;
 }
 
-GrGLProgram::CachedData* GrGpuGL::ProgramCache::getProgramData(
-                                        const GrGLProgram& desc,
-                                        GrCustomStage** stages) {
+GrGLProgram* GrGpuGL::ProgramCache::getProgram(const ProgramDesc& desc, GrCustomStage** stages) {
     Entry newEntry;
-    newEntry.fKey.setKeyData(desc.keyData());
-        
+    newEntry.fKey.setKeyData(desc.asKey());
+
     Entry* entry = fHashCache.find(newEntry.fKey);
     if (NULL == entry) {
-        if (!desc.genProgram(fGL, stages, &newEntry.fProgramData)) {
+        newEntry.fProgram.reset(SkNEW(GrGLProgram));
+        if (!newEntry.fProgram->genProgram(fGL, desc, stages)) {
             return NULL;
         }
         if (fCount < kMaxEntries) {
@@ -60,10 +52,9 @@
                 }
             }
             fHashCache.remove(entry->fKey, entry);
-            GrGpuGL::DeleteProgram(fGL.interface(),
-                                            &entry->fProgramData);
+            GrGpuGL::DeleteProgram(fGL.interface(), entry->fProgram);
         }
-        entry->copyAndTakeOwnership(newEntry);
+        *entry = newEntry;
         fHashCache.insert(entry->fKey, entry);
     }
 
@@ -75,21 +66,20 @@
         }
     }
     ++fCurrLRUStamp;
-    return &entry->fProgramData;
+    return entry->fProgram;
 }
 
-void GrGpuGL::DeleteProgram(const GrGLInterface* gl,
-                            CachedData* programData) {
-    GR_GL_CALL(gl, DeleteShader(programData->fVShaderID));
-    if (programData->fGShaderID) {
-        GR_GL_CALL(gl, DeleteShader(programData->fGShaderID));
+void GrGpuGL::DeleteProgram(const GrGLInterface* gl, GrGLProgram* program) {
+    GR_GL_CALL(gl, DeleteShader(program->fVShaderID));
+    if (program->fGShaderID) {
+        GR_GL_CALL(gl, DeleteShader(program->fGShaderID));
     }
-    GR_GL_CALL(gl, DeleteShader(programData->fFShaderID));
-    GR_GL_CALL(gl, DeleteProgram(programData->fProgramID));
-    GR_DEBUGCODE(programData->fVShaderID = 0);
-    GR_DEBUGCODE(programData->fGShaderID = 0);
-    GR_DEBUGCODE(programData->fFShaderID = 0);
-    GR_DEBUGCODE(programData->fProgramID = 0);
+    GR_GL_CALL(gl, DeleteShader(program->fFShaderID));
+    GR_GL_CALL(gl, DeleteProgram(program->fProgramID));
+    GR_DEBUGCODE(program->fVShaderID = 0);
+    GR_DEBUGCODE(program->fGShaderID = 0);
+    GR_DEBUGCODE(program->fFShaderID = 0);
+    GR_DEBUGCODE(program->fProgramID = 0);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -150,8 +140,8 @@
             fHWPathMatrixState.fViewMatrix = vm;
             fHWPathMatrixState.fRTSize = viewportSize;
         }
-    } else if (!fProgramData->fViewMatrix.cheapEqualTo(vm) ||
-               fProgramData->fViewportSize != viewportSize) {
+    } else if (!fCurrentProgram->fViewMatrix.cheapEqualTo(vm) ||
+               fCurrentProgram->fViewportSize != viewportSize) {
         GrMatrix m;
         m.setAll(
             GrIntToScalar(2) / viewportSize.fWidth, 0, -GR_Scalar1,
@@ -174,11 +164,11 @@
         };
 
         GrAssert(GrGLProgram::kUnusedUniform != 
-                 fProgramData->fUniLocations.fViewMatrixUni);
-        GL_CALL(UniformMatrix3fv(fProgramData->fUniLocations.fViewMatrixUni,
+                 fCurrentProgram->fUniLocations.fViewMatrixUni);
+        GL_CALL(UniformMatrix3fv(fCurrentProgram->fUniLocations.fViewMatrixUni,
                                  1, false, mt));
-        fProgramData->fViewMatrix = vm;
-        fProgramData->fViewportSize = viewportSize;
+        fCurrentProgram->fViewMatrix = vm;
+        fCurrentProgram->fViewportSize = viewportSize;
     }
 }
 
@@ -225,13 +215,13 @@
         static_cast<const GrGLTexture*>(drawState.getTexture(s));
     if (NULL != texture) {
 
-        bool orientationChange = fProgramData->fTextureOrientation[s] !=
+        bool orientationChange = fCurrentProgram->fTextureOrientation[s] !=
                                  texture->orientation();
 
         const GrGLint& matrixUni =
-            fProgramData->fUniLocations.fStages[s].fTextureMatrixUni;
+            fCurrentProgram->fUniLocations.fStages[s].fTextureMatrixUni;
 
-        const GrMatrix& hwMatrix = fProgramData->fTextureMatrices[s];
+        const GrMatrix& hwMatrix = fCurrentProgram->fTextureMatrices[s];
         const GrMatrix& samplerMatrix = drawState.getSampler(s).getMatrix();
 
         if (GrGLProgram::kUnusedUniform != matrixUni &&
@@ -255,16 +245,15 @@
             };
 
             GL_CALL(UniformMatrix3fv(matrixUni, 1, false, mt));
-            fProgramData->fTextureMatrices[s] = samplerMatrix;
+            fCurrentProgram->fTextureMatrices[s] = samplerMatrix;
         }
 
-        const GrGLint& domUni = 
-            fProgramData->fUniLocations.fStages[s].fTexDomUni;
+        const GrGLint& domUni =  fCurrentProgram->fUniLocations.fStages[s].fTexDomUni;
         const GrRect &texDom = drawState.getSampler(s).getTextureDomain();
         if (GrGLProgram::kUnusedUniform != domUni &&
-            (orientationChange ||fProgramData->fTextureDomain[s] != texDom)) {
+            (orientationChange ||fCurrentProgram->fTextureDomain[s] != texDom)) {
 
-            fProgramData->fTextureDomain[s] = texDom;
+            fCurrentProgram->fTextureDomain[s] = texDom;
 
             float values[4] = {
                 GrScalarToFloat(texDom.left()),
@@ -283,15 +272,14 @@
             }
             GL_CALL(Uniform4fv(domUni, 1, values));
         }
-        fProgramData->fTextureOrientation[s] = texture->orientation();
+        fCurrentProgram->fTextureOrientation[s] = texture->orientation();
     }
 }
 
 
 void GrGpuGL::flushColorMatrix() {
-    // const ProgramDesc& desc = fCurrentProgram.getDesc();
-    int matrixUni = fProgramData->fUniLocations.fColorMatrixUni;
-    int vecUni = fProgramData->fUniLocations.fColorMatrixVecUni;
+    int matrixUni = fCurrentProgram->fUniLocations.fColorMatrixUni;
+    int vecUni = fCurrentProgram->fUniLocations.fColorMatrixVecUni;
     if (GrGLProgram::kUnusedUniform != matrixUni
      && GrGLProgram::kUnusedUniform != vecUni) {
         const float* m = this->getDrawState().getColorMatrix();
@@ -320,7 +308,7 @@
 }
 
 void GrGpuGL::flushColor(GrColor color) {
-    const ProgramDesc& desc = fCurrentProgram.getDesc();
+    const ProgramDesc& desc = fCurrentProgram->getDesc();
     const GrDrawState& drawState = this->getDrawState();
 
     if (this->getVertexLayout() & kColor_VertexLayoutBit) {
@@ -340,15 +328,14 @@
                 }
                 break;
             case ProgramDesc::kUniform_ColorInput:
-                if (fProgramData->fColor != color) {
+                if (fCurrentProgram->fColor != color) {
                     // OpenGL ES doesn't support unsigned byte varieties of
                     // glUniform
                     float c[] = GR_COLOR_TO_VEC4(color);
                     GrAssert(GrGLProgram::kUnusedUniform != 
-                             fProgramData->fUniLocations.fColorUni);
-                    GL_CALL(Uniform4fv(fProgramData->fUniLocations.fColorUni,
-                                        1, c));
-                    fProgramData->fColor = color;
+                             fCurrentProgram->fUniLocations.fColorUni);
+                    GL_CALL(Uniform4fv(fCurrentProgram->fUniLocations.fColorUni, 1, c));
+                    fCurrentProgram->fColor = color;
                 }
                 break;
             case ProgramDesc::kSolidWhite_ColorInput:
@@ -358,18 +345,18 @@
                 GrCrash("Unknown color type.");
         }
     }
-    if (fProgramData->fUniLocations.fColorFilterUni
+    if (fCurrentProgram->fUniLocations.fColorFilterUni
                 != GrGLProgram::kUnusedUniform
-            && fProgramData->fColorFilterColor
+            && fCurrentProgram->fColorFilterColor
                 != drawState.getColorFilterColor()) {
         float c[] = GR_COLOR_TO_VEC4(drawState.getColorFilterColor());
-        GL_CALL(Uniform4fv(fProgramData->fUniLocations.fColorFilterUni, 1, c));
-        fProgramData->fColorFilterColor = drawState.getColorFilterColor();
+        GL_CALL(Uniform4fv(fCurrentProgram->fUniLocations.fColorFilterUni, 1, c));
+        fCurrentProgram->fColorFilterColor = drawState.getColorFilterColor();
     }
 }
 
 void GrGpuGL::flushCoverage(GrColor coverage) {
-    const ProgramDesc& desc = fCurrentProgram.getDesc();
+    const ProgramDesc& desc = fCurrentProgram->getDesc();
     // const GrDrawState& drawState = this->getDrawState();
 
 
@@ -390,15 +377,14 @@
                 }
                 break;
             case ProgramDesc::kUniform_ColorInput:
-                if (fProgramData->fCoverage != coverage) {
+                if (fCurrentProgram->fCoverage != coverage) {
                     // OpenGL ES doesn't support unsigned byte varieties of
                     // glUniform
                     float c[] = GR_COLOR_TO_VEC4(coverage);
                     GrAssert(GrGLProgram::kUnusedUniform != 
-                             fProgramData->fUniLocations.fCoverageUni);
-                    GL_CALL(Uniform4fv(fProgramData->fUniLocations.fCoverageUni,
-                                        1, c));
-                    fProgramData->fCoverage = coverage;
+                             fCurrentProgram->fUniLocations.fCoverageUni);
+                    GL_CALL(Uniform4fv(fCurrentProgram->fUniLocations.fCoverageUni, 1, c));
+                    fCurrentProgram->fCoverage = coverage;
                 }
                 break;
             case ProgramDesc::kSolidWhite_ColorInput:
@@ -428,20 +414,21 @@
         }
 
         GrCustomStage* customStages [GrDrawState::kNumStages];
-        this->buildProgram(kDrawPoints_DrawType == type,
-                           blendOpts, dstCoeff, customStages);
-        fProgramData = fProgramCache->getProgramData(fCurrentProgram,
-                                                     customStages);
-        if (NULL == fProgramData) {
+        GrGLProgram::Desc desc;
+        this->buildProgram(kDrawPoints_DrawType == type, blendOpts, dstCoeff, customStages, &desc);
+
+        fCurrentProgram.reset(fProgramCache->getProgram(desc, customStages));
+        if (NULL == fCurrentProgram.get()) {
             GrAssert(!"Failed to create program!");
             return false;
         }
+        fCurrentProgram.get()->ref();
 
-        if (fHWProgramID != fProgramData->fProgramID) {
-            GL_CALL(UseProgram(fProgramData->fProgramID));
-            fHWProgramID = fProgramData->fProgramID;
+        if (fHWProgramID != fCurrentProgram->fProgramID) {
+            GL_CALL(UseProgram(fCurrentProgram->fProgramID));
+            fHWProgramID = fCurrentProgram->fProgramID;
         }
-        fCurrentProgram.overrideBlend(&srcCoeff, &dstCoeff);
+        fCurrentProgram->overrideBlend(&srcCoeff, &dstCoeff);
         this->flushBlend(kDrawLines_DrawType == type, srcCoeff, dstCoeff);
 
         GrColor color;
@@ -472,16 +459,13 @@
 
                 this->flushTextureMatrixAndDomain(s);
 
-                if (NULL != fProgramData->fCustomStage[s]) {
-                    const GrSamplerState& sampler =
-                        this->getDrawState().getSampler(s);
-                    const GrGLTexture* texture =
-                        static_cast<const GrGLTexture*>(
-                            this->getDrawState().getTexture(s));
-                    fProgramData->fCustomStage[s]->setData(
-                        this->glInterface(),
-                        *sampler.getCustomStage(),
-                        drawState.getRenderTarget(), s);
+                if (NULL != fCurrentProgram->fProgramStage[s]) {
+                    const GrSamplerState& sampler = this->getDrawState().getSampler(s);
+                    const GrGLTexture* texture = static_cast<const GrGLTexture*>(
+                                                    this->getDrawState().getTexture(s));
+                    fCurrentProgram->fProgramStage[s]->setData(this->glInterface(),
+                                                               *sampler.getCustomStage(),
+                                                               drawState.getRenderTarget(), s);
                 }
             }
         }
@@ -658,7 +642,7 @@
 
 namespace {
 
-void setup_custom_stage(GrGLProgram::ProgramDesc::StageDesc* stage,
+void setup_custom_stage(GrGLProgram::Desc::StageDesc* stage,
                         const GrSamplerState& sampler,
                         GrCustomStage** customStages,
                         GrGLProgram* program, int index) {
@@ -678,8 +662,8 @@
 void GrGpuGL::buildProgram(bool isPoints,
                            BlendOptFlags blendOpts,
                            GrBlendCoeff dstCoeff,
-                           GrCustomStage** customStages) {
-    ProgramDesc& desc = fCurrentProgram.fProgramDesc;
+                           GrCustomStage** customStages,
+                           ProgramDesc* desc) {
     const GrDrawState& drawState = this->getDrawState();
 
     // This should already have been caught
@@ -696,31 +680,31 @@
     // to a canonical value to avoid duplicate programs with different keys.
 
     // Must initialize all fields or cache will have false negatives!
-    desc.fVertexLayout = this->getVertexLayout();
+    desc->fVertexLayout = this->getVertexLayout();
 
-    desc.fEmitsPointSize = isPoints;
+    desc->fEmitsPointSize = isPoints;
 
     bool requiresAttributeColors = 
-        !skipColor && SkToBool(desc.fVertexLayout & kColor_VertexLayoutBit);
+        !skipColor && SkToBool(desc->fVertexLayout & kColor_VertexLayoutBit);
     bool requiresAttributeCoverage = 
-        !skipCoverage && SkToBool(desc.fVertexLayout &
+        !skipCoverage && SkToBool(desc->fVertexLayout &
                                   kCoverage_VertexLayoutBit);
 
     // fColorInput/fCoverageInput records how colors are specified for the.
     // program. So we strip the bits from the layout to avoid false negatives
     // when searching for an existing program in the cache.
-    desc.fVertexLayout &= ~(kColor_VertexLayoutBit | kCoverage_VertexLayoutBit);
+    desc->fVertexLayout &= ~(kColor_VertexLayoutBit | kCoverage_VertexLayoutBit);
 
-    desc.fColorFilterXfermode = skipColor ?
+    desc->fColorFilterXfermode = skipColor ?
                                 SkXfermode::kDst_Mode :
                                 drawState.getColorFilterMode();
 
-    desc.fColorMatrixEnabled = drawState.isStateFlagEnabled(GrDrawState::kColorMatrix_StateBit);
+    desc->fColorMatrixEnabled = drawState.isStateFlagEnabled(GrDrawState::kColorMatrix_StateBit);
 
     // no reason to do edge aa or look at per-vertex coverage if coverage is
     // ignored
     if (skipCoverage) {
-        desc.fVertexLayout &= ~(kEdge_VertexLayoutBit |
+        desc->fVertexLayout &= ~(kEdge_VertexLayoutBit |
                                 kCoverage_VertexLayoutBit);
     }
 
@@ -729,40 +713,40 @@
                              (!requiresAttributeColors &&
                               0xffffffff == drawState.getColor());
     if (GR_AGGRESSIVE_SHADER_OPTS && colorIsTransBlack) {
-        desc.fColorInput = ProgramDesc::kTransBlack_ColorInput;
+        desc->fColorInput = ProgramDesc::kTransBlack_ColorInput;
     } else if (GR_AGGRESSIVE_SHADER_OPTS && colorIsSolidWhite) {
-        desc.fColorInput = ProgramDesc::kSolidWhite_ColorInput;
+        desc->fColorInput = ProgramDesc::kSolidWhite_ColorInput;
     } else if (GR_GL_NO_CONSTANT_ATTRIBUTES && !requiresAttributeColors) {
-        desc.fColorInput = ProgramDesc::kUniform_ColorInput;
+        desc->fColorInput = ProgramDesc::kUniform_ColorInput;
     } else {
-        desc.fColorInput = ProgramDesc::kAttribute_ColorInput;
+        desc->fColorInput = ProgramDesc::kAttribute_ColorInput;
     }
     
     bool covIsSolidWhite = !requiresAttributeCoverage &&
                            0xffffffff == drawState.getCoverage();
     
     if (skipCoverage) {
-        desc.fCoverageInput = ProgramDesc::kTransBlack_ColorInput;
+        desc->fCoverageInput = ProgramDesc::kTransBlack_ColorInput;
     } else if (covIsSolidWhite) {
-        desc.fCoverageInput = ProgramDesc::kSolidWhite_ColorInput;
+        desc->fCoverageInput = ProgramDesc::kSolidWhite_ColorInput;
     } else if (GR_GL_NO_CONSTANT_ATTRIBUTES && !requiresAttributeCoverage) {
-        desc.fCoverageInput = ProgramDesc::kUniform_ColorInput;
+        desc->fCoverageInput = ProgramDesc::kUniform_ColorInput;
     } else {
-        desc.fCoverageInput = ProgramDesc::kAttribute_ColorInput;
+        desc->fCoverageInput = ProgramDesc::kAttribute_ColorInput;
     }
 
     int lastEnabledStage = -1;
 
-    if (!skipCoverage && (desc.fVertexLayout &
+    if (!skipCoverage && (desc->fVertexLayout &
                           GrDrawTarget::kEdge_VertexLayoutBit)) {
-        desc.fVertexEdgeType = drawState.getVertexEdgeType();
+        desc->fVertexEdgeType = drawState.getVertexEdgeType();
     } else {
         // use canonical value when not set to avoid cache misses
-        desc.fVertexEdgeType = GrDrawState::kHairLine_EdgeType;
+        desc->fVertexEdgeType = GrDrawState::kHairLine_EdgeType;
     }
 
     for (int s = 0; s < GrDrawState::kNumStages; ++s) {
-        StageDesc& stage = desc.fStages[s];
+        StageDesc& stage = desc->fStages[s];
 
         stage.fOptFlags = 0;
         stage.setEnabled(this->isStageEnabled(s));
@@ -825,8 +809,7 @@
                 }
             }
 
-            setup_custom_stage(&stage, sampler, customStages,
-                               &fCurrentProgram, s);
+            setup_custom_stage(&stage, sampler, customStages, fCurrentProgram.get(), s);
 
         } else {
             stage.fOptFlags         = 0;
@@ -841,23 +824,23 @@
         // when rounding.
         GrAssert(4 == GrBytesPerPixel(drawState.getRenderTarget()->config()));
         if (kUpOnWrite_DownOnRead_UnpremulConversion == fUnpremulConversion) {
-            desc.fOutputConfig =
+            desc->fOutputConfig =
                 ProgramDesc::kUnpremultiplied_RoundUp_OutputConfig;
         } else {
-            desc.fOutputConfig =
+            desc->fOutputConfig =
                 ProgramDesc::kUnpremultiplied_RoundDown_OutputConfig;
         }
     } else {
-        desc.fOutputConfig = ProgramDesc::kPremultiplied_OutputConfig;
+        desc->fOutputConfig = ProgramDesc::kPremultiplied_OutputConfig;
     }
 
-    desc.fDualSrcOutput = ProgramDesc::kNone_DualSrcOutput;
+    desc->fDualSrcOutput = ProgramDesc::kNone_DualSrcOutput;
 
     // currently the experimental GS will only work with triangle prims
     // (and it doesn't do anything other than pass through values from
     // the VS to the FS anyway).
 #if 0 && GR_GL_EXPERIMENTAL_GS
-    desc.fExperimentalGS = this->getCaps().fGeometryShaderSupport;
+    desc->fExperimentalGS = this->getCaps().fGeometryShaderSupport;
 #endif
 
     // we want to avoid generating programs with different "first cov stage"
@@ -866,7 +849,7 @@
     // coverage stages or the distinction between coverage and color is
     // immaterial.
     int firstCoverageStage = GrDrawState::kNumStages;
-    desc.fFirstCoverageStage = GrDrawState::kNumStages;
+    desc->fFirstCoverageStage = GrDrawState::kNumStages;
     bool hasCoverage = drawState.getFirstCoverageStage() <= lastEnabledStage;
     if (hasCoverage) {
         firstCoverageStage = drawState.getFirstCoverageStage();
@@ -876,13 +859,13 @@
     if (!hasCoverage) {
         hasCoverage =
                requiresAttributeCoverage ||
-               (desc.fVertexLayout & GrDrawTarget::kEdge_VertexLayoutBit);
+               (desc->fVertexLayout & GrDrawTarget::kEdge_VertexLayoutBit);
     }
 
     if (hasCoverage) {
         // color filter is applied between color/coverage computation
-        if (SkXfermode::kDst_Mode != desc.fColorFilterXfermode) {
-            desc.fFirstCoverageStage = firstCoverageStage;
+        if (SkXfermode::kDst_Mode != desc->fColorFilterXfermode) {
+            desc->fFirstCoverageStage = firstCoverageStage;
         }
 
         if (this->getCaps().fDualSourceBlendingSupport &&
@@ -890,18 +873,18 @@
                            kCoverageAsAlpha_BlendOptFlag))) {
             if (kZero_GrBlendCoeff == dstCoeff) {
                 // write the coverage value to second color
-                desc.fDualSrcOutput =  ProgramDesc::kCoverage_DualSrcOutput;
-                desc.fFirstCoverageStage = firstCoverageStage;
+                desc->fDualSrcOutput =  ProgramDesc::kCoverage_DualSrcOutput;
+                desc->fFirstCoverageStage = firstCoverageStage;
             } else if (kSA_GrBlendCoeff == dstCoeff) {
                 // SA dst coeff becomes 1-(1-SA)*coverage when dst is partially 
                 // cover
-                desc.fDualSrcOutput = ProgramDesc::kCoverageISA_DualSrcOutput;
-                desc.fFirstCoverageStage = firstCoverageStage;
+                desc->fDualSrcOutput = ProgramDesc::kCoverageISA_DualSrcOutput;
+                desc->fFirstCoverageStage = firstCoverageStage;
             } else if (kSC_GrBlendCoeff == dstCoeff) {
                 // SA dst coeff becomes 1-(1-SA)*coverage when dst is partially
                 // cover
-                desc.fDualSrcOutput = ProgramDesc::kCoverageISC_DualSrcOutput;
-                desc.fFirstCoverageStage = firstCoverageStage;
+                desc->fDualSrcOutput = ProgramDesc::kCoverageISC_DualSrcOutput;
+                desc->fFirstCoverageStage = firstCoverageStage;
             }
         }
     }
diff --git a/src/gpu/gl/GrGpuGL_unittest.cpp b/src/gpu/gl/GrGpuGL_unittest.cpp
index 004013a..9dd3e77 100644
--- a/src/gpu/gl/GrGpuGL_unittest.cpp
+++ b/src/gpu/gl/GrGpuGL_unittest.cpp
@@ -231,8 +231,6 @@
         StageDesc::kSmearAlpha_InConfigFlag,
         StageDesc::kSmearRed_InConfigFlag,
     };
-    GrGLProgram program;
-    ProgramDesc& pdesc = program.fProgramDesc;
 
     static const int NUM_TESTS = 512;
 
@@ -247,6 +245,7 @@
         }
 #endif
 
+        ProgramDesc pdesc;
         pdesc.fVertexLayout = 0;
         pdesc.fEmitsPointSize = random.nextF() > .5f;
         pdesc.fColorInput = random_int(&random, ProgramDesc::kColorInputCnt);
@@ -317,14 +316,15 @@
                 }
             }
         }
-        CachedData cachedData;
         GR_STATIC_ASSERT(sizeof(customStages) ==
                          GrDrawState::kNumStages * sizeof(GrCustomStage*));
         GrCustomStage** stages = reinterpret_cast<GrCustomStage**>(&customStages);
-        if (!program.genProgram(this->glContextInfo(), stages, &cachedData)) {
+        SkAutoTUnref<GrGLProgram> program(SkNEW(GrGLProgram));
+
+        if (!program->genProgram(this->glContextInfo(), pdesc, stages)) {
             return false;
         }
-        DeleteProgram(this->glInterface(), &cachedData);
+        DeleteProgram(this->glInterface(), program);
     }
     return true;
 }
diff --git a/src/gpu/gr_unittests.cpp b/src/gpu/gr_unittests.cpp
index d554861..4d11721 100644
--- a/src/gpu/gr_unittests.cpp
+++ b/src/gpu/gr_unittests.cpp
@@ -87,10 +87,10 @@
         kDataLenUsedForKey = 8
     };
 
-    GrBinHashKey<BogusEntry, kDataLenUsedForKey> keyA;
+    GrTBinHashKey<BogusEntry, kDataLenUsedForKey> keyA;
     keyA.setKeyData(testStringA);
     // test copy constructor and comparison
-    GrBinHashKey<BogusEntry, kDataLenUsedForKey> keyA2(keyA);
+    GrTBinHashKey<BogusEntry, kDataLenUsedForKey> keyA2(keyA);
     GrAssert(keyA.compare(keyA2) == 0);
     GrAssert(keyA.getHash() == keyA2.getHash());
     // test re-init
@@ -98,7 +98,7 @@
     GrAssert(keyA.compare(keyA2) == 0);
     GrAssert(keyA.getHash() == keyA2.getHash());
     // test sorting
-    GrBinHashKey<BogusEntry, kDataLenUsedForKey> keyB;
+    GrTBinHashKey<BogusEntry, kDataLenUsedForKey> keyB;
     keyB.setKeyData(testStringB);
     GrAssert(keyA.compare(keyB) < 0);
     GrAssert(keyA.getHash() != keyB.getHash());