• 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 "include/core/SkRefCnt.h"
12 #include "include/core/SkTypes.h"
13 #include "include/gpu/ganesh/GrDirectContext.h"
14 #include "include/private/base/SkDebug.h"
15 #include "include/private/base/SkTArray.h"
16 #include "include/private/base/SkTDArray.h"
17 #include "include/private/base/SkTo.h"
18 #include "include/private/gpu/ganesh/GrTypesPriv.h"
19 #include "src/base/SkTDPQueue.h"
20 #include "src/core/SkMessageBus.h"
21 #include "src/core/SkTDynamicHash.h"
22 #include "src/core/SkTMultiMap.h"
23 #include "src/gpu/GpuTypesPriv.h"
24 #include "src/gpu/ResourceKey.h"
25 #include "src/gpu/ganesh/GrGpuResource.h"
26 #include "src/gpu/ganesh/GrGpuResourceCacheAccess.h"
27 #include "src/gpu/ganesh/GrGpuResourcePriv.h"
28 
29 #include <cstddef>
30 #include <cstdint>
31 #include <functional>
32 #include <type_traits>
33 #include <utility>
34 
35 class GrProxyProvider;
36 class GrSurface;
37 class GrThreadSafeCache;
38 class SkString;
39 class SkTraceMemoryDump;
40 enum class GrPurgeResourceOptions;
41 namespace skgpu {
42 class SingleOwner;
43 }
44 
45 /**
46  * Manages the lifetime of all GrGpuResource instances.
47  *
48  * Resources may have optionally have two types of keys:
49  *      1) A scratch key. This is for resources whose allocations are cached but not their contents.
50  *         Multiple resources can share the same scratch key. This is so a caller can have two
51  *         resource instances with the same properties (e.g. multipass rendering that ping-pongs
52  *         between two temporary surfaces). The scratch key is set at resource creation time and
53  *         should never change. Resources need not have a scratch key.
54  *      2) A unique key. This key's meaning is specific to the domain that created the key. Only one
55  *         resource may have a given unique key. The unique key can be set, cleared, or changed
56  *         anytime after resource creation.
57  *
58  * A unique key always takes precedence over a scratch key when a resource has both types of keys.
59  * If a resource has neither key type then it will be deleted as soon as the last reference to it
60  * is dropped.
61  */
62 class GrResourceCache {
63 public:
64     GrResourceCache(skgpu::SingleOwner* owner,
65                     GrDirectContext::DirectContextID owningContextID,
66                     uint32_t familyID);
67     ~GrResourceCache();
68 
69     /**
70      * This is used to safely return a resource to the cache when the owner may be on another
71      * thread from GrDirectContext. If the context still exists then using this method ensures that
72      * the resource is received by the cache for processing (recycling or destruction) on the
73      * context's thread.
74      *
75      * This is templated as it is rather than simply taking sk_sp<GrGpuResource> in order to enforce
76      * that the caller passes an rvalue. If the caller doesn't move its ref into this function
77      * then it will retain ownership, defeating the purpose. (Note that sk_sp<GrGpuResource>&&
78      * doesn't work either because calling with sk_sp<GrSpecificResource> will create a temporary
79      * sk_sp<GrGpuResource> which is an rvalue).
80      */
81     template<typename T>
82     static std::enable_if_t<std::is_base_of_v<GrGpuResource, T>, void>
ReturnResourceFromThread(sk_sp<T> && resource,GrDirectContext::DirectContextID id)83     ReturnResourceFromThread(sk_sp<T>&& resource, GrDirectContext::DirectContextID id) {
84         UnrefResourceMessage msg(std::move(resource), id);
85         UnrefResourceMessage::Bus::Post(std::move(msg));
86     }
87 
88     // Default maximum number of bytes of gpu memory of budgeted resources in the cache.
89     static const size_t kDefaultMaxSize             = 256 * (1 << 20);
90 
91     /** Used to access functionality needed by GrGpuResource for lifetime management. */
92     class ResourceAccess;
93     ResourceAccess resourceAccess();
94 
95     /** Unique ID of the owning GrContext. */
contextUniqueID()96     uint32_t contextUniqueID() const { return fContextUniqueID; }
97 
98     /** Sets the max gpu memory byte size of the cache. */
99     void setLimit(size_t bytes);
100 
101     /**
102      * Returns the number of resources.
103      */
getResourceCount()104     int getResourceCount() const {
105         return fPurgeableQueue.count() + fNonpurgeableResources.size();
106     }
107 
108     /**
109      * Returns the number of resources that count against the budget.
110      */
getBudgetedResourceCount()111     int getBudgetedResourceCount() const { return fBudgetedCount; }
112 
113     /**
114      * Returns the number of bytes consumed by resources.
115      */
getResourceBytes()116     size_t getResourceBytes() const { return fBytes; }
117 
118     /**
119      * Returns the number of bytes held by unlocked resources which are available for purging.
120      */
getPurgeableBytes()121     size_t getPurgeableBytes() const { return fPurgeableBytes; }
122 
123     /**
124      * Returns the number of bytes consumed by budgeted resources.
125      */
getBudgetedResourceBytes()126     size_t getBudgetedResourceBytes() const { return fBudgetedBytes; }
127 
128     /**
129      * Returns the number of bytes consumed by cached resources.
130      */
getMaxResourceBytes()131     size_t getMaxResourceBytes() const { return fMaxBytes; }
132 
133     /**
134      * Abandons the backend API resources owned by all GrGpuResource objects and removes them from
135      * the cache.
136      */
137     void abandonAll();
138 
139     /**
140      * Releases the backend API resources owned by all GrGpuResource objects and removes them from
141      * the cache.
142      */
143     void releaseAll();
144 
145     /**
146      * Find a resource that matches a scratch key.
147      */
148     GrGpuResource* findAndRefScratchResource(const skgpu::ScratchKey& scratchKey);
149 
150 #ifdef SK_DEBUG
151     // This is not particularly fast and only used for validation, so debug only.
countScratchEntriesForKey(const skgpu::ScratchKey & scratchKey)152     int countScratchEntriesForKey(const skgpu::ScratchKey& scratchKey) const {
153         return fScratchMap.countForKey(scratchKey);
154     }
155 #endif
156 
157     /**
158      * Find a resource that matches a unique key.
159      */
findAndRefUniqueResource(const skgpu::UniqueKey & key)160     GrGpuResource* findAndRefUniqueResource(const skgpu::UniqueKey& key) {
161         GrGpuResource* resource = fUniqueHash.find(key);
162         if (resource) {
163             this->refAndMakeResourceMRU(resource);
164         }
165         return resource;
166     }
167 
168     /**
169      * Query whether a unique key exists in the cache.
170      */
hasUniqueKey(const skgpu::UniqueKey & key)171     bool hasUniqueKey(const skgpu::UniqueKey& key) const {
172         return SkToBool(fUniqueHash.find(key));
173     }
174 
175     /** Purges resources to become under budget and processes resources with invalidated unique
176         keys. */
177     void purgeAsNeeded();
178 
179     // Purge unlocked resources. If 'opts' is kScratchResourcesOnly, the purgeable resources
180     // containing persistent data are spared. If it is kAllResources then all purgeable resources
181     // will be deleted.
purgeUnlockedResources(GrPurgeResourceOptions opts)182     void purgeUnlockedResources(GrPurgeResourceOptions opts) {
183         this->purgeUnlockedResources(/*purgeTime=*/nullptr, opts);
184     }
185 
186     // Purge unlocked resources not used since the passed point in time. If 'opts' is
187     // kScratchResourcesOnly, the purgeable resources containing persistent data are spared.
188     // If it is kAllResources then all purgeable resources older than 'purgeTime' will be deleted.
purgeResourcesNotUsedSince(skgpu::StdSteadyClock::time_point purgeTime,GrPurgeResourceOptions opts)189     void purgeResourcesNotUsedSince(skgpu::StdSteadyClock::time_point purgeTime,
190                                     GrPurgeResourceOptions opts) {
191         this->purgeUnlockedResources(&purgeTime, opts);
192     }
193 
194     /** If it's possible to purge enough resources to get the provided amount of budget
195         headroom, do so and return true. If it's not possible, do nothing and return false.
196      */
197     bool purgeToMakeHeadroom(size_t desiredHeadroomBytes);
198 
overBudget()199     bool overBudget() const { return fBudgetedBytes > fMaxBytes; }
200 
201     /**
202      * Purge unlocked resources from the cache until the the provided byte count has been reached
203      * or we have purged all unlocked resources. The default policy is to purge in LRU order, but
204      * can be overridden to prefer purging scratch resources (in LRU order) prior to purging other
205      * resource types.
206      *
207      * @param maxBytesToPurge the desired number of bytes to be purged.
208      * @param preferScratchResources If true scratch resources will be purged prior to other
209      *                               resource types.
210      */
211     void purgeUnlockedResources(size_t bytesToPurge, bool preferScratchResources);
212 
213 #if GR_CACHE_STATS
214     struct Stats {
215         int fTotal;
216         int fNumPurgeable;
217         int fNumNonPurgeable;
218 
219         int fScratch;
220         int fWrapped;
221         size_t fUnbudgetedSize;
222 
StatsStats223         Stats() { this->reset(); }
224 
resetStats225         void reset() {
226             fTotal = 0;
227             fNumPurgeable = 0;
228             fNumNonPurgeable = 0;
229             fScratch = 0;
230             fWrapped = 0;
231             fUnbudgetedSize = 0;
232         }
233 
updateStats234         void update(GrGpuResource* resource) {
235             if (resource->cacheAccess().isScratch()) {
236                 ++fScratch;
237             }
238             if (resource->resourcePriv().refsWrappedObjects()) {
239                 ++fWrapped;
240             }
241             if (GrBudgetedType::kBudgeted != resource->resourcePriv().budgetedType()) {
242                 fUnbudgetedSize += resource->gpuMemorySize();
243             }
244         }
245     };
246 
247     void getStats(Stats*) const;
248 
249 #if defined(GPU_TEST_UTILS)
250     void dumpStats(SkString*) const;
251 
252     void dumpStatsKeyValuePairs(
253             skia_private::TArray<SkString>* keys, skia_private::TArray<double>* value) const;
254 #endif
255 
256 #endif // GR_CACHE_STATS
257 
258 #if defined(GPU_TEST_UTILS)
259     int countUniqueKeysWithTag(const char* tag) const;
260 
261     void changeTimestamp(uint32_t newTimestamp);
262 
263     void visitSurfaces(const std::function<void(const GrSurface*, bool purgeable)>&) const;
264 #endif
265 
266     // Enumerates all cached resources and dumps their details to traceMemoryDump.
267     void dumpMemoryStatistics(SkTraceMemoryDump* traceMemoryDump) const;
268 
setProxyProvider(GrProxyProvider * proxyProvider)269     void setProxyProvider(GrProxyProvider* proxyProvider) { fProxyProvider = proxyProvider; }
setThreadSafeCache(GrThreadSafeCache * threadSafeCache)270     void setThreadSafeCache(GrThreadSafeCache* threadSafeCache) {
271         fThreadSafeCache = threadSafeCache;
272     }
273 
274     // It'd be nice if this could be private but SkMessageBus relies on macros to define types that
275     // require this to be public.
276     class UnrefResourceMessage {
277     public:
recipient()278         GrDirectContext::DirectContextID recipient() const { return fRecipient; }
279 
280         UnrefResourceMessage(UnrefResourceMessage&&) = default;
281         UnrefResourceMessage& operator=(UnrefResourceMessage&&) = default;
282 
283     private:
284         friend class GrResourceCache;
285 
286         using Bus = SkMessageBus<UnrefResourceMessage,
287                                  GrDirectContext::DirectContextID,
288                                  /*AllowCopyableMessage=*/false>;
289 
UnrefResourceMessage(sk_sp<GrGpuResource> && resource,GrDirectContext::DirectContextID recipient)290         UnrefResourceMessage(sk_sp<GrGpuResource>&& resource,
291                              GrDirectContext::DirectContextID recipient)
292                 : fResource(std::move(resource)), fRecipient(recipient) {}
293 
294         UnrefResourceMessage(const UnrefResourceMessage&) = delete;
295         UnrefResourceMessage& operator=(const UnrefResourceMessage&) = delete;
296 
297         sk_sp<GrGpuResource> fResource;
298         GrDirectContext::DirectContextID fRecipient;
299     };
300 
301 private:
302     ///////////////////////////////////////////////////////////////////////////
303     /// @name Methods accessible via ResourceAccess
304     ////
305     void insertResource(GrGpuResource*);
306     void removeResource(GrGpuResource*);
307     void notifyARefCntReachedZero(GrGpuResource*, GrGpuResource::LastRemovedRef);
308     void changeUniqueKey(GrGpuResource*, const skgpu::UniqueKey&);
309     void removeUniqueKey(GrGpuResource*);
310     void willRemoveScratchKey(const GrGpuResource*);
311     void didChangeBudgetStatus(GrGpuResource*);
312     void refResource(GrGpuResource* resource);
313     /// @}
314 
315     void refAndMakeResourceMRU(GrGpuResource*);
316     void processFreedGpuResources();
317     void addToNonpurgeableArray(GrGpuResource*);
318     void removeFromNonpurgeableArray(GrGpuResource*);
319 
wouldFit(size_t bytes)320     bool wouldFit(size_t bytes) const { return fBudgetedBytes+bytes <= fMaxBytes; }
321 
322     uint32_t getNextTimestamp();
323 
324     void purgeUnlockedResources(const skgpu::StdSteadyClock::time_point* purgeTime,
325                                 GrPurgeResourceOptions opts);
326 
327 #ifdef SK_DEBUG
328     bool isInCache(const GrGpuResource* r) const;
329     void validate() const;
330 #else
validate()331     void validate() const {}
332 #endif
333 
334     class AutoValidate;
335 
336     struct ScratchMapTraits {
GetKeyScratchMapTraits337         static const skgpu::ScratchKey& GetKey(const GrGpuResource& r) {
338             return r.resourcePriv().getScratchKey();
339         }
340 
HashScratchMapTraits341         static uint32_t Hash(const skgpu::ScratchKey& key) { return key.hash(); }
OnFreeScratchMapTraits342         static void OnFree(GrGpuResource*) { }
343     };
344     typedef SkTMultiMap<GrGpuResource, skgpu::ScratchKey, ScratchMapTraits> ScratchMap;
345 
346     struct UniqueHashTraits {
GetKeyUniqueHashTraits347         static const skgpu::UniqueKey& GetKey(const GrGpuResource& r) { return r.getUniqueKey(); }
348 
HashUniqueHashTraits349         static uint32_t Hash(const skgpu::UniqueKey& key) { return key.hash(); }
350     };
351     typedef SkTDynamicHash<GrGpuResource, skgpu::UniqueKey, UniqueHashTraits> UniqueHash;
352 
CompareTimestamp(GrGpuResource * const & a,GrGpuResource * const & b)353     static bool CompareTimestamp(GrGpuResource* const& a, GrGpuResource* const& b) {
354         return a->cacheAccess().timestamp() < b->cacheAccess().timestamp();
355     }
356 
AccessResourceIndex(GrGpuResource * const & res)357     static int* AccessResourceIndex(GrGpuResource* const& res) {
358         return res->cacheAccess().accessCacheIndex();
359     }
360 
361     typedef SkMessageBus<skgpu::UniqueKeyInvalidatedMessage, uint32_t>::Inbox InvalidUniqueKeyInbox;
362     typedef SkTDPQueue<GrGpuResource*, CompareTimestamp, AccessResourceIndex> PurgeableQueue;
363     typedef SkTDArray<GrGpuResource*> ResourceArray;
364 
365     GrProxyProvider*                    fProxyProvider = nullptr;
366     GrThreadSafeCache*                  fThreadSafeCache = nullptr;
367 
368     // Whenever a resource is added to the cache or the result of a cache lookup, fTimestamp is
369     // assigned as the resource's timestamp and then incremented. fPurgeableQueue orders the
370     // purgeable resources by this value, and thus is used to purge resources in LRU order.
371     uint32_t                            fTimestamp = 0;
372     PurgeableQueue                      fPurgeableQueue;
373     ResourceArray                       fNonpurgeableResources;
374 
375     // This map holds all resources that can be used as scratch resources.
376     ScratchMap                          fScratchMap;
377     // This holds all resources that have unique keys.
378     UniqueHash                          fUniqueHash;
379 
380     // our budget, used in purgeAsNeeded()
381     size_t                              fMaxBytes = kDefaultMaxSize;
382 
383 #if GR_CACHE_STATS
384     int                                 fHighWaterCount = 0;
385     size_t                              fHighWaterBytes = 0;
386     int                                 fBudgetedHighWaterCount = 0;
387     size_t                              fBudgetedHighWaterBytes = 0;
388 #endif
389 
390     // our current stats for all resources
391     SkDEBUGCODE(int                     fCount = 0;)
392     size_t                              fBytes = 0;
393 
394     // our current stats for resources that count against the budget
395     int                                 fBudgetedCount = 0;
396     size_t                              fBudgetedBytes = 0;
397     size_t                              fPurgeableBytes = 0;
398 
399     InvalidUniqueKeyInbox               fInvalidUniqueKeyInbox;
400     UnrefResourceMessage::Bus::Inbox    fUnrefResourceInbox;
401 
402     GrDirectContext::DirectContextID    fOwningContextID;
403     uint32_t                            fContextUniqueID = SK_InvalidUniqueID;
404     skgpu::SingleOwner*                 fSingleOwner = nullptr;
405 
406     // This resource is allowed to be in the nonpurgeable array for the sake of validate() because
407     // we're in the midst of converting it to purgeable status.
408     SkDEBUGCODE(GrGpuResource*          fNewlyPurgeableResourceForValidation = nullptr;)
409 };
410 
411 class GrResourceCache::ResourceAccess {
412 private:
ResourceAccess(GrResourceCache * cache)413     ResourceAccess(GrResourceCache* cache) : fCache(cache) { }
ResourceAccess(const ResourceAccess & that)414     ResourceAccess(const ResourceAccess& that) : fCache(that.fCache) { }
415     ResourceAccess& operator=(const ResourceAccess&) = delete;
416 
417     /**
418      * Insert a resource into the cache.
419      */
insertResource(GrGpuResource * resource)420     void insertResource(GrGpuResource* resource) { fCache->insertResource(resource); }
421 
422     /**
423      * Removes a resource from the cache.
424      */
removeResource(GrGpuResource * resource)425     void removeResource(GrGpuResource* resource) { fCache->removeResource(resource); }
426 
427     /**
428      * Adds a ref to a resource with proper tracking if the resource has 0 refs prior to
429      * adding the ref.
430      */
refResource(GrGpuResource * resource)431     void refResource(GrGpuResource* resource) { fCache->refResource(resource); }
432 
433     /**
434      * Notifications that should be sent to the cache when the ref/io cnt status of resources
435      * changes.
436      */
437     enum RefNotificationFlags {
438         /** All types of refs on the resource have reached zero. */
439         kAllCntsReachedZero_RefNotificationFlag = 0x1,
440         /** The normal (not pending IO type) ref cnt has reached zero. */
441         kRefCntReachedZero_RefNotificationFlag  = 0x2,
442     };
443     /**
444      * Called by GrGpuResources when they detect one of their ref cnts have reached zero. This may
445      * either be the main ref or the command buffer usage ref.
446      */
notifyARefCntReachedZero(GrGpuResource * resource,GrGpuResource::LastRemovedRef removedRef)447     void notifyARefCntReachedZero(GrGpuResource* resource,
448                                   GrGpuResource::LastRemovedRef removedRef) {
449         fCache->notifyARefCntReachedZero(resource, removedRef);
450     }
451 
452     /**
453      * Called by GrGpuResources to change their unique keys.
454      */
changeUniqueKey(GrGpuResource * resource,const skgpu::UniqueKey & newKey)455     void changeUniqueKey(GrGpuResource* resource, const skgpu::UniqueKey& newKey) {
456          fCache->changeUniqueKey(resource, newKey);
457     }
458 
459     /**
460      * Called by a GrGpuResource to remove its unique key.
461      */
removeUniqueKey(GrGpuResource * resource)462     void removeUniqueKey(GrGpuResource* resource) { fCache->removeUniqueKey(resource); }
463 
464     /**
465      * Called by a GrGpuResource when it removes its scratch key.
466      */
willRemoveScratchKey(const GrGpuResource * resource)467     void willRemoveScratchKey(const GrGpuResource* resource) {
468         fCache->willRemoveScratchKey(resource);
469     }
470 
471     /**
472      * Called by GrGpuResources when they change from budgeted to unbudgeted or vice versa.
473      */
didChangeBudgetStatus(GrGpuResource * resource)474     void didChangeBudgetStatus(GrGpuResource* resource) { fCache->didChangeBudgetStatus(resource); }
475 
476     // No taking addresses of this type.
477     const ResourceAccess* operator&() const;
478     ResourceAccess* operator&();
479 
480     GrResourceCache* fCache;
481 
482     friend class GrGpuResource; // To access all the proxy inline methods.
483     friend class GrResourceCache; // To create this type.
484 };
485 
SkShouldPostMessageToBus(const GrResourceCache::UnrefResourceMessage & msg,GrDirectContext::DirectContextID potentialRecipient)486 static inline bool SkShouldPostMessageToBus(const GrResourceCache::UnrefResourceMessage& msg,
487                                             GrDirectContext::DirectContextID potentialRecipient) {
488     return potentialRecipient == msg.recipient();
489 }
490 
resourceAccess()491 inline GrResourceCache::ResourceAccess GrResourceCache::resourceAccess() {
492     return ResourceAccess(this);
493 }
494 
495 #endif
496