From 2219db750899856b7334c1968db64226971a7992 Mon Sep 17 00:00:00 2001 From: columbarius Date: Thu, 5 May 2022 23:41:53 +0200 Subject: [PATCH] screencast: use dmabuf_feedback The compositor can announce it's default rendering device via linux_dmabuf_feedback as the main_device [1]. We should use this device whenever possible. If aquiring this device fails we are adviced to use force linear layout on buffers allocated with the implicit api. With linux_dmabuf_v1 the modifier event is deprecated. Instead the format_table event in combination with the tranches of linux_dmabuf_feedback_v1 has to be used. [1] https://gitlab.freedesktop.org/wayland/wayland-protocols/-/blob/main/unstable/linux-dmabuf/feedback.rst --- include/screencast_common.h | 11 +- include/wlr_screencast.h | 3 +- src/screencast/screencast_common.c | 10 +- src/screencast/wlr_screencast.c | 194 ++++++++++++++++++++++++++--- 4 files changed, 193 insertions(+), 25 deletions(-) diff --git a/include/screencast_common.h b/include/screencast_common.h index 3996c84..92f8ea7 100644 --- a/include/screencast_common.h +++ b/include/screencast_common.h @@ -5,6 +5,7 @@ #include #include #include +#include #include "fps_limit.h" @@ -97,6 +98,12 @@ struct xdpw_format_modifier_pair { uint64_t modifier; }; +struct xdpw_dmabuf_feedback_data { + void *format_table_data; + uint32_t format_table_size; + bool device_used; +}; + struct xdpw_screencast_context { // xdpw @@ -113,6 +120,8 @@ struct xdpw_screencast_context { struct zxdg_output_manager_v1 *xdg_output_manager; struct wl_shm *shm; struct zwp_linux_dmabuf_v1 *linux_dmabuf; + struct zwp_linux_dmabuf_feedback_v1 *linux_dmabuf_feedback; + struct xdpw_dmabuf_feedback_data feedback_data; struct wl_list format_modifier_pairs; // gbm @@ -173,7 +182,7 @@ struct xdpw_wlr_output { }; void randname(char *buf); -struct gbm_device *xdpw_gbm_device_create(void); +struct gbm_device *xdpw_gbm_device_create(drmDevice *device); struct xdpw_buffer *xdpw_buffer_create(struct xdpw_screencast_instance *cast, enum buffer_type buffer_type, struct xdpw_screencopy_frame_info *frame_info); void xdpw_buffer_destroy(struct xdpw_buffer *buffer); diff --git a/include/wlr_screencast.h b/include/wlr_screencast.h index 1af267f..83eaa49 100644 --- a/include/wlr_screencast.h +++ b/include/wlr_screencast.h @@ -12,7 +12,8 @@ #define XDG_OUTPUT_MANAGER_VERSION 3 -#define LINUX_DMABUF_VERSION 3 +#define LINUX_DMABUF_VERSION 4 +#define LINUX_DMABUF_VERSION_MIN 3 struct xdpw_state; diff --git a/src/screencast/screencast_common.c b/src/screencast/screencast_common.c index 038cf7a..8e759a1 100644 --- a/src/screencast/screencast_common.c +++ b/src/screencast/screencast_common.c @@ -7,7 +7,6 @@ #include #include #include -#include #include "linux-dmabuf-unstable-v1-client-protocol.h" #include "logger.h" @@ -23,13 +22,16 @@ void randname(char *buf) { } } -static char *gbm_find_render_node() { +static char *gbm_find_render_node(drmDevice *device) { drmDevice *devices[64]; char *render_node = NULL; int n = drmGetDevices2(0, devices, sizeof(devices) / sizeof(devices[0])); for (int i = 0; i < n; ++i) { drmDevice *dev = devices[i]; + if (device && !drmDevicesEqual(device, dev)) { + continue; + } if (!(dev->available_nodes & (1 << DRM_NODE_RENDER))) continue; @@ -41,11 +43,11 @@ static char *gbm_find_render_node() { return render_node; } -struct gbm_device *xdpw_gbm_device_create(void) { +struct gbm_device *xdpw_gbm_device_create(drmDevice *device) { struct gbm_device *gbm; char *render_node = NULL; - render_node = gbm_find_render_node(); + render_node = gbm_find_render_node(device); if (render_node == NULL) { logprint(ERROR, "xdpw: Could not find render node"); return NULL; diff --git a/src/screencast/wlr_screencast.c b/src/screencast/wlr_screencast.c index 0e27282..cbc4c56 100644 --- a/src/screencast/wlr_screencast.c +++ b/src/screencast/wlr_screencast.c @@ -15,6 +15,7 @@ #include #include #include +#include #include "screencast.h" #include "pipewire_screencast.h" @@ -545,6 +546,26 @@ static void wlr_remove_output(struct xdpw_wlr_output *out) { free(out); } +static void wlr_format_modifier_pair_add(struct xdpw_screencast_context *ctx, + uint32_t format, uint64_t modifier) { + struct xdpw_format_modifier_pair *fm_pair = calloc(1, sizeof(struct xdpw_format_modifier_pair)); + + fm_pair->fourcc = format; + fm_pair->modifier = modifier; + + logprint(TRACE, "wlroots: format %u (%lu)", fm_pair->fourcc, fm_pair->modifier); + + wl_list_insert(&ctx->format_modifier_pairs, &fm_pair->link); +} + +static void wlr_format_modifier_pair_emtpy_list(struct xdpw_screencast_context *ctx) { + struct xdpw_format_modifier_pair *fm_pair, *tmp; + wl_list_for_each_safe(fm_pair, tmp, &ctx->format_modifier_pairs, link) { + wl_list_remove(&fm_pair->link); + free(fm_pair); + } +} + static void linux_dmabuf_handle_modifier(void *data, struct zwp_linux_dmabuf_v1 *zwp_linux_dmabuf_v1, uint32_t format, uint32_t modifier_hi, uint32_t modifier_lo) { @@ -552,14 +573,8 @@ static void linux_dmabuf_handle_modifier(void *data, logprint(TRACE, "wlroots: linux_dmabuf_handle_modifier called"); - struct xdpw_format_modifier_pair *fm_pair = calloc(1, sizeof(struct xdpw_format_modifier_pair)); - - fm_pair->fourcc = format; - fm_pair->modifier = ((((uint64_t)modifier_hi) << 32) | modifier_lo); - - logprint(TRACE, "wlroots: format %u (%u)", fm_pair->fourcc, fm_pair->modifier); - - wl_list_insert(&ctx->format_modifier_pairs, &fm_pair->link); + uint64_t modifier = (((uint64_t)modifier_hi) << 32) | modifier_lo; + wlr_format_modifier_pair_add(ctx, format, modifier); } static const struct zwp_linux_dmabuf_v1_listener linux_dmabuf_listener = { @@ -567,6 +582,134 @@ static const struct zwp_linux_dmabuf_v1_listener linux_dmabuf_listener = { .modifier = linux_dmabuf_handle_modifier, }; +static void linux_dmabuf_feedback_handle_main_device(void *data, + struct zwp_linux_dmabuf_feedback_v1 *zwp_linux_dmabuf_feedback_v1, struct wl_array *device_arr) { + struct xdpw_screencast_context *ctx = data; + + logprint(DEBUG, "wlroots: linux_dmabuf_feedback_handle_main_device called"); + + assert(ctx->gbm == NULL); + + dev_t device; + assert(device_arr->size == sizeof(device)); + memcpy(&device, device_arr->data, sizeof(device)); + + drmDevice *drmDev; + if (drmGetDeviceFromDevId(device, /* flags */ 0, &drmDev) != 0) { + logprint(WARN, "wlroots: unable to open main device"); + ctx->state->config->screencast_conf.force_mod_linear = true; + return; + } + ctx->gbm = xdpw_gbm_device_create(drmDev); +} + +static void linux_dmabuf_feedback_format_table(void *data, + struct zwp_linux_dmabuf_feedback_v1 *zwp_linux_dmabuf_feedback_v1, int fd, uint32_t size) { + struct xdpw_screencast_context *ctx = data; + + logprint(DEBUG, "wlroots: linux_dmabuf_feedback_format_table called"); + + wlr_format_modifier_pair_emtpy_list(ctx); + + ctx->feedback_data.format_table_data = mmap(NULL , size, PROT_READ, MAP_PRIVATE, fd, 0); + if (ctx->feedback_data.format_table_data == MAP_FAILED) { + ctx->feedback_data.format_table_data = NULL; + ctx->feedback_data.format_table_size = 0; + return; + } + ctx->feedback_data.format_table_size = size; +} + +static void linux_dmabuf_feedback_handle_done(void *data, + struct zwp_linux_dmabuf_feedback_v1 *zwp_linux_dmabuf_feedback_v1) { + struct xdpw_screencast_context *ctx = data; + + logprint(DEBUG, "wlroots: linux_dmabuf_feedback_handle_done called"); + + if (ctx->feedback_data.format_table_data) { + munmap(ctx->feedback_data.format_table_data, ctx->feedback_data.format_table_size); + } + ctx->feedback_data.format_table_data = NULL; + ctx->feedback_data.format_table_size = 0; +} + +static void linux_dmabuf_feedback_tranche_target_devices(void *data, + struct zwp_linux_dmabuf_feedback_v1 *zwp_linux_dmabuf_feedback_v1, struct wl_array *device_arr) { + struct xdpw_screencast_context *ctx = data; + + logprint(DEBUG, "wlroots: linux_dmabuf_feedback_tranche_target_devices called"); + + dev_t device; + assert(device_arr->size == sizeof(device)); + memcpy(&device, device_arr->data, sizeof(device)); + + drmDevice *drmDev; + if (drmGetDeviceFromDevId(device, /* flags */ 0, &drmDev) != 0) { + return; + } + + if (ctx->gbm) { + drmDevice *drmDevRenderer = NULL; + drmGetDevice2(gbm_device_get_fd(ctx->gbm), /* flags */ 0, &drmDevRenderer); + ctx->feedback_data.device_used = drmDevicesEqual(drmDevRenderer, drmDev); + } else { + ctx->gbm = xdpw_gbm_device_create(drmDev); + ctx->feedback_data.device_used = ctx->gbm != NULL; + } +} + +static void linux_dmabuf_feedback_tranche_flags(void *data, + struct zwp_linux_dmabuf_feedback_v1 *zwp_linux_dmabuf_feedback_v1, uint32_t flags) { + logprint(DEBUG, "wlroots: linux_dmabuf_feedback_tranche_flags called"); +} + +static void linux_dmabuf_feedback_tranche_formats(void *data, + struct zwp_linux_dmabuf_feedback_v1 *zwp_linux_dmabuf_feedback_v1, struct wl_array *indices) { + struct xdpw_screencast_context *ctx = data; + + logprint(DEBUG, "wlroots: linux_dmabuf_feedback_format_table called"); + + if (!ctx->feedback_data.device_used || !ctx->feedback_data.format_table_data) { + return; + } + struct fm_entry { + uint32_t format; + uint32_t padding; + uint64_t modifier; + }; + // An entry in the table has to be 16 bytes long + assert(sizeof(struct fm_entry) == 16); + + uint32_t n_modifiers = ctx->feedback_data.format_table_size/sizeof(struct fm_entry); + struct fm_entry *fm_entry = ctx->feedback_data.format_table_data; + uint16_t *idx; + wl_array_for_each(idx, indices) { + if (*idx >= n_modifiers) { + continue; + } + wlr_format_modifier_pair_add(ctx, (fm_entry + *idx)->format, (fm_entry + *idx)->modifier); + } +} + +static void linux_dmabuf_feedback_tranche_done(void *data, + struct zwp_linux_dmabuf_feedback_v1 *zwp_linux_dmabuf_feedback_v1) { + struct xdpw_screencast_context *ctx = data; + + logprint(DEBUG, "wlroots: linux_dmabuf_feedback_tranche_done called"); + + ctx->feedback_data.device_used = false; +} + +static const struct zwp_linux_dmabuf_feedback_v1_listener linux_dmabuf_listener_feedback = { + .main_device = linux_dmabuf_feedback_handle_main_device, + .format_table = linux_dmabuf_feedback_format_table, + .done = linux_dmabuf_feedback_handle_done, + .tranche_target_device = linux_dmabuf_feedback_tranche_target_devices, + .tranche_flags = linux_dmabuf_feedback_tranche_flags, + .tranche_formats = linux_dmabuf_feedback_tranche_formats, + .tranche_done = linux_dmabuf_feedback_tranche_done, +}; + static void wlr_registry_handle_add(void *data, struct wl_registry *reg, uint32_t id, const char *interface, uint32_t ver) { struct xdpw_screencast_context *ctx = data; @@ -609,10 +752,22 @@ static void wlr_registry_handle_add(void *data, struct wl_registry *reg, wl_registry_bind(reg, id, &zxdg_output_manager_v1_interface, XDG_OUTPUT_MANAGER_VERSION); } if (strcmp(interface, zwp_linux_dmabuf_v1_interface.name) == 0) { - logprint(DEBUG, "wlroots: |-- registered to interface %s (Version %u)", interface, LINUX_DMABUF_VERSION); - ctx->linux_dmabuf = wl_registry_bind(reg, id, &zwp_linux_dmabuf_v1_interface, LINUX_DMABUF_VERSION); + uint32_t version = ver; + if (LINUX_DMABUF_VERSION < ver) { + version = LINUX_DMABUF_VERSION; + } else if (LINUX_DMABUF_VERSION_MIN > ver) { + logprint(INFO, "wlroots: interface %s (Version %u) is required for DMA-BUF screencast", interface, LINUX_DMABUF_VERSION_MIN); + return; + } + logprint(DEBUG, "wlroots: |-- registered to interface %s (Version %u)", interface, version); + ctx->linux_dmabuf = wl_registry_bind(reg, id, &zwp_linux_dmabuf_v1_interface, version); - zwp_linux_dmabuf_v1_add_listener(ctx->linux_dmabuf, &linux_dmabuf_listener, ctx); + if (version >= 4) { + ctx->linux_dmabuf_feedback = zwp_linux_dmabuf_v1_get_default_feedback(ctx->linux_dmabuf); + zwp_linux_dmabuf_feedback_v1_add_listener(ctx->linux_dmabuf_feedback, &linux_dmabuf_listener_feedback, ctx); + } else { + zwp_linux_dmabuf_v1_add_listener(ctx->linux_dmabuf, &linux_dmabuf_listener, ctx); + } } } @@ -677,20 +832,18 @@ int xdpw_wlr_screencopy_init(struct xdpw_state *state) { } // make sure we have a gbm device - ctx->gbm = xdpw_gbm_device_create(); - if (!ctx->gbm) { - logprint(ERROR, "System doesn't support gbm!"); + if (ctx->linux_dmabuf && !ctx->gbm) { + ctx->gbm = xdpw_gbm_device_create(NULL); + if (!ctx->gbm) { + logprint(ERROR, "System doesn't support gbm!"); + } } return 0; } void xdpw_wlr_screencopy_finish(struct xdpw_screencast_context *ctx) { - struct xdpw_format_modifier_pair *fm_pair, *tmp_fmp; - wl_list_for_each_safe(fm_pair, tmp_fmp, &ctx->format_modifier_pairs, link) { - wl_list_remove(&fm_pair->link); - free(fm_pair); - } + wlr_format_modifier_pair_emtpy_list(ctx); struct xdpw_wlr_output *output, *tmp_o; wl_list_for_each_safe(output, tmp_o, &ctx->output_list, link) { @@ -718,6 +871,9 @@ void xdpw_wlr_screencopy_finish(struct xdpw_screencast_context *ctx) { gbm_device_destroy(ctx->gbm); close(fd); } + if (ctx->linux_dmabuf_feedback) { + zwp_linux_dmabuf_feedback_v1_destroy(ctx->linux_dmabuf_feedback); + } if (ctx->linux_dmabuf) { zwp_linux_dmabuf_v1_destroy(ctx->linux_dmabuf); }