aboutsummaryrefslogtreecommitdiff
path: root/backend
diff options
context:
space:
mode:
authorSimon Ser <contact@emersion.fr>2021-12-08 01:43:15 +0100
committerKirill Primak <vyivel@posteo.net>2021-12-15 14:34:08 +0000
commit9f41627aa10a94d9427bc315fa3d363a61b94d7c (patch)
tree026d7e9e5ea55dfff036e17fe73a270e98d07648 /backend
parent8e566f716c464d886f1aed7e57b5bf26c3502426 (diff)
backend/wayland: add basic linux-dmabuf feedback support
This patch makes it so we bind to zwp_linux_dmabuf_v1 version 4 and we use it to grab the main device. v4 sends supported formats via a table so we need to handle this as well. v4 allows wlroots to remove the requirement for Mesa's internal wl_drm interface.
Diffstat (limited to 'backend')
-rw-r--r--backend/wayland/backend.c166
1 files changed, 161 insertions, 5 deletions
diff --git a/backend/wayland/backend.c b/backend/wayland/backend.c
index 63d6a7df..1df753b9 100644
--- a/backend/wayland/backend.c
+++ b/backend/wayland/backend.c
@@ -4,6 +4,8 @@
#include <limits.h>
#include <stdint.h>
#include <stdlib.h>
+#include <sys/mman.h>
+#include <sys/types.h>
#include <unistd.h>
#include <drm_fourcc.h>
@@ -30,6 +32,21 @@
#include "tablet-unstable-v2-client-protocol.h"
#include "relative-pointer-unstable-v1-client-protocol.h"
+struct wlr_wl_linux_dmabuf_feedback_v1 {
+ struct wlr_wl_backend *backend;
+ dev_t main_device_id;
+ struct wlr_wl_linux_dmabuf_v1_table_entry *format_table;
+ size_t format_table_size;
+
+ dev_t tranche_target_device_id;
+};
+
+struct wlr_wl_linux_dmabuf_v1_table_entry {
+ uint32_t format;
+ uint32_t pad; /* unused */
+ uint64_t modifier;
+};
+
struct wlr_wl_backend *get_wl_backend_from_backend(struct wlr_backend *backend) {
assert(wlr_backend_is_wl(backend));
return (struct wlr_wl_backend *)backend;
@@ -108,6 +125,119 @@ static const struct zwp_linux_dmabuf_v1_listener linux_dmabuf_v1_listener = {
.modifier = linux_dmabuf_v1_handle_modifier,
};
+static void linux_dmabuf_feedback_v1_handle_done(void *data,
+ struct zwp_linux_dmabuf_feedback_v1 *feedback) {
+ // This space is intentionally left blank
+}
+
+static void linux_dmabuf_feedback_v1_handle_format_table(void *data,
+ struct zwp_linux_dmabuf_feedback_v1 *feedback, int fd, uint32_t size) {
+ struct wlr_wl_linux_dmabuf_feedback_v1 *feedback_data = data;
+
+ feedback_data->format_table = NULL;
+
+ void *table_data = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
+ if (table_data == MAP_FAILED) {
+ wlr_log_errno(WLR_ERROR, "failed to mmap DMA-BUF format table");
+ } else {
+ feedback_data->format_table = table_data;
+ feedback_data->format_table_size = size;
+ }
+ close(fd);
+}
+
+static void linux_dmabuf_feedback_v1_handle_main_device(void *data,
+ struct zwp_linux_dmabuf_feedback_v1 *feedback,
+ struct wl_array *dev_id_arr) {
+ struct wlr_wl_linux_dmabuf_feedback_v1 *feedback_data = data;
+
+ dev_t dev_id;
+ assert(dev_id_arr->size == sizeof(dev_id));
+ memcpy(&dev_id, dev_id_arr->data, sizeof(dev_id));
+
+ feedback_data->main_device_id = dev_id;
+
+ drmDevice *device = NULL;
+ if (drmGetDeviceFromDevId(dev_id, 0, &device) != 0) {
+ wlr_log_errno(WLR_ERROR, "drmGetDeviceFromDevId failed");
+ return;
+ }
+
+ const char *name = NULL;
+ if (device->available_nodes & (1 << DRM_NODE_RENDER)) {
+ name = device->nodes[DRM_NODE_RENDER];
+ } else {
+ // Likely a split display/render setup. Pick the primary node and hope
+ // Mesa will open the right render node under-the-hood.
+ assert(device->available_nodes & (1 << DRM_NODE_PRIMARY));
+ name = device->nodes[DRM_NODE_PRIMARY];
+ wlr_log(WLR_DEBUG, "DRM device %s has no render node, "
+ "falling back to primary node", name);
+ }
+
+ feedback_data->backend->drm_render_name = strdup(name);
+
+ drmFreeDevice(&device);
+}
+
+static void linux_dmabuf_feedback_v1_handle_tranche_done(void *data,
+ struct zwp_linux_dmabuf_feedback_v1 *feedback) {
+ struct wlr_wl_linux_dmabuf_feedback_v1 *feedback_data = data;
+ feedback_data->tranche_target_device_id = 0;
+}
+
+static void linux_dmabuf_feedback_v1_handle_tranche_target_device(void *data,
+ struct zwp_linux_dmabuf_feedback_v1 *feedback,
+ struct wl_array *dev_id_arr) {
+ struct wlr_wl_linux_dmabuf_feedback_v1 *feedback_data = data;
+
+ dev_t dev_id;
+ assert(dev_id_arr->size == sizeof(dev_id));
+ memcpy(&dev_id, dev_id_arr->data, sizeof(dev_id));
+
+ feedback_data->tranche_target_device_id = dev_id;
+}
+
+static void linux_dmabuf_feedback_v1_handle_tranche_formats(void *data,
+ struct zwp_linux_dmabuf_feedback_v1 *feedback,
+ struct wl_array *indices_arr) {
+ struct wlr_wl_linux_dmabuf_feedback_v1 *feedback_data = data;
+
+ if (feedback_data->format_table == NULL) {
+ return;
+ }
+ if (feedback_data->tranche_target_device_id != feedback_data->main_device_id) {
+ return;
+ }
+
+ size_t table_cap = feedback_data->format_table_size /
+ sizeof(struct wlr_wl_linux_dmabuf_v1_table_entry);
+ uint16_t *index_ptr;
+ wl_array_for_each(index_ptr, indices_arr) {
+ assert(*index_ptr < table_cap);
+ const struct wlr_wl_linux_dmabuf_v1_table_entry *entry =
+ &feedback_data->format_table[*index_ptr];
+ wlr_drm_format_set_add(&feedback_data->backend->linux_dmabuf_v1_formats,
+ entry->format, entry->modifier);
+ }
+}
+
+static void linux_dmabuf_feedback_v1_handle_tranche_flags(void *data,
+ struct zwp_linux_dmabuf_feedback_v1 *feedback, uint32_t flags) {
+ // TODO: handle SCANOUT flag
+}
+
+static const struct zwp_linux_dmabuf_feedback_v1_listener
+ linux_dmabuf_feedback_v1_listener = {
+ .done = linux_dmabuf_feedback_v1_handle_done,
+ .format_table = linux_dmabuf_feedback_v1_handle_format_table,
+ .main_device = linux_dmabuf_feedback_v1_handle_main_device,
+ .tranche_done = linux_dmabuf_feedback_v1_handle_tranche_done,
+ .tranche_target_device = linux_dmabuf_feedback_v1_handle_tranche_target_device,
+ .tranche_formats = linux_dmabuf_feedback_v1_handle_tranche_formats,
+ .tranche_flags = linux_dmabuf_feedback_v1_handle_tranche_flags,
+};
+
static bool device_has_name(const drmDevice *device, const char *name) {
for (size_t i = 0; i < DRM_NODE_MAX; i++) {
if (!(device->available_nodes & (1 << i))) {
@@ -172,8 +302,6 @@ static char *get_render_name(const char *name) {
static void legacy_drm_handle_device(void *data, struct wl_drm *drm,
const char *name) {
struct wlr_wl_backend *wl = data;
-
- // TODO: get FD from linux-dmabuf hints instead
wl->drm_render_name = get_render_name(name);
}
@@ -245,7 +373,7 @@ static void registry_global(void *data, struct wl_registry *registry,
} else if (strcmp(iface, zwp_linux_dmabuf_v1_interface.name) == 0 &&
version >= 3) {
wl->zwp_linux_dmabuf_v1 = wl_registry_bind(registry, name,
- &zwp_linux_dmabuf_v1_interface, 3);
+ &zwp_linux_dmabuf_v1_interface, version >= 4 ? 4 : version);
zwp_linux_dmabuf_v1_add_listener(wl->zwp_linux_dmabuf_v1,
&linux_dmabuf_v1_listener, wl);
} else if (strcmp(iface, zwp_relative_pointer_manager_v1_interface.name) == 0) {
@@ -428,10 +556,9 @@ struct wlr_backend *wlr_wl_backend_create(struct wl_display *display,
wlr_log_errno(WLR_ERROR, "Could not obtain reference to remote registry");
goto error_display;
}
-
wl_registry_add_listener(wl->registry, &registry_listener, wl);
+
wl_display_roundtrip(wl->remote_display); // get globals
- wl_display_roundtrip(wl->remote_display); // get linux-dmabuf formats
if (!wl->compositor) {
wlr_log(WLR_ERROR,
@@ -444,6 +571,35 @@ struct wlr_backend *wlr_wl_backend_create(struct wl_display *display,
goto error_registry;
}
+ struct zwp_linux_dmabuf_feedback_v1 *linux_dmabuf_feedback_v1 = NULL;
+ struct wlr_wl_linux_dmabuf_feedback_v1 feedback_data = { .backend = wl };
+ if (wl->zwp_linux_dmabuf_v1 != NULL &&
+ zwp_linux_dmabuf_v1_get_version(wl->zwp_linux_dmabuf_v1) >=
+ ZWP_LINUX_DMABUF_V1_GET_DEFAULT_FEEDBACK_SINCE_VERSION) {
+ linux_dmabuf_feedback_v1 =
+ zwp_linux_dmabuf_v1_get_default_feedback(wl->zwp_linux_dmabuf_v1);
+ if (linux_dmabuf_feedback_v1 == NULL) {
+ wlr_log(WLR_ERROR, "Allocation failed");
+ goto error_registry;
+ }
+ zwp_linux_dmabuf_feedback_v1_add_listener(linux_dmabuf_feedback_v1,
+ &linux_dmabuf_feedback_v1_listener, &feedback_data);
+
+ if (wl->legacy_drm != NULL) {
+ wl_drm_destroy(wl->legacy_drm);
+ wl->legacy_drm = NULL;
+ }
+ }
+
+ wl_display_roundtrip(wl->remote_display); // get linux-dmabuf formats
+
+ if (feedback_data.format_table != NULL) {
+ munmap(feedback_data.format_table, feedback_data.format_table_size);
+ }
+ if (linux_dmabuf_feedback_v1 != NULL) {
+ zwp_linux_dmabuf_feedback_v1_destroy(linux_dmabuf_feedback_v1);
+ }
+
struct wl_event_loop *loop = wl_display_get_event_loop(wl->local_display);
int fd = wl_display_get_fd(wl->remote_display);
wl->remote_display_src = wl_event_loop_add_fd(loop, fd, WL_EVENT_READABLE,