Track the number of resources that would become purgeable after flush
in GrResourceCache.
Bug: skia:8927
Change-Id: Ia00ba0ea541a22e29e9a8208044e1fabd5296782
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/205484
Reviewed-by: Robert Phillips <robertphillips@google.com>
Commit-Queue: Brian Salomon <bsalomon@google.com>
diff --git a/src/gpu/GrDeinstantiateProxyTracker.cpp b/src/gpu/GrDeinstantiateProxyTracker.cpp
index 4e65133..06d1e93 100644
--- a/src/gpu/GrDeinstantiateProxyTracker.cpp
+++ b/src/gpu/GrDeinstantiateProxyTracker.cpp
@@ -18,7 +18,7 @@
SkASSERT(proxy != fProxies[i].get());
}
#endif
- proxy->firstRefAccess().ref();
+ proxy->firstRefAccess().ref(fCache);
fProxies.push_back(sk_sp<GrSurfaceProxy>(proxy));
}
diff --git a/src/gpu/GrDeinstantiateProxyTracker.h b/src/gpu/GrDeinstantiateProxyTracker.h
index 2555ab1..f144fc2 100644
--- a/src/gpu/GrDeinstantiateProxyTracker.h
+++ b/src/gpu/GrDeinstantiateProxyTracker.h
@@ -11,9 +11,11 @@
#include "GrSurfaceProxy.h"
#include "SkTArray.h"
+class GrResourceCache;
+
class GrDeinstantiateProxyTracker {
public:
- GrDeinstantiateProxyTracker() {}
+ GrDeinstantiateProxyTracker(GrResourceCache* cache) : fCache(cache) {}
// Adds a proxy which will be deinstantiated at the end of flush. The same proxy may not be
// added multiple times.
@@ -23,6 +25,7 @@
void deinstantiateAllProxies();
private:
+ GrResourceCache* fCache;
SkTArray<sk_sp<GrSurfaceProxy>> fProxies;
};
diff --git a/src/gpu/GrDrawingManager.cpp b/src/gpu/GrDrawingManager.cpp
index bf17d6c..8de898d 100644
--- a/src/gpu/GrDrawingManager.cpp
+++ b/src/gpu/GrDrawingManager.cpp
@@ -238,7 +238,8 @@
fCpuBufferCache = GrBufferAllocPool::CpuBufferCache::Make(maxCachedBuffers);
}
- GrOpFlushState flushState(gpu, resourceProvider, &fTokenTracker, fCpuBufferCache);
+ GrOpFlushState flushState(gpu, resourceProvider, resourceCache, &fTokenTracker,
+ fCpuBufferCache);
GrOnFlushResourceProvider onFlushProvider(this);
// TODO: AFAICT the only reason fFlushState is on GrDrawingManager rather than on the
diff --git a/src/gpu/GrGpuResource.cpp b/src/gpu/GrGpuResource.cpp
index 6dc0dfa..3d2c197 100644
--- a/src/gpu/GrGpuResource.cpp
+++ b/src/gpu/GrGpuResource.cpp
@@ -104,6 +104,8 @@
return this->internalHasRef() || this->internalHasPendingIO();
}
+bool GrGpuResource::hasRef() const { return this->internalHasRef(); }
+
SkString GrGpuResource::getResourceName() const {
// Dump resource as "skia/gpu_resources/resource_#".
SkString resourceName("skia/gpu_resources/resource_");
@@ -229,3 +231,10 @@
} while (id == SK_InvalidUniqueID);
return id;
}
+
+//////////////////////////////////////////////////////////////////////////////
+
+void GrGpuResource::ProxyAccess::ref(GrResourceCache* cache) {
+ SkASSERT(cache == fResource->getContext()->priv().getResourceCache());
+ cache->resourceAccess().refResource(fResource);
+}
diff --git a/src/gpu/GrGpuResourceCacheAccess.h b/src/gpu/GrGpuResourceCacheAccess.h
index dd01b72..0e1ace0 100644
--- a/src/gpu/GrGpuResourceCacheAccess.h
+++ b/src/gpu/GrGpuResourceCacheAccess.h
@@ -55,6 +55,9 @@
/** Called by the cache to assign a new unique key. */
void setUniqueKey(const GrUniqueKey& key) { fResource->fUniqueKey = key; }
+ /** Is the resource ref'ed (not counting pending IOs). */
+ bool hasRef() const { return fResource->hasRef(); }
+
/** Called by the cache to make the unique key invalid. */
void removeUniqueKey() { fResource->fUniqueKey.reset(); }
diff --git a/src/gpu/GrOpFlushState.cpp b/src/gpu/GrOpFlushState.cpp
index 7a6aa18..3b59782 100644
--- a/src/gpu/GrOpFlushState.cpp
+++ b/src/gpu/GrOpFlushState.cpp
@@ -16,13 +16,14 @@
//////////////////////////////////////////////////////////////////////////////
GrOpFlushState::GrOpFlushState(GrGpu* gpu, GrResourceProvider* resourceProvider,
- GrTokenTracker* tokenTracker,
+ GrResourceCache* cache, GrTokenTracker* tokenTracker,
sk_sp<GrBufferAllocPool::CpuBufferCache> cpuBufferCache)
: fVertexPool(gpu, cpuBufferCache)
, fIndexPool(gpu, std::move(cpuBufferCache))
, fGpu(gpu)
, fResourceProvider(resourceProvider)
- , fTokenTracker(tokenTracker) {}
+ , fTokenTracker(tokenTracker)
+ , fDeinstantiateProxyTracker(cache) {}
const GrCaps& GrOpFlushState::caps() const {
return *fGpu->caps();
diff --git a/src/gpu/GrOpFlushState.h b/src/gpu/GrOpFlushState.h
index 07983d4..3016061 100644
--- a/src/gpu/GrOpFlushState.h
+++ b/src/gpu/GrOpFlushState.h
@@ -29,7 +29,7 @@
// vertexSpace and indexSpace may either be null or an alloation of size
// GrBufferAllocPool::kDefaultBufferSize. If the latter, then CPU memory is only allocated for
// vertices/indices when a buffer larger than kDefaultBufferSize is required.
- GrOpFlushState(GrGpu*, GrResourceProvider*, GrTokenTracker*,
+ GrOpFlushState(GrGpu*, GrResourceProvider*, GrResourceCache*, GrTokenTracker*,
sk_sp<GrBufferAllocPool::CpuBufferCache> = nullptr);
~GrOpFlushState() final { this->reset(); }
diff --git a/src/gpu/GrProxyProvider.cpp b/src/gpu/GrProxyProvider.cpp
index c831471..f33755d 100644
--- a/src/gpu/GrProxyProvider.cpp
+++ b/src/gpu/GrProxyProvider.cpp
@@ -105,7 +105,11 @@
GrTextureProxy* proxy = fUniquelyKeyedProxies.find(key);
sk_sp<GrTextureProxy> result;
if (proxy) {
- proxy->firstRefAccess().ref();
+ GrResourceCache* cache = nullptr;
+ if (auto directContext = fImageContext->priv().asDirectContext()) {
+ cache = directContext->priv().getResourceCache();
+ }
+ proxy->firstRefAccess().ref(cache);
result.reset(proxy);
SkASSERT(result->origin() == origin);
}
diff --git a/src/gpu/GrResourceCache.cpp b/src/gpu/GrResourceCache.cpp
index 8e91969..3347797 100644
--- a/src/gpu/GrResourceCache.cpp
+++ b/src/gpu/GrResourceCache.cpp
@@ -8,6 +8,8 @@
#include "GrResourceCache.h"
#include <atomic>
#include "GrCaps.h"
+#include "GrContext.h"
+#include "GrContextPriv.h"
#include "GrGpuResourceCacheAccess.h"
#include "GrProxyProvider.h"
#include "GrSingleOwner.h"
@@ -109,28 +111,12 @@
GrResourceCache::GrResourceCache(const GrCaps* caps, GrSingleOwner* singleOwner,
uint32_t contextUniqueID)
- : fProxyProvider(nullptr)
- , fTimestamp(0)
- , fMaxCount(kDefaultMaxCount)
- , fMaxBytes(kDefaultMaxSize)
-#if GR_CACHE_STATS
- , fHighWaterCount(0)
- , fHighWaterBytes(0)
- , fBudgetedHighWaterCount(0)
- , fBudgetedHighWaterBytes(0)
-#endif
- , fBytes(0)
- , fBudgetedCount(0)
- , fBudgetedBytes(0)
- , fPurgeableBytes(0)
- , fInvalidUniqueKeyInbox(contextUniqueID)
+ : fInvalidUniqueKeyInbox(contextUniqueID)
, fFreedGpuResourceInbox(contextUniqueID)
, fContextUniqueID(contextUniqueID)
, fSingleOwner(singleOwner)
, fPreferVRAMUseOverFlushes(caps->preferVRAMUseOverFlushes()) {
SkASSERT(contextUniqueID != SK_InvalidUniqueID);
- SkDEBUGCODE(fCount = 0;)
- SkDEBUGCODE(fNewlyPurgeableResourceForValidation = nullptr;)
}
GrResourceCache::~GrResourceCache() {
@@ -281,6 +267,17 @@
SkASSERT(!fResourcesAwaitingUnref.count());
}
+void GrResourceCache::refResource(GrGpuResource* resource) {
+ SkASSERT(resource);
+ SkASSERT(resource->getContext()->priv().getResourceCache() == this);
+ if (resource->cacheAccess().hasRef()) {
+ resource->ref();
+ } else {
+ this->refAndMakeResourceMRU(resource);
+ }
+ this->validate();
+}
+
class GrResourceCache::AvailableForScratchUse {
public:
AvailableForScratchUse(bool rejectPendingIO) : fRejectPendingIO(rejectPendingIO) { }
@@ -409,6 +406,10 @@
fPurgeableBytes -= resource->gpuMemorySize();
fPurgeableQueue.remove(resource);
this->addToNonpurgeableArray(resource);
+ } else if (!resource->cacheAccess().hasRef() &&
+ resource->resourcePriv().budgetedType() == GrBudgetedType::kBudgeted) {
+ SkASSERT(fNumBudgetedResourcesFlushWillMakePurgeable > 0);
+ fNumBudgetedResourcesFlushWillMakePurgeable--;
}
resource->cacheAccess().ref();
@@ -437,6 +438,19 @@
#endif
resource->cacheAccess().setTimestamp(this->getNextTimestamp());
SkDEBUGCODE(fNewlyPurgeableResourceForValidation = nullptr);
+ if (!resource->resourcePriv().isPurgeable() &&
+ resource->resourcePriv().budgetedType() == GrBudgetedType::kBudgeted) {
+ SkASSERT(resource->resourcePriv().hasPendingIO_debugOnly());
+ ++fNumBudgetedResourcesFlushWillMakePurgeable;
+ }
+ } else {
+ // If this is budgeted and just became purgeable by dropping the last pending IO
+ // then it clearly no longer needs a flush to become purgeable.
+ if (resource->resourcePriv().budgetedType() == GrBudgetedType::kBudgeted &&
+ resource->resourcePriv().isPurgeable()) {
+ SkASSERT(fNumBudgetedResourcesFlushWillMakePurgeable > 0);
+ fNumBudgetedResourcesFlushWillMakePurgeable--;
+ }
}
if (!SkToBool(ResourceAccess::kAllCntsReachedZero_RefNotificationFlag & flags)) {
@@ -508,11 +522,17 @@
fBudgetedHighWaterBytes = SkTMax(fBudgetedBytes, fBudgetedHighWaterBytes);
fBudgetedHighWaterCount = SkTMax(fBudgetedCount, fBudgetedHighWaterCount);
#endif
+ if (!resource->resourcePriv().isPurgeable() && !resource->cacheAccess().hasRef()) {
+ ++fNumBudgetedResourcesFlushWillMakePurgeable;
+ }
this->purgeAsNeeded();
} else {
SkASSERT(resource->resourcePriv().budgetedType() != GrBudgetedType::kUnbudgetedCacheable);
--fBudgetedCount;
fBudgetedBytes -= size;
+ if (!resource->resourcePriv().isPurgeable() && !resource->cacheAccess().hasRef()) {
+ --fNumBudgetedResourcesFlushWillMakePurgeable;
+ }
}
SkASSERT(wasPurgeable == resource->resourcePriv().isPurgeable());
TRACE_COUNTER2("skia.gpu.cache", "skia budget", "used",
@@ -891,12 +911,19 @@
Stats stats(this);
size_t purgeableBytes = 0;
+ int numBudgetedResourcesFlushWillMakePurgeable = 0;
for (int i = 0; i < fNonpurgeableResources.count(); ++i) {
SkASSERT(!fNonpurgeableResources[i]->resourcePriv().isPurgeable() ||
fNewlyPurgeableResourceForValidation == fNonpurgeableResources[i]);
SkASSERT(*fNonpurgeableResources[i]->cacheAccess().accessCacheIndex() == i);
SkASSERT(!fNonpurgeableResources[i]->wasDestroyed());
+ if (fNonpurgeableResources[i]->resourcePriv().budgetedType() == GrBudgetedType::kBudgeted &&
+ !fNonpurgeableResources[i]->cacheAccess().hasRef() &&
+ fNewlyPurgeableResourceForValidation != fNonpurgeableResources[i]) {
+ SkASSERT(fNonpurgeableResources[i]->resourcePriv().hasPendingIO_debugOnly());
+ ++numBudgetedResourcesFlushWillMakePurgeable;
+ }
stats.update(fNonpurgeableResources[i]);
}
for (int i = 0; i < fPurgeableQueue.count(); ++i) {
@@ -911,6 +938,8 @@
SkASSERT(fBudgetedCount <= fCount);
SkASSERT(fBudgetedBytes <= fBytes);
SkASSERT(stats.fBytes == fBytes);
+ SkASSERT(fNumBudgetedResourcesFlushWillMakePurgeable ==
+ numBudgetedResourcesFlushWillMakePurgeable);
SkASSERT(stats.fBudgetedBytes == fBudgetedBytes);
SkASSERT(stats.fBudgetedCount == fBudgetedCount);
SkASSERT(purgeableBytes == fPurgeableBytes);
diff --git a/src/gpu/GrResourceCache.h b/src/gpu/GrResourceCache.h
index 84378e6..ffaa6f3 100644
--- a/src/gpu/GrResourceCache.h
+++ b/src/gpu/GrResourceCache.h
@@ -265,9 +265,10 @@
void removeUniqueKey(GrGpuResource*);
void willRemoveScratchKey(const GrGpuResource*);
void didChangeBudgetStatus(GrGpuResource*);
- void refAndMakeResourceMRU(GrGpuResource*);
+ void refResource(GrGpuResource* resource);
/// @}
+ void refAndMakeResourceMRU(GrGpuResource*);
void processFreedGpuResources();
void addToNonpurgeableArray(GrGpuResource*);
void removeFromNonpurgeableArray(GrGpuResource*);
@@ -338,11 +339,11 @@
typedef SkTDPQueue<GrGpuResource*, CompareTimestamp, AccessResourceIndex> PurgeableQueue;
typedef SkTDArray<GrGpuResource*> ResourceArray;
- GrProxyProvider* fProxyProvider;
+ GrProxyProvider* fProxyProvider = nullptr;
// Whenever a resource is added to the cache or the result of a cache lookup, fTimestamp is
// assigned as the resource's timestamp and then incremented. fPurgeableQueue orders the
// purgeable resources by this value, and thus is used to purge resources in LRU order.
- uint32_t fTimestamp;
+ uint32_t fTimestamp = 0;
PurgeableQueue fPurgeableQueue;
ResourceArray fNonpurgeableResources;
@@ -352,37 +353,38 @@
UniqueHash fUniqueHash;
// our budget, used in purgeAsNeeded()
- int fMaxCount;
- size_t fMaxBytes;
+ int fMaxCount = kDefaultMaxCount;
+ size_t fMaxBytes = kDefaultMaxSize;
#if GR_CACHE_STATS
- int fHighWaterCount;
- size_t fHighWaterBytes;
- int fBudgetedHighWaterCount;
- size_t fBudgetedHighWaterBytes;
+ int fHighWaterCount = 0;
+ size_t fHighWaterBytes = 0;
+ int fBudgetedHighWaterCount = 0;
+ size_t fBudgetedHighWaterBytes = 0;
#endif
// our current stats for all resources
- SkDEBUGCODE(int fCount;)
- size_t fBytes;
+ SkDEBUGCODE(int fCount = 0;)
+ size_t fBytes = 0;
// our current stats for resources that count against the budget
- int fBudgetedCount;
- size_t fBudgetedBytes;
- size_t fPurgeableBytes;
+ int fBudgetedCount = 0;
+ size_t fBudgetedBytes = 0;
+ size_t fPurgeableBytes = 0;
+ int fNumBudgetedResourcesFlushWillMakePurgeable = 0;
InvalidUniqueKeyInbox fInvalidUniqueKeyInbox;
FreedGpuResourceInbox fFreedGpuResourceInbox;
ReourcesAwaitingUnref fResourcesAwaitingUnref;
- uint32_t fContextUniqueID;
- GrSingleOwner* fSingleOwner;
+ uint32_t fContextUniqueID = SK_InvalidUniqueID;
+ GrSingleOwner* fSingleOwner = nullptr;
// This resource is allowed to be in the nonpurgeable array for the sake of validate() because
// we're in the midst of converting it to purgeable status.
- SkDEBUGCODE(GrGpuResource* fNewlyPurgeableResourceForValidation;)
+ SkDEBUGCODE(GrGpuResource* fNewlyPurgeableResourceForValidation = nullptr;)
- bool fPreferVRAMUseOverFlushes;
+ bool fPreferVRAMUseOverFlushes = false;
};
GR_MAKE_BITFIELD_CLASS_OPS(GrResourceCache::ScratchFlags);
@@ -404,6 +406,12 @@
void removeResource(GrGpuResource* resource) { fCache->removeResource(resource); }
/**
+ * Adds a ref to a resource with proper tracking if the resource has 0 refs prior to
+ * adding the ref.
+ */
+ void refResource(GrGpuResource* resource) { fCache->refResource(resource); }
+
+ /**
* Notifications that should be sent to the cache when the ref/io cnt status of resources
* changes.
*/