Checkpoint in moving texture matrices out of GrGLProgram and into GrGLEffect.
Review URL: https://codereview.appspot.com/6818064

git-svn-id: http://skia.googlecode.com/svn/trunk@6233 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/effects/SkBlendImageFilter.cpp b/src/effects/SkBlendImageFilter.cpp
index fb0e4d0..9ce5726 100644
--- a/src/effects/SkBlendImageFilter.cpp
+++ b/src/effects/SkBlendImageFilter.cpp
@@ -13,6 +13,7 @@
 #include "SkGr.h"
 #include "SkGrPixelRef.h"
 #include "gl/GrGLEffect.h"
+#include "gl/GrGLEffectMatrix.h"
 #include "GrTBackendEffectFactory.h"
 #endif
 
@@ -127,16 +128,19 @@
 
     static inline EffectKey GenKey(const GrEffectStage&, const GrGLCaps&);
 
+    virtual void setData(const GrGLUniformManager&, const GrEffectStage&);
+
 private:
     typedef GrGLEffect INHERITED;
     SkBlendImageFilter::Mode fMode;
+    GrGLEffectMatrix fEffectMatrix;
 };
 
 ///////////////////////////////////////////////////////////////////////////////
 
 class GrBlendEffect : public GrSingleTextureEffect {
 public:
-    GrBlendEffect(SkBlendImageFilter::Mode mode, GrTexture* foreground);
+    GrBlendEffect(SkBlendImageFilter::Mode mode, GrTexture* foreground, const SkMatrix&);
     virtual ~GrBlendEffect();
 
     virtual bool isEqual(const GrEffect&) const SK_OVERRIDE;
@@ -209,17 +213,19 @@
     foregroundTexMatrix.setIDiv(foreground->width(), foreground->height());
     GrPaint paint;
     paint.colorStage(0)->setEffect(
-        SkNEW_ARGS(GrSingleTextureEffect, (background.get())), backgroundTexMatrix)->unref();
+        SkNEW_ARGS(GrSingleTextureEffect, (background.get(), backgroundTexMatrix)))->unref();
     paint.colorStage(1)->setEffect(
-        SkNEW_ARGS(GrBlendEffect, (fMode, foreground.get())), foregroundTexMatrix)->unref();
+        SkNEW_ARGS(GrBlendEffect, (fMode, foreground.get(), foregroundTexMatrix)))->unref();
     context->drawRect(paint, rect);
     return dst;
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 
-GrBlendEffect::GrBlendEffect(SkBlendImageFilter::Mode mode, GrTexture* foreground)
-    : INHERITED(foreground), fMode(mode) {
+GrBlendEffect::GrBlendEffect(SkBlendImageFilter::Mode mode,
+                             GrTexture* foreground,
+                             const SkMatrix& matrix)
+    : INHERITED(foreground, matrix), fMode(mode) {
 }
 
 GrBlendEffect::~GrBlendEffect() {
@@ -227,8 +233,7 @@
 
 bool GrBlendEffect::isEqual(const GrEffect& sBase) const {
     const GrBlendEffect& s = static_cast<const GrBlendEffect&>(sBase);
-    return INHERITED::isEqual(sBase) &&
-           fMode == s.fMode;
+    return INHERITED::isEqual(sBase) && fMode == s.fMode;
 }
 
 const GrBackendEffectFactory& GrBlendEffect::getFactory() const {
@@ -241,6 +246,7 @@
                                  const GrEffect& effect)
     : INHERITED(factory),
       fMode(static_cast<const GrBlendEffect&>(effect).mode()) {
+    fRequiresTextureMatrix = false;
 }
 
 GrGLBlendEffect::~GrGLBlendEffect() {
@@ -248,16 +254,19 @@
 
 void GrGLBlendEffect::emitCode(GrGLShaderBuilder* builder,
                                const GrEffectStage&,
-                               EffectKey,
+                               EffectKey key,
                                const char* vertexCoords,
                                const char* outputColor,
                                const char* inputColor,
                                const TextureSamplerArray& samplers) {
+    const char* coords;
+    GrSLType coordsType =  fEffectMatrix.emitCode(builder, key, vertexCoords, &coords);
+
     SkString* code = &builder->fFSCode;
     const char* bgColor = inputColor;
     const char* fgColor = "fgColor";
     code->appendf("\t\tvec4 %s = ", fgColor);
-    builder->appendTextureLookup(code, samplers[0]);
+    builder->appendTextureLookup(code, samplers[0], coords, coordsType);
     code->append(";\n");
     code->appendf("\t\t%s.a = 1.0 - (1.0 - %s.a) * (1.0 - %s.b);\n", outputColor, bgColor, fgColor);
     switch (fMode) {
@@ -279,7 +288,16 @@
     }
 }
 
-GrGLEffect::EffectKey GrGLBlendEffect::GenKey(const GrEffectStage& s, const GrGLCaps&) {
-    return static_cast<const GrBlendEffect&>(*s.getEffect()).mode();
+void GrGLBlendEffect::setData(const GrGLUniformManager& uman, const GrEffectStage& stage) {
+    const GrBlendEffect& blend = static_cast<const GrBlendEffect&>(*stage.getEffect());
+    fEffectMatrix.setData(uman, blend.getMatrix(), stage.getCoordChangeMatrix(), blend.texture(0));
+}
+
+GrGLEffect::EffectKey GrGLBlendEffect::GenKey(const GrEffectStage& stage, const GrGLCaps&) {
+    const GrBlendEffect& blend = static_cast<const GrBlendEffect&>(*stage.getEffect());
+    EffectKey key = 
+        GrGLEffectMatrix::GenKey(blend.getMatrix(), stage.getCoordChangeMatrix(), blend.texture(0));
+    key |= (blend.mode() << GrGLEffectMatrix::kKeyBits);
+    return key;
 }
 #endif
diff --git a/src/effects/SkColorMatrixFilter.cpp b/src/effects/SkColorMatrixFilter.cpp
index 58d2673..fa724ca 100644
--- a/src/effects/SkColorMatrixFilter.cpp
+++ b/src/effects/SkColorMatrixFilter.cpp
@@ -350,6 +350,8 @@
         : INHERITED(factory)
         , fMatrixHandle(GrGLUniformManager::kInvalidUniformHandle)
         , fVectorHandle(GrGLUniformManager::kInvalidUniformHandle) {
+            // no texture
+            fRequiresTextureMatrix = false;
         }
 
         virtual void emitCode(GrGLShaderBuilder* builder,
diff --git a/src/effects/gradients/SkGradientShader.cpp b/src/effects/gradients/SkGradientShader.cpp
index 60eff91..78c7e34 100644
--- a/src/effects/gradients/SkGradientShader.cpp
+++ b/src/effects/gradients/SkGradientShader.cpp
@@ -678,7 +678,9 @@
 GrGLGradientEffect::GrGLGradientEffect(const GrBackendEffectFactory& factory)
     : INHERITED(factory)
     , fCachedYCoord(GR_ScalarMax)
-    , fFSYUni(GrGLUniformManager::kInvalidUniformHandle) { }
+    , fFSYUni(GrGLUniformManager::kInvalidUniformHandle) {
+    fRequiresTextureMatrix = false;
+}
 
 GrGLGradientEffect::~GrGLGradientEffect() { }
 
@@ -688,13 +690,37 @@
 }
 
 void GrGLGradientEffect::setData(const GrGLUniformManager& uman, const GrEffectStage& stage) {
-    GrScalar yCoord = static_cast<const GrGradientEffect&>(*stage.getEffect()).getYCoord();
+    const GrGradientEffect& e = static_cast<const GrGradientEffect&>(*stage.getEffect());
+    const GrTexture* texture = e.texture(0);
+    fEffectMatrix.setData(uman, e.getMatrix(), stage.getCoordChangeMatrix(), texture);
+
+    GrScalar yCoord = e.getYCoord();
     if (yCoord != fCachedYCoord) {
         uman.set1f(fFSYUni, yCoord);
         fCachedYCoord = yCoord;
     }
 }
 
+GrGLEffect::EffectKey GrGLGradientEffect::GenMatrixKey(const GrEffectStage& s) {
+    const GrGradientEffect& e = static_cast<const GrGradientEffect&>(*s.getEffect());
+    const GrTexture* texture = e.texture(0);
+    return GrGLEffectMatrix::GenKey(e.getMatrix(), s.getCoordChangeMatrix(), texture); 
+}
+
+void GrGLGradientEffect::setupMatrix(GrGLShaderBuilder* builder,
+                                     EffectKey key,
+                                     const char* vertexCoords,
+                                     const char** fsCoordName,
+                                     const char** vsVaryingName,
+                                     GrSLType* vsVaryingType) {
+    fEffectMatrix.emitCodeMakeFSCoords2D(builder,
+                                         key & kMatrixKeyMask,
+                                         vertexCoords,
+                                         fsCoordName,
+                                         vsVaryingName,
+                                         vsVaryingType);
+}
+
 void GrGLGradientEffect::emitColorLookup(GrGLShaderBuilder* builder,
                                          const char* gradientTValue,
                                          const char* outputColor,
@@ -714,6 +740,7 @@
 
 GrGradientEffect::GrGradientEffect(GrContext* ctx,
                                    const SkGradientShaderBase& shader,
+                                   const SkMatrix& matrix,
                                    SkShader::TileMode tileMode)
     : INHERITED(1) {
     // TODO: check for simple cases where we don't need a texture:
@@ -721,6 +748,8 @@
     //shader.asAGradient(&info);
     //if (info.fColorCount == 2) { ...
 
+    fMatrix = matrix;
+
     SkBitmap bitmap;
     shader.getGradientTableBitmap(&bitmap);
 
diff --git a/src/effects/gradients/SkGradientShaderPriv.h b/src/effects/gradients/SkGradientShaderPriv.h
index 9f14e7f..2437911 100644
--- a/src/effects/gradients/SkGradientShaderPriv.h
+++ b/src/effects/gradients/SkGradientShaderPriv.h
@@ -193,12 +193,13 @@
 #if SK_SUPPORT_GPU
 
 #include "gl/GrGLEffect.h"
+#include "gl/GrGLEffectMatrix.h"
 
 class GrEffectStage;
 class GrBackendEffectFactory;
 
 /*
- * The intepretation of the texture matrix depends on the sample mode. The
+ * The interpretation of the texture matrix depends on the sample mode. The
  * texture matrix is applied both when the texture coordinates are explicit
  * and  when vertex positions are used as texture  coordinates. In the latter
  * case the texture matrix is applied to the pre-view-matrix position
@@ -228,6 +229,7 @@
 
     GrGradientEffect(GrContext* ctx,
                      const SkGradientShaderBase& shader,
+                     const SkMatrix& matrix,
                      SkShader::TileMode tileMode);
 
     virtual ~GrGradientEffect();
@@ -236,11 +238,12 @@
 
     bool useAtlas() const { return SkToBool(-1 != fRow); }
     GrScalar getYCoord() const { return fYCoord; };
+    const SkMatrix& getMatrix() const { return fMatrix;}
 
     virtual bool isEqual(const GrEffect& effect) const SK_OVERRIDE {
         const GrGradientEffect& s = static_cast<const GrGradientEffect&>(effect);
         return INHERITED::isEqual(effect) && this->useAtlas() == s.useAtlas() &&
-               fYCoord == s.getYCoord();
+               fYCoord == s.getYCoord() && fMatrix.cheapEqualTo(s.getMatrix());
     }
 
 protected:
@@ -263,6 +266,7 @@
     GrScalar fYCoord;
     GrTextureStripAtlas* fAtlas;
     int fRow;
+    SkMatrix fMatrix;
 
     typedef GrEffect INHERITED;
 
@@ -279,6 +283,36 @@
     virtual void setData(const GrGLUniformManager&, const GrEffectStage&) SK_OVERRIDE;
 
 protected:
+    /**
+     * Subclasses must reserve the lower kMatrixKeyBitCnt of their key for use by
+     * GrGLGradientEffect.
+     */
+    enum {
+        kMatrixKeyBitCnt = GrGLEffectMatrix::kKeyBits,
+        kMatrixKeyMask = (1 << kMatrixKeyBitCnt) - 1,
+    };
+
+    /**
+     * Subclasses must call this. It will return a value restricted to the lower kMatrixKeyBitCnt
+     * bits.
+     */
+    static EffectKey GenMatrixKey(const GrEffectStage& s);
+
+    /**
+     * 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,
+                     const char* vertexCoords,
+                     const char** fsCoordName,
+                     const char** 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 emitYCoordUniform(GrGLShaderBuilder* builder);
@@ -295,6 +329,7 @@
 private:
     GrScalar fCachedYCoord;
     GrGLUniformManager::UniformHandle fFSYUni;
+    GrGLEffectMatrix fEffectMatrix;
 
     typedef GrGLEffect INHERITED;
 };
diff --git a/src/effects/gradients/SkLinearGradient.cpp b/src/effects/gradients/SkLinearGradient.cpp
index d957656..7161450 100644
--- a/src/effects/gradients/SkLinearGradient.cpp
+++ b/src/effects/gradients/SkLinearGradient.cpp
@@ -495,7 +495,9 @@
                           const char* inputColor,
                           const TextureSamplerArray&) SK_OVERRIDE;
 
-    static EffectKey GenKey(const GrEffectStage&, const GrGLCaps& caps) { return 0; }
+    static EffectKey GenKey(const GrEffectStage& stage, const GrGLCaps&) {
+        return GenMatrixKey(stage);
+    }
 
 private:
 
@@ -507,8 +509,11 @@
 class GrLinearGradient : public GrGradientEffect {
 public:
 
-    GrLinearGradient(GrContext* ctx, const SkLinearGradient& shader, SkShader::TileMode tm)
-        : INHERITED(ctx, shader, tm) { }
+    GrLinearGradient(GrContext* ctx,
+                     const SkLinearGradient& shader,
+                     const SkMatrix& matrix,
+                     SkShader::TileMode tm)
+        : INHERITED(ctx, shader, matrix, tm) { }
     virtual ~GrLinearGradient() { }
 
     static const char* Name() { return "Linear Gradient"; }
@@ -553,15 +558,18 @@
 /////////////////////////////////////////////////////////////////////
 
 void GrGLLinearGradient::emitCode(GrGLShaderBuilder* builder,
-                                  const GrEffectStage&,
-                                  EffectKey,
+                                  const GrEffectStage& stage,
+                                  EffectKey key,
                                   const char* vertexCoords,
                                   const char* outputColor,
                                   const char* inputColor,
                                   const TextureSamplerArray& samplers) {
     this->emitYCoordUniform(builder);
+    const char* coords;
+    this->setupMatrix(builder, key, vertexCoords, &coords);
     SkString t;
-    t.printf("%s.x", builder->defaultTexCoordsName());
+    t.append(coords);
+    t.append(".x");
     this->emitColorLookup(builder, t.c_str(), outputColor, inputColor, samplers[0]);
 }
 
@@ -574,7 +582,7 @@
         return false;
     }
     matrix.postConcat(fPtsToUnit);
-    stage->setEffect(SkNEW_ARGS(GrLinearGradient, (context, *this, fTileMode)), matrix)->unref();
+    stage->setEffect(SkNEW_ARGS(GrLinearGradient, (context, *this, matrix, fTileMode)))->unref();
     return true;
 }
 
diff --git a/src/effects/gradients/SkRadialGradient.cpp b/src/effects/gradients/SkRadialGradient.cpp
index a20ea35..02a56da 100644
--- a/src/effects/gradients/SkRadialGradient.cpp
+++ b/src/effects/gradients/SkRadialGradient.cpp
@@ -490,7 +490,9 @@
                           const char* inputColor,
                           const TextureSamplerArray&) SK_OVERRIDE;
 
-    static EffectKey GenKey(const GrEffectStage&, const GrGLCaps& caps) { return 0; }
+    static EffectKey GenKey(const GrEffectStage& stage, const GrGLCaps&) {
+        return GenMatrixKey(stage);
+    }
 
 private:
 
@@ -503,8 +505,11 @@
 class GrRadialGradient : public GrGradientEffect {
 public:
 
-    GrRadialGradient(GrContext* ctx, const SkRadialGradient& shader, SkShader::TileMode tm)
-        : INHERITED(ctx, shader, tm) {
+    GrRadialGradient(GrContext* ctx,
+                     const SkRadialGradient& shader,
+                     const SkMatrix& matrix,
+                     SkShader::TileMode tm)
+        : INHERITED(ctx, shader, matrix, tm) {
     }
 
     virtual ~GrRadialGradient() { }
@@ -551,15 +556,18 @@
 /////////////////////////////////////////////////////////////////////
 
 void GrGLRadialGradient::emitCode(GrGLShaderBuilder* builder,
-                                  const GrEffectStage&,
-                                  EffectKey,
+                                  const GrEffectStage& stage,
+                                  EffectKey key,
                                   const char* vertexCoords,
                                   const char* outputColor,
                                   const char* inputColor,
                                   const TextureSamplerArray& samplers) {
     this->emitYCoordUniform(builder);
-    SkString t;
-    t.printf("length(%s.xy)", builder->defaultTexCoordsName());
+    const char* coords;
+    this->setupMatrix(builder, key, vertexCoords, &coords);
+    SkString t("length(");
+    t.append(coords);
+    t.append(")");
     this->emitColorLookup(builder, t.c_str(), outputColor, inputColor, samplers[0]);
 }
 
@@ -573,7 +581,7 @@
         return false;
     }
     matrix.postConcat(fPtsToUnit);
-    stage->setEffect(SkNEW_ARGS(GrRadialGradient, (context, *this, fTileMode)), matrix)->unref();
+    stage->setEffect(SkNEW_ARGS(GrRadialGradient, (context, *this, matrix, fTileMode)))->unref();
     return true;
 }
 
diff --git a/src/effects/gradients/SkSweepGradient.cpp b/src/effects/gradients/SkSweepGradient.cpp
index a783e37..589cf4a 100644
--- a/src/effects/gradients/SkSweepGradient.cpp
+++ b/src/effects/gradients/SkSweepGradient.cpp
@@ -399,7 +399,9 @@
                           const char* inputColor,
                           const TextureSamplerArray&) SK_OVERRIDE;
 
-    static EffectKey GenKey(const GrEffectStage&, const GrGLCaps& caps) { return 0; }
+    static EffectKey GenKey(const GrEffectStage& stage, const GrGLCaps&) {
+        return GenMatrixKey(stage);
+    }
 
 private:
 
@@ -413,8 +415,9 @@
 public:
 
     GrSweepGradient(GrContext* ctx,
-                    const SkSweepGradient& shader)
-    : INHERITED(ctx, shader, SkShader::kClamp_TileMode) { }
+                    const SkSweepGradient& shader,
+                    const SkMatrix& matrix)
+    : INHERITED(ctx, shader, matrix, SkShader::kClamp_TileMode) { }
     virtual ~GrSweepGradient() { }
 
     static const char* Name() { return "Sweep Gradient"; }
@@ -457,16 +460,17 @@
 /////////////////////////////////////////////////////////////////////
 
 void GrGLSweepGradient::emitCode(GrGLShaderBuilder* builder,
-                                 const GrEffectStage&,
-                                 EffectKey,
+                                 const GrEffectStage& stage,
+                                 EffectKey key,
                                  const char* vertexCoords,
                                  const char* outputColor,
                                  const char* inputColor,
                                  const TextureSamplerArray& samplers) {
     this->emitYCoordUniform(builder);
+    const char* coords;
+    this->setupMatrix(builder, key, vertexCoords, &coords);
     SkString t;
-    t.printf("atan(- %s.y, - %s.x) * 0.1591549430918 + 0.5",
-        builder->defaultTexCoordsName(), builder->defaultTexCoordsName());
+    t.printf("atan(- %s.y, - %s.x) * 0.1591549430918 + 0.5", coords, coords);
     this->emitColorLookup(builder, t.c_str(), outputColor, inputColor, samplers[0]);
 }
 
@@ -478,7 +482,7 @@
         return false;
     }
     matrix.postConcat(fPtsToUnit);
-    stage->setEffect(SkNEW_ARGS(GrSweepGradient, (context, *this)), matrix)->unref();
+    stage->setEffect(SkNEW_ARGS(GrSweepGradient, (context, *this, matrix)))->unref();
     return true;
 }
 
diff --git a/src/effects/gradients/SkTwoPointConicalGradient.cpp b/src/effects/gradients/SkTwoPointConicalGradient.cpp
index f93f660..41292bb 100644
--- a/src/effects/gradients/SkTwoPointConicalGradient.cpp
+++ b/src/effects/gradients/SkTwoPointConicalGradient.cpp
@@ -371,8 +371,9 @@
 
     GrConical2Gradient(GrContext* ctx,
                        const SkTwoPointConicalGradient& shader,
+                       const SkMatrix& matrix,
                        SkShader::TileMode tm)
-        : INHERITED(ctx, shader, tm)
+        : INHERITED(ctx, shader, matrix, tm)
         , fCenterX1(shader.getCenterX1())
         , fRadius0(shader.getStartRadius())
         , fDiffRadius(shader.getDiffRadius()) { }
@@ -468,12 +469,17 @@
 }
 
 void GrGLConical2Gradient::emitCode(GrGLShaderBuilder* builder,
-                                    const GrEffectStage&,
-                                    EffectKey,
+                                    const GrEffectStage& stage,
+                                    EffectKey key,
                                     const char* vertexCoords,
                                     const char* outputColor,
                                     const char* inputColor,
                                     const TextureSamplerArray& samplers) {
+    const char* fsCoords;
+    const char* vsCoordsVarying;
+    GrSLType coordsVaryingType;
+    this->setupMatrix(builder, key, vertexCoords, &fsCoords, &vsCoordsVarying, &coordsVaryingType);
+
     this->emitYCoordUniform(builder);
     // 2 copies of uniform array, 1 for each of vertex & fragment shader,
     // to work around Xoom bug. Doesn't seem to cause performance decrease
@@ -485,7 +491,7 @@
 
     // For radial gradients without perspective we can pass the linear
     // part of the quadratic as a varying.
-    if (!builder->defaultTextureMatrixIsPerspective()) {
+    if (kVec2f_GrSLType == coordsVaryingType) {
         builder->addVarying(kFloat_GrSLType, "Conical2BCoeff",
                             &fVSVaryingName, &fFSVaryingName);
     }
@@ -502,11 +508,11 @@
 
         // For radial gradients without perspective we can pass the linear
         // part of the quadratic as a varying.
-        if (!builder->defaultTextureMatrixIsPerspective()) {
+        if (kVec2f_GrSLType == coordsVaryingType) {
             // r2Var = -2 * (r2Parm[2] * varCoord.x - r2Param[3] * r2Param[5])
             code->appendf("\t%s = -2.0 * (%s * %s.x + %s * %s);\n",
                           fVSVaryingName, p2.c_str(),
-                          vertexCoords, p3.c_str(), p5.c_str());
+                          vsCoordsVarying, p3.c_str(), p5.c_str());
         }
     }
 
@@ -538,12 +544,12 @@
         // If we we're able to interpolate the linear component,
         // bVar is the varying; otherwise compute it
         SkString bVar;
-        if (!builder->defaultTextureMatrixIsPerspective()) {
+        if (kVec2f_GrSLType == coordsVaryingType) {
             bVar = fFSVaryingName;
         } else {
             bVar = "b";
             code->appendf("\tfloat %s = -2.0 * (%s * %s.x + %s * %s);\n",
-                          bVar.c_str(), p2.c_str(), builder->defaultTexCoordsName(),
+                          bVar.c_str(), p2.c_str(), fsCoords,
                           p3.c_str(), p5.c_str());
         }
 
@@ -553,7 +559,7 @@
 
         // c = (x^2)+(y^2) - params[4]
         code->appendf("\tfloat %s = dot(%s, %s) - %s;\n", cName.c_str(),
-                      builder->defaultTexCoordsName(), builder->defaultTexCoordsName(),
+                      fsCoords, fsCoords,
                       p4.c_str());
 
         // Non-degenerate case (quadratic)
@@ -669,7 +675,15 @@
 }
 
 GrGLEffect::EffectKey GrGLConical2Gradient::GenKey(const GrEffectStage& s, const GrGLCaps&) {
-    return (static_cast<const GrConical2Gradient&>(*s.getEffect()).isDegenerate());
+    enum {
+        kIsDegenerate = 1 << kMatrixKeyBitCnt,
+    };
+
+    EffectKey key = GenMatrixKey(s);
+    if (static_cast<const GrConical2Gradient&>(*s.getEffect()).isDegenerate()) {
+        key |= kIsDegenerate;
+    }
+    return key;
 }
 
 /////////////////////////////////////////////////////////////////////
@@ -695,7 +709,7 @@
         matrix.postConcat(rot);
     }
 
-    stage->setEffect(SkNEW_ARGS(GrConical2Gradient, (context, *this, fTileMode)), matrix)->unref();
+    stage->setEffect(SkNEW_ARGS(GrConical2Gradient, (context, *this, matrix, fTileMode)))->unref();
 
     return true;
 }
diff --git a/src/effects/gradients/SkTwoPointRadialGradient.cpp b/src/effects/gradients/SkTwoPointRadialGradient.cpp
index 659bce0..d018629 100644
--- a/src/effects/gradients/SkTwoPointRadialGradient.cpp
+++ b/src/effects/gradients/SkTwoPointRadialGradient.cpp
@@ -404,8 +404,11 @@
 class GrRadial2Gradient : public GrGradientEffect {
 public:
 
-    GrRadial2Gradient(GrContext* ctx, const SkTwoPointRadialGradient& shader, SkShader::TileMode tm)
-        : INHERITED(ctx, shader, tm)
+    GrRadial2Gradient(GrContext* ctx,
+                      const SkTwoPointRadialGradient& shader,
+                      const SkMatrix& matrix,
+                      SkShader::TileMode tm)
+        : INHERITED(ctx, shader, matrix, tm)
         , fCenterX1(shader.getCenterX1())
         , fRadius0(shader.getStartRadius())
         , fPosRoot(shader.getDiffRadius() < 0) { }
@@ -501,14 +504,19 @@
 }
 
 void GrGLRadial2Gradient::emitCode(GrGLShaderBuilder* builder,
-                                   const GrEffectStage&,
-                                   EffectKey,
+                                   const GrEffectStage& stage,
+                                   EffectKey key,
                                    const char* vertexCoords,
                                    const char* outputColor,
                                    const char* inputColor,
                                    const TextureSamplerArray& samplers) {
 
     this->emitYCoordUniform(builder);
+    const char* fsCoords;
+    const char* vsCoordsVarying;
+    GrSLType coordsVaryingType;
+    this->setupMatrix(builder, key, vertexCoords, &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.
@@ -519,9 +527,8 @@
 
     // For radial gradients without perspective we can pass the linear
     // part of the quadratic as a varying.
-    if (!builder->defaultTextureMatrixIsPerspective()) {
-        builder->addVarying(kFloat_GrSLType, "Radial2BCoeff",
-                          &fVSVaryingName, &fFSVaryingName);
+    if (kVec2f_GrSLType == coordsVaryingType) {
+        builder->addVarying(kFloat_GrSLType, "Radial2BCoeff", &fVSVaryingName, &fFSVaryingName);
     }
 
     // VS
@@ -534,11 +541,11 @@
 
         // For radial gradients without perspective we can pass the linear
         // part of the quadratic as a varying.
-        if (!builder->defaultTextureMatrixIsPerspective()) {
+        if (kVec2f_GrSLType == coordsVaryingType) {
             // r2Var = 2 * (r2Parm[2] * varCoord.x - r2Param[3])
             code->appendf("\t%s = 2.0 *(%s * %s.x - %s);\n",
                           fVSVaryingName, p2.c_str(),
-                          vertexCoords, p3.c_str());
+                          vsCoordsVarying, p3.c_str());
         }
     }
 
@@ -565,20 +572,19 @@
         // If we we're able to interpolate the linear component,
         // bVar is the varying; otherwise compute it
         SkString bVar;
-        if (!builder->defaultTextureMatrixIsPerspective()) {
+        if (kVec2f_GrSLType == coordsVaryingType) {
             bVar = fFSVaryingName;
         } else {
             bVar = "b";
             code->appendf("\tfloat %s = 2.0 * (%s * %s.x - %s);\n",
-                          bVar.c_str(), p2.c_str(),
-                          builder->defaultTexCoordsName(), p3.c_str());
+                          bVar.c_str(), p2.c_str(), fsCoords, p3.c_str());
         }
 
         // c = (x^2)+(y^2) - params[4]
         code->appendf("\tfloat %s = dot(%s, %s) - %s;\n",
                       cName.c_str(),
-                      builder->defaultTexCoordsName(),
-                      builder->defaultTexCoordsName(),
+                      fsCoords,
+                      fsCoords,
                       p4.c_str());
 
         // If we aren't degenerate, emit some extra code, and accept a slightly
@@ -643,7 +649,15 @@
 }
 
 GrGLEffect::EffectKey GrGLRadial2Gradient::GenKey(const GrEffectStage& s, const GrGLCaps&) {
-    return (static_cast<const GrRadial2Gradient&>(*s.getEffect()).isDegenerate());
+    enum {
+        kIsDegenerate = 1 << kMatrixKeyBitCnt,
+    };
+
+    EffectKey key = GenMatrixKey(s);
+    if (static_cast<const GrRadial2Gradient&>(*s.getEffect()).isDegenerate()) {
+        key |= kIsDegenerate;
+    }
+    return key;
 }
 
 /////////////////////////////////////////////////////////////////////
@@ -667,7 +681,7 @@
         matrix.postConcat(rot);
     }
 
-    stage->setEffect(SkNEW_ARGS(GrRadial2Gradient, (context, *this, fTileMode)), matrix)->unref();
+    stage->setEffect(SkNEW_ARGS(GrRadial2Gradient, (context, *this, matrix, fTileMode)))->unref();
     return true;
 }
 
diff --git a/src/gpu/GrContext.cpp b/src/gpu/GrContext.cpp
index f7f6b0c..b9b2a82 100644
--- a/src/gpu/GrContext.cpp
+++ b/src/gpu/GrContext.cpp
@@ -1808,7 +1808,7 @@
                              i < scaleFactorY ? 0.5f : 1.0f);
 
         paint.colorStage(0)->setEffect(SkNEW_ARGS(GrSingleTextureEffect,
-                                                  (srcTexture, true)), matrix)->unref();
+                                                  (srcTexture, matrix, true)))->unref();
         this->drawRectToRect(paint, dstRect, srcRect);
         srcRect = dstRect;
         srcTexture = dstTexture;
@@ -1865,8 +1865,8 @@
         // FIXME:  This should be mitchell, not bilinear.
         matrix.setIDiv(srcTexture->width(), srcTexture->height());
         this->setRenderTarget(dstTexture->asRenderTarget());
-        paint.colorStage(0)->setEffect(SkNEW_ARGS(GrSingleTextureEffect,(srcTexture, true)),
-                                       matrix)->unref();
+        paint.colorStage(0)->setEffect(SkNEW_ARGS(GrSingleTextureEffect,(srcTexture,
+                                                                         matrix, true)))->unref();
         SkRect dstRect(srcRect);
         scale_rect(&dstRect, (float) scaleFactorX, (float) scaleFactorY);
         this->drawRectToRect(paint, dstRect, srcRect);
diff --git a/src/gpu/GrDrawState.h b/src/gpu/GrDrawState.h
index 3c6c3a3..b539441 100644
--- a/src/gpu/GrDrawState.h
+++ b/src/gpu/GrDrawState.h
@@ -198,16 +198,16 @@
     }
     void createTextureEffect(int stageIdx, GrTexture* texture, const GrMatrix& matrix) {
         GrAssert(!this->getStage(stageIdx).getEffect());
-        GrEffect* effect = SkNEW_ARGS(GrSingleTextureEffect, (texture));
-        this->stage(stageIdx)->setEffect(effect, matrix)->unref();
+        GrEffect* effect = SkNEW_ARGS(GrSingleTextureEffect, (texture, matrix));
+        this->stage(stageIdx)->setEffect(effect)->unref();
     }
     void createTextureEffect(int stageIdx,
                              GrTexture* texture,
                              const GrMatrix& matrix,
                              const GrTextureParams& params) {
         GrAssert(!this->getStage(stageIdx).getEffect());
-        GrEffect* effect = SkNEW_ARGS(GrSingleTextureEffect, (texture, params));
-        this->stage(stageIdx)->setEffect(effect, matrix)->unref();
+        GrEffect* effect = SkNEW_ARGS(GrSingleTextureEffect, (texture, matrix, params));
+        this->stage(stageIdx)->setEffect(effect)->unref();
     }
 
 
diff --git a/src/gpu/GrEffect.cpp b/src/gpu/GrEffect.cpp
index 09032bf..d470c9c 100644
--- a/src/gpu/GrEffect.cpp
+++ b/src/gpu/GrEffect.cpp
@@ -20,6 +20,26 @@
 }
 #endif
 
+namespace GrEffectUnitTest {
+const SkMatrix& TestMatrix(SkRandom* random) {
+    static SkMatrix gMatrices[5];
+    static bool gOnce;
+    if (!gOnce) {
+        gMatrices[0].reset();
+        gMatrices[1].setTranslate(SkIntToScalar(-100), SkIntToScalar(100));
+        gMatrices[2].setRotate(SkIntToScalar(17));
+        gMatrices[3].setRotate(SkIntToScalar(185));
+        gMatrices[3].postTranslate(SkIntToScalar(66), SkIntToScalar(-33));
+        gMatrices[3].postScale(SkIntToScalar(2), SK_ScalarHalf);
+        gMatrices[4].setRotate(SkIntToScalar(215));
+        gMatrices[4].set(SkMatrix::kMPersp0, SkFloatToScalar(0.00013f));
+        gMatrices[4].set(SkMatrix::kMPersp1, SkFloatToScalar(-0.000039f));
+        gOnce = true;
+    }
+    return gMatrices[random->nextULessThan(static_cast<uint32_t>(SK_ARRAY_COUNT(gMatrices)))];
+}
+}
+
 class GrEffect_Globals {
 public:
     static GrMemoryPool* GetTLS() {
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index cdccbcd..946d54d 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -608,7 +608,7 @@
         GrScalar sy = SkFloatToScalar(1.f / bitmap.height());
         matrix.postScale(sx, sy);
     }
-    stage->setEffect(SkNEW_ARGS(GrSingleTextureEffect, (texture, params)), matrix)->unref();
+    stage->setEffect(SkNEW_ARGS(GrSingleTextureEffect, (texture, matrix, params)))->unref();
 
     return true;
 }
@@ -875,7 +875,7 @@
             matrix.setIDiv(pathTexture->width(), pathTexture->height());
             // Blend pathTexture over blurTexture.
             context->setRenderTarget(blurTexture->asRenderTarget());
-            paint.colorStage(0)->setEffect(SkNEW_ARGS(GrSingleTextureEffect, (pathTexture)), matrix)->unref();
+            paint.colorStage(0)->setEffect(SkNEW_ARGS(GrSingleTextureEffect, (pathTexture, matrix)))->unref();
             if (SkMaskFilter::kInner_BlurType == blurType) {
                 // inner:  dst = dst * src
                 paint.setBlendFunc(kDC_GrBlendCoeff, kZero_GrBlendCoeff);
@@ -906,7 +906,7 @@
     matrix.postIDiv(blurTexture->width(), blurTexture->height());
 
     grp->coverageStage(MASK_IDX)->reset();
-    grp->coverageStage(MASK_IDX)->setEffect(SkNEW_ARGS(GrSingleTextureEffect, (blurTexture)), matrix)->unref();
+    grp->coverageStage(MASK_IDX)->setEffect(SkNEW_ARGS(GrSingleTextureEffect, (blurTexture, matrix)))->unref();
     context->drawRect(*grp, finalRect);
     return true;
 }
@@ -962,7 +962,7 @@
     m.setTranslate(-dstM.fBounds.fLeft*SK_Scalar1, -dstM.fBounds.fTop*SK_Scalar1);
     m.postIDiv(texture->width(), texture->height());
 
-    grp->coverageStage(MASK_IDX)->setEffect(SkNEW_ARGS(GrSingleTextureEffect, (texture)), m)->unref();
+    grp->coverageStage(MASK_IDX)->setEffect(SkNEW_ARGS(GrSingleTextureEffect, (texture, m)))->unref();
     GrRect d;
     d.setLTRB(GrIntToScalar(dstM.fBounds.fLeft),
               GrIntToScalar(dstM.fBounds.fTop),
diff --git a/src/gpu/effects/GrSingleTextureEffect.cpp b/src/gpu/effects/GrSingleTextureEffect.cpp
index 8866153..0f3b614 100644
--- a/src/gpu/effects/GrSingleTextureEffect.cpp
+++ b/src/gpu/effects/GrSingleTextureEffect.cpp
@@ -7,6 +7,7 @@
 
 #include "effects/GrSingleTextureEffect.h"
 #include "gl/GrGLEffect.h"
+#include "gl/GrGLEffectMatrix.h"
 #include "gl/GrGLSL.h"
 #include "gl/GrGLTexture.h"
 #include "GrTBackendEffectFactory.h"
@@ -16,25 +17,43 @@
 public:
     GrGLSingleTextureEffect(const GrBackendEffectFactory& factory, const GrEffect&)
     : INHERITED (factory) {
+        fRequiresTextureMatrix = false;
     }
 
     virtual void emitCode(GrGLShaderBuilder* builder,
                           const GrEffectStage&,
-                          EffectKey,
+                          EffectKey key,
                           const char* vertexCoords,
                           const char* outputColor,
                           const char* inputColor,
                           const TextureSamplerArray& samplers) SK_OVERRIDE {
-
+        const char* coordName;
+        GrSLType coordType = fEffectMatrix.emitCode(builder, key, vertexCoords, &coordName);
         builder->fFSCode.appendf("\t%s = ", outputColor);
-        builder->appendTextureLookupAndModulate(&builder->fFSCode, inputColor, samplers[0]);
+        builder->appendTextureLookupAndModulate(&builder->fFSCode,
+                                                inputColor,
+                                                samplers[0],
+                                                coordName,
+                                                coordType);
         builder->fFSCode.append(";\n");
     }
 
-    static inline EffectKey GenKey(const GrEffectStage&, const GrGLCaps&) { return 0; }
+    static inline EffectKey GenKey(const GrEffectStage& stage, const GrGLCaps&) {
+        const GrSingleTextureEffect& ste =
+            static_cast<const GrSingleTextureEffect&>(*stage.getEffect());
+        return GrGLEffectMatrix::GenKey(ste.getMatrix(),
+                                        stage.getCoordChangeMatrix(),
+                                        ste.texture(0));
+    }
+
+    virtual void setData(const GrGLUniformManager& uman, const GrEffectStage& stage) SK_OVERRIDE {
+        const GrSingleTextureEffect& ste =
+            static_cast<const GrSingleTextureEffect&>(*stage.getEffect());
+        fEffectMatrix.setData(uman, ste.getMatrix(), stage.getCoordChangeMatrix(), ste.texture(0));
+    }
 
 private:
-
+    GrGLEffectMatrix fEffectMatrix;
     typedef GrGLEffect INHERITED;
 };
 
@@ -43,16 +62,39 @@
 GrSingleTextureEffect::GrSingleTextureEffect(GrTexture* texture)
     : INHERITED(1)
     , fTextureAccess(texture) {
+    fMatrix.reset();
 }
 
 GrSingleTextureEffect::GrSingleTextureEffect(GrTexture* texture, bool bilerp)
     : INHERITED(1)
     , fTextureAccess(texture, bilerp) {
+    fMatrix.reset();
 }
 
 GrSingleTextureEffect::GrSingleTextureEffect(GrTexture* texture, const GrTextureParams& params)
     : INHERITED(1)
     , fTextureAccess(texture, params) {
+    fMatrix.reset();
+}
+
+GrSingleTextureEffect::GrSingleTextureEffect(GrTexture* texture, const GrMatrix& m)
+    : INHERITED(1)
+    , fTextureAccess(texture)
+    , fMatrix(m) {
+}
+
+GrSingleTextureEffect::GrSingleTextureEffect(GrTexture* texture, const GrMatrix& m, bool bilerp)
+    : INHERITED(1)
+    , fTextureAccess(texture, bilerp)
+    , fMatrix(m) {
+}
+
+GrSingleTextureEffect::GrSingleTextureEffect(GrTexture* texture,
+                                             const GrMatrix& m,
+                                             const GrTextureParams& params)
+    : INHERITED(1)
+    , fTextureAccess(texture, params)
+    , fMatrix(m) {
 }
 
 GrSingleTextureEffect::~GrSingleTextureEffect() {
@@ -76,5 +118,6 @@
                                             GrTexture* textures[]) {
     int texIdx = random->nextBool() ? GrEffectUnitTest::kSkiaPMTextureIdx :
                                       GrEffectUnitTest::kAlphaTextureIdx;
-    return SkNEW_ARGS(GrSingleTextureEffect, (textures[texIdx]));
+    const SkMatrix& matrix = GrEffectUnitTest::TestMatrix(random);
+    return SkNEW_ARGS(GrSingleTextureEffect, (textures[texIdx], matrix));
 }
diff --git a/src/gpu/effects/GrSingleTextureEffect.h b/src/gpu/effects/GrSingleTextureEffect.h
index 346a6f4..23b416d 100644
--- a/src/gpu/effects/GrSingleTextureEffect.h
+++ b/src/gpu/effects/GrSingleTextureEffect.h
@@ -9,6 +9,7 @@
 #define GrSingleTextureEffect_DEFINED
 
 #include "GrEffect.h"
+#include "GrMatrix.h"
 
 class GrGLSingleTextureEffect;
 
@@ -18,28 +19,37 @@
 class GrSingleTextureEffect : public GrEffect {
 
 public:
-    /** Uses default texture params (unfiltered, clamp) */
-    GrSingleTextureEffect(GrTexture* texture);
-
-    /** Uses default tile mode (clamp) */
-    GrSingleTextureEffect(GrTexture* texture, bool bilerp);
-
+    /** These three constructors assume an identity matrix */
+    GrSingleTextureEffect(GrTexture* texture); /* unfiltered, clamp mode */
+    GrSingleTextureEffect(GrTexture* texture, bool bilerp); /* clamp mode */
     GrSingleTextureEffect(GrTexture* texture, const GrTextureParams&);
 
+    /** These three constructors take an explicit matrix */
+    GrSingleTextureEffect(GrTexture*, const GrMatrix&); /* unfiltered, clamp mode */
+    GrSingleTextureEffect(GrTexture*, const GrMatrix&, bool bilerp); /* clamp mode */
+    GrSingleTextureEffect(GrTexture*, const GrMatrix&, const GrTextureParams&);
+
     virtual ~GrSingleTextureEffect();
 
     virtual const GrTextureAccess& textureAccess(int index) const SK_OVERRIDE;
 
     static const char* Name() { return "Single Texture"; }
 
+    const GrMatrix& getMatrix() const { return fMatrix; }
+
     typedef GrGLSingleTextureEffect GLEffect;
 
     virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
 
+    virtual bool isEqual(const GrEffect& effect) const SK_OVERRIDE {
+        const GrSingleTextureEffect& ste = static_cast<const GrSingleTextureEffect&>(effect);
+        return INHERITED::isEqual(effect) && fMatrix.cheapEqualTo(ste.getMatrix());
+    }
 private:
     GR_DECLARE_EFFECT_TEST;
 
     GrTextureAccess fTextureAccess;
+    GrMatrix        fMatrix;
 
     typedef GrEffect INHERITED;
 };
diff --git a/src/gpu/gl/GrGLEffect.cpp b/src/gpu/gl/GrGLEffect.cpp
index 0bbf1f7..5e0875b 100644
--- a/src/gpu/gl/GrGLEffect.cpp
+++ b/src/gpu/gl/GrGLEffect.cpp
@@ -10,6 +10,8 @@
 
 GrGLEffect::GrGLEffect(const GrBackendEffectFactory& factory)
     : fFactory(factory) {
+
+    fRequiresTextureMatrix = true;
 }
 
 GrGLEffect::~GrGLEffect() {
diff --git a/src/gpu/gl/GrGLEffect.h b/src/gpu/gl/GrGLEffect.h
index 30b8455..0fd5722 100644
--- a/src/gpu/gl/GrGLEffect.h
+++ b/src/gpu/gl/GrGLEffect.h
@@ -51,7 +51,9 @@
 
         @param builder      Interface used to emit code in the shaders.
         @param stage        The effect stage that generated this program stage.
-        @param key          The key that was computed by EffectKey() from the generating GrEffect.
+        @param key          The key that was computed by GenKey() from the generating GrEffect.
+                            Only the bits indicated by GrBackendEffectFactory::kEffectKeyBits are
+                            guaranteed to match the value produced by GenKey();
         @param vertexCoords A vec2 of texture coordinates in the VS, which may be altered. This will
                             be removed soon and stages will be responsible for computing their own
                             coords.
@@ -85,7 +87,14 @@
 
     static EffectKey GenTextureKey(const GrEffect&, const GrGLCaps&);
 
+    bool requiresTextureMatrix() const { return fRequiresTextureMatrix; }
+
+
 protected:
+    // HACK: This is a temporary field that allows GrGLEffect subclasses to opt into the new
+    // shader gen where a texture matrix is not automatically inserted. It defaults to true and is
+    // set to false in a subclass to opt into the new behavior.
+    bool fRequiresTextureMatrix;
 
     const GrBackendEffectFactory& fFactory;
 };
diff --git a/src/gpu/gl/GrGLEffectMatrix.cpp b/src/gpu/gl/GrGLEffectMatrix.cpp
new file mode 100644
index 0000000..0db87f9
--- /dev/null
+++ b/src/gpu/gl/GrGLEffectMatrix.cpp
@@ -0,0 +1,213 @@
+/*
+ * 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 "GrTexture.h"
+
+GrGLEffect::EffectKey GrGLEffectMatrix::GenKey(const SkMatrix& effectMatrix,
+                                               const SkMatrix& coordChangeMatrix,
+                                               const GrTexture* texture) {
+    SkMatrix::TypeMask type0 = effectMatrix.getType();
+    SkMatrix::TypeMask type1 = coordChangeMatrix.getType();
+
+    static const int kNonTransMask = SkMatrix::kAffine_Mask |
+                                     SkMatrix::kScale_Mask  |
+                                     SkMatrix::kPerspective_Mask;
+    int combinedTypes = type0 | type1;
+
+    bool reverseY = (NULL != texture) && GrSurface::kBottomLeft_Origin == texture->origin();
+
+    if (SkMatrix::kPerspective_Mask & combinedTypes) {
+        return kGeneral_Key;
+    } else if ((kNonTransMask & combinedTypes) || reverseY) {
+        return kNoPersp_Key;
+    } else if (kTrans_Key & combinedTypes) {
+        return kTrans_Key;
+    } else {
+        GrAssert(effectMatrix.isIdentity() && coordChangeMatrix.isIdentity());
+        return kIdentity_Key;
+    }
+}
+
+GrSLType GrGLEffectMatrix::emitCode(GrGLShaderBuilder* builder,
+                                    EffectKey key,
+                                    const char* vertexCoords,
+                                    const char** fsCoordName,
+                                    const char** vsCoordName,
+                                    const char* suffix) {
+    GrSLType varyingType;
+    const char* uniName;
+    key &= kKeyMask;
+    switch (key) {
+        case kIdentity_Key:
+            fUniType = kVoid_GrSLType;
+            varyingType = kVec2f_GrSLType;
+            break;
+        case kTrans_Key:
+            fUniType = kVec2f_GrSLType;
+            uniName = "StageTranslate";
+            varyingType = kVec2f_GrSLType;
+            break;
+        case kNoPersp_Key:
+            fUniType = kMat33f_GrSLType;
+            uniName = "StageMatrix";
+            varyingType = kVec2f_GrSLType;
+            break;
+        case kGeneral_Key:
+            fUniType = kMat33f_GrSLType;
+            uniName = "StageMatrix";
+            varyingType = kVec3f_GrSLType;
+            break;
+        default:
+            GrCrash("Unexpected key.");
+    }
+    SkString suffixedUniName;
+    if (NULL != suffix) {
+        suffixedUniName.append(uniName);
+        suffixedUniName.append(suffix);
+        uniName = suffixedUniName.c_str();
+    }
+    if (kVoid_GrSLType != fUniType) {
+        fUni = builder->addUniform(GrGLShaderBuilder::kVertex_ShaderType,
+                                   fUniType,
+                                   uniName,
+                                   &uniName);
+    }
+
+    const char* varyingName = "StageCoord";
+    SkString suffixedVaryingName;
+    if (NULL != suffix) {
+        suffixedVaryingName.append(varyingName);
+        suffixedVaryingName.append(suffix);
+        varyingName = suffixedVaryingName.c_str();
+    }
+    const char* vsVaryingName;
+    const char* fsVaryingName;
+    builder->addVarying(varyingType, varyingName, &vsVaryingName, &fsVaryingName);
+
+    // varying = matrix * vertex-coords (logically)
+    switch (fUniType) {
+        case kVoid_GrSLType:
+            GrAssert(kVec2f_GrSLType == varyingType);
+            builder->fVSCode.appendf("\t%s = %s;\n", vsVaryingName, vertexCoords);
+            break;
+        case kVec2f_GrSLType:
+            GrAssert(kVec2f_GrSLType == varyingType);
+            builder->fVSCode.appendf("\t%s = %s + %s;\n", vsVaryingName, uniName, vertexCoords);
+            break;
+        case kMat33f_GrSLType: {
+            GrAssert(kVec2f_GrSLType == varyingType || kVec3f_GrSLType == varyingType);
+            if (kVec2f_GrSLType == varyingType) {
+                builder->fVSCode.appendf("\t%s = (%s * vec3(%s, 1)).xy;\n",
+                                         vsVaryingName, uniName, vertexCoords);
+            } else {
+                builder->fVSCode.appendf("\t%s = %s * vec3(%s, 1);\n",
+                                         vsVaryingName, uniName, vertexCoords);
+            }
+            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,
+                                              const char* vertexCoords,
+                                              const char** fsCoordName,
+                                              const char** vsVaryingName,
+                                              GrSLType* vsVaryingType,
+                                              const char* suffix) {
+    const char* fsVaryingName;
+        
+    GrSLType varyingType = this->emitCode(builder,
+                                          key,
+                                          vertexCoords,
+                                          &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->fFSCode.appendf("\tvec2 %s = %s.xy / %s.z;",
+                                    coordName, fsVaryingName, fsVaryingName);
+        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 SkMatrix& coordChangeMatrix,
+                              const GrTexture* texture) {
+    GrAssert((GrGLUniformManager::kInvalidUniformHandle == fUni) ==
+                (kVoid_GrSLType == fUniType));
+    switch (fUniType) {
+        case kVoid_GrSLType:
+            GrAssert(matrix.isIdentity());
+            GrAssert(coordChangeMatrix.isIdentity());
+            GrAssert(NULL == texture || GrSurface::kTopLeft_Origin == texture->origin());
+            return;
+        case kVec2f_GrSLType: {
+            GrAssert(SkMatrix::kTranslate_Mask == (matrix.getType() | coordChangeMatrix.getType()));
+            GrAssert(NULL == texture || GrSurface::kTopLeft_Origin == 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 && GrSurface::kBottomLeft_Origin == texture->origin()) {
+                // combined.postScale(1,-1);
+                // combined.postTranslate(0,1);
+                combined.set(SkMatrix::kMSkewY,
+                    combined[SkMatrix::kMPersp0] - combined[SkMatrix::kMSkewY]);
+                combined.set(SkMatrix::kMScaleY,
+                    combined[SkMatrix::kMPersp1] - combined[SkMatrix::kMScaleY]);
+                combined.set(SkMatrix::kMTransY,
+                    combined[SkMatrix::kMPersp2] - combined[SkMatrix::kMTransY]);
+            }
+            if (!fPrevMatrix.cheapEqualTo(combined)) {
+                uniformManager.setSkMatrix(fUni, combined);
+                fPrevMatrix = combined;
+            }
+            break;
+        }
+        default:
+            GrCrash("Unexpected uniform type.");
+    }
+}
diff --git a/src/gpu/gl/GrGLEffectMatrix.h b/src/gpu/gl/GrGLEffectMatrix.h
new file mode 100644
index 0000000..9e45f3e
--- /dev/null
+++ b/src/gpu/gl/GrGLEffectMatrix.h
@@ -0,0 +1,97 @@
+/*
+ * 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;
+class SkRandom;
+
+/**
+ * This is a helper to implement a texture matrix in a GrGLEffect.
+ */
+class GrGLEffectMatrix {
+public:
+    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 = 2,
+        kKeyMask = (1 << kKeyBits) - 1,
+    };
+
+    GrGLEffectMatrix() : fUni(GrGLUniformManager::kInvalidUniformHandle) {
+        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 lookups, or if the GrGLEffect subclass
+     * wants to handle origin adjustments in some other manner. coordChangeMatrix is the matrix
+     * from GrEffectStage.
+     */
+    static EffectKey GenKey(const SkMatrix& effectMatrix,
+                            const SkMatrix& coordChangeMatrix,
+                            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 GrGLEffect.
+     */
+    GrSLType emitCode(GrGLShaderBuilder*,
+                      EffectKey,
+                      const char* vertexCoords,
+                      const char** fsCoordName, /* optional */
+                      const char** 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,
+                                const char* vertexCoords,
+                                const char** fsCoordName, /* optional */
+                                const char** vsVaryingName = NULL,
+                                GrSLType* vsVaryingType = NULL,
+                                const char* suffix = NULL);
+    /**
+     * Call from a GrGLEffect's subclass to update the texture matrix. The matrix,
+     * coordChangeMatrix, and texture params should match those used with GenKey.
+     */
+    void setData(const GrGLUniformManager& uniformManager,
+                 const SkMatrix& effectMatrix,
+                 const SkMatrix& coordChangeMatrix,
+                 const GrTexture*);
+
+private:
+    enum {
+        kIdentity_Key   = 0,
+        kTrans_Key      = 1,
+        kNoPersp_Key    = 2,
+        kGeneral_Key    = 3,
+    };
+
+    GrGLUniformManager::UniformHandle fUni;
+    GrSLType                          fUniType;
+    SkMatrix                          fPrevMatrix;
+};
+
+#endif
\ No newline at end of file
diff --git a/src/gpu/gl/GrGLProgram.cpp b/src/gpu/gl/GrGLProgram.cpp
index cd16d9e..120bd1e 100644
--- a/src/gpu/gl/GrGLProgram.cpp
+++ b/src/gpu/gl/GrGLProgram.cpp
@@ -909,57 +909,64 @@
 
     /// Vertex Shader Stuff
 
-    // decide whether we need a matrix to transform texture coords and whether the varying needs a
-    // perspective coord.
-    const char* matName = NULL;
-    GrSLType texCoordVaryingType;
-    if (desc.fOptFlags & StageDesc::kIdentityMatrix_OptFlagBit) {
-        texCoordVaryingType = kVec2f_GrSLType;
-    } else {
-        uniforms->fTextureMatrixUni = builder->addUniform(GrGLShaderBuilder::kVertex_ShaderType,
-                                                         kMat33f_GrSLType, "TexM", &matName);
-        builder->getUniformVariable(uniforms->fTextureMatrixUni);
+    const char* vertexCoords;
 
-        if (desc.fOptFlags & StageDesc::kNoPerspective_OptFlagBit) {
+    // Has the effect not yet been updated to insert its own texture matrix if necessary.
+    if (glEffect->requiresTextureMatrix()) {
+        // Decide whether we need a matrix to transform texture coords and whether the varying needs
+        // a perspective coord.
+        const char* matName = NULL;
+        GrSLType texCoordVaryingType;
+        if (desc.fOptFlags & StageDesc::kIdentityMatrix_OptFlagBit) {
             texCoordVaryingType = kVec2f_GrSLType;
         } else {
-            texCoordVaryingType = kVec3f_GrSLType;
-        }
-    }
-    const char *varyingVSName, *varyingFSName;
-    builder->addVarying(texCoordVaryingType,
-                        "Stage",
-                        &varyingVSName,
-                        &varyingFSName);
-    builder->setupTextureAccess(varyingFSName, texCoordVaryingType);
+            uniforms->fTextureMatrixUni = builder->addUniform(GrGLShaderBuilder::kVertex_ShaderType,
+                                                              kMat33f_GrSLType, "TexM", &matName);
+            builder->getUniformVariable(uniforms->fTextureMatrixUni);
 
+            if (desc.fOptFlags & StageDesc::kNoPerspective_OptFlagBit) {
+                texCoordVaryingType = kVec2f_GrSLType;
+            } else {
+                texCoordVaryingType = kVec3f_GrSLType;
+            }
+        }
+        const char *varyingVSName, *varyingFSName;
+        builder->addVarying(texCoordVaryingType,
+                            "Stage",
+                            &varyingVSName,
+                            &varyingFSName);
+        builder->setupTextureAccess(varyingFSName, texCoordVaryingType);
+
+        if (!matName) {
+            GrAssert(kVec2f_GrSLType == texCoordVaryingType);
+            builder->fVSCode.appendf("\t%s = %s;\n", varyingVSName, vsInCoord);
+        } else {
+            // varying = texMatrix * texCoord
+            builder->fVSCode.appendf("\t%s = (%s * vec3(%s, 1))%s;\n",
+                                     varyingVSName, matName, vsInCoord,
+                                     vector_all_coords(GrSLTypeToVecLength(texCoordVaryingType)));
+        }
+        vertexCoords = varyingVSName;
+    } else {
+        vertexCoords = vsInCoord;
+    }
+
+    // setup texture samplers for gl effect
     int numTextures = effect->numTextures();
     SkSTArray<8, GrGLShaderBuilder::TextureSampler> textureSamplers;
-
     textureSamplers.push_back_n(numTextures);
-
     for (int i = 0; i < numTextures; ++i) {
         textureSamplers[i].init(builder, &effect->textureAccess(i));
         uniforms->fSamplerUniforms.push_back(textureSamplers[i].fSamplerUniform);
     }
 
-    if (!matName) {
-        GrAssert(kVec2f_GrSLType == texCoordVaryingType);
-        builder->fVSCode.appendf("\t%s = %s;\n", varyingVSName, vsInCoord);
-    } else {
-        // varying = texMatrix * texCoord
-        builder->fVSCode.appendf("\t%s = (%s * vec3(%s, 1))%s;\n",
-                                  varyingVSName, matName, vsInCoord,
-                                  vector_all_coords(GrSLTypeToVecLength(texCoordVaryingType)));
-    }
-
     // Enclose custom code in a block to avoid namespace conflicts
     builder->fVSCode.appendf("\t{ // %s\n", glEffect->name());
     builder->fFSCode.appendf("\t{ // %s \n", glEffect->name());
     glEffect->emitCode(builder,
                        stage,
                        desc.fEffectKey,
-                       varyingVSName,
+                       vertexCoords,
                        fsOutColor,
                        fsInColor,
                        textureSamplers);
diff --git a/src/gpu/gl/GrGLUniformManager.cpp b/src/gpu/gl/GrGLUniformManager.cpp
index 684ef8c..7a92d25 100644
--- a/src/gpu/gl/GrGLUniformManager.cpp
+++ b/src/gpu/gl/GrGLUniformManager.cpp
@@ -8,6 +8,7 @@
 #include "gl/GrGLShaderBuilder.h"
 #include "gl/GrGLProgram.h"
 #include "gl/GrGLUniformHandle.h"
+#include "SkMatrix.h"
 
 #define ASSERT_ARRAY_UPLOAD_IN_BOUNDS(UNI, OFFSET, COUNT) \
          GrAssert(offset + arrayCount <= uni.fArrayCount || \
@@ -231,6 +232,23 @@
     }
 }
 
+void GrGLUniformManager::setSkMatrix(UniformHandle u, const SkMatrix& matrix) const {
+    GR_STATIC_ASSERT(SK_SCALAR_IS_FLOAT);
+    GrGLfloat mt[] = {
+        matrix.get(SkMatrix::kMScaleX),
+        matrix.get(SkMatrix::kMSkewY),
+        matrix.get(SkMatrix::kMPersp0),
+        matrix.get(SkMatrix::kMSkewX),
+        matrix.get(SkMatrix::kMScaleY),
+        matrix.get(SkMatrix::kMPersp1),
+        matrix.get(SkMatrix::kMTransX),
+        matrix.get(SkMatrix::kMTransY),
+        matrix.get(SkMatrix::kMPersp2),
+    };
+    this->setMatrix3f(u, mt);
+}
+
+
 void GrGLUniformManager::getUniformLocations(GrGLuint programID, const BuilderUniformArray& uniforms) {
     GrAssert(uniforms.count() == fUniforms.count());
     int count = fUniforms.count();
diff --git a/src/gpu/gl/GrGLUniformManager.h b/src/gpu/gl/GrGLUniformManager.h
index e9856c6..8f435d3 100644
--- a/src/gpu/gl/GrGLUniformManager.h
+++ b/src/gpu/gl/GrGLUniformManager.h
@@ -15,6 +15,7 @@
 #include "SkTArray.h"
 
 class GrGLContextInfo;
+class SkMatrix;
 
 /** Manages a program's uniforms.
 */
@@ -47,6 +48,9 @@
     void setMatrix3fv(UniformHandle, int offset, int arrayCount, const GrGLfloat matrices[]) const;
     void setMatrix4fv(UniformHandle, int offset, int arrayCount, const GrGLfloat matrices[]) const;
 
+    // convenience method for uploading a SkMatrix to a 3x3 matrix uniform
+    void setSkMatrix(UniformHandle, const SkMatrix&) const;
+
     struct BuilderUniform {
         GrGLShaderVar fVariable;
         uint32_t      fVisibility;