Remove separate cache for clip mask textures

Review URL: https://codereview.chromium.org/1377943003
diff --git a/gyp/gpu.gypi b/gyp/gpu.gypi
index 4670057..1025a1e 100644
--- a/gyp/gpu.gypi
+++ b/gyp/gpu.gypi
@@ -78,8 +78,6 @@
       '<(skia_src_path)/gpu/GrBufferAllocPool.h',
       '<(skia_src_path)/gpu/GrCaps.cpp',
       '<(skia_src_path)/gpu/GrClip.cpp',
-      '<(skia_src_path)/gpu/GrClipMaskCache.h',
-      '<(skia_src_path)/gpu/GrClipMaskCache.cpp',
       '<(skia_src_path)/gpu/GrClipMaskManager.h',
       '<(skia_src_path)/gpu/GrClipMaskManager.cpp',
       '<(skia_src_path)/gpu/GrContext.cpp',
diff --git a/include/gpu/GrContext.h b/include/gpu/GrContext.h
index 5c02d37..59ce867 100644
--- a/include/gpu/GrContext.h
+++ b/include/gpu/GrContext.h
@@ -430,7 +430,6 @@
         void abandon();
         bool abandoned() const { return NULL == fDrawTarget; }
 
-        void purgeResources();
         void reset();
         void flush();
 
diff --git a/src/gpu/GrClipMaskCache.cpp b/src/gpu/GrClipMaskCache.cpp
deleted file mode 100644
index 42a5d46..0000000
--- a/src/gpu/GrClipMaskCache.cpp
+++ /dev/null
@@ -1,19 +0,0 @@
-
-/*
- * Copyright 2012 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "GrClipMaskCache.h"
-
-GrClipMaskCache::GrClipMaskCache(GrResourceProvider* resourceProvider)
-    : fStack(sizeof(GrClipStackFrame))
-    , fResourceProvider(resourceProvider) {
-    // We need an initial frame to capture the clip state prior to
-    // any pushes
-    new (fStack.push_back()) GrClipStackFrame;
-}
-
-void GrClipMaskCache::push() { new (fStack.push_back()) GrClipStackFrame; }
diff --git a/src/gpu/GrClipMaskCache.h b/src/gpu/GrClipMaskCache.h
deleted file mode 100644
index 56b0e97..0000000
--- a/src/gpu/GrClipMaskCache.h
+++ /dev/null
@@ -1,231 +0,0 @@
-/*
- * Copyright 2012 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef GrClipMaskCache_DEFINED
-#define GrClipMaskCache_DEFINED
-
-#include "GrResourceProvider.h"
-#include "SkClipStack.h"
-#include "SkTypes.h"
-
-class GrTexture;
-
-/**
- * The stencil buffer stores the last clip path - providing a single entry
- * "cache". This class provides similar functionality for AA clip paths
- */
-class GrClipMaskCache : SkNoncopyable {
-public:
-    GrClipMaskCache(GrResourceProvider*);
-
-    ~GrClipMaskCache() {
-        while (!fStack.empty()) {
-            GrClipStackFrame* temp = (GrClipStackFrame*) fStack.back();
-            temp->~GrClipStackFrame();
-            fStack.pop_back();
-        }
-    }
-
-    bool canReuse(int32_t clipGenID, const SkIRect& bounds) {
-
-        SkASSERT(clipGenID != SkClipStack::kWideOpenGenID);
-        SkASSERT(clipGenID != SkClipStack::kEmptyGenID);
-
-        GrClipStackFrame* back = (GrClipStackFrame*) fStack.back();
-
-        // We could reuse the mask if bounds is a subset of last bounds. We'd have to communicate
-        // an offset to the caller.
-        if (back->fLastMask &&
-            !back->fLastMask->wasDestroyed() &&
-            back->fLastBound == bounds &&
-            back->fLastClipGenID == clipGenID) {
-            return true;
-        }
-
-        return false;
-    }
-
-    void reset() {
-        if (fStack.empty()) {
-//            SkASSERT(false);
-            return;
-        }
-
-        GrClipStackFrame* back = (GrClipStackFrame*) fStack.back();
-
-        back->reset();
-    }
-
-    /**
-     * After a "push" the clip state is entirely open. Currently, the
-     * entire clip stack will be re-rendered into a new clip mask.
-     * TODO: can we take advantage of the nested nature of the clips to
-     * reduce the mask creation cost?
-     */
-    void push();
-
-    void pop() {
-        //SkASSERT(!fStack.empty());
-
-        if (!fStack.empty()) {
-            GrClipStackFrame* back = (GrClipStackFrame*) fStack.back();
-
-            back->~GrClipStackFrame();
-            fStack.pop_back();
-        }
-    }
-
-    int32_t getLastClipGenID() const {
-
-        if (fStack.empty()) {
-            return SkClipStack::kInvalidGenID;
-        }
-
-        return ((GrClipStackFrame*) fStack.back())->fLastClipGenID;
-    }
-
-    GrTexture* getLastMask() {
-
-        if (fStack.empty()) {
-            SkASSERT(false);
-            return nullptr;
-        }
-
-        GrClipStackFrame* back = (GrClipStackFrame*) fStack.back();
-
-        return back->fLastMask;
-    }
-
-    const GrTexture* getLastMask() const {
-
-        if (fStack.empty()) {
-            SkASSERT(false);
-            return nullptr;
-        }
-
-        GrClipStackFrame* back = (GrClipStackFrame*) fStack.back();
-
-        return back->fLastMask;
-    }
-
-    void acquireMask(int32_t clipGenID,
-                     const GrSurfaceDesc& desc,
-                     const SkIRect& bound) {
-
-        if (fStack.empty()) {
-            SkASSERT(false);
-            return;
-        }
-
-        GrClipStackFrame* back = (GrClipStackFrame*) fStack.back();
-
-        back->acquireMask(fResourceProvider, clipGenID, desc, bound);
-    }
-
-    int getLastMaskWidth() const {
-
-        if (fStack.empty()) {
-            SkASSERT(false);
-            return -1;
-        }
-
-        GrClipStackFrame* back = (GrClipStackFrame*) fStack.back();
-
-        if (nullptr == back->fLastMask) {
-            return -1;
-        }
-
-        return back->fLastMask->width();
-    }
-
-    int getLastMaskHeight() const {
-
-        if (fStack.empty()) {
-            SkASSERT(false);
-            return -1;
-        }
-
-        GrClipStackFrame* back = (GrClipStackFrame*) fStack.back();
-
-        if (nullptr == back->fLastMask) {
-            return -1;
-        }
-
-        return back->fLastMask->height();
-    }
-
-    void getLastBound(SkIRect* bound) const {
-
-        if (fStack.empty()) {
-            SkASSERT(false);
-            bound->setEmpty();
-            return;
-        }
-
-        GrClipStackFrame* back = (GrClipStackFrame*) fStack.back();
-
-        *bound = back->fLastBound;
-    }
-
-    //  TODO: Remove this when we hold cache keys instead of refs to textures.
-    void purgeResources() {
-        SkDeque::F2BIter iter(fStack);
-        for (GrClipStackFrame* frame = (GrClipStackFrame*) iter.next();
-                frame != nullptr;
-                frame = (GrClipStackFrame*) iter.next()) {
-            frame->reset();
-        }
-    }
-
-private:
-    struct GrClipStackFrame {
-
-        GrClipStackFrame() {
-            this->reset();
-        }
-
-        void acquireMask(GrResourceProvider* resourceProvider,
-                         int32_t clipGenID,
-                         const GrSurfaceDesc& desc,
-                         const SkIRect& bound) {
-
-            fLastClipGenID = clipGenID;
-
-            // TODO: Determine if we really need the NoPendingIO flag anymore.
-            // (http://skbug.com/4156)
-            static const uint32_t kFlags = GrResourceProvider::kNoPendingIO_Flag;
-            fLastMask.reset(resourceProvider->createApproxTexture(desc, kFlags));
-
-            fLastBound = bound;
-        }
-
-        void reset () {
-            fLastClipGenID = SkClipStack::kInvalidGenID;
-
-            GrSurfaceDesc desc;
-
-            fLastMask.reset(nullptr);
-            fLastBound.setEmpty();
-        }
-
-        int32_t                 fLastClipGenID;
-        // The mask's width & height values are used by GrClipMaskManager to correctly scale the
-        // texture coords for the geometry drawn with this mask. TODO: This should be a cache key
-        // and not a hard ref to a texture.
-        SkAutoTUnref<GrTexture> fLastMask;
-        // fLastBound stores the bounding box of the clip mask in clip-stack space. This rect is
-        // used by GrClipMaskManager to position a rect and compute texture coords for the mask.
-        SkIRect                 fLastBound;
-    };
-
-    SkDeque             fStack;
-    GrResourceProvider* fResourceProvider;
-
-    typedef SkNoncopyable INHERITED;
-};
-
-#endif // GrClipMaskCache_DEFINED
diff --git a/src/gpu/GrClipMaskManager.cpp b/src/gpu/GrClipMaskManager.cpp
index 0df74a6..1ddcef1 100644
--- a/src/gpu/GrClipMaskManager.cpp
+++ b/src/gpu/GrClipMaskManager.cpp
@@ -9,10 +9,12 @@
 #include "GrCaps.h"
 #include "GrDrawContext.h"
 #include "GrDrawTarget.h"
+#include "GrGpuResourcePriv.h"
 #include "GrPaint.h"
 #include "GrPathRenderer.h"
 #include "GrRenderTarget.h"
 #include "GrRenderTargetPriv.h"
+#include "GrResourceProvider.h"
 #include "GrStencilAttachment.h"
 #include "GrSWMaskHelper.h"
 #include "SkRasterClip.h"
@@ -78,7 +80,6 @@
 
 GrClipMaskManager::GrClipMaskManager(GrDrawTarget* drawTarget)
     : fCurrClipMaskType(kNone_ClipMaskType)
-    , fAACache(drawTarget->cmmAccess().resourceProvider())
     , fDrawTarget(drawTarget)
     , fClipMode(kIgnoreClip_StencilClipMode) {
 }
@@ -344,13 +345,6 @@
         // if alpha clip mask creation fails fall through to the non-AA code paths
     }
 
-    // Either a hard (stencil buffer) clip was explicitly requested or an anti-aliased clip couldn't
-    // be created. In either case, free up the texture in the anti-aliased mask cache.
-    // TODO: this may require more investigation. Ganesh performs a lot of utility draws (e.g.,
-    // clears, GrBufferedDrawTarget playbacks) that hit the stencil buffer path. These may be
-    // "incorrectly" clearing the AA cache.
-    fAACache.reset();
-
     // use the stencil clip if we can't represent the clip as a rectangle.
     SkIPoint clipSpaceToStencilSpaceOffset = -clip.origin();
     this->createStencilClipMask(rt,
@@ -516,62 +510,55 @@
 }
 
 ////////////////////////////////////////////////////////////////////////////////
-// Return the texture currently in the cache if it exists. Otherwise, return nullptr
-GrTexture* GrClipMaskManager::getCachedMaskTexture(int32_t elementsGenID,
-                                                   const SkIRect& clipSpaceIBounds) {
-    bool cached = fAACache.canReuse(elementsGenID, clipSpaceIBounds);
-    if (!cached) {
+// Create a 8-bit clip mask in alpha
+
+static void GetClipMaskKey(int32_t clipGenID, const SkIRect& bounds, GrUniqueKey* key) {
+    static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
+    GrUniqueKey::Builder builder(key, kDomain, 3);
+    builder[0] = clipGenID;
+    builder[1] = SkToU16(bounds.fLeft) | (SkToU16(bounds.fRight) << 16);
+    builder[2] = SkToU16(bounds.fTop) | (SkToU16(bounds.fBottom) << 16);
+}
+
+GrTexture* GrClipMaskManager::createCachedMask(int width, int height, const GrUniqueKey& key,
+                                               bool renderTarget) {
+    GrSurfaceDesc desc;
+    desc.fWidth = width;
+    desc.fHeight = height;
+    desc.fFlags = renderTarget ? kRenderTarget_GrSurfaceFlag : kNone_GrSurfaceFlags;
+    if (!renderTarget || fDrawTarget->caps()->isConfigRenderable(kAlpha_8_GrPixelConfig, false)) {
+        desc.fConfig = kAlpha_8_GrPixelConfig;
+    } else {
+        desc.fConfig = kRGBA_8888_GrPixelConfig;
+    }
+
+    GrTexture* texture = fDrawTarget->cmmAccess().resourceProvider()->createApproxTexture(desc, 0);
+    if (!texture) {
         return nullptr;
     }
-
-    return fAACache.getLastMask();
+    texture->resourcePriv().setUniqueKey(key);
+    return texture;
 }
 
-////////////////////////////////////////////////////////////////////////////////
-// Allocate a texture in the texture cache. This function returns the texture
-// allocated (or nullptr on error).
-GrTexture* GrClipMaskManager::allocMaskTexture(int32_t elementsGenID,
-                                               const SkIRect& clipSpaceIBounds,
-                                               bool willUpload) {
-    // Since we are setting up the cache we should free up the
-    // currently cached mask so it can be reused.
-    fAACache.reset();
-
-    GrSurfaceDesc desc;
-    desc.fFlags = willUpload ? kNone_GrSurfaceFlags : kRenderTarget_GrSurfaceFlag;
-    desc.fWidth = clipSpaceIBounds.width();
-    desc.fHeight = clipSpaceIBounds.height();
-    desc.fConfig = kRGBA_8888_GrPixelConfig;
-    if (willUpload ||
-        this->getContext()->caps()->isConfigRenderable(kAlpha_8_GrPixelConfig, false)) {
-        // We would always like A8 but it isn't supported on all platforms
-        desc.fConfig = kAlpha_8_GrPixelConfig;
-    }
-
-    fAACache.acquireMask(elementsGenID, desc, clipSpaceIBounds);
-    return fAACache.getLastMask();
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Create a 8-bit clip mask in alpha
 GrTexture* GrClipMaskManager::createAlphaClipMask(int32_t elementsGenID,
                                                   GrReducedClip::InitialState initialState,
                                                   const GrReducedClip::ElementList& elements,
                                                   const SkVector& clipToMaskOffset,
                                                   const SkIRect& clipSpaceIBounds) {
     SkASSERT(kNone_ClipMaskType == fCurrClipMaskType);
-
-    // First, check for cached texture
-    GrTexture* result = this->getCachedMaskTexture(elementsGenID, clipSpaceIBounds);
-    if (result) {
+    GrResourceProvider* resourceProvider = fDrawTarget->cmmAccess().resourceProvider();
+    GrUniqueKey key;
+    GetClipMaskKey(elementsGenID, clipSpaceIBounds, &key);
+    if (GrTexture* texture = resourceProvider->findAndRefTextureByUniqueKey(key)) {
         fCurrClipMaskType = kAlpha_ClipMaskType;
-        return result;
+        return texture;
     }
 
+    SkAutoTUnref<GrTexture> texture(this->createCachedMask(
+        clipSpaceIBounds.width(), clipSpaceIBounds.height(), key, true));
+
     // There's no texture in the cache. Let's try to allocate it then.
-    result = this->allocMaskTexture(elementsGenID, clipSpaceIBounds, false);
-    if (nullptr == result) {
-        fAACache.reset();
+    if (!texture) {
         return nullptr;
     }
 
@@ -589,7 +576,7 @@
     fDrawTarget->clear(&maskSpaceIBounds,
                        GrReducedClip::kAllIn_InitialState == initialState ? 0xffffffff : 0x00000000,
                        true,
-                       result->asRenderTarget());
+                       texture->asRenderTarget());
 
     // When we use the stencil in the below loop it is important to have this clip installed.
     // The second pass that zeros the stencil buffer renders the rect maskSpaceIBounds so the first
@@ -608,7 +595,7 @@
 
             pipelineBuilder.setClip(clip);
             GrPathRenderer* pr = nullptr;
-            bool useTemp = !this->canStencilAndDrawElement(&pipelineBuilder, result, &pr, element);
+            bool useTemp = !this->canStencilAndDrawElement(&pipelineBuilder, texture, &pr, element);
             GrTexture* dst;
             // This is the bounds of the clip element in the space of the alpha-mask. The temporary
             // mask buffer can be substantially larger than the actually clip stack element. We
@@ -629,7 +616,7 @@
                     temp.reset(this->createTempMask(maskSpaceIBounds.fRight,
                                                     maskSpaceIBounds.fBottom));
                     if (!temp) {
-                        fAACache.reset();
+                        texture->resourcePriv().removeUniqueKey();
                         return nullptr;
                     }
                 }
@@ -643,7 +630,7 @@
             } else {
                 // draw directly into the result with the stencil set to make the pixels affected
                 // by the clip shape be non-zero.
-                dst = result;
+                dst = texture;
                 GR_STATIC_CONST_SAME_STENCIL(kStencilInElement,
                                              kReplace_StencilOp,
                                              kReplace_StencilOp,
@@ -656,25 +643,25 @@
             }
 
             if (!this->drawElement(&pipelineBuilder, translate, dst, element, pr)) {
-                fAACache.reset();
+                texture->resourcePriv().removeUniqueKey();
                 return nullptr;
             }
 
             if (useTemp) {
                 GrPipelineBuilder backgroundPipelineBuilder;
-                backgroundPipelineBuilder.setRenderTarget(result->asRenderTarget());
+                backgroundPipelineBuilder.setRenderTarget(texture->asRenderTarget());
 
                 // Now draw into the accumulator using the real operation and the temp buffer as a
                 // texture
                 this->mergeMask(&backgroundPipelineBuilder,
-                                result,
+                                texture,
                                 temp,
                                 op,
                                 maskSpaceIBounds,
                                 maskSpaceElementIBounds);
             } else {
                 GrPipelineBuilder backgroundPipelineBuilder;
-                backgroundPipelineBuilder.setRenderTarget(result->asRenderTarget());
+                backgroundPipelineBuilder.setRenderTarget(texture->asRenderTarget());
 
                 set_coverage_drawing_xpf(op, !invert, &backgroundPipelineBuilder);
                 // Draw to the exterior pixels (those with a zero stencil value).
@@ -697,12 +684,12 @@
             // all the remaining ops can just be directly draw into the accumulation buffer
             set_coverage_drawing_xpf(op, false, &pipelineBuilder);
             // The color passed in here does not matter since the coverageSetOpXP won't read it.
-            this->drawElement(&pipelineBuilder, translate, result, element);
+            this->drawElement(&pipelineBuilder, translate, texture, element);
         }
     }
 
     fCurrClipMaskType = kAlpha_ClipMaskType;
-    return result;
+    return texture.detach();
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -1079,10 +1066,11 @@
                                                      const SkVector& clipToMaskOffset,
                                                      const SkIRect& clipSpaceIBounds) {
     SkASSERT(kNone_ClipMaskType == fCurrClipMaskType);
-
-    GrTexture* result = this->getCachedMaskTexture(elementsGenID, clipSpaceIBounds);
-    if (result) {
-        return result;
+    GrUniqueKey key;
+    GetClipMaskKey(elementsGenID, clipSpaceIBounds, &key);
+    GrResourceProvider* resourceProvider = fDrawTarget->cmmAccess().resourceProvider();
+    if (GrTexture* texture = resourceProvider->findAndRefTextureByUniqueKey(key)) {
+        return texture;
     }
 
     // The mask texture may be larger than necessary. We round out the clip space bounds and pin
@@ -1133,9 +1121,9 @@
     }
 
     // Allocate clip mask texture
-    result = this->allocMaskTexture(elementsGenID, clipSpaceIBounds, true);
+    GrTexture* result = this->createCachedMask(clipSpaceIBounds.width(), clipSpaceIBounds.height(),
+                                               key, false);
     if (nullptr == result) {
-        fAACache.reset();
         return nullptr;
     }
     helper.toTexture(result);
@@ -1145,9 +1133,6 @@
 }
 
 ////////////////////////////////////////////////////////////////////////////////
-void GrClipMaskManager::purgeResources() {
-    fAACache.purgeResources();
-}
 
 void GrClipMaskManager::adjustPathStencilParams(const GrStencilAttachment* stencilAttachment,
                                                 GrStencilSettings* settings) {
diff --git a/src/gpu/GrClipMaskManager.h b/src/gpu/GrClipMaskManager.h
index 26afd2c..0c0c10c 100644
--- a/src/gpu/GrClipMaskManager.h
+++ b/src/gpu/GrClipMaskManager.h
@@ -7,7 +7,6 @@
 #ifndef GrClipMaskManager_DEFINED
 #define GrClipMaskManager_DEFINED
 
-#include "GrClipMaskCache.h"
 #include "GrPipelineBuilder.h"
 #include "GrReducedClip.h"
 #include "GrStencil.h"
@@ -49,12 +48,6 @@
                        GrScissorState*,
                        const SkRect* devBounds);
 
-    /**
-     * Purge resources to free up memory. TODO: This class shouldn't hold any long lived refs
-     * which will allow GrResourceCache to automatically purge anything this class has created.
-     */
-    void purgeResources();
-
     bool isClipInStencil() const {
         return kStencil_ClipMaskType == fCurrClipMaskType;
     }
@@ -113,17 +106,7 @@
                                       const SkVector& clipToMaskOffset,
                                       const SkIRect& clipSpaceIBounds);
 
-    // Returns the cached mask texture if it matches the elementsGenID and the clipSpaceIBounds.
-    // Returns nullptr if not found.
-    GrTexture* getCachedMaskTexture(int32_t elementsGenID, const SkIRect& clipSpaceIBounds);
-
-    // Handles allocation (if needed) of a clip alpha-mask texture for both the sw-upload
-    // or gpu-rendered cases.
-    GrTexture* allocMaskTexture(int32_t elementsGenID,
-                                const SkIRect& clipSpaceIBounds,
-                                bool willUpload);
-
-    bool useSWOnlyPath(const GrPipelineBuilder&,
+   bool useSWOnlyPath(const GrPipelineBuilder&,
                        const SkVector& clipToMaskOffset,
                        const GrReducedClip::ElementList& elements);
 
@@ -153,8 +136,6 @@
 
     GrTexture* createTempMask(int width, int height);
 
-    void setupCache(const SkClipStack& clip,
-                    const SkIRect& bounds);
     /**
      * Called prior to return control back the GrGpu in setupClipping. It updates the
      * GrPipelineBuilder with stencil settings that account for stencil-based clipping.
@@ -170,6 +151,8 @@
                              StencilClipMode mode,
                              int stencilBitCnt);
 
+    GrTexture* createCachedMask(int width, int height, const GrUniqueKey& key, bool renderTarget);
+
     /**
      * We may represent the clip as a mask in the stencil buffer or as an alpha
      * texture. It may be neither because the scissor rect suffices or we
@@ -181,7 +164,6 @@
         kAlpha_ClipMaskType,
     } fCurrClipMaskType;
 
-    GrClipMaskCache fAACache;       // cache for the AA path
     GrDrawTarget*   fDrawTarget;    // This is our owning draw target.
     StencilClipMode fClipMode;
 
diff --git a/src/gpu/GrContext.cpp b/src/gpu/GrContext.cpp
index 4d6b6b8..065d61b 100644
--- a/src/gpu/GrContext.cpp
+++ b/src/gpu/GrContext.cpp
@@ -87,12 +87,6 @@
     }
 }
 
-void GrContext::DrawingMgr::purgeResources() {
-    if (fDrawTarget) {
-        fDrawTarget->purgeResources();
-    }
-}
-
 void GrContext::DrawingMgr::reset() {
     if (fDrawTarget) {
         fDrawTarget->reset();
@@ -243,8 +237,6 @@
 void GrContext::freeGpuResources() {
     this->flush();
 
-    fDrawingMgr.purgeResources();
-
     fBatchFontCache->freeAll();
     fLayerCache->freeAll();
     // a path renderer may be holding onto resources
diff --git a/src/gpu/GrDrawContext.cpp b/src/gpu/GrDrawContext.cpp
index 1969bd0..0a63eb8 100644
--- a/src/gpu/GrDrawContext.cpp
+++ b/src/gpu/GrDrawContext.cpp
@@ -14,6 +14,7 @@
 #include "GrPathRenderer.h"
 #include "GrRenderTarget.h"
 #include "GrRenderTargetPriv.h"
+#include "GrResourceProvider.h"
 #include "GrStencilAndCoverTextContext.h"
 
 #include "batches/GrBatch.h"
diff --git a/src/gpu/GrDrawTarget.h b/src/gpu/GrDrawTarget.h
index b12ac99..3c28b1e 100644
--- a/src/gpu/GrDrawTarget.h
+++ b/src/gpu/GrDrawTarget.h
@@ -20,6 +20,8 @@
 #include "GrVertexBuffer.h"
 #include "GrXferProcessor.h"
 
+#include "batches/GrDrawBatch.h"
+
 #include "SkClipStack.h"
 #include "SkMatrix.h"
 #include "SkPath.h"
@@ -34,7 +36,6 @@
 class GrClip;
 class GrCaps;
 class GrPath;
-class GrDrawBatch;
 class GrDrawPathBatchBase;
 class GrPathRangeDraw;
 
@@ -170,15 +171,6 @@
                      GrSurface* src,
                      const SkIRect& srcRect,
                      const SkIPoint& dstPoint);
-    /**
-     * Release any resources that are cached but not currently in use. This
-     * is intended to give an application some recourse when resources are low.
-     * TODO: Stop holding on to resources.
-     */
-    virtual void purgeResources() {
-        // The clip mask manager can rebuild all its clip masks so just get rid of them all.
-        fClipMaskManager->purgeResources();
-    };
 
     bool programUnitTest(GrContext* owner, int maxStages);
 
diff --git a/src/gpu/GrResourceProvider.h b/src/gpu/GrResourceProvider.h
index 33a9f3b..a709c70 100644
--- a/src/gpu/GrResourceProvider.h
+++ b/src/gpu/GrResourceProvider.h
@@ -90,6 +90,7 @@
 
     using GrTextureProvider::assignUniqueKeyToResource;
     using GrTextureProvider::findAndRefResourceByUniqueKey;
+    using GrTextureProvider::findAndRefTextureByUniqueKey;
     using GrTextureProvider::abandon;
 
     enum Flags {
@@ -142,6 +143,8 @@
      */
     GrStencilAttachment* attachStencilAttachment(GrRenderTarget* rt);
 
+    const GrCaps* caps() { return this->gpu()->caps(); }
+
 private:
     const GrIndexBuffer* createInstancedIndexBuffer(const uint16_t* pattern,
                                                     int patternSize,
diff --git a/tests/ClipBoundsTest.cpp b/tests/ClipBoundsTest.cpp
new file mode 100644
index 0000000..781ee87
--- /dev/null
+++ b/tests/ClipBoundsTest.cpp
@@ -0,0 +1,85 @@
+
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "Test.h"
+// This is a GR test
+#if SK_SUPPORT_GPU
+#include "GrClipMaskManager.h"
+#include "GrContextFactory.h"
+#include "SkGpuDevice.h"
+
+// Ensure that the 'getConservativeBounds' calls are returning bounds clamped
+// to the render target
+static void test_clip_bounds(skiatest::Reporter* reporter, GrContext* context) {
+
+    static const int kXSize = 100;
+    static const int kYSize = 100;
+
+    GrSurfaceDesc desc;
+    desc.fFlags     = kRenderTarget_GrSurfaceFlag;
+    desc.fConfig    = kAlpha_8_GrPixelConfig;
+    desc.fWidth     = kXSize;
+    desc.fHeight    = kYSize;
+
+    SkAutoTUnref<GrTexture> texture(
+        context->textureProvider()->createTexture(desc, false, nullptr, 0));
+    if (!texture) {
+        return;
+    }
+
+    SkIRect intScreen = SkIRect::MakeWH(kXSize, kYSize);
+    SkRect screen = SkRect::Make(intScreen);
+
+    SkRect clipRect(screen);
+    clipRect.outset(10, 10);
+
+    // create a clip stack that will (trivially) reduce to a single rect that
+    // is larger than the screen
+    SkClipStack stack;
+    stack.clipDevRect(clipRect, SkRegion::kReplace_Op, false);
+
+    bool isIntersectionOfRects = true;
+    SkRect devStackBounds;
+
+    stack.getConservativeBounds(0, 0, kXSize, kYSize,
+                                &devStackBounds,
+                                &isIntersectionOfRects);
+
+    // make sure that the SkClipStack is behaving itself
+    REPORTER_ASSERT(reporter, screen == devStackBounds);
+    REPORTER_ASSERT(reporter, isIntersectionOfRects);
+
+    // wrap the SkClipStack in a GrClip
+    GrClip clipData;
+    clipData.setClipStack(&stack);
+
+    SkIRect devGrClipBound;
+    clipData.getConservativeBounds(texture,
+                                   &devGrClipBound,
+                                   &isIntersectionOfRects);
+
+    // make sure that GrClip is behaving itself
+    REPORTER_ASSERT(reporter, intScreen == devGrClipBound);
+    REPORTER_ASSERT(reporter, isIntersectionOfRects);
+}
+
+DEF_GPUTEST(GrClipBounds, reporter, factory) {
+    for (int type = 0; type < GrContextFactory::kLastGLContextType; ++type) {
+        GrContextFactory::GLContextType glType = static_cast<GrContextFactory::GLContextType>(type);
+        if (!GrContextFactory::IsRenderingGLContext(glType)) {
+            continue;
+        }
+        GrContext* context = factory->get(glType);
+        if (nullptr == context) {
+            continue;
+        }
+        test_clip_bounds(reporter, context);
+    }
+}
+
+#endif
diff --git a/tests/ClipCacheTest.cpp b/tests/ClipCacheTest.cpp
deleted file mode 100644
index c57fb37..0000000
--- a/tests/ClipCacheTest.cpp
+++ /dev/null
@@ -1,231 +0,0 @@
-/*
- * Copyright 2012 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "Test.h"
-// This is a GR test
-#if SK_SUPPORT_GPU
-#include "GrClipMaskManager.h"
-#include "GrContextFactory.h"
-#include "SkGpuDevice.h"
-
-static const int X_SIZE = 12;
-static const int Y_SIZE = 12;
-
-////////////////////////////////////////////////////////////////////////////////
-// note: this is unused
-static GrTexture* create_texture(GrContext* context) {
-    unsigned char textureData[X_SIZE][Y_SIZE][4];
-
-    memset(textureData, 0, 4* X_SIZE * Y_SIZE);
-
-    GrSurfaceDesc desc;
-
-    // let Skia know we will be using this texture as a render target
-    desc.fFlags     = kRenderTarget_GrSurfaceFlag;
-    desc.fConfig    = kSkia8888_GrPixelConfig;
-    desc.fWidth     = X_SIZE;
-    desc.fHeight    = Y_SIZE;
-
-    // We are initializing the texture with zeros here
-    GrTexture* texture = context->textureProvider()->createTexture(desc, false, textureData, 0);
-    if (!texture) {
-        return nullptr;
-    }
-
-    return texture;
-}
-
-// Ensure that the 'getConservativeBounds' calls are returning bounds clamped
-// to the render target
-static void test_clip_bounds(skiatest::Reporter* reporter, GrContext* context) {
-
-    static const int kXSize = 100;
-    static const int kYSize = 100;
-
-    GrSurfaceDesc desc;
-    desc.fFlags     = kRenderTarget_GrSurfaceFlag;
-    desc.fConfig    = kAlpha_8_GrPixelConfig;
-    desc.fWidth     = kXSize;
-    desc.fHeight    = kYSize;
-
-    GrTexture* texture = context->textureProvider()->createTexture(desc, false, nullptr, 0);
-    if (!texture) {
-        return;
-    }
-
-    SkAutoTUnref<GrTexture> au(texture);
-
-    SkIRect intScreen = SkIRect::MakeWH(kXSize, kYSize);
-    SkRect screen;
-
-    screen = SkRect::MakeWH(SkIntToScalar(kXSize),
-                            SkIntToScalar(kYSize));
-
-    SkRect clipRect(screen);
-    clipRect.outset(10, 10);
-
-    // create a clip stack that will (trivially) reduce to a single rect that
-    // is larger than the screen
-    SkClipStack stack;
-    stack.clipDevRect(clipRect, SkRegion::kReplace_Op, false);
-
-    bool isIntersectionOfRects = true;
-    SkRect devStackBounds;
-
-    stack.getConservativeBounds(0, 0, kXSize, kYSize,
-                                &devStackBounds,
-                                &isIntersectionOfRects);
-
-    // make sure that the SkClipStack is behaving itself
-    REPORTER_ASSERT(reporter, screen == devStackBounds);
-    REPORTER_ASSERT(reporter, isIntersectionOfRects);
-
-    // wrap the SkClipStack in a GrClip
-    GrClip clipData;
-    clipData.setClipStack(&stack);
-
-    SkIRect devGrClipBound;
-    clipData.getConservativeBounds(texture,
-                                   &devGrClipBound,
-                                   &isIntersectionOfRects);
-
-    // make sure that GrClip is behaving itself
-    REPORTER_ASSERT(reporter, intScreen == devGrClipBound);
-    REPORTER_ASSERT(reporter, isIntersectionOfRects);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// verify that the top state of the stack matches the passed in state
-static void check_state(skiatest::Reporter* reporter,
-                        const GrClipMaskCache& cache,
-                        const SkClipStack& clip,
-                        GrTexture* mask,
-                        const SkIRect& bound) {
-    REPORTER_ASSERT(reporter, clip.getTopmostGenID() == cache.getLastClipGenID());
-
-    REPORTER_ASSERT(reporter, mask == cache.getLastMask());
-
-    SkIRect cacheBound;
-    cache.getLastBound(&cacheBound);
-    REPORTER_ASSERT(reporter, bound == cacheBound);
-}
-
-static void check_empty_state(skiatest::Reporter* reporter,
-                              const GrClipMaskCache& cache) {
-    REPORTER_ASSERT(reporter, SkClipStack::kInvalidGenID == cache.getLastClipGenID());
-    REPORTER_ASSERT(reporter, nullptr == cache.getLastMask());
-
-    SkIRect emptyBound;
-    emptyBound.setEmpty();
-
-    SkIRect cacheBound;
-    cache.getLastBound(&cacheBound);
-    REPORTER_ASSERT(reporter, emptyBound == cacheBound);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// basic test of the cache's base functionality:
-//  push, pop, set, canReuse & getters
-static void test_cache(skiatest::Reporter* reporter, GrContext* context) {
-
-    if (false) { // avoid bit rot, suppress warning
-        create_texture(context);
-    }
-    GrClipMaskCache cache(context->resourceProvider());
-
-    // check initial state
-    check_empty_state(reporter, cache);
-
-    // set the current state
-    SkIRect bound1;
-    bound1.set(0, 0, 100, 100);
-
-    SkClipStack clip1(bound1);
-
-    GrSurfaceDesc desc;
-    desc.fFlags = kRenderTarget_GrSurfaceFlag;
-    desc.fWidth = X_SIZE;
-    desc.fHeight = Y_SIZE;
-    desc.fConfig = kSkia8888_GrPixelConfig;
-
-    cache.acquireMask(clip1.getTopmostGenID(), desc, bound1);
-
-    GrTexture* texture1 = cache.getLastMask();
-    REPORTER_ASSERT(reporter, texture1);
-    if (nullptr == texture1) {
-        return;
-    }
-
-    // check that the set took
-    check_state(reporter, cache, clip1, texture1, bound1);
-
-    // push the state
-    cache.push();
-
-    // verify that the pushed state is initially empty
-    check_empty_state(reporter, cache);
-
-    // modify the new state
-    SkIRect bound2;
-    bound2.set(-10, -10, 10, 10);
-
-    SkClipStack clip2(bound2);
-
-    cache.acquireMask(clip2.getTopmostGenID(), desc, bound2);
-
-    GrTexture* texture2 = cache.getLastMask();
-    REPORTER_ASSERT(reporter, texture2);
-    if (nullptr == texture2) {
-        return;
-    }
-
-    // check that the changes took
-    check_state(reporter, cache, clip2, texture2, bound2);
-
-    // check to make sure canReuse works
-    REPORTER_ASSERT(reporter, cache.canReuse(clip2.getTopmostGenID(), bound2));
-    REPORTER_ASSERT(reporter, !cache.canReuse(clip1.getTopmostGenID(), bound1));
-
-    // pop the state
-    cache.pop();
-
-    // verify that the old state is restored
-    check_state(reporter, cache, clip1, texture1, bound1);
-
-    // manually clear the state
-    cache.reset();
-
-    // verify it is now empty
-    check_empty_state(reporter, cache);
-
-    // pop again - so there is no state
-    cache.pop();
-
-#if !defined(SK_DEBUG)
-    // verify that the getters don't crash
-    // only do in release since it generates asserts in debug
-    check_empty_state(reporter, cache);
-#endif
-}
-
-DEF_GPUTEST(ClipCache, reporter, factory) {
-    for (int type = 0; type < GrContextFactory::kLastGLContextType; ++type) {
-        GrContextFactory::GLContextType glType = static_cast<GrContextFactory::GLContextType>(type);
-        if (!GrContextFactory::IsRenderingGLContext(glType)) {
-            continue;
-        }
-        GrContext* context = factory->get(glType);
-        if (nullptr == context) {
-            continue;
-        }
-
-        test_cache(reporter, context);
-        test_clip_bounds(reporter, context);
-    }
-}
-
-#endif
diff --git a/tests/ResourceCacheTest.cpp b/tests/ResourceCacheTest.cpp
index 0036f83..83e4535 100644
--- a/tests/ResourceCacheTest.cpp
+++ b/tests/ResourceCacheTest.cpp
@@ -18,6 +18,7 @@
 #include "GrRenderTarget.h"
 #include "GrRenderTargetPriv.h"
 #include "GrResourceCache.h"
+#include "GrResourceProvider.h"
 #include "GrTest.h"
 #include "SkCanvas.h"
 #include "SkGr.h"