diff options
-rw-r--r-- | bench/GrResourceCacheBench.cpp | 79 | ||||
-rw-r--r-- | include/gpu/GrContext.h | 2 | ||||
-rw-r--r-- | include/gpu/GrGpuResource.h | 2 | ||||
-rw-r--r-- | src/core/SkTMultiMap.h | 13 | ||||
-rwxr-xr-x | src/gpu/GrContext.cpp | 25 | ||||
-rw-r--r-- | src/gpu/GrGpu.cpp | 9 | ||||
-rw-r--r-- | src/gpu/GrGpuResource.cpp | 23 | ||||
-rw-r--r-- | src/gpu/GrResourceCache.cpp | 32 | ||||
-rw-r--r-- | src/gpu/GrResourceCache.h | 17 | ||||
-rw-r--r-- | src/gpu/GrResourceCache2.cpp | 29 | ||||
-rw-r--r-- | src/gpu/GrResourceCache2.h | 40 | ||||
-rw-r--r-- | src/gpu/GrTexture.cpp | 5 | ||||
-rw-r--r-- | tests/ResourceCacheTest.cpp | 254 |
13 files changed, 397 insertions, 133 deletions
diff --git a/bench/GrResourceCacheBench.cpp b/bench/GrResourceCacheBench.cpp index 30d4cd410c..91c77fb589 100644 --- a/bench/GrResourceCacheBench.cpp +++ b/bench/GrResourceCacheBench.cpp @@ -6,13 +6,15 @@ * found in the LICENSE file. */ +#include "Benchmark.h" + #if SK_SUPPORT_GPU -#include "Benchmark.h" #include "GrGpuResource.h" #include "GrContext.h" #include "GrGpu.h" #include "GrResourceCache.h" +#include "GrResourceCache2.h" #include "GrStencilBuffer.h" #include "GrTexture.h" #include "GrTexturePriv.h" @@ -117,18 +119,18 @@ static void populate_cache(GrResourceCache* cache, GrGpu* gpu, int resourceCount } } -static void check_cache_contents_or_die(GrResourceCache* cache, int k) { +static void check_cache_contents_or_die(GrResourceCache2* cache, int k) { // Benchmark find calls that succeed. { GrSurfaceDesc desc; get_texture_desc(k, &desc); GrResourceKey key = TextureResource::ComputeKey(desc); - GrGpuResource* item = cache->find(key); - if (NULL == item) { + SkAutoTUnref<GrGpuResource> item(cache->findAndRefContentResource(key)); + if (!item) { SkFAIL("cache add does not work as expected"); return; } - if (static_cast<TextureResource*>(item)->fID != k) { + if (static_cast<TextureResource*>(item.get())->fID != k) { SkFAIL("cache add does not work as expected"); return; } @@ -137,12 +139,12 @@ static void check_cache_contents_or_die(GrResourceCache* cache, int k) { int w, h, s; get_stencil(k, &w, &h, &s); GrResourceKey key = StencilResource::ComputeKey(w, h, s); - GrGpuResource* item = cache->find(key); - if (NULL == item) { + SkAutoTUnref<GrGpuResource> item(cache->findAndRefContentResource(key)); + if (!item) { SkFAIL("cache add does not work as expected"); return; } - if (static_cast<TextureResource*>(item)->fID != k) { + if (static_cast<TextureResource*>(item.get())->fID != k) { SkFAIL("cache add does not work as expected"); return; } @@ -154,7 +156,7 @@ static void check_cache_contents_or_die(GrResourceCache* cache, int k) { get_texture_desc(k, &desc); desc.fHeight |= 1; GrResourceKey key = TextureResource::ComputeKey(desc); - GrGpuResource* item = cache->find(key); + SkAutoTUnref<GrGpuResource> item(cache->findAndRefContentResource(key)); if (item) { SkFAIL("cache add does not work as expected"); return; @@ -165,7 +167,7 @@ static void check_cache_contents_or_die(GrResourceCache* cache, int k) { get_stencil(k, &w, &h, &s); h |= 1; GrResourceKey key = StencilResource::ComputeKey(w, h, s); - GrGpuResource* item = cache->find(key); + SkAutoTUnref<GrGpuResource> item(cache->findAndRefContentResource(key)); if (item) { SkFAIL("cache add does not work as expected"); return; @@ -176,12 +178,11 @@ static void check_cache_contents_or_die(GrResourceCache* cache, int k) { class GrResourceCacheBenchAdd : public Benchmark { enum { RESOURCE_COUNT = CACHE_SIZE_COUNT / 2, - DUPLICATE_COUNT = CACHE_SIZE_COUNT / 4, }; public: virtual bool isSuitableFor(Backend backend) SK_OVERRIDE { - return backend == kGPU_Backend; + return backend == kNonRendering_Backend; } protected: @@ -190,18 +191,32 @@ protected: } virtual void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE { - GrGpu* gpu = canvas->getGrContext()->getGpu(); + SkAutoTUnref<GrContext> context(GrContext::CreateMockContext()); + if (NULL == context) { + return; + } + // 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. + cache->purgeAllUnlocked(); + SkASSERT(0 == cache->getCachedResourceCount() && 0 == cache->getCachedResourceBytes()); + + GrGpu* gpu = context->getGpu(); for (int i = 0; i < loops; ++i) { - GrResourceCache cache(gpu->caps(), CACHE_SIZE_COUNT, CACHE_SIZE_BYTES); - populate_cache(&cache, gpu, DUPLICATE_COUNT); - populate_cache(&cache, gpu, RESOURCE_COUNT); + SkASSERT(0 == cache->getCachedResourceCount() && 0 == cache->getCachedResourceBytes()); + + populate_cache(cache, gpu, RESOURCE_COUNT); // Check that cache works. for (int k = 0; k < RESOURCE_COUNT; k += 33) { - check_cache_contents_or_die(&cache, k); + check_cache_contents_or_die(cache2, k); } - cache.purgeAllUnlocked(); + cache->purgeAllUnlocked(); } } @@ -211,13 +226,12 @@ private: class GrResourceCacheBenchFind : public Benchmark { enum { - RESOURCE_COUNT = (CACHE_SIZE_COUNT / 2) - 100, - DUPLICATE_COUNT = 100 + RESOURCE_COUNT = CACHE_SIZE_COUNT / 2, }; public: virtual bool isSuitableFor(Backend backend) SK_OVERRIDE { - return backend == kGPU_Backend; + return backend == kNonRendering_Backend; } protected: @@ -226,14 +240,27 @@ protected: } virtual void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE { - GrGpu* gpu = canvas->getGrContext()->getGpu(); - GrResourceCache cache(gpu->caps(), CACHE_SIZE_COUNT, CACHE_SIZE_BYTES); - populate_cache(&cache, gpu, DUPLICATE_COUNT); - populate_cache(&cache, gpu, RESOURCE_COUNT); + SkAutoTUnref<GrContext> context(GrContext::CreateMockContext()); + if (NULL == context) { + return; + } + // 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. + cache->purgeAllUnlocked(); + SkASSERT(0 == cache->getCachedResourceCount() && 0 == cache->getCachedResourceBytes()); + + GrGpu* gpu = context->getGpu(); + + populate_cache(cache, gpu, RESOURCE_COUNT); for (int i = 0; i < loops; ++i) { for (int k = 0; k < RESOURCE_COUNT; ++k) { - check_cache_contents_or_die(&cache, k); + check_cache_contents_or_die(cache2, k); } } } diff --git a/include/gpu/GrContext.h b/include/gpu/GrContext.h index 2adc84211a..97acda1e79 100644 --- a/include/gpu/GrContext.h +++ b/include/gpu/GrContext.h @@ -897,7 +897,7 @@ public: * called to check the cache for a SB that matches an RT's criteria. */ void addStencilBuffer(GrStencilBuffer* sb); - GrStencilBuffer* findStencilBuffer(int width, int height, int sampleCnt); + GrStencilBuffer* findAndRefStencilBuffer(int width, int height, int sampleCnt); GrPathRenderer* getPathRenderer( const SkPath& path, diff --git a/include/gpu/GrGpuResource.h b/include/gpu/GrGpuResource.h index 4ffe17e739..d3341d9bea 100644 --- a/include/gpu/GrGpuResource.h +++ b/include/gpu/GrGpuResource.h @@ -172,7 +172,7 @@ public: */ virtual size_t gpuMemorySize() const = 0; - void setCacheEntry(GrResourceCacheEntry* cacheEntry) { fCacheEntry = cacheEntry; } + bool setCacheEntry(GrResourceCacheEntry* cacheEntry); GrResourceCacheEntry* getCacheEntry() const { return fCacheEntry; } bool isScratch() const; diff --git a/src/core/SkTMultiMap.h b/src/core/SkTMultiMap.h index 70076f0cad..1168ed6e91 100644 --- a/src/core/SkTMultiMap.h +++ b/src/core/SkTMultiMap.h @@ -102,6 +102,19 @@ public: int count() const { return fCount; } +#ifdef SK_DEBUG + // This is not particularly fast and only used for validation, so debug only. + int countForKey(const Key& key) const { + int count = 0; + ValueList* list = fHash.find(key); + while (list) { + list = list->fNext; + ++count; + } + return count; + } +#endif + private: SkTDynamicHash<ValueList, Key> fHash; int fCount; diff --git a/src/gpu/GrContext.cpp b/src/gpu/GrContext.cpp index 83ef58f3de..6f9395b5c3 100755 --- a/src/gpu/GrContext.cpp +++ b/src/gpu/GrContext.cpp @@ -255,20 +255,20 @@ GrTexture* GrContext::findAndRefTexture(const GrSurfaceDesc& desc, const GrCacheID& cacheID, const GrTextureParams* params) { GrResourceKey resourceKey = GrTexturePriv::ComputeKey(fGpu, params, desc, cacheID); - GrGpuResource* resource = fResourceCache->find(resourceKey); + + GrGpuResource* resource = this->findAndRefCachedResource(resourceKey); if (resource) { - resource->ref(); + SkASSERT(static_cast<GrSurface*>(resource)->asTexture()); return static_cast<GrSurface*>(resource)->asTexture(); - } else { - return NULL; } + return NULL; } bool GrContext::isTextureInCache(const GrSurfaceDesc& desc, const GrCacheID& cacheID, const GrTextureParams* params) const { GrResourceKey resourceKey = GrTexturePriv::ComputeKey(fGpu, params, desc, cacheID); - return fResourceCache->hasKey(resourceKey); + return fResourceCache2->hasContentKey(resourceKey); } void GrContext::addStencilBuffer(GrStencilBuffer* sb) { @@ -280,12 +280,9 @@ void GrContext::addStencilBuffer(GrStencilBuffer* sb) { fResourceCache->addResource(resourceKey, sb); } -GrStencilBuffer* GrContext::findStencilBuffer(int width, int height, - int sampleCnt) { - GrResourceKey resourceKey = GrStencilBuffer::ComputeKey(width, - height, - sampleCnt); - GrGpuResource* resource = fResourceCache->find(resourceKey); +GrStencilBuffer* GrContext::findAndRefStencilBuffer(int width, int height, int sampleCnt) { + GrResourceKey resourceKey = GrStencilBuffer::ComputeKey(width, height, sampleCnt); + GrGpuResource* resource = this->findAndRefCachedResource(resourceKey); return static_cast<GrStencilBuffer*>(resource); } @@ -1755,8 +1752,10 @@ void GrContext::addResourceToCache(const GrResourceKey& resourceKey, GrGpuResour } GrGpuResource* GrContext::findAndRefCachedResource(const GrResourceKey& resourceKey) { - GrGpuResource* resource = fResourceCache->find(resourceKey); - SkSafeRef(resource); + GrGpuResource* resource = fResourceCache2->findAndRefContentResource(resourceKey); + if (resource) { + fResourceCache->makeResourceMRU(resource); + } return resource; } diff --git a/src/gpu/GrGpu.cpp b/src/gpu/GrGpu.cpp index 64ee29bde5..ab24c5447f 100644 --- a/src/gpu/GrGpu.cpp +++ b/src/gpu/GrGpu.cpp @@ -84,10 +84,8 @@ GrTexture* GrGpu::createTexture(const GrSurfaceDesc& desc, bool GrGpu::attachStencilBufferToRenderTarget(GrRenderTarget* rt) { SkASSERT(NULL == rt->getStencilBuffer()); - GrStencilBuffer* sb = - this->getContext()->findStencilBuffer(rt->width(), - rt->height(), - rt->numSamples()); + SkAutoTUnref<GrStencilBuffer> sb( + this->getContext()->findAndRefStencilBuffer(rt->width(), rt->height(), rt->numSamples())); if (sb) { rt->setStencilBuffer(sb); bool attached = this->attachStencilBufferToRenderTarget(sb, rt); @@ -96,8 +94,7 @@ bool GrGpu::attachStencilBufferToRenderTarget(GrRenderTarget* rt) { } return attached; } - if (this->createStencilBufferForRenderTarget(rt, - rt->width(), rt->height())) { + if (this->createStencilBufferForRenderTarget(rt, rt->width(), rt->height())) { // Right now we're clearing the stencil buffer here after it is // attached to an RT for the first time. When we start matching // stencil buffers with smaller color targets this will no longer diff --git a/src/gpu/GrGpuResource.cpp b/src/gpu/GrGpuResource.cpp index 705cdea424..b77acffc68 100644 --- a/src/gpu/GrGpuResource.cpp +++ b/src/gpu/GrGpuResource.cpp @@ -78,6 +78,24 @@ GrContext* GrGpuResource::getContext() { } } +bool GrGpuResource::setCacheEntry(GrResourceCacheEntry* cacheEntry) { + // GrResourceCache never changes the cacheEntry once one has been added. + SkASSERT(NULL == cacheEntry || NULL == fCacheEntry); + + fCacheEntry = cacheEntry; + if (this->wasDestroyed() || NULL == cacheEntry) { + return true; + } + + if (!cacheEntry->key().isScratch()) { + if (!get_resource_cache2(fGpu)->didAddContentKey(this)) { + fCacheEntry = NULL; + return false; + } + } + return true; +} + void GrGpuResource::notifyIsPurgable() const { if (fCacheEntry && !this->wasDestroyed()) { get_resource_cache(fGpu)->notifyPurgable(this); @@ -92,6 +110,7 @@ void GrGpuResource::setScratchKey(const GrResourceKey& scratchKey) { } const GrResourceKey* GrGpuResource::getContentKey() const { + // Currently scratch resources have a cache entry in GrResourceCache with a scratch key. if (fCacheEntry && !fCacheEntry->key().isScratch()) { return &fCacheEntry->key(); } @@ -99,8 +118,8 @@ const GrResourceKey* GrGpuResource::getContentKey() const { } bool GrGpuResource::isScratch() const { - // Currently scratch resources have a cache entry in GrResourceCache with a scratch key. - return NULL != fCacheEntry && fCacheEntry->key().isScratch(); + SkASSERT(fScratchKey.isScratch()); + return NULL == this->getContentKey() && !fScratchKey.isNullScratch(); } uint32_t GrGpuResource::CreateUniqueID() { diff --git a/src/gpu/GrResourceCache.cpp b/src/gpu/GrResourceCache.cpp index 9754d4467a..8eed4d4b7d 100644 --- a/src/gpu/GrResourceCache.cpp +++ b/src/gpu/GrResourceCache.cpp @@ -49,7 +49,8 @@ GrResourceCacheEntry::GrResourceCacheEntry(GrResourceCache* resourceCache, } GrResourceCacheEntry::~GrResourceCacheEntry() { - fResource->setCacheEntry(NULL); + // We're relying on having the cache entry to remove this from GrResourceCache2's content hash. + // fResource->setCacheEntry(NULL); fResource->unref(); } @@ -185,26 +186,11 @@ void GrResourceCache::notifyPurgable(const GrGpuResource* resource) { } } -GrGpuResource* GrResourceCache::find(const GrResourceKey& key) { - // GrResourceCache2 is responsible for scratch resources. - SkASSERT(!key.isScratch()); - - GrAutoResourceCacheValidate atcv(this); - - GrResourceCacheEntry* entry = fCache.find(key); - if (NULL == entry) { - return NULL; +bool GrResourceCache::addResource(const GrResourceKey& key, GrGpuResource* resource) { + if (NULL != resource->getCacheEntry()) { + return false; } - // Make this resource MRU - this->internalDetach(entry); - this->attachToHead(entry); - - return entry->fResource; -} - -void GrResourceCache::addResource(const GrResourceKey& key, GrGpuResource* resource) { - SkASSERT(NULL == resource->getCacheEntry()); // 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 @@ -213,12 +199,16 @@ void GrResourceCache::addResource(const GrResourceKey& key, GrGpuResource* resou GrAutoResourceCacheValidate atcv(this); GrResourceCacheEntry* entry = SkNEW_ARGS(GrResourceCacheEntry, (this, key, resource)); - resource->setCacheEntry(entry); + if (!resource->setCacheEntry(entry)) { + SkDELETE(entry); + this->purgeAsNeeded(); + return false; + } this->attachToHead(entry); fCache.insert(key, entry); - this->purgeAsNeeded(); + return true; } void GrResourceCache::didIncreaseResourceSize(const GrResourceCacheEntry* entry, size_t amountInc) { diff --git a/src/gpu/GrResourceCache.h b/src/gpu/GrResourceCache.h index f6d064af39..874f16ac63 100644 --- a/src/gpu/GrResourceCache.h +++ b/src/gpu/GrResourceCache.h @@ -140,12 +140,6 @@ public: */ int getCachedResourceCount() const { return fEntryCount; } - /** - * Search for an entry with the same Key. If found, return it. - * If not found, return null. - */ - GrGpuResource* find(const GrResourceKey& key); - void makeResourceMRU(GrGpuResource*); /** Called by GrGpuResources when they detects that they are newly purgable. */ @@ -157,14 +151,11 @@ public: * * 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. */ - void addResource(const GrResourceKey& key, GrGpuResource* resource); - - /** - * Determines if the cache contains an entry matching a key. If a matching - * entry exists but was detached then it will not be found. - */ - bool hasKey(const GrResourceKey& key) const { return SkToBool(fCache.find(key)); } + bool addResource(const GrResourceKey& key, GrGpuResource* resource); /** * Notify the cache that the size of a resource has changed. diff --git a/src/gpu/GrResourceCache2.cpp b/src/gpu/GrResourceCache2.cpp index 6bc23a3096..65e522aafb 100644 --- a/src/gpu/GrResourceCache2.cpp +++ b/src/gpu/GrResourceCache2.cpp @@ -9,7 +9,6 @@ #include "GrResourceCache2.h" #include "GrGpuResource.h" -#include "SkRefCnt.h" GrResourceCache2::~GrResourceCache2() { this->releaseAll(); @@ -22,6 +21,8 @@ void GrResourceCache2::insertResource(GrGpuResource* resource) { fResources.addToHead(resource); ++fCount; if (!resource->getScratchKey().isNullScratch()) { + // TODO(bsalomon): Make this assertion possible. + // SkASSERT(!resource->isWrapped()); fScratchMap.insert(resource->getScratchKey(), resource); } } @@ -32,6 +33,9 @@ void GrResourceCache2::removeResource(GrGpuResource* resource) { if (!resource->getScratchKey().isNullScratch()) { fScratchMap.remove(resource->getScratchKey(), resource); } + if (const GrResourceKey* contentKey = resource->getContentKey()) { + fContentHash.remove(*contentKey); + } --fCount; } @@ -43,6 +47,7 @@ void GrResourceCache2::abandonAll() { SkASSERT(head != fResources.head()); } SkASSERT(!fScratchMap.count()); + SkASSERT(!fContentHash.count()); SkASSERT(!fCount); } @@ -89,3 +94,25 @@ GrGpuResource* GrResourceCache2::findAndRefScratchResource(const GrResourceKey& } return SkSafeRef(fScratchMap.find(scratchKey, AvailableForScratchUse(false))); } + +void GrResourceCache2::willRemoveContentKey(const GrGpuResource* resource) { + SkASSERT(resource); + SkASSERT(resource->getContentKey()); + SkDEBUGCODE(GrGpuResource* res = fContentHash.find(*resource->getContentKey())); + SkASSERT(res == resource); + + fContentHash.remove(*resource->getContentKey()); +} + +bool GrResourceCache2::didAddContentKey(GrGpuResource* resource) { + SkASSERT(resource); + SkASSERT(resource->getContentKey()); + + GrGpuResource* res = fContentHash.find(*resource->getContentKey()); + if (NULL != res) { + return false; + } + + fContentHash.add(resource); + return true; +} diff --git a/src/gpu/GrResourceCache2.h b/src/gpu/GrResourceCache2.h index a365a5aec7..9424c40850 100644 --- a/src/gpu/GrResourceCache2.h +++ b/src/gpu/GrResourceCache2.h @@ -11,6 +11,7 @@ #include "GrGpuResource.h" #include "GrResourceKey.h" +#include "SkRefCnt.h" #include "SkTInternalLList.h" #include "SkTMultiMap.h" @@ -28,6 +29,14 @@ public: void removeResource(GrGpuResource*); + void willRemoveContentKey(const GrGpuResource*); + + // 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 didAddContentKey(GrGpuResource*); + void abandonAll(); void releaseAll(); @@ -39,6 +48,24 @@ public: kRequireNoPendingIO_ScratchFlag = 0x2, }; GrGpuResource* findAndRefScratchResource(const GrResourceKey& scratchKey, uint32_t flags = 0); + +#ifdef SK_DEBUG + // This is not particularly fast and only used for validation, so debug only. + int countScratchEntriesForKey(const GrResourceKey& scratchKey) const { + SkASSERT(scratchKey.isScratch()); + return fScratchMap.countForKey(scratchKey); + } +#endif + + GrGpuResource* findAndRefContentResource(const GrResourceKey& contentKey) { + SkASSERT(!contentKey.isScratch()); + return SkSafeRef(fContentHash.find(contentKey)); + } + + bool hasContentKey(const GrResourceKey& contentKey) const { + SkASSERT(!contentKey.isScratch()); + return SkToBool(fContentHash.find(contentKey)); + } private: #ifdef SK_DEBUG @@ -56,10 +83,21 @@ private: }; typedef SkTMultiMap<GrGpuResource, GrResourceKey, ScratchMapTraits> ScratchMap; + struct ContentHashTraits { + static const GrResourceKey& GetKey(const GrGpuResource& r) { + return *r.getContentKey(); + } + + static uint32_t Hash(const GrResourceKey& key) { return key.getHash(); } + }; + typedef SkTDynamicHash<GrGpuResource, GrResourceKey, ContentHashTraits> ContentHash; + int fCount; SkTInternalLList<GrGpuResource> fResources; // This map holds all resources that can be used as scratch resources. - ScratchMap fScratchMap; + ScratchMap fScratchMap; + // This holds all resources that have content keys. + ContentHash fContentHash; }; #endif diff --git a/src/gpu/GrTexture.cpp b/src/gpu/GrTexture.cpp index 21395409d6..72e82252dc 100644 --- a/src/gpu/GrTexture.cpp +++ b/src/gpu/GrTexture.cpp @@ -121,7 +121,10 @@ GrSurfaceOrigin resolve_origin(const GrSurfaceDesc& desc) { GrTexture::GrTexture(GrGpu* gpu, bool isWrapped, const GrSurfaceDesc& desc) : INHERITED(gpu, isWrapped, desc) , fMipMapsStatus(kNotAllocated_MipMapsStatus) { - this->setScratchKey(GrTexturePriv::ComputeScratchKey(desc)); + + if (!isWrapped) { + this->setScratchKey(GrTexturePriv::ComputeScratchKey(desc)); + } // only make sense if alloc size is pow2 fShiftFixedX = 31 - SkCLZ(fDesc.fWidth); fShiftFixedY = 31 - SkCLZ(fDesc.fHeight); diff --git a/tests/ResourceCacheTest.cpp b/tests/ResourceCacheTest.cpp index 2827cbfd6d..24ecb949ca 100644 --- a/tests/ResourceCacheTest.cpp +++ b/tests/ResourceCacheTest.cpp @@ -65,11 +65,21 @@ class TestResource : public GrGpuResource { public: SK_DECLARE_INST_COUNT(TestResource); - TestResource(GrGpu* gpu, size_t size = kDefaultSize) + TestResource(GrGpu* gpu) : INHERITED(gpu, false) , fCache(NULL) , fToDelete(NULL) - , fSize(size) { + , fSize(kDefaultSize) { + ++fNumAlive; + this->registerWithCache(); + } + + TestResource(GrGpu* gpu, const GrResourceKey& scratchKey) + : INHERITED(gpu, false) + , fCache(NULL) + , fToDelete(NULL) + , fSize(kDefaultSize) { + this->setScratchKey(scratchKey); ++fNumAlive; this->registerWithCache(); } @@ -108,52 +118,189 @@ private: }; int TestResource::fNumAlive = 0; -static void test_purge_invalidated(skiatest::Reporter* reporter, GrContext* context) { +static void test_duplicate_scratch_key(skiatest::Reporter* reporter) { + SkAutoTUnref<GrContext> context(GrContext::CreateMockContext()); + REPORTER_ASSERT(reporter, SkToBool(context)); + if (NULL == context) { + return; + } + context->setResourceCacheLimits(5, 30000); + GrResourceCache* cache = context->getResourceCache(); + SkDEBUGCODE(GrResourceCache2* cache2 = context->getResourceCache2();) + cache->purgeAllUnlocked(); + SkASSERT(0 == cache->getCachedResourceCount() && 0 == cache->getCachedResourceBytes()); + + GrCacheID::Key keyData; + GrCacheID::Domain domain = GrResourceKey::ScratchDomain(); + GrResourceKey::ResourceType t = GrResourceKey::GenerateResourceType(); + GrResourceKey scratchKey(GrCacheID(domain, keyData), t, 0); + + // Create two resources that have the same scratch key. + TestResource* a = new TestResource(context->getGpu(), scratchKey); + TestResource* b = new TestResource(context->getGpu(), scratchKey); + a->setSize(11); + b->setSize(12); + // Scratch resources are registered with GrResourceCache2 just by existing. There are 2. + SkDEBUGCODE(REPORTER_ASSERT(reporter, 2 == cache2->countScratchEntriesForKey(scratchKey));) + + 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() == + 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. + cache->purgeAllUnlocked(); + REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive()); + REPORTER_ASSERT(reporter, 2 == cache->getCachedResourceCount()); + + // Unref but don't purge + a->unref(); + b->unref(); + REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive()); + SkDEBUGCODE(REPORTER_ASSERT(reporter, 2 == cache2->countScratchEntriesForKey(scratchKey));) + + // Purge again. This time resources should be purgable. + cache->purgeAllUnlocked(); + REPORTER_ASSERT(reporter, 0 == TestResource::NumAlive()); + REPORTER_ASSERT(reporter, 0 == cache->getCachedResourceCount()); + SkDEBUGCODE(REPORTER_ASSERT(reporter, 0 == cache2->countScratchEntriesForKey(scratchKey));) +} + +static void test_duplicate_content_key(skiatest::Reporter* reporter) { + SkAutoTUnref<GrContext> context(GrContext::CreateMockContext()); + REPORTER_ASSERT(reporter, SkToBool(context)); + if (NULL == context) { + return; + } + context->setResourceCacheLimits(5, 30000); + GrResourceCache* cache = context->getResourceCache(); + cache->purgeAllUnlocked(); + SkASSERT(0 == cache->getCachedResourceCount() && 0 == cache->getCachedResourceBytes()); + GrCacheID::Domain domain = GrCacheID::GenerateDomain(); GrCacheID::Key keyData; - keyData.fData64[0] = 5; - keyData.fData64[1] = 18; + memset(&keyData, 0, sizeof(keyData)); 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); + 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(); + cache->purgeAllUnlocked(); + a->setSize(10); + REPORTER_ASSERT(reporter, 1 == cache->getCachedResourceCount()); + REPORTER_ASSERT(reporter, 1 == TestResource::NumAlive()); + + a->unref(); + cache->purgeAllUnlocked(); + REPORTER_ASSERT(reporter, 0 == cache->getCachedResourceCount()); + REPORTER_ASSERT(reporter, 0 == cache->getCachedResourceBytes()); + REPORTER_ASSERT(reporter, 0 == TestResource::NumAlive()); +} + +static void test_purge_invalidated(skiatest::Reporter* reporter) { + SkAutoTUnref<GrContext> context(GrContext::CreateMockContext()); + REPORTER_ASSERT(reporter, SkToBool(context)); + if (NULL == context) { + return; + } + + GrCacheID::Domain domain = GrCacheID::GenerateDomain(); + GrCacheID::Key keyData; + memset(&keyData, 0, sizeof(keyData)); + + GrResourceKey::ResourceType t = GrResourceKey::GenerateResourceType(); + + keyData.fData64[0] = 1; + GrResourceKey key1(GrCacheID(domain, keyData), t, 0); + keyData.fData64[0] = 2; + GrResourceKey key2(GrCacheID(domain, keyData), t, 0); + keyData.fData64[0] = 3; + GrResourceKey key3(GrCacheID(domain, keyData), t, 0); + context->setResourceCacheLimits(5, 30000); GrResourceCache* cache = context->getResourceCache(); + GrResourceCache2* cache2 = context->getResourceCache2(); cache->purgeAllUnlocked(); SkASSERT(0 == cache->getCachedResourceCount() && 0 == cache->getCachedResourceBytes()); - // Add two resources with the same key that delete each other from the cache when destroyed. + // Add three resources to the cache. TestResource* a = new TestResource(context->getGpu()); TestResource* b = new TestResource(context->getGpu()); - cache->addResource(key, a); - cache->addResource(key, b); - // Circle back. - a->setDeleteWhenDestroyed(cache, b); - b->setDeleteWhenDestroyed(cache, a); + TestResource* c = new TestResource(context->getGpu()); + cache->addResource(key1, a); + cache->addResource(key2, b); + cache->addResource(key3, c); a->unref(); b->unref(); + c->unref(); - // Add a third independent resource also with the same key. - GrGpuResource* r = new TestResource(context->getGpu()); - cache->addResource(key, r); - r->unref(); + REPORTER_ASSERT(reporter, cache2->hasContentKey(key1)); + REPORTER_ASSERT(reporter, cache2->hasContentKey(key2)); + REPORTER_ASSERT(reporter, cache2->hasContentKey(key3)); - // Invalidate all three, all three should be purged and destroyed. + // Invalidate two of the three, they should be purged and destroyed. REPORTER_ASSERT(reporter, 3 == TestResource::NumAlive()); - const GrResourceInvalidatedMessage msg = { key }; - SkMessageBus<GrResourceInvalidatedMessage>::Post(msg); + const GrResourceInvalidatedMessage msg1 = { key1 }; + SkMessageBus<GrResourceInvalidatedMessage>::Post(msg1); + const GrResourceInvalidatedMessage msg2 = { key2 }; + SkMessageBus<GrResourceInvalidatedMessage>::Post(msg2); + cache->purgeAsNeeded(); + REPORTER_ASSERT(reporter, 1 == TestResource::NumAlive()); + REPORTER_ASSERT(reporter, !cache2->hasContentKey(key1)); + REPORTER_ASSERT(reporter, !cache2->hasContentKey(key2)); + REPORTER_ASSERT(reporter, cache2->hasContentKey(key3)); + + // Invalidate the third. + const GrResourceInvalidatedMessage msg3 = { key3 }; + SkMessageBus<GrResourceInvalidatedMessage>::Post(msg3); cache->purgeAsNeeded(); REPORTER_ASSERT(reporter, 0 == TestResource::NumAlive()); + REPORTER_ASSERT(reporter, !cache2->hasContentKey(key3)); } -static void test_cache_delete_on_destruction(skiatest::Reporter* reporter, - GrContext* context) { +static void test_cache_delete_on_destruction(skiatest::Reporter* reporter) { + SkAutoTUnref<GrContext> context(GrContext::CreateMockContext()); + REPORTER_ASSERT(reporter, SkToBool(context)); + if (NULL == context) { + return; + } + GrCacheID::Domain domain = GrCacheID::GenerateDomain(); GrCacheID::Key keyData; - keyData.fData64[0] = 5; - keyData.fData64[1] = 0; + memset(&keyData, 0, sizeof(keyData)); GrResourceKey::ResourceType t = GrResourceKey::GenerateResourceType(); - GrResourceKey key(GrCacheID(domain, keyData), t, 0); + keyData.fData64[0] = 1; + GrResourceKey key1(GrCacheID(domain, keyData), t, 0); + + keyData.fData64[0] = 2; + GrResourceKey key2(GrCacheID(domain, keyData), t, 0); { context->setResourceCacheLimits(3, 30000); @@ -163,15 +310,17 @@ static void test_cache_delete_on_destruction(skiatest::Reporter* reporter, TestResource* a = new TestResource(context->getGpu()); TestResource* b = new TestResource(context->getGpu()); - cache->addResource(key, a); - cache->addResource(key, b); + cache->addResource(key1, a); + cache->addResource(key2, b); a->setDeleteWhenDestroyed(cache, b); b->setDeleteWhenDestroyed(cache, a); a->unref(); b->unref(); + REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive()); + cache->purgeAllUnlocked(); REPORTER_ASSERT(reporter, 0 == TestResource::NumAlive()); } @@ -180,10 +329,11 @@ static void test_cache_delete_on_destruction(skiatest::Reporter* reporter, 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()); - cache->addResource(key, a); - cache->addResource(key, b); + cache->addResource(key1, a); + cache->addResource(key2, b); a->setDeleteWhenDestroyed(cache, b); b->setDeleteWhenDestroyed(cache, a); @@ -192,13 +342,17 @@ static void test_cache_delete_on_destruction(skiatest::Reporter* reporter, b->unref(); cache->deleteResource(a->getCacheEntry()); - REPORTER_ASSERT(reporter, 0 == TestResource::NumAlive()); } } -static void test_resource_size_changed(skiatest::Reporter* reporter, - GrContext* context) { +static void test_resource_size_changed(skiatest::Reporter* reporter) { + SkAutoTUnref<GrContext> context(GrContext::CreateMockContext()); + REPORTER_ASSERT(reporter, SkToBool(context)); + if (NULL == context) { + return; + } + GrCacheID::Domain domain = GrCacheID::GenerateDomain(); GrResourceKey::ResourceType t = GrResourceKey::GenerateResourceType(); @@ -216,6 +370,7 @@ static void test_resource_size_changed(skiatest::Reporter* reporter, { context->setResourceCacheLimits(3, 30000); GrResourceCache* cache = context->getResourceCache(); + GrResourceCache2* cache2 = context->getResourceCache2(); cache->purgeAllUnlocked(); SkASSERT(0 == cache->getCachedResourceCount() && 0 == cache->getCachedResourceBytes()); @@ -231,9 +386,12 @@ static void test_resource_size_changed(skiatest::Reporter* reporter, REPORTER_ASSERT(reporter, 200 == cache->getCachedResourceBytes()); REPORTER_ASSERT(reporter, 2 == cache->getCachedResourceCount()); - - static_cast<TestResource*>(cache->find(key2))->setSize(200); - static_cast<TestResource*>(cache->find(key1))->setSize(50); + { + SkAutoTUnref<TestResource> find2(static_cast<TestResource*>(cache2->findAndRefContentResource(key2))); + find2->setSize(200); + SkAutoTUnref<TestResource> find1(static_cast<TestResource*>(cache2->findAndRefContentResource(key1))); + find1->setSize(50); + } REPORTER_ASSERT(reporter, 250 == cache->getCachedResourceBytes()); REPORTER_ASSERT(reporter, 2 == cache->getCachedResourceCount()); @@ -243,22 +401,28 @@ static void test_resource_size_changed(skiatest::Reporter* reporter, { context->setResourceCacheLimits(2, 300); GrResourceCache* cache = context->getResourceCache(); + GrResourceCache2* cache2 = context->getResourceCache2(); cache->purgeAllUnlocked(); SkASSERT(0 == cache->getCachedResourceCount() && 0 == cache->getCachedResourceBytes()); - TestResource* a = new TestResource(context->getGpu(), 100); + TestResource* a = new TestResource(context->getGpu()); + a->setSize(100); cache->addResource(key1, a); a->unref(); - TestResource* b = new TestResource(context->getGpu(), 100); + TestResource* b = new TestResource(context->getGpu()); + b->setSize(100); cache->addResource(key2, b); b->unref(); REPORTER_ASSERT(reporter, 200 == cache->getCachedResourceBytes()); REPORTER_ASSERT(reporter, 2 == cache->getCachedResourceCount()); - static_cast<TestResource*>(cache->find(key2))->setSize(201); - REPORTER_ASSERT(reporter, !cache->hasKey(key1)); + { + SkAutoTUnref<TestResource> find2(static_cast<TestResource*>(cache2->findAndRefContentResource(key2))); + find2->setSize(201); + } + REPORTER_ASSERT(reporter, !cache2->hasContentKey(key1)); REPORTER_ASSERT(reporter, 201 == cache->getCachedResourceBytes()); REPORTER_ASSERT(reporter, 1 == cache->getCachedResourceCount()); @@ -286,16 +450,12 @@ DEF_GPUTEST(ResourceCache, reporter, factory) { test_cache(reporter, context, surface->getCanvas()); } - // The below tests use a mock context. - SkAutoTUnref<GrContext> context(GrContext::CreateMockContext()); - REPORTER_ASSERT(reporter, SkToBool(context)); - if (NULL == context) { - return; - } - - test_purge_invalidated(reporter, context); - test_cache_delete_on_destruction(reporter, context); - test_resource_size_changed(reporter, context); + // The below tests create their own mock contexts. + test_duplicate_content_key(reporter); + test_duplicate_scratch_key(reporter); + test_purge_invalidated(reporter); + test_cache_delete_on_destruction(reporter); + test_resource_size_changed(reporter); } #endif |