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

Commit d78329b0 authored by Jim Shargo's avatar Jim Shargo Committed by Android (Google) Code Review
Browse files

Merge "Surface: Add a SurfaceQueueOutput struct for replaced buffers" into main

parents 778360bc bad6c068
Loading
Loading
Loading
Loading
+33 −9
Original line number Diff line number Diff line
@@ -741,11 +741,12 @@ status_t Surface::dequeueBuffer(sp<GraphicBuffer>* buffer, sp<Fence>* outFence)
    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 @@ void Surface::onBufferQueuedLocked(int slot, sp<Fence> fence,

#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 @@ int Surface::queueBuffer(android_native_buffer_t* buffer, int fenceFd) {
        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 @@ int Surface::queueBuffers(const std::vector<BatchQueuedBuffer>& buffers) {
            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 @@ int Surface::queueBuffers(const std::vector<BatchQueuedBuffer>& buffers) {

        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;
}

+21 −1
Original line number Diff line number Diff line
@@ -87,6 +87,15 @@ public:
    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 @@ private:
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 @@ public:
    // 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 @@ public:
        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 };
+95 −0
Original line number Diff line number Diff line
@@ -2422,6 +2422,101 @@ TEST_F(SurfaceTest, TestRemoteSurfaceDied_Disconnect_CallbackNotCalled) {
    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