Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit bfcc6102 authored by Huihong Luo's avatar Huihong Luo Committed by Leon Scroggins
Browse files

Generate texture pool asynchronously

This improves performance by moving allocateHelper to a different
thread, which is launched early so that the allocation is ready when
it is needed. In addition, each display will only need at most one
additional texture per frame, so replace the idea of a minimum pool
with making sure there is either at least one texture in the pool
or a future which will generate one.

Bug: 256184546
Test: atest libcompositionengine_test
Change-Id: Icb92a85c07b9f2911d1365aa9bbc8d0750a8c116
parent 206cc88d
Loading
Loading
Loading
Loading
+10 −7
Original line number Diff line number Diff line
@@ -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.
@@ -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 {
@@ -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
+50 −14
Original line number Diff line number Diff line
@@ -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);
}

@@ -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()) {
@@ -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 |
@@ -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);
+11 −22
Original line number Diff line number Diff line
@@ -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 {
@@ -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) {
@@ -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(),
@@ -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);

@@ -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());
    }

@@ -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