[GPU] Use new Var type for inputs/outputs of FS and VS
Review URL: http://codereview.appspot.com/5056048/



git-svn-id: http://skia.googlecode.com/svn/trunk@2289 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/gpu/include/GrAllocator.h b/gpu/include/GrAllocator.h
index 7652be3..eca2ab0 100755
--- a/gpu/include/GrAllocator.h
+++ b/gpu/include/GrAllocator.h
@@ -46,7 +46,7 @@
      *
      * @return pointer to the added item.
      */
-    void* push_back() {        
+    void* push_back() {
         int indexInBlock = fCount % fItemsPerBlock;
         // we always have at least one block
         if (0 == indexInBlock) {
@@ -153,7 +153,7 @@
      *                          Must be at least size(T)*itemsPerBlock sized.
      *                          Caller is responsible for freeing this memory.
      */
-    GrTAllocator(int itemsPerBlock, void* initialBlock)
+    explicit GrTAllocator(int itemsPerBlock, void* initialBlock = NULL)
         : fAllocator(sizeof(T), itemsPerBlock, initialBlock) {}
 
     /**
@@ -163,7 +163,7 @@
      *                          and the size of subsequent blocks.
      */
     template <int N>
-    GrTAllocator(SkAlignedSTStorage<N,T>* initialBlock)
+    explicit GrTAllocator(SkAlignedSTStorage<N,T>* initialBlock)
         : fAllocator(sizeof(T), N, initialBlock->get()) {}
     
     /**
@@ -177,7 +177,14 @@
         new (item) T;
         return *(T*)item;
     }
-    
+
+    T& push_back(const T& t) {
+        void* item = fAllocator.push_back();
+        GrAssert(NULL != item);
+        new (item) T(t);
+        return *(T*)item;
+    }
+
     /**
      * removes all added items
      */
diff --git a/gpu/src/GrGLProgram.cpp b/gpu/src/GrGLProgram.cpp
index 0efb99f..07a2e04 100644
--- a/gpu/src/GrGLProgram.cpp
+++ b/gpu/src/GrGLProgram.cpp
@@ -9,8 +9,8 @@
 
 #include "GrGLProgram.h"
 
-#include "GrGLConfig.h"
-
+#include "GrAllocator.h"
+#include "GrGLShaderVar.h"
 #include "SkTrace.h"
 #include "SkXfermode.h"
 
@@ -36,6 +36,32 @@
 
 #define PRINT_SHADERS 0
 
+typedef GrTAllocator<GrGLShaderVar> VarArray;
+
+// number of each input/output type in a single allocation block
+static const int gVarsPerBlock = 8;
+// except FS outputs where we expect 2 at most.
+static const int gMaxFSOutputs = 2;
+
+struct ShaderCodeSegments {
+    ShaderCodeSegments() 
+    : fVSUnis(gVarsPerBlock)
+    , fVSAttrs(gVarsPerBlock)
+    , fVaryings(gVarsPerBlock)
+    , fFSUnis(gVarsPerBlock)
+    , fFSOutputs(gMaxFSOutputs) {}
+    GrStringBuilder fHeader; // VS+FS, GLSL version, etc
+    VarArray        fVSUnis;
+    VarArray        fVSAttrs;
+    VarArray        fVaryings;
+    VarArray        fFSUnis;
+    VarArray        fFSOutputs;
+    GrStringBuilder fFSFunctions;
+    GrStringBuilder fVSCode;
+    GrStringBuilder fFSCode;
+};
+
+
 #if GR_GL_ATTRIBUTE_MATRICES
     #define VIEW_MATRIX_NAME "aViewM"
 #else
@@ -49,53 +75,61 @@
 #define EDGES_UNI_NAME "uEdges"
 #define COL_FILTER_UNI_NAME "uColorFilter"
 
-static inline void tex_attr_name(int coordIdx, GrStringBuilder* s) {
+namespace {
+inline void tex_attr_name(int coordIdx, GrStringBuilder* s) {
     *s = "aTexCoord";
     s->appendS32(coordIdx);
 }
 
-static inline const char* float_vector_type(int count) {
-    static const char* FLOAT_VECS[] = {"ERROR", "float", "vec2", "vec3", "vec4"};
-    GrAssert(count >= 1 && count < (int)GR_ARRAY_COUNT(FLOAT_VECS));
-    return FLOAT_VECS[count];
+inline GrGLShaderVar::Type float_vector_type(int count) {
+    GR_STATIC_ASSERT(GrGLShaderVar::kFloat_Type == 0);
+    GR_STATIC_ASSERT(GrGLShaderVar::kVec2f_Type == 1);
+    GR_STATIC_ASSERT(GrGLShaderVar::kVec3f_Type == 2);
+    GR_STATIC_ASSERT(GrGLShaderVar::kVec4f_Type == 3);
+    GrAssert(count > 0 && count <= 4);
+    return (GrGLShaderVar::Type)(count - 1);
 }
 
-static inline const char* vector_homog_coord(int count) {
+inline const char* float_vector_type_str(int count) {
+    return GrGLShaderVar::TypeString(float_vector_type(count));
+}
+
+inline const char* vector_homog_coord(int count) {
     static const char* HOMOGS[] = {"ERROR", "", ".y", ".z", ".w"};
     GrAssert(count >= 1 && count < (int)GR_ARRAY_COUNT(HOMOGS));
     return HOMOGS[count];
 }
 
-static inline const char* vector_nonhomog_coords(int count) {
+inline const char* vector_nonhomog_coords(int count) {
     static const char* NONHOMOGS[] = {"ERROR", "", ".x", ".xy", ".xyz"};
     GrAssert(count >= 1 && count < (int)GR_ARRAY_COUNT(NONHOMOGS));
     return NONHOMOGS[count];
 }
 
-static inline const char* vector_all_coords(int count) {
+inline const char* vector_all_coords(int count) {
     static const char* ALL[] = {"ERROR", "", ".xy", ".xyz", ".xyzw"};
     GrAssert(count >= 1 && count < (int)GR_ARRAY_COUNT(ALL));
     return ALL[count];
 }
 
-static inline const char* all_ones_vec(int count) {
+inline const char* all_ones_vec(int count) {
     static const char* ONESVEC[] = {"ERROR", "1.0", "vec2(1,1)",
                                     "vec3(1,1,1)", "vec4(1,1,1,1)"};
     GrAssert(count >= 1 && count < (int)GR_ARRAY_COUNT(ONESVEC));
     return ONESVEC[count];
 }
 
-static inline const char* all_zeros_vec(int count) {
+inline const char* all_zeros_vec(int count) {
     static const char* ZEROSVEC[] = {"ERROR", "0.0", "vec2(0,0)",
                                     "vec3(0,0,0)", "vec4(0,0,0,0)"};
     GrAssert(count >= 1 && count < (int)GR_ARRAY_COUNT(ZEROSVEC));
     return ZEROSVEC[count];
 }
 
-static inline const char* declared_color_output_name() { return "fsColorOut"; }
-static inline const char* dual_source_output_name() { return "dualSourceOut"; }
+inline const char* declared_color_output_name() { return "fsColorOut"; }
+inline const char* dual_source_output_name() { return "dualSourceOut"; }
 
-static void tex_matrix_name(int stage, GrStringBuilder* s) {
+inline void tex_matrix_name(int stage, GrStringBuilder* s) {
 #if GR_GL_ATTRIBUTE_MATRICES
     *s = "aTexM";
 #else
@@ -104,42 +138,43 @@
     s->appendS32(stage);
 }
 
-static void normalized_texel_size_name(int stage, GrStringBuilder* s) {
+inline void normalized_texel_size_name(int stage, GrStringBuilder* s) {
     *s = "uTexelSize";
     s->appendS32(stage);
 }
 
-static void sampler_name(int stage, GrStringBuilder* s) {
+inline void sampler_name(int stage, GrStringBuilder* s) {
     *s = "uSampler";
     s->appendS32(stage);
 }
 
-static void stage_varying_name(int stage, GrStringBuilder* s) {
+inline void stage_varying_name(int stage, GrStringBuilder* s) {
     *s = "vStage";
     s->appendS32(stage);
 }
 
-static void radial2_param_name(int stage, GrStringBuilder* s) {
+inline void radial2_param_name(int stage, GrStringBuilder* s) {
     *s = "uRadial2Params";
     s->appendS32(stage);
 }
 
-static void radial2_varying_name(int stage, GrStringBuilder* s) {
+inline void radial2_varying_name(int stage, GrStringBuilder* s) {
     *s = "vB";
     s->appendS32(stage);
 }
 
-static void convolve_param_names(int stage, GrStringBuilder* k, GrStringBuilder* i) {
+inline void convolve_param_names(int stage, GrStringBuilder* k, GrStringBuilder* i) {
     *k = "uKernel";
     k->appendS32(stage);
     *i = "uImageIncrement";
     i->appendS32(stage);
 }
 
-static void tex_domain_name(int stage, GrStringBuilder* s) {
+inline void tex_domain_name(int stage, GrStringBuilder* s) {
     *s = "uTexDom";
     s->appendS32(stage);
 }
+}
 
 GrGLProgram::GrGLProgram() {
 }
@@ -321,15 +356,39 @@
     add_helper(outputVar, colorStr.c_str(), constStr.c_str(), fsCode);
 }
 
+namespace {
+
+const char* glsl_version_string(const GrGLInterface* gl,
+                                GrGLProgram::GLSLVersion v) {
+    switch (v) {
+        case GrGLProgram::k120_GLSLVersion:
+            if (gl->supportsES()) {
+                // ES2s shader language is based on version 1.20 but is version
+                // 1.00 of the ES language.
+                return "#version 100\n";
+            } else {
+                return "#version 120\n";
+            }
+        case GrGLProgram::k150_GLSLVersion:
+            GrAssert(!gl->supportsES());
+            return "#version 150\n";
+        default:
+            GrCrash("Unknown GL version.");
+            return ""; // suppress warning
+    }
+}
+
+}
+
 void GrGLProgram::genEdgeCoverage(const GrGLInterface* gl,
                                   GrVertexLayout layout,
                                   CachedData* programData,
                                   GrStringBuilder* coverageVar,
                                   ShaderCodeSegments* segments) const {
     if (fProgramDesc.fEdgeAANumEdges > 0) {
-        segments->fFSUnis.append("uniform vec3 " EDGES_UNI_NAME "[");
-        segments->fFSUnis.appendS32(fProgramDesc.fEdgeAANumEdges);
-        segments->fFSUnis.append("];\n");
+        segments->fFSUnis.push_back().set(GrGLShaderVar::kVec3f_Type,
+                                          EDGES_UNI_NAME,
+                                          fProgramDesc.fEdgeAANumEdges);
         programData->fUniLocations.fEdgesUni = kUseUniform;
         int count = fProgramDesc.fEdgeAANumEdges;
         segments->fFSCode.append(
@@ -376,8 +435,8 @@
         }
         *coverageVar = "edgeAlpha";
     } else  if (layout & GrDrawTarget::kEdge_VertexLayoutBit) {
-        segments->fVSAttrs.append("attribute vec4 " EDGE_ATTR_NAME ";\n");
-        segments->fVaryings.append("varying vec4 vEdge;\n");
+        segments->fVaryings.push_back().set(GrGLShaderVar::kVec4f_Type, "vEdge");
+        segments->fVSAttrs.push_back().set(GrGLShaderVar::kVec4f_Type, EDGE_ATTR_NAME);
         segments->fVSCode.append("\tvEdge = " EDGE_ATTR_NAME ";\n");
         if (GrDrawTarget::kHairLine_EdgeType == fProgramDesc.fVertexEdgeType) {
             segments->fFSCode.append("\tfloat edgeAlpha = abs(dot(vec3(gl_FragCoord.xy,1), vEdge.xyz));\n");
@@ -402,7 +461,33 @@
     }
 }
 
-bool GrGLProgram::genProgram(const GrGLInterface* gl, 
+namespace {
+
+// returns true if the color output was explicitly declared or not.
+bool decl_and_get_fs_color_output(GrGLProgram::GLSLVersion v,
+                                  VarArray* fsOutputs,
+                                  const char** name) {
+    switch (v) {
+        case GrGLProgram::k120_GLSLVersion:
+            *name = "gl_FragColor";
+            return false;
+            break;
+        case GrGLProgram::k150_GLSLVersion:
+            *name = declared_color_output_name();
+            fsOutputs->push_back().set(GrGLShaderVar::kVec4f_Type,
+                                       declared_color_output_name());
+            return true;
+            break;
+        default:
+            GrCrash("Unknown GLSL version.");
+            return false; // suppress warning
+    }
+}
+
+}
+
+bool GrGLProgram::genProgram(const GrGLInterface* gl,
+                             GLSLVersion glslVersion,
                              GrGLProgram::CachedData* programData) const {
 
     ShaderCodeSegments segments;
@@ -432,25 +517,19 @@
     // declare an output, which is incompatible with gl_FragColor/gl_FragData.
     const char* fsColorOutput;
     bool dualSourceOutputWritten = false;
-    bool usingDeclaredOutputs = ProgramDesc::kNone_DualSrcOutput !=
-                                fProgramDesc.fDualSrcOutput;
-    if (usingDeclaredOutputs) {
-        GrAssert(0 == segments.fHeader.size());
-        segments.fHeader.printf("#version 150\n");
-        fsColorOutput = declared_color_output_name();
-        segments.fFSOutputs.appendf("out vec4 %s;\n", fsColorOutput);
-    } else {
-        fsColorOutput = "gl_FragColor";
-    }
+    segments.fHeader.printf(glsl_version_string(gl, glslVersion));
+    bool isColorDeclared = decl_and_get_fs_color_output(glslVersion,
+                                                        &segments.fFSOutputs,
+                                                        &fsColorOutput);
 
 #if GR_GL_ATTRIBUTE_MATRICES
-    segments.fVSAttrs += "attribute mat3 " VIEW_MATRIX_NAME ";\n";
+    segments.fVSAttrs.push_back().set(GrGLShaderVar::kMat33f_Type, VIEW_MATRIX_NAME);
     programData->fUniLocations.fViewMatrixUni = kSetAsAttribute;
 #else
-    segments.fVSUnis  += "uniform mat3 " VIEW_MATRIX_NAME ";\n";
+    segments.fVSUnis.push_back().set(GrGLShaderVar::kMat33f_Type, VIEW_MATRIX_NAME);
     programData->fUniLocations.fViewMatrixUni = kUseUniform;
 #endif
-    segments.fVSAttrs += "attribute vec2 " POS_ATTR_NAME ";\n";
+    segments.fVSAttrs.push_back().set(GrGLShaderVar::kVec2f_Type, POS_ATTR_NAME);
 
     segments.fVSCode.append(
         "void main() {\n"
@@ -463,13 +542,16 @@
     if (needComputedColor) {
         switch (fProgramDesc.fColorType) {
             case ProgramDesc::kAttribute_ColorType:
-                segments.fVSAttrs.append( "attribute vec4 " COL_ATTR_NAME ";\n");
-                segments.fVaryings.append("varying vec4 vColor;\n");
-                segments.fVSCode.append(    "\tvColor = " COL_ATTR_NAME ";\n");
+                segments.fVSAttrs.push_back().set(GrGLShaderVar::kVec4f_Type,
+                                                  COL_ATTR_NAME);
+                segments.fVaryings.push_back().set(GrGLShaderVar::kVec4f_Type,
+                                                   "vColor");
+                segments.fVSCode.append("\tvColor = " COL_ATTR_NAME ";\n");
                 inColor = "vColor";
                 break;
             case ProgramDesc::kUniform_ColorType:
-                segments.fFSUnis.append(  "uniform vec4 " COL_UNI_NAME ";\n");
+                segments.fFSUnis.push_back().set(GrGLShaderVar::kVec4f_Type,
+                                                 COL_UNI_NAME);
                 programData->fUniLocations.fColorUni = kUseUniform;
                 inColor = COL_UNI_NAME;
                 break;
@@ -490,7 +572,8 @@
     for (int t = 0; t < GrDrawTarget::kMaxTexCoords; ++t) {
         if (GrDrawTarget::VertexUsesTexCoordIdx(t, layout)) {
             tex_attr_name(t, texCoordAttrs + t);
-            segments.fVSAttrs.appendf("attribute vec2 %s;\n", texCoordAttrs[t].c_str());
+            segments.fVSAttrs.push_back().set(GrGLShaderVar::kVec2f_Type,
+                                              texCoordAttrs[t].c_str());
         }
     }
 
@@ -521,7 +604,8 @@
                     inCoords = texCoordAttrs[tcIdx].c_str();
                 }
 
-                genStageCode(gl, s,
+                genStageCode(gl,
+                             s,
                              fProgramDesc.fStages[s],
                              inColor.size() ? inColor.c_str() : NULL,
                              outColor.c_str(),
@@ -544,7 +628,8 @@
                           &needColorFilterUniform, &bogus);
     }
     if (needColorFilterUniform) {
-        segments.fFSUnis.append(  "uniform vec4 " COL_FILTER_UNI_NAME ";\n");
+        segments.fFSUnis.push_back().set(GrGLShaderVar::kVec4f_Type,
+                                         COL_FILTER_UNI_NAME);
         programData->fUniLocations.fColorFilterUni = kUseUniform;
     }
 
@@ -608,8 +693,8 @@
             }
         }
         if (ProgramDesc::kNone_DualSrcOutput != fProgramDesc.fDualSrcOutput) {
-            segments.fFSOutputs.appendf("out vec4 %s;\n",
-                                        dual_source_output_name());
+            segments.fFSOutputs.push_back().set(GrGLShaderVar::kVec4f_Type,
+                                                dual_source_output_name());
             bool outputIsZero = false;
             GrStringBuilder coeff;
             if (ProgramDesc::kCoverage_DualSrcOutput !=
@@ -644,9 +729,9 @@
 
     if (!wroteFragColorZero) {
         modulate_helper(fsColorOutput,
-                         inColor.c_str(),
-                         inCoverage.c_str(),
-                         &segments.fFSCode);
+                        inColor.c_str(),
+                        inCoverage.c_str(),
+                        &segments.fFSCode);
     }
 
     segments.fVSCode.append("}\n");
@@ -655,12 +740,12 @@
     ///////////////////////////////////////////////////////////////////////////
     // compile and setup attribs and unis
 
-    if (!CompileFSAndVS(gl, segments, programData)) {
+    if (!CompileFSAndVS(gl, glslVersion, segments, programData)) {
         return false;
     }
 
     if (!this->bindOutputsAttribsAndLinkProgram(gl, texCoordAttrs,
-                                                usingDeclaredOutputs,
+                                                isColorDeclared,
                                                 dualSourceOutputWritten,
                                                 programData)) {
         return false;
@@ -671,7 +756,36 @@
     return true;
 }
 
+namespace {
+
+void expand_decls(const VarArray& unis,
+                  const GrGLInterface* gl,
+                  const char* prefix,
+                  GrStringBuilder* string) {
+    const int count = unis.count();
+    for (int i = 0; i < count; ++i) {
+        string->append(prefix);
+        string->append(" ");
+        unis[i].appendDecl(gl, string);
+        string->append(";\n");
+    }
+}
+
+void print_shader(int stringCnt,
+                  const char** strings,
+                  int* stringLengths) {
+    for (int i = 0; i < stringCnt; ++i) {
+        if (NULL == stringLengths || stringLengths[i] < 0) {
+            GrPrintf(strings[i]);
+        } else {
+            GrPrintf("%.*s", stringLengths[i], strings[i]);
+        }
+    }
+}
+}
+
 bool GrGLProgram::CompileFSAndVS(const GrGLInterface* gl,
+                                 GLSLVersion glslVersion,
                                  const ShaderCodeSegments& segments,
                                  CachedData* programData) {
 
@@ -679,25 +793,41 @@
     const char* strings[MAX_STRINGS];
     int lengths[MAX_STRINGS];
     int stringCnt = 0;
+    GrStringBuilder attrs;
+    GrStringBuilder varyings;
+    GrStringBuilder unis;
+    GrStringBuilder fsOutputs;
+
+    static const char* gVaryingPrefixes[2][2] = {{"varying", "varying"},
+                                                 {"out", "in"}};
+    const char** varyingPrefixes = glslVersion == k120_GLSLVersion ?
+                                                    gVaryingPrefixes[0] :
+                                                    gVaryingPrefixes[1];
+    const char* attributePrefix = glslVersion == k120_GLSLVersion ?
+                                                    "attribute" :
+                                                    "in";
 
     if (segments.fHeader.size()) {
         strings[stringCnt] = segments.fHeader.c_str();
         lengths[stringCnt] = segments.fHeader.size();
         ++stringCnt;
     }
-    if (segments.fVSUnis.size()) {
-        strings[stringCnt] = segments.fVSUnis.c_str();
-        lengths[stringCnt] = segments.fVSUnis.size();
+    expand_decls(segments.fVSUnis, gl, "uniform", &unis);
+    if (unis.size()) {
+        strings[stringCnt] = unis.c_str();
+        lengths[stringCnt] = unis.size();
         ++stringCnt;
     }
-    if (segments.fVSAttrs.size()) {
-        strings[stringCnt] = segments.fVSAttrs.c_str();
-        lengths[stringCnt] = segments.fVSAttrs.size();
+    expand_decls(segments.fVSAttrs, gl, attributePrefix, &attrs);
+    if (attrs.size()) {
+        strings[stringCnt] = attrs.c_str();
+        lengths[stringCnt] = attrs.size();
         ++stringCnt;
     }
-    if (segments.fVaryings.size()) {
-        strings[stringCnt] = segments.fVaryings.c_str();
-        lengths[stringCnt] = segments.fVaryings.size();
+    expand_decls(segments.fVaryings, gl, varyingPrefixes[0], &varyings);
+    if (varyings.size()) {
+        strings[stringCnt] = varyings.c_str();
+        lengths[stringCnt] = varyings.size();
         ++stringCnt;
     }
 
@@ -707,13 +837,10 @@
     ++stringCnt;
 
 #if PRINT_SHADERS
-    GrPrintf(segments.fHeader.c_str());
-    GrPrintf(segments.fVSUnis.c_str());
-    GrPrintf(segments.fVSAttrs.c_str());
-    GrPrintf(segments.fVaryings.c_str());
-    GrPrintf(segments.fVSCode.c_str());
+    print_shader(stringCnt, strings, lengths);
     GrPrintf("\n");
 #endif
+
     GrAssert(stringCnt <= MAX_STRINGS);
     programData->fVShaderID = CompileShader(gl, GR_GL_VERTEX_SHADER,
                                             stringCnt, strings, lengths);
@@ -734,19 +861,26 @@
         lengths[stringCnt] = strlen(GrShaderPrecision(gl));
         ++stringCnt;
     }
-    if (segments.fFSUnis.size()) {
-        strings[stringCnt] = segments.fFSUnis.c_str();
-        lengths[stringCnt] = segments.fFSUnis.size();
+    unis.reset();
+    expand_decls(segments.fFSUnis, gl, "uniform", &unis);
+    if (unis.size()) {
+        strings[stringCnt] = unis.c_str();
+        lengths[stringCnt] = unis.size();
         ++stringCnt;
     }
-    if (segments.fVaryings.size()) {
-        strings[stringCnt] = segments.fVaryings.c_str();
-        lengths[stringCnt] = segments.fVaryings.size();
+    varyings.reset();
+    expand_decls(segments.fVaryings, gl, varyingPrefixes[1], &varyings);
+    if (varyings.size()) {
+        strings[stringCnt] = varyings.c_str();
+        lengths[stringCnt] = varyings.size();
         ++stringCnt;
     }
-    if (segments.fFSOutputs.size()) {
-        strings[stringCnt] = segments.fFSOutputs.c_str();
-        lengths[stringCnt] = segments.fFSOutputs.size();
+    expand_decls(segments.fFSOutputs, gl, "out", &fsOutputs);
+    if (fsOutputs.size()) {
+        // We shouldn't have declared outputs on 1.2
+        GrAssert(k120_GLSLVersion != glslVersion);
+        strings[stringCnt] = fsOutputs.c_str();
+        lengths[stringCnt] = fsOutputs.size();
         ++stringCnt;
     }
     if (segments.fFSFunctions.size()) {
@@ -761,15 +895,10 @@
     ++stringCnt;
 
 #if PRINT_SHADERS
-    GrPrintf(segments.fHeader.c_str());
-    GrPrintf(GrShaderPrecision(gl));
-    GrPrintf(segments.fFSUnis.c_str());
-    GrPrintf(segments.fVaryings.c_str());
-    GrPrintf(segments.fFSOutputs.c_str());
-    GrPrintf(segments.fFSFunctions.c_str());
-    GrPrintf(segments.fFSCode.c_str());
+    print_shader(stringCnt, strings, lengths);
     GrPrintf("\n");
 #endif
+
     GrAssert(stringCnt <= MAX_STRINGS);
     programData->fFShaderID = CompileShader(gl, GR_GL_FRAGMENT_SHADER,
                                             stringCnt, strings, lengths);
@@ -810,13 +939,7 @@
             GrGLsizei length = GR_GL_INIT_ZERO;
             GR_GL_CALL(gl, GetShaderInfoLog(shader, infoLen+1, 
                                             &length, (char*)log.get()));
-            for (int i = 0; i < stringCnt; ++i) {
-                if (NULL == stringLengths || stringLengths[i] < 0) {
-                    GrPrintf(strings[i]);
-                } else {
-                    GrPrintf("%.*s", stringLengths[i], strings[i]);
-                }
-            }
+            print_shader(stringCnt, strings, stringLengths);
             GrPrintf("\n%s", log.get());
         }
         GrAssert(!"Shader compilation failed!");
@@ -1027,9 +1150,6 @@
 
     GrAssert(stageNum >= 0 && stageNum <= 9);
 
-    GrStringBuilder varyingName;
-    stage_varying_name(stageNum, &varyingName);
-
     // First decide how many coords are needed to access the texture
     // Right now it's always 2 but we could start using 1D textures for
     // gradients.
@@ -1039,18 +1159,22 @@
 
     // decide whether we need a matrix to transform texture coords
     // and whether the varying needs a perspective coord.
-    GrStringBuilder texMName;
-    tex_matrix_name(stageNum, &texMName);
+    const char* matName = NULL;
     if (desc.fOptFlags & StageDesc::kIdentityMatrix_OptFlagBit) {
         varyingDims = coordDims;
     } else {
+        GrGLShaderVar* mat;
     #if GR_GL_ATTRIBUTE_MATRICES
-        segments->fVSAttrs.appendf("attribute mat3 %s;\n", texMName.c_str());
+        mat = &segments->fVSAttrs.push_back();
         locations->fTextureMatrixUni = kSetAsAttribute;
     #else
-        segments->fVSUnis.appendf("uniform mat3 %s;\n", texMName.c_str());
+        mat = &segments->fVSUnis.push_back();
         locations->fTextureMatrixUni = kUseUniform;
     #endif
+        tex_matrix_name(stageNum, mat->accessName());
+        mat->setType(GrGLShaderVar::kMat33f_Type);
+        matName = mat->getName().c_str();
+
         if (desc.fOptFlags & StageDesc::kNoPerspective_OptFlagBit) {
             varyingDims = coordDims;
         } else {
@@ -1058,75 +1182,93 @@
         }
     }
 
-    GrStringBuilder samplerName;
-    sampler_name(stageNum, &samplerName);
-    segments->fFSUnis.appendf("uniform sampler2D %s;\n", samplerName.c_str());
+    segments->fFSUnis.push_back().setType(GrGLShaderVar::kSampler2D_Type);
+    sampler_name(stageNum, segments->fFSUnis.back().accessName());
     locations->fSamplerUni = kUseUniform;
+    const char* samplerName = segments->fFSUnis.back().getName().c_str();
 
-    GrStringBuilder texelSizeName;
+    const char* texelSizeName = NULL;
     if (StageDesc::k2x2_FetchMode == desc.fFetchMode) {
-        normalized_texel_size_name(stageNum, &texelSizeName);
-        segments->fFSUnis.appendf("uniform vec2 %s;\n", texelSizeName.c_str());
+        segments->fFSUnis.push_back().setType(GrGLShaderVar::kVec2f_Type);
+        normalized_texel_size_name(stageNum, segments->fFSUnis.back().accessName());
+        texelSizeName = segments->fFSUnis.back().getName().c_str();
     }
 
-    segments->fVaryings.appendf("varying %s %s;\n",
-                                float_vector_type(varyingDims), varyingName.c_str());
+    const char* varyingName;
+    GrGLShaderVar* varying = &segments->fVaryings.push_back();
+    stage_varying_name(stageNum, varying->accessName());
+    varying->setType(float_vector_type(varyingDims));
+    varyingName = varying->getName().c_str();
 
-    if (desc.fOptFlags & StageDesc::kIdentityMatrix_OptFlagBit) {
+    if (!matName) {
         GrAssert(varyingDims == coordDims);
-        segments->fVSCode.appendf("\t%s = %s;\n", varyingName.c_str(), vsInCoord);
+        segments->fVSCode.appendf("\t%s = %s;\n", varyingName, vsInCoord);
     } else {
         // varying = texMatrix * texCoord
         segments->fVSCode.appendf("\t%s = (%s * vec3(%s, 1))%s;\n",
-                                  varyingName.c_str(), texMName.c_str(),
-                                  vsInCoord, vector_all_coords(varyingDims));
+                                  varyingName, matName, vsInCoord,
+                                  vector_all_coords(varyingDims));
     }
 
-    GrStringBuilder radial2ParamsName;
-    radial2_param_name(stageNum, &radial2ParamsName);
-    // for radial grads without perspective we can pass the linear
-    // part of the quadratic as a varying.
-    GrStringBuilder radial2VaryingName;
-    radial2_varying_name(stageNum, &radial2VaryingName);
+    const char* radial2ParamsName = NULL;
+    const char* radial2VaryingName = NULL;
 
-    if (StageDesc::kRadial2Gradient_CoordMapping == desc.fCoordMapping || 
+    if (StageDesc::kRadial2Gradient_CoordMapping == desc.fCoordMapping ||
         StageDesc::kRadial2GradientDegenerate_CoordMapping == desc.fCoordMapping) {
 
-        segments->fVSUnis.appendf("uniform %s float %s[6];\n",
-                                  GrPrecision(gl), radial2ParamsName.c_str());
-        segments->fFSUnis.appendf("uniform float %s[6];\n",
-                                  radial2ParamsName.c_str());
+        GrGLShaderVar* radial2FSParams = &segments->fFSUnis.push_back();
+        radial2FSParams->setType(GrGLShaderVar::kFloat_Type);
+        radial2FSParams->setArrayCount(6);
+        radial2_param_name(stageNum, radial2FSParams->accessName());
+        segments->fVSUnis.push_back(*radial2FSParams).setEmitPrecision(true);
+        radial2ParamsName = radial2FSParams->getName().c_str();
+
         locations->fRadial2Uni = kUseUniform;
 
-        // if there is perspective we don't interpolate this
+        // for radial grads without perspective we can pass the linear
+        // part of the quadratic as a varying.
         if (varyingDims == coordDims) {
             GrAssert(2 == coordDims);
-            segments->fVaryings.appendf("varying float %s;\n", radial2VaryingName.c_str());
+
+            GrGLShaderVar* radial2Varying = &segments->fVaryings.push_back();
+            radial2Varying->setType(GrGLShaderVar::kFloat_Type);
+            radial2_varying_name(stageNum, radial2Varying->accessName());
+            radial2VaryingName = radial2Varying->getName().c_str();
 
             // r2Var = 2 * (r2Parm[2] * varCoord.x - r2Param[3])
+            const char* r2ParamName = radial2FSParams->getName().c_str();
+            const char* r2VarName = radial2Varying->getName().c_str();
             segments->fVSCode.appendf("\t%s = 2.0 *(%s[2] * %s.x - %s[3]);\n",
-                                      radial2VaryingName.c_str(), radial2ParamsName.c_str(),
-                                      varyingName.c_str(), radial2ParamsName.c_str());
+                                      r2VarName, r2ParamName, varyingName,
+                                      r2ParamName);
         }
     }
 
-    GrStringBuilder kernelName, kernelWidthName, imageIncrementName;
-    convolve_param_names(stageNum, &kernelName, &imageIncrementName);
-
+    const char* kernelName = NULL;
+    const char* imageIncrementName = NULL;
     if (ProgramDesc::StageDesc::kConvolution_FetchMode == desc.fFetchMode) {
-        segments->fFSUnis.appendf("uniform float %s[%d];\n",
-                                  kernelName.c_str(), desc.fKernelWidth);
-        segments->fFSUnis.appendf("uniform vec2 %s;\n",
-                                  imageIncrementName.c_str());
-        segments->fVSUnis.appendf("uniform %s vec2 %s;\n",
-                                  GrPrecision(gl),
-                                  imageIncrementName.c_str());
+
+        GrGLShaderVar* kernel = &segments->fFSUnis.push_back();
+        kernel->setType(GrGLShaderVar::kFloat_Type);
+        kernel->setArrayCount(desc.fKernelWidth);
+        GrGLShaderVar* imgInc = &segments->fFSUnis.push_back();
+        imgInc->setType(GrGLShaderVar::kVec2f_Type);
+
+        convolve_param_names(stageNum,
+                             kernel->accessName(),
+                             imgInc->accessName());
+        kernelName = kernel->getName().c_str();
+        imageIncrementName = imgInc->getName().c_str();
+
+        // need image increment in both VS and FS
+        segments->fVSUnis.push_back(*imgInc).setEmitPrecision(true);
+
         locations->fKernelUni = kUseUniform;
         locations->fImageIncrementUni = kUseUniform;
         float scale = (desc.fKernelWidth - 1) * 0.5f;
         segments->fVSCode.appendf("\t%s -= vec2(%g, %g) * %s;\n",
-                                  varyingName.c_str(), scale, scale,
-                                  imageIncrementName.c_str());
+                                  varyingName, scale, scale,
+                                  imageIncrementName);
     }
 
     /// Fragment Shader Stuff
@@ -1149,12 +1291,12 @@
             fsCoordName = "inCoord";
             fsCoordName.appendS32(stageNum);
             segments->fFSCode.appendf("\t%s %s = %s%s / %s%s;\n",
-                                       float_vector_type(coordDims),
-                                       fsCoordName.c_str(),
-                                       varyingName.c_str(),
-                                       vector_nonhomog_coords(varyingDims),
-                                       varyingName.c_str(),
-                                       vector_homog_coord(varyingDims));
+                                GrGLShaderVar::TypeString(float_vector_type(coordDims)),
+                                fsCoordName.c_str(),
+                                varyingName,
+                                vector_nonhomog_coords(varyingDims),
+                                varyingName,
+                                vector_homog_coord(varyingDims));
         }
     }
 
@@ -1192,18 +1334,18 @@
             bVar = "b";
             bVar.appendS32(stageNum);
             segments->fFSCode.appendf("\tfloat %s = 2.0 * (%s[2] * %s.x - %s[3]);\n",
-                                        bVar.c_str(), radial2ParamsName.c_str(),
-                                        fsCoordName.c_str(), radial2ParamsName.c_str());
+                                        bVar.c_str(), radial2ParamsName,
+                                        fsCoordName.c_str(), radial2ParamsName);
         }
 
         // c = (x^2)+(y^2) - params[4]
         segments->fFSCode.appendf("\tfloat %s = dot(%s, %s) - %s[4];\n",
                                   cName.c_str(), fsCoordName.c_str(),
                                   fsCoordName.c_str(),
-                                  radial2ParamsName.c_str());
+                                  radial2ParamsName);
         // ac4 = 4.0 * params[0] * c
         segments->fFSCode.appendf("\tfloat %s = %s[0] * 4.0 * %s;\n",
-                                  ac4Name.c_str(), radial2ParamsName.c_str(),
+                                  ac4Name.c_str(), radial2ParamsName,
                                   cName.c_str());
 
         // root = sqrt(b^2-4ac)
@@ -1215,8 +1357,8 @@
         // x coord is: (-b + params[5] * sqrt(b^2-4ac)) * params[1]
         // y coord is 0.5 (texture is effectively 1D)
         sampleCoords.printf("vec2((-%s + %s[5] * %s) * %s[1], 0.5)",
-                            bVar.c_str(), radial2ParamsName.c_str(),
-                            rootName.c_str(), radial2ParamsName.c_str());
+                            bVar.c_str(), radial2ParamsName,
+                            rootName.c_str(), radial2ParamsName);
         complexCoord = true;
         break;}
     case StageDesc::kRadial2GradientDegenerate_CoordMapping: {
@@ -1235,15 +1377,15 @@
             bVar = "b";
             bVar.appendS32(stageNum);
             segments->fFSCode.appendf("\tfloat %s = 2.0 * (%s[2] * %s.x - %s[3]);\n",
-                                        bVar.c_str(), radial2ParamsName.c_str(),
-                                        fsCoordName.c_str(), radial2ParamsName.c_str());
+                                        bVar.c_str(), radial2ParamsName,
+                                        fsCoordName.c_str(), radial2ParamsName);
         }
 
         // c = (x^2)+(y^2) - params[4]
         segments->fFSCode.appendf("\tfloat %s = dot(%s, %s) - %s[4];\n",
                                   cName.c_str(), fsCoordName.c_str(),
                                   fsCoordName.c_str(),
-                                  radial2ParamsName.c_str());
+                                  radial2ParamsName);
 
         // x coord is: -c/b
         // y coord is 0.5 (texture is effectively 1D)
@@ -1267,12 +1409,10 @@
         StageDesc::kCustomTextureDomain_OptFlagBit) {
         GrStringBuilder texDomainName;
         tex_domain_name(stageNum, &texDomainName);
-        segments->fFSUnis.appendf("uniform %s %s;\n",
-                                  float_vector_type(4),
-                                  texDomainName.c_str());
+        segments->fFSUnis.push_back().set(GrGLShaderVar::kVec4f_Type, texDomainName);
         GrStringBuilder coordVar("clampCoord");
         segments->fFSCode.appendf("\t%s %s = clamp(%s, %s.xy, %s.zw);\n",
-                                  float_vector_type(coordDims),
+                                  float_vector_type_str(coordDims),
                                   coordVar.c_str(),
                                   sampleCoords.c_str(),
                                   texDomainName.c_str(),
@@ -1288,17 +1428,17 @@
             GrStringBuilder coordVar("tCoord");
             coordVar.appendS32(stageNum);
             segments->fFSCode.appendf("\t%s %s = %s;\n",
-                                      float_vector_type(coordDims),
-                                      coordVar.c_str(), sampleCoords.c_str());
+                                float_vector_type_str(coordDims),
+                                coordVar.c_str(), sampleCoords.c_str());
             sampleCoords = coordVar;
         }
         GrAssert(2 == coordDims);
         GrStringBuilder accumVar("accum");
         accumVar.appendS32(stageNum);
-        segments->fFSCode.appendf("\tvec4 %s  = %s(%s, %s + vec2(-%s.x,-%s.y))%s;\n", accumVar.c_str(), texFunc.c_str(), samplerName.c_str(), sampleCoords.c_str(), texelSizeName.c_str(), texelSizeName.c_str(), smear);
-        segments->fFSCode.appendf("\t%s += %s(%s, %s + vec2(+%s.x,-%s.y))%s;\n", accumVar.c_str(), texFunc.c_str(), samplerName.c_str(), sampleCoords.c_str(), texelSizeName.c_str(), texelSizeName.c_str(), smear);
-        segments->fFSCode.appendf("\t%s += %s(%s, %s + vec2(-%s.x,+%s.y))%s;\n", accumVar.c_str(), texFunc.c_str(), samplerName.c_str(), sampleCoords.c_str(), texelSizeName.c_str(), texelSizeName.c_str(), smear);
-        segments->fFSCode.appendf("\t%s += %s(%s, %s + vec2(+%s.x,+%s.y))%s;\n", accumVar.c_str(), texFunc.c_str(), samplerName.c_str(), sampleCoords.c_str(), texelSizeName.c_str(), texelSizeName.c_str(), smear);
+        segments->fFSCode.appendf("\tvec4 %s  = %s(%s, %s + vec2(-%s.x,-%s.y))%s;\n", accumVar.c_str(), texFunc.c_str(), samplerName, sampleCoords.c_str(), texelSizeName, texelSizeName, smear);
+        segments->fFSCode.appendf("\t%s += %s(%s, %s + vec2(+%s.x,-%s.y))%s;\n", accumVar.c_str(), texFunc.c_str(), samplerName, sampleCoords.c_str(), texelSizeName, texelSizeName, smear);
+        segments->fFSCode.appendf("\t%s += %s(%s, %s + vec2(-%s.x,+%s.y))%s;\n", accumVar.c_str(), texFunc.c_str(), samplerName, sampleCoords.c_str(), texelSizeName, texelSizeName, smear);
+        segments->fFSCode.appendf("\t%s += %s(%s, %s + vec2(+%s.x,+%s.y))%s;\n", accumVar.c_str(), texFunc.c_str(), samplerName, sampleCoords.c_str(), texelSizeName, texelSizeName, smear);
         segments->fFSCode.appendf("\t%s = .25 * %s%s;\n", fsOutColor, accumVar.c_str(), modulate.c_str());
     } else if (ProgramDesc::StageDesc::kConvolution_FetchMode == desc.fFetchMode) {
         GrStringBuilder sumVar("sum");
@@ -1315,18 +1455,18 @@
                                   desc.fKernelWidth);
         segments->fFSCode.appendf("\t\t%s += %s(%s, %s)%s * %s[i];\n",
                                   sumVar.c_str(), texFunc.c_str(),
-                                  samplerName.c_str(), coordVar.c_str(), smear,
-                                  kernelName.c_str());
+                                  samplerName, coordVar.c_str(), smear,
+                                  kernelName);
         segments->fFSCode.appendf("\t\t%s += %s;\n",
                                   coordVar.c_str(),
-                                  imageIncrementName.c_str());
+                                  imageIncrementName);
         segments->fFSCode.appendf("\t}\n");
         segments->fFSCode.appendf("\t%s = %s%s;\n", fsOutColor,
                                   sumVar.c_str(), modulate.c_str());
     } else {
         segments->fFSCode.appendf("\t%s = %s(%s, %s)%s%s;\n",
                                   fsOutColor, texFunc.c_str(), 
-                                  samplerName.c_str(), sampleCoords.c_str(),
+                                  samplerName, sampleCoords.c_str(),
                                   smear, modulate.c_str());
     }
 }
diff --git a/gpu/src/GrGLProgram.h b/gpu/src/GrGLProgram.h
index 90e515f..e37d0a2 100644
--- a/gpu/src/GrGLProgram.h
+++ b/gpu/src/GrGLProgram.h
@@ -18,17 +18,7 @@
 
 class GrBinHashKeyBuilder;
 
-struct ShaderCodeSegments {
-    GrStringBuilder fHeader; // VS+FS, GLSL version, etc
-    GrStringBuilder fVSUnis;
-    GrStringBuilder fVSAttrs;
-    GrStringBuilder fVaryings;
-    GrStringBuilder fFSUnis;
-    GrStringBuilder fFSOutputs;
-    GrStringBuilder fFSFunctions;
-    GrStringBuilder fVSCode;
-    GrStringBuilder fFSCode;
-};
+struct ShaderCodeSegments;
 
 /**
  * This class manages a GPU program and records per-program information.
@@ -41,6 +31,11 @@
  */
 class GrGLProgram {
 public:
+    enum GLSLVersion {
+        k120_GLSLVersion, // Desktop GLSL 1.2 and ES2 shading lang
+        k150_GLSLVersion  // Desktop GLSL 1.5
+    };
+
     class CachedData;
 
     GrGLProgram();
@@ -52,6 +47,7 @@
      *  but in a separate cacheable container.
      */
     bool genProgram(const GrGLInterface* gl,
+                    GLSLVersion glslVersion,
                     CachedData* programData) const;
 
      /**
@@ -286,7 +282,7 @@
                       const char* vsInCoord,
                       ShaderCodeSegments* segments,
                       StageUniLocations* locations) const;
-    
+
     // generates code to compute coverage based on edge AA.
     void genEdgeCoverage(const GrGLInterface* gl,
                          GrVertexLayout layout,
@@ -295,6 +291,7 @@
                          ShaderCodeSegments* segments) const;
 
     static bool CompileFSAndVS(const GrGLInterface* gl,
+                               GLSLVersion glslVersion,
                                const ShaderCodeSegments& segments, 
                                CachedData* programData);
 
diff --git a/gpu/src/GrGLShaderVar.h b/gpu/src/GrGLShaderVar.h
new file mode 100644
index 0000000..5c50079
--- /dev/null
+++ b/gpu/src/GrGLShaderVar.h
@@ -0,0 +1,217 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrGLShaderVar_DEFINED
+#define GrGLShaderVar_DEFINED
+
+#include "GrGLInterface.h"
+
+/**
+ * Represents a variable in a shader
+ */
+class GrGLShaderVar {
+public:
+
+    enum Type {
+        kFloat_Type,
+        kVec2f_Type,
+        kVec3f_Type,
+        kVec4f_Type,
+        kMat33f_Type,
+        kSampler2D_Type,
+    };
+
+    /**
+     * Defaults to a float with no precision specifier
+     */
+    GrGLShaderVar() {
+        fType = kFloat_Type;
+        fCount = kNonArray;
+        fEmitPrecision = false;
+    }
+
+    GrGLShaderVar(const GrGLShaderVar& var)
+        : fType(var.fType)
+        , fName(var.fName)
+        , fCount(var.fCount)
+        , fEmitPrecision(var.fEmitPrecision) {}
+
+    /**
+     * Values for array count that have special meaning. We allow 1-sized arrays.
+     */
+    enum {
+        kNonArray     =  0, // not an array
+        kUnsizedArray = -1, // an unsized array (declared with [])
+    };
+
+    /**
+     * Sets as a non-array.
+     */
+    void set(Type type,
+             const GrStringBuilder& name,
+             bool emitPrecision = false) {
+        fType = type;
+        fName = name;
+        fCount = kNonArray;
+        fEmitPrecision = emitPrecision;
+    }
+
+    /**
+     * Sets as a non-array.
+     */
+    void set(Type type,
+             const char* name,
+             bool specifyPrecision = false) {
+        fType = type;
+        fName = name;
+        fCount = kNonArray;
+        fEmitPrecision = specifyPrecision;
+    }
+
+    /**
+     * Set all var options
+     */
+    void set(Type type,
+             const GrStringBuilder& name,
+             int count,
+             bool specifyPrecision = false) {
+        fType = type;
+        fName = name;
+        fCount = count;
+        fEmitPrecision = specifyPrecision;
+    }
+
+    /**
+     * Set all var options
+     */
+    void set(Type type,
+             const char* name,
+             int count,
+             bool specifyPrecision = false) {
+        fType = type;
+        fName = name;
+        fCount = count;
+        fEmitPrecision = specifyPrecision;
+    }
+
+    /**
+     * Is the var an array.
+     */
+    bool isArray() const { return kNonArray != fCount; }
+    /**
+     * Is this an unsized array, (i.e. declared with []).
+     */
+    bool isUnsizedArray() const { return kUnsizedArray == fCount; }
+    /**
+     * Get the array length of the var.
+     */
+    int getArrayCount() const { return fCount; }
+    /**
+     * Set the array length of the var
+     */
+    void setArrayCount(int count) { fCount = count; }
+    /**
+     * Set to be a non-array.
+     */
+    void setNonArray() { fCount = kNonArray; }
+    /**
+     * Set to be an unsized array.
+     */
+    void setUnsizedArray() { fCount = kUnsizedArray; }
+
+    /**
+     * Access the var name as a writable string
+     */
+    GrStringBuilder* accessName() { return &fName; }
+    /**
+     * Set the var name
+     */
+    void setName(const GrStringBuilder& n) { fName = n; }
+    void setName(const char* n) { fName = n; }
+    /**
+     * Get the var name.
+     */
+    const GrStringBuilder& getName() const { return fName; }
+
+    /**
+     * Get the type of the var
+     */
+    Type getType() const { return fType; }
+    /**
+     * Set the type of the var
+     */
+    void setType(Type type) { fType = type; }
+
+    /**
+     * Must the variable declaration emit a precision specifier
+     */
+    bool emitsPrecision() const { return fEmitPrecision; }
+    /**
+     * Specify whether the declaration should specify precision
+     */
+    void setEmitPrecision(bool p) { fEmitPrecision = p; }
+
+    /**
+     * Write a declaration of this variable to out.
+     */
+    void appendDecl(const GrGLInterface* gl, GrStringBuilder* out) const {
+        if (this->emitsPrecision()) {
+            out->append(PrecisionString(gl));
+            out->append(" ");
+        }
+        if (this->isArray()) {
+            if (this->isUnsizedArray()) {
+                out->appendf("%s %s[]", 
+                             TypeString(this->getType()), 
+                             this->getName().c_str());
+            } else {
+                GrAssert(this->getArrayCount() > 0);
+                out->appendf("%s %s[%d]", 
+                             TypeString(this->getType()),
+                             this->getName().c_str(),
+                             this->getArrayCount());
+            }
+        } else {
+            out->appendf("%s %s",
+                         TypeString(this->getType()),
+                         this->getName().c_str());
+        }
+    }
+
+    static const char* TypeString(Type t) {
+        switch (t) {
+            case kFloat_Type:
+                return "float";
+            case kVec2f_Type:
+                return "vec2";
+            case kVec3f_Type:
+                return "vec3";
+            case kVec4f_Type:
+                return "vec4";
+            case kMat33f_Type:
+                return "mat3";
+            case kSampler2D_Type:
+                return "sampler2D";
+            default:
+                GrCrash("Unknown shader var type.");
+                return ""; // suppress warning
+        }
+    }
+
+private:
+    static const char* PrecisionString(const GrGLInterface* gl) {
+        return gl->supportsDesktop() ? "" : "mediump";
+    }
+
+    Type            fType;
+    GrStringBuilder fName;
+    int             fCount;
+    bool            fEmitPrecision;
+};
+
+#endif
diff --git a/gpu/src/GrGpuGL.h b/gpu/src/GrGpuGL.h
index b1accf6..67df419 100644
--- a/gpu/src/GrGpuGL.h
+++ b/gpu/src/GrGpuGL.h
@@ -26,6 +26,7 @@
 
     const GrGLInterface* glInterface() const { return fGL; }
     GrGLBinding glBinding() const { return fGLBinding; }
+    float glVersion() const { return fGLVersion; }
 
 protected:
     GrGpuGL(const GrGLInterface* glInterface, GrGLBinding glBinding);
@@ -46,10 +47,6 @@
     DrState   fHWDrawState;
     bool      fHWStencilClip;
 
-    // read these once at begining and then never again
-    SkString fExtensionString;
-    float fGLVersion;
-
     // As flush of GL state proceeds it updates fHDrawState
     // to reflect the new state. Later parts of the state flush
     // may perform cascaded changes but cannot refer to fHWDrawState.
@@ -193,13 +190,16 @@
     friend class GrGLTexture;
     friend class GrGLRenderTarget;
 
+    // read these once at begining and then never again
+    SkString fExtensionString;
+    float fGLVersion;
 
     SkTArray<GrGLStencilBuffer::Format, true> fStencilFormats;
     // we want to clear stencil buffers when they are created. We want to clear
     // the entire buffer even if it is larger than the color attachment. We
     // attach it to this fbo with no color attachment to do the initial clear.
     GrGLuint fStencilClearFBO;
-    
+
     bool fHWBlendDisabled;
 
     GrGLuint fAASamples[4];
diff --git a/gpu/src/GrGpuGLShaders.cpp b/gpu/src/GrGpuGLShaders.cpp
index a9a3953..f894f1c 100644
--- a/gpu/src/GrGpuGLShaders.cpp
+++ b/gpu/src/GrGpuGLShaders.cpp
@@ -51,15 +51,19 @@
     enum {
         kMaxEntries = 32
     };
-    Entry                 fEntries[kMaxEntries];
-    int                   fCount;
-    unsigned int          fCurrLRUStamp;
-    const GrGLInterface*  fGL;
+    Entry                       fEntries[kMaxEntries];
+    int                         fCount;
+    unsigned int                fCurrLRUStamp;
+    const GrGLInterface*        fGL;
+    GrGLProgram::GLSLVersion    fGLSLVersion;
+
 public:
-    ProgramCache(const GrGLInterface* gl) 
+    ProgramCache(const GrGLInterface* gl,
+                 GrGLProgram::GLSLVersion glslVersion) 
         : fCount(0)
         , fCurrLRUStamp(0)
-        , fGL(gl) {
+        , fGL(gl)
+        , fGLSLVersion(glslVersion) {
     }
 
     ~ProgramCache() {
@@ -85,7 +89,7 @@
         
         Entry* entry = fHashCache.find(newEntry.fKey);
         if (NULL == entry) {
-            if (!desc.genProgram(fGL, &newEntry.fProgramData)) {
+            if (!desc.genProgram(fGL, fGLSLVersion, &newEntry.fProgramData)) {
                 return NULL;
             }
             if (fCount < kMaxEntries) {
@@ -136,14 +140,32 @@
 #define GL_CALL(X) GR_GL_CALL(this->glInterface(), X)
 
 namespace {
-    template <typename T>
-    T random_val(GrRandom* r, T count) {
-        return (T)(int)(r->nextF() * count);
+
+GrGLProgram::GLSLVersion get_glsl_version(GrGLBinding binding, float glVersion) {
+    switch (binding) {
+        case kDesktop_GrGLBinding:
+            return (glVersion >= 3.2) ? GrGLProgram::k150_GLSLVersion :
+                                        GrGLProgram::k120_GLSLVersion;
+        case kES2_GrGLBinding:
+            return GrGLProgram::k120_GLSLVersion;
+        default:
+            GrCrash("Attempting to get GLSL version in unknown or fixed-"
+                     "function GL binding.");
+            return GrGLProgram::k120_GLSLVersion; // suppress warning
     }
-};
+}
+
+template <typename T>
+T random_val(GrRandom* r, T count) {
+    return (T)(int)(r->nextF() * count);
+}
+
+}
 
 bool GrGpuGLShaders::programUnitTest() {
 
+    GrGLProgram::GLSLVersion glslVersion = 
+            get_glsl_version(this->glBinding(), this->glVersion());
     static const int STAGE_OPTS[] = {
         0,
         StageDesc::kNoPerspective_OptFlagBit,
@@ -242,15 +264,12 @@
             stage.fKernelWidth = 4 * random.nextF() + 2;
         }
         CachedData cachedData;
-        if (!program.genProgram(this->glInterface(), &cachedData)) {
+        if (!program.genProgram(this->glInterface(),
+                                glslVersion,
+                                &cachedData)) {
             return false;
         }
         DeleteProgram(this->glInterface(), &cachedData);
-        bool again = false;
-        if (again) {
-            program.genProgram(this->glInterface(), &cachedData);
-            DeleteProgram(this->glInterface(), &cachedData);
-        }
     }
     return true;
 }
@@ -272,7 +291,8 @@
     fShaderSupport = true;
     if (kDesktop_GrGLBinding == this->glBinding()) {
         fDualSourceBlendingSupport =
-                            fGLVersion >= 3.3f ||
+                            this->glVersion() >= 3.25f || // TODO: when resolving Issue 387 change 
+                                                          // this back to 3.3
                             this->hasExtension("GL_ARB_blend_func_extended");
         fShaderDerivativeSupport = true;
     } else {
@@ -282,7 +302,9 @@
     }
 
     fProgramData = NULL;
-    fProgramCache = new ProgramCache(gl);
+    GrGLProgram::GLSLVersion glslVersion =
+        get_glsl_version(this->glBinding(), this->glVersion());
+    fProgramCache = new ProgramCache(gl, glslVersion);
 
 #if 0
     this->programUnitTest();
diff --git a/gyp/gpu.gyp b/gyp/gpu.gyp
index dc113d1..2b94240 100644
--- a/gyp/gpu.gyp
+++ b/gyp/gpu.gyp
@@ -149,6 +149,7 @@
         '../gpu/src/GrGLProgram.h',
         '../gpu/src/GrGLRenderTarget.cpp',
         '../gpu/src/GrGLRenderTarget.h',
+        '../gpu/src/GrGLShaderVar.h',
         '../gpu/src/GrGLStencilBuffer.cpp',
         '../gpu/src/GrGLStencilBuffer.h',
         '../gpu/src/GrGLTexture.cpp',
diff --git a/include/core/SkTArray.h b/include/core/SkTArray.h
index 975aeed..3e2a006 100644
--- a/include/core/SkTArray.h
+++ b/include/core/SkTArray.h
@@ -246,6 +246,16 @@
     }
 
     /**
+     * Version of above that uses a copy constructor to initialize the new item
+     */
+    T& push_back(const T& t) {
+        checkRealloc(1);
+        new ((char*)fMemArray+sizeof(T)*fCount) T(t);
+        ++fCount;
+        return fItemArray[fCount-1];
+    }
+
+    /**
      * Allocates n more default T values, and returns the address of the start
      * of that new range. Note: this address is only valid until the next API
      * call made on the array that might add or remove elements.
@@ -261,6 +271,34 @@
     }
 
     /**
+     * Version of above that uses a copy constructor to initialize all n items
+     * to the same T.
+     */
+    T* push_back_n(int n, const T& t) {
+        SkASSERT(n >= 0);
+        checkRealloc(n);
+        for (int i = 0; i < n; ++i) {
+            new (fItemArray + fCount + i) T(t);
+        }
+        fCount += n;
+        return fItemArray + fCount - n;
+    }
+
+    /**
+     * Version of above that uses a copy constructor to initialize the n items
+     * to separate T values.
+     */
+    T* push_back_n(int n, const T t[]) {
+        SkASSERT(n >= 0);
+        checkRealloc(n);
+        for (int i = 0; i < n; ++i) {
+            new (fItemArray + fCount + i) T(t[i]);
+        }
+        fCount += n;
+        return fItemArray + fCount - n;
+    }
+
+    /**
      * Removes the last element. Not safe to call when count() == 0.
      */
     void pop_back() {