backend-pipewire: add DmaBuf allocation and import
The GL renderer allows the PipeWire backend to allocate DMABUFs that can be used as renderbuffers and passed to PipeWire. This allows to avoid a copy of the rendered output when using PipeWire. PipeWire negotiates DmaBuf buffers using the SPA_FORMAT_VIDEO_modifier property. Currently, the PipeWire-backend only announces and handles DRM_FORMAT_MOD_LINEAR, which allows to share DmaBufs with consumers that have modifier support for linear buffers. Consumers which could consume DmaBufs but don't support modifiers (DRM_FORMAT_MOD_INVALID) are not supported. Moreover, consumers that would benefit from other formats with modifiers, for example tiled or compressed, aren't not supported, too. Modifier negotiation can be added by extending the list of announced modifiers and selecting and checking the proposed modifiers in param_changed. Signed-off-by: Michael Tretter <m.tretter@pengutronix.de>
This commit is contained in:
parent
fa2f05219e
commit
ae53d196db
|
@ -54,6 +54,7 @@
|
|||
#include "shared/xalloc.h"
|
||||
#include <libweston/libweston.h>
|
||||
#include <libweston/backend-pipewire.h>
|
||||
#include <libweston/linux-dmabuf.h>
|
||||
#include <libweston/weston-log.h>
|
||||
#include "pixel-formats.h"
|
||||
#include "pixman-renderer.h"
|
||||
|
@ -101,6 +102,7 @@ struct pipewire_head {
|
|||
struct pipewire_frame_data {
|
||||
struct weston_renderbuffer *renderbuffer;
|
||||
struct pipewire_memfd *memfd;
|
||||
struct pipewire_dmabuf *dmabuf;
|
||||
};
|
||||
|
||||
/* Pipewire default configuration for heads */
|
||||
|
@ -202,6 +204,14 @@ spa_video_format_from_drm_fourcc(uint32_t fourcc)
|
|||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
pipewire_backend_has_dmabuf_allocator(struct pipewire_backend *backend)
|
||||
{
|
||||
struct weston_renderer *renderer = backend->compositor->renderer;
|
||||
|
||||
return renderer->dmabuf_alloc != NULL;
|
||||
}
|
||||
|
||||
static struct spa_pod *
|
||||
spa_pod_build_format(struct spa_pod_builder *builder,
|
||||
int width, int height, int framerate,
|
||||
|
@ -247,10 +257,20 @@ pipewire_output_connect(struct pipewire_output *output)
|
|||
uint8_t buffer[1024];
|
||||
struct spa_pod_builder builder =
|
||||
SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
|
||||
const struct spa_pod *params[1];
|
||||
const struct spa_pod *params[2];
|
||||
int i = 0;
|
||||
int ret;
|
||||
|
||||
if (pipewire_backend_has_dmabuf_allocator(output->backend)) {
|
||||
/* TODO: Add support for modifier discovery and negotiation. */
|
||||
uint64_t modifier[] = { DRM_FORMAT_MOD_LINEAR };
|
||||
params[i++] = spa_pod_build_format(&builder,
|
||||
output->base.width, output->base.height,
|
||||
output->base.current_mode->refresh / 1000,
|
||||
output->pixel_format->format,
|
||||
modifier);
|
||||
}
|
||||
|
||||
params[i++] = spa_pod_build_format(&builder,
|
||||
output->base.width, output->base.height,
|
||||
output->base.current_mode->refresh / 1000,
|
||||
|
@ -451,6 +471,51 @@ pipewire_output_stream_state_changed(void *data, enum pw_stream_state old,
|
|||
}
|
||||
}
|
||||
|
||||
struct pipewire_dmabuf {
|
||||
struct linux_dmabuf_memory *linux_dmabuf_memory;
|
||||
unsigned int size;
|
||||
};
|
||||
|
||||
static struct pipewire_dmabuf *
|
||||
pipewire_output_create_dmabuf(struct pipewire_output *output)
|
||||
{
|
||||
struct pipewire_backend *b = output->backend;
|
||||
struct weston_renderer *renderer = b->compositor->renderer;
|
||||
struct linux_dmabuf_memory *linux_dmabuf_memory;
|
||||
struct pipewire_dmabuf *dmabuf;
|
||||
const struct pixel_format_info *format;
|
||||
unsigned int width;
|
||||
unsigned int height;
|
||||
uint64_t modifier[] = { DRM_FORMAT_MOD_LINEAR };
|
||||
|
||||
format = output->pixel_format;
|
||||
width = output->base.width;
|
||||
height = output->base.height;
|
||||
|
||||
linux_dmabuf_memory = renderer->dmabuf_alloc(renderer, width, height,
|
||||
format->format,
|
||||
modifier,
|
||||
ARRAY_LENGTH(modifier));
|
||||
if (!linux_dmabuf_memory) {
|
||||
weston_log("Failed to allocate DMABUF (%ux%u %s)\n",
|
||||
width, height, format->drm_format_name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dmabuf = xzalloc(sizeof(*dmabuf));
|
||||
dmabuf->linux_dmabuf_memory = linux_dmabuf_memory;
|
||||
dmabuf->size = linux_dmabuf_memory->attributes->stride[0] * height;
|
||||
|
||||
return dmabuf;
|
||||
}
|
||||
|
||||
static void
|
||||
pipewire_destroy_dmabuf(struct pipewire_output *output,
|
||||
struct pipewire_dmabuf *dmabuf)
|
||||
{
|
||||
free(dmabuf);
|
||||
}
|
||||
|
||||
static void
|
||||
pipewire_output_stream_param_changed(void *data, uint32_t id,
|
||||
const struct spa_pod *format)
|
||||
|
@ -479,7 +544,28 @@ pipewire_output_stream_param_changed(void *data, uint32_t id,
|
|||
|
||||
spa_format_video_raw_parse(format, &video_info.info.raw);
|
||||
|
||||
width = video_info.info.raw.size.width;
|
||||
height = video_info.info.raw.size.height;
|
||||
|
||||
/* Default to MemFd */
|
||||
buffertype = SPA_DATA_MemFd;
|
||||
stride = width * output->pixel_format->bpp / 8;
|
||||
size = height * stride;
|
||||
|
||||
/* Use DmaBuf if requested and supported */
|
||||
if (spa_pod_find_prop(format, NULL, SPA_FORMAT_VIDEO_modifier)) {
|
||||
struct pipewire_dmabuf *dmabuf;
|
||||
|
||||
dmabuf = pipewire_output_create_dmabuf(output);
|
||||
if (dmabuf) {
|
||||
buffertype = SPA_DATA_DmaBuf;
|
||||
stride = dmabuf->linux_dmabuf_memory->attributes->stride[0];
|
||||
size = dmabuf->size;
|
||||
|
||||
dmabuf->linux_dmabuf_memory->destroy(dmabuf->linux_dmabuf_memory);
|
||||
pipewire_destroy_dmabuf(output, dmabuf);
|
||||
}
|
||||
}
|
||||
|
||||
pipewire_output_debug(output, "param changed: %dx%d@(%d/%d) (%s) (%s)",
|
||||
video_info.info.raw.size.width,
|
||||
|
@ -491,11 +577,6 @@ pipewire_output_stream_param_changed(void *data, uint32_t id,
|
|||
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;
|
||||
stride = width * output->pixel_format->bpp / 8;
|
||||
size = height * stride;
|
||||
|
||||
params[0] = spa_pod_builder_add_object(&builder,
|
||||
SPA_TYPE_OBJECT_ParamBuffers, SPA_PARAM_Buffers,
|
||||
SPA_PARAM_BUFFERS_size, SPA_POD_Int(size),
|
||||
|
@ -548,6 +629,12 @@ pipewire_output_stream_add_buffer_gl(struct pipewire_output *output,
|
|||
unsigned int width;
|
||||
unsigned int height;
|
||||
void *ptr;
|
||||
struct pipewire_frame_data *frame_data = buffer->user_data;
|
||||
struct pipewire_dmabuf *dmabuf = frame_data->dmabuf;
|
||||
|
||||
if (dmabuf)
|
||||
return renderer->create_renderbuffer_dmabuf(&output->base,
|
||||
dmabuf->linux_dmabuf_memory);
|
||||
|
||||
format = output->pixel_format;
|
||||
width = output->base.width;
|
||||
|
@ -621,6 +708,27 @@ pipewire_output_setup_memfd(struct pipewire_output *output,
|
|||
buf->n_datas = 1;
|
||||
}
|
||||
|
||||
static void
|
||||
pipewire_output_setup_dmabuf(struct pipewire_output *output,
|
||||
struct pw_buffer *buffer,
|
||||
struct pipewire_dmabuf *dmabuf)
|
||||
{
|
||||
struct spa_buffer *buf = buffer->buffer;
|
||||
struct spa_data *d = buf->datas;
|
||||
struct linux_dmabuf_memory *linux_dmabuf_memory = dmabuf->linux_dmabuf_memory;
|
||||
|
||||
d[0].type = SPA_DATA_DmaBuf;
|
||||
d[0].flags = SPA_DATA_FLAG_READWRITE;
|
||||
d[0].fd = linux_dmabuf_memory->attributes->fd[0];
|
||||
d[0].mapoffset = 0;
|
||||
d[0].maxsize = dmabuf->size;
|
||||
d[0].data = NULL;
|
||||
d[0].chunk->offset = linux_dmabuf_memory->attributes->offset[0];
|
||||
d[0].chunk->stride = linux_dmabuf_memory->attributes->stride[0];
|
||||
d[0].chunk->size = dmabuf->size;
|
||||
buffer->buffer->n_datas = 1;
|
||||
}
|
||||
|
||||
static void
|
||||
pipewire_output_stream_add_buffer(void *data, struct pw_buffer *buffer)
|
||||
{
|
||||
|
@ -636,7 +744,18 @@ 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)) {
|
||||
if (buffertype & (1u << SPA_DATA_DmaBuf)) {
|
||||
struct pipewire_dmabuf *dmabuf;
|
||||
|
||||
dmabuf = pipewire_output_create_dmabuf(output);
|
||||
if (!dmabuf) {
|
||||
pw_stream_set_error(output->stream, -ENOMEM,
|
||||
"failed to allocate DMABUF buffer");
|
||||
return;
|
||||
}
|
||||
pipewire_output_setup_dmabuf(output, buffer, dmabuf);
|
||||
frame_data->dmabuf = dmabuf;
|
||||
} else if (buffertype & (1u << SPA_DATA_MemFd)) {
|
||||
struct pipewire_memfd *memfd;
|
||||
|
||||
memfd = pipewire_output_create_memfd(output);
|
||||
|
@ -671,6 +790,14 @@ pipewire_output_stream_remove_buffer(void *data, struct pw_buffer *buffer)
|
|||
|
||||
pipewire_output_debug(output, "remove buffer: %p", buffer);
|
||||
|
||||
if (frame_data->dmabuf) {
|
||||
struct weston_compositor *ec = output->base.compositor;
|
||||
const struct weston_renderer *renderer = ec->renderer;
|
||||
|
||||
renderer->remove_renderbuffer_dmabuf(&output->base,
|
||||
frame_data->renderbuffer);
|
||||
pipewire_destroy_dmabuf(output, frame_data->dmabuf);
|
||||
}
|
||||
if (frame_data->memfd) {
|
||||
munmap(d[0].data, d[0].maxsize);
|
||||
pipewire_destroy_memfd(output, frame_data->memfd);
|
||||
|
@ -800,6 +927,8 @@ static void
|
|||
pipewire_submit_buffer(struct pipewire_output *output,
|
||||
struct pw_buffer *buffer)
|
||||
{
|
||||
struct pipewire_frame_data *frame_data = buffer->user_data;
|
||||
struct pipewire_dmabuf *dmabuf = frame_data->dmabuf;
|
||||
struct spa_buffer *spa_buffer;
|
||||
struct spa_meta_header *h;
|
||||
const struct pixel_format_info *pixel_format;
|
||||
|
@ -807,7 +936,10 @@ pipewire_submit_buffer(struct pipewire_output *output,
|
|||
size_t size;
|
||||
|
||||
pixel_format = output->pixel_format;
|
||||
stride = output->base.width * pixel_format->bpp / 8;
|
||||
if (dmabuf)
|
||||
stride = dmabuf->linux_dmabuf_memory->attributes->stride[0];
|
||||
else
|
||||
stride = output->base.width * pixel_format->bpp / 8;
|
||||
size = output->base.height * stride;
|
||||
|
||||
spa_buffer = buffer->buffer;
|
||||
|
|
Loading…
Reference in New Issue