Fix OffscreenBuffer leak
am: 74af6e2
* commit '74af6e282f8a8f75928a071e8200039517cf5c12':
Fix OffscreenBuffer leak
Change-Id: I24c16488d73588efe15e64ab711f8d3bc7a580b7
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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...");
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
RENDERTHREAD_TEST(OffscreenBufferPool, resize) {
|
||||
OffscreenBufferPool pool;
|
||||
|
||||
auto layer = pool.get(thread.renderState(), 64u, 64u);
|
||||
layer->dirty(Rect(64, 64));
|
||||
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());
|
||||
// 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());
|
||||
// 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());
|
||||
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());
|
||||
// original allocation now only thing in pool
|
||||
EXPECT_EQ(1u, pool.getCount());
|
||||
EXPECT_EQ(layer->getSizeInBytes(), pool.getSize());
|
||||
|
||||
pool.putOrDelete(layer2);
|
||||
});
|
||||
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));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user