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

Commit bad6c068 authored by Jim Shargo's avatar Jim Shargo
Browse files

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
parent 5e243ddd
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