From df776419934bf7bd2e245a17118190b9d98df89b Mon Sep 17 00:00:00 2001 From: Leandro Ribeiro Date: Thu, 13 Oct 2022 18:16:37 -0300 Subject: [PATCH] vulkan/wsi/wayland: add per-surface dma-buf feedback support Follow up of "vulkan/wsi/wayland: add default dma-buf feedback support". The surface feedback is dynamic, differently from the default feedback. When we receive per-surface feedback, that means that we could be using a better DRM format/modifier pair for the chain's buffers. So the next time that the client calls vkAcquireNextImageKHR(), we return VK_SUBOPTIMAL_KHR instead of VK_SUCCESS. Some clients will re-create the swapchain when receiving SUBOPTIMAL, and for those we'll re-create the chain. An optimal DRM format/modifier pair from the per-surface feedback will be used to create the images of this swapchain. Note that for now we won't be able to change the DRM format, only the modifier. That's good enough for many cases in which direct scanout would not be possible because of the modifier. In order to be able to switch the format, we'll need a mechanism to negotiate preferred formats with clients. Currently we can only expose a set of supported formats all with the same preference. Reviewed-by: Simon Ser Signed-off-by: Leandro Ribeiro Part-of: --- src/vulkan/wsi/wsi_common_wayland.c | 200 +++++++++++++++++++++++++++- 1 file changed, 197 insertions(+), 3 deletions(-) diff --git a/src/vulkan/wsi/wsi_common_wayland.c b/src/vulkan/wsi/wsi_common_wayland.c index 41476e0ac26..5ae797514f1 100644 --- a/src/vulkan/wsi/wsi_common_wayland.c +++ b/src/vulkan/wsi/wsi_common_wayland.c @@ -130,6 +130,9 @@ struct wsi_wl_surface { struct wsi_wl_swapchain *chain; struct wl_surface *surface; struct wsi_wl_display *display; + + struct zwp_linux_dmabuf_feedback_v1 *wl_dmabuf_feedback; + struct dmabuf_feedback dmabuf_feedback, pending_dmabuf_feedback; }; struct wsi_wl_swapchain { @@ -145,6 +148,8 @@ struct wsi_wl_swapchain { uint32_t drm_format; enum wl_shm_format shm_format; + bool suboptimal; + uint32_t num_drm_modifiers; const uint64_t *drm_modifiers; @@ -1122,9 +1127,178 @@ wsi_wl_surface_destroy(VkIcdSurfaceBase *icd_surface, VkInstance _instance, if (wsi_wl_surface->display) wsi_wl_display_destroy(wsi_wl_surface->display); + if (wsi_wl_surface->wl_dmabuf_feedback) { + zwp_linux_dmabuf_feedback_v1_destroy(wsi_wl_surface->wl_dmabuf_feedback); + dmabuf_feedback_fini(&wsi_wl_surface->dmabuf_feedback); + dmabuf_feedback_fini(&wsi_wl_surface->pending_dmabuf_feedback); + } + vk_free2(&instance->alloc, pAllocator, wsi_wl_surface); } +static struct wsi_wl_format * +pick_format_from_surface_dmabuf_feedback(struct wsi_wl_surface *wsi_wl_surface, + VkFormat vk_format) +{ + struct wsi_wl_format *f = NULL; + + /* If the main_device was not advertised, we don't have valid feedback */ + if (wsi_wl_surface->dmabuf_feedback.main_device == 0) + return NULL; + + util_dynarray_foreach(&wsi_wl_surface->dmabuf_feedback.tranches, + struct dmabuf_feedback_tranche, tranche) { + f = find_format(&tranche->formats, vk_format); + if (f) + break; + } + + return f; +} + +static void +surface_dmabuf_feedback_format_table(void *data, + struct zwp_linux_dmabuf_feedback_v1 *zwp_linux_dmabuf_feedback_v1, + int32_t fd, uint32_t size) +{ + struct wsi_wl_surface *wsi_wl_surface = data; + struct dmabuf_feedback *feedback = &wsi_wl_surface->pending_dmabuf_feedback; + + feedback->format_table.size = size; + feedback->format_table.data = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); + + close(fd); +} + +static void +surface_dmabuf_feedback_main_device(void *data, + struct zwp_linux_dmabuf_feedback_v1 *dmabuf_feedback, + struct wl_array *device) +{ + struct wsi_wl_surface *wsi_wl_surface = data; + struct dmabuf_feedback *feedback = &wsi_wl_surface->pending_dmabuf_feedback; + + memcpy(&feedback->main_device, device->data, sizeof(feedback->main_device)); +} + +static void +surface_dmabuf_feedback_tranche_target_device(void *data, + struct zwp_linux_dmabuf_feedback_v1 *dmabuf_feedback, + struct wl_array *device) +{ + struct wsi_wl_surface *wsi_wl_surface = data; + struct dmabuf_feedback *feedback = &wsi_wl_surface->pending_dmabuf_feedback; + + memcpy(&feedback->pending_tranche.target_device, device->data, + sizeof(feedback->pending_tranche.target_device)); +} + +static void +surface_dmabuf_feedback_tranche_flags(void *data, + struct zwp_linux_dmabuf_feedback_v1 *dmabuf_feedback, + uint32_t flags) +{ + struct wsi_wl_surface *wsi_wl_surface = data; + struct dmabuf_feedback *feedback = &wsi_wl_surface->pending_dmabuf_feedback; + + feedback->pending_tranche.flags = flags; +} + +static void +surface_dmabuf_feedback_tranche_formats(void *data, + struct zwp_linux_dmabuf_feedback_v1 *dmabuf_feedback, + struct wl_array *indices) +{ + struct wsi_wl_surface *wsi_wl_surface = data; + struct dmabuf_feedback *feedback = &wsi_wl_surface->pending_dmabuf_feedback; + uint32_t format; + uint64_t modifier; + uint16_t *index; + + /* Compositor may advertise or not a format table. If it does, we use it. + * Otherwise, we steal the most recent advertised format table. If we don't have + * a most recent advertised format table, compositor did something wrong. */ + if (feedback->format_table.data == NULL) { + feedback->format_table = wsi_wl_surface->dmabuf_feedback.format_table; + dmabuf_feedback_format_table_init(&wsi_wl_surface->dmabuf_feedback.format_table); + } + if (feedback->format_table.data == MAP_FAILED || + feedback->format_table.data == NULL) + return; + + wl_array_for_each(index, indices) { + format = feedback->format_table.data[*index].format; + modifier = feedback->format_table.data[*index].modifier; + + wsi_wl_display_add_drm_format_modifier(wsi_wl_surface->display, + &wsi_wl_surface->pending_dmabuf_feedback.pending_tranche.formats, + format, modifier); + } +} + +static void +surface_dmabuf_feedback_tranche_done(void *data, + struct zwp_linux_dmabuf_feedback_v1 *dmabuf_feedback) +{ + struct wsi_wl_surface *wsi_wl_surface = data; + struct dmabuf_feedback *feedback = &wsi_wl_surface->pending_dmabuf_feedback; + + /* Add tranche to array of tranches. */ + util_dynarray_append(&feedback->tranches, struct dmabuf_feedback_tranche, + feedback->pending_tranche); + + dmabuf_feedback_tranche_init(&feedback->pending_tranche); +} + +static void +surface_dmabuf_feedback_done(void *data, + struct zwp_linux_dmabuf_feedback_v1 *dmabuf_feedback) +{ + struct wsi_wl_surface *wsi_wl_surface = data; + + dmabuf_feedback_fini(&wsi_wl_surface->dmabuf_feedback); + wsi_wl_surface->dmabuf_feedback = wsi_wl_surface->pending_dmabuf_feedback; + dmabuf_feedback_init(&wsi_wl_surface->pending_dmabuf_feedback); + + wsi_wl_surface->chain->suboptimal = true; +} + +static const struct zwp_linux_dmabuf_feedback_v1_listener +surface_dmabuf_feedback_listener = { + .format_table = surface_dmabuf_feedback_format_table, + .main_device = surface_dmabuf_feedback_main_device, + .tranche_target_device = surface_dmabuf_feedback_tranche_target_device, + .tranche_flags = surface_dmabuf_feedback_tranche_flags, + .tranche_formats = surface_dmabuf_feedback_tranche_formats, + .tranche_done = surface_dmabuf_feedback_tranche_done, + .done = surface_dmabuf_feedback_done, +}; + +static VkResult wsi_wl_surface_bind_to_dmabuf_feedback(struct wsi_wl_surface *wsi_wl_surface) +{ + wsi_wl_surface->wl_dmabuf_feedback = + zwp_linux_dmabuf_v1_get_surface_feedback(wsi_wl_surface->display->wl_dmabuf, + wsi_wl_surface->surface); + + zwp_linux_dmabuf_feedback_v1_add_listener(wsi_wl_surface->wl_dmabuf_feedback, + &surface_dmabuf_feedback_listener, + wsi_wl_surface); + + if (dmabuf_feedback_init(&wsi_wl_surface->dmabuf_feedback) < 0) + goto fail; + if (dmabuf_feedback_init(&wsi_wl_surface->pending_dmabuf_feedback) < 0) + goto fail_pending; + + return VK_SUCCESS; + +fail_pending: + dmabuf_feedback_fini(&wsi_wl_surface->dmabuf_feedback); +fail: + zwp_linux_dmabuf_feedback_v1_destroy(wsi_wl_surface->wl_dmabuf_feedback); + wsi_wl_surface->wl_dmabuf_feedback = NULL; + return VK_ERROR_OUT_OF_HOST_MEMORY; +} + static VkResult wsi_wl_surface_init(struct wsi_wl_surface *wsi_wl_surface, struct wsi_device *wsi_device) { @@ -1149,6 +1323,18 @@ static VkResult wsi_wl_surface_init(struct wsi_wl_surface *wsi_wl_surface, wl_proxy_set_queue((struct wl_proxy *) wsi_wl_surface->surface, wsi_wl_surface->display->queue); + /* Bind wsi_wl_surface to dma-buf feedback. */ + if (wsi_wl_surface->display->wl_dmabuf && + zwp_linux_dmabuf_v1_get_version(wsi_wl_surface->display->wl_dmabuf) >= + ZWP_LINUX_DMABUF_V1_GET_SURFACE_FEEDBACK_SINCE_VERSION) { + result = wsi_wl_surface_bind_to_dmabuf_feedback(wsi_wl_surface); + if (result != VK_SUCCESS) + goto fail; + + wl_display_roundtrip_queue(wsi_wl_surface->display->wl_display, + wsi_wl_surface->display->queue); + } + return VK_SUCCESS; fail: @@ -1220,7 +1406,7 @@ wsi_wl_swapchain_acquire_next_image(struct wsi_swapchain *wsi_chain, /* We found a non-busy image */ *image_index = i; chain->images[i].busy = true; - return VK_SUCCESS; + return (chain->suboptimal ? VK_SUBOPTIMAL_KHR : VK_SUCCESS); } } @@ -1563,8 +1749,16 @@ wsi_wl_surface_create_swapchain(VkIcdSurfaceBase *icd_surface, * support them. */ if (wsi_wl_surface->display->wl_dmabuf && wsi_device->supports_modifiers) { - struct wsi_wl_format *f = find_format(&wsi_wl_surface->display->formats, - pCreateInfo->imageFormat); + struct wsi_wl_format *f = NULL; + /* Try to select modifiers for our vk_format from surface dma-buf + * feedback. If that doesn't work, fallback to the list of supported + * formats/modifiers by the display. */ + if (wsi_wl_surface->wl_dmabuf_feedback) + f = pick_format_from_surface_dmabuf_feedback(wsi_wl_surface, + pCreateInfo->imageFormat); + if (f == NULL) + f = find_format(&chain->wsi_wl_surface->display->formats, + pCreateInfo->imageFormat); if (f != NULL) { num_drm_modifiers = u_vector_length(&f->modifiers); drm_modifiers = u_vector_tail(&f->modifiers);