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(hashedKey); 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 * It is a goal to make the GrResourceCache the central repository and bookkeeper 211 * of all resources. It should replace the linked list of GrResources that 212 * GrGpu uses to call abandon/release. 213 */ 214 class GrResourceCache { 215 public: 216 GrResourceCache(int maxCount, size_t maxBytes); 217 ~GrResourceCache(); 218 219 /** 220 * Return the current resource cache limits. 221 * 222 * @param maxResource If non-null, returns maximum number of resources 223 * that can be held in the cache. 224 * @param maxBytes If non-null, returns maximum number of bytes of 225 * gpu memory that can be held in the cache. 226 */ 227 void getLimits(int* maxResources, size_t* maxBytes) const; 228 229 /** 230 * Specify the resource cache limits. If the current cache exceeds either 231 * of these, it will be purged (LRU) to keep the cache within these limits. 232 * 233 * @param maxResources The maximum number of resources that can be held in 234 * the cache. 235 * @param maxBytes The maximum number of bytes of resource memory that 236 * can be held in the cache. 237 */ 238 void setLimits(int maxResources, size_t maxResourceBytes); 239 240 /** 241 * The callback function used by the cache when it is still over budget 242 * after a purge. The passed in 'data' is the same 'data' handed to 243 * setOverbudgetCallback. The callback returns true if some resources 244 * have been freed. 245 */ 246 typedef bool (*PFOverbudgetCB)(void* data); 247 248 /** 249 * Set the callback the cache should use when it is still over budget 250 * after a purge. The 'data' provided here will be passed back to the 251 * callback. Note that the cache will attempt to purge any resources newly 252 * freed by the callback. 253 */ 254 void setOverbudgetCallback(PFOverbudgetCB overbudgetCB, void* data) { 255 fOverbudgetCB = overbudgetCB; 256 fOverbudgetData = data; 257 } 258 259 /** 260 * Returns the number of bytes consumed by cached resources. 261 */ 262 size_t getCachedResourceBytes() const { return fEntryBytes; } 263 264 // For a found or added resource to be completely exclusive to the caller 265 // both the kNoOtherOwners and kHide flags need to be specified 266 enum OwnershipFlags { 267 kNoOtherOwners_OwnershipFlag = 0x1, // found/added resource has no other owners 268 kHide_OwnershipFlag = 0x2 // found/added resource is hidden from future 'find's 269 }; 270 271 /** 272 * Search for an entry with the same Key. If found, return it. 273 * If not found, return null. 274 * If ownershipFlags includes kNoOtherOwners and a resource is returned 275 * then that resource has no other refs to it. 276 * If ownershipFlags includes kHide and a resource is returned then that 277 * resource will not be returned from future 'find' calls until it is 278 * 'freed' (and recycled) or makeNonExclusive is called. 279 * For a resource to be completely exclusive to a caller both kNoOtherOwners 280 * and kHide must be specified. 281 */ 282 GrResource* find(const GrResourceKey& key, 283 uint32_t ownershipFlags = 0); 284 285 /** 286 * Add the new resource to the cache (by creating a new cache entry based 287 * on the provided key and resource). 288 * 289 * Ownership of the resource is transferred to the resource cache, 290 * which will unref() it when it is purged or deleted. 291 * 292 * If ownershipFlags includes kHide, subsequent calls to 'find' will not 293 * return 'resource' until it is 'freed' (and recycled) or makeNonExclusive 294 * is called. 295 */ 296 void addResource(const GrResourceKey& key, 297 GrResource* resource, 298 uint32_t ownershipFlags = 0); 299 300 /** 301 * Determines if the cache contains an entry matching a key. If a matching 302 * entry exists but was detached then it will not be found. 303 */ 304 bool hasKey(const GrResourceKey& key) const { return NULL != fCache.find(key); } 305 306 /** 307 * Hide 'entry' so that future searches will not find it. Such 308 * hidden entries will not be purged. The entry still counts against 309 * the cache's budget and should be made non-exclusive when exclusive access 310 * is no longer needed. 311 */ 312 void makeExclusive(GrResourceEntry* entry); 313 314 /** 315 * Restore 'entry' so that it can be found by future searches. 'entry' 316 * will also be purgeable (provided its lock count is now 0.) 317 */ 318 void makeNonExclusive(GrResourceEntry* entry); 319 320 /** 321 * Remove a resource from the cache and delete it! 322 */ 323 void deleteResource(GrResourceEntry* entry); 324 325 /** 326 * Removes every resource in the cache that isn't locked. 327 */ 328 void purgeAllUnlocked(); 329 330 /** 331 * Allow cache to purge unused resources to obey resource limitations 332 * Note: this entry point will be hidden (again) once totally ref-driven 333 * cache maintenance is implemented. Note that the overbudget callback 334 * will be called if the initial purge doesn't get the cache under 335 * its budget. 336 * 337 * extraCount and extraBytes are added to the current resource allocation 338 * to make sure enough room is available for future additions (e.g, 339 * 10MB across 10 textures is about to be added). 340 */ 341 void purgeAsNeeded(int extraCount = 0, size_t extraBytes = 0); 342 343 #if GR_DEBUG 344 void validate() const; 345 #else 346 void validate() const {} 347 #endif 348 349 #if GR_CACHE_STATS 350 void printStats(); 351 #endif 352 353 private: 354 enum BudgetBehaviors { 355 kAccountFor_BudgetBehavior, 356 kIgnore_BudgetBehavior 357 }; 358 359 void internalDetach(GrResourceEntry*, BudgetBehaviors behavior = kAccountFor_BudgetBehavior); 360 void attachToHead(GrResourceEntry*, BudgetBehaviors behavior = kAccountFor_BudgetBehavior); 361 362 void removeInvalidResource(GrResourceEntry* entry); 363 364 GrTHashTable<GrResourceEntry, GrResourceKey, 8> fCache; 365 366 // We're an internal doubly linked list 367 typedef SkTInternalLList<GrResourceEntry> EntryList; 368 EntryList fList; 369 370 #if GR_DEBUG 371 // These objects cannot be returned by a search 372 EntryList fExclusiveList; 373 #endif 374 375 // our budget, used in purgeAsNeeded() 376 int fMaxCount; 377 size_t fMaxBytes; 378 379 // our current stats, related to our budget 380 #if GR_CACHE_STATS 381 int fHighWaterEntryCount; 382 size_t fHighWaterEntryBytes; 383 int fHighWaterClientDetachedCount; 384 size_t fHighWaterClientDetachedBytes; 385 #endif 386 387 int fEntryCount; 388 size_t fEntryBytes; 389 int fClientDetachedCount; 390 size_t fClientDetachedBytes; 391 392 // prevents recursive purging 393 bool fPurging; 394 395 PFOverbudgetCB fOverbudgetCB; 396 void* fOverbudgetData; 397 398 void internalPurge(int extraCount, size_t extraBytes); 399 400 #if GR_DEBUG 401 static size_t countBytes(const SkTInternalLList<GrResourceEntry>& list); 402 #endif 403 }; 404 405 /////////////////////////////////////////////////////////////////////////////// 406 407 #if GR_DEBUG 408 class GrAutoResourceCacheValidate { 409 public: 410 GrAutoResourceCacheValidate(GrResourceCache* cache) : fCache(cache) { 411 cache->validate(); 412 } 413 ~GrAutoResourceCacheValidate() { 414 fCache->validate(); 415 } 416 private: 417 GrResourceCache* fCache; 418 }; 419 #else 420 class GrAutoResourceCacheValidate { 421 public: 422 GrAutoResourceCacheValidate(GrResourceCache*) {} 423 }; 424 #endif 425 426 #endif 427