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