| /* |
| * 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 "GrContextFactory.h" |
| #include "GrResourceCache.h" |
| #include "SkGpuDevice.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->getGpuTextureCacheBytes(); |
| |
| int oldMaxNum; |
| size_t oldMaxBytes; |
| context->getTextureCacheLimits(&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->setTextureCacheLimits(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->getGpuTextureCacheBytes(); |
| |
| // we should never go over the size limit |
| REPORTER_ASSERT(reporter, curCacheSize <= maxCacheSize); |
| } |
| |
| context->setTextureCacheLimits(oldMaxNum, oldMaxBytes); |
| } |
| |
| class TestResource : public GrResource { |
| public: |
| SK_DECLARE_INST_COUNT(TestResource); |
| explicit TestResource(GrGpu* gpu) |
| : INHERITED(gpu, false) |
| , fCache(NULL) |
| , fToDelete(NULL) { |
| ++fAlive; |
| } |
| |
| ~TestResource() { |
| --fAlive; |
| if (NULL != fToDelete) { |
| // Breaks our little 2-element cycle below. |
| fToDelete->setDeleteWhenDestroyed(NULL, NULL); |
| fCache->deleteResource(fToDelete->getCacheEntry()); |
| } |
| this->release(); |
| } |
| |
| size_t sizeInBytes() const SK_OVERRIDE { return 100; } |
| |
| static int alive() { return fAlive; } |
| |
| void setDeleteWhenDestroyed(GrResourceCache* cache, TestResource* resource) { |
| fCache = cache; |
| fToDelete = resource; |
| } |
| |
| private: |
| GrResourceCache* fCache; |
| TestResource* fToDelete; |
| static int fAlive; |
| |
| typedef GrResource 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. |
| GrResource* 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()); |
| } |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| 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; |
| |
| SkAutoTUnref<GrTexture> texture(context->createUncachedTexture(desc, NULL, 0)); |
| SkAutoTUnref<SkGpuDevice> device(SkNEW_ARGS(SkGpuDevice, (context, texture.get()))); |
| SkCanvas canvas(device.get()); |
| |
| test_cache(reporter, context, &canvas); |
| test_purge_invalidated(reporter, context); |
| test_cache_delete_on_destruction(reporter, context); |
| } |
| } |
| |
| #endif |