linux-dmabuf-v1: add per-surface feedback
This commit is contained in:
		
							parent
							
								
									1d8340754b
								
							
						
					
					
						commit
						c50c4fc5cc
					
				|  | @ -15,6 +15,8 @@ | |||
| #include <wlr/types/wlr_buffer.h> | ||||
| #include <wlr/render/dmabuf.h> | ||||
| 
 | ||||
| struct wlr_surface; | ||||
| 
 | ||||
| struct wlr_dmabuf_v1_buffer { | ||||
| 	struct wlr_buffer base; | ||||
| 
 | ||||
|  | @ -63,6 +65,7 @@ struct wlr_linux_dmabuf_v1 { | |||
| 	// private state
 | ||||
| 
 | ||||
| 	struct wlr_linux_dmabuf_feedback_v1_compiled *default_feedback; | ||||
| 	struct wl_list surfaces; // wlr_linux_dmabuf_v1_surface.link
 | ||||
| 
 | ||||
| 	struct wl_listener display_destroy; | ||||
| 	struct wl_listener renderer_destroy; | ||||
|  | @ -74,4 +77,13 @@ struct wlr_linux_dmabuf_v1 { | |||
| struct wlr_linux_dmabuf_v1 *wlr_linux_dmabuf_v1_create(struct wl_display *display, | ||||
| 	struct wlr_renderer *renderer); | ||||
| 
 | ||||
| /**
 | ||||
|  * Set a surface's DMA-BUF feedback. | ||||
|  * | ||||
|  * Passing a NULL feedback resets it to the default feedback. | ||||
|  */ | ||||
| bool wlr_linux_dmabuf_v1_set_surface_feedback( | ||||
| 	struct wlr_linux_dmabuf_v1 *linux_dmabuf, struct wlr_surface *surface, | ||||
| 	const struct wlr_linux_dmabuf_feedback_v1 *feedback); | ||||
| 
 | ||||
| #endif | ||||
|  |  | |||
|  | @ -6,6 +6,7 @@ | |||
| #include <unistd.h> | ||||
| #include <wlr/render/wlr_renderer.h> | ||||
| #include <wlr/types/wlr_linux_dmabuf_v1.h> | ||||
| #include <wlr/types/wlr_surface.h> | ||||
| #include <wlr/util/log.h> | ||||
| #include "linux-dmabuf-unstable-v1-protocol.h" | ||||
| #include "render/drm_format_set.h" | ||||
|  | @ -47,6 +48,17 @@ struct wlr_linux_dmabuf_feedback_v1_table_entry { | |||
| _Static_assert(sizeof(struct wlr_linux_dmabuf_feedback_v1_table_entry) == 16, | ||||
| 	"Expected wlr_linux_dmabuf_feedback_v1_table_entry to be tightly packed"); | ||||
| 
 | ||||
| struct wlr_linux_dmabuf_v1_surface { | ||||
| 	struct wlr_surface *surface; | ||||
| 	struct wlr_linux_dmabuf_v1 *linux_dmabuf; | ||||
| 	struct wl_list link; // wlr_linux_dmabuf_v1.surfaces
 | ||||
| 
 | ||||
| 	struct wlr_addon addon; | ||||
| 	struct wlr_linux_dmabuf_feedback_v1_compiled *feedback; | ||||
| 
 | ||||
| 	struct wl_list feedback_resources; // wl_resource_get_link
 | ||||
| }; | ||||
| 
 | ||||
| static void buffer_handle_destroy(struct wl_client *client, | ||||
| 		struct wl_resource *resource) { | ||||
| 	wl_resource_destroy(resource); | ||||
|  | @ -451,6 +463,36 @@ static const struct zwp_linux_dmabuf_feedback_v1_interface | |||
| 	.destroy = linux_dmabuf_feedback_destroy, | ||||
| }; | ||||
| 
 | ||||
| static ssize_t get_drm_format_set_index(const struct wlr_drm_format_set *set, | ||||
| 		uint32_t format, uint64_t modifier) { | ||||
| 	bool format_found = false; | ||||
| 	const struct wlr_drm_format *fmt; | ||||
| 	size_t idx = 0; | ||||
| 	for (size_t i = 0; i < set->len; i++) { | ||||
| 		fmt = set->formats[i]; | ||||
| 		if (fmt->format == format) { | ||||
| 			format_found = true; | ||||
| 			break; | ||||
| 		} | ||||
| 		idx += 1 + fmt->len; | ||||
| 	} | ||||
| 	if (!format_found) { | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	if (modifier == DRM_FORMAT_MOD_INVALID) { | ||||
| 		return idx; | ||||
| 	} | ||||
| 
 | ||||
| 	for (size_t i = 0; i < fmt->len; i++) { | ||||
| 		if (fmt->modifiers[i] == modifier) { | ||||
| 			return idx; | ||||
| 		} | ||||
| 		idx++; | ||||
| 	} | ||||
| 	return -1; | ||||
| } | ||||
| 
 | ||||
| static struct wlr_linux_dmabuf_feedback_v1_compiled *feedback_compile( | ||||
| 		const struct wlr_linux_dmabuf_feedback_v1 *feedback) { | ||||
| 	assert(feedback->tranches_len > 0); | ||||
|  | @ -523,7 +565,53 @@ static struct wlr_linux_dmabuf_feedback_v1_compiled *feedback_compile( | |||
| 
 | ||||
| 	// Build the indices lists for all but the last (fallback) tranches
 | ||||
| 	for (size_t i = 0; i < feedback->tranches_len - 1; i++) { | ||||
| 		assert(false); // TODO: unimplemented
 | ||||
| 		const struct wlr_linux_dmabuf_feedback_v1_tranche *tranche = | ||||
| 			&feedback->tranches[i]; | ||||
| 		struct wlr_linux_dmabuf_feedback_v1_compiled_tranche *compiled_tranche = | ||||
| 			&compiled->tranches[i]; | ||||
| 
 | ||||
| 		compiled_tranche->target_device = tranche->target_device; | ||||
| 		compiled_tranche->flags = tranche->flags; | ||||
| 
 | ||||
| 		wl_array_init(&compiled_tranche->indices); | ||||
| 		if (!wl_array_add(&compiled_tranche->indices, table_len * sizeof(uint16_t))) { | ||||
| 			wlr_log(WLR_ERROR, "Failed to allocate tranche indices array"); | ||||
| 			goto error_compiled; | ||||
| 		} | ||||
| 
 | ||||
| 		n = 0; | ||||
| 		uint16_t *indices = compiled_tranche->indices.data; | ||||
| 		for (size_t j = 0; j < tranche->formats->len; j++) { | ||||
| 			const struct wlr_drm_format *fmt = tranche->formats->formats[j]; | ||||
| 
 | ||||
| 			ssize_t index = get_drm_format_set_index( | ||||
| 				fallback_tranche->formats, fmt->format, | ||||
| 				DRM_FORMAT_MOD_INVALID); | ||||
| 			if (index < 0) { | ||||
| 				wlr_log(WLR_ERROR, "Format 0x%" PRIX32 " and modifier " | ||||
| 					"INVALID are in tranche #%zu but are missing " | ||||
| 					"from the fallback tranche", | ||||
| 					fmt->format, i); | ||||
| 				goto error_compiled; | ||||
| 			} | ||||
| 			indices[n] = index; | ||||
| 			n++; | ||||
| 
 | ||||
| 			for (size_t k = 0; k < fmt->len; k++) { | ||||
| 				ssize_t index = get_drm_format_set_index( | ||||
| 					fallback_tranche->formats, fmt->format, fmt->modifiers[k]); | ||||
| 				if (index < 0) { | ||||
| 					wlr_log(WLR_ERROR, "Format 0x%" PRIX32 " and modifier " | ||||
| 						"0x%" PRIX64 " are in tranche #%zu but are missing " | ||||
| 						"from the fallback tranche", | ||||
| 						fmt->format, fmt->modifiers[k], i); | ||||
| 					goto error_compiled; | ||||
| 				} | ||||
| 				indices[n] = index; | ||||
| 				n++; | ||||
| 			} | ||||
| 		} | ||||
| 		compiled_tranche->indices.size = n * sizeof(uint16_t); | ||||
| 	} | ||||
| 
 | ||||
| 	struct wlr_linux_dmabuf_feedback_v1_compiled_tranche *fallback_compiled_tranche = | ||||
|  | @ -556,6 +644,9 @@ error_compiled: | |||
| 
 | ||||
| static void compiled_feedback_destroy( | ||||
| 		struct wlr_linux_dmabuf_feedback_v1_compiled *feedback) { | ||||
| 	if (feedback == NULL) { | ||||
| 		return; | ||||
| 	} | ||||
| 	for (size_t i = 0; i < feedback->tranches_len; i++) { | ||||
| 		wl_array_release(&feedback->tranches[i].indices); | ||||
| 	} | ||||
|  | @ -656,11 +747,96 @@ static void linux_dmabuf_get_default_feedback(struct wl_client *client, | |||
| 	feedback_send(linux_dmabuf->default_feedback, feedback_resource); | ||||
| } | ||||
| 
 | ||||
| static void surface_destroy(struct wlr_linux_dmabuf_v1_surface *surface) { | ||||
| 	struct wl_resource *resource, *resource_tmp; | ||||
| 	wl_resource_for_each_safe(resource, resource_tmp, &surface->feedback_resources) { | ||||
| 		struct wl_list *link = wl_resource_get_link(resource); | ||||
| 		wl_list_remove(link); | ||||
| 		wl_list_init(link); | ||||
| 	} | ||||
| 
 | ||||
| 	compiled_feedback_destroy(surface->feedback); | ||||
| 
 | ||||
| 	wlr_addon_finish(&surface->addon); | ||||
| 	wl_list_remove(&surface->link); | ||||
| 	free(surface); | ||||
| } | ||||
| 
 | ||||
| static void surface_addon_destroy(struct wlr_addon *addon) { | ||||
| 	struct wlr_linux_dmabuf_v1_surface *surface = | ||||
| 		wl_container_of(addon, surface, addon); | ||||
| 	surface_destroy(surface); | ||||
| } | ||||
| 
 | ||||
| static const struct wlr_addon_interface surface_addon_impl = { | ||||
| 	.name = "wlr_linux_dmabuf_v1_surface", | ||||
| 	.destroy = surface_addon_destroy, | ||||
| }; | ||||
| 
 | ||||
| static struct wlr_linux_dmabuf_v1_surface *surface_get_or_create( | ||||
| 		struct wlr_linux_dmabuf_v1 *linux_dmabuf, | ||||
| 		struct wlr_surface *wlr_surface) { | ||||
| 	struct wlr_addon *addon = | ||||
| 		wlr_addon_find(&wlr_surface->addons, linux_dmabuf, &surface_addon_impl); | ||||
| 	if (addon != NULL) { | ||||
| 		struct wlr_linux_dmabuf_v1_surface *surface = | ||||
| 			wl_container_of(addon, surface, addon); | ||||
| 		return surface; | ||||
| 	} | ||||
| 
 | ||||
| 	struct wlr_linux_dmabuf_v1_surface *surface = calloc(1, sizeof(*surface)); | ||||
| 	if (surface == NULL) { | ||||
| 		return NULL; | ||||
| 	} | ||||
| 
 | ||||
| 	surface->surface = wlr_surface; | ||||
| 	surface->linux_dmabuf = linux_dmabuf; | ||||
| 	wl_list_init(&surface->feedback_resources); | ||||
| 	wlr_addon_init(&surface->addon, &wlr_surface->addons, linux_dmabuf, | ||||
| 		&surface_addon_impl); | ||||
| 	wl_list_insert(&linux_dmabuf->surfaces, &surface->link); | ||||
| 
 | ||||
| 	return surface; | ||||
| } | ||||
| 
 | ||||
| static const struct wlr_linux_dmabuf_feedback_v1_compiled *surface_get_feedback( | ||||
| 		struct wlr_linux_dmabuf_v1_surface *surface) { | ||||
| 	if (surface->feedback != NULL) { | ||||
| 		return surface->feedback; | ||||
| 	} | ||||
| 	return surface->linux_dmabuf->default_feedback; | ||||
| } | ||||
| 
 | ||||
| static void surface_feedback_handle_resource_destroy(struct wl_resource *resource) { | ||||
| 	wl_list_remove(wl_resource_get_link(resource)); | ||||
| } | ||||
| 
 | ||||
| static void linux_dmabuf_get_surface_feedback(struct wl_client *client, | ||||
| 		struct wl_resource *resource, uint32_t id, | ||||
| 		struct wl_resource *surface_resource) { | ||||
| 	// TODO: implement per-surface feedback
 | ||||
| 	linux_dmabuf_get_default_feedback(client, resource, id); | ||||
| 	struct wlr_linux_dmabuf_v1 *linux_dmabuf = | ||||
| 		linux_dmabuf_from_resource(resource); | ||||
| 	struct wlr_surface *wlr_surface = wlr_surface_from_resource(surface_resource); | ||||
| 
 | ||||
| 	struct wlr_linux_dmabuf_v1_surface *surface = | ||||
| 		surface_get_or_create(linux_dmabuf, wlr_surface); | ||||
| 	if (surface == NULL) { | ||||
| 		wl_client_post_no_memory(client); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	uint32_t version = wl_resource_get_version(resource); | ||||
| 	struct wl_resource *feedback_resource = wl_resource_create(client, | ||||
| 		&zwp_linux_dmabuf_feedback_v1_interface, version, id); | ||||
| 	if (feedback_resource == NULL) { | ||||
| 		wl_client_post_no_memory(client); | ||||
| 		return; | ||||
| 	} | ||||
| 	wl_resource_set_implementation(feedback_resource, &linux_dmabuf_feedback_impl, | ||||
| 		NULL, surface_feedback_handle_resource_destroy); | ||||
| 	wl_list_insert(&surface->feedback_resources, wl_resource_get_link(feedback_resource)); | ||||
| 
 | ||||
| 	feedback_send(surface_get_feedback(surface), feedback_resource); | ||||
| } | ||||
| 
 | ||||
| static void linux_dmabuf_destroy(struct wl_client *client, | ||||
|  | @ -736,6 +912,11 @@ static void linux_dmabuf_bind(struct wl_client *client, void *data, | |||
| static void linux_dmabuf_v1_destroy(struct wlr_linux_dmabuf_v1 *linux_dmabuf) { | ||||
| 	wlr_signal_emit_safe(&linux_dmabuf->events.destroy, linux_dmabuf); | ||||
| 
 | ||||
| 	struct wlr_linux_dmabuf_v1_surface *surface, *surface_tmp; | ||||
| 	wl_list_for_each_safe(surface, surface_tmp, &linux_dmabuf->surfaces, link) { | ||||
| 		surface_destroy(surface); | ||||
| 	} | ||||
| 
 | ||||
| 	compiled_feedback_destroy(linux_dmabuf->default_feedback); | ||||
| 
 | ||||
| 	wl_list_remove(&linux_dmabuf->display_destroy.link); | ||||
|  | @ -767,6 +948,7 @@ struct wlr_linux_dmabuf_v1 *wlr_linux_dmabuf_v1_create(struct wl_display *displa | |||
| 	} | ||||
| 	linux_dmabuf->renderer = renderer; | ||||
| 
 | ||||
| 	wl_list_init(&linux_dmabuf->surfaces); | ||||
| 	wl_signal_init(&linux_dmabuf->events.destroy); | ||||
| 
 | ||||
| 	linux_dmabuf->global = | ||||
|  | @ -794,3 +976,32 @@ struct wlr_linux_dmabuf_v1 *wlr_linux_dmabuf_v1_create(struct wl_display *displa | |||
| 
 | ||||
| 	return linux_dmabuf; | ||||
| } | ||||
| 
 | ||||
| bool wlr_linux_dmabuf_v1_set_surface_feedback( | ||||
| 		struct wlr_linux_dmabuf_v1 *linux_dmabuf, | ||||
| 		struct wlr_surface *wlr_surface, | ||||
| 		const struct wlr_linux_dmabuf_feedback_v1 *feedback) { | ||||
| 	struct wlr_linux_dmabuf_v1_surface *surface = | ||||
| 		surface_get_or_create(linux_dmabuf, wlr_surface); | ||||
| 	if (surface == NULL) { | ||||
| 		return false; | ||||
| 	} | ||||
| 
 | ||||
| 	struct wlr_linux_dmabuf_feedback_v1_compiled *compiled = NULL; | ||||
| 	if (feedback != NULL) { | ||||
| 		compiled = feedback_compile(feedback); | ||||
| 		if (compiled == NULL) { | ||||
| 			return false; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	compiled_feedback_destroy(surface->feedback); | ||||
| 	surface->feedback = compiled; | ||||
| 
 | ||||
| 	struct wl_resource *resource; | ||||
| 	wl_resource_for_each(resource, &surface->feedback_resources) { | ||||
| 		feedback_send(surface_get_feedback(surface), resource); | ||||
| 	} | ||||
| 
 | ||||
| 	return true; | ||||
| } | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue