Reland "Use a custom FP for tessellation atlas clips"

This is a reland of 90a0d9f61879940d3c632d886af9f15c311e18fe

Original change's description:
> Use a custom FP for tessellation atlas clips
>
> Something about GrTextureEffect::MakeSubset was upsetting NVIDIA
> Vulkan. This is cleaner anyway though since we only have to create 2
> fps instead of 3, and since we don't need to make new shaders for
> inverting coverage anymore.
>
> Bug: skia:12102
> Change-Id: I5d03ed12abba5c4053e08062c75ac8d40933b422
> Reviewed-on: https://skia-review.googlesource.com/c/skia/+/419150
> Reviewed-by: Michael Ludwig <michaelludwig@google.com>
> Reviewed-by: Greg Daniel <egdaniel@google.com>
> Commit-Queue: Chris Dalton <csmartdalton@google.com>

Bug: skia:12102
Change-Id: I32c3f698ee22179caee196ef884ae64456595c96
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/419563
Reviewed-by: John Stiles <johnstiles@google.com>
Commit-Queue: Chris Dalton <csmartdalton@google.com>
diff --git a/gn/gpu.gni b/gn/gpu.gni
index 94519b6..e2b8bae 100644
--- a/gn/gpu.gni
+++ b/gn/gpu.gni
@@ -447,6 +447,8 @@
   "$_src/gpu/tessellate/GrVectorXform.h",
 
   # tessellate/shaders
+  "$_src/gpu/tessellate/shaders/GrModulateAtlasCoverageFP.cpp",
+  "$_src/gpu/tessellate/shaders/GrModulateAtlasCoverageFP.h",
   "$_src/gpu/tessellate/shaders/GrPathTessellationShader.cpp",
   "$_src/gpu/tessellate/shaders/GrPathTessellationShader.h",
   "$_src/gpu/tessellate/shaders/GrPathTessellationShader_Hardware.cpp",
diff --git a/src/gpu/GrDynamicAtlas.cpp b/src/gpu/GrDynamicAtlas.cpp
index 80818fd..ea44c8c 100644
--- a/src/gpu/GrDynamicAtlas.cpp
+++ b/src/gpu/GrDynamicAtlas.cpp
@@ -109,6 +109,11 @@
     return fNodeAllocator.make<Node>(previous, rectanizer, l, t);
 }
 
+GrSurfaceProxyView GrDynamicAtlas::surfaceProxyView(const GrCaps& caps) const {
+    return {fTextureProxy, kTextureOrigin,
+            caps.getReadSwizzle(fTextureProxy->backendFormat(), fColorType)};
+}
+
 bool GrDynamicAtlas::addRect(int width, int height, SkIPoint16* location) {
     // This can't be called anymore once instantiate() has been called.
     SkASSERT(!this->isInstantiated());
diff --git a/src/gpu/GrDynamicAtlas.h b/src/gpu/GrDynamicAtlas.h
index 6191648..38ba3c7 100644
--- a/src/gpu/GrDynamicAtlas.h
+++ b/src/gpu/GrDynamicAtlas.h
@@ -56,6 +56,7 @@
 
     int maxAtlasSize() const { return fMaxAtlasSize; }
     GrTextureProxy* textureProxy() const { return fTextureProxy.get(); }
+    GrSurfaceProxyView surfaceProxyView(const GrCaps&) const;
     bool isInstantiated() const { return fTextureProxy->isInstantiated(); }
     int currentWidth() const { return fWidth; }
     int currentHeight() const { return fHeight; }
diff --git a/src/gpu/GrProcessor.h b/src/gpu/GrProcessor.h
index d5628d6..8eec222 100644
--- a/src/gpu/GrProcessor.h
+++ b/src/gpu/GrProcessor.h
@@ -128,6 +128,7 @@
         kFwidthSquircleTestProcessor_ClassID,
         kSwizzleFragmentProcessor_ClassID,
         kTessellate_BoundingBoxShader_ClassID,
+        kTessellate_GrModulateAtlasCoverageFP_ClassID,
         kTessellate_GrStrokeTessellationShader_ClassID,
         kTessellate_HardwareCurveShader_ClassID,
         kTessellate_HardwareWedgeShader_ClassID,
diff --git a/src/gpu/tessellate/GrTessellationPathRenderer.cpp b/src/gpu/tessellate/GrTessellationPathRenderer.cpp
index 9854ff0..c20cfa5 100644
--- a/src/gpu/tessellate/GrTessellationPathRenderer.cpp
+++ b/src/gpu/tessellate/GrTessellationPathRenderer.cpp
@@ -25,6 +25,7 @@
 #include "src/gpu/tessellate/GrPathStencilCoverOp.h"
 #include "src/gpu/tessellate/GrPathTessellateOp.h"
 #include "src/gpu/tessellate/GrStrokeTessellateOp.h"
+#include "src/gpu/tessellate/shaders/GrModulateAtlasCoverageFP.h"
 
 constexpr static SkISize kAtlasInitialSize{512, 512};
 constexpr static int kMaxAtlasSize = 2048;
@@ -210,11 +211,13 @@
     surfaceDrawContext->addDrawOp(args.fClip, std::move(op));
 }
 
-GrFPResult GrTessellationPathRenderer::makeAtlasClipFP(
-        const SkIRect& drawBounds, const SkMatrix& viewMatrix, const SkPath& path, GrAA aa,
-        std::unique_ptr<GrFragmentProcessor> inputCoverage, const GrCaps& caps) {
+GrFPResult GrTessellationPathRenderer::makeAtlasClipFP(const SkIRect& drawBounds,
+                                                       const SkMatrix& viewMatrix,
+                                                       const SkPath& path, GrAA aa,
+                                                       std::unique_ptr<GrFragmentProcessor> inputFP,
+                                                       const GrCaps& caps) {
     if (viewMatrix.hasPerspective()) {
-        return GrFPFailure(std::move(inputCoverage));
+        return GrFPFailure(std::move(inputFP));
     }
     SkIRect devIBounds;
     SkIPoint16 locationInAtlas;
@@ -224,63 +227,31 @@
                                  aa != GrAA::kNo, &devIBounds, &locationInAtlas,
                                  &transposedInAtlas)) {
         // The path is too big, or the atlas ran out of room.
-        return GrFPFailure(std::move(inputCoverage));
+        return GrFPFailure(std::move(inputFP));
     }
-    GrSurfaceProxyView atlasView(sk_ref_sp(fAtlas.textureProxy()), GrDynamicAtlas::kTextureOrigin,
-                                 caps.getReadSwizzle(fAtlas.textureProxy()->backendFormat(),
-                                                     GrColorType::kAlpha_8));
     SkMatrix atlasMatrix;
-    SkRect atlasSubset, atlasDomain;
     auto [atlasX, atlasY] = locationInAtlas;
     if (!transposedInAtlas) {
-        auto atlasOffset = SkVector::Make(atlasX - devIBounds.left(), atlasY - devIBounds.top());
-        atlasMatrix = SkMatrix::Translate(atlasOffset);
-        atlasSubset = SkRect::Make(devIBounds).makeOffset(atlasOffset);
-        atlasDomain = SkRect::Make(drawBounds).makeOffset(atlasOffset);
+        atlasMatrix = SkMatrix::Translate(atlasX - devIBounds.left(), atlasY - devIBounds.top());
     } else {
         atlasMatrix.setAll(0, 1, atlasX - devIBounds.top(),
                            1, 0, atlasY - devIBounds.left(),
                            0, 0, 1);
-        atlasSubset = SkRect::MakeXYWH(atlasX, atlasY, devIBounds.height(), devIBounds.width());
-        atlasDomain = atlasMatrix.mapRect(SkRect::Make(drawBounds));
     }
-#ifdef SK_DEBUG
-    if (!path.isInverseFillType()) {
-        // At this point in time we expect callers to tighten the scissor for "kIntersect" clips, as
-        // opposed to us having to enforce the texture subset. Feel free to remove this assert if
-        // that ever changes.
-        SkASSERT(atlasDomain.isEmpty() || atlasSubset.contains(atlasDomain));
-    }
-#endif
-    // Inset the domain because if it is equal to the subset, then it falls on an exact boundary
-    // between pixels, the "nearest" filter becomes undefined, and GrTextureEffect is forced to
-    // manually enforce the subset. This inset is justifiable because textures are sampled at pixel
-    // center, unless sample shading is enabled, in which case we assume standard sample locations
-    // (https://www.khronos.org/registry/vulkan/specs/1.2/html/chap25.html).
-    // NOTE: At MSAA16, standard sample locations begin falling on actual pixel boundaries. If this
-    // happens then we simply have to rely on the fact that the atlas has a 1px padding between
-    // entries.
-    constexpr static float kMinInsetOfStandardMSAA8Locations = 1/16.f;
-    atlasDomain.inset(kMinInsetOfStandardMSAA8Locations, kMinInsetOfStandardMSAA8Locations);
-    // Look up clip coverage in the atlas.
-    GrSamplerState samplerState(GrSamplerState::WrapMode::kClampToBorder,
-                                GrSamplerState::Filter::kNearest);
-    auto fp = GrTextureEffect::MakeSubset(std::move(atlasView), kPremul_SkAlphaType, atlasMatrix,
-                                          samplerState, atlasSubset, atlasDomain, caps);
-    // Feed sk_FragCoord into the above texture lookup.
-    fp = GrDeviceSpaceEffect::Make(std::move(fp));
+    auto flags = GrModulateAtlasCoverageFP::Flags::kNone;
     if (path.isInverseFillType()) {
-        // outputCoverage = inputCoverage * (1 - atlasAlpha)
-        fp = GrBlendFragmentProcessor::Make(
-                std::move(fp), std::move(inputCoverage), SkBlendMode::kDstOut,
-                GrBlendFragmentProcessor::BlendBehavior::kSkModeBehavior);
-    } else {
-        // outputCoverage = inputCoverage * atlasAlpha
-        fp = GrBlendFragmentProcessor::Make(
-                std::move(fp), std::move(inputCoverage), SkBlendMode::kDstIn,
-                GrBlendFragmentProcessor::BlendBehavior::kSkModeBehavior);
+        flags |= GrModulateAtlasCoverageFP::Flags::kInvertCoverage;
     }
-    return GrFPSuccess(std::move(fp));
+    if (!devIBounds.contains(drawBounds)) {
+        flags |= GrModulateAtlasCoverageFP::Flags::kCheckBounds;
+        // At this point in time we expect callers to tighten the scissor for "kIntersect" clips, as
+        // opposed to us having to check the path bounds. Feel free to remove this assert if that
+        // ever changes.
+        SkASSERT(path.isInverseFillType());
+    }
+    return GrFPSuccess(std::make_unique<GrModulateAtlasCoverageFP>(flags, std::move(inputFP),
+                                                                   fAtlas.surfaceProxyView(caps),
+                                                                   atlasMatrix, devIBounds));
 }
 
 void GrTessellationPathRenderer::AtlasPathKey::set(const SkMatrix& m, bool antialias,
diff --git a/src/gpu/tessellate/GrTessellationPathRenderer.h b/src/gpu/tessellate/GrTessellationPathRenderer.h
index 5850f6a..8ba1f9c 100644
--- a/src/gpu/tessellate/GrTessellationPathRenderer.h
+++ b/src/gpu/tessellate/GrTessellationPathRenderer.h
@@ -42,16 +42,16 @@
     bool onDrawPath(const DrawPathArgs&) override;
     void onStencilPath(const StencilPathArgs&) override;
 
-    // Returns a fragment processor that modulates inputCoverage by the given deviceSpacePath's
-    // coverage, implemented using an internal atlas.
+    // Returns a fragment processor that modulates inputFP by the given deviceSpacePath's coverage,
+    // implemented using an internal atlas.
     //
-    // Returns 'inputCoverage' wrapped in GrFPFailure() if the path was too big, or if the atlas was
-    // out of room. (Currently, "too big" means more than 128*128 total pixels, or larger than half
-    // the atlas size in either dimension.)
+    // Returns 'inputFP' wrapped in GrFPFailure() if the path was too big, or if the atlas was out
+    // of room. (Currently, "too big" means more than 128*128 total pixels, or larger than half the
+    // atlas size in either dimension.)
     //
-    // Also return GrFPFailure() if the view matrix has perspective.
+    // Also returns GrFPFailure() if the view matrix has perspective.
     GrFPResult makeAtlasClipFP(const SkIRect& drawBounds, const SkMatrix&, const SkPath&, GrAA,
-                               std::unique_ptr<GrFragmentProcessor> inputCoverage, const GrCaps&);
+                               std::unique_ptr<GrFragmentProcessor> inputFP, const GrCaps&);
 
     void preFlush(GrOnFlushResourceProvider*, SkSpan<const uint32_t> taskIDs) override;
 
diff --git a/src/gpu/tessellate/shaders/GrModulateAtlasCoverageFP.cpp b/src/gpu/tessellate/shaders/GrModulateAtlasCoverageFP.cpp
new file mode 100644
index 0000000..8c1b153
--- /dev/null
+++ b/src/gpu/tessellate/shaders/GrModulateAtlasCoverageFP.cpp
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2021 Google LLC.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "src/gpu/tessellate/shaders/GrModulateAtlasCoverageFP.h"
+
+#include "src/gpu/GrDynamicAtlas.h"
+#include "src/gpu/effects/GrTextureEffect.h"
+
+GrModulateAtlasCoverageFP::GrModulateAtlasCoverageFP(Flags flags,
+                                                     std::unique_ptr<GrFragmentProcessor> inputFP,
+                                                     GrSurfaceProxyView atlasView,
+                                                     const SkMatrix& devToAtlasMatrix,
+                                                     const SkIRect& devIBounds)
+        : GrFragmentProcessor(kTessellate_GrModulateAtlasCoverageFP_ClassID,
+                              kCompatibleWithCoverageAsAlpha_OptimizationFlag)
+        , fFlags(flags)
+        , fBounds((fFlags & Flags::kCheckBounds) ? devIBounds : SkIRect{0,0,0,0}) {
+    this->registerChild(std::move(inputFP));
+    this->registerChild(GrTextureEffect::Make(std::move(atlasView), kUnknown_SkAlphaType,
+                                              devToAtlasMatrix, GrSamplerState::Filter::kNearest),
+                        SkSL::SampleUsage::Explicit());
+}
+
+GrModulateAtlasCoverageFP::GrModulateAtlasCoverageFP(const GrModulateAtlasCoverageFP& that)
+        : GrFragmentProcessor(kTessellate_GrModulateAtlasCoverageFP_ClassID,
+                              kCompatibleWithCoverageAsAlpha_OptimizationFlag)
+        , fFlags(that.fFlags)
+        , fBounds(that.fBounds) {
+    this->cloneAndRegisterAllChildProcessors(that);
+}
+
+std::unique_ptr<GrGLSLFragmentProcessor> GrModulateAtlasCoverageFP::onMakeProgramImpl() const {
+    class Impl : public GrGLSLFragmentProcessor {
+        void emitCode(EmitArgs& args) override {
+            auto fp = args.fFp.cast<GrModulateAtlasCoverageFP>();
+            auto f = args.fFragBuilder;
+            auto uniHandler = args.fUniformHandler;
+            SkString inputColor = this->invokeChild(0, args);
+            f->codeAppend("half coverage = 0;");
+            if (fp.fFlags & Flags::kCheckBounds) {
+                const char* boundsName;
+                fBoundsUniform = uniHandler->addUniform(&fp, kFragment_GrShaderFlag,
+                                                        kFloat4_GrSLType, "bounds", &boundsName);
+                // Are we inside the path's valid atlas bounds?
+                f->codeAppendf("if (all(greaterThan(sk_FragCoord.xy, %s.xy)) && "
+                                   "all(lessThan(sk_FragCoord.xy, %s.zw))) ",
+                               boundsName, boundsName);
+            }
+            f->codeAppendf("{");
+            SkString atlasCoverage = this->invokeChild(1, args, "sk_FragCoord.xy");
+            f->codeAppendf("coverage = %s.a;", atlasCoverage.c_str());
+            f->codeAppendf("}");
+            const char* coverageMaybeInvertName;
+            fCoverageMaybeInvertUniform = uniHandler->addUniform(&fp, kFragment_GrShaderFlag,
+                                                                 kHalf2_GrSLType, "coverageInvert",
+                                                                 &coverageMaybeInvertName);
+            // Invert coverage, if needed.
+            f->codeAppendf("coverage = coverage * %s.x + %s.y;",
+                           coverageMaybeInvertName, coverageMaybeInvertName);
+            f->codeAppendf("return %s * coverage;", inputColor.c_str());
+        }
+        void onSetData(const GrGLSLProgramDataManager& pdman,
+                       const GrFragmentProcessor& processor) override {
+            auto fp = processor.cast<GrModulateAtlasCoverageFP>();
+            if (fp.fFlags & Flags::kCheckBounds) {
+                pdman.set4fv(fBoundsUniform, 1, SkRect::Make(fp.fBounds).asScalars());
+            }
+            if (fp.fFlags & Flags::kInvertCoverage) {
+                pdman.set2f(fCoverageMaybeInvertUniform, -1, 1);  // -1*coverage + 1 = 1 - coverage.
+            } else {
+                pdman.set2f(fCoverageMaybeInvertUniform, 1, 0);  // 1*coverage + 0 = coverage.
+            }
+        }
+        UniformHandle fBoundsUniform;
+        UniformHandle fCoverageMaybeInvertUniform;
+    };
+    return std::make_unique<Impl>();
+}
diff --git a/src/gpu/tessellate/shaders/GrModulateAtlasCoverageFP.h b/src/gpu/tessellate/shaders/GrModulateAtlasCoverageFP.h
new file mode 100644
index 0000000..9ac146d
--- /dev/null
+++ b/src/gpu/tessellate/shaders/GrModulateAtlasCoverageFP.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2021 Google LLC.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrGrModulateAtlasCoverageFP_DEFINED
+#define GrGrModulateAtlasCoverageFP_DEFINED
+
+#include "src/gpu/GrFragmentProcessor.h"
+
+// Multiplies 'inputFP' by the coverage value in an atlas, optionally inverting or clamping to 0.
+class GrModulateAtlasCoverageFP : public GrFragmentProcessor {
+public:
+    enum class Flags {
+        kNone = 0,
+        kInvertCoverage = 1 << 0,  // Return inputColor * (1 - atlasCoverage).
+        kCheckBounds = 1 << 1  // Clamp atlasCoverage to 0 if outside the path's valid atlas bounds.
+    };
+
+    GR_DECL_BITFIELD_CLASS_OPS_FRIENDS(Flags);
+
+    GrModulateAtlasCoverageFP(Flags flags, std::unique_ptr<GrFragmentProcessor> inputFP,
+                              GrSurfaceProxyView atlasView, const SkMatrix& devToAtlasMatrix,
+                              const SkIRect& devIBounds);
+
+    GrModulateAtlasCoverageFP(const GrModulateAtlasCoverageFP& that);
+
+    const char* name() const override {
+        return "GrModulateAtlasCoverageFP";
+    }
+    void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const override {
+        b->add32(fFlags & Flags::kCheckBounds);
+    }
+    std::unique_ptr<GrFragmentProcessor> clone() const override {
+        return std::make_unique<GrModulateAtlasCoverageFP>(*this);
+    }
+    bool onIsEqual(const GrFragmentProcessor& that) const override {
+        auto fp = that.cast<GrModulateAtlasCoverageFP>();
+        return fFlags == fp.fFlags && fBounds == fp.fBounds;
+    }
+    std::unique_ptr<GrGLSLFragmentProcessor> onMakeProgramImpl() const override;
+
+private:
+    const Flags fFlags;
+    const SkIRect fBounds;
+};
+
+GR_MAKE_BITFIELD_CLASS_OPS(GrModulateAtlasCoverageFP::Flags);
+
+#endif