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