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