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