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

Commit 959842dd authored by Brian Lindahl's avatar Brian Lindahl Committed by Android (Google) Code Review
Browse files

Merge changes I96c24390,I76ec7cd2

* changes:
  Uncache the active buffer slot last
  Stop using nullptr for GraphicBuffers in unit tests
parents 1f410ea6 3e1e1e69
Loading
Loading
Loading
Loading
+4 −2
Original line number Diff line number Diff line
@@ -23,7 +23,9 @@ namespace renderengine {
namespace mock {

class FakeExternalTexture : public renderengine::ExternalTexture {
    const sp<GraphicBuffer> mNullBuffer = nullptr;
    const sp<GraphicBuffer> mEmptyBuffer =
            sp<GraphicBuffer>::make(1u, 1u, PIXEL_FORMAT_RGBA_8888,
                                    GRALLOC_USAGE_SW_WRITE_OFTEN | GRALLOC_USAGE_SW_READ_OFTEN);
    uint32_t mWidth;
    uint32_t mHeight;
    uint64_t mId;
@@ -34,7 +36,7 @@ public:
    FakeExternalTexture(uint32_t width, uint32_t height, uint64_t id, PixelFormat pixelFormat,
                        uint64_t usage)
          : mWidth(width), mHeight(height), mId(id), mPixelFormat(pixelFormat), mUsage(usage) {}
    const sp<GraphicBuffer>& getBuffer() const { return mNullBuffer; }
    const sp<GraphicBuffer>& getBuffer() const { return mEmptyBuffer; }
    bool hasSameBuffer(const renderengine::ExternalTexture& other) const override {
        return getId() == other.getId();
    }
+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
+0 −5
Original line number Diff line number Diff line
@@ -28,11 +28,6 @@ HwcBufferCache::HwcBufferCache() {
}

HwcSlotAndBuffer HwcBufferCache::getHwcSlotAndBuffer(const sp<GraphicBuffer>& buffer) {
    // TODO(b/261930578): This is for unit tests which don't mock GraphicBuffers but instead send
    // in nullptrs.
    if (buffer == nullptr) {
        return {0, nullptr};
    }
    if (auto i = mCacheByBufferId.find(buffer->getId()); i != mCacheByBufferId.end()) {
        Cache& cache = i->second;
        // mark this cache slot as more recently used so it won't get evicted anytime soon
+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);
}

/*
Loading