Make GPU coord transforms automatic

Adds a GrCoordTransform class and updates the framework to handle
coord transforms similar to how it handles textures with
GrTextureAccess. Renames GrGLEffectMatrix to GrGLCoordTransform and
slightly repurposes it to be used by the framework instead of effects.

R=bsalomon@google.com, robertphillips@google.com

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

git-svn-id: http://skia.googlecode.com/svn/trunk@11569 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/gpu/GrAAConvexPathRenderer.cpp b/src/gpu/GrAAConvexPathRenderer.cpp
index 7063440..78f2045 100644
--- a/src/gpu/GrAAConvexPathRenderer.cpp
+++ b/src/gpu/GrAAConvexPathRenderer.cpp
@@ -531,6 +531,7 @@
                               EffectKey key,
                               const char* outputColor,
                               const char* inputColor,
+                              const TransformedCoordsArray&,
                               const TextureSamplerArray& samplers) SK_OVERRIDE {
             GrGLShaderBuilder::VertexBuilder* vertexBuilder = builder->getVertexBuilder();
             SkASSERT(NULL != vertexBuilder);
diff --git a/src/gpu/GrAARectRenderer.cpp b/src/gpu/GrAARectRenderer.cpp
index e4bf853..320b3d7 100644
--- a/src/gpu/GrAARectRenderer.cpp
+++ b/src/gpu/GrAARectRenderer.cpp
@@ -49,6 +49,7 @@
                               EffectKey key,
                               const char* outputColor,
                               const char* inputColor,
+                              const TransformedCoordsArray&,
                               const TextureSamplerArray& samplers) SK_OVERRIDE {
             GrGLShaderBuilder::VertexBuilder* vertexBuilder = builder->getVertexBuilder();
             SkASSERT(NULL != vertexBuilder);
@@ -169,6 +170,7 @@
                               EffectKey key,
                               const char* outputColor,
                               const char* inputColor,
+                              const TransformedCoordsArray&,
                               const TextureSamplerArray& samplers) SK_OVERRIDE {
             GrGLShaderBuilder::VertexBuilder* vertexBuilder = builder->getVertexBuilder();
             SkASSERT(NULL != vertexBuilder);
diff --git a/src/gpu/GrClipMaskManager.cpp b/src/gpu/GrClipMaskManager.cpp
index 8465880..58b6a4b 100644
--- a/src/gpu/GrClipMaskManager.cpp
+++ b/src/gpu/GrClipMaskManager.cpp
@@ -55,7 +55,7 @@
                                       GrTextureDomainEffect::MakeTexelDomain(result, domainTexels),
                                       GrTextureDomainEffect::kDecal_WrapMode,
                                       GrTextureParams::kNone_FilterMode,
-                                      GrEffect::kPosition_CoordsType))->unref();
+                                      kPosition_GrCoordSet))->unref();
 }
 
 bool path_needs_SW_renderer(GrContext* context,
diff --git a/src/gpu/GrEffect.cpp b/src/gpu/GrEffect.cpp
index 53dabb6..653a1da 100644
--- a/src/gpu/GrEffect.cpp
+++ b/src/gpu/GrEffect.cpp
@@ -8,6 +8,7 @@
 #include "GrEffect.h"
 #include "GrBackendEffectFactory.h"
 #include "GrContext.h"
+#include "GrCoordTransform.h"
 #include "GrMemoryPool.h"
 #include "SkTLS.h"
 
@@ -86,6 +87,10 @@
     return this->getFactory().name();
 }
 
+void GrEffect::addCoordTransform(const GrCoordTransform* transform) {
+    fCoordTransforms.push_back(transform);
+}
+
 void GrEffect::addTextureAccess(const GrTextureAccess* access) {
     fTextureAccesses.push_back(access);
 }
@@ -97,3 +102,16 @@
 void GrEffect::operator delete(void* target) {
     GrEffect_Globals::GetTLS()->release(target);
 }
+
+#ifdef SK_DEBUG
+void GrEffect::assertEquality(const GrEffect& other) const {
+    SkASSERT(this->numTransforms() == other.numTransforms());
+    for (int i = 0; i < this->numTransforms(); ++i) {
+        SkASSERT(this->coordTransform(i) == other.coordTransform(i));
+    }
+    SkASSERT(this->numTextures() == other.numTextures());
+    for (int i = 0; i < this->numTextures(); ++i) {
+        SkASSERT(this->textureAccess(i) == other.textureAccess(i));
+    }
+}
+#endif
diff --git a/src/gpu/GrOvalRenderer.cpp b/src/gpu/GrOvalRenderer.cpp
index 66ca053..2c923f8 100644
--- a/src/gpu/GrOvalRenderer.cpp
+++ b/src/gpu/GrOvalRenderer.cpp
@@ -98,6 +98,7 @@
                               EffectKey key,
                               const char* outputColor,
                               const char* inputColor,
+                              const TransformedCoordsArray&,
                               const TextureSamplerArray& samplers) SK_OVERRIDE {
             GrGLShaderBuilder::VertexBuilder* vertexBuilder = builder->getVertexBuilder();
             SkASSERT(NULL != vertexBuilder);
@@ -212,6 +213,7 @@
                               EffectKey key,
                               const char* outputColor,
                               const char* inputColor,
+                              const TransformedCoordsArray&,
                               const TextureSamplerArray& samplers) SK_OVERRIDE {
             GrGLShaderBuilder::VertexBuilder* vertexBuilder = builder->getVertexBuilder();
             SkASSERT(NULL != vertexBuilder);
@@ -355,6 +357,7 @@
                               EffectKey key,
                               const char* outputColor,
                               const char* inputColor,
+                              const TransformedCoordsArray&,
                               const TextureSamplerArray& samplers) SK_OVERRIDE {
             GrGLShaderBuilder::VertexBuilder* vertexBuilder = builder->getVertexBuilder();
             SkASSERT(NULL != vertexBuilder);
diff --git a/src/gpu/GrSWMaskHelper.cpp b/src/gpu/GrSWMaskHelper.cpp
index 6b2a759..624b796 100644
--- a/src/gpu/GrSWMaskHelper.cpp
+++ b/src/gpu/GrSWMaskHelper.cpp
@@ -204,7 +204,7 @@
                          GrSimpleTextureEffect::Create(texture,
                                                        maskMatrix,
                                                        GrTextureParams::kNone_FilterMode,
-                                                       GrEffect::kPosition_CoordsType))->unref();
+                                                       kPosition_GrCoordSet))->unref();
 
     target->drawSimpleRect(dstRect);
 }
diff --git a/src/gpu/effects/GrBezierEffect.cpp b/src/gpu/effects/GrBezierEffect.cpp
index b3e9f62..9adf592 100644
--- a/src/gpu/effects/GrBezierEffect.cpp
+++ b/src/gpu/effects/GrBezierEffect.cpp
@@ -20,6 +20,7 @@
                           EffectKey key,
                           const char* outputColor,
                           const char* inputColor,
+                          const TransformedCoordsArray&,
                           const TextureSamplerArray&) SK_OVERRIDE;
 
     static inline EffectKey GenKey(const GrDrawEffect&, const GrGLCaps&);
@@ -44,6 +45,7 @@
                                EffectKey key,
                                const char* outputColor,
                                const char* inputColor,
+                               const TransformedCoordsArray&,
                                const TextureSamplerArray& samplers) {
     GrGLShaderBuilder::VertexBuilder* vertexBuilder = builder->getVertexBuilder();
     SkASSERT(NULL != vertexBuilder);
@@ -163,6 +165,7 @@
                           EffectKey key,
                           const char* outputColor,
                           const char* inputColor,
+                          const TransformedCoordsArray&,
                           const TextureSamplerArray&) SK_OVERRIDE;
 
     static inline EffectKey GenKey(const GrDrawEffect&, const GrGLCaps&);
@@ -187,6 +190,7 @@
                               EffectKey key,
                               const char* outputColor,
                               const char* inputColor,
+                              const TransformedCoordsArray&,
                               const TextureSamplerArray& samplers) {
     GrGLShaderBuilder::VertexBuilder* vertexBuilder = builder->getVertexBuilder();
     SkASSERT(NULL != vertexBuilder);
@@ -295,6 +299,7 @@
                           EffectKey key,
                           const char* outputColor,
                           const char* inputColor,
+                          const TransformedCoordsArray&,
                           const TextureSamplerArray&) SK_OVERRIDE;
 
     static inline EffectKey GenKey(const GrDrawEffect&, const GrGLCaps&);
@@ -319,6 +324,7 @@
                                EffectKey key,
                                const char* outputColor,
                                const char* inputColor,
+                               const TransformedCoordsArray&,
                                const TextureSamplerArray& samplers) {
     GrGLShaderBuilder::VertexBuilder* vertexBuilder = builder->getVertexBuilder();
     SkASSERT(NULL != vertexBuilder);
diff --git a/src/gpu/effects/GrBicubicEffect.cpp b/src/gpu/effects/GrBicubicEffect.cpp
index cf20d15..ccbf788 100644
--- a/src/gpu/effects/GrBicubicEffect.cpp
+++ b/src/gpu/effects/GrBicubicEffect.cpp
@@ -19,10 +19,9 @@
                           EffectKey,
                           const char* outputColor,
                           const char* inputColor,
+                          const TransformedCoordsArray&,
                           const TextureSamplerArray&) SK_OVERRIDE;
 
-    static inline EffectKey GenKey(const GrDrawEffect&, const GrGLCaps&);
-
     virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE;
 
 private:
@@ -31,15 +30,11 @@
     UniformHandle       fCoefficientsUni;
     UniformHandle       fImageIncrementUni;
 
-    GrGLEffectMatrix    fEffectMatrix;
-
     typedef GrGLEffect INHERITED;
 };
 
-GrGLBicubicEffect::GrGLBicubicEffect(const GrBackendEffectFactory& factory,
-                                     const GrDrawEffect& drawEffect)
-    : INHERITED(factory)
-    , fEffectMatrix(drawEffect.castEffect<GrBicubicEffect>().coordsType()) {
+GrGLBicubicEffect::GrGLBicubicEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&)
+    : INHERITED(factory) {
 }
 
 void GrGLBicubicEffect::emitCode(GrGLShaderBuilder* builder,
@@ -47,9 +42,9 @@
                                  EffectKey key,
                                  const char* outputColor,
                                  const char* inputColor,
+                                 const TransformedCoordsArray& coords,
                                  const TextureSamplerArray& samplers) {
-    SkString coords;
-    fEffectMatrix.emitCodeMakeFSCoords2D(builder, key, &coords);
+    SkString coords2D = builder->ensureFSCoords2D(coords, 0);
     fCoefficientsUni = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
                                            kMat44f_GrSLType, "Coefficients");
     fImageIncrementUni = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
@@ -76,7 +71,7 @@
                             "\tvec4 c = coefficients * ts;\n"
                             "\treturn c.x * c0 + c.y * c1 + c.z * c2 + c.w * c3;\n",
                             &cubicBlendName);
-    builder->fsCodeAppendf("\tvec2 coord = %s - %s * vec2(0.5, 0.5);\n", coords.c_str(), imgInc);
+    builder->fsCodeAppendf("\tvec2 coord = %s - %s * vec2(0.5, 0.5);\n", coords2D.c_str(), imgInc);
     builder->fsCodeAppendf("\tvec2 f = fract(coord / %s);\n", imgInc);
     for (int y = 0; y < 4; ++y) {
         for (int x = 0; x < 4; ++x) {
@@ -91,15 +86,6 @@
     builder->fsCodeAppendf("\t%s = %s(%s, f.y, s0, s1, s2, s3);\n", outputColor, cubicBlendName.c_str(), coeff);
 }
 
-GrGLEffect::EffectKey GrGLBicubicEffect::GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) {
-    const GrBicubicEffect& bicubic = drawEffect.castEffect<GrBicubicEffect>();
-    EffectKey matrixKey = GrGLEffectMatrix::GenKey(bicubic.getMatrix(),
-                                                   drawEffect,
-                                                   bicubic.coordsType(),
-                                                   bicubic.texture(0));
-    return matrixKey;
-}
-
 void GrGLBicubicEffect::setData(const GrGLUniformManager& uman,
                                 const GrDrawEffect& drawEffect) {
     const GrBicubicEffect& effect = drawEffect.castEffect<GrBicubicEffect>();
@@ -109,10 +95,6 @@
     imageIncrement[1] = 1.0f / texture.height();
     uman.set2fv(fImageIncrementUni, 0, 1, imageIncrement);
     uman.setMatrix4f(fCoefficientsUni, effect.coefficients());
-    fEffectMatrix.setData(uman,
-                          effect.getMatrix(),
-                          drawEffect,
-                          effect.texture(0));
 }
 
 GrBicubicEffect::GrBicubicEffect(GrTexture* texture,
@@ -130,8 +112,8 @@
                                  const SkScalar coefficients[16],
                                  const SkMatrix &matrix,
                                  const GrTextureParams &params,
-                                 CoordsType coordsType)
-  : INHERITED(texture, MakeDivByTextureWHMatrix(texture), params, coordsType) {
+                                 GrCoordSet coordSet)
+  : INHERITED(texture, MakeDivByTextureWHMatrix(texture), params, coordSet) {
     for (int y = 0; y < 4; y++) {
         for (int x = 0; x < 4; x++) {
             // Convert from row-major scalars to column-major floats.
diff --git a/src/gpu/effects/GrBicubicEffect.h b/src/gpu/effects/GrBicubicEffect.h
index 0a40891..eabc79f 100644
--- a/src/gpu/effects/GrBicubicEffect.h
+++ b/src/gpu/effects/GrBicubicEffect.h
@@ -11,7 +11,6 @@
 #include "GrSingleTextureEffect.h"
 #include "GrDrawEffect.h"
 #include "gl/GrGLEffect.h"
-#include "gl/GrGLEffectMatrix.h"
 #include "GrTBackendEffectFactory.h"
 
 class GrGLBicubicEffect;
@@ -36,8 +35,8 @@
     static GrEffectRef* Create(GrTexture* tex, const SkScalar coefficients[16],
                                const SkMatrix& matrix,
                                const GrTextureParams& p,
-                               CoordsType coordsType = kLocal_CoordsType) {
-        AutoEffectUnref effect(SkNEW_ARGS(GrBicubicEffect, (tex, coefficients, matrix, p, coordsType)));
+                               GrCoordSet coordSet = kLocal_GrCoordSet) {
+        AutoEffectUnref effect(SkNEW_ARGS(GrBicubicEffect, (tex, coefficients, matrix, p, coordSet)));
         return CreateEffectRef(effect);
     }
 
@@ -48,14 +47,14 @@
     static GrEffectRef* Create(GrTexture* tex,
                                const SkMatrix& matrix,
                                const GrTextureParams& p,
-                               CoordsType coordsType = kLocal_CoordsType) {
-        return Create(tex, gMitchellCoefficients, matrix, p, coordsType);
+                               GrCoordSet coordSet = kLocal_GrCoordSet) {
+        return Create(tex, gMitchellCoefficients, matrix, p, coordSet);
     }
 
 private:
     GrBicubicEffect(GrTexture*, const SkScalar coefficients[16]);
     GrBicubicEffect(GrTexture*, const SkScalar coefficients[16],
-                    const SkMatrix &matrix, const GrTextureParams &p, CoordsType coordsType);
+                    const SkMatrix &matrix, const GrTextureParams &p, GrCoordSet coordSet);
     virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE;
     float    fCoefficients[16];
 
diff --git a/src/gpu/effects/GrConfigConversionEffect.cpp b/src/gpu/effects/GrConfigConversionEffect.cpp
index 935e074..daf5141 100644
--- a/src/gpu/effects/GrConfigConversionEffect.cpp
+++ b/src/gpu/effects/GrConfigConversionEffect.cpp
@@ -10,15 +10,13 @@
 #include "GrTBackendEffectFactory.h"
 #include "GrSimpleTextureEffect.h"
 #include "gl/GrGLEffect.h"
-#include "gl/GrGLEffectMatrix.h"
 #include "SkMatrix.h"
 
 class GrGLConfigConversionEffect : public GrGLEffect {
 public:
     GrGLConfigConversionEffect(const GrBackendEffectFactory& factory,
                                const GrDrawEffect& drawEffect)
-    : INHERITED (factory)
-    , fEffectMatrix(drawEffect.castEffect<GrConfigConversionEffect>().coordsType()) {
+    : INHERITED (factory) {
         const GrConfigConversionEffect& effect = drawEffect.castEffect<GrConfigConversionEffect>();
         fSwapRedAndBlue = effect.swapsRedAndBlue();
         fPMConversion = effect.pmConversion();
@@ -29,11 +27,10 @@
                           EffectKey key,
                           const char* outputColor,
                           const char* inputColor,
+                          const TransformedCoordsArray& coords,
                           const TextureSamplerArray& samplers) SK_OVERRIDE {
-        SkString coords;
-        GrSLType coordsType = fEffectMatrix.emitCode(builder, key, &coords);
         builder->fsCodeAppendf("\t\t%s = ", outputColor);
-        builder->fsAppendTextureLookup(samplers[0], coords.c_str(), coordsType);
+        builder->fsAppendTextureLookup(samplers[0], coords[0].c_str(), coords[0].type());
         builder->fsCodeAppend(";\n");
         if (GrConfigConversionEffect::kNone_PMConversion == fPMConversion) {
             SkASSERT(fSwapRedAndBlue);
@@ -73,27 +70,14 @@
         builder->fsCodeAppend(modulate.c_str());
     }
 
-    void setData(const GrGLUniformManager& uman, const GrDrawEffect& drawEffect) {
-        const GrConfigConversionEffect& conv = drawEffect.castEffect<GrConfigConversionEffect>();
-        fEffectMatrix.setData(uman, conv.getMatrix(), drawEffect, conv.texture(0));
-    }
-
     static inline EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) {
         const GrConfigConversionEffect& conv = drawEffect.castEffect<GrConfigConversionEffect>();
-        EffectKey key = static_cast<EffectKey>(conv.swapsRedAndBlue()) | (conv.pmConversion() << 1);
-        key <<= GrGLEffectMatrix::kKeyBits;
-        EffectKey matrixKey =  GrGLEffectMatrix::GenKey(conv.getMatrix(),
-                                                        drawEffect,
-                                                        conv.coordsType(),
-                                                        conv.texture(0));
-        SkASSERT(!(matrixKey & key));
-        return matrixKey | key;
+        return static_cast<EffectKey>(conv.swapsRedAndBlue()) | (conv.pmConversion() << 1);
     }
 
 private:
     bool                                    fSwapRedAndBlue;
     GrConfigConversionEffect::PMConversion  fPMConversion;
-    GrGLEffectMatrix                        fEffectMatrix;
 
     typedef GrGLEffect INHERITED;
 
diff --git a/src/gpu/effects/GrConvolutionEffect.cpp b/src/gpu/effects/GrConvolutionEffect.cpp
index 19fed1c..4d16361 100644
--- a/src/gpu/effects/GrConvolutionEffect.cpp
+++ b/src/gpu/effects/GrConvolutionEffect.cpp
@@ -7,7 +7,6 @@
 
 #include "GrConvolutionEffect.h"
 #include "gl/GrGLEffect.h"
-#include "gl/GrGLEffectMatrix.h"
 #include "gl/GrGLSL.h"
 #include "gl/GrGLTexture.h"
 #include "GrTBackendEffectFactory.h"
@@ -24,6 +23,7 @@
                           EffectKey,
                           const char* outputColor,
                           const char* inputColor,
+                          const TransformedCoordsArray&,
                           const TextureSamplerArray&) SK_OVERRIDE;
 
     virtual void setData(const GrGLUniformManager& uman, const GrDrawEffect&) SK_OVERRIDE;
@@ -41,15 +41,13 @@
     UniformHandle       fKernelUni;
     UniformHandle       fImageIncrementUni;
     UniformHandle       fBoundsUni;
-    GrGLEffectMatrix    fEffectMatrix;
 
     typedef GrGLEffect INHERITED;
 };
 
 GrGLConvolutionEffect::GrGLConvolutionEffect(const GrBackendEffectFactory& factory,
                                              const GrDrawEffect& drawEffect)
-    : INHERITED(factory)
-    , fEffectMatrix(drawEffect.castEffect<GrConvolutionEffect>().coordsType()) {
+    : INHERITED(factory) {
     const GrConvolutionEffect& c = drawEffect.castEffect<GrConvolutionEffect>();
     fRadius = c.radius();
     fUseBounds = c.useBounds();
@@ -61,9 +59,9 @@
                                      EffectKey key,
                                      const char* outputColor,
                                      const char* inputColor,
+                                     const TransformedCoordsArray& coords,
                                      const TextureSamplerArray& samplers) {
-    SkString coords;
-    fEffectMatrix.emitCodeMakeFSCoords2D(builder, key, &coords);
+    SkString coords2D = builder->ensureFSCoords2D(coords, 0);
     fImageIncrementUni = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
                                              kVec2f_GrSLType, "ImageIncrement");
     if (this->useBounds()) {
@@ -79,7 +77,7 @@
     const GrGLShaderVar& kernel = builder->getUniformVariable(fKernelUni);
     const char* imgInc = builder->getUniformCStr(fImageIncrementUni);
 
-    builder->fsCodeAppendf("\t\tvec2 coord = %s - %d.0 * %s;\n", coords.c_str(), fRadius, imgInc);
+    builder->fsCodeAppendf("\t\tvec2 coord = %s - %d.0 * %s;\n", coords2D.c_str(), fRadius, imgInc);
 
     // Manually unroll loop because some drivers don't; yields 20-30% speedup.
     for (int i = 0; i < width; i++) {
@@ -133,7 +131,6 @@
         }
     }
     uman.set1fv(fKernelUni, 0, this->width(), conv.kernel());
-    fEffectMatrix.setData(uman, conv.getMatrix(), drawEffect, conv.texture(0));
 }
 
 GrGLEffect::EffectKey GrGLConvolutionEffect::GenKey(const GrDrawEffect& drawEffect,
@@ -145,12 +142,7 @@
         key |= 0x2;
         key |= GrConvolutionEffect::kY_Direction == conv.direction() ? 0x1 : 0x0;
     }
-    key <<= GrGLEffectMatrix::kKeyBits;
-    EffectKey matrixKey = GrGLEffectMatrix::GenKey(conv.getMatrix(),
-                                                   drawEffect,
-                                                   conv.coordsType(),
-                                                   conv.texture(0));
-    return key | matrixKey;
+    return key;
 }
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/src/gpu/effects/GrCustomCoordsTextureEffect.cpp b/src/gpu/effects/GrCustomCoordsTextureEffect.cpp
index a5c28c5..b14de8b 100644
--- a/src/gpu/effects/GrCustomCoordsTextureEffect.cpp
+++ b/src/gpu/effects/GrCustomCoordsTextureEffect.cpp
@@ -7,7 +7,6 @@
 
 #include "GrCustomCoordsTextureEffect.h"
 #include "gl/GrGLEffect.h"
-#include "gl/GrGLEffectMatrix.h"
 #include "gl/GrGLSL.h"
 #include "gl/GrGLTexture.h"
 #include "GrTBackendEffectFactory.h"
@@ -23,6 +22,7 @@
                           EffectKey key,
                           const char* outputColor,
                           const char* inputColor,
+                          const TransformedCoordsArray&,
                           const TextureSamplerArray& samplers) SK_OVERRIDE {
         GrGLShaderBuilder::VertexBuilder* vertexBuilder = builder->getVertexBuilder();
         SkASSERT(NULL != vertexBuilder);
@@ -46,10 +46,6 @@
         builder->fsCodeAppend(";\n");
     }
 
-    static inline EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) {
-        return 1 << GrGLEffectMatrix::kKeyBits;
-    }
-
     virtual void setData(const GrGLUniformManager& uman,
                          const GrDrawEffect& drawEffect) SK_OVERRIDE {}
 
diff --git a/src/gpu/effects/GrSimpleTextureEffect.cpp b/src/gpu/effects/GrSimpleTextureEffect.cpp
index 0ee6f78..b27b737 100644
--- a/src/gpu/effects/GrSimpleTextureEffect.cpp
+++ b/src/gpu/effects/GrSimpleTextureEffect.cpp
@@ -7,7 +7,6 @@
 
 #include "GrSimpleTextureEffect.h"
 #include "gl/GrGLEffect.h"
-#include "gl/GrGLEffectMatrix.h"
 #include "gl/GrGLSL.h"
 #include "gl/GrGLTexture.h"
 #include "GrTBackendEffectFactory.h"
@@ -15,9 +14,8 @@
 
 class GrGLSimpleTextureEffect : public GrGLEffect {
 public:
-    GrGLSimpleTextureEffect(const GrBackendEffectFactory& factory, const GrDrawEffect& drawEffect)
-        : INHERITED (factory)
-        , fEffectMatrix(drawEffect.castEffect<GrSimpleTextureEffect>().coordsType()) {
+    GrGLSimpleTextureEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&)
+        : INHERITED (factory) {
     }
 
     virtual void emitCode(GrGLShaderBuilder* builder,
@@ -25,35 +23,17 @@
                           EffectKey key,
                           const char* outputColor,
                           const char* inputColor,
+                          const TransformedCoordsArray& coords,
                           const TextureSamplerArray& samplers) SK_OVERRIDE {
-        SkString fsCoordName;
-        GrSLType fsCoordSLType;
-        fsCoordSLType = fEffectMatrix.emitCode(builder, key, &fsCoordName);
-
         builder->fsCodeAppendf("\t%s = ", outputColor);
         builder->fsAppendTextureLookupAndModulate(inputColor,
                                                   samplers[0],
-                                                  fsCoordName.c_str(),
-                                                  fsCoordSLType);
+                                                  coords[0].c_str(),
+                                                  coords[0].type());
         builder->fsCodeAppend(";\n");
     }
 
-    static inline EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) {
-        const GrSimpleTextureEffect& ste = drawEffect.castEffect<GrSimpleTextureEffect>();
-        return GrGLEffectMatrix::GenKey(ste.getMatrix(),
-                                        drawEffect,
-                                        ste.coordsType(),
-                                        ste.texture(0));
-    }
-
-    virtual void setData(const GrGLUniformManager& uman,
-                         const GrDrawEffect& drawEffect) SK_OVERRIDE {
-        const GrSimpleTextureEffect& ste = drawEffect.castEffect<GrSimpleTextureEffect>();
-        fEffectMatrix.setData(uman, ste.getMatrix(), drawEffect, ste.texture(0));
-    }
-
 private:
-    GrGLEffectMatrix fEffectMatrix;
     typedef GrGLEffect INHERITED;
 };
 
@@ -89,12 +69,12 @@
     GrTextureParams params(tileModes, random->nextBool() ? GrTextureParams::kBilerp_FilterMode :
                                                            GrTextureParams::kNone_FilterMode);
 
-    static const CoordsType kCoordsTypes[] = {
-        kLocal_CoordsType,
-        kPosition_CoordsType
+    static const GrCoordSet kCoordSets[] = {
+        kLocal_GrCoordSet,
+        kPosition_GrCoordSet
     };
-    CoordsType coordsType = kCoordsTypes[random->nextULessThan(GR_ARRAY_COUNT(kCoordsTypes))];
+    GrCoordSet coordSet = kCoordSets[random->nextULessThan(GR_ARRAY_COUNT(kCoordSets))];
 
     const SkMatrix& matrix = GrEffectUnitTest::TestMatrix(random);
-    return GrSimpleTextureEffect::Create(textures[texIdx], matrix, coordsType);
+    return GrSimpleTextureEffect::Create(textures[texIdx], matrix, coordSet);
 }
diff --git a/src/gpu/effects/GrSimpleTextureEffect.h b/src/gpu/effects/GrSimpleTextureEffect.h
index c694197..c326ccf 100644
--- a/src/gpu/effects/GrSimpleTextureEffect.h
+++ b/src/gpu/effects/GrSimpleTextureEffect.h
@@ -25,8 +25,8 @@
     /* unfiltered, clamp mode */
     static GrEffectRef* Create(GrTexture* tex,
                                const SkMatrix& matrix,
-                               CoordsType coordsType = kLocal_CoordsType) {
-        AutoEffectUnref effect(SkNEW_ARGS(GrSimpleTextureEffect, (tex, matrix, GrTextureParams::kNone_FilterMode, coordsType)));
+                               GrCoordSet coordSet = kLocal_GrCoordSet) {
+        AutoEffectUnref effect(SkNEW_ARGS(GrSimpleTextureEffect, (tex, matrix, GrTextureParams::kNone_FilterMode, coordSet)));
         return CreateEffectRef(effect);
     }
 
@@ -34,17 +34,17 @@
     static GrEffectRef* Create(GrTexture* tex,
                                const SkMatrix& matrix,
                                GrTextureParams::FilterMode filterMode,
-                               CoordsType coordsType = kLocal_CoordsType) {
+                               GrCoordSet coordSet = kLocal_GrCoordSet) {
         AutoEffectUnref effect(
-            SkNEW_ARGS(GrSimpleTextureEffect, (tex, matrix, filterMode, coordsType)));
+            SkNEW_ARGS(GrSimpleTextureEffect, (tex, matrix, filterMode, coordSet)));
         return CreateEffectRef(effect);
     }
 
     static GrEffectRef* Create(GrTexture* tex,
                                const SkMatrix& matrix,
                                const GrTextureParams& p,
-                               CoordsType coordsType = kLocal_CoordsType) {
-        AutoEffectUnref effect(SkNEW_ARGS(GrSimpleTextureEffect, (tex, matrix, p, coordsType)));
+                               GrCoordSet coordSet = kLocal_GrCoordSet) {
+        AutoEffectUnref effect(SkNEW_ARGS(GrSimpleTextureEffect, (tex, matrix, p, coordSet)));
         return CreateEffectRef(effect);
     }
 
@@ -62,20 +62,20 @@
     GrSimpleTextureEffect(GrTexture* texture,
                           const SkMatrix& matrix,
                           GrTextureParams::FilterMode filterMode,
-                          CoordsType coordsType)
-        : GrSingleTextureEffect(texture, matrix, filterMode, coordsType) {
+                          GrCoordSet coordSet)
+        : GrSingleTextureEffect(texture, matrix, filterMode, coordSet) {
     }
 
     GrSimpleTextureEffect(GrTexture* texture,
                           const SkMatrix& matrix,
                           const GrTextureParams& params,
-                          CoordsType coordsType)
-        : GrSingleTextureEffect(texture, matrix, params, coordsType) {
+                          GrCoordSet coordSet)
+        : GrSingleTextureEffect(texture, matrix, params, coordSet) {
     }
 
     virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE {
         const GrSimpleTextureEffect& ste = CastEffect<GrSimpleTextureEffect>(other);
-        return this->hasSameTextureParamsMatrixAndCoordsType(ste);
+        return this->hasSameTextureParamsMatrixAndSourceCoords(ste);
     }
 
     GR_DECLARE_EFFECT_TEST;
diff --git a/src/gpu/effects/GrSingleTextureEffect.cpp b/src/gpu/effects/GrSingleTextureEffect.cpp
index 532ce04..3ab5e3a 100644
--- a/src/gpu/effects/GrSingleTextureEffect.cpp
+++ b/src/gpu/effects/GrSingleTextureEffect.cpp
@@ -9,30 +9,30 @@
 
 GrSingleTextureEffect::GrSingleTextureEffect(GrTexture* texture,
                                              const SkMatrix& m,
-                                             CoordsType coordsType)
-    : fTextureAccess(texture)
-    , fMatrix(m)
-    , fCoordsType(coordsType) {
+                                             GrCoordSet coordSet)
+    : fCoordTransform(coordSet, m, texture)
+    , fTextureAccess(texture) {
+    this->addCoordTransform(&fCoordTransform);
     this->addTextureAccess(&fTextureAccess);
 }
 
 GrSingleTextureEffect::GrSingleTextureEffect(GrTexture* texture,
                                              const SkMatrix& m,
                                              GrTextureParams::FilterMode filterMode,
-                                             CoordsType coordsType)
-    : fTextureAccess(texture, filterMode)
-    , fMatrix(m)
-    , fCoordsType(coordsType) {
+                                             GrCoordSet coordSet)
+    : fCoordTransform(coordSet, m, texture)
+    , fTextureAccess(texture, filterMode) {
+    this->addCoordTransform(&fCoordTransform);
     this->addTextureAccess(&fTextureAccess);
 }
 
 GrSingleTextureEffect::GrSingleTextureEffect(GrTexture* texture,
                                              const SkMatrix& m,
                                              const GrTextureParams& params,
-                                             CoordsType coordsType)
-    : fTextureAccess(texture, params)
-    , fMatrix(m)
-    , fCoordsType(coordsType) {
+                                             GrCoordSet coordSet)
+    : fCoordTransform(coordSet, m, texture)
+    , fTextureAccess(texture, params) {
+    this->addCoordTransform(&fCoordTransform);
     this->addTextureAccess(&fTextureAccess);
 }
 
diff --git a/src/gpu/effects/GrSingleTextureEffect.h b/src/gpu/effects/GrSingleTextureEffect.h
index 27a7d79..a8f9a6d 100644
--- a/src/gpu/effects/GrSingleTextureEffect.h
+++ b/src/gpu/effects/GrSingleTextureEffect.h
@@ -10,6 +10,7 @@
 
 #include "GrEffect.h"
 #include "SkMatrix.h"
+#include "GrCoordTransform.h"
 
 class GrTexture;
 
@@ -21,30 +22,25 @@
 public:
     virtual ~GrSingleTextureEffect();
 
-    const SkMatrix& getMatrix() const { return fMatrix; }
-
-    /** Indicates whether the matrix operates on local coords or positions */
-    CoordsType coordsType() const { return fCoordsType; }
-
 protected:
     /** unfiltered, clamp mode */
-    GrSingleTextureEffect(GrTexture*, const SkMatrix&, CoordsType = kLocal_CoordsType);
+    GrSingleTextureEffect(GrTexture*, const SkMatrix&, GrCoordSet = kLocal_GrCoordSet);
     /** clamp mode */
     GrSingleTextureEffect(GrTexture*, const SkMatrix&, GrTextureParams::FilterMode filterMode,
-                          CoordsType = kLocal_CoordsType);
+                          GrCoordSet = kLocal_GrCoordSet);
     GrSingleTextureEffect(GrTexture*,
                           const SkMatrix&,
                           const GrTextureParams&,
-                          CoordsType = kLocal_CoordsType);
+                          GrCoordSet = kLocal_GrCoordSet);
 
     /**
      * Helper for subclass onIsEqual() functions.
      */
-    bool hasSameTextureParamsMatrixAndCoordsType(const GrSingleTextureEffect& other) const {
+    bool hasSameTextureParamsMatrixAndSourceCoords(const GrSingleTextureEffect& other) const {
         // We don't have to check the accesses' swizzles because they are inferred from the texture.
         return fTextureAccess == other.fTextureAccess &&
-               this->getMatrix().cheapEqualTo(other.getMatrix()) &&
-               fCoordsType == other.fCoordsType;
+               fCoordTransform.getMatrix().cheapEqualTo(other.fCoordTransform.getMatrix()) &&
+               fCoordTransform.sourceCoords() == other.fCoordTransform.sourceCoords();
     }
 
     /**
@@ -62,9 +58,8 @@
     }
 
 private:
-    GrTextureAccess fTextureAccess;
-    SkMatrix        fMatrix;
-    CoordsType      fCoordsType;
+    GrCoordTransform fCoordTransform;
+    GrTextureAccess  fTextureAccess;
 
     typedef GrEffect INHERITED;
 };
diff --git a/src/gpu/effects/GrTextureDomainEffect.cpp b/src/gpu/effects/GrTextureDomainEffect.cpp
index 59bd909..eca99d7 100644
--- a/src/gpu/effects/GrTextureDomainEffect.cpp
+++ b/src/gpu/effects/GrTextureDomainEffect.cpp
@@ -9,7 +9,6 @@
 #include "GrSimpleTextureEffect.h"
 #include "GrTBackendEffectFactory.h"
 #include "gl/GrGLEffect.h"
-#include "gl/GrGLEffectMatrix.h"
 #include "SkFloatingPoint.h"
 
 class GrGLTextureDomainEffect : public GrGLEffect {
@@ -21,6 +20,7 @@
                           EffectKey,
                           const char* outputColor,
                           const char* inputColor,
+                          const TransformedCoordsArray&,
                           const TextureSamplerArray&) SK_OVERRIDE;
 
     virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE;
@@ -29,16 +29,14 @@
 
 private:
     GrGLUniformManager::UniformHandle fNameUni;
-    GrGLEffectMatrix                  fEffectMatrix;
     GrGLfloat                         fPrevDomain[4];
 
     typedef GrGLEffect INHERITED;
 };
 
 GrGLTextureDomainEffect::GrGLTextureDomainEffect(const GrBackendEffectFactory& factory,
-                                                 const GrDrawEffect& drawEffect)
-    : INHERITED(factory)
-    , fEffectMatrix(drawEffect.castEffect<GrTextureDomainEffect>().coordsType()) {
+                                                 const GrDrawEffect&)
+    : INHERITED(factory) {
     fPrevDomain[0] = SK_FloatNaN;
 }
 
@@ -47,18 +45,18 @@
                                        EffectKey key,
                                        const char* outputColor,
                                        const char* inputColor,
+                                       const TransformedCoordsArray& coords,
                                        const TextureSamplerArray& samplers) {
     const GrTextureDomainEffect& texDom = drawEffect.castEffect<GrTextureDomainEffect>();
 
-    SkString coords;
-    fEffectMatrix.emitCodeMakeFSCoords2D(builder, key, &coords);
+    SkString coords2D = builder->ensureFSCoords2D(coords, 0);
     const char* domain;
     fNameUni = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
                                     kVec4f_GrSLType, "TexDom", &domain);
     if (GrTextureDomainEffect::kClamp_WrapMode == texDom.wrapMode()) {
 
         builder->fsCodeAppendf("\tvec2 clampCoord = clamp(%s, %s.xy, %s.zw);\n",
-                                coords.c_str(), domain, domain);
+                                coords2D.c_str(), domain, domain);
 
         builder->fsCodeAppendf("\t%s = ", outputColor);
         builder->fsAppendTextureLookupAndModulate(inputColor, samplers[0], "clampCoord");
@@ -75,21 +73,21 @@
             // result=white;" code fails to compile.
             builder->fsCodeAppend("\tvec4 outside = vec4(0.0, 0.0, 0.0, 0.0);\n");
             builder->fsCodeAppend("\tvec4 inside = ");
-            builder->fsAppendTextureLookupAndModulate(inputColor, samplers[0], coords.c_str());
+            builder->fsAppendTextureLookupAndModulate(inputColor, samplers[0], coords2D.c_str());
             builder->fsCodeAppend(";\n");
 
             builder->fsCodeAppendf("\tfloat x = abs(2.0*(%s.x - %s.x)/(%s.z - %s.x) - 1.0);\n",
-                                   coords.c_str(), domain, domain, domain);
+                                   coords2D.c_str(), domain, domain, domain);
             builder->fsCodeAppendf("\tfloat y = abs(2.0*(%s.y - %s.y)/(%s.w - %s.y) - 1.0);\n",
-                                   coords.c_str(), domain, domain, domain);
+                                   coords2D.c_str(), domain, domain, domain);
             builder->fsCodeAppend("\tfloat blend = step(1.0, max(x, y));\n");
             builder->fsCodeAppendf("\t%s = mix(inside, outside, blend);\n", outputColor);
         } else {
             builder->fsCodeAppend("\tbvec4 outside;\n");
-            builder->fsCodeAppendf("\toutside.xy = lessThan(%s, %s.xy);\n", coords.c_str(), domain);
-            builder->fsCodeAppendf("\toutside.zw = greaterThan(%s, %s.zw);\n", coords.c_str(), domain);
+            builder->fsCodeAppendf("\toutside.xy = lessThan(%s, %s.xy);\n", coords2D.c_str(), domain);
+            builder->fsCodeAppendf("\toutside.zw = greaterThan(%s, %s.zw);\n", coords2D.c_str(), domain);
             builder->fsCodeAppendf("\t%s = any(outside) ? vec4(0.0, 0.0, 0.0, 0.0) : ", outputColor);
-            builder->fsAppendTextureLookupAndModulate(inputColor, samplers[0], coords.c_str());
+            builder->fsAppendTextureLookupAndModulate(inputColor, samplers[0], coords2D.c_str());
             builder->fsCodeAppend(";\n");
         }
     }
@@ -118,22 +116,11 @@
         uman.set4fv(fNameUni, 0, 1, values);
         memcpy(fPrevDomain, values, 4 * sizeof(GrGLfloat));
     }
-    fEffectMatrix.setData(uman,
-                          texDom.getMatrix(),
-                          drawEffect,
-                          texDom.texture(0));
 }
 
 GrGLEffect::EffectKey GrGLTextureDomainEffect::GenKey(const GrDrawEffect& drawEffect,
                                                       const GrGLCaps&) {
-    const GrTextureDomainEffect& texDom = drawEffect.castEffect<GrTextureDomainEffect>();
-    EffectKey key = texDom.wrapMode();
-    key <<= GrGLEffectMatrix::kKeyBits;
-    EffectKey matrixKey = GrGLEffectMatrix::GenKey(texDom.getMatrix(),
-                                                   drawEffect,
-                                                   texDom.coordsType(),
-                                                   texDom.texture(0));
-    return key | matrixKey;
+    return drawEffect.castEffect<GrTextureDomainEffect>().wrapMode();
 }
 
 
@@ -144,7 +131,7 @@
                                            const SkRect& domain,
                                            WrapMode wrapMode,
                                            GrTextureParams::FilterMode filterMode,
-                                           CoordsType coordsType) {
+                                           GrCoordSet coordSet) {
     static const SkRect kFullRect = {0, 0, SK_Scalar1, SK_Scalar1};
     if (kClamp_WrapMode == wrapMode && domain.contains(kFullRect)) {
         return GrSimpleTextureEffect::Create(texture, matrix, filterMode);
@@ -167,7 +154,7 @@
                                                                   clippedDomain,
                                                                   wrapMode,
                                                                   filterMode,
-                                                                  coordsType)));
+                                                                  coordSet)));
         return CreateEffectRef(effect);
 
     }
@@ -178,8 +165,8 @@
                                              const SkRect& domain,
                                              WrapMode wrapMode,
                                              GrTextureParams::FilterMode filterMode,
-                                             CoordsType coordsType)
-    : GrSingleTextureEffect(texture, matrix, filterMode, coordsType)
+                                             GrCoordSet coordSet)
+    : GrSingleTextureEffect(texture, matrix, filterMode, coordSet)
     , fWrapMode(wrapMode)
     , fTextureDomain(domain) {
 }
@@ -194,7 +181,7 @@
 
 bool GrTextureDomainEffect::onIsEqual(const GrEffect& sBase) const {
     const GrTextureDomainEffect& s = CastEffect<GrTextureDomainEffect>(sBase);
-    return this->hasSameTextureParamsMatrixAndCoordsType(s) &&
+    return this->hasSameTextureParamsMatrixAndSourceCoords(s) &&
            this->fTextureDomain == s.fTextureDomain;
 }
 
@@ -224,7 +211,7 @@
     WrapMode wrapMode = random->nextBool() ? kClamp_WrapMode : kDecal_WrapMode;
     const SkMatrix& matrix = GrEffectUnitTest::TestMatrix(random);
     bool bilerp = random->nextBool();
-    CoordsType coords = random->nextBool() ? kLocal_CoordsType : kPosition_CoordsType;
+    GrCoordSet coords = random->nextBool() ? kLocal_GrCoordSet : kPosition_GrCoordSet;
     return GrTextureDomainEffect::Create(textures[texIdx],
                                          matrix,
                                          domain,
diff --git a/src/gpu/effects/GrTextureDomainEffect.h b/src/gpu/effects/GrTextureDomainEffect.h
index d07f2fc..46ee2a6 100644
--- a/src/gpu/effects/GrTextureDomainEffect.h
+++ b/src/gpu/effects/GrTextureDomainEffect.h
@@ -39,7 +39,7 @@
                                const SkRect& domain,
                                WrapMode,
                                GrTextureParams::FilterMode filterMode,
-                               CoordsType = kLocal_CoordsType);
+                               GrCoordSet = kLocal_GrCoordSet);
 
     virtual ~GrTextureDomainEffect();
 
@@ -77,7 +77,7 @@
                           const SkRect& domain,
                           WrapMode,
                           GrTextureParams::FilterMode filterMode,
-                          CoordsType type);
+                          GrCoordSet);
 
     virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE;
 
diff --git a/src/gpu/gl/GrGLCoordTransform.cpp b/src/gpu/gl/GrGLCoordTransform.cpp
new file mode 100644
index 0000000..bfbae1e
--- /dev/null
+++ b/src/gpu/gl/GrGLCoordTransform.cpp
@@ -0,0 +1,188 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrGLCoordTransform.h"
+#include "GrDrawEffect.h"
+#include "GrTexture.h"
+#include "GrGLShaderBuilder.h"
+
+GrGLCoordTransform::EffectKey GrGLCoordTransform::GenKey(const GrDrawEffect& drawEffect,
+                                                         int transformIdx) {
+    const GrCoordTransform& transform = (*drawEffect.effect())->coordTransform(transformIdx);
+    EffectKey key = 0;
+    SkMatrix::TypeMask type0 = transform.getMatrix().getType();
+    SkMatrix::TypeMask type1;
+    if (kLocal_GrCoordSet == transform.sourceCoords()) {
+        type1 = drawEffect.getCoordChangeMatrix().getType();
+    } else {
+        if (drawEffect.programHasExplicitLocalCoords()) {
+            // We only make the key indicate that device coords are referenced when the local coords
+            // are not actually determined by positions. Otherwise the local coords var and position
+            // var are identical.
+            key |= kPositionCoords_Flag;
+        }
+        type1 = SkMatrix::kIdentity_Mask;
+    }
+
+    int combinedTypes = type0 | type1;
+
+    bool reverseY = transform.reverseY();
+
+    if (SkMatrix::kPerspective_Mask & combinedTypes) {
+        key |= kGeneral_MatrixType;
+    } else if (((SkMatrix::kAffine_Mask | SkMatrix::kScale_Mask) & combinedTypes) || reverseY) {
+        key |= kNoPersp_MatrixType;
+    } else if (SkMatrix::kTranslate_Mask & combinedTypes) {
+        key |= kTrans_MatrixType;
+    } else {
+        key |= kIdentity_MatrixType;
+    }
+    return key;
+}
+
+void GrGLCoordTransform::emitCode(GrGLShaderBuilder* builder,
+                                  EffectKey key,
+                                  TransformedCoords* transformedCoords,
+                                  int suffix) {
+    GrGLShaderBuilder::VertexBuilder* vertexBuilder = builder->getVertexBuilder();
+    SkASSERT(NULL != vertexBuilder);
+
+    GrSLType varyingType = kVoid_GrSLType;
+    const char* uniName;
+    switch (key & kMatrixTypeKeyMask) {
+        case kIdentity_MatrixType:
+            fUniType = kVoid_GrSLType;
+            uniName = NULL;
+            varyingType = kVec2f_GrSLType;
+            break;
+        case kTrans_MatrixType:
+            fUniType = kVec2f_GrSLType;
+            uniName = "StageTranslate";
+            varyingType = kVec2f_GrSLType;
+            break;
+        case kNoPersp_MatrixType:
+            fUniType = kMat33f_GrSLType;
+            uniName = "StageMatrix";
+            varyingType = kVec2f_GrSLType;
+            break;
+        case kGeneral_MatrixType:
+            fUniType = kMat33f_GrSLType;
+            uniName = "StageMatrix";
+            varyingType = kVec3f_GrSLType;
+            break;
+        default:
+            GrCrash("Unexpected key.");
+    }
+    SkString suffixedUniName;
+    if (kVoid_GrSLType != fUniType) {
+        if (0 != suffix) {
+            suffixedUniName.append(uniName);
+            suffixedUniName.appendf("_%i", suffix);
+            uniName = suffixedUniName.c_str();
+        }
+        fUni = builder->addUniform(GrGLShaderBuilder::kVertex_Visibility,
+                                   fUniType,
+                                   uniName,
+                                   &uniName);
+    }
+
+    const char* varyingName = "MatrixCoord";
+    SkString suffixedVaryingName;
+    if (0 != suffix) {
+        suffixedVaryingName.append(varyingName);
+        suffixedVaryingName.appendf("_%i", suffix);
+        varyingName = suffixedVaryingName.c_str();
+    }
+    const char* vsVaryingName;
+    const char* fsVaryingName;
+    vertexBuilder->addVarying(varyingType, varyingName, &vsVaryingName, &fsVaryingName);
+
+    const GrGLShaderVar& coords = (kPositionCoords_Flag & key) ?
+                                      vertexBuilder->positionAttribute() :
+                                      vertexBuilder->localCoordsAttribute();
+    // varying = matrix * coords (logically)
+    switch (fUniType) {
+        case kVoid_GrSLType:
+            SkASSERT(kVec2f_GrSLType == varyingType);
+            vertexBuilder->vsCodeAppendf("\t%s = %s;\n", vsVaryingName, coords.c_str());
+            break;
+        case kVec2f_GrSLType:
+            SkASSERT(kVec2f_GrSLType == varyingType);
+            vertexBuilder->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) {
+                vertexBuilder->vsCodeAppendf("\t%s = (%s * vec3(%s, 1)).xy;\n",
+                                             vsVaryingName, uniName, coords.c_str());
+            } else {
+                vertexBuilder->vsCodeAppendf("\t%s = %s * vec3(%s, 1);\n",
+                                             vsVaryingName, uniName, coords.c_str());
+            }
+            break;
+        }
+        default:
+            GrCrash("Unexpected uniform type.");
+    }
+    SkASSERT(NULL != transformedCoords);
+    transformedCoords->fName = fsVaryingName;
+    transformedCoords->fType = varyingType;
+    transformedCoords->fVSName = vsVaryingName;
+}
+
+void GrGLCoordTransform::setData(const GrGLUniformManager& uniformManager,
+                                 const GrDrawEffect& drawEffect,
+                                 int transformIdx) {
+    SkASSERT(fUni.isValid() != (kVoid_GrSLType == fUniType));
+    const GrCoordTransform& transform = (*drawEffect.effect())->coordTransform(transformIdx);
+    const SkMatrix& matrix = transform.getMatrix();
+    const SkMatrix& coordChangeMatrix = kLocal_GrCoordSet == transform.sourceCoords() ?
+                                            drawEffect.getCoordChangeMatrix() :
+                                            SkMatrix::I();
+    switch (fUniType) {
+        case kVoid_GrSLType:
+            SkASSERT(matrix.isIdentity());
+            SkASSERT(coordChangeMatrix.isIdentity());
+            SkASSERT(!transform.reverseY());
+            return;
+        case kVec2f_GrSLType: {
+            SkASSERT(SkMatrix::kTranslate_Mask == (matrix.getType() | coordChangeMatrix.getType()));
+            SkASSERT(!transform.reverseY());
+            SkScalar tx = matrix[SkMatrix::kMTransX] + (coordChangeMatrix)[SkMatrix::kMTransX];
+            SkScalar ty = matrix[SkMatrix::kMTransY] + (coordChangeMatrix)[SkMatrix::kMTransY];
+            if (fPrevMatrix.get(SkMatrix::kMTransX) != tx ||
+                fPrevMatrix.get(SkMatrix::kMTransY) != ty) {
+                uniformManager.set2f(fUni, tx, ty);
+                fPrevMatrix.set(SkMatrix::kMTransX, tx);
+                fPrevMatrix.set(SkMatrix::kMTransY, ty);
+            }
+            break;
+        }
+        case kMat33f_GrSLType: {
+            SkMatrix combined;
+            combined.setConcat(matrix, coordChangeMatrix);
+            if (transform.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 (!fPrevMatrix.cheapEqualTo(combined)) {
+                uniformManager.setSkMatrix(fUni, combined);
+                fPrevMatrix = combined;
+            }
+            break;
+        }
+        default:
+            GrCrash("Unexpected uniform type.");
+    }
+}
diff --git a/src/gpu/gl/GrGLCoordTransform.h b/src/gpu/gl/GrGLCoordTransform.h
new file mode 100644
index 0000000..cd2f38a
--- /dev/null
+++ b/src/gpu/gl/GrGLCoordTransform.h
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrGLCoordTransform_DEFINED
+#define GrGLCoordTransform_DEFINED
+
+#include "GrBackendEffectFactory.h"
+#include "GrCoordTransform.h"
+#include "GrGLUniformManager.h"
+
+class GrTexture;
+class GrGLShaderBuilder;
+
+/**
+ * This is a helper class used by the framework to implement a coordinate transform that operates on
+ * incoming coords in the vertex shader and writes them to a varying to be used in the fragment
+ * shader. Effects should not use this class directly, but instead call GrEffect::addCoordTransform.
+ * When the input coords are local coordinates this class accounts for the coord change matrix
+ * communicated via GrDrawEffect. The input coords may also be positions and in this case the coord
+ * change matrix is ignored. The GrGLCoordTransform may emit different code based on the type of
+ * matrix and thus must contribute to the effect's key.
+ *
+ * This class cannot be used to apply a matrix to coordinates that come in the form of custom vertex
+ * attributes.
+ */
+class GrGLCoordTransform {
+private:
+    // We specialize the generated code for each of these matrix types.
+    enum MatrixTypes {
+        kIdentity_MatrixType    = 0,
+        kTrans_MatrixType       = 1,
+        kNoPersp_MatrixType     = 2,
+        kGeneral_MatrixType     = 3,
+    };
+    // The key for is made up of a matrix type and a bit that indicates the source of the input
+    // coords.
+    enum {
+        kMatrixTypeKeyBits      = 2,
+        kMatrixTypeKeyMask      = (1 << kMatrixTypeKeyBits) - 1,
+        kPositionCoords_Flag    = (1 << kMatrixTypeKeyBits),
+        kKeyBitsPrivate         = kMatrixTypeKeyBits + 1,
+    };
+
+public:
+
+    typedef GrBackendEffectFactory::EffectKey EffectKey;
+
+    /**
+     * A GrGLCoordTransform key is kKeyBits long. The framework automatically generates and includes
+     * these in EffectKeys.
+     */
+    enum {
+        kKeyBits = kKeyBitsPrivate,
+        kKeyMask = (1 << kKeyBits) - 1,
+    };
+
+    GrGLCoordTransform() { fPrevMatrix = SkMatrix::InvalidMatrix(); }
+
+    /**
+     * Generates the key for the portion of the code emitted by this class's emitCode() function.
+     */
+    static EffectKey GenKey(const GrDrawEffect&, int transformIdx);
+
+    /**
+     * Stores the name and type of a transformed set of coordinates. This class is passed to
+     * GrGLEffect::emitCode.
+     */
+    class TransformedCoords {
+    public:
+        const char* c_str() const { return fName.c_str(); }
+        GrSLType type() const { return fType; }
+        const SkString& getName() const { return fName; }
+        // TODO: Remove the VS name when we have vertexless shaders, and gradients are reworked.
+        const SkString& getVSName() const { return fVSName; }
+
+    private:
+        friend class GrGLCoordTransform;
+
+        SkString fName;
+        GrSLType fType;
+        SkString fVSName;
+    };
+
+    /**
+     * Emits code to implement the matrix in the VS. A varying is added as an output of the VS and
+     * input to the FS. The varying may be either a vec2f or vec3f depending upon whether
+     * perspective interpolation is required or not. The names of the varying in the VS and FS as
+     * well as its type are written to the TransformedCoords* object. The suffix is an optional
+     * parameter that can be used to make all variables emitted by the object unique within a stage.
+     * It is only necessary if multiple GrGLCoordTransform objects are used by a single GrGLEffect.
+     */
+    void emitCode(GrGLShaderBuilder*, EffectKey, TransformedCoords*, int suffix = 0);
+
+    /**
+     * Call from a GrGLEffect's subclass to update the texture matrix. The matrix and reverseY value
+     * should match those used with GenKey.
+     */
+    void setData(const GrGLUniformManager&, const GrDrawEffect&, int transformIdx);
+
+    GrGLUniformManager::UniformHandle fUni;
+    GrSLType                          fUniType;
+    SkMatrix                          fPrevMatrix;
+};
+
+#endif
diff --git a/src/gpu/gl/GrGLEffect.cpp b/src/gpu/gl/GrGLEffect.cpp
index 28be7af..af351a4 100644
--- a/src/gpu/gl/GrGLEffect.cpp
+++ b/src/gpu/gl/GrGLEffect.cpp
@@ -7,6 +7,7 @@
 
 #include "GrGLSL.h"
 #include "GrGLEffect.h"
+#include "GrCoordTransform.h"
 #include "GrDrawEffect.h"
 
 GrGLEffect::GrGLEffect(const GrBackendEffectFactory& factory)
@@ -34,6 +35,18 @@
     return key;
 }
 
+GrGLEffect::EffectKey GrGLEffect::GenTransformKey(const GrDrawEffect& drawEffect) {
+    EffectKey key = 0;
+    int numTransforms = (*drawEffect.effect())->numTransforms();
+    for (int index = 0; index < numTransforms; ++index) {
+        EffectKey value = GrGLCoordTransform::GenKey(drawEffect, index);
+        value <<= index * GrGLCoordTransform::kKeyBits;
+        SkASSERT(0 == (value & key)); // keys for each transform ought not to overlap
+        key |= value;
+    }
+    return key;
+}
+
 GrGLEffect::EffectKey GrGLEffect::GenAttribKey(const GrDrawEffect& drawEffect) {
     EffectKey key = 0;
 
diff --git a/src/gpu/gl/GrGLEffect.h b/src/gpu/gl/GrGLEffect.h
index 5df2281..d3c49d1 100644
--- a/src/gpu/gl/GrGLEffect.h
+++ b/src/gpu/gl/GrGLEffect.h
@@ -18,8 +18,8 @@
 /** @file
     This file contains specializations for OpenGL of the shader stages declared in
     include/gpu/GrEffect.h. Objects of type GrGLEffect are responsible for emitting the
-    GLSL code that implements a GrEffect and for uploading uniforms at draw time. They also
-    must have a function:
+    GLSL code that implements a GrEffect and for uploading uniforms at draw time. If they don't
+    always emit the same GLSL code, they must have a function:
         static inline EffectKey GenKey(const GrDrawEffect&, const GrGLCaps&)
     that is used to implement a program cache. When two GrEffects produce the same key this means
     that their GrGLEffects would emit the same GLSL code.
@@ -45,6 +45,7 @@
         kEffectKeyBits = GrBackendEffectFactory::kEffectKeyBits,
     };
 
+    typedef GrGLShaderBuilder::TransformedCoordsArray TransformedCoordsArray;
     typedef GrGLShaderBuilder::TextureSamplerArray TextureSamplerArray;
 
     GrGLEffect(const GrBackendEffectFactory&);
@@ -76,6 +77,7 @@
                           EffectKey key,
                           const char* outputColor,
                           const char* inputColor,
+                          const TransformedCoordsArray& coords,
                           const TextureSamplerArray& samplers) = 0;
 
     /** A GrGLEffect instance can be reused with any GrEffect that produces the same stage
@@ -89,7 +91,10 @@
 
     const char* name() const { return fFactory.name(); }
 
+    static inline EffectKey GenKey(const GrDrawEffect&, const GrGLCaps&) { return 0; }
+
     static EffectKey GenTextureKey(const GrDrawEffect&, const GrGLCaps&);
+    static EffectKey GenTransformKey(const GrDrawEffect&);
     static EffectKey GenAttribKey(const GrDrawEffect& stage);
 
 protected:
diff --git a/src/gpu/gl/GrGLEffectMatrix.cpp b/src/gpu/gl/GrGLEffectMatrix.cpp
deleted file mode 100644
index 880df12..0000000
--- a/src/gpu/gl/GrGLEffectMatrix.cpp
+++ /dev/null
@@ -1,241 +0,0 @@
-/*
- * Copyright 2012 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "GrGLEffectMatrix.h"
-#include "GrDrawEffect.h"
-#include "GrTexture.h"
-
-GrGLEffect::EffectKey GrGLEffectMatrix::GenKey(const SkMatrix& effectMatrix,
-                                               const GrDrawEffect& drawEffect,
-                                               CoordsType coordsType,
-                                               const GrTexture* texture) {
-    EffectKey key = 0;
-    SkMatrix::TypeMask type0 = effectMatrix.getType();
-    SkMatrix::TypeMask type1;
-    if (GrEffect::kLocal_CoordsType == coordsType) {
-        type1 = drawEffect.getCoordChangeMatrix().getType();
-    } else {
-        if (drawEffect.programHasExplicitLocalCoords()) {
-            // We only make the key indicate that device coords are referenced when the local coords
-            // are not actually determined by positions.
-            key |= kPositionCoords_Flag;
-        }
-        type1 = SkMatrix::kIdentity_Mask;
-    }
-
-    int combinedTypes = type0 | type1;
-
-    bool reverseY = (NULL != texture) && kBottomLeft_GrSurfaceOrigin == texture->origin();
-
-    if (SkMatrix::kPerspective_Mask & combinedTypes) {
-        key |= kGeneral_MatrixType;
-    } else if (((SkMatrix::kAffine_Mask | SkMatrix::kScale_Mask) & combinedTypes) || reverseY) {
-        key |= kNoPersp_MatrixType;
-    } else if (SkMatrix::kTranslate_Mask & combinedTypes) {
-        key |= kTrans_MatrixType;
-    } else {
-        key |= kIdentity_MatrixType;
-    }
-    return key;
-}
-
-GrSLType GrGLEffectMatrix::emitCode(GrGLShaderBuilder* builder,
-                                    EffectKey key,
-                                    SkString* fsCoordName,
-                                    SkString* vsCoordName,
-                                    const char* suffix) {
-    GrGLShaderBuilder::VertexBuilder* vertexBuilder = builder->getVertexBuilder();
-    SkASSERT(NULL != vertexBuilder);
-
-    GrSLType varyingType = kVoid_GrSLType;
-    const char* uniName;
-    key &= kKeyMask;
-    switch (key & kMatrixTypeKeyMask) {
-        case kIdentity_MatrixType:
-            fUniType = kVoid_GrSLType;
-            uniName = NULL;
-            varyingType = kVec2f_GrSLType;
-            break;
-        case kTrans_MatrixType:
-            fUniType = kVec2f_GrSLType;
-            uniName = "StageTranslate";
-            varyingType = kVec2f_GrSLType;
-            break;
-        case kNoPersp_MatrixType:
-            fUniType = kMat33f_GrSLType;
-            uniName = "StageMatrix";
-            varyingType = kVec2f_GrSLType;
-            break;
-        case kGeneral_MatrixType:
-            fUniType = kMat33f_GrSLType;
-            uniName = "StageMatrix";
-            varyingType = kVec3f_GrSLType;
-            break;
-        default:
-            GrCrash("Unexpected key.");
-    }
-    SkString suffixedUniName;
-    if (kVoid_GrSLType != fUniType) {
-        if (NULL != suffix) {
-            suffixedUniName.append(uniName);
-            suffixedUniName.append(suffix);
-            uniName = suffixedUniName.c_str();
-        }
-        fUni = builder->addUniform(GrGLShaderBuilder::kVertex_Visibility,
-                                   fUniType,
-                                   uniName,
-                                   &uniName);
-    }
-
-    const char* varyingName = "MatrixCoord";
-    SkString suffixedVaryingName;
-    if (NULL != suffix) {
-        suffixedVaryingName.append(varyingName);
-        suffixedVaryingName.append(suffix);
-        varyingName = suffixedVaryingName.c_str();
-    }
-    const char* vsVaryingName;
-    const char* fsVaryingName;
-    vertexBuilder->addVarying(varyingType, varyingName, &vsVaryingName, &fsVaryingName);
-
-    const GrGLShaderVar* coords;
-    switch (fCoordsType) {
-        case GrEffect::kLocal_CoordsType:
-            SkASSERT(!(kPositionCoords_Flag & key));
-            coords = &vertexBuilder->localCoordsAttribute();
-            break;
-        case GrEffect::kPosition_CoordsType:
-            SkASSERT((kPositionCoords_Flag & key) || !vertexBuilder->hasExplicitLocalCoords());
-            coords = &vertexBuilder->positionAttribute();
-            break;
-        default:
-            coords = NULL; // prevents warning
-            GrCrash("Unexpected coords type.");
-    }
-    // varying = matrix * coords (logically)
-    switch (fUniType) {
-        case kVoid_GrSLType:
-            SkASSERT(kVec2f_GrSLType == varyingType);
-            vertexBuilder->vsCodeAppendf("\t%s = %s;\n", vsVaryingName, coords->c_str());
-            break;
-        case kVec2f_GrSLType:
-            SkASSERT(kVec2f_GrSLType == varyingType);
-            vertexBuilder->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) {
-                vertexBuilder->vsCodeAppendf("\t%s = (%s * vec3(%s, 1)).xy;\n",
-                                             vsVaryingName, uniName, coords->c_str());
-            } else {
-                vertexBuilder->vsCodeAppendf("\t%s = %s * vec3(%s, 1);\n",
-                                             vsVaryingName, uniName, coords->c_str());
-            }
-            break;
-        }
-        default:
-            GrCrash("Unexpected uniform type.");
-    }
-    if (NULL != vsCoordName) {
-        *vsCoordName = vsVaryingName;
-    }
-    if (NULL != fsCoordName) {
-        *fsCoordName = fsVaryingName;
-    }
-    return varyingType;
-}
-
-/**
-    * This is similar to emitCode except that it performs perspective division in the FS if the
-    * texture coordinates have a w coordinate. The fsCoordName always refers to a vec2f.
-    */
-void GrGLEffectMatrix::emitCodeMakeFSCoords2D(GrGLShaderBuilder* builder,
-                                              EffectKey key,
-                                              SkString* fsCoordName,
-                                              SkString* vsVaryingName,
-                                              GrSLType* vsVaryingType,
-                                              const char* suffix) {
-    SkString fsVaryingName;
-
-    GrSLType varyingType = this->emitCode(builder,
-                                          key,
-                                          &fsVaryingName,
-                                          vsVaryingName,
-                                          suffix);
-    if (kVec3f_GrSLType == varyingType) {
-
-        const char* coordName = "coords2D";
-        SkString suffixedCoordName;
-        if (NULL != suffix) {
-            suffixedCoordName.append(coordName);
-            suffixedCoordName.append(suffix);
-            coordName = suffixedCoordName.c_str();
-        }
-        builder->fsCodeAppendf("\tvec2 %s = %s.xy / %s.z;",
-                               coordName, fsVaryingName.c_str(), fsVaryingName.c_str());
-        if (NULL != fsCoordName) {
-            *fsCoordName = coordName;
-        }
-    } else if(NULL != fsCoordName) {
-        *fsCoordName = fsVaryingName;
-    }
-    if (NULL != vsVaryingType) {
-        *vsVaryingType = varyingType;
-    }
-}
-
-void GrGLEffectMatrix::setData(const GrGLUniformManager& uniformManager,
-                               const SkMatrix& matrix,
-                               const GrDrawEffect& drawEffect,
-                               const GrTexture* texture) {
-    SkASSERT(fUni.isValid() != (kVoid_GrSLType == fUniType));
-    const SkMatrix& coordChangeMatrix = GrEffect::kLocal_CoordsType == fCoordsType ?
-                                            drawEffect.getCoordChangeMatrix() :
-                                            SkMatrix::I();
-    switch (fUniType) {
-        case kVoid_GrSLType:
-            SkASSERT(matrix.isIdentity());
-            SkASSERT(coordChangeMatrix.isIdentity());
-            SkASSERT(NULL == texture || kTopLeft_GrSurfaceOrigin == texture->origin());
-            return;
-        case kVec2f_GrSLType: {
-            SkASSERT(SkMatrix::kTranslate_Mask == (matrix.getType() | coordChangeMatrix.getType()));
-            SkASSERT(NULL == texture || kTopLeft_GrSurfaceOrigin == texture->origin());
-            SkScalar tx = matrix[SkMatrix::kMTransX] + (coordChangeMatrix)[SkMatrix::kMTransX];
-            SkScalar ty = matrix[SkMatrix::kMTransY] + (coordChangeMatrix)[SkMatrix::kMTransY];
-            if (fPrevMatrix.get(SkMatrix::kMTransX) != tx ||
-                fPrevMatrix.get(SkMatrix::kMTransY) != ty) {
-                uniformManager.set2f(fUni, tx, ty);
-                fPrevMatrix.set(SkMatrix::kMTransX, tx);
-                fPrevMatrix.set(SkMatrix::kMTransY, ty);
-            }
-            break;
-        }
-        case kMat33f_GrSLType: {
-            SkMatrix combined;
-            combined.setConcat(matrix, coordChangeMatrix);
-            if (NULL != texture && kBottomLeft_GrSurfaceOrigin == texture->origin()) {
-                // 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 (!fPrevMatrix.cheapEqualTo(combined)) {
-                uniformManager.setSkMatrix(fUni, combined);
-                fPrevMatrix = combined;
-            }
-            break;
-        }
-        default:
-            GrCrash("Unexpected uniform type.");
-    }
-}
diff --git a/src/gpu/gl/GrGLEffectMatrix.h b/src/gpu/gl/GrGLEffectMatrix.h
deleted file mode 100644
index c5ac5f0..0000000
--- a/src/gpu/gl/GrGLEffectMatrix.h
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * Copyright 2012 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef GrGLEffectMatrix_DEFINED
-#define GrGLEffectMatrix_DEFINED
-
-#include "GrGLEffect.h"
-#include "SkMatrix.h"
-
-class GrTexture;
-
-/**
- * This is a helper to implement a matrix in a GrGLEffect that operates on incoming coords in the
- * vertex shader and writes them to an attribute to be used in the fragment shader. When the input
- * coords in the vertex shader are local coordinates this class accounts for the coord change matrix
- * communicated via GrDrawEffect. The input coords may also be positions and in this case the coord
- * change matrix is ignored. The GrGLEffectMatrix will emit different code based on the type of
- * matrix and thus must contribute to the effect's key.
- *
- * This class cannot be used to apply a matrix to coordinates that come in the form of custom vertex
- * attributes.
- */
-class GrGLEffectMatrix {
-private:
-    // We specialize the generated code for each of these matrix types.
-    enum MatrixTypes {
-        kIdentity_MatrixType    = 0,
-        kTrans_MatrixType       = 1,
-        kNoPersp_MatrixType     = 2,
-        kGeneral_MatrixType     = 3,
-    };
-    // The key for is made up of a matrix type and a bit that indicates the source of the input
-    // coords.
-    enum {
-        kMatrixTypeKeyBits      = 2,
-        kMatrixTypeKeyMask      = (1 << kMatrixTypeKeyBits) - 1,
-        kPositionCoords_Flag    = (1 << kMatrixTypeKeyBits),
-        kKeyBitsPrivate         = kMatrixTypeKeyBits + 1,
-    };
-
-public:
-
-    typedef GrEffect::CoordsType CoordsType;
-
-    typedef GrGLEffect::EffectKey EffectKey;
-
-    /**
-     * The matrix uses kKeyBits of the effect's EffectKey. A GrGLEffect may place these bits at an
-     * arbitrary shift in its final key. However, when GrGLEffectMatrix::emitCode*() code is called
-     * the relevant bits must be in the lower kKeyBits of the key parameter.
-     */
-    enum {
-        kKeyBits = kKeyBitsPrivate,
-        kKeyMask = (1 << kKeyBits) - 1,
-    };
-
-    GrGLEffectMatrix(CoordsType coordsType)
-        : fCoordsType(coordsType) {
-        fPrevMatrix = SkMatrix::InvalidMatrix();
-    }
-
-    /**
-     * Generates the key for the portion of the code emitted by this class's emitCode() function.
-     * Pass a texture to make GrGLEffectMatrix automatically adjust for the texture's origin. Pass
-     * NULL when not using the EffectMatrix for a texture lookup, or if the GrGLEffect subclass
-     * wants to handle origin adjustments in some other manner. The coords type param must match the
-     * param that would be used to initialize GrGLEffectMatrix for the generating GrEffect.
-     */
-    static EffectKey GenKey(const SkMatrix& effectMatrix,
-                            const GrDrawEffect&,
-                            CoordsType,
-                            const GrTexture*);
-
-    /**
-     * Emits code to implement the matrix in the VS. A varying is added as an output of the VS and
-     * input to the FS. The varying may be either a vec2f or vec3f depending upon whether
-     * perspective interpolation is required or not. The names of the varying in the VS and FS are
-     * are returned as output parameters and the type of the varying is the return value. The suffix
-     * is an optional parameter that can be used to make all variables emitted by the object
-     * unique within a stage. It is only necessary if multiple GrGLEffectMatrix objects are used by
-     * a single GrGLEffect.
-     */
-    GrSLType emitCode(GrGLShaderBuilder*,
-                      EffectKey,
-                      SkString* fsCoordName, /* optional */
-                      SkString* vsCoordName = NULL,
-                      const char* suffix = NULL);
-
-    /**
-     * This is similar to emitCode except that it performs perspective division in the FS if the
-     * texture coordinates have a w coordinate. The fsCoordName always refers to a vec2f.
-     */
-    void emitCodeMakeFSCoords2D(GrGLShaderBuilder*,
-                                EffectKey,
-                                SkString* fsCoordName, /* optional */
-                                SkString* vsVaryingName = NULL,
-                                GrSLType* vsVaryingType = NULL,
-                                const char* suffix = NULL);
-    /**
-     * Call from a GrGLEffect's subclass to update the texture matrix. The effectMatrix and texture
-     * params should match those used with GenKey.
-     */
-    void setData(const GrGLUniformManager& uniformManager,
-                 const SkMatrix& effectMatrix,
-                 const GrDrawEffect& drawEffect,
-                 const GrTexture*);
-
-    GrGLUniformManager::UniformHandle fUni;
-    GrSLType                          fUniType;
-    SkMatrix                          fPrevMatrix;
-    CoordsType                        fCoordsType;
-};
-
-#endif
diff --git a/src/gpu/gl/GrGLProgram.cpp b/src/gpu/gl/GrGLProgram.cpp
index 2f8e807..89f5f58 100644
--- a/src/gpu/gl/GrGLProgram.cpp
+++ b/src/gpu/gl/GrGLProgram.cpp
@@ -9,6 +9,7 @@
 
 #include "GrAllocator.h"
 #include "GrEffect.h"
+#include "GrCoordTransform.h"
 #include "GrDrawEffect.h"
 #include "GrGLEffect.h"
 #include "GrGpuGL.h"
@@ -235,13 +236,16 @@
     need_blend_inputs(filterColorCoeff, colorCoeff, &needFilterColor, &needColor);
 
     // used in order for builder to return the per-stage uniform handles.
+    typedef SkTArray<GrGLCoordTransform, false>* CoordTransformArrayPtr;
     typedef SkTArray<GrGLUniformManager::UniformHandle, true>* UniHandleArrayPtr;
     int maxColorOrCovEffectCnt = GrMax(fDesc.numColorEffects(), fDesc.numCoverageEffects());
+    SkAutoTArray<CoordTransformArrayPtr> effectCoordTransformArrays(maxColorOrCovEffectCnt);
     SkAutoTArray<UniHandleArrayPtr> effectUniformArrays(maxColorOrCovEffectCnt);
     SkAutoTArray<GrGLEffect*> glEffects(maxColorOrCovEffectCnt);
 
     if (needColor) {
         for (int e = 0; e < fDesc.numColorEffects(); ++e) {
+            effectCoordTransformArrays[e] = &fColorEffects[e].fCoordTransforms;
             effectUniformArrays[e] = &fColorEffects[e].fSamplerUnis;
         }
 
@@ -250,6 +254,7 @@
                             fDesc.numColorEffects(),
                             &inColor,
                             &knownColorValue,
+                            effectCoordTransformArrays.get(),
                             effectUniformArrays.get(),
                             glEffects.get());
 
@@ -286,6 +291,7 @@
     GrSLConstantVec knownCoverageValue = builder.getKnownCoverageValue();
 
     for (int e = 0; e < fDesc.numCoverageEffects(); ++e) {
+        effectCoordTransformArrays[e] = &fCoverageEffects[e].fCoordTransforms;
         effectUniformArrays[e] = &fCoverageEffects[e].fSamplerUnis;
     }
 
@@ -294,6 +300,7 @@
                         fDesc.numCoverageEffects(),
                         &inCoverage,
                         &knownCoverageValue,
+                        effectCoordTransformArrays.get(),
                         effectUniformArrays.get(),
                         glEffects.get());
     for (int e = 0; e < fDesc.numCoverageEffects(); ++e) {
@@ -439,13 +446,20 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 void GrGLProgram::setEffectData(const GrEffectStage& stage,
-                                const EffectAndSamplers& effect) {
+                                EffectAndSamplers& effect) {
 
     // Let the GrGLEffect set its data.
     bool explicitLocalCoords = -1 != fDesc.getHeader().fLocalCoordAttributeIndex;
     GrDrawEffect drawEffect(stage, explicitLocalCoords);
     effect.fGLEffect->setData(fUniformManager, drawEffect);
 
+    // Set the effect's coord transform matrices.
+    int numTransforms = effect.fCoordTransforms.count();
+    SkASSERT((*stage.getEffect())->numTransforms() == numTransforms);
+    for (int c = 0; c < numTransforms; ++c) {
+        effect.fCoordTransforms[c].setData(fUniformManager, drawEffect, c);
+    }
+
     // Bind the texures for the effect.
     int numSamplers = effect.fSamplerUnis.count();
     SkASSERT((*stage.getEffect())->numTextures() == numSamplers);
diff --git a/src/gpu/gl/GrGLProgram.h b/src/gpu/gl/GrGLProgram.h
index 0304acf..4412309 100644
--- a/src/gpu/gl/GrGLProgram.h
+++ b/src/gpu/gl/GrGLProgram.h
@@ -11,6 +11,7 @@
 
 #include "GrDrawState.h"
 #include "GrGLContext.h"
+#include "GrGLCoordTransform.h"
 #include "GrGLProgramDesc.h"
 #include "GrGLShaderBuilder.h"
 #include "GrGLSL.h"
@@ -148,15 +149,17 @@
         UniformHandle       fDstCopySamplerUni;
     };
 
+    typedef SkSTArray<4, GrGLCoordTransform, false> CoordTransformSArray;
     typedef SkSTArray<4, UniformHandle, true> SamplerUniSArray;
     typedef SkSTArray<4, int, true> TextureUnitSArray;
 
     struct EffectAndSamplers {
         EffectAndSamplers() : fGLEffect(NULL) {}
         ~EffectAndSamplers() { delete fGLEffect; }
-        GrGLEffect*         fGLEffect;
-        SamplerUniSArray    fSamplerUnis;  // sampler uni handles for effect's GrTextureAccess
-        TextureUnitSArray   fTextureUnits; // texture unit used for each entry of fSamplerUnis
+        GrGLEffect*          fGLEffect;
+        CoordTransformSArray fCoordTransforms;
+        SamplerUniSArray     fSamplerUnis;  // sampler uni handles for effect's GrTextureAccess
+        TextureUnitSArray    fTextureUnits; // texture unit used for each entry of fSamplerUnis
     };
 
     GrGLProgram(GrGpuGL* gpu,
@@ -177,7 +180,7 @@
     void initEffectSamplerUniforms(EffectAndSamplers* effect, int* texUnitIdx);
 
     // Helper for setData().
-    void setEffectData(const GrEffectStage& stage, const EffectAndSamplers& effect);
+    void setEffectData(const GrEffectStage& stage, EffectAndSamplers& effect);
 
     // Helper for setData(). Makes GL calls to specify the initial color when there is not
     // per-vertex colors.
diff --git a/src/gpu/gl/GrGLShaderBuilder.cpp b/src/gpu/gl/GrGLShaderBuilder.cpp
index f4ffb4d..313ad86 100644
--- a/src/gpu/gl/GrGLShaderBuilder.cpp
+++ b/src/gpu/gl/GrGLShaderBuilder.cpp
@@ -8,6 +8,7 @@
 #include "gl/GrGLShaderBuilder.h"
 #include "gl/GrGLProgram.h"
 #include "gl/GrGLUniformHandle.h"
+#include "GrCoordTransform.h"
 #include "GrDrawEffect.h"
 #include "GrGpuGL.h"
 #include "GrTexture.h"
@@ -347,9 +348,8 @@
     GrGLSLModulatef<4>(&fFSCode, modulation, lookup.c_str());
 }
 
-GrBackendEffectFactory::EffectKey GrGLShaderBuilder::KeyForTextureAccess(
-                                                            const GrTextureAccess& access,
-                                                            const GrGLCaps& caps) {
+GrGLShaderBuilder::EffectKey GrGLShaderBuilder::KeyForTextureAccess(const GrTextureAccess& access,
+                                                                    const GrGLCaps& caps) {
     uint32_t configComponentMask = GrPixelConfigComponentMask(access.getTexture()->config());
     if (swizzle_requires_alpha_remapping(caps, configComponentMask, access.swizzleMask())) {
         return 1;
@@ -440,6 +440,21 @@
     return h;
 }
 
+SkString GrGLShaderBuilder::ensureFSCoords2D(const TransformedCoordsArray& coords, int index) {
+    if (kVec3f_GrSLType != coords[index].type()) {
+        SkASSERT(kVec2f_GrSLType == coords[index].type());
+        return coords[index].getName();
+    }
+
+    SkString coords2D("coords2D");
+    if (0 != index) {
+        coords2D.appendf("_%i", index);
+    }
+    this->fsCodeAppendf("\tvec2 %s = %s.xy / %s.z;",
+                        coords2D.c_str(), coords[index].c_str(), coords[index].c_str());
+    return coords2D;
+}
+
 const char* GrGLShaderBuilder::fragmentPosition() {
     if (fCodeStage.inStageCode()) {
         const GrEffectRef& effect = *fCodeStage.effectStage()->getEffect();
@@ -559,10 +574,11 @@
 
 void GrGLShaderBuilder::emitEffects(
                         const GrEffectStage* effectStages[],
-                        const GrBackendEffectFactory::EffectKey effectKeys[],
+                        const EffectKey effectKeys[],
                         int effectCnt,
                         SkString* fsInOutColor,
                         GrSLConstantVec* fsInOutColorKnownValue,
+                        SkTArray<GrGLCoordTransform, false>* effectCoordTransformArrays[],
                         SkTArray<GrGLUniformManager::UniformHandle, true>* effectSamplerHandles[],
                         GrGLEffect* glEffects[]) {
     bool effectEmitted = false;
@@ -577,6 +593,17 @@
 
         CodeStage::AutoStageRestore csar(&fCodeStage, &stage);
 
+        int numTransforms = effect->numTransforms();
+        SkSTArray<8, GrGLCoordTransform::TransformedCoords> transformedCoords;
+        transformedCoords.push_back_n(numTransforms);
+        EffectKey transformKey = GrBackendEffectFactory::GetTransformKey(effectKeys[e]);
+        for (int c = 0; c < numTransforms; ++c) {
+            GrGLCoordTransform& ct = effectCoordTransformArrays[e]->push_back();
+            EffectKey key = (transformKey >> (c * GrGLCoordTransform::kKeyBits)) &
+                            (GrGLCoordTransform::kKeyMask);
+            ct.emitCode(this, key, &transformedCoords[c], c);
+        }
+
         int numTextures = effect->numTextures();
         SkSTArray<8, GrGLShaderBuilder::TextureSampler> textureSamplers;
         textureSamplers.push_back_n(numTextures);
@@ -584,6 +611,7 @@
             textureSamplers[t].init(this, &effect->textureAccess(t), t);
             effectSamplerHandles[e]->push_back(textureSamplers[t].fSamplerUniform);
         }
+
         GrDrawEffect drawEffect(stage, NULL != fVertexBuilder.get()
                                        && fVertexBuilder->hasExplicitLocalCoords());
 
@@ -625,6 +653,7 @@
                                effectKeys[e],
                                outColor.c_str(),
                                inColor.isEmpty() ? NULL : inColor.c_str(),
+                               transformedCoords,
                                textureSamplers);
 
         if (NULL != fVertexBuilder.get()) {
diff --git a/src/gpu/gl/GrGLShaderBuilder.h b/src/gpu/gl/GrGLShaderBuilder.h
index d73a731..a70a0b0 100644
--- a/src/gpu/gl/GrGLShaderBuilder.h
+++ b/src/gpu/gl/GrGLShaderBuilder.h
@@ -13,6 +13,7 @@
 #include "GrColor.h"
 #include "GrEffect.h"
 #include "SkTypes.h"
+#include "gl/GrGLCoordTransform.h"
 #include "gl/GrGLSL.h"
 #include "gl/GrGLUniformManager.h"
 
@@ -96,8 +97,10 @@
         friend class GrGLShaderBuilder; // to call init().
     };
 
+    typedef SkTArray<GrGLCoordTransform::TransformedCoords> TransformedCoordsArray;
     typedef SkTArray<TextureSampler> TextureSamplerArray;
     typedef GrTAllocator<GrGLShaderVar> VarArray;
+    typedef GrBackendEffectFactory::EffectKey EffectKey;
 
     enum ShaderVisibility {
         kVertex_Visibility   = 0x1,
@@ -175,8 +178,7 @@
     /** Generates a EffectKey for the shader code based on the texture access parameters and the
         capabilities of the GL context.  This is useful for keying the shader programs that may
         have multiple representations, based on the type/format of textures used. */
-    static GrBackendEffectFactory::EffectKey KeyForTextureAccess(const GrTextureAccess&,
-                                                                 const GrGLCaps&);
+    static EffectKey KeyForTextureAccess(const GrTextureAccess&, const GrGLCaps&);
 
     typedef uint8_t DstReadKey;
     typedef uint8_t FragPosKey;
@@ -225,6 +227,13 @@
         return this->getUniformVariable(u).c_str();
     }
 
+    /**
+     * This returns a variable name to access the 2D, perspective correct version of the coords in
+     * the fragment shader. If the coordinates at index are 3-dimensional, it immediately emits a
+     * perspective divide into the fragment shader (xy / z) to convert them to 2D.
+     */
+    SkString ensureFSCoords2D(const TransformedCoordsArray&, int index);
+
     /** Returns a variable name that represents the position of the fragment in the FS. The position
         is in device space (e.g. 0,0 is the top left and pixel centers are at half-integers). */
     const char* fragmentPosition();
@@ -255,10 +264,11 @@
      * effectStages.
      */
     void emitEffects(const GrEffectStage* effectStages[],
-                     const GrBackendEffectFactory::EffectKey effectKeys[],
+                     const EffectKey effectKeys[],
                      int effectCnt,
                      SkString*  inOutFSColor,
                      GrSLConstantVec* fsInOutColorKnownValue,
+                     SkTArray<GrGLCoordTransform, false>* effectCoordTransformArrays[],
                      SkTArray<GrGLUniformManager::UniformHandle, true>* effectSamplerHandles[],
                      GrGLEffect* glEffects[]);