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