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 purgeUnlockAndSafeCacheGpuResources();
195
196 // Purge unlocked resources not used since the passed point in time. If 'scratchResourcesOnly'
197 // is true the purgeable resources containing persistent data are spared. If it is false then
198 // all purgeable resources older than 'purgeTime' will be deleted.
199 void purgeResourcesNotUsedSince(GrStdSteadyClock::time_point purgeTime,
200 bool scratchResourcesOnly=false) {
201 this->purgeUnlockedResources(&purgeTime, scratchResourcesOnly);
202 }
203
204 /** If it's possible to purge enough resources to get the provided amount of budget
205 headroom, do so and return true. If it's not possible, do nothing and return false.
206 */
207 bool purgeToMakeHeadroom(size_t desiredHeadroomBytes);
208
overBudget()209 bool overBudget() const { return fBudgetedBytes > fMaxBytes; }
210
211 /**
212 * Purge unlocked resources from the cache until the the provided byte count has been reached
213 * or we have purged all unlocked resources. The default policy is to purge in LRU order, but
214 * can be overridden to prefer purging scratch resources (in LRU order) prior to purging other
215 * resource types.
216 *
217 * @param maxBytesToPurge the desired number of bytes to be purged.
218 * @param preferScratchResources If true scratch resources will be purged prior to other
219 * resource types.
220 */
221 void purgeUnlockedResources(size_t bytesToPurge, bool preferScratchResources);
222
223 /** Returns true if the cache would like a flush to occur in order to make more resources
224 purgeable. */
225 bool requestsFlush() const;
226
227 /** Maintain a ref to this texture until we receive a GrTextureFreedMessage. */
228 void insertDelayedTextureUnref(GrTexture*);
229
230 #if GR_CACHE_STATS
231 struct Stats {
232 int fTotal;
233 int fNumPurgeable;
234 int fNumNonPurgeable;
235
236 int fScratch;
237 int fWrapped;
238 size_t fUnbudgetedSize;
239
StatsStats240 Stats() { this->reset(); }
241
resetStats242 void reset() {
243 fTotal = 0;
244 fNumPurgeable = 0;
245 fNumNonPurgeable = 0;
246 fScratch = 0;
247 fWrapped = 0;
248 fUnbudgetedSize = 0;
249 }
250
updateStats251 void update(GrGpuResource* resource) {
252 if (resource->cacheAccess().isScratch()) {
253 ++fScratch;
254 }
255 if (resource->resourcePriv().refsWrappedObjects()) {
256 ++fWrapped;
257 }
258 if (GrBudgetedType::kBudgeted != resource->resourcePriv().budgetedType()) {
259 fUnbudgetedSize += resource->gpuMemorySize();
260 }
261 }
262 };
263
264 void getStats(Stats*) const;
265
266 #if GR_TEST_UTILS
267 void dumpStats(SkString*) const;
268
269 void dumpStatsKeyValuePairs(SkTArray<SkString>* keys, SkTArray<double>* value) const;
270 #endif
271
272 #endif // GR_CACHE_STATS
273
274 #if GR_TEST_UTILS
275 int countUniqueKeysWithTag(const char* tag) const;
276
277 void changeTimestamp(uint32_t newTimestamp);
278 #endif
279
280 // Enumerates all cached resources and dumps their details to traceMemoryDump.
281 void dumpMemoryStatistics(SkTraceMemoryDump* traceMemoryDump) const;
282 void dumpMemoryStatistics(SkTraceMemoryDump* traceMemoryDump, const GrGpuResourceTag& tag) const;
283
setProxyProvider(GrProxyProvider * proxyProvider)284 void setProxyProvider(GrProxyProvider* proxyProvider) { fProxyProvider = proxyProvider; }
setThreadSafeCache(GrThreadSafeCache * threadSafeCache)285 void setThreadSafeCache(GrThreadSafeCache* threadSafeCache) {
286 fThreadSafeCache = threadSafeCache;
287 }
288
289 std::set<GrGpuResourceTag> getAllGrGpuResourceTags() const; // Get the tag of all GPU resources
290
291 private:
292 ///////////////////////////////////////////////////////////////////////////
293 /// @name Methods accessible via ResourceAccess
294 ////
295 void insertResource(GrGpuResource*);
296 void removeResource(GrGpuResource*);
297 void notifyARefCntReachedZero(GrGpuResource*, GrGpuResource::LastRemovedRef);
298 void changeUniqueKey(GrGpuResource*, const GrUniqueKey&);
299 void removeUniqueKey(GrGpuResource*);
300 void willRemoveScratchKey(const GrGpuResource*);
301 void didChangeBudgetStatus(GrGpuResource*);
302 void refResource(GrGpuResource* resource);
303 /// @}
304
305 void refAndMakeResourceMRU(GrGpuResource*);
306 void processFreedGpuResources();
307 void addToNonpurgeableArray(GrGpuResource*);
308 void removeFromNonpurgeableArray(GrGpuResource*);
309
wouldFit(size_t bytes)310 bool wouldFit(size_t bytes) const { return fBudgetedBytes+bytes <= fMaxBytes; }
311
312 uint32_t getNextTimestamp();
313
314 void purgeUnlockedResources(const GrStdSteadyClock::time_point* purgeTime,
315 bool scratchResourcesOnly);
316 bool isInCache(const GrGpuResource* r) const;
317 bool isInPurgeableCache(const GrGpuResource* r) const;
318 bool isInNonpurgeableCache(const GrGpuResource* r) const;
319 #ifdef SK_DEBUG
320 void validate() const;
321 #else
validate()322 void validate() const {}
323 #endif
324
325 class AutoValidate;
326
327 class AvailableForScratchUse;
328
329 struct ScratchMapTraits {
GetKeyScratchMapTraits330 static const GrScratchKey& GetKey(const GrGpuResource& r) {
331 return r.resourcePriv().getScratchKey();
332 }
333
HashScratchMapTraits334 static uint32_t Hash(const GrScratchKey& key) { return key.hash(); }
OnFreeScratchMapTraits335 static void OnFree(GrGpuResource*) { }
336 };
337 typedef SkTMultiMap<GrGpuResource, GrScratchKey, ScratchMapTraits> ScratchMap;
338
339 struct UniqueHashTraits {
GetKeyUniqueHashTraits340 static const GrUniqueKey& GetKey(const GrGpuResource& r) { return r.getUniqueKey(); }
341
HashUniqueHashTraits342 static uint32_t Hash(const GrUniqueKey& key) { return key.hash(); }
343 };
344 typedef SkTDynamicHash<GrGpuResource, GrUniqueKey, UniqueHashTraits> UniqueHash;
345
346 class TextureAwaitingUnref {
347 public:
348 TextureAwaitingUnref();
349 TextureAwaitingUnref(GrTexture* texture);
350 TextureAwaitingUnref(const TextureAwaitingUnref&) = delete;
351 TextureAwaitingUnref& operator=(const TextureAwaitingUnref&) = delete;
352 TextureAwaitingUnref(TextureAwaitingUnref&&);
353 TextureAwaitingUnref& operator=(TextureAwaitingUnref&&);
354 ~TextureAwaitingUnref();
355 void addRef();
356 void unref();
357 bool finished();
358
359 private:
360 GrTexture* fTexture = nullptr;
361 int fNumUnrefs = 0;
362 };
363 using TexturesAwaitingUnref = SkTHashMap<uint32_t, TextureAwaitingUnref>;
364
CompareTimestamp(GrGpuResource * const & a,GrGpuResource * const & b)365 static bool CompareTimestamp(GrGpuResource* const& a, GrGpuResource* const& b) {
366 return a->cacheAccess().timestamp() < b->cacheAccess().timestamp();
367 }
368
AccessResourceIndex(GrGpuResource * const & res)369 static int* AccessResourceIndex(GrGpuResource* const& res) {
370 return res->cacheAccess().accessCacheIndex();
371 }
372
373 using TextureFreedMessageBus = SkMessageBus<GrTextureFreedMessage,
374 GrDirectContext::DirectContextID>;
375
376 typedef SkMessageBus<GrUniqueKeyInvalidatedMessage, uint32_t>::Inbox InvalidUniqueKeyInbox;
377 typedef SkTDPQueue<GrGpuResource*, CompareTimestamp, AccessResourceIndex> PurgeableQueue;
378 typedef SkTDArray<GrGpuResource*> ResourceArray;
379
380 GrProxyProvider* fProxyProvider = nullptr;
381 GrThreadSafeCache* fThreadSafeCache = nullptr;
382
383 // Whenever a resource is added to the cache or the result of a cache lookup, fTimestamp is
384 // assigned as the resource's timestamp and then incremented. fPurgeableQueue orders the
385 // purgeable resources by this value, and thus is used to purge resources in LRU order.
386 uint32_t fTimestamp = 0;
387 PurgeableQueue fPurgeableQueue;
388 ResourceArray fNonpurgeableResources;
389
390 // This map holds all resources that can be used as scratch resources.
391 ScratchMap fScratchMap;
392 // This holds all resources that have unique keys.
393 UniqueHash fUniqueHash;
394
395 // our budget, used in purgeAsNeeded()
396 size_t fMaxBytes = kDefaultMaxSize;
397
398 #if GR_CACHE_STATS
399 int fHighWaterCount = 0;
400 size_t fHighWaterBytes = 0;
401 int fBudgetedHighWaterCount = 0;
402 size_t fBudgetedHighWaterBytes = 0;
403 #endif
404
405 // our current stats for all resources
406 SkDEBUGCODE(int fCount = 0;)
407 size_t fBytes = 0;
408
409 // our current stats for resources that count against the budget
410 int fBudgetedCount = 0;
411 size_t fBudgetedBytes = 0;
412 size_t fPurgeableBytes = 0;
413 int fNumBudgetedResourcesFlushWillMakePurgeable = 0;
414
415 InvalidUniqueKeyInbox fInvalidUniqueKeyInbox;
416 TextureFreedMessageBus::Inbox fFreedTextureInbox;
417 TexturesAwaitingUnref fTexturesAwaitingUnref;
418
419 GrDirectContext::DirectContextID fOwningContextID;
420 uint32_t fContextUniqueID = SK_InvalidUniqueID;
421 GrSingleOwner* fSingleOwner = nullptr;
422
423 // This resource is allowed to be in the nonpurgeable array for the sake of validate() because
424 // we're in the midst of converting it to purgeable status.
425 SkDEBUGCODE(GrGpuResource* fNewlyPurgeableResourceForValidation = nullptr;)
426
427 //Indicates the cached resource tags.
428 std::stack<GrGpuResourceTag> grResourceTagCacheStack;
429 };
430
431 class GrResourceCache::ResourceAccess {
432 private:
ResourceAccess(GrResourceCache * cache)433 ResourceAccess(GrResourceCache* cache) : fCache(cache) { }
ResourceAccess(const ResourceAccess & that)434 ResourceAccess(const ResourceAccess& that) : fCache(that.fCache) { }
435 ResourceAccess& operator=(const ResourceAccess&) = delete;
436
437 /**
438 * Insert a resource into the cache.
439 */
insertResource(GrGpuResource * resource)440 void insertResource(GrGpuResource* resource) { fCache->insertResource(resource); }
441
442 /**
443 * Removes a resource from the cache.
444 */
removeResource(GrGpuResource * resource)445 void removeResource(GrGpuResource* resource) { fCache->removeResource(resource); }
446
447 /**
448 * Adds a ref to a resource with proper tracking if the resource has 0 refs prior to
449 * adding the ref.
450 */
refResource(GrGpuResource * resource)451 void refResource(GrGpuResource* resource) { fCache->refResource(resource); }
452
453 /**
454 * Get current resource tag for gpu cache recycle.
455 */
getCurrentGrResourceTag()456 GrGpuResourceTag getCurrentGrResourceTag() const { return fCache->getCurrentGrResourceTag(); }
457
458 /**
459 * Notifications that should be sent to the cache when the ref/io cnt status of resources
460 * changes.
461 */
462 enum RefNotificationFlags {
463 /** All types of refs on the resource have reached zero. */
464 kAllCntsReachedZero_RefNotificationFlag = 0x1,
465 /** The normal (not pending IO type) ref cnt has reached zero. */
466 kRefCntReachedZero_RefNotificationFlag = 0x2,
467 };
468 /**
469 * Called by GrGpuResources when they detect one of their ref cnts have reached zero. This may
470 * either be the main ref or the command buffer usage ref.
471 */
notifyARefCntReachedZero(GrGpuResource * resource,GrGpuResource::LastRemovedRef removedRef)472 void notifyARefCntReachedZero(GrGpuResource* resource,
473 GrGpuResource::LastRemovedRef removedRef) {
474 fCache->notifyARefCntReachedZero(resource, removedRef);
475 }
476
477 /**
478 * Called by GrGpuResources to change their unique keys.
479 */
changeUniqueKey(GrGpuResource * resource,const GrUniqueKey & newKey)480 void changeUniqueKey(GrGpuResource* resource, const GrUniqueKey& newKey) {
481 fCache->changeUniqueKey(resource, newKey);
482 }
483
484 /**
485 * Called by a GrGpuResource to remove its unique key.
486 */
removeUniqueKey(GrGpuResource * resource)487 void removeUniqueKey(GrGpuResource* resource) { fCache->removeUniqueKey(resource); }
488
489 /**
490 * Called by a GrGpuResource when it removes its scratch key.
491 */
willRemoveScratchKey(const GrGpuResource * resource)492 void willRemoveScratchKey(const GrGpuResource* resource) {
493 fCache->willRemoveScratchKey(resource);
494 }
495
496 /**
497 * Called by GrGpuResources when they change from budgeted to unbudgeted or vice versa.
498 */
didChangeBudgetStatus(GrGpuResource * resource)499 void didChangeBudgetStatus(GrGpuResource* resource) { fCache->didChangeBudgetStatus(resource); }
500
501 // No taking addresses of this type.
502 const ResourceAccess* operator&() const;
503 ResourceAccess* operator&();
504
505 GrResourceCache* fCache;
506
507 friend class GrGpuResource; // To access all the proxy inline methods.
508 friend class GrResourceCache; // To create this type.
509 };
510
resourceAccess()511 inline GrResourceCache::ResourceAccess GrResourceCache::resourceAccess() {
512 return ResourceAccess(this);
513 }
514
515 #endif
516