Chromium Code Reviews| Index: src/gpu/GrResourceCache.cpp |
| diff --git a/src/gpu/GrResourceCache.cpp b/src/gpu/GrResourceCache.cpp |
| index 5cf3f82c7d780d1acc5ee53ca20d9f6140b1a7b4..49cd42d0b24b9475bd12ac4d441a860ae9caae87 100644 |
| --- a/src/gpu/GrResourceCache.cpp |
| +++ b/src/gpu/GrResourceCache.cpp |
| @@ -25,25 +25,58 @@ GrResourceKey::ResourceType GrResourceKey::GenerateResourceType() { |
| /////////////////////////////////////////////////////////////////////////////// |
| -GrResourceEntry::GrResourceEntry(const GrResourceKey& key, GrResource* resource) |
| - : fKey(key), fResource(resource) { |
| - // we assume ownership of the resource, and will unref it when we die |
| - SkASSERT(resource); |
| - resource->ref(); |
| +GrResourceEntry::GrResourceEntry(const GrResourceKey& key, GrResource* firstResource) |
| + : fKey(key) |
| + , fExclusiveCount(0) { |
| + addResource(firstResource); |
|
mtklein
2013/12/03 19:02:02
this->
|
| } |
| GrResourceEntry::~GrResourceEntry() { |
|
mtklein
2013/12/03 19:02:02
The responsibility for resource ownership here see
|
| - fResource->setCacheEntry(NULL); |
| - fResource->unref(); |
| + SkASSERT(isEmpty()); |
| + } |
| + |
| +void GrResourceEntry::addResource(GrResource* resource) { |
| + // We assume ownership of the resource, and will unref it when resource is |
| + // removed from the entry. |
| + resource->ref(); |
| + resource->setCacheEntry(this); |
| + fResources.addToHead(resource); |
| } |
| -#ifdef SK_DEBUG |
| -void GrResourceEntry::validate() const { |
| - SkASSERT(fResource); |
| - SkASSERT(fResource->getCacheEntry() == this); |
| - fResource->validate(); |
| +bool GrResourceEntry::removeResource(GrResource* resource) { |
| + fResources.remove(resource); |
| + |
| + // The unref() might run destructor which would remove other resources from this entry and delete the |
| + // entry. Temporarily increase exclusive count, so that entry stays alive. |
| + ++fExclusiveCount; |
| + |
| + resource->setCacheEntry(NULL); |
| + resource->unref(); |
| + |
| + --fExclusiveCount; |
| + |
| + return isEmpty(); |
| +} |
| + |
| +bool GrResourceEntry::removeExclusiveResource(GrResource* resource) { |
| + resource->setCacheEntry(NULL); |
| + resource->unref(); |
| + |
| + // Decrease the count after unref(). See removeResource. |
| + --fExclusiveCount; |
| + |
| + return isEmpty(); |
| +} |
| + |
| +void GrResourceEntry::makeExclusive(GrResource* resource) { |
| + fResources.remove(resource); |
| + fExclusiveCount++; |
| +} |
| + |
| +void GrResourceEntry::makeNonExclusive(GrResource* resource) { |
| + fResources.addToHead(resource); |
| + fExclusiveCount--; |
| } |
| -#endif |
| /////////////////////////////////////////////////////////////////////////////// |
| @@ -71,20 +104,12 @@ GrResourceCache::GrResourceCache(int maxCount, size_t maxBytes) : |
| GrResourceCache::~GrResourceCache() { |
| GrAutoResourceCacheValidate atcv(this); |
| - EntryList::Iter iter; |
| - |
| - // Unlike the removeAll, here we really remove everything, including locked resources. |
| - while (GrResourceEntry* entry = fList.head()) { |
| + while (GrResource* resource = fList.head()) { |
| GrAutoResourceCacheValidate atcv(this); |
| - |
| - // remove from our cache |
| - fCache.remove(entry->fKey, entry); |
| - |
| - // remove from our llist |
| - this->internalDetach(entry); |
| - |
| - delete entry; |
| + this->removeResource(resource); |
| } |
| + |
| + SkASSERT(fCache.count() == 0); |
| } |
| void GrResourceCache::getLimits(int* maxResources, size_t* maxResourceBytes) const{ |
| @@ -107,14 +132,14 @@ void GrResourceCache::setLimits(int maxResources, size_t maxResourceBytes) { |
| } |
| } |
| -void GrResourceCache::internalDetach(GrResourceEntry* entry, |
| +void GrResourceCache::internalDetach(GrResource* resource, |
| BudgetBehaviors behavior) { |
| - fList.remove(entry); |
| + fList.remove(resource); |
| // update our stats |
| if (kIgnore_BudgetBehavior == behavior) { |
| fClientDetachedCount += 1; |
| - fClientDetachedBytes += entry->resource()->sizeInBytes(); |
| + fClientDetachedBytes += resource->sizeInBytes(); |
| #if GR_CACHE_STATS |
| if (fHighWaterClientDetachedCount < fClientDetachedCount) { |
| @@ -129,23 +154,23 @@ void GrResourceCache::internalDetach(GrResourceEntry* entry, |
| SkASSERT(kAccountFor_BudgetBehavior == behavior); |
| fEntryCount -= 1; |
| - fEntryBytes -= entry->resource()->sizeInBytes(); |
| + fEntryBytes -= resource->sizeInBytes(); |
| } |
| } |
| -void GrResourceCache::attachToHead(GrResourceEntry* entry, |
| +void GrResourceCache::attachToHead(GrResource* resource, |
| BudgetBehaviors behavior) { |
| - fList.addToHead(entry); |
| + fList.addToHead(resource); |
| // update our stats |
| if (kIgnore_BudgetBehavior == behavior) { |
| fClientDetachedCount -= 1; |
| - fClientDetachedBytes -= entry->resource()->sizeInBytes(); |
| + fClientDetachedBytes -= resource->sizeInBytes(); |
| } else { |
| SkASSERT(kAccountFor_BudgetBehavior == behavior); |
| fEntryCount += 1; |
| - fEntryBytes += entry->resource()->sizeInBytes(); |
| + fEntryBytes += resource->sizeInBytes(); |
| #if GR_CACHE_STATS |
| if (fHighWaterEntryCount < fEntryCount) { |
| @@ -163,37 +188,55 @@ void GrResourceCache::attachToHead(GrResourceEntry* entry, |
| // is relying on the texture. |
| class GrTFindUnreffedFunctor { |
| public: |
| - bool operator()(const GrResourceEntry* entry) const { |
| - return entry->resource()->unique(); |
| + bool operator()(const GrResource* resource) const { |
| + return resource->unique(); |
| } |
| }; |
| GrResource* GrResourceCache::find(const GrResourceKey& key, uint32_t ownershipFlags) { |
| GrAutoResourceCacheValidate atcv(this); |
| - GrResourceEntry* entry = NULL; |
| + GrResourceEntry* entry = fCache.find(key); |
| + if (NULL == entry) { |
| + return NULL; |
| + } |
| + GrResource* resource; |
| if (ownershipFlags & kNoOtherOwners_OwnershipFlag) { |
| GrTFindUnreffedFunctor functor; |
| - |
| - entry = fCache.find<GrTFindUnreffedFunctor>(key, functor); |
| + resource = entry->resources().find(functor); |
| } else { |
| - entry = fCache.find(key); |
| + // Find a resource not referenced outside cache, or the least referenced one. |
| + typedef GrResourceEntry::CacheEntryResourcesInternalLListType::Iter EntryResourcesIter; |
| + |
| + EntryResourcesIter iter; |
| + resource = iter.init(entry->resources(), EntryResourcesIter::kTail_IterStart); |
|
mtklein
2013/12/03 19:02:02
If we don't need to iterate both ways down the lis
|
| + if (NULL != resource && resource->getRefCnt() > 1) { |
|
mtklein
2013/12/03 19:02:02
Oooh, calls to getRefCnt() make me super scared.
|
| + int refCount = resource->getRefCnt(); |
| + for (GrResource* nextResource = iter.next(); |
| + NULL != nextResource && refCount > 1; |
| + nextResource = iter.next()) { |
| + if (nextResource->getRefCnt() > refCount) { |
| + resource = nextResource; |
| + refCount = nextResource->getRefCnt(); |
| + } |
| + } |
| + } |
| } |
| - if (NULL == entry) { |
| + if (NULL == resource) { |
| return NULL; |
| } |
| if (ownershipFlags & kHide_OwnershipFlag) { |
| - this->makeExclusive(entry); |
| + this->makeExclusive(resource); |
| } else { |
| // Make this resource MRU |
| - this->internalDetach(entry); |
| - this->attachToHead(entry); |
| + this->internalDetach(resource); |
| + this->attachToHead(resource); |
| } |
| - return entry->fResource; |
| + return resource; |
| } |
| void GrResourceCache::addResource(const GrResourceKey& key, |
| @@ -207,32 +250,34 @@ void GrResourceCache::addResource(const GrResourceKey& key, |
| SkASSERT(!fPurging); |
| GrAutoResourceCacheValidate atcv(this); |
| - GrResourceEntry* entry = SkNEW_ARGS(GrResourceEntry, (key, resource)); |
| - resource->setCacheEntry(entry); |
| + GrResourceEntry* entry = fCache.find(key); |
| + if (NULL == entry) { |
| + entry = SkNEW_ARGS(GrResourceEntry, (key, resource)); |
| + fCache.add(entry); |
| + } else { |
| + entry->addResource(resource); |
| + } |
| - this->attachToHead(entry); |
| - fCache.insert(key, entry); |
| + this->attachToHead(resource); |
| if (ownershipFlags & kHide_OwnershipFlag) { |
| - this->makeExclusive(entry); |
| + this->makeExclusive(resource); |
| } |
| - |
| } |
| -void GrResourceCache::makeExclusive(GrResourceEntry* entry) { |
| +void GrResourceCache::makeExclusive(GrResource* resource) { |
| GrAutoResourceCacheValidate atcv(this); |
| - |
| // When scratch textures are detached (to hide them from future finds) they |
| // still count against the resource budget |
| - this->internalDetach(entry, kIgnore_BudgetBehavior); |
| - fCache.remove(entry->key(), entry); |
| + this->internalDetach(resource, kIgnore_BudgetBehavior); |
| + resource->getCacheEntry()->makeExclusive(resource); |
| #ifdef SK_DEBUG |
| - fExclusiveList.addToHead(entry); |
| + fExclusiveList.addToHead(resource); |
| #endif |
| } |
| -void GrResourceCache::removeInvalidResource(GrResourceEntry* entry) { |
| +void GrResourceCache::removeInvalidResource(GrResource* resource) { |
| // If the resource went invalid while it was detached then purge it |
| // This can happen when a 3D context was lost, |
| // the client called GrContext::contextDestroyed() to notify Gr, |
| @@ -240,26 +285,31 @@ void GrResourceCache::removeInvalidResource(GrResourceEntry* entry) { |
| // texture (which was invalidated at contextDestroyed time). |
| fClientDetachedCount -= 1; |
| fEntryCount -= 1; |
| - size_t size = entry->resource()->sizeInBytes(); |
| + size_t size = resource->sizeInBytes(); |
| fClientDetachedBytes -= size; |
| fEntryBytes -= size; |
| } |
| -void GrResourceCache::makeNonExclusive(GrResourceEntry* entry) { |
| +void GrResourceCache::makeNonExclusive(GrResource* resource) { |
| GrAutoResourceCacheValidate atcv(this); |
| #ifdef SK_DEBUG |
| - fExclusiveList.remove(entry); |
| + fExclusiveList.remove(resource); |
| #endif |
| - if (entry->resource()->isValid()) { |
| + if (resource->isValid()) { |
| // Since scratch textures still count against the cache budget even |
| // when they have been removed from the cache, re-adding them doesn't |
| // alter the budget information. |
| - attachToHead(entry, kIgnore_BudgetBehavior); |
| - fCache.insert(entry->key(), entry); |
| + attachToHead(resource, kIgnore_BudgetBehavior); |
| + resource->getCacheEntry()->makeNonExclusive(resource); |
| } else { |
| - this->removeInvalidResource(entry); |
| + this->removeInvalidResource(resource); |
| + GrResourceEntry* entry = resource->getCacheEntry(); |
| + if (entry->removeExclusiveResource(resource)) { |
| + fCache.remove(entry->key()); |
| + delete entry; |
| + } |
| } |
| } |
| @@ -312,22 +362,27 @@ void GrResourceCache::purgeInvalidated() { |
| // |
| // This is complicated and confusing. May try this in the future. For |
| // now, these resources are just LRU'd as if we never got the message. |
| - GrResourceEntry* entry = fCache.find(invalidated[i].key, GrTFindUnreffedFunctor()); |
| + GrResourceEntry* entry = fCache.find(invalidated[i].key); |
| if (entry) { |
| - this->deleteResource(entry); |
| + GrTFindUnreffedFunctor functor; |
| + GrResource* resource; |
| + do { |
| + resource = entry->resources().find(functor); |
| + } 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
|
| } |
| } |
| } |
| -void GrResourceCache::deleteResource(GrResourceEntry* entry) { |
| - SkASSERT(1 == entry->fResource->getRefCnt()); |
| - |
| - // remove from our cache |
| - fCache.remove(entry->key(), entry); |
| +bool GrResourceCache::removeResource(GrResource* resource) { |
| + GrResourceEntry* entry = resource->getCacheEntry(); |
| + this->internalDetach(resource); |
| + if (entry->removeResource(resource)) { |
| + fCache.remove(entry->key()); |
| + delete entry; |
| + return false; |
| + } |
| - // remove from our llist |
| - this->internalDetach(entry); |
| - delete entry; |
| + return true; |
| } |
| void GrResourceCache::internalPurge(int extraCount, size_t extraBytes) { |
| @@ -339,7 +394,7 @@ void GrResourceCache::internalPurge(int extraCount, size_t extraBytes) { |
| // The purging process is repeated several times since one pass |
| // may free up other resources |
| do { |
| - EntryList::Iter iter; |
| + CacheLRUInternalLListType::Iter iter; |
| changed = false; |
| @@ -347,9 +402,9 @@ void GrResourceCache::internalPurge(int extraCount, size_t extraBytes) { |
| // doubly linked list doesn't invalidate its data/pointers |
| // outside of the specific area where a deletion occurs (e.g., |
| // in internalDetach) |
| - GrResourceEntry* entry = iter.init(fList, EntryList::Iter::kTail_IterStart); |
| + GrResource* resource = iter.init(fList, CacheLRUInternalLListType::Iter::kTail_IterStart); |
| - while (NULL != entry) { |
| + while (NULL != resource) { |
| GrAutoResourceCacheValidate atcv(this); |
| if ((fEntryCount+extraCount) <= fMaxCount && |
| @@ -358,12 +413,12 @@ void GrResourceCache::internalPurge(int extraCount, size_t extraBytes) { |
| break; |
| } |
| - GrResourceEntry* prev = iter.prev(); |
| - if (entry->fResource->unique()) { |
| + GrResource* prev = iter.prev(); |
| + if (resource->unique()) { |
| changed = true; |
| - this->deleteResource(entry); |
| + this->deleteResource(resource); |
| } |
| - entry = prev; |
| + resource = prev; |
| } |
| } while (!withinBudget && changed); |
| } |
| @@ -384,14 +439,12 @@ void GrResourceCache::purgeAllUnlocked() { |
| #ifdef SK_DEBUG |
| SkASSERT(fExclusiveList.countEntries() == fClientDetachedCount); |
| SkASSERT(countBytes(fExclusiveList) == fClientDetachedBytes); |
| - if (!fCache.count()) { |
| - // Items may have been detached from the cache (such as the backing |
| - // texture for an SkGpuDevice). The above purge would not have removed |
| - // them. |
| - SkASSERT(fEntryCount == fClientDetachedCount); |
| - SkASSERT(fEntryBytes == fClientDetachedBytes); |
| - SkASSERT(fList.isEmpty()); |
| - } |
| + // Items may have been detached from the cache (such as the backing |
| + // texture for an SkGpuDevice). The above purge would not have removed |
| + // them. |
| + SkASSERT(fEntryCount == fClientDetachedCount); |
| + SkASSERT(fEntryBytes == fClientDetachedBytes); |
| + SkASSERT(fList.isEmpty()); |
| #endif |
| fMaxBytes = savedMaxBytes; |
| @@ -401,16 +454,15 @@ void GrResourceCache::purgeAllUnlocked() { |
| /////////////////////////////////////////////////////////////////////////////// |
| #ifdef SK_DEBUG |
| -size_t GrResourceCache::countBytes(const EntryList& list) { |
| +size_t GrResourceCache::countBytes(const CacheLRUInternalLListType& list) { |
| size_t bytes = 0; |
| - EntryList::Iter iter; |
| - |
| - const GrResourceEntry* entry = iter.init(const_cast<EntryList&>(list), |
| - EntryList::Iter::kTail_IterStart); |
| + CacheLRUInternalLListType::Iter iter; |
| + const GrResource* resource = iter.init(const_cast<CacheLRUInternalLListType&>(list), |
| + CacheLRUInternalLListType::Iter::kTail_IterStart); |
| - for ( ; NULL != entry; entry = iter.prev()) { |
| - bytes += entry->resource()->sizeInBytes(); |
| + for ( ; NULL != resource; resource = iter.prev()) { |
| + bytes += resource->sizeInBytes(); |
| } |
| return bytes; |
| } |
| @@ -426,28 +478,25 @@ void GrResourceCache::validate() const { |
| SkASSERT(both_zero_or_nonzero(fClientDetachedCount, fClientDetachedBytes)); |
| SkASSERT(fClientDetachedBytes <= fEntryBytes); |
| SkASSERT(fClientDetachedCount <= fEntryCount); |
| - SkASSERT((fEntryCount - fClientDetachedCount) == fCache.count()); |
| - |
| - fCache.validate(); |
| - |
| - EntryList::Iter iter; |
| + CacheLRUInternalLListType::Iter iter; |
| // check that the exclusively held entries are okay |
| - const GrResourceEntry* entry = iter.init(const_cast<EntryList&>(fExclusiveList), |
| - EntryList::Iter::kHead_IterStart); |
| + GrResource* resource = iter.init(const_cast<CacheLRUInternalLListType&>(fExclusiveList), |
| + CacheLRUInternalLListType::Iter::kHead_IterStart); |
| - for ( ; NULL != entry; entry = iter.next()) { |
| - entry->validate(); |
| + for ( ; NULL != resource; resource = iter.next()) { |
| + resource->validate(); |
| } |
| // check that the shareable entries are okay |
| - entry = iter.init(const_cast<EntryList&>(fList), EntryList::Iter::kHead_IterStart); |
| + resource = iter.init(const_cast<CacheLRUInternalLListType&>(fList), |
| + CacheLRUInternalLListType::Iter::kHead_IterStart); |
| int count = 0; |
| - for ( ; NULL != entry; entry = iter.next()) { |
| - entry->validate(); |
| - SkASSERT(fCache.find(entry->key())); |
| + for ( ; NULL != resource; resource = iter.next()) { |
| + resource->validate(); |
| + SkASSERT(fCache.find(resource->getCacheEntry()->key())); |
| count += 1; |
| } |
| SkASSERT(count == fEntryCount - fClientDetachedCount); |
| @@ -469,12 +518,12 @@ void GrResourceCache::validate() const { |
| void GrResourceCache::printStats() { |
| int locked = 0; |
| - EntryList::Iter iter; |
| + CacheLRUInternalLListType::Iter iter; |
| - GrResourceEntry* entry = iter.init(fList, EntryList::Iter::kTail_IterStart); |
| + GrResource* resource = iter.init(fList, CacheLRUInternalLListType::Iter::kTail_IterStart); |
| - for ( ; NULL != entry; entry = iter.prev()) { |
| - if (entry->fResource->getRefCnt() > 1) { |
| + for ( ; NULL != resource; resource = iter.prev()) { |
| + if (resource->getRefCnt() > 1) { |
| ++locked; |
| } |
| } |