Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 | 1 |
| 2 /* | 2 /* |
| 3 * Copyright 2010 Google Inc. | 3 * Copyright 2010 Google Inc. |
| 4 * | 4 * |
| 5 * Use of this source code is governed by a BSD-style license that can be | 5 * Use of this source code is governed by a BSD-style license that can be |
| 6 * found in the LICENSE file. | 6 * found in the LICENSE file. |
| 7 */ | 7 */ |
| 8 | 8 |
| 9 | 9 |
| 10 | 10 |
| 11 #include "GrResourceCache.h" | 11 #include "GrResourceCache.h" |
| 12 #include "GrResource.h" | 12 #include "GrResource.h" |
| 13 | 13 |
| 14 | 14 |
| 15 GrResourceKey::ResourceType GrResourceKey::GenerateResourceType() { | 15 GrResourceKey::ResourceType GrResourceKey::GenerateResourceType() { |
| 16 static int32_t gNextType = 0; | 16 static int32_t gNextType = 0; |
| 17 | 17 |
| 18 int32_t type = sk_atomic_inc(&gNextType); | 18 int32_t type = sk_atomic_inc(&gNextType); |
| 19 if (type >= (1 << 8 * sizeof(ResourceType))) { | 19 if (type >= (1 << 8 * sizeof(ResourceType))) { |
| 20 GrCrash("Too many Resource Types"); | 20 GrCrash("Too many Resource Types"); |
| 21 } | 21 } |
| 22 | 22 |
| 23 return static_cast<ResourceType>(type); | 23 return static_cast<ResourceType>(type); |
| 24 } | 24 } |
| 25 | 25 |
| 26 /////////////////////////////////////////////////////////////////////////////// | 26 /////////////////////////////////////////////////////////////////////////////// |
| 27 | 27 |
| 28 GrResourceEntry::GrResourceEntry(const GrResourceKey& key, GrResource* resource) | 28 GrResourceEntry::GrResourceEntry(const GrResourceKey& key, GrResource* firstReso urce) |
| 29 : fKey(key), fResource(resource) { | 29 : fKey(key) |
| 30 // we assume ownership of the resource, and will unref it when we die | 30 , fExclusiveCount(0) { |
| 31 SkASSERT(resource); | 31 addResource(firstResource); |
|
mtklein
2013/12/03 19:02:02
this->
| |
| 32 resource->ref(); | |
| 33 } | 32 } |
| 34 | 33 |
| 35 GrResourceEntry::~GrResourceEntry() { | 34 GrResourceEntry::~GrResourceEntry() { |
|
mtklein
2013/12/03 19:02:02
The responsibility for resource ownership here see
| |
| 36 fResource->setCacheEntry(NULL); | 35 SkASSERT(isEmpty()); |
| 37 fResource->unref(); | 36 } |
| 37 | |
| 38 void GrResourceEntry::addResource(GrResource* resource) { | |
| 39 // We assume ownership of the resource, and will unref it when resource is | |
| 40 // removed from the entry. | |
| 41 resource->ref(); | |
| 42 resource->setCacheEntry(this); | |
| 43 fResources.addToHead(resource); | |
| 38 } | 44 } |
| 39 | 45 |
| 40 #ifdef SK_DEBUG | 46 bool GrResourceEntry::removeResource(GrResource* resource) { |
| 41 void GrResourceEntry::validate() const { | 47 fResources.remove(resource); |
| 42 SkASSERT(fResource); | 48 |
| 43 SkASSERT(fResource->getCacheEntry() == this); | 49 // The unref() might run destructor which would remove other resources from this entry and delete the |
| 44 fResource->validate(); | 50 // entry. Temporarily increase exclusive count, so that entry stays alive. |
| 51 ++fExclusiveCount; | |
| 52 | |
| 53 resource->setCacheEntry(NULL); | |
| 54 resource->unref(); | |
| 55 | |
| 56 --fExclusiveCount; | |
| 57 | |
| 58 return isEmpty(); | |
| 45 } | 59 } |
| 46 #endif | 60 |
| 61 bool GrResourceEntry::removeExclusiveResource(GrResource* resource) { | |
| 62 resource->setCacheEntry(NULL); | |
| 63 resource->unref(); | |
| 64 | |
| 65 // Decrease the count after unref(). See removeResource. | |
| 66 --fExclusiveCount; | |
| 67 | |
| 68 return isEmpty(); | |
| 69 } | |
| 70 | |
| 71 void GrResourceEntry::makeExclusive(GrResource* resource) { | |
| 72 fResources.remove(resource); | |
| 73 fExclusiveCount++; | |
| 74 } | |
| 75 | |
| 76 void GrResourceEntry::makeNonExclusive(GrResource* resource) { | |
| 77 fResources.addToHead(resource); | |
| 78 fExclusiveCount--; | |
| 79 } | |
| 47 | 80 |
| 48 /////////////////////////////////////////////////////////////////////////////// | 81 /////////////////////////////////////////////////////////////////////////////// |
| 49 | 82 |
| 50 GrResourceCache::GrResourceCache(int maxCount, size_t maxBytes) : | 83 GrResourceCache::GrResourceCache(int maxCount, size_t maxBytes) : |
| 51 fMaxCount(maxCount), | 84 fMaxCount(maxCount), |
| 52 fMaxBytes(maxBytes) { | 85 fMaxBytes(maxBytes) { |
| 53 #if GR_CACHE_STATS | 86 #if GR_CACHE_STATS |
| 54 fHighWaterEntryCount = 0; | 87 fHighWaterEntryCount = 0; |
| 55 fHighWaterEntryBytes = 0; | 88 fHighWaterEntryBytes = 0; |
| 56 fHighWaterClientDetachedCount = 0; | 89 fHighWaterClientDetachedCount = 0; |
| 57 fHighWaterClientDetachedBytes = 0; | 90 fHighWaterClientDetachedBytes = 0; |
| 58 #endif | 91 #endif |
| 59 | 92 |
| 60 fEntryCount = 0; | 93 fEntryCount = 0; |
| 61 fEntryBytes = 0; | 94 fEntryBytes = 0; |
| 62 fClientDetachedCount = 0; | 95 fClientDetachedCount = 0; |
| 63 fClientDetachedBytes = 0; | 96 fClientDetachedBytes = 0; |
| 64 | 97 |
| 65 fPurging = false; | 98 fPurging = false; |
| 66 | 99 |
| 67 fOverbudgetCB = NULL; | 100 fOverbudgetCB = NULL; |
| 68 fOverbudgetData = NULL; | 101 fOverbudgetData = NULL; |
| 69 } | 102 } |
| 70 | 103 |
| 71 GrResourceCache::~GrResourceCache() { | 104 GrResourceCache::~GrResourceCache() { |
| 72 GrAutoResourceCacheValidate atcv(this); | 105 GrAutoResourceCacheValidate atcv(this); |
| 73 | 106 |
| 74 EntryList::Iter iter; | 107 while (GrResource* resource = fList.head()) { |
| 108 GrAutoResourceCacheValidate atcv(this); | |
| 109 this->removeResource(resource); | |
| 110 } | |
| 75 | 111 |
| 76 // Unlike the removeAll, here we really remove everything, including locked resources. | 112 SkASSERT(fCache.count() == 0); |
| 77 while (GrResourceEntry* entry = fList.head()) { | |
| 78 GrAutoResourceCacheValidate atcv(this); | |
| 79 | |
| 80 // remove from our cache | |
| 81 fCache.remove(entry->fKey, entry); | |
| 82 | |
| 83 // remove from our llist | |
| 84 this->internalDetach(entry); | |
| 85 | |
| 86 delete entry; | |
| 87 } | |
| 88 } | 113 } |
| 89 | 114 |
| 90 void GrResourceCache::getLimits(int* maxResources, size_t* maxResourceBytes) con st{ | 115 void GrResourceCache::getLimits(int* maxResources, size_t* maxResourceBytes) con st{ |
| 91 if (NULL != maxResources) { | 116 if (NULL != maxResources) { |
| 92 *maxResources = fMaxCount; | 117 *maxResources = fMaxCount; |
| 93 } | 118 } |
| 94 if (NULL != maxResourceBytes) { | 119 if (NULL != maxResourceBytes) { |
| 95 *maxResourceBytes = fMaxBytes; | 120 *maxResourceBytes = fMaxBytes; |
| 96 } | 121 } |
| 97 } | 122 } |
| 98 | 123 |
| 99 void GrResourceCache::setLimits(int maxResources, size_t maxResourceBytes) { | 124 void GrResourceCache::setLimits(int maxResources, size_t maxResourceBytes) { |
| 100 bool smaller = (maxResources < fMaxCount) || (maxResourceBytes < fMaxBytes); | 125 bool smaller = (maxResources < fMaxCount) || (maxResourceBytes < fMaxBytes); |
| 101 | 126 |
| 102 fMaxCount = maxResources; | 127 fMaxCount = maxResources; |
| 103 fMaxBytes = maxResourceBytes; | 128 fMaxBytes = maxResourceBytes; |
| 104 | 129 |
| 105 if (smaller) { | 130 if (smaller) { |
| 106 this->purgeAsNeeded(); | 131 this->purgeAsNeeded(); |
| 107 } | 132 } |
| 108 } | 133 } |
| 109 | 134 |
| 110 void GrResourceCache::internalDetach(GrResourceEntry* entry, | 135 void GrResourceCache::internalDetach(GrResource* resource, |
| 111 BudgetBehaviors behavior) { | 136 BudgetBehaviors behavior) { |
| 112 fList.remove(entry); | 137 fList.remove(resource); |
| 113 | 138 |
| 114 // update our stats | 139 // update our stats |
| 115 if (kIgnore_BudgetBehavior == behavior) { | 140 if (kIgnore_BudgetBehavior == behavior) { |
| 116 fClientDetachedCount += 1; | 141 fClientDetachedCount += 1; |
| 117 fClientDetachedBytes += entry->resource()->sizeInBytes(); | 142 fClientDetachedBytes += resource->sizeInBytes(); |
| 118 | 143 |
| 119 #if GR_CACHE_STATS | 144 #if GR_CACHE_STATS |
| 120 if (fHighWaterClientDetachedCount < fClientDetachedCount) { | 145 if (fHighWaterClientDetachedCount < fClientDetachedCount) { |
| 121 fHighWaterClientDetachedCount = fClientDetachedCount; | 146 fHighWaterClientDetachedCount = fClientDetachedCount; |
| 122 } | 147 } |
| 123 if (fHighWaterClientDetachedBytes < fClientDetachedBytes) { | 148 if (fHighWaterClientDetachedBytes < fClientDetachedBytes) { |
| 124 fHighWaterClientDetachedBytes = fClientDetachedBytes; | 149 fHighWaterClientDetachedBytes = fClientDetachedBytes; |
| 125 } | 150 } |
| 126 #endif | 151 #endif |
| 127 | 152 |
| 128 } else { | 153 } else { |
| 129 SkASSERT(kAccountFor_BudgetBehavior == behavior); | 154 SkASSERT(kAccountFor_BudgetBehavior == behavior); |
| 130 | 155 |
| 131 fEntryCount -= 1; | 156 fEntryCount -= 1; |
| 132 fEntryBytes -= entry->resource()->sizeInBytes(); | 157 fEntryBytes -= resource->sizeInBytes(); |
| 133 } | 158 } |
| 134 } | 159 } |
| 135 | 160 |
| 136 void GrResourceCache::attachToHead(GrResourceEntry* entry, | 161 void GrResourceCache::attachToHead(GrResource* resource, |
| 137 BudgetBehaviors behavior) { | 162 BudgetBehaviors behavior) { |
| 138 fList.addToHead(entry); | 163 fList.addToHead(resource); |
| 139 | 164 |
| 140 // update our stats | 165 // update our stats |
| 141 if (kIgnore_BudgetBehavior == behavior) { | 166 if (kIgnore_BudgetBehavior == behavior) { |
| 142 fClientDetachedCount -= 1; | 167 fClientDetachedCount -= 1; |
| 143 fClientDetachedBytes -= entry->resource()->sizeInBytes(); | 168 fClientDetachedBytes -= resource->sizeInBytes(); |
| 144 } else { | 169 } else { |
| 145 SkASSERT(kAccountFor_BudgetBehavior == behavior); | 170 SkASSERT(kAccountFor_BudgetBehavior == behavior); |
| 146 | 171 |
| 147 fEntryCount += 1; | 172 fEntryCount += 1; |
| 148 fEntryBytes += entry->resource()->sizeInBytes(); | 173 fEntryBytes += resource->sizeInBytes(); |
| 149 | 174 |
| 150 #if GR_CACHE_STATS | 175 #if GR_CACHE_STATS |
| 151 if (fHighWaterEntryCount < fEntryCount) { | 176 if (fHighWaterEntryCount < fEntryCount) { |
| 152 fHighWaterEntryCount = fEntryCount; | 177 fHighWaterEntryCount = fEntryCount; |
| 153 } | 178 } |
| 154 if (fHighWaterEntryBytes < fEntryBytes) { | 179 if (fHighWaterEntryBytes < fEntryBytes) { |
| 155 fHighWaterEntryBytes = fEntryBytes; | 180 fHighWaterEntryBytes = fEntryBytes; |
| 156 } | 181 } |
| 157 #endif | 182 #endif |
| 158 } | 183 } |
| 159 } | 184 } |
| 160 | 185 |
| 161 // This functor just searches for an entry with only a single ref (from | 186 // This functor just searches for an entry with only a single ref (from |
| 162 // the texture cache itself). Presumably in this situation no one else | 187 // the texture cache itself). Presumably in this situation no one else |
| 163 // is relying on the texture. | 188 // is relying on the texture. |
| 164 class GrTFindUnreffedFunctor { | 189 class GrTFindUnreffedFunctor { |
| 165 public: | 190 public: |
| 166 bool operator()(const GrResourceEntry* entry) const { | 191 bool operator()(const GrResource* resource) const { |
| 167 return entry->resource()->unique(); | 192 return resource->unique(); |
| 168 } | 193 } |
| 169 }; | 194 }; |
| 170 | 195 |
| 171 GrResource* GrResourceCache::find(const GrResourceKey& key, uint32_t ownershipFl ags) { | 196 GrResource* GrResourceCache::find(const GrResourceKey& key, uint32_t ownershipFl ags) { |
| 172 GrAutoResourceCacheValidate atcv(this); | 197 GrAutoResourceCacheValidate atcv(this); |
| 173 | 198 |
| 174 GrResourceEntry* entry = NULL; | 199 GrResourceEntry* entry = fCache.find(key); |
| 175 | |
| 176 if (ownershipFlags & kNoOtherOwners_OwnershipFlag) { | |
| 177 GrTFindUnreffedFunctor functor; | |
| 178 | |
| 179 entry = fCache.find<GrTFindUnreffedFunctor>(key, functor); | |
| 180 } else { | |
| 181 entry = fCache.find(key); | |
| 182 } | |
| 183 | |
| 184 if (NULL == entry) { | 200 if (NULL == entry) { |
| 185 return NULL; | 201 return NULL; |
| 186 } | 202 } |
| 187 | 203 |
| 204 GrResource* resource; | |
| 205 if (ownershipFlags & kNoOtherOwners_OwnershipFlag) { | |
| 206 GrTFindUnreffedFunctor functor; | |
| 207 resource = entry->resources().find(functor); | |
| 208 } else { | |
| 209 // Find a resource not referenced outside cache, or the least referenced one. | |
| 210 typedef GrResourceEntry::CacheEntryResourcesInternalLListType::Iter Entr yResourcesIter; | |
| 211 | |
| 212 EntryResourcesIter iter; | |
| 213 resource = iter.init(entry->resources(), EntryResourcesIter::kTail_IterS tart); | |
|
mtklein
2013/12/03 19:02:02
If we don't need to iterate both ways down the lis
| |
| 214 if (NULL != resource && resource->getRefCnt() > 1) { | |
|
mtklein
2013/12/03 19:02:02
Oooh, calls to getRefCnt() make me super scared.
| |
| 215 int refCount = resource->getRefCnt(); | |
| 216 for (GrResource* nextResource = iter.next(); | |
| 217 NULL != nextResource && refCount > 1; | |
| 218 nextResource = iter.next()) { | |
| 219 if (nextResource->getRefCnt() > refCount) { | |
| 220 resource = nextResource; | |
| 221 refCount = nextResource->getRefCnt(); | |
| 222 } | |
| 223 } | |
| 224 } | |
| 225 } | |
| 226 | |
| 227 if (NULL == resource) { | |
| 228 return NULL; | |
| 229 } | |
| 230 | |
| 188 if (ownershipFlags & kHide_OwnershipFlag) { | 231 if (ownershipFlags & kHide_OwnershipFlag) { |
| 189 this->makeExclusive(entry); | 232 this->makeExclusive(resource); |
| 190 } else { | 233 } else { |
| 191 // Make this resource MRU | 234 // Make this resource MRU |
| 192 this->internalDetach(entry); | 235 this->internalDetach(resource); |
| 193 this->attachToHead(entry); | 236 this->attachToHead(resource); |
| 194 } | 237 } |
| 195 | 238 |
| 196 return entry->fResource; | 239 return resource; |
| 197 } | 240 } |
| 198 | 241 |
| 199 void GrResourceCache::addResource(const GrResourceKey& key, | 242 void GrResourceCache::addResource(const GrResourceKey& key, |
| 200 GrResource* resource, | 243 GrResource* resource, |
| 201 uint32_t ownershipFlags) { | 244 uint32_t ownershipFlags) { |
| 202 SkASSERT(NULL == resource->getCacheEntry()); | 245 SkASSERT(NULL == resource->getCacheEntry()); |
| 203 // we don't expect to create new resources during a purge. In theory | 246 // we don't expect to create new resources during a purge. In theory |
| 204 // this could cause purgeAsNeeded() into an infinite loop (e.g. | 247 // this could cause purgeAsNeeded() into an infinite loop (e.g. |
| 205 // each resource destroyed creates and locks 2 resources and | 248 // each resource destroyed creates and locks 2 resources and |
| 206 // unlocks 1 thereby causing a new purge). | 249 // unlocks 1 thereby causing a new purge). |
| 207 SkASSERT(!fPurging); | 250 SkASSERT(!fPurging); |
| 208 GrAutoResourceCacheValidate atcv(this); | 251 GrAutoResourceCacheValidate atcv(this); |
| 209 | 252 |
| 210 GrResourceEntry* entry = SkNEW_ARGS(GrResourceEntry, (key, resource)); | 253 GrResourceEntry* entry = fCache.find(key); |
| 211 resource->setCacheEntry(entry); | 254 if (NULL == entry) { |
| 255 entry = SkNEW_ARGS(GrResourceEntry, (key, resource)); | |
| 256 fCache.add(entry); | |
| 257 } else { | |
| 258 entry->addResource(resource); | |
| 259 } | |
| 212 | 260 |
| 213 this->attachToHead(entry); | 261 this->attachToHead(resource); |
| 214 fCache.insert(key, entry); | |
| 215 | 262 |
| 216 if (ownershipFlags & kHide_OwnershipFlag) { | 263 if (ownershipFlags & kHide_OwnershipFlag) { |
| 217 this->makeExclusive(entry); | 264 this->makeExclusive(resource); |
| 218 } | 265 } |
| 219 | |
| 220 } | 266 } |
| 221 | 267 |
| 222 void GrResourceCache::makeExclusive(GrResourceEntry* entry) { | 268 void GrResourceCache::makeExclusive(GrResource* resource) { |
| 223 GrAutoResourceCacheValidate atcv(this); | 269 GrAutoResourceCacheValidate atcv(this); |
| 224 | |
| 225 // When scratch textures are detached (to hide them from future finds) they | 270 // When scratch textures are detached (to hide them from future finds) they |
| 226 // still count against the resource budget | 271 // still count against the resource budget |
| 227 this->internalDetach(entry, kIgnore_BudgetBehavior); | 272 this->internalDetach(resource, kIgnore_BudgetBehavior); |
| 228 fCache.remove(entry->key(), entry); | 273 resource->getCacheEntry()->makeExclusive(resource); |
| 229 | 274 |
| 230 #ifdef SK_DEBUG | 275 #ifdef SK_DEBUG |
| 231 fExclusiveList.addToHead(entry); | 276 fExclusiveList.addToHead(resource); |
| 232 #endif | 277 #endif |
| 233 } | 278 } |
| 234 | 279 |
| 235 void GrResourceCache::removeInvalidResource(GrResourceEntry* entry) { | 280 void GrResourceCache::removeInvalidResource(GrResource* resource) { |
| 236 // If the resource went invalid while it was detached then purge it | 281 // If the resource went invalid while it was detached then purge it |
| 237 // This can happen when a 3D context was lost, | 282 // This can happen when a 3D context was lost, |
| 238 // the client called GrContext::contextDestroyed() to notify Gr, | 283 // the client called GrContext::contextDestroyed() to notify Gr, |
| 239 // and then later an SkGpuDevice's destructor releases its backing | 284 // and then later an SkGpuDevice's destructor releases its backing |
| 240 // texture (which was invalidated at contextDestroyed time). | 285 // texture (which was invalidated at contextDestroyed time). |
| 241 fClientDetachedCount -= 1; | 286 fClientDetachedCount -= 1; |
| 242 fEntryCount -= 1; | 287 fEntryCount -= 1; |
| 243 size_t size = entry->resource()->sizeInBytes(); | 288 size_t size = resource->sizeInBytes(); |
| 244 fClientDetachedBytes -= size; | 289 fClientDetachedBytes -= size; |
| 245 fEntryBytes -= size; | 290 fEntryBytes -= size; |
| 246 } | 291 } |
| 247 | 292 |
| 248 void GrResourceCache::makeNonExclusive(GrResourceEntry* entry) { | 293 void GrResourceCache::makeNonExclusive(GrResource* resource) { |
| 249 GrAutoResourceCacheValidate atcv(this); | 294 GrAutoResourceCacheValidate atcv(this); |
| 250 | 295 |
| 251 #ifdef SK_DEBUG | 296 #ifdef SK_DEBUG |
| 252 fExclusiveList.remove(entry); | 297 fExclusiveList.remove(resource); |
| 253 #endif | 298 #endif |
| 254 | 299 |
| 255 if (entry->resource()->isValid()) { | 300 if (resource->isValid()) { |
| 256 // Since scratch textures still count against the cache budget even | 301 // Since scratch textures still count against the cache budget even |
| 257 // when they have been removed from the cache, re-adding them doesn't | 302 // when they have been removed from the cache, re-adding them doesn't |
| 258 // alter the budget information. | 303 // alter the budget information. |
| 259 attachToHead(entry, kIgnore_BudgetBehavior); | 304 attachToHead(resource, kIgnore_BudgetBehavior); |
| 260 fCache.insert(entry->key(), entry); | 305 resource->getCacheEntry()->makeNonExclusive(resource); |
| 261 } else { | 306 } else { |
| 262 this->removeInvalidResource(entry); | 307 this->removeInvalidResource(resource); |
| 308 GrResourceEntry* entry = resource->getCacheEntry(); | |
| 309 if (entry->removeExclusiveResource(resource)) { | |
| 310 fCache.remove(entry->key()); | |
| 311 delete entry; | |
| 312 } | |
| 263 } | 313 } |
| 264 } | 314 } |
| 265 | 315 |
| 266 /** | 316 /** |
| 267 * Destroying a resource may potentially trigger the unlock of additional | 317 * Destroying a resource may potentially trigger the unlock of additional |
| 268 * resources which in turn will trigger a nested purge. We block the nested | 318 * resources which in turn will trigger a nested purge. We block the nested |
| 269 * purge using the fPurging variable. However, the initial purge will keep | 319 * purge using the fPurging variable. However, the initial purge will keep |
| 270 * looping until either all resources in the cache are unlocked or we've met | 320 * looping until either all resources in the cache are unlocked or we've met |
| 271 * the budget. There is an assertion in createAndLock to check against a | 321 * the budget. There is an assertion in createAndLock to check against a |
| 272 * resource's destructor inserting new resources into the cache. If these | 322 * resource's destructor inserting new resources into the cache. If these |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 305 fInvalidationInbox.poll(&invalidated); | 355 fInvalidationInbox.poll(&invalidated); |
| 306 | 356 |
| 307 for (int i = 0; i < invalidated.count(); i++) { | 357 for (int i = 0; i < invalidated.count(); i++) { |
| 308 // We're somewhat missing an opportunity here. We could use the | 358 // We're somewhat missing an opportunity here. We could use the |
| 309 // default find functor that gives us back resources whether we own | 359 // default find functor that gives us back resources whether we own |
| 310 // them exclusively or not, and when they're not exclusively owned mark | 360 // them exclusively or not, and when they're not exclusively owned mark |
| 311 // them for purging later when they do become exclusively owned. | 361 // them for purging later when they do become exclusively owned. |
| 312 // | 362 // |
| 313 // This is complicated and confusing. May try this in the future. For | 363 // This is complicated and confusing. May try this in the future. For |
| 314 // now, these resources are just LRU'd as if we never got the message. | 364 // now, these resources are just LRU'd as if we never got the message. |
| 315 GrResourceEntry* entry = fCache.find(invalidated[i].key, GrTFindUnreffed Functor()); | 365 GrResourceEntry* entry = fCache.find(invalidated[i].key); |
| 316 if (entry) { | 366 if (entry) { |
| 317 this->deleteResource(entry); | 367 GrTFindUnreffedFunctor functor; |
| 368 GrResource* resource; | |
| 369 do { | |
| 370 resource = entry->resources().find(functor); | |
| 371 } while (resource != NULL && this->removeResource(resource)); | |
|
mtklein
2013/12/03 19:02:02
Hmm, won't this be O(N^2)? This is a case where u
| |
| 318 } | 372 } |
| 319 } | 373 } |
| 320 } | 374 } |
| 321 | 375 |
| 322 void GrResourceCache::deleteResource(GrResourceEntry* entry) { | 376 bool GrResourceCache::removeResource(GrResource* resource) { |
| 323 SkASSERT(1 == entry->fResource->getRefCnt()); | 377 GrResourceEntry* entry = resource->getCacheEntry(); |
| 378 this->internalDetach(resource); | |
| 379 if (entry->removeResource(resource)) { | |
| 380 fCache.remove(entry->key()); | |
| 381 delete entry; | |
| 382 return false; | |
| 383 } | |
| 324 | 384 |
| 325 // remove from our cache | 385 return true; |
| 326 fCache.remove(entry->key(), entry); | |
| 327 | |
| 328 // remove from our llist | |
| 329 this->internalDetach(entry); | |
| 330 delete entry; | |
| 331 } | 386 } |
| 332 | 387 |
| 333 void GrResourceCache::internalPurge(int extraCount, size_t extraBytes) { | 388 void GrResourceCache::internalPurge(int extraCount, size_t extraBytes) { |
| 334 SkASSERT(fPurging); | 389 SkASSERT(fPurging); |
| 335 | 390 |
| 336 bool withinBudget = false; | 391 bool withinBudget = false; |
| 337 bool changed = false; | 392 bool changed = false; |
| 338 | 393 |
| 339 // The purging process is repeated several times since one pass | 394 // The purging process is repeated several times since one pass |
| 340 // may free up other resources | 395 // may free up other resources |
| 341 do { | 396 do { |
| 342 EntryList::Iter iter; | 397 CacheLRUInternalLListType::Iter iter; |
| 343 | 398 |
| 344 changed = false; | 399 changed = false; |
| 345 | 400 |
| 346 // Note: the following code relies on the fact that the | 401 // Note: the following code relies on the fact that the |
| 347 // doubly linked list doesn't invalidate its data/pointers | 402 // doubly linked list doesn't invalidate its data/pointers |
| 348 // outside of the specific area where a deletion occurs (e.g., | 403 // outside of the specific area where a deletion occurs (e.g., |
| 349 // in internalDetach) | 404 // in internalDetach) |
| 350 GrResourceEntry* entry = iter.init(fList, EntryList::Iter::kTail_IterSta rt); | 405 GrResource* resource = iter.init(fList, CacheLRUInternalLListType::Iter: :kTail_IterStart); |
| 351 | 406 |
| 352 while (NULL != entry) { | 407 while (NULL != resource) { |
| 353 GrAutoResourceCacheValidate atcv(this); | 408 GrAutoResourceCacheValidate atcv(this); |
| 354 | 409 |
| 355 if ((fEntryCount+extraCount) <= fMaxCount && | 410 if ((fEntryCount+extraCount) <= fMaxCount && |
| 356 (fEntryBytes+extraBytes) <= fMaxBytes) { | 411 (fEntryBytes+extraBytes) <= fMaxBytes) { |
| 357 withinBudget = true; | 412 withinBudget = true; |
| 358 break; | 413 break; |
| 359 } | 414 } |
| 360 | 415 |
| 361 GrResourceEntry* prev = iter.prev(); | 416 GrResource* prev = iter.prev(); |
| 362 if (entry->fResource->unique()) { | 417 if (resource->unique()) { |
| 363 changed = true; | 418 changed = true; |
| 364 this->deleteResource(entry); | 419 this->deleteResource(resource); |
| 365 } | 420 } |
| 366 entry = prev; | 421 resource = prev; |
| 367 } | 422 } |
| 368 } while (!withinBudget && changed); | 423 } while (!withinBudget && changed); |
| 369 } | 424 } |
| 370 | 425 |
| 371 void GrResourceCache::purgeAllUnlocked() { | 426 void GrResourceCache::purgeAllUnlocked() { |
| 372 GrAutoResourceCacheValidate atcv(this); | 427 GrAutoResourceCacheValidate atcv(this); |
| 373 | 428 |
| 374 // we can have one GrResource holding a lock on another | 429 // we can have one GrResource holding a lock on another |
| 375 // so we don't want to just do a simple loop kicking each | 430 // so we don't want to just do a simple loop kicking each |
| 376 // entry out. Instead change the budget and purge. | 431 // entry out. Instead change the budget and purge. |
| 377 | 432 |
| 378 size_t savedMaxBytes = fMaxBytes; | 433 size_t savedMaxBytes = fMaxBytes; |
| 379 int savedMaxCount = fMaxCount; | 434 int savedMaxCount = fMaxCount; |
| 380 fMaxBytes = (size_t) -1; | 435 fMaxBytes = (size_t) -1; |
| 381 fMaxCount = 0; | 436 fMaxCount = 0; |
| 382 this->purgeAsNeeded(); | 437 this->purgeAsNeeded(); |
| 383 | 438 |
| 384 #ifdef SK_DEBUG | 439 #ifdef SK_DEBUG |
| 385 SkASSERT(fExclusiveList.countEntries() == fClientDetachedCount); | 440 SkASSERT(fExclusiveList.countEntries() == fClientDetachedCount); |
| 386 SkASSERT(countBytes(fExclusiveList) == fClientDetachedBytes); | 441 SkASSERT(countBytes(fExclusiveList) == fClientDetachedBytes); |
| 387 if (!fCache.count()) { | 442 // Items may have been detached from the cache (such as the backing |
| 388 // Items may have been detached from the cache (such as the backing | 443 // texture for an SkGpuDevice). The above purge would not have removed |
| 389 // texture for an SkGpuDevice). The above purge would not have removed | 444 // them. |
| 390 // them. | 445 SkASSERT(fEntryCount == fClientDetachedCount); |
| 391 SkASSERT(fEntryCount == fClientDetachedCount); | 446 SkASSERT(fEntryBytes == fClientDetachedBytes); |
| 392 SkASSERT(fEntryBytes == fClientDetachedBytes); | 447 SkASSERT(fList.isEmpty()); |
| 393 SkASSERT(fList.isEmpty()); | |
| 394 } | |
| 395 #endif | 448 #endif |
| 396 | 449 |
| 397 fMaxBytes = savedMaxBytes; | 450 fMaxBytes = savedMaxBytes; |
| 398 fMaxCount = savedMaxCount; | 451 fMaxCount = savedMaxCount; |
| 399 } | 452 } |
| 400 | 453 |
| 401 /////////////////////////////////////////////////////////////////////////////// | 454 /////////////////////////////////////////////////////////////////////////////// |
| 402 | 455 |
| 403 #ifdef SK_DEBUG | 456 #ifdef SK_DEBUG |
| 404 size_t GrResourceCache::countBytes(const EntryList& list) { | 457 size_t GrResourceCache::countBytes(const CacheLRUInternalLListType& list) { |
| 405 size_t bytes = 0; | 458 size_t bytes = 0; |
| 406 | 459 |
| 407 EntryList::Iter iter; | 460 CacheLRUInternalLListType::Iter iter; |
| 461 const GrResource* resource = iter.init(const_cast<CacheLRUInternalLListType& >(list), | |
| 462 CacheLRUInternalLListType::Iter::kTai l_IterStart); | |
| 408 | 463 |
| 409 const GrResourceEntry* entry = iter.init(const_cast<EntryList&>(list), | 464 for ( ; NULL != resource; resource = iter.prev()) { |
| 410 EntryList::Iter::kTail_IterStart); | 465 bytes += resource->sizeInBytes(); |
| 411 | |
| 412 for ( ; NULL != entry; entry = iter.prev()) { | |
| 413 bytes += entry->resource()->sizeInBytes(); | |
| 414 } | 466 } |
| 415 return bytes; | 467 return bytes; |
| 416 } | 468 } |
| 417 | 469 |
| 418 static bool both_zero_or_nonzero(int count, size_t bytes) { | 470 static bool both_zero_or_nonzero(int count, size_t bytes) { |
| 419 return (count == 0 && bytes == 0) || (count > 0 && bytes > 0); | 471 return (count == 0 && bytes == 0) || (count > 0 && bytes > 0); |
| 420 } | 472 } |
| 421 | 473 |
| 422 void GrResourceCache::validate() const { | 474 void GrResourceCache::validate() const { |
| 423 fList.validate(); | 475 fList.validate(); |
| 424 fExclusiveList.validate(); | 476 fExclusiveList.validate(); |
| 425 SkASSERT(both_zero_or_nonzero(fEntryCount, fEntryBytes)); | 477 SkASSERT(both_zero_or_nonzero(fEntryCount, fEntryBytes)); |
| 426 SkASSERT(both_zero_or_nonzero(fClientDetachedCount, fClientDetachedBytes)); | 478 SkASSERT(both_zero_or_nonzero(fClientDetachedCount, fClientDetachedBytes)); |
| 427 SkASSERT(fClientDetachedBytes <= fEntryBytes); | 479 SkASSERT(fClientDetachedBytes <= fEntryBytes); |
| 428 SkASSERT(fClientDetachedCount <= fEntryCount); | 480 SkASSERT(fClientDetachedCount <= fEntryCount); |
| 429 SkASSERT((fEntryCount - fClientDetachedCount) == fCache.count()); | |
| 430 | 481 |
| 431 fCache.validate(); | 482 CacheLRUInternalLListType::Iter iter; |
| 432 | |
| 433 | |
| 434 EntryList::Iter iter; | |
| 435 | 483 |
| 436 // check that the exclusively held entries are okay | 484 // check that the exclusively held entries are okay |
| 437 const GrResourceEntry* entry = iter.init(const_cast<EntryList&>(fExclusiveLi st), | 485 GrResource* resource = iter.init(const_cast<CacheLRUInternalLListType&>(fExc lusiveList), |
| 438 EntryList::Iter::kHead_IterStart); | 486 CacheLRUInternalLListType::Iter::kHead_Iter Start); |
| 439 | 487 |
| 440 for ( ; NULL != entry; entry = iter.next()) { | 488 for ( ; NULL != resource; resource = iter.next()) { |
| 441 entry->validate(); | 489 resource->validate(); |
| 442 } | 490 } |
| 443 | 491 |
| 444 // check that the shareable entries are okay | 492 // check that the shareable entries are okay |
| 445 entry = iter.init(const_cast<EntryList&>(fList), EntryList::Iter::kHead_Iter Start); | 493 resource = iter.init(const_cast<CacheLRUInternalLListType&>(fList), |
| 494 CacheLRUInternalLListType::Iter::kHead_IterStart); | |
| 446 | 495 |
| 447 int count = 0; | 496 int count = 0; |
| 448 for ( ; NULL != entry; entry = iter.next()) { | 497 for ( ; NULL != resource; resource = iter.next()) { |
| 449 entry->validate(); | 498 resource->validate(); |
| 450 SkASSERT(fCache.find(entry->key())); | 499 SkASSERT(fCache.find(resource->getCacheEntry()->key())); |
| 451 count += 1; | 500 count += 1; |
| 452 } | 501 } |
| 453 SkASSERT(count == fEntryCount - fClientDetachedCount); | 502 SkASSERT(count == fEntryCount - fClientDetachedCount); |
| 454 | 503 |
| 455 size_t bytes = countBytes(fList); | 504 size_t bytes = countBytes(fList); |
| 456 SkASSERT(bytes == fEntryBytes - fClientDetachedBytes); | 505 SkASSERT(bytes == fEntryBytes - fClientDetachedBytes); |
| 457 | 506 |
| 458 bytes = countBytes(fExclusiveList); | 507 bytes = countBytes(fExclusiveList); |
| 459 SkASSERT(bytes == fClientDetachedBytes); | 508 SkASSERT(bytes == fClientDetachedBytes); |
| 460 | 509 |
| 461 SkASSERT(fList.countEntries() == fEntryCount - fClientDetachedCount); | 510 SkASSERT(fList.countEntries() == fEntryCount - fClientDetachedCount); |
| 462 | 511 |
| 463 SkASSERT(fExclusiveList.countEntries() == fClientDetachedCount); | 512 SkASSERT(fExclusiveList.countEntries() == fClientDetachedCount); |
| 464 } | 513 } |
| 465 #endif // SK_DEBUG | 514 #endif // SK_DEBUG |
| 466 | 515 |
| 467 #if GR_CACHE_STATS | 516 #if GR_CACHE_STATS |
| 468 | 517 |
| 469 void GrResourceCache::printStats() { | 518 void GrResourceCache::printStats() { |
| 470 int locked = 0; | 519 int locked = 0; |
| 471 | 520 |
| 472 EntryList::Iter iter; | 521 CacheLRUInternalLListType::Iter iter; |
| 473 | 522 |
| 474 GrResourceEntry* entry = iter.init(fList, EntryList::Iter::kTail_IterStart); | 523 GrResource* resource = iter.init(fList, CacheLRUInternalLListType::Iter::kTa il_IterStart); |
| 475 | 524 |
| 476 for ( ; NULL != entry; entry = iter.prev()) { | 525 for ( ; NULL != resource; resource = iter.prev()) { |
| 477 if (entry->fResource->getRefCnt() > 1) { | 526 if (resource->getRefCnt() > 1) { |
| 478 ++locked; | 527 ++locked; |
| 479 } | 528 } |
| 480 } | 529 } |
| 481 | 530 |
| 482 SkDebugf("Budget: %d items %d bytes\n", fMaxCount, fMaxBytes); | 531 SkDebugf("Budget: %d items %d bytes\n", fMaxCount, fMaxBytes); |
| 483 SkDebugf("\t\tEntry Count: current %d (%d locked) high %d\n", | 532 SkDebugf("\t\tEntry Count: current %d (%d locked) high %d\n", |
| 484 fEntryCount, locked, fHighWaterEntryCount); | 533 fEntryCount, locked, fHighWaterEntryCount); |
| 485 SkDebugf("\t\tEntry Bytes: current %d high %d\n", | 534 SkDebugf("\t\tEntry Bytes: current %d high %d\n", |
| 486 fEntryBytes, fHighWaterEntryBytes); | 535 fEntryBytes, fHighWaterEntryBytes); |
| 487 SkDebugf("\t\tDetached Entry Count: current %d high %d\n", | 536 SkDebugf("\t\tDetached Entry Count: current %d high %d\n", |
| 488 fClientDetachedCount, fHighWaterClientDetachedCount); | 537 fClientDetachedCount, fHighWaterClientDetachedCount); |
| 489 SkDebugf("\t\tDetached Bytes: current %d high %d\n", | 538 SkDebugf("\t\tDetached Bytes: current %d high %d\n", |
| 490 fClientDetachedBytes, fHighWaterClientDetachedBytes); | 539 fClientDetachedBytes, fHighWaterClientDetachedBytes); |
| 491 } | 540 } |
| 492 | 541 |
| 493 #endif | 542 #endif |
| 494 | 543 |
| 495 /////////////////////////////////////////////////////////////////////////////// | 544 /////////////////////////////////////////////////////////////////////////////// |
| OLD | NEW |