1 /*
2  * Copyright 2014 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #ifndef GrResourceCache_DEFINED
9 #define GrResourceCache_DEFINED
10 
11 #include "GrGpuResource.h"
12 #include "GrGpuResourceCacheAccess.h"
13 #include "GrGpuResourcePriv.h"
14 #include "GrResourceCache.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 class GrResourceCache {
45 public:
46     GrResourceCache(const GrCaps* caps);
47     ~GrResourceCache();
48 
49     // Default maximum number of budgeted resources in the cache.
50     static const int    kDefaultMaxCount            = 2 * (1 << 12);
51     // Default maximum number of bytes of gpu memory of budgeted resources in the cache.
52     static const size_t kDefaultMaxSize             = 96 * (1 << 20);
53     // Default number of external flushes a budgeted resources can go unused in the cache before it
54     // is purged. Using a value <= 0 disables this feature. This will be removed once Chrome
55     // starts using time-based purging.
56     static const int    kDefaultMaxUnusedFlushes =
57             1  * /* flushes per frame */
58             60 * /* fps */
59             30;  /* seconds */
60 
61     /** Used to access functionality needed by GrGpuResource for lifetime management. */
62     class ResourceAccess;
63     ResourceAccess resourceAccess();
64 
65     /**
66      * Sets the cache limits in terms of number of resources, max gpu memory byte size, and number
67      * of external GrContext flushes that a resource can be unused before it is evicted. The latter
68      * value is a suggestion and there is no promise that a resource will be purged immediately
69      * after it hasn't been used in maxUnusedFlushes flushes.
70      */
71     void setLimits(int count, size_t bytes, int maxUnusedFlushes = kDefaultMaxUnusedFlushes);
72 
73     /**
74      * Returns the number of resources.
75      */
getResourceCount()76     int getResourceCount() const {
77         return fPurgeableQueue.count() + fNonpurgeableResources.count();
78     }
79 
80     /**
81      * Returns the number of resources that count against the budget.
82      */
getBudgetedResourceCount()83     int getBudgetedResourceCount() const { return fBudgetedCount; }
84 
85     /**
86      * Returns the number of bytes consumed by resources.
87      */
getResourceBytes()88     size_t getResourceBytes() const { return fBytes; }
89 
90     /**
91      * Returns the number of bytes consumed by budgeted resources.
92      */
getBudgetedResourceBytes()93     size_t getBudgetedResourceBytes() const { return fBudgetedBytes; }
94 
95     /**
96      * Returns the cached resources count budget.
97      */
getMaxResourceCount()98     int getMaxResourceCount() const { return fMaxCount; }
99 
100     /**
101      * Returns the number of bytes consumed by cached resources.
102      */
getMaxResourceBytes()103     size_t getMaxResourceBytes() const { return fMaxBytes; }
104 
105     /**
106      * Abandons the backend API resources owned by all GrGpuResource objects and removes them from
107      * the cache.
108      */
109     void abandonAll();
110 
111     /**
112      * Releases the backend API resources owned by all GrGpuResource objects and removes them from
113      * the cache.
114      */
115     void releaseAll();
116 
117     enum {
118         /** Preferentially returns scratch resources with no pending IO. */
119         kPreferNoPendingIO_ScratchFlag = 0x1,
120         /** Will not return any resources that match but have pending IO. */
121         kRequireNoPendingIO_ScratchFlag = 0x2,
122     };
123 
124     /**
125      * Find a resource that matches a scratch key.
126      */
127     GrGpuResource* findAndRefScratchResource(const GrScratchKey& scratchKey,
128                                              size_t resourceSize,
129                                              uint32_t flags);
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     /** Purge all resources not used since the passed in time. */
164     void purgeResourcesNotUsedSince(GrStdSteadyClock::time_point);
165 
166     /** Returns true if the cache would like a flush to occur in order to make more resources
167         purgeable. */
requestsFlush()168     bool requestsFlush() const { return fRequestFlush; }
169 
170     enum FlushType {
171         kExternal,
172         kImmediateMode,
173         kCacheRequested,
174     };
175     void notifyFlushOccurred(FlushType);
176 
177 #if GR_CACHE_STATS
178     struct Stats {
179         int fTotal;
180         int fNumPurgeable;
181         int fNumNonPurgeable;
182 
183         int fScratch;
184         int fWrapped;
185         size_t fUnbudgetedSize;
186 
StatsStats187         Stats() { this->reset(); }
188 
resetStats189         void reset() {
190             fTotal = 0;
191             fNumPurgeable = 0;
192             fNumNonPurgeable = 0;
193             fScratch = 0;
194             fWrapped = 0;
195             fUnbudgetedSize = 0;
196         }
197 
updateStats198         void update(GrGpuResource* resource) {
199             if (resource->cacheAccess().isScratch()) {
200                 ++fScratch;
201             }
202             if (resource->resourcePriv().refsWrappedObjects()) {
203                 ++fWrapped;
204             }
205             if (SkBudgeted::kNo  == resource->resourcePriv().isBudgeted()) {
206                 fUnbudgetedSize += resource->gpuMemorySize();
207             }
208         }
209     };
210 
211     void getStats(Stats*) const;
212 
213     void dumpStats(SkString*) const;
214 
215     void dumpStatsKeyValuePairs(SkTArray<SkString>* keys, SkTArray<double>* value) const;
216 #endif
217 
218 #ifdef SK_DEBUG
219     int countUniqueKeysWithTag(const char* tag) const;
220 #endif
221 
222     // This function is for unit testing and is only defined in test tools.
223     void changeTimestamp(uint32_t newTimestamp);
224 
225     // Enumerates all cached resources and dumps their details to traceMemoryDump.
226     void dumpMemoryStatistics(SkTraceMemoryDump* traceMemoryDump) const;
227 
228 private:
229     ///////////////////////////////////////////////////////////////////////////
230     /// @name Methods accessible via ResourceAccess
231     ////
232     void insertResource(GrGpuResource*);
233     void removeResource(GrGpuResource*);
234     void notifyCntReachedZero(GrGpuResource*, uint32_t flags);
235     void didChangeGpuMemorySize(const GrGpuResource*, size_t oldSize);
236     void changeUniqueKey(GrGpuResource*, const GrUniqueKey&);
237     void removeUniqueKey(GrGpuResource*);
238     void willRemoveScratchKey(const GrGpuResource*);
239     void didChangeBudgetStatus(GrGpuResource*);
240     void refAndMakeResourceMRU(GrGpuResource*);
241     /// @}
242 
243     void processInvalidUniqueKeys(const SkTArray<GrUniqueKeyInvalidatedMessage>&);
244     void addToNonpurgeableArray(GrGpuResource*);
245     void removeFromNonpurgeableArray(GrGpuResource*);
overBudget()246     bool overBudget() const { return fBudgetedBytes > fMaxBytes || fBudgetedCount > fMaxCount; }
247 
wouldFit(size_t bytes)248     bool wouldFit(size_t bytes) {
249         return fBudgetedBytes+bytes <= fMaxBytes && fBudgetedCount+1 <= fMaxCount;
250     }
251 
252     uint32_t getNextTimestamp();
253 
254 #ifdef SK_DEBUG
255     bool isInCache(const GrGpuResource* r) const;
256     void validate() const;
257 #else
validate()258     void validate() const {}
259 #endif
260 
261     class AutoValidate;
262 
263     class AvailableForScratchUse;
264 
265     struct ScratchMapTraits {
GetKeyScratchMapTraits266         static const GrScratchKey& GetKey(const GrGpuResource& r) {
267             return r.resourcePriv().getScratchKey();
268         }
269 
HashScratchMapTraits270         static uint32_t Hash(const GrScratchKey& key) { return key.hash(); }
271     };
272     typedef SkTMultiMap<GrGpuResource, GrScratchKey, ScratchMapTraits> ScratchMap;
273 
274     struct UniqueHashTraits {
GetKeyUniqueHashTraits275         static const GrUniqueKey& GetKey(const GrGpuResource& r) { return r.getUniqueKey(); }
276 
HashUniqueHashTraits277         static uint32_t Hash(const GrUniqueKey& key) { return key.hash(); }
278     };
279     typedef SkTDynamicHash<GrGpuResource, GrUniqueKey, UniqueHashTraits> UniqueHash;
280 
CompareTimestamp(GrGpuResource * const & a,GrGpuResource * const & b)281     static bool CompareTimestamp(GrGpuResource* const& a, GrGpuResource* const& b) {
282         return a->cacheAccess().timestamp() < b->cacheAccess().timestamp();
283     }
284 
AccessResourceIndex(GrGpuResource * const & res)285     static int* AccessResourceIndex(GrGpuResource* const& res) {
286         return res->cacheAccess().accessCacheIndex();
287     }
288 
289     typedef SkMessageBus<GrUniqueKeyInvalidatedMessage>::Inbox InvalidUniqueKeyInbox;
290     typedef SkTDPQueue<GrGpuResource*, CompareTimestamp, AccessResourceIndex> PurgeableQueue;
291     typedef SkTDArray<GrGpuResource*> ResourceArray;
292 
293     // Whenever a resource is added to the cache or the result of a cache lookup, fTimestamp is
294     // assigned as the resource's timestamp and then incremented. fPurgeableQueue orders the
295     // purgeable resources by this value, and thus is used to purge resources in LRU order.
296     uint32_t                            fTimestamp;
297     PurgeableQueue                      fPurgeableQueue;
298     ResourceArray                       fNonpurgeableResources;
299 
300     // This map holds all resources that can be used as scratch resources.
301     ScratchMap                          fScratchMap;
302     // This holds all resources that have unique keys.
303     UniqueHash                          fUniqueHash;
304 
305     // our budget, used in purgeAsNeeded()
306     int                                 fMaxCount;
307     size_t                              fMaxBytes;
308     int                                 fMaxUnusedFlushes;
309 
310 #if GR_CACHE_STATS
311     int                                 fHighWaterCount;
312     size_t                              fHighWaterBytes;
313     int                                 fBudgetedHighWaterCount;
314     size_t                              fBudgetedHighWaterBytes;
315 #endif
316 
317     // our current stats for all resources
318     SkDEBUGCODE(int                     fCount;)
319     size_t                              fBytes;
320 
321     // our current stats for resources that count against the budget
322     int                                 fBudgetedCount;
323     size_t                              fBudgetedBytes;
324 
325     bool                                fRequestFlush;
326     uint32_t                            fExternalFlushCnt;
327 
328     InvalidUniqueKeyInbox               fInvalidUniqueKeyInbox;
329 
330     // This resource is allowed to be in the nonpurgeable array for the sake of validate() because
331     // we're in the midst of converting it to purgeable status.
332     SkDEBUGCODE(GrGpuResource*          fNewlyPurgeableResourceForValidation;)
333 
334     bool                                fPreferVRAMUseOverFlushes;
335 };
336 
337 class GrResourceCache::ResourceAccess {
338 private:
ResourceAccess(GrResourceCache * cache)339     ResourceAccess(GrResourceCache* cache) : fCache(cache) { }
ResourceAccess(const ResourceAccess & that)340     ResourceAccess(const ResourceAccess& that) : fCache(that.fCache) { }
341     ResourceAccess& operator=(const ResourceAccess&); // unimpl
342 
343     /**
344      * Insert a resource into the cache.
345      */
insertResource(GrGpuResource * resource)346     void insertResource(GrGpuResource* resource) { fCache->insertResource(resource); }
347 
348     /**
349      * Removes a resource from the cache.
350      */
removeResource(GrGpuResource * resource)351     void removeResource(GrGpuResource* resource) { fCache->removeResource(resource); }
352 
353     /**
354      * Notifications that should be sent to the cache when the ref/io cnt status of resources
355      * changes.
356      */
357     enum RefNotificationFlags {
358         /** All types of refs on the resource have reached zero. */
359         kAllCntsReachedZero_RefNotificationFlag = 0x1,
360         /** The normal (not pending IO type) ref cnt has reached zero. */
361         kRefCntReachedZero_RefNotificationFlag  = 0x2,
362     };
363     /**
364      * Called by GrGpuResources when they detect that their ref/io cnts have reached zero. When the
365      * normal ref cnt reaches zero the flags that are set should be:
366      *     a) kRefCntReachedZero if a pending IO cnt is still non-zero.
367      *     b) (kRefCntReachedZero | kAllCntsReachedZero) when all pending IO cnts are also zero.
368      * kAllCntsReachedZero is set by itself if a pending IO cnt is decremented to zero and all the
369      * the other cnts are already zero.
370      */
notifyCntReachedZero(GrGpuResource * resource,uint32_t flags)371     void notifyCntReachedZero(GrGpuResource* resource, uint32_t flags) {
372         fCache->notifyCntReachedZero(resource, flags);
373     }
374 
375     /**
376      * Called by GrGpuResources when their sizes change.
377      */
didChangeGpuMemorySize(const GrGpuResource * resource,size_t oldSize)378     void didChangeGpuMemorySize(const GrGpuResource* resource, size_t oldSize) {
379         fCache->didChangeGpuMemorySize(resource, oldSize);
380     }
381 
382     /**
383      * Called by GrGpuResources to change their unique keys.
384      */
changeUniqueKey(GrGpuResource * resource,const GrUniqueKey & newKey)385     void changeUniqueKey(GrGpuResource* resource, const GrUniqueKey& newKey) {
386          fCache->changeUniqueKey(resource, newKey);
387     }
388 
389     /**
390      * Called by a GrGpuResource to remove its unique key.
391      */
removeUniqueKey(GrGpuResource * resource)392     void removeUniqueKey(GrGpuResource* resource) { fCache->removeUniqueKey(resource); }
393 
394     /**
395      * Called by a GrGpuResource when it removes its scratch key.
396      */
willRemoveScratchKey(const GrGpuResource * resource)397     void willRemoveScratchKey(const GrGpuResource* resource) {
398         fCache->willRemoveScratchKey(resource);
399     }
400 
401     /**
402      * Called by GrGpuResources when they change from budgeted to unbudgeted or vice versa.
403      */
didChangeBudgetStatus(GrGpuResource * resource)404     void didChangeBudgetStatus(GrGpuResource* resource) { fCache->didChangeBudgetStatus(resource); }
405 
406     // No taking addresses of this type.
407     const ResourceAccess* operator&() const;
408     ResourceAccess* operator&();
409 
410     GrResourceCache* fCache;
411 
412     friend class GrGpuResource; // To access all the proxy inline methods.
413     friend class GrResourceCache; // To create this type.
414 };
415 
resourceAccess()416 inline GrResourceCache::ResourceAccess GrResourceCache::resourceAccess() {
417     return ResourceAccess(this);
418 }
419 
420 #endif
421