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]);