Re-land of "converted GrSimpleTextureEffect to sksl"

This reverts commit baf981f71614e7a2fbe6af2726e65126d431ed8e.

Bug: skia:
Change-Id: I36f6bfb616f1ec2b89043e3a6f7cbdf473bc9588
Reviewed-on: https://skia-review.googlesource.com/22369
Reviewed-by: Greg Daniel <egdaniel@google.com>
Commit-Queue: Ethan Nicholas <ethannicholas@google.com>
diff --git a/gn/sksl.gni b/gn/sksl.gni
index 6dd4deb..5ddf18f 100644
--- a/gn/sksl.gni
+++ b/gn/sksl.gni
@@ -27,4 +27,5 @@
   "$_src/effects/GrAlphaThresholdFragmentProcessor.fp",
   "$_src/effects/GrCircleBlurFragmentProcessor.fp",
   "$_src/gpu/effects/GrDitherEffect.fp",
+  "$_src/gpu/effects/GrSimpleTextureEffect.fp",
 ]
diff --git a/src/effects/GrAlphaThresholdFragmentProcessor.cpp b/src/effects/GrAlphaThresholdFragmentProcessor.cpp
index ede2218..f400777 100644
--- a/src/effects/GrAlphaThresholdFragmentProcessor.cpp
+++ b/src/effects/GrAlphaThresholdFragmentProcessor.cpp
@@ -38,7 +38,7 @@
         fOuterThresholdVar = args.fUniformHandler->addUniform(kFragment_GrShaderFlag, kFloat_GrSLType, kDefault_GrSLPrecision, "outerThreshold");
         SkSL::String sk_TransformedCoords2D_0 = fragBuilder->ensureCoords2D(args.fTransformedCoords[0]);
         SkSL::String sk_TransformedCoords2D_1 = fragBuilder->ensureCoords2D(args.fTransformedCoords[1]);
-        fragBuilder->codeAppendf("vec4 _tmp0;\nvec4 color = (_tmp0 = texture(%s, %s).%s , %s != mat4(1.0) ? vec4(clamp((%s * vec4(_tmp0.xyz, 1.0)).xyz, 0.0, _tmp0.w), _tmp0.w) : _tmp0);\nvec4 mask_color = texture(%s, %s).%s;\nif (mask_color.w < 0.5) {\n    if (color.w > %s) {\n        float scale = %s / color.w;\n        color.xyz *= scale;\n        color.w = %s;\n    }\n} else if (color.w < %s) {\n    float scale = %s / max(0.001, color.w);\n    color.xyz *= scale;\n    color.w = %s;\n}\n%s = color;\n", fragBuilder->getProgramBuilder()->samplerVariable(args.fTexSamplers[0]).c_str(), sk_TransformedCoords2D_0.c_str(), fragBuilder->getProgramBuilder()->samplerSwizzle(args.fTexSamplers[0]).c_str(), fColorSpaceHelper.isValid() ? args.fUniformHandler->getUniformCStr(fColorSpaceHelper.gamutXformUniform()) : "mat4(1.0)", fColorSpaceHelper.isValid() ? args.fUniformHandler->getUniformCStr(fColorSpaceHelper.gamutXformUniform()) : "mat4(1.0)", fragBuilder->getProgramBuilder()->samplerVariable(args.fTexSamplers[1]).c_str(), sk_TransformedCoords2D_1.c_str(), fragBuilder->getProgramBuilder()->samplerSwizzle(args.fTexSamplers[1]).c_str(), args.fUniformHandler->getUniformCStr(fOuterThresholdVar), args.fUniformHandler->getUniformCStr(fOuterThresholdVar), args.fUniformHandler->getUniformCStr(fOuterThresholdVar), args.fUniformHandler->getUniformCStr(fInnerThresholdVar), args.fUniformHandler->getUniformCStr(fInnerThresholdVar), args.fUniformHandler->getUniformCStr(fInnerThresholdVar), args.fOutputColor);
+        fragBuilder->codeAppendf("vec4 _tmpVar1;vec4 color = %stexture(%s, %s).%s%s;\nvec4 mask_color = texture(%s, %s).%s;\nif (mask_color.w < 0.5) {\n    if (color.w > %s) {\n        float scale = %s / color.w;\n        color.xyz *= scale;\n        color.w = %s;\n    }\n} else if (color.w < %s) {\n    float scale = %s / max(0.001, color.w);\n    color.xyz *= scale;\n    color.w = %s;\n}\n%s = color;\n", fColorSpaceHelper.isValid() ? "(_tmpVar1 = " : "", fragBuilder->getProgramBuilder()->samplerVariable(args.fTexSamplers[0]).c_str(), sk_TransformedCoords2D_0.c_str(), fragBuilder->getProgramBuilder()->samplerSwizzle(args.fTexSamplers[0]).c_str(), fColorSpaceHelper.isValid() ? SkStringPrintf(", vec4(clamp((%s * vec4(_tmpVar1.rgb, 1.0)).rgb, 0.0, _tmpVar1.a), _tmpVar1.a))", args.fUniformHandler->getUniformCStr(fColorSpaceHelper.gamutXformUniform())).c_str() : "", fragBuilder->getProgramBuilder()->samplerVariable(args.fTexSamplers[1]).c_str(), sk_TransformedCoords2D_1.c_str(), fragBuilder->getProgramBuilder()->samplerSwizzle(args.fTexSamplers[1]).c_str(), args.fUniformHandler->getUniformCStr(fOuterThresholdVar), args.fUniformHandler->getUniformCStr(fOuterThresholdVar), args.fUniformHandler->getUniformCStr(fOuterThresholdVar), args.fUniformHandler->getUniformCStr(fInnerThresholdVar), args.fUniformHandler->getUniformCStr(fInnerThresholdVar), args.fUniformHandler->getUniformCStr(fInnerThresholdVar), args.fOutputColor);
     }
 private:
     void onSetData(const GrGLSLProgramDataManager& pdman, const GrFragmentProcessor& _proc) override {
diff --git a/src/effects/GrAlphaThresholdFragmentProcessor.fp b/src/effects/GrAlphaThresholdFragmentProcessor.fp
index b576ecf..3c06dbe 100644
--- a/src/effects/GrAlphaThresholdFragmentProcessor.fp
+++ b/src/effects/GrAlphaThresholdFragmentProcessor.fp
@@ -28,20 +28,12 @@
     }
 }
 
-@fields {
-    GrCoordTransform fImageCoordTransform;
-    GrCoordTransform fMaskCoordTransform;
+@coordTransform(image) {
+    SkMatrix::I()
 }
 
-@initializers {
-    fImageCoordTransform(SkMatrix::I(), image.get()),
-    fMaskCoordTransform(SkMatrix::MakeTrans(SkIntToScalar(-bounds.x()), SkIntToScalar(-bounds.y())),
-                        mask.get())
-}
-
-@constructorCode {
-    this->addCoordTransform(&fImageCoordTransform);
-    this->addCoordTransform(&fMaskCoordTransform);
+@coordTransform(mask) {
+    SkMatrix::MakeTrans(SkIntToScalar(-bounds.x()), SkIntToScalar(-bounds.y()))
 }
 
 @header {
diff --git a/src/effects/GrAlphaThresholdFragmentProcessor.h b/src/effects/GrAlphaThresholdFragmentProcessor.h
index 702b6ae..5dd669a 100644
--- a/src/effects/GrAlphaThresholdFragmentProcessor.h
+++ b/src/effects/GrAlphaThresholdFragmentProcessor.h
@@ -16,6 +16,7 @@
     #include "GrColorSpaceXform.h"
 #include "GrFragmentProcessor.h"
 #include "GrCoordTransform.h"
+#include "GrColorSpaceXform.h"
 #include "effects/GrProxyMove.h"
 class GrAlphaThresholdFragmentProcessor : public GrFragmentProcessor {
 public:
@@ -44,35 +45,34 @@
     const SkIRect& bounds
 )
     : INHERITED(kNone_OptimizationFlags)
-    , 
-    fImageCoordTransform(SkMatrix::I(), image.get()),
-    fMaskCoordTransform(SkMatrix::MakeTrans(SkIntToScalar(-bounds.x()), SkIntToScalar(-bounds.y())),
-                        mask.get())
-
     , fImage(std::move(image))
     , fColorXform(colorXform)
     , fMask(std::move(mask))
     , fInnerThreshold(innerThreshold)
-    , fOuterThreshold(outerThreshold) {
-
-    this->addCoordTransform(&fImageCoordTransform);
-    this->addCoordTransform(&fMaskCoordTransform);
+    , fOuterThreshold(outerThreshold)
+    , fImageCoordTransform(
+    SkMatrix::I()
+, fImage.proxy())
+    , fMaskCoordTransform(
+    SkMatrix::MakeTrans(SkIntToScalar(-bounds.x()), SkIntToScalar(-bounds.y()))
+, fMask.proxy()) {
         this->addTextureSampler(&fImage);
         this->addTextureSampler(&fMask);
+        this->addCoordTransform(&fImageCoordTransform);
+        this->addCoordTransform(&fMaskCoordTransform);
         this->initClassID<GrAlphaThresholdFragmentProcessor>();
     }
     GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
     void onGetGLSLProcessorKey(const GrShaderCaps&,GrProcessorKeyBuilder*) const override;
     bool onIsEqual(const GrFragmentProcessor&) const override;
     GR_DECLARE_FRAGMENT_PROCESSOR_TEST
-
-    GrCoordTransform fImageCoordTransform;
-    GrCoordTransform fMaskCoordTransform;
     TextureSampler fImage;
     sk_sp<GrColorSpaceXform> fColorXform;
     TextureSampler fMask;
     float fInnerThreshold;
     float fOuterThreshold;
+    GrCoordTransform fImageCoordTransform;
+    GrCoordTransform fMaskCoordTransform;
     typedef GrFragmentProcessor INHERITED;
 };
 #endif
diff --git a/src/effects/GrCircleBlurFragmentProcessor.h b/src/effects/GrCircleBlurFragmentProcessor.h
index c01ea9b..72632d2 100644
--- a/src/effects/GrCircleBlurFragmentProcessor.h
+++ b/src/effects/GrCircleBlurFragmentProcessor.h
@@ -14,6 +14,7 @@
 #if SK_SUPPORT_GPU
 #include "GrFragmentProcessor.h"
 #include "GrCoordTransform.h"
+#include "GrColorSpaceXform.h"
 #include "effects/GrProxyMove.h"
 class GrCircleBlurFragmentProcessor : public GrFragmentProcessor {
 public:
diff --git a/src/gpu/effects/GrDitherEffect.h b/src/gpu/effects/GrDitherEffect.h
index d0bf9a9..a996ad9 100644
--- a/src/gpu/effects/GrDitherEffect.h
+++ b/src/gpu/effects/GrDitherEffect.h
@@ -14,6 +14,7 @@
 #if SK_SUPPORT_GPU
 #include "GrFragmentProcessor.h"
 #include "GrCoordTransform.h"
+#include "GrColorSpaceXform.h"
 #include "effects/GrProxyMove.h"
 class GrDitherEffect : public GrFragmentProcessor {
 public:
diff --git a/src/gpu/effects/GrSimpleTextureEffect.cpp b/src/gpu/effects/GrSimpleTextureEffect.cpp
index 938fb04..181d0f7 100644
--- a/src/gpu/effects/GrSimpleTextureEffect.cpp
+++ b/src/gpu/effects/GrSimpleTextureEffect.cpp
@@ -1,111 +1,81 @@
 /*
- * Copyright 2012 Google Inc.
+ * Copyright 2017 Google Inc.
  *
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
 
+/*
+ * This file was autogenerated from GrSimpleTextureEffect.fp; do not modify.
+ */
 #include "GrSimpleTextureEffect.h"
-#include "GrProxyMove.h"
+#if SK_SUPPORT_GPU
 #include "glsl/GrGLSLColorSpaceXformHelper.h"
 #include "glsl/GrGLSLFragmentProcessor.h"
 #include "glsl/GrGLSLFragmentShaderBuilder.h"
-
-GrSimpleTextureEffect::GrSimpleTextureEffect(sk_sp<GrTextureProxy> proxy,
-                                             sk_sp<GrColorSpaceXform> colorSpaceXform,
-                                             const SkMatrix& matrix,
-                                             GrSamplerParams::FilterMode filterMode)
-        : INHERITED{ModulationFlags(proxy->config()),
-                    GR_PROXY_MOVE(proxy),
-                    std::move(colorSpaceXform),
-                    matrix,
-                    filterMode} {
-    this->initClassID<GrSimpleTextureEffect>();
-}
-
-GrSimpleTextureEffect::GrSimpleTextureEffect(sk_sp<GrTextureProxy> proxy,
-                                             sk_sp<GrColorSpaceXform> colorSpaceXform,
-                                             const SkMatrix& matrix,
-                                             const GrSamplerParams& params)
-        : INHERITED{ModulationFlags(proxy->config()),
-                    GR_PROXY_MOVE(proxy),
-                    std::move(colorSpaceXform),
-                    matrix,
-                    params} {
-    this->initClassID<GrSimpleTextureEffect>();
-}
-
-class GrGLSimpleTextureEffect : public GrGLSLFragmentProcessor {
+#include "glsl/GrGLSLProgramBuilder.h"
+#include "SkSLCPP.h"
+#include "SkSLUtil.h"
+class GrGLSLSimpleTextureEffect : public GrGLSLFragmentProcessor {
 public:
+    GrGLSLSimpleTextureEffect() {}
     void emitCode(EmitArgs& args) override {
-        const GrSimpleTextureEffect& textureEffect = args.fFp.cast<GrSimpleTextureEffect>();
-        fColorSpaceHelper.emitCode(args.fUniformHandler, textureEffect.colorSpaceXform());
-
         GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
-        fragBuilder->codeAppendf("%s = ", args.fOutputColor);
-        fragBuilder->appendTextureLookupAndModulate(args.fInputColor,
-                                                    args.fTexSamplers[0],
-                                                    args.fTransformedCoords[0].c_str(),
-                                                    args.fTransformedCoords[0].getType(),
-                                                    &fColorSpaceHelper);
-        fragBuilder->codeAppend(";");
+        const GrSimpleTextureEffect& _outer = args.fFp.cast<GrSimpleTextureEffect>();
+        (void) _outer;
+        fColorSpaceHelper.emitCode(args.fUniformHandler, _outer.colorXform().get());
+        SkSL::String sk_TransformedCoords2D_0 = fragBuilder->ensureCoords2D(args.fTransformedCoords[0]);
+        fragBuilder->codeAppendf("vec4 _tmpVar1;%s = %s * %stexture(%s, %s).%s%s;\n", args.fOutputColor, args.fInputColor ? args.fInputColor : "vec4(1)", fColorSpaceHelper.isValid() ? "(_tmpVar1 = " : "", fragBuilder->getProgramBuilder()->samplerVariable(args.fTexSamplers[0]).c_str(), sk_TransformedCoords2D_0.c_str(), fragBuilder->getProgramBuilder()->samplerSwizzle(args.fTexSamplers[0]).c_str(), fColorSpaceHelper.isValid() ? SkStringPrintf(", vec4(clamp((%s * vec4(_tmpVar1.rgb, 1.0)).rgb, 0.0, _tmpVar1.a), _tmpVar1.a))", args.fUniformHandler->getUniformCStr(fColorSpaceHelper.gamutXformUniform())).c_str() : "");
     }
-
-    static inline void GenKey(const GrProcessor& effect, const GrShaderCaps&,
-                              GrProcessorKeyBuilder* b) {
-        const GrSimpleTextureEffect& textureEffect = effect.cast<GrSimpleTextureEffect>();
-        b->add32(GrColorSpaceXform::XformKey(textureEffect.colorSpaceXform()));
-    }
-
-protected:
-    void onSetData(const GrGLSLProgramDataManager& pdman,
-                   const GrFragmentProcessor& processor) override {
-        const GrSimpleTextureEffect& textureEffect = processor.cast<GrSimpleTextureEffect>();
-        if (SkToBool(textureEffect.colorSpaceXform())) {
-            fColorSpaceHelper.setData(pdman, textureEffect.colorSpaceXform());
+private:
+    void onSetData(const GrGLSLProgramDataManager& pdman, const GrFragmentProcessor& _proc) override {
+        const GrSimpleTextureEffect& _outer = _proc.cast<GrSimpleTextureEffect>();
+        {
+        if (fColorSpaceHelper.isValid()) {
+            fColorSpaceHelper.setData(pdman, _outer.colorXform().get());
+        }
         }
     }
-
-private:
-    typedef GrGLSLFragmentProcessor INHERITED;
-
+    UniformHandle fImageVar;
     GrGLSLColorSpaceXformHelper fColorSpaceHelper;
 };
-
-///////////////////////////////////////////////////////////////////////////////
-
-void GrSimpleTextureEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps,
-                                                  GrProcessorKeyBuilder* b) const {
-    GrGLSimpleTextureEffect::GenKey(*this, caps, b);
+GrGLSLFragmentProcessor* GrSimpleTextureEffect::onCreateGLSLInstance() const {
+    return new GrGLSLSimpleTextureEffect();
 }
-
-GrGLSLFragmentProcessor* GrSimpleTextureEffect::onCreateGLSLInstance() const  {
-    return new GrGLSimpleTextureEffect;
+void GrSimpleTextureEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const {
+    b->add32(GrColorSpaceXform::XformKey(fColorXform.get()));
 }
-
-///////////////////////////////////////////////////////////////////////////////
-
+bool GrSimpleTextureEffect::onIsEqual(const GrFragmentProcessor& other) const {
+    const GrSimpleTextureEffect& that = other.cast<GrSimpleTextureEffect>();
+    (void) that;
+    if (fImage != that.fImage) return false;
+    if (fColorXform != that.fColorXform) return false;
+    if (fMatrix != that.fMatrix) return false;
+    return true;
+}
 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrSimpleTextureEffect);
-
 #if GR_TEST_UTILS
-sk_sp<GrFragmentProcessor> GrSimpleTextureEffect::TestCreate(GrProcessorTestData* d) {
-    int texIdx = d->fRandom->nextBool() ? GrProcessorUnitTest::kSkiaPMTextureIdx
-                                        : GrProcessorUnitTest::kAlphaTextureIdx;
+sk_sp<GrFragmentProcessor> GrSimpleTextureEffect::TestCreate(GrProcessorTestData* testData) {
+
+    int texIdx = testData->fRandom->nextBool() ? GrProcessorUnitTest::kSkiaPMTextureIdx
+                                               : GrProcessorUnitTest::kAlphaTextureIdx;
     static const SkShader::TileMode kTileModes[] = {
         SkShader::kClamp_TileMode,
         SkShader::kRepeat_TileMode,
         SkShader::kMirror_TileMode,
     };
     SkShader::TileMode tileModes[] = {
-        kTileModes[d->fRandom->nextULessThan(SK_ARRAY_COUNT(kTileModes))],
-        kTileModes[d->fRandom->nextULessThan(SK_ARRAY_COUNT(kTileModes))],
+        kTileModes[testData->fRandom->nextULessThan(SK_ARRAY_COUNT(kTileModes))],
+        kTileModes[testData->fRandom->nextULessThan(SK_ARRAY_COUNT(kTileModes))],
     };
-    GrSamplerParams params(tileModes, d->fRandom->nextBool() ? GrSamplerParams::kBilerp_FilterMode
-                                                             : GrSamplerParams::kNone_FilterMode);
+    GrSamplerParams params(tileModes, testData->fRandom->nextBool()
+                                                               ? GrSamplerParams::kBilerp_FilterMode
+                                                               : GrSamplerParams::kNone_FilterMode);
 
-    const SkMatrix& matrix = GrTest::TestMatrix(d->fRandom);
-    sk_sp<GrColorSpaceXform> colorSpaceXform = GrTest::TestColorXform(d->fRandom);
-    return GrSimpleTextureEffect::Make(d->textureProxy(texIdx),
-                                       std::move(colorSpaceXform), matrix);
+    const SkMatrix& matrix = GrTest::TestMatrix(testData->fRandom);
+    sk_sp<GrColorSpaceXform> colorSpaceXform = GrTest::TestColorXform(testData->fRandom);
+    return GrSimpleTextureEffect::Make(testData->textureProxy(texIdx), std::move(colorSpaceXform),
+                                       matrix);
 }
 #endif
+#endif
diff --git a/src/gpu/effects/GrSimpleTextureEffect.fp b/src/gpu/effects/GrSimpleTextureEffect.fp
new file mode 100644
index 0000000..2094444
--- /dev/null
+++ b/src/gpu/effects/GrSimpleTextureEffect.fp
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+in uniform sampler2D image;
+in uniform colorSpaceXform colorXform;
+in mat4 matrix;
+
+@constructorParams {
+    GrSamplerParams samplerParams
+}
+
+@coordTransform(image) {
+    matrix
+}
+
+@samplerParams(image) {
+    samplerParams
+}
+
+@make {
+    static sk_sp<GrFragmentProcessor> Make(sk_sp<GrTextureProxy> proxy,
+                                           sk_sp<GrColorSpaceXform> colorSpaceXform,
+                                           const SkMatrix& matrix) {
+        return sk_sp<GrFragmentProcessor>(
+            new GrSimpleTextureEffect(std::move(proxy), std::move(colorSpaceXform), matrix,
+                    GrSamplerParams(SkShader::kClamp_TileMode, GrSamplerParams::kNone_FilterMode)));
+    }
+
+    /* clamp mode */
+    static sk_sp<GrFragmentProcessor> Make(sk_sp<GrTextureProxy> proxy,
+                                           sk_sp<GrColorSpaceXform> colorSpaceXform,
+                                           const SkMatrix& matrix,
+                                           GrSamplerParams::FilterMode filterMode) {
+        return sk_sp<GrFragmentProcessor>(
+            new GrSimpleTextureEffect(std::move(proxy), std::move(colorSpaceXform), matrix,
+                                      GrSamplerParams(SkShader::kClamp_TileMode, filterMode)));
+     }
+
+    static sk_sp<GrFragmentProcessor> Make(sk_sp<GrTextureProxy> proxy,
+                                           sk_sp<GrColorSpaceXform> colorSpaceXform,
+                                           const SkMatrix& matrix,
+                                           const GrSamplerParams& p) {
+        return sk_sp<GrFragmentProcessor>(
+            new GrSimpleTextureEffect(std::move(proxy), std::move(colorSpaceXform), matrix, p));
+    }
+}
+
+@optimizationFlags {
+    kCompatibleWithCoverageAsAlpha_OptimizationFlag |
+    (GrPixelConfigIsOpaque(image->config()) ? kPreservesOpaqueInput_OptimizationFlag :
+                                              kNone_OptimizationFlags)
+}
+
+void main() {
+    sk_OutColor = sk_InColor * texture(image, sk_TransformedCoords2D[0], colorXform);
+}
+
+@test(testData) {
+    int texIdx = testData->fRandom->nextBool() ? GrProcessorUnitTest::kSkiaPMTextureIdx
+                                               : GrProcessorUnitTest::kAlphaTextureIdx;
+    static const SkShader::TileMode kTileModes[] = {
+        SkShader::kClamp_TileMode,
+        SkShader::kRepeat_TileMode,
+        SkShader::kMirror_TileMode,
+    };
+    SkShader::TileMode tileModes[] = {
+        kTileModes[testData->fRandom->nextULessThan(SK_ARRAY_COUNT(kTileModes))],
+        kTileModes[testData->fRandom->nextULessThan(SK_ARRAY_COUNT(kTileModes))],
+    };
+    GrSamplerParams params(tileModes, testData->fRandom->nextBool()
+                                                               ? GrSamplerParams::kBilerp_FilterMode
+                                                               : GrSamplerParams::kNone_FilterMode);
+
+    const SkMatrix& matrix = GrTest::TestMatrix(testData->fRandom);
+    sk_sp<GrColorSpaceXform> colorSpaceXform = GrTest::TestColorXform(testData->fRandom);
+    return GrSimpleTextureEffect::Make(testData->textureProxy(texIdx), std::move(colorSpaceXform),
+                                       matrix);
+}
diff --git a/src/gpu/effects/GrSimpleTextureEffect.h b/src/gpu/effects/GrSimpleTextureEffect.h
index 5013e3d..aed91d7 100644
--- a/src/gpu/effects/GrSimpleTextureEffect.h
+++ b/src/gpu/effects/GrSimpleTextureEffect.h
@@ -1,77 +1,82 @@
 /*
- * Copyright 2013 Google Inc.
+ * Copyright 2017 Google Inc.
  *
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
 
+/*
+ * This file was autogenerated from GrSimpleTextureEffect.fp; do not modify.
+ */
 #ifndef GrSimpleTextureEffect_DEFINED
 #define GrSimpleTextureEffect_DEFINED
-
-#include "GrSingleTextureEffect.h"
-#include "GrTextureProxy.h"
-
-class GrInvariantOutput;
-
-/**
- * The output color of this effect is a modulation of the input color and a sample from a texture.
- * It allows explicit specification of the filtering and wrap modes (GrSamplerParams) and accepts
- * a matrix that is used to compute texture coordinates from local coordinates.
- */
-class GrSimpleTextureEffect : public GrSingleTextureEffect {
+#include "SkTypes.h"
+#if SK_SUPPORT_GPU
+#include "GrFragmentProcessor.h"
+#include "GrCoordTransform.h"
+#include "GrColorSpaceXform.h"
+#include "effects/GrProxyMove.h"
+class GrSimpleTextureEffect : public GrFragmentProcessor {
 public:
-    /* unfiltered, clamp mode */
+    sk_sp<GrColorSpaceXform> colorXform() const { return fColorXform; }
+    SkMatrix44 matrix() const { return fMatrix; }
+
     static sk_sp<GrFragmentProcessor> Make(sk_sp<GrTextureProxy> proxy,
                                            sk_sp<GrColorSpaceXform> colorSpaceXform,
                                            const SkMatrix& matrix) {
         return sk_sp<GrFragmentProcessor>(
-            new GrSimpleTextureEffect(std::move(proxy),
-                                      std::move(colorSpaceXform), matrix,
-                                      GrSamplerParams::kNone_FilterMode));
+            new GrSimpleTextureEffect(std::move(proxy), std::move(colorSpaceXform), matrix,
+                    GrSamplerParams(SkShader::kClamp_TileMode, GrSamplerParams::kNone_FilterMode)));
     }
 
-    /* clamp mode */
+    
     static sk_sp<GrFragmentProcessor> Make(sk_sp<GrTextureProxy> proxy,
                                            sk_sp<GrColorSpaceXform> colorSpaceXform,
                                            const SkMatrix& matrix,
                                            GrSamplerParams::FilterMode filterMode) {
         return sk_sp<GrFragmentProcessor>(
-            new GrSimpleTextureEffect(std::move(proxy),
-                                      std::move(colorSpaceXform),
-                                      matrix, filterMode));
-    }
+            new GrSimpleTextureEffect(std::move(proxy), std::move(colorSpaceXform), matrix,
+                                      GrSamplerParams(SkShader::kClamp_TileMode, filterMode)));
+     }
 
     static sk_sp<GrFragmentProcessor> Make(sk_sp<GrTextureProxy> proxy,
                                            sk_sp<GrColorSpaceXform> colorSpaceXform,
                                            const SkMatrix& matrix,
                                            const GrSamplerParams& p) {
-        return sk_sp<GrFragmentProcessor>(new GrSimpleTextureEffect(std::move(proxy),
-                                                                    std::move(colorSpaceXform),
-                                                                    matrix, p));
+        return sk_sp<GrFragmentProcessor>(
+            new GrSimpleTextureEffect(std::move(proxy), std::move(colorSpaceXform), matrix, p));
     }
-
-    ~GrSimpleTextureEffect() override {}
-
-    const char* name() const override { return "SimpleTexture"; }
-
+    const char* name() const override { return "SimpleTextureEffect"; }
 private:
-    GrSimpleTextureEffect(sk_sp<GrTextureProxy>,
-                          sk_sp<GrColorSpaceXform>, const SkMatrix& matrix,
-                          GrSamplerParams::FilterMode);
-
-    GrSimpleTextureEffect(sk_sp<GrTextureProxy>,
-                          sk_sp<GrColorSpaceXform>, const SkMatrix& matrix,
-                          const GrSamplerParams&);
-
+    GrSimpleTextureEffect(sk_sp<GrTextureProxy> image, sk_sp<GrColorSpaceXform> colorXform, SkMatrix44 matrix, 
+    GrSamplerParams samplerParams
+)
+    : INHERITED((OptimizationFlags) 
+    kCompatibleWithCoverageAsAlpha_OptimizationFlag |
+    (GrPixelConfigIsOpaque(image->config()) ? kPreservesOpaqueInput_OptimizationFlag :
+                                              kNone_OptimizationFlags)
+)
+    , fImage(std::move(image), 
+    samplerParams
+)
+    , fColorXform(colorXform)
+    , fMatrix(matrix)
+    , fImageCoordTransform(
+    matrix
+, fImage.proxy()) {
+        this->addTextureSampler(&fImage);
+        this->addCoordTransform(&fImageCoordTransform);
+        this->initClassID<GrSimpleTextureEffect>();
+    }
     GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
-
-    void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override;
-
-    bool onIsEqual(const GrFragmentProcessor& other) const override { return true; }
-
+    void onGetGLSLProcessorKey(const GrShaderCaps&,GrProcessorKeyBuilder*) const override;
+    bool onIsEqual(const GrFragmentProcessor&) const override;
     GR_DECLARE_FRAGMENT_PROCESSOR_TEST
-
-    typedef GrSingleTextureEffect INHERITED;
+    TextureSampler fImage;
+    sk_sp<GrColorSpaceXform> fColorXform;
+    SkMatrix44 fMatrix;
+    GrCoordTransform fImageCoordTransform;
+    typedef GrFragmentProcessor INHERITED;
 };
-
+#endif
 #endif
diff --git a/src/sksl/README b/src/sksl/README
index c2718ad..f590bbe 100644
--- a/src/sksl/README
+++ b/src/sksl/README
@@ -89,6 +89,11 @@
                         the name of the GrGLSLProgramDataManager)
     @test(<testData>)  (the body of the TestCreate function, where <testData> is
                         the name of the GrProcessorTestData* parameter)
+    @coordTransform(<sampler>)
+                       (the matrix to attach to the named sampler2D's
+                        GrCoordTransform)
+    @samplerParams(<sampler>)
+                       (the sampler params to attach to the named sampler2D)
 * global 'in' variables represent data passed to the fragment processor at
   construction time. These variables become constructor parameters and are
   stored in fragment processor fields. vec2s map to SkPoints, and vec4s map to
diff --git a/src/sksl/SkSLCPPCodeGenerator.cpp b/src/sksl/SkSLCPPCodeGenerator.cpp
index 72dcd01..238615d 100644
--- a/src/sksl/SkSLCPPCodeGenerator.cpp
+++ b/src/sksl/SkSLCPPCodeGenerator.cpp
@@ -163,7 +163,7 @@
 
 String CPPCodeGenerator::getSamplerHandle(const Variable& var) {
     int samplerCount = 0;
-    for (const auto param : fSectionAndParameterHelper.fParameters) {
+    for (const auto param : fSectionAndParameterHelper.getParameters()) {
         if (&var == param) {
             return "args.fTexSamplers[" + to_string(samplerCount) + "]";
         }
@@ -226,6 +226,20 @@
 }
 
 void CPPCodeGenerator::writeFunctionCall(const FunctionCall& c) {
+    if (c.fFunction.fBuiltin && c.fFunction.fName == "COLORSPACE") {
+        String tmpVar = "_tmpVar" + to_string(++fVarCount);
+        fFunctionHeader += "vec4 " + tmpVar + ";";
+        ASSERT(c.fArguments.size() == 2);
+        this->write("%s");
+        fFormatArgs.push_back("fColorSpaceHelper.isValid() ? \"(" + tmpVar + " = \" : \"\"");
+        this->writeExpression(*c.fArguments[0], kTopLevel_Precedence);
+        ASSERT(c.fArguments[1]->fKind == Expression::kVariableReference_Kind);
+        String xform("args.fUniformHandler->getUniformCStr(fColorSpaceHelper.gamutXformUniform())");
+        this->write("%s");
+        fFormatArgs.push_back("fColorSpaceHelper.isValid() ? SkStringPrintf(\", vec4(clamp((%s * vec4(" + tmpVar + ".rgb, 1.0)).rgb, 0.0, " + tmpVar +
+                              ".a), " + tmpVar + ".a))\", " + xform + ").c_str() : \"\"");
+        return;
+    }
     INHERITED::writeFunctionCall(c);
     if (c.fFunction.fBuiltin && c.fFunction.fName == "texture") {
         this->write(".%s");
@@ -267,9 +281,9 @@
 }
 
 void CPPCodeGenerator::writeSection(const char* name, const char* prefix) {
-    const auto found = fSectionAndParameterHelper.fSections.find(String(name));
-    if (found != fSectionAndParameterHelper.fSections.end()) {
-        this->writef("%s%s", prefix, found->second->fText.c_str());
+    const Section* s = fSectionAndParameterHelper.getSection(name);
+    if (s) {
+        this->writef("%s%s", prefix, s->fText.c_str());
     }
 }
 
@@ -398,10 +412,8 @@
 
 void CPPCodeGenerator::writeSetData(std::vector<const Variable*>& uniforms) {
     const char* fullName = fFullName.c_str();
-    auto section = fSectionAndParameterHelper.fSections.find(String(SET_DATA_SECTION));
-    const char* pdman = section != fSectionAndParameterHelper.fSections.end() ?
-                                                                section->second->fArgument.c_str() :
-                                                                "pdman";
+    const Section* section = fSectionAndParameterHelper.getSection(SET_DATA_SECTION);
+    const char* pdman = section ? section->fArgument.c_str() : "pdman";
     this->writef("    void onSetData(const GrGLSLProgramDataManager& %s, "
                                     "const GrFragmentProcessor& _proc) override {\n",
                  pdman);
@@ -439,7 +451,7 @@
     if (wroteProcessor) {
         this->writef("        }\n");
     }
-    if (section != fSectionAndParameterHelper.fSections.end()) {
+    if (section) {
         for (const auto& p : fProgram.fElements) {
             if (ProgramElement::kVar_Kind == p->fKind) {
                 const VarDeclarations* decls = (const VarDeclarations*) p.get();
@@ -470,27 +482,25 @@
 }
 
 void CPPCodeGenerator::writeTest() {
-    const auto found = fSectionAndParameterHelper.fSections.find(TEST_CODE_SECTION);
-    if (found == fSectionAndParameterHelper.fSections.end()) {
-        return;
+    const Section* test = fSectionAndParameterHelper.getSection(TEST_CODE_SECTION);
+    if (test) {
+        this->writef("GR_DEFINE_FRAGMENT_PROCESSOR_TEST(%s);\n"
+                     "#if GR_TEST_UTILS\n"
+                     "sk_sp<GrFragmentProcessor> %s::TestCreate(GrProcessorTestData* %s) {\n",
+                     fFullName.c_str(),
+                     fFullName.c_str(),
+                     test->fArgument.c_str());
+        this->writeSection(TEST_CODE_SECTION);
+        this->write("}\n"
+                    "#endif\n");
     }
-    const Section* test = found->second;
-    this->writef("GR_DEFINE_FRAGMENT_PROCESSOR_TEST(%s);\n"
-                 "#if GR_TEST_UTILS\n"
-                 "sk_sp<GrFragmentProcessor> %s::TestCreate(GrProcessorTestData* %s) {\n",
-                 fFullName.c_str(),
-                 fFullName.c_str(),
-                 test->fArgument.c_str());
-    this->writeSection(TEST_CODE_SECTION);
-    this->write("}\n"
-                "#endif\n");
 }
 
 void CPPCodeGenerator::writeGetKey() {
     this->writef("void %s::onGetGLSLProcessorKey(const GrShaderCaps& caps, "
                                                 "GrProcessorKeyBuilder* b) const {\n",
                  fFullName.c_str());
-    for (const auto& param : fSectionAndParameterHelper.fParameters) {
+    for (const auto& param : fSectionAndParameterHelper.getParameters()) {
         const char* name = param->fName.c_str();
         if (param->fType == *fContext.fColorSpaceXform_Type) {
             this->writef("    b->add32(GrColorSpaceXform::XformKey(%s.get()));\n",
@@ -580,7 +590,7 @@
             this->writef("    UniformHandle %sVar;\n", HCodeGenerator::FieldName(name).c_str());
         }
     }
-    for (const auto& param : fSectionAndParameterHelper.fParameters) {
+    for (const auto& param : fSectionAndParameterHelper.getParameters()) {
         const char* name = param->fName.c_str();
         if (needs_uniform_var(*param)) {
             this->writef("    UniformHandle %sVar;\n", HCodeGenerator::FieldName(name).c_str());
@@ -599,7 +609,7 @@
                  "    const %s& that = other.cast<%s>();\n"
                  "    (void) that;\n",
                  fullName, fullName, fullName);
-    for (const auto& param : fSectionAndParameterHelper.fParameters) {
+    for (const auto& param : fSectionAndParameterHelper.getParameters()) {
         const char* name = param->fName.c_str();
         this->writef("    if (%s != that.%s) return false;\n",
                      HCodeGenerator::FieldName(name).c_str(),
diff --git a/src/sksl/SkSLHCodeGenerator.cpp b/src/sksl/SkSLHCodeGenerator.cpp
index cd3f7f2..fdc2b3b 100644
--- a/src/sksl/SkSLHCodeGenerator.cpp
+++ b/src/sksl/SkSLHCodeGenerator.cpp
@@ -69,9 +69,9 @@
 }
 
 bool HCodeGenerator::writeSection(const char* name, const char* prefix) {
-    const auto found = fSectionAndParameterHelper.fSections.find(String(name));
-    if (found != fSectionAndParameterHelper.fSections.end()) {
-        this->writef("%s%s", prefix, found->second->fText.c_str());
+    const Section* s = fSectionAndParameterHelper.getSection(name);
+    if (s) {
+        this->writef("%s%s", prefix, s->fText.c_str());
         return true;
     }
     return false;
@@ -81,10 +81,9 @@
     // super-simple parse, just assume the last token before a comma is the name of a parameter
     // (which is true as long as there are no multi-parameter template types involved). Will replace
     // this with something more robust if the need arises.
-    const auto found = fSectionAndParameterHelper.fSections.find(
-                                                                String(CONSTRUCTOR_PARAMS_SECTION));
-    if (found != fSectionAndParameterHelper.fSections.end()) {
-        const char* s = found->second->fText.c_str();
+    const Section* section = fSectionAndParameterHelper.getSection(CONSTRUCTOR_PARAMS_SECTION);
+    if (section) {
+        const char* s = section->fText.c_str();
         #define BUFFER_SIZE 64
         char lastIdentifier[BUFFER_SIZE];
         int lastIdentifierLength = 0;
@@ -126,7 +125,7 @@
     if (!this->writeSection(MAKE_SECTION)) {
         this->writef("    static sk_sp<GrFragmentProcessor> Make(");
         separator = "";
-        for (const auto& param : fSectionAndParameterHelper.fParameters) {
+        for (const auto& param : fSectionAndParameterHelper.getParameters()) {
             this->writef("%s%s %s", separator, ParameterType(param->fType).c_str(),
                          param->fName.c_str());
             separator = ", ";
@@ -136,7 +135,7 @@
                      "        return sk_sp<GrFragmentProcessor>(new %s(",
                      fFullName.c_str());
         separator = "";
-        for (const auto& param : fSectionAndParameterHelper.fParameters) {
+        for (const auto& param : fSectionAndParameterHelper.getParameters()) {
             this->writef("%s%s", separator, param->fName.c_str());
             separator = ", ";
         }
@@ -146,13 +145,25 @@
     }
 }
 
+void HCodeGenerator::failOnSection(const char* section, const char* msg) {
+    std::vector<const Section*> s = fSectionAndParameterHelper.getSections(section);
+    if (s.size()) {
+        fErrors.error(s[0]->fPosition, String("@") + section + " " + msg);
+    }
+}
+
 void HCodeGenerator::writeConstructor() {
     if (this->writeSection(CONSTRUCTOR_SECTION)) {
-        return;
+        const char* msg = "may not be present when constructor is overridden";
+        this->failOnSection(CONSTRUCTOR_CODE_SECTION, msg);
+        this->failOnSection(CONSTRUCTOR_PARAMS_SECTION, msg);
+        this->failOnSection(COORD_TRANSFORM_SECTION, msg);
+        this->failOnSection(INITIALIZERS_SECTION, msg);
+        this->failOnSection(OPTIMIZATION_FLAGS_SECTION, msg);
     }
     this->writef("    %s(", fFullName.c_str());
     const char* separator = "";
-    for (const auto& param : fSectionAndParameterHelper.fParameters) {
+    for (const auto& param : fSectionAndParameterHelper.getParameters()) {
         this->writef("%s%s %s", separator, ParameterType(param->fType).c_str(),
                      param->fName.c_str());
         separator = ", ";
@@ -165,23 +176,38 @@
     }
     this->writef(")");
     this->writeSection(INITIALIZERS_SECTION, "\n    , ");
-    for (const auto& param : fSectionAndParameterHelper.fParameters) {
+    for (const auto& param : fSectionAndParameterHelper.getParameters()) {
         const char* name = param->fName.c_str();
         if (param->fType.kind() == Type::kSampler_Kind) {
-            this->writef("\n    , %s(std::move(%s))", FieldName(name).c_str(),
-                         name);
+            this->writef("\n    , %s(std::move(%s)", FieldName(name).c_str(), name);
+            for (const Section* s : fSectionAndParameterHelper.getSections(
+                                                                          SAMPLER_PARAMS_SECTION)) {
+                if (s->fArgument == name) {
+                    this->writef(", %s", s->fText.c_str());
+                }
+            }
+            this->writef(")");
         } else {
             this->writef("\n    , %s(%s)", FieldName(name).c_str(), name);
         }
     }
+    for (const Section* s : fSectionAndParameterHelper.getSections(COORD_TRANSFORM_SECTION)) {
+        String field = FieldName(s->fArgument.c_str());
+        this->writef("\n    , %sCoordTransform(%s, %s.proxy())", field.c_str(), s->fText.c_str(),
+                     field.c_str());
+    }
     this->writef(" {\n");
     this->writeSection(CONSTRUCTOR_CODE_SECTION);
-    for (const auto& param : fSectionAndParameterHelper.fParameters) {
+    for (const auto& param : fSectionAndParameterHelper.getParameters()) {
         if (param->fType.kind() == Type::kSampler_Kind) {
             this->writef("        this->addTextureSampler(&%s);\n",
                          FieldName(param->fName.c_str()).c_str());
         }
     }
+    for (const Section* s : fSectionAndParameterHelper.getSections(COORD_TRANSFORM_SECTION)) {
+        String field = FieldName(s->fArgument.c_str());
+        this->writef("        this->addCoordTransform(&%sCoordTransform);\n", field.c_str());
+    }
     this->writef("        this->initClassID<%s>();\n"
                  "    }\n",
                  fFullName.c_str());
@@ -189,10 +215,14 @@
 
 void HCodeGenerator::writeFields() {
     this->writeSection(FIELDS_SECTION);
-    for (const auto& param : fSectionAndParameterHelper.fParameters) {
+    for (const auto& param : fSectionAndParameterHelper.getParameters()) {
         const char* name = param->fName.c_str();
         this->writef("    %s %s;\n", FieldType(param->fType).c_str(), FieldName(name).c_str());
     }
+    for (const Section* s : fSectionAndParameterHelper.getSections(COORD_TRANSFORM_SECTION)) {
+        this->writef("    GrCoordTransform %sCoordTransform;\n",
+                     FieldName(s->fArgument.c_str()).c_str());
+    }
 }
 
 bool HCodeGenerator::generateCode() {
@@ -206,12 +236,13 @@
     this->writeSection(HEADER_SECTION);
     this->writef("#include \"GrFragmentProcessor.h\"\n"
                  "#include \"GrCoordTransform.h\"\n"
+                 "#include \"GrColorSpaceXform.h\"\n"
                  "#include \"effects/GrProxyMove.h\"\n");
     this->writef("class %s : public GrFragmentProcessor {\n"
                  "public:\n",
                  fFullName.c_str());
     this->writeSection(CLASS_SECTION);
-    for (const auto& param : fSectionAndParameterHelper.fParameters) {
+    for (const auto& param : fSectionAndParameterHelper.getParameters()) {
         if (param->fType.kind() == Type::kSampler_Kind) {
             continue;
         }
diff --git a/src/sksl/SkSLHCodeGenerator.h b/src/sksl/SkSLHCodeGenerator.h
index b2fef51..3e76555 100644
--- a/src/sksl/SkSLHCodeGenerator.h
+++ b/src/sksl/SkSLHCodeGenerator.h
@@ -61,6 +61,8 @@
 
     void writeFields();
 
+    void failOnSection(const char* section, const char* msg);
+
     String fName;
     String fFullName;
     SectionAndParameterHelper fSectionAndParameterHelper;
diff --git a/src/sksl/SkSLIRGenerator.cpp b/src/sksl/SkSLIRGenerator.cpp
index 78672cb..4165ea9 100644
--- a/src/sksl/SkSLIRGenerator.cpp
+++ b/src/sksl/SkSLIRGenerator.cpp
@@ -1394,84 +1394,17 @@
 }
 
 std::unique_ptr<Expression> IRGenerator::applyColorSpace(std::unique_ptr<Expression> texture,
-                                                         const Variable* xform) {
-    // Before:
-    // vec4 color = texture(img, coords, xform);
-    // After:
-    // vec4 tmp;
-    // vec4 color = (tmp = texture(img, coords) ,
-    //               xform != mat4(1) ?
-    //                            vec4(clamp((xform * vec4(tmp.rgb, 1.0)).rgb, 0.0, tmp.a), tmp.a) :
-    //                            tmp);
-
-    // a few macros to keep line lengths manageable
-    #define EXPR std::unique_ptr<Expression>
-    #define REF(v) EXPR(new VariableReference(p, *v))
-    #define FLOAT(x) EXPR(new FloatLiteral(fContext, p, x))
-    using std::move;
-    Position p = Position();
-    // vec4 tmp;
-    Variable* tmp = new Variable(p, Modifiers(), "_tmp" + to_string(fTmpCount++), texture->fType,
-                                 Variable::kLocal_Storage);
-    fRootSymbolTable->takeOwnership(tmp);
-    std::vector<std::unique_ptr<VarDeclaration>> decls;
-    decls.emplace_back(new VarDeclaration(tmp, std::vector<std::unique_ptr<Expression>>(),
-                                          nullptr));
-    const Type& type = texture->fType;
-    fExtraVars.emplace_back(new VarDeclarationsStatement(std::unique_ptr<VarDeclarations>(
-                                       new VarDeclarations(p, &type, move(decls)))));
-    // tmp = texture
-    EXPR assignment = EXPR(new BinaryExpression(p,
-                                                EXPR(new VariableReference(p, *tmp,
-                                                                VariableReference::kWrite_RefKind)),
-                                                Token::EQ,
-                                                move(texture), type));
-    // 1.0
-    std::vector<EXPR> matArgs;
-    matArgs.push_back(FLOAT(1.0));
-    // mat4(1.0)
-    EXPR mat = EXPR(new Constructor(p, *fContext.fMat4x4_Type, move(matArgs)));
-    // <xform> != mat4(1.0)
-    EXPR matNeq = EXPR(new BinaryExpression(p, REF(xform), Token::NEQ, move(mat),
-                                            *fContext.fBool_Type));
-    // tmp.rgb
-    std::vector<int> rgb { 0, 1, 2 };
-    EXPR tmpRgb = EXPR(new Swizzle(fContext, REF(tmp), rgb));
-    // vec4(tmp.rgb, 1.0)
-    std::vector<EXPR> tmpVecArgs;
-    tmpVecArgs.push_back(move(tmpRgb));
-    tmpVecArgs.push_back(FLOAT(1.0));
-    EXPR tmpVec = EXPR(new Constructor(p, *fContext.fVec4_Type, move(tmpVecArgs)));
-    // xform * vec4(tmp.rgb, 1.0)
-    EXPR mul = EXPR(new BinaryExpression(p, REF(xform), Token::STAR, move(tmpVec),
-                                         *fContext.fVec4_Type));
-    // (xform * vec4(tmp.rgb, 1.0)).rgb
-    EXPR mulRGB = EXPR(new Swizzle(fContext, std::move(mul), rgb));
-    // tmp.a
-    std::vector<int> a { 3 };
-    EXPR tmpA = EXPR(new Swizzle(fContext, REF(tmp), a));
-    // clamp((xform * vec4(tmp.rgb, 1.0)).rgb, 0.0, tmp.a)
-    EXPR clamp = this->convertIdentifier(ASTIdentifier(p, "clamp"));
-    std::vector<EXPR> clampArgs;
-    clampArgs.push_back(move(mulRGB));
-    clampArgs.push_back(FLOAT(0));
-    clampArgs.push_back(move(tmpA));
-    EXPR clampCall = this->call(p, move(clamp), move(clampArgs));
-    // tmp.a
-    tmpA = EXPR(new Swizzle(fContext, REF(tmp), a));
-    // vec4(clamp((xform * vec4(tmp.rgb, 1.0)).rgb, 0.0, tmp.a), tmp.a)
-    std::vector<EXPR> finalVecArgs;
-    finalVecArgs.push_back(move(clampCall));
-    finalVecArgs.push_back(move(tmpA));
-    EXPR finalVec = EXPR(new Constructor(p, *fContext.fVec4_Type, move(finalVecArgs)));
-    // xform != mat4(1) ? vec4(clamp((xform * vec4(tmp.rgb, 1.0)).rgb, 0.0, tmp.a), tmp.a) : tmp)
-    EXPR ternary = EXPR(new TernaryExpression(p, move(matNeq), move(finalVec), REF(tmp)));
-    // (tmp = texture ,
-    //   xform != mat4(1) ? vec4(clamp((xform * vec4(tmp.rgb, 1.0)).rgb, 0.0, tmp.a), tmp.a) : tmp))
-    return EXPR(new BinaryExpression(p, move(assignment), Token::COMMA, move(ternary), type));
-    #undef EXPR
-    #undef REF
-    #undef FLOAT
+                                                         std::unique_ptr<Expression> xform) {
+    // Before: texture(img, coords, xform);
+    // After: COLORSPACE(texture(img, coords), xform)
+    Position p = texture->fPosition;
+    std::vector<std::unique_ptr<Expression>> args;
+    args.push_back(std::move(texture));
+    args.push_back(std::move(xform));
+    const Symbol* colorspaceSymbol = (*fSymbolTable)["COLORSPACE"];
+    ASSERT(colorspaceSymbol->fKind == Symbol::kFunctionDeclaration_Kind);
+    const FunctionDeclaration& colorspaceFunction = (FunctionDeclaration&) *colorspaceSymbol;
+    return this->call(p, colorspaceFunction, std::move(args));
 }
 
 std::unique_ptr<Expression> IRGenerator::call(Position position,
@@ -1490,12 +1423,11 @@
     if (ref->fFunctions[0]->fName == "texture" &&
         arguments.back()->fType == *fContext.fColorSpaceXform_Type) {
         std::unique_ptr<Expression> colorspace = std::move(arguments.back());
-        ASSERT(colorspace->fKind == Expression::kVariableReference_Kind);
         arguments.pop_back();
         return this->applyColorSpace(this->call(position,
                                                 std::move(functionValue),
                                                 std::move(arguments)),
-                                     &((VariableReference&) *colorspace).fVariable);
+                                     std::move(colorspace));
     }
 
     int bestCost = INT_MAX;
diff --git a/src/sksl/SkSLIRGenerator.h b/src/sksl/SkSLIRGenerator.h
index 3078a9e..70416c5 100644
--- a/src/sksl/SkSLIRGenerator.h
+++ b/src/sksl/SkSLIRGenerator.h
@@ -161,7 +161,7 @@
      * to implement texture(sampler, coord, colorSpaceXForm).
      */
     std::unique_ptr<Expression> applyColorSpace(std::unique_ptr<Expression> texture,
-                                                const Variable* xform);
+                                                std::unique_ptr<Expression> xform);
     void fixRectSampling(std::vector<std::unique_ptr<Expression>>& arguments);
     void checkValid(const Expression& expr);
     void markWrittenTo(const Expression& expr, bool readWrite);
diff --git a/src/sksl/SkSLSPIRVCodeGenerator.cpp b/src/sksl/SkSLSPIRVCodeGenerator.cpp
index 878c7fc..21b8339 100644
--- a/src/sksl/SkSLSPIRVCodeGenerator.cpp
+++ b/src/sksl/SkSLSPIRVCodeGenerator.cpp
@@ -2035,6 +2035,40 @@
     return id;
 }
 
+SpvId SPIRVCodeGenerator::writeMatrixComparison(const Type& operandType, SpvId lhs, SpvId rhs,
+                                                SpvOp_ floatOperator, SpvOp_ intOperator,
+                                                OutputStream& out) {
+    SpvOp_ compareOp = is_float(fContext, operandType) ? floatOperator : intOperator;
+    ASSERT(operandType.kind() == Type::kMatrix_Kind);
+    SpvId rowType = this->getType(operandType.componentType().toCompound(fContext,
+                                                                         operandType.columns(),
+                                                                         1));
+    SpvId bvecType = this->getType(fContext.fBool_Type->toCompound(fContext,
+                                                                    operandType.columns(),
+                                                                    1));
+    SpvId boolType = this->getType(*fContext.fBool_Type);
+    SpvId result = 0;
+    for (int i = 0; i < operandType.rows(); i++) {
+        SpvId rowL = this->nextId();
+        this->writeInstruction(SpvOpCompositeExtract, rowType, rowL, lhs, 0, out);
+        SpvId rowR = this->nextId();
+        this->writeInstruction(SpvOpCompositeExtract, rowType, rowR, rhs, 0, out);
+        SpvId compare = this->nextId();
+        this->writeInstruction(compareOp, bvecType, compare, rowL, rowR, out);
+        SpvId all = this->nextId();
+        this->writeInstruction(SpvOpAll, boolType, all, compare, out);
+        if (result != 0) {
+            SpvId next = this->nextId();
+            this->writeInstruction(SpvOpLogicalAnd, boolType, next, result, all, out);
+            result = next;
+        }
+        else {
+            result = all;
+        }
+    }
+    return result;
+}
+
 SpvId SPIRVCodeGenerator::writeBinaryExpression(const BinaryExpression& b, OutputStream& out) {
     // handle cases where we don't necessarily evaluate both LHS and RHS
     switch (b.fOperator) {
@@ -2139,6 +2173,10 @@
     }
     switch (b.fOperator) {
         case Token::EQEQ: {
+            if (operandType->kind() == Type::kMatrix_Kind) {
+                return this->writeMatrixComparison(*operandType, lhs, rhs, SpvOpFOrdEqual,
+                                                   SpvOpIEqual, out);
+            }
             ASSERT(resultType == *fContext.fBool_Type);
             return this->foldToBool(this->writeBinaryOperation(resultType, *operandType, lhs, rhs,
                                                                SpvOpFOrdEqual, SpvOpIEqual,
@@ -2146,6 +2184,10 @@
                                     *operandType, out);
         }
         case Token::NEQ:
+            if (operandType->kind() == Type::kMatrix_Kind) {
+                return this->writeMatrixComparison(*operandType, lhs, rhs, SpvOpFOrdNotEqual,
+                                                   SpvOpINotEqual, out);
+            }
             ASSERT(resultType == *fContext.fBool_Type);
             return this->foldToBool(this->writeBinaryOperation(resultType, *operandType, lhs, rhs,
                                                                SpvOpFOrdNotEqual, SpvOpINotEqual,
diff --git a/src/sksl/SkSLSPIRVCodeGenerator.h b/src/sksl/SkSLSPIRVCodeGenerator.h
index 9944428..1ce9d0c 100644
--- a/src/sksl/SkSLSPIRVCodeGenerator.h
+++ b/src/sksl/SkSLSPIRVCodeGenerator.h
@@ -185,6 +185,9 @@
      */
     SpvId foldToBool(SpvId id, const Type& operandType, OutputStream& out);
 
+    SpvId writeMatrixComparison(const Type& operandType, SpvId lhs, SpvId rhs, SpvOp_ floatOperator,
+                                SpvOp_ intOperator, OutputStream& out);
+
     SpvId writeBinaryOperation(const Type& resultType, const Type& operandType, SpvId lhs,
                                SpvId rhs, SpvOp_ ifFloat, SpvOp_ ifInt, SpvOp_ ifUInt,
                                SpvOp_ ifBool, OutputStream& out);
diff --git a/src/sksl/SkSLSectionAndParameterHelper.h b/src/sksl/SkSLSectionAndParameterHelper.h
index f3242c3..bbe007c 100644
--- a/src/sksl/SkSLSectionAndParameterHelper.h
+++ b/src/sksl/SkSLSectionAndParameterHelper.h
@@ -18,18 +18,20 @@
 namespace SkSL {
 
 #define CLASS_SECTION              "class"
+#define CONSTRUCTOR_SECTION        "constructor"
+#define CONSTRUCTOR_CODE_SECTION   "constructorCode"
+#define CONSTRUCTOR_PARAMS_SECTION "constructorParams"
+#define COORD_TRANSFORM_SECTION    "coordTransform"
 #define CPP_SECTION                "cpp"
 #define CPP_END_SECTION            "cppEnd"
 #define HEADER_SECTION             "header"
 #define HEADER_END_SECTION         "headerEnd"
-#define CONSTRUCTOR_PARAMS_SECTION "constructorParams"
-#define CONSTRUCTOR_SECTION        "constructor"
-#define CONSTRUCTOR_CODE_SECTION   "constructorCode"
-#define INITIALIZERS_SECTION       "initializers"
 #define EMIT_CODE_SECTION          "emitCode"
 #define FIELDS_SECTION             "fields"
+#define INITIALIZERS_SECTION       "initializers"
 #define MAKE_SECTION               "make"
 #define OPTIMIZATION_FLAGS_SECTION "optimizationFlags"
+#define SAMPLER_PARAMS_SECTION     "samplerParams"
 #define SET_DATA_SECTION           "setData"
 #define TEST_CODE_SECTION          "test"
 
@@ -65,11 +67,12 @@
                         errors.error(s->fPosition,
                                      ("unsupported section '@" + s->fName + "'").c_str());
                     }
-                    if (fSections.find(s->fName) != fSections.end()) {
+                    if (!SectionPermitsDuplicates(s->fName.c_str()) &&
+                            fSections.find(s->fName) != fSections.end()) {
                         errors.error(s->fPosition,
                                      ("duplicate section '@" + s->fName + "'").c_str());
                     }
-                    fSections[s->fName] = s;
+                    fSections[s->fName].push_back(s);
                     break;
                 }
                 default:
@@ -78,6 +81,28 @@
         }
     }
 
+    const Section* getSection(const char* name) {
+        ASSERT(!SectionPermitsDuplicates(name));
+        auto found = fSections.find(name);
+        if (found == fSections.end()) {
+            return nullptr;
+        }
+        ASSERT(found->second.size() == 1);
+        return found->second[0];
+    }
+
+    std::vector<const Section*> getSections(const char* name) {
+        auto found = fSections.find(name);
+        if (found == fSections.end()) {
+            return std::vector<const Section*>();
+        }
+        return found->second;
+    }
+
+    const std::vector<const Variable*>& getParameters() {
+        return fParameters;
+    }
+
     static bool IsParameter(const Variable& var) {
         return (var.fModifiers.fFlags & Modifiers::kIn_Flag) &&
                -1 == var.fModifiers.fLayout.fBuiltin;
@@ -85,29 +110,39 @@
 
     static bool IsSupportedSection(const char* name) {
         return !strcmp(name, CLASS_SECTION) ||
-               !strcmp(name, CPP_SECTION) ||
-               !strcmp(name, CPP_END_SECTION) ||
-               !strcmp(name, HEADER_SECTION) ||
-               !strcmp(name, HEADER_END_SECTION) ||
                !strcmp(name, CONSTRUCTOR_SECTION) ||
                !strcmp(name, CONSTRUCTOR_CODE_SECTION) ||
                !strcmp(name, CONSTRUCTOR_PARAMS_SECTION) ||
+               !strcmp(name, COORD_TRANSFORM_SECTION) ||
+               !strcmp(name, CPP_SECTION) ||
+               !strcmp(name, CPP_END_SECTION) ||
                !strcmp(name, EMIT_CODE_SECTION) ||
                !strcmp(name, FIELDS_SECTION) ||
+               !strcmp(name, HEADER_SECTION) ||
+               !strcmp(name, HEADER_END_SECTION) ||
                !strcmp(name, INITIALIZERS_SECTION) ||
                !strcmp(name, MAKE_SECTION) ||
                !strcmp(name, OPTIMIZATION_FLAGS_SECTION) ||
+               !strcmp(name, SAMPLER_PARAMS_SECTION) ||
                !strcmp(name, SET_DATA_SECTION) ||
                !strcmp(name, TEST_CODE_SECTION);
     }
 
     static bool SectionAcceptsArgument(const char* name) {
-        return !strcmp(name, SET_DATA_SECTION) ||
+        return !strcmp(name, COORD_TRANSFORM_SECTION) ||
+               !strcmp(name, SAMPLER_PARAMS_SECTION) ||
+               !strcmp(name, SET_DATA_SECTION) ||
                !strcmp(name, TEST_CODE_SECTION);
     }
 
+    static bool SectionPermitsDuplicates(const char* name) {
+        return !strcmp(name, COORD_TRANSFORM_SECTION) ||
+               !strcmp(name, SAMPLER_PARAMS_SECTION);
+    }
+
+private:
     std::vector<const Variable*> fParameters;
-    std::unordered_map<String, const Section*> fSections;
+    std::unordered_map<String, std::vector<const Section*>> fSections;
 };
 
 } // namespace SkSL
diff --git a/src/sksl/ir/SkSLType.cpp b/src/sksl/ir/SkSLType.cpp
index c919cbc..7516377 100644
--- a/src/sksl/ir/SkSLType.cpp
+++ b/src/sksl/ir/SkSLType.cpp
@@ -128,6 +128,17 @@
                 }
             default: ABORT("unsupported row count (%d)", rows);
         }
+    } else if (*this == *context.fBool_Type) {
+        switch (rows) {
+            case 1:
+                switch (columns) {
+                    case 2: return *context.fBVec2_Type;
+                    case 3: return *context.fBVec3_Type;
+                    case 4: return *context.fBVec4_Type;
+                    default: ABORT("unsupported vector column count (%d)", columns);
+                }
+            default: ABORT("unsupported row count (%d)", rows);
+        }
     }
     ABORT("unsupported scalar_to_compound type %s", this->description().c_str());
 }
diff --git a/src/sksl/sksl_fp.include b/src/sksl/sksl_fp.include
index 566e441..0d8d99d 100644
--- a/src/sksl/sksl_fp.include
+++ b/src/sksl/sksl_fp.include
@@ -20,4 +20,6 @@
 layout(builtin=10004) out vec4 sk_OutColor;
 layout(builtin=10005) vec2[] sk_TransformedCoords2D;
 layout(builtin=10006) sampler2D[] sk_TextureSamplers;
+
+vec4 COLORSPACE(vec4 color, colorSpaceXform colorSpace);
 )
diff --git a/tests/SkSLFPTest.cpp b/tests/SkSLFPTest.cpp
index 62cfa6c..a5a1a6e 100644
--- a/tests/SkSLFPTest.cpp
+++ b/tests/SkSLFPTest.cpp
@@ -82,6 +82,7 @@
              "#if SK_SUPPORT_GPU\n"
              "#include \"GrFragmentProcessor.h\"\n"
              "#include \"GrCoordTransform.h\"\n"
+             "#include \"GrColorSpaceXform.h\"\n"
              "#include \"effects/GrProxyMove.h\"\n"
              "class GrTest : public GrFragmentProcessor {\n"
              "public:\n"
@@ -353,16 +354,14 @@
              "sk_sp<GrColorSpaceXform> fColorXform;"
          },
          {
-             "fragBuilder->codeAppendf(\"vec4 _tmp0;\\n%s = %s * (_tmp0 = texture(%s, "
-             "vec2(0.0, 0.0)).%s , %s != mat4(1.0) ? vec4(clamp((%s * vec4(_tmp0.xyz, 1.0)).xyz, "
-             "0.0, _tmp0.w), _tmp0.w) : _tmp0);\\n\", args.fOutputColor, args.fInputColor ? "
-             "args.fInputColor : \"vec4(1)\", fragBuilder->getProgramBuilder()->"
-             "samplerVariable(args.fTexSamplers[0]).c_str(), "
+             "fragBuilder->codeAppendf(\"vec4 _tmpVar1;%s = %s * %stexture(%s, "
+             "vec2(0.0, 0.0)).%s%s;\\n\", args.fOutputColor, args.fInputColor ? args.fInputColor : "
+             "\"vec4(1)\", fColorSpaceHelper.isValid() ? \"(_tmpVar1 = \" : \"\", "
+             "fragBuilder->getProgramBuilder()->samplerVariable(args.fTexSamplers[0]).c_str(), "
              "fragBuilder->getProgramBuilder()->samplerSwizzle(args.fTexSamplers[0]).c_str(), "
-             "fColorSpaceHelper.isValid() ? args.fUniformHandler->getUniformCStr("
-             "fColorSpaceHelper.gamutXformUniform()) : \"mat4(1.0)\", "
-             "fColorSpaceHelper.isValid() ? args.fUniformHandler->getUniformCStr("
-             "fColorSpaceHelper.gamutXformUniform()) : \"mat4(1.0)\");"
+             "fColorSpaceHelper.isValid() ? SkStringPrintf(\", vec4(clamp((%s * vec4(_tmpVar1.rgb, "
+             "1.0)).rgb, 0.0, _tmpVar1.a), _tmpVar1.a))\", args.fUniformHandler->getUniformCStr("
+             "fColorSpaceHelper.gamutXformUniform())).c_str() : \"\");"
          });
 }