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