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 "GrTypes.h" 15 #include "GrTHashCache.h" 16 17 class GrResource; 18 19 // return true if a<b, or false if b<a 20 // 21 #define RET_IF_LT_OR_GT(a, b) \ 22 do { \ 23 if ((a) < (b)) { \ 24 return true; \ 25 } \ 26 if ((b) < (a)) { \ 27 return false; \ 28 } \ 29 } while (0) 30 31 /** 32 * Helper class for GrResourceCache, the Key is used to identify src data for 33 * a resource. It is identified by 2 32bit data fields which can hold any 34 * data (uninterpreted by the cache) and a width/height. 35 */ 36 class GrResourceKey { 37 public: 38 enum { 39 kHashBits = 7, 40 kHashCount = 1 << kHashBits, 41 kHashMask = kHashCount - 1 42 }; 43 GrResourceKey(uint32_t p0,uint32_t p1,uint32_t p2,uint32_t p3)44 GrResourceKey(uint32_t p0, uint32_t p1, uint32_t p2, uint32_t p3) { 45 fP[0] = p0; 46 fP[1] = p1; 47 fP[2] = p2; 48 fP[3] = p3; 49 this->computeHashIndex(); 50 } 51 GrResourceKey(uint32_t v[4])52 GrResourceKey(uint32_t v[4]) { 53 memcpy(fP, v, 4 * sizeof(uint32_t)); 54 this->computeHashIndex(); 55 } 56 GrResourceKey(const GrResourceKey & src)57 GrResourceKey(const GrResourceKey& src) { 58 memcpy(fP, src.fP, 4 * sizeof(uint32_t)); 59 #if GR_DEBUG 60 this->computeHashIndex(); 61 GrAssert(fHashIndex == src.fHashIndex); 62 #endif 63 fHashIndex = src.fHashIndex; 64 } 65 66 //!< returns hash value [0..kHashMask] for the key hashIndex()67 int hashIndex() const { return fHashIndex; } 68 69 friend bool operator==(const GrResourceKey& a, const GrResourceKey& b) { 70 GR_DEBUGASSERT(-1 != a.fHashIndex && -1 != b.fHashIndex); 71 return 0 == memcmp(a.fP, b.fP, 4 * sizeof(uint32_t)); 72 } 73 74 friend bool operator!=(const GrResourceKey& a, const GrResourceKey& b) { 75 GR_DEBUGASSERT(-1 != a.fHashIndex && -1 != b.fHashIndex); 76 return !(a == b); 77 } 78 79 friend bool operator<(const GrResourceKey& a, const GrResourceKey& b) { 80 RET_IF_LT_OR_GT(a.fP[0], b.fP[0]); 81 RET_IF_LT_OR_GT(a.fP[1], b.fP[1]); 82 RET_IF_LT_OR_GT(a.fP[2], b.fP[2]); 83 return a.fP[3] < b.fP[3]; 84 } 85 getValue32(int i)86 uint32_t getValue32(int i) const { 87 GrAssert(i >=0 && i < 4); 88 return fP[i]; 89 } 90 private: 91 rol(uint32_t x)92 static uint32_t rol(uint32_t x) { 93 return (x >> 24) | (x << 8); 94 } ror(uint32_t x)95 static uint32_t ror(uint32_t x) { 96 return (x >> 8) | (x << 24); 97 } rohalf(uint32_t x)98 static uint32_t rohalf(uint32_t x) { 99 return (x >> 16) | (x << 16); 100 } 101 computeHashIndex()102 void computeHashIndex() { 103 uint32_t hash = fP[0] ^ rol(fP[1]) ^ ror(fP[2]) ^ rohalf(fP[3]); 104 // this way to mix and reduce hash to its index may have to change 105 // depending on how many bits we allocate to the index 106 hash ^= hash >> 16; 107 hash ^= hash >> 8; 108 fHashIndex = hash & kHashMask; 109 } 110 111 uint32_t fP[4]; 112 113 // this is computed from the fP... fields 114 int fHashIndex; 115 116 friend class GrContext; 117 }; 118 119 /////////////////////////////////////////////////////////////////////////////// 120 121 class GrResourceEntry { 122 public: resource()123 GrResource* resource() const { return fResource; } key()124 const GrResourceKey& key() const { return fKey; } 125 126 #if GR_DEBUG next()127 GrResourceEntry* next() const { return fNext; } prev()128 GrResourceEntry* prev() const { return fPrev; } 129 #endif 130 131 #if GR_DEBUG 132 void validate() const; 133 #else validate()134 void validate() const {} 135 #endif 136 137 private: 138 GrResourceEntry(const GrResourceKey& key, GrResource* resource); 139 ~GrResourceEntry(); 140 isLocked()141 bool isLocked() const { return fLockCount != 0; } lock()142 void lock() { ++fLockCount; } unlock()143 void unlock() { 144 GrAssert(fLockCount > 0); 145 --fLockCount; 146 } 147 148 GrResourceKey fKey; 149 GrResource* fResource; 150 151 // track if we're in use, used when we need to purge 152 // we only purge unlocked entries 153 int fLockCount; 154 155 // we're a dlinklist 156 GrResourceEntry* fPrev; 157 GrResourceEntry* fNext; 158 159 friend class GrResourceCache; 160 }; 161 162 /////////////////////////////////////////////////////////////////////////////// 163 164 #include "GrTHashCache.h" 165 166 /** 167 * Cache of GrResource objects. 168 * 169 * These have a corresponding GrResourceKey, built from 128bits identifying the 170 * resource. 171 * 172 * The cache stores the entries in a double-linked list, which is its LRU. 173 * When an entry is "locked" (i.e. given to the caller), it is moved to the 174 * head of the list. If/when we must purge some of the entries, we walk the 175 * list backwards from the tail, since those are the least recently used. 176 * 177 * For fast searches, we maintain a sorted array (based on the GrResourceKey) 178 * which we can bsearch. When a new entry is added, it is inserted into this 179 * array. 180 * 181 * For even faster searches, a hash is computed from the Key. If there is 182 * a collision between two keys with the same hash, we fall back on the 183 * bsearch, and update the hash to reflect the most recent Key requested. 184 */ 185 class GrResourceCache { 186 public: 187 GrResourceCache(int maxCount, size_t maxBytes); 188 ~GrResourceCache(); 189 190 /** 191 * Return the current resource cache limits. 192 * 193 * @param maxResource If non-null, returns maximum number of resources 194 * that can be held in the cache. 195 * @param maxBytes If non-null, returns maximum number of bytes of 196 * gpu memory that can be held in the cache. 197 */ 198 void getLimits(int* maxResources, size_t* maxBytes) const; 199 200 /** 201 * Specify the resource cache limits. If the current cache exceeds either 202 * of these, it will be purged (LRU) to keep the cache within these limits. 203 * 204 * @param maxResources The maximum number of resources that can be held in 205 * the cache. 206 * @param maxBytes The maximum number of bytes of resource memory that 207 * can be held in the cache. 208 */ 209 void setLimits(int maxResource, size_t maxResourceBytes); 210 211 /** 212 * Returns the number of bytes consumed by cached resources. 213 */ getCachedResourceBytes()214 size_t getCachedResourceBytes() const { return fEntryBytes; } 215 216 /** 217 * Controls whether locks should be nestable or not. 218 */ 219 enum LockType { 220 kNested_LockType, 221 kSingle_LockType, 222 }; 223 224 /** 225 * Search for an entry with the same Key. If found, "lock" it and return it. 226 * If not found, return null. 227 */ 228 GrResourceEntry* findAndLock(const GrResourceKey&, LockType style); 229 230 /** 231 * Create a new entry, based on the specified key and resource, and return 232 * its "locked" entry. 233 * 234 * Ownership of the resource is transferred to the Entry, which will unref() 235 * it when we are purged or deleted. 236 */ 237 GrResourceEntry* createAndLock(const GrResourceKey&, GrResource*); 238 239 /** 240 * Determines if the cache contains an entry matching a key. If a matching 241 * entry exists but was detached then it will not be found. 242 */ 243 bool hasKey(const GrResourceKey& key) const; 244 245 /** 246 * Detach removes an entry from the cache. This prevents the entry from 247 * being found by a subsequent findAndLock() until it is reattached. The 248 * entry still counts against the cache's budget and should be reattached 249 * when exclusive access is no longer needed. 250 */ 251 void detach(GrResourceEntry*); 252 253 /** 254 * Reattaches a resource to the cache and unlocks it. Allows it to be found 255 * by a subsequent findAndLock or be purged (provided its lock count is 256 * now 0.) 257 */ 258 void reattachAndUnlock(GrResourceEntry*); 259 260 /** 261 * When done with an entry, call unlock(entry) on it, which returns it to 262 * a purgable state. 263 */ 264 void unlock(GrResourceEntry*); 265 266 void removeAll(); 267 268 #if GR_DEBUG 269 void validate() const; 270 #else validate()271 void validate() const {} 272 #endif 273 274 private: 275 void internalDetach(GrResourceEntry*, bool); 276 void attachToHead(GrResourceEntry*, bool); 277 void purgeAsNeeded(); 278 279 class Key; 280 GrTHashTable<GrResourceEntry, Key, 8> fCache; 281 282 // manage the dlink list 283 GrResourceEntry* fHead; 284 GrResourceEntry* fTail; 285 286 // our budget, used in purgeAsNeeded() 287 int fMaxCount; 288 size_t fMaxBytes; 289 290 // our current stats, related to our budget 291 int fEntryCount; 292 int fUnlockedEntryCount; 293 size_t fEntryBytes; 294 int fClientDetachedCount; 295 size_t fClientDetachedBytes; 296 297 // prevents recursive purging 298 bool fPurging; 299 }; 300 301 /////////////////////////////////////////////////////////////////////////////// 302 303 #if GR_DEBUG 304 class GrAutoResourceCacheValidate { 305 public: GrAutoResourceCacheValidate(GrResourceCache * cache)306 GrAutoResourceCacheValidate(GrResourceCache* cache) : fCache(cache) { 307 cache->validate(); 308 } ~GrAutoResourceCacheValidate()309 ~GrAutoResourceCacheValidate() { 310 fCache->validate(); 311 } 312 private: 313 GrResourceCache* fCache; 314 }; 315 #else 316 class GrAutoResourceCacheValidate { 317 public: GrAutoResourceCacheValidate(GrResourceCache *)318 GrAutoResourceCacheValidate(GrResourceCache*) {} 319 }; 320 #endif 321 322 #endif 323