Index: src/gpu/GrResourceCache.cpp |
diff --git a/src/gpu/GrResourceCache.cpp b/src/gpu/GrResourceCache.cpp |
index 12e959f198f11aa0287e1a9fe8afd605277d6fb3..e9be509569a09efef5d22d9ad9ffb5b84a722a73 100644 |
--- a/src/gpu/GrResourceCache.cpp |
+++ b/src/gpu/GrResourceCache.cpp |
@@ -82,10 +82,14 @@ |
#if GR_CACHE_STATS |
fHighWaterEntryCount = 0; |
fHighWaterEntryBytes = 0; |
+ fHighWaterClientDetachedCount = 0; |
+ fHighWaterClientDetachedBytes = 0; |
#endif |
fEntryCount = 0; |
fEntryBytes = 0; |
+ fClientDetachedCount = 0; |
+ fClientDetachedBytes = 0; |
fPurging = false; |
@@ -132,26 +136,55 @@ |
} |
} |
-void GrResourceCache::internalDetach(GrResourceCacheEntry* entry) { |
+void GrResourceCache::internalDetach(GrResourceCacheEntry* entry, |
+ BudgetBehaviors behavior) { |
fList.remove(entry); |
- fEntryCount -= 1; |
- fEntryBytes -= entry->fCachedSize; |
-} |
- |
-void GrResourceCache::attachToHead(GrResourceCacheEntry* entry) { |
+ |
+ // update our stats |
+ if (kIgnore_BudgetBehavior == behavior) { |
+ fClientDetachedCount += 1; |
+ fClientDetachedBytes += entry->fCachedSize; |
+ |
+#if GR_CACHE_STATS |
+ if (fHighWaterClientDetachedCount < fClientDetachedCount) { |
+ fHighWaterClientDetachedCount = fClientDetachedCount; |
+ } |
+ if (fHighWaterClientDetachedBytes < fClientDetachedBytes) { |
+ fHighWaterClientDetachedBytes = fClientDetachedBytes; |
+ } |
+#endif |
+ |
+ } else { |
+ SkASSERT(kAccountFor_BudgetBehavior == behavior); |
+ |
+ fEntryCount -= 1; |
+ fEntryBytes -= entry->fCachedSize; |
+ } |
+} |
+ |
+void GrResourceCache::attachToHead(GrResourceCacheEntry* entry, |
+ BudgetBehaviors behavior) { |
fList.addToHead(entry); |
- fEntryCount += 1; |
- fEntryBytes += entry->fCachedSize; |
+ // update our stats |
+ if (kIgnore_BudgetBehavior == behavior) { |
+ fClientDetachedCount -= 1; |
+ fClientDetachedBytes -= entry->fCachedSize; |
+ } else { |
+ SkASSERT(kAccountFor_BudgetBehavior == behavior); |
+ |
+ fEntryCount += 1; |
+ fEntryBytes += entry->fCachedSize; |
#if GR_CACHE_STATS |
- if (fHighWaterEntryCount < fEntryCount) { |
- fHighWaterEntryCount = fEntryCount; |
- } |
- if (fHighWaterEntryBytes < fEntryBytes) { |
- fHighWaterEntryBytes = fEntryBytes; |
- } |
-#endif |
+ if (fHighWaterEntryCount < fEntryCount) { |
+ fHighWaterEntryCount = fEntryCount; |
+ } |
+ if (fHighWaterEntryBytes < fEntryBytes) { |
+ fHighWaterEntryBytes = fEntryBytes; |
+ } |
+#endif |
+ } |
} |
// This functor just searches for an entry with only a single ref (from |
@@ -160,40 +193,41 @@ |
class GrTFindUnreffedFunctor { |
public: |
bool operator()(const GrResourceCacheEntry* entry) const { |
- return entry->resource()->isPurgable(); |
+ return entry->resource()->unique(); |
} |
}; |
- |
-void GrResourceCache::makeResourceMRU(GrGpuResource* resource) { |
- GrResourceCacheEntry* entry = resource->getCacheEntry(); |
- if (entry) { |
+GrGpuResource* GrResourceCache::find(const GrResourceKey& key, uint32_t ownershipFlags) { |
+ GrAutoResourceCacheValidate atcv(this); |
+ |
+ GrResourceCacheEntry* entry = NULL; |
+ |
+ if (ownershipFlags & kNoOtherOwners_OwnershipFlag) { |
+ GrTFindUnreffedFunctor functor; |
+ |
+ entry = fCache.find<GrTFindUnreffedFunctor>(key, functor); |
+ } else { |
+ entry = fCache.find(key); |
+ } |
+ |
+ if (NULL == entry) { |
+ return NULL; |
+ } |
+ |
+ if (ownershipFlags & kHide_OwnershipFlag) { |
+ this->makeExclusive(entry); |
+ } else { |
+ // Make this resource MRU |
this->internalDetach(entry); |
this->attachToHead(entry); |
} |
-} |
- |
-GrGpuResource* GrResourceCache::find(const GrResourceKey& key) { |
- GrAutoResourceCacheValidate atcv(this); |
- |
- GrResourceCacheEntry* entry = NULL; |
- |
- entry = fCache.find(key); |
- |
- if (NULL == entry) { |
- return NULL; |
- } |
- |
- // Make this resource MRU |
- this->internalDetach(entry); |
- this->attachToHead(entry); |
- |
- // GrResourceCache2 is responsible for scratch resources. |
- SkASSERT(GrIORef::kNo_IsScratch == entry->resource()->fIsScratch); |
+ |
return entry->fResource; |
} |
-void GrResourceCache::addResource(const GrResourceKey& key, GrGpuResource* resource) { |
+void GrResourceCache::addResource(const GrResourceKey& key, |
+ GrGpuResource* resource, |
+ uint32_t ownershipFlags) { |
SkASSERT(NULL == resource->getCacheEntry()); |
// we don't expect to create new resources during a purge. In theory |
// this could cause purgeAsNeeded() into an infinite loop (e.g. |
@@ -207,15 +241,77 @@ |
this->attachToHead(entry); |
fCache.insert(key, entry); |
+ |
+ if (ownershipFlags & kHide_OwnershipFlag) { |
+ this->makeExclusive(entry); |
+ } |
+ |
+} |
+ |
+void GrResourceCache::makeExclusive(GrResourceCacheEntry* entry) { |
+ GrAutoResourceCacheValidate atcv(this); |
+ |
+ SkASSERT(!entry->fIsExclusive); |
+ entry->fIsExclusive = true; |
+ |
+ // 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); |
+ |
+#ifdef SK_DEBUG |
+ fExclusiveList.addToHead(entry); |
+#endif |
+} |
+ |
+void GrResourceCache::removeInvalidResource(GrResourceCacheEntry* entry) { |
+ // 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::abandonContext() to notify Gr, |
+ // and then later an SkGpuDevice's destructor releases its backing |
+ // texture (which was invalidated at contextDestroyed time). |
+ // TODO: Safely delete the GrResourceCacheEntry as well. |
+ fClientDetachedCount -= 1; |
+ fEntryCount -= 1; |
+ fClientDetachedBytes -= entry->fCachedSize; |
+ fEntryBytes -= entry->fCachedSize; |
+ entry->fCachedSize = 0; |
+} |
+ |
+void GrResourceCache::makeNonExclusive(GrResourceCacheEntry* entry) { |
+ GrAutoResourceCacheValidate atcv(this); |
+ |
+#ifdef SK_DEBUG |
+ fExclusiveList.remove(entry); |
+#endif |
+ |
+ if (!entry->resource()->wasDestroyed()) { |
+ // 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); |
+ |
+ SkASSERT(entry->fIsExclusive); |
+ entry->fIsExclusive = false; |
+ } else { |
+ this->removeInvalidResource(entry); |
+ } |
} |
void GrResourceCache::didIncreaseResourceSize(const GrResourceCacheEntry* entry, size_t amountInc) { |
fEntryBytes += amountInc; |
+ if (entry->fIsExclusive) { |
+ fClientDetachedBytes += amountInc; |
+ } |
this->purgeAsNeeded(); |
} |
void GrResourceCache::didDecreaseResourceSize(const GrResourceCacheEntry* entry, size_t amountDec) { |
fEntryBytes -= amountDec; |
+ if (entry->fIsExclusive) { |
+ fClientDetachedBytes -= amountDec; |
+ } |
#ifdef SK_DEBUG |
this->validate(); |
#endif |
@@ -263,6 +359,13 @@ |
fInvalidationInbox.poll(&invalidated); |
for (int i = 0; i < invalidated.count(); i++) { |
+ // We're somewhat missing an opportunity here. We could use the |
+ // default find functor that gives us back resources whether we own |
+ // them exclusively or not, and when they're not exclusively owned mark |
+ // them for purging later when they do become exclusively owned. |
+ // |
+ // 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. |
while (GrResourceCacheEntry* entry = fCache.find(invalidated[i].key, GrTFindUnreffedFunctor())) { |
this->deleteResource(entry); |
} |
@@ -270,7 +373,7 @@ |
} |
void GrResourceCache::deleteResource(GrResourceCacheEntry* entry) { |
- SkASSERT(entry->fResource->isPurgable()); |
+ SkASSERT(entry->fResource->unique()); |
// remove from our cache |
fCache.remove(entry->key(), entry); |
@@ -309,7 +412,7 @@ |
} |
GrResourceCacheEntry* prev = iter.prev(); |
- if (entry->fResource->isPurgable()) { |
+ if (entry->fResource->unique()) { |
changed = true; |
this->deleteResource(entry); |
} |
@@ -332,7 +435,14 @@ |
this->purgeAsNeeded(); |
#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()); |
} |
#endif |
@@ -364,14 +474,25 @@ |
void GrResourceCache::validate() const { |
fList.validate(); |
+ fExclusiveList.validate(); |
SkASSERT(both_zero_or_nonzero(fEntryCount, fEntryBytes)); |
- SkASSERT(fEntryCount == fCache.count()); |
+ SkASSERT(both_zero_or_nonzero(fClientDetachedCount, fClientDetachedBytes)); |
+ SkASSERT(fClientDetachedBytes <= fEntryBytes); |
+ SkASSERT(fClientDetachedCount <= fEntryCount); |
+ SkASSERT((fEntryCount - fClientDetachedCount) == fCache.count()); |
EntryList::Iter iter; |
+ // check that the exclusively held entries are okay |
+ const GrResourceCacheEntry* entry = iter.init(const_cast<EntryList&>(fExclusiveList), |
+ EntryList::Iter::kHead_IterStart); |
+ |
+ for ( ; entry; entry = iter.next()) { |
+ entry->validate(); |
+ } |
+ |
// check that the shareable entries are okay |
- const GrResourceCacheEntry* entry = iter.init(const_cast<EntryList&>(fList), |
- EntryList::Iter::kHead_IterStart); |
+ entry = iter.init(const_cast<EntryList&>(fList), EntryList::Iter::kHead_IterStart); |
int count = 0; |
for ( ; entry; entry = iter.next()) { |
@@ -379,11 +500,17 @@ |
SkASSERT(fCache.find(entry->key())); |
count += 1; |
} |
- SkASSERT(count == fEntryCount); |
- |
- size_t bytes = this->countBytes(fList); |
- SkASSERT(bytes == fEntryBytes); |
- SkASSERT(fList.countEntries() == fEntryCount); |
+ SkASSERT(count == fEntryCount - fClientDetachedCount); |
+ |
+ size_t bytes = countBytes(fList); |
+ SkASSERT(bytes == fEntryBytes - fClientDetachedBytes); |
+ |
+ bytes = countBytes(fExclusiveList); |
+ SkASSERT(bytes == fClientDetachedBytes); |
+ |
+ SkASSERT(fList.countEntries() == fEntryCount - fClientDetachedCount); |
+ |
+ SkASSERT(fExclusiveList.countEntries() == fClientDetachedCount); |
} |
#endif // SK_DEBUG |
@@ -407,6 +534,10 @@ |
fEntryCount, locked, fHighWaterEntryCount); |
SkDebugf("\t\tEntry Bytes: current %d high %d\n", |
fEntryBytes, fHighWaterEntryBytes); |
+ SkDebugf("\t\tDetached Entry Count: current %d high %d\n", |
+ fClientDetachedCount, fHighWaterClientDetachedCount); |
+ SkDebugf("\t\tDetached Bytes: current %d high %d\n", |
+ fClientDetachedBytes, fHighWaterClientDetachedBytes); |
} |
#endif |