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