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