Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WSI: QueuePresent: canBypassXWayland(): Use latency hiding & reduce redundant requests/replies #1231

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions layer/VkLayer_FROG_gamescope_wsi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1234,6 +1234,7 @@ namespace GamescopeWSILayer {
continue;
}

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);
Expand Down
112 changes: 99 additions & 13 deletions layer/xcb_helpers.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,113 @@
#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 {
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,
.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)
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;
Expand All @@ -34,8 +125,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;
Expand All @@ -61,8 +151,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);
Expand All @@ -77,8 +166,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;
Expand Down Expand Up @@ -112,8 +200,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);
Expand All @@ -130,8 +217,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 &&
Expand Down
Loading