Call release buffer if the buffer is overwritten in the client
Clients can overwrite the buffer in a transaction before it's applied.
When this happens, the buffer is then replaced, but the client doesn't
get a callback that the buffer was released. This could cause the client
to run out of buffers since the dropped ones become lost.
This can happen in two situations:
1. Merging two transactions where each has a buffer for the same
SurfaceControl
2. Transaction.setBuffer is called multiple times before an apply
In both cases, call the release callback so the client can properly
handle the dropped buffers.
It's also possible that the merging occurs in a different process than
the one that set the buffer. Because of that, we need to ensure we call
the correct releaseBufferListener that's associated with the one that
set the buffer.
The transformHint is removed from the releaseCallback since it's already
provided in the transaction callback. It was originally added in the
release callback to give it to BBQ early so it can know the new
transform hint before rendering a new frame. However, the transform hint
is now provided by VRI to BBQ so it no longer needs to be sent back via
release callback.
Test: ReleaseBufferCallbackTest
Change-Id: I9df0c713bf841f53e1a3d092f33179573a680dc4
Bug: 200285149
diff --git a/services/surfaceflinger/tests/ReleaseBufferCallback_test.cpp b/services/surfaceflinger/tests/ReleaseBufferCallback_test.cpp
index 3847a51..e50c2fc 100644
--- a/services/surfaceflinger/tests/ReleaseBufferCallback_test.cpp
+++ b/services/surfaceflinger/tests/ReleaseBufferCallback_test.cpp
@@ -31,7 +31,7 @@
public:
static void function(void* callbackContext, ReleaseCallbackId callbackId,
const sp<Fence>& releaseFence,
- uint32_t /*currentMaxAcquiredBufferCount*/) {
+ std::optional<uint32_t> /*currentMaxAcquiredBufferCount*/) {
if (!callbackContext) {
FAIL() << "failed to get callback context";
}
@@ -385,4 +385,84 @@
ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, firstBufferCallbackId));
}
+TEST_F(ReleaseBufferCallbackTest, DISABLED_SetBuffer_OverwriteBuffers) {
+ sp<SurfaceControl> layer = createBufferStateLayer();
+ ReleaseBufferCallbackHelper* releaseCallback = getReleaseBufferCallbackHelper();
+
+ sp<GraphicBuffer> firstBuffer = getBuffer();
+ ReleaseCallbackId firstBufferCallbackId(firstBuffer->getId(), generateFrameNumber());
+
+ // Create transaction with a buffer.
+ Transaction transaction;
+ transaction.setBuffer(layer, firstBuffer, std::nullopt, firstBufferCallbackId.framenumber,
+ firstBufferCallbackId, releaseCallback->getCallback());
+
+ sp<GraphicBuffer> secondBuffer = getBuffer();
+ ReleaseCallbackId secondBufferCallbackId(secondBuffer->getId(), generateFrameNumber());
+
+ // Call setBuffer on the same transaction with a different buffer.
+ transaction.setBuffer(layer, secondBuffer, std::nullopt, secondBufferCallbackId.framenumber,
+ secondBufferCallbackId, releaseCallback->getCallback());
+
+ ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, firstBufferCallbackId));
+}
+
+TEST_F(ReleaseBufferCallbackTest, DISABLED_Merge_Transactions_OverwriteBuffers) {
+ sp<SurfaceControl> layer = createBufferStateLayer();
+ ReleaseBufferCallbackHelper* releaseCallback = getReleaseBufferCallbackHelper();
+
+ sp<GraphicBuffer> firstBuffer = getBuffer();
+ ReleaseCallbackId firstBufferCallbackId(firstBuffer->getId(), generateFrameNumber());
+
+ // Create transaction with a buffer.
+ Transaction transaction1;
+ transaction1.setBuffer(layer, firstBuffer, std::nullopt, firstBufferCallbackId.framenumber,
+ firstBufferCallbackId, releaseCallback->getCallback());
+
+ sp<GraphicBuffer> secondBuffer = getBuffer();
+ ReleaseCallbackId secondBufferCallbackId(secondBuffer->getId(), generateFrameNumber());
+
+ // Create a second transaction with a new buffer for the same layer.
+ Transaction transaction2;
+ transaction2.setBuffer(layer, secondBuffer, std::nullopt, secondBufferCallbackId.framenumber,
+ secondBufferCallbackId, releaseCallback->getCallback());
+
+ // merge transaction1 into transaction2 so ensure we get a proper buffer release callback.
+ transaction1.merge(std::move(transaction2));
+ ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, firstBufferCallbackId));
+}
+
+TEST_F(ReleaseBufferCallbackTest, DISABLED_MergeBuffers_Different_Processes) {
+ sp<TransactionCompletedListener> firstCompletedListener = new TransactionCompletedListener();
+ sp<TransactionCompletedListener> secondCompletedListener = new TransactionCompletedListener();
+
+ TransactionCompletedListener::setInstance(firstCompletedListener);
+
+ sp<SurfaceControl> layer = createBufferStateLayer();
+ ReleaseBufferCallbackHelper* releaseCallback = getReleaseBufferCallbackHelper();
+
+ sp<GraphicBuffer> firstBuffer = getBuffer();
+ ReleaseCallbackId firstBufferCallbackId(firstBuffer->getId(), generateFrameNumber());
+
+ Transaction transaction1;
+ transaction1.setBuffer(layer, firstBuffer, std::nullopt, firstBufferCallbackId.framenumber,
+ firstBufferCallbackId, releaseCallback->getCallback());
+
+ // Sent a second buffer to allow the first buffer to get released.
+ sp<GraphicBuffer> secondBuffer = getBuffer();
+ ReleaseCallbackId secondBufferCallbackId(secondBuffer->getId(), generateFrameNumber());
+
+ Transaction transaction2;
+ transaction2.setBuffer(layer, secondBuffer, std::nullopt, secondBufferCallbackId.framenumber,
+ secondBufferCallbackId, releaseCallback->getCallback());
+
+ // Set a different TransactionCompletedListener to mimic a second process
+ TransactionCompletedListener::setInstance(secondCompletedListener);
+ Transaction().merge(std::move(transaction1)).merge(std::move(transaction2)).apply();
+
+ // Make sure we can still get the release callback even though the merge happened in a different
+ // process.
+ ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, firstBufferCallbackId));
+}
+
} // namespace android