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