mirror of
https://github.com/hyprwm/xdg-desktop-portal-hyprland.git
synced 2024-11-22 14:35:57 +01:00
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
This commit is contained in:
parent
3591fd2a6c
commit
2219db7508
4 changed files with 193 additions and 25 deletions
|
@ -5,6 +5,7 @@
|
|||
#include <pipewire/pipewire.h>
|
||||
#include <spa/param/video/format-utils.h>
|
||||
#include <wayland-client-protocol.h>
|
||||
#include <xf86drm.h>
|
||||
|
||||
#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);
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <libdrm/drm_fourcc.h>
|
||||
#include <xf86drm.h>
|
||||
#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;
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include <unistd.h>
|
||||
#include <assert.h>
|
||||
#include <wayland-client-protocol.h>
|
||||
#include <xf86drm.h>
|
||||
|
||||
#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);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue