Stop flattening GrCoordTransforms in parent GrFragmentProcessors.

This changes moves to a model that iterates over GrCTs in a GrFP hierarchy when inserting transformations by GrGLSLPrimitiveProcessors.
GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2339203002

Committed: https://skia.googlesource.com/skia/+/d91237ee051523f439238042674ade99207fe4a6
Review-Url: https://codereview.chromium.org/2339203002
diff --git a/src/gpu/GrDefaultGeoProcFactory.cpp b/src/gpu/GrDefaultGeoProcFactory.cpp
index 1b496b3..497f6b7 100644
--- a/src/gpu/GrDefaultGeoProcFactory.cpp
+++ b/src/gpu/GrDefaultGeoProcFactory.cpp
@@ -99,8 +99,7 @@
                                      gpArgs->fPositionVar,
                                      gp.inLocalCoords()->fName,
                                      gp.localMatrix(),
-                                     args.fTransformsIn,
-                                     args.fTransformsOut);
+                                     args.fFPCoordTransformHandler);
             } else {
                 // emit transforms with position
                 this->emitTransforms(vertBuilder,
@@ -109,8 +108,7 @@
                                      gpArgs->fPositionVar,
                                      gp.inPosition()->fName,
                                      gp.localMatrix(),
-                                     args.fTransformsIn,
-                                     args.fTransformsOut);
+                                     args.fFPCoordTransformHandler);
             }
 
             // Setup coverage as pass through
@@ -150,7 +148,8 @@
         }
 
         void setData(const GrGLSLProgramDataManager& pdman,
-                     const GrPrimitiveProcessor& gp) override {
+                     const GrPrimitiveProcessor& gp,
+                     FPCoordTransformIter&& transformIter) override {
             const DefaultGeoProc& dgp = gp.cast<DefaultGeoProc>();
 
             if (!dgp.viewMatrix().isIdentity() && !fViewMatrix.cheapEqualTo(dgp.viewMatrix())) {
@@ -172,14 +171,7 @@
                 pdman.set1f(fCoverageUniform, GrNormalizeByteToFloat(dgp.coverage()));
                 fCoverage = dgp.coverage();
             }
-        }
-
-        void setTransformData(const GrPrimitiveProcessor& primProc,
-                              const GrGLSLProgramDataManager& pdman,
-                              int index,
-                              const SkTArray<const GrCoordTransform*, true>& transforms) override {
-            this->setTransformDataHelper(primProc.cast<DefaultGeoProc>().fLocalMatrix, pdman, index,
-                                         transforms);
+            this->setTransformDataHelper(dgp.fLocalMatrix, pdman, &transformIter);
         }
 
     private:
diff --git a/src/gpu/GrFragmentProcessor.cpp b/src/gpu/GrFragmentProcessor.cpp
index a9d4a5e..3dfe25e 100644
--- a/src/gpu/GrFragmentProcessor.cpp
+++ b/src/gpu/GrFragmentProcessor.cpp
@@ -8,6 +8,7 @@
 #include "GrFragmentProcessor.h"
 #include "GrCoordTransform.h"
 #include "GrInvariantOutput.h"
+#include "GrPipeline.h"
 #include "GrProcOptInfo.h"
 #include "glsl/GrGLSLFragmentProcessor.h"
 #include "glsl/GrGLSLFragmentShaderBuilder.h"
@@ -81,16 +82,10 @@
     fCoordTransforms.push_back(transform);
     fUsesLocalCoords = fUsesLocalCoords || transform->sourceCoords() == kLocal_GrCoordSet;
     SkDEBUGCODE(transform->setInProcessor();)
-    fNumTransformsExclChildren++;
 }
 
 int GrFragmentProcessor::registerChildProcessor(sk_sp<GrFragmentProcessor> child) {
-    // Append the child's transforms to our transforms array and the child's textures array to our
-    // textures array
-    if (!child->fCoordTransforms.empty()) {
-        fCoordTransforms.push_back_n(child->fCoordTransforms.count(),
-                                     child->fCoordTransforms.begin());
-    }
+    // Append the child's textures array to our textures array
     if (!child->fTextureAccesses.empty()) {
         fTextureAccesses.push_back_n(child->fTextureAccesses.count(),
                                      child->fTextureAccesses.begin());
@@ -120,10 +115,10 @@
 }
 
 bool GrFragmentProcessor::hasSameTransforms(const GrFragmentProcessor& that) const {
-    if (this->numTransforms() != that.numTransforms()) {
+    if (this->numCoordTransforms() != that.numCoordTransforms()) {
         return false;
     }
-    int count = this->numTransforms();
+    int count = this->numCoordTransforms();
     for (int i = 0; i < count; ++i) {
         if (this->coordTransform(i) != that.coordTransform(i)) {
             return false;
@@ -408,3 +403,39 @@
     }
     return sk_sp<GrFragmentProcessor>(new SeriesFragmentProcessor(series, cnt));
 }
+
+//////////////////////////////////////////////////////////////////////////////
+
+GrFragmentProcessor::Iter::Iter(const GrPipeline& pipeline) {
+    for (int i = pipeline.numFragmentProcessors() - 1; i >= 0; --i) {
+        fFPStack.push_back(&pipeline.getFragmentProcessor(i));
+    }
+}
+
+const GrFragmentProcessor* GrFragmentProcessor::Iter::next() {
+    if (fFPStack.empty()) {
+        return nullptr;
+    }
+    const GrFragmentProcessor* back = fFPStack.back();
+    fFPStack.pop_back();
+    for (int i = back->numChildProcessors() - 1; i >= 0; --i) {
+        fFPStack.push_back(&back->childProcessor(i));
+    }
+    return back;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+const GrCoordTransform* GrFragmentProcessor::CoordTransformIter::next() {
+    if (!fCurrFP) {
+        return nullptr;
+    }
+    while (fCTIdx == fCurrFP->numCoordTransforms()) {
+        fCTIdx = 0;
+        fCurrFP = fFPIter.next();
+        if (!fCurrFP) {
+            return nullptr;
+        }
+    }
+    return &fCurrFP->coordTransform(fCTIdx++);
+}
diff --git a/src/gpu/GrOvalRenderer.cpp b/src/gpu/GrOvalRenderer.cpp
index a04aa00..90b2d8e 100644
--- a/src/gpu/GrOvalRenderer.cpp
+++ b/src/gpu/GrOvalRenderer.cpp
@@ -161,8 +161,7 @@
                                  gpArgs->fPositionVar,
                                  cgp.fInPosition->fName,
                                  cgp.fLocalMatrix,
-                                 args.fTransformsIn,
-                                 args.fTransformsOut);
+                                 args.fFPCoordTransformHandler);
 
             fragBuilder->codeAppend("float d = length(circleEdge.xy);");
             fragBuilder->codeAppend("float distanceToOuterEdge = circleEdge.z * (1.0 - d);");
@@ -210,14 +209,10 @@
             b->add32(key);
         }
 
-        void setData(const GrGLSLProgramDataManager&, const GrPrimitiveProcessor&) override {}
-
-        void setTransformData(const GrPrimitiveProcessor& primProc,
-                              const GrGLSLProgramDataManager& pdman,
-                              int index,
-                              const SkTArray<const GrCoordTransform*, true>& transforms) override {
+        void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& primProc,
+                     FPCoordTransformIter&& transformIter) override {
             this->setTransformDataHelper(primProc.cast<CircleGeometryProcessor>().fLocalMatrix,
-                                         pdman, index, transforms);
+                                         pdman, &transformIter);
         }
 
     private:
@@ -319,8 +314,7 @@
                                  gpArgs->fPositionVar,
                                  egp.fInPosition->fName,
                                  egp.fLocalMatrix,
-                                 args.fTransformsIn,
-                                 args.fTransformsOut);
+                                 args.fFPCoordTransformHandler);
 
             // for outer curve
             fragBuilder->codeAppendf("vec2 scaledOffset = %s*%s.xy;", ellipseOffsets.fsIn(),
@@ -357,15 +351,10 @@
             b->add32(key);
         }
 
-        void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& gp) override {
-        }
-
-        void setTransformData(const GrPrimitiveProcessor& primProc,
-                              const GrGLSLProgramDataManager& pdman,
-                              int index,
-                              const SkTArray<const GrCoordTransform*, true>& transforms) override {
-            this->setTransformDataHelper(primProc.cast<EllipseGeometryProcessor>().fLocalMatrix,
-                                         pdman, index, transforms);
+        void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& primProc,
+                     FPCoordTransformIter&& transformIter) override {
+            const EllipseGeometryProcessor& egp = primProc.cast<EllipseGeometryProcessor>();
+            this->setTransformDataHelper(egp.fLocalMatrix, pdman, &transformIter);
         }
 
     private:
@@ -472,8 +461,7 @@
                                  uniformHandler,
                                  gpArgs->fPositionVar,
                                  diegp.fInPosition->fName,
-                                 args.fTransformsIn,
-                                 args.fTransformsOut);
+                                 args.fFPCoordTransformHandler);
 
             SkAssertResult(fragBuilder->enableFeature(
                     GrGLSLFragmentShaderBuilder::kStandardDerivatives_GLSLFeature));
@@ -525,8 +513,8 @@
             b->add32(key);
         }
 
-        void setData(const GrGLSLProgramDataManager& pdman,
-                     const GrPrimitiveProcessor& gp) override {
+        void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& gp,
+                     FPCoordTransformIter&& transformIter) override {
             const DIEllipseGeometryProcessor& diegp = gp.cast<DIEllipseGeometryProcessor>();
 
             if (!diegp.fViewMatrix.isIdentity() && !fViewMatrix.cheapEqualTo(diegp.fViewMatrix)) {
@@ -535,6 +523,7 @@
                 GrGLSLGetMatrix<3>(viewMatrix, fViewMatrix);
                 pdman.setMatrix3f(fViewMatrixUniform, viewMatrix);
             }
+            this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
         }
 
     private:
diff --git a/src/gpu/GrPathProcessor.cpp b/src/gpu/GrPathProcessor.cpp
index aa776ec..c90481b 100644
--- a/src/gpu/GrPathProcessor.cpp
+++ b/src/gpu/GrPathProcessor.cpp
@@ -34,7 +34,7 @@
         }
 
         // emit transforms
-        this->emitTransforms(args.fVaryingHandler, args.fTransformsIn, args.fTransformsOut);
+        this->emitTransforms(args.fVaryingHandler, args.fFPCoordTransformHandler);
 
         // Setup uniform color
         if (pathProc.overrides().readsColor()) {
@@ -54,34 +54,30 @@
     }
 
     void emitTransforms(GrGLSLVaryingHandler* varyingHandler,
-                        const TransformsIn& tin,
-                        TransformsOut* tout) {
-        tout->push_back_n(tin.count());
-        fInstalledTransforms.push_back_n(tin.count());
-        for (int i = 0; i < tin.count(); i++) {
-            const ProcCoords& coordTransforms = tin[i];
-            fInstalledTransforms[i].push_back_n(coordTransforms.count());
-            for (int t = 0; t < coordTransforms.count(); t++) {
-                GrSLType varyingType =
-                        coordTransforms[t]->getMatrix().hasPerspective() ? kVec3f_GrSLType :
-                                                                           kVec2f_GrSLType;
+                        FPCoordTransformHandler* transformHandler) {
+        int i = 0;
+        while (const GrCoordTransform* coordTransform = transformHandler->nextCoordTransform()) {
+            GrSLType varyingType =
+                    coordTransform->getMatrix().hasPerspective() ? kVec3f_GrSLType
+                                                                 : kVec2f_GrSLType;
 
-                SkString strVaryingName("MatrixCoord");
-                strVaryingName.appendf("_%i_%i", i, t);
-                GrGLSLVertToFrag v(varyingType);
-                GrGLVaryingHandler* glVaryingHandler = (GrGLVaryingHandler*) varyingHandler;
-                fInstalledTransforms[i][t].fHandle =
-                        glVaryingHandler->addPathProcessingVarying(strVaryingName.c_str(),
-                                                                   &v).toIndex();
-                fInstalledTransforms[i][t].fType = varyingType;
+            SkString strVaryingName;
+            strVaryingName.printf("TransformedCoord_%d", i);
+            GrGLSLVertToFrag v(varyingType);
+            GrGLVaryingHandler* glVaryingHandler = (GrGLVaryingHandler*) varyingHandler;
+            fInstalledTransforms.push_back().fHandle =
+                    glVaryingHandler->addPathProcessingVarying(strVaryingName.c_str(),
+                                                               &v).toIndex();
+            fInstalledTransforms.back().fType = varyingType;
 
-                (*tout)[i].emplace_back(SkString(v.fsIn()), varyingType);
-            }
+            transformHandler->specifyCoordsForCurrCoordTransform(SkString(v.fsIn()), varyingType);
+            ++i;
         }
     }
 
     void setData(const GrGLSLProgramDataManager& pd,
-                 const GrPrimitiveProcessor& primProc) override {
+                 const GrPrimitiveProcessor& primProc,
+                 FPCoordTransformIter&& transformIter) override {
         const GrPathProcessor& pathProc = primProc.cast<GrPathProcessor>();
         if (pathProc.overrides().readsColor() && pathProc.color() != fColor) {
             float c[4];
@@ -89,28 +85,21 @@
             pd.set4fv(fColorUniform, 1, c);
             fColor = pathProc.color();
         }
-    }
 
-    void setTransformData(const GrPrimitiveProcessor& primProc,
-                          const GrGLSLProgramDataManager& pdman,
-                          int index,
-                          const SkTArray<const GrCoordTransform*, true>& coordTransforms) override {
-        const GrPathProcessor& pathProc = primProc.cast<GrPathProcessor>();
-        SkTArray<TransformVarying, true>& transforms = fInstalledTransforms[index];
-        int numTransforms = transforms.count();
-        for (int t = 0; t < numTransforms; ++t) {
-            SkASSERT(transforms[t].fHandle.isValid());
-            const SkMatrix& transform = GetTransformMatrix(pathProc.localMatrix(),
-                                                           *coordTransforms[t]);
-            if (transforms[t].fCurrentValue.cheapEqualTo(transform)) {
+        int t = 0;
+        while (const GrCoordTransform* coordTransform = transformIter.next()) {
+            SkASSERT(fInstalledTransforms[t].fHandle.isValid());
+            const SkMatrix& m = GetTransformMatrix(pathProc.localMatrix(), *coordTransform);
+            if (fInstalledTransforms[t].fCurrentValue.cheapEqualTo(m)) {
                 continue;
             }
-            transforms[t].fCurrentValue = transform;
+            fInstalledTransforms[t].fCurrentValue = m;
 
-            SkASSERT(transforms[t].fType == kVec2f_GrSLType ||
-                     transforms[t].fType == kVec3f_GrSLType);
-            unsigned components = transforms[t].fType == kVec2f_GrSLType ? 2 : 3;
-            pdman.setPathFragmentInputTransform(transforms[t].fHandle, components, transform);
+            SkASSERT(fInstalledTransforms[t].fType == kVec2f_GrSLType ||
+                     fInstalledTransforms[t].fType == kVec3f_GrSLType);
+            unsigned components = fInstalledTransforms[t].fType == kVec2f_GrSLType ? 2 : 3;
+            pd.setPathFragmentInputTransform(fInstalledTransforms[t].fHandle, components, m);
+            ++t;
         }
     }
 
@@ -122,7 +111,7 @@
         GrSLType       fType = kVoid_GrSLType;
     };
 
-    SkSTArray<8, SkSTArray<2, TransformVarying, true> > fInstalledTransforms;
+    SkTArray<TransformVarying, true> fInstalledTransforms;
 
     UniformHandle fColorUniform;
     GrColor fColor;
diff --git a/src/gpu/GrProgramDesc.cpp b/src/gpu/GrProgramDesc.cpp
index a22063c..c452d34 100644
--- a/src/gpu/GrProgramDesc.cpp
+++ b/src/gpu/GrProgramDesc.cpp
@@ -101,7 +101,7 @@
     fp.getGLSLProcessorKey(glslCaps, b);
 
     return gen_meta_key(fp, glslCaps, primProc.getTransformKey(fp.coordTransforms(),
-                                                               fp.numTransformsExclChildren()), b);
+                                                               fp.numCoordTransforms()), b);
 }
 
 bool GrProgramDesc::Build(GrProgramDesc* desc,
diff --git a/src/gpu/batches/GrAAConvexPathRenderer.cpp b/src/gpu/batches/GrAAConvexPathRenderer.cpp
index a24a0c5..224567a 100644
--- a/src/gpu/batches/GrAAConvexPathRenderer.cpp
+++ b/src/gpu/batches/GrAAConvexPathRenderer.cpp
@@ -577,8 +577,7 @@
                                  gpArgs->fPositionVar,
                                  qe.inPosition()->fName,
                                  qe.localMatrix(),
-                                 args.fTransformsIn,
-                                 args.fTransformsOut);
+                                 args.fFPCoordTransformHandler);
 
             SkAssertResult(fragBuilder->enableFeature(
                     GrGLSLFragmentShaderBuilder::kStandardDerivatives_GLSLFeature));
@@ -614,7 +613,8 @@
         }
 
         void setData(const GrGLSLProgramDataManager& pdman,
-                     const GrPrimitiveProcessor& gp) override {
+                     const GrPrimitiveProcessor& gp,
+                     FPCoordTransformIter&& transformIter) override {
             const QuadEdgeEffect& qe = gp.cast<QuadEdgeEffect>();
             if (qe.color() != fColor) {
                 float c[4];
@@ -622,14 +622,7 @@
                 pdman.set4fv(fColorUniform, 1, c);
                 fColor = qe.color();
             }
-        }
-
-        void setTransformData(const GrPrimitiveProcessor& primProc,
-                              const GrGLSLProgramDataManager& pdman,
-                              int index,
-                              const SkTArray<const GrCoordTransform*, true>& transforms) override {
-            this->setTransformDataHelper(primProc.cast<QuadEdgeEffect>().fLocalMatrix, pdman, index,
-                                         transforms);
+            this->setTransformDataHelper(qe.fLocalMatrix, pdman, &transformIter);
         }
 
     private:
diff --git a/src/gpu/batches/GrAnalyticRectBatch.cpp b/src/gpu/batches/GrAnalyticRectBatch.cpp
index 7be0ad3..8c0f419 100644
--- a/src/gpu/batches/GrAnalyticRectBatch.cpp
+++ b/src/gpu/batches/GrAnalyticRectBatch.cpp
@@ -18,6 +18,7 @@
 #include "batches/GrVertexBatch.h"
 #include "glsl/GrGLSLFragmentShaderBuilder.h"
 #include "glsl/GrGLSLGeometryProcessor.h"
+#include "glsl/GrGLSLGeometryProcessor.h"
 #include "glsl/GrGLSLProgramDataManager.h"
 #include "glsl/GrGLSLVarying.h"
 #include "glsl/GrGLSLVertexShaderBuilder.h"
@@ -120,8 +121,7 @@
                                  gpArgs->fPositionVar,
                                  rgp.inPosition()->fName,
                                  rgp.localMatrix(),
-                                 args.fTransformsIn,
-                                 args.fTransformsOut);
+                                 args.fFPCoordTransformHandler);
 
             // TODO: compute all these offsets, spans, and scales in the VS
             fragBuilder->codeAppendf("float insetW = min(1.0, %s.x) - 0.5;",
@@ -194,15 +194,10 @@
             b->add32(0x0);
         }
 
-        void setData(const GrGLSLProgramDataManager& pdman,
-                     const GrPrimitiveProcessor& gp) override {}
-
-        void setTransformData(const GrPrimitiveProcessor& primProc,
-                              const GrGLSLProgramDataManager& pdman,
-                              int index,
-                              const SkTArray<const GrCoordTransform*, true>& transforms) override {
-            this->setTransformDataHelper(primProc.cast<RectGeometryProcessor>().fLocalMatrix, pdman,
-                                         index, transforms);
+        void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& primProc,
+                     FPCoordTransformIter&& transformIter) override {
+            const RectGeometryProcessor& rgp = primProc.cast<RectGeometryProcessor>();
+            this->setTransformDataHelper(rgp.fLocalMatrix, pdman,&transformIter);
         }
 
     private:
diff --git a/src/gpu/batches/GrMSAAPathRenderer.cpp b/src/gpu/batches/GrMSAAPathRenderer.cpp
index d6b5578..19dd366 100644
--- a/src/gpu/batches/GrMSAAPathRenderer.cpp
+++ b/src/gpu/batches/GrMSAAPathRenderer.cpp
@@ -148,8 +148,8 @@
 
             // emit transforms
             this->emitTransforms(vsBuilder, varyingHandler, uniformHandler, gpArgs->fPositionVar,
-                                 qp.inPosition()->fName, SkMatrix::I(), args.fTransformsIn,
-                                 args.fTransformsOut);
+                                 qp.inPosition()->fName, SkMatrix::I(),
+                                 args.fFPCoordTransformHandler);
 
             GrGLSLPPFragmentBuilder* fsBuilder = args.fFragBuilder;
             fsBuilder->codeAppendf("if (%s.x * %s.x >= %s.y) discard;", uv.fsIn(), uv.fsIn(),
@@ -167,14 +167,15 @@
             b->add32(key);
         }
 
-        virtual void setData(const GrGLSLProgramDataManager& pdman,
-                             const GrPrimitiveProcessor& gp) override {
+        void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& gp,
+                     FPCoordTransformIter&& transformIter) override {
             const MSAAQuadProcessor& qp = gp.cast<MSAAQuadProcessor>();
             if (!qp.viewMatrix().isIdentity()) {
                 float viewMatrix[3 * 3];
                 GrGLSLGetMatrix<3>(viewMatrix, qp.viewMatrix());
                 pdman.setMatrix3f(fViewMatrixUniform, viewMatrix);
             }
+            this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
         }
 
     private:
diff --git a/src/gpu/batches/GrPLSPathRenderer.cpp b/src/gpu/batches/GrPLSPathRenderer.cpp
index 81009a6..e8711c0 100644
--- a/src/gpu/batches/GrPLSPathRenderer.cpp
+++ b/src/gpu/batches/GrPLSPathRenderer.cpp
@@ -333,8 +333,8 @@
 
             // emit transforms
             this->emitTransforms(vsBuilder, varyingHandler, uniformHandler, gpArgs->fPositionVar,
-                                 te.inPosition()->fName, te.localMatrix(), args.fTransformsIn,
-                                 args.fTransformsOut);
+                                 te.inPosition()->fName, te.localMatrix(),
+                                 args.fFPCoordTransformHandler);
 
             GrGLSLPPFragmentBuilder* fsBuilder = args.fFragBuilder;
             SkAssertResult(fsBuilder->enableFeature(
@@ -391,16 +391,10 @@
             b->add32(key);
         }
 
-        virtual void setData(const GrGLSLProgramDataManager& pdman,
-                             const GrPrimitiveProcessor& gp) override {
-        }
-
-        void setTransformData(const GrPrimitiveProcessor& primProc,
-                              const GrGLSLProgramDataManager& pdman,
-                              int index,
-                              const SkTArray<const GrCoordTransform*, true>& transforms) override {
-            this->setTransformDataHelper(primProc.cast<PLSAATriangleEffect>().fLocalMatrix, pdman,
-                                         index, transforms);
+        void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& gp,
+                     FPCoordTransformIter&& transformIter) override {
+            this->setTransformDataHelper(gp.cast<PLSAATriangleEffect>().fLocalMatrix, pdman,
+                                         &transformIter);
         }
 
     private:
@@ -522,8 +516,8 @@
 
             // emit transforms
             this->emitTransforms(vsBuilder, varyingHandler, uniformHandler, gpArgs->fPositionVar,
-                                 qe.inPosition()->fName, qe.localMatrix(), args.fTransformsIn,
-                                 args.fTransformsOut);
+                                 qe.inPosition()->fName, qe.localMatrix(),
+                                 args.fFPCoordTransformHandler);
 
             GrGLSLPPFragmentBuilder* fsBuilder = args.fFragBuilder;
             SkAssertResult(fsBuilder->enableFeature(
@@ -581,16 +575,10 @@
             b->add32(key);
         }
 
-        virtual void setData(const GrGLSLProgramDataManager& pdman,
-                             const GrPrimitiveProcessor& gp) override {
-        }
-
-        void setTransformData(const GrPrimitiveProcessor& primProc,
-                              const GrGLSLProgramDataManager& pdman,
-                              int index,
-                              const SkTArray<const GrCoordTransform*, true>& transforms) override {
-            this->setTransformDataHelper(primProc.cast<PLSQuadEdgeEffect>().fLocalMatrix, pdman,
-                                         index, transforms);
+        void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& gp,
+                             FPCoordTransformIter&& transformIter) override {
+            this->setTransformDataHelper(gp.cast<PLSQuadEdgeEffect>().fLocalMatrix, pdman,
+                                         &transformIter);
         }
 
     private:
@@ -680,8 +668,8 @@
             varyingHandler->emitAttributes(fe);
             this->setupPosition(vsBuilder, gpArgs, fe.inPosition()->fName);
             this->emitTransforms(vsBuilder, varyingHandler, uniformHandler, gpArgs->fPositionVar,
-                                 fe.inPosition()->fName, fe.localMatrix(), args.fTransformsIn,
-                                 args.fTransformsOut);
+                                 fe.inPosition()->fName, fe.localMatrix(),
+                                 args.fFPCoordTransformHandler);
 
             GrGLSLPPFragmentBuilder* fsBuilder = args.fFragBuilder;
             SkAssertResult(fsBuilder->enableFeature(
@@ -716,8 +704,8 @@
             b->add32(key);
         }
 
-        virtual void setData(const GrGLSLProgramDataManager& pdman,
-                             const GrPrimitiveProcessor& gp) override {
+        void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& gp,
+                     FPCoordTransformIter&& transformIter) override {
             const PLSFinishEffect& fe = gp.cast<PLSFinishEffect>();
             pdman.set1f(fUseEvenOdd, fe.fUseEvenOdd);
             if (fe.color() != fColor && !fe.colorIgnored()) {
@@ -726,14 +714,7 @@
                 pdman.set4fv(fColorUniform, 1, c);
                 fColor = fe.color();
             }
-        }
-
-        void setTransformData(const GrPrimitiveProcessor& primProc,
-                              const GrGLSLProgramDataManager& pdman,
-                              int index,
-                              const SkTArray<const GrCoordTransform*, true>& transforms) override {
-            this->setTransformDataHelper(primProc.cast<PLSFinishEffect>().fLocalMatrix, pdman,
-                                         index, transforms);
+            this->setTransformDataHelper(fe.fLocalMatrix, pdman, &transformIter);
         }
 
     private:
diff --git a/src/gpu/effects/GrBezierEffect.cpp b/src/gpu/effects/GrBezierEffect.cpp
index 1894192..798695d 100644
--- a/src/gpu/effects/GrBezierEffect.cpp
+++ b/src/gpu/effects/GrBezierEffect.cpp
@@ -25,8 +25,8 @@
                               const GrGLSLCaps&,
                               GrProcessorKeyBuilder*);
 
-    void setData(const GrGLSLProgramDataManager& pdman,
-                 const GrPrimitiveProcessor& primProc) override {
+    void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& primProc,
+                 FPCoordTransformIter&& transformIter) override {
         const GrConicEffect& ce = primProc.cast<GrConicEffect>();
 
         if (!ce.viewMatrix().isIdentity() && !fViewMatrix.cheapEqualTo(ce.viewMatrix())) {
@@ -47,14 +47,7 @@
             pdman.set1f(fCoverageScaleUniform, GrNormalizeByteToFloat(ce.coverageScale()));
             fCoverageScale = ce.coverageScale();
         }
-    }
-
-    void setTransformData(const GrPrimitiveProcessor& primProc,
-                          const GrGLSLProgramDataManager& pdman,
-                          int index,
-                          const SkTArray<const GrCoordTransform*, true>& transforms) override {
-        this->setTransformDataHelper(primProc.cast<GrConicEffect>().localMatrix(), pdman, index,
-                                     transforms);
+        this->setTransformDataHelper(ce.localMatrix(), pdman, &transformIter);
     }
 
 private:
@@ -109,8 +102,7 @@
                          gpArgs->fPositionVar,
                          gp.inPosition()->fName,
                          gp.localMatrix(),
-                         args.fTransformsIn,
-                         args.fTransformsOut);
+                         args.fFPCoordTransformHandler);
 
     // TODO: this precision check should actually be a check on the number of bits
     // high and medium provide and the selection of the lowest level that suffices.
@@ -299,8 +291,8 @@
                               const GrGLSLCaps&,
                               GrProcessorKeyBuilder*);
 
-    void setData(const GrGLSLProgramDataManager& pdman,
-                 const GrPrimitiveProcessor& primProc) override {
+    void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& primProc,
+                 FPCoordTransformIter&& transformIter) override {
         const GrQuadEffect& qe = primProc.cast<GrQuadEffect>();
 
         if (!qe.viewMatrix().isIdentity() && !fViewMatrix.cheapEqualTo(qe.viewMatrix())) {
@@ -321,14 +313,7 @@
             pdman.set1f(fCoverageScaleUniform, GrNormalizeByteToFloat(qe.coverageScale()));
             fCoverageScale = qe.coverageScale();
         }
-    }
-
-    void setTransformData(const GrPrimitiveProcessor& primProc,
-                          const GrGLSLProgramDataManager& pdman,
-                          int index,
-                          const SkTArray<const GrCoordTransform*, true>& transforms) override {
-        this->setTransformDataHelper(primProc.cast<GrQuadEffect>().localMatrix(), pdman, index,
-                                     transforms);
+        this->setTransformDataHelper(qe.localMatrix(), pdman, &transformIter);
     }
 
 private:
@@ -383,8 +368,7 @@
                          gpArgs->fPositionVar,
                          gp.inPosition()->fName,
                          gp.localMatrix(),
-                         args.fTransformsIn,
-                         args.fTransformsOut);
+                         args.fFPCoordTransformHandler);
 
     fragBuilder->codeAppendf("float edgeAlpha;");
 
@@ -516,8 +500,8 @@
                               const GrGLSLCaps&,
                               GrProcessorKeyBuilder*);
 
-    void setData(const GrGLSLProgramDataManager& pdman,
-                 const GrPrimitiveProcessor& primProc) override {
+    void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& primProc,
+                 FPCoordTransformIter&& transformIter) override {
         const GrCubicEffect& ce = primProc.cast<GrCubicEffect>();
 
         if (!ce.viewMatrix().isIdentity() && !fViewMatrix.cheapEqualTo(ce.viewMatrix())) {
@@ -533,6 +517,7 @@
             pdman.set4fv(fColorUniform, 1, c);
             fColor = ce.color();
         }
+        this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
     }
 
 private:
@@ -584,8 +569,7 @@
                          uniformHandler,
                          gpArgs->fPositionVar,
                          gp.inPosition()->fName,
-                         args.fTransformsIn,
-                         args.fTransformsOut);
+                         args.fFPCoordTransformHandler);
 
 
     GrGLSLShaderVar edgeAlpha("edgeAlpha", kFloat_GrSLType, 0, kHigh_GrSLPrecision);
diff --git a/src/gpu/effects/GrBitmapTextGeoProc.cpp b/src/gpu/effects/GrBitmapTextGeoProc.cpp
index 20a4e93..7f53663 100644
--- a/src/gpu/effects/GrBitmapTextGeoProc.cpp
+++ b/src/gpu/effects/GrBitmapTextGeoProc.cpp
@@ -60,8 +60,7 @@
                              gpArgs->fPositionVar,
                              cte.inPosition()->fName,
                              cte.localMatrix(),
-                             args.fTransformsIn,
-                             args.fTransformsOut);
+                             args.fFPCoordTransformHandler);
 
         if (cte.maskFormat() == kARGB_GrMaskFormat) {
             fragBuilder->codeAppendf("%s = ", args.fOutputColor);
@@ -84,7 +83,8 @@
         }
     }
 
-    void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& gp) override {
+    void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& gp,
+                 FPCoordTransformIter&& transformIter) override {
         const GrBitmapTextGeoProc& btgp = gp.cast<GrBitmapTextGeoProc>();
         if (btgp.color() != fColor && !btgp.hasVertexColor()) {
             float c[4];
@@ -92,14 +92,7 @@
             pdman.set4fv(fColorUniform, 1, c);
             fColor = btgp.color();
         }
-    }
-
-    void setTransformData(const GrPrimitiveProcessor& primProc,
-                          const GrGLSLProgramDataManager& pdman,
-                          int index,
-                          const SkTArray<const GrCoordTransform*, true>& transforms) override {
-        this->setTransformDataHelper(primProc.cast<GrBitmapTextGeoProc>().localMatrix(), pdman,
-                                     index, transforms);
+        this->setTransformDataHelper(btgp.localMatrix(), pdman, &transformIter);
     }
 
     static inline void GenKey(const GrGeometryProcessor& proc,
diff --git a/src/gpu/effects/GrDashingEffect.cpp b/src/gpu/effects/GrDashingEffect.cpp
index 167cefa..9ce725b 100644
--- a/src/gpu/effects/GrDashingEffect.cpp
+++ b/src/gpu/effects/GrDashingEffect.cpp
@@ -817,16 +817,8 @@
                               const GrGLSLCaps&,
                               GrProcessorKeyBuilder*);
 
-    void setData(const GrGLSLProgramDataManager&, const GrPrimitiveProcessor&) override;
-
-    void setTransformData(const GrPrimitiveProcessor& primProc,
-                          const GrGLSLProgramDataManager& pdman,
-                          int index,
-                          const SkTArray<const GrCoordTransform*, true>& transforms) override {
-        this->setTransformDataHelper(primProc.cast<DashingCircleEffect>().localMatrix(), pdman,
-                                     index, transforms);
-    }
-
+    void setData(const GrGLSLProgramDataManager&, const GrPrimitiveProcessor&,
+                 FPCoordTransformIter&& transformIter) override;
 private:
     UniformHandle fParamUniform;
     UniformHandle fColorUniform;
@@ -879,8 +871,7 @@
                          gpArgs->fPositionVar,
                          dce.inPosition()->fName,
                          dce.localMatrix(),
-                         args.fTransformsIn,
-                         args.fTransformsOut);
+                         args.fFPCoordTransformHandler);
 
     // transforms all points so that we can compare them to our test circle
     fragBuilder->codeAppendf("float xShifted = %s.x - floor(%s.x / %s.z) * %s.z;",
@@ -901,7 +892,8 @@
 }
 
 void GLDashingCircleEffect::setData(const GrGLSLProgramDataManager& pdman,
-                                    const GrPrimitiveProcessor& processor) {
+                                    const GrPrimitiveProcessor& processor,
+                                    FPCoordTransformIter&& transformIter)  {
     const DashingCircleEffect& dce = processor.cast<DashingCircleEffect>();
     if (dce.color() != fColor) {
         float c[4];
@@ -909,6 +901,7 @@
         pdman.set4fv(fColorUniform, 1, c);
         fColor = dce.color();
     }
+    this->setTransformDataHelper(dce.localMatrix(), pdman, &transformIter);
 }
 
 void GLDashingCircleEffect::GenKey(const GrGeometryProcessor& gp,
@@ -1037,15 +1030,8 @@
                               const GrGLSLCaps&,
                               GrProcessorKeyBuilder*);
 
-    void setData(const GrGLSLProgramDataManager&, const GrPrimitiveProcessor&) override;
-
-    void setTransformData(const GrPrimitiveProcessor& primProc,
-                          const GrGLSLProgramDataManager& pdman,
-                          int index,
-                          const SkTArray<const GrCoordTransform*, true>& transforms) override {
-        this->setTransformDataHelper(primProc.cast<DashingLineEffect>().localMatrix(), pdman, index,
-                                     transforms);
-    }
+    void setData(const GrGLSLProgramDataManager&, const GrPrimitiveProcessor&,
+                 FPCoordTransformIter&& iter) override;
 
 private:
     GrColor       fColor;
@@ -1094,8 +1080,7 @@
                          gpArgs->fPositionVar,
                          de.inPosition()->fName,
                          de.localMatrix(),
-                         args.fTransformsIn,
-                         args.fTransformsOut);
+                         args.fFPCoordTransformHandler);
 
     // transforms all points so that we can compare them to our test rect
     fragBuilder->codeAppendf("float xShifted = %s.x - floor(%s.x / %s.z) * %s.z;",
@@ -1134,7 +1119,8 @@
 }
 
 void GLDashingLineEffect::setData(const GrGLSLProgramDataManager& pdman,
-                                  const GrPrimitiveProcessor& processor) {
+                                  const GrPrimitiveProcessor& processor,
+                                  FPCoordTransformIter&& transformIter) {
     const DashingLineEffect& de = processor.cast<DashingLineEffect>();
     if (de.color() != fColor) {
         float c[4];
@@ -1142,6 +1128,7 @@
         pdman.set4fv(fColorUniform, 1, c);
         fColor = de.color();
     }
+    this->setTransformDataHelper(de.localMatrix(), pdman, &transformIter);
 }
 
 void GLDashingLineEffect::GenKey(const GrGeometryProcessor& gp,
diff --git a/src/gpu/effects/GrDistanceFieldGeoProc.cpp b/src/gpu/effects/GrDistanceFieldGeoProc.cpp
index c74c5ce..5404b0c 100644
--- a/src/gpu/effects/GrDistanceFieldGeoProc.cpp
+++ b/src/gpu/effects/GrDistanceFieldGeoProc.cpp
@@ -73,8 +73,7 @@
                              uniformHandler,
                              gpArgs->fPositionVar,
                              dfTexEffect.inPosition()->fName,
-                             args.fTransformsIn,
-                             args.fTransformsOut);
+                             args.fFPCoordTransformHandler);
 
         // add varyings
         GrGLSLVertToFrag recipScale(kFloat_GrSLType);
@@ -179,7 +178,8 @@
         fragBuilder->codeAppendf("%s = vec4(val);", args.fOutputCoverage);
     }
 
-    void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& proc) override {
+    void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& proc,
+                 FPCoordTransformIter&& transformIter) override {
 #ifdef SK_GAMMA_APPLY_TO_A8
         const GrDistanceFieldA8TextGeoProc& dfTexEffect = proc.cast<GrDistanceFieldA8TextGeoProc>();
         float distanceAdjust = dfTexEffect.getDistanceAdjust();
@@ -196,6 +196,7 @@
             GrGLSLGetMatrix<3>(viewMatrix, fViewMatrix);
             pdman.setMatrix3f(fViewMatrixUniform, viewMatrix);
         }
+        this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
     }
 
     static inline void GenKey(const GrGeometryProcessor& gp,
@@ -345,8 +346,7 @@
                              uniformHandler,
                              gpArgs->fPositionVar,
                              dfTexEffect.inPosition()->fName,
-                             args.fTransformsIn,
-                             args.fTransformsOut);
+                             args.fFPCoordTransformHandler);
 
         const char* textureSizeUniName = nullptr;
         fTextureSizeUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
@@ -433,7 +433,8 @@
         fragBuilder->codeAppendf("%s = vec4(val);", args.fOutputCoverage);
     }
 
-    void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& proc) override {
+    void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& proc,
+                 FPCoordTransformIter&& transformIter) override {
         SkASSERT(fTextureSizeUni.isValid());
 
         GrTexture* texture = proc.texture(0);
@@ -453,6 +454,7 @@
             GrGLSLGetMatrix<3>(viewMatrix, fViewMatrix);
             pdman.setMatrix3f(fViewMatrixUniform, viewMatrix);
         }
+        this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
     }
 
     static inline void GenKey(const GrGeometryProcessor& gp,
@@ -582,8 +584,7 @@
                              uniformHandler,
                              gpArgs->fPositionVar,
                              dfTexEffect.inPosition()->fName,
-                             args.fTransformsIn,
-                             args.fTransformsOut);
+                             args.fFPCoordTransformHandler);
 
         // set up varyings
         bool isUniformScale = (dfTexEffect.getFlags() & kUniformScale_DistanceFieldEffectMask) ==
@@ -732,8 +733,8 @@
         fragBuilder->codeAppendf("%s = val;", args.fOutputCoverage);
     }
 
-    void setData(const GrGLSLProgramDataManager& pdman,
-                 const GrPrimitiveProcessor& processor) override {
+    void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& processor,
+                 FPCoordTransformIter&& transformIter) override {
         SkASSERT(fDistanceAdjustUni.isValid());
 
         const GrDistanceFieldLCDTextGeoProc& dflcd = processor.cast<GrDistanceFieldLCDTextGeoProc>();
@@ -752,6 +753,7 @@
             GrGLSLGetMatrix<3>(viewMatrix, fViewMatrix);
             pdman.setMatrix3f(fViewMatrixUniform, viewMatrix);
         }
+        this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
     }
 
     static inline void GenKey(const GrGeometryProcessor& gp,
diff --git a/src/gpu/gl/GrGLProgram.cpp b/src/gpu/gl/GrGLProgram.cpp
index a87aa5a..f34fce4 100644
--- a/src/gpu/gl/GrGLProgram.cpp
+++ b/src/gpu/gl/GrGLProgram.cpp
@@ -70,7 +70,8 @@
     // we set the textures, and uniforms for installed processors in a generic way, but subclasses
     // of GLProgram determine how to set coord transforms
     int nextSamplerIdx = 0;
-    fGeometryProcessor->setData(fProgramDataManager, primProc);
+    fGeometryProcessor->setData(fProgramDataManager, primProc,
+                                GrFragmentProcessor::CoordTransformIter(pipeline));
     this->bindTextures(primProc, pipeline.getAllowSRGBInputs(), &nextSamplerIdx);
 
     this->setFragmentData(primProc, pipeline, &nextSamplerIdx);
@@ -107,16 +108,10 @@
     for (int i = 0; i < numProcessors; ++i) {
         const GrFragmentProcessor& processor = pipeline.getFragmentProcessor(i);
         fFragmentProcessors[i]->setData(fProgramDataManager, processor);
-        this->setTransformData(primProc, processor, i);
         this->bindTextures(processor, pipeline.getAllowSRGBInputs(), nextSamplerIdx);
     }
 }
-void GrGLProgram::setTransformData(const GrPrimitiveProcessor& primProc,
-                                   const GrFragmentProcessor& processor,
-                                   int index) {
-    fGeometryProcessor->setTransformData(primProc, fProgramDataManager, index,
-                                         processor.coordTransforms());
-}
+
 
 void GrGLProgram::setRenderTargetState(const GrPrimitiveProcessor& primProc,
                                        const GrPipeline& pipeline) {
diff --git a/src/gpu/gl/GrGLProgram.h b/src/gpu/gl/GrGLProgram.h
index ea98d87..34037a2 100644
--- a/src/gpu/gl/GrGLProgram.h
+++ b/src/gpu/gl/GrGLProgram.h
@@ -120,7 +120,6 @@
 
     // A helper to loop over effects, set the transforms (via subclass) and bind textures
     void setFragmentData(const GrPrimitiveProcessor&, const GrPipeline&, int* nextSamplerIdx);
-    void setTransformData(const GrPrimitiveProcessor&, const GrFragmentProcessor&, int index);
 
     // Helper for setData() that sets the view matrix and loads the render target height uniform
     void setRenderTargetState(const GrPrimitiveProcessor&, const GrPipeline&);
diff --git a/src/gpu/glsl/GrGLSLFragmentProcessor.cpp b/src/gpu/glsl/GrGLSLFragmentProcessor.cpp
index 9a58db7..82a07ca 100644
--- a/src/gpu/glsl/GrGLSLFragmentProcessor.cpp
+++ b/src/gpu/glsl/GrGLSLFragmentProcessor.cpp
@@ -43,16 +43,16 @@
     const GrFragmentProcessor& childProc = args.fFp.childProcessor(childIndex);
 
     /*
-     * We now want to find the subset of coords and samplers that belong to the child and its
-     * descendants and put that into childCoords and childSamplers. To do so, we'll do a forwards
-     * linear search.
+     * TODO: Move textures and buffers to the iterator model used by coords.
+     * We now want to find the subset of samplers that belong to the child and its descendants and
+     * put that into childSamplers. To do so, we'll do a forwards linear search.
      *
      * Explanation:
-     * Each GrFragmentProcessor has a copy of all the transforms and textures of itself and
-     * all procs in its subtree. For example, suppose we have frag proc A, who has two children B
-     * and D. B has a child C, and D has two children E and F. Each frag proc's transforms array
-     * contains its own transforms, followed by the transforms of all its descendants (i.e. preorder
-     * traversal). Suppose procs A, B, C, D, E, F have 1, 2, 1, 1, 3, 2 transforms respectively.
+     * Each GrFragmentProcessor has a copy of all the textures of itself and all procs in its
+     * subtree. For example, suppose we have frag proc A, who has two children B and D. B has a
+     * child C, and D has two children E and F. Each frag proc's textures array contains its own
+     * textures, followed by the textures of all its descendants (i.e. preorder traversal). Suppose
+     * procs A, B, C, D, E, F have 1, 2, 1, 1, 3, 2 textures respectively.
      *
      *                                   (A)
      *                        [a1,b1,b2,c1,d1,e1,e2,e3,f1,f2]
@@ -66,28 +66,22 @@
      *                     [c1]      [e1,e2,e3]      [f1,f2]
      *
      * So if we're inside proc A's emitCode, and A is about to call emitCode on proc D, we want the
-     * EmitArgs that's passed onto D to only contain its and its descendants' coords. The
-     * EmitArgs given to A would contain the transforms [a1,b1,b2,c1,d1,e1,e2,e3,f1,f2], and we want
+     * EmitArgs that's passed onto D to only contain its and its descendants' textures. The
+     * EmitArgs given to A would contain the textures [a1,b1,b2,c1,d1,e1,e2,e3,f1,f2], and we want
      * to extract the subset [d1,e1,e2,e3,f1,f2] to pass on to D. We can do this with a linear
-     * search since we know that A has 1 transform (using A.numTransformsExclChildren()), and B's
-     * subtree has 3 transforms (using B.numTransforms()), so we know the start of D's transforms is
-     * 4 after the start of A's transforms.
-     * Textures work the same way as transforms.
+     * search since we know that A has 1 texture (using A.numTexturesExclChildren()), and B's
+     * subtree has 3 textures (using B.numTextures()), so we know the start of D's textures is
+     * 4 after the start of A's textures.
+     * Textures work the same way as textures.
      */
-    int firstCoordAt = args.fFp.numTransformsExclChildren();
     int firstTextureAt = args.fFp.numTexturesExclChildren();
     int firstBufferAt = args.fFp.numBuffersExclChildren();
     for (int i = 0; i < childIndex; ++i) {
-        firstCoordAt += args.fFp.childProcessor(i).numTransforms();
         firstTextureAt += args.fFp.childProcessor(i).numTextures();
         firstBufferAt += args.fFp.childProcessor(i).numBuffers();
     }
-    SkTArray<GrShaderVar> childCoords;
     const SamplerHandle* childTexSamplers = nullptr;
     const SamplerHandle* childBufferSamplers =  nullptr;
-    if (childProc.numTransforms() > 0) {
-        childCoords.push_back_n(childProc.numTransforms(), &args.fTransformedCoords[firstCoordAt]);
-    }
     if (childProc.numTextures() > 0) {
         childTexSamplers = &args.fTexSamplers[firstTextureAt];
     }
@@ -99,13 +93,14 @@
     fragBuilder->codeAppend("{\n");
     fragBuilder->codeAppendf("// Child Index %d (mangle: %s): %s\n", childIndex,
                              fragBuilder->getMangleString().c_str(), childProc.name());
+    TransformedCoordVars coordVars = args.fTransformedCoords.childTransforms(childIndex);
     EmitArgs childArgs(fragBuilder,
                        args.fUniformHandler,
                        args.fGLSLCaps,
                        childProc,
                        outputColor,
                        inputColor,
-                       childCoords,
+                       coordVars,
                        childTexSamplers,
                        childBufferSamplers,
                        args.fGpImplementsDistanceVector);
@@ -114,3 +109,19 @@
 
     fragBuilder->onAfterChildProcEmitCode();
 }
+
+//////////////////////////////////////////////////////////////////////////////
+
+using TransformedCoordVars = GrGLSLFragmentProcessor::TransformedCoordVars;
+TransformedCoordVars TransformedCoordVars::childTransforms(int childIdx) const {
+    const GrFragmentProcessor* child = &fFP->childProcessor(childIdx);
+    GrFragmentProcessor::Iter iter(fFP);
+    int numToSkip = 0;
+    while (true) {
+        const GrFragmentProcessor* fp = iter.next();
+        if (fp == child) {
+            return TransformedCoordVars(child, fTransformedVars + numToSkip);
+        }
+        numToSkip += fp->numCoordTransforms();
+    }
+}
diff --git a/src/gpu/glsl/GrGLSLFragmentProcessor.h b/src/gpu/glsl/GrGLSLFragmentProcessor.h
index f4a93eb..9889bcc 100644
--- a/src/gpu/glsl/GrGLSLFragmentProcessor.h
+++ b/src/gpu/glsl/GrGLSLFragmentProcessor.h
@@ -33,6 +33,30 @@
     typedef GrGLSLProgramDataManager::UniformHandle UniformHandle;
     typedef GrGLSLProgramDataManager::UniformHandle SamplerHandle;
 
+    /**
+     * When building a program from a GrPipeline this is used to provide the GrShaderVars that
+     * contain the resulting transformed coords from each of a GrFragmentProcessor's
+     * GrCoordTransforms. This allows the GrFragmentProcessor subclasses to refer to the transformed
+     * coords in fragment code.
+     */
+    class TransformedCoordVars {
+    public:
+        TransformedCoordVars(const GrFragmentProcessor* fp, const GrShaderVar* vars)
+                : fFP(fp)
+                , fTransformedVars(vars) {}
+
+        const GrShaderVar& operator[] (int i) const {
+            SkASSERT(i >= 0 && i < fFP->numCoordTransforms());
+            return fTransformedVars[i];
+        }
+
+        TransformedCoordVars childTransforms(int childIdx) const;
+
+    private:
+        const GrFragmentProcessor* fFP;
+        const GrShaderVar*         fTransformedVars;
+    };
+
     /** Called when the program stage should insert its code into the shaders. The code in each
         shader will be in its own block ({}) and so locally scoped names will not collide across
         stages.
@@ -50,7 +74,7 @@
                                  etc.) that allows the processor to communicate back similar known
                                  info about its output.
         @param transformedCoords Fragment shader variables containing the coords computed using
-                                 each of the GrFragmentProcessor's Coord Transforms.
+                                 each of the GrFragmentProcessor's GrCoordTransforms.
         @param texSamplers       Contains one entry for each GrTextureAccess of the GrProcessor.
                                  These can be passed to the builder to emit texture reads in the
                                  generated code.
@@ -65,7 +89,7 @@
                  const GrFragmentProcessor& fp,
                  const char* outputColor,
                  const char* inputColor,
-                 const SkTArray<GrShaderVar>& transformedCoords,
+                 const TransformedCoordVars& transformedCoordVars,
                  const SamplerHandle* texSamplers,
                  const SamplerHandle* bufferSamplers,
                  bool gpImplementsDistanceVector)
@@ -75,7 +99,7 @@
             , fFp(fp)
             , fOutputColor(outputColor)
             , fInputColor(inputColor)
-            , fTransformedCoords(transformedCoords)
+            , fTransformedCoords(transformedCoordVars)
             , fTexSamplers(texSamplers)
             , fBufferSamplers(bufferSamplers)
             , fGpImplementsDistanceVector(gpImplementsDistanceVector) {}
@@ -85,7 +109,7 @@
         const GrFragmentProcessor& fFp;
         const char* fOutputColor;
         const char* fInputColor;
-        const SkTArray<GrShaderVar>& fTransformedCoords;
+        const TransformedCoordVars& fTransformedCoords;
         const SamplerHandle* fTexSamplers;
         const SamplerHandle* fBufferSamplers;
         bool fGpImplementsDistanceVector;
diff --git a/src/gpu/glsl/GrGLSLGeometryProcessor.cpp b/src/gpu/glsl/GrGLSLGeometryProcessor.cpp
index ddbe8e1..bea49e5 100644
--- a/src/gpu/glsl/GrGLSLGeometryProcessor.cpp
+++ b/src/gpu/glsl/GrGLSLGeometryProcessor.cpp
@@ -29,73 +29,84 @@
                                              const GrShaderVar& posVar,
                                              const char* localCoords,
                                              const SkMatrix& localMatrix,
-                                             const TransformsIn& tin,
-                                             TransformsOut* tout) {
-    tout->push_back_n(tin.count());
-    fInstalledTransforms.push_back_n(tin.count());
-    for (int i = 0; i < tin.count(); i++) {
-        const ProcCoords& coordTransforms = tin[i];
-        fInstalledTransforms[i].push_back_n(coordTransforms.count());
-        for (int t = 0; t < coordTransforms.count(); t++) {
-            SkString strUniName("StageMatrix");
-            strUniName.appendf("_%i_%i", i, t);
-            GrSLType varyingType;
+                                             FPCoordTransformHandler* handler) {
+    int i = 0;
+    while (const GrCoordTransform* coordTransform = handler->nextCoordTransform()) {
+        SkString strUniName;
+        strUniName.printf("CoordTransformMatrix_%d", i);
+        GrSLType varyingType;
 
-            GrCoordSet coordType = coordTransforms[t]->sourceCoords();
-            uint32_t type = coordTransforms[t]->getMatrix().getType();
-            if (kLocal_GrCoordSet == coordType) {
-                type |= localMatrix.getType();
-            }
-            varyingType = SkToBool(SkMatrix::kPerspective_Mask & type) ? kVec3f_GrSLType :
-                                                                         kVec2f_GrSLType;
-            GrSLPrecision precision = coordTransforms[t]->precision();
+        GrCoordSet coordType = coordTransform->sourceCoords();
+        uint32_t type = coordTransform->getMatrix().getType();
+        if (kLocal_GrCoordSet == coordType) {
+            type |= localMatrix.getType();
+        }
+        varyingType = SkToBool(SkMatrix::kPerspective_Mask & type) ? kVec3f_GrSLType :
+                                                                     kVec2f_GrSLType;
+        GrSLPrecision precision = coordTransform->precision();
 
-            const char* uniName;
-            fInstalledTransforms[i][t].fHandle =
-                    uniformHandler->addUniform(kVertex_GrShaderFlag,
-                                               kMat33f_GrSLType, precision,
-                                               strUniName.c_str(),
-                                               &uniName).toIndex();
+        const char* uniName;
 
-            SkString strVaryingName("MatrixCoord");
-            strVaryingName.appendf("_%i_%i", i, t);
 
-            GrGLSLVertToFrag v(varyingType);
-            varyingHandler->addVarying(strVaryingName.c_str(), &v, precision);
+        fInstalledTransforms.push_back().fHandle = uniformHandler->addUniform(kVertex_GrShaderFlag,
+                                                                              kMat33f_GrSLType,
+                                                                              precision,
+                                                                              strUniName.c_str(),
+                                                                              &uniName).toIndex();
+        SkString strVaryingName;
+        strVaryingName.printf("TransformedCoords_%d", i);
 
-            SkASSERT(kVec2f_GrSLType == varyingType || kVec3f_GrSLType == varyingType);
-            (*tout)[i].emplace_back(SkString(v.fsIn()), varyingType);
+        GrGLSLVertToFrag v(varyingType);
+        varyingHandler->addVarying(strVaryingName.c_str(), &v, precision);
 
-            // varying = matrix * coords (logically)
-            if (kDevice_GrCoordSet == coordType) {
-                if (kVec2f_GrSLType == varyingType) {
-                    if (kVec2f_GrSLType == posVar.getType()) {
-                        vb->codeAppendf("%s = (%s * vec3(%s, 1)).xy;",
-                                        v.vsOut(), uniName, posVar.c_str());
-                    } else {
-                        // The brackets here are just to scope the temp variable
-                        vb->codeAppendf("{ vec3 temp = %s * %s;", uniName, posVar.c_str());
-                        vb->codeAppendf("%s = vec2(temp.x/temp.z, temp.y/temp.z); }", v.vsOut());
-                    }
+        SkASSERT(kVec2f_GrSLType == varyingType || kVec3f_GrSLType == varyingType);
+        handler->specifyCoordsForCurrCoordTransform(SkString(v.fsIn()), varyingType);
+
+        // varying = matrix * coords (logically)
+        if (kDevice_GrCoordSet == coordType) {
+            if (kVec2f_GrSLType == varyingType) {
+                if (kVec2f_GrSLType == posVar.getType()) {
+                    vb->codeAppendf("%s = (%s * vec3(%s, 1)).xy;",
+                                    v.vsOut(), uniName, posVar.c_str());
                 } else {
-                    if (kVec2f_GrSLType == posVar.getType()) {
-                        vb->codeAppendf("%s = %s * vec3(%s, 1);",
-                                        v.vsOut(), uniName, posVar.c_str());
-                    } else {
-                        vb->codeAppendf("%s = %s * %s;", v.vsOut(), uniName, posVar.c_str());
-                    }
+                    // The brackets here are just to scope the temp variable
+                    vb->codeAppendf("{ vec3 temp = %s * %s;", uniName, posVar.c_str());
+                    vb->codeAppendf("%s = vec2(temp.x/temp.z, temp.y/temp.z); }", v.vsOut());
                 }
             } else {
-                if (kVec2f_GrSLType == varyingType) {
-                    vb->codeAppendf("%s = (%s * vec3(%s, 1)).xy;", v.vsOut(), uniName, localCoords);
+                if (kVec2f_GrSLType == posVar.getType()) {
+                    vb->codeAppendf("%s = %s * vec3(%s, 1);",
+                                    v.vsOut(), uniName, posVar.c_str());
                 } else {
-                    vb->codeAppendf("%s = %s * vec3(%s, 1);", v.vsOut(), uniName, localCoords);
+                    vb->codeAppendf("%s = %s * %s;", v.vsOut(), uniName, posVar.c_str());
                 }
             }
+        } else {
+            if (kVec2f_GrSLType == varyingType) {
+                vb->codeAppendf("%s = (%s * vec3(%s, 1)).xy;", v.vsOut(), uniName, localCoords);
+            } else {
+                vb->codeAppendf("%s = %s * vec3(%s, 1);", v.vsOut(), uniName, localCoords);
+            }
         }
+        ++i;
     }
 }
 
+void GrGLSLGeometryProcessor::setTransformDataHelper(const SkMatrix& localMatrix,
+                                                     const GrGLSLProgramDataManager& pdman,
+                                                     FPCoordTransformIter* transformIter) {
+    int i = 0;
+    while (const GrCoordTransform* coordTransform = transformIter->next()) {
+        const SkMatrix& m = GetTransformMatrix(localMatrix, *coordTransform);
+        if (!fInstalledTransforms[i].fCurrentValue.cheapEqualTo(m)) {
+            pdman.setSkMatrix(fInstalledTransforms[i].fHandle.toIndex(), m);
+            fInstalledTransforms[i].fCurrentValue = m;
+        }
+        ++i;
+    }
+    SkASSERT(i == fInstalledTransforms.count());
+}
+
 void GrGLSLGeometryProcessor::setupPosition(GrGLSLVertexBuilder* vertBuilder,
                                             GrGPArgs* gpArgs,
                                             const char* posName) {
diff --git a/src/gpu/glsl/GrGLSLGeometryProcessor.h b/src/gpu/glsl/GrGLSLGeometryProcessor.h
index d1e715f..6777620 100644
--- a/src/gpu/glsl/GrGLSLGeometryProcessor.h
+++ b/src/gpu/glsl/GrGLSLGeometryProcessor.h
@@ -22,31 +22,11 @@
     /* Any general emit code goes in the base class emitCode.  Subclasses override onEmitCode */
     void emitCode(EmitArgs&) override;
 
-    // By default we use the identity matrix
-    void setTransformData(const GrPrimitiveProcessor&,
-                          const GrGLSLProgramDataManager& pdman,
-                          int index,
-                          const SkTArray<const GrCoordTransform*, true>& transforms) override {
-        this->setTransformDataHelper(SkMatrix::I(), pdman, index, transforms);
-    }
-
 protected:
     // A helper which subclasses can use if needed and used above in the default setTransformData().
     void setTransformDataHelper(const SkMatrix& localMatrix,
                                 const GrGLSLProgramDataManager& pdman,
-                                int index,
-                                const SkTArray<const GrCoordTransform*, true>& transforms) {
-        SkTArray<TransformUniform, true>& procTransforms = fInstalledTransforms[index];
-        int numTransforms = transforms.count();
-        for (int t = 0; t < numTransforms; ++t) {
-            SkASSERT(procTransforms[t].fHandle.isValid());
-            const SkMatrix& transform = GetTransformMatrix(localMatrix, *transforms[t]);
-            if (!procTransforms[t].fCurrentValue.cheapEqualTo(transform)) {
-                pdman.setSkMatrix(procTransforms[t].fHandle.toIndex(), transform);
-                procTransforms[t].fCurrentValue = transform;
-            }
-        }
-    }
+                                FPCoordTransformIter*);
 
     // Emit a uniform matrix for each coord transform.
     void emitTransforms(GrGLSLVertexBuilder* vb,
@@ -54,10 +34,9 @@
                         GrGLSLUniformHandler* uniformHandler,
                         const GrShaderVar& posVar,
                         const char* localCoords,
-                        const TransformsIn& tin,
-                        TransformsOut* tout) {
+                        FPCoordTransformHandler* handler) {
         this->emitTransforms(vb, varyingHandler, uniformHandler,
-                             posVar, localCoords, SkMatrix::I(), tin, tout);
+                             posVar, localCoords, SkMatrix::I(), handler);
     }
 
     // Emit pre-transformed coords as a vertex attribute per coord-transform.
@@ -67,8 +46,7 @@
                         const GrShaderVar& posVar,
                         const char* localCoords,
                         const SkMatrix& localMatrix,
-                        const TransformsIn&,
-                        TransformsOut*);
+                        FPCoordTransformHandler*);
 
     struct GrGPArgs {
         // The variable used by a GP to store its position. It can be
@@ -103,7 +81,7 @@
         SkMatrix       fCurrentValue = SkMatrix::InvalidMatrix();
     };
 
-    SkSTArray<8, SkSTArray<2, TransformUniform, true> > fInstalledTransforms;
+    SkTArray<TransformUniform, true> fInstalledTransforms;
 
     typedef GrGLSLPrimitiveProcessor INHERITED;
 };
diff --git a/src/gpu/glsl/GrGLSLPrimitiveProcessor.cpp b/src/gpu/glsl/GrGLSLPrimitiveProcessor.cpp
index c19239a..5ae28a0 100644
--- a/src/gpu/glsl/GrGLSLPrimitiveProcessor.cpp
+++ b/src/gpu/glsl/GrGLSLPrimitiveProcessor.cpp
@@ -47,3 +47,16 @@
                                                &stagedLocalVarName);
     fragBuilder->codeAppendf("%s = %s;", outputName, stagedLocalVarName);
 }
+
+//////////////////////////////////////////////////////////////////////////////
+
+const GrCoordTransform* GrGLSLPrimitiveProcessor::FPCoordTransformHandler::nextCoordTransform() {
+#ifdef SK_DEBUG
+    SkASSERT(nullptr == fCurr || fAddedCoord);
+    fAddedCoord = false;
+    fCurr = fIter.next();
+    return fCurr;
+#else
+    return fIter.next();
+#endif
+}
diff --git a/src/gpu/glsl/GrGLSLPrimitiveProcessor.h b/src/gpu/glsl/GrGLSLPrimitiveProcessor.h
index 6f3381f..d270fa1 100644
--- a/src/gpu/glsl/GrGLSLPrimitiveProcessor.h
+++ b/src/gpu/glsl/GrGLSLPrimitiveProcessor.h
@@ -8,6 +8,7 @@
 #ifndef GrGLSLPrimitiveProcessor_DEFINED
 #define GrGLSLPrimitiveProcessor_DEFINED
 
+#include "GrFragmentProcessor.h"
 #include "GrPrimitiveProcessor.h"
 #include "glsl/GrGLSLProgramDataManager.h"
 #include "glsl/GrGLSLSampler.h"
@@ -23,14 +24,45 @@
 
 class GrGLSLPrimitiveProcessor {
 public:
+    using FPCoordTransformIter = GrFragmentProcessor::CoordTransformIter;
+
     virtual ~GrGLSLPrimitiveProcessor() {}
 
     typedef GrGLSLProgramDataManager::UniformHandle UniformHandle;
     typedef GrGLSLProgramDataManager::UniformHandle SamplerHandle;
 
-    typedef SkSTArray<2, const GrCoordTransform*, true> ProcCoords;
-    typedef SkSTArray<8, ProcCoords> TransformsIn;
-    typedef SkSTArray<8, SkTArray<GrShaderVar>> TransformsOut;
+    /**
+     * This class provides access to the GrCoordTransforms across all GrFragmentProcessors in a
+     * GrPipeline. It is also used by the primitive processor to specify the fragment shader
+     * variable that will hold the transformed coords for each GrCoordTransform. It is required that
+     * the primitive processor iterate over each coord transform and insert a shader var result for
+     * each. The GrGLSLFragmentProcessors will reference these variables in their fragment code.
+     */
+    class FPCoordTransformHandler : public SkNoncopyable {
+    public:
+        FPCoordTransformHandler(const GrPipeline& pipeline,
+                                SkTArray<GrShaderVar>* transformedCoordVars)
+                : fIter(pipeline)
+                , fTransformedCoordVars(transformedCoordVars) {}
+
+        ~FPCoordTransformHandler() { SkASSERT(!this->nextCoordTransform());}
+
+        const GrCoordTransform* nextCoordTransform();
+
+        // 'args' are constructor params to GrShaderVar.
+        template<typename... Args>
+        void specifyCoordsForCurrCoordTransform(Args&&... args) {
+            SkASSERT(!fAddedCoord);
+            fTransformedCoordVars->emplace_back(std::forward<Args>(args)...);
+            SkDEBUGCODE(fAddedCoord = true;)
+        }
+
+    private:
+        GrFragmentProcessor::CoordTransformIter fIter;
+        SkDEBUGCODE(bool                        fAddedCoord = false;)
+        SkDEBUGCODE(const GrCoordTransform*     fCurr = nullptr;)
+        SkTArray<GrShaderVar>*                  fTransformedCoordVars;
+    };
 
     struct EmitArgs {
         EmitArgs(GrGLSLVertexBuilder* vertBuilder,
@@ -44,8 +76,7 @@
                  const char* distanceVectorName,
                  const SamplerHandle* texSamplers,
                  const SamplerHandle* bufferSamplers,
-                 const TransformsIn& transformsIn,
-                 TransformsOut* transformsOut)
+                 FPCoordTransformHandler* transformHandler)
             : fVertBuilder(vertBuilder)
             , fFragBuilder(fragBuilder)
             , fVaryingHandler(varyingHandler)
@@ -57,8 +88,7 @@
             , fDistanceVectorName(distanceVectorName)
             , fTexSamplers(texSamplers)
             , fBufferSamplers(bufferSamplers)
-            , fTransformsIn(transformsIn)
-            , fTransformsOut(transformsOut) {}
+            , fFPCoordTransformHandler(transformHandler) {}
         GrGLSLVertexBuilder* fVertBuilder;
         GrGLSLPPFragmentBuilder* fFragBuilder;
         GrGLSLVaryingHandler* fVaryingHandler;
@@ -70,8 +100,7 @@
         const char* fDistanceVectorName;
         const SamplerHandle* fTexSamplers;
         const SamplerHandle* fBufferSamplers;
-        const TransformsIn& fTransformsIn;
-        TransformsOut* fTransformsOut;
+        FPCoordTransformHandler* fFPCoordTransformHandler;
     };
 
     /**
@@ -80,21 +109,22 @@
      */
     virtual void emitCode(EmitArgs&) = 0;
 
-    /** A GrGLSLPrimitiveProcessor instance can be reused with any GrGLSLPrimitiveProcessor that
-        produces the same stage key; this function reads data from a GrGLSLPrimitiveProcessor and
-        uploads any uniform variables required  by the shaders created in emitCode(). The
-        GrPrimitiveProcessor parameter is guaranteed to be of the same type that created this
-        GrGLSLPrimitiveProcessor and to have an identical processor key as the one that created this
-        GrGLSLPrimitiveProcessor.  */
-    virtual void setData(const GrGLSLProgramDataManager&, const GrPrimitiveProcessor&) = 0;
+    /**
+     * A GrGLSLPrimitiveProcessor instance can be reused with any GrGLSLPrimitiveProcessor that
+     * produces the same stage key; this function reads data from a GrGLSLPrimitiveProcessor and
+     * uploads any uniform variables required  by the shaders created in emitCode(). The
+     * GrPrimitiveProcessor parameter is guaranteed to be of the same type and to have an
+     * identical processor key as the GrPrimitiveProcessor that created this
+     * GrGLSLPrimitiveProcessor.
+     * The subclass may use the transform iterator to perform any setup required for the particular
+     * set of fp transform matrices, such as uploading via uniforms. The iterator will iterate over
+     * the transforms in the same order as the TransformHandler passed to emitCode.
+     */
+    virtual void setData(const GrGLSLProgramDataManager&, const GrPrimitiveProcessor&,
+                         FPCoordTransformIter&&) = 0;
 
     static SkMatrix GetTransformMatrix(const SkMatrix& localMatrix, const GrCoordTransform&);
 
-    virtual void setTransformData(const GrPrimitiveProcessor&,
-                                  const GrGLSLProgramDataManager& pdman,
-                                  int index,
-                                  const SkTArray<const GrCoordTransform*, true>& transforms) = 0;
-
 protected:
     void setupUniformColor(GrGLSLPPFragmentBuilder* fragBuilder,
                            GrGLSLUniformHandler* uniformHandler,
diff --git a/src/gpu/glsl/GrGLSLProgramBuilder.cpp b/src/gpu/glsl/GrGLSLProgramBuilder.cpp
index 4d442f9..29470c4 100644
--- a/src/gpu/glsl/GrGLSLProgramBuilder.cpp
+++ b/src/gpu/glsl/GrGLSLProgramBuilder.cpp
@@ -53,19 +53,9 @@
     // be sent to the GrGLSLPrimitiveProcessor in its emitCode function
     const GrPrimitiveProcessor& primProc = this->primitiveProcessor();
 
-    for (int i = 0; i < this->pipeline().numFragmentProcessors(); i++) {
-        const GrFragmentProcessor& processor = this->pipeline().getFragmentProcessor(i);
-
-        SkTArray<const GrCoordTransform*, true>& procCoords = fCoordTransforms.push_back();
-        processor.gatherCoordTransforms(&procCoords);
-    }
-
     this->emitAndInstallPrimProc(primProc, inputColor, inputCoverage);
 
-    int numProcs = this->pipeline().numFragmentProcessors();
-    this->emitAndInstallFragProcs(0, this->pipeline().numColorFragmentProcessors(), inputColor);
-    this->emitAndInstallFragProcs(this->pipeline().numColorFragmentProcessors(), numProcs,
-                                  inputCoverage);
+    this->emitAndInstallFragProcs(inputColor, inputCoverage);
     if (primProc.getPixelLocalStorageState() !=
         GrPixelLocalStorageState::kDraw_GrPixelLocalStorageState) {
         this->emitAndInstallXferProc(this->pipeline().getXferProcessor(), *inputColor,
@@ -109,6 +99,8 @@
     SkSTArray<2, SamplerHandle> bufferSamplers(proc.numBuffers());
     this->emitSamplers(proc, &texSamplers, &bufferSamplers);
 
+    GrGLSLPrimitiveProcessor::FPCoordTransformHandler transformHandler(fPipeline,
+                                                                       &fTransformedCoordVars);
     GrGLSLGeometryProcessor::EmitArgs args(&fVS,
                                            &fFS,
                                            this->varyingHandler(),
@@ -120,8 +112,7 @@
                                            distanceVectorName,
                                            texSamplers.begin(),
                                            bufferSamplers.begin(),
-                                           fCoordTransforms,
-                                           &fOutCoords);
+                                           &transformHandler);
     fGeometryProcessor->emitCode(args);
 
     // We have to check that effects and the code they emit are consistent, ie if an effect
@@ -131,14 +122,21 @@
     fFS.codeAppend("}");
 }
 
-void GrGLSLProgramBuilder::emitAndInstallFragProcs(int procOffset,
-                                                   int numProcs,
-                                                   GrGLSLExpr4* inOut) {
-    for (int i = procOffset; i < numProcs; ++i) {
+void GrGLSLProgramBuilder::emitAndInstallFragProcs(GrGLSLExpr4* color, GrGLSLExpr4* coverage) {
+    int transformedCoordVarsIdx = 0;
+    GrGLSLExpr4** inOut = &color;
+    for (int i = 0; i < this->pipeline().numFragmentProcessors(); ++i) {
+        if (i == this->pipeline().numColorFragmentProcessors()) {
+            inOut = &coverage;
+        }
         GrGLSLExpr4 output;
         const GrFragmentProcessor& fp = this->pipeline().getFragmentProcessor(i);
-        this->emitAndInstallFragProc(fp, i, *inOut, &output);
-        *inOut = output;
+        this->emitAndInstallFragProc(fp, i, transformedCoordVarsIdx, **inOut, &output);
+        GrFragmentProcessor::Iter iter(&fp);
+        while (const GrFragmentProcessor* fp = iter.next()) {
+            transformedCoordVarsIdx += fp->numCoordTransforms();
+        }
+        **inOut = output;
     }
 }
 
@@ -146,6 +144,7 @@
 // the fix is to allow effects to take the GrGLSLExpr4 directly
 void GrGLSLProgramBuilder::emitAndInstallFragProc(const GrFragmentProcessor& fp,
                                                   int index,
+                                                  int transformedCoordVarsIdx,
                                                   const GrGLSLExpr4& input,
                                                   GrGLSLExpr4* output) {
     // Program builders have a bit of state we need to clear with each effect
@@ -163,13 +162,15 @@
     SkSTArray<2, SamplerHandle> bufferSamplers(fp.numBuffers());
     this->emitSamplers(fp, &texSamplers, &bufferSamplers);
 
+    const GrShaderVar* coordVars = fTransformedCoordVars.begin() + transformedCoordVarsIdx;
+    GrGLSLFragmentProcessor::TransformedCoordVars coords(&fp, coordVars);
     GrGLSLFragmentProcessor::EmitArgs args(&fFS,
                                            this->uniformHandler(),
                                            this->glslCaps(),
                                            fp,
                                            output->c_str(),
                                            input.isOnes() ? nullptr : input.c_str(),
-                                           fOutCoords[index],
+                                           coords,
                                            texSamplers.begin(),
                                            bufferSamplers.begin(),
                                            this->primitiveProcessor().implementsDistanceVector());
@@ -418,5 +419,4 @@
     this->varyingHandler()->finalize();
     fVS.finalize(kVertex_GrShaderFlag);
     fFS.finalize(kFragment_GrShaderFlag);
-
 }
diff --git a/src/gpu/glsl/GrGLSLProgramBuilder.h b/src/gpu/glsl/GrGLSLProgramBuilder.h
index 27c8437..8a8cff5 100644
--- a/src/gpu/glsl/GrGLSLProgramBuilder.h
+++ b/src/gpu/glsl/GrGLSLProgramBuilder.h
@@ -139,9 +139,10 @@
     void emitAndInstallPrimProc(const GrPrimitiveProcessor&,
                                 GrGLSLExpr4* outputColor,
                                 GrGLSLExpr4* outputCoverage);
-    void emitAndInstallFragProcs(int procOffset, int numProcs, GrGLSLExpr4* inOut);
+    void emitAndInstallFragProcs(GrGLSLExpr4* colorInOut, GrGLSLExpr4* coverageInOut);
     void emitAndInstallFragProc(const GrFragmentProcessor&,
                                 int index,
+                                int transformedCoordVarsIdx,
                                 const GrGLSLExpr4& input,
                                 GrGLSLExpr4* output);
     void emitAndInstallXferProc(const GrXferProcessor&,
@@ -167,11 +168,10 @@
     void verify(const GrFragmentProcessor&);
 #endif
 
-    GrGLSLPrimitiveProcessor::TransformsIn     fCoordTransforms;
-    GrGLSLPrimitiveProcessor::TransformsOut    fOutCoords;
-    int                                        fNumVertexSamplers;
-    int                                        fNumGeometrySamplers;
-    int                                        fNumFragmentSamplers;
+    int                         fNumVertexSamplers;
+    int                         fNumGeometrySamplers;
+    int                         fNumFragmentSamplers;
+    SkSTArray<4, GrShaderVar>   fTransformedCoordVars;
 };
 
 #endif
diff --git a/src/gpu/instanced/InstanceProcessor.cpp b/src/gpu/instanced/InstanceProcessor.cpp
index 2b05ec1..480155b 100644
--- a/src/gpu/instanced/InstanceProcessor.cpp
+++ b/src/gpu/instanced/InstanceProcessor.cpp
@@ -80,7 +80,10 @@
     void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override;
 
 private:
-    void setData(const GrGLSLProgramDataManager&, const GrPrimitiveProcessor&) override {}
+    void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor&,
+                 FPCoordTransformIter&& transformIter) override {
+        this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
+    }
 
     class VertexInputs;
     class Backend;
@@ -388,7 +391,7 @@
     gpArgs->fPositionVar.set(positionType, "deviceCoords");
 
     this->emitTransforms(v, varyingHandler, uniHandler, gpArgs->fPositionVar, localCoords,
-                         args.fTransformsIn, args.fTransformsOut);
+                         args.fFPCoordTransformHandler);
 }
 
 ////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/gpu/vk/GrVkPipelineState.cpp b/src/gpu/vk/GrVkPipelineState.cpp
index 2398385..50250a5 100644
--- a/src/gpu/vk/GrVkPipelineState.cpp
+++ b/src/gpu/vk/GrVkPipelineState.cpp
@@ -194,14 +194,13 @@
 
     SkSTArray<8, const GrTextureAccess*> textureBindings;
 
-    fGeometryProcessor->setData(fDataManager, primProc);
+    fGeometryProcessor->setData(fDataManager, primProc,
+                                GrFragmentProcessor::CoordTransformIter(pipeline));
     append_texture_bindings(primProc, &textureBindings);
 
     for (int i = 0; i < fFragmentProcessors.count(); ++i) {
         const GrFragmentProcessor& processor = pipeline.getFragmentProcessor(i);
         fFragmentProcessors[i]->setData(fDataManager, processor);
-        fGeometryProcessor->setTransformData(primProc, fDataManager, i,
-                                             processor.coordTransforms());
         append_texture_bindings(processor, &textureBindings);
     }