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