[graphite] Add use of ProxyCache to ClipAtlasManager.

* Uses the ProxyCache for clips that are too large for the atlas
* Shrinks the atlas and plot sizes to correspond to common cases
* Adds the ability to append the clip bounds to the key

Bug: b/404880920
Change-Id: I85e6f2f9cac1b544db1a33ac3af3404dffcf89df
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/969022
Reviewed-by: Robert Phillips <robertphillips@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 a9c7859..e92d87f 100644
--- a/src/gpu/graphite/ClipAtlasManager.cpp
+++ b/src/gpu/graphite/ClipAtlasManager.cpp
@@ -7,32 +7,136 @@
 
 #include "src/gpu/graphite/ClipAtlasManager.h"
 
+#include "include/core/SkBitmap.h"
 #include "include/gpu/graphite/Recorder.h"
 #include "include/private/base/SkFixed.h"
 #include "src/base/SkFloatBits.h"
 #include "src/gpu/graphite/AtlasProvider.h"
+#include "src/gpu/graphite/ProxyCache.h"
 #include "src/gpu/graphite/RasterPathUtils.h"
 #include "src/gpu/graphite/RecorderPriv.h"
 #include "src/gpu/graphite/TextureProxy.h"
 
 namespace skgpu::graphite {
 
-static constexpr int kClipAtlasWidth = 4096;
-static constexpr int kClipAtlasHeight = 4096;
+static constexpr int kClipAtlasWidth = 2048;
+static constexpr int kClipAtlasHeight = 2048;
 
 ClipAtlasManager::ClipAtlasManager(Recorder* recorder)
         : fRecorder(recorder)
         , fDrawAtlasMgr(kClipAtlasWidth, kClipAtlasHeight,
-                        /*plotWidth=*/kClipAtlasWidth, /*plotHeight=*/kClipAtlasHeight,
+                        /*plotWidth=*/kClipAtlasWidth/2, /*plotHeight=*/kClipAtlasHeight/2,
                         DrawAtlas::UseStorageTextures::kNo,
                         "ClipAtlas", recorder->priv().caps()) {}
 
+namespace {
+// Needed to ensure that we have surrounding context, e.g. for inverse clips this would be solid.
+constexpr int kEntryPadding = 1;
+
+// Copied and modified from Ganesh ClipStack
+void draw_to_sw_mask(RasterMaskHelper* helper,
+                     const ClipStack::Element& e,
+                     bool isFirstElement,
+                     const SkIRect& resultBounds) {
+    // If the first element to draw is an intersect, we clear to 0 and will draw it directly with
+    // coverage 1 (subsequent intersect elements will be inverse-filled and draw 0 outside).
+    // If the first element to draw is a difference, we clear to 1, and in all cases we draw the
+    // difference element directly with coverage 0.
+    if (isFirstElement) {
+        helper->clear(e.fOp == SkClipOp::kIntersect ? 0x00 : 0xFF, resultBounds);
+    }
+
+    uint8_t alpha;
+    bool invert;
+    if (e.fOp == SkClipOp::kIntersect) {
+        // Intersect modifies pixels outside of its geometry. If this is the first element,
+        // we can draw directly with coverage 1 since we cleared to 0. Otherwise we draw the
+        // inverse-filled shape with 0 coverage to erase everything outside the element.
+        if (isFirstElement) {
+            alpha = 0xFF;
+            invert = false;
+        } else {
+            alpha = 0x00;
+            invert = true;
+        }
+    } else {
+        // For difference ops, can always just subtract the shape directly by drawing 0 coverage
+        SkASSERT(e.fOp == SkClipOp::kDifference);
+        alpha = 0x00;
+        invert = false;
+    }
+
+    // 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 != e.fShape.inverted()) {
+        Shape inverted(e.fShape);
+        inverted.setInverted(invert);
+        helper->drawClip(inverted, e.fLocalToDevice, alpha, resultBounds);
+    } else {
+        helper->drawClip(e.fShape, e.fLocalToDevice, alpha, resultBounds);
+    }
+}
+
+void draw_clip_mask_to_pixmap(const ClipStack::ElementList* elementList,
+                              SkIRect iBounds,
+                              SkISize renderSize,
+                              SkIRect iDrawBounds,
+                              SkAutoPixmapStorage* dst) {
+    // The shape bounds are expanded by kEntryPadding so we need to take that into account here.
+    SkIVector transformedMaskOffset = {iBounds.left() - kEntryPadding,
+                                       iBounds.top() - kEntryPadding};
+    RasterMaskHelper helper(dst);
+    if (!helper.init(renderSize, transformedMaskOffset)) {
+        return;
+    }
+
+    SkASSERT(!elementList->empty());
+    for (int i = 0; i < elementList->size(); ++i) {
+        draw_to_sw_mask(&helper, *(*elementList)[i], i == 0, iDrawBounds);
+    }
+}
+} // anonymous namespace
+
 const TextureProxy* ClipAtlasManager::findOrCreateEntry(uint32_t stackRecordID,
                                                         const ClipStack::ElementList* elementList,
                                                         SkIRect iBounds,
                                                         SkIPoint* outPos) {
-    skgpu::UniqueKey maskKey = GenerateClipMaskKey(stackRecordID, elementList);
-    return fDrawAtlasMgr.findOrCreateEntry(fRecorder, maskKey, elementList, iBounds, outPos);
+    // For the ClipAtlas cache, we don't include the bounds in the key
+    skgpu::UniqueKey maskKey = GenerateClipMaskKey(stackRecordID, elementList, {});
+
+    const TextureProxy* atlasProxy = fDrawAtlasMgr.findOrCreateEntry(fRecorder, maskKey,
+                                                                     elementList, iBounds, outPos);
+    if (atlasProxy) {
+        return atlasProxy;
+    }
+
+    // We need to include the bounds in the key when using the ProxyCache
+    maskKey = GenerateClipMaskKey(stackRecordID, elementList, iBounds);
+    // Bounds relative to the bitmap origin
+    // Expanded to include padding as well (so we clear correctly for inverse clip)
+    SkIRect iDrawBounds = SkIRect::MakeXYWH(0, 0,
+                                            iBounds.width() + 2*kEntryPadding,
+                                            iBounds.height() + 2*kEntryPadding);
+    const struct ClipDrawContext {
+        const ClipStack::ElementList* fElementList;
+        SkIRect fBounds;
+        SkISize fRenderSize;
+        SkIRect fDrawBounds;
+    } context = { elementList, iBounds, iDrawBounds.size(), iDrawBounds };
+    sk_sp<TextureProxy> proxy = fRecorder->priv().proxyCache()->findOrCreateCachedProxy(
+            fRecorder, maskKey, &context,
+            [](const void* ctx) {
+                const ClipDrawContext* cdc = static_cast<const ClipDrawContext*>(ctx);
+                SkBitmap bm;
+                SkAutoPixmapStorage dst;
+                draw_clip_mask_to_pixmap(cdc->fElementList, cdc->fBounds, cdc->fRenderSize,
+                                         cdc->fDrawBounds, &dst);
+                bm.installPixels(dst);
+                return bm;
+            });
+    *outPos = { kEntryPadding, kEntryPadding };
+
+    return proxy.get();
 }
 
 bool ClipAtlasManager::recordUploads(DrawContext* dc) {
@@ -76,11 +180,6 @@
     }
 }
 
-namespace {
-// Needed to ensure that we have surrounding context, e.g. for inverse clips this would be solid.
-constexpr int kEntryPadding = 1;
-}  // namespace
-
 const TextureProxy* ClipAtlasManager::DrawAtlasMgr::findOrCreateEntry(
             Recorder* recorder,
             const skgpu::UniqueKey& maskKey,
@@ -144,51 +243,6 @@
     return proxy;
 }
 
-// Copied and modified from Ganesh ClipStack
-void draw_to_sw_mask(RasterMaskHelper* helper,
-                     const ClipStack::Element& e,
-                     bool clearMask,
-                     const SkIRect& resultBounds) {
-    // If the first element to draw is an intersect, we clear to 0 and will draw it directly with
-    // coverage 1 (subsequent intersect elements will be inverse-filled and draw 0 outside).
-    // If the first element to draw is a difference, we clear to 1, and in all cases we draw the
-    // difference element directly with coverage 0.
-    if (clearMask) {
-        helper->clear(e.fOp == SkClipOp::kIntersect ? 0x00 : 0xFF, resultBounds);
-    }
-
-    uint8_t alpha;
-    bool invert;
-    if (e.fOp == SkClipOp::kIntersect) {
-        // Intersect modifies pixels outside of its geometry. If this isn't the first op, we
-        // draw the inverse-filled shape with 0 coverage to erase everything outside the element
-        // But if we are the first element, we can draw directly with coverage 1 since we
-        // cleared to 0.
-        if (clearMask) {
-            alpha = 0xFF;
-            invert = false;
-        } else {
-            alpha = 0x00;
-            invert = true;
-        }
-    } else {
-        // For difference ops, can always just subtract the shape directly by drawing 0 coverage
-        SkASSERT(e.fOp == SkClipOp::kDifference);
-        alpha = 0x00;
-        invert = false;
-    }
-
-    // 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 != e.fShape.inverted()) {
-        Shape inverted(e.fShape);
-        inverted.setInverted(invert);
-        helper->drawClip(inverted, e.fLocalToDevice, alpha, resultBounds);
-    } else {
-        helper->drawClip(e.fShape, e.fLocalToDevice, alpha, resultBounds);
-    }
-}
-
 const TextureProxy* ClipAtlasManager::DrawAtlasMgr::addToAtlas(
             Recorder* recorder,
             const ClipStack::ElementList* elementsForMask,
@@ -200,23 +254,18 @@
     if (maskSize.isEmpty()) {
         return nullptr;
     }
-
-    // Bounds relative to the AtlasLocator
-    // Expanded to include padding as well (so we clear correctly for inverse clip)
-    SkIRect iShapeBounds = SkIRect::MakeXYWH(0, 0,
-                                             maskSize.width() + 2*kEntryPadding,
-                                             maskSize.height() + 2*kEntryPadding);
+    // Expand to include padding as well (so we clear correctly for inverse clip)
+    maskSize.fWidth += 2*kEntryPadding;
+    maskSize.fHeight += 2*kEntryPadding;
 
     // Request space in DrawAtlas, including padding
     DrawAtlas::ErrorCode errorCode = fDrawAtlas->addRect(recorder,
-                                                         iShapeBounds.width(),
-                                                         iShapeBounds.height(),
+                                                         maskSize.width(),
+                                                         maskSize.height(),
                                                          locator);
     if (errorCode != DrawAtlas::ErrorCode::kSucceeded) {
         return nullptr;
     }
-    SkIPoint topLeft = locator->topLeft();
-    *outPos = SkIPoint::Make(topLeft.x() + kEntryPadding, topLeft.y() + kEntryPadding);
 
     // Rasterize path to backing pixmap.
     // This pixmap will be the size of the Plot that contains the given rect, not the entire atlas,
@@ -225,21 +274,15 @@
     SkAutoPixmapStorage dst;
     SkIPoint renderPos = fDrawAtlas->prepForRender(*locator, &dst);
 
-    // The shape bounds are expanded by kEntryPadding so we need to take that into account here.
-    SkIVector transformedMaskOffset = {iBounds.left() - kEntryPadding,
-                                       iBounds.top() - kEntryPadding};
-    RasterMaskHelper helper(&dst);
-    if (!helper.init(fDrawAtlas->plotSize(), transformedMaskOffset)) {
-        return nullptr;
-    }
-
+    // Bounds relative to the AtlasLocator
+    SkIRect iDrawBounds = SkIRect::MakeXYWH(0, 0, maskSize.width(), maskSize.height());
     // Offset bounds to plot location for draw
-    iShapeBounds.offset(renderPos.x(), renderPos.y());
+    iDrawBounds.offset(renderPos.x(), renderPos.y());
 
-    SkASSERT(!elementsForMask->empty());
-    for (int i = 0; i < elementsForMask->size(); ++i) {
-        draw_to_sw_mask(&helper, *(*elementsForMask)[i], i == 0, iShapeBounds);
-    }
+    draw_clip_mask_to_pixmap(elementsForMask, iBounds, fDrawAtlas->plotSize(), iDrawBounds, &dst);
+
+    SkIPoint topLeft = locator->topLeft();
+    *outPos = SkIPoint::Make(topLeft.x() + kEntryPadding, topLeft.y() + kEntryPadding);
 
     fDrawAtlas->setLastUseToken(*locator,
                                 recorder->priv().tokenTracker()->nextFlushToken());
diff --git a/src/gpu/graphite/RasterPathUtils.cpp b/src/gpu/graphite/RasterPathUtils.cpp
index 12100ef..fe8a85d 100644
--- a/src/gpu/graphite/RasterPathUtils.cpp
+++ b/src/gpu/graphite/RasterPathUtils.cpp
@@ -179,7 +179,8 @@
 }
 
 skgpu::UniqueKey GenerateClipMaskKey(uint32_t stackRecordID,
-                                     const ClipStack::ElementList* elementsForMask) {
+                                     const ClipStack::ElementList* elementsForMask,
+                                     SkIRect iBounds) {
     skgpu::UniqueKey maskKey;
     {
         static constexpr int kMaxShapeCountForKey = 2;
@@ -200,9 +201,23 @@
                 keySize += kXformKeySize + shapeKeySize;
             }
             if (canCreateKey) {
+                if (!iBounds.isEmpty()) {
+                    keySize += 2;
+                }
                 skgpu::UniqueKey::Builder builder(&maskKey, kDomain, keySize,
                                                   "Clip Path Mask");
                 int elementKeyIndex = 0;
+                if (!iBounds.isEmpty()) {
+                    SkASSERT(SkTFitsIn<int16_t>(iBounds.left()));
+                    SkASSERT(SkTFitsIn<int16_t>(iBounds.top()));
+                    SkASSERT(SkTFitsIn<int16_t>(iBounds.right()));
+                    SkASSERT(SkTFitsIn<int16_t>(iBounds.bottom()));
+
+                    builder[0] = iBounds.left() | (iBounds.top() << 16);
+                    builder[1] = iBounds.right() | (iBounds.bottom() << 16);
+                    elementKeyIndex += 2;
+                }
+
                 for (int i = 0; i < elementsForMask->size(); ++i) {
                     const ClipStack::Element* element = (*elementsForMask)[i];
 
diff --git a/src/gpu/graphite/RasterPathUtils.h b/src/gpu/graphite/RasterPathUtils.h
index 91953a5..9fb5011 100644
--- a/src/gpu/graphite/RasterPathUtils.h
+++ b/src/gpu/graphite/RasterPathUtils.h
@@ -74,7 +74,8 @@
                                      skvx::half2 maskSize);
 
 skgpu::UniqueKey GenerateClipMaskKey(uint32_t stackRecordID,
-                                     const ClipStack::ElementList* elementsForMask);
+                                     const ClipStack::ElementList* elementsForMask,
+                                     SkIRect iBounds);
 
 }  // namespace skgpu::graphite