mirror of
https://gitlab.freedesktop.org/mesa/mesa.git
synced 2026-05-08 22:08:26 +02:00
wsi/wayland: Support VK_KHR_present_wait
Use the wp_presentation extension to detect when a given presentation has taken effect. Since this protocol is not guaranteed to be supported, it must be enabled through driconf (vk_khr_present_wait=true) for the time being. Signed-off-by: Daniel Stone <daniels@collabora.com> Signed-off-by: Hans-Kristian Arntzen <post@arntzen-software.no> Co-authored-by: Hans-Kristian Arntzen <post@arntzen-software.no> Reviewed-by: Joshua Ashton <joshua@froggi.es> Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/22682>
This commit is contained in:
parent
47cd010931
commit
43f868d175
3 changed files with 292 additions and 0 deletions
|
|
@ -60,6 +60,7 @@ libwayland_drm = static_library(
|
|||
wp_dir = dep_wl_protocols.get_variable(pkgconfig : 'pkgdatadir')
|
||||
wp_protos = {
|
||||
'linux-dmabuf-unstable-v1': 'unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml',
|
||||
'presentation-time': 'stable/presentation-time/presentation-time.xml',
|
||||
}
|
||||
wp_files = {}
|
||||
foreach name, xml : wp_protos
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ endif
|
|||
if with_platform_wayland
|
||||
files_vulkan_wsi += files('wsi_common_wayland.c')
|
||||
files_vulkan_wsi += wp_files['linux-dmabuf-unstable-v1']
|
||||
files_vulkan_wsi += wp_files['presentation-time']
|
||||
endif
|
||||
|
||||
if with_platform_windows
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@
|
|||
#include "wsi_common_entrypoints.h"
|
||||
#include "wsi_common_private.h"
|
||||
#include "linux-dmabuf-unstable-v1-client-protocol.h"
|
||||
#include "presentation-time-client-protocol.h"
|
||||
|
||||
#include <util/compiler.h>
|
||||
#include <util/hash_table.h>
|
||||
|
|
@ -101,6 +102,9 @@ struct wsi_wl_display {
|
|||
|
||||
struct dmabuf_feedback_format_table format_table;
|
||||
|
||||
/* users want per-chain wsi_wl_swapchain->present_ids.wp_presentation */
|
||||
struct wp_presentation *wp_presentation_notwrapped;
|
||||
|
||||
struct wsi_wayland *wsi_wl;
|
||||
|
||||
/* Formats populated by zwp_linux_dmabuf_v1 or wl_shm interfaces */
|
||||
|
|
@ -168,6 +172,16 @@ struct wsi_wl_swapchain {
|
|||
VkPresentModeKHR present_mode;
|
||||
bool fifo_ready;
|
||||
|
||||
struct {
|
||||
pthread_mutex_t lock; /* protects all members */
|
||||
uint64_t max_completed;
|
||||
struct wl_list outstanding_list;
|
||||
pthread_cond_t list_advanced;
|
||||
struct wl_event_queue *queue;
|
||||
struct wp_presentation *wp_presentation;
|
||||
bool dispatch_in_progress;
|
||||
} present_ids;
|
||||
|
||||
struct wsi_wl_image images[0];
|
||||
};
|
||||
VK_DEFINE_NONDISP_HANDLE_CASTS(wsi_wl_swapchain, base.base, VkSwapchainKHR,
|
||||
|
|
@ -748,6 +762,11 @@ registry_handle_global(void *data, struct wl_registry *registry,
|
|||
zwp_linux_dmabuf_v1_add_listener(display->wl_dmabuf,
|
||||
&dmabuf_listener, display);
|
||||
}
|
||||
|
||||
if (strcmp(interface, wp_presentation_interface.name) == 0) {
|
||||
display->wp_presentation_notwrapped =
|
||||
wl_registry_bind(registry, name, &wp_presentation_interface, 1);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -771,6 +790,8 @@ wsi_wl_display_finish(struct wsi_wl_display *display)
|
|||
wl_shm_destroy(display->wl_shm);
|
||||
if (display->wl_dmabuf)
|
||||
zwp_linux_dmabuf_v1_destroy(display->wl_dmabuf);
|
||||
if (display->wp_presentation_notwrapped)
|
||||
wp_presentation_destroy(display->wp_presentation_notwrapped);
|
||||
if (display->wl_display_wrapper)
|
||||
wl_proxy_wrapper_destroy(display->wl_display_wrapper);
|
||||
if (display->queue)
|
||||
|
|
@ -1531,6 +1552,14 @@ wsi_CreateWaylandSurfaceKHR(VkInstance _instance,
|
|||
return VK_SUCCESS;
|
||||
}
|
||||
|
||||
struct wsi_wl_present_id {
|
||||
struct wp_presentation_feedback *feedback;
|
||||
uint64_t present_id;
|
||||
const VkAllocationCallbacks *alloc;
|
||||
struct wsi_wl_swapchain *chain;
|
||||
struct wl_list link;
|
||||
};
|
||||
|
||||
static struct wsi_image *
|
||||
wsi_wl_swapchain_get_wsi_image(struct wsi_swapchain *wsi_chain,
|
||||
uint32_t image_index)
|
||||
|
|
@ -1560,6 +1589,173 @@ wsi_wl_swapchain_set_present_mode(struct wsi_swapchain *wsi_chain,
|
|||
chain->base.present_mode = mode;
|
||||
}
|
||||
|
||||
static VkResult
|
||||
wsi_wl_swapchain_wait_for_present(struct wsi_swapchain *wsi_chain,
|
||||
uint64_t present_id,
|
||||
uint64_t timeout)
|
||||
{
|
||||
struct wsi_wl_swapchain *chain = (struct wsi_wl_swapchain *)wsi_chain;
|
||||
struct wl_display *wl_display = chain->wsi_wl_surface->display->wl_display;
|
||||
struct timespec start_time, end_time;
|
||||
struct timespec rel_timeout;
|
||||
int wl_fd = wl_display_get_fd(wl_display);
|
||||
VkResult ret;
|
||||
int err;
|
||||
|
||||
timespec_from_nsec(&rel_timeout, timeout);
|
||||
|
||||
/* pthread_mutex_timedlock uses CLOCK_REALTIME, most of the time;
|
||||
* there is no way to check if it uses this or the clock that time()
|
||||
* uses (?!), so we just guess. */
|
||||
clock_gettime(CLOCK_REALTIME, &start_time);
|
||||
timespec_add(&end_time, &rel_timeout, &start_time);
|
||||
|
||||
/* Need to observe that the swapchain semaphore has been unsignalled,
|
||||
* as this is guaranteed when a present is complete. */
|
||||
VkResult result = wsi_swapchain_wait_for_present_semaphore(
|
||||
&chain->base, present_id, timeout);
|
||||
if (result != VK_SUCCESS)
|
||||
return result;
|
||||
|
||||
if (!chain->present_ids.wp_presentation) {
|
||||
/* If we're enabling present wait despite the protocol not being supported,
|
||||
* use best effort not to crash, even if result will not be correct.
|
||||
* For correctness, we must at least wait for the timeline semaphore to complete. */
|
||||
return VK_SUCCESS;
|
||||
}
|
||||
|
||||
err = pthread_mutex_timedlock(&chain->present_ids.lock, &end_time);
|
||||
if (err == ETIMEDOUT)
|
||||
return VK_TIMEOUT;
|
||||
else if (err != 0)
|
||||
return VK_ERROR_OUT_OF_DATE_KHR;
|
||||
|
||||
if (chain->present_ids.max_completed >= present_id) {
|
||||
pthread_mutex_unlock(&chain->present_ids.lock);
|
||||
return VK_SUCCESS;
|
||||
}
|
||||
|
||||
/* Someone else is dispatching events; wait for them to update the chain
|
||||
* status and wake us up. */
|
||||
while (chain->present_ids.dispatch_in_progress) {
|
||||
/* We only own the lock when the wait succeeds. */
|
||||
err = pthread_cond_timedwait(&chain->present_ids.list_advanced,
|
||||
&chain->present_ids.lock, &end_time);
|
||||
|
||||
if (err == ETIMEDOUT) {
|
||||
pthread_mutex_unlock(&chain->present_ids.lock);
|
||||
return VK_TIMEOUT;
|
||||
} else if (err != 0) {
|
||||
pthread_mutex_unlock(&chain->present_ids.lock);
|
||||
return VK_ERROR_OUT_OF_DATE_KHR;
|
||||
}
|
||||
|
||||
if (chain->present_ids.max_completed >= present_id) {
|
||||
pthread_mutex_unlock(&chain->present_ids.lock);
|
||||
return VK_SUCCESS;
|
||||
}
|
||||
|
||||
/* Whoever was previously dispatching the events isn't anymore, so we
|
||||
* will take over and fall through below. */
|
||||
if (!chain->present_ids.dispatch_in_progress)
|
||||
break;
|
||||
}
|
||||
|
||||
assert(!chain->present_ids.dispatch_in_progress);
|
||||
chain->present_ids.dispatch_in_progress = true;
|
||||
|
||||
/* Whether or not we were dispatching the events before, we are now: pull
|
||||
* all the new events from our event queue, post them, and wake up everyone
|
||||
* else who might be waiting. */
|
||||
while (1) {
|
||||
ret = wl_display_dispatch_queue_pending(wl_display, chain->present_ids.queue);
|
||||
if (ret < 0) {
|
||||
ret = VK_ERROR_OUT_OF_DATE_KHR;
|
||||
goto relinquish_dispatch;
|
||||
}
|
||||
|
||||
/* Some events dispatched: check the new completions. */
|
||||
if (ret > 0) {
|
||||
/* Completed our own present; stop our own dispatching and let
|
||||
* someone else pick it up. */
|
||||
if (chain->present_ids.max_completed >= present_id) {
|
||||
ret = VK_SUCCESS;
|
||||
goto relinquish_dispatch;
|
||||
}
|
||||
|
||||
/* Wake up other waiters who may have been unblocked by the events
|
||||
* we just read. */
|
||||
pthread_cond_broadcast(&chain->present_ids.list_advanced);
|
||||
}
|
||||
|
||||
/* Check for timeout, and relinquish the dispatch to another thread
|
||||
* if we're over our budget. */
|
||||
struct timespec current_time;
|
||||
clock_gettime(CLOCK_REALTIME, ¤t_time);
|
||||
if (timespec_after(¤t_time, &end_time)) {
|
||||
ret = VK_TIMEOUT;
|
||||
goto relinquish_dispatch;
|
||||
}
|
||||
|
||||
/* To poll and read from WL fd safely, we must be cooperative.
|
||||
* See wl_display_prepare_read_queue in https://wayland.freedesktop.org/docs/html/apb.html */
|
||||
|
||||
/* Try to read events from the server. */
|
||||
ret = wl_display_prepare_read_queue(wl_display, chain->present_ids.queue);
|
||||
if (ret < 0) {
|
||||
/* Another thread might have read events for our queue already. Go
|
||||
* back to dispatch them.
|
||||
*/
|
||||
if (errno == EAGAIN)
|
||||
continue;
|
||||
ret = VK_ERROR_OUT_OF_DATE_KHR;
|
||||
goto relinquish_dispatch;
|
||||
}
|
||||
|
||||
/* Drop the lock around poll, so people can wait whilst we sleep. */
|
||||
pthread_mutex_unlock(&chain->present_ids.lock);
|
||||
|
||||
struct pollfd pollfd = {
|
||||
.fd = wl_fd,
|
||||
.events = POLLIN
|
||||
};
|
||||
timespec_sub(&rel_timeout, &end_time, ¤t_time);
|
||||
ret = ppoll(&pollfd, 1, &rel_timeout, NULL);
|
||||
|
||||
/* Re-lock after poll; either we're dispatching events under the lock or
|
||||
* bouncing out from an error also under the lock. We can't use timedlock
|
||||
* here because we need to acquire to clear dispatch_in_progress. */
|
||||
pthread_mutex_lock(&chain->present_ids.lock);
|
||||
|
||||
if (ret <= 0) {
|
||||
int lerrno = errno;
|
||||
wl_display_cancel_read(wl_display);
|
||||
if (ret < 0) {
|
||||
/* If ppoll() was interrupted, try again. */
|
||||
if (lerrno == EINTR || lerrno == EAGAIN)
|
||||
continue;
|
||||
ret = VK_ERROR_OUT_OF_DATE_KHR;
|
||||
goto relinquish_dispatch;
|
||||
}
|
||||
assert(ret == 0);
|
||||
continue;
|
||||
}
|
||||
|
||||
ret = wl_display_read_events(wl_display);
|
||||
if (ret < 0) {
|
||||
ret = VK_ERROR_OUT_OF_DATE_KHR;
|
||||
goto relinquish_dispatch;
|
||||
}
|
||||
}
|
||||
|
||||
relinquish_dispatch:
|
||||
assert(chain->present_ids.dispatch_in_progress);
|
||||
chain->present_ids.dispatch_in_progress = false;
|
||||
pthread_cond_broadcast(&chain->present_ids.list_advanced);
|
||||
pthread_mutex_unlock(&chain->present_ids.lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static VkResult
|
||||
wsi_wl_swapchain_acquire_next_image(struct wsi_swapchain *wsi_chain,
|
||||
const VkAcquireNextImageInfoKHR *info,
|
||||
|
|
@ -1636,6 +1832,54 @@ wsi_wl_swapchain_acquire_next_image(struct wsi_swapchain *wsi_chain,
|
|||
}
|
||||
}
|
||||
|
||||
static void
|
||||
presentation_handle_sync_output(void *data,
|
||||
struct wp_presentation_feedback *feedback,
|
||||
struct wl_output *output)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
presentation_handle_presented(void *data,
|
||||
struct wp_presentation_feedback *feedback,
|
||||
uint32_t tv_sec_hi, uint32_t tv_sec_lo,
|
||||
uint32_t tv_nsec, uint32_t refresh,
|
||||
uint32_t seq_hi, uint32_t seq_lo,
|
||||
uint32_t flags)
|
||||
{
|
||||
struct wsi_wl_present_id *id = data;
|
||||
|
||||
/* present_ids.lock already held around dispatch */
|
||||
if (id->present_id > id->chain->present_ids.max_completed)
|
||||
id->chain->present_ids.max_completed = id->present_id;
|
||||
|
||||
wp_presentation_feedback_destroy(feedback);
|
||||
wl_list_remove(&id->link);
|
||||
vk_free(id->alloc, id);
|
||||
}
|
||||
|
||||
static void
|
||||
presentation_handle_discarded(void *data,
|
||||
struct wp_presentation_feedback *feedback)
|
||||
{
|
||||
struct wsi_wl_present_id *id = data;
|
||||
|
||||
/* present_ids.lock already held around dispatch */
|
||||
if (id->present_id > id->chain->present_ids.max_completed)
|
||||
id->chain->present_ids.max_completed = id->present_id;
|
||||
|
||||
wp_presentation_feedback_destroy(feedback);
|
||||
wl_list_remove(&id->link);
|
||||
vk_free(id->alloc, id);
|
||||
}
|
||||
|
||||
static const struct wp_presentation_feedback_listener
|
||||
pres_feedback_listener = {
|
||||
presentation_handle_sync_output,
|
||||
presentation_handle_presented,
|
||||
presentation_handle_discarded,
|
||||
};
|
||||
|
||||
static void
|
||||
frame_handle_done(void *data, struct wl_callback *callback, uint32_t serial)
|
||||
{
|
||||
|
|
@ -1700,6 +1944,24 @@ wsi_wl_swapchain_queue_present(struct wsi_swapchain *wsi_chain,
|
|||
chain->fifo_ready = true;
|
||||
}
|
||||
|
||||
if (present_id > 0 && chain->present_ids.wp_presentation) {
|
||||
struct wsi_wl_present_id *id =
|
||||
vk_zalloc(chain->wsi_wl_surface->display->wsi_wl->alloc, sizeof(*id), sizeof(uintptr_t),
|
||||
VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
|
||||
id->chain = chain;
|
||||
id->present_id = present_id;
|
||||
id->alloc = chain->wsi_wl_surface->display->wsi_wl->alloc;
|
||||
|
||||
pthread_mutex_lock(&chain->present_ids.lock);
|
||||
id->feedback = wp_presentation_feedback(chain->present_ids.wp_presentation,
|
||||
chain->wsi_wl_surface->surface);
|
||||
wp_presentation_feedback_add_listener(id->feedback,
|
||||
&pres_feedback_listener,
|
||||
id);
|
||||
wl_list_insert(&chain->present_ids.outstanding_list, &id->link);
|
||||
pthread_mutex_unlock(&chain->present_ids.lock);
|
||||
}
|
||||
|
||||
chain->images[image_index].busy = true;
|
||||
wl_surface_commit(wsi_wl_surface->surface);
|
||||
wl_display_flush(wsi_wl_surface->display->wl_display);
|
||||
|
|
@ -1849,6 +2111,14 @@ wsi_wl_swapchain_chain_free(struct wsi_wl_swapchain *chain,
|
|||
if (chain->wsi_wl_surface)
|
||||
chain->wsi_wl_surface->chain = NULL;
|
||||
|
||||
if (chain->present_ids.wp_presentation) {
|
||||
assert(!chain->present_ids.dispatch_in_progress);
|
||||
assert(wl_list_empty(&chain->present_ids.outstanding_list));
|
||||
wl_proxy_wrapper_destroy(chain->present_ids.wp_presentation);
|
||||
pthread_cond_destroy(&chain->present_ids.list_advanced);
|
||||
pthread_mutex_destroy(&chain->present_ids.lock);
|
||||
}
|
||||
|
||||
wsi_swapchain_finish(&chain->base);
|
||||
|
||||
vk_free(pAllocator, chain);
|
||||
|
|
@ -1975,6 +2245,7 @@ wsi_wl_surface_create_swapchain(VkIcdSurfaceBase *icd_surface,
|
|||
chain->base.queue_present = wsi_wl_swapchain_queue_present;
|
||||
chain->base.release_images = wsi_wl_swapchain_release_images;
|
||||
chain->base.set_present_mode = wsi_wl_swapchain_set_present_mode;
|
||||
chain->base.wait_for_present = wsi_wl_swapchain_wait_for_present;
|
||||
chain->base.present_mode = wsi_swapchain_get_present_mode(wsi_device, pCreateInfo);
|
||||
chain->base.image_count = num_images;
|
||||
chain->extent = pCreateInfo->imageExtent;
|
||||
|
|
@ -1987,6 +2258,25 @@ wsi_wl_surface_create_swapchain(VkIcdSurfaceBase *icd_surface,
|
|||
}
|
||||
chain->num_drm_modifiers = num_drm_modifiers;
|
||||
chain->drm_modifiers = drm_modifiers;
|
||||
|
||||
if (chain->wsi_wl_surface->display->wp_presentation_notwrapped) {
|
||||
pthread_mutex_init(&chain->present_ids.lock, NULL);
|
||||
|
||||
pthread_condattr_t condattr;
|
||||
pthread_condattr_init(&condattr);
|
||||
pthread_condattr_setclock(&condattr, CLOCK_REALTIME);
|
||||
pthread_cond_init(&chain->present_ids.list_advanced, &condattr);
|
||||
pthread_condattr_destroy(&condattr);
|
||||
|
||||
wl_list_init(&chain->present_ids.outstanding_list);
|
||||
chain->present_ids.queue =
|
||||
wl_display_create_queue(chain->wsi_wl_surface->display->wl_display);
|
||||
chain->present_ids.wp_presentation =
|
||||
wl_proxy_create_wrapper(chain->wsi_wl_surface->display->wp_presentation_notwrapped);
|
||||
wl_proxy_set_queue((struct wl_proxy *) chain->present_ids.wp_presentation,
|
||||
chain->present_ids.queue);
|
||||
}
|
||||
|
||||
chain->fifo_ready = true;
|
||||
|
||||
for (uint32_t i = 0; i < chain->base.image_count; i++) {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue