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

Commit df6201b9 authored by Alec Mouri's avatar Alec Mouri
Browse files

Add a TexturePool class into layer caching

In order to reduce the risk of buffer allocations burning cycles during
rendering, add a texture pool which preallocates a set of screen-sized
buffers available for use for layer caching.

This texture pool has some properties to ease implementation details:
1. Textures are allocated synchronously. It's not very hard to add
asynchronous support, but that does add a bit of complexity and Android
12 is almost finalized. It's also not yet clear if asynchronous
allocation is needed since we can just tune the min texture pool size so
that more buffers are preallocated; although it is certainly more
flexible if we're able to defer allocations off-thread.
2. The texture pool is soft-bounded. If needed additional textures may
be allocated to allow for complex geometries, but beyond an upper bound
any additional textures may be deallocated.
3. The texture pool only supports screen-sized buffers, so we don't
allocate smaller scratch buffers to save on memory. However, typically
layer caching only caches screen-sized buffers. This also means that if
the display size changes, then the texture pool must be reallocated.
Note that the "display size" really refers to the framebuffer size, not
necessarily the app-visible display.

Bug: 184860700
Test: builds, boots
Test: libcompositionengine_test
Change-Id: Ic084310bae2b41be49c2cfe9f8c5a20ff683d1ad
parent cda04d4f
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -57,6 +57,7 @@ cc_library {
        "src/planner/LayerState.cpp",
        "src/planner/Planner.cpp",
        "src/planner/Predictor.cpp",
        "src/planner/TexturePool.cpp",
        "src/ClientCompositionRequestCache.cpp",
        "src/CompositionEngine.cpp",
        "src/Display.cpp",
@@ -107,6 +108,7 @@ cc_test {
        "tests/planner/FlattenerTest.cpp",
        "tests/planner/LayerStateTest.cpp",
        "tests/planner/PredictorTest.cpp",
        "tests/planner/TexturePoolTest.cpp",
        "tests/CompositionEngineTest.cpp",
        "tests/DisplayColorProfileTest.cpp",
        "tests/DisplayTest.cpp",
+11 −4
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@
#include <compositionengine/Output.h>
#include <compositionengine/ProjectionSpace.h>
#include <compositionengine/impl/planner/LayerState.h>
#include <compositionengine/impl/planner/TexturePool.h>
#include <renderengine/RenderEngine.h>

#include <chrono>
@@ -64,9 +65,12 @@ public:
    size_t getLayerCount() const { return mLayers.size(); }
    const Layer& getFirstLayer() const { return mLayers[0]; }
    const Rect& getBounds() const { return mBounds; }
    Rect getTextureBounds() const { return mOutputSpace.content; }
    const Region& getVisibleRegion() const { return mVisibleRegion; }
    size_t getAge() const { return mAge; }
    const std::shared_ptr<renderengine::ExternalTexture>& getBuffer() const { return mTexture; }
    std::shared_ptr<renderengine::ExternalTexture> getBuffer() const {
        return mTexture ? mTexture->get() : nullptr;
    }
    const sp<Fence>& getDrawFence() const { return mDrawFence; }
    const ProjectionSpace& getOutputSpace() const { return mOutputSpace; }
    ui::Dataspace getOutputDataspace() const { return mOutputDataspace; }
@@ -89,7 +93,7 @@ public:

    void setLastUpdate(std::chrono::steady_clock::time_point now) { mLastUpdate = now; }
    void append(const CachedSet& other) {
        mTexture = nullptr;
        mTexture.reset();
        mOutputDataspace = ui::Dataspace::UNKNOWN;
        mDrawFence = nullptr;
        mBlurLayer = nullptr;
@@ -105,7 +109,8 @@ public:
    void incrementAge() { ++mAge; }

    // Renders the cached set with the supplied output composition state.
    void render(renderengine::RenderEngine& re, const OutputCompositionState& outputState);
    void render(renderengine::RenderEngine& re, TexturePool& texturePool,
                const OutputCompositionState& outputState);

    void dump(std::string& result) const;

@@ -151,7 +156,9 @@ private:
    Region mVisibleRegion;
    size_t mAge = 0;

    std::shared_ptr<renderengine::ExternalTexture> mTexture;
    // TODO(b/190411067): This is a shared pointer only because CachedSets are copied into different
    // containers in the Flattener. Logically this should have unique ownership otherwise.
    std::shared_ptr<TexturePool::AutoTexture> mTexture;
    sp<Fence> mDrawFence;
    ProjectionSpace mOutputSpace;
    ui::Dataspace mOutputDataspace;
+9 −4
Original line number Diff line number Diff line
@@ -37,16 +37,18 @@ class Predictor;

class Flattener {
public:
    Flattener(bool enableHolePunch = false);
    Flattener(renderengine::RenderEngine& renderEngine, bool enableHolePunch = false);

    void setDisplaySize(ui::Size size) { mDisplaySize = size; }
    void setDisplaySize(ui::Size size) {
        mDisplaySize = size;
        mTexturePool.setDisplaySize(size);
    }

    NonBufferHash flattenLayers(const std::vector<const LayerState*>& layers, NonBufferHash,
                                std::chrono::steady_clock::time_point now);

    // Renders the newest cached sets with the supplied output composition state
    void renderCachedSets(renderengine::RenderEngine& re,
                          const OutputCompositionState& outputState);
    void renderCachedSets(const OutputCompositionState& outputState);

    void dump(std::string& result) const;
    void dumpLayers(std::string& result) const;
@@ -145,8 +147,11 @@ private:

    void buildCachedSets(std::chrono::steady_clock::time_point now);

    renderengine::RenderEngine& mRenderEngine;
    const bool mEnableHolePunch;

    TexturePool mTexturePool;

    ui::Size mDisplaySize;

    NonBufferHash mCurrentGeometry;
+2 −3
Original line number Diff line number Diff line
@@ -41,7 +41,7 @@ namespace compositionengine::impl::planner {
// as a more efficient representation of parts of the layer stack.
class Planner {
public:
    Planner();
    Planner(renderengine::RenderEngine& renderengine);

    void setDisplaySize(ui::Size);

@@ -59,8 +59,7 @@ public:
            compositionengine::Output::OutputLayersEnumerator<compositionengine::Output>&& layers);

    // The planner will call to the Flattener to render any pending cached set
    void renderCachedSets(renderengine::RenderEngine& re,
                          const OutputCompositionState& outputState);
    void renderCachedSets(const OutputCompositionState& outputState);

    void dump(const Vector<String16>& args, std::string&);

+102 −0
Original line number Diff line number Diff line
/*
 * Copyright 2021 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#pragma once

#include <compositionengine/Output.h>
#include <compositionengine/ProjectionSpace.h>
#include <compositionengine/impl/planner/LayerState.h>
#include <renderengine/RenderEngine.h>

#include <renderengine/ExternalTexture.h>
#include <chrono>
#include "android-base/macros.h"

namespace android::compositionengine::impl::planner {

// A pool of textures that only manages textures of a single size.
// While it is possible to define a texture pool supporting variable-sized textures to save on
// memory, it is a simpler implementation to only manage screen-sized textures. The texture pool is
// unbounded - there are a minimum number of textures preallocated. Under heavy system load, new
// textures may be allocated, but only a maximum number of retained once those textures are no
// longer necessary.
class TexturePool {
public:
    // RAII class helping with managing textures from the texture pool
    // Textures once they're no longer used should be returned to the pool instead of outright
    // deleted.
    class AutoTexture {
    public:
        AutoTexture(TexturePool& texturePool,
                    std::shared_ptr<renderengine::ExternalTexture> texture, const sp<Fence>& fence)
              : mTexturePool(texturePool), mTexture(texture), mFence(fence) {}

        ~AutoTexture() { mTexturePool.returnTexture(std::move(mTexture), mFence); }

        sp<Fence> getReadyFence() { return mFence; }

        void setReadyFence(const sp<Fence>& fence) { mFence = fence; }

        // Disable copying and assigning
        AutoTexture(const AutoTexture&) = delete;
        AutoTexture& operator=(const AutoTexture&) = delete;

        // Gets a pointer to the underlying external texture
        const std::shared_ptr<renderengine::ExternalTexture>& get() const { return mTexture; }

    private:
        TexturePool& mTexturePool;
        std::shared_ptr<renderengine::ExternalTexture> mTexture;
        sp<Fence> mFence;
    };

    TexturePool(renderengine::RenderEngine& renderEngine) : mRenderEngine(renderEngine) {}

    virtual ~TexturePool() = default;

    // Sets the display size for the texture pool.
    // This will trigger a reallocation for all remaining textures in the pool.
    // setDisplaySize must be called for the texture pool to be used.
    void setDisplaySize(ui::Size size);

    // Borrows a new texture from the pool.
    // If the pool is currently starved of textures, then a new texture is generated.
    // When the AutoTexture object is destroyed, the scratch texture is automatically returned
    // to the pool.
    std::shared_ptr<AutoTexture> borrowTexture();

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 {
        std::shared_ptr<renderengine::ExternalTexture> texture;
        sp<Fence> fence;
    };

    std::deque<Entry> mPool;

private:
    std::shared_ptr<renderengine::ExternalTexture> genTexture();
    // Returns a previously borrowed texture to the pool.
    void returnTexture(std::shared_ptr<renderengine::ExternalTexture>&& texture,
                       const sp<Fence>& fence);
    renderengine::RenderEngine& mRenderEngine;
    ui::Size mSize;
};

} // namespace android::compositionengine::impl::planner
Loading