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

Commit 3e1e1e69 authored by Brian Lindahl's avatar Brian Lindahl
Browse files

Uncache the active buffer slot last

This allows the memory for the active buffer to be freed as soon as
possible by purging it from HWC cache as soon as the next buffer is sent
to the layer.

Bug: 258196272
Test: atest OutputLayerUncacheBufferTest
Change-Id: I96c24390de5757ac99b369119e9ba031afb0b042
parent f5ab5ae6
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -137,6 +137,7 @@ struct OutputLayerCompositionState {
        HwcBufferCache hwcBufferCache;

        // The previously-active buffer for this layer.
        uint64_t activeBufferId;
        uint32_t activeBufferSlot;

        // Set to true when overridden info has been sent to HW composer
+21 −7
Original line number Diff line number Diff line
@@ -617,13 +617,24 @@ void OutputLayer::uncacheBuffers(const std::vector<uint64_t>& bufferIdsToUncache
        return;
    }

    // Uncache the active buffer last so that it's the first buffer to be purged from the cache
    // next time a buffer is sent to this layer.
    bool uncacheActiveBuffer = false;

    std::vector<uint32_t> slotsToClear;
    for (uint64_t bufferId : bufferIdsToUncache) {
        if (bufferId == state.hwc->activeBufferId) {
            uncacheActiveBuffer = true;
        } else {
            uint32_t slot = state.hwc->hwcBufferCache.uncache(bufferId);
            if (slot != UINT32_MAX) {
                slotsToClear.push_back(slot);
            }
        }
    }
    if (uncacheActiveBuffer) {
        slotsToClear.push_back(state.hwc->hwcBufferCache.uncache(state.hwc->activeBufferId));
    }

    hal::Error error =
            state.hwc->hwcLayer->setBufferSlotsToClear(slotsToClear, state.hwc->activeBufferSlot);
@@ -655,18 +666,21 @@ void OutputLayer::writeBufferStateToHWC(HWC2::Layer* hwcLayer,
            hwcSlotAndBuffer = state.hwc->hwcBufferCache.getOverrideHwcSlotAndBuffer(
                    state.overrideInfo.buffer->getBuffer());
            hwcFence = state.overrideInfo.acquireFence;
            // Keep track of the active buffer ID so when it's discarded we uncache it last so its
            // slot will be used first, allowing the memory to be freed as soon as possible.
            state.hwc->activeBufferId = state.overrideInfo.buffer->getBuffer()->getId();
        } else {
            hwcSlotAndBuffer =
                    state.hwc->hwcBufferCache.getHwcSlotAndBuffer(outputIndependentState.buffer);
            hwcFence = outputIndependentState.acquireFence;
            // Keep track of the active buffer ID so when it's discarded we uncache it last so its
            // slot will be used first, allowing the memory to be freed as soon as possible.
            state.hwc->activeBufferId = outputIndependentState.buffer->getId();
        }

        // Keep track of the active buffer slot, so we can restore it after clearing other buffer
        // slots.
        if (hwcSlotAndBuffer.buffer) {
        state.hwc->activeBufferSlot = hwcSlotAndBuffer.slot;
    }
    }

    if (auto error = hwcLayer->setBuffer(hwcSlotAndBuffer.slot, hwcSlotAndBuffer.buffer, hwcFence);
        error != hal::Error::NONE) {
+29 −12
Original line number Diff line number Diff line
@@ -1318,6 +1318,7 @@ TEST_F(OutputLayerWriteStateToHWCTest, setBlockingRegion) {
struct OutputLayerUncacheBufferTest : public OutputLayerTest {
    static const sp<GraphicBuffer> kBuffer1;
    static const sp<GraphicBuffer> kBuffer2;
    static const sp<GraphicBuffer> kBuffer3;
    static const sp<Fence> kFence;

    OutputLayerUncacheBufferTest() {
@@ -1343,6 +1344,10 @@ const sp<GraphicBuffer> OutputLayerUncacheBufferTest::kBuffer2 =
        sp<GraphicBuffer>::make(2, 3, PIXEL_FORMAT_RGBA_8888,
                                AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN |
                                        AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN);
const sp<GraphicBuffer> OutputLayerUncacheBufferTest::kBuffer3 =
        sp<GraphicBuffer>::make(4, 5, PIXEL_FORMAT_RGBA_8888,
                                AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN |
                                        AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN);
const sp<Fence> OutputLayerUncacheBufferTest::kFence = sp<Fence>::make();

TEST_F(OutputLayerUncacheBufferTest, canUncacheAndReuseSlot) {
@@ -1360,26 +1365,38 @@ TEST_F(OutputLayerUncacheBufferTest, canUncacheAndReuseSlot) {
                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
    Mock::VerifyAndClearExpectations(&mHwcLayer);

    // buffer slots are cleared in HWC
    std::vector<uint32_t> slotsToClear = {0, 1};
    EXPECT_CALL(mHwcLayer,
                setBufferSlotsToClear(/*slotsToClear*/ slotsToClear, /*activeBufferSlot*/ 1));
    mOutputLayer.uncacheBuffers({kBuffer1->getId(), kBuffer2->getId()});
    // Buffer3 is stored in slot 2
    mLayerFEState.buffer = kBuffer3;
    EXPECT_CALL(mHwcLayer, setBuffer(/*slot*/ 2, kBuffer3, kFence));
    mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
    Mock::VerifyAndClearExpectations(&mHwcLayer);

    // rather than allocating a new slot, the active buffer slot (slot 1) is reused first to free
    // the memory as soon as possible
    mLayerFEState.buffer = kBuffer1;
    EXPECT_CALL(mHwcLayer, setBuffer(/*slot*/ 1, kBuffer1, kFence));
    // Buffer2 becomes the active buffer again (with a nullptr) and reuses slot 1
    mLayerFEState.buffer = kBuffer2;
    sp<GraphicBuffer> nullBuffer = nullptr;
    EXPECT_CALL(mHwcLayer, setBuffer(/*slot*/ 1, nullBuffer, kFence));
    mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
    Mock::VerifyAndClearExpectations(&mHwcLayer);

    // rather than allocating a new slot, slot 0 is reused
    mLayerFEState.buffer = kBuffer2;
    EXPECT_CALL(mHwcLayer, setBuffer(/*slot*/ 0, kBuffer2, kFence));
    // Buffer slots are cleared
    std::vector<uint32_t> slotsToClear = {0, 2, 1}; // order doesn't matter
    EXPECT_CALL(mHwcLayer, setBufferSlotsToClear(slotsToClear, /*activeBufferSlot*/ 1));
    // Uncache the active buffer in between other buffers to exercise correct algorithmic behavior.
    mOutputLayer.uncacheBuffers({kBuffer1->getId(), kBuffer2->getId(), kBuffer3->getId()});
    Mock::VerifyAndClearExpectations(&mHwcLayer);

    // Buffer1 becomes active again, and rather than allocating a new slot, or re-using slot 0,
    // the active buffer slot (slot 1 for Buffer2) is reused first, which allows HWC to free the
    // memory for the active buffer. Note: slot 1 is different from the first and last buffer slot
    // requested to be cleared in slotsToClear (slot 1), above, indicating that the algorithm
    // correctly identifies the active buffer as the buffer in slot 1, despite ping-ponging.
    mLayerFEState.buffer = kBuffer1;
    EXPECT_CALL(mHwcLayer, setBuffer(/*slot*/ 1, kBuffer1, kFence));
    mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
    Mock::VerifyAndClearExpectations(&mHwcLayer);
}

/*