GrResourceCache2 manages scratch texture.

BUG=skia:2889

Review URL: https://codereview.chromium.org/608883003
diff --git a/src/gpu/GrContext.cpp b/src/gpu/GrContext.cpp
index d0f3cc5..a722eed 100755
--- a/src/gpu/GrContext.cpp
+++ b/src/gpu/GrContext.cpp
@@ -70,25 +70,6 @@
 
 #define ASSERT_OWNED_RESOURCE(R) SkASSERT(!(R) || (R)->getContext() == this)
 
-GrTexture* GrAutoScratchTexture::detach() {
-    if (NULL == fTexture) {
-        return NULL;
-    }
-    GrTexture* texture = fTexture;
-    fTexture = NULL;
-
-    // This GrAutoScratchTexture has a ref from lockAndRefScratchTexture, which we give up now.
-    // The cache also has a ref which we are lending to the caller of detach(). When the caller
-    // lets go of the ref and the ref count goes to 0 internal_dispose will see this flag is
-    // set and re-ref the texture, thereby restoring the cache's ref.
-    SkASSERT(!texture->unique());
-    texture->texturePriv().setFlag((GrTextureFlags) GrTexture::kReturnToCache_FlagBit);
-    texture->unref();
-    SkASSERT(texture->getCacheEntry());
-
-    return texture;
-}
-
 // Glorified typedef to avoid including GrDrawState.h in GrContext.h
 class GrContext::AutoRestoreEffects : public GrDrawState::AutoRestoreEffects {};
 
@@ -153,7 +134,8 @@
     fDrawState = SkNEW(GrDrawState);
     fGpu->setDrawState(fDrawState);
 
-    fResourceCache = SkNEW_ARGS(GrResourceCache, (MAX_RESOURCE_CACHE_COUNT,
+    fResourceCache = SkNEW_ARGS(GrResourceCache, (fGpu->caps(),
+                                                  MAX_RESOURCE_CACHE_COUNT,
                                                   MAX_RESOURCE_CACHE_BYTES));
     fResourceCache->setOverbudgetCallback(OverbudgetCB, this);
     fResourceCache2 = SkNEW(GrResourceCache2);
@@ -446,9 +428,6 @@
     }
 
     if (texture) {
-        // Adding a resource could put us overbudget. Try to free up the
-        // necessary space before adding it.
-        fResourceCache->purgeAsNeeded(1, texture->gpuMemorySize());
         fResourceCache->addResource(resourceKey, texture);
 
         if (cacheKey) {
@@ -459,157 +438,71 @@
     return texture;
 }
 
-static GrTexture* create_scratch_texture(GrGpu* gpu,
-                                         GrResourceCache* resourceCache,
-                                         const GrTextureDesc& desc) {
-    GrTexture* texture = gpu->createTexture(desc, NULL, 0);
-    if (texture) {
-        GrResourceKey key = GrTexturePriv::ComputeScratchKey(texture->desc());
-        // Adding a resource could put us overbudget. Try to free up the
-        // necessary space before adding it.
-        resourceCache->purgeAsNeeded(1, texture->gpuMemorySize());
-        // Make the resource exclusive so future 'find' calls don't return it
-        resourceCache->addResource(key, texture, GrResourceCache::kHide_OwnershipFlag);
+GrTexture* GrContext::createNewScratchTexture(const GrTextureDesc& desc) {
+    GrTexture* texture = fGpu->createTexture(desc, NULL, 0);
+    if (!texture) {
+        return NULL;
     }
+    fResourceCache->addResource(texture->getScratchKey(), texture);
+    texture->fIsScratch = GrGpuResource::kYes_IsScratch;
     return texture;
 }
 
-GrTexture* GrContext::lockAndRefScratchTexture(const GrTextureDesc& inDesc, ScratchTexMatch match) {
+GrTexture* GrContext::lockAndRefScratchTexture(const GrTextureDesc& inDesc, ScratchTexMatch match,
+                                               bool calledDuringFlush) {
 
+    // kNoStencil has no meaning if kRT isn't set.
     SkASSERT((inDesc.fFlags & kRenderTarget_GrTextureFlagBit) ||
              !(inDesc.fFlags & kNoStencil_GrTextureFlagBit));
 
-    // Renderable A8 targets are not universally supported (e.g., not on ANGLE)
-    SkASSERT(this->isConfigRenderable(kAlpha_8_GrPixelConfig, inDesc.fSampleCnt > 0) ||
-             !(inDesc.fFlags & kRenderTarget_GrTextureFlagBit) ||
-             (inDesc.fConfig != kAlpha_8_GrPixelConfig));
+    // Make sure caller has checked for renderability if kRT is set.
+    SkASSERT(!(inDesc.fFlags & kRenderTarget_GrTextureFlagBit) ||
+             this->isConfigRenderable(inDesc.fConfig, inDesc.fSampleCnt > 0));
 
-    if (!fGpu->caps()->reuseScratchTextures() &&
-        !(inDesc.fFlags & kRenderTarget_GrTextureFlagBit)) {
-        // If we're never recycling this texture we can always make it the right size
-        return create_scratch_texture(fGpu, fResourceCache, inDesc);
-    }
+    SkTCopyOnFirstWrite<GrTextureDesc> desc(inDesc);
 
-    GrTextureDesc desc = inDesc;
-
-    if (kApprox_ScratchTexMatch == match) {
-        // bin by pow2 with a reasonable min
-        static const int MIN_SIZE = 16;
-        desc.fWidth  = SkTMax(MIN_SIZE, GrNextPow2(desc.fWidth));
-        desc.fHeight = SkTMax(MIN_SIZE, GrNextPow2(desc.fHeight));
-    }
-
-    GrGpuResource* resource = NULL;
-    int origWidth = desc.fWidth;
-    int origHeight = desc.fHeight;
-
-    do {
-        GrResourceKey key = GrTexturePriv::ComputeScratchKey(desc);
-        // Ensure we have exclusive access to the texture so future 'find' calls don't return it
-        resource = fResourceCache->find(key, GrResourceCache::kHide_OwnershipFlag);
-        if (resource) {
-            resource->ref();
-            break;
-        }
-        if (kExact_ScratchTexMatch == match) {
-            break;
-        }
-        // We had a cache miss and we are in approx mode, relax the fit of the flags.
-
-        // We no longer try to reuse textures that were previously used as render targets in
-        // situations where no RT is needed; doing otherwise can confuse the video driver and
-        // cause significant performance problems in some cases.
-        if (desc.fFlags & kNoStencil_GrTextureFlagBit) {
-            desc.fFlags = desc.fFlags & ~kNoStencil_GrTextureFlagBit;
-        } else {
-            break;
+    if (fGpu->caps()->reuseScratchTextures() || (desc->fFlags & kRenderTarget_GrTextureFlagBit)) {
+        GrTextureFlags origFlags = desc->fFlags;
+        if (kApprox_ScratchTexMatch == match) {
+            // bin by pow2 with a reasonable min
+            static const int MIN_SIZE = 16;
+            GrTextureDesc* wdesc = desc.writable();
+            wdesc->fWidth  = SkTMax(MIN_SIZE, GrNextPow2(desc->fWidth));
+            wdesc->fHeight = SkTMax(MIN_SIZE, GrNextPow2(desc->fHeight));
         }
 
-    } while (true);
+        do {
+            GrResourceKey key = GrTexturePriv::ComputeScratchKey(*desc);
+            GrGpuResource* resource = fResourceCache2->findAndRefScratchResource(key,
+                                                                                 calledDuringFlush);
+            if (resource) {
+                fResourceCache->makeResourceMRU(resource);
+                return static_cast<GrTexture*>(resource);
+            }
 
-    if (NULL == resource) {
-        desc.fFlags = inDesc.fFlags;
-        desc.fWidth = origWidth;
-        desc.fHeight = origHeight;
-        resource = create_scratch_texture(fGpu, fResourceCache, desc);
+            if (kExact_ScratchTexMatch == match) {
+                break;
+            }
+            // We had a cache miss and we are in approx mode, relax the fit of the flags.
+
+            // We no longer try to reuse textures that were previously used as render targets in
+            // situations where no RT is needed; doing otherwise can confuse the video driver and
+            // cause significant performance problems in some cases.
+            if (desc->fFlags & kNoStencil_GrTextureFlagBit) {
+                desc.writable()->fFlags = desc->fFlags & ~kNoStencil_GrTextureFlagBit;
+            } else {
+                break;
+            }
+
+        } while (true);
+
+        desc.writable()->fFlags = origFlags;
     }
 
-    return static_cast<GrTexture*>(resource);
-}
-
-void GrContext::addExistingTextureToCache(GrTexture* texture) {
-
-    if (NULL == texture) {
-        return;
-    }
-
-    // This texture should already have a cache entry since it was once
-    // attached
-    SkASSERT(texture->getCacheEntry());
-
-    // Conceptually, the cache entry is going to assume responsibility
-    // for the creation ref. Assert refcnt == 1.
-    // Except that this also gets called when the texture is prematurely
-    // abandoned. In that case the ref count may be > 1.
-    // SkASSERT(texture->unique());
-
-    if (fGpu->caps()->reuseScratchTextures() || texture->asRenderTarget()) {
-        // Since this texture came from an AutoScratchTexture it should
-        // still be in the exclusive pile. Recycle it.
-        fResourceCache->makeNonExclusive(texture->getCacheEntry());
-        this->purgeCache();
-    } else {
-        // When we aren't reusing textures we know this scratch texture
-        // will never be reused and would be just wasting time in the cache
-        fResourceCache->makeNonExclusive(texture->getCacheEntry());
-        fResourceCache->deleteResource(texture->getCacheEntry());
-    }
-}
-
-void GrContext::unlockScratchTexture(GrTexture* texture) {
-    if (texture->wasDestroyed()) {
-        if (texture->getCacheEntry()->key().isScratch()) {
-            // This texture was detached from the cache but the cache still had a ref to it but
-            // not a pointer to it. This will unref the texture and delete its resource cache
-            // entry.
-            delete texture->getCacheEntry();
-        }
-        return;
-    }
-
-    ASSERT_OWNED_RESOURCE(texture);
-    SkASSERT(texture->getCacheEntry());
-
-    // 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 (texture->getCacheEntry()->key().isScratch()) {
-        if (fGpu->caps()->reuseScratchTextures() || texture->asRenderTarget()) {
-            fResourceCache->makeNonExclusive(texture->getCacheEntry());
-            this->purgeCache();
-        } else if (texture->unique()) {
-            // Only the cache now knows about this texture. Since we're never
-            // reusing scratch textures (in this code path) it would just be
-            // wasting time sitting in the cache.
-            fResourceCache->makeNonExclusive(texture->getCacheEntry());
-            fResourceCache->deleteResource(texture->getCacheEntry());
-        } else {
-            // In this case (there is still a non-cache ref) but we don't really
-            // want to readd it to the cache (since it will never be reused).
-            // Instead, give up the cache's ref and leave the decision up to
-            // addExistingTextureToCache once its ref count reaches 0. For
-            // this to work we need to leave it in the exclusive list.
-            texture->texturePriv().setFlag((GrTextureFlags) GrTexture::kReturnToCache_FlagBit);
-            // Give up the cache's ref to the texture
-            texture->unref();
-        }
-    }
-}
-
-void GrContext::purgeCache() {
-    if (fResourceCache) {
-        fResourceCache->purgeAsNeeded();
-    }
+    GrTexture* texture = this->createNewScratchTexture(*desc);
+    SkASSERT(NULL == texture || 
+             texture->getScratchKey() == GrTexturePriv::ComputeScratchKey(*desc));
+    return texture;
 }
 
 bool GrContext::OverbudgetCB(void* data) {
@@ -1349,6 +1242,7 @@
     } else {
         fDrawBuffer->flush();
     }
+    fResourceCache->purgeAsNeeded();
     fFlushToReduceCacheSize = false;
 }
 
@@ -1941,7 +1835,6 @@
 }
 
 void GrContext::addResourceToCache(const GrResourceKey& resourceKey, GrGpuResource* resource) {
-    fResourceCache->purgeAsNeeded(1, resource->gpuMemorySize());
     fResourceCache->addResource(resourceKey, resource);
 }