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 "SkDiscardableMemory.h"
9 #include "SkResourceCache.h"
10 #include "Test.h"
11 
12 namespace {
13 static void* gGlobalAddress;
14 struct TestingKey : public SkResourceCache::Key {
15     intptr_t    fValue;
16 
TestingKey__anoncd2cba920111::TestingKey17     TestingKey(intptr_t value, uint64_t sharedID = 0) : fValue(value) {
18         this->init(&gGlobalAddress, sharedID, sizeof(fValue));
19     }
20 };
21 struct TestingRec : public SkResourceCache::Rec {
TestingRec__anoncd2cba920111::TestingRec22     TestingRec(const TestingKey& key, uint32_t value) : fKey(key), fValue(value) {}
23 
24     TestingKey  fKey;
25     intptr_t    fValue;
26 
getKey__anoncd2cba920111::TestingRec27     const Key& getKey() const override { return fKey; }
bytesUsed__anoncd2cba920111::TestingRec28     size_t bytesUsed() const override { return sizeof(fKey) + sizeof(fValue); }
getCategory__anoncd2cba920111::TestingRec29     const char* getCategory() const override { return "test_cache"; }
diagnostic_only_getDiscardable__anoncd2cba920111::TestingRec30     SkDiscardableMemory* diagnostic_only_getDiscardable() const override { return nullptr; }
31 
Visitor__anoncd2cba920111::TestingRec32     static bool Visitor(const SkResourceCache::Rec& baseRec, void* context) {
33         const TestingRec& rec = static_cast<const TestingRec&>(baseRec);
34         intptr_t* result = (intptr_t*)context;
35 
36         *result = rec.fValue;
37         return true;
38     }
39 };
40 }
41 
42 static const int COUNT = 10;
43 static const int DIM = 256;
44 
test_cache(skiatest::Reporter * reporter,SkResourceCache & cache,bool testPurge)45 static void test_cache(skiatest::Reporter* reporter, SkResourceCache& cache, bool testPurge) {
46     for (int i = 0; i < COUNT; ++i) {
47         TestingKey key(i);
48         intptr_t value = -1;
49 
50         REPORTER_ASSERT(reporter, !cache.find(key, TestingRec::Visitor, &value));
51         REPORTER_ASSERT(reporter, -1 == value);
52 
53         cache.add(new TestingRec(key, i));
54 
55         REPORTER_ASSERT(reporter, cache.find(key, TestingRec::Visitor, &value));
56         REPORTER_ASSERT(reporter, i == value);
57     }
58 
59     if (testPurge) {
60         // stress test, should trigger purges
61         for (int i = 0; i < COUNT * 100; ++i) {
62             TestingKey key(i);
63             cache.add(new TestingRec(key, i));
64         }
65     }
66 
67     // test the originals after all that purging
68     for (int i = 0; i < COUNT; ++i) {
69         intptr_t value;
70         (void)cache.find(TestingKey(i), TestingRec::Visitor, &value);
71     }
72 
73     cache.setTotalByteLimit(0);
74 }
75 
test_cache_purge_shared_id(skiatest::Reporter * reporter,SkResourceCache & cache)76 static void test_cache_purge_shared_id(skiatest::Reporter* reporter, SkResourceCache& cache) {
77     for (int i = 0; i < COUNT; ++i) {
78         TestingKey key(i, i & 1);   // every other key will have a 1 for its sharedID
79         cache.add(new TestingRec(key, i));
80     }
81 
82     // Ensure that everyone is present
83     for (int i = 0; i < COUNT; ++i) {
84         TestingKey key(i, i & 1);   // every other key will have a 1 for its sharedID
85         intptr_t value = -1;
86 
87         REPORTER_ASSERT(reporter, cache.find(key, TestingRec::Visitor, &value));
88         REPORTER_ASSERT(reporter, value == i);
89     }
90 
91     // Now purge the ones that had a non-zero sharedID (the odd-indexed ones)
92     cache.purgeSharedID(1);
93 
94     // Ensure that only the even ones are still present
95     for (int i = 0; i < COUNT; ++i) {
96         TestingKey key(i, i & 1);   // every other key will have a 1 for its sharedID
97         intptr_t value = -1;
98 
99         if (i & 1) {
100             REPORTER_ASSERT(reporter, !cache.find(key, TestingRec::Visitor, &value));
101         } else {
102             REPORTER_ASSERT(reporter, cache.find(key, TestingRec::Visitor, &value));
103             REPORTER_ASSERT(reporter, value == i);
104         }
105     }
106 }
107 
108 #include "SkDiscardableMemoryPool.h"
109 
110 static SkDiscardableMemoryPool* gPool;
pool_factory(size_t bytes)111 static SkDiscardableMemory* pool_factory(size_t bytes) {
112     SkASSERT(gPool);
113     return gPool->create(bytes);
114 }
115 
DEF_TEST(ImageCache,reporter)116 DEF_TEST(ImageCache, reporter) {
117     static const size_t defLimit = DIM * DIM * 4 * COUNT + 1024;    // 1K slop
118 
119     {
120         SkResourceCache cache(defLimit);
121         test_cache(reporter, cache, true);
122     }
123     {
124         sk_sp<SkDiscardableMemoryPool> pool(SkDiscardableMemoryPool::Make(defLimit));
125         gPool = pool.get();
126         SkResourceCache cache(pool_factory);
127         test_cache(reporter, cache, true);
128     }
129     {
130         SkResourceCache cache(SkDiscardableMemory::Create);
131         test_cache(reporter, cache, false);
132     }
133     {
134         SkResourceCache cache(defLimit);
135         test_cache_purge_shared_id(reporter, cache);
136     }
137 }
138 
DEF_TEST(ImageCache_doubleAdd,r)139 DEF_TEST(ImageCache_doubleAdd, r) {
140     // Adding the same key twice should be safe.
141     SkResourceCache cache(4096);
142 
143     TestingKey key(1);
144 
145     cache.add(new TestingRec(key, 2));
146     cache.add(new TestingRec(key, 3));
147 
148     // Lookup can return either value.
149     intptr_t value = -1;
150     REPORTER_ASSERT(r, cache.find(key, TestingRec::Visitor, &value));
151     REPORTER_ASSERT(r, 2 == value || 3 == value);
152 }
153