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