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 here to ensure SK_SUPPORT_GPU is set correctly before it is examined.
9 #include "SkTypes.h"
10
11 #if SK_SUPPORT_GPU
12 #include <thread>
13 #include "GrContext.h"
14 #include "GrContextFactory.h"
15 #include "GrGpu.h"
16 #include "GrGpuResourceCacheAccess.h"
17 #include "GrGpuResourcePriv.h"
18 #include "GrRenderTarget.h"
19 #include "GrRenderTargetPriv.h"
20 #include "GrResourceCache.h"
21 #include "GrResourceProvider.h"
22 #include "GrTest.h"
23 #include "SkCanvas.h"
24 #include "SkGr.h"
25 #include "SkMessageBus.h"
26 #include "SkMipMap.h"
27 #include "SkSurface.h"
28 #include "Test.h"
29
30 static const int gWidth = 640;
31 static const int gHeight = 480;
32
33 ////////////////////////////////////////////////////////////////////////////////
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ResourceCacheCache,reporter,ctxInfo)34 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ResourceCacheCache, reporter, ctxInfo) {
35 GrContext* context = ctxInfo.grContext();
36 GrSurfaceDesc desc;
37 desc.fConfig = kRGBA_8888_GrPixelConfig;
38 desc.fFlags = kRenderTarget_GrSurfaceFlag;
39 desc.fWidth = gWidth;
40 desc.fHeight = gHeight;
41 SkImageInfo info = SkImageInfo::MakeN32Premul(gWidth, gHeight);
42 auto surface(SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, info));
43 SkCanvas* canvas = surface->getCanvas();
44
45 const SkIRect size = SkIRect::MakeWH(gWidth, gHeight);
46
47 SkBitmap src;
48 src.allocN32Pixels(size.width(), size.height());
49 src.eraseColor(SK_ColorBLACK);
50 size_t srcSize = src.getSize();
51
52 size_t initialCacheSize;
53 context->getResourceCacheUsage(nullptr, &initialCacheSize);
54
55 int oldMaxNum;
56 size_t oldMaxBytes;
57 context->getResourceCacheLimits(&oldMaxNum, &oldMaxBytes);
58
59 // Set the cache limits so we can fit 10 "src" images and the
60 // max number of textures doesn't matter
61 size_t maxCacheSize = initialCacheSize + 10*srcSize;
62 context->setResourceCacheLimits(1000, maxCacheSize);
63
64 SkBitmap readback;
65 readback.allocN32Pixels(size.width(), size.height());
66
67 for (int i = 0; i < 100; ++i) {
68 canvas->drawBitmap(src, 0, 0);
69 canvas->readPixels(size, &readback);
70
71 // "modify" the src texture
72 src.notifyPixelsChanged();
73
74 size_t curCacheSize;
75 context->getResourceCacheUsage(nullptr, &curCacheSize);
76
77 // we should never go over the size limit
78 REPORTER_ASSERT(reporter, curCacheSize <= maxCacheSize);
79 }
80
81 context->setResourceCacheLimits(oldMaxNum, oldMaxBytes);
82 }
83
is_rendering_and_not_angle_es3(sk_gpu_test::GrContextFactory::ContextType type)84 static bool is_rendering_and_not_angle_es3(sk_gpu_test::GrContextFactory::ContextType type) {
85 if (type == sk_gpu_test::GrContextFactory::kANGLE_D3D11_ES3_ContextType ||
86 type == sk_gpu_test::GrContextFactory::kANGLE_GL_ES3_ContextType) {
87 return false;
88 }
89 return sk_gpu_test::GrContextFactory::IsRenderingContext(type);
90 }
91
92 // This currently fails on ES3 ANGLE contexts
93 DEF_GPUTEST_FOR_CONTEXTS(ResourceCacheStencilBuffers, &is_rendering_and_not_angle_es3, reporter,
94 ctxInfo) {
95 GrContext* context = ctxInfo.grContext();
96 GrSurfaceDesc smallDesc;
97 smallDesc.fFlags = kRenderTarget_GrSurfaceFlag;
98 smallDesc.fConfig = kRGBA_8888_GrPixelConfig;
99 smallDesc.fWidth = 4;
100 smallDesc.fHeight = 4;
101 smallDesc.fSampleCnt = 0;
102
103 GrResourceProvider* resourceProvider = context->resourceProvider();
104 // Test that two budgeted RTs with the same desc share a stencil buffer.
105 sk_sp<GrTexture> smallRT0(resourceProvider->createTexture(smallDesc, SkBudgeted::kYes));
106 if (smallRT0 && smallRT0->asRenderTarget()) {
107 resourceProvider->attachStencilAttachment(smallRT0->asRenderTarget());
108 }
109
110 sk_sp<GrTexture> smallRT1(resourceProvider->createTexture(smallDesc, SkBudgeted::kYes));
111 if (smallRT1 && smallRT1->asRenderTarget()) {
112 resourceProvider->attachStencilAttachment(smallRT1->asRenderTarget());
113 }
114
115 REPORTER_ASSERT(reporter,
116 smallRT0 && smallRT1 &&
117 smallRT0->asRenderTarget() && smallRT1->asRenderTarget() &&
118 resourceProvider->attachStencilAttachment(smallRT0->asRenderTarget()) ==
119 resourceProvider->attachStencilAttachment(smallRT1->asRenderTarget()));
120
121 // An unbudgeted RT with the same desc should also share.
122 sk_sp<GrTexture> smallRT2(resourceProvider->createTexture(smallDesc, SkBudgeted::kNo));
123 if (smallRT2 && smallRT2->asRenderTarget()) {
124 resourceProvider->attachStencilAttachment(smallRT2->asRenderTarget());
125 }
126 REPORTER_ASSERT(reporter,
127 smallRT0 && smallRT2 &&
128 smallRT0->asRenderTarget() && smallRT2->asRenderTarget() &&
129 resourceProvider->attachStencilAttachment(smallRT0->asRenderTarget()) ==
130 resourceProvider->attachStencilAttachment(smallRT2->asRenderTarget()));
131
132 // An RT with a much larger size should not share.
133 GrSurfaceDesc bigDesc;
134 bigDesc.fFlags = kRenderTarget_GrSurfaceFlag;
135 bigDesc.fConfig = kRGBA_8888_GrPixelConfig;
136 bigDesc.fWidth = 400;
137 bigDesc.fHeight = 200;
138 bigDesc.fSampleCnt = 0;
139 sk_sp<GrTexture> bigRT(resourceProvider->createTexture(bigDesc, SkBudgeted::kNo));
140 if (bigRT && bigRT->asRenderTarget()) {
141 resourceProvider->attachStencilAttachment(bigRT->asRenderTarget());
142 }
143 REPORTER_ASSERT(reporter,
144 smallRT0 && bigRT &&
145 smallRT0->asRenderTarget() && bigRT->asRenderTarget() &&
146 resourceProvider->attachStencilAttachment(smallRT0->asRenderTarget()) !=
147 resourceProvider->attachStencilAttachment(bigRT->asRenderTarget()));
148
149 if (context->caps()->maxSampleCount() >= 4) {
150 // An RT with a different sample count should not share.
151 GrSurfaceDesc smallMSAADesc = smallDesc;
152 smallMSAADesc.fSampleCnt = 4;
153 sk_sp<GrTexture> smallMSAART0(resourceProvider->createTexture(smallMSAADesc,
154 SkBudgeted::kNo));
155 if (smallMSAART0 && smallMSAART0->asRenderTarget()) {
156 resourceProvider->attachStencilAttachment(smallMSAART0->asRenderTarget());
157 }
158 #ifdef SK_BUILD_FOR_ANDROID
159 if (!smallMSAART0) {
160 // The nexus player seems to fail to create MSAA textures.
161 return;
162 }
163 #endif
164 REPORTER_ASSERT(reporter,
165 smallRT0 && smallMSAART0 &&
166 smallRT0->asRenderTarget() && smallMSAART0->asRenderTarget() &&
167 resourceProvider->attachStencilAttachment(smallRT0->asRenderTarget()) !=
168 resourceProvider->attachStencilAttachment(smallMSAART0->asRenderTarget()));
169 // A second MSAA RT should share with the first MSAA RT.
170 sk_sp<GrTexture> smallMSAART1(resourceProvider->createTexture(smallMSAADesc,
171 SkBudgeted::kNo));
172 if (smallMSAART1 && smallMSAART1->asRenderTarget()) {
173 resourceProvider->attachStencilAttachment(smallMSAART1->asRenderTarget());
174 }
175 REPORTER_ASSERT(reporter,
176 smallMSAART0 && smallMSAART1 &&
177 smallMSAART0->asRenderTarget() &&
178 smallMSAART1->asRenderTarget() &&
179 resourceProvider->attachStencilAttachment(smallMSAART0->asRenderTarget()) ==
180 resourceProvider->attachStencilAttachment(smallMSAART1->asRenderTarget()));
181 // But not one with a larger sample count should not. (Also check that the request for 4
182 // samples didn't get rounded up to >= 8 or else they could share.).
183 if (context->caps()->maxSampleCount() >= 8 &&
184 smallMSAART0 && smallMSAART0->asRenderTarget() &&
185 smallMSAART0->asRenderTarget()->numColorSamples() < 8) {
186 smallMSAADesc.fSampleCnt = 8;
187 smallMSAART1.reset(resourceProvider->createTexture(smallMSAADesc, SkBudgeted::kNo));
188 sk_sp<GrTexture> smallMSAART1(
189 resourceProvider->createTexture(smallMSAADesc, SkBudgeted::kNo));
190 if (smallMSAART1 && smallMSAART1->asRenderTarget()) {
191 resourceProvider->attachStencilAttachment(smallMSAART1->asRenderTarget());
192 }
193 REPORTER_ASSERT(reporter,
194 smallMSAART0 && smallMSAART1 &&
195 smallMSAART0->asRenderTarget() &&
196 smallMSAART1->asRenderTarget() &&
197 resourceProvider->attachStencilAttachment(smallMSAART0->asRenderTarget()) !=
198 resourceProvider->attachStencilAttachment(smallMSAART1->asRenderTarget()));
199 }
200 }
201 }
202
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ResourceCacheWrappedResources,reporter,ctxInfo)203 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ResourceCacheWrappedResources, reporter, ctxInfo) {
204 GrContext* context = ctxInfo.grContext();
205 GrGpu* gpu = context->getGpu();
206 // this test is only valid for GL
207 if (!gpu || !gpu->glContextForTesting()) {
208 return;
209 }
210
211 GrBackendObject texHandles[3];
212 static const int kW = 100;
213 static const int kH = 100;
214
215 texHandles[0] = gpu->createTestingOnlyBackendTexture(nullptr, kW, kH, kRGBA_8888_GrPixelConfig);
216 texHandles[1] = gpu->createTestingOnlyBackendTexture(nullptr, kW, kH, kRGBA_8888_GrPixelConfig);
217 texHandles[2] = gpu->createTestingOnlyBackendTexture(nullptr, kW, kH, kRGBA_8888_GrPixelConfig);
218
219 context->resetContext();
220
221 GrBackendTextureDesc desc;
222 desc.fConfig = kBGRA_8888_GrPixelConfig;
223 desc.fWidth = kW;
224 desc.fHeight = kH;
225
226 desc.fTextureHandle = texHandles[0];
227 sk_sp<GrTexture> borrowed(context->resourceProvider()->wrapBackendTexture(
228 desc, kBorrow_GrWrapOwnership));
229
230 desc.fTextureHandle = texHandles[1];
231 sk_sp<GrTexture> adopted(context->resourceProvider()->wrapBackendTexture(
232 desc, kAdopt_GrWrapOwnership));
233
234 desc.fTextureHandle = texHandles[2];
235 sk_sp<GrTexture> adoptedAndCached(context->resourceProvider()->wrapBackendTexture(
236 desc, kAdoptAndCache_GrWrapOwnership));
237
238 REPORTER_ASSERT(reporter, borrowed != nullptr && adopted != nullptr &&
239 adoptedAndCached != nullptr);
240 if (!borrowed || !adopted || !adoptedAndCached) {
241 return;
242 }
243
244 borrowed.reset(nullptr);
245 adopted.reset(nullptr);
246 adoptedAndCached.reset(nullptr);
247
248 context->flush();
249
250 bool borrowedIsAlive = gpu->isTestingOnlyBackendTexture(texHandles[0]);
251 bool adoptedIsAlive = gpu->isTestingOnlyBackendTexture(texHandles[1]);
252 bool adoptedAndCachedIsAlive = gpu->isTestingOnlyBackendTexture(texHandles[2]);
253
254 REPORTER_ASSERT(reporter, borrowedIsAlive);
255 REPORTER_ASSERT(reporter, !adoptedIsAlive);
256 REPORTER_ASSERT(reporter, adoptedAndCachedIsAlive); // Still alive because it's in the cache
257
258 gpu->deleteTestingOnlyBackendTexture(texHandles[0], !borrowedIsAlive);
259 gpu->deleteTestingOnlyBackendTexture(texHandles[1], !adoptedIsAlive);
260 // We can't delete texHandles[2] - we've given control of the lifetime to the context/cache
261
262 context->resetContext();
263
264 // Purge the cache. This should force texHandles[2] to be deleted
265 context->getResourceCache()->purgeAllUnlocked();
266 adoptedAndCachedIsAlive = gpu->isTestingOnlyBackendTexture(texHandles[2]);
267 REPORTER_ASSERT(reporter, !adoptedAndCachedIsAlive);
268 gpu->deleteTestingOnlyBackendTexture(texHandles[2], !adoptedAndCachedIsAlive);
269 }
270
271 class TestResource : public GrGpuResource {
272 enum ScratchConstructor { kScratchConstructor };
273 public:
274 static const size_t kDefaultSize = 100;
275
276 /** Property that distinctly categorizes the resource.
277 * For example, textures have width, height, ... */
278 enum SimulatedProperty { kA_SimulatedProperty, kB_SimulatedProperty };
279
TestResource(GrGpu * gpu,SkBudgeted budgeted=SkBudgeted::kYes,size_t size=kDefaultSize)280 TestResource(GrGpu* gpu, SkBudgeted budgeted = SkBudgeted::kYes, size_t size = kDefaultSize)
281 : INHERITED(gpu)
282 , fToDelete(nullptr)
283 , fSize(size)
284 , fProperty(kA_SimulatedProperty)
285 , fIsScratch(false) {
286 ++fNumAlive;
287 this->registerWithCache(budgeted);
288 }
289
CreateScratch(GrGpu * gpu,SkBudgeted budgeted,SimulatedProperty property)290 static TestResource* CreateScratch(GrGpu* gpu, SkBudgeted budgeted,
291 SimulatedProperty property) {
292 return new TestResource(gpu, budgeted, property, kScratchConstructor);
293 }
CreateWrapped(GrGpu * gpu,size_t size=kDefaultSize)294 static TestResource* CreateWrapped(GrGpu* gpu, size_t size = kDefaultSize) {
295 return new TestResource(gpu, size);
296 }
297
~TestResource()298 ~TestResource() override {
299 --fNumAlive;
300 SkSafeUnref(fToDelete);
301 }
302
setSize(size_t size)303 void setSize(size_t size) {
304 fSize = size;
305 this->didChangeGpuMemorySize();
306 }
307
NumAlive()308 static int NumAlive() { return fNumAlive; }
309
setUnrefWhenDestroyed(TestResource * resource)310 void setUnrefWhenDestroyed(TestResource* resource) {
311 SkRefCnt_SafeAssign(fToDelete, resource);
312 }
313
ComputeScratchKey(SimulatedProperty property,GrScratchKey * key)314 static void ComputeScratchKey(SimulatedProperty property, GrScratchKey* key) {
315 static GrScratchKey::ResourceType t = GrScratchKey::GenerateResourceType();
316 GrScratchKey::Builder builder(key, t, kScratchKeyFieldCnt);
317 for (int i = 0; i < kScratchKeyFieldCnt; ++i) {
318 builder[i] = static_cast<uint32_t>(i + property);
319 }
320 }
321
ExpectedScratchKeySize()322 static size_t ExpectedScratchKeySize() {
323 return sizeof(uint32_t) * (kScratchKeyFieldCnt + GrScratchKey::kMetaDataCnt);
324 }
325 private:
326 static const int kScratchKeyFieldCnt = 6;
327
TestResource(GrGpu * gpu,SkBudgeted budgeted,SimulatedProperty property,ScratchConstructor)328 TestResource(GrGpu* gpu, SkBudgeted budgeted, SimulatedProperty property, ScratchConstructor)
329 : INHERITED(gpu)
330 , fToDelete(nullptr)
331 , fSize(kDefaultSize)
332 , fProperty(property)
333 , fIsScratch(true) {
334 ++fNumAlive;
335 this->registerWithCache(budgeted);
336 }
337
338 // Constructor for simulating resources that wrap backend objects.
TestResource(GrGpu * gpu,size_t size)339 TestResource(GrGpu* gpu, size_t size)
340 : INHERITED(gpu)
341 , fToDelete(nullptr)
342 , fSize(size)
343 , fProperty(kA_SimulatedProperty)
344 , fIsScratch(false) {
345 ++fNumAlive;
346 this->registerWithCacheWrapped();
347 }
348
computeScratchKey(GrScratchKey * key) const349 void computeScratchKey(GrScratchKey* key) const override {
350 if (fIsScratch) {
351 ComputeScratchKey(fProperty, key);
352 }
353 }
354
onGpuMemorySize() const355 size_t onGpuMemorySize() const override { return fSize; }
356
357 TestResource* fToDelete;
358 size_t fSize;
359 static int fNumAlive;
360 SimulatedProperty fProperty;
361 bool fIsScratch;
362 typedef GrGpuResource INHERITED;
363 };
364 int TestResource::fNumAlive = 0;
365
366 class Mock {
367 public:
Mock(int maxCnt,size_t maxBytes)368 Mock(int maxCnt, size_t maxBytes) {
369 fContext.reset(GrContext::CreateMockContext());
370 SkASSERT(fContext);
371 fContext->setResourceCacheLimits(maxCnt, maxBytes);
372 GrResourceCache* cache = fContext->getResourceCache();
373 cache->purgeAllUnlocked();
374 SkASSERT(0 == cache->getResourceCount() && 0 == cache->getResourceBytes());
375 }
376
cache()377 GrResourceCache* cache() { return fContext->getResourceCache(); }
378
context()379 GrContext* context() { return fContext.get(); }
380
381 private:
382 sk_sp<GrContext> fContext;
383 };
384
test_no_key(skiatest::Reporter * reporter)385 static void test_no_key(skiatest::Reporter* reporter) {
386 Mock mock(10, 30000);
387 GrContext* context = mock.context();
388 GrResourceCache* cache = mock.cache();
389
390 // Create a bunch of resources with no keys
391 TestResource* a = new TestResource(context->getGpu());
392 TestResource* b = new TestResource(context->getGpu());
393 TestResource* c = new TestResource(context->getGpu());
394 TestResource* d = new TestResource(context->getGpu());
395 a->setSize(11);
396 b->setSize(12);
397 c->setSize(13);
398 d->setSize(14);
399
400 REPORTER_ASSERT(reporter, 4 == TestResource::NumAlive());
401 REPORTER_ASSERT(reporter, 4 == cache->getResourceCount());
402 REPORTER_ASSERT(reporter, a->gpuMemorySize() + b->gpuMemorySize() + c->gpuMemorySize() +
403 d->gpuMemorySize() == cache->getResourceBytes());
404
405 // Should be safe to purge without deleting the resources since we still have refs.
406 cache->purgeAllUnlocked();
407 REPORTER_ASSERT(reporter, 4 == TestResource::NumAlive());
408
409 // Since the resources have neither unique nor scratch keys, delete immediately upon unref.
410
411 a->unref();
412 REPORTER_ASSERT(reporter, 3 == TestResource::NumAlive());
413 REPORTER_ASSERT(reporter, 3 == cache->getResourceCount());
414 REPORTER_ASSERT(reporter, b->gpuMemorySize() + c->gpuMemorySize() + d->gpuMemorySize() ==
415 cache->getResourceBytes());
416
417 c->unref();
418 REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive());
419 REPORTER_ASSERT(reporter, 2 == cache->getResourceCount());
420 REPORTER_ASSERT(reporter, b->gpuMemorySize() + d->gpuMemorySize() ==
421 cache->getResourceBytes());
422
423 d->unref();
424 REPORTER_ASSERT(reporter, 1 == TestResource::NumAlive());
425 REPORTER_ASSERT(reporter, 1 == cache->getResourceCount());
426 REPORTER_ASSERT(reporter, b->gpuMemorySize() == cache->getResourceBytes());
427
428 b->unref();
429 REPORTER_ASSERT(reporter, 0 == TestResource::NumAlive());
430 REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
431 REPORTER_ASSERT(reporter, 0 == cache->getResourceBytes());
432 }
433
434 // Each integer passed as a template param creates a new domain.
435 template <int>
make_unique_key(GrUniqueKey * key,int data,const char * tag=nullptr)436 static void make_unique_key(GrUniqueKey* key, int data, const char* tag = nullptr) {
437 static GrUniqueKey::Domain d = GrUniqueKey::GenerateDomain();
438 GrUniqueKey::Builder builder(key, d, 1, tag);
439 builder[0] = data;
440 }
441
test_budgeting(skiatest::Reporter * reporter)442 static void test_budgeting(skiatest::Reporter* reporter) {
443 Mock mock(10, 300);
444 GrContext* context = mock.context();
445 GrResourceCache* cache = mock.cache();
446
447 GrUniqueKey uniqueKey;
448 make_unique_key<0>(&uniqueKey, 0);
449
450 // Create a scratch, a unique, and a wrapped resource
451 TestResource* scratch =
452 TestResource::CreateScratch(context->getGpu(), SkBudgeted::kYes, TestResource::kB_SimulatedProperty);
453 scratch->setSize(10);
454 TestResource* unique = new TestResource(context->getGpu());
455 unique->setSize(11);
456 unique->resourcePriv().setUniqueKey(uniqueKey);
457 TestResource* wrapped = TestResource::CreateWrapped(context->getGpu());
458 wrapped->setSize(12);
459 TestResource* unbudgeted =
460 new TestResource(context->getGpu(), SkBudgeted::kNo);
461 unbudgeted->setSize(13);
462
463 // Make sure we can't add a unique key to the wrapped resource
464 GrUniqueKey uniqueKey2;
465 make_unique_key<0>(&uniqueKey2, 1);
466 wrapped->resourcePriv().setUniqueKey(uniqueKey2);
467 REPORTER_ASSERT(reporter, nullptr == cache->findAndRefUniqueResource(uniqueKey2));
468
469 // Make sure sizes are as we expect
470 REPORTER_ASSERT(reporter, 4 == cache->getResourceCount());
471 REPORTER_ASSERT(reporter, scratch->gpuMemorySize() + unique->gpuMemorySize() +
472 wrapped->gpuMemorySize() + unbudgeted->gpuMemorySize() ==
473 cache->getResourceBytes());
474 REPORTER_ASSERT(reporter, 2 == cache->getBudgetedResourceCount());
475 REPORTER_ASSERT(reporter, scratch->gpuMemorySize() + unique->gpuMemorySize() ==
476 cache->getBudgetedResourceBytes());
477
478 // Our refs mean that the resources are non purgeable.
479 cache->purgeAllUnlocked();
480 REPORTER_ASSERT(reporter, 4 == cache->getResourceCount());
481 REPORTER_ASSERT(reporter, scratch->gpuMemorySize() + unique->gpuMemorySize() +
482 wrapped->gpuMemorySize() + unbudgeted->gpuMemorySize() ==
483 cache->getResourceBytes());
484 REPORTER_ASSERT(reporter, 2 == cache->getBudgetedResourceCount());
485 REPORTER_ASSERT(reporter, scratch->gpuMemorySize() + unique->gpuMemorySize() ==
486 cache->getBudgetedResourceBytes());
487
488 // Unreffing the wrapped resource should free it right away.
489 wrapped->unref();
490 REPORTER_ASSERT(reporter, 3 == cache->getResourceCount());
491 REPORTER_ASSERT(reporter, scratch->gpuMemorySize() + unique->gpuMemorySize() +
492 unbudgeted->gpuMemorySize() == cache->getResourceBytes());
493
494 // Now try freeing the budgeted resources first
495 wrapped = TestResource::CreateWrapped(context->getGpu());
496 scratch->setSize(12);
497 unique->unref();
498 cache->purgeAllUnlocked();
499 REPORTER_ASSERT(reporter, 3 == cache->getResourceCount());
500 REPORTER_ASSERT(reporter, scratch->gpuMemorySize() + wrapped->gpuMemorySize() +
501 unbudgeted->gpuMemorySize() == cache->getResourceBytes());
502 REPORTER_ASSERT(reporter, 1 == cache->getBudgetedResourceCount());
503 REPORTER_ASSERT(reporter, scratch->gpuMemorySize() == cache->getBudgetedResourceBytes());
504
505 scratch->unref();
506 cache->purgeAllUnlocked();
507 REPORTER_ASSERT(reporter, 2 == cache->getResourceCount());
508 REPORTER_ASSERT(reporter, unbudgeted->gpuMemorySize() + wrapped->gpuMemorySize() ==
509 cache->getResourceBytes());
510 REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceCount());
511 REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceBytes());
512
513 wrapped->unref();
514 REPORTER_ASSERT(reporter, 1 == cache->getResourceCount());
515 REPORTER_ASSERT(reporter, unbudgeted->gpuMemorySize() == cache->getResourceBytes());
516 REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceCount());
517 REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceBytes());
518
519 unbudgeted->unref();
520 REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
521 REPORTER_ASSERT(reporter, 0 == cache->getResourceBytes());
522 REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceCount());
523 REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceBytes());
524 }
525
test_unbudgeted(skiatest::Reporter * reporter)526 static void test_unbudgeted(skiatest::Reporter* reporter) {
527 Mock mock(10, 30000);
528 GrContext* context = mock.context();
529 GrResourceCache* cache = mock.cache();
530
531 GrUniqueKey uniqueKey;
532 make_unique_key<0>(&uniqueKey, 0);
533
534 TestResource* scratch;
535 TestResource* unique;
536 TestResource* wrapped;
537 TestResource* unbudgeted;
538
539 // A large uncached or wrapped resource shouldn't evict anything.
540 scratch = TestResource::CreateScratch(context->getGpu(), SkBudgeted::kYes,
541 TestResource::kB_SimulatedProperty);
542
543 scratch->setSize(10);
544 scratch->unref();
545 REPORTER_ASSERT(reporter, 1 == cache->getResourceCount());
546 REPORTER_ASSERT(reporter, 10 == cache->getResourceBytes());
547 REPORTER_ASSERT(reporter, 1 == cache->getBudgetedResourceCount());
548 REPORTER_ASSERT(reporter, 10 == cache->getBudgetedResourceBytes());
549
550 unique = new TestResource(context->getGpu());
551 unique->setSize(11);
552 unique->resourcePriv().setUniqueKey(uniqueKey);
553 unique->unref();
554 REPORTER_ASSERT(reporter, 2 == cache->getResourceCount());
555 REPORTER_ASSERT(reporter, 21 == cache->getResourceBytes());
556 REPORTER_ASSERT(reporter, 2 == cache->getBudgetedResourceCount());
557 REPORTER_ASSERT(reporter, 21 == cache->getBudgetedResourceBytes());
558
559 size_t large = 2 * cache->getResourceBytes();
560 unbudgeted = new TestResource(context->getGpu(), SkBudgeted::kNo, large);
561 REPORTER_ASSERT(reporter, 3 == cache->getResourceCount());
562 REPORTER_ASSERT(reporter, 21 + large == cache->getResourceBytes());
563 REPORTER_ASSERT(reporter, 2 == cache->getBudgetedResourceCount());
564 REPORTER_ASSERT(reporter, 21 == cache->getBudgetedResourceBytes());
565
566 unbudgeted->unref();
567 REPORTER_ASSERT(reporter, 2 == cache->getResourceCount());
568 REPORTER_ASSERT(reporter, 21 == cache->getResourceBytes());
569 REPORTER_ASSERT(reporter, 2 == cache->getBudgetedResourceCount());
570 REPORTER_ASSERT(reporter, 21 == cache->getBudgetedResourceBytes());
571
572 wrapped = TestResource::CreateWrapped(context->getGpu(), large);
573 REPORTER_ASSERT(reporter, 3 == cache->getResourceCount());
574 REPORTER_ASSERT(reporter, 21 + large == cache->getResourceBytes());
575 REPORTER_ASSERT(reporter, 2 == cache->getBudgetedResourceCount());
576 REPORTER_ASSERT(reporter, 21 == cache->getBudgetedResourceBytes());
577
578 wrapped->unref();
579 REPORTER_ASSERT(reporter, 2 == cache->getResourceCount());
580 REPORTER_ASSERT(reporter, 21 == cache->getResourceBytes());
581 REPORTER_ASSERT(reporter, 2 == cache->getBudgetedResourceCount());
582 REPORTER_ASSERT(reporter, 21 == cache->getBudgetedResourceBytes());
583
584 cache->purgeAllUnlocked();
585 REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
586 REPORTER_ASSERT(reporter, 0 == cache->getResourceBytes());
587 REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceCount());
588 REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceBytes());
589 }
590
591 // This method can't be static because it needs to friended in GrGpuResource::CacheAccess.
592 void test_unbudgeted_to_scratch(skiatest::Reporter* reporter);
test_unbudgeted_to_scratch(skiatest::Reporter * reporter)593 /*static*/ void test_unbudgeted_to_scratch(skiatest::Reporter* reporter) {
594 Mock mock(10, 300);
595 GrContext* context = mock.context();
596 GrResourceCache* cache = mock.cache();
597
598 TestResource* resource =
599 TestResource::CreateScratch(context->getGpu(), SkBudgeted::kNo,
600 TestResource::kA_SimulatedProperty);
601 GrScratchKey key;
602 TestResource::ComputeScratchKey(TestResource::kA_SimulatedProperty, &key);
603
604 size_t size = resource->gpuMemorySize();
605 for (int i = 0; i < 2; ++i) {
606 // Since this resource is unbudgeted, it should not be reachable as scratch.
607 REPORTER_ASSERT(reporter, resource->resourcePriv().getScratchKey() == key);
608 REPORTER_ASSERT(reporter, !resource->cacheAccess().isScratch());
609 REPORTER_ASSERT(reporter, SkBudgeted::kNo == resource->resourcePriv().isBudgeted());
610 REPORTER_ASSERT(reporter, nullptr == cache->findAndRefScratchResource(key, TestResource::kDefaultSize, 0));
611 REPORTER_ASSERT(reporter, 1 == cache->getResourceCount());
612 REPORTER_ASSERT(reporter, size == cache->getResourceBytes());
613 REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceCount());
614 REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceBytes());
615
616 // Once it is unrefed, it should become available as scratch.
617 resource->unref();
618 REPORTER_ASSERT(reporter, 1 == cache->getResourceCount());
619 REPORTER_ASSERT(reporter, size == cache->getResourceBytes());
620 REPORTER_ASSERT(reporter, 1 == cache->getBudgetedResourceCount());
621 REPORTER_ASSERT(reporter, size == cache->getBudgetedResourceBytes());
622 resource = static_cast<TestResource*>(cache->findAndRefScratchResource(key, TestResource::kDefaultSize, 0));
623 REPORTER_ASSERT(reporter, resource);
624 REPORTER_ASSERT(reporter, resource->resourcePriv().getScratchKey() == key);
625 REPORTER_ASSERT(reporter, resource->cacheAccess().isScratch());
626 REPORTER_ASSERT(reporter, SkBudgeted::kYes == resource->resourcePriv().isBudgeted());
627
628 if (0 == i) {
629 // If made unbudgeted, it should return to original state: ref'ed and unbudgeted. Try
630 // the above tests again.
631 resource->resourcePriv().makeUnbudgeted();
632 } else {
633 // After the second time around, try removing the scratch key
634 resource->resourcePriv().removeScratchKey();
635 REPORTER_ASSERT(reporter, 1 == cache->getResourceCount());
636 REPORTER_ASSERT(reporter, size == cache->getResourceBytes());
637 REPORTER_ASSERT(reporter, 1 == cache->getBudgetedResourceCount());
638 REPORTER_ASSERT(reporter, size == cache->getBudgetedResourceBytes());
639 REPORTER_ASSERT(reporter, !resource->resourcePriv().getScratchKey().isValid());
640 REPORTER_ASSERT(reporter, !resource->cacheAccess().isScratch());
641 REPORTER_ASSERT(reporter, SkBudgeted::kYes == resource->resourcePriv().isBudgeted());
642
643 // now when it is unrefed it should die since it has no key.
644 resource->unref();
645 REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
646 REPORTER_ASSERT(reporter, 0 == cache->getResourceBytes());
647 REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceCount());
648 REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceBytes());
649 }
650 }
651 }
652
test_duplicate_scratch_key(skiatest::Reporter * reporter)653 static void test_duplicate_scratch_key(skiatest::Reporter* reporter) {
654 Mock mock(5, 30000);
655 GrContext* context = mock.context();
656 GrResourceCache* cache = mock.cache();
657
658 // Create two resources that have the same scratch key.
659 TestResource* a = TestResource::CreateScratch(context->getGpu(),
660 SkBudgeted::kYes,
661 TestResource::kB_SimulatedProperty);
662 TestResource* b = TestResource::CreateScratch(context->getGpu(),
663 SkBudgeted::kYes,
664 TestResource::kB_SimulatedProperty);
665 a->setSize(11);
666 b->setSize(12);
667 GrScratchKey scratchKey1;
668 TestResource::ComputeScratchKey(TestResource::kA_SimulatedProperty, &scratchKey1);
669 // Check for negative case consistency. (leaks upon test failure.)
670 REPORTER_ASSERT(reporter, nullptr == cache->findAndRefScratchResource(scratchKey1, TestResource::kDefaultSize, 0));
671
672 GrScratchKey scratchKey;
673 TestResource::ComputeScratchKey(TestResource::kB_SimulatedProperty, &scratchKey);
674
675 // Scratch resources are registered with GrResourceCache just by existing. There are 2.
676 REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive());
677 SkDEBUGCODE(REPORTER_ASSERT(reporter, 2 == cache->countScratchEntriesForKey(scratchKey));)
678 REPORTER_ASSERT(reporter, 2 == cache->getResourceCount());
679 REPORTER_ASSERT(reporter, a->gpuMemorySize() + b->gpuMemorySize() ==
680 cache->getResourceBytes());
681
682 // Our refs mean that the resources are non purgeable.
683 cache->purgeAllUnlocked();
684 REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive());
685 REPORTER_ASSERT(reporter, 2 == cache->getResourceCount());
686
687 // Unref but don't purge
688 a->unref();
689 b->unref();
690 REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive());
691 SkDEBUGCODE(REPORTER_ASSERT(reporter, 2 == cache->countScratchEntriesForKey(scratchKey));)
692
693 // Purge again. This time resources should be purgeable.
694 cache->purgeAllUnlocked();
695 REPORTER_ASSERT(reporter, 0 == TestResource::NumAlive());
696 REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
697 SkDEBUGCODE(REPORTER_ASSERT(reporter, 0 == cache->countScratchEntriesForKey(scratchKey));)
698 }
699
test_remove_scratch_key(skiatest::Reporter * reporter)700 static void test_remove_scratch_key(skiatest::Reporter* reporter) {
701 Mock mock(5, 30000);
702 GrContext* context = mock.context();
703 GrResourceCache* cache = mock.cache();
704
705 // Create two resources that have the same scratch key.
706 TestResource* a = TestResource::CreateScratch(context->getGpu(), SkBudgeted::kYes,
707 TestResource::kB_SimulatedProperty);
708 TestResource* b = TestResource::CreateScratch(context->getGpu(), SkBudgeted::kYes,
709 TestResource::kB_SimulatedProperty);
710 a->unref();
711 b->unref();
712
713 GrScratchKey scratchKey;
714 // Ensure that scratch key lookup is correct for negative case.
715 TestResource::ComputeScratchKey(TestResource::kA_SimulatedProperty, &scratchKey);
716 // (following leaks upon test failure).
717 REPORTER_ASSERT(reporter, cache->findAndRefScratchResource(scratchKey, TestResource::kDefaultSize, 0) == nullptr);
718
719 // Scratch resources are registered with GrResourceCache just by existing. There are 2.
720 TestResource::ComputeScratchKey(TestResource::kB_SimulatedProperty, &scratchKey);
721 REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive());
722 SkDEBUGCODE(REPORTER_ASSERT(reporter, 2 == cache->countScratchEntriesForKey(scratchKey));)
723 REPORTER_ASSERT(reporter, 2 == cache->getResourceCount());
724
725 // Find the first resource and remove its scratch key
726 GrGpuResource* find;
727 find = cache->findAndRefScratchResource(scratchKey, TestResource::kDefaultSize, 0);
728 find->resourcePriv().removeScratchKey();
729 // It's still alive, but not cached by scratch key anymore
730 REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive());
731 SkDEBUGCODE(REPORTER_ASSERT(reporter, 1 == cache->countScratchEntriesForKey(scratchKey));)
732 REPORTER_ASSERT(reporter, 2 == cache->getResourceCount());
733
734 // The cache should immediately delete it when it's unrefed since it isn't accessible.
735 find->unref();
736 REPORTER_ASSERT(reporter, 1 == TestResource::NumAlive());
737 SkDEBUGCODE(REPORTER_ASSERT(reporter, 1 == cache->countScratchEntriesForKey(scratchKey));)
738 REPORTER_ASSERT(reporter, 1 == cache->getResourceCount());
739
740 // Repeat for the second resource.
741 find = cache->findAndRefScratchResource(scratchKey, TestResource::kDefaultSize, 0);
742 find->resourcePriv().removeScratchKey();
743 REPORTER_ASSERT(reporter, 1 == TestResource::NumAlive());
744 SkDEBUGCODE(REPORTER_ASSERT(reporter, 0 == cache->countScratchEntriesForKey(scratchKey));)
745 REPORTER_ASSERT(reporter, 1 == cache->getResourceCount());
746
747 // Should be able to call this multiple times with no problem.
748 find->resourcePriv().removeScratchKey();
749 REPORTER_ASSERT(reporter, 1 == TestResource::NumAlive());
750 SkDEBUGCODE(REPORTER_ASSERT(reporter, 0 == cache->countScratchEntriesForKey(scratchKey));)
751 REPORTER_ASSERT(reporter, 1 == cache->getResourceCount());
752
753 find->unref();
754 REPORTER_ASSERT(reporter, 0 == TestResource::NumAlive());
755 SkDEBUGCODE(REPORTER_ASSERT(reporter, 0 == cache->countScratchEntriesForKey(scratchKey));)
756 REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
757 }
758
test_scratch_key_consistency(skiatest::Reporter * reporter)759 static void test_scratch_key_consistency(skiatest::Reporter* reporter) {
760 Mock mock(5, 30000);
761 GrContext* context = mock.context();
762 GrResourceCache* cache = mock.cache();
763
764 // Create two resources that have the same scratch key.
765 TestResource* a = TestResource::CreateScratch(context->getGpu(), SkBudgeted::kYes,
766 TestResource::kB_SimulatedProperty);
767 TestResource* b = TestResource::CreateScratch(context->getGpu(), SkBudgeted::kYes,
768 TestResource::kB_SimulatedProperty);
769 a->unref();
770 b->unref();
771
772 GrScratchKey scratchKey;
773 // Ensure that scratch key comparison and assignment is consistent.
774 GrScratchKey scratchKey1;
775 TestResource::ComputeScratchKey(TestResource::kA_SimulatedProperty, &scratchKey1);
776 GrScratchKey scratchKey2;
777 TestResource::ComputeScratchKey(TestResource::kB_SimulatedProperty, &scratchKey2);
778 REPORTER_ASSERT(reporter, scratchKey1.size() == TestResource::ExpectedScratchKeySize());
779 REPORTER_ASSERT(reporter, scratchKey1 != scratchKey2);
780 REPORTER_ASSERT(reporter, scratchKey2 != scratchKey1);
781 scratchKey = scratchKey1;
782 REPORTER_ASSERT(reporter, scratchKey.size() == TestResource::ExpectedScratchKeySize());
783 REPORTER_ASSERT(reporter, scratchKey1 == scratchKey);
784 REPORTER_ASSERT(reporter, scratchKey == scratchKey1);
785 REPORTER_ASSERT(reporter, scratchKey2 != scratchKey);
786 REPORTER_ASSERT(reporter, scratchKey != scratchKey2);
787 scratchKey = scratchKey2;
788 REPORTER_ASSERT(reporter, scratchKey.size() == TestResource::ExpectedScratchKeySize());
789 REPORTER_ASSERT(reporter, scratchKey1 != scratchKey);
790 REPORTER_ASSERT(reporter, scratchKey != scratchKey1);
791 REPORTER_ASSERT(reporter, scratchKey2 == scratchKey);
792 REPORTER_ASSERT(reporter, scratchKey == scratchKey2);
793
794 // Ensure that scratch key lookup is correct for negative case.
795 TestResource::ComputeScratchKey(TestResource::kA_SimulatedProperty, &scratchKey);
796 // (following leaks upon test failure).
797 REPORTER_ASSERT(reporter, cache->findAndRefScratchResource(scratchKey, TestResource::kDefaultSize, 0) == nullptr);
798
799 // Find the first resource with a scratch key and a copy of a scratch key.
800 TestResource::ComputeScratchKey(TestResource::kB_SimulatedProperty, &scratchKey);
801 GrGpuResource* find = cache->findAndRefScratchResource(scratchKey, TestResource::kDefaultSize, 0);
802 REPORTER_ASSERT(reporter, find != nullptr);
803 find->unref();
804
805 scratchKey2 = scratchKey;
806 find = cache->findAndRefScratchResource(scratchKey2, TestResource::kDefaultSize, 0);
807 REPORTER_ASSERT(reporter, find != nullptr);
808 REPORTER_ASSERT(reporter, find == a || find == b);
809
810 GrGpuResource* find2 = cache->findAndRefScratchResource(scratchKey2, TestResource::kDefaultSize, 0);
811 REPORTER_ASSERT(reporter, find2 != nullptr);
812 REPORTER_ASSERT(reporter, find2 == a || find2 == b);
813 REPORTER_ASSERT(reporter, find2 != find);
814 find2->unref();
815 find->unref();
816 }
817
test_duplicate_unique_key(skiatest::Reporter * reporter)818 static void test_duplicate_unique_key(skiatest::Reporter* reporter) {
819 Mock mock(5, 30000);
820 GrContext* context = mock.context();
821 GrResourceCache* cache = mock.cache();
822
823 GrUniqueKey key;
824 make_unique_key<0>(&key, 0);
825
826 // Create two resources that we will attempt to register with the same unique key.
827 TestResource* a = new TestResource(context->getGpu());
828 a->setSize(11);
829
830 // Set key on resource a.
831 a->resourcePriv().setUniqueKey(key);
832 REPORTER_ASSERT(reporter, a == cache->findAndRefUniqueResource(key));
833 a->unref();
834
835 // Make sure that redundantly setting a's key works.
836 a->resourcePriv().setUniqueKey(key);
837 REPORTER_ASSERT(reporter, a == cache->findAndRefUniqueResource(key));
838 a->unref();
839 REPORTER_ASSERT(reporter, 1 == cache->getResourceCount());
840 REPORTER_ASSERT(reporter, a->gpuMemorySize() == cache->getResourceBytes());
841 REPORTER_ASSERT(reporter, 1 == TestResource::NumAlive());
842
843 // Create resource b and set the same key. It should replace a's unique key cache entry.
844 TestResource* b = new TestResource(context->getGpu());
845 b->setSize(12);
846 b->resourcePriv().setUniqueKey(key);
847 REPORTER_ASSERT(reporter, b == cache->findAndRefUniqueResource(key));
848 b->unref();
849
850 // Still have two resources because a is still reffed.
851 REPORTER_ASSERT(reporter, 2 == cache->getResourceCount());
852 REPORTER_ASSERT(reporter, a->gpuMemorySize() + b->gpuMemorySize() == cache->getResourceBytes());
853 REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive());
854
855 a->unref();
856 // Now a should be gone.
857 REPORTER_ASSERT(reporter, 1 == cache->getResourceCount());
858 REPORTER_ASSERT(reporter, b->gpuMemorySize() == cache->getResourceBytes());
859 REPORTER_ASSERT(reporter, 1 == TestResource::NumAlive());
860
861 // Now replace b with c, but make sure c can start with one unique key and change it to b's key.
862 // Also make b be unreffed when replacement occurs.
863 b->unref();
864 TestResource* c = new TestResource(context->getGpu());
865 GrUniqueKey differentKey;
866 make_unique_key<0>(&differentKey, 1);
867 c->setSize(13);
868 c->resourcePriv().setUniqueKey(differentKey);
869 REPORTER_ASSERT(reporter, 2 == cache->getResourceCount());
870 REPORTER_ASSERT(reporter, b->gpuMemorySize() + c->gpuMemorySize() == cache->getResourceBytes());
871 REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive());
872 // c replaces b and b should be immediately purged.
873 c->resourcePriv().setUniqueKey(key);
874 REPORTER_ASSERT(reporter, 1 == cache->getResourceCount());
875 REPORTER_ASSERT(reporter, c->gpuMemorySize() == cache->getResourceBytes());
876 REPORTER_ASSERT(reporter, 1 == TestResource::NumAlive());
877
878 // c shouldn't be purged because it is ref'ed.
879 cache->purgeAllUnlocked();
880 REPORTER_ASSERT(reporter, 1 == cache->getResourceCount());
881 REPORTER_ASSERT(reporter, c->gpuMemorySize() == cache->getResourceBytes());
882 REPORTER_ASSERT(reporter, 1 == TestResource::NumAlive());
883
884 // Drop the ref on c, it should be kept alive because it has a unique key.
885 c->unref();
886 REPORTER_ASSERT(reporter, 1 == cache->getResourceCount());
887 REPORTER_ASSERT(reporter, c->gpuMemorySize() == cache->getResourceBytes());
888 REPORTER_ASSERT(reporter, 1 == TestResource::NumAlive());
889
890 // Verify that we can find c, then remove its unique key. It should get purged immediately.
891 REPORTER_ASSERT(reporter, c == cache->findAndRefUniqueResource(key));
892 c->resourcePriv().removeUniqueKey();
893 c->unref();
894 REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
895 REPORTER_ASSERT(reporter, 0 == cache->getResourceBytes());
896 REPORTER_ASSERT(reporter, 0 == TestResource::NumAlive());
897
898 {
899 GrUniqueKey key2;
900 make_unique_key<0>(&key2, 0);
901 sk_sp<TestResource> d(new TestResource(context->getGpu()));
902 int foo = 4132;
903 key2.setCustomData(SkData::MakeWithCopy(&foo, sizeof(foo)));
904 d->resourcePriv().setUniqueKey(key2);
905 }
906
907 GrUniqueKey key3;
908 make_unique_key<0>(&key3, 0);
909 sk_sp<GrGpuResource> d2(cache->findAndRefUniqueResource(key3));
910 REPORTER_ASSERT(reporter, *(int*) d2->getUniqueKey().getCustomData()->data() == 4132);
911 }
912
test_purge_invalidated(skiatest::Reporter * reporter)913 static void test_purge_invalidated(skiatest::Reporter* reporter) {
914 Mock mock(5, 30000);
915 GrContext* context = mock.context();
916 GrResourceCache* cache = mock.cache();
917
918 GrUniqueKey key1, key2, key3;
919 make_unique_key<0>(&key1, 1);
920 make_unique_key<0>(&key2, 2);
921 make_unique_key<0>(&key3, 3);
922
923 // Add three resources to the cache. Only c is usable as scratch.
924 TestResource* a = new TestResource(context->getGpu());
925 TestResource* b = new TestResource(context->getGpu());
926 TestResource* c = TestResource::CreateScratch(context->getGpu(), SkBudgeted::kYes,
927 TestResource::kA_SimulatedProperty);
928 a->resourcePriv().setUniqueKey(key1);
929 b->resourcePriv().setUniqueKey(key2);
930 c->resourcePriv().setUniqueKey(key3);
931 a->unref();
932 // hold b until *after* the message is sent.
933 c->unref();
934
935 REPORTER_ASSERT(reporter, cache->hasUniqueKey(key1));
936 REPORTER_ASSERT(reporter, cache->hasUniqueKey(key2));
937 REPORTER_ASSERT(reporter, cache->hasUniqueKey(key3));
938 REPORTER_ASSERT(reporter, 3 == TestResource::NumAlive());
939
940 typedef GrUniqueKeyInvalidatedMessage Msg;
941 typedef SkMessageBus<GrUniqueKeyInvalidatedMessage> Bus;
942
943 // Invalidate two of the three, they should be purged and no longer accessible via their keys.
944 Bus::Post(Msg(key1));
945 Bus::Post(Msg(key2));
946 cache->purgeAsNeeded();
947 // a should be deleted now, but we still have a ref on b.
948 REPORTER_ASSERT(reporter, !cache->hasUniqueKey(key1));
949 REPORTER_ASSERT(reporter, !cache->hasUniqueKey(key2));
950 REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive());
951 REPORTER_ASSERT(reporter, cache->hasUniqueKey(key3));
952
953 // Invalidate the third.
954 Bus::Post(Msg(key3));
955 cache->purgeAsNeeded();
956 // we still have a ref on b, c should be recycled as scratch.
957 REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive());
958 REPORTER_ASSERT(reporter, !cache->hasUniqueKey(key3));
959
960 // make b purgeable. It should be immediately deleted since it has no key.
961 b->unref();
962 REPORTER_ASSERT(reporter, 1 == TestResource::NumAlive());
963
964 // Make sure we actually get to c via it's scratch key, before we say goodbye.
965 GrScratchKey scratchKey;
966 TestResource::ComputeScratchKey(TestResource::kA_SimulatedProperty, &scratchKey);
967 GrGpuResource* scratch = cache->findAndRefScratchResource(scratchKey, TestResource::kDefaultSize, 0);
968 REPORTER_ASSERT(reporter, scratch == c);
969 SkSafeUnref(scratch);
970
971 // Get rid of c.
972 cache->purgeAllUnlocked();
973 scratch = cache->findAndRefScratchResource(scratchKey, TestResource::kDefaultSize, 0);
974 REPORTER_ASSERT(reporter, 0 == TestResource::NumAlive());
975 REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
976 REPORTER_ASSERT(reporter, 0 == cache->getResourceBytes());
977 REPORTER_ASSERT(reporter, !scratch);
978 SkSafeUnref(scratch);
979 }
980
test_cache_chained_purge(skiatest::Reporter * reporter)981 static void test_cache_chained_purge(skiatest::Reporter* reporter) {
982 Mock mock(3, 30000);
983 GrContext* context = mock.context();
984 GrResourceCache* cache = mock.cache();
985
986 GrUniqueKey key1, key2;
987 make_unique_key<0>(&key1, 1);
988 make_unique_key<0>(&key2, 2);
989
990 TestResource* a = new TestResource(context->getGpu());
991 TestResource* b = new TestResource(context->getGpu());
992 a->resourcePriv().setUniqueKey(key1);
993 b->resourcePriv().setUniqueKey(key2);
994
995 // Make a cycle
996 a->setUnrefWhenDestroyed(b);
997 b->setUnrefWhenDestroyed(a);
998
999 REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive());
1000
1001 a->unref();
1002 b->unref();
1003
1004 REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive());
1005
1006 cache->purgeAllUnlocked();
1007 REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive());
1008
1009 // Break the cycle
1010 a->setUnrefWhenDestroyed(nullptr);
1011 REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive());
1012
1013 cache->purgeAllUnlocked();
1014 REPORTER_ASSERT(reporter, 0 == TestResource::NumAlive());
1015 }
1016
test_resource_size_changed(skiatest::Reporter * reporter)1017 static void test_resource_size_changed(skiatest::Reporter* reporter) {
1018 GrUniqueKey key1, key2;
1019 make_unique_key<0>(&key1, 1);
1020 make_unique_key<0>(&key2, 2);
1021
1022 // Test changing resources sizes (both increase & decrease).
1023 {
1024 Mock mock(3, 30000);
1025 GrContext* context = mock.context();
1026 GrResourceCache* cache = mock.cache();
1027
1028 TestResource* a = new TestResource(context->getGpu());
1029 a->resourcePriv().setUniqueKey(key1);
1030 a->unref();
1031
1032 TestResource* b = new TestResource(context->getGpu());
1033 b->resourcePriv().setUniqueKey(key2);
1034 b->unref();
1035
1036 REPORTER_ASSERT(reporter, 200 == cache->getResourceBytes());
1037 REPORTER_ASSERT(reporter, 2 == cache->getResourceCount());
1038 {
1039 sk_sp<TestResource> find2(
1040 static_cast<TestResource*>(cache->findAndRefUniqueResource(key2)));
1041 find2->setSize(200);
1042 sk_sp<TestResource> find1(
1043 static_cast<TestResource*>(cache->findAndRefUniqueResource(key1)));
1044 find1->setSize(50);
1045 }
1046
1047 REPORTER_ASSERT(reporter, 250 == cache->getResourceBytes());
1048 REPORTER_ASSERT(reporter, 2 == cache->getResourceCount());
1049 }
1050
1051 // Test increasing a resources size beyond the cache budget.
1052 {
1053 Mock mock(2, 300);
1054 GrContext* context = mock.context();
1055 GrResourceCache* cache = mock.cache();
1056
1057 TestResource* a = new TestResource(context->getGpu());
1058 a->setSize(100);
1059 a->resourcePriv().setUniqueKey(key1);
1060 a->unref();
1061
1062 TestResource* b = new TestResource(context->getGpu());
1063 b->setSize(100);
1064 b->resourcePriv().setUniqueKey(key2);
1065 b->unref();
1066
1067 REPORTER_ASSERT(reporter, 200 == cache->getResourceBytes());
1068 REPORTER_ASSERT(reporter, 2 == cache->getResourceCount());
1069
1070 {
1071 sk_sp<TestResource> find2(static_cast<TestResource*>(
1072 cache->findAndRefUniqueResource(key2)));
1073 find2->setSize(201);
1074 }
1075 REPORTER_ASSERT(reporter, !cache->hasUniqueKey(key1));
1076
1077 REPORTER_ASSERT(reporter, 201 == cache->getResourceBytes());
1078 REPORTER_ASSERT(reporter, 1 == cache->getResourceCount());
1079 }
1080 }
1081
test_timestamp_wrap(skiatest::Reporter * reporter)1082 static void test_timestamp_wrap(skiatest::Reporter* reporter) {
1083 static const int kCount = 50;
1084 static const int kBudgetCnt = kCount / 2;
1085 static const int kLockedFreq = 8;
1086 static const int kBudgetSize = 0x80000000;
1087
1088 SkRandom random;
1089
1090 // Run the test 2*kCount times;
1091 for (int i = 0; i < 2 * kCount; ++i ) {
1092 Mock mock(kBudgetCnt, kBudgetSize);
1093 GrContext* context = mock.context();
1094 GrResourceCache* cache = mock.cache();
1095
1096 // Pick a random number of resources to add before the timestamp will wrap.
1097 cache->changeTimestamp(SK_MaxU32 - random.nextULessThan(kCount + 1));
1098
1099 static const int kNumToPurge = kCount - kBudgetCnt;
1100
1101 SkTDArray<int> shouldPurgeIdxs;
1102 int purgeableCnt = 0;
1103 SkTDArray<GrGpuResource*> resourcesToUnref;
1104
1105 // Add kCount resources, holding onto resources at random so we have a mix of purgeable and
1106 // unpurgeable resources.
1107 for (int j = 0; j < kCount; ++j) {
1108 GrUniqueKey key;
1109 make_unique_key<0>(&key, j);
1110
1111 TestResource* r = new TestResource(context->getGpu());
1112 r->resourcePriv().setUniqueKey(key);
1113 if (random.nextU() % kLockedFreq) {
1114 // Make this is purgeable.
1115 r->unref();
1116 ++purgeableCnt;
1117 if (purgeableCnt <= kNumToPurge) {
1118 *shouldPurgeIdxs.append() = j;
1119 }
1120 } else {
1121 *resourcesToUnref.append() = r;
1122 }
1123 }
1124
1125 // Verify that the correct resources were purged.
1126 int currShouldPurgeIdx = 0;
1127 for (int j = 0; j < kCount; ++j) {
1128 GrUniqueKey key;
1129 make_unique_key<0>(&key, j);
1130 GrGpuResource* res = cache->findAndRefUniqueResource(key);
1131 if (currShouldPurgeIdx < shouldPurgeIdxs.count() &&
1132 shouldPurgeIdxs[currShouldPurgeIdx] == j) {
1133 ++currShouldPurgeIdx;
1134 REPORTER_ASSERT(reporter, nullptr == res);
1135 } else {
1136 REPORTER_ASSERT(reporter, nullptr != res);
1137 }
1138 SkSafeUnref(res);
1139 }
1140
1141 for (int j = 0; j < resourcesToUnref.count(); ++j) {
1142 resourcesToUnref[j]->unref();
1143 }
1144 }
1145 }
1146
test_flush(skiatest::Reporter * reporter)1147 static void test_flush(skiatest::Reporter* reporter) {
1148 Mock mock(1000000, 1000000);
1149 GrContext* context = mock.context();
1150 GrResourceCache* cache = mock.cache();
1151
1152 // The current cache impl will round the max flush count to the next power of 2. So we choose a
1153 // power of two here to keep things simpler.
1154 static const int kFlushCount = 16;
1155 cache->setLimits(1000000, 1000000, kFlushCount);
1156
1157 {
1158 // Insert a resource and send a flush notification kFlushCount times.
1159 for (int i = 0; i < kFlushCount; ++i) {
1160 TestResource* r = new TestResource(context->getGpu());
1161 GrUniqueKey k;
1162 make_unique_key<1>(&k, i);
1163 r->resourcePriv().setUniqueKey(k);
1164 r->unref();
1165 cache->notifyFlushOccurred(GrResourceCache::kExternal);
1166 }
1167
1168 // Send flush notifications to the cache. Each flush should purge the oldest resource.
1169 for (int i = 0; i < kFlushCount; ++i) {
1170 cache->notifyFlushOccurred(GrResourceCache::kExternal);
1171 REPORTER_ASSERT(reporter, kFlushCount - i - 1 == cache->getResourceCount());
1172 for (int j = 0; j < i; ++j) {
1173 GrUniqueKey k;
1174 make_unique_key<1>(&k, j);
1175 GrGpuResource* r = cache->findAndRefUniqueResource(k);
1176 REPORTER_ASSERT(reporter, !SkToBool(r));
1177 SkSafeUnref(r);
1178 }
1179 }
1180
1181 REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
1182 cache->purgeAllUnlocked();
1183 }
1184
1185 // Do a similar test but where we leave refs on some resources to prevent them from being
1186 // purged.
1187 {
1188 GrGpuResource* refedResources[kFlushCount >> 1];
1189 for (int i = 0; i < kFlushCount; ++i) {
1190 TestResource* r = new TestResource(context->getGpu());
1191 GrUniqueKey k;
1192 make_unique_key<1>(&k, i);
1193 r->resourcePriv().setUniqueKey(k);
1194 // Leave a ref on every other resource, beginning with the first.
1195 if (SkToBool(i & 0x1)) {
1196 refedResources[i/2] = r;
1197 } else {
1198 r->unref();
1199 }
1200 cache->notifyFlushOccurred(GrResourceCache::kExternal);
1201 }
1202
1203 for (int i = 0; i < kFlushCount; ++i) {
1204 // Should get a resource purged every other flush.
1205 cache->notifyFlushOccurred(GrResourceCache::kExternal);
1206 REPORTER_ASSERT(reporter, kFlushCount - i/2 - 1 == cache->getResourceCount());
1207 }
1208
1209 // Unref all the resources that we kept refs on in the first loop.
1210 for (int i = 0; i < kFlushCount >> 1; ++i) {
1211 refedResources[i]->unref();
1212 }
1213
1214 // After kFlushCount + 1 flushes they all will have sat in the purgeable queue for
1215 // kFlushCount full flushes.
1216 for (int i = 0; i < kFlushCount + 1; ++i) {
1217 REPORTER_ASSERT(reporter, kFlushCount >> 1 == cache->getResourceCount());
1218 cache->notifyFlushOccurred(GrResourceCache::kExternal);
1219 }
1220 REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
1221
1222 cache->purgeAllUnlocked();
1223 }
1224
1225 REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
1226
1227 // Verify that calling flush() on a GrContext with nothing to do will not trigger resource
1228 // eviction.
1229 context->flush();
1230 for (int i = 0; i < 10; ++i) {
1231 TestResource* r = new TestResource(context->getGpu());
1232 GrUniqueKey k;
1233 make_unique_key<1>(&k, i);
1234 r->resourcePriv().setUniqueKey(k);
1235 r->unref();
1236 }
1237 REPORTER_ASSERT(reporter, 10 == cache->getResourceCount());
1238 for (int i = 0; i < 10 * kFlushCount; ++i) {
1239 context->flush();
1240 }
1241 REPORTER_ASSERT(reporter, 10 == cache->getResourceCount());
1242 }
1243
test_time_purge(skiatest::Reporter * reporter)1244 static void test_time_purge(skiatest::Reporter* reporter) {
1245 Mock mock(1000000, 1000000);
1246 GrContext* context = mock.context();
1247 GrResourceCache* cache = mock.cache();
1248
1249 static constexpr int kCnts[] = {1, 10, 1024};
1250 auto nowish = []() {
1251 // We sleep so that we ensure we get a value that is greater than the last call to
1252 // GrStdSteadyClock::now().
1253 std::this_thread::sleep_for(GrStdSteadyClock::duration(5));
1254 auto result = GrStdSteadyClock::now();
1255 // Also sleep afterwards so we don't get this value again.
1256 std::this_thread::sleep_for(GrStdSteadyClock::duration(5));
1257 return result;
1258 };
1259
1260 for (int cnt : kCnts) {
1261 std::unique_ptr<GrStdSteadyClock::time_point[]> timeStamps(
1262 new GrStdSteadyClock::time_point[cnt]);
1263 {
1264 // Insert resources and get time points between each addition.
1265 for (int i = 0; i < cnt; ++i) {
1266 TestResource* r = new TestResource(context->getGpu());
1267 GrUniqueKey k;
1268 make_unique_key<1>(&k, i);
1269 r->resourcePriv().setUniqueKey(k);
1270 r->unref();
1271 timeStamps.get()[i] = nowish();
1272 }
1273
1274 // Purge based on the time points between resource additions. Each purge should remove
1275 // the oldest resource.
1276 for (int i = 0; i < cnt; ++i) {
1277 cache->purgeResourcesNotUsedSince(timeStamps[i]);
1278 REPORTER_ASSERT(reporter, cnt - i - 1 == cache->getResourceCount());
1279 for (int j = 0; j < i; ++j) {
1280 GrUniqueKey k;
1281 make_unique_key<1>(&k, j);
1282 GrGpuResource* r = cache->findAndRefUniqueResource(k);
1283 REPORTER_ASSERT(reporter, !SkToBool(r));
1284 SkSafeUnref(r);
1285 }
1286 }
1287
1288 REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
1289 cache->purgeAllUnlocked();
1290 }
1291
1292 // Do a similar test but where we leave refs on some resources to prevent them from being
1293 // purged.
1294 {
1295 std::unique_ptr<GrGpuResource* []> refedResources(new GrGpuResource*[cnt / 2]);
1296 for (int i = 0; i < cnt; ++i) {
1297 TestResource* r = new TestResource(context->getGpu());
1298 GrUniqueKey k;
1299 make_unique_key<1>(&k, i);
1300 r->resourcePriv().setUniqueKey(k);
1301 // Leave a ref on every other resource, beginning with the first.
1302 if (SkToBool(i & 0x1)) {
1303 refedResources.get()[i / 2] = r;
1304 } else {
1305 r->unref();
1306 }
1307 timeStamps.get()[i] = nowish();
1308 }
1309
1310 for (int i = 0; i < cnt; ++i) {
1311 // Should get a resource purged every other frame.
1312 cache->purgeResourcesNotUsedSince(timeStamps[i]);
1313 REPORTER_ASSERT(reporter, cnt - i / 2 - 1 == cache->getResourceCount());
1314 }
1315
1316 // Unref all the resources that we kept refs on in the first loop.
1317 for (int i = 0; i < (cnt / 2); ++i) {
1318 refedResources.get()[i]->unref();
1319 cache->purgeResourcesNotUsedSince(nowish());
1320 REPORTER_ASSERT(reporter, cnt / 2 - i - 1 == cache->getResourceCount());
1321 }
1322
1323 cache->purgeAllUnlocked();
1324 }
1325
1326 REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
1327
1328 // Verify that calling flush() on a GrContext with nothing to do will not trigger resource
1329 // eviction
1330 context->flush();
1331 for (int i = 0; i < 10; ++i) {
1332 TestResource* r = new TestResource(context->getGpu());
1333 GrUniqueKey k;
1334 make_unique_key<1>(&k, i);
1335 r->resourcePriv().setUniqueKey(k);
1336 r->unref();
1337 }
1338 REPORTER_ASSERT(reporter, 10 == cache->getResourceCount());
1339 context->flush();
1340 REPORTER_ASSERT(reporter, 10 == cache->getResourceCount());
1341 cache->purgeResourcesNotUsedSince(nowish());
1342 REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
1343 }
1344 }
1345
test_large_resource_count(skiatest::Reporter * reporter)1346 static void test_large_resource_count(skiatest::Reporter* reporter) {
1347 // Set the cache size to double the resource count because we're going to create 2x that number
1348 // resources, using two different key domains. Add a little slop to the bytes because we resize
1349 // down to 1 byte after creating the resource.
1350 static const int kResourceCnt = 2000;
1351
1352 Mock mock(2 * kResourceCnt, 2 * kResourceCnt + 1000);
1353 GrContext* context = mock.context();
1354 GrResourceCache* cache = mock.cache();
1355
1356 for (int i = 0; i < kResourceCnt; ++i) {
1357 GrUniqueKey key1, key2;
1358 make_unique_key<1>(&key1, i);
1359 make_unique_key<2>(&key2, i);
1360
1361 TestResource* resource;
1362
1363 resource = new TestResource(context->getGpu());
1364 resource->resourcePriv().setUniqueKey(key1);
1365 resource->setSize(1);
1366 resource->unref();
1367
1368 resource = new TestResource(context->getGpu());
1369 resource->resourcePriv().setUniqueKey(key2);
1370 resource->setSize(1);
1371 resource->unref();
1372 }
1373
1374 REPORTER_ASSERT(reporter, TestResource::NumAlive() == 2 * kResourceCnt);
1375 REPORTER_ASSERT(reporter, cache->getBudgetedResourceBytes() == 2 * kResourceCnt);
1376 REPORTER_ASSERT(reporter, cache->getBudgetedResourceCount() == 2 * kResourceCnt);
1377 REPORTER_ASSERT(reporter, cache->getResourceBytes() == 2 * kResourceCnt);
1378 REPORTER_ASSERT(reporter, cache->getResourceCount() == 2 * kResourceCnt);
1379 for (int i = 0; i < kResourceCnt; ++i) {
1380 GrUniqueKey key1, key2;
1381 make_unique_key<1>(&key1, i);
1382 make_unique_key<2>(&key2, i);
1383
1384 REPORTER_ASSERT(reporter, cache->hasUniqueKey(key1));
1385 REPORTER_ASSERT(reporter, cache->hasUniqueKey(key2));
1386 }
1387
1388 cache->purgeAllUnlocked();
1389 REPORTER_ASSERT(reporter, TestResource::NumAlive() == 0);
1390 REPORTER_ASSERT(reporter, cache->getBudgetedResourceBytes() == 0);
1391 REPORTER_ASSERT(reporter, cache->getBudgetedResourceCount() == 0);
1392 REPORTER_ASSERT(reporter, cache->getResourceBytes() == 0);
1393 REPORTER_ASSERT(reporter, cache->getResourceCount() == 0);
1394
1395 for (int i = 0; i < kResourceCnt; ++i) {
1396 GrUniqueKey key1, key2;
1397 make_unique_key<1>(&key1, i);
1398 make_unique_key<2>(&key2, i);
1399
1400 REPORTER_ASSERT(reporter, !cache->hasUniqueKey(key1));
1401 REPORTER_ASSERT(reporter, !cache->hasUniqueKey(key2));
1402 }
1403 }
1404
test_custom_data(skiatest::Reporter * reporter)1405 static void test_custom_data(skiatest::Reporter* reporter) {
1406 GrUniqueKey key1, key2;
1407 make_unique_key<0>(&key1, 1);
1408 make_unique_key<0>(&key2, 2);
1409 int foo = 4132;
1410 key1.setCustomData(SkData::MakeWithCopy(&foo, sizeof(foo)));
1411 REPORTER_ASSERT(reporter, *(int*) key1.getCustomData()->data() == 4132);
1412 REPORTER_ASSERT(reporter, key2.getCustomData() == nullptr);
1413
1414 // Test that copying a key also takes a ref on its custom data.
1415 GrUniqueKey key3 = key1;
1416 REPORTER_ASSERT(reporter, *(int*) key3.getCustomData()->data() == 4132);
1417 }
1418
test_abandoned(skiatest::Reporter * reporter)1419 static void test_abandoned(skiatest::Reporter* reporter) {
1420 Mock mock(10, 300);
1421 GrContext* context = mock.context();
1422 sk_sp<GrGpuResource> resource(new TestResource(context->getGpu()));
1423 context->abandonContext();
1424
1425 REPORTER_ASSERT(reporter, resource->wasDestroyed());
1426
1427 // Call all the public methods on resource in the abandoned state. They shouldn't crash.
1428
1429 resource->uniqueID();
1430 resource->getUniqueKey();
1431 resource->wasDestroyed();
1432 resource->gpuMemorySize();
1433 resource->getContext();
1434
1435 resource->abandon();
1436 resource->resourcePriv().getScratchKey();
1437 resource->resourcePriv().isBudgeted();
1438 resource->resourcePriv().makeBudgeted();
1439 resource->resourcePriv().makeUnbudgeted();
1440 resource->resourcePriv().removeScratchKey();
1441 GrUniqueKey key;
1442 make_unique_key<0>(&key, 1);
1443 resource->resourcePriv().setUniqueKey(key);
1444 resource->resourcePriv().removeUniqueKey();
1445 }
1446
test_tags(skiatest::Reporter * reporter)1447 static void test_tags(skiatest::Reporter* reporter) {
1448 #ifdef SK_DEBUG
1449 // We will insert 1 resource with tag "tag1", 2 with "tag2", and so on, up through kLastTagIdx.
1450 static constexpr int kLastTagIdx = 10;
1451 static constexpr int kNumResources = kLastTagIdx * (kLastTagIdx + 1) / 2;
1452
1453 Mock mock(kNumResources, kNumResources * TestResource::kDefaultSize);
1454 GrContext* context = mock.context();
1455 GrResourceCache* cache = mock.cache();
1456
1457 SkString tagStr;
1458 int tagIdx = 0;
1459 int currTagCnt = 0;
1460
1461 for (int i = 0; i < kNumResources; ++i, ++currTagCnt) {
1462 sk_sp<GrGpuResource> resource(new TestResource(context->getGpu()));
1463 GrUniqueKey key;
1464 if (currTagCnt == tagIdx) {
1465 tagIdx += 1;
1466 currTagCnt = 0;
1467 tagStr.printf("tag%d", tagIdx);
1468 }
1469 make_unique_key<1>(&key, i, tagStr.c_str());
1470 resource->resourcePriv().setUniqueKey(key);
1471 }
1472 SkASSERT(kLastTagIdx == tagIdx);
1473 SkASSERT(currTagCnt == kLastTagIdx);
1474
1475 // Test i = 0 to exercise unused tag string.
1476 for (int i = 0; i <= kLastTagIdx; ++i) {
1477 tagStr.printf("tag%d", i);
1478 REPORTER_ASSERT(reporter, cache->countUniqueKeysWithTag(tagStr.c_str()) == i);
1479 }
1480 #endif
1481 }
1482
DEF_GPUTEST(ResourceCacheMisc,reporter,factory)1483 DEF_GPUTEST(ResourceCacheMisc, reporter, factory) {
1484 // The below tests create their own mock contexts.
1485 test_no_key(reporter);
1486 test_budgeting(reporter);
1487 test_unbudgeted(reporter);
1488 test_unbudgeted_to_scratch(reporter);
1489 test_duplicate_unique_key(reporter);
1490 test_duplicate_scratch_key(reporter);
1491 test_remove_scratch_key(reporter);
1492 test_scratch_key_consistency(reporter);
1493 test_purge_invalidated(reporter);
1494 test_cache_chained_purge(reporter);
1495 test_resource_size_changed(reporter);
1496 test_timestamp_wrap(reporter);
1497 test_flush(reporter);
1498 test_time_purge(reporter);
1499 test_large_resource_count(reporter);
1500 test_custom_data(reporter);
1501 test_abandoned(reporter);
1502 test_tags(reporter);
1503 }
1504
1505 ////////////////////////////////////////////////////////////////////////////////
make_normal_texture(GrResourceProvider * provider,GrSurfaceFlags flags,int width,int height,int sampleCnt)1506 static sk_sp<GrTexture> make_normal_texture(GrResourceProvider* provider,
1507 GrSurfaceFlags flags,
1508 int width, int height,
1509 int sampleCnt) {
1510 GrSurfaceDesc desc;
1511 desc.fFlags = flags;
1512 desc.fWidth = width;
1513 desc.fHeight = height;
1514 desc.fConfig = kRGBA_8888_GrPixelConfig;
1515 desc.fSampleCnt = sampleCnt;
1516
1517 return sk_sp<GrTexture>(provider->createTexture(desc, SkBudgeted::kYes));
1518 }
1519
make_mipmap_texture(GrResourceProvider * provider,GrSurfaceFlags flags,int width,int height,int sampleCnt)1520 static sk_sp<GrTexture> make_mipmap_texture(GrResourceProvider* provider,
1521 GrSurfaceFlags flags,
1522 int width, int height,
1523 int sampleCnt) {
1524 SkBitmap bm;
1525
1526 bm.allocN32Pixels(width, height, true);
1527 bm.eraseColor(SK_ColorBLUE);
1528
1529 sk_sp<SkMipMap> mipmaps(SkMipMap::Build(bm, SkDestinationSurfaceColorMode::kLegacy, nullptr));
1530 SkASSERT(mipmaps);
1531 SkASSERT(mipmaps->countLevels() > 1);
1532
1533 int mipLevelCount = mipmaps->countLevels() + 1;
1534
1535 std::unique_ptr<GrMipLevel[]> texels(new GrMipLevel[mipLevelCount]);
1536
1537 texels[0].fPixels = bm.getPixels();
1538 texels[0].fRowBytes = bm.rowBytes();
1539
1540 for (int i = 1; i < mipLevelCount; ++i) {
1541 SkMipMap::Level generatedMipLevel;
1542 mipmaps->getLevel(i - 1, &generatedMipLevel);
1543 texels[i].fPixels = generatedMipLevel.fPixmap.addr();
1544 texels[i].fRowBytes = generatedMipLevel.fPixmap.rowBytes();
1545 }
1546
1547 GrSurfaceDesc desc;
1548 desc.fFlags = flags;
1549 desc.fWidth = width;
1550 desc.fHeight = height;
1551 desc.fConfig = kRGBA_8888_GrPixelConfig;
1552 desc.fSampleCnt = sampleCnt;
1553 desc.fIsMipMapped = true;
1554
1555 return sk_sp<GrTexture>(provider->createMipMappedTexture(desc, SkBudgeted::kYes,
1556 texels.get(), mipLevelCount));
1557 }
1558
1559 // Exercise GrSurface::gpuMemorySize for different combos of MSAA, RT-only,
1560 // Texture-only, both-RT-and-Texture and MIPmapped
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GPUMemorySize,reporter,ctxInfo)1561 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GPUMemorySize, reporter, ctxInfo) {
1562 GrContext* context = ctxInfo.grContext();
1563 GrResourceProvider* provider = context->resourceProvider();
1564
1565 static const int kSize = 64;
1566
1567 sk_sp<GrTexture> tex;
1568
1569 // Normal versions
1570 tex = make_normal_texture(provider, kRenderTarget_GrSurfaceFlag, kSize, kSize, 0);
1571 size_t size = tex->gpuMemorySize();
1572 REPORTER_ASSERT(reporter, kSize*kSize*4 == size);
1573
1574 if (context->caps()->maxSampleCount() >= 4) {
1575 tex = make_normal_texture(provider, kRenderTarget_GrSurfaceFlag, kSize, kSize, 4);
1576 size = tex->gpuMemorySize();
1577 REPORTER_ASSERT(reporter, kSize*kSize*4 == size || // msaa4 failed
1578 kSize*kSize*4*4 == size || // auto-resolving
1579 kSize*kSize*4*5 == size); // explicit resolve buffer
1580 }
1581
1582 tex = make_normal_texture(provider, kNone_GrSurfaceFlags, kSize, kSize, 0);
1583 size = tex->gpuMemorySize();
1584 REPORTER_ASSERT(reporter, kSize*kSize*4 == size);
1585
1586 // Mipmapped versions
1587 tex = make_mipmap_texture(provider, kRenderTarget_GrSurfaceFlag, kSize, kSize, 0);
1588 size = tex->gpuMemorySize();
1589 REPORTER_ASSERT(reporter, kSize*kSize*4+(kSize*kSize*4)/3 == size);
1590
1591 if (context->caps()->maxSampleCount() >= 4) {
1592 tex = make_mipmap_texture(provider, kRenderTarget_GrSurfaceFlag, kSize, kSize, 4);
1593 size = tex->gpuMemorySize();
1594 REPORTER_ASSERT(reporter,
1595 kSize*kSize*4+(kSize*kSize*4)/3 == size || // msaa4 failed
1596 kSize*kSize*4*4+(kSize*kSize*4)/3 == size || // auto-resolving
1597 kSize*kSize*4*5+(kSize*kSize*4)/3 == size); // explicit resolve buffer
1598 }
1599
1600 tex = make_mipmap_texture(provider, kNone_GrSurfaceFlags, kSize, kSize, 0);
1601 size = tex->gpuMemorySize();
1602 REPORTER_ASSERT(reporter, kSize*kSize*4+(kSize*kSize*4)/3 == size);
1603 }
1604
1605 #endif
1606