Use GrResourceCache2 to service content key lookups

BUG=skia:2889

Review URL: https://codereview.chromium.org/707493002
diff --git a/src/core/SkTMultiMap.h b/src/core/SkTMultiMap.h
index 70076f0..1168ed6 100644
--- a/src/core/SkTMultiMap.h
+++ b/src/core/SkTMultiMap.h
@@ -102,6 +102,19 @@
 
     int count() const { return fCount; }
 
+#ifdef SK_DEBUG
+    // This is not particularly fast and only used for validation, so debug only.
+    int countForKey(const Key& key) const {
+        int count = 0;
+        ValueList* list = fHash.find(key);
+        while (list) {
+            list = list->fNext;
+            ++count;
+        }
+        return count;
+    }
+#endif
+
 private:
     SkTDynamicHash<ValueList, Key> fHash;
     int fCount;
diff --git a/src/gpu/GrContext.cpp b/src/gpu/GrContext.cpp
index 83ef58f..6f9395b 100755
--- a/src/gpu/GrContext.cpp
+++ b/src/gpu/GrContext.cpp
@@ -255,20 +255,20 @@
                                         const GrCacheID& cacheID,
                                         const GrTextureParams* params) {
     GrResourceKey resourceKey = GrTexturePriv::ComputeKey(fGpu, params, desc, cacheID);
-    GrGpuResource* resource = fResourceCache->find(resourceKey);
+
+    GrGpuResource* resource = this->findAndRefCachedResource(resourceKey);
     if (resource) {
-        resource->ref();
+        SkASSERT(static_cast<GrSurface*>(resource)->asTexture());
         return static_cast<GrSurface*>(resource)->asTexture();
-    } else {
-        return NULL;
     }
+    return NULL;
 }
 
 bool GrContext::isTextureInCache(const GrSurfaceDesc& desc,
                                  const GrCacheID& cacheID,
                                  const GrTextureParams* params) const {
     GrResourceKey resourceKey = GrTexturePriv::ComputeKey(fGpu, params, desc, cacheID);
-    return fResourceCache->hasKey(resourceKey);
+    return fResourceCache2->hasContentKey(resourceKey);
 }
 
 void GrContext::addStencilBuffer(GrStencilBuffer* sb) {
@@ -280,12 +280,9 @@
     fResourceCache->addResource(resourceKey, sb);
 }
 
-GrStencilBuffer* GrContext::findStencilBuffer(int width, int height,
-                                              int sampleCnt) {
-    GrResourceKey resourceKey = GrStencilBuffer::ComputeKey(width,
-                                                            height,
-                                                            sampleCnt);
-    GrGpuResource* resource = fResourceCache->find(resourceKey);
+GrStencilBuffer* GrContext::findAndRefStencilBuffer(int width, int height, int sampleCnt) {
+    GrResourceKey resourceKey = GrStencilBuffer::ComputeKey(width, height, sampleCnt);
+    GrGpuResource* resource = this->findAndRefCachedResource(resourceKey);
     return static_cast<GrStencilBuffer*>(resource);
 }
 
@@ -1755,8 +1752,10 @@
 }
 
 GrGpuResource* GrContext::findAndRefCachedResource(const GrResourceKey& resourceKey) {
-    GrGpuResource* resource = fResourceCache->find(resourceKey);
-    SkSafeRef(resource);
+    GrGpuResource* resource = fResourceCache2->findAndRefContentResource(resourceKey);
+    if (resource) {
+        fResourceCache->makeResourceMRU(resource);
+    }
     return resource;
 }
 
diff --git a/src/gpu/GrGpu.cpp b/src/gpu/GrGpu.cpp
index 64ee29b..ab24c54 100644
--- a/src/gpu/GrGpu.cpp
+++ b/src/gpu/GrGpu.cpp
@@ -84,10 +84,8 @@
 
 bool GrGpu::attachStencilBufferToRenderTarget(GrRenderTarget* rt) {
     SkASSERT(NULL == rt->getStencilBuffer());
-    GrStencilBuffer* sb =
-        this->getContext()->findStencilBuffer(rt->width(),
-                                              rt->height(),
-                                              rt->numSamples());
+    SkAutoTUnref<GrStencilBuffer> sb(
+        this->getContext()->findAndRefStencilBuffer(rt->width(), rt->height(), rt->numSamples()));
     if (sb) {
         rt->setStencilBuffer(sb);
         bool attached = this->attachStencilBufferToRenderTarget(sb, rt);
@@ -96,8 +94,7 @@
         }
         return attached;
     }
-    if (this->createStencilBufferForRenderTarget(rt,
-                                                 rt->width(), rt->height())) {
+    if (this->createStencilBufferForRenderTarget(rt, rt->width(), rt->height())) {
         // Right now we're clearing the stencil buffer here after it is
         // attached to an RT for the first time. When we start matching
         // stencil buffers with smaller color targets this will no longer
diff --git a/src/gpu/GrGpuResource.cpp b/src/gpu/GrGpuResource.cpp
index 705cdea..b77acff 100644
--- a/src/gpu/GrGpuResource.cpp
+++ b/src/gpu/GrGpuResource.cpp
@@ -78,6 +78,24 @@
     }
 }
 
+bool GrGpuResource::setCacheEntry(GrResourceCacheEntry* cacheEntry) {
+    // GrResourceCache never changes the cacheEntry once one has been added.
+    SkASSERT(NULL == cacheEntry || NULL == fCacheEntry);
+
+    fCacheEntry = cacheEntry;
+    if (this->wasDestroyed() || NULL == cacheEntry) {
+        return true;
+    }
+
+    if (!cacheEntry->key().isScratch()) {
+        if (!get_resource_cache2(fGpu)->didAddContentKey(this)) {
+            fCacheEntry = NULL;
+            return false;
+        }
+    }
+    return true;
+}
+
 void GrGpuResource::notifyIsPurgable() const {
     if (fCacheEntry && !this->wasDestroyed()) {
         get_resource_cache(fGpu)->notifyPurgable(this);
@@ -92,6 +110,7 @@
 }
 
 const GrResourceKey* GrGpuResource::getContentKey() const {
+    // Currently scratch resources have a cache entry in GrResourceCache with a scratch key.
     if (fCacheEntry && !fCacheEntry->key().isScratch()) {
         return &fCacheEntry->key();
     }
@@ -99,8 +118,8 @@
 }
 
 bool GrGpuResource::isScratch() const {
-    // Currently scratch resources have a cache entry in GrResourceCache with a scratch key.
-    return NULL != fCacheEntry && fCacheEntry->key().isScratch();
+    SkASSERT(fScratchKey.isScratch());
+    return NULL == this->getContentKey() && !fScratchKey.isNullScratch();
 }
 
 uint32_t GrGpuResource::CreateUniqueID() {
diff --git a/src/gpu/GrResourceCache.cpp b/src/gpu/GrResourceCache.cpp
index 9754d44..8eed4d4 100644
--- a/src/gpu/GrResourceCache.cpp
+++ b/src/gpu/GrResourceCache.cpp
@@ -49,7 +49,8 @@
 }
 
 GrResourceCacheEntry::~GrResourceCacheEntry() {
-    fResource->setCacheEntry(NULL);
+    // We're relying on having the cache entry to remove this from GrResourceCache2's content hash.
+    // fResource->setCacheEntry(NULL);
     fResource->unref();
 }
 
@@ -185,26 +186,11 @@
     }
 }
 
-GrGpuResource* GrResourceCache::find(const GrResourceKey& key) {
-    // GrResourceCache2 is responsible for scratch resources.
-    SkASSERT(!key.isScratch());
-
-    GrAutoResourceCacheValidate atcv(this);
-
-    GrResourceCacheEntry* entry = fCache.find(key);
-    if (NULL == entry) {
-        return NULL;
+bool GrResourceCache::addResource(const GrResourceKey& key, GrGpuResource* resource) {
+    if (NULL != resource->getCacheEntry()) {
+        return false;
     }
 
-    // Make this resource MRU
-    this->internalDetach(entry);
-    this->attachToHead(entry);
-
-    return entry->fResource;
-}
-
-void GrResourceCache::addResource(const GrResourceKey& key, GrGpuResource* resource) {
-    SkASSERT(NULL == resource->getCacheEntry());
     // we don't expect to create new resources during a purge. In theory
     // this could cause purgeAsNeeded() into an infinite loop (e.g.
     // each resource destroyed creates and locks 2 resources and
@@ -213,12 +199,16 @@
     GrAutoResourceCacheValidate atcv(this);
 
     GrResourceCacheEntry* entry = SkNEW_ARGS(GrResourceCacheEntry, (this, key, resource));
-    resource->setCacheEntry(entry);
+    if (!resource->setCacheEntry(entry)) {
+        SkDELETE(entry);
+        this->purgeAsNeeded();
+        return false;
+    }
 
     this->attachToHead(entry);
     fCache.insert(key, entry);
-
     this->purgeAsNeeded();
+    return true;
 }
 
 void GrResourceCache::didIncreaseResourceSize(const GrResourceCacheEntry* entry, size_t amountInc) {
diff --git a/src/gpu/GrResourceCache.h b/src/gpu/GrResourceCache.h
index f6d064a..874f16a 100644
--- a/src/gpu/GrResourceCache.h
+++ b/src/gpu/GrResourceCache.h
@@ -140,12 +140,6 @@
      */
     int getCachedResourceCount() const { return fEntryCount; }
 
-    /**
-     *  Search for an entry with the same Key. If found, return it.
-     *  If not found, return null.
-     */
-    GrGpuResource* find(const GrResourceKey& key);
-
     void makeResourceMRU(GrGpuResource*);
 
     /** Called by GrGpuResources when they detects that they are newly purgable. */
@@ -157,14 +151,11 @@
      *
      *  Ownership of the resource is transferred to the resource cache,
      *  which will unref() it when it is purged or deleted.
+     *
+     *  This can fail if the key is already taken, or the resource is already in
+     *  the cache.
      */
-    void addResource(const GrResourceKey& key, GrGpuResource* resource);
-
-    /**
-     * Determines if the cache contains an entry matching a key. If a matching
-     * entry exists but was detached then it will not be found.
-     */
-    bool hasKey(const GrResourceKey& key) const { return SkToBool(fCache.find(key)); }
+    bool addResource(const GrResourceKey& key, GrGpuResource* resource);
 
     /**
      * Notify the cache that the size of a resource has changed.
diff --git a/src/gpu/GrResourceCache2.cpp b/src/gpu/GrResourceCache2.cpp
index 6bc23a3..65e522a 100644
--- a/src/gpu/GrResourceCache2.cpp
+++ b/src/gpu/GrResourceCache2.cpp
@@ -9,7 +9,6 @@
 
 #include "GrResourceCache2.h"
 #include "GrGpuResource.h"  
-#include "SkRefCnt.h"
 
 GrResourceCache2::~GrResourceCache2() {
     this->releaseAll();
@@ -22,6 +21,8 @@
     fResources.addToHead(resource);
     ++fCount;
     if (!resource->getScratchKey().isNullScratch()) {
+        // TODO(bsalomon): Make this assertion possible.
+        // SkASSERT(!resource->isWrapped());
         fScratchMap.insert(resource->getScratchKey(), resource);
     }
 }
@@ -32,6 +33,9 @@
     if (!resource->getScratchKey().isNullScratch()) {
         fScratchMap.remove(resource->getScratchKey(), resource);
     }
+    if (const GrResourceKey* contentKey = resource->getContentKey()) {
+        fContentHash.remove(*contentKey);
+    }
     --fCount;
 }
 
@@ -43,6 +47,7 @@
         SkASSERT(head != fResources.head());
     }
     SkASSERT(!fScratchMap.count());
+    SkASSERT(!fContentHash.count());
     SkASSERT(!fCount);
 }
 
@@ -89,3 +94,25 @@
     }
     return SkSafeRef(fScratchMap.find(scratchKey, AvailableForScratchUse(false)));
 }
+
+void GrResourceCache2::willRemoveContentKey(const GrGpuResource* resource) {
+    SkASSERT(resource);
+    SkASSERT(resource->getContentKey());
+    SkDEBUGCODE(GrGpuResource* res = fContentHash.find(*resource->getContentKey()));
+    SkASSERT(res == resource);
+
+    fContentHash.remove(*resource->getContentKey());
+}
+
+bool GrResourceCache2::didAddContentKey(GrGpuResource* resource) {
+    SkASSERT(resource);
+    SkASSERT(resource->getContentKey());
+
+    GrGpuResource* res = fContentHash.find(*resource->getContentKey());
+    if (NULL != res) {
+        return false;
+    }
+
+    fContentHash.add(resource);
+    return true;
+}
diff --git a/src/gpu/GrResourceCache2.h b/src/gpu/GrResourceCache2.h
index a365a5a..9424c40 100644
--- a/src/gpu/GrResourceCache2.h
+++ b/src/gpu/GrResourceCache2.h
@@ -11,6 +11,7 @@
 
 #include "GrGpuResource.h"
 #include "GrResourceKey.h"
+#include "SkRefCnt.h"
 #include "SkTInternalLList.h"
 #include "SkTMultiMap.h"
 
@@ -28,6 +29,14 @@
 
     void removeResource(GrGpuResource*);
 
+    void willRemoveContentKey(const GrGpuResource*);
+
+    // This currently returns a bool and fails when an existing resource has a key that collides
+    // with the new content key. In the future it will null out the content key for the existing
+    // resource. The failure is a temporary measure taken because duties are split between two
+    // cache objects currently.
+    bool didAddContentKey(GrGpuResource*);
+
     void abandonAll();
 
     void releaseAll();
@@ -39,6 +48,24 @@
         kRequireNoPendingIO_ScratchFlag = 0x2,
     };
     GrGpuResource* findAndRefScratchResource(const GrResourceKey& scratchKey, uint32_t flags = 0);
+    
+#ifdef SK_DEBUG
+    // This is not particularly fast and only used for validation, so debug only.
+    int countScratchEntriesForKey(const GrResourceKey& scratchKey) const {
+        SkASSERT(scratchKey.isScratch());
+        return fScratchMap.countForKey(scratchKey);
+    }
+#endif
+
+    GrGpuResource* findAndRefContentResource(const GrResourceKey& contentKey) {
+        SkASSERT(!contentKey.isScratch());
+        return SkSafeRef(fContentHash.find(contentKey));
+    }
+
+    bool hasContentKey(const GrResourceKey& contentKey) const {
+        SkASSERT(!contentKey.isScratch());
+        return SkToBool(fContentHash.find(contentKey));
+    }
 
 private:
 #ifdef SK_DEBUG
@@ -56,10 +83,21 @@
     };
     typedef SkTMultiMap<GrGpuResource, GrResourceKey, ScratchMapTraits> ScratchMap;
 
+    struct ContentHashTraits {
+        static const GrResourceKey& GetKey(const GrGpuResource& r) {
+            return *r.getContentKey();
+        }
+
+        static uint32_t Hash(const GrResourceKey& key) { return key.getHash(); }
+    };
+    typedef SkTDynamicHash<GrGpuResource, GrResourceKey, ContentHashTraits> ContentHash;
+
     int                                 fCount;
     SkTInternalLList<GrGpuResource>     fResources;
     // This map holds all resources that can be used as scratch resources.
-    ScratchMap                          fScratchMap; 
+    ScratchMap                          fScratchMap;
+    // This holds all resources that have content keys.
+    ContentHash                         fContentHash;
 };
 
 #endif
diff --git a/src/gpu/GrTexture.cpp b/src/gpu/GrTexture.cpp
index 2139540..72e8225 100644
--- a/src/gpu/GrTexture.cpp
+++ b/src/gpu/GrTexture.cpp
@@ -121,7 +121,10 @@
 GrTexture::GrTexture(GrGpu* gpu, bool isWrapped, const GrSurfaceDesc& desc)
     : INHERITED(gpu, isWrapped, desc)
     , fMipMapsStatus(kNotAllocated_MipMapsStatus) {
-    this->setScratchKey(GrTexturePriv::ComputeScratchKey(desc));
+
+    if (!isWrapped) {
+        this->setScratchKey(GrTexturePriv::ComputeScratchKey(desc));
+    }
     // only make sense if alloc size is pow2
     fShiftFixedX = 31 - SkCLZ(fDesc.fWidth);
     fShiftFixedY = 31 - SkCLZ(fDesc.fHeight);