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>
This commit is contained in:
Brian Salomon 2021-04-16 08:39:35 -04:00 committed by Skia Commit-Bot
parent 59f1a9cb7a
commit 816db16d19
3 changed files with 113 additions and 152 deletions

View File

@ -29,7 +29,13 @@ private:
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;

View File

@ -150,61 +150,16 @@ bool GrTextureEffect::Sampling::hasBorderAlpha() const {
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 @@ std::unique_ptr<GrFragmentProcessor> GrTextureEffect::Make(GrSurfaceProxyView vi
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 @@ std::unique_ptr<GrFragmentProcessor> GrTextureEffect::MakeSubset(GrSurfaceProxyV
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 @@ std::unique_ptr<GrFragmentProcessor> GrTextureEffect::MakeSubset(GrSurfaceProxyV
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 @@ std::unique_ptr<GrFragmentProcessor> GrTextureEffect::MakeCustomLinearFilterInse
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 @@ inline bool GrTextureEffect::ShaderModeIsClampToBorder(ShaderMode m) {
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 @@ void GrTextureEffect::Impl::emitCode(EmitArgs& args) {
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 @@ void GrTextureEffect::Impl::emitCode(EmitArgs& args) {
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 @@ void GrTextureEffect::Impl::emitCode(EmitArgs& args) {
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 @@ void GrTextureEffect::Impl::emitCode(EmitArgs& args) {
&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 @@ void GrTextureEffect::Impl::onSetData(const GrGLSLProgramDataManager& pdm,
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 @@ void GrTextureEffect::Impl::onSetData(const GrGLSLProgramDataManager& pdm,
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 @@ void GrTextureEffect::onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyB
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 @@ bool GrTextureEffect::onIsEqual(const GrFragmentProcessor& other) const {
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 @@ GrTextureEffect::GrTextureEffect(const GrTextureEffect& src)
, 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();
}

View File

@ -100,6 +100,12 @@ public:
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 @@ public:
private:
UniformHandle fSubsetUni;
UniformHandle fClampUni;
UniformHandle fNormUni;
UniformHandle fIDimsUni;
UniformHandle fBorderUni;
GrGLSLShaderBuilder::SamplerHandle fSamplerHandle;
};
@ -139,6 +145,11 @@ private:
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 @@ private:
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 @@ private:
bool onIsEqual(const GrFragmentProcessor&) const override;
bool matrixEffectShouldNormalize() const;
bool hasClampToBorderShaderMode() const {
return ShaderModeIsClampToBorder(fShaderModes[0]) ||
ShaderModeIsClampToBorder(fShaderModes[1]);