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

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

Merge "Caching between SF and HWC extended to Buffer State Layers"

parents 19dc6a17 6f89c379
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -362,7 +362,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
@@ -570,14 +570,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:
    bool getSlot(const sp<GraphicBuffer>& buffer, uint32_t* outSlot);
    uint32_t 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
+34 −13
Original line number Diff line number Diff line
@@ -21,31 +21,52 @@
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));
}
bool HwcBufferCache::getSlot(const sp<GraphicBuffer>& buffer, uint32_t* outSlot) {
    // search for cached buffer first
    for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
        // Weak pointers in the cache may have had their object destroyed.
        // Comparisons between weak pointers will accurately reflect this case,
        // but comparisons between weak and strong may not.  Thus, we create a weak
        // pointer from strong pointer buffer
        wp<GraphicBuffer> weakCopy(buffer);
        if (mBuffers[i].second == weakCopy) {
            *outSlot = i;
            return true;
        }
    }

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
    *outSlot = getLeastRecentlyUsedSlot();
    return false;
}

    if (static_cast<size_t>(slot) >= mBuffers.size()) {
        mBuffers.resize(slot + 1);
uint32_t 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) {
    bool cached = getSlot(buffer, outSlot);

    if (mBuffers[slot] == buffer) {
    auto& [currentCounter, currentBuffer] = mBuffers[*outSlot];
    if (cached) {
        // 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
+62 −42
Original line number Diff line number Diff line
@@ -22,60 +22,80 @@
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);
    }
    bool getSlot(const sp<GraphicBuffer>& buffer, uint32_t* outSlot) {
        return HwcBufferCache::getSlot(buffer, outSlot);
    }
    uint32_t 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