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 // Include here to ensure SK_SUPPORT_GPU is set correctly before it is examined. 9 #include "SkTypes.h" 10 11 #if SK_SUPPORT_GPU 12 #include <thread> 13 #include "GrContext.h" 14 #include "GrContextPriv.h" 15 #include "GrContextFactory.h" 16 #include "GrGpu.h" 17 #include "GrGpuResourceCacheAccess.h" 18 #include "GrGpuResourcePriv.h" 19 #include "GrProxyProvider.h" 20 #include "GrRenderTargetPriv.h" 21 #include "GrResourceCache.h" 22 #include "GrResourceProvider.h" 23 #include "GrTest.h" 24 #include "GrTexture.h" 25 26 #include "SkCanvas.h" 27 #include "SkGr.h" 28 #include "SkMessageBus.h" 29 #include "SkMipMap.h" 30 #include "SkSurface.h" 31 #include "Test.h" 32 33 static const int gWidth = 640; 34 static const int gHeight = 480; 35 36 //////////////////////////////////////////////////////////////////////////////// 37 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ResourceCacheCache, reporter, ctxInfo) { 38 GrContext* context = ctxInfo.grContext(); 39 GrSurfaceDesc desc; 40 desc.fConfig = kRGBA_8888_GrPixelConfig; 41 desc.fFlags = kRenderTarget_GrSurfaceFlag; 42 desc.fWidth = gWidth; 43 desc.fHeight = gHeight; 44 SkImageInfo info = SkImageInfo::MakeN32Premul(gWidth, gHeight); 45 auto surface(SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, info)); 46 SkCanvas* canvas = surface->getCanvas(); 47 48 const SkIRect size = SkIRect::MakeWH(gWidth, gHeight); 49 50 SkBitmap src; 51 src.allocN32Pixels(size.width(), size.height()); 52 src.eraseColor(SK_ColorBLACK); 53 size_t srcSize = src.computeByteSize(); 54 55 size_t initialCacheSize; 56 context->getResourceCacheUsage(nullptr, &initialCacheSize); 57 58 int oldMaxNum; 59 size_t oldMaxBytes; 60 context->getResourceCacheLimits(&oldMaxNum, &oldMaxBytes); 61 62 // Set the cache limits so we can fit 10 "src" images and the 63 // max number of textures doesn't matter 64 size_t maxCacheSize = initialCacheSize + 10*srcSize; 65 context->setResourceCacheLimits(1000, maxCacheSize); 66 67 SkBitmap readback; 68 readback.allocN32Pixels(size.width(), size.height()); 69 70 for (int i = 0; i < 100; ++i) { 71 canvas->drawBitmap(src, 0, 0); 72 surface->readPixels(readback, 0, 0); 73 74 // "modify" the src texture 75 src.notifyPixelsChanged(); 76 77 size_t curCacheSize; 78 context->getResourceCacheUsage(nullptr, &curCacheSize); 79 80 // we should never go over the size limit 81 REPORTER_ASSERT(reporter, curCacheSize <= maxCacheSize); 82 } 83 84 context->setResourceCacheLimits(oldMaxNum, oldMaxBytes); 85 } 86 87 static bool is_rendering_and_not_angle_es3(sk_gpu_test::GrContextFactory::ContextType type) { 88 if (type == sk_gpu_test::GrContextFactory::kANGLE_D3D11_ES3_ContextType || 89 type == sk_gpu_test::GrContextFactory::kANGLE_GL_ES3_ContextType) { 90 return false; 91 } 92 return sk_gpu_test::GrContextFactory::IsRenderingContext(type); 93 } 94 95 static GrStencilAttachment* get_SB(GrRenderTarget* rt) { 96 return rt->renderTargetPriv().getStencilAttachment(); 97 } 98 99 static sk_sp<GrRenderTarget> create_RT_with_SB(GrResourceProvider* provider, 100 int size, int sampleCount, SkBudgeted budgeted) { 101 GrSurfaceDesc desc; 102 desc.fFlags = kRenderTarget_GrSurfaceFlag; 103 desc.fOrigin = kBottomLeft_GrSurfaceOrigin; 104 desc.fWidth = size; 105 desc.fHeight = size; 106 desc.fConfig = kRGBA_8888_GrPixelConfig; 107 desc.fSampleCnt = sampleCount; 108 109 sk_sp<GrTexture> tex(provider->createTexture(desc, budgeted)); 110 if (!tex || !tex->asRenderTarget()) { 111 return nullptr; 112 } 113 114 if (!provider->attachStencilAttachment(tex->asRenderTarget())) { 115 return nullptr; 116 } 117 SkASSERT(get_SB(tex->asRenderTarget())); 118 119 return sk_ref_sp(tex->asRenderTarget()); 120 } 121 122 // This currently fails on ES3 ANGLE contexts 123 DEF_GPUTEST_FOR_CONTEXTS(ResourceCacheStencilBuffers, &is_rendering_and_not_angle_es3, reporter, 124 ctxInfo, nullptr) { 125 GrContext* context = ctxInfo.grContext(); 126 if (context->caps()->avoidStencilBuffers()) { 127 return; 128 } 129 130 GrResourceProvider* resourceProvider = context->contextPriv().resourceProvider(); 131 132 sk_sp<GrRenderTarget> smallRT0 = create_RT_with_SB(resourceProvider, 4, 1, SkBudgeted::kYes); 133 REPORTER_ASSERT(reporter, smallRT0); 134 135 { 136 // Two budgeted RTs with the same desc should share a stencil buffer. 137 sk_sp<GrRenderTarget> smallRT1 = create_RT_with_SB(resourceProvider, 4, 1, SkBudgeted::kYes); 138 REPORTER_ASSERT(reporter, smallRT1); 139 140 REPORTER_ASSERT(reporter, get_SB(smallRT0.get()) == get_SB(smallRT1.get())); 141 } 142 143 { 144 // An unbudgeted RT with the same desc should also share. 145 sk_sp<GrRenderTarget> smallRT2 = create_RT_with_SB(resourceProvider, 4, 1, SkBudgeted::kNo); 146 REPORTER_ASSERT(reporter, smallRT2); 147 148 REPORTER_ASSERT(reporter, get_SB(smallRT0.get()) == get_SB(smallRT2.get())); 149 } 150 151 { 152 // An RT with a much larger size should not share. 153 sk_sp<GrRenderTarget> bigRT = create_RT_with_SB(resourceProvider, 400, 1, SkBudgeted::kNo); 154 REPORTER_ASSERT(reporter, bigRT); 155 156 REPORTER_ASSERT(reporter, get_SB(smallRT0.get()) != get_SB(bigRT.get())); 157 } 158 159 int smallSampleCount = context->caps()->getRenderTargetSampleCount(2, kRGBA_8888_GrPixelConfig); 160 if (smallSampleCount > 1) { 161 // An RT with a different sample count should not share. 162 sk_sp<GrRenderTarget> smallMSAART0 = create_RT_with_SB(resourceProvider, 4, 163 smallSampleCount, SkBudgeted::kNo); 164 #ifdef SK_BUILD_FOR_ANDROID 165 if (!smallMSAART0) { 166 // The nexus player seems to fail to create MSAA textures. 167 return; 168 } 169 #else 170 REPORTER_ASSERT(reporter, smallMSAART0); 171 #endif 172 173 REPORTER_ASSERT(reporter, get_SB(smallRT0.get()) != get_SB(smallMSAART0.get())); 174 175 { 176 // A second MSAA RT should share with the first MSAA RT. 177 sk_sp<GrRenderTarget> smallMSAART1 = create_RT_with_SB(resourceProvider, 4, 178 smallSampleCount, 179 SkBudgeted::kNo); 180 REPORTER_ASSERT(reporter, smallMSAART1); 181 182 REPORTER_ASSERT(reporter, get_SB(smallMSAART0.get()) == get_SB(smallMSAART1.get())); 183 } 184 185 // But one with a larger sample count should not. (Also check that the two requests didn't 186 // rounded up to the same actual sample count or else they could share.). 187 int bigSampleCount = 188 context->caps()->getRenderTargetSampleCount(5, kRGBA_8888_GrPixelConfig); 189 if (bigSampleCount > 0 && bigSampleCount != smallSampleCount) { 190 sk_sp<GrRenderTarget> smallMSAART2 = create_RT_with_SB(resourceProvider, 4, 191 bigSampleCount, 192 SkBudgeted::kNo); 193 REPORTER_ASSERT(reporter, smallMSAART2); 194 195 REPORTER_ASSERT(reporter, get_SB(smallMSAART0.get()) != get_SB(smallMSAART2.get())); 196 } 197 } 198 } 199 200 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ResourceCacheWrappedResources, reporter, ctxInfo) { 201 GrContext* context = ctxInfo.grContext(); 202 GrResourceProvider* resourceProvider = context->contextPriv().resourceProvider(); 203 GrGpu* gpu = context->contextPriv().getGpu(); 204 // this test is only valid for GL 205 if (!gpu || !gpu->glContextForTesting()) { 206 return; 207 } 208 209 GrBackendTexture backendTextures[2]; 210 static const int kW = 100; 211 static const int kH = 100; 212 213 backendTextures[0] = gpu->createTestingOnlyBackendTexture(nullptr, kW, kH, 214 kRGBA_8888_GrPixelConfig, 215 false, GrMipMapped::kNo); 216 backendTextures[1] = gpu->createTestingOnlyBackendTexture(nullptr, kW, kH, 217 kRGBA_8888_GrPixelConfig, 218 false, GrMipMapped::kNo); 219 REPORTER_ASSERT(reporter, backendTextures[0].isValid()); 220 REPORTER_ASSERT(reporter, backendTextures[1].isValid()); 221 if (!backendTextures[0].isValid() || !backendTextures[1].isValid()) { 222 return; 223 } 224 225 context->resetContext(); 226 227 sk_sp<GrTexture> borrowed(resourceProvider->wrapBackendTexture( 228 backendTextures[0], kBorrow_GrWrapOwnership)); 229 230 sk_sp<GrTexture> adopted(resourceProvider->wrapBackendTexture( 231 backendTextures[1], kAdopt_GrWrapOwnership)); 232 233 REPORTER_ASSERT(reporter, borrowed != nullptr && adopted != nullptr); 234 if (!borrowed || !adopted) { 235 return; 236 } 237 238 borrowed.reset(nullptr); 239 adopted.reset(nullptr); 240 241 context->flush(); 242 243 bool borrowedIsAlive = gpu->isTestingOnlyBackendTexture(backendTextures[0]); 244 bool adoptedIsAlive = gpu->isTestingOnlyBackendTexture(backendTextures[1]); 245 246 REPORTER_ASSERT(reporter, borrowedIsAlive); 247 REPORTER_ASSERT(reporter, !adoptedIsAlive); 248 249 gpu->deleteTestingOnlyBackendTexture(&(backendTextures[0]), !borrowedIsAlive); 250 gpu->deleteTestingOnlyBackendTexture(&(backendTextures[1]), !adoptedIsAlive); 251 252 context->resetContext(); 253 } 254 255 class TestResource : public GrGpuResource { 256 enum ScratchConstructor { kScratchConstructor }; 257 public: 258 static const size_t kDefaultSize = 100; 259 260 /** Property that distinctly categorizes the resource. 261 * For example, textures have width, height, ... */ 262 enum SimulatedProperty { kA_SimulatedProperty, kB_SimulatedProperty }; 263 264 TestResource(GrGpu* gpu, SkBudgeted budgeted = SkBudgeted::kYes, size_t size = kDefaultSize) 265 : INHERITED(gpu) 266 , fToDelete(nullptr) 267 , fSize(size) 268 , fProperty(kA_SimulatedProperty) 269 , fIsScratch(false) { 270 ++fNumAlive; 271 this->registerWithCache(budgeted); 272 } 273 274 static TestResource* CreateScratch(GrGpu* gpu, SkBudgeted budgeted, 275 SimulatedProperty property) { 276 return new TestResource(gpu, budgeted, property, kScratchConstructor); 277 } 278 static TestResource* CreateWrapped(GrGpu* gpu, size_t size = kDefaultSize) { 279 return new TestResource(gpu, size); 280 } 281 282 ~TestResource() override { 283 --fNumAlive; 284 SkSafeUnref(fToDelete); 285 } 286 287 void setSize(size_t size) { 288 fSize = size; 289 this->didChangeGpuMemorySize(); 290 } 291 292 static int NumAlive() { return fNumAlive; } 293 294 void setUnrefWhenDestroyed(TestResource* resource) { 295 SkRefCnt_SafeAssign(fToDelete, resource); 296 } 297 298 static void ComputeScratchKey(SimulatedProperty property, GrScratchKey* key) { 299 static GrScratchKey::ResourceType t = GrScratchKey::GenerateResourceType(); 300 GrScratchKey::Builder builder(key, t, kScratchKeyFieldCnt); 301 for (int i = 0; i < kScratchKeyFieldCnt; ++i) { 302 builder[i] = static_cast<uint32_t>(i + property); 303 } 304 } 305 306 static size_t ExpectedScratchKeySize() { 307 return sizeof(uint32_t) * (kScratchKeyFieldCnt + GrScratchKey::kMetaDataCnt); 308 } 309 private: 310 static const int kScratchKeyFieldCnt = 6; 311 312 TestResource(GrGpu* gpu, SkBudgeted budgeted, SimulatedProperty property, ScratchConstructor) 313 : INHERITED(gpu) 314 , fToDelete(nullptr) 315 , fSize(kDefaultSize) 316 , fProperty(property) 317 , fIsScratch(true) { 318 ++fNumAlive; 319 this->registerWithCache(budgeted); 320 } 321 322 // Constructor for simulating resources that wrap backend objects. 323 TestResource(GrGpu* gpu, size_t size) 324 : INHERITED(gpu) 325 , fToDelete(nullptr) 326 , fSize(size) 327 , fProperty(kA_SimulatedProperty) 328 , fIsScratch(false) { 329 ++fNumAlive; 330 this->registerWithCacheWrapped(); 331 } 332 333 void computeScratchKey(GrScratchKey* key) const override { 334 if (fIsScratch) { 335 ComputeScratchKey(fProperty, key); 336 } 337 } 338 339 size_t onGpuMemorySize() const override { return fSize; } 340 const char* getResourceType() const override { return "Test"; } 341 342 TestResource* fToDelete; 343 size_t fSize; 344 static int fNumAlive; 345 SimulatedProperty fProperty; 346 bool fIsScratch; 347 typedef GrGpuResource INHERITED; 348 }; 349 int TestResource::fNumAlive = 0; 350 351 class Mock { 352 public: 353 Mock(int maxCnt, size_t maxBytes) { 354 fContext = GrContext::MakeMock(nullptr); 355 SkASSERT(fContext); 356 fContext->setResourceCacheLimits(maxCnt, maxBytes); 357 GrResourceCache* cache = fContext->contextPriv().getResourceCache(); 358 cache->purgeAllUnlocked(); 359 SkASSERT(0 == cache->getResourceCount() && 0 == cache->getResourceBytes()); 360 } 361 362 GrResourceCache* cache() { return fContext->contextPriv().getResourceCache(); } 363 364 GrContext* context() { return fContext.get(); } 365 366 private: 367 sk_sp<GrContext> fContext; 368 }; 369 370 static void test_no_key(skiatest::Reporter* reporter) { 371 Mock mock(10, 30000); 372 GrContext* context = mock.context(); 373 GrResourceCache* cache = mock.cache(); 374 GrGpu* gpu = context->contextPriv().getGpu(); 375 376 // Create a bunch of resources with no keys 377 TestResource* a = new TestResource(gpu); 378 TestResource* b = new TestResource(gpu); 379 TestResource* c = new TestResource(gpu); 380 TestResource* d = new TestResource(gpu); 381 a->setSize(11); 382 b->setSize(12); 383 c->setSize(13); 384 d->setSize(14); 385 386 REPORTER_ASSERT(reporter, 4 == TestResource::NumAlive()); 387 REPORTER_ASSERT(reporter, 4 == cache->getResourceCount()); 388 REPORTER_ASSERT(reporter, a->gpuMemorySize() + b->gpuMemorySize() + c->gpuMemorySize() + 389 d->gpuMemorySize() == cache->getResourceBytes()); 390 391 // Should be safe to purge without deleting the resources since we still have refs. 392 cache->purgeAllUnlocked(); 393 REPORTER_ASSERT(reporter, 4 == TestResource::NumAlive()); 394 395 // Since the resources have neither unique nor scratch keys, delete immediately upon unref. 396 397 a->unref(); 398 REPORTER_ASSERT(reporter, 3 == TestResource::NumAlive()); 399 REPORTER_ASSERT(reporter, 3 == cache->getResourceCount()); 400 REPORTER_ASSERT(reporter, b->gpuMemorySize() + c->gpuMemorySize() + d->gpuMemorySize() == 401 cache->getResourceBytes()); 402 403 c->unref(); 404 REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive()); 405 REPORTER_ASSERT(reporter, 2 == cache->getResourceCount()); 406 REPORTER_ASSERT(reporter, b->gpuMemorySize() + d->gpuMemorySize() == 407 cache->getResourceBytes()); 408 409 d->unref(); 410 REPORTER_ASSERT(reporter, 1 == TestResource::NumAlive()); 411 REPORTER_ASSERT(reporter, 1 == cache->getResourceCount()); 412 REPORTER_ASSERT(reporter, b->gpuMemorySize() == cache->getResourceBytes()); 413 414 b->unref(); 415 REPORTER_ASSERT(reporter, 0 == TestResource::NumAlive()); 416 REPORTER_ASSERT(reporter, 0 == cache->getResourceCount()); 417 REPORTER_ASSERT(reporter, 0 == cache->getResourceBytes()); 418 } 419 420 // Each integer passed as a template param creates a new domain. 421 template <int> 422 static void make_unique_key(GrUniqueKey* key, int data, const char* tag = nullptr) { 423 static GrUniqueKey::Domain d = GrUniqueKey::GenerateDomain(); 424 GrUniqueKey::Builder builder(key, d, 1, tag); 425 builder[0] = data; 426 } 427 428 static void test_purge_unlocked(skiatest::Reporter* reporter) { 429 Mock mock(10, 30000); 430 GrContext* context = mock.context(); 431 GrResourceCache* cache = mock.cache(); 432 GrGpu* gpu = context->contextPriv().getGpu(); 433 434 // Create two resource w/ a unique key and two w/o but all of which have scratch keys. 435 TestResource* a = TestResource::CreateScratch(gpu, SkBudgeted::kYes, 436 TestResource::kA_SimulatedProperty); 437 a->setSize(11); 438 439 GrUniqueKey uniqueKey; 440 make_unique_key<0>(&uniqueKey, 0); 441 442 TestResource* b = TestResource::CreateScratch(gpu, SkBudgeted::kYes, 443 TestResource::kA_SimulatedProperty); 444 b->setSize(12); 445 b->resourcePriv().setUniqueKey(uniqueKey); 446 447 TestResource* c = TestResource::CreateScratch(gpu, SkBudgeted::kYes, 448 TestResource::kA_SimulatedProperty); 449 c->setSize(13); 450 451 GrUniqueKey uniqueKey2; 452 make_unique_key<0>(&uniqueKey2, 1); 453 454 TestResource* d = TestResource::CreateScratch(gpu, SkBudgeted::kYes, 455 TestResource::kA_SimulatedProperty); 456 d->setSize(14); 457 d->resourcePriv().setUniqueKey(uniqueKey2); 458 459 460 REPORTER_ASSERT(reporter, 4 == TestResource::NumAlive()); 461 REPORTER_ASSERT(reporter, 4 == cache->getResourceCount()); 462 REPORTER_ASSERT(reporter, a->gpuMemorySize() + b->gpuMemorySize() + c->gpuMemorySize() + 463 d->gpuMemorySize() == cache->getResourceBytes()); 464 465 // Should be safe to purge without deleting the resources since we still have refs. 466 cache->purgeUnlockedResources(false); 467 REPORTER_ASSERT(reporter, 4 == TestResource::NumAlive()); 468 469 // Unref them all. Since they all have keys they should remain in the cache. 470 471 a->unref(); 472 b->unref(); 473 c->unref(); 474 d->unref(); 475 REPORTER_ASSERT(reporter, 4 == TestResource::NumAlive()); 476 REPORTER_ASSERT(reporter, 4 == cache->getResourceCount()); 477 REPORTER_ASSERT(reporter, a->gpuMemorySize() + b->gpuMemorySize() + c->gpuMemorySize() + 478 d->gpuMemorySize() == cache->getResourceBytes()); 479 480 // Purge only the two scratch resources 481 cache->purgeUnlockedResources(true); 482 483 REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive()); 484 REPORTER_ASSERT(reporter, 2 == cache->getResourceCount()); 485 REPORTER_ASSERT(reporter, b->gpuMemorySize() + d->gpuMemorySize() == 486 cache->getResourceBytes()); 487 488 // Purge the uniquely keyed resources 489 cache->purgeUnlockedResources(false); 490 491 REPORTER_ASSERT(reporter, 0 == TestResource::NumAlive()); 492 REPORTER_ASSERT(reporter, 0 == cache->getResourceCount()); 493 REPORTER_ASSERT(reporter, 0 == cache->getResourceBytes()); 494 } 495 496 static void test_budgeting(skiatest::Reporter* reporter) { 497 Mock mock(10, 300); 498 GrContext* context = mock.context(); 499 GrResourceCache* cache = mock.cache(); 500 GrGpu* gpu = context->contextPriv().getGpu(); 501 502 GrUniqueKey uniqueKey; 503 make_unique_key<0>(&uniqueKey, 0); 504 505 // Create a scratch, a unique, and a wrapped resource 506 TestResource* scratch = 507 TestResource::CreateScratch(gpu, SkBudgeted::kYes, TestResource::kB_SimulatedProperty); 508 scratch->setSize(10); 509 TestResource* unique = new TestResource(gpu); 510 unique->setSize(11); 511 unique->resourcePriv().setUniqueKey(uniqueKey); 512 TestResource* wrapped = TestResource::CreateWrapped(gpu); 513 wrapped->setSize(12); 514 TestResource* unbudgeted = new TestResource(gpu, SkBudgeted::kNo); 515 unbudgeted->setSize(13); 516 517 // Make sure we can add a unique key to the wrapped resource 518 GrUniqueKey uniqueKey2; 519 make_unique_key<0>(&uniqueKey2, 1); 520 wrapped->resourcePriv().setUniqueKey(uniqueKey2); 521 GrGpuResource* wrappedViaKey = cache->findAndRefUniqueResource(uniqueKey2); 522 REPORTER_ASSERT(reporter, wrappedViaKey != nullptr); 523 524 // Remove the extra ref we just added. 525 wrappedViaKey->unref(); 526 527 // Make sure sizes are as we expect 528 REPORTER_ASSERT(reporter, 4 == cache->getResourceCount()); 529 REPORTER_ASSERT(reporter, scratch->gpuMemorySize() + unique->gpuMemorySize() + 530 wrapped->gpuMemorySize() + unbudgeted->gpuMemorySize() == 531 cache->getResourceBytes()); 532 REPORTER_ASSERT(reporter, 2 == cache->getBudgetedResourceCount()); 533 REPORTER_ASSERT(reporter, scratch->gpuMemorySize() + unique->gpuMemorySize() == 534 cache->getBudgetedResourceBytes()); 535 REPORTER_ASSERT(reporter, 0 == cache->getPurgeableBytes()); 536 537 // Our refs mean that the resources are non purgeable. 538 cache->purgeAllUnlocked(); 539 REPORTER_ASSERT(reporter, 4 == cache->getResourceCount()); 540 REPORTER_ASSERT(reporter, scratch->gpuMemorySize() + unique->gpuMemorySize() + 541 wrapped->gpuMemorySize() + unbudgeted->gpuMemorySize() == 542 cache->getResourceBytes()); 543 REPORTER_ASSERT(reporter, 2 == cache->getBudgetedResourceCount()); 544 REPORTER_ASSERT(reporter, scratch->gpuMemorySize() + unique->gpuMemorySize() == 545 cache->getBudgetedResourceBytes()); 546 REPORTER_ASSERT(reporter, 0 == cache->getPurgeableBytes()); 547 548 // Unreffing the wrapped resource should free it right away. 549 wrapped->unref(); 550 REPORTER_ASSERT(reporter, 3 == cache->getResourceCount()); 551 REPORTER_ASSERT(reporter, scratch->gpuMemorySize() + unique->gpuMemorySize() + 552 unbudgeted->gpuMemorySize() == cache->getResourceBytes()); 553 REPORTER_ASSERT(reporter, 0 == cache->getPurgeableBytes()); 554 555 // Now try freeing the budgeted resources first 556 wrapped = TestResource::CreateWrapped(gpu); 557 scratch->setSize(12); 558 unique->unref(); 559 REPORTER_ASSERT(reporter, 11 == cache->getPurgeableBytes()); 560 cache->purgeAllUnlocked(); 561 REPORTER_ASSERT(reporter, 3 == cache->getResourceCount()); 562 REPORTER_ASSERT(reporter, scratch->gpuMemorySize() + wrapped->gpuMemorySize() + 563 unbudgeted->gpuMemorySize() == cache->getResourceBytes()); 564 REPORTER_ASSERT(reporter, 1 == cache->getBudgetedResourceCount()); 565 REPORTER_ASSERT(reporter, scratch->gpuMemorySize() == cache->getBudgetedResourceBytes()); 566 REPORTER_ASSERT(reporter, 0 == cache->getPurgeableBytes()); 567 568 scratch->unref(); 569 REPORTER_ASSERT(reporter, 12 == cache->getPurgeableBytes()); 570 cache->purgeAllUnlocked(); 571 REPORTER_ASSERT(reporter, 2 == cache->getResourceCount()); 572 REPORTER_ASSERT(reporter, unbudgeted->gpuMemorySize() + wrapped->gpuMemorySize() == 573 cache->getResourceBytes()); 574 REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceCount()); 575 REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceBytes()); 576 REPORTER_ASSERT(reporter, 0 == cache->getPurgeableBytes()); 577 578 wrapped->unref(); 579 REPORTER_ASSERT(reporter, 1 == cache->getResourceCount()); 580 REPORTER_ASSERT(reporter, unbudgeted->gpuMemorySize() == cache->getResourceBytes()); 581 REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceCount()); 582 REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceBytes()); 583 REPORTER_ASSERT(reporter, 0 == cache->getPurgeableBytes()); 584 585 unbudgeted->unref(); 586 REPORTER_ASSERT(reporter, 0 == cache->getResourceCount()); 587 REPORTER_ASSERT(reporter, 0 == cache->getResourceBytes()); 588 REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceCount()); 589 REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceBytes()); 590 REPORTER_ASSERT(reporter, 0 == cache->getPurgeableBytes()); 591 } 592 593 static void test_unbudgeted(skiatest::Reporter* reporter) { 594 Mock mock(10, 30000); 595 GrContext* context = mock.context(); 596 GrResourceCache* cache = mock.cache(); 597 GrGpu* gpu = context->contextPriv().getGpu(); 598 599 GrUniqueKey uniqueKey; 600 make_unique_key<0>(&uniqueKey, 0); 601 602 TestResource* scratch; 603 TestResource* unique; 604 TestResource* wrapped; 605 TestResource* unbudgeted; 606 607 // A large uncached or wrapped resource shouldn't evict anything. 608 scratch = TestResource::CreateScratch(gpu, SkBudgeted::kYes, 609 TestResource::kB_SimulatedProperty); 610 611 scratch->setSize(10); 612 scratch->unref(); 613 REPORTER_ASSERT(reporter, 1 == cache->getResourceCount()); 614 REPORTER_ASSERT(reporter, 10 == cache->getResourceBytes()); 615 REPORTER_ASSERT(reporter, 1 == cache->getBudgetedResourceCount()); 616 REPORTER_ASSERT(reporter, 10 == cache->getBudgetedResourceBytes()); 617 REPORTER_ASSERT(reporter, 10 == cache->getPurgeableBytes()); 618 619 unique = new TestResource(gpu); 620 unique->setSize(11); 621 unique->resourcePriv().setUniqueKey(uniqueKey); 622 unique->unref(); 623 REPORTER_ASSERT(reporter, 2 == cache->getResourceCount()); 624 REPORTER_ASSERT(reporter, 21 == cache->getResourceBytes()); 625 REPORTER_ASSERT(reporter, 2 == cache->getBudgetedResourceCount()); 626 REPORTER_ASSERT(reporter, 21 == cache->getBudgetedResourceBytes()); 627 REPORTER_ASSERT(reporter, 21 == cache->getPurgeableBytes()); 628 629 size_t large = 2 * cache->getResourceBytes(); 630 unbudgeted = new TestResource(gpu, SkBudgeted::kNo, large); 631 REPORTER_ASSERT(reporter, 3 == cache->getResourceCount()); 632 REPORTER_ASSERT(reporter, 21 + large == cache->getResourceBytes()); 633 REPORTER_ASSERT(reporter, 2 == cache->getBudgetedResourceCount()); 634 REPORTER_ASSERT(reporter, 21 == cache->getBudgetedResourceBytes()); 635 REPORTER_ASSERT(reporter, 21 == cache->getPurgeableBytes()); 636 637 unbudgeted->unref(); 638 REPORTER_ASSERT(reporter, 2 == cache->getResourceCount()); 639 REPORTER_ASSERT(reporter, 21 == cache->getResourceBytes()); 640 REPORTER_ASSERT(reporter, 2 == cache->getBudgetedResourceCount()); 641 REPORTER_ASSERT(reporter, 21 == cache->getBudgetedResourceBytes()); 642 REPORTER_ASSERT(reporter, 21 == cache->getPurgeableBytes()); 643 644 wrapped = TestResource::CreateWrapped(gpu, large); 645 REPORTER_ASSERT(reporter, 3 == cache->getResourceCount()); 646 REPORTER_ASSERT(reporter, 21 + large == cache->getResourceBytes()); 647 REPORTER_ASSERT(reporter, 2 == cache->getBudgetedResourceCount()); 648 REPORTER_ASSERT(reporter, 21 == cache->getBudgetedResourceBytes()); 649 REPORTER_ASSERT(reporter, 21 == cache->getPurgeableBytes()); 650 651 wrapped->unref(); 652 REPORTER_ASSERT(reporter, 2 == cache->getResourceCount()); 653 REPORTER_ASSERT(reporter, 21 == cache->getResourceBytes()); 654 REPORTER_ASSERT(reporter, 2 == cache->getBudgetedResourceCount()); 655 REPORTER_ASSERT(reporter, 21 == cache->getBudgetedResourceBytes()); 656 REPORTER_ASSERT(reporter, 21 == cache->getPurgeableBytes()); 657 658 cache->purgeAllUnlocked(); 659 REPORTER_ASSERT(reporter, 0 == cache->getResourceCount()); 660 REPORTER_ASSERT(reporter, 0 == cache->getResourceBytes()); 661 REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceCount()); 662 REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceBytes()); 663 REPORTER_ASSERT(reporter, 0 == cache->getPurgeableBytes()); 664 } 665 666 // This method can't be static because it needs to friended in GrGpuResource::CacheAccess. 667 void test_unbudgeted_to_scratch(skiatest::Reporter* reporter); 668 /*static*/ void test_unbudgeted_to_scratch(skiatest::Reporter* reporter) { 669 Mock mock(10, 300); 670 GrContext* context = mock.context(); 671 GrResourceCache* cache = mock.cache(); 672 GrGpu* gpu = context->contextPriv().getGpu(); 673 674 TestResource* resource = 675 TestResource::CreateScratch(gpu, SkBudgeted::kNo, TestResource::kA_SimulatedProperty); 676 GrScratchKey key; 677 TestResource::ComputeScratchKey(TestResource::kA_SimulatedProperty, &key); 678 679 size_t size = resource->gpuMemorySize(); 680 for (int i = 0; i < 2; ++i) { 681 // Since this resource is unbudgeted, it should not be reachable as scratch. 682 REPORTER_ASSERT(reporter, resource->resourcePriv().getScratchKey() == key); 683 REPORTER_ASSERT(reporter, !resource->cacheAccess().isScratch()); 684 REPORTER_ASSERT(reporter, SkBudgeted::kNo == resource->resourcePriv().isBudgeted()); 685 REPORTER_ASSERT(reporter, nullptr == cache->findAndRefScratchResource(key, TestResource::kDefaultSize, 0)); 686 REPORTER_ASSERT(reporter, 1 == cache->getResourceCount()); 687 REPORTER_ASSERT(reporter, size == cache->getResourceBytes()); 688 REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceCount()); 689 REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceBytes()); 690 REPORTER_ASSERT(reporter, 0 == cache->getPurgeableBytes()); 691 692 // Once it is unrefed, it should become available as scratch. 693 resource->unref(); 694 REPORTER_ASSERT(reporter, 1 == cache->getResourceCount()); 695 REPORTER_ASSERT(reporter, size == cache->getResourceBytes()); 696 REPORTER_ASSERT(reporter, 1 == cache->getBudgetedResourceCount()); 697 REPORTER_ASSERT(reporter, size == cache->getBudgetedResourceBytes()); 698 REPORTER_ASSERT(reporter, size == cache->getPurgeableBytes()); 699 resource = static_cast<TestResource*>(cache->findAndRefScratchResource(key, TestResource::kDefaultSize, 0)); 700 REPORTER_ASSERT(reporter, resource); 701 REPORTER_ASSERT(reporter, resource->resourcePriv().getScratchKey() == key); 702 REPORTER_ASSERT(reporter, resource->cacheAccess().isScratch()); 703 REPORTER_ASSERT(reporter, SkBudgeted::kYes == resource->resourcePriv().isBudgeted()); 704 705 if (0 == i) { 706 // If made unbudgeted, it should return to original state: ref'ed and unbudgeted. Try 707 // the above tests again. 708 resource->resourcePriv().makeUnbudgeted(); 709 } else { 710 // After the second time around, try removing the scratch key 711 resource->resourcePriv().removeScratchKey(); 712 REPORTER_ASSERT(reporter, 1 == cache->getResourceCount()); 713 REPORTER_ASSERT(reporter, size == cache->getResourceBytes()); 714 REPORTER_ASSERT(reporter, 1 == cache->getBudgetedResourceCount()); 715 REPORTER_ASSERT(reporter, size == cache->getBudgetedResourceBytes()); 716 REPORTER_ASSERT(reporter, 0 == cache->getPurgeableBytes()); 717 REPORTER_ASSERT(reporter, !resource->resourcePriv().getScratchKey().isValid()); 718 REPORTER_ASSERT(reporter, !resource->cacheAccess().isScratch()); 719 REPORTER_ASSERT(reporter, SkBudgeted::kYes == resource->resourcePriv().isBudgeted()); 720 721 // now when it is unrefed it should die since it has no key. 722 resource->unref(); 723 REPORTER_ASSERT(reporter, 0 == cache->getResourceCount()); 724 REPORTER_ASSERT(reporter, 0 == cache->getResourceBytes()); 725 REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceCount()); 726 REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceBytes()); 727 REPORTER_ASSERT(reporter, 0 == cache->getPurgeableBytes()); 728 } 729 } 730 } 731 732 static void test_duplicate_scratch_key(skiatest::Reporter* reporter) { 733 Mock mock(5, 30000); 734 GrContext* context = mock.context(); 735 GrResourceCache* cache = mock.cache(); 736 GrGpu* gpu = context->contextPriv().getGpu(); 737 738 // Create two resources that have the same scratch key. 739 TestResource* a = TestResource::CreateScratch(gpu, 740 SkBudgeted::kYes, 741 TestResource::kB_SimulatedProperty); 742 TestResource* b = TestResource::CreateScratch(gpu, 743 SkBudgeted::kYes, 744 TestResource::kB_SimulatedProperty); 745 a->setSize(11); 746 b->setSize(12); 747 GrScratchKey scratchKey1; 748 TestResource::ComputeScratchKey(TestResource::kA_SimulatedProperty, &scratchKey1); 749 // Check for negative case consistency. (leaks upon test failure.) 750 REPORTER_ASSERT(reporter, nullptr == cache->findAndRefScratchResource(scratchKey1, TestResource::kDefaultSize, 0)); 751 752 GrScratchKey scratchKey; 753 TestResource::ComputeScratchKey(TestResource::kB_SimulatedProperty, &scratchKey); 754 755 // Scratch resources are registered with GrResourceCache just by existing. There are 2. 756 REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive()); 757 SkDEBUGCODE(REPORTER_ASSERT(reporter, 2 == cache->countScratchEntriesForKey(scratchKey));) 758 REPORTER_ASSERT(reporter, 2 == cache->getResourceCount()); 759 REPORTER_ASSERT(reporter, a->gpuMemorySize() + b->gpuMemorySize() == 760 cache->getResourceBytes()); 761 762 // Our refs mean that the resources are non purgeable. 763 cache->purgeAllUnlocked(); 764 REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive()); 765 REPORTER_ASSERT(reporter, 2 == cache->getResourceCount()); 766 767 // Unref but don't purge 768 a->unref(); 769 b->unref(); 770 REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive()); 771 SkDEBUGCODE(REPORTER_ASSERT(reporter, 2 == cache->countScratchEntriesForKey(scratchKey));) 772 773 // Purge again. This time resources should be purgeable. 774 cache->purgeAllUnlocked(); 775 REPORTER_ASSERT(reporter, 0 == TestResource::NumAlive()); 776 REPORTER_ASSERT(reporter, 0 == cache->getResourceCount()); 777 SkDEBUGCODE(REPORTER_ASSERT(reporter, 0 == cache->countScratchEntriesForKey(scratchKey));) 778 } 779 780 static void test_remove_scratch_key(skiatest::Reporter* reporter) { 781 Mock mock(5, 30000); 782 GrContext* context = mock.context(); 783 GrResourceCache* cache = mock.cache(); 784 GrGpu* gpu = context->contextPriv().getGpu(); 785 786 // Create two resources that have the same scratch key. 787 TestResource* a = TestResource::CreateScratch(gpu, SkBudgeted::kYes, 788 TestResource::kB_SimulatedProperty); 789 TestResource* b = TestResource::CreateScratch(gpu, SkBudgeted::kYes, 790 TestResource::kB_SimulatedProperty); 791 a->unref(); 792 b->unref(); 793 794 GrScratchKey scratchKey; 795 // Ensure that scratch key lookup is correct for negative case. 796 TestResource::ComputeScratchKey(TestResource::kA_SimulatedProperty, &scratchKey); 797 // (following leaks upon test failure). 798 REPORTER_ASSERT(reporter, cache->findAndRefScratchResource(scratchKey, TestResource::kDefaultSize, 0) == nullptr); 799 800 // Scratch resources are registered with GrResourceCache just by existing. There are 2. 801 TestResource::ComputeScratchKey(TestResource::kB_SimulatedProperty, &scratchKey); 802 REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive()); 803 SkDEBUGCODE(REPORTER_ASSERT(reporter, 2 == cache->countScratchEntriesForKey(scratchKey));) 804 REPORTER_ASSERT(reporter, 2 == cache->getResourceCount()); 805 806 // Find the first resource and remove its scratch key 807 GrGpuResource* find; 808 find = cache->findAndRefScratchResource(scratchKey, TestResource::kDefaultSize, 0); 809 find->resourcePriv().removeScratchKey(); 810 // It's still alive, but not cached by scratch key anymore 811 REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive()); 812 SkDEBUGCODE(REPORTER_ASSERT(reporter, 1 == cache->countScratchEntriesForKey(scratchKey));) 813 REPORTER_ASSERT(reporter, 2 == cache->getResourceCount()); 814 815 // The cache should immediately delete it when it's unrefed since it isn't accessible. 816 find->unref(); 817 REPORTER_ASSERT(reporter, 1 == TestResource::NumAlive()); 818 SkDEBUGCODE(REPORTER_ASSERT(reporter, 1 == cache->countScratchEntriesForKey(scratchKey));) 819 REPORTER_ASSERT(reporter, 1 == cache->getResourceCount()); 820 821 // Repeat for the second resource. 822 find = cache->findAndRefScratchResource(scratchKey, TestResource::kDefaultSize, 0); 823 find->resourcePriv().removeScratchKey(); 824 REPORTER_ASSERT(reporter, 1 == TestResource::NumAlive()); 825 SkDEBUGCODE(REPORTER_ASSERT(reporter, 0 == cache->countScratchEntriesForKey(scratchKey));) 826 REPORTER_ASSERT(reporter, 1 == cache->getResourceCount()); 827 828 // Should be able to call this multiple times with no problem. 829 find->resourcePriv().removeScratchKey(); 830 REPORTER_ASSERT(reporter, 1 == TestResource::NumAlive()); 831 SkDEBUGCODE(REPORTER_ASSERT(reporter, 0 == cache->countScratchEntriesForKey(scratchKey));) 832 REPORTER_ASSERT(reporter, 1 == cache->getResourceCount()); 833 834 find->unref(); 835 REPORTER_ASSERT(reporter, 0 == TestResource::NumAlive()); 836 SkDEBUGCODE(REPORTER_ASSERT(reporter, 0 == cache->countScratchEntriesForKey(scratchKey));) 837 REPORTER_ASSERT(reporter, 0 == cache->getResourceCount()); 838 } 839 840 static void test_scratch_key_consistency(skiatest::Reporter* reporter) { 841 Mock mock(5, 30000); 842 GrContext* context = mock.context(); 843 GrResourceCache* cache = mock.cache(); 844 GrGpu* gpu = context->contextPriv().getGpu(); 845 846 // Create two resources that have the same scratch key. 847 TestResource* a = TestResource::CreateScratch(gpu, SkBudgeted::kYes, 848 TestResource::kB_SimulatedProperty); 849 TestResource* b = TestResource::CreateScratch(gpu, SkBudgeted::kYes, 850 TestResource::kB_SimulatedProperty); 851 a->unref(); 852 b->unref(); 853 854 GrScratchKey scratchKey; 855 // Ensure that scratch key comparison and assignment is consistent. 856 GrScratchKey scratchKey1; 857 TestResource::ComputeScratchKey(TestResource::kA_SimulatedProperty, &scratchKey1); 858 GrScratchKey scratchKey2; 859 TestResource::ComputeScratchKey(TestResource::kB_SimulatedProperty, &scratchKey2); 860 REPORTER_ASSERT(reporter, scratchKey1.size() == TestResource::ExpectedScratchKeySize()); 861 REPORTER_ASSERT(reporter, scratchKey1 != scratchKey2); 862 REPORTER_ASSERT(reporter, scratchKey2 != scratchKey1); 863 scratchKey = scratchKey1; 864 REPORTER_ASSERT(reporter, scratchKey.size() == TestResource::ExpectedScratchKeySize()); 865 REPORTER_ASSERT(reporter, scratchKey1 == scratchKey); 866 REPORTER_ASSERT(reporter, scratchKey == scratchKey1); 867 REPORTER_ASSERT(reporter, scratchKey2 != scratchKey); 868 REPORTER_ASSERT(reporter, scratchKey != scratchKey2); 869 scratchKey = scratchKey2; 870 REPORTER_ASSERT(reporter, scratchKey.size() == TestResource::ExpectedScratchKeySize()); 871 REPORTER_ASSERT(reporter, scratchKey1 != scratchKey); 872 REPORTER_ASSERT(reporter, scratchKey != scratchKey1); 873 REPORTER_ASSERT(reporter, scratchKey2 == scratchKey); 874 REPORTER_ASSERT(reporter, scratchKey == scratchKey2); 875 876 // Ensure that scratch key lookup is correct for negative case. 877 TestResource::ComputeScratchKey(TestResource::kA_SimulatedProperty, &scratchKey); 878 // (following leaks upon test failure). 879 REPORTER_ASSERT(reporter, cache->findAndRefScratchResource(scratchKey, TestResource::kDefaultSize, 0) == nullptr); 880 881 // Find the first resource with a scratch key and a copy of a scratch key. 882 TestResource::ComputeScratchKey(TestResource::kB_SimulatedProperty, &scratchKey); 883 GrGpuResource* find = cache->findAndRefScratchResource(scratchKey, TestResource::kDefaultSize, 0); 884 REPORTER_ASSERT(reporter, find != nullptr); 885 find->unref(); 886 887 scratchKey2 = scratchKey; 888 find = cache->findAndRefScratchResource(scratchKey2, TestResource::kDefaultSize, 0); 889 REPORTER_ASSERT(reporter, find != nullptr); 890 REPORTER_ASSERT(reporter, find == a || find == b); 891 892 GrGpuResource* find2 = cache->findAndRefScratchResource(scratchKey2, TestResource::kDefaultSize, 0); 893 REPORTER_ASSERT(reporter, find2 != nullptr); 894 REPORTER_ASSERT(reporter, find2 == a || find2 == b); 895 REPORTER_ASSERT(reporter, find2 != find); 896 find2->unref(); 897 find->unref(); 898 } 899 900 static void test_duplicate_unique_key(skiatest::Reporter* reporter) { 901 Mock mock(5, 30000); 902 GrContext* context = mock.context(); 903 GrResourceCache* cache = mock.cache(); 904 GrGpu* gpu = context->contextPriv().getGpu(); 905 906 GrUniqueKey key; 907 make_unique_key<0>(&key, 0); 908 909 // Create two resources that we will attempt to register with the same unique key. 910 TestResource* a = new TestResource(gpu); 911 a->setSize(11); 912 913 // Set key on resource a. 914 a->resourcePriv().setUniqueKey(key); 915 REPORTER_ASSERT(reporter, a == cache->findAndRefUniqueResource(key)); 916 a->unref(); 917 918 // Make sure that redundantly setting a's key works. 919 a->resourcePriv().setUniqueKey(key); 920 REPORTER_ASSERT(reporter, a == cache->findAndRefUniqueResource(key)); 921 a->unref(); 922 REPORTER_ASSERT(reporter, 1 == cache->getResourceCount()); 923 REPORTER_ASSERT(reporter, a->gpuMemorySize() == cache->getResourceBytes()); 924 REPORTER_ASSERT(reporter, 1 == TestResource::NumAlive()); 925 926 // Create resource b and set the same key. It should replace a's unique key cache entry. 927 TestResource* b = new TestResource(gpu); 928 b->setSize(12); 929 b->resourcePriv().setUniqueKey(key); 930 REPORTER_ASSERT(reporter, b == cache->findAndRefUniqueResource(key)); 931 b->unref(); 932 933 // Still have two resources because a is still reffed. 934 REPORTER_ASSERT(reporter, 2 == cache->getResourceCount()); 935 REPORTER_ASSERT(reporter, a->gpuMemorySize() + b->gpuMemorySize() == cache->getResourceBytes()); 936 REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive()); 937 938 a->unref(); 939 // Now a should be gone. 940 REPORTER_ASSERT(reporter, 1 == cache->getResourceCount()); 941 REPORTER_ASSERT(reporter, b->gpuMemorySize() == cache->getResourceBytes()); 942 REPORTER_ASSERT(reporter, 1 == TestResource::NumAlive()); 943 944 // Now replace b with c, but make sure c can start with one unique key and change it to b's key. 945 // Also make b be unreffed when replacement occurs. 946 b->unref(); 947 TestResource* c = new TestResource(gpu); 948 GrUniqueKey differentKey; 949 make_unique_key<0>(&differentKey, 1); 950 c->setSize(13); 951 c->resourcePriv().setUniqueKey(differentKey); 952 REPORTER_ASSERT(reporter, 2 == cache->getResourceCount()); 953 REPORTER_ASSERT(reporter, b->gpuMemorySize() + c->gpuMemorySize() == cache->getResourceBytes()); 954 REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive()); 955 // c replaces b and b should be immediately purged. 956 c->resourcePriv().setUniqueKey(key); 957 REPORTER_ASSERT(reporter, 1 == cache->getResourceCount()); 958 REPORTER_ASSERT(reporter, c->gpuMemorySize() == cache->getResourceBytes()); 959 REPORTER_ASSERT(reporter, 1 == TestResource::NumAlive()); 960 961 // c shouldn't be purged because it is ref'ed. 962 cache->purgeAllUnlocked(); 963 REPORTER_ASSERT(reporter, 1 == cache->getResourceCount()); 964 REPORTER_ASSERT(reporter, c->gpuMemorySize() == cache->getResourceBytes()); 965 REPORTER_ASSERT(reporter, 1 == TestResource::NumAlive()); 966 967 // Drop the ref on c, it should be kept alive because it has a unique key. 968 c->unref(); 969 REPORTER_ASSERT(reporter, 1 == cache->getResourceCount()); 970 REPORTER_ASSERT(reporter, c->gpuMemorySize() == cache->getResourceBytes()); 971 REPORTER_ASSERT(reporter, 1 == TestResource::NumAlive()); 972 973 // Verify that we can find c, then remove its unique key. It should get purged immediately. 974 REPORTER_ASSERT(reporter, c == cache->findAndRefUniqueResource(key)); 975 c->resourcePriv().removeUniqueKey(); 976 c->unref(); 977 REPORTER_ASSERT(reporter, 0 == cache->getResourceCount()); 978 REPORTER_ASSERT(reporter, 0 == cache->getResourceBytes()); 979 REPORTER_ASSERT(reporter, 0 == TestResource::NumAlive()); 980 981 { 982 GrUniqueKey key2; 983 make_unique_key<0>(&key2, 0); 984 sk_sp<TestResource> d(new TestResource(gpu)); 985 int foo = 4132; 986 key2.setCustomData(SkData::MakeWithCopy(&foo, sizeof(foo))); 987 d->resourcePriv().setUniqueKey(key2); 988 } 989 990 GrUniqueKey key3; 991 make_unique_key<0>(&key3, 0); 992 sk_sp<GrGpuResource> d2(cache->findAndRefUniqueResource(key3)); 993 REPORTER_ASSERT(reporter, *(int*) d2->getUniqueKey().getCustomData()->data() == 4132); 994 } 995 996 static void test_purge_invalidated(skiatest::Reporter* reporter) { 997 Mock mock(5, 30000); 998 GrContext* context = mock.context(); 999 GrResourceCache* cache = mock.cache(); 1000 GrGpu* gpu = context->contextPriv().getGpu(); 1001 1002 GrUniqueKey key1, key2, key3; 1003 make_unique_key<0>(&key1, 1); 1004 make_unique_key<0>(&key2, 2); 1005 make_unique_key<0>(&key3, 3); 1006 1007 // Add three resources to the cache. Only c is usable as scratch. 1008 TestResource* a = new TestResource(gpu); 1009 TestResource* b = new TestResource(gpu); 1010 TestResource* c = TestResource::CreateScratch(gpu, SkBudgeted::kYes, 1011 TestResource::kA_SimulatedProperty); 1012 a->resourcePriv().setUniqueKey(key1); 1013 b->resourcePriv().setUniqueKey(key2); 1014 c->resourcePriv().setUniqueKey(key3); 1015 a->unref(); 1016 // hold b until *after* the message is sent. 1017 c->unref(); 1018 1019 REPORTER_ASSERT(reporter, cache->hasUniqueKey(key1)); 1020 REPORTER_ASSERT(reporter, cache->hasUniqueKey(key2)); 1021 REPORTER_ASSERT(reporter, cache->hasUniqueKey(key3)); 1022 REPORTER_ASSERT(reporter, 3 == TestResource::NumAlive()); 1023 1024 typedef GrUniqueKeyInvalidatedMessage Msg; 1025 typedef SkMessageBus<GrUniqueKeyInvalidatedMessage> Bus; 1026 1027 // Invalidate two of the three, they should be purged and no longer accessible via their keys. 1028 Bus::Post(Msg(key1)); 1029 Bus::Post(Msg(key2)); 1030 cache->purgeAsNeeded(); 1031 // a should be deleted now, but we still have a ref on b. 1032 REPORTER_ASSERT(reporter, !cache->hasUniqueKey(key1)); 1033 REPORTER_ASSERT(reporter, !cache->hasUniqueKey(key2)); 1034 REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive()); 1035 REPORTER_ASSERT(reporter, cache->hasUniqueKey(key3)); 1036 1037 // Invalidate the third. 1038 Bus::Post(Msg(key3)); 1039 cache->purgeAsNeeded(); 1040 // we still have a ref on b, c should be recycled as scratch. 1041 REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive()); 1042 REPORTER_ASSERT(reporter, !cache->hasUniqueKey(key3)); 1043 1044 // make b purgeable. It should be immediately deleted since it has no key. 1045 b->unref(); 1046 REPORTER_ASSERT(reporter, 1 == TestResource::NumAlive()); 1047 1048 // Make sure we actually get to c via it's scratch key, before we say goodbye. 1049 GrScratchKey scratchKey; 1050 TestResource::ComputeScratchKey(TestResource::kA_SimulatedProperty, &scratchKey); 1051 GrGpuResource* scratch = cache->findAndRefScratchResource(scratchKey, TestResource::kDefaultSize, 0); 1052 REPORTER_ASSERT(reporter, scratch == c); 1053 SkSafeUnref(scratch); 1054 1055 // Get rid of c. 1056 cache->purgeAllUnlocked(); 1057 scratch = cache->findAndRefScratchResource(scratchKey, TestResource::kDefaultSize, 0); 1058 REPORTER_ASSERT(reporter, 0 == TestResource::NumAlive()); 1059 REPORTER_ASSERT(reporter, 0 == cache->getResourceCount()); 1060 REPORTER_ASSERT(reporter, 0 == cache->getResourceBytes()); 1061 REPORTER_ASSERT(reporter, !scratch); 1062 SkSafeUnref(scratch); 1063 } 1064 1065 static void test_cache_chained_purge(skiatest::Reporter* reporter) { 1066 Mock mock(3, 30000); 1067 GrContext* context = mock.context(); 1068 GrResourceCache* cache = mock.cache(); 1069 GrGpu* gpu = context->contextPriv().getGpu(); 1070 1071 GrUniqueKey key1, key2; 1072 make_unique_key<0>(&key1, 1); 1073 make_unique_key<0>(&key2, 2); 1074 1075 TestResource* a = new TestResource(gpu); 1076 TestResource* b = new TestResource(gpu); 1077 a->resourcePriv().setUniqueKey(key1); 1078 b->resourcePriv().setUniqueKey(key2); 1079 1080 // Make a cycle 1081 a->setUnrefWhenDestroyed(b); 1082 b->setUnrefWhenDestroyed(a); 1083 1084 REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive()); 1085 1086 a->unref(); 1087 b->unref(); 1088 1089 REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive()); 1090 1091 cache->purgeAllUnlocked(); 1092 REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive()); 1093 1094 // Break the cycle 1095 a->setUnrefWhenDestroyed(nullptr); 1096 REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive()); 1097 1098 cache->purgeAllUnlocked(); 1099 REPORTER_ASSERT(reporter, 0 == TestResource::NumAlive()); 1100 } 1101 1102 static void test_resource_size_changed(skiatest::Reporter* reporter) { 1103 GrUniqueKey key1, key2; 1104 make_unique_key<0>(&key1, 1); 1105 make_unique_key<0>(&key2, 2); 1106 1107 // Test changing resources sizes (both increase & decrease). 1108 { 1109 Mock mock(3, 30000); 1110 GrContext* context = mock.context(); 1111 GrResourceCache* cache = mock.cache(); 1112 GrGpu* gpu = context->contextPriv().getGpu(); 1113 1114 TestResource* a = new TestResource(gpu); 1115 a->resourcePriv().setUniqueKey(key1); 1116 a->unref(); 1117 1118 TestResource* b = new TestResource(gpu); 1119 b->resourcePriv().setUniqueKey(key2); 1120 b->unref(); 1121 1122 REPORTER_ASSERT(reporter, 200 == cache->getResourceBytes()); 1123 REPORTER_ASSERT(reporter, 2 == cache->getResourceCount()); 1124 { 1125 sk_sp<TestResource> find2( 1126 static_cast<TestResource*>(cache->findAndRefUniqueResource(key2))); 1127 find2->setSize(200); 1128 sk_sp<TestResource> find1( 1129 static_cast<TestResource*>(cache->findAndRefUniqueResource(key1))); 1130 find1->setSize(50); 1131 } 1132 1133 REPORTER_ASSERT(reporter, 250 == cache->getResourceBytes()); 1134 REPORTER_ASSERT(reporter, 2 == cache->getResourceCount()); 1135 } 1136 1137 // Test increasing a resources size beyond the cache budget. 1138 { 1139 Mock mock(2, 300); 1140 GrContext* context = mock.context(); 1141 GrResourceCache* cache = mock.cache(); 1142 GrGpu* gpu = context->contextPriv().getGpu(); 1143 1144 TestResource* a = new TestResource(gpu); 1145 a->setSize(100); 1146 a->resourcePriv().setUniqueKey(key1); 1147 a->unref(); 1148 1149 TestResource* b = new TestResource(gpu); 1150 b->setSize(100); 1151 b->resourcePriv().setUniqueKey(key2); 1152 b->unref(); 1153 1154 REPORTER_ASSERT(reporter, 200 == cache->getResourceBytes()); 1155 REPORTER_ASSERT(reporter, 2 == cache->getResourceCount()); 1156 1157 { 1158 sk_sp<TestResource> find2(static_cast<TestResource*>( 1159 cache->findAndRefUniqueResource(key2))); 1160 find2->setSize(201); 1161 } 1162 REPORTER_ASSERT(reporter, !cache->hasUniqueKey(key1)); 1163 1164 REPORTER_ASSERT(reporter, 201 == cache->getResourceBytes()); 1165 REPORTER_ASSERT(reporter, 1 == cache->getResourceCount()); 1166 } 1167 } 1168 1169 static void test_timestamp_wrap(skiatest::Reporter* reporter) { 1170 static const int kCount = 50; 1171 static const int kBudgetCnt = kCount / 2; 1172 static const int kLockedFreq = 8; 1173 static const int kBudgetSize = 0x80000000; 1174 1175 SkRandom random; 1176 1177 // Run the test 2*kCount times; 1178 for (int i = 0; i < 2 * kCount; ++i ) { 1179 Mock mock(kBudgetCnt, kBudgetSize); 1180 GrContext* context = mock.context(); 1181 GrResourceCache* cache = mock.cache(); 1182 GrGpu* gpu = context->contextPriv().getGpu(); 1183 1184 // Pick a random number of resources to add before the timestamp will wrap. 1185 cache->changeTimestamp(SK_MaxU32 - random.nextULessThan(kCount + 1)); 1186 1187 static const int kNumToPurge = kCount - kBudgetCnt; 1188 1189 SkTDArray<int> shouldPurgeIdxs; 1190 int purgeableCnt = 0; 1191 SkTDArray<GrGpuResource*> resourcesToUnref; 1192 1193 // Add kCount resources, holding onto resources at random so we have a mix of purgeable and 1194 // unpurgeable resources. 1195 for (int j = 0; j < kCount; ++j) { 1196 GrUniqueKey key; 1197 make_unique_key<0>(&key, j); 1198 1199 TestResource* r = new TestResource(gpu); 1200 r->resourcePriv().setUniqueKey(key); 1201 if (random.nextU() % kLockedFreq) { 1202 // Make this is purgeable. 1203 r->unref(); 1204 ++purgeableCnt; 1205 if (purgeableCnt <= kNumToPurge) { 1206 *shouldPurgeIdxs.append() = j; 1207 } 1208 } else { 1209 *resourcesToUnref.append() = r; 1210 } 1211 } 1212 1213 // Verify that the correct resources were purged. 1214 int currShouldPurgeIdx = 0; 1215 for (int j = 0; j < kCount; ++j) { 1216 GrUniqueKey key; 1217 make_unique_key<0>(&key, j); 1218 GrGpuResource* res = cache->findAndRefUniqueResource(key); 1219 if (currShouldPurgeIdx < shouldPurgeIdxs.count() && 1220 shouldPurgeIdxs[currShouldPurgeIdx] == j) { 1221 ++currShouldPurgeIdx; 1222 REPORTER_ASSERT(reporter, nullptr == res); 1223 } else { 1224 REPORTER_ASSERT(reporter, nullptr != res); 1225 } 1226 SkSafeUnref(res); 1227 } 1228 1229 for (int j = 0; j < resourcesToUnref.count(); ++j) { 1230 resourcesToUnref[j]->unref(); 1231 } 1232 } 1233 } 1234 1235 static void test_flush(skiatest::Reporter* reporter) { 1236 Mock mock(1000000, 1000000); 1237 GrContext* context = mock.context(); 1238 GrResourceCache* cache = mock.cache(); 1239 GrGpu* gpu = context->contextPriv().getGpu(); 1240 1241 // The current cache impl will round the max flush count to the next power of 2. So we choose a 1242 // power of two here to keep things simpler. 1243 static const int kFlushCount = 16; 1244 cache->setLimits(1000000, 1000000, kFlushCount); 1245 1246 { 1247 // Insert a resource and send a flush notification kFlushCount times. 1248 for (int i = 0; i < kFlushCount; ++i) { 1249 TestResource* r = new TestResource(gpu); 1250 GrUniqueKey k; 1251 make_unique_key<1>(&k, i); 1252 r->resourcePriv().setUniqueKey(k); 1253 r->unref(); 1254 cache->notifyFlushOccurred(GrResourceCache::kExternal); 1255 } 1256 1257 // Send flush notifications to the cache. Each flush should purge the oldest resource. 1258 for (int i = 0; i < kFlushCount; ++i) { 1259 cache->notifyFlushOccurred(GrResourceCache::kExternal); 1260 REPORTER_ASSERT(reporter, kFlushCount - i - 1 == cache->getResourceCount()); 1261 for (int j = 0; j < i; ++j) { 1262 GrUniqueKey k; 1263 make_unique_key<1>(&k, j); 1264 GrGpuResource* r = cache->findAndRefUniqueResource(k); 1265 REPORTER_ASSERT(reporter, !SkToBool(r)); 1266 SkSafeUnref(r); 1267 } 1268 } 1269 1270 REPORTER_ASSERT(reporter, 0 == cache->getResourceCount()); 1271 cache->purgeAllUnlocked(); 1272 } 1273 1274 // Do a similar test but where we leave refs on some resources to prevent them from being 1275 // purged. 1276 { 1277 GrGpuResource* refedResources[kFlushCount >> 1]; 1278 for (int i = 0; i < kFlushCount; ++i) { 1279 TestResource* r = new TestResource(gpu); 1280 GrUniqueKey k; 1281 make_unique_key<1>(&k, i); 1282 r->resourcePriv().setUniqueKey(k); 1283 // Leave a ref on every other resource, beginning with the first. 1284 if (SkToBool(i & 0x1)) { 1285 refedResources[i/2] = r; 1286 } else { 1287 r->unref(); 1288 } 1289 cache->notifyFlushOccurred(GrResourceCache::kExternal); 1290 } 1291 1292 for (int i = 0; i < kFlushCount; ++i) { 1293 // Should get a resource purged every other flush. 1294 cache->notifyFlushOccurred(GrResourceCache::kExternal); 1295 REPORTER_ASSERT(reporter, kFlushCount - i/2 - 1 == cache->getResourceCount()); 1296 } 1297 1298 // Unref all the resources that we kept refs on in the first loop. 1299 for (int i = 0; i < kFlushCount >> 1; ++i) { 1300 refedResources[i]->unref(); 1301 } 1302 1303 // After kFlushCount + 1 flushes they all will have sat in the purgeable queue for 1304 // kFlushCount full flushes. 1305 for (int i = 0; i < kFlushCount + 1; ++i) { 1306 REPORTER_ASSERT(reporter, kFlushCount >> 1 == cache->getResourceCount()); 1307 cache->notifyFlushOccurred(GrResourceCache::kExternal); 1308 } 1309 REPORTER_ASSERT(reporter, 0 == cache->getResourceCount()); 1310 1311 cache->purgeAllUnlocked(); 1312 } 1313 1314 REPORTER_ASSERT(reporter, 0 == cache->getResourceCount()); 1315 1316 // Verify that calling flush() on a GrContext with nothing to do will not trigger resource 1317 // eviction. 1318 context->flush(); 1319 for (int i = 0; i < 10; ++i) { 1320 TestResource* r = new TestResource(gpu); 1321 GrUniqueKey k; 1322 make_unique_key<1>(&k, i); 1323 r->resourcePriv().setUniqueKey(k); 1324 r->unref(); 1325 } 1326 REPORTER_ASSERT(reporter, 10 == cache->getResourceCount()); 1327 for (int i = 0; i < 10 * kFlushCount; ++i) { 1328 context->flush(); 1329 } 1330 REPORTER_ASSERT(reporter, 10 == cache->getResourceCount()); 1331 } 1332 1333 static void test_time_purge(skiatest::Reporter* reporter) { 1334 Mock mock(1000000, 1000000); 1335 GrContext* context = mock.context(); 1336 GrResourceCache* cache = mock.cache(); 1337 GrGpu* gpu = context->contextPriv().getGpu(); 1338 1339 static constexpr int kCnts[] = {1, 10, 1024}; 1340 auto nowish = []() { 1341 // We sleep so that we ensure we get a value that is greater than the last call to 1342 // GrStdSteadyClock::now(). 1343 std::this_thread::sleep_for(GrStdSteadyClock::duration(5)); 1344 auto result = GrStdSteadyClock::now(); 1345 // Also sleep afterwards so we don't get this value again. 1346 std::this_thread::sleep_for(GrStdSteadyClock::duration(5)); 1347 return result; 1348 }; 1349 1350 for (int cnt : kCnts) { 1351 std::unique_ptr<GrStdSteadyClock::time_point[]> timeStamps( 1352 new GrStdSteadyClock::time_point[cnt]); 1353 { 1354 // Insert resources and get time points between each addition. 1355 for (int i = 0; i < cnt; ++i) { 1356 TestResource* r = new TestResource(gpu); 1357 GrUniqueKey k; 1358 make_unique_key<1>(&k, i); 1359 r->resourcePriv().setUniqueKey(k); 1360 r->unref(); 1361 timeStamps.get()[i] = nowish(); 1362 } 1363 1364 // Purge based on the time points between resource additions. Each purge should remove 1365 // the oldest resource. 1366 for (int i = 0; i < cnt; ++i) { 1367 cache->purgeResourcesNotUsedSince(timeStamps[i]); 1368 REPORTER_ASSERT(reporter, cnt - i - 1 == cache->getResourceCount()); 1369 for (int j = 0; j < i; ++j) { 1370 GrUniqueKey k; 1371 make_unique_key<1>(&k, j); 1372 GrGpuResource* r = cache->findAndRefUniqueResource(k); 1373 REPORTER_ASSERT(reporter, !SkToBool(r)); 1374 SkSafeUnref(r); 1375 } 1376 } 1377 1378 REPORTER_ASSERT(reporter, 0 == cache->getResourceCount()); 1379 cache->purgeAllUnlocked(); 1380 } 1381 1382 // Do a similar test but where we leave refs on some resources to prevent them from being 1383 // purged. 1384 { 1385 std::unique_ptr<GrGpuResource* []> refedResources(new GrGpuResource*[cnt / 2]); 1386 for (int i = 0; i < cnt; ++i) { 1387 TestResource* r = new TestResource(gpu); 1388 GrUniqueKey k; 1389 make_unique_key<1>(&k, i); 1390 r->resourcePriv().setUniqueKey(k); 1391 // Leave a ref on every other resource, beginning with the first. 1392 if (SkToBool(i & 0x1)) { 1393 refedResources.get()[i / 2] = r; 1394 } else { 1395 r->unref(); 1396 } 1397 timeStamps.get()[i] = nowish(); 1398 } 1399 1400 for (int i = 0; i < cnt; ++i) { 1401 // Should get a resource purged every other frame. 1402 cache->purgeResourcesNotUsedSince(timeStamps[i]); 1403 REPORTER_ASSERT(reporter, cnt - i / 2 - 1 == cache->getResourceCount()); 1404 } 1405 1406 // Unref all the resources that we kept refs on in the first loop. 1407 for (int i = 0; i < (cnt / 2); ++i) { 1408 refedResources.get()[i]->unref(); 1409 cache->purgeResourcesNotUsedSince(nowish()); 1410 REPORTER_ASSERT(reporter, cnt / 2 - i - 1 == cache->getResourceCount()); 1411 } 1412 1413 cache->purgeAllUnlocked(); 1414 } 1415 1416 REPORTER_ASSERT(reporter, 0 == cache->getResourceCount()); 1417 1418 // Verify that calling flush() on a GrContext with nothing to do will not trigger resource 1419 // eviction 1420 context->flush(); 1421 for (int i = 0; i < 10; ++i) { 1422 TestResource* r = new TestResource(gpu); 1423 GrUniqueKey k; 1424 make_unique_key<1>(&k, i); 1425 r->resourcePriv().setUniqueKey(k); 1426 r->unref(); 1427 } 1428 REPORTER_ASSERT(reporter, 10 == cache->getResourceCount()); 1429 context->flush(); 1430 REPORTER_ASSERT(reporter, 10 == cache->getResourceCount()); 1431 cache->purgeResourcesNotUsedSince(nowish()); 1432 REPORTER_ASSERT(reporter, 0 == cache->getResourceCount()); 1433 } 1434 } 1435 1436 static void test_partial_purge(skiatest::Reporter* reporter) { 1437 Mock mock(6, 100); 1438 GrContext* context = mock.context(); 1439 GrResourceCache* cache = mock.cache(); 1440 GrGpu* gpu = context->contextPriv().getGpu(); 1441 1442 enum TestsCase { 1443 kOnlyScratch_TestCase = 0, 1444 kPartialScratch_TestCase = 1, 1445 kAllScratch_TestCase = 2, 1446 kPartial_TestCase = 3, 1447 kAll_TestCase = 4, 1448 kNone_TestCase = 5, 1449 kEndTests_TestCase = kNone_TestCase + 1 1450 }; 1451 1452 for (int testCase = 0; testCase < kEndTests_TestCase; testCase++) { 1453 1454 GrUniqueKey key1, key2, key3; 1455 make_unique_key<0>(&key1, 1); 1456 make_unique_key<0>(&key2, 2); 1457 make_unique_key<0>(&key3, 3); 1458 1459 // Add three unique resources to the cache. 1460 TestResource *unique1 = new TestResource(gpu); 1461 TestResource *unique2 = new TestResource(gpu); 1462 TestResource *unique3 = new TestResource(gpu); 1463 1464 unique1->resourcePriv().setUniqueKey(key1); 1465 unique2->resourcePriv().setUniqueKey(key2); 1466 unique3->resourcePriv().setUniqueKey(key3); 1467 1468 unique1->setSize(10); 1469 unique2->setSize(11); 1470 unique3->setSize(12); 1471 1472 // Add two scratch resources to the cache. 1473 TestResource *scratch1 = TestResource::CreateScratch(gpu, SkBudgeted::kYes, 1474 TestResource::kA_SimulatedProperty); 1475 TestResource *scratch2 = TestResource::CreateScratch(gpu, SkBudgeted::kYes, 1476 TestResource::kB_SimulatedProperty); 1477 scratch1->setSize(13); 1478 scratch2->setSize(14); 1479 1480 1481 REPORTER_ASSERT(reporter, 5 == cache->getBudgetedResourceCount()); 1482 REPORTER_ASSERT(reporter, 60 == cache->getBudgetedResourceBytes()); 1483 REPORTER_ASSERT(reporter, 0 == cache->getPurgeableBytes()); 1484 1485 // Add resources to the purgeable queue 1486 unique1->unref(); 1487 scratch1->unref(); 1488 unique2->unref(); 1489 scratch2->unref(); 1490 unique3->unref(); 1491 1492 REPORTER_ASSERT(reporter, 5 == cache->getBudgetedResourceCount()); 1493 REPORTER_ASSERT(reporter, 60 == cache->getBudgetedResourceBytes()); 1494 REPORTER_ASSERT(reporter, 60 == cache->getPurgeableBytes()); 1495 1496 switch(testCase) { 1497 case kOnlyScratch_TestCase: { 1498 context->purgeUnlockedResources(14, true); 1499 REPORTER_ASSERT(reporter, 3 == cache->getBudgetedResourceCount()); 1500 REPORTER_ASSERT(reporter, 33 == cache->getBudgetedResourceBytes()); 1501 break; 1502 } 1503 case kPartialScratch_TestCase: { 1504 context->purgeUnlockedResources(3, true); 1505 REPORTER_ASSERT(reporter, 4 == cache->getBudgetedResourceCount()); 1506 REPORTER_ASSERT(reporter, 47 == cache->getBudgetedResourceBytes()); 1507 break; 1508 } 1509 case kAllScratch_TestCase: { 1510 context->purgeUnlockedResources(50, true); 1511 REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceCount()); 1512 REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceBytes()); 1513 break; 1514 } 1515 case kPartial_TestCase: { 1516 context->purgeUnlockedResources(13, false); 1517 REPORTER_ASSERT(reporter, 3 == cache->getBudgetedResourceCount()); 1518 REPORTER_ASSERT(reporter, 37 == cache->getBudgetedResourceBytes()); 1519 break; 1520 } 1521 case kAll_TestCase: { 1522 context->purgeUnlockedResources(50, false); 1523 REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceCount()); 1524 REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceBytes()); 1525 break; 1526 } 1527 case kNone_TestCase: { 1528 context->purgeUnlockedResources(0, true); 1529 context->purgeUnlockedResources(0, false); 1530 REPORTER_ASSERT(reporter, 5 == cache->getBudgetedResourceCount()); 1531 REPORTER_ASSERT(reporter, 60 == cache->getBudgetedResourceBytes()); 1532 REPORTER_ASSERT(reporter, 60 == cache->getPurgeableBytes()); 1533 break; 1534 } 1535 }; 1536 1537 // ensure all are purged before the next 1538 context->purgeAllUnlockedResources(); 1539 REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceCount()); 1540 REPORTER_ASSERT(reporter, 0 == cache->getPurgeableBytes()); 1541 1542 } 1543 } 1544 1545 static void test_large_resource_count(skiatest::Reporter* reporter) { 1546 // Set the cache size to double the resource count because we're going to create 2x that number 1547 // resources, using two different key domains. Add a little slop to the bytes because we resize 1548 // down to 1 byte after creating the resource. 1549 static const int kResourceCnt = 2000; 1550 1551 Mock mock(2 * kResourceCnt, 2 * kResourceCnt + 1000); 1552 GrContext* context = mock.context(); 1553 GrResourceCache* cache = mock.cache(); 1554 GrGpu* gpu = context->contextPriv().getGpu(); 1555 1556 for (int i = 0; i < kResourceCnt; ++i) { 1557 GrUniqueKey key1, key2; 1558 make_unique_key<1>(&key1, i); 1559 make_unique_key<2>(&key2, i); 1560 1561 TestResource* resource; 1562 1563 resource = new TestResource(gpu); 1564 resource->resourcePriv().setUniqueKey(key1); 1565 resource->setSize(1); 1566 resource->unref(); 1567 1568 resource = new TestResource(gpu); 1569 resource->resourcePriv().setUniqueKey(key2); 1570 resource->setSize(1); 1571 resource->unref(); 1572 } 1573 1574 REPORTER_ASSERT(reporter, TestResource::NumAlive() == 2 * kResourceCnt); 1575 REPORTER_ASSERT(reporter, cache->getPurgeableBytes() == 2 * kResourceCnt); 1576 REPORTER_ASSERT(reporter, cache->getBudgetedResourceBytes() == 2 * kResourceCnt); 1577 REPORTER_ASSERT(reporter, cache->getBudgetedResourceCount() == 2 * kResourceCnt); 1578 REPORTER_ASSERT(reporter, cache->getResourceBytes() == 2 * kResourceCnt); 1579 REPORTER_ASSERT(reporter, cache->getResourceCount() == 2 * kResourceCnt); 1580 for (int i = 0; i < kResourceCnt; ++i) { 1581 GrUniqueKey key1, key2; 1582 make_unique_key<1>(&key1, i); 1583 make_unique_key<2>(&key2, i); 1584 1585 REPORTER_ASSERT(reporter, cache->hasUniqueKey(key1)); 1586 REPORTER_ASSERT(reporter, cache->hasUniqueKey(key2)); 1587 } 1588 1589 cache->purgeAllUnlocked(); 1590 REPORTER_ASSERT(reporter, TestResource::NumAlive() == 0); 1591 REPORTER_ASSERT(reporter, cache->getPurgeableBytes() == 0); 1592 REPORTER_ASSERT(reporter, cache->getBudgetedResourceBytes() == 0); 1593 REPORTER_ASSERT(reporter, cache->getBudgetedResourceCount() == 0); 1594 REPORTER_ASSERT(reporter, cache->getResourceBytes() == 0); 1595 REPORTER_ASSERT(reporter, cache->getResourceCount() == 0); 1596 1597 for (int i = 0; i < kResourceCnt; ++i) { 1598 GrUniqueKey key1, key2; 1599 make_unique_key<1>(&key1, i); 1600 make_unique_key<2>(&key2, i); 1601 1602 REPORTER_ASSERT(reporter, !cache->hasUniqueKey(key1)); 1603 REPORTER_ASSERT(reporter, !cache->hasUniqueKey(key2)); 1604 } 1605 } 1606 1607 static void test_custom_data(skiatest::Reporter* reporter) { 1608 GrUniqueKey key1, key2; 1609 make_unique_key<0>(&key1, 1); 1610 make_unique_key<0>(&key2, 2); 1611 int foo = 4132; 1612 key1.setCustomData(SkData::MakeWithCopy(&foo, sizeof(foo))); 1613 REPORTER_ASSERT(reporter, *(int*) key1.getCustomData()->data() == 4132); 1614 REPORTER_ASSERT(reporter, key2.getCustomData() == nullptr); 1615 1616 // Test that copying a key also takes a ref on its custom data. 1617 GrUniqueKey key3 = key1; 1618 REPORTER_ASSERT(reporter, *(int*) key3.getCustomData()->data() == 4132); 1619 } 1620 1621 static void test_abandoned(skiatest::Reporter* reporter) { 1622 Mock mock(10, 300); 1623 GrContext* context = mock.context(); 1624 GrGpu* gpu = context->contextPriv().getGpu(); 1625 1626 sk_sp<GrGpuResource> resource(new TestResource(gpu)); 1627 context->abandonContext(); 1628 1629 REPORTER_ASSERT(reporter, resource->wasDestroyed()); 1630 1631 // Call all the public methods on resource in the abandoned state. They shouldn't crash. 1632 1633 resource->uniqueID(); 1634 resource->getUniqueKey(); 1635 resource->wasDestroyed(); 1636 resource->gpuMemorySize(); 1637 resource->getContext(); 1638 1639 resource->abandon(); 1640 resource->resourcePriv().getScratchKey(); 1641 resource->resourcePriv().isBudgeted(); 1642 resource->resourcePriv().makeBudgeted(); 1643 resource->resourcePriv().makeUnbudgeted(); 1644 resource->resourcePriv().removeScratchKey(); 1645 GrUniqueKey key; 1646 make_unique_key<0>(&key, 1); 1647 resource->resourcePriv().setUniqueKey(key); 1648 resource->resourcePriv().removeUniqueKey(); 1649 } 1650 1651 static void test_tags(skiatest::Reporter* reporter) { 1652 #ifdef SK_DEBUG 1653 // We will insert 1 resource with tag "tag1", 2 with "tag2", and so on, up through kLastTagIdx. 1654 static constexpr int kLastTagIdx = 10; 1655 static constexpr int kNumResources = kLastTagIdx * (kLastTagIdx + 1) / 2; 1656 1657 Mock mock(kNumResources, kNumResources * TestResource::kDefaultSize); 1658 GrContext* context = mock.context(); 1659 GrResourceCache* cache = mock.cache(); 1660 GrGpu* gpu = context->contextPriv().getGpu(); 1661 1662 // tag strings are expected to be long lived 1663 std::vector<SkString> tagStrings; 1664 1665 SkString tagStr; 1666 int tagIdx = 0; 1667 int currTagCnt = 0; 1668 1669 for (int i = 0; i < kNumResources; ++i, ++currTagCnt) { 1670 1671 sk_sp<GrGpuResource> resource(new TestResource(gpu)); 1672 GrUniqueKey key; 1673 if (currTagCnt == tagIdx) { 1674 tagIdx += 1; 1675 currTagCnt = 0; 1676 tagStr.printf("tag%d", tagIdx); 1677 tagStrings.emplace_back(tagStr); 1678 } 1679 make_unique_key<1>(&key, i, tagStrings.back().c_str()); 1680 resource->resourcePriv().setUniqueKey(key); 1681 } 1682 SkASSERT(kLastTagIdx == tagIdx); 1683 SkASSERT(currTagCnt == kLastTagIdx); 1684 1685 // Test i = 0 to exercise unused tag string. 1686 for (int i = 0; i <= kLastTagIdx; ++i) { 1687 tagStr.printf("tag%d", i); 1688 REPORTER_ASSERT(reporter, cache->countUniqueKeysWithTag(tagStr.c_str()) == i); 1689 } 1690 #endif 1691 } 1692 1693 DEF_GPUTEST(ResourceCacheMisc, reporter, /* options */) { 1694 // The below tests create their own mock contexts. 1695 test_no_key(reporter); 1696 test_purge_unlocked(reporter); 1697 test_budgeting(reporter); 1698 test_unbudgeted(reporter); 1699 test_unbudgeted_to_scratch(reporter); 1700 test_duplicate_unique_key(reporter); 1701 test_duplicate_scratch_key(reporter); 1702 test_remove_scratch_key(reporter); 1703 test_scratch_key_consistency(reporter); 1704 test_purge_invalidated(reporter); 1705 test_cache_chained_purge(reporter); 1706 test_resource_size_changed(reporter); 1707 test_timestamp_wrap(reporter); 1708 test_flush(reporter); 1709 test_time_purge(reporter); 1710 test_partial_purge(reporter); 1711 test_large_resource_count(reporter); 1712 test_custom_data(reporter); 1713 test_abandoned(reporter); 1714 test_tags(reporter); 1715 } 1716 1717 //////////////////////////////////////////////////////////////////////////////// 1718 static sk_sp<GrTexture> make_normal_texture(GrResourceProvider* provider, 1719 GrSurfaceFlags flags, 1720 int width, int height, 1721 int sampleCnt) { 1722 GrSurfaceDesc desc; 1723 desc.fFlags = flags; 1724 desc.fOrigin = kTopLeft_GrSurfaceOrigin; 1725 desc.fWidth = width; 1726 desc.fHeight = height; 1727 desc.fConfig = kRGBA_8888_GrPixelConfig; 1728 desc.fSampleCnt = sampleCnt; 1729 1730 return provider->createTexture(desc, SkBudgeted::kYes); 1731 } 1732 1733 static sk_sp<GrTextureProxy> make_mipmap_proxy(GrProxyProvider* proxyProvider, 1734 GrSurfaceFlags flags, 1735 int width, int height, 1736 int sampleCnt) { 1737 GrSurfaceDesc desc; 1738 desc.fFlags = flags; 1739 desc.fOrigin = (flags & kRenderTarget_GrSurfaceFlag) ? kBottomLeft_GrSurfaceOrigin 1740 : kTopLeft_GrSurfaceOrigin; 1741 desc.fWidth = width; 1742 desc.fHeight = height; 1743 desc.fConfig = kRGBA_8888_GrPixelConfig; 1744 desc.fSampleCnt = sampleCnt; 1745 1746 return proxyProvider->createMipMapProxy(desc, SkBudgeted::kYes); 1747 } 1748 1749 // Exercise GrSurface::gpuMemorySize for different combos of MSAA, RT-only, 1750 // Texture-only, both-RT-and-Texture and MIPmapped 1751 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GPUMemorySize, reporter, ctxInfo) { 1752 GrContext* context = ctxInfo.grContext(); 1753 GrProxyProvider* proxyProvider = context->contextPriv().proxyProvider(); 1754 GrResourceProvider* resourceProvider = context->contextPriv().resourceProvider(); 1755 1756 static const int kSize = 64; 1757 1758 // Normal versions 1759 { 1760 sk_sp<GrTexture> tex; 1761 1762 tex = make_normal_texture(resourceProvider, kRenderTarget_GrSurfaceFlag, kSize, kSize, 1); 1763 size_t size = tex->gpuMemorySize(); 1764 REPORTER_ASSERT(reporter, kSize*kSize*4 == size); 1765 1766 size_t sampleCount = 1767 (size_t)context->caps()->getRenderTargetSampleCount(4, kRGBA_8888_GrPixelConfig); 1768 if (sampleCount >= 4) { 1769 tex = make_normal_texture(resourceProvider, kRenderTarget_GrSurfaceFlag, kSize, kSize, 1770 sampleCount); 1771 size = tex->gpuMemorySize(); 1772 REPORTER_ASSERT(reporter, 1773 kSize*kSize*4 == size || // msaa4 failed 1774 kSize*kSize*4*sampleCount == size || // auto-resolving 1775 kSize*kSize*4*(sampleCount+1) == size); // explicit resolve buffer 1776 } 1777 1778 tex = make_normal_texture(resourceProvider, kNone_GrSurfaceFlags, kSize, kSize, 1); 1779 size = tex->gpuMemorySize(); 1780 REPORTER_ASSERT(reporter, kSize*kSize*4 == size); 1781 } 1782 1783 1784 // Mipmapped versions 1785 if (context->caps()->mipMapSupport()) { 1786 sk_sp<GrTextureProxy> proxy; 1787 1788 proxy = make_mipmap_proxy(proxyProvider, kRenderTarget_GrSurfaceFlag, kSize, kSize, 1); 1789 size_t size = proxy->gpuMemorySize(); 1790 REPORTER_ASSERT(reporter, kSize*kSize*4+(kSize*kSize*4)/3 == size); 1791 1792 size_t sampleCount = 1793 (size_t)context->caps()->getRenderTargetSampleCount(4, kRGBA_8888_GrPixelConfig); 1794 if (sampleCount >= 4) { 1795 proxy = make_mipmap_proxy(proxyProvider, kRenderTarget_GrSurfaceFlag, kSize, kSize, 1796 sampleCount); 1797 size = proxy->gpuMemorySize(); 1798 REPORTER_ASSERT(reporter, 1799 kSize*kSize*4+(kSize*kSize*4)/3 == size || // msaa4 failed 1800 kSize*kSize*4*sampleCount+(kSize*kSize*4)/3 == size || // auto-resolving 1801 kSize*kSize*4*(sampleCount+1)+(kSize*kSize*4)/3 == size); // explicit resolve buffer 1802 } 1803 1804 proxy = make_mipmap_proxy(proxyProvider, kNone_GrSurfaceFlags, kSize, kSize, 1); 1805 size = proxy->gpuMemorySize(); 1806 REPORTER_ASSERT(reporter, kSize*kSize*4+(kSize*kSize*4)/3 == size); 1807 } 1808 } 1809 1810 #endif 1811