| Index: src/gpu/GrContext.cpp
 | 
| diff --git a/src/gpu/GrContext.cpp b/src/gpu/GrContext.cpp
 | 
| index d0f3cc56713226f7f432f6dbd4a90c19d6e94526..a722eed88ac5cac8a663adbe9f7fc14ac1d6315d 100755
 | 
| --- a/src/gpu/GrContext.cpp
 | 
| +++ b/src/gpu/GrContext.cpp
 | 
| @@ -70,25 +70,6 @@ static const int DRAW_BUFFER_IBPOOL_PREALLOC_BUFFERS = 4;
 | 
|  
 | 
|  #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 @@ bool GrContext::init(GrBackend backend, GrBackendContext backendContext) {
 | 
|      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 @@ GrTexture* GrContext::createTexture(const GrTextureParams* params,
 | 
|      }
 | 
|  
 | 
|      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 @@ GrTexture* GrContext::createTexture(const GrTextureParams* params,
 | 
|      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);
 | 
| -    }
 | 
| -
 | 
| -    GrTextureDesc desc = inDesc;
 | 
| +    SkTCopyOnFirstWrite<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);
 | 
| -
 | 
| -    if (NULL == resource) {
 | 
| -        desc.fFlags = inDesc.fFlags;
 | 
| -        desc.fWidth = origWidth;
 | 
| -        desc.fHeight = origHeight;
 | 
| -        resource = create_scratch_texture(fGpu, fResourceCache, desc);
 | 
| -    }
 | 
| -
 | 
| -    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());
 | 
| +        do {
 | 
| +            GrResourceKey key = GrTexturePriv::ComputeScratchKey(*desc);
 | 
| +            GrGpuResource* resource = fResourceCache2->findAndRefScratchResource(key,
 | 
| +                                                                                 calledDuringFlush);
 | 
| +            if (resource) {
 | 
| +                fResourceCache->makeResourceMRU(resource);
 | 
| +                return static_cast<GrTexture*>(resource);
 | 
| +            }
 | 
|  
 | 
| -    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());
 | 
| -    }
 | 
| -}
 | 
| +            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;
 | 
| +            }
 | 
|  
 | 
| -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;
 | 
| -    }
 | 
| +        } while (true);
 | 
|  
 | 
| -    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();
 | 
| -        }
 | 
| +        desc.writable()->fFlags = origFlags;
 | 
|      }
 | 
| -}
 | 
|  
 | 
| -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 @@ void GrContext::flush(int flagsBitfield) {
 | 
|      } else {
 | 
|          fDrawBuffer->flush();
 | 
|      }
 | 
| +    fResourceCache->purgeAsNeeded();
 | 
|      fFlushToReduceCacheSize = false;
 | 
|  }
 | 
|  
 | 
| @@ -1941,7 +1835,6 @@ const GrFragmentProcessor* GrContext::createUPMToPMEffect(GrTexture* texture,
 | 
|  }
 | 
|  
 | 
|  void GrContext::addResourceToCache(const GrResourceKey& resourceKey, GrGpuResource* resource) {
 | 
| -    fResourceCache->purgeAsNeeded(1, resource->gpuMemorySize());
 | 
|      fResourceCache->addResource(resourceKey, resource);
 | 
|  }
 | 
|  
 | 
| 
 |