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