Move most of the transform logic into the primitive processors

BUG=skia:

Review URL: https://codereview.chromium.org/822423004
diff --git a/gyp/gpu.gypi b/gyp/gpu.gypi
index 5be7f4b..90896b3 100644
--- a/gyp/gpu.gypi
+++ b/gyp/gpu.gypi
@@ -272,10 +272,6 @@
       '<(skia_src_path)/gpu/gl/GrGLVertexBuffer.h',
 
       # Files for building GLSL shaders
-      '<(skia_src_path)/gpu/gl/builders/GrGLLegacyNvprProgramBuilder.cpp',
-      '<(skia_src_path)/gpu/gl/builders/GrGLLegacyNvprProgramBuilder.h',
-      '<(skia_src_path)/gpu/gl/builders/GrGLNvprProgramBuilder.cpp',
-      '<(skia_src_path)/gpu/gl/builders/GrGLNvprProgramBuilder.h',
       '<(skia_src_path)/gpu/gl/builders/GrGLProgramBuilder.cpp',
       '<(skia_src_path)/gpu/gl/builders/GrGLProgramBuilder.h',
       '<(skia_src_path)/gpu/gl/builders/GrGLShaderBuilder.cpp',
diff --git a/include/gpu/GrFragmentProcessor.h b/include/gpu/GrFragmentProcessor.h
index fe6e6fa..341b3fb 100644
--- a/include/gpu/GrFragmentProcessor.h
+++ b/include/gpu/GrFragmentProcessor.h
@@ -47,6 +47,10 @@
         numTransforms(). */
     const GrCoordTransform& coordTransform(int index) const { return *fCoordTransforms[index]; }
 
+    const SkTArray<const GrCoordTransform*, true>& coordTransforms() const {
+        return fCoordTransforms;
+    }
+
     /** Will this prceossor read the destination pixel value? */
     bool willReadDstColor() const { return fWillReadDstColor; }
 
diff --git a/src/gpu/GrAAConvexPathRenderer.cpp b/src/gpu/GrAAConvexPathRenderer.cpp
index 784f685..0690175 100644
--- a/src/gpu/GrAAConvexPathRenderer.cpp
+++ b/src/gpu/GrAAConvexPathRenderer.cpp
@@ -535,11 +535,14 @@
                     const GrBatchTracker&)
             : fColor(GrColor_ILLEGAL) {}
 
-        void emitCode(const EmitArgs& args) SK_OVERRIDE {
+        void onEmitCode(EmitArgs& args) SK_OVERRIDE {
             const QuadEdgeEffect& qe = args.fGP.cast<QuadEdgeEffect>();
             GrGLGPBuilder* pb = args.fPB;
             GrGLVertexBuilder* vsBuilder = pb->getVertexShaderBuilder();
 
+            // emit attributes
+            vsBuilder->emitAttributes(qe);
+
             GrGLVertToFrag v(kVec4f_GrSLType);
             args.fPB->addVarying("QuadEdge", &v);
             vsBuilder->codeAppendf("%s = %s;", v.vsOut(), qe.inQuadEdge()->fName);
@@ -550,16 +553,16 @@
             this->setupColorPassThrough(pb, local.fInputColorType, args.fOutputColor, NULL,
                                         &fColorUniform);
 
-            // setup coord outputs
-            vsBuilder->codeAppendf("%s = %s;", vsBuilder->positionCoords(), qe.inPosition()->fName);
-            vsBuilder->codeAppendf("%s = %s;", vsBuilder->localCoords(), qe.inPosition()->fName);
-
             // setup uniform viewMatrix
             this->addUniformViewMatrix(pb);
 
-            // setup position varying
-            vsBuilder->codeAppendf("%s = %s * vec3(%s, 1);", vsBuilder->glPosition(),
-                                   this->uViewM(), qe.inPosition()->fName);
+            // Setup position
+            vsBuilder->codeAppendf("%s = %s * vec3(%s, 1);",  this->position(), this->uViewM(),
+                                   qe.inPosition()->fName);
+
+            // emit transforms
+            this->emitTransforms(args.fPB,  this->position(), qe.inPosition()->fName,
+                                 qe.localMatrix(), args.fTransformsIn, args.fTransformsOut);
 
             GrGLGPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
 
@@ -622,7 +625,8 @@
         GLProcessor::GenKey(*this, bt, caps, b);
     }
 
-    GrGLGeometryProcessor* createGLInstance(const GrBatchTracker& bt) const SK_OVERRIDE {
+    virtual GrGLPrimitiveProcessor* createGLInstance(const GrBatchTracker& bt,
+                                                     const GrGLCaps&) const SK_OVERRIDE {
         return SkNEW_ARGS(GLProcessor, (*this, bt));
     }
 
diff --git a/src/gpu/GrDefaultGeoProcFactory.cpp b/src/gpu/GrDefaultGeoProcFactory.cpp
index 49c663d..a6eaa6d 100644
--- a/src/gpu/GrDefaultGeoProcFactory.cpp
+++ b/src/gpu/GrDefaultGeoProcFactory.cpp
@@ -83,33 +83,37 @@
         GLProcessor(const GrGeometryProcessor& gp, const GrBatchTracker&)
             : fColor(GrColor_ILLEGAL), fCoverage(0xff) {}
 
-        void emitCode(const EmitArgs& args) SK_OVERRIDE {
+        void onEmitCode(EmitArgs& args) SK_OVERRIDE {
             const DefaultGeoProc& gp = args.fGP.cast<DefaultGeoProc>();
             GrGLGPBuilder* pb = args.fPB;
             GrGLVertexBuilder* vs = pb->getVertexShaderBuilder();
             GrGLGPFragmentBuilder* fs = args.fPB->getFragmentShaderBuilder();
             const BatchTracker& local = args.fBT.cast<BatchTracker>();
 
-            vs->codeAppendf("%s = %s;", vs->positionCoords(), gp.inPosition()->fName);
+            // emit attributes
+            vs->emitAttributes(gp);
 
             // Setup pass through color
             this->setupColorPassThrough(pb, local.fInputColorType, args.fOutputColor, gp.inColor(),
                                         &fColorUniform);
 
-            // Setup local coords if needed
-            if (gp.inLocalCoords()) {
-                vs->codeAppendf("%s = %s;", vs->localCoords(), gp.inLocalCoords()->fName);
-            } else {
-                vs->codeAppendf("%s = %s;", vs->localCoords(), gp.inPosition()->fName);
-            }
-
             // setup uniform viewMatrix
             this->addUniformViewMatrix(pb);
 
-            // setup position varying
-            vs->codeAppendf("%s = %s * vec3(%s, 1);", vs->glPosition(), this->uViewM(),
+            // Setup position
+            vs->codeAppendf("%s = %s * vec3(%s, 1);",  this->position(), this->uViewM(),
                             gp.inPosition()->fName);
 
+            if (gp.inLocalCoords()) {
+                // emit transforms with explicit local coords
+                this->emitTransforms(pb,  this->position(), gp.inLocalCoords()->fName,
+                                     gp.localMatrix(), args.fTransformsIn, args.fTransformsOut);
+            } else {
+                // emit transforms with position
+                this->emitTransforms(pb,  this->position(), gp.inPosition()->fName,
+                                     gp.localMatrix(), args.fTransformsIn, args.fTransformsOut);
+            }
+
             // Setup coverage as pass through
             if (kUniform_GrGPInput == local.fInputCoverageType) {
                 const char* fragCoverage;
@@ -174,7 +178,8 @@
         GLProcessor::GenKey(*this, bt, caps, b);
     }
 
-    GrGLGeometryProcessor* createGLInstance(const GrBatchTracker& bt) const SK_OVERRIDE {
+    virtual GrGLPrimitiveProcessor* createGLInstance(const GrBatchTracker& bt,
+                                                     const GrGLCaps&) const SK_OVERRIDE {
         return SkNEW_ARGS(GLProcessor, (*this, bt));
     }
 
diff --git a/src/gpu/GrGeometryProcessor.cpp b/src/gpu/GrGeometryProcessor.cpp
index 6bd6b2b..e8ffc7b 100644
--- a/src/gpu/GrGeometryProcessor.cpp
+++ b/src/gpu/GrGeometryProcessor.cpp
@@ -7,8 +7,68 @@
 
 #include "GrGeometryProcessor.h"
 
-#include "gl/GrGLGeometryProcessor.h"
+#include "GrCoordTransform.h"
 #include "GrInvariantOutput.h"
+#include "gl/GrGLGeometryProcessor.h"
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * The key for an individual coord transform is made up of a matrix type, a precision, and a bit
+ * that indicates the source of the input coords.
+ */
+enum {
+    kMatrixTypeKeyBits   = 1,
+    kMatrixTypeKeyMask   = (1 << kMatrixTypeKeyBits) - 1,
+
+    kPrecisionBits       = 2,
+    kPrecisionShift      = kMatrixTypeKeyBits,
+
+    kPositionCoords_Flag = (1 << (kPrecisionShift + kPrecisionBits)),
+    kDeviceCoords_Flag   = kPositionCoords_Flag + kPositionCoords_Flag,
+
+    kTransformKeyBits    = kMatrixTypeKeyBits + kPrecisionBits + 2,
+};
+
+GR_STATIC_ASSERT(kHigh_GrSLPrecision < (1 << kPrecisionBits));
+
+/**
+ * We specialize the vertex code for each of these matrix types.
+ */
+enum MatrixType {
+    kNoPersp_MatrixType  = 0,
+    kGeneral_MatrixType  = 1,
+};
+
+uint32_t
+GrPrimitiveProcessor::getTransformKey(const SkTArray<const GrCoordTransform*, true>& coords) const {
+    uint32_t totalKey = 0;
+    for (int t = 0; t < coords.count(); ++t) {
+        uint32_t key = 0;
+        const GrCoordTransform* coordTransform = coords[t];
+        if (coordTransform->getMatrix().hasPerspective()) {
+            key |= kGeneral_MatrixType;
+        } else {
+            key |= kNoPersp_MatrixType;
+        }
+
+        if (kLocal_GrCoordSet == coordTransform->sourceCoords() &&
+            !this->hasExplicitLocalCoords()) {
+            key |= kPositionCoords_Flag;
+        } else if (kDevice_GrCoordSet == coordTransform->sourceCoords()) {
+            key |= kDeviceCoords_Flag;
+        }
+
+        GR_STATIC_ASSERT(kGrSLPrecisionCount <= (1 << kPrecisionBits));
+        key |= (coordTransform->precision() << kPrecisionShift);
+
+        key <<= kTransformKeyBits * t;
+
+        SkASSERT(0 == (totalKey & key)); // keys for each transform ought not to overlap
+        totalKey |= key;
+    }
+    return totalKey;
+}
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
@@ -33,11 +93,34 @@
 
 #include "gl/builders/GrGLProgramBuilder.h"
 
-void GrGLGeometryProcessor::setupColorPassThrough(GrGLGPBuilder* pb,
-                                                  GrGPInput inputType,
-                                                  const char* outputName,
-                                                  const GrGeometryProcessor::GrAttribute* colorAttr,
-                                                  UniformHandle* colorUniform) {
+SkMatrix GrGLPrimitiveProcessor::GetTransformMatrix(const SkMatrix& localMatrix,
+                                                    const GrCoordTransform& coordTransform) {
+    SkMatrix combined;
+    // We only apply the localmatrix to localcoords
+    if (kLocal_GrCoordSet == coordTransform.sourceCoords()) {
+        combined.setConcat(coordTransform.getMatrix(), localMatrix);
+    } else {
+        combined = coordTransform.getMatrix();
+    }
+    if (coordTransform.reverseY()) {
+        // combined.postScale(1,-1);
+        // combined.postTranslate(0,1);
+        combined.set(SkMatrix::kMSkewY,
+            combined[SkMatrix::kMPersp0] - combined[SkMatrix::kMSkewY]);
+        combined.set(SkMatrix::kMScaleY,
+            combined[SkMatrix::kMPersp1] - combined[SkMatrix::kMScaleY]);
+        combined.set(SkMatrix::kMTransY,
+            combined[SkMatrix::kMPersp2] - combined[SkMatrix::kMTransY]);
+    }
+    return combined;
+}
+
+void
+GrGLPrimitiveProcessor::setupColorPassThrough(GrGLGPBuilder* pb,
+                                              GrGPInput inputType,
+                                              const char* outputName,
+                                              const GrGeometryProcessor::GrAttribute* colorAttr,
+                                              UniformHandle* colorUniform) {
     GrGLGPFragmentBuilder* fs = pb->getFragmentShaderBuilder();
     if (kUniform_GrGPInput == inputType) {
         SkASSERT(colorUniform);
@@ -56,15 +139,15 @@
     }
 }
 
-void GrGLGeometryProcessor::addUniformViewMatrix(GrGLGPBuilder* pb) {
+void GrGLPrimitiveProcessor::addUniformViewMatrix(GrGLGPBuilder* pb) {
     fViewMatrixUniform = pb->addUniform(GrGLProgramBuilder::kVertex_Visibility,
                                         kMat33f_GrSLType, kDefault_GrSLPrecision,
                                         "uViewM",
                                         &fViewMatrixName);
 }
 
-void GrGLGeometryProcessor::setUniformViewMatrix(const GrGLProgramDataManager& pdman,
-                                                 const SkMatrix& viewMatrix) {
+void GrGLPrimitiveProcessor::setUniformViewMatrix(const GrGLProgramDataManager& pdman,
+                                                  const SkMatrix& viewMatrix) {
     if (!fViewMatrix.cheapEqualTo(viewMatrix)) {
         SkASSERT(fViewMatrixUniform.isValid());
         fViewMatrix = viewMatrix;
@@ -77,6 +160,98 @@
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
+
+void GrGLGeometryProcessor::emitCode(EmitArgs& args) {
+    GrGLVertexBuilder* vsBuilder = args.fPB->getVertexShaderBuilder();
+    vsBuilder->codeAppendf("vec3 %s;", this->position());
+    this->onEmitCode(args);
+    vsBuilder->transformToNormalizedDeviceSpace(this->position());
+}
+
+void GrGLGeometryProcessor::emitTransforms(GrGLGPBuilder* pb,
+                                           const char* position,
+                                           const char* localCoords,
+                                           const SkMatrix& localMatrix,
+                                           const TransformsIn& tin,
+                                           TransformsOut* tout) {
+    GrGLVertexBuilder* vb = pb->getVertexShaderBuilder();
+    tout->push_back_n(tin.count());
+    fInstalledTransforms.push_back_n(tin.count());
+    for (int i = 0; i < tin.count(); i++) {
+        const ProcCoords& coordTransforms = tin[i];
+        fInstalledTransforms[i].push_back_n(coordTransforms.count());
+        for (int t = 0; t < coordTransforms.count(); t++) {
+            SkString strUniName("StageMatrix");
+            strUniName.appendf("_%i_%i", i, t);
+            GrSLType varyingType;
+
+            GrCoordSet coordType = coordTransforms[t]->sourceCoords();
+            uint32_t type = coordTransforms[t]->getMatrix().getType();
+            if (kLocal_GrCoordSet == coordType) {
+                type |= localMatrix.getType();
+            }
+            varyingType = SkToBool(SkMatrix::kPerspective_Mask & type) ? kVec3f_GrSLType :
+                                                                         kVec2f_GrSLType;
+            GrSLPrecision precision = coordTransforms[t]->precision();
+
+            const char* uniName;
+            fInstalledTransforms[i][t].fHandle =
+                    pb->addUniform(GrGLProgramBuilder::kVertex_Visibility,
+                                   kMat33f_GrSLType, precision,
+                                   strUniName.c_str(),
+                                   &uniName).toShaderBuilderIndex();
+
+            SkString strVaryingName("MatrixCoord");
+            strVaryingName.appendf("_%i_%i", i, t);
+
+            GrGLVertToFrag v(varyingType);
+            pb->addVarying(strVaryingName.c_str(), &v, precision);
+
+            SkASSERT(kVec2f_GrSLType == varyingType || kVec3f_GrSLType == varyingType);
+            SkNEW_APPEND_TO_TARRAY(&(*tout)[i], GrGLProcessor::TransformedCoords,
+                                   (SkString(v.fsIn()), varyingType));
+
+            // varying = matrix * coords (logically)
+            if (kDevice_GrCoordSet == coordType) {
+                if (kVec2f_GrSLType == varyingType) {
+                    vb->codeAppendf("%s = (%s * %s).xy;", v.vsOut(), uniName, position);
+                } else {
+                    vb->codeAppendf("%s = %s * %s;", v.vsOut(), uniName, position);
+                }
+            } else {
+                if (kVec2f_GrSLType == varyingType) {
+                    vb->codeAppendf("%s = (%s * vec3(%s, 1)).xy;", v.vsOut(), uniName, localCoords);
+                } else {
+                    vb->codeAppendf("%s = %s * vec3(%s, 1);", v.vsOut(), uniName, localCoords);
+                }
+            }
+        }
+    }
+}
+
+
+void
+GrGLGeometryProcessor::setTransformData(const GrPrimitiveProcessor* primProc,
+                                        const GrGLProgramDataManager& pdman,
+                                        int index,
+                                        const SkTArray<const GrCoordTransform*, true>& transforms) {
+    SkSTArray<2, Transform, true>& procTransforms = fInstalledTransforms[index];
+    int numTransforms = transforms.count();
+    for (int t = 0; t < numTransforms; ++t) {
+        SkASSERT(procTransforms[t].fHandle.isValid());
+        const SkMatrix& transform = GetTransformMatrix(primProc->localMatrix(), *transforms[t]);
+        if (!procTransforms[t].fCurrentValue.cheapEqualTo(transform)) {
+            pdman.setSkMatrix(procTransforms[t].fHandle.convertToUniformHandle(), transform);
+            procTransforms[t].fCurrentValue = transform;
+        }
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+#include "gl/GrGLGpu.h"
+#include "gl/GrGLPathRendering.h"
+
 struct PathBatchTracker {
     GrGPInput fInputColorType;
     GrGPInput fInputCoverageType;
@@ -84,58 +259,214 @@
     bool fUsesLocalCoords;
 };
 
-class GrGLPathProcessor : public GrGLGeometryProcessor {
+GrGLPathProcessor::GrGLPathProcessor(const GrPathProcessor&, const GrBatchTracker&)
+    : fColor(GrColor_ILLEGAL) {}
+
+void GrGLPathProcessor::emitCode(EmitArgs& args) {
+    GrGLGPBuilder* pb = args.fPB;
+    GrGLGPFragmentBuilder* fs = args.fPB->getFragmentShaderBuilder();
+    const PathBatchTracker& local = args.fBT.cast<PathBatchTracker>();
+
+    // emit transforms
+    this->emitTransforms(args.fPB, args.fTransformsIn, args.fTransformsOut);
+
+    // Setup uniform color
+    if (kUniform_GrGPInput == local.fInputColorType) {
+        const char* stagedLocalVarName;
+        fColorUniform = pb->addUniform(GrGLProgramBuilder::kFragment_Visibility,
+                                       kVec4f_GrSLType,
+                                       kDefault_GrSLPrecision,
+                                       "Color",
+                                       &stagedLocalVarName);
+        fs->codeAppendf("%s = %s;", args.fOutputColor, stagedLocalVarName);
+    }
+
+    // setup constant solid coverage
+    if (kAllOnes_GrGPInput == local.fInputCoverageType) {
+        fs->codeAppendf("%s = vec4(1);", args.fOutputCoverage);
+    }
+}
+
+void GrGLPathProcessor::GenKey(const GrPathProcessor&,
+                               const GrBatchTracker& bt,
+                               const GrGLCaps&,
+                               GrProcessorKeyBuilder* b) {
+    const PathBatchTracker& local = bt.cast<PathBatchTracker>();
+    b->add32(local.fInputColorType | local.fInputCoverageType << 16);
+}
+
+void GrGLPathProcessor::setData(const GrGLProgramDataManager& pdman,
+                                const GrPrimitiveProcessor& primProc,
+                                const GrBatchTracker& bt) {
+    const PathBatchTracker& local = bt.cast<PathBatchTracker>();
+    if (kUniform_GrGPInput == local.fInputColorType && local.fColor != fColor) {
+        GrGLfloat c[4];
+        GrColorToRGBAFloat(local.fColor, c);
+        pdman.set4fv(fColorUniform, 1, c);
+        fColor = local.fColor;
+    }
+}
+
+class GrGLLegacyPathProcessor : public GrGLPathProcessor {
 public:
-    GrGLPathProcessor(const GrPathProcessor&, const GrBatchTracker&)
-        : fColor(GrColor_ILLEGAL) {}
+    GrGLLegacyPathProcessor(const GrPathProcessor& pathProc, const GrBatchTracker& bt,
+                            int maxTexCoords)
+        : INHERITED(pathProc, bt)
+        , fMaxTexCoords(maxTexCoords)
+        , fTexCoordSetCnt(0) {}
 
-    void emitCode(const EmitArgs& args) SK_OVERRIDE {
-        GrGLGPBuilder* pb = args.fPB;
-        GrGLGPFragmentBuilder* fs = args.fPB->getFragmentShaderBuilder();
-        const PathBatchTracker& local = args.fBT.cast<PathBatchTracker>();
+    int addTexCoordSets(int count) {
+        int firstFreeCoordSet = fTexCoordSetCnt;
+        fTexCoordSetCnt += count;
+        SkASSERT(fMaxTexCoords >= fTexCoordSetCnt);
+        return firstFreeCoordSet;
+    }
 
-        // Setup uniform color
-        if (kUniform_GrGPInput == local.fInputColorType) {
-            const char* stagedLocalVarName;
-            fColorUniform = pb->addUniform(GrGLProgramBuilder::kFragment_Visibility,
-                                           kVec4f_GrSLType,
-                                           kDefault_GrSLPrecision,
-                                           "Color",
-                                           &stagedLocalVarName);
-            fs->codeAppendf("%s = %s;", args.fOutputColor, stagedLocalVarName);
-        }
+    void emitTransforms(GrGLGPBuilder*, const TransformsIn& tin, TransformsOut* tout) SK_OVERRIDE {
+        tout->push_back_n(tin.count());
+        fInstalledTransforms.push_back_n(tin.count());
+        for (int i = 0; i < tin.count(); i++) {
+            const ProcCoords& coordTransforms = tin[i];
+            int texCoordIndex = this->addTexCoordSets(coordTransforms.count());
 
-        // setup constant solid coverage
-        if (kAllOnes_GrGPInput == local.fInputCoverageType) {
-            fs->codeAppendf("%s = vec4(1);", args.fOutputCoverage);
+            // Use the first uniform location as the texcoord index.
+            fInstalledTransforms[i].push_back_n(1);
+            fInstalledTransforms[i][0].fHandle = ShaderVarHandle(texCoordIndex);
+
+            SkString name;
+            for (int t = 0; t < coordTransforms.count(); ++t) {
+                GrSLType type = coordTransforms[t]->getMatrix().hasPerspective() ? kVec3f_GrSLType :
+                                                                                   kVec2f_GrSLType;
+
+                name.printf("%s(gl_TexCoord[%i])", GrGLSLTypeString(type), texCoordIndex++);
+                SkNEW_APPEND_TO_TARRAY(&(*tout)[i], GrGLProcessor::TransformedCoords, (name, type));
+            }
         }
     }
 
-    static inline void GenKey(const GrPathProcessor&,
-                              const GrBatchTracker& bt,
-                              const GrGLCaps&,
-                              GrProcessorKeyBuilder* b) {
-        const PathBatchTracker& local = bt.cast<PathBatchTracker>();
-        b->add32(local.fInputColorType | local.fInputCoverageType << 16);
+    void setTransformData(const GrPrimitiveProcessor* primProc,
+                          int index,
+                          const SkTArray<const GrCoordTransform*, true>& transforms,
+                          GrGLPathRendering* glpr,
+                          GrGLuint) SK_OVERRIDE {
+        // We've hidden the texcoord index in the first entry of the transforms array for each
+        // effect
+        int texCoordIndex = fInstalledTransforms[index][0].fHandle.handle();
+        for (int t = 0; t < transforms.count(); ++t) {
+            const SkMatrix& transform = GetTransformMatrix(primProc->localMatrix(), *transforms[t]);
+            GrGLPathRendering::PathTexGenComponents components =
+                    GrGLPathRendering::kST_PathTexGenComponents;
+            if (transform.hasPerspective()) {
+                components = GrGLPathRendering::kSTR_PathTexGenComponents;
+            }
+            glpr->enablePathTexGen(texCoordIndex++, components, transform);
+        }
     }
 
-    void setData(const GrGLProgramDataManager& pdman,
-                 const GrPrimitiveProcessor& primProc,
-                 const GrBatchTracker& bt) SK_OVERRIDE {
-        const PathBatchTracker& local = bt.cast<PathBatchTracker>();
-        if (kUniform_GrGPInput == local.fInputColorType && local.fColor != fColor) {
-            GrGLfloat c[4];
-            GrColorToRGBAFloat(local.fColor, c);
-            pdman.set4fv(fColorUniform, 1, c);
-            fColor = local.fColor;
+    void didSetData(GrGLPathRendering* glpr) SK_OVERRIDE {
+        glpr->flushPathTexGenSettings(fTexCoordSetCnt);
+    }
+
+private:
+    int fMaxTexCoords;
+    int fTexCoordSetCnt;
+
+    typedef GrGLPathProcessor INHERITED;
+};
+
+class GrGLNormalPathProcessor : public GrGLPathProcessor {
+public:
+    GrGLNormalPathProcessor(const GrPathProcessor& pathProc, const GrBatchTracker& bt)
+        : INHERITED(pathProc, bt) {}
+
+    void emitTransforms(GrGLGPBuilder* pb, const TransformsIn& tin,
+                        TransformsOut* tout) SK_OVERRIDE {
+        tout->push_back_n(tin.count());
+        fInstalledTransforms.push_back_n(tin.count());
+        for (int i = 0; i < tin.count(); i++) {
+            const ProcCoords& coordTransforms = tin[i];
+            fInstalledTransforms[i].push_back_n(coordTransforms.count());
+            for (int t = 0; t < coordTransforms.count(); t++) {
+                GrSLType varyingType =
+                        coordTransforms[t]->getMatrix().hasPerspective() ? kVec3f_GrSLType :
+                                                                           kVec2f_GrSLType;
+
+                const char* varyingName = "MatrixCoord";
+                SkString suffixedVaryingName;
+                if (0 != t) {
+                    suffixedVaryingName.append(varyingName);
+                    suffixedVaryingName.appendf("_%i", t);
+                    varyingName = suffixedVaryingName.c_str();
+                }
+                GrGLVertToFrag v(varyingType);
+                pb->addVarying(varyingName, &v);
+                SeparableVaryingInfo& varyingInfo = fSeparableVaryingInfos.push_back();
+                varyingInfo.fVariable = pb->getFragmentShaderBuilder()->fInputs.back();
+                varyingInfo.fLocation = fSeparableVaryingInfos.count() - 1;
+                varyingInfo.fType = varyingType;
+                fInstalledTransforms[i][t].fHandle = ShaderVarHandle(varyingInfo.fLocation);
+                fInstalledTransforms[i][t].fType = varyingType;
+
+                SkNEW_APPEND_TO_TARRAY(&(*tout)[i], GrGLProcessor::TransformedCoords,
+                                       (SkString(v.fsIn()), varyingType));
+            }
+        }
+    }
+
+    void resolveSeparableVaryings(GrGLGpu* gpu, GrGLuint programId) {
+        int count = fSeparableVaryingInfos.count();
+        for (int i = 0; i < count; ++i) {
+            GrGLint location;
+            GR_GL_CALL_RET(gpu->glInterface(),
+                           location,
+                           GetProgramResourceLocation(programId,
+                                                      GR_GL_FRAGMENT_INPUT,
+                                                      fSeparableVaryingInfos[i].fVariable.c_str()));
+            fSeparableVaryingInfos[i].fLocation = location;
+        }
+    }
+
+    void setTransformData(const GrPrimitiveProcessor* primProc,
+                          int index,
+                          const SkTArray<const GrCoordTransform*, true>& coordTransforms,
+                          GrGLPathRendering* glpr,
+                          GrGLuint programID) SK_OVERRIDE {
+        SkSTArray<2, Transform, true>& transforms = fInstalledTransforms[index];
+        int numTransforms = transforms.count();
+        for (int t = 0; t < numTransforms; ++t) {
+            SkASSERT(transforms[t].fHandle.isValid());
+            const SkMatrix& transform = GetTransformMatrix(primProc->localMatrix(),
+                                                           *coordTransforms[t]);
+            if (transforms[t].fCurrentValue.cheapEqualTo(transform)) {
+                continue;
+            }
+            transforms[t].fCurrentValue = transform;
+            const SeparableVaryingInfo& fragmentInput =
+                    fSeparableVaryingInfos[transforms[t].fHandle.handle()];
+            SkASSERT(transforms[t].fType == kVec2f_GrSLType ||
+                     transforms[t].fType == kVec3f_GrSLType);
+            unsigned components = transforms[t].fType == kVec2f_GrSLType ? 2 : 3;
+            glpr->setProgramPathFragmentInputTransform(programID,
+                                                       fragmentInput.fLocation,
+                                                       GR_GL_OBJECT_LINEAR,
+                                                       components,
+                                                       transform);
         }
     }
 
 private:
-    UniformHandle fColorUniform;
-    GrColor fColor;
+    struct SeparableVaryingInfo {
+        GrSLType      fType;
+        GrGLShaderVar fVariable;
+        GrGLint       fLocation;
+    };
 
-    typedef GrGLGeometryProcessor INHERITED;
+
+    typedef SkSTArray<8, SeparableVaryingInfo, true> SeparableVaryingInfoArray;
+
+    SeparableVaryingInfoArray fSeparableVaryingInfos;
+
+    typedef GrGLPathProcessor INHERITED;
 };
 
 GrPathProcessor::GrPathProcessor(GrColor color,
@@ -196,6 +527,14 @@
     GrGLPathProcessor::GenKey(*this, bt, caps, b);
 }
 
-GrGLGeometryProcessor* GrPathProcessor::createGLInstance(const GrBatchTracker& bt) const {
-    return SkNEW_ARGS(GrGLPathProcessor, (*this, bt));
+GrGLPrimitiveProcessor* GrPathProcessor::createGLInstance(const GrBatchTracker& bt,
+                                                          const GrGLCaps& caps) const {
+    SkASSERT(caps.nvprSupport() != GrGLCaps::kNone_NvprSupport);
+    if (caps.nvprSupport() == GrGLCaps::kLegacy_NvprSupport) {
+        return SkNEW_ARGS(GrGLLegacyPathProcessor, (*this, bt,
+                                                    caps.maxFixedFunctionTextureCoords()));
+    } else {
+        return SkNEW_ARGS(GrGLNormalPathProcessor, (*this, bt));
+    }
 }
+
diff --git a/src/gpu/GrGeometryProcessor.h b/src/gpu/GrGeometryProcessor.h
index ad75d23..074fffc 100644
--- a/src/gpu/GrGeometryProcessor.h
+++ b/src/gpu/GrGeometryProcessor.h
@@ -66,7 +66,7 @@
 };
 
 class GrGLCaps;
-class GrGLGeometryProcessor;
+class GrGLPrimitiveProcessor;
 class GrOptDrawState;
 
 struct GrInitInvariantOutput;
@@ -122,6 +122,11 @@
     virtual void getInvariantOutputCoverage(GrInitInvariantOutput* out) const = 0;
 
     /**
+     * Gets a transformKey from an array of coord transforms
+     */
+    uint32_t getTransformKey(const SkTArray<const GrCoordTransform*, true>&) const;
+
+    /**
      * Sets a unique key on the GrProcessorKeyBuilder that is directly associated with this geometry
      * processor's GL backend implementation.
      */
@@ -133,7 +138,8 @@
     /** Returns a new instance of the appropriate *GL* implementation class
         for the given GrProcessor; caller is responsible for deleting
         the object. */
-    virtual GrGLGeometryProcessor* createGLInstance(const GrBatchTracker& bt) const = 0;
+    virtual GrGLPrimitiveProcessor* createGLInstance(const GrBatchTracker& bt,
+                                                     const GrGLCaps& caps) const = 0;
 
 protected:
     GrPrimitiveProcessor(const SkMatrix& viewMatrix, const SkMatrix& localMatrix)
@@ -171,6 +177,8 @@
     }
 
 private:
+    virtual bool hasExplicitLocalCoords() const = 0;
+
     SkMatrix fViewMatrix;
     SkMatrix fLocalMatrix;
 
@@ -261,7 +269,6 @@
 
         return this->onCanMakeEqual(mine, other, theirs);
     }
-
     
     // TODO we can remove color from the GrGeometryProcessor base class once we have bundles of
     // primitive data
@@ -270,9 +277,6 @@
     // TODO this is a total hack until the gp can do deferred geometry
     bool hasVertexColor() const { return fHasVertexColor; }
 
-    // TODO this is a total hack until gp can setup and manage local coords
-    bool hasLocalCoords() const { return fHasLocalCoords; }
-
     void getInvariantOutputColor(GrInitInvariantOutput* out) const SK_OVERRIDE;
     void getInvariantOutputCoverage(GrInitInvariantOutput* out) const SK_OVERRIDE;
 
@@ -332,9 +336,12 @@
     virtual bool onCanMakeEqual(const GrBatchTracker& mine,
                                 const GrGeometryProcessor& that,
                                 const GrBatchTracker& theirs) const = 0;
+
     // TODO delete this when we have more advanced equality testing via bundles and the BT
     virtual bool onIsEqual(const GrGeometryProcessor&) const = 0;
 
+    bool hasExplicitLocalCoords() const SK_OVERRIDE { return fHasLocalCoords; }
+
     SkSTArray<kMaxVertexAttribs, GrAttribute, true> fAttribs;
     size_t fVertexStride;
     GrColor fColor;
@@ -375,12 +382,18 @@
                                    const GrGLCaps& caps,
                                    GrProcessorKeyBuilder* b) const SK_OVERRIDE;
 
-    GrGLGeometryProcessor* createGLInstance(const GrBatchTracker& bt) const SK_OVERRIDE;
+    virtual GrGLPrimitiveProcessor* createGLInstance(const GrBatchTracker& bt,
+                                                     const GrGLCaps& caps) const SK_OVERRIDE;
+
+protected:
+    GrPathProcessor(GrColor color, const SkMatrix& viewMatrix, const SkMatrix& localMatrix);
 
 private:
-    GrPathProcessor(GrColor color, const SkMatrix& viewMatrix, const SkMatrix& localMatrix);
+    bool hasExplicitLocalCoords() const SK_OVERRIDE { return false; }
+
     GrColor fColor;
 
     typedef GrPrimitiveProcessor INHERITED;
 };
+
 #endif
diff --git a/src/gpu/GrOptDrawState.cpp b/src/gpu/GrOptDrawState.cpp
index 2365f1e..f9febf4 100644
--- a/src/gpu/GrOptDrawState.cpp
+++ b/src/gpu/GrOptDrawState.cpp
@@ -87,9 +87,6 @@
         fFlags |= kDither_Flag;
     }
 
-    // TODO move local coords completely into GP
-    bool hasLocalCoords = gp && gp->hasLocalCoords();
-
     int firstColorStageIdx = colorPOI.firstEffectiveStageIndex();
 
     // TODO: Once we can handle single or four channel input into coverage stages then we can use
@@ -102,8 +99,6 @@
     this->adjustProgramFromOptimizations(drawState, optFlags, colorPOI, coveragePOI,
                                          &firstColorStageIdx, &firstCoverageStageIdx);
 
-    fDescInfo.fRequiresLocalCoordAttrib = hasLocalCoords;
-
     bool usesLocalCoords = false;
 
     // Copy Stages from DS to ODS
diff --git a/src/gpu/GrOvalRenderer.cpp b/src/gpu/GrOvalRenderer.cpp
index 7f93bbe..b5f3f5a 100644
--- a/src/gpu/GrOvalRenderer.cpp
+++ b/src/gpu/GrOvalRenderer.cpp
@@ -83,12 +83,15 @@
                     const GrBatchTracker&)
             : fColor(GrColor_ILLEGAL) {}
 
-        void emitCode(const EmitArgs& args) SK_OVERRIDE {
+        void onEmitCode(EmitArgs& args) SK_OVERRIDE {
             const CircleEdgeEffect& ce = args.fGP.cast<CircleEdgeEffect>();
             GrGLGPBuilder* pb = args.fPB;
             const BatchTracker& local = args.fBT.cast<BatchTracker>();
             GrGLVertexBuilder* vsBuilder = args.fPB->getVertexShaderBuilder();
 
+            // emit attributes
+            vsBuilder->emitAttributes(ce);
+
             GrGLVertToFrag v(kVec4f_GrSLType);
             args.fPB->addVarying("CircleEdge", &v);
             vsBuilder->codeAppendf("%s = %s;", v.vsOut(), ce.inCircleEdge()->fName);
@@ -97,16 +100,16 @@
             this->setupColorPassThrough(pb, local.fInputColorType, args.fOutputColor, NULL,
                                         &fColorUniform);
 
-            // setup coord outputs
-            vsBuilder->codeAppendf("%s = %s;", vsBuilder->positionCoords(), ce.inPosition()->fName);
-            vsBuilder->codeAppendf("%s = %s;", vsBuilder->localCoords(), ce.inPosition()->fName);
-
             // setup uniform viewMatrix
             this->addUniformViewMatrix(pb);
 
-            // setup position varying
-            vsBuilder->codeAppendf("%s = %s * vec3(%s, 1);", vsBuilder->glPosition(),
-                                   this->uViewM(), ce.inPosition()->fName);
+            // Setup position
+            vsBuilder->codeAppendf("%s = %s * vec3(%s, 1);",  this->position(), this->uViewM(),
+                                   ce.inPosition()->fName);
+
+            // emit transforms
+            this->emitTransforms(args.fPB,  this->position(), ce.inPosition()->fName,
+                                 ce.localMatrix(), args.fTransformsIn, args.fTransformsOut);;
 
             GrGLGPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
             fsBuilder->codeAppendf("float d = length(%s.xy);", v.fsIn());
@@ -157,7 +160,8 @@
         GLProcessor::GenKey(*this, bt, caps, b);
     }
 
-    GrGLGeometryProcessor* createGLInstance(const GrBatchTracker& bt) const SK_OVERRIDE {
+    virtual GrGLPrimitiveProcessor* createGLInstance(const GrBatchTracker& bt,
+                                                     const GrGLCaps&) const SK_OVERRIDE {
         return SkNEW_ARGS(GLProcessor, (*this, bt));
     }
 
@@ -255,12 +259,15 @@
                     const GrBatchTracker&)
             : fColor(GrColor_ILLEGAL) {}
 
-        void emitCode(const EmitArgs& args) SK_OVERRIDE {
+        void onEmitCode(EmitArgs& args) SK_OVERRIDE {
             const EllipseEdgeEffect& ee = args.fGP.cast<EllipseEdgeEffect>();
             GrGLGPBuilder* pb = args.fPB;
             const BatchTracker& local = args.fBT.cast<BatchTracker>();
             GrGLVertexBuilder* vsBuilder = args.fPB->getVertexShaderBuilder();
 
+            // emit attributes
+            vsBuilder->emitAttributes(ee);
+
             GrGLVertToFrag ellipseOffsets(kVec2f_GrSLType);
             args.fPB->addVarying("EllipseOffsets", &ellipseOffsets);
             vsBuilder->codeAppendf("%s = %s;", ellipseOffsets.vsOut(),
@@ -275,16 +282,16 @@
             this->setupColorPassThrough(pb, local.fInputColorType, args.fOutputColor, NULL,
                                         &fColorUniform);
 
-            // setup coord outputs
-            vsBuilder->codeAppendf("%s = %s;", vsBuilder->positionCoords(), ee.inPosition()->fName);
-            vsBuilder->codeAppendf("%s = %s;", vsBuilder->localCoords(), ee.inPosition()->fName);
-
             // setup uniform viewMatrix
             this->addUniformViewMatrix(pb);
 
-            // setup position varying
-            vsBuilder->codeAppendf("%s = %s * vec3(%s, 1);", vsBuilder->glPosition(),
-                                   this->uViewM(), ee.inPosition()->fName);
+            // Setup position
+            vsBuilder->codeAppendf("%s = %s * vec3(%s, 1);", this->position(), this->uViewM(),
+                                   ee.inPosition()->fName);
+
+            // emit transforms
+            this->emitTransforms(args.fPB, this->position(), ee.inPosition()->fName,
+                                 ee.localMatrix(), args.fTransformsIn, args.fTransformsOut);
 
             // for outer curve
             GrGLGPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
@@ -351,7 +358,8 @@
         GLProcessor::GenKey(*this, bt, caps, b);
     }
 
-    GrGLGeometryProcessor* createGLInstance(const GrBatchTracker& bt) const SK_OVERRIDE {
+    virtual GrGLPrimitiveProcessor* createGLInstance(const GrBatchTracker& bt,
+                                                     const GrGLCaps&) const SK_OVERRIDE {
         return SkNEW_ARGS(GLProcessor, (*this, bt));
     }
 
@@ -455,12 +463,15 @@
                     const GrBatchTracker&)
             : fColor(GrColor_ILLEGAL) {}
 
-        void emitCode(const EmitArgs& args) SK_OVERRIDE {
+        void onEmitCode(EmitArgs& args) SK_OVERRIDE {
             const DIEllipseEdgeEffect& ee = args.fGP.cast<DIEllipseEdgeEffect>();
             GrGLGPBuilder* pb = args.fPB;
             const BatchTracker& local = args.fBT.cast<BatchTracker>();
             GrGLVertexBuilder* vsBuilder = args.fPB->getVertexShaderBuilder();
 
+            // emit attributes
+            vsBuilder->emitAttributes(ee);
+
             GrGLVertToFrag offsets0(kVec2f_GrSLType);
             args.fPB->addVarying("EllipseOffsets0", &offsets0);
             vsBuilder->codeAppendf("%s = %s;", offsets0.vsOut(),
@@ -475,16 +486,16 @@
             this->setupColorPassThrough(pb, local.fInputColorType, args.fOutputColor, NULL,
                                         &fColorUniform);
 
-            // setup coord outputs
-            vsBuilder->codeAppendf("%s = %s;", vsBuilder->positionCoords(), ee.inPosition()->fName);
-            vsBuilder->codeAppendf("%s = %s;", vsBuilder->localCoords(), ee.inPosition()->fName);
-
             // setup uniform viewMatrix
             this->addUniformViewMatrix(pb);
 
-            // setup position varying
-            vsBuilder->codeAppendf("%s = %s * vec3(%s, 1);", vsBuilder->glPosition(),
-                                   this->uViewM(), ee.inPosition()->fName);
+            // Setup position
+            vsBuilder->codeAppendf("%s = %s * vec3(%s, 1);", this->position(), this->uViewM(),
+                                   ee.inPosition()->fName);
+
+            // emit transforms
+            this->emitTransforms(args.fPB, this->position(), ee.inPosition()->fName,
+                                 ee.localMatrix(), args.fTransformsIn, args.fTransformsOut);
 
             GrGLGPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
             SkAssertResult(fsBuilder->enableFeature(
@@ -566,7 +577,8 @@
         GLProcessor::GenKey(*this, bt, caps, b);
     }
 
-    GrGLGeometryProcessor* createGLInstance(const GrBatchTracker& bt) const SK_OVERRIDE {
+    virtual GrGLPrimitiveProcessor* createGLInstance(const GrBatchTracker& bt,
+                                                     const GrGLCaps&) const SK_OVERRIDE {
         return SkNEW_ARGS(GLProcessor, (*this, bt));
     }
 
diff --git a/src/gpu/GrProgramDesc.h b/src/gpu/GrProgramDesc.h
index 9ba20aa..e07e116 100644
--- a/src/gpu/GrProgramDesc.h
+++ b/src/gpu/GrProgramDesc.h
@@ -83,8 +83,7 @@
     struct DescInfo {
         bool operator==(const DescInfo& that) const {
             return fReadsDst == that.fReadsDst &&
-                   fReadsFragPosition == that.fReadsFragPosition &&
-                   fRequiresLocalCoordAttrib == that.fRequiresLocalCoordAttrib;
+                   fReadsFragPosition == that.fReadsFragPosition;
         }
         bool operator!=(const DescInfo& that) const { return !(*this == that); };
 
@@ -92,7 +91,6 @@
         // programs.
         bool            fReadsDst;
         bool            fReadsFragPosition;
-        bool            fRequiresLocalCoordAttrib;
     };
 
 private:
diff --git a/src/gpu/effects/GrBezierEffect.cpp b/src/gpu/effects/GrBezierEffect.cpp
index e16601d..f674345 100644
--- a/src/gpu/effects/GrBezierEffect.cpp
+++ b/src/gpu/effects/GrBezierEffect.cpp
@@ -24,7 +24,7 @@
     GrGLConicEffect(const GrGeometryProcessor&,
                     const GrBatchTracker&);
 
-    void emitCode(const EmitArgs&) SK_OVERRIDE;
+    void onEmitCode(EmitArgs&) SK_OVERRIDE;
 
     static inline void GenKey(const GrGeometryProcessor&,
                               const GrBatchTracker&,
@@ -66,12 +66,15 @@
     fEdgeType = ce.getEdgeType();
 }
 
-void GrGLConicEffect::emitCode(const EmitArgs& args) {
+void GrGLConicEffect::onEmitCode(EmitArgs& args) {
     GrGLGPBuilder* pb = args.fPB;
     GrGLVertexBuilder* vsBuilder = args.fPB->getVertexShaderBuilder();
     const GrConicEffect& gp = args.fGP.cast<GrConicEffect>();
     const ConicBatchTracker& local = args.fBT.cast<ConicBatchTracker>();
 
+    // emit attributes
+    vsBuilder->emitAttributes(gp);
+
     GrGLVertToFrag v(kVec4f_GrSLType);
     args.fPB->addVarying("ConicCoeffs", &v);
     vsBuilder->codeAppendf("%s = %s;", v.vsOut(), gp.inConicCoeffs()->fName);
@@ -80,17 +83,17 @@
     this->setupColorPassThrough(args.fPB, local.fInputColorType, args.fOutputColor, NULL,
                                 &fColorUniform);
 
-    // setup coord outputs
-    vsBuilder->codeAppendf("%s = %s;", vsBuilder->positionCoords(), gp.inPosition()->fName);
-    vsBuilder->codeAppendf("%s = %s;", vsBuilder->localCoords(), gp.inPosition()->fName);
-
     // setup uniform viewMatrix
     this->addUniformViewMatrix(pb);
 
-    // setup position varying
-    vsBuilder->codeAppendf("%s = %s * vec3(%s, 1);", vsBuilder->glPosition(), this->uViewM(),
+    // Setup position
+    vsBuilder->codeAppendf("%s = %s * vec3(%s, 1);", this->position(), this->uViewM(),
                            gp.inPosition()->fName);
 
+    // emit transforms with position
+    this->emitTransforms(pb, this->position(), gp.inPosition()->fName, gp.localMatrix(),
+                         args.fTransformsIn, args.fTransformsOut);
+
     GrGLGPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
     fsBuilder->codeAppend("float edgeAlpha;");
 
@@ -184,7 +187,8 @@
     GrGLConicEffect::GenKey(*this, bt, caps, b);
 }
 
-GrGLGeometryProcessor* GrConicEffect::createGLInstance(const GrBatchTracker& bt) const {
+GrGLPrimitiveProcessor* GrConicEffect::createGLInstance(const GrBatchTracker& bt,
+                                                        const GrGLCaps&) const {
     return SkNEW_ARGS(GrGLConicEffect, (*this, bt));
 }
 
@@ -258,7 +262,7 @@
     GrGLQuadEffect(const GrGeometryProcessor&,
                    const GrBatchTracker&);
 
-    void emitCode(const EmitArgs&) SK_OVERRIDE;
+    void onEmitCode(EmitArgs&) SK_OVERRIDE;
 
     static inline void GenKey(const GrGeometryProcessor&,
                               const GrBatchTracker&,
@@ -300,12 +304,15 @@
     fEdgeType = ce.getEdgeType();
 }
 
-void GrGLQuadEffect::emitCode(const EmitArgs& args) {
+void GrGLQuadEffect::onEmitCode(EmitArgs& args) {
     GrGLGPBuilder* pb = args.fPB;
     GrGLVertexBuilder* vsBuilder = args.fPB->getVertexShaderBuilder();
     const GrQuadEffect& gp = args.fGP.cast<GrQuadEffect>();
     const QuadBatchTracker& local = args.fBT.cast<QuadBatchTracker>();
 
+    // emit attributes
+    vsBuilder->emitAttributes(gp);
+
     GrGLVertToFrag v(kVec4f_GrSLType);
     args.fPB->addVarying("HairQuadEdge", &v);
     vsBuilder->codeAppendf("%s = %s;", v.vsOut(), gp.inHairQuadEdge()->fName);
@@ -314,17 +321,17 @@
     this->setupColorPassThrough(args.fPB, local.fInputColorType, args.fOutputColor, NULL,
                                 &fColorUniform);
 
-    // setup coord outputs
-    vsBuilder->codeAppendf("%s = %s;", vsBuilder->positionCoords(), gp.inPosition()->fName);
-    vsBuilder->codeAppendf("%s = %s;", vsBuilder->localCoords(), gp.inPosition()->fName);
-
     // setup uniform viewMatrix
     this->addUniformViewMatrix(pb);
 
-    // setup position varying
-    vsBuilder->codeAppendf("%s = %s * vec3(%s, 1);", vsBuilder->glPosition(), this->uViewM(),
+    // Setup position
+    vsBuilder->codeAppendf("%s = %s * vec3(%s, 1);", this->position(), this->uViewM(),
                            gp.inPosition()->fName);
 
+    // emit transforms with position
+    this->emitTransforms(pb, this->position(), gp.inPosition()->fName, gp.localMatrix(),
+                         args.fTransformsIn, args.fTransformsOut);
+
     GrGLGPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
     fsBuilder->codeAppendf("float edgeAlpha;");
 
@@ -404,7 +411,8 @@
     GrGLQuadEffect::GenKey(*this, bt, caps, b);
 }
 
-GrGLGeometryProcessor* GrQuadEffect::createGLInstance(const GrBatchTracker& bt) const {
+GrGLPrimitiveProcessor* GrQuadEffect::createGLInstance(const GrBatchTracker& bt,
+                                                       const GrGLCaps&) const {
     return SkNEW_ARGS(GrGLQuadEffect, (*this, bt));
 }
 
@@ -478,7 +486,7 @@
     GrGLCubicEffect(const GrGeometryProcessor&,
                     const GrBatchTracker&);
 
-    void emitCode(const EmitArgs&) SK_OVERRIDE;
+    void onEmitCode(EmitArgs&) SK_OVERRIDE;
 
     static inline void GenKey(const GrGeometryProcessor&,
                               const GrBatchTracker&,
@@ -514,11 +522,14 @@
     fEdgeType = ce.getEdgeType();
 }
 
-void GrGLCubicEffect::emitCode(const EmitArgs& args) {
+void GrGLCubicEffect::onEmitCode(EmitArgs& args) {
     GrGLVertexBuilder* vsBuilder = args.fPB->getVertexShaderBuilder();
     const GrCubicEffect& gp = args.fGP.cast<GrCubicEffect>();
     const CubicBatchTracker& local = args.fBT.cast<CubicBatchTracker>();
 
+    // emit attributes
+    vsBuilder->emitAttributes(gp);
+
     GrGLVertToFrag v(kVec4f_GrSLType);
     args.fPB->addVarying("CubicCoeffs", &v, kHigh_GrSLPrecision);
     vsBuilder->codeAppendf("%s = %s;", v.vsOut(), gp.inCubicCoeffs()->fName);
@@ -527,17 +538,17 @@
     this->setupColorPassThrough(args.fPB, local.fInputColorType, args.fOutputColor, NULL,
                                 &fColorUniform);
 
-    // setup coord outputs
-    vsBuilder->codeAppendf("%s = %s;", vsBuilder->positionCoords(), gp.inPosition()->fName);
-    vsBuilder->codeAppendf("%s = %s;", vsBuilder->localCoords(), gp.inPosition()->fName);
-
     // setup uniform viewMatrix
     this->addUniformViewMatrix(args.fPB);
 
-    // setup position varying
-    vsBuilder->codeAppendf("%s = %s * vec3(%s, 1);", vsBuilder->glPosition(), this->uViewM(),
+    // Setup position
+    vsBuilder->codeAppendf("%s = %s * vec3(%s, 1);", this->position(), this->uViewM(),
                            gp.inPosition()->fName);
 
+    // emit transforms with position
+    this->emitTransforms(args.fPB, this->position(), gp.inPosition()->fName, gp.localMatrix(),
+                         args.fTransformsIn, args.fTransformsOut);
+
     GrGLGPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
 
     GrGLShaderVar edgeAlpha("edgeAlpha", kFloat_GrSLType, 0, kHigh_GrSLPrecision);
@@ -647,7 +658,8 @@
     GrGLCubicEffect::GenKey(*this, bt, caps, b);
 }
 
-GrGLGeometryProcessor* GrCubicEffect::createGLInstance(const GrBatchTracker& bt) const {
+GrGLPrimitiveProcessor* GrCubicEffect::createGLInstance(const GrBatchTracker& bt,
+                                                        const GrGLCaps&) const {
     return SkNEW_ARGS(GrGLCubicEffect, (*this, bt));
 }
 
diff --git a/src/gpu/effects/GrBezierEffect.h b/src/gpu/effects/GrBezierEffect.h
index f31542f..86f09bc 100644
--- a/src/gpu/effects/GrBezierEffect.h
+++ b/src/gpu/effects/GrBezierEffect.h
@@ -102,7 +102,8 @@
                                    const GrGLCaps& caps,
                                    GrProcessorKeyBuilder* b) const SK_OVERRIDE;
 
-    GrGLGeometryProcessor* createGLInstance(const GrBatchTracker& bt) const SK_OVERRIDE;
+    virtual GrGLPrimitiveProcessor* createGLInstance(const GrBatchTracker& bt,
+                                                     const GrGLCaps&) const SK_OVERRIDE;
 
     void initBatchTracker(GrBatchTracker*, const InitBT&) const SK_OVERRIDE;
     bool onCanMakeEqual(const GrBatchTracker&,
@@ -186,7 +187,8 @@
                                    const GrGLCaps& caps,
                                    GrProcessorKeyBuilder* b) const SK_OVERRIDE;
 
-    GrGLGeometryProcessor* createGLInstance(const GrBatchTracker& bt) const SK_OVERRIDE;
+    virtual GrGLPrimitiveProcessor* createGLInstance(const GrBatchTracker& bt,
+                                                     const GrGLCaps&) const SK_OVERRIDE;
 
     void initBatchTracker(GrBatchTracker*, const InitBT&) const SK_OVERRIDE;
     bool onCanMakeEqual(const GrBatchTracker&,
@@ -266,7 +268,8 @@
                                    const GrGLCaps& caps,
                                    GrProcessorKeyBuilder* b) const SK_OVERRIDE;
 
-    GrGLGeometryProcessor* createGLInstance(const GrBatchTracker& bt) const SK_OVERRIDE;
+    virtual GrGLPrimitiveProcessor* createGLInstance(const GrBatchTracker& bt,
+                                                     const GrGLCaps&) const SK_OVERRIDE;
 
     void initBatchTracker(GrBatchTracker*, const InitBT&) const SK_OVERRIDE;
     bool onCanMakeEqual(const GrBatchTracker&,
diff --git a/src/gpu/effects/GrBitmapTextGeoProc.cpp b/src/gpu/effects/GrBitmapTextGeoProc.cpp
index 1cfb644..0818fc0 100644
--- a/src/gpu/effects/GrBitmapTextGeoProc.cpp
+++ b/src/gpu/effects/GrBitmapTextGeoProc.cpp
@@ -25,13 +25,16 @@
     GrGLBitmapTextGeoProc(const GrGeometryProcessor&, const GrBatchTracker&)
         : fColor(GrColor_ILLEGAL) {}
 
-    void emitCode(const EmitArgs& args) SK_OVERRIDE {
+    void onEmitCode(EmitArgs& args) SK_OVERRIDE {
         const GrBitmapTextGeoProc& cte = args.fGP.cast<GrBitmapTextGeoProc>();
         const BitmapTextBatchTracker& local = args.fBT.cast<BitmapTextBatchTracker>();
 
         GrGLGPBuilder* pb = args.fPB;
         GrGLVertexBuilder* vsBuilder = pb->getVertexShaderBuilder();
 
+        // emit attributes
+        vsBuilder->emitAttributes(cte);
+
         GrGLVertToFrag v(kVec2f_GrSLType);
         pb->addVarying("TextureCoords", &v);
         vsBuilder->codeAppendf("%s = %s;", v.vsOut(), cte.inTextureCoords()->fName);
@@ -40,17 +43,17 @@
         this->setupColorPassThrough(pb, local.fInputColorType, args.fOutputColor, cte.inColor(),
                                     &fColorUniform);
 
-        // setup output coords
-        vsBuilder->codeAppendf("%s = %s;", vsBuilder->positionCoords(), cte.inPosition()->fName);
-        vsBuilder->codeAppendf("%s = %s;", vsBuilder->localCoords(), cte.inPosition()->fName);
-
         // setup uniform viewMatrix
         this->addUniformViewMatrix(pb);
 
-        // setup position varying
-        vsBuilder->codeAppendf("%s = %s * vec3(%s, 1);", vsBuilder->glPosition(), this->uViewM(),
+        // Setup position
+        vsBuilder->codeAppendf("%s = %s * vec3(%s, 1);",  this->position(), this->uViewM(),
                                cte.inPosition()->fName);
 
+        // emit transforms
+        this->emitTransforms(args.fPB,  this->position(), cte.inPosition()->fName,
+                             cte.localMatrix(), args.fTransformsIn, args.fTransformsOut);
+
         GrGLGPFragmentBuilder* fsBuilder = pb->getFragmentShaderBuilder();
         fsBuilder->codeAppendf("%s = ", args.fOutputCoverage);
         fsBuilder->appendTextureLookup(args.fSamplers[0], v.fsIn(), kVec2f_GrSLType);
@@ -135,8 +138,9 @@
     GrGLBitmapTextGeoProc::GenKey(*this, bt, caps, b);
 }
 
-GrGLGeometryProcessor*
-GrBitmapTextGeoProc::createGLInstance(const GrBatchTracker& bt) const {
+GrGLPrimitiveProcessor*
+GrBitmapTextGeoProc::createGLInstance(const GrBatchTracker& bt,
+                                      const GrGLCaps& caps) const {
     return SkNEW_ARGS(GrGLBitmapTextGeoProc, (*this, bt));
 }
 
diff --git a/src/gpu/effects/GrBitmapTextGeoProc.h b/src/gpu/effects/GrBitmapTextGeoProc.h
index 85e4f42..399678a 100644
--- a/src/gpu/effects/GrBitmapTextGeoProc.h
+++ b/src/gpu/effects/GrBitmapTextGeoProc.h
@@ -40,7 +40,8 @@
                                    const GrGLCaps& caps,
                                    GrProcessorKeyBuilder* b) const SK_OVERRIDE;
 
-    GrGLGeometryProcessor* createGLInstance(const GrBatchTracker& bt) const SK_OVERRIDE;
+    virtual GrGLPrimitiveProcessor* createGLInstance(const GrBatchTracker& bt,
+                                                     const GrGLCaps& caps) const SK_OVERRIDE;
 
     void initBatchTracker(GrBatchTracker*, const InitBT&) const SK_OVERRIDE;
     bool onCanMakeEqual(const GrBatchTracker&,
diff --git a/src/gpu/effects/GrDashingEffect.cpp b/src/gpu/effects/GrDashingEffect.cpp
index 8b69dca..2543145 100644
--- a/src/gpu/effects/GrDashingEffect.cpp
+++ b/src/gpu/effects/GrDashingEffect.cpp
@@ -496,7 +496,8 @@
                                    const GrGLCaps&,
                                    GrProcessorKeyBuilder* b) const SK_OVERRIDE;
 
-    GrGLGeometryProcessor* createGLInstance(const GrBatchTracker&) const SK_OVERRIDE;
+    virtual GrGLPrimitiveProcessor* createGLInstance(const GrBatchTracker&,
+                                                     const GrGLCaps&) const SK_OVERRIDE;
 
     void initBatchTracker(GrBatchTracker* bt, const InitBT& init) const SK_OVERRIDE;
 
@@ -530,7 +531,7 @@
 public:
     GLDashingCircleEffect(const GrGeometryProcessor&, const GrBatchTracker&);
 
-    void emitCode(const EmitArgs&) SK_OVERRIDE;
+    void onEmitCode(EmitArgs&) SK_OVERRIDE;
 
     static inline void GenKey(const GrGeometryProcessor&,
                               const GrBatchTracker&,
@@ -559,7 +560,7 @@
     fPrevIntervalLength = SK_ScalarMax;
 }
 
-void GLDashingCircleEffect::emitCode(const EmitArgs& args) {
+void GLDashingCircleEffect::onEmitCode(EmitArgs& args) {
     const DashingCircleEffect& dce = args.fGP.cast<DashingCircleEffect>();
     const DashingCircleBatchTracker local = args.fBT.cast<DashingCircleBatchTracker>();
     GrGLGPBuilder* pb = args.fPB;
@@ -572,6 +573,9 @@
 
     GrGLVertexBuilder* vsBuilder = args.fPB->getVertexShaderBuilder();
 
+    // emit attributes
+    vsBuilder->emitAttributes(dce);
+
     GrGLVertToFrag v(kVec2f_GrSLType);
     args.fPB->addVarying("Coord", &v);
     vsBuilder->codeAppendf("%s = %s;", v.vsOut(), dce.inCoord()->fName);
@@ -579,17 +583,17 @@
     // Setup pass through color
     this->setupColorPassThrough(pb, local.fInputColorType, args.fOutputColor, NULL, &fColorUniform);
 
-    // setup coord outputs
-    vsBuilder->codeAppendf("%s = %s;", vsBuilder->positionCoords(), dce.inPosition()->fName);
-    vsBuilder->codeAppendf("%s = %s;", vsBuilder->localCoords(), dce.inPosition()->fName);
-
     // setup uniform viewMatrix
     this->addUniformViewMatrix(pb);
 
-    // setup position varying
-    vsBuilder->codeAppendf("%s = %s * vec3(%s, 1);", vsBuilder->glPosition(), this->uViewM(),
+    // Setup position
+    vsBuilder->codeAppendf("%s = %s * vec3(%s, 1);",  this->position(), this->uViewM(),
                            dce.inPosition()->fName);
 
+    // emit transforms
+    this->emitTransforms(args.fPB,  this->position(), dce.inPosition()->fName, dce.localMatrix(),
+                         args.fTransformsIn, args.fTransformsOut);
+
     // transforms all points so that we can compare them to our test circle
     GrGLGPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
     fsBuilder->codeAppendf("\t\tfloat xShifted = %s.x - floor(%s.x / %s.z) * %s.z;\n",
@@ -669,7 +673,8 @@
     GLDashingCircleEffect::GenKey(*this, bt, caps, b);
 }
 
-GrGLGeometryProcessor* DashingCircleEffect::createGLInstance(const GrBatchTracker& bt) const {
+GrGLPrimitiveProcessor* DashingCircleEffect::createGLInstance(const GrBatchTracker& bt,
+                                                              const GrGLCaps&) const {
     return SkNEW_ARGS(GLDashingCircleEffect, (*this, bt));
 }
 
@@ -783,7 +788,8 @@
                                    const GrGLCaps& caps,
                                    GrProcessorKeyBuilder* b) const SK_OVERRIDE;
 
-    GrGLGeometryProcessor* createGLInstance(const GrBatchTracker& bt) const SK_OVERRIDE;
+    virtual GrGLPrimitiveProcessor* createGLInstance(const GrBatchTracker& bt,
+                                                     const GrGLCaps&) const SK_OVERRIDE;
 
     void initBatchTracker(GrBatchTracker* bt, const InitBT& init) const SK_OVERRIDE;
 
@@ -816,7 +822,7 @@
 public:
     GLDashingLineEffect(const GrGeometryProcessor&, const GrBatchTracker&);
 
-    void emitCode(const EmitArgs&) SK_OVERRIDE;
+    void onEmitCode(EmitArgs&) SK_OVERRIDE;
 
     static inline void GenKey(const GrGeometryProcessor&,
                               const GrBatchTracker&,
@@ -844,7 +850,7 @@
     fPrevIntervalLength = SK_ScalarMax;
 }
 
-void GLDashingLineEffect::emitCode(const EmitArgs& args) {
+void GLDashingLineEffect::onEmitCode(EmitArgs& args) {
     const DashingLineEffect& de = args.fGP.cast<DashingLineEffect>();
     const DashingLineBatchTracker& local = args.fBT.cast<DashingLineBatchTracker>();
     GrGLGPBuilder* pb = args.fPB;
@@ -865,6 +871,9 @@
 
     GrGLVertexBuilder* vsBuilder = args.fPB->getVertexShaderBuilder();
 
+    // emit attributes
+    vsBuilder->emitAttributes(de);
+
     GrGLVertToFrag v(kVec2f_GrSLType);
     args.fPB->addVarying("Coord", &v);
     vsBuilder->codeAppendf("%s = %s;", v.vsOut(), de.inCoord()->fName);
@@ -872,17 +881,17 @@
     // Setup pass through color
     this->setupColorPassThrough(pb, local.fInputColorType, args.fOutputColor, NULL, &fColorUniform);
 
-    // setup coord outputs
-    vsBuilder->codeAppendf("%s = %s;", vsBuilder->positionCoords(), de.inPosition()->fName);
-    vsBuilder->codeAppendf("%s = %s;", vsBuilder->localCoords(), de.inPosition()->fName);
-
     // setup uniform viewMatrix
     this->addUniformViewMatrix(pb);
 
-    // setup position varying
-    vsBuilder->codeAppendf("%s = %s * vec3(%s, 1);", vsBuilder->glPosition(), this->uViewM(),
+    // Setup position
+    vsBuilder->codeAppendf("%s = %s * vec3(%s, 1);",  this->position(), this->uViewM(),
                            de.inPosition()->fName);
 
+    // emit transforms
+    this->emitTransforms(args.fPB,  this->position(), de.inPosition()->fName, de.localMatrix(),
+                         args.fTransformsIn, args.fTransformsOut);
+
     // transforms all points so that we can compare them to our test rect
     GrGLGPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
     fsBuilder->codeAppendf("\t\tfloat xShifted = %s.x - floor(%s.x / %s) * %s;\n",
@@ -969,7 +978,8 @@
     GLDashingLineEffect::GenKey(*this, bt, caps, b);
 }
 
-GrGLGeometryProcessor* DashingLineEffect::createGLInstance(const GrBatchTracker& bt) const {
+GrGLPrimitiveProcessor* DashingLineEffect::createGLInstance(const GrBatchTracker& bt,
+                                                            const GrGLCaps&) const {
     return SkNEW_ARGS(GLDashingLineEffect, (*this, bt));
 }
 
diff --git a/src/gpu/effects/GrDistanceFieldTextureEffect.cpp b/src/gpu/effects/GrDistanceFieldTextureEffect.cpp
index cdb6ff7..24fe001 100755
--- a/src/gpu/effects/GrDistanceFieldTextureEffect.cpp
+++ b/src/gpu/effects/GrDistanceFieldTextureEffect.cpp
@@ -35,7 +35,7 @@
 #endif
         {}
 
-    void emitCode(const EmitArgs& args) SK_OVERRIDE {
+    void onEmitCode(EmitArgs& args) SK_OVERRIDE {
         const GrDistanceFieldTextureEffect& dfTexEffect =
                 args.fGP.cast<GrDistanceFieldTextureEffect>();
         const DistanceFieldBatchTracker& local = args.fBT.cast<DistanceFieldBatchTracker>();
@@ -45,6 +45,10 @@
                 GrGLFragmentShaderBuilder::kStandardDerivatives_GLSLFeature));
 
         GrGLVertexBuilder* vsBuilder = args.fPB->getVertexShaderBuilder();
+
+        // emit attributes
+        vsBuilder->emitAttributes(dfTexEffect);
+
         GrGLVertToFrag v(kVec2f_GrSLType);
         args.fPB->addVarying("TextureCoords", &v);
         vsBuilder->codeAppendf("%s = %s;", v.vsOut(), dfTexEffect.inTextureCoords()->fName);
@@ -56,15 +60,13 @@
         // setup uniform viewMatrix
         this->addUniformViewMatrix(pb);
 
-        // setup position varying
-        vsBuilder->codeAppendf("%s = %s * vec3(%s, 1);", vsBuilder->glPosition(),
-                               this->uViewM(), dfTexEffect.inPosition()->fName);
+        // Setup position
+        vsBuilder->codeAppendf("%s = %s * vec3(%s, 1);",  this->position(), this->uViewM(),
+                               dfTexEffect.inPosition()->fName);
 
-        // setup output coords
-        vsBuilder->codeAppendf("%s = %s;", vsBuilder->positionCoords(),
-                               dfTexEffect.inPosition()->fName);
-        vsBuilder->codeAppendf("%s = %s;", vsBuilder->localCoords(),
-                               dfTexEffect.inPosition()->fName);
+        // emit transforms
+        this->emitTransforms(args.fPB,  this->position(), dfTexEffect.inPosition()->fName,
+                             dfTexEffect.localMatrix(), args.fTransformsIn, args.fTransformsOut);
 
         const char* textureSizeUniName = NULL;
         fTextureSizeUni = args.fPB->addUniform(GrGLProgramBuilder::kFragment_Visibility,
@@ -242,8 +244,9 @@
     GrGLDistanceFieldTextureEffect::GenKey(*this, bt, caps, b);
 }
 
-GrGLGeometryProcessor*
-GrDistanceFieldTextureEffect::createGLInstance(const GrBatchTracker& bt) const {
+GrGLPrimitiveProcessor*
+GrDistanceFieldTextureEffect::createGLInstance(const GrBatchTracker& bt,
+                                               const GrGLCaps&) const {
     return SkNEW_ARGS(GrGLDistanceFieldTextureEffect, (*this, bt));
 }
 
@@ -321,7 +324,7 @@
                                           const GrBatchTracker&)
         : fColor(GrColor_ILLEGAL), fTextureSize(SkISize::Make(-1, -1)) {}
 
-    void emitCode(const EmitArgs& args) SK_OVERRIDE {
+    void onEmitCode(EmitArgs& args) SK_OVERRIDE {
         const GrDistanceFieldNoGammaTextureEffect& dfTexEffect =
                 args.fGP.cast<GrDistanceFieldNoGammaTextureEffect>();
 
@@ -333,6 +336,10 @@
                                      GrGLFragmentShaderBuilder::kStandardDerivatives_GLSLFeature));
 
         GrGLVertexBuilder* vsBuilder = args.fPB->getVertexShaderBuilder();
+
+        // emit attributes
+        vsBuilder->emitAttributes(dfTexEffect);
+
         GrGLVertToFrag v(kVec2f_GrSLType);
         args.fPB->addVarying("TextureCoords", &v);
 
@@ -342,18 +349,16 @@
 
         vsBuilder->codeAppendf("%s = %s;", v.vsOut(), dfTexEffect.inTextureCoords()->fName);
 
-        // setup coord outputs
-        vsBuilder->codeAppendf("%s = %s;", vsBuilder->positionCoords(),
-                               dfTexEffect.inPosition()->fName);
-        vsBuilder->codeAppendf("%s = %s;", vsBuilder->localCoords(),
-                               dfTexEffect.inPosition()->fName);
-
         // setup uniform viewMatrix
         this->addUniformViewMatrix(pb);
 
-        // setup position varying
-        vsBuilder->codeAppendf("%s = %s * vec3(%s, 1);", vsBuilder->glPosition(),
-                               this->uViewM(), dfTexEffect.inPosition()->fName);
+        // Setup position
+        vsBuilder->codeAppendf("%s = %s * vec3(%s, 1);",  this->position(), this->uViewM(),
+                               dfTexEffect.inPosition()->fName);
+
+        // emit transforms
+        this->emitTransforms(args.fPB,  this->position(), dfTexEffect.inPosition()->fName,
+                             dfTexEffect.localMatrix(), args.fTransformsIn, args.fTransformsOut);
 
         const char* textureSizeUniName = NULL;
         fTextureSizeUni = args.fPB->addUniform(GrGLProgramBuilder::kFragment_Visibility,
@@ -493,8 +498,9 @@
     GrGLDistanceFieldNoGammaTextureEffect::GenKey(*this, bt, caps, b);
 }
 
-GrGLGeometryProcessor*
-GrDistanceFieldNoGammaTextureEffect::createGLInstance(const GrBatchTracker& bt) const {
+GrGLPrimitiveProcessor*
+GrDistanceFieldNoGammaTextureEffect::createGLInstance(const GrBatchTracker& bt,
+                                                      const GrGLCaps&) const {
     return SkNEW_ARGS(GrGLDistanceFieldNoGammaTextureEffect, (*this, bt));
 }
 
@@ -562,13 +568,17 @@
     , fTextureSize(SkISize::Make(-1,-1))
     , fTextColor(GrColor_ILLEGAL) {}
 
-    void emitCode(const EmitArgs& args) SK_OVERRIDE {
+    void onEmitCode(EmitArgs& args) SK_OVERRIDE {
         const GrDistanceFieldLCDTextureEffect& dfTexEffect =
                 args.fGP.cast<GrDistanceFieldLCDTextureEffect>();
         const DistanceFieldLCDBatchTracker& local = args.fBT.cast<DistanceFieldLCDBatchTracker>();
         GrGLGPBuilder* pb = args.fPB;
 
         GrGLVertexBuilder* vsBuilder = args.fPB->getVertexShaderBuilder();
+
+        // emit attributes
+        vsBuilder->emitAttributes(dfTexEffect);
+
         GrGLVertToFrag v(kVec2f_GrSLType);
         args.fPB->addVarying("TextureCoords", &v);
         vsBuilder->codeAppendf("%s = %s;", v.vsOut(), dfTexEffect.inTextureCoords()->fName);
@@ -577,19 +587,17 @@
         this->setupColorPassThrough(pb, local.fInputColorType, args.fOutputColor, NULL,
                                     &fColorUniform);
 
-        // setup coord outputs
-        vsBuilder->codeAppendf("%s = %s;", vsBuilder->positionCoords(),
-                               dfTexEffect.inPosition()->fName);
-        vsBuilder->codeAppendf("%s = %s;", vsBuilder->localCoords(),
-                               dfTexEffect.inPosition()->fName);
-
         // setup uniform viewMatrix
         this->addUniformViewMatrix(pb);
 
-        // setup position varying
-        vsBuilder->codeAppendf("%s = %s * vec3(%s, 1);", vsBuilder->glPosition(), this->uViewM(),
+        // Setup position
+        vsBuilder->codeAppendf("%s = %s * vec3(%s, 1);",  this->position(), this->uViewM(),
                                dfTexEffect.inPosition()->fName);
 
+        // emit transforms
+        this->emitTransforms(args.fPB,  this->position(), dfTexEffect.inPosition()->fName,
+                             dfTexEffect.localMatrix(), args.fTransformsIn, args.fTransformsOut);
+
         const char* textureSizeUniName = NULL;
         // width, height, 1/(3*width)
         fTextureSizeUni = args.fPB->addUniform(GrGLProgramBuilder::kFragment_Visibility,
@@ -805,8 +813,9 @@
     GrGLDistanceFieldLCDTextureEffect::GenKey(*this, bt, caps, b);
 }
 
-GrGLGeometryProcessor*
-GrDistanceFieldLCDTextureEffect::createGLInstance(const GrBatchTracker& bt) const {
+GrGLPrimitiveProcessor*
+GrDistanceFieldLCDTextureEffect::createGLInstance(const GrBatchTracker& bt,
+                                                  const GrGLCaps&) const {
     return SkNEW_ARGS(GrGLDistanceFieldLCDTextureEffect, (*this, bt));
 }
 
diff --git a/src/gpu/effects/GrDistanceFieldTextureEffect.h b/src/gpu/effects/GrDistanceFieldTextureEffect.h
index 6edcf98..2fb448b 100644
--- a/src/gpu/effects/GrDistanceFieldTextureEffect.h
+++ b/src/gpu/effects/GrDistanceFieldTextureEffect.h
@@ -80,7 +80,8 @@
                                    const GrGLCaps& caps,
                                    GrProcessorKeyBuilder* b) const SK_OVERRIDE;
 
-    GrGLGeometryProcessor* createGLInstance(const GrBatchTracker& bt) const SK_OVERRIDE;
+    virtual GrGLPrimitiveProcessor* createGLInstance(const GrBatchTracker& bt,
+                                                     const GrGLCaps&) const SK_OVERRIDE;
 
     void initBatchTracker(GrBatchTracker* bt, const InitBT& init) const SK_OVERRIDE;
 
@@ -144,7 +145,8 @@
                                    const GrGLCaps& caps,
                                    GrProcessorKeyBuilder* b) const SK_OVERRIDE;
 
-    GrGLGeometryProcessor* createGLInstance(const GrBatchTracker& bt) const SK_OVERRIDE;
+    virtual GrGLPrimitiveProcessor* createGLInstance(const GrBatchTracker& bt,
+                                                     const GrGLCaps&) const SK_OVERRIDE;
 
     void initBatchTracker(GrBatchTracker* bt, const InitBT& init) const SK_OVERRIDE;
 
@@ -201,7 +203,8 @@
                                    const GrGLCaps& caps,
                                    GrProcessorKeyBuilder* b) const SK_OVERRIDE;
 
-    GrGLGeometryProcessor* createGLInstance(const GrBatchTracker& bt) const SK_OVERRIDE;
+    virtual GrGLPrimitiveProcessor* createGLInstance(const GrBatchTracker& bt,
+                                                     const GrGLCaps&) const SK_OVERRIDE;
 
     void initBatchTracker(GrBatchTracker* bt, const InitBT& init) const SK_OVERRIDE;
 
diff --git a/src/gpu/gl/GrGLCaps.cpp b/src/gpu/gl/GrGLCaps.cpp
index 376bfd4..73df30b 100644
--- a/src/gpu/gl/GrGLCaps.cpp
+++ b/src/gpu/gl/GrGLCaps.cpp
@@ -25,6 +25,7 @@
     fMSFBOType = kNone_MSFBOType;
     fInvalidateFBType = kNone_InvalidateFBType;
     fLATCAlias = kLATC_LATCAlias;
+    fNvprSupport = kNone_NvprSupport;
     fMapBufferType = kNone_MapBufferType;
     fMaxFragmentUniformVectors = 0;
     fMaxVertexAttributes = 0;
@@ -67,6 +68,7 @@
     fStencilFormats = caps.fStencilFormats;
     fStencilVerifiedColorConfigs = caps.fStencilVerifiedColorConfigs;
     fLATCAlias = caps.fLATCAlias;
+    fNvprSupport = caps.fNvprSupport;
     fMaxFragmentUniformVectors = caps.fMaxFragmentUniformVectors;
     fMaxVertexAttributes = caps.fMaxVertexAttributes;
     fMaxFragmentTextureUnits = caps.fMaxFragmentTextureUnits;
@@ -359,9 +361,16 @@
                  ((ctxInfo.version() >= GR_GL_VER(4,3) ||
                    ctxInfo.hasExtension("GL_ARB_program_interface_query")) &&
                   gli->fFunctions.fProgramPathFragmentInputGen));
+            if (fPathRenderingSupport) {
+                fNvprSupport = gli->fFunctions.fProgramPathFragmentInputGen ? kNormal_NvprSupport :
+                                                                              kLegacy_NvprSupport;
+            }
         } else {
             fPathRenderingSupport = ctxInfo.version() >= GR_GL_VER(3,1);
+            fNvprSupport = fPathRenderingSupport ? kNormal_NvprSupport : kNone_NvprSupport;
         }
+    } else {
+        fNvprSupport = kNone_NvprSupport;
     }
 
     fGpuTracingSupport = ctxInfo.hasExtension("GL_EXT_debug_marker");
diff --git a/src/gpu/gl/GrGLCaps.h b/src/gpu/gl/GrGLCaps.h
index 77baf38..527cd33 100644
--- a/src/gpu/gl/GrGLCaps.h
+++ b/src/gpu/gl/GrGLCaps.h
@@ -286,6 +286,18 @@
 
     LATCAlias latcAlias() const { return fLATCAlias; }
 
+    /**
+     * Which type of path rendering is supported, if any
+     * TODO delete this when we only support normal non-legacy nvpr
+     */
+    enum NvprSupport {
+        kNone_NvprSupport,
+        kLegacy_NvprSupport,
+        kNormal_NvprSupport,
+    };
+
+    NvprSupport nvprSupport() const { return fNvprSupport; }
+
 private:
     /**
      * Maintains a bit per GrPixelConfig. It is used to avoid redundantly
@@ -354,6 +366,7 @@
     InvalidateFBType    fInvalidateFBType;
     MapBufferType       fMapBufferType;
     LATCAlias           fLATCAlias;
+    NvprSupport         fNvprSupport;
 
     bool fRGBA8RenderbufferSupport : 1;
     bool fBGRAIsInternalFormat : 1;
diff --git a/src/gpu/gl/GrGLGeometryProcessor.h b/src/gpu/gl/GrGLGeometryProcessor.h
index 0b24144..524cdf3 100644
--- a/src/gpu/gl/GrGLGeometryProcessor.h
+++ b/src/gpu/gl/GrGLGeometryProcessor.h
@@ -11,57 +11,67 @@
 #include "GrGLProcessor.h"
 
 class GrBatchTracker;
+class GrFragmentProcessor;
 class GrGLGPBuilder;
 
-/**
- * If a GL effect needs a GrGLFullShaderBuilder* object to emit vertex code, then it must inherit
- * from this class. Since paths don't have vertices, this class is only meant to be used internally
- * by skia, for special cases.
- */
-class GrGLGeometryProcessor {
+class GrGLPrimitiveProcessor {
 public:
-    GrGLGeometryProcessor() : fViewMatrixName(NULL) { fViewMatrix = SkMatrix::InvalidMatrix(); }
-    virtual ~GrGLGeometryProcessor() {}
+    GrGLPrimitiveProcessor() : fViewMatrixName(NULL) { fViewMatrix = SkMatrix::InvalidMatrix(); }
+    virtual ~GrGLPrimitiveProcessor() {}
 
     typedef GrGLProgramDataManager::UniformHandle UniformHandle;
     typedef GrGLProcessor::TextureSamplerArray TextureSamplerArray;
 
+    typedef SkSTArray<2, const GrCoordTransform*, true> ProcCoords;
+    typedef SkSTArray<8, ProcCoords> TransformsIn;
+    typedef SkSTArray<8, GrGLProcessor::TransformedCoordsArray> TransformsOut;
+
     struct EmitArgs {
         EmitArgs(GrGLGPBuilder* pb,
                  const GrPrimitiveProcessor& gp,
                  const GrBatchTracker& bt,
                  const char* outputColor,
                  const char* outputCoverage,
-                 const TextureSamplerArray& samplers)
+                 const TextureSamplerArray& samplers,
+                 const TransformsIn& transformsIn,
+                 TransformsOut* transformsOut)
             : fPB(pb)
             , fGP(gp)
             , fBT(bt)
             , fOutputColor(outputColor)
             , fOutputCoverage(outputCoverage)
-            , fSamplers(samplers) {}
+            , fSamplers(samplers)
+            , fTransformsIn(transformsIn)
+            , fTransformsOut(transformsOut) {}
         GrGLGPBuilder* fPB;
         const GrPrimitiveProcessor& fGP;
         const GrBatchTracker& fBT;
         const char* fOutputColor;
         const char* fOutputCoverage;
         const TextureSamplerArray& fSamplers;
+        const TransformsIn& fTransformsIn;
+        TransformsOut* fTransformsOut;
     };
 
     /**
      * This is similar to emitCode() in the base class, except it takes a full shader builder.
      * This allows the effect subclass to emit vertex code.
      */
-    virtual void emitCode(const EmitArgs&) = 0;
+    virtual void emitCode(EmitArgs&) = 0;
 
-    /** A GrGLGeometryProcessor instance can be reused with any GrGLGeometryProcessor that produces
-        the same stage key; this function reads data from a GrGLGeometryProcessor and uploads any
-        uniform variables required  by the shaders created in emitCode(). The GrGeometryProcessor
-        parameter is guaranteed to be of the same type that created this GrGLGeometryProcessor and
-        to have an identical processor key as the one that created this GrGLGeometryProcessor.  */
+
+    /** A GrGLPrimitiveProcessor instance can be reused with any GrGLPrimitiveProcessor that
+        produces the same stage key; this function reads data from a GrGLPrimitiveProcessor and
+        uploads any uniform variables required  by the shaders created in emitCode(). The
+        GrPrimitiveProcessor parameter is guaranteed to be of the same type that created this
+        GrGLPrimitiveProcessor and to have an identical processor key as the one that created this
+        GrGLPrimitiveProcessor.  */
     virtual void setData(const GrGLProgramDataManager&,
                          const GrPrimitiveProcessor&,
                          const GrBatchTracker&) = 0;
 
+    static SkMatrix GetTransformMatrix(const SkMatrix& localMatrix, const GrCoordTransform&);
+
 protected:
     /** a helper which can setup vertex, constant, or uniform color depending on inputType.
      *  This function will only do the minimum required to emit the correct shader code.  If
@@ -86,12 +96,111 @@
     void setUniformViewMatrix(const GrGLProgramDataManager&,
                               const SkMatrix& viewMatrix);
 
+    class ShaderVarHandle {
+    public:
+        bool isValid() const { return fHandle > -1; }
+        ShaderVarHandle() : fHandle(-1) {}
+        ShaderVarHandle(int value) : fHandle(value) { SkASSERT(this->isValid()); }
+        int handle() const { SkASSERT(this->isValid()); return fHandle; }
+        UniformHandle convertToUniformHandle() {
+            SkASSERT(this->isValid());
+            return GrGLProgramDataManager::UniformHandle::CreateFromUniformIndex(fHandle);
+        }
+
+    private:
+        int fHandle;
+    };
+
+    struct Transform {
+        Transform() : fType(kVoid_GrSLType) { fCurrentValue = SkMatrix::InvalidMatrix(); }
+        ShaderVarHandle fHandle;
+        SkMatrix       fCurrentValue;
+        GrSLType       fType;
+    };
+
+    SkSTArray<8, SkSTArray<2, Transform, true> > fInstalledTransforms;
+
 private:
     UniformHandle fViewMatrixUniform;
     SkMatrix fViewMatrix;
     const char* fViewMatrixName;
+};
 
-    typedef GrGLProcessor INHERITED;
+class GrGLPathRendering;
+/**
+ * If a GL effect needs a GrGLFullShaderBuilder* object to emit vertex code, then it must inherit
+ * from this class. Since paths don't have vertices, this class is only meant to be used internally
+ * by skia, for special cases.
+ */
+class GrGLGeometryProcessor : public GrGLPrimitiveProcessor {
+public:
+    /* Any general emit code goes in the base class emitCode.  Subclasses override onEmitCode */
+    void emitCode(EmitArgs&) SK_OVERRIDE;
+
+    void setTransformData(const GrPrimitiveProcessor*,
+                          const GrGLProgramDataManager&,
+                          int index,
+                          const SkTArray<const GrCoordTransform*, true>& transforms);
+
+protected:
+    const char* position() const { return "pos3"; }
+
+    // Many GrGeometryProcessors do not need explicit local coords
+    void emitTransforms(GrGLGPBuilder* gp,
+                        const char* position,
+                        const SkMatrix& localMatrix,
+                        const TransformsIn& tin,
+                        TransformsOut* tout) {
+        this->emitTransforms(gp, position, position, localMatrix, tin, tout);
+    }
+
+    void emitTransforms(GrGLGPBuilder*,
+                        const char* position,
+                        const char* localCoords,
+                        const SkMatrix& localMatrix,
+                        const TransformsIn&,
+                        TransformsOut*);
+
+private:
+    virtual void onEmitCode(EmitArgs&) = 0;
+
+    typedef GrGLPrimitiveProcessor INHERITED;
+};
+
+class GrGLGpu;
+
+class GrGLPathProcessor : public GrGLPrimitiveProcessor {
+public:
+    GrGLPathProcessor(const GrPathProcessor&, const GrBatchTracker&);
+
+    static void GenKey(const GrPathProcessor&,
+                       const GrBatchTracker& bt,
+                       const GrGLCaps&,
+                       GrProcessorKeyBuilder* b);
+
+    void emitCode(EmitArgs&) SK_OVERRIDE;
+
+    virtual void emitTransforms(GrGLGPBuilder*, const TransformsIn&, TransformsOut*) = 0;
+
+    virtual void resolveSeparableVaryings(GrGLGpu* gpu, GrGLuint programId) {}
+
+    void setData(const GrGLProgramDataManager&,
+                 const GrPrimitiveProcessor&,
+                 const GrBatchTracker&) SK_OVERRIDE;
+
+    virtual void setTransformData(const GrPrimitiveProcessor*,
+                                  int index,
+                                  const SkTArray<const GrCoordTransform*, true>& transforms,
+                                  GrGLPathRendering*,
+                                  GrGLuint programID) = 0;
+
+    virtual void didSetData(GrGLPathRendering*) {}
+
+private:
+    UniformHandle fColorUniform;
+    GrColor fColor;
+
+    typedef GrGLPrimitiveProcessor INHERITED;
 };
 
 #endif
diff --git a/src/gpu/gl/GrGLProgram.cpp b/src/gpu/gl/GrGLProgram.cpp
index 9400e54..1af3e56 100644
--- a/src/gpu/gl/GrGLProgram.cpp
+++ b/src/gpu/gl/GrGLProgram.cpp
@@ -24,34 +24,6 @@
 #define GL_CALL(X) GR_GL_CALL(fGpu->glInterface(), X)
 #define GL_CALL_RET(R, X) GR_GL_CALL_RET(fGpu->glInterface(), R, X)
 
-/**
- * Retrieves the final matrix that a transform needs to apply to its source coords.
- */
-static SkMatrix get_transform_matrix(const GrPendingFragmentStage& stage,
-                                     int transformIdx,
-                                     const SkMatrix& localMatrix) {
-    const GrCoordTransform& coordTransform = stage.processor()->coordTransform(transformIdx);
-    SkMatrix combined;
-
-    // We only apply the localmatrix to localcoords
-    if (kLocal_GrCoordSet == coordTransform.sourceCoords()) {
-        combined.setConcat(coordTransform.getMatrix(), localMatrix);
-    } else {
-        combined = coordTransform.getMatrix();
-    }
-    if (coordTransform.reverseY()) {
-        // combined.postScale(1,-1);
-        // combined.postTranslate(0,1);
-        combined.set(SkMatrix::kMSkewY,
-            combined[SkMatrix::kMPersp0] - combined[SkMatrix::kMSkewY]);
-        combined.set(SkMatrix::kMScaleY,
-            combined[SkMatrix::kMPersp1] - combined[SkMatrix::kMScaleY]);
-        combined.set(SkMatrix::kMTransY,
-            combined[SkMatrix::kMPersp2] - combined[SkMatrix::kMTransY]);
-    }
-    return combined;
-}
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
 GrGLProgram::GrGLProgram(GrGLGpu* gpu,
@@ -93,9 +65,7 @@
         fProgramDataManager.setSampler(fBuiltinUniformHandles.fDstCopySamplerUni, texUnitIdx);
         fDstCopyTexUnit = texUnitIdx++;
     }
-    if (fGeometryProcessor.get()) {
-        this->initSamplers(fGeometryProcessor.get(), &texUnitIdx);
-    }
+    this->initSamplers(fGeometryProcessor.get(), &texUnitIdx);
     if (fXferProcessor.get()) {
         this->initSamplers(fXferProcessor.get(), &texUnitIdx);
     }
@@ -105,8 +75,9 @@
     }
 }
 
-void GrGLProgram::initSamplers(GrGLInstalledProc* ip, int* texUnitIdx) {
-    SkTArray<GrGLInstalledProc::Sampler, true>& samplers = ip->fSamplers;
+template <class Proc>
+void GrGLProgram::initSamplers(Proc* ip, int* texUnitIdx) {
+    SkTArray<typename Proc::Sampler, true>& samplers = ip->fSamplers;
     int numSamplers = samplers.count();
     for (int s = 0; s < numSamplers; ++s) {
         SkASSERT(samplers[s].fUniform.isValid());
@@ -115,8 +86,9 @@
     }
 }
 
-void GrGLProgram::bindTextures(const GrGLInstalledProc* ip, const GrProcessor& processor) {
-    const SkTArray<GrGLInstalledProc::Sampler, true>& samplers = ip->fSamplers;
+template <class Proc>
+void GrGLProgram::bindTextures(const Proc* ip, const GrProcessor& processor) {
+    const SkTArray<typename Proc::Sampler, true>& samplers = ip->fSamplers;
     int numSamplers = samplers.count();
     SkASSERT(numSamplers == processor.numTextures());
     for (int s = 0; s < numSamplers; ++s) {
@@ -161,12 +133,12 @@
     const GrPrimitiveProcessor& primProc = *optState.getPrimitiveProcessor();
     const GrBatchTracker& bt = optState.getBatchTracker();
     fGeometryProcessor->fGLProc->setData(fProgramDataManager, primProc, bt);
-    this->bindTextures(fGeometryProcessor, primProc);
+    this->bindTextures(fGeometryProcessor.get(), primProc);
 
     if (fXferProcessor.get()) {
         const GrXferProcessor& xp = *optState.getXferProcessor();
         fXferProcessor->fGLProc->setData(fProgramDataManager, xp);
-        this->bindTextures(fXferProcessor, xp);
+        this->bindTextures(fXferProcessor.get(), xp);
     }
     this->setFragmentData(optState);
 
@@ -180,25 +152,21 @@
         const GrPendingFragmentStage& stage = optState.getFragmentStage(e);
         const GrProcessor& processor = *stage.processor();
         fFragmentProcessors->fProcs[e]->fGLProc->setData(fProgramDataManager, processor);
-        const SkMatrix& localMatrix = optState.getPrimitiveProcessor()->localMatrix();
-        this->setTransformData(stage, localMatrix, fFragmentProcessors->fProcs[e]);
+        this->setTransformData(optState.getPrimitiveProcessor(),
+                               stage,
+                               e,
+                               fFragmentProcessors->fProcs[e]);
         this->bindTextures(fFragmentProcessors->fProcs[e], processor);
     }
 }
-void GrGLProgram::setTransformData(const GrPendingFragmentStage& processor,
-                                   const SkMatrix& localMatrix,
+void GrGLProgram::setTransformData(const GrPrimitiveProcessor* primProc,
+                                   const GrPendingFragmentStage& processor,
+                                   int index,
                                    GrGLInstalledFragProc* ip) {
-    SkTArray<GrGLInstalledFragProc::Transform, true>& transforms = ip->fTransforms;
-    int numTransforms = transforms.count();
-    SkASSERT(numTransforms == processor.processor()->numTransforms());
-    for (int t = 0; t < numTransforms; ++t) {
-        SkASSERT(transforms[t].fHandle.isValid());
-        const SkMatrix& matrix = get_transform_matrix(processor, t, localMatrix);
-        if (!transforms[t].fCurrentValue.cheapEqualTo(matrix)) {
-            fProgramDataManager.setSkMatrix(transforms[t].fHandle.convertToUniformHandle(), matrix);
-            transforms[t].fCurrentValue = matrix;
-        }
-    }
+    GrGLGeometryProcessor* gp =
+            static_cast<GrGLGeometryProcessor*>(fGeometryProcessor.get()->fGLProc.get());
+    gp->setTransformData(primProc, fProgramDataManager, index,
+                         processor.processor()->coordTransforms());
 }
 
 void GrGLProgram::didSetData(GrGpu::DrawType drawType) {
@@ -234,29 +202,6 @@
 
 /////////////////////////////////////////////////////////////////////////////////////////
 
-GrGLNvprProgramBase::GrGLNvprProgramBase(GrGLGpu* gpu,
-                                         const GrProgramDesc& desc,
-                                         const BuiltinUniformHandles& builtinUniforms,
-                                         GrGLuint programID,
-                                         const UniformInfoArray& uniforms,
-                                         GrGLInstalledGeoProc* primProc,
-                                         GrGLInstalledXferProc* xferProcessor,
-                                         GrGLInstalledFragProcs* fragmentProcessors)
-    : INHERITED(gpu, desc, builtinUniforms, programID, uniforms, primProc,
-                xferProcessor, fragmentProcessors) {
-}
-
-void GrGLNvprProgramBase::onSetRenderTargetState(const GrOptDrawState& optState) {
-    SkASSERT(GrGpu::IsPathRenderingDrawType(optState.drawType()));
-    const GrRenderTarget* rt = optState.getRenderTarget();
-    SkISize size;
-    size.set(rt->width(), rt->height());
-    fGpu->glPathRendering()->setProjectionMatrix(optState.getPrimitiveProcessor()->viewMatrix(),
-                                                 size, rt->origin());
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
 GrGLNvprProgram::GrGLNvprProgram(GrGLGpu* gpu,
                                  const GrProgramDesc& desc,
                                  const BuiltinUniformHandles& builtinUniforms,
@@ -264,85 +209,32 @@
                                  const UniformInfoArray& uniforms,
                                  GrGLInstalledGeoProc* primProc,
                                  GrGLInstalledXferProc* xferProcessor,
-                                 GrGLInstalledFragProcs* fragmentProcessors,
-                                 const SeparableVaryingInfoArray& separableVaryings)
+                                 GrGLInstalledFragProcs* fragmentProcessors)
     : INHERITED(gpu, desc, builtinUniforms, programID, uniforms, primProc,
                 xferProcessor, fragmentProcessors) {
-    int count = separableVaryings.count();
-    fVaryings.push_back_n(count);
-    for (int i = 0; i < count; i++) {
-        Varying& varying = fVaryings[i];
-        const SeparableVaryingInfo& builderVarying = separableVaryings[i];
-        SkASSERT(GrGLShaderVar::kNonArray == builderVarying.fVariable.getArrayCount());
-        SkDEBUGCODE(
-            varying.fType = builderVarying.fVariable.getType();
-        );
-        varying.fLocation = builderVarying.fLocation;
-    }
 }
-
 void GrGLNvprProgram::didSetData(GrGpu::DrawType drawType) {
     SkASSERT(GrGpu::IsPathRenderingDrawType(drawType));
+    GrGLPathProcessor* pathProc =
+            static_cast<GrGLPathProcessor*>(fGeometryProcessor.get()->fGLProc.get());
+    pathProc->didSetData(fGpu->glPathRendering());
 }
 
-void GrGLNvprProgram::setTransformData(const GrPendingFragmentStage& proc,
-                                       const SkMatrix& localMatrix,
+void GrGLNvprProgram::setTransformData(const GrPrimitiveProcessor* primProc,
+                                       const GrPendingFragmentStage& proc,
+                                       int index,
                                        GrGLInstalledFragProc* ip) {
-    SkTArray<GrGLInstalledFragProc::Transform, true>& transforms = ip->fTransforms;
-    int numTransforms = transforms.count();
-    SkASSERT(numTransforms == proc.processor()->numTransforms());
-    for (int t = 0; t < numTransforms; ++t) {
-        SkASSERT(transforms[t].fHandle.isValid());
-        const SkMatrix& transform = get_transform_matrix(proc, t, localMatrix);
-        if (transforms[t].fCurrentValue.cheapEqualTo(transform)) {
-            continue;
-        }
-        transforms[t].fCurrentValue = transform;
-        const Varying& fragmentInput = fVaryings[transforms[t].fHandle.handle()];
-        SkASSERT(transforms[t].fType == kVec2f_GrSLType || transforms[t].fType == kVec3f_GrSLType);
-        unsigned components = transforms[t].fType == kVec2f_GrSLType ? 2 : 3;
-        fGpu->glPathRendering()->setProgramPathFragmentInputTransform(fProgramID,
-                                                                      fragmentInput.fLocation,
-                                                                      GR_GL_OBJECT_LINEAR,
-                                                                      components,
-                                                                      transform);
-    }
+    GrGLPathProcessor* pathProc =
+            static_cast<GrGLPathProcessor*>(fGeometryProcessor.get()->fGLProc.get());
+    pathProc->setTransformData(primProc, index, proc.processor()->coordTransforms(),
+                               fGpu->glPathRendering(), fProgramID);
 }
 
-//////////////////////////////////////////////////////////////////////////////////////
-
-GrGLLegacyNvprProgram::GrGLLegacyNvprProgram(GrGLGpu* gpu,
-                                             const GrProgramDesc& desc,
-                                             const BuiltinUniformHandles& builtinUniforms,
-                                             GrGLuint programID,
-                                             const UniformInfoArray& uniforms,
-                                             GrGLInstalledGeoProc* primProc,
-                                             GrGLInstalledXferProc* xp,
-                                             GrGLInstalledFragProcs* fps,
-                                             int texCoordSetCnt)
-    : INHERITED(gpu, desc, builtinUniforms, programID, uniforms, primProc, xp, fps)
-    , fTexCoordSetCnt(texCoordSetCnt) {
-}
-
-void GrGLLegacyNvprProgram::didSetData(GrGpu::DrawType drawType) {
-    SkASSERT(GrGpu::IsPathRenderingDrawType(drawType));
-    fGpu->glPathRendering()->flushPathTexGenSettings(fTexCoordSetCnt);
-}
-
-void
-GrGLLegacyNvprProgram::setTransformData(const GrPendingFragmentStage& proc,
-                                        const SkMatrix& localMatrix,
-                                        GrGLInstalledFragProc* ip) {
-    // We've hidden the texcoord index in the first entry of the transforms array for each effect
-    int texCoordIndex = ip->fTransforms[0].fHandle.handle();
-    int numTransforms = proc.processor()->numTransforms();
-    for (int t = 0; t < numTransforms; ++t) {
-        const SkMatrix& transform = get_transform_matrix(proc, t, localMatrix);
-        GrGLPathRendering::PathTexGenComponents components =
-                GrGLPathRendering::kST_PathTexGenComponents;
-        if (proc.isPerspectiveCoordTransform(t)) {
-            components = GrGLPathRendering::kSTR_PathTexGenComponents;
-        }
-        fGpu->glPathRendering()->enablePathTexGen(texCoordIndex++, components, transform);
-    }
+void GrGLNvprProgram::onSetRenderTargetState(const GrOptDrawState& optState) {
+    SkASSERT(GrGpu::IsPathRenderingDrawType(optState.drawType()));
+    const GrRenderTarget* rt = optState.getRenderTarget();
+    SkISize size;
+    size.set(rt->width(), rt->height());
+    fGpu->glPathRendering()->setProjectionMatrix(optState.getPrimitiveProcessor()->viewMatrix(),
+                                                 size, rt->origin());
 }
diff --git a/src/gpu/gl/GrGLProgram.h b/src/gpu/gl/GrGLProgram.h
index e70fee7..d18a92d 100644
--- a/src/gpu/gl/GrGLProgram.h
+++ b/src/gpu/gl/GrGLProgram.h
@@ -10,7 +10,6 @@
 #define GrGLProgram_DEFINED
 
 #include "builders/GrGLProgramBuilder.h"
-#include "builders/GrGLNvprProgramBuilder.h"
 #include "GrDrawState.h"
 #include "GrGLContext.h"
 #include "GrGLProgramDesc.h"
@@ -118,14 +117,17 @@
 
     // Sets the texture units for samplers.
     void initSamplerUniforms();
-    void initSamplers(GrGLInstalledProc*, int* texUnitIdx);
+    template <class Proc>
+    void initSamplers(Proc*, int* texUnitIdx);
 
     // A templated helper to loop over effects, set the transforms(via subclass) and bind textures
     void setFragmentData(const GrOptDrawState&);
-    virtual void setTransformData(const GrPendingFragmentStage&,
-                                  const SkMatrix& localMatrix,
+    virtual void setTransformData(const GrPrimitiveProcessor*,
+                                  const GrPendingFragmentStage&,
+                                  int index,
                                   GrGLInstalledFragProc*);
-    void bindTextures(const GrGLInstalledProc*, const GrProcessor&);
+    template <class Proc>
+    void bindTextures(const Proc*, const GrProcessor&);
 
     /*
      * Legacy NVPR needs a hook here to flush path tex gen settings.
@@ -166,28 +168,8 @@
  * specialized methods for setting transform data. Both types of NVPR also require setting the
  * projection matrix through a special function call
  */
-class GrGLNvprProgramBase : public GrGLProgram {
+class GrGLNvprProgram : public GrGLProgram {
 protected:
-    GrGLNvprProgramBase(GrGLGpu*,
-                        const GrProgramDesc&,
-                        const BuiltinUniformHandles&,
-                        GrGLuint programID,
-                        const UniformInfoArray&,
-                        GrGLInstalledGeoProc*,
-                        GrGLInstalledXferProc* xferProcessor,
-                        GrGLInstalledFragProcs* fragmentProcessors);
-    virtual void onSetRenderTargetState(const GrOptDrawState&);
-
-    typedef GrGLProgram INHERITED;
-};
-
-class GrGLNvprProgram : public GrGLNvprProgramBase {
-public:
-    bool hasVertexShader() const SK_OVERRIDE { return true; }
-
-private:
-    typedef GrGLNvprProgramBuilder::SeparableVaryingInfo SeparableVaryingInfo;
-    typedef GrGLNvprProgramBuilder::SeparableVaryingInfoArray SeparableVaryingInfoArray;
     GrGLNvprProgram(GrGLGpu*,
                     const GrProgramDesc&,
                     const BuiltinUniformHandles&,
@@ -195,50 +177,19 @@
                     const UniformInfoArray&,
                     GrGLInstalledGeoProc*,
                     GrGLInstalledXferProc* xferProcessor,
-                    GrGLInstalledFragProcs* fragmentProcessors,
-                    const SeparableVaryingInfoArray& separableVaryings);
-    void didSetData(GrGpu::DrawType) SK_OVERRIDE;
-    virtual void setTransformData(const GrPendingFragmentStage&,
-                                  const SkMatrix& localMatrix,
-                                  GrGLInstalledFragProc*) SK_OVERRIDE;
+                    GrGLInstalledFragProcs* fragmentProcessors);
 
-    struct Varying {
-        GrGLint     fLocation;
-        SkDEBUGCODE(
-            GrSLType    fType;
-        );
-    };
-    SkTArray<Varying, true> fVaryings;
+private:
+    void didSetData(GrGpu::DrawType) SK_OVERRIDE;
+    virtual void setTransformData(const GrPrimitiveProcessor*,
+                                  const GrPendingFragmentStage&,
+                                  int index,
+                                  GrGLInstalledFragProc*) SK_OVERRIDE;
+    virtual void onSetRenderTargetState(const GrOptDrawState&);
 
     friend class GrGLNvprProgramBuilder;
 
-    typedef GrGLNvprProgramBase INHERITED;
-};
-
-class GrGLLegacyNvprProgram : public GrGLNvprProgramBase {
-public:
-    bool hasVertexShader() const SK_OVERRIDE { return false; }
-
-private:
-    GrGLLegacyNvprProgram(GrGLGpu* gpu,
-                          const GrProgramDesc& desc,
-                          const BuiltinUniformHandles&,
-                          GrGLuint programID,
-                          const UniformInfoArray&,
-                          GrGLInstalledGeoProc*,
-                          GrGLInstalledXferProc* xp,
-                          GrGLInstalledFragProcs* fps,
-                          int texCoordSetCnt);
-    void didSetData(GrGpu::DrawType) SK_OVERRIDE;
-    virtual void setTransformData(const GrPendingFragmentStage&,
-                                  const SkMatrix& localMatrix,
-                                  GrGLInstalledFragProc*) SK_OVERRIDE;
-
-    int fTexCoordSetCnt;
-
-    friend class GrGLLegacyNvprProgramBuilder;
-
-    typedef GrGLNvprProgramBase INHERITED;
+    typedef GrGLProgram INHERITED;
 };
 
 #endif
diff --git a/src/gpu/gl/GrGLProgramDataManager.h b/src/gpu/gl/GrGLProgramDataManager.h
index 31f7f2e..f893d18 100644
--- a/src/gpu/gl/GrGLProgramDataManager.h
+++ b/src/gpu/gl/GrGLProgramDataManager.h
@@ -54,6 +54,7 @@
 
         friend class GrGLProgramDataManager; // For accessing toProgramDataIndex().
         friend class GrGLProgramBuilder; // For accessing toShaderBuilderIndex().
+        friend class GrGLGeometryProcessor;
     };
 
     struct UniformInfo {
diff --git a/src/gpu/gl/GrGLProgramDesc.cpp b/src/gpu/gl/GrGLProgramDesc.cpp
index d5dc84c..631cf42 100644
--- a/src/gpu/gl/GrGLProgramDesc.cpp
+++ b/src/gpu/gl/GrGLProgramDesc.cpp
@@ -40,62 +40,6 @@
     return false;
 }
 
-/**
- * The key for an individual coord transform is made up of a matrix type, a precision, and a bit
- * that indicates the source of the input coords.
- */
-enum {
-    kMatrixTypeKeyBits   = 1,
-    kMatrixTypeKeyMask   = (1 << kMatrixTypeKeyBits) - 1,
-    
-    kPrecisionBits       = 2,
-    kPrecisionShift      = kMatrixTypeKeyBits,
-
-    kPositionCoords_Flag = (1 << (kPrecisionShift + kPrecisionBits)),
-    kDeviceCoords_Flag   = kPositionCoords_Flag + kPositionCoords_Flag,
-
-    kTransformKeyBits    = kMatrixTypeKeyBits + kPrecisionBits + 2,
-};
-
-GR_STATIC_ASSERT(kHigh_GrSLPrecision < (1 << kPrecisionBits));
-
-/**
- * We specialize the vertex code for each of these matrix types.
- */
-enum MatrixType {
-    kNoPersp_MatrixType  = 0,
-    kGeneral_MatrixType  = 1,
-};
-
-static uint32_t gen_transform_key(const GrPendingFragmentStage& stage, bool useExplicitLocalCoords) {
-    uint32_t totalKey = 0;
-    int numTransforms = stage.processor()->numTransforms();
-    for (int t = 0; t < numTransforms; ++t) {
-        uint32_t key = 0;
-        if (stage.isPerspectiveCoordTransform(t)) {
-            key |= kGeneral_MatrixType;
-        } else {
-            key |= kNoPersp_MatrixType;
-        }
-
-        const GrCoordTransform& coordTransform = stage.processor()->coordTransform(t);
-        if (kLocal_GrCoordSet == coordTransform.sourceCoords() && !useExplicitLocalCoords) {
-            key |= kPositionCoords_Flag;
-        } else if (kDevice_GrCoordSet == coordTransform.sourceCoords()) {
-            key |= kDeviceCoords_Flag;
-        }
-
-        GR_STATIC_ASSERT(kGrSLPrecisionCount <= (1 << kPrecisionBits));
-        key |= (coordTransform.precision() << kPrecisionShift);
-
-        key <<= kTransformKeyBits * t;
-
-        SkASSERT(0 == (totalKey & key)); // keys for each transform ought not to overlap
-        totalKey |= key;
-    }
-    return totalKey;
-}
-
 static uint32_t gen_texture_key(const GrProcessor& proc, const GrGLCaps& caps) {
     uint32_t key = 0;
     int numTextures = proc.numTextures();
@@ -152,8 +96,6 @@
     // bindings in use or other descriptor field settings) it should be set
     // to a canonical value to avoid duplicate programs with different keys.
 
-    bool requiresLocalCoordAttrib = descInfo.fRequiresLocalCoordAttrib;
-
     GR_STATIC_ASSERT(0 == kProcessorKeysOffset % sizeof(uint32_t));
     // Make room for everything up to the effect keys.
     desc->fKey.reset();
@@ -172,8 +114,7 @@
         const GrPendingFragmentStage& fps = optState.getFragmentStage(s);
         const GrFragmentProcessor& fp = *fps.processor();
         fp.getGLProcessorKey(gpu->glCaps(), &b);
-        if (!get_meta_key(fp, gpu->glCaps(),
-                          gen_transform_key(fps, requiresLocalCoordAttrib), &b)) {
+        if (!get_meta_key(fp, gpu->glCaps(), primProc.getTransformKey(fp.coordTransforms()), &b)) {
             desc->fKey.reset();
             return false;
         }
@@ -197,7 +138,6 @@
     bool isPathRendering = GrGpu::IsPathRenderingDrawType(drawType);
     if (gpu->caps()->pathRenderingSupport() && isPathRendering) {
         header->fUseNvpr = true;
-        SkASSERT(!optState.hasGeometryProcessor());
     } else {
         header->fUseNvpr = false;
     }
diff --git a/src/gpu/gl/builders/GrGLFragmentShaderBuilder.h b/src/gpu/gl/builders/GrGLFragmentShaderBuilder.h
index 8e8a008..688bbe6 100644
--- a/src/gpu/gl/builders/GrGLFragmentShaderBuilder.h
+++ b/src/gpu/gl/builders/GrGLFragmentShaderBuilder.h
@@ -48,6 +48,8 @@
     virtual const char* fragmentPosition() = 0;
 
 private:
+    friend class GrGLNormalPathProcessor;
+
     typedef GrGLShaderBuilder INHERITED;
 };
 
@@ -153,7 +155,6 @@
     bool fHasReadDstColor;
     bool fHasReadFragmentPosition;
 
-    friend class GrGLNvprProgramBuilder;
     friend class GrGLProgramBuilder;
 
     typedef GrGLFPFragmentBuilder INHERITED;
diff --git a/src/gpu/gl/builders/GrGLLegacyNvprProgramBuilder.cpp b/src/gpu/gl/builders/GrGLLegacyNvprProgramBuilder.cpp
deleted file mode 100644
index e8b4075..0000000
--- a/src/gpu/gl/builders/GrGLLegacyNvprProgramBuilder.cpp
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright 2014 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "GrGLLegacyNvprProgramBuilder.h"
-#include "../GrGLGpu.h"
-
-GrGLLegacyNvprProgramBuilder::GrGLLegacyNvprProgramBuilder(GrGLGpu* gpu,
-                                                           const GrOptDrawState& optState)
-    : INHERITED(gpu, optState)
-    , fTexCoordSetCnt(0) {
-}
-
-int GrGLLegacyNvprProgramBuilder::addTexCoordSets(int count) {
-    int firstFreeCoordSet = fTexCoordSetCnt;
-    fTexCoordSetCnt += count;
-    SkASSERT(gpu()->glCaps().maxFixedFunctionTextureCoords() >= fTexCoordSetCnt);
-    return firstFreeCoordSet;
-}
-
-void GrGLLegacyNvprProgramBuilder::emitTransforms(const GrPendingFragmentStage& processorStage,
-                                            GrGLProcessor::TransformedCoordsArray* outCoords,
-                                            GrGLInstalledFragProc* ifp) {
-    int numTransforms = processorStage.processor()->numTransforms();
-    int texCoordIndex = this->addTexCoordSets(numTransforms);
-
-    // Use the first uniform location as the texcoord index.  This may seem a bit hacky but it
-    // allows us to use one program effects object for all of our programs which really simplifies
-    // the code overall
-    ifp->fTransforms.push_back_n(1);
-    ifp->fTransforms[0].fHandle = GrGLInstalledFragProc::ShaderVarHandle(texCoordIndex);
-
-    SkString name;
-    for (int t = 0; t < numTransforms; ++t) {
-        GrSLType type = processorStage.isPerspectiveCoordTransform(t) ? kVec3f_GrSLType :
-                                                                        kVec2f_GrSLType;
-
-        name.printf("%s(gl_TexCoord[%i])", GrGLSLTypeString(type), texCoordIndex++);
-        SkNEW_APPEND_TO_TARRAY(outCoords, GrGLProcessor::TransformedCoords, (name, type));
-    }
-}
-
-GrGLProgram* GrGLLegacyNvprProgramBuilder::createProgram(GrGLuint programID) {
-    return SkNEW_ARGS(GrGLLegacyNvprProgram, (fGpu, fDesc, fUniformHandles, programID, fUniforms,
-            fGeometryProcessor, fXferProcessor, fFragmentProcessors.get(),
-                                              fTexCoordSetCnt));
-}
diff --git a/src/gpu/gl/builders/GrGLLegacyNvprProgramBuilder.h b/src/gpu/gl/builders/GrGLLegacyNvprProgramBuilder.h
deleted file mode 100644
index 519e87a..0000000
--- a/src/gpu/gl/builders/GrGLLegacyNvprProgramBuilder.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright 2014 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef GrGLLegacyNvprProgramBuilder_DEFINED
-#define GrGLLegacyNvprProgramBuilder_DEFINED
-
-#include "GrGLProgramBuilder.h"
-
-class GrGLLegacyNvprProgramBuilder : public GrGLProgramBuilder {
-public:
-    GrGLLegacyNvprProgramBuilder(GrGLGpu*, const GrOptDrawState&);
-
-    GrGLProgram* createProgram(GrGLuint programID) SK_OVERRIDE;
-
-private:
-    int addTexCoordSets(int count);
-    void emitTransforms(const GrPendingFragmentStage&,
-                        GrGLProcessor::TransformedCoordsArray* outCoords,
-                        GrGLInstalledFragProc*) SK_OVERRIDE;
-
-    int fTexCoordSetCnt;
-
-    typedef GrGLProgramBuilder INHERITED;
-};
-
-#endif
diff --git a/src/gpu/gl/builders/GrGLNvprProgramBuilder.cpp b/src/gpu/gl/builders/GrGLNvprProgramBuilder.cpp
deleted file mode 100644
index e50037d..0000000
--- a/src/gpu/gl/builders/GrGLNvprProgramBuilder.cpp
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright 2014 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "GrGLNvprProgramBuilder.h"
-#include "../GrGLGpu.h"
-
-#define GL_CALL(X) GR_GL_CALL(this->gpu()->glInterface(), X)
-#define GL_CALL_RET(R, X) GR_GL_CALL_RET(this->gpu()->glInterface(), R, X)
-
-GrGLNvprProgramBuilder::GrGLNvprProgramBuilder(GrGLGpu* gpu,
-                                               const GrOptDrawState& optState)
-        : INHERITED(gpu, optState)
-        , fSeparableVaryingInfos(kVarsPerBlock) {
-}
-
-void GrGLNvprProgramBuilder::emitTransforms(const GrPendingFragmentStage& processorStage,
-                                            GrGLProcessor::TransformedCoordsArray* outCoords,
-                                            GrGLInstalledFragProc* ifp) {
-    const GrFragmentProcessor* effect = processorStage.processor();
-    int numTransforms = effect->numTransforms();
-
-    ifp->fTransforms.push_back_n(numTransforms);
-
-    for (int t = 0; t < numTransforms; t++) {
-        GrSLType varyingType =
-                processorStage.isPerspectiveCoordTransform(t) ?
-                        kVec3f_GrSLType :
-                        kVec2f_GrSLType;
-
-        const char* varyingName = "MatrixCoord";
-        SkString suffixedVaryingName;
-        if (0 != t) {
-            suffixedVaryingName.append(varyingName);
-            suffixedVaryingName.appendf("_%i", t);
-            varyingName = suffixedVaryingName.c_str();
-        }
-        GrGLVertToFrag v(varyingType);
-        ifp->fTransforms[t].fHandle = this->addSeparableVarying(varyingName, &v);
-        ifp->fTransforms[t].fType = varyingType;
-
-        SkNEW_APPEND_TO_TARRAY(outCoords, GrGLProcessor::TransformedCoords,
-                               (SkString(v.fsIn()), varyingType));
-    }
-}
-
-GrGLInstalledFragProc::ShaderVarHandle
-GrGLNvprProgramBuilder::addSeparableVarying(const char* name, GrGLVarying* v) {
-    this->addVarying(name, v);
-    SeparableVaryingInfo& varying = fSeparableVaryingInfos.push_back();
-    varying.fVariable = fFS.fInputs.back();
-    return GrGLInstalledFragProc::ShaderVarHandle(fSeparableVaryingInfos.count() - 1);
-}
-
-void GrGLNvprProgramBuilder::resolveSeparableVaryings(GrGLuint programId) {
-    int count = fSeparableVaryingInfos.count();
-    for (int i = 0; i < count; ++i) {
-        GrGLint location;
-        GL_CALL_RET(location,
-                    GetProgramResourceLocation(programId,
-                                               GR_GL_FRAGMENT_INPUT,
-                                               fSeparableVaryingInfos[i].fVariable.c_str()));
-        fSeparableVaryingInfos[i].fLocation = location;
-    }
-}
-
-GrGLProgram* GrGLNvprProgramBuilder::createProgram(GrGLuint programID) {
-    // this is just for nvpr es, which has separable varyings that are plugged in after
-    // building
-    this->resolveSeparableVaryings(programID);
-    return SkNEW_ARGS(GrGLNvprProgram, (fGpu, fDesc, fUniformHandles, programID, fUniforms,
-                                        fGeometryProcessor,
-                                        fXferProcessor, fFragmentProcessors.get(),
-                                        fSeparableVaryingInfos));
-}
diff --git a/src/gpu/gl/builders/GrGLNvprProgramBuilder.h b/src/gpu/gl/builders/GrGLNvprProgramBuilder.h
deleted file mode 100644
index 83a3558..0000000
--- a/src/gpu/gl/builders/GrGLNvprProgramBuilder.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright 2014 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef GrGLNvprProgramBuilder_DEFINED
-#define GrGLNvprProgramBuilder_DEFINED
-
-#include "GrGLProgramBuilder.h"
-
-class GrGLNvprProgramBuilder : public GrGLProgramBuilder {
-public:
-    GrGLNvprProgramBuilder(GrGLGpu*, const GrOptDrawState&);
-
-    /*
-     * The separable varying info must be passed to GrGLProgram so this must
-     * be part of the public interface
-     */
-    struct SeparableVaryingInfo {
-        GrGLShaderVar fVariable;
-        GrGLint       fLocation;
-    };
-
-    typedef GrTAllocator<SeparableVaryingInfo> SeparableVaryingInfoArray;
-
-    GrGLProgram* createProgram(GrGLuint programID) SK_OVERRIDE;
-
-private:
-    virtual void emitTransforms(const GrPendingFragmentStage&,
-                                GrGLProcessor::TransformedCoordsArray* outCoords,
-                                GrGLInstalledFragProc*) SK_OVERRIDE;
-
-    typedef GrGLInstalledFragProc::ShaderVarHandle ShaderVarHandle;
-
-    /**
-     * Add a separable varying input variable to the current program.
-     * A separable varying (fragment shader input) is a varying that can be used also when vertex
-     * shaders are not used. With a vertex shader, the operation is same as with other
-     * varyings. Without a vertex shader, such as with NV_path_rendering, GL APIs are used to
-     * populate the variable. The APIs can refer to the variable through the returned handle.
-     */
-    ShaderVarHandle addSeparableVarying(const char* name, GrGLVarying* v);
-
-    void resolveSeparableVaryings(GrGLuint programId);
-
-    SeparableVaryingInfoArray fSeparableVaryingInfos;
-
-    typedef GrGLProgramBuilder INHERITED;
-};
-
-#endif
diff --git a/src/gpu/gl/builders/GrGLProgramBuilder.cpp b/src/gpu/gl/builders/GrGLProgramBuilder.cpp
index 0c41781..147723b 100644
--- a/src/gpu/gl/builders/GrGLProgramBuilder.cpp
+++ b/src/gpu/gl/builders/GrGLProgramBuilder.cpp
@@ -12,8 +12,6 @@
 #include "../GrGLXferProcessor.h"
 #include "../GrGLGpu.h"
 #include "GrCoordTransform.h"
-#include "GrGLLegacyNvprProgramBuilder.h"
-#include "GrGLNvprProgramBuilder.h"
 #include "GrGLProgramBuilder.h"
 #include "GrTexture.h"
 #include "SkRTConf.h"
@@ -22,6 +20,30 @@
 #define GL_CALL(X) GR_GL_CALL(this->gpu()->glInterface(), X)
 #define GL_CALL_RET(R, X) GR_GL_CALL_RET(this->gpu()->glInterface(), R, X)
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+class GrGLNvprProgramBuilder : public GrGLProgramBuilder {
+public:
+    GrGLNvprProgramBuilder(GrGLGpu* gpu, const GrOptDrawState& optState)
+        : INHERITED(gpu, optState) {}
+
+    GrGLProgram* createProgram(GrGLuint programID) SK_OVERRIDE {
+        // this is just for nvpr es, which has separable varyings that are plugged in after
+        // building
+        GrGLPathProcessor* pathProc =
+                static_cast<GrGLPathProcessor*>(fGeometryProcessor->fGLProc.get());
+        pathProc->resolveSeparableVaryings(fGpu, programID);
+        return SkNEW_ARGS(GrGLNvprProgram, (fGpu, fDesc, fUniformHandles, programID, fUniforms,
+                                            fGeometryProcessor,
+                                            fXferProcessor, fFragmentProcessors.get()));
+    }
+
+private:
+    typedef GrGLProgramBuilder INHERITED;
+};
+
+
+
 //////////////////////////////////////////////////////////////////////////////
 
 const int GrGLProgramBuilder::kVarsPerBlock = 8;
@@ -29,12 +51,11 @@
 GrGLProgram* GrGLProgramBuilder::CreateProgram(const GrOptDrawState& optState, GrGLGpu* gpu) {
     // create a builder.  This will be handed off to effects so they can use it to add
     // uniforms, varyings, textures, etc
-    SkAutoTDelete<GrGLProgramBuilder> builder(CreateProgramBuilder(optState,
-                                                                   optState.hasGeometryProcessor(),
-                                                                   gpu));
+    SkAutoTDelete<GrGLProgramBuilder> builder(CreateProgramBuilder(optState, gpu));
 
     GrGLProgramBuilder* pb = builder.get();
-    const GrGLProgramDescBuilder::GLKeyHeader& header = GrGLProgramDescBuilder::GetHeader(pb->desc());
+    const GrGLProgramDescBuilder::GLKeyHeader& header =
+            GrGLProgramDescBuilder::GetHeader(pb->desc());
 
     // emit code to read the dst copy texture, if necessary
     if (GrGLFragmentShaderBuilder::kNoDstRead_DstReadKey != header.fDstReadKey &&
@@ -53,18 +74,12 @@
 }
 
 GrGLProgramBuilder* GrGLProgramBuilder::CreateProgramBuilder(const GrOptDrawState& optState,
-                                                             bool hasGeometryProcessor,
                                                              GrGLGpu* gpu) {
     const GrProgramDesc& desc = optState.programDesc();
     if (GrGLProgramDescBuilder::GetHeader(desc).fUseNvpr) {
         SkASSERT(gpu->glCaps().pathRenderingSupport());
-        SkASSERT(!hasGeometryProcessor);
-        if (gpu->glPathRendering()->texturingMode() ==
-            GrGLPathRendering::FixedFunction_TexturingMode) {
-            return SkNEW_ARGS(GrGLLegacyNvprProgramBuilder, (gpu, optState));
-        } else {
-            return SkNEW_ARGS(GrGLNvprProgramBuilder, (gpu, optState));
-        }
+        SkASSERT(!optState.hasGeometryProcessor());
+        return SkNEW_ARGS(GrGLNvprProgramBuilder, (gpu, optState));
     } else {
         return SkNEW_ARGS(GrGLProgramBuilder, (gpu, optState));
     }
@@ -177,19 +192,15 @@
 }
 
 void GrGLProgramBuilder::emitAndInstallProcs(GrGLSLExpr4* inputColor, GrGLSLExpr4* inputCoverage) {
-    if (fOptState.hasGeometryProcessor()) {
-        fVS.codeAppend("gl_PointSize = 1.0;");
-
-        // Setup position
-        // TODO it'd be possible to remove these from the vertexshader builder and have them
-        // be outputs from the emit call.  We don't do this because emitargs is constant.  It would
-        // be easy to change this though
-        fVS.codeAppendf("vec3 %s;", fVS.glPosition());
-        fVS.codeAppendf("vec2 %s;", fVS.positionCoords());
-        fVS.codeAppendf("vec2 %s;", fVS.localCoords());
-
-        const GrGeometryProcessor& gp = *fOptState.getGeometryProcessor();
-        fVS.emitAttributes(gp);
+    // First we loop over all of the installed processors and collect coord transforms.  These will
+    // be sent to the GrGLPrimitiveProcessor in its emitCode function
+    SkSTArray<8, GrGLProcessor::TransformedCoordsArray> outCoords;
+    for (int i = 0; i < fOptState.numFragmentStages(); i++) {
+        const GrFragmentProcessor* processor = fOptState.getFragmentStage(i).processor();
+        SkSTArray<2, const GrCoordTransform*, true>& procCoords = fCoordTransforms.push_back();
+        for (int t = 0; t < processor->numTransforms(); t++) {
+            procCoords.push_back(&processor->coordTransform(t));
+        }
     }
 
     const GrPrimitiveProcessor& primProc = *fOptState.getPrimitiveProcessor();
@@ -199,11 +210,6 @@
     int numProcs = fOptState.numFragmentStages();
     this->emitAndInstallFragProcs(0, fOptState.numColorStages(), inputColor);
     this->emitAndInstallFragProcs(fOptState.numColorStages(), numProcs,  inputCoverage);
-
-    if (fOptState.hasGeometryProcessor()) {
-        fVS.transformToNormalizedDeviceSpace();
-    }
-
     this->emitAndInstallXferProc(*fOptState.getXferProcessor(), *inputColor, *inputCoverage);
 }
 
@@ -247,7 +253,7 @@
     openBrace.printf("{ // Stage %d, %s\n", fStageIndex, proc.name());
     fFS.codeAppend(openBrace.c_str());
 
-    this->emitAndInstallProc(proc, output->c_str(), input.isOnes() ? NULL : input.c_str());
+    this->emitAndInstallProc(proc, index, output->c_str(), input.isOnes() ? NULL : input.c_str());
 
     fFS.codeAppend("}");
 }
@@ -271,6 +277,7 @@
 }
 
 void GrGLProgramBuilder::emitAndInstallProc(const GrPendingFragmentStage& fs,
+                                            int index,
                                             const char* outColor,
                                             const char* inColor) {
     GrGLInstalledFragProc* ifp = SkNEW(GrGLInstalledFragProc);
@@ -281,11 +288,7 @@
     SkSTArray<4, GrGLProcessor::TextureSampler> samplers(fp.numTextures());
     this->emitSamplers(fp, &samplers, ifp);
 
-    // Fragment processors can have coord transforms
-    SkSTArray<2, GrGLProcessor::TransformedCoords> coords(fp.numTransforms());
-    this->emitTransforms(fs, &coords, ifp);
-
-    ifp->fGLProc->emitCode(this, fp, outColor, inColor, coords, samplers);
+    ifp->fGLProc->emitCode(this, fp, outColor, inColor, fOutCoords[index], samplers);
 
     // We have to check that effects and the code they emit are consistent, ie if an effect
     // asks for dst color, then the emit code needs to follow suit
@@ -300,12 +303,13 @@
     fGeometryProcessor = SkNEW(GrGLInstalledGeoProc);
 
     const GrBatchTracker& bt = fOptState.getBatchTracker();
-    fGeometryProcessor->fGLProc.reset(gp.createGLInstance(bt));
+    fGeometryProcessor->fGLProc.reset(gp.createGLInstance(bt, fGpu->glCaps()));
 
     SkSTArray<4, GrGLProcessor::TextureSampler> samplers(gp.numTextures());
     this->emitSamplers(gp, &samplers, fGeometryProcessor);
 
-    GrGLGeometryProcessor::EmitArgs args(this, gp, bt, outColor, outCoverage, samplers);
+    GrGLGeometryProcessor::EmitArgs args(this, gp, bt, outColor, outCoverage, samplers,
+                                         fCoordTransforms, &fOutCoords);
     fGeometryProcessor->fGLProc->emitCode(args);
 
     // We have to check that effects and the code they emit are consistent, ie if an effect
@@ -367,59 +371,10 @@
     SkASSERT(fFS.hasReadDstColor() == fp.willReadDstColor());
 }
 
-void GrGLProgramBuilder::emitTransforms(const GrPendingFragmentStage& stage,
-                                        GrGLProcessor::TransformedCoordsArray* outCoords,
-                                        GrGLInstalledFragProc* ifp) {
-    const GrFragmentProcessor* processor = stage.processor();
-    int numTransforms = processor->numTransforms();
-    ifp->fTransforms.push_back_n(numTransforms);
-
-    for (int t = 0; t < numTransforms; t++) {
-        const char* uniName = "StageMatrix";
-        GrSLType varyingType;
-
-        GrCoordSet coordType = processor->coordTransform(t).sourceCoords();
-        const SkMatrix& localMatrix = fOptState.getPrimitiveProcessor()->localMatrix();
-        uint32_t type = processor->coordTransform(t).getMatrix().getType();
-        if (kLocal_GrCoordSet == coordType) {
-            type |= localMatrix.getType();
-        }
-        varyingType = SkToBool(SkMatrix::kPerspective_Mask & type) ? kVec3f_GrSLType :
-                                                                     kVec2f_GrSLType;
-        GrSLPrecision precision = processor->coordTransform(t).precision();
-
-        SkString suffixedUniName;
-        if (0 != t) {
-            suffixedUniName.append(uniName);
-            suffixedUniName.appendf("_%i", t);
-            uniName = suffixedUniName.c_str();
-        }
-        ifp->fTransforms[t].fHandle = this->addUniform(GrGLProgramBuilder::kVertex_Visibility,
-                                                       kMat33f_GrSLType, precision,
-                                                       uniName,
-                                                       &uniName).toShaderBuilderIndex();
-
-        const char* varyingName = "MatrixCoord";
-        SkString suffixedVaryingName;
-        if (0 != t) {
-            suffixedVaryingName.append(varyingName);
-            suffixedVaryingName.appendf("_%i", t);
-            varyingName = suffixedVaryingName.c_str();
-        }
-
-        GrGLVertToFrag v(varyingType);
-        this->addVarying(varyingName, &v, precision);
-        fCoordVaryings.push_back(TransformVarying(v, uniName, coordType));
-
-        SkASSERT(kVec2f_GrSLType == varyingType || kVec3f_GrSLType == varyingType);
-        SkNEW_APPEND_TO_TARRAY(outCoords, GrGLProcessor::TransformedCoords,
-                               (SkString(v.fsIn()), varyingType));
-    }
-}
-
+template <class Proc>
 void GrGLProgramBuilder::emitSamplers(const GrProcessor& processor,
                                       GrGLProcessor::TextureSamplerArray* outSamplers,
-                                      GrGLInstalledProc* ip) {
+                                      GrGLInstalledProc<Proc>* ip) {
     int numTextures = processor.numTextures();
     ip->fSamplers.push_back_n(numTextures);
     SkString name;
diff --git a/src/gpu/gl/builders/GrGLProgramBuilder.h b/src/gpu/gl/builders/GrGLProgramBuilder.h
index fd1d6c8..37e678f 100644
--- a/src/gpu/gl/builders/GrGLProgramBuilder.h
+++ b/src/gpu/gl/builders/GrGLProgramBuilder.h
@@ -89,6 +89,7 @@
     const char* gsIn() const { return fGsIn; }
     const char* gsOut() const { return fGsOut; }
     const char* fsIn() const { return fFsIn; }
+    GrSLType type() const { return fType; }
 
 protected:
     enum Varying {
@@ -181,11 +182,31 @@
      * *NOTE* NO MEMBERS ALLOWED, MULTIPLE INHERITANCE
      */
 };
-struct GrGLInstalledProc;
-struct GrGLInstalledGeoProc;
-struct GrGLInstalledXferProc;
-struct GrGLInstalledFragProc;
-struct GrGLInstalledFragProcs;
+
+/**
+ * The below struct represent processors installed in programs.
+ */
+template <class Proc>
+struct GrGLInstalledProc {
+     typedef GrGLProgramDataManager::UniformHandle UniformHandle;
+
+     struct Sampler {
+         SkDEBUGCODE(Sampler() : fTextureUnit(-1) {})
+         UniformHandle  fUniform;
+         int            fTextureUnit;
+     };
+     SkSTArray<4, Sampler, true> fSamplers;
+     SkAutoTDelete<Proc> fGLProc;
+};
+
+typedef GrGLInstalledProc<GrGLPrimitiveProcessor> GrGLInstalledGeoProc;
+typedef GrGLInstalledProc<GrGLXferProcessor> GrGLInstalledXferProc;
+typedef GrGLInstalledProc<GrGLFragmentProcessor> GrGLInstalledFragProc;
+
+struct GrGLInstalledFragProcs : public SkRefCnt {
+    virtual ~GrGLInstalledFragProcs();
+    SkSTArray<8, GrGLInstalledFragProc*, true> fProcs;
+};
 
 /*
  * Please note - no diamond problems because of virtual inheritance.  Also, both base classes
@@ -256,9 +277,7 @@
     typedef GrGLProgramDataManager::UniformInfo UniformInfo;
     typedef GrGLProgramDataManager::UniformInfoArray UniformInfoArray;
 
-    static GrGLProgramBuilder* CreateProgramBuilder(const GrOptDrawState&,
-                                                    bool hasGeometryProcessor,
-                                                    GrGLGpu*);
+    static GrGLProgramBuilder* CreateProgramBuilder(const GrOptDrawState&, GrGLGpu*);
 
     GrGLProgramBuilder(GrGLGpu*, const GrOptDrawState&);
 
@@ -287,6 +306,7 @@
 
     // these emit functions help to keep the createAndEmitProcessors template general
     void emitAndInstallProc(const GrPendingFragmentStage&,
+                            int index,
                             const char* outColor,
                             const char* inColor);
     void emitAndInstallProc(const GrPrimitiveProcessor&,
@@ -299,14 +319,11 @@
     void verify(const GrPrimitiveProcessor&);
     void verify(const GrXferProcessor&);
     void verify(const GrFragmentProcessor&);
+    template <class Proc>
     void emitSamplers(const GrProcessor&,
                       GrGLProcessor::TextureSamplerArray* outSamplers,
-                      GrGLInstalledProc*);
+                      GrGLInstalledProc<Proc>*);
 
-    // each specific program builder has a distinct transform and must override this function
-    virtual void emitTransforms(const GrPendingFragmentStage&,
-                                GrGLProcessor::TransformedCoordsArray* outCoords,
-                                GrGLInstalledFragProc*);
     GrGLProgram* finalize();
     void bindUniformLocations(GrGLuint programID);
     bool checkLinkStatus(GrGLuint programID);
@@ -350,14 +367,6 @@
     void enterStage() { fOutOfStage = false; }
     int stageIndex() const { return fStageIndex; }
 
-    struct TransformVarying {
-        TransformVarying(const GrGLVarying& v, const char* uniName, GrCoordSet coordSet)
-            : fV(v), fUniName(uniName), fCoordSet(coordSet) {}
-        GrGLVarying fV;
-        SkString fUniName;
-        GrCoordSet fCoordSet;
-    };
-
     const char* rtAdjustment() const { return "rtAdjustment"; }
 
     // number of each input/output type in a single allocation block, used by many builders
@@ -378,68 +387,12 @@
     const GrProgramDesc& fDesc;
     GrGLGpu* fGpu;
     UniformInfoArray fUniforms;
-    SkSTArray<16, TransformVarying, true> fCoordVaryings;
+    GrGLPrimitiveProcessor::TransformsIn fCoordTransforms;
+    GrGLPrimitiveProcessor::TransformsOut fOutCoords;
 
     friend class GrGLShaderBuilder;
     friend class GrGLVertexBuilder;
     friend class GrGLFragmentShaderBuilder;
     friend class GrGLGeometryBuilder;
 };
-
-/**
- * The below structs represent processors installed in programs.  All processors can have texture
- * samplers, but only frag processors have coord transforms, hence the need for different structs
- */
-struct GrGLInstalledProc {
-     typedef GrGLProgramDataManager::UniformHandle UniformHandle;
-
-     struct Sampler {
-         SkDEBUGCODE(Sampler() : fTextureUnit(-1) {})
-         UniformHandle  fUniform;
-         int            fTextureUnit;
-     };
-     SkSTArray<4, Sampler, true> fSamplers;
-};
-
-struct GrGLInstalledGeoProc : public GrGLInstalledProc {
-    SkAutoTDelete<GrGLGeometryProcessor> fGLProc;
-};
-
-struct GrGLInstalledXferProc : public GrGLInstalledProc {
-    SkAutoTDelete<GrGLXferProcessor> fGLProc;
-};
-
-struct GrGLInstalledFragProc : public GrGLInstalledProc {
-    GrGLInstalledFragProc() : fGLProc(NULL) {}
-    class ShaderVarHandle {
-    public:
-        bool isValid() const { return fHandle > -1; }
-        ShaderVarHandle() : fHandle(-1) {}
-        ShaderVarHandle(int value) : fHandle(value) { SkASSERT(this->isValid()); }
-        int handle() const { SkASSERT(this->isValid()); return fHandle; }
-        UniformHandle convertToUniformHandle() {
-            SkASSERT(this->isValid());
-            return GrGLProgramDataManager::UniformHandle::CreateFromUniformIndex(fHandle);
-        }
-
-    private:
-        int fHandle;
-    };
-
-    struct Transform {
-        Transform() : fType(kVoid_GrSLType) { fCurrentValue = SkMatrix::InvalidMatrix(); }
-        ShaderVarHandle fHandle;
-        SkMatrix       fCurrentValue;
-        GrSLType       fType;
-    };
-
-    SkAutoTDelete<GrGLFragmentProcessor> fGLProc;
-    SkSTArray<2, Transform, true>        fTransforms;
-};
-
-struct GrGLInstalledFragProcs : public SkRefCnt {
-    virtual ~GrGLInstalledFragProcs();
-    SkSTArray<8, GrGLInstalledFragProc*, true> fProcs;
-};
-
 #endif
diff --git a/src/gpu/gl/builders/GrGLVertexShaderBuilder.cpp b/src/gpu/gl/builders/GrGLVertexShaderBuilder.cpp
index fd79abb..ead0edf 100644
--- a/src/gpu/gl/builders/GrGLVertexShaderBuilder.cpp
+++ b/src/gpu/gl/builders/GrGLVertexShaderBuilder.cpp
@@ -35,52 +35,23 @@
     return;
 }
 
-void GrGLVertexBuilder::transformToNormalizedDeviceSpace() {
+void GrGLVertexBuilder::transformToNormalizedDeviceSpace(const char* pos3) {
+    SkASSERT(!fRtAdjustName);
+
     // setup RT Uniform
     fProgramBuilder->fUniformHandles.fRTAdjustmentUni =
             fProgramBuilder->addUniform(GrGLProgramBuilder::kVertex_Visibility,
                                         kVec4f_GrSLType, kDefault_GrSLPrecision,
                                         fProgramBuilder->rtAdjustment(),
                                         &fRtAdjustName);
-    // Wire transforms
-    SkTArray<GrGLProgramBuilder::TransformVarying, true>& transVs = fProgramBuilder->fCoordVaryings;
-    int transformCount = transVs.count();
-    for (int i = 0; i < transformCount; i++) {
-        GrCoordSet coordSet = transVs[i].fCoordSet;
-        const char* coords = NULL;
-        switch (coordSet) {
-            case kLocal_GrCoordSet:
-                coords = this->localCoords();
-                break;
-            case kDevice_GrCoordSet:
-                coords = this->glPosition();
-                break;
-        }
-
-        // varying = matrix * coords (logically)
-        const GrGLVarying& v = transVs[i].fV;
-        if (kDevice_GrCoordSet == coordSet) {
-            if (kVec2f_GrSLType == v.fType) {
-                this->codeAppendf("%s = (%s * %s).xy;", v.fVsOut, transVs[i].fUniName.c_str(),
-                                  coords);
-            } else {
-                this->codeAppendf("%s = %s * %s;", v.fVsOut, transVs[i].fUniName.c_str(), coords);
-            }
-        } else {
-            if (kVec2f_GrSLType == v.fType) {
-                this->codeAppendf("%s = (%s * vec3(%s, 1)).xy;", v.fVsOut, transVs[i].fUniName.c_str(),
-                                  coords);
-            } else {
-                this->codeAppendf("%s = %s * vec3(%s, 1);", v.fVsOut, transVs[i].fUniName.c_str(),
-                                  coords);
-            }
-        }
-    }
 
     // Transform from Skia's device coords to GL's normalized device coords.
     this->codeAppendf("gl_Position = vec4(dot(%s.xz, %s.xy), dot(%s.yz, %s.zw), 0, %s.z);",
-                      this->glPosition(), fRtAdjustName, this->glPosition(), fRtAdjustName,
-                      this->glPosition());
+                      pos3, fRtAdjustName, pos3, fRtAdjustName, pos3);
+
+    // We could have the GrGeometryProcessor do this, but its just easier to have it performed here.
+    // If we ever need to set variable pointsize, then we can reinvestigate
+    this->codeAppend("gl_PointSize = 1.0;");
 }
 
 void GrGLVertexBuilder::bindVertexAttributes(GrGLuint programID) {
diff --git a/src/gpu/gl/builders/GrGLVertexShaderBuilder.h b/src/gpu/gl/builders/GrGLVertexShaderBuilder.h
index ba978a8..da094b5 100644
--- a/src/gpu/gl/builders/GrGLVertexShaderBuilder.h
+++ b/src/gpu/gl/builders/GrGLVertexShaderBuilder.h
@@ -16,10 +16,8 @@
 public:
     GrGLVertexBuilder(GrGLProgramBuilder* program);
 
-    /** returns the expected position output */
-    const char* glPosition() const { return "pos3"; }
-    const char* positionCoords() const { return "position"; }
-    const char* localCoords() const { return "localCoords"; }
+    void transformToNormalizedDeviceSpace(const char* pos3);
+    void emitAttributes(const GrGeometryProcessor& gp);
 
     void addAttribute(const GrGeometryProcessor::GrAttribute* attr) {
         this->addAttribute(GrShaderVar(attr->fName,
@@ -36,8 +34,6 @@
     /*
      * private helpers for compilation by GrGLProgramBuilder
      */
-    void transformToNormalizedDeviceSpace();
-    void emitAttributes(const GrGeometryProcessor& gp);
     void bindVertexAttributes(GrGLuint programID);
     bool compileAndAttachShaders(GrGLuint programId, SkTDArray<GrGLuint>* shaderIds) const;