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