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