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