Move GrDomainEffect functionality into GrTextureEffect and delete
the former.
New factories for GrTextureEffect have two main variants:
MakeTexelSubset(): adjusts the input integer rectangle to account for
filtering
MakeSubset(): assumes caller has calculated the exact rectangle needed
as floats.
Currently this disables filtering for shader-based mirroring or repeat.
Will fix this later. The old effect also did not support this.
Change-Id: If47d8ecfbb349b0d7b39ab5ba864fe3cc1b139e4
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/265518
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 d6df230..5102f9e 100644
--- a/src/core/SkBlurMF.cpp
+++ b/src/core/SkBlurMF.cpp
@@ -28,7 +28,6 @@
#include "src/gpu/GrShaderCaps.h"
#include "src/gpu/GrStyle.h"
#include "src/gpu/GrTextureProxy.h"
-#include "src/gpu/effects/GrTextureDomain.h"
#include "src/gpu/effects/GrTextureEffect.h"
#include "src/gpu/effects/generated/GrCircleBlurFragmentProcessor.h"
#include "src/gpu/effects/generated/GrRRectBlurEffect.h"
diff --git a/src/core/SkGpuBlurUtils.cpp b/src/core/SkGpuBlurUtils.cpp
index 514e85a..f63f31e 100644
--- a/src/core/SkGpuBlurUtils.cpp
+++ b/src/core/SkGpuBlurUtils.cpp
@@ -311,17 +311,15 @@
}
GrPaint paint;
- auto fp = GrTextureEffect::Make(std::move(srcProxy), srcAlphaType, SkMatrix::I(),
- GrSamplerState::Filter::kBilerp);
+ std::unique_ptr<GrFragmentProcessor> fp;
if (i == 1) {
- // GrDomainEffect does not support kRepeat_Mode with GrSamplerState::Filter.
- GrTextureDomain::Mode domainMode;
+ GrSamplerState::WrapMode wrapMode;
if (mode == SkTileMode::kClamp) {
- domainMode = GrTextureDomain::kClamp_Mode;
+ wrapMode = GrSamplerState::WrapMode::kClamp;
} else {
- // GrDomainEffect does not support k[Mirror]Repeat with GrSamplerState::Filter.
- // So we use decal.
- domainMode = GrTextureDomain::kDecal_Mode;
+ // GrTextureEffect does not support WrapMode::k[Mirror]Repeat with
+ // GrSamplerState::Filter::kBilerp. So we use kClampToBorder.
+ wrapMode = GrSamplerState::WrapMode::kClampToBorder;
}
SkRect domain = SkRect::Make(*contentRect);
domain.inset((i < scaleFactorX) ? SK_ScalarHalf + SK_ScalarNearlyZero : 0.0f,
@@ -333,8 +331,14 @@
if (domain.fBottom < domain.fTop) {
domain.fTop = domain.fBottom = SkScalarAve(domain.fTop, domain.fBottom);
}
- fp = GrDomainEffect::Make(std::move(fp), domain, domainMode, true);
+ const auto& caps = *context->priv().caps();
+ GrSamplerState sampler(wrapMode, GrSamplerState::Filter::kBilerp);
+ fp = GrTextureEffect::MakeSubset(std::move(srcProxy), srcAlphaType, SkMatrix::I(),
+ sampler, domain, caps);
srcRect.offset(-srcOffset);
+ } else {
+ fp = GrTextureEffect::Make(std::move(srcProxy), srcAlphaType, SkMatrix::I(),
+ GrSamplerState::Filter::kBilerp);
}
paint.addColorFragmentProcessor(std::move(fp));
paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
@@ -388,11 +392,9 @@
}
GrPaint paint;
- SkRect domain = GrTextureDomain::MakeTexelDomain(srcBounds, GrTextureDomain::kClamp_Mode,
- GrTextureDomain::kClamp_Mode);
- auto fp = GrTextureEffect::Make(std::move(srcProxy), srcAlphaType, SkMatrix::I(),
- GrSamplerState::Filter::kBilerp);
- fp = GrDomainEffect::Make(std::move(fp), domain, GrTextureDomain::kClamp_Mode, true);
+ const auto& caps = *context->priv().caps();
+ auto fp = GrTextureEffect::MakeTexelSubset(std::move(srcProxy), srcAlphaType, SkMatrix::I(),
+ GrSamplerState::Filter::kBilerp, srcBounds, caps);
paint.addColorFragmentProcessor(std::move(fp));
paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
GrFixedClip clip(SkIRect::MakeSize(dstSize));
diff --git a/src/core/SkGpuBlurUtils.h b/src/core/SkGpuBlurUtils.h
index 53463e8..47bb9f0 100644
--- a/src/core/SkGpuBlurUtils.h
+++ b/src/core/SkGpuBlurUtils.h
@@ -10,7 +10,6 @@
#if SK_SUPPORT_GPU
#include "src/gpu/GrRenderTargetContext.h"
-#include "src/gpu/effects/GrTextureDomain.h"
class GrContext;
class GrTexture;
diff --git a/src/effects/imagefilters/SkArithmeticImageFilter.cpp b/src/effects/imagefilters/SkArithmeticImageFilter.cpp
index 8a08602..ecca33e 100644
--- a/src/effects/imagefilters/SkArithmeticImageFilter.cpp
+++ b/src/effects/imagefilters/SkArithmeticImageFilter.cpp
@@ -25,7 +25,6 @@
#include "src/gpu/GrTextureProxy.h"
#include "src/gpu/SkGr.h"
#include "src/gpu/effects/GrSkSLFP.h"
-#include "src/gpu/effects/GrTextureDomain.h"
#include "src/gpu/effects/generated/GrConstColorProcessor.h"
#include "src/gpu/glsl/GrGLSLFragmentProcessor.h"
#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
@@ -348,18 +347,17 @@
GrPaint paint;
std::unique_ptr<GrFragmentProcessor> bgFP;
+ const auto& caps = *ctx.getContext()->priv().caps();
+ GrSamplerState sampler(GrSamplerState::WrapMode::kClampToBorder,
+ GrSamplerState::Filter::kNearest);
if (backgroundProxy) {
SkIRect bgSubset = background->subset();
SkMatrix backgroundMatrix = SkMatrix::MakeTrans(
SkIntToScalar(bgSubset.left() - backgroundOffset.fX),
SkIntToScalar(bgSubset.top() - backgroundOffset.fY));
- bgFP = GrTextureEffect::Make(std::move(backgroundProxy), background->alphaType(),
- backgroundMatrix, GrSamplerState::Filter::kNearest);
- bgFP = GrDomainEffect::Make(
- std::move(bgFP),
- GrTextureDomain::MakeTexelDomain(bgSubset, GrTextureDomain::kDecal_Mode),
- GrTextureDomain::kDecal_Mode, false);
+ bgFP = GrTextureEffect::MakeTexelSubset(std::move(backgroundProxy), background->alphaType(),
+ backgroundMatrix, sampler, bgSubset, caps);
bgFP = GrColorSpaceXformEffect::Make(std::move(bgFP), background->getColorSpace(),
background->alphaType(),
ctx.colorSpace());
@@ -373,18 +371,14 @@
SkMatrix foregroundMatrix = SkMatrix::MakeTrans(
SkIntToScalar(fgSubset.left() - foregroundOffset.fX),
SkIntToScalar(fgSubset.top() - foregroundOffset.fY));
- auto foregroundFP =
- GrTextureEffect::Make(std::move(foregroundProxy), foreground->alphaType(),
- foregroundMatrix, GrSamplerState::Filter::kNearest);
- foregroundFP = GrDomainEffect::Make(
- std::move(foregroundFP),
- GrTextureDomain::MakeTexelDomain(fgSubset, GrTextureDomain::kDecal_Mode),
- GrTextureDomain::kDecal_Mode, false);
- foregroundFP = GrColorSpaceXformEffect::Make(std::move(foregroundFP),
- foreground->getColorSpace(),
- foreground->alphaType(),
- ctx.colorSpace());
- paint.addColorFragmentProcessor(std::move(foregroundFP));
+ auto fgFP = GrTextureEffect::MakeTexelSubset(std::move(foregroundProxy),
+ foreground->alphaType(), foregroundMatrix,
+ sampler, fgSubset, caps);
+ fgFP = GrColorSpaceXformEffect::Make(std::move(fgFP),
+ foreground->getColorSpace(),
+ foreground->alphaType(),
+ ctx.colorSpace());
+ paint.addColorFragmentProcessor(std::move(fgFP));
static auto effect = std::get<0>(SkRuntimeEffect::Make(SkString(SKSL_ARITHMETIC_SRC)));
ArithmeticFPInputs inputs;
diff --git a/src/effects/imagefilters/SkXfermodeImageFilter.cpp b/src/effects/imagefilters/SkXfermodeImageFilter.cpp
index 8924456..0a80ea2 100644
--- a/src/effects/imagefilters/SkXfermodeImageFilter.cpp
+++ b/src/effects/imagefilters/SkXfermodeImageFilter.cpp
@@ -22,9 +22,7 @@
#include "src/gpu/GrRecordingContextPriv.h"
#include "src/gpu/GrRenderTargetContext.h"
#include "src/gpu/GrTextureProxy.h"
-
#include "src/gpu/SkGr.h"
-#include "src/gpu/effects/GrTextureDomain.h"
#include "src/gpu/effects/GrTextureEffect.h"
#include "src/gpu/effects/generated/GrConstColorProcessor.h"
#endif
@@ -260,18 +258,17 @@
GrPaint paint;
std::unique_ptr<GrFragmentProcessor> bgFP;
+ const auto& caps = *ctx.getContext()->priv().caps();
+ GrSamplerState sampler(GrSamplerState::WrapMode::kClampToBorder,
+ GrSamplerState::Filter::kNearest);
if (backgroundProxy) {
SkIRect bgSubset = background->subset();
SkMatrix bgMatrix = SkMatrix::MakeTrans(
SkIntToScalar(bgSubset.left() - backgroundOffset.fX),
SkIntToScalar(bgSubset.top() - backgroundOffset.fY));
- bgFP = GrTextureEffect::Make(std::move(backgroundProxy), background->alphaType(), bgMatrix,
- GrSamplerState::Filter::kNearest);
- bgFP = GrDomainEffect::Make(
- std::move(bgFP),
- GrTextureDomain::MakeTexelDomain(bgSubset, GrTextureDomain::kDecal_Mode),
- GrTextureDomain::kDecal_Mode, false);
+ bgFP = GrTextureEffect::MakeTexelSubset(std::move(backgroundProxy), background->alphaType(),
+ bgMatrix, sampler, bgSubset, caps);
bgFP = GrColorSpaceXformEffect::Make(std::move(bgFP), background->getColorSpace(),
background->alphaType(),
ctx.colorSpace());
@@ -285,18 +282,14 @@
SkMatrix fgMatrix = SkMatrix::MakeTrans(
SkIntToScalar(fgSubset.left() - foregroundOffset.fX),
SkIntToScalar(fgSubset.top() - foregroundOffset.fY));
- auto foregroundFP =
- GrTextureEffect::Make(std::move(foregroundProxy), foreground->alphaType(), fgMatrix,
- GrSamplerState::Filter::kNearest);
- foregroundFP = GrDomainEffect::Make(
- std::move(foregroundFP),
- GrTextureDomain::MakeTexelDomain(fgSubset, GrTextureDomain::kDecal_Mode),
- GrTextureDomain::kDecal_Mode, false);
- foregroundFP = GrColorSpaceXformEffect::Make(std::move(foregroundFP),
- foreground->getColorSpace(),
- foreground->alphaType(),
- ctx.colorSpace());
- paint.addColorFragmentProcessor(std::move(foregroundFP));
+ auto fgFP = GrTextureEffect::MakeTexelSubset(std::move(foregroundProxy),
+ foreground->alphaType(), fgMatrix, sampler,
+ fgSubset, caps);
+ fgFP = GrColorSpaceXformEffect::Make(std::move(fgFP),
+ foreground->getColorSpace(),
+ foreground->alphaType(),
+ ctx.colorSpace());
+ paint.addColorFragmentProcessor(std::move(fgFP));
std::unique_ptr<GrFragmentProcessor> xferFP = this->makeFGFrag(std::move(bgFP));
diff --git a/src/gpu/GrImageTextureMaker.cpp b/src/gpu/GrImageTextureMaker.cpp
index ead5dfe..cc6f145 100644
--- a/src/gpu/GrImageTextureMaker.cpp
+++ b/src/gpu/GrImageTextureMaker.cpp
@@ -8,6 +8,7 @@
#include "src/gpu/GrImageTextureMaker.h"
#include "src/gpu/GrColorSpaceXform.h"
+#include "src/gpu/GrContextPriv.h"
#include "src/gpu/GrRecordingContextPriv.h"
#include "src/gpu/SkGr.h"
#include "src/gpu/effects/GrYUVtoRGBEffect.h"
@@ -113,8 +114,9 @@
domain = &constraintRect;
}
- auto fp = GrYUVtoRGBEffect::Make(fImage->fProxies, fImage->fYUVAIndices,
- fImage->fYUVColorSpace, filter, textureMatrix, domain);
+ const auto& caps = *fImage->context()->priv().caps();
+ auto fp = GrYUVtoRGBEffect::Make(fImage->fProxies, fImage->fYUVAIndices, fImage->fYUVColorSpace,
+ filter, caps, textureMatrix, domain);
if (fImage->fFromColorSpace) {
fp = GrColorSpaceXformEffect::Make(std::move(fp), fImage->fFromColorSpace.get(),
fImage->alphaType(), fImage->colorSpace());
diff --git a/src/gpu/GrProcessor.h b/src/gpu/GrProcessor.h
index fe6b12b..f3ae030 100644
--- a/src/gpu/GrProcessor.h
+++ b/src/gpu/GrProcessor.h
@@ -107,7 +107,6 @@
kGrDistanceFieldA8TextGeoProc_ClassID,
kGrDistanceFieldLCDTextGeoProc_ClassID,
kGrDistanceFieldPathGeoProc_ClassID,
- kGrDomainEffect_ClassID,
kGrDualIntervalGradientColorizer_ClassID,
kGrEllipseEffect_ClassID,
kGrFillRRectOp_Processor_ClassID,
diff --git a/src/gpu/GrProcessorUnitTest.cpp b/src/gpu/GrProcessorUnitTest.cpp
index afd79ed..21f282b 100644
--- a/src/gpu/GrProcessorUnitTest.cpp
+++ b/src/gpu/GrProcessorUnitTest.cpp
@@ -81,7 +81,7 @@
* we verify the count is as expected. If a new factory is added, then these numbers must be
* manually adjusted.
*/
-static const int kFPFactoryCount = 37;
+static const int kFPFactoryCount = 36;
static const int kGPFactoryCount = 14;
static const int kXPFactoryCount = 4;
diff --git a/src/gpu/GrRenderTargetContext.cpp b/src/gpu/GrRenderTargetContext.cpp
index bab4d1db..b98386b 100644
--- a/src/gpu/GrRenderTargetContext.cpp
+++ b/src/gpu/GrRenderTargetContext.cpp
@@ -45,7 +45,6 @@
#include "src/gpu/SkGr.h"
#include "src/gpu/effects/GrBicubicEffect.h"
#include "src/gpu/effects/GrRRectEffect.h"
-#include "src/gpu/effects/GrTextureDomain.h"
#include "src/gpu/effects/generated/GrColorMatrixFragmentProcessor.h"
#include "src/gpu/geometry/GrQuad.h"
#include "src/gpu/geometry/GrQuadUtils.h"
diff --git a/src/gpu/GrSamplerState.h b/src/gpu/GrSamplerState.h
index 848c84c..fc6b728 100644
--- a/src/gpu/GrSamplerState.h
+++ b/src/gpu/GrSamplerState.h
@@ -27,6 +27,9 @@
constexpr GrSamplerState(WrapMode wrapXAndY, Filter filter)
: fWrapModes{wrapXAndY, wrapXAndY}, fFilter(filter) {}
+ constexpr GrSamplerState(WrapMode wrapX, WrapMode wrapY, Filter filter)
+ : fWrapModes{wrapX, wrapY}, fFilter(filter) {}
+
constexpr GrSamplerState(const WrapMode wrapModes[2], Filter filter)
: fWrapModes{wrapModes[0], wrapModes[1]}, fFilter(filter) {}
diff --git a/src/gpu/GrTextureProducer.cpp b/src/gpu/GrTextureProducer.cpp
index 46c6134..243582d 100644
--- a/src/gpu/GrTextureProducer.cpp
+++ b/src/gpu/GrTextureProducer.cpp
@@ -33,15 +33,12 @@
SkRect localRect = inputProxy->getBoundsRect();
- bool needsDomain = false;
bool resizing = false;
if (copyParams.fFilter != GrSamplerState::Filter::kNearest) {
- bool resizing = localRect.width() != dstRect.width() ||
- localRect.height() != dstRect.height();
- needsDomain = resizing && inputProxy->isFunctionallyExact();
+ resizing = localRect.width() != dstRect.width() || localRect.height() != dstRect.height();
}
- if (copyParams.fFilter == GrSamplerState::Filter::kNearest && !needsDomain && !resizing &&
+ if (copyParams.fFilter == GrSamplerState::Filter::kNearest && !resizing &&
dstWillRequireMipMaps) {
sk_sp<GrTextureProxy> proxy = GrCopyBaseMipMapToTextureProxy(context, inputProxy.get(),
colorType);
@@ -57,18 +54,13 @@
return nullptr;
}
+ const auto& caps = *context->priv().caps();
GrPaint paint;
- auto fp = GrTextureEffect::Make(std::move(inputProxy), kUnknown_SkAlphaType, SkMatrix::I(),
- copyParams.fFilter);
- if (needsDomain) {
- const SkRect domain = localRect.makeInset(0.5f, 0.5f);
- // This would cause us to read values from outside the subset. Surely, the caller knows
- // better!
- SkASSERT(copyParams.fFilter != GrSamplerState::Filter::kMipMap);
- fp = GrDomainEffect::Make(std::move(fp), domain, GrTextureDomain::kClamp_Mode,
- copyParams.fFilter);
- }
+ GrSamplerState sampler(GrSamplerState::WrapMode::kClamp, copyParams.fFilter);
+ auto boundsRect = SkIRect::MakeSize(inputProxy->dimensions());
+ auto fp = GrTextureEffect::MakeTexelSubset(std::move(inputProxy), kUnknown_SkAlphaType,
+ SkMatrix::I(), sampler, boundsRect, localRect, caps);
paint.addColorFragmentProcessor(std::move(fp));
paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
@@ -199,21 +191,19 @@
const SkRect& domain,
const GrSamplerState::Filter* filterOrNullForBicubic) {
SkASSERT(kTightCopy_DomainMode != domainMode);
- bool clampToBorderSupport = fContext->priv().caps()->clampToBorderSupport();
+ const auto& caps = *fContext->priv().caps();
SkAlphaType srcAlphaType = this->alphaType();
if (filterOrNullForBicubic) {
- GrSamplerState::WrapMode wrapMode = fDomainNeedsDecal && clampToBorderSupport
+ GrSamplerState::WrapMode wrapMode = fDomainNeedsDecal
? GrSamplerState::WrapMode::kClampToBorder
: GrSamplerState::WrapMode::kClamp;
GrSamplerState samplerState(wrapMode, *filterOrNullForBicubic);
- auto fp =
- GrTextureEffect::Make(std::move(proxy), srcAlphaType, textureMatrix, samplerState);
- if (kDomain_DomainMode == domainMode || (fDomainNeedsDecal && !clampToBorderSupport)) {
- GrTextureDomain::Mode wrapMode = fDomainNeedsDecal ? GrTextureDomain::kDecal_Mode
- : GrTextureDomain::kClamp_Mode;
- return GrDomainEffect::Make(std::move(fp), domain, wrapMode, *filterOrNullForBicubic);
+ if (kNoDomain_DomainMode == domainMode) {
+ return GrTextureEffect::Make(std::move(proxy), srcAlphaType, textureMatrix,
+ samplerState, caps);
}
- return fp;
+ return GrTextureEffect::MakeSubset(std::move(proxy), srcAlphaType, textureMatrix,
+ samplerState, domain, caps);
} else {
static const GrSamplerState::WrapMode kClampClamp[] = {
GrSamplerState::WrapMode::kClamp, GrSamplerState::WrapMode::kClamp};
@@ -221,6 +211,7 @@
GrSamplerState::WrapMode::kClampToBorder, GrSamplerState::WrapMode::kClampToBorder};
static constexpr auto kDir = GrBicubicEffect::Direction::kXY;
+ bool clampToBorderSupport = caps.clampToBorderSupport();
if (kDomain_DomainMode == domainMode || (fDomainNeedsDecal && !clampToBorderSupport)) {
GrTextureDomain::Mode wrapMode = fDomainNeedsDecal ? GrTextureDomain::kDecal_Mode
: GrTextureDomain::kClamp_Mode;
diff --git a/src/gpu/GrYUVProvider.cpp b/src/gpu/GrYUVProvider.cpp
index d974f2e..178554a 100644
--- a/src/gpu/GrYUVProvider.cpp
+++ b/src/gpu/GrYUVProvider.cpp
@@ -168,8 +168,9 @@
}
GrPaint paint;
+ const auto& caps = *ctx->priv().caps();
auto yuvToRgbProcessor = GrYUVtoRGBEffect::Make(yuvTextureProxies, yuvaIndices, yuvColorSpace,
- GrSamplerState::Filter::kNearest);
+ GrSamplerState::Filter::kNearest, caps);
paint.addColorFragmentProcessor(std::move(yuvToRgbProcessor));
// If the caller expects the pixels in a different color space than the one from the image,
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index 6fa5173..ba29ad7 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -43,7 +43,6 @@
#include "src/gpu/GrTracing.h"
#include "src/gpu/SkGr.h"
#include "src/gpu/effects/GrBicubicEffect.h"
-#include "src/gpu/effects/GrTextureDomain.h"
#include "src/gpu/geometry/GrShape.h"
#include "src/gpu/text/GrTextTarget.h"
#include "src/image/SkImage_Base.h"
@@ -936,6 +935,7 @@
// the rest from the SkPaint.
std::unique_ptr<GrFragmentProcessor> fp;
+ const auto& caps = *this->caps();
if (needsTextureDomain && (SkCanvas::kStrict_SrcRectConstraint == constraint)) {
// Use a constrained texture domain to avoid color bleeding
SkRect domain;
@@ -955,9 +955,8 @@
static constexpr auto kDir = GrBicubicEffect::Direction::kXY;
fp = GrBicubicEffect::Make(std::move(proxy), texMatrix, domain, kDir, srcAlphaType);
} else {
- fp = GrTextureEffect::Make(std::move(proxy), srcAlphaType, texMatrix, samplerState);
- fp = GrDomainEffect::Make(std::move(fp), domain, GrTextureDomain::kClamp_Mode,
- samplerState.filter());
+ fp = GrTextureEffect::MakeSubset(std::move(proxy), srcAlphaType, texMatrix,
+ samplerState, domain, caps);
}
} else if (bicubic) {
SkASSERT(GrSamplerState::Filter::kNearest == samplerState.filter());
@@ -965,7 +964,7 @@
static constexpr auto kDir = GrBicubicEffect::Direction::kXY;
fp = GrBicubicEffect::Make(std::move(proxy), texMatrix, wrapMode, kDir, srcAlphaType);
} else {
- fp = GrTextureEffect::Make(std::move(proxy), srcAlphaType, texMatrix, samplerState);
+ fp = GrTextureEffect::Make(std::move(proxy), srcAlphaType, texMatrix, samplerState, caps);
}
fp = GrColorSpaceXformEffect::Make(std::move(fp), bitmap.colorSpace(), bitmap.alphaType(),
diff --git a/src/gpu/effects/GrTextureDomain.cpp b/src/gpu/effects/GrTextureDomain.cpp
index ad629c5..cd15573 100644
--- a/src/gpu/effects/GrTextureDomain.cpp
+++ b/src/gpu/effects/GrTextureDomain.cpp
@@ -319,191 +319,6 @@
///////////////////////////////////////////////////////////////////////////////
-std::unique_ptr<GrFragmentProcessor> GrDomainEffect::Make(std::unique_ptr<GrFragmentProcessor> fp,
- const SkRect& domain,
- GrTextureDomain::Mode mode,
- bool decalIsFiltered) {
- return Make(std::move(fp), domain, mode, mode, decalIsFiltered);
-}
-
-std::unique_ptr<GrFragmentProcessor> GrDomainEffect::Make(std::unique_ptr<GrFragmentProcessor> fp,
- const SkRect& domain,
- GrTextureDomain::Mode modeX,
- GrTextureDomain::Mode modeY,
- bool decalIsFiltered) {
- if (modeX == GrTextureDomain::kIgnore_Mode && modeY == GrTextureDomain::kIgnore_Mode) {
- return fp;
- }
- int count = 0;
- GrCoordTransform* coordTransform = nullptr;
- for (auto [transform, ignored] : GrFragmentProcessor::FPCoordTransformRange(*fp)) {
- ++count;
- coordTransform = &transform;
- }
- // If there are no coord transforms on the passed FP or it's children then there's no need to
- // enforce a domain.
- // We have a limitation that only one coord transform is support when overriding local coords.
- // If that limit were relaxed we would need to add a coord transform for each descendent FP
- // transform and possibly have multiple domain rects to account for different proxy
- // normalization and y-reversals.
- if (count != 1) {
- return fp;
- }
- GrCoordTransform transformCopy = *coordTransform;
- // Reset the child FP's coord transform.
- *coordTransform = {};
- // If both domain modes happen to be ignore, it would be faster to just drop the domain logic
- // entirely and return the original FP. We'd need a GrMatrixProcessor if the matrix is not
- // identity, though.
- return std::unique_ptr<GrFragmentProcessor>(new GrDomainEffect(
- std::move(fp), transformCopy, domain, modeX, modeY, decalIsFiltered));
-}
-
-std::unique_ptr<GrFragmentProcessor> GrDomainEffect::Make(std::unique_ptr<GrFragmentProcessor> fp,
- const SkRect& domain,
- GrTextureDomain::Mode mode,
- GrSamplerState::Filter filter) {
- bool filterIfDecal = filter != GrSamplerState::Filter::kNearest;
- return Make(std::move(fp), domain, mode, filterIfDecal);
-}
-
-std::unique_ptr<GrFragmentProcessor> GrDomainEffect::Make(std::unique_ptr<GrFragmentProcessor> fp,
- const SkRect& domain,
- GrTextureDomain::Mode modeX,
- GrTextureDomain::Mode modeY,
- GrSamplerState::Filter filter) {
- bool filterIfDecal = filter != GrSamplerState::Filter::kNearest;
- return Make(std::move(fp), domain, modeX, modeY, filterIfDecal);
-}
-GrFragmentProcessor::OptimizationFlags GrDomainEffect::Flags(GrFragmentProcessor* fp,
- GrTextureDomain::Mode modeX,
- GrTextureDomain::Mode modeY) {
- auto fpFlags = GrFragmentProcessor::ProcessorOptimizationFlags(fp);
- if (modeX == GrTextureDomain::kDecal_Mode || modeY == GrTextureDomain::kDecal_Mode) {
- return fpFlags & ~kPreservesOpaqueInput_OptimizationFlag;
- }
- return fpFlags;
-}
-
-GrDomainEffect::GrDomainEffect(std::unique_ptr<GrFragmentProcessor> fp,
- const GrCoordTransform& coordTransform,
- const SkRect& domain,
- GrTextureDomain::Mode modeX,
- GrTextureDomain::Mode modeY,
- bool decalIsFiltered)
- : INHERITED(kGrDomainEffect_ClassID, Flags(fp.get(), modeX, modeY))
- , fCoordTransform(coordTransform)
- , fDomain(domain, modeX, modeY)
- , fDecalIsFiltered(decalIsFiltered) {
- SkASSERT(fp);
- fp->setSampledWithExplicitCoords(true);
- this->registerChildProcessor(std::move(fp));
- this->addCoordTransform(&fCoordTransform);
- if (fDomain.modeX() != GrTextureDomain::kDecal_Mode &&
- fDomain.modeY() != GrTextureDomain::kDecal_Mode) {
- // Canonicalize this don't care value so we don't have to worry about it elsewhere.
- fDecalIsFiltered = false;
- }
-}
-
-GrDomainEffect::GrDomainEffect(const GrDomainEffect& that)
- : INHERITED(kGrDomainEffect_ClassID, that.optimizationFlags())
- , fCoordTransform(that.fCoordTransform)
- , fDomain(that.fDomain)
- , fDecalIsFiltered(that.fDecalIsFiltered) {
- auto child = that.childProcessor(0).clone();
- child->setSampledWithExplicitCoords(true);
- this->registerChildProcessor(std::move(child));
- this->addCoordTransform(&fCoordTransform);
-}
-
-void GrDomainEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps,
- GrProcessorKeyBuilder* b) const {
- b->add32(GrTextureDomain::GLDomain::DomainKey(fDomain));
-}
-
-GrGLSLFragmentProcessor* GrDomainEffect::onCreateGLSLInstance() const {
- class GLSLProcessor : public GrGLSLFragmentProcessor {
- public:
- void emitCode(EmitArgs& args) override {
- const GrDomainEffect& de = args.fFp.cast<GrDomainEffect>();
- const GrTextureDomain& domain = de.fDomain;
-
- SkString coords2D =
- args.fFragBuilder->ensureCoords2D(args.fTransformedCoords[0].fVaryingPoint);
-
- fGLDomain.sampleProcessor(domain, args.fInputColor, args.fOutputColor, coords2D, this,
- args, 0);
- }
-
- protected:
- void onSetData(const GrGLSLProgramDataManager& pdman,
- const GrFragmentProcessor& fp) override {
- const GrDomainEffect& de = fp.cast<GrDomainEffect>();
- const GrTextureDomain& domain = de.fDomain;
- // TODO: Update GrCoordTransform to return a view instead of proxy
- const GrSurfaceProxy* proxy = de.fCoordTransform.proxy();
- // If we don't have a proxy the value of the origin doesn't matter
- GrSurfaceOrigin origin = proxy ? proxy->origin() : kTopLeft_GrSurfaceOrigin;
- fGLDomain.setData(pdman, domain, proxy, origin, de.fDecalIsFiltered);
- }
-
- private:
- GrTextureDomain::GLDomain fGLDomain;
- };
-
- return new GLSLProcessor;
-}
-
-bool GrDomainEffect::onIsEqual(const GrFragmentProcessor& sBase) const {
- auto& td = sBase.cast<GrDomainEffect>();
- return fDomain == td.fDomain && fDecalIsFiltered == td.fDecalIsFiltered;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrDomainEffect);
-
-#if GR_TEST_UTILS
-std::unique_ptr<GrFragmentProcessor> GrDomainEffect::TestCreate(GrProcessorTestData* d) {
- do {
- GrTextureDomain::Mode modeX =
- (GrTextureDomain::Mode)d->fRandom->nextULessThan(GrTextureDomain::kModeCount);
- GrTextureDomain::Mode modeY =
- (GrTextureDomain::Mode)d->fRandom->nextULessThan(GrTextureDomain::kModeCount);
- auto child = GrProcessorUnitTest::MakeChildFP(d);
- const auto* childPtr = child.get();
- SkRect domain;
- // We assert if the child's coord transform has a proxy and the domain rect is outside its
- // bounds.
- GrFragmentProcessor::CoordTransformIter ctIter(*child);
- if (!ctIter) {
- continue;
- }
- auto [transform, fp] = *ctIter;
- if (auto proxy = transform.proxy()) {
- auto [w, h] = proxy->backingStoreDimensions();
- domain.fLeft = d->fRandom->nextRangeScalar(0, w);
- domain.fRight = d->fRandom->nextRangeScalar(0, w);
- domain.fTop = d->fRandom->nextRangeScalar(0, h);
- domain.fBottom = d->fRandom->nextRangeScalar(0, h);
- } else {
- domain.fLeft = d->fRandom->nextRangeScalar(-100.f, 100.f);
- domain.fRight = d->fRandom->nextRangeScalar(-100.f, 100.f);
- domain.fTop = d->fRandom->nextRangeScalar(-100.f, 100.f);
- domain.fBottom = d->fRandom->nextRangeScalar(-100.f, 100.f);
- }
- domain.sort();
- bool filterIfDecal = d->fRandom->nextBool();
- auto result = GrDomainEffect::Make(std::move(child), domain, modeX, modeY, filterIfDecal);
- if (result && result.get() != childPtr) {
- return result;
- }
- } while (true);
-}
-#endif
-
-///////////////////////////////////////////////////////////////////////////////
std::unique_ptr<GrFragmentProcessor> GrDeviceSpaceTextureDecalFragmentProcessor::Make(
sk_sp<GrSurfaceProxy> proxy, const SkIRect& subset, const SkIPoint& deviceSpaceOffset) {
return std::unique_ptr<GrFragmentProcessor>(new GrDeviceSpaceTextureDecalFragmentProcessor(
diff --git a/src/gpu/effects/GrTextureDomain.h b/src/gpu/effects/GrTextureDomain.h
index 8e2a6e4..70e6766 100644
--- a/src/gpu/effects/GrTextureDomain.h
+++ b/src/gpu/effects/GrTextureDomain.h
@@ -231,85 +231,6 @@
int fIndex;
};
-/**
- * This effect applies a domain rectangle with an edge "mode" to the result of the child FP's coord
- * transform. Currently the passed FP (including its descendants) must have exactly 1 coord
- * transform (due to internal program builder restrictions). Also, it's important to note that the
- * domain rectangle is applied AFTER the corod transform. This allows us to continue to lift the
- * coord transform to the vertex shader. It might make this nicer for some use cases to add a
- * pre-coord transform option and try to adjust the domain rect internally to convert to
- * post-coord transform and keep everything in the vertex shader for simple use cases.
- */
-class GrDomainEffect : public GrFragmentProcessor {
-public:
- static std::unique_ptr<GrFragmentProcessor> Make(std::unique_ptr<GrFragmentProcessor>,
- const SkRect& domain,
- GrTextureDomain::Mode,
- bool decalIsFiltered);
-
- static std::unique_ptr<GrFragmentProcessor> Make(std::unique_ptr<GrFragmentProcessor>,
- const SkRect& domain,
- GrTextureDomain::Mode modeX,
- GrTextureDomain::Mode modeY,
- bool decalIsFiltered);
-
- // These variants infer decalIsFiltered from the Filter mode (true if not kNearest).
- static std::unique_ptr<GrFragmentProcessor> Make(std::unique_ptr<GrFragmentProcessor>,
- const SkRect& domain,
- GrTextureDomain::Mode,
- GrSamplerState::Filter);
-
- static std::unique_ptr<GrFragmentProcessor> Make(std::unique_ptr<GrFragmentProcessor>,
- const SkRect& domain,
- GrTextureDomain::Mode modeX,
- GrTextureDomain::Mode modeY,
- GrSamplerState::Filter);
-
- const char* name() const override { return "Domain"; }
-
- std::unique_ptr<GrFragmentProcessor> clone() const override {
- return std::unique_ptr<GrFragmentProcessor>(new GrDomainEffect(*this));
- }
-
-#ifdef SK_DEBUG
- SkString dumpInfo() const override {
- SkString str;
- str.appendf("Domain: [L: %.2f, T: %.2f, R: %.2f, B: %.2f], filterDecal: %d",
- fDomain.domain().fLeft, fDomain.domain().fTop, fDomain.domain().fRight,
- fDomain.domain().fBottom, fDecalIsFiltered);
- str.append(INHERITED::dumpInfo());
- return str;
- }
-#endif
-
-private:
- GrFragmentProcessor::OptimizationFlags Flags(GrFragmentProcessor*, GrTextureDomain::Mode,
- GrTextureDomain::Mode);
-
- GrCoordTransform fCoordTransform;
- GrTextureDomain fDomain;
- bool fDecalIsFiltered;
-
- GrDomainEffect(std::unique_ptr<GrFragmentProcessor>,
- const GrCoordTransform& transform,
- const SkRect& domain,
- GrTextureDomain::Mode modeX,
- GrTextureDomain::Mode modeY,
- bool decalIsFiltered);
-
- explicit GrDomainEffect(const GrDomainEffect&);
-
- GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
-
- void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override;
-
- bool onIsEqual(const GrFragmentProcessor&) const override;
-
- GR_DECLARE_FRAGMENT_PROCESSOR_TEST
-
- typedef GrFragmentProcessor INHERITED;
-};
-
class GrDeviceSpaceTextureDecalFragmentProcessor : public GrFragmentProcessor {
public:
static std::unique_ptr<GrFragmentProcessor> Make(sk_sp<GrSurfaceProxy>,
diff --git a/src/gpu/effects/GrTextureEffect.cpp b/src/gpu/effects/GrTextureEffect.cpp
index e964a77..c8a52fb 100644
--- a/src/gpu/effects/GrTextureEffect.cpp
+++ b/src/gpu/effects/GrTextureEffect.cpp
@@ -8,24 +8,233 @@
#include "src/gpu/effects/GrTextureEffect.h"
#include "include/gpu/GrTexture.h"
+#include "src/gpu/GrTexturePriv.h"
#include "src/gpu/glsl/GrGLSLFragmentProcessor.h"
#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
#include "src/gpu/glsl/GrGLSLProgramBuilder.h"
#include "src/sksl/SkSLCPP.h"
#include "src/sksl/SkSLUtil.h"
+namespace {
+struct Span {
+ float fA = 0.f, fB = 0.f;
+
+ Span makeInset(float o) const {
+ Span r = {fA + o, fB - o};
+ if (r.fA > r.fB) {
+ r.fA = r.fB = (r.fA + r.fB) / 2;
+ }
+ return r;
+ }
+
+ bool contains(Span r) const { return fA <= r.fA && fB >= r.fB; }
+};
+} // anonymous namespace
+
+GrTextureEffect::Sampling::Sampling(GrSamplerState sampler, SkISize size, const GrCaps& caps)
+ : fHWSampler(sampler) {
+ if (!caps.clampToBorderSupport()) {
+ if (fHWSampler.wrapModeX() == GrSamplerState::WrapMode::kClampToBorder) {
+ fHWSampler.setWrapModeX(GrSamplerState::WrapMode::kClamp);
+ fShaderModes[0] = ShaderMode::kDecal;
+ Span span{0, (float)size.width()};
+ if (sampler.filter() != GrSamplerState::Filter::kNearest) {
+ span = span.makeInset(0.5f);
+ }
+ fShaderSubset.fLeft = span.fA;
+ fShaderSubset.fRight = span.fB;
+ }
+ if (fHWSampler.wrapModeY() == GrSamplerState::WrapMode::kClampToBorder) {
+ fHWSampler.setWrapModeY(GrSamplerState::WrapMode::kClamp);
+ fShaderModes[1] = ShaderMode::kDecal;
+ Span span{0, (float)size.height()};
+ if (sampler.filter() != GrSamplerState::Filter::kNearest) {
+ span = span.makeInset(0.5f);
+ }
+ fShaderSubset.fTop = span.fA;
+ fShaderSubset.fBottom = span.fB;
+ }
+ }
+}
+
+GrTextureEffect::Sampling::Sampling(const GrSurfaceProxy& proxy,
+ GrSamplerState sampler,
+ const SkRect& subset,
+ bool adjustForFilter,
+ const SkRect* domain,
+ const GrCaps& caps) {
+ using Mode = GrSamplerState::WrapMode;
+ using Filter = GrSamplerState::Filter;
+
+ struct Result1D {
+ ShaderMode fShaderMode;
+ Span fShaderSubset;
+ Mode fHWMode;
+ Filter fFilter;
+ };
+
+ auto resolve = [adjustForFilter, filter = sampler.filter(), &caps](int size, Mode mode,
+ Span subset, Span domain) {
+ float inset;
+ Result1D r;
+ r.fFilter = filter;
+ bool canDoHW = mode != Mode::kClampToBorder || caps.clampToBorderSupport();
+ if (canDoHW && size > 0 && subset.fA <= 0 && subset.fB >= size) {
+ r.fShaderMode = ShaderMode::kNone;
+ r.fHWMode = mode;
+ return r;
+ }
+
+ inset = (adjustForFilter && filter != Filter::kNearest) ? 0.5 : 0;
+ auto insetSubset = subset.makeInset(inset);
+
+ if (canDoHW && insetSubset.contains(domain)) {
+ r.fShaderMode = ShaderMode::kNone;
+ r.fHWMode = mode;
+ return r;
+ }
+
+ if (mode == Mode::kRepeat || mode == Mode::kMirrorRepeat) {
+ r.fFilter = Filter::kNearest;
+ r.fShaderSubset = subset;
+ } else {
+ r.fShaderSubset = insetSubset;
+ }
+ r.fShaderMode = static_cast<ShaderMode>(mode);
+ r.fHWMode = Mode::kClamp;
+ return r;
+ };
+
+ SkISize dim = proxy.isFullyLazy() ? SkISize{-1, -1} : proxy.backingStoreDimensions();
+
+ Span subsetX{subset.fLeft, subset.fRight};
+ auto domainX = domain ? Span{domain->fLeft, domain->fRight}
+ : Span{SK_FloatNegativeInfinity, SK_FloatInfinity};
+ auto x = resolve(dim.width(), sampler.wrapModeX(), subsetX, domainX);
+
+ Span subsetY{subset.fTop, subset.fBottom};
+ auto domainY = domain ? Span{domain->fTop, domain->fBottom}
+ : Span{SK_FloatNegativeInfinity, SK_FloatInfinity};
+ auto y = resolve(dim.height(), sampler.wrapModeY(), subsetY, domainY);
+
+ fHWSampler = {x.fHWMode, y.fHWMode, std::min(x.fFilter, y.fFilter)};
+ fShaderModes[0] = x.fShaderMode;
+ fShaderModes[1] = y.fShaderMode;
+ fShaderSubset = {x.fShaderSubset.fA, y.fShaderSubset.fA,
+ x.fShaderSubset.fB, y.fShaderSubset.fB};
+}
+
+bool GrTextureEffect::Sampling::usesDecal() const {
+ return fShaderModes[0] == ShaderMode::kDecal || fShaderModes[1] == ShaderMode::kDecal ||
+ fHWSampler.wrapModeX() == GrSamplerState::WrapMode::kClampToBorder ||
+ fHWSampler.wrapModeY() == GrSamplerState::WrapMode::kClampToBorder;
+}
+
std::unique_ptr<GrFragmentProcessor> GrTextureEffect::Make(sk_sp<GrSurfaceProxy> proxy,
SkAlphaType alphaType,
const SkMatrix& matrix,
- GrSamplerState sampler) {
+ GrSamplerState::Filter filter) {
return std::unique_ptr<GrFragmentProcessor>(
- new GrTextureEffect(std::move(proxy), alphaType, matrix, sampler));
+ new GrTextureEffect(std::move(proxy), alphaType, matrix, Sampling(filter)));
+}
+
+std::unique_ptr<GrFragmentProcessor> GrTextureEffect::Make(sk_sp<GrSurfaceProxy> proxy,
+ SkAlphaType alphaType,
+ const SkMatrix& matrix,
+ GrSamplerState sampler,
+ const GrCaps& caps) {
+ Sampling sampling(sampler, proxy->dimensions(), caps);
+ return std::unique_ptr<GrFragmentProcessor>(
+ new GrTextureEffect(std::move(proxy), alphaType, matrix, sampling));
+}
+
+std::unique_ptr<GrFragmentProcessor> GrTextureEffect::MakeTexelSubset(sk_sp<GrSurfaceProxy> proxy,
+ SkAlphaType alphaType,
+ const SkMatrix& matrix,
+ GrSamplerState sampler,
+ const SkIRect& subset,
+ const GrCaps& caps) {
+ Sampling sampling(*proxy, sampler, SkRect::Make(subset), true, nullptr, caps);
+ return std::unique_ptr<GrFragmentProcessor>(
+ new GrTextureEffect(std::move(proxy), alphaType, matrix, sampling));
+}
+
+std::unique_ptr<GrFragmentProcessor> GrTextureEffect::MakeTexelSubset(sk_sp<GrSurfaceProxy> proxy,
+ SkAlphaType alphaType,
+ const SkMatrix& matrix,
+ GrSamplerState sampler,
+ const SkIRect& subset,
+ const SkRect& domain,
+ const GrCaps& caps) {
+ Sampling sampling(*proxy, sampler, SkRect::Make(subset), true, &domain, caps);
+ return std::unique_ptr<GrFragmentProcessor>(
+ new GrTextureEffect(std::move(proxy), alphaType, matrix, sampling));
+}
+
+std::unique_ptr<GrFragmentProcessor> GrTextureEffect::MakeSubset(sk_sp<GrSurfaceProxy> proxy,
+ SkAlphaType alphaType,
+ const SkMatrix& matrix,
+ GrSamplerState sampler,
+ const SkRect& subset,
+ const GrCaps& caps) {
+ Sampling sampling(*proxy, sampler, subset, false, nullptr, caps);
+ return std::unique_ptr<GrFragmentProcessor>(
+ new GrTextureEffect(std::move(proxy), alphaType, matrix, sampling));
+}
+
+std::unique_ptr<GrFragmentProcessor> GrTextureEffect::MakeSubset(sk_sp<GrSurfaceProxy> proxy,
+ SkAlphaType alphaType,
+ const SkMatrix& matrix,
+ GrSamplerState sampler,
+ const SkRect& subset,
+ const SkRect& domain,
+ const GrCaps& caps) {
+ Sampling sampling(*proxy, sampler, subset, false, &domain, caps);
+ return std::unique_ptr<GrFragmentProcessor>(
+ new GrTextureEffect(std::move(proxy), alphaType, matrix, sampling));
}
GrGLSLFragmentProcessor* GrTextureEffect::onCreateGLSLInstance() const {
class Impl : public GrGLSLFragmentProcessor {
+ UniformHandle fSubsetUni;
+ UniformHandle fDecalUni;
+
public:
void emitCode(EmitArgs& args) override {
+ auto appendWrap = [](GrGLSLShaderBuilder* builder, ShaderMode mode, const char* inCoord,
+ const char* domainStart, const char* domainEnd, bool is2D,
+ const char* out) {
+ switch (mode) {
+ case ShaderMode::kNone:
+ builder->codeAppendf("%s = %s;\n", out, inCoord);
+ break;
+ case ShaderMode::kDecal:
+ // 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 ShaderMode::kClamp:
+ builder->codeAppendf("%s = clamp(%s, %s, %s);", out, inCoord, domainStart,
+ domainEnd);
+ break;
+ case ShaderMode::kRepeat:
+ builder->codeAppendf("%s = mod(%s - %s, %s - %s) + %s;", out, inCoord,
+ domainStart, domainEnd, domainStart, domainStart);
+ break;
+ case ShaderMode::kMirrorRepeat: {
+ const char* type = is2D ? "float2" : "float";
+ builder->codeAppend("{");
+ builder->codeAppendf("%s w = %s - %s;", type, domainEnd, domainStart);
+ builder->codeAppendf("%s w2 = 2 * w;", type);
+ builder->codeAppendf("%s m = mod(%s - %s, w2);", type, inCoord,
+ domainStart);
+ builder->codeAppendf("%s = mix(m, w2 - m, step(w, m)) + %s;", out,
+ domainStart);
+ builder->codeAppend("}");
+ break;
+ }
+ }
+ };
+ auto te = args.fFp.cast<GrTextureEffect>();
const char* coords;
if (args.fFp.coordTransformsApplyToLocalCoords()) {
coords = args.fTransformedCoords[0].fVaryingPoint.c_str();
@@ -33,30 +242,165 @@
coords = "_coords";
}
auto* fb = args.fFragBuilder;
- fb->codeAppendf("%s = ", args.fOutputColor);
- fb->appendTextureLookupAndBlend(args.fInputColor, SkBlendMode::kModulate,
- args.fTexSamplers[0], coords);
- fb->codeAppendf(";");
+ if (te.fShaderModes[0] == ShaderMode::kNone &&
+ te.fShaderModes[1] == ShaderMode::kNone) {
+ fb->codeAppendf("%s = ", args.fOutputColor);
+ fb->appendTextureLookupAndBlend(args.fInputColor, SkBlendMode::kModulate,
+ args.fTexSamplers[0], coords);
+ fb->codeAppendf(";");
+ } else {
+ const char* subsetName;
+ SkString uniName("TexDom");
+ fSubsetUni = args.fUniformHandler->addUniform(
+ kFragment_GrShaderFlag, kHalf4_GrSLType, "subset", &subsetName);
+
+ // 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
+ auto inCoords = fb->ensureCoords2D(args.fTransformedCoords[0].fVaryingPoint);
+ fb->codeAppend("float2 clampedCoord;");
+ SkString start;
+ SkString end;
+ if (te.fShaderModes[0] == te.fShaderModes[1]) {
+ // Doing the domain setup using vectors seems to avoid shader compilation issues
+ // on Chromecast, possibly due to reducing shader length.
+ start.printf("%s.xy", subsetName);
+ end.printf("%s.zw", subsetName);
+ appendWrap(fb, te.fShaderModes[0], inCoords.c_str(), start.c_str(), end.c_str(),
+ true, "clampedCoord");
+ } else {
+ SkString origX, origY;
+ // Apply x mode to the x coordinate using the left and right edges of the domain
+ // rect (stored as the x and z components of the domain uniform).
+ start.printf("%s.x", subsetName);
+ end.printf("%s.z", subsetName);
+ origX.printf("%s.x", inCoords.c_str());
+ appendWrap(fb, te.fShaderModes[0], origX.c_str(), start.c_str(), end.c_str(),
+ false, "clampedCoord.x");
+ // Repeat the same logic for y.
+ start.printf("%s.y", subsetName);
+ end.printf("%s.w", subsetName);
+ origY.printf("%s.y", inCoords.c_str());
+ appendWrap(fb, te.fShaderModes[1], origY.c_str(), start.c_str(), end.c_str(),
+ false, "clampedCoord.y");
+ }
+ SkString textureLookup;
+ fb->appendTextureLookup(&textureLookup, args.fTexSamplers[0], "clampedCoord");
+ fb->codeAppendf("half4 textureColor = %s;", textureLookup.c_str());
+
+ // Apply decal mode's transparency interpolation if needed
+ bool decalX = te.fShaderModes[0] == ShaderMode::kDecal;
+ bool decalY = te.fShaderModes[1] == ShaderMode::kDecal;
+ if (decalX || decalY) {
+ const char* decalName;
+ // Half3 since this will hold texture width, height, and then a step function
+ // control param
+ fDecalUni = args.fUniformHandler->addUniform(
+ kFragment_GrShaderFlag, kHalf3_GrSLType, uniName.c_str(), &decalName);
+ // The decal err is the max absolute 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) {
+ fb->codeAppendf(
+ "half err = max(half(abs(clampedCoord.x - %s.x) * %s.x), "
+ " half(abs(clampedCoord.y - %s.y) * %s.y));",
+ inCoords.c_str(), decalName, inCoords.c_str(), decalName);
+ } else if (decalX) {
+ fb->codeAppendf("half err = half(abs(clampedCoord.x - %s.x) * %s.x);",
+ inCoords.c_str(), decalName);
+ } else {
+ SkASSERT(decalY);
+ fb->codeAppendf("half err = half(abs(clampedCoord.y - %s.y) * %s.y);",
+ inCoords.c_str(), decalName);
+ }
+
+ // 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 0 so this becomes a step function centered at the
+ // clamped coordinate. When bilerp, fSizeName.z is set to 1 and it becomes
+ // a simple linear blend between texture and transparent.
+ fb->codeAppendf(
+ "if (err > %s.z) { err = 1.0; } else if (%s.z < 1) { err = 0.0; }",
+ decalName, decalName);
+ fb->codeAppend("textureColor = mix(textureColor, half4(0), err);");
+ }
+ fb->codeAppendf("%s = textureColor * %s;", args.fOutputColor, args.fInputColor);
+ }
+ }
+
+ protected:
+ void onSetData(const GrGLSLProgramDataManager& pdm,
+ const GrFragmentProcessor& fp) override {
+ const auto& te = fp.cast<GrTextureEffect>();
+ if (fSubsetUni.isValid()) {
+ const float w = te.fSampler.peekTexture()->width();
+ const float h = te.fSampler.peekTexture()->height();
+
+ const auto& s = te.fSubset;
+ float rect[] = {s.fLeft, s.fTop, s.fRight, s.fBottom};
+ float decalW[3];
+
+ if (te.fSampler.view().origin() == kBottomLeft_GrSurfaceOrigin) {
+ rect[1] = h - rect[1];
+ rect[3] = h - rect[3];
+ std::swap(rect[1], rect[3]);
+ }
+
+ if (te.fSampler.peekTexture()->texturePriv().textureType() !=
+ GrTextureType::kRectangle) {
+ float iw = 1.f / w;
+ float ih = 1.f / h;
+ rect[0] *= iw;
+ rect[2] *= iw;
+ rect[1] *= ih;
+ rect[3] *= ih;
+ decalW[0] = w;
+ decalW[1] = h;
+ } else {
+ decalW[0] = 1;
+ decalW[1] = 1;
+ }
+ pdm.set4fv(fSubsetUni, 1, rect);
+
+ if (fDecalUni.isValid()) {
+ bool filter = te.textureSampler(0).samplerState().filter() !=
+ GrSamplerState::Filter::kNearest;
+ decalW[2] = filter ? 1.f : 0.f;
+ pdm.set3fv(fDecalUni, 1, decalW);
+ }
+ }
}
};
return new Impl;
}
-void GrTextureEffect::onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const {}
+void GrTextureEffect::onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const {
+ bool shaderFilter = (fShaderModes[0] == ShaderMode::kDecal ||
+ fShaderModes[1] == ShaderMode::kDecal) &&
+ fSampler.samplerState().filter() != GrSamplerState::Filter::kNearest;
+ auto m0 = static_cast<uint32_t>(fShaderModes[0]);
+ auto m1 = static_cast<uint32_t>(fShaderModes[1]);
+ b->add32(shaderFilter << 31 | (m0 << 16) | m1);
+}
-bool GrTextureEffect::onIsEqual(const GrFragmentProcessor&) const { return true; }
-
-static inline bool uses_border(const GrSamplerState s) {
- return s.wrapModeX() == GrSamplerState::WrapMode::kClampToBorder ||
- s.wrapModeY() == GrSamplerState::WrapMode::kClampToBorder;
+bool GrTextureEffect::onIsEqual(const GrFragmentProcessor& other) const {
+ auto that = other.cast<GrTextureEffect>();
+ return fShaderModes[0] == that.fShaderModes[1] && fShaderModes[1] == that.fShaderModes[1] &&
+ fSubset == that.fSubset;
}
GrTextureEffect::GrTextureEffect(sk_sp<GrSurfaceProxy> texture, SkAlphaType alphaType,
- const SkMatrix& matrix, GrSamplerState sampler)
+ const SkMatrix& matrix, const Sampling& sampling)
: GrFragmentProcessor(kGrTextureEffect_ClassID,
- ModulateForSamplerOptFlags(alphaType, uses_border(sampler)))
+ ModulateForSamplerOptFlags(alphaType, sampling.usesDecal()))
, fCoordTransform(matrix, texture.get())
- , fSampler(std::move(texture), sampler) {
+ , fSampler(std::move(texture), sampling.fHWSampler)
+ , fSubset(sampling.fShaderSubset)
+ , fShaderModes{sampling.fShaderModes[0], sampling.fShaderModes[1]} {
+ // We always compare the range even when it isn't used so assert we have canonical don't care
+ // values.
+ SkASSERT(fShaderModes[0] != ShaderMode::kNone || (fSubset.fLeft == 0 && fSubset.fRight == 0));
+ SkASSERT(fShaderModes[1] != ShaderMode::kNone || (fSubset.fTop == 0 && fSubset.fBottom == 0));
this->setTextureSamplerCnt(1);
this->addCoordTransform(&fCoordTransform);
}
@@ -64,7 +408,9 @@
GrTextureEffect::GrTextureEffect(const GrTextureEffect& src)
: INHERITED(kGrTextureEffect_ClassID, src.optimizationFlags())
, fCoordTransform(src.fCoordTransform)
- , fSampler(src.fSampler) {
+ , fSampler(src.fSampler)
+ , fSubset(src.fSubset)
+ , fShaderModes{src.fShaderModes[0], src.fShaderModes[1]} {
this->setTextureSamplerCnt(1);
this->addCoordTransform(&fCoordTransform);
}
@@ -95,6 +441,6 @@
: GrSamplerState::Filter::kNearest);
const SkMatrix& matrix = GrTest::TestMatrix(testData->fRandom);
- return GrTextureEffect::Make(std::move(proxy), at, matrix, params);
+ return GrTextureEffect::Make(std::move(proxy), at, matrix, params, *testData->caps());
}
#endif
diff --git a/src/gpu/effects/GrTextureEffect.h b/src/gpu/effects/GrTextureEffect.h
index c6fc305..9e62a48 100644
--- a/src/gpu/effects/GrTextureEffect.h
+++ b/src/gpu/effects/GrTextureEffect.h
@@ -15,23 +15,117 @@
class GrTextureEffect : public GrFragmentProcessor {
public:
+ /** Make from a filter. The sampler will be configured with clamp mode. */
+ static std::unique_ptr<GrFragmentProcessor> Make(
+ sk_sp<GrSurfaceProxy>,
+ SkAlphaType,
+ const SkMatrix& = SkMatrix::I(),
+ GrSamplerState::Filter = GrSamplerState::Filter::kNearest);
+
+ /**
+ * Make from a full GrSamplerState. Caps are required to determine support for kClampToBorder.
+ * This will be emulated in the shader if there is no hardware support.
+ */
static std::unique_ptr<GrFragmentProcessor> Make(sk_sp<GrSurfaceProxy>,
SkAlphaType,
- const SkMatrix& = SkMatrix::I(),
- GrSamplerState = {});
+ const SkMatrix&,
+ GrSamplerState,
+ const GrCaps& caps);
+
+ /**
+ * Makes a texture effect that samples a subset of a texture. The wrap modes of the
+ * GrSampleState are applied to the subset in the shader rather than using HW samplers.
+ * The 'subset' parameter specifies the texels in the base level. The shader code will
+ * avoid allowing bilerp filtering to read outside the texel window. However, if MIP
+ * filtering is used and a shader invocation reads from a level other than the base
+ * then it may read texel values that were computed from in part from base level texels
+ * outside the window. More specifically, we treat the MIP map case exactly like the
+ * bilerp case in terms of how the final texture coords are computed.
+ */
+ static std::unique_ptr<GrFragmentProcessor> MakeTexelSubset(sk_sp<GrSurfaceProxy>,
+ SkAlphaType,
+ const SkMatrix&,
+ GrSamplerState,
+ const SkIRect& subset,
+ const GrCaps& caps);
+ /**
+ * The same as above but also takes a 'domain' that specifies any known limit on the post-
+ * matrix texture coords that will be used to sample the texture. Specifying this requires
+ * knowledge of how this effect will be nested into a paint, the local coords used with the
+ * draw, etc. It is only used to attempt to optimize away the shader subset calculations.
+ */
+ static std::unique_ptr<GrFragmentProcessor> MakeTexelSubset(sk_sp<GrSurfaceProxy>,
+ SkAlphaType,
+ const SkMatrix&,
+ GrSamplerState,
+ const SkIRect& subset,
+ const SkRect& domain,
+ const GrCaps& caps);
+
+ /**
+ * This is similar to MakeTexelSubset but takes a float rather than integer subset rect.
+ * No adjustment is done for filtering, the texture coordinates are limited to the
+ * unmodified subset. The subset should be unnormalized. The effect will apply texture
+ * coordinate normalization after subset restriction (logically).
+ */
+ static std::unique_ptr<GrFragmentProcessor> MakeSubset(sk_sp<GrSurfaceProxy>,
+ SkAlphaType,
+ const SkMatrix&,
+ GrSamplerState,
+ const SkRect& subset,
+ const GrCaps& caps);
+
+ /**
+ * The same as above but also takes a 'domain' that specifies any known limit on the post-
+ * matrix texture coords that will be used to sample the texture. Specifying this requires
+ * knowledge of how this effect will be nested into a paint, the local coords used with the
+ * draw, etc. It is only used to attempt to optimize away the shader subset calculations.
+ */
+ static std::unique_ptr<GrFragmentProcessor> MakeSubset(sk_sp<GrSurfaceProxy>,
+ SkAlphaType,
+ const SkMatrix&,
+ GrSamplerState,
+ const SkRect& subset,
+ const SkRect& domain,
+ const GrCaps& caps);
std::unique_ptr<GrFragmentProcessor> clone() const override;
- const char* name() const override { return "SimpleTextureEffect"; }
+ const char* name() const override { return "TextureEffect"; }
private:
+ enum class ShaderMode : uint16_t {
+ kClamp = static_cast<int>(GrSamplerState::WrapMode::kClamp),
+ kRepeat = static_cast<int>(GrSamplerState::WrapMode::kRepeat),
+ kMirrorRepeat = static_cast<int>(GrSamplerState::WrapMode::kMirrorRepeat),
+ kDecal = static_cast<int>(GrSamplerState::WrapMode::kClampToBorder),
+ kNone,
+ };
+
+ struct Sampling {
+ GrSamplerState fHWSampler;
+ ShaderMode fShaderModes[2] = {ShaderMode::kNone, ShaderMode::kNone};
+ SkRect fShaderSubset = {0, 0, 0, 0};
+ Sampling(GrSamplerState::Filter filter) : fHWSampler(filter) {}
+ Sampling(GrSamplerState, SkISize, const GrCaps&);
+ Sampling(const GrSurfaceProxy& proxy,
+ GrSamplerState sampler,
+ const SkRect&,
+ bool,
+ const SkRect*,
+ const GrCaps&);
+ inline bool usesDecal() const;
+ };
+
GrCoordTransform fCoordTransform;
TextureSampler fSampler;
+ SkRect fSubset;
+ ShaderMode fShaderModes[2];
+
+ inline GrTextureEffect(sk_sp<GrSurfaceProxy>, SkAlphaType, const SkMatrix&, const Sampling&);
GrTextureEffect(const GrTextureEffect& src);
- inline GrTextureEffect(sk_sp<GrSurfaceProxy>, SkAlphaType, const SkMatrix&, GrSamplerState);
-
GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override;
diff --git a/src/gpu/effects/GrYUVtoRGBEffect.cpp b/src/gpu/effects/GrYUVtoRGBEffect.cpp
index 987db7e..a85b810 100644
--- a/src/gpu/effects/GrYUVtoRGBEffect.cpp
+++ b/src/gpu/effects/GrYUVtoRGBEffect.cpp
@@ -19,6 +19,7 @@
const SkYUVAIndex yuvaIndices[4],
SkYUVColorSpace yuvColorSpace,
GrSamplerState::Filter filterMode,
+ const GrCaps& caps,
const SkMatrix& localMatrix,
const SkRect* domain) {
int numPlanes;
@@ -63,16 +64,18 @@
} else if (domain) {
planeDomain = *domain;
}
- planeFPs[i] =
- GrTextureEffect::Make(proxies[i], kUnknown_SkAlphaType, *planeMatrix, planeFilter);
+
if (domain) {
SkASSERT(planeFilter != GrSamplerState::Filter::kMipMap);
if (planeFilter != GrSamplerState::Filter::kNearest) {
// Inset by half a pixel for bilerp, after scaling to the size of the plane
planeDomain.inset(0.5f, 0.5f);
}
- planeFPs[i] = GrDomainEffect::Make(std::move(planeFPs[i]), planeDomain,
- GrTextureDomain::kClamp_Mode, false);
+ planeFPs[i] = GrTextureEffect::MakeSubset(proxies[i], kUnknown_SkAlphaType,
+ *planeMatrix, planeFilter, planeDomain, caps);
+ } else {
+ planeFPs[i] = GrTextureEffect::Make(proxies[i], kUnknown_SkAlphaType, *planeMatrix,
+ planeFilter);
}
}
@@ -187,7 +190,6 @@
UniformHandle fColorSpaceMatrixVar;
UniformHandle fColorSpaceTranslateVar;
- GrTextureDomain::GLDomain fGLDomains[4];
};
return new GrGLSLYUVtoRGBEffect;
diff --git a/src/gpu/effects/GrYUVtoRGBEffect.h b/src/gpu/effects/GrYUVtoRGBEffect.h
index b99de32..6ed3b63 100644
--- a/src/gpu/effects/GrYUVtoRGBEffect.h
+++ b/src/gpu/effects/GrYUVtoRGBEffect.h
@@ -10,11 +10,9 @@
#include "include/core/SkTypes.h"
+#include "include/core/SkYUVAIndex.h"
#include "src/gpu/GrCoordTransform.h"
#include "src/gpu/GrFragmentProcessor.h"
-#include "src/gpu/effects/GrTextureDomain.h"
-
-#include "include/core/SkYUVAIndex.h"
class GrYUVtoRGBEffect : public GrFragmentProcessor {
public:
@@ -26,6 +24,7 @@
const SkYUVAIndex indices[4],
SkYUVColorSpace yuvColorSpace,
GrSamplerState::Filter filterMode,
+ const GrCaps&,
const SkMatrix& localMatrix = SkMatrix::I(),
const SkRect* domain = nullptr);
#ifdef SK_DEBUG
diff --git a/src/gpu/ops/GrTextureOp.cpp b/src/gpu/ops/GrTextureOp.cpp
index 3b7fafc..2eb257c 100644
--- a/src/gpu/ops/GrTextureOp.cpp
+++ b/src/gpu/ops/GrTextureOp.cpp
@@ -30,7 +30,6 @@
#include "src/gpu/GrTexturePriv.h"
#include "src/gpu/GrTextureProxy.h"
#include "src/gpu/SkGr.h"
-#include "src/gpu/effects/GrTextureDomain.h"
#include "src/gpu/effects/generated/GrClampFragmentProcessor.h"
#include "src/gpu/geometry/GrQuad.h"
#include "src/gpu/geometry/GrQuadBuffer.h"
@@ -1072,13 +1071,21 @@
GrSurfaceProxy* proxy = proxyView.proxy();
std::unique_ptr<GrFragmentProcessor> fp;
- fp = GrTextureEffect::Make(sk_ref_sp(proxy), alphaType, SkMatrix::I(), filter);
if (domain) {
// Update domain to match what GrTextureOp would do for bilerp, but don't do any
- // normalization since GrTextureDomainEffect handles that and the origin.
+ // normalization since GrTextureEffect handles that and the origin.
SkRect correctedDomain = normalize_domain(filter, {1.f, 1.f, 0.f}, domain);
- fp = GrDomainEffect::Make(std::move(fp), correctedDomain, GrTextureDomain::kClamp_Mode,
- filter);
+ const auto& caps = *context->priv().caps();
+ SkRect localRect;
+ if (localQuad.asRect(&localRect)) {
+ fp = GrTextureEffect::MakeSubset(sk_ref_sp(proxy), alphaType, SkMatrix::I(), filter,
+ correctedDomain, localRect, caps);
+ } else {
+ fp = GrTextureEffect::MakeSubset(sk_ref_sp(proxy), alphaType, SkMatrix::I(), filter,
+ correctedDomain, caps);
+ }
+ } else {
+ fp = GrTextureEffect::Make(sk_ref_sp(proxy), alphaType, SkMatrix::I(), filter);
}
fp = GrColorSpaceXformEffect::Make(std::move(fp), std::move(textureXform));
paint.addColorFragmentProcessor(std::move(fp));
diff --git a/src/image/SkImage_GpuBase.cpp b/src/image/SkImage_GpuBase.cpp
index 44a0275..6a21b93 100644
--- a/src/image/SkImage_GpuBase.cpp
+++ b/src/image/SkImage_GpuBase.cpp
@@ -348,8 +348,9 @@
GrPaint paint;
paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
+ const auto& caps = *ctx->priv().caps();
auto fp = GrYUVtoRGBEffect::Make(proxies, yuvaIndices, yuvColorSpace,
- GrSamplerState::Filter::kNearest);
+ GrSamplerState::Filter::kNearest, caps);
if (colorSpaceXform) {
fp = GrColorSpaceXformEffect::Make(std::move(fp), std::move(colorSpaceXform));
}
diff --git a/src/shaders/SkImageShader.cpp b/src/shaders/SkImageShader.cpp
index a88c349..2e3bba5 100755
--- a/src/shaders/SkImageShader.cpp
+++ b/src/shaders/SkImageShader.cpp
@@ -204,22 +204,6 @@
GrSamplerState::WrapMode wrapModes[] = {tile_mode_to_wrap_mode(fTileModeX),
tile_mode_to_wrap_mode(fTileModeY)};
- // 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->priv().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;
- }
- }
-
// Must set wrap and filter on the sampler before requesting a texture. In two places below
// we check the matrix scale factors to determine how to interpret the filter quality setting.
// This completely ignores the complexity of the drawVertices case where explicit local coords
@@ -240,22 +224,31 @@
lmInverse.postScale(scaleAdjust[0], scaleAdjust[1]);
+ const auto& caps = *args.fContext->priv().caps();
+
std::unique_ptr<GrFragmentProcessor> inner;
if (doBicubic) {
- // 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
+ // 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 (!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;
+ }
+ }
static constexpr auto kDir = GrBicubicEffect::Direction::kXY;
inner = GrBicubicEffect::Make(std::move(proxy), lmInverse, wrapModes, domainX, domainY,
kDir, srcAlphaType);
} else {
- auto dimensions = proxy->dimensions();
- inner = GrTextureEffect::Make(std::move(proxy), srcAlphaType, lmInverse, samplerState);
- if (domainX != GrTextureDomain::kIgnore_Mode || domainY != GrTextureDomain::kIgnore_Mode) {
- SkRect domain = GrTextureDomain::MakeTexelDomain(SkIRect::MakeSize(dimensions),
- domainX, domainY);
- inner = GrDomainEffect::Make(std::move(inner), domain, domainX, domainY,
- samplerState.filter());
- }
+ inner = GrTextureEffect::Make(std::move(proxy), srcAlphaType, lmInverse, samplerState,
+ caps);
}
inner = GrColorSpaceXformEffect::Make(std::move(inner), fImage->colorSpace(), srcAlphaType,
args.fDstColorInfo->colorSpace());