Add scratch-only version of performDeferredCleanup
Change-Id: I5707d1da1b69ab1ffaa77d7a391a187ac3e8eba1
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/417267
Commit-Queue: Michael Ludwig <michaelludwig@google.com>
Reviewed-by: Robert Phillips <robertphillips@google.com>
diff --git a/bench/GrResourceCacheBench.cpp b/bench/GrResourceCacheBench.cpp
index 6133c07..dc2fef8 100644
--- a/bench/GrResourceCacheBench.cpp
+++ b/bench/GrResourceCacheBench.cpp
@@ -79,7 +79,7 @@
GrResourceCache* cache = context->priv().getResourceCache();
// Make sure the cache is empty.
- cache->purgeAllUnlocked();
+ cache->purgeUnlockedResources();
SkASSERT(0 == cache->getResourceCount() && 0 == cache->getResourceBytes());
GrGpu* gpu = context->priv().getGpu();
@@ -125,7 +125,7 @@
GrResourceCache* cache = fContext->priv().getResourceCache();
// Make sure the cache is empty.
- cache->purgeAllUnlocked();
+ cache->purgeUnlockedResources();
SkASSERT(0 == cache->getResourceCount() && 0 == cache->getResourceBytes());
GrGpu* gpu = fContext->priv().getGpu();
diff --git a/bench/ImageCacheBudgetBench.cpp b/bench/ImageCacheBudgetBench.cpp
index a224cbf..5aa1621 100644
--- a/bench/ImageCacheBudgetBench.cpp
+++ b/bench/ImageCacheBudgetBench.cpp
@@ -11,6 +11,7 @@
#include "include/core/SkSurface.h"
#include "include/gpu/GrDirectContext.h"
#include "src/gpu/GrDirectContextPriv.h"
+#include "src/gpu/GrResourceCache.h"
#include "tools/ToolUtils.h"
@@ -45,7 +46,7 @@
auto context = canvas->recordingContext()->asDirectContext();
SkASSERT(context);
context->flushAndSubmit();
- context->priv().testingOnly_purgeAllUnlockedResources();
+ context->priv().getResourceCache()->purgeUnlockedResources();
sk_sp<SkImage> image;
make_images(&image, 1);
draw_image(canvas, image.get());
@@ -54,7 +55,7 @@
context->getResourceCacheUsage(&baselineCount, nullptr);
baselineCount -= 1; // for the image's textures.
context->setResourceCacheLimits(baselineCount + approxImagesInBudget, 1 << 30);
- context->priv().testingOnly_purgeAllUnlockedResources();
+ context->priv().getResourceCache()->purgeUnlockedResources();
}
//////////////////////////////////////////////////////////////////////////////
diff --git a/include/gpu/GrDirectContext.h b/include/gpu/GrDirectContext.h
index 022bad5..807d1b3 100644
--- a/include/gpu/GrDirectContext.h
+++ b/include/gpu/GrDirectContext.h
@@ -253,8 +253,18 @@
/**
* Purge GPU resources that haven't been used in the past 'msNotUsed' milliseconds or are
* otherwise marked for deletion, regardless of whether the context is under budget.
+ *
+ * If 'scratchResourcesOnly' is true all unlocked scratch resources older than 'msNotUsed' will
+ * be purged but the unlocked resources with persistent data will remain. If
+ * 'scratchResourcesOnly' is false then all unlocked resources older than 'msNotUsed' will be
+ * purged.
+ *
+ * @param msNotUsed Only unlocked resources not used in these last milliseconds
+ * will be cleaned up.
+ * @param scratchResourcesOnly If true only unlocked scratch resources will be purged.
*/
- void performDeferredCleanup(std::chrono::milliseconds msNotUsed);
+ void performDeferredCleanup(std::chrono::milliseconds msNotUsed,
+ bool scratchResourcesOnly=false);
// Temporary compatibility API for Android.
void purgeResourcesNotUsedInMs(std::chrono::milliseconds msNotUsed) {
diff --git a/src/gpu/GrDirectContext.cpp b/src/gpu/GrDirectContext.cpp
index 3a2ae7e..36b5c52 100644
--- a/src/gpu/GrDirectContext.cpp
+++ b/src/gpu/GrDirectContext.cpp
@@ -193,7 +193,7 @@
this->drawingManager()->freeGpuResources();
- fResourceCache->purgeAllUnlocked();
+ fResourceCache->purgeUnlockedResources();
}
bool GrDirectContext::init() {
@@ -317,7 +317,8 @@
fGpu->releaseUnlockedBackendObjects();
}
-void GrDirectContext::performDeferredCleanup(std::chrono::milliseconds msNotUsed) {
+void GrDirectContext::performDeferredCleanup(std::chrono::milliseconds msNotUsed,
+ bool scratchResourcesOnly) {
TRACE_EVENT0("skia.gpu", TRACE_FUNC);
ASSERT_SINGLE_OWNER
@@ -331,7 +332,7 @@
auto purgeTime = GrStdSteadyClock::now() - msNotUsed;
fResourceCache->purgeAsNeeded();
- fResourceCache->purgeResourcesNotUsedSince(purgeTime);
+ fResourceCache->purgeResourcesNotUsedSince(purgeTime, scratchResourcesOnly);
// The textBlob Cache doesn't actually hold any GPU resource but this is a convenient
// place to purge stale blobs
diff --git a/src/gpu/GrDirectContextPriv.cpp b/src/gpu/GrDirectContextPriv.cpp
index 66917a1..b00a1a2 100644
--- a/src/gpu/GrDirectContextPriv.cpp
+++ b/src/gpu/GrDirectContextPriv.cpp
@@ -185,10 +185,6 @@
SkColorInfo(colorType, kPremul_SkAlphaType, nullptr));
}
-void GrDirectContextPriv::testingOnly_purgeAllUnlockedResources() {
- fContext->fResourceCache->purgeAllUnlocked();
-}
-
void GrDirectContextPriv::testingOnly_flushAndRemoveOnFlushCallbackObject(
GrOnFlushCallbackObject* cb) {
fContext->flushAndSubmit();
diff --git a/src/gpu/GrDirectContextPriv.h b/src/gpu/GrDirectContextPriv.h
index fd205d0..fd8f9af 100644
--- a/src/gpu/GrDirectContextPriv.h
+++ b/src/gpu/GrDirectContextPriv.h
@@ -187,13 +187,6 @@
if it gets cached or used more generally. */
sk_sp<SkImage> testingOnly_getFontAtlasImage(GrMaskFormat format, unsigned int index = 0);
- /**
- * Purge all the unlocked resources from the cache.
- * This entry point is mainly meant for timing texture uploads
- * and is not defined in normal builds of Skia.
- */
- void testingOnly_purgeAllUnlockedResources();
-
void testingOnly_flushAndRemoveOnFlushCallbackObject(GrOnFlushCallbackObject*);
#endif
diff --git a/src/gpu/GrResourceCache.cpp b/src/gpu/GrResourceCache.cpp
index 64d470c..2a96973 100644
--- a/src/gpu/GrResourceCache.cpp
+++ b/src/gpu/GrResourceCache.cpp
@@ -564,19 +564,43 @@
this->validate();
}
-void GrResourceCache::purgeUnlockedResources(bool scratchResourcesOnly) {
+void GrResourceCache::purgeUnlockedResources(const GrStdSteadyClock::time_point* purgeTime,
+ bool scratchResourcesOnly) {
if (!scratchResourcesOnly) {
- fThreadSafeCache->dropUniqueRefs(nullptr);
+ if (purgeTime) {
+ fThreadSafeCache->dropUniqueRefsOlderThan(*purgeTime);
+ } else {
+ fThreadSafeCache->dropUniqueRefs(nullptr);
+ }
// We could disable maintaining the heap property here, but it would add a lot of
// complexity. Moreover, this is rarely called.
while (fPurgeableQueue.count()) {
GrGpuResource* resource = fPurgeableQueue.peek();
+
+ const GrStdSteadyClock::time_point resourceTime =
+ resource->cacheAccess().timeWhenResourceBecamePurgeable();
+ if (purgeTime && resourceTime >= *purgeTime) {
+ // Resources were given both LRU timestamps and tagged with a frame number when
+ // they first became purgeable. The LRU timestamp won't change again until the
+ // resource is made non-purgeable again. So, at this point all the remaining
+ // resources in the timestamp-sorted queue will have a frame number >= to this
+ // one.
+ break;
+ }
+
SkASSERT(resource->resourcePriv().isPurgeable());
resource->cacheAccess().release();
}
} else {
+ // Early out if the very first item is too new to purge to avoid sorting the queue when
+ // nothing will be deleted.
+ if (purgeTime && fPurgeableQueue.count() &&
+ fPurgeableQueue.peek()->cacheAccess().timeWhenResourceBecamePurgeable() >= *purgeTime) {
+ return;
+ }
+
// Sort the queue
fPurgeableQueue.sort();
@@ -584,6 +608,13 @@
SkTDArray<GrGpuResource*> scratchResources;
for (int i = 0; i < fPurgeableQueue.count(); i++) {
GrGpuResource* resource = fPurgeableQueue.at(i);
+
+ const GrStdSteadyClock::time_point resourceTime =
+ resource->cacheAccess().timeWhenResourceBecamePurgeable();
+ if (purgeTime && resourceTime >= *purgeTime) {
+ // scratch or not, all later iterations will be too recently used to purge.
+ break;
+ }
SkASSERT(resource->resourcePriv().isPurgeable());
if (!resource->getUniqueKey().isValid()) {
*scratchResources.append() = resource;
@@ -600,26 +631,6 @@
this->validate();
}
-void GrResourceCache::purgeResourcesNotUsedSince(GrStdSteadyClock::time_point purgeTime) {
- fThreadSafeCache->dropUniqueRefsOlderThan(purgeTime);
-
- while (fPurgeableQueue.count()) {
- const GrStdSteadyClock::time_point resourceTime =
- fPurgeableQueue.peek()->cacheAccess().timeWhenResourceBecamePurgeable();
- if (resourceTime >= purgeTime) {
- // Resources were given both LRU timestamps and tagged with a frame number when
- // they first became purgeable. The LRU timestamp won't change again until the
- // resource is made non-purgeable again. So, at this point all the remaining
- // resources in the timestamp-sorted queue will have a frame number >= to this
- // one.
- break;
- }
- GrGpuResource* resource = fPurgeableQueue.peek();
- SkASSERT(resource->resourcePriv().isPurgeable());
- resource->cacheAccess().release();
- }
-}
-
bool GrResourceCache::purgeToMakeHeadroom(size_t desiredHeadroomBytes) {
AutoValidate av(this);
if (desiredHeadroomBytes > fMaxBytes) {
diff --git a/src/gpu/GrResourceCache.h b/src/gpu/GrResourceCache.h
index eb52ca2..198ace2 100644
--- a/src/gpu/GrResourceCache.h
+++ b/src/gpu/GrResourceCache.h
@@ -154,16 +154,20 @@
keys. */
void purgeAsNeeded();
- /** Purges all resources that don't have external owners. */
- void purgeAllUnlocked() { this->purgeUnlockedResources(false); }
-
// Purge unlocked resources. If 'scratchResourcesOnly' is true the purgeable resources
// containing persistent data are spared. If it is false then all purgeable resources will
// be deleted.
- void purgeUnlockedResources(bool scratchResourcesOnly);
+ void purgeUnlockedResources(bool scratchResourcesOnly=false) {
+ this->purgeUnlockedResources(/*purgeTime=*/nullptr, scratchResourcesOnly);
+ }
- /** Purge all resources not used since the passed in time. */
- void purgeResourcesNotUsedSince(GrStdSteadyClock::time_point);
+ // Purge unlocked resources not used since the passed point in time. If 'scratchResourcesOnly'
+ // is true the purgeable resources containing persistent data are spared. If it is false then
+ // all purgeable resources older than 'purgeTime' will be deleted.
+ void purgeResourcesNotUsedSince(GrStdSteadyClock::time_point purgeTime,
+ bool scratchResourcesOnly=false) {
+ this->purgeUnlockedResources(&purgeTime, scratchResourcesOnly);
+ }
/** If it's possible to purge enough resources to get the provided amount of budget
headroom, do so and return true. If it's not possible, do nothing and return false.
@@ -273,6 +277,9 @@
uint32_t getNextTimestamp();
+ void purgeUnlockedResources(const GrStdSteadyClock::time_point* purgeTime,
+ bool scratchResourcesOnly);
+
#ifdef SK_DEBUG
bool isInCache(const GrGpuResource* r) const;
void validate() const;
diff --git a/tests/GrSurfaceTest.cpp b/tests/GrSurfaceTest.cpp
index 0f94d5e..717039c 100644
--- a/tests/GrSurfaceTest.cpp
+++ b/tests/GrSurfaceTest.cpp
@@ -281,7 +281,7 @@
}
}
- dContext->priv().testingOnly_purgeAllUnlockedResources();
+ dContext->priv().getResourceCache()->purgeUnlockedResources();
}
// Try creating the texture as a deferred proxy.
@@ -311,7 +311,7 @@
}
}
}
- dContext->priv().testingOnly_purgeAllUnlockedResources();
+ dContext->priv().getResourceCache()->purgeUnlockedResources();
}
}
}
diff --git a/tests/PathRendererCacheTests.cpp b/tests/PathRendererCacheTests.cpp
index 2024b17..fbd6750 100644
--- a/tests/PathRendererCacheTests.cpp
+++ b/tests/PathRendererCacheTests.cpp
@@ -124,7 +124,7 @@
}
dContext->flushAndSubmit();
REPORTER_ASSERT(reporter, SkPathPriv::GenIDChangeListenersCount(path) == 20);
- cache->purgeAllUnlocked();
+ cache->purgeUnlockedResources();
// The listeners don't actually purge until we try to add another one.
draw_path(dContext.get(), rtc.get(), path, pathRenderer.get(), aaType, style);
REPORTER_ASSERT(reporter, SkPathPriv::GenIDChangeListenersCount(path) == 1);
diff --git a/tests/ResourceCacheTest.cpp b/tests/ResourceCacheTest.cpp
index 9e4a27c..3ca5acc 100644
--- a/tests/ResourceCacheTest.cpp
+++ b/tests/ResourceCacheTest.cpp
@@ -331,7 +331,7 @@
SkASSERT(fDContext);
fDContext->setResourceCacheLimit(maxBytes);
GrResourceCache* cache = fDContext->priv().getResourceCache();
- cache->purgeAllUnlocked();
+ cache->purgeUnlockedResources();
SkASSERT(0 == cache->getResourceCount() && 0 == cache->getResourceBytes());
}
@@ -364,7 +364,7 @@
d->gpuMemorySize() == cache->getResourceBytes());
// Should be safe to purge without deleting the resources since we still have refs.
- cache->purgeAllUnlocked();
+ cache->purgeUnlockedResources();
REPORTER_ASSERT(reporter, 4 == TestResource::NumAlive());
// Since the resources have neither unique nor scratch keys, delete immediately upon unref.
@@ -578,7 +578,7 @@
REPORTER_ASSERT(reporter, 0 == cache->getPurgeableBytes());
// Our refs mean that the resources are non purgeable.
- cache->purgeAllUnlocked();
+ cache->purgeUnlockedResources();
REPORTER_ASSERT(reporter, 5 == cache->getResourceCount());
REPORTER_ASSERT(reporter, scratch->gpuMemorySize() + unique->gpuMemorySize() +
wrappedCacheable->gpuMemorySize() +
@@ -607,7 +607,7 @@
REPORTER_ASSERT(reporter, 11 == cache->getPurgeableBytes());
// This will free 'unique' but not wrappedCacheable which has a key. That requires the key to be
// removed to be freed.
- cache->purgeAllUnlocked();
+ cache->purgeUnlockedResources();
REPORTER_ASSERT(reporter, 4 == cache->getResourceCount());
wrappedCacheableViaKey = cache->findAndRefUniqueResource(uniqueKey2);
@@ -631,7 +631,7 @@
scratch->unref();
REPORTER_ASSERT(reporter, 10 == cache->getPurgeableBytes());
- cache->purgeAllUnlocked();
+ cache->purgeUnlockedResources();
REPORTER_ASSERT(reporter, 3 == cache->getResourceCount());
REPORTER_ASSERT(reporter, unbudgeted->gpuMemorySize() + wrappedCacheable->gpuMemorySize() +
wrappedUncacheable->gpuMemorySize() ==
@@ -719,7 +719,7 @@
REPORTER_ASSERT(reporter, 21 == cache->getBudgetedResourceBytes());
REPORTER_ASSERT(reporter, 21 == cache->getPurgeableBytes());
- cache->purgeAllUnlocked();
+ cache->purgeUnlockedResources();
REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
REPORTER_ASSERT(reporter, 0 == cache->getResourceBytes());
REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceCount());
@@ -824,7 +824,7 @@
cache->getResourceBytes());
// Our refs mean that the resources are non purgeable.
- cache->purgeAllUnlocked();
+ cache->purgeUnlockedResources();
REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive());
REPORTER_ASSERT(reporter, 2 == cache->getResourceCount());
@@ -836,7 +836,7 @@
SkDEBUGCODE(REPORTER_ASSERT(reporter, 2 == cache->countScratchEntriesForKey(scratchKey));)
// Purge again. This time resources should be purgeable.
- cache->purgeAllUnlocked();
+ cache->purgeUnlockedResources();
REPORTER_ASSERT(reporter, 0 == TestResource::NumAlive());
REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
SkDEBUGCODE(REPORTER_ASSERT(reporter, 0 == cache->countScratchEntriesForKey(scratchKey));)
@@ -1017,7 +1017,7 @@
REPORTER_ASSERT(reporter, 1 == TestResource::NumAlive());
// c shouldn't be purged because it is ref'ed.
- cache->purgeAllUnlocked();
+ cache->purgeUnlockedResources();
REPORTER_ASSERT(reporter, 1 == cache->getResourceCount());
REPORTER_ASSERT(reporter, c->gpuMemorySize() == cache->getResourceBytes());
REPORTER_ASSERT(reporter, 1 == TestResource::NumAlive());
@@ -1111,7 +1111,7 @@
SkSafeUnref(scratch);
// Get rid of c.
- cache->purgeAllUnlocked();
+ cache->purgeUnlockedResources();
scratch = cache->findAndRefScratchResource(scratchKey);
REPORTER_ASSERT(reporter, 0 == TestResource::NumAlive());
REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
@@ -1146,14 +1146,14 @@
REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive());
- cache->purgeAllUnlocked();
+ cache->purgeUnlockedResources();
REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive());
// Break the cycle
unownedA->setUnrefWhenDestroyed(nullptr);
REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive());
- cache->purgeAllUnlocked();
+ cache->purgeUnlockedResources();
REPORTER_ASSERT(reporter, 0 == TestResource::NumAlive());
}
@@ -1267,7 +1267,7 @@
}
REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
- cache->purgeAllUnlocked();
+ cache->purgeUnlockedResources();
}
// Do a similar test but where we leave refs on some resources to prevent them from being
@@ -1301,7 +1301,37 @@
REPORTER_ASSERT(reporter, cnt / 2 - i - 1 == cache->getResourceCount());
}
- cache->purgeAllUnlocked();
+ cache->purgeUnlockedResources();
+ }
+
+ REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
+
+ // Do a similar test where we alternate adding scratch and uniquely keyed resources, but
+ // then purge old scratch resources.
+ {
+ for (int i = 0; i < cnt; ++i) {
+ const bool isScratch = (i % 2 == 0);
+ const SkBudgeted budgeted = SkBudgeted::kYes;
+ const TestResource::SimulatedProperty property = TestResource::kA_SimulatedProperty;
+ TestResource* r = isScratch ? TestResource::CreateScratch(gpu, budgeted, property)
+ : new TestResource(gpu, budgeted, property);
+ if (!isScratch) {
+ GrUniqueKey k;
+ make_unique_key<1>(&k, i);
+ r->resourcePriv().setUniqueKey(k);
+ }
+ r->unref();
+ timeStamps.get()[i] = nowish();
+ }
+
+ for (int i = 0; i < cnt; ++i) {
+ // Should get a resource purged every other frame, since the uniquely keyed
+ // resources will not be considered.
+ cache->purgeResourcesNotUsedSince(timeStamps[i], /*scratchResourcesOnly=*/true);
+ REPORTER_ASSERT(reporter, cnt - i / 2 - 1 == cache->getResourceCount());
+ }
+ // Unref remaining resources
+ cache->purgeResourcesNotUsedSince(nowish());
}
REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
@@ -1421,7 +1451,7 @@
}
// ensure all are purged before the next
- dContext->priv().testingOnly_purgeAllUnlockedResources();
+ dContext->priv().getResourceCache()->purgeUnlockedResources();
REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceCount());
REPORTER_ASSERT(reporter, 0 == cache->getPurgeableBytes());
diff --git a/tests/TextureProxyTest.cpp b/tests/TextureProxyTest.cpp
index ab92f82..b4d5165 100644
--- a/tests/TextureProxyTest.cpp
+++ b/tests/TextureProxyTest.cpp
@@ -188,7 +188,7 @@
// Mega-purging it should remove it from both the hash and the cache
proxy = nullptr;
- cache->purgeAllUnlocked();
+ cache->purgeUnlockedResources();
if (!expectResourceToOutliveProxy) {
expectedCacheCount -= cacheEntriesPerProxy;
}
@@ -276,7 +276,7 @@
}
#endif
- dContext->priv().testingOnly_purgeAllUnlockedResources();
+ dContext->priv().getResourceCache()->purgeUnlockedResources();
REPORTER_ASSERT(reporter, 0 == proxyProvider->numUniqueKeyProxies_TestOnly());
REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
@@ -319,7 +319,7 @@
REPORTER_ASSERT(reporter, cacheEntriesPerProxy == cache->getResourceCount());
proxy = nullptr;
- dContext->priv().testingOnly_purgeAllUnlockedResources();
+ dContext->priv().getResourceCache()->purgeUnlockedResources();
REPORTER_ASSERT(reporter, 0 == proxyProvider->numUniqueKeyProxies_TestOnly());
REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
@@ -352,7 +352,7 @@
}
REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
- cache->purgeAllUnlocked();
+ cache->purgeUnlockedResources();
}
basic_test(direct, reporter, create_wrapped_backend(direct), cacheEntriesPerProxy);