Make SkGpuBlurUtils take SkTileMode
This is to get ready for moving all of our texture domain
implementation into GrTextureEffect.
This may cause a short term perf regression we might make GrDomainEffect
with unnecessary shader clamping.
Change-Id: I04d9c4c6999539cfd35bda07b943c3b7eb224df8
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/264397
Commit-Queue: Brian Salomon <bsalomon@google.com>
Reviewed-by: Michael Ludwig <michaelludwig@google.com>
diff --git a/src/core/SkBlurMF.cpp b/src/core/SkBlurMF.cpp
index aa00e50..30be330 100644
--- a/src/core/SkBlurMF.cpp
+++ b/src/core/SkBlurMF.cpp
@@ -883,17 +883,18 @@
// If we're doing a normal blur, we can clobber the pathTexture in the
// gaussianBlur. Otherwise, we need to save it for later compositing.
bool isNormalBlur = (kNormal_SkBlurStyle == fBlurStyle);
- auto renderTargetContext = SkGpuBlurUtils::GaussianBlur(context,
- srcProxy,
- srcColorType,
- srcAlphaType,
- SkIPoint::Make(0, 0),
- nullptr,
- clipRect,
- SkIRect::MakeEmpty(),
- xformedSigma,
- xformedSigma,
- GrTextureDomain::kIgnore_Mode);
+ auto renderTargetContext =
+ SkGpuBlurUtils::GaussianBlur(context,
+ srcProxy,
+ srcColorType,
+ srcAlphaType,
+ SkIPoint::Make(0, 0),
+ nullptr,
+ clipRect,
+ SkIRect::MakeSize(srcProxy->dimensions()),
+ xformedSigma,
+ xformedSigma,
+ SkTileMode::kClamp);
if (!renderTargetContext) {
return nullptr;
}
diff --git a/src/core/SkGpuBlurUtils.cpp b/src/core/SkGpuBlurUtils.cpp
index b4d265b..28105c8 100644
--- a/src/core/SkGpuBlurUtils.cpp
+++ b/src/core/SkGpuBlurUtils.cpp
@@ -71,6 +71,21 @@
return sigma;
}
+static GrTextureDomain::Mode to_texture_domain_mode(SkTileMode tileMode) {
+ switch (tileMode) {
+ case SkTileMode::kClamp:
+ return GrTextureDomain::kClamp_Mode;
+ case SkTileMode::kDecal:
+ return GrTextureDomain::kDecal_Mode;
+ case SkTileMode::kMirror:
+ // TODO (michaelludwig) - Support mirror mode, treat as repeat for now
+ case SkTileMode::kRepeat:
+ return GrTextureDomain::kRepeat_Mode;
+ default:
+ SK_ABORT("Unsupported tile mode.");
+ }
+}
+
static void convolve_gaussian_1d(GrRenderTargetContext* renderTargetContext,
const GrClip& clip,
const SkIRect& dstRect,
@@ -80,11 +95,12 @@
Direction direction,
int radius,
float sigma,
- GrTextureDomain::Mode mode,
+ SkTileMode mode,
int bounds[2]) {
GrPaint paint;
+ auto domainMode = to_texture_domain_mode(mode);
std::unique_ptr<GrFragmentProcessor> conv(GrGaussianConvolutionFragmentProcessor::Make(
- std::move(proxy), srcAlphaType, direction, radius, sigma, mode, bounds));
+ std::move(proxy), srcAlphaType, direction, radius, sigma, domainMode, bounds));
paint.addColorFragmentProcessor(std::move(conv));
paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
SkMatrix localMatrix = SkMatrix::MakeTrans(-SkIntToScalar(srcOffset.x()),
@@ -102,7 +118,7 @@
int radiusY,
SkScalar sigmaX,
SkScalar sigmaY,
- GrTextureDomain::Mode mode,
+ SkTileMode mode,
int finalW,
int finalH,
sk_sp<SkColorSpace> finalCS,
@@ -119,8 +135,9 @@
SkISize size = SkISize::Make(2 * radiusX + 1, 2 * radiusY + 1);
SkIPoint kernelOffset = SkIPoint::Make(radiusX, radiusY);
GrPaint paint;
+ auto domainMode = to_texture_domain_mode(mode);
auto conv = GrMatrixConvolutionEffect::MakeGaussian(std::move(srcProxy), srcBounds, size,
- 1.0, 0.0, kernelOffset, mode, true,
+ 1.0, 0.0, kernelOffset, domainMode, true,
sigmaX, sigmaY);
paint.addColorFragmentProcessor(std::move(conv));
paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
@@ -150,7 +167,7 @@
int radius,
float sigma,
SkIRect* contentRect,
- GrTextureDomain::Mode mode,
+ SkTileMode mode,
int finalW,
int finalH,
sk_sp<SkColorSpace> finalCS,
@@ -169,11 +186,14 @@
int bounds[2] = { 0, 0 };
SkIRect dstRect = SkIRect::MakeWH(srcRect.width(), srcRect.height());
SkIPoint netOffset = srcOffset - proxyOffset;
- if (GrTextureDomain::kIgnore_Mode == mode) {
+ if (SkTileMode::kClamp == mode && proxyOffset.isZero() &&
+ contentRect->contains(SkIRect::MakeSize(srcProxy->backingStoreDimensions()))) {
+ bounds[0] = direction == Direction::kX ? contentRect->left() : contentRect->top();
+ bounds[1] = direction == Direction::kX ? contentRect->right() : contentRect->bottom();
*contentRect = dstRect;
convolve_gaussian_1d(dstRenderTargetContext.get(), clip, dstRect, netOffset,
std::move(srcProxy), srcAlphaType, direction, radius, sigma,
- GrTextureDomain::kIgnore_Mode, bounds);
+ SkTileMode::kClamp, bounds);
return dstRenderTargetContext;
}
// These destination rects need to be adjusted by srcOffset, but should *not* be adjusted by
@@ -235,9 +255,11 @@
srcAlphaType, direction, radius, sigma, mode, bounds);
convolve_gaussian_1d(dstRenderTargetContext.get(), clip, rightRect, netOffset, srcProxy,
srcAlphaType, direction, radius, sigma, mode, bounds);
+ bounds[0] = 0;
+ bounds[1] = direction == Direction::kX ? srcProxy->width() : srcProxy->height();
convolve_gaussian_1d(dstRenderTargetContext.get(), clip, midRect, netOffset,
std::move(srcProxy), srcAlphaType, direction, radius, sigma,
- GrTextureDomain::kIgnore_Mode, bounds);
+ SkTileMode::kClamp, bounds);
}
return dstRenderTargetContext;
@@ -253,22 +275,15 @@
const SkIPoint& proxyOffset,
SkIPoint* srcOffset,
SkIRect* contentRect,
- int scaleFactorX, int scaleFactorY,
- int radiusX, int radiusY,
- GrTextureDomain::Mode mode,
- int finalW,
- int finalH,
+ int scaleFactorX,
+ int scaleFactorY,
+ SkTileMode mode,
sk_sp<SkColorSpace> finalCS) {
SkASSERT(SkIsPow2(scaleFactorX) && SkIsPow2(scaleFactorY));
SkASSERT(scaleFactorX > 1 || scaleFactorY > 1);
- SkIRect srcRect;
- if (GrTextureDomain::kIgnore_Mode == mode) {
- srcRect = SkIRect::MakeWH(finalW, finalH);
- } else {
- srcRect = *contentRect;
- srcRect.offset(*srcOffset);
- }
+ SkIRect srcRect = *contentRect;
+ srcRect.offset(*srcOffset);
scale_irect_roundout(&srcRect, 1.0f / scaleFactorX, 1.0f / scaleFactorY);
scale_irect(&srcRect, scaleFactorX, scaleFactorY);
@@ -294,13 +309,16 @@
GrPaint paint;
auto fp = GrTextureEffect::Make(std::move(srcProxy), srcAlphaType, SkMatrix::I(),
GrSamplerState::Filter::kBilerp);
- if (GrTextureDomain::kIgnore_Mode != mode && i == 1) {
+ if (i == 1) {
// GrDomainEffect does not support kRepeat_Mode with GrSamplerState::Filter.
- GrTextureDomain::Mode modeForScaling = (GrTextureDomain::kRepeat_Mode == mode ||
- GrTextureDomain::kMirrorRepeat_Mode == mode)
- ? GrTextureDomain::kDecal_Mode
- : mode;
-
+ GrTextureDomain::Mode domainMode;
+ if (mode == SkTileMode::kClamp) {
+ domainMode = GrTextureDomain::kClamp_Mode;
+ } else {
+ // GrDomainEffect does not support k[Mirror]Repeat with GrSamplerState::Filter.
+ // So we use decal.
+ domainMode = GrTextureDomain::kDecal_Mode;
+ }
SkRect domain = SkRect::Make(*contentRect);
domain.inset((i < scaleFactorX) ? SK_ScalarHalf + SK_ScalarNearlyZero : 0.0f,
(i < scaleFactorY) ? SK_ScalarHalf + SK_ScalarNearlyZero : 0.0f);
@@ -312,7 +330,7 @@
domain.fTop = domain.fBottom = SkScalarAve(domain.fTop, domain.fBottom);
}
domain.offset(proxyOffset.x(), proxyOffset.y());
- fp = GrDomainEffect::Make(std::move(fp), domain, modeForScaling, true);
+ fp = GrDomainEffect::Make(std::move(fp), domain, domainMode, true);
srcRect.offset(-(*srcOffset));
// TODO: consume the srcOffset in both first draws and always set it to zero
// back in GaussianBlur
@@ -403,7 +421,7 @@
const SkIRect& srcBounds,
float sigmaX,
float sigmaY,
- GrTextureDomain::Mode mode,
+ SkTileMode mode,
SkBackingFit fit) {
SkASSERT(context);
@@ -445,21 +463,14 @@
xFit = SkBackingFit::kApprox; // the y-pass will be last
}
- GrTextureDomain::Mode currDomainMode = mode;
if (scaleFactorX > 1 || scaleFactorY > 1) {
- srcProxy =
- decimate(context, std::move(srcProxy), srcColorType, srcAlphaType, localProxyOffset,
- &srcOffset, &localSrcBounds, scaleFactorX, scaleFactorY, radiusX, radiusY,
- currDomainMode, finalW, finalH, colorSpace);
+ srcProxy = decimate(context, std::move(srcProxy), srcColorType, srcAlphaType,
+ localProxyOffset, &srcOffset, &localSrcBounds, scaleFactorX,
+ scaleFactorY, mode, colorSpace);
if (!srcProxy) {
return nullptr;
}
localProxyOffset.set(0, 0);
- if (GrTextureDomain::kIgnore_Mode == currDomainMode) {
- // decimate() always returns an approx texture, possibly with garbage after the image.
- // We can't ignore the domain anymore.
- currDomainMode = GrTextureDomain::kClamp_Mode;
- }
}
std::unique_ptr<GrRenderTargetContext> dstRenderTargetContext;
@@ -467,10 +478,10 @@
auto srcRect = SkIRect::MakeWH(finalW, finalH);
scale_irect_roundout(&srcRect, 1.0f / scaleFactorX, 1.0f / scaleFactorY);
if (sigmaX > 0.0f) {
- dstRenderTargetContext = convolve_gaussian(
- context, std::move(srcProxy), srcColorType, srcAlphaType, localProxyOffset, srcRect,
- srcOffset, Direction::kX, radiusX, sigmaX, &localSrcBounds, currDomainMode, finalW,
- finalH, colorSpace, xFit);
+ dstRenderTargetContext =
+ convolve_gaussian(context, std::move(srcProxy), srcColorType, srcAlphaType,
+ localProxyOffset, srcRect, srcOffset, Direction::kX, radiusX,
+ sigmaX, &localSrcBounds, mode, finalW, finalH, colorSpace, xFit);
if (!dstRenderTargetContext) {
return nullptr;
}
@@ -483,18 +494,13 @@
srcRect.offsetTo(0, 0);
srcOffset.set(0, 0);
localProxyOffset.set(0, 0);
- if (SkBackingFit::kApprox == xFit && GrTextureDomain::kIgnore_Mode == currDomainMode) {
- // srcProxy is now an approx texture, possibly with garbage after the image. We can't
- // ignore the domain anymore.
- currDomainMode = GrTextureDomain::kClamp_Mode;
- }
}
if (sigmaY > 0.0f) {
- dstRenderTargetContext = convolve_gaussian(
- context, std::move(srcProxy), srcColorType, srcAlphaType, localProxyOffset, srcRect,
- srcOffset, Direction::kY, radiusY, sigmaY, &localSrcBounds, currDomainMode, finalW,
- finalH, colorSpace, yFit);
+ dstRenderTargetContext =
+ convolve_gaussian(context, std::move(srcProxy), srcColorType, srcAlphaType,
+ localProxyOffset, srcRect, srcOffset, Direction::kY, radiusY,
+ sigmaY, &localSrcBounds, mode, finalW, finalH, colorSpace, yFit);
if (!dstRenderTargetContext) {
return nullptr;
}
diff --git a/src/core/SkGpuBlurUtils.h b/src/core/SkGpuBlurUtils.h
index 9143518..a1b3308 100644
--- a/src/core/SkGpuBlurUtils.h
+++ b/src/core/SkGpuBlurUtils.h
@@ -18,32 +18,32 @@
struct SkRect;
namespace SkGpuBlurUtils {
- /**
- * Applies a 2D Gaussian blur to a given texture. The blurred result is returned
- * as a renderTargetContext in case the caller wishes to draw into the result.
- *
- * The 'proxyOffset' is kept separate form 'srcBounds' because they exist in different
- * coordinate spaces. 'srcBounds' exists in the content space of the special image, and
- * 'proxyOffset' maps from the content space to the proxy's space.
- *
- * Note: one of sigmaX and sigmaY should be non-zero!
- * @param context The GPU context
- * @param srcProxy The source to be blurred.
- * @param srcColorType The colorType of srcProxy
- * @param srcAlphaType The alphaType of srcProxy
- * @param proxyOffset The offset from the top-left corner to valid texels in 'srcProxy',
- which should come from the subset of the owning SkSpecialImage.
- * @param colorSpace Color space of the source (used for the renderTargetContext result,
- * too).
- * @param dstBounds The destination bounds, relative to the source texture.
- * @param srcBounds The source bounds, relative to the source texture's offset. No pixels
- * will be sampled outside of this rectangle.
- * @param sigmaX The blur's standard deviation in X.
- * @param sigmaY The blur's standard deviation in Y.
- * @param mode The mode to handle samples outside bounds.
- * @param fit backing fit for the returned render target context
- * @return The renderTargetContext containing the blurred result.
- */
+/**
+ * Applies a 2D Gaussian blur to a given texture. The blurred result is returned
+ * as a renderTargetContext in case the caller wishes to draw into the result.
+ *
+ * The 'proxyOffset' is kept separate form 'srcBounds' because they exist in different
+ * coordinate spaces. 'srcBounds' exists in the content space of the special image, and
+ * 'proxyOffset' maps from the content space to the proxy's space.
+ *
+ * Note: one of sigmaX and sigmaY should be non-zero!
+ * @param context The GPU context
+ * @param srcProxy The source to be blurred.
+ * @param srcColorType The colorType of srcProxy
+ * @param srcAlphaType The alphaType of srcProxy
+ * @param proxyOffset The offset from the top-left corner to valid texels in 'srcProxy',
+ which should come from the subset of the owning SkSpecialImage.
+ * @param colorSpace Color space of the source (used for the renderTargetContext result,
+ * too).
+ * @param dstBounds The destination bounds, relative to the source texture.
+ * @param srcBounds The source bounds, relative to the source texture's offset. No pixels
+ * will be sampled outside of this rectangle.
+ * @param sigmaX The blur's standard deviation in X.
+ * @param sigmaY The blur's standard deviation in Y.
+ * @param tileMode The mode to handle samples outside bounds.
+ * @param fit backing fit for the returned render target context
+ * @return The renderTargetContext containing the blurred result.
+ */
std::unique_ptr<GrRenderTargetContext> GaussianBlur(GrRecordingContext* context,
sk_sp<GrTextureProxy> srcProxy,
GrColorType srcColorType,
@@ -54,7 +54,7 @@
const SkIRect& srcBounds,
float sigmaX,
float sigmaY,
- GrTextureDomain::Mode mode,
+ SkTileMode mode,
SkBackingFit fit = SkBackingFit::kApprox);
};
diff --git a/src/effects/imagefilters/SkBlurImageFilter.cpp b/src/effects/imagefilters/SkBlurImageFilter.cpp
index 2dde5e2..2bfd8e8 100644
--- a/src/effects/imagefilters/SkBlurImageFilter.cpp
+++ b/src/effects/imagefilters/SkBlurImageFilter.cpp
@@ -131,23 +131,6 @@
buffer.writeInt(static_cast<int>(fTileMode));
}
-#if SK_SUPPORT_GPU
-static GrTextureDomain::Mode to_texture_domain_mode(SkTileMode tileMode) {
- switch (tileMode) {
- case SkTileMode::kClamp:
- return GrTextureDomain::kClamp_Mode;
- case SkTileMode::kDecal:
- return GrTextureDomain::kDecal_Mode;
- case SkTileMode::kMirror:
- // TODO (michaelludwig) - Support mirror mode, treat as repeat for now
- case SkTileMode::kRepeat:
- return GrTextureDomain::kRepeat_Mode;
- default:
- SK_ABORT("Unsupported tile mode.");
- }
-}
-#endif
-
// This is defined by the SVG spec:
// https://drafts.fxtf.org/filter-effects/#feGaussianBlurElement
static int calculate_window(double sigma) {
@@ -677,7 +660,7 @@
inputBounds,
sigma.x(),
sigma.y(),
- to_texture_domain_mode(fTileMode));
+ fTileMode);
if (!renderTargetContext) {
return nullptr;
}
diff --git a/src/gpu/GrFragmentProcessor.h b/src/gpu/GrFragmentProcessor.h
index 4150dca..b7206de 100644
--- a/src/gpu/GrFragmentProcessor.h
+++ b/src/gpu/GrFragmentProcessor.h
@@ -502,12 +502,13 @@
bool isInitialized() const { return SkToBool(this->proxy()); }
+ GrSurfaceProxy* proxy() const { return fView.proxy(); }
+
#if GR_TEST_UTILS
void set(GrSurfaceProxyView, GrSamplerState);
#endif
-private:
- GrSurfaceProxy* proxy() const { return fView.proxy(); }
+private:
GrSurfaceProxyView fView;
GrSamplerState fSamplerState;
};
diff --git a/src/gpu/effects/GrGaussianConvolutionFragmentProcessor.cpp b/src/gpu/effects/GrGaussianConvolutionFragmentProcessor.cpp
index 9a66a07..35dfcd0 100644
--- a/src/gpu/effects/GrGaussianConvolutionFragmentProcessor.cpp
+++ b/src/gpu/effects/GrGaussianConvolutionFragmentProcessor.cpp
@@ -229,15 +229,49 @@
, fRadius(radius)
, fDirection(direction)
, fMode(mode) {
- // Make sure the sampler's ctor uses the clamp wrap mode
- SkASSERT(fTextureSampler.samplerState().wrapModeX() == GrSamplerState::WrapMode::kClamp &&
- fTextureSampler.samplerState().wrapModeY() == GrSamplerState::WrapMode::kClamp);
this->addCoordTransform(&fCoordTransform);
this->setTextureSamplerCnt(1);
SkASSERT(radius <= kMaxKernelRadius);
fill_in_1D_gaussian_kernel(fKernel, this->width(), gaussianSigma, this->radius());
-
+ // SkGpuBlurUtils is not as aggressive as it once was about avoiding domains. So we check
+ // here if we can omit the domain. TODO: remove this when this effect uses a child to
+ // sample the texture.
+ auto samplerProxy = fTextureSampler.proxy();
+ if (!samplerProxy->isFullyLazy()) {
+ int wh = (fDirection == Direction::kX) ? samplerProxy->backingStoreDimensions().width()
+ : samplerProxy->backingStoreDimensions().height();
+ if (bounds[0] == 0 && bounds[1] == wh) {
+ bool useSampler = false;
+ GrSamplerState::WrapMode samplerMode = GrSamplerState::WrapMode::kClamp;
+ switch (fMode) {
+ case GrTextureDomain::kClamp_Mode:
+ case GrTextureDomain::kIgnore_Mode:
+ useSampler = true;
+ break;
+ case GrTextureDomain::kRepeat_Mode:
+ useSampler = true;
+ samplerMode = GrSamplerState::WrapMode::kRepeat;
+ break;
+ case GrTextureDomain::kMirrorRepeat_Mode:
+ useSampler = true;
+ samplerMode = GrSamplerState::WrapMode::kMirrorRepeat;
+ break;
+ case GrTextureDomain::kDecal_Mode:
+ // Not sure if we support this in HW without having GrCaps here.
+ // Just wait until we replace this with GrTextureEffect.
+ break;
+ }
+ if (useSampler) {
+ fMode = GrTextureDomain::kIgnore_Mode;
+ if (fDirection == Direction::kX) {
+ fTextureSampler.samplerState().setWrapModeX(samplerMode);
+ } else {
+ fTextureSampler.samplerState().setWrapModeY(samplerMode);
+ }
+ }
+ }
+ }
memcpy(fBounds, bounds, sizeof(fBounds));
}
diff --git a/src/gpu/effects/GrRRectBlurEffect.fp b/src/gpu/effects/GrRRectBlurEffect.fp
index 290295b..9234ac6 100644
--- a/src/gpu/effects/GrRRectBlurEffect.fp
+++ b/src/gpu/effects/GrRRectBlurEffect.fp
@@ -80,10 +80,10 @@
SkIPoint::Make(0, 0),
nullptr,
SkIRect::MakeSize(dimensions),
- SkIRect::MakeEmpty(),
+ SkIRect::MakeSize(dimensions),
xformedSigma,
xformedSigma,
- GrTextureDomain::kIgnore_Mode,
+ SkTileMode::kClamp,
SkBackingFit::kExact);
if (!rtc2) {
return nullptr;
diff --git a/src/gpu/effects/generated/GrRRectBlurEffect.h b/src/gpu/effects/generated/GrRRectBlurEffect.h
index 94b6ca1..1985277 100644
--- a/src/gpu/effects/generated/GrRRectBlurEffect.h
+++ b/src/gpu/effects/generated/GrRRectBlurEffect.h
@@ -80,10 +80,10 @@
SkIPoint::Make(0, 0),
nullptr,
SkIRect::MakeSize(dimensions),
- SkIRect::MakeEmpty(),
+ SkIRect::MakeSize(dimensions),
xformedSigma,
xformedSigma,
- GrTextureDomain::kIgnore_Mode,
+ SkTileMode::kClamp,
SkBackingFit::kExact);
if (!rtc2) {
return nullptr;