Recycle stencil buffers across render targets.
Review URL: https://codereview.chromium.org/939093002
diff --git a/src/gpu/GrGpu.cpp b/src/gpu/GrGpu.cpp
index eef965e..27fed3c 100644
--- a/src/gpu/GrGpu.cpp
+++ b/src/gpu/GrGpu.cpp
@@ -66,7 +66,7 @@
!(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 @@
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 @@
}
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 @@
// 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 @@
}
// 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 {
diff --git a/src/gpu/GrGpu.h b/src/gpu/GrGpu.h
index 2d5eaba..a0950e0 100644
--- a/src/gpu/GrGpu.h
+++ b/src/gpu/GrGpu.h
@@ -481,8 +481,7 @@
// 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 @@
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;
diff --git a/src/gpu/GrStencilBuffer.cpp b/src/gpu/GrStencilBuffer.cpp
index be463a0..3f2b28f 100644
--- a/src/gpu/GrStencilBuffer.cpp
+++ b/src/gpu/GrStencilBuffer.cpp
@@ -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;
}
diff --git a/src/gpu/GrStencilBuffer.h b/src/gpu/GrStencilBuffer.h
index 43d2114..3788e61 100644
--- a/src/gpu/GrStencilBuffer.h
+++ b/src/gpu/GrStencilBuffer.h
@@ -47,7 +47,10 @@
!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 @@
, fBits(bits)
, fSampleCnt(sampleCnt)
, fLastClipStackGenID(SkClipStack::kInvalidGenID) {
- if (kCached_LifeCycle == lifeCycle) {
- GrScratchKey key;
- ComputeKey(width, height, sampleCnt, &key);
- this->setScratchKey(key);
- }
fLastClipStackRect.setEmpty();
}
diff --git a/src/gpu/GrTest.cpp b/src/gpu/GrTest.cpp
index 8df59ff..0e1c069 100644
--- a/src/gpu/GrTest.cpp
+++ b/src/gpu/GrTest.cpp
@@ -229,8 +229,7 @@
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;
}
diff --git a/src/gpu/gl/GrGLGpu.cpp b/src/gpu/gl/GrGLGpu.cpp
index 5a861d4..ef4423b 100644
--- a/src/gpu/gl/GrGLGpu.cpp
+++ b/src/gpu/gl/GrGLGpu.cpp
@@ -442,8 +442,6 @@
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 @@
}
}
-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 @@
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) {
diff --git a/src/gpu/gl/GrGLGpu.h b/src/gpu/gl/GrGLGpu.h
index 18f0f74..7cbca01 100644
--- a/src/gpu/gl/GrGLGpu.h
+++ b/src/gpu/gl/GrGLGpu.h
@@ -122,8 +122,7 @@
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,
diff --git a/src/gpu/gl/GrGLStencilBuffer.h b/src/gpu/gl/GrGLStencilBuffer.h
index 305ece8..75e98a0 100644
--- a/src/gpu/gl/GrGLStencilBuffer.h
+++ b/src/gpu/gl/GrGLStencilBuffer.h
@@ -24,6 +24,7 @@
};
struct IDDesc {
+ IDDesc() : fRenderbufferID(0), fLifeCycle(kCached_LifeCycle) {}
GrGLuint fRenderbufferID;
GrGpuResource::LifeCycle fLifeCycle;
};
diff --git a/tests/ResourceCacheTest.cpp b/tests/ResourceCacheTest.cpp
index 64cd327..d9e4c18 100644
--- a/tests/ResourceCacheTest.cpp
+++ b/tests/ResourceCacheTest.cpp
@@ -63,6 +63,73 @@
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 @@
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.