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.
This commit is contained in:
parent
8e566f716c
commit
9f41627aa1
|
@ -4,6 +4,8 @@
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <sys/types.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include <drm_fourcc.h>
|
#include <drm_fourcc.h>
|
||||||
|
@ -30,6 +32,21 @@
|
||||||
#include "tablet-unstable-v2-client-protocol.h"
|
#include "tablet-unstable-v2-client-protocol.h"
|
||||||
#include "relative-pointer-unstable-v1-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) {
|
struct wlr_wl_backend *get_wl_backend_from_backend(struct wlr_backend *backend) {
|
||||||
assert(wlr_backend_is_wl(backend));
|
assert(wlr_backend_is_wl(backend));
|
||||||
return (struct wlr_wl_backend *)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,
|
.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) {
|
static bool device_has_name(const drmDevice *device, const char *name) {
|
||||||
for (size_t i = 0; i < DRM_NODE_MAX; i++) {
|
for (size_t i = 0; i < DRM_NODE_MAX; i++) {
|
||||||
if (!(device->available_nodes & (1 << 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,
|
static void legacy_drm_handle_device(void *data, struct wl_drm *drm,
|
||||||
const char *name) {
|
const char *name) {
|
||||||
struct wlr_wl_backend *wl = data;
|
struct wlr_wl_backend *wl = data;
|
||||||
|
|
||||||
// TODO: get FD from linux-dmabuf hints instead
|
|
||||||
wl->drm_render_name = get_render_name(name);
|
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 &&
|
} else if (strcmp(iface, zwp_linux_dmabuf_v1_interface.name) == 0 &&
|
||||||
version >= 3) {
|
version >= 3) {
|
||||||
wl->zwp_linux_dmabuf_v1 = wl_registry_bind(registry, name,
|
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,
|
zwp_linux_dmabuf_v1_add_listener(wl->zwp_linux_dmabuf_v1,
|
||||||
&linux_dmabuf_v1_listener, wl);
|
&linux_dmabuf_v1_listener, wl);
|
||||||
} else if (strcmp(iface, zwp_relative_pointer_manager_v1_interface.name) == 0) {
|
} 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");
|
wlr_log_errno(WLR_ERROR, "Could not obtain reference to remote registry");
|
||||||
goto error_display;
|
goto error_display;
|
||||||
}
|
}
|
||||||
|
|
||||||
wl_registry_add_listener(wl->registry, ®istry_listener, wl);
|
wl_registry_add_listener(wl->registry, ®istry_listener, wl);
|
||||||
|
|
||||||
wl_display_roundtrip(wl->remote_display); // get globals
|
wl_display_roundtrip(wl->remote_display); // get globals
|
||||||
wl_display_roundtrip(wl->remote_display); // get linux-dmabuf formats
|
|
||||||
|
|
||||||
if (!wl->compositor) {
|
if (!wl->compositor) {
|
||||||
wlr_log(WLR_ERROR,
|
wlr_log(WLR_ERROR,
|
||||||
|
@ -444,6 +571,35 @@ struct wlr_backend *wlr_wl_backend_create(struct wl_display *display,
|
||||||
goto error_registry;
|
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);
|
struct wl_event_loop *loop = wl_display_get_event_loop(wl->local_display);
|
||||||
int fd = wl_display_get_fd(wl->remote_display);
|
int fd = wl_display_get_fd(wl->remote_display);
|
||||||
wl->remote_display_src = wl_event_loop_add_fd(loop, fd, WL_EVENT_READABLE,
|
wl->remote_display_src = wl_event_loop_add_fd(loop, fd, WL_EVENT_READABLE,
|
||||||
|
|
|
@ -105,7 +105,7 @@ wayland_server = dependency('wayland-server',
|
||||||
)
|
)
|
||||||
|
|
||||||
drm = dependency('libdrm',
|
drm = dependency('libdrm',
|
||||||
version: '>=2.4.108',
|
version: '>=2.4.109',
|
||||||
fallback: ['libdrm', 'ext_libdrm'],
|
fallback: ['libdrm', 'ext_libdrm'],
|
||||||
default_options: [
|
default_options: [
|
||||||
'libkms=false',
|
'libkms=false',
|
||||||
|
|
Loading…
Reference in New Issue