Flutter Engine
The Flutter Engine
ResourceCacheTest.cpp
Go to the documentation of this file.
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
13#include "include/core/SkData.h"
16#include "include/core/SkRect.h"
18#include "include/core/SkSize.h"
26#include "include/gpu/GrTypes.h"
32#include "src/base/SkRandom.h"
35#include "src/gpu/ResourceKey.h"
51#include "tests/Test.h"
54
55#include <chrono>
56#include <cstddef>
57#include <cstdint>
58#include <initializer_list>
59#include <memory>
60#include <string_view>
61#include <thread>
62#include <utility>
63#include <vector>
64
65class GrAttachment;
66struct GrContextOptions;
67
68static const int gWidth = 640;
69static const int gHeight = 480;
70
71////////////////////////////////////////////////////////////////////////////////
74 ctxInfo,
76 auto context = ctxInfo.directContext();
79 SkCanvas* canvas = surface->getCanvas();
80
82
84 src.allocN32Pixels(size.width(), size.height());
85 src.eraseColor(SK_ColorBLACK);
86 size_t srcSize = src.computeByteSize();
87
88 size_t initialCacheSize;
89 context->getResourceCacheUsage(nullptr, &initialCacheSize);
90
91 size_t oldMaxBytes = context->getResourceCacheLimit();
92
93 // Set the cache limits so we can fit 10 "src" images and the
94 // max number of textures doesn't matter
95 size_t maxCacheSize = initialCacheSize + 10*srcSize;
96 context->setResourceCacheLimit(maxCacheSize);
97
99 readback.allocN32Pixels(size.width(), size.height());
100
101 for (int i = 0; i < 100; ++i) {
102 canvas->drawImage(src.asImage(), 0, 0);
103 surface->readPixels(readback, 0, 0);
104
105 // "modify" the src texture
106 src.notifyPixelsChanged();
107
108 size_t curCacheSize;
109 context->getResourceCacheUsage(nullptr, &curCacheSize);
110
111 // we should never go over the size limit
112 REPORTER_ASSERT(reporter, curCacheSize <= maxCacheSize);
113 }
114
115 context->setResourceCacheLimit(oldMaxBytes);
116}
117
122 return false;
123 }
125}
126
128
130 int size,
131 int sampleCount,
132 skgpu::Budgeted budgeted) {
133 auto format =
135 sk_sp<GrTexture> tex(provider->createTexture({size, size},
136 format,
139 sampleCount,
141 budgeted,
143 /*label=*/{}));
144 if (!tex || !tex->asRenderTarget()) {
145 return nullptr;
146 }
147
148 if (!provider->attachStencilAttachment(tex->asRenderTarget(), sampleCount > 1)) {
149 return nullptr;
150 }
152
153 return sk_ref_sp(tex->asRenderTarget());
154}
155
156// This currently fails on ES3 ANGLE contexts
157DEF_GANESH_TEST_FOR_CONTEXTS(ResourceCacheStencilBuffers,
159 reporter,
160 ctxInfo,
161 nullptr,
163 auto context = ctxInfo.directContext();
164 const GrCaps* caps = context->priv().caps();
165
166 if (caps->avoidStencilBuffers()) {
167 return;
168 }
169
170 GrResourceProvider* resourceProvider = context->priv().resourceProvider();
171
174
175 sk_sp<GrRenderTarget> smallRT0 =
176 create_RT_with_SB(resourceProvider, 4, 1, skgpu::Budgeted::kYes);
177 REPORTER_ASSERT(reporter, smallRT0);
178
179 {
180 // Two budgeted RTs with the same desc should share a stencil buffer.
181 sk_sp<GrRenderTarget> smallRT1 =
182 create_RT_with_SB(resourceProvider, 4, 1, skgpu::Budgeted::kYes);
183 REPORTER_ASSERT(reporter, smallRT1);
184
185 REPORTER_ASSERT(reporter, get_SB(smallRT0.get()) == get_SB(smallRT1.get()));
186 }
187
188 {
189 // An unbudgeted RT with the same desc should also share.
190 sk_sp<GrRenderTarget> smallRT2 =
191 create_RT_with_SB(resourceProvider, 4, 1, skgpu::Budgeted::kNo);
192 REPORTER_ASSERT(reporter, smallRT2);
193
194 REPORTER_ASSERT(reporter, get_SB(smallRT0.get()) == get_SB(smallRT2.get()));
195 }
196
197 {
198 // An RT with a much larger size should not share.
200 create_RT_with_SB(resourceProvider, 400, 1, skgpu::Budgeted::kNo);
202
203 REPORTER_ASSERT(reporter, get_SB(smallRT0.get()) != get_SB(bigRT.get()));
204 }
205
206 int smallSampleCount =
207 context->priv().caps()->getRenderTargetSampleCount(2, format);
208 if (smallSampleCount > 1) {
209 // An RT with a different sample count should not share.
210 sk_sp<GrRenderTarget> smallMSAART0 =
211 create_RT_with_SB(resourceProvider, 4, smallSampleCount, skgpu::Budgeted::kNo);
212 REPORTER_ASSERT(reporter, smallMSAART0);
213
214 REPORTER_ASSERT(reporter, get_SB(smallRT0.get()) != get_SB(smallMSAART0.get()));
215
216 {
217 // A second MSAA RT should share with the first MSAA RT.
218 sk_sp<GrRenderTarget> smallMSAART1 =
219 create_RT_with_SB(resourceProvider, 4, smallSampleCount, skgpu::Budgeted::kNo);
220 REPORTER_ASSERT(reporter, smallMSAART1);
221
222 REPORTER_ASSERT(reporter, get_SB(smallMSAART0.get()) == get_SB(smallMSAART1.get()));
223 }
224
225 // But one with a larger sample count should not. (Also check that the two requests didn't
226 // rounded up to the same actual sample count or else they could share.).
227 int bigSampleCount = context->priv().caps()->getRenderTargetSampleCount(5, format);
228 if (bigSampleCount > 0 && bigSampleCount != smallSampleCount) {
229 sk_sp<GrRenderTarget> smallMSAART2 =
230 create_RT_with_SB(resourceProvider, 4, bigSampleCount, skgpu::Budgeted::kNo);
231 REPORTER_ASSERT(reporter, smallMSAART2);
232
233 REPORTER_ASSERT(reporter, get_SB(smallMSAART0.get()) != get_SB(smallMSAART2.get()));
234 }
235 }
236}
237
238DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(ResourceCacheWrappedResources,
239 reporter,
240 ctxInfo,
242 auto context = ctxInfo.directContext();
243 GrResourceProvider* resourceProvider = context->priv().resourceProvider();
244 GrGpu* gpu = context->priv().getGpu();
245 // this test is only valid for GL
246 if (!gpu || !gpu->glContextForTesting()) {
247 return;
248 }
249
250 static const int kW = 100;
251 static const int kH = 100;
252
253 auto mbet = sk_gpu_test::ManagedBackendTexture::MakeWithoutData(
255 GrBackendTexture unmbet = context->createBackendTexture(
257 if (!mbet || !unmbet.isValid()) {
258 ERRORF(reporter, "Could not create backend texture.");
259 return;
260 }
261
262 context->resetContext();
263
264 sk_sp<GrTexture> borrowed(resourceProvider->wrapBackendTexture(
266
267 sk_sp<GrTexture> adopted(resourceProvider->wrapBackendTexture(
269
270 REPORTER_ASSERT(reporter, borrowed != nullptr && adopted != nullptr);
271 if (!borrowed || !adopted) {
272 return;
273 }
274
275 borrowed.reset();
276 adopted.reset();
277
278 context->flushAndSubmit(GrSyncCpu::kYes);
279
280 bool borrowedIsAlive = gpu->isTestingOnlyBackendTexture(mbet->texture());
281 bool adoptedIsAlive = gpu->isTestingOnlyBackendTexture(unmbet);
282
283 REPORTER_ASSERT(reporter, borrowedIsAlive);
284 REPORTER_ASSERT(reporter, !adoptedIsAlive);
285
286 if (adoptedIsAlive) {
287 context->deleteBackendTexture(unmbet);
288 }
289
290 context->resetContext();
291}
292
294 enum ScratchConstructor { kScratchConstructor };
295public:
296 static const size_t kDefaultSize = 100;
297
298 /** Property that distinctly categorizes the resource.
299 * For example, textures have width, height, ... */
301
303 std::string_view label,
305 size_t size = kDefaultSize)
306 : INHERITED(gpu, label)
307 , fToDelete(nullptr)
308 , fSize(size)
309 , fProperty(kA_SimulatedProperty)
310 , fIsScratch(false) {
311 ++fNumAlive;
312 this->registerWithCache(budgeted);
313 }
314
316 skgpu::Budgeted budgeted,
317 SimulatedProperty property,
318 size_t size = kDefaultSize) {
319 return new TestResource(gpu, budgeted, property, kScratchConstructor, /*label=*/{}, size);
320 }
322 GrWrapCacheable cacheable,
323 size_t size = kDefaultSize) {
324 return new TestResource(gpu, cacheable, size, /*label=*/{});
325 }
326
327 ~TestResource() override {
328 --fNumAlive;
329 }
330
331 static int NumAlive() { return fNumAlive; }
332
334 fToDelete = std::move(resource);
335 }
336
339 skgpu::ScratchKey::Builder builder(key, t, kScratchKeyFieldCnt);
340 for (int i = 0; i < kScratchKeyFieldCnt; ++i) {
341 builder[i] = static_cast<uint32_t>(i + property);
342 }
343 }
344
345 static size_t ExpectedScratchKeySize() {
346 return sizeof(uint32_t) * (kScratchKeyFieldCnt + skgpu::ScratchKey::kMetaDataCnt);
347 }
348private:
349 static const int kScratchKeyFieldCnt = 6;
350
351 TestResource(GrGpu* gpu,
352 skgpu::Budgeted budgeted,
353 SimulatedProperty property,
354 ScratchConstructor,
355 std::string_view label,
356 size_t size = kDefaultSize)
357 : INHERITED(gpu, label)
358 , fToDelete(nullptr)
359 , fSize(size)
360 , fProperty(property)
361 , fIsScratch(true) {
362 ++fNumAlive;
363 this->registerWithCache(budgeted);
364 }
365
366 // Constructor for simulating resources that wrap backend objects.
367 TestResource(GrGpu* gpu, GrWrapCacheable cacheable, size_t size, std::string_view label)
368 : INHERITED(gpu, label)
369 , fToDelete(nullptr)
370 , fSize(size)
371 , fProperty(kA_SimulatedProperty)
372 , fIsScratch(false) {
373 ++fNumAlive;
374 this->registerWithCacheWrapped(cacheable);
375 }
376
377 void computeScratchKey(skgpu::ScratchKey* key) const override {
378 if (fIsScratch) {
379 ComputeScratchKey(fProperty, key);
380 }
381 }
382
383 size_t onGpuMemorySize() const override { return fSize; }
384 void onSetLabel() override{}
385 const char* getResourceType() const override { return "Test"; }
386
387 sk_sp<TestResource> fToDelete;
388 size_t fSize;
389 static int fNumAlive;
390 SimulatedProperty fProperty;
391 bool fIsScratch;
392 using INHERITED = GrGpuResource;
393};
394int TestResource::fNumAlive = 0;
395
396class Mock {
397public:
398 Mock(size_t maxBytes) {
399 fDContext = GrDirectContext::MakeMock(nullptr);
400 SkASSERT(fDContext);
401 fDContext->setResourceCacheLimit(maxBytes);
402 GrResourceCache* cache = fDContext->priv().getResourceCache();
405 }
406
407 GrResourceCache* cache() { return fDContext->priv().getResourceCache(); }
408 GrGpu* gpu() { return fDContext->priv().getGpu(); }
409 GrDirectContext* dContext() { return fDContext.get(); }
410
411 void reset() {
412 fDContext.reset();
413 }
414
415private:
416 sk_sp<GrDirectContext> fDContext;
417};
418
420 Mock mock(30000);
421 GrResourceCache* cache = mock.cache();
422 GrGpu* gpu = mock.gpu();
423
424 // Create a bunch of resources with no keys
425 TestResource* a = new TestResource(gpu, /*label=*/{}, skgpu::Budgeted::kYes, 11);
426 TestResource* b = new TestResource(gpu, /*label=*/{}, skgpu::Budgeted::kYes, 12);
427 TestResource* c = new TestResource(gpu, /*label=*/{}, skgpu::Budgeted::kYes, 13);
428 TestResource* d = new TestResource(gpu, /*label=*/{}, skgpu::Budgeted::kYes, 14);
429
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(GrPurgeResourceOptions::kAllResources);
438
439 // Since the resources have neither unique nor scratch keys, delete immediately upon unref.
440
441 a->unref();
443 REPORTER_ASSERT(reporter, 3 == cache->getResourceCount());
444 REPORTER_ASSERT(reporter, b->gpuMemorySize() + c->gpuMemorySize() + d->gpuMemorySize() ==
445 cache->getResourceBytes());
446
447 c->unref();
449 REPORTER_ASSERT(reporter, 2 == cache->getResourceCount());
450 REPORTER_ASSERT(reporter, b->gpuMemorySize() + d->gpuMemorySize() ==
451 cache->getResourceBytes());
452
453 d->unref();
455 REPORTER_ASSERT(reporter, 1 == cache->getResourceCount());
456 REPORTER_ASSERT(reporter, b->gpuMemorySize() == cache->getResourceBytes());
457
458 b->unref();
460 REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
461 REPORTER_ASSERT(reporter, 0 == cache->getResourceBytes());
462}
463
464// Each integer passed as a template param creates a new domain.
465template <int>
466static void make_unique_key(skgpu::UniqueKey* key, int data, const char* tag = nullptr) {
469 builder[0] = data;
470}
471
473 Mock mock(30000);
474 GrResourceCache* cache = mock.cache();
475 GrGpu* gpu = mock.gpu();
476
477 // Create two resource w/ a unique key and two w/o but all of which have scratch keys.
480
481 skgpu::UniqueKey uniqueKey;
482 make_unique_key<0>(&uniqueKey, 0);
483
486 b->resourcePriv().setUniqueKey(uniqueKey);
487
490
491 skgpu::UniqueKey uniqueKey2;
492 make_unique_key<0>(&uniqueKey2, 1);
493
496 d->resourcePriv().setUniqueKey(uniqueKey2);
497
498
500 REPORTER_ASSERT(reporter, 4 == cache->getResourceCount());
501 REPORTER_ASSERT(reporter, a->gpuMemorySize() + b->gpuMemorySize() + c->gpuMemorySize() +
502 d->gpuMemorySize() == cache->getResourceBytes());
503
504 // Should be safe to purge without deleting the resources since we still have refs.
505 cache->purgeUnlockedResources(GrPurgeResourceOptions::kAllResources);
507
508 // Unref them all. Since they all have keys they should remain in the cache.
509
510 a->unref();
511 b->unref();
512 c->unref();
513 d->unref();
515 REPORTER_ASSERT(reporter, 4 == cache->getResourceCount());
516 REPORTER_ASSERT(reporter, a->gpuMemorySize() + b->gpuMemorySize() + c->gpuMemorySize() +
517 d->gpuMemorySize() == cache->getResourceBytes());
518
519 // Purge only the two scratch resources
521
523 REPORTER_ASSERT(reporter, 2 == cache->getResourceCount());
524 REPORTER_ASSERT(reporter, b->gpuMemorySize() + d->gpuMemorySize() ==
525 cache->getResourceBytes());
526
527 // Purge the uniquely keyed resources
528 cache->purgeUnlockedResources(GrPurgeResourceOptions::kAllResources);
529
531 REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
532 REPORTER_ASSERT(reporter, 0 == cache->getResourceBytes());
533}
534
536 Mock mock(30000);
537 GrResourceCache* cache = mock.cache();
538 GrGpu* gpu = mock.gpu();
539
540 // Create two resource w/ scratch keys.
543
546
548 REPORTER_ASSERT(reporter, 2 == cache->getResourceCount());
549 REPORTER_ASSERT(reporter, a->gpuMemorySize() + b->gpuMemorySize() == cache->getResourceBytes());
550
551 // Should be safe to purge without deleting the resources since we still have refs.
554
555 // Add command buffer usages to all resources
556 a->refCommandBuffer();
557 b->refCommandBuffer();
558
559 // Should be safe to purge without deleting the resources since we still have refs and command
560 // buffer usages.
563
564 // Unref the first resource
565 a->unref();
567 REPORTER_ASSERT(reporter, 2 == cache->getResourceCount());
568 REPORTER_ASSERT(reporter, a->gpuMemorySize() + b->gpuMemorySize() == cache->getResourceBytes());
569
570 // Should be safe to purge without deleting the resources since we still have command buffer
571 // usages and the second still has a ref.
574
575 // Remove command buffer usages
576 a->unrefCommandBuffer();
577 b->unrefCommandBuffer();
579 REPORTER_ASSERT(reporter, 2 == cache->getResourceCount());
580 REPORTER_ASSERT(reporter, a->gpuMemorySize() + b->gpuMemorySize() == cache->getResourceBytes());
581
582 // Purge this time should remove the first resources since it no longer has any refs or command
583 // buffer usages.
586 REPORTER_ASSERT(reporter, 1 == cache->getResourceCount());
587 REPORTER_ASSERT(reporter, b->gpuMemorySize() == cache->getResourceBytes());
588
589 // Unref the second resource
590 b->unref();
592 REPORTER_ASSERT(reporter, 1 == cache->getResourceCount());
593 REPORTER_ASSERT(reporter, b->gpuMemorySize() == cache->getResourceBytes());
594
595 // Purge the last resource
596 cache->purgeUnlockedResources(GrPurgeResourceOptions::kAllResources);
597
599 REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
600 REPORTER_ASSERT(reporter, 0 == cache->getResourceBytes());
601}
602
604 Mock mock(300);
605 GrResourceCache* cache = mock.cache();
606 GrGpu* gpu = mock.gpu();
607
608 skgpu::UniqueKey uniqueKey;
609 make_unique_key<0>(&uniqueKey, 0);
610
611 // Create a scratch, a unique, and a wrapped resource
614 TestResource* unique = new TestResource(gpu, /*label=*/{}, skgpu::Budgeted::kYes, 11);
615 unique->resourcePriv().setUniqueKey(uniqueKey);
617 TestResource* wrappedUncacheable = TestResource::CreateWrapped(gpu, GrWrapCacheable::kNo, 13);
618 TestResource* unbudgeted = new TestResource(gpu, /*label=*/{}, skgpu::Budgeted::kNo, 14);
619
620 // Make sure we can add a unique key to the wrapped resources
621 skgpu::UniqueKey uniqueKey2;
622 make_unique_key<0>(&uniqueKey2, 1);
623 skgpu::UniqueKey uniqueKey3;
624 make_unique_key<0>(&uniqueKey3, 2);
625 wrappedCacheable->resourcePriv().setUniqueKey(uniqueKey2);
626 wrappedUncacheable->resourcePriv().setUniqueKey(uniqueKey3);
627 GrGpuResource* wrappedCacheableViaKey = cache->findAndRefUniqueResource(uniqueKey2);
628 REPORTER_ASSERT(reporter, wrappedCacheableViaKey);
629 GrGpuResource* wrappedUncacheableViaKey = cache->findAndRefUniqueResource(uniqueKey3);
630 REPORTER_ASSERT(reporter, wrappedUncacheableViaKey);
631
632 // Remove the extra refs we just added.
633 SkSafeUnref(wrappedCacheableViaKey);
634 SkSafeUnref(wrappedUncacheableViaKey);
635
636 // Make sure sizes are as we expect
637 REPORTER_ASSERT(reporter, 5 == cache->getResourceCount());
638 REPORTER_ASSERT(reporter, scratch->gpuMemorySize() + unique->gpuMemorySize() +
639 wrappedCacheable->gpuMemorySize() +
640 wrappedUncacheable->gpuMemorySize() +
641 unbudgeted->gpuMemorySize() ==
642 cache->getResourceBytes());
643 REPORTER_ASSERT(reporter, 2 == cache->getBudgetedResourceCount());
644 REPORTER_ASSERT(reporter, scratch->gpuMemorySize() + unique->gpuMemorySize() ==
645 cache->getBudgetedResourceBytes());
646 REPORTER_ASSERT(reporter, 0 == cache->getPurgeableBytes());
647
648 // Our refs mean that the resources are non purgeable.
649 cache->purgeUnlockedResources(GrPurgeResourceOptions::kAllResources);
650 REPORTER_ASSERT(reporter, 5 == cache->getResourceCount());
651 REPORTER_ASSERT(reporter, scratch->gpuMemorySize() + unique->gpuMemorySize() +
652 wrappedCacheable->gpuMemorySize() +
653 wrappedUncacheable->gpuMemorySize() +
654 unbudgeted->gpuMemorySize() ==
655 cache->getResourceBytes());
656 REPORTER_ASSERT(reporter, 2 == cache->getBudgetedResourceCount());
657 REPORTER_ASSERT(reporter, scratch->gpuMemorySize() + unique->gpuMemorySize() ==
658 cache->getBudgetedResourceBytes());
659 REPORTER_ASSERT(reporter, 0 == cache->getPurgeableBytes());
660
661 // Unreffing the cacheable wrapped resource with a unique key shouldn't free it right away.
662 // However, unreffing the uncacheable wrapped resource should free it.
663 wrappedCacheable->unref();
664 wrappedUncacheable->unref();
665 REPORTER_ASSERT(reporter, 4 == cache->getResourceCount());
666 REPORTER_ASSERT(reporter, scratch->gpuMemorySize() + unique->gpuMemorySize() +
667 wrappedCacheable->gpuMemorySize() +
668 unbudgeted->gpuMemorySize() ==
669 cache->getResourceBytes());
670 REPORTER_ASSERT(reporter, 0 == cache->getPurgeableBytes());
671
672 // Now try freeing the budgeted resources first
673 wrappedUncacheable = TestResource::CreateWrapped(gpu, GrWrapCacheable::kNo);
674 unique->unref();
675 REPORTER_ASSERT(reporter, 11 == cache->getPurgeableBytes());
676 // This will free 'unique' but not wrappedCacheable which has a key. That requires the key to be
677 // removed to be freed.
678 cache->purgeUnlockedResources(GrPurgeResourceOptions::kAllResources);
679 REPORTER_ASSERT(reporter, 4 == cache->getResourceCount());
680
681 wrappedCacheableViaKey = cache->findAndRefUniqueResource(uniqueKey2);
682 REPORTER_ASSERT(reporter, wrappedCacheableViaKey);
683 if (wrappedCacheableViaKey) {
684 wrappedCacheableViaKey->resourcePriv().removeUniqueKey();
685 wrappedCacheable->unref();
686 }
687 // We shouldn't have to call purgeAllUnlocked as removing the key on a wrapped cacheable
688 // resource should immediately delete it.
689 REPORTER_ASSERT(reporter, 3 == cache->getResourceCount());
690
691 wrappedCacheable = TestResource::CreateWrapped(gpu, GrWrapCacheable::kYes);
692 REPORTER_ASSERT(reporter, scratch->gpuMemorySize() + wrappedCacheable->gpuMemorySize() +
693 wrappedUncacheable->gpuMemorySize() +
694 unbudgeted->gpuMemorySize() ==
695 cache->getResourceBytes());
696 REPORTER_ASSERT(reporter, 1 == cache->getBudgetedResourceCount());
697 REPORTER_ASSERT(reporter, scratch->gpuMemorySize() == cache->getBudgetedResourceBytes());
698 REPORTER_ASSERT(reporter, 0 == cache->getPurgeableBytes());
699
700 scratch->unref();
701 REPORTER_ASSERT(reporter, 10 == cache->getPurgeableBytes());
702 cache->purgeUnlockedResources(GrPurgeResourceOptions::kAllResources);
703 REPORTER_ASSERT(reporter, 3 == cache->getResourceCount());
704 REPORTER_ASSERT(reporter, unbudgeted->gpuMemorySize() + wrappedCacheable->gpuMemorySize() +
705 wrappedUncacheable->gpuMemorySize() ==
706 cache->getResourceBytes());
707 REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceCount());
708 REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceBytes());
709 REPORTER_ASSERT(reporter, 0 == cache->getPurgeableBytes());
710
711 // Unreffing the wrapped resources (with no unique key) should free them right away.
712 wrappedUncacheable->unref();
713 wrappedCacheable->unref();
714 REPORTER_ASSERT(reporter, 1 == cache->getResourceCount());
715 REPORTER_ASSERT(reporter, unbudgeted->gpuMemorySize() == cache->getResourceBytes());
716 REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceCount());
717 REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceBytes());
718 REPORTER_ASSERT(reporter, 0 == cache->getPurgeableBytes());
719
720 unbudgeted->unref();
721 REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
722 REPORTER_ASSERT(reporter, 0 == cache->getResourceBytes());
723 REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceCount());
724 REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceBytes());
725 REPORTER_ASSERT(reporter, 0 == cache->getPurgeableBytes());
726}
727
729 Mock mock(30000);
730 GrResourceCache* cache = mock.cache();
731 GrGpu* gpu = mock.gpu();
732
733 skgpu::UniqueKey uniqueKey;
734 make_unique_key<0>(&uniqueKey, 0);
735
736 TestResource* scratch;
737 TestResource* unique;
739 TestResource* unbudgeted;
740
741 // A large uncached or wrapped resource shouldn't evict anything.
744
745 scratch->unref();
746 REPORTER_ASSERT(reporter, 1 == cache->getResourceCount());
747 REPORTER_ASSERT(reporter, 10 == cache->getResourceBytes());
748 REPORTER_ASSERT(reporter, 1 == cache->getBudgetedResourceCount());
749 REPORTER_ASSERT(reporter, 10 == cache->getBudgetedResourceBytes());
750 REPORTER_ASSERT(reporter, 10 == cache->getPurgeableBytes());
751
752 unique = new TestResource(gpu, /*label=*/{}, skgpu::Budgeted::kYes, 11);
753 unique->resourcePriv().setUniqueKey(uniqueKey);
754 unique->unref();
755 REPORTER_ASSERT(reporter, 2 == cache->getResourceCount());
756 REPORTER_ASSERT(reporter, 21 == cache->getResourceBytes());
757 REPORTER_ASSERT(reporter, 2 == cache->getBudgetedResourceCount());
758 REPORTER_ASSERT(reporter, 21 == cache->getBudgetedResourceBytes());
759 REPORTER_ASSERT(reporter, 21 == cache->getPurgeableBytes());
760
761 size_t large = 2 * cache->getResourceBytes();
762 unbudgeted = new TestResource(gpu, /*label=*/{}, skgpu::Budgeted::kNo, large);
763 REPORTER_ASSERT(reporter, 3 == cache->getResourceCount());
764 REPORTER_ASSERT(reporter, 21 + large == cache->getResourceBytes());
765 REPORTER_ASSERT(reporter, 2 == cache->getBudgetedResourceCount());
766 REPORTER_ASSERT(reporter, 21 == cache->getBudgetedResourceBytes());
767 REPORTER_ASSERT(reporter, 21 == cache->getPurgeableBytes());
768
769 unbudgeted->unref();
770 REPORTER_ASSERT(reporter, 2 == cache->getResourceCount());
771 REPORTER_ASSERT(reporter, 21 == cache->getResourceBytes());
772 REPORTER_ASSERT(reporter, 2 == cache->getBudgetedResourceCount());
773 REPORTER_ASSERT(reporter, 21 == cache->getBudgetedResourceBytes());
774 REPORTER_ASSERT(reporter, 21 == cache->getPurgeableBytes());
775
777 REPORTER_ASSERT(reporter, 3 == cache->getResourceCount());
778 REPORTER_ASSERT(reporter, 21 + large == cache->getResourceBytes());
779 REPORTER_ASSERT(reporter, 2 == cache->getBudgetedResourceCount());
780 REPORTER_ASSERT(reporter, 21 == cache->getBudgetedResourceBytes());
781 REPORTER_ASSERT(reporter, 21 == cache->getPurgeableBytes());
782
783 wrapped->unref();
784 REPORTER_ASSERT(reporter, 2 == cache->getResourceCount());
785 REPORTER_ASSERT(reporter, 21 == cache->getResourceBytes());
786 REPORTER_ASSERT(reporter, 2 == cache->getBudgetedResourceCount());
787 REPORTER_ASSERT(reporter, 21 == cache->getBudgetedResourceBytes());
788 REPORTER_ASSERT(reporter, 21 == cache->getPurgeableBytes());
789
790 cache->purgeUnlockedResources(GrPurgeResourceOptions::kAllResources);
791 REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
792 REPORTER_ASSERT(reporter, 0 == cache->getResourceBytes());
793 REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceCount());
794 REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceBytes());
795 REPORTER_ASSERT(reporter, 0 == cache->getPurgeableBytes());
796}
797
798// This method can't be static because it needs to friended in GrGpuResource::CacheAccess.
801 Mock mock(300);
802 GrResourceCache* cache = mock.cache();
803 GrGpu* gpu = mock.gpu();
804
809
810 size_t size = resource->gpuMemorySize();
811 for (int i = 0; i < 2; ++i) {
812 // Since this resource is unbudgeted, it should not be reachable as scratch.
813 REPORTER_ASSERT(reporter, resource->resourcePriv().getScratchKey() == key);
814 REPORTER_ASSERT(reporter, !resource->cacheAccess().isScratch());
816 resource->resourcePriv().budgetedType());
817 REPORTER_ASSERT(reporter, !cache->findAndRefScratchResource(key));
818 REPORTER_ASSERT(reporter, 1 == cache->getResourceCount());
819 REPORTER_ASSERT(reporter, size == cache->getResourceBytes());
820 REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceCount());
821 REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceBytes());
822 REPORTER_ASSERT(reporter, 0 == cache->getPurgeableBytes());
823
824 // Once it is unrefed, it should become available as scratch.
825 resource->unref();
826 REPORTER_ASSERT(reporter, 1 == cache->getResourceCount());
827 REPORTER_ASSERT(reporter, size == cache->getResourceBytes());
828 REPORTER_ASSERT(reporter, 1 == cache->getBudgetedResourceCount());
829 REPORTER_ASSERT(reporter, size == cache->getBudgetedResourceBytes());
830 REPORTER_ASSERT(reporter, size == cache->getPurgeableBytes());
831 resource = static_cast<TestResource*>(cache->findAndRefScratchResource(key));
833 REPORTER_ASSERT(reporter, resource->resourcePriv().getScratchKey() == key);
834 REPORTER_ASSERT(reporter, resource->cacheAccess().isScratch());
836 GrBudgetedType::kBudgeted == resource->resourcePriv().budgetedType());
837
838 if (0 == i) {
839 // If made unbudgeted, it should return to original state: ref'ed and unbudgeted. Try
840 // the above tests again.
841 resource->resourcePriv().makeUnbudgeted();
842 } else {
843 // After the second time around, try removing the scratch key
844 resource->resourcePriv().removeScratchKey();
845 REPORTER_ASSERT(reporter, 1 == cache->getResourceCount());
846 REPORTER_ASSERT(reporter, size == cache->getResourceBytes());
847 REPORTER_ASSERT(reporter, 1 == cache->getBudgetedResourceCount());
848 REPORTER_ASSERT(reporter, size == cache->getBudgetedResourceBytes());
849 REPORTER_ASSERT(reporter, 0 == cache->getPurgeableBytes());
850 REPORTER_ASSERT(reporter, !resource->resourcePriv().getScratchKey().isValid());
851 REPORTER_ASSERT(reporter, !resource->cacheAccess().isScratch());
853 GrBudgetedType::kBudgeted == resource->resourcePriv().budgetedType());
854
855 // now when it is unrefed it should die since it has no key.
856 resource->unref();
857 REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
858 REPORTER_ASSERT(reporter, 0 == cache->getResourceBytes());
859 REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceCount());
860 REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceBytes());
861 REPORTER_ASSERT(reporter, 0 == cache->getPurgeableBytes());
862 }
863 }
864}
865
867 Mock mock(30000);
868 GrResourceCache* cache = mock.cache();
869 GrGpu* gpu = mock.gpu();
870
871 // Create two resources that have the same scratch key.
876 skgpu::ScratchKey scratchKey1;
878 // Check for negative case consistency. (leaks upon test failure.)
879 REPORTER_ASSERT(reporter, !cache->findAndRefScratchResource(scratchKey1));
880
881 skgpu::ScratchKey scratchKey;
883
884 // Scratch resources are registered with GrResourceCache just by existing. There are 2.
886 // As long as there are outstanding refs on the resources they will not be in the scratch map
887 SkDEBUGCODE(REPORTER_ASSERT(reporter, 0 == cache->countScratchEntriesForKey(scratchKey));)
888 REPORTER_ASSERT(reporter, 2 == cache->getResourceCount());
889 REPORTER_ASSERT(reporter, a->gpuMemorySize() + b->gpuMemorySize() ==
890 cache->getResourceBytes());
891
892 // Our refs mean that the resources are non purgeable.
893 cache->purgeUnlockedResources(GrPurgeResourceOptions::kAllResources);
895 REPORTER_ASSERT(reporter, 2 == cache->getResourceCount());
896
897 // Unref but don't purge
898 a->unref();
899 b->unref();
901 // Since we removed the refs to the resources they will now be in the scratch map
902 SkDEBUGCODE(REPORTER_ASSERT(reporter, 2 == cache->countScratchEntriesForKey(scratchKey));)
903
904 // Purge again. This time resources should be purgeable.
905 cache->purgeUnlockedResources(GrPurgeResourceOptions::kAllResources);
907 REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
908 SkDEBUGCODE(REPORTER_ASSERT(reporter, 0 == cache->countScratchEntriesForKey(scratchKey));)
909}
910
912 Mock mock(30000);
913 GrResourceCache* cache = mock.cache();
914 GrGpu* gpu = mock.gpu();
915
916 // Create two resources that have the same scratch key.
921 a->unref();
922 b->unref();
923
924 skgpu::ScratchKey scratchKey;
925 // Ensure that scratch key lookup is correct for negative case.
927 // (following leaks upon test failure).
928 REPORTER_ASSERT(reporter, !cache->findAndRefScratchResource(scratchKey));
929
930 // Scratch resources are registered with GrResourceCache just by existing. There are 2.
933 SkDEBUGCODE(REPORTER_ASSERT(reporter, 2 == cache->countScratchEntriesForKey(scratchKey));)
934 REPORTER_ASSERT(reporter, 2 == cache->getResourceCount());
935
936 // Find the first resource and remove its scratch key
937 GrGpuResource* find = cache->findAndRefScratchResource(scratchKey);
938 find->resourcePriv().removeScratchKey();
939 // It's still alive, but not cached by scratch key anymore
941 SkDEBUGCODE(REPORTER_ASSERT(reporter, 1 == cache->countScratchEntriesForKey(scratchKey));)
942 REPORTER_ASSERT(reporter, 2 == cache->getResourceCount());
943
944 // The cache should immediately delete it when it's unrefed since it isn't accessible.
945 find->unref();
947 SkDEBUGCODE(REPORTER_ASSERT(reporter, 1 == cache->countScratchEntriesForKey(scratchKey));)
948 REPORTER_ASSERT(reporter, 1 == cache->getResourceCount());
949
950 // Repeat for the second resource.
951 find = cache->findAndRefScratchResource(scratchKey);
952 find->resourcePriv().removeScratchKey();
954 SkDEBUGCODE(REPORTER_ASSERT(reporter, 0 == cache->countScratchEntriesForKey(scratchKey));)
955 REPORTER_ASSERT(reporter, 1 == cache->getResourceCount());
956
957 // Should be able to call this multiple times with no problem.
958 find->resourcePriv().removeScratchKey();
960 SkDEBUGCODE(REPORTER_ASSERT(reporter, 0 == cache->countScratchEntriesForKey(scratchKey));)
961 REPORTER_ASSERT(reporter, 1 == cache->getResourceCount());
962
963 find->unref();
965 SkDEBUGCODE(REPORTER_ASSERT(reporter, 0 == cache->countScratchEntriesForKey(scratchKey));)
966 REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
967}
968
970 Mock mock(30000);
971 GrResourceCache* cache = mock.cache();
972 GrGpu* gpu = mock.gpu();
973
974 // Create two resources that have the same scratch key.
979 a->unref();
980 b->unref();
981
982 skgpu::ScratchKey scratchKey;
983 // Ensure that scratch key comparison and assignment is consistent.
984 skgpu::ScratchKey scratchKey1;
986 skgpu::ScratchKey scratchKey2;
989 REPORTER_ASSERT(reporter, scratchKey1 != scratchKey2);
990 REPORTER_ASSERT(reporter, scratchKey2 != scratchKey1);
991 scratchKey = scratchKey1;
993 REPORTER_ASSERT(reporter, scratchKey1 == scratchKey);
994 REPORTER_ASSERT(reporter, scratchKey == scratchKey1);
995 REPORTER_ASSERT(reporter, scratchKey2 != scratchKey);
996 REPORTER_ASSERT(reporter, scratchKey != scratchKey2);
997 scratchKey = scratchKey2;
999 REPORTER_ASSERT(reporter, scratchKey1 != scratchKey);
1000 REPORTER_ASSERT(reporter, scratchKey != scratchKey1);
1001 REPORTER_ASSERT(reporter, scratchKey2 == scratchKey);
1002 REPORTER_ASSERT(reporter, scratchKey == scratchKey2);
1003
1004 // Ensure that scratch key lookup is correct for negative case.
1006 // (following leaks upon test failure).
1007 REPORTER_ASSERT(reporter, !cache->findAndRefScratchResource(scratchKey));
1008
1009 // Find the first resource with a scratch key and a copy of a scratch key.
1011 GrGpuResource* find = cache->findAndRefScratchResource(scratchKey);
1012 REPORTER_ASSERT(reporter, find != nullptr);
1013 find->unref();
1014
1015 scratchKey2 = scratchKey;
1016 find = cache->findAndRefScratchResource(scratchKey2);
1017 REPORTER_ASSERT(reporter, find != nullptr);
1018 REPORTER_ASSERT(reporter, find == a || find == b);
1019
1020 GrGpuResource* find2 = cache->findAndRefScratchResource(scratchKey2);
1021 REPORTER_ASSERT(reporter, find2 != nullptr);
1022 REPORTER_ASSERT(reporter, find2 == a || find2 == b);
1023 REPORTER_ASSERT(reporter, find2 != find);
1024 find2->unref();
1025 find->unref();
1026}
1027
1029 Mock mock(30000);
1030 GrResourceCache* cache = mock.cache();
1031 GrGpu* gpu = mock.gpu();
1032
1034 make_unique_key<0>(&key, 0);
1035
1036 // Create two resources that we will attempt to register with the same unique key.
1037 TestResource* a = new TestResource(gpu, /*label=*/{}, skgpu::Budgeted::kYes, 11);
1038
1039 // Set key on resource a.
1040 a->resourcePriv().setUniqueKey(key);
1041 REPORTER_ASSERT(reporter, a == cache->findAndRefUniqueResource(key));
1042 a->unref();
1043
1044 // Make sure that redundantly setting a's key works.
1045 a->resourcePriv().setUniqueKey(key);
1046 REPORTER_ASSERT(reporter, a == cache->findAndRefUniqueResource(key));
1047 a->unref();
1048 REPORTER_ASSERT(reporter, 1 == cache->getResourceCount());
1049 REPORTER_ASSERT(reporter, a->gpuMemorySize() == cache->getResourceBytes());
1051
1052 // Create resource b and set the same key. It should replace a's unique key cache entry.
1053 TestResource* b = new TestResource(gpu, /*label=*/{}, skgpu::Budgeted::kYes, 12);
1054 b->resourcePriv().setUniqueKey(key);
1055 REPORTER_ASSERT(reporter, b == cache->findAndRefUniqueResource(key));
1056 b->unref();
1057
1058 // Still have two resources because a is still reffed.
1059 REPORTER_ASSERT(reporter, 2 == cache->getResourceCount());
1060 REPORTER_ASSERT(reporter, a->gpuMemorySize() + b->gpuMemorySize() == cache->getResourceBytes());
1062
1063 a->unref();
1064 // Now a should be gone.
1065 REPORTER_ASSERT(reporter, 1 == cache->getResourceCount());
1066 REPORTER_ASSERT(reporter, b->gpuMemorySize() == cache->getResourceBytes());
1068
1069 // Now replace b with c, but make sure c can start with one unique key and change it to b's key.
1070 // Also make b be unreffed when replacement occurs.
1071 b->unref();
1072 TestResource* c = new TestResource(gpu, /*label=*/{}, skgpu::Budgeted::kYes, 13);
1073 skgpu::UniqueKey differentKey;
1074 make_unique_key<0>(&differentKey, 1);
1075 c->resourcePriv().setUniqueKey(differentKey);
1076 REPORTER_ASSERT(reporter, 2 == cache->getResourceCount());
1077 REPORTER_ASSERT(reporter, b->gpuMemorySize() + c->gpuMemorySize() == cache->getResourceBytes());
1079 // c replaces b and b should be immediately purged.
1081 REPORTER_ASSERT(reporter, 1 == cache->getResourceCount());
1082 REPORTER_ASSERT(reporter, c->gpuMemorySize() == cache->getResourceBytes());
1084
1085 // c shouldn't be purged because it is ref'ed.
1086 cache->purgeUnlockedResources(GrPurgeResourceOptions::kAllResources);
1087 REPORTER_ASSERT(reporter, 1 == cache->getResourceCount());
1088 REPORTER_ASSERT(reporter, c->gpuMemorySize() == cache->getResourceBytes());
1090
1091 // Drop the ref on c, it should be kept alive because it has a unique key.
1092 c->unref();
1093 REPORTER_ASSERT(reporter, 1 == cache->getResourceCount());
1094 REPORTER_ASSERT(reporter, c->gpuMemorySize() == cache->getResourceBytes());
1096
1097 // Verify that we can find c, then remove its unique key. It should get purged immediately.
1098 REPORTER_ASSERT(reporter, c == cache->findAndRefUniqueResource(key));
1100 c->unref();
1101 REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
1102 REPORTER_ASSERT(reporter, 0 == cache->getResourceBytes());
1104
1105 {
1106 skgpu::UniqueKey key2;
1107 make_unique_key<0>(&key2, 0);
1108 sk_sp<TestResource> d(new TestResource(gpu, /*label=*/{}));
1109 int foo = 4132;
1110 key2.setCustomData(SkData::MakeWithCopy(&foo, sizeof(foo)));
1111 d->resourcePriv().setUniqueKey(key2);
1112 }
1113
1114 skgpu::UniqueKey key3;
1115 make_unique_key<0>(&key3, 0);
1116 sk_sp<GrGpuResource> d2(cache->findAndRefUniqueResource(key3));
1117 REPORTER_ASSERT(reporter, *(const int*) d2->getUniqueKey().getCustomData()->data() == 4132);
1118}
1119
1121 Mock mock(30000);
1122 auto dContext = mock.dContext();
1123 GrResourceCache* cache = mock.cache();
1124 GrGpu* gpu = mock.gpu();
1125
1126 skgpu::UniqueKey key1, key2, key3;
1127 make_unique_key<0>(&key1, 1);
1128 make_unique_key<0>(&key2, 2);
1129 make_unique_key<0>(&key3, 3);
1130
1131 // Add three resources to the cache. Only c is usable as scratch.
1132 TestResource* a = new TestResource(gpu, /*label=*/{});
1133 TestResource* b = new TestResource(gpu, /*label=*/{});
1136 a->resourcePriv().setUniqueKey(key1);
1137 b->resourcePriv().setUniqueKey(key2);
1138 c->resourcePriv().setUniqueKey(key3);
1139 a->unref();
1140 // hold b until *after* the message is sent.
1141 c->unref();
1142
1143 REPORTER_ASSERT(reporter, cache->hasUniqueKey(key1));
1144 REPORTER_ASSERT(reporter, cache->hasUniqueKey(key2));
1145 REPORTER_ASSERT(reporter, cache->hasUniqueKey(key3));
1147
1149 typedef SkMessageBus<Msg, uint32_t> Bus;
1150
1151 // Invalidate two of the three, they should be purged and no longer accessible via their keys.
1152 Bus::Post(Msg(key1, dContext->priv().contextID()));
1153 Bus::Post(Msg(key2, dContext->priv().contextID()));
1154 cache->purgeAsNeeded();
1155 // a should be deleted now, but we still have a ref on b.
1156 REPORTER_ASSERT(reporter, !cache->hasUniqueKey(key1));
1157 REPORTER_ASSERT(reporter, !cache->hasUniqueKey(key2));
1159 REPORTER_ASSERT(reporter, cache->hasUniqueKey(key3));
1160
1161 // Invalidate the third.
1162 Bus::Post(Msg(key3, dContext->priv().contextID()));
1163 cache->purgeAsNeeded();
1164 // we still have a ref on b, c should be recycled as scratch.
1166 REPORTER_ASSERT(reporter, !cache->hasUniqueKey(key3));
1167
1168 // make b purgeable. It should be immediately deleted since it has no key.
1169 b->unref();
1171
1172 // Make sure we actually get to c via it's scratch key, before we say goodbye.
1173 skgpu::ScratchKey scratchKey;
1175 GrGpuResource* scratch = cache->findAndRefScratchResource(scratchKey);
1176 REPORTER_ASSERT(reporter, scratch == c);
1177 SkSafeUnref(scratch);
1178
1179 // Get rid of c.
1180 cache->purgeUnlockedResources(GrPurgeResourceOptions::kAllResources);
1181 scratch = cache->findAndRefScratchResource(scratchKey);
1183 REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
1184 REPORTER_ASSERT(reporter, 0 == cache->getResourceBytes());
1185 REPORTER_ASSERT(reporter, !scratch);
1186 SkSafeUnref(scratch);
1187}
1188
1190 Mock mock(30000);
1191 GrResourceCache* cache = mock.cache();
1192 GrGpu* gpu = mock.gpu();
1193
1194 skgpu::UniqueKey key1, key2;
1195 make_unique_key<0>(&key1, 1);
1196 make_unique_key<0>(&key2, 2);
1197
1198 sk_sp<TestResource> a(new TestResource(gpu, /*label=*/{}));
1199 sk_sp<TestResource> b(new TestResource(gpu, /*label=*/{}));
1200 a->resourcePriv().setUniqueKey(key1);
1201 b->resourcePriv().setUniqueKey(key2);
1202
1203 // Make a cycle
1204 a->setUnrefWhenDestroyed(b);
1205 b->setUnrefWhenDestroyed(a);
1206
1208
1209 TestResource* unownedA = a.release();
1210 unownedA->unref();
1211 b.reset();
1212
1214
1215 cache->purgeUnlockedResources(GrPurgeResourceOptions::kAllResources);
1217
1218 // Break the cycle
1219 unownedA->setUnrefWhenDestroyed(nullptr);
1221
1222 cache->purgeUnlockedResources(GrPurgeResourceOptions::kAllResources);
1224}
1225
1227 static const int kCount = 50;
1228 static const int kLockedFreq = 8;
1229 static const int kBudgetSize = 0; // always over budget
1230
1231 SkRandom random;
1232
1233 // Run the test 2*kCount times;
1234 for (int i = 0; i < 2 * kCount; ++i ) {
1235 Mock mock(kBudgetSize);
1236 GrResourceCache* cache = mock.cache();
1237 GrGpu* gpu = mock.gpu();
1238
1239 // Pick a random number of resources to add before the timestamp will wrap.
1240 cache->changeTimestamp(UINT32_MAX - random.nextULessThan(kCount + 1));
1241
1242 static const int kNumToPurge = kCount;
1243
1244 SkTDArray<int> shouldPurgeIdxs;
1245 int purgeableCnt = 0;
1246 SkTDArray<GrGpuResource*> resourcesToUnref;
1247
1248 // Add kCount resources, holding onto resources at random so we have a mix of purgeable and
1249 // unpurgeable resources.
1250 for (int j = 0; j < kCount; ++j) {
1252 make_unique_key<0>(&key, j);
1253
1254 TestResource* r = new TestResource(gpu, /*label=*/{});
1256 if (random.nextU() % kLockedFreq) {
1257 // Make this is purgeable.
1258 r->unref();
1259 ++purgeableCnt;
1260 if (purgeableCnt <= kNumToPurge) {
1261 *shouldPurgeIdxs.append() = j;
1262 }
1263 } else {
1264 *resourcesToUnref.append() = r;
1265 }
1266 }
1267
1268 // Verify that the correct resources were purged.
1269 int currShouldPurgeIdx = 0;
1270 for (int j = 0; j < kCount; ++j) {
1272 make_unique_key<0>(&key, j);
1273 GrGpuResource* res = cache->findAndRefUniqueResource(key);
1274 if (currShouldPurgeIdx < shouldPurgeIdxs.size() &&
1275 shouldPurgeIdxs[currShouldPurgeIdx] == j) {
1276 ++currShouldPurgeIdx;
1277 REPORTER_ASSERT(reporter, nullptr == res);
1278 } else {
1279 REPORTER_ASSERT(reporter, nullptr != res);
1280 }
1281 SkSafeUnref(res);
1282 }
1283
1284 for (int j = 0; j < resourcesToUnref.size(); ++j) {
1285 resourcesToUnref[j]->unref();
1286 }
1287 }
1288}
1289
1291 Mock mock(1000000);
1292 auto dContext = mock.dContext();
1293 GrResourceCache* cache = mock.cache();
1294 GrGpu* gpu = mock.gpu();
1295
1296 static constexpr int kCnts[] = {1, 10, 1024};
1297 auto nowish = []() {
1298 // We sleep so that we ensure we get a value that is greater than the last call to
1299 // GrStdSteadyClock::now().
1300 std::this_thread::sleep_for(skgpu::StdSteadyClock::duration(5));
1301 auto result = skgpu::StdSteadyClock::now();
1302 // Also sleep afterwards so we don't get this value again.
1303 std::this_thread::sleep_for(skgpu::StdSteadyClock::duration(5));
1304 return result;
1305 };
1306
1307 for (int cnt : kCnts) {
1308 std::unique_ptr<skgpu::StdSteadyClock::time_point[]> timeStamps(
1309 new skgpu::StdSteadyClock::time_point[cnt]);
1310 {
1311 // Insert resources and get time points between each addition.
1312 for (int i = 0; i < cnt; ++i) {
1313 TestResource* r = new TestResource(gpu, /*label=*/{});
1315 make_unique_key<1>(&k, i);
1316 r->resourcePriv().setUniqueKey(k);
1317 r->unref();
1318 timeStamps.get()[i] = nowish();
1319 }
1320
1321 // Purge based on the time points between resource additions. Each purge should remove
1322 // the oldest resource.
1323 for (int i = 0; i < cnt; ++i) {
1324 cache->purgeResourcesNotUsedSince(timeStamps[i],
1326 REPORTER_ASSERT(reporter, cnt - i - 1 == cache->getResourceCount());
1327 for (int j = 0; j < i; ++j) {
1329 make_unique_key<1>(&k, j);
1330 GrGpuResource* r = cache->findAndRefUniqueResource(k);
1332 SkSafeUnref(r);
1333 }
1334 }
1335
1336 REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
1337 cache->purgeUnlockedResources(GrPurgeResourceOptions::kAllResources);
1338 }
1339
1340 // Do a similar test but where we leave refs on some resources to prevent them from being
1341 // purged.
1342 {
1343 std::unique_ptr<GrGpuResource* []> refedResources(new GrGpuResource*[cnt / 2]);
1344 for (int i = 0; i < cnt; ++i) {
1345 TestResource* r = new TestResource(gpu, /*label=*/{});
1347 make_unique_key<1>(&k, i);
1348 r->resourcePriv().setUniqueKey(k);
1349 // Leave a ref on every other resource, beginning with the first.
1350 if (SkToBool(i & 0x1)) {
1351 refedResources.get()[i / 2] = r;
1352 } else {
1353 r->unref();
1354 }
1355 timeStamps.get()[i] = nowish();
1356 }
1357
1358 for (int i = 0; i < cnt; ++i) {
1359 // Should get a resource purged every other frame.
1360 cache->purgeResourcesNotUsedSince(timeStamps[i],
1362 REPORTER_ASSERT(reporter, cnt - i / 2 - 1 == cache->getResourceCount());
1363 }
1364
1365 // Unref all the resources that we kept refs on in the first loop.
1366 for (int i = 0; i < (cnt / 2); ++i) {
1367 refedResources.get()[i]->unref();
1368 cache->purgeResourcesNotUsedSince(nowish(), GrPurgeResourceOptions::kAllResources);
1369 REPORTER_ASSERT(reporter, cnt / 2 - i - 1 == cache->getResourceCount());
1370 }
1371
1372 cache->purgeUnlockedResources(GrPurgeResourceOptions::kAllResources);
1373 }
1374
1375 REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
1376
1377 // Do a similar test where we alternate adding scratch and uniquely keyed resources, but
1378 // then purge old scratch resources.
1379 {
1380 for (int i = 0; i < cnt; ++i) {
1381 const bool isScratch = (i % 2 == 0);
1382 const skgpu::Budgeted budgeted = skgpu::Budgeted::kYes;
1384 TestResource* r = isScratch
1385 ? TestResource::CreateScratch(gpu, budgeted, property)
1386 : new TestResource(gpu, /*label=*/{}, budgeted, property);
1387 if (!isScratch) {
1389 make_unique_key<1>(&k, i);
1390 r->resourcePriv().setUniqueKey(k);
1391 }
1392 r->unref();
1393 timeStamps.get()[i] = nowish();
1394 }
1395
1396 for (int i = 0; i < cnt; ++i) {
1397 // Should get a resource purged every other frame, since the uniquely keyed
1398 // resources will not be considered.
1399 cache->purgeResourcesNotUsedSince(timeStamps[i],
1401 REPORTER_ASSERT(reporter, cnt - i / 2 - 1 == cache->getResourceCount());
1402 }
1403 // Unref remaining resources
1404 cache->purgeResourcesNotUsedSince(nowish(), GrPurgeResourceOptions::kAllResources);
1405 }
1406
1407 REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
1408
1409 // Verify that calling flush() on a context with nothing to do will not trigger resource
1410 // eviction
1411 dContext->flushAndSubmit();
1412 for (int i = 0; i < 10; ++i) {
1413 TestResource* r = new TestResource(gpu, /*label=*/{});
1415 make_unique_key<1>(&k, i);
1416 r->resourcePriv().setUniqueKey(k);
1417 r->unref();
1418 }
1419 REPORTER_ASSERT(reporter, 10 == cache->getResourceCount());
1420 dContext->flushAndSubmit();
1421 REPORTER_ASSERT(reporter, 10 == cache->getResourceCount());
1422 cache->purgeResourcesNotUsedSince(nowish(), GrPurgeResourceOptions::kAllResources);
1423 REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
1424 }
1425}
1426
1428 Mock mock(100);
1429 auto dContext = mock.dContext();
1430 GrResourceCache* cache = mock.cache();
1431 GrGpu* gpu = mock.gpu();
1432
1433 enum TestsCase {
1434 kOnlyScratch_TestCase = 0,
1435 kPartialScratch_TestCase = 1,
1436 kAllScratch_TestCase = 2,
1437 kPartial_TestCase = 3,
1438 kAll_TestCase = 4,
1439 kNone_TestCase = 5,
1440 kEndTests_TestCase = kNone_TestCase + 1
1441 };
1442
1443 for (int testCase = 0; testCase < kEndTests_TestCase; testCase++) {
1444
1445 skgpu::UniqueKey key1, key2, key3;
1446 make_unique_key<0>(&key1, 1);
1447 make_unique_key<0>(&key2, 2);
1448 make_unique_key<0>(&key3, 3);
1449
1450 // Add three unique resources to the cache.
1451 TestResource* unique1 = new TestResource(gpu, /*label=*/{}, skgpu::Budgeted::kYes, 10);
1452 TestResource* unique2 = new TestResource(gpu, /*label=*/{}, skgpu::Budgeted::kYes, 11);
1453 TestResource* unique3 = new TestResource(gpu, /*label=*/{}, skgpu::Budgeted::kYes, 12);
1454
1455 unique1->resourcePriv().setUniqueKey(key1);
1456 unique2->resourcePriv().setUniqueKey(key2);
1457 unique3->resourcePriv().setUniqueKey(key3);
1458
1459 // Add two scratch resources to the cache.
1464
1465 REPORTER_ASSERT(reporter, 5 == cache->getBudgetedResourceCount());
1466 REPORTER_ASSERT(reporter, 60 == cache->getBudgetedResourceBytes());
1467 REPORTER_ASSERT(reporter, 0 == cache->getPurgeableBytes());
1468
1469 // Add resources to the purgeable queue
1470 unique1->unref();
1471 scratch1->unref();
1472 unique2->unref();
1473 scratch2->unref();
1474 unique3->unref();
1475
1476 REPORTER_ASSERT(reporter, 5 == cache->getBudgetedResourceCount());
1477 REPORTER_ASSERT(reporter, 60 == cache->getBudgetedResourceBytes());
1478 REPORTER_ASSERT(reporter, 60 == cache->getPurgeableBytes());
1479
1480 switch(testCase) {
1481 case kOnlyScratch_TestCase: {
1482 dContext->purgeUnlockedResources(14, true);
1483 REPORTER_ASSERT(reporter, 3 == cache->getBudgetedResourceCount());
1484 REPORTER_ASSERT(reporter, 33 == cache->getBudgetedResourceBytes());
1485 break;
1486 }
1487 case kPartialScratch_TestCase: {
1488 dContext->purgeUnlockedResources(3, true);
1489 REPORTER_ASSERT(reporter, 4 == cache->getBudgetedResourceCount());
1490 REPORTER_ASSERT(reporter, 47 == cache->getBudgetedResourceBytes());
1491 break;
1492 }
1493 case kAllScratch_TestCase: {
1494 dContext->purgeUnlockedResources(50, true);
1495 REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceCount());
1496 REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceBytes());
1497 break;
1498 }
1499 case kPartial_TestCase: {
1500 dContext->purgeUnlockedResources(13, false);
1501 REPORTER_ASSERT(reporter, 3 == cache->getBudgetedResourceCount());
1502 REPORTER_ASSERT(reporter, 37 == cache->getBudgetedResourceBytes());
1503 break;
1504 }
1505 case kAll_TestCase: {
1506 dContext->purgeUnlockedResources(50, false);
1507 REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceCount());
1508 REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceBytes());
1509 break;
1510 }
1511 case kNone_TestCase: {
1512 dContext->purgeUnlockedResources(0, true);
1513 dContext->purgeUnlockedResources(0, false);
1514 REPORTER_ASSERT(reporter, 5 == cache->getBudgetedResourceCount());
1515 REPORTER_ASSERT(reporter, 60 == cache->getBudgetedResourceBytes());
1516 REPORTER_ASSERT(reporter, 60 == cache->getPurgeableBytes());
1517 break;
1518 }
1519 }
1520
1521 // ensure all are purged before the next
1522 dContext->priv().getResourceCache()->purgeUnlockedResources(
1524 REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceCount());
1525 REPORTER_ASSERT(reporter, 0 == cache->getPurgeableBytes());
1526
1527 }
1528}
1529
1531 skgpu::UniqueKey key1, key2;
1532 make_unique_key<0>(&key1, 1);
1533 make_unique_key<0>(&key2, 2);
1534 int foo = 4132;
1535 key1.setCustomData(SkData::MakeWithCopy(&foo, sizeof(foo)));
1536 REPORTER_ASSERT(reporter, *(const int*) key1.getCustomData()->data() == 4132);
1537 REPORTER_ASSERT(reporter, key2.getCustomData() == nullptr);
1538
1539 // Test that copying a key also takes a ref on its custom data.
1540 skgpu::UniqueKey key3 = key1;
1541 REPORTER_ASSERT(reporter, *(const int*) key3.getCustomData()->data() == 4132);
1542}
1543
1545 Mock mock(300);
1546 auto dContext = mock.dContext();
1547 GrGpu* gpu = mock.gpu();
1548
1549 sk_sp<GrGpuResource> resource(new TestResource(gpu, /*label=*/{}));
1550 dContext->abandonContext();
1551
1552 REPORTER_ASSERT(reporter, resource->wasDestroyed());
1553
1554 // Call all the public methods on resource in the abandoned state. They shouldn't crash.
1555
1556 resource->uniqueID();
1557 resource->getUniqueKey();
1558 resource->wasDestroyed();
1559 resource->gpuMemorySize();
1560 resource->getContext();
1561
1562 resource->resourcePriv().getScratchKey();
1563 resource->resourcePriv().budgetedType();
1564 resource->resourcePriv().makeBudgeted();
1565 resource->resourcePriv().makeUnbudgeted();
1566 resource->resourcePriv().removeScratchKey();
1568 make_unique_key<0>(&key, 1);
1569 resource->resourcePriv().setUniqueKey(key);
1570 resource->resourcePriv().removeUniqueKey();
1571}
1572
1574#ifdef SK_DEBUG
1575 // We will insert 1 resource with tag "tag1", 2 with "tag2", and so on, up through kLastTagIdx.
1576 static constexpr int kLastTagIdx = 10;
1577 static constexpr int kNumResources = kLastTagIdx * (kLastTagIdx + 1) / 2;
1578
1579 Mock mock(kNumResources * TestResource::kDefaultSize);
1580 GrResourceCache* cache = mock.cache();
1581 GrGpu* gpu = mock.gpu();
1582
1583 // tag strings are expected to be long lived
1584 std::vector<SkString> tagStrings;
1585
1586 SkString tagStr;
1587 int tagIdx = 0;
1588 int currTagCnt = 0;
1589
1590 for (int i = 0; i < kNumResources; ++i, ++currTagCnt) {
1591
1592 sk_sp<GrGpuResource> resource(new TestResource(gpu, /*label=*/{}));
1594 if (currTagCnt == tagIdx) {
1595 tagIdx += 1;
1596 currTagCnt = 0;
1597 tagStr.printf("tag%d", tagIdx);
1598 tagStrings.emplace_back(tagStr);
1599 }
1600 make_unique_key<1>(&key, i, tagStrings.back().c_str());
1601 resource->resourcePriv().setUniqueKey(key);
1602 }
1603 SkASSERT(kLastTagIdx == tagIdx);
1604 SkASSERT(currTagCnt == kLastTagIdx);
1605
1606 // Test i = 0 to exercise unused tag string.
1607 for (int i = 0; i <= kLastTagIdx; ++i) {
1608 tagStr.printf("tag%d", i);
1609 REPORTER_ASSERT(reporter, cache->countUniqueKeysWithTag(tagStr.c_str()) == i);
1610 }
1611#endif
1612}
1613
1615 Mock mock(30000);
1616 auto dContext = mock.dContext();
1617 GrResourceCache* cache = mock.cache();
1618 GrGpu* gpu = mock.gpu();
1619
1622 int freed[3] = { 0, 0, 0 };
1623
1624 auto releaseProc = [](void* ctx) {
1625 int* index = (int*) ctx;
1626 *index = 1;
1627 };
1628
1629 for (int i = 0; i < 3; ++i) {
1630 backends[i] = dContext->createBackendTexture(16,
1631 16,
1639 wrapped[i]->setRelease(releaseProc, &freed[i]);
1640 }
1641
1642 REPORTER_ASSERT(reporter, 0 == (freed[0] + freed[1] + freed[2]));
1643
1644 // This should free nothing since no messages were sent.
1645 cache->purgeAsNeeded();
1646
1647 REPORTER_ASSERT(reporter, 0 == (freed[0] + freed[1] + freed[2]));
1648
1649 // Send message to free the first resource
1650 GrResourceCache::ReturnResourceFromThread(std::move(wrapped[0]), dContext->directContextID());
1651 cache->purgeAsNeeded();
1652
1653 REPORTER_ASSERT(reporter, 1 == (freed[0] + freed[1] + freed[2]));
1654 REPORTER_ASSERT(reporter, 1 == freed[0]);
1655
1656 GrResourceCache::ReturnResourceFromThread(std::move(wrapped[2]), dContext->directContextID());
1657 cache->purgeAsNeeded();
1658
1659 REPORTER_ASSERT(reporter, 2 == (freed[0] + freed[1] + freed[2]));
1660 REPORTER_ASSERT(reporter, 0 == freed[1]);
1661
1662 wrapped[1].reset();
1663
1664 mock.reset();
1665
1666 REPORTER_ASSERT(reporter, 3 == (freed[0] + freed[1] + freed[2]));
1667}
1668
1669DEF_GANESH_TEST(ResourceCacheMisc, reporter, /* options */, CtsEnforcement::kApiLevel_T) {
1670 // The below tests create their own mock contexts.
1690}
1691
1692// This simulates a portion of Chrome's context abandonment processing.
1693// Please see: crbug.com/1011368 and crbug.com/1014993
1694DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(ResourceMessagesAfterAbandon,
1695 reporter,
1696 ctxInfo,
1698 using namespace skgpu;
1699
1700 auto dContext = ctxInfo.directContext();
1701 GrGpu* gpu = dContext->priv().getGpu();
1702
1703 Protected isProtected = Protected(dContext->priv().caps()->supportsProtectedContent());
1704
1705 GrBackendTexture backend = dContext->createBackendTexture(
1707 isProtected);
1712
1713 auto releaseProc = [](void* ctx) {
1714 int* index = (int*) ctx;
1715 *index = 1;
1716 };
1717
1718 int freed = 0;
1719
1720 tex->setRelease(releaseProc, &freed);
1721
1722 REPORTER_ASSERT(reporter, 0 == freed);
1723
1724 // We must delete the backend texture before abandoning the context in vulkan. We just do it
1725 // for all the backends for consistency.
1726 dContext->deleteBackendTexture(backend);
1727 dContext->abandonContext();
1728
1729 REPORTER_ASSERT(reporter, 1 == freed);
1730
1731 // In the past, creating this message could cause an exception due to
1732 // an un-safe pointer upcast from GrTexture* to GrGpuResource* through virtual inheritance
1733 // after deletion of tex.
1734 GrResourceCache::ReturnResourceFromThread(std::move(tex), dContext->directContextID());
1735
1736 // This doesn't actually do anything but it does trigger us to read messages
1737 dContext->purgeUnlockedResources(GrPurgeResourceOptions::kAllResources);
1738}
1739
1740////////////////////////////////////////////////////////////////////////////////
1742 GrRenderable renderable,
1743 SkISize dims,
1744 int sampleCnt) {
1745 auto format = provider->caps()->getDefaultBackendFormat(GrColorType::kRGBA_8888, renderable);
1746 return provider->createTexture(dims,
1747 format,
1749 renderable,
1750 sampleCnt,
1754 /*label=*/{});
1755}
1756
1758 GrRenderable renderable,
1759 SkISize dims,
1760 int sampleCnt) {
1761 GrProxyProvider* proxyProvider = rContext->priv().proxyProvider();
1762 const GrCaps* caps = rContext->priv().caps();
1763
1764
1767
1768 return proxyProvider->createProxy(format,
1769 dims,
1770 renderable,
1771 sampleCnt,
1776 /*label=*/{});
1777}
1778
1779// Exercise GrSurface::gpuMemorySize for different combos of MSAA, RT-only,
1780// Texture-only, both-RT-and-Texture and MIPmapped
1782 reporter,
1783 ctxInfo,
1785 auto context = ctxInfo.directContext();
1786 GrResourceProvider* resourceProvider = context->priv().resourceProvider();
1787 const GrCaps* caps = context->priv().caps();
1788
1789 static constexpr SkISize kSize = {64, 64};
1790 static constexpr auto kArea = kSize.area();
1791
1792 // Normal versions
1793 {
1794 sk_sp<GrTexture> tex;
1795
1796 tex = make_normal_texture(resourceProvider, GrRenderable::kYes, kSize, 1);
1797 size_t size = tex->gpuMemorySize();
1798 REPORTER_ASSERT(reporter, kArea*4 == size);
1799
1800 size_t sampleCount = (size_t)caps->getRenderTargetSampleCount(4, tex->backendFormat());
1801 if (sampleCount >= 4) {
1802 tex = make_normal_texture(resourceProvider, GrRenderable::kYes, kSize, sampleCount);
1803 size = tex->gpuMemorySize();
1805 kArea*4 == size || // msaa4 failed
1806 kArea*4*sampleCount == size || // auto-resolving
1807 kArea*4*(sampleCount+1) == size); // explicit resolve buffer
1808 }
1809
1810 tex = make_normal_texture(resourceProvider, GrRenderable::kNo, kSize, 1);
1811 size = tex->gpuMemorySize();
1812 REPORTER_ASSERT(reporter, kArea*4 == size);
1813 }
1814
1815 // Mipmapped versions
1816 if (caps->mipmapSupport()) {
1818
1819 proxy = make_mipmap_proxy(context, GrRenderable::kYes, kSize, 1);
1820 size_t size = proxy->gpuMemorySize();
1821 REPORTER_ASSERT(reporter, kArea*4 + (kArea*4)/3 == size);
1822
1823 size_t sampleCount = (size_t)caps->getRenderTargetSampleCount(4, proxy->backendFormat());
1824 if (sampleCount >= 4) {
1825 proxy = make_mipmap_proxy(context, GrRenderable::kYes, kSize, sampleCount);
1826 size = proxy->gpuMemorySize();
1828 kArea*4 + (kArea*4)/3 == size || // msaa4 failed
1829 kArea*4*sampleCount + (kArea*4)/3 == size || // auto-resolving
1830 kArea*4*(sampleCount+1) + (kArea*4)/3 == size); // explicit resolve buffer
1831 }
1832
1833 proxy = make_mipmap_proxy(context, GrRenderable::kNo, kSize, 1);
1834 size = proxy->gpuMemorySize();
1835 REPORTER_ASSERT(reporter, kArea*4 + (kArea*4)/3 == size);
1836 }
1837}
1838
1840 reporter,
1841 ctxInfo,
1843 constexpr size_t kTexSize = 16 * 16 * 4;
1844
1845 auto dContext = ctxInfo.directContext();
1846 dContext->setResourceCacheLimit(2 * kTexSize);
1847 auto resourceProvider = dContext->priv().resourceProvider();
1848 auto resourceCache = dContext->priv().getResourceCache();
1849 for (bool success : { true, false }) {
1850 reporter->push(SkString(success ? "success" : "failure"));
1851
1852 resourceCache->releaseAll();
1853 REPORTER_ASSERT(reporter, resourceCache->getBudgetedResourceBytes() == 0);
1854
1855 // Make one unpurgeable texture and one purgeable texture.
1856 auto lockedTex = make_normal_texture(resourceProvider, GrRenderable::kNo, {16, 16}, 1);
1857 REPORTER_ASSERT(reporter, lockedTex->gpuMemorySize() == kTexSize);
1858
1859 // N.b. this surface is renderable so "reuseScratchTextures = false" won't mess us up.
1860 auto purgeableTex = make_normal_texture(resourceProvider, GrRenderable::kYes, {16, 16}, 1);
1861 if (success) {
1862 purgeableTex = nullptr;
1863 }
1864
1865 size_t expectedPurgeable = success ? kTexSize : 0;
1866 REPORTER_ASSERT(reporter, expectedPurgeable == resourceCache->getPurgeableBytes(),
1867 "%zu vs %zu", expectedPurgeable, resourceCache->getPurgeableBytes());
1868 REPORTER_ASSERT(reporter, success == resourceCache->purgeToMakeHeadroom(kTexSize));
1869 size_t expectedBudgeted = success ? kTexSize : (2 * kTexSize);
1870 REPORTER_ASSERT(reporter, expectedBudgeted == resourceCache->getBudgetedResourceBytes(),
1871 "%zu vs %zu", expectedBudgeted, resourceCache->getBudgetedResourceBytes());
1872 reporter->pop();
1873 }
1874}
1875
1876#if GR_GPU_STATS
1877DEF_GANESH_TEST_FOR_MOCK_CONTEXT(OverbudgetFlush, reporter, ctxInfo) {
1878 auto context = ctxInfo.directContext();
1879 context->setResourceCacheLimit(1);
1880
1881 // Helper that determines if cache is overbudget.
1882 auto overbudget = [context] {
1883 int uNum;
1884 size_t uSize;
1885 context->getResourceCacheUsage(&uNum, &uSize);
1886 size_t bSize = context->getResourceCacheLimit();
1887 return uSize > bSize;
1888 };
1889
1890 // Helper that does a trivial draw to a surface.
1891 auto drawToSurf = [](SkSurface* surf) {
1892 surf->getCanvas()->drawRect(SkRect::MakeWH(1,1), SkPaint());
1893 };
1894
1895 // Helper that checks whether a flush has occurred between calls.
1896 int baseFlushCount = 0;
1897 auto getFlushCountDelta = [context, &baseFlushCount]() {
1898 int cur = context->priv().getGpu()->stats()->numSubmitToGpus();
1899 int delta = cur - baseFlushCount;
1900 baseFlushCount = cur;
1901 return delta;
1902 };
1903
1905 auto surf1 = SkSurfaces::RenderTarget(context, skgpu::Budgeted::kYes, info, 1, nullptr);
1906 auto surf2 = SkSurfaces::RenderTarget(context, skgpu::Budgeted::kYes, info, 1, nullptr);
1907
1908 drawToSurf(surf1.get());
1909 drawToSurf(surf2.get());
1910
1911 // Flush each surface once to ensure that their backing stores are allocated.
1912 context->flushAndSubmit(surf1.get(), GrSyncCpu::kNo);
1913 context->flushAndSubmit(surf2.get(), GrSyncCpu::kNo);
1914 REPORTER_ASSERT(reporter, overbudget());
1915 getFlushCountDelta();
1916
1917 // Nothing should be purgeable so drawing to either surface doesn't cause a flush.
1918 drawToSurf(surf1.get());
1919 REPORTER_ASSERT(reporter, !getFlushCountDelta());
1920 drawToSurf(surf2.get());
1921 REPORTER_ASSERT(reporter, !getFlushCountDelta());
1922 REPORTER_ASSERT(reporter, overbudget());
1923
1924 // Make surf1 purgeable. Drawing to surf2 should flush.
1925 context->flushAndSubmit(surf1.get(), GrSyncCpu::kNo);
1926 surf1.reset();
1927 drawToSurf(surf2.get());
1928 REPORTER_ASSERT(reporter, getFlushCountDelta());
1929 REPORTER_ASSERT(reporter, overbudget());
1930}
1931#endif
static void readback(const SkBitmap &src, int *result, int resultCount)
Definition: BlurTest.cpp:264
const char * backend
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition: DM.cpp:213
reporter
Definition: FontMgrTest.cpp:39
GrWrapCacheable
Definition: GrTypesPriv.h:85
@ kRead_GrIOType
Definition: GrTypesPriv.h:403
@ kAdopt_GrWrapOwnership
Definition: GrTypesPriv.h:82
@ kBorrow_GrWrapOwnership
Definition: GrTypesPriv.h:79
GrColorType
Definition: GrTypesPriv.h:540
skgpu::Protected Protected
static void test_purge_unlocked(skiatest::Reporter *reporter)
static GrAttachment * get_SB(GrRenderTarget *rt)
static void test_free_texture_messages(skiatest::Reporter *reporter)
static void make_unique_key(skgpu::UniqueKey *key, int data, const char *tag=nullptr)
static void test_tags(skiatest::Reporter *reporter)
static void test_custom_data(skiatest::Reporter *reporter)
static void test_purge_command_buffer_usage(skiatest::Reporter *reporter)
static void test_time_purge(skiatest::Reporter *reporter)
static sk_sp< GrTexture > make_normal_texture(GrResourceProvider *provider, GrRenderable renderable, SkISize dims, int sampleCnt)
static bool is_rendering_and_not_angle_es3(skgpu::ContextType type)
static void test_abandoned(skiatest::Reporter *reporter)
static void test_timestamp_wrap(skiatest::Reporter *reporter)
void test_unbudgeted_to_scratch(skiatest::Reporter *reporter)
static void test_unbudgeted(skiatest::Reporter *reporter)
static void test_remove_scratch_key(skiatest::Reporter *reporter)
static const int gWidth
static sk_sp< GrRenderTarget > create_RT_with_SB(GrResourceProvider *provider, int size, int sampleCount, skgpu::Budgeted budgeted)
static void test_cache_chained_purge(skiatest::Reporter *reporter)
static void test_partial_purge(skiatest::Reporter *reporter)
static sk_sp< GrTextureProxy > make_mipmap_proxy(GrRecordingContext *rContext, GrRenderable renderable, SkISize dims, int sampleCnt)
DEF_GANESH_TEST(ResourceCacheMisc, reporter,, CtsEnforcement::kApiLevel_T)
static void test_no_key(skiatest::Reporter *reporter)
static const int gHeight
static void test_scratch_key_consistency(skiatest::Reporter *reporter)
static void test_budgeting(skiatest::Reporter *reporter)
DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(ResourceCacheCache, reporter, ctxInfo, CtsEnforcement::kApiLevel_T)
static void test_purge_invalidated(skiatest::Reporter *reporter)
static void test_duplicate_unique_key(skiatest::Reporter *reporter)
DEF_GANESH_TEST_FOR_CONTEXTS(ResourceCacheStencilBuffers, &is_rendering_and_not_angle_es3, reporter, ctxInfo, nullptr, CtsEnforcement::kApiLevel_T)
static void test_duplicate_scratch_key(skiatest::Reporter *reporter)
@ kPremul_SkAlphaType
pixel components are premultiplied by alpha
Definition: SkAlphaType.h:29
#define SkASSERT(cond)
Definition: SkAssert.h:116
@ kRGBA_8888_SkColorType
pixel with 8 bits for red, green, blue, alpha; in 32-bit word
Definition: SkColorType.h:24
constexpr SkColor SK_ColorBLACK
Definition: SkColor.h:103
static void releaseProc(const void *ptr, void *context)
static SkString resource(SkPDFResourceType type, int index)
static void SkSafeUnref(T *obj)
Definition: SkRefCnt.h:149
sk_sp< T > sk_ref_sp(T *obj)
Definition: SkRefCnt.h:381
SkDEBUGCODE(SK_SPI) SkThreadID SkGetThreadID()
static constexpr bool SkToBool(const T &x)
Definition: SkTo.h:35
#define DEF_GANESH_TEST_FOR_MOCK_CONTEXT(name, reporter, context_info)
Definition: Test.h:450
#define REPORTER_ASSERT(r, cond,...)
Definition: Test.h:286
#define ERRORF(r,...)
Definition: Test.h:293
static sk_sp< GrTextureProxy > wrapped(skiatest::Reporter *reporter, GrRecordingContext *rContext, GrProxyProvider *proxyProvider, SkBackingFit fit)
int find(T *array, int N, T item)
GLenum type
constexpr int kH
constexpr int kW
bool isValid() const
const GrCaps * caps() const
Definition: GrCaps.h:57
bool mipmapSupport() const
Definition: GrCaps.h:72
bool avoidStencilBuffers() const
Definition: GrCaps.h:139
GrBackendFormat getDefaultBackendFormat(GrColorType, GrRenderable) const
Definition: GrCaps.cpp:400
virtual int getRenderTargetSampleCount(int requestedCount, const GrBackendFormat &) const =0
GrResourceCache * getResourceCache()
static sk_sp< GrDirectContext > MakeMock(const GrMockOptions *, const GrContextOptions &)
GrDirectContextPriv priv()
void setResourceCacheLimit(size_t maxResourceBytes)
void setUniqueKey(const skgpu::UniqueKey &key)
size_t gpuMemorySize() const
const skgpu::UniqueKey & getUniqueKey() const
GrGpuResource(GrGpu *, std::string_view label)
void registerWithCacheWrapped(GrWrapCacheable)
void registerWithCache(skgpu::Budgeted)
ResourcePriv resourcePriv()
Definition: GrGpu.h:62
sk_sp< GrTexture > wrapBackendTexture(const GrBackendTexture &, GrWrapOwnership, GrWrapCacheable, GrIOType)
Definition: GrGpu.cpp:297
void unref() const
Definition: GrGpuResource.h:62
sk_sp< GrTextureProxy > createProxy(const GrBackendFormat &, SkISize dimensions, GrRenderable, int renderTargetSampleCnt, skgpu::Mipmapped, SkBackingFit, skgpu::Budgeted, GrProtected, std::string_view label, GrInternalSurfaceFlags=GrInternalSurfaceFlags::kNone, UseAllocator useAllocator=UseAllocator::kYes)
GrProxyProvider * proxyProvider()
GrRecordingContextPriv priv()
GrAttachment * getStencilAttachment(bool useMSAASurface) const
size_t getResourceBytes() const
static std::enable_if_t< std::is_base_of_v< GrGpuResource, T >, void > ReturnResourceFromThread(sk_sp< T > &&resource, GrDirectContext::DirectContextID id)
int getResourceCount() const
void purgeUnlockedResources(GrPurgeResourceOptions opts)
bool attachStencilAttachment(GrRenderTarget *rt, bool useMSAASurface)
sk_sp< GrTexture > wrapBackendTexture(const GrBackendTexture &tex, GrWrapOwnership, GrWrapCacheable, GrIOType)
GrResourceProviderPriv priv()
sk_sp< GrTexture > createTexture(SkISize dimensions, const GrBackendFormat &format, GrTextureType textureType, skgpu::Renderable renderable, int renderTargetSampleCnt, skgpu::Mipmapped mipmapped, skgpu::Budgeted budgeted, skgpu::Protected isProtected, std::string_view label)
const GrCaps * caps() const
const GrBackendFormat & backendFormat() const
size_t gpuMemorySize() const
virtual GrBackendFormat backendFormat() const =0
void setRelease(sk_sp< skgpu::RefCntedCallback > releaseHelper)
Definition: GrSurface.cpp:60
virtual GrRenderTarget * asRenderTarget()
Definition: GrSurface.h:65
Mock(size_t maxBytes)
GrDirectContext * dContext()
void reset()
GrResourceCache * cache()
GrGpu * gpu()
void drawImage(const SkImage *image, SkScalar left, SkScalar top)
Definition: SkCanvas.h:1528
const void * data() const
Definition: SkData.h:37
static sk_sp< SkData > MakeWithCopy(const void *data, size_t length)
Definition: SkData.cpp:111
void unref() const
Definition: SkRefCnt.h:177
uint32_t nextU()
Definition: SkRandom.h:42
uint32_t nextULessThan(uint32_t count)
Definition: SkRandom.h:93
void printf(const char format[],...) SK_PRINTF_LIKE(2
Definition: SkString.cpp:534
const char * c_str() const
Definition: SkString.h:133
int size() const
Definition: SkTDArray.h:138
T * append()
Definition: SkTDArray.h:191
static const size_t kDefaultSize
static size_t ExpectedScratchKeySize()
static TestResource * CreateScratch(GrGpu *gpu, skgpu::Budgeted budgeted, SimulatedProperty property, size_t size=kDefaultSize)
static int NumAlive()
~TestResource() override
static TestResource * CreateWrapped(GrGpu *gpu, GrWrapCacheable cacheable, size_t size=kDefaultSize)
TestResource(GrGpu *gpu, std::string_view label, skgpu::Budgeted budgeted=skgpu::Budgeted::kYes, size_t size=kDefaultSize)
static void ComputeScratchKey(SimulatedProperty property, skgpu::ScratchKey *key)
void setUnrefWhenDestroyed(sk_sp< TestResource > resource)
T * get() const
Definition: SkRefCnt.h:303
void reset(T *ptr=nullptr)
Definition: SkRefCnt.h:310
size_t size() const
Definition: ResourceKey.h:42
static ResourceType GenerateResourceType()
Definition: ResourceKey.cpp:16
uint32_t ResourceType
Definition: ResourceKey.h:200
static Domain GenerateDomain()
Definition: ResourceKey.cpp:27
SkData * getCustomData() const
Definition: ResourceKey.h:263
void setCustomData(sk_sp< SkData > data)
Definition: ResourceKey.h:262
VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE auto & d
Definition: main.cc:19
VkSurfaceKHR surface
Definition: main.cc:49
double duration
Definition: examples.cpp:30
static bool b
struct MyStruct a[10]
GAsyncResult * result
uint32_t uint32_t * format
constexpr int kSize
SK_API sk_sp< SkSurface > RenderTarget(GrRecordingContext *context, skgpu::Budgeted budgeted, const SkImageInfo &imageInfo, int sampleCount, GrSurfaceOrigin surfaceOrigin, const SkSurfaceProps *surfaceProps, bool shouldCreateWithMips=false, bool isProtected=false)
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace Enable an endless trace buffer The default is a ring buffer This is useful when very old events need to viewed For during application launch Memory usage will continue to grow indefinitely however Start app with an specific route defined on the framework flutter assets Path to the Flutter assets directory enable service port Allow the VM service to fallback to automatic port selection if binding to a specified port fails trace Trace early application lifecycle Automatically switches to an endless trace buffer trace skia Filters out all Skia trace event categories except those that are specified in this comma separated list dump skp on shader Automatically dump the skp that triggers new shader compilations This is useful for writing custom ShaderWarmUp to reduce jank By this is not enabled to reduce the overhead purge persistent cache
Definition: switches.h:191
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition: switches.h:259
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap The size limit in megabytes for the Dart VM old gen heap space enable Enable the Impeller renderer on supported platforms Ignored if Impeller is not supported on the platform enable vulkan Enable loading Vulkan validation layers The layers must be available to the application and loadable On non Vulkan backends
Definition: switches.h:276
Definition: GpuTools.h:21
ContextType
Definition: ContextType.h:19
@ kANGLE_GL_ES3
ANGLE on OpenGL OpenGL ES 2 context.
@ kANGLE_D3D11_ES3
ANGLE on Direct3D11 OpenGL ES 2 context.
@ kANGLE_Metal_ES3
ANGLE on Metal ES 2 context.
Budgeted
Definition: GpuTypes.h:35
Renderable
Definition: GpuTypes.h:69
bool IsRenderingContext(skgpu::ContextType type)
Definition: ContextType.cpp:88
Protected
Definition: GpuTypes.h:61
Definition: SkRect.h:32
static constexpr SkIRect MakeWH(int32_t w, int32_t h)
Definition: SkRect.h:56
Definition: SkSize.h:16
static SkImageInfo MakeN32Premul(int width, int height)
static SkImageInfo Make(int width, int height, SkColorType ct, SkAlphaType at)
static constexpr SkRect MakeWH(float w, float h)
Definition: SkRect.h:609
std::shared_ptr< const fml::Mapping > data
Definition: texture_gles.cc:63