ccpr: Don't use cache entries as path listeners

Allocates a separate "Key" object that holds the key and listens on the
path. This eliminates threading concerns and automatically fixes the
recycling logic.

Bug: skia:8452
Change-Id: I9b446ddf1d5da7bc44a8c1c3ff4c4efc0fdc97b1
Reviewed-on: https://skia-review.googlesource.com/c/166100
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Chris Dalton <csmartdalton@google.com>
diff --git a/src/gpu/ccpr/GrCCPathCache.h b/src/gpu/ccpr/GrCCPathCache.h
index c0e3cef..16f31d0 100644
--- a/src/gpu/ccpr/GrCCPathCache.h
+++ b/src/gpu/ccpr/GrCCPathCache.h
@@ -27,6 +27,42 @@
     GrCCPathCache();
     ~GrCCPathCache();
 
+    class Key : public SkPathRef::GenIDChangeListener {
+    public:
+        static sk_sp<Key> Make(uint32_t pathCacheUniqueID, int dataCountU32,
+                               const void* data = nullptr);
+
+        uint32_t pathCacheUniqueID() const { return fPathCacheUniqueID; }
+
+        int dataSizeInBytes() const { return fDataSizeInBytes; }
+        const uint32_t* data() const;
+
+        void resetDataCountU32(int dataCountU32) {
+            SkASSERT(dataCountU32 <= fDataReserveCountU32);
+            fDataSizeInBytes = dataCountU32 * sizeof(uint32_t);
+        }
+        uint32_t* data();
+
+        bool operator==(const Key&) const;
+
+        // Called when our corresponding path is modified or deleted. Not threadsafe.
+        void onChange() override;
+
+    private:
+        Key(uint32_t pathCacheUniqueID, int dataCountU32)
+                : fPathCacheUniqueID(pathCacheUniqueID)
+                , fDataSizeInBytes(dataCountU32 * sizeof(uint32_t))
+                SkDEBUGCODE(, fDataReserveCountU32(dataCountU32)) {
+            SkASSERT(SK_InvalidUniqueID != fPathCacheUniqueID);
+        }
+
+        const uint32_t fPathCacheUniqueID;
+        int fDataSizeInBytes;
+        SkDEBUGCODE(const int fDataReserveCountU32);
+        // The GrShape's unstyled key is stored as a variable-length footer to this class. GetKey
+        // provides access to it.
+    };
+
     // Stores the components of a transformation that affect a path mask (i.e. everything but
     // integer translation). During construction, any integer portions of the matrix's translate are
     // shaved off and returned to the caller. The caller is responsible for those integer shifts.
@@ -50,29 +86,20 @@
     sk_sp<GrCCPathCacheEntry> find(const GrShape&, const MaskTransform&,
                                    CreateIfAbsent = CreateIfAbsent::kNo);
 
-    void evict(GrCCPathCacheEntry*);
-
     void purgeAsNeeded();
 
 private:
-    // Wrapper around a raw GrShape key that has a specialized operator==. Used by the hash table.
-    struct HashKey {
-        const uint32_t* fData;
-    };
-    friend bool operator==(const HashKey&, const HashKey&);
-
     // This is a special ref ptr for GrCCPathCacheEntry, used by the hash table. It provides static
     // methods for SkTHash, and can only be moved. This guarantees the hash table holds exactly one
     // reference for each entry. Also, when a HashNode goes out of scope, that means it is exiting
     // the hash table. We take that opportunity to remove it from the LRU list and do some cleanup.
     class HashNode : SkNoncopyable {
     public:
-        static HashKey GetKey(const HashNode& node) { return GetKey(node.entry()); }
-        static HashKey GetKey(const GrCCPathCacheEntry*);
-        static uint32_t Hash(HashKey);
+        static const Key& GetKey(const HashNode&);
+        static uint32_t Hash(const Key&);
 
         HashNode() = default;
-        HashNode(GrCCPathCache*, const MaskTransform&, const GrShape&);
+        HashNode(GrCCPathCache*, sk_sp<Key>, const MaskTransform&, const GrShape&);
         HashNode(HashNode&& node)
                 : fPathCache(node.fPathCache), fEntry(std::move(node.fEntry)) {
             SkASSERT(!node.fEntry);
@@ -89,28 +116,30 @@
 
         GrCCPathCache* fPathCache = nullptr;
         sk_sp<GrCCPathCacheEntry> fEntry;
-        // The GrShape's unstyled key is stored as a variable-length footer to the 'fEntry'
-        // allocation. GetKey provides access to it.
     };
 
-    SkTHashTable<HashNode, HashKey> fHashTable;
-    SkTInternalLList<GrCCPathCacheEntry> fLRU;
-    SkMessageBus<sk_sp<GrCCPathCacheEntry>>::Inbox fInvalidatedEntriesInbox;
+    void evict(const GrCCPathCache::Key& key) {
+        fHashTable.remove(key);  // HashNode::willExitHashTable() takes care of the rest.
+    }
 
-    SkDEBUGCODE(SkThreadID fGraphicsThreadID = kIllegalThreadID);
+    SkTHashTable<HashNode, const GrCCPathCache::Key&> fHashTable;
+    SkTInternalLList<GrCCPathCacheEntry> fLRU;
+    SkMessageBus<sk_sp<Key>>::Inbox fInvalidatedKeysInbox;
+    sk_sp<Key> fScratchKey;  // Reused for creating a temporary key in the find() method.
 };
 
 /**
  * This class stores all the data necessary to draw a specific path + matrix combination from their
  * corresponding cached atlas.
  */
-class GrCCPathCacheEntry : public SkPathRef::GenIDChangeListener {
+class GrCCPathCacheEntry : public GrNonAtomicRef<GrCCPathCacheEntry> {
 public:
     SK_DECLARE_INTERNAL_LLIST_INTERFACE(GrCCPathCacheEntry);
 
-    ~GrCCPathCacheEntry() override;
-
-    uint32_t pathCacheUniqueID() const { return fPathCacheUniqueID; }
+    ~GrCCPathCacheEntry() {
+        SkASSERT(!fCurrFlushAtlas);  // Client is required to reset fCurrFlushAtlas back to null.
+        this->invalidateAtlas();
+    }
 
     // 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
@@ -162,16 +191,16 @@
 private:
     using MaskTransform = GrCCPathCache::MaskTransform;
 
-    GrCCPathCacheEntry(uint32_t pathCacheUniqueID, const MaskTransform&);
+    GrCCPathCacheEntry(sk_sp<GrCCPathCache::Key> cacheKey, const MaskTransform& maskTransform)
+            : fCacheKey(std::move(cacheKey)), fMaskTransform(maskTransform) {
+    }
 
     // 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. Not threadsafe.
-    void onChange() override;
+    sk_sp<GrCCPathCache::Key> fCacheKey;
 
-    const uint32_t fPathCacheUniqueID;
     MaskTransform fMaskTransform;
     int fHitCount = 1;
 
@@ -188,8 +217,6 @@
     // This field is for when a path gets drawn more than once during the same flush.
     const GrCCAtlas* fCurrFlushAtlas = nullptr;
 
-    SkDEBUGCODE(SkThreadID fGraphicsThreadID);
-
     friend class GrCCPathCache;
     friend void GrCCPathProcessor::Instance::set(const GrCCPathCacheEntry&, const SkIVector&,
                                                  GrColor, DoEvenOddFill);  // To access data.