Loading services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/TexturePool.h +10 −7 Original line number Diff line number Diff line Loading @@ -66,7 +66,7 @@ public: TexturePool(renderengine::RenderEngine& renderEngine) : mRenderEngine(renderEngine), mEnabled(false) {} virtual ~TexturePool() = default; virtual ~TexturePool(); // Sets the display size for the texture pool. // This will trigger a reallocation for all remaining textures in the pool. Loading @@ -83,11 +83,10 @@ public: // be held by the pool. This is useful when the active display changes. void setEnabled(bool enable); void dump(std::string& out) const; void dump(std::string& out) const EXCLUDES(mMutex); protected: // Proteted visibility so that they can be used for testing const static constexpr size_t kMinPoolSize = 3; const static constexpr size_t kMaxPoolSize = 4; struct Entry { Loading @@ -96,16 +95,20 @@ protected: }; std::deque<Entry> mPool; std::future<std::shared_ptr<renderengine::ExternalTexture>> mGenTextureFuture; private: std::shared_ptr<renderengine::ExternalTexture> genTexture(); std::shared_ptr<renderengine::ExternalTexture> genTexture(ui::Size size); // Returns a previously borrowed texture to the pool. void returnTexture(std::shared_ptr<renderengine::ExternalTexture>&& texture, const sp<Fence>& fence); void allocatePool(); renderengine::RenderEngine& mRenderEngine; ui::Size mSize; void genTextureAsyncIfNeeded() REQUIRES(mMutex); void resetPool() REQUIRES(mMutex); renderengine::RenderEngine& mRenderEngine GUARDED_BY(mRenderEngineMutex); ui::Size mSize GUARDED_BY(mMutex); bool mEnabled; mutable std::mutex mMutex; mutable std::mutex mRenderEngineMutex; }; } // namespace android::compositionengine::impl::planner services/surfaceflinger/CompositionEngine/src/planner/TexturePool.cpp +50 −14 Original line number Diff line number Diff line Loading @@ -25,31 +25,61 @@ namespace android::compositionengine::impl::planner { void TexturePool::allocatePool() { TexturePool::~TexturePool() { if (mGenTextureFuture.valid()) { mGenTextureFuture.get(); } } void TexturePool::resetPool() { if (mGenTextureFuture.valid()) { mGenTextureFuture.get(); } mPool.clear(); if (mEnabled && mSize.isValid()) { mPool.resize(kMinPoolSize); std::generate_n(mPool.begin(), kMinPoolSize, [&]() { return Entry{genTexture(), nullptr}; }); genTextureAsyncIfNeeded(); } // Generate a new texture asynchronously so it will not require allocation on the main // thread. void TexturePool::genTextureAsyncIfNeeded() { if (mEnabled && mSize.isValid() && !mGenTextureFuture.valid()) { mGenTextureFuture = std::async( std::launch::async, [&](ui::Size size) { return genTexture(size); }, mSize); } } void TexturePool::setDisplaySize(ui::Size size) { std::lock_guard lock(mMutex); if (mSize == size) { return; } mSize = size; allocatePool(); resetPool(); } std::shared_ptr<TexturePool::AutoTexture> TexturePool::borrowTexture() { if (mPool.empty()) { return std::make_shared<AutoTexture>(*this, genTexture(), nullptr); std::lock_guard lock(mMutex); std::shared_ptr<TexturePool::AutoTexture> tex; if (mGenTextureFuture.valid()) { tex = std::make_shared<AutoTexture>(*this, mGenTextureFuture.get(), nullptr); } else { tex = std::make_shared<AutoTexture>(*this, genTexture(mSize), nullptr); } // Speculatively generate a new texture, so that the next call does not need // to wait for allocation. genTextureAsyncIfNeeded(); return tex; } const auto entry = mPool.front(); mPool.pop_front(); if (mPool.empty()) { std::lock_guard lock(mMutex); // Similiarly generate a new texture when lending out the last entry, so that // the next call does not need to wait for allocation. genTextureAsyncIfNeeded(); } return std::make_shared<AutoTexture>(*this, entry.texture, entry.fence); } Loading @@ -60,6 +90,8 @@ void TexturePool::returnTexture(std::shared_ptr<renderengine::ExternalTexture>&& return; } std::lock_guard lock(mMutex); // Or the texture on the floor if the pool is no longer tracking textures of the same size. if (static_cast<int32_t>(texture->getBuffer()->getWidth()) != mSize.getWidth() || static_cast<int32_t>(texture->getBuffer()->getHeight()) != mSize.getHeight()) { Loading @@ -80,13 +112,14 @@ void TexturePool::returnTexture(std::shared_ptr<renderengine::ExternalTexture>&& mPool.push_back({std::move(texture), fence}); } std::shared_ptr<renderengine::ExternalTexture> TexturePool::genTexture() { LOG_ALWAYS_FATAL_IF(!mSize.isValid(), "Attempted to generate texture with invalid size"); std::shared_ptr<renderengine::ExternalTexture> TexturePool::genTexture(ui::Size size) { std::lock_guard lock(mRenderEngineMutex); LOG_ALWAYS_FATAL_IF(!size.isValid(), "Attempted to generate texture with invalid size"); return std::make_shared< renderengine::impl:: ExternalTexture>(sp<GraphicBuffer>:: make(static_cast<uint32_t>(mSize.getWidth()), static_cast<uint32_t>(mSize.getHeight()), make(static_cast<uint32_t>(size.getWidth()), static_cast<uint32_t>(size.getHeight()), HAL_PIXEL_FORMAT_RGBA_8888, 1U, static_cast<uint64_t>( GraphicBuffer::USAGE_HW_RENDER | Loading @@ -100,10 +133,13 @@ std::shared_ptr<renderengine::ExternalTexture> TexturePool::genTexture() { void TexturePool::setEnabled(bool enabled) { mEnabled = enabled; allocatePool(); std::lock_guard lock(mMutex); resetPool(); } void TexturePool::dump(std::string& out) const { std::lock_guard lock(mMutex); base::StringAppendF(&out, "TexturePool (%s) has %zu buffers of size [%" PRId32 ", %" PRId32 "]\n", mEnabled ? "enabled" : "disabled", mPool.size(), mSize.width, mSize.height); Loading services/surfaceflinger/CompositionEngine/tests/planner/TexturePoolTest.cpp +11 −22 Original line number Diff line number Diff line Loading @@ -32,9 +32,9 @@ class TestableTexturePool : public TexturePool { public: TestableTexturePool(renderengine::RenderEngine& renderEngine) : TexturePool(renderEngine) {} size_t getMinPoolSize() const { return kMinPoolSize; } size_t getMaxPoolSize() const { return kMaxPoolSize; } size_t getPoolSize() const { return mPool.size(); } size_t isGenTextureFutureValid() const { return mGenTextureFuture.valid(); } }; struct TexturePoolTest : public testing::Test { Loading @@ -56,16 +56,8 @@ struct TexturePoolTest : public testing::Test { TestableTexturePool mTexturePool = TestableTexturePool(mRenderEngine); }; TEST_F(TexturePoolTest, preallocatesMinPool) { EXPECT_EQ(mTexturePool.getMinPoolSize(), mTexturePool.getPoolSize()); } TEST_F(TexturePoolTest, doesNotAllocateBeyondMinPool) { for (size_t i = 0; i < mTexturePool.getMinPoolSize() + 1; i++) { auto texture = mTexturePool.borrowTexture(); } EXPECT_EQ(mTexturePool.getMinPoolSize(), mTexturePool.getPoolSize()); TEST_F(TexturePoolTest, preallocatesZeroSizePool) { EXPECT_EQ(mTexturePool.getPoolSize(), 0u); } TEST_F(TexturePoolTest, cyclesUpToMaxPoolSize) { Loading Loading @@ -119,10 +111,10 @@ TEST_F(TexturePoolTest, reallocatesWhenDisplaySizeChanges) { static_cast<int32_t>(texture->get()->getBuffer()->getHeight())); mTexturePool.setDisplaySize(kDisplaySizeTwo); EXPECT_EQ(mTexturePool.getMinPoolSize(), mTexturePool.getPoolSize()); EXPECT_EQ(mTexturePool.getPoolSize(), 0u); texture.reset(); // When the texture is returned to the pool, the pool now destroys it. EXPECT_EQ(mTexturePool.getMinPoolSize(), mTexturePool.getPoolSize()); EXPECT_EQ(mTexturePool.getPoolSize(), 0u); texture = mTexturePool.borrowTexture(); EXPECT_EQ(kDisplaySizeTwo.getWidth(), Loading @@ -132,14 +124,11 @@ TEST_F(TexturePoolTest, reallocatesWhenDisplaySizeChanges) { } TEST_F(TexturePoolTest, freesBuffersWhenDisabled) { EXPECT_EQ(mTexturePool.getPoolSize(), mTexturePool.getMinPoolSize()); std::deque<std::shared_ptr<TexturePool::AutoTexture>> textures; for (size_t i = 0; i < mTexturePool.getMinPoolSize() - 1; i++) { for (size_t i = 0; i < 2; i++) { textures.emplace_back(mTexturePool.borrowTexture()); } EXPECT_EQ(mTexturePool.getPoolSize(), 1u); mTexturePool.setEnabled(false); EXPECT_EQ(mTexturePool.getPoolSize(), 0u); Loading @@ -148,12 +137,11 @@ TEST_F(TexturePoolTest, freesBuffersWhenDisabled) { } TEST_F(TexturePoolTest, doesNotHoldBuffersWhenDisabled) { EXPECT_EQ(mTexturePool.getPoolSize(), mTexturePool.getMinPoolSize()); mTexturePool.setEnabled(false); EXPECT_EQ(mTexturePool.getPoolSize(), 0u); std::deque<std::shared_ptr<TexturePool::AutoTexture>> textures; for (size_t i = 0; i < mTexturePool.getMinPoolSize() - 1; i++) { for (size_t i = 0; i < 2; i++) { textures.emplace_back(mTexturePool.borrowTexture()); } Loading @@ -162,12 +150,13 @@ TEST_F(TexturePoolTest, doesNotHoldBuffersWhenDisabled) { EXPECT_EQ(mTexturePool.getPoolSize(), 0u); } TEST_F(TexturePoolTest, reallocatesWhenReEnabled) { EXPECT_EQ(mTexturePool.getPoolSize(), mTexturePool.getMinPoolSize()); TEST_F(TexturePoolTest, genFutureWhenReEnabled) { mTexturePool.setEnabled(false); EXPECT_EQ(mTexturePool.getPoolSize(), 0u); EXPECT_FALSE(mTexturePool.isGenTextureFutureValid()); mTexturePool.setEnabled(true); EXPECT_EQ(mTexturePool.getPoolSize(), mTexturePool.getMinPoolSize()); EXPECT_EQ(mTexturePool.getPoolSize(), 0u); EXPECT_TRUE(mTexturePool.isGenTextureFutureValid()); } } // namespace Loading Loading
services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/TexturePool.h +10 −7 Original line number Diff line number Diff line Loading @@ -66,7 +66,7 @@ public: TexturePool(renderengine::RenderEngine& renderEngine) : mRenderEngine(renderEngine), mEnabled(false) {} virtual ~TexturePool() = default; virtual ~TexturePool(); // Sets the display size for the texture pool. // This will trigger a reallocation for all remaining textures in the pool. Loading @@ -83,11 +83,10 @@ public: // be held by the pool. This is useful when the active display changes. void setEnabled(bool enable); void dump(std::string& out) const; void dump(std::string& out) const EXCLUDES(mMutex); protected: // Proteted visibility so that they can be used for testing const static constexpr size_t kMinPoolSize = 3; const static constexpr size_t kMaxPoolSize = 4; struct Entry { Loading @@ -96,16 +95,20 @@ protected: }; std::deque<Entry> mPool; std::future<std::shared_ptr<renderengine::ExternalTexture>> mGenTextureFuture; private: std::shared_ptr<renderengine::ExternalTexture> genTexture(); std::shared_ptr<renderengine::ExternalTexture> genTexture(ui::Size size); // Returns a previously borrowed texture to the pool. void returnTexture(std::shared_ptr<renderengine::ExternalTexture>&& texture, const sp<Fence>& fence); void allocatePool(); renderengine::RenderEngine& mRenderEngine; ui::Size mSize; void genTextureAsyncIfNeeded() REQUIRES(mMutex); void resetPool() REQUIRES(mMutex); renderengine::RenderEngine& mRenderEngine GUARDED_BY(mRenderEngineMutex); ui::Size mSize GUARDED_BY(mMutex); bool mEnabled; mutable std::mutex mMutex; mutable std::mutex mRenderEngineMutex; }; } // namespace android::compositionengine::impl::planner
services/surfaceflinger/CompositionEngine/src/planner/TexturePool.cpp +50 −14 Original line number Diff line number Diff line Loading @@ -25,31 +25,61 @@ namespace android::compositionengine::impl::planner { void TexturePool::allocatePool() { TexturePool::~TexturePool() { if (mGenTextureFuture.valid()) { mGenTextureFuture.get(); } } void TexturePool::resetPool() { if (mGenTextureFuture.valid()) { mGenTextureFuture.get(); } mPool.clear(); if (mEnabled && mSize.isValid()) { mPool.resize(kMinPoolSize); std::generate_n(mPool.begin(), kMinPoolSize, [&]() { return Entry{genTexture(), nullptr}; }); genTextureAsyncIfNeeded(); } // Generate a new texture asynchronously so it will not require allocation on the main // thread. void TexturePool::genTextureAsyncIfNeeded() { if (mEnabled && mSize.isValid() && !mGenTextureFuture.valid()) { mGenTextureFuture = std::async( std::launch::async, [&](ui::Size size) { return genTexture(size); }, mSize); } } void TexturePool::setDisplaySize(ui::Size size) { std::lock_guard lock(mMutex); if (mSize == size) { return; } mSize = size; allocatePool(); resetPool(); } std::shared_ptr<TexturePool::AutoTexture> TexturePool::borrowTexture() { if (mPool.empty()) { return std::make_shared<AutoTexture>(*this, genTexture(), nullptr); std::lock_guard lock(mMutex); std::shared_ptr<TexturePool::AutoTexture> tex; if (mGenTextureFuture.valid()) { tex = std::make_shared<AutoTexture>(*this, mGenTextureFuture.get(), nullptr); } else { tex = std::make_shared<AutoTexture>(*this, genTexture(mSize), nullptr); } // Speculatively generate a new texture, so that the next call does not need // to wait for allocation. genTextureAsyncIfNeeded(); return tex; } const auto entry = mPool.front(); mPool.pop_front(); if (mPool.empty()) { std::lock_guard lock(mMutex); // Similiarly generate a new texture when lending out the last entry, so that // the next call does not need to wait for allocation. genTextureAsyncIfNeeded(); } return std::make_shared<AutoTexture>(*this, entry.texture, entry.fence); } Loading @@ -60,6 +90,8 @@ void TexturePool::returnTexture(std::shared_ptr<renderengine::ExternalTexture>&& return; } std::lock_guard lock(mMutex); // Or the texture on the floor if the pool is no longer tracking textures of the same size. if (static_cast<int32_t>(texture->getBuffer()->getWidth()) != mSize.getWidth() || static_cast<int32_t>(texture->getBuffer()->getHeight()) != mSize.getHeight()) { Loading @@ -80,13 +112,14 @@ void TexturePool::returnTexture(std::shared_ptr<renderengine::ExternalTexture>&& mPool.push_back({std::move(texture), fence}); } std::shared_ptr<renderengine::ExternalTexture> TexturePool::genTexture() { LOG_ALWAYS_FATAL_IF(!mSize.isValid(), "Attempted to generate texture with invalid size"); std::shared_ptr<renderengine::ExternalTexture> TexturePool::genTexture(ui::Size size) { std::lock_guard lock(mRenderEngineMutex); LOG_ALWAYS_FATAL_IF(!size.isValid(), "Attempted to generate texture with invalid size"); return std::make_shared< renderengine::impl:: ExternalTexture>(sp<GraphicBuffer>:: make(static_cast<uint32_t>(mSize.getWidth()), static_cast<uint32_t>(mSize.getHeight()), make(static_cast<uint32_t>(size.getWidth()), static_cast<uint32_t>(size.getHeight()), HAL_PIXEL_FORMAT_RGBA_8888, 1U, static_cast<uint64_t>( GraphicBuffer::USAGE_HW_RENDER | Loading @@ -100,10 +133,13 @@ std::shared_ptr<renderengine::ExternalTexture> TexturePool::genTexture() { void TexturePool::setEnabled(bool enabled) { mEnabled = enabled; allocatePool(); std::lock_guard lock(mMutex); resetPool(); } void TexturePool::dump(std::string& out) const { std::lock_guard lock(mMutex); base::StringAppendF(&out, "TexturePool (%s) has %zu buffers of size [%" PRId32 ", %" PRId32 "]\n", mEnabled ? "enabled" : "disabled", mPool.size(), mSize.width, mSize.height); Loading
services/surfaceflinger/CompositionEngine/tests/planner/TexturePoolTest.cpp +11 −22 Original line number Diff line number Diff line Loading @@ -32,9 +32,9 @@ class TestableTexturePool : public TexturePool { public: TestableTexturePool(renderengine::RenderEngine& renderEngine) : TexturePool(renderEngine) {} size_t getMinPoolSize() const { return kMinPoolSize; } size_t getMaxPoolSize() const { return kMaxPoolSize; } size_t getPoolSize() const { return mPool.size(); } size_t isGenTextureFutureValid() const { return mGenTextureFuture.valid(); } }; struct TexturePoolTest : public testing::Test { Loading @@ -56,16 +56,8 @@ struct TexturePoolTest : public testing::Test { TestableTexturePool mTexturePool = TestableTexturePool(mRenderEngine); }; TEST_F(TexturePoolTest, preallocatesMinPool) { EXPECT_EQ(mTexturePool.getMinPoolSize(), mTexturePool.getPoolSize()); } TEST_F(TexturePoolTest, doesNotAllocateBeyondMinPool) { for (size_t i = 0; i < mTexturePool.getMinPoolSize() + 1; i++) { auto texture = mTexturePool.borrowTexture(); } EXPECT_EQ(mTexturePool.getMinPoolSize(), mTexturePool.getPoolSize()); TEST_F(TexturePoolTest, preallocatesZeroSizePool) { EXPECT_EQ(mTexturePool.getPoolSize(), 0u); } TEST_F(TexturePoolTest, cyclesUpToMaxPoolSize) { Loading Loading @@ -119,10 +111,10 @@ TEST_F(TexturePoolTest, reallocatesWhenDisplaySizeChanges) { static_cast<int32_t>(texture->get()->getBuffer()->getHeight())); mTexturePool.setDisplaySize(kDisplaySizeTwo); EXPECT_EQ(mTexturePool.getMinPoolSize(), mTexturePool.getPoolSize()); EXPECT_EQ(mTexturePool.getPoolSize(), 0u); texture.reset(); // When the texture is returned to the pool, the pool now destroys it. EXPECT_EQ(mTexturePool.getMinPoolSize(), mTexturePool.getPoolSize()); EXPECT_EQ(mTexturePool.getPoolSize(), 0u); texture = mTexturePool.borrowTexture(); EXPECT_EQ(kDisplaySizeTwo.getWidth(), Loading @@ -132,14 +124,11 @@ TEST_F(TexturePoolTest, reallocatesWhenDisplaySizeChanges) { } TEST_F(TexturePoolTest, freesBuffersWhenDisabled) { EXPECT_EQ(mTexturePool.getPoolSize(), mTexturePool.getMinPoolSize()); std::deque<std::shared_ptr<TexturePool::AutoTexture>> textures; for (size_t i = 0; i < mTexturePool.getMinPoolSize() - 1; i++) { for (size_t i = 0; i < 2; i++) { textures.emplace_back(mTexturePool.borrowTexture()); } EXPECT_EQ(mTexturePool.getPoolSize(), 1u); mTexturePool.setEnabled(false); EXPECT_EQ(mTexturePool.getPoolSize(), 0u); Loading @@ -148,12 +137,11 @@ TEST_F(TexturePoolTest, freesBuffersWhenDisabled) { } TEST_F(TexturePoolTest, doesNotHoldBuffersWhenDisabled) { EXPECT_EQ(mTexturePool.getPoolSize(), mTexturePool.getMinPoolSize()); mTexturePool.setEnabled(false); EXPECT_EQ(mTexturePool.getPoolSize(), 0u); std::deque<std::shared_ptr<TexturePool::AutoTexture>> textures; for (size_t i = 0; i < mTexturePool.getMinPoolSize() - 1; i++) { for (size_t i = 0; i < 2; i++) { textures.emplace_back(mTexturePool.borrowTexture()); } Loading @@ -162,12 +150,13 @@ TEST_F(TexturePoolTest, doesNotHoldBuffersWhenDisabled) { EXPECT_EQ(mTexturePool.getPoolSize(), 0u); } TEST_F(TexturePoolTest, reallocatesWhenReEnabled) { EXPECT_EQ(mTexturePool.getPoolSize(), mTexturePool.getMinPoolSize()); TEST_F(TexturePoolTest, genFutureWhenReEnabled) { mTexturePool.setEnabled(false); EXPECT_EQ(mTexturePool.getPoolSize(), 0u); EXPECT_FALSE(mTexturePool.isGenTextureFutureValid()); mTexturePool.setEnabled(true); EXPECT_EQ(mTexturePool.getPoolSize(), mTexturePool.getMinPoolSize()); EXPECT_EQ(mTexturePool.getPoolSize(), 0u); EXPECT_TRUE(mTexturePool.isGenTextureFutureValid()); } } // namespace Loading