1
2 /*
3 * Copyright 2011 Google Inc.
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
9
10
11 #ifndef GrResourceCache_DEFINED
12 #define GrResourceCache_DEFINED
13
14 #include "GrConfig.h"
15 #include "GrTypes.h"
16 #include "GrTHashCache.h"
17 #include "GrBinHashKey.h"
18 #include "SkTInternalLList.h"
19
20 class GrResource;
21 class GrResourceEntry;
22
23 class GrResourceKey {
24 public:
25 enum {
26 kHashBits = 7,
27 kHashCount = 1 << kHashBits,
28 kHashMask = kHashCount - 1
29 };
30
ScratchDomain()31 static GrCacheID::Domain ScratchDomain() {
32 static const GrCacheID::Domain gDomain = GrCacheID::GenerateDomain();
33 return gDomain;
34 }
35
36 /** Uniquely identifies the GrResource subclass in the key to avoid collisions
37 across resource types. */
38 typedef uint8_t ResourceType;
39
40 /** Flags set by the GrResource subclass. */
41 typedef uint8_t ResourceFlags;
42
43 /** Generate a unique ResourceType */
44 static ResourceType GenerateResourceType();
45
46 /** Creates a key for resource */
GrResourceKey(const GrCacheID & id,ResourceType type,ResourceFlags flags)47 GrResourceKey(const GrCacheID& id, ResourceType type, ResourceFlags flags) {
48 this->init(id.getDomain(), id.getKey(), type, flags);
49 };
50
GrResourceKey(const GrResourceKey & src)51 GrResourceKey(const GrResourceKey& src) {
52 fKey = src.fKey;
53 }
54
GrResourceKey()55 GrResourceKey() {
56 fKey.fHashedKey.reset();
57 }
58
reset(const GrCacheID & id,ResourceType type,ResourceFlags flags)59 void reset(const GrCacheID& id, ResourceType type, ResourceFlags flags) {
60 this->init(id.getDomain(), id.getKey(), type, flags);
61 }
62
63 //!< returns hash value [0..kHashMask] for the key
getHash()64 int getHash() const {
65 return fKey.fHashedKey.getHash() & kHashMask;
66 }
67
isScratch()68 bool isScratch() const {
69 return ScratchDomain() ==
70 *reinterpret_cast<const GrCacheID::Domain*>(fKey.fHashedKey.getData() +
71 kCacheIDDomainOffset);
72 }
73
getResourceType()74 ResourceType getResourceType() const {
75 return *reinterpret_cast<const ResourceType*>(fKey.fHashedKey.getData() +
76 kResourceTypeOffset);
77 }
78
getResourceFlags()79 ResourceFlags getResourceFlags() const {
80 return *reinterpret_cast<const ResourceFlags*>(fKey.fHashedKey.getData() +
81 kResourceFlagsOffset);
82 }
83
compare(const GrResourceKey & other)84 int compare(const GrResourceKey& other) const {
85 return fKey.fHashedKey.compare(other.fKey.fHashedKey);
86 }
87
LT(const GrResourceKey & a,const GrResourceKey & b)88 static bool LT(const GrResourceKey& a, const GrResourceKey& b) {
89 return a.compare(b) < 0;
90 }
91
EQ(const GrResourceKey & a,const GrResourceKey & b)92 static bool EQ(const GrResourceKey& a, const GrResourceKey& b) {
93 return 0 == a.compare(b);
94 }
95
96 inline static bool LT(const GrResourceEntry& entry, const GrResourceKey& key);
97 inline static bool EQ(const GrResourceEntry& entry, const GrResourceKey& key);
98 inline static bool LT(const GrResourceEntry& a, const GrResourceEntry& b);
99 inline static bool EQ(const GrResourceEntry& a, const GrResourceEntry& b);
100
101 private:
102 enum {
103 kCacheIDKeyOffset = 0,
104 kCacheIDDomainOffset = kCacheIDKeyOffset + sizeof(GrCacheID::Key),
105 kResourceTypeOffset = kCacheIDDomainOffset + sizeof(GrCacheID::Domain),
106 kResourceFlagsOffset = kResourceTypeOffset + sizeof(ResourceType),
107 kPadOffset = kResourceFlagsOffset + sizeof(ResourceFlags),
108 kKeySize = SkAlign4(kPadOffset),
109 kPadSize = kKeySize - kPadOffset
110 };
111
init(const GrCacheID::Domain domain,const GrCacheID::Key & key,ResourceType type,ResourceFlags flags)112 void init(const GrCacheID::Domain domain,
113 const GrCacheID::Key& key,
114 ResourceType type,
115 ResourceFlags flags) {
116 union {
117 uint8_t fKey8[kKeySize];
118 uint32_t fKey32[kKeySize / 4];
119 } keyData;
120
121 uint8_t* k = keyData.fKey8;
122 memcpy(k + kCacheIDKeyOffset, key.fData8, sizeof(GrCacheID::Key));
123 memcpy(k + kCacheIDDomainOffset, &domain, sizeof(GrCacheID::Domain));
124 memcpy(k + kResourceTypeOffset, &type, sizeof(ResourceType));
125 memcpy(k + kResourceFlagsOffset, &flags, sizeof(ResourceFlags));
126 memset(k + kPadOffset, 0, kPadSize);
127 fKey.fHashedKey.setKeyData(keyData.fKey32);
128 }
129
130 struct Key;
131 typedef GrTBinHashKey<Key, kKeySize> HashedKey;
132
133 struct Key {
compareKey134 int compare(const HashedKey& hashedKey) const {
135 return fHashedKey.compare(fHashedKey);
136 }
137
138 HashedKey fHashedKey;
139 };
140
141 Key fKey;
142 };
143
144 ///////////////////////////////////////////////////////////////////////////////
145
146 class GrResourceEntry {
147 public:
resource()148 GrResource* resource() const { return fResource; }
key()149 const GrResourceKey& key() const { return fKey; }
150
151 #if GR_DEBUG
152 void validate() const;
153 #else
validate()154 void validate() const {}
155 #endif
156
157 private:
158 GrResourceEntry(const GrResourceKey& key, GrResource* resource);
159 ~GrResourceEntry();
160
161 GrResourceKey fKey;
162 GrResource* fResource;
163
164 // we're a linked list
165 SK_DECLARE_INTERNAL_LLIST_INTERFACE(GrResourceEntry);
166
167 friend class GrResourceCache;
168 friend class GrDLinkedList;
169 };
170
LT(const GrResourceEntry & entry,const GrResourceKey & key)171 bool GrResourceKey::LT(const GrResourceEntry& entry, const GrResourceKey& key) {
172 return LT(entry.key(), key);
173 }
174
EQ(const GrResourceEntry & entry,const GrResourceKey & key)175 bool GrResourceKey::EQ(const GrResourceEntry& entry, const GrResourceKey& key) {
176 return EQ(entry.key(), key);
177 }
178
LT(const GrResourceEntry & a,const GrResourceEntry & b)179 bool GrResourceKey::LT(const GrResourceEntry& a, const GrResourceEntry& b) {
180 return LT(a.key(), b.key());
181 }
182
EQ(const GrResourceEntry & a,const GrResourceEntry & b)183 bool GrResourceKey::EQ(const GrResourceEntry& a, const GrResourceEntry& b) {
184 return EQ(a.key(), b.key());
185 }
186
187 ///////////////////////////////////////////////////////////////////////////////
188
189 #include "GrTHashCache.h"
190
191 /**
192 * Cache of GrResource objects.
193 *
194 * These have a corresponding GrResourceKey, built from 128bits identifying the
195 * resource.
196 *
197 * The cache stores the entries in a double-linked list, which is its LRU.
198 * When an entry is "locked" (i.e. given to the caller), it is moved to the
199 * head of the list. If/when we must purge some of the entries, we walk the
200 * list backwards from the tail, since those are the least recently used.
201 *
202 * For fast searches, we maintain a sorted array (based on the GrResourceKey)
203 * which we can bsearch. When a new entry is added, it is inserted into this
204 * array.
205 *
206 * For even faster searches, a hash is computed from the Key. If there is
207 * a collision between two keys with the same hash, we fall back on the
208 * bsearch, and update the hash to reflect the most recent Key requested.
209 */
210 class GrResourceCache {
211 public:
212 GrResourceCache(int maxCount, size_t maxBytes);
213 ~GrResourceCache();
214
215 /**
216 * Return the current resource cache limits.
217 *
218 * @param maxResource If non-null, returns maximum number of resources
219 * that can be held in the cache.
220 * @param maxBytes If non-null, returns maximum number of bytes of
221 * gpu memory that can be held in the cache.
222 */
223 void getLimits(int* maxResources, size_t* maxBytes) const;
224
225 /**
226 * Specify the resource cache limits. If the current cache exceeds either
227 * of these, it will be purged (LRU) to keep the cache within these limits.
228 *
229 * @param maxResources The maximum number of resources that can be held in
230 * the cache.
231 * @param maxBytes The maximum number of bytes of resource memory that
232 * can be held in the cache.
233 */
234 void setLimits(int maxResource, size_t maxResourceBytes);
235
236 /**
237 * Returns the number of bytes consumed by cached resources.
238 */
getCachedResourceBytes()239 size_t getCachedResourceBytes() const { return fEntryBytes; }
240
241 // For a found or added resource to be completely exclusive to the caller
242 // both the kNoOtherOwners and kHide flags need to be specified
243 enum OwnershipFlags {
244 kNoOtherOwners_OwnershipFlag = 0x1, // found/added resource has no other owners
245 kHide_OwnershipFlag = 0x2 // found/added resource is hidden from future 'find's
246 };
247
248 /**
249 * Search for an entry with the same Key. If found, return it.
250 * If not found, return null.
251 * If ownershipFlags includes kNoOtherOwners and a resource is returned
252 * then that resource has no other refs to it.
253 * If ownershipFlags includes kHide and a resource is returned then that
254 * resource will not be returned from future 'find' calls until it is
255 * 'freed' (and recycled) or makeNonExclusive is called.
256 * For a resource to be completely exclusive to a caller both kNoOtherOwners
257 * and kHide must be specified.
258 */
259 GrResource* find(const GrResourceKey& key,
260 uint32_t ownershipFlags = 0);
261
262 /**
263 * Add the new resource to the cache (by creating a new cache entry based
264 * on the provided key and resource).
265 *
266 * Ownership of the resource is transferred to the resource cache,
267 * which will unref() it when it is purged or deleted.
268 *
269 * If ownershipFlags includes kHide, subsequent calls to 'find' will not
270 * return 'resource' until it is 'freed' (and recycled) or makeNonExclusive
271 * is called.
272 */
273 void addResource(const GrResourceKey& key,
274 GrResource* resource,
275 uint32_t ownershipFlags = 0);
276
277 /**
278 * Determines if the cache contains an entry matching a key. If a matching
279 * entry exists but was detached then it will not be found.
280 */
281 bool hasKey(const GrResourceKey& key) const;
282
283 /**
284 * Hide 'entry' so that future searches will not find it. Such
285 * hidden entries will not be purged. The entry still counts against
286 * the cache's budget and should be made non-exclusive when exclusive access
287 * is no longer needed.
288 */
289 void makeExclusive(GrResourceEntry* entry);
290
291 /**
292 * Restore 'entry' so that it can be found by future searches. 'entry'
293 * will also be purgeable (provided its lock count is now 0.)
294 */
295 void makeNonExclusive(GrResourceEntry* entry);
296
297 /**
298 * Removes every resource in the cache that isn't locked.
299 */
300 void purgeAllUnlocked();
301
302 /**
303 * Allow cache to purge unused resources to obey resource limitations
304 * Note: this entry point will be hidden (again) once totally ref-driven
305 * cache maintenance is implemented
306 */
307 void purgeAsNeeded();
308
309 #if GR_DEBUG
310 void validate() const;
311 #else
validate()312 void validate() const {}
313 #endif
314
315 #if GR_CACHE_STATS
316 void printStats();
317 #endif
318
319 private:
320 enum BudgetBehaviors {
321 kAccountFor_BudgetBehavior,
322 kIgnore_BudgetBehavior
323 };
324
325 void internalDetach(GrResourceEntry*, BudgetBehaviors behavior = kAccountFor_BudgetBehavior);
326 void attachToHead(GrResourceEntry*, BudgetBehaviors behavior = kAccountFor_BudgetBehavior);
327
328 void removeInvalidResource(GrResourceEntry* entry);
329
330 GrTHashTable<GrResourceEntry, GrResourceKey, 8> fCache;
331
332 // We're an internal doubly linked list
333 typedef SkTInternalLList<GrResourceEntry> EntryList;
334 EntryList fList;
335
336 #if GR_DEBUG
337 // These objects cannot be returned by a search
338 EntryList fExclusiveList;
339 #endif
340
341 // our budget, used in purgeAsNeeded()
342 int fMaxCount;
343 size_t fMaxBytes;
344
345 // our current stats, related to our budget
346 #if GR_CACHE_STATS
347 int fHighWaterEntryCount;
348 size_t fHighWaterEntryBytes;
349 int fHighWaterClientDetachedCount;
350 size_t fHighWaterClientDetachedBytes;
351 #endif
352
353 int fEntryCount;
354 size_t fEntryBytes;
355 int fClientDetachedCount;
356 size_t fClientDetachedBytes;
357
358 // prevents recursive purging
359 bool fPurging;
360
361 #if GR_DEBUG
362 static size_t countBytes(const SkTInternalLList<GrResourceEntry>& list);
363 #endif
364 };
365
366 ///////////////////////////////////////////////////////////////////////////////
367
368 #if GR_DEBUG
369 class GrAutoResourceCacheValidate {
370 public:
GrAutoResourceCacheValidate(GrResourceCache * cache)371 GrAutoResourceCacheValidate(GrResourceCache* cache) : fCache(cache) {
372 cache->validate();
373 }
~GrAutoResourceCacheValidate()374 ~GrAutoResourceCacheValidate() {
375 fCache->validate();
376 }
377 private:
378 GrResourceCache* fCache;
379 };
380 #else
381 class GrAutoResourceCacheValidate {
382 public:
GrAutoResourceCacheValidate(GrResourceCache *)383 GrAutoResourceCacheValidate(GrResourceCache*) {}
384 };
385 #endif
386
387 #endif
388