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>
This commit is contained in:
Brian Salomon 2020-01-14 16:00:17 -05:00 committed by Skia Commit-Bot
parent 2a57e79921
commit 1817e70938
8 changed files with 142 additions and 117 deletions

View File

@ -883,17 +883,18 @@ sk_sp<GrTextureProxy> SkBlurMaskFilterImpl::filterMaskGPU(GrRecordingContext* co
// 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;
}

View File

@ -71,6 +71,21 @@ static float adjust_sigma(float sigma, int maxTextureSize, int *scaleFactor, int
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 @@ static void convolve_gaussian_1d(GrRenderTargetContext* renderTargetContext,
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 @@ static std::unique_ptr<GrRenderTargetContext> convolve_gaussian_2d(GrRecordingCo
int radiusY,
SkScalar sigmaX,
SkScalar sigmaY,
GrTextureDomain::Mode mode,
SkTileMode mode,
int finalW,
int finalH,
sk_sp<SkColorSpace> finalCS,
@ -119,8 +135,9 @@ static std::unique_ptr<GrRenderTargetContext> convolve_gaussian_2d(GrRecordingCo
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 @@ static std::unique_ptr<GrRenderTargetContext> convolve_gaussian(GrRecordingConte
int radius,
float sigma,
SkIRect* contentRect,
GrTextureDomain::Mode mode,
SkTileMode mode,
int finalW,
int finalH,
sk_sp<SkColorSpace> finalCS,
@ -169,11 +186,14 @@ static std::unique_ptr<GrRenderTargetContext> convolve_gaussian(GrRecordingConte
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 @@ static std::unique_ptr<GrRenderTargetContext> convolve_gaussian(GrRecordingConte
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 @@ static sk_sp<GrTextureProxy> decimate(GrRecordingContext* context,
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 @@ static sk_sp<GrTextureProxy> decimate(GrRecordingContext* context,
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 @@ static sk_sp<GrTextureProxy> decimate(GrRecordingContext* context,
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 @@ std::unique_ptr<GrRenderTargetContext> GaussianBlur(GrRecordingContext* context,
const SkIRect& srcBounds,
float sigmaX,
float sigmaY,
GrTextureDomain::Mode mode,
SkTileMode mode,
SkBackingFit fit) {
SkASSERT(context);
@ -445,21 +463,14 @@ std::unique_ptr<GrRenderTargetContext> GaussianBlur(GrRecordingContext* context,
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 @@ std::unique_ptr<GrRenderTargetContext> GaussianBlur(GrRecordingContext* context,
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 @@ std::unique_ptr<GrRenderTargetContext> GaussianBlur(GrRecordingContext* context,
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;
}

View File

@ -18,32 +18,32 @@ class GrTexture;
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 @@ std::unique_ptr<GrRenderTargetContext> GaussianBlur(GrRecordingContext* context,
const SkIRect& srcBounds,
float sigmaX,
float sigmaY,
GrTextureDomain::Mode mode,
SkTileMode mode,
SkBackingFit fit = SkBackingFit::kApprox);
};

View File

@ -131,23 +131,6 @@ void SkBlurImageFilterImpl::flatten(SkWriteBuffer& buffer) const {
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 @@ sk_sp<SkSpecialImage> SkBlurImageFilterImpl::gpuFilter(
inputBounds,
sigma.x(),
sigma.y(),
to_texture_domain_mode(fTileMode));
fTileMode);
if (!renderTargetContext) {
return nullptr;
}

View File

@ -502,12 +502,13 @@ public:
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;
};

View File

@ -229,15 +229,49 @@ GrGaussianConvolutionFragmentProcessor::GrGaussianConvolutionFragmentProcessor(
, 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));
}

View File

@ -80,10 +80,10 @@ uniform half blurRadius;
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;

View File

@ -80,10 +80,10 @@ public:
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;