summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Chris Craik <ccraik@google.com> 2016-04-05 13:18:56 -0700
committer Chris Craik <ccraik@google.com> 2016-04-05 20:42:37 +0000
commit74af6e282f8a8f75928a071e8200039517cf5c12 (patch)
treee45fe0baae9cf967636ccc67c1a3c5932601b42e
parent505a8d9ddfb7ce24a01666aaf40c81801e9d9afb (diff)
Fix OffscreenBuffer leak
Fixes: 27941148 Make OffscreenBuffer lifecycle an explicit (and tested) contract between FrameBuilder and BakedOpRenderer, entirely separate from dispatch. This makes it safe to reject any rendering work via overdraw content rejection (before it gets to a BakedOpDispatcher). Adds a couple tests around OffscreenBuffer leaks, and switches OffscreenBuffer tests to RENDERTHREAD_TEST macro, as appropriate. Change-Id: Id114b835d042708ae921028fb4b17e5fa485fe64
-rw-r--r--libs/hwui/BakedOpDispatcher.cpp4
-rw-r--r--libs/hwui/BakedOpRenderer.cpp4
-rw-r--r--libs/hwui/BakedOpRenderer.h1
-rw-r--r--libs/hwui/FrameBuilder.h6
-rw-r--r--libs/hwui/RecordedOp.h11
-rw-r--r--libs/hwui/renderstate/OffscreenBufferPool.cpp2
-rw-r--r--libs/hwui/tests/unit/FrameBuilderTests.cpp44
-rw-r--r--libs/hwui/tests/unit/LeakCheckTests.cpp19
-rw-r--r--libs/hwui/tests/unit/OffscreenBufferPoolTests.cpp197
9 files changed, 173 insertions, 115 deletions
diff --git a/libs/hwui/BakedOpDispatcher.cpp b/libs/hwui/BakedOpDispatcher.cpp
index f43bf860946b..5fb842598046 100644
--- a/libs/hwui/BakedOpDispatcher.cpp
+++ b/libs/hwui/BakedOpDispatcher.cpp
@@ -809,10 +809,6 @@ void BakedOpDispatcher::onLayerOp(BakedOpRenderer& renderer, const LayerOp& op,
Rect(op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight()))
.build();
renderer.renderGlop(state, glop);
-
- if (op.destroy) {
- renderer.renderState().layerPool().putOrDelete(buffer);
- }
}
}
diff --git a/libs/hwui/BakedOpRenderer.cpp b/libs/hwui/BakedOpRenderer.cpp
index 20f102bac351..3c302b3f6c9f 100644
--- a/libs/hwui/BakedOpRenderer.cpp
+++ b/libs/hwui/BakedOpRenderer.cpp
@@ -37,6 +37,10 @@ OffscreenBuffer* BakedOpRenderer::startTemporaryLayer(uint32_t width, uint32_t h
return buffer;
}
+void BakedOpRenderer::recycleTemporaryLayer(OffscreenBuffer* offscreenBuffer) {
+ mRenderState.layerPool().putOrDelete(offscreenBuffer);
+}
+
void BakedOpRenderer::startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) {
LOG_ALWAYS_FATAL_IF(mRenderTarget.offscreenBuffer, "already has layer...");
diff --git a/libs/hwui/BakedOpRenderer.h b/libs/hwui/BakedOpRenderer.h
index 1b4065aa0aa6..62bc564a4a2a 100644
--- a/libs/hwui/BakedOpRenderer.h
+++ b/libs/hwui/BakedOpRenderer.h
@@ -69,6 +69,7 @@ public:
void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect);
void endFrame(const Rect& repaintRect);
WARN_UNUSED_RESULT OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height);
+ void recycleTemporaryLayer(OffscreenBuffer* offscreenBuffer);
void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect);
void endLayer();
WARN_UNUSED_RESULT OffscreenBuffer* copyToLayer(const Rect& area);
diff --git a/libs/hwui/FrameBuilder.h b/libs/hwui/FrameBuilder.h
index e41822729695..039ab6bd88ab 100644
--- a/libs/hwui/FrameBuilder.h
+++ b/libs/hwui/FrameBuilder.h
@@ -86,6 +86,7 @@ public:
*/
template <typename StaticDispatcher, typename Renderer>
void replayBakedOps(Renderer& renderer) {
+ std::vector<OffscreenBuffer*> temporaryLayers;
finishDefer();
/**
* Defines a LUT of lambdas which allow a recorded BakedOpState to use state->op->opId to
@@ -129,6 +130,7 @@ public:
} else if (!layer.empty()) {
// save layer - skip entire layer if empty (in which case, LayerOp has null layer).
layer.offscreenBuffer = renderer.startTemporaryLayer(layer.width, layer.height);
+ temporaryLayers.push_back(layer.offscreenBuffer);
GL_CHECKPOINT(MODERATE);
layer.replayBakedOpsImpl((void*)&renderer, unmergedReceivers, mergedReceivers);
GL_CHECKPOINT(MODERATE);
@@ -145,6 +147,10 @@ public:
GL_CHECKPOINT(MODERATE);
renderer.endFrame(fbo0.repaintRect);
}
+
+ for (auto& temporaryLayer : temporaryLayers) {
+ renderer.recycleTemporaryLayer(temporaryLayer);
+ }
}
void dump() const {
diff --git a/libs/hwui/RecordedOp.h b/libs/hwui/RecordedOp.h
index 0271a80d4c17..aee9d6370083 100644
--- a/libs/hwui/RecordedOp.h
+++ b/libs/hwui/RecordedOp.h
@@ -501,22 +501,20 @@ struct CopyFromLayerOp : RecordedOp {
* when creating/tracking a SkPaint* during defer isn't worth the bother.
*/
struct LayerOp : RecordedOp {
- // Records a one-use (saveLayer) layer for drawing. Once drawn, the layer will be destroyed.
+ // Records a one-use (saveLayer) layer for drawing.
LayerOp(BASE_PARAMS, OffscreenBuffer** layerHandle)
: SUPER_PAINTLESS(LayerOp)
, layerHandle(layerHandle)
, alpha(paint ? paint->getAlpha() / 255.0f : 1.0f)
, mode(PaintUtils::getXfermodeDirect(paint))
- , colorFilter(paint ? paint->getColorFilter() : nullptr)
- , destroy(true) {}
+ , colorFilter(paint ? paint->getColorFilter() : nullptr) {}
LayerOp(RenderNode& node)
: RecordedOp(RecordedOpId::LayerOp, Rect(node.getWidth(), node.getHeight()), Matrix4::identity(), nullptr, nullptr)
, layerHandle(node.getLayerHandle())
, alpha(node.properties().layerProperties().alpha() / 255.0f)
, mode(node.properties().layerProperties().xferMode())
- , colorFilter(node.properties().layerProperties().colorFilter())
- , destroy(false) {}
+ , colorFilter(node.properties().layerProperties().colorFilter()) {}
// Records a handle to the Layer object, since the Layer itself won't be
// constructed until after this operation is constructed.
@@ -527,9 +525,6 @@ struct LayerOp : RecordedOp {
// pointer to object owned by either LayerProperties, or a recorded Paint object in a
// BeginLayerOp. Lives longer than LayerOp in either case, so no skia ref counting is used.
SkColorFilter* colorFilter;
-
- // whether to destroy the layer, once rendered
- const bool destroy;
};
}; // namespace uirenderer
diff --git a/libs/hwui/renderstate/OffscreenBufferPool.cpp b/libs/hwui/renderstate/OffscreenBufferPool.cpp
index bb1a044a8369..73b6c02a3fdc 100644
--- a/libs/hwui/renderstate/OffscreenBufferPool.cpp
+++ b/libs/hwui/renderstate/OffscreenBufferPool.cpp
@@ -127,7 +127,7 @@ int OffscreenBufferPool::Entry::compare(const Entry& lhs, const Entry& rhs) {
}
void OffscreenBufferPool::clear() {
- for (auto entry : mPool) {
+ for (auto& entry : mPool) {
delete entry.layer;
}
mPool.clear();
diff --git a/libs/hwui/tests/unit/FrameBuilderTests.cpp b/libs/hwui/tests/unit/FrameBuilderTests.cpp
index bcf31aeb6689..0ea246f00fb7 100644
--- a/libs/hwui/tests/unit/FrameBuilderTests.cpp
+++ b/libs/hwui/tests/unit/FrameBuilderTests.cpp
@@ -49,9 +49,12 @@ class TestRendererBase {
public:
virtual ~TestRendererBase() {}
virtual OffscreenBuffer* startTemporaryLayer(uint32_t, uint32_t) {
- ADD_FAILURE() << "Layer creation not expected in this test";
+ ADD_FAILURE() << "Temporary layers not expected in this test";
return nullptr;
}
+ virtual void recycleTemporaryLayer(OffscreenBuffer*) {
+ ADD_FAILURE() << "Temporary layers not expected in this test";
+ }
virtual void startRepaintLayer(OffscreenBuffer*, const Rect& repaintRect) {
ADD_FAILURE() << "Layer repaint not expected in this test";
}
@@ -710,6 +713,10 @@ RENDERTHREAD_TEST(FrameBuilder, saveLayer_simple) {
EXPECT_EQ(Rect(200, 200), state.computedState.clipRect());
EXPECT_TRUE(state.computedState.transform.isIdentity());
}
+ void recycleTemporaryLayer(OffscreenBuffer* offscreenBuffer) override {
+ EXPECT_EQ(4, mIndex++);
+ EXPECT_EQ(nullptr, offscreenBuffer);
+ }
};
auto node = TestUtils::createNode(0, 0, 200, 200,
@@ -722,7 +729,7 @@ RENDERTHREAD_TEST(FrameBuilder, saveLayer_simple) {
TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
SaveLayerSimpleTestRenderer renderer;
frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(4, renderer.getIndex());
+ EXPECT_EQ(5, renderer.getIndex());
}
RENDERTHREAD_TEST(FrameBuilder, saveLayer_nested) {
@@ -774,6 +781,15 @@ RENDERTHREAD_TEST(FrameBuilder, saveLayer_nested) {
EXPECT_EQ(Rect(800, 800), op.unmappedBounds); // outer layer
} else { ADD_FAILURE(); }
}
+ void recycleTemporaryLayer(OffscreenBuffer* offscreenBuffer) override {
+ const int index = mIndex++;
+ // order isn't important, but we need to see both
+ if (index == 10) {
+ EXPECT_EQ((OffscreenBuffer*)0x400, offscreenBuffer);
+ } else if (index == 11) {
+ EXPECT_EQ((OffscreenBuffer*)0x800, offscreenBuffer);
+ } else { ADD_FAILURE(); }
+ }
};
auto node = TestUtils::createNode(0, 0, 800, 800,
@@ -794,7 +810,7 @@ RENDERTHREAD_TEST(FrameBuilder, saveLayer_nested) {
TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
SaveLayerNestedTestRenderer renderer;
frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(10, renderer.getIndex());
+ EXPECT_EQ(12, renderer.getIndex());
}
RENDERTHREAD_TEST(FrameBuilder, saveLayer_contentRejection) {
@@ -1009,10 +1025,15 @@ RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_complex) {
}
void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
EXPECT_EQ(9, mIndex++);
+ EXPECT_EQ((OffscreenBuffer*)0xabcd, *op.layerHandle);
}
void endFrame(const Rect& repaintRect) override {
EXPECT_EQ(11, mIndex++);
}
+ void recycleTemporaryLayer(OffscreenBuffer* offscreenBuffer) override {
+ EXPECT_EQ(12, mIndex++);
+ EXPECT_EQ((OffscreenBuffer*)0xabcd, offscreenBuffer);
+ }
};
auto node = TestUtils::createNode(0, 0, 600, 600, // 500x500 triggers clipping
@@ -1029,7 +1050,7 @@ RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_complex) {
TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
SaveLayerUnclippedComplexTestRenderer renderer;
frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(12, renderer.getIndex());
+ EXPECT_EQ(13, renderer.getIndex());
}
RENDERTHREAD_TEST(FrameBuilder, hwLayer_simple) {
@@ -1151,6 +1172,9 @@ RENDERTHREAD_TEST(FrameBuilder, hwLayer_complex) {
void endFrame(const Rect& repaintRect) override {
EXPECT_EQ(12, mIndex++);
}
+ void recycleTemporaryLayer(OffscreenBuffer* offscreenBuffer) override {
+ EXPECT_EQ(13, mIndex++);
+ }
};
auto child = TestUtils::createNode(50, 50, 150, 150,
@@ -1188,7 +1212,7 @@ RENDERTHREAD_TEST(FrameBuilder, hwLayer_complex) {
syncedList, sLightGeometry, Caches::getInstance());
HwLayerComplexTestRenderer renderer;
frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(13, renderer.getIndex());
+ EXPECT_EQ(14, renderer.getIndex());
// clean up layer pointers, so we can safely destruct RenderNodes
*(child->getLayerHandle()) = nullptr;
@@ -1592,6 +1616,9 @@ RENDERTHREAD_TEST(FrameBuilder, shadowSaveLayer) {
void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
EXPECT_EQ(4, mIndex++);
}
+ void recycleTemporaryLayer(OffscreenBuffer* offscreenBuffer) override {
+ EXPECT_EQ(5, mIndex++);
+ }
};
auto parent = TestUtils::createNode(0, 0, 200, 200,
@@ -1610,7 +1637,7 @@ RENDERTHREAD_TEST(FrameBuilder, shadowSaveLayer) {
(FrameBuilder::LightGeometry) {{ 100, 100, 100 }, 50}, Caches::getInstance());
ShadowSaveLayerTestRenderer renderer;
frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(5, renderer.getIndex());
+ EXPECT_EQ(6, renderer.getIndex());
}
RENDERTHREAD_TEST(FrameBuilder, shadowHwLayer) {
@@ -1839,6 +1866,9 @@ void testSaveLayerAlphaClip(SaveLayerAlphaData* outObservedData,
void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
EXPECT_EQ(3, mIndex++);
}
+ void recycleTemporaryLayer(OffscreenBuffer* offscreenBuffer) override {
+ EXPECT_EQ(4, mIndex++);
+ }
private:
SaveLayerAlphaData* mOutData;
};
@@ -1864,7 +1894,7 @@ void testSaveLayerAlphaClip(SaveLayerAlphaData* outObservedData,
frameBuilder.replayBakedOps<TestDispatcher>(renderer);
// assert, since output won't be valid if we haven't seen a save layer triggered
- ASSERT_EQ(4, renderer.getIndex()) << "Test must trigger saveLayer alpha behavior.";
+ ASSERT_EQ(5, renderer.getIndex()) << "Test must trigger saveLayer alpha behavior.";
}
RENDERTHREAD_TEST(FrameBuilder, renderPropSaveLayerAlphaClipBig) {
diff --git a/libs/hwui/tests/unit/LeakCheckTests.cpp b/libs/hwui/tests/unit/LeakCheckTests.cpp
index 9161f90b54aa..e2fc376db50d 100644
--- a/libs/hwui/tests/unit/LeakCheckTests.cpp
+++ b/libs/hwui/tests/unit/LeakCheckTests.cpp
@@ -30,6 +30,25 @@ const LayerUpdateQueue sEmptyLayerUpdateQueue;
const FrameBuilder::LightGeometry sLightGeometery = { {100, 100, 100}, 50};
const BakedOpRenderer::LightInfo sLightInfo = { 128, 128 };
+RENDERTHREAD_TEST(LeakCheck, saveLayer_overdrawRejection) {
+ auto node = TestUtils::createNode(0, 0, 100, 100,
+ [](RenderProperties& props, RecordingCanvas& canvas) {
+ canvas.saveLayerAlpha(0, 0, 100, 100, 128, SaveFlags::ClipToLayer);
+ canvas.drawRect(0, 0, 100, 100, SkPaint());
+ canvas.restore();
+
+ // opaque draw, rejects saveLayer beneath
+ canvas.drawRect(0, 0, 100, 100, SkPaint());
+ });
+ RenderState& renderState = renderThread.renderState();
+ Caches& caches = Caches::getInstance();
+
+ FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100,
+ TestUtils::createSyncedNodeList(node), sLightGeometery, Caches::getInstance());
+ BakedOpRenderer renderer(caches, renderState, true, sLightInfo);
+ frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer);
+}
+
RENDERTHREAD_TEST(LeakCheck, saveLayerUnclipped_simple) {
auto node = TestUtils::createNode(0, 0, 200, 200,
[](RenderProperties& props, RecordingCanvas& canvas) {
diff --git a/libs/hwui/tests/unit/OffscreenBufferPoolTests.cpp b/libs/hwui/tests/unit/OffscreenBufferPoolTests.cpp
index 37a485e3a9ac..b7950aab5662 100644
--- a/libs/hwui/tests/unit/OffscreenBufferPoolTests.cpp
+++ b/libs/hwui/tests/unit/OffscreenBufferPoolTests.cpp
@@ -30,119 +30,126 @@ TEST(OffscreenBuffer, computeIdealDimension) {
EXPECT_EQ(1024u, OffscreenBuffer::computeIdealDimension(1000));
}
-TEST(OffscreenBuffer, construct) {
- TestUtils::runOnRenderThread([] (renderthread::RenderThread& thread) {
- OffscreenBuffer layer(thread.renderState(), Caches::getInstance(), 49u, 149u);
- EXPECT_EQ(49u, layer.viewportWidth);
- EXPECT_EQ(149u, layer.viewportHeight);
+RENDERTHREAD_TEST(OffscreenBuffer, construct) {
+ OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 49u, 149u);
+ EXPECT_EQ(49u, layer.viewportWidth);
+ EXPECT_EQ(149u, layer.viewportHeight);
- EXPECT_EQ(64u, layer.texture.width());
- EXPECT_EQ(192u, layer.texture.height());
+ EXPECT_EQ(64u, layer.texture.width());
+ EXPECT_EQ(192u, layer.texture.height());
- EXPECT_EQ(64u * 192u * 4u, layer.getSizeInBytes());
- });
+ EXPECT_EQ(64u * 192u * 4u, layer.getSizeInBytes());
}
-TEST(OffscreenBuffer, getTextureCoordinates) {
- TestUtils::runOnRenderThread([] (renderthread::RenderThread& thread) {
- OffscreenBuffer layerAligned(thread.renderState(), Caches::getInstance(), 256u, 256u);
- EXPECT_EQ(Rect(0, 1, 1, 0),
- layerAligned.getTextureCoordinates());
+RENDERTHREAD_TEST(OffscreenBuffer, getTextureCoordinates) {
+ OffscreenBuffer layerAligned(renderThread.renderState(), Caches::getInstance(), 256u, 256u);
+ EXPECT_EQ(Rect(0, 1, 1, 0),
+ layerAligned.getTextureCoordinates());
- OffscreenBuffer layerUnaligned(thread.renderState(), Caches::getInstance(), 200u, 225u);
- EXPECT_EQ(Rect(0, 225.0f / 256.0f, 200.0f / 256.0f, 0),
- layerUnaligned.getTextureCoordinates());
- });
+ OffscreenBuffer layerUnaligned(renderThread.renderState(), Caches::getInstance(), 200u, 225u);
+ EXPECT_EQ(Rect(0, 225.0f / 256.0f, 200.0f / 256.0f, 0),
+ layerUnaligned.getTextureCoordinates());
}
-TEST(OffscreenBuffer, dirty) {
- TestUtils::runOnRenderThread([] (renderthread::RenderThread& thread) {
- OffscreenBuffer buffer(thread.renderState(), Caches::getInstance(), 256u, 256u);
- buffer.dirty(Rect(-100, -100, 100, 100));
- EXPECT_EQ(android::Rect(100, 100), buffer.region.getBounds());
- });
+RENDERTHREAD_TEST(OffscreenBuffer, dirty) {
+ OffscreenBuffer buffer(renderThread.renderState(), Caches::getInstance(), 256u, 256u);
+ buffer.dirty(Rect(-100, -100, 100, 100));
+ EXPECT_EQ(android::Rect(100, 100), buffer.region.getBounds());
}
-TEST(OffscreenBufferPool, construct) {
- TestUtils::runOnRenderThread([] (renderthread::RenderThread& thread) {
- OffscreenBufferPool pool;
- EXPECT_EQ(0u, pool.getCount()) << "pool must be created empty";
- EXPECT_EQ(0u, pool.getSize()) << "pool must be created empty";
- EXPECT_EQ((uint32_t) Properties::layerPoolSize, pool.getMaxSize())
- << "pool must read size from Properties";
- });
+RENDERTHREAD_TEST(OffscreenBufferPool, construct) {
+ OffscreenBufferPool pool;
+ EXPECT_EQ(0u, pool.getCount()) << "pool must be created empty";
+ EXPECT_EQ(0u, pool.getSize()) << "pool must be created empty";
+ EXPECT_EQ((uint32_t) Properties::layerPoolSize, pool.getMaxSize())
+ << "pool must read size from Properties";
}
-TEST(OffscreenBufferPool, getPutClear) {
- TestUtils::runOnRenderThread([] (renderthread::RenderThread& thread) {
- OffscreenBufferPool pool;
+RENDERTHREAD_TEST(OffscreenBufferPool, getPutClear) {
+ OffscreenBufferPool pool;
- auto layer = pool.get(thread.renderState(), 100u, 200u);
- EXPECT_EQ(100u, layer->viewportWidth);
- EXPECT_EQ(200u, layer->viewportHeight);
+ auto layer = pool.get(renderThread.renderState(), 100u, 200u);
+ EXPECT_EQ(100u, layer->viewportWidth);
+ EXPECT_EQ(200u, layer->viewportHeight);
- ASSERT_LT(layer->getSizeInBytes(), pool.getMaxSize());
+ ASSERT_LT(layer->getSizeInBytes(), pool.getMaxSize());
- pool.putOrDelete(layer);
- ASSERT_EQ(layer->getSizeInBytes(), pool.getSize());
+ pool.putOrDelete(layer);
+ ASSERT_EQ(layer->getSizeInBytes(), pool.getSize());
- auto layer2 = pool.get(thread.renderState(), 102u, 202u);
- EXPECT_EQ(layer, layer2) << "layer should be recycled";
- ASSERT_EQ(0u, pool.getSize()) << "pool should have been emptied by removing only layer";
+ auto layer2 = pool.get(renderThread.renderState(), 102u, 202u);
+ EXPECT_EQ(layer, layer2) << "layer should be recycled";
+ ASSERT_EQ(0u, pool.getSize()) << "pool should have been emptied by removing only layer";
- pool.putOrDelete(layer);
- EXPECT_EQ(1u, pool.getCount());
- pool.clear();
- EXPECT_EQ(0u, pool.getSize());
- EXPECT_EQ(0u, pool.getCount());
- });
+ pool.putOrDelete(layer);
+ EXPECT_EQ(1u, pool.getCount());
+ pool.clear();
+ EXPECT_EQ(0u, pool.getSize());
+ EXPECT_EQ(0u, pool.getCount());
}
-TEST(OffscreenBufferPool, resize) {
- TestUtils::runOnRenderThread([] (renderthread::RenderThread& thread) {
- OffscreenBufferPool pool;
-
- auto layer = pool.get(thread.renderState(), 64u, 64u);
- layer->dirty(Rect(64, 64));
-
- // resize in place
- ASSERT_EQ(layer, pool.resize(layer, 60u, 55u));
- EXPECT_TRUE(layer->region.isEmpty()) << "In place resize should clear usage region";
- EXPECT_EQ(60u, layer->viewportWidth);
- EXPECT_EQ(55u, layer->viewportHeight);
- EXPECT_EQ(64u, layer->texture.width());
- EXPECT_EQ(64u, layer->texture.height());
-
- // resized to use different object in pool
- auto layer2 = pool.get(thread.renderState(), 128u, 128u);
- layer2->dirty(Rect(128, 128));
- EXPECT_FALSE(layer2->region.isEmpty());
- pool.putOrDelete(layer2);
- ASSERT_EQ(1u, pool.getCount());
-
- ASSERT_EQ(layer2, pool.resize(layer, 120u, 125u));
- EXPECT_TRUE(layer2->region.isEmpty()) << "Swap resize should clear usage region";
- EXPECT_EQ(120u, layer2->viewportWidth);
- EXPECT_EQ(125u, layer2->viewportHeight);
- EXPECT_EQ(128u, layer2->texture.width());
- EXPECT_EQ(128u, layer2->texture.height());
-
- // original allocation now only thing in pool
- EXPECT_EQ(1u, pool.getCount());
- EXPECT_EQ(layer->getSizeInBytes(), pool.getSize());
-
- pool.putOrDelete(layer2);
- });
+RENDERTHREAD_TEST(OffscreenBufferPool, resize) {
+ OffscreenBufferPool pool;
+
+ auto layer = pool.get(renderThread.renderState(), 64u, 64u);
+ layer->dirty(Rect(64, 64));
+
+ // resize in place
+ ASSERT_EQ(layer, pool.resize(layer, 60u, 55u));
+ EXPECT_TRUE(layer->region.isEmpty()) << "In place resize should clear usage region";
+ EXPECT_EQ(60u, layer->viewportWidth);
+ EXPECT_EQ(55u, layer->viewportHeight);
+ EXPECT_EQ(64u, layer->texture.width());
+ EXPECT_EQ(64u, layer->texture.height());
+
+ // resized to use different object in pool
+ auto layer2 = pool.get(renderThread.renderState(), 128u, 128u);
+ layer2->dirty(Rect(128, 128));
+ EXPECT_FALSE(layer2->region.isEmpty());
+ pool.putOrDelete(layer2);
+ ASSERT_EQ(1u, pool.getCount());
+
+ ASSERT_EQ(layer2, pool.resize(layer, 120u, 125u));
+ EXPECT_TRUE(layer2->region.isEmpty()) << "Swap resize should clear usage region";
+ EXPECT_EQ(120u, layer2->viewportWidth);
+ EXPECT_EQ(125u, layer2->viewportHeight);
+ EXPECT_EQ(128u, layer2->texture.width());
+ EXPECT_EQ(128u, layer2->texture.height());
+
+ // original allocation now only thing in pool
+ EXPECT_EQ(1u, pool.getCount());
+ EXPECT_EQ(layer->getSizeInBytes(), pool.getSize());
+
+ pool.putOrDelete(layer2);
}
-TEST(OffscreenBufferPool, putAndDestroy) {
- TestUtils::runOnRenderThread([] (renderthread::RenderThread& thread) {
- OffscreenBufferPool pool;
- // layer too big to return to the pool
- // Note: this relies on the fact that the pool won't reject based on max texture size
- auto hugeLayer = pool.get(thread.renderState(), pool.getMaxSize() / 64, 64);
- EXPECT_GT(hugeLayer->getSizeInBytes(), pool.getMaxSize());
- pool.putOrDelete(hugeLayer);
- EXPECT_EQ(0u, pool.getCount()); // failed to put (so was destroyed instead)
- });
+RENDERTHREAD_TEST(OffscreenBufferPool, putAndDestroy) {
+ OffscreenBufferPool pool;
+ // layer too big to return to the pool
+ // Note: this relies on the fact that the pool won't reject based on max texture size
+ auto hugeLayer = pool.get(renderThread.renderState(), pool.getMaxSize() / 64, 64);
+ EXPECT_GT(hugeLayer->getSizeInBytes(), pool.getMaxSize());
+ pool.putOrDelete(hugeLayer);
+ EXPECT_EQ(0u, pool.getCount()); // failed to put (so was destroyed instead)
+}
+
+RENDERTHREAD_TEST(OffscreenBufferPool, clear) {
+ EXPECT_EQ(0, GpuMemoryTracker::getInstanceCount(GpuObjectType::OffscreenBuffer));
+ OffscreenBufferPool pool;
+
+ // Create many buffers, with several at each size
+ std::vector<OffscreenBuffer*> buffers;
+ for (int size = 32; size <= 128; size += 32) {
+ for (int i = 0; i < 10; i++) {
+ buffers.push_back(pool.get(renderThread.renderState(), size, size));
+ }
+ }
+ EXPECT_EQ(0u, pool.getCount()) << "Expect nothing inside";
+ for (auto& buffer : buffers) pool.putOrDelete(buffer);
+ EXPECT_EQ(40u, pool.getCount()) << "Expect all items added";
+ EXPECT_EQ(40, GpuMemoryTracker::getInstanceCount(GpuObjectType::OffscreenBuffer));
+ pool.clear();
+ EXPECT_EQ(0u, pool.getCount()) << "Expect all items cleared";
+
+ EXPECT_EQ(0, GpuMemoryTracker::getInstanceCount(GpuObjectType::OffscreenBuffer));
}