#include "GrGpuResource.h"
#include "GrContext.h"
#include "GrGpu.h"
+#include "GrResourceCache.h"
#include "GrResourceCache2.h"
#include "GrStencilBuffer.h"
#include "GrTexture.h"
}
static void get_texture_desc(int i, GrSurfaceDesc* desc) {
- desc->fFlags = kRenderTarget_GrSurfaceFlag | kNoStencil_GrSurfaceFlag;
+ desc->fFlags = kRenderTarget_GrSurfaceFlag |
+ kNoStencil_GrSurfaceFlag;
desc->fWidth = i % 1024;
desc->fHeight = i * 2 % 1024;
desc->fConfig = static_cast<GrPixelConfig>(i % (kLast_GrPixelConfig + 1));
- desc->fSampleCnt = ((i % 2) == 0) ? 0 : 4;
+ desc->fSampleCnt = i % 1 == 0 ? 0 : 4;
}
-static void populate_cache(GrGpu* gpu, int resourceCount) {
+static void populate_cache(GrResourceCache* cache, GrGpu* gpu, int resourceCount) {
for (int i = 0; i < resourceCount; ++i) {
int w, h, s;
get_stencil(i, &w, &h, &s);
GrResourceKey key = GrStencilBuffer::ComputeKey(w, h, s);
GrGpuResource* resource = SkNEW_ARGS(StencilResource, (gpu, i));
- resource->cacheAccess().setContentKey(key);
+ cache->purgeAsNeeded(1, resource->gpuMemorySize());
+ cache->addResource(key, resource);
resource->unref();
}
get_texture_desc(i, &desc);
GrResourceKey key = TextureResource::ComputeKey(desc);
GrGpuResource* resource = SkNEW_ARGS(TextureResource, (gpu, i));
- resource->cacheAccess().setContentKey(key);
+ cache->purgeAsNeeded(1, resource->gpuMemorySize());
+ cache->addResource(key, resource);
resource->unref();
}
}
// Set the cache budget to be very large so no purging occurs.
context->setResourceCacheLimits(2 * RESOURCE_COUNT, 1 << 30);
+ GrResourceCache* cache = context->getResourceCache();
GrResourceCache2* cache2 = context->getResourceCache2();
// Make sure the cache is empty.
- cache2->purgeAllUnlocked();
- SkASSERT(0 == cache2->getResourceCount() && 0 == cache2->getResourceBytes());
+ cache->purgeAllUnlocked();
+ SkASSERT(0 == cache->getCachedResourceCount() && 0 == cache->getCachedResourceBytes());
GrGpu* gpu = context->getGpu();
for (int i = 0; i < loops; ++i) {
- SkASSERT(0 == cache2->getResourceCount() && 0 == cache2->getResourceBytes());
+ SkASSERT(0 == cache->getCachedResourceCount() && 0 == cache->getCachedResourceBytes());
- populate_cache(gpu, RESOURCE_COUNT);
+ populate_cache(cache, gpu, RESOURCE_COUNT);
// Check that cache works.
for (int k = 0; k < RESOURCE_COUNT; k += 33) {
check_cache_contents_or_die(cache2, k);
}
- cache2->purgeAllUnlocked();
+ cache->purgeAllUnlocked();
}
}
// Set the cache budget to be very large so no purging occurs.
context->setResourceCacheLimits(2 * RESOURCE_COUNT, 1 << 30);
+ GrResourceCache* cache = context->getResourceCache();
GrResourceCache2* cache2 = context->getResourceCache2();
// Make sure the cache is empty.
- cache2->purgeAllUnlocked();
- SkASSERT(0 == cache2->getResourceCount() && 0 == cache2->getResourceBytes());
+ cache->purgeAllUnlocked();
+ SkASSERT(0 == cache->getCachedResourceCount() && 0 == cache->getCachedResourceBytes());
GrGpu* gpu = context->getGpu();
- populate_cache(gpu, RESOURCE_COUNT);
+ populate_cache(cache, gpu, RESOURCE_COUNT);
for (int i = 0; i < loops; ++i) {
for (int k = 0; k < RESOURCE_COUNT; ++k) {
],
},
}],
+ [ 'skia_resource_cache_mb_limit != 0', {
+ 'defines': [
+ 'GR_DEFAULT_RESOURCE_CACHE_MB_LIMIT=<(skia_resource_cache_mb_limit)',
+ ],
+ }],
+ [ 'skia_resource_cache_count_limit != 0', {
+ 'defines': [
+ 'GR_DEFAULT_RESOURCE_CACHE_COUNT_LIMIT=<(skia_resource_cache_count_limit)',
+ ],
+ }],
],
'direct_dependent_settings': {
'conditions': [
'<(skia_src_path)/gpu/GrRenderTarget.cpp',
'<(skia_src_path)/gpu/GrReducedClip.cpp',
'<(skia_src_path)/gpu/GrReducedClip.h',
+ '<(skia_src_path)/gpu/GrResourceCache.cpp',
+ '<(skia_src_path)/gpu/GrResourceCache.h',
'<(skia_src_path)/gpu/GrResourceCache2.cpp',
'<(skia_src_path)/gpu/GrResourceCache2.h',
'<(skia_src_path)/gpu/GrStencil.cpp',
#define GR_GEOM_BUFFER_MAP_THRESHOLD (1 << 15)
#endif
+/**
+ * GR_DEFAULT_RESOURCE_CACHE_MB_LIMIT gives a threshold (in megabytes) for the
+ * maximum size of the texture cache in vram. The value is only a default and
+ * can be overridden at runtime.
+ */
+#if !defined(GR_DEFAULT_RESOURCE_CACHE_MB_LIMIT)
+ #define GR_DEFAULT_RESOURCE_CACHE_MB_LIMIT 96
+#endif
+
+/**
+ * GR_DEFAULT_RESOURCE_CACHE_COUNT_LIMIT specifies the maximum number of
+ * textures the texture cache can hold in vram. The value is only a default and
+ * can be overridden at runtime.
+ */
+#if !defined(GR_DEFAULT_RESOURCE_CACHE_COUNT_LIMIT)
+ #define GR_DEFAULT_RESOURCE_CACHE_COUNT_LIMIT 2048
+#endif
+
/**
* GR_STROKE_PATH_RENDERING controls whether or not the GrStrokePathRenderer can be selected
* as a path renderer. GrStrokePathRenderer is currently an experimental path renderer.
class GrPath;
class GrPathRenderer;
class GrResourceEntry;
+class GrResourceCache;
class GrResourceCache2;
class GrStencilBuffer;
class GrTestTarget;
GrDrawTarget* getTextTarget();
const GrIndexBuffer* getQuadIndexBuffer() const;
GrAARectRenderer* getAARectRenderer() { return fAARectRenderer; }
+ GrResourceCache* getResourceCache() { return fResourceCache; }
GrResourceCache2* getResourceCache2() { return fResourceCache2; }
// Called by tests that draw directly to the context via GrDrawTarget
const GrClipData* fClip; // TODO: make this ref counted
GrDrawState* fDrawState;
+ GrResourceCache* fResourceCache;
GrResourceCache2* fResourceCache2;
GrFontCache* fFontCache;
SkAutoTDelete<GrLayerCache> fLayerCache;
// Set by OverbudgetCB() to request that GrContext flush before exiting a draw.
bool fFlushToReduceCacheSize;
+
GrAARectRenderer* fAARectRenderer;
GrOvalRenderer* fOvalRenderer;
size_t rowBytes,
bool filter);
+ GrTexture* createNewScratchTexture(const GrSurfaceDesc& desc);
+
/**
* These functions create premul <-> unpremul effects if it is possible to generate a pair
* of effects that make a readToUPM->writeToPM->readToUPM cycle invariant. Otherwise, they
/**
* This callback allows the resource cache to callback into the GrContext
- * when the cache is still over budget after a purge.
+ * when the cache is still overbudget after a purge.
*/
- static void OverBudgetCB(void* data);
+ static bool OverbudgetCB(void* data);
typedef SkRefCnt INHERITED;
};
class GrContext;
class GrGpu;
class GrResourceCache2;
+class GrResourceCacheEntry;
/**
* Base class for GrGpuResource. Handles the various types of refs we need. Separated out as a base
};
/**
- * Base class for objects that can be kept in the GrResourceCache2.
+ * Base class for objects that can be kept in the GrResourceCache.
*/
class SK_API GrGpuResource : public GrIORef<GrGpuResource> {
public:
uint32_t fFlags;
+ GrResourceCacheEntry* fCacheEntry; // NULL if not in cache
mutable size_t fGpuMemorySize;
const uint32_t fUniqueID;
*/
//#define GR_GEOM_BUFFER_MAP_THRESHOLD (1<<15)
+/**
+ * This gives a threshold in megabytes for the maximum size of the texture cache
+ * in vram. The value is only a default and can be overridden at runtime.
+ */
+//#define GR_DEFAULT_RESOURCE_CACHE_MB_LIMIT 96
+
+/**
+ * This specifies the maximum number of textures the texture cache can hold
+ * in vram. The value is only a default and can be overridden at runtime.
+ */
+//#define GR_DEFAULT_RESOURCE_CACHE_COUNT_LIMIT 2048
+
#endif
////////////////////////////////////////////////////////////////////////////////
-// The cache listens for these messages to purge junk resources proactively.
-struct GrResourceInvalidatedMessage {
- GrResourceKey key;
-};
-
bool GrIsBitmapInCache(const GrContext*, const SkBitmap&, const GrTextureParams*);
GrTexture* GrRefCachedBitmapTexture(GrContext*, const SkBitmap&, const GrTextureParams*);
#include "GrRect.h"
#include "SkChecksum.h"
-#include "SkTDynamicHash.h"
class GrContext;
class GrPlot;
#include "GrOvalRenderer.h"
#include "GrPathRenderer.h"
#include "GrPathUtils.h"
+#include "GrResourceCache.h"
#include "GrResourceCache2.h"
#include "GrSoftwarePathRenderer.h"
#include "GrStencilBuffer.h"
#define GR_DEBUG_PARTIAL_COVERAGE_CHECK 0
#endif
+static const size_t MAX_RESOURCE_CACHE_COUNT = GR_DEFAULT_RESOURCE_CACHE_COUNT_LIMIT;
+static const size_t MAX_RESOURCE_CACHE_BYTES = GR_DEFAULT_RESOURCE_CACHE_MB_LIMIT * 1024 * 1024;
+
static const size_t DRAW_BUFFER_VBPOOL_BUFFER_SIZE = 1 << 15;
static const int DRAW_BUFFER_VBPOOL_PREALLOC_BUFFERS = 4;
fClip = NULL;
fPathRendererChain = NULL;
fSoftwarePathRenderer = NULL;
+ fResourceCache = NULL;
fResourceCache2 = NULL;
fFontCache = NULL;
fDrawBuffer = NULL;
void GrContext::initCommon() {
fDrawState = SkNEW(GrDrawState);
+ fResourceCache = SkNEW_ARGS(GrResourceCache, (fGpu->caps(),
+ MAX_RESOURCE_CACHE_COUNT,
+ MAX_RESOURCE_CACHE_BYTES));
+ fResourceCache->setOverbudgetCallback(OverbudgetCB, this);
fResourceCache2 = SkNEW(GrResourceCache2);
- fResourceCache2->setOverBudgetCallback(OverBudgetCB, this);
fFontCache = SkNEW_ARGS(GrFontCache, (fGpu));
}
SkDELETE(fResourceCache2);
+ fResourceCache2 = NULL;
+ SkDELETE(fResourceCache);
+ fResourceCache = NULL;
SkDELETE(fFontCache);
SkDELETE(fDrawBuffer);
SkDELETE(fDrawBufferVBAllocPool);
fAARectRenderer->reset();
fOvalRenderer->reset();
+ fResourceCache->purgeAllUnlocked();
+
fFontCache->freeAll();
fLayerCache->freeAll();
}
fAARectRenderer->reset();
fOvalRenderer->reset();
+ fResourceCache->purgeAllUnlocked();
fFontCache->freeAll();
fLayerCache->freeAll();
// a path renderer may be holding onto resources
}
void GrContext::getResourceCacheUsage(int* resourceCount, size_t* resourceBytes) const {
- if (resourceCount) {
- *resourceCount = fResourceCache2->getResourceCount();
- }
- if (resourceBytes) {
- *resourceBytes = fResourceCache2->getResourceBytes();
- }
+ if (resourceCount) {
+ *resourceCount = fResourceCache->getCachedResourceCount();
+ }
+ if (resourceBytes) {
+ *resourceBytes = fResourceCache->getCachedResourceBytes();
+ }
}
GrTextContext* GrContext::createTextContext(GrRenderTarget* renderTarget,
}
void GrContext::addStencilBuffer(GrStencilBuffer* sb) {
- // TODO: Make GrStencilBuffers use the scratch mechanism rather than content keys.
ASSERT_OWNED_RESOURCE(sb);
GrResourceKey resourceKey = GrStencilBuffer::ComputeKey(sb->width(),
sb->height(),
sb->numSamples());
- SkAssertResult(sb->cacheAccess().setContentKey(resourceKey));
+ fResourceCache->addResource(resourceKey, sb);
}
GrStencilBuffer* GrContext::findAndRefStencilBuffer(int width, int height, int sampleCnt) {
}
if (texture) {
- if (texture->cacheAccess().setContentKey(resourceKey)) {
- if (cacheKey) {
- *cacheKey = resourceKey;
- }
- } else {
- texture->unref();
- texture = NULL;
+ fResourceCache->addResource(resourceKey, texture);
+
+ if (cacheKey) {
+ *cacheKey = resourceKey;
}
}
return texture;
}
+GrTexture* GrContext::createNewScratchTexture(const GrSurfaceDesc& desc) {
+ GrTexture* texture = fGpu->createTexture(desc, NULL, 0);
+ if (!texture) {
+ return NULL;
+ }
+ fResourceCache->addResource(texture->cacheAccess().getScratchKey(), texture);
+ return texture;
+}
+
GrTexture* GrContext::refScratchTexture(const GrSurfaceDesc& inDesc, ScratchTexMatch match,
bool calledDuringFlush) {
// kNoStencil has no meaning if kRT isn't set.
}
GrGpuResource* resource = fResourceCache2->findAndRefScratchResource(key, scratchFlags);
if (resource) {
+ fResourceCache->makeResourceMRU(resource);
return static_cast<GrSurface*>(resource)->asTexture();
}
desc.writable()->fFlags = origFlags;
}
- GrTexture* texture = fGpu->createTexture(*desc, NULL, 0);
+ GrTexture* texture = this->createNewScratchTexture(*desc);
SkASSERT(NULL == texture ||
texture->cacheAccess().getScratchKey() == GrTexturePriv::ComputeScratchKey(*desc));
return texture;
}
-void GrContext::OverBudgetCB(void* data) {
+bool GrContext::OverbudgetCB(void* data) {
SkASSERT(data);
GrContext* context = reinterpret_cast<GrContext*>(data);
// Flush the InOrderDrawBuffer to possibly free up some textures
context->fFlushToReduceCacheSize = true;
+
+ return true;
}
}
void GrContext::getResourceCacheLimits(int* maxTextures, size_t* maxTextureBytes) const {
- if (maxTextures) {
- *maxTextures = fResourceCache2->getMaxResourceCount();
- }
- if (maxTextureBytes) {
- *maxTextureBytes = fResourceCache2->getMaxResourceBytes();
- }
+ fResourceCache->getLimits(maxTextures, maxTextureBytes);
}
void GrContext::setResourceCacheLimits(int maxTextures, size_t maxTextureBytes) {
- fResourceCache2->setLimits(maxTextures, maxTextureBytes);
+ fResourceCache->setLimits(maxTextures, maxTextureBytes);
}
int GrContext::getMaxTextureSize() const {
} else {
fDrawBuffer->flush();
}
+ fResourceCache->purgeAsNeeded();
fFlushToReduceCacheSize = false;
}
}
void GrContext::addResourceToCache(const GrResourceKey& resourceKey, GrGpuResource* resource) {
- resource->cacheAccess().setContentKey(resourceKey);
+ fResourceCache->addResource(resourceKey, resource);
}
GrGpuResource* GrContext::findAndRefCachedResource(const GrResourceKey& resourceKey) {
- return fResourceCache2->findAndRefContentResource(resourceKey);
+ GrGpuResource* resource = fResourceCache2->findAndRefContentResource(resourceKey);
+ if (resource) {
+ fResourceCache->makeResourceMRU(resource);
+ }
+ return resource;
}
void GrContext::addGpuTraceMarker(const GrGpuTraceMarker* marker) {
///////////////////////////////////////////////////////////////////////////////
#if GR_CACHE_STATS
void GrContext::printCacheStats() const {
- fResourceCache2->printStats();
+ fResourceCache->printStats();
}
#endif
return gpu->getContext()->getResourceCache2();
}
+static inline GrResourceCache* get_resource_cache(GrGpu* gpu) {
+ SkASSERT(gpu);
+ SkASSERT(gpu->getContext());
+ SkASSERT(gpu->getContext()->getResourceCache());
+ return gpu->getContext()->getResourceCache();
+}
+
GrGpuResource::GrGpuResource(GrGpu* gpu, bool isWrapped)
: fGpu(gpu)
+ , fCacheEntry(NULL)
, fGpuMemorySize(kInvalidGpuMemorySize)
, fUniqueID(CreateUniqueID())
, fScratchKey(GrResourceKey::NullScratchKey())
}
void GrGpuResource::registerWithCache() {
- get_resource_cache2(fGpu)->resourceAccess().insertResource(this);
+ get_resource_cache2(fGpu)->insertResource(this);
}
GrGpuResource::~GrGpuResource() {
void GrGpuResource::release() {
if (fGpu) {
this->onRelease();
- get_resource_cache2(fGpu)->resourceAccess().removeResource(this);
+ get_resource_cache2(fGpu)->removeResource(this);
fGpu = NULL;
- fGpuMemorySize = 0;
}
}
void GrGpuResource::abandon() {
if (fGpu) {
this->onAbandon();
- get_resource_cache2(fGpu)->resourceAccess().removeResource(this);
+ get_resource_cache2(fGpu)->removeResource(this);
fGpu = NULL;
- fGpuMemorySize = 0;
}
}
}
}
-void GrGpuResource::didChangeGpuMemorySize() const {
- if (this->wasDestroyed()) {
- return;
- }
-
- size_t oldSize = fGpuMemorySize;
- SkASSERT(kInvalidGpuMemorySize != oldSize);
- fGpuMemorySize = kInvalidGpuMemorySize;
- get_resource_cache2(fGpu)->resourceAccess().didChangeGpuMemorySize(this, oldSize);
-}
-
bool GrGpuResource::setContentKey(const GrResourceKey& contentKey) {
// Currently this can only be called once and can't be called when the resource is scratch.
SkASSERT(!contentKey.isScratch());
fContentKey = contentKey;
fContentKeySet = true;
- if (!get_resource_cache2(fGpu)->resourceAccess().didSetContentKey(this)) {
+ if (!get_resource_cache2(fGpu)->didSetContentKey(this)) {
fContentKeySet = false;
return false;
}
}
void GrGpuResource::notifyIsPurgable() const {
- if (!this->wasDestroyed()) {
- get_resource_cache2(fGpu)->resourceAccess().notifyPurgable(this);
+ if (fCacheEntry && !this->wasDestroyed()) {
+ get_resource_cache(fGpu)->notifyPurgable(this);
}
}
return fResource->setContentKey(contentKey);
}
+ /**
+ * Used by legacy cache to attach a cache entry. This is to be removed soon.
+ */
+ void setCacheEntry(GrResourceCacheEntry* cacheEntry) {
+ // GrResourceCache never changes the cacheEntry once one has been added.
+ SkASSERT(NULL == cacheEntry || NULL == fResource->fCacheEntry);
+ fResource->fCacheEntry = cacheEntry;
+ }
+
+ /**
+ * Is the resource in the legacy cache? This is to be removed soon.
+ */
+ bool isInCache() const { return SkToBool(fResource->fCacheEntry); }
+
+ /**
+ * Returns the cache entry for the legacy cache. This is to be removed soon.
+ */
+ GrResourceCacheEntry* getCacheEntry() const { return fResource->fCacheEntry; }
+
/**
* Is the resource currently cached as scratch? This means it has a valid scratch key and does
* not have a content key.
#define GrPath_DEFINED
#include "GrGpuResource.h"
+#include "GrResourceCache.h"
#include "SkPath.h"
#include "SkRect.h"
#include "SkStrokeRec.h"
#define GrPathRange_DEFINED
#include "GrGpuResource.h"
+#include "GrResourceCache.h"
#include "SkRefCnt.h"
#include "SkStrokeRec.h"
#include "SkTArray.h"
--- /dev/null
+
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrResourceCache.h"
+#include "GrGpuResource.h"
+#include "GrGpuResourceCacheAccess.h"
+#include "GrTexturePriv.h"
+
+DECLARE_SKMESSAGEBUS_MESSAGE(GrResourceInvalidatedMessage);
+
+///////////////////////////////////////////////////////////////////////////////
+
+void GrGpuResource::didChangeGpuMemorySize() const {
+ fGpuMemorySize = kInvalidGpuMemorySize;
+ if (this->cacheAccess().isInCache()) {
+ fCacheEntry->didChangeResourceSize();
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+GrResourceKey::ResourceType GrResourceKey::GenerateResourceType() {
+ static int32_t gNextType = 0;
+
+ int32_t type = sk_atomic_inc(&gNextType);
+ if (type >= (1 << 8 * sizeof(ResourceType))) {
+ SkFAIL("Too many Resource Types");
+ }
+
+ return static_cast<ResourceType>(type);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+GrResourceCacheEntry::GrResourceCacheEntry(GrResourceCache* resourceCache, GrGpuResource* resource)
+ : fResourceCache(resourceCache),
+ fResource(resource),
+ fCachedSize(resource->gpuMemorySize()) {
+ // we assume ownership of the resource, and will unref it when we die
+ SkASSERT(resource);
+ resource->ref();
+}
+
+GrResourceCacheEntry::~GrResourceCacheEntry() {
+ // We're relying on having the cache entry to remove this from GrResourceCache2's content hash.
+ // fResource->setCacheEntry(NULL);
+ fResource->unref();
+}
+
+#ifdef SK_DEBUG
+void GrResourceCacheEntry::validate() const {
+ SkASSERT(fResourceCache);
+ SkASSERT(fResource);
+ SkASSERT(fResource->cacheAccess().getCacheEntry() == this);
+ SkASSERT(fResource->gpuMemorySize() == fCachedSize);
+ fResource->validate();
+}
+#endif
+
+void GrResourceCacheEntry::didChangeResourceSize() {
+ size_t oldSize = fCachedSize;
+ fCachedSize = fResource->gpuMemorySize();
+ if (fCachedSize > oldSize) {
+ fResourceCache->didIncreaseResourceSize(this, fCachedSize - oldSize);
+ } else if (fCachedSize < oldSize) {
+ fResourceCache->didDecreaseResourceSize(this, oldSize - fCachedSize);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+GrResourceCache::GrResourceCache(const GrDrawTargetCaps* caps, int maxCount, size_t maxBytes)
+ : fMaxCount(maxCount)
+ , fMaxBytes(maxBytes)
+ , fCaps(SkRef(caps)) {
+#if GR_CACHE_STATS
+ fHighWaterEntryCount = 0;
+ fHighWaterEntryBytes = 0;
+#endif
+
+ fEntryCount = 0;
+ fEntryBytes = 0;
+
+ fPurging = false;
+
+ fOverbudgetCB = NULL;
+ fOverbudgetData = NULL;
+}
+
+GrResourceCache::~GrResourceCache() {
+ GrAutoResourceCacheValidate atcv(this);
+
+ EntryList::Iter iter;
+
+ // Unlike the removeAll, here we really remove everything, including locked resources.
+ while (GrResourceCacheEntry* entry = fList.head()) {
+ GrAutoResourceCacheValidate atcv(this);
+
+ // remove from our llist
+ this->internalDetach(entry);
+
+ delete entry;
+ }
+}
+
+void GrResourceCache::getLimits(int* maxResources, size_t* maxResourceBytes) const{
+ if (maxResources) {
+ *maxResources = fMaxCount;
+ }
+ if (maxResourceBytes) {
+ *maxResourceBytes = fMaxBytes;
+ }
+}
+
+void GrResourceCache::setLimits(int maxResources, size_t maxResourceBytes) {
+ bool smaller = (maxResources < fMaxCount) || (maxResourceBytes < fMaxBytes);
+
+ fMaxCount = maxResources;
+ fMaxBytes = maxResourceBytes;
+
+ if (smaller) {
+ this->purgeAsNeeded();
+ }
+}
+
+void GrResourceCache::internalDetach(GrResourceCacheEntry* entry) {
+ fList.remove(entry);
+ fEntryCount -= 1;
+ fEntryBytes -= entry->fCachedSize;
+}
+
+void GrResourceCache::attachToHead(GrResourceCacheEntry* entry) {
+ fList.addToHead(entry);
+
+ fEntryCount += 1;
+ fEntryBytes += entry->fCachedSize;
+
+#if GR_CACHE_STATS
+ if (fHighWaterEntryCount < fEntryCount) {
+ fHighWaterEntryCount = fEntryCount;
+ }
+ if (fHighWaterEntryBytes < fEntryBytes) {
+ fHighWaterEntryBytes = fEntryBytes;
+ }
+#endif
+}
+
+
+void GrResourceCache::makeResourceMRU(GrGpuResource* resource) {
+ GrResourceCacheEntry* entry = resource->cacheAccess().getCacheEntry();
+ if (entry) {
+ this->internalDetach(entry);
+ this->attachToHead(entry);
+ }
+}
+
+void GrResourceCache::notifyPurgable(const GrGpuResource* resource) {
+ // Remove scratch textures from the cache the moment they become purgeable if
+ // scratch texture reuse is turned off.
+ SkASSERT(resource->cacheAccess().getCacheEntry());
+ if (resource->cacheAccess().isScratch()) {
+ const GrResourceKey& key = resource->cacheAccess().getScratchKey();
+ if (key.getResourceType() == GrTexturePriv::ResourceType() &&
+ !fCaps->reuseScratchTextures() &&
+ !(static_cast<const GrSurface*>(resource)->desc().fFlags & kRenderTarget_GrSurfaceFlag)) {
+ this->deleteResource(resource->cacheAccess().getCacheEntry());
+ }
+ }
+}
+
+bool GrResourceCache::addResource(const GrResourceKey& key, GrGpuResource* resource) {
+ if (NULL != resource->cacheAccess().getCacheEntry()) {
+ return false;
+ }
+
+ if (key.isScratch()) {
+ SkASSERT(resource->cacheAccess().isScratch());
+ SkASSERT(key == resource->cacheAccess().getScratchKey());
+ } else {
+ if (!resource->cacheAccess().setContentKey(key)) {
+ return false;
+ }
+ }
+
+ // we don't expect to create new resources during a purge. In theory
+ // this could cause purgeAsNeeded() into an infinite loop (e.g.
+ // each resource destroyed creates and locks 2 resources and
+ // unlocks 1 thereby causing a new purge).
+ SkASSERT(!fPurging);
+ GrAutoResourceCacheValidate atcv(this);
+
+ GrResourceCacheEntry* entry = SkNEW_ARGS(GrResourceCacheEntry, (this, resource));
+ resource->cacheAccess().setCacheEntry(entry);
+
+ this->attachToHead(entry);
+ this->purgeAsNeeded();
+ return true;
+}
+
+void GrResourceCache::didIncreaseResourceSize(const GrResourceCacheEntry* entry, size_t amountInc) {
+ fEntryBytes += amountInc;
+ this->purgeAsNeeded();
+}
+
+void GrResourceCache::didDecreaseResourceSize(const GrResourceCacheEntry* entry, size_t amountDec) {
+ fEntryBytes -= amountDec;
+#ifdef SK_DEBUG
+ this->validate();
+#endif
+}
+
+/**
+ * Destroying a resource may potentially trigger the unlock of additional
+ * resources which in turn will trigger a nested purge. We block the nested
+ * purge using the fPurging variable. However, the initial purge will keep
+ * looping until either all resources in the cache are unlocked or we've met
+ * the budget. There is an assertion in createAndLock to check against a
+ * resource's destructor inserting new resources into the cache. If these
+ * new resources were unlocked before purgeAsNeeded completed it could
+ * potentially make purgeAsNeeded loop infinitely.
+ *
+ * extraCount and extraBytes are added to the current resource totals to account
+ * for incoming resources (e.g., GrContext is about to add 10MB split between
+ * 10 textures).
+ */
+void GrResourceCache::purgeAsNeeded(int extraCount, size_t extraBytes) {
+ if (fPurging) {
+ return;
+ }
+
+ fPurging = true;
+
+ this->internalPurge(extraCount, extraBytes);
+ if (((fEntryCount+extraCount) > fMaxCount ||
+ (fEntryBytes+extraBytes) > fMaxBytes) &&
+ fOverbudgetCB) {
+ // Despite the purge we're still over budget. See if Ganesh can
+ // release some resources and purge again.
+ if ((*fOverbudgetCB)(fOverbudgetData)) {
+ this->internalPurge(extraCount, extraBytes);
+ }
+ }
+
+ fPurging = false;
+}
+
+void GrResourceCache::purgeInvalidated() {
+ // TODO: Implement this in GrResourceCache2.
+}
+
+void GrResourceCache::deleteResource(GrResourceCacheEntry* entry) {
+ SkASSERT(entry->fResource->isPurgable());
+ // remove from our llist
+ this->internalDetach(entry);
+ delete entry;
+}
+
+void GrResourceCache::internalPurge(int extraCount, size_t extraBytes) {
+ SkASSERT(fPurging);
+
+ bool withinBudget = false;
+ bool changed = false;
+
+ // The purging process is repeated several times since one pass
+ // may free up other resources
+ do {
+ EntryList::Iter iter;
+
+ changed = false;
+
+ // Note: the following code relies on the fact that the
+ // doubly linked list doesn't invalidate its data/pointers
+ // outside of the specific area where a deletion occurs (e.g.,
+ // in internalDetach)
+ GrResourceCacheEntry* entry = iter.init(fList, EntryList::Iter::kTail_IterStart);
+
+ while (entry) {
+ GrAutoResourceCacheValidate atcv(this);
+
+ if ((fEntryCount+extraCount) <= fMaxCount &&
+ (fEntryBytes+extraBytes) <= fMaxBytes) {
+ withinBudget = true;
+ break;
+ }
+
+ GrResourceCacheEntry* prev = iter.prev();
+ if (entry->fResource->isPurgable()) {
+ changed = true;
+ this->deleteResource(entry);
+ }
+ entry = prev;
+ }
+ } while (!withinBudget && changed);
+}
+
+void GrResourceCache::purgeAllUnlocked() {
+ GrAutoResourceCacheValidate atcv(this);
+
+ // we can have one GrCacheable holding a lock on another
+ // so we don't want to just do a simple loop kicking each
+ // entry out. Instead change the budget and purge.
+
+ size_t savedMaxBytes = fMaxBytes;
+ int savedMaxCount = fMaxCount;
+ fMaxBytes = (size_t) -1;
+ fMaxCount = 0;
+ this->purgeAsNeeded();
+
+ fMaxBytes = savedMaxBytes;
+ fMaxCount = savedMaxCount;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_DEBUG
+size_t GrResourceCache::countBytes(const EntryList& list) {
+ size_t bytes = 0;
+
+ EntryList::Iter iter;
+
+ const GrResourceCacheEntry* entry = iter.init(const_cast<EntryList&>(list),
+ EntryList::Iter::kTail_IterStart);
+
+ for ( ; entry; entry = iter.prev()) {
+ bytes += entry->resource()->gpuMemorySize();
+ }
+ return bytes;
+}
+
+static bool both_zero_or_nonzero(int count, size_t bytes) {
+ return (count == 0 && bytes == 0) || (count > 0 && bytes > 0);
+}
+
+void GrResourceCache::validate() const {
+ fList.validate();
+ SkASSERT(both_zero_or_nonzero(fEntryCount, fEntryBytes));
+
+ EntryList::Iter iter;
+
+ // check that the shareable entries are okay
+ const GrResourceCacheEntry* entry = iter.init(const_cast<EntryList&>(fList),
+ EntryList::Iter::kHead_IterStart);
+
+ int count = 0;
+ for ( ; entry; entry = iter.next()) {
+ entry->validate();
+ count += 1;
+ }
+ SkASSERT(count == fEntryCount);
+
+ size_t bytes = this->countBytes(fList);
+ SkASSERT(bytes == fEntryBytes);
+ SkASSERT(fList.countEntries() == fEntryCount);
+}
+#endif // SK_DEBUG
+
+#if GR_CACHE_STATS
+
+void GrResourceCache::printStats() {
+ int locked = 0;
+ int scratch = 0;
+
+ EntryList::Iter iter;
+
+ GrResourceCacheEntry* entry = iter.init(fList, EntryList::Iter::kTail_IterStart);
+
+ for ( ; entry; entry = iter.prev()) {
+ if (!entry->fResource->isPurgable()) {
+ ++locked;
+ }
+ if (entry->fResource->cacheAccess().isScratch()) {
+ ++scratch;
+ }
+ }
+
+ float countUtilization = (100.f * fEntryCount) / fMaxCount;
+ float byteUtilization = (100.f * fEntryBytes) / fMaxBytes;
+
+ SkDebugf("Budget: %d items %d bytes\n", fMaxCount, fMaxBytes);
+ SkDebugf("\t\tEntry Count: current %d (%d locked, %d scratch %.2g%% full), high %d\n",
+ fEntryCount, locked, scratch, countUtilization, fHighWaterEntryCount);
+ SkDebugf("\t\tEntry Bytes: current %d (%.2g%% full) high %d\n",
+ fEntryBytes, byteUtilization, fHighWaterEntryBytes);
+}
+
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
--- /dev/null
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrResourceCache_DEFINED
+#define GrResourceCache_DEFINED
+
+#include "GrDrawTargetCaps.h"
+#include "GrResourceKey.h"
+#include "SkTMultiMap.h"
+#include "SkMessageBus.h"
+#include "SkTInternalLList.h"
+
+class GrGpuResource;
+class GrResourceCache;
+class GrResourceCacheEntry;
+
+
+// The cache listens for these messages to purge junk resources proactively.
+struct GrResourceInvalidatedMessage {
+ GrResourceKey key;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class GrResourceCacheEntry {
+public:
+ GrGpuResource* resource() const { return fResource; }
+
+ static uint32_t Hash(const GrGpuResource* resource) {
+ return static_cast<uint32_t>(reinterpret_cast<intptr_t>(resource));
+ }
+#ifdef SK_DEBUG
+ void validate() const;
+#else
+ void validate() const {}
+#endif
+
+ /**
+ * Update the cached size for this entry and inform the resource cache that
+ * it has changed. Usually invoked from GrGpuResource::didChangeGpuMemorySize,
+ * not directly from here.
+ */
+ void didChangeResourceSize();
+
+private:
+ GrResourceCacheEntry(GrResourceCache*, GrGpuResource*);
+ ~GrResourceCacheEntry();
+
+ GrResourceCache* fResourceCache;
+ GrGpuResource* fResource;
+ size_t fCachedSize;
+
+ // Linked list for the LRU ordering.
+ SK_DECLARE_INTERNAL_LLIST_INTERFACE(GrResourceCacheEntry);
+
+ friend class GrResourceCache;
+ friend class GrContext;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Cache of GrGpuResource objects.
+ *
+ * These have a corresponding GrResourceKey, built from 128bits identifying the
+ * resource. Multiple resources can map to same GrResourceKey.
+ *
+ * The cache stores the entries in a double-linked list, which is its LRU.
+ * When an entry is "locked" (i.e. given to the caller), it is moved to the
+ * head of the list. If/when we must purge some of the entries, we walk the
+ * list backwards from the tail, since those are the least recently used.
+ *
+ * For fast searches, we maintain a hash map based on the GrResourceKey.
+ *
+ * It is a goal to make the GrResourceCache the central repository and bookkeeper
+ * of all resources. It should replace the linked list of GrGpuResources that
+ * GrGpu uses to call abandon/release.
+ */
+class GrResourceCache {
+public:
+ GrResourceCache(const GrDrawTargetCaps*, int maxCount, size_t maxBytes);
+ ~GrResourceCache();
+
+ /**
+ * Return the current resource cache limits.
+ *
+ * @param maxResource If non-null, returns maximum number of resources
+ * that can be held in the cache.
+ * @param maxBytes If non-null, returns maximum number of bytes of
+ * gpu memory that can be held in the cache.
+ */
+ void getLimits(int* maxResources, size_t* maxBytes) const;
+
+ /**
+ * Specify the resource cache limits. If the current cache exceeds either
+ * of these, it will be purged (LRU) to keep the cache within these limits.
+ *
+ * @param maxResources The maximum number of resources that can be held in
+ * the cache.
+ * @param maxBytes The maximum number of bytes of resource memory that
+ * can be held in the cache.
+ */
+ void setLimits(int maxResources, size_t maxResourceBytes);
+
+ /**
+ * The callback function used by the cache when it is still over budget
+ * after a purge. The passed in 'data' is the same 'data' handed to
+ * setOverbudgetCallback. The callback returns true if some resources
+ * have been freed.
+ */
+ typedef bool (*PFOverbudgetCB)(void* data);
+
+ /**
+ * Set the callback the cache should use when it is still over budget
+ * after a purge. The 'data' provided here will be passed back to the
+ * callback. Note that the cache will attempt to purge any resources newly
+ * freed by the callback.
+ */
+ void setOverbudgetCallback(PFOverbudgetCB overbudgetCB, void* data) {
+ fOverbudgetCB = overbudgetCB;
+ fOverbudgetData = data;
+ }
+
+ /**
+ * Returns the number of bytes consumed by cached resources.
+ */
+ size_t getCachedResourceBytes() const { return fEntryBytes; }
+
+ /**
+ * Returns the number of cached resources.
+ */
+ int getCachedResourceCount() const { return fEntryCount; }
+
+ void makeResourceMRU(GrGpuResource*);
+
+ /** Called by GrGpuResources when they detects that they are newly purgable. */
+ void notifyPurgable(const GrGpuResource*);
+
+ /**
+ * Add the new resource to the cache (by creating a new cache entry based
+ * on the provided key and resource).
+ *
+ * Ownership of the resource is transferred to the resource cache,
+ * which will unref() it when it is purged or deleted.
+ *
+ * This can fail if the key is already taken, or the resource is already in
+ * the cache.
+ */
+ bool addResource(const GrResourceKey& key, GrGpuResource* resource);
+
+ /**
+ * Notify the cache that the size of a resource has changed.
+ */
+ void didIncreaseResourceSize(const GrResourceCacheEntry*, size_t amountInc);
+ void didDecreaseResourceSize(const GrResourceCacheEntry*, size_t amountDec);
+
+ /**
+ * Remove a resource from the cache and delete it!
+ */
+ void deleteResource(GrResourceCacheEntry* entry);
+
+ /**
+ * Removes every resource in the cache that isn't locked.
+ */
+ void purgeAllUnlocked();
+
+ /**
+ * Allow cache to purge unused resources to obey resource limitations
+ * Note: this entry point will be hidden (again) once totally ref-driven
+ * cache maintenance is implemented. Note that the overbudget callback
+ * will be called if the initial purge doesn't get the cache under
+ * its budget.
+ *
+ * extraCount and extraBytes are added to the current resource allocation
+ * to make sure enough room is available for future additions (e.g,
+ * 10MB across 10 textures is about to be added).
+ */
+ void purgeAsNeeded(int extraCount = 0, size_t extraBytes = 0);
+
+#ifdef SK_DEBUG
+ void validate() const;
+#else
+ void validate() const {}
+#endif
+
+#if GR_CACHE_STATS
+ void printStats();
+#endif
+
+private:
+ void internalDetach(GrResourceCacheEntry*);
+ void attachToHead(GrResourceCacheEntry*);
+ void purgeInvalidated();
+ void internalPurge(int extraCount, size_t extraBytes);
+#ifdef SK_DEBUG
+ static size_t countBytes(const SkTInternalLList<GrResourceCacheEntry>& list);
+#endif
+
+ // We're an internal doubly linked list
+ typedef SkTInternalLList<GrResourceCacheEntry> EntryList;
+ EntryList fList;
+
+ // our budget, used in purgeAsNeeded()
+ int fMaxCount;
+ size_t fMaxBytes;
+
+ // our current stats, related to our budget
+#if GR_CACHE_STATS
+ int fHighWaterEntryCount;
+ size_t fHighWaterEntryBytes;
+#endif
+
+ int fEntryCount;
+ size_t fEntryBytes;
+
+ // prevents recursive purging
+ bool fPurging;
+
+ PFOverbudgetCB fOverbudgetCB;
+ void* fOverbudgetData;
+
+ SkAutoTUnref<const GrDrawTargetCaps> fCaps;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_DEBUG
+ class GrAutoResourceCacheValidate {
+ public:
+ GrAutoResourceCacheValidate(GrResourceCache* cache) : fCache(cache) {
+ cache->validate();
+ }
+ ~GrAutoResourceCacheValidate() {
+ fCache->validate();
+ }
+ private:
+ GrResourceCache* fCache;
+ };
+#else
+ class GrAutoResourceCacheValidate {
+ public:
+ GrAutoResourceCacheValidate(GrResourceCache*) {}
+ };
+#endif
+
+#endif
#include "GrResourceCache2.h"
#include "GrGpuResource.h"
-#include "SkGr.h"
-#include "SkMessageBus.h"
-
-DECLARE_SKMESSAGEBUS_MESSAGE(GrResourceInvalidatedMessage);
-
-//////////////////////////////////////////////////////////////////////////////
-
GrResourceKey& GrResourceKey::NullScratchKey() {
static const GrCacheID::Key kBogusKey = { { {0} } };
static GrCacheID kBogusID(ScratchDomain(), kBogusKey);
return gDomain;
}
-GrResourceKey::ResourceType GrResourceKey::GenerateResourceType() {
- static int32_t gNextType = 0;
-
- int32_t type = sk_atomic_inc(&gNextType);
- if (type >= (1 << 8 * sizeof(ResourceType))) {
- SkFAIL("Too many Resource Types");
- }
-
- return static_cast<ResourceType>(type);
-}
-
//////////////////////////////////////////////////////////////////////////////
-class GrResourceCache2::AutoValidate : ::SkNoncopyable {
-public:
- AutoValidate(GrResourceCache2* cache) : fCache(cache) { cache->validate(); }
- ~AutoValidate() { fCache->validate(); }
-private:
- GrResourceCache2* fCache;
-};
-
- //////////////////////////////////////////////////////////////////////////////
-
-static const int kDefaultMaxCount = 2 * (1 << 10);
-static const size_t kDefaultMaxSize = 96 * (1 << 20);
-
-GrResourceCache2::GrResourceCache2()
- : fMaxCount(kDefaultMaxCount)
- , fMaxBytes(kDefaultMaxSize)
-#if GR_CACHE_STATS
- , fHighWaterCount(0)
- , fHighWaterBytes(0)
-#endif
- , fCount(0)
- , fBytes(0)
- , fPurging(false)
- , fNewlyPurgableResourceWhilePurging(false)
- , fOverBudgetCB(NULL)
- , fOverBudgetData(NULL) {
-}
-
GrResourceCache2::~GrResourceCache2() {
this->releaseAll();
}
-void GrResourceCache2::setLimits(int count, size_t bytes) {
- fMaxCount = count;
- fMaxBytes = bytes;
- this->purgeAsNeeded();
-}
-
void GrResourceCache2::insertResource(GrGpuResource* resource) {
- AutoValidate av(this);
-
SkASSERT(resource);
SkASSERT(!resource->wasDestroyed());
SkASSERT(!this->isInCache(resource));
- SkASSERT(!fPurging);
fResources.addToHead(resource);
- resource->ref();
-
++fCount;
- SkDEBUGCODE(fHighWaterCount = SkTMax(fCount, fHighWaterCount));
- fBytes += resource->gpuMemorySize();
- SkDEBUGCODE(fHighWaterBytes = SkTMax(fBytes, fHighWaterBytes));
if (!resource->cacheAccess().getScratchKey().isNullScratch()) {
// TODO(bsalomon): Make this assertion possible.
// SkASSERT(!resource->isWrapped());
fScratchMap.insert(resource->cacheAccess().getScratchKey(), resource);
}
-
- this->purgeAsNeeded();
}
void GrResourceCache2::removeResource(GrGpuResource* resource) {
- AutoValidate av(this);
-
- --fCount;
- fBytes -= resource->gpuMemorySize();
SkASSERT(this->isInCache(resource));
fResources.remove(resource);
if (!resource->cacheAccess().getScratchKey().isNullScratch()) {
if (const GrResourceKey* contentKey = resource->cacheAccess().getContentKey()) {
fContentHash.remove(*contentKey);
}
+ --fCount;
}
void GrResourceCache2::abandonAll() {
- AutoValidate av(this);
-
- SkASSERT(!fPurging);
while (GrGpuResource* head = fResources.head()) {
SkASSERT(!head->wasDestroyed());
head->abandon();
- head->unref();
// abandon should have already removed this from the list.
SkASSERT(head != fResources.head());
}
}
void GrResourceCache2::releaseAll() {
- AutoValidate av(this);
-
- SkASSERT(!fPurging);
while (GrGpuResource* head = fResources.head()) {
SkASSERT(!head->wasDestroyed());
head->release();
- head->unref();
// release should have already removed this from the list.
SkASSERT(head != fResources.head());
}
GrGpuResource* GrResourceCache2::findAndRefScratchResource(const GrResourceKey& scratchKey,
uint32_t flags) {
- AutoValidate av(this);
-
- SkASSERT(!fPurging);
SkASSERT(scratchKey.isScratch());
- GrGpuResource* resource;
if (flags & (kPreferNoPendingIO_ScratchFlag | kRequireNoPendingIO_ScratchFlag)) {
- resource = fScratchMap.find(scratchKey, AvailableForScratchUse(true));
+ GrGpuResource* resource = fScratchMap.find(scratchKey, AvailableForScratchUse(true));
if (resource) {
- this->makeResourceMRU(resource);
return SkRef(resource);
} else if (flags & kRequireNoPendingIO_ScratchFlag) {
return NULL;
// TODO: fail here when kPrefer is specified, we didn't find a resource without pending io,
// but there is still space in our budget for the resource.
}
- resource = fScratchMap.find(scratchKey, AvailableForScratchUse(false));
- if (resource) {
- resource->ref();
- this->makeResourceMRU(resource);
- }
- return resource;
+ return SkSafeRef(fScratchMap.find(scratchKey, AvailableForScratchUse(false)));
}
bool GrResourceCache2::didSetContentKey(GrGpuResource* resource) {
- SkASSERT(!fPurging);
SkASSERT(resource);
- SkASSERT(this->isInCache(resource));
SkASSERT(resource->cacheAccess().getContentKey());
SkASSERT(!resource->cacheAccess().getContentKey()->isScratch());
}
fContentHash.add(resource);
- this->validate();
return true;
}
-
-void GrResourceCache2::makeResourceMRU(GrGpuResource* resource) {
- AutoValidate av(this);
-
- SkASSERT(!fPurging);
- SkASSERT(resource);
- SkASSERT(this->isInCache(resource));
- fResources.remove(resource);
- fResources.addToHead(resource);
-}
-
-void GrResourceCache2::notifyPurgable(const GrGpuResource* resource) {
- SkASSERT(resource);
- SkASSERT(this->isInCache(resource));
- SkASSERT(resource->isPurgable());
-
- // We can't purge if in the middle of purging because purge is iterating. Instead record
- // that additional resources became purgable.
- if (fPurging) {
- fNewlyPurgableResourceWhilePurging = true;
- return;
- }
-
- // Purge the resource if we're over budget
- bool overBudget = fCount > fMaxCount || fBytes > fMaxBytes;
-
- // We should not be over budget here unless all resources are unpuragble.
-#ifdef SK_DEBUG
- if (overBudget) {
- ResourceList::Iter iter;
- GrGpuResource* r = iter.init(fResources, ResourceList::Iter::kHead_IterStart);
- for ( ; r; r = iter.next()) {
- SkASSERT(r == resource || !r->isPurgable());
- }
- }
-#endif
-
- // Also purge if the resource has neither a valid scratch key nor a content key.
- bool noKey = !resource->cacheAccess().isScratch() &&
- (NULL == resource->cacheAccess().getContentKey());
-
- if (overBudget || noKey) {
- SkDEBUGCODE(int beforeCount = fCount;)
- resource->unref();
- SkASSERT(fCount == beforeCount - 1);
- }
-
- this->validate();
-}
-
-void GrResourceCache2::didChangeGpuMemorySize(const GrGpuResource* resource, size_t oldSize) {
- // SkASSERT(!fPurging); GrPathRange increases size during flush. :(
- SkASSERT(resource);
- SkASSERT(this->isInCache(resource));
-
- fBytes += resource->gpuMemorySize() - oldSize;
- SkDEBUGCODE(fHighWaterBytes = SkTMax(fBytes, fHighWaterBytes));
-
- this->purgeAsNeeded();
- this->validate();
-}
-
-void GrResourceCache2::internalPurgeAsNeeded() {
- SkASSERT(!fPurging);
- SkASSERT(!fNewlyPurgableResourceWhilePurging);
- SkASSERT(fCount > fMaxCount || fBytes > fMaxBytes);
-
- fPurging = true;
-
- AutoValidate av(this); // Put this after setting fPurging so we're allowed to be over budget.
-
- bool overBudget = true;
- do {
- fNewlyPurgableResourceWhilePurging = false;
- ResourceList::Iter resourceIter;
- GrGpuResource* resource = resourceIter.init(fResources,
- ResourceList::Iter::kTail_IterStart);
-
- while (resource) {
- GrGpuResource* prev = resourceIter.prev();
- if (resource->isPurgable()) {
- resource->unref();
- }
- resource = prev;
- if (fCount <= fMaxCount && fBytes <= fMaxBytes) {
- overBudget = false;
- resource = NULL;
- }
- }
-
- if (!fNewlyPurgableResourceWhilePurging && overBudget && fOverBudgetCB) {
- // Despite the purge we're still over budget. Call our over budget callback.
- (*fOverBudgetCB)(fOverBudgetData);
- }
- } while (overBudget && fNewlyPurgableResourceWhilePurging);
-
- fNewlyPurgableResourceWhilePurging = false;
- fPurging = false;
-}
-
-void GrResourceCache2::purgeAllUnlocked() {
- SkASSERT(!fPurging);
- SkASSERT(!fNewlyPurgableResourceWhilePurging);
-
- fPurging = true;
-
- AutoValidate av(this); // Put this after setting fPurging so we're allowed to be over budget.
-
- do {
- fNewlyPurgableResourceWhilePurging = false;
- ResourceList::Iter resourceIter;
- GrGpuResource* resource =
- resourceIter.init(fResources, ResourceList::Iter::kTail_IterStart);
-
- while (resource) {
- GrGpuResource* prev = resourceIter.prev();
- if (resource->isPurgable()) {
- resource->unref();
- }
- resource = prev;
- }
-
- if (!fNewlyPurgableResourceWhilePurging && fCount && fOverBudgetCB) {
- (*fOverBudgetCB)(fOverBudgetData);
- }
- } while (fNewlyPurgableResourceWhilePurging);
- fPurging = false;
-}
-
-#ifdef SK_DEBUG
-void GrResourceCache2::validate() const {
- size_t bytes = 0;
- int count = 0;
- int locked = 0;
- int scratch = 0;
- int couldBeScratch = 0;
- int content = 0;
-
- ResourceList::Iter iter;
- GrGpuResource* resource = iter.init(fResources, ResourceList::Iter::kHead_IterStart);
- for ( ; resource; resource = iter.next()) {
- bytes += resource->gpuMemorySize();
- ++count;
-
- if (!resource->isPurgable()) {
- ++locked;
- }
-
- if (resource->cacheAccess().isScratch()) {
- SkASSERT(NULL == resource->cacheAccess().getContentKey());
- ++scratch;
- SkASSERT(fScratchMap.countForKey(resource->cacheAccess().getScratchKey()));
- } else if (!resource->cacheAccess().getScratchKey().isNullScratch()) {
- SkASSERT(NULL != resource->cacheAccess().getContentKey());
- ++couldBeScratch;
- SkASSERT(fScratchMap.countForKey(resource->cacheAccess().getScratchKey()));
- }
-
- if (const GrResourceKey* contentKey = resource->cacheAccess().getContentKey()) {
- ++content;
- SkASSERT(fContentHash.find(*contentKey) == resource);
- }
- }
-
- SkASSERT(bytes == fBytes);
- SkASSERT(count == fCount);
-#if GR_CACHE_STATS
- SkASSERT(bytes <= fHighWaterBytes);
- SkASSERT(count <= fHighWaterCount);
-#endif
- SkASSERT(content == fContentHash.count());
- SkASSERT(scratch + couldBeScratch == fScratchMap.count());
-
- bool overBudget = bytes > fMaxBytes || count > fMaxCount;
- SkASSERT(!overBudget || locked == count || fPurging);
-}
-#endif
-
-#if GR_CACHE_STATS
-void GrResourceCache2::printStats() const {
- this->validate();
-
- int locked = 0;
- int scratch = 0;
-
- ResourceList::Iter iter;
- GrGpuResource* resource = iter.init(fResources, ResourceList::Iter::kHead_IterStart);
-
- for ( ; resource; resource = iter.next()) {
- if (!resource->isPurgable()) {
- ++locked;
- }
- if (resource->cacheAccess().isScratch()) {
- ++scratch;
- }
- }
-
- float countUtilization = (100.f * fCount) / fMaxCount;
- float byteUtilization = (100.f * fBytes) / fMaxBytes;
-
- SkDebugf("Budget: %d items %d bytes\n", fMaxCount, fMaxBytes);
- SkDebugf("\t\tEntry Count: current %d (%d locked, %d scratch %.2g%% full), high %d\n",
- fCount, locked, scratch, countUtilization, fHighWaterCount);
- SkDebugf("\t\tEntry Bytes: current %d (%.2g%% full) high %d\n",
- fBytes, byteUtilization, fHighWaterBytes);
-}
-
-#endif
#include "SkTMultiMap.h"
/**
- * Manages the lifetime of all GrGpuResource instances.
- *
- * 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
- * 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
- * allocation properties. They may not collide. The content key can be set after resource
- * creation. Currently it may only be set once and cannot be cleared. This restriction will
- * be removed.
- * If a resource has neither key type then it will be deleted as soon as the last reference to it
- * is dropped. If a key has both keys the content key takes precedence.
+ * Eventual replacement for GrResourceCache. Currently it simply holds a list
+ * of all GrGpuResource objects for a GrContext. It is used to invalidate all
+ * the resources when necessary.
*/
class GrResourceCache2 {
public:
- GrResourceCache2();
+ GrResourceCache2() : fCount(0) {};
~GrResourceCache2();
- /** Used to access functionality needed by GrGpuResource for lifetime management. */
- class ResourceAccess;
- ResourceAccess resourceAccess();
-
- /**
- * Sets the cache limits in terms of number of resources and max gpu memory byte size.
- */
- void setLimits(int count, size_t bytes);
-
- /**
- * Returns the number of cached resources.
- */
- int getResourceCount() const { return fCount; }
-
- /**
- * Returns the number of bytes consumed by cached resources.
- */
- size_t getResourceBytes() const { return fBytes; }
+ void insertResource(GrGpuResource*);
- /**
- * Returns the cached resources count budget.
- */
- int getMaxResourceCount() const { return fMaxCount; }
+ void removeResource(GrGpuResource*);
- /**
- * Returns the number of bytes consumed by cached resources.
- */
- size_t getMaxResourceBytes() const { return fMaxBytes; }
+ // This currently returns a bool and fails when an existing resource has a key that collides
+ // with the new content key. In the future it will null out the content key for the existing
+ // resource. The failure is a temporary measure taken because duties are split between two
+ // cache objects currently.
+ bool didSetContentKey(GrGpuResource*);
- /**
- * Abandons the backend API resources owned by all GrGpuResource objects and removes them from
- * the cache.
- */
void abandonAll();
- /**
- * Releases the backend API resources owned by all GrGpuResource objects and removes them from
- * the cache.
- */
void releaseAll();
enum {
/** Will not return any resources that match but have pending IO. */
kRequireNoPendingIO_ScratchFlag = 0x2,
};
-
- /**
- * Find a resource that matches a scratch key.
- */
GrGpuResource* findAndRefScratchResource(const GrResourceKey& scratchKey, uint32_t flags = 0);
#ifdef SK_DEBUG
}
#endif
- /**
- * Find a resource that matches a content key.
- */
GrGpuResource* findAndRefContentResource(const GrResourceKey& contentKey) {
SkASSERT(!contentKey.isScratch());
- GrGpuResource* resource = fContentHash.find(contentKey);
- if (resource) {
- resource->ref();
- this->makeResourceMRU(resource);
- }
- return resource;
+ return SkSafeRef(fContentHash.find(contentKey));
}
- /**
- * Query whether a content key exists in the cache.
- */
bool hasContentKey(const GrResourceKey& contentKey) const {
SkASSERT(!contentKey.isScratch());
return SkToBool(fContentHash.find(contentKey));
}
- /** Purges all resources that don't have external owners. */
- void purgeAllUnlocked();
-
- /**
- * The callback function used by the cache when it is still over budget after a purge. The
- * passed in 'data' is the same 'data' handed to setOverbudgetCallback.
- */
- typedef void (*PFOverBudgetCB)(void* data);
-
- /**
- * Set the callback the cache should use when it is still over budget after a purge. The 'data'
- * provided here will be passed back to the callback. Note that the cache will attempt to purge
- * any resources newly freed by the callback.
- */
- void setOverBudgetCallback(PFOverBudgetCB overBudgetCB, void* data) {
- fOverBudgetCB = overBudgetCB;
- fOverBudgetData = data;
- }
-
-#if GR_GPU_STATS
- void printStats() const;
-#endif
-
private:
- ///////////////////////////////////////////////////////////////////////////
- /// @name Methods accessible via ResourceAccess
- ////
- void insertResource(GrGpuResource*);
- void removeResource(GrGpuResource*);
- void notifyPurgable(const GrGpuResource*);
- void didChangeGpuMemorySize(const GrGpuResource*, size_t oldSize);
- bool didSetContentKey(GrGpuResource*);
- void makeResourceMRU(GrGpuResource*);
- /// @}
-
- void purgeAsNeeded() {
- if (fPurging || (fCount <= fMaxCount && fBytes < fMaxBytes)) {
- return;
- }
- this->internalPurgeAsNeeded();
- }
-
- void internalPurgeAsNeeded();
-
#ifdef SK_DEBUG
bool isInCache(const GrGpuResource* r) const { return fResources.isInList(r); }
- void validate() const;
-#else
- void validate() const {}
#endif
- class AutoValidate;
-
class AvailableForScratchUse;
struct ScratchMapTraits {
};
typedef SkTDynamicHash<GrGpuResource, GrResourceKey, ContentHashTraits> ContentHash;
- typedef SkTInternalLList<GrGpuResource> ResourceList;
-
- ResourceList fResources;
+ int fCount;
+ SkTInternalLList<GrGpuResource> fResources;
// This map holds all resources that can be used as scratch resources.
ScratchMap fScratchMap;
// This holds all resources that have content keys.
ContentHash fContentHash;
-
- // our budget, used in purgeAsNeeded()
- int fMaxCount;
- size_t fMaxBytes;
-
-#if GR_CACHE_STATS
- int fHighWaterCount;
- size_t fHighWaterBytes;
-#endif
-
- // our current stats, related to our budget
- int fCount;
- size_t fBytes;
-
- // prevents recursive purging
- bool fPurging;
- bool fNewlyPurgableResourceWhilePurging;
-
- PFOverBudgetCB fOverBudgetCB;
- void* fOverBudgetData;
-
};
-class GrResourceCache2::ResourceAccess {
-private:
- ResourceAccess(GrResourceCache2* cache) : fCache(cache) { }
- ResourceAccess(const ResourceAccess& that) : fCache(that.fCache) { }
- ResourceAccess& operator=(const ResourceAccess&); // unimpl
-
- /**
- * Insert a resource into the cache.
- */
- void insertResource(GrGpuResource* resource) { fCache->insertResource(resource); }
-
- /**
- * Removes a resource from the cache.
- */
- void removeResource(GrGpuResource* resource) { fCache->removeResource(resource); }
-
- /**
- * Called by GrGpuResources when they detects that they are newly purgable.
- */
- void notifyPurgable(const GrGpuResource* resource) { fCache->notifyPurgable(resource); }
-
- /**
- * Called by GrGpuResources when their sizes change.
- */
- void didChangeGpuMemorySize(const GrGpuResource* resource, size_t oldSize) {
- fCache->didChangeGpuMemorySize(resource, oldSize);
- }
-
- /**
- * Called by GrGpuResources when their content keys change.
- *
- * This currently returns a bool and fails when an existing resource has a key that collides
- * with the new content key. In the future it will null out the content key for the existing
- * resource. The failure is a temporary measure taken because duties are split between two
- * cache objects currently.
- */
- bool didSetContentKey(GrGpuResource* resource) { return fCache->didSetContentKey(resource); }
-
- // No taking addresses of this type.
- const ResourceAccess* operator&() const;
- ResourceAccess* operator&();
-
- GrResourceCache2* fCache;
-
- friend class GrGpuResource; // To access all the proxy inline methods.
- friend class GrResourceCache2; // To create this type.
-};
-
-inline GrResourceCache2::ResourceAccess GrResourceCache2::resourceAccess() {
- return ResourceAccess(this);
-}
-
#endif
#include "GrResourceCache2.h"
void GrStencilBuffer::transferToCache() {
+ SkASSERT(!this->cacheAccess().isInCache());
+
this->getGpu()->getContext()->addStencilBuffer(this);
}
#include "GrTest.h"
#include "GrInOrderDrawBuffer.h"
-#include "GrResourceCache2.h"
+#include "GrResourceCache.h"
void GrTestTarget::init(GrContext* ctx, GrDrawTarget* target) {
SkASSERT(!fContext);
}
void GrContext::purgeAllUnlockedResources() {
- fResourceCache2->purgeAllUnlocked();
+ fResourceCache->purgeAllUnlocked();
}
///////////////////////////////////////////////////////////////////////////////
#include "GrContext.h"
#include "GrDrawTargetCaps.h"
#include "GrGpu.h"
+#include "GrResourceCache.h"
#include "GrTexture.h"
#include "GrTexturePriv.h"
#include "SkMessageBus.h"
#include "SkPixelRef.h"
#include "SkTextureCompressor.h"
+#include "GrResourceCache.h"
#include "GrGpu.h"
#include "effects/GrDitherEffect.h"
#include "GrDrawTargetCaps.h"
#include "GrContext.h"
#include "GrContextFactory.h"
#include "GrGpu.h"
+#include "GrResourceCache.h"
#include "GrResourceCache2.h"
#include "SkCanvas.h"
-#include "SkGr.h"
-#include "SkMessageBus.h"
#include "SkSurface.h"
#include "Test.h"
SK_DECLARE_INST_COUNT(TestResource);
TestResource(GrGpu* gpu)
: INHERITED(gpu, false)
+ , fCache(NULL)
, fToDelete(NULL)
, fSize(kDefaultSize) {
++fNumAlive;
TestResource(GrGpu* gpu, const GrResourceKey& scratchKey)
: INHERITED(gpu, false)
+ , fCache(NULL)
, fToDelete(NULL)
, fSize(kDefaultSize) {
this->setScratchKey(scratchKey);
~TestResource() {
--fNumAlive;
- SkSafeUnref(fToDelete);
+ if (fToDelete) {
+ // Breaks our little 2-element cycle below.
+ fToDelete->setDeleteWhenDestroyed(NULL, NULL);
+ fCache->deleteResource(fToDelete->cacheAccess().getCacheEntry());
+ }
this->release();
}
static int NumAlive() { return fNumAlive; }
- void setUnrefWhenDestroyed(TestResource* resource) {
- SkRefCnt_SafeAssign(fToDelete, resource);
+ void setDeleteWhenDestroyed(GrResourceCache* cache, TestResource* resource) {
+ fCache = cache;
+ fToDelete = resource;
}
private:
size_t onGpuMemorySize() const SK_OVERRIDE { return fSize; }
+ GrResourceCache* fCache;
TestResource* fToDelete;
size_t fSize;
static int fNumAlive;
};
int TestResource::fNumAlive = 0;
-static void test_no_key(skiatest::Reporter* reporter) {
- SkAutoTUnref<GrContext> context(GrContext::CreateMockContext());
- REPORTER_ASSERT(reporter, SkToBool(context));
- if (NULL == context) {
- return;
- }
- context->setResourceCacheLimits(10, 30000);
- GrResourceCache2* cache2 = context->getResourceCache2();
- cache2->purgeAllUnlocked();
- SkASSERT(0 == cache2->getResourceCount() && 0 == cache2->getResourceBytes());
-
- // Create a bunch of resources with no keys
- TestResource* a = new TestResource(context->getGpu());
- TestResource* b = new TestResource(context->getGpu());
- TestResource* c = new TestResource(context->getGpu());
- TestResource* d = new TestResource(context->getGpu());
- a->setSize(11);
- b->setSize(12);
- c->setSize(13);
- d->setSize(14);
-
- REPORTER_ASSERT(reporter, 4 == TestResource::NumAlive());
- REPORTER_ASSERT(reporter, 4 == cache2->getResourceCount());
- REPORTER_ASSERT(reporter, a->gpuMemorySize() + b->gpuMemorySize() + c->gpuMemorySize() +
- d->gpuMemorySize() == cache2->getResourceBytes());
-
- // Should be safe to purge without deleting the resources since we still have refs.
- cache2->purgeAllUnlocked();
- REPORTER_ASSERT(reporter, 4 == TestResource::NumAlive());
-
- // Since the resources have neither content nor scratch keys, delete immediately upon unref.
-
- a->unref();
- REPORTER_ASSERT(reporter, 3 == TestResource::NumAlive());
- REPORTER_ASSERT(reporter, 3 == cache2->getResourceCount());
- REPORTER_ASSERT(reporter, b->gpuMemorySize() + c->gpuMemorySize() + d->gpuMemorySize() ==
- cache2->getResourceBytes());
-
- c->unref();
- REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive());
- REPORTER_ASSERT(reporter, 2 == cache2->getResourceCount());
- REPORTER_ASSERT(reporter, b->gpuMemorySize() + d->gpuMemorySize() ==
- cache2->getResourceBytes());
-
- d->unref();
- REPORTER_ASSERT(reporter, 1 == TestResource::NumAlive());
- REPORTER_ASSERT(reporter, 1 == cache2->getResourceCount());
- REPORTER_ASSERT(reporter, b->gpuMemorySize() == cache2->getResourceBytes());
-
- b->unref();
- REPORTER_ASSERT(reporter, 0 == TestResource::NumAlive());
- REPORTER_ASSERT(reporter, 0 == cache2->getResourceCount());
- REPORTER_ASSERT(reporter, 0 == cache2->getResourceBytes());
-}
-
static void test_duplicate_scratch_key(skiatest::Reporter* reporter) {
SkAutoTUnref<GrContext> context(GrContext::CreateMockContext());
REPORTER_ASSERT(reporter, SkToBool(context));
return;
}
context->setResourceCacheLimits(5, 30000);
- GrResourceCache2* cache2 = context->getResourceCache2();
- cache2->purgeAllUnlocked();
- SkASSERT(0 == cache2->getResourceCount() && 0 == cache2->getResourceBytes());
+ GrResourceCache* cache = context->getResourceCache();
+ SkDEBUGCODE(GrResourceCache2* cache2 = context->getResourceCache2();)
+ cache->purgeAllUnlocked();
+ SkASSERT(0 == cache->getCachedResourceCount() && 0 == cache->getCachedResourceBytes());
GrCacheID::Key keyData;
memset(&keyData, 0, sizeof(keyData));
a->setSize(11);
b->setSize(12);
// Scratch resources are registered with GrResourceCache2 just by existing. There are 2.
- REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive());
SkDEBUGCODE(REPORTER_ASSERT(reporter, 2 == cache2->countScratchEntriesForKey(scratchKey));)
- REPORTER_ASSERT(reporter, 2 == cache2->getResourceCount());
+
+ REPORTER_ASSERT(reporter, cache->addResource(scratchKey, a));
+
+ SkDEBUGCODE(REPORTER_ASSERT(reporter, 2 == cache2->countScratchEntriesForKey(scratchKey));)
+
+ // Can't add the same resource twice.
+ REPORTER_ASSERT(reporter, !cache->addResource(scratchKey, a));
+ REPORTER_ASSERT(reporter, 1 == cache->getCachedResourceCount());
+ REPORTER_ASSERT(reporter, a->gpuMemorySize() == cache->getCachedResourceBytes());
+ SkDEBUGCODE(REPORTER_ASSERT(reporter, 2 == cache2->countScratchEntriesForKey(scratchKey));)
+
+ // Add a second with the same key.
+ REPORTER_ASSERT(reporter, cache->addResource(scratchKey, b));
+ REPORTER_ASSERT(reporter, 2 == cache->getCachedResourceCount());
REPORTER_ASSERT(reporter, a->gpuMemorySize() + b->gpuMemorySize() ==
- cache2->getResourceBytes());
+ cache->getCachedResourceBytes());
+ REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive());
+ SkDEBUGCODE(REPORTER_ASSERT(reporter, 2 == cache2->countScratchEntriesForKey(scratchKey));)
// Our refs mean that the resources are non purgable.
- cache2->purgeAllUnlocked();
+ cache->purgeAllUnlocked();
REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive());
- REPORTER_ASSERT(reporter, 2 == cache2->getResourceCount());
+ REPORTER_ASSERT(reporter, 2 == cache->getCachedResourceCount());
// Unref but don't purge
a->unref();
SkDEBUGCODE(REPORTER_ASSERT(reporter, 2 == cache2->countScratchEntriesForKey(scratchKey));)
// Purge again. This time resources should be purgable.
- cache2->purgeAllUnlocked();
+ cache->purgeAllUnlocked();
REPORTER_ASSERT(reporter, 0 == TestResource::NumAlive());
- REPORTER_ASSERT(reporter, 0 == cache2->getResourceCount());
+ REPORTER_ASSERT(reporter, 0 == cache->getCachedResourceCount());
SkDEBUGCODE(REPORTER_ASSERT(reporter, 0 == cache2->countScratchEntriesForKey(scratchKey));)
}
return;
}
context->setResourceCacheLimits(5, 30000);
- GrResourceCache2* cache2 = context->getResourceCache2();
- cache2->purgeAllUnlocked();
- SkASSERT(0 == cache2->getResourceCount() && 0 == cache2->getResourceBytes());
+ GrResourceCache* cache = context->getResourceCache();
+ cache->purgeAllUnlocked();
+ SkASSERT(0 == cache->getCachedResourceCount() && 0 == cache->getCachedResourceBytes());
GrCacheID::Domain domain = GrCacheID::GenerateDomain();
GrCacheID::Key keyData;
GrResourceKey::ResourceType t = GrResourceKey::GenerateResourceType();
GrResourceKey key(GrCacheID(domain, keyData), t, 0);
+
// Create two resources that we will attempt to register with the same content key.
TestResource* a = new TestResource(context->getGpu());
TestResource* b = new TestResource(context->getGpu());
a->setSize(11);
b->setSize(12);
-
- // Can't set the same content key on two resources.
- REPORTER_ASSERT(reporter, a->cacheAccess().setContentKey(key));
- REPORTER_ASSERT(reporter, !b->cacheAccess().setContentKey(key));
-
- // Still have two resources because b is still reffed.
- REPORTER_ASSERT(reporter, 2 == cache2->getResourceCount());
- REPORTER_ASSERT(reporter, a->gpuMemorySize() + b->gpuMemorySize() ==
- cache2->getResourceBytes());
+ REPORTER_ASSERT(reporter, cache->addResource(key, a));
+ // Can't add the same or another resource with the same key.
+ REPORTER_ASSERT(reporter, !cache->addResource(key, a));
+ REPORTER_ASSERT(reporter, !cache->addResource(key, b));
+ REPORTER_ASSERT(reporter, 1 == cache->getCachedResourceCount());
+ REPORTER_ASSERT(reporter, a->gpuMemorySize() == cache->getCachedResourceBytes());
REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive());
b->unref();
- // Now b should be gone.
- REPORTER_ASSERT(reporter, 1 == cache2->getResourceCount());
- REPORTER_ASSERT(reporter, a->gpuMemorySize() == cache2->getResourceBytes());
- REPORTER_ASSERT(reporter, 1 == TestResource::NumAlive());
-
- cache2->purgeAllUnlocked();
- REPORTER_ASSERT(reporter, 1 == cache2->getResourceCount());
- REPORTER_ASSERT(reporter, a->gpuMemorySize() == cache2->getResourceBytes());
+ cache->purgeAllUnlocked();
+ a->setSize(10);
+ REPORTER_ASSERT(reporter, 1 == cache->getCachedResourceCount());
REPORTER_ASSERT(reporter, 1 == TestResource::NumAlive());
- // Drop the ref on a but it isn't immediately purged as it still has a valid scratch key.
a->unref();
- REPORTER_ASSERT(reporter, 1 == cache2->getResourceCount());
- REPORTER_ASSERT(reporter, a->gpuMemorySize() == cache2->getResourceBytes());
- REPORTER_ASSERT(reporter, 1 == TestResource::NumAlive());
-
- cache2->purgeAllUnlocked();
- REPORTER_ASSERT(reporter, 0 == cache2->getResourceCount());
- REPORTER_ASSERT(reporter, 0 == cache2->getResourceBytes());
+ cache->purgeAllUnlocked();
+ REPORTER_ASSERT(reporter, 0 == cache->getCachedResourceCount());
+ REPORTER_ASSERT(reporter, 0 == cache->getCachedResourceBytes());
REPORTER_ASSERT(reporter, 0 == TestResource::NumAlive());
}
GrResourceKey key3(GrCacheID(domain, keyData), t, 0);
context->setResourceCacheLimits(5, 30000);
+ GrResourceCache* cache = context->getResourceCache();
GrResourceCache2* cache2 = context->getResourceCache2();
- cache2->purgeAllUnlocked();
- SkASSERT(0 == cache2->getResourceCount() && 0 == cache2->getResourceBytes());
+ cache->purgeAllUnlocked();
+ SkASSERT(0 == cache->getCachedResourceCount() && 0 == cache->getCachedResourceBytes());
// Add three resources to the cache.
TestResource* a = new TestResource(context->getGpu());
TestResource* b = new TestResource(context->getGpu());
TestResource* c = new TestResource(context->getGpu());
- a->cacheAccess().setContentKey(key1);
- b->cacheAccess().setContentKey(key2);
- c->cacheAccess().setContentKey(key3);
+ cache->addResource(key1, a);
+ cache->addResource(key2, b);
+ cache->addResource(key3, c);
a->unref();
b->unref();
c->unref();
SkMessageBus<GrResourceInvalidatedMessage>::Post(msg1);
const GrResourceInvalidatedMessage msg2 = { key2 };
SkMessageBus<GrResourceInvalidatedMessage>::Post(msg2);
+ cache->purgeAsNeeded();
#if 0 // Disabled until reimplemented in GrResourceCache2.
- cache2->purgeAsNeeded();
REPORTER_ASSERT(reporter, 1 == TestResource::NumAlive());
REPORTER_ASSERT(reporter, !cache2->hasContentKey(key1));
REPORTER_ASSERT(reporter, !cache2->hasContentKey(key2));
// Invalidate the third.
const GrResourceInvalidatedMessage msg3 = { key3 };
SkMessageBus<GrResourceInvalidatedMessage>::Post(msg3);
+ cache->purgeAsNeeded();
#if 0 // Disabled until reimplemented in GrResourceCache2.
- cache2->purgeAsNeeded();
REPORTER_ASSERT(reporter, 0 == TestResource::NumAlive());
REPORTER_ASSERT(reporter, !cache2->hasContentKey(key3));
#endif
-
- cache2->purgeAllUnlocked();
- REPORTER_ASSERT(reporter, 0 == TestResource::NumAlive());
- REPORTER_ASSERT(reporter, 0 == cache2->getResourceCount());
- REPORTER_ASSERT(reporter, 0 == cache2->getResourceBytes());
}
-static void test_cache_chained_purge(skiatest::Reporter* reporter) {
+static void test_cache_delete_on_destruction(skiatest::Reporter* reporter) {
SkAutoTUnref<GrContext> context(GrContext::CreateMockContext());
REPORTER_ASSERT(reporter, SkToBool(context));
if (NULL == context) {
{
context->setResourceCacheLimits(3, 30000);
- GrResourceCache2* cache2 = context->getResourceCache2();
- cache2->purgeAllUnlocked();
- SkASSERT(0 == cache2->getResourceCount() && 0 == cache2->getResourceBytes());
+ GrResourceCache* cache = context->getResourceCache();
+ cache->purgeAllUnlocked();
+ SkASSERT(0 == cache->getCachedResourceCount() && 0 == cache->getCachedResourceBytes());
TestResource* a = new TestResource(context->getGpu());
TestResource* b = new TestResource(context->getGpu());
- a->cacheAccess().setContentKey(key1);
- b->cacheAccess().setContentKey(key2);
-
- // Make a cycle
- a->setUnrefWhenDestroyed(b);
- b->setUnrefWhenDestroyed(a);
+ cache->addResource(key1, a);
+ cache->addResource(key2, b);
- REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive());
+ a->setDeleteWhenDestroyed(cache, b);
+ b->setDeleteWhenDestroyed(cache, a);
a->unref();
b->unref();
REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive());
- cache2->purgeAllUnlocked();
- REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive());
+ cache->purgeAllUnlocked();
+ REPORTER_ASSERT(reporter, 0 == TestResource::NumAlive());
+ }
+ {
+ context->setResourceCacheLimits(3, 30000);
+ GrResourceCache* cache = context->getResourceCache();
+ cache->purgeAllUnlocked();
+ SkASSERT(0 == cache->getCachedResourceCount() && 0 == cache->getCachedResourceBytes());
- // Break the cycle
- a->setUnrefWhenDestroyed(NULL);
- REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive());
+ TestResource* a = new TestResource(context->getGpu());
+ TestResource* b = new TestResource(context->getGpu());
+ cache->addResource(key1, a);
+ cache->addResource(key2, b);
+
+ a->setDeleteWhenDestroyed(cache, b);
+ b->setDeleteWhenDestroyed(cache, a);
- cache2->purgeAllUnlocked();
+ a->unref();
+ b->unref();
+
+ cache->deleteResource(a->cacheAccess().getCacheEntry());
REPORTER_ASSERT(reporter, 0 == TestResource::NumAlive());
}
}
// Test changing resources sizes (both increase & decrease).
{
context->setResourceCacheLimits(3, 30000);
+ GrResourceCache* cache = context->getResourceCache();
GrResourceCache2* cache2 = context->getResourceCache2();
- cache2->purgeAllUnlocked();
- SkASSERT(0 == cache2->getResourceCount() && 0 == cache2->getResourceBytes());
+ cache->purgeAllUnlocked();
+ SkASSERT(0 == cache->getCachedResourceCount() && 0 == cache->getCachedResourceBytes());
TestResource* a = new TestResource(context->getGpu());
- a->cacheAccess().setContentKey(key1);
+ a->setSize(100); // Test didChangeGpuMemorySize() when not in the cache.
+ cache->addResource(key1, a);
a->unref();
TestResource* b = new TestResource(context->getGpu());
- b->cacheAccess().setContentKey(key2);
+ b->setSize(100);
+ cache->addResource(key2, b);
b->unref();
- REPORTER_ASSERT(reporter, 200 == cache2->getResourceBytes());
- REPORTER_ASSERT(reporter, 2 == cache2->getResourceCount());
+ REPORTER_ASSERT(reporter, 200 == cache->getCachedResourceBytes());
+ REPORTER_ASSERT(reporter, 2 == cache->getCachedResourceCount());
{
SkAutoTUnref<TestResource> find2(static_cast<TestResource*>(cache2->findAndRefContentResource(key2)));
find2->setSize(200);
find1->setSize(50);
}
- REPORTER_ASSERT(reporter, 250 == cache2->getResourceBytes());
- REPORTER_ASSERT(reporter, 2 == cache2->getResourceCount());
+ REPORTER_ASSERT(reporter, 250 == cache->getCachedResourceBytes());
+ REPORTER_ASSERT(reporter, 2 == cache->getCachedResourceCount());
}
// Test increasing a resources size beyond the cache budget.
{
context->setResourceCacheLimits(2, 300);
+ GrResourceCache* cache = context->getResourceCache();
GrResourceCache2* cache2 = context->getResourceCache2();
- cache2->purgeAllUnlocked();
- SkASSERT(0 == cache2->getResourceCount() && 0 == cache2->getResourceBytes());
+ cache->purgeAllUnlocked();
+ SkASSERT(0 == cache->getCachedResourceCount() && 0 == cache->getCachedResourceBytes());
TestResource* a = new TestResource(context->getGpu());
a->setSize(100);
- a->cacheAccess().setContentKey(key1);
+ cache->addResource(key1, a);
a->unref();
TestResource* b = new TestResource(context->getGpu());
b->setSize(100);
- b->cacheAccess().setContentKey(key2);
+ cache->addResource(key2, b);
b->unref();
- REPORTER_ASSERT(reporter, 200 == cache2->getResourceBytes());
- REPORTER_ASSERT(reporter, 2 == cache2->getResourceCount());
+ REPORTER_ASSERT(reporter, 200 == cache->getCachedResourceBytes());
+ REPORTER_ASSERT(reporter, 2 == cache->getCachedResourceCount());
{
SkAutoTUnref<TestResource> find2(static_cast<TestResource*>(cache2->findAndRefContentResource(key2)));
}
REPORTER_ASSERT(reporter, !cache2->hasContentKey(key1));
- REPORTER_ASSERT(reporter, 201 == cache2->getResourceBytes());
- REPORTER_ASSERT(reporter, 1 == cache2->getResourceCount());
+ REPORTER_ASSERT(reporter, 201 == cache->getCachedResourceBytes());
+ REPORTER_ASSERT(reporter, 1 == cache->getCachedResourceCount());
}
}
}
// The below tests create their own mock contexts.
- test_no_key(reporter);
test_duplicate_content_key(reporter);
test_duplicate_scratch_key(reporter);
test_purge_invalidated(reporter);
- test_cache_chained_purge(reporter);
+ test_cache_delete_on_destruction(reporter);
test_resource_size_changed(reporter);
}
}
#if SK_SUPPORT_GPU
+static void TestSurfaceInCache(skiatest::Reporter* reporter,
+ SurfaceType surfaceType,
+ GrContext* context) {
+ context->freeGpuResources();
+ int resourceCount;
+
+ context->getResourceCacheUsage(&resourceCount, NULL);
+ REPORTER_ASSERT(reporter, 0 == resourceCount);
+ SkAutoTUnref<SkSurface> surface(createSurface(surfaceType, context));
+ // Note: the stencil buffer is always cached, so kGpu_SurfaceType uses
+ // one cached resource, and kGpuScratch_SurfaceType uses two.
+ int expectedCachedResources = surfaceType == kGpuScratch_SurfaceType ? 2 : 1;
+ context->getResourceCacheUsage(&resourceCount, NULL);
+ REPORTER_ASSERT(reporter, expectedCachedResources == resourceCount);
+
+ // Verify that all the cached resources are locked in cache.
+ context->freeGpuResources();
+ context->getResourceCacheUsage(&resourceCount, NULL);
+ REPORTER_ASSERT(reporter, expectedCachedResources == resourceCount);
+
+ // Verify that all the cached resources are unlocked upon surface release
+ surface.reset(0);
+ context->freeGpuResources();
+ context->getResourceCacheUsage(&resourceCount, NULL);
+ REPORTER_ASSERT(reporter, 0 == resourceCount);
+}
+
static void Test_crbug263329(skiatest::Reporter* reporter,
SurfaceType surfaceType,
GrContext* context) {
}
GrContext* context = factory->get(glCtxType);
if (context) {
+ TestSurfaceInCache(reporter, kGpu_SurfaceType, context);
+ TestSurfaceInCache(reporter, kGpuScratch_SurfaceType, context);
Test_crbug263329(reporter, kGpu_SurfaceType, context);
Test_crbug263329(reporter, kGpuScratch_SurfaceType, context);
TestSurfaceCopyOnWrite(reporter, kGpu_SurfaceType, context);