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;
};