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