diff options
author | Daniel Stone <daniels@collabora.com> | 2022-05-11 19:47:52 +0100 |
---|---|---|
committer | Marge Bot <emma+marge@anholt.net> | 2023-05-03 16:09:10 +0000 |
commit | 43f868d17572b428e1f54763d794e05a4d45b070 (patch) | |
tree | 7212e9e0a8f6ce09a532e63b55c99395d797d7c0 | |
parent | 47cd010931a48fcdf259ffcd455ca065f180adc3 (diff) | |
download | mesa-43f868d17572b428e1f54763d794e05a4d45b070.tar.gz |
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>
-rw-r--r-- | src/egl/wayland/wayland-drm/meson.build | 1 | ||||
-rw-r--r-- | src/vulkan/wsi/meson.build | 1 | ||||
-rw-r--r-- | src/vulkan/wsi/wsi_common_wayland.c | 290 |
3 files changed, 292 insertions, 0 deletions
diff --git a/src/egl/wayland/wayland-drm/meson.build b/src/egl/wayland/wayland-drm/meson.build index 7bb9237e06e..6ab5b9260ff 100644 --- a/src/egl/wayland/wayland-drm/meson.build +++ b/src/egl/wayland/wayland-drm/meson.build @@ -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 diff --git a/src/vulkan/wsi/meson.build b/src/vulkan/wsi/meson.build index 08414a386da..163fb22bf11 100644 --- a/src/vulkan/wsi/meson.build +++ b/src/vulkan/wsi/meson.build @@ -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 diff --git a/src/vulkan/wsi/wsi_common_wayland.c b/src/vulkan/wsi/wsi_common_wayland.c index 154d1278c7a..1ca62da7a79 100644 --- a/src/vulkan/wsi/wsi_common_wayland.c +++ b/src/vulkan/wsi/wsi_common_wayland.c @@ -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) @@ -1561,6 +1590,173 @@ wsi_wl_swapchain_set_present_mode(struct wsi_swapchain *wsi_chain, } 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, uint32_t *image_index) @@ -1637,6 +1833,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) { struct wsi_wl_swapchain *chain = data; @@ -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++) { |