Revert of Replace GrResourceCache with GrResourceCache2. (patchset #6 id:100001 of https://codereview.chromium.org/716143004/)

Reason for revert:
Breaking stuff

Original issue's description:
> Replace GrResourceCache with GrResourceCache2.
>
> BUG=skia:2889
>
> Committed: https://skia.googlesource.com/skia/+/66a450f21a3da174b7eed89a1d5fc8591e8b6ee6

TBR=robertphillips@google.com
NOTREECHECKS=true
NOTRY=true
BUG=skia:2889

Review URL: https://codereview.chromium.org/715333003
diff --git a/bench/GrResourceCacheBench.cpp b/bench/GrResourceCacheBench.cpp
index ef97ed8..6d631dd 100644
--- a/bench/GrResourceCacheBench.cpp
+++ b/bench/GrResourceCacheBench.cpp
@@ -13,6 +13,7 @@
 #include "GrGpuResource.h"
 #include "GrContext.h"
 #include "GrGpu.h"
+#include "GrResourceCache.h"
 #include "GrResourceCache2.h"
 #include "GrStencilBuffer.h"
 #include "GrTexture.h"
@@ -88,20 +89,22 @@
 }
 
 static void get_texture_desc(int i, GrSurfaceDesc* desc) {
-    desc->fFlags = kRenderTarget_GrSurfaceFlag | kNoStencil_GrSurfaceFlag;
+    desc->fFlags = kRenderTarget_GrSurfaceFlag |
+        kNoStencil_GrSurfaceFlag;
     desc->fWidth  = i % 1024;
     desc->fHeight = i * 2 % 1024;
     desc->fConfig = static_cast<GrPixelConfig>(i % (kLast_GrPixelConfig + 1));
-    desc->fSampleCnt = ((i % 2) == 0) ? 0 : 4;
+    desc->fSampleCnt = i % 1 == 0 ? 0 : 4;
 }
 
-static void populate_cache(GrGpu* gpu, int resourceCount) {
+static void populate_cache(GrResourceCache* cache, GrGpu* gpu, int resourceCount) {
     for (int i = 0; i < resourceCount; ++i) {
         int w, h, s;
         get_stencil(i, &w, &h, &s);
         GrResourceKey key = GrStencilBuffer::ComputeKey(w, h, s);
         GrGpuResource* resource = SkNEW_ARGS(StencilResource, (gpu, i));
-        resource->cacheAccess().setContentKey(key);
+        cache->purgeAsNeeded(1, resource->gpuMemorySize());
+        cache->addResource(key, resource);
         resource->unref();
     }
 
@@ -110,7 +113,8 @@
         get_texture_desc(i, &desc);
         GrResourceKey key =  TextureResource::ComputeKey(desc);
         GrGpuResource* resource = SkNEW_ARGS(TextureResource, (gpu, i));
-        resource->cacheAccess().setContentKey(key);
+        cache->purgeAsNeeded(1, resource->gpuMemorySize());
+        cache->addResource(key, resource);
         resource->unref();
     }
 }
@@ -194,24 +198,25 @@
         // Set the cache budget to be very large so no purging occurs.
         context->setResourceCacheLimits(2 * RESOURCE_COUNT, 1 << 30);
 
+        GrResourceCache* cache = context->getResourceCache();
         GrResourceCache2* cache2 = context->getResourceCache2();
 
         // Make sure the cache is empty.
-        cache2->purgeAllUnlocked();
-        SkASSERT(0 == cache2->getResourceCount() && 0 == cache2->getResourceBytes());
+        cache->purgeAllUnlocked();
+        SkASSERT(0 == cache->getCachedResourceCount() && 0 == cache->getCachedResourceBytes());
 
         GrGpu* gpu = context->getGpu();
 
         for (int i = 0; i < loops; ++i) {
-            SkASSERT(0 == cache2->getResourceCount() && 0 == cache2->getResourceBytes());
+            SkASSERT(0 == cache->getCachedResourceCount() && 0 == cache->getCachedResourceBytes());
 
-            populate_cache(gpu, RESOURCE_COUNT);
+            populate_cache(cache, gpu, RESOURCE_COUNT);
 
             // Check that cache works.
             for (int k = 0; k < RESOURCE_COUNT; k += 33) {
                 check_cache_contents_or_die(cache2, k);
             }
-            cache2->purgeAllUnlocked();
+            cache->purgeAllUnlocked();
         }
     }
 
@@ -242,15 +247,16 @@
         // Set the cache budget to be very large so no purging occurs.
         context->setResourceCacheLimits(2 * RESOURCE_COUNT, 1 << 30);
 
+        GrResourceCache* cache = context->getResourceCache();
         GrResourceCache2* cache2 = context->getResourceCache2();
 
         // Make sure the cache is empty.
-        cache2->purgeAllUnlocked();
-        SkASSERT(0 == cache2->getResourceCount() && 0 == cache2->getResourceBytes());
+        cache->purgeAllUnlocked();
+        SkASSERT(0 == cache->getCachedResourceCount() && 0 == cache->getCachedResourceBytes());
 
         GrGpu* gpu = context->getGpu();
 
-        populate_cache(gpu, RESOURCE_COUNT);
+        populate_cache(cache, gpu, RESOURCE_COUNT);
 
         for (int i = 0; i < loops; ++i) {
             for (int k = 0; k < RESOURCE_COUNT; ++k) {
diff --git a/gyp/gpu.gyp b/gyp/gpu.gyp
index 6319456..d40afe1 100644
--- a/gyp/gpu.gyp
+++ b/gyp/gpu.gyp
@@ -63,6 +63,16 @@
           ],
         },
       }],
+      [ 'skia_resource_cache_mb_limit != 0', {
+        'defines': [
+          'GR_DEFAULT_RESOURCE_CACHE_MB_LIMIT=<(skia_resource_cache_mb_limit)',
+        ],
+      }],
+      [ 'skia_resource_cache_count_limit != 0', {
+        'defines': [
+          'GR_DEFAULT_RESOURCE_CACHE_COUNT_LIMIT=<(skia_resource_cache_count_limit)',
+        ],
+      }],
     ],
     'direct_dependent_settings': {
       'conditions': [
diff --git a/gyp/gpu.gypi b/gyp/gpu.gypi
index 5095c44..8eca40c 100644
--- a/gyp/gpu.gypi
+++ b/gyp/gpu.gypi
@@ -136,6 +136,8 @@
       '<(skia_src_path)/gpu/GrRenderTarget.cpp',
       '<(skia_src_path)/gpu/GrReducedClip.cpp',
       '<(skia_src_path)/gpu/GrReducedClip.h',
+      '<(skia_src_path)/gpu/GrResourceCache.cpp',
+      '<(skia_src_path)/gpu/GrResourceCache.h',
       '<(skia_src_path)/gpu/GrResourceCache2.cpp',
       '<(skia_src_path)/gpu/GrResourceCache2.h',
       '<(skia_src_path)/gpu/GrStencil.cpp',
diff --git a/include/gpu/GrConfig.h b/include/gpu/GrConfig.h
index d48ccd5..e70c978 100644
--- a/include/gpu/GrConfig.h
+++ b/include/gpu/GrConfig.h
@@ -213,6 +213,24 @@
 #endif
 
 /**
+ * GR_DEFAULT_RESOURCE_CACHE_MB_LIMIT gives a threshold (in megabytes) for the
+ * maximum size of the texture cache in vram. The value is only a default and
+ * can be overridden at runtime.
+ */
+#if !defined(GR_DEFAULT_RESOURCE_CACHE_MB_LIMIT)
+    #define GR_DEFAULT_RESOURCE_CACHE_MB_LIMIT 96
+#endif
+
+/**
+ * GR_DEFAULT_RESOURCE_CACHE_COUNT_LIMIT specifies the maximum number of
+ * textures the texture cache can hold in vram. The value is only a default and
+ * can be overridden at runtime.
+ */
+#if !defined(GR_DEFAULT_RESOURCE_CACHE_COUNT_LIMIT)
+    #define GR_DEFAULT_RESOURCE_CACHE_COUNT_LIMIT 2048
+#endif
+
+/**
  * GR_STROKE_PATH_RENDERING controls whether or not the GrStrokePathRenderer can be selected
  * as a path renderer. GrStrokePathRenderer is currently an experimental path renderer.
  */
diff --git a/include/gpu/GrContext.h b/include/gpu/GrContext.h
index fcfcb77..97acda1 100644
--- a/include/gpu/GrContext.h
+++ b/include/gpu/GrContext.h
@@ -33,6 +33,7 @@
 class GrPath;
 class GrPathRenderer;
 class GrResourceEntry;
+class GrResourceCache;
 class GrResourceCache2;
 class GrStencilBuffer;
 class GrTestTarget;
@@ -882,6 +883,7 @@
     GrDrawTarget* getTextTarget();
     const GrIndexBuffer* getQuadIndexBuffer() const;
     GrAARectRenderer* getAARectRenderer() { return fAARectRenderer; }
+    GrResourceCache* getResourceCache() { return fResourceCache; }
     GrResourceCache2* getResourceCache2() { return fResourceCache2; }
 
     // Called by tests that draw directly to the context via GrDrawTarget
@@ -946,6 +948,7 @@
     const GrClipData*               fClip;  // TODO: make this ref counted
     GrDrawState*                    fDrawState;
 
+    GrResourceCache*                fResourceCache;
     GrResourceCache2*               fResourceCache2;
     GrFontCache*                    fFontCache;
     SkAutoTDelete<GrLayerCache>     fLayerCache;
@@ -957,6 +960,9 @@
     GrIndexBufferAllocPool*         fDrawBufferIBAllocPool;
     GrInOrderDrawBuffer*            fDrawBuffer;
 
+    // Set by OverbudgetCB() to request that GrContext flush before exiting a draw.
+    bool                            fFlushToReduceCacheSize;
+
     GrAARectRenderer*               fAARectRenderer;
     GrOvalRenderer*                 fOvalRenderer;
 
@@ -983,9 +989,10 @@
     void setupDrawBuffer();
 
     class AutoRestoreEffects;
+    class AutoCheckFlush;
     /// Sets the paint and returns the target to draw into. The paint can be NULL in which case the
     /// draw state is left unmodified.
-    GrDrawTarget* prepareToDraw(const GrPaint*, AutoRestoreEffects*);
+    GrDrawTarget* prepareToDraw(const GrPaint*, AutoRestoreEffects*, AutoCheckFlush*);
 
     void internalDrawPath(GrDrawTarget* target, bool useAA, const SkPath& path,
                           const GrStrokeInfo& stroke);
@@ -996,6 +1003,8 @@
                                     size_t rowBytes,
                                     bool filter);
 
+    GrTexture* createNewScratchTexture(const GrSurfaceDesc& desc);
+
     /**
      * These functions create premul <-> unpremul effects if it is possible to generate a pair
      * of effects that make a readToUPM->writeToPM->readToUPM cycle invariant. Otherwise, they
@@ -1006,9 +1015,9 @@
 
     /**
      *  This callback allows the resource cache to callback into the GrContext
-     *  when the cache is still over budget after a purge.
+     *  when the cache is still overbudget after a purge.
      */
-    static void OverBudgetCB(void* data);
+    static bool OverbudgetCB(void* data);
 
     typedef SkRefCnt INHERITED;
 };
diff --git a/include/gpu/GrGpuResource.h b/include/gpu/GrGpuResource.h
index 3c87117..5231b15 100644
--- a/include/gpu/GrGpuResource.h
+++ b/include/gpu/GrGpuResource.h
@@ -16,6 +16,7 @@
 class GrContext;
 class GrGpu;
 class GrResourceCache2;
+class GrResourceCacheEntry;
 
 /**
  * Base class for GrGpuResource. Handles the various types of refs we need. Separated out as a base
@@ -125,7 +126,7 @@
 };
 
 /**
- * Base class for objects that can be kept in the GrResourceCache2.
+ * Base class for objects that can be kept in the GrResourceCache.
  */
 class SK_API GrGpuResource : public GrIORef<GrGpuResource> {
 public:
@@ -256,6 +257,7 @@
 
     uint32_t                fFlags;
 
+    GrResourceCacheEntry*   fCacheEntry;  // NULL if not in cache
     mutable size_t          fGpuMemorySize;
     const uint32_t          fUniqueID;
 
diff --git a/include/gpu/GrUserConfig.h b/include/gpu/GrUserConfig.h
index 4ff95f7..092ff9d 100644
--- a/include/gpu/GrUserConfig.h
+++ b/include/gpu/GrUserConfig.h
@@ -19,4 +19,16 @@
  */
 //#define GR_GEOM_BUFFER_MAP_THRESHOLD (1<<15)
 
+/**
+ * This gives a threshold in megabytes for the maximum size of the texture cache
+ * in vram. The value is only a default and can be overridden at runtime.
+ */
+//#define GR_DEFAULT_RESOURCE_CACHE_MB_LIMIT 96
+
+/**
+ * This specifies the maximum number of textures the texture cache can hold
+ * in vram. The value is only a default and can be overridden at runtime.
+ */
+//#define GR_DEFAULT_RESOURCE_CACHE_COUNT_LIMIT 2048
+
 #endif
diff --git a/include/gpu/SkGr.h b/include/gpu/SkGr.h
index 8025c95..026525b 100644
--- a/include/gpu/SkGr.h
+++ b/include/gpu/SkGr.h
@@ -68,11 +68,6 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
-// The cache listens for these messages to purge junk resources proactively.
-struct GrResourceInvalidatedMessage {
-    GrResourceKey key;
-};
-
 bool GrIsBitmapInCache(const GrContext*, const SkBitmap&, const GrTextureParams*);
 
 GrTexture* GrRefCachedBitmapTexture(GrContext*, const SkBitmap&, const GrTextureParams*);
diff --git a/src/gpu/GrAADistanceFieldPathRenderer.h b/src/gpu/GrAADistanceFieldPathRenderer.h
index c337016..10f0eba 100755
--- a/src/gpu/GrAADistanceFieldPathRenderer.h
+++ b/src/gpu/GrAADistanceFieldPathRenderer.h
@@ -15,7 +15,6 @@
 #include "GrRect.h"
 
 #include "SkChecksum.h"
-#include "SkTDynamicHash.h"
 
 class GrContext;
 class GrPlot;
diff --git a/src/gpu/GrContext.cpp b/src/gpu/GrContext.cpp
index 9a180ee..3a93404 100755
--- a/src/gpu/GrContext.cpp
+++ b/src/gpu/GrContext.cpp
@@ -25,6 +25,7 @@
 #include "GrOvalRenderer.h"
 #include "GrPathRenderer.h"
 #include "GrPathUtils.h"
+#include "GrResourceCache.h"
 #include "GrResourceCache2.h"
 #include "GrSoftwarePathRenderer.h"
 #include "GrStencilBuffer.h"
@@ -51,6 +52,9 @@
     #define GR_DEBUG_PARTIAL_COVERAGE_CHECK 0
 #endif
 
+static const size_t MAX_RESOURCE_CACHE_COUNT = GR_DEFAULT_RESOURCE_CACHE_COUNT_LIMIT;
+static const size_t MAX_RESOURCE_CACHE_BYTES = GR_DEFAULT_RESOURCE_CACHE_MB_LIMIT * 1024 * 1024;
+
 static const size_t DRAW_BUFFER_VBPOOL_BUFFER_SIZE = 1 << 15;
 static const int DRAW_BUFFER_VBPOOL_PREALLOC_BUFFERS = 4;
 
@@ -62,6 +66,20 @@
 // Glorified typedef to avoid including GrDrawState.h in GrContext.h
 class GrContext::AutoRestoreEffects : public GrDrawState::AutoRestoreEffects {};
 
+class GrContext::AutoCheckFlush {
+public:
+    AutoCheckFlush(GrContext* context) : fContext(context) { SkASSERT(context); }
+
+    ~AutoCheckFlush() {
+        if (fContext->fFlushToReduceCacheSize) {
+            fContext->flush();
+        }
+    }
+
+private:
+    GrContext* fContext;
+};
+
 GrContext* GrContext::Create(GrBackend backend, GrBackendContext backendContext,
                              const Options* opts) {
     GrContext* context;
@@ -85,11 +103,13 @@
     fClip = NULL;
     fPathRendererChain = NULL;
     fSoftwarePathRenderer = NULL;
+    fResourceCache = NULL;
     fResourceCache2 = NULL;
     fFontCache = NULL;
     fDrawBuffer = NULL;
     fDrawBufferVBAllocPool = NULL;
     fDrawBufferIBAllocPool = NULL;
+    fFlushToReduceCacheSize = false;
     fAARectRenderer = NULL;
     fOvalRenderer = NULL;
     fViewMatrix.reset();
@@ -110,8 +130,11 @@
 void GrContext::initCommon() {
     fDrawState = SkNEW(GrDrawState);
 
+    fResourceCache = SkNEW_ARGS(GrResourceCache, (fGpu->caps(),
+                                                  MAX_RESOURCE_CACHE_COUNT,
+                                                  MAX_RESOURCE_CACHE_BYTES));
+    fResourceCache->setOverbudgetCallback(OverbudgetCB, this);
     fResourceCache2 = SkNEW(GrResourceCache2);
-    fResourceCache2->setOverBudgetCallback(OverBudgetCB, this);
 
     fFontCache = SkNEW_ARGS(GrFontCache, (fGpu));
 
@@ -137,6 +160,9 @@
     }
 
     SkDELETE(fResourceCache2);
+    fResourceCache2 = NULL;
+    SkDELETE(fResourceCache);
+    fResourceCache = NULL;
     SkDELETE(fFontCache);
     SkDELETE(fDrawBuffer);
     SkDELETE(fDrawBufferVBAllocPool);
@@ -175,6 +201,8 @@
     fAARectRenderer->reset();
     fOvalRenderer->reset();
 
+    fResourceCache->purgeAllUnlocked();
+
     fFontCache->freeAll();
     fLayerCache->freeAll();
 }
@@ -193,6 +221,7 @@
     fAARectRenderer->reset();
     fOvalRenderer->reset();
 
+    fResourceCache->purgeAllUnlocked();
     fFontCache->freeAll();
     fLayerCache->freeAll();
     // a path renderer may be holding onto resources
@@ -201,12 +230,12 @@
 }
 
 void GrContext::getResourceCacheUsage(int* resourceCount, size_t* resourceBytes) const {
-    if (resourceCount) {
-        *resourceCount = fResourceCache2->getResourceCount();
-    }
-    if (resourceBytes) {
-        *resourceBytes = fResourceCache2->getResourceBytes();
-    }
+  if (resourceCount) {
+    *resourceCount = fResourceCache->getCachedResourceCount();
+  }
+  if (resourceBytes) {
+    *resourceBytes = fResourceCache->getCachedResourceBytes();
+  }
 }
 
 GrTextContext* GrContext::createTextContext(GrRenderTarget* renderTarget,
@@ -244,13 +273,12 @@
 }
 
 void GrContext::addStencilBuffer(GrStencilBuffer* sb) {
-    // TODO: Make GrStencilBuffers use the scratch mechanism rather than content keys.
     ASSERT_OWNED_RESOURCE(sb);
 
     GrResourceKey resourceKey = GrStencilBuffer::ComputeKey(sb->width(),
                                                             sb->height(),
                                                             sb->numSamples());
-    SkAssertResult(sb->cacheAccess().setContentKey(resourceKey));
+    fResourceCache->addResource(resourceKey, sb);
 }
 
 GrStencilBuffer* GrContext::findAndRefStencilBuffer(int width, int height, int sampleCnt) {
@@ -392,19 +420,25 @@
     }
 
     if (texture) {
-        if (texture->cacheAccess().setContentKey(resourceKey)) {
-            if (cacheKey) {
-                *cacheKey = resourceKey;
-            }
-        } else {
-            texture->unref();
-            texture = NULL;
+        fResourceCache->addResource(resourceKey, texture);
+
+        if (cacheKey) {
+            *cacheKey = resourceKey;
         }
     }
 
     return texture;
 }
 
+GrTexture* GrContext::createNewScratchTexture(const GrSurfaceDesc& desc) {
+    GrTexture* texture = fGpu->createTexture(desc, NULL, 0);
+    if (!texture) {
+        return NULL;
+    }
+    fResourceCache->addResource(texture->cacheAccess().getScratchKey(), texture);
+    return texture;
+}
+
 GrTexture* GrContext::refScratchTexture(const GrSurfaceDesc& inDesc, ScratchTexMatch match,
                                         bool calledDuringFlush) {
     // kNoStencil has no meaning if kRT isn't set.
@@ -439,6 +473,7 @@
             }
             GrGpuResource* resource = fResourceCache2->findAndRefScratchResource(key, scratchFlags);
             if (resource) {
+                fResourceCache->makeResourceMRU(resource);
                 return static_cast<GrSurface*>(resource)->asTexture();
             }
 
@@ -461,17 +496,21 @@
         desc.writable()->fFlags = origFlags;
     }
 
-    GrTexture* texture = fGpu->createTexture(*desc, NULL, 0);
+    GrTexture* texture = this->createNewScratchTexture(*desc);
     SkASSERT(NULL == texture || 
              texture->cacheAccess().getScratchKey() == GrTexturePriv::ComputeScratchKey(*desc));
     return texture;
 }
 
-void GrContext::OverBudgetCB(void* data) {
-    // Flush the InOrderDrawBuffer to possibly free up some textures
+bool GrContext::OverbudgetCB(void* data) {
     SkASSERT(data);
+
     GrContext* context = reinterpret_cast<GrContext*>(data);
-    context->flush();
+
+    // Flush the InOrderDrawBuffer to possibly free up some textures
+    context->fFlushToReduceCacheSize = true;
+
+    return true;
 }
 
 
@@ -483,16 +522,11 @@
 }
 
 void GrContext::getResourceCacheLimits(int* maxTextures, size_t* maxTextureBytes) const {
-    if (maxTextures) {
-        *maxTextures = fResourceCache2->getMaxResourceCount();
-    }
-    if (maxTextureBytes) {
-        *maxTextureBytes = fResourceCache2->getMaxResourceBytes();
-    }
+    fResourceCache->getLimits(maxTextures, maxTextureBytes);
 }
 
 void GrContext::setResourceCacheLimits(int maxTextures, size_t maxTextureBytes) {
-    fResourceCache2->setLimits(maxTextures, maxTextureBytes);
+    fResourceCache->setLimits(maxTextures, maxTextureBytes);
 }
 
 int GrContext::getMaxTextureSize() const {
@@ -548,8 +582,9 @@
     SkASSERT(renderTarget);
 
     AutoRestoreEffects are;
+    AutoCheckFlush acf(this);
     GR_CREATE_TRACE_MARKER_CONTEXT("GrContext::clear", this);
-    GrDrawTarget* target = this->prepareToDraw(NULL, &are);
+    GrDrawTarget* target = this->prepareToDraw(NULL, &are, &acf);
     if (NULL == target) {
         return;
     }
@@ -681,7 +716,8 @@
     }
 
     AutoRestoreEffects are;
-    GrDrawTarget* target = this->prepareToDraw(&paint, &are);
+    AutoCheckFlush acf(this);
+    GrDrawTarget* target = this->prepareToDraw(&paint, &are, &acf);
     if (NULL == target) {
         return;
     }
@@ -791,7 +827,8 @@
                                const SkRect& localRect,
                                const SkMatrix* localMatrix) {
     AutoRestoreEffects are;
-    GrDrawTarget* target = this->prepareToDraw(&paint, &are);
+    AutoCheckFlush acf(this);
+    GrDrawTarget* target = this->prepareToDraw(&paint, &are, &acf);
     if (NULL == target) {
         return;
     }
@@ -854,9 +891,10 @@
                              const uint16_t indices[],
                              int indexCount) {
     AutoRestoreEffects are;
-    GrDrawTarget::AutoReleaseGeometry geo;
+    AutoCheckFlush acf(this);
+    GrDrawTarget::AutoReleaseGeometry geo; // must be inside AutoCheckFlush scope
 
-    GrDrawTarget* target = this->prepareToDraw(&paint, &are);
+    GrDrawTarget* target = this->prepareToDraw(&paint, &are, &acf);
     if (NULL == target) {
         return;
     }
@@ -916,7 +954,8 @@
     }
 
     AutoRestoreEffects are;
-    GrDrawTarget* target = this->prepareToDraw(&paint, &are);
+    AutoCheckFlush acf(this);
+    GrDrawTarget* target = this->prepareToDraw(&paint, &are, &acf);
     if (NULL == target) {
         return;
     }
@@ -942,7 +981,8 @@
     }
 
     AutoRestoreEffects are;
-    GrDrawTarget* target = this->prepareToDraw(&paint, &are);
+    AutoCheckFlush acf(this);
+    GrDrawTarget* target = this->prepareToDraw(&paint, &are, &acf);
 
     GR_CREATE_TRACE_MARKER("GrContext::drawDRRect", target);
 
@@ -974,7 +1014,8 @@
     }
 
     AutoRestoreEffects are;
-    GrDrawTarget* target = this->prepareToDraw(&paint, &are);
+    AutoCheckFlush acf(this);
+    GrDrawTarget* target = this->prepareToDraw(&paint, &are, &acf);
     if (NULL == target) {
         return;
     }
@@ -1061,7 +1102,8 @@
         SkPoint pts[2];
         if (path.isLine(pts)) {
             AutoRestoreEffects are;
-            GrDrawTarget* target = this->prepareToDraw(&paint, &are);
+            AutoCheckFlush acf(this);
+            GrDrawTarget* target = this->prepareToDraw(&paint, &are, &acf);
             if (NULL == target) {
                 return;
             }
@@ -1097,7 +1139,8 @@
     // the writePixels that uploads to the scratch will perform a flush so we're
     // OK.
     AutoRestoreEffects are;
-    GrDrawTarget* target = this->prepareToDraw(&paint, &are);
+    AutoCheckFlush acf(this);
+    GrDrawTarget* target = this->prepareToDraw(&paint, &are, &acf);
     if (NULL == target) {
         return;
     }
@@ -1199,6 +1242,8 @@
     } else {
         fDrawBuffer->flush();
     }
+    fResourceCache->purgeAsNeeded();
+    fFlushToReduceCacheSize = false;
 }
 
 bool sw_convert_to_premul(GrPixelConfig srcConfig, int width, int height, size_t inRowBytes,
@@ -1318,7 +1363,7 @@
     // drawing a rect to the render target.
     // The bracket ensures we pop the stack if we wind up flushing below.
     {
-        GrDrawTarget* drawTarget = this->prepareToDraw(NULL, NULL);
+        GrDrawTarget* drawTarget = this->prepareToDraw(NULL, NULL, NULL);
         GrDrawTarget::AutoGeometryAndStatePush agasp(drawTarget, GrDrawTarget::kReset_ASRInit,
                                                      &matrix);
         GrDrawState* drawState = drawTarget->drawState();
@@ -1498,7 +1543,8 @@
     SkASSERT(renderTarget);
     ASSERT_OWNED_RESOURCE(renderTarget);
     AutoRestoreEffects are;
-    GrDrawTarget* target = this->prepareToDraw(NULL, &are);
+    AutoCheckFlush acf(this);
+    GrDrawTarget* target = this->prepareToDraw(NULL, &are, &acf);
     if (NULL == target) {
         return;
     }
@@ -1516,7 +1562,7 @@
     // Since we're going to the draw target and not GPU, no need to check kNoFlush
     // here.
 
-    GrDrawTarget* target = this->prepareToDraw(NULL, NULL);
+    GrDrawTarget* target = this->prepareToDraw(NULL, NULL, NULL);
     if (NULL == target) {
         return;
     }
@@ -1535,7 +1581,9 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
-GrDrawTarget* GrContext::prepareToDraw(const GrPaint* paint, AutoRestoreEffects* are) {
+GrDrawTarget* GrContext::prepareToDraw(const GrPaint* paint,
+                                       AutoRestoreEffects* are,
+                                       AutoCheckFlush* acf) {
     // All users of this draw state should be freeing up all effects when they're done.
     // Otherwise effects that own resources may keep those resources alive indefinitely.
     SkASSERT(0 == fDrawState->numColorStages() && 0 == fDrawState->numCoverageStages() &&
@@ -1548,6 +1596,7 @@
     ASSERT_OWNED_RESOURCE(fRenderTarget.get());
     if (paint) {
         SkASSERT(are);
+        SkASSERT(acf);
         are->set(fDrawState);
         fDrawState->setFromPaint(*paint, fViewMatrix, fRenderTarget.get());
 #if GR_DEBUG_PARTIAL_COVERAGE_CHECK
@@ -1647,7 +1696,7 @@
 }
 
 GrDrawTarget* GrContext::getTextTarget() {
-    return this->prepareToDraw(NULL, NULL);
+    return this->prepareToDraw(NULL, NULL, NULL);
 }
 
 const GrIndexBuffer* GrContext::getQuadIndexBuffer() const {
@@ -1697,11 +1746,15 @@
 }
 
 void GrContext::addResourceToCache(const GrResourceKey& resourceKey, GrGpuResource* resource) {
-    resource->cacheAccess().setContentKey(resourceKey);
+    fResourceCache->addResource(resourceKey, resource);
 }
 
 GrGpuResource* GrContext::findAndRefCachedResource(const GrResourceKey& resourceKey) {
-    return fResourceCache2->findAndRefContentResource(resourceKey);
+    GrGpuResource* resource = fResourceCache2->findAndRefContentResource(resourceKey);
+    if (resource) {
+        fResourceCache->makeResourceMRU(resource);
+    }
+    return resource;
 }
 
 void GrContext::addGpuTraceMarker(const GrGpuTraceMarker* marker) {
@@ -1721,7 +1774,7 @@
 ///////////////////////////////////////////////////////////////////////////////
 #if GR_CACHE_STATS
 void GrContext::printCacheStats() const {
-    fResourceCache2->printStats();
+    fResourceCache->printStats();
 }
 #endif
 
diff --git a/src/gpu/GrGpuResource.cpp b/src/gpu/GrGpuResource.cpp
index be8ea0d..f6f7282 100644
--- a/src/gpu/GrGpuResource.cpp
+++ b/src/gpu/GrGpuResource.cpp
@@ -18,8 +18,16 @@
     return gpu->getContext()->getResourceCache2();
 }
 
+static inline GrResourceCache* get_resource_cache(GrGpu* gpu) {
+    SkASSERT(gpu);
+    SkASSERT(gpu->getContext());
+    SkASSERT(gpu->getContext()->getResourceCache());
+    return gpu->getContext()->getResourceCache();
+}
+
 GrGpuResource::GrGpuResource(GrGpu* gpu, bool isWrapped)
     : fGpu(gpu)
+    , fCacheEntry(NULL)
     , fGpuMemorySize(kInvalidGpuMemorySize)
     , fUniqueID(CreateUniqueID())
     , fScratchKey(GrResourceKey::NullScratchKey())
@@ -32,7 +40,7 @@
 }
 
 void GrGpuResource::registerWithCache() {
-    get_resource_cache2(fGpu)->resourceAccess().insertResource(this);
+    get_resource_cache2(fGpu)->insertResource(this);
 }
 
 GrGpuResource::~GrGpuResource() {
@@ -43,18 +51,16 @@
 void GrGpuResource::release() { 
     if (fGpu) {
         this->onRelease();
-        get_resource_cache2(fGpu)->resourceAccess().removeResource(this);
+        get_resource_cache2(fGpu)->removeResource(this);
         fGpu = NULL;
-        fGpuMemorySize = 0;
     }
 }
 
 void GrGpuResource::abandon() {
     if (fGpu) {
         this->onAbandon();
-        get_resource_cache2(fGpu)->resourceAccess().removeResource(this);
+        get_resource_cache2(fGpu)->removeResource(this);
         fGpu = NULL;
-        fGpuMemorySize = 0;
     }
 }
 
@@ -74,17 +80,6 @@
     }
 }
 
-void GrGpuResource::didChangeGpuMemorySize() const {
-    if (this->wasDestroyed()) {
-        return;
-    }
-
-    size_t oldSize = fGpuMemorySize;
-    SkASSERT(kInvalidGpuMemorySize != oldSize);
-    fGpuMemorySize = kInvalidGpuMemorySize;
-    get_resource_cache2(fGpu)->resourceAccess().didChangeGpuMemorySize(this, oldSize);
-}
-
 bool GrGpuResource::setContentKey(const GrResourceKey& contentKey) {
     // Currently this can only be called once and can't be called when the resource is scratch.
     SkASSERT(!contentKey.isScratch());
@@ -97,7 +92,7 @@
     fContentKey = contentKey;
     fContentKeySet = true;
 
-    if (!get_resource_cache2(fGpu)->resourceAccess().didSetContentKey(this)) {
+    if (!get_resource_cache2(fGpu)->didSetContentKey(this)) {
         fContentKeySet = false;
         return false;
     }
@@ -105,8 +100,8 @@
 }
 
 void GrGpuResource::notifyIsPurgable() const {
-    if (!this->wasDestroyed()) {
-        get_resource_cache2(fGpu)->resourceAccess().notifyPurgable(this);
+    if (fCacheEntry && !this->wasDestroyed()) {
+        get_resource_cache(fGpu)->notifyPurgable(this);
     }
 }
 
diff --git a/src/gpu/GrGpuResourceCacheAccess.h b/src/gpu/GrGpuResourceCacheAccess.h
index 7417a55..af5c054 100644
--- a/src/gpu/GrGpuResourceCacheAccess.h
+++ b/src/gpu/GrGpuResourceCacheAccess.h
@@ -29,6 +29,25 @@
     }
 
     /**
+     * Used by legacy cache to attach a cache entry. This is to be removed soon.
+     */
+    void setCacheEntry(GrResourceCacheEntry* cacheEntry) {
+        // GrResourceCache never changes the cacheEntry once one has been added.
+        SkASSERT(NULL == cacheEntry || NULL == fResource->fCacheEntry);
+        fResource->fCacheEntry = cacheEntry;
+    }
+
+    /**
+     * Is the resource in the legacy cache? This is to be removed soon.
+     */
+    bool isInCache() const { return SkToBool(fResource->fCacheEntry); }
+
+    /**
+     * Returns the cache entry for the legacy cache. This is to be removed soon.
+     */
+    GrResourceCacheEntry* getCacheEntry() const { return fResource->fCacheEntry; }
+
+    /**
      * Is the resource currently cached as scratch? This means it has a valid scratch key and does
      * not have a content key.
      */
diff --git a/src/gpu/GrPath.h b/src/gpu/GrPath.h
index 394db6f..a571935 100644
--- a/src/gpu/GrPath.h
+++ b/src/gpu/GrPath.h
@@ -9,6 +9,7 @@
 #define GrPath_DEFINED
 
 #include "GrGpuResource.h"
+#include "GrResourceCache.h"
 #include "SkPath.h"
 #include "SkRect.h"
 #include "SkStrokeRec.h"
diff --git a/src/gpu/GrPathRange.h b/src/gpu/GrPathRange.h
index 5bfecb0..dc8ce1d 100644
--- a/src/gpu/GrPathRange.h
+++ b/src/gpu/GrPathRange.h
@@ -9,6 +9,7 @@
 #define GrPathRange_DEFINED
 
 #include "GrGpuResource.h"
+#include "GrResourceCache.h"
 #include "SkRefCnt.h"
 #include "SkStrokeRec.h"
 #include "SkTArray.h"
diff --git a/src/gpu/GrResourceCache.cpp b/src/gpu/GrResourceCache.cpp
new file mode 100644
index 0000000..a73d117
--- /dev/null
+++ b/src/gpu/GrResourceCache.cpp
@@ -0,0 +1,393 @@
+
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrResourceCache.h"
+#include "GrGpuResource.h"
+#include "GrGpuResourceCacheAccess.h"
+#include "GrTexturePriv.h"
+
+DECLARE_SKMESSAGEBUS_MESSAGE(GrResourceInvalidatedMessage);
+
+///////////////////////////////////////////////////////////////////////////////
+
+void GrGpuResource::didChangeGpuMemorySize() const {
+    fGpuMemorySize = kInvalidGpuMemorySize;
+    if (this->cacheAccess().isInCache()) {
+        fCacheEntry->didChangeResourceSize();
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+GrResourceKey::ResourceType GrResourceKey::GenerateResourceType() {
+    static int32_t gNextType = 0;
+
+    int32_t type = sk_atomic_inc(&gNextType);
+    if (type >= (1 << 8 * sizeof(ResourceType))) {
+        SkFAIL("Too many Resource Types");
+    }
+
+    return static_cast<ResourceType>(type);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+GrResourceCacheEntry::GrResourceCacheEntry(GrResourceCache* resourceCache, GrGpuResource* resource)
+        : fResourceCache(resourceCache),
+          fResource(resource),
+          fCachedSize(resource->gpuMemorySize()) {
+    // we assume ownership of the resource, and will unref it when we die
+    SkASSERT(resource);
+    resource->ref();
+}
+
+GrResourceCacheEntry::~GrResourceCacheEntry() {
+    // We're relying on having the cache entry to remove this from GrResourceCache2's content hash.
+    // fResource->setCacheEntry(NULL);
+    fResource->unref();
+}
+
+#ifdef SK_DEBUG
+void GrResourceCacheEntry::validate() const {
+    SkASSERT(fResourceCache);
+    SkASSERT(fResource);
+    SkASSERT(fResource->cacheAccess().getCacheEntry() == this);
+    SkASSERT(fResource->gpuMemorySize() == fCachedSize);
+    fResource->validate();
+}
+#endif
+
+void GrResourceCacheEntry::didChangeResourceSize() {
+    size_t oldSize = fCachedSize;
+    fCachedSize = fResource->gpuMemorySize();
+    if (fCachedSize > oldSize) {
+        fResourceCache->didIncreaseResourceSize(this, fCachedSize - oldSize);
+    } else if (fCachedSize < oldSize) {
+        fResourceCache->didDecreaseResourceSize(this, oldSize - fCachedSize);
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+GrResourceCache::GrResourceCache(const GrDrawTargetCaps* caps, int maxCount, size_t maxBytes)
+    : fMaxCount(maxCount)
+    , fMaxBytes(maxBytes)
+    , fCaps(SkRef(caps)) {
+#if GR_CACHE_STATS
+    fHighWaterEntryCount          = 0;
+    fHighWaterEntryBytes          = 0;
+#endif
+
+    fEntryCount                   = 0;
+    fEntryBytes                   = 0;
+
+    fPurging                      = false;
+
+    fOverbudgetCB                 = NULL;
+    fOverbudgetData               = NULL;
+}
+
+GrResourceCache::~GrResourceCache() {
+    GrAutoResourceCacheValidate atcv(this);
+
+    EntryList::Iter iter;
+
+    // Unlike the removeAll, here we really remove everything, including locked resources.
+    while (GrResourceCacheEntry* entry = fList.head()) {
+        GrAutoResourceCacheValidate atcv(this);
+
+        // remove from our llist
+        this->internalDetach(entry);
+
+        delete entry;
+    }
+}
+
+void GrResourceCache::getLimits(int* maxResources, size_t* maxResourceBytes) const{
+    if (maxResources) {
+        *maxResources = fMaxCount;
+    }
+    if (maxResourceBytes) {
+        *maxResourceBytes = fMaxBytes;
+    }
+}
+
+void GrResourceCache::setLimits(int maxResources, size_t maxResourceBytes) {
+    bool smaller = (maxResources < fMaxCount) || (maxResourceBytes < fMaxBytes);
+
+    fMaxCount = maxResources;
+    fMaxBytes = maxResourceBytes;
+
+    if (smaller) {
+        this->purgeAsNeeded();
+    }
+}
+
+void GrResourceCache::internalDetach(GrResourceCacheEntry* entry) {
+    fList.remove(entry);
+    fEntryCount -= 1;
+    fEntryBytes -= entry->fCachedSize;
+}
+
+void GrResourceCache::attachToHead(GrResourceCacheEntry* entry) {
+    fList.addToHead(entry);
+
+    fEntryCount += 1;
+    fEntryBytes += entry->fCachedSize;
+
+#if GR_CACHE_STATS
+    if (fHighWaterEntryCount < fEntryCount) {
+        fHighWaterEntryCount = fEntryCount;
+    }
+    if (fHighWaterEntryBytes < fEntryBytes) {
+        fHighWaterEntryBytes = fEntryBytes;
+    }
+#endif
+}
+
+
+void GrResourceCache::makeResourceMRU(GrGpuResource* resource) {
+    GrResourceCacheEntry* entry = resource->cacheAccess().getCacheEntry();
+    if (entry) {
+        this->internalDetach(entry);
+        this->attachToHead(entry);
+    }
+}
+
+void GrResourceCache::notifyPurgable(const GrGpuResource* resource) {
+    // Remove scratch textures from the cache the moment they become purgeable if
+    // scratch texture reuse is turned off.
+    SkASSERT(resource->cacheAccess().getCacheEntry());
+    if (resource->cacheAccess().isScratch()) {
+        const GrResourceKey& key = resource->cacheAccess().getScratchKey();
+        if (key.getResourceType() == GrTexturePriv::ResourceType() &&
+            !fCaps->reuseScratchTextures() &&
+            !(static_cast<const GrSurface*>(resource)->desc().fFlags & kRenderTarget_GrSurfaceFlag)) {
+            this->deleteResource(resource->cacheAccess().getCacheEntry());
+        }
+    }
+}
+
+bool GrResourceCache::addResource(const GrResourceKey& key, GrGpuResource* resource) {
+    if (NULL != resource->cacheAccess().getCacheEntry()) {
+        return false;
+    }
+    
+    if (key.isScratch()) {
+        SkASSERT(resource->cacheAccess().isScratch());
+        SkASSERT(key == resource->cacheAccess().getScratchKey());
+    } else {
+        if (!resource->cacheAccess().setContentKey(key)) {
+            return false;
+        }
+    }
+
+    // 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
+    // unlocks 1 thereby causing a new purge).
+    SkASSERT(!fPurging);
+    GrAutoResourceCacheValidate atcv(this);
+
+    GrResourceCacheEntry* entry = SkNEW_ARGS(GrResourceCacheEntry, (this, resource));
+    resource->cacheAccess().setCacheEntry(entry);
+
+    this->attachToHead(entry);
+    this->purgeAsNeeded();
+    return true;
+}
+
+void GrResourceCache::didIncreaseResourceSize(const GrResourceCacheEntry* entry, size_t amountInc) {
+    fEntryBytes += amountInc;
+    this->purgeAsNeeded();
+}
+
+void GrResourceCache::didDecreaseResourceSize(const GrResourceCacheEntry* entry, size_t amountDec) {
+    fEntryBytes -= amountDec;
+#ifdef SK_DEBUG
+    this->validate();
+#endif
+}
+
+/**
+ * Destroying a resource may potentially trigger the unlock of additional
+ * resources which in turn will trigger a nested purge. We block the nested
+ * purge using the fPurging variable. However, the initial purge will keep
+ * looping until either all resources in the cache are unlocked or we've met
+ * the budget. There is an assertion in createAndLock to check against a
+ * resource's destructor inserting new resources into the cache. If these
+ * new resources were unlocked before purgeAsNeeded completed it could
+ * potentially make purgeAsNeeded loop infinitely.
+ *
+ * extraCount and extraBytes are added to the current resource totals to account
+ * for incoming resources (e.g., GrContext is about to add 10MB split between
+ * 10 textures).
+ */
+void GrResourceCache::purgeAsNeeded(int extraCount, size_t extraBytes) {
+    if (fPurging) {
+        return;
+    }
+
+    fPurging = true;
+
+    this->internalPurge(extraCount, extraBytes);
+    if (((fEntryCount+extraCount) > fMaxCount ||
+        (fEntryBytes+extraBytes) > fMaxBytes) &&
+        fOverbudgetCB) {
+        // Despite the purge we're still over budget. See if Ganesh can
+        // release some resources and purge again.
+        if ((*fOverbudgetCB)(fOverbudgetData)) {
+            this->internalPurge(extraCount, extraBytes);
+        }
+    }
+
+    fPurging = false;
+}
+
+void GrResourceCache::purgeInvalidated() {
+    // TODO: Implement this in GrResourceCache2.
+}
+
+void GrResourceCache::deleteResource(GrResourceCacheEntry* entry) {
+    SkASSERT(entry->fResource->isPurgable());
+    // remove from our llist
+    this->internalDetach(entry);
+    delete entry;
+}
+
+void GrResourceCache::internalPurge(int extraCount, size_t extraBytes) {
+    SkASSERT(fPurging);
+
+    bool withinBudget = false;
+    bool changed = false;
+
+    // The purging process is repeated several times since one pass
+    // may free up other resources
+    do {
+        EntryList::Iter iter;
+
+        changed = false;
+
+        // Note: the following code relies on the fact that the
+        // doubly linked list doesn't invalidate its data/pointers
+        // outside of the specific area where a deletion occurs (e.g.,
+        // in internalDetach)
+        GrResourceCacheEntry* entry = iter.init(fList, EntryList::Iter::kTail_IterStart);
+
+        while (entry) {
+            GrAutoResourceCacheValidate atcv(this);
+
+            if ((fEntryCount+extraCount) <= fMaxCount &&
+                (fEntryBytes+extraBytes) <= fMaxBytes) {
+                withinBudget = true;
+                break;
+            }
+
+            GrResourceCacheEntry* prev = iter.prev();
+            if (entry->fResource->isPurgable()) {
+                changed = true;
+                this->deleteResource(entry);
+            }
+            entry = prev;
+        }
+    } while (!withinBudget && changed);
+}
+
+void GrResourceCache::purgeAllUnlocked() {
+    GrAutoResourceCacheValidate atcv(this);
+
+    // we can have one GrCacheable holding a lock on another
+    // so we don't want to just do a simple loop kicking each
+    // entry out. Instead change the budget and purge.
+
+    size_t savedMaxBytes = fMaxBytes;
+    int savedMaxCount = fMaxCount;
+    fMaxBytes = (size_t) -1;
+    fMaxCount = 0;
+    this->purgeAsNeeded();
+
+    fMaxBytes = savedMaxBytes;
+    fMaxCount = savedMaxCount;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_DEBUG
+size_t GrResourceCache::countBytes(const EntryList& list) {
+    size_t bytes = 0;
+
+    EntryList::Iter iter;
+
+    const GrResourceCacheEntry* entry = iter.init(const_cast<EntryList&>(list),
+                                                  EntryList::Iter::kTail_IterStart);
+
+    for ( ; entry; entry = iter.prev()) {
+        bytes += entry->resource()->gpuMemorySize();
+    }
+    return bytes;
+}
+
+static bool both_zero_or_nonzero(int count, size_t bytes) {
+    return (count == 0 && bytes == 0) || (count > 0 && bytes > 0);
+}
+
+void GrResourceCache::validate() const {
+    fList.validate();
+    SkASSERT(both_zero_or_nonzero(fEntryCount, fEntryBytes));
+
+    EntryList::Iter iter;
+
+    // check that the shareable entries are okay
+    const GrResourceCacheEntry* entry = iter.init(const_cast<EntryList&>(fList),
+                                                  EntryList::Iter::kHead_IterStart);
+
+    int count = 0;
+    for ( ; entry; entry = iter.next()) {
+        entry->validate();
+        count += 1;
+    }
+    SkASSERT(count == fEntryCount);
+
+    size_t bytes = this->countBytes(fList);
+    SkASSERT(bytes == fEntryBytes);
+    SkASSERT(fList.countEntries() == fEntryCount);
+}
+#endif // SK_DEBUG
+
+#if GR_CACHE_STATS
+
+void GrResourceCache::printStats() {
+    int locked = 0;
+    int scratch = 0;
+
+    EntryList::Iter iter;
+
+    GrResourceCacheEntry* entry = iter.init(fList, EntryList::Iter::kTail_IterStart);
+
+    for ( ; entry; entry = iter.prev()) {
+        if (!entry->fResource->isPurgable()) {
+            ++locked;
+        }
+        if (entry->fResource->cacheAccess().isScratch()) {
+            ++scratch;
+        }
+    }
+
+    float countUtilization = (100.f * fEntryCount) / fMaxCount;
+    float byteUtilization = (100.f * fEntryBytes) / fMaxBytes;
+
+    SkDebugf("Budget: %d items %d bytes\n", fMaxCount, fMaxBytes);
+    SkDebugf("\t\tEntry Count: current %d (%d locked, %d scratch %.2g%% full), high %d\n",
+                fEntryCount, locked, scratch, countUtilization, fHighWaterEntryCount);
+    SkDebugf("\t\tEntry Bytes: current %d (%.2g%% full) high %d\n",
+                fEntryBytes, byteUtilization, fHighWaterEntryBytes);
+}
+
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
diff --git a/src/gpu/GrResourceCache.h b/src/gpu/GrResourceCache.h
new file mode 100644
index 0000000..80e4b3f
--- /dev/null
+++ b/src/gpu/GrResourceCache.h
@@ -0,0 +1,251 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrResourceCache_DEFINED
+#define GrResourceCache_DEFINED
+
+#include "GrDrawTargetCaps.h"
+#include "GrResourceKey.h"
+#include "SkTMultiMap.h"
+#include "SkMessageBus.h"
+#include "SkTInternalLList.h"
+
+class GrGpuResource;
+class GrResourceCache;
+class GrResourceCacheEntry;
+
+
+// The cache listens for these messages to purge junk resources proactively.
+struct GrResourceInvalidatedMessage {
+    GrResourceKey key;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class GrResourceCacheEntry {
+public:
+    GrGpuResource* resource() const { return fResource; }
+
+    static uint32_t Hash(const GrGpuResource* resource) {
+        return static_cast<uint32_t>(reinterpret_cast<intptr_t>(resource));
+    }
+#ifdef SK_DEBUG
+    void validate() const;
+#else
+    void validate() const {}
+#endif
+
+    /**
+     *  Update the cached size for this entry and inform the resource cache that
+     *  it has changed. Usually invoked from GrGpuResource::didChangeGpuMemorySize,
+     *  not directly from here.
+     */
+    void didChangeResourceSize();
+
+private:
+    GrResourceCacheEntry(GrResourceCache*, GrGpuResource*);
+    ~GrResourceCacheEntry();
+
+    GrResourceCache* fResourceCache;
+    GrGpuResource*   fResource;
+    size_t           fCachedSize;
+
+    // Linked list for the LRU ordering.
+    SK_DECLARE_INTERNAL_LLIST_INTERFACE(GrResourceCacheEntry);
+
+    friend class GrResourceCache;
+    friend class GrContext;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ *  Cache of GrGpuResource objects.
+ *
+ *  These have a corresponding GrResourceKey, built from 128bits identifying the
+ *  resource. Multiple resources can map to same GrResourceKey.
+ *
+ *  The cache stores the entries in a double-linked list, which is its LRU.
+ *  When an entry is "locked" (i.e. given to the caller), it is moved to the
+ *  head of the list. If/when we must purge some of the entries, we walk the
+ *  list backwards from the tail, since those are the least recently used.
+ *
+ *  For fast searches, we maintain a hash map based on the GrResourceKey.
+ *
+ *  It is a goal to make the GrResourceCache the central repository and bookkeeper
+ *  of all resources. It should replace the linked list of GrGpuResources that
+ *  GrGpu uses to call abandon/release.
+ */
+class GrResourceCache {
+public:
+    GrResourceCache(const GrDrawTargetCaps*, int maxCount, size_t maxBytes);
+    ~GrResourceCache();
+
+    /**
+     *  Return the current resource cache limits.
+     *
+     *  @param maxResource If non-null, returns maximum number of resources
+     *                     that can be held in the cache.
+     *  @param maxBytes    If non-null, returns maximum number of bytes of
+     *                     gpu memory that can be held in the cache.
+     */
+    void getLimits(int* maxResources, size_t* maxBytes) const;
+
+    /**
+     *  Specify the resource cache limits. If the current cache exceeds either
+     *  of these, it will be purged (LRU) to keep the cache within these limits.
+     *
+     *  @param maxResources The maximum number of resources that can be held in
+     *                      the cache.
+     *  @param maxBytes     The maximum number of bytes of resource memory that
+     *                      can be held in the cache.
+     */
+    void setLimits(int maxResources, size_t maxResourceBytes);
+
+    /**
+     *  The callback function used by the cache when it is still over budget
+     *  after a purge. The passed in 'data' is the same 'data' handed to
+     *  setOverbudgetCallback. The callback returns true if some resources
+     *  have been freed.
+     */
+    typedef bool (*PFOverbudgetCB)(void* data);
+
+    /**
+     *  Set the callback the cache should use when it is still over budget
+     *  after a purge. The 'data' provided here will be passed back to the
+     *  callback. Note that the cache will attempt to purge any resources newly
+     *  freed by the callback.
+     */
+    void setOverbudgetCallback(PFOverbudgetCB overbudgetCB, void* data) {
+        fOverbudgetCB = overbudgetCB;
+        fOverbudgetData = data;
+    }
+
+    /**
+     * Returns the number of bytes consumed by cached resources.
+     */
+    size_t getCachedResourceBytes() const { return fEntryBytes; }
+
+    /**
+     * Returns the number of cached resources.
+     */
+    int getCachedResourceCount() const { return fEntryCount; }
+
+    void makeResourceMRU(GrGpuResource*);
+
+    /** Called by GrGpuResources when they detects that they are newly purgable. */
+    void notifyPurgable(const GrGpuResource*);
+
+    /**
+     *  Add the new resource to the cache (by creating 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.
+     *
+     *  This can fail if the key is already taken, or the resource is already in
+     *  the cache.
+     */
+    bool addResource(const GrResourceKey& key, GrGpuResource* resource);
+
+    /**
+     * Notify the cache that the size of a resource has changed.
+     */
+    void didIncreaseResourceSize(const GrResourceCacheEntry*, size_t amountInc);
+    void didDecreaseResourceSize(const GrResourceCacheEntry*, size_t amountDec);
+
+    /**
+     * Remove a resource from the cache and delete it!
+     */
+    void deleteResource(GrResourceCacheEntry* entry);
+
+    /**
+     * Removes every resource in the cache that isn't locked.
+     */
+    void purgeAllUnlocked();
+
+    /**
+     * Allow cache to purge unused resources to obey resource limitations
+     * Note: this entry point will be hidden (again) once totally ref-driven
+     * cache maintenance is implemented. Note that the overbudget callback
+     * will be called if the initial purge doesn't get the cache under
+     * its budget.
+     *
+     * extraCount and extraBytes are added to the current resource allocation
+     * to make sure enough room is available for future additions (e.g,
+     * 10MB across 10 textures is about to be added).
+     */
+    void purgeAsNeeded(int extraCount = 0, size_t extraBytes = 0);
+
+#ifdef SK_DEBUG
+    void validate() const;
+#else
+    void validate() const {}
+#endif
+
+#if GR_CACHE_STATS
+    void printStats();
+#endif
+
+private:
+    void internalDetach(GrResourceCacheEntry*);
+    void attachToHead(GrResourceCacheEntry*);
+    void purgeInvalidated();
+    void internalPurge(int extraCount, size_t extraBytes);
+#ifdef SK_DEBUG
+    static size_t countBytes(const SkTInternalLList<GrResourceCacheEntry>& list);
+#endif
+
+    // We're an internal doubly linked list
+    typedef SkTInternalLList<GrResourceCacheEntry> EntryList;
+    EntryList                               fList;
+
+    // our budget, used in purgeAsNeeded()
+    int                                     fMaxCount;
+    size_t                                  fMaxBytes;
+
+    // our current stats, related to our budget
+#if GR_CACHE_STATS
+    int                                     fHighWaterEntryCount;
+    size_t                                  fHighWaterEntryBytes;
+#endif
+
+    int                                     fEntryCount;
+    size_t                                  fEntryBytes;
+
+    // prevents recursive purging
+    bool                                    fPurging;
+
+    PFOverbudgetCB                          fOverbudgetCB;
+    void*                                   fOverbudgetData;
+
+    SkAutoTUnref<const GrDrawTargetCaps>    fCaps;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_DEBUG
+    class GrAutoResourceCacheValidate {
+    public:
+        GrAutoResourceCacheValidate(GrResourceCache* cache) : fCache(cache) {
+            cache->validate();
+        }
+        ~GrAutoResourceCacheValidate() {
+            fCache->validate();
+        }
+    private:
+        GrResourceCache* fCache;
+    };
+#else
+    class GrAutoResourceCacheValidate {
+    public:
+        GrAutoResourceCacheValidate(GrResourceCache*) {}
+    };
+#endif
+
+#endif
diff --git a/src/gpu/GrResourceCache2.cpp b/src/gpu/GrResourceCache2.cpp
index 109e815..83143d7 100644
--- a/src/gpu/GrResourceCache2.cpp
+++ b/src/gpu/GrResourceCache2.cpp
@@ -10,13 +10,6 @@
 #include "GrResourceCache2.h"
 #include "GrGpuResource.h"  
 
-#include "SkGr.h"
-#include "SkMessageBus.h"
-
-DECLARE_SKMESSAGEBUS_MESSAGE(GrResourceInvalidatedMessage);
-
-//////////////////////////////////////////////////////////////////////////////
-
 GrResourceKey& GrResourceKey::NullScratchKey() {
     static const GrCacheID::Key kBogusKey = { { {0} } };
     static GrCacheID kBogusID(ScratchDomain(), kBogusKey);
@@ -34,85 +27,26 @@
     return gDomain;
 }
 
-GrResourceKey::ResourceType GrResourceKey::GenerateResourceType() {
-    static int32_t gNextType = 0;
-
-    int32_t type = sk_atomic_inc(&gNextType);
-    if (type >= (1 << 8 * sizeof(ResourceType))) {
-        SkFAIL("Too many Resource Types");
-    }
-
-    return static_cast<ResourceType>(type);
-}
-
 //////////////////////////////////////////////////////////////////////////////
 
-class GrResourceCache2::AutoValidate : ::SkNoncopyable {
-public:
-    AutoValidate(GrResourceCache2* cache) : fCache(cache) { cache->validate(); }
-    ~AutoValidate() { fCache->validate(); }
-private:
-    GrResourceCache2* fCache;
-};
-
- //////////////////////////////////////////////////////////////////////////////
-
-static const int kDefaultMaxCount = 2 * (1 << 10);
-static const size_t kDefaultMaxSize = 96 * (1 << 20);
-
-GrResourceCache2::GrResourceCache2()
-    : fMaxCount(kDefaultMaxCount)
-    , fMaxBytes(kDefaultMaxSize)
-#if GR_CACHE_STATS
-    , fHighWaterCount(0)
-    , fHighWaterBytes(0)
-#endif
-    , fCount(0)
-    , fBytes(0)
-    , fPurging(false)
-    , fNewlyPurgableResourceWhilePurging(false)
-    , fOverBudgetCB(NULL)
-    , fOverBudgetData(NULL) {
-}
-
 GrResourceCache2::~GrResourceCache2() {
     this->releaseAll();
 }
 
-void GrResourceCache2::setLimits(int count, size_t bytes) {
-    fMaxCount = count;
-    fMaxBytes = bytes;
-    this->purgeAsNeeded();
-}
-
 void GrResourceCache2::insertResource(GrGpuResource* resource) {
-    AutoValidate av(this);
-
     SkASSERT(resource);
     SkASSERT(!resource->wasDestroyed());
     SkASSERT(!this->isInCache(resource));
-    SkASSERT(!fPurging);
     fResources.addToHead(resource);
-    resource->ref();
-
     ++fCount;
-    SkDEBUGCODE(fHighWaterCount = SkTMax(fCount, fHighWaterCount));
-    fBytes += resource->gpuMemorySize();
-    SkDEBUGCODE(fHighWaterBytes = SkTMax(fBytes, fHighWaterBytes));
     if (!resource->cacheAccess().getScratchKey().isNullScratch()) {
         // TODO(bsalomon): Make this assertion possible.
         // SkASSERT(!resource->isWrapped());
         fScratchMap.insert(resource->cacheAccess().getScratchKey(), resource);
     }
-    
-    this->purgeAsNeeded();
 }
 
 void GrResourceCache2::removeResource(GrGpuResource* resource) {
-    AutoValidate av(this);
-
-    --fCount;
-    fBytes -= resource->gpuMemorySize();
     SkASSERT(this->isInCache(resource));
     fResources.remove(resource);    
     if (!resource->cacheAccess().getScratchKey().isNullScratch()) {
@@ -121,16 +55,13 @@
     if (const GrResourceKey* contentKey = resource->cacheAccess().getContentKey()) {
         fContentHash.remove(*contentKey);
     }
+    --fCount;
 }
 
 void GrResourceCache2::abandonAll() {
-    AutoValidate av(this);
-
-    SkASSERT(!fPurging);
     while (GrGpuResource* head = fResources.head()) {
         SkASSERT(!head->wasDestroyed());
         head->abandon();
-        head->unref();
         // abandon should have already removed this from the list.
         SkASSERT(head != fResources.head());
     }
@@ -140,13 +71,9 @@
 }
 
 void GrResourceCache2::releaseAll() {
-    AutoValidate av(this);
-
-    SkASSERT(!fPurging);
     while (GrGpuResource* head = fResources.head()) {
         SkASSERT(!head->wasDestroyed());
         head->release();
-        head->unref();
         // release should have already removed this from the list.
         SkASSERT(head != fResources.head());
     }
@@ -172,16 +99,11 @@
 
 GrGpuResource* GrResourceCache2::findAndRefScratchResource(const GrResourceKey& scratchKey,
                                                            uint32_t flags) {
-    AutoValidate av(this);
-
-    SkASSERT(!fPurging);
     SkASSERT(scratchKey.isScratch());
 
-    GrGpuResource* resource;
     if (flags & (kPreferNoPendingIO_ScratchFlag | kRequireNoPendingIO_ScratchFlag)) {
-        resource = fScratchMap.find(scratchKey, AvailableForScratchUse(true));
+        GrGpuResource* resource = fScratchMap.find(scratchKey, AvailableForScratchUse(true));
         if (resource) {
-            this->makeResourceMRU(resource);
             return SkRef(resource);
         } else if (flags & kRequireNoPendingIO_ScratchFlag) {
             return NULL;
@@ -189,18 +111,11 @@
         // TODO: fail here when kPrefer is specified, we didn't find a resource without pending io,
         // but there is still space in our budget for the resource.
     }
-    resource = fScratchMap.find(scratchKey, AvailableForScratchUse(false));
-    if (resource) {
-        resource->ref();
-        this->makeResourceMRU(resource);
-    }
-    return resource;
+    return SkSafeRef(fScratchMap.find(scratchKey, AvailableForScratchUse(false)));
 }
 
 bool GrResourceCache2::didSetContentKey(GrGpuResource* resource) {
-    SkASSERT(!fPurging);
     SkASSERT(resource);
-    SkASSERT(this->isInCache(resource));
     SkASSERT(resource->cacheAccess().getContentKey());
     SkASSERT(!resource->cacheAccess().getContentKey()->isScratch());
 
@@ -210,214 +125,5 @@
     }
 
     fContentHash.add(resource);
-    this->validate();
     return true;
 }
-
-void GrResourceCache2::makeResourceMRU(GrGpuResource* resource) {
-    AutoValidate av(this);
-
-    SkASSERT(!fPurging);
-    SkASSERT(resource);
-    SkASSERT(this->isInCache(resource));
-    fResources.remove(resource);    
-    fResources.addToHead(resource);    
-}
-
-void GrResourceCache2::notifyPurgable(const GrGpuResource* resource) {
-    SkASSERT(resource);
-    SkASSERT(this->isInCache(resource));
-    SkASSERT(resource->isPurgable());
-
-    // We can't purge if in the middle of purging because purge is iterating. Instead record
-    // that additional resources became purgable.
-    if (fPurging) {
-        fNewlyPurgableResourceWhilePurging = true;
-        return;
-    }
-
-    // Purge the resource if we're over budget
-    bool overBudget = fCount > fMaxCount || fBytes > fMaxBytes;
-
-    // We should not be over budget here unless all resources are unpuragble.
-#ifdef SK_DEBUG
-    if (overBudget) {
-        ResourceList::Iter iter;
-        GrGpuResource* r = iter.init(fResources, ResourceList::Iter::kHead_IterStart);
-        for ( ; r; r = iter.next()) {
-            SkASSERT(r == resource || !r->isPurgable());
-        }
-    }
-#endif
-
-    // Also purge if the resource has neither a valid scratch key nor a content key.
-    bool noKey = !resource->cacheAccess().isScratch() &&
-                 (NULL == resource->cacheAccess().getContentKey());
-
-    if (overBudget || noKey) {
-        SkDEBUGCODE(int beforeCount = fCount;)
-        resource->unref();
-        SkASSERT(fCount == beforeCount - 1);
-    }
-
-    this->validate();
-}
-
-void GrResourceCache2::didChangeGpuMemorySize(const GrGpuResource* resource, size_t oldSize) {
-    // SkASSERT(!fPurging); GrPathRange increases size during flush. :(
-    SkASSERT(resource);
-    SkASSERT(this->isInCache(resource));
-
-    fBytes += resource->gpuMemorySize() - oldSize;
-    SkDEBUGCODE(fHighWaterBytes = SkTMax(fBytes, fHighWaterBytes));
-
-    this->purgeAsNeeded();
-    this->validate();
-}
-
-void GrResourceCache2::internalPurgeAsNeeded() {
-    SkASSERT(!fPurging);
-    SkASSERT(!fNewlyPurgableResourceWhilePurging);
-    SkASSERT(fCount > fMaxCount || fBytes > fMaxBytes);
-
-    fPurging = true;
-
-    AutoValidate av(this); // Put this after setting fPurging so we're allowed to be over budget.
-
-    bool overBudget = true;
-    do {
-        fNewlyPurgableResourceWhilePurging = false;
-        ResourceList::Iter resourceIter;
-        GrGpuResource* resource = resourceIter.init(fResources,
-                                                    ResourceList::Iter::kTail_IterStart);
-
-        while (resource) {
-            GrGpuResource* prev = resourceIter.prev();
-            if (resource->isPurgable()) {
-                resource->unref();
-            }
-            resource = prev;
-            if (fCount <= fMaxCount && fBytes <= fMaxBytes) {
-                overBudget = false;
-                resource = NULL;
-            }
-        }
-
-        if (!fNewlyPurgableResourceWhilePurging && overBudget && fOverBudgetCB) {
-            // Despite the purge we're still over budget. Call our over budget callback.
-            (*fOverBudgetCB)(fOverBudgetData);
-        }
-    } while (overBudget && fNewlyPurgableResourceWhilePurging);
-
-    fNewlyPurgableResourceWhilePurging = false;
-    fPurging = false;
-}
-
-void GrResourceCache2::purgeAllUnlocked() {
-    SkASSERT(!fPurging);
-    SkASSERT(!fNewlyPurgableResourceWhilePurging);
-
-    fPurging = true;
-
-    AutoValidate av(this); // Put this after setting fPurging so we're allowed to be over budget.
-
-    do {
-        fNewlyPurgableResourceWhilePurging = false;
-        ResourceList::Iter resourceIter;
-        GrGpuResource* resource =
-            resourceIter.init(fResources, ResourceList::Iter::kTail_IterStart);
-
-        while (resource) {
-            GrGpuResource* prev = resourceIter.prev();
-            if (resource->isPurgable()) {
-                resource->unref();
-            } 
-            resource = prev;
-        }
-
-        if (!fNewlyPurgableResourceWhilePurging && fCount && fOverBudgetCB) {
-            (*fOverBudgetCB)(fOverBudgetData);
-        }
-    } while (fNewlyPurgableResourceWhilePurging);
-    fPurging = false;
-}
-
-#ifdef SK_DEBUG
-void GrResourceCache2::validate() const {
-    size_t bytes = 0;
-    int count = 0;
-    int locked = 0;
-    int scratch = 0;
-    int couldBeScratch = 0;
-    int content = 0;
-
-    ResourceList::Iter iter;
-    GrGpuResource* resource = iter.init(fResources, ResourceList::Iter::kHead_IterStart);
-    for ( ; resource; resource = iter.next()) {
-        bytes += resource->gpuMemorySize();
-        ++count;
-
-        if (!resource->isPurgable()) {
-            ++locked;
-        }
-
-        if (resource->cacheAccess().isScratch()) {
-            SkASSERT(NULL == resource->cacheAccess().getContentKey());
-            ++scratch;
-            SkASSERT(fScratchMap.countForKey(resource->cacheAccess().getScratchKey()));
-        } else if (!resource->cacheAccess().getScratchKey().isNullScratch()) {
-            SkASSERT(NULL != resource->cacheAccess().getContentKey());
-            ++couldBeScratch;
-            SkASSERT(fScratchMap.countForKey(resource->cacheAccess().getScratchKey()));
-        }
-
-        if (const GrResourceKey* contentKey = resource->cacheAccess().getContentKey()) {
-            ++content;
-            SkASSERT(fContentHash.find(*contentKey) == resource);
-        }
-    }
-
-    SkASSERT(bytes == fBytes);
-    SkASSERT(count == fCount);
-#if GR_CACHE_STATS
-    SkASSERT(bytes <= fHighWaterBytes);
-    SkASSERT(count <= fHighWaterCount);
-#endif
-    SkASSERT(content == fContentHash.count());
-    SkASSERT(scratch + couldBeScratch == fScratchMap.count());
-
-    bool overBudget = bytes > fMaxBytes || count > fMaxCount;
-    SkASSERT(!overBudget || locked == count || fPurging);
-}
-#endif
-
-#if GR_CACHE_STATS
-void GrResourceCache2::printStats() const {
-    this->validate();
-
-    int locked = 0;
-    int scratch = 0;
-
-    ResourceList::Iter iter;
-    GrGpuResource* resource = iter.init(fResources, ResourceList::Iter::kHead_IterStart);
-
-    for ( ; resource; resource = iter.next()) {
-        if (!resource->isPurgable()) {
-            ++locked;
-        }
-        if (resource->cacheAccess().isScratch()) {
-            ++scratch;
-        }
-    }
-
-    float countUtilization = (100.f * fCount) / fMaxCount;
-    float byteUtilization = (100.f * fBytes) / fMaxBytes;
-
-    SkDebugf("Budget: %d items %d bytes\n", fMaxCount, fMaxBytes);
-    SkDebugf("\t\tEntry Count: current %d (%d locked, %d scratch %.2g%% full), high %d\n",
-                fCount, locked, scratch, countUtilization, fHighWaterCount);
-    SkDebugf("\t\tEntry Bytes: current %d (%.2g%% full) high %d\n",
-                fBytes, byteUtilization, fHighWaterBytes);
-}
-
-#endif
diff --git a/src/gpu/GrResourceCache2.h b/src/gpu/GrResourceCache2.h
index 8b4d1d0..1cc9587 100644
--- a/src/gpu/GrResourceCache2.h
+++ b/src/gpu/GrResourceCache2.h
@@ -17,65 +17,27 @@
 #include "SkTMultiMap.h"
 
 /**
- * Manages the lifetime of all GrGpuResource instances.
- *
- * Resources may have optionally have two types of keys:
- *      1) A scratch key. This is for resources whose allocations are cached but not their contents.
- *         Multiple resources can share the same scratch key. This is so a caller can have two
- *         resource instances with the same properties (e.g. multipass rendering that ping pongs
- *         between two temporary surfaces. The scratch key is set at resource creation time and
- *         should never change. Resources need not have a scratch key.
- *      2) A content key. This key represents the contents of the resource rather than just its
- *         allocation properties. They may not collide. The content key can be set after resource
- *         creation. Currently it may only be set once and cannot be cleared. This restriction will
- *         be removed.
- * If a resource has neither key type then it will be deleted as soon as the last reference to it
- * is dropped. If a key has both keys the content key takes precedence.
+ *  Eventual replacement for GrResourceCache. Currently it simply holds a list
+ *  of all GrGpuResource objects for a GrContext. It is used to invalidate all
+ *  the resources when necessary.
  */
 class GrResourceCache2 {
 public:
-    GrResourceCache2();
+    GrResourceCache2() : fCount(0) {};
     ~GrResourceCache2();
 
-    /** Used to access functionality needed by GrGpuResource for lifetime management. */
-    class ResourceAccess;
-    ResourceAccess resourceAccess();
+    void insertResource(GrGpuResource*);
 
-    /**
-     * Sets the cache limits in terms of number of resources and max gpu memory byte size.
-     */
-    void setLimits(int count, size_t bytes);
+    void removeResource(GrGpuResource*);
 
-    /**
-     * Returns the number of cached resources.
-     */
-    int getResourceCount() const { return fCount; }
+    // 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 didSetContentKey(GrGpuResource*);
 
-    /**
-     * Returns the number of bytes consumed by cached resources.
-     */
-    size_t getResourceBytes() const { return fBytes; }
-
-    /**
-     * Returns the cached resources count budget.
-     */
-    int getMaxResourceCount() const { return fMaxCount; }
-
-    /**
-     * Returns the number of bytes consumed by cached resources.
-     */
-    size_t getMaxResourceBytes() const { return fMaxBytes; }
-
-    /**
-     * Abandons the backend API resources owned by all GrGpuResource objects and removes them from
-     * the cache.
-     */
     void abandonAll();
 
-    /**
-     * Releases the backend API resources owned by all GrGpuResource objects and removes them from
-     * the cache.
-     */
     void releaseAll();
 
     enum {
@@ -84,10 +46,6 @@
         /** Will not return any resources that match but have pending IO. */
         kRequireNoPendingIO_ScratchFlag = 0x2,
     };
-
-    /**
-     * Find a resource that matches a scratch key.
-     */
     GrGpuResource* findAndRefScratchResource(const GrResourceKey& scratchKey, uint32_t flags = 0);
     
 #ifdef SK_DEBUG
@@ -98,80 +56,21 @@
     }
 #endif
 
-    /**
-     * Find a resource that matches a content key.
-     */
     GrGpuResource* findAndRefContentResource(const GrResourceKey& contentKey) {
         SkASSERT(!contentKey.isScratch());
-        GrGpuResource* resource = fContentHash.find(contentKey);
-        if (resource) {
-            resource->ref();
-            this->makeResourceMRU(resource);
-        }
-        return resource;
+        return SkSafeRef(fContentHash.find(contentKey));
     }
 
-    /**
-     * Query whether a content key exists in the cache.
-     */
     bool hasContentKey(const GrResourceKey& contentKey) const {
         SkASSERT(!contentKey.isScratch());
         return SkToBool(fContentHash.find(contentKey));
     }
 
-    /** Purges all resources that don't have external owners. */
-    void purgeAllUnlocked();
-
-    /**
-     * The callback function used by the cache when it is still over budget after a purge. The
-     * passed in 'data' is the same 'data' handed to setOverbudgetCallback.
-     */
-    typedef void (*PFOverBudgetCB)(void* data);
-
-    /**
-     * Set the callback the cache should use when it is still over budget after a purge. The 'data'
-     * provided here will be passed back to the callback. Note that the cache will attempt to purge
-     * any resources newly freed by the callback.
-     */
-    void setOverBudgetCallback(PFOverBudgetCB overBudgetCB, void* data) {
-        fOverBudgetCB = overBudgetCB;
-        fOverBudgetData = data;
-    }
-
-#if GR_GPU_STATS
-    void printStats() const;
-#endif
-
 private:
-    ///////////////////////////////////////////////////////////////////////////
-    /// @name Methods accessible via ResourceAccess
-    ////
-    void insertResource(GrGpuResource*);
-    void removeResource(GrGpuResource*);
-    void notifyPurgable(const GrGpuResource*);
-    void didChangeGpuMemorySize(const GrGpuResource*, size_t oldSize);
-    bool didSetContentKey(GrGpuResource*);
-    void makeResourceMRU(GrGpuResource*);
-    /// @}
-
-    void purgeAsNeeded() {
-        if (fPurging || (fCount <= fMaxCount && fBytes < fMaxBytes)) {
-            return;
-        }
-        this->internalPurgeAsNeeded();
-    }
-
-    void internalPurgeAsNeeded();
-
 #ifdef SK_DEBUG
     bool isInCache(const GrGpuResource* r) const { return fResources.isInList(r); }
-    void validate() const;
-#else
-    void validate() const {}
 #endif
 
-    class AutoValidate;
-
     class AvailableForScratchUse;
 
     struct ScratchMapTraits {
@@ -192,86 +91,12 @@
     };
     typedef SkTDynamicHash<GrGpuResource, GrResourceKey, ContentHashTraits> ContentHash;
 
-    typedef SkTInternalLList<GrGpuResource> ResourceList;
-
-    ResourceList                        fResources;
+    int                                 fCount;
+    SkTInternalLList<GrGpuResource>     fResources;
     // This map holds all resources that can be used as scratch resources.
     ScratchMap                          fScratchMap;
     // This holds all resources that have content keys.
     ContentHash                         fContentHash;
-
-    // our budget, used in purgeAsNeeded()
-    int                                 fMaxCount;
-    size_t                              fMaxBytes;
-
-#if GR_CACHE_STATS
-    int                                 fHighWaterCount;
-    size_t                              fHighWaterBytes;
-#endif
-
-    // our current stats, related to our budget
-    int                                 fCount;
-    size_t                              fBytes;
-
-    // prevents recursive purging
-    bool                                fPurging;
-    bool                                fNewlyPurgableResourceWhilePurging;
-
-    PFOverBudgetCB                      fOverBudgetCB;
-    void*                               fOverBudgetData;
-
 };
 
-class GrResourceCache2::ResourceAccess {
-private:
-    ResourceAccess(GrResourceCache2* cache) : fCache(cache) { }
-    ResourceAccess(const ResourceAccess& that) : fCache(that.fCache) { }
-    ResourceAccess& operator=(const ResourceAccess&); // unimpl
-
-    /**
-     * Insert a resource into the cache.
-     */
-    void insertResource(GrGpuResource* resource) { fCache->insertResource(resource); }
-
-    /**
-     * Removes a resource from the cache.
-     */
-    void removeResource(GrGpuResource* resource) { fCache->removeResource(resource); }
-
-    /**
-     * Called by GrGpuResources when they detects that they are newly purgable.
-     */
-    void notifyPurgable(const GrGpuResource* resource) { fCache->notifyPurgable(resource); }
-
-    /**
-     * Called by GrGpuResources when their sizes change.
-     */
-    void didChangeGpuMemorySize(const GrGpuResource* resource, size_t oldSize) {
-        fCache->didChangeGpuMemorySize(resource, oldSize);
-    }
-
-    /**
-     * Called by GrGpuResources when their content keys change.
-     *
-     * 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 didSetContentKey(GrGpuResource* resource) { return fCache->didSetContentKey(resource); }
-
-    // No taking addresses of this type.
-    const ResourceAccess* operator&() const;
-    ResourceAccess* operator&();
-
-    GrResourceCache2* fCache;
-
-    friend class GrGpuResource; // To access all the proxy inline methods.
-    friend class GrResourceCache2; // To create this type.
-};
-
-inline GrResourceCache2::ResourceAccess GrResourceCache2::resourceAccess() {
-    return ResourceAccess(this);
-}
-
 #endif
diff --git a/src/gpu/GrStencilBuffer.cpp b/src/gpu/GrStencilBuffer.cpp
index 16b0150..b288415 100644
--- a/src/gpu/GrStencilBuffer.cpp
+++ b/src/gpu/GrStencilBuffer.cpp
@@ -13,6 +13,8 @@
 #include "GrResourceCache2.h"
 
 void GrStencilBuffer::transferToCache() {
+    SkASSERT(!this->cacheAccess().isInCache());
+
     this->getGpu()->getContext()->addStencilBuffer(this);
 }
 
diff --git a/src/gpu/GrTest.cpp b/src/gpu/GrTest.cpp
index 5286984..611059a 100644
--- a/src/gpu/GrTest.cpp
+++ b/src/gpu/GrTest.cpp
@@ -9,7 +9,7 @@
 #include "GrTest.h"
 
 #include "GrInOrderDrawBuffer.h"
-#include "GrResourceCache2.h"
+#include "GrResourceCache.h"
 
 void GrTestTarget::init(GrContext* ctx, GrDrawTarget* target) {
     SkASSERT(!fContext);
@@ -38,7 +38,7 @@
 }
 
 void GrContext::purgeAllUnlockedResources() {
-    fResourceCache2->purgeAllUnlocked();
+    fResourceCache->purgeAllUnlocked();
 }
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/src/gpu/GrTexture.cpp b/src/gpu/GrTexture.cpp
index 9f700c7..58bbeaa 100644
--- a/src/gpu/GrTexture.cpp
+++ b/src/gpu/GrTexture.cpp
@@ -9,6 +9,7 @@
 #include "GrContext.h"
 #include "GrDrawTargetCaps.h"
 #include "GrGpu.h"
+#include "GrResourceCache.h"
 #include "GrTexture.h"
 #include "GrTexturePriv.h"
 
diff --git a/src/gpu/SkGr.cpp b/src/gpu/SkGr.cpp
index a86ee60..affbd95 100644
--- a/src/gpu/SkGr.cpp
+++ b/src/gpu/SkGr.cpp
@@ -12,6 +12,7 @@
 #include "SkMessageBus.h"
 #include "SkPixelRef.h"
 #include "SkTextureCompressor.h"
+#include "GrResourceCache.h"
 #include "GrGpu.h"
 #include "effects/GrDitherEffect.h"
 #include "GrDrawTargetCaps.h"
diff --git a/tests/ResourceCacheTest.cpp b/tests/ResourceCacheTest.cpp
index 6f51d30..7cebddb 100644
--- a/tests/ResourceCacheTest.cpp
+++ b/tests/ResourceCacheTest.cpp
@@ -10,10 +10,9 @@
 #include "GrContext.h"
 #include "GrContextFactory.h"
 #include "GrGpu.h"
+#include "GrResourceCache.h"
 #include "GrResourceCache2.h"
 #include "SkCanvas.h"
-#include "SkGr.h"
-#include "SkMessageBus.h"
 #include "SkSurface.h"
 #include "Test.h"
 
@@ -68,6 +67,7 @@
     SK_DECLARE_INST_COUNT(TestResource);
     TestResource(GrGpu* gpu)
         : INHERITED(gpu, false)
+        , fCache(NULL)
         , fToDelete(NULL)
         , fSize(kDefaultSize) {
         ++fNumAlive;
@@ -76,6 +76,7 @@
 
     TestResource(GrGpu* gpu, const GrResourceKey& scratchKey)
         : INHERITED(gpu, false)
+        , fCache(NULL)
         , fToDelete(NULL)
         , fSize(kDefaultSize) {
         this->setScratchKey(scratchKey);
@@ -85,7 +86,11 @@
 
     ~TestResource() {
         --fNumAlive;
-        SkSafeUnref(fToDelete);
+        if (fToDelete) {
+            // Breaks our little 2-element cycle below.
+            fToDelete->setDeleteWhenDestroyed(NULL, NULL);
+            fCache->deleteResource(fToDelete->cacheAccess().getCacheEntry());
+        }
         this->release();
     }
 
@@ -96,13 +101,15 @@
 
     static int NumAlive() { return fNumAlive; }
 
-    void setUnrefWhenDestroyed(TestResource* resource) {
-        SkRefCnt_SafeAssign(fToDelete, resource);
+    void setDeleteWhenDestroyed(GrResourceCache* cache, TestResource* resource) {
+        fCache = cache;
+        fToDelete = resource;
     }
 
 private:
     size_t onGpuMemorySize() const SK_OVERRIDE { return fSize; }
 
+    GrResourceCache* fCache;
     TestResource* fToDelete;
     size_t fSize;
     static int fNumAlive;
@@ -111,61 +118,6 @@
 };
 int TestResource::fNumAlive = 0;
 
-static void test_no_key(skiatest::Reporter* reporter) {
-    SkAutoTUnref<GrContext> context(GrContext::CreateMockContext());
-    REPORTER_ASSERT(reporter, SkToBool(context));
-    if (NULL == context) {
-        return;
-    }
-    context->setResourceCacheLimits(10, 30000);
-    GrResourceCache2* cache2 = context->getResourceCache2();
-    cache2->purgeAllUnlocked();
-    SkASSERT(0 == cache2->getResourceCount() && 0 == cache2->getResourceBytes());
-
-    // Create a bunch of resources with no keys
-    TestResource* a = new TestResource(context->getGpu());
-    TestResource* b = new TestResource(context->getGpu());
-    TestResource* c = new TestResource(context->getGpu());
-    TestResource* d = new TestResource(context->getGpu());
-    a->setSize(11);
-    b->setSize(12);
-    c->setSize(13);
-    d->setSize(14);
-
-    REPORTER_ASSERT(reporter, 4 == TestResource::NumAlive());
-    REPORTER_ASSERT(reporter, 4 == cache2->getResourceCount());
-    REPORTER_ASSERT(reporter, a->gpuMemorySize() + b->gpuMemorySize() + c->gpuMemorySize() +
-                              d->gpuMemorySize() == cache2->getResourceBytes());
-
-    // Should be safe to purge without deleting the resources since we still have refs.
-    cache2->purgeAllUnlocked();
-    REPORTER_ASSERT(reporter, 4 == TestResource::NumAlive());
-
-    // Since the resources have neither content nor scratch keys, delete immediately upon unref.
-
-    a->unref();
-    REPORTER_ASSERT(reporter, 3 == TestResource::NumAlive());
-    REPORTER_ASSERT(reporter, 3 == cache2->getResourceCount());
-    REPORTER_ASSERT(reporter, b->gpuMemorySize() + c->gpuMemorySize() + d->gpuMemorySize() ==
-                              cache2->getResourceBytes());
-
-    c->unref();
-    REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive());
-    REPORTER_ASSERT(reporter, 2 == cache2->getResourceCount());
-    REPORTER_ASSERT(reporter, b->gpuMemorySize() + d->gpuMemorySize() ==
-                              cache2->getResourceBytes());
-
-    d->unref();
-    REPORTER_ASSERT(reporter, 1 == TestResource::NumAlive());
-    REPORTER_ASSERT(reporter, 1 == cache2->getResourceCount());
-    REPORTER_ASSERT(reporter, b->gpuMemorySize() == cache2->getResourceBytes());
-
-    b->unref();
-    REPORTER_ASSERT(reporter, 0 == TestResource::NumAlive());
-    REPORTER_ASSERT(reporter, 0 == cache2->getResourceCount());
-    REPORTER_ASSERT(reporter, 0 == cache2->getResourceBytes());
-}
-
 static void test_duplicate_scratch_key(skiatest::Reporter* reporter) {
     SkAutoTUnref<GrContext> context(GrContext::CreateMockContext());
     REPORTER_ASSERT(reporter, SkToBool(context));
@@ -173,9 +125,10 @@
         return;
     }
     context->setResourceCacheLimits(5, 30000);
-    GrResourceCache2* cache2 = context->getResourceCache2();
-    cache2->purgeAllUnlocked();
-    SkASSERT(0 == cache2->getResourceCount() && 0 == cache2->getResourceBytes());
+    GrResourceCache* cache = context->getResourceCache();
+    SkDEBUGCODE(GrResourceCache2* cache2 = context->getResourceCache2();)
+    cache->purgeAllUnlocked();
+    SkASSERT(0 == cache->getCachedResourceCount() && 0 == cache->getCachedResourceBytes());
 
     GrCacheID::Key keyData;
     memset(&keyData, 0, sizeof(keyData));
@@ -189,16 +142,30 @@
     a->setSize(11);
     b->setSize(12);
     // Scratch resources are registered with GrResourceCache2 just by existing. There are 2.
+    SkDEBUGCODE(REPORTER_ASSERT(reporter, 2 == cache2->countScratchEntriesForKey(scratchKey));)
+
+    REPORTER_ASSERT(reporter, cache->addResource(scratchKey, a));
+
+    SkDEBUGCODE(REPORTER_ASSERT(reporter, 2 == cache2->countScratchEntriesForKey(scratchKey));)
+
+    // Can't add the same resource twice.
+    REPORTER_ASSERT(reporter, !cache->addResource(scratchKey, a));
+    REPORTER_ASSERT(reporter, 1 == cache->getCachedResourceCount());
+    REPORTER_ASSERT(reporter, a->gpuMemorySize() == cache->getCachedResourceBytes());
+    SkDEBUGCODE(REPORTER_ASSERT(reporter, 2 == cache2->countScratchEntriesForKey(scratchKey));)
+
+    // Add a second with the same key.
+    REPORTER_ASSERT(reporter, cache->addResource(scratchKey, b));
+    REPORTER_ASSERT(reporter, 2 == cache->getCachedResourceCount());
+    REPORTER_ASSERT(reporter, a->gpuMemorySize() + b->gpuMemorySize() ==
+                              cache->getCachedResourceBytes());
     REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive());
     SkDEBUGCODE(REPORTER_ASSERT(reporter, 2 == cache2->countScratchEntriesForKey(scratchKey));)
-    REPORTER_ASSERT(reporter, 2 == cache2->getResourceCount());
-    REPORTER_ASSERT(reporter, a->gpuMemorySize() + b->gpuMemorySize() ==
-                              cache2->getResourceBytes());
 
     // Our refs mean that the resources are non purgable.
-    cache2->purgeAllUnlocked();
+    cache->purgeAllUnlocked();
     REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive());
-    REPORTER_ASSERT(reporter, 2 == cache2->getResourceCount());
+    REPORTER_ASSERT(reporter, 2 == cache->getCachedResourceCount());
 
     // Unref but don't purge
     a->unref();
@@ -207,9 +174,9 @@
     SkDEBUGCODE(REPORTER_ASSERT(reporter, 2 == cache2->countScratchEntriesForKey(scratchKey));)
 
     // Purge again. This time resources should be purgable.
-    cache2->purgeAllUnlocked();
+    cache->purgeAllUnlocked();
     REPORTER_ASSERT(reporter, 0 == TestResource::NumAlive());
-    REPORTER_ASSERT(reporter, 0 == cache2->getResourceCount());
+    REPORTER_ASSERT(reporter, 0 == cache->getCachedResourceCount());
     SkDEBUGCODE(REPORTER_ASSERT(reporter, 0 == cache2->countScratchEntriesForKey(scratchKey));)
 }
 
@@ -220,9 +187,9 @@
         return;
     }
     context->setResourceCacheLimits(5, 30000);
-    GrResourceCache2* cache2 = context->getResourceCache2();
-    cache2->purgeAllUnlocked();
-    SkASSERT(0 == cache2->getResourceCount() && 0 == cache2->getResourceBytes());
+    GrResourceCache* cache = context->getResourceCache();
+    cache->purgeAllUnlocked();
+    SkASSERT(0 == cache->getCachedResourceCount() && 0 == cache->getCachedResourceBytes());
 
     GrCacheID::Domain domain = GrCacheID::GenerateDomain();
     GrCacheID::Key keyData;
@@ -230,42 +197,30 @@
     GrResourceKey::ResourceType t = GrResourceKey::GenerateResourceType();
     GrResourceKey key(GrCacheID(domain, keyData), t, 0);
     
+
     // Create two resources that we will attempt to register with the same content key.
     TestResource* a = new TestResource(context->getGpu());
     TestResource* b = new TestResource(context->getGpu());
     a->setSize(11);
     b->setSize(12);
-    
-    // Can't set the same content key on two resources.
-    REPORTER_ASSERT(reporter, a->cacheAccess().setContentKey(key));
-    REPORTER_ASSERT(reporter, !b->cacheAccess().setContentKey(key));
-
-    // Still have two resources because b is still reffed.
-    REPORTER_ASSERT(reporter, 2 == cache2->getResourceCount());
-    REPORTER_ASSERT(reporter, a->gpuMemorySize() + b->gpuMemorySize() ==
-                              cache2->getResourceBytes());
+    REPORTER_ASSERT(reporter, cache->addResource(key, a));
+    // Can't add the same or another resource with the same key.
+    REPORTER_ASSERT(reporter, !cache->addResource(key, a));
+    REPORTER_ASSERT(reporter, !cache->addResource(key, b));
+    REPORTER_ASSERT(reporter, 1 == cache->getCachedResourceCount());
+    REPORTER_ASSERT(reporter, a->gpuMemorySize() == cache->getCachedResourceBytes());
     REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive());
 
     b->unref();
-    // Now b should be gone.
-    REPORTER_ASSERT(reporter, 1 == cache2->getResourceCount());
-    REPORTER_ASSERT(reporter, a->gpuMemorySize() == cache2->getResourceBytes());
+    cache->purgeAllUnlocked();
+    a->setSize(10);
+    REPORTER_ASSERT(reporter, 1 == cache->getCachedResourceCount());
     REPORTER_ASSERT(reporter, 1 == TestResource::NumAlive());
 
-    cache2->purgeAllUnlocked();
-    REPORTER_ASSERT(reporter, 1 == cache2->getResourceCount());
-    REPORTER_ASSERT(reporter, a->gpuMemorySize() == cache2->getResourceBytes());
-    REPORTER_ASSERT(reporter, 1 == TestResource::NumAlive());
-
-    // Drop the ref on a but it isn't immediately purged as it still has a valid scratch key.
     a->unref();
-    REPORTER_ASSERT(reporter, 1 == cache2->getResourceCount());
-    REPORTER_ASSERT(reporter, a->gpuMemorySize() == cache2->getResourceBytes());
-    REPORTER_ASSERT(reporter, 1 == TestResource::NumAlive());
-
-    cache2->purgeAllUnlocked();
-    REPORTER_ASSERT(reporter, 0 == cache2->getResourceCount());
-    REPORTER_ASSERT(reporter, 0 == cache2->getResourceBytes());
+    cache->purgeAllUnlocked();
+    REPORTER_ASSERT(reporter, 0 == cache->getCachedResourceCount());
+    REPORTER_ASSERT(reporter, 0 == cache->getCachedResourceBytes());
     REPORTER_ASSERT(reporter, 0 == TestResource::NumAlive());
 }
 
@@ -290,17 +245,18 @@
     GrResourceKey key3(GrCacheID(domain, keyData), t, 0);
     
     context->setResourceCacheLimits(5, 30000);
+    GrResourceCache* cache = context->getResourceCache();
     GrResourceCache2* cache2 = context->getResourceCache2();
-    cache2->purgeAllUnlocked();
-    SkASSERT(0 == cache2->getResourceCount() && 0 == cache2->getResourceBytes());
+    cache->purgeAllUnlocked();
+    SkASSERT(0 == cache->getCachedResourceCount() && 0 == cache->getCachedResourceBytes());
 
     // Add three resources to the cache.
     TestResource* a = new TestResource(context->getGpu());
     TestResource* b = new TestResource(context->getGpu());
     TestResource* c = new TestResource(context->getGpu());
-    a->cacheAccess().setContentKey(key1);
-    b->cacheAccess().setContentKey(key2);
-    c->cacheAccess().setContentKey(key3);
+    cache->addResource(key1, a);
+    cache->addResource(key2, b);
+    cache->addResource(key3, c);
     a->unref();
     b->unref();
     c->unref();
@@ -315,8 +271,8 @@
     SkMessageBus<GrResourceInvalidatedMessage>::Post(msg1);
     const GrResourceInvalidatedMessage msg2 = { key2 };
     SkMessageBus<GrResourceInvalidatedMessage>::Post(msg2);
+    cache->purgeAsNeeded();
 #if 0 // Disabled until reimplemented in GrResourceCache2.
-    cache2->purgeAsNeeded();
     REPORTER_ASSERT(reporter, 1 == TestResource::NumAlive());
     REPORTER_ASSERT(reporter, !cache2->hasContentKey(key1));
     REPORTER_ASSERT(reporter, !cache2->hasContentKey(key2));
@@ -326,19 +282,14 @@
     // Invalidate the third.
     const GrResourceInvalidatedMessage msg3 = { key3 };
     SkMessageBus<GrResourceInvalidatedMessage>::Post(msg3);
+    cache->purgeAsNeeded();
 #if 0 // Disabled until reimplemented in GrResourceCache2.
-    cache2->purgeAsNeeded();
     REPORTER_ASSERT(reporter, 0 == TestResource::NumAlive());
     REPORTER_ASSERT(reporter, !cache2->hasContentKey(key3));
 #endif
-
-    cache2->purgeAllUnlocked();
-    REPORTER_ASSERT(reporter, 0 == TestResource::NumAlive());
-    REPORTER_ASSERT(reporter, 0 == cache2->getResourceCount());
-    REPORTER_ASSERT(reporter, 0 == cache2->getResourceBytes());
 }
 
-static void test_cache_chained_purge(skiatest::Reporter* reporter) {
+static void test_cache_delete_on_destruction(skiatest::Reporter* reporter) {
     SkAutoTUnref<GrContext> context(GrContext::CreateMockContext());
     REPORTER_ASSERT(reporter, SkToBool(context));
     if (NULL == context) {
@@ -358,34 +309,44 @@
 
     {
         context->setResourceCacheLimits(3, 30000);
-        GrResourceCache2* cache2 = context->getResourceCache2();
-        cache2->purgeAllUnlocked();
-        SkASSERT(0 == cache2->getResourceCount() && 0 == cache2->getResourceBytes());
+        GrResourceCache* cache = context->getResourceCache();
+        cache->purgeAllUnlocked();
+        SkASSERT(0 == cache->getCachedResourceCount() && 0 == cache->getCachedResourceBytes());
 
         TestResource* a = new TestResource(context->getGpu());
         TestResource* b = new TestResource(context->getGpu());
-        a->cacheAccess().setContentKey(key1);
-        b->cacheAccess().setContentKey(key2);
+        cache->addResource(key1, a);
+        cache->addResource(key2, b);
 
-        // Make a cycle
-        a->setUnrefWhenDestroyed(b);
-        b->setUnrefWhenDestroyed(a);
-
-        REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive());
+        a->setDeleteWhenDestroyed(cache, b);
+        b->setDeleteWhenDestroyed(cache, a);
 
         a->unref();
         b->unref();
 
         REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive());
 
-        cache2->purgeAllUnlocked();
-        REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive());
+        cache->purgeAllUnlocked();
+        REPORTER_ASSERT(reporter, 0 == TestResource::NumAlive());
+    }
+    {
+        context->setResourceCacheLimits(3, 30000);
+        GrResourceCache* cache = context->getResourceCache();
+        cache->purgeAllUnlocked();
+        SkASSERT(0 == cache->getCachedResourceCount() && 0 == cache->getCachedResourceBytes());
 
-        // Break the cycle
-        a->setUnrefWhenDestroyed(NULL);
-        REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive());
+        TestResource* a = new TestResource(context->getGpu());
+        TestResource* b = new TestResource(context->getGpu());
+        cache->addResource(key1, a);
+        cache->addResource(key2, b);
 
-        cache2->purgeAllUnlocked();
+        a->setDeleteWhenDestroyed(cache, b);
+        b->setDeleteWhenDestroyed(cache, a);
+
+        a->unref();
+        b->unref();
+
+        cache->deleteResource(a->cacheAccess().getCacheEntry());
         REPORTER_ASSERT(reporter, 0 == TestResource::NumAlive());
     }
 }
@@ -413,20 +374,23 @@
     // Test changing resources sizes (both increase & decrease).
     {
         context->setResourceCacheLimits(3, 30000);
+        GrResourceCache* cache = context->getResourceCache();
         GrResourceCache2* cache2 = context->getResourceCache2();
-        cache2->purgeAllUnlocked();
-        SkASSERT(0 == cache2->getResourceCount() && 0 == cache2->getResourceBytes());
+        cache->purgeAllUnlocked();
+        SkASSERT(0 == cache->getCachedResourceCount() && 0 == cache->getCachedResourceBytes());
 
         TestResource* a = new TestResource(context->getGpu());
-        a->cacheAccess().setContentKey(key1);
+        a->setSize(100); // Test didChangeGpuMemorySize() when not in the cache.
+        cache->addResource(key1, a);
         a->unref();
 
         TestResource* b = new TestResource(context->getGpu());
-        b->cacheAccess().setContentKey(key2);
+        b->setSize(100);
+        cache->addResource(key2, b);
         b->unref();
 
-        REPORTER_ASSERT(reporter, 200 == cache2->getResourceBytes());
-        REPORTER_ASSERT(reporter, 2 == cache2->getResourceCount());
+        REPORTER_ASSERT(reporter, 200 == cache->getCachedResourceBytes());
+        REPORTER_ASSERT(reporter, 2 == cache->getCachedResourceCount());
         {
             SkAutoTUnref<TestResource> find2(static_cast<TestResource*>(cache2->findAndRefContentResource(key2)));
             find2->setSize(200);
@@ -434,29 +398,30 @@
             find1->setSize(50);
         }
 
-        REPORTER_ASSERT(reporter, 250 == cache2->getResourceBytes());
-        REPORTER_ASSERT(reporter, 2 == cache2->getResourceCount());
+        REPORTER_ASSERT(reporter, 250 == cache->getCachedResourceBytes());
+        REPORTER_ASSERT(reporter, 2 == cache->getCachedResourceCount());
     }
 
     // Test increasing a resources size beyond the cache budget.
     {
         context->setResourceCacheLimits(2, 300);
+        GrResourceCache* cache = context->getResourceCache();
         GrResourceCache2* cache2 = context->getResourceCache2();
-        cache2->purgeAllUnlocked();
-        SkASSERT(0 == cache2->getResourceCount() && 0 == cache2->getResourceBytes());
+        cache->purgeAllUnlocked();
+        SkASSERT(0 == cache->getCachedResourceCount() && 0 == cache->getCachedResourceBytes());
 
         TestResource* a = new TestResource(context->getGpu());
         a->setSize(100);
-        a->cacheAccess().setContentKey(key1);
+        cache->addResource(key1, a);
         a->unref();
 
         TestResource* b = new TestResource(context->getGpu());
         b->setSize(100);
-        b->cacheAccess().setContentKey(key2);
+        cache->addResource(key2, b);
         b->unref();
 
-        REPORTER_ASSERT(reporter, 200 == cache2->getResourceBytes());
-        REPORTER_ASSERT(reporter, 2 == cache2->getResourceCount());
+        REPORTER_ASSERT(reporter, 200 == cache->getCachedResourceBytes());
+        REPORTER_ASSERT(reporter, 2 == cache->getCachedResourceCount());
 
         {
             SkAutoTUnref<TestResource> find2(static_cast<TestResource*>(cache2->findAndRefContentResource(key2)));
@@ -464,8 +429,8 @@
         }
         REPORTER_ASSERT(reporter, !cache2->hasContentKey(key1));
 
-        REPORTER_ASSERT(reporter, 201 == cache2->getResourceBytes());
-        REPORTER_ASSERT(reporter, 1 == cache2->getResourceCount());
+        REPORTER_ASSERT(reporter, 201 == cache->getCachedResourceBytes());
+        REPORTER_ASSERT(reporter, 1 == cache->getCachedResourceCount());
     }
 }
 
@@ -491,11 +456,10 @@
     }
 
     // The below tests create their own mock contexts.
-    test_no_key(reporter);
     test_duplicate_content_key(reporter);
     test_duplicate_scratch_key(reporter);
     test_purge_invalidated(reporter);
-    test_cache_chained_purge(reporter);
+    test_cache_delete_on_destruction(reporter);
     test_resource_size_changed(reporter);
 }
 
diff --git a/tests/SurfaceTest.cpp b/tests/SurfaceTest.cpp
index 1913fcb..69c8b84 100644
--- a/tests/SurfaceTest.cpp
+++ b/tests/SurfaceTest.cpp
@@ -321,6 +321,33 @@
 }
 
 #if SK_SUPPORT_GPU
+static void TestSurfaceInCache(skiatest::Reporter* reporter,
+                               SurfaceType surfaceType,
+                               GrContext* context) {
+    context->freeGpuResources();
+    int resourceCount;
+
+    context->getResourceCacheUsage(&resourceCount, NULL);
+    REPORTER_ASSERT(reporter, 0 == resourceCount);
+    SkAutoTUnref<SkSurface> surface(createSurface(surfaceType, context));
+    // Note: the stencil buffer is always cached, so kGpu_SurfaceType uses
+    // one cached resource, and kGpuScratch_SurfaceType uses two.
+    int expectedCachedResources = surfaceType == kGpuScratch_SurfaceType ? 2 : 1;
+    context->getResourceCacheUsage(&resourceCount, NULL);
+    REPORTER_ASSERT(reporter, expectedCachedResources == resourceCount);
+
+    // Verify that all the cached resources are locked in cache.
+    context->freeGpuResources();
+    context->getResourceCacheUsage(&resourceCount, NULL);
+    REPORTER_ASSERT(reporter, expectedCachedResources == resourceCount);
+
+    // Verify that all the cached resources are unlocked upon surface release
+    surface.reset(0);
+    context->freeGpuResources();
+    context->getResourceCacheUsage(&resourceCount, NULL);
+    REPORTER_ASSERT(reporter, 0 == resourceCount);
+}
+
 static void Test_crbug263329(skiatest::Reporter* reporter,
                              SurfaceType surfaceType,
                              GrContext* context) {
@@ -426,6 +453,8 @@
             }
             GrContext* context = factory->get(glCtxType);
             if (context) {
+                TestSurfaceInCache(reporter, kGpu_SurfaceType, context);
+                TestSurfaceInCache(reporter, kGpuScratch_SurfaceType, context);
                 Test_crbug263329(reporter, kGpu_SurfaceType, context);
                 Test_crbug263329(reporter, kGpuScratch_SurfaceType, context);
                 TestSurfaceCopyOnWrite(reporter, kGpu_SurfaceType, context);