Maintain the active buffer when clearing buffer slots

When an app discards graphic buffers, for example when a MediaCodec
disconnects from a surface, those buffers will be uncached and removed
from HWC buffer slots. The active buffer, however, should still remain
active until the next buffer is queued up.

Bug: 262037933
Bug: 258196272
Test: atest OutputLayerUncacheBufferTest
Test: atest VtsHalGraphicsComposer3_TargetTest
Test: atest VtsHalGraphicsComposerV2_2TargetTest
Change-Id: I7c4eefb17e8bad694d698f9ad6d1d289f4af8d2c
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h
index 6e9ea6f..b6a4240 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h
@@ -83,7 +83,7 @@
     // If the buffer is already in the cache, the buffer is null to optimize away sending HWC the
     // buffer handle.
     //
-    HwcSlotAndBuffer getHwcSlotAndBufferForOverride(const sp<GraphicBuffer>& buffer);
+    HwcSlotAndBuffer getOverrideHwcSlotAndBuffer(const sp<GraphicBuffer>& buffer);
 
     //
     // When a client process discards a buffer, it needs to be purged from the HWC cache.
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
index 2b383c1..2a5bfae 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
@@ -136,6 +136,9 @@
         // cost of sending reused buffers to the HWC.
         HwcBufferCache hwcBufferCache;
 
+        // The previously-active buffer for this layer.
+        uint32_t activeBufferSlot;
+
         // Set to true when overridden info has been sent to HW composer
         bool stateOverridden = false;
 
diff --git a/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp b/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp
index e2c0acf..34ed214 100644
--- a/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp
@@ -42,7 +42,7 @@
     return {cache(buffer), buffer};
 }
 
-HwcSlotAndBuffer HwcBufferCache::getHwcSlotAndBufferForOverride(const sp<GraphicBuffer>& buffer) {
+HwcSlotAndBuffer HwcBufferCache::getOverrideHwcSlotAndBuffer(const sp<GraphicBuffer>& buffer) {
     if (buffer == mLastOverrideBuffer) {
         return {kOverrideBufferSlot, nullptr};
     }
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index 0ac2d43..766d7ea 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -610,21 +610,27 @@
     }
 }
 
-void OutputLayer::uncacheBuffers(std::vector<uint64_t> const& bufferIdsToUncache) {
+void OutputLayer::uncacheBuffers(const std::vector<uint64_t>& bufferIdsToUncache) {
     auto& state = editState();
     // Skip doing this if there is no HWC interface
     if (!state.hwc) {
         return;
     }
 
-    for (auto bufferId : bufferIdsToUncache) {
+    std::vector<uint32_t> slotsToClear;
+    for (uint64_t bufferId : bufferIdsToUncache) {
         uint32_t slot = state.hwc->hwcBufferCache.uncache(bufferId);
-        if (slot != UINT32_MAX && state.hwc->hwcLayer) {
-            hal::Error error = state.hwc->hwcLayer->clearBufferSlot(slot);
-            ALOGE("[%s] Failed to clear buffer slot %d: %s (%d)", getLayerFE().getDebugName(), slot,
-                  to_string(error).c_str(), static_cast<int32_t>(error));
+        if (slot != UINT32_MAX) {
+            slotsToClear.push_back(slot);
         }
     }
+
+    hal::Error error =
+            state.hwc->hwcLayer->setBufferSlotsToClear(slotsToClear, state.hwc->activeBufferSlot);
+    if (error != hal::Error::NONE) {
+        ALOGE("[%s] Failed to clear buffer slots: %s (%d)", getLayerFE().getDebugName(),
+              to_string(error).c_str(), static_cast<int32_t>(error));
+    }
 }
 
 void OutputLayer::writeBufferStateToHWC(HWC2::Layer* hwcLayer,
@@ -641,15 +647,25 @@
 
     HwcSlotAndBuffer hwcSlotAndBuffer;
     sp<Fence> hwcFence;
-    // Override buffers use a special cache slot so that they don't evict client buffers.
-    if (getState().overrideInfo.buffer != nullptr && !skipLayer) {
-        hwcSlotAndBuffer = editState().hwc->hwcBufferCache.getHwcSlotAndBufferForOverride(
-                getState().overrideInfo.buffer->getBuffer());
-        hwcFence = getState().overrideInfo.acquireFence;
-    } else {
-        hwcSlotAndBuffer =
-                editState().hwc->hwcBufferCache.getHwcSlotAndBuffer(outputIndependentState.buffer);
-        hwcFence = outputIndependentState.acquireFence;
+    {
+        // Editing the state only because we update the HWC buffer cache and active buffer.
+        auto& state = editState();
+        // Override buffers use a special cache slot so that they don't evict client buffers.
+        if (state.overrideInfo.buffer != nullptr && !skipLayer) {
+            hwcSlotAndBuffer = state.hwc->hwcBufferCache.getOverrideHwcSlotAndBuffer(
+                    state.overrideInfo.buffer->getBuffer());
+            hwcFence = state.overrideInfo.acquireFence;
+        } else {
+            hwcSlotAndBuffer =
+                    state.hwc->hwcBufferCache.getHwcSlotAndBuffer(outputIndependentState.buffer);
+            hwcFence = outputIndependentState.acquireFence;
+        }
+
+        // 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);
diff --git a/services/surfaceflinger/CompositionEngine/tests/HwcBufferCacheTest.cpp b/services/surfaceflinger/CompositionEngine/tests/HwcBufferCacheTest.cpp
index cf72e8f..c5fb594 100644
--- a/services/surfaceflinger/CompositionEngine/tests/HwcBufferCacheTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/HwcBufferCacheTest.cpp
@@ -115,20 +115,20 @@
     EXPECT_EQ(cache.uncache(mBuffer2->getId()), UINT32_MAX);
 }
 
-TEST_F(HwcBufferCacheTest, getHwcSlotAndBufferForOverride_whenCached_returnsSameSlotAndNullBuffer) {
+TEST_F(HwcBufferCacheTest, getOverrideHwcSlotAndBuffer_whenCached_returnsSameSlotAndNullBuffer) {
     HwcBufferCache cache;
     sp<GraphicBuffer> outBuffer;
 
-    HwcSlotAndBuffer originalSlotAndBuffer = cache.getHwcSlotAndBufferForOverride(mBuffer1);
+    HwcSlotAndBuffer originalSlotAndBuffer = cache.getOverrideHwcSlotAndBuffer(mBuffer1);
     EXPECT_NE(originalSlotAndBuffer.slot, UINT32_MAX);
     EXPECT_EQ(originalSlotAndBuffer.buffer, mBuffer1);
 
-    HwcSlotAndBuffer finalSlotAndBuffer = cache.getHwcSlotAndBufferForOverride(mBuffer1);
+    HwcSlotAndBuffer finalSlotAndBuffer = cache.getOverrideHwcSlotAndBuffer(mBuffer1);
     EXPECT_EQ(finalSlotAndBuffer.slot, originalSlotAndBuffer.slot);
     EXPECT_EQ(finalSlotAndBuffer.buffer, nullptr);
 }
 
-TEST_F(HwcBufferCacheTest, getHwcSlotAndBufferForOverride_whenSlotsFull_returnsIndependentSlot) {
+TEST_F(HwcBufferCacheTest, getOverrideHwcSlotAndBuffer_whenSlotsFull_returnsIndependentSlot) {
     HwcBufferCache cache;
     sp<GraphicBuffer> outBuffer;
 
@@ -149,7 +149,7 @@
 
     sp<GraphicBuffer> overrideBuffer =
             sp<GraphicBuffer>::make(1u, 1u, HAL_PIXEL_FORMAT_RGBA_8888, 1u, 0u);
-    HwcSlotAndBuffer overrideSlotAndBuffer = cache.getHwcSlotAndBufferForOverride(overrideBuffer);
+    HwcSlotAndBuffer overrideSlotAndBuffer = cache.getOverrideHwcSlotAndBuffer(overrideBuffer);
     // expect us to have a slot number
     EXPECT_NE(overrideSlotAndBuffer.slot, UINT32_MAX);
     // expect this to be the first time we cached the buffer
@@ -173,7 +173,7 @@
     HwcBufferCache cache;
     sp<GraphicBuffer> outBuffer;
 
-    HwcSlotAndBuffer hwcSlotAndBuffer = cache.getHwcSlotAndBufferForOverride(mBuffer1);
+    HwcSlotAndBuffer hwcSlotAndBuffer = cache.getOverrideHwcSlotAndBuffer(mBuffer1);
     ASSERT_NE(hwcSlotAndBuffer.slot, UINT32_MAX);
 
     EXPECT_EQ(cache.uncache(mBuffer1->getId()), hwcSlotAndBuffer.slot);
@@ -184,7 +184,7 @@
     HwcBufferCache cache;
     sp<GraphicBuffer> outBuffer;
 
-    HwcSlotAndBuffer hwcSlotAndBuffer = cache.getHwcSlotAndBufferForOverride(mBuffer1);
+    HwcSlotAndBuffer hwcSlotAndBuffer = cache.getOverrideHwcSlotAndBuffer(mBuffer1);
     ASSERT_NE(hwcSlotAndBuffer.slot, UINT32_MAX);
 
     EXPECT_EQ(cache.uncache(mBuffer2->getId()), UINT32_MAX);
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h b/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h
index c7f9c69..b0b1a02 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h
@@ -56,7 +56,7 @@
     MOCK_METHOD3(setBuffer,
                  Error(uint32_t, const android::sp<android::GraphicBuffer>&,
                        const android::sp<android::Fence>&));
-    MOCK_METHOD1(clearBufferSlot, Error(uint32_t));
+    MOCK_METHOD2(setBufferSlotsToClear, Error(const std::vector<uint32_t>&, uint32_t));
     MOCK_METHOD1(setSurfaceDamage, Error(const android::Region&));
     MOCK_METHOD1(setBlendMode, Error(hal::BlendMode));
     MOCK_METHOD1(setColor, Error(aidl::android::hardware::graphics::composer3::Color));
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
index f2128c9..0edc226 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
@@ -1361,19 +1361,21 @@
     Mock::VerifyAndClearExpectations(&mHwcLayer);
 
     // buffer slots are cleared in HWC
-    EXPECT_CALL(mHwcLayer, clearBufferSlot(/*slot*/ 0));
-    EXPECT_CALL(mHwcLayer, clearBufferSlot(/*slot*/ 1));
+    std::vector<uint32_t> slotsToClear = {0, 1};
+    EXPECT_CALL(mHwcLayer,
+                setBufferSlotsToClear(/*slotsToClear*/ slotsToClear, /*activeBufferSlot*/ 1));
     mOutputLayer.uncacheBuffers({kBuffer1->getId(), kBuffer2->getId()});
     Mock::VerifyAndClearExpectations(&mHwcLayer);
 
-    // slot 1 is reused for Buffer1
+    // 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));
     mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
                                  /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
     Mock::VerifyAndClearExpectations(&mHwcLayer);
 
-    // slot 0 is reused for Buffer2
+    // rather than allocating a new slot, slot 0 is reused
     mLayerFEState.buffer = kBuffer2;
     EXPECT_CALL(mHwcLayer, setBuffer(/*slot*/ 0, kBuffer2, kFence));
     mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,