Replace GrResourceCache with GrResourceCache2.

BUG=skia:2889

Review URL: https://codereview.chromium.org/716143004
diff --git a/src/gpu/GrAADistanceFieldPathRenderer.h b/src/gpu/GrAADistanceFieldPathRenderer.h
index 10f0eba..c337016 100755
--- a/src/gpu/GrAADistanceFieldPathRenderer.h
+++ b/src/gpu/GrAADistanceFieldPathRenderer.h
@@ -15,6 +15,7 @@
 #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 3a93404..9a180ee 100755
--- a/src/gpu/GrContext.cpp
+++ b/src/gpu/GrContext.cpp
@@ -25,7 +25,6 @@
 #include "GrOvalRenderer.h"
 #include "GrPathRenderer.h"
 #include "GrPathUtils.h"
-#include "GrResourceCache.h"
 #include "GrResourceCache2.h"
 #include "GrSoftwarePathRenderer.h"
 #include "GrStencilBuffer.h"
@@ -52,9 +51,6 @@
     #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;
 
@@ -66,20 +62,6 @@
 // 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;
@@ -103,13 +85,11 @@
     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();
@@ -130,11 +110,8 @@
 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));
 
@@ -160,9 +137,6 @@
     }
 
     SkDELETE(fResourceCache2);
-    fResourceCache2 = NULL;
-    SkDELETE(fResourceCache);
-    fResourceCache = NULL;
     SkDELETE(fFontCache);
     SkDELETE(fDrawBuffer);
     SkDELETE(fDrawBufferVBAllocPool);
@@ -201,8 +175,6 @@
     fAARectRenderer->reset();
     fOvalRenderer->reset();
 
-    fResourceCache->purgeAllUnlocked();
-
     fFontCache->freeAll();
     fLayerCache->freeAll();
 }
@@ -221,7 +193,6 @@
     fAARectRenderer->reset();
     fOvalRenderer->reset();
 
-    fResourceCache->purgeAllUnlocked();
     fFontCache->freeAll();
     fLayerCache->freeAll();
     // a path renderer may be holding onto resources
@@ -230,12 +201,12 @@
 }
 
 void GrContext::getResourceCacheUsage(int* resourceCount, size_t* resourceBytes) const {
-  if (resourceCount) {
-    *resourceCount = fResourceCache->getCachedResourceCount();
-  }
-  if (resourceBytes) {
-    *resourceBytes = fResourceCache->getCachedResourceBytes();
-  }
+    if (resourceCount) {
+        *resourceCount = fResourceCache2->getResourceCount();
+    }
+    if (resourceBytes) {
+        *resourceBytes = fResourceCache2->getResourceBytes();
+    }
 }
 
 GrTextContext* GrContext::createTextContext(GrRenderTarget* renderTarget,
@@ -273,12 +244,13 @@
 }
 
 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());
-    fResourceCache->addResource(resourceKey, sb);
+    SkAssertResult(sb->cacheAccess().setContentKey(resourceKey));
 }
 
 GrStencilBuffer* GrContext::findAndRefStencilBuffer(int width, int height, int sampleCnt) {
@@ -420,25 +392,19 @@
     }
 
     if (texture) {
-        fResourceCache->addResource(resourceKey, texture);
-
-        if (cacheKey) {
-            *cacheKey = resourceKey;
+        if (texture->cacheAccess().setContentKey(resourceKey)) {
+            if (cacheKey) {
+                *cacheKey = resourceKey;
+            }
+        } else {
+            texture->unref();
+            texture = NULL;
         }
     }
 
     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.
@@ -473,7 +439,6 @@
             }
             GrGpuResource* resource = fResourceCache2->findAndRefScratchResource(key, scratchFlags);
             if (resource) {
-                fResourceCache->makeResourceMRU(resource);
                 return static_cast<GrSurface*>(resource)->asTexture();
             }
 
@@ -496,21 +461,17 @@
         desc.writable()->fFlags = origFlags;
     }
 
-    GrTexture* texture = this->createNewScratchTexture(*desc);
+    GrTexture* texture = fGpu->createTexture(*desc, NULL, 0);
     SkASSERT(NULL == texture || 
              texture->cacheAccess().getScratchKey() == GrTexturePriv::ComputeScratchKey(*desc));
     return texture;
 }
 
-bool GrContext::OverbudgetCB(void* data) {
-    SkASSERT(data);
-
-    GrContext* context = reinterpret_cast<GrContext*>(data);
-
+void GrContext::OverBudgetCB(void* data) {
     // Flush the InOrderDrawBuffer to possibly free up some textures
-    context->fFlushToReduceCacheSize = true;
-
-    return true;
+    SkASSERT(data);
+    GrContext* context = reinterpret_cast<GrContext*>(data);
+    context->flush();
 }
 
 
@@ -522,11 +483,16 @@
 }
 
 void GrContext::getResourceCacheLimits(int* maxTextures, size_t* maxTextureBytes) const {
-    fResourceCache->getLimits(maxTextures, maxTextureBytes);
+    if (maxTextures) {
+        *maxTextures = fResourceCache2->getMaxResourceCount();
+    }
+    if (maxTextureBytes) {
+        *maxTextureBytes = fResourceCache2->getMaxResourceBytes();
+    }
 }
 
 void GrContext::setResourceCacheLimits(int maxTextures, size_t maxTextureBytes) {
-    fResourceCache->setLimits(maxTextures, maxTextureBytes);
+    fResourceCache2->setLimits(maxTextures, maxTextureBytes);
 }
 
 int GrContext::getMaxTextureSize() const {
@@ -582,9 +548,8 @@
     SkASSERT(renderTarget);
 
     AutoRestoreEffects are;
-    AutoCheckFlush acf(this);
     GR_CREATE_TRACE_MARKER_CONTEXT("GrContext::clear", this);
-    GrDrawTarget* target = this->prepareToDraw(NULL, &are, &acf);
+    GrDrawTarget* target = this->prepareToDraw(NULL, &are);
     if (NULL == target) {
         return;
     }
@@ -716,8 +681,7 @@
     }
 
     AutoRestoreEffects are;
-    AutoCheckFlush acf(this);
-    GrDrawTarget* target = this->prepareToDraw(&paint, &are, &acf);
+    GrDrawTarget* target = this->prepareToDraw(&paint, &are);
     if (NULL == target) {
         return;
     }
@@ -827,8 +791,7 @@
                                const SkRect& localRect,
                                const SkMatrix* localMatrix) {
     AutoRestoreEffects are;
-    AutoCheckFlush acf(this);
-    GrDrawTarget* target = this->prepareToDraw(&paint, &are, &acf);
+    GrDrawTarget* target = this->prepareToDraw(&paint, &are);
     if (NULL == target) {
         return;
     }
@@ -891,10 +854,9 @@
                              const uint16_t indices[],
                              int indexCount) {
     AutoRestoreEffects are;
-    AutoCheckFlush acf(this);
-    GrDrawTarget::AutoReleaseGeometry geo; // must be inside AutoCheckFlush scope
+    GrDrawTarget::AutoReleaseGeometry geo;
 
-    GrDrawTarget* target = this->prepareToDraw(&paint, &are, &acf);
+    GrDrawTarget* target = this->prepareToDraw(&paint, &are);
     if (NULL == target) {
         return;
     }
@@ -954,8 +916,7 @@
     }
 
     AutoRestoreEffects are;
-    AutoCheckFlush acf(this);
-    GrDrawTarget* target = this->prepareToDraw(&paint, &are, &acf);
+    GrDrawTarget* target = this->prepareToDraw(&paint, &are);
     if (NULL == target) {
         return;
     }
@@ -981,8 +942,7 @@
     }
 
     AutoRestoreEffects are;
-    AutoCheckFlush acf(this);
-    GrDrawTarget* target = this->prepareToDraw(&paint, &are, &acf);
+    GrDrawTarget* target = this->prepareToDraw(&paint, &are);
 
     GR_CREATE_TRACE_MARKER("GrContext::drawDRRect", target);
 
@@ -1014,8 +974,7 @@
     }
 
     AutoRestoreEffects are;
-    AutoCheckFlush acf(this);
-    GrDrawTarget* target = this->prepareToDraw(&paint, &are, &acf);
+    GrDrawTarget* target = this->prepareToDraw(&paint, &are);
     if (NULL == target) {
         return;
     }
@@ -1102,8 +1061,7 @@
         SkPoint pts[2];
         if (path.isLine(pts)) {
             AutoRestoreEffects are;
-            AutoCheckFlush acf(this);
-            GrDrawTarget* target = this->prepareToDraw(&paint, &are, &acf);
+            GrDrawTarget* target = this->prepareToDraw(&paint, &are);
             if (NULL == target) {
                 return;
             }
@@ -1139,8 +1097,7 @@
     // the writePixels that uploads to the scratch will perform a flush so we're
     // OK.
     AutoRestoreEffects are;
-    AutoCheckFlush acf(this);
-    GrDrawTarget* target = this->prepareToDraw(&paint, &are, &acf);
+    GrDrawTarget* target = this->prepareToDraw(&paint, &are);
     if (NULL == target) {
         return;
     }
@@ -1242,8 +1199,6 @@
     } else {
         fDrawBuffer->flush();
     }
-    fResourceCache->purgeAsNeeded();
-    fFlushToReduceCacheSize = false;
 }
 
 bool sw_convert_to_premul(GrPixelConfig srcConfig, int width, int height, size_t inRowBytes,
@@ -1363,7 +1318,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, NULL);
+        GrDrawTarget* drawTarget = this->prepareToDraw(NULL, NULL);
         GrDrawTarget::AutoGeometryAndStatePush agasp(drawTarget, GrDrawTarget::kReset_ASRInit,
                                                      &matrix);
         GrDrawState* drawState = drawTarget->drawState();
@@ -1543,8 +1498,7 @@
     SkASSERT(renderTarget);
     ASSERT_OWNED_RESOURCE(renderTarget);
     AutoRestoreEffects are;
-    AutoCheckFlush acf(this);
-    GrDrawTarget* target = this->prepareToDraw(NULL, &are, &acf);
+    GrDrawTarget* target = this->prepareToDraw(NULL, &are);
     if (NULL == target) {
         return;
     }
@@ -1562,7 +1516,7 @@
     // Since we're going to the draw target and not GPU, no need to check kNoFlush
     // here.
 
-    GrDrawTarget* target = this->prepareToDraw(NULL, NULL, NULL);
+    GrDrawTarget* target = this->prepareToDraw(NULL, NULL);
     if (NULL == target) {
         return;
     }
@@ -1581,9 +1535,7 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
-GrDrawTarget* GrContext::prepareToDraw(const GrPaint* paint,
-                                       AutoRestoreEffects* are,
-                                       AutoCheckFlush* acf) {
+GrDrawTarget* GrContext::prepareToDraw(const GrPaint* paint, AutoRestoreEffects* are) {
     // 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() &&
@@ -1596,7 +1548,6 @@
     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
@@ -1696,7 +1647,7 @@
 }
 
 GrDrawTarget* GrContext::getTextTarget() {
-    return this->prepareToDraw(NULL, NULL, NULL);
+    return this->prepareToDraw(NULL, NULL);
 }
 
 const GrIndexBuffer* GrContext::getQuadIndexBuffer() const {
@@ -1746,15 +1697,11 @@
 }
 
 void GrContext::addResourceToCache(const GrResourceKey& resourceKey, GrGpuResource* resource) {
-    fResourceCache->addResource(resourceKey, resource);
+    resource->cacheAccess().setContentKey(resourceKey);
 }
 
 GrGpuResource* GrContext::findAndRefCachedResource(const GrResourceKey& resourceKey) {
-    GrGpuResource* resource = fResourceCache2->findAndRefContentResource(resourceKey);
-    if (resource) {
-        fResourceCache->makeResourceMRU(resource);
-    }
-    return resource;
+    return fResourceCache2->findAndRefContentResource(resourceKey);
 }
 
 void GrContext::addGpuTraceMarker(const GrGpuTraceMarker* marker) {
@@ -1774,7 +1721,7 @@
 ///////////////////////////////////////////////////////////////////////////////
 #if GR_CACHE_STATS
 void GrContext::printCacheStats() const {
-    fResourceCache->printStats();
+    fResourceCache2->printStats();
 }
 #endif
 
diff --git a/src/gpu/GrGpuResource.cpp b/src/gpu/GrGpuResource.cpp
index f6f7282..be8ea0d 100644
--- a/src/gpu/GrGpuResource.cpp
+++ b/src/gpu/GrGpuResource.cpp
@@ -18,16 +18,8 @@
     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())
@@ -40,7 +32,7 @@
 }
 
 void GrGpuResource::registerWithCache() {
-    get_resource_cache2(fGpu)->insertResource(this);
+    get_resource_cache2(fGpu)->resourceAccess().insertResource(this);
 }
 
 GrGpuResource::~GrGpuResource() {
@@ -51,16 +43,18 @@
 void GrGpuResource::release() { 
     if (fGpu) {
         this->onRelease();
-        get_resource_cache2(fGpu)->removeResource(this);
+        get_resource_cache2(fGpu)->resourceAccess().removeResource(this);
         fGpu = NULL;
+        fGpuMemorySize = 0;
     }
 }
 
 void GrGpuResource::abandon() {
     if (fGpu) {
         this->onAbandon();
-        get_resource_cache2(fGpu)->removeResource(this);
+        get_resource_cache2(fGpu)->resourceAccess().removeResource(this);
         fGpu = NULL;
+        fGpuMemorySize = 0;
     }
 }
 
@@ -80,6 +74,17 @@
     }
 }
 
+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());
@@ -92,7 +97,7 @@
     fContentKey = contentKey;
     fContentKeySet = true;
 
-    if (!get_resource_cache2(fGpu)->didSetContentKey(this)) {
+    if (!get_resource_cache2(fGpu)->resourceAccess().didSetContentKey(this)) {
         fContentKeySet = false;
         return false;
     }
@@ -100,8 +105,8 @@
 }
 
 void GrGpuResource::notifyIsPurgable() const {
-    if (fCacheEntry && !this->wasDestroyed()) {
-        get_resource_cache(fGpu)->notifyPurgable(this);
+    if (!this->wasDestroyed()) {
+        get_resource_cache2(fGpu)->resourceAccess().notifyPurgable(this);
     }
 }
 
diff --git a/src/gpu/GrGpuResourceCacheAccess.h b/src/gpu/GrGpuResourceCacheAccess.h
index af5c054..7417a55 100644
--- a/src/gpu/GrGpuResourceCacheAccess.h
+++ b/src/gpu/GrGpuResourceCacheAccess.h
@@ -29,25 +29,6 @@
     }
 
     /**
-     * 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 a571935..394db6f 100644
--- a/src/gpu/GrPath.h
+++ b/src/gpu/GrPath.h
@@ -9,7 +9,6 @@
 #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 dc8ce1d..5bfecb0 100644
--- a/src/gpu/GrPathRange.h
+++ b/src/gpu/GrPathRange.h
@@ -9,7 +9,6 @@
 #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
deleted file mode 100644
index a73d117..0000000
--- a/src/gpu/GrResourceCache.cpp
+++ /dev/null
@@ -1,393 +0,0 @@
-
-/*
- * 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
deleted file mode 100644
index 80e4b3f..0000000
--- a/src/gpu/GrResourceCache.h
+++ /dev/null
@@ -1,251 +0,0 @@
-
-/*
- * 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 83143d7..109e815 100644
--- a/src/gpu/GrResourceCache2.cpp
+++ b/src/gpu/GrResourceCache2.cpp
@@ -10,6 +10,13 @@
 #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);
@@ -27,26 +34,85 @@
     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()) {
@@ -55,13 +121,16 @@
     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());
     }
@@ -71,9 +140,13 @@
 }
 
 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());
     }
@@ -99,11 +172,16 @@
 
 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)) {
-        GrGpuResource* resource = fScratchMap.find(scratchKey, AvailableForScratchUse(true));
+        resource = fScratchMap.find(scratchKey, AvailableForScratchUse(true));
         if (resource) {
+            this->makeResourceMRU(resource);
             return SkRef(resource);
         } else if (flags & kRequireNoPendingIO_ScratchFlag) {
             return NULL;
@@ -111,11 +189,18 @@
         // 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.
     }
-    return SkSafeRef(fScratchMap.find(scratchKey, AvailableForScratchUse(false)));
+    resource = fScratchMap.find(scratchKey, AvailableForScratchUse(false));
+    if (resource) {
+        resource->ref();
+        this->makeResourceMRU(resource);
+    }
+    return resource;
 }
 
 bool GrResourceCache2::didSetContentKey(GrGpuResource* resource) {
+    SkASSERT(!fPurging);
     SkASSERT(resource);
+    SkASSERT(this->isInCache(resource));
     SkASSERT(resource->cacheAccess().getContentKey());
     SkASSERT(!resource->cacheAccess().getContentKey()->isScratch());
 
@@ -125,5 +210,214 @@
     }
 
     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 1cc9587..8b4d1d0 100644
--- a/src/gpu/GrResourceCache2.h
+++ b/src/gpu/GrResourceCache2.h
@@ -17,27 +17,65 @@
 #include "SkTMultiMap.h"
 
 /**
- *  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.
+ * 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.
  */
 class GrResourceCache2 {
 public:
-    GrResourceCache2() : fCount(0) {};
+    GrResourceCache2();
     ~GrResourceCache2();
 
-    void insertResource(GrGpuResource*);
+    /** Used to access functionality needed by GrGpuResource for lifetime management. */
+    class ResourceAccess;
+    ResourceAccess resourceAccess();
 
-    void removeResource(GrGpuResource*);
+    /**
+     * Sets the cache limits in terms of number of resources and max gpu memory byte size.
+     */
+    void setLimits(int count, size_t bytes);
 
-    // 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 cached resources.
+     */
+    int getResourceCount() const { return fCount; }
 
+    /**
+     * 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 {
@@ -46,6 +84,10 @@
         /** 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
@@ -56,21 +98,80 @@
     }
 #endif
 
+    /**
+     * Find a resource that matches a content key.
+     */
     GrGpuResource* findAndRefContentResource(const GrResourceKey& contentKey) {
         SkASSERT(!contentKey.isScratch());
-        return SkSafeRef(fContentHash.find(contentKey));
+        GrGpuResource* resource = fContentHash.find(contentKey);
+        if (resource) {
+            resource->ref();
+            this->makeResourceMRU(resource);
+        }
+        return resource;
     }
 
+    /**
+     * 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 {
@@ -91,12 +192,86 @@
     };
     typedef SkTDynamicHash<GrGpuResource, GrResourceKey, ContentHashTraits> ContentHash;
 
-    int                                 fCount;
-    SkTInternalLList<GrGpuResource>     fResources;
+    typedef SkTInternalLList<GrGpuResource> ResourceList;
+
+    ResourceList                        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 b288415..16b0150 100644
--- a/src/gpu/GrStencilBuffer.cpp
+++ b/src/gpu/GrStencilBuffer.cpp
@@ -13,8 +13,6 @@
 #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 611059a..5286984 100644
--- a/src/gpu/GrTest.cpp
+++ b/src/gpu/GrTest.cpp
@@ -9,7 +9,7 @@
 #include "GrTest.h"
 
 #include "GrInOrderDrawBuffer.h"
-#include "GrResourceCache.h"
+#include "GrResourceCache2.h"
 
 void GrTestTarget::init(GrContext* ctx, GrDrawTarget* target) {
     SkASSERT(!fContext);
@@ -38,7 +38,7 @@
 }
 
 void GrContext::purgeAllUnlockedResources() {
-    fResourceCache->purgeAllUnlocked();
+    fResourceCache2->purgeAllUnlocked();
 }
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/src/gpu/GrTexture.cpp b/src/gpu/GrTexture.cpp
index 58bbeaa..9f700c7 100644
--- a/src/gpu/GrTexture.cpp
+++ b/src/gpu/GrTexture.cpp
@@ -9,7 +9,6 @@
 #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 affbd95..a86ee60 100644
--- a/src/gpu/SkGr.cpp
+++ b/src/gpu/SkGr.cpp
@@ -12,7 +12,6 @@
 #include "SkMessageBus.h"
 #include "SkPixelRef.h"
 #include "SkTextureCompressor.h"
-#include "GrResourceCache.h"
 #include "GrGpu.h"
 #include "effects/GrDitherEffect.h"
 #include "GrDrawTargetCaps.h"