Use an array of nonpurgeable resources in GrResourceCache
Review URL: https://codereview.chromium.org/932863004
diff --git a/src/gpu/GrResourceCache.cpp b/src/gpu/GrResourceCache.cpp
index d87e1a5..04b7ec5 100644
--- a/src/gpu/GrResourceCache.cpp
+++ b/src/gpu/GrResourceCache.cpp
@@ -67,12 +67,12 @@
, fBudgetedHighWaterCount(0)
, fBudgetedHighWaterBytes(0)
#endif
- , fCount(0)
, fBytes(0)
, fBudgetedCount(0)
, fBudgetedBytes(0)
, fOverBudgetCB(NULL)
, fOverBudgetData(NULL) {
+ SkDEBUGCODE(fCount = 0;)
}
GrResourceCache::~GrResourceCache() {
@@ -87,15 +87,16 @@
void GrResourceCache::insertResource(GrGpuResource* resource) {
SkASSERT(resource);
- SkASSERT(!resource->wasDestroyed());
SkASSERT(!this->isInCache(resource));
- fResources.addToHead(resource);
+ SkASSERT(!resource->wasDestroyed());
+ SkASSERT(!resource->isPurgeable());
+ this->addToNonpurgeableArray(resource);
size_t size = resource->gpuMemorySize();
- ++fCount;
+ SkDEBUGCODE(++fCount;)
fBytes += size;
#if GR_CACHE_STATS
- fHighWaterCount = SkTMax(fCount, fHighWaterCount);
+ fHighWaterCount = SkTMax(this->getResourceCount(), fHighWaterCount);
fHighWaterBytes = SkTMax(fBytes, fHighWaterBytes);
#endif
if (resource->resourcePriv().isBudgeted()) {
@@ -122,17 +123,18 @@
if (resource->isPurgeable()) {
fPurgeableQueue.remove(resource);
+ } else {
+ this->removeFromNonpurgeableArray(resource);
}
size_t size = resource->gpuMemorySize();
- --fCount;
+ SkDEBUGCODE(--fCount;)
fBytes -= size;
if (resource->resourcePriv().isBudgeted()) {
--fBudgetedCount;
fBudgetedBytes -= size;
}
- fResources.remove(resource);
if (resource->resourcePriv().getScratchKey().isValid()) {
fScratchMap.remove(resource->resourcePriv().getScratchKey(), resource);
}
@@ -145,15 +147,22 @@
void GrResourceCache::abandonAll() {
AutoValidate av(this);
- while (GrGpuResource* head = fResources.head()) {
- SkASSERT(!head->wasDestroyed());
- head->cacheAccess().abandon();
- // abandon should have already removed this from the list.
- SkASSERT(head != fResources.head());
+ while (fNonpurgeableResources.count()) {
+ GrGpuResource* back = *(fNonpurgeableResources.end() - 1);
+ SkASSERT(!back->wasDestroyed());
+ back->cacheAccess().abandon();
}
+
+ while (fPurgeableQueue.count()) {
+ GrGpuResource* top = fPurgeableQueue.peek();
+ SkASSERT(!top->wasDestroyed());
+ top->cacheAccess().abandon();
+ }
+
SkASSERT(!fScratchMap.count());
SkASSERT(!fContentHash.count());
SkASSERT(!fCount);
+ SkASSERT(!this->getResourceCount());
SkASSERT(!fBytes);
SkASSERT(!fBudgetedCount);
SkASSERT(!fBudgetedBytes);
@@ -162,14 +171,22 @@
void GrResourceCache::releaseAll() {
AutoValidate av(this);
- while (GrGpuResource* head = fResources.head()) {
- SkASSERT(!head->wasDestroyed());
- head->cacheAccess().release();
- // release should have already removed this from the list.
- SkASSERT(head != fResources.head());
+ while(fNonpurgeableResources.count()) {
+ GrGpuResource* back = *(fNonpurgeableResources.end() - 1);
+ SkASSERT(!back->wasDestroyed());
+ back->cacheAccess().release();
}
+
+ while (fPurgeableQueue.count()) {
+ GrGpuResource* top = fPurgeableQueue.peek();
+ SkASSERT(!top->wasDestroyed());
+ top->cacheAccess().release();
+ }
+
SkASSERT(!fScratchMap.count());
+ SkASSERT(!fContentHash.count());
SkASSERT(!fCount);
+ SkASSERT(!this->getResourceCount());
SkASSERT(!fBytes);
SkASSERT(!fBudgetedCount);
SkASSERT(!fBudgetedBytes);
@@ -248,10 +265,11 @@
if (resource->isPurgeable()) {
// It's about to become unpurgeable.
fPurgeableQueue.remove(resource);
+ this->addToNonpurgeableArray(resource);
}
resource->ref();
resource->cacheAccess().setTimestamp(fTimestamp++);
- SkASSERT(!resource->isPurgeable());
+ this->validate();
}
void GrResourceCache::notifyPurgeable(GrGpuResource* resource) {
@@ -259,7 +277,7 @@
SkASSERT(this->isInCache(resource));
SkASSERT(resource->isPurgeable());
- SkASSERT(-1 == *resource->cacheAccess().accessCacheIndex());
+ this->removeFromNonpurgeableArray(resource);
fPurgeableQueue.insert(resource);
if (!resource->resourcePriv().isBudgeted()) {
@@ -267,29 +285,26 @@
if (!resource->cacheAccess().isWrapped() &&
resource->resourcePriv().getScratchKey().isValid()) {
// We won't purge an existing resource to make room for this one.
- bool underBudget = fBudgetedCount < fMaxCount &&
- fBudgetedBytes + resource->gpuMemorySize() <= fMaxBytes;
- if (underBudget) {
+ if (fBudgetedCount < fMaxCount &&
+ fBudgetedBytes + resource->gpuMemorySize() <= fMaxBytes) {
resource->resourcePriv().makeBudgeted();
return;
}
}
} else {
// Purge the resource immediately if we're over budget
- bool overBudget = fBudgetedCount > fMaxCount || fBudgetedBytes > fMaxBytes;
-
// Also purge if the resource has neither a valid scratch key nor a content key.
bool noKey = !resource->resourcePriv().getScratchKey().isValid() &&
- !resource->getContentKey().isValid();
- if (!overBudget && !noKey) {
+ !resource->getContentKey().isValid();
+ if (!this->overBudget() && !noKey) {
return;
}
}
- SkDEBUGCODE(int beforeCount = fCount;)
+ SkDEBUGCODE(int beforeCount = this->getResourceCount();)
resource->cacheAccess().release();
// We should at least free this resource, perhaps dependent resources as well.
- SkASSERT(fCount < beforeCount);
+ SkASSERT(this->getResourceCount() < beforeCount);
this->validate();
}
@@ -338,14 +353,14 @@
}
void GrResourceCache::internalPurgeAsNeeded() {
- SkASSERT(fBudgetedCount > fMaxCount || fBudgetedBytes > fMaxBytes);
+ SkASSERT(this->overBudget());
bool stillOverbudget = true;
while (fPurgeableQueue.count()) {
GrGpuResource* resource = fPurgeableQueue.peek();
SkASSERT(resource->isPurgeable());
resource->cacheAccess().release();
- if (fBudgetedCount <= fMaxCount && fBudgetedBytes <= fMaxBytes) {
+ if (!this->overBudget()) {
stillOverbudget = false;
break;
}
@@ -384,6 +399,24 @@
}
}
+void GrResourceCache::addToNonpurgeableArray(GrGpuResource* resource) {
+ int index = fNonpurgeableResources.count();
+ *fNonpurgeableResources.append() = resource;
+ *resource->cacheAccess().accessCacheIndex() = index;
+}
+
+void GrResourceCache::removeFromNonpurgeableArray(GrGpuResource* resource) {
+ int* index = resource->cacheAccess().accessCacheIndex();
+ // Fill the whole we will create in the array with the tail object, adjust its index, and
+ // then pop the array
+ GrGpuResource* tail = *(fNonpurgeableResources.end() - 1);
+ SkASSERT(fNonpurgeableResources[*index] == resource);
+ fNonpurgeableResources[*index] = tail;
+ *tail->cacheAccess().accessCacheIndex() = *index;
+ fNonpurgeableResources.pop();
+ SkDEBUGCODE(*index = -1);
+}
+
#ifdef SK_DEBUG
void GrResourceCache::validate() const {
// Reduce the frequency of validations for large resource counts.
@@ -393,80 +426,108 @@
return;
}
- size_t bytes = 0;
- int count = 0;
- int budgetedCount = 0;
- size_t budgetedBytes = 0;
- int locked = 0;
- int scratch = 0;
- int couldBeScratch = 0;
- int content = 0;
+ struct Stats {
+ size_t fBytes;
+ int fBudgetedCount;
+ size_t fBudgetedBytes;
+ int fLocked;
+ int fScratch;
+ int fCouldBeScratch;
+ int fContent;
+ const ScratchMap* fScratchMap;
+ const ContentHash* fContentHash;
- ResourceList::Iter iter;
- GrGpuResource* resource = iter.init(fResources, ResourceList::Iter::kHead_IterStart);
- for ( ; resource; resource = iter.next()) {
- bytes += resource->gpuMemorySize();
- ++count;
-
- if (!resource->isPurgeable()) {
- ++locked;
+ Stats(const GrResourceCache* cache) {
+ memset(this, 0, sizeof(*this));
+ fScratchMap = &cache->fScratchMap;
+ fContentHash = &cache->fContentHash;
}
- if (resource->cacheAccess().isScratch()) {
- SkASSERT(!resource->getContentKey().isValid());
- ++scratch;
- SkASSERT(fScratchMap.countForKey(resource->resourcePriv().getScratchKey()));
- SkASSERT(!resource->cacheAccess().isWrapped());
- } else if (resource->resourcePriv().getScratchKey().isValid()) {
- SkASSERT(!resource->resourcePriv().isBudgeted() ||
- resource->getContentKey().isValid());
- ++couldBeScratch;
- SkASSERT(fScratchMap.countForKey(resource->resourcePriv().getScratchKey()));
- SkASSERT(!resource->cacheAccess().isWrapped());
- }
- const GrContentKey& contentKey = resource->getContentKey();
- if (contentKey.isValid()) {
- ++content;
- SkASSERT(fContentHash.find(contentKey) == resource);
- SkASSERT(!resource->cacheAccess().isWrapped());
- SkASSERT(resource->resourcePriv().isBudgeted());
- }
+ void update(GrGpuResource* resource) {
+ fBytes += resource->gpuMemorySize();
- if (resource->resourcePriv().isBudgeted()) {
- ++budgetedCount;
- budgetedBytes += resource->gpuMemorySize();
- }
+ if (!resource->isPurgeable()) {
+ ++fLocked;
+ }
- if (!resource->isPurgeable()) {
- SkASSERT(-1 == *resource->cacheAccess().accessCacheIndex());
+ if (resource->cacheAccess().isScratch()) {
+ SkASSERT(!resource->getContentKey().isValid());
+ ++fScratch;
+ SkASSERT(fScratchMap->countForKey(resource->resourcePriv().getScratchKey()));
+ SkASSERT(!resource->cacheAccess().isWrapped());
+ } else if (resource->resourcePriv().getScratchKey().isValid()) {
+ SkASSERT(!resource->resourcePriv().isBudgeted() ||
+ resource->getContentKey().isValid());
+ ++fCouldBeScratch;
+ SkASSERT(fScratchMap->countForKey(resource->resourcePriv().getScratchKey()));
+ SkASSERT(!resource->cacheAccess().isWrapped());
+ }
+ const GrContentKey& contentKey = resource->getContentKey();
+ if (contentKey.isValid()) {
+ ++fContent;
+ SkASSERT(fContentHash->find(contentKey) == resource);
+ SkASSERT(!resource->cacheAccess().isWrapped());
+ SkASSERT(resource->resourcePriv().isBudgeted());
+ }
+
+ if (resource->resourcePriv().isBudgeted()) {
+ ++fBudgetedCount;
+ fBudgetedBytes += resource->gpuMemorySize();
+ }
}
+ };
+
+ Stats stats(this);
+
+ for (int i = 0; i < fNonpurgeableResources.count(); ++i) {
+ SkASSERT(!fNonpurgeableResources[i]->isPurgeable());
+ SkASSERT(*fNonpurgeableResources[i]->cacheAccess().accessCacheIndex() == i);
+ SkASSERT(!fNonpurgeableResources[i]->wasDestroyed());
+ stats.update(fNonpurgeableResources[i]);
}
-
for (int i = 0; i < fPurgeableQueue.count(); ++i) {
SkASSERT(fPurgeableQueue.at(i)->isPurgeable());
+ SkASSERT(*fPurgeableQueue.at(i)->cacheAccess().accessCacheIndex() == i);
+ SkASSERT(!fPurgeableQueue.at(i)->wasDestroyed());
+ stats.update(fPurgeableQueue.at(i));
}
- SkASSERT(fCount - locked == fPurgeableQueue.count());
+ SkASSERT(fCount == this->getResourceCount());
SkASSERT(fBudgetedCount <= fCount);
- SkASSERT(fBudgetedBytes <= fBudgetedBytes);
- SkASSERT(bytes == fBytes);
- SkASSERT(count == fCount);
- SkASSERT(budgetedBytes == fBudgetedBytes);
- SkASSERT(budgetedCount == fBudgetedCount);
+ SkASSERT(fBudgetedBytes <= fBytes);
+ SkASSERT(stats.fBytes == fBytes);
+ SkASSERT(stats.fBudgetedBytes == fBudgetedBytes);
+ SkASSERT(stats.fBudgetedCount == fBudgetedCount);
#if GR_CACHE_STATS
SkASSERT(fBudgetedHighWaterCount <= fHighWaterCount);
SkASSERT(fBudgetedHighWaterBytes <= fHighWaterBytes);
- SkASSERT(bytes <= fHighWaterBytes);
- SkASSERT(count <= fHighWaterCount);
- SkASSERT(budgetedBytes <= fBudgetedHighWaterBytes);
- SkASSERT(budgetedCount <= fBudgetedHighWaterCount);
+ SkASSERT(fBytes <= fHighWaterBytes);
+ SkASSERT(fCount <= fHighWaterCount);
+ SkASSERT(fBudgetedBytes <= fBudgetedHighWaterBytes);
+ SkASSERT(fBudgetedCount <= fBudgetedHighWaterCount);
#endif
- SkASSERT(content == fContentHash.count());
- SkASSERT(scratch + couldBeScratch == fScratchMap.count());
+ SkASSERT(stats.fContent == fContentHash.count());
+ SkASSERT(stats.fScratch + stats.fCouldBeScratch == fScratchMap.count());
// This assertion is not currently valid because we can be in recursive notifyIsPurgeable()
// calls. This will be fixed when subresource registration is explicit.
// bool overBudget = budgetedBytes > fMaxBytes || budgetedCount > fMaxCount;
// SkASSERT(!overBudget || locked == count || fPurging);
}
+
+bool GrResourceCache::isInCache(const GrGpuResource* resource) const {
+ int index = *resource->cacheAccess().accessCacheIndex();
+ if (index < 0) {
+ return false;
+ }
+ if (index < fPurgeableQueue.count() && fPurgeableQueue.at(index) == resource) {
+ return true;
+ }
+ if (index < fNonpurgeableResources.count() && fNonpurgeableResources[index] == resource) {
+ return true;
+ }
+ SkDEBUGFAIL("Resource index should be -1 or the resource should be in the cache.");
+ return false;
+}
+
#endif