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