| 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
|
|
|