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