1 /* 2 * Copyright 2014 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 "GrContext.h" 11 #include "GrLayerCache.h" 12 #include "GrResourceCache.h" 13 #include "SkPictureRecorder.h" 14 #include "Test.h" 15 16 class TestingAccess { 17 public: 18 static int NumPlots() { 19 return GrLayerCache::kNumPlotsX * GrLayerCache::kNumPlotsY; 20 } 21 static SkISize PlotSize() { 22 return SkISize::Make(GrLayerCache::kAtlasTextureWidth / GrLayerCache::kNumPlotsX, 23 GrLayerCache::kAtlasTextureHeight / GrLayerCache::kNumPlotsY); 24 } 25 26 static GrTexture* GetBackingTexture(GrLayerCache* cache) { 27 return cache->fAtlas->getTextureOrNull(); 28 } 29 30 static int NumLayers(GrLayerCache* cache) { 31 return cache->numLayers(); 32 } 33 static void Purge(GrLayerCache* cache, uint32_t pictureID) { 34 cache->purge(pictureID); 35 } 36 static int Uses(GrCachedLayer* layer) { 37 return layer->uses(); 38 } 39 static GrCachedLayer* Find(GrLayerCache* cache, uint32_t pictureID, 40 const SkMatrix& initialMat, 41 const int* key, int keySize) { 42 return cache->findLayer(pictureID, initialMat, key, keySize); 43 } 44 }; 45 46 // Add several layers to the cache 47 static void create_layers(skiatest::Reporter* reporter, 48 GrLayerCache* cache, 49 const SkPicture& picture, 50 int numToAdd, 51 int idOffset) { 52 53 for (int i = 0; i < numToAdd; ++i) { 54 int key[1] = { idOffset+i+1 }; 55 GrCachedLayer* layer = cache->findLayerOrCreate(picture.uniqueID(), 56 idOffset+i+1, idOffset+i+2, 57 SkIRect::MakeEmpty(), 58 SkIRect::MakeEmpty(), 59 SkMatrix::I(), 60 key, 1, 61 nullptr); 62 REPORTER_ASSERT(reporter, layer); 63 GrCachedLayer* temp = TestingAccess::Find(cache, picture.uniqueID(), SkMatrix::I(), 64 key, 1); 65 REPORTER_ASSERT(reporter, temp == layer); 66 67 REPORTER_ASSERT(reporter, TestingAccess::NumLayers(cache) == idOffset + i + 1); 68 69 REPORTER_ASSERT(reporter, picture.uniqueID() == layer->pictureID()); 70 REPORTER_ASSERT(reporter, layer->start() == idOffset + i + 1); 71 REPORTER_ASSERT(reporter, layer->stop() == idOffset + i + 2); 72 REPORTER_ASSERT(reporter, nullptr == layer->texture()); 73 REPORTER_ASSERT(reporter, nullptr == layer->paint()); 74 REPORTER_ASSERT(reporter, !layer->isAtlased()); 75 } 76 } 77 78 static void lock_layer(skiatest::Reporter* reporter, 79 GrLayerCache* cache, 80 GrCachedLayer* layer) { 81 // Make each layer big enough to consume one whole plot in the atlas 82 GrSurfaceDesc desc; 83 desc.fFlags = kRenderTarget_GrSurfaceFlag; 84 desc.fWidth = TestingAccess::PlotSize().fWidth; 85 desc.fHeight = TestingAccess::PlotSize().fHeight; 86 desc.fConfig = kSkia8888_GrPixelConfig; 87 88 bool needsRerendering; 89 bool inAtlas = cache->tryToAtlas(layer, desc, &needsRerendering); 90 if (!inAtlas) { 91 cache->lock(layer, desc, &needsRerendering); 92 } 93 REPORTER_ASSERT(reporter, needsRerendering); 94 95 cache->lock(layer, desc, &needsRerendering); 96 REPORTER_ASSERT(reporter, !needsRerendering); 97 98 REPORTER_ASSERT(reporter, layer->texture()); 99 REPORTER_ASSERT(reporter, layer->locked()); 100 101 cache->addUse(layer); 102 103 REPORTER_ASSERT(reporter, 1 == TestingAccess::Uses(layer)); 104 } 105 106 // This test case exercises the public API of the GrLayerCache class. 107 // In particular it checks its interaction with the resource cache (w.r.t. 108 // locking & unlocking textures). 109 // TODO: need to add checks on VRAM usage! 110 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GpuLayerCache, reporter, context) { 111 // Add one more layer than can fit in the atlas 112 static const int kInitialNumLayers = TestingAccess::NumPlots() + 1; 113 114 #if GR_CACHE_STATS 115 GrResourceCache::Stats stats; 116 #endif 117 118 SkAutoTUnref<const SkPicture> picture; 119 120 { 121 SkPictureRecorder recorder; 122 SkCanvas* c = recorder.beginRecording(1, 1); 123 // Draw something, anything, to prevent an empty-picture optimization, 124 // which is a singleton and never purged. 125 c->drawRect(SkRect::MakeWH(1,1), SkPaint()); 126 picture.reset(recorder.endRecording()); 127 } 128 129 GrResourceCache* resourceCache = context->getResourceCache(); 130 131 GrLayerCache cache(context); 132 133 create_layers(reporter, &cache, *picture, kInitialNumLayers, 0); 134 135 for (int i = 0; i < kInitialNumLayers; ++i) { 136 int key[1] = { i + 1 }; 137 GrCachedLayer* layer = TestingAccess::Find(&cache, picture->uniqueID(), SkMatrix::I(), 138 key, 1); 139 REPORTER_ASSERT(reporter, layer); 140 141 lock_layer(reporter, &cache, layer); 142 143 #if GR_CACHE_STATS 144 resourceCache->getStats(&stats); 145 #endif 146 147 // The first 4 layers should be in the atlas (and thus have non-empty rects) 148 if (i < TestingAccess::NumPlots()) { 149 REPORTER_ASSERT(reporter, layer->isAtlased()); 150 #if GR_CACHE_STATS 151 REPORTER_ASSERT(reporter, 1 == stats.fTotal); 152 #endif 153 } else { 154 // The 5th layer couldn't fit in the atlas 155 REPORTER_ASSERT(reporter, !layer->isAtlased()); 156 #if GR_CACHE_STATS 157 REPORTER_ASSERT(reporter, 2 == stats.fTotal); 158 #endif 159 } 160 } 161 162 // Unlock the textures 163 for (int i = 0; i < kInitialNumLayers; ++i) { 164 int key[1] = { i+1 }; 165 166 GrCachedLayer* layer = TestingAccess::Find(&cache, picture->uniqueID(), SkMatrix::I(), 167 key, 1); 168 REPORTER_ASSERT(reporter, layer); 169 cache.removeUse(layer); 170 } 171 172 #if GR_CACHE_STATS 173 resourceCache->getStats(&stats); 174 REPORTER_ASSERT(reporter, 2 == stats.fTotal); 175 // The floating layer is purgeable the cache is not 176 REPORTER_ASSERT(reporter, 1 == stats.fNumPurgeable); 177 REPORTER_ASSERT(reporter, 1 == stats.fNumNonPurgeable); 178 #endif 179 180 for (int i = 0; i < kInitialNumLayers; ++i) { 181 int key[1] = { i+1 }; 182 183 GrCachedLayer* layer = TestingAccess::Find(&cache, picture->uniqueID(), SkMatrix::I(), 184 key, 1); 185 REPORTER_ASSERT(reporter, layer); 186 187 // All the layers should be unlocked 188 REPORTER_ASSERT(reporter, !layer->locked()); 189 190 // When hoisted layers aren't cached they are aggressively removed 191 // from the atlas 192 #if GR_CACHE_HOISTED_LAYERS 193 // The first 4 layers should still be in the atlas. 194 if (i < 4) { 195 REPORTER_ASSERT(reporter, layer->texture()); 196 REPORTER_ASSERT(reporter, layer->isAtlased()); 197 } else { 198 #endif 199 // The final layer should not be atlased. 200 REPORTER_ASSERT(reporter, nullptr == layer->texture()); 201 REPORTER_ASSERT(reporter, !layer->isAtlased()); 202 #if GR_CACHE_HOISTED_LAYERS 203 } 204 #endif 205 } 206 207 // Let go of the backing texture 208 cache.end(); 209 REPORTER_ASSERT(reporter, nullptr == TestingAccess::GetBackingTexture(&cache)); 210 211 #if GR_CACHE_STATS 212 resourceCache->getStats(&stats); 213 REPORTER_ASSERT(reporter, 2 == stats.fTotal); 214 // Now both the floater and the atlas are purgeable 215 REPORTER_ASSERT(reporter, 2 == stats.fNumPurgeable); 216 #endif 217 218 // re-attach to the backing texture 219 cache.begin(); 220 REPORTER_ASSERT(reporter, TestingAccess::GetBackingTexture(&cache)); 221 222 #if GR_CACHE_STATS 223 resourceCache->getStats(&stats); 224 REPORTER_ASSERT(reporter, 2 == stats.fTotal); 225 // The atlas is restored to being non-purgeable 226 REPORTER_ASSERT(reporter, 1 == stats.fNumPurgeable); 227 REPORTER_ASSERT(reporter, 1 == stats.fNumNonPurgeable); 228 #endif 229 230 { 231 int key[1] = { kInitialNumLayers+1 }; 232 233 // Add an additional layer. Since all the layers are unlocked this 234 // will force out the first atlased layer 235 create_layers(reporter, &cache, *picture, 1, kInitialNumLayers); 236 GrCachedLayer* layer = TestingAccess::Find(&cache, picture->uniqueID(), SkMatrix::I(), 237 key, 1); 238 REPORTER_ASSERT(reporter, layer); 239 240 lock_layer(reporter, &cache, layer); 241 cache.removeUse(layer); 242 } 243 244 for (int i = 0; i < kInitialNumLayers+1; ++i) { 245 int key[1] = { i+1 }; 246 247 GrCachedLayer* layer = TestingAccess::Find(&cache, picture->uniqueID(), SkMatrix::I(), 248 key, 1); 249 #if GR_CACHE_HOISTED_LAYERS 250 // 3 old layers plus the new one should be in the atlas. 251 if (1 == i || 2 == i || 3 == i || 5 == i) { 252 REPORTER_ASSERT(reporter, layer); 253 REPORTER_ASSERT(reporter, !layer->locked()); 254 REPORTER_ASSERT(reporter, layer->texture()); 255 REPORTER_ASSERT(reporter, layer->isAtlased()); 256 } else if (4 == i) { 257 #endif 258 // The one that was never atlased should still be around 259 REPORTER_ASSERT(reporter, layer); 260 261 REPORTER_ASSERT(reporter, nullptr == layer->texture()); 262 REPORTER_ASSERT(reporter, !layer->isAtlased()); 263 #if GR_CACHE_HOISTED_LAYERS 264 } else { 265 // The one bumped out of the atlas (i.e., 0) should be gone 266 REPORTER_ASSERT(reporter, nullptr == layer); 267 } 268 #endif 269 } 270 271 //-------------------------------------------------------------------- 272 // Free them all SkGpuDevice-style. This will not free up the 273 // atlas' texture but will eliminate all the layers. 274 TestingAccess::Purge(&cache, picture->uniqueID()); 275 276 REPORTER_ASSERT(reporter, TestingAccess::NumLayers(&cache) == 0); 277 278 #if GR_CACHE_STATS 279 resourceCache->getStats(&stats); 280 REPORTER_ASSERT(reporter, 2 == stats.fTotal); 281 // Atlas isn't purgeable 282 REPORTER_ASSERT(reporter, 1 == stats.fNumPurgeable); 283 REPORTER_ASSERT(reporter, 1 == stats.fNumNonPurgeable); 284 #endif 285 286 //-------------------------------------------------------------------- 287 // Test out the GrContext-style purge. This should remove all the layers 288 // and the atlas. 289 // Re-create the layers 290 create_layers(reporter, &cache, *picture, kInitialNumLayers, 0); 291 292 // Free them again GrContext-style. This should free up everything. 293 cache.freeAll(); 294 295 REPORTER_ASSERT(reporter, TestingAccess::NumLayers(&cache) == 0); 296 297 REPORTER_ASSERT(reporter, nullptr == TestingAccess::GetBackingTexture(&cache)); 298 299 #if GR_CACHE_STATS 300 resourceCache->getStats(&stats); 301 REPORTER_ASSERT(reporter, 2 == stats.fTotal); 302 REPORTER_ASSERT(reporter, 2 == stats.fNumPurgeable); 303 #endif 304 305 // Purge the resource cache ... 306 resourceCache->purgeAllUnlocked(); 307 308 #if GR_CACHE_STATS 309 resourceCache->getStats(&stats); 310 REPORTER_ASSERT(reporter, 0 == stats.fTotal); 311 #endif 312 313 // and try to re-attach to the backing texture. This should fail 314 cache.begin(); 315 REPORTER_ASSERT(reporter, nullptr == TestingAccess::GetBackingTexture(&cache)); 316 317 //-------------------------------------------------------------------- 318 // Test out the MessageBus-style purge. This will not free the atlas 319 // but should eliminate the free-floating layers. 320 create_layers(reporter, &cache, *picture, kInitialNumLayers, 0); 321 322 // Allocate/use the layers 323 for (int i = 0; i < kInitialNumLayers; ++i) { 324 int key[1] = { i + 1 }; 325 GrCachedLayer* layer = TestingAccess::Find(&cache, picture->uniqueID(), SkMatrix::I(), 326 key, 1); 327 REPORTER_ASSERT(reporter, layer); 328 329 lock_layer(reporter, &cache, layer); 330 } 331 332 #if GR_CACHE_STATS 333 resourceCache->getStats(&stats); 334 REPORTER_ASSERT(reporter, 2 == stats.fTotal); 335 REPORTER_ASSERT(reporter, 2 == stats.fNumNonPurgeable); 336 #endif 337 338 // Unlock the textures 339 for (int i = 0; i < kInitialNumLayers; ++i) { 340 int key[1] = { i+1 }; 341 342 GrCachedLayer* layer = TestingAccess::Find(&cache, picture->uniqueID(), SkMatrix::I(), 343 key, 1); 344 REPORTER_ASSERT(reporter, layer); 345 cache.removeUse(layer); 346 } 347 348 picture.reset(nullptr); 349 cache.processDeletedPictures(); 350 351 REPORTER_ASSERT(reporter, TestingAccess::NumLayers(&cache) == 0); 352 353 #if GR_CACHE_STATS 354 resourceCache->getStats(&stats); 355 REPORTER_ASSERT(reporter, 2 == stats.fTotal); 356 REPORTER_ASSERT(reporter, 1 == stats.fNumPurgeable); 357 REPORTER_ASSERT(reporter, 1 == stats.fNumNonPurgeable); 358 #endif 359 360 cache.end(); 361 362 #if GR_CACHE_STATS 363 resourceCache->getStats(&stats); 364 REPORTER_ASSERT(reporter, 2 == stats.fTotal); 365 REPORTER_ASSERT(reporter, 2 == stats.fNumPurgeable); 366 #endif 367 } 368 369 #endif 370