Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(79)

Unified Diff: src/gpu/GrResourceCache.cpp

Issue 91453002: Speed up GrResourceCache add and lookup by using TDynamicHash (Closed) Base URL: https://skia.googlecode.com/svn/trunk
Patch Set: Created 7 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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;
}
}

Powered by Google App Engine
This is Rietveld 408576698