Revert of Replace GrResourceCache with GrResourceCache2. (patchset #6 id:100001 of https://codereview.chromium.org/716143004/)

Reason for revert:
Breaking stuff

Original issue's description:
> Replace GrResourceCache with GrResourceCache2.
>
> BUG=skia:2889
>
> Committed: https://skia.googlesource.com/skia/+/66a450f21a3da174b7eed89a1d5fc8591e8b6ee6

TBR=robertphillips@google.com
NOTREECHECKS=true
NOTRY=true
BUG=skia:2889

Review URL: https://codereview.chromium.org/715333003
This commit is contained in:
bsalomon 2014-11-13 13:33:28 -08:00 committed by Commit bot
parent 66a450f21a
commit f21dab9540
24 changed files with 1014 additions and 720 deletions

View File

@ -13,6 +13,7 @@
#include "GrGpuResource.h"
#include "GrContext.h"
#include "GrGpu.h"
#include "GrResourceCache.h"
#include "GrResourceCache2.h"
#include "GrStencilBuffer.h"
#include "GrTexture.h"
@ -88,20 +89,22 @@ static void get_stencil(int i, int* w, int* h, int* s) {
}
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();
}
@ -110,7 +113,8 @@ static void populate_cache(GrGpu* gpu, int resourceCount) {
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();
}
}
@ -194,24 +198,25 @@ protected:
// 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();
}
}
@ -242,15 +247,16 @@ protected:
// 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) {

View File

@ -63,6 +63,16 @@
],
},
}],
[ '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': [

View File

@ -136,6 +136,8 @@
'<(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',

View File

@ -212,6 +212,24 @@ typedef unsigned __int64 uint64_t;
#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.

View File

@ -33,6 +33,7 @@ class GrOvalRenderer;
class GrPath;
class GrPathRenderer;
class GrResourceEntry;
class GrResourceCache;
class GrResourceCache2;
class GrStencilBuffer;
class GrTestTarget;
@ -882,6 +883,7 @@ public:
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
@ -946,6 +948,7 @@ private:
const GrClipData* fClip; // TODO: make this ref counted
GrDrawState* fDrawState;
GrResourceCache* fResourceCache;
GrResourceCache2* fResourceCache2;
GrFontCache* fFontCache;
SkAutoTDelete<GrLayerCache> fLayerCache;
@ -957,6 +960,9 @@ private:
GrIndexBufferAllocPool* fDrawBufferIBAllocPool;
GrInOrderDrawBuffer* fDrawBuffer;
// Set by OverbudgetCB() to request that GrContext flush before exiting a draw.
bool fFlushToReduceCacheSize;
GrAARectRenderer* fAARectRenderer;
GrOvalRenderer* fOvalRenderer;
@ -983,9 +989,10 @@ private:
void setupDrawBuffer();
class AutoRestoreEffects;
class AutoCheckFlush;
/// Sets the paint and returns the target to draw into. The paint can be NULL in which case the
/// draw state is left unmodified.
GrDrawTarget* prepareToDraw(const GrPaint*, AutoRestoreEffects*);
GrDrawTarget* prepareToDraw(const GrPaint*, AutoRestoreEffects*, AutoCheckFlush*);
void internalDrawPath(GrDrawTarget* target, bool useAA, const SkPath& path,
const GrStrokeInfo& stroke);
@ -996,6 +1003,8 @@ private:
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
@ -1006,9 +1015,9 @@ private:
/**
* 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;
};

View File

@ -16,6 +16,7 @@
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
@ -125,7 +126,7 @@ private:
};
/**
* 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:
@ -256,6 +257,7 @@ private:
uint32_t fFlags;
GrResourceCacheEntry* fCacheEntry; // NULL if not in cache
mutable size_t fGpuMemorySize;
const uint32_t fUniqueID;

View File

@ -19,4 +19,16 @@
*/
//#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

View File

@ -68,11 +68,6 @@ static inline GrColor SkColor2GrColorJustAlpha(SkColor c) {
////////////////////////////////////////////////////////////////////////////////
// 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*);

View File

@ -15,7 +15,6 @@
#include "GrRect.h"
#include "SkChecksum.h"
#include "SkTDynamicHash.h"
class GrContext;
class GrPlot;

View File

@ -25,6 +25,7 @@
#include "GrOvalRenderer.h"
#include "GrPathRenderer.h"
#include "GrPathUtils.h"
#include "GrResourceCache.h"
#include "GrResourceCache2.h"
#include "GrSoftwarePathRenderer.h"
#include "GrStencilBuffer.h"
@ -51,6 +52,9 @@
#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;
@ -62,6 +66,20 @@ static const int DRAW_BUFFER_IBPOOL_PREALLOC_BUFFERS = 4;
// Glorified typedef to avoid including GrDrawState.h in GrContext.h
class GrContext::AutoRestoreEffects : public GrDrawState::AutoRestoreEffects {};
class GrContext::AutoCheckFlush {
public:
AutoCheckFlush(GrContext* context) : fContext(context) { SkASSERT(context); }
~AutoCheckFlush() {
if (fContext->fFlushToReduceCacheSize) {
fContext->flush();
}
}
private:
GrContext* fContext;
};
GrContext* GrContext::Create(GrBackend backend, GrBackendContext backendContext,
const Options* opts) {
GrContext* context;
@ -85,11 +103,13 @@ GrContext::GrContext(const Options& opts) : fOptions(opts) {
fClip = NULL;
fPathRendererChain = NULL;
fSoftwarePathRenderer = NULL;
fResourceCache = NULL;
fResourceCache2 = NULL;
fFontCache = NULL;
fDrawBuffer = NULL;
fDrawBufferVBAllocPool = NULL;
fDrawBufferIBAllocPool = NULL;
fFlushToReduceCacheSize = false;
fAARectRenderer = NULL;
fOvalRenderer = NULL;
fViewMatrix.reset();
@ -110,8 +130,11 @@ bool GrContext::init(GrBackend backend, GrBackendContext backendContext) {
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));
@ -137,6 +160,9 @@ GrContext::~GrContext() {
}
SkDELETE(fResourceCache2);
fResourceCache2 = NULL;
SkDELETE(fResourceCache);
fResourceCache = NULL;
SkDELETE(fFontCache);
SkDELETE(fDrawBuffer);
SkDELETE(fDrawBufferVBAllocPool);
@ -175,6 +201,8 @@ void GrContext::abandonContext() {
fAARectRenderer->reset();
fOvalRenderer->reset();
fResourceCache->purgeAllUnlocked();
fFontCache->freeAll();
fLayerCache->freeAll();
}
@ -193,6 +221,7 @@ void GrContext::freeGpuResources() {
fAARectRenderer->reset();
fOvalRenderer->reset();
fResourceCache->purgeAllUnlocked();
fFontCache->freeAll();
fLayerCache->freeAll();
// a path renderer may be holding onto resources
@ -201,12 +230,12 @@ void GrContext::freeGpuResources() {
}
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,
@ -244,13 +273,12 @@ bool GrContext::isTextureInCache(const GrSurfaceDesc& desc,
}
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) {
@ -392,19 +420,25 @@ GrTexture* GrContext::createTexture(const GrTextureParams* params,
}
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.
@ -439,6 +473,7 @@ GrTexture* GrContext::refScratchTexture(const GrSurfaceDesc& inDesc, ScratchTexM
}
GrGpuResource* resource = fResourceCache2->findAndRefScratchResource(key, scratchFlags);
if (resource) {
fResourceCache->makeResourceMRU(resource);
return static_cast<GrSurface*>(resource)->asTexture();
}
@ -461,17 +496,21 @@ GrTexture* GrContext::refScratchTexture(const GrSurfaceDesc& inDesc, ScratchTexM
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) {
// Flush the InOrderDrawBuffer to possibly free up some textures
bool GrContext::OverbudgetCB(void* data) {
SkASSERT(data);
GrContext* context = reinterpret_cast<GrContext*>(data);
context->flush();
// Flush the InOrderDrawBuffer to possibly free up some textures
context->fFlushToReduceCacheSize = true;
return true;
}
@ -483,16 +522,11 @@ GrTexture* GrContext::createUncachedTexture(const GrSurfaceDesc& descIn,
}
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 {
@ -548,8 +582,9 @@ void GrContext::clear(const SkIRect* rect,
SkASSERT(renderTarget);
AutoRestoreEffects are;
AutoCheckFlush acf(this);
GR_CREATE_TRACE_MARKER_CONTEXT("GrContext::clear", this);
GrDrawTarget* target = this->prepareToDraw(NULL, &are);
GrDrawTarget* target = this->prepareToDraw(NULL, &are, &acf);
if (NULL == target) {
return;
}
@ -681,7 +716,8 @@ void GrContext::drawRect(const GrPaint& paint,
}
AutoRestoreEffects are;
GrDrawTarget* target = this->prepareToDraw(&paint, &are);
AutoCheckFlush acf(this);
GrDrawTarget* target = this->prepareToDraw(&paint, &are, &acf);
if (NULL == target) {
return;
}
@ -791,7 +827,8 @@ void GrContext::drawRectToRect(const GrPaint& paint,
const SkRect& localRect,
const SkMatrix* localMatrix) {
AutoRestoreEffects are;
GrDrawTarget* target = this->prepareToDraw(&paint, &are);
AutoCheckFlush acf(this);
GrDrawTarget* target = this->prepareToDraw(&paint, &are, &acf);
if (NULL == target) {
return;
}
@ -854,9 +891,10 @@ void GrContext::drawVertices(const GrPaint& paint,
const uint16_t indices[],
int indexCount) {
AutoRestoreEffects are;
GrDrawTarget::AutoReleaseGeometry geo;
AutoCheckFlush acf(this);
GrDrawTarget::AutoReleaseGeometry geo; // must be inside AutoCheckFlush scope
GrDrawTarget* target = this->prepareToDraw(&paint, &are);
GrDrawTarget* target = this->prepareToDraw(&paint, &are, &acf);
if (NULL == target) {
return;
}
@ -916,7 +954,8 @@ void GrContext::drawRRect(const GrPaint& paint,
}
AutoRestoreEffects are;
GrDrawTarget* target = this->prepareToDraw(&paint, &are);
AutoCheckFlush acf(this);
GrDrawTarget* target = this->prepareToDraw(&paint, &are, &acf);
if (NULL == target) {
return;
}
@ -942,7 +981,8 @@ void GrContext::drawDRRect(const GrPaint& paint,
}
AutoRestoreEffects are;
GrDrawTarget* target = this->prepareToDraw(&paint, &are);
AutoCheckFlush acf(this);
GrDrawTarget* target = this->prepareToDraw(&paint, &are, &acf);
GR_CREATE_TRACE_MARKER("GrContext::drawDRRect", target);
@ -974,7 +1014,8 @@ void GrContext::drawOval(const GrPaint& paint,
}
AutoRestoreEffects are;
GrDrawTarget* target = this->prepareToDraw(&paint, &are);
AutoCheckFlush acf(this);
GrDrawTarget* target = this->prepareToDraw(&paint, &are, &acf);
if (NULL == target) {
return;
}
@ -1061,7 +1102,8 @@ void GrContext::drawPath(const GrPaint& paint, const SkPath& path, const GrStrok
SkPoint pts[2];
if (path.isLine(pts)) {
AutoRestoreEffects are;
GrDrawTarget* target = this->prepareToDraw(&paint, &are);
AutoCheckFlush acf(this);
GrDrawTarget* target = this->prepareToDraw(&paint, &are, &acf);
if (NULL == target) {
return;
}
@ -1097,7 +1139,8 @@ void GrContext::drawPath(const GrPaint& paint, const SkPath& path, const GrStrok
// the writePixels that uploads to the scratch will perform a flush so we're
// OK.
AutoRestoreEffects are;
GrDrawTarget* target = this->prepareToDraw(&paint, &are);
AutoCheckFlush acf(this);
GrDrawTarget* target = this->prepareToDraw(&paint, &are, &acf);
if (NULL == target) {
return;
}
@ -1199,6 +1242,8 @@ void GrContext::flush(int flagsBitfield) {
} else {
fDrawBuffer->flush();
}
fResourceCache->purgeAsNeeded();
fFlushToReduceCacheSize = false;
}
bool sw_convert_to_premul(GrPixelConfig srcConfig, int width, int height, size_t inRowBytes,
@ -1318,7 +1363,7 @@ bool GrContext::writeSurfacePixels(GrSurface* surface,
// drawing a rect to the render target.
// The bracket ensures we pop the stack if we wind up flushing below.
{
GrDrawTarget* drawTarget = this->prepareToDraw(NULL, NULL);
GrDrawTarget* drawTarget = this->prepareToDraw(NULL, NULL, NULL);
GrDrawTarget::AutoGeometryAndStatePush agasp(drawTarget, GrDrawTarget::kReset_ASRInit,
&matrix);
GrDrawState* drawState = drawTarget->drawState();
@ -1498,7 +1543,8 @@ void GrContext::discardRenderTarget(GrRenderTarget* renderTarget) {
SkASSERT(renderTarget);
ASSERT_OWNED_RESOURCE(renderTarget);
AutoRestoreEffects are;
GrDrawTarget* target = this->prepareToDraw(NULL, &are);
AutoCheckFlush acf(this);
GrDrawTarget* target = this->prepareToDraw(NULL, &are, &acf);
if (NULL == target) {
return;
}
@ -1516,7 +1562,7 @@ void GrContext::copySurface(GrSurface* dst, GrSurface* src, const SkIRect& srcRe
// Since we're going to the draw target and not GPU, no need to check kNoFlush
// here.
GrDrawTarget* target = this->prepareToDraw(NULL, NULL);
GrDrawTarget* target = this->prepareToDraw(NULL, NULL, NULL);
if (NULL == target) {
return;
}
@ -1535,7 +1581,9 @@ void GrContext::flushSurfaceWrites(GrSurface* surface) {
////////////////////////////////////////////////////////////////////////////////
GrDrawTarget* GrContext::prepareToDraw(const GrPaint* paint, AutoRestoreEffects* are) {
GrDrawTarget* GrContext::prepareToDraw(const GrPaint* paint,
AutoRestoreEffects* are,
AutoCheckFlush* acf) {
// All users of this draw state should be freeing up all effects when they're done.
// Otherwise effects that own resources may keep those resources alive indefinitely.
SkASSERT(0 == fDrawState->numColorStages() && 0 == fDrawState->numCoverageStages() &&
@ -1548,6 +1596,7 @@ GrDrawTarget* GrContext::prepareToDraw(const GrPaint* paint, AutoRestoreEffects*
ASSERT_OWNED_RESOURCE(fRenderTarget.get());
if (paint) {
SkASSERT(are);
SkASSERT(acf);
are->set(fDrawState);
fDrawState->setFromPaint(*paint, fViewMatrix, fRenderTarget.get());
#if GR_DEBUG_PARTIAL_COVERAGE_CHECK
@ -1647,7 +1696,7 @@ void GrContext::setupDrawBuffer() {
}
GrDrawTarget* GrContext::getTextTarget() {
return this->prepareToDraw(NULL, NULL);
return this->prepareToDraw(NULL, NULL, NULL);
}
const GrIndexBuffer* GrContext::getQuadIndexBuffer() const {
@ -1697,11 +1746,15 @@ const GrFragmentProcessor* GrContext::createUPMToPMEffect(GrTexture* texture,
}
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) {
@ -1721,7 +1774,7 @@ void GrContext::removeGpuTraceMarker(const GrGpuTraceMarker* marker) {
///////////////////////////////////////////////////////////////////////////////
#if GR_CACHE_STATS
void GrContext::printCacheStats() const {
fResourceCache2->printStats();
fResourceCache->printStats();
}
#endif

View File

@ -18,8 +18,16 @@ static inline GrResourceCache2* get_resource_cache2(GrGpu* gpu) {
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())
@ -32,7 +40,7 @@ GrGpuResource::GrGpuResource(GrGpu* gpu, bool isWrapped)
}
void GrGpuResource::registerWithCache() {
get_resource_cache2(fGpu)->resourceAccess().insertResource(this);
get_resource_cache2(fGpu)->insertResource(this);
}
GrGpuResource::~GrGpuResource() {
@ -43,18 +51,16 @@ 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;
}
}
@ -74,17 +80,6 @@ GrContext* GrGpuResource::getContext() {
}
}
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());
@ -97,7 +92,7 @@ bool GrGpuResource::setContentKey(const GrResourceKey& contentKey) {
fContentKey = contentKey;
fContentKeySet = true;
if (!get_resource_cache2(fGpu)->resourceAccess().didSetContentKey(this)) {
if (!get_resource_cache2(fGpu)->didSetContentKey(this)) {
fContentKeySet = false;
return false;
}
@ -105,8 +100,8 @@ bool GrGpuResource::setContentKey(const GrResourceKey& contentKey) {
}
void GrGpuResource::notifyIsPurgable() const {
if (!this->wasDestroyed()) {
get_resource_cache2(fGpu)->resourceAccess().notifyPurgable(this);
if (fCacheEntry && !this->wasDestroyed()) {
get_resource_cache(fGpu)->notifyPurgable(this);
}
}

View File

@ -28,6 +28,25 @@ public:
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.

View File

@ -9,6 +9,7 @@
#define GrPath_DEFINED
#include "GrGpuResource.h"
#include "GrResourceCache.h"
#include "SkPath.h"
#include "SkRect.h"
#include "SkStrokeRec.h"

View File

@ -9,6 +9,7 @@
#define GrPathRange_DEFINED
#include "GrGpuResource.h"
#include "GrResourceCache.h"
#include "SkRefCnt.h"
#include "SkStrokeRec.h"
#include "SkTArray.h"

393
src/gpu/GrResourceCache.cpp Normal file
View File

@ -0,0 +1,393 @@
/*
* 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
///////////////////////////////////////////////////////////////////////////////

251
src/gpu/GrResourceCache.h Normal file
View File

@ -0,0 +1,251 @@
/*
* 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

View File

@ -10,13 +10,6 @@
#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);
@ -34,85 +27,26 @@ GrCacheID::Domain GrResourceKey::ScratchDomain() {
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()) {
@ -121,16 +55,13 @@ void GrResourceCache2::removeResource(GrGpuResource* resource) {
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());
}
@ -140,13 +71,9 @@ void GrResourceCache2::abandonAll() {
}
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());
}
@ -172,16 +99,11 @@ private:
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;
@ -189,18 +111,11 @@ GrGpuResource* GrResourceCache2::findAndRefScratchResource(const GrResourceKey&
// 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());
@ -210,214 +125,5 @@ bool GrResourceCache2::didSetContentKey(GrGpuResource* resource) {
}
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

View File

@ -17,65 +17,27 @@
#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();
void insertResource(GrGpuResource*);
/**
* Sets the cache limits in terms of number of resources and max gpu memory byte size.
*/
void setLimits(int count, size_t bytes);
void removeResource(GrGpuResource*);
/**
* Returns the number of cached resources.
*/
int getResourceCount() const { return fCount; }
// 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*);
/**
* Returns the number of bytes consumed by cached resources.
*/
size_t getResourceBytes() const { return fBytes; }
/**
* Returns the cached resources count budget.
*/
int getMaxResourceCount() const { return fMaxCount; }
/**
* Returns the number of bytes consumed by cached resources.
*/
size_t getMaxResourceBytes() const { return fMaxBytes; }
/**
* 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 {
@ -84,10 +46,6 @@ public:
/** 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
@ -98,80 +56,21 @@ public:
}
#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 {
@ -192,86 +91,12 @@ private:
};
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

View File

@ -13,6 +13,8 @@
#include "GrResourceCache2.h"
void GrStencilBuffer::transferToCache() {
SkASSERT(!this->cacheAccess().isInCache());
this->getGpu()->getContext()->addStencilBuffer(this);
}

View File

@ -9,7 +9,7 @@
#include "GrTest.h"
#include "GrInOrderDrawBuffer.h"
#include "GrResourceCache2.h"
#include "GrResourceCache.h"
void GrTestTarget::init(GrContext* ctx, GrDrawTarget* target) {
SkASSERT(!fContext);
@ -38,7 +38,7 @@ void GrContext::setMaxTextureSizeOverride(int maxTextureSizeOverride) {
}
void GrContext::purgeAllUnlockedResources() {
fResourceCache2->purgeAllUnlocked();
fResourceCache->purgeAllUnlocked();
}
///////////////////////////////////////////////////////////////////////////////

View File

@ -9,6 +9,7 @@
#include "GrContext.h"
#include "GrDrawTargetCaps.h"
#include "GrGpu.h"
#include "GrResourceCache.h"
#include "GrTexture.h"
#include "GrTexturePriv.h"

View File

@ -12,6 +12,7 @@
#include "SkMessageBus.h"
#include "SkPixelRef.h"
#include "SkTextureCompressor.h"
#include "GrResourceCache.h"
#include "GrGpu.h"
#include "effects/GrDitherEffect.h"
#include "GrDrawTargetCaps.h"

View File

@ -10,10 +10,9 @@
#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"
@ -68,6 +67,7 @@ public:
SK_DECLARE_INST_COUNT(TestResource);
TestResource(GrGpu* gpu)
: INHERITED(gpu, false)
, fCache(NULL)
, fToDelete(NULL)
, fSize(kDefaultSize) {
++fNumAlive;
@ -76,6 +76,7 @@ public:
TestResource(GrGpu* gpu, const GrResourceKey& scratchKey)
: INHERITED(gpu, false)
, fCache(NULL)
, fToDelete(NULL)
, fSize(kDefaultSize) {
this->setScratchKey(scratchKey);
@ -85,7 +86,11 @@ public:
~TestResource() {
--fNumAlive;
SkSafeUnref(fToDelete);
if (fToDelete) {
// Breaks our little 2-element cycle below.
fToDelete->setDeleteWhenDestroyed(NULL, NULL);
fCache->deleteResource(fToDelete->cacheAccess().getCacheEntry());
}
this->release();
}
@ -96,13 +101,15 @@ public:
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;
@ -111,61 +118,6 @@ private:
};
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));
@ -173,9 +125,10 @@ static void test_duplicate_scratch_key(skiatest::Reporter* reporter) {
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));
@ -189,16 +142,30 @@ static void test_duplicate_scratch_key(skiatest::Reporter* reporter) {
a->setSize(11);
b->setSize(12);
// Scratch resources are registered with GrResourceCache2 just by existing. There are 2.
SkDEBUGCODE(REPORTER_ASSERT(reporter, 2 == cache2->countScratchEntriesForKey(scratchKey));)
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() ==
cache->getCachedResourceBytes());
REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive());
SkDEBUGCODE(REPORTER_ASSERT(reporter, 2 == cache2->countScratchEntriesForKey(scratchKey));)
REPORTER_ASSERT(reporter, 2 == cache2->getResourceCount());
REPORTER_ASSERT(reporter, a->gpuMemorySize() + b->gpuMemorySize() ==
cache2->getResourceBytes());
// 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();
@ -207,9 +174,9 @@ static void test_duplicate_scratch_key(skiatest::Reporter* reporter) {
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));)
}
@ -220,9 +187,9 @@ static void test_duplicate_content_key(skiatest::Reporter* reporter) {
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;
@ -230,42 +197,30 @@ static void test_duplicate_content_key(skiatest::Reporter* reporter) {
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());
cache->purgeAllUnlocked();
a->setSize(10);
REPORTER_ASSERT(reporter, 1 == cache->getCachedResourceCount());
REPORTER_ASSERT(reporter, 1 == TestResource::NumAlive());
cache2->purgeAllUnlocked();
REPORTER_ASSERT(reporter, 1 == cache2->getResourceCount());
REPORTER_ASSERT(reporter, a->gpuMemorySize() == cache2->getResourceBytes());
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());
}
@ -290,17 +245,18 @@ static void test_purge_invalidated(skiatest::Reporter* reporter) {
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();
@ -315,8 +271,8 @@ static void test_purge_invalidated(skiatest::Reporter* reporter) {
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));
@ -326,19 +282,14 @@ static void test_purge_invalidated(skiatest::Reporter* reporter) {
// 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) {
@ -358,34 +309,44 @@ static void test_cache_chained_purge(skiatest::Reporter* reporter) {
{
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);
cache->addResource(key1, a);
cache->addResource(key2, b);
// Make a cycle
a->setUnrefWhenDestroyed(b);
b->setUnrefWhenDestroyed(a);
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);
cache2->purgeAllUnlocked();
a->setDeleteWhenDestroyed(cache, b);
b->setDeleteWhenDestroyed(cache, a);
a->unref();
b->unref();
cache->deleteResource(a->cacheAccess().getCacheEntry());
REPORTER_ASSERT(reporter, 0 == TestResource::NumAlive());
}
}
@ -413,20 +374,23 @@ static void test_resource_size_changed(skiatest::Reporter* reporter) {
// 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);
@ -434,29 +398,30 @@ static void test_resource_size_changed(skiatest::Reporter* reporter) {
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)));
@ -464,8 +429,8 @@ static void test_resource_size_changed(skiatest::Reporter* reporter) {
}
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());
}
}
@ -491,11 +456,10 @@ DEF_GPUTEST(ResourceCache, reporter, factory) {
}
// 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);
}

View File

@ -321,6 +321,33 @@ static void TestSurfaceWritableAfterSnapshotRelease(skiatest::Reporter* 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) {
@ -426,6 +453,8 @@ DEF_GPUTEST(Surface, reporter, factory) {
}
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);