Index: src/gpu/GrResourceCache.cpp |
diff --git a/src/gpu/GrResourceCache.cpp b/src/gpu/GrResourceCache.cpp |
index d87e1a5b588211fe70716f6cc6cd1024487b23c3..04b7ec57771887c4b3f742ec1701bc2f09a91afa 100644 |
--- a/src/gpu/GrResourceCache.cpp |
+++ b/src/gpu/GrResourceCache.cpp |
@@ -67,12 +67,12 @@ GrResourceCache::GrResourceCache() |
, 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::setLimits(int count, size_t bytes) { |
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 @@ void GrResourceCache::removeResource(GrGpuResource* resource) { |
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::removeResource(GrGpuResource* resource) { |
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::abandonAll() { |
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 @@ void GrResourceCache::refAndMakeResourceMRU(GrGpuResource* resource) { |
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 @@ void GrResourceCache::notifyPurgeable(GrGpuResource* resource) { |
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 @@ void GrResourceCache::notifyPurgeable(GrGpuResource* resource) { |
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::didChangeBudgetStatus(GrGpuResource* resource) { |
} |
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::processInvalidContentKeys( |
} |
} |
+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 @@ void GrResourceCache::validate() const { |
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; |
- |
- 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; |
+ struct Stats { |
+ size_t fBytes; |
+ int fBudgetedCount; |
+ size_t fBudgetedBytes; |
+ int fLocked; |
+ int fScratch; |
+ int fCouldBeScratch; |
+ int fContent; |
+ const ScratchMap* fScratchMap; |
+ const ContentHash* fContentHash; |
+ |
+ 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 |