diff options
-rw-r--r-- | bench/GrResourceCacheBench.cpp | 32 | ||||
-rw-r--r-- | gyp/gpu.gyp | 10 | ||||
-rw-r--r-- | gyp/gpu.gypi | 2 | ||||
-rw-r--r-- | include/gpu/GrConfig.h | 18 | ||||
-rw-r--r-- | include/gpu/GrContext.h | 10 | ||||
-rw-r--r-- | include/gpu/GrGpuResource.h | 4 | ||||
-rw-r--r-- | include/gpu/GrUserConfig.h | 12 | ||||
-rw-r--r-- | include/gpu/SkGr.h | 5 | ||||
-rwxr-xr-x | src/gpu/GrAADistanceFieldPathRenderer.h | 1 | ||||
-rwxr-xr-x | src/gpu/GrContext.cpp | 78 | ||||
-rw-r--r-- | src/gpu/GrGpuResource.cpp | 33 | ||||
-rw-r--r-- | src/gpu/GrGpuResourceCacheAccess.h | 19 | ||||
-rw-r--r-- | src/gpu/GrPath.h | 1 | ||||
-rw-r--r-- | src/gpu/GrPathRange.h | 1 | ||||
-rw-r--r-- | src/gpu/GrResourceCache.cpp | 393 | ||||
-rw-r--r-- | src/gpu/GrResourceCache.h | 251 | ||||
-rw-r--r-- | src/gpu/GrResourceCache2.cpp | 300 | ||||
-rw-r--r-- | src/gpu/GrResourceCache2.h | 203 | ||||
-rw-r--r-- | src/gpu/GrStencilBuffer.cpp | 2 | ||||
-rw-r--r-- | src/gpu/GrTest.cpp | 4 | ||||
-rw-r--r-- | src/gpu/GrTexture.cpp | 1 | ||||
-rw-r--r-- | src/gpu/SkGr.cpp | 1 | ||||
-rw-r--r-- | tests/ResourceCacheTest.cpp | 254 | ||||
-rw-r--r-- | tests/SurfaceTest.cpp | 29 |
24 files changed, 962 insertions, 702 deletions
diff --git a/bench/GrResourceCacheBench.cpp b/bench/GrResourceCacheBench.cpp index ef97ed8971..6d631ddb2c 100644 --- a/bench/GrResourceCacheBench.cpp +++ b/bench/GrResourceCacheBench.cpp @@ -13,6 +13,7 @@ #include "GrGpuResource.h" #include "GrContext.h" #include "GrGpu.h" +#include "GrResourceCache.h" #include "GrResourceCache2.h" #include "GrStencilBuffer.h" #include "GrTexture.h" @@ -88,20 +89,22 @@ static void get_stencil(int i, int* w, int* h, int* s) { } static void get_texture_desc(int i, GrSurfaceDesc* desc) { - desc->fFlags = kRenderTarget_GrSurfaceFlag | kNoStencil_GrSurfaceFlag; + desc->fFlags = kRenderTarget_GrSurfaceFlag | + kNoStencil_GrSurfaceFlag; desc->fWidth = i % 1024; desc->fHeight = i * 2 % 1024; desc->fConfig = static_cast<GrPixelConfig>(i % (kLast_GrPixelConfig + 1)); - desc->fSampleCnt = ((i % 2) == 0) ? 0 : 4; + desc->fSampleCnt = i % 1 == 0 ? 0 : 4; } -static void populate_cache(GrGpu* gpu, int resourceCount) { +static void populate_cache(GrResourceCache* cache, GrGpu* gpu, int resourceCount) { for (int i = 0; i < resourceCount; ++i) { int w, h, s; get_stencil(i, &w, &h, &s); GrResourceKey key = GrStencilBuffer::ComputeKey(w, h, s); GrGpuResource* resource = SkNEW_ARGS(StencilResource, (gpu, i)); - resource->cacheAccess().setContentKey(key); + cache->purgeAsNeeded(1, resource->gpuMemorySize()); + cache->addResource(key, resource); resource->unref(); } @@ -110,7 +113,8 @@ static void populate_cache(GrGpu* gpu, int resourceCount) { get_texture_desc(i, &desc); GrResourceKey key = TextureResource::ComputeKey(desc); GrGpuResource* resource = SkNEW_ARGS(TextureResource, (gpu, i)); - resource->cacheAccess().setContentKey(key); + cache->purgeAsNeeded(1, resource->gpuMemorySize()); + cache->addResource(key, resource); resource->unref(); } } @@ -194,24 +198,25 @@ protected: // Set the cache budget to be very large so no purging occurs. context->setResourceCacheLimits(2 * RESOURCE_COUNT, 1 << 30); + GrResourceCache* cache = context->getResourceCache(); GrResourceCache2* cache2 = context->getResourceCache2(); // Make sure the cache is empty. - cache2->purgeAllUnlocked(); - SkASSERT(0 == cache2->getResourceCount() && 0 == cache2->getResourceBytes()); + cache->purgeAllUnlocked(); + SkASSERT(0 == cache->getCachedResourceCount() && 0 == cache->getCachedResourceBytes()); GrGpu* gpu = context->getGpu(); for (int i = 0; i < loops; ++i) { - SkASSERT(0 == cache2->getResourceCount() && 0 == cache2->getResourceBytes()); + SkASSERT(0 == cache->getCachedResourceCount() && 0 == cache->getCachedResourceBytes()); - populate_cache(gpu, RESOURCE_COUNT); + populate_cache(cache, gpu, RESOURCE_COUNT); // Check that cache works. for (int k = 0; k < RESOURCE_COUNT; k += 33) { check_cache_contents_or_die(cache2, k); } - cache2->purgeAllUnlocked(); + cache->purgeAllUnlocked(); } } @@ -242,15 +247,16 @@ protected: // Set the cache budget to be very large so no purging occurs. context->setResourceCacheLimits(2 * RESOURCE_COUNT, 1 << 30); + GrResourceCache* cache = context->getResourceCache(); GrResourceCache2* cache2 = context->getResourceCache2(); // Make sure the cache is empty. - cache2->purgeAllUnlocked(); - SkASSERT(0 == cache2->getResourceCount() && 0 == cache2->getResourceBytes()); + cache->purgeAllUnlocked(); + SkASSERT(0 == cache->getCachedResourceCount() && 0 == cache->getCachedResourceBytes()); GrGpu* gpu = context->getGpu(); - populate_cache(gpu, RESOURCE_COUNT); + populate_cache(cache, gpu, RESOURCE_COUNT); for (int i = 0; i < loops; ++i) { for (int k = 0; k < RESOURCE_COUNT; ++k) { diff --git a/gyp/gpu.gyp b/gyp/gpu.gyp index 631945607d..d40afe1a95 100644 --- a/gyp/gpu.gyp +++ b/gyp/gpu.gyp @@ -63,6 +63,16 @@ ], }, }], + [ 'skia_resource_cache_mb_limit != 0', { + 'defines': [ + 'GR_DEFAULT_RESOURCE_CACHE_MB_LIMIT=<(skia_resource_cache_mb_limit)', + ], + }], + [ 'skia_resource_cache_count_limit != 0', { + 'defines': [ + 'GR_DEFAULT_RESOURCE_CACHE_COUNT_LIMIT=<(skia_resource_cache_count_limit)', + ], + }], ], 'direct_dependent_settings': { 'conditions': [ diff --git a/gyp/gpu.gypi b/gyp/gpu.gypi index 5095c44c5c..8eca40c3d3 100644 --- a/gyp/gpu.gypi +++ b/gyp/gpu.gypi @@ -136,6 +136,8 @@ '<(skia_src_path)/gpu/GrRenderTarget.cpp', '<(skia_src_path)/gpu/GrReducedClip.cpp', '<(skia_src_path)/gpu/GrReducedClip.h', + '<(skia_src_path)/gpu/GrResourceCache.cpp', + '<(skia_src_path)/gpu/GrResourceCache.h', '<(skia_src_path)/gpu/GrResourceCache2.cpp', '<(skia_src_path)/gpu/GrResourceCache2.h', '<(skia_src_path)/gpu/GrStencil.cpp', diff --git a/include/gpu/GrConfig.h b/include/gpu/GrConfig.h index d48ccd5dc6..e70c978fc0 100644 --- a/include/gpu/GrConfig.h +++ b/include/gpu/GrConfig.h @@ -213,6 +213,24 @@ typedef unsigned __int64 uint64_t; #endif /** + * GR_DEFAULT_RESOURCE_CACHE_MB_LIMIT gives a threshold (in megabytes) for the + * maximum size of the texture cache in vram. The value is only a default and + * can be overridden at runtime. + */ +#if !defined(GR_DEFAULT_RESOURCE_CACHE_MB_LIMIT) + #define GR_DEFAULT_RESOURCE_CACHE_MB_LIMIT 96 +#endif + +/** + * GR_DEFAULT_RESOURCE_CACHE_COUNT_LIMIT specifies the maximum number of + * textures the texture cache can hold in vram. The value is only a default and + * can be overridden at runtime. + */ +#if !defined(GR_DEFAULT_RESOURCE_CACHE_COUNT_LIMIT) + #define GR_DEFAULT_RESOURCE_CACHE_COUNT_LIMIT 2048 +#endif + +/** * GR_STROKE_PATH_RENDERING controls whether or not the GrStrokePathRenderer can be selected * as a path renderer. GrStrokePathRenderer is currently an experimental path renderer. */ diff --git a/include/gpu/GrContext.h b/include/gpu/GrContext.h index 3dbf7b3ea8..97acda1e79 100644 --- a/include/gpu/GrContext.h +++ b/include/gpu/GrContext.h @@ -33,6 +33,7 @@ class GrOvalRenderer; class GrPath; class GrPathRenderer; class GrResourceEntry; +class GrResourceCache; class GrResourceCache2; class GrStencilBuffer; class GrTestTarget; @@ -882,6 +883,7 @@ public: GrDrawTarget* getTextTarget(); const GrIndexBuffer* getQuadIndexBuffer() const; GrAARectRenderer* getAARectRenderer() { return fAARectRenderer; } + GrResourceCache* getResourceCache() { return fResourceCache; } GrResourceCache2* getResourceCache2() { return fResourceCache2; } // Called by tests that draw directly to the context via GrDrawTarget @@ -946,6 +948,7 @@ private: const GrClipData* fClip; // TODO: make this ref counted GrDrawState* fDrawState; + GrResourceCache* fResourceCache; GrResourceCache2* fResourceCache2; GrFontCache* fFontCache; SkAutoTDelete<GrLayerCache> fLayerCache; @@ -959,6 +962,7 @@ private: // Set by OverbudgetCB() to request that GrContext flush before exiting a draw. bool fFlushToReduceCacheSize; + GrAARectRenderer* fAARectRenderer; GrOvalRenderer* fOvalRenderer; @@ -999,6 +1003,8 @@ private: size_t rowBytes, bool filter); + GrTexture* createNewScratchTexture(const GrSurfaceDesc& desc); + /** * These functions create premul <-> unpremul effects if it is possible to generate a pair * of effects that make a readToUPM->writeToPM->readToUPM cycle invariant. Otherwise, they @@ -1009,9 +1015,9 @@ private: /** * This callback allows the resource cache to callback into the GrContext - * when the cache is still over budget after a purge. + * when the cache is still overbudget after a purge. */ - static void OverBudgetCB(void* data); + static bool OverbudgetCB(void* data); typedef SkRefCnt INHERITED; }; diff --git a/include/gpu/GrGpuResource.h b/include/gpu/GrGpuResource.h index 3c87117124..5231b15f89 100644 --- a/include/gpu/GrGpuResource.h +++ b/include/gpu/GrGpuResource.h @@ -16,6 +16,7 @@ class GrContext; class GrGpu; class GrResourceCache2; +class GrResourceCacheEntry; /** * Base class for GrGpuResource. Handles the various types of refs we need. Separated out as a base @@ -125,7 +126,7 @@ private: }; /** - * Base class for objects that can be kept in the GrResourceCache2. + * Base class for objects that can be kept in the GrResourceCache. */ class SK_API GrGpuResource : public GrIORef<GrGpuResource> { public: @@ -256,6 +257,7 @@ private: uint32_t fFlags; + GrResourceCacheEntry* fCacheEntry; // NULL if not in cache mutable size_t fGpuMemorySize; const uint32_t fUniqueID; diff --git a/include/gpu/GrUserConfig.h b/include/gpu/GrUserConfig.h index 4ff95f73b5..092ff9d8bc 100644 --- a/include/gpu/GrUserConfig.h +++ b/include/gpu/GrUserConfig.h @@ -19,4 +19,16 @@ */ //#define GR_GEOM_BUFFER_MAP_THRESHOLD (1<<15) +/** + * This gives a threshold in megabytes for the maximum size of the texture cache + * in vram. The value is only a default and can be overridden at runtime. + */ +//#define GR_DEFAULT_RESOURCE_CACHE_MB_LIMIT 96 + +/** + * This specifies the maximum number of textures the texture cache can hold + * in vram. The value is only a default and can be overridden at runtime. + */ +//#define GR_DEFAULT_RESOURCE_CACHE_COUNT_LIMIT 2048 + #endif diff --git a/include/gpu/SkGr.h b/include/gpu/SkGr.h index 8025c956ef..026525be5d 100644 --- a/include/gpu/SkGr.h +++ b/include/gpu/SkGr.h @@ -68,11 +68,6 @@ static inline GrColor SkColor2GrColorJustAlpha(SkColor c) { //////////////////////////////////////////////////////////////////////////////// -// The cache listens for these messages to purge junk resources proactively. -struct GrResourceInvalidatedMessage { - GrResourceKey key; -}; - bool GrIsBitmapInCache(const GrContext*, const SkBitmap&, const GrTextureParams*); GrTexture* GrRefCachedBitmapTexture(GrContext*, const SkBitmap&, const GrTextureParams*); diff --git a/src/gpu/GrAADistanceFieldPathRenderer.h b/src/gpu/GrAADistanceFieldPathRenderer.h index c337016c0b..10f0ebab93 100755 --- a/src/gpu/GrAADistanceFieldPathRenderer.h +++ b/src/gpu/GrAADistanceFieldPathRenderer.h @@ -15,7 +15,6 @@ #include "GrRect.h" #include "SkChecksum.h" -#include "SkTDynamicHash.h" class GrContext; class GrPlot; diff --git a/src/gpu/GrContext.cpp b/src/gpu/GrContext.cpp index 80fdace9ba..3a93404af1 100755 --- a/src/gpu/GrContext.cpp +++ b/src/gpu/GrContext.cpp @@ -25,6 +25,7 @@ #include "GrOvalRenderer.h" #include "GrPathRenderer.h" #include "GrPathUtils.h" +#include "GrResourceCache.h" #include "GrResourceCache2.h" #include "GrSoftwarePathRenderer.h" #include "GrStencilBuffer.h" @@ -51,6 +52,9 @@ #define GR_DEBUG_PARTIAL_COVERAGE_CHECK 0 #endif +static const size_t MAX_RESOURCE_CACHE_COUNT = GR_DEFAULT_RESOURCE_CACHE_COUNT_LIMIT; +static const size_t MAX_RESOURCE_CACHE_BYTES = GR_DEFAULT_RESOURCE_CACHE_MB_LIMIT * 1024 * 1024; + static const size_t DRAW_BUFFER_VBPOOL_BUFFER_SIZE = 1 << 15; static const int DRAW_BUFFER_VBPOOL_PREALLOC_BUFFERS = 4; @@ -99,6 +103,7 @@ GrContext::GrContext(const Options& opts) : fOptions(opts) { fClip = NULL; fPathRendererChain = NULL; fSoftwarePathRenderer = NULL; + fResourceCache = NULL; fResourceCache2 = NULL; fFontCache = NULL; fDrawBuffer = NULL; @@ -125,8 +130,11 @@ bool GrContext::init(GrBackend backend, GrBackendContext backendContext) { void GrContext::initCommon() { fDrawState = SkNEW(GrDrawState); + fResourceCache = SkNEW_ARGS(GrResourceCache, (fGpu->caps(), + MAX_RESOURCE_CACHE_COUNT, + MAX_RESOURCE_CACHE_BYTES)); + fResourceCache->setOverbudgetCallback(OverbudgetCB, this); fResourceCache2 = SkNEW(GrResourceCache2); - fResourceCache2->setOverBudgetCallback(OverBudgetCB, this); fFontCache = SkNEW_ARGS(GrFontCache, (fGpu)); @@ -152,6 +160,9 @@ GrContext::~GrContext() { } SkDELETE(fResourceCache2); + fResourceCache2 = NULL; + SkDELETE(fResourceCache); + fResourceCache = NULL; SkDELETE(fFontCache); SkDELETE(fDrawBuffer); SkDELETE(fDrawBufferVBAllocPool); @@ -190,6 +201,8 @@ void GrContext::abandonContext() { fAARectRenderer->reset(); fOvalRenderer->reset(); + fResourceCache->purgeAllUnlocked(); + fFontCache->freeAll(); fLayerCache->freeAll(); } @@ -208,6 +221,7 @@ void GrContext::freeGpuResources() { fAARectRenderer->reset(); fOvalRenderer->reset(); + fResourceCache->purgeAllUnlocked(); fFontCache->freeAll(); fLayerCache->freeAll(); // a path renderer may be holding onto resources @@ -216,12 +230,12 @@ void GrContext::freeGpuResources() { } void GrContext::getResourceCacheUsage(int* resourceCount, size_t* resourceBytes) const { - if (resourceCount) { - *resourceCount = fResourceCache2->getResourceCount(); - } - if (resourceBytes) { - *resourceBytes = fResourceCache2->getResourceBytes(); - } + if (resourceCount) { + *resourceCount = fResourceCache->getCachedResourceCount(); + } + if (resourceBytes) { + *resourceBytes = fResourceCache->getCachedResourceBytes(); + } } GrTextContext* GrContext::createTextContext(GrRenderTarget* renderTarget, @@ -259,13 +273,12 @@ bool GrContext::isTextureInCache(const GrSurfaceDesc& desc, } void GrContext::addStencilBuffer(GrStencilBuffer* sb) { - // TODO: Make GrStencilBuffers use the scratch mechanism rather than content keys. ASSERT_OWNED_RESOURCE(sb); GrResourceKey resourceKey = GrStencilBuffer::ComputeKey(sb->width(), sb->height(), sb->numSamples()); - SkAssertResult(sb->cacheAccess().setContentKey(resourceKey)); + fResourceCache->addResource(resourceKey, sb); } GrStencilBuffer* GrContext::findAndRefStencilBuffer(int width, int height, int sampleCnt) { @@ -407,19 +420,25 @@ GrTexture* GrContext::createTexture(const GrTextureParams* params, } if (texture) { - if (texture->cacheAccess().setContentKey(resourceKey)) { - if (cacheKey) { - *cacheKey = resourceKey; - } - } else { - texture->unref(); - texture = NULL; + fResourceCache->addResource(resourceKey, texture); + + if (cacheKey) { + *cacheKey = resourceKey; } } return texture; } +GrTexture* GrContext::createNewScratchTexture(const GrSurfaceDesc& desc) { + GrTexture* texture = fGpu->createTexture(desc, NULL, 0); + if (!texture) { + return NULL; + } + fResourceCache->addResource(texture->cacheAccess().getScratchKey(), texture); + return texture; +} + GrTexture* GrContext::refScratchTexture(const GrSurfaceDesc& inDesc, ScratchTexMatch match, bool calledDuringFlush) { // kNoStencil has no meaning if kRT isn't set. @@ -454,6 +473,7 @@ GrTexture* GrContext::refScratchTexture(const GrSurfaceDesc& inDesc, ScratchTexM } GrGpuResource* resource = fResourceCache2->findAndRefScratchResource(key, scratchFlags); if (resource) { + fResourceCache->makeResourceMRU(resource); return static_cast<GrSurface*>(resource)->asTexture(); } @@ -476,19 +496,21 @@ GrTexture* GrContext::refScratchTexture(const GrSurfaceDesc& inDesc, ScratchTexM desc.writable()->fFlags = origFlags; } - GrTexture* texture = fGpu->createTexture(*desc, NULL, 0); + GrTexture* texture = this->createNewScratchTexture(*desc); SkASSERT(NULL == texture || texture->cacheAccess().getScratchKey() == GrTexturePriv::ComputeScratchKey(*desc)); return texture; } -void GrContext::OverBudgetCB(void* data) { +bool GrContext::OverbudgetCB(void* data) { SkASSERT(data); GrContext* context = reinterpret_cast<GrContext*>(data); // Flush the InOrderDrawBuffer to possibly free up some textures context->fFlushToReduceCacheSize = true; + + return true; } @@ -500,16 +522,11 @@ GrTexture* GrContext::createUncachedTexture(const GrSurfaceDesc& descIn, } void GrContext::getResourceCacheLimits(int* maxTextures, size_t* maxTextureBytes) const { - if (maxTextures) { - *maxTextures = fResourceCache2->getMaxResourceCount(); - } - if (maxTextureBytes) { - *maxTextureBytes = fResourceCache2->getMaxResourceBytes(); - } + fResourceCache->getLimits(maxTextures, maxTextureBytes); } void GrContext::setResourceCacheLimits(int maxTextures, size_t maxTextureBytes) { - fResourceCache2->setLimits(maxTextures, maxTextureBytes); + fResourceCache->setLimits(maxTextures, maxTextureBytes); } int GrContext::getMaxTextureSize() const { @@ -1225,6 +1242,7 @@ void GrContext::flush(int flagsBitfield) { } else { fDrawBuffer->flush(); } + fResourceCache->purgeAsNeeded(); fFlushToReduceCacheSize = false; } @@ -1728,11 +1746,15 @@ const GrFragmentProcessor* GrContext::createUPMToPMEffect(GrTexture* texture, } void GrContext::addResourceToCache(const GrResourceKey& resourceKey, GrGpuResource* resource) { - resource->cacheAccess().setContentKey(resourceKey); + fResourceCache->addResource(resourceKey, resource); } GrGpuResource* GrContext::findAndRefCachedResource(const GrResourceKey& resourceKey) { - return fResourceCache2->findAndRefContentResource(resourceKey); + GrGpuResource* resource = fResourceCache2->findAndRefContentResource(resourceKey); + if (resource) { + fResourceCache->makeResourceMRU(resource); + } + return resource; } void GrContext::addGpuTraceMarker(const GrGpuTraceMarker* marker) { @@ -1752,7 +1774,7 @@ void GrContext::removeGpuTraceMarker(const GrGpuTraceMarker* marker) { /////////////////////////////////////////////////////////////////////////////// #if GR_CACHE_STATS void GrContext::printCacheStats() const { - fResourceCache2->printStats(); + fResourceCache->printStats(); } #endif diff --git a/src/gpu/GrGpuResource.cpp b/src/gpu/GrGpuResource.cpp index be8ea0daf0..f6f7282700 100644 --- a/src/gpu/GrGpuResource.cpp +++ b/src/gpu/GrGpuResource.cpp @@ -18,8 +18,16 @@ static inline GrResourceCache2* get_resource_cache2(GrGpu* gpu) { return gpu->getContext()->getResourceCache2(); } +static inline GrResourceCache* get_resource_cache(GrGpu* gpu) { + SkASSERT(gpu); + SkASSERT(gpu->getContext()); + SkASSERT(gpu->getContext()->getResourceCache()); + return gpu->getContext()->getResourceCache(); +} + GrGpuResource::GrGpuResource(GrGpu* gpu, bool isWrapped) : fGpu(gpu) + , fCacheEntry(NULL) , fGpuMemorySize(kInvalidGpuMemorySize) , fUniqueID(CreateUniqueID()) , fScratchKey(GrResourceKey::NullScratchKey()) @@ -32,7 +40,7 @@ GrGpuResource::GrGpuResource(GrGpu* gpu, bool isWrapped) } void GrGpuResource::registerWithCache() { - get_resource_cache2(fGpu)->resourceAccess().insertResource(this); + get_resource_cache2(fGpu)->insertResource(this); } GrGpuResource::~GrGpuResource() { @@ -43,18 +51,16 @@ GrGpuResource::~GrGpuResource() { void GrGpuResource::release() { if (fGpu) { this->onRelease(); - get_resource_cache2(fGpu)->resourceAccess().removeResource(this); + get_resource_cache2(fGpu)->removeResource(this); fGpu = NULL; - fGpuMemorySize = 0; } } void GrGpuResource::abandon() { if (fGpu) { this->onAbandon(); - get_resource_cache2(fGpu)->resourceAccess().removeResource(this); + get_resource_cache2(fGpu)->removeResource(this); fGpu = NULL; - fGpuMemorySize = 0; } } @@ -74,17 +80,6 @@ GrContext* GrGpuResource::getContext() { } } -void GrGpuResource::didChangeGpuMemorySize() const { - if (this->wasDestroyed()) { - return; - } - - size_t oldSize = fGpuMemorySize; - SkASSERT(kInvalidGpuMemorySize != oldSize); - fGpuMemorySize = kInvalidGpuMemorySize; - get_resource_cache2(fGpu)->resourceAccess().didChangeGpuMemorySize(this, oldSize); -} - bool GrGpuResource::setContentKey(const GrResourceKey& contentKey) { // Currently this can only be called once and can't be called when the resource is scratch. SkASSERT(!contentKey.isScratch()); @@ -97,7 +92,7 @@ bool GrGpuResource::setContentKey(const GrResourceKey& contentKey) { fContentKey = contentKey; fContentKeySet = true; - if (!get_resource_cache2(fGpu)->resourceAccess().didSetContentKey(this)) { + if (!get_resource_cache2(fGpu)->didSetContentKey(this)) { fContentKeySet = false; return false; } @@ -105,8 +100,8 @@ bool GrGpuResource::setContentKey(const GrResourceKey& contentKey) { } void GrGpuResource::notifyIsPurgable() const { - if (!this->wasDestroyed()) { - get_resource_cache2(fGpu)->resourceAccess().notifyPurgable(this); + if (fCacheEntry && !this->wasDestroyed()) { + get_resource_cache(fGpu)->notifyPurgable(this); } } diff --git a/src/gpu/GrGpuResourceCacheAccess.h b/src/gpu/GrGpuResourceCacheAccess.h index 7417a55a71..af5c05464b 100644 --- a/src/gpu/GrGpuResourceCacheAccess.h +++ b/src/gpu/GrGpuResourceCacheAccess.h @@ -29,6 +29,25 @@ public: } /** + * Used by legacy cache to attach a cache entry. This is to be removed soon. + */ + void setCacheEntry(GrResourceCacheEntry* cacheEntry) { + // GrResourceCache never changes the cacheEntry once one has been added. + SkASSERT(NULL == cacheEntry || NULL == fResource->fCacheEntry); + fResource->fCacheEntry = cacheEntry; + } + + /** + * Is the resource in the legacy cache? This is to be removed soon. + */ + bool isInCache() const { return SkToBool(fResource->fCacheEntry); } + + /** + * Returns the cache entry for the legacy cache. This is to be removed soon. + */ + GrResourceCacheEntry* getCacheEntry() const { return fResource->fCacheEntry; } + + /** * Is the resource currently cached as scratch? This means it has a valid scratch key and does * not have a content key. */ diff --git a/src/gpu/GrPath.h b/src/gpu/GrPath.h index 394db6f5c2..a571935390 100644 --- a/src/gpu/GrPath.h +++ b/src/gpu/GrPath.h @@ -9,6 +9,7 @@ #define GrPath_DEFINED #include "GrGpuResource.h" +#include "GrResourceCache.h" #include "SkPath.h" #include "SkRect.h" #include "SkStrokeRec.h" diff --git a/src/gpu/GrPathRange.h b/src/gpu/GrPathRange.h index 5bfecb0a68..dc8ce1d71b 100644 --- a/src/gpu/GrPathRange.h +++ b/src/gpu/GrPathRange.h @@ -9,6 +9,7 @@ #define GrPathRange_DEFINED #include "GrGpuResource.h" +#include "GrResourceCache.h" #include "SkRefCnt.h" #include "SkStrokeRec.h" #include "SkTArray.h" diff --git a/src/gpu/GrResourceCache.cpp b/src/gpu/GrResourceCache.cpp new file mode 100644 index 0000000000..a73d11703d --- /dev/null +++ b/src/gpu/GrResourceCache.cpp @@ -0,0 +1,393 @@ + +/* + * Copyright 2010 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "GrResourceCache.h" +#include "GrGpuResource.h" +#include "GrGpuResourceCacheAccess.h" +#include "GrTexturePriv.h" + +DECLARE_SKMESSAGEBUS_MESSAGE(GrResourceInvalidatedMessage); + +/////////////////////////////////////////////////////////////////////////////// + +void GrGpuResource::didChangeGpuMemorySize() const { + fGpuMemorySize = kInvalidGpuMemorySize; + if (this->cacheAccess().isInCache()) { + fCacheEntry->didChangeResourceSize(); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +GrResourceKey::ResourceType GrResourceKey::GenerateResourceType() { + static int32_t gNextType = 0; + + int32_t type = sk_atomic_inc(&gNextType); + if (type >= (1 << 8 * sizeof(ResourceType))) { + SkFAIL("Too many Resource Types"); + } + + return static_cast<ResourceType>(type); +} + +/////////////////////////////////////////////////////////////////////////////// + +GrResourceCacheEntry::GrResourceCacheEntry(GrResourceCache* resourceCache, GrGpuResource* resource) + : fResourceCache(resourceCache), + fResource(resource), + fCachedSize(resource->gpuMemorySize()) { + // we assume ownership of the resource, and will unref it when we die + SkASSERT(resource); + resource->ref(); +} + +GrResourceCacheEntry::~GrResourceCacheEntry() { + // We're relying on having the cache entry to remove this from GrResourceCache2's content hash. + // fResource->setCacheEntry(NULL); + fResource->unref(); +} + +#ifdef SK_DEBUG +void GrResourceCacheEntry::validate() const { + SkASSERT(fResourceCache); + SkASSERT(fResource); + SkASSERT(fResource->cacheAccess().getCacheEntry() == this); + SkASSERT(fResource->gpuMemorySize() == fCachedSize); + fResource->validate(); +} +#endif + +void GrResourceCacheEntry::didChangeResourceSize() { + size_t oldSize = fCachedSize; + fCachedSize = fResource->gpuMemorySize(); + if (fCachedSize > oldSize) { + fResourceCache->didIncreaseResourceSize(this, fCachedSize - oldSize); + } else if (fCachedSize < oldSize) { + fResourceCache->didDecreaseResourceSize(this, oldSize - fCachedSize); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +GrResourceCache::GrResourceCache(const GrDrawTargetCaps* caps, int maxCount, size_t maxBytes) + : fMaxCount(maxCount) + , fMaxBytes(maxBytes) + , fCaps(SkRef(caps)) { +#if GR_CACHE_STATS + fHighWaterEntryCount = 0; + fHighWaterEntryBytes = 0; +#endif + + fEntryCount = 0; + fEntryBytes = 0; + + fPurging = false; + + fOverbudgetCB = NULL; + fOverbudgetData = NULL; +} + +GrResourceCache::~GrResourceCache() { + GrAutoResourceCacheValidate atcv(this); + + EntryList::Iter iter; + + // Unlike the removeAll, here we really remove everything, including locked resources. + while (GrResourceCacheEntry* entry = fList.head()) { + GrAutoResourceCacheValidate atcv(this); + + // remove from our llist + this->internalDetach(entry); + + delete entry; + } +} + +void GrResourceCache::getLimits(int* maxResources, size_t* maxResourceBytes) const{ + if (maxResources) { + *maxResources = fMaxCount; + } + if (maxResourceBytes) { + *maxResourceBytes = fMaxBytes; + } +} + +void GrResourceCache::setLimits(int maxResources, size_t maxResourceBytes) { + bool smaller = (maxResources < fMaxCount) || (maxResourceBytes < fMaxBytes); + + fMaxCount = maxResources; + fMaxBytes = maxResourceBytes; + + if (smaller) { + this->purgeAsNeeded(); + } +} + +void GrResourceCache::internalDetach(GrResourceCacheEntry* entry) { + fList.remove(entry); + fEntryCount -= 1; + fEntryBytes -= entry->fCachedSize; +} + +void GrResourceCache::attachToHead(GrResourceCacheEntry* entry) { + fList.addToHead(entry); + + fEntryCount += 1; + fEntryBytes += entry->fCachedSize; + +#if GR_CACHE_STATS + if (fHighWaterEntryCount < fEntryCount) { + fHighWaterEntryCount = fEntryCount; + } + if (fHighWaterEntryBytes < fEntryBytes) { + fHighWaterEntryBytes = fEntryBytes; + } +#endif +} + + +void GrResourceCache::makeResourceMRU(GrGpuResource* resource) { + GrResourceCacheEntry* entry = resource->cacheAccess().getCacheEntry(); + if (entry) { + this->internalDetach(entry); + this->attachToHead(entry); + } +} + +void GrResourceCache::notifyPurgable(const GrGpuResource* resource) { + // Remove scratch textures from the cache the moment they become purgeable if + // scratch texture reuse is turned off. + SkASSERT(resource->cacheAccess().getCacheEntry()); + if (resource->cacheAccess().isScratch()) { + const GrResourceKey& key = resource->cacheAccess().getScratchKey(); + if (key.getResourceType() == GrTexturePriv::ResourceType() && + !fCaps->reuseScratchTextures() && + !(static_cast<const GrSurface*>(resource)->desc().fFlags & kRenderTarget_GrSurfaceFlag)) { + this->deleteResource(resource->cacheAccess().getCacheEntry()); + } + } +} + +bool GrResourceCache::addResource(const GrResourceKey& key, GrGpuResource* resource) { + if (NULL != resource->cacheAccess().getCacheEntry()) { + return false; + } + + if (key.isScratch()) { + SkASSERT(resource->cacheAccess().isScratch()); + SkASSERT(key == resource->cacheAccess().getScratchKey()); + } else { + if (!resource->cacheAccess().setContentKey(key)) { + return false; + } + } + + // we don't expect to create new resources during a purge. In theory + // this could cause purgeAsNeeded() into an infinite loop (e.g. + // each resource destroyed creates and locks 2 resources and + // unlocks 1 thereby causing a new purge). + SkASSERT(!fPurging); + GrAutoResourceCacheValidate atcv(this); + + GrResourceCacheEntry* entry = SkNEW_ARGS(GrResourceCacheEntry, (this, resource)); + resource->cacheAccess().setCacheEntry(entry); + + this->attachToHead(entry); + this->purgeAsNeeded(); + return true; +} + +void GrResourceCache::didIncreaseResourceSize(const GrResourceCacheEntry* entry, size_t amountInc) { + fEntryBytes += amountInc; + this->purgeAsNeeded(); +} + +void GrResourceCache::didDecreaseResourceSize(const GrResourceCacheEntry* entry, size_t amountDec) { + fEntryBytes -= amountDec; +#ifdef SK_DEBUG + this->validate(); +#endif +} + +/** + * Destroying a resource may potentially trigger the unlock of additional + * resources which in turn will trigger a nested purge. We block the nested + * purge using the fPurging variable. However, the initial purge will keep + * looping until either all resources in the cache are unlocked or we've met + * the budget. There is an assertion in createAndLock to check against a + * resource's destructor inserting new resources into the cache. If these + * new resources were unlocked before purgeAsNeeded completed it could + * potentially make purgeAsNeeded loop infinitely. + * + * extraCount and extraBytes are added to the current resource totals to account + * for incoming resources (e.g., GrContext is about to add 10MB split between + * 10 textures). + */ +void GrResourceCache::purgeAsNeeded(int extraCount, size_t extraBytes) { + if (fPurging) { + return; + } + + fPurging = true; + + this->internalPurge(extraCount, extraBytes); + if (((fEntryCount+extraCount) > fMaxCount || + (fEntryBytes+extraBytes) > fMaxBytes) && + fOverbudgetCB) { + // Despite the purge we're still over budget. See if Ganesh can + // release some resources and purge again. + if ((*fOverbudgetCB)(fOverbudgetData)) { + this->internalPurge(extraCount, extraBytes); + } + } + + fPurging = false; +} + +void GrResourceCache::purgeInvalidated() { + // TODO: Implement this in GrResourceCache2. +} + +void GrResourceCache::deleteResource(GrResourceCacheEntry* entry) { + SkASSERT(entry->fResource->isPurgable()); + // remove from our llist + this->internalDetach(entry); + delete entry; +} + +void GrResourceCache::internalPurge(int extraCount, size_t extraBytes) { + SkASSERT(fPurging); + + bool withinBudget = false; + bool changed = false; + + // The purging process is repeated several times since one pass + // may free up other resources + do { + EntryList::Iter iter; + + changed = false; + + // Note: the following code relies on the fact that the + // doubly linked list doesn't invalidate its data/pointers + // outside of the specific area where a deletion occurs (e.g., + // in internalDetach) + GrResourceCacheEntry* entry = iter.init(fList, EntryList::Iter::kTail_IterStart); + + while (entry) { + GrAutoResourceCacheValidate atcv(this); + + if ((fEntryCount+extraCount) <= fMaxCount && + (fEntryBytes+extraBytes) <= fMaxBytes) { + withinBudget = true; + break; + } + + GrResourceCacheEntry* prev = iter.prev(); + if (entry->fResource->isPurgable()) { + changed = true; + this->deleteResource(entry); + } + entry = prev; + } + } while (!withinBudget && changed); +} + +void GrResourceCache::purgeAllUnlocked() { + GrAutoResourceCacheValidate atcv(this); + + // we can have one GrCacheable holding a lock on another + // so we don't want to just do a simple loop kicking each + // entry out. Instead change the budget and purge. + + size_t savedMaxBytes = fMaxBytes; + int savedMaxCount = fMaxCount; + fMaxBytes = (size_t) -1; + fMaxCount = 0; + this->purgeAsNeeded(); + + fMaxBytes = savedMaxBytes; + fMaxCount = savedMaxCount; +} + +/////////////////////////////////////////////////////////////////////////////// + +#ifdef SK_DEBUG +size_t GrResourceCache::countBytes(const EntryList& list) { + size_t bytes = 0; + + EntryList::Iter iter; + + const GrResourceCacheEntry* entry = iter.init(const_cast<EntryList&>(list), + EntryList::Iter::kTail_IterStart); + + for ( ; entry; entry = iter.prev()) { + bytes += entry->resource()->gpuMemorySize(); + } + return bytes; +} + +static bool both_zero_or_nonzero(int count, size_t bytes) { + return (count == 0 && bytes == 0) || (count > 0 && bytes > 0); +} + +void GrResourceCache::validate() const { + fList.validate(); + SkASSERT(both_zero_or_nonzero(fEntryCount, fEntryBytes)); + + EntryList::Iter iter; + + // check that the shareable entries are okay + const GrResourceCacheEntry* entry = iter.init(const_cast<EntryList&>(fList), + EntryList::Iter::kHead_IterStart); + + int count = 0; + for ( ; entry; entry = iter.next()) { + entry->validate(); + count += 1; + } + SkASSERT(count == fEntryCount); + + size_t bytes = this->countBytes(fList); + SkASSERT(bytes == fEntryBytes); + SkASSERT(fList.countEntries() == fEntryCount); +} +#endif // SK_DEBUG + +#if GR_CACHE_STATS + +void GrResourceCache::printStats() { + int locked = 0; + int scratch = 0; + + EntryList::Iter iter; + + GrResourceCacheEntry* entry = iter.init(fList, EntryList::Iter::kTail_IterStart); + + for ( ; entry; entry = iter.prev()) { + if (!entry->fResource->isPurgable()) { + ++locked; + } + if (entry->fResource->cacheAccess().isScratch()) { + ++scratch; + } + } + + float countUtilization = (100.f * fEntryCount) / fMaxCount; + float byteUtilization = (100.f * fEntryBytes) / fMaxBytes; + + SkDebugf("Budget: %d items %d bytes\n", fMaxCount, fMaxBytes); + SkDebugf("\t\tEntry Count: current %d (%d locked, %d scratch %.2g%% full), high %d\n", + fEntryCount, locked, scratch, countUtilization, fHighWaterEntryCount); + SkDebugf("\t\tEntry Bytes: current %d (%.2g%% full) high %d\n", + fEntryBytes, byteUtilization, fHighWaterEntryBytes); +} + +#endif + +/////////////////////////////////////////////////////////////////////////////// diff --git a/src/gpu/GrResourceCache.h b/src/gpu/GrResourceCache.h new file mode 100644 index 0000000000..80e4b3f1e4 --- /dev/null +++ b/src/gpu/GrResourceCache.h @@ -0,0 +1,251 @@ + +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef GrResourceCache_DEFINED +#define GrResourceCache_DEFINED + +#include "GrDrawTargetCaps.h" +#include "GrResourceKey.h" +#include "SkTMultiMap.h" +#include "SkMessageBus.h" +#include "SkTInternalLList.h" + +class GrGpuResource; +class GrResourceCache; +class GrResourceCacheEntry; + + +// The cache listens for these messages to purge junk resources proactively. +struct GrResourceInvalidatedMessage { + GrResourceKey key; +}; + +/////////////////////////////////////////////////////////////////////////////// + +class GrResourceCacheEntry { +public: + GrGpuResource* resource() const { return fResource; } + + static uint32_t Hash(const GrGpuResource* resource) { + return static_cast<uint32_t>(reinterpret_cast<intptr_t>(resource)); + } +#ifdef SK_DEBUG + void validate() const; +#else + void validate() const {} +#endif + + /** + * Update the cached size for this entry and inform the resource cache that + * it has changed. Usually invoked from GrGpuResource::didChangeGpuMemorySize, + * not directly from here. + */ + void didChangeResourceSize(); + +private: + GrResourceCacheEntry(GrResourceCache*, GrGpuResource*); + ~GrResourceCacheEntry(); + + GrResourceCache* fResourceCache; + GrGpuResource* fResource; + size_t fCachedSize; + + // Linked list for the LRU ordering. + SK_DECLARE_INTERNAL_LLIST_INTERFACE(GrResourceCacheEntry); + + friend class GrResourceCache; + friend class GrContext; +}; + +/////////////////////////////////////////////////////////////////////////////// + +/** + * Cache of GrGpuResource objects. + * + * These have a corresponding GrResourceKey, built from 128bits identifying the + * resource. Multiple resources can map to same GrResourceKey. + * + * The cache stores the entries in a double-linked list, which is its LRU. + * When an entry is "locked" (i.e. given to the caller), it is moved to the + * head of the list. If/when we must purge some of the entries, we walk the + * list backwards from the tail, since those are the least recently used. + * + * For fast searches, we maintain a hash map based on the GrResourceKey. + * + * It is a goal to make the GrResourceCache the central repository and bookkeeper + * of all resources. It should replace the linked list of GrGpuResources that + * GrGpu uses to call abandon/release. + */ +class GrResourceCache { +public: + GrResourceCache(const GrDrawTargetCaps*, int maxCount, size_t maxBytes); + ~GrResourceCache(); + + /** + * Return the current resource cache limits. + * + * @param maxResource If non-null, returns maximum number of resources + * that can be held in the cache. + * @param maxBytes If non-null, returns maximum number of bytes of + * gpu memory that can be held in the cache. + */ + void getLimits(int* maxResources, size_t* maxBytes) const; + + /** + * Specify the resource cache limits. If the current cache exceeds either + * of these, it will be purged (LRU) to keep the cache within these limits. + * + * @param maxResources The maximum number of resources that can be held in + * the cache. + * @param maxBytes The maximum number of bytes of resource memory that + * can be held in the cache. + */ + void setLimits(int maxResources, size_t maxResourceBytes); + + /** + * The callback function used by the cache when it is still over budget + * after a purge. The passed in 'data' is the same 'data' handed to + * setOverbudgetCallback. The callback returns true if some resources + * have been freed. + */ + typedef bool (*PFOverbudgetCB)(void* data); + + /** + * Set the callback the cache should use when it is still over budget + * after a purge. The 'data' provided here will be passed back to the + * callback. Note that the cache will attempt to purge any resources newly + * freed by the callback. + */ + void setOverbudgetCallback(PFOverbudgetCB overbudgetCB, void* data) { + fOverbudgetCB = overbudgetCB; + fOverbudgetData = data; + } + + /** + * Returns the number of bytes consumed by cached resources. + */ + size_t getCachedResourceBytes() const { return fEntryBytes; } + + /** + * Returns the number of cached resources. + */ + int getCachedResourceCount() const { return fEntryCount; } + + void makeResourceMRU(GrGpuResource*); + + /** Called by GrGpuResources when they detects that they are newly purgable. */ + void notifyPurgable(const GrGpuResource*); + + /** + * Add the new resource to the cache (by creating a new cache entry based + * on the provided key and resource). + * + * Ownership of the resource is transferred to the resource cache, + * which will unref() it when it is purged or deleted. + * + * This can fail if the key is already taken, or the resource is already in + * the cache. + */ + bool addResource(const GrResourceKey& key, GrGpuResource* resource); + + /** + * Notify the cache that the size of a resource has changed. + */ + void didIncreaseResourceSize(const GrResourceCacheEntry*, size_t amountInc); + void didDecreaseResourceSize(const GrResourceCacheEntry*, size_t amountDec); + + /** + * Remove a resource from the cache and delete it! + */ + void deleteResource(GrResourceCacheEntry* entry); + + /** + * Removes every resource in the cache that isn't locked. + */ + void purgeAllUnlocked(); + + /** + * Allow cache to purge unused resources to obey resource limitations + * Note: this entry point will be hidden (again) once totally ref-driven + * cache maintenance is implemented. Note that the overbudget callback + * will be called if the initial purge doesn't get the cache under + * its budget. + * + * extraCount and extraBytes are added to the current resource allocation + * to make sure enough room is available for future additions (e.g, + * 10MB across 10 textures is about to be added). + */ + void purgeAsNeeded(int extraCount = 0, size_t extraBytes = 0); + +#ifdef SK_DEBUG + void validate() const; +#else + void validate() const {} +#endif + +#if GR_CACHE_STATS + void printStats(); +#endif + +private: + void internalDetach(GrResourceCacheEntry*); + void attachToHead(GrResourceCacheEntry*); + void purgeInvalidated(); + void internalPurge(int extraCount, size_t extraBytes); +#ifdef SK_DEBUG + static size_t countBytes(const SkTInternalLList<GrResourceCacheEntry>& list); +#endif + + // We're an internal doubly linked list + typedef SkTInternalLList<GrResourceCacheEntry> EntryList; + EntryList fList; + + // our budget, used in purgeAsNeeded() + int fMaxCount; + size_t fMaxBytes; + + // our current stats, related to our budget +#if GR_CACHE_STATS + int fHighWaterEntryCount; + size_t fHighWaterEntryBytes; +#endif + + int fEntryCount; + size_t fEntryBytes; + + // prevents recursive purging + bool fPurging; + + PFOverbudgetCB fOverbudgetCB; + void* fOverbudgetData; + + SkAutoTUnref<const GrDrawTargetCaps> fCaps; +}; + +/////////////////////////////////////////////////////////////////////////////// + +#ifdef SK_DEBUG + class GrAutoResourceCacheValidate { + public: + GrAutoResourceCacheValidate(GrResourceCache* cache) : fCache(cache) { + cache->validate(); + } + ~GrAutoResourceCacheValidate() { + fCache->validate(); + } + private: + GrResourceCache* fCache; + }; +#else + class GrAutoResourceCacheValidate { + public: + GrAutoResourceCacheValidate(GrResourceCache*) {} + }; +#endif + +#endif diff --git a/src/gpu/GrResourceCache2.cpp b/src/gpu/GrResourceCache2.cpp index 109e815dac..83143d7818 100644 --- a/src/gpu/GrResourceCache2.cpp +++ b/src/gpu/GrResourceCache2.cpp @@ -10,13 +10,6 @@ #include "GrResourceCache2.h" #include "GrGpuResource.h" -#include "SkGr.h" -#include "SkMessageBus.h" - -DECLARE_SKMESSAGEBUS_MESSAGE(GrResourceInvalidatedMessage); - -////////////////////////////////////////////////////////////////////////////// - GrResourceKey& GrResourceKey::NullScratchKey() { static const GrCacheID::Key kBogusKey = { { {0} } }; static GrCacheID kBogusID(ScratchDomain(), kBogusKey); @@ -34,85 +27,26 @@ GrCacheID::Domain GrResourceKey::ScratchDomain() { return gDomain; } -GrResourceKey::ResourceType GrResourceKey::GenerateResourceType() { - static int32_t gNextType = 0; - - int32_t type = sk_atomic_inc(&gNextType); - if (type >= (1 << 8 * sizeof(ResourceType))) { - SkFAIL("Too many Resource Types"); - } - - return static_cast<ResourceType>(type); -} - ////////////////////////////////////////////////////////////////////////////// -class GrResourceCache2::AutoValidate : ::SkNoncopyable { -public: - AutoValidate(GrResourceCache2* cache) : fCache(cache) { cache->validate(); } - ~AutoValidate() { fCache->validate(); } -private: - GrResourceCache2* fCache; -}; - - ////////////////////////////////////////////////////////////////////////////// - -static const int kDefaultMaxCount = 2 * (1 << 10); -static const size_t kDefaultMaxSize = 96 * (1 << 20); - -GrResourceCache2::GrResourceCache2() - : fMaxCount(kDefaultMaxCount) - , fMaxBytes(kDefaultMaxSize) -#if GR_CACHE_STATS - , fHighWaterCount(0) - , fHighWaterBytes(0) -#endif - , fCount(0) - , fBytes(0) - , fPurging(false) - , fNewlyPurgableResourceWhilePurging(false) - , fOverBudgetCB(NULL) - , fOverBudgetData(NULL) { -} - GrResourceCache2::~GrResourceCache2() { this->releaseAll(); } -void GrResourceCache2::setLimits(int count, size_t bytes) { - fMaxCount = count; - fMaxBytes = bytes; - this->purgeAsNeeded(); -} - void GrResourceCache2::insertResource(GrGpuResource* resource) { - AutoValidate av(this); - SkASSERT(resource); SkASSERT(!resource->wasDestroyed()); SkASSERT(!this->isInCache(resource)); - SkASSERT(!fPurging); fResources.addToHead(resource); - resource->ref(); - ++fCount; - SkDEBUGCODE(fHighWaterCount = SkTMax(fCount, fHighWaterCount)); - fBytes += resource->gpuMemorySize(); - SkDEBUGCODE(fHighWaterBytes = SkTMax(fBytes, fHighWaterBytes)); if (!resource->cacheAccess().getScratchKey().isNullScratch()) { // TODO(bsalomon): Make this assertion possible. // SkASSERT(!resource->isWrapped()); fScratchMap.insert(resource->cacheAccess().getScratchKey(), resource); } - - this->purgeAsNeeded(); } void GrResourceCache2::removeResource(GrGpuResource* resource) { - AutoValidate av(this); - - --fCount; - fBytes -= resource->gpuMemorySize(); SkASSERT(this->isInCache(resource)); fResources.remove(resource); if (!resource->cacheAccess().getScratchKey().isNullScratch()) { @@ -121,16 +55,13 @@ void GrResourceCache2::removeResource(GrGpuResource* resource) { if (const GrResourceKey* contentKey = resource->cacheAccess().getContentKey()) { fContentHash.remove(*contentKey); } + --fCount; } void GrResourceCache2::abandonAll() { - AutoValidate av(this); - - SkASSERT(!fPurging); while (GrGpuResource* head = fResources.head()) { SkASSERT(!head->wasDestroyed()); head->abandon(); - head->unref(); // abandon should have already removed this from the list. SkASSERT(head != fResources.head()); } @@ -140,13 +71,9 @@ void GrResourceCache2::abandonAll() { } void GrResourceCache2::releaseAll() { - AutoValidate av(this); - - SkASSERT(!fPurging); while (GrGpuResource* head = fResources.head()) { SkASSERT(!head->wasDestroyed()); head->release(); - head->unref(); // release should have already removed this from the list. SkASSERT(head != fResources.head()); } @@ -172,16 +99,11 @@ private: GrGpuResource* GrResourceCache2::findAndRefScratchResource(const GrResourceKey& scratchKey, uint32_t flags) { - AutoValidate av(this); - - SkASSERT(!fPurging); SkASSERT(scratchKey.isScratch()); - GrGpuResource* resource; if (flags & (kPreferNoPendingIO_ScratchFlag | kRequireNoPendingIO_ScratchFlag)) { - resource = fScratchMap.find(scratchKey, AvailableForScratchUse(true)); + GrGpuResource* resource = fScratchMap.find(scratchKey, AvailableForScratchUse(true)); if (resource) { - this->makeResourceMRU(resource); return SkRef(resource); } else if (flags & kRequireNoPendingIO_ScratchFlag) { return NULL; @@ -189,18 +111,11 @@ GrGpuResource* GrResourceCache2::findAndRefScratchResource(const GrResourceKey& // TODO: fail here when kPrefer is specified, we didn't find a resource without pending io, // but there is still space in our budget for the resource. } - resource = fScratchMap.find(scratchKey, AvailableForScratchUse(false)); - if (resource) { - resource->ref(); - this->makeResourceMRU(resource); - } - return resource; + return SkSafeRef(fScratchMap.find(scratchKey, AvailableForScratchUse(false))); } bool GrResourceCache2::didSetContentKey(GrGpuResource* resource) { - SkASSERT(!fPurging); SkASSERT(resource); - SkASSERT(this->isInCache(resource)); SkASSERT(resource->cacheAccess().getContentKey()); SkASSERT(!resource->cacheAccess().getContentKey()->isScratch()); @@ -210,214 +125,5 @@ bool GrResourceCache2::didSetContentKey(GrGpuResource* resource) { } fContentHash.add(resource); - this->validate(); return true; } - -void GrResourceCache2::makeResourceMRU(GrGpuResource* resource) { - AutoValidate av(this); - - SkASSERT(!fPurging); - SkASSERT(resource); - SkASSERT(this->isInCache(resource)); - fResources.remove(resource); - fResources.addToHead(resource); -} - -void GrResourceCache2::notifyPurgable(const GrGpuResource* resource) { - SkASSERT(resource); - SkASSERT(this->isInCache(resource)); - SkASSERT(resource->isPurgable()); - - // We can't purge if in the middle of purging because purge is iterating. Instead record - // that additional resources became purgable. - if (fPurging) { - fNewlyPurgableResourceWhilePurging = true; - return; - } - - // Purge the resource if we're over budget - bool overBudget = fCount > fMaxCount || fBytes > fMaxBytes; - - // We should not be over budget here unless all resources are unpuragble. -#ifdef SK_DEBUG - if (overBudget) { - ResourceList::Iter iter; - GrGpuResource* r = iter.init(fResources, ResourceList::Iter::kHead_IterStart); - for ( ; r; r = iter.next()) { - SkASSERT(r == resource || !r->isPurgable()); - } - } -#endif - - // Also purge if the resource has neither a valid scratch key nor a content key. - bool noKey = !resource->cacheAccess().isScratch() && - (NULL == resource->cacheAccess().getContentKey()); - - if (overBudget || noKey) { - SkDEBUGCODE(int beforeCount = fCount;) - resource->unref(); - SkASSERT(fCount == beforeCount - 1); - } - - this->validate(); -} - -void GrResourceCache2::didChangeGpuMemorySize(const GrGpuResource* resource, size_t oldSize) { - // SkASSERT(!fPurging); GrPathRange increases size during flush. :( - SkASSERT(resource); - SkASSERT(this->isInCache(resource)); - - fBytes += resource->gpuMemorySize() - oldSize; - SkDEBUGCODE(fHighWaterBytes = SkTMax(fBytes, fHighWaterBytes)); - - this->purgeAsNeeded(); - this->validate(); -} - -void GrResourceCache2::internalPurgeAsNeeded() { - SkASSERT(!fPurging); - SkASSERT(!fNewlyPurgableResourceWhilePurging); - SkASSERT(fCount > fMaxCount || fBytes > fMaxBytes); - - fPurging = true; - - AutoValidate av(this); // Put this after setting fPurging so we're allowed to be over budget. - - bool overBudget = true; - do { - fNewlyPurgableResourceWhilePurging = false; - ResourceList::Iter resourceIter; - GrGpuResource* resource = resourceIter.init(fResources, - ResourceList::Iter::kTail_IterStart); - - while (resource) { - GrGpuResource* prev = resourceIter.prev(); - if (resource->isPurgable()) { - resource->unref(); - } - resource = prev; - if (fCount <= fMaxCount && fBytes <= fMaxBytes) { - overBudget = false; - resource = NULL; - } - } - - if (!fNewlyPurgableResourceWhilePurging && overBudget && fOverBudgetCB) { - // Despite the purge we're still over budget. Call our over budget callback. - (*fOverBudgetCB)(fOverBudgetData); - } - } while (overBudget && fNewlyPurgableResourceWhilePurging); - - fNewlyPurgableResourceWhilePurging = false; - fPurging = false; -} - -void GrResourceCache2::purgeAllUnlocked() { - SkASSERT(!fPurging); - SkASSERT(!fNewlyPurgableResourceWhilePurging); - - fPurging = true; - - AutoValidate av(this); // Put this after setting fPurging so we're allowed to be over budget. - - do { - fNewlyPurgableResourceWhilePurging = false; - ResourceList::Iter resourceIter; - GrGpuResource* resource = - resourceIter.init(fResources, ResourceList::Iter::kTail_IterStart); - - while (resource) { - GrGpuResource* prev = resourceIter.prev(); - if (resource->isPurgable()) { - resource->unref(); - } - resource = prev; - } - - if (!fNewlyPurgableResourceWhilePurging && fCount && fOverBudgetCB) { - (*fOverBudgetCB)(fOverBudgetData); - } - } while (fNewlyPurgableResourceWhilePurging); - fPurging = false; -} - -#ifdef SK_DEBUG -void GrResourceCache2::validate() const { - size_t bytes = 0; - int count = 0; - int locked = 0; - int scratch = 0; - int couldBeScratch = 0; - int content = 0; - - ResourceList::Iter iter; - GrGpuResource* resource = iter.init(fResources, ResourceList::Iter::kHead_IterStart); - for ( ; resource; resource = iter.next()) { - bytes += resource->gpuMemorySize(); - ++count; - - if (!resource->isPurgable()) { - ++locked; - } - - if (resource->cacheAccess().isScratch()) { - SkASSERT(NULL == resource->cacheAccess().getContentKey()); - ++scratch; - SkASSERT(fScratchMap.countForKey(resource->cacheAccess().getScratchKey())); - } else if (!resource->cacheAccess().getScratchKey().isNullScratch()) { - SkASSERT(NULL != resource->cacheAccess().getContentKey()); - ++couldBeScratch; - SkASSERT(fScratchMap.countForKey(resource->cacheAccess().getScratchKey())); - } - - if (const GrResourceKey* contentKey = resource->cacheAccess().getContentKey()) { - ++content; - SkASSERT(fContentHash.find(*contentKey) == resource); - } - } - - SkASSERT(bytes == fBytes); - SkASSERT(count == fCount); -#if GR_CACHE_STATS - SkASSERT(bytes <= fHighWaterBytes); - SkASSERT(count <= fHighWaterCount); -#endif - SkASSERT(content == fContentHash.count()); - SkASSERT(scratch + couldBeScratch == fScratchMap.count()); - - bool overBudget = bytes > fMaxBytes || count > fMaxCount; - SkASSERT(!overBudget || locked == count || fPurging); -} -#endif - -#if GR_CACHE_STATS -void GrResourceCache2::printStats() const { - this->validate(); - - int locked = 0; - int scratch = 0; - - ResourceList::Iter iter; - GrGpuResource* resource = iter.init(fResources, ResourceList::Iter::kHead_IterStart); - - for ( ; resource; resource = iter.next()) { - if (!resource->isPurgable()) { - ++locked; - } - if (resource->cacheAccess().isScratch()) { - ++scratch; - } - } - - float countUtilization = (100.f * fCount) / fMaxCount; - float byteUtilization = (100.f * fBytes) / fMaxBytes; - - SkDebugf("Budget: %d items %d bytes\n", fMaxCount, fMaxBytes); - SkDebugf("\t\tEntry Count: current %d (%d locked, %d scratch %.2g%% full), high %d\n", - fCount, locked, scratch, countUtilization, fHighWaterCount); - SkDebugf("\t\tEntry Bytes: current %d (%.2g%% full) high %d\n", - fBytes, byteUtilization, fHighWaterBytes); -} - -#endif diff --git a/src/gpu/GrResourceCache2.h b/src/gpu/GrResourceCache2.h index 8b4d1d0e67..1cc958799d 100644 --- a/src/gpu/GrResourceCache2.h +++ b/src/gpu/GrResourceCache2.h @@ -17,65 +17,27 @@ #include "SkTMultiMap.h" /** - * Manages the lifetime of all GrGpuResource instances. - * - * Resources may have optionally have two types of keys: - * 1) A scratch key. This is for resources whose allocations are cached but not their contents. - * Multiple resources can share the same scratch key. This is so a caller can have two - * resource instances with the same properties (e.g. multipass rendering that ping pongs - * between two temporary surfaces. The scratch key is set at resource creation time and - * should never change. Resources need not have a scratch key. - * 2) A content key. This key represents the contents of the resource rather than just its - * allocation properties. They may not collide. The content key can be set after resource - * creation. Currently it may only be set once and cannot be cleared. This restriction will - * be removed. - * If a resource has neither key type then it will be deleted as soon as the last reference to it - * is dropped. If a key has both keys the content key takes precedence. + * Eventual replacement for GrResourceCache. Currently it simply holds a list + * of all GrGpuResource objects for a GrContext. It is used to invalidate all + * the resources when necessary. */ class GrResourceCache2 { public: - GrResourceCache2(); + GrResourceCache2() : fCount(0) {}; ~GrResourceCache2(); - /** Used to access functionality needed by GrGpuResource for lifetime management. */ - class ResourceAccess; - ResourceAccess resourceAccess(); - - /** - * Sets the cache limits in terms of number of resources and max gpu memory byte size. - */ - void setLimits(int count, size_t bytes); - - /** - * Returns the number of cached resources. - */ - int getResourceCount() const { return fCount; } - - /** - * Returns the number of bytes consumed by cached resources. - */ - size_t getResourceBytes() const { return fBytes; } + void insertResource(GrGpuResource*); - /** - * Returns the cached resources count budget. - */ - int getMaxResourceCount() const { return fMaxCount; } + void removeResource(GrGpuResource*); - /** - * Returns the number of bytes consumed by cached resources. - */ - size_t getMaxResourceBytes() const { return fMaxBytes; } + // This currently returns a bool and fails when an existing resource has a key that collides + // with the new content key. In the future it will null out the content key for the existing + // resource. The failure is a temporary measure taken because duties are split between two + // cache objects currently. + bool didSetContentKey(GrGpuResource*); - /** - * Abandons the backend API resources owned by all GrGpuResource objects and removes them from - * the cache. - */ void abandonAll(); - /** - * Releases the backend API resources owned by all GrGpuResource objects and removes them from - * the cache. - */ void releaseAll(); enum { @@ -84,10 +46,6 @@ public: /** Will not return any resources that match but have pending IO. */ kRequireNoPendingIO_ScratchFlag = 0x2, }; - - /** - * Find a resource that matches a scratch key. - */ GrGpuResource* findAndRefScratchResource(const GrResourceKey& scratchKey, uint32_t flags = 0); #ifdef SK_DEBUG @@ -98,80 +56,21 @@ public: } #endif - /** - * Find a resource that matches a content key. - */ GrGpuResource* findAndRefContentResource(const GrResourceKey& contentKey) { SkASSERT(!contentKey.isScratch()); - GrGpuResource* resource = fContentHash.find(contentKey); - if (resource) { - resource->ref(); - this->makeResourceMRU(resource); - } - return resource; + return SkSafeRef(fContentHash.find(contentKey)); } - /** - * Query whether a content key exists in the cache. - */ bool hasContentKey(const GrResourceKey& contentKey) const { SkASSERT(!contentKey.isScratch()); return SkToBool(fContentHash.find(contentKey)); } - /** Purges all resources that don't have external owners. */ - void purgeAllUnlocked(); - - /** - * The callback function used by the cache when it is still over budget after a purge. The - * passed in 'data' is the same 'data' handed to setOverbudgetCallback. - */ - typedef void (*PFOverBudgetCB)(void* data); - - /** - * Set the callback the cache should use when it is still over budget after a purge. The 'data' - * provided here will be passed back to the callback. Note that the cache will attempt to purge - * any resources newly freed by the callback. - */ - void setOverBudgetCallback(PFOverBudgetCB overBudgetCB, void* data) { - fOverBudgetCB = overBudgetCB; - fOverBudgetData = data; - } - -#if GR_GPU_STATS - void printStats() const; -#endif - private: - /////////////////////////////////////////////////////////////////////////// - /// @name Methods accessible via ResourceAccess - //// - void insertResource(GrGpuResource*); - void removeResource(GrGpuResource*); - void notifyPurgable(const GrGpuResource*); - void didChangeGpuMemorySize(const GrGpuResource*, size_t oldSize); - bool didSetContentKey(GrGpuResource*); - void makeResourceMRU(GrGpuResource*); - /// @} - - void purgeAsNeeded() { - if (fPurging || (fCount <= fMaxCount && fBytes < fMaxBytes)) { - return; - } - this->internalPurgeAsNeeded(); - } - - void internalPurgeAsNeeded(); - #ifdef SK_DEBUG bool isInCache(const GrGpuResource* r) const { return fResources.isInList(r); } - void validate() const; -#else - void validate() const {} #endif - class AutoValidate; - class AvailableForScratchUse; struct ScratchMapTraits { @@ -192,86 +91,12 @@ private: }; typedef SkTDynamicHash<GrGpuResource, GrResourceKey, ContentHashTraits> ContentHash; - typedef SkTInternalLList<GrGpuResource> ResourceList; - - ResourceList fResources; + int fCount; + SkTInternalLList<GrGpuResource> fResources; // This map holds all resources that can be used as scratch resources. ScratchMap fScratchMap; // This holds all resources that have content keys. ContentHash fContentHash; - - // our budget, used in purgeAsNeeded() - int fMaxCount; - size_t fMaxBytes; - -#if GR_CACHE_STATS - int fHighWaterCount; - size_t fHighWaterBytes; -#endif - - // our current stats, related to our budget - int fCount; - size_t fBytes; - - // prevents recursive purging - bool fPurging; - bool fNewlyPurgableResourceWhilePurging; - - PFOverBudgetCB fOverBudgetCB; - void* fOverBudgetData; - }; -class GrResourceCache2::ResourceAccess { -private: - ResourceAccess(GrResourceCache2* cache) : fCache(cache) { } - ResourceAccess(const ResourceAccess& that) : fCache(that.fCache) { } - ResourceAccess& operator=(const ResourceAccess&); // unimpl - - /** - * Insert a resource into the cache. - */ - void insertResource(GrGpuResource* resource) { fCache->insertResource(resource); } - - /** - * Removes a resource from the cache. - */ - void removeResource(GrGpuResource* resource) { fCache->removeResource(resource); } - - /** - * Called by GrGpuResources when they detects that they are newly purgable. - */ - void notifyPurgable(const GrGpuResource* resource) { fCache->notifyPurgable(resource); } - - /** - * Called by GrGpuResources when their sizes change. - */ - void didChangeGpuMemorySize(const GrGpuResource* resource, size_t oldSize) { - fCache->didChangeGpuMemorySize(resource, oldSize); - } - - /** - * Called by GrGpuResources when their content keys change. - * - * This currently returns a bool and fails when an existing resource has a key that collides - * with the new content key. In the future it will null out the content key for the existing - * resource. The failure is a temporary measure taken because duties are split between two - * cache objects currently. - */ - bool didSetContentKey(GrGpuResource* resource) { return fCache->didSetContentKey(resource); } - - // No taking addresses of this type. - const ResourceAccess* operator&() const; - ResourceAccess* operator&(); - - GrResourceCache2* fCache; - - friend class GrGpuResource; // To access all the proxy inline methods. - friend class GrResourceCache2; // To create this type. -}; - -inline GrResourceCache2::ResourceAccess GrResourceCache2::resourceAccess() { - return ResourceAccess(this); -} - #endif diff --git a/src/gpu/GrStencilBuffer.cpp b/src/gpu/GrStencilBuffer.cpp index 16b0150a4d..b288415301 100644 --- a/src/gpu/GrStencilBuffer.cpp +++ b/src/gpu/GrStencilBuffer.cpp @@ -13,6 +13,8 @@ #include "GrResourceCache2.h" void GrStencilBuffer::transferToCache() { + SkASSERT(!this->cacheAccess().isInCache()); + this->getGpu()->getContext()->addStencilBuffer(this); } diff --git a/src/gpu/GrTest.cpp b/src/gpu/GrTest.cpp index 528698478a..611059ae4c 100644 --- a/src/gpu/GrTest.cpp +++ b/src/gpu/GrTest.cpp @@ -9,7 +9,7 @@ #include "GrTest.h" #include "GrInOrderDrawBuffer.h" -#include "GrResourceCache2.h" +#include "GrResourceCache.h" void GrTestTarget::init(GrContext* ctx, GrDrawTarget* target) { SkASSERT(!fContext); @@ -38,7 +38,7 @@ void GrContext::setMaxTextureSizeOverride(int maxTextureSizeOverride) { } void GrContext::purgeAllUnlockedResources() { - fResourceCache2->purgeAllUnlocked(); + fResourceCache->purgeAllUnlocked(); } /////////////////////////////////////////////////////////////////////////////// diff --git a/src/gpu/GrTexture.cpp b/src/gpu/GrTexture.cpp index 9f700c7556..58bbeaae42 100644 --- a/src/gpu/GrTexture.cpp +++ b/src/gpu/GrTexture.cpp @@ -9,6 +9,7 @@ #include "GrContext.h" #include "GrDrawTargetCaps.h" #include "GrGpu.h" +#include "GrResourceCache.h" #include "GrTexture.h" #include "GrTexturePriv.h" diff --git a/src/gpu/SkGr.cpp b/src/gpu/SkGr.cpp index a86ee60317..affbd956b0 100644 --- a/src/gpu/SkGr.cpp +++ b/src/gpu/SkGr.cpp @@ -12,6 +12,7 @@ #include "SkMessageBus.h" #include "SkPixelRef.h" #include "SkTextureCompressor.h" +#include "GrResourceCache.h" #include "GrGpu.h" #include "effects/GrDitherEffect.h" #include "GrDrawTargetCaps.h" diff --git a/tests/ResourceCacheTest.cpp b/tests/ResourceCacheTest.cpp index 6f51d30766..7cebddb0cc 100644 --- a/tests/ResourceCacheTest.cpp +++ b/tests/ResourceCacheTest.cpp @@ -10,10 +10,9 @@ #include "GrContext.h" #include "GrContextFactory.h" #include "GrGpu.h" +#include "GrResourceCache.h" #include "GrResourceCache2.h" #include "SkCanvas.h" -#include "SkGr.h" -#include "SkMessageBus.h" #include "SkSurface.h" #include "Test.h" @@ -68,6 +67,7 @@ public: SK_DECLARE_INST_COUNT(TestResource); TestResource(GrGpu* gpu) : INHERITED(gpu, false) + , fCache(NULL) , fToDelete(NULL) , fSize(kDefaultSize) { ++fNumAlive; @@ -76,6 +76,7 @@ public: TestResource(GrGpu* gpu, const GrResourceKey& scratchKey) : INHERITED(gpu, false) + , fCache(NULL) , fToDelete(NULL) , fSize(kDefaultSize) { this->setScratchKey(scratchKey); @@ -85,7 +86,11 @@ public: ~TestResource() { --fNumAlive; - SkSafeUnref(fToDelete); + if (fToDelete) { + // Breaks our little 2-element cycle below. + fToDelete->setDeleteWhenDestroyed(NULL, NULL); + fCache->deleteResource(fToDelete->cacheAccess().getCacheEntry()); + } this->release(); } @@ -96,13 +101,15 @@ public: static int NumAlive() { return fNumAlive; } - void setUnrefWhenDestroyed(TestResource* resource) { - SkRefCnt_SafeAssign(fToDelete, resource); + void setDeleteWhenDestroyed(GrResourceCache* cache, TestResource* resource) { + fCache = cache; + fToDelete = resource; } private: size_t onGpuMemorySize() const SK_OVERRIDE { return fSize; } + GrResourceCache* fCache; TestResource* fToDelete; size_t fSize; static int fNumAlive; @@ -111,61 +118,6 @@ private: }; int TestResource::fNumAlive = 0; -static void test_no_key(skiatest::Reporter* reporter) { - SkAutoTUnref<GrContext> context(GrContext::CreateMockContext()); - REPORTER_ASSERT(reporter, SkToBool(context)); - if (NULL == context) { - return; - } - context->setResourceCacheLimits(10, 30000); - GrResourceCache2* cache2 = context->getResourceCache2(); - cache2->purgeAllUnlocked(); - SkASSERT(0 == cache2->getResourceCount() && 0 == cache2->getResourceBytes()); - - // Create a bunch of resources with no keys - TestResource* a = new TestResource(context->getGpu()); - TestResource* b = new TestResource(context->getGpu()); - TestResource* c = new TestResource(context->getGpu()); - TestResource* d = new TestResource(context->getGpu()); - a->setSize(11); - b->setSize(12); - c->setSize(13); - d->setSize(14); - - REPORTER_ASSERT(reporter, 4 == TestResource::NumAlive()); - REPORTER_ASSERT(reporter, 4 == cache2->getResourceCount()); - REPORTER_ASSERT(reporter, a->gpuMemorySize() + b->gpuMemorySize() + c->gpuMemorySize() + - d->gpuMemorySize() == cache2->getResourceBytes()); - - // Should be safe to purge without deleting the resources since we still have refs. - cache2->purgeAllUnlocked(); - REPORTER_ASSERT(reporter, 4 == TestResource::NumAlive()); - - // Since the resources have neither content nor scratch keys, delete immediately upon unref. - - a->unref(); - REPORTER_ASSERT(reporter, 3 == TestResource::NumAlive()); - REPORTER_ASSERT(reporter, 3 == cache2->getResourceCount()); - REPORTER_ASSERT(reporter, b->gpuMemorySize() + c->gpuMemorySize() + d->gpuMemorySize() == - cache2->getResourceBytes()); - - c->unref(); - REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive()); - REPORTER_ASSERT(reporter, 2 == cache2->getResourceCount()); - REPORTER_ASSERT(reporter, b->gpuMemorySize() + d->gpuMemorySize() == - cache2->getResourceBytes()); - - d->unref(); - REPORTER_ASSERT(reporter, 1 == TestResource::NumAlive()); - REPORTER_ASSERT(reporter, 1 == cache2->getResourceCount()); - REPORTER_ASSERT(reporter, b->gpuMemorySize() == cache2->getResourceBytes()); - - b->unref(); - REPORTER_ASSERT(reporter, 0 == TestResource::NumAlive()); - REPORTER_ASSERT(reporter, 0 == cache2->getResourceCount()); - REPORTER_ASSERT(reporter, 0 == cache2->getResourceBytes()); -} - static void test_duplicate_scratch_key(skiatest::Reporter* reporter) { SkAutoTUnref<GrContext> context(GrContext::CreateMockContext()); REPORTER_ASSERT(reporter, SkToBool(context)); @@ -173,9 +125,10 @@ static void test_duplicate_scratch_key(skiatest::Reporter* reporter) { return; } context->setResourceCacheLimits(5, 30000); - GrResourceCache2* cache2 = context->getResourceCache2(); - cache2->purgeAllUnlocked(); - SkASSERT(0 == cache2->getResourceCount() && 0 == cache2->getResourceBytes()); + GrResourceCache* cache = context->getResourceCache(); + SkDEBUGCODE(GrResourceCache2* cache2 = context->getResourceCache2();) + cache->purgeAllUnlocked(); + SkASSERT(0 == cache->getCachedResourceCount() && 0 == cache->getCachedResourceBytes()); GrCacheID::Key keyData; memset(&keyData, 0, sizeof(keyData)); @@ -189,16 +142,30 @@ static void test_duplicate_scratch_key(skiatest::Reporter* reporter) { a->setSize(11); b->setSize(12); // Scratch resources are registered with GrResourceCache2 just by existing. There are 2. - REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive()); SkDEBUGCODE(REPORTER_ASSERT(reporter, 2 == cache2->countScratchEntriesForKey(scratchKey));) - REPORTER_ASSERT(reporter, 2 == cache2->getResourceCount()); + + REPORTER_ASSERT(reporter, cache->addResource(scratchKey, a)); + + SkDEBUGCODE(REPORTER_ASSERT(reporter, 2 == cache2->countScratchEntriesForKey(scratchKey));) + + // Can't add the same resource twice. + REPORTER_ASSERT(reporter, !cache->addResource(scratchKey, a)); + REPORTER_ASSERT(reporter, 1 == cache->getCachedResourceCount()); + REPORTER_ASSERT(reporter, a->gpuMemorySize() == cache->getCachedResourceBytes()); + SkDEBUGCODE(REPORTER_ASSERT(reporter, 2 == cache2->countScratchEntriesForKey(scratchKey));) + + // Add a second with the same key. + REPORTER_ASSERT(reporter, cache->addResource(scratchKey, b)); + REPORTER_ASSERT(reporter, 2 == cache->getCachedResourceCount()); REPORTER_ASSERT(reporter, a->gpuMemorySize() + b->gpuMemorySize() == - cache2->getResourceBytes()); + cache->getCachedResourceBytes()); + REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive()); + SkDEBUGCODE(REPORTER_ASSERT(reporter, 2 == cache2->countScratchEntriesForKey(scratchKey));) // Our refs mean that the resources are non purgable. - cache2->purgeAllUnlocked(); + cache->purgeAllUnlocked(); REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive()); - REPORTER_ASSERT(reporter, 2 == cache2->getResourceCount()); + REPORTER_ASSERT(reporter, 2 == cache->getCachedResourceCount()); // Unref but don't purge a->unref(); @@ -207,9 +174,9 @@ static void test_duplicate_scratch_key(skiatest::Reporter* reporter) { SkDEBUGCODE(REPORTER_ASSERT(reporter, 2 == cache2->countScratchEntriesForKey(scratchKey));) // Purge again. This time resources should be purgable. - cache2->purgeAllUnlocked(); + cache->purgeAllUnlocked(); REPORTER_ASSERT(reporter, 0 == TestResource::NumAlive()); - REPORTER_ASSERT(reporter, 0 == cache2->getResourceCount()); + REPORTER_ASSERT(reporter, 0 == cache->getCachedResourceCount()); SkDEBUGCODE(REPORTER_ASSERT(reporter, 0 == cache2->countScratchEntriesForKey(scratchKey));) } @@ -220,9 +187,9 @@ static void test_duplicate_content_key(skiatest::Reporter* reporter) { return; } context->setResourceCacheLimits(5, 30000); - GrResourceCache2* cache2 = context->getResourceCache2(); - cache2->purgeAllUnlocked(); - SkASSERT(0 == cache2->getResourceCount() && 0 == cache2->getResourceBytes()); + GrResourceCache* cache = context->getResourceCache(); + cache->purgeAllUnlocked(); + SkASSERT(0 == cache->getCachedResourceCount() && 0 == cache->getCachedResourceBytes()); GrCacheID::Domain domain = GrCacheID::GenerateDomain(); GrCacheID::Key keyData; @@ -230,42 +197,30 @@ static void test_duplicate_content_key(skiatest::Reporter* reporter) { GrResourceKey::ResourceType t = GrResourceKey::GenerateResourceType(); GrResourceKey key(GrCacheID(domain, keyData), t, 0); + // Create two resources that we will attempt to register with the same content key. TestResource* a = new TestResource(context->getGpu()); TestResource* b = new TestResource(context->getGpu()); a->setSize(11); b->setSize(12); - - // Can't set the same content key on two resources. - REPORTER_ASSERT(reporter, a->cacheAccess().setContentKey(key)); - REPORTER_ASSERT(reporter, !b->cacheAccess().setContentKey(key)); - - // Still have two resources because b is still reffed. - REPORTER_ASSERT(reporter, 2 == cache2->getResourceCount()); - REPORTER_ASSERT(reporter, a->gpuMemorySize() + b->gpuMemorySize() == - cache2->getResourceBytes()); + REPORTER_ASSERT(reporter, cache->addResource(key, a)); + // Can't add the same or another resource with the same key. + REPORTER_ASSERT(reporter, !cache->addResource(key, a)); + REPORTER_ASSERT(reporter, !cache->addResource(key, b)); + REPORTER_ASSERT(reporter, 1 == cache->getCachedResourceCount()); + REPORTER_ASSERT(reporter, a->gpuMemorySize() == cache->getCachedResourceBytes()); REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive()); b->unref(); - // Now b should be gone. - REPORTER_ASSERT(reporter, 1 == cache2->getResourceCount()); - REPORTER_ASSERT(reporter, a->gpuMemorySize() == cache2->getResourceBytes()); - REPORTER_ASSERT(reporter, 1 == TestResource::NumAlive()); - - cache2->purgeAllUnlocked(); - REPORTER_ASSERT(reporter, 1 == cache2->getResourceCount()); - REPORTER_ASSERT(reporter, a->gpuMemorySize() == cache2->getResourceBytes()); + cache->purgeAllUnlocked(); + a->setSize(10); + REPORTER_ASSERT(reporter, 1 == cache->getCachedResourceCount()); REPORTER_ASSERT(reporter, 1 == TestResource::NumAlive()); - // Drop the ref on a but it isn't immediately purged as it still has a valid scratch key. a->unref(); - REPORTER_ASSERT(reporter, 1 == cache2->getResourceCount()); - REPORTER_ASSERT(reporter, a->gpuMemorySize() == cache2->getResourceBytes()); - REPORTER_ASSERT(reporter, 1 == TestResource::NumAlive()); - - cache2->purgeAllUnlocked(); - REPORTER_ASSERT(reporter, 0 == cache2->getResourceCount()); - REPORTER_ASSERT(reporter, 0 == cache2->getResourceBytes()); + cache->purgeAllUnlocked(); + REPORTER_ASSERT(reporter, 0 == cache->getCachedResourceCount()); + REPORTER_ASSERT(reporter, 0 == cache->getCachedResourceBytes()); REPORTER_ASSERT(reporter, 0 == TestResource::NumAlive()); } @@ -290,17 +245,18 @@ static void test_purge_invalidated(skiatest::Reporter* reporter) { GrResourceKey key3(GrCacheID(domain, keyData), t, 0); context->setResourceCacheLimits(5, 30000); + GrResourceCache* cache = context->getResourceCache(); GrResourceCache2* cache2 = context->getResourceCache2(); - cache2->purgeAllUnlocked(); - SkASSERT(0 == cache2->getResourceCount() && 0 == cache2->getResourceBytes()); + cache->purgeAllUnlocked(); + SkASSERT(0 == cache->getCachedResourceCount() && 0 == cache->getCachedResourceBytes()); // Add three resources to the cache. TestResource* a = new TestResource(context->getGpu()); TestResource* b = new TestResource(context->getGpu()); TestResource* c = new TestResource(context->getGpu()); - a->cacheAccess().setContentKey(key1); - b->cacheAccess().setContentKey(key2); - c->cacheAccess().setContentKey(key3); + cache->addResource(key1, a); + cache->addResource(key2, b); + cache->addResource(key3, c); a->unref(); b->unref(); c->unref(); @@ -315,8 +271,8 @@ static void test_purge_invalidated(skiatest::Reporter* reporter) { SkMessageBus<GrResourceInvalidatedMessage>::Post(msg1); const GrResourceInvalidatedMessage msg2 = { key2 }; SkMessageBus<GrResourceInvalidatedMessage>::Post(msg2); + cache->purgeAsNeeded(); #if 0 // Disabled until reimplemented in GrResourceCache2. - cache2->purgeAsNeeded(); REPORTER_ASSERT(reporter, 1 == TestResource::NumAlive()); REPORTER_ASSERT(reporter, !cache2->hasContentKey(key1)); REPORTER_ASSERT(reporter, !cache2->hasContentKey(key2)); @@ -326,19 +282,14 @@ static void test_purge_invalidated(skiatest::Reporter* reporter) { // Invalidate the third. const GrResourceInvalidatedMessage msg3 = { key3 }; SkMessageBus<GrResourceInvalidatedMessage>::Post(msg3); + cache->purgeAsNeeded(); #if 0 // Disabled until reimplemented in GrResourceCache2. - cache2->purgeAsNeeded(); REPORTER_ASSERT(reporter, 0 == TestResource::NumAlive()); REPORTER_ASSERT(reporter, !cache2->hasContentKey(key3)); #endif - - cache2->purgeAllUnlocked(); - REPORTER_ASSERT(reporter, 0 == TestResource::NumAlive()); - REPORTER_ASSERT(reporter, 0 == cache2->getResourceCount()); - REPORTER_ASSERT(reporter, 0 == cache2->getResourceBytes()); } -static void test_cache_chained_purge(skiatest::Reporter* reporter) { +static void test_cache_delete_on_destruction(skiatest::Reporter* reporter) { SkAutoTUnref<GrContext> context(GrContext::CreateMockContext()); REPORTER_ASSERT(reporter, SkToBool(context)); if (NULL == context) { @@ -358,34 +309,44 @@ static void test_cache_chained_purge(skiatest::Reporter* reporter) { { context->setResourceCacheLimits(3, 30000); - GrResourceCache2* cache2 = context->getResourceCache2(); - cache2->purgeAllUnlocked(); - SkASSERT(0 == cache2->getResourceCount() && 0 == cache2->getResourceBytes()); + GrResourceCache* cache = context->getResourceCache(); + cache->purgeAllUnlocked(); + SkASSERT(0 == cache->getCachedResourceCount() && 0 == cache->getCachedResourceBytes()); TestResource* a = new TestResource(context->getGpu()); TestResource* b = new TestResource(context->getGpu()); - a->cacheAccess().setContentKey(key1); - b->cacheAccess().setContentKey(key2); - - // Make a cycle - a->setUnrefWhenDestroyed(b); - b->setUnrefWhenDestroyed(a); + cache->addResource(key1, a); + cache->addResource(key2, b); - REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive()); + a->setDeleteWhenDestroyed(cache, b); + b->setDeleteWhenDestroyed(cache, a); a->unref(); b->unref(); REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive()); - cache2->purgeAllUnlocked(); - REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive()); + cache->purgeAllUnlocked(); + REPORTER_ASSERT(reporter, 0 == TestResource::NumAlive()); + } + { + context->setResourceCacheLimits(3, 30000); + GrResourceCache* cache = context->getResourceCache(); + cache->purgeAllUnlocked(); + SkASSERT(0 == cache->getCachedResourceCount() && 0 == cache->getCachedResourceBytes()); - // Break the cycle - a->setUnrefWhenDestroyed(NULL); - REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive()); + TestResource* a = new TestResource(context->getGpu()); + TestResource* b = new TestResource(context->getGpu()); + cache->addResource(key1, a); + cache->addResource(key2, b); + + a->setDeleteWhenDestroyed(cache, b); + b->setDeleteWhenDestroyed(cache, a); - cache2->purgeAllUnlocked(); + a->unref(); + b->unref(); + + cache->deleteResource(a->cacheAccess().getCacheEntry()); REPORTER_ASSERT(reporter, 0 == TestResource::NumAlive()); } } @@ -413,20 +374,23 @@ static void test_resource_size_changed(skiatest::Reporter* reporter) { // Test changing resources sizes (both increase & decrease). { context->setResourceCacheLimits(3, 30000); + GrResourceCache* cache = context->getResourceCache(); GrResourceCache2* cache2 = context->getResourceCache2(); - cache2->purgeAllUnlocked(); - SkASSERT(0 == cache2->getResourceCount() && 0 == cache2->getResourceBytes()); + cache->purgeAllUnlocked(); + SkASSERT(0 == cache->getCachedResourceCount() && 0 == cache->getCachedResourceBytes()); TestResource* a = new TestResource(context->getGpu()); - a->cacheAccess().setContentKey(key1); + a->setSize(100); // Test didChangeGpuMemorySize() when not in the cache. + cache->addResource(key1, a); a->unref(); TestResource* b = new TestResource(context->getGpu()); - b->cacheAccess().setContentKey(key2); + b->setSize(100); + cache->addResource(key2, b); b->unref(); - REPORTER_ASSERT(reporter, 200 == cache2->getResourceBytes()); - REPORTER_ASSERT(reporter, 2 == cache2->getResourceCount()); + REPORTER_ASSERT(reporter, 200 == cache->getCachedResourceBytes()); + REPORTER_ASSERT(reporter, 2 == cache->getCachedResourceCount()); { SkAutoTUnref<TestResource> find2(static_cast<TestResource*>(cache2->findAndRefContentResource(key2))); find2->setSize(200); @@ -434,29 +398,30 @@ static void test_resource_size_changed(skiatest::Reporter* reporter) { find1->setSize(50); } - REPORTER_ASSERT(reporter, 250 == cache2->getResourceBytes()); - REPORTER_ASSERT(reporter, 2 == cache2->getResourceCount()); + REPORTER_ASSERT(reporter, 250 == cache->getCachedResourceBytes()); + REPORTER_ASSERT(reporter, 2 == cache->getCachedResourceCount()); } // Test increasing a resources size beyond the cache budget. { context->setResourceCacheLimits(2, 300); + GrResourceCache* cache = context->getResourceCache(); GrResourceCache2* cache2 = context->getResourceCache2(); - cache2->purgeAllUnlocked(); - SkASSERT(0 == cache2->getResourceCount() && 0 == cache2->getResourceBytes()); + cache->purgeAllUnlocked(); + SkASSERT(0 == cache->getCachedResourceCount() && 0 == cache->getCachedResourceBytes()); TestResource* a = new TestResource(context->getGpu()); a->setSize(100); - a->cacheAccess().setContentKey(key1); + cache->addResource(key1, a); a->unref(); TestResource* b = new TestResource(context->getGpu()); b->setSize(100); - b->cacheAccess().setContentKey(key2); + cache->addResource(key2, b); b->unref(); - REPORTER_ASSERT(reporter, 200 == cache2->getResourceBytes()); - REPORTER_ASSERT(reporter, 2 == cache2->getResourceCount()); + REPORTER_ASSERT(reporter, 200 == cache->getCachedResourceBytes()); + REPORTER_ASSERT(reporter, 2 == cache->getCachedResourceCount()); { SkAutoTUnref<TestResource> find2(static_cast<TestResource*>(cache2->findAndRefContentResource(key2))); @@ -464,8 +429,8 @@ static void test_resource_size_changed(skiatest::Reporter* reporter) { } REPORTER_ASSERT(reporter, !cache2->hasContentKey(key1)); - REPORTER_ASSERT(reporter, 201 == cache2->getResourceBytes()); - REPORTER_ASSERT(reporter, 1 == cache2->getResourceCount()); + REPORTER_ASSERT(reporter, 201 == cache->getCachedResourceBytes()); + REPORTER_ASSERT(reporter, 1 == cache->getCachedResourceCount()); } } @@ -491,11 +456,10 @@ DEF_GPUTEST(ResourceCache, reporter, factory) { } // The below tests create their own mock contexts. - test_no_key(reporter); test_duplicate_content_key(reporter); test_duplicate_scratch_key(reporter); test_purge_invalidated(reporter); - test_cache_chained_purge(reporter); + test_cache_delete_on_destruction(reporter); test_resource_size_changed(reporter); } diff --git a/tests/SurfaceTest.cpp b/tests/SurfaceTest.cpp index 1913fcb146..69c8b845ec 100644 --- a/tests/SurfaceTest.cpp +++ b/tests/SurfaceTest.cpp @@ -321,6 +321,33 @@ static void TestSurfaceWritableAfterSnapshotRelease(skiatest::Reporter* reporter } #if SK_SUPPORT_GPU +static void TestSurfaceInCache(skiatest::Reporter* reporter, + SurfaceType surfaceType, + GrContext* context) { + context->freeGpuResources(); + int resourceCount; + + context->getResourceCacheUsage(&resourceCount, NULL); + REPORTER_ASSERT(reporter, 0 == resourceCount); + SkAutoTUnref<SkSurface> surface(createSurface(surfaceType, context)); + // Note: the stencil buffer is always cached, so kGpu_SurfaceType uses + // one cached resource, and kGpuScratch_SurfaceType uses two. + int expectedCachedResources = surfaceType == kGpuScratch_SurfaceType ? 2 : 1; + context->getResourceCacheUsage(&resourceCount, NULL); + REPORTER_ASSERT(reporter, expectedCachedResources == resourceCount); + + // Verify that all the cached resources are locked in cache. + context->freeGpuResources(); + context->getResourceCacheUsage(&resourceCount, NULL); + REPORTER_ASSERT(reporter, expectedCachedResources == resourceCount); + + // Verify that all the cached resources are unlocked upon surface release + surface.reset(0); + context->freeGpuResources(); + context->getResourceCacheUsage(&resourceCount, NULL); + REPORTER_ASSERT(reporter, 0 == resourceCount); +} + static void Test_crbug263329(skiatest::Reporter* reporter, SurfaceType surfaceType, GrContext* context) { @@ -426,6 +453,8 @@ DEF_GPUTEST(Surface, reporter, factory) { } GrContext* context = factory->get(glCtxType); if (context) { + TestSurfaceInCache(reporter, kGpu_SurfaceType, context); + TestSurfaceInCache(reporter, kGpuScratch_SurfaceType, context); Test_crbug263329(reporter, kGpu_SurfaceType, context); Test_crbug263329(reporter, kGpuScratch_SurfaceType, context); TestSurfaceCopyOnWrite(reporter, kGpu_SurfaceType, context); |