ccpr: Recycle cache entries when possible to avoid malloc

Bug: skia:
Change-Id: Id06098f66ad6399c11707f8380597e7eeb392eec
Reviewed-on: https://skia-review.googlesource.com/138441
Commit-Queue: Chris Dalton <csmartdalton@google.com>
Reviewed-by: Robert Phillips <robertphillips@google.com>
diff --git a/src/gpu/ccpr/GrCCPathCache.cpp b/src/gpu/ccpr/GrCCPathCache.cpp
index b6eb82a..b22b1dd 100644
--- a/src/gpu/ccpr/GrCCPathCache.cpp
+++ b/src/gpu/ccpr/GrCCPathCache.cpp
@@ -78,19 +78,6 @@
     SkASSERT(fEntry->fCacheWeakPtr);
     fEntry->fCacheWeakPtr->fLRU.remove(fEntry);
     fEntry->fCacheWeakPtr = nullptr;
-
-    if (GrCCAtlas::CachedAtlasInfo* info = fEntry->fCachedAtlasInfo.get()) {
-        // Mark our own pixels invalid in the cached atlas texture now that we have been evicted.
-        info->fNumInvalidatedPathPixels += fEntry->height() * fEntry->width();
-        if (!info->fIsPurgedFromResourceCache &&
-            info->fNumInvalidatedPathPixels >= info->fNumPathPixels / 2) {
-            // Too many invalidated pixels: purge the atlas texture from the resource cache.
-            SkMessageBus<GrUniqueKeyInvalidatedMessage>::Post(
-                    GrUniqueKeyInvalidatedMessage(fEntry->fAtlasKey));
-            info->fIsPurgedFromResourceCache = true;
-        }
-    }
-
     fEntry->unref();
 }
 
@@ -115,9 +102,15 @@
         entry = node->entry();
         SkASSERT(this == entry->fCacheWeakPtr);
         if (fuzzy_equals(m, entry->fMaskTransform)) {
-            ++entry->fHitCount;
+            ++entry->fHitCount;  // The path was reused with a compatible matrix.
+        } else if (CreateIfAbsent::kYes == createIfAbsent && entry->unique()) {
+            // This entry is unique: we can recycle it instead of deleting and malloc-ing a new one.
+            entry->fMaskTransform = m;
+            entry->fHitCount = 1;
+            entry->invalidateAtlas();
+            SkASSERT(!entry->fCurrFlushAtlas);  // Should be null because 'entry' is unique.
         } else {
-            this->evict(entry);  // The path was reused with an incompatible matrix.
+            this->evict(entry);
             entry = nullptr;
         }
     }
@@ -148,6 +141,14 @@
     fHashTable.remove(HashNode::GetKey(entry));  // ~HashNode() handles the rest.
 }
 
+
+GrCCPathCacheEntry::~GrCCPathCacheEntry() {
+    SkASSERT(!fCacheWeakPtr);  // HashNode should have cleared our cache pointer.
+    SkASSERT(!fCurrFlushAtlas);  // Client is required to reset fCurrFlushAtlas back to null.
+
+    this->invalidateAtlas();
+}
+
 void GrCCPathCacheEntry::initAsStashedAtlas(const GrUniqueKey& atlasKey,
                                             const SkIVector& atlasOffset, const SkRect& devBounds,
                                             const SkRect& devBounds45, const SkIRect& devIBounds,
@@ -179,6 +180,23 @@
     fCachedAtlasInfo->fNumPathPixels += this->height() * this->width();
 }
 
+void GrCCPathCacheEntry::invalidateAtlas() {
+    if (fCachedAtlasInfo) {
+        // Mark our own pixels invalid in the cached atlas texture.
+        fCachedAtlasInfo->fNumInvalidatedPathPixels += this->height() * this->width();
+        if (!fCachedAtlasInfo->fIsPurgedFromResourceCache &&
+            fCachedAtlasInfo->fNumInvalidatedPathPixels >= fCachedAtlasInfo->fNumPathPixels / 2) {
+            // Too many invalidated pixels: purge the atlas texture from the resource cache.
+            SkMessageBus<GrUniqueKeyInvalidatedMessage>::Post(
+                    GrUniqueKeyInvalidatedMessage(fAtlasKey));
+            fCachedAtlasInfo->fIsPurgedFromResourceCache = true;
+        }
+    }
+
+    fAtlasKey.reset();
+    fCachedAtlasInfo = nullptr;
+}
+
 void GrCCPathCacheEntry::onChange() {
     // Our corresponding path was modified or deleted. Evict ourselves.
     if (fCacheWeakPtr) {
diff --git a/src/gpu/ccpr/GrCCPathCache.h b/src/gpu/ccpr/GrCCPathCache.h
index 6315f7c..67e7d9f 100644
--- a/src/gpu/ccpr/GrCCPathCache.h
+++ b/src/gpu/ccpr/GrCCPathCache.h
@@ -100,6 +100,8 @@
 public:
     SK_DECLARE_INTERNAL_LLIST_INTERFACE(GrCCPathCacheEntry);
 
+    ~GrCCPathCacheEntry() override;
+
     // The number of times this specific entry (path + matrix combination) has been pulled from
     // the path cache. As long as the caller does exactly one lookup per draw, this translates to
     // the number of times the path has been drawn with a compatible matrix.
@@ -153,11 +155,15 @@
     GrCCPathCacheEntry(GrCCPathCache* cache, const MaskTransform& m)
             : fCacheWeakPtr(cache), fMaskTransform(m) {}
 
+    // Resets this entry back to not having an atlas, and purges its previous atlas texture from the
+    // resource cache if needed.
+    void invalidateAtlas();
+
     // Called when our corresponding path is modified or deleted.
     void onChange() override;
 
     GrCCPathCache* fCacheWeakPtr;  // Gets manually reset to null by the path cache upon eviction.
-    const MaskTransform fMaskTransform;
+    MaskTransform fMaskTransform;
     int fHitCount = 1;
 
     GrUniqueKey fAtlasKey;