converted arithmetic FP to new FP structure

Bug: skia:
Change-Id: I5492b378fa0f2ab7b453b2b1b18e4aef898370d2
Reviewed-on: https://skia-review.googlesource.com/148910
Commit-Queue: Ethan Nicholas <ethannicholas@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
diff --git a/gn/gpu.gni b/gn/gpu.gni
index b92b6cd..a0d010f 100644
--- a/gn/gpu.gni
+++ b/gn/gpu.gni
@@ -332,8 +332,6 @@
   "$_src/gpu/effects/GrAARectEffect.h",
   "$_src/gpu/effects/GrAlphaThresholdFragmentProcessor.cpp",
   "$_src/gpu/effects/GrAlphaThresholdFragmentProcessor.h",
-  "$_src/gpu/effects/GrArithmeticFP.cpp",
-  "$_src/gpu/effects/GrArithmeticFP.h",
   "$_src/gpu/effects/GrBlurredEdgeFragmentProcessor.cpp",
   "$_src/gpu/effects/GrBlurredEdgeFragmentProcessor.h",
   "$_src/gpu/effects/GrCircleBlurFragmentProcessor.cpp",
diff --git a/gn/sksl.gni b/gn/sksl.gni
index fa04c8e..aae7e26 100644
--- a/gn/sksl.gni
+++ b/gn/sksl.gni
@@ -31,7 +31,6 @@
 skia_gpu_processor_sources = [
   "$_src/gpu/effects/GrAARectEffect.fp",
   "$_src/gpu/effects/GrAlphaThresholdFragmentProcessor.fp",
-  "$_src/gpu/effects/GrArithmeticFP.fp",
   "$_src/gpu/effects/GrBlurredEdgeFragmentProcessor.fp",
   "$_src/gpu/effects/GrCircleBlurFragmentProcessor.fp",
   "$_src/gpu/effects/GrCircleEffect.fp",
diff --git a/include/effects/SkArithmeticImageFilter.h b/include/effects/SkArithmeticImageFilter.h
index 40b91e8..7f4aea1 100644
--- a/include/effects/SkArithmeticImageFilter.h
+++ b/include/effects/SkArithmeticImageFilter.h
@@ -10,6 +10,13 @@
 
 #include "SkImageFilter.h"
 
+extern const char* SKSL_ARITHMETIC_SRC;
+
+struct ArithmeticFPInputs {
+    float k[4];
+    bool enforcePMColor;
+};
+
 class SK_API SkArithmeticImageFilter {
 public:
     static sk_sp<SkImageFilter> Make(float k1, float k2, float k3, float k4, bool enforcePMColor,
diff --git a/src/effects/imagefilters/SkArithmeticImageFilter.cpp b/src/effects/imagefilters/SkArithmeticImageFilter.cpp
index 644494e..2e5b801 100644
--- a/src/effects/imagefilters/SkArithmeticImageFilter.cpp
+++ b/src/effects/imagefilters/SkArithmeticImageFilter.cpp
@@ -22,8 +22,8 @@
 #include "GrRenderTargetContext.h"
 #include "GrTextureProxy.h"
 #include "SkGr.h"
-#include "effects/GrArithmeticFP.h"
 #include "effects/GrConstColorProcessor.h"
+#include "effects/GrSkSLFP.h"
 #include "effects/GrTextureDomain.h"
 #include "glsl/GrGLSLFragmentProcessor.h"
 #include "glsl/GrGLSLFragmentShaderBuilder.h"
@@ -31,6 +31,20 @@
 #include "glsl/GrGLSLUniformHandler.h"
 #endif
 
+const char* SKSL_ARITHMETIC_SRC = R"(
+in uniform float4 k;
+layout(key) const in bool enforcePMColor;
+in fragmentProcessor child;
+
+void main(int x, int y, inout half4 color) {
+    half4 dst = process(child);
+    color = saturate(k.x * color * dst + k.y * color + k.z * dst + k.w);
+    if (enforcePMColor) {
+        color.rgb = min(color.rgb, color.a);
+    }
+}
+)";
+
 class ArithmeticImageFilterImpl : public SkImageFilter {
 public:
     ArithmeticImageFilterImpl(float k1, float k2, float k3, float k4, bool enforcePMColor,
@@ -268,21 +282,6 @@
 
 #if SK_SUPPORT_GPU
 
-#if GR_TEST_UTILS
-std::unique_ptr<GrFragmentProcessor> GrArithmeticFP::TestCreate(GrProcessorTestData* d) {
-    float k1 = d->fRandom->nextF();
-    float k2 = d->fRandom->nextF();
-    float k3 = d->fRandom->nextF();
-    float k4 = d->fRandom->nextF();
-    bool enforcePMColor = d->fRandom->nextBool();
-
-    std::unique_ptr<GrFragmentProcessor> dst(GrProcessorUnitTest::MakeChildFP(d));
-    return GrArithmeticFP::Make(k1, k2, k3, k4, enforcePMColor, std::move(dst));
-}
-#endif
-
-GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrArithmeticFP);
-
 sk_sp<SkSpecialImage> ArithmeticImageFilterImpl::filterImageGPU(
         SkSpecialImage* source,
         sk_sp<SkSpecialImage> background,
@@ -336,11 +335,19 @@
                                                      outputProperties.colorSpace());
         paint.addColorFragmentProcessor(std::move(foregroundFP));
 
-        std::unique_ptr<GrFragmentProcessor> xferFP =
-                GrArithmeticFP::Make(fK[0], fK[1], fK[2], fK[3], fEnforcePMColor, std::move(bgFP));
-
-        // A null 'xferFP' here means kSrc_Mode was used in which case we can just proceed
+        static int arithmeticIndex = GrSkSLFP::NewIndex();
+        ArithmeticFPInputs inputs;
+        static_assert(sizeof(inputs.k) == sizeof(fK), "struct size mismatch");
+        memcpy(inputs.k, fK, sizeof(inputs.k));
+        inputs.enforcePMColor = fEnforcePMColor;
+        std::unique_ptr<GrFragmentProcessor> xferFP = GrSkSLFP::Make(context,
+                                                                     arithmeticIndex,
+                                                                     "Arithmetic",
+                                                                     SKSL_ARITHMETIC_SRC,
+                                                                     &inputs,
+                                                                     sizeof(inputs));
         if (xferFP) {
+            ((GrSkSLFP&) *xferFP).addChild(std::move(bgFP));
             paint.addColorFragmentProcessor(std::move(xferFP));
         }
     } else {
diff --git a/src/gpu/GrProcessor.cpp b/src/gpu/GrProcessor.cpp
index aba8d9b..aafe1d8 100644
--- a/src/gpu/GrProcessor.cpp
+++ b/src/gpu/GrProcessor.cpp
@@ -57,7 +57,7 @@
  * we verify the count is as expected.  If a new factory is added, then these numbers must be
  * manually adjusted.
  */
-static const int kFPFactoryCount = 37;
+static const int kFPFactoryCount = 36;
 static const int kGPFactoryCount = 14;
 static const int kXPFactoryCount = 4;
 
diff --git a/src/gpu/effects/GrArithmeticFP.cpp b/src/gpu/effects/GrArithmeticFP.cpp
deleted file mode 100644
index b7d37a8..0000000
--- a/src/gpu/effects/GrArithmeticFP.cpp
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright 2018 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 GrArithmeticFP.fp; do not modify.
- **************************************************************************************************/
-#include "GrArithmeticFP.h"
-#include "glsl/GrGLSLFragmentProcessor.h"
-#include "glsl/GrGLSLFragmentShaderBuilder.h"
-#include "glsl/GrGLSLProgramBuilder.h"
-#include "GrTexture.h"
-#include "SkSLCPP.h"
-#include "SkSLUtil.h"
-class GrGLSLArithmeticFP : public GrGLSLFragmentProcessor {
-public:
-    GrGLSLArithmeticFP() {}
-    void emitCode(EmitArgs& args) override {
-        GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
-        const GrArithmeticFP& _outer = args.fFp.cast<GrArithmeticFP>();
-        (void)_outer;
-        auto k1 = _outer.k1();
-        (void)k1;
-        auto k2 = _outer.k2();
-        (void)k2;
-        auto k3 = _outer.k3();
-        (void)k3;
-        auto k4 = _outer.k4();
-        (void)k4;
-        auto enforcePMColor = _outer.enforcePMColor();
-        (void)enforcePMColor;
-        fKVar = args.fUniformHandler->addUniform(kFragment_GrShaderFlag, kFloat4_GrSLType,
-                                                 kDefault_GrSLPrecision, "k");
-        SkString _child0("_child0");
-        this->emitChild(0, &_child0, args);
-        fragBuilder->codeAppendf(
-                "half4 dst = %s;\n%s = half4(clamp(float4(float4(((%s.x * float4(%s)) * dst + %s.y "
-                "* float4(%s)) + %s.z * float4(dst)) + %s.w), 0.0, 1.0));\nif (%s) {\n    %s.xyz = "
-                "half3(min(float3(%s.xyz), float(%s.w)));\n}\n",
-                _child0.c_str(), args.fOutputColor, args.fUniformHandler->getUniformCStr(fKVar),
-                args.fInputColor ? args.fInputColor : "half4(1)",
-                args.fUniformHandler->getUniformCStr(fKVar),
-                args.fInputColor ? args.fInputColor : "half4(1)",
-                args.fUniformHandler->getUniformCStr(fKVar),
-                args.fUniformHandler->getUniformCStr(fKVar),
-                (_outer.enforcePMColor() ? "true" : "false"), args.fOutputColor, args.fOutputColor,
-                args.fOutputColor);
-    }
-
-private:
-    void onSetData(const GrGLSLProgramDataManager& pdman,
-                   const GrFragmentProcessor& _proc) override {
-        const GrArithmeticFP& _outer = _proc.cast<GrArithmeticFP>();
-        auto k1 = _outer.k1();
-        (void)k1;
-        auto k2 = _outer.k2();
-        (void)k2;
-        auto k3 = _outer.k3();
-        (void)k3;
-        auto k4 = _outer.k4();
-        (void)k4;
-        auto enforcePMColor = _outer.enforcePMColor();
-        (void)enforcePMColor;
-        UniformHandle& k = fKVar;
-        (void)k;
-
-        pdman.set4f(k, k1, k2, k3, k4);
-    }
-    UniformHandle fKVar;
-};
-GrGLSLFragmentProcessor* GrArithmeticFP::onCreateGLSLInstance() const {
-    return new GrGLSLArithmeticFP();
-}
-void GrArithmeticFP::onGetGLSLProcessorKey(const GrShaderCaps& caps,
-                                           GrProcessorKeyBuilder* b) const {
-    b->add32((int32_t)fEnforcePMColor);
-}
-bool GrArithmeticFP::onIsEqual(const GrFragmentProcessor& other) const {
-    const GrArithmeticFP& that = other.cast<GrArithmeticFP>();
-    (void)that;
-    if (fK1 != that.fK1) return false;
-    if (fK2 != that.fK2) return false;
-    if (fK3 != that.fK3) return false;
-    if (fK4 != that.fK4) return false;
-    if (fEnforcePMColor != that.fEnforcePMColor) return false;
-    return true;
-}
-GrArithmeticFP::GrArithmeticFP(const GrArithmeticFP& src)
-        : INHERITED(kGrArithmeticFP_ClassID, src.optimizationFlags())
-        , fK1(src.fK1)
-        , fK2(src.fK2)
-        , fK3(src.fK3)
-        , fK4(src.fK4)
-        , fEnforcePMColor(src.fEnforcePMColor) {
-    this->registerChildProcessor(src.childProcessor(0).clone());
-}
-std::unique_ptr<GrFragmentProcessor> GrArithmeticFP::clone() const {
-    return std::unique_ptr<GrFragmentProcessor>(new GrArithmeticFP(*this));
-}
diff --git a/src/gpu/effects/GrArithmeticFP.fp b/src/gpu/effects/GrArithmeticFP.fp
deleted file mode 100644
index ce16b91..0000000
--- a/src/gpu/effects/GrArithmeticFP.fp
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright 2018 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-in float k1;
-in float k2;
-in float k3;
-in float k4;
-layout(key) in bool enforcePMColor;
-in fragmentProcessor child;
-
-uniform float4 k;
-
-void main() {
-    half4 dst = process(child);
-    sk_OutColor = saturate(k.x * sk_InColor * dst + k.y * sk_InColor + k.z * dst + k.w);
-    if (enforcePMColor) {
-        sk_OutColor.rgb = min(sk_OutColor.rgb, sk_OutColor.a);
-    }
-}
-
-@setData(pdman) {
-    pdman.set4f(k, k1, k2, k3, k4);
-}
diff --git a/src/gpu/effects/GrArithmeticFP.h b/src/gpu/effects/GrArithmeticFP.h
deleted file mode 100644
index 7debf26..0000000
--- a/src/gpu/effects/GrArithmeticFP.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright 2018 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 GrArithmeticFP.fp; do not modify.
- **************************************************************************************************/
-#ifndef GrArithmeticFP_DEFINED
-#define GrArithmeticFP_DEFINED
-#include "SkTypes.h"
-#include "GrFragmentProcessor.h"
-#include "GrCoordTransform.h"
-class GrArithmeticFP : public GrFragmentProcessor {
-public:
-    float k1() const { return fK1; }
-    float k2() const { return fK2; }
-    float k3() const { return fK3; }
-    float k4() const { return fK4; }
-    bool enforcePMColor() const { return fEnforcePMColor; }
-    static std::unique_ptr<GrFragmentProcessor> Make(float k1, float k2, float k3, float k4,
-                                                     bool enforcePMColor,
-                                                     std::unique_ptr<GrFragmentProcessor> child) {
-        return std::unique_ptr<GrFragmentProcessor>(
-                new GrArithmeticFP(k1, k2, k3, k4, enforcePMColor, std::move(child)));
-    }
-    GrArithmeticFP(const GrArithmeticFP& src);
-    std::unique_ptr<GrFragmentProcessor> clone() const override;
-    const char* name() const override { return "ArithmeticFP"; }
-
-private:
-    GrArithmeticFP(float k1, float k2, float k3, float k4, bool enforcePMColor,
-                   std::unique_ptr<GrFragmentProcessor> child)
-            : INHERITED(kGrArithmeticFP_ClassID, kNone_OptimizationFlags)
-            , fK1(k1)
-            , fK2(k2)
-            , fK3(k3)
-            , fK4(k4)
-            , fEnforcePMColor(enforcePMColor) {
-        this->registerChildProcessor(std::move(child));
-    }
-    GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
-    void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override;
-    bool onIsEqual(const GrFragmentProcessor&) const override;
-    GR_DECLARE_FRAGMENT_PROCESSOR_TEST
-    float fK1;
-    float fK2;
-    float fK3;
-    float fK4;
-    bool fEnforcePMColor;
-    typedef GrFragmentProcessor INHERITED;
-};
-#endif
diff --git a/src/gpu/effects/GrSkSLFP.cpp b/src/gpu/effects/GrSkSLFP.cpp
index bc84c83..0ebd5c4 100644
--- a/src/gpu/effects/GrSkSLFP.cpp
+++ b/src/gpu/effects/GrSkSLFP.cpp
@@ -9,9 +9,11 @@
 #include "glsl/GrGLSLFragmentProcessor.h"
 #include "glsl/GrGLSLFragmentShaderBuilder.h"
 #include "glsl/GrGLSLProgramBuilder.h"
+#include "GrConstColorProcessor.h"
 #include "GrContext.h"
 #include "GrContextPriv.h"
 #include "GrTexture.h"
+#include "SkArithmeticImageFilter.h"
 #include "SkSLUtil.h"
 
 GrSkSLFPFactory::GrSkSLFPFactory(const char* name, const GrShaderCaps* shaderCaps, const char* sksl)
@@ -58,9 +60,20 @@
             int32_t v = *(int32_t*) (((uint8_t*) inputs) + offset);
             inputMap.insert(std::make_pair(name, SkSL::Program::Settings::Value(v)));
             offset += sizeof(int32_t);
+        } else if (&v->fType == fCompiler.context().fBool_Type.get()) {
+            bool v = *(((bool*) inputs) + offset);
+            inputMap.insert(std::make_pair(name, SkSL::Program::Settings::Value(v)));
+            offset += sizeof(bool);
+        } else if (&v->fType == fCompiler.context().fFloat4_Type.get()) {
+            SkASSERT(v->fModifiers.fFlags & SkSL::Modifiers::kUniform_Flag);
+            offset = SkAlign4(offset) + sizeof(float) * 4;
+        } else if (&v->fType == fCompiler.context().fFragmentProcessor_Type.get()) {
+            // do nothing
+        } else {
+            printf("can't handle input var: %s\n", SkSL::String(v->fType.fName).c_str());
+            SkASSERT(false);
         }
     }
-    SkASSERT(offset == inputSize);
 
     std::unique_ptr<SkSL::Program> specialized = fCompiler.specialize(*fBaseProgram, inputMap);
     SkAssertResult(fCompiler.optimize(*specialized));
@@ -71,11 +84,56 @@
 
 class GrGLSLSkSLFP : public GrGLSLFragmentProcessor {
 public:
-    GrGLSLSkSLFP(SkSL::String glsl, std::vector<SkSL::Compiler::FormatArg> formatArgs)
-            : fGLSL(glsl)
+    GrGLSLSkSLFP(const SkSL::Context* context, const std::vector<const SkSL::Variable*>* inputVars,
+                 SkSL::String glsl, std::vector<SkSL::Compiler::FormatArg> formatArgs)
+            : fContext(*context)
+            , fInputVars(*inputVars)
+            , fGLSL(glsl)
             , fFormatArgs(formatArgs) {}
 
+    GrSLType uniformType(const SkSL::Type& type) {
+        if (type == *fContext.fFloat_Type) {
+            return kFloat_GrSLType;
+        } else if (type == *fContext.fHalf_Type) {
+            return kHalf_GrSLType;
+        } else if (type == *fContext.fFloat2_Type) {
+            return kFloat2_GrSLType;
+        } else if (type == *fContext.fHalf2_Type) {
+            return kHalf2_GrSLType;
+        } else if (type == *fContext.fFloat4_Type) {
+            return kFloat4_GrSLType;
+        } else if (type == *fContext.fHalf4_Type) {
+            return kHalf4_GrSLType;
+        } else if (type == *fContext.fFloat4x4_Type) {
+            return kFloat4x4_GrSLType;
+        } else if (type == *fContext.fHalf4x4_Type) {
+            return kHalf4x4_GrSLType;
+        } else if (type == *fContext.fBool_Type) {
+            return kBool_GrSLType;
+        } else if (type == *fContext.fInt_Type) {
+            return kInt_GrSLType;
+        }
+        printf("%s\n", SkSL::String(type.fName).c_str());
+        SK_ABORT("unsupported uniform type");
+        return kFloat_GrSLType;
+    }
+
     void emitCode(EmitArgs& args) override {
+        for (const auto& v : fInputVars) {
+            if (v->fModifiers.fFlags & SkSL::Modifiers::kUniform_Flag && v->fType !=
+                                                                *fContext.fFragmentProcessor_Type) {
+                fUniformHandles.push_back(args.fUniformHandler->addUniform(
+                                                                   kFragment_GrShaderFlag,
+                                                                   this->uniformType(v->fType),
+                                                                   kDefault_GrSLPrecision,
+                                                                   SkSL::String(v->fName).c_str()));
+            }
+        }
+        std::vector<SkString> childNames;
+        for (int i = 0; i < this->numChildProcessors(); ++i) {
+            childNames.push_back(SkStringPrintf("_child%d", i));
+            this->emitChild(i, &childNames[i], args);
+        }
         GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
         int substringStartIndex = 0;
         int formatArgIndex = 0;
@@ -87,17 +145,26 @@
                 ++i;
                 c = fGLSL[i];
                 switch (c) {
-                    case 's':
-                        switch (fFormatArgs[formatArgIndex++]) {
-                            case SkSL::Compiler::FormatArg::kInput:
+                    case 's': {
+                        SkSL::Compiler::FormatArg& arg = fFormatArgs[formatArgIndex++];
+                        switch (arg.fKind) {
+                            case SkSL::Compiler::FormatArg::Kind::kInput:
                                 fragBuilder->codeAppend(args.fInputColor ? args.fInputColor
                                                                          : "half4(1)");
                                 break;
-                            case SkSL::Compiler::FormatArg::kOutput:
+                            case SkSL::Compiler::FormatArg::Kind::kOutput:
                                 fragBuilder->codeAppend(args.fOutputColor);
                                 break;
+                            case SkSL::Compiler::FormatArg::Kind::kUniform:
+                                fragBuilder->codeAppend(args.fUniformHandler->getUniformCStr(
+                                                                      fUniformHandles[arg.fIndex]));
+                                break;
+                            case SkSL::Compiler::FormatArg::Kind::kChildProcessor:
+                                fragBuilder->codeAppend(childNames[arg.fIndex].c_str());
+                                break;
                         }
                         break;
+                    }
                     default:
                         fragBuilder->codeAppendf("%c", c);
                 }
@@ -108,21 +175,51 @@
                                 fGLSL.length() - substringStartIndex);
     }
 
+    void onSetData(const GrGLSLProgramDataManager& pdman,
+                   const GrFragmentProcessor& _proc) override {
+        size_t uniformIndex = 0;
+        size_t offset = 0;
+        const GrSkSLFP& outer = _proc.cast<GrSkSLFP>();
+        char* inputs = (char*) outer.fInputs.get();
+        const SkSL::Context& context = outer.fFactory->fCompiler.context();
+        for (const auto& v : outer.fFactory->fInputVars) {
+            if (&v->fType == context.fFloat4_Type.get()) {
+                offset = SkAlign4(offset);
+                float f1 = *(float*) (inputs + offset);
+                offset += sizeof(float);
+                float f2 = *(float*) (inputs + offset);
+                offset += sizeof(float);
+                float f3 = *(float*) (inputs + offset);
+                offset += sizeof(float);
+                float f4 = *(float*) (inputs + offset);
+                offset += sizeof(float);
+                if (v->fModifiers.fFlags & SkSL::Modifiers::kUniform_Flag) {
+                    pdman.set4f(fUniformHandles[uniformIndex++], f1, f2, f3, f4);
+                }
+            }
+            if (&v->fType == context.fBool_Type.get()) {
+                SkASSERT(!(v->fModifiers.fFlags & SkSL::Modifiers::kUniform_Flag));
+                ++offset;
+            }
+        }
+    }
+
+    const SkSL::Context& fContext;
+    const std::vector<const SkSL::Variable*>& fInputVars;
     // nearly-finished GLSL; still contains printf-style "%s" format tokens
     const SkSL::String fGLSL;
     std::vector<SkSL::Compiler::FormatArg> fFormatArgs;
+    std::vector<UniformHandle> fUniformHandles;
 };
 
-std::unique_ptr<GrFragmentProcessor> GrSkSLFP::Make(GrContext* context, int index, const char* name,
-                                                    const char* sksl, const void* inputs,
-                                                    size_t inputSize) {
-    return std::unique_ptr<GrFragmentProcessor>(new GrSkSLFP(
-                                                        context->contextPriv().getFPFactoryCache(),
-                                                        context->contextPriv().caps()->shaderCaps(),
-                                                        index, name, sksl, inputs, inputSize));
+std::unique_ptr<GrSkSLFP> GrSkSLFP::Make(GrContext* context, int index, const char* name,
+                                         const char* sksl, const void* inputs,
+                                         size_t inputSize) {
+    return std::unique_ptr<GrSkSLFP>(new GrSkSLFP(context->contextPriv().getFPFactoryCache(),
+                                                  context->contextPriv().caps()->shaderCaps(),
+                                                  index, name, sksl, inputs, inputSize));
 }
 
-
 GrSkSLFP::GrSkSLFP(sk_sp<GrSkSLFPFactoryCache> factoryCache, const GrShaderCaps* shaderCaps,
                    int index, const char* name, const char* sksl, const void* inputs,
                    size_t inputSize)
@@ -164,16 +261,20 @@
     }
 }
 
+void GrSkSLFP::addChild(std::unique_ptr<GrFragmentProcessor> child) {
+    this->registerChildProcessor(std::move(child));
+}
+
 GrGLSLFragmentProcessor* GrSkSLFP::onCreateGLSLInstance() const {
     this->createFactory();
     const SkSL::Program* specialized = fFactory->getSpecialization(fKey, fInputs.get(), fInputSize);
     SkSL::String glsl;
     std::vector<SkSL::Compiler::FormatArg> formatArgs;
-     if (!fFactory->fCompiler.toPipelineStage(*specialized, &glsl, &formatArgs)) {
+    if (!fFactory->fCompiler.toPipelineStage(*specialized, &glsl, &formatArgs)) {
         printf("%s\n", fFactory->fCompiler.errorText().c_str());
-        abort();
+        SkASSERT(false);
     }
-    return new GrGLSLSkSLFP(glsl, formatArgs);
+    return new GrGLSLSkSLFP(specialized->fContext.get(), &fFactory->fInputVars, glsl, formatArgs);
 }
 
 void GrSkSLFP::onGetGLSLProcessorKey(const GrShaderCaps& caps,
@@ -181,8 +282,9 @@
     this->createFactory();
     size_t offset = 0;
     char* inputs = (char*) fInputs.get();
+    const SkSL::Context& context = fFactory->fCompiler.context();
     for (const auto& v : fFactory->fInputVars) {
-        if (&v->fType == fFactory->fCompiler.context().fInt_Type.get()) {
+        if (&v->fType == context.fInt_Type.get()) {
             offset = SkAlign4(offset);
             if (v->fModifiers.fLayout.fKey) {
                 fKey += inputs[offset + 0];
@@ -192,13 +294,36 @@
                 b->add32(*(int32_t*) (inputs + offset));
             }
             offset += sizeof(int32_t);
-        }
-        else {
+        } else if (&v->fType == context.fFloat4_Type.get()) {
+            if (v->fModifiers.fLayout.fKey) {
+                for (size_t i = 0; i < sizeof(float) * 4; ++i) {
+                    fKey += inputs[offset + i];
+                }
+                b->add32(*(int32_t*) (inputs + offset));
+                offset += sizeof(float);
+                b->add32(*(int32_t*) (inputs + offset));
+                offset += sizeof(float);
+                b->add32(*(int32_t*) (inputs + offset));
+                offset += sizeof(float);
+                b->add32(*(int32_t*) (inputs + offset));
+                offset += sizeof(float);
+            } else {
+                offset += sizeof(float) * 4;
+            }
+        } else if (&v->fType == context.fBool_Type.get()) {
+            if (v->fModifiers.fLayout.fKey) {
+                fKey += inputs[offset];
+                b->add32(inputs[offset]);
+            }
+            ++offset;
+        } else if (&v->fType == context.fFragmentProcessor_Type.get()) {
+            continue;
+        } else {
             // unsupported input var type
+            printf("%s\n", SkSL::String(v->fType.fName).c_str());
             SkASSERT(false);
         }
     }
-    SkASSERT(offset == fInputSize);
 }
 
 bool GrSkSLFP::onIsEqual(const GrFragmentProcessor& other) const {
@@ -209,7 +334,11 @@
 }
 
 std::unique_ptr<GrFragmentProcessor> GrSkSLFP::clone() const {
-    return std::unique_ptr<GrFragmentProcessor>(new GrSkSLFP(*this));
+    std::unique_ptr<GrSkSLFP> result(new GrSkSLFP(*this));
+    for (int i = 0; i < this->numChildProcessors(); ++i) {
+        result->registerChildProcessor(this->childProcessor(i).clone());
+    }
+    return std::unique_ptr<GrFragmentProcessor>(result.release());
 }
 
 // We have to do a bit of manual refcounting in the cache methods below. Ideally, we could just
@@ -222,7 +351,7 @@
         return nullptr;
     }
     GrSkSLFPFactory* result = fFactories[index];
-    result->ref();
+    SkSafeRef(result);
     return sk_sp<GrSkSLFPFactory>(result);
 }
 
@@ -252,13 +381,31 @@
 using Value = SkSL::Program::Settings::Value;
 
 std::unique_ptr<GrFragmentProcessor> GrSkSLFP::TestCreate(GrProcessorTestData* d) {
-    int type = d->fRandom->nextULessThan(1);
+    int type = d->fRandom->nextULessThan(2);
     switch (type) {
         case 0: {
             static int ditherIndex = NewIndex();
             int rangeType = d->fRandom->nextULessThan(3);
-            return GrSkSLFP::Make(d->context(), ditherIndex, "Dither", SKSL_DITHER_SRC, &rangeType,
-                                  sizeof(rangeType));
+            std::unique_ptr<GrSkSLFP> result = GrSkSLFP::Make(d->context(), ditherIndex, "Dither",
+                                                              SKSL_DITHER_SRC, &rangeType,
+                                                              sizeof(rangeType));
+            return std::unique_ptr<GrFragmentProcessor>(result.release());
+        }
+        case 1: {
+            static int arithmeticIndex = NewIndex();
+            ArithmeticFPInputs inputs;
+            inputs.k[0] = d->fRandom->nextF();
+            inputs.k[1] = d->fRandom->nextF();
+            inputs.k[2] = d->fRandom->nextF();
+            inputs.k[3] = d->fRandom->nextF();
+            inputs.enforcePMColor = d->fRandom->nextBool();
+            std::unique_ptr<GrSkSLFP> result = GrSkSLFP::Make(d->context(), arithmeticIndex,
+                                                              "Arithmetic", SKSL_ARITHMETIC_SRC,
+                                                              &inputs, sizeof(inputs));
+            result->addChild(GrConstColorProcessor::Make(
+                                                        GrColor4f::OpaqueWhite(),
+                                                        GrConstColorProcessor::InputMode::kIgnore));
+            return std::unique_ptr<GrFragmentProcessor>(result.release());
         }
     }
     SK_ABORT("unreachable");
diff --git a/src/gpu/effects/GrSkSLFP.h b/src/gpu/effects/GrSkSLFP.h
index 428e089..f89e9bf 100644
--- a/src/gpu/effects/GrSkSLFP.h
+++ b/src/gpu/effects/GrSkSLFP.h
@@ -60,7 +60,7 @@
      * 'NewIndex()'. Each given SkSL string should have a single, statically defined index
      * associated with it.
      */
-    static std::unique_ptr<GrFragmentProcessor> Make(
+    static std::unique_ptr<GrSkSLFP> Make(
                    GrContext* context,
                    int index,
                    const char* name,
@@ -70,6 +70,8 @@
 
     const char* name() const override;
 
+    void addChild(std::unique_ptr<GrFragmentProcessor> child);
+
     std::unique_ptr<GrFragmentProcessor> clone() const override;
 
 private:
@@ -108,6 +110,8 @@
 
     typedef GrFragmentProcessor INHERITED;
 
+    friend class GrGLSLSkSLFP;
+
     friend class GrSkSLFPFactory;
 };
 
diff --git a/src/sksl/SkSLCompiler.h b/src/sksl/SkSLCompiler.h
index 339da2b..cd70278 100644
--- a/src/sksl/SkSLCompiler.h
+++ b/src/sksl/SkSLCompiler.h
@@ -63,9 +63,24 @@
         kPermitInvalidStaticTests_Flag = 1,
     };
 
-    enum class FormatArg {
-        kInput,
-        kOutput
+    struct FormatArg {
+        enum class Kind {
+            kInput,
+            kOutput,
+            kUniform,
+            kChildProcessor
+        };
+
+        FormatArg(Kind kind)
+                : fKind(kind) {}
+
+        FormatArg(Kind kind, int index)
+                : fKind(kind)
+                , fIndex(index) {}
+
+        Kind fKind;
+
+        int fIndex;
     };
 
     Compiler(Flags flags = kNone_Flags);
diff --git a/src/sksl/SkSLIRGenerator.cpp b/src/sksl/SkSLIRGenerator.cpp
index 5d4e5c3..65fdbaf 100644
--- a/src/sksl/SkSLIRGenerator.cpp
+++ b/src/sksl/SkSLIRGenerator.cpp
@@ -1987,7 +1987,6 @@
 std::unique_ptr<Expression> IRGenerator::getArg(int offset, String name) const {
     auto found = fSettings->fArgs.find(name);
     if (found == fSettings->fArgs.end()) {
-        fErrors.error(offset, "unknown argument '" + name + "'");
         return nullptr;
     }
     String fullName = "sk_Args." + name;
diff --git a/src/sksl/SkSLPipelineStageCodeGenerator.cpp b/src/sksl/SkSLPipelineStageCodeGenerator.cpp
index aceb75e..4ca839d 100644
--- a/src/sksl/SkSLPipelineStageCodeGenerator.cpp
+++ b/src/sksl/SkSLPipelineStageCodeGenerator.cpp
@@ -76,6 +76,39 @@
     }
 }
 
+void PipelineStageCodeGenerator::writeFunctionCall(const FunctionCall& c) {
+    if (c.fFunction.fBuiltin && c.fFunction.fName == "process") {
+        SkASSERT(c.fArguments.size() == 1);
+        SkASSERT(Expression::kVariableReference_Kind == c.fArguments[0]->fKind);
+        int index = 0;
+        bool found = false;
+        for (const auto& p : fProgram) {
+            if (ProgramElement::kVar_Kind == p.fKind) {
+                const VarDeclarations& decls = (const VarDeclarations&) p;
+                for (const auto& raw : decls.fVars) {
+                    VarDeclaration& decl = (VarDeclaration&) *raw;
+                    if (decl.fVar == &((VariableReference&) *c.fArguments[0]).fVariable) {
+                        found = true;
+                    } else if (decl.fVar->fType == *fContext.fFragmentProcessor_Type) {
+                        ++index;
+                    }
+                }
+            }
+            if (found) {
+                break;
+            }
+        }
+        SkASSERT(found);
+        fExtraEmitCodeCode += "        this->emitChild(" + to_string(index) + ", fChildren[" +
+                              to_string(index) + "], args);\n";
+        this->write("%s");
+        fFormatArgs->push_back(Compiler::FormatArg(Compiler::FormatArg::Kind::kChildProcessor,
+                                                   index));
+        return;
+    }
+    INHERITED::writeFunctionCall(c);
+}
+
 void PipelineStageCodeGenerator::writeIntLiteral(const IntLiteral& i) {
     this->write(to_string((int32_t) i.fValue));
 }
@@ -84,11 +117,11 @@
     switch (ref.fVariable.fModifiers.fLayout.fBuiltin) {
         case SK_INCOLOR_BUILTIN:
             this->write("%s");
-            fFormatArgs->push_back(Compiler::FormatArg::kInput);
+            fFormatArgs->push_back(Compiler::FormatArg(Compiler::FormatArg::Kind::kInput));
             break;
         case SK_OUTCOLOR_BUILTIN:
             this->write("%s");
-            fFormatArgs->push_back(Compiler::FormatArg::kOutput);
+            fFormatArgs->push_back(Compiler::FormatArg(Compiler::FormatArg::Kind::kOutput));
             break;
         case SK_MAIN_X_BUILTIN:
             this->write("sk_FragCoord.x");
@@ -97,7 +130,36 @@
             this->write("sk_FragCoord.y");
             break;
         default:
-            this->write(ref.fVariable.fName);
+            if (ref.fVariable.fModifiers.fFlags & Modifiers::kUniform_Flag) {
+                this->write("%s");
+                int index = 0;
+                bool found = false;
+                for (const auto& e : fProgram) {
+                    if (found) {
+                        break;
+                    }
+                    if (e.fKind == ProgramElement::Kind::kVar_Kind) {
+                        const VarDeclarations& decls = (const VarDeclarations&) e;
+                        for (const auto& decl : decls.fVars) {
+                            const Variable& var = *((VarDeclaration&) *decl).fVar;
+                            if (&var == &ref.fVariable) {
+                                found = true;
+                                break;
+                            }
+                            if (var.fModifiers.fFlags & (Modifiers::kIn_Flag |
+                                                         Modifiers::kUniform_Flag)) {
+                                ++index;
+                            }
+                        }
+                    }
+                }
+                SkASSERT(found);
+                fFormatArgs->push_back(Compiler::FormatArg(Compiler::FormatArg::Kind::kUniform,
+                                                           index));
+            }
+            else {
+                this->write(ref.fVariable.fName);
+            }
     }
 }
 
@@ -122,8 +184,8 @@
         StringStream buffer;
         fOut = &buffer;
         this->write("%s = %s;\n");
-        fFormatArgs->push_back(Compiler::FormatArg::kOutput);
-        fFormatArgs->push_back(Compiler::FormatArg::kInput);
+        fFormatArgs->push_back(Compiler::FormatArg(Compiler::FormatArg::Kind::kOutput));
+        fFormatArgs->push_back(Compiler::FormatArg(Compiler::FormatArg::Kind::kInput));
         for (const auto& s : ((Block&) *f.fBody).fStatements) {
             this->writeStatement(*s);
             this->writeLine();
diff --git a/src/sksl/SkSLPipelineStageCodeGenerator.h b/src/sksl/SkSLPipelineStageCodeGenerator.h
index 09b40ce..9de7a4a 100644
--- a/src/sksl/SkSLPipelineStageCodeGenerator.h
+++ b/src/sksl/SkSLPipelineStageCodeGenerator.h
@@ -36,6 +36,8 @@
 
     void writeBinaryExpression(const BinaryExpression& b, Precedence parentPrecedence) override;
 
+    void writeFunctionCall(const FunctionCall& c) override;
+
     void writeIntLiteral(const IntLiteral& i) override;
 
     void writeVariableReference(const VariableReference& ref) override;
diff --git a/src/sksl/sksl_pipeline.inc b/src/sksl/sksl_pipeline.inc
index f0a2221..f1e749f 100644
--- a/src/sksl/sksl_pipeline.inc
+++ b/src/sksl/sksl_pipeline.inc
@@ -16,4 +16,6 @@
     layout(builtin=10009) int sk_x;
     layout(builtin=10010) int sk_y;
     layout(builtin=10004) out half4 sk_OutColor;
+
+    half4 process(fragmentProcessor fp);
 )