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