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