Recycle stencil buffers across render targets.
Review URL: https://codereview.chromium.org/939093002
This commit is contained in:
parent
90c6bc4e85
commit
02a44a4886
@ -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 {
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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,
|
||||
|
@ -24,6 +24,7 @@ public:
|
||||
};
|
||||
|
||||
struct IDDesc {
|
||||
IDDesc() : fRenderbufferID(0), fLifeCycle(kCached_LifeCycle) {}
|
||||
GrGLuint fRenderbufferID;
|
||||
GrGpuResource::LifeCycle fLifeCycle;
|
||||
};
|
||||
|
@ -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.
|
||||
|
Loading…
Reference in New Issue
Block a user