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