Scratch textures are no longer removed from the cache in Debug

http://codereview.appspot.com/6465079/



git-svn-id: http://skia.googlecode.com/svn/trunk@5221 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/include/gpu/GrContext.h b/include/gpu/GrContext.h
index 077493f..9fb47e5 100644
--- a/include/gpu/GrContext.h
+++ b/include/gpu/GrContext.h
@@ -179,17 +179,6 @@
     void unlockTexture(GrTexture* texture);
 
     /**
-     * Free any data associated with the provided entry in the texture cache.
-     * Currently this entry point is only used when a scratch texture is 
-     * detached from the cache. In this case the GrResourceEntry* associated
-     * with the texture needs to be freed since it will be re-allocated when
-     * the texture is re-added. This entry point will be removed soon since the
-     * texture can now carry around a pointer to its GrResourceEntry* (and
-     * will eventually take over its functionality).
-     */
-    void freeEntry(GrTexture* texture);
-
-    /**
      * Creates a texture that is outside the cache. Does not count against
      * cache's budget.
      */
@@ -858,9 +847,10 @@
     GrTexture* detach() {
         GrTexture* temp = fTexture;
 
-        // freeEntry will remove the texture cache's ref
-        temp->ref();
-        fContext->freeEntry(fTexture);
+        // Conceptually the texture's cache entry loses its ref to the
+        // texture while the caller of this method gets a ref.
+        GrAssert(NULL != temp->getCacheEntry());
+
         fTexture = NULL;
 
         temp->setFlag((GrTextureFlags) GrTexture::kReturnToCache_FlagBit);
diff --git a/src/gpu/GrContext.cpp b/src/gpu/GrContext.cpp
index b517a54..66da559 100644
--- a/src/gpu/GrContext.cpp
+++ b/src/gpu/GrContext.cpp
@@ -470,10 +470,11 @@
 
     // If the caller gives us the same desc/sampler twice we don't want
     // to return the same texture the second time (unless it was previously
-    // released). So we detach the entry from the cache and reattach at release.
+    // released). So make it exclusive to hide it from future searches.
     if (NULL != resource) {
-        fTextureCache->detach(resource->getCacheEntry());
+        fTextureCache->makeExclusive(resource->getCacheEntry());
     }
+
     return static_cast<GrTexture*>(resource);
 }
 
@@ -483,14 +484,20 @@
         return;
     }
 
-    // 'texture' is a scratch texture returning to the fold
-    GrCacheData cacheData(GrCacheData::kScratch_CacheID);
+    // This texture should already have a cache entry since it was once
+    // attached
+    GrAssert(NULL != texture->getCacheEntry());
 
-    GrResourceKey key = GrTexture::ComputeKey(fGpu, NULL,
-                                              texture->desc(),
-                                              cacheData,
-                                              true);
-    fTextureCache->attach(key, texture);
+    // Conceptually, the cache entry is going to assume responsibility
+    // for the creation ref.
+    GrAssert(1 == texture->getRefCnt());
+
+    // Since this texture came from an AutoScratchTexture it should
+    // still be in the exclusive pile
+    fTextureCache->makeNonExclusive(texture->getCacheEntry());
+
+    // and it should still be locked
+    fTextureCache->unlock(texture->getCacheEntry());
 }
 
 void GrContext::unlockTexture(GrTexture* texture) {
@@ -501,18 +508,10 @@
     // while it was locked (to avoid two callers simultaneously getting
     // the same texture).
     if (GrTexture::IsScratchTexture(texture->getCacheEntry()->key())) {
-        fTextureCache->reattachAndUnlock(texture->getCacheEntry());
-    } else {
-        fTextureCache->unlock(texture->getCacheEntry());
+        fTextureCache->makeNonExclusive(texture->getCacheEntry());
     }
-}
 
-void GrContext::freeEntry(GrTexture* texture) {
-    ASSERT_OWNED_RESOURCE(texture);
-    GrAssert(NULL != texture->getCacheEntry());
-
-    fTextureCache->freeEntry(texture->getCacheEntry());
-    texture->setCacheEntry(NULL);
+    fTextureCache->unlock(texture->getCacheEntry());
 }
 
 GrTexture* GrContext::createUncachedTexture(const GrTextureDesc& descIn,
diff --git a/src/gpu/GrResourceCache.cpp b/src/gpu/GrResourceCache.cpp
index 7e0b892..be79624 100644
--- a/src/gpu/GrResourceCache.cpp
+++ b/src/gpu/GrResourceCache.cpp
@@ -21,6 +21,7 @@
 }
 
 GrResourceEntry::~GrResourceEntry() {
+    fResource->setCacheEntry(NULL);
     fResource->unref();
 }
 
@@ -52,7 +53,6 @@
     fClientDetachedCount          = 0;
     fClientDetachedBytes          = 0;
 
-    fHead = fTail = NULL;
     fPurging = false;
 }
 
@@ -84,26 +84,12 @@
 
 void GrResourceCache::internalDetach(GrResourceEntry* entry,
                                     bool clientDetach) {
-    GrResourceEntry* prev = entry->fPrev;
-    GrResourceEntry* next = entry->fNext;
+    fList.remove(entry);
 
-    if (prev) {
-        prev->fNext = next;
-    } else {
-        fHead = next;
-    }
-    if (next) {
-        next->fPrev = prev;
-    } else {
-        fTail = prev;
-    }
     if (!entry->isLocked()) {
         --fUnlockedEntryCount;
     }
 
-    entry->fPrev = NULL;
-    entry->fNext = NULL;
-
     // update our stats
     if (clientDetach) {
         fClientDetachedCount += 1;
@@ -126,15 +112,8 @@
 
 void GrResourceCache::attachToHead(GrResourceEntry* entry,
                                   bool clientReattach) {
-    entry->fPrev = NULL;
-    entry->fNext = fHead;
-    if (fHead) {
-        fHead->fPrev = entry;
-    }
-    fHead = entry;
-    if (NULL == fTail) {
-        fTail = entry;
-    }
+    fList.addToHead(entry);
+
     if (!entry->isLocked()) {
         ++fUnlockedEntryCount;
 #if GR_DEBUG
@@ -227,7 +206,6 @@
     GrAutoResourceCacheValidate atcv(this);
 
     GrResourceEntry* entry = SkNEW_ARGS(GrResourceEntry, (key, resource));
-
     resource->setCacheEntry(entry);
 
     if (lock) {
@@ -260,35 +238,43 @@
     this->create(key, resource, false, true);
 }
 
-void GrResourceCache::detach(GrResourceEntry* entry) {
+void GrResourceCache::makeExclusive(GrResourceEntry* entry) {
     GrAutoResourceCacheValidate atcv(this);
+
     this->internalDetach(entry, true);
     fCache.remove(entry->fKey, entry);
+
+#if GR_DEBUG
+    fExclusiveList.addToHead(entry);
+#endif
 }
 
-void GrResourceCache::freeEntry(GrResourceEntry* entry) {
-    GrAssert(NULL == entry->fNext && NULL == entry->fPrev);
-    delete entry;
+void GrResourceCache::removeInvalidResource(GrResourceEntry* entry) {
+    // If the resource went invalid while it was detached then purge it
+    // This can happen when a 3D context was lost,
+    // the client called GrContext::contextDestroyed() to notify Gr,
+    // and then later an SkGpuDevice's destructor releases its backing
+    // texture (which was invalidated at contextDestroyed time).
+    fClientDetachedCount -= 1;
+    fEntryCount -= 1;
+    size_t size = entry->resource()->sizeInBytes();
+    fClientDetachedBytes -= size;
+    fEntryBytes -= size;
 }
 
-void GrResourceCache::reattachAndUnlock(GrResourceEntry* entry) {
+void GrResourceCache::makeNonExclusive(GrResourceEntry* entry) {
     GrAutoResourceCacheValidate atcv(this);
+
+#if GR_DEBUG
+    fExclusiveList.remove(entry);
+#endif
+
     if (entry->resource()->isValid()) {
         attachToHead(entry, true);
         fCache.insert(entry->key(), entry);
     } else {
-        // If the resource went invalid while it was detached then purge it
-        // This can happen when a 3D context was lost,
-        // the client called GrContext::contextDestroyed() to notify Gr,
-        // and then later an SkGpuDevice's destructor releases its backing
-        // texture (which was invalidated at contextDestroyed time).
-        fClientDetachedCount -= 1;
-        fEntryCount -= 1;
-        size_t size = entry->resource()->sizeInBytes();
-        fClientDetachedBytes -= size;
-        fEntryBytes -= size;
+        this->removeInvalidResource(entry);
     }
-    this->unlock(entry);
 }
 
 void GrResourceCache::unlock(GrResourceEntry* entry) {
@@ -325,7 +311,7 @@
         fPurging = true;
         bool withinBudget = false;
         do {
-            GrResourceEntry* entry = fTail;
+            GrResourceEntry* entry = fList.fTail;
             while (entry && fUnlockedEntryCount) {
                 GrAutoResourceCacheValidate atcv(this);
                 if (fEntryCount <= fMaxCount && fEntryBytes <= fMaxBytes) {
@@ -347,6 +333,7 @@
                              entry->resource()->width(),
                              entry->resource()->height());
         #endif
+
                     delete entry;
                 }
                 entry = prev;
@@ -370,6 +357,8 @@
     this->purgeAsNeeded();
 
 #if GR_DEBUG
+    GrAssert(fExclusiveList.countEntries() == fClientDetachedCount);
+    GrAssert(fExclusiveList.countBytes() == fClientDetachedBytes);
     GrAssert(!fUnlockedEntryCount);
     if (!fCache.count()) {
         // Items may have been detached from the cache (such as the backing
@@ -377,8 +366,7 @@
         // them.
         GrAssert(fEntryCount == fClientDetachedCount);
         GrAssert(fEntryBytes == fClientDetachedBytes);
-        GrAssert(NULL == fHead);
-        GrAssert(NULL == fTail);
+        GrAssert(fList.isEmpty());
     }
 #endif
 
@@ -401,14 +389,12 @@
     return count;
 }
 
-#if GR_DEBUG
 static bool both_zero_or_nonzero(int count, size_t bytes) {
     return (count == 0 && bytes == 0) || (count > 0 && bytes > 0);
 }
-#endif
 
 void GrResourceCache::validate() const {
-    GrAssert(!fHead == !fTail);
+    GrAssert(!fList.fHead == !fList.fTail);
     GrAssert(both_zero_or_nonzero(fEntryCount, fEntryBytes));
     GrAssert(both_zero_or_nonzero(fClientDetachedCount, fClientDetachedBytes));
     GrAssert(fClientDetachedBytes <= fEntryBytes);
@@ -417,32 +403,34 @@
 
     fCache.validate();
 
-    GrResourceEntry* entry = fHead;
+    GrResourceEntry* entry = fList.fHead;
     int count = 0;
     int unlockCount = 0;
-    size_t bytes = 0;
     while (entry) {
         entry->validate();
         GrAssert(fCache.find(entry->key()));
         count += 1;
-        bytes += entry->resource()->sizeInBytes();
         if (!entry->isLocked()) {
             unlockCount += 1;
         }
         entry = entry->fNext;
     }
     GrAssert(count == fEntryCount - fClientDetachedCount);
+
+    size_t bytes = fList.countBytes();
     GrAssert(bytes == fEntryBytes  - fClientDetachedBytes);
+
+    bytes = fExclusiveList.countBytes();
+    GrAssert(bytes == fClientDetachedBytes);
+
     GrAssert(unlockCount == fUnlockedEntryCount);
 
-    count = 0;
-    for (entry = fTail; entry; entry = entry->fPrev) {
-        count += 1;
-    }
-    GrAssert(count == fEntryCount - fClientDetachedCount);
+    GrAssert(fList.countEntries() == fEntryCount - fClientDetachedCount);
+
+    GrAssert(fExclusiveList.countEntries() == fClientDetachedCount);
 
     for (int i = 0; i < count; i++) {
-        int matches = countMatches(fHead, fCache.getArray()[i]);
+        int matches = countMatches(fList.fHead, fCache.getArray()[i]);
         GrAssert(1 == matches);
     }
 }
@@ -462,3 +450,70 @@
 }
 
 #endif
+
+///////////////////////////////////////////////////////////////////////////////
+void GrDLinkedList::remove(GrResourceEntry* entry) {
+    GrAssert(this->isInList(entry));
+
+    GrResourceEntry* prev = entry->fPrev;
+    GrResourceEntry* next = entry->fNext;
+
+    if (prev) {
+        prev->fNext = next;
+    } else {
+        fHead = next;
+    }
+    if (next) {
+        next->fPrev = prev;
+    } else {
+        fTail = prev;
+    }
+
+    entry->fPrev = NULL;
+    entry->fNext = NULL;
+}
+
+void GrDLinkedList::addToHead(GrResourceEntry* entry) {
+    GrAssert(NULL == entry->fPrev && NULL == entry->fNext);
+
+    entry->fPrev = NULL;
+    entry->fNext = fHead;
+    if (fHead) {
+        fHead->fPrev = entry;
+    }
+    fHead = entry;
+    if (NULL == fTail) {
+        fTail = entry;
+    }
+}
+
+#if GR_DEBUG
+
+bool GrDLinkedList::isInList(const GrResourceEntry* entry) const {
+
+    for (GrResourceEntry* cur = fHead; NULL != cur; cur = cur->fNext) {
+        if (entry == cur) {
+            return true;
+        }
+    }
+
+    return false;
+}
+
+int GrDLinkedList::countEntries() const {
+    int count = 0;
+    for (GrResourceEntry* entry = fTail; NULL != entry; entry = entry->fPrev) {
+        ++count;
+    }
+    return count;
+}
+
+size_t GrDLinkedList::countBytes() const {
+    size_t bytes = 0;
+    for (GrResourceEntry* entry = fTail; NULL != entry; entry = entry->fPrev) {
+        bytes += entry->resource()->sizeInBytes();
+    }
+    return bytes;
+}
+
+#endif // GR_DEBUG
diff --git a/src/gpu/GrResourceCache.h b/src/gpu/GrResourceCache.h
index 838b3b8..7b5870e 100644
--- a/src/gpu/GrResourceCache.h
+++ b/src/gpu/GrResourceCache.h
@@ -157,12 +157,34 @@
     GrResourceEntry* fNext;
 
     friend class GrResourceCache;
+    friend class GrDLinkedList;
 };
 
 ///////////////////////////////////////////////////////////////////////////////
 
 #include "GrTHashCache.h"
 
+class GrDLinkedList {
+public:
+    GrDLinkedList()
+        : fHead(NULL)
+        , fTail(NULL) {
+    }
+
+    void remove(GrResourceEntry* entry);
+    void addToHead(GrResourceEntry* entry);
+
+#ifdef GR_DEBUG
+    bool isInList(const GrResourceEntry* entry) const;
+    bool isEmpty() const { return NULL == fHead && NULL == fTail; }
+    int countEntries() const;
+    size_t countBytes() const;
+#endif
+
+    GrResourceEntry* fHead;
+    GrResourceEntry* fTail;
+};
+
 /**
  *  Cache of GrResource objects.
  *
@@ -254,21 +276,18 @@
     bool hasKey(const GrResourceKey& key) const;
 
     /**
-     * Detach removes an entry from the cache. This prevents the entry from
-     * being found by a subsequent findAndLock() until it is reattached. The
-     * entry still counts against the cache's budget and should be reattached
-     * when exclusive access is no longer needed.
+     * Hide 'entry' so that future searches will not find it. Such
+     * hidden entries will not be purged. The entry still counts against
+     * the cache's budget and should be made non-exclusive when exclusive access
+     * is no longer needed.
      */
-    void detach(GrResourceEntry*);
-
-    void freeEntry(GrResourceEntry* entry);
+    void makeExclusive(GrResourceEntry* entry);
 
     /**
-     * Reattaches a resource to the cache and unlocks it. Allows it to be found
-     * by a subsequent findAndLock or be purged (provided its lock count is
-     * now 0.)
+     * Restore 'entry' so that it can be found by future searches. 'entry'
+     * will also be purgeable (provided its lock count is now 0.)
      */
-    void reattachAndUnlock(GrResourceEntry*);
+    void makeNonExclusive(GrResourceEntry* entry);
 
     /**
      *  When done with an entry, call unlock(entry) on it, which returns it to
@@ -290,12 +309,18 @@
     void attachToHead(GrResourceEntry*, bool);
     void purgeAsNeeded();
 
+    void removeInvalidResource(GrResourceEntry* entry);
+
     class Key;
     GrTHashTable<GrResourceEntry, Key, 8> fCache;
 
     // manage the dlink list
-    GrResourceEntry* fHead;
-    GrResourceEntry* fTail;
+    GrDLinkedList    fList;
+
+#if GR_DEBUG
+    // These objects cannot be returned by a search
+    GrDLinkedList    fExclusiveList;
+#endif
 
     // our budget, used in purgeAsNeeded()
     int fMaxCount;