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>
This commit is contained in:
Michael Ludwig 2021-06-09 20:49:48 -04:00 committed by Skia Commit-Bot
parent 962dec40cb
commit 9d1cc05100
12 changed files with 118 additions and 69 deletions

View File

@ -79,7 +79,7 @@ protected:
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 @@ protected:
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();

View File

@ -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 @@ void set_cache_budget(SkCanvas* canvas, int approxImagesInBudget) {
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 @@ void set_cache_budget(SkCanvas* canvas, int approxImagesInBudget) {
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();
}
//////////////////////////////////////////////////////////////////////////////

View File

@ -253,8 +253,18 @@ public:
/**
* 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) {

View File

@ -193,7 +193,7 @@ void GrDirectContext::freeGpuResources() {
this->drawingManager()->freeGpuResources();
fResourceCache->purgeAllUnlocked();
fResourceCache->purgeUnlockedResources();
}
bool GrDirectContext::init() {
@ -317,7 +317,8 @@ void GrDirectContext::purgeUnlockedResources(bool scratchResourcesOnly) {
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 @@ void GrDirectContext::performDeferredCleanup(std::chrono::milliseconds msNotUsed
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

View File

@ -185,10 +185,6 @@ sk_sp<SkImage> GrDirectContextPriv::testingOnly_getFontAtlasImage(GrMaskFormat f
SkColorInfo(colorType, kPremul_SkAlphaType, nullptr));
}
void GrDirectContextPriv::testingOnly_purgeAllUnlockedResources() {
fContext->fResourceCache->purgeAllUnlocked();
}
void GrDirectContextPriv::testingOnly_flushAndRemoveOnFlushCallbackObject(
GrOnFlushCallbackObject* cb) {
fContext->flushAndSubmit();

View File

@ -187,13 +187,6 @@ public:
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

View File

@ -564,19 +564,43 @@ void GrResourceCache::purgeAsNeeded() {
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 @@ void GrResourceCache::purgeUnlockedResources(bool scratchResourcesOnly) {
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 @@ void GrResourceCache::purgeUnlockedResources(bool scratchResourcesOnly) {
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) {

View File

@ -154,16 +154,20 @@ public:
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 @@ private:
uint32_t getNextTimestamp();
void purgeUnlockedResources(const GrStdSteadyClock::time_point* purgeTime,
bool scratchResourcesOnly);
#ifdef SK_DEBUG
bool isInCache(const GrGpuResource* r) const;
void validate() const;

View File

@ -281,7 +281,7 @@ DEF_GPUTEST(InitialTextureClear, reporter, baseOptions) {
}
}
dContext->priv().testingOnly_purgeAllUnlockedResources();
dContext->priv().getResourceCache()->purgeUnlockedResources();
}
// Try creating the texture as a deferred proxy.
@ -311,7 +311,7 @@ DEF_GPUTEST(InitialTextureClear, reporter, baseOptions) {
}
}
}
dContext->priv().testingOnly_purgeAllUnlockedResources();
dContext->priv().getResourceCache()->purgeUnlockedResources();
}
}
}

View File

@ -124,7 +124,7 @@ static void test_path(skiatest::Reporter* reporter,
}
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);

View File

@ -331,7 +331,7 @@ public:
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 @@ static void test_no_key(skiatest::Reporter* reporter) {
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 @@ static void test_budgeting(skiatest::Reporter* reporter) {
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 @@ static void test_budgeting(skiatest::Reporter* reporter) {
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 @@ static void test_budgeting(skiatest::Reporter* reporter) {
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 @@ static void test_unbudgeted(skiatest::Reporter* reporter) {
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 @@ static void test_duplicate_scratch_key(skiatest::Reporter* reporter) {
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 @@ static void test_duplicate_scratch_key(skiatest::Reporter* reporter) {
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 @@ static void test_duplicate_unique_key(skiatest::Reporter* reporter) {
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 @@ static void test_purge_invalidated(skiatest::Reporter* reporter) {
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 @@ static void test_cache_chained_purge(skiatest::Reporter* reporter) {
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 @@ static void test_time_purge(skiatest::Reporter* reporter) {
}
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 @@ static void test_time_purge(skiatest::Reporter* reporter) {
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 @@ static void test_partial_purge(skiatest::Reporter* reporter) {
}
// 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());

View File

@ -188,7 +188,7 @@ static void basic_test(GrDirectContext* dContext,
// 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 @@ static void invalidation_test(GrDirectContext* dContext,
}
#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 @@ static void invalidation_and_instantiation_test(GrDirectContext* dContext,
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 @@ DEF_GPUTEST_FOR_RENDERING_CONTEXTS(TextureProxyTest, reporter, ctxInfo) {
}
REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
cache->purgeAllUnlocked();
cache->purgeUnlockedResources();
}
basic_test(direct, reporter, create_wrapped_backend(direct), cacheEntriesPerProxy);