Make GrTextureCache into a generic GrResource cache. Also some GrContext texture interface cleanup.

http://codereview.appspot.com/4815055/


git-svn-id: http://skia.googlecode.com/svn/trunk@1965 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/gpu/include/GrContext.h b/gpu/include/GrContext.h
index 0f69e0b..c7a87da 100644
--- a/gpu/include/GrContext.h
+++ b/gpu/include/GrContext.h
@@ -18,16 +18,18 @@
 #define GrContext_DEFINED
 
 #include "GrClip.h"
-#include "GrTextureCache.h"
 #include "GrPaint.h"
 #include "GrPathRenderer.h"
 
 class GrFontCache;
 class GrGpu;
 struct GrGpuStats;
-class GrVertexBufferAllocPool;
 class GrIndexBufferAllocPool;
 class GrInOrderDrawBuffer;
+class GrResourceEntry;
+class GrResourceCache;
+class GrVertexBufferAllocPool;
+
 
 class GR_API GrContext : public GrRefCnt {
 public:
@@ -82,54 +84,92 @@
     // Textures
 
     /**
-     *  Search for an entry with the same Key. If found, "lock" it and return it.
-     *  If not found, return null.
+     * Token that refers to an entry in the texture cache. Returned by
+     * functions that lock textures. Passed to unlockTexture.
      */
-    GrTextureEntry* findAndLockTexture(GrTextureKey*,
-                                       const GrSamplerState&);
+    class TextureCacheEntry {
+    public:
+        TextureCacheEntry() : fEntry(NULL) {}
+        TextureCacheEntry(const TextureCacheEntry& e) : fEntry(e.fEntry) {}
+        TextureCacheEntry& operator= (const TextureCacheEntry& e) {
+            fEntry = e.fEntry;
+            return *this;
+        }
+        GrTexture* texture() const;
+        void reset() { fEntry = NULL; }
+    private:
+        explicit TextureCacheEntry(GrResourceEntry* entry) { fEntry = entry; }
+        void set(GrResourceEntry* entry) { fEntry = entry; }
+        GrResourceEntry* cacheEntry() { return fEntry; }
+        GrResourceEntry* fEntry;
+        friend class GrContext;
+    };
 
+    /**
+     * Key generated by client. Should be a unique key on the texture data.
+     * Does not need to consider that width and height of the texture. Two
+     * textures with the same TextureKey but different bounds will not collide.
+     */
+    typedef uint64_t TextureKey;
+
+    /**
+     *  Search for an entry based on key and dimensions. If found, "lock" it and
+     *  return it. The entry's texture() function will return NULL if not found.
+     *  Must call be balanced with an unlockTexture() call.
+     */
+    TextureCacheEntry findAndLockTexture(TextureKey key,
+                                         int width,
+                                         int height,
+                                         const GrSamplerState&);
 
     /**
      *  Create a new entry, based on the specified key and texture, and return
-     *  its "locked" entry.
-     *
-     *  Ownership of the texture is transferred to the Entry, which will unref()
-     *  it when we are purged or deleted.
+     *  its "locked" entry. Must call be balanced with an unlockTexture() call.
      */
-    GrTextureEntry* createAndLockTexture(GrTextureKey* key,
-                                         const GrSamplerState&,
-                                         const GrTextureDesc&,
-                                         void* srcData, size_t rowBytes);
+    TextureCacheEntry createAndLockTexture(TextureKey key,
+                                           const GrSamplerState&,
+                                           const GrTextureDesc&,
+                                           void* srcData, size_t rowBytes);
+
+    /**
+     * Enum that determines how closely a returned scratch texture must match
+     * a provided GrTextureDesc.
+     */
+    enum ScratchTexMatch {
+        /**
+         * Finds a texture that exactly matches the descriptor.
+         */
+        kExact_ScratchTexMatch,
+        /**
+         * Finds a texture that approximately matches the descriptor. Will be
+         * at least as large in width and height as desc specifies. If desc
+         * specifies that texture is a render target then result will be a
+         * render target. If desc specifies a render target and doesn't set the
+         * no stencil flag then result will have a stencil. Format and aa level
+         * will always match.
+         */
+        kApprox_ScratchTexMatch
+    };
 
     /**
      * Returns a texture matching the desc. It's contents are unknown. Subsequent
      * requests with the same descriptor are not guaranteed to return the same
      * texture. The same texture is guaranteed not be returned again until it is
-     * unlocked.
+     * unlocked. Must call be balanced with an unlockTexture() call.
      *
      * Textures created by createAndLockTexture() hide the complications of
      * tiling non-power-of-two textures on APIs that don't support this (e.g. 
-     * unextended GLES2). Tiling a npot texture created by lockKeylessTexture on
+     * unextended GLES2). Tiling a npot texture created by lockScratchTexture on
      * such an API will create gaps in the tiling pattern. This includes clamp
      * mode. (This may be addressed in a future update.)
      */
-    GrTextureEntry* lockKeylessTexture(const GrTextureDesc& desc);
-
-    /**
-     * Finds a texture that approximately matches the descriptor. Will be
-     * at least as large in width and height as desc specifies. If desc
-     * specifies that texture is a render target then result will be a
-     * render target. If desc specifies a render target and doesn't set the
-     * no stencil flag then result will have a stencil. Format and aa level
-     * will always match.
-     */
-    GrTextureEntry* findApproximateKeylessTexture(const GrTextureDesc& desc);
+    TextureCacheEntry lockScratchTexture(const GrTextureDesc& desc, ScratchTexMatch match);
 
     /**
      *  When done with an entry, call unlockTexture(entry) on it, which returns
      *  it to the cache, where it may be purged.
      */
-    void unlockTexture(GrTextureEntry* entry);
+    void unlockTexture(TextureCacheEntry entry);
 
     /**
      * Creates a texture that is outside the cache. Does not count against
@@ -195,9 +235,6 @@
     ///////////////////////////////////////////////////////////////////////////
     // Platform Surfaces
 
-    // GrContext provides an interface for wrapping externally created textures
-    // and rendertargets in their Gr-equivalents.
-
     /**
      * Wraps an existing 3D API surface in a GrObject. desc.fFlags determines
      * the type of object returned. If kIsTexture is set the returned object
@@ -213,6 +250,7 @@
      *         on failure.
      */
     GrResource* createPlatformSurface(const GrPlatformSurfaceDesc& desc);
+
     /**
      * Reads the current target object (e.g. FBO or IDirect3DSurface9*) and
      * viewport state from the underlying 3D API and wraps it in a
@@ -559,9 +597,9 @@
     };
     DrawCategory fLastDrawCategory;
 
-    GrGpu*          fGpu;
-    GrTextureCache* fTextureCache;
-    GrFontCache*    fFontCache;
+    GrGpu*              fGpu;
+    GrResourceCache*    fTextureCache;
+    GrFontCache*        fFontCache;
 
     GrPathRenderer*         fCustomPathRenderer;
     GrDefaultPathRenderer   fDefaultPathRenderer;
@@ -597,10 +635,6 @@
 
     static void SetPaint(const GrPaint& paint, GrDrawTarget* target);
 
-    bool finalizeTextureKey(GrTextureKey*, 
-                            const GrSamplerState&,
-                            bool keyless) const;
-
     GrDrawTarget* prepareToDraw(const GrPaint& paint, DrawCategory drawType);
 
     void drawClipIntoStencil();
@@ -680,24 +714,54 @@
 };
 
 /**
- * Unlocks a texture entry when this goes out of scope.  Entry may be NULL.
+ * Gets and locks a scratch texture from a descriptor using
+ * either exact or approximate criteria. Unlocks texture in
+ * the destructor.
  */
-class GrAutoUnlockTextureEntry : ::GrNoncopyable {
+class GrAutoScratchTexture : ::GrNoncopyable {
 public:
-    GrAutoUnlockTextureEntry(GrContext* context, GrTextureEntry* entry)
-      : fContext(context)
-      , fEntry(entry) {
+    GrAutoScratchTexture()
+        : fContext(NULL) {
     }
-    ~GrAutoUnlockTextureEntry() {
-        if (fContext && fEntry) {
+
+    GrAutoScratchTexture(GrContext* context,
+                         const GrTextureDesc& desc,
+                         GrContext::ScratchTexMatch match =
+                            GrContext::kApprox_ScratchTexMatch)
+      : fContext(NULL) {
+      this->set(context, desc, match);
+    }
+    
+    ~GrAutoScratchTexture() {
+        if (NULL != fContext) {
             fContext->unlockTexture(fEntry);
         }
     }
 
-    GrTexture* texture() { return fEntry->texture(); }
+    GrTexture* set(GrContext* context,
+                   const GrTextureDesc& desc,
+                   GrContext::ScratchTexMatch match =
+                        GrContext::kApprox_ScratchTexMatch) {
+        if (NULL != fContext) {
+            fContext->unlockTexture(fEntry);
+        }
+        fContext = context;
+        if (NULL != fContext) {
+            fEntry = fContext->lockScratchTexture(desc, match);
+            GrTexture* ret = fEntry.texture();
+            if (NULL == ret) {
+                fContext = NULL;
+            }
+            return ret;
+        } else {
+            return NULL;
+        }
+    }
+
+    GrTexture* texture() { return fEntry.texture(); }
 private:
-    GrContext*      fContext;
-    GrTextureEntry* fEntry;
+    GrContext*                    fContext;
+    GrContext::TextureCacheEntry  fEntry;
 };
 
 #endif
diff --git a/gpu/include/GrTextureCache.h b/gpu/include/GrTextureCache.h
deleted file mode 100644
index 444ffea..0000000
--- a/gpu/include/GrTextureCache.h
+++ /dev/null
@@ -1,308 +0,0 @@
-/*
-    Copyright 2010 Google Inc.
-
-    Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
- */
-
-
-#ifndef GrTextureCache_DEFINED
-#define GrTextureCache_DEFINED
-
-#include "GrTypes.h"
-#include "GrTHashCache.h"
-
-class GrTexture;
-
-// return true if a<b, or false if b<a
-//
-#define RET_IF_LT_OR_GT(a, b)   \
-    do {                        \
-        if ((a) < (b)) {        \
-            return true;        \
-        }                       \
-        if ((b) < (a)) {        \
-            return false;       \
-        }                       \
-    } while (0)
-
-/**
- *  Helper class for GrTextureCache, the Key is used to identify src data for
- *  a texture. It is identified by 2 32bit data fields which can hold any
- *  data (uninterpreted by the cache) and a width/height.
- */
-class GrTextureKey {
-public:
-    enum {
-        kHashBits   = 7,
-        kHashCount  = 1 << kHashBits,
-        kHashMask   = kHashCount - 1
-    };
-
-    GrTextureKey(uint32_t p0, uint32_t p1, uint16_t width, uint16_t height) {
-        fP0 = p0;
-        fP1 = p1;
-        fP2 = width | (height << 16);
-        GR_DEBUGCODE(fHashIndex = -1);
-    }
-
-    GrTextureKey(const GrTextureKey& src) {
-        fP0 = src.fP0;
-        fP1 = src.fP1;
-        fP2 = src.fP2;
-        finalize(src.fPrivateBits);
-    }
-
-    //!< returns hash value [0..kHashMask] for the key
-    int hashIndex() const { return fHashIndex; }
-
-    friend bool operator==(const GrTextureKey& a, const GrTextureKey& b) {
-        GR_DEBUGASSERT(-1 != a.fHashIndex && -1 != b.fHashIndex);
-        return a.fP0 == b.fP0 && a.fP1 == b.fP1 && a.fP2 == b.fP2 &&
-               a.fPrivateBits == b.fPrivateBits;
-    }
-
-    friend bool operator!=(const GrTextureKey& a, const GrTextureKey& b) {
-        GR_DEBUGASSERT(-1 != a.fHashIndex && -1 != b.fHashIndex);
-        return !(a == b);
-    }
-
-    friend bool operator<(const GrTextureKey& a, const GrTextureKey& b) {
-        RET_IF_LT_OR_GT(a.fP0, b.fP0);
-        RET_IF_LT_OR_GT(a.fP1, b.fP1);
-        RET_IF_LT_OR_GT(a.fP2, b.fP2);
-        return a.fPrivateBits < b.fPrivateBits;
-    }
-
-private:
-    void finalize(uint32_t privateBits) {
-        fPrivateBits = privateBits;
-        this->computeHashIndex();
-    }
-
-    uint16_t width() const { return fP2 & 0xffff; }
-    uint16_t height() const { return (fP2 >> 16); }
-
-    uint32_t getPrivateBits() const { return fPrivateBits; }
-
-    static uint32_t rol(uint32_t x) {
-        return (x >> 24) | (x << 8);
-    }
-    static uint32_t ror(uint32_t x) {
-        return (x >> 8) | (x << 24);
-    }
-    static uint32_t rohalf(uint32_t x) {
-        return (x >> 16) | (x << 16);
-    }
-
-    void computeHashIndex() {
-        uint32_t hash = fP0 ^ rol(fP1) ^ ror(fP2) ^ rohalf(fPrivateBits);
-        // this way to mix and reduce hash to its index may have to change
-        // depending on how many bits we allocate to the index
-        hash ^= hash >> 16;
-        hash ^= hash >> 8;
-        fHashIndex = hash & kHashMask;
-    }
-
-    uint32_t    fP0;
-    uint32_t    fP1;
-    uint32_t    fP2;
-    uint32_t    fPrivateBits;
-
-    // this is computed from the fP... fields
-    int         fHashIndex;
-
-    friend class GrContext;
-};
-
-///////////////////////////////////////////////////////////////////////////////
-
-class GrTextureEntry {
-public:
-    GrTexture* texture() const { return fTexture; }
-    const GrTextureKey& key() const { return fKey; }
-
-#if GR_DEBUG
-    GrTextureEntry* next() const { return fNext; }
-    GrTextureEntry* prev() const { return fPrev; }
-#endif
-
-#if GR_DEBUG
-    void validate() const;
-#else
-    void validate() const {}
-#endif
-
-private:
-    GrTextureEntry(const GrTextureKey& key, GrTexture* texture);
-    ~GrTextureEntry();
-
-    bool isLocked() const { return fLockCount != 0; }
-    void lock() { ++fLockCount; }
-    void unlock() {
-        GrAssert(fLockCount > 0);
-        --fLockCount;
-    }
-
-    GrTextureKey    fKey;
-    GrTexture*      fTexture;
-
-    // track if we're in use, used when we need to purge
-    // we only purge unlocked entries
-    int fLockCount;
-
-    // we're a dlinklist
-    GrTextureEntry* fPrev;
-    GrTextureEntry* fNext;
-
-    friend class GrTextureCache;
-};
-
-///////////////////////////////////////////////////////////////////////////////
-
-#include "GrTHashCache.h"
-
-/**
- *  Cache of GrTexture objects.
- *
- *  These have a corresponding GrTextureKey, built from 96bits identifying the
- *  texture/bitmap.
- *
- *  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 sorted array (based on the GrTextureKey)
- *  which we can bsearch. When a new entry is added, it is inserted into this
- *  array.
- *
- *  For even faster searches, a hash is computed from the Key. If there is
- *  a collision between two keys with the same hash, we fall back on the
- *  bsearch, and update the hash to reflect the most recent Key requested.
- */
-class GrTextureCache {
-public:
-    GrTextureCache(int maxCount, size_t maxBytes);
-    ~GrTextureCache();
-
-    /**
-     *  Return the current texture cache limits.
-     *
-     *  @param maxTextures If non-null, returns maximum number of textures that
-     *                     can be held in the cache.
-     *  @param maxTextureBytes If non-null, returns maximum number of bytes of
-     *                         texture memory that can be held in the cache.
-     */
-    void getLimits(int* maxTextures, size_t* maxTextureBytes) const;
-
-    /**
-     *  Specify the texture cache limits. If the current cache exceeds either
-     *  of these, it will be purged (LRU) to keep the cache within these limits.
-     *
-     *  @param maxTextures The maximum number of textures that can be held in
-     *                     the cache.
-     *  @param maxTextureBytes The maximum number of bytes of texture memory
-     *                         that can be held in the cache.
-     */
-    void setLimits(int maxTextures, size_t maxTextureBytes);
-
-    /**
-     *  Search for an entry with the same Key. If found, "lock" it and return it.
-     *  If not found, return null.
-     */
-    GrTextureEntry* findAndLock(const GrTextureKey&);
-
-    /**
-     *  Create a new entry, based on the specified key and texture, and return
-     *  its "locked" entry.
-     *
-     *  Ownership of the texture is transferred to the Entry, which will unref()
-     *  it when we are purged or deleted.
-     */
-    GrTextureEntry* createAndLock(const GrTextureKey&, GrTexture*);
-
-    /**
-     * Detach removes an entry from the cache. This prevents the entry from
-     * being found by a subsequent findAndLock() until it is reattached. The
-     * entry still counts against the cache's budget and should be reattached
-     * when exclusive access is no longer needed.
-     */
-    void detach(GrTextureEntry*);
-
-    /**
-     * Reattaches a texture to the cache and unlocks it. Allows it to be found
-     * by a subsequent findAndLock or be purged (provided its lock count is
-     * now 0.)
-     */
-    void reattachAndUnlock(GrTextureEntry*);
-
-    /**
-     *  When done with an entry, call unlock(entry) on it, which returns it to
-     *  a purgable state.
-     */
-    void unlock(GrTextureEntry*);
-
-    void removeAll();
-
-#if GR_DEBUG
-    void validate() const;
-#else
-    void validate() const {}
-#endif
-
-private:
-    void internalDetach(GrTextureEntry*, bool);
-    void attachToHead(GrTextureEntry*, bool);
-    void purgeAsNeeded();   // uses kFreeTexture_DeleteMode
-
-    class Key;
-    GrTHashTable<GrTextureEntry, Key, 8> fCache;
-
-    // manage the dlink list
-    GrTextureEntry* fHead;
-    GrTextureEntry* fTail;
-
-    // our budget, used in purgeAsNeeded()
-    int fMaxCount;
-    size_t fMaxBytes;
-
-    // our current stats, related to our budget
-    int fEntryCount;
-    size_t fEntryBytes;
-    int fClientDetachedCount;
-    size_t fClientDetachedBytes;
-};
-
-///////////////////////////////////////////////////////////////////////////////
-
-#if GR_DEBUG
-    class GrAutoTextureCacheValidate {
-    public:
-        GrAutoTextureCacheValidate(GrTextureCache* cache) : fCache(cache) {
-            cache->validate();
-        }
-        ~GrAutoTextureCacheValidate() {
-            fCache->validate();
-        }
-    private:
-        GrTextureCache* fCache;
-    };
-#else
-    class GrAutoTextureCacheValidate {
-    public:
-        GrAutoTextureCacheValidate(GrTextureCache*) {}
-    };
-#endif
-
-#endif
-
diff --git a/gpu/src/GrContext.cpp b/gpu/src/GrContext.cpp
index b829323..2f34e5a 100644
--- a/gpu/src/GrContext.cpp
+++ b/gpu/src/GrContext.cpp
@@ -22,7 +22,7 @@
 #include "GrInOrderDrawBuffer.h"
 #include "GrPathRenderer.h"
 #include "GrPathUtils.h"
-#include "GrTextureCache.h"
+#include "GrResourceCache.h"
 #include "GrTextStrike.h"
 #include "SkTrace.h"
 
@@ -135,41 +135,66 @@
 enum {
     kNPOTBit    = 0x1,
     kFilterBit  = 0x2,
-    kKeylessBit = 0x4,
+    kScratchBit = 0x4,
 };
 
-bool GrContext::finalizeTextureKey(GrTextureKey* key,
-                                   const GrSamplerState& sampler,
-                                   bool keyless) const {
-    uint32_t bits = 0;
-    uint16_t width = key->width();
-    uint16_t height = key->height();
+GrTexture* GrContext::TextureCacheEntry::texture() const {
+    if (NULL == fEntry) {
+        return NULL; 
+    } else {
+        return (GrTexture*) fEntry->resource();
+    }
+}
 
-    if (!fGpu->npotTextureTileSupport()) {
+namespace {
+// returns true if this is a "special" texture because of gpu NPOT limitations
+bool gen_texture_key_values(const GrGpu* gpu,
+                            const GrSamplerState& sampler,
+                            GrContext::TextureKey clientKey,
+                            int width,
+                            int height,
+                            bool scratch,
+                            uint32_t v[4]) {
+    GR_STATIC_ASSERT(sizeof(GrContext::TextureKey) == sizeof(uint64_t));
+    // we assume we only need 16 bits of width and height
+    // assert that texture creation will fail anyway if this assumption
+    // would cause key collisions.
+    GrAssert(gpu->maxTextureSize() <= SK_MaxU16);
+    v[0] = clientKey & 0xffffffffUL;
+    v[1] = (clientKey >> 32) & 0xffffffffUL;
+    v[2] = width | (height << 16);
+
+    v[3] = 0;
+    if (!gpu->npotTextureTileSupport()) {
         bool isPow2 = GrIsPow2(width) && GrIsPow2(height);
 
         bool tiled = (sampler.getWrapX() != GrSamplerState::kClamp_WrapMode) ||
                      (sampler.getWrapY() != GrSamplerState::kClamp_WrapMode);
 
         if (tiled && !isPow2) {
-            bits |= kNPOTBit;
+            v[3] |= kNPOTBit;
             if (GrSamplerState::kNearest_Filter != sampler.getFilter()) {
-                bits |= kFilterBit;
+                v[3] |= kFilterBit;
             }
         }
     }
 
-    if (keyless) {
-        bits |= kKeylessBit;
+    if (scratch) {
+        v[3] |= kScratchBit;
     }
-    key->finalize(bits);
-    return 0 != bits;
+
+    return v[3] & kNPOTBit;
+}
 }
 
-GrTextureEntry* GrContext::findAndLockTexture(GrTextureKey* key,
-                                              const GrSamplerState& sampler) {
-    finalizeTextureKey(key, sampler, false);
-    return fTextureCache->findAndLock(*key);
+GrContext::TextureCacheEntry GrContext::findAndLockTexture(TextureKey key,
+                                                           int width,
+                                                           int height,
+                                                const GrSamplerState& sampler) {
+    uint32_t v[4];
+    gen_texture_key_values(fGpu, sampler, key, width, height, false, v);
+    GrResourceKey resourceKey(v);
+    return TextureCacheEntry(fTextureCache->findAndLock(resourceKey));
 }
 
 static void stretchImage(void* dst,
@@ -199,32 +224,34 @@
     }
 }
 
-GrTextureEntry* GrContext::createAndLockTexture(GrTextureKey* key,
+GrContext::TextureCacheEntry GrContext::createAndLockTexture(TextureKey key,
                                                 const GrSamplerState& sampler,
                                                 const GrTextureDesc& desc,
                                                 void* srcData, size_t rowBytes) {
     SK_TRACE_EVENT0("GrContext::createAndLockTexture");
-    GrAssert(key->width() == desc.fWidth);
-    GrAssert(key->height() == desc.fHeight);
 
 #if GR_DUMP_TEXTURE_UPLOAD
     GrPrintf("GrContext::createAndLockTexture [%d %d]\n", desc.fWidth, desc.fHeight);
 #endif
 
-    GrTextureEntry* entry = NULL;
-    bool special = finalizeTextureKey(key, sampler, false);
-    if (special) {
-        GrTextureEntry* clampEntry;
-        GrTextureKey clampKey(*key);
-        clampEntry = findAndLockTexture(&clampKey, GrSamplerState::ClampNoFilter());
+    TextureCacheEntry entry;
+    uint32_t v[4];
+    bool special = gen_texture_key_values(fGpu, sampler, key,
+                                          desc.fWidth, desc.fHeight, false, v);
+    GrResourceKey resourceKey(v);
 
-        if (NULL == clampEntry) {
-            clampEntry = createAndLockTexture(&clampKey,
+    if (special) {
+        TextureCacheEntry clampEntry = 
+                            findAndLockTexture(key, desc.fWidth, desc.fHeight,
+                                               GrSamplerState::ClampNoFilter());
+
+        if (NULL == clampEntry.texture()) {
+            clampEntry = createAndLockTexture(key,
                                               GrSamplerState::ClampNoFilter(),
                                               desc, srcData, rowBytes);
-            GrAssert(NULL != clampEntry);
-            if (NULL == clampEntry) {
-                return NULL;
+            GrAssert(NULL != clampEntry.texture());
+            if (NULL == clampEntry.texture()) {
+                return entry;
             }
         }
         GrTextureDesc rtDesc = desc;
@@ -241,7 +268,7 @@
         if (NULL != texture) {
             GrDrawTarget::AutoStateRestore asr(fGpu);
             fGpu->setRenderTarget(texture->asRenderTarget());
-            fGpu->setTexture(0, clampEntry->texture());
+            fGpu->setTexture(0, clampEntry.texture());
             fGpu->disableStencil();
             fGpu->setViewMatrix(GrMatrix::I());
             fGpu->setAlpha(0xff);
@@ -276,7 +303,7 @@
                 verts[1].setIRectFan(0, 0, 1, 1, 2*sizeof(GrPoint));
                 fGpu->drawNonIndexed(kTriangleFan_PrimitiveType,
                                      0, 4);
-                entry = fTextureCache->createAndLock(*key, texture);
+                entry.set(fTextureCache->createAndLock(resourceKey, texture));
             }
             texture->releaseRenderTarget();
         } else {
@@ -302,69 +329,64 @@
                                                      stretchedPixels.get(),
                                                      stretchedRowBytes);
             GrAssert(NULL != texture);
-            entry = fTextureCache->createAndLock(*key, texture);
+            entry.set(fTextureCache->createAndLock(resourceKey, texture));
         }
-        fTextureCache->unlock(clampEntry);
+        fTextureCache->unlock(clampEntry.cacheEntry());
 
     } else {
         GrTexture* texture = fGpu->createTexture(desc, srcData, rowBytes);
         if (NULL != texture) {
-            entry = fTextureCache->createAndLock(*key, texture);
-        } else {
-            entry = NULL;
+            entry.set(fTextureCache->createAndLock(resourceKey, texture));
         }
     }
     return entry;
 }
 
-GrTextureEntry* GrContext::lockKeylessTexture(const GrTextureDesc& desc) {
-    uint32_t p0 = desc.fFormat;
-    uint32_t p1 = (desc.fAALevel << 16) | desc.fFlags;
-    GrTextureKey key(p0, p1, desc.fWidth, desc.fHeight);
-    this->finalizeTextureKey(&key, GrSamplerState::ClampNoFilter(), true);
-    
-    GrTextureEntry* entry = fTextureCache->findAndLock(key);
-    if (NULL == entry) {
-        GrTexture* texture = fGpu->createTexture(desc, NULL, 0);
-        if (NULL != texture) {
-            entry = fTextureCache->createAndLock(key, texture);
-        }
-    }
-    // If the caller gives us the same desc/sampler twice we don't want
-    // to return the same texture the second time (unless it was previously
-    // released). So we detach the entry from the cache and reattach at release.
-    if (NULL != entry) {
-        fTextureCache->detach(entry);
-    }
-    return entry;
+namespace {
+inline void gen_scratch_tex_key_values(const GrGpu* gpu, 
+                                       const GrTextureDesc& desc,
+                                       uint32_t v[4]) {
+    // Instead of a client-provided key of the texture contents
+    // we create a key of from the descriptor.
+    GrContext::TextureKey descKey = desc.fAALevel |
+                                    (desc.fFlags << 8) |
+                                    ((uint64_t) desc.fFormat << 32);
+    // this code path isn't friendly to tiling with NPOT restricitons
+    // We just pass ClampNoFilter()
+    gen_texture_key_values(gpu, GrSamplerState::ClampNoFilter(), descKey,
+                            desc.fWidth, desc.fHeight, true, v);
+}
 }
 
-GrTextureEntry* GrContext::findApproximateKeylessTexture(
-                                                    const GrTextureDesc& inDesc) {
+GrContext::TextureCacheEntry GrContext::lockScratchTexture(
+                                                const GrTextureDesc& inDesc,
+                                                ScratchTexMatch match) {
+
     GrTextureDesc desc = inDesc;
-    // bin by pow2 with a reasonable min
-    static const int MIN_SIZE = 256;
-    desc.fWidth  = GrMax(MIN_SIZE, GrNextPow2(desc.fWidth));
-    desc.fHeight = GrMax(MIN_SIZE, GrNextPow2(desc.fHeight));
+    if (kExact_ScratchTexMatch != match) {
+        // bin by pow2 with a reasonable min
+        static const int MIN_SIZE = 256;
+        desc.fWidth  = GrMax(MIN_SIZE, GrNextPow2(desc.fWidth));
+        desc.fHeight = GrMax(MIN_SIZE, GrNextPow2(desc.fHeight));
+    }
 
     uint32_t p0 = desc.fFormat;
     uint32_t p1 = (desc.fAALevel << 16) | desc.fFlags;
     
-    GrTextureEntry* entry;
-    bool keepTrying = true;
+    GrResourceEntry* entry;
     int origWidth = desc.fWidth;
     int origHeight = desc.fHeight;
     bool doubledW = false;
     bool doubledH = false;
 
     do {
-        GrTextureKey key(p0, p1, desc.fWidth, desc.fHeight);
-        this->finalizeTextureKey(&key, GrSamplerState::ClampNoFilter(), true);
+        uint32_t v[4];
+        gen_scratch_tex_key_values(fGpu, desc, v);
+        GrResourceKey key(v);
         entry = fTextureCache->findAndLock(key);
-
         // if we miss, relax the fit of the flags...
         // then try doubling width... then height.
-        if (NULL != entry) {
+        if (NULL != entry || kExact_ScratchTexMatch == match) {
             break;
         }
         if (!(desc.fFlags & kRenderTarget_GrTextureFlagBit)) {
@@ -392,9 +414,9 @@
         desc.fHeight = origHeight;
         GrTexture* texture = fGpu->createTexture(desc, NULL, 0);
         if (NULL != texture) {
-            GrTextureKey key(p0, p1, desc.fWidth, desc.fHeight);
-            this->finalizeTextureKey(&key, GrSamplerState::ClampNoFilter(), 
-                                     true);
+            uint32_t v[4];
+            gen_scratch_tex_key_values(fGpu, desc, v);
+            GrResourceKey key(v);
             entry = fTextureCache->createAndLock(key, texture);
         }
     }
@@ -405,14 +427,17 @@
     if (NULL != entry) {
         fTextureCache->detach(entry);
     }
-    return entry;
+    return TextureCacheEntry(entry);
 }
 
-void GrContext::unlockTexture(GrTextureEntry* entry) {
-    if (kKeylessBit & entry->key().getPrivateBits()) {
-        fTextureCache->reattachAndUnlock(entry);
+void GrContext::unlockTexture(TextureCacheEntry entry) {
+    // If this is a scratch texture we detached it from the cache
+    // while it was locked (to avoid two callers simultaneously getting
+    // the same texture).
+    if (kScratchBit & entry.cacheEntry()->key().getValue32(3)) {
+        fTextureCache->reattachAndUnlock(entry.cacheEntry());
     } else {
-        fTextureCache->unlock(entry);
+        fTextureCache->unlock(entry.cacheEntry());
     }
 }
 
@@ -529,9 +554,6 @@
 ////////////////////////////////////////////////////////////////////////////////
 
 struct GrContext::OffscreenRecord {
-    OffscreenRecord() { fEntry0 = NULL; fEntry1 = NULL; }
-    ~OffscreenRecord() { GrAssert(NULL == fEntry0 && NULL == fEntry1); }
-
     enum Downsample {
         k4x4TwoPass_Downsample,
         k4x4SinglePass_Downsample,
@@ -542,8 +564,8 @@
     int                            fTileCountX;
     int                            fTileCountY;
     int                            fScale;
-    GrTextureEntry*                fEntry0;
-    GrTextureEntry*                fEntry1;
+    GrAutoScratchTexture           fOffscreen0;
+    GrAutoScratchTexture           fOffscreen1;
     GrDrawTarget::SavedDrawState   fSavedState;
     GrClip                         fClip;
 };
@@ -585,8 +607,8 @@
 
     GrAssert(GR_USE_OFFSCREEN_AA);
 
-    GrAssert(NULL == record->fEntry0);
-    GrAssert(NULL == record->fEntry1);
+    GrAssert(NULL == record->fOffscreen0.texture());
+    GrAssert(NULL == record->fOffscreen1.texture());
     GrAssert(!boundRect.isEmpty());
 
     int boundW = boundRect.width();
@@ -627,31 +649,28 @@
     
     desc.fWidth *= record->fScale;
     desc.fHeight *= record->fScale;
-
-    record->fEntry0 = this->findApproximateKeylessTexture(desc);
-    if (NULL == record->fEntry0) {
+    record->fOffscreen0.set(this, desc);
+    if (NULL == record->fOffscreen0.texture()) {
         return false;
     }
     // the approximate lookup might have given us some slop space, might as well
     // use it when computing the tiles size.
     // these are scale values, will adjust after considering
     // the possible second offscreen.
-    record->fTileSizeX = record->fEntry0->texture()->width();
-    record->fTileSizeY = record->fEntry0->texture()->height();
+    record->fTileSizeX = record->fOffscreen0.texture()->width();
+    record->fTileSizeY = record->fOffscreen0.texture()->height();
 
     if (OffscreenRecord::k4x4TwoPass_Downsample == record->fDownsample) {
         desc.fWidth /= 2;
         desc.fHeight /= 2;
-        record->fEntry1 = this->findApproximateKeylessTexture(desc);
-        if (NULL == record->fEntry1) {
-            this->unlockTexture(record->fEntry0);
-            record->fEntry0 = NULL;
+        record->fOffscreen1.set(this, desc);
+        if (NULL == record->fOffscreen1.texture()) {
             return false;
         }
         record->fTileSizeX = GrMin(record->fTileSizeX, 
-                                   2 * record->fEntry0->texture()->width());
+                                   2 * record->fOffscreen0.texture()->width());
         record->fTileSizeY = GrMin(record->fTileSizeY, 
-                                   2 * record->fEntry0->texture()->height());
+                                   2 * record->fOffscreen0.texture()->height());
     }
     record->fTileSizeX /= record->fScale;
     record->fTileSizeY /= record->fScale;
@@ -670,7 +689,7 @@
                                       int tileX, int tileY,
                                       OffscreenRecord* record) {
 
-    GrRenderTarget* offRT0 = record->fEntry0->texture()->asRenderTarget();
+    GrRenderTarget* offRT0 = record->fOffscreen0.texture()->asRenderTarget();
     GrAssert(NULL != offRT0);
 
     GrPaint tempPaint;
@@ -715,7 +734,7 @@
                                  int tileX, int tileY,
                                  OffscreenRecord* record) {
     SK_TRACE_EVENT0("GrContext::doOffscreenAAPass2");
-    GrAssert(NULL != record->fEntry0);
+    GrAssert(NULL != record->fOffscreen0.texture());
     GrDrawTarget::AutoGeometryPush agp(target);
     GrIRect tileRect;
     tileRect.fLeft = boundRect.fLeft + tileX * record->fTileSizeX;
@@ -738,7 +757,7 @@
     GrSamplerState sampler(GrSamplerState::kClamp_WrapMode, 
                            GrSamplerState::kClamp_WrapMode, filter);
 
-    GrTexture* src = record->fEntry0->texture();
+    GrTexture* src = record->fOffscreen0.texture();
     int scale;
 
     enum {
@@ -746,9 +765,9 @@
     };
 
     if (OffscreenRecord::k4x4TwoPass_Downsample == record->fDownsample) {
-        GrAssert(NULL != record->fEntry1);
+        GrAssert(NULL != record->fOffscreen1.texture());
         scale = 2;
-        GrRenderTarget* dst = record->fEntry1->texture()->asRenderTarget();
+        GrRenderTarget* dst = record->fOffscreen1.texture()->asRenderTarget();
         
         // Do 2x2 downsample from first to second
         target->setTexture(kOffscreenStage, src);
@@ -762,7 +781,7 @@
                                      scale * tileRect.height());
         target->drawSimpleRect(rect, NULL, 1 << kOffscreenStage);
         
-        src = record->fEntry1->texture();
+        src = record->fOffscreen1.texture();
     } else if (OffscreenRecord::kFSAA_Downsample == record->fDownsample) {
         scale = 1;
         GrIRect rect = SkIRect::MakeWH(tileRect.width(), tileRect.height());
@@ -811,16 +830,10 @@
 void GrContext::cleanupOffscreenAA(GrDrawTarget* target,
                                    GrPathRenderer* pr,
                                    OffscreenRecord* record) {
-    this->unlockTexture(record->fEntry0);
-    record->fEntry0 = NULL;
     if (pr) {
         // Counterpart of scale() in prepareForOffscreenAA()
         //pr->scaleCurveTolerance(SkScalarInvert(SkIntToScalar(record->fScale)));
     }
-    if (NULL != record->fEntry1) {
-        this->unlockTexture(record->fEntry1);
-        record->fEntry1 = NULL;
-    }
     target->restoreDrawState(record->fSavedState);
 }
 
@@ -1471,9 +1484,8 @@
     const GrTextureDesc desc = {
         kNone_GrTextureFlags, kNone_GrAALevel, width, height, config
     };
-    GrAutoUnlockTextureEntry aute(this,
-                                    this->findApproximateKeylessTexture(desc));
-    GrTexture* texture = aute.texture();
+    GrAutoScratchTexture ast(this, desc);
+    GrTexture* texture = ast.texture();
     if (NULL == texture) {
         return;
     }
@@ -1630,8 +1642,8 @@
     fCustomPathRenderer = GrPathRenderer::CreatePathRenderer();
     fGpu->setClipPathRenderer(fCustomPathRenderer);
 
-    fTextureCache = new GrTextureCache(MAX_TEXTURE_CACHE_COUNT,
-                                       MAX_TEXTURE_CACHE_BYTES);
+    fTextureCache = new GrResourceCache(MAX_TEXTURE_CACHE_COUNT,
+                                        MAX_TEXTURE_CACHE_BYTES);
     fFontCache = new GrFontCache(fGpu);
 
     fLastDrawCategory = kUnbuffered_DrawCategory;
diff --git a/gpu/src/GrGpu.cpp b/gpu/src/GrGpu.cpp
index 007ea31..10307e3 100644
--- a/gpu/src/GrGpu.cpp
+++ b/gpu/src/GrGpu.cpp
@@ -16,7 +16,6 @@
 
 #include "GrGpu.h"
 #include "GrTextStrike.h"
-#include "GrTextureCache.h"
 #include "GrClipIterator.h"
 #include "GrIndexBuffer.h"
 #include "GrVertexBuffer.h"
diff --git a/gpu/src/GrTextureCache.cpp b/gpu/src/GrResourceCache.cpp
similarity index 62%
rename from gpu/src/GrTextureCache.cpp
rename to gpu/src/GrResourceCache.cpp
index c3a61ac..15f9817 100644
--- a/gpu/src/GrTextureCache.cpp
+++ b/gpu/src/GrResourceCache.cpp
@@ -15,33 +15,33 @@
  */
 
 
-#include "GrTextureCache.h"
-#include "GrTexture.h"
+#include "GrResourceCache.h"
+#include "GrResource.h"
 
-GrTextureEntry::GrTextureEntry(const GrTextureKey& key, GrTexture* texture)
-        : fKey(key), fTexture(texture) {
+GrResourceEntry::GrResourceEntry(const GrResourceKey& key, GrResource* resource)
+        : fKey(key), fResource(resource) {
     fLockCount = 0;
     fPrev = fNext = NULL;
 
-    // we assume ownership of the texture, and will unref it when we die
-    GrAssert(texture);
+    // we assume ownership of the resource, and will unref it when we die
+    GrAssert(resource);
 }
 
-GrTextureEntry::~GrTextureEntry() {
-    fTexture->unref();
+GrResourceEntry::~GrResourceEntry() {
+    fResource->unref();
 }
 
 #if GR_DEBUG
-void GrTextureEntry::validate() const {
+void GrResourceEntry::validate() const {
     GrAssert(fLockCount >= 0);
-    GrAssert(fTexture);
-    fTexture->validate();
+    GrAssert(fResource);
+    fResource->validate();
 }
 #endif
 
 ///////////////////////////////////////////////////////////////////////////////
 
-GrTextureCache::GrTextureCache(int maxCount, size_t maxBytes) :
+GrResourceCache::GrResourceCache(int maxCount, size_t maxBytes) :
         fMaxCount(maxCount),
         fMaxBytes(maxBytes) {
     fEntryCount          = 0;
@@ -52,36 +52,36 @@
     fHead = fTail = NULL;
 }
 
-GrTextureCache::~GrTextureCache() {
-    GrAutoTextureCacheValidate atcv(this);
+GrResourceCache::~GrResourceCache() {
+    GrAutoResourceCacheValidate atcv(this);
 
     this->removeAll();
 }
 
-void GrTextureCache::getLimits(int* maxTextures, size_t* maxTextureBytes) const{
-    if (maxTextures) {
-        *maxTextures = fMaxCount;
+void GrResourceCache::getLimits(int* maxResources, size_t* maxResourceBytes) const{
+    if (maxResources) {
+        *maxResources = fMaxCount;
     }
-    if (maxTextureBytes) {
-        *maxTextureBytes = fMaxBytes;
+    if (maxResourceBytes) {
+        *maxResourceBytes = fMaxBytes;
     }
 }
 
-void GrTextureCache::setLimits(int maxTextures, size_t maxTextureBytes) {
-    bool smaller = (maxTextures < fMaxCount) || (maxTextureBytes < fMaxBytes);
+void GrResourceCache::setLimits(int maxResources, size_t maxResourceBytes) {
+    bool smaller = (maxResources < fMaxCount) || (maxResourceBytes < fMaxBytes);
 
-    fMaxCount = maxTextures;
-    fMaxBytes = maxTextureBytes;
+    fMaxCount = maxResources;
+    fMaxBytes = maxResourceBytes;
 
     if (smaller) {
         this->purgeAsNeeded();
     }
 }
 
-void GrTextureCache::internalDetach(GrTextureEntry* entry,
+void GrResourceCache::internalDetach(GrResourceEntry* entry,
                                     bool clientDetach) {
-    GrTextureEntry* prev = entry->fPrev;
-    GrTextureEntry* next = entry->fNext;
+    GrResourceEntry* prev = entry->fPrev;
+    GrResourceEntry* next = entry->fNext;
 
     if (prev) {
         prev->fNext = next;
@@ -97,14 +97,14 @@
     // update our stats
     if (clientDetach) {
         fClientDetachedCount += 1;
-        fClientDetachedBytes += entry->texture()->sizeInBytes();
+        fClientDetachedBytes += entry->resource()->sizeInBytes();
     } else {
         fEntryCount -= 1;
-        fEntryBytes -= entry->texture()->sizeInBytes();
+        fEntryBytes -= entry->resource()->sizeInBytes();
     }
 }
 
-void GrTextureCache::attachToHead(GrTextureEntry* entry,
+void GrResourceCache::attachToHead(GrResourceEntry* entry,
                                   bool clientReattach) {
     entry->fPrev = NULL;
     entry->fNext = fHead;
@@ -119,19 +119,19 @@
     // update our stats
     if (clientReattach) {
         fClientDetachedCount -= 1;
-        fClientDetachedBytes -= entry->texture()->sizeInBytes();
+        fClientDetachedBytes -= entry->resource()->sizeInBytes();
     } else {
         fEntryCount += 1;
-        fEntryBytes += entry->texture()->sizeInBytes();
+        fEntryBytes += entry->resource()->sizeInBytes();
     }
 }
 
-class GrTextureCache::Key {
-    typedef GrTextureEntry T;
+class GrResourceCache::Key {
+    typedef GrResourceEntry T;
 
-    const GrTextureKey& fKey;
+    const GrResourceKey& fKey;
 public:
-    Key(const GrTextureKey& key) : fKey(key) {}
+    Key(const GrResourceKey& key) : fKey(key) {}
 
     uint32_t getHash() const { return fKey.hashIndex(); }
 
@@ -154,10 +154,10 @@
 #endif
 };
 
-GrTextureEntry* GrTextureCache::findAndLock(const GrTextureKey& key) {
-    GrAutoTextureCacheValidate atcv(this);
+GrResourceEntry* GrResourceCache::findAndLock(const GrResourceKey& key) {
+    GrAutoResourceCacheValidate atcv(this);
 
-    GrTextureEntry* entry = fCache.find(key);
+    GrResourceEntry* entry = fCache.find(key);
     if (entry) {
         this->internalDetach(entry, false);
         this->attachToHead(entry, false);
@@ -167,18 +167,18 @@
     return entry;
 }
 
-GrTextureEntry* GrTextureCache::createAndLock(const GrTextureKey& key,
-                                              GrTexture* texture) {
-    GrAutoTextureCacheValidate atcv(this);
+GrResourceEntry* GrResourceCache::createAndLock(const GrResourceKey& key,
+                                              GrResource* resource) {
+    GrAutoResourceCacheValidate atcv(this);
 
-    GrTextureEntry* entry = new GrTextureEntry(key, texture);
+    GrResourceEntry* entry = new GrResourceEntry(key, resource);
 
     this->attachToHead(entry, false);
     fCache.insert(key, entry);
 
 #if GR_DUMP_TEXTURE_UPLOAD
-    GrPrintf("--- add texture to cache %p, count=%d bytes= %d %d\n",
-             entry, fEntryCount, texture->sizeInBytes(), fEntryBytes);
+    GrPrintf("--- add resource to cache %p, count=%d bytes= %d %d\n",
+             entry, fEntryCount, resource->sizeInBytes(), fEntryBytes);
 #endif
 
     // mark the entry as "busy" so it doesn't get purged
@@ -187,19 +187,19 @@
     return entry;
 }
 
-void GrTextureCache::detach(GrTextureEntry* entry) {
+void GrResourceCache::detach(GrResourceEntry* entry) {
     internalDetach(entry, true);
     fCache.remove(entry->fKey, entry);
 }
 
-void GrTextureCache::reattachAndUnlock(GrTextureEntry* entry) {
+void GrResourceCache::reattachAndUnlock(GrResourceEntry* entry) {
     attachToHead(entry, true);
     fCache.insert(entry->key(), entry);
     unlock(entry);
 }
 
-void GrTextureCache::unlock(GrTextureEntry* entry) {
-    GrAutoTextureCacheValidate atcv(this);
+void GrResourceCache::unlock(GrResourceEntry* entry) {
+    GrAutoResourceCacheValidate atcv(this);
 
     GrAssert(entry);
     GrAssert(entry->isLocked());
@@ -209,16 +209,16 @@
     this->purgeAsNeeded();
 }
 
-void GrTextureCache::purgeAsNeeded() {
-    GrAutoTextureCacheValidate atcv(this);
+void GrResourceCache::purgeAsNeeded() {
+    GrAutoResourceCacheValidate atcv(this);
 
-    GrTextureEntry* entry = fTail;
+    GrResourceEntry* entry = fTail;
     while (entry) {
         if (fEntryCount <= fMaxCount && fEntryBytes <= fMaxBytes) {
             break;
         }
 
-        GrTextureEntry* prev = entry->fPrev;
+        GrResourceEntry* prev = entry->fPrev;
         if (!entry->isLocked()) {
             // remove from our cache
             fCache.remove(entry->fKey, entry);
@@ -227,9 +227,9 @@
             this->internalDetach(entry, false);
 
 #if GR_DUMP_TEXTURE_UPLOAD
-            GrPrintf("--- ~texture from cache %p [%d %d]\n", entry->texture(),
-                     entry->texture()->width(),
-                     entry->texture()->height());
+            GrPrintf("--- ~resource from cache %p [%d %d]\n", entry->resource(),
+                     entry->resource()->width(),
+                     entry->resource()->height());
 #endif
             delete entry;
         }
@@ -237,15 +237,15 @@
     }
 }
 
-void GrTextureCache::removeAll() {
+void GrResourceCache::removeAll() {
     GrAssert(!fClientDetachedCount);
     GrAssert(!fClientDetachedBytes);
 
-    GrTextureEntry* entry = fHead;
+    GrResourceEntry* entry = fHead;
     while (entry) {
         GrAssert(!entry->isLocked());
 
-        GrTextureEntry* next = entry->fNext;
+        GrResourceEntry* next = entry->fNext;
         delete entry;
         entry = next;
     }
@@ -259,8 +259,8 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 #if GR_DEBUG
-static int countMatches(const GrTextureEntry* head, const GrTextureEntry* target) {
-    const GrTextureEntry* entry = head;
+static int countMatches(const GrResourceEntry* head, const GrResourceEntry* target) {
+    const GrResourceEntry* entry = head;
     int count = 0;
     while (entry) {
         if (target == entry) {
@@ -277,7 +277,7 @@
 }
 #endif
 
-void GrTextureCache::validate() const {
+void GrResourceCache::validate() const {
     GrAssert(!fHead == !fTail);
     GrAssert(both_zero_or_nonzero(fEntryCount, fEntryBytes));
     GrAssert(both_zero_or_nonzero(fClientDetachedCount, fClientDetachedBytes));
@@ -287,14 +287,14 @@
 
     fCache.validate();
 
-    GrTextureEntry* entry = fHead;
+    GrResourceEntry* entry = fHead;
     int count = 0;
     size_t bytes = 0;
     while (entry) {
         entry->validate();
         GrAssert(fCache.find(entry->key()));
         count += 1;
-        bytes += entry->texture()->sizeInBytes();
+        bytes += entry->resource()->sizeInBytes();
         entry = entry->fNext;
     }
     GrAssert(count == fEntryCount - fClientDetachedCount);
diff --git a/gpu/src/GrResourceCache.h b/gpu/src/GrResourceCache.h
new file mode 100644
index 0000000..73d2924
--- /dev/null
+++ b/gpu/src/GrResourceCache.h
@@ -0,0 +1,307 @@
+/*
+    Copyright 2011 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#ifndef GrResourceCache_DEFINED
+#define GrResourceCache_DEFINED
+
+#include "GrTypes.h"
+#include "GrTHashCache.h"
+
+class GrResource;
+
+// return true if a<b, or false if b<a
+//
+#define RET_IF_LT_OR_GT(a, b)   \
+    do {                        \
+        if ((a) < (b)) {        \
+            return true;        \
+        }                       \
+        if ((b) < (a)) {        \
+            return false;       \
+        }                       \
+    } while (0)
+
+/**
+ *  Helper class for GrResourceCache, the Key is used to identify src data for
+ *  a resource. It is identified by 2 32bit data fields which can hold any
+ *  data (uninterpreted by the cache) and a width/height.
+ */
+class GrResourceKey {
+public:
+    enum {
+        kHashBits   = 7,
+        kHashCount  = 1 << kHashBits,
+        kHashMask   = kHashCount - 1
+    };
+
+    GrResourceKey(uint32_t p0, uint32_t p1, uint32_t p2, uint32_t p3) {
+        fP[0] = p0;
+        fP[1] = p1;
+        fP[2] = p2;
+        fP[3] = p3;
+        this->computeHashIndex();
+    }
+
+    GrResourceKey(uint32_t v[4]) {
+        memcpy(fP, v, 4 * sizeof(uint32_t));
+        this->computeHashIndex();
+    }
+
+    GrResourceKey(const GrResourceKey& src) {
+        memcpy(fP, src.fP, 4 * sizeof(uint32_t));
+#if GR_DEBUG
+        this->computeHashIndex();
+        GrAssert(fHashIndex == src.fHashIndex);
+#endif
+        fHashIndex = src.fHashIndex;
+    }
+
+    //!< returns hash value [0..kHashMask] for the key
+    int hashIndex() const { return fHashIndex; }
+
+    friend bool operator==(const GrResourceKey& a, const GrResourceKey& b) {
+        GR_DEBUGASSERT(-1 != a.fHashIndex && -1 != b.fHashIndex);
+        return 0 == memcmp(a.fP, b.fP, 4 * sizeof(uint32_t));
+    }
+
+    friend bool operator!=(const GrResourceKey& a, const GrResourceKey& b) {
+        GR_DEBUGASSERT(-1 != a.fHashIndex && -1 != b.fHashIndex);
+        return !(a == b);
+    }
+
+    friend bool operator<(const GrResourceKey& a, const GrResourceKey& b) {
+        RET_IF_LT_OR_GT(a.fP[0], b.fP[0]);
+        RET_IF_LT_OR_GT(a.fP[1], b.fP[1]);
+        RET_IF_LT_OR_GT(a.fP[2], b.fP[2]);
+        return a.fP[3] < b.fP[3];
+    }
+
+    uint32_t getValue32(int i) const {
+        GrAssert(i >=0 && i < 4);
+        return fP[i];
+    }
+private:
+
+    static uint32_t rol(uint32_t x) {
+        return (x >> 24) | (x << 8);
+    }
+    static uint32_t ror(uint32_t x) {
+        return (x >> 8) | (x << 24);
+    }
+    static uint32_t rohalf(uint32_t x) {
+        return (x >> 16) | (x << 16);
+    }
+
+    void computeHashIndex() {
+        uint32_t hash = fP[0] ^ rol(fP[1]) ^ ror(fP[2]) ^ rohalf(fP[3]);
+        // this way to mix and reduce hash to its index may have to change
+        // depending on how many bits we allocate to the index
+        hash ^= hash >> 16;
+        hash ^= hash >> 8;
+        fHashIndex = hash & kHashMask;
+    }
+
+    uint32_t    fP[4];
+
+    // this is computed from the fP... fields
+    int         fHashIndex;
+
+    friend class GrContext;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class GrResourceEntry {
+public:
+    GrResource* resource() const { return fResource; }
+    const GrResourceKey& key() const { return fKey; }
+
+#if GR_DEBUG
+    GrResourceEntry* next() const { return fNext; }
+    GrResourceEntry* prev() const { return fPrev; }
+#endif
+
+#if GR_DEBUG
+    void validate() const;
+#else
+    void validate() const {}
+#endif
+
+private:
+    GrResourceEntry(const GrResourceKey& key, GrResource* resource);
+    ~GrResourceEntry();
+
+    bool isLocked() const { return fLockCount != 0; }
+    void lock() { ++fLockCount; }
+    void unlock() {
+        GrAssert(fLockCount > 0);
+        --fLockCount;
+    }
+
+    GrResourceKey    fKey;
+    GrResource*      fResource;
+
+    // track if we're in use, used when we need to purge
+    // we only purge unlocked entries
+    int fLockCount;
+
+    // we're a dlinklist
+    GrResourceEntry* fPrev;
+    GrResourceEntry* fNext;
+
+    friend class GrResourceCache;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "GrTHashCache.h"
+
+/**
+ *  Cache of GrResource objects.
+ *
+ *  These have a corresponding GrResourceKey, built from 128bits identifying the
+ *  resource.
+ *
+ *  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 sorted array (based on the GrResourceKey)
+ *  which we can bsearch. When a new entry is added, it is inserted into this
+ *  array.
+ *
+ *  For even faster searches, a hash is computed from the Key. If there is
+ *  a collision between two keys with the same hash, we fall back on the
+ *  bsearch, and update the hash to reflect the most recent Key requested.
+ */
+class GrResourceCache {
+public:
+    GrResourceCache(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 maxResource, size_t maxResourceBytes);
+
+    /**
+     *  Search for an entry with the same Key. If found, "lock" it and return it.
+     *  If not found, return null.
+     */
+    GrResourceEntry* findAndLock(const GrResourceKey&);
+
+    /**
+     *  Create a new entry, based on the specified key and resource, and return
+     *  its "locked" entry.
+     *
+     *  Ownership of the resource is transferred to the Entry, which will unref()
+     *  it when we are purged or deleted.
+     */
+    GrResourceEntry* createAndLock(const GrResourceKey&, GrResource*);
+
+    /**
+     * Detach removes an entry from the cache. This prevents the entry from
+     * being found by a subsequent findAndLock() until it is reattached. The
+     * entry still counts against the cache's budget and should be reattached
+     * when exclusive access is no longer needed.
+     */
+    void detach(GrResourceEntry*);
+
+    /**
+     * Reattaches a resource to the cache and unlocks it. Allows it to be found
+     * by a subsequent findAndLock or be purged (provided its lock count is
+     * now 0.)
+     */
+    void reattachAndUnlock(GrResourceEntry*);
+
+    /**
+     *  When done with an entry, call unlock(entry) on it, which returns it to
+     *  a purgable state.
+     */
+    void unlock(GrResourceEntry*);
+
+    void removeAll();
+
+#if GR_DEBUG
+    void validate() const;
+#else
+    void validate() const {}
+#endif
+
+private:
+    void internalDetach(GrResourceEntry*, bool);
+    void attachToHead(GrResourceEntry*, bool);
+    void purgeAsNeeded();   // uses kFreeResource_DeleteMode
+
+    class Key;
+    GrTHashTable<GrResourceEntry, Key, 8> fCache;
+
+    // manage the dlink list
+    GrResourceEntry* fHead;
+    GrResourceEntry* fTail;
+
+    // our budget, used in purgeAsNeeded()
+    int fMaxCount;
+    size_t fMaxBytes;
+
+    // our current stats, related to our budget
+    int fEntryCount;
+    size_t fEntryBytes;
+    int fClientDetachedCount;
+    size_t fClientDetachedBytes;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+#if GR_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/gyp/gpu.gyp b/gyp/gpu.gyp
index 04231c1..1b5e138 100644
--- a/gyp/gpu.gyp
+++ b/gyp/gpu.gyp
@@ -132,7 +132,6 @@
         '../gpu/include/GrTextContext.h',
         '../gpu/include/GrTextStrike.h',
         '../gpu/include/GrTexture.h',
-        '../gpu/include/GrTextureCache.h',
         '../gpu/include/GrTHashCache.h',
         '../gpu/include/GrTLList.h',
         '../gpu/include/GrTypes.h',
@@ -173,13 +172,14 @@
         '../gpu/src/GrRectanizer.cpp',
         '../gpu/src/GrRedBlackTree.h',
         '../gpu/src/GrResource.cpp',
+        '../gpu/src/GrResourceCache.cpp',
+        '../gpu/src/GrResourceCache.h',
         '../gpu/src/GrStencil.cpp',
         '../gpu/src/GrTesselatedPathRenderer.cpp',
         '../gpu/src/GrTextContext.cpp',
         '../gpu/src/GrTextStrike.cpp',
         '../gpu/src/GrTextStrike_impl.h',
         '../gpu/src/GrTexture.cpp',
-        '../gpu/src/GrTextureCache.cpp',
         '../gpu/src/gr_unittests.cpp',
 
         '../gpu/src/mac/GrGLDefaultInterface_mac.cpp',
diff --git a/include/gpu/SkGpuDevice.h b/include/gpu/SkGpuDevice.h
index bddf24e..b4ab185 100644
--- a/include/gpu/SkGpuDevice.h
+++ b/include/gpu/SkGpuDevice.h
@@ -22,6 +22,7 @@
 #include "SkBitmap.h"
 #include "SkDevice.h"
 #include "SkRegion.h"
+#include "GrContext.h"
 
 struct SkDrawProcs;
 struct GrSkDrawProcs;
@@ -126,18 +127,16 @@
     virtual void makeRenderTargetCurrent();
 
 protected:
-
-    class TexCache;
+    typedef GrContext::TextureCacheEntry TexCache;
     enum TexType {
         kBitmap_TexType,
         kDeviceRenderTarget_TexType,
         kSaveLayerDeviceRenderTarget_TexType
     };
-    TexCache* lockCachedTexture(const SkBitmap& bitmap,
-                                const GrSamplerState& sampler,
-                                GrTexture** texture,
-                                TexType type = kBitmap_TexType);
-    void unlockCachedTexture(TexCache*);
+    TexCache lockCachedTexture(const SkBitmap& bitmap,
+                               const GrSamplerState& sampler,
+                               TexType type = kBitmap_TexType);
+    void unlockCachedTexture(TexCache);
 
     class SkAutoCachedTexture {
     public:
@@ -152,7 +151,7 @@
 
     private:
         SkGpuDevice*    fDevice;
-        TexCache*       fTex;
+        TexCache        fTex;
     };
     friend class SkAutoTexCache;
 
@@ -162,11 +161,11 @@
     GrSkDrawProcs*  fDrawProcs;
 
     // state for our offscreen render-target
-    TexCache*       fCache;
-    GrTexture*      fTexture;
-    GrRenderTarget* fRenderTarget;
-    bool            fNeedClear;
-    bool            fNeedPrepareRenderTarget;
+    TexCache            fCache;
+    GrTexture*          fTexture;
+    GrRenderTarget*     fRenderTarget;
+    bool                fNeedClear;
+    bool                fNeedPrepareRenderTarget;
 
     // called from rt and tex cons
     void initFromRenderTarget(GrContext*, GrRenderTarget*);
diff --git a/include/gpu/SkGr.h b/include/gpu/SkGr.h
index 65565c9..e2656ae 100644
--- a/include/gpu/SkGr.h
+++ b/include/gpu/SkGr.h
@@ -231,10 +231,11 @@
 ////////////////////////////////////////////////////////////////////////////////
 // Helper functions
 
-GrTextureEntry* sk_gr_create_bitmap_texture(GrContext* ctx,
-                                            GrTextureKey* key,
-                                            const GrSamplerState& sampler,
-                                            const SkBitmap& bitmap);
+static const GrContext::TextureKey gUNCACHED_KEY = ~0;
+GrContext::TextureCacheEntry sk_gr_create_bitmap_texture(GrContext* ctx,
+                                                GrContext::TextureKey key,
+                                                const GrSamplerState& sampler,
+                                                const SkBitmap& bitmap);
 
 
 #endif
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index 45dac33..790767e 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -67,34 +67,33 @@
                                  const GrSamplerState& sampler,
                                  GrTexture** texture) {
     GrAssert(texture);
-    fTex = NULL;
     *texture = this->set(device, bitmap, sampler);
 }
 
 SkGpuDevice::SkAutoCachedTexture::SkAutoCachedTexture() {
-    fTex = NULL;
 }
 
 GrTexture* SkGpuDevice::SkAutoCachedTexture::set(SkGpuDevice* device,
                                                  const SkBitmap& bitmap,
                                                  const GrSamplerState& sampler) {
-    if (fTex) {
+    if (fTex.texture()) {
         fDevice->unlockCachedTexture(fTex);
     }
     fDevice = device;
     GrTexture* texture = (GrTexture*)bitmap.getTexture();
     if (texture) {
         // return the native texture
-        fTex = NULL;
+        fTex.reset();
     } else {
         // look it up in our cache
-        fTex = device->lockCachedTexture(bitmap, sampler, &texture);
+        fTex = device->lockCachedTexture(bitmap, sampler);
+        texture = fTex.texture();
     }
     return texture;
 }
 
 SkGpuDevice::SkAutoCachedTexture::~SkAutoCachedTexture() {
-    if (fTex) {
+    if (fTex.texture()) {
         fDevice->unlockCachedTexture(fTex);
     }
 }
@@ -170,7 +169,6 @@
     fContext = context;
     fContext->ref();
     
-    fCache = NULL;
     fTexture = NULL;
     fRenderTarget = NULL;
     fNeedClear = false;
@@ -199,7 +197,6 @@
     fContext = context;
     fContext->ref();
 
-    fCache = NULL;
     fTexture = NULL;
     fRenderTarget = NULL;
     fNeedClear = false;
@@ -214,10 +211,9 @@
     TexType type = (kSaveLayer_Usage == usage) ? 
                             kSaveLayerDeviceRenderTarget_TexType :
                             kDeviceRenderTarget_TexType;
-    fCache = this->lockCachedTexture(bm, GrSamplerState::ClampNoFilter(),
-                                     &fTexture, type);
-    if (fCache) {
-        SkASSERT(NULL != fTexture);
+    fCache = this->lockCachedTexture(bm, GrSamplerState::ClampNoFilter(), type);
+    fTexture = fCache.texture();
+    if (fTexture) {
         SkASSERT(NULL != fTexture->asRenderTarget());
         // hold a ref directly on fTexture (even though fCache has one) to match
         // other constructor paths. Simplifies cleanup.
@@ -260,10 +256,10 @@
 
     SkSafeUnref(fTexture);
     SkSafeUnref(fRenderTarget);
-    if (fCache) {
+    if (fCache.texture()) {
         GrAssert(NULL != fTexture);
         GrAssert(fRenderTarget == fTexture->asRenderTarget());
-        fContext->unlockTexture((GrTextureEntry*)fCache);
+        fContext->unlockTexture(fCache);
     } 
     fContext->unref();
 }
@@ -895,15 +891,13 @@
         kRGBA_8888_GrPixelConfig
     };
 
-    GrTextureEntry* srcEntry = context->findApproximateKeylessTexture(desc);
-    GrTextureEntry* dstEntry = context->findApproximateKeylessTexture(desc);
-    GrAutoUnlockTextureEntry srcLock(context, srcEntry),
-                             dstLock(context, dstEntry);
-    if (NULL == srcEntry || NULL == dstEntry) {
+    GrAutoScratchTexture srcEntry(context, desc);
+    GrAutoScratchTexture dstEntry(context, desc);
+    if (NULL == srcEntry.texture() || NULL == dstEntry.texture()) {
         return false;
     }
-    GrTexture* srcTexture = srcEntry->texture();
-    GrTexture* dstTexture = dstEntry->texture();
+    GrTexture* srcTexture = srcEntry.texture();
+    GrTexture* dstTexture = dstEntry.texture();
     if (NULL == srcTexture || NULL == dstTexture) {
         return false;
     }
@@ -939,18 +933,18 @@
     paint.reset();
     paint.getTextureSampler(0)->setFilter(GrSamplerState::kBilinear_Filter);
     paint.getTextureSampler(0)->setMatrix(sampleM);
-    GrTextureEntry* origEntry = NULL;
+    GrAutoScratchTexture origEntry;
+
     if (blurType != SkMaskFilter::kNormal_BlurType) {
         // Stash away a copy of the unblurred image.
-        origEntry = context->findApproximateKeylessTexture(desc);
-        if (NULL == origEntry) {
+        origEntry.set(context, desc);
+        if (NULL == origEntry.texture()) {
             return false;
         }
-        context->setRenderTarget(origEntry->texture()->asRenderTarget());
+        context->setRenderTarget(origEntry.texture()->asRenderTarget());
         paint.setTexture(0, srcTexture);
         context->drawRect(paint, srcRect);
     }
-    GrAutoUnlockTextureEntry origLock(context, origEntry);
     for (int i = 1; i < scaleFactor; i *= 2) {
         sampleM.setIDiv(srcTexture->width(), srcTexture->height());
         paint.getTextureSampler(0)->setMatrix(sampleM);
@@ -1011,7 +1005,7 @@
     }
 
     if (blurType != SkMaskFilter::kNormal_BlurType) {
-        GrTexture* origTexture = origEntry->texture();
+        GrTexture* origTexture = origEntry.texture();
         paint.getTextureSampler(0)->setFilter(GrSamplerState::kNearest_Filter);
         sampleM.setIDiv(origTexture->width(), origTexture->height());
         paint.getTextureSampler(0)->setMatrix(sampleM);
@@ -1102,9 +1096,8 @@
         kAlpha_8_GrPixelConfig
     };
 
-    GrAutoUnlockTextureEntry aute(context,
-                                  context->findApproximateKeylessTexture(desc));
-    GrTexture* texture = aute.texture();
+    GrAutoScratchTexture ast(context, desc);
+    GrTexture* texture = ast.texture();
 
     if (NULL == texture) {
         return false;
@@ -1723,12 +1716,10 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-SkGpuDevice::TexCache* SkGpuDevice::lockCachedTexture(const SkBitmap& bitmap,
-                                                      const GrSamplerState& sampler,
-                                                      GrTexture** texture,
-                                                      TexType type) {
-    GrTexture* newTexture = NULL;
-    GrTextureEntry* entry = NULL;
+SkGpuDevice::TexCache SkGpuDevice::lockCachedTexture(const SkBitmap& bitmap,
+                                            const GrSamplerState& sampler,
+                                            TexType type) {
+    GrContext::TextureCacheEntry entry;
     GrContext* ctx = this->context();
 
     if (kBitmap_TexType != type) {
@@ -1739,46 +1730,41 @@
             bitmap.height(),
             SkGr::Bitmap2PixelConfig(bitmap)
         };
+        GrContext::ScratchTexMatch match;
         if (kSaveLayerDeviceRenderTarget_TexType == type) {
             // we know layers will only be drawn through drawDevice.
             // drawDevice has been made to work with content embedded in a
             // larger texture so its okay to use the approximate version.
-            entry = ctx->findApproximateKeylessTexture(desc);
+            match = GrContext::kApprox_ScratchTexMatch;
         } else {
             SkASSERT(kDeviceRenderTarget_TexType == type);
-            entry = ctx->lockKeylessTexture(desc);
+            match = GrContext::kExact_ScratchTexMatch;
         }
+        entry = ctx->lockScratchTexture(desc, match);
     } else {
         if (!bitmap.isVolatile()) {
-            uint32_t p0, p1;
-            p0 = bitmap.getGenerationID();
-            p1 = bitmap.pixelRefOffset();
-            GrTextureKey key(p0, p1, bitmap.width(), bitmap.height());
+            GrContext::TextureKey key = bitmap.getGenerationID();
+            key |= ((uint64_t) bitmap.pixelRefOffset()) << 32;
         
-            entry = ctx->findAndLockTexture(&key, sampler);
-            if (NULL == entry)
-                entry = sk_gr_create_bitmap_texture(ctx, &key, sampler, 
+            entry = ctx->findAndLockTexture(key, bitmap.width(),
+                                            bitmap.height(), sampler);
+            if (NULL == entry.texture()) {
+                entry = sk_gr_create_bitmap_texture(ctx, key, sampler, 
                                                     bitmap);
+            }
         } else {
-            entry = sk_gr_create_bitmap_texture(ctx, NULL, sampler, bitmap);
+            entry = sk_gr_create_bitmap_texture(ctx, gUNCACHED_KEY, sampler, bitmap);
         }
-        if (NULL == entry) {
+        if (NULL == entry.texture()) {
             GrPrintf("---- failed to create texture for cache [%d %d]\n",
                      bitmap.width(), bitmap.height());
         }
     }
-
-    if (NULL != entry) {
-        newTexture = entry->texture();
-        if (texture) {
-            *texture = newTexture;
-        }
-    }
-    return (TexCache*)entry;
+    return entry;
 }
 
-void SkGpuDevice::unlockCachedTexture(TexCache* cache) {
-    this->context()->unlockTexture((GrTextureEntry*)cache);
+void SkGpuDevice::unlockCachedTexture(TexCache cache) {
+    this->context()->unlockTexture(cache);
 }
 
 SkDevice* SkGpuDevice::onCreateCompatibleDevice(SkBitmap::Config config, 
diff --git a/src/gpu/SkGr.cpp b/src/gpu/SkGr.cpp
index 4f1e6fd..49b17e7 100644
--- a/src/gpu/SkGr.cpp
+++ b/src/gpu/SkGr.cpp
@@ -63,13 +63,15 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
-GrTextureEntry* sk_gr_create_bitmap_texture(GrContext* ctx,
-                                            GrTextureKey* key,
-                                            const GrSamplerState& sampler,
-                                            const SkBitmap& origBitmap) {
+GrContext::TextureCacheEntry sk_gr_create_bitmap_texture(GrContext* ctx,
+                                                GrContext::TextureKey key,
+                                                const GrSamplerState& sampler,
+                                                const SkBitmap& origBitmap) {
     SkAutoLockPixels alp(origBitmap);
+    GrContext::TextureCacheEntry entry;
+
     if (!origBitmap.readyToDraw()) {
-        return NULL;
+        return entry;
     }
 
     SkBitmap tmpBitmap;
@@ -98,12 +100,13 @@
             // our compressed data will be trimmed, so pass width() for its
             // "rowBytes", since they are the same now.
             
-            if (NULL != key) {
+            if (gUNCACHED_KEY != key) {
                 return ctx->createAndLockTexture(key, sampler, desc, storage.get(),
                                                  bitmap->width());
             } else {
-                GrTextureEntry* entry = ctx->lockKeylessTexture(desc);
-                entry->texture()->uploadTextureData(0, 0, bitmap->width(), 
+                entry = ctx->lockScratchTexture(desc,
+                                        GrContext::kExact_ScratchTexMatch);
+                entry.texture()->uploadTextureData(0, 0, bitmap->width(), 
                     bitmap->height(), storage.get(), 0);
                 return entry;
             }
@@ -116,12 +119,14 @@
     }
 
     desc.fFormat = SkGr::Bitmap2PixelConfig(*bitmap);
-    if (NULL != key) {
-        return ctx->createAndLockTexture(key, sampler, desc, 
-            bitmap->getPixels(), bitmap->rowBytes());
+    if (gUNCACHED_KEY != key) {
+        return ctx->createAndLockTexture(key, sampler, desc,
+                                         bitmap->getPixels(),
+                                         bitmap->rowBytes());
     } else {
-        GrTextureEntry* entry = ctx->lockKeylessTexture(desc);
-        entry->texture()->uploadTextureData(0, 0, bitmap->width(), 
+        entry = ctx->lockScratchTexture(desc,
+                                        GrContext::kExact_ScratchTexMatch);
+        entry.texture()->uploadTextureData(0, 0, bitmap->width(), 
             bitmap->height(), bitmap->getPixels(), bitmap->rowBytes());
         return entry;
     }