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