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