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