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

Commit 3e4466c1 authored by Valerie Hau's avatar Valerie Hau Committed by Android (Google) Code Review
Browse files

Merge "Caching between SF and HWC for BufferStateLayers"

parents 97a57e0e b28f007e
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -366,7 +366,7 @@ void BufferQueueLayer::setHwcLayerBuffer(const sp<const DisplayDevice>& display)
    uint32_t hwcSlot = 0;
    sp<GraphicBuffer> hwcBuffer;
    (*outputLayer->editState().hwc)
            .hwcBufferCache.getHwcBuffer(mActiveBufferSlot, mActiveBuffer, &hwcSlot, &hwcBuffer);
            .hwcBufferCache.getHwcBuffer(mActiveBuffer, &hwcSlot, &hwcBuffer);

    auto acquireFence = mConsumer->getCurrentFence();
    auto error = hwcLayer->setBuffer(hwcSlot, hwcBuffer, acquireFence);
+6 −3
Original line number Diff line number Diff line
@@ -565,14 +565,17 @@ status_t BufferStateLayer::updateFrameNumber(nsecs_t /*latchTime*/) {
void BufferStateLayer::setHwcLayerBuffer(const sp<const DisplayDevice>& display) {
    const auto outputLayer = findOutputLayerForDisplay(display);
    LOG_FATAL_IF(!outputLayer || !outputLayer->getState().hwc);
    auto& hwcLayer = (*outputLayer->getState().hwc).hwcLayer;
    auto& hwcInfo = *outputLayer->editState().hwc;
    auto& hwcLayer = hwcInfo.hwcLayer;

    const State& s(getDrawingState());

    // TODO(marissaw): support more than one slot
    // obtain slot
    uint32_t hwcSlot = 0;
    sp<GraphicBuffer> buffer;
    hwcInfo.hwcBufferCache.getHwcBuffer(s.buffer, &hwcSlot, &buffer);

    auto error = hwcLayer->setBuffer(hwcSlot, s.buffer, s.acquireFence);
    auto error = hwcLayer->setBuffer(hwcSlot, buffer, s.acquireFence);
    if (error != HWC2::Error::None) {
        ALOGE("[%s] Failed to set buffer %p: %s (%d)", mName.string(),
              s.buffer->handle, to_string(error).c_str(), static_cast<int32_t>(error));
+16 −6
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@
#include <cstdint>
#include <vector>

#include <gui/BufferQueue.h>
#include <utils/StrongPointer.h>

namespace android {
@@ -39,19 +40,28 @@ namespace compositionengine::impl {
class HwcBufferCache {
public:
    HwcBufferCache();

    // Given a buffer queue slot and buffer, return the HWC cache slot and
    // Given a buffer, return the HWC cache slot and
    // buffer to be sent to HWC.
    //
    // outBuffer is set to buffer when buffer is not in the HWC cache;
    // otherwise, outBuffer is set to nullptr.
    void getHwcBuffer(int slot, const sp<GraphicBuffer>& buffer, uint32_t* outSlot,
    void getHwcBuffer(const sp<GraphicBuffer>& buffer, uint32_t* outSlot,
                      sp<GraphicBuffer>* outBuffer);

protected:
    int getSlot(const sp<GraphicBuffer>& buffer);
    int getLeastRecentlyUsedSlot();
    uint64_t getCounter();

private:
    // a vector as we expect "slot" to be in the range of [0, 63] (that is,
    // less than BufferQueue::NUM_BUFFER_SLOTS).
    std::vector<sp<GraphicBuffer>> mBuffers;
    // an array where the index corresponds to a slot and the value corresponds to a (counter,
    // buffer) pair. "counter" is a unique value that indicates the last time this slot was updated
    // or used and allows us to keep track of the least-recently used buffer.
    std::pair<uint64_t, wp<GraphicBuffer>> mBuffers[BufferQueue::NUM_BUFFER_SLOTS];

    // The cache increments this counter value when a slot is updated or used.
    // Used to track the least recently-used buffer
    uint64_t mCounter = 1;
};

} // namespace compositionengine::impl
+27 −13
Original line number Diff line number Diff line
@@ -21,31 +21,45 @@
namespace android::compositionengine::impl {

HwcBufferCache::HwcBufferCache() {
    mBuffers.reserve(BufferQueue::NUM_BUFFER_SLOTS);
    std::fill(std::begin(mBuffers), std::end(mBuffers),
              std::pair<uint64_t, wp<GraphicBuffer>>(0, nullptr));
}
int HwcBufferCache::getSlot(const sp<GraphicBuffer>& buffer) {
    // search for cached buffer first
    for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
        if (mBuffers[i].second == buffer) {
            return i;
        }
    }

void HwcBufferCache::getHwcBuffer(int slot, const sp<GraphicBuffer>& buffer, uint32_t* outSlot,
                                  sp<GraphicBuffer>* outBuffer) {
    if (slot == BufferQueue::INVALID_BUFFER_SLOT || slot < 0) {
        // default to slot 0
        slot = 0;
    // use the least-recently used slot
    return getLeastRecentlyUsedSlot();
}

    if (static_cast<size_t>(slot) >= mBuffers.size()) {
        mBuffers.resize(slot + 1);
int HwcBufferCache::getLeastRecentlyUsedSlot() {
    auto iter = std::min_element(std::begin(mBuffers), std::end(mBuffers));
    return std::distance(std::begin(mBuffers), iter);
}

    *outSlot = slot;
void HwcBufferCache::getHwcBuffer(const sp<GraphicBuffer>& buffer, uint32_t* outSlot,
                                  sp<GraphicBuffer>* outBuffer) {
    *outSlot = getSlot(buffer);

    if (mBuffers[slot] == buffer) {
    auto& [currentCounter, currentBuffer] = mBuffers[*outSlot];
    if (currentBuffer == buffer) {
        // already cached in HWC, skip sending the buffer
        *outBuffer = nullptr;
        currentCounter = getCounter();
    } else {
        *outBuffer = buffer;

        // update cache
        mBuffers[slot] = buffer;
        currentBuffer = buffer;
        currentCounter = getCounter();
    }
}

uint64_t HwcBufferCache::getCounter() {
    return mCounter++;
}
} // namespace android::compositionengine::impl
+60 −42
Original line number Diff line number Diff line
@@ -22,60 +22,78 @@
namespace android::compositionengine {
namespace {

class TestableHwcBufferCache : public impl::HwcBufferCache {
public:
    void getHwcBuffer(const sp<GraphicBuffer>& buffer, uint32_t* outSlot,
                      sp<GraphicBuffer>* outBuffer) {
        HwcBufferCache::getHwcBuffer(buffer, outSlot, outBuffer);
    }
    int getSlot(const sp<GraphicBuffer>& buffer) { return HwcBufferCache::getSlot(buffer); }
    int getLeastRecentlyUsedSlot() { return HwcBufferCache::getLeastRecentlyUsedSlot(); }
};

class HwcBufferCacheTest : public testing::Test {
public:
    ~HwcBufferCacheTest() override = default;

    void testSlot(const int inSlot, const uint32_t expectedSlot) {
    TestableHwcBufferCache mCache;
    sp<GraphicBuffer> mBuffer1{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
    sp<GraphicBuffer> mBuffer2{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
};

TEST_F(HwcBufferCacheTest, testSlot) {
    uint32_t outSlot;
    sp<GraphicBuffer> outBuffer;

    // The first time, the output  is the same as the input
        mCache.getHwcBuffer(inSlot, mBuffer1, &outSlot, &outBuffer);
        EXPECT_EQ(expectedSlot, outSlot);
    mCache.getHwcBuffer(mBuffer1, &outSlot, &outBuffer);
    EXPECT_EQ(0, outSlot);
    EXPECT_EQ(mBuffer1, outBuffer);

    // The second time with the same buffer, the outBuffer is nullptr.
        mCache.getHwcBuffer(inSlot, mBuffer1, &outSlot, &outBuffer);
        EXPECT_EQ(expectedSlot, outSlot);
    mCache.getHwcBuffer(mBuffer1, &outSlot, &outBuffer);
    EXPECT_EQ(0, outSlot);
    EXPECT_EQ(nullptr, outBuffer.get());

    // With a new buffer, the outBuffer is the input.
        mCache.getHwcBuffer(inSlot, mBuffer2, &outSlot, &outBuffer);
        EXPECT_EQ(expectedSlot, outSlot);
    mCache.getHwcBuffer(mBuffer2, &outSlot, &outBuffer);
    EXPECT_EQ(1, outSlot);
    EXPECT_EQ(mBuffer2, outBuffer);

    // Again, the second request with the same buffer sets outBuffer to nullptr.
        mCache.getHwcBuffer(inSlot, mBuffer2, &outSlot, &outBuffer);
        EXPECT_EQ(expectedSlot, outSlot);
    mCache.getHwcBuffer(mBuffer2, &outSlot, &outBuffer);
    EXPECT_EQ(1, outSlot);
    EXPECT_EQ(nullptr, outBuffer.get());

    // Setting a slot to use nullptr lookslike works, but note that
    // the output values make it look like no new buffer is being set....
        mCache.getHwcBuffer(inSlot, sp<GraphicBuffer>(), &outSlot, &outBuffer);
        EXPECT_EQ(expectedSlot, outSlot);
    mCache.getHwcBuffer(sp<GraphicBuffer>(), &outSlot, &outBuffer);
    EXPECT_EQ(2, outSlot);
    EXPECT_EQ(nullptr, outBuffer.get());
}

    impl::HwcBufferCache mCache;
    sp<GraphicBuffer> mBuffer1{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
    sp<GraphicBuffer> mBuffer2{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
};
TEST_F(HwcBufferCacheTest, testGetLeastRecentlyUsedSlot) {
    int slot;
    uint32_t outSlot;
    sp<GraphicBuffer> outBuffer;

TEST_F(HwcBufferCacheTest, cacheWorksForSlotZero) {
    testSlot(0, 0);
    // fill up cache
    for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
        sp<GraphicBuffer> buf{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
        mCache.getHwcBuffer(buf, &outSlot, &outBuffer);
        EXPECT_EQ(buf, outBuffer);
        EXPECT_EQ(i, outSlot);
    }

TEST_F(HwcBufferCacheTest, cacheWorksForMaxSlot) {
    testSlot(BufferQueue::NUM_BUFFER_SLOTS - 1, BufferQueue::NUM_BUFFER_SLOTS - 1);
}
    slot = mCache.getLeastRecentlyUsedSlot();
    EXPECT_EQ(0, slot);

TEST_F(HwcBufferCacheTest, cacheMapsNegativeSlotToZero) {
    testSlot(-123, 0);
}
    mCache.getHwcBuffer(mBuffer1, &outSlot, &outBuffer);
    EXPECT_EQ(0, outSlot);
    EXPECT_EQ(mBuffer1, outBuffer);

TEST_F(HwcBufferCacheTest, cacheMapsInvalidBufferSlotToZero) {
    testSlot(BufferQueue::INVALID_BUFFER_SLOT, 0);
    slot = mCache.getLeastRecentlyUsedSlot();
    EXPECT_EQ(1, slot);
}

} // namespace
Loading