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