Support decal mode in GrTextureProducer
Bug: skia:
Change-Id: I9dab07c527f20d58896b17d6c3ea4e632835db3b
Reviewed-on: https://skia-review.googlesource.com/c/193366
Commit-Queue: Michael Ludwig <michaelludwig@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
diff --git a/src/gpu/GrBitmapTextureMaker.cpp b/src/gpu/GrBitmapTextureMaker.cpp
index adc6347..ee93521 100644
--- a/src/gpu/GrBitmapTextureMaker.cpp
+++ b/src/gpu/GrBitmapTextureMaker.cpp
@@ -19,8 +19,9 @@
static bool bmp_is_alpha_only(const SkBitmap& bm) { return kAlpha_8_SkColorType == bm.colorType(); }
-GrBitmapTextureMaker::GrBitmapTextureMaker(GrRecordingContext* context, const SkBitmap& bitmap)
- : INHERITED(context, bitmap.width(), bitmap.height(), bmp_is_alpha_only(bitmap))
+GrBitmapTextureMaker::GrBitmapTextureMaker(GrRecordingContext* context, const SkBitmap& bitmap,
+ bool useDecal)
+ : INHERITED(context, bitmap.width(), bitmap.height(), bmp_is_alpha_only(bitmap), useDecal)
, fBitmap(bitmap) {
if (!bitmap.isVolatile()) {
SkIPoint origin = bitmap.pixelRefOrigin();
diff --git a/src/gpu/GrBitmapTextureMaker.h b/src/gpu/GrBitmapTextureMaker.h
index 886014b..da9869c 100644
--- a/src/gpu/GrBitmapTextureMaker.h
+++ b/src/gpu/GrBitmapTextureMaker.h
@@ -16,7 +16,8 @@
subset of the pixelref specified by the bitmap. */
class GrBitmapTextureMaker : public GrTextureMaker {
public:
- GrBitmapTextureMaker(GrRecordingContext* context, const SkBitmap& bitmap);
+ GrBitmapTextureMaker(GrRecordingContext* context, const SkBitmap& bitmap,
+ bool useDecal = false);
protected:
sk_sp<GrTextureProxy> refOriginalTextureProxy(bool willBeMipped,
diff --git a/src/gpu/GrImageTextureMaker.cpp b/src/gpu/GrImageTextureMaker.cpp
index 52b06af..b412cf0 100644
--- a/src/gpu/GrImageTextureMaker.cpp
+++ b/src/gpu/GrImageTextureMaker.cpp
@@ -13,8 +13,8 @@
#include "effects/GrYUVtoRGBEffect.h"
GrImageTextureMaker::GrImageTextureMaker(GrRecordingContext* context, const SkImage* client,
- SkImage::CachingHint chint)
- : INHERITED(context, client->width(), client->height(), client->isAlphaOnly())
+ SkImage::CachingHint chint, bool useDecal)
+ : INHERITED(context, client->width(), client->height(), client->isAlphaOnly(), useDecal)
, fImage(static_cast<const SkImage_Lazy*>(client))
, fCachingHint(chint) {
SkASSERT(client->isLazyGenerated());
@@ -45,8 +45,9 @@
/////////////////////////////////////////////////////////////////////////////////////////////////
-GrYUVAImageTextureMaker::GrYUVAImageTextureMaker(GrContext* context, const SkImage* client )
- : INHERITED(context, client->width(), client->height(), client->isAlphaOnly())
+GrYUVAImageTextureMaker::GrYUVAImageTextureMaker(GrContext* context, const SkImage* client,
+ bool useDecal)
+ : INHERITED(context, client->width(), client->height(), client->isAlphaOnly(), useDecal)
, fImage(static_cast<const SkImage_GpuYUVA*>(client)) {
SkASSERT(as_IB(client)->isYUVA());
GrMakeKeyFromImageID(&fOriginalKey, client->uniqueID(),
@@ -93,9 +94,11 @@
// Check simple cases to see if we need to fall back to flattening the image
// TODO: See if we can relax this -- for example, if filterConstraint
// is kYes_FilterConstraint we still may not need a TextureDomain
- // in some cases.
+ // in some cases. Or allow YUVtoRGBEffect to take a wrap mode to
+ // handle ClampToBorder when a decal is needed.
if (!textureMatrix.isIdentity() || kNo_FilterConstraint != filterConstraint ||
- !coordsLimitedToConstraintRect || !filterOrNullForBicubic) {
+ !coordsLimitedToConstraintRect || !filterOrNullForBicubic ||
+ this->domainNeedsDecal()) {
return this->INHERITED::createFragmentProcessor(textureMatrix, constraintRect,
filterConstraint,
coordsLimitedToConstraintRect,
diff --git a/src/gpu/GrImageTextureMaker.h b/src/gpu/GrImageTextureMaker.h
index 76f539d..ba7772b 100644
--- a/src/gpu/GrImageTextureMaker.h
+++ b/src/gpu/GrImageTextureMaker.h
@@ -19,7 +19,7 @@
class GrImageTextureMaker : public GrTextureMaker {
public:
GrImageTextureMaker(GrRecordingContext* context, const SkImage* client,
- SkImage::CachingHint chint);
+ SkImage::CachingHint chint, bool useDecal = false);
protected:
// TODO: consider overriding this, for the case where the underlying generator might be
@@ -45,7 +45,7 @@
/** This class manages the conversion of generator-backed YUVA images to GrTextures. */
class GrYUVAImageTextureMaker : public GrTextureMaker {
public:
- GrYUVAImageTextureMaker(GrContext* context, const SkImage* client);
+ GrYUVAImageTextureMaker(GrContext* context, const SkImage* client, bool useDecal = false);
protected:
// TODO: consider overriding this, for the case where the underlying generator might be
diff --git a/src/gpu/GrTextureAdjuster.cpp b/src/gpu/GrTextureAdjuster.cpp
index 751d646..a840c52 100644
--- a/src/gpu/GrTextureAdjuster.cpp
+++ b/src/gpu/GrTextureAdjuster.cpp
@@ -16,9 +16,10 @@
GrTextureAdjuster::GrTextureAdjuster(GrRecordingContext* context, sk_sp<GrTextureProxy> original,
SkAlphaType alphaType,
uint32_t uniqueID,
- SkColorSpace* cs)
+ SkColorSpace* cs,
+ bool useDecal)
: INHERITED(context, original->width(), original->height(),
- GrPixelConfigIsAlphaOnly(original->config()))
+ GrPixelConfigIsAlphaOnly(original->config()), useDecal)
, fOriginal(std::move(original))
, fAlphaType(alphaType)
, fColorSpace(cs)
@@ -119,14 +120,9 @@
const GrSamplerState::Filter* filterOrNullForBicubic) {
SkMatrix textureMatrix = origTextureMatrix;
- SkRect domain;
- GrSamplerState samplerState;
- if (filterOrNullForBicubic) {
- samplerState.setFilterMode(*filterOrNullForBicubic);
- }
SkScalar scaleAdjust[2] = { 1.0f, 1.0f };
sk_sp<GrTextureProxy> proxy(
- this->refTextureProxyForParams(samplerState, scaleAdjust));
+ this->refTextureProxyForParams(filterOrNullForBicubic, scaleAdjust));
if (!proxy) {
return nullptr;
}
@@ -136,6 +132,7 @@
textureMatrix.postScale(scaleAdjust[0], scaleAdjust[1]);
}
+ SkRect domain;
DomainMode domainMode =
DetermineDomainMode(constraintRect, filterConstraint, coordsLimitedToConstraintRect,
proxy.get(), filterOrNullForBicubic, &domain);
@@ -155,6 +152,6 @@
}
SkASSERT(kNoDomain_DomainMode == domainMode ||
(domain.fLeft <= domain.fRight && domain.fTop <= domain.fBottom));
- return CreateFragmentProcessorForDomainAndFilter(std::move(proxy), textureMatrix, domainMode,
- domain, filterOrNullForBicubic);
+ return this->createFragmentProcessorForDomainAndFilter(
+ std::move(proxy), textureMatrix, domainMode, domain, filterOrNullForBicubic);
}
diff --git a/src/gpu/GrTextureAdjuster.h b/src/gpu/GrTextureAdjuster.h
index 89bf4a2..58195e9 100644
--- a/src/gpu/GrTextureAdjuster.h
+++ b/src/gpu/GrTextureAdjuster.h
@@ -31,8 +31,8 @@
// We do not ref the texture nor the colorspace, so the caller must keep them in scope while
// this Adjuster is alive.
- GrTextureAdjuster(GrRecordingContext*, sk_sp<GrTextureProxy>, SkAlphaType, uint32_t uniqueID,
- SkColorSpace*);
+ GrTextureAdjuster(GrRecordingContext*, sk_sp<GrTextureProxy>, SkAlphaType,
+ uint32_t uniqueID, SkColorSpace*, bool useDecal = false);
protected:
SkAlphaType alphaType() const override { return fAlphaType; }
diff --git a/src/gpu/GrTextureMaker.cpp b/src/gpu/GrTextureMaker.cpp
index 2bdc975..fbaa300 100644
--- a/src/gpu/GrTextureMaker.cpp
+++ b/src/gpu/GrTextureMaker.cpp
@@ -112,7 +112,7 @@
const GrSamplerState::Filter* fmForDetermineDomain = filterOrNullForBicubic;
if (filterOrNullForBicubic && GrSamplerState::Filter::kMipMap == *filterOrNullForBicubic &&
kYes_FilterConstraint == filterConstraint) {
- // TODo: Here we should force a copy restricted to the constraintRect since MIP maps will
+ // TODO: Here we should force a copy restricted to the constraintRect since MIP maps will
// read outside the constraint rect. However, as in the adjuster case, we aren't currently
// doing that.
// We instead we compute the domain as though were bilerping which is only correct if we
@@ -121,25 +121,20 @@
fmForDetermineDomain = &kBilerp;
}
- GrSamplerState samplerState;
- if (filterOrNullForBicubic) {
- samplerState = GrSamplerState(GrSamplerState::WrapMode::kClamp, *filterOrNullForBicubic);
- } else {
- // Bicubic doesn't use filtering for it's texture accesses.
- samplerState = GrSamplerState::ClampNearest();
- }
SkScalar scaleAdjust[2] = { 1.0f, 1.0f };
- sk_sp<GrTextureProxy> proxy(this->refTextureProxyForParams(samplerState, scaleAdjust));
+ sk_sp<GrTextureProxy> proxy(this->refTextureProxyForParams(filterOrNullForBicubic,
+ scaleAdjust));
if (!proxy) {
return nullptr;
}
SkMatrix adjustedMatrix = textureMatrix;
adjustedMatrix.postScale(scaleAdjust[0], scaleAdjust[1]);
+
SkRect domain;
DomainMode domainMode =
DetermineDomainMode(constraintRect, filterConstraint, coordsLimitedToConstraintRect,
proxy.get(), fmForDetermineDomain, &domain);
SkASSERT(kTightCopy_DomainMode != domainMode);
- return CreateFragmentProcessorForDomainAndFilter(std::move(proxy), adjustedMatrix, domainMode,
- domain, filterOrNullForBicubic);
+ return this->createFragmentProcessorForDomainAndFilter(
+ std::move(proxy), adjustedMatrix, domainMode, domain, filterOrNullForBicubic);
}
diff --git a/src/gpu/GrTextureMaker.h b/src/gpu/GrTextureMaker.h
index 725a532..543bbda 100644
--- a/src/gpu/GrTextureMaker.h
+++ b/src/gpu/GrTextureMaker.h
@@ -26,8 +26,9 @@
const GrSamplerState::Filter* filterOrNullForBicubic) override;
protected:
- GrTextureMaker(GrRecordingContext* context, int width, int height, bool isAlphaOnly)
- : INHERITED(context, width, height, isAlphaOnly) {}
+ GrTextureMaker(GrRecordingContext* context, int width, int height, bool isAlphaOnly,
+ bool domainNeedsLocal)
+ : INHERITED(context, width, height, isAlphaOnly, domainNeedsLocal) {}
/**
* Return the maker's "original" texture. It is the responsibility of the maker to handle any
diff --git a/src/gpu/GrTextureProducer.cpp b/src/gpu/GrTextureProducer.cpp
index 9fe9079..a5affe9 100644
--- a/src/gpu/GrTextureProducer.cpp
+++ b/src/gpu/GrTextureProducer.cpp
@@ -198,34 +198,64 @@
return kDomain_DomainMode;
}
-std::unique_ptr<GrFragmentProcessor> GrTextureProducer::CreateFragmentProcessorForDomainAndFilter(
+std::unique_ptr<GrFragmentProcessor> GrTextureProducer::createFragmentProcessorForDomainAndFilter(
sk_sp<GrTextureProxy> proxy,
const SkMatrix& textureMatrix,
DomainMode domainMode,
const SkRect& domain,
const GrSamplerState::Filter* filterOrNullForBicubic) {
SkASSERT(kTightCopy_DomainMode != domainMode);
+ bool clampToBorderSupport = fContext->priv().caps()->clampToBorderSupport();
if (filterOrNullForBicubic) {
- if (kDomain_DomainMode == domainMode) {
+ if (kDomain_DomainMode == domainMode || (fDomainNeedsDecal && !clampToBorderSupport)) {
+ GrTextureDomain::Mode wrapMode = fDomainNeedsDecal ? GrTextureDomain::kDecal_Mode
+ : GrTextureDomain::kClamp_Mode;
return GrTextureDomainEffect::Make(std::move(proxy), textureMatrix, domain,
- GrTextureDomain::kClamp_Mode,
- *filterOrNullForBicubic);
+ wrapMode, *filterOrNullForBicubic);
} else {
- GrSamplerState samplerState(GrSamplerState::WrapMode::kClamp, *filterOrNullForBicubic);
+ GrSamplerState::WrapMode wrapMode =
+ fDomainNeedsDecal ? GrSamplerState::WrapMode::kClampToBorder
+ : GrSamplerState::WrapMode::kClamp;
+ GrSamplerState samplerState(wrapMode, *filterOrNullForBicubic);
return GrSimpleTextureEffect::Make(std::move(proxy), textureMatrix, samplerState);
}
} else {
- if (kDomain_DomainMode == domainMode) {
- return GrBicubicEffect::Make(std::move(proxy), textureMatrix, domain);
+ static const GrSamplerState::WrapMode kClampClamp[] = {
+ GrSamplerState::WrapMode::kClamp, GrSamplerState::WrapMode::kClamp};
+ static const GrSamplerState::WrapMode kDecalDecal[] = {
+ GrSamplerState::WrapMode::kClampToBorder, GrSamplerState::WrapMode::kClampToBorder};
+
+ if (kDomain_DomainMode == domainMode || (fDomainNeedsDecal && !clampToBorderSupport)) {
+ GrTextureDomain::Mode wrapMode = fDomainNeedsDecal ? GrTextureDomain::kDecal_Mode
+ : GrTextureDomain::kClamp_Mode;
+ return GrBicubicEffect::Make(std::move(proxy), textureMatrix, kClampClamp,
+ wrapMode, wrapMode,
+ kDomain_DomainMode == domainMode ? &domain : nullptr);
} else {
- static const GrSamplerState::WrapMode kClampClamp[] = {
- GrSamplerState::WrapMode::kClamp, GrSamplerState::WrapMode::kClamp};
- return GrBicubicEffect::Make(std::move(proxy), textureMatrix, kClampClamp);
+ return GrBicubicEffect::Make(std::move(proxy), textureMatrix,
+ fDomainNeedsDecal ? kDecalDecal : kClampClamp);
}
}
}
sk_sp<GrTextureProxy> GrTextureProducer::refTextureProxyForParams(
+ const GrSamplerState::Filter* filterOrNullForBicubic,
+ SkScalar scaleAdjust[2]) {
+ GrSamplerState sampler; // Default is nearest + clamp
+ if (filterOrNullForBicubic) {
+ sampler.setFilterMode(*filterOrNullForBicubic);
+ }
+ if (fDomainNeedsDecal) {
+ // Assuming hardware support, switch to clamp-to-border instead of clamp
+ if (fContext->priv().caps()->clampToBorderSupport()) {
+ sampler.setWrapModeX(GrSamplerState::WrapMode::kClampToBorder);
+ sampler.setWrapModeY(GrSamplerState::WrapMode::kClampToBorder);
+ }
+ }
+ return this->refTextureProxyForParams(sampler, scaleAdjust);
+}
+
+sk_sp<GrTextureProxy> GrTextureProducer::refTextureProxyForParams(
const GrSamplerState& sampler,
SkScalar scaleAdjust[2]) {
// Check that the caller pre-initialized scaleAdjust
diff --git a/src/gpu/GrTextureProducer.h b/src/gpu/GrTextureProducer.h
index eb90336..b1d94df 100644
--- a/src/gpu/GrTextureProducer.h
+++ b/src/gpu/GrTextureProducer.h
@@ -85,11 +85,8 @@
sk_sp<GrTextureProxy> refTextureProxyForParams(const GrSamplerState&,
SkScalar scaleAdjust[2]);
- sk_sp<GrTextureProxy> refTextureProxyForParams(GrSamplerState::Filter filter,
- SkScalar scaleAdjust[2]) {
- return this->refTextureProxyForParams(
- GrSamplerState(GrSamplerState::WrapMode::kClamp, filter), scaleAdjust);
- }
+ sk_sp<GrTextureProxy> refTextureProxyForParams(
+ const GrSamplerState::Filter* filterOrNullForBicubic, SkScalar scaleAdjust[2]);
/**
* Returns a texture. If willNeedMips is true then the returned texture is guaranteed to have
@@ -107,17 +104,20 @@
int width() const { return fWidth; }
int height() const { return fHeight; }
bool isAlphaOnly() const { return fIsAlphaOnly; }
+ bool domainNeedsDecal() const { return fDomainNeedsDecal; }
virtual SkAlphaType alphaType() const = 0;
virtual SkColorSpace* colorSpace() const = 0;
protected:
friend class GrTextureProducer_TestAccess;
- GrTextureProducer(GrRecordingContext* context, int width, int height, bool isAlphaOnly)
+ GrTextureProducer(GrRecordingContext* context, int width, int height, bool isAlphaOnly,
+ bool domainNeedsDecal)
: fContext(context)
, fWidth(width)
, fHeight(height)
- , fIsAlphaOnly(isAlphaOnly) {}
+ , fIsAlphaOnly(isAlphaOnly)
+ , fDomainNeedsDecal(domainNeedsDecal) {}
/** Helper for creating a key for a copy from an original key. */
static void MakeCopyKeyFromOrigKey(const GrUniqueKey& origKey,
@@ -168,7 +168,7 @@
const GrSamplerState::Filter* filterModeOrNullForBicubic,
SkRect* domainRect);
- static std::unique_ptr<GrFragmentProcessor> CreateFragmentProcessorForDomainAndFilter(
+ std::unique_ptr<GrFragmentProcessor> createFragmentProcessorForDomainAndFilter(
sk_sp<GrTextureProxy> proxy,
const SkMatrix& textureMatrix,
DomainMode,
@@ -186,6 +186,9 @@
const int fWidth;
const int fHeight;
const bool fIsAlphaOnly;
+ // If true, any domain effect uses kDecal instead of kClamp, and sampler filter uses
+ // kClampToBorder instead of kClamp.
+ const bool fDomainNeedsDecal;
typedef SkNoncopyable INHERITED;
};
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index cf1f665..0b790ca 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -1374,7 +1374,7 @@
auto dstColorSpace = fRenderTargetContext->colorSpaceInfo().colorSpace();
const GrSamplerState::Filter filter = compute_lattice_filter_mode(*paint);
- auto proxy = producer->refTextureProxyForParams(filter, nullptr);
+ auto proxy = producer->refTextureProxyForParams(&filter, nullptr);
if (!proxy) {
return;
}
diff --git a/src/gpu/effects/GrBicubicEffect.cpp b/src/gpu/effects/GrBicubicEffect.cpp
index b3bd5e2..8de347c 100644
--- a/src/gpu/effects/GrBicubicEffect.cpp
+++ b/src/gpu/effects/GrBicubicEffect.cpp
@@ -118,37 +118,20 @@
}
GrBicubicEffect::GrBicubicEffect(sk_sp<GrTextureProxy> proxy,
- const SkMatrix& matrix,
+ const SkMatrix& matrix, const SkRect& domain,
const GrSamplerState::WrapMode wrapModes[2],
GrTextureDomain::Mode modeX, GrTextureDomain::Mode modeY)
: INHERITED{kGrBicubicEffect_ClassID,
ModulateForSamplerOptFlags(proxy->config(),
GrTextureDomain::IsDecalSampled(wrapModes, modeX,modeY))}
, fCoordTransform(matrix, proxy.get())
- , fDomain(proxy.get(),
- GrTextureDomain::MakeTexelDomain(
- SkIRect::MakeWH(proxy->width(), proxy->height()), modeX, modeY),
- modeX, modeY)
+ , fDomain(proxy.get(), domain, modeX, modeY)
, fTextureSampler(std::move(proxy),
GrSamplerState(wrapModes, GrSamplerState::Filter::kNearest)) {
this->addCoordTransform(&fCoordTransform);
this->setTextureSamplerCnt(1);
}
-GrBicubicEffect::GrBicubicEffect(sk_sp<GrTextureProxy> proxy,
- const SkMatrix& matrix,
- const SkRect& domain)
- : INHERITED(kGrBicubicEffect_ClassID, ModulateForClampedSamplerOptFlags(proxy->config()))
- , fCoordTransform(matrix, proxy.get())
- , fDomain(proxy.get(), domain, GrTextureDomain::kClamp_Mode, GrTextureDomain::kClamp_Mode)
- , fTextureSampler(std::move(proxy)) {
- // 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);
-}
-
GrBicubicEffect::GrBicubicEffect(const GrBicubicEffect& that)
: INHERITED(kGrBicubicEffect_ClassID, that.optimizationFlags())
, fCoordTransform(that.fCoordTransform)
diff --git a/src/gpu/effects/GrBicubicEffect.h b/src/gpu/effects/GrBicubicEffect.h
index 68a63e1..be2e44c 100644
--- a/src/gpu/effects/GrBicubicEffect.h
+++ b/src/gpu/effects/GrBicubicEffect.h
@@ -49,9 +49,12 @@
const SkMatrix& matrix,
const GrSamplerState::WrapMode wrapModes[2],
GrTextureDomain::Mode modeX,
- GrTextureDomain::Mode modeY) {
- return std::unique_ptr<GrFragmentProcessor>(new GrBicubicEffect(std::move(proxy), matrix,
- wrapModes, modeX, modeY));
+ GrTextureDomain::Mode modeY,
+ const SkRect* domain = nullptr) {
+ SkRect resolvedDomain = domain ? *domain : GrTextureDomain::MakeTexelDomain(
+ SkIRect::MakeWH(proxy->width(), proxy->height()), modeX, modeY);
+ return std::unique_ptr<GrFragmentProcessor>(new GrBicubicEffect(
+ std::move(proxy), matrix, resolvedDomain, wrapModes, modeX, modeY));
}
/**
@@ -60,8 +63,10 @@
static std::unique_ptr<GrFragmentProcessor> Make(sk_sp<GrTextureProxy> proxy,
const SkMatrix& matrix,
const SkRect& domain) {
- return std::unique_ptr<GrFragmentProcessor>(new GrBicubicEffect(std::move(proxy), matrix,
- domain));
+ static const GrSamplerState::WrapMode kClampClamp[] = {
+ GrSamplerState::WrapMode::kClamp, GrSamplerState::WrapMode::kClamp};
+ return Make(std::move(proxy), matrix, kClampClamp, GrTextureDomain::kClamp_Mode,
+ GrTextureDomain::kClamp_Mode, &domain);
}
/**
@@ -75,10 +80,9 @@
GrSamplerState::Filter* filterMode);
private:
- GrBicubicEffect(sk_sp<GrTextureProxy>, const SkMatrix& matrix,
+ GrBicubicEffect(sk_sp<GrTextureProxy>, const SkMatrix& matrix, const SkRect& domain,
const GrSamplerState::WrapMode wrapModes[2],
GrTextureDomain::Mode modeX, GrTextureDomain::Mode modeY);
- GrBicubicEffect(sk_sp<GrTextureProxy>, const SkMatrix &matrix, const SkRect& domain);
explicit GrBicubicEffect(const GrBicubicEffect&);
GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;