Recycle stencil buffers across render targets.

Review URL: https://codereview.chromium.org/939093002
This commit is contained in:
bsalomon 2015-02-19 09:09:00 -08:00 committed by Commit bot
parent 90c6bc4e85
commit 02a44a4886
9 changed files with 94 additions and 35 deletions

View File

@ -66,7 +66,7 @@ GrTexture* GrGpu::createTexture(const GrSurfaceDesc& desc, bool budgeted,
!(kNoStencil_GrSurfaceFlag & desc.fFlags)) {
SkASSERT(tex->asRenderTarget());
// TODO: defer this and attach dynamically
if (!this->attachStencilBufferToRenderTarget(tex->asRenderTarget(), budgeted)) {
if (!this->attachStencilBufferToRenderTarget(tex->asRenderTarget())) {
tex->unref();
return NULL;
}
@ -84,12 +84,13 @@ GrTexture* GrGpu::createTexture(const GrSurfaceDesc& desc, bool budgeted,
return tex;
}
bool GrGpu::attachStencilBufferToRenderTarget(GrRenderTarget* rt, bool budgeted) {
bool GrGpu::attachStencilBufferToRenderTarget(GrRenderTarget* rt) {
SkASSERT(NULL == rt->getStencilBuffer());
GrScratchKey sbKey;
GrStencilBuffer::ComputeKey(rt->width(), rt->height(), rt->numSamples(), &sbKey);
GrUniqueKey sbKey;
GrStencilBuffer::ComputeSharedStencilBufferKey(rt->width(), rt->height(), rt->numSamples(),
&sbKey);
SkAutoTUnref<GrStencilBuffer> sb(static_cast<GrStencilBuffer*>(
this->getContext()->getResourceCache()->findAndRefScratchResource(sbKey)));
this->getContext()->getResourceCache()->findAndRefUniqueResource(sbKey)));
if (sb) {
rt->setStencilBuffer(sb);
bool attached = this->attachStencilBufferToRenderTarget(sb, rt);
@ -98,7 +99,7 @@ bool GrGpu::attachStencilBufferToRenderTarget(GrRenderTarget* rt, bool budgeted)
}
return attached;
}
if (this->createStencilBufferForRenderTarget(rt, budgeted, rt->width(), rt->height())) {
if (this->createStencilBufferForRenderTarget(rt, rt->width(), rt->height())) {
// Right now we're clearing the stencil buffer here after it is
// attached to an RT for the first time. When we start matching
// stencil buffers with smaller color targets this will no longer
@ -108,6 +109,7 @@ bool GrGpu::attachStencilBufferToRenderTarget(GrRenderTarget* rt, bool budgeted)
// FBO. But iOS doesn't allow a stencil-only FBO. It reports unsupported
// FBO status.
this->clearStencil(rt);
rt->getStencilBuffer()->resourcePriv().setUniqueKey(sbKey);
return true;
} else {
return false;
@ -122,8 +124,7 @@ GrTexture* GrGpu::wrapBackendTexture(const GrBackendTextureDesc& desc) {
}
// TODO: defer this and attach dynamically
GrRenderTarget* tgt = tex->asRenderTarget();
if (tgt &&
!this->attachStencilBufferToRenderTarget(tgt, true /*budgeted*/)) {
if (tgt && !this->attachStencilBufferToRenderTarget(tgt)) {
tex->unref();
return NULL;
} else {

View File

@ -481,8 +481,7 @@ private:
// width and height may be larger than rt (if underlying API allows it).
// Should attach the SB to the RT. Returns false if compatible sb could
// not be created.
virtual bool createStencilBufferForRenderTarget(GrRenderTarget*, bool budgeted,
int width, int height) = 0;
virtual bool createStencilBufferForRenderTarget(GrRenderTarget*, int width, int height) = 0;
// attaches an existing SB to an existing RT.
virtual bool attachStencilBufferToRenderTarget(GrStencilBuffer*, GrRenderTarget*) = 0;
@ -491,7 +490,7 @@ private:
virtual void clearStencil(GrRenderTarget* target) = 0;
// Given a rt, find or create a stencil buffer and attach it
bool attachStencilBufferToRenderTarget(GrRenderTarget* target, bool budgeted);
bool attachStencilBufferToRenderTarget(GrRenderTarget* target);
virtual void didAddGpuTraceMarker() = 0;
virtual void didRemoveGpuTraceMarker() = 0;

View File

@ -9,11 +9,11 @@
#include "GrStencilBuffer.h"
#include "GrResourceKey.h"
void GrStencilBuffer::ComputeKey(int width, int height, int sampleCnt, GrScratchKey* key) {
static const GrScratchKey::ResourceType kType = GrScratchKey::GenerateResourceType();
GrScratchKey::Builder builder(key, kType, 2);
SkASSERT(width <= SK_MaxU16);
SkASSERT(height <= SK_MaxU16);
builder[0] = width | (height << 16);
builder[1] = sampleCnt;
void GrStencilBuffer::ComputeSharedStencilBufferKey(int width, int height, int sampleCnt,
GrUniqueKey* key) {
static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
GrUniqueKey::Builder builder(key, kDomain, 3);
builder[0] = width;
builder[1] = height;
builder[2] = sampleCnt;
}

View File

@ -47,7 +47,10 @@ public:
!fLastClipStackRect.contains(clipSpaceRect);
}
static void ComputeKey(int width, int height, int sampleCnt, GrScratchKey* key);
// We create a unique stencil buffer at each width, height and sampleCnt and share it for
// all render targets that require a stencil with those params.
static void ComputeSharedStencilBufferKey(int width, int height, int sampleCnt,
GrUniqueKey* key);
protected:
GrStencilBuffer(GrGpu* gpu, LifeCycle lifeCycle, int width, int height, int bits, int sampleCnt)
@ -57,11 +60,6 @@ protected:
, fBits(bits)
, fSampleCnt(sampleCnt)
, fLastClipStackGenID(SkClipStack::kInvalidGenID) {
if (kCached_LifeCycle == lifeCycle) {
GrScratchKey key;
ComputeKey(width, height, sampleCnt, &key);
this->setScratchKey(key);
}
fLastClipStackRect.setEmpty();
}

View File

@ -229,8 +229,7 @@ private:
void onResolveRenderTarget(GrRenderTarget* target) SK_OVERRIDE { return; }
bool createStencilBufferForRenderTarget(GrRenderTarget*, bool budgeted,
int width, int height) SK_OVERRIDE {
bool createStencilBufferForRenderTarget(GrRenderTarget*, int width, int height) SK_OVERRIDE {
return false;
}

View File

@ -442,8 +442,6 @@ GrRenderTarget* GrGLGpu::onWrapBackendRenderTarget(const GrBackendRenderTargetDe
GrRenderTarget* tgt = SkNEW_ARGS(GrGLRenderTarget, (this, desc, idDesc));
if (wrapDesc.fStencilBits) {
GrGLStencilBuffer::IDDesc sbDesc;
sbDesc.fRenderbufferID = 0;
sbDesc.fLifeCycle = GrGpuResource::kWrapped_LifeCycle;
GrGLStencilBuffer::Format format;
format.fInternalFormat = GrGLStencilBuffer::kUnknownInternalFormat;
format.fPacked = false;
@ -1128,8 +1126,7 @@ void inline get_stencil_rb_sizes(const GrGLInterface* gl,
}
}
bool GrGLGpu::createStencilBufferForRenderTarget(GrRenderTarget* rt, bool budgeted, int width,
int height) {
bool GrGLGpu::createStencilBufferForRenderTarget(GrRenderTarget* rt, int width, int height) {
// All internally created RTs are also textures. We don't create
// SBs for a client's standalone RT (that is a RT that isn't also a texture).
SkASSERT(rt->asTexture());
@ -1138,9 +1135,6 @@ bool GrGLGpu::createStencilBufferForRenderTarget(GrRenderTarget* rt, bool budget
int samples = rt->numSamples();
GrGLStencilBuffer::IDDesc sbDesc;
sbDesc.fRenderbufferID = 0;
sbDesc.fLifeCycle = budgeted ? GrGpuResource::kCached_LifeCycle
: GrGpuResource::kUncached_LifeCycle;
int stencilFmtCnt = this->glCaps().stencilFormats().count();
for (int i = 0; i < stencilFmtCnt; ++i) {

View File

@ -122,8 +122,7 @@ private:
GrIndexBuffer* onCreateIndexBuffer(size_t size, bool dynamic) SK_OVERRIDE;
GrTexture* onWrapBackendTexture(const GrBackendTextureDesc&) SK_OVERRIDE;
GrRenderTarget* onWrapBackendRenderTarget(const GrBackendRenderTargetDesc&) SK_OVERRIDE;
bool createStencilBufferForRenderTarget(GrRenderTarget* rt, bool budgeted,
int width, int height) SK_OVERRIDE;
bool createStencilBufferForRenderTarget(GrRenderTarget* rt, int width, int height) SK_OVERRIDE;
bool attachStencilBufferToRenderTarget(GrStencilBuffer* sb, GrRenderTarget* rt) SK_OVERRIDE;
void onClear(GrRenderTarget*, const SkIRect* rect, GrColor color,

View File

@ -24,6 +24,7 @@ public:
};
struct IDDesc {
IDDesc() : fRenderbufferID(0), fLifeCycle(kCached_LifeCycle) {}
GrGLuint fRenderbufferID;
GrGpuResource::LifeCycle fLifeCycle;
};

View File

@ -63,6 +63,73 @@ static void test_cache(skiatest::Reporter* reporter, GrContext* context, SkCanva
context->setResourceCacheLimits(oldMaxNum, oldMaxBytes);
}
static void test_stencil_buffers(skiatest::Reporter* reporter, GrContext* context) {
GrSurfaceDesc smallDesc;
smallDesc.fFlags = kRenderTarget_GrSurfaceFlag;
smallDesc.fConfig = kSkia8888_GrPixelConfig;
smallDesc.fWidth = 4;
smallDesc.fHeight = 4;
smallDesc.fSampleCnt = 0;
// Test that two budgeted RTs with the same desc share a stencil buffer.
SkAutoTUnref<GrTexture> smallRT0(context->createTexture(smallDesc, true));
SkAutoTUnref<GrTexture> smallRT1(context->createTexture(smallDesc, true));
REPORTER_ASSERT(reporter, smallRT0 && smallRT1 &&
smallRT0->asRenderTarget() && smallRT1->asRenderTarget() &&
smallRT0->asRenderTarget()->getStencilBuffer() ==
smallRT1->asRenderTarget()->getStencilBuffer());
// An unbudgeted RT with the same desc should also share.
SkAutoTUnref<GrTexture> smallRT2(context->createTexture(smallDesc, false));
REPORTER_ASSERT(reporter, smallRT0 && smallRT2 &&
smallRT0->asRenderTarget() && smallRT2->asRenderTarget() &&
smallRT0->asRenderTarget()->getStencilBuffer() ==
smallRT2->asRenderTarget()->getStencilBuffer());
// An RT with a much larger size should not share.
GrSurfaceDesc bigDesc;
bigDesc.fFlags = kRenderTarget_GrSurfaceFlag;
bigDesc.fConfig = kSkia8888_GrPixelConfig;
bigDesc.fWidth = 400;
bigDesc.fHeight = 200;
bigDesc.fSampleCnt = 0;
SkAutoTUnref<GrTexture> bigRT(context->createTexture(bigDesc, false));
REPORTER_ASSERT(reporter, smallRT0 && bigRT &&
smallRT0->asRenderTarget() && bigRT->asRenderTarget() &&
smallRT0->asRenderTarget()->getStencilBuffer() !=
bigRT->asRenderTarget()->getStencilBuffer());
if (context->getMaxSampleCount() >= 4) {
// An RT with a different sample count should not share.
GrSurfaceDesc smallMSAADesc = smallDesc;
smallMSAADesc.fSampleCnt = 4;
SkAutoTUnref<GrTexture> smallMSAART0(context->createTexture(smallMSAADesc, false));
REPORTER_ASSERT(reporter, smallRT0 && smallMSAART0 &&
smallRT0->asRenderTarget() && smallMSAART0->asRenderTarget() &&
smallRT0->asRenderTarget()->getStencilBuffer() !=
smallMSAART0->asRenderTarget()->getStencilBuffer());
// A second MSAA RT should share with the first MSAA RT.
SkAutoTUnref<GrTexture> smallMSAART1(context->createTexture(smallMSAADesc, false));
REPORTER_ASSERT(reporter, smallMSAART0 && smallMSAART1 &&
smallMSAART0->asRenderTarget() &&
smallMSAART1->asRenderTarget() &&
smallMSAART0->asRenderTarget()->getStencilBuffer() ==
smallMSAART1->asRenderTarget()->getStencilBuffer());
// But not one with a larger sample count should not. (Also check that the request for 4
// samples didn't get rounded up to >= 8 or else they could share.).
if (context->getMaxSampleCount() >= 8 && smallMSAART0 && smallMSAART0->asRenderTarget() &&
smallMSAART0->asRenderTarget()->numSamples() < 8) {
smallMSAADesc.fSampleCnt = 8;
smallMSAART1.reset(context->createTexture(smallMSAADesc, false));
REPORTER_ASSERT(reporter, smallMSAART0 && smallMSAART1 &&
smallMSAART0->asRenderTarget() &&
smallMSAART1->asRenderTarget() &&
smallMSAART0->asRenderTarget()->getStencilBuffer() !=
smallMSAART1->asRenderTarget()->getStencilBuffer());
}
}
}
class TestResource : public GrGpuResource {
static const size_t kDefaultSize = 100;
enum ScratchConstructor { kScratchConstructor };
@ -936,6 +1003,7 @@ DEF_GPUTEST(ResourceCache, reporter, factory) {
SkAutoTUnref<SkSurface> surface(SkSurface::NewRenderTarget(context,
SkSurface::kNo_Budgeted, info));
test_cache(reporter, context, surface->getCanvas());
test_stencil_buffers(reporter, context);
}
// The below tests create their own mock contexts.