1 /* 2 * Copyright 2013 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8 #if SK_SUPPORT_GPU 9 10 #include "SkCanvas.h" 11 #include "GrContextFactory.h" 12 #include "GrResourceCache.h" 13 #include "SkSurface.h" 14 #include "Test.h" 15 16 static const int gWidth = 640; 17 static const int gHeight = 480; 18 19 //////////////////////////////////////////////////////////////////////////////// 20 static void test_cache(skiatest::Reporter* reporter, 21 GrContext* context, 22 SkCanvas* canvas) { 23 const SkIRect size = SkIRect::MakeWH(gWidth, gHeight); 24 25 SkBitmap src; 26 src.allocN32Pixels(size.width(), size.height()); 27 src.eraseColor(SK_ColorBLACK); 28 size_t srcSize = src.getSize(); 29 30 size_t initialCacheSize; 31 context->getResourceCacheUsage(NULL, &initialCacheSize); 32 33 int oldMaxNum; 34 size_t oldMaxBytes; 35 context->getResourceCacheLimits(&oldMaxNum, &oldMaxBytes); 36 37 // Set the cache limits so we can fit 10 "src" images and the 38 // max number of textures doesn't matter 39 size_t maxCacheSize = initialCacheSize + 10*srcSize; 40 context->setResourceCacheLimits(1000, maxCacheSize); 41 42 SkBitmap readback; 43 readback.allocN32Pixels(size.width(), size.height()); 44 45 for (int i = 0; i < 100; ++i) { 46 canvas->drawBitmap(src, 0, 0); 47 canvas->readPixels(size, &readback); 48 49 // "modify" the src texture 50 src.notifyPixelsChanged(); 51 52 size_t curCacheSize; 53 context->getResourceCacheUsage(NULL, &curCacheSize); 54 55 // we should never go over the size limit 56 REPORTER_ASSERT(reporter, curCacheSize <= maxCacheSize); 57 } 58 59 context->setResourceCacheLimits(oldMaxNum, oldMaxBytes); 60 } 61 62 class TestResource : public GrGpuResource { 63 static const size_t kDefaultSize = 100; 64 65 public: 66 SK_DECLARE_INST_COUNT(TestResource); 67 TestResource(GrGpu* gpu, size_t size = kDefaultSize) 68 : INHERITED(gpu, false) 69 , fCache(NULL) 70 , fToDelete(NULL) 71 , fSize(size) { 72 ++fAlive; 73 this->registerWithCache(); 74 } 75 76 ~TestResource() { 77 --fAlive; 78 if (fToDelete) { 79 // Breaks our little 2-element cycle below. 80 fToDelete->setDeleteWhenDestroyed(NULL, NULL); 81 fCache->deleteResource(fToDelete->getCacheEntry()); 82 } 83 this->release(); 84 } 85 86 void setSize(size_t size) { 87 fSize = size; 88 this->didChangeGpuMemorySize(); 89 } 90 91 size_t gpuMemorySize() const SK_OVERRIDE { return fSize; } 92 93 static int alive() { return fAlive; } 94 95 void setDeleteWhenDestroyed(GrResourceCache* cache, TestResource* resource) { 96 fCache = cache; 97 fToDelete = resource; 98 } 99 100 private: 101 GrResourceCache* fCache; 102 TestResource* fToDelete; 103 size_t fSize; 104 static int fAlive; 105 106 typedef GrGpuResource INHERITED; 107 }; 108 int TestResource::fAlive = 0; 109 110 static void test_purge_invalidated(skiatest::Reporter* reporter, GrContext* context) { 111 GrCacheID::Domain domain = GrCacheID::GenerateDomain(); 112 GrCacheID::Key keyData; 113 keyData.fData64[0] = 5; 114 keyData.fData64[1] = 18; 115 GrResourceKey::ResourceType t = GrResourceKey::GenerateResourceType(); 116 GrResourceKey key(GrCacheID(domain, keyData), t, 0); 117 118 GrResourceCache cache(5, 30000); 119 120 // Add two resources with the same key that delete each other from the cache when destroyed. 121 TestResource* a = new TestResource(context->getGpu()); 122 TestResource* b = new TestResource(context->getGpu()); 123 cache.addResource(key, a); 124 cache.addResource(key, b); 125 // Circle back. 126 a->setDeleteWhenDestroyed(&cache, b); 127 b->setDeleteWhenDestroyed(&cache, a); 128 a->unref(); 129 b->unref(); 130 131 // Add a third independent resource also with the same key. 132 GrGpuResource* r = new TestResource(context->getGpu()); 133 cache.addResource(key, r); 134 r->unref(); 135 136 // Invalidate all three, all three should be purged and destroyed. 137 REPORTER_ASSERT(reporter, 3 == TestResource::alive()); 138 const GrResourceInvalidatedMessage msg = { key }; 139 SkMessageBus<GrResourceInvalidatedMessage>::Post(msg); 140 cache.purgeAsNeeded(); 141 REPORTER_ASSERT(reporter, 0 == TestResource::alive()); 142 } 143 144 static void test_cache_delete_on_destruction(skiatest::Reporter* reporter, 145 GrContext* context) { 146 GrCacheID::Domain domain = GrCacheID::GenerateDomain(); 147 GrCacheID::Key keyData; 148 keyData.fData64[0] = 5; 149 keyData.fData64[1] = 0; 150 GrResourceKey::ResourceType t = GrResourceKey::GenerateResourceType(); 151 152 GrResourceKey key(GrCacheID(domain, keyData), t, 0); 153 154 { 155 { 156 GrResourceCache cache(3, 30000); 157 TestResource* a = new TestResource(context->getGpu()); 158 TestResource* b = new TestResource(context->getGpu()); 159 cache.addResource(key, a); 160 cache.addResource(key, b); 161 162 a->setDeleteWhenDestroyed(&cache, b); 163 b->setDeleteWhenDestroyed(&cache, a); 164 165 a->unref(); 166 b->unref(); 167 REPORTER_ASSERT(reporter, 2 == TestResource::alive()); 168 } 169 REPORTER_ASSERT(reporter, 0 == TestResource::alive()); 170 } 171 { 172 GrResourceCache cache(3, 30000); 173 TestResource* a = new TestResource(context->getGpu()); 174 TestResource* b = new TestResource(context->getGpu()); 175 cache.addResource(key, a); 176 cache.addResource(key, b); 177 178 a->setDeleteWhenDestroyed(&cache, b); 179 b->setDeleteWhenDestroyed(&cache, a); 180 181 a->unref(); 182 b->unref(); 183 184 cache.deleteResource(a->getCacheEntry()); 185 186 REPORTER_ASSERT(reporter, 0 == TestResource::alive()); 187 } 188 } 189 190 static void test_resource_size_changed(skiatest::Reporter* reporter, 191 GrContext* context) { 192 GrCacheID::Domain domain = GrCacheID::GenerateDomain(); 193 GrResourceKey::ResourceType t = GrResourceKey::GenerateResourceType(); 194 195 GrCacheID::Key key1Data; 196 key1Data.fData64[0] = 0; 197 key1Data.fData64[1] = 0; 198 GrResourceKey key1(GrCacheID(domain, key1Data), t, 0); 199 200 GrCacheID::Key key2Data; 201 key2Data.fData64[0] = 1; 202 key2Data.fData64[1] = 0; 203 GrResourceKey key2(GrCacheID(domain, key2Data), t, 0); 204 205 // Test changing resources sizes (both increase & decrease). 206 { 207 GrResourceCache cache(2, 300); 208 209 TestResource* a = new TestResource(context->getGpu()); 210 a->setSize(100); // Test didChangeGpuMemorySize() when not in the cache. 211 cache.addResource(key1, a); 212 a->unref(); 213 214 TestResource* b = new TestResource(context->getGpu()); 215 b->setSize(100); 216 cache.addResource(key2, b); 217 b->unref(); 218 219 REPORTER_ASSERT(reporter, 200 == cache.getCachedResourceBytes()); 220 REPORTER_ASSERT(reporter, 2 == cache.getCachedResourceCount()); 221 222 static_cast<TestResource*>(cache.find(key2))->setSize(200); 223 static_cast<TestResource*>(cache.find(key1))->setSize(50); 224 225 REPORTER_ASSERT(reporter, 250 == cache.getCachedResourceBytes()); 226 REPORTER_ASSERT(reporter, 2 == cache.getCachedResourceCount()); 227 } 228 229 // Test increasing a resources size beyond the cache budget. 230 { 231 GrResourceCache cache(2, 300); 232 233 TestResource* a = new TestResource(context->getGpu(), 100); 234 cache.addResource(key1, a); 235 a->unref(); 236 237 TestResource* b = new TestResource(context->getGpu(), 100); 238 cache.addResource(key2, b); 239 b->unref(); 240 241 REPORTER_ASSERT(reporter, 200 == cache.getCachedResourceBytes()); 242 REPORTER_ASSERT(reporter, 2 == cache.getCachedResourceCount()); 243 244 static_cast<TestResource*>(cache.find(key2))->setSize(201); 245 REPORTER_ASSERT(reporter, NULL == cache.find(key1)); 246 247 REPORTER_ASSERT(reporter, 201 == cache.getCachedResourceBytes()); 248 REPORTER_ASSERT(reporter, 1 == cache.getCachedResourceCount()); 249 } 250 251 // Test changing the size of an exclusively-held resource. 252 { 253 GrResourceCache cache(2, 300); 254 255 TestResource* a = new TestResource(context->getGpu(), 100); 256 cache.addResource(key1, a); 257 cache.makeExclusive(a->getCacheEntry()); 258 259 TestResource* b = new TestResource(context->getGpu(), 100); 260 cache.addResource(key2, b); 261 b->unref(); 262 263 REPORTER_ASSERT(reporter, 200 == cache.getCachedResourceBytes()); 264 REPORTER_ASSERT(reporter, 2 == cache.getCachedResourceCount()); 265 REPORTER_ASSERT(reporter, NULL == cache.find(key1)); 266 267 a->setSize(200); 268 269 REPORTER_ASSERT(reporter, 300 == cache.getCachedResourceBytes()); 270 REPORTER_ASSERT(reporter, 2 == cache.getCachedResourceCount()); 271 // Internal resource cache validation will test the detached size (debug mode only). 272 273 cache.makeNonExclusive(a->getCacheEntry()); 274 a->unref(); 275 276 REPORTER_ASSERT(reporter, 300 == cache.getCachedResourceBytes()); 277 REPORTER_ASSERT(reporter, 2 == cache.getCachedResourceCount()); 278 REPORTER_ASSERT(reporter, cache.find(key1)); 279 // Internal resource cache validation will test the detached size (debug mode only). 280 } 281 } 282 283 //////////////////////////////////////////////////////////////////////////////// 284 DEF_GPUTEST(ResourceCache, reporter, factory) { 285 for (int type = 0; type < GrContextFactory::kLastGLContextType; ++type) { 286 GrContextFactory::GLContextType glType = static_cast<GrContextFactory::GLContextType>(type); 287 if (!GrContextFactory::IsRenderingGLContext(glType)) { 288 continue; 289 } 290 GrContext* context = factory->get(glType); 291 if (NULL == context) { 292 continue; 293 } 294 295 GrTextureDesc desc; 296 desc.fConfig = kSkia8888_GrPixelConfig; 297 desc.fFlags = kRenderTarget_GrTextureFlagBit; 298 desc.fWidth = gWidth; 299 desc.fHeight = gHeight; 300 SkImageInfo info = SkImageInfo::MakeN32Premul(gWidth, gHeight); 301 SkAutoTUnref<SkSurface> surface(SkSurface::NewRenderTarget(context, info)); 302 303 test_cache(reporter, context, surface->getCanvas()); 304 test_purge_invalidated(reporter, context); 305 test_cache_delete_on_destruction(reporter, context); 306 test_resource_size_changed(reporter, context); 307 } 308 } 309 310 #endif 311