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