ResourceCacheTest.cpp revision 9c46b68d5340efc91368cef1cc5775f13c97cab8
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 13#include "GrContext.h" 14#include "GrContextFactory.h" 15#include "GrGpu.h" 16#include "GrGpuResourceCacheAccess.h" 17#include "GrGpuResourcePriv.h" 18#include "GrRenderTarget.h" 19#include "GrRenderTargetPriv.h" 20#include "GrResourceCache.h" 21#include "GrResourceProvider.h" 22#include "GrTest.h" 23#include "SkCanvas.h" 24#include "SkGr.h" 25#include "SkMessageBus.h" 26#include "SkSurface.h" 27#include "Test.h" 28 29static const int gWidth = 640; 30static const int gHeight = 480; 31 32//////////////////////////////////////////////////////////////////////////////// 33DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ResourceCacheCache, reporter, ctxInfo) { 34 GrContext* context = ctxInfo.grContext(); 35 GrSurfaceDesc desc; 36 desc.fConfig = kRGBA_8888_GrPixelConfig; 37 desc.fFlags = kRenderTarget_GrSurfaceFlag; 38 desc.fWidth = gWidth; 39 desc.fHeight = gHeight; 40 SkImageInfo info = SkImageInfo::MakeN32Premul(gWidth, gHeight); 41 auto surface(SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, info)); 42 SkCanvas* canvas = surface->getCanvas(); 43 44 const SkIRect size = SkIRect::MakeWH(gWidth, gHeight); 45 46 SkBitmap src; 47 src.allocN32Pixels(size.width(), size.height()); 48 src.eraseColor(SK_ColorBLACK); 49 size_t srcSize = src.getSize(); 50 51 size_t initialCacheSize; 52 context->getResourceCacheUsage(nullptr, &initialCacheSize); 53 54 int oldMaxNum; 55 size_t oldMaxBytes; 56 context->getResourceCacheLimits(&oldMaxNum, &oldMaxBytes); 57 58 // Set the cache limits so we can fit 10 "src" images and the 59 // max number of textures doesn't matter 60 size_t maxCacheSize = initialCacheSize + 10*srcSize; 61 context->setResourceCacheLimits(1000, maxCacheSize); 62 63 SkBitmap readback; 64 readback.allocN32Pixels(size.width(), size.height()); 65 66 for (int i = 0; i < 100; ++i) { 67 canvas->drawBitmap(src, 0, 0); 68 canvas->readPixels(size, &readback); 69 70 // "modify" the src texture 71 src.notifyPixelsChanged(); 72 73 size_t curCacheSize; 74 context->getResourceCacheUsage(nullptr, &curCacheSize); 75 76 // we should never go over the size limit 77 REPORTER_ASSERT(reporter, curCacheSize <= maxCacheSize); 78 } 79 80 context->setResourceCacheLimits(oldMaxNum, oldMaxBytes); 81} 82 83static bool is_rendering_and_not_angle_es3(sk_gpu_test::GrContextFactory::ContextType type) { 84 if (type == sk_gpu_test::GrContextFactory::kANGLE_D3D11_ES3_ContextType || 85 type == sk_gpu_test::GrContextFactory::kANGLE_GL_ES3_ContextType) { 86 return false; 87 } 88 return sk_gpu_test::GrContextFactory::IsRenderingContext(type); 89} 90 91// This currently fails on ES3 ANGLE contexts 92DEF_GPUTEST_FOR_CONTEXTS(ResourceCacheStencilBuffers, &is_rendering_and_not_angle_es3, reporter, 93 ctxInfo) { 94 GrContext* context = ctxInfo.grContext(); 95 GrSurfaceDesc smallDesc; 96 smallDesc.fFlags = kRenderTarget_GrSurfaceFlag; 97 smallDesc.fConfig = kRGBA_8888_GrPixelConfig; 98 smallDesc.fWidth = 4; 99 smallDesc.fHeight = 4; 100 smallDesc.fSampleCnt = 0; 101 102 GrTextureProvider* cache = context->textureProvider(); 103 GrResourceProvider* resourceProvider = context->resourceProvider(); 104 // Test that two budgeted RTs with the same desc share a stencil buffer. 105 sk_sp<GrTexture> smallRT0(cache->createTexture(smallDesc, SkBudgeted::kYes)); 106 if (smallRT0 && smallRT0->asRenderTarget()) { 107 resourceProvider->attachStencilAttachment(smallRT0->asRenderTarget()); 108 } 109 110 sk_sp<GrTexture> smallRT1(cache->createTexture(smallDesc, SkBudgeted::kYes)); 111 if (smallRT1 && smallRT1->asRenderTarget()) { 112 resourceProvider->attachStencilAttachment(smallRT1->asRenderTarget()); 113 } 114 115 REPORTER_ASSERT(reporter, 116 smallRT0 && smallRT1 && 117 smallRT0->asRenderTarget() && smallRT1->asRenderTarget() && 118 resourceProvider->attachStencilAttachment(smallRT0->asRenderTarget()) == 119 resourceProvider->attachStencilAttachment(smallRT1->asRenderTarget())); 120 121 // An unbudgeted RT with the same desc should also share. 122 sk_sp<GrTexture> smallRT2(cache->createTexture(smallDesc, SkBudgeted::kNo)); 123 if (smallRT2 && smallRT2->asRenderTarget()) { 124 resourceProvider->attachStencilAttachment(smallRT2->asRenderTarget()); 125 } 126 REPORTER_ASSERT(reporter, 127 smallRT0 && smallRT2 && 128 smallRT0->asRenderTarget() && smallRT2->asRenderTarget() && 129 resourceProvider->attachStencilAttachment(smallRT0->asRenderTarget()) == 130 resourceProvider->attachStencilAttachment(smallRT2->asRenderTarget())); 131 132 // An RT with a much larger size should not share. 133 GrSurfaceDesc bigDesc; 134 bigDesc.fFlags = kRenderTarget_GrSurfaceFlag; 135 bigDesc.fConfig = kRGBA_8888_GrPixelConfig; 136 bigDesc.fWidth = 400; 137 bigDesc.fHeight = 200; 138 bigDesc.fSampleCnt = 0; 139 sk_sp<GrTexture> bigRT(cache->createTexture(bigDesc, SkBudgeted::kNo)); 140 if (bigRT && bigRT->asRenderTarget()) { 141 resourceProvider->attachStencilAttachment(bigRT->asRenderTarget()); 142 } 143 REPORTER_ASSERT(reporter, 144 smallRT0 && bigRT && 145 smallRT0->asRenderTarget() && bigRT->asRenderTarget() && 146 resourceProvider->attachStencilAttachment(smallRT0->asRenderTarget()) != 147 resourceProvider->attachStencilAttachment(bigRT->asRenderTarget())); 148 149 if (context->caps()->maxSampleCount() >= 4) { 150 // An RT with a different sample count should not share. 151 GrSurfaceDesc smallMSAADesc = smallDesc; 152 smallMSAADesc.fSampleCnt = 4; 153 sk_sp<GrTexture> smallMSAART0(cache->createTexture(smallMSAADesc, SkBudgeted::kNo)); 154 if (smallMSAART0 && smallMSAART0->asRenderTarget()) { 155 resourceProvider->attachStencilAttachment(smallMSAART0->asRenderTarget()); 156 } 157#ifdef SK_BUILD_FOR_ANDROID 158 if (!smallMSAART0) { 159 // The nexus player seems to fail to create MSAA textures. 160 return; 161 } 162#endif 163 REPORTER_ASSERT(reporter, 164 smallRT0 && smallMSAART0 && 165 smallRT0->asRenderTarget() && smallMSAART0->asRenderTarget() && 166 resourceProvider->attachStencilAttachment(smallRT0->asRenderTarget()) != 167 resourceProvider->attachStencilAttachment(smallMSAART0->asRenderTarget())); 168 // A second MSAA RT should share with the first MSAA RT. 169 sk_sp<GrTexture> smallMSAART1(cache->createTexture(smallMSAADesc, SkBudgeted::kNo)); 170 if (smallMSAART1 && smallMSAART1->asRenderTarget()) { 171 resourceProvider->attachStencilAttachment(smallMSAART1->asRenderTarget()); 172 } 173 REPORTER_ASSERT(reporter, 174 smallMSAART0 && smallMSAART1 && 175 smallMSAART0->asRenderTarget() && 176 smallMSAART1->asRenderTarget() && 177 resourceProvider->attachStencilAttachment(smallMSAART0->asRenderTarget()) == 178 resourceProvider->attachStencilAttachment(smallMSAART1->asRenderTarget())); 179 // But not one with a larger sample count should not. (Also check that the request for 4 180 // samples didn't get rounded up to >= 8 or else they could share.). 181 if (context->caps()->maxSampleCount() >= 8 && 182 smallMSAART0 && smallMSAART0->asRenderTarget() && 183 smallMSAART0->asRenderTarget()->numColorSamples() < 8) { 184 smallMSAADesc.fSampleCnt = 8; 185 smallMSAART1.reset(cache->createTexture(smallMSAADesc, SkBudgeted::kNo)); 186 sk_sp<GrTexture> smallMSAART1( 187 cache->createTexture(smallMSAADesc, SkBudgeted::kNo)); 188 if (smallMSAART1 && smallMSAART1->asRenderTarget()) { 189 resourceProvider->attachStencilAttachment(smallMSAART1->asRenderTarget()); 190 } 191 REPORTER_ASSERT(reporter, 192 smallMSAART0 && smallMSAART1 && 193 smallMSAART0->asRenderTarget() && 194 smallMSAART1->asRenderTarget() && 195 resourceProvider->attachStencilAttachment(smallMSAART0->asRenderTarget()) != 196 resourceProvider->attachStencilAttachment(smallMSAART1->asRenderTarget())); 197 } 198 } 199} 200 201DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ResourceCacheWrappedResources, reporter, ctxInfo) { 202 GrContext* context = ctxInfo.grContext(); 203 GrGpu* gpu = context->getGpu(); 204 // this test is only valid for GL 205 if (!gpu || !gpu->glContextForTesting()) { 206 return; 207 } 208 209 GrBackendObject texHandles[2]; 210 static const int kW = 100; 211 static const int kH = 100; 212 213 texHandles[0] = gpu->createTestingOnlyBackendTexture(nullptr, kW, kH, kRGBA_8888_GrPixelConfig); 214 texHandles[1] = gpu->createTestingOnlyBackendTexture(nullptr, kW, kH, kRGBA_8888_GrPixelConfig); 215 216 context->resetContext(); 217 218 GrBackendTextureDesc desc; 219 desc.fConfig = kBGRA_8888_GrPixelConfig; 220 desc.fWidth = kW; 221 desc.fHeight = kH; 222 223 desc.fTextureHandle = texHandles[0]; 224 sk_sp<GrTexture> borrowed(context->textureProvider()->wrapBackendTexture( 225 desc, kBorrow_GrWrapOwnership)); 226 227 desc.fTextureHandle = texHandles[1]; 228 sk_sp<GrTexture> adopted(context->textureProvider()->wrapBackendTexture( 229 desc, kAdopt_GrWrapOwnership)); 230 231 printf("\nborrowed: %p, adopted: %p\n", borrowed.get(), adopted.get()); 232 REPORTER_ASSERT(reporter, borrowed != nullptr && adopted != nullptr); 233 if (!borrowed || !adopted) { 234 return; 235 } 236 237 borrowed.reset(nullptr); 238 adopted.reset(nullptr); 239 240 context->flush(); 241 242 bool borrowedIsAlive = gpu->isTestingOnlyBackendTexture(texHandles[0]); 243 bool adoptedIsAlive = gpu->isTestingOnlyBackendTexture(texHandles[1]); 244 245 REPORTER_ASSERT(reporter, borrowedIsAlive); 246 REPORTER_ASSERT(reporter, !adoptedIsAlive); 247 248 gpu->deleteTestingOnlyBackendTexture(texHandles[0], !borrowedIsAlive); 249 gpu->deleteTestingOnlyBackendTexture(texHandles[1], !adoptedIsAlive); 250 251 context->resetContext(); 252} 253 254class TestResource : public GrGpuResource { 255 enum ScratchConstructor { kScratchConstructor }; 256public: 257 static const size_t kDefaultSize = 100; 258 259 /** Property that distinctly categorizes the resource. 260 * For example, textures have width, height, ... */ 261 enum SimulatedProperty { kA_SimulatedProperty, kB_SimulatedProperty }; 262 263 TestResource(GrGpu* gpu, SkBudgeted budgeted = SkBudgeted::kYes, size_t size = kDefaultSize) 264 : INHERITED(gpu) 265 , fToDelete(nullptr) 266 , fSize(size) 267 , fProperty(kA_SimulatedProperty) 268 , fIsScratch(false) { 269 ++fNumAlive; 270 this->registerWithCache(budgeted); 271 } 272 273 static TestResource* CreateScratch(GrGpu* gpu, SkBudgeted budgeted, 274 SimulatedProperty property) { 275 return new TestResource(gpu, budgeted, property, kScratchConstructor); 276 } 277 static TestResource* CreateWrapped(GrGpu* gpu, size_t size = kDefaultSize) { 278 return new TestResource(gpu, size); 279 } 280 281 ~TestResource() { 282 --fNumAlive; 283 SkSafeUnref(fToDelete); 284 } 285 286 void setSize(size_t size) { 287 fSize = size; 288 this->didChangeGpuMemorySize(); 289 } 290 291 static int NumAlive() { return fNumAlive; } 292 293 void setUnrefWhenDestroyed(TestResource* resource) { 294 SkRefCnt_SafeAssign(fToDelete, resource); 295 } 296 297 static void ComputeScratchKey(SimulatedProperty property, GrScratchKey* key) { 298 static GrScratchKey::ResourceType t = GrScratchKey::GenerateResourceType(); 299 GrScratchKey::Builder builder(key, t, kScratchKeyFieldCnt); 300 for (int i = 0; i < kScratchKeyFieldCnt; ++i) { 301 builder[i] = static_cast<uint32_t>(i + property); 302 } 303 } 304 305 static size_t ExpectedScratchKeySize() { 306 return sizeof(uint32_t) * (kScratchKeyFieldCnt + GrScratchKey::kMetaDataCnt); 307 } 308private: 309 static const int kScratchKeyFieldCnt = 6; 310 311 TestResource(GrGpu* gpu, SkBudgeted budgeted, SimulatedProperty property, ScratchConstructor) 312 : INHERITED(gpu) 313 , fToDelete(nullptr) 314 , fSize(kDefaultSize) 315 , fProperty(property) 316 , fIsScratch(true) { 317 ++fNumAlive; 318 this->registerWithCache(budgeted); 319 } 320 321 // Constructor for simulating resources that wrap backend objects. 322 TestResource(GrGpu* gpu, size_t size) 323 : INHERITED(gpu) 324 , fToDelete(nullptr) 325 , fSize(size) 326 , fProperty(kA_SimulatedProperty) 327 , fIsScratch(false) { 328 ++fNumAlive; 329 this->registerWithCacheWrapped(); 330 } 331 332 void computeScratchKey(GrScratchKey* key) const override { 333 if (fIsScratch) { 334 ComputeScratchKey(fProperty, key); 335 } 336 } 337 338 size_t onGpuMemorySize() const override { return fSize; } 339 340 TestResource* fToDelete; 341 size_t fSize; 342 static int fNumAlive; 343 SimulatedProperty fProperty; 344 bool fIsScratch; 345 typedef GrGpuResource INHERITED; 346}; 347int TestResource::fNumAlive = 0; 348 349class Mock { 350public: 351 Mock(int maxCnt, size_t maxBytes) { 352 fContext.reset(GrContext::CreateMockContext()); 353 SkASSERT(fContext); 354 fContext->setResourceCacheLimits(maxCnt, maxBytes); 355 GrResourceCache* cache = fContext->getResourceCache(); 356 cache->purgeAllUnlocked(); 357 SkASSERT(0 == cache->getResourceCount() && 0 == cache->getResourceBytes()); 358 } 359 360 GrResourceCache* cache() { return fContext->getResourceCache(); } 361 362 GrContext* context() { return fContext.get(); } 363 364private: 365 sk_sp<GrContext> fContext; 366}; 367 368static void test_no_key(skiatest::Reporter* reporter) { 369 Mock mock(10, 30000); 370 GrContext* context = mock.context(); 371 GrResourceCache* cache = mock.cache(); 372 373 // Create a bunch of resources with no keys 374 TestResource* a = new TestResource(context->getGpu()); 375 TestResource* b = new TestResource(context->getGpu()); 376 TestResource* c = new TestResource(context->getGpu()); 377 TestResource* d = new TestResource(context->getGpu()); 378 a->setSize(11); 379 b->setSize(12); 380 c->setSize(13); 381 d->setSize(14); 382 383 REPORTER_ASSERT(reporter, 4 == TestResource::NumAlive()); 384 REPORTER_ASSERT(reporter, 4 == cache->getResourceCount()); 385 REPORTER_ASSERT(reporter, a->gpuMemorySize() + b->gpuMemorySize() + c->gpuMemorySize() + 386 d->gpuMemorySize() == cache->getResourceBytes()); 387 388 // Should be safe to purge without deleting the resources since we still have refs. 389 cache->purgeAllUnlocked(); 390 REPORTER_ASSERT(reporter, 4 == TestResource::NumAlive()); 391 392 // Since the resources have neither unique nor scratch keys, delete immediately upon unref. 393 394 a->unref(); 395 REPORTER_ASSERT(reporter, 3 == TestResource::NumAlive()); 396 REPORTER_ASSERT(reporter, 3 == cache->getResourceCount()); 397 REPORTER_ASSERT(reporter, b->gpuMemorySize() + c->gpuMemorySize() + d->gpuMemorySize() == 398 cache->getResourceBytes()); 399 400 c->unref(); 401 REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive()); 402 REPORTER_ASSERT(reporter, 2 == cache->getResourceCount()); 403 REPORTER_ASSERT(reporter, b->gpuMemorySize() + d->gpuMemorySize() == 404 cache->getResourceBytes()); 405 406 d->unref(); 407 REPORTER_ASSERT(reporter, 1 == TestResource::NumAlive()); 408 REPORTER_ASSERT(reporter, 1 == cache->getResourceCount()); 409 REPORTER_ASSERT(reporter, b->gpuMemorySize() == cache->getResourceBytes()); 410 411 b->unref(); 412 REPORTER_ASSERT(reporter, 0 == TestResource::NumAlive()); 413 REPORTER_ASSERT(reporter, 0 == cache->getResourceCount()); 414 REPORTER_ASSERT(reporter, 0 == cache->getResourceBytes()); 415} 416 417// Each integer passed as a template param creates a new domain. 418template <int> static void make_unique_key(GrUniqueKey* key, int data) { 419 static GrUniqueKey::Domain d = GrUniqueKey::GenerateDomain(); 420 GrUniqueKey::Builder builder(key, d, 1); 421 builder[0] = data; 422} 423 424static void test_budgeting(skiatest::Reporter* reporter) { 425 Mock mock(10, 300); 426 GrContext* context = mock.context(); 427 GrResourceCache* cache = mock.cache(); 428 429 GrUniqueKey uniqueKey; 430 make_unique_key<0>(&uniqueKey, 0); 431 432 // Create a scratch, a unique, and a wrapped resource 433 TestResource* scratch = 434 TestResource::CreateScratch(context->getGpu(), SkBudgeted::kYes, TestResource::kB_SimulatedProperty); 435 scratch->setSize(10); 436 TestResource* unique = new TestResource(context->getGpu()); 437 unique->setSize(11); 438 unique->resourcePriv().setUniqueKey(uniqueKey); 439 TestResource* wrapped = TestResource::CreateWrapped(context->getGpu()); 440 wrapped->setSize(12); 441 TestResource* unbudgeted = 442 new TestResource(context->getGpu(), SkBudgeted::kNo); 443 unbudgeted->setSize(13); 444 445 // Make sure we can't add a unique key to the wrapped resource 446 GrUniqueKey uniqueKey2; 447 make_unique_key<0>(&uniqueKey2, 1); 448 wrapped->resourcePriv().setUniqueKey(uniqueKey2); 449 REPORTER_ASSERT(reporter, nullptr == cache->findAndRefUniqueResource(uniqueKey2)); 450 451 // Make sure sizes are as we expect 452 REPORTER_ASSERT(reporter, 4 == cache->getResourceCount()); 453 REPORTER_ASSERT(reporter, scratch->gpuMemorySize() + unique->gpuMemorySize() + 454 wrapped->gpuMemorySize() + unbudgeted->gpuMemorySize() == 455 cache->getResourceBytes()); 456 REPORTER_ASSERT(reporter, 2 == cache->getBudgetedResourceCount()); 457 REPORTER_ASSERT(reporter, scratch->gpuMemorySize() + unique->gpuMemorySize() == 458 cache->getBudgetedResourceBytes()); 459 460 // Our refs mean that the resources are non purgeable. 461 cache->purgeAllUnlocked(); 462 REPORTER_ASSERT(reporter, 4 == cache->getResourceCount()); 463 REPORTER_ASSERT(reporter, scratch->gpuMemorySize() + unique->gpuMemorySize() + 464 wrapped->gpuMemorySize() + unbudgeted->gpuMemorySize() == 465 cache->getResourceBytes()); 466 REPORTER_ASSERT(reporter, 2 == cache->getBudgetedResourceCount()); 467 REPORTER_ASSERT(reporter, scratch->gpuMemorySize() + unique->gpuMemorySize() == 468 cache->getBudgetedResourceBytes()); 469 470 // Unreffing the wrapped resource should free it right away. 471 wrapped->unref(); 472 REPORTER_ASSERT(reporter, 3 == cache->getResourceCount()); 473 REPORTER_ASSERT(reporter, scratch->gpuMemorySize() + unique->gpuMemorySize() + 474 unbudgeted->gpuMemorySize() == cache->getResourceBytes()); 475 476 // Now try freeing the budgeted resources first 477 wrapped = TestResource::CreateWrapped(context->getGpu()); 478 scratch->setSize(12); 479 unique->unref(); 480 cache->purgeAllUnlocked(); 481 REPORTER_ASSERT(reporter, 3 == cache->getResourceCount()); 482 REPORTER_ASSERT(reporter, scratch->gpuMemorySize() + wrapped->gpuMemorySize() + 483 unbudgeted->gpuMemorySize() == cache->getResourceBytes()); 484 REPORTER_ASSERT(reporter, 1 == cache->getBudgetedResourceCount()); 485 REPORTER_ASSERT(reporter, scratch->gpuMemorySize() == cache->getBudgetedResourceBytes()); 486 487 scratch->unref(); 488 cache->purgeAllUnlocked(); 489 REPORTER_ASSERT(reporter, 2 == cache->getResourceCount()); 490 REPORTER_ASSERT(reporter, unbudgeted->gpuMemorySize() + wrapped->gpuMemorySize() == 491 cache->getResourceBytes()); 492 REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceCount()); 493 REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceBytes()); 494 495 wrapped->unref(); 496 REPORTER_ASSERT(reporter, 1 == cache->getResourceCount()); 497 REPORTER_ASSERT(reporter, unbudgeted->gpuMemorySize() == cache->getResourceBytes()); 498 REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceCount()); 499 REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceBytes()); 500 501 unbudgeted->unref(); 502 REPORTER_ASSERT(reporter, 0 == cache->getResourceCount()); 503 REPORTER_ASSERT(reporter, 0 == cache->getResourceBytes()); 504 REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceCount()); 505 REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceBytes()); 506} 507 508static void test_unbudgeted(skiatest::Reporter* reporter) { 509 Mock mock(10, 30000); 510 GrContext* context = mock.context(); 511 GrResourceCache* cache = mock.cache(); 512 513 GrUniqueKey uniqueKey; 514 make_unique_key<0>(&uniqueKey, 0); 515 516 TestResource* scratch; 517 TestResource* unique; 518 TestResource* wrapped; 519 TestResource* unbudgeted; 520 521 // A large uncached or wrapped resource shouldn't evict anything. 522 scratch = TestResource::CreateScratch(context->getGpu(), SkBudgeted::kYes, 523 TestResource::kB_SimulatedProperty); 524 525 scratch->setSize(10); 526 scratch->unref(); 527 REPORTER_ASSERT(reporter, 1 == cache->getResourceCount()); 528 REPORTER_ASSERT(reporter, 10 == cache->getResourceBytes()); 529 REPORTER_ASSERT(reporter, 1 == cache->getBudgetedResourceCount()); 530 REPORTER_ASSERT(reporter, 10 == cache->getBudgetedResourceBytes()); 531 532 unique = new TestResource(context->getGpu()); 533 unique->setSize(11); 534 unique->resourcePriv().setUniqueKey(uniqueKey); 535 unique->unref(); 536 REPORTER_ASSERT(reporter, 2 == cache->getResourceCount()); 537 REPORTER_ASSERT(reporter, 21 == cache->getResourceBytes()); 538 REPORTER_ASSERT(reporter, 2 == cache->getBudgetedResourceCount()); 539 REPORTER_ASSERT(reporter, 21 == cache->getBudgetedResourceBytes()); 540 541 size_t large = 2 * cache->getResourceBytes(); 542 unbudgeted = new TestResource(context->getGpu(), SkBudgeted::kNo, large); 543 REPORTER_ASSERT(reporter, 3 == cache->getResourceCount()); 544 REPORTER_ASSERT(reporter, 21 + large == cache->getResourceBytes()); 545 REPORTER_ASSERT(reporter, 2 == cache->getBudgetedResourceCount()); 546 REPORTER_ASSERT(reporter, 21 == cache->getBudgetedResourceBytes()); 547 548 unbudgeted->unref(); 549 REPORTER_ASSERT(reporter, 2 == cache->getResourceCount()); 550 REPORTER_ASSERT(reporter, 21 == cache->getResourceBytes()); 551 REPORTER_ASSERT(reporter, 2 == cache->getBudgetedResourceCount()); 552 REPORTER_ASSERT(reporter, 21 == cache->getBudgetedResourceBytes()); 553 554 wrapped = TestResource::CreateWrapped(context->getGpu(), large); 555 REPORTER_ASSERT(reporter, 3 == cache->getResourceCount()); 556 REPORTER_ASSERT(reporter, 21 + large == cache->getResourceBytes()); 557 REPORTER_ASSERT(reporter, 2 == cache->getBudgetedResourceCount()); 558 REPORTER_ASSERT(reporter, 21 == cache->getBudgetedResourceBytes()); 559 560 wrapped->unref(); 561 REPORTER_ASSERT(reporter, 2 == cache->getResourceCount()); 562 REPORTER_ASSERT(reporter, 21 == cache->getResourceBytes()); 563 REPORTER_ASSERT(reporter, 2 == cache->getBudgetedResourceCount()); 564 REPORTER_ASSERT(reporter, 21 == cache->getBudgetedResourceBytes()); 565 566 cache->purgeAllUnlocked(); 567 REPORTER_ASSERT(reporter, 0 == cache->getResourceCount()); 568 REPORTER_ASSERT(reporter, 0 == cache->getResourceBytes()); 569 REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceCount()); 570 REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceBytes()); 571} 572 573// This method can't be static because it needs to friended in GrGpuResource::CacheAccess. 574void test_unbudgeted_to_scratch(skiatest::Reporter* reporter); 575/*static*/ void test_unbudgeted_to_scratch(skiatest::Reporter* reporter) { 576 Mock mock(10, 300); 577 GrContext* context = mock.context(); 578 GrResourceCache* cache = mock.cache(); 579 580 TestResource* resource = 581 TestResource::CreateScratch(context->getGpu(), SkBudgeted::kNo, 582 TestResource::kA_SimulatedProperty); 583 GrScratchKey key; 584 TestResource::ComputeScratchKey(TestResource::kA_SimulatedProperty, &key); 585 586 size_t size = resource->gpuMemorySize(); 587 for (int i = 0; i < 2; ++i) { 588 // Since this resource is unbudgeted, it should not be reachable as scratch. 589 REPORTER_ASSERT(reporter, resource->resourcePriv().getScratchKey() == key); 590 REPORTER_ASSERT(reporter, !resource->cacheAccess().isScratch()); 591 REPORTER_ASSERT(reporter, SkBudgeted::kNo == resource->resourcePriv().isBudgeted()); 592 REPORTER_ASSERT(reporter, nullptr == cache->findAndRefScratchResource(key, TestResource::kDefaultSize, 0)); 593 REPORTER_ASSERT(reporter, 1 == cache->getResourceCount()); 594 REPORTER_ASSERT(reporter, size == cache->getResourceBytes()); 595 REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceCount()); 596 REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceBytes()); 597 598 // Once it is unrefed, it should become available as scratch. 599 resource->unref(); 600 REPORTER_ASSERT(reporter, 1 == cache->getResourceCount()); 601 REPORTER_ASSERT(reporter, size == cache->getResourceBytes()); 602 REPORTER_ASSERT(reporter, 1 == cache->getBudgetedResourceCount()); 603 REPORTER_ASSERT(reporter, size == cache->getBudgetedResourceBytes()); 604 resource = static_cast<TestResource*>(cache->findAndRefScratchResource(key, TestResource::kDefaultSize, 0)); 605 REPORTER_ASSERT(reporter, resource); 606 REPORTER_ASSERT(reporter, resource->resourcePriv().getScratchKey() == key); 607 REPORTER_ASSERT(reporter, resource->cacheAccess().isScratch()); 608 REPORTER_ASSERT(reporter, SkBudgeted::kYes == resource->resourcePriv().isBudgeted()); 609 610 if (0 == i) { 611 // If made unbudgeted, it should return to original state: ref'ed and unbudgeted. Try 612 // the above tests again. 613 resource->resourcePriv().makeUnbudgeted(); 614 } else { 615 // After the second time around, try removing the scratch key 616 resource->resourcePriv().removeScratchKey(); 617 REPORTER_ASSERT(reporter, 1 == cache->getResourceCount()); 618 REPORTER_ASSERT(reporter, size == cache->getResourceBytes()); 619 REPORTER_ASSERT(reporter, 1 == cache->getBudgetedResourceCount()); 620 REPORTER_ASSERT(reporter, size == cache->getBudgetedResourceBytes()); 621 REPORTER_ASSERT(reporter, !resource->resourcePriv().getScratchKey().isValid()); 622 REPORTER_ASSERT(reporter, !resource->cacheAccess().isScratch()); 623 REPORTER_ASSERT(reporter, SkBudgeted::kYes == resource->resourcePriv().isBudgeted()); 624 625 // now when it is unrefed it should die since it has no key. 626 resource->unref(); 627 REPORTER_ASSERT(reporter, 0 == cache->getResourceCount()); 628 REPORTER_ASSERT(reporter, 0 == cache->getResourceBytes()); 629 REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceCount()); 630 REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceBytes()); 631 } 632 } 633} 634 635static void test_duplicate_scratch_key(skiatest::Reporter* reporter) { 636 Mock mock(5, 30000); 637 GrContext* context = mock.context(); 638 GrResourceCache* cache = mock.cache(); 639 640 // Create two resources that have the same scratch key. 641 TestResource* a = TestResource::CreateScratch(context->getGpu(), 642 SkBudgeted::kYes, 643 TestResource::kB_SimulatedProperty); 644 TestResource* b = TestResource::CreateScratch(context->getGpu(), 645 SkBudgeted::kYes, 646 TestResource::kB_SimulatedProperty); 647 a->setSize(11); 648 b->setSize(12); 649 GrScratchKey scratchKey1; 650 TestResource::ComputeScratchKey(TestResource::kA_SimulatedProperty, &scratchKey1); 651 // Check for negative case consistency. (leaks upon test failure.) 652 REPORTER_ASSERT(reporter, nullptr == cache->findAndRefScratchResource(scratchKey1, TestResource::kDefaultSize, 0)); 653 654 GrScratchKey scratchKey; 655 TestResource::ComputeScratchKey(TestResource::kB_SimulatedProperty, &scratchKey); 656 657 // Scratch resources are registered with GrResourceCache just by existing. There are 2. 658 REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive()); 659 SkDEBUGCODE(REPORTER_ASSERT(reporter, 2 == cache->countScratchEntriesForKey(scratchKey));) 660 REPORTER_ASSERT(reporter, 2 == cache->getResourceCount()); 661 REPORTER_ASSERT(reporter, a->gpuMemorySize() + b->gpuMemorySize() == 662 cache->getResourceBytes()); 663 664 // Our refs mean that the resources are non purgeable. 665 cache->purgeAllUnlocked(); 666 REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive()); 667 REPORTER_ASSERT(reporter, 2 == cache->getResourceCount()); 668 669 // Unref but don't purge 670 a->unref(); 671 b->unref(); 672 REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive()); 673 SkDEBUGCODE(REPORTER_ASSERT(reporter, 2 == cache->countScratchEntriesForKey(scratchKey));) 674 675 // Purge again. This time resources should be purgeable. 676 cache->purgeAllUnlocked(); 677 REPORTER_ASSERT(reporter, 0 == TestResource::NumAlive()); 678 REPORTER_ASSERT(reporter, 0 == cache->getResourceCount()); 679 SkDEBUGCODE(REPORTER_ASSERT(reporter, 0 == cache->countScratchEntriesForKey(scratchKey));) 680} 681 682static void test_remove_scratch_key(skiatest::Reporter* reporter) { 683 Mock mock(5, 30000); 684 GrContext* context = mock.context(); 685 GrResourceCache* cache = mock.cache(); 686 687 // Create two resources that have the same scratch key. 688 TestResource* a = TestResource::CreateScratch(context->getGpu(), SkBudgeted::kYes, 689 TestResource::kB_SimulatedProperty); 690 TestResource* b = TestResource::CreateScratch(context->getGpu(), SkBudgeted::kYes, 691 TestResource::kB_SimulatedProperty); 692 a->unref(); 693 b->unref(); 694 695 GrScratchKey scratchKey; 696 // Ensure that scratch key lookup is correct for negative case. 697 TestResource::ComputeScratchKey(TestResource::kA_SimulatedProperty, &scratchKey); 698 // (following leaks upon test failure). 699 REPORTER_ASSERT(reporter, cache->findAndRefScratchResource(scratchKey, TestResource::kDefaultSize, 0) == nullptr); 700 701 // Scratch resources are registered with GrResourceCache just by existing. There are 2. 702 TestResource::ComputeScratchKey(TestResource::kB_SimulatedProperty, &scratchKey); 703 REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive()); 704 SkDEBUGCODE(REPORTER_ASSERT(reporter, 2 == cache->countScratchEntriesForKey(scratchKey));) 705 REPORTER_ASSERT(reporter, 2 == cache->getResourceCount()); 706 707 // Find the first resource and remove its scratch key 708 GrGpuResource* find; 709 find = cache->findAndRefScratchResource(scratchKey, TestResource::kDefaultSize, 0); 710 find->resourcePriv().removeScratchKey(); 711 // It's still alive, but not cached by scratch key anymore 712 REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive()); 713 SkDEBUGCODE(REPORTER_ASSERT(reporter, 1 == cache->countScratchEntriesForKey(scratchKey));) 714 REPORTER_ASSERT(reporter, 2 == cache->getResourceCount()); 715 716 // The cache should immediately delete it when it's unrefed since it isn't accessible. 717 find->unref(); 718 REPORTER_ASSERT(reporter, 1 == TestResource::NumAlive()); 719 SkDEBUGCODE(REPORTER_ASSERT(reporter, 1 == cache->countScratchEntriesForKey(scratchKey));) 720 REPORTER_ASSERT(reporter, 1 == cache->getResourceCount()); 721 722 // Repeat for the second resource. 723 find = cache->findAndRefScratchResource(scratchKey, TestResource::kDefaultSize, 0); 724 find->resourcePriv().removeScratchKey(); 725 REPORTER_ASSERT(reporter, 1 == TestResource::NumAlive()); 726 SkDEBUGCODE(REPORTER_ASSERT(reporter, 0 == cache->countScratchEntriesForKey(scratchKey));) 727 REPORTER_ASSERT(reporter, 1 == cache->getResourceCount()); 728 729 // Should be able to call this multiple times with no problem. 730 find->resourcePriv().removeScratchKey(); 731 REPORTER_ASSERT(reporter, 1 == TestResource::NumAlive()); 732 SkDEBUGCODE(REPORTER_ASSERT(reporter, 0 == cache->countScratchEntriesForKey(scratchKey));) 733 REPORTER_ASSERT(reporter, 1 == cache->getResourceCount()); 734 735 find->unref(); 736 REPORTER_ASSERT(reporter, 0 == TestResource::NumAlive()); 737 SkDEBUGCODE(REPORTER_ASSERT(reporter, 0 == cache->countScratchEntriesForKey(scratchKey));) 738 REPORTER_ASSERT(reporter, 0 == cache->getResourceCount()); 739} 740 741static void test_scratch_key_consistency(skiatest::Reporter* reporter) { 742 Mock mock(5, 30000); 743 GrContext* context = mock.context(); 744 GrResourceCache* cache = mock.cache(); 745 746 // Create two resources that have the same scratch key. 747 TestResource* a = TestResource::CreateScratch(context->getGpu(), SkBudgeted::kYes, 748 TestResource::kB_SimulatedProperty); 749 TestResource* b = TestResource::CreateScratch(context->getGpu(), SkBudgeted::kYes, 750 TestResource::kB_SimulatedProperty); 751 a->unref(); 752 b->unref(); 753 754 GrScratchKey scratchKey; 755 // Ensure that scratch key comparison and assignment is consistent. 756 GrScratchKey scratchKey1; 757 TestResource::ComputeScratchKey(TestResource::kA_SimulatedProperty, &scratchKey1); 758 GrScratchKey scratchKey2; 759 TestResource::ComputeScratchKey(TestResource::kB_SimulatedProperty, &scratchKey2); 760 REPORTER_ASSERT(reporter, scratchKey1.size() == TestResource::ExpectedScratchKeySize()); 761 REPORTER_ASSERT(reporter, scratchKey1 != scratchKey2); 762 REPORTER_ASSERT(reporter, scratchKey2 != scratchKey1); 763 scratchKey = scratchKey1; 764 REPORTER_ASSERT(reporter, scratchKey.size() == TestResource::ExpectedScratchKeySize()); 765 REPORTER_ASSERT(reporter, scratchKey1 == scratchKey); 766 REPORTER_ASSERT(reporter, scratchKey == scratchKey1); 767 REPORTER_ASSERT(reporter, scratchKey2 != scratchKey); 768 REPORTER_ASSERT(reporter, scratchKey != scratchKey2); 769 scratchKey = scratchKey2; 770 REPORTER_ASSERT(reporter, scratchKey.size() == TestResource::ExpectedScratchKeySize()); 771 REPORTER_ASSERT(reporter, scratchKey1 != scratchKey); 772 REPORTER_ASSERT(reporter, scratchKey != scratchKey1); 773 REPORTER_ASSERT(reporter, scratchKey2 == scratchKey); 774 REPORTER_ASSERT(reporter, scratchKey == scratchKey2); 775 776 // Ensure that scratch key lookup is correct for negative case. 777 TestResource::ComputeScratchKey(TestResource::kA_SimulatedProperty, &scratchKey); 778 // (following leaks upon test failure). 779 REPORTER_ASSERT(reporter, cache->findAndRefScratchResource(scratchKey, TestResource::kDefaultSize, 0) == nullptr); 780 781 // Find the first resource with a scratch key and a copy of a scratch key. 782 TestResource::ComputeScratchKey(TestResource::kB_SimulatedProperty, &scratchKey); 783 GrGpuResource* find = cache->findAndRefScratchResource(scratchKey, TestResource::kDefaultSize, 0); 784 REPORTER_ASSERT(reporter, find != nullptr); 785 find->unref(); 786 787 scratchKey2 = scratchKey; 788 find = cache->findAndRefScratchResource(scratchKey2, TestResource::kDefaultSize, 0); 789 REPORTER_ASSERT(reporter, find != nullptr); 790 REPORTER_ASSERT(reporter, find == a || find == b); 791 792 GrGpuResource* find2 = cache->findAndRefScratchResource(scratchKey2, TestResource::kDefaultSize, 0); 793 REPORTER_ASSERT(reporter, find2 != nullptr); 794 REPORTER_ASSERT(reporter, find2 == a || find2 == b); 795 REPORTER_ASSERT(reporter, find2 != find); 796 find2->unref(); 797 find->unref(); 798} 799 800static void test_duplicate_unique_key(skiatest::Reporter* reporter) { 801 Mock mock(5, 30000); 802 GrContext* context = mock.context(); 803 GrResourceCache* cache = mock.cache(); 804 805 GrUniqueKey key; 806 make_unique_key<0>(&key, 0); 807 808 // Create two resources that we will attempt to register with the same unique key. 809 TestResource* a = new TestResource(context->getGpu()); 810 a->setSize(11); 811 812 // Set key on resource a. 813 a->resourcePriv().setUniqueKey(key); 814 REPORTER_ASSERT(reporter, a == cache->findAndRefUniqueResource(key)); 815 a->unref(); 816 817 // Make sure that redundantly setting a's key works. 818 a->resourcePriv().setUniqueKey(key); 819 REPORTER_ASSERT(reporter, a == cache->findAndRefUniqueResource(key)); 820 a->unref(); 821 REPORTER_ASSERT(reporter, 1 == cache->getResourceCount()); 822 REPORTER_ASSERT(reporter, a->gpuMemorySize() == cache->getResourceBytes()); 823 REPORTER_ASSERT(reporter, 1 == TestResource::NumAlive()); 824 825 // Create resource b and set the same key. It should replace a's unique key cache entry. 826 TestResource* b = new TestResource(context->getGpu()); 827 b->setSize(12); 828 b->resourcePriv().setUniqueKey(key); 829 REPORTER_ASSERT(reporter, b == cache->findAndRefUniqueResource(key)); 830 b->unref(); 831 832 // Still have two resources because a is still reffed. 833 REPORTER_ASSERT(reporter, 2 == cache->getResourceCount()); 834 REPORTER_ASSERT(reporter, a->gpuMemorySize() + b->gpuMemorySize() == cache->getResourceBytes()); 835 REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive()); 836 837 a->unref(); 838 // Now a should be gone. 839 REPORTER_ASSERT(reporter, 1 == cache->getResourceCount()); 840 REPORTER_ASSERT(reporter, b->gpuMemorySize() == cache->getResourceBytes()); 841 REPORTER_ASSERT(reporter, 1 == TestResource::NumAlive()); 842 843 // Now replace b with c, but make sure c can start with one unique key and change it to b's key. 844 // Also make b be unreffed when replacement occurs. 845 b->unref(); 846 TestResource* c = new TestResource(context->getGpu()); 847 GrUniqueKey differentKey; 848 make_unique_key<0>(&differentKey, 1); 849 c->setSize(13); 850 c->resourcePriv().setUniqueKey(differentKey); 851 REPORTER_ASSERT(reporter, 2 == cache->getResourceCount()); 852 REPORTER_ASSERT(reporter, b->gpuMemorySize() + c->gpuMemorySize() == cache->getResourceBytes()); 853 REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive()); 854 // c replaces b and b should be immediately purged. 855 c->resourcePriv().setUniqueKey(key); 856 REPORTER_ASSERT(reporter, 1 == cache->getResourceCount()); 857 REPORTER_ASSERT(reporter, c->gpuMemorySize() == cache->getResourceBytes()); 858 REPORTER_ASSERT(reporter, 1 == TestResource::NumAlive()); 859 860 // c shouldn't be purged because it is ref'ed. 861 cache->purgeAllUnlocked(); 862 REPORTER_ASSERT(reporter, 1 == cache->getResourceCount()); 863 REPORTER_ASSERT(reporter, c->gpuMemorySize() == cache->getResourceBytes()); 864 REPORTER_ASSERT(reporter, 1 == TestResource::NumAlive()); 865 866 // Drop the ref on c, it should be kept alive because it has a unique key. 867 c->unref(); 868 REPORTER_ASSERT(reporter, 1 == cache->getResourceCount()); 869 REPORTER_ASSERT(reporter, c->gpuMemorySize() == cache->getResourceBytes()); 870 REPORTER_ASSERT(reporter, 1 == TestResource::NumAlive()); 871 872 // Verify that we can find c, then remove its unique key. It should get purged immediately. 873 REPORTER_ASSERT(reporter, c == cache->findAndRefUniqueResource(key)); 874 c->resourcePriv().removeUniqueKey(); 875 c->unref(); 876 REPORTER_ASSERT(reporter, 0 == cache->getResourceCount()); 877 REPORTER_ASSERT(reporter, 0 == cache->getResourceBytes()); 878 REPORTER_ASSERT(reporter, 0 == TestResource::NumAlive()); 879 880 { 881 GrUniqueKey key2; 882 make_unique_key<0>(&key2, 0); 883 sk_sp<TestResource> d(new TestResource(context->getGpu())); 884 int foo = 4132; 885 key2.setCustomData(SkData::MakeWithCopy(&foo, sizeof(foo))); 886 d->resourcePriv().setUniqueKey(key2); 887 } 888 889 GrUniqueKey key3; 890 make_unique_key<0>(&key3, 0); 891 sk_sp<GrGpuResource> d2(cache->findAndRefUniqueResource(key3)); 892 REPORTER_ASSERT(reporter, *(int*) d2->getUniqueKey().getCustomData()->data() == 4132); 893} 894 895static void test_purge_invalidated(skiatest::Reporter* reporter) { 896 Mock mock(5, 30000); 897 GrContext* context = mock.context(); 898 GrResourceCache* cache = mock.cache(); 899 900 GrUniqueKey key1, key2, key3; 901 make_unique_key<0>(&key1, 1); 902 make_unique_key<0>(&key2, 2); 903 make_unique_key<0>(&key3, 3); 904 905 // Add three resources to the cache. Only c is usable as scratch. 906 TestResource* a = new TestResource(context->getGpu()); 907 TestResource* b = new TestResource(context->getGpu()); 908 TestResource* c = TestResource::CreateScratch(context->getGpu(), SkBudgeted::kYes, 909 TestResource::kA_SimulatedProperty); 910 a->resourcePriv().setUniqueKey(key1); 911 b->resourcePriv().setUniqueKey(key2); 912 c->resourcePriv().setUniqueKey(key3); 913 a->unref(); 914 // hold b until *after* the message is sent. 915 c->unref(); 916 917 REPORTER_ASSERT(reporter, cache->hasUniqueKey(key1)); 918 REPORTER_ASSERT(reporter, cache->hasUniqueKey(key2)); 919 REPORTER_ASSERT(reporter, cache->hasUniqueKey(key3)); 920 REPORTER_ASSERT(reporter, 3 == TestResource::NumAlive()); 921 922 typedef GrUniqueKeyInvalidatedMessage Msg; 923 typedef SkMessageBus<GrUniqueKeyInvalidatedMessage> Bus; 924 925 // Invalidate two of the three, they should be purged and no longer accessible via their keys. 926 Bus::Post(Msg(key1)); 927 Bus::Post(Msg(key2)); 928 cache->purgeAsNeeded(); 929 // a should be deleted now, but we still have a ref on b. 930 REPORTER_ASSERT(reporter, !cache->hasUniqueKey(key1)); 931 REPORTER_ASSERT(reporter, !cache->hasUniqueKey(key2)); 932 REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive()); 933 REPORTER_ASSERT(reporter, cache->hasUniqueKey(key3)); 934 935 // Invalidate the third. 936 Bus::Post(Msg(key3)); 937 cache->purgeAsNeeded(); 938 // we still have a ref on b, c should be recycled as scratch. 939 REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive()); 940 REPORTER_ASSERT(reporter, !cache->hasUniqueKey(key3)); 941 942 // make b purgeable. It should be immediately deleted since it has no key. 943 b->unref(); 944 REPORTER_ASSERT(reporter, 1 == TestResource::NumAlive()); 945 946 // Make sure we actually get to c via it's scratch key, before we say goodbye. 947 GrScratchKey scratchKey; 948 TestResource::ComputeScratchKey(TestResource::kA_SimulatedProperty, &scratchKey); 949 GrGpuResource* scratch = cache->findAndRefScratchResource(scratchKey, TestResource::kDefaultSize, 0); 950 REPORTER_ASSERT(reporter, scratch == c); 951 SkSafeUnref(scratch); 952 953 // Get rid of c. 954 cache->purgeAllUnlocked(); 955 scratch = cache->findAndRefScratchResource(scratchKey, TestResource::kDefaultSize, 0); 956 REPORTER_ASSERT(reporter, 0 == TestResource::NumAlive()); 957 REPORTER_ASSERT(reporter, 0 == cache->getResourceCount()); 958 REPORTER_ASSERT(reporter, 0 == cache->getResourceBytes()); 959 REPORTER_ASSERT(reporter, !scratch); 960 SkSafeUnref(scratch); 961} 962 963static void test_cache_chained_purge(skiatest::Reporter* reporter) { 964 Mock mock(3, 30000); 965 GrContext* context = mock.context(); 966 GrResourceCache* cache = mock.cache(); 967 968 GrUniqueKey key1, key2; 969 make_unique_key<0>(&key1, 1); 970 make_unique_key<0>(&key2, 2); 971 972 TestResource* a = new TestResource(context->getGpu()); 973 TestResource* b = new TestResource(context->getGpu()); 974 a->resourcePriv().setUniqueKey(key1); 975 b->resourcePriv().setUniqueKey(key2); 976 977 // Make a cycle 978 a->setUnrefWhenDestroyed(b); 979 b->setUnrefWhenDestroyed(a); 980 981 REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive()); 982 983 a->unref(); 984 b->unref(); 985 986 REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive()); 987 988 cache->purgeAllUnlocked(); 989 REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive()); 990 991 // Break the cycle 992 a->setUnrefWhenDestroyed(nullptr); 993 REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive()); 994 995 cache->purgeAllUnlocked(); 996 REPORTER_ASSERT(reporter, 0 == TestResource::NumAlive()); 997} 998 999static void test_resource_size_changed(skiatest::Reporter* reporter) { 1000 GrUniqueKey key1, key2; 1001 make_unique_key<0>(&key1, 1); 1002 make_unique_key<0>(&key2, 2); 1003 1004 // Test changing resources sizes (both increase & decrease). 1005 { 1006 Mock mock(3, 30000); 1007 GrContext* context = mock.context(); 1008 GrResourceCache* cache = mock.cache(); 1009 1010 TestResource* a = new TestResource(context->getGpu()); 1011 a->resourcePriv().setUniqueKey(key1); 1012 a->unref(); 1013 1014 TestResource* b = new TestResource(context->getGpu()); 1015 b->resourcePriv().setUniqueKey(key2); 1016 b->unref(); 1017 1018 REPORTER_ASSERT(reporter, 200 == cache->getResourceBytes()); 1019 REPORTER_ASSERT(reporter, 2 == cache->getResourceCount()); 1020 { 1021 sk_sp<TestResource> find2( 1022 static_cast<TestResource*>(cache->findAndRefUniqueResource(key2))); 1023 find2->setSize(200); 1024 sk_sp<TestResource> find1( 1025 static_cast<TestResource*>(cache->findAndRefUniqueResource(key1))); 1026 find1->setSize(50); 1027 } 1028 1029 REPORTER_ASSERT(reporter, 250 == cache->getResourceBytes()); 1030 REPORTER_ASSERT(reporter, 2 == cache->getResourceCount()); 1031 } 1032 1033 // Test increasing a resources size beyond the cache budget. 1034 { 1035 Mock mock(2, 300); 1036 GrContext* context = mock.context(); 1037 GrResourceCache* cache = mock.cache(); 1038 1039 TestResource* a = new TestResource(context->getGpu()); 1040 a->setSize(100); 1041 a->resourcePriv().setUniqueKey(key1); 1042 a->unref(); 1043 1044 TestResource* b = new TestResource(context->getGpu()); 1045 b->setSize(100); 1046 b->resourcePriv().setUniqueKey(key2); 1047 b->unref(); 1048 1049 REPORTER_ASSERT(reporter, 200 == cache->getResourceBytes()); 1050 REPORTER_ASSERT(reporter, 2 == cache->getResourceCount()); 1051 1052 { 1053 sk_sp<TestResource> find2(static_cast<TestResource*>( 1054 cache->findAndRefUniqueResource(key2))); 1055 find2->setSize(201); 1056 } 1057 REPORTER_ASSERT(reporter, !cache->hasUniqueKey(key1)); 1058 1059 REPORTER_ASSERT(reporter, 201 == cache->getResourceBytes()); 1060 REPORTER_ASSERT(reporter, 1 == cache->getResourceCount()); 1061 } 1062} 1063 1064static void test_timestamp_wrap(skiatest::Reporter* reporter) { 1065 static const int kCount = 50; 1066 static const int kBudgetCnt = kCount / 2; 1067 static const int kLockedFreq = 8; 1068 static const int kBudgetSize = 0x80000000; 1069 1070 SkRandom random; 1071 1072 // Run the test 2*kCount times; 1073 for (int i = 0; i < 2 * kCount; ++i ) { 1074 Mock mock(kBudgetCnt, kBudgetSize); 1075 GrContext* context = mock.context(); 1076 GrResourceCache* cache = mock.cache(); 1077 1078 // Pick a random number of resources to add before the timestamp will wrap. 1079 cache->changeTimestamp(SK_MaxU32 - random.nextULessThan(kCount + 1)); 1080 1081 static const int kNumToPurge = kCount - kBudgetCnt; 1082 1083 SkTDArray<int> shouldPurgeIdxs; 1084 int purgeableCnt = 0; 1085 SkTDArray<GrGpuResource*> resourcesToUnref; 1086 1087 // Add kCount resources, holding onto resources at random so we have a mix of purgeable and 1088 // unpurgeable resources. 1089 for (int j = 0; j < kCount; ++j) { 1090 GrUniqueKey key; 1091 make_unique_key<0>(&key, j); 1092 1093 TestResource* r = new TestResource(context->getGpu()); 1094 r->resourcePriv().setUniqueKey(key); 1095 if (random.nextU() % kLockedFreq) { 1096 // Make this is purgeable. 1097 r->unref(); 1098 ++purgeableCnt; 1099 if (purgeableCnt <= kNumToPurge) { 1100 *shouldPurgeIdxs.append() = j; 1101 } 1102 } else { 1103 *resourcesToUnref.append() = r; 1104 } 1105 } 1106 1107 // Verify that the correct resources were purged. 1108 int currShouldPurgeIdx = 0; 1109 for (int j = 0; j < kCount; ++j) { 1110 GrUniqueKey key; 1111 make_unique_key<0>(&key, j); 1112 GrGpuResource* res = cache->findAndRefUniqueResource(key); 1113 if (currShouldPurgeIdx < shouldPurgeIdxs.count() && 1114 shouldPurgeIdxs[currShouldPurgeIdx] == j) { 1115 ++currShouldPurgeIdx; 1116 REPORTER_ASSERT(reporter, nullptr == res); 1117 } else { 1118 REPORTER_ASSERT(reporter, nullptr != res); 1119 } 1120 SkSafeUnref(res); 1121 } 1122 1123 for (int j = 0; j < resourcesToUnref.count(); ++j) { 1124 resourcesToUnref[j]->unref(); 1125 } 1126 } 1127} 1128 1129static void test_flush(skiatest::Reporter* reporter) { 1130 Mock mock(1000000, 1000000); 1131 GrContext* context = mock.context(); 1132 GrResourceCache* cache = mock.cache(); 1133 1134 // The current cache impl will round the max flush count to the next power of 2. So we choose a 1135 // power of two here to keep things simpler. 1136 static const int kFlushCount = 16; 1137 cache->setLimits(1000000, 1000000, kFlushCount); 1138 1139 { 1140 // Insert a resource and send a flush notification kFlushCount times. 1141 for (int i = 0; i < kFlushCount; ++i) { 1142 TestResource* r = new TestResource(context->getGpu()); 1143 GrUniqueKey k; 1144 make_unique_key<1>(&k, i); 1145 r->resourcePriv().setUniqueKey(k); 1146 r->unref(); 1147 cache->notifyFlushOccurred(GrResourceCache::kExternal); 1148 } 1149 1150 // Send flush notifications to the cache. Each flush should purge the oldest resource. 1151 for (int i = 0; i < kFlushCount; ++i) { 1152 cache->notifyFlushOccurred(GrResourceCache::kExternal); 1153 REPORTER_ASSERT(reporter, kFlushCount - i - 1 == cache->getResourceCount()); 1154 for (int j = 0; j < i; ++j) { 1155 GrUniqueKey k; 1156 make_unique_key<1>(&k, j); 1157 GrGpuResource* r = cache->findAndRefUniqueResource(k); 1158 REPORTER_ASSERT(reporter, !SkToBool(r)); 1159 SkSafeUnref(r); 1160 } 1161 } 1162 1163 REPORTER_ASSERT(reporter, 0 == cache->getResourceCount()); 1164 cache->purgeAllUnlocked(); 1165 } 1166 1167 // Do a similar test but where we leave refs on some resources to prevent them from being 1168 // purged. 1169 { 1170 GrGpuResource* refedResources[kFlushCount >> 1]; 1171 for (int i = 0; i < kFlushCount; ++i) { 1172 TestResource* r = new TestResource(context->getGpu()); 1173 GrUniqueKey k; 1174 make_unique_key<1>(&k, i); 1175 r->resourcePriv().setUniqueKey(k); 1176 // Leave a ref on every other resource, beginning with the first. 1177 if (SkToBool(i & 0x1)) { 1178 refedResources[i/2] = r; 1179 } else { 1180 r->unref(); 1181 } 1182 cache->notifyFlushOccurred(GrResourceCache::kExternal); 1183 } 1184 1185 for (int i = 0; i < kFlushCount; ++i) { 1186 // Should get a resource purged every other flush. 1187 cache->notifyFlushOccurred(GrResourceCache::kExternal); 1188 REPORTER_ASSERT(reporter, kFlushCount - i/2 - 1 == cache->getResourceCount()); 1189 } 1190 1191 // Unref all the resources that we kept refs on in the first loop. 1192 for (int i = 0; i < kFlushCount >> 1; ++i) { 1193 refedResources[i]->unref(); 1194 } 1195 1196 // After kFlushCount + 1 flushes they all will have sat in the purgeable queue for 1197 // kFlushCount full flushes. 1198 for (int i = 0; i < kFlushCount + 1; ++i) { 1199 REPORTER_ASSERT(reporter, kFlushCount >> 1 == cache->getResourceCount()); 1200 cache->notifyFlushOccurred(GrResourceCache::kExternal); 1201 } 1202 REPORTER_ASSERT(reporter, 0 == cache->getResourceCount()); 1203 1204 cache->purgeAllUnlocked(); 1205 } 1206 1207 REPORTER_ASSERT(reporter, 0 == cache->getResourceCount()); 1208 1209 // Verify that calling flush() on a GrContext with nothing to do will not trigger resource 1210 // eviction. 1211 context->flush(); 1212 for (int i = 0; i < 10; ++i) { 1213 TestResource* r = new TestResource(context->getGpu()); 1214 GrUniqueKey k; 1215 make_unique_key<1>(&k, i); 1216 r->resourcePriv().setUniqueKey(k); 1217 r->unref(); 1218 } 1219 REPORTER_ASSERT(reporter, 10 == cache->getResourceCount()); 1220 for (int i = 0; i < 10 * kFlushCount; ++i) { 1221 context->flush(); 1222 } 1223 REPORTER_ASSERT(reporter, 10 == cache->getResourceCount()); 1224} 1225 1226static void test_large_resource_count(skiatest::Reporter* reporter) { 1227 // Set the cache size to double the resource count because we're going to create 2x that number 1228 // resources, using two different key domains. Add a little slop to the bytes because we resize 1229 // down to 1 byte after creating the resource. 1230 static const int kResourceCnt = 2000; 1231 1232 Mock mock(2 * kResourceCnt, 2 * kResourceCnt + 1000); 1233 GrContext* context = mock.context(); 1234 GrResourceCache* cache = mock.cache(); 1235 1236 for (int i = 0; i < kResourceCnt; ++i) { 1237 GrUniqueKey key1, key2; 1238 make_unique_key<1>(&key1, i); 1239 make_unique_key<2>(&key2, i); 1240 1241 TestResource* resource; 1242 1243 resource = new TestResource(context->getGpu()); 1244 resource->resourcePriv().setUniqueKey(key1); 1245 resource->setSize(1); 1246 resource->unref(); 1247 1248 resource = new TestResource(context->getGpu()); 1249 resource->resourcePriv().setUniqueKey(key2); 1250 resource->setSize(1); 1251 resource->unref(); 1252 } 1253 1254 REPORTER_ASSERT(reporter, TestResource::NumAlive() == 2 * kResourceCnt); 1255 REPORTER_ASSERT(reporter, cache->getBudgetedResourceBytes() == 2 * kResourceCnt); 1256 REPORTER_ASSERT(reporter, cache->getBudgetedResourceCount() == 2 * kResourceCnt); 1257 REPORTER_ASSERT(reporter, cache->getResourceBytes() == 2 * kResourceCnt); 1258 REPORTER_ASSERT(reporter, cache->getResourceCount() == 2 * kResourceCnt); 1259 for (int i = 0; i < kResourceCnt; ++i) { 1260 GrUniqueKey key1, key2; 1261 make_unique_key<1>(&key1, i); 1262 make_unique_key<2>(&key2, i); 1263 1264 REPORTER_ASSERT(reporter, cache->hasUniqueKey(key1)); 1265 REPORTER_ASSERT(reporter, cache->hasUniqueKey(key2)); 1266 } 1267 1268 cache->purgeAllUnlocked(); 1269 REPORTER_ASSERT(reporter, TestResource::NumAlive() == 0); 1270 REPORTER_ASSERT(reporter, cache->getBudgetedResourceBytes() == 0); 1271 REPORTER_ASSERT(reporter, cache->getBudgetedResourceCount() == 0); 1272 REPORTER_ASSERT(reporter, cache->getResourceBytes() == 0); 1273 REPORTER_ASSERT(reporter, cache->getResourceCount() == 0); 1274 1275 for (int i = 0; i < kResourceCnt; ++i) { 1276 GrUniqueKey key1, key2; 1277 make_unique_key<1>(&key1, i); 1278 make_unique_key<2>(&key2, i); 1279 1280 REPORTER_ASSERT(reporter, !cache->hasUniqueKey(key1)); 1281 REPORTER_ASSERT(reporter, !cache->hasUniqueKey(key2)); 1282 } 1283} 1284 1285static void test_custom_data(skiatest::Reporter* reporter) { 1286 GrUniqueKey key1, key2; 1287 make_unique_key<0>(&key1, 1); 1288 make_unique_key<0>(&key2, 2); 1289 int foo = 4132; 1290 key1.setCustomData(SkData::MakeWithCopy(&foo, sizeof(foo))); 1291 REPORTER_ASSERT(reporter, *(int*) key1.getCustomData()->data() == 4132); 1292 REPORTER_ASSERT(reporter, key2.getCustomData() == nullptr); 1293 1294 // Test that copying a key also takes a ref on its custom data. 1295 GrUniqueKey key3 = key1; 1296 REPORTER_ASSERT(reporter, *(int*) key3.getCustomData()->data() == 4132); 1297} 1298 1299static void test_abandoned(skiatest::Reporter* reporter) { 1300 Mock mock(10, 300); 1301 GrContext* context = mock.context(); 1302 sk_sp<GrGpuResource> resource(new TestResource(context->getGpu())); 1303 context->abandonContext(); 1304 1305 REPORTER_ASSERT(reporter, resource->wasDestroyed()); 1306 1307 // Call all the public methods on resource in the abandoned state. They shouldn't crash. 1308 1309 resource->uniqueID(); 1310 resource->getUniqueKey(); 1311 resource->wasDestroyed(); 1312 resource->gpuMemorySize(); 1313 resource->getContext(); 1314 1315 resource->abandon(); 1316 resource->resourcePriv().getScratchKey(); 1317 resource->resourcePriv().isBudgeted(); 1318 resource->resourcePriv().makeBudgeted(); 1319 resource->resourcePriv().makeUnbudgeted(); 1320 resource->resourcePriv().removeScratchKey(); 1321 GrUniqueKey key; 1322 make_unique_key<0>(&key, 1); 1323 resource->resourcePriv().setUniqueKey(key); 1324 resource->resourcePriv().removeUniqueKey(); 1325} 1326 1327DEF_GPUTEST(ResourceCacheMisc, reporter, factory) { 1328 // The below tests create their own mock contexts. 1329 test_no_key(reporter); 1330 test_budgeting(reporter); 1331 test_unbudgeted(reporter); 1332 test_unbudgeted_to_scratch(reporter); 1333 test_duplicate_unique_key(reporter); 1334 test_duplicate_scratch_key(reporter); 1335 test_remove_scratch_key(reporter); 1336 test_scratch_key_consistency(reporter); 1337 test_purge_invalidated(reporter); 1338 test_cache_chained_purge(reporter); 1339 test_resource_size_changed(reporter); 1340 test_timestamp_wrap(reporter); 1341 test_flush(reporter); 1342 test_large_resource_count(reporter); 1343 test_custom_data(reporter); 1344 test_abandoned(reporter); 1345} 1346 1347#endif 1348