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 "GrGpuResource.h"
12 #include "GrGpuResourceCacheAccess.h"
13 #include "GrGpuResourcePriv.h"
14 #include "GrResourceKey.h"
15 #include "SkMessageBus.h"
16 #include "SkRefCnt.h"
17 #include "SkTArray.h"
18 #include "SkTDPQueue.h"
19 #include "SkTInternalLList.h"
20 #include "SkTMultiMap.h"
21
22 class GrCaps;
23 class GrProxyProvider;
24 class SkString;
25 class SkTraceMemoryDump;
26 class GrSingleOwner;
27
28 struct GrGpuResourceFreedMessage {
29 GrGpuResource* fResource;
30 uint32_t fOwningUniqueID;
31 };
32
SkShouldPostMessageToBus(const GrGpuResourceFreedMessage & msg,uint32_t msgBusUniqueID)33 static inline bool SkShouldPostMessageToBus(
34 const GrGpuResourceFreedMessage& msg, uint32_t msgBusUniqueID) {
35 // The inbox's ID is the unique ID of the owning GrContext.
36 return msgBusUniqueID == msg.fOwningUniqueID;
37 }
38
39 /**
40 * Manages the lifetime of all GrGpuResource instances.
41 *
42 * Resources may have optionally have two types of keys:
43 * 1) A scratch key. This is for resources whose allocations are cached but not their contents.
44 * Multiple resources can share the same scratch key. This is so a caller can have two
45 * resource instances with the same properties (e.g. multipass rendering that ping-pongs
46 * between two temporary surfaces). The scratch key is set at resource creation time and
47 * should never change. Resources need not have a scratch key.
48 * 2) A unique key. This key's meaning is specific to the domain that created the key. Only one
49 * resource may have a given unique key. The unique key can be set, cleared, or changed
50 * anytime after resource creation.
51 *
52 * A unique key always takes precedence over a scratch key when a resource has both types of keys.
53 * If a resource has neither key type then it will be deleted as soon as the last reference to it
54 * is dropped.
55 */
56 class GrResourceCache {
57 public:
58 GrResourceCache(const GrCaps*, GrSingleOwner* owner, uint32_t contextUniqueID);
59 ~GrResourceCache();
60
61 // Default maximum number of budgeted resources in the cache.
62 static const int kDefaultMaxCount = 2 * (1 << 12);
63 // Default maximum number of bytes of gpu memory of budgeted resources in the cache.
64 static const size_t kDefaultMaxSize = 96 * (1 << 20);
65
66 /** Used to access functionality needed by GrGpuResource for lifetime management. */
67 class ResourceAccess;
68 ResourceAccess resourceAccess();
69
70 /** Unique ID of the owning GrContext. */
contextUniqueID()71 uint32_t contextUniqueID() const { return fContextUniqueID; }
72
73 /** Sets the cache limits in terms of number of resources and max gpu memory byte size. */
74 void setLimits(int count, size_t bytes);
75
76 /**
77 * Returns the number of resources.
78 */
getResourceCount()79 int getResourceCount() const {
80 return fPurgeableQueue.count() + fNonpurgeableResources.count();
81 }
82
83 /**
84 * Returns the number of resources that count against the budget.
85 */
getBudgetedResourceCount()86 int getBudgetedResourceCount() const { return fBudgetedCount; }
87
88 /**
89 * Returns the number of bytes consumed by resources.
90 */
getResourceBytes()91 size_t getResourceBytes() const { return fBytes; }
92
93 /**
94 * Returns the number of bytes held by unlocked reosources which are available for purging.
95 */
getPurgeableBytes()96 size_t getPurgeableBytes() const { return fPurgeableBytes; }
97
98 /**
99 * Returns the number of bytes consumed by budgeted resources.
100 */
getBudgetedResourceBytes()101 size_t getBudgetedResourceBytes() const { return fBudgetedBytes; }
102
103 /**
104 * Returns the cached resources count budget.
105 */
getMaxResourceCount()106 int getMaxResourceCount() const { return fMaxCount; }
107
108 /**
109 * Returns the number of bytes consumed by cached resources.
110 */
getMaxResourceBytes()111 size_t getMaxResourceBytes() const { return fMaxBytes; }
112
113 /**
114 * Abandons the backend API resources owned by all GrGpuResource objects and removes them from
115 * the cache.
116 */
117 void abandonAll();
118
119 /**
120 * Releases the backend API resources owned by all GrGpuResource objects and removes them from
121 * the cache.
122 */
123 void releaseAll();
124
125 enum class ScratchFlags {
126 kNone = 0,
127 /** Preferentially returns scratch resources with no pending IO. */
128 kPreferNoPendingIO = 0x1,
129 /** Will not return any resources that match but have pending IO. */
130 kRequireNoPendingIO = 0x2,
131 };
132
133 /**
134 * Find a resource that matches a scratch key.
135 */
136 GrGpuResource* findAndRefScratchResource(const GrScratchKey& scratchKey, size_t resourceSize,
137 ScratchFlags);
138
139 #ifdef SK_DEBUG
140 // This is not particularly fast and only used for validation, so debug only.
countScratchEntriesForKey(const GrScratchKey & scratchKey)141 int countScratchEntriesForKey(const GrScratchKey& scratchKey) const {
142 return fScratchMap.countForKey(scratchKey);
143 }
144 #endif
145
146 /**
147 * Find a resource that matches a unique key.
148 */
findAndRefUniqueResource(const GrUniqueKey & key)149 GrGpuResource* findAndRefUniqueResource(const GrUniqueKey& key) {
150 GrGpuResource* resource = fUniqueHash.find(key);
151 if (resource) {
152 this->refAndMakeResourceMRU(resource);
153 }
154 return resource;
155 }
156
157 /**
158 * Query whether a unique key exists in the cache.
159 */
hasUniqueKey(const GrUniqueKey & key)160 bool hasUniqueKey(const GrUniqueKey& key) const {
161 return SkToBool(fUniqueHash.find(key));
162 }
163
164 /** Purges resources to become under budget and processes resources with invalidated unique
165 keys. */
166 void purgeAsNeeded();
167
168 /** Purges all resources that don't have external owners. */
purgeAllUnlocked()169 void purgeAllUnlocked() { this->purgeUnlockedResources(false); }
170
171 // Purge unlocked resources. If 'scratchResourcesOnly' is true the purgeable resources
172 // containing persistent data are spared. If it is false then all purgeable resources will
173 // be deleted.
174 void purgeUnlockedResources(bool scratchResourcesOnly);
175
176 /** Purge all resources not used since the passed in time. */
177 void purgeResourcesNotUsedSince(GrStdSteadyClock::time_point);
178
overBudget()179 bool overBudget() const { return fBudgetedBytes > fMaxBytes || fBudgetedCount > fMaxCount; }
180
181 /**
182 * Purge unlocked resources from the cache until the the provided byte count has been reached
183 * or we have purged all unlocked resources. The default policy is to purge in LRU order, but
184 * can be overridden to prefer purging scratch resources (in LRU order) prior to purging other
185 * resource types.
186 *
187 * @param maxBytesToPurge the desired number of bytes to be purged.
188 * @param preferScratchResources If true scratch resources will be purged prior to other
189 * resource types.
190 */
191 void purgeUnlockedResources(size_t bytesToPurge, bool preferScratchResources);
192
193 /** Returns true if the cache would like a flush to occur in order to make more resources
194 purgeable. */
requestsFlush()195 bool requestsFlush() const { return this->overBudget() && !fPurgeableQueue.count(); }
196
197 /** Maintain a ref to this resource until we receive a GrGpuResourceFreedMessage. */
198 void insertCrossContextGpuResource(GrGpuResource* resource);
199
200 #if GR_CACHE_STATS
201 struct Stats {
202 int fTotal;
203 int fNumPurgeable;
204 int fNumNonPurgeable;
205
206 int fScratch;
207 int fWrapped;
208 size_t fUnbudgetedSize;
209
StatsStats210 Stats() { this->reset(); }
211
resetStats212 void reset() {
213 fTotal = 0;
214 fNumPurgeable = 0;
215 fNumNonPurgeable = 0;
216 fScratch = 0;
217 fWrapped = 0;
218 fUnbudgetedSize = 0;
219 }
220
updateStats221 void update(GrGpuResource* resource) {
222 if (resource->cacheAccess().isScratch()) {
223 ++fScratch;
224 }
225 if (resource->resourcePriv().refsWrappedObjects()) {
226 ++fWrapped;
227 }
228 if (GrBudgetedType::kBudgeted != resource->resourcePriv().budgetedType()) {
229 fUnbudgetedSize += resource->gpuMemorySize();
230 }
231 }
232 };
233
234 void getStats(Stats*) const;
235
236 void dumpStats(SkString*) const;
237
238 void dumpStatsKeyValuePairs(SkTArray<SkString>* keys, SkTArray<double>* value) const;
239 #endif
240
241 #ifdef SK_DEBUG
242 int countUniqueKeysWithTag(const char* tag) const;
243 #endif
244
245 // This function is for unit testing and is only defined in test tools.
246 void changeTimestamp(uint32_t newTimestamp);
247
248 // Enumerates all cached resources and dumps their details to traceMemoryDump.
249 void dumpMemoryStatistics(SkTraceMemoryDump* traceMemoryDump) const;
250
setProxyProvider(GrProxyProvider * proxyProvider)251 void setProxyProvider(GrProxyProvider* proxyProvider) { fProxyProvider = proxyProvider; }
252
253 private:
254 ///////////////////////////////////////////////////////////////////////////
255 /// @name Methods accessible via ResourceAccess
256 ////
257 void insertResource(GrGpuResource*);
258 void removeResource(GrGpuResource*);
259 void notifyCntReachedZero(GrGpuResource*, uint32_t flags);
260 void changeUniqueKey(GrGpuResource*, const GrUniqueKey&);
261 void removeUniqueKey(GrGpuResource*);
262 void willRemoveScratchKey(const GrGpuResource*);
263 void didChangeBudgetStatus(GrGpuResource*);
264 void refAndMakeResourceMRU(GrGpuResource*);
265 /// @}
266
267 void processFreedGpuResources();
268 void addToNonpurgeableArray(GrGpuResource*);
269 void removeFromNonpurgeableArray(GrGpuResource*);
270
wouldFit(size_t bytes)271 bool wouldFit(size_t bytes) {
272 return fBudgetedBytes+bytes <= fMaxBytes && fBudgetedCount+1 <= fMaxCount;
273 }
274
275 uint32_t getNextTimestamp();
276
277 #ifdef SK_DEBUG
278 bool isInCache(const GrGpuResource* r) const;
279 void validate() const;
280 #else
validate()281 void validate() const {}
282 #endif
283
284 class AutoValidate;
285
286 class AvailableForScratchUse;
287
288 struct ScratchMapTraits {
GetKeyScratchMapTraits289 static const GrScratchKey& GetKey(const GrGpuResource& r) {
290 return r.resourcePriv().getScratchKey();
291 }
292
HashScratchMapTraits293 static uint32_t Hash(const GrScratchKey& key) { return key.hash(); }
OnFreeScratchMapTraits294 static void OnFree(GrGpuResource*) { }
295 };
296 typedef SkTMultiMap<GrGpuResource, GrScratchKey, ScratchMapTraits> ScratchMap;
297
298 struct UniqueHashTraits {
GetKeyUniqueHashTraits299 static const GrUniqueKey& GetKey(const GrGpuResource& r) { return r.getUniqueKey(); }
300
HashUniqueHashTraits301 static uint32_t Hash(const GrUniqueKey& key) { return key.hash(); }
302 };
303 typedef SkTDynamicHash<GrGpuResource, GrUniqueKey, UniqueHashTraits> UniqueHash;
304
CompareTimestamp(GrGpuResource * const & a,GrGpuResource * const & b)305 static bool CompareTimestamp(GrGpuResource* const& a, GrGpuResource* const& b) {
306 return a->cacheAccess().timestamp() < b->cacheAccess().timestamp();
307 }
308
AccessResourceIndex(GrGpuResource * const & res)309 static int* AccessResourceIndex(GrGpuResource* const& res) {
310 return res->cacheAccess().accessCacheIndex();
311 }
312
313 typedef SkMessageBus<GrUniqueKeyInvalidatedMessage>::Inbox InvalidUniqueKeyInbox;
314 typedef SkMessageBus<GrGpuResourceFreedMessage>::Inbox FreedGpuResourceInbox;
315 typedef SkTDPQueue<GrGpuResource*, CompareTimestamp, AccessResourceIndex> PurgeableQueue;
316 typedef SkTDArray<GrGpuResource*> ResourceArray;
317
318 GrProxyProvider* fProxyProvider;
319 // Whenever a resource is added to the cache or the result of a cache lookup, fTimestamp is
320 // assigned as the resource's timestamp and then incremented. fPurgeableQueue orders the
321 // purgeable resources by this value, and thus is used to purge resources in LRU order.
322 uint32_t fTimestamp;
323 PurgeableQueue fPurgeableQueue;
324 ResourceArray fNonpurgeableResources;
325
326 // This map holds all resources that can be used as scratch resources.
327 ScratchMap fScratchMap;
328 // This holds all resources that have unique keys.
329 UniqueHash fUniqueHash;
330
331 // our budget, used in purgeAsNeeded()
332 int fMaxCount;
333 size_t fMaxBytes;
334
335 #if GR_CACHE_STATS
336 int fHighWaterCount;
337 size_t fHighWaterBytes;
338 int fBudgetedHighWaterCount;
339 size_t fBudgetedHighWaterBytes;
340 #endif
341
342 // our current stats for all resources
343 SkDEBUGCODE(int fCount;)
344 size_t fBytes;
345
346 // our current stats for resources that count against the budget
347 int fBudgetedCount;
348 size_t fBudgetedBytes;
349 size_t fPurgeableBytes;
350
351 InvalidUniqueKeyInbox fInvalidUniqueKeyInbox;
352 FreedGpuResourceInbox fFreedGpuResourceInbox;
353
354 SkTDArray<GrGpuResource*> fResourcesWaitingForFreeMsg;
355
356 uint32_t fContextUniqueID;
357 GrSingleOwner* fSingleOwner;
358
359 // This resource is allowed to be in the nonpurgeable array for the sake of validate() because
360 // we're in the midst of converting it to purgeable status.
361 SkDEBUGCODE(GrGpuResource* fNewlyPurgeableResourceForValidation;)
362
363 bool fPreferVRAMUseOverFlushes;
364 };
365
366 GR_MAKE_BITFIELD_CLASS_OPS(GrResourceCache::ScratchFlags);
367
368 class GrResourceCache::ResourceAccess {
369 private:
ResourceAccess(GrResourceCache * cache)370 ResourceAccess(GrResourceCache* cache) : fCache(cache) { }
ResourceAccess(const ResourceAccess & that)371 ResourceAccess(const ResourceAccess& that) : fCache(that.fCache) { }
372 ResourceAccess& operator=(const ResourceAccess&); // unimpl
373
374 /**
375 * Insert a resource into the cache.
376 */
insertResource(GrGpuResource * resource)377 void insertResource(GrGpuResource* resource) { fCache->insertResource(resource); }
378
379 /**
380 * Removes a resource from the cache.
381 */
removeResource(GrGpuResource * resource)382 void removeResource(GrGpuResource* resource) { fCache->removeResource(resource); }
383
384 /**
385 * Notifications that should be sent to the cache when the ref/io cnt status of resources
386 * changes.
387 */
388 enum RefNotificationFlags {
389 /** All types of refs on the resource have reached zero. */
390 kAllCntsReachedZero_RefNotificationFlag = 0x1,
391 /** The normal (not pending IO type) ref cnt has reached zero. */
392 kRefCntReachedZero_RefNotificationFlag = 0x2,
393 };
394 /**
395 * Called by GrGpuResources when they detect that their ref/io cnts have reached zero. When the
396 * normal ref cnt reaches zero the flags that are set should be:
397 * a) kRefCntReachedZero if a pending IO cnt is still non-zero.
398 * b) (kRefCntReachedZero | kAllCntsReachedZero) when all pending IO cnts are also zero.
399 * kAllCntsReachedZero is set by itself if a pending IO cnt is decremented to zero and all the
400 * the other cnts are already zero.
401 */
notifyCntReachedZero(GrGpuResource * resource,uint32_t flags)402 void notifyCntReachedZero(GrGpuResource* resource, uint32_t flags) {
403 fCache->notifyCntReachedZero(resource, flags);
404 }
405
406 /**
407 * Called by GrGpuResources to change their unique keys.
408 */
changeUniqueKey(GrGpuResource * resource,const GrUniqueKey & newKey)409 void changeUniqueKey(GrGpuResource* resource, const GrUniqueKey& newKey) {
410 fCache->changeUniqueKey(resource, newKey);
411 }
412
413 /**
414 * Called by a GrGpuResource to remove its unique key.
415 */
removeUniqueKey(GrGpuResource * resource)416 void removeUniqueKey(GrGpuResource* resource) { fCache->removeUniqueKey(resource); }
417
418 /**
419 * Called by a GrGpuResource when it removes its scratch key.
420 */
willRemoveScratchKey(const GrGpuResource * resource)421 void willRemoveScratchKey(const GrGpuResource* resource) {
422 fCache->willRemoveScratchKey(resource);
423 }
424
425 /**
426 * Called by GrGpuResources when they change from budgeted to unbudgeted or vice versa.
427 */
didChangeBudgetStatus(GrGpuResource * resource)428 void didChangeBudgetStatus(GrGpuResource* resource) { fCache->didChangeBudgetStatus(resource); }
429
430 // No taking addresses of this type.
431 const ResourceAccess* operator&() const;
432 ResourceAccess* operator&();
433
434 GrResourceCache* fCache;
435
436 friend class GrGpuResource; // To access all the proxy inline methods.
437 friend class GrResourceCache; // To create this type.
438 };
439
resourceAccess()440 inline GrResourceCache::ResourceAccess GrResourceCache::resourceAccess() {
441 return ResourceAccess(this);
442 }
443
444 #endif
445