Make GrResourceCache2 responsible for calling release, abandon, and ~.

BUG=skia:2889

TBR=robertphillips@google.com

NOTRY=true

Review URL: https://codereview.chromium.org/729683002
This commit is contained in:
bsalomon 2014-11-14 13:33:09 -08:00 committed by Commit bot
parent 85588344c3
commit 12299ab7a1
19 changed files with 91 additions and 116 deletions

View File

@ -33,8 +33,6 @@ public:
this->registerWithCache();
}
virtual ~StencilResource() { this->release(); }
static GrResourceKey ComputeKey(int width, int height, int sampleCnt) {
return GrStencilBuffer::ComputeKey(width, height, sampleCnt);
}
@ -58,8 +56,6 @@ public:
this->registerWithCache();
}
virtual ~TextureResource() { this->release(); }
static GrResourceKey ComputeKey(const GrSurfaceDesc& desc) {
GrCacheID::Key key;
memset(&key, 0, sizeof(key));

View File

@ -56,21 +56,20 @@ public:
this->didUnref();
}
bool isPurgable() const { return this->reffedOnlyByCache() && !this->internalHasPendingIO(); }
bool reffedOnlyByCache() const { return 1 == fRefCnt; }
void validate() const {
#ifdef SK_DEBUG
SkASSERT(fRefCnt >= 0);
SkASSERT(fPendingReads >= 0);
SkASSERT(fPendingWrites >= 0);
SkASSERT(fRefCnt + fPendingReads + fPendingWrites > 0);
SkASSERT(fRefCnt + fPendingReads + fPendingWrites >= 0);
#endif
}
protected:
GrIORef() : fRefCnt(1), fPendingReads(0), fPendingWrites(0) { }
bool isPurgable() const { return !this->internalHasRef() && !this->internalHasPendingIO(); }
bool internalHasPendingRead() const { return SkToBool(fPendingReads); }
bool internalHasPendingWrite() const { return SkToBool(fPendingWrites); }
bool internalHasPendingIO() const { return SkToBool(fPendingWrites | fPendingReads); }
@ -102,14 +101,8 @@ private:
private:
void didUnref() const {
if (0 == fPendingReads && 0 == fPendingWrites) {
if (0 == fRefCnt) {
// Must call derived destructor since this is not a virtual class.
SkDELETE(static_cast<const DERIVED*>(this));
} else if (1 == fRefCnt) {
// The one ref is the cache's
static_cast<const DERIVED*>(this)->notifyIsPurgable();
}
if (0 == fPendingReads && 0 == fPendingWrites && 0 == fRefCnt) {
static_cast<const DERIVED*>(this)->notifyIsPurgable();
}
}
@ -131,18 +124,6 @@ class SK_API GrGpuResource : public GrIORef<GrGpuResource> {
public:
SK_DECLARE_INST_COUNT(GrGpuResource)
/**
* Frees the object in the underlying 3D API. It must be safe to call this
* when the object has been previously abandoned.
*/
void release();
/**
* Removes references to objects in the underlying 3D API without freeing
* them. Used when the API context has been torn down before the GrContext.
*/
void abandon();
/**
* Tests whether a object has been abandoned or released. All objects will
* be in this state after their creating GrContext is destroyed or has
@ -203,10 +184,12 @@ protected:
GrGpu* getGpu() const { return fGpu; }
// Derived classes should always call their parent class' onRelease
// and onAbandon methods in their overrides.
virtual void onRelease() {};
virtual void onAbandon() {};
/** Overridden to free GPU resources in the backend API. */
virtual void onRelease() { }
/** Overridden to abandon any internal handles, ptrs, etc to backend API resources.
This may be called when the underlying 3D context is no longer valid and so no
backend API calls should be made. */
virtual void onAbandon() { }
bool isWrapped() const { return kWrapped_FlagBit & fFlags; }
@ -223,6 +206,17 @@ protected:
void setScratchKey(const GrResourceKey& scratchKey);
private:
/**
* Frees the object in the underlying 3D API. Called by CacheAccess.
*/
void release();
/**
* Removes references to objects in the underlying 3D API without freeing them.
* Called by CacheAccess.
*/
void abandon();
virtual size_t onGpuMemorySize() const = 0;
// See comments in CacheAccess.

View File

@ -36,26 +36,24 @@ void GrGpuResource::registerWithCache() {
}
GrGpuResource::~GrGpuResource() {
// subclass should have released this.
// The cache should have released or destroyed this resource.
SkASSERT(this->wasDestroyed());
}
void GrGpuResource::release() {
if (fGpu) {
this->onRelease();
get_resource_cache2(fGpu)->resourceAccess().removeResource(this);
fGpu = NULL;
fGpuMemorySize = 0;
}
SkASSERT(fGpu);
this->onRelease();
get_resource_cache2(fGpu)->resourceAccess().removeResource(this);
fGpu = NULL;
fGpuMemorySize = 0;
}
void GrGpuResource::abandon() {
if (fGpu) {
this->onAbandon();
get_resource_cache2(fGpu)->resourceAccess().removeResource(this);
fGpu = NULL;
fGpuMemorySize = 0;
}
SkASSERT(fGpu);
this->onAbandon();
get_resource_cache2(fGpu)->resourceAccess().removeResource(this);
fGpu = NULL;
fGpuMemorySize = 0;
}
const GrContext* GrGpuResource::getContext() const {
@ -90,7 +88,7 @@ bool GrGpuResource::setContentKey(const GrResourceKey& contentKey) {
SkASSERT(!contentKey.isScratch());
SkASSERT(this->internalHasRef());
if (fContentKeySet) {
if (fContentKeySet || this->wasDestroyed()) {
return false;
}
@ -105,8 +103,12 @@ bool GrGpuResource::setContentKey(const GrResourceKey& contentKey) {
}
void GrGpuResource::notifyIsPurgable() const {
if (!this->wasDestroyed()) {
get_resource_cache2(fGpu)->resourceAccess().notifyPurgable(this);
if (this->wasDestroyed()) {
// We've already been removed from the cache. Goodbye cruel world!
SkDELETE(this);
} else {
GrGpuResource* mutableThis = const_cast<GrGpuResource*>(this);
get_resource_cache2(fGpu)->resourceAccess().notifyPurgable(mutableThis);
}
}

View File

@ -54,6 +54,26 @@ public:
return NULL;
}
/**
* Called by the cache to delete the resource under normal circumstances.
*/
void release() {
fResource->release();
if (fResource->isPurgable()) {
SkDELETE(fResource);
}
}
/**
* Called by the cache to delete the resource when the backend 3D context is no longer valid.
*/
void abandon() {
fResource->abandon();
if (fResource->isPurgable()) {
SkDELETE(fResource);
}
}
private:
CacheAccess(GrGpuResource* resource) : fResource(resource) { }
CacheAccess(const CacheAccess& that) : fResource(that.fResource) { }

View File

@ -93,7 +93,6 @@ void GrResourceCache2::insertResource(GrGpuResource* resource) {
SkASSERT(!this->isInCache(resource));
SkASSERT(!fPurging);
fResources.addToHead(resource);
resource->ref();
++fCount;
SkDEBUGCODE(fHighWaterCount = SkTMax(fCount, fHighWaterCount));
@ -129,8 +128,7 @@ void GrResourceCache2::abandonAll() {
SkASSERT(!fPurging);
while (GrGpuResource* head = fResources.head()) {
SkASSERT(!head->wasDestroyed());
head->abandon();
head->unref();
head->cacheAccess().abandon();
// abandon should have already removed this from the list.
SkASSERT(head != fResources.head());
}
@ -145,8 +143,7 @@ void GrResourceCache2::releaseAll() {
SkASSERT(!fPurging);
while (GrGpuResource* head = fResources.head()) {
SkASSERT(!head->wasDestroyed());
head->release();
head->unref();
head->cacheAccess().release();
// release should have already removed this from the list.
SkASSERT(head != fResources.head());
}
@ -159,7 +156,7 @@ public:
AvailableForScratchUse(bool rejectPendingIO) : fRejectPendingIO(rejectPendingIO) { }
bool operator()(const GrGpuResource* resource) const {
if (!resource->reffedOnlyByCache() || !resource->cacheAccess().isScratch()) {
if (resource->internalHasRef() || !resource->cacheAccess().isScratch()) {
return false;
}
@ -224,7 +221,7 @@ void GrResourceCache2::makeResourceMRU(GrGpuResource* resource) {
fResources.addToHead(resource);
}
void GrResourceCache2::notifyPurgable(const GrGpuResource* resource) {
void GrResourceCache2::notifyPurgable(GrGpuResource* resource) {
SkASSERT(resource);
SkASSERT(this->isInCache(resource));
SkASSERT(resource->isPurgable());
@ -239,25 +236,14 @@ void GrResourceCache2::notifyPurgable(const GrGpuResource* resource) {
// 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();
// We should at least have freed resource. It may have in turn freed other resources.
resource->cacheAccess().release();
// We should at least free this resource, perhaps dependent resources as well.
SkASSERT(fCount < beforeCount);
}
@ -295,7 +281,7 @@ void GrResourceCache2::internalPurgeAsNeeded() {
while (resource) {
GrGpuResource* prev = resourceIter.prev();
if (resource->isPurgable()) {
resource->unref();
resource->cacheAccess().release();
}
resource = prev;
if (fCount <= fMaxCount && fBytes <= fMaxBytes) {
@ -331,8 +317,8 @@ void GrResourceCache2::purgeAllUnlocked() {
while (resource) {
GrGpuResource* prev = resourceIter.prev();
if (resource->isPurgable()) {
resource->unref();
}
resource->cacheAccess().release();
}
resource = prev;
}
@ -387,8 +373,10 @@ void GrResourceCache2::validate() const {
SkASSERT(content == fContentHash.count());
SkASSERT(scratch + couldBeScratch == fScratchMap.count());
bool overBudget = bytes > fMaxBytes || count > fMaxCount;
SkASSERT(!overBudget || locked == count || fPurging);
// This assertion is not currently valid because we can be in recursive notifyIsPurgable()
// calls. This will be fixed when subresource registration is explicit.
// bool overBudget = bytes > fMaxBytes || count > fMaxCount;
// SkASSERT(!overBudget || locked == count || fPurging);
}
#endif

View File

@ -148,7 +148,7 @@ private:
////
void insertResource(GrGpuResource*);
void removeResource(GrGpuResource*);
void notifyPurgable(const GrGpuResource*);
void notifyPurgable(GrGpuResource*);
void didChangeGpuMemorySize(const GrGpuResource*, size_t oldSize);
bool didSetContentKey(GrGpuResource*);
void makeResourceMRU(GrGpuResource*);
@ -241,7 +241,7 @@ private:
/**
* Called by GrGpuResources when they detects that they are newly purgable.
*/
void notifyPurgable(const GrGpuResource* resource) { fCache->notifyPurgable(resource); }
void notifyPurgable(GrGpuResource* resource) { fCache->notifyPurgable(resource); }
/**
* Called by GrGpuResources when their sizes change.

View File

@ -20,7 +20,6 @@ public:
typedef GrGLBufferImpl::Desc Desc;
GrGLIndexBuffer(GrGpuGL* gpu, const Desc& desc);
virtual ~GrGLIndexBuffer() { this->release(); }
GrGLuint bufferID() const { return fImpl.bufferID(); }
size_t baseOffset() const { return fImpl.baseOffset(); }

View File

@ -143,10 +143,6 @@ GrGLPath::GrGLPath(GrGpuGL* gpu, const SkPath& path, const SkStrokeRec& stroke)
this->registerWithCache();
}
GrGLPath::~GrGLPath() {
this->release();
}
void GrGLPath::onRelease() {
if (0 != fPathID && !this->isWrapped()) {
static_cast<GrGpuGL*>(this->getGpu())->glPathRendering()->deletePaths(fPathID, 1);

View File

@ -28,7 +28,6 @@ public:
const SkStrokeRec&);
GrGLPath(GrGpuGL* gpu, const SkPath& path, const SkStrokeRec& stroke);
virtual ~GrGLPath();
GrGLuint pathID() const { return fPathID; }
protected:

View File

@ -29,10 +29,6 @@ GrGLPathRange::GrGLPathRange(GrGpuGL* gpu,
this->registerWithCache();
}
GrGLPathRange::~GrGLPathRange() {
this->release();
}
void GrGLPathRange::onInitPath(int index, const SkPath& skPath) const {
GrGpuGL* gpu = static_cast<GrGpuGL*>(this->getGpu());
if (NULL == gpu) {

View File

@ -39,8 +39,6 @@ public:
size_t gpuMemorySize,
const SkStrokeRec&);
virtual ~GrGLPathRange();
GrGLuint basePathID() const { return fBasePathID; }
protected:

View File

@ -30,8 +30,6 @@ public:
GrGLRenderTarget(GrGpuGL*, const GrSurfaceDesc&, const IDDesc&);
virtual ~GrGLRenderTarget() { this->release(); }
void setViewport(const GrGLIRect& rect) { fViewport = rect; }
const GrGLIRect& getViewport() const { return fViewport; }

View File

@ -9,10 +9,6 @@
#include "GrGLStencilBuffer.h"
#include "GrGpuGL.h"
GrGLStencilBuffer::~GrGLStencilBuffer() {
this->release();
}
size_t GrGLStencilBuffer::onGpuMemorySize() const {
uint64_t size = this->width();
size *= this->height();

View File

@ -35,8 +35,6 @@ public:
this->registerWithCache();
}
virtual ~GrGLStencilBuffer();
GrGLuint renderbufferID() const {
return fRenderbufferID;
}

View File

@ -33,8 +33,6 @@ public:
GrGLTexture(GrGpuGL*, const GrSurfaceDesc&, const IDDesc&);
virtual ~GrGLTexture() { this->release(); }
virtual GrBackendObject getTextureHandle() const SK_OVERRIDE;
virtual void textureParamsModified() SK_OVERRIDE { fTexParams.invalidate(); }

View File

@ -34,8 +34,6 @@ public:
this->registerWithCache();
}
virtual ~GrGLTextureRenderTarget() { this->release(); }
protected:
virtual void onAbandon() SK_OVERRIDE {
GrGLRenderTarget::onAbandon();

View File

@ -20,7 +20,6 @@ public:
typedef GrGLBufferImpl::Desc Desc;
GrGLVertexBuffer(GrGpuGL* gpu, const Desc& desc);
virtual ~GrGLVertexBuffer() { this->release(); }
GrGLuint bufferID() const { return fImpl.bufferID(); }
size_t baseOffset() const { return fImpl.baseOffset(); }

View File

@ -1131,21 +1131,22 @@ bool GrGpuGL::createStencilBufferForRenderTarget(GrRenderTarget* rt,
SkASSERT(height >= rt->height());
int samples = rt->numSamples();
GrGLuint sbID;
GL_CALL(GenRenderbuffers(1, &sbID));
if (!sbID) {
return false;
}
GrGLuint sbID = 0;
int stencilFmtCnt = this->glCaps().stencilFormats().count();
for (int i = 0; i < stencilFmtCnt; ++i) {
if (!sbID) {
GL_CALL(GenRenderbuffers(1, &sbID));
}
if (!sbID) {
return false;
}
GL_CALL(BindRenderbuffer(GR_GL_RENDERBUFFER, sbID));
// we start with the last stencil format that succeeded in hopes
// that we won't go through this loop more than once after the
// first (painful) stencil creation.
int sIdx = (i + fLastSuccessfulStencilFmtIdx) % stencilFmtCnt;
const GrGLCaps::StencilFormat& sFmt =
this->glCaps().stencilFormats()[sIdx];
const GrGLCaps::StencilFormat& sFmt = this->glCaps().stencilFormats()[sIdx];
CLEAR_ERROR_BEFORE_ALLOC(this->glInterface());
// we do this "if" so that we don't call the multisample
// version on a GL that doesn't have an MSAA extension.
@ -1156,12 +1157,10 @@ bool GrGpuGL::createStencilBufferForRenderTarget(GrRenderTarget* rt,
sFmt.fInternalFormat,
width, height);
} else {
GL_ALLOC_CALL(this->glInterface(),
RenderbufferStorage(GR_GL_RENDERBUFFER,
sFmt.fInternalFormat,
width, height));
created =
(GR_GL_NO_ERROR == check_alloc_error(rt->desc(), this->glInterface()));
GL_ALLOC_CALL(this->glInterface(), RenderbufferStorage(GR_GL_RENDERBUFFER,
sFmt.fInternalFormat,
width, height));
created = (GR_GL_NO_ERROR == check_alloc_error(rt->desc(), this->glInterface()));
}
if (created) {
// After sized formats we attempt an unsized format and take
@ -1172,13 +1171,15 @@ bool GrGpuGL::createStencilBufferForRenderTarget(GrRenderTarget* rt,
SkAutoTUnref<GrStencilBuffer> sb(SkNEW_ARGS(GrGLStencilBuffer,
(this, kIsWrapped, sbID, width, height,
samples, format)));
// If we fail we have to create a new render buffer ID since we gave this one to the
// GrGLStencilBuffer object.
sbID = 0;
if (this->attachStencilBufferToRenderTarget(sb, rt)) {
fLastSuccessfulStencilFmtIdx = sIdx;
sb->transferToCache();
rt->setStencilBuffer(sb);
return true;
}
sb->abandon(); // otherwise we lose sbID
}
}
}
GL_CALL(DeleteRenderbuffers(1, &sbID));

View File

@ -86,7 +86,6 @@ public:
~TestResource() {
--fNumAlive;
SkSafeUnref(fToDelete);
this->release();
}
void setSize(size_t size) {