Allow GPU resources to not be counted against the cache budget.
BUG=skia:2889 Review URL: https://codereview.chromium.org/721353002
This commit is contained in:
parent
bd27e514e2
commit
84c8e62fad
@ -191,7 +191,7 @@ protected:
|
||||
backend API calls should be made. */
|
||||
virtual void onAbandon() { }
|
||||
|
||||
bool isWrapped() const { return kWrapped_FlagBit & fFlags; }
|
||||
bool isWrapped() const { return SkToBool(kWrapped_Flag & fFlags); }
|
||||
|
||||
/**
|
||||
* This entry point should be called whenever gpuMemorySize() should report a different size.
|
||||
@ -221,7 +221,7 @@ private:
|
||||
|
||||
// See comments in CacheAccess.
|
||||
bool setContentKey(const GrResourceKey& contentKey);
|
||||
|
||||
void setBudgeted(bool countsAgainstBudget);
|
||||
void notifyIsPurgable() const;
|
||||
|
||||
#ifdef SK_DEBUG
|
||||
@ -233,31 +233,39 @@ private:
|
||||
// We're in an internal doubly linked list owned by GrResourceCache2
|
||||
SK_DECLARE_INTERNAL_LLIST_INTERFACE(GrGpuResource);
|
||||
|
||||
// This is not ref'ed but abandon() or release() will be called before the GrGpu object
|
||||
// is destroyed. Those calls set will this to NULL.
|
||||
GrGpu* fGpu;
|
||||
|
||||
enum Flags {
|
||||
/**
|
||||
* This object wraps a GPU object given to us by the user.
|
||||
* Lifetime management is left up to the user (i.e., we will not
|
||||
* free it).
|
||||
*/
|
||||
kWrapped_FlagBit = 0x1,
|
||||
};
|
||||
|
||||
static const size_t kInvalidGpuMemorySize = ~static_cast<size_t>(0);
|
||||
enum Flags {
|
||||
/**
|
||||
* The resource counts against the resource cache's budget.
|
||||
*/
|
||||
kBudgeted_Flag = 0x1,
|
||||
|
||||
uint32_t fFlags;
|
||||
/**
|
||||
* This object wraps a GPU object given to us by Skia's client. Skia will not free the
|
||||
* underlying backend API GPU resources when the GrGpuResource is destroyed. This also
|
||||
* implies that kBudgeted_Flag is not set.
|
||||
*/
|
||||
kWrapped_Flag = 0x2,
|
||||
|
||||
mutable size_t fGpuMemorySize;
|
||||
const uint32_t fUniqueID;
|
||||
/**
|
||||
* If set then fContentKey is valid and the resource is cached based on its content.
|
||||
*/
|
||||
kContentKeySet_Flag = 0x4,
|
||||
};
|
||||
|
||||
// TODO(bsalomon): Remove GrResourceKey and use different simpler types for content and scratch
|
||||
// keys.
|
||||
GrResourceKey fScratchKey;
|
||||
GrResourceKey fContentKey;
|
||||
bool fContentKeySet;
|
||||
|
||||
// This is not ref'ed but abandon() or release() will be called before the GrGpu object
|
||||
// is destroyed. Those calls set will this to NULL.
|
||||
GrGpu* fGpu;
|
||||
mutable size_t fGpuMemorySize;
|
||||
|
||||
uint32_t fFlags;
|
||||
const uint32_t fUniqueID;
|
||||
|
||||
typedef GrIORef<GrGpuResource> INHERITED;
|
||||
friend class GrIORef<GrGpuResource>; // to access notifyIsPurgable.
|
||||
|
@ -490,7 +490,13 @@ GrTexture* GrContext::createUncachedTexture(const GrSurfaceDesc& descIn,
|
||||
void* srcData,
|
||||
size_t rowBytes) {
|
||||
GrSurfaceDesc descCopy = descIn;
|
||||
return fGpu->createTexture(descCopy, srcData, rowBytes);
|
||||
GrTexture* texture = fGpu->createTexture(descCopy, srcData, rowBytes);
|
||||
if (texture) {
|
||||
// TODO: It'd be nice to be able to do this before creation so we don't boot something
|
||||
// out of the cache. We could temporarily boost the cache budget.
|
||||
texture->cacheAccess().setBudgeted(false);
|
||||
}
|
||||
return texture;
|
||||
}
|
||||
|
||||
void GrContext::getResourceCacheLimits(int* maxTextures, size_t* maxTextureBytes) const {
|
||||
|
@ -19,15 +19,15 @@ static inline GrResourceCache2* get_resource_cache2(GrGpu* gpu) {
|
||||
}
|
||||
|
||||
GrGpuResource::GrGpuResource(GrGpu* gpu, bool isWrapped)
|
||||
: fGpu(gpu)
|
||||
: fScratchKey(GrResourceKey::NullScratchKey())
|
||||
, fGpu(gpu)
|
||||
, fGpuMemorySize(kInvalidGpuMemorySize)
|
||||
, fUniqueID(CreateUniqueID())
|
||||
, fScratchKey(GrResourceKey::NullScratchKey())
|
||||
, fContentKeySet(false) {
|
||||
, fUniqueID(CreateUniqueID()) {
|
||||
if (isWrapped) {
|
||||
fFlags = kWrapped_FlagBit;
|
||||
fFlags = kWrapped_Flag;
|
||||
} else {
|
||||
fFlags = 0;
|
||||
// By default all non-wrapped resources are budgeted.
|
||||
fFlags = kBudgeted_Flag;
|
||||
}
|
||||
}
|
||||
|
||||
@ -92,16 +92,16 @@ bool GrGpuResource::setContentKey(const GrResourceKey& contentKey) {
|
||||
if (this->isWrapped()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fContentKeySet || this->wasDestroyed()) {
|
||||
|
||||
if ((fFlags & kContentKeySet_Flag) || this->wasDestroyed()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
fContentKey = contentKey;
|
||||
fContentKeySet = true;
|
||||
fFlags |= kContentKeySet_Flag;
|
||||
|
||||
if (!get_resource_cache2(fGpu)->resourceAccess().didSetContentKey(this)) {
|
||||
fContentKeySet = false;
|
||||
fFlags &= ~kContentKeySet_Flag;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@ -136,3 +136,21 @@ uint32_t GrGpuResource::CreateUniqueID() {
|
||||
} while (id == SK_InvalidUniqueID);
|
||||
return id;
|
||||
}
|
||||
|
||||
void GrGpuResource::setBudgeted(bool countsAgainstBudget) {
|
||||
// Wrapped resources never count against the budget, nothing to do. No point in changing the
|
||||
// budgeting of destroyed resources.
|
||||
if (this->isWrapped() || this->wasDestroyed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t oldFlags = fFlags;
|
||||
if (countsAgainstBudget) {
|
||||
fFlags |= kBudgeted_Flag;
|
||||
} else {
|
||||
fFlags &= ~kBudgeted_Flag;
|
||||
}
|
||||
if (fFlags != oldFlags) {
|
||||
get_resource_cache2(fGpu)->resourceAccess().didChangeBudgetStatus(this);
|
||||
}
|
||||
}
|
||||
|
@ -28,6 +28,11 @@ public:
|
||||
return fResource->setContentKey(contentKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes whether the resource counts against the resource cache budget.
|
||||
*/
|
||||
void setBudgeted(bool countsAgainstBudget) { fResource->setBudgeted(countsAgainstBudget); }
|
||||
|
||||
/**
|
||||
* Is the resource currently cached as scratch? This means it has a valid scratch key and does
|
||||
* not have a content key.
|
||||
@ -48,14 +53,26 @@ public:
|
||||
* If the resource is currently cached by a content key, the key is returned, otherwise NULL.
|
||||
*/
|
||||
const GrResourceKey* getContentKey() const {
|
||||
if (fResource->fContentKeySet) {
|
||||
if (fResource->fFlags & GrGpuResource::kContentKeySet_Flag) {
|
||||
return &fResource->fContentKey;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the resource object wrapping an externally allocated GPU resource?
|
||||
*/
|
||||
bool isWrapped() const { return fResource->isWrapped(); }
|
||||
|
||||
/**
|
||||
* Does the resource count against the resource budget?
|
||||
*/
|
||||
bool isBudgeted() const {
|
||||
bool ret = SkToBool(GrGpuResource::kBudgeted_Flag & fResource->fFlags);
|
||||
SkASSERT(!(ret && fResource->isWrapped()));
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the cache to delete the resource under normal circumstances.
|
||||
*/
|
||||
|
@ -100,12 +100,12 @@ void GrResourceCache2::insertResource(GrGpuResource* resource) {
|
||||
|
||||
size_t size = resource->gpuMemorySize();
|
||||
++fCount;
|
||||
fBytes += resource->gpuMemorySize();
|
||||
fBytes += size;
|
||||
#if GR_CACHE_STATS
|
||||
fHighWaterCount = SkTMax(fCount, fHighWaterCount);
|
||||
fHighWaterBytes = SkTMax(fBytes, fHighWaterBytes);
|
||||
#endif
|
||||
if (!resource->cacheAccess().isWrapped()) {
|
||||
if (resource->cacheAccess().isBudgeted()) {
|
||||
++fBudgetedCount;
|
||||
fBudgetedBytes += size;
|
||||
#if GR_CACHE_STATS
|
||||
@ -114,8 +114,7 @@ void GrResourceCache2::insertResource(GrGpuResource* resource) {
|
||||
#endif
|
||||
}
|
||||
if (!resource->cacheAccess().getScratchKey().isNullScratch()) {
|
||||
// TODO(bsalomon): Make this assertion possible.
|
||||
// SkASSERT(!resource->isWrapped());
|
||||
SkASSERT(!resource->cacheAccess().isWrapped());
|
||||
fScratchMap.insert(resource->cacheAccess().getScratchKey(), resource);
|
||||
}
|
||||
|
||||
@ -130,7 +129,7 @@ void GrResourceCache2::removeResource(GrGpuResource* resource) {
|
||||
size_t size = resource->gpuMemorySize();
|
||||
--fCount;
|
||||
fBytes -= size;
|
||||
if (!resource->cacheAccess().isWrapped()) {
|
||||
if (resource->cacheAccess().isBudgeted()) {
|
||||
--fBudgetedCount;
|
||||
fBudgetedBytes -= size;
|
||||
}
|
||||
@ -187,7 +186,6 @@ public:
|
||||
if (resource->internalHasRef() || !resource->cacheAccess().isScratch()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return !fRejectPendingIO || !resource->internalHasPendingIO();
|
||||
}
|
||||
|
||||
@ -293,7 +291,7 @@ void GrResourceCache2::didChangeGpuMemorySize(const GrGpuResource* resource, siz
|
||||
#if GR_CACHE_STATS
|
||||
fHighWaterBytes = SkTMax(fBytes, fHighWaterBytes);
|
||||
#endif
|
||||
if (!resource->cacheAccess().isWrapped()) {
|
||||
if (resource->cacheAccess().isBudgeted()) {
|
||||
fBudgetedBytes += delta;
|
||||
#if GR_CACHE_STATS
|
||||
fBudgetedHighWaterBytes = SkTMax(fBudgetedBytes, fBudgetedHighWaterBytes);
|
||||
@ -304,6 +302,26 @@ void GrResourceCache2::didChangeGpuMemorySize(const GrGpuResource* resource, siz
|
||||
this->validate();
|
||||
}
|
||||
|
||||
void GrResourceCache2::didChangeBudgetStatus(GrGpuResource* resource) {
|
||||
SkASSERT(!fPurging);
|
||||
SkASSERT(resource);
|
||||
SkASSERT(this->isInCache(resource));
|
||||
|
||||
size_t size = resource->gpuMemorySize();
|
||||
|
||||
if (resource->cacheAccess().isBudgeted()) {
|
||||
++fBudgetedCount;
|
||||
fBudgetedBytes += size;
|
||||
this->purgeAsNeeded();
|
||||
} else {
|
||||
--fBudgetedCount;
|
||||
fBudgetedBytes -= size;
|
||||
}
|
||||
|
||||
this->validate();
|
||||
}
|
||||
|
||||
|
||||
void GrResourceCache2::internalPurgeAsNeeded() {
|
||||
SkASSERT(!fPurging);
|
||||
SkASSERT(!fNewlyPurgableResourceWhilePurging);
|
||||
@ -410,7 +428,7 @@ void GrResourceCache2::validate() const {
|
||||
SkASSERT(!resource->cacheAccess().isWrapped());
|
||||
}
|
||||
|
||||
if (!resource->cacheAccess().isWrapped()) {
|
||||
if (resource->cacheAccess().isBudgeted()) {
|
||||
++budgetedCount;
|
||||
budgetedBytes += resource->gpuMemorySize();
|
||||
}
|
||||
@ -446,6 +464,8 @@ void GrResourceCache2::printStats() const {
|
||||
|
||||
int locked = 0;
|
||||
int scratch = 0;
|
||||
int wrapped = 0;
|
||||
size_t unbudgetedSize = 0;
|
||||
|
||||
ResourceList::Iter iter;
|
||||
GrGpuResource* resource = iter.init(fResources, ResourceList::Iter::kHead_IterStart);
|
||||
@ -457,17 +477,23 @@ void GrResourceCache2::printStats() const {
|
||||
if (resource->cacheAccess().isScratch()) {
|
||||
++scratch;
|
||||
}
|
||||
if (resource->cacheAccess().isWrapped()) {
|
||||
++wrapped;
|
||||
}
|
||||
if (!resource->cacheAccess().isBudgeted()) {
|
||||
unbudgetedSize += resource->gpuMemorySize();
|
||||
}
|
||||
}
|
||||
|
||||
float countUtilization = (100.f * fBudgetedCount) / fMaxCount;
|
||||
float byteUtilization = (100.f * fBudgetedBytes) / fMaxBytes;
|
||||
|
||||
SkDebugf("Budget: %d items %d bytes\n", fMaxCount, fMaxBytes);
|
||||
SkDebugf(
|
||||
"\t\tEntry Count: current %d (%d budgeted, %d locked, %d scratch %.2g%% full), high %d\n",
|
||||
fCount, fBudgetedCount, locked, scratch, countUtilization, fHighWaterCount);
|
||||
SkDebugf("\t\tEntry Bytes: current %d (budgeted %d, %.2g%% full) high %d\n",
|
||||
fBytes, fBudgetedBytes, byteUtilization, fHighWaterBytes);
|
||||
SkDebugf("\t\tEntry Count: current %d"
|
||||
" (%d budgeted, %d wrapped, %d locked, %d scratch %.2g%% full), high %d\n",
|
||||
fCount, fBudgetedCount, wrapped, locked, scratch, countUtilization, fHighWaterCount);
|
||||
SkDebugf("\t\tEntry Bytes: current %d (budgeted %d, %.2g%% full, %d unbudgeted) high %d\n",
|
||||
fBytes, fBudgetedBytes, byteUtilization, unbudgetedSize, fHighWaterBytes);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -22,7 +22,7 @@
|
||||
* Resources may have optionally have two types of keys:
|
||||
* 1) A scratch key. This is for resources whose allocations are cached but not their contents.
|
||||
* Multiple resources can share the same scratch key. This is so a caller can have two
|
||||
* resource instances with the same properties (e.g. multipass rendering that ping pongs
|
||||
* resource instances with the same properties (e.g. multipass rendering that ping-pongs
|
||||
* between two temporary surfaces. The scratch key is set at resource creation time and
|
||||
* should never change. Resources need not have a scratch key.
|
||||
* 2) A content key. This key represents the contents of the resource rather than just its
|
||||
@ -161,6 +161,7 @@ private:
|
||||
void notifyPurgable(GrGpuResource*);
|
||||
void didChangeGpuMemorySize(const GrGpuResource*, size_t oldSize);
|
||||
bool didSetContentKey(GrGpuResource*);
|
||||
void didChangeBudgetStatus(GrGpuResource*);
|
||||
void makeResourceMRU(GrGpuResource*);
|
||||
/// @}
|
||||
|
||||
@ -276,6 +277,12 @@ private:
|
||||
*/
|
||||
bool didSetContentKey(GrGpuResource* resource) { return fCache->didSetContentKey(resource); }
|
||||
|
||||
|
||||
/**
|
||||
* Called by GrGpuResources when they change from budgeted to unbudgeted or vice versa.
|
||||
*/
|
||||
void didChangeBudgetStatus(GrGpuResource* resource) { fCache->didChangeBudgetStatus(resource); }
|
||||
|
||||
// No taking addresses of this type.
|
||||
const ResourceAccess* operator&() const;
|
||||
ResourceAccess* operator&();
|
||||
|
@ -173,7 +173,7 @@ static void test_no_key(skiatest::Reporter* reporter) {
|
||||
REPORTER_ASSERT(reporter, 0 == cache2->getResourceBytes());
|
||||
}
|
||||
|
||||
static void test_wrapped(skiatest::Reporter* reporter) {
|
||||
static void test_budgeting(skiatest::Reporter* reporter) {
|
||||
SkAutoTUnref<GrContext> context(GrContext::CreateMockContext());
|
||||
REPORTER_ASSERT(reporter, SkToBool(context));
|
||||
if (NULL == context) {
|
||||
@ -199,6 +199,9 @@ static void test_wrapped(skiatest::Reporter* reporter) {
|
||||
REPORTER_ASSERT(reporter, content->cacheAccess().setContentKey(contentKey));
|
||||
TestResource* wrapped = new TestResource(context->getGpu(), true);
|
||||
scratch->setSize(12);
|
||||
TestResource* unbudgeted = new TestResource(context->getGpu());
|
||||
unbudgeted->setSize(13);
|
||||
unbudgeted->cacheAccess().setBudgeted(false);
|
||||
|
||||
// Make sure we can't add a content key to the wrapped resource
|
||||
keyData.fData8[0] = 1;
|
||||
@ -207,49 +210,60 @@ static void test_wrapped(skiatest::Reporter* reporter) {
|
||||
REPORTER_ASSERT(reporter, NULL == cache2->findAndRefContentResource(contentKey2));
|
||||
|
||||
// Make sure sizes are as we expect
|
||||
REPORTER_ASSERT(reporter, 3 == cache2->getResourceCount());
|
||||
REPORTER_ASSERT(reporter, 4 == cache2->getResourceCount());
|
||||
REPORTER_ASSERT(reporter, scratch->gpuMemorySize() + content->gpuMemorySize() +
|
||||
wrapped->gpuMemorySize() == cache2->getResourceBytes());
|
||||
wrapped->gpuMemorySize() + unbudgeted->gpuMemorySize() ==
|
||||
cache2->getResourceBytes());
|
||||
REPORTER_ASSERT(reporter, 2 == cache2->getBudgetedResourceCount());
|
||||
REPORTER_ASSERT(reporter, scratch->gpuMemorySize() + content->gpuMemorySize() ==
|
||||
cache2->getBudgetedResourceBytes());
|
||||
|
||||
// Our refs mean that the resources are non purgable.
|
||||
cache2->purgeAllUnlocked();
|
||||
REPORTER_ASSERT(reporter, 3 == cache2->getResourceCount());
|
||||
REPORTER_ASSERT(reporter, 4 == cache2->getResourceCount());
|
||||
REPORTER_ASSERT(reporter, scratch->gpuMemorySize() + content->gpuMemorySize() +
|
||||
wrapped->gpuMemorySize() == cache2->getResourceBytes());
|
||||
wrapped->gpuMemorySize() + unbudgeted->gpuMemorySize() ==
|
||||
cache2->getResourceBytes());
|
||||
REPORTER_ASSERT(reporter, 2 == cache2->getBudgetedResourceCount());
|
||||
REPORTER_ASSERT(reporter, scratch->gpuMemorySize() + content->gpuMemorySize() ==
|
||||
cache2->getBudgetedResourceBytes());
|
||||
|
||||
// Unreffing the wrapped resource should free it right away.
|
||||
wrapped->unref();
|
||||
REPORTER_ASSERT(reporter, 2 == cache2->getResourceCount());
|
||||
REPORTER_ASSERT(reporter, scratch->gpuMemorySize() + content->gpuMemorySize() ==
|
||||
cache2->getResourceBytes());
|
||||
REPORTER_ASSERT(reporter, 3 == cache2->getResourceCount());
|
||||
REPORTER_ASSERT(reporter, scratch->gpuMemorySize() + content->gpuMemorySize() +
|
||||
unbudgeted->gpuMemorySize() == cache2->getResourceBytes());
|
||||
|
||||
// Now try freeing the other two resources first
|
||||
// Now try freeing the budgeted resources first
|
||||
wrapped = new TestResource(context->getGpu(), true);
|
||||
scratch->setSize(12);
|
||||
content->unref();
|
||||
cache2->purgeAllUnlocked();
|
||||
REPORTER_ASSERT(reporter, 2 == cache2->getResourceCount());
|
||||
REPORTER_ASSERT(reporter, scratch->gpuMemorySize() + wrapped->gpuMemorySize() ==
|
||||
cache2->getResourceBytes());
|
||||
REPORTER_ASSERT(reporter, 3 == cache2->getResourceCount());
|
||||
REPORTER_ASSERT(reporter, scratch->gpuMemorySize() + wrapped->gpuMemorySize() +
|
||||
unbudgeted->gpuMemorySize() == cache2->getResourceBytes());
|
||||
REPORTER_ASSERT(reporter, 1 == cache2->getBudgetedResourceCount());
|
||||
REPORTER_ASSERT(reporter, scratch->gpuMemorySize() == cache2->getBudgetedResourceBytes());
|
||||
|
||||
scratch->unref();
|
||||
cache2->purgeAllUnlocked();
|
||||
REPORTER_ASSERT(reporter, 1 == cache2->getResourceCount());
|
||||
REPORTER_ASSERT(reporter, wrapped->gpuMemorySize() == cache2->getResourceBytes());
|
||||
REPORTER_ASSERT(reporter, 2 == cache2->getResourceCount());
|
||||
REPORTER_ASSERT(reporter, unbudgeted->gpuMemorySize() + wrapped->gpuMemorySize() ==
|
||||
cache2->getResourceBytes());
|
||||
REPORTER_ASSERT(reporter, 0 == cache2->getBudgetedResourceCount());
|
||||
REPORTER_ASSERT(reporter, 0 == cache2->getBudgetedResourceBytes());
|
||||
|
||||
wrapped->unref();
|
||||
REPORTER_ASSERT(reporter, 1 == cache2->getResourceCount());
|
||||
REPORTER_ASSERT(reporter, unbudgeted->gpuMemorySize() == cache2->getResourceBytes());
|
||||
REPORTER_ASSERT(reporter, 0 == cache2->getBudgetedResourceCount());
|
||||
REPORTER_ASSERT(reporter, 0 == cache2->getBudgetedResourceBytes());
|
||||
|
||||
unbudgeted->unref();
|
||||
REPORTER_ASSERT(reporter, 0 == cache2->getResourceCount());
|
||||
REPORTER_ASSERT(reporter, 0 == cache2->getResourceBytes());
|
||||
REPORTER_ASSERT(reporter, 0 == cache2->getBudgetedResourceCount());
|
||||
REPORTER_ASSERT(reporter, 0 == cache2->getBudgetedResourceBytes());
|
||||
}
|
||||
|
||||
static void test_duplicate_scratch_key(skiatest::Reporter* reporter) {
|
||||
@ -578,7 +592,7 @@ DEF_GPUTEST(ResourceCache, reporter, factory) {
|
||||
|
||||
// The below tests create their own mock contexts.
|
||||
test_no_key(reporter);
|
||||
test_wrapped(reporter);
|
||||
test_budgeting(reporter);
|
||||
test_duplicate_content_key(reporter);
|
||||
test_duplicate_scratch_key(reporter);
|
||||
test_purge_invalidated(reporter);
|
||||
|
Loading…
Reference in New Issue
Block a user