AutoScratchTexture can now release its texture and it will return to the texture cache when freed

http://codereview.appspot.com/6262043/



git-svn-id: http://skia.googlecode.com/svn/trunk@4301 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/gpu/GrContext.cpp b/src/gpu/GrContext.cpp
index 1fda26b..fc2b143 100644
--- a/src/gpu/GrContext.cpp
+++ b/src/gpu/GrContext.cpp
@@ -520,6 +520,18 @@
     return TextureCacheEntry(entry);
 }
 
+void GrContext::addExistingTextureToCache(GrTexture* texture) {
+
+    if (NULL == texture) {
+        return;
+    }
+
+    GrResourceKey key = GrTexture::ComputeKey(fGpu, NULL,
+                                              texture->desc(),
+                                              true);
+    fTextureCache->attach(key, texture);
+}
+
 void GrContext::unlockTexture(TextureCacheEntry entry) {
     ASSERT_OWNED_RESOURCE(entry.texture());
     // If this is a scratch texture we detached it from the cache
@@ -532,6 +544,12 @@
     }
 }
 
+void GrContext::freeEntry(TextureCacheEntry entry) {
+    ASSERT_OWNED_RESOURCE(entry.texture());
+
+    fTextureCache->freeEntry(entry.cacheEntry());
+}
+
 GrTexture* GrContext::createUncachedTexture(const GrTextureDesc& descIn,
                                             void* srcData,
                                             size_t rowBytes) {
diff --git a/src/gpu/GrResourceCache.cpp b/src/gpu/GrResourceCache.cpp
index 6a6c166..8c93971 100644
--- a/src/gpu/GrResourceCache.cpp
+++ b/src/gpu/GrResourceCache.cpp
@@ -81,6 +81,9 @@
         --fUnlockedEntryCount;
     }
 
+    entry->fPrev = NULL;
+    entry->fNext = NULL;
+
     // update our stats
     if (clientDetach) {
         fClientDetachedCount += 1;
@@ -165,8 +168,9 @@
     return NULL != fCache.find(key);
 }
 
-GrResourceEntry* GrResourceCache::createAndLock(const GrResourceKey& key,
-                                              GrResource* resource) {
+GrResourceEntry* GrResourceCache::create(const GrResourceKey& key,
+                                         GrResource* resource,
+                                         bool lock) {
     // 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
@@ -176,9 +180,11 @@
 
     GrResourceEntry* entry = new GrResourceEntry(key, resource);
 
-    // mark the entry as "busy" so it doesn't get purged
-    // do this before attach for locked count tracking
-    entry->lock();
+    if (lock) {
+        // mark the entry as "busy" so it doesn't get purged
+        // do this before attach for locked count tracking
+        entry->lock();
+    }
 
     this->attachToHead(entry, false);
     fCache.insert(key, entry);
@@ -192,12 +198,27 @@
     return entry;
 }
 
+GrResourceEntry* GrResourceCache::createAndLock(const GrResourceKey& key,
+                                                GrResource* resource) {
+    return this->create(key, resource, true);
+}
+
+void GrResourceCache::attach(const GrResourceKey& key,
+                             GrResource* resource) {
+    this->create(key, resource, false);
+}
+
 void GrResourceCache::detach(GrResourceEntry* entry) {
     GrAutoResourceCacheValidate atcv(this);
-    internalDetach(entry, true);
+    this->internalDetach(entry, true);
     fCache.remove(entry->fKey, entry);
 }
 
+void GrResourceCache::freeEntry(GrResourceEntry* entry) {
+    GrAssert(NULL == entry->fNext && NULL == entry->fPrev);
+    delete entry;
+}
+
 void GrResourceCache::reattachAndUnlock(GrResourceEntry* entry) {
     GrAutoResourceCacheValidate atcv(this);
     if (entry->resource()->isValid()) {
diff --git a/src/gpu/GrResourceCache.h b/src/gpu/GrResourceCache.h
index ca7ca10..ba5ea4e 100644
--- a/src/gpu/GrResourceCache.h
+++ b/src/gpu/GrResourceCache.h
@@ -231,15 +231,26 @@
     GrResourceEntry* findAndLock(const GrResourceKey&, LockType style);
 
     /**
-     *  Create a new entry, based on the specified key and resource, and return
-     *  its "locked" entry.
+     *  Create a new cache entry, based on the provided key and resource, and 
+     *  return it.
      *
-     *  Ownership of the resource is transferred to the Entry, which will
-     *  unref() it when we are purged or deleted.
+     *  Ownership of the resource is transferred to the resource cache, 
+     *  which will unref() it when it is purged or deleted.
      */
     GrResourceEntry* createAndLock(const GrResourceKey&, GrResource*);
 
     /**
+     *  Create a new cache entry, based on the provided key and resource.
+     *
+     *  Ownership of the resource is transferred to the resource cache, 
+     *  which will unref() it when it is purged or deleted.
+     *
+     *  Currently this entry point is only intended for textures "detached"
+     *  from an AutoScratchTexture object.
+     */
+    void attach(const GrResourceKey& key, GrResource* 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.
      */
@@ -253,6 +264,8 @@
      */
     void detach(GrResourceEntry*);
 
+    void freeEntry(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
@@ -298,6 +311,11 @@
     
     // prevents recursive purging
     bool fPurging;
+
+    GrResourceEntry* create(const GrResourceKey& key,
+                            GrResource* resource,
+                            bool lock);
+
 };
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/src/gpu/GrSoftwarePathRenderer.cpp b/src/gpu/GrSoftwarePathRenderer.cpp
index 72ad543..bfad6df 100644
--- a/src/gpu/GrSoftwarePathRenderer.cpp
+++ b/src/gpu/GrSoftwarePathRenderer.cpp
@@ -336,7 +336,11 @@
     if (sw_draw_path_to_mask_texture(path, pathBounds,
                                      fill, fContext,
                                      translate, &ast, antiAlias)) {
+#if 1
         GrTexture* texture = ast.texture();
+#else
+        SkAutoTUnref<GrTexture> texture(ast.detach());
+#endif
         GrAssert(NULL != texture);
         GrDrawTarget::AutoDeviceCoordDraw adcd(target, stageMask);
         enum {
@@ -351,6 +355,7 @@
         GrScalar h = GrIntToScalar(pathBounds.height());
         GrRect maskRect = GrRect::MakeWH(w / texture->width(),
                                          h / texture->height());
+
         const GrRect* srcRects[GrDrawState::kNumStages] = {NULL};
         srcRects[kPathMaskStage] = &maskRect;
         stageMask |= 1 << kPathMaskStage;
diff --git a/src/gpu/GrTexture.cpp b/src/gpu/GrTexture.cpp
index 67ce629..19dc322 100644
--- a/src/gpu/GrTexture.cpp
+++ b/src/gpu/GrTexture.cpp
@@ -16,6 +16,30 @@
 
 SK_DEFINE_INST_COUNT(GrTexture)
 
+/** 
+ * This method allows us to interrupt the normal deletion process and place
+ * textures back in the texture cache when their ref count goes to zero.
+ */
+void GrTexture::internal_dispose() const {
+
+    if (this->isSetFlag((GrTextureFlags) kReturnToCache_FlagBit) &&
+        NULL != this->INHERITED::getContext()) {
+        GrTexture* nonConstThis = const_cast<GrTexture *>(this);
+        this->fRefCnt = 1;      // restore ref count to initial setting
+
+        nonConstThis->resetFlag((GrTextureFlags) kReturnToCache_FlagBit);
+        nonConstThis->INHERITED::getContext()->addExistingTextureToCache(nonConstThis);
+
+        // Note: this next assert is only correct for the texture cache's
+        // current single threaded usage. If we ever start accessing it via 
+        // threads it isn't guaranteed to be correct.
+        GrAssert(1 == this->INHERITED::getRefCnt());
+        return;
+    }
+
+    this->INHERITED::internal_dispose();
+}
+
 bool GrTexture::readPixels(int left, int top, int width, int height,
                            GrPixelConfig config, void* buffer,
                            size_t rowBytes) {
@@ -59,6 +83,11 @@
     }
 }
 
+void GrTexture::onRelease() {
+    GrAssert(!this->isSetFlag((GrTextureFlags) kReturnToCache_FlagBit));
+    this->releaseRenderTarget();
+}
+
 void GrTexture::onAbandon() {
     if (NULL != fRenderTarget) {
         fRenderTarget->abandon();