This CL cleans up the last remaining users of localCoordChange on paint

NOTREECHECKS=True
BUG=skia:

Review URL: https://codereview.chromium.org/817853002
diff --git a/expectations/gm/ignored-tests.txt b/expectations/gm/ignored-tests.txt
index 6574096..fe87f05 100644
--- a/expectations/gm/ignored-tests.txt
+++ b/expectations/gm/ignored-tests.txt
@@ -33,6 +33,9 @@
 ## epoger will rebaseline by 25 Dec 2013
 #gradtext
 
+#joshualitt
+gpusamplerstress
+
 # robertphillips - skia:2995
 blurrects
 
diff --git a/gm/convexpolyeffect.cpp b/gm/convexpolyeffect.cpp
index 03b9ad2..65a1816 100644
--- a/gm/convexpolyeffect.cpp
+++ b/gm/convexpolyeffect.cpp
@@ -136,7 +136,6 @@
                 SkAutoTUnref<const GrGeometryProcessor> gp(
                         GrDefaultGeoProcFactory::Create(0xff000000));
                 ds.addCoverageProcessor(fp);
-                ds.setIdentityViewMatrix();
                 ds.setRenderTarget(rt);
 
                 GrDrawTarget::AutoReleaseGeometry geo(tt.target(), 4, gp->getVertexStride(), 0);
@@ -193,7 +192,6 @@
                 SkAutoTUnref<const GrGeometryProcessor> gp(
                         GrDefaultGeoProcFactory::Create(0xff000000));
                 ds.addCoverageProcessor(fp);
-                ds.setIdentityViewMatrix();
                 ds.setRenderTarget(rt);
 
                 GrDrawTarget::AutoReleaseGeometry geo(tt.target(), 4, gp->getVertexStride(), 0);
diff --git a/gm/rrects.cpp b/gm/rrects.cpp
index 6c672b9..bacc819 100644
--- a/gm/rrects.cpp
+++ b/gm/rrects.cpp
@@ -121,7 +121,6 @@
                                                                                    rrect));
                         if (fp) {
                             drawState.addCoverageProcessor(fp);
-                            drawState.setIdentityViewMatrix();
                             drawState.setRenderTarget(rt);
 
                             SkRect bounds = rrect.getBounds();
diff --git a/include/gpu/GrPaint.h b/include/gpu/GrPaint.h
index d60b47f..3c131a6 100644
--- a/include/gpu/GrPaint.h
+++ b/include/gpu/GrPaint.h
@@ -147,32 +147,6 @@
      */
     bool isOpaqueAndConstantColor(GrColor* constantColor) const;
 
-    /**
-     * DO NOT USE THESE
-     * TODO Remove remaining use cases and delete these
-     */
-    bool localCoordChangeInverse(const SkMatrix& newToOld) {
-        SkMatrix oldToNew;
-        bool computed = false;
-        for (int i = 0; i < fColorStages.count(); ++i) {
-            if (!computed && !newToOld.invert(&oldToNew)) {
-                return false;
-            } else {
-                computed = true;
-            }
-            fColorStages[i].localCoordChange(oldToNew);
-        }
-        for (int i = 0; i < fCoverageStages.count(); ++i) {
-            if (!computed && !newToOld.invert(&oldToNew)) {
-                return false;
-            } else {
-                computed = true;
-            }
-            fCoverageStages[i].localCoordChange(oldToNew);
-        }
-        return true;
-    }
-
 private:
     friend class GrContext; // To access above two functions
     friend class GrStencilAndCoverTextContext;  // To access above two functions
diff --git a/src/effects/SkDisplacementMapEffect.cpp b/src/effects/SkDisplacementMapEffect.cpp
index 2c7f5ee..d18d3c3 100644
--- a/src/effects/SkDisplacementMapEffect.cpp
+++ b/src/effects/SkDisplacementMapEffect.cpp
@@ -564,9 +564,9 @@
     // Unpremultiply the displacement
     fsBuilder->codeAppendf("\t\t%s.rgb = (%s.a < %s) ? vec3(0.0) : clamp(%s.rgb / %s.a, 0.0, 1.0);",
                            dColor, dColor, nearZero, dColor, dColor);
-
+    SkString coords2D = fsBuilder->ensureFSCoords2D(coords, 1);
     fsBuilder->codeAppendf("\t\tvec2 %s = %s + %s*(%s.",
-                           cCoords, coords[1].c_str(), scaleUni, dColor);
+                           cCoords, coords2D.c_str(), scaleUni, dColor);
 
     switch (fXChannelSelector) {
       case SkDisplacementMapEffect::kR_ChannelSelectorType:
@@ -611,7 +611,9 @@
         "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);
     fsBuilder->codeAppendf("%s = %s ? vec4(0.0) : ", outputColor, outOfBounds);
-    fsBuilder->appendTextureLookup(samplers[1], cCoords, coords[1].getType());
+
+    // cCoords is always a vec2f
+    fsBuilder->appendTextureLookup(samplers[1], cCoords, kVec2f_GrSLType);
     fsBuilder->codeAppend(";\n");
 }
 
diff --git a/src/gpu/GrAAConvexPathRenderer.cpp b/src/gpu/GrAAConvexPathRenderer.cpp
index 311010f..6d69823 100644
--- a/src/gpu/GrAAConvexPathRenderer.cpp
+++ b/src/gpu/GrAAConvexPathRenderer.cpp
@@ -575,7 +575,8 @@
                                   const GrGLCaps&,
                                   GrProcessorKeyBuilder* b) {
             const BatchTracker& local = bt.cast<BatchTracker>();
-            b->add32(local.fInputColorType);
+            b->add32((local.fInputColorType << 16) |
+                     (local.fUsesLocalCoords && gp.localMatrix().hasPerspective() ? 0x1 : 0x0));
         }
 
         virtual void setData(const GrGLProgramDataManager& pdman,
diff --git a/src/gpu/GrBitmapTextContext.cpp b/src/gpu/GrBitmapTextContext.cpp
index 732a514..790590d 100755
--- a/src/gpu/GrBitmapTextContext.cpp
+++ b/src/gpu/GrBitmapTextContext.cpp
@@ -154,7 +154,8 @@
     SkFixed fx = SkScalarToFixed(x) + halfSampleX;
     SkFixed fy = SkScalarToFixed(y) + halfSampleY;
 
-    if (!fPaint.localCoordChangeInverse(viewMatrix)) {
+    // if we have RGB, then we won't have any SkShaders so no need to use a localmatrix
+    if (kARGB_GrMaskFormat != fCurrMaskFormat && !viewMatrix.invert(&fLocalMatrix)) {
         SkDebugf("Cannot invert viewmatrix\n");
     }
 
@@ -202,7 +203,9 @@
 
     // store original matrix before we reset, so we can use it to transform positions
     SkMatrix ctm = viewMatrix;
-    if (!fPaint.localCoordChangeInverse(viewMatrix)) {
+
+    // if we have RGB, then we won't have any SkShaders so no need to use a localmatrix
+    if (kARGB_GrMaskFormat != fCurrMaskFormat && !viewMatrix.invert(&fLocalMatrix)) {
             SkDebugf("Cannot invert viewmatrix\n");
     }
 
@@ -559,7 +562,6 @@
         }
 
         GrTextureParams params(SkShader::kRepeat_TileMode, GrTextureParams::kNone_FilterMode);
-        // TODO cache these GPs
         if (kARGB_GrMaskFormat == fCurrMaskFormat) {
             uint32_t textureUniqueID = fCurrTexture->getUniqueID();
             if (textureUniqueID != fEffectTextureUniqueID ||
@@ -569,19 +571,22 @@
                 fCachedTextureProcessor.reset(GrSimpleTextureEffect::Create(fCurrTexture,
                                                                             SkMatrix::I(),
                                                                             params));
+                fEffectTextureUniqueID = textureUniqueID;
             }
             drawState.addColorProcessor(fCachedTextureProcessor.get());
         } else {
             uint32_t textureUniqueID = fCurrTexture->getUniqueID();
             if (textureUniqueID != fEffectTextureUniqueID ||
-                fCachedGeometryProcessor->color() != color) {
+                fCachedGeometryProcessor->color() != color ||
+                !fCachedGeometryProcessor->localMatrix().cheapEqualTo(fLocalMatrix)) {
                 bool hasColor = kA8_GrMaskFormat == fCurrMaskFormat;
                 bool opaqueVertexColors = GrColorIsOpaque(fPaint.getColor());
                 fCachedGeometryProcessor.reset(GrBitmapTextGeoProc::Create(color,
                                                                            fCurrTexture,
                                                                            params,
                                                                            hasColor,
-                                                                           opaqueVertexColors));
+                                                                           opaqueVertexColors,
+                                                                           fLocalMatrix));
                 fEffectTextureUniqueID = textureUniqueID;
             }
         }
diff --git a/src/gpu/GrBitmapTextContext.h b/src/gpu/GrBitmapTextContext.h
index ad21a2f..f1e6b03 100644
--- a/src/gpu/GrBitmapTextContext.h
+++ b/src/gpu/GrBitmapTextContext.h
@@ -35,6 +35,7 @@
     SkAutoTUnref<const GrFragmentProcessor> fCachedTextureProcessor;
     // Used to check whether fCachedEffect is still valid.
     uint32_t                          fEffectTextureUniqueID;
+    SkMatrix                          fLocalMatrix;
 
     GrBitmapTextContext(GrContext*, const SkDeviceProperties&);
 
diff --git a/src/gpu/GrClipMaskManager.cpp b/src/gpu/GrClipMaskManager.cpp
index 5e2c0aa..e56f4dd 100644
--- a/src/gpu/GrClipMaskManager.cpp
+++ b/src/gpu/GrClipMaskManager.cpp
@@ -425,8 +425,6 @@
                                   SkRegion::Op op,
                                   const SkIRect& dstBound,
                                   const SkIRect& srcBound) {
-    SkAssertResult(drawState->setIdentityViewMatrix());
-
     drawState->setRenderTarget(dstMask->asRenderTarget());
 
     // We want to invert the coverage here
@@ -606,11 +604,11 @@
                 return NULL;
             }
 
-            GrDrawState backgroundDrawState(translate);
-            backgroundDrawState.enableState(GrDrawState::kClip_StateBit);
-            backgroundDrawState.setRenderTarget(result->asRenderTarget());
-
             if (useTemp) {
+                GrDrawState backgroundDrawState;
+                backgroundDrawState.enableState(GrDrawState::kClip_StateBit);
+                backgroundDrawState.setRenderTarget(result->asRenderTarget());
+
                 // Now draw into the accumulator using the real operation and the temp buffer as a
                 // texture
                 this->mergeMask(&backgroundDrawState,
@@ -620,6 +618,10 @@
                                 maskSpaceIBounds,
                                 maskSpaceElementIBounds);
             } else {
+                GrDrawState backgroundDrawState(translate);
+                backgroundDrawState.enableState(GrDrawState::kClip_StateBit);
+                backgroundDrawState.setRenderTarget(result->asRenderTarget());
+
                 set_coverage_drawing_xpf(op, !invert, &backgroundDrawState);
                 // Draw to the exterior pixels (those with a zero stencil value).
                 GR_STATIC_CONST_SAME_STENCIL(kDrawOutsideElement,
diff --git a/src/gpu/GrContext.cpp b/src/gpu/GrContext.cpp
index c637101..2818aa9 100755
--- a/src/gpu/GrContext.cpp
+++ b/src/gpu/GrContext.cpp
@@ -555,29 +555,42 @@
               SkIntToScalar(getRenderTarget()->width()),
               SkIntToScalar(getRenderTarget()->height()));
     SkTCopyOnFirstWrite<GrPaint> paint(origPaint);
-    GR_CREATE_TRACE_MARKER_CONTEXT("GrContext::drawPaint", this);
+
+    // by definition this fills the entire clip, no need for AA
+    if (paint->isAntiAlias()) {
+        paint.writable()->setAntiAlias(false);
+    }
+
+    bool isPerspective = viewMatrix.hasPerspective();
 
     // We attempt to map r by the inverse matrix and draw that. mapRect will
     // map the four corners and bound them with a new rect. This will not
     // produce a correct result for some perspective matrices.
-    if (!viewMatrix.hasPerspective()) {
+    if (!isPerspective) {
         SkMatrix inverse;
         if (!viewMatrix.invert(&inverse)) {
             SkDebugf("Could not invert matrix\n");
             return;
         }
         inverse.mapRect(&r);
+        this->drawRect(*paint, viewMatrix, r);
     } else {
-        if (!paint.writable()->localCoordChangeInverse(viewMatrix)) {
+        SkMatrix localMatrix;
+        if (!viewMatrix.invert(&localMatrix)) {
             SkDebugf("Could not invert matrix\n");
             return;
         }
+
+        AutoCheckFlush acf(this);
+        GrDrawState drawState;
+        GrDrawTarget* target = this->prepareToDraw(&drawState, paint, &SkMatrix::I(), &acf);
+        if (NULL == target) {
+            return;
+        }
+
+        GR_CREATE_TRACE_MARKER("GrContext::drawPaintWithPerspective", target);
+        target->drawRect(&drawState, paint->getColor(), r, NULL, &localMatrix);
     }
-    // by definition this fills the entire clip, no need for AA
-    if (paint->isAntiAlias()) {
-        paint.writable()->setAntiAlias(false);
-    }
-    this->drawRect(*paint, viewMatrix.hasPerspective() ? SkMatrix::I() : viewMatrix, r);
 }
 
 #ifdef SK_DEVELOPER
diff --git a/src/gpu/GrDefaultGeoProcFactory.cpp b/src/gpu/GrDefaultGeoProcFactory.cpp
index a74565c..7f557b5 100644
--- a/src/gpu/GrDefaultGeoProcFactory.cpp
+++ b/src/gpu/GrDefaultGeoProcFactory.cpp
@@ -22,8 +22,9 @@
 class DefaultGeoProc : public GrGeometryProcessor {
 public:
     static GrGeometryProcessor* Create(GrColor color, uint8_t coverage, uint32_t gpTypeFlags,
-                                       bool opaqueVertexColors) {
-        return SkNEW_ARGS(DefaultGeoProc, (color, coverage, gpTypeFlags, opaqueVertexColors));
+                                       bool opaqueVertexColors, const SkMatrix& localMatrix) {
+        return SkNEW_ARGS(DefaultGeoProc, (color, coverage, gpTypeFlags, opaqueVertexColors,
+                                           localMatrix));
     }
 
     virtual const char* name() const SK_OVERRIDE { return "DefaultGeometryProcessor"; }
@@ -122,10 +123,11 @@
                                   const GrGLCaps&,
                                   GrProcessorKeyBuilder* b) {
             const DefaultGeoProc& def = gp.cast<DefaultGeoProc>();
-            b->add32(def.fFlags);
-
             const BatchTracker& local = bt.cast<BatchTracker>();
-            b->add32(local.fInputColorType | local.fInputCoverageType << 16);
+            uint32_t key = def.fFlags;
+            key |= local.fInputColorType << 8 | local.fInputCoverageType << 16;
+            key |= local.fUsesLocalCoords && gp.localMatrix().hasPerspective() ? 0x1 << 24 : 0x0;
+            b->add32(key);
         }
 
         virtual void setData(const GrGLProgramDataManager& pdman,
@@ -164,8 +166,9 @@
     }
 
 private:
-    DefaultGeoProc(GrColor color, uint8_t coverage, uint32_t gpTypeFlags, bool opaqueVertexColors)
-        : INHERITED(color, opaqueVertexColors)
+    DefaultGeoProc(GrColor color, uint8_t coverage, uint32_t gpTypeFlags, bool opaqueVertexColors,
+                   const SkMatrix& localMatrix)
+        : INHERITED(color, opaqueVertexColors, localMatrix)
         , fInPosition(NULL)
         , fInColor(NULL)
         , fInLocalCoords(NULL)
@@ -244,12 +247,14 @@
     }
 
     return DefaultGeoProc::Create(GrRandomColor(random), GrRandomCoverage(random),
-                                  flags, random->nextBool());
+                                  flags, random->nextBool(),
+                                  GrProcessorUnitTest::TestMatrix(random));
 }
 
 const GrGeometryProcessor* GrDefaultGeoProcFactory::Create(GrColor color,
                                                            uint32_t gpTypeFlags,
                                                            bool opaqueVertexColors,
-                                                           uint8_t coverage) {
-    return DefaultGeoProc::Create(color, coverage, gpTypeFlags, opaqueVertexColors);
+                                                           uint8_t coverage,
+                                                           const SkMatrix& localMatrix) {
+    return DefaultGeoProc::Create(color, coverage, gpTypeFlags, opaqueVertexColors, localMatrix);
 }
diff --git a/src/gpu/GrDefaultGeoProcFactory.h b/src/gpu/GrDefaultGeoProcFactory.h
index f13ff85..b7db74d 100644
--- a/src/gpu/GrDefaultGeoProcFactory.h
+++ b/src/gpu/GrDefaultGeoProcFactory.h
@@ -83,7 +83,15 @@
     static const GrGeometryProcessor* Create(GrColor,
                                              uint32_t gpTypeFlags = 0,
                                              bool opaqueVertexColors = false,
-                                             uint8_t coverage = 0xff);
+                                             uint8_t coverage = 0xff,
+                                             const SkMatrix& localMatrix = SkMatrix::I());
+
+    static const GrGeometryProcessor* Create(GrColor color,
+                                             uint32_t gpTypeFlags,
+                                             const SkMatrix& localMatrix) {
+        return Create(color, gpTypeFlags, false, 0xff, localMatrix);
+    }
+
     static size_t DefaultVertexStride() { return sizeof(PositionAttr); }
 };
 
diff --git a/src/gpu/GrDrawState.cpp b/src/gpu/GrDrawState.cpp
index 68cd01c..643253e 100644
--- a/src/gpu/GrDrawState.cpp
+++ b/src/gpu/GrDrawState.cpp
@@ -112,24 +112,6 @@
     fCoveragePrimProc = NULL;
 }
 
-bool GrDrawState::setIdentityViewMatrix()  {
-    if (this->numFragmentStages()) {
-        SkMatrix invVM;
-        if (!fViewMatrix.invert(&invVM)) {
-            // sad trombone sound
-            return false;
-        }
-        for (int s = 0; s < this->numColorStages(); ++s) {
-            fColorStages[s].localCoordChange(invVM);
-        }
-        for (int s = 0; s < this->numCoverageStages(); ++s) {
-            fCoverageStages[s].localCoordChange(invVM);
-        }
-    }
-    fViewMatrix.reset();
-    return true;
-}
-
 void GrDrawState::setFromPaint(const GrPaint& paint, const SkMatrix& vm, GrRenderTarget* rt) {
     SkASSERT(0 == fBlockEffectRemovalCnt || 0 == this->numFragmentStages());
 
diff --git a/src/gpu/GrDrawState.h b/src/gpu/GrDrawState.h
index 24f987d..36e2b3a 100644
--- a/src/gpu/GrDrawState.h
+++ b/src/gpu/GrDrawState.h
@@ -303,12 +303,6 @@
         return false;
     }
 
-    /**
-     * Sets the view matrix to identity and updates any installed effects to compensate for the
-     * coord system change.
-     */
-    bool setIdentityViewMatrix();
-
     ////////////////////////////////////////////////////////////////////////////
 
     /**
diff --git a/src/gpu/GrDrawTarget.h b/src/gpu/GrDrawTarget.h
index 64cbc87..d2c20a1 100644
--- a/src/gpu/GrDrawTarget.h
+++ b/src/gpu/GrDrawTarget.h
@@ -303,10 +303,10 @@
      * @param rect        the rect to draw
      * @param localRect   optional rect that specifies local coords to map onto
      *                    rect. If NULL then rect serves as the local coords.
-     * @param localMatrix optional matrix applied to localRect. If
-     *                    srcRect is non-NULL and srcMatrix is non-NULL
-     *                    then srcRect will be transformed by srcMatrix.
-     *                    srcMatrix can be NULL when no srcMatrix is desired.
+     * @param localMatrix Optional local matrix. The local coordinates are specified by localRect,
+     *                    or if it is NULL by rect. This matrix applies to the coordinate implied by
+     *                    that rectangle before it is input to GrCoordTransforms that read local
+     *                    coordinates
      */
     void drawRect(GrDrawState* ds,
                   GrColor color,
diff --git a/src/gpu/GrInOrderDrawBuffer.cpp b/src/gpu/GrInOrderDrawBuffer.cpp
index dc074b7..6e5c1fe 100644
--- a/src/gpu/GrInOrderDrawBuffer.cpp
+++ b/src/gpu/GrInOrderDrawBuffer.cpp
@@ -62,13 +62,18 @@
 
     The vertex attrib order is always pos, color, [local coords].
  */
-static const GrGeometryProcessor* create_rect_gp(GrDrawState* drawState,
-                                                 bool hasLocalCoords,
-                                                 GrColor color) {
+static const GrGeometryProcessor* create_rect_gp(bool hasExplicitLocalCoords,
+                                                 GrColor color,
+                                                 const SkMatrix* localMatrix) {
     uint32_t flags = GrDefaultGeoProcFactory::kPosition_GPType |
                      GrDefaultGeoProcFactory::kColor_GPType;
-    flags |= hasLocalCoords ? GrDefaultGeoProcFactory::kLocalCoord_GPType : 0;
-    return GrDefaultGeoProcFactory::Create(color, flags, GrColorIsOpaque(color));
+    flags |= hasExplicitLocalCoords ? GrDefaultGeoProcFactory::kLocalCoord_GPType : 0;
+    if (localMatrix) {
+        return GrDefaultGeoProcFactory::Create(color, flags, GrColorIsOpaque(color), 0xff,
+                                               *localMatrix);
+    } else {
+        return GrDefaultGeoProcFactory::Create(color, flags, GrColorIsOpaque(color));
+    }
 }
 
 static bool path_fill_type_is_winding(const GrStencilSettings& pathStencilSettings) {
@@ -115,7 +120,11 @@
                                      const SkMatrix* localMatrix) {
     GrDrawState::AutoRestoreEffects are(ds);
 
-    SkAutoTUnref<const GrGeometryProcessor> gp(create_rect_gp(ds, SkToBool(localRect), color));
+    bool hasExplicitLocalCoords = SkToBool(localRect);
+    SkAutoTUnref<const GrGeometryProcessor> gp(
+            create_rect_gp(hasExplicitLocalCoords,
+                           color,
+                           hasExplicitLocalCoords ? NULL : localMatrix));
 
     size_t vstride = gp->getVertexStride();
     SkASSERT(vstride == sizeof(SkPoint) + sizeof(GrColor) + (SkToBool(localRect) ? sizeof(SkPoint) :
diff --git a/src/gpu/GrOvalRenderer.cpp b/src/gpu/GrOvalRenderer.cpp
index 304d6d6..05838cf 100644
--- a/src/gpu/GrOvalRenderer.cpp
+++ b/src/gpu/GrOvalRenderer.cpp
@@ -123,7 +123,9 @@
                            GrProcessorKeyBuilder* b) {
             const BatchTracker& local = bt.cast<BatchTracker>();
             const CircleEdgeEffect& circleEffect = processor.cast<CircleEdgeEffect>();
-            b->add32(circleEffect.isStroked() << 16 | local.fInputColorType);
+            uint16_t key = circleEffect.isStroked() ? 0x1 : 0x0;
+            key |= local.fUsesLocalCoords && processor.localMatrix().hasPerspective() ? 0x2 : 0x0;
+            b->add32(key << 16 | local.fInputColorType);
         }
 
         virtual void setData(const GrGLProgramDataManager& pdman,
@@ -306,7 +308,9 @@
                            GrProcessorKeyBuilder* b) {
             const BatchTracker& local = bt.cast<BatchTracker>();
             const EllipseEdgeEffect& ellipseEffect = processor.cast<EllipseEdgeEffect>();
-            b->add32(ellipseEffect.isStroked() << 16 | local.fInputColorType);
+            uint16_t key = ellipseEffect.isStroked() ? 0x1 : 0x0;
+            key |= local.fUsesLocalCoords && processor.localMatrix().hasPerspective() ? 0x2 : 0x0;
+            b->add32(key << 16 | local.fInputColorType);
         }
 
         virtual void setData(const GrGLProgramDataManager& pdman,
@@ -510,7 +514,10 @@
                            GrProcessorKeyBuilder* b) {
             const BatchTracker& local = bt.cast<BatchTracker>();
             const DIEllipseEdgeEffect& ellipseEffect = processor.cast<DIEllipseEdgeEffect>();
-            b->add32(ellipseEffect.getMode() << 16 | local.fInputColorType);
+            uint16_t key = ellipseEffect.getMode();
+            key |= local.fUsesLocalCoords && processor.localMatrix().hasPerspective() ? 0x1 << 8 :
+                                                                                        0x0;
+            b->add32(key << 16 | local.fInputColorType);
         }
 
         virtual void setData(const GrGLProgramDataManager& pdman,
@@ -1086,7 +1093,7 @@
     if (applyAA) {
         bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
     }
-    target->drawRect(drawState, color, bounds, NULL, NULL);
+    target->drawSimpleRect(drawState, color, bounds);
     return true;
 }
 
diff --git a/src/gpu/effects/GrBezierEffect.cpp b/src/gpu/effects/GrBezierEffect.cpp
index 2a4ef36..0ba7bee 100644
--- a/src/gpu/effects/GrBezierEffect.cpp
+++ b/src/gpu/effects/GrBezierEffect.cpp
@@ -165,6 +165,7 @@
     uint32_t key = ce.isAntiAliased() ? (ce.isFilled() ? 0x0 : 0x1) : 0x2;
     key |= kUniform_GrGPInput == local.fInputColorType ? 0x4 : 0x0;
     key |= 0xff != local.fCoverageScale ? 0x8 : 0x0;
+    key |= local.fUsesLocalCoords && processor.localMatrix().hasPerspective() ? 0x10 : 0x0;
     b->add32(key);
 }
 
@@ -374,6 +375,7 @@
     uint32_t key = ce.isAntiAliased() ? (ce.isFilled() ? 0x0 : 0x1) : 0x2;
     key |= kUniform_GrGPInput == local.fInputColorType ? 0x4 : 0x0;
     key |= 0xff != local.fCoverageScale ? 0x8 : 0x0;
+    key |= local.fUsesLocalCoords && processor.localMatrix().hasPerspective() ? 0x10 : 0x0;
     b->add32(key);
 }
 
@@ -605,6 +607,7 @@
     const CubicBatchTracker& local = bt.cast<CubicBatchTracker>();
     uint32_t key = ce.isAntiAliased() ? (ce.isFilled() ? 0x0 : 0x1) : 0x2;
     key |= kUniform_GrGPInput == local.fInputColorType ? 0x4 : 0x8;
+    key |= local.fUsesLocalCoords && processor.localMatrix().hasPerspective() ? 0x10 : 0x0;
     b->add32(key);
 }
 
diff --git a/src/gpu/effects/GrBitmapTextGeoProc.cpp b/src/gpu/effects/GrBitmapTextGeoProc.cpp
index 09eee1c..44de98a 100644
--- a/src/gpu/effects/GrBitmapTextGeoProc.cpp
+++ b/src/gpu/effects/GrBitmapTextGeoProc.cpp
@@ -75,8 +75,10 @@
         // on addVertexAttrib.
         // TODO When we have deferred geometry we can fix this
         const GrBitmapTextGeoProc& gp = proc.cast<GrBitmapTextGeoProc>();
-        b->add32(SkToBool(gp.inColor()));
-        b->add32(local.fInputColorType);
+        uint32_t key = 0;
+        key |= SkToBool(gp.inColor()) ? 0x1 : 0x0;
+        key |= local.fUsesLocalCoords && proc.localMatrix().hasPerspective() ? 0x2 : 0x0;
+        b->add32(local.fInputColorType << 16 | key);
     }
 
 private:
@@ -90,8 +92,10 @@
 
 GrBitmapTextGeoProc::GrBitmapTextGeoProc(GrColor color, GrTexture* texture,
                                          const GrTextureParams& params, bool useColorAttrib,
-                                         bool opaqueVertexColors)
-    : INHERITED(color, opaqueVertexColors), fTextureAccess(texture, params), fInColor(NULL) {
+                                         bool opaqueVertexColors, const SkMatrix& localMatrix)
+    : INHERITED(color, opaqueVertexColors, localMatrix)
+    , fTextureAccess(texture, params)
+    , fInColor(NULL) {
     this->initClassID<GrBitmapTextGeoProc>();
     fInPosition = &this->addVertexAttrib(GrAttribute("inPosition", kVec2f_GrVertexAttribType));
     if (useColorAttrib) {
@@ -172,5 +176,6 @@
                                                            GrTextureParams::kNone_FilterMode);
 
     return GrBitmapTextGeoProc::Create(GrRandomColor(random), textures[texIdx], params,
-                                       random->nextBool(), random->nextBool());
+                                       random->nextBool(), random->nextBool(),
+                                       GrProcessorUnitTest::TestMatrix(random));
 }
diff --git a/src/gpu/effects/GrBitmapTextGeoProc.h b/src/gpu/effects/GrBitmapTextGeoProc.h
index 507515b..c69ffd7 100644
--- a/src/gpu/effects/GrBitmapTextGeoProc.h
+++ b/src/gpu/effects/GrBitmapTextGeoProc.h
@@ -22,8 +22,10 @@
 class GrBitmapTextGeoProc : public GrGeometryProcessor {
 public:
     static GrGeometryProcessor* Create(GrColor color, GrTexture* tex, const GrTextureParams& p,
-                                       bool useColorAttrib, bool opaqueVertexColors) {
-        return SkNEW_ARGS(GrBitmapTextGeoProc, (color, tex, p, useColorAttrib, opaqueVertexColors));
+                                       bool useColorAttrib, bool opaqueVertexColors,
+                                       const SkMatrix& localMatrix) {
+        return SkNEW_ARGS(GrBitmapTextGeoProc, (color, tex, p, useColorAttrib, opaqueVertexColors,
+                                                localMatrix));
     }
 
     virtual ~GrBitmapTextGeoProc() {}
@@ -47,7 +49,7 @@
 
 private:
     GrBitmapTextGeoProc(GrColor, GrTexture* texture, const GrTextureParams& params,
-                        bool useColorAttrib, bool opaqueVertexColors);
+                        bool useColorAttrib, bool opaqueVertexColors, const SkMatrix& localMatrix);
 
     virtual bool onIsEqual(const GrGeometryProcessor& other) const SK_OVERRIDE;
 
diff --git a/src/gpu/effects/GrDashingEffect.cpp b/src/gpu/effects/GrDashingEffect.cpp
index 35b7637..d5b0b48 100644
--- a/src/gpu/effects/GrDashingEffect.cpp
+++ b/src/gpu/effects/GrDashingEffect.cpp
@@ -624,6 +624,7 @@
                                    GrProcessorKeyBuilder* b) {
     const DashingCircleBatchTracker& local = bt.cast<DashingCircleBatchTracker>();
     const DashingCircleEffect& dce = processor.cast<DashingCircleEffect>();
+    b->add32(local.fUsesLocalCoords && processor.localMatrix().hasPerspective());
     b->add32(dce.getEdgeType() << 16 | local.fInputColorType);
 }
 
@@ -913,6 +914,7 @@
                                  GrProcessorKeyBuilder* b) {
     const DashingLineBatchTracker& local = bt.cast<DashingLineBatchTracker>();
     const DashingLineEffect& de = processor.cast<DashingLineEffect>();
+    b->add32(local.fUsesLocalCoords && processor.localMatrix().hasPerspective());
     b->add32(de.getEdgeType() << 16 | local.fInputColorType);
 }
 
diff --git a/src/gpu/effects/GrDistanceFieldTextureEffect.cpp b/src/gpu/effects/GrDistanceFieldTextureEffect.cpp
index 0f930af..f5510d4 100755
--- a/src/gpu/effects/GrDistanceFieldTextureEffect.cpp
+++ b/src/gpu/effects/GrDistanceFieldTextureEffect.cpp
@@ -166,8 +166,10 @@
         const GrDistanceFieldTextureEffect& dfTexEffect =
                 processor.cast<GrDistanceFieldTextureEffect>();
         const DistanceFieldBatchTracker& local = bt.cast<DistanceFieldBatchTracker>();
-        b->add32(dfTexEffect.getFlags());
-        b->add32(local.fInputColorType);
+        uint32_t key = dfTexEffect.getFlags();
+        key |= local.fInputColorType << 16;
+        key |= local.fUsesLocalCoords && processor.localMatrix().hasPerspective() ? 0x1 << 24: 0x0;
+        b->add32(key);
     }
 
 private:
@@ -422,8 +424,10 @@
             proc.cast<GrDistanceFieldNoGammaTextureEffect>();
 
         const DistanceFieldNoGammaBatchTracker& local = bt.cast<DistanceFieldNoGammaBatchTracker>();
-        b->add32(dfTexEffect.getFlags());
-        b->add32(local.fInputColorType);
+        uint32_t key = dfTexEffect.getFlags();
+        key |= local.fInputColorType << 16;
+        key |= local.fUsesLocalCoords && proc.localMatrix().hasPerspective() ? 0x1 << 24: 0x0;
+        b->add32(key);
     }
 
 private:
@@ -724,8 +728,10 @@
                 processor.cast<GrDistanceFieldLCDTextureEffect>();
 
         const DistanceFieldLCDBatchTracker& local = bt.cast<DistanceFieldLCDBatchTracker>();
-        b->add32(dfTexEffect.getFlags());
-        b->add32(local.fInputColorType);
+        uint32_t key = dfTexEffect.getFlags();
+        key |= local.fInputColorType << 16;
+        key |= local.fUsesLocalCoords && processor.localMatrix().hasPerspective() ? 0x1 << 24: 0x0;
+        b->add32(key);
     }
 
 private:
diff --git a/src/gpu/gl/builders/GrGLProgramBuilder.cpp b/src/gpu/gl/builders/GrGLProgramBuilder.cpp
index cfb1d38..13a1453 100644
--- a/src/gpu/gl/builders/GrGLProgramBuilder.cpp
+++ b/src/gpu/gl/builders/GrGLProgramBuilder.cpp
@@ -378,8 +378,22 @@
 
     for (int t = 0; t < numTransforms; t++) {
         const char* uniName = "StageMatrix";
-        GrSLType varyingType = stage.isPerspectiveCoordTransform(t) ? kVec3f_GrSLType :
-                                                                      kVec2f_GrSLType;
+        GrSLType varyingType;
+
+        // TODO when we have deleted the coord change matrices we can get rid of the below check
+        GrCoordSet coordType = processor->coordTransform(t).sourceCoords();
+        const SkMatrix& localMatrix = fOptState.getPrimitiveProcessor()->localMatrix();
+        if (localMatrix.isIdentity()) {
+            varyingType = stage.isPerspectiveCoordTransform(t) ? kVec3f_GrSLType :
+                                                                 kVec2f_GrSLType;
+        } else {
+            uint32_t type = processor->coordTransform(t).getMatrix().getType();
+            if (kLocal_GrCoordSet == coordType) {
+                type |= localMatrix.getType();
+            }
+            varyingType = SkToBool(SkMatrix::kPerspective_Mask & type) ? kVec3f_GrSLType :
+                                                                         kVec2f_GrSLType;
+        }
         GrSLPrecision precision = processor->coordTransform(t).precision();
 
         SkString suffixedUniName;
@@ -401,7 +415,6 @@
             varyingName = suffixedVaryingName.c_str();
         }
 
-        GrCoordSet coordType = processor->coordTransform(t).sourceCoords();
         GrGLVertToFrag v(varyingType);
         this->addVarying(varyingName, &v, precision);
         fCoordVaryings.push_back(TransformVarying(v, uniName, coordType));