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