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