GrMatrixEffect applies coord normalization/flip for GrTextureEffect.
It checks if its child is a GrTextureEffect and if so the child supplies
a matrix to concat.
Additionally, GrTextureEffect shader modes that operate on unnormalized
texture coords no longer receive prenormalized coords that must be
unnormalized.
Hoping this addresses (maybe partially) this regression:
https://perf.skia.org/e/?begin=1618332600&end=1618386249&keys=Xdf47e259cd874b84b2e0c31c9465abe8&num_commits=50&request_type=1&xbaroffset=54589
Bug: skia:11844
Change-Id: I97eeb88440d5d81acb3edd8c406b17680da67438
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/397218
Reviewed-by: Robert Phillips <robertphillips@google.com>
Commit-Queue: Brian Salomon <bsalomon@google.com>
diff --git a/src/gpu/effects/GrMatrixEffect.cpp b/src/gpu/effects/GrMatrixEffect.cpp
index 2590e97..19d8609 100644
--- a/src/gpu/effects/GrMatrixEffect.cpp
+++ b/src/gpu/effects/GrMatrixEffect.cpp
@@ -29,7 +29,13 @@
void onSetData(const GrGLSLProgramDataManager& pdman,
const GrFragmentProcessor& proc) override {
const GrMatrixEffect& mtx = proc.cast<GrMatrixEffect>();
- pdman.setSkMatrix(fMatrixVar, mtx.matrix());
+ if (auto te = mtx.childProcessor(0)->asTextureEffect()) {
+ SkMatrix m = te->coordAdjustmentMatrix();
+ m.preConcat(mtx.matrix());
+ pdman.setSkMatrix(fMatrixVar, m);
+ } else {
+ pdman.setSkMatrix(fMatrixVar, mtx.matrix());
+ }
}
UniformHandle fMatrixVar;
diff --git a/src/gpu/effects/GrTextureEffect.cpp b/src/gpu/effects/GrTextureEffect.cpp
index 8d8ac97..a9fe6e1 100644
--- a/src/gpu/effects/GrTextureEffect.cpp
+++ b/src/gpu/effects/GrTextureEffect.cpp
@@ -150,61 +150,16 @@
return false;
}
-static void get_matrix(const SkMatrix& preMatrix, const GrSurfaceProxyView& view,
- SkMatrix* outMatrix, bool* outLazyProxyNormalization) {
- SkMatrix combined = preMatrix;
- bool canNormalize = view.proxy()->backendFormat().textureType() != GrTextureType::kRectangle;
- if (canNormalize) {
- if (view.proxy()->isFullyLazy()) {
- *outLazyProxyNormalization = true;
- } else {
- SkMatrixPriv::PostIDiv(&combined, view.proxy()->backingStoreDimensions().fWidth,
- view.proxy()->backingStoreDimensions().fHeight);
- *outLazyProxyNormalization = false;
- }
- } else {
- *outLazyProxyNormalization = false;
- }
- if (view.origin() == kBottomLeft_GrSurfaceOrigin) {
- if (canNormalize) {
- if (!view.proxy()->isFullyLazy()) {
- // combined.postScale(1,-1);
- // combined.postTranslate(0,1);
- combined.set(SkMatrix::kMSkewY,
- combined[SkMatrix::kMPersp0] - combined[SkMatrix::kMSkewY]);
- combined.set(SkMatrix::kMScaleY,
- combined[SkMatrix::kMPersp1] - combined[SkMatrix::kMScaleY]);
- combined.set(SkMatrix::kMTransY,
- combined[SkMatrix::kMPersp2] - combined[SkMatrix::kMTransY]);
- }
- } else {
- // combined.postScale(1, -1);
- // combined.postTranslate(0,1);
- SkScalar h = view.proxy()->backingStoreDimensions().fHeight;
- combined.set(SkMatrix::kMSkewY,
- h * combined[SkMatrix::kMPersp0] - combined[SkMatrix::kMSkewY]);
- combined.set(SkMatrix::kMScaleY,
- h * combined[SkMatrix::kMPersp1] - combined[SkMatrix::kMScaleY]);
- combined.set(SkMatrix::kMTransY,
- h * combined[SkMatrix::kMPersp2] - combined[SkMatrix::kMTransY]);
- }
- }
- *outMatrix = combined;
-}
-
std::unique_ptr<GrFragmentProcessor> GrTextureEffect::Make(GrSurfaceProxyView view,
SkAlphaType alphaType,
const SkMatrix& matrix,
Filter filter,
MipmapMode mm) {
- SkMatrix final;
- bool lazyProxyNormalization;
- get_matrix(matrix, view, &final, &lazyProxyNormalization);
- return GrMatrixEffect::Make(final, std::unique_ptr<GrFragmentProcessor>(
- new GrTextureEffect(std::move(view),
- alphaType,
- Sampling(filter, mm),
- lazyProxyNormalization)));
+ Sampling sampling = Sampling(filter, mm);
+ std::unique_ptr<GrFragmentProcessor> te(new GrTextureEffect(std::move(view),
+ alphaType,
+ sampling));
+ return GrMatrixEffect::Make(matrix, std::move(te));
}
std::unique_ptr<GrFragmentProcessor> GrTextureEffect::Make(GrSurfaceProxyView view,
@@ -213,16 +168,16 @@
GrSamplerState sampler,
const GrCaps& caps,
const float border[4]) {
- Sampling sampling(*view.proxy(), sampler, SkRect::Make(view.proxy()->dimensions()), nullptr,
- border, caps);
- SkMatrix final;
- bool lazyProxyNormalization;
- get_matrix(matrix, view, &final, &lazyProxyNormalization);
- return GrMatrixEffect::Make(final, std::unique_ptr<GrFragmentProcessor>(
- new GrTextureEffect(std::move(view),
- alphaType,
- sampling,
- lazyProxyNormalization)));
+ Sampling sampling(*view.proxy(),
+ sampler,
+ SkRect::Make(view.proxy()->dimensions()),
+ nullptr,
+ border,
+ caps);
+ std::unique_ptr<GrFragmentProcessor> te(new GrTextureEffect(std::move(view),
+ alphaType,
+ sampling));
+ return GrMatrixEffect::Make(matrix, std::move(te));
}
std::unique_ptr<GrFragmentProcessor> GrTextureEffect::MakeSubset(GrSurfaceProxyView view,
@@ -233,14 +188,10 @@
const GrCaps& caps,
const float border[4]) {
Sampling sampling(*view.proxy(), sampler, subset, nullptr, border, caps);
- SkMatrix final;
- bool lazyProxyNormalization;
- get_matrix(matrix, view, &final, &lazyProxyNormalization);
- return GrMatrixEffect::Make(final, std::unique_ptr<GrFragmentProcessor>(
- new GrTextureEffect(std::move(view),
- alphaType,
- sampling,
- lazyProxyNormalization)));
+ std::unique_ptr<GrFragmentProcessor> te(new GrTextureEffect(std::move(view),
+ alphaType,
+ sampling));
+ return GrMatrixEffect::Make(matrix, std::move(te));
}
std::unique_ptr<GrFragmentProcessor> GrTextureEffect::MakeSubset(GrSurfaceProxyView view,
@@ -252,14 +203,10 @@
const GrCaps& caps,
const float border[4]) {
Sampling sampling(*view.proxy(), sampler, subset, &domain, border, caps);
- SkMatrix final;
- bool lazyProxyNormalization;
- get_matrix(matrix, view, &final, &lazyProxyNormalization);
- return GrMatrixEffect::Make(final, std::unique_ptr<GrFragmentProcessor>(
- new GrTextureEffect(std::move(view),
- alphaType,
- sampling,
- lazyProxyNormalization)));
+ std::unique_ptr<GrFragmentProcessor> te(new GrTextureEffect(std::move(view),
+ alphaType,
+ sampling));
+ return GrMatrixEffect::Make(matrix, std::move(te));
}
std::unique_ptr<GrFragmentProcessor> GrTextureEffect::MakeCustomLinearFilterInset(
@@ -275,12 +222,28 @@
const float border[4]) {
GrSamplerState sampler(wx, wy, Filter::kLinear);
Sampling sampling(*view.proxy(), sampler, subset, domain, border, caps, inset);
- SkMatrix final;
- bool lazyProxyNormalization;
- get_matrix(matrix, view, &final, &lazyProxyNormalization);
- return GrMatrixEffect::Make(
- final, std::unique_ptr<GrFragmentProcessor>(new GrTextureEffect(
- std::move(view), alphaType, sampling, lazyProxyNormalization)));
+ std::unique_ptr<GrFragmentProcessor> te(new GrTextureEffect(std::move(view),
+ alphaType,
+ sampling));
+ return GrMatrixEffect::Make(matrix, std::move(te));
+}
+
+SkMatrix GrTextureEffect::coordAdjustmentMatrix() const {
+ SkMatrix m;
+ GrTexture* texture = this->texture();
+ SkISize d = texture->dimensions();
+ if (this->matrixEffectShouldNormalize()) {
+ if (fView.origin() == kBottomLeft_GrSurfaceOrigin) {
+ m.setScaleTranslate(1.f / d.width(), -1.f / d.height(), 0, 1);
+ } else {
+ m.setScale(1.f / d.width(), 1.f / d.height());
+ }
+ } else {
+ if (fView.origin() == kBottomLeft_GrSurfaceOrigin) {
+ m.setScaleTranslate(1.f, -1.f, 0, d.height());
+ }
+ }
+ return m;
}
GrTextureEffect::ShaderMode GrTextureEffect::GetShaderMode(Wrap wrap,
@@ -319,6 +282,21 @@
return m == ShaderMode::kClampToBorder_Nearest || m == ShaderMode::kClampToBorder_Filter;
}
+bool GrTextureEffect::ShaderModeRequiresUnormCoord(ShaderMode m) {
+ switch (m) {
+ case ShaderMode::kNone: return false;
+ case ShaderMode::kClamp: return false;
+ case ShaderMode::kRepeat_Nearest_None: return false;
+ case ShaderMode::kRepeat_Linear_None: return true;
+ case ShaderMode::kRepeat_Nearest_Mipmap: return true;
+ case ShaderMode::kRepeat_Linear_Mipmap: return true;
+ case ShaderMode::kMirrorRepeat: return false;
+ case ShaderMode::kClampToBorder_Nearest: return true;
+ case ShaderMode::kClampToBorder_Filter: return true;
+ }
+ SkUNREACHABLE;
+};
+
void GrTextureEffect::Impl::emitCode(EmitArgs& args) {
using ShaderMode = GrTextureEffect::ShaderMode;
@@ -328,15 +306,7 @@
if (te.fShaderModes[0] == ShaderMode::kNone &&
te.fShaderModes[1] == ShaderMode::kNone) {
fb->codeAppendf("return ");
- if (te.fLazyProxyNormalization) {
- const char* norm = nullptr;
- fNormUni = args.fUniformHandler->addUniform(&te, kFragment_GrShaderFlag,
- kFloat4_GrSLType, "norm", &norm);
- SkString coordString = SkStringPrintf("%s * %s.zw", args.fSampleCoord, norm);
- fb->appendTextureLookup(fSamplerHandle, coordString.c_str());
- } else {
- fb->appendTextureLookup(fSamplerHandle, args.fSampleCoord);
- }
+ fb->appendTextureLookup(fSamplerHandle, args.fSampleCoord);
fb->codeAppendf(";");
} else {
// Here is the basic flow of the various ShaderModes are implemented in a series of
@@ -361,8 +331,6 @@
fb->codeAppendf("float2 inCoord = %s;", args.fSampleCoord);
const auto& m = te.fShaderModes;
- GrTextureType textureType = te.view().proxy()->backendFormat().textureType();
- bool canNormalize = textureType != GrTextureType::kRectangle;
const char* borderName = nullptr;
if (te.hasClampToBorderShaderMode()) {
@@ -399,24 +367,6 @@
SkUNREACHABLE;
};
- // To keep things a little simpler, when we have filtering logic in the shader we
- // operate on unnormalized texture coordinates. We will add a uniform that stores
- // {w, h, 1/w, 1/h} in a float4 below.
- auto modeRequiresUnormCoords = [](ShaderMode m) {
- switch (m) {
- case ShaderMode::kNone: return false;
- case ShaderMode::kClamp: return false;
- case ShaderMode::kRepeat_Nearest_None: return false;
- case ShaderMode::kRepeat_Linear_None: return true;
- case ShaderMode::kRepeat_Nearest_Mipmap: return true;
- case ShaderMode::kRepeat_Linear_Mipmap: return true;
- case ShaderMode::kMirrorRepeat: return false;
- case ShaderMode::kClampToBorder_Nearest: return true;
- case ShaderMode::kClampToBorder_Filter: return true;
- }
- SkUNREACHABLE;
- };
-
bool useSubset[2] = {modeUsesSubset(m[0]), modeUsesSubset(m[1])};
bool useClamp [2] = {modeUsesClamp (m[0]), modeUsesClamp (m[1])};
@@ -432,30 +382,28 @@
&te, kFragment_GrShaderFlag, kFloat4_GrSLType, "clamp", &clampName);
}
- bool unormCoordsRequired = modeRequiresUnormCoords(m[0]) || modeRequiresUnormCoords(m[1]);
+ bool unormCoordsRequiredForShaderMode = ShaderModeRequiresUnormCoord(m[0]) ||
+ ShaderModeRequiresUnormCoord(m[1]);
+ // We should not pre-normalize the input coords with GrMatrixEffect if we're going to
+ // operate on unnormalized coords and then normalize after the shader mode.
+ SkASSERT(!(unormCoordsRequiredForShaderMode && te.matrixEffectShouldNormalize()));
+ bool sampleCoordsMustBeNormalized =
+ te.fView.asTextureProxy()->textureType() != GrTextureType::kRectangle;
- const char* norm = nullptr;
- if (canNormalize && (unormCoordsRequired || te.fLazyProxyNormalization)) {
+ const char* idims = nullptr;
+ if (unormCoordsRequiredForShaderMode && sampleCoordsMustBeNormalized) {
// TODO: Detect support for textureSize() or polyfill textureSize() in SkSL and
// always use?
- fNormUni = args.fUniformHandler->addUniform(&te, kFragment_GrShaderFlag,
- kFloat4_GrSLType, "norm", &norm);
-
- if (!te.fLazyProxyNormalization) {
- // TODO: Remove the normalization from the CoordTransform to skip unnormalizing
- // step here.
- fb->codeAppendf("inCoord *= %s.xy;", norm);
- } else if (te.view().origin() == kBottomLeft_GrSurfaceOrigin) {
- fb->codeAppendf("inCoord.y = %s.y - inCoord.y;", norm);
- }
+ fIDimsUni = args.fUniformHandler->addUniform(&te, kFragment_GrShaderFlag,
+ kFloat2_GrSLType, "idims", &idims);
}
// Generates a string to read at a coordinate, normalizing coords if necessary.
auto read = [&](const char* coord) {
SkString result;
SkString normCoord;
- if (norm) {
- normCoord.printf("(%s) * %s.zw", coord, norm);
+ if (idims) {
+ normCoord.printf("(%s) * %s", coord, idims);
} else {
normCoord = coord;
}
@@ -724,10 +672,10 @@
auto type = te.texture()->textureType();
- float norm[4] = {w, h, 1.f/w, 1.f/h};
+ float idims[2] = {1.f/w, 1.f/h};
- if (fNormUni.isValid()) {
- pdm.set4fv(fNormUni, 1, norm);
+ if (fIDimsUni.isValid()) {
+ pdm.set2fv(fIDimsUni, 1, idims);
SkASSERT(type != GrTextureType::kRectangle);
}
@@ -737,11 +685,11 @@
rect[3] = h - rect[3];
std::swap(rect[1], rect[3]);
}
- if (!fNormUni.isValid() && type != GrTextureType::kRectangle) {
- rect[0] *= norm[2];
- rect[2] *= norm[2];
- rect[1] *= norm[3];
- rect[3] *= norm[3];
+ if (!fIDimsUni.isValid() && type != GrTextureType::kRectangle) {
+ rect[0] *= idims[0];
+ rect[2] *= idims[0];
+ rect[1] *= idims[1];
+ rect[3] *= idims[1];
}
pdm.set4fv(uni, 1, rect);
};
@@ -769,13 +717,6 @@
auto m1 = static_cast<uint32_t>(fShaderModes[1]);
b->addBits(8, m1, "shaderMode1");
-
- // The origin only affects the shader code when we're doing last minute normalization
- // for lazy proxies.
- b->addBool(fLazyProxyNormalization, "normalization");
- if (fLazyProxyNormalization) {
- b->addBits(1, this->view().origin(), "origin");
- }
}
bool GrTextureEffect::onIsEqual(const GrFragmentProcessor& other) const {
@@ -798,18 +739,22 @@
return true;
}
+bool GrTextureEffect::matrixEffectShouldNormalize() const {
+ return fView.asTextureProxy()->textureType() != GrTextureType::kRectangle &&
+ !ShaderModeRequiresUnormCoord(fShaderModes[0]) &&
+ !ShaderModeRequiresUnormCoord(fShaderModes[1]);
+}
+
GrTextureEffect::GrTextureEffect(GrSurfaceProxyView view,
SkAlphaType alphaType,
- const Sampling& sampling,
- bool lazyProxyNormalization)
+ const Sampling& sampling)
: GrFragmentProcessor(kGrTextureEffect_ClassID,
ModulateForSamplerOptFlags(alphaType, sampling.hasBorderAlpha()))
, fView(std::move(view))
, fSamplerState(sampling.fHWSampler)
, fSubset(sampling.fShaderSubset)
, fClamp(sampling.fShaderClamp)
- , fShaderModes{sampling.fShaderModes[0], sampling.fShaderModes[1]}
- , fLazyProxyNormalization(lazyProxyNormalization) {
+ , 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));
@@ -824,8 +769,7 @@
, fSamplerState(src.fSamplerState)
, fSubset(src.fSubset)
, fClamp(src.fClamp)
- , fShaderModes{src.fShaderModes[0], src.fShaderModes[1]}
- , fLazyProxyNormalization(src.fLazyProxyNormalization) {
+ , fShaderModes{src.fShaderModes[0], src.fShaderModes[1]} {
std::copy_n(src.fBorder, 4, fBorder);
this->setUsesSampleCoordsDirectly();
}
diff --git a/src/gpu/effects/GrTextureEffect.h b/src/gpu/effects/GrTextureEffect.h
index 26d3743..11c0f84 100644
--- a/src/gpu/effects/GrTextureEffect.h
+++ b/src/gpu/effects/GrTextureEffect.h
@@ -100,6 +100,12 @@
const GrSurfaceProxyView& view() const { return fView; }
+ // Gets a matrix that is concat'ed by wrapping GrMatrixEffect that handles y-flip and coord
+ // normalization if required. This matrix is not always known when we make the GrTextureEffect
+ // because of fully-lazy proxies. Hence, this method exists to allow this concat to happen
+ // after proxy instantiation with coordination from GrMatrixEffect.
+ SkMatrix coordAdjustmentMatrix() const;
+
class Impl : public GrGLSLFragmentProcessor {
public:
void emitCode(EmitArgs&) override;
@@ -112,7 +118,7 @@
private:
UniformHandle fSubsetUni;
UniformHandle fClampUni;
- UniformHandle fNormUni;
+ UniformHandle fIDimsUni;
UniformHandle fBorderUni;
GrGLSLShaderBuilder::SamplerHandle fSamplerHandle;
};
@@ -139,6 +145,11 @@
GrSamplerState::Filter,
GrSamplerState::MipmapMode);
static bool ShaderModeIsClampToBorder(ShaderMode);
+ // To keep things a little simpler, when we have filtering logic in the shader we
+ // operate on unnormalized texture coordinates. We will add a uniform that stores
+ // {1/w, 1/h} in a float2 and normalizes after the mode is handled if the texture
+ // is not rectangle.
+ static bool ShaderModeRequiresUnormCoord(ShaderMode);
GrSurfaceProxyView fView;
GrSamplerState fSamplerState;
@@ -146,10 +157,8 @@
SkRect fSubset;
SkRect fClamp;
ShaderMode fShaderModes[2];
- // true if we are dealing with a fully lazy proxy which can't be normalized until runtime
- bool fLazyProxyNormalization;
- inline GrTextureEffect(GrSurfaceProxyView, SkAlphaType, const Sampling&, bool);
+ inline GrTextureEffect(GrSurfaceProxyView, SkAlphaType, const Sampling&);
explicit GrTextureEffect(const GrTextureEffect& src);
@@ -159,6 +168,8 @@
bool onIsEqual(const GrFragmentProcessor&) const override;
+ bool matrixEffectShouldNormalize() const;
+
bool hasClampToBorderShaderMode() const {
return ShaderModeIsClampToBorder(fShaderModes[0]) ||
ShaderModeIsClampToBorder(fShaderModes[1]);