1
2 /*
3 * Copyright 2014 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 #ifndef GrResourceCache_DEFINED
10 #define GrResourceCache_DEFINED
11
12 #include "GrGpuResource.h"
13 #include "GrGpuResourceCacheAccess.h"
14 #include "GrGpuResourcePriv.h"
15 #include "GrResourceKey.h"
16 #include "SkMessageBus.h"
17 #include "SkRefCnt.h"
18 #include "SkTArray.h"
19 #include "SkTDPQueue.h"
20 #include "SkTInternalLList.h"
21 #include "SkTMultiMap.h"
22
23 class SkString;
24
25 /**
26 * Manages the lifetime of all GrGpuResource instances.
27 *
28 * Resources may have optionally have two types of keys:
29 * 1) A scratch key. This is for resources whose allocations are cached but not their contents.
30 * Multiple resources can share the same scratch key. This is so a caller can have two
31 * resource instances with the same properties (e.g. multipass rendering that ping-pongs
32 * between two temporary surfaces). The scratch key is set at resource creation time and
33 * should never change. Resources need not have a scratch key.
34 * 2) A unique key. This key's meaning is specific to the domain that created the key. Only one
35 * resource may have a given unique key. The unique key can be set, cleared, or changed
36 * anytime after resource creation.
37 *
38 * A unique key always takes precedence over a scratch key when a resource has both types of keys.
39 * If a resource has neither key type then it will be deleted as soon as the last reference to it
40 * is dropped.
41 *
42 * When proactive purging is enabled, on every flush, the timestamp of that flush is stored in a
43 * n-sized ring buffer. When purging occurs each purgeable resource's timestamp is compared to the
44 * timestamp of the n-th prior flush. If the resource's last use timestamp is older than the old
45 * flush then the resource is proactively purged even when the cache is under budget. By default
46 * this feature is disabled, though it can be enabled by calling GrResourceCache::setLimits.
47 */
48 class GrResourceCache {
49 public:
50 GrResourceCache();
51 ~GrResourceCache();
52
53 // Default maximum number of budgeted resources in the cache.
54 static const int kDefaultMaxCount = 2 * (1 << 12);
55 // Default maximum number of bytes of gpu memory of budgeted resources in the cache.
56 static const size_t kDefaultMaxSize = 96 * (1 << 20);
57 // Default number of flushes a budgeted resources can go unused in the cache before it is
58 // purged. Large values disable the feature (as the ring buffer of flush timestamps would be
59 // large). This is currently the default until we decide to enable this feature
60 // of the cache by default.
61 static const int kDefaultMaxUnusedFlushes = 1024;
62
63 /** Used to access functionality needed by GrGpuResource for lifetime management. */
64 class ResourceAccess;
65 ResourceAccess resourceAccess();
66
67 /**
68 * Sets the cache limits in terms of number of resources, max gpu memory byte size, and number
69 * of GrContext flushes that a resource can be unused before it is evicted. The latter value is
70 * a suggestion and there is no promise that a resource will be purged immediately after it
71 * hasn't been used in maxUnusedFlushes flushes.
72 */
73 void setLimits(int count, size_t bytes, int maxUnusedFlushes = kDefaultMaxUnusedFlushes);
74
75 /**
76 * Returns the number of resources.
77 */
getResourceCount()78 int getResourceCount() const {
79 return fPurgeableQueue.count() + fNonpurgeableResources.count();
80 }
81
82 /**
83 * Returns the number of resources that count against the budget.
84 */
getBudgetedResourceCount()85 int getBudgetedResourceCount() const { return fBudgetedCount; }
86
87 /**
88 * Returns the number of bytes consumed by resources.
89 */
getResourceBytes()90 size_t getResourceBytes() const { return fBytes; }
91
92 /**
93 * Returns the number of bytes consumed by budgeted resources.
94 */
getBudgetedResourceBytes()95 size_t getBudgetedResourceBytes() const { return fBudgetedBytes; }
96
97 /**
98 * Returns the cached resources count budget.
99 */
getMaxResourceCount()100 int getMaxResourceCount() const { return fMaxCount; }
101
102 /**
103 * Returns the number of bytes consumed by cached resources.
104 */
getMaxResourceBytes()105 size_t getMaxResourceBytes() const { return fMaxBytes; }
106
107 /**
108 * Abandons the backend API resources owned by all GrGpuResource objects and removes them from
109 * the cache.
110 */
111 void abandonAll();
112
113 /**
114 * Releases the backend API resources owned by all GrGpuResource objects and removes them from
115 * the cache.
116 */
117 void releaseAll();
118
119 enum {
120 /** Preferentially returns scratch resources with no pending IO. */
121 kPreferNoPendingIO_ScratchFlag = 0x1,
122 /** Will not return any resources that match but have pending IO. */
123 kRequireNoPendingIO_ScratchFlag = 0x2,
124 };
125
126 /**
127 * Find a resource that matches a scratch key.
128 */
129 GrGpuResource* findAndRefScratchResource(const GrScratchKey& scratchKey, uint32_t flags = 0);
130
131 #ifdef SK_DEBUG
132 // This is not particularly fast and only used for validation, so debug only.
countScratchEntriesForKey(const GrScratchKey & scratchKey)133 int countScratchEntriesForKey(const GrScratchKey& scratchKey) const {
134 return fScratchMap.countForKey(scratchKey);
135 }
136 #endif
137
138 /**
139 * Find a resource that matches a unique key.
140 */
findAndRefUniqueResource(const GrUniqueKey & key)141 GrGpuResource* findAndRefUniqueResource(const GrUniqueKey& key) {
142 GrGpuResource* resource = fUniqueHash.find(key);
143 if (resource) {
144 this->refAndMakeResourceMRU(resource);
145 }
146 return resource;
147 }
148
149 /**
150 * Query whether a unique key exists in the cache.
151 */
hasUniqueKey(const GrUniqueKey & key)152 bool hasUniqueKey(const GrUniqueKey& key) const {
153 return SkToBool(fUniqueHash.find(key));
154 }
155
156 /** Purges resources to become under budget and processes resources with invalidated unique
157 keys. */
158 void purgeAsNeeded();
159
160 /** Purges all resources that don't have external owners. */
161 void purgeAllUnlocked();
162
163 /**
164 * The callback function used by the cache when it is still over budget after a purge. The
165 * passed in 'data' is the same 'data' handed to setOverbudgetCallback.
166 */
167 typedef void (*PFOverBudgetCB)(void* data);
168
169 /**
170 * Set the callback the cache should use when it is still over budget after a purge. The 'data'
171 * provided here will be passed back to the callback. Note that the cache will attempt to purge
172 * any resources newly freed by the callback.
173 */
setOverBudgetCallback(PFOverBudgetCB overBudgetCB,void * data)174 void setOverBudgetCallback(PFOverBudgetCB overBudgetCB, void* data) {
175 fOverBudgetCB = overBudgetCB;
176 fOverBudgetData = data;
177 }
178
179 void notifyFlushOccurred();
180
181 #if GR_GPU_STATS
182 void dumpStats(SkString*) const;
183 #endif
184
185 // This function is for unit testing and is only defined in test tools.
186 void changeTimestamp(uint32_t newTimestamp);
187
188 private:
189 ///////////////////////////////////////////////////////////////////////////
190 /// @name Methods accessible via ResourceAccess
191 ////
192 void insertResource(GrGpuResource*);
193 void removeResource(GrGpuResource*);
194 void notifyCntReachedZero(GrGpuResource*, uint32_t flags);
195 void didChangeGpuMemorySize(const GrGpuResource*, size_t oldSize);
196 void changeUniqueKey(GrGpuResource*, const GrUniqueKey&);
197 void removeUniqueKey(GrGpuResource*);
198 void willRemoveScratchKey(const GrGpuResource*);
199 void didChangeBudgetStatus(GrGpuResource*);
200 void refAndMakeResourceMRU(GrGpuResource*);
201 /// @}
202
203 void resetFlushTimestamps();
204 void processInvalidUniqueKeys(const SkTArray<GrUniqueKeyInvalidatedMessage>&);
205 void addToNonpurgeableArray(GrGpuResource*);
206 void removeFromNonpurgeableArray(GrGpuResource*);
overBudget()207 bool overBudget() const { return fBudgetedBytes > fMaxBytes || fBudgetedCount > fMaxCount; }
208
209 uint32_t getNextTimestamp();
210
211 #ifdef SK_DEBUG
212 bool isInCache(const GrGpuResource* r) const;
213 void validate() const;
214 #else
validate()215 void validate() const {}
216 #endif
217
218 class AutoValidate;
219
220 class AvailableForScratchUse;
221
222 struct ScratchMapTraits {
GetKeyScratchMapTraits223 static const GrScratchKey& GetKey(const GrGpuResource& r) {
224 return r.resourcePriv().getScratchKey();
225 }
226
HashScratchMapTraits227 static uint32_t Hash(const GrScratchKey& key) { return key.hash(); }
228 };
229 typedef SkTMultiMap<GrGpuResource, GrScratchKey, ScratchMapTraits> ScratchMap;
230
231 struct UniqueHashTraits {
GetKeyUniqueHashTraits232 static const GrUniqueKey& GetKey(const GrGpuResource& r) { return r.getUniqueKey(); }
233
HashUniqueHashTraits234 static uint32_t Hash(const GrUniqueKey& key) { return key.hash(); }
235 };
236 typedef SkTDynamicHash<GrGpuResource, GrUniqueKey, UniqueHashTraits> UniqueHash;
237
CompareTimestamp(GrGpuResource * const & a,GrGpuResource * const & b)238 static bool CompareTimestamp(GrGpuResource* const& a, GrGpuResource* const& b) {
239 return a->cacheAccess().timestamp() < b->cacheAccess().timestamp();
240 }
241
AccessResourceIndex(GrGpuResource * const & res)242 static int* AccessResourceIndex(GrGpuResource* const& res) {
243 return res->cacheAccess().accessCacheIndex();
244 }
245
246 typedef SkMessageBus<GrUniqueKeyInvalidatedMessage>::Inbox InvalidUniqueKeyInbox;
247 typedef SkTDPQueue<GrGpuResource*, CompareTimestamp, AccessResourceIndex> PurgeableQueue;
248 typedef SkTDArray<GrGpuResource*> ResourceArray;
249
250 // Whenever a resource is added to the cache or the result of a cache lookup, fTimestamp is
251 // assigned as the resource's timestamp and then incremented. fPurgeableQueue orders the
252 // purgeable resources by this value, and thus is used to purge resources in LRU order.
253 uint32_t fTimestamp;
254 PurgeableQueue fPurgeableQueue;
255 ResourceArray fNonpurgeableResources;
256
257 // This map holds all resources that can be used as scratch resources.
258 ScratchMap fScratchMap;
259 // This holds all resources that have unique keys.
260 UniqueHash fUniqueHash;
261
262 // our budget, used in purgeAsNeeded()
263 int fMaxCount;
264 size_t fMaxBytes;
265 int fMaxUnusedFlushes;
266
267 #if GR_CACHE_STATS
268 int fHighWaterCount;
269 size_t fHighWaterBytes;
270 int fBudgetedHighWaterCount;
271 size_t fBudgetedHighWaterBytes;
272 #endif
273
274 // our current stats for all resources
275 SkDEBUGCODE(int fCount;)
276 size_t fBytes;
277
278 // our current stats for resources that count against the budget
279 int fBudgetedCount;
280 size_t fBudgetedBytes;
281
282 PFOverBudgetCB fOverBudgetCB;
283 void* fOverBudgetData;
284
285 // We keep track of the "timestamps" of the last n flushes. If a resource hasn't been used in
286 // that time then we well preemptively purge it to reduce memory usage.
287 uint32_t* fFlushTimestamps;
288 int fLastFlushTimestampIndex;
289
290 InvalidUniqueKeyInbox fInvalidUniqueKeyInbox;
291
292 // This resource is allowed to be in the nonpurgeable array for the sake of validate() because
293 // we're in the midst of converting it to purgeable status.
294 SkDEBUGCODE(GrGpuResource* fNewlyPurgeableResourceForValidation;)
295 };
296
297 class GrResourceCache::ResourceAccess {
298 private:
ResourceAccess(GrResourceCache * cache)299 ResourceAccess(GrResourceCache* cache) : fCache(cache) { }
ResourceAccess(const ResourceAccess & that)300 ResourceAccess(const ResourceAccess& that) : fCache(that.fCache) { }
301 ResourceAccess& operator=(const ResourceAccess&); // unimpl
302
303 /**
304 * Insert a resource into the cache.
305 */
insertResource(GrGpuResource * resource)306 void insertResource(GrGpuResource* resource) { fCache->insertResource(resource); }
307
308 /**
309 * Removes a resource from the cache.
310 */
removeResource(GrGpuResource * resource)311 void removeResource(GrGpuResource* resource) { fCache->removeResource(resource); }
312
313 /**
314 * Notifications that should be sent to the cache when the ref/io cnt status of resources
315 * changes.
316 */
317 enum RefNotificationFlags {
318 /** All types of refs on the resource have reached zero. */
319 kAllCntsReachedZero_RefNotificationFlag = 0x1,
320 /** The normal (not pending IO type) ref cnt has reached zero. */
321 kRefCntReachedZero_RefNotificationFlag = 0x2,
322 };
323 /**
324 * Called by GrGpuResources when they detect that their ref/io cnts have reached zero. When the
325 * normal ref cnt reaches zero the flags that are set should be:
326 * a) kRefCntReachedZero if a pending IO cnt is still non-zero.
327 * b) (kRefCntReachedZero | kAllCntsReachedZero) when all pending IO cnts are also zero.
328 * kAllCntsReachedZero is set by itself if a pending IO cnt is decremented to zero and all the
329 * the other cnts are already zero.
330 */
notifyCntReachedZero(GrGpuResource * resource,uint32_t flags)331 void notifyCntReachedZero(GrGpuResource* resource, uint32_t flags) {
332 fCache->notifyCntReachedZero(resource, flags);
333 }
334
335 /**
336 * Called by GrGpuResources when their sizes change.
337 */
didChangeGpuMemorySize(const GrGpuResource * resource,size_t oldSize)338 void didChangeGpuMemorySize(const GrGpuResource* resource, size_t oldSize) {
339 fCache->didChangeGpuMemorySize(resource, oldSize);
340 }
341
342 /**
343 * Called by GrGpuResources to change their unique keys.
344 */
changeUniqueKey(GrGpuResource * resource,const GrUniqueKey & newKey)345 void changeUniqueKey(GrGpuResource* resource, const GrUniqueKey& newKey) {
346 fCache->changeUniqueKey(resource, newKey);
347 }
348
349 /**
350 * Called by a GrGpuResource to remove its unique key.
351 */
removeUniqueKey(GrGpuResource * resource)352 void removeUniqueKey(GrGpuResource* resource) { fCache->removeUniqueKey(resource); }
353
354 /**
355 * Called by a GrGpuResource when it removes its scratch key.
356 */
willRemoveScratchKey(const GrGpuResource * resource)357 void willRemoveScratchKey(const GrGpuResource* resource) {
358 fCache->willRemoveScratchKey(resource);
359 }
360
361 /**
362 * Called by GrGpuResources when they change from budgeted to unbudgeted or vice versa.
363 */
didChangeBudgetStatus(GrGpuResource * resource)364 void didChangeBudgetStatus(GrGpuResource* resource) { fCache->didChangeBudgetStatus(resource); }
365
366 // No taking addresses of this type.
367 const ResourceAccess* operator&() const;
368 ResourceAccess* operator&();
369
370 GrResourceCache* fCache;
371
372 friend class GrGpuResource; // To access all the proxy inline methods.
373 friend class GrResourceCache; // To create this type.
374 };
375
resourceAccess()376 inline GrResourceCache::ResourceAccess GrResourceCache::resourceAccess() {
377 return ResourceAccess(this);
378 }
379
380 #endif
381