1 /* 2 Copyright 2010 Google Inc. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 18 #include "GrTextureCache.h" 19 #include "GrTexture.h" 20 21 GrTextureEntry::GrTextureEntry(const GrTextureKey& key, GrTexture* texture) 22 : fKey(key), fTexture(texture) { 23 fLockCount = 0; 24 fPrev = fNext = NULL; 25 26 // we assume ownership of the texture, and will unref it when we die 27 GrAssert(texture); 28 } 29 30 GrTextureEntry::~GrTextureEntry() { 31 fTexture->unref(); 32 } 33 34 #if GR_DEBUG 35 void GrTextureEntry::validate() const { 36 GrAssert(fLockCount >= 0); 37 GrAssert(fTexture); 38 fTexture->validate(); 39 } 40 #endif 41 42 /////////////////////////////////////////////////////////////////////////////// 43 44 GrTextureCache::GrTextureCache(int maxCount, size_t maxBytes) : 45 fMaxCount(maxCount), 46 fMaxBytes(maxBytes) { 47 fEntryCount = 0; 48 fEntryBytes = 0; 49 fClientDetachedCount = 0; 50 fClientDetachedBytes = 0; 51 52 fHead = fTail = NULL; 53 } 54 55 GrTextureCache::~GrTextureCache() { 56 GrAutoTextureCacheValidate atcv(this); 57 58 this->removeAll(); 59 } 60 61 void GrTextureCache::getLimits(int* maxTextures, size_t* maxTextureBytes) const{ 62 if (maxTextures) { 63 *maxTextures = fMaxCount; 64 } 65 if (maxTextureBytes) { 66 *maxTextureBytes = fMaxBytes; 67 } 68 } 69 70 void GrTextureCache::setLimits(int maxTextures, size_t maxTextureBytes) { 71 bool smaller = (maxTextures < fMaxCount) || (maxTextureBytes < fMaxBytes); 72 73 fMaxCount = maxTextures; 74 fMaxBytes = maxTextureBytes; 75 76 if (smaller) { 77 this->purgeAsNeeded(); 78 } 79 } 80 81 void GrTextureCache::internalDetach(GrTextureEntry* entry, 82 bool clientDetach) { 83 GrTextureEntry* prev = entry->fPrev; 84 GrTextureEntry* next = entry->fNext; 85 86 if (prev) { 87 prev->fNext = next; 88 } else { 89 fHead = next; 90 } 91 if (next) { 92 next->fPrev = prev; 93 } else { 94 fTail = prev; 95 } 96 97 // update our stats 98 if (clientDetach) { 99 fClientDetachedCount += 1; 100 fClientDetachedBytes += entry->texture()->sizeInBytes(); 101 } else { 102 fEntryCount -= 1; 103 fEntryBytes -= entry->texture()->sizeInBytes(); 104 } 105 } 106 107 void GrTextureCache::attachToHead(GrTextureEntry* entry, 108 bool clientReattach) { 109 entry->fPrev = NULL; 110 entry->fNext = fHead; 111 if (fHead) { 112 fHead->fPrev = entry; 113 } 114 fHead = entry; 115 if (NULL == fTail) { 116 fTail = entry; 117 } 118 119 // update our stats 120 if (clientReattach) { 121 fClientDetachedCount -= 1; 122 fClientDetachedBytes -= entry->texture()->sizeInBytes(); 123 } else { 124 fEntryCount += 1; 125 fEntryBytes += entry->texture()->sizeInBytes(); 126 } 127 } 128 129 class GrTextureCache::Key { 130 typedef GrTextureEntry T; 131 132 const GrTextureKey& fKey; 133 public: 134 Key(const GrTextureKey& key) : fKey(key) {} 135 136 uint32_t getHash() const { return fKey.hashIndex(); } 137 138 static bool LT(const T& entry, const Key& key) { 139 return entry.key() < key.fKey; 140 } 141 static bool EQ(const T& entry, const Key& key) { 142 return entry.key() == key.fKey; 143 } 144 #if GR_DEBUG 145 static uint32_t GetHash(const T& entry) { 146 return entry.key().hashIndex(); 147 } 148 static bool LT(const T& a, const T& b) { 149 return a.key() < b.key(); 150 } 151 static bool EQ(const T& a, const T& b) { 152 return a.key() == b.key(); 153 } 154 #endif 155 }; 156 157 GrTextureEntry* GrTextureCache::findAndLock(const GrTextureKey& key) { 158 GrAutoTextureCacheValidate atcv(this); 159 160 GrTextureEntry* entry = fCache.find(key); 161 if (entry) { 162 this->internalDetach(entry, false); 163 this->attachToHead(entry, false); 164 // mark the entry as "busy" so it doesn't get purged 165 entry->lock(); 166 } 167 return entry; 168 } 169 170 GrTextureEntry* GrTextureCache::createAndLock(const GrTextureKey& key, 171 GrTexture* texture) { 172 GrAutoTextureCacheValidate atcv(this); 173 174 GrTextureEntry* entry = new GrTextureEntry(key, texture); 175 176 this->attachToHead(entry, false); 177 fCache.insert(key, entry); 178 179 #if GR_DUMP_TEXTURE_UPLOAD 180 GrPrintf("--- add texture to cache %p, count=%d bytes= %d %d\n", 181 entry, fEntryCount, texture->sizeInBytes(), fEntryBytes); 182 #endif 183 184 // mark the entry as "busy" so it doesn't get purged 185 entry->lock(); 186 this->purgeAsNeeded(); 187 return entry; 188 } 189 190 void GrTextureCache::detach(GrTextureEntry* entry) { 191 internalDetach(entry, true); 192 fCache.remove(entry->fKey, entry); 193 } 194 195 void GrTextureCache::reattachAndUnlock(GrTextureEntry* entry) { 196 attachToHead(entry, true); 197 fCache.insert(entry->key(), entry); 198 unlock(entry); 199 } 200 201 void GrTextureCache::unlock(GrTextureEntry* entry) { 202 GrAutoTextureCacheValidate atcv(this); 203 204 GrAssert(entry); 205 GrAssert(entry->isLocked()); 206 GrAssert(fCache.find(entry->key())); 207 208 entry->unlock(); 209 this->purgeAsNeeded(); 210 } 211 212 void GrTextureCache::purgeAsNeeded() { 213 GrAutoTextureCacheValidate atcv(this); 214 215 GrTextureEntry* entry = fTail; 216 while (entry) { 217 if (fEntryCount <= fMaxCount && fEntryBytes <= fMaxBytes) { 218 break; 219 } 220 221 GrTextureEntry* prev = entry->fPrev; 222 if (!entry->isLocked()) { 223 // remove from our cache 224 fCache.remove(entry->fKey, entry); 225 226 // remove from our llist 227 this->internalDetach(entry, false); 228 229 #if GR_DUMP_TEXTURE_UPLOAD 230 GrPrintf("--- ~texture from cache %p [%d %d]\n", entry->texture(), 231 entry->texture()->width(), 232 entry->texture()->height()); 233 #endif 234 delete entry; 235 } 236 entry = prev; 237 } 238 } 239 240 void GrTextureCache::removeAll() { 241 GrAssert(!fClientDetachedCount); 242 GrAssert(!fClientDetachedBytes); 243 244 GrTextureEntry* entry = fHead; 245 while (entry) { 246 GrAssert(!entry->isLocked()); 247 248 GrTextureEntry* next = entry->fNext; 249 delete entry; 250 entry = next; 251 } 252 253 fCache.removeAll(); 254 fHead = fTail = NULL; 255 fEntryCount = 0; 256 fEntryBytes = 0; 257 } 258 259 /////////////////////////////////////////////////////////////////////////////// 260 261 #if GR_DEBUG 262 static int countMatches(const GrTextureEntry* head, const GrTextureEntry* target) { 263 const GrTextureEntry* entry = head; 264 int count = 0; 265 while (entry) { 266 if (target == entry) { 267 count += 1; 268 } 269 entry = entry->next(); 270 } 271 return count; 272 } 273 274 #if GR_DEBUG 275 static bool both_zero_or_nonzero(int count, size_t bytes) { 276 return (count == 0 && bytes == 0) || (count > 0 && bytes > 0); 277 } 278 #endif 279 280 void GrTextureCache::validate() const { 281 GrAssert(!fHead == !fTail); 282 GrAssert(both_zero_or_nonzero(fEntryCount, fEntryBytes)); 283 GrAssert(both_zero_or_nonzero(fClientDetachedCount, fClientDetachedBytes)); 284 GrAssert(fClientDetachedBytes <= fEntryBytes); 285 GrAssert(fClientDetachedCount <= fEntryCount); 286 GrAssert((fEntryCount - fClientDetachedCount) == fCache.count()); 287 288 fCache.validate(); 289 290 GrTextureEntry* entry = fHead; 291 int count = 0; 292 size_t bytes = 0; 293 while (entry) { 294 entry->validate(); 295 GrAssert(fCache.find(entry->key())); 296 count += 1; 297 bytes += entry->texture()->sizeInBytes(); 298 entry = entry->fNext; 299 } 300 GrAssert(count == fEntryCount - fClientDetachedCount); 301 GrAssert(bytes == fEntryBytes - fClientDetachedBytes); 302 303 count = 0; 304 for (entry = fTail; entry; entry = entry->fPrev) { 305 count += 1; 306 } 307 GrAssert(count == fEntryCount - fClientDetachedCount); 308 309 for (int i = 0; i < count; i++) { 310 int matches = countMatches(fHead, fCache.getArray()[i]); 311 GrAssert(1 == matches); 312 } 313 } 314 #endif 315 316 317