1 2 /* 3 * Copyright 2011 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 10 11 #ifndef GrResourceCache_DEFINED 12 #define GrResourceCache_DEFINED 13 14 #include "GrConfig.h" 15 #include "GrTypes.h" 16 #include "GrTHashTable.h" 17 #include "GrBinHashKey.h" 18 #include "SkMessageBus.h" 19 #include "SkTInternalLList.h" 20 21 class GrResource; 22 class GrResourceEntry; 23 24 class GrResourceKey { 25 public: 26 enum { 27 kHashBits = 7, 28 kHashCount = 1 << kHashBits, 29 kHashMask = kHashCount - 1 30 }; 31 32 static GrCacheID::Domain ScratchDomain() { 33 static const GrCacheID::Domain gDomain = GrCacheID::GenerateDomain(); 34 return gDomain; 35 } 36 37 /** Uniquely identifies the GrResource subclass in the key to avoid collisions 38 across resource types. */ 39 typedef uint8_t ResourceType; 40 41 /** Flags set by the GrResource subclass. */ 42 typedef uint8_t ResourceFlags; 43 44 /** Generate a unique ResourceType */ 45 static ResourceType GenerateResourceType(); 46 47 /** Creates a key for resource */ 48 GrResourceKey(const GrCacheID& id, ResourceType type, ResourceFlags flags) { 49 this->init(id.getDomain(), id.getKey(), type, flags); 50 }; 51 52 GrResourceKey(const GrResourceKey& src) { 53 fKey = src.fKey; 54 } 55 56 GrResourceKey() { 57 fKey.reset(); 58 } 59 60 void reset(const GrCacheID& id, ResourceType type, ResourceFlags flags) { 61 this->init(id.getDomain(), id.getKey(), type, flags); 62 } 63 64 //!< returns hash value [0..kHashMask] for the key 65 int getHash() const { 66 return fKey.getHash() & kHashMask; 67 } 68 69 bool isScratch() const { 70 return ScratchDomain() == 71 *reinterpret_cast<const GrCacheID::Domain*>(fKey.getData() + 72 kCacheIDDomainOffset); 73 } 74 75 ResourceType getResourceType() const { 76 return *reinterpret_cast<const ResourceType*>(fKey.getData() + 77 kResourceTypeOffset); 78 } 79 80 ResourceFlags getResourceFlags() const { 81 return *reinterpret_cast<const ResourceFlags*>(fKey.getData() + 82 kResourceFlagsOffset); 83 } 84 85 bool operator==(const GrResourceKey& other) const { return fKey == other.fKey; } 86 bool operator<(const GrResourceKey& other) const { return fKey < other.fKey; } 87 88 static bool LessThan(const GrResourceEntry& entry, const GrResourceKey& key); 89 static bool Equals(const GrResourceEntry& entry, const GrResourceKey& key); 90 #ifdef SK_DEBUG 91 static bool LessThan(const GrResourceEntry& a, const GrResourceEntry& b); 92 static bool Equals(const GrResourceEntry& a, const GrResourceEntry& b); 93 #endif 94 95 private: 96 enum { 97 kCacheIDKeyOffset = 0, 98 kCacheIDDomainOffset = kCacheIDKeyOffset + sizeof(GrCacheID::Key), 99 kResourceTypeOffset = kCacheIDDomainOffset + sizeof(GrCacheID::Domain), 100 kResourceFlagsOffset = kResourceTypeOffset + sizeof(ResourceType), 101 kPadOffset = kResourceFlagsOffset + sizeof(ResourceFlags), 102 kKeySize = SkAlign4(kPadOffset), 103 kPadSize = kKeySize - kPadOffset 104 }; 105 106 void init(const GrCacheID::Domain domain, 107 const GrCacheID::Key& key, 108 ResourceType type, 109 ResourceFlags flags) { 110 union { 111 uint8_t fKey8[kKeySize]; 112 uint32_t fKey32[kKeySize / 4]; 113 } keyData; 114 115 uint8_t* k = keyData.fKey8; 116 memcpy(k + kCacheIDKeyOffset, key.fData8, sizeof(GrCacheID::Key)); 117 memcpy(k + kCacheIDDomainOffset, &domain, sizeof(GrCacheID::Domain)); 118 memcpy(k + kResourceTypeOffset, &type, sizeof(ResourceType)); 119 memcpy(k + kResourceFlagsOffset, &flags, sizeof(ResourceFlags)); 120 memset(k + kPadOffset, 0, kPadSize); 121 fKey.setKeyData(keyData.fKey32); 122 } 123 GrBinHashKey<kKeySize> fKey; 124 }; 125 126 // The cache listens for these messages to purge junk resources proactively. 127 struct GrResourceInvalidatedMessage { 128 GrResourceKey key; 129 }; 130 131 /////////////////////////////////////////////////////////////////////////////// 132 133 class GrResourceEntry { 134 public: 135 GrResource* resource() const { return fResource; } 136 const GrResourceKey& key() const { return fKey; } 137 138 #ifdef SK_DEBUG 139 void validate() const; 140 #else 141 void validate() const {} 142 #endif 143 144 private: 145 GrResourceEntry(const GrResourceKey& key, GrResource* resource); 146 ~GrResourceEntry(); 147 148 GrResourceKey fKey; 149 GrResource* fResource; 150 151 // we're a linked list 152 SK_DECLARE_INTERNAL_LLIST_INTERFACE(GrResourceEntry); 153 154 friend class GrResourceCache; 155 friend class GrDLinkedList; 156 }; 157 158 inline bool GrResourceKey::LessThan(const GrResourceEntry& entry, const GrResourceKey& key) { 159 return entry.key() < key; 160 } 161 162 inline bool GrResourceKey::Equals(const GrResourceEntry& entry, const GrResourceKey& key) { 163 return entry.key() == key; 164 } 165 166 #ifdef SK_DEBUG 167 inline bool GrResourceKey::LessThan(const GrResourceEntry& a, const GrResourceEntry& b) { 168 return a.key() < b.key(); 169 } 170 171 inline bool GrResourceKey::Equals(const GrResourceEntry& a, const GrResourceEntry& b) { 172 return a.key() == b.key(); 173 } 174 #endif 175 176 /////////////////////////////////////////////////////////////////////////////// 177 178 /** 179 * Cache of GrResource objects. 180 * 181 * These have a corresponding GrResourceKey, built from 128bits identifying the 182 * resource. 183 * 184 * The cache stores the entries in a double-linked list, which is its LRU. 185 * When an entry is "locked" (i.e. given to the caller), it is moved to the 186 * head of the list. If/when we must purge some of the entries, we walk the 187 * list backwards from the tail, since those are the least recently used. 188 * 189 * For fast searches, we maintain a sorted array (based on the GrResourceKey) 190 * which we can bsearch. When a new entry is added, it is inserted into this 191 * array. 192 * 193 * For even faster searches, a hash is computed from the Key. If there is 194 * a collision between two keys with the same hash, we fall back on the 195 * bsearch, and update the hash to reflect the most recent Key requested. 196 * 197 * It is a goal to make the GrResourceCache the central repository and bookkeeper 198 * of all resources. It should replace the linked list of GrResources that 199 * GrGpu uses to call abandon/release. 200 */ 201 class GrResourceCache { 202 public: 203 GrResourceCache(int maxCount, size_t maxBytes); 204 ~GrResourceCache(); 205 206 /** 207 * Return the current resource cache limits. 208 * 209 * @param maxResource If non-null, returns maximum number of resources 210 * that can be held in the cache. 211 * @param maxBytes If non-null, returns maximum number of bytes of 212 * gpu memory that can be held in the cache. 213 */ 214 void getLimits(int* maxResources, size_t* maxBytes) const; 215 216 /** 217 * Specify the resource cache limits. If the current cache exceeds either 218 * of these, it will be purged (LRU) to keep the cache within these limits. 219 * 220 * @param maxResources The maximum number of resources that can be held in 221 * the cache. 222 * @param maxBytes The maximum number of bytes of resource memory that 223 * can be held in the cache. 224 */ 225 void setLimits(int maxResources, size_t maxResourceBytes); 226 227 /** 228 * The callback function used by the cache when it is still over budget 229 * after a purge. The passed in 'data' is the same 'data' handed to 230 * setOverbudgetCallback. The callback returns true if some resources 231 * have been freed. 232 */ 233 typedef bool (*PFOverbudgetCB)(void* data); 234 235 /** 236 * Set the callback the cache should use when it is still over budget 237 * after a purge. The 'data' provided here will be passed back to the 238 * callback. Note that the cache will attempt to purge any resources newly 239 * freed by the callback. 240 */ 241 void setOverbudgetCallback(PFOverbudgetCB overbudgetCB, void* data) { 242 fOverbudgetCB = overbudgetCB; 243 fOverbudgetData = data; 244 } 245 246 /** 247 * Returns the number of bytes consumed by cached resources. 248 */ 249 size_t getCachedResourceBytes() const { return fEntryBytes; } 250 251 // For a found or added resource to be completely exclusive to the caller 252 // both the kNoOtherOwners and kHide flags need to be specified 253 enum OwnershipFlags { 254 kNoOtherOwners_OwnershipFlag = 0x1, // found/added resource has no other owners 255 kHide_OwnershipFlag = 0x2 // found/added resource is hidden from future 'find's 256 }; 257 258 /** 259 * Search for an entry with the same Key. If found, return it. 260 * If not found, return null. 261 * If ownershipFlags includes kNoOtherOwners and a resource is returned 262 * then that resource has no other refs to it. 263 * If ownershipFlags includes kHide and a resource is returned then that 264 * resource will not be returned from future 'find' calls until it is 265 * 'freed' (and recycled) or makeNonExclusive is called. 266 * For a resource to be completely exclusive to a caller both kNoOtherOwners 267 * and kHide must be specified. 268 */ 269 GrResource* find(const GrResourceKey& key, 270 uint32_t ownershipFlags = 0); 271 272 /** 273 * Add the new resource to the cache (by creating a new cache entry based 274 * on the provided key and resource). 275 * 276 * Ownership of the resource is transferred to the resource cache, 277 * which will unref() it when it is purged or deleted. 278 * 279 * If ownershipFlags includes kHide, subsequent calls to 'find' will not 280 * return 'resource' until it is 'freed' (and recycled) or makeNonExclusive 281 * is called. 282 */ 283 void addResource(const GrResourceKey& key, 284 GrResource* resource, 285 uint32_t ownershipFlags = 0); 286 287 /** 288 * Determines if the cache contains an entry matching a key. If a matching 289 * entry exists but was detached then it will not be found. 290 */ 291 bool hasKey(const GrResourceKey& key) const { return NULL != fCache.find(key); } 292 293 /** 294 * Hide 'entry' so that future searches will not find it. Such 295 * hidden entries will not be purged. The entry still counts against 296 * the cache's budget and should be made non-exclusive when exclusive access 297 * is no longer needed. 298 */ 299 void makeExclusive(GrResourceEntry* entry); 300 301 /** 302 * Restore 'entry' so that it can be found by future searches. 'entry' 303 * will also be purgeable (provided its lock count is now 0.) 304 */ 305 void makeNonExclusive(GrResourceEntry* entry); 306 307 /** 308 * Remove a resource from the cache and delete it! 309 */ 310 void deleteResource(GrResourceEntry* entry); 311 312 /** 313 * Removes every resource in the cache that isn't locked. 314 */ 315 void purgeAllUnlocked(); 316 317 /** 318 * Allow cache to purge unused resources to obey resource limitations 319 * Note: this entry point will be hidden (again) once totally ref-driven 320 * cache maintenance is implemented. Note that the overbudget callback 321 * will be called if the initial purge doesn't get the cache under 322 * its budget. 323 * 324 * extraCount and extraBytes are added to the current resource allocation 325 * to make sure enough room is available for future additions (e.g, 326 * 10MB across 10 textures is about to be added). 327 */ 328 void purgeAsNeeded(int extraCount = 0, size_t extraBytes = 0); 329 330 #ifdef SK_DEBUG 331 void validate() const; 332 #else 333 void validate() const {} 334 #endif 335 336 #if GR_CACHE_STATS 337 void printStats(); 338 #endif 339 340 private: 341 enum BudgetBehaviors { 342 kAccountFor_BudgetBehavior, 343 kIgnore_BudgetBehavior 344 }; 345 346 void internalDetach(GrResourceEntry*, BudgetBehaviors behavior = kAccountFor_BudgetBehavior); 347 void attachToHead(GrResourceEntry*, BudgetBehaviors behavior = kAccountFor_BudgetBehavior); 348 349 void removeInvalidResource(GrResourceEntry* entry); 350 351 GrTHashTable<GrResourceEntry, GrResourceKey, 8> fCache; 352 353 // We're an internal doubly linked list 354 typedef SkTInternalLList<GrResourceEntry> EntryList; 355 EntryList fList; 356 357 #ifdef SK_DEBUG 358 // These objects cannot be returned by a search 359 EntryList fExclusiveList; 360 #endif 361 362 // our budget, used in purgeAsNeeded() 363 int fMaxCount; 364 size_t fMaxBytes; 365 366 // our current stats, related to our budget 367 #if GR_CACHE_STATS 368 int fHighWaterEntryCount; 369 size_t fHighWaterEntryBytes; 370 int fHighWaterClientDetachedCount; 371 size_t fHighWaterClientDetachedBytes; 372 #endif 373 374 int fEntryCount; 375 size_t fEntryBytes; 376 int fClientDetachedCount; 377 size_t fClientDetachedBytes; 378 379 // prevents recursive purging 380 bool fPurging; 381 382 PFOverbudgetCB fOverbudgetCB; 383 void* fOverbudgetData; 384 385 void internalPurge(int extraCount, size_t extraBytes); 386 387 // Listen for messages that a resource has been invalidated and purge cached junk proactively. 388 SkMessageBus<GrResourceInvalidatedMessage>::Inbox fInvalidationInbox; 389 void purgeInvalidated(); 390 391 #ifdef SK_DEBUG 392 static size_t countBytes(const SkTInternalLList<GrResourceEntry>& list); 393 #endif 394 }; 395 396 /////////////////////////////////////////////////////////////////////////////// 397 398 #ifdef SK_DEBUG 399 class GrAutoResourceCacheValidate { 400 public: 401 GrAutoResourceCacheValidate(GrResourceCache* cache) : fCache(cache) { 402 cache->validate(); 403 } 404 ~GrAutoResourceCacheValidate() { 405 fCache->validate(); 406 } 407 private: 408 GrResourceCache* fCache; 409 }; 410 #else 411 class GrAutoResourceCacheValidate { 412 public: 413 GrAutoResourceCacheValidate(GrResourceCache*) {} 414 }; 415 #endif 416 417 #endif 418