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 <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, ®istry_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, | ||||
|  |  | |||
|  | @ -105,7 +105,7 @@ wayland_server = dependency('wayland-server', | |||
| ) | ||||
| 
 | ||||
| drm = dependency('libdrm', | ||||
| 	version: '>=2.4.108', | ||||
| 	version: '>=2.4.109', | ||||
| 	fallback: ['libdrm', 'ext_libdrm'], | ||||
| 	default_options: [ | ||||
| 		'libkms=false', | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue