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