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/src@11569 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/core/SkXfermode.cpp b/core/SkXfermode.cpp
index ac5cee4..e50e4f5 100644
--- a/core/SkXfermode.cpp
+++ b/core/SkXfermode.cpp
@@ -895,10 +895,10 @@
 #if SK_SUPPORT_GPU
 
 #include "GrEffect.h"
+#include "GrCoordTransform.h"
 #include "GrEffectUnitTest.h"
 #include "GrTBackendEffectFactory.h"
 #include "gl/GrGLEffect.h"
-#include "gl/GrGLEffectMatrix.h"
 
 /**
  * GrEffect that implements the all the separable xfer modes that cannot be expressed as Coeffs.
@@ -935,24 +935,22 @@
     class GLEffect : public GrGLEffect {
     public:
         GLEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&)
-            : GrGLEffect(factory )
-            , fBackgroundEffectMatrix(kCoordsType) {
+            : GrGLEffect(factory) {
         }
         virtual void emitCode(GrGLShaderBuilder* builder,
                               const GrDrawEffect& drawEffect,
                               EffectKey key,
                               const char* outputColor,
                               const char* inputColor,
+                              const TransformedCoordsArray& coords,
                               const TextureSamplerArray& samplers) SK_OVERRIDE {
             SkXfermode::Mode mode = drawEffect.castEffect<XferEffect>().mode();
             const GrTexture* backgroundTex = drawEffect.castEffect<XferEffect>().backgroundAccess().getTexture();
             const char* dstColor;
             if (backgroundTex) {
-                SkString bgCoords;
-                GrSLType bgCoordsType = fBackgroundEffectMatrix.emitCode(builder, key, &bgCoords, NULL, "BG");
                 dstColor = "bgColor";
                 builder->fsCodeAppendf("\t\tvec4 %s = ", dstColor);
-                builder->fsAppendTextureLookup(samplers[0], bgCoords.c_str(), bgCoordsType);
+                builder->fsAppendTextureLookup(samplers[0], coords[0].c_str(), coords[0].type());
                 builder->fsCodeAppendf(";\n");
             } else {
                 dstColor = builder->dstColor();
@@ -1088,28 +1086,7 @@
         }
 
         static inline EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) {
-            const XferEffect& xfer = drawEffect.castEffect<XferEffect>();
-            GrTexture* bgTex = xfer.backgroundAccess().getTexture();
-            EffectKey bgKey = 0;
-            if (bgTex) {
-                bgKey = GrGLEffectMatrix::GenKey(GrEffect::MakeDivByTextureWHMatrix(bgTex),
-                                                 drawEffect,
-                                                 GLEffect::kCoordsType,
-                                                 bgTex);
-            }
-            EffectKey modeKey = xfer.mode() << GrGLEffectMatrix::kKeyBits;
-            return modeKey | bgKey;
-        }
-
-        virtual void setData(const GrGLUniformManager& uman, const GrDrawEffect& drawEffect) SK_OVERRIDE {
-            const XferEffect& xfer = drawEffect.castEffect<XferEffect>();
-            GrTexture* bgTex = xfer.backgroundAccess().getTexture();
-            if (bgTex) {
-                fBackgroundEffectMatrix.setData(uman,
-                                                GrEffect::MakeDivByTextureWHMatrix(bgTex),
-                                                drawEffect,
-                                                bgTex);
-            }
+            return drawEffect.castEffect<XferEffect>().mode();
         }
 
     private:
@@ -1329,8 +1306,6 @@
 
         }
 
-        static const GrEffect::CoordsType kCoordsType = GrEffect::kLocal_CoordsType;
-        GrGLEffectMatrix   fBackgroundEffectMatrix;
         typedef GrGLEffect INHERITED;
     };
 
@@ -1340,6 +1315,8 @@
     XferEffect(SkXfermode::Mode mode, GrTexture* background)
         : fMode(mode) {
         if (background) {
+            fBackgroundTransform.reset(kLocal_GrCoordSet, background);
+            this->addCoordTransform(&fBackgroundTransform);
             fBackgroundAccess.reset(background);
             this->addTextureAccess(&fBackgroundAccess);
         } else {
@@ -1353,6 +1330,7 @@
     }
 
     SkXfermode::Mode fMode;
+    GrCoordTransform fBackgroundTransform;
     GrTextureAccess  fBackgroundAccess;
 
     typedef GrEffect INHERITED;
diff --git a/effects/SkArithmeticMode.cpp b/effects/SkArithmeticMode.cpp
index fd3a683..d746ecb 100644
--- a/effects/SkArithmeticMode.cpp
+++ b/effects/SkArithmeticMode.cpp
@@ -12,8 +12,8 @@
 #include "SkUnPreMultiply.h"
 #if SK_SUPPORT_GPU
 #include "GrContext.h"
+#include "GrCoordTransform.h"
 #include "gl/GrGLEffect.h"
-#include "gl/GrGLEffectMatrix.h"
 #include "GrTBackendEffectFactory.h"
 #include "SkImageFilterUtils.h"
 #endif
@@ -246,15 +246,12 @@
                           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:
-    static const GrEffect::CoordsType kCoordsType = GrEffect::kLocal_CoordsType;
-    GrGLEffectMatrix fBackgroundEffectMatrix;
     GrGLUniformManager::UniformHandle fKUni;
 
     typedef GrGLEffect INHERITED;
@@ -289,6 +286,7 @@
 
     GrArithmeticEffect(float k1, float k2, float k3, float k4, GrTexture* background);
     float                       fK1, fK2, fK3, fK4;
+    GrCoordTransform            fBackgroundTransform;
     GrTextureAccess             fBackgroundAccess;
 
     GR_DECLARE_EFFECT_TEST;
@@ -302,6 +300,8 @@
                                        GrTexture* background)
   : fK1(k1), fK2(k2), fK3(k3), fK4(k4) {
     if (background) {
+        fBackgroundTransform.reset(kLocal_GrCoordSet, background);
+        this->addCoordTransform(&fBackgroundTransform);
         fBackgroundAccess.reset(background);
         this->addTextureAccess(&fBackgroundAccess);
     } else {
@@ -334,8 +334,7 @@
 
 GrGLArithmeticEffect::GrGLArithmeticEffect(const GrBackendEffectFactory& factory,
                                            const GrDrawEffect& drawEffect)
-   : INHERITED(factory)
-   , fBackgroundEffectMatrix(kCoordsType) {
+   : INHERITED(factory) {
 }
 
 GrGLArithmeticEffect::~GrGLArithmeticEffect() {
@@ -346,15 +345,14 @@
                                     EffectKey key,
                                     const char* outputColor,
                                     const char* inputColor,
+                                    const TransformedCoordsArray& coords,
                                     const TextureSamplerArray& samplers) {
 
     GrTexture* backgroundTex = drawEffect.castEffect<GrArithmeticEffect>().backgroundTexture();
     const char* dstColor;
     if (backgroundTex) {
-        SkString bgCoords;
-        GrSLType bgCoordsType = fBackgroundEffectMatrix.emitCode(builder, key, &bgCoords, NULL, "BG");
         builder->fsCodeAppend("\t\tvec4 bgColor = ");
-        builder->fsAppendTextureLookup(samplers[0], bgCoords.c_str(), bgCoordsType);
+        builder->fsAppendTextureLookup(samplers[0], coords[0].c_str(), coords[0].type());
         builder->fsCodeAppendf(";\n");
         dstColor = "bgColor";
     } else {
@@ -393,26 +391,6 @@
 void GrGLArithmeticEffect::setData(const GrGLUniformManager& uman, const GrDrawEffect& drawEffect) {
     const GrArithmeticEffect& arith = drawEffect.castEffect<GrArithmeticEffect>();
     uman.set4f(fKUni, arith.k1(), arith.k2(), arith.k3(), arith.k4());
-    GrTexture* bgTex = arith.backgroundTexture();
-    if (bgTex) {
-        fBackgroundEffectMatrix.setData(uman,
-                                        GrEffect::MakeDivByTextureWHMatrix(bgTex),
-                                        drawEffect,
-                                        bgTex);
-    }
-}
-
-GrGLEffect::EffectKey GrGLArithmeticEffect::GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) {
-    const GrArithmeticEffect& effect = drawEffect.castEffect<GrArithmeticEffect>();
-    GrTexture* bgTex = effect.backgroundTexture();
-    EffectKey bgKey = 0;
-    if (bgTex) {
-        bgKey = GrGLEffectMatrix::GenKey(GrEffect::MakeDivByTextureWHMatrix(bgTex),
-                                         drawEffect,
-                                         GrGLArithmeticEffect::kCoordsType,
-                                         bgTex);
-    }
-    return bgKey;
 }
 
 GrEffectRef* GrArithmeticEffect::TestCreate(SkRandom* rand,
diff --git a/effects/SkBitmapAlphaThresholdShader.cpp b/effects/SkBitmapAlphaThresholdShader.cpp
index 84cdbf2..c8db3a5 100644
--- a/effects/SkBitmapAlphaThresholdShader.cpp
+++ b/effects/SkBitmapAlphaThresholdShader.cpp
@@ -62,9 +62,9 @@
 
 #if SK_SUPPORT_GPU
 #include "GrContext.h"
+#include "GrCoordTransform.h"
 #include "GrEffect.h"
 #include "gl/GrGLEffect.h"
-#include "gl/GrGLEffectMatrix.h"
 #include "GrTBackendEffectFactory.h"
 #include "GrTextureAccess.h"
 
@@ -109,36 +109,24 @@
         GLEffect(const GrBackendEffectFactory& factory,
                     const GrDrawEffect& e)
         : GrGLEffect(factory)
-        , fBmpMatrix(GrEffect::kLocal_CoordsType)
-        , fMaskMatrix(GrEffect::kLocal_CoordsType)
         , fPrevThreshold(-SK_Scalar1) {
         }
 
         virtual void emitCode(GrGLShaderBuilder* builder,
-                                const GrDrawEffect& drawEffect,
-                                EffectKey key,
-                                const char* outputColor,
-                                const char* inputColor,
-                                const TextureSamplerArray& samplers) SK_OVERRIDE {
-            SkString bmpCoord;
-            SkString maskCoord;
-
-            GrSLType bmpCoordType = fBmpMatrix.emitCode(builder, key, &bmpCoord, NULL, "Bmp");
-            EffectKey maskMatrixKey = key >> GrGLEffectMatrix::kKeyBits;
-            GrSLType maskCoordType = fMaskMatrix.emitCode(builder,
-                                                          maskMatrixKey,
-                                                          &maskCoord,
-                                                          NULL,
-                                                          "Mask");
-
+                              const GrDrawEffect& drawEffect,
+                              EffectKey key,
+                              const char* outputColor,
+                              const char* inputColor,
+                              const TransformedCoordsArray& coords,
+                              const TextureSamplerArray& samplers) SK_OVERRIDE {
             // put bitmap color in "color"
             builder->fsCodeAppend("\t\tvec4 color = ");
-            builder->fsAppendTextureLookup(samplers[0], bmpCoord.c_str(), bmpCoordType);
+            builder->fsAppendTextureLookup(samplers[0], coords[0].c_str(), coords[0].type());
             builder->fsCodeAppend(";\n");
 
             // put alpha from mask texture in "mask"
             builder->fsCodeAppend("\t\tfloat mask = ");
-            builder->fsAppendTextureLookup(samplers[1], maskCoord.c_str(), maskCoordType);
+            builder->fsAppendTextureLookup(samplers[1], coords[1].c_str(), coords[1].type());
             builder->fsCodeAppend(".a;\n");
 
             const char* threshold;
@@ -171,31 +159,12 @@
 
         virtual void setData(const GrGLUniformManager& uman, const GrDrawEffect& e) SK_OVERRIDE {
             const ThresholdEffect& effect = e.castEffect<ThresholdEffect>();
-            fBmpMatrix.setData(uman, effect.fBmpMatrix, e, effect.fBmpAccess.getTexture());
-            fMaskMatrix.setData(uman, effect.fMaskMatrix, e, effect.fMaskAccess.getTexture());
             if (fPrevThreshold != effect.fThreshold) {
                 uman.set1f(fThresholdUniHandle, effect.fThreshold);
             }
         }
 
-        static inline EffectKey GenKey(const GrDrawEffect& e, const GrGLCaps&) {
-            const ThresholdEffect& effect = e.castEffect<ThresholdEffect>();
-
-            EffectKey bmpMKey = GrGLEffectMatrix::GenKey(effect.fBmpMatrix,
-                                                         e,
-                                                         GrEffect::kLocal_CoordsType,
-                                                         effect.fBmpAccess.getTexture());
-            EffectKey maskMKey = GrGLEffectMatrix::GenKey(effect.fMaskMatrix,
-                                                          e,
-                                                          GrEffect::kLocal_CoordsType,
-                                                          effect.fMaskAccess.getTexture());
-            return bmpMKey | (maskMKey << GrGLEffectMatrix::kKeyBits);
-        }
-
     private:
-        GrGLEffectMatrix fBmpMatrix;
-        GrGLEffectMatrix fMaskMatrix;
-
         GrGLUniformManager::UniformHandle fThresholdUniHandle;
         SkScalar                          fPrevThreshold;
     };
@@ -206,12 +175,14 @@
     ThresholdEffect(GrTexture* bmpTexture, const SkMatrix& bmpMatrix,
                     GrTexture* maskTexture, const SkMatrix& maskMatrix,
                     SkScalar threshold)
-    : fBmpAccess(bmpTexture, GrTextureParams())
+    : fBmpTransform(kLocal_GrCoordSet, bmpMatrix, bmpTexture)
+    , fBmpAccess(bmpTexture, GrTextureParams())
+    , fMaskTransform(kLocal_GrCoordSet, maskMatrix, maskTexture)
     , fMaskAccess(maskTexture, GrTextureParams())
-    , fBmpMatrix(bmpMatrix)
-    , fMaskMatrix(maskMatrix)
     , fThreshold(threshold) {
+        this->addCoordTransform(&fBmpTransform);
         this->addTextureAccess(&fBmpAccess);
+        this->addCoordTransform(&fMaskTransform);
         this->addTextureAccess(&fMaskAccess);
     }
 
@@ -219,16 +190,15 @@
         const ThresholdEffect& e = CastEffect<ThresholdEffect>(other);
         return e.fBmpAccess.getTexture() == fBmpAccess.getTexture() &&
                e.fMaskAccess.getTexture() == fMaskAccess.getTexture() &&
-               e.fBmpMatrix == fBmpMatrix &&
-               e.fMaskMatrix == fMaskMatrix &&
+               e.fBmpTransform.getMatrix() == fBmpTransform.getMatrix() &&
+               e.fMaskTransform.getMatrix() == fMaskTransform.getMatrix() &&
                e.fThreshold == fThreshold;
     }
 
-    GrTextureAccess fBmpAccess;
-    GrTextureAccess fMaskAccess;
-
-    SkMatrix fBmpMatrix;
-    SkMatrix fMaskMatrix;
+    GrCoordTransform fBmpTransform;
+    GrTextureAccess  fBmpAccess;
+    GrCoordTransform fMaskTransform;
+    GrTextureAccess  fMaskAccess;
 
     SkScalar fThreshold;
 };
diff --git a/effects/SkColorMatrixFilter.cpp b/effects/SkColorMatrixFilter.cpp
index 1f841d0..484836a 100644
--- a/effects/SkColorMatrixFilter.cpp
+++ b/effects/SkColorMatrixFilter.cpp
@@ -399,6 +399,7 @@
                               EffectKey,
                               const char* outputColor,
                               const char* inputColor,
+                              const TransformedCoordsArray&,
                               const TextureSamplerArray&) SK_OVERRIDE {
             fMatrixHandle = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
                                                 kMat44f_GrSLType,
diff --git a/effects/SkDisplacementMapEffect.cpp b/effects/SkDisplacementMapEffect.cpp
index a751365..020ee02 100644
--- a/effects/SkDisplacementMapEffect.cpp
+++ b/effects/SkDisplacementMapEffect.cpp
@@ -11,8 +11,8 @@
 #include "SkColorPriv.h"
 #if SK_SUPPORT_GPU
 #include "GrContext.h"
+#include "GrCoordTransform.h"
 #include "gl/GrGLEffect.h"
-#include "gl/GrGLEffectMatrix.h"
 #include "GrTBackendEffectFactory.h"
 #include "SkImageFilterUtils.h"
 #endif
@@ -209,6 +209,7 @@
                           EffectKey,
                           const char* outputColor,
                           const char* inputColor,
+                          const TransformedCoordsArray&,
                           const TextureSamplerArray&) SK_OVERRIDE;
 
     static inline EffectKey GenKey(const GrDrawEffect&, const GrGLCaps&);
@@ -216,12 +217,8 @@
     virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE;
 
 private:
-    static const GrEffect::CoordsType kCoordsType = GrEffect::kLocal_CoordsType;
-
     SkDisplacementMapEffect::ChannelSelectorType fXChannelSelector;
     SkDisplacementMapEffect::ChannelSelectorType fYChannelSelector;
-    GrGLEffectMatrix fDisplacementEffectMatrix;
-    GrGLEffectMatrix fColorEffectMatrix;
     GrGLUniformManager::UniformHandle fScaleUni;
 
     typedef GrGLEffect INHERITED;
@@ -257,7 +254,6 @@
     virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
 
 private:
-
     virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE;
 
     GrDisplacementMapEffect(SkDisplacementMapEffect::ChannelSelectorType xChannelSelector,
@@ -266,7 +262,9 @@
 
     GR_DECLARE_EFFECT_TEST;
 
+    GrCoordTransform            fDisplacementTransform;
     GrTextureAccess             fDisplacementAccess;
+    GrCoordTransform            fColorTransform;
     GrTextureAccess             fColorAccess;
     SkDisplacementMapEffect::ChannelSelectorType fXChannelSelector;
     SkDisplacementMapEffect::ChannelSelectorType fYChannelSelector;
@@ -327,12 +325,16 @@
                              SkScalar scale,
                              GrTexture* displacement,
                              GrTexture* color)
-    : fDisplacementAccess(displacement)
+    : fDisplacementTransform(kLocal_GrCoordSet, displacement)
+    , fDisplacementAccess(displacement)
+    , fColorTransform(kLocal_GrCoordSet, color)
     , fColorAccess(color)
     , fXChannelSelector(xChannelSelector)
     , fYChannelSelector(yChannelSelector)
     , fScale(scale) {
+    this->addCoordTransform(&fDisplacementTransform);
     this->addTextureAccess(&fDisplacementAccess);
+    this->addCoordTransform(&fColorTransform);
     this->addTextureAccess(&fColorAccess);
 }
 
@@ -393,9 +395,7 @@
                                                      const GrDrawEffect& drawEffect)
     : INHERITED(factory)
     , fXChannelSelector(drawEffect.castEffect<GrDisplacementMapEffect>().xChannelSelector())
-    , fYChannelSelector(drawEffect.castEffect<GrDisplacementMapEffect>().yChannelSelector())
-    , fDisplacementEffectMatrix(kCoordsType)
-    , fColorEffectMatrix(kCoordsType) {
+    , fYChannelSelector(drawEffect.castEffect<GrDisplacementMapEffect>().yChannelSelector()) {
 }
 
 GrGLDisplacementMapEffect::~GrGLDisplacementMapEffect() {
@@ -406,20 +406,13 @@
                                          EffectKey key,
                                          const char* outputColor,
                                          const char* inputColor,
+                                         const TransformedCoordsArray& coords,
                                          const TextureSamplerArray& samplers) {
     sk_ignore_unused_variable(inputColor);
 
     fScaleUni = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
                                     kVec2f_GrSLType, "Scale");
     const char* scaleUni = builder->getUniformCStr(fScaleUni);
-
-    SkString dCoordsIn;
-    GrSLType dCoordsType = fDisplacementEffectMatrix.emitCode(
-                                builder, key, &dCoordsIn, NULL, "DISPL");
-    SkString cCoordsIn;
-    GrSLType cCoordsType = fColorEffectMatrix.emitCode(
-                                builder, key, &cCoordsIn, NULL, "COLOR");
-
     const char* dColor = "dColor";
     const char* cCoords = "cCoords";
     const char* outOfBounds = "outOfBounds";
@@ -428,7 +421,7 @@
                                    // leave room for 32-bit float GPU rounding errors.
 
     builder->fsCodeAppendf("\t\tvec4 %s = ", dColor);
-    builder->fsAppendTextureLookup(samplers[0], dCoordsIn.c_str(), dCoordsType);
+    builder->fsAppendTextureLookup(samplers[0], coords[0].c_str(), coords[0].type());
     builder->fsCodeAppend(";\n");
 
     // Unpremultiply the displacement
@@ -436,7 +429,7 @@
                            dColor, dColor, nearZero, dColor, dColor);
 
     builder->fsCodeAppendf("\t\tvec2 %s = %s + %s*(%s.",
-                           cCoords, cCoordsIn.c_str(), scaleUni, dColor);
+                           cCoords, coords[1].c_str(), scaleUni, dColor);
 
     switch (fXChannelSelector) {
       case SkDisplacementMapEffect::kR_ChannelSelectorType:
@@ -481,7 +474,7 @@
         "bool %s = (%s.x < 0.0) || (%s.y < 0.0) || (%s.x > 1.0) || (%s.y > 1.0);\t\t",
         outOfBounds, cCoords, cCoords, cCoords, cCoords);
     builder->fsCodeAppendf("%s = %s ? vec4(0.0) : ", outputColor, outOfBounds);
-    builder->fsAppendTextureLookup(samplers[1], cCoords, cCoordsType);
+    builder->fsAppendTextureLookup(samplers[1], cCoords, coords[1].type());
     builder->fsCodeAppend(";\n");
 }
 
@@ -489,17 +482,7 @@
                                         const GrDrawEffect& drawEffect) {
     const GrDisplacementMapEffect& displacementMap =
         drawEffect.castEffect<GrDisplacementMapEffect>();
-    GrTexture* displTex = displacementMap.texture(0);
     GrTexture* colorTex = displacementMap.texture(1);
-    fDisplacementEffectMatrix.setData(uman,
-                                     GrEffect::MakeDivByTextureWHMatrix(displTex),
-                                     drawEffect,
-                                     displTex);
-    fColorEffectMatrix.setData(uman,
-                               GrEffect::MakeDivByTextureWHMatrix(colorTex),
-                               drawEffect,
-                               colorTex);
-
     SkScalar scaleX = SkScalarDiv(displacementMap.scale(), SkIntToScalar(colorTex->width()));
     SkScalar scaleY = SkScalarDiv(displacementMap.scale(), SkIntToScalar(colorTex->height()));
     uman.set2f(fScaleUni, SkScalarToFloat(scaleX),
@@ -512,24 +495,9 @@
     const GrDisplacementMapEffect& displacementMap =
         drawEffect.castEffect<GrDisplacementMapEffect>();
 
-    GrTexture* displTex = displacementMap.texture(0);
-    GrTexture* colorTex = displacementMap.texture(1);
+    EffectKey xKey = displacementMap.xChannelSelector();
+    EffectKey yKey = displacementMap.yChannelSelector() << SkDisplacementMapEffect::kKeyBits;
 
-    EffectKey displKey = GrGLEffectMatrix::GenKey(GrEffect::MakeDivByTextureWHMatrix(displTex),
-                                                  drawEffect,
-                                                  kCoordsType,
-                                                  displTex);
-
-    EffectKey colorKey = GrGLEffectMatrix::GenKey(GrEffect::MakeDivByTextureWHMatrix(colorTex),
-                                                  drawEffect,
-                                                  kCoordsType,
-                                                  colorTex);
-
-    colorKey <<= GrGLEffectMatrix::kKeyBits;
-    EffectKey xKey = displacementMap.xChannelSelector() << (2 * GrGLEffectMatrix::kKeyBits);
-    EffectKey yKey = displacementMap.yChannelSelector() << (2 * GrGLEffectMatrix::kKeyBits +
-                                                            SkDisplacementMapEffect::kKeyBits);
-
-    return xKey | yKey | displKey | colorKey;
+    return xKey | yKey;
 }
 #endif
diff --git a/effects/SkLightingImageFilter.cpp b/effects/SkLightingImageFilter.cpp
index ca52b98..aac5462 100644
--- a/effects/SkLightingImageFilter.cpp
+++ b/effects/SkLightingImageFilter.cpp
@@ -16,7 +16,6 @@
 #if SK_SUPPORT_GPU
 #include "effects/GrSingleTextureEffect.h"
 #include "gl/GrGLEffect.h"
-#include "gl/GrGLEffectMatrix.h"
 #include "GrEffect.h"
 #include "GrTBackendEffectFactory.h"
 
@@ -1075,6 +1074,7 @@
                           EffectKey,
                           const char* outputColor,
                           const char* inputColor,
+                          const TransformedCoordsArray&,
                           const TextureSamplerArray&) SK_OVERRIDE;
 
     static inline EffectKey GenKey(const GrDrawEffect&, const GrGLCaps&);
@@ -1093,7 +1093,6 @@
     UniformHandle       fImageIncrementUni;
     UniformHandle       fSurfaceScaleUni;
     GrGLLight*          fLight;
-    GrGLEffectMatrix    fEffectMatrix;
 };
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -1196,8 +1195,7 @@
 
 GrGLLightingEffect::GrGLLightingEffect(const GrBackendEffectFactory& factory,
                                        const GrDrawEffect& drawEffect)
-    : INHERITED(factory)
-    , fEffectMatrix(drawEffect.castEffect<GrLightingEffect>().coordsType()) {
+    : INHERITED(factory) {
     const GrLightingEffect& m = drawEffect.castEffect<GrLightingEffect>();
     fLight = m.light()->createGLLight();
 }
@@ -1211,9 +1209,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,
@@ -1272,7 +1270,7 @@
                             interiorNormalBody.c_str(),
                             &interiorNormalName);
 
-    builder->fsCodeAppendf("\t\tvec2 coord = %s;\n", coords.c_str());
+    builder->fsCodeAppendf("\t\tvec2 coord = %s;\n", coords2D.c_str());
     builder->fsCodeAppend("\t\tfloat m[9];\n");
 
     const char* imgInc = builder->getUniformCStr(fImageIncrementUni);
@@ -1304,14 +1302,7 @@
 
 GrGLEffect::EffectKey GrGLLightingEffect::GenKey(const GrDrawEffect& drawEffect,
                                                  const GrGLCaps& caps) {
-    const GrLightingEffect& lighting = drawEffect.castEffect<GrLightingEffect>();
-    EffectKey key = lighting.light()->type();
-    key <<= GrGLEffectMatrix::kKeyBits;
-    EffectKey matrixKey = GrGLEffectMatrix::GenKey(lighting.getMatrix(),
-                                                   drawEffect,
-                                                   lighting.coordsType(),
-                                                   lighting.texture(0));
-    return key | matrixKey;
+    return drawEffect.castEffect<GrLightingEffect>().light()->type();
 }
 
 void GrGLLightingEffect::setData(const GrGLUniformManager& uman,
@@ -1323,10 +1314,6 @@
     uman.set1f(fSurfaceScaleUni, lighting.surfaceScale());
     SkAutoTUnref<SkLight> transformedLight(lighting.light()->transform(lighting.filterMatrix()));
     fLight->setData(uman, transformedLight);
-    fEffectMatrix.setData(uman,
-                          lighting.getMatrix(),
-                          drawEffect,
-                          lighting.texture(0));
 }
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/effects/SkLumaXfermode.cpp b/effects/SkLumaXfermode.cpp
index a23e010..3ecb0be 100644
--- a/effects/SkLumaXfermode.cpp
+++ b/effects/SkLumaXfermode.cpp
@@ -12,7 +12,6 @@
 
 #if SK_SUPPORT_GPU
 #include "gl/GrGLEffect.h"
-#include "gl/GrGLEffectMatrix.h"
 #include "GrContext.h"
 #include "GrTBackendEffectFactory.h"
 #endif
@@ -159,6 +158,7 @@
                           EffectKey,
                           const char* outputColor,
                           const char* inputColor,
+                          const TransformedCoordsArray&,
                           const TextureSamplerArray&) SK_OVERRIDE;
 
     static inline EffectKey GenKey(const GrDrawEffect&, const GrGLCaps&);
@@ -203,11 +203,12 @@
 }
 
 void GrGLLumaMaskEffect::emitCode(GrGLShaderBuilder* builder,
-                                    const GrDrawEffect& effect,
-                                    EffectKey key,
-                                    const char* outputColor,
-                                    const char* inputColor,
-                                    const TextureSamplerArray& samplers) {
+                                  const GrDrawEffect& effect,
+                                  EffectKey key,
+                                  const char* outputColor,
+                                  const char* inputColor,
+                                  const TransformedCoordsArray&,
+                                  const TextureSamplerArray& samplers) {
 
     const GrLumaMaskEffect& lumaEffect = effect.castEffect<GrLumaMaskEffect>();
     const char* dstColor = builder->dstColor();
diff --git a/effects/SkMagnifierImageFilter.cpp b/effects/SkMagnifierImageFilter.cpp
index c74b067..e3d6efd 100644
--- a/effects/SkMagnifierImageFilter.cpp
+++ b/effects/SkMagnifierImageFilter.cpp
@@ -14,7 +14,6 @@
 #if SK_SUPPORT_GPU
 #include "effects/GrSingleTextureEffect.h"
 #include "gl/GrGLEffect.h"
-#include "gl/GrGLEffectMatrix.h"
 #include "gl/GrGLSL.h"
 #include "gl/GrGLTexture.h"
 #include "GrTBackendEffectFactory.h"
@@ -99,26 +98,21 @@
                           EffectKey,
                           const char* outputColor,
                           const char* inputColor,
+                          const TransformedCoordsArray&,
                           const TextureSamplerArray&) SK_OVERRIDE;
 
     virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE;
 
-    static inline EffectKey GenKey(const GrDrawEffect&, const GrGLCaps&);
-
 private:
     UniformHandle       fOffsetVar;
     UniformHandle       fZoomVar;
     UniformHandle       fInsetVar;
 
-    GrGLEffectMatrix    fEffectMatrix;
-
     typedef GrGLEffect INHERITED;
 };
 
-GrGLMagnifierEffect::GrGLMagnifierEffect(const GrBackendEffectFactory& factory,
-                                         const GrDrawEffect& drawEffect)
-    : INHERITED(factory)
-    , fEffectMatrix(drawEffect.castEffect<GrMagnifierEffect>().coordsType()) {
+GrGLMagnifierEffect::GrGLMagnifierEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&)
+    : INHERITED(factory) {
 }
 
 void GrGLMagnifierEffect::emitCode(GrGLShaderBuilder* builder,
@@ -126,9 +120,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);
     fOffsetVar = builder->addUniform(
         GrGLShaderBuilder::kFragment_Visibility |
         GrGLShaderBuilder::kVertex_Visibility,
@@ -142,10 +136,10 @@
         GrGLShaderBuilder::kVertex_Visibility,
         kVec2f_GrSLType, "uInset");
 
-    builder->fsCodeAppendf("\t\tvec2 coord = %s;\n", coords.c_str());
+    builder->fsCodeAppendf("\t\tvec2 coord = %s;\n", coords2D.c_str());
     builder->fsCodeAppendf("\t\tvec2 zoom_coord = %s + %s / %s;\n",
                            builder->getUniformCStr(fOffsetVar),
-                           coords.c_str(),
+                           coords2D.c_str(),
                            builder->getUniformCStr(fZoomVar));
 
     builder->fsCodeAppend("\t\tvec2 delta = min(coord, vec2(1.0, 1.0) - coord);\n");
@@ -180,16 +174,6 @@
     uman.set2f(fOffsetVar, zoom.x_offset(), zoom.y_offset());
     uman.set2f(fZoomVar, zoom.x_zoom(), zoom.y_zoom());
     uman.set2f(fInsetVar, zoom.x_inset(), zoom.y_inset());
-    fEffectMatrix.setData(uman, zoom.getMatrix(), drawEffect, zoom.texture(0));
-}
-
-GrGLEffect::EffectKey GrGLMagnifierEffect::GenKey(const GrDrawEffect& drawEffect,
-                                                  const GrGLCaps&) {
-    const GrMagnifierEffect& zoom = drawEffect.castEffect<GrMagnifierEffect>();
-    return GrGLEffectMatrix::GenKey(zoom.getMatrix(),
-                                    drawEffect,
-                                    zoom.coordsType(),
-                                    zoom.texture(0));
 }
 
 /////////////////////////////////////////////////////////////////////
diff --git a/effects/SkMatrixConvolutionImageFilter.cpp b/effects/SkMatrixConvolutionImageFilter.cpp
index 9446b8e..d73f633 100644
--- a/effects/SkMatrixConvolutionImageFilter.cpp
+++ b/effects/SkMatrixConvolutionImageFilter.cpp
@@ -14,7 +14,6 @@
 
 #if SK_SUPPORT_GPU
 #include "gl/GrGLEffect.h"
-#include "gl/GrGLEffectMatrix.h"
 #include "effects/GrSingleTextureEffect.h"
 #include "GrTBackendEffectFactory.h"
 #include "GrTexture.h"
@@ -324,6 +323,7 @@
                           EffectKey,
                           const char* outputColor,
                           const char* inputColor,
+                          const TransformedCoordsArray&,
                           const TextureSamplerArray&) SK_OVERRIDE;
 
     static inline EffectKey GenKey(const GrDrawEffect&, const GrGLCaps&);
@@ -343,15 +343,12 @@
     UniformHandle       fGainUni;
     UniformHandle       fBiasUni;
 
-    GrGLEffectMatrix    fEffectMatrix;
-
     typedef GrGLEffect INHERITED;
 };
 
 GrGLMatrixConvolutionEffect::GrGLMatrixConvolutionEffect(const GrBackendEffectFactory& factory,
                                                          const GrDrawEffect& drawEffect)
-    : INHERITED(factory)
-    , fEffectMatrix(drawEffect.castEffect<GrMatrixConvolutionEffect>().coordsType()) {
+    : INHERITED(factory) {
     const GrMatrixConvolutionEffect& m = drawEffect.castEffect<GrMatrixConvolutionEffect>();
     fKernelSize = m.kernelSize();
     fTileMode = m.tileMode();
@@ -384,9 +381,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");
     fKernelUni = builder->addUniformArray(GrGLShaderBuilder::kFragment_Visibility,
@@ -407,7 +404,7 @@
     int kHeight = fKernelSize.height();
 
     builder->fsCodeAppend("\t\tvec4 sum = vec4(0, 0, 0, 0);\n");
-    builder->fsCodeAppendf("\t\tvec2 coord = %s - %s * %s;\n", coords.c_str(), target, imgInc);
+    builder->fsCodeAppendf("\t\tvec2 coord = %s - %s * %s;\n", coords2D.c_str(), target, imgInc);
     builder->fsCodeAppendf("\t\tfor (int y = 0; y < %d; y++) {\n", kHeight);
     builder->fsCodeAppendf("\t\t\tfor (int x = 0; x < %d; x++) {\n", kWidth);
     builder->fsCodeAppendf("\t\t\t\tfloat k = %s[y * %d + x];\n", kernel, kWidth);
@@ -426,7 +423,7 @@
         builder->fsCodeAppendf("\t\t%s.rgb = clamp(%s.rgb, 0.0, %s.a);\n", outputColor, outputColor, outputColor);
     } else {
         builder->fsCodeAppend("\t\tvec4 c = ");
-        appendTextureLookup(builder, samplers[0], coords.c_str(), fTileMode);
+        appendTextureLookup(builder, samplers[0], coords2D.c_str(), fTileMode);
         builder->fsCodeAppend(";\n");
         builder->fsCodeAppendf("\t\t%s.a = c.a;\n", outputColor);
         builder->fsCodeAppendf("\t\t%s.rgb = sum.rgb * %s + %s;\n", outputColor, gain, bias);
@@ -452,12 +449,7 @@
     EffectKey key = encodeXY(m.kernelSize().width(), m.kernelSize().height());
     key |= m.tileMode() << 7;
     key |= m.convolveAlpha() ? 1 << 9 : 0;
-    key <<= GrGLEffectMatrix::kKeyBits;
-    EffectKey matrixKey = GrGLEffectMatrix::GenKey(m.getMatrix(),
-                                                   drawEffect,
-                                                   m.coordsType(),
-                                                   m.texture(0));
-    return key | matrixKey;
+    return key;
 }
 
 void GrGLMatrixConvolutionEffect::setData(const GrGLUniformManager& uman,
@@ -476,10 +468,6 @@
     uman.set1fv(fKernelUni, 0, fKernelSize.width() * fKernelSize.height(), conv.kernel());
     uman.set1f(fGainUni, conv.gain());
     uman.set1f(fBiasUni, conv.bias());
-    fEffectMatrix.setData(uman,
-                          conv.getMatrix(),
-                          drawEffect,
-                          conv.texture(0));
 }
 
 GrMatrixConvolutionEffect::GrMatrixConvolutionEffect(GrTexture* texture,
diff --git a/effects/SkMorphologyImageFilter.cpp b/effects/SkMorphologyImageFilter.cpp
index 949dc4a..8b314c8 100644
--- a/effects/SkMorphologyImageFilter.cpp
+++ b/effects/SkMorphologyImageFilter.cpp
@@ -15,7 +15,6 @@
 #include "GrTexture.h"
 #include "GrTBackendEffectFactory.h"
 #include "gl/GrGLEffect.h"
-#include "gl/GrGLEffectMatrix.h"
 #include "effects/Gr1DKernelEffect.h"
 #include "SkImageFilterUtils.h"
 #endif
@@ -314,6 +313,7 @@
                           EffectKey,
                           const char* outputColor,
                           const char* inputColor,
+                          const TransformedCoordsArray&,
                           const TextureSamplerArray&) SK_OVERRIDE;
 
     static inline EffectKey GenKey(const GrDrawEffect&, const GrGLCaps&);
@@ -326,15 +326,13 @@
     int                                 fRadius;
     GrMorphologyEffect::MorphologyType  fType;
     GrGLUniformManager::UniformHandle   fImageIncrementUni;
-    GrGLEffectMatrix                    fEffectMatrix;
 
     typedef GrGLEffect INHERITED;
 };
 
 GrGLMorphologyEffect::GrGLMorphologyEffect(const GrBackendEffectFactory& factory,
                                            const GrDrawEffect& drawEffect)
-    : INHERITED(factory)
-    , fEffectMatrix(drawEffect.castEffect<GrMorphologyEffect>().coordsType()) {
+    : INHERITED(factory) {
     const GrMorphologyEffect& m = drawEffect.castEffect<GrMorphologyEffect>();
     fRadius = m.radius();
     fType = m.type();
@@ -345,9 +343,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");
 
@@ -368,7 +366,7 @@
     }
     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);
     builder->fsCodeAppendf("\t\tfor (int i = 0; i < %d; i++) {\n", this->width());
     builder->fsCodeAppendf("\t\t\t%s = %s(%s, ", outputColor, func, outputColor);
     builder->fsAppendTextureLookup(samplers[0], "coord");
@@ -385,12 +383,7 @@
     const GrMorphologyEffect& m = drawEffect.castEffect<GrMorphologyEffect>();
     EffectKey key = static_cast<EffectKey>(m.radius());
     key |= (m.type() << 8);
-    key <<= GrGLEffectMatrix::kKeyBits;
-    EffectKey matrixKey = GrGLEffectMatrix::GenKey(m.getMatrix(),
-                                                   drawEffect,
-                                                   m.coordsType(),
-                                                   m.texture(0));
-    return key | matrixKey;
+    return key;
 }
 
 void GrGLMorphologyEffect::setData(const GrGLUniformManager& uman,
@@ -411,7 +404,6 @@
             GrCrash("Unknown filter direction.");
     }
     uman.set2fv(fImageIncrementUni, 0, 1, imageIncrement);
-    fEffectMatrix.setData(uman, kern.getMatrix(), drawEffect, kern.texture(0));
 }
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/effects/SkPerlinNoiseShader.cpp b/effects/SkPerlinNoiseShader.cpp
index de418c3..df6b78e 100644
--- a/effects/SkPerlinNoiseShader.cpp
+++ b/effects/SkPerlinNoiseShader.cpp
@@ -14,8 +14,8 @@
 
 #if SK_SUPPORT_GPU
 #include "GrContext.h"
+#include "GrCoordTransform.h"
 #include "gl/GrGLEffect.h"
-#include "gl/GrGLEffectMatrix.h"
 #include "GrTBackendEffectFactory.h"
 #include "SkGr.h"
 #endif
@@ -515,7 +515,6 @@
     GrGLUniformManager::UniformHandle   fBaseFrequencyUni;
     GrGLUniformManager::UniformHandle   fAlphaUni;
     GrGLUniformManager::UniformHandle   fInvMatrixUni;
-    GrGLEffectMatrix                    fEffectMatrix;
 
 private:
     typedef GrGLEffect INHERITED;
@@ -533,6 +532,7 @@
                           EffectKey,
                           const char* outputColor,
                           const char* inputColor,
+                          const TransformedCoordsArray&,
                           const TextureSamplerArray&) SK_OVERRIDE;
 
     virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE;
@@ -557,6 +557,7 @@
                           EffectKey,
                           const char* outputColor,
                           const char* inputColor,
+                          const TransformedCoordsArray&,
                           const TextureSamplerArray&) SK_OVERRIDE;
 
     virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE;
@@ -577,9 +578,8 @@
     bool stitchTiles() const { return fStitchTiles; }
     const SkVector& baseFrequency() const { return fBaseFrequency; }
     int numOctaves() const { return fNumOctaves; }
-    const SkMatrix& matrix() const { return fMatrix; }
+    const SkMatrix& matrix() const { return fCoordTransform.getMatrix(); }
     uint8_t alpha() const { return fAlpha; }
-    GrGLEffectMatrix::CoordsType coordsType() const { return GrEffect::kLocal_CoordsType; }
 
     void getConstantColorComponents(GrColor*, uint32_t* validFlags) const SK_OVERRIDE {
         *validFlags = 0; // This is noise. Nothing is constant.
@@ -592,7 +592,7 @@
                fBaseFrequency == s.fBaseFrequency &&
                fNumOctaves == s.fNumOctaves &&
                fStitchTiles == s.fStitchTiles &&
-               fMatrix == s.fMatrix &&
+               fCoordTransform.getMatrix() == s.fCoordTransform.getMatrix() &&
                fAlpha == s.fAlpha;
     }
 
@@ -604,9 +604,16 @@
       , fStitchTiles(stitchTiles)
       , fMatrix(matrix)
       , fAlpha(alpha) {
+        // This (1,1) translation is due to WebKit's 1 based coordinates for the noise
+        // (as opposed to 0 based, usually). The same adjustment is in the shadeSpan() functions.
+        SkMatrix m = matrix;
+        m.postTranslate(SK_Scalar1, SK_Scalar1);
+        fCoordTransform.reset(kLocal_GrCoordSet, m);
+        this->addCoordTransform(&fCoordTransform);
     }
 
     SkPerlinNoiseShader::Type       fType;
+    GrCoordTransform                fCoordTransform;
     SkVector                        fBaseFrequency;
     int                             fNumOctaves;
     bool                            fStitchTiles;
@@ -746,11 +753,11 @@
                                 EffectKey key,
                                 const char* outputColor,
                                 const char* inputColor,
+                                const TransformedCoordsArray& coords,
                                 const TextureSamplerArray&) {
     sk_ignore_unused_variable(inputColor);
 
-    SkString vCoords;
-    fEffectMatrix.emitCodeMakeFSCoords2D(builder, key, &vCoords);
+    SkString vCoords = builder->ensureFSCoords2D(coords, 0);
 
     fSeedUni = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
                                    kFloat_GrSLType, "seed");
@@ -962,11 +969,11 @@
                                EffectKey key,
                                const char* outputColor,
                                const char* inputColor,
+                               const TransformedCoordsArray& coords,
                                const TextureSamplerArray& samplers) {
     sk_ignore_unused_variable(inputColor);
 
-    SkString vCoords;
-    fEffectMatrix.emitCodeMakeFSCoords2D(builder, key, &vCoords);
+    SkString vCoords = builder->ensureFSCoords2D(coords, 0);
 
     fInvMatrixUni = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
                                         kMat33f_GrSLType, "invMatrix");
@@ -1217,8 +1224,7 @@
   : INHERITED (factory)
   , fType(drawEffect.castEffect<GrPerlinNoiseEffect>().type())
   , fStitchTiles(drawEffect.castEffect<GrPerlinNoiseEffect>().stitchTiles())
-  , fNumOctaves(drawEffect.castEffect<GrPerlinNoiseEffect>().numOctaves())
-  , fEffectMatrix(drawEffect.castEffect<GrPerlinNoiseEffect>().coordsType()) {
+  , fNumOctaves(drawEffect.castEffect<GrPerlinNoiseEffect>().numOctaves()) {
 }
 
 GrGLEffect::EffectKey GrGLNoise::GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) {
@@ -1244,12 +1250,7 @@
         key |= 0x4; // Flip the 3rd bit if tile stitching is on
     }
 
-    key = key << GrGLEffectMatrix::kKeyBits;
-
-    SkMatrix m = turbulence.matrix();
-    m.postTranslate(SK_Scalar1, SK_Scalar1);
-    return key | GrGLEffectMatrix::GenKey(m, drawEffect,
-                 drawEffect.castEffect<GrPerlinNoiseEffect>().coordsType(), NULL);
+    return key;
 }
 
 void GrGLNoise::setData(const GrGLUniformManager& uman, const GrDrawEffect& drawEffect) {
@@ -1260,6 +1261,7 @@
     uman.set1f(fAlphaUni, SkScalarDiv(SkIntToScalar(turbulence.alpha()), SkIntToScalar(255)));
 
     SkMatrix m = turbulence.matrix();
+    m.postTranslate(-SK_Scalar1, -SK_Scalar1);
     SkMatrix invM;
     if (!m.invert(&invM)) {
         invM.reset();
@@ -1267,11 +1269,6 @@
         invM.postConcat(invM); // Square the matrix
     }
     uman.setSkMatrix(fInvMatrixUni, invM);
-
-    // This (1,1) translation is due to WebKit's 1 based coordinates for the noise
-    // (as opposed to 0 based, usually). The same adjustment is in the shadeSpan() functions.
-    m.postTranslate(SK_Scalar1, SK_Scalar1);
-    fEffectMatrix.setData(uman, m, drawEffect, NULL);
 }
 
 void GrGLPerlinNoise::setData(const GrGLUniformManager& uman, const GrDrawEffect& drawEffect) {
diff --git a/effects/SkTableColorFilter.cpp b/effects/SkTableColorFilter.cpp
index f6088f5..083b54c 100644
--- a/effects/SkTableColorFilter.cpp
+++ b/effects/SkTableColorFilter.cpp
@@ -272,6 +272,7 @@
                           EffectKey,
                           const char* outputColor,
                           const char* inputColor,
+                          const TransformedCoordsArray&,
                           const TextureSamplerArray&) SK_OVERRIDE;
 
     virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE {}
@@ -292,6 +293,7 @@
                                   EffectKey,
                                   const char* outputColor,
                                   const char* inputColor,
+                                  const TransformedCoordsArray&,
                                   const TextureSamplerArray& samplers) {
 
     static const float kColorScaleFactor = 255.0f / 256.0f;
diff --git a/effects/gradients/SkGradientShader.cpp b/effects/gradients/SkGradientShader.cpp
index be6fdb0..74355b8 100644
--- a/effects/gradients/SkGradientShader.cpp
+++ b/effects/gradients/SkGradientShader.cpp
@@ -825,8 +825,7 @@
 
 GrGLGradientEffect::GrGLGradientEffect(const GrBackendEffectFactory& factory)
     : INHERITED(factory)
-    , fCachedYCoord(SK_ScalarMax)
-    , fEffectMatrix(kCoordsType) {
+    , fCachedYCoord(SK_ScalarMax) {
 }
 
 GrGLGradientEffect::~GrGLGradientEffect() { }
@@ -883,7 +882,6 @@
 
     if (GrGradientEffect::kTwo_ColorType == e.getColorType()){
 
-        fEffectMatrix.setData(uman, e.getMatrix(), drawEffect, NULL);
         if (GrGradientEffect::kBeforeInterp_PremulType == e.getPremulType()) {
             set_mul_color_uni(uman, fColorStartUni, e.getColors(0));
             set_mul_color_uni(uman, fColorEndUni,   e.getColors(1));
@@ -894,7 +892,6 @@
 
     } else if (GrGradientEffect::kThree_ColorType == e.getColorType()){
 
-        fEffectMatrix.setData(uman, e.getMatrix(), drawEffect, NULL);
         if (GrGradientEffect::kBeforeInterp_PremulType == e.getPremulType()) {
             set_mul_color_uni(uman, fColorStartUni, e.getColors(0));
             set_mul_color_uni(uman, fColorMidUni,   e.getColors(1));
@@ -905,8 +902,6 @@
             set_color_uni(uman, fColorEndUni,   e.getColors(2));
         }
     } else {
-        const GrTexture* texture = e.texture(0);
-        fEffectMatrix.setData(uman, e.getMatrix(), drawEffect, texture);
 
         SkScalar yCoord = e.getYCoord();
         if (yCoord != fCachedYCoord) {
@@ -919,13 +914,8 @@
 
 GrGLEffect::EffectKey GrGLGradientEffect::GenBaseGradientKey(const GrDrawEffect& drawEffect) {
     const GrGradientEffect& e = drawEffect.castEffect<GrGradientEffect>();
-    const GrTexture* texture = NULL;
 
-    if (GrGradientEffect::kTexture_ColorType == e.getColorType()){
-        texture = e.texture(0);
-    }
-
-    EffectKey key = GrGLEffectMatrix::GenKey(e.getMatrix(), drawEffect, kCoordsType, texture);
+    EffectKey key = 0;
 
     if (GrGradientEffect::kTwo_ColorType == e.getColorType()) {
         key |= kTwoColorKey;
@@ -940,18 +930,6 @@
     return key;
 }
 
-void GrGLGradientEffect::setupMatrix(GrGLShaderBuilder* builder,
-                                     EffectKey key,
-                                     SkString* fsCoordName,
-                                     SkString* vsVaryingName,
-                                     GrSLType* vsVaryingType) {
-    fEffectMatrix.emitCodeMakeFSCoords2D(builder,
-                                         key & kMatrixKeyMask,
-                                         fsCoordName,
-                                         vsVaryingName,
-                                         vsVaryingType);
-}
-
 void GrGLGradientEffect::emitColor(GrGLShaderBuilder* builder,
                                    const char* gradientTValue,
                                    EffectKey key,
@@ -1023,8 +1001,6 @@
                                    const SkMatrix& matrix,
                                    SkShader::TileMode tileMode) {
 
-
-    fMatrix = matrix;
     fIsOpaque = shader.isOpaque();
 
     SkShader::GradientInfo info;
@@ -1055,6 +1031,7 @@
         } else {
             fPremulType = kAfterInterp_PremulType;
         }
+        fCoordTransform.reset(kCoordSet, matrix);
     } else {
         // doesn't matter how this is set, just be consistent because it is part of the effect key.
         fPremulType = kBeforeInterp_PremulType;
@@ -1080,9 +1057,11 @@
         if (-1 != fRow) {
             fYCoord = fAtlas->getYOffset(fRow) + SK_ScalarHalf *
             fAtlas->getVerticalScaleFactor();
+            fCoordTransform.reset(kCoordSet, matrix, fAtlas->getTexture());
             fTextureAccess.reset(fAtlas->getTexture(), params);
         } else {
             GrTexture* texture = GrLockAndRefCachedBitmapTexture(ctx, bitmap, &params);
+            fCoordTransform.reset(kCoordSet, matrix, texture);
             fTextureAccess.reset(texture, params);
             fYCoord = SK_ScalarHalf;
 
@@ -1093,6 +1072,7 @@
         }
         this->addTextureAccess(&fTextureAccess);
     }
+    this->addCoordTransform(&fCoordTransform);
 }
 
 GrGradientEffect::~GrGradientEffect() {
@@ -1124,7 +1104,7 @@
                 s.fTextureAccess.getParams().getTileModeX() &&
             this->useAtlas() == s.useAtlas() &&
             fYCoord == s.getYCoord() &&
-            fMatrix.cheapEqualTo(s.getMatrix());
+            fCoordTransform.getMatrix().cheapEqualTo(s.fCoordTransform.getMatrix());
     }
 
     return false;
diff --git a/effects/gradients/SkGradientShaderPriv.h b/effects/gradients/SkGradientShaderPriv.h
index 55a0061..d573783 100644
--- a/effects/gradients/SkGradientShaderPriv.h
+++ b/effects/gradients/SkGradientShaderPriv.h
@@ -203,8 +203,8 @@
 
 #if SK_SUPPORT_GPU
 
+#include "GrCoordTransform.h"
 #include "gl/GrGLEffect.h"
-#include "gl/GrGLEffectMatrix.h"
 
 class GrEffectStage;
 class GrBackendEffectFactory;
@@ -247,7 +247,6 @@
 
     bool useAtlas() const { return SkToBool(-1 != fRow); }
     SkScalar getYCoord() const { return fYCoord; };
-    const SkMatrix& getMatrix() const { return fMatrix;}
 
     virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
 
@@ -290,16 +289,17 @@
     virtual bool onIsEqual(const GrEffect& effect) const SK_OVERRIDE;
 
 private:
+    static const GrCoordSet kCoordSet = kLocal_GrCoordSet;
 
     enum {
         kMaxAnalyticColors = 3 // if more colors use texture
     };
 
+    GrCoordTransform fCoordTransform;
     GrTextureAccess fTextureAccess;
     SkScalar fYCoord;
     GrTextureStripAtlas* fAtlas;
     int fRow;
-    SkMatrix fMatrix;
     bool fIsOpaque;
     ColorType fColorType;
     SkColor fColors[kMaxAnalyticColors];
@@ -320,26 +320,19 @@
     virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE;
 
 protected:
-    /**
-     * Subclasses must reserve the lower kMatrixKeyBitCnt of their key for use by
-     * GrGLGradientEffect.
-     */
     enum {
-        kMatrixKeyBitCnt = GrGLEffectMatrix::kKeyBits,
-        kMatrixKeyMask = (1 << kMatrixKeyBitCnt) - 1,
-
         kPremulTypeKeyBitCnt = 1,
-        kPremulTypeMask = 1 << kMatrixKeyBitCnt,
+        kPremulTypeMask = 1,
         kPremulBeforeInterpKey = kPremulTypeMask,
 
-        kTwoColorKey = 2 << (kMatrixKeyBitCnt + kPremulTypeKeyBitCnt),
-        kThreeColorKey = 3 << (kMatrixKeyBitCnt + kPremulTypeKeyBitCnt),
+        kTwoColorKey = 2 << kPremulTypeKeyBitCnt,
+        kThreeColorKey = 3 << kPremulTypeKeyBitCnt,
         kColorKeyMask = kTwoColorKey | kThreeColorKey,
         kColorKeyBitCnt = 2,
 
         // Subclasses must shift any key bits they produce up by this amount
         // and combine with the result of GenBaseGradientKey.
-        kBaseKeyBitCnt = (kMatrixKeyBitCnt + kPremulTypeKeyBitCnt + kColorKeyBitCnt)
+        kBaseKeyBitCnt = (kPremulTypeKeyBitCnt + kColorKeyBitCnt)
     };
 
     static GrGradientEffect::ColorType ColorTypeFromKey(EffectKey key){
@@ -364,20 +357,6 @@
      */
     static EffectKey GenBaseGradientKey(const GrDrawEffect&);
 
-    /**
-     * Inserts code to implement the GrGradientEffect's matrix. This should be called before a
-     * subclass emits its own code. The name of the 2D coords is output via fsCoordName and already
-     * incorporates any perspective division. The caller can also optionally retrieve the name of
-     * the varying inserted in the VS and its type, which may be either vec2f or vec3f depending
-     * upon whether the matrix has perspective or not. It is not necessary to mask the key before
-     * calling.
-     */
-    void setupMatrix(GrGLShaderBuilder* builder,
-                     EffectKey key,
-                     SkString* fsCoordName,
-                     SkString* vsVaryingName = NULL,
-                     GrSLType* vsVaryingType = NULL);
-
     // Emits the uniform used as the y-coord to texture samples in derived classes. Subclasses
     // should call this method from their emitCode().
     void emitUniforms(GrGLShaderBuilder* builder, EffectKey key);
@@ -394,14 +373,11 @@
                    const GrGLShaderBuilder::TextureSamplerArray& samplers);
 
 private:
-    static const GrEffect::CoordsType kCoordsType = GrEffect::kLocal_CoordsType;
-
     SkScalar fCachedYCoord;
     GrGLUniformManager::UniformHandle fFSYUni;
     GrGLUniformManager::UniformHandle fColorStartUni;
     GrGLUniformManager::UniformHandle fColorMidUni;
     GrGLUniformManager::UniformHandle fColorEndUni;
-    GrGLEffectMatrix fEffectMatrix;
 
     typedef GrGLEffect INHERITED;
 };
diff --git a/effects/gradients/SkLinearGradient.cpp b/effects/gradients/SkLinearGradient.cpp
index aa8486d..5563a03 100644
--- a/effects/gradients/SkLinearGradient.cpp
+++ b/effects/gradients/SkLinearGradient.cpp
@@ -448,6 +448,7 @@
                           EffectKey,
                           const char* outputColor,
                           const char* inputColor,
+                          const TransformedCoordsArray&,
                           const TextureSamplerArray&) SK_OVERRIDE;
 
     static EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) {
@@ -522,12 +523,10 @@
                                   EffectKey key,
                                   const char* outputColor,
                                   const char* inputColor,
+                                  const TransformedCoordsArray& coords,
                                   const TextureSamplerArray& samplers) {
     this->emitUniforms(builder, key);
-    SkString coords;
-    this->setupMatrix(builder, key, &coords);
-    SkString t;
-    t.append(coords);
+    SkString t = builder->ensureFSCoords2D(coords, 0);
     t.append(".x");
     this->emitColor(builder, t.c_str(), key, outputColor, inputColor, samplers);
 }
diff --git a/effects/gradients/SkRadialGradient.cpp b/effects/gradients/SkRadialGradient.cpp
index 923d31f..8bac12a 100644
--- a/effects/gradients/SkRadialGradient.cpp
+++ b/effects/gradients/SkRadialGradient.cpp
@@ -481,6 +481,7 @@
                           EffectKey,
                           const char* outputColor,
                           const char* inputColor,
+                          const TransformedCoordsArray&,
                           const TextureSamplerArray&) SK_OVERRIDE;
 
     static EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) {
@@ -557,12 +558,11 @@
                                   EffectKey key,
                                   const char* outputColor,
                                   const char* inputColor,
+                                  const TransformedCoordsArray& coords,
                                   const TextureSamplerArray& samplers) {
     this->emitUniforms(builder, key);
-    SkString coords;
-    this->setupMatrix(builder, key, &coords);
     SkString t("length(");
-    t.append(coords);
+    t.append(builder->ensureFSCoords2D(coords, 0));
     t.append(")");
     this->emitColor(builder, t.c_str(), key, outputColor, inputColor, samplers);
 }
diff --git a/effects/gradients/SkSweepGradient.cpp b/effects/gradients/SkSweepGradient.cpp
index 65e6c96..c38205b 100644
--- a/effects/gradients/SkSweepGradient.cpp
+++ b/effects/gradients/SkSweepGradient.cpp
@@ -402,6 +402,7 @@
                           EffectKey,
                           const char* outputColor,
                           const char* inputColor,
+                          const TransformedCoordsArray&,
                           const TextureSamplerArray&) SK_OVERRIDE;
 
     static EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) {
@@ -471,12 +472,12 @@
                                  EffectKey key,
                                  const char* outputColor,
                                  const char* inputColor,
+                                 const TransformedCoordsArray& coords,
                                  const TextureSamplerArray& samplers) {
     this->emitUniforms(builder, key);
-    SkString coords;
-    this->setupMatrix(builder, key, &coords);
+    SkString coords2D = builder->ensureFSCoords2D(coords, 0);
     SkString t;
-    t.printf("atan(- %s.y, - %s.x) * 0.1591549430918 + 0.5", coords.c_str(), coords.c_str());
+    t.printf("atan(- %s.y, - %s.x) * 0.1591549430918 + 0.5", coords2D.c_str(), coords2D.c_str());
     this->emitColor(builder, t.c_str(), key,
                           outputColor, inputColor, samplers);
 }
diff --git a/effects/gradients/SkTwoPointConicalGradient.cpp b/effects/gradients/SkTwoPointConicalGradient.cpp
index 6315565..0a5e29b 100644
--- a/effects/gradients/SkTwoPointConicalGradient.cpp
+++ b/effects/gradients/SkTwoPointConicalGradient.cpp
@@ -347,6 +347,7 @@
                           EffectKey,
                           const char* outputColor,
                           const char* inputColor,
+                          const TransformedCoordsArray&,
                           const TextureSamplerArray&) SK_OVERRIDE;
     virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE;
 
@@ -488,12 +489,8 @@
                                     EffectKey key,
                                     const char* outputColor,
                                     const char* inputColor,
+                                    const TransformedCoordsArray& coords,
                                     const TextureSamplerArray& samplers) {
-    SkString fsCoords;
-    SkString vsCoordsVarying;
-    GrSLType coordsVaryingType;
-    this->setupMatrix(builder, key, &fsCoords, &vsCoordsVarying, &coordsVaryingType);
-
     this->emitUniforms(builder, key);
     // 2 copies of uniform array, 1 for each of vertex & fragment shader,
     // to work around Xoom bug. Doesn't seem to cause performance decrease
@@ -506,7 +503,7 @@
     // For radial gradients without perspective we can pass the linear
     // part of the quadratic as a varying.
     GrGLShaderBuilder::VertexBuilder* vertexBuilder =
-        (kVec2f_GrSLType == coordsVaryingType) ? builder->getVertexBuilder() : NULL;
+        (kVec2f_GrSLType == coords[0].type()) ? builder->getVertexBuilder() : NULL;
     if (NULL != vertexBuilder) {
         vertexBuilder->addVarying(kFloat_GrSLType, "Conical2BCoeff",
                                      &fVSVaryingName, &fFSVaryingName);
@@ -527,13 +524,13 @@
             // r2Var = -2 * (r2Parm[2] * varCoord.x - r2Param[3] * r2Param[5])
             vertexBuilder->vsCodeAppendf("\t%s = -2.0 * (%s * %s.x + %s * %s);\n",
                                             fVSVaryingName, p2.c_str(),
-                                            vsCoordsVarying.c_str(), p3.c_str(), p5.c_str());
+                                            coords[0].getVSName().c_str(), p3.c_str(), p5.c_str());
         }
     }
 
     // FS
     {
-
+        SkString coords2D = builder->ensureFSCoords2D(coords, 0);
         SkString cName("c");
         SkString ac4Name("ac4");
         SkString dName("d");
@@ -563,7 +560,7 @@
         } else {
             bVar = "b";
             builder->fsCodeAppendf("\tfloat %s = -2.0 * (%s * %s.x + %s * %s);\n",
-                                   bVar.c_str(), p2.c_str(), fsCoords.c_str(),
+                                   bVar.c_str(), p2.c_str(), coords2D.c_str(),
                                    p3.c_str(), p5.c_str());
         }
 
@@ -573,7 +570,7 @@
 
         // c = (x^2)+(y^2) - params[4]
         builder->fsCodeAppendf("\tfloat %s = dot(%s, %s) - %s;\n", cName.c_str(),
-                               fsCoords.c_str(), fsCoords.c_str(),
+                               coords2D.c_str(), coords2D.c_str(),
                                p4.c_str());
 
         // Non-degenerate case (quadratic)
diff --git a/effects/gradients/SkTwoPointRadialGradient.cpp b/effects/gradients/SkTwoPointRadialGradient.cpp
index e3f5e9f..53d980a 100644
--- a/effects/gradients/SkTwoPointRadialGradient.cpp
+++ b/effects/gradients/SkTwoPointRadialGradient.cpp
@@ -389,6 +389,7 @@
                           EffectKey,
                           const char* outputColor,
                           const char* inputColor,
+                          const TransformedCoordsArray&,
                           const TextureSamplerArray&) SK_OVERRIDE;
     virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE;
 
@@ -530,15 +531,10 @@
                                    EffectKey key,
                                    const char* outputColor,
                                    const char* inputColor,
+                                   const TransformedCoordsArray& coords,
                                    const TextureSamplerArray& samplers) {
 
     this->emitUniforms(builder, key);
-    SkString fsCoords;
-    SkString vsCoordsVarying;
-
-    GrSLType coordsVaryingType;
-    this->setupMatrix(builder, key, &fsCoords, &vsCoordsVarying, &coordsVaryingType);
-
     // 2 copies of uniform array, 1 for each of vertex & fragment shader,
     // to work around Xoom bug. Doesn't seem to cause performance decrease
     // in test apps, but need to keep an eye on it.
@@ -550,7 +546,7 @@
     // For radial gradients without perspective we can pass the linear
     // part of the quadratic as a varying.
     GrGLShaderBuilder::VertexBuilder* vertexBuilder =
-        (kVec2f_GrSLType == coordsVaryingType) ? builder->getVertexBuilder() : NULL;
+        (kVec2f_GrSLType == coords[0].type()) ? builder->getVertexBuilder() : NULL;
     if (NULL != vertexBuilder) {
         vertexBuilder->addVarying(kFloat_GrSLType, "Radial2BCoeff",
                                     &fVSVaryingName, &fFSVaryingName);
@@ -569,12 +565,13 @@
             // r2Var = 2 * (r2Parm[2] * varCoord.x - r2Param[3])
             vertexBuilder->vsCodeAppendf("\t%s = 2.0 *(%s * %s.x - %s);\n",
                                            fVSVaryingName, p2.c_str(),
-                                           vsCoordsVarying.c_str(), p3.c_str());
+                                           coords[0].getVSName().c_str(), p3.c_str());
         }
     }
 
     // FS
     {
+        SkString coords2D = builder->ensureFSCoords2D(coords, 0);
         SkString cName("c");
         SkString ac4Name("ac4");
         SkString rootName("root");
@@ -600,14 +597,14 @@
         } else {
             bVar = "b";
             builder->fsCodeAppendf("\tfloat %s = 2.0 * (%s * %s.x - %s);\n",
-                                   bVar.c_str(), p2.c_str(), fsCoords.c_str(), p3.c_str());
+                                   bVar.c_str(), p2.c_str(), coords2D.c_str(), p3.c_str());
         }
 
         // c = (x^2)+(y^2) - params[4]
         builder->fsCodeAppendf("\tfloat %s = dot(%s, %s) - %s;\n",
                                cName.c_str(),
-                               fsCoords.c_str(),
-                               fsCoords.c_str(),
+                               coords2D.c_str(),
+                               coords2D.c_str(),
                                p4.c_str());
 
         // If we aren't degenerate, emit some extra code, and accept a slightly
diff --git a/gpu/GrAAConvexPathRenderer.cpp b/gpu/GrAAConvexPathRenderer.cpp
index 7063440..78f2045 100644
--- a/gpu/GrAAConvexPathRenderer.cpp
+++ b/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/gpu/GrAARectRenderer.cpp b/gpu/GrAARectRenderer.cpp
index e4bf853..320b3d7 100644
--- a/gpu/GrAARectRenderer.cpp
+++ b/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/gpu/GrClipMaskManager.cpp b/gpu/GrClipMaskManager.cpp
index 8465880..58b6a4b 100644
--- a/gpu/GrClipMaskManager.cpp
+++ b/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/gpu/GrEffect.cpp b/gpu/GrEffect.cpp
index 53dabb6..653a1da 100644
--- a/gpu/GrEffect.cpp
+++ b/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/gpu/GrOvalRenderer.cpp b/gpu/GrOvalRenderer.cpp
index 66ca053..2c923f8 100644
--- a/gpu/GrOvalRenderer.cpp
+++ b/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/gpu/GrSWMaskHelper.cpp b/gpu/GrSWMaskHelper.cpp
index 6b2a759..624b796 100644
--- a/gpu/GrSWMaskHelper.cpp
+++ b/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/gpu/effects/GrBezierEffect.cpp b/gpu/effects/GrBezierEffect.cpp
index b3e9f62..9adf592 100644
--- a/gpu/effects/GrBezierEffect.cpp
+++ b/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/gpu/effects/GrBicubicEffect.cpp b/gpu/effects/GrBicubicEffect.cpp
index cf20d15..ccbf788 100644
--- a/gpu/effects/GrBicubicEffect.cpp
+++ b/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/gpu/effects/GrBicubicEffect.h b/gpu/effects/GrBicubicEffect.h
index 0a40891..eabc79f 100644
--- a/gpu/effects/GrBicubicEffect.h
+++ b/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/gpu/effects/GrConfigConversionEffect.cpp b/gpu/effects/GrConfigConversionEffect.cpp
index 935e074..daf5141 100644
--- a/gpu/effects/GrConfigConversionEffect.cpp
+++ b/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/gpu/effects/GrConvolutionEffect.cpp b/gpu/effects/GrConvolutionEffect.cpp
index 19fed1c..4d16361 100644
--- a/gpu/effects/GrConvolutionEffect.cpp
+++ b/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/gpu/effects/GrCustomCoordsTextureEffect.cpp b/gpu/effects/GrCustomCoordsTextureEffect.cpp
index a5c28c5..b14de8b 100644
--- a/gpu/effects/GrCustomCoordsTextureEffect.cpp
+++ b/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/gpu/effects/GrSimpleTextureEffect.cpp b/gpu/effects/GrSimpleTextureEffect.cpp
index 0ee6f78..b27b737 100644
--- a/gpu/effects/GrSimpleTextureEffect.cpp
+++ b/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/gpu/effects/GrSimpleTextureEffect.h b/gpu/effects/GrSimpleTextureEffect.h
index c694197..c326ccf 100644
--- a/gpu/effects/GrSimpleTextureEffect.h
+++ b/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/gpu/effects/GrSingleTextureEffect.cpp b/gpu/effects/GrSingleTextureEffect.cpp
index 532ce04..3ab5e3a 100644
--- a/gpu/effects/GrSingleTextureEffect.cpp
+++ b/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/gpu/effects/GrSingleTextureEffect.h b/gpu/effects/GrSingleTextureEffect.h
index 27a7d79..a8f9a6d 100644
--- a/gpu/effects/GrSingleTextureEffect.h
+++ b/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/gpu/effects/GrTextureDomainEffect.cpp b/gpu/effects/GrTextureDomainEffect.cpp
index 59bd909..eca99d7 100644
--- a/gpu/effects/GrTextureDomainEffect.cpp
+++ b/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/gpu/effects/GrTextureDomainEffect.h b/gpu/effects/GrTextureDomainEffect.h
index d07f2fc..46ee2a6 100644
--- a/gpu/effects/GrTextureDomainEffect.h
+++ b/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/gpu/gl/GrGLCoordTransform.cpp b/gpu/gl/GrGLCoordTransform.cpp
new file mode 100644
index 0000000..bfbae1e
--- /dev/null
+++ b/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/gpu/gl/GrGLCoordTransform.h b/gpu/gl/GrGLCoordTransform.h
new file mode 100644
index 0000000..cd2f38a
--- /dev/null
+++ b/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/gpu/gl/GrGLEffect.cpp b/gpu/gl/GrGLEffect.cpp
index 28be7af..af351a4 100644
--- a/gpu/gl/GrGLEffect.cpp
+++ b/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/gpu/gl/GrGLEffect.h b/gpu/gl/GrGLEffect.h
index 5df2281..d3c49d1 100644
--- a/gpu/gl/GrGLEffect.h
+++ b/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/gpu/gl/GrGLEffectMatrix.cpp b/gpu/gl/GrGLEffectMatrix.cpp
deleted file mode 100644
index 880df12..0000000
--- a/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/gpu/gl/GrGLEffectMatrix.h b/gpu/gl/GrGLEffectMatrix.h
deleted file mode 100644
index c5ac5f0..0000000
--- a/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/gpu/gl/GrGLProgram.cpp b/gpu/gl/GrGLProgram.cpp
index 2f8e807..89f5f58 100644
--- a/gpu/gl/GrGLProgram.cpp
+++ b/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/gpu/gl/GrGLProgram.h b/gpu/gl/GrGLProgram.h
index 0304acf..4412309 100644
--- a/gpu/gl/GrGLProgram.h
+++ b/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/gpu/gl/GrGLShaderBuilder.cpp b/gpu/gl/GrGLShaderBuilder.cpp
index f4ffb4d..313ad86 100644
--- a/gpu/gl/GrGLShaderBuilder.cpp
+++ b/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/gpu/gl/GrGLShaderBuilder.h b/gpu/gl/GrGLShaderBuilder.h
index d73a731..a70a0b0 100644
--- a/gpu/gl/GrGLShaderBuilder.h
+++ b/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[]);