Revert "Revert "added GrSkSLFP and converted DitherEffect to use it""

This reverts commit f2030783094e502fb74221077a5ee7cb41287fe4.

Bug: skia:
Change-Id: Icaaa8b3ea652a8f126bfbcc788a360493a7ebe3e
Reviewed-on: https://skia-review.googlesource.com/137391
Commit-Queue: Ethan Nicholas <ethannicholas@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
diff --git a/src/gpu/effects/GrDitherEffect.cpp b/src/gpu/effects/GrDitherEffect.cpp
deleted file mode 100644
index 17c8776..0000000
--- a/src/gpu/effects/GrDitherEffect.cpp
+++ /dev/null
@@ -1,75 +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 GrDitherEffect.fp; do not modify.
- **************************************************************************************************/
-#include "GrDitherEffect.h"
-#include "glsl/GrGLSLFragmentProcessor.h"
-#include "glsl/GrGLSLFragmentShaderBuilder.h"
-#include "glsl/GrGLSLProgramBuilder.h"
-#include "GrTexture.h"
-#include "SkSLCPP.h"
-#include "SkSLUtil.h"
-class GrGLSLDitherEffect : public GrGLSLFragmentProcessor {
-public:
-    GrGLSLDitherEffect() {}
-    void emitCode(EmitArgs& args) override {
-        GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
-        const GrDitherEffect& _outer = args.fFp.cast<GrDitherEffect>();
-        (void)_outer;
-        auto rangeType = _outer.rangeType();
-        (void)rangeType;
-        fragBuilder->codeAppendf(
-                "half value;\nhalf range;\n@switch (%d) {\n    case 0:\n        range = "
-                "0.0039215686274509803;\n        break;\n    case 1:\n        range = "
-                "0.015873015873015872;\n        break;\n    default:\n        range = "
-                "0.066666666666666666;\n        break;\n}\n@if (sk_Caps.integerSupport) {\n    "
-                "uint x = uint(sk_FragCoord.x);\n    uint y = uint(sk_FragCoord.y);\n    uint m = "
-                "(((((y & 1) << 5 | (x & 1) << 4) | (y & 2) << 2) | (x & 2) << 1) | (y & 4) >> 1) "
-                "| (x & 4) >> 2;\n    value = float(float(half(m)) / 64.0) - 0.4",
-                _outer.rangeType());
-        fragBuilder->codeAppendf(
-                "921875;\n} else {\n    half4 modValues = half4(mod(sk_FragCoord.xyxy, "
-                "float4(half4(2.0, 2.0, 4.0, 4.0))));\n    half4 stepValues = "
-                "half4(step(float4(modValues), float4(half4(1.0, 1.0, 2.0, 2.0))));\n    value = "
-                "float(dot(stepValues, half4(0.5, 0.25, 0.125, 0.0625))) - 0.46875;\n}\n%s = "
-                "half4(clamp(float3(%s.xyz + value * range), 0.0, float(%s.w)), %s.w);\n",
-                args.fOutputColor, args.fInputColor ? args.fInputColor : "half4(1)",
-                args.fInputColor ? args.fInputColor : "half4(1)",
-                args.fInputColor ? args.fInputColor : "half4(1)");
-    }
-
-private:
-    void onSetData(const GrGLSLProgramDataManager& pdman,
-                   const GrFragmentProcessor& _proc) override {}
-};
-GrGLSLFragmentProcessor* GrDitherEffect::onCreateGLSLInstance() const {
-    return new GrGLSLDitherEffect();
-}
-void GrDitherEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps,
-                                           GrProcessorKeyBuilder* b) const {
-    b->add32((int32_t)fRangeType);
-}
-bool GrDitherEffect::onIsEqual(const GrFragmentProcessor& other) const {
-    const GrDitherEffect& that = other.cast<GrDitherEffect>();
-    (void)that;
-    if (fRangeType != that.fRangeType) return false;
-    return true;
-}
-GrDitherEffect::GrDitherEffect(const GrDitherEffect& src)
-        : INHERITED(kGrDitherEffect_ClassID, src.optimizationFlags()), fRangeType(src.fRangeType) {}
-std::unique_ptr<GrFragmentProcessor> GrDitherEffect::clone() const {
-    return std::unique_ptr<GrFragmentProcessor>(new GrDitherEffect(*this));
-}
-GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrDitherEffect);
-#if GR_TEST_UTILS
-std::unique_ptr<GrFragmentProcessor> GrDitherEffect::TestCreate(GrProcessorTestData* testData) {
-    float range = testData->fRandom->nextRangeF(0.001f, 0.05f);
-    return std::unique_ptr<GrFragmentProcessor>(new GrDitherEffect(range));
-}
-#endif
diff --git a/src/gpu/effects/GrDitherEffect.fp b/src/gpu/effects/GrDitherEffect.fp
deleted file mode 100644
index ed6c0e6..0000000
--- a/src/gpu/effects/GrDitherEffect.fp
+++ /dev/null
@@ -1,85 +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 controls the range of values added to color channels
-layout(key) in int rangeType;
-
-@make {
-    static std::unique_ptr<GrFragmentProcessor> Make(GrPixelConfig dstConfig) {
-        int rangeType;
-        switch (dstConfig) {
-            case kGray_8_GrPixelConfig:
-            case kGray_8_as_Lum_GrPixelConfig:
-            case kGray_8_as_Red_GrPixelConfig:
-            case kRGBA_8888_GrPixelConfig:
-            case kRGB_888_GrPixelConfig:
-            case kBGRA_8888_GrPixelConfig:
-                rangeType = 0;
-                break;
-            case kRGB_565_GrPixelConfig:
-                rangeType = 1;
-                break;
-            case kRGBA_4444_GrPixelConfig:
-                rangeType = 2;
-                break;
-            case kUnknown_GrPixelConfig:
-            case kSRGBA_8888_GrPixelConfig:
-            case kSBGRA_8888_GrPixelConfig:
-            case kRGBA_1010102_GrPixelConfig:
-            case kAlpha_half_GrPixelConfig:
-            case kAlpha_half_as_Red_GrPixelConfig:
-            case kRGBA_float_GrPixelConfig:
-            case kRG_float_GrPixelConfig:
-            case kRGBA_half_GrPixelConfig:
-            case kAlpha_8_GrPixelConfig:
-            case kAlpha_8_as_Alpha_GrPixelConfig:
-            case kAlpha_8_as_Red_GrPixelConfig:
-                return nullptr;
-        }
-        return std::unique_ptr<GrFragmentProcessor>(new GrDitherEffect(rangeType));
-    }
-}
-
-void main() {
-    half value;
-    half range;
-    @switch (rangeType) {
-        case 0:
-            range = 1.0 / 255.0;
-            break;
-        case 1:
-            range = 1.0 / 63.0;
-            break;
-        default:
-            // Experimentally this looks better than the expected value of 1/15.
-            range = 1.0 / 15.0;
-            break;
-    }
-    @if (sk_Caps.integerSupport) {
-        // This ordered-dither code is lifted from the cpu backend.
-        uint x = uint(sk_FragCoord.x);
-        uint y = uint(sk_FragCoord.y);
-        uint m = (y & 1) << 5 | (x & 1) << 4 |
-                 (y & 2) << 2 | (x & 2) << 1 |
-                 (y & 4) >> 1 | (x & 4) >> 2;
-        value = half(m) * 1.0 / 64.0 - 63.0 / 128.0;
-    } else {
-        // Simulate the integer effect used above using step/mod. For speed, simulates a 4x4
-        // dither pattern rather than an 8x8 one.
-        half4 modValues = mod(sk_FragCoord.xyxy, half4(2.0, 2.0, 4.0, 4.0));
-        half4 stepValues = step(modValues, half4(1.0, 1.0, 2.0, 2.0));
-        value = dot(stepValues, half4(8.0 / 16.0, 4.0 / 16.0, 2.0 / 16.0, 1.0 / 16.0)) - 15.0 / 32.0;
-    }
-    // For each color channel, add the random offset to the channel value and then clamp
-    // between 0 and alpha to keep the color premultiplied.
-    sk_OutColor = half4(clamp(sk_InColor.rgb + value * range, 0, sk_InColor.a), sk_InColor.a);
-}
-
-@test(testData) {
-    float range = testData->fRandom->nextRangeF(0.001f, 0.05f);
-    return std::unique_ptr<GrFragmentProcessor>(new GrDitherEffect(range));
-}
diff --git a/src/gpu/effects/GrDitherEffect.h b/src/gpu/effects/GrDitherEffect.h
deleted file mode 100644
index 70adc45..0000000
--- a/src/gpu/effects/GrDitherEffect.h
+++ /dev/null
@@ -1,67 +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 GrDitherEffect.fp; do not modify.
- **************************************************************************************************/
-#ifndef GrDitherEffect_DEFINED
-#define GrDitherEffect_DEFINED
-#include "SkTypes.h"
-#include "GrFragmentProcessor.h"
-#include "GrCoordTransform.h"
-class GrDitherEffect : public GrFragmentProcessor {
-public:
-    int rangeType() const { return fRangeType; }
-
-    static std::unique_ptr<GrFragmentProcessor> Make(GrPixelConfig dstConfig) {
-        int rangeType;
-        switch (dstConfig) {
-            case kGray_8_GrPixelConfig:
-            case kGray_8_as_Lum_GrPixelConfig:
-            case kGray_8_as_Red_GrPixelConfig:
-            case kRGBA_8888_GrPixelConfig:
-            case kRGB_888_GrPixelConfig:
-            case kBGRA_8888_GrPixelConfig:
-                rangeType = 0;
-                break;
-            case kRGB_565_GrPixelConfig:
-                rangeType = 1;
-                break;
-            case kRGBA_4444_GrPixelConfig:
-                rangeType = 2;
-                break;
-            case kUnknown_GrPixelConfig:
-            case kSRGBA_8888_GrPixelConfig:
-            case kSBGRA_8888_GrPixelConfig:
-            case kRGBA_1010102_GrPixelConfig:
-            case kAlpha_half_GrPixelConfig:
-            case kAlpha_half_as_Red_GrPixelConfig:
-            case kRGBA_float_GrPixelConfig:
-            case kRG_float_GrPixelConfig:
-            case kRGBA_half_GrPixelConfig:
-            case kAlpha_8_GrPixelConfig:
-            case kAlpha_8_as_Alpha_GrPixelConfig:
-            case kAlpha_8_as_Red_GrPixelConfig:
-                return nullptr;
-        }
-        return std::unique_ptr<GrFragmentProcessor>(new GrDitherEffect(rangeType));
-    }
-    GrDitherEffect(const GrDitherEffect& src);
-    std::unique_ptr<GrFragmentProcessor> clone() const override;
-    const char* name() const override { return "DitherEffect"; }
-
-private:
-    GrDitherEffect(int rangeType)
-            : INHERITED(kGrDitherEffect_ClassID, kNone_OptimizationFlags), fRangeType(rangeType) {}
-    GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
-    void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override;
-    bool onIsEqual(const GrFragmentProcessor&) const override;
-    GR_DECLARE_FRAGMENT_PROCESSOR_TEST
-    int fRangeType;
-    typedef GrFragmentProcessor INHERITED;
-};
-#endif
diff --git a/src/gpu/effects/GrRectBlurEffect.cpp b/src/gpu/effects/GrRectBlurEffect.cpp
index 219fefa..d423b78 100644
--- a/src/gpu/effects/GrRectBlurEffect.cpp
+++ b/src/gpu/effects/GrRectBlurEffect.cpp
@@ -46,13 +46,13 @@
         fProfileSizeVar = args.fUniformHandler->addUniform(kFragment_GrShaderFlag, kHalf_GrSLType,
                                                            kDefault_GrSLPrecision, "profileSize");
         fragBuilder->codeAppendf(
-                "bool highPrecision = %s;\n@if (highPrecision) {\n    float2 translatedPos = "
-                "sk_FragCoord.xy - %s.xy;\n    float width = %s.z - %s.x;\n    float height = %s.w "
-                "- %s.y;\n    float2 smallDims = float2(width - float(%s), height - float(%s));\n  "
-                "  float center = 2.0 * floor(float(float(%s / 2.0) + 0.25)) - 1.0;\n    float2 wh "
-                "= smallDims - float2(center, center);\n    half hcoord = "
-                "half((abs(translatedPos.x - 0.5 * width) - 0.5 * wh.x) / float(%s));\n    half "
-                "hlookup = texture(%s, float2(float(hcoord), 0.5)).%s.w",
+                "/* key */ bool highPrecision = %s;\n@if (highPrecision) {\n    float2 "
+                "translatedPos = sk_FragCoord.xy - %s.xy;\n    float width = %s.z - %s.x;\n    "
+                "float height = %s.w - %s.y;\n    float2 smallDims = float2(width - float(%s), "
+                "height - float(%s));\n    float center = 2.0 * floor(float(float(%s / 2.0) + "
+                "0.25)) - 1.0;\n    float2 wh = smallDims - float2(center, center);\n    half "
+                "hcoord = half((abs(translatedPos.x - 0.5 * width) - 0.5 * wh.x) / float(%s));\n   "
+                " half hlookup = texture(%s, float2(float(hcoord), ",
                 (highPrecision ? "true" : "false"), args.fUniformHandler->getUniformCStr(fRectVar),
                 args.fUniformHandler->getUniformCStr(fRectVar),
                 args.fUniformHandler->getUniformCStr(fRectVar),
@@ -62,16 +62,16 @@
                 args.fUniformHandler->getUniformCStr(fProfileSizeVar),
                 args.fUniformHandler->getUniformCStr(fProfileSizeVar),
                 args.fUniformHandler->getUniformCStr(fProfileSizeVar),
-                fragBuilder->getProgramBuilder()->samplerVariable(args.fTexSamplers[0]).c_str(),
-                fragBuilder->getProgramBuilder()->samplerSwizzle(args.fTexSamplers[0]).c_str());
+                fragBuilder->getProgramBuilder()->samplerVariable(args.fTexSamplers[0]).c_str());
         fragBuilder->codeAppendf(
-                ";\n    half vcoord = half((abs(translatedPos.y - 0.5 * height) - 0.5 * wh.y) / "
-                "float(%s));\n    half vlookup = texture(%s, float2(float(vcoord), 0.5)).%s.w;\n   "
-                " %s = (%s * hlookup) * vlookup;\n} else {\n    half2 translatedPos = "
-                "half2(sk_FragCoord.xy - %s.xy);\n    half width = half(%s.z - %s.x);\n    half "
-                "height = half(%s.w - %s.y);\n    half2 smallDims = half2(width - %s, height - "
-                "%s);\n    half center = half(2.0 * floor(float(float(%s / 2.0) + 0.25)) - 1.0);\n "
-                "   half2 wh = smallDims - half2(float2(floa",
+                "0.5)).%s.w;\n    half vcoord = half((abs(translatedPos.y - 0.5 * height) - 0.5 * "
+                "wh.y) / float(%s));\n    half vlookup = texture(%s, float2(float(vcoord), "
+                "0.5)).%s.w;\n    %s = (%s * hlookup) * vlookup;\n} else {\n    half2 "
+                "translatedPos = half2(sk_FragCoord.xy - %s.xy);\n    half width = half(%s.z - "
+                "%s.x);\n    half height = half(%s.w - %s.y);\n    half2 smallDims = half2(width - "
+                "%s, height - %s);\n    half center = half(2.0 * floor(float(float(%s / 2.0) + "
+                "0.25)) - 1.0);\n    half2 wh = smallDims - half2(f",
+                fragBuilder->getProgramBuilder()->samplerSwizzle(args.fTexSamplers[0]).c_str(),
                 args.fUniformHandler->getUniformCStr(fProfileSizeVar),
                 fragBuilder->getProgramBuilder()->samplerVariable(args.fTexSamplers[0]).c_str(),
                 fragBuilder->getProgramBuilder()->samplerSwizzle(args.fTexSamplers[0]).c_str(),
@@ -85,7 +85,7 @@
                 args.fUniformHandler->getUniformCStr(fProfileSizeVar),
                 args.fUniformHandler->getUniformCStr(fProfileSizeVar));
         fragBuilder->codeAppendf(
-                "t(center), float(center)));\n    half hcoord = "
+                "loat2(float(center), float(center)));\n    half hcoord = "
                 "half((abs(float(float(translatedPos.x) - 0.5 * float(width))) - 0.5 * "
                 "float(wh.x)) / float(%s));\n    half hlookup = texture(%s, float2(float(hcoord), "
                 "0.5)).%s.w;\n    half vcoord = half((abs(float(float(translatedPos.y) - 0.5 * "
diff --git a/src/gpu/effects/GrSkSLFP.cpp b/src/gpu/effects/GrSkSLFP.cpp
new file mode 100644
index 0000000..bc84c83
--- /dev/null
+++ b/src/gpu/effects/GrSkSLFP.cpp
@@ -0,0 +1,268 @@
+/*
+ * Copyright 2018 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrSkSLFP.h"
+#include "glsl/GrGLSLFragmentProcessor.h"
+#include "glsl/GrGLSLFragmentShaderBuilder.h"
+#include "glsl/GrGLSLProgramBuilder.h"
+#include "GrContext.h"
+#include "GrContextPriv.h"
+#include "GrTexture.h"
+#include "SkSLUtil.h"
+
+GrSkSLFPFactory::GrSkSLFPFactory(const char* name, const GrShaderCaps* shaderCaps, const char* sksl)
+        : fName(name) {
+    SkSL::Program::Settings settings;
+    settings.fCaps = shaderCaps;
+    fBaseProgram = fCompiler.convertProgram(SkSL::Program::kPipelineStage_Kind,
+                                            SkSL::String(sksl),
+                                            settings);
+    if (fCompiler.errorCount()) {
+        SkDebugf("%s\n", fCompiler.errorText().c_str());
+    }
+    SkASSERT(fBaseProgram);
+    SkASSERT(!fCompiler.errorCount());
+    for (const auto& e : *fBaseProgram) {
+        if (e.fKind == SkSL::ProgramElement::kVar_Kind) {
+            SkSL::VarDeclarations& v = (SkSL::VarDeclarations&) e;
+            for (const auto& varStatement : v.fVars) {
+                const SkSL::Variable& var = *((SkSL::VarDeclaration&) *varStatement).fVar;
+                if (var.fModifiers.fFlags & SkSL::Modifiers::kIn_Flag) {
+                    fInputVars.push_back(&var);
+                }
+                if (var.fModifiers.fLayout.fKey) {
+                    fKeyVars.push_back(&var);
+                }
+            }
+        }
+    }
+}
+
+const SkSL::Program* GrSkSLFPFactory::getSpecialization(const SkSL::String& key, const void* inputs,
+                                                        size_t inputSize) {
+    const auto& found = fSpecializations.find(key);
+    if (found != fSpecializations.end()) {
+        return found->second.get();
+    }
+
+    std::unordered_map<SkSL::String, SkSL::Program::Settings::Value> inputMap;
+    size_t offset = 0;
+    for (const auto& v : fInputVars) {
+        SkSL::String name(v->fName);
+        if (&v->fType == fCompiler.context().fInt_Type.get()) {
+            offset = SkAlign4(offset);
+            int32_t v = *(int32_t*) (((uint8_t*) inputs) + offset);
+            inputMap.insert(std::make_pair(name, SkSL::Program::Settings::Value(v)));
+            offset += sizeof(int32_t);
+        }
+    }
+    SkASSERT(offset == inputSize);
+
+    std::unique_ptr<SkSL::Program> specialized = fCompiler.specialize(*fBaseProgram, inputMap);
+    SkAssertResult(fCompiler.optimize(*specialized));
+    const SkSL::Program* result = specialized.get();
+    fSpecializations.insert(std::make_pair(key, std::move(specialized)));
+    return result;
+}
+
+class GrGLSLSkSLFP : public GrGLSLFragmentProcessor {
+public:
+    GrGLSLSkSLFP(SkSL::String glsl, std::vector<SkSL::Compiler::FormatArg> formatArgs)
+            : fGLSL(glsl)
+            , fFormatArgs(formatArgs) {}
+
+    void emitCode(EmitArgs& args) override {
+        GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
+        int substringStartIndex = 0;
+        int formatArgIndex = 0;
+        for (size_t i = 0; i < fGLSL.length(); ++i) {
+            char c = fGLSL[i];
+            if (c == '%') {
+                fragBuilder->codeAppend(fGLSL.c_str() + substringStartIndex,
+                                        i - substringStartIndex);
+                ++i;
+                c = fGLSL[i];
+                switch (c) {
+                    case 's':
+                        switch (fFormatArgs[formatArgIndex++]) {
+                            case SkSL::Compiler::FormatArg::kInput:
+                                fragBuilder->codeAppend(args.fInputColor ? args.fInputColor
+                                                                         : "half4(1)");
+                                break;
+                            case SkSL::Compiler::FormatArg::kOutput:
+                                fragBuilder->codeAppend(args.fOutputColor);
+                                break;
+                        }
+                        break;
+                    default:
+                        fragBuilder->codeAppendf("%c", c);
+                }
+                substringStartIndex = i + 1;
+            }
+        }
+        fragBuilder->codeAppend(fGLSL.c_str() + substringStartIndex,
+                                fGLSL.length() - substringStartIndex);
+    }
+
+    // nearly-finished GLSL; still contains printf-style "%s" format tokens
+    const SkSL::String fGLSL;
+    std::vector<SkSL::Compiler::FormatArg> fFormatArgs;
+};
+
+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));
+}
+
+
+GrSkSLFP::GrSkSLFP(sk_sp<GrSkSLFPFactoryCache> factoryCache, const GrShaderCaps* shaderCaps,
+                   int index, const char* name, const char* sksl, const void* inputs,
+                   size_t inputSize)
+        : INHERITED(kGrSkSLFP_ClassID, kNone_OptimizationFlags)
+        , fFactoryCache(factoryCache)
+        , fShaderCaps(sk_ref_sp(shaderCaps))
+        , fIndex(index)
+        , fName(name)
+        , fSkSL(sksl)
+        , fInputs(new int8_t[inputSize])
+        , fInputSize(inputSize) {
+    memcpy(fInputs.get(), inputs, inputSize);
+}
+
+GrSkSLFP::GrSkSLFP(const GrSkSLFP& other)
+        : INHERITED(kGrSkSLFP_ClassID, kNone_OptimizationFlags)
+        , fFactoryCache(other.fFactoryCache)
+        , fShaderCaps(other.fShaderCaps)
+        , fFactory(other.fFactory)
+        , fIndex(other.fIndex)
+        , fName(other.fName)
+        , fSkSL(other.fSkSL)
+        , fInputs(new int8_t[other.fInputSize])
+        , fInputSize(other.fInputSize) {
+    memcpy(fInputs.get(), other.fInputs.get(), fInputSize);
+}
+
+const char* GrSkSLFP::name() const {
+    return fName;
+}
+
+void GrSkSLFP::createFactory() const {
+    if (!fFactory) {
+        fFactory = fFactoryCache->get(fIndex);
+        if (!fFactory) {
+            fFactory = sk_sp<GrSkSLFPFactory>(new GrSkSLFPFactory(fName, fShaderCaps.get(), fSkSL));
+            fFactoryCache->set(fIndex, fFactory);
+        }
+    }
+}
+
+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)) {
+        printf("%s\n", fFactory->fCompiler.errorText().c_str());
+        abort();
+    }
+    return new GrGLSLSkSLFP(glsl, formatArgs);
+}
+
+void GrSkSLFP::onGetGLSLProcessorKey(const GrShaderCaps& caps,
+                                     GrProcessorKeyBuilder* b) const {
+    this->createFactory();
+    size_t offset = 0;
+    char* inputs = (char*) fInputs.get();
+    for (const auto& v : fFactory->fInputVars) {
+        if (&v->fType == fFactory->fCompiler.context().fInt_Type.get()) {
+            offset = SkAlign4(offset);
+            if (v->fModifiers.fLayout.fKey) {
+                fKey += inputs[offset + 0];
+                fKey += inputs[offset + 1];
+                fKey += inputs[offset + 2];
+                fKey += inputs[offset + 3];
+                b->add32(*(int32_t*) (inputs + offset));
+            }
+            offset += sizeof(int32_t);
+        }
+        else {
+            // unsupported input var type
+            SkASSERT(false);
+        }
+    }
+    SkASSERT(offset == fInputSize);
+}
+
+bool GrSkSLFP::onIsEqual(const GrFragmentProcessor& other) const {
+    const GrSkSLFP& sk = other.cast<GrSkSLFP>();
+    SkASSERT(fIndex != sk.fIndex || fInputSize == sk.fInputSize);
+    return fIndex == sk.fIndex &&
+            !memcmp(fInputs.get(), sk.fInputs.get(), fInputSize);
+}
+
+std::unique_ptr<GrFragmentProcessor> GrSkSLFP::clone() const {
+    return std::unique_ptr<GrFragmentProcessor>(new GrSkSLFP(*this));
+}
+
+// We have to do a bit of manual refcounting in the cache methods below. Ideally, we could just
+// define fFactories to contain sk_sp<GrSkSLFPFactory> rather than GrSkSLFPFactory*, but that would
+// require GrContext to include GrSkSLFP, which creates much bigger headaches than a few manual
+// refcounts.
+
+sk_sp<GrSkSLFPFactory> GrSkSLFPFactoryCache::get(int index) {
+    if (index >= (int) fFactories.size()) {
+        return nullptr;
+    }
+    GrSkSLFPFactory* result = fFactories[index];
+    result->ref();
+    return sk_sp<GrSkSLFPFactory>(result);
+}
+
+void GrSkSLFPFactoryCache::set(int index, sk_sp<GrSkSLFPFactory> factory) {
+    while (index >= (int) fFactories.size()) {
+        fFactories.emplace_back();
+    }
+    factory->ref();
+    SkASSERT(!fFactories[index]);
+    fFactories[index] = factory.get();
+}
+
+GrSkSLFPFactoryCache::~GrSkSLFPFactoryCache() {
+    for (GrSkSLFPFactory* factory : fFactories) {
+        if (factory) {
+            factory->unref();
+        }
+    }
+}
+
+GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrSkSLFP);
+
+#if GR_TEST_UTILS
+
+#include "SkGr.h"
+
+using Value = SkSL::Program::Settings::Value;
+
+std::unique_ptr<GrFragmentProcessor> GrSkSLFP::TestCreate(GrProcessorTestData* d) {
+    int type = d->fRandom->nextULessThan(1);
+    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));
+        }
+    }
+    SK_ABORT("unreachable");
+    return nullptr;
+}
+
+#endif
diff --git a/src/gpu/effects/GrSkSLFP.h b/src/gpu/effects/GrSkSLFP.h
new file mode 100644
index 0000000..428e089
--- /dev/null
+++ b/src/gpu/effects/GrSkSLFP.h
@@ -0,0 +1,147 @@
+/*
+ * Copyright 2018 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrSkSLFP_DEFINED
+#define GrSkSLFP_DEFINED
+
+#include "GrCaps.h"
+#include "GrFragmentProcessor.h"
+#include "GrCoordTransform.h"
+#include "GrShaderCaps.h"
+#include "SkSLCompiler.h"
+#include "SkSLPipelineStageCodeGenerator.h"
+#include "SkRefCnt.h"
+#include "../private/GrSkSLFPFactoryCache.h"
+
+class GrContext;
+class GrSkSLFPFactory;
+
+class GrSkSLFP : public GrFragmentProcessor {
+public:
+    /**
+     * Returns a new unique identifier. Each different SkSL fragment processor should call
+     * NewIndex once, statically, and use this index for all calls to Make.
+     */
+    static int NewIndex() {
+        static int index = 0;
+        return sk_atomic_inc(&index);
+    }
+
+    /**
+     * Creates a new fragment processor from an SkSL source string and a struct of inputs to the
+     * program. The input struct's type is derived from the 'in' variables in the SkSL source, so
+     * e.g. the shader:
+     *
+     *    in bool dither;
+     *    in float x;
+     *    in float y;
+     *    ....
+     *
+     * would expect a pointer to a struct set up like:
+     *
+     * struct {
+     *     bool dither;
+     *     float x;
+     *     float y;
+     * };
+     *
+     * As turning SkSL into GLSL / SPIR-V / etc. is fairly expensive, and the output may differ
+     * based on the inputs, internally the process is divided into two steps: we first parse and
+     * semantically analyze the SkSL into an internal representation, and then "specialize" this
+     * internal representation based on the inputs. The unspecialized internal representation of
+     * the program is cached, so further specializations of the same code are much faster than the
+     * first call.
+     *
+     * This caching is based on the 'index' parameter, which should be derived by statically calling
+     * 'NewIndex()'. Each given SkSL string should have a single, statically defined index
+     * associated with it.
+     */
+    static std::unique_ptr<GrFragmentProcessor> Make(
+                   GrContext* context,
+                   int index,
+                   const char* name,
+                   const char* sksl,
+                   const void* inputs,
+                   size_t inputSize);
+
+    const char* name() const override;
+
+    std::unique_ptr<GrFragmentProcessor> clone() const override;
+
+private:
+    GrSkSLFP(sk_sp<GrSkSLFPFactoryCache> factoryCache, const GrShaderCaps* shaderCaps, int fIndex,
+             const char* name, const char* sksl, const void* inputs, size_t inputSize);
+
+    GrSkSLFP(const GrSkSLFP& other);
+
+    GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
+
+    void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override;
+
+    bool onIsEqual(const GrFragmentProcessor&) const override;
+
+    void createFactory() const;
+
+    sk_sp<GrSkSLFPFactoryCache> fFactoryCache;
+
+    const sk_sp<GrShaderCaps> fShaderCaps;
+
+    mutable sk_sp<GrSkSLFPFactory> fFactory;
+
+    int fIndex;
+
+    const char* fName;
+
+    const char* fSkSL;
+
+    const std::unique_ptr<int8_t[]> fInputs;
+
+    size_t fInputSize;
+
+    mutable SkSL::String fKey;
+
+    GR_DECLARE_FRAGMENT_PROCESSOR_TEST
+
+    typedef GrFragmentProcessor INHERITED;
+
+    friend class GrSkSLFPFactory;
+};
+
+/**
+ * Produces GrFragmentProcessors from SkSL code. As the shader code produced from the SkSL depends
+ * upon the inputs to the SkSL (static if's, etc.) we first create a factory for a given SkSL
+ * string, then use that to create the actual GrFragmentProcessor.
+ */
+class GrSkSLFPFactory : public SkNVRefCnt<GrSkSLFPFactory> {
+public:
+    /**
+     * Constructs a GrSkSLFPFactory for a given SkSL source string. Creating a factory will
+     * preprocess the SkSL and determine which of its inputs are declared "key" (meaning they cause
+     * the produced shaders to differ), so it is important to reuse the same factory instance for
+     * the same shader in order to avoid repeatedly re-parsing the SkSL.
+     */
+    GrSkSLFPFactory(const char* name, const GrShaderCaps* shaderCaps, const char* sksl);
+
+    const SkSL::Program* getSpecialization(const SkSL::String& key, const void* inputs,
+                                           size_t inputSize);
+
+    const char* fName;
+
+    SkSL::Compiler fCompiler;
+
+    std::shared_ptr<SkSL::Program> fBaseProgram;
+
+    std::vector<const SkSL::Variable*> fInputVars;
+
+    std::vector<const SkSL::Variable*> fKeyVars;
+
+    std::unordered_map<SkSL::String, std::unique_ptr<const SkSL::Program>> fSpecializations;
+
+    friend class GrSkSLFP;
+};
+
+#endif