diff options
author | Derek Sollenberger <djsollen@google.com> | 2017-05-25 16:43:59 -0400 |
---|---|---|
committer | Skia Commit-Bot <skia-commit-bot@chromium.org> | 2017-05-26 18:14:33 +0000 |
commit | 5480a18d8799511034d0da219c72932cd8f25274 (patch) | |
tree | 0be82d1f2275f2df75a5bf77eb330fadf677166b | |
parent | dc8c34455849574e4e0260ea35485f093723c10d (diff) |
Add method to GrContext to purge unlocked resources.
Beyond setting the total cache limits this method enables clients to
request to purge a specific number of bytes, as well as specify their
preference to purge scratch resources over resources of other types.
Change-Id: I9259d5544d34251575d77eebe599388f213ff3ce
Reviewed-on: https://skia-review.googlesource.com/17987
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Derek Sollenberger <djsollen@google.com>
-rw-r--r-- | include/gpu/GrContext.h | 12 | ||||
-rw-r--r-- | src/core/SkTDPQueue.h | 14 | ||||
-rw-r--r-- | src/gpu/GrContext.cpp | 5 | ||||
-rw-r--r-- | src/gpu/GrResourceCache.cpp | 41 | ||||
-rw-r--r-- | src/gpu/GrResourceCache.h | 12 | ||||
-rw-r--r-- | tests/ResourceCacheTest.cpp | 109 | ||||
-rw-r--r-- | tests/TDPQueueTest.cpp | 49 |
7 files changed, 242 insertions, 0 deletions
diff --git a/include/gpu/GrContext.h b/include/gpu/GrContext.h index 5bd9574678..0f3d925113 100644 --- a/include/gpu/GrContext.h +++ b/include/gpu/GrContext.h @@ -172,6 +172,18 @@ public: */ void purgeResourcesNotUsedInMs(std::chrono::milliseconds ms); + /** + * Purge unlocked resources from the cache until the the provided byte count has been reached + * or we have purged all unlocked resources. The default policy is to purge in LRU order, but + * can be overridden to prefer purging scratch resources (in LRU order) prior to purging other + * resource types. + * + * @param maxBytesToPurge the desired number of bytes to be purged. + * @param preferScratchResources If true scratch resources will be purged prior to other + * resource types. + */ + void purgeUnlockedResources(size_t bytesToPurge, bool preferScratchResources); + /** Access the context capabilities */ const GrCaps* caps() const { return fCaps; } diff --git a/src/core/SkTDPQueue.h b/src/core/SkTDPQueue.h index b5c9923c81..5f6fd3bc73 100644 --- a/src/core/SkTDPQueue.h +++ b/src/core/SkTDPQueue.h @@ -9,6 +9,7 @@ #define SkTDPQueue_DEFINED #include "SkTDArray.h" +#include "SkTSort.h" /** * This class implements a priority queue. T is the type of the elements in the queue. LESS is a @@ -102,6 +103,19 @@ public: to peek(). Otherwise, there is no guarantee about ordering of elements in the queue. */ T at(int i) const { return fArray[i]; } + /** Sorts the queue into priority order. The queue is only guarenteed to remain in sorted order + * until any other operation, other than at(), is performed. + */ + void sort() { + if (fArray.count() > 1) { + SkTQSort<T>(fArray.begin(), fArray.end() - 1, LESS); + for (int i = 0; i < fArray.count(); i++) { + this->setIndex(i); + } + this->validate(); + } + } + private: static int LeftOf(int x) { SkASSERT(x >= 0); return 2 * x + 1; } static int ParentOf(int x) { SkASSERT(x > 0); return (x - 1) >> 1; } diff --git a/src/gpu/GrContext.cpp b/src/gpu/GrContext.cpp index e7aff3586a..e78dc6ab12 100644 --- a/src/gpu/GrContext.cpp +++ b/src/gpu/GrContext.cpp @@ -204,6 +204,11 @@ void GrContext::purgeResourcesNotUsedInMs(std::chrono::milliseconds ms) { fResourceCache->purgeResourcesNotUsedSince(GrStdSteadyClock::now() - ms); } +void GrContext::purgeUnlockedResources(size_t bytesToPurge, bool preferScratchResources) { + ASSERT_SINGLE_OWNER + fResourceCache->purgeUnlockedResources(bytesToPurge, preferScratchResources); +} + void GrContext::getResourceCacheUsage(int* resourceCount, size_t* resourceBytes) const { ASSERT_SINGLE_OWNER diff --git a/src/gpu/GrResourceCache.cpp b/src/gpu/GrResourceCache.cpp index 53a62c05c0..ef703e2474 100644 --- a/src/gpu/GrResourceCache.cpp +++ b/src/gpu/GrResourceCache.cpp @@ -536,6 +536,47 @@ void GrResourceCache::purgeResourcesNotUsedSince(GrStdSteadyClock::time_point pu } } +void GrResourceCache::purgeUnlockedResources(size_t bytesToPurge, bool preferScratchResources) { + + const size_t tmpByteBudget = SkTMax((size_t)0, fBytes - bytesToPurge); + bool stillOverbudget = tmpByteBudget < fBytes; + + if (preferScratchResources && bytesToPurge < fPurgeableBytes) { + // Sort the queue + fPurgeableQueue.sort(); + + // Make a list of the scratch resources to delete + SkTDArray<GrGpuResource*> scratchResources; + size_t scratchByteCount = 0; + for (int i = 0; i < fPurgeableQueue.count() && stillOverbudget; i++) { + GrGpuResource* resource = fPurgeableQueue.at(i); + SkASSERT(resource->isPurgeable()); + if (!resource->getUniqueKey().isValid()) { + *scratchResources.append() = resource; + scratchByteCount += resource->gpuMemorySize(); + stillOverbudget = tmpByteBudget < fBytes - scratchByteCount; + } + } + + // Delete the scratch resources. This must be done as a separate pass + // to avoid messing up the sorted order of the queue + for (int i = 0; i < scratchResources.count(); i++) { + scratchResources.getAt(i)->cacheAccess().release(); + } + stillOverbudget = tmpByteBudget < fBytes; + + this->validate(); + } + + // Purge any remaining resources in LRU order + if (stillOverbudget) { + const size_t cachedByteCount = fMaxBytes; + fMaxBytes = tmpByteBudget; + this->purgeAsNeeded(); + fMaxBytes = cachedByteCount; + } +} + void GrResourceCache::processInvalidUniqueKeys( const SkTArray<GrUniqueKeyInvalidatedMessage>& msgs) { for (int i = 0; i < msgs.count(); ++i) { diff --git a/src/gpu/GrResourceCache.h b/src/gpu/GrResourceCache.h index 6362c37e09..f851ab1592 100644 --- a/src/gpu/GrResourceCache.h +++ b/src/gpu/GrResourceCache.h @@ -173,6 +173,18 @@ public: /** Purge all resources not used since the passed in time. */ void purgeResourcesNotUsedSince(GrStdSteadyClock::time_point); + /** + * Purge unlocked resources from the cache until the the provided byte count has been reached + * or we have purged all unlocked resources. The default policy is to purge in LRU order, but + * can be overridden to prefer purging scratch resources (in LRU order) prior to purging other + * resource types. + * + * @param maxBytesToPurge the desired number of bytes to be purged. + * @param preferScratchResources If true scratch resources will be purged prior to other + * resource types. + */ + void purgeUnlockedResources(size_t bytesToPurge, bool preferScratchResources); + /** Returns true if the cache would like a flush to occur in order to make more resources purgeable. */ bool requestsFlush() const { return fRequestFlush; } diff --git a/tests/ResourceCacheTest.cpp b/tests/ResourceCacheTest.cpp index 3a63521803..08f60ea1c5 100644 --- a/tests/ResourceCacheTest.cpp +++ b/tests/ResourceCacheTest.cpp @@ -1360,6 +1360,114 @@ static void test_time_purge(skiatest::Reporter* reporter) { } } +static void test_partial_purge(skiatest::Reporter* reporter) { + Mock mock(6, 100); + GrContext* context = mock.context(); + GrResourceCache* cache = mock.cache(); + + enum TestsCase { + kOnlyScratch_TestCase = 0, + kPartialScratch_TestCase = 1, + kAllScratch_TestCase = 2, + kPartial_TestCase = 3, + kAll_TestCase = 4, + kNone_TestCase = 5, + kEndTests_TestCase = kNone_TestCase + 1 + }; + + for (int testCase = 0; testCase < kEndTests_TestCase; testCase++) { + + GrUniqueKey key1, key2, key3; + make_unique_key<0>(&key1, 1); + make_unique_key<0>(&key2, 2); + make_unique_key<0>(&key3, 3); + + // Add three unique resources to the cache. + TestResource *unique1 = new TestResource(context->getGpu()); + TestResource *unique2 = new TestResource(context->getGpu()); + TestResource *unique3 = new TestResource(context->getGpu()); + + unique1->resourcePriv().setUniqueKey(key1); + unique2->resourcePriv().setUniqueKey(key2); + unique3->resourcePriv().setUniqueKey(key3); + + unique1->setSize(10); + unique2->setSize(11); + unique3->setSize(12); + + // Add two scratch resources to the cache. + TestResource *scratch1 = TestResource::CreateScratch(context->getGpu(), SkBudgeted::kYes, + TestResource::kA_SimulatedProperty); + TestResource *scratch2 = TestResource::CreateScratch(context->getGpu(), SkBudgeted::kYes, + TestResource::kB_SimulatedProperty); + scratch1->setSize(13); + scratch2->setSize(14); + + + REPORTER_ASSERT(reporter, 5 == cache->getBudgetedResourceCount()); + REPORTER_ASSERT(reporter, 60 == cache->getBudgetedResourceBytes()); + REPORTER_ASSERT(reporter, 0 == cache->getPurgeableBytes()); + + // Add resources to the purgeable queue + unique1->unref(); + scratch1->unref(); + unique2->unref(); + scratch2->unref(); + unique3->unref(); + + REPORTER_ASSERT(reporter, 5 == cache->getBudgetedResourceCount()); + REPORTER_ASSERT(reporter, 60 == cache->getBudgetedResourceBytes()); + REPORTER_ASSERT(reporter, 60 == cache->getPurgeableBytes()); + + switch(testCase) { + case kOnlyScratch_TestCase: { + context->purgeUnlockedResources(14, true); + REPORTER_ASSERT(reporter, 3 == cache->getBudgetedResourceCount()); + REPORTER_ASSERT(reporter, 33 == cache->getBudgetedResourceBytes()); + break; + } + case kPartialScratch_TestCase: { + context->purgeUnlockedResources(3, true); + REPORTER_ASSERT(reporter, 4 == cache->getBudgetedResourceCount()); + REPORTER_ASSERT(reporter, 47 == cache->getBudgetedResourceBytes()); + break; + } + case kAllScratch_TestCase: { + context->purgeUnlockedResources(50, true); + REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceCount()); + REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceBytes()); + break; + } + case kPartial_TestCase: { + context->purgeUnlockedResources(13, false); + REPORTER_ASSERT(reporter, 3 == cache->getBudgetedResourceCount()); + REPORTER_ASSERT(reporter, 37 == cache->getBudgetedResourceBytes()); + break; + } + case kAll_TestCase: { + context->purgeUnlockedResources(50, false); + REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceCount()); + REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceBytes()); + break; + } + case kNone_TestCase: { + context->purgeUnlockedResources(0, true); + context->purgeUnlockedResources(0, false); + REPORTER_ASSERT(reporter, 5 == cache->getBudgetedResourceCount()); + REPORTER_ASSERT(reporter, 60 == cache->getBudgetedResourceBytes()); + REPORTER_ASSERT(reporter, 60 == cache->getPurgeableBytes()); + break; + } + }; + + // ensure all are purged before the next + context->purgeAllUnlockedResources(); + REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceCount()); + REPORTER_ASSERT(reporter, 0 == cache->getPurgeableBytes()); + + } +} + static void test_large_resource_count(skiatest::Reporter* reporter) { // Set the cache size to double the resource count because we're going to create 2x that number // resources, using two different key domains. Add a little slop to the bytes because we resize @@ -1515,6 +1623,7 @@ DEF_GPUTEST(ResourceCacheMisc, reporter, factory) { test_timestamp_wrap(reporter); test_flush(reporter); test_time_purge(reporter); + test_partial_purge(reporter); test_large_resource_count(reporter); test_custom_data(reporter); test_abandoned(reporter); diff --git a/tests/TDPQueueTest.cpp b/tests/TDPQueueTest.cpp index 70367a7b82..75eab25a4b 100644 --- a/tests/TDPQueueTest.cpp +++ b/tests/TDPQueueTest.cpp @@ -150,7 +150,56 @@ void random_test(skiatest::Reporter* reporter) { } } +void sort_test(skiatest::Reporter* reporter) { + SkRandom random; + + SkTDPQueue<Dummy *, Dummy::LessP, Dummy::PQIndex> pqTest; + SkTDPQueue<Dummy *, Dummy::LessP, Dummy::PQIndex> pqControl; + + // Create a random set of Dummy objects and populate the test queue. + int count = random.nextULessThan(100); + SkTDArray<Dummy> testArray; + testArray.setReserve(count); + for (int i = 0; i < count; i++) { + Dummy *dummy = testArray.append(); + dummy->fPriority = random.nextS(); + dummy->fValue = random.nextS(); + dummy->fIndex = -1; + pqTest.insert(&testArray[i]); + } + + // Stick equivalent dummy objects into the control queue. + SkTDArray<Dummy> controlArray; + controlArray.setReserve(count); + for (int i = 0; i < count; i++) { + Dummy *dummy = controlArray.append(); + dummy->fPriority = testArray[i].fPriority; + dummy->fValue = testArray[i].fValue; + dummy->fIndex = -1; + pqControl.insert(&controlArray[i]); + } + + // Sort the queue + pqTest.sort(); + + // Compare elements in the queue to ensure they are in sorted order + int prevPriority = pqTest.peek()->fPriority; + for (int i = 0; i < count; i++) { + REPORTER_ASSERT(reporter, i <= pqTest.at(i)->fIndex); + REPORTER_ASSERT(reporter, prevPriority <= pqTest.at(i)->fPriority); + prevPriority = pqTest.at(i)->fPriority; + } + + // Verify that after sorting the queue still produces the same result as the control queue + for (int i = 0; i < count; i++) { + REPORTER_ASSERT(reporter, *pqControl.peek() == *pqTest.peek()); + pqControl.pop(); + pqTest.pop(); + } +} + DEF_TEST(TDPQueueTest, reporter) { simple_test(reporter); random_test(reporter); + sort_test(reporter); } |