Infrastructure for new Ganesh shader pipeline: base classes for GPU
implementation of user-defined effects.

http://codereview.appspot.com/6052047/



git-svn-id: http://skia.googlecode.com/svn/trunk@3726 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/gyp/gpu.gyp b/gyp/gpu.gyp
index fc2277e..0ffaa28 100644
--- a/gyp/gpu.gyp
+++ b/gyp/gpu.gyp
@@ -219,6 +219,8 @@
         '../src/gpu/GrBufferAllocPool.h',
         '../src/gpu/GrClip.cpp',
         '../src/gpu/GrContext.cpp',
+        '../src/gpu/GrCustomStage.cpp',
+        '../src/gpu/GrCustomStage.h',
         '../src/gpu/GrDefaultPathRenderer.cpp',
         '../src/gpu/GrDefaultPathRenderer.h',
         '../src/gpu/GrDefaultTextContext.cpp',
@@ -285,6 +287,8 @@
         '../src/gpu/gl/GrGLIRect.h',
         '../src/gpu/gl/GrGLProgram.cpp',
         '../src/gpu/gl/GrGLProgram.h',
+        '../src/gpu/gl/GrGLProgramStage.cpp',
+        '../src/gpu/gl/GrGLProgramStage.h',
         '../src/gpu/gl/GrGLRenderTarget.cpp',
         '../src/gpu/gl/GrGLRenderTarget.h',
         '../src/gpu/gl/GrGLShaderVar.h',
diff --git a/src/gpu/GrAllocator.h b/src/gpu/GrAllocator.h
index 555e56f..f333d42 100755
--- a/src/gpu/GrAllocator.h
+++ b/src/gpu/GrAllocator.h
@@ -11,6 +11,7 @@
 #ifndef GrAllocator_DEFINED
 #define GrAllocator_DEFINED
 
+#include "GrNoncopyable.h"
 #include "GrConfig.h"
 #include "SkTArray.h"
 
diff --git a/src/gpu/GrCustomStage.cpp b/src/gpu/GrCustomStage.cpp
new file mode 100644
index 0000000..f63c79a
--- /dev/null
+++ b/src/gpu/GrCustomStage.cpp
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrContext.h"
+#include "GrCustomStage.h"
+
+GrCustomStage::GrCustomStage() {
+
+}
+
+GrCustomStage::~GrCustomStage() {
+
+}
+
+bool GrCustomStage::isOpaque(bool inputTextureIsOpaque) const {
+    return false;
+}
+
diff --git a/src/gpu/GrCustomStage.h b/src/gpu/GrCustomStage.h
new file mode 100644
index 0000000..a5b8133
--- /dev/null
+++ b/src/gpu/GrCustomStage.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrCustomStage_DEFINED
+#define GrCustomStage_DEFINED
+
+class GrContext;
+class GrGLProgramStageFactory;
+
+/** Provides custom vertex shader, fragment shader, uniform data for a
+    particular stage of the Ganesh shading pipeline.
+    TODO: may want to refcount these? */
+class GrCustomStage {
+
+public:
+
+    GrCustomStage();
+    virtual ~GrCustomStage();
+
+    /** If given an input texture that is/is not opaque, is this
+        stage guaranteed to produce an opaque output? */
+    virtual bool isOpaque(bool inputTextureIsOpaque) const;
+
+    virtual GrGLProgramStageFactory* getGLFactory() = 0;
+
+protected:
+
+};
+
+#endif
diff --git a/src/gpu/gl/GrGLProgram.cpp b/src/gpu/gl/GrGLProgram.cpp
index f277647..16a60ce 100644
--- a/src/gpu/gl/GrGLProgram.cpp
+++ b/src/gpu/gl/GrGLProgram.cpp
@@ -87,29 +87,8 @@
     s->appendS32(coordIdx);
 }
 
-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);
-}
-
 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];
-}
-
-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];
+    return GrGLShaderVar::TypeString(GrSLFloatVectorType(count));
 }
 
 inline const char* vector_all_coords(int count) {
@@ -370,7 +349,7 @@
 
 // Adds a var that is computed in the VS and read in FS.
 // If there is a GS it will just pass it through.
-void append_varying(GrGLShaderVar::Type type,
+void append_varying(GrSLType type,
                     const char* name,
                     ShaderCodeSegments* segments,
                     const char** vsOutName = NULL,
@@ -416,7 +395,7 @@
 
 // version of above that adds a stage number to the
 // the var name (for uniqueness)
-void append_varying(GrGLShaderVar::Type type,
+void append_varying(GrSLType type,
                     const char* name,
                     int stageNum,
                     ShaderCodeSegments* segments,
@@ -434,7 +413,7 @@
                                   GrStringBuilder* coverageVar,
                                   ShaderCodeSegments* segments) const {
     if (fProgramDesc.fEdgeAANumEdges > 0) {
-        segments->fFSUnis.push_back().set(GrGLShaderVar::kVec3f_Type,
+        segments->fFSUnis.push_back().set(kVec3f_GrSLType,
                                           GrGLShaderVar::kUniform_TypeModifier,
                                           EDGES_UNI_NAME,
                                           fProgramDesc.fEdgeAANumEdges);
@@ -485,9 +464,9 @@
         *coverageVar = "edgeAlpha";
     } else  if (layout & GrDrawTarget::kEdge_VertexLayoutBit) {
         const char *vsName, *fsName;
-        append_varying(GrGLShaderVar::kVec4f_Type, "Edge", segments,
+        append_varying(kVec4f_GrSLType, "Edge", segments,
             &vsName, &fsName);
-        segments->fVSAttrs.push_back().set(GrGLShaderVar::kVec4f_Type,
+        segments->fVSAttrs.push_back().set(kVec4f_GrSLType,
             GrGLShaderVar::kAttribute_TypeModifier, EDGE_ATTR_NAME);
         segments->fVSCode.appendf("\t%s = " EDGE_ATTR_NAME ";\n", vsName);
         if (GrDrawState::kHairLine_EdgeType == fProgramDesc.fVertexEdgeType) {
@@ -539,16 +518,16 @@
                    GrStringBuilder* inColor) {
     switch (colorInput) {
         case GrGLProgram::ProgramDesc::kAttribute_ColorInput: {
-            segments->fVSAttrs.push_back().set(GrGLShaderVar::kVec4f_Type,
+            segments->fVSAttrs.push_back().set(kVec4f_GrSLType,
                 GrGLShaderVar::kAttribute_TypeModifier,
                 COL_ATTR_NAME);
             const char *vsName, *fsName;
-            append_varying(GrGLShaderVar::kVec4f_Type, "Color", segments, &vsName, &fsName);
+            append_varying(kVec4f_GrSLType, "Color", segments, &vsName, &fsName);
             segments->fVSCode.appendf("\t%s = " COL_ATTR_NAME ";\n", vsName);
             *inColor = fsName;
             } break;
         case GrGLProgram::ProgramDesc::kUniform_ColorInput:
-            segments->fFSUnis.push_back().set(GrGLShaderVar::kVec4f_Type,
+            segments->fFSUnis.push_back().set(kVec4f_GrSLType,
                 GrGLShaderVar::kUniform_TypeModifier,
                 COL_UNI_NAME);
             programData->fUniLocations.fColorUni = kUseUniform;
@@ -567,11 +546,11 @@
 
 void genAttributeCoverage(ShaderCodeSegments* segments,
                           GrStringBuilder* inOutCoverage) {
-    segments->fVSAttrs.push_back().set(GrGLShaderVar::kVec4f_Type,
+    segments->fVSAttrs.push_back().set(kVec4f_GrSLType,
                                        GrGLShaderVar::kAttribute_TypeModifier,
                                        COV_ATTR_NAME);
     const char *vsName, *fsName;
-    append_varying(GrGLShaderVar::kVec4f_Type, "Coverage", 
+    append_varying(kVec4f_GrSLType, "Coverage", 
                    segments, &vsName, &fsName);
     segments->fVSCode.appendf("\t%s = " COV_ATTR_NAME ";\n", vsName);
     if (inOutCoverage->size()) {
@@ -586,7 +565,7 @@
 void genUniformCoverage(ShaderCodeSegments* segments,
                         GrGLProgram::CachedData* programData,
                         GrStringBuilder* inOutCoverage) {
-    segments->fFSUnis.push_back().set(GrGLShaderVar::kVec4f_Type,
+    segments->fFSUnis.push_back().set(kVec4f_GrSLType,
                                       GrGLShaderVar::kUniform_TypeModifier,
                                       COV_UNI_NAME);
     programData->fUniLocations.fCoverageUni = kUseUniform;
@@ -711,15 +690,15 @@
     }
 
 #if GR_GL_ATTRIBUTE_MATRICES
-    segments.fVSAttrs.push_back().set(GrGLShaderVar::kMat33f_Type,
+    segments.fVSAttrs.push_back().set(kMat33f_GrSLType,
         GrGLShaderVar::kAttribute_TypeModifier, VIEW_MATRIX_NAME);
     programData->fUniLocations.fViewMatrixUni = kSetAsAttribute;
 #else
-    segments.fVSUnis.push_back().set(GrGLShaderVar::kMat33f_Type,
+    segments.fVSUnis.push_back().set(kMat33f_GrSLType,
         GrGLShaderVar::kUniform_TypeModifier, VIEW_MATRIX_NAME);
     programData->fUniLocations.fViewMatrixUni = kUseUniform;
 #endif
-    segments.fVSAttrs.push_back().set(GrGLShaderVar::kVec2f_Type,
+    segments.fVSAttrs.push_back().set(kVec2f_GrSLType,
         GrGLShaderVar::kAttribute_TypeModifier, POS_ATTR_NAME);
 
     segments.fVSCode.append(
@@ -747,7 +726,7 @@
     for (int t = 0; t < GrDrawState::kMaxTexCoords; ++t) {
         if (GrDrawTarget::VertexUsesTexCoordIdx(t, layout)) {
             tex_attr_name(t, texCoordAttrs + t);
-            segments.fVSAttrs.push_back().set(GrGLShaderVar::kVec2f_Type,
+            segments.fVSAttrs.push_back().set(kVec2f_GrSLType,
                 GrGLShaderVar::kAttribute_TypeModifier,
                 texCoordAttrs[t].c_str());
         }
@@ -807,7 +786,7 @@
         }
     }
     if (needColorFilterUniform) {
-        segments.fFSUnis.push_back().set(GrGLShaderVar::kVec4f_Type,
+        segments.fFSUnis.push_back().set(kVec4f_GrSLType,
                                          GrGLShaderVar::kUniform_TypeModifier,
                                          COL_FILTER_UNI_NAME);
         programData->fUniLocations.fColorFilterUni = kUseUniform;
@@ -828,10 +807,10 @@
         inColor = "filteredColor";
     }
     if (applyColorMatrix) {
-        segments.fFSUnis.push_back().set(GrGLShaderVar::kMat44f_Type,
+        segments.fFSUnis.push_back().set(kMat44f_GrSLType,
                                          GrGLShaderVar::kUniform_TypeModifier,
                                          COL_MATRIX_UNI_NAME);
-        segments.fFSUnis.push_back().set(GrGLShaderVar::kVec4f_Type,
+        segments.fFSUnis.push_back().set(kVec4f_GrSLType,
                                          GrGLShaderVar::kUniform_TypeModifier,
                                          COL_MATRIX_VEC_UNI_NAME);
         programData->fUniLocations.fColorMatrixUni = kUseUniform;
@@ -911,7 +890,7 @@
             }
         }
         if (ProgramDesc::kNone_DualSrcOutput != fProgramDesc.fDualSrcOutput) {
-            segments.fFSOutputs.push_back().set(GrGLShaderVar::kVec4f_Type,
+            segments.fFSOutputs.push_back().set(kVec4f_GrSLType,
                 GrGLShaderVar::kOut_TypeModifier,
                 dual_source_output_name());
             bool outputIsZero = coverageIsZero;
@@ -1411,7 +1390,7 @@
                         int varyingDims, int coordDims) {
 
     GrGLShaderVar* radial2FSParams = &segments->fFSUnis.push_back();
-    radial2FSParams->setType(GrGLShaderVar::kFloat_Type);
+    radial2FSParams->setType(kFloat_GrSLType);
     radial2FSParams->setTypeModifier(GrGLShaderVar::kUniform_TypeModifier);
     radial2FSParams->setArrayCount(6);
     radial2_param_name(stageNum, radial2FSParams->accessName());
@@ -1423,7 +1402,7 @@
     // part of the quadratic as a varying.
     if (varyingDims == coordDims) {
         GrAssert(2 == coordDims);
-        append_varying(GrGLShaderVar::kFloat_Type,
+        append_varying(kFloat_GrSLType,
                        "Radial2BCoeff",
                        stageNum,
                        segments,
@@ -1601,11 +1580,11 @@
                       const char* varyingVSName) {
     //GrGLShaderVar* kernel = &segments->fFSUnis.push_back();
     *kernel = &segments->fFSUnis.push_back();
-    (*kernel)->setType(GrGLShaderVar::kFloat_Type);
+    (*kernel)->setType(kFloat_GrSLType);
     (*kernel)->setTypeModifier(GrGLShaderVar::kUniform_TypeModifier);
     (*kernel)->setArrayCount(desc.fKernelWidth);
     GrGLShaderVar* imgInc = &segments->fFSUnis.push_back();
-    imgInc->setType(GrGLShaderVar::kVec2f_Type);
+    imgInc->setType(kVec2f_GrSLType);
     imgInc->setTypeModifier(GrGLShaderVar::kUniform_TypeModifier);
 
     convolve_param_names(stageNum,
@@ -1669,7 +1648,7 @@
                      const char** imageIncrementName,
                      const char* varyingVSName) {
     GrGLShaderVar* imgInc = &segments->fFSUnis.push_back();
-    imgInc->setType(GrGLShaderVar::kVec2f_Type);
+    imgInc->setType(kVec2f_GrSLType);
     imgInc->setTypeModifier(GrGLShaderVar::kUniform_TypeModifier);
 
     image_increment_param_name(stageNum, imgInc->accessName());
@@ -1763,7 +1742,7 @@
         locations->fTextureMatrixUni = kUseUniform;
     #endif
         tex_matrix_name(stageNum, mat->accessName());
-        mat->setType(GrGLShaderVar::kMat33f_Type);
+        mat->setType(kMat33f_GrSLType);
         matName = mat->getName().c_str();
 
         if (desc.fOptFlags & StageDesc::kNoPerspective_OptFlagBit) {
@@ -1773,7 +1752,7 @@
         }
     }
 
-    segments->fFSUnis.push_back().set(GrGLShaderVar::kSampler2D_Type,
+    segments->fFSUnis.push_back().set(kSampler2D_GrSLType,
         GrGLShaderVar::kUniform_TypeModifier, "");
     sampler_name(stageNum, segments->fFSUnis.back().accessName());
     locations->fSamplerUni = kUseUniform;
@@ -1781,14 +1760,14 @@
 
     const char* texelSizeName = NULL;
     if (StageDesc::k2x2_FetchMode == desc.fFetchMode) {
-        segments->fFSUnis.push_back().set(GrGLShaderVar::kVec2f_Type,
+        segments->fFSUnis.push_back().set(kVec2f_GrSLType,
             GrGLShaderVar::kUniform_TypeModifier, "");
         normalized_texel_size_name(stageNum, segments->fFSUnis.back().accessName());
         texelSizeName = segments->fFSUnis.back().getName().c_str();
     }
 
     const char *varyingVSName, *varyingFSName;
-    append_varying(float_vector_type(varyingDims),
+    append_varying(GrSLFloatVectorType(varyingDims),
                     "Stage",
                    stageNum,
                    segments,
@@ -1849,12 +1828,12 @@
             fsCoordName = "inCoord";
             fsCoordName.appendS32(stageNum);
             segments->fFSCode.appendf("\t%s %s = %s%s / %s%s;\n",
-                                GrGLShaderVar::TypeString(float_vector_type(coordDims)),
-                                fsCoordName.c_str(),
-                                varyingFSName,
-                                vector_nonhomog_coords(varyingDims),
-                                varyingFSName,
-                                vector_homog_coord(varyingDims));
+                GrGLShaderVar::TypeString(GrSLFloatVectorType(coordDims)),
+                fsCoordName.c_str(),
+                varyingFSName,
+                GrGLSLVectorNonhomogCoords(varyingDims),
+                varyingFSName,
+                GrGLSLVectorHomogCoord(varyingDims));
         }
     }
 
@@ -1918,7 +1897,7 @@
         StageDesc::kCustomTextureDomain_OptFlagBit) {
         GrStringBuilder texDomainName;
         tex_domain_name(stageNum, &texDomainName);
-        segments->fFSUnis.push_back().set(GrGLShaderVar::kVec4f_Type,
+        segments->fFSUnis.push_back().set(kVec4f_GrSLType,
             GrGLShaderVar::kUniform_TypeModifier, texDomainName);
         GrStringBuilder coordVar("clampCoord");
         segments->fFSCode.appendf("\t%s %s = clamp(%s, %s.xy, %s.zw);\n",
diff --git a/src/gpu/gl/GrGLProgramStage.cpp b/src/gpu/gl/GrGLProgramStage.cpp
new file mode 100644
index 0000000..0ba0399
--- /dev/null
+++ b/src/gpu/gl/GrGLProgramStage.cpp
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrGLSL.h"
+#include "GrGLProgramStage.h"
+
+GrGLProgramStageFactory::~GrGLProgramStageFactory(void) {
+
+}
+
+uint16_t GrGLProgramStageFactory::stageKey(const GrCustomStage*) {
+    return 0;
+}
+
+void GrGLProgramStage::setupVSUnis(VarArray& vsUnis, int stage) {
+
+}
+
+void GrGLProgramStage::setupFSUnis(VarArray& fsUnis, int stage) {
+
+}
+
+void GrGLProgramStage::initUniforms(const GrGLInterface*, int progID) {
+
+}
+
+void GrGLProgramStage::setData(const GrGLInterface*, GrCustomStage*) {
+
+}
+
+GrStringBuilder GrGLProgramStage::emitTextureSetup(GrStringBuilder* code,
+                                                   const char* coordName,
+                                                   int stageNum,
+                                                   int coordDims,
+                                                   int varyingDims) {
+    GrStringBuilder retval;
+
+    switch (fSamplerMode) {
+        case kDefault_SamplerMode:
+            // Fall through
+        case kProj_SamplerMode:
+            // Do nothing
+            retval = coordName;
+            break;
+        case kExplicitDivide_SamplerMode:
+            retval = "inCoord";
+            retval.appendS32(stageNum);
+            code->appendf("\t %s %s = %s%s / %s%s\n",
+                GrGLShaderVar::TypeString(GrSLFloatVectorType(coordDims)),
+                fCoordName.c_str(),
+                coordName,
+                GrGLSLVectorNonhomogCoords(varyingDims),
+                coordName,
+                GrGLSLVectorHomogCoord(varyingDims));
+            break;
+    }
+    return retval;
+}
+
+void GrGLProgramStage::emitTextureLookup(GrStringBuilder* code,
+                                         const char* samplerName,
+                                         const char* coordName) {
+    switch (fSamplerMode) {
+        case kDefault_SamplerMode:
+            // Fall through
+        case kExplicitDivide_SamplerMode:
+            code->appendf("texture2D(%s, %s)", samplerName, coordName);
+            break;
+        case kProj_SamplerMode:
+            code->appendf("texture2DProj(%s, %s)", samplerName, coordName);
+            break;
+    }
+
+}
+
diff --git a/src/gpu/gl/GrGLProgramStage.h b/src/gpu/gl/GrGLProgramStage.h
new file mode 100644
index 0000000..c9c4100
--- /dev/null
+++ b/src/gpu/gl/GrGLProgramStage.h
@@ -0,0 +1,146 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrGLCustomStage_DEFINED
+#define GrGLCustomStage_DEFINED
+
+#include "../GrAllocator.h"
+#include "GrGLShaderVar.h"
+#include "GrGLSL.h"
+#include "../GrStringBuilder.h"
+
+class GrCustomStage;
+class GrGLInterface;
+
+/** @file
+    This file contains specializations for OpenGL of the shader stages
+    declared in src/gpu/GrCustomStage.h. All the functions emit
+    GLSL shader code and OpenGL calls.
+
+    These objects are created by a factory function on the
+    GrCustomStage.
+    TODO: lifetime management.
+*/
+
+class GrGLProgramStage {
+
+public:
+    // TODO: redundant with GrGLProgram.cpp
+    enum {
+        kUnusedUniform = -1,
+        kUseUniform = 2000
+    };
+
+    typedef GrTAllocator<GrGLShaderVar> VarArray;
+
+
+    /** Creates any uniform variables the vertex shader requires
+        and appends them to vsUnis;
+        must guarantee they are unique (typically done by
+        appending the stage number). */
+    virtual void setupVSUnis(VarArray& vsUnis, int stage);
+
+    /** Creates any uniform variables the fragment shader requires
+        and appends them to fsUnis;
+        must guarantee they are unique (typically done by
+        appending the stage number). */
+    virtual void setupFSUnis(VarArray& fsUnis, int stage);
+
+    /** Given an empty GrStringBuilder and the names of variables;
+        must write shader code into that GrStringBuilder.
+        Vertex shader input is a vec2 of coordinates, which may
+        be altered.
+        The code will be inside an otherwise-empty block. */
+    virtual void emitVS(GrStringBuilder* code,
+                        const char* vertexCoords) = 0;
+
+    /** Given an empty GrStringBuilder and the names of variables;
+        must write shader code into that GrStringBuilder.
+        The code will be inside an otherwise-empty block.
+        Fragment shader inputs are a vec2 of coordinates, one texture,
+        and a color; output is a color. */
+    /* TODO: don't give them the samplerName, just a handle; then give
+       a function here for them to call into that'll apply any texture
+       domain - but do we force them to be honest about texture domain
+       parameters? */
+    virtual void emitFS(GrStringBuilder* code,
+                        const char* outputColor,
+                        const char* inputColor,
+                        const char* samplerName,
+                        const char* sampleCoords) = 0;
+
+    /** Binds uniforms; we must have already bound the program and
+        determined its GL program ID. */
+    virtual void initUniforms(const GrGLInterface*, int programID);
+
+    /** A GrGLCustomStage instance can be reused with any GrCustomStage
+        that produces the same stage key; this function reads data from
+        a stage and uploads any uniform variables required by the shaders
+        created in emit*().
+        flush() to change the GrCustomStage from which the uniforms
+        are to be read.
+        TODO: since we don't have a factory, we can't assert to enforce
+        this. Shouldn't we? */
+    virtual void setData(const GrGLInterface*, GrCustomStage*);
+
+    // TODO: needs a better name
+    enum SamplerMode {
+        kDefault_SamplerMode,
+        kProj_SamplerMode,
+        kExplicitDivide_SamplerMode  // must do an explicit divide
+    };
+
+    void setSamplerMode(SamplerMode shaderMode) { fSamplerMode = shaderMode; }
+
+protected:
+
+    /** Returns the *effective* coord name after any perspective divide
+        or other transform. */
+    GrStringBuilder emitTextureSetup(GrStringBuilder* code,
+                                     const char* coordName,
+                                     int stageNum,
+                                     int coordDims,
+                                     int varyingDims);
+
+    /** Convenience function for subclasses to write texture2D() or
+        texture2DProj(), depending on fSamplerMode. */
+    void emitTextureLookup(GrStringBuilder* code,
+                           const char* samplerName,
+                           const char* coordName);
+
+    SamplerMode fSamplerMode;
+    GrStringBuilder fCoordName;
+
+};
+
+
+/// Every GrGLProgramStage subclass needs a GrGLProgramStageFactory subclass
+/// to manage its creation.
+
+class GrGLProgramStageFactory {
+
+public:
+
+    virtual ~GrGLProgramStageFactory();
+
+    /** Returns a short unique identifier for this subclass x its
+        parameters. If the key differs, different shader code must
+        be generated; if the key matches, shader code can be reused.
+        0 == no custom stage.  */
+    virtual uint16_t stageKey(const GrCustomStage*);
+
+    virtual GrGLProgramStage* createGLInstance(GrCustomStage*) = 0;
+
+protected:
+
+    /** Disable default constructor - instances should be singletons
+        with static factory functions: our test examples are all stateless,
+        but we suspect that future implementations may want to cache data?  */
+    GrGLProgramStageFactory() { }
+};
+
+#endif
diff --git a/src/gpu/gl/GrGLSL.cpp b/src/gpu/gl/GrGLSL.cpp
index e933ee8..21dd940 100644
--- a/src/gpu/gl/GrGLSL.cpp
+++ b/src/gpu/gl/GrGLSL.cpp
@@ -75,8 +75,30 @@
                              const char* nameIfDeclared,
                              GrGLShaderVar* var) {
     bool declaredOutput = k110_GrGLSLGeneration != gen;
-    var->set(GrGLShaderVar::kVec4f_Type,
+    var->set(kVec4f_GrSLType,
              GrGLShaderVar::kOut_TypeModifier,
              declaredOutput ? nameIfDeclared : "gl_FragColor");
     return declaredOutput;
 }
+
+GrSLType GrSLFloatVectorType (int count) {
+    GR_STATIC_ASSERT(kFloat_GrSLType == 0);
+    GR_STATIC_ASSERT(kVec2f_GrSLType == 1);
+    GR_STATIC_ASSERT(kVec3f_GrSLType == 2);
+    GR_STATIC_ASSERT(kVec4f_GrSLType == 3);
+    GrAssert(count > 0 && count <= 4);
+    return (GrSLType)(count - 1);
+}
+
+const char* GrGLSLVectorHomogCoord(int count) {
+    static const char* HOMOGS[] = {"ERROR", "", ".y", ".z", ".w"};
+    GrAssert(count >= 1 && count < (int)GR_ARRAY_COUNT(HOMOGS));
+    return HOMOGS[count];
+}
+
+const char* GrGLSLVectorNonhomogCoords(int count) {
+    static const char* NONHOMOGS[] = {"ERROR", "", ".x", ".xy", ".xyz"};
+    GrAssert(count >= 1 && count < (int)GR_ARRAY_COUNT(NONHOMOGS));
+    return NONHOMOGS[count];
+}
+
diff --git a/src/gpu/gl/GrGLSL.h b/src/gpu/gl/GrGLSL.h
index a3d3921..587d6f2 100644
--- a/src/gpu/gl/GrGLSL.h
+++ b/src/gpu/gl/GrGLSL.h
@@ -30,6 +30,21 @@
 };
 
 /**
+ * Types of shader-language-specific boxed variables we can create.
+ * (Currently only GrGLShaderVars, but should be applicable to other shader
+ * langauges.)
+ */
+enum GrSLType {
+    kFloat_GrSLType,
+    kVec2f_GrSLType,
+    kVec3f_GrSLType,
+    kVec4f_GrSLType,
+    kMat33f_GrSLType,
+    kMat44f_GrSLType,
+    kSampler2D_GrSLType
+};
+
+/**
  * Gets the most recent GLSL Generation compatible with the OpenGL context.
  */
 GrGLSLGeneration GrGetGLSLGeneration(GrGLBinding binding,
@@ -69,8 +84,20 @@
  * In either case var is initialized to represent the color output in the
  * shader.
  */
- bool GrGLSLSetupFSColorOuput(GrGLSLGeneration gen,
+bool GrGLSLSetupFSColorOuput(GrGLSLGeneration gen,
                              const char* nameIfDeclared,
                              GrGLShaderVar* var);
 
+/** Convert a count of 1..n floats into the corresponding type enum,
+    e.g. 1 -> kFloat_GrSLType, 2 -> kVec2_GrSLType, ... */
+GrSLType GrSLFloatVectorType(int count);
+
+/** Return the GLSL swizzle operator for a homogenous component of a vector
+    with the given number of coordnates, e.g. 2 -> ".y", 3 -> ".z" */
+const char* GrGLSLVectorHomogCoord(int count);
+
+/** Return the GLSL swizzle operator for a nonhomogenous components of a vector
+    with the given number of coordnates, e.g. 2 -> ".x", 3 -> ".xy" */
+const char* GrGLSLVectorNonhomogCoords(int count);
+
 #endif
diff --git a/src/gpu/gl/GrGLShaderVar.h b/src/gpu/gl/GrGLShaderVar.h
index dc7d52b..149214e 100644
--- a/src/gpu/gl/GrGLShaderVar.h
+++ b/src/gpu/gl/GrGLShaderVar.h
@@ -21,16 +21,6 @@
 class GrGLShaderVar {
 public:
 
-    enum Type {
-        kFloat_Type,
-        kVec2f_Type,
-        kVec3f_Type,
-        kVec4f_Type,
-        kMat33f_Type,
-        kMat44f_Type,
-        kSampler2D_Type,
-    };
-
     /**
      * Early versions of GLSL have Varying and Attribute; those are later
      * deprecated, but we still need to know whether a Varying variable
@@ -48,7 +38,7 @@
      * Defaults to a float with no precision specifier
      */
     GrGLShaderVar() {
-        fType = kFloat_Type;
+        fType = kFloat_GrSLType;
         fTypeModifier = kNone_TypeModifier;
         fCount = kNonArray;
         fEmitPrecision = false;
@@ -74,7 +64,7 @@
     /**
      * Sets as a non-array.
      */
-    void set(Type type,
+    void set(GrSLType type,
              TypeModifier typeModifier,
              const GrStringBuilder& name,
              bool emitPrecision = false,
@@ -90,7 +80,7 @@
     /**
      * Sets as a non-array.
      */
-    void set(Type type,
+    void set(GrSLType type,
              TypeModifier typeModifier,
              const char* name,
              bool specifyPrecision = false,
@@ -106,7 +96,7 @@
     /**
      * Set all var options
      */
-    void set(Type type,
+    void set(GrSLType type,
              TypeModifier typeModifier,
              const GrStringBuilder& name,
              int count,
@@ -123,7 +113,7 @@
     /**
      * Set all var options
      */
-    void set(Type type,
+    void set(GrSLType type,
              TypeModifier typeModifier,
              const char* name,
              int count,
@@ -179,11 +169,11 @@
     /**
      * Get the type of the var
      */
-    Type getType() const { return fType; }
+    GrSLType getType() const { return fType; }
     /**
      * Set the type of the var
      */
-    void setType(Type type) { fType = type; }
+    void setType(GrSLType type) { fType = type; }
 
     TypeModifier getTypeModifier() const { return fTypeModifier; }
     void setTypeModifier(TypeModifier type) { fTypeModifier = type; }
@@ -210,7 +200,7 @@
             out->append(GrGetGLSLVarPrecisionDeclType(gl.binding()));
             out->append(" ");
         }
-        Type effectiveType = this->getType();
+        GrSLType effectiveType = this->getType();
         if (this->isArray()) {
             if (this->isUnsizedArray()) {
                 out->appendf("%s %s[]", 
@@ -231,21 +221,21 @@
         out->append(";\n");
     }
 
-    static const char* TypeString(Type t) {
+    static const char* TypeString(GrSLType t) {
         switch (t) {
-            case kFloat_Type:
+            case kFloat_GrSLType:
                 return "float";
-            case kVec2f_Type:
+            case kVec2f_GrSLType:
                 return "vec2";
-            case kVec3f_Type:
+            case kVec3f_GrSLType:
                 return "vec3";
-            case kVec4f_Type:
+            case kVec4f_GrSLType:
                 return "vec4";
-            case kMat33f_Type:
+            case kMat33f_GrSLType:
                 return "mat3";
-            case kMat44f_Type:
+            case kMat44f_GrSLType:
                 return "mat4";
-            case kSampler2D_Type:
+            case kSampler2D_GrSLType:
                 return "sampler2D";
             default:
                 GrCrash("Unknown shader var type.");
@@ -287,7 +277,7 @@
         }
     }
 
-    Type            fType;
+    GrSLType fType;
     TypeModifier    fTypeModifier;
     GrStringBuilder fName;
     int             fCount;