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 'opts' is kScratchResourcesOnly, the purgeable resources
171 // containing persistent data are spared. If it is kAllResources then all purgeable resources
172 // will be deleted.
purgeUnlockedResources(GrPurgeResourceOptions opts)173 void purgeUnlockedResources(GrPurgeResourceOptions opts) {
174 this->purgeUnlockedResources(/*purgeTime=*/nullptr, opts);
175 }
176
177 // Purge unlocked resources not used since the passed point in time. If 'opts' is
178 // kScratchResourcesOnly, the purgeable resources containing persistent data are spared.
179 // If it is kAllResources then all purgeable resources older than 'purgeTime' will be deleted.
purgeResourcesNotUsedSince(skgpu::StdSteadyClock::time_point purgeTime,GrPurgeResourceOptions opts)180 void purgeResourcesNotUsedSince(skgpu::StdSteadyClock::time_point purgeTime,
181 GrPurgeResourceOptions opts) {
182 this->purgeUnlockedResources(&purgeTime, opts);
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 defined(GR_TEST_UTILS)
245 void dumpStats(SkString*) const;
246
247 void dumpStatsKeyValuePairs(
248 skia_private::TArray<SkString>* keys, skia_private::TArray<double>* value) const;
249 #endif
250
251 #endif // GR_CACHE_STATS
252
253 #if defined(GR_TEST_UTILS)
254 int countUniqueKeysWithTag(const char* tag) const;
255
256 void changeTimestamp(uint32_t newTimestamp);
257
258 void visitSurfaces(const std::function<void(const GrSurface*, bool purgeable)>&) const;
259 #endif
260
261 // Enumerates all cached resources and dumps their details to traceMemoryDump.
262 void dumpMemoryStatistics(SkTraceMemoryDump* traceMemoryDump) const;
263
setProxyProvider(GrProxyProvider * proxyProvider)264 void setProxyProvider(GrProxyProvider* proxyProvider) { fProxyProvider = proxyProvider; }
setThreadSafeCache(GrThreadSafeCache * threadSafeCache)265 void setThreadSafeCache(GrThreadSafeCache* threadSafeCache) {
266 fThreadSafeCache = threadSafeCache;
267 }
268
269 // It'd be nice if this could be private but SkMessageBus relies on macros to define types that
270 // require this to be public.
271 class UnrefResourceMessage {
272 public:
recipient()273 GrDirectContext::DirectContextID recipient() const { return fRecipient; }
274
275 UnrefResourceMessage(UnrefResourceMessage&&) = default;
276 UnrefResourceMessage& operator=(UnrefResourceMessage&&) = default;
277
278 private:
279 friend class GrResourceCache;
280
281 using Bus = SkMessageBus<UnrefResourceMessage,
282 GrDirectContext::DirectContextID,
283 /*AllowCopyableMessage=*/false>;
284
UnrefResourceMessage(sk_sp<GrGpuResource> && resource,GrDirectContext::DirectContextID recipient)285 UnrefResourceMessage(sk_sp<GrGpuResource>&& resource,
286 GrDirectContext::DirectContextID recipient)
287 : fResource(std::move(resource)), fRecipient(recipient) {}
288
289 UnrefResourceMessage(const UnrefResourceMessage&) = delete;
290 UnrefResourceMessage& operator=(const UnrefResourceMessage&) = delete;
291
292 sk_sp<GrGpuResource> fResource;
293 GrDirectContext::DirectContextID fRecipient;
294 };
295
296 private:
297 ///////////////////////////////////////////////////////////////////////////
298 /// @name Methods accessible via ResourceAccess
299 ////
300 void insertResource(GrGpuResource*);
301 void removeResource(GrGpuResource*);
302 void notifyARefCntReachedZero(GrGpuResource*, GrGpuResource::LastRemovedRef);
303 void changeUniqueKey(GrGpuResource*, const skgpu::UniqueKey&);
304 void removeUniqueKey(GrGpuResource*);
305 void willRemoveScratchKey(const GrGpuResource*);
306 void didChangeBudgetStatus(GrGpuResource*);
307 void refResource(GrGpuResource* resource);
308 /// @}
309
310 void refAndMakeResourceMRU(GrGpuResource*);
311 void processFreedGpuResources();
312 void addToNonpurgeableArray(GrGpuResource*);
313 void removeFromNonpurgeableArray(GrGpuResource*);
314
wouldFit(size_t bytes)315 bool wouldFit(size_t bytes) const { return fBudgetedBytes+bytes <= fMaxBytes; }
316
317 uint32_t getNextTimestamp();
318
319 void purgeUnlockedResources(const skgpu::StdSteadyClock::time_point* purgeTime,
320 GrPurgeResourceOptions opts);
321
322 #ifdef SK_DEBUG
323 bool isInCache(const GrGpuResource* r) const;
324 void validate() const;
325 #else
validate()326 void validate() const {}
327 #endif
328
329 class AutoValidate;
330
331 struct ScratchMapTraits {
GetKeyScratchMapTraits332 static const skgpu::ScratchKey& GetKey(const GrGpuResource& r) {
333 return r.resourcePriv().getScratchKey();
334 }
335
HashScratchMapTraits336 static uint32_t Hash(const skgpu::ScratchKey& key) { return key.hash(); }
OnFreeScratchMapTraits337 static void OnFree(GrGpuResource*) { }
338 };
339 typedef SkTMultiMap<GrGpuResource, skgpu::ScratchKey, ScratchMapTraits> ScratchMap;
340
341 struct UniqueHashTraits {
GetKeyUniqueHashTraits342 static const skgpu::UniqueKey& GetKey(const GrGpuResource& r) { return r.getUniqueKey(); }
343
HashUniqueHashTraits344 static uint32_t Hash(const skgpu::UniqueKey& key) { return key.hash(); }
345 };
346 typedef SkTDynamicHash<GrGpuResource, skgpu::UniqueKey, UniqueHashTraits> UniqueHash;
347
CompareTimestamp(GrGpuResource * const & a,GrGpuResource * const & b)348 static bool CompareTimestamp(GrGpuResource* const& a, GrGpuResource* const& b) {
349 return a->cacheAccess().timestamp() < b->cacheAccess().timestamp();
350 }
351
AccessResourceIndex(GrGpuResource * const & res)352 static int* AccessResourceIndex(GrGpuResource* const& res) {
353 return res->cacheAccess().accessCacheIndex();
354 }
355
356 typedef SkMessageBus<skgpu::UniqueKeyInvalidatedMessage, uint32_t>::Inbox InvalidUniqueKeyInbox;
357 typedef SkTDPQueue<GrGpuResource*, CompareTimestamp, AccessResourceIndex> PurgeableQueue;
358 typedef SkTDArray<GrGpuResource*> ResourceArray;
359
360 GrProxyProvider* fProxyProvider = nullptr;
361 GrThreadSafeCache* fThreadSafeCache = nullptr;
362
363 // Whenever a resource is added to the cache or the result of a cache lookup, fTimestamp is
364 // assigned as the resource's timestamp and then incremented. fPurgeableQueue orders the
365 // purgeable resources by this value, and thus is used to purge resources in LRU order.
366 uint32_t fTimestamp = 0;
367 PurgeableQueue fPurgeableQueue;
368 ResourceArray fNonpurgeableResources;
369
370 // This map holds all resources that can be used as scratch resources.
371 ScratchMap fScratchMap;
372 // This holds all resources that have unique keys.
373 UniqueHash fUniqueHash;
374
375 // our budget, used in purgeAsNeeded()
376 size_t fMaxBytes = kDefaultMaxSize;
377
378 #if GR_CACHE_STATS
379 int fHighWaterCount = 0;
380 size_t fHighWaterBytes = 0;
381 int fBudgetedHighWaterCount = 0;
382 size_t fBudgetedHighWaterBytes = 0;
383 #endif
384
385 // our current stats for all resources
386 SkDEBUGCODE(int fCount = 0;)
387 size_t fBytes = 0;
388
389 // our current stats for resources that count against the budget
390 int fBudgetedCount = 0;
391 size_t fBudgetedBytes = 0;
392 size_t fPurgeableBytes = 0;
393 int fNumBudgetedResourcesFlushWillMakePurgeable = 0;
394
395 InvalidUniqueKeyInbox fInvalidUniqueKeyInbox;
396 UnrefResourceMessage::Bus::Inbox fUnrefResourceInbox;
397
398 GrDirectContext::DirectContextID fOwningContextID;
399 uint32_t fContextUniqueID = SK_InvalidUniqueID;
400 skgpu::SingleOwner* fSingleOwner = nullptr;
401
402 // This resource is allowed to be in the nonpurgeable array for the sake of validate() because
403 // we're in the midst of converting it to purgeable status.
404 SkDEBUGCODE(GrGpuResource* fNewlyPurgeableResourceForValidation = nullptr;)
405 };
406
407 class GrResourceCache::ResourceAccess {
408 private:
ResourceAccess(GrResourceCache * cache)409 ResourceAccess(GrResourceCache* cache) : fCache(cache) { }
ResourceAccess(const ResourceAccess & that)410 ResourceAccess(const ResourceAccess& that) : fCache(that.fCache) { }
411 ResourceAccess& operator=(const ResourceAccess&) = delete;
412
413 /**
414 * Insert a resource into the cache.
415 */
insertResource(GrGpuResource * resource)416 void insertResource(GrGpuResource* resource) { fCache->insertResource(resource); }
417
418 /**
419 * Removes a resource from the cache.
420 */
removeResource(GrGpuResource * resource)421 void removeResource(GrGpuResource* resource) { fCache->removeResource(resource); }
422
423 /**
424 * Adds a ref to a resource with proper tracking if the resource has 0 refs prior to
425 * adding the ref.
426 */
refResource(GrGpuResource * resource)427 void refResource(GrGpuResource* resource) { fCache->refResource(resource); }
428
429 /**
430 * Notifications that should be sent to the cache when the ref/io cnt status of resources
431 * changes.
432 */
433 enum RefNotificationFlags {
434 /** All types of refs on the resource have reached zero. */
435 kAllCntsReachedZero_RefNotificationFlag = 0x1,
436 /** The normal (not pending IO type) ref cnt has reached zero. */
437 kRefCntReachedZero_RefNotificationFlag = 0x2,
438 };
439 /**
440 * Called by GrGpuResources when they detect one of their ref cnts have reached zero. This may
441 * either be the main ref or the command buffer usage ref.
442 */
notifyARefCntReachedZero(GrGpuResource * resource,GrGpuResource::LastRemovedRef removedRef)443 void notifyARefCntReachedZero(GrGpuResource* resource,
444 GrGpuResource::LastRemovedRef removedRef) {
445 fCache->notifyARefCntReachedZero(resource, removedRef);
446 }
447
448 /**
449 * Called by GrGpuResources to change their unique keys.
450 */
changeUniqueKey(GrGpuResource * resource,const skgpu::UniqueKey & newKey)451 void changeUniqueKey(GrGpuResource* resource, const skgpu::UniqueKey& newKey) {
452 fCache->changeUniqueKey(resource, newKey);
453 }
454
455 /**
456 * Called by a GrGpuResource to remove its unique key.
457 */
removeUniqueKey(GrGpuResource * resource)458 void removeUniqueKey(GrGpuResource* resource) { fCache->removeUniqueKey(resource); }
459
460 /**
461 * Called by a GrGpuResource when it removes its scratch key.
462 */
willRemoveScratchKey(const GrGpuResource * resource)463 void willRemoveScratchKey(const GrGpuResource* resource) {
464 fCache->willRemoveScratchKey(resource);
465 }
466
467 /**
468 * Called by GrGpuResources when they change from budgeted to unbudgeted or vice versa.
469 */
didChangeBudgetStatus(GrGpuResource * resource)470 void didChangeBudgetStatus(GrGpuResource* resource) { fCache->didChangeBudgetStatus(resource); }
471
472 // No taking addresses of this type.
473 const ResourceAccess* operator&() const;
474 ResourceAccess* operator&();
475
476 GrResourceCache* fCache;
477
478 friend class GrGpuResource; // To access all the proxy inline methods.
479 friend class GrResourceCache; // To create this type.
480 };
481
SkShouldPostMessageToBus(const GrResourceCache::UnrefResourceMessage & msg,GrDirectContext::DirectContextID potentialRecipient)482 static inline bool SkShouldPostMessageToBus(const GrResourceCache::UnrefResourceMessage& msg,
483 GrDirectContext::DirectContextID potentialRecipient) {
484 return potentialRecipient == msg.recipient();
485 }
486
resourceAccess()487 inline GrResourceCache::ResourceAccess GrResourceCache::resourceAccess() {
488 return ResourceAccess(this);
489 }
490
491 #endif
492