Surface: Add a SurfaceQueueOutput struct for replaced buffers
With IGBPs, this is provided back to clients via an output object, which
Surface doesn't currently provide.
This lets us notify clients when this happens.
BYPASS_IGBP_IGBC_API_REASON=warren buffers
Bug: 340933794
Flag: com.android.graphics.libgui.flags.wb_platform_api_improvements
Test: new SurfaceTest tests
Change-Id: I2ec5a0598988fdfcfe7356a14be0f99a206ef6a8
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index 76d74fe..66e7ddd 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -741,11 +741,12 @@
return res;
}
-status_t Surface::queueBuffer(const sp<GraphicBuffer>& buffer, const sp<Fence>& fd) {
+status_t Surface::queueBuffer(const sp<GraphicBuffer>& buffer, const sp<Fence>& fd,
+ SurfaceQueueBufferOutput* output) {
if (buffer == nullptr) {
return BAD_VALUE;
}
- return queueBuffer(buffer.get(), fd ? fd->get() : -1);
+ return queueBuffer(buffer.get(), fd ? fd->get() : -1, output);
}
status_t Surface::detachBuffer(const sp<GraphicBuffer>& buffer) {
@@ -1195,7 +1196,8 @@
#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
-int Surface::queueBuffer(android_native_buffer_t* buffer, int fenceFd) {
+int Surface::queueBuffer(android_native_buffer_t* buffer, int fenceFd,
+ SurfaceQueueBufferOutput* surfaceOutput) {
ATRACE_CALL();
ALOGV("Surface::queueBuffer");
@@ -1245,16 +1247,26 @@
onBufferQueuedLocked(slot, fence, output);
}
+ if (surfaceOutput != nullptr) {
+ *surfaceOutput = {.bufferReplaced = output.bufferReplaced};
+ }
+
return err;
}
-int Surface::queueBuffers(const std::vector<BatchQueuedBuffer>& buffers) {
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
+int Surface::queueBuffers(const std::vector<BatchQueuedBuffer>& buffers,
+ std::vector<SurfaceQueueBufferOutput>* queueBufferOutputs)
+#else
+int Surface::queueBuffers(const std::vector<BatchQueuedBuffer>& buffers)
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
+{
ATRACE_CALL();
ALOGV("Surface::queueBuffers");
size_t numBuffers = buffers.size();
- std::vector<IGraphicBufferProducer::QueueBufferInput> queueBufferInputs(numBuffers);
- std::vector<IGraphicBufferProducer::QueueBufferOutput> queueBufferOutputs;
+ std::vector<IGraphicBufferProducer::QueueBufferInput> igbpQueueBufferInputs(numBuffers);
+ std::vector<IGraphicBufferProducer::QueueBufferOutput> igbpQueueBufferOutputs;
std::vector<int> bufferSlots(numBuffers, -1);
std::vector<sp<Fence>> bufferFences(numBuffers);
@@ -1280,12 +1292,13 @@
IGraphicBufferProducer::QueueBufferInput input;
getQueueBufferInputLocked(buffers[batchIdx].buffer, buffers[batchIdx].fenceFd,
buffers[batchIdx].timestamp, &input);
+ input.slot = i;
bufferFences[batchIdx] = input.fence;
- queueBufferInputs[batchIdx] = input;
+ igbpQueueBufferInputs[batchIdx] = input;
}
}
nsecs_t now = systemTime();
- err = mGraphicBufferProducer->queueBuffers(queueBufferInputs, &queueBufferOutputs);
+ err = mGraphicBufferProducer->queueBuffers(igbpQueueBufferInputs, &igbpQueueBufferOutputs);
{
Mutex::Autolock lock(mMutex);
mLastQueueDuration = systemTime() - now;
@@ -1295,10 +1308,21 @@
for (size_t batchIdx = 0; batchIdx < numBuffers; batchIdx++) {
onBufferQueuedLocked(bufferSlots[batchIdx], bufferFences[batchIdx],
- queueBufferOutputs[batchIdx]);
+ igbpQueueBufferOutputs[batchIdx]);
}
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
+ if (queueBufferOutputs != nullptr) {
+ queueBufferOutputs->clear();
+ queueBufferOutputs->resize(numBuffers);
+ for (size_t batchIdx = 0; batchIdx < numBuffers; batchIdx++) {
+ (*queueBufferOutputs)[batchIdx].bufferReplaced =
+ igbpQueueBufferOutputs[batchIdx].bufferReplaced;
+ }
+ }
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
+
return err;
}
diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h
index 5ea81c4..e74f9ad 100644
--- a/libs/gui/include/gui/Surface.h
+++ b/libs/gui/include/gui/Surface.h
@@ -87,6 +87,15 @@
virtual void onBufferDetached(int /*slot*/) override {}
};
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
+// Contains additional data from the queueBuffer operation.
+struct SurfaceQueueBufferOutput {
+ // True if this queueBuffer caused a buffer to be replaced in the queue
+ // (and therefore not will not be acquired)
+ bool bufferReplaced = false;
+};
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
+
/*
* An implementation of ANativeWindow that feeds graphics buffers into a
* BufferQueue.
@@ -363,7 +372,12 @@
protected:
virtual int dequeueBuffer(ANativeWindowBuffer** buffer, int* fenceFd);
virtual int cancelBuffer(ANativeWindowBuffer* buffer, int fenceFd);
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
+ virtual int queueBuffer(ANativeWindowBuffer* buffer, int fenceFd,
+ SurfaceQueueBufferOutput* surfaceOutput = nullptr);
+#else
virtual int queueBuffer(ANativeWindowBuffer* buffer, int fenceFd);
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
virtual int perform(int operation, va_list args);
virtual int setSwapInterval(int interval);
@@ -422,7 +436,8 @@
// Queues a buffer, with an optional fd fence that captures pending work on the buffer. This
// buffer must have been returned by dequeueBuffer or associated with this Surface via an
// attachBuffer operation.
- status_t queueBuffer(const sp<GraphicBuffer>& buffer, const sp<Fence>& fd = Fence::NO_FENCE);
+ status_t queueBuffer(const sp<GraphicBuffer>& buffer, const sp<Fence>& fd = Fence::NO_FENCE,
+ SurfaceQueueBufferOutput* output = nullptr);
// Detaches this buffer, dissociating it from this Surface. This buffer must have been returned
// by queueBuffer or associated with this Surface via an attachBuffer operation.
@@ -443,8 +458,13 @@
int fenceFd = -1;
nsecs_t timestamp = NATIVE_WINDOW_TIMESTAMP_AUTO;
};
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
+ virtual int queueBuffers(const std::vector<BatchQueuedBuffer>& buffers,
+ std::vector<SurfaceQueueBufferOutput>* queueBufferOutputs = nullptr);
+#else
virtual int queueBuffers(
const std::vector<BatchQueuedBuffer>& buffers);
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
protected:
enum { NUM_BUFFER_SLOTS = BufferQueueDefs::NUM_BUFFER_SLOTS };
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 0c3859e..88893b6 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -2422,6 +2422,101 @@
std::future_status status = watcherDiedFuture.wait_for(std::chrono::seconds(1));
EXPECT_EQ(std::future_status::timeout, status);
}
+
+TEST_F(SurfaceTest, QueueBufferOutput_TracksReplacements) {
+ sp<BufferItemConsumer> consumer = sp<BufferItemConsumer>::make(GRALLOC_USAGE_SW_READ_OFTEN);
+ ASSERT_EQ(OK, consumer->setMaxBufferCount(3));
+ ASSERT_EQ(OK, consumer->setMaxAcquiredBufferCount(1));
+
+ sp<Surface> surface = consumer->getSurface();
+ sp<StubSurfaceListener> listener = sp<StubSurfaceListener>::make();
+
+ // Async mode sets up an extra buffer so the surface can queue it without waiting.
+ ASSERT_EQ(OK, surface->setMaxDequeuedBufferCount(1));
+ ASSERT_EQ(OK, surface->setAsyncMode(true));
+ ASSERT_EQ(OK, surface->connect(NATIVE_WINDOW_API_CPU, listener));
+
+ sp<GraphicBuffer> buffer;
+ sp<Fence> fence;
+ SurfaceQueueBufferOutput output;
+ BufferItem item;
+
+ // We can queue directly, without an output arg.
+ EXPECT_EQ(OK, surface->dequeueBuffer(&buffer, &fence));
+ EXPECT_EQ(OK, surface->queueBuffer(buffer, fence));
+ EXPECT_EQ(OK, consumer->acquireBuffer(&item, 0));
+ EXPECT_EQ(OK, consumer->releaseBuffer(item));
+
+ // We can queue with an output arg, and that we don't expect to see a replacement.
+ EXPECT_EQ(OK, surface->dequeueBuffer(&buffer, &fence));
+ EXPECT_EQ(OK, surface->queueBuffer(buffer, fence, &output));
+ EXPECT_FALSE(output.bufferReplaced);
+
+ // We expect see a replacement when we queue a second buffer in async mode, and the consumer
+ // hasn't acquired the first one yet.
+ EXPECT_EQ(OK, surface->dequeueBuffer(&buffer, &fence));
+ EXPECT_EQ(OK, surface->queueBuffer(buffer, fence, &output));
+ EXPECT_TRUE(output.bufferReplaced);
+}
+
+TEST_F(SurfaceTest, QueueBufferOutput_TracksReplacements_Plural) {
+ sp<BufferItemConsumer> consumer = sp<BufferItemConsumer>::make(GRALLOC_USAGE_SW_READ_OFTEN);
+ ASSERT_EQ(OK, consumer->setMaxBufferCount(4));
+ ASSERT_EQ(OK, consumer->setMaxAcquiredBufferCount(1));
+
+ sp<Surface> surface = consumer->getSurface();
+ consumer->setName(String8("TRPTest"));
+ sp<StubSurfaceListener> listener = sp<StubSurfaceListener>::make();
+
+ // Async mode sets up an extra buffer so the surface can queue it without waiting.
+ ASSERT_EQ(OK, surface->setMaxDequeuedBufferCount(2));
+ ASSERT_EQ(OK, surface->setAsyncMode(true));
+ ASSERT_EQ(OK, surface->connect(NATIVE_WINDOW_API_CPU, listener));
+
+ // dequeueBuffers requires a vector of a certain size:
+ std::vector<Surface::BatchBuffer> buffers(2);
+ std::vector<Surface::BatchQueuedBuffer> queuedBuffers;
+ std::vector<SurfaceQueueBufferOutput> outputs;
+ BufferItem item;
+
+ auto moveBuffersToQueuedBuffers = [&]() {
+ EXPECT_EQ(2u, buffers.size());
+ EXPECT_NE(nullptr, buffers[0].buffer);
+ EXPECT_NE(nullptr, buffers[1].buffer);
+
+ queuedBuffers.clear();
+ for (auto& buffer : buffers) {
+ auto& queuedBuffer = queuedBuffers.emplace_back();
+ queuedBuffer.buffer = buffer.buffer;
+ queuedBuffer.fenceFd = buffer.fenceFd;
+ queuedBuffer.timestamp = NATIVE_WINDOW_TIMESTAMP_AUTO;
+ }
+ buffers = {{}, {}};
+ };
+
+ // We can queue directly, without an output arg.
+ EXPECT_EQ(OK, surface->dequeueBuffers(&buffers));
+ moveBuffersToQueuedBuffers();
+ EXPECT_EQ(OK, surface->queueBuffers(queuedBuffers));
+ EXPECT_EQ(OK, consumer->acquireBuffer(&item, 0));
+ EXPECT_EQ(OK, consumer->releaseBuffer(item));
+
+ // We can queue with an output arg. Only the second one should be replaced.
+ EXPECT_EQ(OK, surface->dequeueBuffers(&buffers));
+ moveBuffersToQueuedBuffers();
+ EXPECT_EQ(OK, surface->queueBuffers(queuedBuffers, &outputs));
+ EXPECT_EQ(2u, outputs.size());
+ EXPECT_FALSE(outputs[0].bufferReplaced);
+ EXPECT_TRUE(outputs[1].bufferReplaced);
+
+ // Since we haven't acquired anything, both queued buffers will replace the original one.
+ EXPECT_EQ(OK, surface->dequeueBuffers(&buffers));
+ moveBuffersToQueuedBuffers();
+ EXPECT_EQ(OK, surface->queueBuffers(queuedBuffers, &outputs));
+ EXPECT_EQ(2u, outputs.size());
+ EXPECT_TRUE(outputs[0].bufferReplaced);
+ EXPECT_TRUE(outputs[1].bufferReplaced);
+}
#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
} // namespace android