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