| /* |
| * Copyright 2013 Google Inc. |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #if SK_SUPPORT_GPU |
| |
| #include "SkCanvas.h" |
| #include "GrContextFactory.h" |
| #include "GrResourceCache.h" |
| #include "SkSurface.h" |
| #include "Test.h" |
| |
| static const int gWidth = 640; |
| static const int gHeight = 480; |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| static void test_cache(skiatest::Reporter* reporter, |
| GrContext* context, |
| SkCanvas* canvas) { |
| const SkIRect size = SkIRect::MakeWH(gWidth, gHeight); |
| |
| SkBitmap src; |
| src.allocN32Pixels(size.width(), size.height()); |
| src.eraseColor(SK_ColorBLACK); |
| size_t srcSize = src.getSize(); |
| |
| size_t initialCacheSize; |
| context->getResourceCacheUsage(NULL, &initialCacheSize); |
| |
| int oldMaxNum; |
| size_t oldMaxBytes; |
| context->getResourceCacheLimits(&oldMaxNum, &oldMaxBytes); |
| |
| // Set the cache limits so we can fit 10 "src" images and the |
| // max number of textures doesn't matter |
| size_t maxCacheSize = initialCacheSize + 10*srcSize; |
| context->setResourceCacheLimits(1000, maxCacheSize); |
| |
| SkBitmap readback; |
| readback.allocN32Pixels(size.width(), size.height()); |
| |
| for (int i = 0; i < 100; ++i) { |
| canvas->drawBitmap(src, 0, 0); |
| canvas->readPixels(size, &readback); |
| |
| // "modify" the src texture |
| src.notifyPixelsChanged(); |
| |
| size_t curCacheSize; |
| context->getResourceCacheUsage(NULL, &curCacheSize); |
| |
| // we should never go over the size limit |
| REPORTER_ASSERT(reporter, curCacheSize <= maxCacheSize); |
| } |
| |
| context->setResourceCacheLimits(oldMaxNum, oldMaxBytes); |
| } |
| |
| class TestResource : public GrGpuResource { |
| static const size_t kDefaultSize = 100; |
| |
| public: |
| SK_DECLARE_INST_COUNT(TestResource); |
| TestResource(GrGpu* gpu, size_t size = kDefaultSize) |
| : INHERITED(gpu, false) |
| , fCache(NULL) |
| , fToDelete(NULL) |
| , fSize(size) { |
| ++fAlive; |
| this->registerWithCache(); |
| } |
| |
| ~TestResource() { |
| --fAlive; |
| if (fToDelete) { |
| // Breaks our little 2-element cycle below. |
| fToDelete->setDeleteWhenDestroyed(NULL, NULL); |
| fCache->deleteResource(fToDelete->getCacheEntry()); |
| } |
| this->release(); |
| } |
| |
| void setSize(size_t size) { |
| fSize = size; |
| this->didChangeGpuMemorySize(); |
| } |
| |
| size_t gpuMemorySize() const SK_OVERRIDE { return fSize; } |
| |
| static int alive() { return fAlive; } |
| |
| void setDeleteWhenDestroyed(GrResourceCache* cache, TestResource* resource) { |
| fCache = cache; |
| fToDelete = resource; |
| } |
| |
| private: |
| GrResourceCache* fCache; |
| TestResource* fToDelete; |
| size_t fSize; |
| static int fAlive; |
| |
| typedef GrGpuResource INHERITED; |
| }; |
| int TestResource::fAlive = 0; |
| |
| static void test_purge_invalidated(skiatest::Reporter* reporter, GrContext* context) { |
| GrCacheID::Domain domain = GrCacheID::GenerateDomain(); |
| GrCacheID::Key keyData; |
| keyData.fData64[0] = 5; |
| keyData.fData64[1] = 18; |
| GrResourceKey::ResourceType t = GrResourceKey::GenerateResourceType(); |
| GrResourceKey key(GrCacheID(domain, keyData), t, 0); |
| |
| GrResourceCache cache(5, 30000); |
| |
| // Add two resources with the same key that delete each other from the cache when destroyed. |
| 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); |
| a->unref(); |
| b->unref(); |
| |
| // Add a third independent resource also with the same key. |
| GrGpuResource* r = new TestResource(context->getGpu()); |
| cache.addResource(key, r); |
| r->unref(); |
| |
| // Invalidate all three, all three should be purged and destroyed. |
| REPORTER_ASSERT(reporter, 3 == TestResource::alive()); |
| const GrResourceInvalidatedMessage msg = { key }; |
| SkMessageBus<GrResourceInvalidatedMessage>::Post(msg); |
| cache.purgeAsNeeded(); |
| REPORTER_ASSERT(reporter, 0 == TestResource::alive()); |
| } |
| |
| static void test_cache_delete_on_destruction(skiatest::Reporter* reporter, |
| GrContext* context) { |
| GrCacheID::Domain domain = GrCacheID::GenerateDomain(); |
| GrCacheID::Key keyData; |
| keyData.fData64[0] = 5; |
| keyData.fData64[1] = 0; |
| GrResourceKey::ResourceType t = GrResourceKey::GenerateResourceType(); |
| |
| GrResourceKey key(GrCacheID(domain, keyData), t, 0); |
| |
| { |
| { |
| GrResourceCache cache(3, 30000); |
| TestResource* a = new TestResource(context->getGpu()); |
| TestResource* b = new TestResource(context->getGpu()); |
| cache.addResource(key, a); |
| cache.addResource(key, b); |
| |
| a->setDeleteWhenDestroyed(&cache, b); |
| b->setDeleteWhenDestroyed(&cache, a); |
| |
| a->unref(); |
| b->unref(); |
| REPORTER_ASSERT(reporter, 2 == TestResource::alive()); |
| } |
| REPORTER_ASSERT(reporter, 0 == TestResource::alive()); |
| } |
| { |
| GrResourceCache cache(3, 30000); |
| TestResource* a = new TestResource(context->getGpu()); |
| TestResource* b = new TestResource(context->getGpu()); |
| cache.addResource(key, a); |
| cache.addResource(key, b); |
| |
| a->setDeleteWhenDestroyed(&cache, b); |
| b->setDeleteWhenDestroyed(&cache, a); |
| |
| a->unref(); |
| b->unref(); |
| |
| cache.deleteResource(a->getCacheEntry()); |
| |
| REPORTER_ASSERT(reporter, 0 == TestResource::alive()); |
| } |
| } |
| |
| static void test_resource_size_changed(skiatest::Reporter* reporter, |
| GrContext* context) { |
| GrCacheID::Domain domain = GrCacheID::GenerateDomain(); |
| GrResourceKey::ResourceType t = GrResourceKey::GenerateResourceType(); |
| |
| GrCacheID::Key key1Data; |
| key1Data.fData64[0] = 0; |
| key1Data.fData64[1] = 0; |
| GrResourceKey key1(GrCacheID(domain, key1Data), t, 0); |
| |
| GrCacheID::Key key2Data; |
| key2Data.fData64[0] = 1; |
| key2Data.fData64[1] = 0; |
| GrResourceKey key2(GrCacheID(domain, key2Data), t, 0); |
| |
| // Test changing resources sizes (both increase & decrease). |
| { |
| GrResourceCache cache(2, 300); |
| |
| TestResource* a = new TestResource(context->getGpu()); |
| a->setSize(100); // Test didChangeGpuMemorySize() when not in the cache. |
| cache.addResource(key1, a); |
| a->unref(); |
| |
| 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(200); |
| static_cast<TestResource*>(cache.find(key1))->setSize(50); |
| |
| REPORTER_ASSERT(reporter, 250 == cache.getCachedResourceBytes()); |
| REPORTER_ASSERT(reporter, 2 == cache.getCachedResourceCount()); |
| } |
| |
| // Test increasing a resources size beyond the cache budget. |
| { |
| GrResourceCache cache(2, 300); |
| |
| TestResource* a = new TestResource(context->getGpu(), 100); |
| cache.addResource(key1, a); |
| a->unref(); |
| |
| TestResource* b = new TestResource(context->getGpu(), 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, NULL == cache.find(key1)); |
| |
| REPORTER_ASSERT(reporter, 201 == cache.getCachedResourceBytes()); |
| REPORTER_ASSERT(reporter, 1 == cache.getCachedResourceCount()); |
| } |
| |
| // Test changing the size of an exclusively-held resource. |
| { |
| GrResourceCache cache(2, 300); |
| |
| TestResource* a = new TestResource(context->getGpu(), 100); |
| cache.addResource(key1, a); |
| cache.makeExclusive(a->getCacheEntry()); |
| |
| TestResource* b = new TestResource(context->getGpu(), 100); |
| cache.addResource(key2, b); |
| b->unref(); |
| |
| REPORTER_ASSERT(reporter, 200 == cache.getCachedResourceBytes()); |
| REPORTER_ASSERT(reporter, 2 == cache.getCachedResourceCount()); |
| REPORTER_ASSERT(reporter, NULL == cache.find(key1)); |
| |
| a->setSize(200); |
| |
| REPORTER_ASSERT(reporter, 300 == cache.getCachedResourceBytes()); |
| REPORTER_ASSERT(reporter, 2 == cache.getCachedResourceCount()); |
| // Internal resource cache validation will test the detached size (debug mode only). |
| |
| cache.makeNonExclusive(a->getCacheEntry()); |
| a->unref(); |
| |
| REPORTER_ASSERT(reporter, 300 == cache.getCachedResourceBytes()); |
| REPORTER_ASSERT(reporter, 2 == cache.getCachedResourceCount()); |
| REPORTER_ASSERT(reporter, cache.find(key1)); |
| // Internal resource cache validation will test the detached size (debug mode only). |
| } |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| DEF_GPUTEST(ResourceCache, reporter, factory) { |
| for (int type = 0; type < GrContextFactory::kLastGLContextType; ++type) { |
| GrContextFactory::GLContextType glType = static_cast<GrContextFactory::GLContextType>(type); |
| if (!GrContextFactory::IsRenderingGLContext(glType)) { |
| continue; |
| } |
| GrContext* context = factory->get(glType); |
| if (NULL == context) { |
| continue; |
| } |
| |
| GrTextureDesc desc; |
| desc.fConfig = kSkia8888_GrPixelConfig; |
| desc.fFlags = kRenderTarget_GrTextureFlagBit; |
| desc.fWidth = gWidth; |
| desc.fHeight = gHeight; |
| SkImageInfo info = SkImageInfo::MakeN32Premul(gWidth, gHeight); |
| SkAutoTUnref<SkSurface> surface(SkSurface::NewRenderTarget(context, info)); |
| |
| test_cache(reporter, context, surface->getCanvas()); |
| test_purge_invalidated(reporter, context); |
| test_cache_delete_on_destruction(reporter, context); |
| test_resource_size_changed(reporter, context); |
| } |
| } |
| |
| #endif |