Use vertexless shaders when NVpr is available

Adds support for vertexless shaders and enables them when
NV_path_rendering is available. This takes a
GrGLFragmentOnlyShaderBuilder class, a GrGLTexGenEffectArray class,
support for setting TexGen and the projection matrix in GrGpuGL, and
code for setting the GL fixed function state where necessary.

R=bsalomon@google.com, kkinnunen@nvidia.com

Author: cdalton@nvidia.com

Review URL: https://codereview.chromium.org/25846002

git-svn-id: http://skia.googlecode.com/svn/trunk@11620 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/gpu/gl/GrGLProgram.cpp b/src/gpu/gl/GrGLProgram.cpp
index 4fe7a2b..1ca61e3 100644
--- a/src/gpu/gl/GrGLProgram.cpp
+++ b/src/gpu/gl/GrGLProgram.cpp
@@ -39,7 +39,9 @@
                          const GrEffectStage* colorStages[],
                          const GrEffectStage* coverageStages[])
 : fGpu(gpu)
-, fUniformManager(gpu) {
+, fUniformManager(gpu)
+, fHasVertexShader(false)
+, fNumTexCoordSets(0) {
     fDesc = desc;
     fProgramID = 0;
 
@@ -48,7 +50,21 @@
     fColor = GrColor_ILLEGAL;
     fColorFilterColor = GrColor_ILLEGAL;
 
-    this->genProgram(colorStages, coverageStages);
+    if (fDesc.getHeader().fHasVertexCode ||
+        !fGpu->glCaps().fixedFunctionSupport() ||
+        !fGpu->glCaps().pathStencilingSupport()) {
+
+        GrGLFullShaderBuilder fullBuilder(fGpu, fUniformManager, fDesc);
+        if (this->genProgram(&fullBuilder, colorStages, coverageStages)) {
+            fUniformHandles.fViewMatrixUni = fullBuilder.getViewMatrixUniform();
+            fHasVertexShader = true;
+        }
+    } else {
+        GrGLFragmentOnlyShaderBuilder fragmentOnlyBuilder(fGpu, fUniformManager, fDesc);
+        if (this->genProgram(&fragmentOnlyBuilder, colorStages, coverageStages)) {
+            fNumTexCoordSets = fragmentOnlyBuilder.getNumTexCoordSets();
+        }
+    }
 }
 
 GrGLProgram::~GrGLProgram() {
@@ -205,18 +221,16 @@
 
 }
 
-bool GrGLProgram::genProgram(const GrEffectStage* colorStages[],
+bool GrGLProgram::genProgram(GrGLShaderBuilder* builder,
+                             const GrEffectStage* colorStages[],
                              const GrEffectStage* coverageStages[]) {
     SkASSERT(0 == fProgramID);
 
     const GrGLProgramDesc::KeyHeader& header = fDesc.getHeader();
 
-    GrGLFullShaderBuilder builder(fGpu, fUniformManager, fDesc);
-    fUniformHandles.fViewMatrixUni = builder.getViewMatrixUniform();
-
     // incoming color to current stage being processed.
-    SkString inColor = builder.getInputColor();
-    GrSLConstantVec knownColorValue = builder.getKnownColorValue();
+    SkString inColor = builder->getInputColor();
+    GrSLConstantVec knownColorValue = builder->getKnownColorValue();
 
     // Get the coeffs for the Mode-based color filter, determine if color is needed.
     SkXfermode::Coeff colorCoeff;
@@ -229,20 +243,20 @@
     need_blend_inputs(filterColorCoeff, colorCoeff, &needFilterColor, &needColor);
 
     fColorEffects.reset(
-        builder.createAndEmitEffects(colorStages,
-                                     fDesc.effectKeys(),
-                                     needColor ? fDesc.numColorEffects() : 0,
-                                     &inColor,
-                                     &knownColorValue));
+        builder->createAndEmitEffects(colorStages,
+                                      fDesc.effectKeys(),
+                                      needColor ? fDesc.numColorEffects() : 0,
+                                      &inColor,
+                                      &knownColorValue));
 
     // Insert the color filter. This will soon be replaced by a color effect.
     if (SkXfermode::kDst_Mode != header.fColorFilterXfermode) {
         const char* colorFilterColorUniName = NULL;
-        fUniformHandles.fColorFilterUni = builder.addUniform(GrGLShaderBuilder::kFragment_Visibility,
-                                                             kVec4f_GrSLType, "FilterColor",
-                                                             &colorFilterColorUniName);
+        fUniformHandles.fColorFilterUni = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
+                                                              kVec4f_GrSLType, "FilterColor",
+                                                              &colorFilterColorUniName);
 
-        builder.fsCodeAppend("\tvec4 filteredColor;\n");
+        builder->fsCodeAppend("\tvec4 filteredColor;\n");
         const char* color;
         // add_color_filter requires a real input string.
         if (knownColorValue == kOnes_GrSLConstantVec) {
@@ -252,36 +266,36 @@
         } else {
             color = inColor.c_str();
         }
-        add_color_filter(&builder, "filteredColor", filterColorCoeff,
+        add_color_filter(builder, "filteredColor", filterColorCoeff,
                          colorCoeff, colorFilterColorUniName, color);
         inColor = "filteredColor";
     }
 
     ///////////////////////////////////////////////////////////////////////////
     // compute the partial coverage
-    SkString inCoverage = builder.getInputCoverage();
-    GrSLConstantVec knownCoverageValue = builder.getKnownCoverageValue();
+    SkString inCoverage = builder->getInputCoverage();
+    GrSLConstantVec knownCoverageValue = builder->getKnownCoverageValue();
 
     fCoverageEffects.reset(
-        builder.createAndEmitEffects(coverageStages,
-                                     fDesc.getEffectKeys() + fDesc.numColorEffects(),
-                                     fDesc.numCoverageEffects(),
-                                     &inCoverage,
-                                     &knownCoverageValue));
+        builder->createAndEmitEffects(coverageStages,
+                                      fDesc.getEffectKeys() + fDesc.numColorEffects(),
+                                      fDesc.numCoverageEffects(),
+                                      &inCoverage,
+                                      &knownCoverageValue));
 
     // discard if coverage is zero
     if (header.fDiscardIfZeroCoverage && kOnes_GrSLConstantVec != knownCoverageValue) {
         if (kZeros_GrSLConstantVec == knownCoverageValue) {
             // This is unfortunate.
-            builder.fsCodeAppend("\tdiscard;\n");
+            builder->fsCodeAppend("\tdiscard;\n");
         } else {
-            builder.fsCodeAppendf("\tif (all(lessThanEqual(%s, vec4(0.0)))) {\n\t\tdiscard;\n\t}\n",
-                                  inCoverage.c_str());
+            builder->fsCodeAppendf("\tif (all(lessThanEqual(%s, vec4(0.0)))) {\n\t\tdiscard;\n\t}\n",
+                                   inCoverage.c_str());
         }
     }
 
     if (GrGLProgramDesc::CoverageOutputUsesSecondaryOutput(header.fCoverageOutput)) {
-        const char* secondaryOutputName = builder.enableSecondaryOutput();
+        const char* secondaryOutputName = builder->enableSecondaryOutput();
 
         // default coeff to ones for kCoverage_DualSrcOutput
         SkString coeff;
@@ -317,7 +331,7 @@
                            knownCoeffValue,
                            knownCoverageValue,
                            false);
-        builder.fsCodeAppendf("\t%s = %s;\n", secondaryOutputName, modulate.c_str());
+        builder->fsCodeAppendf("\t%s = %s;\n", secondaryOutputName, modulate.c_str());
     }
 
     ///////////////////////////////////////////////////////////////////////////
@@ -343,7 +357,7 @@
         SkString dstContribution;
         GrSLConstantVec knownDstContributionValue = GrGLSLModulatef<4>(&dstContribution,
                                                                        dstCoeff.c_str(),
-                                                                       builder.dstColor(),
+                                                                       builder->dstColor(),
                                                                        knownDstCoeffValue,
                                                                        kNone_GrSLConstantVec,
                                                                        true);
@@ -358,18 +372,18 @@
     } else {
         expand_known_value4f(&fragColor, knownFragColorValue);
     }
-    builder.fsCodeAppendf("\t%s = %s;\n", builder.getColorOutputName(), fragColor.c_str());
+    builder->fsCodeAppendf("\t%s = %s;\n", builder->getColorOutputName(), fragColor.c_str());
 
-    if (!builder.finish(&fProgramID)) {
+    if (!builder->finish(&fProgramID)) {
         return false;
     }
 
-    fUniformHandles.fRTHeightUni = builder.getRTHeightUniform();
-    fUniformHandles.fDstCopyTopLeftUni = builder.getDstCopyTopLeftUniform();
-    fUniformHandles.fDstCopyScaleUni = builder.getDstCopyScaleUniform();
-    fUniformHandles.fColorUni = builder.getColorUniform();
-    fUniformHandles.fCoverageUni = builder.getCoverageUniform();
-    fUniformHandles.fDstCopySamplerUni = builder.getDstCopySamplerUniform();
+    fUniformHandles.fRTHeightUni = builder->getRTHeightUniform();
+    fUniformHandles.fDstCopyTopLeftUni = builder->getDstCopyTopLeftUniform();
+    fUniformHandles.fDstCopyScaleUni = builder->getDstCopyScaleUniform();
+    fUniformHandles.fColorUni = builder->getColorUniform();
+    fUniformHandles.fCoverageUni = builder->getCoverageUniform();
+    fUniformHandles.fDstCopySamplerUni = builder->getDstCopySamplerUniform();
     // This must be called after we set fDstCopySamplerUni above.
     this->initSamplerUniforms();
 
@@ -445,6 +459,10 @@
 
     fColorEffects->setData(fGpu, fUniformManager, colorStages);
     fCoverageEffects->setData(fGpu, fUniformManager, coverageStages);
+
+    if (!fHasVertexShader) {
+        fGpu->disableUnusedTexGen(fNumTexCoordSets);
+    }
 }
 
 void GrGLProgram::setColor(const GrDrawState& drawState,
@@ -537,9 +555,13 @@
         fUniformManager.set1f(fUniformHandles.fRTHeightUni, SkIntToScalar(size.fHeight));
     }
 
-    if (fMatrixState.fRenderTargetOrigin != rt->origin() ||
-        !fMatrixState.fViewMatrix.cheapEqualTo(drawState.getViewMatrix()) ||
-        fMatrixState.fRenderTargetSize != size) {
+    if (!fHasVertexShader) {
+        SkASSERT(!fUniformHandles.fViewMatrixUni.isValid());
+        fGpu->setProjectionMatrix(drawState.getViewMatrix(), size, rt->origin());
+    } else if (fMatrixState.fRenderTargetOrigin != rt->origin() ||
+               fMatrixState.fRenderTargetSize != size ||
+               !fMatrixState.fViewMatrix.cheapEqualTo(drawState.getViewMatrix())) {
+        SkASSERT(fUniformHandles.fViewMatrixUni.isValid());
 
         fMatrixState.fViewMatrix = drawState.getViewMatrix();
         fMatrixState.fRenderTargetSize = size;
diff --git a/src/gpu/gl/GrGLProgram.h b/src/gpu/gl/GrGLProgram.h
index c8bcbf4..23ed456 100644
--- a/src/gpu/gl/GrGLProgram.h
+++ b/src/gpu/gl/GrGLProgram.h
@@ -62,6 +62,8 @@
      */
     GrGLuint programID() const { return fProgramID; }
 
+    bool hasVertexShader() const { return fHasVertexShader; }
+
     /**
      * Some GL state that is relevant to programs is not stored per-program. In particular color
      * and coverage attributes can be global state. This struct is read and updated by
@@ -160,7 +162,9 @@
      * This is the heavy initialization routine for building a GLProgram. colorStages and
      * coverageStages correspond to the output of GrGLProgramDesc::Build().
      */
-    bool genProgram(const GrEffectStage* colorStages[], const GrEffectStage* coverageStages[]);
+    bool genProgram(GrGLShaderBuilder* builder,
+                    const GrEffectStage* colorStages[],
+                    const GrEffectStage* coverageStages[]);
 
     // Sets the texture units for samplers
     void initSamplerUniforms();
@@ -195,6 +199,9 @@
     GrGLUniformManager                fUniformManager;
     UniformHandles                    fUniformHandles;
 
+    bool                              fHasVertexShader;
+    int                               fNumTexCoordSets;
+
     typedef SkRefCnt INHERITED;
 };
 
diff --git a/src/gpu/gl/GrGLProgramDesc.h b/src/gpu/gl/GrGLProgramDesc.h
index 0c7c8cf..46eed09 100644
--- a/src/gpu/gl/GrGLProgramDesc.h
+++ b/src/gpu/gl/GrGLProgramDesc.h
@@ -233,6 +233,7 @@
     friend class GrGLProgram;
     friend class GrGLShaderBuilder;
     friend class GrGLFullShaderBuilder;
+    friend class GrGLFragmentOnlyShaderBuilder;
 };
 
 #endif
diff --git a/src/gpu/gl/GrGLProgramEffects.cpp b/src/gpu/gl/GrGLProgramEffects.cpp
index d5826ab..17c666a 100644
--- a/src/gpu/gl/GrGLProgramEffects.cpp
+++ b/src/gpu/gl/GrGLProgramEffects.cpp
@@ -37,7 +37,6 @@
     kMatrixTypeKeyMask   = (1 << kMatrixTypeKeyBits) - 1,
     kPositionCoords_Flag = (1 << kMatrixTypeKeyBits),
     kTransformKeyBits    = kMatrixTypeKeyBits + 1,
-    kTransformKeyMask    = (1 << kTransformKeyBits) - 1,
 };
 
 namespace {
@@ -69,8 +68,78 @@
     return false;
 }
 
+/**
+ * Retrieves the matrix type from transformKey for the transform at transformIdx.
+ */
+MatrixType get_matrix_type(EffectKey transformKey, int transformIdx) {
+    return static_cast<MatrixType>(
+               (transformKey >> (kTransformKeyBits * transformIdx)) & kMatrixTypeKeyMask);
 }
 
+/**
+ * Retrieves the source coords from transformKey for the transform at transformIdx. It may not be
+ * the same coordinate set as the original GrCoordTransform if the position and local coords are
+ * identical for this program.
+ */
+GrCoordSet get_source_coords(EffectKey transformKey, int transformIdx) {
+    return (transformKey >> (kTransformKeyBits * transformIdx)) & kPositionCoords_Flag ?
+               kPosition_GrCoordSet :
+               kLocal_GrCoordSet;
+}
+
+/**
+ * Retrieves the final translation that a transform needs to apply to its source coords (and
+ * verifies that a translation is all it needs).
+ */
+void get_transform_translation(const GrDrawEffect& drawEffect,
+                               int transformIdx,
+                               GrGLfloat* tx,
+                               GrGLfloat* ty) {
+    const GrCoordTransform& coordTransform = (*drawEffect.effect())->coordTransform(transformIdx);
+    SkASSERT(!coordTransform.reverseY());
+    const SkMatrix& matrix = coordTransform.getMatrix();
+    if (kLocal_GrCoordSet == coordTransform.sourceCoords() &&
+        !drawEffect.programHasExplicitLocalCoords()) {
+        const SkMatrix& coordChangeMatrix = drawEffect.getCoordChangeMatrix();
+        SkASSERT(SkMatrix::kTranslate_Mask == (matrix.getType() | coordChangeMatrix.getType()));
+        *tx = SkScalarToFloat(matrix[SkMatrix::kMTransX] + coordChangeMatrix[SkMatrix::kMTransX]);
+        *ty = SkScalarToFloat(matrix[SkMatrix::kMTransY] + coordChangeMatrix[SkMatrix::kMTransY]);
+    } else {
+        SkASSERT(SkMatrix::kTranslate_Mask == matrix.getType());
+        *tx = SkScalarToFloat(matrix[SkMatrix::kMTransX]);
+        *ty = SkScalarToFloat(matrix[SkMatrix::kMTransY]);
+    }
+}
+
+/**
+ * Retrieves the final matrix that a transform needs to apply to its source coords.
+ */
+SkMatrix get_transform_matrix(const GrDrawEffect& drawEffect, int transformIdx) {
+    const GrCoordTransform& coordTransform = (*drawEffect.effect())->coordTransform(transformIdx);
+    SkMatrix combined;
+    if (kLocal_GrCoordSet == coordTransform.sourceCoords() &&
+        !drawEffect.programHasExplicitLocalCoords()) {
+        combined.setConcat(coordTransform.getMatrix(), drawEffect.getCoordChangeMatrix());
+    } 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;
+}
+
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
 EffectKey GrGLProgramEffects::GenAttribKey(const GrDrawEffect& drawEffect) {
     EffectKey key = 0;
     int numAttributes = drawEffect.getVertexAttribIndexCount();
@@ -144,6 +213,23 @@
     }
 }
 
+void GrGLProgramEffects::emitSamplers(GrGLShaderBuilder* builder,
+                                      const GrEffectRef& effect,
+                                      TextureSamplerArray* outSamplers) {
+    SkTArray<Sampler, true>& samplers = fSamplers.push_back();
+    int numTextures = effect->numTextures();
+    samplers.push_back_n(numTextures);
+    SkString name;
+    for (int t = 0; t < numTextures; ++t) {
+        name.printf("Sampler%d", t);
+        samplers[t].fUniform = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
+                                                   kSampler2D_GrSLType,
+                                                   name.c_str());
+        SkNEW_APPEND_TO_TARRAY(outSamplers, TextureSampler,
+                               (samplers[t].fUniform, effect->textureAccess(t)));
+    }
+}
+
 void GrGLProgramEffects::initSamplers(const GrGLUniformManager& uniformManager, int* texUnitIdx) {
     int numEffects = fGLEffects.count();
     SkASSERT(numEffects == fSamplers.count());
@@ -158,77 +244,6 @@
     }
 }
 
-void GrGLVertexProgramEffects::setData(GrGpuGL* gpu,
-                                       const GrGLUniformManager& uniformManager,
-                                       const GrEffectStage* effectStages[]) {
-    int numEffects = fGLEffects.count();
-    SkASSERT(numEffects == fTransforms.count());
-    SkASSERT(numEffects == fSamplers.count());
-    for (int e = 0; e < numEffects; ++e) {
-        GrDrawEffect drawEffect(*effectStages[e], fHasExplicitLocalCoords);
-        fGLEffects[e]->setData(uniformManager, drawEffect);
-        this->setTransformData(uniformManager, drawEffect, e);
-        this->bindTextures(gpu, *drawEffect.effect(), e);
-    }
-}
-
-void GrGLVertexProgramEffects::setTransformData(const GrGLUniformManager& uniformManager,
-                                                const GrDrawEffect& drawEffect,
-                                                int effectIdx) {
-    SkTArray<Transform, true>& transforms = fTransforms[effectIdx];
-    int numTransforms = transforms.count();
-    SkASSERT(numTransforms == (*drawEffect.effect())->numTransforms());
-    for (int t = 0; t < numTransforms; ++t) {
-        const GrCoordTransform& coordTransform = (*drawEffect.effect())->coordTransform(t);
-        const SkMatrix& matrix = coordTransform.getMatrix();
-        const SkMatrix& coordChangeMatrix = kLocal_GrCoordSet == coordTransform.sourceCoords() ?
-                                                drawEffect.getCoordChangeMatrix() :
-                                                SkMatrix::I();
-        SkASSERT(transforms[t].fHandle.isValid() != (kVoid_GrSLType == transforms[t].fType));
-        switch (transforms[t].fType) {
-            case kVoid_GrSLType:
-                SkASSERT(matrix.isIdentity());
-                SkASSERT(coordChangeMatrix.isIdentity());
-                SkASSERT(!coordTransform.reverseY());
-                return;
-            case kVec2f_GrSLType: {
-                SkASSERT(SkMatrix::kTranslate_Mask == (matrix.getType() | coordChangeMatrix.getType()));
-                SkASSERT(!coordTransform.reverseY());
-                SkScalar tx = matrix[SkMatrix::kMTransX] + (coordChangeMatrix)[SkMatrix::kMTransX];
-                SkScalar ty = matrix[SkMatrix::kMTransY] + (coordChangeMatrix)[SkMatrix::kMTransY];
-                if (transforms[t].fCurrentValue.get(SkMatrix::kMTransX) != tx ||
-                    transforms[t].fCurrentValue.get(SkMatrix::kMTransY) != ty) {
-                    uniformManager.set2f(transforms[t].fHandle, tx, ty);
-                    transforms[t].fCurrentValue.set(SkMatrix::kMTransX, tx);
-                    transforms[t].fCurrentValue.set(SkMatrix::kMTransY, ty);
-                }
-                break;
-            }
-            case kMat33f_GrSLType: {
-                SkMatrix combined;
-                combined.setConcat(matrix, coordChangeMatrix);
-                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]);
-                }
-                if (!transforms[t].fCurrentValue.cheapEqualTo(combined)) {
-                    uniformManager.setSkMatrix(transforms[t].fHandle, combined);
-                    transforms[t].fCurrentValue = combined;
-                }
-                break;
-            }
-            default:
-                GrCrash("Unexpected uniform type.");
-        }
-    }
-}
-
 void GrGLProgramEffects::bindTextures(GrGpuGL* gpu, const GrEffectRef& effect, int effectIdx) {
     const SkTArray<Sampler, true>& samplers = fSamplers[effectIdx];
     int numSamplers = samplers.count();
@@ -244,75 +259,67 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
-GrGLVertexProgramEffectsBuilder::GrGLVertexProgramEffectsBuilder(GrGLFullShaderBuilder* builder,
-                                                                 int reserveCount)
-    : fBuilder(builder)
-    , fProgramEffects(SkNEW_ARGS(GrGLVertexProgramEffects,
-                                 (reserveCount, fBuilder->hasExplicitLocalCoords()))) {
-}
-
-void GrGLVertexProgramEffectsBuilder::emitEffect(const GrEffectStage& stage,
-                                                 EffectKey key,
-                                                 const char* outColor,
-                                                 const char* inColor,
-                                                 int stageIndex) {
-    SkASSERT(NULL != fProgramEffects.get());
-
-    GrDrawEffect drawEffect(stage, fProgramEffects->fHasExplicitLocalCoords);
+void GrGLVertexProgramEffects::emitEffect(GrGLFullShaderBuilder* builder,
+                                          const GrEffectStage& stage,
+                                          EffectKey key,
+                                          const char* outColor,
+                                          const char* inColor,
+                                          int stageIndex) {
+    GrDrawEffect drawEffect(stage, fHasExplicitLocalCoords);
     const GrEffectRef& effect = *stage.getEffect();
     SkSTArray<2, TransformedCoords> coords(effect->numTransforms());
     SkSTArray<4, TextureSampler> samplers(effect->numTextures());
 
-    this->emitAttributes(stage);
-    this->emitTransforms(effect, key, &coords);
-    INHERITED::emitSamplers(fBuilder, fProgramEffects.get(), effect, &samplers);
+    this->emitAttributes(builder, stage);
+    this->emitTransforms(builder, effect, key, &coords);
+    this->emitSamplers(builder, effect, &samplers);
 
     GrGLEffect* glEffect = effect->getFactory().createGLInstance(drawEffect);
-    fProgramEffects->fGLEffects.push_back(glEffect);
+    fGLEffects.push_back(glEffect);
 
     // Enclose custom code in a block to avoid namespace conflicts
     SkString openBrace;
     openBrace.printf("\t{ // Stage %d: %s\n", stageIndex, glEffect->name());
-    fBuilder->vsCodeAppend(openBrace.c_str());
-    fBuilder->fsCodeAppend(openBrace.c_str());
+    builder->vsCodeAppend(openBrace.c_str());
+    builder->fsCodeAppend(openBrace.c_str());
 
     if (glEffect->isVertexEffect()) {
         GrGLVertexEffect* vertexEffect = static_cast<GrGLVertexEffect*>(glEffect);
-        vertexEffect->emitCode(fBuilder, drawEffect, key, outColor, inColor, coords, samplers);
+        vertexEffect->emitCode(builder, drawEffect, key, outColor, inColor, coords, samplers);
     } else {
-        glEffect->emitCode(fBuilder, drawEffect, key, outColor, inColor, coords, samplers);
+        glEffect->emitCode(builder, drawEffect, key, outColor, inColor, coords, samplers);
     }
 
-    fBuilder->vsCodeAppend("\t}\n");
-    fBuilder->fsCodeAppend("\t}\n");
+    builder->vsCodeAppend("\t}\n");
+    builder->fsCodeAppend("\t}\n");
 }
 
-void GrGLVertexProgramEffectsBuilder::emitAttributes(const GrEffectStage& stage) {
+void GrGLVertexProgramEffects::emitAttributes(GrGLFullShaderBuilder* builder,
+                                              const GrEffectStage& stage) {
     int numAttributes = stage.getVertexAttribIndexCount();
     const int* attributeIndices = stage.getVertexAttribIndices();
     for (int a = 0; a < numAttributes; ++a) {
         // TODO: Make addAttribute mangle the name.
         SkString attributeName("aAttr");
         attributeName.appendS32(attributeIndices[a]);
-        fBuilder->addEffectAttribute(attributeIndices[a],
-                                     (*stage.getEffect())->vertexAttribType(a),
-                                     attributeName);
+        builder->addEffectAttribute(attributeIndices[a],
+                                    (*stage.getEffect())->vertexAttribType(a),
+                                    attributeName);
     }
 }
 
-void GrGLVertexProgramEffectsBuilder::emitTransforms(const GrEffectRef& effect,
-                                                     EffectKey effectKey,
-                                                     TransformedCoordsArray* outCoords) {
-    typedef GrGLVertexProgramEffects::Transform Transform;
-    SkTArray<Transform, true>& transforms = fProgramEffects->fTransforms.push_back();
+void GrGLVertexProgramEffects::emitTransforms(GrGLFullShaderBuilder* builder,
+                                              const GrEffectRef& effect,
+                                              EffectKey effectKey,
+                                              TransformedCoordsArray* outCoords) {
+    SkTArray<Transform, true>& transforms = fTransforms.push_back();
     EffectKey totalKey = GrBackendEffectFactory::GetTransformKey(effectKey);
     int numTransforms = effect->numTransforms();
     transforms.push_back_n(numTransforms);
     for (int t = 0; t < numTransforms; t++) {
-        EffectKey key = (totalKey >> (kTransformKeyBits * t)) & kTransformKeyMask;
         GrSLType varyingType = kVoid_GrSLType;
         const char* uniName;
-        switch (key & kMatrixTypeKeyMask) {
+        switch (get_matrix_type(totalKey, t)) {
             case kIdentity_MatrixType:
                 transforms[t].fType = kVoid_GrSLType;
                 uniName = NULL;
@@ -343,10 +350,10 @@
                 suffixedUniName.appendf("_%i", t);
                 uniName = suffixedUniName.c_str();
             }
-            transforms[t].fHandle = fBuilder->addUniform(GrGLShaderBuilder::kVertex_Visibility,
-                                                         transforms[t].fType,
-                                                         uniName,
-                                                         &uniName);
+            transforms[t].fHandle = builder->addUniform(GrGLShaderBuilder::kVertex_Visibility,
+                                                        transforms[t].fType,
+                                                        uniName,
+                                                        &uniName);
         }
 
         const char* varyingName = "MatrixCoord";
@@ -358,55 +365,222 @@
         }
         const char* vsVaryingName;
         const char* fsVaryingName;
-        fBuilder->addVarying(varyingType, varyingName, &vsVaryingName, &fsVaryingName);
+        builder->addVarying(varyingType, varyingName, &vsVaryingName, &fsVaryingName);
 
-        const GrGLShaderVar& coords = (kPositionCoords_Flag & key) ?
-                                          fBuilder->positionAttribute() :
-                                          fBuilder->localCoordsAttribute();
+        const GrGLShaderVar& coords = kPosition_GrCoordSet == get_source_coords(totalKey, t) ?
+                                          builder->positionAttribute() :
+                                          builder->localCoordsAttribute();
         // varying = matrix * coords (logically)
         switch (transforms[t].fType) {
             case kVoid_GrSLType:
                 SkASSERT(kVec2f_GrSLType == varyingType);
-                fBuilder->vsCodeAppendf("\t%s = %s;\n", vsVaryingName, coords.c_str());
+                builder->vsCodeAppendf("\t%s = %s;\n", vsVaryingName, coords.c_str());
                 break;
             case kVec2f_GrSLType:
                 SkASSERT(kVec2f_GrSLType == varyingType);
-                fBuilder->vsCodeAppendf("\t%s = %s + %s;\n",
-                                        vsVaryingName, uniName, coords.c_str());
+                builder->vsCodeAppendf("\t%s = %s + %s;\n",
+                                       vsVaryingName, uniName, coords.c_str());
                 break;
             case kMat33f_GrSLType: {
                 SkASSERT(kVec2f_GrSLType == varyingType || kVec3f_GrSLType == varyingType);
                 if (kVec2f_GrSLType == varyingType) {
-                    fBuilder->vsCodeAppendf("\t%s = (%s * vec3(%s, 1)).xy;\n",
-                                            vsVaryingName, uniName, coords.c_str());
+                    builder->vsCodeAppendf("\t%s = (%s * vec3(%s, 1)).xy;\n",
+                                           vsVaryingName, uniName, coords.c_str());
                 } else {
-                    fBuilder->vsCodeAppendf("\t%s = %s * vec3(%s, 1);\n",
-                                            vsVaryingName, uniName, coords.c_str());
+                    builder->vsCodeAppendf("\t%s = %s * vec3(%s, 1);\n",
+                                           vsVaryingName, uniName, coords.c_str());
                 }
                 break;
             }
             default:
                 GrCrash("Unexpected uniform type.");
         }
-        SkNEW_APPEND_TO_TARRAY(outCoords, TransformedCoords, (fsVaryingName, varyingType));
+        SkNEW_APPEND_TO_TARRAY(outCoords, TransformedCoords,
+                               (SkString(fsVaryingName), varyingType));
     }
 }
 
-void GrGLProgramEffectsBuilder::emitSamplers(GrGLShaderBuilder* builder,
-                                             GrGLProgramEffects* programEffects,
-                                             const GrEffectRef& effect,
-                                             TextureSamplerArray* outSamplers) {
-    typedef GrGLProgramEffects::Sampler Sampler;
-    SkTArray<Sampler, true>& samplers = programEffects->fSamplers.push_back();
-    int numTextures = effect->numTextures();
-    samplers.push_back_n(numTextures);
-    SkString name;
-    for (int t = 0; t < numTextures; ++t) {
-        name.printf("Sampler%d", t);
-        samplers[t].fUniform = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
-                                                   kSampler2D_GrSLType,
-                                                   name.c_str());
-        SkNEW_APPEND_TO_TARRAY(outSamplers, TextureSampler,
-                               (samplers[t].fUniform, effect->textureAccess(t)));
+void GrGLVertexProgramEffects::setData(GrGpuGL* gpu,
+                                       const GrGLUniformManager& uniformManager,
+                                       const GrEffectStage* effectStages[]) {
+    int numEffects = fGLEffects.count();
+    SkASSERT(numEffects == fTransforms.count());
+    SkASSERT(numEffects == fSamplers.count());
+    for (int e = 0; e < numEffects; ++e) {
+        GrDrawEffect drawEffect(*effectStages[e], fHasExplicitLocalCoords);
+        fGLEffects[e]->setData(uniformManager, drawEffect);
+        this->setTransformData(uniformManager, drawEffect, e);
+        this->bindTextures(gpu, *drawEffect.effect(), e);
     }
 }
+
+void GrGLVertexProgramEffects::setTransformData(const GrGLUniformManager& uniformManager,
+                                                const GrDrawEffect& drawEffect,
+                                                int effectIdx) {
+    SkTArray<Transform, true>& transforms = fTransforms[effectIdx];
+    int numTransforms = transforms.count();
+    SkASSERT(numTransforms == (*drawEffect.effect())->numTransforms());
+    for (int t = 0; t < numTransforms; ++t) {
+        SkASSERT(transforms[t].fHandle.isValid() != (kVoid_GrSLType == transforms[t].fType));
+        switch (transforms[t].fType) {
+            case kVoid_GrSLType:
+                SkASSERT(get_transform_matrix(drawEffect, t).isIdentity());
+                return;
+            case kVec2f_GrSLType: {
+                GrGLfloat tx, ty;
+                get_transform_translation(drawEffect, t, &tx, &ty);
+                if (transforms[t].fCurrentValue.get(SkMatrix::kMTransX) != tx ||
+                    transforms[t].fCurrentValue.get(SkMatrix::kMTransY) != ty) {
+                    uniformManager.set2f(transforms[t].fHandle, tx, ty);
+                    transforms[t].fCurrentValue.set(SkMatrix::kMTransX, tx);
+                    transforms[t].fCurrentValue.set(SkMatrix::kMTransY, ty);
+                }
+                break;
+            }
+            case kMat33f_GrSLType: {
+                const SkMatrix& matrix = get_transform_matrix(drawEffect, t);
+                if (!transforms[t].fCurrentValue.cheapEqualTo(matrix)) {
+                    uniformManager.setSkMatrix(transforms[t].fHandle, matrix);
+                    transforms[t].fCurrentValue = matrix;
+                }
+                break;
+            }
+            default:
+                GrCrash("Unexpected uniform type.");
+        }
+    }
+}
+
+GrGLVertexProgramEffectsBuilder::GrGLVertexProgramEffectsBuilder(GrGLFullShaderBuilder* builder,
+                                                                 int reserveCount)
+    : fBuilder(builder)
+    , fProgramEffects(SkNEW_ARGS(GrGLVertexProgramEffects,
+                                 (reserveCount, fBuilder->hasExplicitLocalCoords()))) {
+}
+
+void GrGLVertexProgramEffectsBuilder::emitEffect(const GrEffectStage& stage,
+                                                 GrGLProgramEffects::EffectKey key,
+                                                 const char* outColor,
+                                                 const char* inColor,
+                                                 int stageIndex) {
+    SkASSERT(NULL != fProgramEffects.get());
+    fProgramEffects->emitEffect(fBuilder, stage, key, outColor, inColor, stageIndex);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+void GrGLTexGenProgramEffects::emitEffect(GrGLFragmentOnlyShaderBuilder* builder,
+                                          const GrEffectStage& stage,
+                                          EffectKey key,
+                                          const char* outColor,
+                                          const char* inColor,
+                                          int stageIndex) {
+    GrDrawEffect drawEffect(stage, false);
+    const GrEffectRef& effect = *stage.getEffect();
+    SkSTArray<2, TransformedCoords> coords(effect->numTransforms());
+    SkSTArray<4, TextureSampler> samplers(effect->numTextures());
+
+    SkASSERT(0 == stage.getVertexAttribIndexCount());
+    this->setupTexGen(builder, effect, key, &coords);
+    this->emitSamplers(builder, effect, &samplers);
+
+    GrGLEffect* glEffect = effect->getFactory().createGLInstance(drawEffect);
+    fGLEffects.push_back(glEffect);
+
+    // Enclose custom code in a block to avoid namespace conflicts
+    SkString openBrace;
+    openBrace.printf("\t{ // Stage %d: %s\n", stageIndex, glEffect->name());
+    builder->fsCodeAppend(openBrace.c_str());
+
+    SkASSERT(!glEffect->isVertexEffect());
+    glEffect->emitCode(builder, drawEffect, key, outColor, inColor, coords, samplers);
+
+    builder->fsCodeAppend("\t}\n");
+}
+
+void GrGLTexGenProgramEffects::setupTexGen(GrGLFragmentOnlyShaderBuilder* builder,
+                                           const GrEffectRef& effect,
+                                           EffectKey effectKey,
+                                           TransformedCoordsArray* outCoords) {
+    int numTransforms = effect->numTransforms();
+    EffectKey totalKey = GrBackendEffectFactory::GetTransformKey(effectKey);
+    int texCoordIndex = builder->addTexCoordSets(numTransforms);
+    SkNEW_APPEND_TO_TARRAY(&fTransforms, Transforms, (totalKey, texCoordIndex));
+    SkString name;
+    for (int t = 0; t < numTransforms; ++t) {
+        GrSLType type = kGeneral_MatrixType == get_matrix_type(totalKey, t) ?
+                            kVec3f_GrSLType :
+                            kVec2f_GrSLType;
+        name.printf("%s(gl_TexCoord[%i])", GrGLSLTypeString(type), texCoordIndex++);
+        SkNEW_APPEND_TO_TARRAY(outCoords, TransformedCoords, (name, type));
+    }
+}
+
+void GrGLTexGenProgramEffects::setData(GrGpuGL* gpu,
+                                       const GrGLUniformManager& uniformManager,
+                                       const GrEffectStage* effectStages[]) {
+    int numEffects = fGLEffects.count();
+    SkASSERT(numEffects == fTransforms.count());
+    SkASSERT(numEffects == fSamplers.count());
+    for (int e = 0; e < numEffects; ++e) {
+        GrDrawEffect drawEffect(*effectStages[e], false);
+        fGLEffects[e]->setData(uniformManager, drawEffect);
+        this->setTexGenState(gpu, drawEffect, e);
+        this->bindTextures(gpu, *drawEffect.effect(), e);
+    }
+}
+
+void GrGLTexGenProgramEffects::setTexGenState(GrGpuGL* gpu,
+                                              const GrDrawEffect& drawEffect,
+                                              int effectIdx) {
+    EffectKey totalKey = fTransforms[effectIdx].fTransformKey;
+    int texCoordIndex = fTransforms[effectIdx].fTexCoordIndex;
+    int numTransforms = (*drawEffect.effect())->numTransforms();
+    for (int t = 0; t < numTransforms; ++t) {
+        switch (get_matrix_type(totalKey, t)) {
+            case kIdentity_MatrixType: {
+                SkASSERT(get_transform_matrix(drawEffect, t).isIdentity());
+                GrGLfloat identity[] = {1, 0, 0,
+                                        0, 1, 0};
+                gpu->enableTexGen(texCoordIndex++, GrGpuGL::kST_TexGenComponents, identity);
+                break;
+            }
+            case kTrans_MatrixType: {
+                GrGLfloat tx, ty;
+                get_transform_translation(drawEffect, t, &tx, &ty);
+                GrGLfloat translate[] = {1, 0, tx,
+                                         0, 1, ty};
+                gpu->enableTexGen(texCoordIndex++, GrGpuGL::kST_TexGenComponents, translate);
+                break;
+            }
+            case kNoPersp_MatrixType: {
+                const SkMatrix& transform = get_transform_matrix(drawEffect, t);
+                gpu->enableTexGen(texCoordIndex++, GrGpuGL::kST_TexGenComponents, transform);
+                break;
+            }
+            case kGeneral_MatrixType: {
+                const SkMatrix& transform = get_transform_matrix(drawEffect, t);
+                gpu->enableTexGen(texCoordIndex++, GrGpuGL::kSTR_TexGenComponents, transform);
+                break;
+            }
+            default:
+                GrCrash("Unexpected matrixs type.");
+        }
+    }
+}
+
+GrGLTexGenProgramEffectsBuilder::GrGLTexGenProgramEffectsBuilder(
+        GrGLFragmentOnlyShaderBuilder* builder,
+        int reserveCount)
+    : fBuilder(builder)
+    , fProgramEffects(SkNEW_ARGS(GrGLTexGenProgramEffects, (reserveCount))) {
+}
+
+void GrGLTexGenProgramEffectsBuilder::emitEffect(const GrEffectStage& stage,
+                                                 GrGLProgramEffects::EffectKey key,
+                                                 const char* outColor,
+                                                 const char* inColor,
+                                                 int stageIndex) {
+    SkASSERT(NULL != fProgramEffects.get());
+    fProgramEffects->emitEffect(fBuilder, stage, key, outColor, inColor, stageIndex);
+}
diff --git a/src/gpu/gl/GrGLProgramEffects.h b/src/gpu/gl/GrGLProgramEffects.h
index f6f975a..4572a42 100644
--- a/src/gpu/gl/GrGLProgramEffects.h
+++ b/src/gpu/gl/GrGLProgramEffects.h
@@ -14,10 +14,10 @@
 #include "GrGLUniformManager.h"
 
 class GrEffectStage;
-class GrGLProgramEffectsBuilder;
 class GrGLVertexProgramEffectsBuilder;
 class GrGLShaderBuilder;
 class GrGLFullShaderBuilder;
+class GrGLFragmentOnlyShaderBuilder;
 
 /**
  * This class encapsulates an array of GrGLEffects and their supporting data (coord transforms
@@ -56,7 +56,7 @@
      */
     class TransformedCoords {
     public:
-        TransformedCoords(const char* name, GrSLType type)
+        TransformedCoords(const SkString& name, GrSLType type)
             : fName(name), fType(type) {
         }
 
@@ -97,14 +97,19 @@
     typedef SkTArray<TextureSampler> TextureSamplerArray;
 
 protected:
-    friend class GrGLProgramEffectsBuilder;
-
     GrGLProgramEffects(int reserveCount)
         : fGLEffects(reserveCount)
         , fSamplers(reserveCount) {
     }
 
     /**
+     * Helper for emitEffect() in a subclasses. Emits uniforms for an effect's texture accesses and
+     * appends the necessary data to the TextureSamplerArray* object so effects can add texture
+     * lookups to their code. This method is only meant to be called during the construction phase.
+     */
+    void emitSamplers(GrGLShaderBuilder*, const GrEffectRef&, TextureSamplerArray*);
+
+    /**
      * Helper for setData(). Binds all the textures for an effect.
      */
     void bindTextures(GrGpuGL*, const GrEffectRef&, int effectIdx);
@@ -120,6 +125,23 @@
 };
 
 /**
+ * This is an abstract base class for constructing different types of GrGLProgramEffects objects.
+ */
+class GrGLProgramEffectsBuilder {
+public:
+    /**
+     * Emits the effect's shader code, and stores the necessary uniforms internally.
+     */
+    virtual void emitEffect(const GrEffectStage&,
+                            GrGLProgramEffects::EffectKey,
+                            const char* outColor,
+                            const char* inColor,
+                            int stageIndex) = 0;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+/**
  * This is a GrGLProgramEffects implementation that does coord transforms with the vertex shader.
  */
 class GrGLVertexProgramEffects : public GrGLProgramEffects {
@@ -138,6 +160,34 @@
     }
 
     /**
+     * Helper for GrGLProgramEffectsBuilder::emitEfffect(). This method is meant to only be called
+     * during the construction phase.
+     */
+    void emitEffect(GrGLFullShaderBuilder*,
+                    const GrEffectStage&,
+                    GrGLProgramEffects::EffectKey,
+                    const char* outColor,
+                    const char* inColor,
+                    int stageIndex);
+
+    /**
+     * Helper for emitEffect(). Emits any attributes an effect may have.
+     */
+    void emitAttributes(GrGLFullShaderBuilder*, const GrEffectStage&);
+
+    /**
+     * Helper for emitEffect(). Emits code to implement an effect's coord transforms in the VS.
+     * Varyings are added as an outputs of the VS and inputs to the FS. The varyings may be either a
+     * vec2f or vec3f depending upon whether perspective interpolation is required or not. The names
+     * of the varyings in the VS and FS as well their types are appended to the
+     * TransformedCoordsArray* object, which is in turn passed to the effect's emitCode() function.
+     */
+    void emitTransforms(GrGLFullShaderBuilder*,
+                        const GrEffectRef&,
+                        EffectKey,
+                        TransformedCoordsArray*);
+
+    /**
      * Helper for setData(). Sets all the transform matrices for an effect.
      */
     void setTransformData(const GrGLUniformManager&, const GrDrawEffect&, int effectIdx);
@@ -155,35 +205,8 @@
     typedef GrGLProgramEffects INHERITED;
 };
 
-////////////////////////////////////////////////////////////////////////////////
-
 /**
- * This is an abstract base class for constructing different types of GrGLProgramEffects objects.
- */
-class GrGLProgramEffectsBuilder {
-public:
-    /**
-     * Emits the effect's shader code, and stores the necessary uniforms internally.
-     */
-    virtual void emitEffect(const GrEffectStage&,
-                            GrGLProgramEffects::EffectKey,
-                            const char* outColor,
-                            const char* inColor,
-                            int stageIndex) = 0;
-
-protected:
-    /**
-     * Helper for emitEffect(). Emits uniforms for an effect's texture accesses and appends the
-     * necessary data to the TextureSamplerArray* object so effects can add texture lookups.
-     */
-    static void emitSamplers(GrGLShaderBuilder*,
-                             GrGLProgramEffects*,
-                             const GrEffectRef&,
-                             GrGLProgramEffects::TextureSamplerArray*);
-};
-
-/**
- * This class is used to construct a GrGLVertexProgramEffects object.
+ * This class is used to construct a GrGLVertexProgramEffects* object.
  */
 class GrGLVertexProgramEffectsBuilder : public GrGLProgramEffectsBuilder {
 public:
@@ -202,26 +225,97 @@
     GrGLProgramEffects* finish() { return fProgramEffects.detach(); }
 
 private:
-    /**
-     * Helper for emitEffect(). Emits any attributes an effect might have.
-     */
-    void emitAttributes(const GrEffectStage&);
-
-    /**
-     * Helper for emitEffect(). Emits code to implement an effect's coord transforms in the VS.
-     * Varyings are added as an outputs of the VS and inputs to the FS. The varyings may be either a
-     * vec2f or vec3f depending upon whether perspective interpolation is required or not. The names
-     * of the varyings in the VS and FS as well their types are appended to the
-     * TransformedCoordsArray* object, which is in turn passed to the effect's emitCode() function.
-     */
-    void emitTransforms(const GrEffectRef&,
-                        GrGLProgramEffects::EffectKey,
-                        GrGLProgramEffects::TransformedCoordsArray*);
-
     GrGLFullShaderBuilder*                  fBuilder;
     SkAutoTDelete<GrGLVertexProgramEffects> fProgramEffects;
 
     typedef GrGLProgramEffectsBuilder INHERITED;
 };
 
+////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * This is a GrGLProgramEffects implementation that does coord transforms with the the built-in GL
+ * TexGen functionality.
+ */
+class GrGLTexGenProgramEffects : public GrGLProgramEffects {
+public:
+    virtual void setData(GrGpuGL*,
+                         const GrGLUniformManager&,
+                         const GrEffectStage* effectStages[]) SK_OVERRIDE;
+
+private:
+    friend class GrGLTexGenProgramEffectsBuilder;
+
+    GrGLTexGenProgramEffects(int reserveCount)
+        : INHERITED(reserveCount)
+        , fTransforms(reserveCount) {
+    }
+
+    /**
+     * Helper for GrGLProgramEffectsBuilder::emitEfffect(). This method is meant to only be called
+     * during the construction phase.
+     */
+    void emitEffect(GrGLFragmentOnlyShaderBuilder*,
+                    const GrEffectStage&,
+                    GrGLProgramEffects::EffectKey,
+                    const char* outColor,
+                    const char* inColor,
+                    int stageIndex);
+
+    /**
+     * Helper for emitEffect(). Allocates texture units from the builder for each transform in an
+     * effect. The transforms all use adjacent texture units. They either use two or three of the
+     * coordinates at a given texture unit, depending on if they need perspective interpolation.
+     * The expressions to access the transformed coords (i.e. 'vec2(gl_TexCoord[0])') as well as the
+     * types are appended to the TransformedCoordsArray* object, which is in turn passed to the
+     * effect's emitCode() function.
+     */
+    void setupTexGen(GrGLFragmentOnlyShaderBuilder*,
+                     const GrEffectRef&,
+                     EffectKey,
+                     TransformedCoordsArray*);
+
+    /**
+     * Helper for setData(). Sets the TexGen state for each transform in an effect.
+     */
+    void setTexGenState(GrGpuGL*, const GrDrawEffect&, int effectIdx);
+
+    struct Transforms {
+        Transforms(EffectKey transformKey, int texCoordIndex)
+            : fTransformKey(transformKey), fTexCoordIndex(texCoordIndex) {}
+        EffectKey fTransformKey;
+        int fTexCoordIndex;
+    };
+
+    SkTArray<Transforms> fTransforms;
+
+    typedef GrGLProgramEffects INHERITED;
+};
+
+/**
+ * This class is used to construct a GrGLTexGenProgramEffects* object.
+ */
+class GrGLTexGenProgramEffectsBuilder : public GrGLProgramEffectsBuilder {
+public:
+    GrGLTexGenProgramEffectsBuilder(GrGLFragmentOnlyShaderBuilder*, int reserveCount);
+
+    virtual void emitEffect(const GrEffectStage&,
+                            GrGLProgramEffects::EffectKey,
+                            const char* outColor,
+                            const char* inColor,
+                            int stageIndex) SK_OVERRIDE;
+
+    /**
+     * Finalizes the building process and returns the effect array. After this call, the builder
+     * becomes invalid.
+     */
+    GrGLProgramEffects* finish() { return fProgramEffects.detach(); }
+
+private:
+    GrGLFragmentOnlyShaderBuilder*          fBuilder;
+    SkAutoTDelete<GrGLTexGenProgramEffects> fProgramEffects;
+
+    typedef GrGLProgramEffectsBuilder INHERITED;
+};
+
 #endif
diff --git a/src/gpu/gl/GrGLShaderBuilder.cpp b/src/gpu/gl/GrGLShaderBuilder.cpp
index 089ece7..00fefe1 100644
--- a/src/gpu/gl/GrGLShaderBuilder.cpp
+++ b/src/gpu/gl/GrGLShaderBuilder.cpp
@@ -441,7 +441,6 @@
     }
 }
 
-
 void GrGLShaderBuilder::fsEmitFunction(GrSLType returnType,
                                        const char* name,
                                        int argCnt,
@@ -920,3 +919,35 @@
          GL_CALL(BindAttribLocation(programId, attrib->fIndex, attrib->fName.c_str()));
     }
 }
+
+////////////////////////////////////////////////////////////////////////////////
+
+GrGLFragmentOnlyShaderBuilder::GrGLFragmentOnlyShaderBuilder(GrGpuGL* gpu,
+                                                             GrGLUniformManager& uniformManager,
+                                                             const GrGLProgramDesc& desc)
+    : INHERITED(gpu, uniformManager, desc)
+    , fNumTexCoordSets(0) {
+
+    SkASSERT(!desc.getHeader().fHasVertexCode);
+    SkASSERT(gpu->glCaps().fixedFunctionSupport());
+    SkASSERT(gpu->glCaps().pathStencilingSupport());
+    SkASSERT(GrGLProgramDesc::kAttribute_ColorInput != desc.getHeader().fColorInput);
+    SkASSERT(GrGLProgramDesc::kAttribute_ColorInput != desc.getHeader().fCoverageInput);
+}
+
+GrGLProgramEffects* GrGLFragmentOnlyShaderBuilder::createAndEmitEffects(
+        const GrEffectStage* effectStages[],
+        const EffectKey effectKeys[],
+        int effectCnt,
+        SkString* inOutFSColor,
+        GrSLConstantVec* fsInOutColorKnownValue) {
+
+    GrGLTexGenProgramEffectsBuilder texGenEffectsBuilder(this, effectCnt);
+    this->INHERITED::createAndEmitEffects(&texGenEffectsBuilder,
+                                          effectStages,
+                                          effectKeys,
+                                          effectCnt,
+                                          inOutFSColor,
+                                          fsInOutColorKnownValue);
+    return texGenEffectsBuilder.finish();
+}
diff --git a/src/gpu/gl/GrGLShaderBuilder.h b/src/gpu/gl/GrGLShaderBuilder.h
index 4c43fc3..208c610 100644
--- a/src/gpu/gl/GrGLShaderBuilder.h
+++ b/src/gpu/gl/GrGLShaderBuilder.h
@@ -450,4 +450,26 @@
     typedef GrGLShaderBuilder INHERITED;
 };
 
+////////////////////////////////////////////////////////////////////////////////
+
+class GrGLFragmentOnlyShaderBuilder : public GrGLShaderBuilder {
+public:
+    GrGLFragmentOnlyShaderBuilder(GrGpuGL*, GrGLUniformManager&, const GrGLProgramDesc&);
+
+    int getNumTexCoordSets() const { return fNumTexCoordSets; }
+    int addTexCoordSets(int count) { return (fNumTexCoordSets += count) - count; }
+
+    virtual GrGLProgramEffects* createAndEmitEffects(
+                const GrEffectStage* effectStages[],
+                const EffectKey effectKeys[],
+                int effectCnt,
+                SkString* inOutFSColor,
+                GrSLConstantVec* fsInOutColorKnownValue) SK_OVERRIDE;
+
+private:
+    int fNumTexCoordSets;
+
+    typedef GrGLShaderBuilder INHERITED;
+};
+
 #endif
diff --git a/src/gpu/gl/GrGpuGL.cpp b/src/gpu/gl/GrGpuGL.cpp
index 50d2a1f..18165d9 100644
--- a/src/gpu/gl/GrGpuGL.cpp
+++ b/src/gpu/gl/GrGpuGL.cpp
@@ -124,6 +124,7 @@
     fCaps.reset(SkRef(ctx.info().caps()));
 
     fHWBoundTextures.reset(ctx.info().caps()->maxFragmentTextureUnits());
+    fHWTexGenSettings.reset(ctx.info().caps()->maxFixedFunctionTextureCoords());
 
     fillInConfigRenderableTable();
 
@@ -377,7 +378,13 @@
             GL_CALL(Disable(GR_GL_TEXTURE_GEN_T));
             GL_CALL(Disable(GR_GL_TEXTURE_GEN_Q));
             GL_CALL(Disable(GR_GL_TEXTURE_GEN_R));
+            if (this->caps()->pathStencilingSupport()) {
+                GL_CALL(PathTexGen(GR_GL_TEXTURE0 + i, GR_GL_NONE, 0, NULL));
+            }
+            fHWTexGenSettings[i].fMode = GR_GL_NONE;
+            fHWTexGenSettings[i].fNumComponents = 0;
         }
+        fHWActiveTexGenSets = 0;
     }
 
     // we assume these values
@@ -2114,6 +2121,124 @@
     texture->setCachedTexParams(newTexParams, this->getResetTimestamp());
 }
 
+void GrGpuGL::setProjectionMatrix(const SkMatrix& matrix,
+                                  const SkISize& renderTargetSize,
+                                  GrSurfaceOrigin renderTargetOrigin) {
+
+    SkASSERT(this->glCaps().fixedFunctionSupport());
+
+    if (renderTargetOrigin == fHWProjectionMatrixState.fRenderTargetOrigin &&
+        renderTargetSize == fHWProjectionMatrixState.fRenderTargetSize &&
+        matrix.cheapEqualTo(fHWProjectionMatrixState.fViewMatrix)) {
+        return;
+    }
+
+    fHWProjectionMatrixState.fViewMatrix = matrix;
+    fHWProjectionMatrixState.fRenderTargetSize = renderTargetSize;
+    fHWProjectionMatrixState.fRenderTargetOrigin = renderTargetOrigin;
+
+    GrGLfloat glMatrix[4 * 4];
+    fHWProjectionMatrixState.getGLMatrix<4>(glMatrix);
+    GL_CALL(MatrixMode(GR_GL_PROJECTION));
+    GL_CALL(LoadMatrixf(glMatrix));
+}
+
+void GrGpuGL::enableTexGen(int unitIdx,
+                           TexGenComponents components,
+                           const GrGLfloat* coefficients) {
+
+    SkASSERT(this->glCaps().fixedFunctionSupport());
+    SkASSERT(this->caps()->pathStencilingSupport());
+    SkASSERT(components >= kS_TexGenComponents && components <= kSTR_TexGenComponents);
+
+    if (GR_GL_OBJECT_LINEAR == fHWTexGenSettings[unitIdx].fMode &&
+        components == fHWTexGenSettings[unitIdx].fNumComponents &&
+        !memcmp(coefficients, fHWTexGenSettings[unitIdx].fCoefficients,
+                3 * components * sizeof(GrGLfloat))) {
+        return;
+    }
+
+    this->setTextureUnit(unitIdx);
+
+    if (GR_GL_OBJECT_LINEAR != fHWTexGenSettings[unitIdx].fMode) {
+        for (int i = 0; i < 4; i++) {
+            GL_CALL(TexGeni(GR_GL_S + i, GR_GL_TEXTURE_GEN_MODE, GR_GL_OBJECT_LINEAR));
+        }
+        fHWTexGenSettings[unitIdx].fMode = GR_GL_OBJECT_LINEAR;
+    }
+
+    for (int i = fHWTexGenSettings[unitIdx].fNumComponents; i < components; i++) {
+        GL_CALL(Enable(GR_GL_TEXTURE_GEN_S + i));
+    }
+    for (int i = components; i < fHWTexGenSettings[unitIdx].fNumComponents; i++) {
+        GL_CALL(Disable(GR_GL_TEXTURE_GEN_S + i));
+    }
+    fHWTexGenSettings[unitIdx].fNumComponents = components;
+
+    for (int i = 0; i < components; i++) {
+        GrGLfloat plane[] = {coefficients[0 + 3 * i],
+                             coefficients[1 + 3 * i],
+                             0,
+                             coefficients[2 + 3 * i]};
+        GL_CALL(TexGenfv(GR_GL_S + i, GR_GL_OBJECT_PLANE, plane));
+    }
+
+    GL_CALL(PathTexGen(GR_GL_TEXTURE0 + unitIdx,
+                       GR_GL_OBJECT_LINEAR,
+                       components,
+                       coefficients));
+
+    memcpy(fHWTexGenSettings[unitIdx].fCoefficients, coefficients,
+           3 * components * sizeof(GrGLfloat));
+
+    fHWActiveTexGenSets = SkTMax(fHWActiveTexGenSets, unitIdx);
+}
+
+void GrGpuGL::enableTexGen(int unitIdx, TexGenComponents components, const SkMatrix& matrix) {
+
+    GrGLfloat coefficients[3 * 3];
+    SkASSERT(this->glCaps().fixedFunctionSupport());
+    SkASSERT(components >= kS_TexGenComponents && components <= kSTR_TexGenComponents);
+
+    coefficients[0] = SkScalarToFloat(matrix[SkMatrix::kMScaleX]);
+    coefficients[1] = SkScalarToFloat(matrix[SkMatrix::kMSkewX]);
+    coefficients[2] = SkScalarToFloat(matrix[SkMatrix::kMTransX]);
+
+    if (components >= kST_TexGenComponents) {
+        coefficients[3] = SkScalarToFloat(matrix[SkMatrix::kMSkewY]);
+        coefficients[4] = SkScalarToFloat(matrix[SkMatrix::kMScaleY]);
+        coefficients[5] = SkScalarToFloat(matrix[SkMatrix::kMTransY]);
+    }
+
+    if (components >= kSTR_TexGenComponents) {
+        coefficients[6] = SkScalarToFloat(matrix[SkMatrix::kMPersp0]);
+        coefficients[7] = SkScalarToFloat(matrix[SkMatrix::kMPersp1]);
+        coefficients[8] = SkScalarToFloat(matrix[SkMatrix::kMPersp2]);
+    }
+
+    enableTexGen(unitIdx, components, coefficients);
+}
+
+void GrGpuGL::disableUnusedTexGen(int numUsedTexCoordSets) {
+
+    SkASSERT(this->glCaps().fixedFunctionSupport());
+
+    for (int i = numUsedTexCoordSets; i < fHWActiveTexGenSets; i++) {
+        if (0 == fHWTexGenSettings[i].fNumComponents) {
+            continue;
+        }
+
+        this->setTextureUnit(i);
+        for (int j = 0; j < fHWTexGenSettings[i].fNumComponents; j++) {
+            GL_CALL(Disable(GR_GL_TEXTURE_GEN_S + j));
+        }
+        GL_CALL(PathTexGen(GR_GL_TEXTURE0 + i, GR_GL_NONE, 0, NULL));
+        fHWTexGenSettings[i].fNumComponents = 0;
+    }
+
+    fHWActiveTexGenSets = SkTMin(fHWActiveTexGenSets, numUsedTexCoordSets);
+}
+
 void GrGpuGL::flushMiscFixedFunctionState() {
 
     const GrDrawState& drawState = this->getDrawState();
diff --git a/src/gpu/gl/GrGpuGL.h b/src/gpu/gl/GrGpuGL.h
index b0195bb..a59654a 100644
--- a/src/gpu/gl/GrGpuGL.h
+++ b/src/gpu/gl/GrGpuGL.h
@@ -37,8 +37,19 @@
     GrGLVersion glVersion() const { return fGLContext.info().version(); }
     GrGLSLGeneration glslGeneration() const { return fGLContext.info().glslGeneration(); }
 
-    // Used by GrGLProgram to bind necessary textures for GrGLEffects.
+    // Used by GrGLProgram and GrGLTexGenProgramEffects to configure OpenGL state.
     void bindTexture(int unitIdx, const GrTextureParams& params, GrGLTexture* texture);
+    void setProjectionMatrix(const SkMatrix& matrix,
+                             const SkISize& renderTargetSize,
+                             GrSurfaceOrigin renderTargetOrigin);
+    enum TexGenComponents {
+        kS_TexGenComponents = 1,
+        kST_TexGenComponents = 2,
+        kSTR_TexGenComponents = 3
+    };
+    void enableTexGen(int unitIdx, TexGenComponents, const GrGLfloat* coefficients);
+    void enableTexGen(int unitIdx, TexGenComponents, const SkMatrix& matrix);
+    void disableUnusedTexGen(int numUsedTexCoordSets);
 
     bool programUnitTest(int maxStages);
 
@@ -213,9 +224,6 @@
 #endif
     };
 
-    // sets the matrix for path stenciling (uses the GL fixed pipe matrices)
-    void flushPathStencilMatrix();
-
     // flushes dithering, color-mask, and face culling stat
     void flushMiscFixedFunctionState();
 
@@ -432,6 +440,14 @@
     TriState                    fHWDitherEnabled;
     GrRenderTarget*             fHWBoundRenderTarget;
     SkTArray<GrTexture*, true>  fHWBoundTextures;
+
+    struct TexGenData {
+        GrGLenum  fMode;
+        GrGLint   fNumComponents;
+        GrGLfloat fCoefficients[3 * 3];
+    };
+    int                         fHWActiveTexGenSets;
+    SkTArray<TexGenData, true>  fHWTexGenSettings;
     ///@}
 
     // we record what stencil format worked last time to hopefully exit early
diff --git a/src/gpu/gl/GrGpuGL_program.cpp b/src/gpu/gl/GrGpuGL_program.cpp
index 8a3444e..92eed8d 100644
--- a/src/gpu/gl/GrGpuGL_program.cpp
+++ b/src/gpu/gl/GrGpuGL_program.cpp
@@ -210,28 +210,6 @@
 
 #define GL_CALL(X) GR_GL_CALL(this->glInterface(), X)
 
-void GrGpuGL::flushPathStencilMatrix() {
-    const SkMatrix& viewMatrix = this->getDrawState().getViewMatrix();
-    const GrRenderTarget* rt = this->getDrawState().getRenderTarget();
-    SkISize size;
-    size.set(rt->width(), rt->height());
-    const SkMatrix& vm = this->getDrawState().getViewMatrix();
-
-    if (fHWProjectionMatrixState.fRenderTargetOrigin != rt->origin() ||
-        !fHWProjectionMatrixState.fViewMatrix.cheapEqualTo(viewMatrix) ||
-        fHWProjectionMatrixState.fRenderTargetSize!= size) {
-
-        fHWProjectionMatrixState.fViewMatrix = vm;
-        fHWProjectionMatrixState.fRenderTargetSize = size;
-        fHWProjectionMatrixState.fRenderTargetOrigin = rt->origin();
-
-        GrGLfloat projectionMatrix[4 * 4];
-        fHWProjectionMatrixState.getGLMatrix<4>(projectionMatrix);
-        GL_CALL(MatrixMode(GR_GL_PROJECTION));
-        GL_CALL(LoadMatrixf(projectionMatrix));
-    }
-}
-
 bool GrGpuGL::flushGraphicsState(DrawType type, const GrDeviceCoordTexture* dstCopy) {
     const GrDrawState& drawState = this->getDrawState();
 
@@ -239,7 +217,10 @@
     SkASSERT(NULL != drawState.getRenderTarget());
 
     if (kStencilPath_DrawType == type) {
-        this->flushPathStencilMatrix();
+        const GrRenderTarget* rt = this->getDrawState().getRenderTarget();
+        SkISize size;
+        size.set(rt->width(), rt->height());
+        this->setProjectionMatrix(drawState.getViewMatrix(), size, rt->origin());
     } else {
         this->flushMiscFixedFunctionState();
 
@@ -360,24 +341,40 @@
     GrGLAttribArrayState* attribState =
         fHWGeometryState.bindArrayAndBuffersToDraw(this, vbuf, ibuf);
 
-    uint32_t usedAttribArraysMask = 0;
-    const GrVertexAttrib* vertexAttrib = this->getDrawState().getVertexAttribs();
-    int vertexAttribCount = this->getDrawState().getVertexAttribCount();
-    for (int vertexAttribIndex = 0; vertexAttribIndex < vertexAttribCount;
-         ++vertexAttribIndex, ++vertexAttrib) {
+    if (!fCurrentProgram->hasVertexShader()) {
+        int posIdx = this->getDrawState().positionAttributeIndex();
+        const GrVertexAttrib* vertexArray = this->getDrawState().getVertexAttribs() + posIdx;
+        GrVertexAttribType vertexArrayType = vertexArray->fType;
+        SkASSERT(!GrGLAttribTypeToLayout(vertexArrayType).fNormalized);
+        SkASSERT(GrGLAttribTypeToLayout(vertexArrayType).fCount == 2);
+        attribState->setFixedFunctionVertexArray(this,
+                                                 vbuf,
+                                                 2,
+                                                 GrGLAttribTypeToLayout(vertexArrayType).fType,
+                                                 stride,
+                                                 reinterpret_cast<GrGLvoid*>(
+                                                 vertexOffsetInBytes + vertexArray->fOffset));
+        attribState->disableUnusedArrays(this, 0, true);
+    } else {
+        uint32_t usedAttribArraysMask = 0;
+        const GrVertexAttrib* vertexAttrib = this->getDrawState().getVertexAttribs();
+        int vertexAttribCount = this->getDrawState().getVertexAttribCount();
+        for (int vertexAttribIndex = 0; vertexAttribIndex < vertexAttribCount;
+             ++vertexAttribIndex, ++vertexAttrib) {
 
-        usedAttribArraysMask |= (1 << vertexAttribIndex);
-        GrVertexAttribType attribType = vertexAttrib->fType;
-        attribState->set(this,
-                         vertexAttribIndex,
-                         vbuf,
-                         GrGLAttribTypeToLayout(attribType).fCount,
-                         GrGLAttribTypeToLayout(attribType).fType,
-                         GrGLAttribTypeToLayout(attribType).fNormalized,
-                         stride,
-                         reinterpret_cast<GrGLvoid*>(
-                         vertexOffsetInBytes + vertexAttrib->fOffset));
+            usedAttribArraysMask |= (1 << vertexAttribIndex);
+            GrVertexAttribType attribType = vertexAttrib->fType;
+            attribState->set(this,
+                             vertexAttribIndex,
+                             vbuf,
+                             GrGLAttribTypeToLayout(attribType).fCount,
+                             GrGLAttribTypeToLayout(attribType).fType,
+                             GrGLAttribTypeToLayout(attribType).fNormalized,
+                             stride,
+                             reinterpret_cast<GrGLvoid*>(
+                             vertexOffsetInBytes + vertexAttrib->fOffset));
+        }
+
+        attribState->disableUnusedArrays(this, usedAttribArraysMask, false);
     }
-
-    attribState->disableUnusedArrays(this, usedAttribArraysMask, false);
 }
diff --git a/tests/GLProgramsTest.cpp b/tests/GLProgramsTest.cpp
index fe17f88..13d0d2e 100644
--- a/tests/GLProgramsTest.cpp
+++ b/tests/GLProgramsTest.cpp
@@ -78,6 +78,7 @@
 
     bool dstRead = false;
     bool fragPos = false;
+    bool vertexCode = false;
     int numStages = numColorStages + numCoverageStages;
     for (int s = 0; s < numStages; ++s) {
         const GrBackendEffectFactory& factory = (*stages[s]->getEffect())->getFactory();
@@ -89,6 +90,9 @@
         if ((*stages[s]->getEffect())->willReadFragmentPosition()) {
             fragPos = true;
         }
+        if ((*stages[s]->getEffect())->hasVertexCode()) {
+            vertexCode = true;
+        }
     }
 
     if (dstRead) {
@@ -103,6 +107,11 @@
         header->fFragPosKey = 0;
     }
 
+    header->fHasVertexCode = vertexCode ||
+                             useLocalCoords ||
+                             kAttribute_ColorInput == header->fColorInput ||
+                             kAttribute_ColorInput == header->fCoverageInput;
+
     CoverageOutput coverageOutput;
     bool illegalCoverageOutput;
     do {