Merge "Surface: Add a SurfaceQueueOutput struct for replaced buffers" into main
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