diff --git a/spec_files/gamescope/1231.patch b/spec_files/gamescope/1231.patch new file mode 100644 index 00000000..e21cb0a8 --- /dev/null +++ b/spec_files/gamescope/1231.patch @@ -0,0 +1,239 @@ +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 + #include + #include ++#include + + 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 cached_cookies; ++ std::tuple 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 + void operator()(T* ptr) const { +- free(const_cast*>(ptr)); ++ if (m_bOwning) ++ free(const_cast*>(ptr)); + } + }; + + template + using Reply = std::unique_ptr; ++ ++ template ++ 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 operator()(XcbConn conn, auto... args) { //have to use auto for argsTwo, since otherwise there'd be a type deduction conflict ++ return Reply { m_replyFunc(conn, m_cookieFunc(conn, args...), nullptr) }; ++ } ++ }; ++ ++ template ++ concept CacheableCookie = std::is_same::value ++ || std::is_same::value; ++ ++ template ++ class XcbFetch { ++ 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 getCachedReply(xcb_connection_t* connection) { ++ if (std::get(g_cache.cached_replies) == nullptr) { ++ std::get(g_cache.cached_replies) = m_replyFunc(connection, std::get(g_cache.cached_cookies), nullptr); ++ } + ++ return Reply{std::get(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 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 { 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 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(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(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 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(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 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(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 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(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(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 GetPrefetcherIf(bool bCond, xcb_connection_t* connection, const xcb_window_t window) { ++ if (bCond) ++ return std::optional(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 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 { m_replyFunc(conn, m_cookieFunc(conn, window), nullptr) }; + + auto ret = getCachedReply(conn); diff --git a/spec_files/gamescope/1394.patch b/spec_files/gamescope/1394.patch new file mode 100644 index 00000000..d6b1ff65 --- /dev/null +++ b/spec_files/gamescope/1394.patch @@ -0,0 +1,215 @@ +From d81aad780c67e8737ceee60ca58742dc8df72648 Mon Sep 17 00:00:00 2001 +From: sharkautarch <128002472+sharkautarch@users.noreply.github.com> +Date: Sun, 23 Jun 2024 10:53:50 -0400 +Subject: [PATCH] use latest upstream wlroots + +--- + .gitmodules | 7 ++++--- + src/rendervulkan.cpp | 37 +++++++++++++++++-------------------- + src/wlserver.cpp | 18 +++++++++++------- + src/wlserver.hpp | 14 ++++++++++++-- + subprojects/wlroots | 2 +- + 5 files changed, 45 insertions(+), 33 deletions(-) + +diff --git a/.gitmodules b/.gitmodules +index ec7d4e4..34e81f5 100644 +--- a/.gitmodules ++++ b/.gitmodules +@@ -1,6 +1,3 @@ +-[submodule "subprojects/wlroots"] +- path = subprojects/wlroots +- url = https://github.com/Joshua-Ashton/wlroots.git + [submodule "subprojects/libliftoff"] + path = subprojects/libliftoff + url = https://gitlab.freedesktop.org/emersion/libliftoff.git +@@ -19,3 +16,7 @@ + [submodule "thirdparty/SPIRV-Headers"] + path = thirdparty/SPIRV-Headers + url = https://github.com/KhronosGroup/SPIRV-Headers/ ++[submodule "subprojects/wlroots"] ++ path = subprojects/wlroots ++ url = https://gitlab.freedesktop.org/wlroots/wlroots.git ++ branch = master +\ No newline at end of file +diff --git a/src/rendervulkan.cpp b/src/rendervulkan.cpp +index 711beb5..0f00b68 100644 +--- a/src/rendervulkan.cpp ++++ b/src/rendervulkan.cpp +@@ -128,7 +128,7 @@ static bool Contains( const std::span x, T value ) + } + + static std::map< VkFormat, std::map< uint64_t, VkDrmFormatModifierPropertiesEXT > > DRMModifierProps = {}; +-static std::vector< uint32_t > sampledShmFormats{}; ++static struct wlr_drm_format_set sampledShmFormats = {}; + static struct wlr_drm_format_set sampledDRMFormats = {}; + + static LogScope vk_log("vulkan"); +@@ -2588,8 +2588,12 @@ bool vulkan_init_format(VkFormat format, uint32_t drmFormat) + return false; + } + +- if ( std::find( sampledShmFormats.begin(), sampledShmFormats.end(), drmFormat ) == sampledShmFormats.end() ) ++ if (sampledShmFormats.formats == nullptr + sampledShmFormats.push_back( drmFormat ); ++ || std::none_of( sampledShmFormats.formats, sampledShmFormats.formats+sampledShmFormats.len, [=](wlr_drm_format format) { return format.format == drmFormat;} ) ) ++ { ++ wlr_drm_format_set_add( &sampledShmFormats, drmFormat, DRM_FORMAT_MOD_INVALID ); ++ } + + if ( !g_device.supportsModifiers() ) + { +@@ -3990,21 +3994,16 @@ static const struct wlr_texture_impl texture_impl = { + .destroy = texture_destroy, + }; + +-static uint32_t renderer_get_render_buffer_caps( struct wlr_renderer *renderer ) ++static const struct wlr_drm_format_set *renderer_get_texture_formats(struct wlr_renderer *wlr_renderer, uint32_t buffer_caps) { + { ++ if (buffer_caps & WLR_BUFFER_CAP_DMABUF) { + return 0; +-} +- +-static const uint32_t *renderer_get_shm_texture_formats( struct wlr_renderer *wlr_renderer, size_t *len +- ) +-{ +- *len = sampledShmFormats.size(); +- return sampledShmFormats.data(); +-} +- +-static const struct wlr_drm_format_set *renderer_get_dmabuf_texture_formats( struct wlr_renderer *wlr_renderer ) +-{ +- return &sampledDRMFormats; ++ return &sampledDRMFormats; ++ } else if (buffer_caps & WLR_BUFFER_CAP_DATA_PTR) { ++ return &sampledShmFormats; ++ } else { ++ return nullptr; ++ } + } + + static int renderer_get_drm_fd( struct wlr_renderer *wlr_renderer ) +@@ -4028,18 +4027,17 @@ static struct wlr_render_pass *renderer_begin_buffer_pass( struct wlr_renderer * + } + + static const struct wlr_renderer_impl renderer_impl = { +- .get_shm_texture_formats = renderer_get_shm_texture_formats, +- .get_dmabuf_texture_formats = renderer_get_dmabuf_texture_formats, ++ .get_texture_formats = renderer_get_texture_formats, + .get_drm_fd = renderer_get_drm_fd, +- .get_render_buffer_caps = renderer_get_render_buffer_caps, + .texture_from_buffer = renderer_texture_from_buffer, + .begin_buffer_pass = renderer_begin_buffer_pass, + }; + + struct wlr_renderer *vulkan_renderer_create( void ) + { ++ static constexpr uint32_t render_buffer_caps = WLR_BUFFER_CAP_DMABUF | WLR_BUFFER_CAP_DATA_PTR | WLR_BUFFER_CAP_SHM; + VulkanRenderer_t *renderer = new VulkanRenderer_t(); +- wlr_renderer_init(&renderer->base, &renderer_impl); ++ wlr_renderer_init(&renderer->base, &renderer_impl, render_buffer_caps); + return &renderer->base; + } + +diff --git a/src/wlserver.cpp b/src/wlserver.cpp +index 776e014..2964ade 100644 +--- a/src/wlserver.cpp ++++ b/src/wlserver.cpp +@@ -25,7 +25,6 @@ + #include + #include + #include +-#include + #include + #include + #include +@@ -37,7 +36,7 @@ + #include + #include + #include +-#include ++#include + #include + #include + #include "wlr_end.hpp" +@@ -70,6 +69,11 @@ + #include + #include + ++extern "C" { ++ __attribute__((visibility("hidden"))) struct wlr_linux_drm_syncobj_surface_v1_state * wlr_linux_drm_syncobj_v1_get_surface_state(struct wlr_surface *wlr_surface); ++ __attribute__((visibility("hidden"))) struct wlr_linux_drm_syncobj_manager_v1 *wlr_linux_drm_syncobj_manager_v1_create(struct wl_display *display, uint32_t version, int drm_fd); ++} ++ + static LogScope wl_log("wlserver"); + bool pending_gesture_x = false; + bool pending_gesture_y = false; +@@ -124,7 +128,7 @@ void GamescopeTimelinePoint::Release() + + //fprintf( stderr, "Release: %lu\n", ulPoint ); + drmSyncobjTimelineSignal( pTimeline->drm_fd, &pTimeline->handle, &ulPoint, 1 ); +- wlr_render_timeline_unref( pTimeline ); ++ wlr_drm_syncobj_timeline_unref( pTimeline ); + } + + // +@@ -136,7 +140,7 @@ void GamescopeTimelinePoint::Release() + + static std::optional TimelinePointToEventFd( const std::optional& oPoint ) + { +- if (!oPoint) ++ if (!oPoint || !(oPoint->pTimeline) ) + return std::nullopt; + + uint64_t uSignalledPoint = 0; +@@ -187,8 +191,8 @@ std::optional PrepareCommit( struct wlr_surface *surf, struct wl + + const auto& pFeedback = wlserver_surface_swapchain_feedback(surf); + +- wlr_linux_drm_syncobj_surface_v1_state *pSyncState = +- wlr_linux_drm_syncobj_v1_get_surface_state( wlserver.wlr.drm_syncobj_manager_v1, surf ); ++ struct wlr_linux_drm_syncobj_surface_v1_state *pSyncState = ++ wlr_linux_drm_syncobj_v1_get_surface_state( surf ); + + auto oAcquirePoint = !pSyncState ? std::nullopt : std::optional { + std::in_place_t{}, +@@ -205,7 +209,7 @@ std::optional PrepareCommit( struct wlr_surface *surf, struct wl + } + + oReleasePoint.emplace( +- wlr_render_timeline_ref( pSyncState->release_timeline ), ++ wlr_drm_syncobj_timeline_ref( pSyncState->release_timeline ), + pSyncState->release_point + ); + } +diff --git a/src/wlserver.hpp b/src/wlserver.hpp +index 07675fe..16e6665 100644 +--- a/src/wlserver.hpp ++++ b/src/wlserver.hpp +@@ -2,6 +2,16 @@ + + #pragma once + ++//two function prototypes from wlr/types/wlr_linux_drm_syncobj_v1.h would cause linker errors ++//due to c++ vs c linkage weirdness ++//need to ignore the prototypes from the header, and instead use versions w/ 'extern "C"' : ++#define wlr_linux_drm_syncobj_v1_get_surface_state wlr_linux_drm_syncobj_v1_get_surface_state_C_LINKAGE ++#define wlr_linux_drm_syncobj_manager_v1_create wlr_linux_drm_syncobj_manager_v1_create_C_LINKAGE ++#include ++ ++#undef wlr_linux_drm_syncobj_v1_get_surface_state ++#undef wlr_linux_drm_syncobj_manager_v1_create ++ + #include + #include + #include +@@ -42,8 +52,8 @@ struct wlserver_vk_swapchain_feedback + + struct GamescopeTimelinePoint + { +- struct wlr_render_timeline *pTimeline = nullptr; +- uint64_t ulPoint = 0; ++ decltype(wlr_linux_drm_syncobj_surface_v1_state::acquire_timeline) pTimeline = nullptr; ++ decltype(wlr_linux_drm_syncobj_surface_v1_state::acquire_point) ulPoint = 0; + + void Release(); + }; diff --git a/spec_files/gamescope/gamescope.spec b/spec_files/gamescope/gamescope.spec index 70431287..6435990e 100644 --- a/spec_files/gamescope/gamescope.spec +++ b/spec_files/gamescope/gamescope.spec @@ -6,7 +6,7 @@ Name: gamescope Version: 100.%{gamescope_tag} -Release: 10.bazzite +Release: 11.bazzite Summary: Micro-compositor for video games on Wayland License: BSD @@ -27,6 +27,10 @@ Patch3: drm-Separate-BOE-and-SDC-OLED-Deck-panel-rates.patch Patch4: revert-299bc34.patch # https://github.com/ValveSoftware/gamescope/pull/1335 Patch5: 1335.patch +# https://github.com/ValveSoftware/gamescope/pull/1231 +Patch6: 1231.patch +# https://github.com/ValveSoftware/gamescope/pull/1394 +Patch7: 1394.patch BuildRequires: meson >= 0.54.0 BuildRequires: ninja-build @@ -100,6 +104,7 @@ Summary: libs for %{name} %prep git clone --depth 1 --branch %{gamescope_tag} %{url}.git cd gamescope +%autopatch -p1 git submodule update --init --recursive mkdir -p pkgconfig cp %{SOURCE0} pkgconfig/stb.pc @@ -107,8 +112,6 @@ cp %{SOURCE0} pkgconfig/stb.pc # Replace spirv-headers include with the system directory sed -i 's^../thirdparty/SPIRV-Headers/include/spirv/^/usr/include/spirv/^' src/meson.build -%autopatch -p1 - %build cd gamescope export PKG_CONFIG_PATH=pkgconfig