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