Make GrRectBlurEffect be analytical rather than use a texture LUT.

The math isn't that complex and the existing code has intergralization of
the profile size that makes getting the texture coords correct tricky
(and currently wrong).


Bug: skia:9319
Change-Id: Ic928737ce4c40d28ee0696c3628e914f35ffe371
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/233985
Commit-Queue: Brian Salomon <bsalomon@google.com>
Reviewed-by: Robert Phillips <robertphillips@google.com>
diff --git a/gm/skbug_9319.cpp b/gm/skbug_9319.cpp
new file mode 100644
index 0000000..5100131
--- /dev/null
+++ b/gm/skbug_9319.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2019 Google LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "gm/gm.h"
+#include "include/core/SkCanvas.h"
+#include "include/core/SkMaskFilter.h"
+#include "include/core/SkPaint.h"
+#include "include/core/SkRRect.h"
+#include "include/core/SkRect.h"
+
+// Illustrates a bug where the outer portion of the GPU rect blur was too dark with a small sigma.
+DEF_SIMPLE_GM(skbug_9319, canvas, 256, 512) {
+    SkPaint p;
+    p.setAntiAlias(true);
+    p.setMaskFilter(SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, 0.5f));
+
+    const auto r = SkRect::MakeXYWH(10, 10, 100, 100);
+
+    {
+        SkAutoCanvasRestore acr(canvas, true);
+        // Clip out interior so that the outer portion stands out.
+        canvas->clipRect(r, SkClipOp::kDifference);
+        canvas->drawRect(r, p);
+    }
+
+    canvas->translate(0, 120);
+
+
+    // RRect for comparison.
+    const auto rr = SkRRect::MakeRectXY(r, .1f, .1f);
+    {
+        SkAutoCanvasRestore acr(canvas, true);
+        canvas->clipRRect(rr, SkClipOp::kDifference);
+        canvas->drawRRect(rr, p);
+    }
+}
diff --git a/gn/gm.gni b/gn/gm.gni
index 914facb..8f7df05 100644
--- a/gn/gm.gni
+++ b/gn/gm.gni
@@ -320,6 +320,7 @@
   "$_gm/skbug_5321.cpp",
   "$_gm/skbug_8664.cpp",
   "$_gm/skbug_8955.cpp",
+  "$_gm/skbug_9319.cpp",
   "$_gm/skinning.cpp",
   "$_gm/smallarc.cpp",
   "$_gm/smallpaths.cpp",
diff --git a/src/core/SkBlurMF.cpp b/src/core/SkBlurMF.cpp
index 09d2f2a..a173e56 100644
--- a/src/core/SkBlurMF.cpp
+++ b/src/core/SkBlurMF.cpp
@@ -762,11 +762,8 @@
 
     if (devRRect.isRect() || SkRRectPriv::IsCircle(devRRect)) {
         if (devRRect.isRect()) {
-            SkScalar pad = 3.0f * xformedSigma;
-            const SkRect dstCoverageRect = devRRect.rect().makeOutset(pad, pad);
-
             fp = GrRectBlurEffect::Make(proxyProvider, *context->priv().caps()->shaderCaps(),
-                                        dstCoverageRect, xformedSigma);
+                                        devRRect.rect(), xformedSigma);
         } else {
             fp = GrCircleBlurFragmentProcessor::Make(proxyProvider, devRRect.rect(), xformedSigma);
         }
diff --git a/src/gpu/effects/GrRectBlurEffect.fp b/src/gpu/effects/GrRectBlurEffect.fp
index f625af2..5498e73 100644
--- a/src/gpu/effects/GrRectBlurEffect.fp
+++ b/src/gpu/effects/GrRectBlurEffect.fp
@@ -12,140 +12,118 @@
     #include "src/gpu/GrShaderCaps.h"
 }
 
-in uniform float4 rect;
-in float sigma;
-in uniform sampler2D blurProfile;
+in float4 rect;
 
-@constructorParams {
-    GrSamplerState samplerParams
-}
+layout(key) bool highp = abs(rect.x) > 16000 || abs(rect.y) > 16000 ||
+                         abs(rect.z) > 16000 || abs(rect.w) > 16000;
 
-@samplerParams(blurProfile) {
-    samplerParams
-}
+layout(when= highp) uniform float4 rectF;
+layout(when=!highp) uniform half4  rectH;
 
-// in OpenGL ES, mediump floats have a minimum range of 2^14. If we have coordinates bigger than
-// that, the shader math will end up with infinities and result in the blur effect not working
-// correctly. To avoid this, we switch into highp when the coordinates are too big. As 2^14 is the
-// minimum range but the actual range can be bigger, we might end up switching to highp sooner than
-// strictly necessary, but most devices that have a bigger range for mediump also have mediump being
-// exactly the same as highp (e.g. all non-OpenGL ES devices), and thus incur no additional penalty
-// for the switch.
-layout(key) bool highPrecision = abs(rect.x) > 16000 || abs(rect.y) > 16000 ||
-                                 abs(rect.z) > 16000 || abs(rect.w) > 16000 ||
-                                 abs(rect.z - rect.x) > 16000 || abs(rect.w - rect.y) > 16000;
-
-layout(when=!highPrecision) uniform half4 proxyRectHalf;
-layout(when=highPrecision) uniform float4 proxyRectFloat;
-uniform half profileSize;
-
-
-@class {
-    static sk_sp<GrTextureProxy> CreateBlurProfileTexture(GrProxyProvider* proxyProvider,
-                                                          float sigma) {
-        unsigned int profileSize = SkScalarCeilToInt(6 * sigma);
-
-        static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
-        GrUniqueKey key;
-        GrUniqueKey::Builder builder(&key, kDomain, 1, "Rect Blur Mask");
-        builder[0] = profileSize;
-        builder.finish();
-
-        sk_sp<GrTextureProxy> blurProfile(proxyProvider->findOrCreateProxyByUniqueKey(
-                key, GrColorType::kAlpha_8, kTopLeft_GrSurfaceOrigin));
-        if (!blurProfile) {
-            SkImageInfo ii = SkImageInfo::MakeA8(profileSize, 1);
-
-            SkBitmap bitmap;
-            if (!bitmap.tryAllocPixels(ii)) {
-                return nullptr;
-            }
-
-            SkBlurMask::ComputeBlurProfile(bitmap.getAddr8(0, 0), profileSize, sigma);
-            bitmap.setImmutable();
-
-            sk_sp<SkImage> image = SkImage::MakeFromBitmap(bitmap);
-            if (!image) {
-                return nullptr;
-            }
-
-            blurProfile = proxyProvider->createTextureProxy(std::move(image),
-                                                            1, SkBudgeted::kYes,
-                                                            SkBackingFit::kExact);
-            if (!blurProfile) {
-                return nullptr;
-            }
-
-            SkASSERT(blurProfile->origin() == kTopLeft_GrSurfaceOrigin);
-            proxyProvider->assignUniqueKeyToProxy(key, blurProfile.get());
-        }
-
-        return blurProfile;
-    }
-}
+in uniform half sigma;
 
 @make {
      static std::unique_ptr<GrFragmentProcessor> Make(GrProxyProvider* proxyProvider,
                                                       const GrShaderCaps& caps,
                                                       const SkRect& rect, float sigma) {
+         float doubleProfileSize = (12 * sigma);
          if (!caps.floatIs32Bits()) {
-             // We promote the rect uniform from half to float when it has large values for
-             // precision. If we don't have full float then fail.
-             if (SkScalarAbs(rect.fLeft) > 16000.f || SkScalarAbs(rect.fTop) > 16000.f ||
-                 SkScalarAbs(rect.fRight) > 16000.f || SkScalarAbs(rect.fBottom) > 16000.f ||
-                 SkScalarAbs(rect.width()) > 16000.f || SkScalarAbs(rect.height()) > 16000.f) {
-                 return nullptr;
+             // We promote the math that gets us into the Gaussian space to full float when the rect
+             // coords are large. If we don't have full float then fail. We could probably clip the
+             // rect to an outset device bounds instead.
+             if (SkScalarAbs(rect.fLeft)  > 16000.f || SkScalarAbs(rect.fTop)    > 16000.f ||
+                 SkScalarAbs(rect.fRight) > 16000.f || SkScalarAbs(rect.fBottom) > 16000.f) {
+                    return nullptr;
              }
          }
-         int doubleProfileSize = SkScalarCeilToInt(12*sigma);
+         // Sigma is always a half.
+         SkASSERT(sigma > 0);
+         if (sigma > 16000.f) {
+             return nullptr;
+         }
 
-         if (doubleProfileSize >= rect.width() || doubleProfileSize >= rect.height()) {
+         if (doubleProfileSize >= (float) rect.width() ||
+             doubleProfileSize >= (float) rect.height()) {
              // if the blur sigma is too large so the gaussian overlaps the whole
              // rect in either direction, fall back to CPU path for now.
              return nullptr;
          }
 
-         sk_sp<GrTextureProxy> blurProfile(CreateBlurProfileTexture(proxyProvider, sigma));
-         if (!blurProfile) {
-            return nullptr;
-         }
-
-         return std::unique_ptr<GrFragmentProcessor>(new GrRectBlurEffect(
-            rect, sigma, std::move(blurProfile),
-            GrSamplerState(GrSamplerState::WrapMode::kClamp, GrSamplerState::Filter::kBilerp)));
+         return std::unique_ptr<GrFragmentProcessor>(new GrRectBlurEffect(rect, sigma));
      }
 }
 
 void main() {
-    @if (highPrecision) {
-        float2 translatedPos = sk_FragCoord.xy - rect.xy;
-        float width = rect.z - rect.x;
-        float height = rect.w - rect.y;
-        float2 smallDims = float2(width - profileSize, height - profileSize);
-        float center = 2 * floor(profileSize / 2 + 0.25) - 1;
-        float2 wh = smallDims - float2(center, center);
-        half hcoord = half(((abs(translatedPos.x - 0.5 * width) - 0.5 * wh.x)) / profileSize);
-        half hlookup = sample(blurProfile, float2(hcoord, 0.5)).a;
-        half vcoord = half(((abs(translatedPos.y - 0.5 * height) - 0.5 * wh.y)) / profileSize);
-        half vlookup = sample(blurProfile, float2(vcoord, 0.5)).a;
-        sk_OutColor = sk_InColor * hlookup * vlookup;
+    half invr = 1.0 / (2.0 * sigma);
+
+    // Get the smaller of the signed distance from the frag coord to the left and right edges.
+    half x;
+    @if (highp) {
+        float lDiff = rectF.x - sk_FragCoord.x;
+        float rDiff = sk_FragCoord.x - rectF.z;
+        x = half(max(lDiff, rDiff) * invr);
     } else {
-        half2 translatedPos = half2(sk_FragCoord.xy - rect.xy);
-        half width = half(rect.z - rect.x);
-        half height = half(rect.w - rect.y);
-        half2 smallDims = half2(width - profileSize, height - profileSize);
-        half center = 2 * floor(profileSize / 2 + 0.25) - 1;
-        half2 wh = smallDims - half2(center, center);
-        half hcoord = ((half(abs(translatedPos.x - 0.5 * width)) - 0.5 * wh.x)) / profileSize;
-        half hlookup = sample(blurProfile, float2(hcoord, 0.5)).a;
-        half vcoord = ((half(abs(translatedPos.y - 0.5 * height)) - 0.5 * wh.y)) / profileSize;
-        half vlookup = sample(blurProfile, float2(vcoord, 0.5)).a;
-        sk_OutColor = sk_InColor * hlookup * vlookup;
+        half lDiff = half(rectH.x - sk_FragCoord.x);
+        half rDiff = half(sk_FragCoord.x - rectH.z);
+        x = max(lDiff, rDiff) * invr;
     }
+    // This is lifted from the implementation of SkBlurMask::ComputeBlurProfile. It approximates
+    // a Gaussian as three box filters, and then computes the integral of this approximation from
+    // -inf to x.
+    // TODO: Make this a function when supported in .fp files as we duplicate it for y below.
+    half xCoverage;
+    if (x > 1.5) {
+        xCoverage = 0.0;
+    } else if (x < -1.5) {
+        xCoverage = 1.0;
+    } else {
+        half x2 = x * x;
+        half x3 = x2 * x;
+
+        if (x > 0.5) {
+            xCoverage = 0.5625 - (x3 / 6.0 - 3.0 * x2 * 0.25 + 1.125 * x);
+        } else if (x > -0.5) {
+            xCoverage = 0.5 - (0.75 * x - x3 / 3.0);
+        } else {
+            xCoverage = 0.4375 + (-x3 / 6.0 - 3.0 * x2 * 0.25 - 1.125 * x);
+        }
+    }
+
+    // Repeat of above for y.
+    half y;
+    @if (highp) {
+        float tDiff = rectF.y - sk_FragCoord.y;
+        float bDiff = sk_FragCoord.y - rectF.w;
+        y = half(max(tDiff, bDiff) * invr);
+    } else {
+        half tDiff = half(rectH.y - sk_FragCoord.y);
+        half bDiff = half(sk_FragCoord.y - rectH.w);
+        y = max(tDiff, bDiff) * invr;
+    }
+    half yCoverage;
+    if (y > 1.5) {
+        yCoverage = 0.0;
+    } else if (y < -1.5) {
+        yCoverage = 1.0;
+    } else {
+        half y2 = y * y;
+        half y3 = y2 * y;
+
+        if (y > 0.5) {
+            yCoverage = 0.5625 - (y3 / 6.0 - 3.0 * y2 * 0.25 + 1.125 * y);
+        } else if (y > -0.5) {
+            yCoverage = 0.5 - (0.75 * y - y3 / 3.0);
+        } else {
+            yCoverage = 0.4375 + (-y3 / 6.0 - 3.0 * y2 * 0.25 - 1.125 * y);
+        }
+    }
+
+    sk_OutColor = sk_InColor * xCoverage * yCoverage;
 }
 
 @setData(pdman) {
-    pdman.set1f(profileSize, SkScalarCeilToScalar(6 * sigma));
+    float r[] {rect.fLeft, rect.fTop, rect.fRight, rect.fBottom};
+    pdman.set4fv(highp ? rectF : rectH, 1, r);
 }
 
 @optimizationFlags { kCompatibleWithCoverageAsAlpha_OptimizationFlag }
diff --git a/src/gpu/effects/generated/GrRectBlurEffect.cpp b/src/gpu/effects/generated/GrRectBlurEffect.cpp
index 97daa7e..1dfd304 100644
--- a/src/gpu/effects/generated/GrRectBlurEffect.cpp
+++ b/src/gpu/effects/generated/GrRectBlurEffect.cpp
@@ -27,136 +27,102 @@
         (void)rect;
         auto sigma = _outer.sigma;
         (void)sigma;
-        highPrecision = ((((abs(rect.left()) > 16000.0 || abs(rect.top()) > 16000.0) ||
-                           abs(rect.right()) > 16000.0) ||
-                          abs(rect.bottom()) > 16000.0) ||
-                         abs(rect.right() - rect.left()) > 16000.0) ||
-                        abs(rect.bottom() - rect.top()) > 16000.0;
-        rectVar =
-                args.fUniformHandler->addUniform(kFragment_GrShaderFlag, kFloat4_GrSLType, "rect");
-        if (!highPrecision) {
-            proxyRectHalfVar = args.fUniformHandler->addUniform(kFragment_GrShaderFlag,
-                                                                kHalf4_GrSLType, "proxyRectHalf");
+        highp = ((abs(rect.left()) > 16000.0 || abs(rect.top()) > 16000.0) ||
+                 abs(rect.right()) > 16000.0) ||
+                abs(rect.bottom()) > 16000.0;
+        if (highp) {
+            rectFVar = args.fUniformHandler->addUniform(kFragment_GrShaderFlag, kFloat4_GrSLType,
+                                                        "rectF");
         }
-        if (highPrecision) {
-            proxyRectFloatVar = args.fUniformHandler->addUniform(
-                    kFragment_GrShaderFlag, kFloat4_GrSLType, "proxyRectFloat");
+        if (!highp) {
+            rectHVar = args.fUniformHandler->addUniform(kFragment_GrShaderFlag, kHalf4_GrSLType,
+                                                        "rectH");
         }
-        profileSizeVar = args.fUniformHandler->addUniform(kFragment_GrShaderFlag, kHalf_GrSLType,
-                                                          "profileSize");
+        sigmaVar =
+                args.fUniformHandler->addUniform(kFragment_GrShaderFlag, kHalf_GrSLType, "sigma");
         fragBuilder->codeAppendf(
-                "/* 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 = float(2.0 * floor(%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 = sample(%s, float2(float(hcoord), 0.5)).%s",
-                (highPrecision ? "true" : "false"), args.fUniformHandler->getUniformCStr(rectVar),
-                args.fUniformHandler->getUniformCStr(rectVar),
-                args.fUniformHandler->getUniformCStr(rectVar),
-                args.fUniformHandler->getUniformCStr(rectVar),
-                args.fUniformHandler->getUniformCStr(rectVar),
-                args.fUniformHandler->getUniformCStr(profileSizeVar),
-                args.fUniformHandler->getUniformCStr(profileSizeVar),
-                args.fUniformHandler->getUniformCStr(profileSizeVar),
-                args.fUniformHandler->getUniformCStr(profileSizeVar),
-                fragBuilder->getProgramBuilder()->samplerVariable(args.fTexSamplers[0]),
-                fragBuilder->getProgramBuilder()->samplerSwizzle(args.fTexSamplers[0]).c_str());
+                "/* key */ bool highp = %s;\nhalf invr = 1.0 / (2.0 * %s);\nhalf x;\n@if (highp) "
+                "{\n    float lDiff = %s.x - sk_FragCoord.x;\n    float rDiff = sk_FragCoord.x - "
+                "%s.z;\n    x = half(max(lDiff, rDiff) * float(invr));\n} else {\n    half lDiff = "
+                "half(float(%s.x) - sk_FragCoord.x);\n    half rDiff = half(sk_FragCoord.x - "
+                "float(%s.z));\n    x = max(lDiff, rDiff) * invr;\n}\nhalf xCoverage;\nif (x > "
+                "1.5) {\n    xCoverage = 0.0;\n} else if (x < -1.5) {\n    xCoverage = 1.0;\n} "
+                "else {\n    half x2 = x * x;\n    half",
+                (highp ? "true" : "false"), args.fUniformHandler->getUniformCStr(sigmaVar),
+                rectFVar.isValid() ? args.fUniformHandler->getUniformCStr(rectFVar) : "float4(0)",
+                rectFVar.isValid() ? args.fUniformHandler->getUniformCStr(rectFVar) : "float4(0)",
+                rectHVar.isValid() ? args.fUniformHandler->getUniformCStr(rectHVar) : "half4(0)",
+                rectHVar.isValid() ? args.fUniformHandler->getUniformCStr(rectHVar) : "half4(0)");
         fragBuilder->codeAppendf(
-                ".w;\n    half vcoord = half((abs(translatedPos.y - 0.5 * height) - 0.5 * wh.y) / "
-                "float(%s));\n    half vlookup = sample(%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 = 2.0 * floor(%s / 2.0 + 0.25) - 1.0;\n    half2 wh = "
-                "smallDims - half2(center, center);\n    half hco",
-                args.fUniformHandler->getUniformCStr(profileSizeVar),
-                fragBuilder->getProgramBuilder()->samplerVariable(args.fTexSamplers[0]),
-                fragBuilder->getProgramBuilder()->samplerSwizzle(args.fTexSamplers[0]).c_str(),
-                args.fOutputColor, args.fInputColor, args.fUniformHandler->getUniformCStr(rectVar),
-                args.fUniformHandler->getUniformCStr(rectVar),
-                args.fUniformHandler->getUniformCStr(rectVar),
-                args.fUniformHandler->getUniformCStr(rectVar),
-                args.fUniformHandler->getUniformCStr(rectVar),
-                args.fUniformHandler->getUniformCStr(profileSizeVar),
-                args.fUniformHandler->getUniformCStr(profileSizeVar),
-                args.fUniformHandler->getUniformCStr(profileSizeVar));
+                " x3 = x2 * x;\n    if (x > 0.5) {\n        xCoverage = 0.5625 - ((x3 / 6.0 - (3.0 "
+                "* x2) * 0.25) + 1.125 * x);\n    } else if (x > -0.5) {\n        xCoverage = 0.5 "
+                "- (0.75 * x - x3 / 3.0);\n    } else {\n        xCoverage = 0.4375 + ((-x3 / 6.0 "
+                "- (3.0 * x2) * 0.25) - 1.125 * x);\n    }\n}\nhalf y;\n@if (highp) {\n    float "
+                "tDiff = %s.y - sk_FragCoord.y;\n    float bDiff = sk_FragCoord.y - %s.w;\n    y = "
+                "half(max(tDiff, bDiff) * float(invr));\n} else {\n    half tDiff = "
+                "half(float(%s.y) - sk_FragCoord.y);\n  ",
+                rectFVar.isValid() ? args.fUniformHandler->getUniformCStr(rectFVar) : "float4(0)",
+                rectFVar.isValid() ? args.fUniformHandler->getUniformCStr(rectFVar) : "float4(0)",
+                rectHVar.isValid() ? args.fUniformHandler->getUniformCStr(rectHVar) : "half4(0)");
         fragBuilder->codeAppendf(
-                "ord = (abs(translatedPos.x - 0.5 * width) - 0.5 * wh.x) / %s;\n    half hlookup = "
-                "sample(%s, float2(float(hcoord), 0.5)).%s.w;\n    half vcoord = "
-                "(abs(translatedPos.y - 0.5 * height) - 0.5 * wh.y) / %s;\n    half vlookup = "
-                "sample(%s, float2(float(vcoord), 0.5)).%s.w;\n    %s = (%s * hlookup) * "
-                "vlookup;\n}\n",
-                args.fUniformHandler->getUniformCStr(profileSizeVar),
-                fragBuilder->getProgramBuilder()->samplerVariable(args.fTexSamplers[0]),
-                fragBuilder->getProgramBuilder()->samplerSwizzle(args.fTexSamplers[0]).c_str(),
-                args.fUniformHandler->getUniformCStr(profileSizeVar),
-                fragBuilder->getProgramBuilder()->samplerVariable(args.fTexSamplers[0]),
-                fragBuilder->getProgramBuilder()->samplerSwizzle(args.fTexSamplers[0]).c_str(),
-                args.fOutputColor, args.fInputColor);
+                "  half bDiff = half(sk_FragCoord.y - float(%s.w));\n    y = max(tDiff, bDiff) * "
+                "invr;\n}\nhalf yCoverage;\nif (y > 1.5) {\n    yCoverage = 0.0;\n} else if (y < "
+                "-1.5) {\n    yCoverage = 1.0;\n} else {\n    half y2 = y * y;\n    half y3 = y2 * "
+                "y;\n    if (y > 0.5) {\n        yCoverage = 0.5625 - ((y3 / 6.0 - (3.0 * y2) * "
+                "0.25) + 1.125 * y);\n    } else if (y > -0.5) {\n        yCoverage = 0.5 - (0.75 "
+                "* y - y3 / 3.0);\n    } else {\n        yCoverage = 0.4375 + ((-y3 / 6.0 - (3.0 * "
+                "y2) * 0.25) - 1.125 * y);\n ",
+                rectHVar.isValid() ? args.fUniformHandler->getUniformCStr(rectHVar) : "half4(0)");
+        fragBuilder->codeAppendf("   }\n}\n%s = (%s * xCoverage) * yCoverage;\n", args.fOutputColor,
+                                 args.fInputColor);
     }
 
 private:
     void onSetData(const GrGLSLProgramDataManager& pdman,
                    const GrFragmentProcessor& _proc) override {
         const GrRectBlurEffect& _outer = _proc.cast<GrRectBlurEffect>();
-        { pdman.set4fv(rectVar, 1, reinterpret_cast<const float*>(&(_outer.rect))); }
-        UniformHandle& rect = rectVar;
+        { pdman.set1f(sigmaVar, (_outer.sigma)); }
+        auto rect = _outer.rect;
         (void)rect;
-        auto sigma = _outer.sigma;
+        UniformHandle& rectF = rectFVar;
+        (void)rectF;
+        UniformHandle& rectH = rectHVar;
+        (void)rectH;
+        UniformHandle& sigma = sigmaVar;
         (void)sigma;
-        GrSurfaceProxy& blurProfileProxy = *_outer.textureSampler(0).proxy();
-        GrTexture& blurProfile = *blurProfileProxy.peekTexture();
-        (void)blurProfile;
-        UniformHandle& proxyRectHalf = proxyRectHalfVar;
-        (void)proxyRectHalf;
-        UniformHandle& proxyRectFloat = proxyRectFloatVar;
-        (void)proxyRectFloat;
-        UniformHandle& profileSize = profileSizeVar;
-        (void)profileSize;
 
-        pdman.set1f(profileSize, SkScalarCeilToScalar(6 * sigma));
+        float r[]{rect.fLeft, rect.fTop, rect.fRight, rect.fBottom};
+        pdman.set4fv(highp ? rectF : rectH, 1, r);
     }
-    bool highPrecision = false;
-    UniformHandle proxyRectHalfVar;
-    UniformHandle proxyRectFloatVar;
-    UniformHandle profileSizeVar;
-    UniformHandle rectVar;
+    bool highp = false;
+    UniformHandle rectFVar;
+    UniformHandle rectHVar;
+    UniformHandle sigmaVar;
 };
 GrGLSLFragmentProcessor* GrRectBlurEffect::onCreateGLSLInstance() const {
     return new GrGLSLRectBlurEffect();
 }
 void GrRectBlurEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps,
                                              GrProcessorKeyBuilder* b) const {
-    bool highPrecision = ((((abs(rect.left()) > 16000.0 || abs(rect.top()) > 16000.0) ||
-                            abs(rect.right()) > 16000.0) ||
-                           abs(rect.bottom()) > 16000.0) ||
-                          abs(rect.right() - rect.left()) > 16000.0) ||
-                         abs(rect.bottom() - rect.top()) > 16000.0;
-    b->add32((int32_t)highPrecision);
+    bool highp = ((abs(rect.left()) > 16000.0 || abs(rect.top()) > 16000.0) ||
+                  abs(rect.right()) > 16000.0) ||
+                 abs(rect.bottom()) > 16000.0;
+    b->add32((int32_t)highp);
 }
 bool GrRectBlurEffect::onIsEqual(const GrFragmentProcessor& other) const {
     const GrRectBlurEffect& that = other.cast<GrRectBlurEffect>();
     (void)that;
     if (rect != that.rect) return false;
     if (sigma != that.sigma) return false;
-    if (blurProfile != that.blurProfile) return false;
     return true;
 }
 GrRectBlurEffect::GrRectBlurEffect(const GrRectBlurEffect& src)
         : INHERITED(kGrRectBlurEffect_ClassID, src.optimizationFlags())
         , rect(src.rect)
-        , sigma(src.sigma)
-        , blurProfile(src.blurProfile) {
-    this->setTextureSamplerCnt(1);
-}
+        , sigma(src.sigma) {}
 std::unique_ptr<GrFragmentProcessor> GrRectBlurEffect::clone() const {
     return std::unique_ptr<GrFragmentProcessor>(new GrRectBlurEffect(*this));
 }
-const GrFragmentProcessor::TextureSampler& GrRectBlurEffect::onTextureSampler(int index) const {
-    return IthTextureSampler(index, blurProfile);
-}
 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrRectBlurEffect);
 #if GR_TEST_UTILS
 std::unique_ptr<GrFragmentProcessor> GrRectBlurEffect::TestCreate(GrProcessorTestData* data) {
diff --git a/src/gpu/effects/generated/GrRectBlurEffect.h b/src/gpu/effects/generated/GrRectBlurEffect.h
index 7083222..2d14df4 100644
--- a/src/gpu/effects/generated/GrRectBlurEffect.h
+++ b/src/gpu/effects/generated/GrRectBlurEffect.h
@@ -21,97 +21,48 @@
 #include "src/gpu/GrFragmentProcessor.h"
 class GrRectBlurEffect : public GrFragmentProcessor {
 public:
-    static sk_sp<GrTextureProxy> CreateBlurProfileTexture(GrProxyProvider* proxyProvider,
-                                                          float sigma) {
-        unsigned int profileSize = SkScalarCeilToInt(6 * sigma);
-
-        static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
-        GrUniqueKey key;
-        GrUniqueKey::Builder builder(&key, kDomain, 1, "Rect Blur Mask");
-        builder[0] = profileSize;
-        builder.finish();
-
-        sk_sp<GrTextureProxy> blurProfile(proxyProvider->findOrCreateProxyByUniqueKey(
-                key, GrColorType::kAlpha_8, kTopLeft_GrSurfaceOrigin));
-        if (!blurProfile) {
-            SkImageInfo ii = SkImageInfo::MakeA8(profileSize, 1);
-
-            SkBitmap bitmap;
-            if (!bitmap.tryAllocPixels(ii)) {
-                return nullptr;
-            }
-
-            SkBlurMask::ComputeBlurProfile(bitmap.getAddr8(0, 0), profileSize, sigma);
-            bitmap.setImmutable();
-
-            sk_sp<SkImage> image = SkImage::MakeFromBitmap(bitmap);
-            if (!image) {
-                return nullptr;
-            }
-
-            blurProfile = proxyProvider->createTextureProxy(std::move(image), 1, SkBudgeted::kYes,
-                                                            SkBackingFit::kExact);
-            if (!blurProfile) {
-                return nullptr;
-            }
-
-            SkASSERT(blurProfile->origin() == kTopLeft_GrSurfaceOrigin);
-            proxyProvider->assignUniqueKeyToProxy(key, blurProfile.get());
-        }
-
-        return blurProfile;
-    }
-
     static std::unique_ptr<GrFragmentProcessor> Make(GrProxyProvider* proxyProvider,
                                                      const GrShaderCaps& caps, const SkRect& rect,
                                                      float sigma) {
+        float doubleProfileSize = (12 * sigma);
         if (!caps.floatIs32Bits()) {
-            // We promote the rect uniform from half to float when it has large values for
-            // precision. If we don't have full float then fail.
+            // We promote the math that gets us into the Gaussian space to full float when the rect
+            // coords are large. If we don't have full float then fail. We could probably clip the
+            // rect to an outset device bounds instead.
             if (SkScalarAbs(rect.fLeft) > 16000.f || SkScalarAbs(rect.fTop) > 16000.f ||
-                SkScalarAbs(rect.fRight) > 16000.f || SkScalarAbs(rect.fBottom) > 16000.f ||
-                SkScalarAbs(rect.width()) > 16000.f || SkScalarAbs(rect.height()) > 16000.f) {
+                SkScalarAbs(rect.fRight) > 16000.f || SkScalarAbs(rect.fBottom) > 16000.f) {
                 return nullptr;
             }
         }
-        int doubleProfileSize = SkScalarCeilToInt(12 * sigma);
+        // Sigma is always a half.
+        SkASSERT(sigma > 0);
+        if (sigma > 16000.f) {
+            return nullptr;
+        }
 
-        if (doubleProfileSize >= rect.width() || doubleProfileSize >= rect.height()) {
+        if (doubleProfileSize >= (float)rect.width() || doubleProfileSize >= (float)rect.height()) {
             // if the blur sigma is too large so the gaussian overlaps the whole
             // rect in either direction, fall back to CPU path for now.
             return nullptr;
         }
 
-        sk_sp<GrTextureProxy> blurProfile(CreateBlurProfileTexture(proxyProvider, sigma));
-        if (!blurProfile) {
-            return nullptr;
-        }
-
-        return std::unique_ptr<GrFragmentProcessor>(new GrRectBlurEffect(
-                rect, sigma, std::move(blurProfile),
-                GrSamplerState(GrSamplerState::WrapMode::kClamp, GrSamplerState::Filter::kBilerp)));
+        return std::unique_ptr<GrFragmentProcessor>(new GrRectBlurEffect(rect, sigma));
     }
     GrRectBlurEffect(const GrRectBlurEffect& src);
     std::unique_ptr<GrFragmentProcessor> clone() const override;
     const char* name() const override { return "RectBlurEffect"; }
     SkRect rect;
     float sigma;
-    TextureSampler blurProfile;
 
 private:
-    GrRectBlurEffect(SkRect rect, float sigma, sk_sp<GrTextureProxy> blurProfile,
-                     GrSamplerState samplerParams)
+    GrRectBlurEffect(SkRect rect, float sigma)
             : INHERITED(kGrRectBlurEffect_ClassID,
                         (OptimizationFlags)kCompatibleWithCoverageAsAlpha_OptimizationFlag)
             , rect(rect)
-            , sigma(sigma)
-            , blurProfile(std::move(blurProfile), samplerParams) {
-        this->setTextureSamplerCnt(1);
-    }
+            , sigma(sigma) {}
     GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
     void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override;
     bool onIsEqual(const GrFragmentProcessor&) const override;
-    const TextureSampler& onTextureSampler(int) const override;
     GR_DECLARE_FRAGMENT_PROCESSOR_TEST
     typedef GrFragmentProcessor INHERITED;
 };