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:
committed by
Marge Bot
parent
edd3c00d5d
commit
df77641993
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user