From fa2f05219e018e7890a2f089049ea09d5eace7da Mon Sep 17 00:00:00 2001 From: Michael Tretter Date: Fri, 22 Sep 2023 09:56:07 +0200 Subject: [PATCH] backend-pipewire: add MemFd allocator Instead of letting PipeWire allocate the buffers, allocate them in the PipeWire backend. This allows the PipeWire backend to define the type of the allocated buffers, which will ultimately allow to allocate buffers that can be rendered to. Signed-off-by: Michael Tretter --- libweston/backend-pipewire/pipewire.c | 101 +++++++++++++++++++++++++- 1 file changed, 97 insertions(+), 4 deletions(-) diff --git a/libweston/backend-pipewire/pipewire.c b/libweston/backend-pipewire/pipewire.c index 3198633e1..aab4fe84e 100644 --- a/libweston/backend-pipewire/pipewire.c +++ b/libweston/backend-pipewire/pipewire.c @@ -100,6 +100,7 @@ struct pipewire_head { struct pipewire_frame_data { struct weston_renderbuffer *renderbuffer; + struct pipewire_memfd *memfd; }; /* Pipewire default configuration for heads */ @@ -257,7 +258,7 @@ pipewire_output_connect(struct pipewire_output *output) ret = pw_stream_connect(output->stream, PW_DIRECTION_OUTPUT, PW_ID_ANY, PW_STREAM_FLAG_DRIVER | - PW_STREAM_FLAG_MAP_BUFFERS, + PW_STREAM_FLAG_ALLOC_BUFFERS, params, i); if (ret != 0) { weston_log("Failed to connect PipeWire stream: %s", @@ -460,6 +461,7 @@ pipewire_output_stream_param_changed(void *data, uint32_t id, SPA_POD_BUILDER_INIT(buffer, sizeof(buffer)); const struct spa_pod *params[2]; struct spa_video_info video_info; + uint32_t buffertype; int32_t width; int32_t height; int32_t stride; @@ -477,13 +479,17 @@ pipewire_output_stream_param_changed(void *data, uint32_t id, spa_format_video_raw_parse(format, &video_info.info.raw); - pipewire_output_debug(output, "param changed: %dx%d@(%d/%d) (%s)", + buffertype = SPA_DATA_MemFd; + + pipewire_output_debug(output, "param changed: %dx%d@(%d/%d) (%s) (%s)", video_info.info.raw.size.width, video_info.info.raw.size.height, video_info.info.raw.max_framerate.num, video_info.info.raw.max_framerate.denom, spa_debug_type_find_short_name(spa_type_video_format, - video_info.info.raw.format)); + video_info.info.raw.format), + spa_debug_type_find_short_name(spa_type_data_type, + buffertype)); width = video_info.info.raw.size.width; height = video_info.info.raw.size.height; @@ -494,7 +500,8 @@ pipewire_output_stream_param_changed(void *data, uint32_t id, SPA_TYPE_OBJECT_ParamBuffers, SPA_PARAM_Buffers, SPA_PARAM_BUFFERS_size, SPA_POD_Int(size), SPA_PARAM_BUFFERS_stride, SPA_POD_Int(stride), - SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(4, 2, 8)); + SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(4, 2, 8), + SPA_PARAM_BUFFERS_dataType, SPA_POD_CHOICE_FLAGS_Int(1u << buffertype)); params[1] = spa_pod_builder_add_object(&builder, SPA_TYPE_OBJECT_ParamMeta, SPA_PARAM_Meta, @@ -551,11 +558,77 @@ pipewire_output_stream_add_buffer_gl(struct pipewire_output *output, format, width, height, ptr); } +struct pipewire_memfd { + int fd; + unsigned int size; +}; + +static struct pipewire_memfd * +pipewire_output_create_memfd(struct pipewire_output *output) +{ + struct pipewire_memfd *memfd; + const struct pixel_format_info *format; + unsigned int width; + unsigned int height; + unsigned int stride; + size_t size; + int fd; + + memfd = xzalloc(sizeof *memfd); + + format = output->pixel_format; + width = output->base.width; + height = output->base.height; + stride = width * format->bpp / 8; + size = height * stride; + + fd = memfd_create("weston-pipewire", MFD_CLOEXEC); + if (fd == -1) + return NULL; + if (ftruncate(fd, size) == -1) + return NULL; + + memfd->fd = fd; + memfd->size = size; + + return memfd; +} + +static void +pipewire_destroy_memfd(struct pipewire_output *output, + struct pipewire_memfd *memfd) +{ + close(memfd->fd); + free(memfd); +} + +static void +pipewire_output_setup_memfd(struct pipewire_output *output, + struct pw_buffer *buffer, + struct pipewire_memfd *memfd) +{ + struct spa_buffer *buf = buffer->buffer; + struct spa_data *d = buf->datas; + + d[0].type = SPA_DATA_MemFd; + d[0].flags = SPA_DATA_FLAG_READWRITE; + d[0].fd = memfd->fd; + d[0].mapoffset = 0; + d[0].maxsize = memfd->size; + d[0].data = mmap(NULL, d[0].maxsize, + PROT_READ|PROT_WRITE, MAP_SHARED, + d[0].fd, d[0].mapoffset); + buf->n_datas = 1; +} + static void pipewire_output_stream_add_buffer(void *data, struct pw_buffer *buffer) { struct pipewire_output *output = data; struct weston_renderer *renderer = output->base.compositor->renderer; + struct spa_buffer *buf = buffer->buffer; + struct spa_data *d = buf->datas; + unsigned int buffertype = d[0].type; struct pipewire_frame_data *frame_data; pipewire_output_debug(output, "add buffer: %p", buffer); @@ -563,6 +636,19 @@ pipewire_output_stream_add_buffer(void *data, struct pw_buffer *buffer) frame_data = xzalloc(sizeof *frame_data); buffer->user_data = frame_data; + if (buffertype & (1u << SPA_DATA_MemFd)) { + struct pipewire_memfd *memfd; + + memfd = pipewire_output_create_memfd(output); + if (!memfd) { + pw_stream_set_error(output->stream, -ENOMEM, + "failed to allocate MemFd buffer"); + return; + } + pipewire_output_setup_memfd(output, buffer, memfd); + frame_data->memfd = memfd; + } + switch (renderer->type) { case WESTON_RENDERER_PIXMAN: frame_data->renderbuffer = pipewire_output_stream_add_buffer_pixman(output, buffer); @@ -580,9 +666,16 @@ pipewire_output_stream_remove_buffer(void *data, struct pw_buffer *buffer) { struct pipewire_output *output = data; struct pipewire_frame_data *frame_data = buffer->user_data; + struct spa_buffer *buf = buffer->buffer; + struct spa_data *d = buf->datas; pipewire_output_debug(output, "remove buffer: %p", buffer); + if (frame_data->memfd) { + munmap(d[0].data, d[0].maxsize); + pipewire_destroy_memfd(output, frame_data->memfd); + } + if (frame_data->renderbuffer) weston_renderbuffer_unref(frame_data->renderbuffer); free(frame_data);