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