bazzite/spec_files/mesa/valve.patch
2023-12-08 15:07:41 -08:00

1544 lines
58 KiB
Diff

From 722c3ec3202ee3c8f7055fc04d40d431d45b05cd Mon Sep 17 00:00:00 2001
From: Derek Foreman <derek.foreman@collabora.com>
Date: Wed, 20 Sep 2023 10:40:33 -0500
Subject: [PATCH 1/8] vulkan/wsi/wayland: refactor wayland dispatch code
We currently have two similar but different bits of code to dispatch
wayland event queues. Pull out as much common code as possible.
Signed-off-by: Derek Foreman <derek.foreman@collabora.com>
---
src/vulkan/wsi/wsi_common_wayland.c | 399 +++++++++++++++-------------
1 file changed, 208 insertions(+), 191 deletions(-)
diff --git a/src/vulkan/wsi/wsi_common_wayland.c b/src/vulkan/wsi/wsi_common_wayland.c
index 8d280340091..cfa1baf6127 100644
--- a/src/vulkan/wsi/wsi_common_wayland.c
+++ b/src/vulkan/wsi/wsi_common_wayland.c
@@ -95,6 +95,11 @@ struct wsi_wl_display {
struct wl_display *wl_display;
/* Actually a proxy wrapper around the event queue */
struct wl_display *wl_display_wrapper;
+
+ pthread_mutex_t wl_fd_lock;
+ pthread_cond_t wl_fd_reader_finished;
+ bool wl_fd_read_in_progress;
+
struct wl_event_queue *queue;
struct wl_shm *wl_shm;
@@ -157,8 +166,12 @@ struct wsi_wl_surface {
struct wsi_wl_swapchain {
struct wsi_swapchain base;
+ struct wl_event_queue *queue;
+
struct wsi_wl_surface *wsi_wl_surface;
struct wp_tearing_control_v1 *tearing_control;
+ struct gamescope_commit_queue_v1 *commit_queue;
+ bool can_timestamp;
struct wl_callback *frame;
@@ -177,10 +184,7 @@ struct wsi_wl_swapchain {
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];
@@ -205,6 +209,135 @@ find_format(struct u_vector *formats, VkFormat format)
return NULL;
}
+static int
+wsi_wl_display_read_queue_with_timeout_internal(struct wsi_wl_display *wsi_wl_display,
+ struct wl_event_queue *queue,
+ uint64_t atimeout)
+{
+ uint64_t current_time_nsec;
+ struct timespec rel_timeout, end_time, current_time;
+ int ret;
+
+ if (wl_display_prepare_read_queue(wsi_wl_display->wl_display, queue) < 0) {
+ /* Another thread might have read events for our queue already. Go
+ * back to dispatch them.
+ */
+ pthread_mutex_unlock(&wsi_wl_display->wl_fd_lock);
+ if (errno == EAGAIN)
+ return VK_SUCCESS;
+
+ return VK_ERROR_OUT_OF_DATE_KHR;
+ }
+
+ wsi_wl_display->wl_fd_read_in_progress = true;
+ pthread_mutex_unlock(&wsi_wl_display->wl_fd_lock);
+
+ while (1) {
+ struct pollfd pollfd = {
+ .fd = wl_display_get_fd(wsi_wl_display->wl_display),
+ .events = POLLIN
+ };
+
+ current_time_nsec = os_time_get_nano();
+ if (current_time_nsec > atimeout) {
+ rel_timeout.tv_sec = 0;
+ rel_timeout.tv_nsec = 0;
+ } else {
+ timespec_from_nsec(&current_time, current_time_nsec);
+ timespec_from_nsec(&end_time, atimeout);
+ timespec_sub(&rel_timeout, &end_time, &current_time);
+ }
+
+ ret = ppoll(&pollfd, 1, &rel_timeout, NULL);
+ if (ret < 0) {
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+
+ ret = VK_ERROR_OUT_OF_DATE_KHR;
+ } else if (ret == 0)
+ ret = VK_TIMEOUT;
+ else
+ ret = VK_SUCCESS;
+
+ break;
+ }
+
+ if (ret != VK_SUCCESS) {
+ wl_display_cancel_read(wsi_wl_display->wl_display);
+ } else {
+ ret = wl_display_read_events(wsi_wl_display->wl_display);
+ if (ret != 0)
+ ret = VK_ERROR_OUT_OF_DATE_KHR;
+ }
+
+ pthread_mutex_lock(&wsi_wl_display->wl_fd_lock);
+ wsi_wl_display->wl_fd_read_in_progress = false;
+ pthread_cond_broadcast(&wsi_wl_display->wl_fd_reader_finished);
+ return ret;
+}
+
+static int
+wsi_wl_display_dispatch_queue_with_timeout(struct wsi_wl_display *wsi_wl_display,
+ struct wl_event_queue *queue,
+ uint64_t timeout)
+{
+ int err;
+ int n_events;
+ uint64_t atimeout, now;
+
+ if (timeout == UINT64_MAX)
+ atimeout = timeout;
+ else
+ atimeout = os_time_get_absolute_timeout(timeout);
+
+ while (1) {
+ n_events = wl_display_dispatch_queue_pending(wsi_wl_display->wl_display,
+ queue);
+ if (n_events > 0) {
+ err = VK_SUCCESS;
+ break;
+ }
+ pthread_mutex_lock(&wsi_wl_display->wl_fd_lock);
+
+ if (wsi_wl_display->wl_fd_read_in_progress) {
+ struct timespec end_time;
+
+ timespec_from_nsec(&end_time, atimeout);
+
+ err = pthread_cond_timedwait(&wsi_wl_display->wl_fd_reader_finished,
+ &wsi_wl_display->wl_fd_lock,
+ &end_time);
+ if (err) {
+ if (errno == ETIMEDOUT)
+ err = VK_TIMEOUT;
+ else
+ err = VK_ERROR_OUT_OF_DATE_KHR;
+ } else {
+ /* We don't know if the other thread actually
+ * dispatched anything, so let the caller decide
+ * whether it should continue.
+ */
+ err = VK_INCOMPLETE;
+ }
+ } else {
+ err = wsi_wl_display_read_queue_with_timeout_internal(wsi_wl_display,
+ queue,
+ timeout);
+ }
+
+ pthread_mutex_unlock(&wsi_wl_display->wl_fd_lock);
+
+ now = os_time_get_nano();
+ if (now > atimeout) {
+ err = VK_TIMEOUT;
+ break;
+ }
+
+ }
+
+ return err;
+}
+
static struct wsi_wl_format *
wsi_wl_display_add_vk_format(struct wsi_wl_display *display,
struct u_vector *formats,
@@ -825,6 +958,8 @@ wsi_wl_display_finish(struct wsi_wl_display *display)
wl_proxy_wrapper_destroy(display->wl_display_wrapper);
if (display->queue)
wl_event_queue_destroy(display->queue);
+ pthread_mutex_destroy(&display->wl_fd_lock);
+ pthread_cond_destroy(&display->wl_fd_reader_finished);
}
static VkResult
@@ -843,6 +978,11 @@ wsi_wl_display_init(struct wsi_wayland *wsi_wl,
display->wl_display = wl_display;
display->sw = sw;
+ display->wl_fd_read_in_progress = false;
+ pthread_mutex_init(&display->wl_fd_lock, NULL);
+ if (!wsi_init_pthread_cond_monotonic(&display->wl_fd_reader_finished))
+ goto fail;
+
display->queue = wl_display_create_queue(wl_display);
if (!display->queue) {
result = VK_ERROR_OUT_OF_HOST_MEMORY;
@@ -943,6 +1083,7 @@ fail_registry:
wl_registry_destroy(registry);
fail:
+ pthread_mutex_destroy(&display->wl_fd_lock);
wsi_wl_display_finish(display);
return result;
}
@@ -1628,19 +1769,15 @@ wsi_wl_swapchain_wait_for_present(struct wsi_swapchain *wsi_chain,
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 end_time;
- int wl_fd = wl_display_get_fd(wl_display);
- VkResult ret;
- int err;
+ uint64_t end_time, time_left, now;
+ int ret;
+ bool expired = false;
+ bool finished;
- uint64_t atimeout;
- if (timeout == 0 || timeout == UINT64_MAX)
- atimeout = timeout;
+ if (timeout == UINT64_MAX)
+ end_time = timeout;
else
- atimeout = os_time_get_absolute_timeout(timeout);
-
- timespec_from_nsec(&end_time, atimeout);
+ end_time = os_time_get_absolute_timeout(timeout);
/* Need to observe that the swapchain semaphore has been unsignalled,
* as this is guaranteed when a present is complete. */
@@ -1656,141 +1793,45 @@ wsi_wl_swapchain_wait_for_present(struct wsi_swapchain *wsi_chain,
return VK_SUCCESS;
}
+ while (1) {
+ ret = wl_display_dispatch_queue_pending(chain->wsi_wl_surface->display->wl_display,
+ chain->queue);
+ if (ret < 0)
+ return VK_ERROR_OUT_OF_DATE_KHR;
+
/* PresentWait can be called concurrently.
* If there is contention on this mutex, it means there is currently a dispatcher in flight holding the lock.
* The lock is only held while there is forward progress processing events from Wayland,
* so there should be no problem locking without timeout.
* We would like to be able to support timeout = 0 to query the current max_completed count.
* A timedlock with no timeout can be problematic in that scenario. */
- err = pthread_mutex_lock(&chain->present_ids.lock);
- if (err != 0)
- return VK_ERROR_OUT_OF_DATE_KHR;
-
- if (chain->present_ids.max_completed >= present_id) {
+ pthread_mutex_lock(&chain->present_ids.lock);
+ finished = 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);
+ if (finished)
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. */
- uint64_t current_time_nsec = os_time_get_nano();
- if (current_time_nsec > atimeout) {
- 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
- };
- struct timespec current_time, rel_timeout;
- timespec_from_nsec(&current_time, current_time_nsec);
- timespec_sub(&rel_timeout, &end_time, &current_time);
- ret = ppoll(&pollfd, 1, &rel_timeout, NULL);
+ if (expired)
+ return VK_TIMEOUT;
- /* 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);
+ now = os_time_get_nano();
+ if (now > end_time)
+ time_left = 0;
+ else
+ time_left = end_time - now;
- 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);
+ ret = wsi_wl_display_dispatch_queue_with_timeout(chain->wsi_wl_surface->display,
+ chain->queue,
+ time_left);
+ if (ret == VK_INCOMPLETE)
continue;
- }
- ret = wl_display_read_events(wl_display);
- if (ret < 0) {
- ret = VK_ERROR_OUT_OF_DATE_KHR;
- goto relinquish_dispatch;
- }
- }
+ if (ret != VK_SUCCESS && ret != VK_TIMEOUT)
+ return ret;
-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;
+ if (time_left == 0)
+ expired = true;
+ }
}
static VkResult
@@ -1800,19 +1841,18 @@ wsi_wl_swapchain_acquire_next_image(struct wsi_swapchain *wsi_chain,
{
struct wsi_wl_swapchain *chain = (struct wsi_wl_swapchain *)wsi_chain;
struct wsi_wl_surface *wsi_wl_surface = chain->wsi_wl_surface;
- struct timespec start_time, end_time;
- struct timespec rel_timeout;
- int wl_fd = wl_display_get_fd(wsi_wl_surface->display->wl_display);
-
- timespec_from_nsec(&rel_timeout, info->timeout);
+ uint64_t end_time, time_left, now;
+ bool expired = false;
+ int ret;
- clock_gettime(CLOCK_MONOTONIC, &start_time);
- timespec_add(&end_time, &rel_timeout, &start_time);
+ if (info->timeout == UINT64_MAX)
+ end_time = info->timeout;
+ else
+ end_time = os_time_get_absolute_timeout(info->timeout);
while (1) {
- /* Try to dispatch potential events. */
- int ret = wl_display_dispatch_queue_pending(wsi_wl_surface->display->wl_display,
- wsi_wl_surface->display->queue);
+ ret = wl_display_dispatch_queue_pending(wsi_wl_surface->display->wl_display,
+ wsi_wl_surface->display->queue);
if (ret < 0)
return VK_ERROR_OUT_OF_DATE_KHR;
@@ -1826,46 +1866,26 @@ wsi_wl_swapchain_acquire_next_image(struct wsi_swapchain *wsi_chain,
}
}
- /* Check for timeout. */
- struct timespec current_time;
- clock_gettime(CLOCK_MONOTONIC, &current_time);
- if (timespec_after(&current_time, &end_time))
- return (info->timeout ? VK_TIMEOUT : VK_NOT_READY);
+ if (expired)
+ return info->timeout ? VK_TIMEOUT : VK_NOT_READY;
- /* Try to read events from the server. */
- ret = wl_display_prepare_read_queue(wsi_wl_surface->display->wl_display,
- wsi_wl_surface->display->queue);
- if (ret < 0) {
- /* Another thread might have read events for our queue already. Go
- * back to dispatch them.
- */
- if (errno == EAGAIN)
- continue;
- return VK_ERROR_OUT_OF_DATE_KHR;
- }
+ now = os_time_get_nano();
+ if (now > end_time)
+ time_left = 0;
+ else
+ time_left = end_time - now;
- struct pollfd pollfd = {
- .fd = wl_fd,
- .events = POLLIN
- };
- timespec_sub(&rel_timeout, &end_time, &current_time);
- ret = ppoll(&pollfd, 1, &rel_timeout, NULL);
- if (ret <= 0) {
- int lerrno = errno;
- wl_display_cancel_read(wsi_wl_surface->display->wl_display);
- if (ret < 0) {
- /* If ppoll() was interrupted, try again. */
- if (lerrno == EINTR || lerrno == EAGAIN)
- continue;
- return VK_ERROR_OUT_OF_DATE_KHR;
- }
- assert(ret == 0);
+ ret = wsi_wl_display_dispatch_queue_with_timeout(wsi_wl_surface->display,
+ wsi_wl_surface->display->queue,
+ time_left);
+ if (ret == VK_ERROR_OUT_OF_DATE_KHR)
+ return ret;
+
+ if (ret == VK_INCOMPLETE)
continue;
- }
- ret = wl_display_read_events(wsi_wl_surface->display->wl_display);
- if (ret < 0)
- return VK_ERROR_OUT_OF_DATE_KHR;
+ if (ret == VK_TIMEOUT)
+ expired = true;
}
}
@@ -1886,9 +1906,10 @@ presentation_handle_presented(void *data,
{
struct wsi_wl_present_id *id = data;
- /* present_ids.lock already held around dispatch */
+ pthread_mutex_lock(&id->chain->present_ids.lock);
if (id->present_id > id->chain->present_ids.max_completed)
id->chain->present_ids.max_completed = id->present_id;
+ pthread_mutex_unlock(&id->chain->present_ids.lock);
wp_presentation_feedback_destroy(feedback);
wl_list_remove(&id->link);
@@ -1901,9 +1922,10 @@ presentation_handle_discarded(void *data,
{
struct wsi_wl_present_id *id = data;
- /* present_ids.lock already held around dispatch */
+ pthread_mutex_lock(&id->chain->present_ids.lock);
if (id->present_id > id->chain->present_ids.max_completed)
id->chain->present_ids.max_completed = id->present_id;
+ pthread_mutex_unlock(&id->chain->present_ids.lock);
wp_presentation_feedback_destroy(feedback);
wl_list_remove(&id->link);
@@ -2149,8 +2171,6 @@ wsi_wl_swapchain_chain_free(struct wsi_wl_swapchain *chain,
chain->wsi_wl_surface->chain = NULL;
if (chain->present_ids.wp_presentation) {
- assert(!chain->present_ids.dispatch_in_progress);
-
/* In VK_EXT_swapchain_maintenance1 there is no requirement to wait for all present IDs to be complete.
* Waiting for the swapchain fence is enough.
* Just clean up anything user did not wait for. */
@@ -2162,7 +2182,6 @@ wsi_wl_swapchain_chain_free(struct wsi_wl_swapchain *chain,
}
wl_proxy_wrapper_destroy(chain->present_ids.wp_presentation);
- pthread_cond_destroy(&chain->present_ids.list_advanced);
pthread_mutex_destroy(&chain->present_ids.lock);
}
@@ -2306,18 +2325,16 @@ wsi_wl_surface_create_swapchain(VkIcdSurfaceBase *icd_surface,
chain->num_drm_modifiers = num_drm_modifiers;
chain->drm_modifiers = drm_modifiers;
+ chain->queue = wl_display_create_queue(chain->wsi_wl_surface->display->wl_display);
+
if (chain->wsi_wl_surface->display->wp_presentation_notwrapped) {
- if (!wsi_init_pthread_cond_monotonic(&chain->present_ids.list_advanced))
- goto fail;
pthread_mutex_init(&chain->present_ids.lock, NULL);
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->queue);
}
chain->fifo_ready = true;
--
2.42.0
From 42c744d99ddd339d617851ac25fe625aa0f65711 Mon Sep 17 00:00:00 2001
From: Derek Foreman <derek.foreman@collabora.com>
Date: Fri, 10 Nov 2023 07:25:35 -0600
Subject: [PATCH 2/8] vulkan/wsi/wayland: Use commit_timing/commit_queue
protocol for FIFO
The commit_timing protocol allows us to set a presentation timestamp,
and the commit_queue protocol allows us to request FIFO semantics for
committed state (instead of the default mailbox).
I these are available, use them to implement Vulkan's FIFO presentation
mode.
Signed-off-by: Derek Foreman <derek.foreman@collabora.com>
---
src/egl/wayland/wayland-drm/meson.build | 2 +
src/vulkan/wsi/meson.build | 2 +
src/vulkan/wsi/wsi_common_wayland.c | 148 ++++++++++++++++++++++--
3 files changed, 140 insertions(+), 12 deletions(-)
diff --git a/src/egl/wayland/wayland-drm/meson.build b/src/egl/wayland/wayland-drm/meson.build
index 6ab5b9260ff..70940cb8771 100644
--- a/src/egl/wayland/wayland-drm/meson.build
+++ b/src/egl/wayland/wayland-drm/meson.build
@@ -59,6 +59,8 @@ libwayland_drm = static_library(
# here for now as the maybe-least-bad solution.
wp_dir = dep_wl_protocols.get_variable(pkgconfig : 'pkgdatadir')
wp_protos = {
+ 'commit-queue-v1': 'staging/commit-queue/commit-queue-v1.xml',
+ 'commit-timing-v1': 'staging/commit-timing/commit-timing-v1.xml',
'linux-dmabuf-unstable-v1': 'unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml',
'presentation-time': 'stable/presentation-time/presentation-time.xml',
}
diff --git a/src/vulkan/wsi/meson.build b/src/vulkan/wsi/meson.build
index dcc7747fcac..70a73805cd4 100644
--- a/src/vulkan/wsi/meson.build
+++ b/src/vulkan/wsi/meson.build
@@ -31,6 +31,8 @@ endif
if with_platform_wayland
files_vulkan_wsi += files('wsi_common_wayland.c')
+ files_vulkan_wsi += wp_files['commit-queue-v1']
+ files_vulkan_wsi += wp_files['commit-timing-v1']
files_vulkan_wsi += wp_files['linux-dmabuf-unstable-v1']
files_vulkan_wsi += wp_files['presentation-time']
endif
diff --git a/src/vulkan/wsi/wsi_common_wayland.c b/src/vulkan/wsi/wsi_common_wayland.c
index cfa1baf6127..d01ff68781e 100644
--- a/src/vulkan/wsi/wsi_common_wayland.c
+++ b/src/vulkan/wsi/wsi_common_wayland.c
@@ -41,6 +41,8 @@
#include "vk_util.h"
#include "wsi_common_entrypoints.h"
#include "wsi_common_private.h"
+#include "commit-queue-v1-client-protocol.h"
+#include "commit-timing-v1-client-protocol.h"
#include "linux-dmabuf-unstable-v1-client-protocol.h"
#include "presentation-time-client-protocol.h"
@@ -111,6 +113,9 @@ struct wsi_wl_display {
/* users want per-chain wsi_wl_swapchain->present_ids.wp_presentation */
struct wp_presentation *wp_presentation_notwrapped;
+ struct wp_commit_queue_manager_v1 *commit_queue_manager;
+ struct wp_commit_timing_manager_v1 *commit_timing_manager;
+
struct wsi_wayland *wsi_wl;
/* Formats populated by zwp_linux_dmabuf_v1 or wl_shm interfaces */
@@ -133,6 +138,7 @@ struct wsi_wayland {
struct wsi_wl_image {
struct wsi_image base;
+ struct wsi_wl_swapchain *chain;
struct wl_buffer *buffer;
bool busy;
int shm_fd;
@@ -178,13 +188,17 @@ struct wsi_wl_swapchain {
const uint64_t *drm_modifiers;
VkPresentModeKHR present_mode;
- bool fifo_ready;
+ bool legacy_fifo_ready;
+
+ uint64_t last_target_time;
struct {
pthread_mutex_t lock; /* protects all members */
uint64_t max_completed;
struct wl_list outstanding_list;
struct wp_presentation *wp_presentation;
+ uint64_t phase_time;
+ unsigned int refresh_nsec;
} present_ids;
struct wsi_wl_image images[0];
@@ -801,6 +944,9 @@ registry_handle_global(void *data, struct wl_registry *registry,
} else if (strcmp(interface, wp_tearing_control_manager_v1_interface.name) == 0) {
display->tearing_control_manager =
wl_registry_bind(registry, name, &wp_tearing_control_manager_v1_interface, 1);
+ } else if (strcmp(interface, gamescope_commit_queue_manager_v1_interface.name) == 0) {
+ display->commit_queue_manager =
+ wl_registry_bind(registry, name, &gamescope_commit_queue_manager_v1_interface, 1);
}
}
@@ -973,6 +973,8 @@ wsi_wl_display_finish(struct wsi_wl_display *display)
zwp_linux_dmabuf_v1_destroy(display->wl_dmabuf);
if (display->wp_presentation_notwrapped)
wp_presentation_destroy(display->wp_presentation_notwrapped);
+ if (display->commit_queue_manager)
+ gamescope_commit_queue_manager_v1_destroy(display->commit_queue_manager);
if (display->tearing_control_manager)
wp_tearing_control_manager_v1_destroy(display->tearing_control_manager);
if (display->wl_display_wrapper)
@@ -1875,6 +1904,16 @@ wsi_wl_swapchain_acquire_next_image(struct wsi_swapchain *wsi_chain,
else
time_left = end_time - now;
+ /* If we can use timestamps, we want to make sure to dispatch the queue
+ * feedback events are in so we can get a refresh rate and a vsync time to
+ * phase lock to */
+ if (chain->can_timestamp) {
+ ret = wl_display_dispatch_queue_pending(wsi_wl_surface->display->wl_display,
+ chain->queue);
+ if (ret < 0)
+ return VK_ERROR_OUT_OF_DATE_KHR;
+ }
+
ret = wsi_wl_display_dispatch_queue_with_timeout(wsi_wl_surface->display,
wsi_wl_surface->display->queue,
time_left);
@@ -1905,10 +1944,16 @@ presentation_handle_presented(void *data,
uint32_t flags)
{
struct wsi_wl_present_id *id = data;
+ struct timespec presentation_time;
pthread_mutex_lock(&id->chain->present_ids.lock);
if (id->present_id > id->chain->present_ids.max_completed)
id->chain->present_ids.max_completed = id->present_id;
+
+ presentation_time.tv_sec = ((uint64_t)tv_sec_hi << 32) + tv_sec_lo;
+ presentation_time.tv_nsec = tv_nsec;
+ id->chain->present_ids.phase_time = timespec_to_nsec(&presentation_time);
+ id->chain->present_ids.refresh_nsec = refresh;
pthread_mutex_unlock(&id->chain->present_ids.lock);
wp_presentation_feedback_destroy(feedback);
@@ -1944,8 +1989,10 @@ frame_handle_done(void *data, struct wl_callback *callback, uint32_t serial)
{
struct wsi_wl_swapchain *chain = data;
+ assert(!chain->can_timestamp);
+
chain->frame = NULL;
- chain->fifo_ready = true;
+ chain->legacy_fifo_ready = true;
wl_callback_destroy(callback);
}
@@ -1954,6 +2001,46 @@ static const struct wl_callback_listener frame_listener = {
frame_handle_done,
};
+static void
+set_timestamp(struct wsi_wl_swapchain *chain)
+{
+ uint64_t now, target;
+ struct timespec target_ts;
+ uint64_t refresh;
+ uint64_t phase_time;
+
+ now = os_time_get_nano();
+
+ pthread_mutex_lock(&chain->present_ids.lock);
+ phase_time = chain->present_ids.phase_time;
+ refresh = chain->present_ids.refresh_nsec;
+ pthread_mutex_unlock(&chain->present_ids.lock);
+
+ if (refresh == 0)
+ refresh = 16666666;
+
+ target = chain->last_target_time + refresh;
+
+ if (now > target) {
+ uint64_t offset;
+
+ if (phase_time > now)
+ now = phase_time;
+
+ offset = (now - phase_time) % refresh;
+ target = now - offset + refresh;
+ }
+
+ timespec_from_nsec(&target_ts, target);
+ wp_commit_timer_v1_set_timestamp(chain->commit_timer,
+ target_ts.tv_sec >> 32, target_ts.tv_sec,
+ target_ts.tv_nsec);
+
+ wp_commit_queue_v1_set_queue_mode(chain->commit_queue,
+ WP_COMMIT_QUEUE_V1_QUEUE_MODE_FIFO);
+ chain->last_target_time = target;
+}
+
static VkResult
wsi_wl_swapchain_queue_present(struct wsi_swapchain *wsi_chain,
uint32_t image_index,
@@ -1962,6 +2049,7 @@ wsi_wl_swapchain_queue_present(struct wsi_swapchain *wsi_chain,
{
struct wsi_wl_swapchain *chain = (struct wsi_wl_swapchain *)wsi_chain;
struct wsi_wl_surface *wsi_wl_surface = chain->wsi_wl_surface;
+ bool mode_fifo = chain->base.present_mode == VK_PRESENT_MODE_FIFO_KHR;
if (chain->buffer_type == WSI_WL_BUFFER_SHM_MEMCPY) {
struct wsi_wl_image *image = &chain->images[image_index];
@@ -1971,7 +2059,7 @@ wsi_wl_swapchain_queue_present(struct wsi_swapchain *wsi_chain,
/* For EXT_swapchain_maintenance1. We might have transitioned from FIFO to MAILBOX.
* In this case we need to let the FIFO request complete, before presenting MAILBOX. */
- while (!chain->fifo_ready) {
+ while (!chain->can_timestamp && !chain->legacy_fifo_ready) {
int ret = wl_display_dispatch_queue(wsi_wl_surface->display->wl_display,
wsi_wl_surface->display->queue);
if (ret < 0)
@@ -1994,16 +2082,19 @@ wsi_wl_swapchain_queue_present(struct wsi_swapchain *wsi_chain,
wl_surface_damage(wsi_wl_surface->surface, 0, 0, INT32_MAX, INT32_MAX);
}
- if (chain->base.present_mode == VK_PRESENT_MODE_FIFO_KHR) {
- chain->frame = wl_surface_frame(wsi_wl_surface->surface);
- wl_callback_add_listener(chain->frame, &frame_listener, chain);
- chain->fifo_ready = false;
- } else {
- /* If we present MAILBOX, any subsequent presentation in FIFO can replace this image. */
- chain->fifo_ready = true;
+ if (!chain->can_timestamp) {
+ if (mode_fifo) {
+ chain->frame = wl_surface_frame(wsi_wl_surface->surface);
+ wl_callback_add_listener(chain->frame, &frame_listener, chain);
+ chain->legacy_fifo_ready = false;
+ } else {
+ /* If we present MAILBOX, any subsequent presentation in FIFO can replace this image. */
+ chain->legacy_fifo_ready = true;
+ }
}
- if (present_id > 0 && chain->present_ids.wp_presentation) {
+ if (chain->present_ids.wp_presentation &&
+ (present_id > 0 || (chain->can_timestamp && mode_fifo))) {
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);
@@ -2022,6 +2113,10 @@ wsi_wl_swapchain_queue_present(struct wsi_swapchain *wsi_chain,
}
chain->images[image_index].busy = true;
+
+ if (chain->can_timestamp && mode_fifo)
+ set_timestamp(chain);
+
wl_surface_commit(wsi_wl_surface->surface);
wl_display_flush(wsi_wl_surface->display->wl_display);
@@ -2137,6 +2232,7 @@ wsi_wl_image_init(struct wsi_wl_swapchain *chain,
goto fail_image;
wl_buffer_add_listener(image->buffer, &buffer_listener, image);
+ image->chain = chain;
return VK_SUCCESS;
@@ -2185,6 +2281,12 @@ wsi_wl_swapchain_chain_free(struct wsi_wl_swapchain *chain,
pthread_mutex_destroy(&chain->present_ids.lock);
}
+ if (chain->commit_queue)
+ wp_commit_queue_v1_destroy(chain->commit_queue);
+
+ if (chain->commit_timer)
+ wp_commit_timer_v1_destroy(chain->commit_timer);
+
wsi_swapchain_finish(&chain->base);
}
@@ -2263,6 +2365,11 @@ wsi_wl_surface_create_swapchain(VkIcdSurfaceBase *icd_surface,
wp_tearing_control_v1_destroy(old_chain->tearing_control);
old_chain->tearing_control = NULL;
}
+ if (old_chain->commit_queue) {
+ gamescope_commit_queue_v1_destroy(old_chain->commit_queue);
+ old_chain->commit_queue = NULL;
+ old_chain->can_timestamp = false;
+ }
}
/* Take ownership of the wsi_wl_surface */
@@ -2337,7 +2452,16 @@ wsi_wl_surface_create_swapchain(VkIcdSurfaceBase *icd_surface,
chain->queue);
}
- chain->fifo_ready = true;
+ chain->legacy_fifo_ready = true;
+ struct wsi_wl_display *dpy = chain->wsi_wl_surface->display;
+ if (dpy->commit_queue_manager &&
+ dpy->commit_timing_manager) {
+ chain->commit_queue = wp_commit_queue_manager_v1_get_queue_controller(dpy->commit_queue_manager,
+ chain->wsi_wl_surface->surface);
+ chain->commit_timer = wp_commit_timing_manager_v1_get_timer(dpy->commit_timing_manager,
+ chain->wsi_wl_surface->surface);
+ chain->can_timestamp = true;
+ }
for (uint32_t i = 0; i < chain->base.image_count; i++) {
result = wsi_wl_image_init(chain, &chain->images[i],
--
2.42.0
From f40957ad3b4d0ee71a3c1f0728d9e44efde83e4a Mon Sep 17 00:00:00 2001
From: Simon Ser <contact@emersion.fr>
Date: Sat, 25 Nov 2023 16:25:58 +0100
Subject: [PATCH 3/8] hack: rip out commit-timing-v1
---
src/egl/wayland/wayland-drm/meson.build | 6 +++---
src/vulkan/wsi/meson.build | 2 +-
src/vulkan/wsi/wsi_common_wayland.c | 24 ++----------------------
3 files changed, 6 insertions(+), 26 deletions(-)
diff --git a/src/egl/wayland/wayland-drm/meson.build b/src/egl/wayland/wayland-drm/meson.build
index 70940cb8771..fc6ed461c92 100644
--- a/src/egl/wayland/wayland-drm/meson.build
+++ b/src/egl/wayland/wayland-drm/meson.build
@@ -60,7 +60,7 @@ libwayland_drm = static_library(
wp_dir = dep_wl_protocols.get_variable(pkgconfig : 'pkgdatadir')
wp_protos = {
'commit-queue-v1': 'staging/commit-queue/commit-queue-v1.xml',
- 'commit-timing-v1': 'staging/commit-timing/commit-timing-v1.xml',
+ #'commit-timing-v1': 'staging/commit-timing/commit-timing-v1.xml',
'linux-dmabuf-unstable-v1': 'unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml',
'presentation-time': 'stable/presentation-time/presentation-time.xml',
}
@@ -68,13 +68,13 @@ wp_files = {}
foreach name, xml : wp_protos
code = custom_target(
name + '-protocol.c',
- input : join_paths(wp_dir, xml),
+ input : wp_dir / xml,
output : name + '-protocol.c',
command : [prog_wl_scanner, wl_scanner_arg, '@INPUT@', '@OUTPUT@'],
)
header = custom_target(
name + '-client-protocol.h',
- input : join_paths(wp_dir, xml),
+ input : wp_dir / xml,
output : name + '-client-protocol.h',
command : [prog_wl_scanner, 'client-header', '@INPUT@', '@OUTPUT@'],
)
diff --git a/src/vulkan/wsi/meson.build b/src/vulkan/wsi/meson.build
index 70a73805cd4..e82fb3de4ec 100644
--- a/src/vulkan/wsi/meson.build
+++ b/src/vulkan/wsi/meson.build
@@ -32,7 +32,7 @@ endif
if with_platform_wayland
files_vulkan_wsi += files('wsi_common_wayland.c')
files_vulkan_wsi += wp_files['commit-queue-v1']
- files_vulkan_wsi += wp_files['commit-timing-v1']
+ #files_vulkan_wsi += wp_files['commit-timing-v1']
files_vulkan_wsi += wp_files['linux-dmabuf-unstable-v1']
files_vulkan_wsi += wp_files['presentation-time']
endif
diff --git a/src/vulkan/wsi/wsi_common_wayland.c b/src/vulkan/wsi/wsi_common_wayland.c
index d01ff68781e..5e05f0488dc 100644
--- a/src/vulkan/wsi/wsi_common_wayland.c
+++ b/src/vulkan/wsi/wsi_common_wayland.c
@@ -42,7 +42,6 @@
#include "wsi_common_entrypoints.h"
#include "wsi_common_private.h"
#include "commit-queue-v1-client-protocol.h"
-#include "commit-timing-v1-client-protocol.h"
#include "linux-dmabuf-unstable-v1-client-protocol.h"
#include "presentation-time-client-protocol.h"
@@ -114,7 +113,6 @@ struct wsi_wl_display {
struct wp_presentation *wp_presentation_notwrapped;
struct wp_commit_queue_manager_v1 *commit_queue_manager;
- struct wp_commit_timing_manager_v1 *commit_timing_manager;
struct wsi_wayland *wsi_wl;
@@ -2032,9 +2024,6 @@ set_timestamp(struct wsi_wl_swapchain *chain)
}
timespec_from_nsec(&target_ts, target);
- wp_commit_timer_v1_set_timestamp(chain->commit_timer,
- target_ts.tv_sec >> 32, target_ts.tv_sec,
- target_ts.tv_nsec);
wp_commit_queue_v1_set_queue_mode(chain->commit_queue,
WP_COMMIT_QUEUE_V1_QUEUE_MODE_FIFO);
@@ -2050,6 +2039,7 @@ wsi_wl_swapchain_queue_present(struct wsi_swapchain *wsi_chain,
struct wsi_wl_swapchain *chain = (struct wsi_wl_swapchain *)wsi_chain;
struct wsi_wl_surface *wsi_wl_surface = chain->wsi_wl_surface;
bool mode_fifo = chain->base.present_mode == VK_PRESENT_MODE_FIFO_KHR;
+ //fprintf(stderr, "FIFO = %d\n", mode_fifo);
if (chain->buffer_type == WSI_WL_BUFFER_SHM_MEMCPY) {
struct wsi_wl_image *image = &chain->images[image_index];
@@ -2284,9 +2274,6 @@ wsi_wl_swapchain_chain_free(struct wsi_wl_swapchain *chain,
if (chain->commit_queue)
wp_commit_queue_v1_destroy(chain->commit_queue);
- if (chain->commit_timer)
- wp_commit_timer_v1_destroy(chain->commit_timer);
-
wsi_swapchain_finish(&chain->base);
}
@@ -2454,12 +2437,9 @@ wsi_wl_surface_create_swapchain(VkIcdSurfaceBase *icd_surface,
chain->legacy_fifo_ready = true;
struct wsi_wl_display *dpy = chain->wsi_wl_surface->display;
- if (dpy->commit_queue_manager &&
- dpy->commit_timing_manager) {
+ if (dpy->commit_queue_manager) {
chain->commit_queue = wp_commit_queue_manager_v1_get_queue_controller(dpy->commit_queue_manager,
chain->wsi_wl_surface->surface);
- chain->commit_timer = wp_commit_timing_manager_v1_get_timer(dpy->commit_timing_manager,
- chain->wsi_wl_surface->surface);
chain->can_timestamp = true;
}
--
2.42.0
From 5bac67e0cc8c36ff1fc7836062790d6d96de00d4 Mon Sep 17 00:00:00 2001
From: Joshua Ashton <joshua@froggi.es>
Date: Wed, 29 Nov 2023 11:06:51 +0000
Subject: [PATCH 4/8] wsi: Use vendored gamescope-commit-queue-v1 protocol
---
.../wayland-drm/gamescope-commit-queue-v1.xml | 181 ++++++++++++++++++
src/egl/wayland/wayland-drm/meson.build | 22 ++-
src/vulkan/wsi/meson.build | 2 +-
src/vulkan/wsi/wsi_common_wayland.c | 22 +--
4 files changed, 214 insertions(+), 13 deletions(-)
create mode 100644 src/egl/wayland/wayland-drm/gamescope-commit-queue-v1.xml
diff --git a/src/egl/wayland/wayland-drm/gamescope-commit-queue-v1.xml b/src/egl/wayland/wayland-drm/gamescope-commit-queue-v1.xml
new file mode 100644
index 00000000000..d460e0bc10f
--- /dev/null
+++ b/src/egl/wayland/wayland-drm/gamescope-commit-queue-v1.xml
@@ -0,0 +1,181 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<protocol name="gamescope_commit_queue_v1">
+ <copyright>
+ Copyright © 2023 Valve Corporation
+
+ Permission is hereby granted, free of charge, to any person obtaining a
+ copy of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom the
+ Software is furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice (including the next
+ paragraph) shall be included in all copies or substantial portions of the
+ Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ DEALINGS IN THE SOFTWARE.
+ </copyright>
+
+ <interface name="gamescope_commit_queue_manager_v1" version="1">
+ <description summary="commit queuing">
+ By design Wayland uses a "mailbox" style presentation model. Under
+ the mailbox model, when wl_surface.commit is called, the currently
+ pending state is intended to replace the current state immediately.
+
+ If state is committed many times before the compositor repaints a
+ scene, each commit takes place immediately, updating the existing
+ state. When the compositor repaints the display only the most
+ recent accumulation of state is visible. This may lead to client
+ buffers being released without presentation if they were replaced
+ before being displayed.
+
+ There are other presentation models such as FIFO (First In First
+ Out) in which state commits are explicitly queued for future
+ repaint intervals, and client buffers should not be released
+ without being displayed.
+
+ Graphics APIs such as Vulkan aim to support these presentation
+ models, but they are not implementable on top of our mailbox model
+ without the ability to change the default surface state handling
+ behaviour.
+
+ This interface provides a way to control the compositor's surface
+ state handling to enable presentation models other than mailbox.
+
+ It does so by exposing control of a compositor surface state queue,
+ and specifying for each call of wl_surface.commit whether the
+ pending state should be handled in a mailbox or a FIFO fashion.
+
+ Warning! The protocol described in this file is currently in the testing
+ phase. Backward compatible changes may be added together with the
+ corresponding interface version bump. Backward incompatible changes can
+ only be done by creating a new major version of the extension.
+ </description>
+ <enum name="error">
+ <description summary="fatal presentation error">
+ These fatal protocol errors may be emitted in response to
+ illegal requests.
+ </description>
+ <entry name="queue_controller_already_exists" value="0"
+ summary="commit queue controller already exists for surface"/>
+ </enum>
+
+ <request name="destroy" type="destructor">
+ <description summary="unbind from the surface queuing interface">
+ Informs the server that the client will no longer be using
+ this protocol object. Existing objects created by this object
+ are not affected.
+ </description>
+ </request>
+
+ <request name="get_queue_controller">
+ <description summary="request commit queue submission interface for surface">
+ Establish a queue controller for a surface.
+
+ Graphics APIs (EGL, Vulkan) will likely use this protocol
+ internally, so clients using them shouldn't directly use this
+ protocol on surfaces managed by those APIs, or a
+ queue_controller_already_exists protocol error will occur.
+ </description>
+ <arg name="id" type="new_id" interface="gamescope_commit_queue_v1"/>
+ <arg name="surface" type="object" interface="wl_surface"/>
+ </request>
+ </interface>
+
+ <interface name="gamescope_commit_queue_v1" version="1">
+ <description summary="commit queue controller">
+ A queue controller for a surface.
+
+ A wayland compositor may implicitly queue surface state to
+ allow it to pick the most recently ready state at repaint time,
+ or to allow surface state to contain timing information.
+
+ The commit queue controller object allows explicit control over
+ the queue of upcoming surface state by allowing a client to attach
+ a queue drain mode to pending surface state before it calls
+ wl_surface.commit.
+ </description>
+
+ <enum name="error">
+ <description summary="fatal presentation error">
+ These fatal protocol errors may be emitted in response to
+ illegal requests.
+ </description>
+ <entry name="invalid_queue_mode" value="0"
+ summary="invalid queue mode"/>
+ </enum>
+
+ <enum name="queue_mode">
+ <description summary="Queue drain mode">
+ This enum is used to choose how the compositor processes a queue
+ entry at output repaint time.
+ </description>
+ <entry name="mailbox" value="0">
+ <description summary="Fast forward through past timestamps">
+ State from this queue slot may be updated immediately (without
+ completing a repaint) if newer state is ready to display at
+ repaint time.
+ </description>
+ </entry>
+ <entry name="fifo" value="1">
+ <description summary="Attempt to display each queued commit">
+ This queue slot will be the last state update for this surface
+ that the compositor will process during the repaint in which
+ it is ready for display.
+
+ If the compositor is presenting with tearing, the surface state
+ must be made current for an iteration of the compositor's repaint
+ loop. This may result in the state being visible for a very short
+ duration, with visible artifacts, or even not visible at all for
+ surfaces that aren't full screen.
+
+ The compositor must not cause state processing to stall indefinitely
+ for a surface that is occluded or otherwise not visible. Instead,
+ if the compositor is choosing not to present a surface for reasons
+ unrelated to state readiness, the FIFO condition must be considered
+ satisfied at the moment new state becomes ready to replace the
+ undisplayed state.
+ </description>
+ </entry>
+ </enum>
+
+ <request name="set_queue_mode">
+ <description summary="set the queue draining mode for the pending commit">
+ This request adds a queue drain mode to the pending surface
+ state, which will be commit by the next wl_surface.commit.
+
+ This request tells the compositor how to process the state
+ from that commit when handling its internal state queue.
+
+ If the drain mode is "mailbox", the compositor may continue
+ processing the next state in the queue before it repaints
+ the display.
+
+ If the drain mode is "fifo", the compositor should ensure the
+ queue is not advanced until after this state has been current
+ for a repaint. The queue may be advance without repaint in the
+ case of off-screen or occluded surfaces.
+
+ The default drain mode when none is specified is "mailbox".
+ </description>
+ <arg name="mode" type="uint" enum="drain_mode"/>
+ </request>
+
+ <request name="destroy" type="destructor">
+ <description summary="Destroy the surface queue controller">
+ Informs the server that the client will no longer be using
+ this protocol object.
+
+ Surface state changes previously made by this protocol are
+ unaffected by this object's destruction.
+ </description>
+ </request>
+ </interface>
+</protocol>
diff --git a/src/egl/wayland/wayland-drm/meson.build b/src/egl/wayland/wayland-drm/meson.build
index fc6ed461c92..a6f9bf2e10b 100644
--- a/src/egl/wayland/wayland-drm/meson.build
+++ b/src/egl/wayland/wayland-drm/meson.build
@@ -59,7 +59,7 @@ libwayland_drm = static_library(
# here for now as the maybe-least-bad solution.
wp_dir = dep_wl_protocols.get_variable(pkgconfig : 'pkgdatadir')
wp_protos = {
- 'commit-queue-v1': 'staging/commit-queue/commit-queue-v1.xml',
+ #'commit-queue-v1': 'staging/commit-queue/commit-queue-v1.xml',
#'commit-timing-v1': 'staging/commit-timing/commit-timing-v1.xml',
'linux-dmabuf-unstable-v1': 'unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml',
'presentation-time': 'stable/presentation-time/presentation-time.xml',
@@ -80,3 +80,23 @@ foreach name, xml : wp_protos
)
wp_files += { name: [code, header] }
endforeach
+
+gamescope_protos = {
+ 'gamescope-commit-queue-v1': 'gamescope-commit-queue-v1.xml',
+}
+foreach name, xml : gamescope_protos
+ code = custom_target(
+ name + '-protocol.c',
+ input : xml,
+ output : name + '-protocol.c',
+ command : [prog_wl_scanner, wl_scanner_arg, '@INPUT@', '@OUTPUT@'],
+ )
+ header = custom_target(
+ name + '-client-protocol.h',
+ input : xml,
+ output : name + '-client-protocol.h',
+ command : [prog_wl_scanner, 'client-header', '@INPUT@', '@OUTPUT@'],
+ )
+ wp_files += { name: [code, header] }
+endforeach
+
diff --git a/src/vulkan/wsi/meson.build b/src/vulkan/wsi/meson.build
index e82fb3de4ec..43e23ca716d 100644
--- a/src/vulkan/wsi/meson.build
+++ b/src/vulkan/wsi/meson.build
@@ -31,7 +31,7 @@ endif
if with_platform_wayland
files_vulkan_wsi += files('wsi_common_wayland.c')
- files_vulkan_wsi += wp_files['commit-queue-v1']
+ files_vulkan_wsi += wp_files['gamescope-commit-queue-v1']
#files_vulkan_wsi += wp_files['commit-timing-v1']
files_vulkan_wsi += wp_files['linux-dmabuf-unstable-v1']
files_vulkan_wsi += wp_files['presentation-time']
diff --git a/src/vulkan/wsi/wsi_common_wayland.c b/src/vulkan/wsi/wsi_common_wayland.c
index 5e05f0488dc..5f76491e7a1 100644
--- a/src/vulkan/wsi/wsi_common_wayland.c
+++ b/src/vulkan/wsi/wsi_common_wayland.c
@@ -41,7 +41,7 @@
#include "vk_util.h"
#include "wsi_common_entrypoints.h"
#include "wsi_common_private.h"
-#include "commit-queue-v1-client-protocol.h"
+#include "gamescope-commit-queue-v1-client-protocol.h"
#include "linux-dmabuf-unstable-v1-client-protocol.h"
#include "presentation-time-client-protocol.h"
@@ -112,7 +112,7 @@ struct wsi_wl_display {
/* users want per-chain wsi_wl_swapchain->present_ids.wp_presentation */
struct wp_presentation *wp_presentation_notwrapped;
- struct wp_commit_queue_manager_v1 *commit_queue_manager;
+ struct gamescope_commit_queue_manager_v1 *commit_queue_manager;
struct wsi_wayland *wsi_wl;
@@ -2025,8 +2025,8 @@ set_timestamp(struct wsi_wl_swapchain *chain)
timespec_from_nsec(&target_ts, target);
- wp_commit_queue_v1_set_queue_mode(chain->commit_queue,
- WP_COMMIT_QUEUE_V1_QUEUE_MODE_FIFO);
+ gamescope_commit_queue_v1_set_queue_mode(chain->commit_queue,
+ GAMESCOPE_COMMIT_QUEUE_V1_QUEUE_MODE_FIFO);
chain->last_target_time = target;
}
@@ -2272,7 +2272,7 @@ wsi_wl_swapchain_chain_free(struct wsi_wl_swapchain *chain,
}
if (chain->commit_queue)
- wp_commit_queue_v1_destroy(chain->commit_queue);
+ gamescope_commit_queue_v1_destroy(chain->commit_queue);
wsi_swapchain_finish(&chain->base);
}
@@ -2438,7 +2438,7 @@ wsi_wl_surface_create_swapchain(VkIcdSurfaceBase *icd_surface,
chain->legacy_fifo_ready = true;
struct wsi_wl_display *dpy = chain->wsi_wl_surface->display;
if (dpy->commit_queue_manager) {
- chain->commit_queue = wp_commit_queue_manager_v1_get_queue_controller(dpy->commit_queue_manager,
+ chain->commit_queue = gamescope_commit_queue_manager_v1_get_queue_controller(dpy->commit_queue_manager,
chain->wsi_wl_surface->surface);
chain->can_timestamp = true;
}
--
2.42.0
From 4aa431b174c37b3bc0748ddc9bd2ca9c808881e2 Mon Sep 17 00:00:00 2001
From: Bas Nieuwenhuizen <bas@basnieuwenhuizen.nl>
Date: Fri, 14 Jan 2022 15:58:45 +0100
Subject: [PATCH 5/8] STEAMOS: radv: min image count override for FH5
Otherwise in combination with the vblank time reservation in
gamescope the game could get stuck in low power states.
---
src/util/00-radv-defaults.conf | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/src/util/00-radv-defaults.conf b/src/util/00-radv-defaults.conf
index 9434dba1e3f..3c3ae562eb9 100644
--- a/src/util/00-radv-defaults.conf
+++ b/src/util/00-radv-defaults.conf
@@ -186,5 +186,9 @@ Application bugs worked around in this file:
<application name="Rocket League" executable="RocketLeague">
<option name="radv_zero_vram" value="true" />
</application>
+
+ <application name="Forza Horizon 5" application_name_match="ForzaHorizon5.exe">
+ <option name="vk_x11_override_min_image_count" value="4" />
+ </application>
</device>
</driconf>
--
2.42.0
From 88327ee8507a323f48e43eeb87cf1f102d04f174 Mon Sep 17 00:00:00 2001
From: Bas Nieuwenhuizen <bas@basnieuwenhuizen.nl>
Date: Mon, 21 Feb 2022 18:43:54 +0100
Subject: [PATCH 6/8] STEAMOS: Dynamic swapchain override for gamescope limiter
---
src/loader/loader_dri3_helper.c | 42 +++++++++++++++++++++++++++++++--
src/loader/loader_dri3_helper.h | 1 +
src/loader/meson.build | 2 +-
src/vulkan/wsi/wsi_common_x11.c | 38 +++++++++++++++++++++++++++++
4 files changed, 80 insertions(+), 3 deletions(-)
diff --git a/src/loader/loader_dri3_helper.c b/src/loader/loader_dri3_helper.c
index 32135770e9d..abc79dda97e 100644
--- a/src/loader/loader_dri3_helper.c
+++ b/src/loader/loader_dri3_helper.c
@@ -289,6 +289,30 @@ dri3_update_max_num_back(struct loader_dri3_drawable *draw)
}
}
+static unsigned
+gamescope_swapchain_override()
+{
+ const char *path = getenv("GAMESCOPE_LIMITER_FILE");
+ if (!path)
+ return 0;
+
+ static simple_mtx_t mtx = SIMPLE_MTX_INITIALIZER;
+ static int fd = -1;
+
+ simple_mtx_lock(&mtx);
+ if (fd < 0) {
+ fd = open(path, O_RDONLY);
+ }
+ simple_mtx_unlock(&mtx);
+
+ if (fd < 0)
+ return 0;
+
+ uint32_t override_value = 0;
+ pread(fd, &override_value, sizeof(override_value), 0);
+ return override_value;
+}
+
void
loader_dri3_set_swap_interval(struct loader_dri3_drawable *draw, int interval)
{
@@ -303,10 +327,12 @@ loader_dri3_set_swap_interval(struct loader_dri3_drawable *draw, int interval)
* PS. changing from value A to B and A < B won't cause swap out of order but
* may still gets wrong target_msc value at the beginning.
*/
- if (draw->swap_interval != interval)
+ if (draw->orig_swap_interval != interval)
loader_dri3_swapbuffer_barrier(draw);
- draw->swap_interval = interval;
+ draw->orig_swap_interval = interval;
+ if (gamescope_swapchain_override() != 1)
+ draw->swap_interval = interval;
}
static void
@@ -438,6 +464,12 @@ loader_dri3_drawable_init(xcb_connection_t *conn,
draw->swap_interval = dri_get_initial_swap_interval(draw->dri_screen_render_gpu,
draw->ext->config);
+ draw->orig_swap_interval = draw->swap_interval;
+
+ unsigned gamescope_override = gamescope_swapchain_override();
+ if (gamescope_override == 1)
+ draw->swap_interval = 1;
+
dri3_update_max_num_back(draw);
/* Create a new drawable */
@@ -1092,6 +1124,12 @@ loader_dri3_swap_buffers_msc(struct loader_dri3_drawable *draw,
if (draw->type == LOADER_DRI3_DRAWABLE_WINDOW) {
dri3_fence_reset(draw->conn, back);
+ unsigned gamescope_override = gamescope_swapchain_override();
+ if (gamescope_override == 1)
+ draw->swap_interval = 1;
+ else
+ draw->swap_interval = draw->orig_swap_interval;
+
/* Compute when we want the frame shown by taking the last known
* successful MSC and adding in a swap interval for each outstanding swap
* request. target_msc=divisor=remainder=0 means "Use glXSwapBuffers()
diff --git a/src/loader/loader_dri3_helper.h b/src/loader/loader_dri3_helper.h
index 1fd340bd145..b8f5eaaf190 100644
--- a/src/loader/loader_dri3_helper.h
+++ b/src/loader/loader_dri3_helper.h
@@ -178,6 +178,7 @@ struct loader_dri3_drawable {
bool block_on_depleted_buffers;
bool queries_buffer_age;
int swap_interval;
+ int orig_swap_interval;
struct loader_dri3_extensions *ext;
const struct loader_dri3_vtable *vtable;
diff --git a/src/loader/meson.build b/src/loader/meson.build
index 35f9991ba2f..154cf809a69 100644
--- a/src/loader/meson.build
+++ b/src/loader/meson.build
@@ -29,7 +29,7 @@ if with_platform_x11 and with_dri3
dependencies : [
idep_mesautil,
dep_libdrm, dep_xcb_dri3, dep_xcb_present, dep_xcb_sync, dep_xshmfence,
- dep_xcb_xfixes,
+ dep_xcb_xfixes, dep_xcb_xrandr, idep_mesautil
],
build_by_default : false,
)
diff --git a/src/vulkan/wsi/wsi_common_x11.c b/src/vulkan/wsi/wsi_common_x11.c
index 72ff193d30a..996fc230673 100644
--- a/src/vulkan/wsi/wsi_common_x11.c
+++ b/src/vulkan/wsi/wsi_common_x11.c
@@ -48,6 +48,7 @@
#include "util/hash_table.h"
#include "util/os_file.h"
#include "util/os_time.h"
+#include "util/simple_mtx.h"
#include "util/u_debug.h"
#include "util/u_thread.h"
#include "util/xmlconfig.h"
@@ -219,6 +220,30 @@ wsi_x11_detect_xwayland(xcb_connection_t *conn,
return is_xwayland;
}
+static unsigned
+gamescope_swapchain_override()
+{
+ const char *path = getenv("GAMESCOPE_LIMITER_FILE");
+ if (!path)
+ return 0;
+
+ static simple_mtx_t mtx = SIMPLE_MTX_INITIALIZER;
+ static int fd = -1;
+
+ simple_mtx_lock(&mtx);
+ if (fd < 0) {
+ fd = open(path, O_RDONLY);
+ }
+ simple_mtx_unlock(&mtx);
+
+ if (fd < 0)
+ return 0;
+
+ uint32_t override_value = 0;
+ pread(fd, &override_value, sizeof(override_value), 0);
+ return override_value;
+}
+
static struct wsi_x11_connection *
wsi_x11_connection_create(struct wsi_device *wsi_dev,
xcb_connection_t *conn)
@@ -1103,6 +1128,8 @@ struct x11_swapchain {
/* Total number of images returned to application in AcquireNextImage. */
uint64_t present_poll_acquire_count;
+ VkPresentModeKHR orig_present_mode;
+
struct x11_image images[0];
};
VK_DEFINE_NONDISP_HANDLE_CASTS(x11_swapchain, base.base, VkSwapchainKHR,
@@ -1852,6 +1879,12 @@ x11_queue_present(struct wsi_swapchain *anv_chain,
if (chain->status < 0)
return chain->status;
+ unsigned gamescope_override = gamescope_swapchain_override();
+ if ((gamescope_override == 1 && chain->base.present_mode != VK_PRESENT_MODE_FIFO_KHR) ||
+ (gamescope_override != 1 && chain->base.present_mode != chain->orig_present_mode)) {
+ return x11_swapchain_result(chain, VK_ERROR_OUT_OF_DATE_KHR);
+ }
+
if (damage && damage->pRectangles && damage->rectangleCount > 0 &&
damage->rectangleCount <= MAX_DAMAGE_RECTS) {
xcb_rectangle_t rects[MAX_DAMAGE_RECTS];
@@ -2610,6 +2643,10 @@ x11_surface_create_swapchain(VkIcdSurfaceBase *icd_surface,
xcb_void_cookie_t cookie;
VkResult result;
VkPresentModeKHR present_mode = wsi_swapchain_get_present_mode(wsi_device, pCreateInfo);
+ VkPresentModeKHR orig_present_mode = present_mode;
+
+ if (gamescope_swapchain_override() == 1)
+ present_mode = VK_PRESENT_MODE_FIFO_KHR;
assert(pCreateInfo->sType == VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR);
@@ -2722,6 +2759,7 @@ x11_surface_create_swapchain(VkIcdSurfaceBase *icd_surface,
chain->base.wait_for_present = x11_wait_for_present;
chain->base.release_images = x11_release_images;
chain->base.present_mode = present_mode;
+ chain->orig_present_mode = orig_present_mode;
chain->base.image_count = num_images;
chain->conn = conn;
chain->window = window;
--
2.42.0