xref: /aosp_15_r20/external/skia/tests/ResourceAllocatorTest.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2017 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/SkCanvas.h"
10 #include "include/core/SkColor.h"
11 #include "include/core/SkColorType.h"
12 #include "include/core/SkImageInfo.h"
13 #include "include/core/SkRefCnt.h"
14 #include "include/core/SkString.h"
15 #include "include/core/SkSurface.h"
16 #include "include/core/SkTypes.h"
17 #include "include/gpu/GpuTypes.h"
18 #include "include/gpu/ganesh/GrBackendSurface.h"
19 #include "include/gpu/ganesh/GrDirectContext.h"
20 #include "include/gpu/ganesh/GrTypes.h"
21 #include "include/gpu/ganesh/SkSurfaceGanesh.h"
22 #include "include/private/base/SkTArray.h"
23 #include "include/private/base/SkTo.h"
24 #include "include/private/gpu/ganesh/GrTypesPriv.h"
25 #include "src/gpu/ResourceKey.h"
26 #include "src/gpu/SkBackingFit.h"
27 #include "src/gpu/ganesh/GrCaps.h"
28 #include "src/gpu/ganesh/GrDirectContextPriv.h"
29 #include "src/gpu/ganesh/GrProxyProvider.h"
30 #include "src/gpu/ganesh/GrResourceAllocator.h"
31 #include "src/gpu/ganesh/GrResourceCache.h"
32 #include "src/gpu/ganesh/GrResourceProvider.h"
33 #include "src/gpu/ganesh/GrSurface.h"
34 #include "src/gpu/ganesh/GrSurfaceProxy.h"
35 #include "src/gpu/ganesh/GrSurfaceProxyPriv.h"
36 #include "src/gpu/ganesh/GrTexture.h"
37 #include "src/gpu/ganesh/GrTextureProxy.h"
38 #include "tests/CtsEnforcement.h"
39 #include "tests/Test.h"
40 #include "tools/gpu/ManagedBackendTexture.h"
41 
42 #include <array>
43 #include <cstddef>
44 #include <functional>
45 #include <utility>
46 
47 using namespace skia_private;
48 
49 class GrRecordingContext;
50 struct GrContextOptions;
51 
52 namespace {
53 struct ProxyParams {
54     int             fSize;
55     GrRenderable    fRenderable;
56     GrColorType     fColorType;
57     SkBackingFit    fFit;
58     int             fSampleCnt;
59     skgpu::Budgeted fBudgeted;
60     enum Kind {
61         kDeferred,
62         kBackend,
63         kFullyLazy,
64         kLazy,
65         kInstantiated
66     };
67     Kind            fKind;
68     skgpu::UniqueKey     fUniqueKey = skgpu::UniqueKey();
69     // TODO: do we care about mipmapping
70 };
71 
72 constexpr GrRenderable kRT = GrRenderable::kYes;
73 constexpr GrRenderable kNotRT = GrRenderable::kNo;
74 
75 constexpr GrColorType kRGBA = GrColorType::kRGBA_8888;
76 constexpr GrColorType kAlpha = GrColorType::kAlpha_8;
77 
78 constexpr SkBackingFit kE = SkBackingFit::kExact;
79 constexpr SkBackingFit kA = SkBackingFit::kApprox;
80 
81 constexpr skgpu::Budgeted kNotB = skgpu::Budgeted::kNo;
82 constexpr skgpu::Budgeted kB = skgpu::Budgeted::kYes;
83 
84 constexpr ProxyParams::Kind kDeferred = ProxyParams::Kind::kDeferred;
85 constexpr ProxyParams::Kind kBackend = ProxyParams::Kind::kBackend;
86 constexpr ProxyParams::Kind kInstantiated = ProxyParams::Kind::kInstantiated;
87 constexpr ProxyParams::Kind kLazy = ProxyParams::Kind::kLazy;
88 constexpr ProxyParams::Kind kFullyLazy = ProxyParams::Kind::kFullyLazy;
89 }
90 
make_deferred(GrProxyProvider * proxyProvider,const GrCaps * caps,const ProxyParams & p)91 static sk_sp<GrSurfaceProxy> make_deferred(GrProxyProvider* proxyProvider, const GrCaps* caps,
92                                            const ProxyParams& p) {
93     const GrBackendFormat format = caps->getDefaultBackendFormat(p.fColorType, p.fRenderable);
94     return proxyProvider->createProxy(format,
95                                       {p.fSize, p.fSize},
96                                       p.fRenderable,
97                                       p.fSampleCnt,
98                                       skgpu::Mipmapped::kNo,
99                                       p.fFit,
100                                       p.fBudgeted,
101                                       GrProtected::kNo,
102                                       /*label=*/"ResourceAllocatorTest_Deffered");
103 }
104 
make_backend(GrDirectContext * dContext,const ProxyParams & p)105 static sk_sp<GrSurfaceProxy> make_backend(GrDirectContext* dContext, const ProxyParams& p) {
106     GrProxyProvider* proxyProvider = dContext->priv().proxyProvider();
107 
108     SkColorType skColorType = GrColorTypeToSkColorType(p.fColorType);
109     SkASSERT(SkColorType::kUnknown_SkColorType != skColorType);
110 
111     auto mbet = sk_gpu_test::ManagedBackendTexture::MakeWithoutData(
112             dContext, p.fSize, p.fSize, skColorType, skgpu::Mipmapped::kNo, GrRenderable::kNo);
113 
114     if (!mbet) {
115         return nullptr;
116     }
117 
118     return proxyProvider->wrapBackendTexture(mbet->texture(),
119                                              kBorrow_GrWrapOwnership,
120                                              GrWrapCacheable::kNo,
121                                              kRead_GrIOType,
122                                              mbet->refCountedCallback());
123 }
124 
make_fully_lazy(GrProxyProvider * proxyProvider,const GrCaps * caps,const ProxyParams & p)125 static sk_sp<GrSurfaceProxy> make_fully_lazy(GrProxyProvider* proxyProvider, const GrCaps* caps,
126                                              const ProxyParams& p) {
127     const GrBackendFormat format = caps->getDefaultBackendFormat(p.fColorType, p.fRenderable);
128     auto cb = [p](GrResourceProvider* provider, const GrSurfaceProxy::LazySurfaceDesc& desc) {
129         auto tex = provider->createTexture({p.fSize, p.fSize},
130                                            desc.fFormat,
131                                            desc.fTextureType,
132                                            desc.fRenderable,
133                                            desc.fSampleCnt,
134                                            desc.fMipmapped,
135                                            desc.fBudgeted,
136                                            desc.fProtected,
137                                            /*label=*/"ResourceAllocatorTest_FullLazy");
138         return GrSurfaceProxy::LazyCallbackResult(std::move(tex));
139     };
140     return GrProxyProvider::MakeFullyLazyProxy(std::move(cb), format, p.fRenderable, p.fSampleCnt,
141                                                GrProtected::kNo, *caps,
142                                                GrSurfaceProxy::UseAllocator::kYes);
143 }
144 
make_lazy(GrProxyProvider * proxyProvider,const GrCaps * caps,const ProxyParams & p)145 static sk_sp<GrSurfaceProxy> make_lazy(GrProxyProvider* proxyProvider, const GrCaps* caps,
146                                        const ProxyParams& p) {
147     const GrBackendFormat format = caps->getDefaultBackendFormat(p.fColorType, p.fRenderable);
148     auto cb = [](GrResourceProvider* provider, const GrSurfaceProxy::LazySurfaceDesc& desc) {
149         auto tex = provider->createTexture(desc.fDimensions,
150                                            desc.fFormat,
151                                            desc.fTextureType,
152                                            desc.fRenderable,
153                                            desc.fSampleCnt,
154                                            desc.fMipmapped,
155                                            desc.fBudgeted,
156                                            desc.fProtected,
157                                            /*label=*/"ResourceAllocatorTest_Lazy");
158         return GrSurfaceProxy::LazyCallbackResult(std::move(tex));
159     };
160     return proxyProvider->createLazyProxy(std::move(cb),
161                                           format,
162                                           {p.fSize, p.fSize},
163                                           skgpu::Mipmapped::kNo,
164                                           GrMipmapStatus::kNotAllocated,
165                                           GrInternalSurfaceFlags::kNone,
166                                           p.fFit,
167                                           p.fBudgeted,
168                                           GrProtected::kNo,
169                                           GrSurfaceProxy::UseAllocator::kYes,
170                                           /*label=*/{});
171 }
172 
make_proxy(GrDirectContext * dContext,const ProxyParams & p)173 static sk_sp<GrSurfaceProxy> make_proxy(GrDirectContext* dContext, const ProxyParams& p) {
174     GrProxyProvider* proxyProvider = dContext->priv().proxyProvider();
175     const GrCaps* caps = dContext->priv().caps();
176     sk_sp<GrSurfaceProxy> proxy;
177     switch (p.fKind) {
178         case ProxyParams::kDeferred:
179             proxy = make_deferred(proxyProvider, caps, p);
180             break;
181         case ProxyParams::kBackend:
182             proxy = make_backend(dContext, p);
183             break;
184         case ProxyParams::kFullyLazy:
185             proxy = make_fully_lazy(proxyProvider, caps, p);
186             break;
187         case ProxyParams::kLazy:
188             proxy = make_lazy(proxyProvider, caps, p);
189             break;
190         case ProxyParams::kInstantiated:
191             proxy = make_deferred(proxyProvider, caps, p);
192             if (proxy) {
193                 auto surf = proxy->priv().createSurface(dContext->priv().resourceProvider());
194                 proxy->priv().assign(std::move(surf));
195             }
196             break;
197     }
198     if (proxy && p.fUniqueKey.isValid()) {
199         SkASSERT(proxy->asTextureProxy());
200         proxyProvider->assignUniqueKeyToProxy(p.fUniqueKey, proxy->asTextureProxy());
201     }
202     return proxy;
203 }
204 
205 // Basic test that two proxies with overlapping intervals and compatible descriptors are
206 // assigned different GrSurfaces.
overlap_test(skiatest::Reporter * reporter,GrDirectContext * dContext,const sk_sp<GrSurfaceProxy> & p1,const sk_sp<GrSurfaceProxy> & p2,bool expectedResult)207 static void overlap_test(skiatest::Reporter* reporter, GrDirectContext* dContext,
208                          const sk_sp<GrSurfaceProxy>& p1, const sk_sp<GrSurfaceProxy>& p2,
209                          bool expectedResult) {
210     GrResourceAllocator alloc(dContext);
211 
212     alloc.addInterval(p1.get(), 0, 4, GrResourceAllocator::ActualUse::kYes,
213                       GrResourceAllocator::AllowRecycling::kYes);
214     alloc.incOps();
215     alloc.addInterval(p2.get(), 1, 2, GrResourceAllocator::ActualUse::kYes,
216                       GrResourceAllocator::AllowRecycling::kYes);
217     alloc.incOps();
218 
219     REPORTER_ASSERT(reporter, alloc.planAssignment());
220     REPORTER_ASSERT(reporter, alloc.makeBudgetHeadroom());
221     REPORTER_ASSERT(reporter, alloc.assign());
222 
223     REPORTER_ASSERT(reporter, p1->peekSurface());
224     REPORTER_ASSERT(reporter, p2->peekSurface());
225     bool doTheBackingStoresMatch = p1->underlyingUniqueID() == p2->underlyingUniqueID();
226     REPORTER_ASSERT(reporter, expectedResult == doTheBackingStoresMatch);
227 }
228 
229 // Test various cases when two proxies do not have overlapping intervals.
230 // This mainly acts as a test of the ResourceAllocator's free pool.
non_overlap_test(skiatest::Reporter * reporter,GrDirectContext * dContext,const sk_sp<GrSurfaceProxy> & p1,const sk_sp<GrSurfaceProxy> & p2,bool expectedResult)231 static void non_overlap_test(skiatest::Reporter* reporter, GrDirectContext* dContext,
232                              const sk_sp<GrSurfaceProxy>& p1, const sk_sp<GrSurfaceProxy>& p2,
233                              bool expectedResult) {
234     GrResourceAllocator alloc(dContext);
235 
236     alloc.incOps();
237     alloc.incOps();
238     alloc.incOps();
239     alloc.incOps();
240     alloc.incOps();
241     alloc.incOps();
242 
243     alloc.addInterval(p1.get(), 0, 2, GrResourceAllocator::ActualUse::kYes,
244                       GrResourceAllocator::AllowRecycling::kYes);
245     alloc.addInterval(p2.get(), 3, 5, GrResourceAllocator::ActualUse::kYes,
246                       GrResourceAllocator::AllowRecycling::kYes);
247 
248     REPORTER_ASSERT(reporter, alloc.planAssignment());
249     REPORTER_ASSERT(reporter, alloc.makeBudgetHeadroom());
250     REPORTER_ASSERT(reporter, alloc.assign());
251 
252     REPORTER_ASSERT(reporter, p1->peekSurface());
253     REPORTER_ASSERT(reporter, p2->peekSurface());
254     bool doTheBackingStoresMatch = p1->underlyingUniqueID() == p2->underlyingUniqueID();
255     REPORTER_ASSERT(reporter, expectedResult == doTheBackingStoresMatch);
256 }
257 
DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(ResourceAllocatorTest,reporter,ctxInfo,CtsEnforcement::kNever)258 DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(ResourceAllocatorTest,
259                                        reporter,
260                                        ctxInfo,
261                                        CtsEnforcement::kNever) {
262     auto dContext = ctxInfo.directContext();
263     const GrCaps* caps = dContext->priv().caps();
264 
265     struct TestCase {
266         ProxyParams   fP1;
267         ProxyParams   fP2;
268         bool          fExpectation;
269     };
270 
271     constexpr bool kShare = true;
272     constexpr bool kDontShare = false;
273 
274     // Non-RT GrSurfaces are never recycled on some platforms.
275     bool kConditionallyShare = caps->reuseScratchTextures();
276 
277     static const TestCase overlappingTests[] = {
278         // Two proxies with overlapping intervals and compatible descriptors should never share
279         // RT version
280         {{64, kRT, kRGBA, kA, 1, kNotB, kDeferred},
281          {64, kRT, kRGBA, kA, 1, kNotB, kDeferred},
282          kDontShare},
283         // non-RT version
284         {{64, kNotRT, kRGBA, kA, 1, kNotB, kDeferred},
285          {64, kNotRT, kRGBA, kA, 1, kNotB, kDeferred},
286          kDontShare},
287     };
288 
289     for (size_t i = 0; i < std::size(overlappingTests); i++) {
290         const TestCase& test = overlappingTests[i];
291         sk_sp<GrSurfaceProxy> p1 = make_proxy(dContext, test.fP1);
292         sk_sp<GrSurfaceProxy> p2 = make_proxy(dContext, test.fP2);
293         reporter->push(SkStringPrintf("case %d", SkToInt(i)));
294         overlap_test(reporter, dContext, std::move(p1), std::move(p2), test.fExpectation);
295         reporter->pop();
296     }
297 
298     auto beFormat = caps->getDefaultBackendFormat(GrColorType::kRGBA_8888, GrRenderable::kYes);
299     int k2 = caps->getRenderTargetSampleCount(2, beFormat);
300     int k4 = caps->getRenderTargetSampleCount(4, beFormat);
301 
302     // This cannot be made static as some of the members depend on non static variables like
303     // kConditionallyShare, k2, and k4.
304     const TestCase nonOverlappingTests[] = {
305         // Two non-overlapping intervals w/ compatible proxies should share
306         // both same size & approx
307         {{64, kRT, kRGBA, kA, 1, kNotB, kDeferred},
308          {64, kRT, kRGBA, kA, 1, kNotB, kDeferred},
309          kShare},
310         {{64, kNotRT, kRGBA, kA, 1, kNotB, kDeferred},
311          {64, kNotRT, kRGBA, kA, 1, kNotB, kDeferred},
312          kConditionallyShare},
313         // diffs sizes but still approx
314         {{64, kRT, kRGBA, kA, 1, kNotB, kDeferred},
315          {50, kRT, kRGBA, kA, 1, kNotB, kDeferred},
316          kShare},
317         {{64, kNotRT, kRGBA, kA, 1, kNotB, kDeferred},
318          {50, kNotRT, kRGBA, kA, 1, kNotB, kDeferred},
319          kConditionallyShare},
320         // sames sizes but exact
321         {{64, kRT, kRGBA, kE, 1, kNotB, kDeferred},
322          {64, kRT, kRGBA, kE, 1, kNotB, kDeferred},
323          kShare},
324         {{64, kNotRT, kRGBA, kE, 1, kNotB, kDeferred},
325          {64, kNotRT, kRGBA, kE, 1, kNotB, kDeferred},
326          kConditionallyShare},
327         // Two non-overlapping intervals w/ different exact sizes should not share
328         {{56, kRT, kRGBA, kE, 1, kNotB, kDeferred},
329          {54, kRT, kRGBA, kE, 1, kNotB, kDeferred},
330          kDontShare},
331         // Two non-overlapping intervals w/ _very different_ approx sizes should not share
332         {{255, kRT, kRGBA, kA, 1, kNotB, kDeferred},
333          {127, kRT, kRGBA, kA, 1, kNotB, kDeferred},
334          kDontShare},
335         // Two non-overlapping intervals w/ different MSAA sample counts should not share
336         {{64, kRT, kRGBA, kA, k2, kNotB, kDeferred},
337          {64, kRT, kRGBA, kA, k4, kNotB, kDeferred},
338          k2 == k4},
339         // Two non-overlapping intervals w/ different configs should not share
340         {{64, kRT, kRGBA, kA, 1, kNotB, kDeferred},
341          {64, kRT, kAlpha, kA, 1, kNotB, kDeferred},
342          kDontShare},
343         // Two non-overlapping intervals w/ different RT classifications should never share
344         {{64, kRT, kRGBA, kA, 1, kNotB, kDeferred},
345          {64, kNotRT, kRGBA, kA, 1, kNotB, kDeferred},
346          kDontShare},
347         {{64, kNotRT, kRGBA, kA, 1, kNotB, kDeferred},
348          {64, kRT, kRGBA, kA, 1, kNotB, kDeferred},
349          kDontShare},
350         // Two non-overlapping intervals w/ different origins should share
351         {{64, kRT, kRGBA, kA, 1, kNotB, kDeferred},
352          {64, kRT, kRGBA, kA, 1, kNotB, kDeferred},
353          kShare},
354         // Wrapped backend textures should never be reused
355         {{64, kNotRT, kRGBA, kE, 1, kNotB, kBackend},
356          {64, kNotRT, kRGBA, kE, 1, kNotB, kDeferred},
357          kDontShare}
358     };
359 
360     for (size_t i = 0; i < std::size(nonOverlappingTests); i++) {
361         const TestCase& test = nonOverlappingTests[i];
362         sk_sp<GrSurfaceProxy> p1 = make_proxy(dContext, test.fP1);
363         sk_sp<GrSurfaceProxy> p2 = make_proxy(dContext, test.fP2);
364 
365         if (!p1 || !p2) {
366             continue; // creation can fail (e.g., for msaa4 on iOS)
367         }
368 
369         reporter->push(SkStringPrintf("case %d", SkToInt(i)));
370         non_overlap_test(reporter, dContext, std::move(p1), std::move(p2),
371                          test.fExpectation);
372         reporter->pop();
373     }
374 }
375 
draw(GrRecordingContext * rContext)376 static void draw(GrRecordingContext* rContext) {
377     SkImageInfo ii = SkImageInfo::Make(1024, 1024, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
378 
379     sk_sp<SkSurface> s = SkSurfaces::RenderTarget(
380             rContext, skgpu::Budgeted::kYes, ii, 1, kTopLeft_GrSurfaceOrigin, nullptr);
381 
382     SkCanvas* c = s->getCanvas();
383 
384     c->clear(SK_ColorBLACK);
385 }
386 
DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(ResourceAllocatorStressTest,reporter,ctxInfo,CtsEnforcement::kApiLevel_T)387 DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(ResourceAllocatorStressTest,
388                                        reporter,
389                                        ctxInfo,
390                                        CtsEnforcement::kApiLevel_T) {
391     auto context = ctxInfo.directContext();
392 
393     size_t maxBytes = context->getResourceCacheLimit();
394 
395     context->setResourceCacheLimit(0); // We'll always be overbudget
396 
397     draw(context);
398     draw(context);
399     draw(context);
400     draw(context);
401     context->flushAndSubmit();
402 
403     context->setResourceCacheLimit(maxBytes);
404 }
405 
406 struct Interval {
407     ProxyParams           fParams;
408     int                   fStart;
409     int                   fEnd;
410     sk_sp<GrSurfaceProxy> fProxy = nullptr;
411 };
412 
413 struct TestCase {
414     const char *          fName;
415     bool                  fShouldFit;
416     size_t                fBudget;
417     TArray<ProxyParams> fPurgeableResourcesInCache = {};
418     TArray<ProxyParams> fUnpurgeableResourcesInCache = {};
419     TArray<Interval>    fIntervals;
420 };
421 
memory_budget_test(skiatest::Reporter * reporter,GrDirectContext * dContext,const TestCase & test)422 static void memory_budget_test(skiatest::Reporter* reporter,
423                                GrDirectContext* dContext,
424                                const TestCase& test) {
425     // Reset cache.
426     auto cache = dContext->priv().getResourceCache();
427     cache->releaseAll();
428     cache->setLimit(test.fBudget);
429 
430     // Add purgeable entries.
431     size_t expectedPurgeableBytes = 0;
432     TArray<sk_sp<GrSurface>> purgeableSurfaces;
433     for (auto& params : test.fPurgeableResourcesInCache) {
434         SkASSERT(params.fKind == kInstantiated);
435         sk_sp<GrSurfaceProxy> proxy = make_proxy(dContext, params);
436         REPORTER_ASSERT(reporter, proxy->peekSurface());
437         expectedPurgeableBytes += proxy->gpuMemorySize();
438         purgeableSurfaces.push_back(sk_ref_sp(proxy->peekSurface()));
439     }
440     purgeableSurfaces.clear();
441     REPORTER_ASSERT(reporter, expectedPurgeableBytes == cache->getPurgeableBytes(),
442                     "%zu", cache->getPurgeableBytes());
443 
444     // Add unpurgeable entries.
445     size_t expectedUnpurgeableBytes = 0;
446     TArray<sk_sp<GrSurface>> unpurgeableSurfaces;
447     for (auto& params : test.fUnpurgeableResourcesInCache) {
448         SkASSERT(params.fKind == kInstantiated);
449         sk_sp<GrSurfaceProxy> proxy = make_proxy(dContext, params);
450         REPORTER_ASSERT(reporter, proxy->peekSurface());
451         expectedUnpurgeableBytes += proxy->gpuMemorySize();
452         unpurgeableSurfaces.push_back(sk_ref_sp(proxy->peekSurface()));
453     }
454 
455     auto unpurgeableBytes = cache->getBudgetedResourceBytes() - cache->getPurgeableBytes();
456     REPORTER_ASSERT(reporter, expectedUnpurgeableBytes == unpurgeableBytes,
457                     "%zu", unpurgeableBytes);
458 
459     // Add intervals and test.
460     GrResourceAllocator alloc(dContext);
461     for (auto& interval : test.fIntervals) {
462         for (int i = interval.fStart; i <= interval.fEnd; i++) {
463             alloc.incOps();
464         }
465         alloc.addInterval(interval.fProxy.get(), interval.fStart, interval.fEnd,
466                           GrResourceAllocator::ActualUse::kYes,
467                           GrResourceAllocator::AllowRecycling::kYes);
468     }
469     REPORTER_ASSERT(reporter, alloc.planAssignment());
470     REPORTER_ASSERT(reporter, alloc.makeBudgetHeadroom() == test.fShouldFit);
471 }
472 
DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(ResourceAllocatorMemoryBudgetTest,reporter,ctxInfo,CtsEnforcement::kApiLevel_T)473 DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(ResourceAllocatorMemoryBudgetTest,
474                                        reporter,
475                                        ctxInfo,
476                                        CtsEnforcement::kApiLevel_T) {
477     auto dContext = ctxInfo.directContext();
478 
479     constexpr bool    kUnder               = true;
480     constexpr bool    kOver                = false;
481     constexpr size_t  kRGBA64Bytes         = 4 * 64 * 64;
482     const ProxyParams kProxy64             = {64, kRT, kRGBA, kE, 1, kB,    kDeferred};
483     const ProxyParams kProxy64NotBudgeted  = {64, kRT, kRGBA, kE, 1, kNotB, kDeferred};
484     const ProxyParams kProxy64Lazy         = {64, kRT, kRGBA, kE, 1, kB,    kLazy};
485     const ProxyParams kProxy64FullyLazy    = {64, kRT, kRGBA, kE, 1, kB,    kFullyLazy};
486     const ProxyParams kProxy32Instantiated = {32, kRT, kRGBA, kE, 1, kB,    kInstantiated};
487     const ProxyParams kProxy64Instantiated = {64, kRT, kRGBA, kE, 1, kB,    kInstantiated};
488 
489     TestCase tests[] = {
490         {"empty DAG", kUnder, 0, {}, {}, {}},
491         {"unbudgeted", kUnder, 0, {}, {}, {{kProxy64NotBudgeted, 0, 2}}},
492         {"basic", kUnder, kRGBA64Bytes, {}, {}, {{kProxy64, 0, 2}}},
493         {"basic, over", kOver, kRGBA64Bytes - 1, {}, {}, {{kProxy64, 0, 2}}},
494         {"shared", kUnder, kRGBA64Bytes, {}, {},
495             {
496                 {kProxy64, 0, 2},
497                 {kProxy64, 3, 5},
498             }},
499         {"retrieved from cache", kUnder, kRGBA64Bytes,
500             /* purgeable */{kProxy64Instantiated},
501             /* unpurgeable */{},
502             {
503                 {kProxy64, 0, 2}
504             }},
505         {"purge 4", kUnder, kRGBA64Bytes,
506             /* purgeable */{
507                 kProxy32Instantiated,
508                 kProxy32Instantiated,
509                 kProxy32Instantiated,
510                 kProxy32Instantiated
511             },
512             /* unpurgeable */{},
513             {
514                 {kProxy64, 0, 2}
515             }},
516         {"dont purge what we've reserved", kOver, kRGBA64Bytes,
517             /* purgeable */{kProxy64Instantiated},
518             /* unpurgeable */{},
519             {
520                 {kProxy64, 0, 2},
521                 {kProxy64, 1, 3}
522             }},
523         {"unpurgeable", kOver, kRGBA64Bytes,
524             /* purgeable */{},
525             /* unpurgeable */{kProxy64Instantiated},
526             {
527                 {kProxy64, 0, 2}
528             }},
529         {"lazy", kUnder, kRGBA64Bytes,
530             /* purgeable */{},
531             /* unpurgeable */{},
532             {
533                 {kProxy64Lazy, 0, 2}
534             }},
535         {"lazy, over", kOver, kRGBA64Bytes - 1,
536             /* purgeable */{},
537             /* unpurgeable */{},
538             {
539                 {kProxy64Lazy, 0, 2}
540             }},
541         {"fully-lazy", kUnder, kRGBA64Bytes,
542             /* purgeable */{},
543             /* unpurgeable */{},
544             {
545                 {kProxy64FullyLazy, 0, 2}
546             }},
547         {"fully-lazy, over", kOver, kRGBA64Bytes - 1,
548             /* purgeable */{},
549             /* unpurgeable */{},
550             {
551                 {kProxy64FullyLazy, 0, 2}
552             }},
553     };
554     SkString match("");
555     for (size_t i = 0; i < std::size(tests); i++) {
556         TestCase& test = tests[i];
557         if (match.isEmpty() || match == SkString(test.fName)) {
558             // Create proxies
559             for (Interval& interval : test.fIntervals) {
560                 interval.fProxy = make_proxy(dContext, interval.fParams);
561             }
562             reporter->push(SkString(test.fName));
563             memory_budget_test(reporter, dContext, test);
564             reporter->pop();
565         }
566     }
567 }
568