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