GrResourceCache.cpp revision 5b25a8d72d129cdfd8fe001635941cf725bcf2bd
1 2/* 3 * Copyright 2010 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#include "GrResourceCache.h" 12#include "GrResource.h" 13 14 15GrResourceKey::ResourceType GrResourceKey::GenerateResourceType() { 16 static int32_t gNextType = 0; 17 18 int32_t type = sk_atomic_inc(&gNextType); 19 if (type >= (1 << 8 * sizeof(ResourceType))) { 20 GrCrash("Too many Resource Types"); 21 } 22 23 return static_cast<ResourceType>(type); 24} 25 26/////////////////////////////////////////////////////////////////////////////// 27 28GrResourceEntry::GrResourceEntry(const GrResourceKey& key, GrResource* resource) 29 : fKey(key), fResource(resource) { 30 // we assume ownership of the resource, and will unref it when we die 31 GrAssert(resource); 32 resource->ref(); 33} 34 35GrResourceEntry::~GrResourceEntry() { 36 fResource->setCacheEntry(NULL); 37 fResource->unref(); 38} 39 40#if GR_DEBUG 41void GrResourceEntry::validate() const { 42 GrAssert(fResource); 43 GrAssert(fResource->getCacheEntry() == this); 44 fResource->validate(); 45} 46#endif 47 48/////////////////////////////////////////////////////////////////////////////// 49 50GrResourceCache::GrResourceCache(int maxCount, size_t maxBytes) : 51 fMaxCount(maxCount), 52 fMaxBytes(maxBytes) { 53#if GR_CACHE_STATS 54 fHighWaterEntryCount = 0; 55 fHighWaterEntryBytes = 0; 56 fHighWaterClientDetachedCount = 0; 57 fHighWaterClientDetachedBytes = 0; 58#endif 59 60 fEntryCount = 0; 61 fEntryBytes = 0; 62 fClientDetachedCount = 0; 63 fClientDetachedBytes = 0; 64 65 fPurging = false; 66} 67 68GrResourceCache::~GrResourceCache() { 69 GrAutoResourceCacheValidate atcv(this); 70 71 EntryList::Iter iter; 72 73 // Unlike the removeAll, here we really remove everything, including locked resources. 74 while (GrResourceEntry* entry = fList.head()) { 75 GrAutoResourceCacheValidate atcv(this); 76 77 // remove from our cache 78 fCache.remove(entry->fKey, entry); 79 80 // remove from our llist 81 this->internalDetach(entry); 82 83 delete entry; 84 } 85} 86 87void GrResourceCache::getLimits(int* maxResources, size_t* maxResourceBytes) const{ 88 if (maxResources) { 89 *maxResources = fMaxCount; 90 } 91 if (maxResourceBytes) { 92 *maxResourceBytes = fMaxBytes; 93 } 94} 95 96void GrResourceCache::setLimits(int maxResources, size_t maxResourceBytes) { 97 bool smaller = (maxResources < fMaxCount) || (maxResourceBytes < fMaxBytes); 98 99 fMaxCount = maxResources; 100 fMaxBytes = maxResourceBytes; 101 102 if (smaller) { 103 this->purgeAsNeeded(); 104 } 105} 106 107void GrResourceCache::internalDetach(GrResourceEntry* entry, 108 BudgetBehaviors behavior) { 109 fList.remove(entry); 110 111 // update our stats 112 if (kIgnore_BudgetBehavior == behavior) { 113 fClientDetachedCount += 1; 114 fClientDetachedBytes += entry->resource()->sizeInBytes(); 115 116#if GR_CACHE_STATS 117 if (fHighWaterClientDetachedCount < fClientDetachedCount) { 118 fHighWaterClientDetachedCount = fClientDetachedCount; 119 } 120 if (fHighWaterClientDetachedBytes < fClientDetachedBytes) { 121 fHighWaterClientDetachedBytes = fClientDetachedBytes; 122 } 123#endif 124 125 } else { 126 GrAssert(kAccountFor_BudgetBehavior == behavior); 127 128 fEntryCount -= 1; 129 fEntryBytes -= entry->resource()->sizeInBytes(); 130 } 131} 132 133void GrResourceCache::attachToHead(GrResourceEntry* entry, 134 BudgetBehaviors behavior) { 135 fList.addToHead(entry); 136 137 // update our stats 138 if (kIgnore_BudgetBehavior == behavior) { 139 fClientDetachedCount -= 1; 140 fClientDetachedBytes -= entry->resource()->sizeInBytes(); 141 } else { 142 GrAssert(kAccountFor_BudgetBehavior == behavior); 143 144 fEntryCount += 1; 145 fEntryBytes += entry->resource()->sizeInBytes(); 146 147#if GR_CACHE_STATS 148 if (fHighWaterEntryCount < fEntryCount) { 149 fHighWaterEntryCount = fEntryCount; 150 } 151 if (fHighWaterEntryBytes < fEntryBytes) { 152 fHighWaterEntryBytes = fEntryBytes; 153 } 154#endif 155 } 156} 157 158// This functor just searches for an entry with only a single ref (from 159// the texture cache itself). Presumably in this situation no one else 160// is relying on the texture. 161class GrTFindUnreffedFunctor { 162public: 163 bool operator()(const GrResourceEntry* entry) const { 164 return 1 == entry->resource()->getRefCnt(); 165 } 166}; 167 168GrResource* GrResourceCache::find(const GrResourceKey& key, uint32_t ownershipFlags) { 169 GrAutoResourceCacheValidate atcv(this); 170 171 GrResourceEntry* entry = NULL; 172 173 if (ownershipFlags & kNoOtherOwners_OwnershipFlag) { 174 GrTFindUnreffedFunctor functor; 175 176 entry = fCache.find<GrTFindUnreffedFunctor>(key, functor); 177 } else { 178 entry = fCache.find(key); 179 } 180 181 if (NULL == entry) { 182 return NULL; 183 } 184 185 if (ownershipFlags & kHide_OwnershipFlag) { 186 this->makeExclusive(entry); 187 } else { 188 // Make this resource MRU 189 this->internalDetach(entry); 190 this->attachToHead(entry); 191 } 192 193 return entry->fResource; 194} 195 196bool GrResourceCache::hasKey(const GrResourceKey& key) const { 197 return NULL != fCache.find(key); 198} 199 200void GrResourceCache::addResource(const GrResourceKey& key, 201 GrResource* resource, 202 uint32_t ownershipFlags) { 203 GrAssert(NULL == resource->getCacheEntry()); 204 // we don't expect to create new resources during a purge. In theory 205 // this could cause purgeAsNeeded() into an infinite loop (e.g. 206 // each resource destroyed creates and locks 2 resources and 207 // unlocks 1 thereby causing a new purge). 208 GrAssert(!fPurging); 209 GrAutoResourceCacheValidate atcv(this); 210 211 GrResourceEntry* entry = SkNEW_ARGS(GrResourceEntry, (key, resource)); 212 resource->setCacheEntry(entry); 213 214 this->attachToHead(entry); 215 fCache.insert(key, entry); 216 217 if (ownershipFlags & kHide_OwnershipFlag) { 218 this->makeExclusive(entry); 219 } 220 221} 222 223void GrResourceCache::makeExclusive(GrResourceEntry* entry) { 224 GrAutoResourceCacheValidate atcv(this); 225 226 // When scratch textures are detached (to hide them from future finds) they 227 // still count against the resource budget 228 this->internalDetach(entry, kIgnore_BudgetBehavior); 229 fCache.remove(entry->key(), entry); 230 231#if GR_DEBUG 232 fExclusiveList.addToHead(entry); 233#endif 234} 235 236void GrResourceCache::removeInvalidResource(GrResourceEntry* entry) { 237 // If the resource went invalid while it was detached then purge it 238 // This can happen when a 3D context was lost, 239 // the client called GrContext::contextDestroyed() to notify Gr, 240 // and then later an SkGpuDevice's destructor releases its backing 241 // texture (which was invalidated at contextDestroyed time). 242 fClientDetachedCount -= 1; 243 fEntryCount -= 1; 244 size_t size = entry->resource()->sizeInBytes(); 245 fClientDetachedBytes -= size; 246 fEntryBytes -= size; 247} 248 249void GrResourceCache::makeNonExclusive(GrResourceEntry* entry) { 250 GrAutoResourceCacheValidate atcv(this); 251 252#if GR_DEBUG 253 fExclusiveList.remove(entry); 254#endif 255 256 if (entry->resource()->isValid()) { 257 // Since scratch textures still count against the cache budget even 258 // when they have been removed from the cache, re-adding them doesn't 259 // alter the budget information. 260 attachToHead(entry, kIgnore_BudgetBehavior); 261 fCache.insert(entry->key(), entry); 262 } else { 263 this->removeInvalidResource(entry); 264 } 265} 266 267/** 268 * Destroying a resource may potentially trigger the unlock of additional 269 * resources which in turn will trigger a nested purge. We block the nested 270 * purge using the fPurging variable. However, the initial purge will keep 271 * looping until either all resources in the cache are unlocked or we've met 272 * the budget. There is an assertion in createAndLock to check against a 273 * resource's destructor inserting new resources into the cache. If these 274 * new resources were unlocked before purgeAsNeeded completed it could 275 * potentially make purgeAsNeeded loop infinitely. 276 */ 277void GrResourceCache::purgeAsNeeded() { 278 if (!fPurging) { 279 fPurging = true; 280 bool withinBudget = false; 281 bool changed = false; 282 283 // The purging process is repeated several times since one pass 284 // may free up other resources 285 do { 286 EntryList::Iter iter; 287 288 changed = false; 289 290 // Note: the following code relies on the fact that the 291 // doubly linked list doesn't invalidate its data/pointers 292 // outside of the specific area where a deletion occurs (e.g., 293 // in internalDetach) 294 GrResourceEntry* entry = iter.init(fList, EntryList::Iter::kTail_IterStart); 295 296 while (NULL != entry) { 297 GrAutoResourceCacheValidate atcv(this); 298 299 if (fEntryCount <= fMaxCount && fEntryBytes <= fMaxBytes) { 300 withinBudget = true; 301 break; 302 } 303 304 GrResourceEntry* prev = iter.prev(); 305 if (1 == entry->fResource->getRefCnt()) { 306 changed = true; 307 308 // remove from our cache 309 fCache.remove(entry->key(), entry); 310 311 // remove from our llist 312 this->internalDetach(entry); 313 delete entry; 314 } 315 entry = prev; 316 } 317 } while (!withinBudget && changed); 318 fPurging = false; 319 } 320} 321 322void GrResourceCache::purgeAllUnlocked() { 323 GrAutoResourceCacheValidate atcv(this); 324 325 // we can have one GrResource holding a lock on another 326 // so we don't want to just do a simple loop kicking each 327 // entry out. Instead change the budget and purge. 328 329 int savedMaxBytes = fMaxBytes; 330 int savedMaxCount = fMaxCount; 331 fMaxBytes = (size_t) -1; 332 fMaxCount = 0; 333 this->purgeAsNeeded(); 334 335#if GR_DEBUG 336 GrAssert(fExclusiveList.countEntries() == fClientDetachedCount); 337 GrAssert(countBytes(fExclusiveList) == fClientDetachedBytes); 338 if (!fCache.count()) { 339 // Items may have been detached from the cache (such as the backing 340 // texture for an SkGpuDevice). The above purge would not have removed 341 // them. 342 GrAssert(fEntryCount == fClientDetachedCount); 343 GrAssert(fEntryBytes == fClientDetachedBytes); 344 GrAssert(fList.isEmpty()); 345 } 346#endif 347 348 fMaxBytes = savedMaxBytes; 349 fMaxCount = savedMaxCount; 350} 351 352/////////////////////////////////////////////////////////////////////////////// 353 354#if GR_DEBUG 355size_t GrResourceCache::countBytes(const EntryList& list) { 356 size_t bytes = 0; 357 358 EntryList::Iter iter; 359 360 const GrResourceEntry* entry = iter.init(const_cast<EntryList&>(list), 361 EntryList::Iter::kTail_IterStart); 362 363 for ( ; NULL != entry; entry = iter.prev()) { 364 bytes += entry->resource()->sizeInBytes(); 365 } 366 return bytes; 367} 368 369static bool both_zero_or_nonzero(int count, size_t bytes) { 370 return (count == 0 && bytes == 0) || (count > 0 && bytes > 0); 371} 372 373void GrResourceCache::validate() const { 374 fList.validate(); 375 fExclusiveList.validate(); 376 GrAssert(both_zero_or_nonzero(fEntryCount, fEntryBytes)); 377 GrAssert(both_zero_or_nonzero(fClientDetachedCount, fClientDetachedBytes)); 378 GrAssert(fClientDetachedBytes <= fEntryBytes); 379 GrAssert(fClientDetachedCount <= fEntryCount); 380 GrAssert((fEntryCount - fClientDetachedCount) == fCache.count()); 381 382 fCache.validate(); 383 384 385 EntryList::Iter iter; 386 387 // check that the exclusively held entries are okay 388 const GrResourceEntry* entry = iter.init(const_cast<EntryList&>(fExclusiveList), 389 EntryList::Iter::kHead_IterStart); 390 391 for ( ; NULL != entry; entry = iter.next()) { 392 entry->validate(); 393 } 394 395 // check that the shareable entries are okay 396 entry = iter.init(const_cast<EntryList&>(fList), EntryList::Iter::kHead_IterStart); 397 398 int count = 0; 399 for ( ; NULL != entry; entry = iter.next()) { 400 entry->validate(); 401 GrAssert(fCache.find(entry->key())); 402 count += 1; 403 } 404 GrAssert(count == fEntryCount - fClientDetachedCount); 405 406 size_t bytes = countBytes(fList); 407 GrAssert(bytes == fEntryBytes - fClientDetachedBytes); 408 409 bytes = countBytes(fExclusiveList); 410 GrAssert(bytes == fClientDetachedBytes); 411 412 GrAssert(fList.countEntries() == fEntryCount - fClientDetachedCount); 413 414 GrAssert(fExclusiveList.countEntries() == fClientDetachedCount); 415} 416#endif // GR_DEBUG 417 418#if GR_CACHE_STATS 419 420void GrResourceCache::printStats() { 421 int locked = 0; 422 423 EntryList::Iter iter; 424 425 GrResourceEntry* entry = iter.init(fList, EntryList::Iter::kTail_IterStart); 426 427 for ( ; NULL != entry; entry = iter.prev()) { 428 if (entry->fResource->getRefCnt() > 1) { 429 ++locked; 430 } 431 } 432 433 SkDebugf("Budget: %d items %d bytes\n", fMaxCount, fMaxBytes); 434 SkDebugf("\t\tEntry Count: current %d (%d locked) high %d\n", 435 fEntryCount, locked, fHighWaterEntryCount); 436 SkDebugf("\t\tEntry Bytes: current %d high %d\n", 437 fEntryBytes, fHighWaterEntryBytes); 438 SkDebugf("\t\tDetached Entry Count: current %d high %d\n", 439 fClientDetachedCount, fHighWaterClientDetachedCount); 440 SkDebugf("\t\tDetached Bytes: current %d high %d\n", 441 fClientDetachedBytes, fHighWaterClientDetachedBytes); 442} 443 444#endif 445 446/////////////////////////////////////////////////////////////////////////////// 447