[graphite] Set up atlas-based clipping.

Disabled for now as there are some correctness issues. But includes:

* Adds genID to ClipStack::SaveRecord for atlas key generation
* Adds use of ClipAtlas to find/create SW clip entries
* Fixes null analytic clip when used with atlas clip
* Various other fixes and clean up

Bug: b/388810102
Change-Id: Ia595dff40c67f3f0a7eeb1ea5df9cdaa56aeef34
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/928449
Reviewed-by: Michael Ludwig <michaelludwig@google.com>
Commit-Queue: Jim Van Verth <jvanverth@google.com>
diff --git a/src/gpu/graphite/ClipAtlasManager.cpp b/src/gpu/graphite/ClipAtlasManager.cpp
index 6b6e312..4f16ced 100644
--- a/src/gpu/graphite/ClipAtlasManager.cpp
+++ b/src/gpu/graphite/ClipAtlasManager.cpp
@@ -19,8 +19,8 @@
 
 ClipAtlasManager::ClipAtlasManager(Recorder* recorder) : fRecorder(recorder) {
     static constexpr SkColorType kColorType = kAlpha_8_SkColorType;
-    static constexpr int kWidth = 2048;
-    static constexpr int kHeight = 2048;
+    static constexpr int kWidth = 4096;
+    static constexpr int kHeight = 4096;
 
     const Caps* caps = recorder->priv().caps();
     fDrawAtlas = DrawAtlas::Make(kColorType,
@@ -130,14 +130,9 @@
 
     // Draw the shape; based on how we've initialized the buffer and chosen alpha+invert,
     // every element is drawn with the kReplace_Op
-    if (invert) {
-        // Must invert the path
-        SkASSERT(!e.fShape.inverted());
-        // TODO: this is an extra copy effectively, just so we can toggle inversion; would be
-        // better perhaps to just call a drawPath() since we know it'll use path rendering w/
-        // the inverse fill type.
+    if (invert != e.fShape.inverted()) {
         Shape inverted(e.fShape);
-        inverted.setInverted(true);
+        inverted.setInverted(invert);
         helper->drawClip(inverted, e.fLocalToDevice, alpha, resultBounds);
     } else {
         helper->drawClip(e.fShape, e.fLocalToDevice, alpha, resultBounds);
diff --git a/src/gpu/graphite/ClipStack_graphite.cpp b/src/gpu/graphite/ClipStack_graphite.cpp
index 8c65492..b4a1125 100644
--- a/src/gpu/graphite/ClipStack_graphite.cpp
+++ b/src/gpu/graphite/ClipStack_graphite.cpp
@@ -10,12 +10,16 @@
 #include "include/core/SkMatrix.h"
 #include "include/core/SkShader.h"
 #include "include/core/SkStrokeRec.h"
+#include "include/gpu/graphite/Recorder.h"
 #include "src/base/SkTLazy.h"
 #include "src/core/SkPathPriv.h"
 #include "src/core/SkRRectPriv.h"
 #include "src/core/SkRectPriv.h"
+#include "src/gpu/graphite/AtlasProvider.h"
+#include "src/gpu/graphite/ClipAtlasManager.h"
 #include "src/gpu/graphite/Device.h"
 #include "src/gpu/graphite/DrawParams.h"
+#include "src/gpu/graphite/RecorderPriv.h"
 #include "src/gpu/graphite/geom/BoundsManager.h"
 #include "src/gpu/graphite/geom/Geometry.h"
 
@@ -35,6 +39,22 @@
     }
 }
 
+static constexpr uint32_t kInvalidGenID  = 0;
+static constexpr uint32_t kEmptyGenID    = 1;
+static constexpr uint32_t kWideOpenGenID = 2;
+
+uint32_t next_gen_id() {
+    // 0-2 are reserved for invalid, empty & wide-open
+    static const uint32_t kFirstUnreservedGenID = 3;
+    static std::atomic<uint32_t> nextID{kFirstUnreservedGenID};
+
+    uint32_t id;
+    do {
+        id = nextID.fetch_add(1, std::memory_order_relaxed);
+    } while (id < kFirstUnreservedGenID);
+    return id;
+}
+
 bool oriented_bbox_intersection(const Rect& a, const Transform& aXform,
                                 const Rect& b, const Transform& bXform) {
     // NOTE: We intentionally exclude projected bounds for two reasons:
@@ -641,7 +661,8 @@
         , fOldestValidIndex(0)
         , fDeferredSaveCount(0)
         , fStackOp(SkClipOp::kIntersect)
-        , fState(ClipState::kWideOpen)  {}
+        , fState(ClipState::kWideOpen)
+        , fGenID(kInvalidGenID) {}
 
 ClipStack::SaveRecord::SaveRecord(const SaveRecord& prior,
                                   int startingElementIndex)
@@ -652,12 +673,26 @@
         , fOldestValidIndex(prior.fOldestValidIndex)
         , fDeferredSaveCount(0)
         , fStackOp(prior.fStackOp)
-        , fState(prior.fState) {
+        , fState(prior.fState)
+        , fGenID(kInvalidGenID) {
     // If the prior record added an element, this one will insert into the same index
     // (that's okay since we'll remove it when this record is popped off the stack).
     SkASSERT(startingElementIndex >= prior.fStartingElementIndex);
 }
 
+uint32_t ClipStack::SaveRecord::genID() const {
+    if (fState == ClipState::kEmpty) {
+        return kEmptyGenID;
+    } else if (fState == ClipState::kWideOpen) {
+        return kWideOpenGenID;
+    } else {
+        // The gen ID shouldn't be empty or wide open, since they are reserved for the above
+        // if-cases. It may be kInvalid if the record hasn't had any elements added to it yet.
+        SkASSERT(fGenID != kEmptyGenID && fGenID != kWideOpenGenID);
+        return fGenID;
+    }
+}
+
 ClipStack::ClipState ClipStack::SaveRecord::state() const {
     if (fShader && fState != ClipState::kEmpty) {
         return ClipState::kComplex;
@@ -932,6 +967,8 @@
         elements->back() = std::move(toAdd);
     }
 
+    // Changing this will prompt ClipStack to invalidate any masks associated with this record.
+    fGenID = next_gen_id();
     return true;
 }
 
@@ -961,6 +998,7 @@
 
     // This invalidates all older elements that are owned by save records lower in the clip stack.
     fOldestValidIndex = fStartingElementIndex;
+    fGenID = next_gen_id();
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -1190,6 +1228,7 @@
                                       const Geometry& geometry,
                                       const SkStrokeRec& style,
                                       bool outsetBoundsForAA,
+                                      bool msaaSupported,
                                       ClipStack::ElementList* outEffectiveElements) const {
     static const Clip kClippedOut = {
             Rect::InfiniteInverted(), Rect::InfiniteInverted(), SkIRect::MakeEmpty(),
@@ -1352,6 +1391,31 @@
         }
     }
 
+#if defined(SK_GRAPHITE_ENABLE_CLIP_ATLAS)
+    // If there is no MSAA supported, rasterize any remaining elements by flattening them
+    // into a single mask and storing in an atlas. Otherwise these will be handled by
+    // Device::drawClip().
+    AtlasProvider* atlasProvider = fDevice->recorder()->priv().atlasProvider();
+    if (!msaaSupported && !outEffectiveElements->empty()) {
+        ClipAtlasManager* clipAtlas = atlasProvider->getClipAtlasManager();
+        SkASSERT(clipAtlas);
+        AtlasClip* atlasClip = &nonMSAAClip.fAtlasClip;
+
+        const TextureProxy* proxy = clipAtlas->findOrCreateEntry(cs.genID(),
+                                                                 outEffectiveElements,
+                                                                 cs.outerBounds(),
+                                                                 &atlasClip->fOutPos);
+        if (proxy) {
+            // Add to Clip
+            atlasClip->fMaskBounds = scissor;
+            atlasClip->fAtlasTexture = sk_ref_sp(proxy);
+
+            // Elements are represented in the clip atlas, discard.
+            outEffectiveElements->clear();
+        }
+    }
+#endif
+
     return Clip(drawBounds, transformedShapeBounds, scissor.asSkIRect(), nonMSAAClip, cs.shader());
 }
 
diff --git a/src/gpu/graphite/ClipStack_graphite.h b/src/gpu/graphite/ClipStack_graphite.h
index f738eee..1c1cff9 100644
--- a/src/gpu/graphite/ClipStack_graphite.h
+++ b/src/gpu/graphite/ClipStack_graphite.h
@@ -97,6 +97,7 @@
                                const Geometry&,
                                const SkStrokeRec&,
                                bool outsetBoundsForAA,
+                               bool msaaSupported,
                                ElementList* outEffectiveElements) const;
 
     // Update the per-clip element state for later rendering using pre-computed clip state data for
@@ -273,6 +274,7 @@
         const Rect&     innerBounds() const { return fInnerBounds;  }
         SkClipOp        op()          const { return fStackOp;      }
         ClipState       state()       const;
+        uint32_t        genID()       const;
 
         int  firstActiveElementIndex() const { return fStartingElementIndex;     }
         int  oldestElementIndex()      const { return fOldestValidIndex;         }
@@ -329,6 +331,7 @@
         // because if kDifference then there is an implicit extra outer bounds at the device edges.
         SkClipOp  fStackOp;
         ClipState fState;
+        uint32_t  fGenID;
     };
 
     Rect deviceBounds() const;
diff --git a/src/gpu/graphite/Device.cpp b/src/gpu/graphite/Device.cpp
index 7b39362..ed88963 100644
--- a/src/gpu/graphite/Device.cpp
+++ b/src/gpu/graphite/Device.cpp
@@ -1272,7 +1272,7 @@
     ClipStack::ElementList clipElements;
     const Clip clip =
             fClip.visitClipStackForDraw(localToDevice, geometry, style, outsetBoundsForAA,
-                                        &clipElements);
+                                        fMSAASupported, &clipElements);
     if (clip.isClippedOut()) {
         // Clipped out, so don't record anything.
         return;
@@ -1284,8 +1284,8 @@
                                                          : SkBlendMode::kSrcOver;
     Coverage rendererCoverage = renderer ? renderer->coverage()
                                          : Coverage::kSingleChannel;
-    if ((clip.shader() || !clip.nonMSAAClip().isEmpty()) && rendererCoverage == Coverage::kNone) {
-        // Must upgrade to single channel coverage if there is a clip shader or analytic clip;
+    if (clip.needsCoverage() && rendererCoverage == Coverage::kNone) {
+        // Must upgrade to single channel coverage if the clip requires coverage;
         // but preserve LCD coverage if the Renderer uses that.
         rendererCoverage = Coverage::kSingleChannel;
     }
diff --git a/src/gpu/graphite/DrawParams.h b/src/gpu/graphite/DrawParams.h
index a96f4ab..f807f8e 100644
--- a/src/gpu/graphite/DrawParams.h
+++ b/src/gpu/graphite/DrawParams.h
@@ -98,6 +98,8 @@
 
     bool isClippedOut() const { return fDrawBounds.isEmptyNegativeOrNaN(); }
 
+    bool needsCoverage() const { return SkToBool(fShader) || !fNonMSAAClip.isEmpty(); }
+
 private:
     // DrawList assumes the DrawBounds are correct for a given shape, transform, and style. They
     // are provided to the DrawList to avoid re-calculating the same bounds.
diff --git a/src/gpu/graphite/PaintParams.cpp b/src/gpu/graphite/PaintParams.cpp
index 6eca33d..085e580 100644
--- a/src/gpu/graphite/PaintParams.cpp
+++ b/src/gpu/graphite/PaintParams.cpp
@@ -258,11 +258,20 @@
                                  PipelineDataGatherer* gatherer) const {
     if (!fNonMSAAClip.isEmpty()) {
         const AnalyticClip& analyticClip = fNonMSAAClip.fAnalyticClip;
-        float radius = analyticClip.fRadius + 0.5f;
-        // N.B.: Because the clip data is normally used with depth-based clipping,
-        // the shape is inverted from its usual state. We re-invert here to
-        // match what the shader snippet expects.
-        SkPoint radiusPair = {(analyticClip.fInverted) ? radius : -radius, 1.0f/radius};
+        SkPoint radiusPair;
+        SkRect analyticBounds;
+        if (!analyticClip.isEmpty()) {
+            float radius = analyticClip.fRadius + 0.5f;
+            // N.B.: Because the clip data is normally used with depth-based clipping,
+            // the shape is inverted from its usual state. We re-invert here to
+            // match what the shader snippet expects.
+            radiusPair = {(analyticClip.fInverted) ? radius : -radius, 1.0f/radius};
+            analyticBounds = analyticClip.fBounds.makeOutset(0.5f).asSkRect();
+        } else {
+            // This will generate no analytic clip.
+            radiusPair = { -0.5f, 1.f };
+            analyticBounds = { 0, 0, 0, 0 };
+        }
 
         const AtlasClip& atlasClip = fNonMSAAClip.fAtlasClip;
         skvx::float2 maskSize = atlasClip.fMaskBounds.size();
@@ -272,7 +281,7 @@
                                   atlasClip.fOutPos.y() - atlasClip.fMaskBounds.top()};
 
         NonMSAAClipBlock::NonMSAAClipData data(
-                analyticClip.fBounds.makeOutset(0.5f).asSkRect(),
+                analyticBounds,
                 radiusPair,
                 analyticClip.edgeSelectRect(),
                 texCoordOffset,
diff --git a/src/gpu/graphite/RasterPathUtils.cpp b/src/gpu/graphite/RasterPathUtils.cpp
index cafd33e..a62ae8a 100644
--- a/src/gpu/graphite/RasterPathUtils.cpp
+++ b/src/gpu/graphite/RasterPathUtils.cpp
@@ -39,11 +39,15 @@
     return true;
 }
 
+void RasterMaskHelper::clear(uint8_t alpha, const SkIRect& shapeBounds) {
+    fPixels->erase(SkColorSetARGB(alpha, 0xFF, 0xFF, 0xFF), shapeBounds);
+}
+
 void RasterMaskHelper::drawShape(const Shape& shape,
                                  const Transform& localToDevice,
                                  const SkStrokeRec& strokeRec,
-                                 const SkIRect& resultBounds) {
-    fRasterClip.setRect(resultBounds);
+                                 const SkIRect& shapeBounds) {
+    fRasterClip.setRect(shapeBounds);
 
     SkPaint paint;
     paint.setBlendMode(SkBlendMode::kSrc);  // "Replace" mode
@@ -56,8 +60,8 @@
     // The atlas transform of the shape is `localToDevice` translated by the top-left offset of the
     // resultBounds and the inverse of the base mask transform offset for the current set of shapes.
     // We will need to translate draws so the bound's UL corner is at the origin
-    translatedMatrix.postTranslate(resultBounds.x() - fTransformedMaskOffset.x(),
-                                   resultBounds.y() - fTransformedMaskOffset.y());
+    translatedMatrix.postTranslate(shapeBounds.x() - fTransformedMaskOffset.x(),
+                                   shapeBounds.y() - fTransformedMaskOffset.y());
 
     fDraw.fCTM = &translatedMatrix;
     // TODO: use drawRect, drawRRect, drawArc
diff --git a/src/gpu/graphite/RasterPathUtils.h b/src/gpu/graphite/RasterPathUtils.h
index 3dd05c1..b5f09c1 100644
--- a/src/gpu/graphite/RasterPathUtils.h
+++ b/src/gpu/graphite/RasterPathUtils.h
@@ -45,13 +45,7 @@
 
     bool init(SkISize pixmapSize, skvx::float2 transformedMaskOffset);
 
-    void clear(uint8_t alpha, const SkIRect& resultBounds) {
-        SkPaint paint;
-        SkMatrix identity;
-        paint.setColor(SkColorSetARGB(alpha, 0xFF, 0xFF, 0xFF));
-        fDraw.fCTM = &identity;
-        fDraw.drawRect(SkRect::Make(resultBounds), paint);
-    }
+    void clear(uint8_t alpha, const SkIRect& resultBounds);
 
     // Draw a single shape into the bitmap (as a path) at location resultBounds.
     void drawShape(const Shape& shape,