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) {