backend-pipewire: add fence to synchronize on finish of render

If the PipeWire-backend renders to a DmaBuf, try to use a fence to synchronize
the submit of the next PipeWire buffer to the completion of the current render
process.

This ensures that the GPU rendering is finished before the buffer is passed to
PipeWire.

If fences are not available, the buffer is submitted without explicit
synchronization as before.

Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de>
Signed-off-by: Michael Tretter <m.tretter@pengutronix.de>
This commit is contained in:
Philipp Zabel 2023-05-02 15:53:22 +02:00 committed by Marius Vlad
parent f271dd34b6
commit 4b4cedfc66
1 changed files with 74 additions and 1 deletions

View File

@ -88,6 +88,7 @@ struct pipewire_output {
struct pw_stream *stream;
struct spa_hook stream_listener;
struct wl_list fence_list;
const struct pixel_format_info *pixel_format;
struct wl_event_source *finish_frame_timer;
@ -780,6 +781,14 @@ pipewire_output_stream_add_buffer(void *data, struct pw_buffer *buffer)
}
}
struct pipewire_fence_data {
struct pipewire_output *output;
struct pw_buffer *buffer;
int fence_sync_fd;
struct wl_event_source *fence_sync_event_source;
struct wl_list link;
};
static void
pipewire_output_stream_remove_buffer(void *data, struct pw_buffer *buffer)
{
@ -787,6 +796,7 @@ pipewire_output_stream_remove_buffer(void *data, struct pw_buffer *buffer)
struct pipewire_frame_data *frame_data = buffer->user_data;
struct spa_buffer *buf = buffer->buffer;
struct spa_data *d = buf->datas;
struct pipewire_fence_data *fence_data;
pipewire_output_debug(output, "remove buffer: %p", buffer);
@ -805,6 +815,10 @@ pipewire_output_stream_remove_buffer(void *data, struct pw_buffer *buffer)
if (frame_data->renderbuffer)
weston_renderbuffer_unref(frame_data->renderbuffer);
wl_list_for_each(fence_data, &output->fence_list, link) {
if (fence_data->buffer == buffer)
fence_data->buffer = NULL;
}
free(frame_data);
}
@ -839,6 +853,8 @@ pipewire_create_output(struct weston_backend *backend, const char *name)
output->backend = b;
output->pixel_format = b->pixel_format;
wl_list_init(&output->fence_list);
props = pw_properties_new(NULL, NULL);
pw_properties_setf(props, PW_KEY_NODE_NAME, "weston.%s", name);
@ -966,6 +982,57 @@ pipewire_submit_buffer(struct pipewire_output *output,
output->seq++;
}
static int
pipewire_output_fence_sync_handler(int fd, uint32_t mask, void *data)
{
struct pipewire_fence_data *fence_data = data;
if (fence_data->buffer)
pipewire_submit_buffer(fence_data->output, fence_data->buffer);
wl_event_source_remove(fence_data->fence_sync_event_source);
close(fence_data->fence_sync_fd);
wl_list_remove(&fence_data->link);
free(fence_data);
return 0;
}
static int
pipewire_schedule_submit_buffer(struct pipewire_output *output,
struct pw_buffer *buffer)
{
struct weston_compositor *ec = output->base.compositor;
struct weston_renderer *renderer = ec->renderer;
struct pipewire_fence_data *fence_data;
struct wl_event_loop *loop;
int fence_sync_fd;
fence_sync_fd = renderer->gl->create_fence_fd(&output->base);
if (fence_sync_fd == -1)
return -1;
fence_data = zalloc(sizeof *fence_data);
if (!fence_data) {
close(fence_sync_fd);
return -1;
}
wl_list_insert(&output->fence_list, &fence_data->link);
loop = wl_display_get_event_loop(output->backend->compositor->wl_display);
fence_data->output = output;
fence_data->buffer = buffer;
fence_data->fence_sync_fd = fence_sync_fd;
fence_data->fence_sync_event_source =
wl_event_loop_add_fd(loop, fence_data->fence_sync_fd,
WL_EVENT_READABLE,
pipewire_output_fence_sync_handler,
fence_data);
return 0;
}
static int
pipewire_output_repaint(struct weston_output *base)
{
@ -974,6 +1041,7 @@ pipewire_output_repaint(struct weston_output *base)
struct pw_buffer *buffer;
struct pipewire_frame_data *frame_data;
pixman_region32_t damage;
bool submit_scheduled = false;
assert(output);
@ -1000,7 +1068,12 @@ pipewire_output_repaint(struct weston_output *base)
else
output->base.full_repaint_needed = true;
pipewire_submit_buffer(output, buffer);
if (buffer->buffer->datas[0].type == SPA_DATA_DmaBuf) {
if (pipewire_schedule_submit_buffer(output, buffer) == 0)
submit_scheduled = true;
}
if (!submit_scheduled)
pipewire_submit_buffer(output, buffer);
out: