aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar Derek Sollenberger <djsollen@google.com>2017-05-25 16:43:59 -0400
committerGravatar Skia Commit-Bot <skia-commit-bot@chromium.org>2017-05-26 18:14:33 +0000
commit5480a18d8799511034d0da219c72932cd8f25274 (patch)
tree0be82d1f2275f2df75a5bf77eb330fadf677166b
parentdc8c34455849574e4e0260ea35485f093723c10d (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.h12
-rw-r--r--src/core/SkTDPQueue.h14
-rw-r--r--src/gpu/GrContext.cpp5
-rw-r--r--src/gpu/GrResourceCache.cpp41
-rw-r--r--src/gpu/GrResourceCache.h12
-rw-r--r--tests/ResourceCacheTest.cpp109
-rw-r--r--tests/TDPQueueTest.cpp49
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);
}