Decal fallback for SkImageShader
Bug: skia:
Change-Id: Ib39f74886c0edc655ded8ba1075e5205361ae650
Reviewed-on: https://skia-review.googlesource.com/c/176225
Commit-Queue: Michael Ludwig <michaelludwig@google.com>
Reviewed-by: Greg Daniel <egdaniel@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
diff --git a/src/effects/imagefilters/SkArithmeticImageFilter.cpp b/src/effects/imagefilters/SkArithmeticImageFilter.cpp
index 5c69068..8e8dabf 100644
--- a/src/effects/imagefilters/SkArithmeticImageFilter.cpp
+++ b/src/effects/imagefilters/SkArithmeticImageFilter.cpp
@@ -314,7 +314,7 @@
SkIntToScalar(bgSubset.top() - backgroundOffset.fY));
bgFP = GrTextureDomainEffect::Make(
std::move(backgroundProxy), backgroundMatrix,
- GrTextureDomain::MakeTexelDomain(bgSubset),
+ GrTextureDomain::MakeTexelDomain(bgSubset, GrTextureDomain::kDecal_Mode),
GrTextureDomain::kDecal_Mode, GrSamplerState::Filter::kNearest);
bgFP = GrColorSpaceXformEffect::Make(std::move(bgFP), background->getColorSpace(),
background->alphaType(),
@@ -331,7 +331,7 @@
SkIntToScalar(fgSubset.top() - foregroundOffset.fY));
auto foregroundFP = GrTextureDomainEffect::Make(
std::move(foregroundProxy), foregroundMatrix,
- GrTextureDomain::MakeTexelDomain(fgSubset),
+ GrTextureDomain::MakeTexelDomain(fgSubset, GrTextureDomain::kDecal_Mode),
GrTextureDomain::kDecal_Mode, GrSamplerState::Filter::kNearest);
foregroundFP = GrColorSpaceXformEffect::Make(std::move(foregroundFP),
foreground->getColorSpace(),
diff --git a/src/effects/imagefilters/SkDisplacementMapEffect.cpp b/src/effects/imagefilters/SkDisplacementMapEffect.cpp
index 3f42e7b..7dc6804 100644
--- a/src/effects/imagefilters/SkDisplacementMapEffect.cpp
+++ b/src/effects/imagefilters/SkDisplacementMapEffect.cpp
@@ -448,8 +448,10 @@
, fDisplacementTransform(offsetMatrix, displacement.get())
, fDisplacementSampler(displacement)
, fColorTransform(color.get())
- , fDomain(color.get(), GrTextureDomain::MakeTexelDomain(SkIRect::MakeSize(colorDimensions)),
- GrTextureDomain::kDecal_Mode)
+ , fDomain(color.get(),
+ GrTextureDomain::MakeTexelDomain(SkIRect::MakeSize(colorDimensions),
+ GrTextureDomain::kDecal_Mode),
+ GrTextureDomain::kDecal_Mode, GrTextureDomain::kDecal_Mode)
, fColorSampler(color)
, fXChannelSelector(xChannelSelector)
, fYChannelSelector(yChannelSelector)
@@ -605,7 +607,8 @@
pdman.set2f(fScaleUni, SkScalarToFloat(scaleX),
proxy->origin() == kTopLeft_GrSurfaceOrigin ?
SkScalarToFloat(scaleY) : SkScalarToFloat(-scaleY));
- fGLDomain.setData(pdman, displacementMap.domain(), proxy);
+ fGLDomain.setData(pdman, displacementMap.domain(), proxy,
+ displacementMap.textureSampler(1).samplerState());
}
void GrGLDisplacementMapEffect::GenKey(const GrProcessor& proc,
diff --git a/src/effects/imagefilters/SkLightingImageFilter.cpp b/src/effects/imagefilters/SkLightingImageFilter.cpp
index 92225a3..8e2357e 100644
--- a/src/effects/imagefilters/SkLightingImageFilter.cpp
+++ b/src/effects/imagefilters/SkLightingImageFilter.cpp
@@ -1676,8 +1676,8 @@
static GrTextureDomain create_domain(GrTextureProxy* proxy, const SkIRect* srcBounds,
GrTextureDomain::Mode mode) {
if (srcBounds) {
- SkRect texelDomain = GrTextureDomain::MakeTexelDomainForMode(*srcBounds, mode);
- return GrTextureDomain(proxy, texelDomain, mode);
+ SkRect texelDomain = GrTextureDomain::MakeTexelDomain(*srcBounds, mode);
+ return GrTextureDomain(proxy, texelDomain, mode, mode);
} else {
return GrTextureDomain::IgnoredDomain();
}
@@ -1926,7 +1926,7 @@
pdman.set1f(fSurfaceScaleUni, lighting.surfaceScale());
sk_sp<SkImageFilterLight> transformedLight(
lighting.light()->transform(lighting.filterMatrix()));
- fDomain.setData(pdman, lighting.domain(), proxy);
+ fDomain.setData(pdman, lighting.domain(), proxy, lighting.textureSampler(0).samplerState());
fLight->setData(pdman, transformedLight.get());
}
diff --git a/src/effects/imagefilters/SkXfermodeImageFilter.cpp b/src/effects/imagefilters/SkXfermodeImageFilter.cpp
index 08b24a6..62147e3 100644
--- a/src/effects/imagefilters/SkXfermodeImageFilter.cpp
+++ b/src/effects/imagefilters/SkXfermodeImageFilter.cpp
@@ -280,10 +280,10 @@
SkMatrix bgMatrix = SkMatrix::MakeTrans(
SkIntToScalar(bgSubset.left() - backgroundOffset.fX),
SkIntToScalar(bgSubset.top() - backgroundOffset.fY));
- bgFP = GrTextureDomainEffect::Make(std::move(backgroundProxy), bgMatrix,
- GrTextureDomain::MakeTexelDomain(bgSubset),
- GrTextureDomain::kDecal_Mode,
- GrSamplerState::Filter::kNearest);
+ bgFP = GrTextureDomainEffect::Make(
+ std::move(backgroundProxy), bgMatrix,
+ GrTextureDomain::MakeTexelDomain(bgSubset, GrTextureDomain::kDecal_Mode),
+ GrTextureDomain::kDecal_Mode, GrSamplerState::Filter::kNearest);
bgFP = GrColorSpaceXformEffect::Make(std::move(bgFP), background->getColorSpace(),
background->alphaType(),
outputProperties.colorSpace());
@@ -299,7 +299,7 @@
SkIntToScalar(fgSubset.top() - foregroundOffset.fY));
auto foregroundFP = GrTextureDomainEffect::Make(
std::move(foregroundProxy), fgMatrix,
- GrTextureDomain::MakeTexelDomain(fgSubset),
+ GrTextureDomain::MakeTexelDomain(fgSubset, GrTextureDomain::kDecal_Mode),
GrTextureDomain::kDecal_Mode, GrSamplerState::Filter::kNearest);
foregroundFP = GrColorSpaceXformEffect::Make(std::move(foregroundFP),
foreground->getColorSpace(),
diff --git a/src/gpu/effects/GrBicubicEffect.cpp b/src/gpu/effects/GrBicubicEffect.cpp
index 087c008..35ee12e 100644
--- a/src/gpu/effects/GrBicubicEffect.cpp
+++ b/src/gpu/effects/GrBicubicEffect.cpp
@@ -113,15 +113,20 @@
imageIncrement[0] = 1.0f / texture->width();
imageIncrement[1] = 1.0f / texture->height();
pdman.set2fv(fImageIncrementUni, 1, imageIncrement);
- fDomain.setData(pdman, bicubicEffect.domain(), proxy);
+ fDomain.setData(pdman, bicubicEffect.domain(), proxy,
+ processor.textureSampler(0).samplerState());
}
GrBicubicEffect::GrBicubicEffect(sk_sp<GrTextureProxy> proxy,
const SkMatrix& matrix,
- const GrSamplerState::WrapMode wrapModes[2])
+ const GrSamplerState::WrapMode wrapModes[2],
+ GrTextureDomain::Mode modeX, GrTextureDomain::Mode modeY)
: INHERITED{kGrBicubicEffect_ClassID, ModulateByConfigOptimizationFlags(proxy->config())}
, fCoordTransform(matrix, proxy.get())
- , fDomain(GrTextureDomain::IgnoredDomain())
+ , fDomain(proxy.get(),
+ GrTextureDomain::MakeTexelDomain(
+ SkIRect::MakeWH(proxy->width(), proxy->height()), modeX, modeY),
+ modeX, modeY)
, fTextureSampler(std::move(proxy),
GrSamplerState(wrapModes, GrSamplerState::Filter::kNearest)) {
this->addCoordTransform(&fCoordTransform);
@@ -133,7 +138,7 @@
const SkRect& domain)
: INHERITED(kGrBicubicEffect_ClassID, ModulateByConfigOptimizationFlags(proxy->config()))
, fCoordTransform(matrix, proxy.get())
- , fDomain(proxy.get(), domain, GrTextureDomain::kClamp_Mode)
+ , fDomain(proxy.get(), domain, GrTextureDomain::kClamp_Mode, GrTextureDomain::kClamp_Mode)
, fTextureSampler(std::move(proxy)) {
this->addCoordTransform(&fCoordTransform);
this->setTextureSamplerCnt(1);
diff --git a/src/gpu/effects/GrBicubicEffect.h b/src/gpu/effects/GrBicubicEffect.h
index 45c416f..68a63e1 100644
--- a/src/gpu/effects/GrBicubicEffect.h
+++ b/src/gpu/effects/GrBicubicEffect.h
@@ -34,8 +34,24 @@
static std::unique_ptr<GrFragmentProcessor> Make(sk_sp<GrTextureProxy> proxy,
const SkMatrix& matrix,
const GrSamplerState::WrapMode wrapModes[2]) {
+ // Ignore the domain on x and y, since this factory relies solely on the wrap mode of the
+ // sampler to constrain texture coordinates
+ return Make(std::move(proxy), matrix, wrapModes, GrTextureDomain::kIgnore_Mode,
+ GrTextureDomain::kIgnore_Mode);
+ }
+
+ /**
+ * Create a Mitchell filter effect with specified texture matrix and x/y tile modes. This
+ * supports providing modes for the texture domain explicitly, in the event that it should
+ * override the behavior of the sampler's tile mode (e.g. clamp to border unsupported).
+ */
+ static std::unique_ptr<GrFragmentProcessor> Make(sk_sp<GrTextureProxy> proxy,
+ 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));
+ wrapModes, modeX, modeY));
}
/**
@@ -60,7 +76,8 @@
private:
GrBicubicEffect(sk_sp<GrTextureProxy>, const SkMatrix& matrix,
- const GrSamplerState::WrapMode wrapModes[2]);
+ 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&);
diff --git a/src/gpu/effects/GrMatrixConvolutionEffect.cpp b/src/gpu/effects/GrMatrixConvolutionEffect.cpp
index b52c962..bd48c4b 100644
--- a/src/gpu/effects/GrMatrixConvolutionEffect.cpp
+++ b/src/gpu/effects/GrMatrixConvolutionEffect.cpp
@@ -140,7 +140,7 @@
pdman.set4fv(fKernelUni, arrayCount, conv.kernel());
pdman.set1f(fGainUni, conv.gain());
pdman.set1f(fBiasUni, conv.bias());
- fDomain.setData(pdman, conv.domain(), proxy);
+ fDomain.setData(pdman, conv.domain(), proxy, conv.textureSampler(0).samplerState());
}
GrMatrixConvolutionEffect::GrMatrixConvolutionEffect(sk_sp<GrTextureProxy> srcProxy,
@@ -156,9 +156,8 @@
// parameters.
: INHERITED(kGrMatrixConvolutionEffect_ClassID, kNone_OptimizationFlags)
, fCoordTransform(srcProxy.get())
- , fDomain(srcProxy.get(),
- GrTextureDomain::MakeTexelDomainForMode(srcBounds, tileMode),
- tileMode)
+ , fDomain(srcProxy.get(), GrTextureDomain::MakeTexelDomain(srcBounds, tileMode),
+ tileMode, tileMode)
, fTextureSampler(std::move(srcProxy))
, fKernelSize(kernelSize)
, fGain(SkScalarToFloat(gain))
diff --git a/src/gpu/effects/GrTextureDomain.cpp b/src/gpu/effects/GrTextureDomain.cpp
index c83271d..f452ca7 100644
--- a/src/gpu/effects/GrTextureDomain.cpp
+++ b/src/gpu/effects/GrTextureDomain.cpp
@@ -21,26 +21,14 @@
#include <utility>
-static bool can_ignore_rect(GrTextureProxy* proxy, const SkRect& domain) {
- if (GrProxyProvider::IsFunctionallyExact(proxy)) {
- const SkIRect kFullRect = SkIRect::MakeWH(proxy->width(), proxy->height());
-
- return domain.contains(kFullRect);
- }
-
- return false;
-}
-
-GrTextureDomain::GrTextureDomain(GrTextureProxy* proxy, const SkRect& domain, Mode mode, int index)
- : fMode(mode)
+GrTextureDomain::GrTextureDomain(GrTextureProxy* proxy, const SkRect& domain, Mode modeX,
+ Mode modeY, int index)
+ : fModeX(modeX)
+ , fModeY(modeY)
, fIndex(index) {
- if (kIgnore_Mode == fMode) {
- return;
- }
-
- if (kClamp_Mode == mode && can_ignore_rect(proxy, domain)) {
- fMode = kIgnore_Mode;
+ if (!proxy) {
+ SkASSERT(modeX == kIgnore_Mode && modeY == kIgnore_Mode);
return;
}
@@ -61,6 +49,33 @@
//////////////////////////////////////////////////////////////////////////////
+static SkString clamp_expression(GrTextureDomain::Mode mode, const char* inCoord,
+ const char* coordSwizzle, const char* domain,
+ const char* minSwizzle, const char* maxSwizzle) {
+ SkString clampedExpr;
+ switch(mode) {
+ case GrTextureDomain::kIgnore_Mode:
+ clampedExpr.printf("%s.%s\n", inCoord, coordSwizzle);
+ break;
+ case GrTextureDomain::kDecal_Mode:
+ // The lookup coordinate to use for decal will be clamped just like kClamp_Mode,
+ // it's just that the post-processing will be different, so fall through
+ case GrTextureDomain::kClamp_Mode:
+ clampedExpr.printf("clamp(%s.%s, %s.%s, %s.%s)",
+ inCoord, coordSwizzle, domain, minSwizzle, domain, maxSwizzle);
+ break;
+ case GrTextureDomain::kRepeat_Mode:
+ clampedExpr.printf("mod(%s.%s - %s.%s, %s.%s - %s.%s) + %s.%s",
+ inCoord, coordSwizzle, domain, minSwizzle, domain, maxSwizzle,
+ domain, minSwizzle, domain, minSwizzle);
+ break;
+ default:
+ SkASSERTF(false, "Unknown texture domain mode: %u\n", (uint32_t) mode);
+ break;
+ }
+ return clampedExpr;
+}
+
void GrTextureDomain::GLDomain::sampleTexture(GrGLSLShaderBuilder* builder,
GrGLSLUniformHandler* uniformHandler,
const GrShaderCaps* shaderCaps,
@@ -69,11 +84,14 @@
const SkString& inCoords,
GrGLSLFragmentProcessor::SamplerHandle sampler,
const char* inModulateColor) {
- SkASSERT(!fHasMode || textureDomain.mode() == fMode);
- SkDEBUGCODE(fMode = textureDomain.mode();)
+ SkASSERT(!fHasMode || (textureDomain.modeX() == fModeX && textureDomain.modeY() == fModeY));
+ SkDEBUGCODE(fModeX = textureDomain.modeX();)
+ SkDEBUGCODE(fModeY = textureDomain.modeY();)
SkDEBUGCODE(fHasMode = true;)
- if (textureDomain.mode() != kIgnore_Mode && !fDomainUni.isValid()) {
+ if ((textureDomain.modeX() != kIgnore_Mode || textureDomain.modeY() != kIgnore_Mode) &&
+ !fDomainUni.isValid()) {
+ // Must include the domain uniform since at least one axis uses it
const char* name;
SkString uniName("TexDom");
if (textureDomain.fIndex >= 0) {
@@ -84,95 +102,114 @@
fDomainName = name;
}
- switch (textureDomain.mode()) {
- case kIgnore_Mode: {
- builder->codeAppendf("%s = ", outColor);
- builder->appendTextureLookupAndModulate(inModulateColor, sampler, inCoords.c_str(),
- kFloat2_GrSLType);
- builder->codeAppend(";");
- break;
+ bool decalX = textureDomain.modeX() == kDecal_Mode;
+ bool decalY = textureDomain.modeY() == kDecal_Mode;
+ if ((decalX || decalY) && !fDecalUni.isValid()) {
+ const char* name;
+ SkString uniName("DecalParams");
+ if (textureDomain.fIndex >= 0) {
+ uniName.appendS32(textureDomain.fIndex);
}
- case kClamp_Mode: {
- SkString clampedCoords;
- clampedCoords.appendf("clamp(%s, %s.xy, %s.zw)",
- inCoords.c_str(), fDomainName.c_str(), fDomainName.c_str());
+ // Half3 since this will hold texture width, height, and then a step function control param
+ fDecalUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kHalf3_GrSLType,
+ uniName.c_str(), &name);
+ fDecalName = name;
+ }
- builder->codeAppendf("%s = ", outColor);
- builder->appendTextureLookupAndModulate(inModulateColor, sampler, clampedCoords.c_str(),
- kFloat2_GrSLType);
- builder->codeAppend(";");
- break;
+ // Add a block so that we can declare variables
+ GrGLSLShaderBuilder::ShaderBlock block(builder);
+ // Always use a local variable for the input coordinates; often callers pass in an expression
+ // and we want to cache it across all of its references in the code below
+ builder->codeAppendf("float2 origCoord = %s;", inCoords.c_str());
+ builder->codeAppend("float2 clampedCoord = ");
+ if (textureDomain.modeX() != textureDomain.modeY()) {
+ // The wrap modes differ on the two axes, so build up a coordinate that respects each axis'
+ // domain rule independently before sampling the texture.
+ SkString tcX = clamp_expression(textureDomain.modeX(), "origCoord", "x",
+ fDomainName.c_str(), "x", "z");
+ SkString tcY = clamp_expression(textureDomain.modeY(), "origCoord", "y",
+ fDomainName.c_str(), "y", "w");
+ builder->codeAppendf("float2(%s, %s)", tcX.c_str(), tcY.c_str());
+ } else {
+ // Since the x and y axis wrap modes are the same, they can be calculated together using
+ // more efficient vector operations
+ SkString tc = clamp_expression(textureDomain.modeX(), "origCoord", "xy",
+ fDomainName.c_str(), "xy", "zw");
+ builder->codeAppend(tc.c_str());
+ }
+ builder->codeAppend(";");
+
+ // Look up the texture sample at the clamped coordinate location
+ builder->codeAppend("half4 inside = ");
+ builder->appendTextureLookupAndModulate(inModulateColor, sampler, "clampedCoord",
+ kFloat2_GrSLType);
+ builder->codeAppend(";");
+
+ // Apply decal mode's transparency interpolation if needed
+ if (decalX || decalY) {
+ // The decal err is the max absoluate value between the clamped coordinate and the original
+ // pixel coordinate. This will then be clamped to 1.f if it's greater than the control
+ // parameter, which simulates kNearest and kBilerp behavior depending on if it's 0 or 1.
+ if (decalX && decalY) {
+ builder->codeAppendf("half err = max(abs(clampedCoord.x - origCoord.x) * %s.x, "
+ "abs(clampedCoord.y - origCoord.y) * %s.y);",
+ fDecalName.c_str(), fDecalName.c_str());
+ } else if (decalX) {
+ builder->codeAppendf("half err = abs(clampedCoord.x - origCoord.x) * %s.x;",
+ fDecalName.c_str());
+ } else {
+ SkASSERT(decalY);
+ builder->codeAppendf("half err = abs(clampedCoord.y - origCoord.y) * %s.y;",
+ fDecalName.c_str());
}
- case kDecal_Mode: {
- // Add a block since we're going to declare variables.
- GrGLSLShaderBuilder::ShaderBlock block(builder);
- const char* domain = fDomainName.c_str();
- if (!shaderCaps->canUseAnyFunctionInShader()) {
- // On the NexusS and GalaxyNexus, the other path (with the 'any'
- // call) causes the compilation error "Calls to any function that
- // may require a gradient calculation inside a conditional block
- // may return undefined results". This appears to be an issue with
- // the 'any' call since even the simple "result=black; if (any())
- // result=white;" code fails to compile.
- builder->codeAppend("half4 outside = half4(0.0, 0.0, 0.0, 0.0);");
- builder->codeAppend("half4 inside = ");
- builder->appendTextureLookupAndModulate(inModulateColor, sampler, inCoords.c_str(),
- kFloat2_GrSLType);
- builder->codeAppend(";");
-
- builder->codeAppendf("float x = (%s).x;", inCoords.c_str());
- builder->codeAppendf("float y = (%s).y;", inCoords.c_str());
-
- builder->codeAppendf("x = abs(2.0*(x - %s.x)/(%s.z - %s.x) - 1.0);",
- domain, domain, domain);
- builder->codeAppendf("y = abs(2.0*(y - %s.y)/(%s.w - %s.y) - 1.0);",
- domain, domain, domain);
- builder->codeAppend("half blend = step(1.0, max(x, y));");
- builder->codeAppendf("%s = mix(inside, outside, blend);", outColor);
- } else {
- builder->codeAppend("bool4 outside;\n");
- builder->codeAppendf("outside.xy = lessThan(%s, %s.xy);", inCoords.c_str(),
- domain);
- builder->codeAppendf("outside.zw = greaterThan(%s, %s.zw);", inCoords.c_str(),
- domain);
- builder->codeAppendf("%s = any(outside) ? half4(0.0, 0.0, 0.0, 0.0) : ",
- outColor);
- builder->appendTextureLookupAndModulate(inModulateColor, sampler, inCoords.c_str(),
- kFloat2_GrSLType);
- builder->codeAppend(";");
- }
- break;
- }
- case kRepeat_Mode: {
- SkString clampedCoords;
- clampedCoords.printf("mod(%s - %s.xy, %s.zw - %s.xy) + %s.xy",
- inCoords.c_str(), fDomainName.c_str(), fDomainName.c_str(),
- fDomainName.c_str(), fDomainName.c_str());
-
- builder->codeAppendf("%s = ", outColor);
- builder->appendTextureLookupAndModulate(inModulateColor, sampler, clampedCoords.c_str(),
- kFloat2_GrSLType);
- builder->codeAppend(";");
- break;
- }
+ // Apply a transform to the error rate, which let's us simulate nearest or bilerp filtering
+ // in the same shader. When the texture is nearest filtered, fSizeName.z is set to 1/2 so
+ // this becomes a step function centered at .5 away from the clamped coordinate (but the
+ // domain for decal is inset by .5 so the edge lines up properly). When bilerp, fSizeName.z
+ // is set to 1 and it becomes a simple linear blend between texture and transparent.
+ builder->codeAppendf("if (err > %s.z) { err = 1.0; } else if (%s.z < 1) { err = 0.0; }",
+ fDecalName.c_str(), fDecalName.c_str());
+ builder->codeAppendf("%s = mix(inside, half4(0, 0, 0, 0), err);", outColor);
+ } else {
+ // A simple look up
+ builder->codeAppendf("%s = inside;", outColor);
}
}
void GrTextureDomain::GLDomain::setData(const GrGLSLProgramDataManager& pdman,
const GrTextureDomain& textureDomain,
- GrTextureProxy* proxy) {
+ GrTextureProxy* proxy,
+ const GrSamplerState& sampler) {
GrTexture* tex = proxy->peekTexture();
- SkASSERT(fHasMode && textureDomain.mode() == fMode);
- if (kIgnore_Mode != textureDomain.mode()) {
+ SkASSERT(fHasMode && textureDomain.modeX() == fModeX && textureDomain.modeY() == fModeY);
+ if (kIgnore_Mode != textureDomain.modeX() || kIgnore_Mode != textureDomain.modeY()) {
+ bool sendDecalData = textureDomain.modeX() == kDecal_Mode ||
+ textureDomain.modeY() == kDecal_Mode;
+
+ // If the texture is using nearest filtering, then the decal filter weight should step from
+ // 0 (texture) to 1 (transparent) one half pixel away from the domain. When doing any other
+ // form of filtering, the weight should be 1.0 so that it smoothly interpolates between the
+ // texture and transparent.
+ SkScalar decalFilterWeight = sampler.filter() == GrSamplerState::Filter::kNearest ?
+ SK_ScalarHalf : 1.0f;
SkScalar wInv, hInv, h;
if (proxy->textureType() == GrTextureType::kRectangle) {
wInv = hInv = 1.f;
h = tex->height();
+
+ // Don't do any scaling by texture size for decal filter rate, it's already in pixels
+ if (sendDecalData) {
+ pdman.set3f(fDecalUni, 1.f, 1.f, decalFilterWeight);
+ }
} else {
wInv = SK_Scalar1 / tex->width();
hInv = SK_Scalar1 / tex->height();
h = 1.f;
+
+ if (sendDecalData) {
+ pdman.set3f(fDecalUni, tex->width(), tex->height(), decalFilterWeight);
+ }
}
float values[kPrevDomainCount] = {
@@ -213,8 +250,9 @@
///////////////////////////////////////////////////////////////////////////////
inline GrFragmentProcessor::OptimizationFlags GrTextureDomainEffect::OptFlags(
- GrPixelConfig config, GrTextureDomain::Mode mode) {
- if (mode == GrTextureDomain::kDecal_Mode || !GrPixelConfigIsOpaque(config)) {
+ GrPixelConfig config, GrTextureDomain::Mode modeX, GrTextureDomain::Mode modeY) {
+ if (modeX == GrTextureDomain::kDecal_Mode || modeY == GrTextureDomain::kDecal_Mode ||
+ !GrPixelConfigIsOpaque(config)) {
return GrFragmentProcessor::kCompatibleWithCoverageAsAlpha_OptimizationFlag;
} else {
return GrFragmentProcessor::kCompatibleWithCoverageAsAlpha_OptimizationFlag |
@@ -228,26 +266,37 @@
const SkRect& domain,
GrTextureDomain::Mode mode,
GrSamplerState::Filter filterMode) {
- if (GrTextureDomain::kIgnore_Mode == mode ||
- (GrTextureDomain::kClamp_Mode == mode && can_ignore_rect(proxy.get(), domain))) {
- return GrSimpleTextureEffect::Make(std::move(proxy), matrix, filterMode);
- } else {
- return std::unique_ptr<GrFragmentProcessor>(new GrTextureDomainEffect(
- std::move(proxy), matrix, domain, mode, filterMode));
- }
+ return Make(std::move(proxy), matrix, domain, mode, mode,
+ GrSamplerState(GrSamplerState::WrapMode::kClamp, filterMode));
+}
+
+std::unique_ptr<GrFragmentProcessor> GrTextureDomainEffect::Make(
+ sk_sp<GrTextureProxy> proxy,
+ const SkMatrix& matrix,
+ const SkRect& domain,
+ GrTextureDomain::Mode modeX,
+ GrTextureDomain::Mode modeY,
+ const GrSamplerState& sampler) {
+ // If both domain modes happen to be ignore, it would be faster to just drop the domain logic
+ // entirely Technically, we could also use the simple texture effect if the domain modes agree
+ // with the sampler modes and the proxy is the same size as the domain. It's a lot easier for
+ // calling code to detect these cases and handle it themselves.
+ return std::unique_ptr<GrFragmentProcessor>(new GrTextureDomainEffect(
+ std::move(proxy), matrix, domain, modeX, modeY, sampler));
}
GrTextureDomainEffect::GrTextureDomainEffect(sk_sp<GrTextureProxy> proxy,
const SkMatrix& matrix,
const SkRect& domain,
- GrTextureDomain::Mode mode,
- GrSamplerState::Filter filterMode)
- : INHERITED(kGrTextureDomainEffect_ClassID, OptFlags(proxy->config(), mode))
+ GrTextureDomain::Mode modeX,
+ GrTextureDomain::Mode modeY,
+ const GrSamplerState& sampler)
+ : INHERITED(kGrTextureDomainEffect_ClassID, OptFlags(proxy->config(), modeX, modeY))
, fCoordTransform(matrix, proxy.get())
- , fTextureDomain(proxy.get(), domain, mode)
- , fTextureSampler(std::move(proxy), filterMode) {
- SkASSERT(mode != GrTextureDomain::kRepeat_Mode ||
- filterMode == GrSamplerState::Filter::kNearest);
+ , fTextureDomain(proxy.get(), domain, modeX, modeY)
+ , fTextureSampler(std::move(proxy), sampler) {
+ SkASSERT((modeX != GrTextureDomain::kRepeat_Mode && modeY != GrTextureDomain::kRepeat_Mode) ||
+ sampler.filter() == GrSamplerState::Filter::kNearest);
this->addCoordTransform(&fCoordTransform);
this->setTextureSamplerCnt(1);
}
@@ -293,7 +342,7 @@
const GrTextureDomain& domain = tde.fTextureDomain;
GrTextureProxy* proxy = tde.textureSampler(0).proxy();
- fGLDomain.setData(pdman, domain, proxy);
+ fGLDomain.setData(pdman, domain, proxy, tde.textureSampler(0).samplerState());
}
private:
@@ -322,16 +371,21 @@
domain.fRight = d->fRandom->nextRangeScalar(domain.fLeft, proxy->width());
domain.fTop = d->fRandom->nextRangeScalar(0, proxy->height());
domain.fBottom = d->fRandom->nextRangeScalar(domain.fTop, proxy->height());
- GrTextureDomain::Mode mode =
+ GrTextureDomain::Mode modeX =
+ (GrTextureDomain::Mode) d->fRandom->nextULessThan(GrTextureDomain::kModeCount);
+ GrTextureDomain::Mode modeY =
(GrTextureDomain::Mode) d->fRandom->nextULessThan(GrTextureDomain::kModeCount);
const SkMatrix& matrix = GrTest::TestMatrix(d->fRandom);
- bool bilerp = mode != GrTextureDomain::kRepeat_Mode ? d->fRandom->nextBool() : false;
+ bool bilerp = modeX != GrTextureDomain::kRepeat_Mode && modeY != GrTextureDomain::kRepeat_Mode ?
+ d->fRandom->nextBool() : false;
return GrTextureDomainEffect::Make(
std::move(proxy),
matrix,
domain,
- mode,
- bilerp ? GrSamplerState::Filter::kBilerp : GrSamplerState::Filter::kNearest);
+ modeX,
+ modeY,
+ GrSamplerState(GrSamplerState::WrapMode::kClamp, bilerp ?
+ GrSamplerState::Filter::kBilerp : GrSamplerState::Filter::kNearest));
}
#endif
@@ -347,8 +401,9 @@
: INHERITED(kGrDeviceSpaceTextureDecalFragmentProcessor_ClassID,
kCompatibleWithCoverageAsAlpha_OptimizationFlag)
, fTextureSampler(proxy, GrSamplerState::ClampNearest())
- , fTextureDomain(proxy.get(), GrTextureDomain::MakeTexelDomain(subset),
- GrTextureDomain::kDecal_Mode) {
+ , fTextureDomain(proxy.get(),
+ GrTextureDomain::MakeTexelDomain(subset, GrTextureDomain::kDecal_Mode),
+ GrTextureDomain::kDecal_Mode, GrTextureDomain::kDecal_Mode) {
this->setTextureSamplerCnt(1);
fDeviceSpaceOffset.fX = deviceSpaceOffset.fX - subset.fLeft;
fDeviceSpaceOffset.fY = deviceSpaceOffset.fY - subset.fTop;
@@ -400,7 +455,8 @@
GrTextureProxy* proxy = dstdfp.textureSampler(0).proxy();
GrTexture* texture = proxy->peekTexture();
- fGLDomain.setData(pdman, dstdfp.fTextureDomain, proxy);
+ fGLDomain.setData(pdman, dstdfp.fTextureDomain, proxy,
+ dstdfp.textureSampler(0).samplerState());
float iw = 1.f / texture->width();
float ih = 1.f / texture->height();
float scaleAndTransData[4] = {
diff --git a/src/gpu/effects/GrTextureDomain.h b/src/gpu/effects/GrTextureDomain.h
index 198bf93..33f3e17 100644
--- a/src/gpu/effects/GrTextureDomain.h
+++ b/src/gpu/effects/GrTextureDomain.h
@@ -45,7 +45,7 @@
static const GrTextureDomain& IgnoredDomain() {
static const GrTextureDomain gDomain((GrTextureProxy*)nullptr,
- SkRect::MakeEmpty(), kIgnore_Mode);
+ SkRect::MakeEmpty(), kIgnore_Mode, kIgnore_Mode);
return gDomain;
}
@@ -53,28 +53,38 @@
* @param index Pass a value >= 0 if using multiple texture domains in the same effect.
* It is used to keep inserted variables from causing name collisions.
*/
- GrTextureDomain(GrTextureProxy*, const SkRect& domain, Mode, int index = -1);
+ GrTextureDomain(GrTextureProxy*, const SkRect& domain, Mode modeX, Mode modeY, int index = -1);
GrTextureDomain(const GrTextureDomain&) = default;
const SkRect& domain() const { return fDomain; }
- Mode mode() const { return fMode; }
+ Mode modeX() const { return fModeX; }
+ Mode modeY() const { return fModeY; }
- /* Computes a domain that bounds all the texels in texelRect. Note that with bilerp enabled
- texels neighboring the domain may be read. */
- static const SkRect MakeTexelDomain(const SkIRect& texelRect) {
- return SkRect::Make(texelRect);
+ /*
+ * Computes a domain that bounds all the texels in texelRect, possibly insetting by half a pixel
+ * depending on the mode. The mode is used for both axes.
+ */
+ static const SkRect MakeTexelDomain(const SkIRect& texelRect, Mode mode) {
+ return MakeTexelDomain(texelRect, mode, mode);
}
- static const SkRect MakeTexelDomainForMode(const SkIRect& texelRect, Mode mode) {
- // For Clamp mode, inset by half a texel.
- SkScalar inset = (mode == kClamp_Mode && !texelRect.isEmpty()) ? SK_ScalarHalf : 0;
- return SkRect::MakeLTRB(texelRect.fLeft + inset, texelRect.fTop + inset,
- texelRect.fRight - inset, texelRect.fBottom - inset);
+ static const SkRect MakeTexelDomain(const SkIRect& texelRect, Mode modeX, Mode modeY) {
+ // For Clamp and decal modes, inset by half a texel
+ SkScalar insetX = ((modeX == kClamp_Mode || modeX == kDecal_Mode) && texelRect.width() > 0)
+ ? SK_ScalarHalf : 0;
+ SkScalar insetY = ((modeY == kClamp_Mode || modeY == kDecal_Mode) && texelRect.height() > 0)
+ ? SK_ScalarHalf : 0;
+ return SkRect::MakeLTRB(texelRect.fLeft + insetX, texelRect.fTop + insetY,
+ texelRect.fRight - insetX, texelRect.fBottom - insetY);
}
bool operator==(const GrTextureDomain& that) const {
- return fMode == that.fMode && (kIgnore_Mode == fMode || fDomain == that.fDomain);
+ return fModeX == that.fModeX && fModeY == that.fModeY &&
+ (kIgnore_Mode == fModeX || (fDomain.fLeft == that.fDomain.fLeft &&
+ fDomain.fRight == that.fDomain.fRight)) &&
+ (kIgnore_Mode == fModeY || (fDomain.fTop == that.fDomain.fTop &&
+ fDomain.fBottom == that.fDomain.fBottom));
}
/**
@@ -115,10 +125,12 @@
* texture domain. The rectangle is automatically adjusted to account for the texture's
* origin.
*/
- void setData(const GrGLSLProgramDataManager&, const GrTextureDomain&, GrTextureProxy*);
+ void setData(const GrGLSLProgramDataManager&, const GrTextureDomain&, GrTextureProxy*,
+ const GrSamplerState& sampler);
enum {
- kDomainKeyBits = 2, // See DomainKey().
+ kModeBits = 2, // See DomainKey().
+ kDomainKeyBits = 4
};
/**
@@ -126,21 +138,28 @@
* computed key. The returned will be limited to the lower kDomainKeyBits bits.
*/
static uint32_t DomainKey(const GrTextureDomain& domain) {
- GR_STATIC_ASSERT(kModeCount <= (1 << kDomainKeyBits));
- return domain.mode();
+ GR_STATIC_ASSERT(kModeCount <= (1 << kModeBits));
+ return domain.modeX() | (domain.modeY() << kModeBits);
}
private:
static const int kPrevDomainCount = 4;
- SkDEBUGCODE(Mode fMode;)
+ SkDEBUGCODE(Mode fModeX;)
+ SkDEBUGCODE(Mode fModeY;)
SkDEBUGCODE(bool fHasMode = false;)
GrGLSLProgramDataManager::UniformHandle fDomainUni;
SkString fDomainName;
+
+ // Only initialized if the domain has at least one decal axis
+ GrGLSLProgramDataManager::UniformHandle fDecalUni;
+ SkString fDecalName;
+
float fPrevDomain[kPrevDomainCount];
};
protected:
- Mode fMode;
+ Mode fModeX;
+ Mode fModeY;
SkRect fDomain;
int fIndex;
};
@@ -153,9 +172,16 @@
static std::unique_ptr<GrFragmentProcessor> Make(sk_sp<GrTextureProxy>,
const SkMatrix&,
const SkRect& domain,
- GrTextureDomain::Mode,
+ GrTextureDomain::Mode mode,
GrSamplerState::Filter filterMode);
+ static std::unique_ptr<GrFragmentProcessor> Make(sk_sp<GrTextureProxy>,
+ const SkMatrix&,
+ const SkRect& domain,
+ GrTextureDomain::Mode modeX,
+ GrTextureDomain::Mode modeY,
+ const GrSamplerState& sampler);
+
const char* name() const override { return "TextureDomain"; }
std::unique_ptr<GrFragmentProcessor> clone() const override {
@@ -181,12 +207,14 @@
GrTextureDomainEffect(sk_sp<GrTextureProxy>,
const SkMatrix&,
const SkRect& domain,
- GrTextureDomain::Mode,
- GrSamplerState::Filter);
+ GrTextureDomain::Mode modeX,
+ GrTextureDomain::Mode modeY,
+ const GrSamplerState&);
explicit GrTextureDomainEffect(const GrTextureDomainEffect&);
- static OptimizationFlags OptFlags(GrPixelConfig config, GrTextureDomain::Mode mode);
+ static OptimizationFlags OptFlags(GrPixelConfig config, GrTextureDomain::Mode modeX,
+ GrTextureDomain::Mode modeY);
GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
diff --git a/src/shaders/SkImageShader.cpp b/src/shaders/SkImageShader.cpp
index cccbc33..c63930e 100644
--- a/src/shaders/SkImageShader.cpp
+++ b/src/shaders/SkImageShader.cpp
@@ -187,16 +187,19 @@
GrSamplerState::WrapMode wrapModes[] = {tile_mode_to_wrap_mode(fTileModeX),
tile_mode_to_wrap_mode(fTileModeY)};
- if ((wrapModes[0] == GrSamplerState::WrapMode::kClampToBorder ||
- wrapModes[1] == GrSamplerState::WrapMode::kClampToBorder) &&
- !args.fContext->contextPriv().caps()->clampToBorderSupport()) {
- // HW clamp to border is unavailable, so fall back to clamp for now
- // TODO(michaelludwig): If clamp-to-border is selected but is unsupported, the texture
- // domain effect could be used to emulate the decal effect.
+
+ // If either domainX or domainY are un-ignored, a texture domain effect has to be used to
+ // implement the decal mode (while leaving non-decal axes alone). The wrap mode originally
+ // clamp-to-border is reset to clamp since the hw cannot implement it directly.
+ GrTextureDomain::Mode domainX = GrTextureDomain::kIgnore_Mode;
+ GrTextureDomain::Mode domainY = GrTextureDomain::kIgnore_Mode;
+ if (!args.fContext->contextPriv().caps()->clampToBorderSupport()) {
if (wrapModes[0] == GrSamplerState::WrapMode::kClampToBorder) {
+ domainX = GrTextureDomain::kDecal_Mode;
wrapModes[0] = GrSamplerState::WrapMode::kClamp;
}
if (wrapModes[1] == GrSamplerState::WrapMode::kClampToBorder) {
+ domainY = GrTextureDomain::kDecal_Mode;
wrapModes[1] = GrSamplerState::WrapMode::kClamp;
}
}
@@ -224,9 +227,19 @@
std::unique_ptr<GrFragmentProcessor> inner;
if (doBicubic) {
- inner = GrBicubicEffect::Make(std::move(proxy), lmInverse, wrapModes);
+ // domainX and domainY will properly apply the decal effect with the texture domain used in
+ // the bicubic filter if clamp to border was unsupported in hardware
+ inner = GrBicubicEffect::Make(std::move(proxy), lmInverse, wrapModes, domainX, domainY);
} else {
- inner = GrSimpleTextureEffect::Make(std::move(proxy), lmInverse, samplerState);
+ if (domainX != GrTextureDomain::kIgnore_Mode || domainY != GrTextureDomain::kIgnore_Mode) {
+ SkRect domain = GrTextureDomain::MakeTexelDomain(
+ SkIRect::MakeWH(proxy->width(), proxy->height()),
+ domainX, domainY);
+ inner = GrTextureDomainEffect::Make(std::move(proxy), lmInverse, domain,
+ domainX, domainY, samplerState);
+ } else {
+ inner = GrSimpleTextureEffect::Make(std::move(proxy), lmInverse, samplerState);
+ }
}
inner = GrColorSpaceXformEffect::Make(std::move(inner), fImage->colorSpace(),
fImage->alphaType(),