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 <contact@emersion.fr>
Signed-off-by: Leandro Ribeiro <leandro.ribeiro@collabora.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/12226>
This commit is contained in:
Leandro Ribeiro
2022-10-13 18:16:37 -03:00
committed by Marge Bot
parent edd3c00d5d
commit df77641993
+197 -3
View File
@@ -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);