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