3 * Copyright 2011 Google Inc.
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
11 #ifndef GrResourceCache_DEFINED
12 #define GrResourceCache_DEFINED
16 #include "SkTMultiMap.h"
17 #include "GrBinHashKey.h"
18 #include "SkMessageBus.h"
19 #include "SkTInternalLList.h"
22 class GrResourceCache;
23 class GrResourceCacheEntry;
27 static GrCacheID::Domain ScratchDomain() {
28 static const GrCacheID::Domain gDomain = GrCacheID::GenerateDomain();
32 /** Uniquely identifies the GrGpuResource subclass in the key to avoid collisions
33 across resource types. */
34 typedef uint8_t ResourceType;
36 /** Flags set by the GrGpuResource subclass. */
37 typedef uint8_t ResourceFlags;
39 /** Generate a unique ResourceType */
40 static ResourceType GenerateResourceType();
42 /** Creates a key for resource */
43 GrResourceKey(const GrCacheID& id, ResourceType type, ResourceFlags flags) {
44 this->init(id.getDomain(), id.getKey(), type, flags);
47 GrResourceKey(const GrResourceKey& src) {
55 void reset(const GrCacheID& id, ResourceType type, ResourceFlags flags) {
56 this->init(id.getDomain(), id.getKey(), type, flags);
59 uint32_t getHash() const {
60 return fKey.getHash();
63 bool isScratch() const {
64 return ScratchDomain() ==
65 *reinterpret_cast<const GrCacheID::Domain*>(fKey.getData() +
66 kCacheIDDomainOffset);
69 ResourceType getResourceType() const {
70 return *reinterpret_cast<const ResourceType*>(fKey.getData() +
74 ResourceFlags getResourceFlags() const {
75 return *reinterpret_cast<const ResourceFlags*>(fKey.getData() +
76 kResourceFlagsOffset);
79 bool operator==(const GrResourceKey& other) const { return fKey == other.fKey; }
83 kCacheIDKeyOffset = 0,
84 kCacheIDDomainOffset = kCacheIDKeyOffset + sizeof(GrCacheID::Key),
85 kResourceTypeOffset = kCacheIDDomainOffset + sizeof(GrCacheID::Domain),
86 kResourceFlagsOffset = kResourceTypeOffset + sizeof(ResourceType),
87 kPadOffset = kResourceFlagsOffset + sizeof(ResourceFlags),
88 kKeySize = SkAlign4(kPadOffset),
89 kPadSize = kKeySize - kPadOffset
92 void init(const GrCacheID::Domain domain,
93 const GrCacheID::Key& key,
95 ResourceFlags flags) {
97 uint8_t fKey8[kKeySize];
98 uint32_t fKey32[kKeySize / 4];
101 uint8_t* k = keyData.fKey8;
102 memcpy(k + kCacheIDKeyOffset, key.fData8, sizeof(GrCacheID::Key));
103 memcpy(k + kCacheIDDomainOffset, &domain, sizeof(GrCacheID::Domain));
104 memcpy(k + kResourceTypeOffset, &type, sizeof(ResourceType));
105 memcpy(k + kResourceFlagsOffset, &flags, sizeof(ResourceFlags));
106 memset(k + kPadOffset, 0, kPadSize);
107 fKey.setKeyData(keyData.fKey32);
109 GrBinHashKey<kKeySize> fKey;
112 // The cache listens for these messages to purge junk resources proactively.
113 struct GrResourceInvalidatedMessage {
117 ///////////////////////////////////////////////////////////////////////////////
119 class GrResourceCacheEntry {
121 GrGpuResource* resource() const { return fResource; }
122 const GrResourceKey& key() const { return fKey; }
124 static const GrResourceKey& GetKey(const GrResourceCacheEntry& e) { return e.key(); }
125 static uint32_t Hash(const GrResourceKey& key) { return key.getHash(); }
127 void validate() const;
129 void validate() const {}
133 * Update the cached size for this entry and inform the resource cache that
134 * it has changed. Usually invoked from GrGpuResource::didChangeGpuMemorySize,
135 * not directly from here.
137 void didChangeResourceSize();
140 GrResourceCacheEntry(GrResourceCache* resourceCache,
141 const GrResourceKey& key,
142 GrGpuResource* resource);
143 ~GrResourceCacheEntry();
145 GrResourceCache* fResourceCache;
147 GrGpuResource* fResource;
151 // Linked list for the LRU ordering.
152 SK_DECLARE_INTERNAL_LLIST_INTERFACE(GrResourceCacheEntry);
154 friend class GrResourceCache;
157 ///////////////////////////////////////////////////////////////////////////////
160 * Cache of GrGpuResource objects.
162 * These have a corresponding GrResourceKey, built from 128bits identifying the
163 * resource. Multiple resources can map to same GrResourceKey.
165 * The cache stores the entries in a double-linked list, which is its LRU.
166 * When an entry is "locked" (i.e. given to the caller), it is moved to the
167 * head of the list. If/when we must purge some of the entries, we walk the
168 * list backwards from the tail, since those are the least recently used.
170 * For fast searches, we maintain a hash map based on the GrResourceKey.
172 * It is a goal to make the GrResourceCache the central repository and bookkeeper
173 * of all resources. It should replace the linked list of GrGpuResources that
174 * GrGpu uses to call abandon/release.
176 class GrResourceCache {
178 GrResourceCache(int maxCount, size_t maxBytes);
182 * Return the current resource cache limits.
184 * @param maxResource If non-null, returns maximum number of resources
185 * that can be held in the cache.
186 * @param maxBytes If non-null, returns maximum number of bytes of
187 * gpu memory that can be held in the cache.
189 void getLimits(int* maxResources, size_t* maxBytes) const;
192 * Specify the resource cache limits. If the current cache exceeds either
193 * of these, it will be purged (LRU) to keep the cache within these limits.
195 * @param maxResources The maximum number of resources that can be held in
197 * @param maxBytes The maximum number of bytes of resource memory that
198 * can be held in the cache.
200 void setLimits(int maxResources, size_t maxResourceBytes);
203 * The callback function used by the cache when it is still over budget
204 * after a purge. The passed in 'data' is the same 'data' handed to
205 * setOverbudgetCallback. The callback returns true if some resources
208 typedef bool (*PFOverbudgetCB)(void* data);
211 * Set the callback the cache should use when it is still over budget
212 * after a purge. The 'data' provided here will be passed back to the
213 * callback. Note that the cache will attempt to purge any resources newly
214 * freed by the callback.
216 void setOverbudgetCallback(PFOverbudgetCB overbudgetCB, void* data) {
217 fOverbudgetCB = overbudgetCB;
218 fOverbudgetData = data;
222 * Returns the number of bytes consumed by cached resources.
224 size_t getCachedResourceBytes() const { return fEntryBytes; }
227 * Returns the number of cached resources.
229 int getCachedResourceCount() const { return fEntryCount; }
231 // For a found or added resource to be completely exclusive to the caller
232 // both the kNoOtherOwners and kHide flags need to be specified
233 enum OwnershipFlags {
234 kNoOtherOwners_OwnershipFlag = 0x1, // found/added resource has no other owners
235 kHide_OwnershipFlag = 0x2 // found/added resource is hidden from future 'find's
239 * Search for an entry with the same Key. If found, return it.
240 * If not found, return null.
241 * If ownershipFlags includes kNoOtherOwners and a resource is returned
242 * then that resource has no other refs to it.
243 * If ownershipFlags includes kHide and a resource is returned then that
244 * resource will not be returned from future 'find' calls until it is
245 * 'freed' (and recycled) or makeNonExclusive is called.
246 * For a resource to be completely exclusive to a caller both kNoOtherOwners
247 * and kHide must be specified.
249 GrGpuResource* find(const GrResourceKey& key,
250 uint32_t ownershipFlags = 0);
253 * Add the new resource to the cache (by creating a new cache entry based
254 * on the provided key and resource).
256 * Ownership of the resource is transferred to the resource cache,
257 * which will unref() it when it is purged or deleted.
259 * If ownershipFlags includes kHide, subsequent calls to 'find' will not
260 * return 'resource' until it is 'freed' (and recycled) or makeNonExclusive
263 void addResource(const GrResourceKey& key,
264 GrGpuResource* resource,
265 uint32_t ownershipFlags = 0);
268 * Determines if the cache contains an entry matching a key. If a matching
269 * entry exists but was detached then it will not be found.
271 bool hasKey(const GrResourceKey& key) const { return NULL != fCache.find(key); }
274 * Hide 'entry' so that future searches will not find it. Such
275 * hidden entries will not be purged. The entry still counts against
276 * the cache's budget and should be made non-exclusive when exclusive access
277 * is no longer needed.
279 void makeExclusive(GrResourceCacheEntry* entry);
282 * Restore 'entry' so that it can be found by future searches. 'entry'
283 * will also be purgeable (provided its lock count is now 0.)
285 void makeNonExclusive(GrResourceCacheEntry* entry);
288 * Notify the cache that the size of a resource has changed.
290 void didIncreaseResourceSize(const GrResourceCacheEntry*, size_t amountInc);
291 void didDecreaseResourceSize(const GrResourceCacheEntry*, size_t amountDec);
294 * Remove a resource from the cache and delete it!
296 void deleteResource(GrResourceCacheEntry* entry);
299 * Removes every resource in the cache that isn't locked.
301 void purgeAllUnlocked();
304 * Allow cache to purge unused resources to obey resource limitations
305 * Note: this entry point will be hidden (again) once totally ref-driven
306 * cache maintenance is implemented. Note that the overbudget callback
307 * will be called if the initial purge doesn't get the cache under
310 * extraCount and extraBytes are added to the current resource allocation
311 * to make sure enough room is available for future additions (e.g,
312 * 10MB across 10 textures is about to be added).
314 void purgeAsNeeded(int extraCount = 0, size_t extraBytes = 0);
317 void validate() const;
319 void validate() const {}
327 enum BudgetBehaviors {
328 kAccountFor_BudgetBehavior,
329 kIgnore_BudgetBehavior
332 void internalDetach(GrResourceCacheEntry*, BudgetBehaviors behavior = kAccountFor_BudgetBehavior);
333 void attachToHead(GrResourceCacheEntry*, BudgetBehaviors behavior = kAccountFor_BudgetBehavior);
335 void removeInvalidResource(GrResourceCacheEntry* entry);
337 SkTMultiMap<GrResourceCacheEntry, GrResourceKey> fCache;
339 // We're an internal doubly linked list
340 typedef SkTInternalLList<GrResourceCacheEntry> EntryList;
344 // These objects cannot be returned by a search
345 EntryList fExclusiveList;
348 // our budget, used in purgeAsNeeded()
352 // our current stats, related to our budget
354 int fHighWaterEntryCount;
355 size_t fHighWaterEntryBytes;
356 int fHighWaterClientDetachedCount;
357 size_t fHighWaterClientDetachedBytes;
362 int fClientDetachedCount;
363 size_t fClientDetachedBytes;
365 // prevents recursive purging
368 PFOverbudgetCB fOverbudgetCB;
369 void* fOverbudgetData;
371 void internalPurge(int extraCount, size_t extraBytes);
373 // Listen for messages that a resource has been invalidated and purge cached junk proactively.
374 SkMessageBus<GrResourceInvalidatedMessage>::Inbox fInvalidationInbox;
375 void purgeInvalidated();
378 static size_t countBytes(const SkTInternalLList<GrResourceCacheEntry>& list);
382 ///////////////////////////////////////////////////////////////////////////////
385 class GrAutoResourceCacheValidate {
387 GrAutoResourceCacheValidate(GrResourceCache* cache) : fCache(cache) {
390 ~GrAutoResourceCacheValidate() {
394 GrResourceCache* fCache;
397 class GrAutoResourceCacheValidate {
399 GrAutoResourceCacheValidate(GrResourceCache*) {}