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