Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
GrResourceCache.h
Go to the documentation of this file.
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
15#include "src/base/SkTDPQueue.h"
18#include "src/core/SkTHash.h"
20#include "src/gpu/ResourceKey.h"
24
25class GrCaps;
26class GrProxyProvider;
27class SkString;
29class GrTexture;
31
32namespace skgpu {
33class 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 */
54public:
57 uint32_t familyID);
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>
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;
85
86 /** Unique ID of the owning GrContext. */
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 */
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 */
102 int getBudgetedResourceCount() const { return fBudgetedCount; }
103
104 /**
105 * Returns the number of bytes consumed by resources.
106 */
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 */
112 size_t getPurgeableBytes() const { return fPurgeableBytes; }
113
114 /**
115 * Returns the number of bytes consumed by budgeted resources.
116 */
117 size_t getBudgetedResourceBytes() const { return fBudgetedBytes; }
118
119 /**
120 * Returns the number of bytes consumed by cached resources.
121 */
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 */
140
141#ifdef SK_DEBUG
142 // This is not particularly fast and only used for validation, so debug only.
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 */
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 */
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.
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.
180 void purgeResourcesNotUsedSince(skgpu::StdSteadyClock::time_point purgeTime,
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
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
218 Stats() { this->reset(); }
219
220 void reset() {
221 fTotal = 0;
222 fNumPurgeable = 0;
223 fNumNonPurgeable = 0;
224 fScratch = 0;
225 fWrapped = 0;
226 fUnbudgetedSize = 0;
227 }
228
229 void update(GrGpuResource* resource) {
230 if (resource->cacheAccess().isScratch()) {
231 ++fScratch;
232 }
233 if (resource->resourcePriv().refsWrappedObjects()) {
234 ++fWrapped;
235 }
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(
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
264 void setProxyProvider(GrProxyProvider* proxyProvider) { fProxyProvider = proxyProvider; }
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.
272 public:
273 GrDirectContext::DirectContextID recipient() const { return fRecipient; }
274
277
278 private:
279 friend class GrResourceCache;
280
283 /*AllowCopyableMessage=*/false>;
284
287 : fResource(std::move(resource)), fRecipient(recipient) {}
288
291
292 sk_sp<GrGpuResource> fResource;
294 };
295
296private:
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
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,
321
322#ifdef SK_DEBUG
323 bool isInCache(const GrGpuResource* r) const;
324 void validate() const;
325#else
326 void validate() const {}
327#endif
328
329 class AutoValidate;
330
331 struct ScratchMapTraits {
332 static const skgpu::ScratchKey& GetKey(const GrGpuResource& r) {
333 return r.resourcePriv().getScratchKey();
334 }
335
336 static uint32_t Hash(const skgpu::ScratchKey& key) { return key.hash(); }
337 static void OnFree(GrGpuResource*) { }
338 };
340
341 struct UniqueHashTraits {
342 static const skgpu::UniqueKey& GetKey(const GrGpuResource& r) { return r.getUniqueKey(); }
343
344 static uint32_t Hash(const skgpu::UniqueKey& key) { return key.hash(); }
345 };
347
348 static bool CompareTimestamp(GrGpuResource* const& a, GrGpuResource* const& b) {
349 return a->cacheAccess().timestamp() < b->cacheAccess().timestamp();
350 }
351
352 static int* AccessResourceIndex(GrGpuResource* const& res) {
353 return res->cacheAccess().accessCacheIndex();
354 }
355
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
408private:
409 ResourceAccess(GrResourceCache* cache) : fCache(cache) { }
410 ResourceAccess(const ResourceAccess& that) : fCache(that.fCache) { }
411 ResourceAccess& operator=(const ResourceAccess&) = delete;
412
413 /**
414 * Insert a resource into the cache.
415 */
416 void insertResource(GrGpuResource* resource) { fCache->insertResource(resource); }
417
418 /**
419 * Removes a resource from the cache.
420 */
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 */
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 */
443 void notifyARefCntReachedZero(GrGpuResource* resource,
445 fCache->notifyARefCntReachedZero(resource, removedRef);
446 }
447
448 /**
449 * Called by GrGpuResources to change their unique keys.
450 */
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 */
458 void removeUniqueKey(GrGpuResource* resource) { fCache->removeUniqueKey(resource); }
459
460 /**
461 * Called by a GrGpuResource when it removes its scratch key.
462 */
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 */
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
483 GrDirectContext::DirectContextID potentialRecipient) {
484 return potentialRecipient == msg.recipient();
485}
486
490
491#endif
m reset()
static bool SkShouldPostMessageToBus(const GrResourceCache::UnrefResourceMessage &msg, GrDirectContext::DirectContextID potentialRecipient)
GrPurgeResourceOptions
Definition GrTypes.h:234
#define SkDEBUGCODE(...)
Definition SkDebug.h:23
static constexpr bool SkToBool(const T &x)
Definition SkTo.h:35
static constexpr uint32_t SK_InvalidUniqueID
Definition SkTypes.h:196
const skgpu::ScratchKey & getScratchKey() const
GrBudgetedType budgetedType() const
size_t gpuMemorySize() const
const skgpu::UniqueKey & getUniqueKey() const
ResourcePriv resourcePriv()
UnrefResourceMessage & operator=(UnrefResourceMessage &&)=default
GrDirectContext::DirectContextID recipient() const
UnrefResourceMessage(UnrefResourceMessage &&)=default
size_t getResourceBytes() const
void setThreadSafeCache(GrThreadSafeCache *threadSafeCache)
void dumpMemoryStatistics(SkTraceMemoryDump *traceMemoryDump) const
void setLimit(size_t bytes)
int getBudgetedResourceCount() const
GrResourceCache(skgpu::SingleOwner *owner, GrDirectContext::DirectContextID owningContextID, uint32_t familyID)
size_t getPurgeableBytes() const
static std::enable_if_t< std::is_base_of_v< GrGpuResource, T >, void > ReturnResourceFromThread(sk_sp< T > &&resource, GrDirectContext::DirectContextID id)
bool hasUniqueKey(const skgpu::UniqueKey &key) const
int getResourceCount() const
bool requestsFlush() const
ResourceAccess resourceAccess()
size_t getMaxResourceBytes() const
bool purgeToMakeHeadroom(size_t desiredHeadroomBytes)
size_t getBudgetedResourceBytes() const
void setProxyProvider(GrProxyProvider *proxyProvider)
static const size_t kDefaultMaxSize
void purgeResourcesNotUsedSince(skgpu::StdSteadyClock::time_point purgeTime, GrPurgeResourceOptions opts)
void purgeUnlockedResources(GrPurgeResourceOptions opts)
bool overBudget() const
GrGpuResource * findAndRefScratchResource(const skgpu::ScratchKey &scratchKey)
GrGpuResource * findAndRefUniqueResource(const skgpu::UniqueKey &key)
uint32_t contextUniqueID() const
SkDEBUGCODE(int fCount=0;) size_t fBytes=0
static void Post(Message m)
int size() const
Definition SkTDArray.h:138
int count() const
Definition SkTDPQueue.h:45
T * find(const Key &key) const
uint32_t hash() const
Definition ResourceKey.h:37
static bool b
struct MyStruct a[10]
Definition ref_ptr.h:256