summaryrefslogtreecommitdiff
path: root/src/vulkan
diff options
context:
space:
mode:
authorDaniel Stone <daniels@collabora.com>2022-05-11 19:47:52 +0100
committerMarge Bot <emma+marge@anholt.net>2023-05-03 16:09:10 +0000
commit43f868d17572b428e1f54763d794e05a4d45b070 (patch)
tree7212e9e0a8f6ce09a532e63b55c99395d797d7c0 /src/vulkan
parent47cd010931a48fcdf259ffcd455ca065f180adc3 (diff)
downloadmesa-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>
Diffstat (limited to 'src/vulkan')
-rw-r--r--src/vulkan/wsi/meson.build1
-rw-r--r--src/vulkan/wsi/wsi_common_wayland.c290
2 files changed, 291 insertions, 0 deletions
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, &current_time);
+ if (timespec_after(&current_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, &current_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++) {