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