239 lines
11 KiB
Diff
239 lines
11 KiB
Diff
From ab115896be1a448bde0eb7673c26300ea4ca5040 Mon Sep 17 00:00:00 2001
|
|
From: sharkautarch <128002472+sharkautarch@users.noreply.github.com>
|
|
Date: Sun, 19 May 2024 20:15:36 -0400
|
|
Subject: [PATCH 1/2] QueuePresent: canBypassXWayland(): fetch multiple xcb
|
|
cookies initially before waiting on any of them
|
|
|
|
---
|
|
layer/VkLayer_FROG_gamescope_wsi.cpp | 1 +
|
|
layer/xcb_helpers.hpp | 105 +++++++++++++++++++++++----
|
|
2 files changed, 93 insertions(+), 13 deletions(-)
|
|
|
|
diff --git a/layer/VkLayer_FROG_gamescope_wsi.cpp b/layer/VkLayer_FROG_gamescope_wsi.cpp
|
|
index 5844c2a63..ca44849f2 100644
|
|
--- a/layer/VkLayer_FROG_gamescope_wsi.cpp
|
|
+++ b/layer/VkLayer_FROG_gamescope_wsi.cpp
|
|
@@ -975,6 +975,7 @@ namespace GamescopeWSILayer {
|
|
continue;
|
|
}
|
|
|
|
+ xcb::Prefetcher prefetcher(gamescopeSurface->connection, gamescopeSurface->window);
|
|
const bool canBypass = gamescopeSurface->canBypassXWayland();
|
|
if (canBypass != gamescopeSwapchain->isBypassingXWayland)
|
|
UpdateSwapchainResult(canBypass ? VK_SUBOPTIMAL_KHR : VK_ERROR_OUT_OF_DATE_KHR);
|
|
diff --git a/layer/xcb_helpers.hpp b/layer/xcb_helpers.hpp
|
|
index 8fac5635b..72d0ec092 100644
|
|
--- a/layer/xcb_helpers.hpp
|
|
+++ b/layer/xcb_helpers.hpp
|
|
@@ -4,22 +4,106 @@
|
|
#include <xcb/composite.h>
|
|
#include <cstdio>
|
|
#include <optional>
|
|
+#include <pthread.h>
|
|
|
|
namespace xcb {
|
|
+ inline static constinit pthread_t g_cache_tid; //incase g_cache could otherwise be accessed by one thread, while it is being deleted by another thread
|
|
+ inline static constinit struct cookie_cache_t {
|
|
+ xcb_window_t window;
|
|
+ std::tuple<xcb_get_geometry_cookie_t, xcb_query_tree_cookie_t> cached_cookies;
|
|
+ std::tuple<xcb_get_geometry_reply_t*, xcb_query_tree_reply_t*> cached_replies;
|
|
+ } g_cache = {};
|
|
+
|
|
+ //Note: this class is currently only meant to be used within GamescopeWSILayer::VkDeviceOverrides::QueuePresentKHR:
|
|
+ struct Prefetcher {
|
|
+ explicit Prefetcher(xcb_connection_t* connection, const xcb_window_t window) {
|
|
+ g_cache = {
|
|
+ .window = window,
|
|
+ .cached_cookies = {
|
|
+ xcb_get_geometry(connection, window),
|
|
+ xcb_query_tree(connection, window)
|
|
+ }
|
|
+ };
|
|
+ g_cache_tid = pthread_self();
|
|
+ }
|
|
|
|
+ ~Prefetcher() {
|
|
+ g_cache_tid = {};
|
|
+ free(std::get<0>(g_cache.cached_replies));
|
|
+ free(std::get<1>(g_cache.cached_replies));
|
|
+ g_cache.cached_replies = {nullptr,nullptr};
|
|
+ }
|
|
+ };
|
|
+
|
|
struct ReplyDeleter {
|
|
+ const bool m_bOwning = true;
|
|
+ consteval ReplyDeleter(bool bOwning = true) : m_bOwning{bOwning} {}
|
|
template <typename T>
|
|
void operator()(T* ptr) const {
|
|
- free(const_cast<std::remove_const_t<T>*>(ptr));
|
|
+ if (m_bOwning)
|
|
+ free(const_cast<std::remove_const_t<T>*>(ptr));
|
|
}
|
|
};
|
|
|
|
template <typename T>
|
|
using Reply = std::unique_ptr<T, ReplyDeleter>;
|
|
+
|
|
+ template <typename Cookie_RetType, typename Reply_RetType, typename XcbConn=xcb_connection_t*, typename... Args>
|
|
+ class XcbFetch {
|
|
+ using cookie_f_ptr_t = Cookie_RetType (*)(XcbConn, Args...);
|
|
+ using reply_f_ptr_t = Reply_RetType* (*)(XcbConn, Cookie_RetType, xcb_generic_error_t**);
|
|
+
|
|
+ const cookie_f_ptr_t m_cookieFunc;
|
|
+ const reply_f_ptr_t m_replyFunc;
|
|
+
|
|
+ public:
|
|
+ consteval XcbFetch(cookie_f_ptr_t cookieFunc, reply_f_ptr_t replyFunc) : m_cookieFunc{cookieFunc}, m_replyFunc{replyFunc} {}
|
|
+
|
|
+ inline Reply<Reply_RetType> operator()(XcbConn conn, auto... args) { //have to use auto for argsTwo, since otherwise there'd be a type deduction conflict
|
|
+ return Reply<Reply_RetType> { m_replyFunc(conn, m_cookieFunc(conn, args...), nullptr) };
|
|
+ }
|
|
+ };
|
|
+
|
|
+ template <typename CookieType>
|
|
+ concept CacheableCookie = std::is_same<CookieType, xcb_get_geometry_cookie_t>::value
|
|
+ || std::is_same<CookieType, xcb_query_tree_cookie_t>::value;
|
|
+
|
|
+ template <CacheableCookie Cookie_RetType, typename Reply_RetType>
|
|
+ class XcbFetch<Cookie_RetType, Reply_RetType, xcb_connection_t*, xcb_window_t> {
|
|
+ using cookie_f_ptr_t = Cookie_RetType (*)(xcb_connection_t*, xcb_window_t);
|
|
+ using reply_f_ptr_t = Reply_RetType* (*)(xcb_connection_t*, Cookie_RetType, xcb_generic_error_t**);
|
|
+
|
|
+ const cookie_f_ptr_t m_cookieFunc;
|
|
+ const reply_f_ptr_t m_replyFunc;
|
|
+
|
|
+ inline Reply<Reply_RetType> getCachedReply(xcb_connection_t* connection) {
|
|
+ if (std::get<Reply_RetType*>(g_cache.cached_replies) == nullptr) {
|
|
+ std::get<Reply_RetType*>(g_cache.cached_replies) = m_replyFunc(connection, std::get<Cookie_RetType>(g_cache.cached_cookies), nullptr);
|
|
+ }
|
|
|
|
+ return Reply<Reply_RetType>{std::get<Reply_RetType*>(g_cache.cached_replies), ReplyDeleter{false}}; // return 'non-owning' unique_ptr
|
|
+ }
|
|
+
|
|
+ public:
|
|
+ consteval XcbFetch(cookie_f_ptr_t cookieFunc, reply_f_ptr_t replyFunc) : m_cookieFunc{cookieFunc}, m_replyFunc{replyFunc} {}
|
|
+
|
|
+ inline Reply<Reply_RetType> operator()(xcb_connection_t* conn, xcb_window_t window) {
|
|
+ const bool tryCached = pthread_equal(g_cache_tid, pthread_self())
|
|
+ && g_cache.window == window;
|
|
+ if (!tryCached) [[unlikely]]
|
|
+ return Reply<Reply_RetType> { m_replyFunc(conn, m_cookieFunc(conn, window), nullptr) };
|
|
+
|
|
+ auto ret = getCachedReply(conn);
|
|
+ #if !defined(NDEBUG) || NDEBUG == 0
|
|
+ if (!ret)
|
|
+ fprintf(stderr, "[Gamescope WSI] getCachedReply() failed.\n");
|
|
+ #endif
|
|
+ return ret;
|
|
+ }
|
|
+ };
|
|
+
|
|
static std::optional<xcb_atom_t> getAtom(xcb_connection_t* connection, std::string_view name) {
|
|
- xcb_intern_atom_cookie_t cookie = xcb_intern_atom(connection, false, name.length(), name.data());
|
|
- auto reply = Reply<xcb_intern_atom_reply_t>{ xcb_intern_atom_reply(connection, cookie, nullptr) };
|
|
+ auto reply = XcbFetch{xcb_intern_atom, xcb_intern_atom_reply}(connection, false, name.length(), name.data());
|
|
if (!reply) {
|
|
fprintf(stderr, "[Gamescope WSI] Failed to get xcb atom.\n");
|
|
return std::nullopt;
|
|
@@ -34,8 +118,7 @@ namespace xcb {
|
|
|
|
xcb_screen_t* screen = xcb_setup_roots_iterator(xcb_get_setup(connection)).data;
|
|
|
|
- xcb_get_property_cookie_t cookie = xcb_get_property(connection, false, screen->root, atom, XCB_ATOM_CARDINAL, 0, sizeof(T) / sizeof(uint32_t));
|
|
- auto reply = Reply<xcb_get_property_reply_t>{ xcb_get_property_reply(connection, cookie, nullptr) };
|
|
+ auto reply = XcbFetch{xcb_get_property, xcb_get_property_reply}(connection, false, screen->root, atom, XCB_ATOM_CARDINAL, 0, sizeof(T) / sizeof(uint32_t));
|
|
if (!reply) {
|
|
fprintf(stderr, "[Gamescope WSI] Failed to read T root window property.\n");
|
|
return std::nullopt;
|
|
@@ -61,8 +144,7 @@ namespace xcb {
|
|
|
|
static std::optional<xcb_window_t> getToplevelWindow(xcb_connection_t* connection, xcb_window_t window) {
|
|
for (;;) {
|
|
- xcb_query_tree_cookie_t cookie = xcb_query_tree(connection, window);
|
|
- auto reply = Reply<xcb_query_tree_reply_t>{ xcb_query_tree_reply(connection, cookie, nullptr) };
|
|
+ auto reply = XcbFetch{xcb_query_tree, xcb_query_tree_reply}(connection, window);
|
|
|
|
if (!reply) {
|
|
fprintf(stderr, "[Gamescope WSI] getToplevelWindow: xcb_query_tree failed for window 0x%x.\n", window);
|
|
@@ -77,8 +159,7 @@ namespace xcb {
|
|
}
|
|
|
|
static std::optional<VkRect2D> getWindowRect(xcb_connection_t* connection, xcb_window_t window) {
|
|
- xcb_get_geometry_cookie_t cookie = xcb_get_geometry(connection, window);
|
|
- auto reply = Reply<xcb_get_geometry_reply_t>{ xcb_get_geometry_reply(connection, cookie, nullptr) };
|
|
+ auto reply = XcbFetch{xcb_get_geometry, xcb_get_geometry_reply}(connection, window);
|
|
if (!reply) {
|
|
fprintf(stderr, "[Gamescope WSI] getWindowRect: xcb_get_geometry failed for window 0x%x.\n", window);
|
|
return std::nullopt;
|
|
@@ -112,8 +193,7 @@ namespace xcb {
|
|
static std::optional<VkExtent2D> getLargestObscuringChildWindowSize(xcb_connection_t* connection, xcb_window_t window) {
|
|
VkExtent2D largestExtent = {};
|
|
|
|
- xcb_query_tree_cookie_t cookie = xcb_query_tree(connection, window);
|
|
- auto reply = Reply<xcb_query_tree_reply_t>{ xcb_query_tree_reply(connection, cookie, nullptr) };
|
|
+ auto reply = XcbFetch{xcb_query_tree, xcb_query_tree_reply}(connection, window);
|
|
|
|
if (!reply) {
|
|
fprintf(stderr, "[Gamescope WSI] getLargestObscuringWindowSize: xcb_query_tree failed for window 0x%x.\n", window);
|
|
@@ -130,8 +210,7 @@ namespace xcb {
|
|
for (uint32_t i = 0; i < reply->children_len; i++) {
|
|
xcb_window_t child = children[i];
|
|
|
|
- xcb_get_window_attributes_cookie_t attributeCookie = xcb_get_window_attributes(connection, child);
|
|
- auto attributeReply = Reply<xcb_get_window_attributes_reply_t>{ xcb_get_window_attributes_reply(connection, attributeCookie, nullptr) };
|
|
+ auto attributeReply = XcbFetch{xcb_get_window_attributes, xcb_get_window_attributes_reply}(connection, child);
|
|
|
|
const bool obscuring =
|
|
attributeReply &&
|
|
|
|
From 1b59621f4de5c05096d1f279cba2e04264124154 Mon Sep 17 00:00:00 2001
|
|
From: sharkautarch <128002472+sharkautarch@users.noreply.github.com>
|
|
Date: Tue, 18 Jun 2024 22:21:23 -0400
|
|
Subject: [PATCH 2/2] WSI: prefetcher: fix issue w/ attempting to prefetch xcb
|
|
stuff for pure wayland surfaces
|
|
|
|
---
|
|
layer/VkLayer_FROG_gamescope_wsi.cpp | 2 +-
|
|
layer/xcb_helpers.hpp | 9 ++++++++-
|
|
2 files changed, 9 insertions(+), 2 deletions(-)
|
|
|
|
diff --git a/layer/VkLayer_FROG_gamescope_wsi.cpp b/layer/VkLayer_FROG_gamescope_wsi.cpp
|
|
index f26819a60..ce011dcd7 100644
|
|
--- a/layer/VkLayer_FROG_gamescope_wsi.cpp
|
|
+++ b/layer/VkLayer_FROG_gamescope_wsi.cpp
|
|
@@ -1234,7 +1234,7 @@ namespace GamescopeWSILayer {
|
|
continue;
|
|
}
|
|
|
|
- xcb::Prefetcher prefetcher(gamescopeSurface->connection, gamescopeSurface->window);
|
|
+ auto prefetcher = xcb::Prefetcher::GetPrefetcherIf(!gamescopeSurface->isWayland(), gamescopeSurface->connection, gamescopeSurface->window);
|
|
const bool canBypass = gamescopeSurface->canBypassXWayland();
|
|
if (canBypass != gamescopeSwapchain->isBypassingXWayland)
|
|
UpdateSwapchainResult(canBypass ? VK_SUBOPTIMAL_KHR : VK_ERROR_OUT_OF_DATE_KHR);
|
|
diff --git a/layer/xcb_helpers.hpp b/layer/xcb_helpers.hpp
|
|
index 72d0ec092..f26aef38b 100644
|
|
--- a/layer/xcb_helpers.hpp
|
|
+++ b/layer/xcb_helpers.hpp
|
|
@@ -16,6 +16,13 @@ namespace xcb {
|
|
|
|
//Note: this class is currently only meant to be used within GamescopeWSILayer::VkDeviceOverrides::QueuePresentKHR:
|
|
struct Prefetcher {
|
|
+ static std::optional<Prefetcher> GetPrefetcherIf(bool bCond, xcb_connection_t* connection, const xcb_window_t window) {
|
|
+ if (bCond)
|
|
+ return std::optional<Prefetcher>(std::in_place_t{}, connection, window);
|
|
+
|
|
+ return std::nullopt;
|
|
+ }
|
|
+
|
|
explicit Prefetcher(xcb_connection_t* connection, const xcb_window_t window) {
|
|
g_cache = {
|
|
.window = window,
|
|
@@ -90,7 +97,7 @@ namespace xcb {
|
|
inline Reply<Reply_RetType> operator()(xcb_connection_t* conn, xcb_window_t window) {
|
|
const bool tryCached = pthread_equal(g_cache_tid, pthread_self())
|
|
&& g_cache.window == window;
|
|
- if (!tryCached) [[unlikely]]
|
|
+ if (!tryCached)
|
|
return Reply<Reply_RetType> { m_replyFunc(conn, m_cookieFunc(conn, window), nullptr) };
|
|
|
|
auto ret = getCachedReply(conn);
|