From 93816bfe0b181a6b293ba674807c20d228690362 Mon Sep 17 00:00:00 2001 From: Raboneko <119771935+raboneko@users.noreply.github.com> Date: Thu, 2 Jan 2025 20:26:44 -0800 Subject: [PATCH] fix: gamescope (bazzite port) (#2845) (#2848) * fix gamescope (bazzite port) * It's 2025 lol Signed-off-by: Owen Zimmerman <123591347+Owen-sz@users.noreply.github.com> --------- Signed-off-by: Owen Zimmerman <123591347+Owen-sz@users.noreply.github.com> (cherry picked from commit 4fc6a555dc823e717123f6b1716492042847a7dd) Co-authored-by: Owen Zimmerman <123591347+Owen-sz@users.noreply.github.com> --- anda/games/gamescope/0001-cstdint.patch | 1 + anda/games/gamescope/0001-limits.patch | 21 - anda/games/gamescope/1483.patch | 34 - anda/games/gamescope/anda.hcl | 13 +- anda/games/gamescope/chimeraos.patch | 2009 -------------- .../disable-steam-touch-click-atom.patch | 51 - anda/games/gamescope/gamescope-legacy.sh | 29 - .../{terra-gamescope.spec => gamescope.spec} | 74 +- anda/games/gamescope/handheld.patch | 2439 +++++++++++++++++ anda/games/gamescope/revert-299bc34.patch | 65 - anda/games/gamescope/stb.pc | 2 +- anda/games/gamescope/update-patch.sh | 3 - anda/games/gamescope/update.rhai | 1 - ...-ctrl-1-2-to-steam-s-wayland-session.patch | 39 - 14 files changed, 2475 insertions(+), 2306 deletions(-) mode change 100644 => 100755 anda/games/gamescope/0001-cstdint.patch delete mode 100644 anda/games/gamescope/0001-limits.patch delete mode 100644 anda/games/gamescope/1483.patch mode change 100644 => 100755 anda/games/gamescope/anda.hcl delete mode 100644 anda/games/gamescope/chimeraos.patch delete mode 100644 anda/games/gamescope/disable-steam-touch-click-atom.patch delete mode 100644 anda/games/gamescope/gamescope-legacy.sh rename anda/games/gamescope/{terra-gamescope.spec => gamescope.spec} (64%) mode change 100644 => 100755 create mode 100755 anda/games/gamescope/handheld.patch delete mode 100644 anda/games/gamescope/revert-299bc34.patch mode change 100644 => 100755 anda/games/gamescope/stb.pc delete mode 100755 anda/games/gamescope/update-patch.sh delete mode 100644 anda/games/gamescope/update.rhai delete mode 100644 anda/games/gamescope/v2-0001-always-send-ctrl-1-2-to-steam-s-wayland-session.patch diff --git a/anda/games/gamescope/0001-cstdint.patch b/anda/games/gamescope/0001-cstdint.patch old mode 100644 new mode 100755 index 36a9ad59f2..5b4351521b --- a/anda/games/gamescope/0001-cstdint.patch +++ b/anda/games/gamescope/0001-cstdint.patch @@ -34,3 +34,4 @@ index 072d439..e4bb633 100644 -- 2.41.0 + diff --git a/anda/games/gamescope/0001-limits.patch b/anda/games/gamescope/0001-limits.patch deleted file mode 100644 index 0b78aafea4..0000000000 --- a/anda/games/gamescope/0001-limits.patch +++ /dev/null @@ -1,21 +0,0 @@ -From 88ce1e5de62886aa14c74421cde6130e16e70d7d Mon Sep 17 00:00:00 2001 -From: psykose -Date: Sat, 6 Jul 2024 20:52:50 +0200 -Subject: [PATCH] utils: include limits.h for PATH_MAX - ---- - src/Utils/Process.cpp | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/src/Utils/Process.cpp b/src/Utils/Process.cpp -index e71786f75..3e748e0d3 100644 ---- a/src/Utils/Process.cpp -+++ b/src/Utils/Process.cpp -@@ -21,6 +21,7 @@ - #include - #include - #include -+#include - #include - #include - #include \ No newline at end of file diff --git a/anda/games/gamescope/1483.patch b/anda/games/gamescope/1483.patch deleted file mode 100644 index 3bde8361d6..0000000000 --- a/anda/games/gamescope/1483.patch +++ /dev/null @@ -1,34 +0,0 @@ -From ca58cb2453e6d9ef44d799e394ee9950b7a35b30 Mon Sep 17 00:00:00 2001 -From: Cappy Ishihara -Date: Wed, 21 Aug 2024 03:56:53 +0700 -Subject: [PATCH] Check if current GPU supports Vulkan DRM modifiers when - `--backend=auto` is used. - -This works around #1218 by making use of the new backend option added in #1321, -but adds a check to automatically fall back to the SDL backend if the current -GPU does not support Vulkan DRM modifiers. ---- - src/main.cpp | 10 +++++++--- - 1 file changed, 7 insertions(+), 3 deletions(-) - -diff --git a/src/main.cpp b/src/main.cpp -index ca4001249..bc6b16904 100644 ---- a/src/main.cpp -+++ b/src/main.cpp -@@ -817,9 +817,13 @@ int main(int argc, char **argv) - if ( eCurrentBackend == gamescope::GamescopeBackend::Auto ) - { - if ( g_pOriginalWaylandDisplay != NULL ) -- eCurrentBackend = gamescope::GamescopeBackend::Wayland; -- else if ( g_pOriginalDisplay != NULL ) -- eCurrentBackend = gamescope::GamescopeBackend::SDL; -+ // Additional check if the current GPU supports Vulkan DRM modifiers -+ // Fallback to SDL if not supported (e.g Older AMD GPUs like Polaris 10/20) -+ if ( vulkan_supports_modifiers() ) -+ eCurrentBackend = gamescope::GamescopeBackend::Wayland; -+ else -+ eCurrentBackend = gamescope::GamescopeBackend::SDL; -+ - else - eCurrentBackend = gamescope::GamescopeBackend::DRM; - } diff --git a/anda/games/gamescope/anda.hcl b/anda/games/gamescope/anda.hcl old mode 100644 new mode 100755 index cd18050ddc..9aee67abdf --- a/anda/games/gamescope/anda.hcl +++ b/anda/games/gamescope/anda.hcl @@ -1,8 +1,9 @@ project pkg { - rpm { - spec = "terra-gamescope.spec" - } - labels { - multilib = 1 - } + rpm { + spec = "gamescope.spec" + } + labels { + multilib = 1 + extra = 1 + } } diff --git a/anda/games/gamescope/chimeraos.patch b/anda/games/gamescope/chimeraos.patch deleted file mode 100644 index f8bd2f877c..0000000000 --- a/anda/games/gamescope/chimeraos.patch +++ /dev/null @@ -1,2009 +0,0 @@ -From 646446c157d55d8508c734cddfc3025834f7cf50 Mon Sep 17 00:00:00 2001 -From: Matthew Anderson -Date: Fri, 17 May 2024 19:43:49 -0500 -Subject: [PATCH 01/21] Add touch-gestures to open up Steam menus - ---- - src/main.cpp | 5 +++++ - src/wlserver.cpp | 28 ++++++++++++++++++++++++++++ - src/wlserver.hpp | 1 + - 3 files changed, 34 insertions(+) - -diff --git a/src/main.cpp b/src/main.cpp -index ca40012..3a1b1ae 100644 ---- a/src/main.cpp -+++ b/src/main.cpp -@@ -108,6 +108,8 @@ const struct option *gamescope_options = (struct option[]){ - - // wlserver options - { "xwayland-count", required_argument, nullptr, 0 }, -+ { "touch-gestures", no_argument, nullptr, 0 }, -+ - - // steamcompmgr options - { "cursor", required_argument, nullptr, 0 }, -@@ -185,6 +187,7 @@ const char usage[] = - " -T, --stats-path write statistics to path\n" - " -C, --hide-cursor-delay hide cursor image after delay\n" - " -e, --steam enable Steam integration\n" -+ " --touch-gestures enable touch gestures for Steam menus\n" - " --xwayland-count create N xwayland servers\n" - " --prefer-vk-device prefer Vulkan device for compositing (ex: 1002:7300)\n" - " --force-orientation rotate the internal display (left, right, normal, upsidedown)\n" -@@ -734,6 +737,8 @@ int main(int argc, char **argv) - g_bDebugLayers = true; - } else if (strcmp(opt_name, "disable-color-management") == 0) { - g_bForceDisableColorMgmt = true; -+ } else if (strcmp(opt_name, "touch-gestures") == 0) { -+ cv_touch_gestures = true; - } else if (strcmp(opt_name, "xwayland-count") == 0) { - g_nXWaylandCount = atoi( optarg ); - } else if (strcmp(opt_name, "composite-debug") == 0) { -diff --git a/src/wlserver.cpp b/src/wlserver.cpp -index ccbd512..26b953a 100644 ---- a/src/wlserver.cpp -+++ b/src/wlserver.cpp -@@ -81,6 +81,7 @@ using namespace std::literals; - extern gamescope::ConVar cv_drm_debug_disable_explicit_sync; - - //#define GAMESCOPE_SWAPCHAIN_DEBUG -+gamescope::ConVar cv_touch_gestures( "enable_touch_gestures", false, "Enable/Disable the usage of touch gestures" ); - - struct wlserver_t wlserver = { - .touch_down_ids = {} -@@ -2526,6 +2527,33 @@ void wlserver_touchmotion( double x, double y, int touch_id, uint32_t time, bool - - if ( bAlwaysWarpCursor ) - wlserver_mousewarp( tx, ty, time, false ); -+ -+ if (cv_touch_gestures) { -+ bool start_gesture = false; -+ -+ // Round the x-coordinate to the nearest whole number -+ uint32_t roundedCursorX = static_cast(std::round(tx)); -+ // Grab 2% of the display to be used for the edge range -+ uint32_t edge_range = static_cast(g_nOutputWidth * 0.02); -+ -+ // Determine if the gesture should start -+ if (roundedCursorX <= edge_range || roundedCursorX >= g_nOutputWidth - edge_range) { -+ start_gesture = true; -+ } -+ -+ // Handle Home gesture -+ if (start_gesture && roundedCursorX >= edge_range) { -+ wlserver_open_steam_menu(0); -+ start_gesture = false; -+ } -+ -+ // Handle QAM gesture -+ if (start_gesture && roundedCursorX >= g_nOutputWidth - edge_range && roundedCursorX <= g_nOutputWidth) { -+ wlserver_open_steam_menu(1); -+ start_gesture = false; -+ } -+ } -+ - } - else if ( eMode == gamescope::TouchClickModes::Disabled ) - { -diff --git a/src/wlserver.hpp b/src/wlserver.hpp -index 0569472..3304c18 100644 ---- a/src/wlserver.hpp -+++ b/src/wlserver.hpp -@@ -272,6 +272,7 @@ void wlserver_x11_surface_info_finish( struct wlserver_x11_surface_info *surf ); - void wlserver_set_xwayland_server_mode( size_t idx, int w, int h, int refresh ); - - extern std::atomic g_bPendingTouchMovement; -+extern gamescope::ConVar cv_touch_gestures; - - void wlserver_open_steam_menu( bool qam ); - --- -2.46.0 - - -From 97c2ed0ea6a5cd43b450e0952db20c08d82bbe60 Mon Sep 17 00:00:00 2001 -From: Matthew Anderson -Date: Fri, 17 May 2024 20:16:20 -0500 -Subject: [PATCH 02/21] Add bypass_steam_resolution to workaround the 720p/800p - restrictions Steam has for games - ---- - src/main.cpp | 3 +++ - src/steamcompmgr.cpp | 11 +++++++++++ - 2 files changed, 14 insertions(+) - -diff --git a/src/main.cpp b/src/main.cpp -index 3a1b1ae..534779d 100644 ---- a/src/main.cpp -+++ b/src/main.cpp -@@ -129,6 +129,8 @@ const struct option *gamescope_options = (struct option[]){ - { "fade-out-duration", required_argument, nullptr, 0 }, - { "force-orientation", required_argument, nullptr, 0 }, - { "force-windows-fullscreen", no_argument, nullptr, 0 }, -+ { "bypass-steam-resolution", no_argument, nullptr, 0 }, -+ - - { "disable-color-management", no_argument, nullptr, 0 }, - { "sdr-gamut-wideness", required_argument, nullptr, 0 }, -@@ -187,6 +189,7 @@ const char usage[] = - " -T, --stats-path write statistics to path\n" - " -C, --hide-cursor-delay hide cursor image after delay\n" - " -e, --steam enable Steam integration\n" -+ " --bypass-steam-resolution bypass Steam's default 720p/800p default resolution\n" - " --touch-gestures enable touch gestures for Steam menus\n" - " --xwayland-count create N xwayland servers\n" - " --prefer-vk-device prefer Vulkan device for compositing (ex: 1002:7300)\n" -diff --git a/src/steamcompmgr.cpp b/src/steamcompmgr.cpp -index e997c85..952a5f8 100644 ---- a/src/steamcompmgr.cpp -+++ b/src/steamcompmgr.cpp -@@ -356,6 +356,8 @@ bool g_bForceHDR10OutputDebug = false; - gamescope::ConVar cv_hdr_enabled{ "hdr_enabled", false, "Whether or not HDR is enabled if it is available." }; - bool g_bHDRItmEnable = false; - int g_nCurrentRefreshRate_CachedValue = 0; -+gamescope::ConVar cv_bypass_steam_resolution{ "bypass_steam_resolution", false, "Workaround the 720p/800p limits Steam uses for games" }; -+ - - static void - update_color_mgmt() -@@ -5387,6 +5389,13 @@ handle_property_notify(xwayland_ctx_t *ctx, XPropertyEvent *ev) - size_t server_idx = size_t{ xwayland_mode_ctl[ 0 ] }; - int width = xwayland_mode_ctl[ 1 ]; - int height = xwayland_mode_ctl[ 2 ]; -+ -+ if ( g_nOutputWidth != 1280 && width == 1280 && cv_bypass_steam_resolution ) -+ { -+ width = g_nOutputWidth; -+ height = g_nOutputHeight; -+ } -+ - bool allowSuperRes = !!xwayland_mode_ctl[ 3 ]; - - if ( !allowSuperRes ) -@@ -7370,6 +7379,8 @@ steamcompmgr_main(int argc, char **argv) - bForceWindowsFullscreen = true; - } else if (strcmp(opt_name, "hdr-enabled") == 0) { - cv_hdr_enabled = true; -+ } else if (strcmp(opt_name, "bypass_steam_resolution") == 0) { -+ cv_bypass_steam_resolution = true; - } else if (strcmp(opt_name, "hdr-debug-force-support") == 0) { - g_bForceHDRSupportDebug = true; - } else if (strcmp(opt_name, "hdr-debug-force-output") == 0) { --- -2.46.0 - - -From 234e3bb0369b7ed45a8e2f3c41f3c1e900be9d34 Mon Sep 17 00:00:00 2001 -From: Matthew Anderson -Date: Wed, 26 Jul 2023 20:46:29 -0500 -Subject: [PATCH 03/21] Add force external orientation. - -Co-authored-by: Bouke Sybren Haarsma ---- - src/Backends/DRMBackend.cpp | 5 +++++ - src/main.cpp | 25 ++++++++++++++++++++++++- - src/main.hpp | 1 + - src/wlserver.cpp | 23 +++++++++++++++++++++++ - 4 files changed, 53 insertions(+), 1 deletion(-) - -diff --git a/src/Backends/DRMBackend.cpp b/src/Backends/DRMBackend.cpp -index 799fd39..3815b98 100644 ---- a/src/Backends/DRMBackend.cpp -+++ b/src/Backends/DRMBackend.cpp -@@ -537,6 +537,7 @@ bool g_bSupportsSyncObjs = false; - - extern gamescope::GamescopeModeGeneration g_eGamescopeModeGeneration; - extern GamescopePanelOrientation g_DesiredInternalOrientation; -+extern GamescopePanelOrientation g_DesiredExternalOrientation; - - extern bool g_bForceDisableColorMgmt; - -@@ -2031,6 +2032,10 @@ namespace gamescope - { - m_ChosenOrientation = g_DesiredInternalOrientation; - } -+ else if ( this->GetScreenType() == GAMESCOPE_SCREEN_TYPE_EXTERNAL && g_DesiredExternalOrientation != GAMESCOPE_PANEL_ORIENTATION_AUTO ) -+ { -+ m_ChosenOrientation = g_DesiredExternalOrientation; -+ } - else - { - if ( this->GetProperties().panel_orientation ) -diff --git a/src/main.cpp b/src/main.cpp -index 534779d..f9be05e 100644 ---- a/src/main.cpp -+++ b/src/main.cpp -@@ -128,6 +128,7 @@ const struct option *gamescope_options = (struct option[]){ - { "disable-xres", no_argument, nullptr, 'x' }, - { "fade-out-duration", required_argument, nullptr, 0 }, - { "force-orientation", required_argument, nullptr, 0 }, -+ { "force-external-orientation", required_argument, nullptr, 0 }, - { "force-windows-fullscreen", no_argument, nullptr, 0 }, - { "bypass-steam-resolution", no_argument, nullptr, 0 }, - -@@ -194,6 +195,7 @@ const char usage[] = - " --xwayland-count create N xwayland servers\n" - " --prefer-vk-device prefer Vulkan device for compositing (ex: 1002:7300)\n" - " --force-orientation rotate the internal display (left, right, normal, upsidedown)\n" -+ " --force-external-orientation rotate the external display (left, right, normal, upsidedown)\n" - " --force-windows-fullscreen force windows inside of gamescope to be the size of the nested display (fullscreen)\n" - " --cursor-scale-height if specified, sets a base output height to linearly scale the cursor against.\n" - " --hdr-enabled enable HDR output (needs Gamescope WSI layer enabled for support from clients)\n" -@@ -289,6 +291,8 @@ bool g_bOutputHDREnabled = false; - bool g_bFullscreen = false; - bool g_bForceRelativeMouse = false; - -+bool g_bExternalForced = false; -+ - bool g_bGrabbed = false; - - float g_mouseSensitivity = 1.0; -@@ -362,7 +366,24 @@ static GamescopePanelOrientation force_orientation(const char *str) - } else if (strcmp(str, "upsidedown") == 0) { - return GAMESCOPE_PANEL_ORIENTATION_180; - } else { -- fprintf( stderr, "gamescope: invalid value for --force-orientation\n" ); -+ fprintf( stderr, "gamescope: invalid value for given for --force-orientation\n" ); -+ exit(1); -+ } -+} -+ -+GamescopePanelOrientation g_DesiredExternalOrientation = GAMESCOPE_PANEL_ORIENTATION_AUTO; -+static GamescopePanelOrientation force_external_orientation(const char *str) -+{ -+ if (strcmp(str, "normal") == 0) { -+ return GAMESCOPE_PANEL_ORIENTATION_0; -+ } else if (strcmp(str, "right") == 0) { -+ return GAMESCOPE_PANEL_ORIENTATION_270; -+ } else if (strcmp(str, "left") == 0) { -+ return GAMESCOPE_PANEL_ORIENTATION_90; -+ } else if (strcmp(str, "upsidedown") == 0) { -+ return GAMESCOPE_PANEL_ORIENTATION_180; -+ } else { -+ fprintf( stderr, "gamescope: invalid value for --force-external-orientation\n" ); - exit(1); - } - } -@@ -755,6 +776,8 @@ int main(int argc, char **argv) - g_eGamescopeModeGeneration = parse_gamescope_mode_generation( optarg ); - } else if (strcmp(opt_name, "force-orientation") == 0) { - g_DesiredInternalOrientation = force_orientation( optarg ); -+ } else if (strcmp(opt_name, "force-external-orientation") == 0) { -+ g_DesiredExternalOrientation = force_external_orientation( optarg ); - } else if (strcmp(opt_name, "sharpness") == 0 || - strcmp(opt_name, "fsr-sharpness") == 0) { - g_upscaleFilterSharpness = atoi( optarg ); -diff --git a/src/main.hpp b/src/main.hpp -index 2e6fb83..ebd018a 100644 ---- a/src/main.hpp -+++ b/src/main.hpp -@@ -28,6 +28,7 @@ extern bool g_bGrabbed; - - extern float g_mouseSensitivity; - extern const char *g_sOutputName; -+extern bool g_bExternalForced; - - enum class GamescopeUpscaleFilter : uint32_t - { -diff --git a/src/wlserver.cpp b/src/wlserver.cpp -index 26b953a..837079a 100644 ---- a/src/wlserver.cpp -+++ b/src/wlserver.cpp -@@ -2488,6 +2488,29 @@ static void apply_touchscreen_orientation(double *x, double *y ) - break; - } - -+ // Rotate screen if it's forced with --force-external-orientation -+ switch ( GetBackend()->GetConnector( gamescope::GAMESCOPE_SCREEN_TYPE_EXTERNAL )->GetCurrentOrientation() ) -+ { -+ default: -+ case GAMESCOPE_PANEL_ORIENTATION_AUTO: -+ case GAMESCOPE_PANEL_ORIENTATION_0: -+ tx = *x; -+ ty = *y; -+ break; -+ case GAMESCOPE_PANEL_ORIENTATION_90: -+ tx = 1.0 - *y; -+ ty = *x; -+ break; -+ case GAMESCOPE_PANEL_ORIENTATION_180: -+ tx = 1.0 - *x; -+ ty = 1.0 - *y; -+ break; -+ case GAMESCOPE_PANEL_ORIENTATION_270: -+ tx = *y; -+ ty = 1.0 - *x; -+ break; -+ } -+ - *x = tx; - *y = ty; - } --- -2.46.0 - - -From bc0ddd46fd9db62fbc3fe14e0b1493e35ceb2be8 Mon Sep 17 00:00:00 2001 -From: Bouke Sybren Haarsma -Date: Tue, 12 Mar 2024 00:07:57 +0100 -Subject: [PATCH 04/21] implement force-panel-type - ---- - src/backend.h | 3 +++ - src/gamescope_shared.h | 1 + - src/main.cpp | 16 ++++++++++++++++ - 3 files changed, 20 insertions(+) - -diff --git a/src/backend.h b/src/backend.h -index 7d9fb46..08e8268 100644 ---- a/src/backend.h -+++ b/src/backend.h -@@ -18,6 +18,7 @@ struct wlr_buffer; - struct wlr_dmabuf_attributes; - - struct FrameInfo_t; -+extern gamescope::GamescopeScreenType g_ForcedScreenType; - - namespace gamescope - { -@@ -221,6 +222,8 @@ namespace gamescope - // Dumb helper we should remove to support multi display someday. - gamescope::GamescopeScreenType GetScreenType() - { -+ if (g_ForcedScreenType != GAMESCOPE_SCREEN_TYPE_AUTO) -+ return g_ForcedScreenType; - if ( GetCurrentConnector() ) - return GetCurrentConnector()->GetScreenType(); - -diff --git a/src/gamescope_shared.h b/src/gamescope_shared.h -index 5ce8591..d1b7a6e 100644 ---- a/src/gamescope_shared.h -+++ b/src/gamescope_shared.h -@@ -25,6 +25,7 @@ namespace gamescope - { - GAMESCOPE_SCREEN_TYPE_INTERNAL, - GAMESCOPE_SCREEN_TYPE_EXTERNAL, -+ GAMESCOPE_SCREEN_TYPE_AUTO, - - GAMESCOPE_SCREEN_TYPE_COUNT - }; -diff --git a/src/main.cpp b/src/main.cpp -index f9be05e..dc3e9c8 100644 ---- a/src/main.cpp -+++ b/src/main.cpp -@@ -129,6 +129,7 @@ const struct option *gamescope_options = (struct option[]){ - { "fade-out-duration", required_argument, nullptr, 0 }, - { "force-orientation", required_argument, nullptr, 0 }, - { "force-external-orientation", required_argument, nullptr, 0 }, -+ { "force-panel-type", required_argument, nullptr, 0 }, - { "force-windows-fullscreen", no_argument, nullptr, 0 }, - { "bypass-steam-resolution", no_argument, nullptr, 0 }, - -@@ -196,6 +197,7 @@ const char usage[] = - " --prefer-vk-device prefer Vulkan device for compositing (ex: 1002:7300)\n" - " --force-orientation rotate the internal display (left, right, normal, upsidedown)\n" - " --force-external-orientation rotate the external display (left, right, normal, upsidedown)\n" -+ " --force-panel-type force gamescope to treat the display as either internal or external\n" - " --force-windows-fullscreen force windows inside of gamescope to be the size of the nested display (fullscreen)\n" - " --cursor-scale-height if specified, sets a base output height to linearly scale the cursor against.\n" - " --hdr-enabled enable HDR output (needs Gamescope WSI layer enabled for support from clients)\n" -@@ -387,6 +389,18 @@ static GamescopePanelOrientation force_external_orientation(const char *str) - exit(1); - } - } -+gamescope::GamescopeScreenType g_ForcedScreenType = gamescope::GAMESCOPE_SCREEN_TYPE_AUTO; -+static gamescope::GamescopeScreenType force_panel_type(const char *str) -+{ -+ if (strcmp(str, "internal") == 0) { -+ return gamescope::GAMESCOPE_SCREEN_TYPE_INTERNAL; -+ } else if (strcmp(str, "external") == 0) { -+ return gamescope::GAMESCOPE_SCREEN_TYPE_EXTERNAL; -+ } else { -+ fprintf( stderr, "gamescope: invalid value for --force-panel-type\n" ); -+ exit(1); -+ } -+} - - static enum GamescopeUpscaleScaler parse_upscaler_scaler(const char *str) - { -@@ -778,6 +792,8 @@ int main(int argc, char **argv) - g_DesiredInternalOrientation = force_orientation( optarg ); - } else if (strcmp(opt_name, "force-external-orientation") == 0) { - g_DesiredExternalOrientation = force_external_orientation( optarg ); -+ } else if (strcmp(opt_name, "force-panel-type") == 0) { -+ g_ForcedScreenType = force_panel_type( optarg ); - } else if (strcmp(opt_name, "sharpness") == 0 || - strcmp(opt_name, "fsr-sharpness") == 0) { - g_upscaleFilterSharpness = atoi( optarg ); --- -2.46.0 - - -From 2a0c92febded984bc0d610eaedc9e8cc4d4ce51a Mon Sep 17 00:00:00 2001 -From: Matthew Anderson -Date: Fri, 17 May 2024 21:11:34 -0500 -Subject: [PATCH 05/21] wlserver: Fix an issue that would cause gamescope to - crash when the touchscreen was used - ---- - src/wlserver.cpp | 23 ----------------------- - 1 file changed, 23 deletions(-) - -diff --git a/src/wlserver.cpp b/src/wlserver.cpp -index 837079a..26b953a 100644 ---- a/src/wlserver.cpp -+++ b/src/wlserver.cpp -@@ -2488,29 +2488,6 @@ static void apply_touchscreen_orientation(double *x, double *y ) - break; - } - -- // Rotate screen if it's forced with --force-external-orientation -- switch ( GetBackend()->GetConnector( gamescope::GAMESCOPE_SCREEN_TYPE_EXTERNAL )->GetCurrentOrientation() ) -- { -- default: -- case GAMESCOPE_PANEL_ORIENTATION_AUTO: -- case GAMESCOPE_PANEL_ORIENTATION_0: -- tx = *x; -- ty = *y; -- break; -- case GAMESCOPE_PANEL_ORIENTATION_90: -- tx = 1.0 - *y; -- ty = *x; -- break; -- case GAMESCOPE_PANEL_ORIENTATION_180: -- tx = 1.0 - *x; -- ty = 1.0 - *y; -- break; -- case GAMESCOPE_PANEL_ORIENTATION_270: -- tx = *y; -- ty = 1.0 - *x; -- break; -- } -- - *x = tx; - *y = ty; - } --- -2.46.0 - - -From 69bae3bffa954251d6a857b1b6928fc4755e40bf Mon Sep 17 00:00:00 2001 -From: Matthew Anderson -Date: Fri, 17 May 2024 21:56:55 -0500 -Subject: [PATCH 06/21] Add --custom-refresh-rates - ---- - src/Backends/DRMBackend.cpp | 4 ++++ - src/main.cpp | 30 ++++++++++++++++++++++++++++++ - src/main.hpp | 2 ++ - 3 files changed, 36 insertions(+) - -diff --git a/src/Backends/DRMBackend.cpp b/src/Backends/DRMBackend.cpp -index 3815b98..e3512a1 100644 ---- a/src/Backends/DRMBackend.cpp -+++ b/src/Backends/DRMBackend.cpp -@@ -2135,6 +2135,10 @@ namespace gamescope - ( m_Mutable.szMakePNP == "VLV"sv && m_Mutable.szModel == "Jupiter"sv ) || - ( m_Mutable.szMakePNP == "VLV"sv && m_Mutable.szModel == "Galileo"sv ); - -+ if ( g_customRefreshRates.size() > 0 ) { -+ m_Mutable.ValidDynamicRefreshRates = std::span(g_customRefreshRates); -+ return; -+ } - if ( bSteamDeckDisplay ) - { - static constexpr uint32_t kPIDGalileoSDC = 0x3003; -diff --git a/src/main.cpp b/src/main.cpp -index dc3e9c8..18eb399 100644 ---- a/src/main.cpp -+++ b/src/main.cpp -@@ -132,6 +132,7 @@ const struct option *gamescope_options = (struct option[]){ - { "force-panel-type", required_argument, nullptr, 0 }, - { "force-windows-fullscreen", no_argument, nullptr, 0 }, - { "bypass-steam-resolution", no_argument, nullptr, 0 }, -+ { "custom-refresh-rates", required_argument, nullptr, 0 }, - - - { "disable-color-management", no_argument, nullptr, 0 }, -@@ -210,6 +211,7 @@ const char usage[] = - " --hdr-itm-target-nits set the target luminace of the inverse tone mapping process.\n" - " Default: 1000 nits, Max: 10000 nits\n" - " --framerate-limit Set a simple framerate limit. Used as a divisor of the refresh rate, rounds down eg 60 / 59 -> 60fps, 60 / 25 -> 30fps. Default: 0, disabled.\n" -+ " --custom-refresh-rates Set custom refresh rates for the output. eg: 60,90,110-120\n" - " --mangoapp Launch with the mangoapp (mangohud) performance overlay enabled. You should use this instead of using mangohud on the game or gamescope.\n" - "\n" - "Nested mode options:\n" -@@ -462,6 +464,32 @@ static enum gamescope::GamescopeBackend parse_backend_name(const char *str) - fprintf( stderr, "gamescope: invalid value for --backend\n" ); - exit(1); - } -+ -+std::vector g_customRefreshRates; -+// eg: 60,60,90,110-120 -+static std::vector parse_custom_refresh_rates( const char *str ) -+{ -+ std::vector rates; -+ char *token = strtok( strdup(str), ","); -+ while (token) -+ { -+ char *dash = strchr(token, '-'); -+ if (dash) -+ { -+ uint32_t start = atoi(token); -+ uint32_t end = atoi(dash + 1); -+ for (uint32_t i = start; i <= end; i++) -+ { -+ rates.push_back(i); -+ } -+ } -+ else -+ { -+ rates.push_back(atoi(token)); -+ } -+ token = strtok(nullptr, ","); -+ } -+ return rates; - } - - struct sigaction handle_signal_action = {}; -@@ -794,6 +822,8 @@ int main(int argc, char **argv) - g_DesiredExternalOrientation = force_external_orientation( optarg ); - } else if (strcmp(opt_name, "force-panel-type") == 0) { - g_ForcedScreenType = force_panel_type( optarg ); -+ } else if (strcmp(opt_name, "custom-refresh-rates") == 0) { -+ g_customRefreshRates = parse_custom_refresh_rates( optarg ); - } else if (strcmp(opt_name, "sharpness") == 0 || - strcmp(opt_name, "fsr-sharpness") == 0) { - g_upscaleFilterSharpness = atoi( optarg ); -diff --git a/src/main.hpp b/src/main.hpp -index ebd018a..4e09e3b 100644 ---- a/src/main.hpp -+++ b/src/main.hpp -@@ -3,6 +3,7 @@ - #include - - #include -+#include - - extern const char *gamescope_optstring; - extern const struct option *gamescope_options; -@@ -29,6 +30,7 @@ extern bool g_bGrabbed; - extern float g_mouseSensitivity; - extern const char *g_sOutputName; - extern bool g_bExternalForced; -+extern std::vector g_customRefreshRates; - - enum class GamescopeUpscaleFilter : uint32_t - { --- -2.46.0 - - -From 5125582bc6fdfc651ca5ccb58cb71d956e2c3676 Mon Sep 17 00:00:00 2001 -From: Matthew Anderson -Date: Sat, 18 May 2024 08:44:38 -0500 -Subject: [PATCH 07/21] Add rotation gamescope_control command - ---- - protocol/gamescope-control.xml | 18 ++++++++++++ - src/Backends/DRMBackend.cpp | 24 ++++++++++++++-- - src/gamescope_shared.h | 18 ++++++++++++ - src/main.cpp | 1 + - src/wlserver.cpp | 50 ++++++++++++++++++++++++++++++++++ - 5 files changed, 109 insertions(+), 2 deletions(-) - -diff --git a/protocol/gamescope-control.xml b/protocol/gamescope-control.xml -index 012c48c..eab8a84 100644 ---- a/protocol/gamescope-control.xml -+++ b/protocol/gamescope-control.xml -@@ -99,5 +99,23 @@ - - - -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ - - -diff --git a/src/Backends/DRMBackend.cpp b/src/Backends/DRMBackend.cpp -index e3512a1..f05c2e8 100644 ---- a/src/Backends/DRMBackend.cpp -+++ b/src/Backends/DRMBackend.cpp -@@ -2028,7 +2028,9 @@ namespace gamescope - - void CDRMConnector::UpdateEffectiveOrientation( const drmModeModeInfo *pMode ) - { -- if ( this->GetScreenType() == GAMESCOPE_SCREEN_TYPE_INTERNAL && g_DesiredInternalOrientation != GAMESCOPE_PANEL_ORIENTATION_AUTO ) -+ if ( this->GetScreenType() == ( GAMESCOPE_SCREEN_TYPE_INTERNAL && g_DesiredInternalOrientation != GAMESCOPE_PANEL_ORIENTATION_AUTO ) -+ || ( GAMESCOPE_SCREEN_TYPE_EXTERNAL && g_DesiredExternalOrientation != GAMESCOPE_PANEL_ORIENTATION_AUTO -+ && g_bExternalForced ) ) - { - m_ChosenOrientation = g_DesiredInternalOrientation; - } -@@ -2960,8 +2962,26 @@ bool drm_update_color_mgmt(struct drm_t *drm) - return true; - } - --int g_nDynamicRefreshHz = 0; -+void drm_set_orientation( struct drm_t *drm, bool isRotated) -+{ -+ int width = g_nOutputWidth; -+ int height = g_nOutputHeight; -+ g_bRotated = isRotated; -+ if ( g_bRotated ) { -+ int tmp = width; -+ width = height; -+ height = tmp; -+ } - -+ if (!drm->pConnector || !drm->pConnector->GetModeConnector()) -+ return; -+ -+ drmModeConnector *connector = drm->pConnector->GetModeConnector(); -+ const drmModeModeInfo *mode = find_mode(connector, width, height, 0); -+ update_drm_effective_orientations(drm, mode); -+} -+ -+int g_nDynamicRefreshHz = 0; - static void drm_unset_mode( struct drm_t *drm ) - { - drm->pending.mode_id = 0; -diff --git a/src/gamescope_shared.h b/src/gamescope_shared.h -index d1b7a6e..cffa576 100644 ---- a/src/gamescope_shared.h -+++ b/src/gamescope_shared.h -@@ -65,6 +65,24 @@ enum GamescopePanelOrientation - GAMESCOPE_PANEL_ORIENTATION_AUTO, - }; - -+struct GamescopeTimelinePoint -+{ -+ struct wlr_drm_syncobj_timeline *pTimeline = nullptr; -+ uint64_t ulPoint = 0; -+ -+ void Release(); -+}; -+ -+enum GamescopePanelExternalOrientation -+{ -+ GAMESCOPE_PANEL_EXTERNAL_ORIENTATION_0, // normal -+ GAMESCOPE_PANEL_EXTERNAL_ORIENTATION_270, // right -+ GAMESCOPE_PANEL_EXTERNAL_ORIENTATION_90, // left -+ GAMESCOPE_PANEL_EXTERNAL_ORIENTATION_180, // upside down -+ -+ GAMESCOPE_PANEL_EXTERNAL_ORIENTATION_AUTO, -+}; -+ - // Disable partial composition for now until we get - // composite priorities working in libliftoff + also - // use the proper libliftoff composite plane system. -diff --git a/src/main.cpp b/src/main.cpp -index 18eb399..675020f 100644 ---- a/src/main.cpp -+++ b/src/main.cpp -@@ -397,6 +397,7 @@ static gamescope::GamescopeScreenType force_panel_type(const char *str) - if (strcmp(str, "internal") == 0) { - return gamescope::GAMESCOPE_SCREEN_TYPE_INTERNAL; - } else if (strcmp(str, "external") == 0) { -+ g_bExternalForced = true; - return gamescope::GAMESCOPE_SCREEN_TYPE_EXTERNAL; - } else { - fprintf( stderr, "gamescope: invalid value for --force-panel-type\n" ); -diff --git a/src/wlserver.cpp b/src/wlserver.cpp -index 26b953a..c37dbc1 100644 ---- a/src/wlserver.cpp -+++ b/src/wlserver.cpp -@@ -1042,6 +1042,55 @@ static void gamescope_control_take_screenshot( struct wl_client *client, struct - } ); - } - -+static void gamescope_control_rotate_display( struct wl_client *client, struct wl_resource *resource, uint32_t orientation, uint32_t target_type ) -+{ -+ bool isRotated = false; -+ if (target_type == GAMESCOPE_CONTROL_DISPLAY_TARGET_TYPE_INTERNAL ) -+ { -+ switch (orientation) { -+ case GAMESCOPE_CONTROL_DISPLAY_ROTATION_FLAG_NORMAL: -+ //m_ChosenOrientation = GAMESCOPE_PANEL_ORIENTATION_0; -+ break; -+ case GAMESCOPE_CONTROL_DISPLAY_ROTATION_FLAG_LEFT: -+ //m_ChosenOrientation = GAMESCOPE_PANEL_ORIENTATION_90; -+ isRotated = true; -+ break; -+ case GAMESCOPE_CONTROL_DISPLAY_ROTATION_FLAG_RIGHT: -+ //m_ChosenOrientation = GAMESCOPE_PANEL_ORIENTATION_270; -+ isRotated = true; -+ break; -+ case GAMESCOPE_CONTROL_DISPLAY_ROTATION_FLAG_UPSIDEDOWN: -+ //m_ChosenOrientation = GAMESCOPE_PANEL_ORIENTATION_180; -+ break; -+ default: -+ wl_log.errorf("Invalid target orientation selected"); -+ } -+ } -+ else if (target_type == GAMESCOPE_CONTROL_DISPLAY_TARGET_TYPE_EXTERNAL ) -+ { -+ switch (orientation) { -+ case GAMESCOPE_CONTROL_DISPLAY_ROTATION_FLAG_NORMAL: -+ //m_ChosenOrientation = GAMESCOPE_PANEL_EXTERNAL_ORIENTATION_0; -+ break; -+ case GAMESCOPE_CONTROL_DISPLAY_ROTATION_FLAG_LEFT: -+ //m_ChosenOrientation = GAMESCOPE_PANEL_EXTERNAL_ORIENTATION_90; -+ isRotated = true; -+ break; -+ case GAMESCOPE_CONTROL_DISPLAY_ROTATION_FLAG_RIGHT: -+ //m_ChosenOrientation = GAMESCOPE_PANEL_EXTERNAL_ORIENTATION_270; -+ isRotated = true; -+ break; -+ case GAMESCOPE_CONTROL_DISPLAY_ROTATION_FLAG_UPSIDEDOWN: -+ //m_ChosenOrientation = GAMESCOPE_PANEL_EXTERNAL_ORIENTATION_180; -+ break; -+ default: -+ wl_log.errorf("Invalid target orientation selected"); -+ } -+ } -+ //drm_set_orientation(&g_DRM, isRotated); -+ //g_DRM.out_of_date = 2; -+} -+ - static void gamescope_control_handle_destroy( struct wl_client *client, struct wl_resource *resource ) - { - wl_resource_destroy( resource ); -@@ -1051,6 +1100,7 @@ static const struct gamescope_control_interface gamescope_control_impl = { - .destroy = gamescope_control_handle_destroy, - .set_app_target_refresh_cycle = gamescope_control_set_app_target_refresh_cycle, - .take_screenshot = gamescope_control_take_screenshot, -+ .rotate_display = gamescope_control_rotate_display, - }; - - static uint32_t get_conn_display_info_flags() --- -2.46.0 - - -From 304c747d5eab4b62fe38225d04529ff53142e832 Mon Sep 17 00:00:00 2001 -From: Matthew Anderson -Date: Sat, 18 May 2024 11:54:50 -0500 -Subject: [PATCH 08/21] Fix an issue that caused force-panel to not work - ---- - protocol/gamescope-control.xml | 1 - - src/Backends/DRMBackend.cpp | 3 + - src/gamescope_shared.h | 10 --- - src/wlserver.cpp | 145 ++++++++++++++++++++------------- - 4 files changed, 90 insertions(+), 69 deletions(-) - -diff --git a/protocol/gamescope-control.xml b/protocol/gamescope-control.xml -index eab8a84..7f5578b 100644 ---- a/protocol/gamescope-control.xml -+++ b/protocol/gamescope-control.xml -@@ -99,7 +99,6 @@ - - - -- - - - -diff --git a/src/Backends/DRMBackend.cpp b/src/Backends/DRMBackend.cpp -index f05c2e8..663b1e2 100644 ---- a/src/Backends/DRMBackend.cpp -+++ b/src/Backends/DRMBackend.cpp -@@ -318,6 +318,9 @@ namespace gamescope - - GamescopeScreenType GetScreenType() const override - { -+ if ( g_ForcedScreenType != GAMESCOPE_SCREEN_TYPE_AUTO ) -+ return g_ForcedScreenType; -+ - if ( m_pConnector->connector_type == DRM_MODE_CONNECTOR_eDP || - m_pConnector->connector_type == DRM_MODE_CONNECTOR_LVDS || - m_pConnector->connector_type == DRM_MODE_CONNECTOR_DSI ) -diff --git a/src/gamescope_shared.h b/src/gamescope_shared.h -index cffa576..a11f598 100644 ---- a/src/gamescope_shared.h -+++ b/src/gamescope_shared.h -@@ -73,16 +73,6 @@ struct GamescopeTimelinePoint - void Release(); - }; - --enum GamescopePanelExternalOrientation --{ -- GAMESCOPE_PANEL_EXTERNAL_ORIENTATION_0, // normal -- GAMESCOPE_PANEL_EXTERNAL_ORIENTATION_270, // right -- GAMESCOPE_PANEL_EXTERNAL_ORIENTATION_90, // left -- GAMESCOPE_PANEL_EXTERNAL_ORIENTATION_180, // upside down -- -- GAMESCOPE_PANEL_EXTERNAL_ORIENTATION_AUTO, --}; -- - // Disable partial composition for now until we get - // composite priorities working in libliftoff + also - // use the proper libliftoff composite plane system. -diff --git a/src/wlserver.cpp b/src/wlserver.cpp -index c37dbc1..ff534ad 100644 ---- a/src/wlserver.cpp -+++ b/src/wlserver.cpp -@@ -82,6 +82,8 @@ extern gamescope::ConVar cv_drm_debug_disable_explicit_sync; - - //#define GAMESCOPE_SWAPCHAIN_DEBUG - gamescope::ConVar cv_touch_gestures( "enable_touch_gestures", false, "Enable/Disable the usage of touch gestures" ); -+extern GamescopePanelOrientation g_DesiredInternalOrientation; -+extern GamescopePanelOrientation g_DesiredExternalOrientation; - - struct wlserver_t wlserver = { - .touch_down_ids = {} -@@ -1048,43 +1050,43 @@ static void gamescope_control_rotate_display( struct wl_client *client, struct w - if (target_type == GAMESCOPE_CONTROL_DISPLAY_TARGET_TYPE_INTERNAL ) - { - switch (orientation) { -- case GAMESCOPE_CONTROL_DISPLAY_ROTATION_FLAG_NORMAL: -- //m_ChosenOrientation = GAMESCOPE_PANEL_ORIENTATION_0; -- break; -- case GAMESCOPE_CONTROL_DISPLAY_ROTATION_FLAG_LEFT: -- //m_ChosenOrientation = GAMESCOPE_PANEL_ORIENTATION_90; -- isRotated = true; -- break; -- case GAMESCOPE_CONTROL_DISPLAY_ROTATION_FLAG_RIGHT: -- //m_ChosenOrientation = GAMESCOPE_PANEL_ORIENTATION_270; -- isRotated = true; -- break; -- case GAMESCOPE_CONTROL_DISPLAY_ROTATION_FLAG_UPSIDEDOWN: -- //m_ChosenOrientation = GAMESCOPE_PANEL_ORIENTATION_180; -- break; -- default: -- wl_log.errorf("Invalid target orientation selected"); -+ case GAMESCOPE_CONTROL_DISPLAY_ROTATION_FLAG_NORMAL: -+ g_DesiredInternalOrientation = GAMESCOPE_PANEL_ORIENTATION_0; -+ break; -+ case GAMESCOPE_CONTROL_DISPLAY_ROTATION_FLAG_LEFT: -+ g_DesiredInternalOrientation = GAMESCOPE_PANEL_ORIENTATION_90; -+ isRotated = true; -+ break; -+ case GAMESCOPE_CONTROL_DISPLAY_ROTATION_FLAG_RIGHT: -+ g_DesiredInternalOrientation = GAMESCOPE_PANEL_ORIENTATION_270; -+ isRotated = true; -+ break; -+ case GAMESCOPE_CONTROL_DISPLAY_ROTATION_FLAG_UPSIDEDOWN: -+ g_DesiredInternalOrientation = GAMESCOPE_PANEL_ORIENTATION_180; -+ break; -+ default: -+ wl_log.errorf("Invalid target orientation selected"); - } - } - else if (target_type == GAMESCOPE_CONTROL_DISPLAY_TARGET_TYPE_EXTERNAL ) - { - switch (orientation) { -- case GAMESCOPE_CONTROL_DISPLAY_ROTATION_FLAG_NORMAL: -- //m_ChosenOrientation = GAMESCOPE_PANEL_EXTERNAL_ORIENTATION_0; -- break; -- case GAMESCOPE_CONTROL_DISPLAY_ROTATION_FLAG_LEFT: -- //m_ChosenOrientation = GAMESCOPE_PANEL_EXTERNAL_ORIENTATION_90; -- isRotated = true; -- break; -- case GAMESCOPE_CONTROL_DISPLAY_ROTATION_FLAG_RIGHT: -- //m_ChosenOrientation = GAMESCOPE_PANEL_EXTERNAL_ORIENTATION_270; -- isRotated = true; -- break; -- case GAMESCOPE_CONTROL_DISPLAY_ROTATION_FLAG_UPSIDEDOWN: -- //m_ChosenOrientation = GAMESCOPE_PANEL_EXTERNAL_ORIENTATION_180; -- break; -- default: -- wl_log.errorf("Invalid target orientation selected"); -+ case GAMESCOPE_CONTROL_DISPLAY_ROTATION_FLAG_NORMAL: -+ g_DesiredExternalOrientation = GAMESCOPE_PANEL_ORIENTATION_0; -+ break; -+ case GAMESCOPE_CONTROL_DISPLAY_ROTATION_FLAG_LEFT: -+ g_DesiredExternalOrientation = GAMESCOPE_PANEL_ORIENTATION_90; -+ isRotated = true; -+ break; -+ case GAMESCOPE_CONTROL_DISPLAY_ROTATION_FLAG_RIGHT: -+ g_DesiredExternalOrientation = GAMESCOPE_PANEL_ORIENTATION_270; -+ isRotated = true; -+ break; -+ case GAMESCOPE_CONTROL_DISPLAY_ROTATION_FLAG_UPSIDEDOWN: -+ g_DesiredExternalOrientation = GAMESCOPE_PANEL_ORIENTATION_180; -+ break; -+ default: -+ wl_log.errorf("Invalid target orientation selected"); - } - } - //drm_set_orientation(&g_DRM, isRotated); -@@ -2512,34 +2514,61 @@ const std::shared_ptr& wlserver_surface_swapchai - /* Handle the orientation of the touch inputs */ - static void apply_touchscreen_orientation(double *x, double *y ) - { -- double tx = 0; -- double ty = 0; -+ double tx = 0; -+ double ty = 0; - -- // Use internal screen always for orientation purposes. -- switch ( GetBackend()->GetConnector( gamescope::GAMESCOPE_SCREEN_TYPE_INTERNAL )->GetCurrentOrientation() ) -- { -- default: -- case GAMESCOPE_PANEL_ORIENTATION_AUTO: -- case GAMESCOPE_PANEL_ORIENTATION_0: -- tx = *x; -- ty = *y; -- break; -- case GAMESCOPE_PANEL_ORIENTATION_90: -- tx = 1.0 - *y; -- ty = *x; -- break; -- case GAMESCOPE_PANEL_ORIENTATION_180: -- tx = 1.0 - *x; -- ty = 1.0 - *y; -- break; -- case GAMESCOPE_PANEL_ORIENTATION_270: -- tx = *y; -- ty = 1.0 - *x; -- break; -- } -+ // Use internal screen always for orientation purposes. -+ if ( g_ForcedScreenType == gamescope::GAMESCOPE_SCREEN_TYPE_INTERNAL || g_ForcedScreenType == gamescope::GAMESCOPE_SCREEN_TYPE_EXTERNAL ) -+ { -+ switch ( g_DesiredInternalOrientation ) -+ { -+ default: -+ case GAMESCOPE_PANEL_ORIENTATION_AUTO: -+ case GAMESCOPE_PANEL_ORIENTATION_0: -+ tx = *x; -+ ty = *y; -+ break; -+ case GAMESCOPE_PANEL_ORIENTATION_90: -+ tx = 1.0 - *y; -+ ty = *x; -+ break; -+ case GAMESCOPE_PANEL_ORIENTATION_180: -+ tx = 1.0 - *x; -+ ty = 1.0 - *y; -+ break; -+ case GAMESCOPE_PANEL_ORIENTATION_270: -+ tx = *y; -+ ty = 1.0 - *x; -+ break; -+ } -+ } -+ else if (g_ForcedScreenType == gamescope::GAMESCOPE_SCREEN_TYPE_AUTO) -+ { -+ switch (GetBackend()->GetConnector(gamescope::GAMESCOPE_SCREEN_TYPE_INTERNAL)->GetCurrentOrientation()) -+ { -+ default: -+ case GAMESCOPE_PANEL_ORIENTATION_AUTO: -+ case GAMESCOPE_PANEL_ORIENTATION_0: -+ tx = *x; -+ ty = *y; -+ break; -+ case GAMESCOPE_PANEL_ORIENTATION_90: -+ tx = 1.0 - *y; -+ ty = *x; -+ break; -+ case GAMESCOPE_PANEL_ORIENTATION_180: -+ tx = 1.0 - *x; -+ ty = 1.0 - *y; -+ break; -+ case GAMESCOPE_PANEL_ORIENTATION_270: -+ tx = *y; -+ ty = 1.0 - *x; -+ break; -+ } -+ } - -- *x = tx; -- *y = ty; -+ *x = tx; -+ *y = ty; - } - - void wlserver_touchmotion( double x, double y, int touch_id, uint32_t time, bool bAlwaysWarpCursor ) --- -2.46.0 - - -From 3efb4df00c1e88219a3ff7c47af99b7217d1fe95 Mon Sep 17 00:00:00 2001 -From: Matthew Anderson -Date: Sat, 18 May 2024 13:50:57 -0500 -Subject: [PATCH 09/21] Fix an arithmetic error - ---- - src/Backends/DRMBackend.cpp | 6 +++--- - 1 file changed, 3 insertions(+), 3 deletions(-) - -diff --git a/src/Backends/DRMBackend.cpp b/src/Backends/DRMBackend.cpp -index 663b1e2..eafb2f9 100644 ---- a/src/Backends/DRMBackend.cpp -+++ b/src/Backends/DRMBackend.cpp -@@ -2031,9 +2031,9 @@ namespace gamescope - - void CDRMConnector::UpdateEffectiveOrientation( const drmModeModeInfo *pMode ) - { -- if ( this->GetScreenType() == ( GAMESCOPE_SCREEN_TYPE_INTERNAL && g_DesiredInternalOrientation != GAMESCOPE_PANEL_ORIENTATION_AUTO ) -- || ( GAMESCOPE_SCREEN_TYPE_EXTERNAL && g_DesiredExternalOrientation != GAMESCOPE_PANEL_ORIENTATION_AUTO -- && g_bExternalForced ) ) -+ if ( this->GetScreenType() == GAMESCOPE_SCREEN_TYPE_INTERNAL && g_DesiredInternalOrientation != GAMESCOPE_PANEL_ORIENTATION_AUTO -+ || GAMESCOPE_SCREEN_TYPE_EXTERNAL && g_DesiredExternalOrientation != GAMESCOPE_PANEL_ORIENTATION_AUTO -+ && g_bExternalForced ) - { - m_ChosenOrientation = g_DesiredInternalOrientation; - } --- -2.46.0 - - -From dd53e4d18b122cae5383d77fc3f6e79084d8993f Mon Sep 17 00:00:00 2001 -From: Matthew Anderson -Date: Sat, 18 May 2024 19:04:48 -0500 -Subject: [PATCH 10/21] Rework the touch gestures to be more smooth - ---- - src/wlserver.cpp | 89 +++++++++++++++++++++++++++++++++++++----------- - 1 file changed, 69 insertions(+), 20 deletions(-) - -diff --git a/src/wlserver.cpp b/src/wlserver.cpp -index ff534ad..593a074 100644 ---- a/src/wlserver.cpp -+++ b/src/wlserver.cpp -@@ -80,6 +80,8 @@ using namespace std::literals; - - extern gamescope::ConVar cv_drm_debug_disable_explicit_sync; - -+bool pending_gesture = false; -+bool pending_osk = false; - //#define GAMESCOPE_SWAPCHAIN_DEBUG - gamescope::ConVar cv_touch_gestures( "enable_touch_gestures", false, "Enable/Disable the usage of touch gestures" ); - extern GamescopePanelOrientation g_DesiredInternalOrientation; -@@ -362,6 +364,39 @@ void wlserver_open_steam_menu( bool qam ) - XTestFakeKeyEvent(server->get_xdisplay(), XKeysymToKeycode( server->get_xdisplay(), XK_Control_L ), False, CurrentTime); - } - -+void wlserver_open_steam_osk(bool osk) -+{ -+ gamescope_xwayland_server_t *server = wlserver_get_xwayland_server( 0 ); -+ if (!server) -+ return; -+ -+ uint32_t osk_open = osk; -+ -+ if (osk_open) -+ { -+ const char *command = "xdg-open steam://open/keyboard?"; -+ int result = system(command); -+ if (result == 0) { -+ printf("Command executed successfully.\n"); -+ } else { -+ printf("Error executing command.\n"); -+ } -+ pending_osk = false; -+ } -+ else -+ { -+ const char *command = "xdg-open steam://close/keyboard?"; -+ int result = system(command); -+ if (result == 0) { -+ printf("Command executed successfully.\n"); -+ } else { -+ printf("Error executing command.\n"); -+ } -+ pending_osk = false; -+ } -+ -+} -+ - static void wlserver_handle_pointer_button(struct wl_listener *listener, void *data) - { - struct wlserver_pointer *pointer = wl_container_of( listener, pointer, button ); -@@ -2607,32 +2642,46 @@ void wlserver_touchmotion( double x, double y, int touch_id, uint32_t time, bool - if ( bAlwaysWarpCursor ) - wlserver_mousewarp( tx, ty, time, false ); - -- if (cv_touch_gestures) { -- bool start_gesture = false; -- -- // Round the x-coordinate to the nearest whole number -+ if ( cv_touch_gestures ) -+ { - uint32_t roundedCursorX = static_cast(std::round(tx)); -- // Grab 2% of the display to be used for the edge range -- uint32_t edge_range = static_cast(g_nOutputWidth * 0.02); -- -- // Determine if the gesture should start -- if (roundedCursorX <= edge_range || roundedCursorX >= g_nOutputWidth - edge_range) { -- start_gesture = true; -- } -- -- // Handle Home gesture -- if (start_gesture && roundedCursorX >= edge_range) { -+ uint32_t roundedCursorY = static_cast(std::round(ty)); -+ uint32_t edge_range_x = static_cast(g_nOutputWidth * 0.02); -+ uint32_t edge_range_y = static_cast(g_nOutputWidth * 0.02); -+ uint32_t gesture_limits_x = edge_range_x * 2; -+ uint32_t gesture_limits_y = edge_range_y * 2; -+ -+ // Left to Right and Right to Left -+ if (!pending_gesture && roundedCursorX >= 1 && roundedCursorX < edge_range_x || -+ !pending_gesture && roundedCursorX >= g_nOutputWidth - edge_range_x ) -+ pending_gesture = true; -+ -+ //left -+ if (pending_gesture && roundedCursorX >= edge_range_x && roundedCursorX < gesture_limits_x) { - wlserver_open_steam_menu(0); -- start_gesture = false; -+ pending_gesture = false; - } -- -- // Handle QAM gesture -- if (start_gesture && roundedCursorX >= g_nOutputWidth - edge_range && roundedCursorX <= g_nOutputWidth) { -+ //right -+ if (pending_gesture && roundedCursorX <= g_nOutputWidth - edge_range_x && roundedCursorX > g_nOutputWidth - gesture_limits_x) { - wlserver_open_steam_menu(1); -- start_gesture = false; -+ pending_gesture = false; -+ } -+ // Top to Bottom and Bottom to Top -+ if (!pending_gesture && roundedCursorY >= 1 && roundedCursorY < edge_range_y || -+ !pending_gesture && roundedCursorY >= g_nOutputHeight - edge_range_y ) -+ pending_gesture = true; -+ // Top -+ if (pending_gesture && roundedCursorY >= edge_range_y && roundedCursorY < gesture_limits_y) { -+ pending_gesture = false; -+ // Top to Bottom function to add -+ } -+ // Bottom -+ if (pending_gesture && !pending_osk && roundedCursorY <= g_nOutputWidth - edge_range_y && roundedCursorY > g_nOutputHeight - gesture_limits_y) { -+ pending_gesture = false; -+ pending_osk = true; -+ //wlserver_open_steam_osk(1); - } - } -- - } - else if ( eMode == gamescope::TouchClickModes::Disabled ) - { --- -2.46.0 - - -From 70d4ea8937e43752e1adb32ebbcdd3d1a0bcf23c Mon Sep 17 00:00:00 2001 -From: Matthew Anderson -Date: Sun, 19 May 2024 08:55:28 -0500 -Subject: [PATCH 11/21] Fix a typo for --bypass-steam-resolution - ---- - src/steamcompmgr.cpp | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/src/steamcompmgr.cpp b/src/steamcompmgr.cpp -index 952a5f8..6fceb1d 100644 ---- a/src/steamcompmgr.cpp -+++ b/src/steamcompmgr.cpp -@@ -7379,7 +7379,7 @@ steamcompmgr_main(int argc, char **argv) - bForceWindowsFullscreen = true; - } else if (strcmp(opt_name, "hdr-enabled") == 0) { - cv_hdr_enabled = true; -- } else if (strcmp(opt_name, "bypass_steam_resolution") == 0) { -+ } else if (strcmp(opt_name, "bypass-steam-resolution") == 0) { - cv_bypass_steam_resolution = true; - } else if (strcmp(opt_name, "hdr-debug-force-support") == 0) { - g_bForceHDRSupportDebug = true; --- -2.46.0 - - -From be8e56d7f3bf251f75da24aa8a99d2e91aa60812 Mon Sep 17 00:00:00 2001 -From: Matthew Anderson -Date: Sun, 19 May 2024 11:48:52 -0500 -Subject: [PATCH 12/21] Handle gesture cases better to prevent unexpected - behavior - ---- - src/wlserver.cpp | 63 +++++++++++++++++++++++++++++++++--------------- - 1 file changed, 43 insertions(+), 20 deletions(-) - -diff --git a/src/wlserver.cpp b/src/wlserver.cpp -index 593a074..833140a 100644 ---- a/src/wlserver.cpp -+++ b/src/wlserver.cpp -@@ -80,7 +80,8 @@ using namespace std::literals; - - extern gamescope::ConVar cv_drm_debug_disable_explicit_sync; - --bool pending_gesture = false; -+bool pending_gesture_x = false; -+bool pending_gesture_y = false; - bool pending_osk = false; - //#define GAMESCOPE_SWAPCHAIN_DEBUG - gamescope::ConVar cv_touch_gestures( "enable_touch_gestures", false, "Enable/Disable the usage of touch gestures" ); -@@ -2606,6 +2607,16 @@ static void apply_touchscreen_orientation(double *x, double *y ) - *y = ty; - } - -+void wlserver_gesture_flush() -+{ -+ pending_gesture_x = false; -+ pending_gesture_y = false; -+} -+ -+// Variables to track the direction of the touch motion -+uint32_t previous_tx = 0; -+uint32_t previous_ty = 0; -+ - void wlserver_touchmotion( double x, double y, int touch_id, uint32_t time, bool bAlwaysWarpCursor ) - { - assert( wlserver_is_lock_held() ); -@@ -2644,43 +2655,55 @@ void wlserver_touchmotion( double x, double y, int touch_id, uint32_t time, bool - - if ( cv_touch_gestures ) - { -- uint32_t roundedCursorX = static_cast(std::round(tx)); -- uint32_t roundedCursorY = static_cast(std::round(ty)); -- uint32_t edge_range_x = static_cast(g_nOutputWidth * 0.02); -- uint32_t edge_range_y = static_cast(g_nOutputWidth * 0.02); -+ uint32_t rounded_tx = static_cast(std::round(tx)); -+ uint32_t rounded_ty = static_cast(std::round(ty)); -+ uint32_t edge_range_x = static_cast(g_nOutputWidth * 0.05); -+ uint32_t edge_range_y = static_cast(g_nOutputWidth * 0.05); - uint32_t gesture_limits_x = edge_range_x * 2; - uint32_t gesture_limits_y = edge_range_y * 2; -+ uint32_t threshold_distance_x = gesture_limits_x; -+ uint32_t threshold_distance_y = gesture_limits_y; - - // Left to Right and Right to Left -- if (!pending_gesture && roundedCursorX >= 1 && roundedCursorX < edge_range_x || -- !pending_gesture && roundedCursorX >= g_nOutputWidth - edge_range_x ) -- pending_gesture = true; -+ if (!pending_gesture_x && ((rounded_tx >= 1 && rounded_tx < edge_range_x) || (rounded_tx >= g_nOutputWidth - edge_range_x))) { -+ // Check if the distance moved is greater than the threshold -+ if (rounded_tx - previous_tx > threshold_distance_x) { -+ pending_gesture_x = true; -+ } -+ } -+ -+ // Top to Bottom and Bottom to Top -+ if (!pending_gesture_y && ((rounded_ty >= 1 && rounded_ty < edge_range_y) || (rounded_ty >= g_nOutputHeight - edge_range_y))) { -+ // Check if the distance moved is greater than the threshold -+ if (rounded_ty - previous_ty > threshold_distance_y) { -+ pending_gesture_y = true; -+ } -+ } - - //left -- if (pending_gesture && roundedCursorX >= edge_range_x && roundedCursorX < gesture_limits_x) { -+ if (pending_gesture_x && previous_tx < rounded_tx && rounded_tx >= edge_range_x && rounded_tx < gesture_limits_x) { - wlserver_open_steam_menu(0); -- pending_gesture = false; -+ wlserver_gesture_flush(); - } - //right -- if (pending_gesture && roundedCursorX <= g_nOutputWidth - edge_range_x && roundedCursorX > g_nOutputWidth - gesture_limits_x) { -+ if (pending_gesture_x && previous_tx > rounded_tx && rounded_tx <= g_nOutputWidth - edge_range_x && rounded_tx > g_nOutputWidth - gesture_limits_x) { - wlserver_open_steam_menu(1); -- pending_gesture = false; -+ wlserver_gesture_flush(); - } -- // Top to Bottom and Bottom to Top -- if (!pending_gesture && roundedCursorY >= 1 && roundedCursorY < edge_range_y || -- !pending_gesture && roundedCursorY >= g_nOutputHeight - edge_range_y ) -- pending_gesture = true; -+ - // Top -- if (pending_gesture && roundedCursorY >= edge_range_y && roundedCursorY < gesture_limits_y) { -- pending_gesture = false; -+ if (pending_gesture_y && previous_ty < rounded_ty && rounded_ty >= edge_range_y && rounded_ty < gesture_limits_y) { -+ wlserver_gesture_flush(); - // Top to Bottom function to add - } - // Bottom -- if (pending_gesture && !pending_osk && roundedCursorY <= g_nOutputWidth - edge_range_y && roundedCursorY > g_nOutputHeight - gesture_limits_y) { -- pending_gesture = false; -+ if (pending_gesture_y && previous_ty > rounded_ty && !pending_osk && rounded_ty <= g_nOutputWidth - edge_range_y && rounded_ty > g_nOutputHeight - gesture_limits_y) { -+ wlserver_gesture_flush(); - pending_osk = true; - //wlserver_open_steam_osk(1); - } -+ previous_tx = rounded_tx; -+ previous_ty = rounded_ty; - } - } - else if ( eMode == gamescope::TouchClickModes::Disabled ) --- -2.46.0 - - -From b31ab55d57f0a8b746d4d0fa7939f6473bcad9a4 Mon Sep 17 00:00:00 2001 -From: Matthew Anderson -Date: Sun, 19 May 2024 18:14:23 -0500 -Subject: [PATCH 13/21] Add references to drm_set_orientation() and g_drm in - wlserver for rotation gamescope-control - ---- - src/wlserver.cpp | 5 +++-- - src/wlserver.hpp | 3 ++- - 2 files changed, 5 insertions(+), 3 deletions(-) - -diff --git a/src/wlserver.cpp b/src/wlserver.cpp -index 833140a..eb3936a 100644 ---- a/src/wlserver.cpp -+++ b/src/wlserver.cpp -@@ -1125,8 +1125,9 @@ static void gamescope_control_rotate_display( struct wl_client *client, struct w - wl_log.errorf("Invalid target orientation selected"); - } - } -- //drm_set_orientation(&g_DRM, isRotated); -- //g_DRM.out_of_date = 2; -+ drm_set_orientation(&g_DRM, isRotated); -+ GetBackend()->DirtyState( true, true ); -+ - } - - static void gamescope_control_handle_destroy( struct wl_client *client, struct wl_resource *resource ) -diff --git a/src/wlserver.hpp b/src/wlserver.hpp -index 3304c18..0754ee5 100644 ---- a/src/wlserver.hpp -+++ b/src/wlserver.hpp -@@ -275,7 +275,8 @@ extern std::atomic g_bPendingTouchMovement; - extern gamescope::ConVar cv_touch_gestures; - - void wlserver_open_steam_menu( bool qam ); -- -+extern void drm_set_orientation( struct drm_t *drm, bool isRotated); -+extern drm_t g_DRM; - uint32_t wlserver_make_new_xwayland_server(); - void wlserver_destroy_xwayland_server(gamescope_xwayland_server_t *server); - --- -2.46.0 - - -From 8df476a6f7d41157e847ee7c3346c796e674d9fe Mon Sep 17 00:00:00 2001 -From: Matthew Anderson -Date: Mon, 20 May 2024 07:02:52 -0500 -Subject: [PATCH 14/21] Fix an issue where forced panel type orientations - weren't being applied - ---- - src/Backends/DRMBackend.cpp | 15 ++++++++------- - 1 file changed, 8 insertions(+), 7 deletions(-) - -diff --git a/src/Backends/DRMBackend.cpp b/src/Backends/DRMBackend.cpp -index eafb2f9..1ea1221 100644 ---- a/src/Backends/DRMBackend.cpp -+++ b/src/Backends/DRMBackend.cpp -@@ -2031,20 +2031,19 @@ namespace gamescope - - void CDRMConnector::UpdateEffectiveOrientation( const drmModeModeInfo *pMode ) - { -- if ( this->GetScreenType() == GAMESCOPE_SCREEN_TYPE_INTERNAL && g_DesiredInternalOrientation != GAMESCOPE_PANEL_ORIENTATION_AUTO -- || GAMESCOPE_SCREEN_TYPE_EXTERNAL && g_DesiredExternalOrientation != GAMESCOPE_PANEL_ORIENTATION_AUTO -- && g_bExternalForced ) -- { -+ if ((this->GetScreenType() == GAMESCOPE_SCREEN_TYPE_INTERNAL && g_DesiredInternalOrientation != GAMESCOPE_PANEL_ORIENTATION_AUTO) || -+ (this->GetScreenType() == GAMESCOPE_SCREEN_TYPE_EXTERNAL && g_DesiredInternalOrientation != GAMESCOPE_PANEL_ORIENTATION_AUTO && g_bExternalForced)) { -+ drm_log.infof("We are rotating the orientation of the internal or faked external display") - m_ChosenOrientation = g_DesiredInternalOrientation; -- } -- else if ( this->GetScreenType() == GAMESCOPE_SCREEN_TYPE_EXTERNAL && g_DesiredExternalOrientation != GAMESCOPE_PANEL_ORIENTATION_AUTO ) -- { -+ } else if (this->GetScreenType() == GAMESCOPE_SCREEN_TYPE_EXTERNAL && g_DesiredExternalOrientation != GAMESCOPE_PANEL_ORIENTATION_AUTO) { -+ drm_log.infof("We are rotating the orientation of an external display"); - m_ChosenOrientation = g_DesiredExternalOrientation; - } - else - { - if ( this->GetProperties().panel_orientation ) - { -+ drm_log.infof("We are using a kernel orientation quirk to rotate the display"); - switch ( this->GetProperties().panel_orientation->GetCurrentValue() ) - { - case DRM_MODE_PANEL_ORIENTATION_NORMAL: -@@ -2066,6 +2065,7 @@ namespace gamescope - - if ( this->GetScreenType() == gamescope::GAMESCOPE_SCREEN_TYPE_INTERNAL && pMode ) - { -+ drm_log.infof("We are using legacy code to rotate the display"); - // Auto-detect portait mode for internal displays - m_ChosenOrientation = pMode->hdisplay < pMode->vdisplay - ? GAMESCOPE_PANEL_ORIENTATION_270 -@@ -2073,6 +2073,7 @@ namespace gamescope - } - else - { -+ drm_log.infof("No orientation quirks have been applied"); - m_ChosenOrientation = GAMESCOPE_PANEL_ORIENTATION_0; - } - } --- -2.46.0 - - -From 5d7781dd8ca41f4d865aa336b4e925beeec9349e Mon Sep 17 00:00:00 2001 -From: Matthew Anderson -Date: Mon, 20 May 2024 07:25:29 -0500 -Subject: [PATCH 15/21] add missing curly bracket... - ---- - src/Backends/DRMBackend.cpp | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/src/Backends/DRMBackend.cpp b/src/Backends/DRMBackend.cpp -index 1ea1221..8d86df0 100644 ---- a/src/Backends/DRMBackend.cpp -+++ b/src/Backends/DRMBackend.cpp -@@ -2033,7 +2033,7 @@ namespace gamescope - { - if ((this->GetScreenType() == GAMESCOPE_SCREEN_TYPE_INTERNAL && g_DesiredInternalOrientation != GAMESCOPE_PANEL_ORIENTATION_AUTO) || - (this->GetScreenType() == GAMESCOPE_SCREEN_TYPE_EXTERNAL && g_DesiredInternalOrientation != GAMESCOPE_PANEL_ORIENTATION_AUTO && g_bExternalForced)) { -- drm_log.infof("We are rotating the orientation of the internal or faked external display") -+ drm_log.infof("We are rotating the orientation of the internal or faked external display"); - m_ChosenOrientation = g_DesiredInternalOrientation; - } else if (this->GetScreenType() == GAMESCOPE_SCREEN_TYPE_EXTERNAL && g_DesiredExternalOrientation != GAMESCOPE_PANEL_ORIENTATION_AUTO) { - drm_log.infof("We are rotating the orientation of an external display"); --- -2.46.0 - - -From 0282352468bb6b99e75eb1c24cb3ed555886fff5 Mon Sep 17 00:00:00 2001 -From: Matthew Anderson -Date: Mon, 20 May 2024 10:17:55 -0500 -Subject: [PATCH 16/21] Fix case where real externals were rotated with faked - external panels - ---- - src/Backends/DRMBackend.cpp | 21 +++++++---- - src/wlserver.cpp | 72 +++++++++++++++++++++++++------------ - 2 files changed, 64 insertions(+), 29 deletions(-) - -diff --git a/src/Backends/DRMBackend.cpp b/src/Backends/DRMBackend.cpp -index 8d86df0..0008505 100644 ---- a/src/Backends/DRMBackend.cpp -+++ b/src/Backends/DRMBackend.cpp -@@ -318,13 +318,20 @@ namespace gamescope - - GamescopeScreenType GetScreenType() const override - { -- if ( g_ForcedScreenType != GAMESCOPE_SCREEN_TYPE_AUTO ) -- return g_ForcedScreenType; -- - if ( m_pConnector->connector_type == DRM_MODE_CONNECTOR_eDP || - m_pConnector->connector_type == DRM_MODE_CONNECTOR_LVDS || - m_pConnector->connector_type == DRM_MODE_CONNECTOR_DSI ) -- return GAMESCOPE_SCREEN_TYPE_INTERNAL; -+ { -+ if ( g_bExternalForced ) -+ { -+ return g_ForcedScreenType; -+ } -+ else -+ { -+ return GAMESCOPE_SCREEN_TYPE_INTERNAL; -+ } -+ } -+ - - return GAMESCOPE_SCREEN_TYPE_EXTERNAL; - } -@@ -2031,11 +2038,11 @@ namespace gamescope - - void CDRMConnector::UpdateEffectiveOrientation( const drmModeModeInfo *pMode ) - { -- if ((this->GetScreenType() == GAMESCOPE_SCREEN_TYPE_INTERNAL && g_DesiredInternalOrientation != GAMESCOPE_PANEL_ORIENTATION_AUTO) || -- (this->GetScreenType() == GAMESCOPE_SCREEN_TYPE_EXTERNAL && g_DesiredInternalOrientation != GAMESCOPE_PANEL_ORIENTATION_AUTO && g_bExternalForced)) { -+ if ( this->GetScreenType() == GAMESCOPE_SCREEN_TYPE_INTERNAL && g_DesiredInternalOrientation != GAMESCOPE_PANEL_ORIENTATION_AUTO ) { - drm_log.infof("We are rotating the orientation of the internal or faked external display"); - m_ChosenOrientation = g_DesiredInternalOrientation; -- } else if (this->GetScreenType() == GAMESCOPE_SCREEN_TYPE_EXTERNAL && g_DesiredExternalOrientation != GAMESCOPE_PANEL_ORIENTATION_AUTO) { -+ } -+ else if (this->GetScreenType() == GAMESCOPE_SCREEN_TYPE_EXTERNAL && g_DesiredExternalOrientation != GAMESCOPE_PANEL_ORIENTATION_AUTO) { - drm_log.infof("We are rotating the orientation of an external display"); - m_ChosenOrientation = g_DesiredExternalOrientation; - } -diff --git a/src/wlserver.cpp b/src/wlserver.cpp -index eb3936a..896ec04 100644 ---- a/src/wlserver.cpp -+++ b/src/wlserver.cpp -@@ -2555,29 +2555,57 @@ static void apply_touchscreen_orientation(double *x, double *y ) - double ty = 0; - - // Use internal screen always for orientation purposes. -- if ( g_ForcedScreenType == gamescope::GAMESCOPE_SCREEN_TYPE_INTERNAL || g_ForcedScreenType == gamescope::GAMESCOPE_SCREEN_TYPE_EXTERNAL ) -+ if ( g_ForcedScreenType != gamescope::GAMESCOPE_SCREEN_TYPE_AUTO ) - { -- switch ( g_DesiredInternalOrientation ) -- { -- default: -- case GAMESCOPE_PANEL_ORIENTATION_AUTO: -- case GAMESCOPE_PANEL_ORIENTATION_0: -- tx = *x; -- ty = *y; -- break; -- case GAMESCOPE_PANEL_ORIENTATION_90: -- tx = 1.0 - *y; -- ty = *x; -- break; -- case GAMESCOPE_PANEL_ORIENTATION_180: -- tx = 1.0 - *x; -- ty = 1.0 - *y; -- break; -- case GAMESCOPE_PANEL_ORIENTATION_270: -- tx = *y; -- ty = 1.0 - *x; -- break; -- } -+ if ( g_ForcedScreenType == gamescope::GAMESCOPE_SCREEN_TYPE_EXTERNAL ) -+ { -+ switch (GetBackend()->GetConnector(gamescope::GAMESCOPE_SCREEN_TYPE_EXTERNAL)->GetCurrentOrientation()) -+ { -+ default: -+ case GAMESCOPE_PANEL_ORIENTATION_AUTO: -+ case GAMESCOPE_PANEL_ORIENTATION_0: -+ tx = *x; -+ ty = *y; -+ break; -+ case GAMESCOPE_PANEL_ORIENTATION_90: -+ tx = 1.0 - *y; -+ ty = *x; -+ break; -+ case GAMESCOPE_PANEL_ORIENTATION_180: -+ tx = 1.0 - *x; -+ ty = 1.0 - *y; -+ break; -+ case GAMESCOPE_PANEL_ORIENTATION_270: -+ tx = *y; -+ ty = 1.0 - *x; -+ break; -+ } -+ } -+ else -+ { -+ switch (GetBackend()->GetConnector(gamescope::GAMESCOPE_SCREEN_TYPE_INTERNAL)->GetCurrentOrientation()) -+ { -+ default: -+ case GAMESCOPE_PANEL_ORIENTATION_AUTO: -+ case GAMESCOPE_PANEL_ORIENTATION_0: -+ tx = *x; -+ ty = *y; -+ break; -+ case GAMESCOPE_PANEL_ORIENTATION_90: -+ tx = 1.0 - *y; -+ ty = *x; -+ break; -+ case GAMESCOPE_PANEL_ORIENTATION_180: -+ tx = 1.0 - *x; -+ ty = 1.0 - *y; -+ break; -+ case GAMESCOPE_PANEL_ORIENTATION_270: -+ tx = *y; -+ ty = 1.0 - *x; -+ break; -+ } -+ } -+ - } - else if (g_ForcedScreenType == gamescope::GAMESCOPE_SCREEN_TYPE_AUTO) - { --- -2.46.0 - - -From 276c1220de3542467b6d8e2218dfa1f2ccfc5b79 Mon Sep 17 00:00:00 2001 -From: Matthew Anderson -Date: Mon, 20 May 2024 16:30:47 -0500 -Subject: [PATCH 17/21] Add verbose panel logs and attempt to address all - orientation issues - ---- - src/Backends/DRMBackend.cpp | 18 ++++++++++++++-- - src/wlserver.cpp | 41 ++++++++++++++++++++----------------- - src/wlserver.hpp | 1 + - 3 files changed, 39 insertions(+), 21 deletions(-) - -diff --git a/src/Backends/DRMBackend.cpp b/src/Backends/DRMBackend.cpp -index 0008505..bd5ffec 100644 ---- a/src/Backends/DRMBackend.cpp -+++ b/src/Backends/DRMBackend.cpp -@@ -56,6 +56,7 @@ static constexpr bool k_bUseCursorPlane = false; - - extern int g_nPreferredOutputWidth; - extern int g_nPreferredOutputHeight; -+bool panelTypeChanged = false; - - gamescope::ConVar cv_drm_single_plane_optimizations( "drm_single_plane_optimizations", true, "Whether or not to enable optimizations for single plane usage." ); - -@@ -324,6 +325,7 @@ namespace gamescope - { - if ( g_bExternalForced ) - { -+ panelTypeChanged = true; - return g_ForcedScreenType; - } - else -@@ -332,7 +334,7 @@ namespace gamescope - } - } - -- -+ panelTypeChanged = false; - return GAMESCOPE_SCREEN_TYPE_EXTERNAL; - } - -@@ -2038,7 +2040,19 @@ namespace gamescope - - void CDRMConnector::UpdateEffectiveOrientation( const drmModeModeInfo *pMode ) - { -- if ( this->GetScreenType() == GAMESCOPE_SCREEN_TYPE_INTERNAL && g_DesiredInternalOrientation != GAMESCOPE_PANEL_ORIENTATION_AUTO ) { -+ -+ if ( this->GetScreenType() == GAMESCOPE_SCREEN_TYPE_EXTERNAL && panelTypeChanged ) -+ drm_log.infof("Display is internal faked as external"); -+ if ( this->GetScreenType() == GAMESCOPE_SCREEN_TYPE_INTERNAL && !panelTypeChanged ) -+ drm_log.infof("Display is real internal"); -+ if (panelTypeChanged){ -+ drm_log.infof("Panel type was changed"); -+ } -+ -+ if (( this->GetScreenType() == GAMESCOPE_SCREEN_TYPE_INTERNAL && g_DesiredInternalOrientation != GAMESCOPE_PANEL_ORIENTATION_AUTO ) || -+ ( this->GetScreenType() == GAMESCOPE_SCREEN_TYPE_EXTERNAL && g_DesiredInternalOrientation != GAMESCOPE_PANEL_ORIENTATION_AUTO -+ && panelTypeChanged)) { -+ - drm_log.infof("We are rotating the orientation of the internal or faked external display"); - m_ChosenOrientation = g_DesiredInternalOrientation; - } -diff --git a/src/wlserver.cpp b/src/wlserver.cpp -index 896ec04..e5bb2e2 100644 ---- a/src/wlserver.cpp -+++ b/src/wlserver.cpp -@@ -2559,26 +2559,29 @@ static void apply_touchscreen_orientation(double *x, double *y ) - { - if ( g_ForcedScreenType == gamescope::GAMESCOPE_SCREEN_TYPE_EXTERNAL ) - { -- switch (GetBackend()->GetConnector(gamescope::GAMESCOPE_SCREEN_TYPE_EXTERNAL)->GetCurrentOrientation()) -+ if(panelTypeChanged) - { -- default: -- case GAMESCOPE_PANEL_ORIENTATION_AUTO: -- case GAMESCOPE_PANEL_ORIENTATION_0: -- tx = *x; -- ty = *y; -- break; -- case GAMESCOPE_PANEL_ORIENTATION_90: -- tx = 1.0 - *y; -- ty = *x; -- break; -- case GAMESCOPE_PANEL_ORIENTATION_180: -- tx = 1.0 - *x; -- ty = 1.0 - *y; -- break; -- case GAMESCOPE_PANEL_ORIENTATION_270: -- tx = *y; -- ty = 1.0 - *x; -- break; -+ switch (GetBackend()->GetConnector(gamescope::GAMESCOPE_SCREEN_TYPE_EXTERNAL)->GetCurrentOrientation()) -+ { -+ default: -+ case GAMESCOPE_PANEL_ORIENTATION_AUTO: -+ case GAMESCOPE_PANEL_ORIENTATION_0: -+ tx = *x; -+ ty = *y; -+ break; -+ case GAMESCOPE_PANEL_ORIENTATION_90: -+ tx = 1.0 - *y; -+ ty = *x; -+ break; -+ case GAMESCOPE_PANEL_ORIENTATION_180: -+ tx = 1.0 - *x; -+ ty = 1.0 - *y; -+ break; -+ case GAMESCOPE_PANEL_ORIENTATION_270: -+ tx = *y; -+ ty = 1.0 - *x; -+ break; -+ } - } - } - else -diff --git a/src/wlserver.hpp b/src/wlserver.hpp -index 0754ee5..bdf3b0b 100644 ---- a/src/wlserver.hpp -+++ b/src/wlserver.hpp -@@ -277,6 +277,7 @@ extern gamescope::ConVar cv_touch_gestures; - void wlserver_open_steam_menu( bool qam ); - extern void drm_set_orientation( struct drm_t *drm, bool isRotated); - extern drm_t g_DRM; -+extern bool panelTypeChanged; - uint32_t wlserver_make_new_xwayland_server(); - void wlserver_destroy_xwayland_server(gamescope_xwayland_server_t *server); - --- -2.46.0 - - -From 11664b820a9065b597d8655e53ade8dd9a29c4bc Mon Sep 17 00:00:00 2001 -From: Bouke Sybren Haarsma -Date: Tue, 28 May 2024 21:56:47 +0200 -Subject: [PATCH 18/21] add closing bracket - ---- - src/main.cpp | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/src/main.cpp b/src/main.cpp -index 675020f..315b718 100644 ---- a/src/main.cpp -+++ b/src/main.cpp -@@ -465,6 +465,7 @@ static enum gamescope::GamescopeBackend parse_backend_name(const char *str) - fprintf( stderr, "gamescope: invalid value for --backend\n" ); - exit(1); - } -+} - - std::vector g_customRefreshRates; - // eg: 60,60,90,110-120 --- -2.46.0 - - -From 1e5c439eb0b1e809a3ec6414e8d0536db22c9601 Mon Sep 17 00:00:00 2001 -From: Bouke Sybren Haarsma -Date: Wed, 3 Jan 2024 17:03:04 +0100 -Subject: [PATCH 19/21] remove hacky texture - -This will use more hardware planes, causing some devices to composite yeilding lower framerates ---- - src/steamcompmgr.cpp | 62 ++++++++++++-------------------------------- - 1 file changed, 17 insertions(+), 45 deletions(-) - -diff --git a/src/steamcompmgr.cpp b/src/steamcompmgr.cpp -index 6fceb1d..4dbbdaf 100644 ---- a/src/steamcompmgr.cpp -+++ b/src/steamcompmgr.cpp -@@ -1620,7 +1620,7 @@ bool MouseCursor::getTexture() - { - pixels[i * image->width + j] = image->pixels[i * image->width + j]; - } -- } -+ } - std::vector resizeBuffer( nDesiredWidth * nDesiredHeight ); - stbir_resize_uint8_srgb( (unsigned char *)pixels.data(), image->width, image->height, 0, - (unsigned char *)resizeBuffer.data(), nDesiredWidth, nDesiredHeight, 0, -@@ -2306,7 +2306,7 @@ paint_all(bool async) - } - } - } -- -+ - int nOldLayerCount = frameInfo.layerCount; - - uint32_t flags = 0; -@@ -2314,7 +2314,7 @@ paint_all(bool async) - flags |= PaintWindowFlag::BasePlane; - paint_window(w, w, &frameInfo, global_focus.cursor, flags); - update_touch_scaling( &frameInfo ); -- -+ - // paint UI unless it's fully hidden, which it communicates to us through opacity=0 - // we paint it to extract scaling coefficients above, then remove the layer if one was added - if ( w->opacity == TRANSLUCENT && bHasVideoUnderlay && nOldLayerCount < frameInfo.layerCount ) -@@ -2327,7 +2327,7 @@ paint_all(bool async) - float opacityScale = g_bPendingFade - ? 0.0f - : ((currentTime - fadeOutStartTime) / (float)g_FadeOutDuration); -- -+ - paint_cached_base_layer(g_HeldCommits[HELD_COMMIT_FADE], g_CachedPlanes[HELD_COMMIT_FADE], &frameInfo, 1.0f - opacityScale, false); - paint_window(w, w, &frameInfo, global_focus.cursor, PaintWindowFlag::BasePlane | PaintWindowFlag::FadeTarget | PaintWindowFlag::DrawBorders, opacityScale, override); - } -@@ -2401,34 +2401,6 @@ paint_all(bool async) - if ( overlay == global_focus.inputFocusWindow ) - update_touch_scaling( &frameInfo ); - } -- else if ( !GetBackend()->UsesVulkanSwapchain() && GetBackend()->IsSessionBased() ) -- { -- auto tex = vulkan_get_hacky_blank_texture(); -- if ( tex != nullptr ) -- { -- // HACK! HACK HACK HACK -- // To avoid stutter when toggling the overlay on -- int curLayer = frameInfo.layerCount++; -- -- FrameInfo_t::Layer_t *layer = &frameInfo.layers[ curLayer ]; -- -- -- layer->scale.x = g_nOutputWidth == tex->width() ? 1.0f : tex->width() / (float)g_nOutputWidth; -- layer->scale.y = g_nOutputHeight == tex->height() ? 1.0f : tex->height() / (float)g_nOutputHeight; -- layer->offset.x = 0.0f; -- layer->offset.y = 0.0f; -- layer->opacity = 1.0f; // BLAH -- layer->zpos = g_zposOverlay; -- layer->applyColorMgmt = g_ColorMgmt.pending.enabled; -- -- layer->colorspace = GAMESCOPE_APP_TEXTURE_COLORSPACE_LINEAR; -- layer->ctm = nullptr; -- layer->tex = tex; -- -- layer->filter = GamescopeUpscaleFilter::NEAREST; -- layer->blackBorder = true; -- } -- } - - if (notification) - { -@@ -3014,7 +2986,7 @@ win_maybe_a_dropdown( steamcompmgr_win_t *w ) - // - // TODO: Come back to me for original Age of Empires HD launcher. - // Does that use it? It wants blending! -- // -+ // - // Only do this if we have CONTROLPARENT right now. Some other apps, such as the - // Street Fighter V (310950) Splash Screen also use LAYERED and TOOLWINDOW, and we don't - // want that to be overlayed. -@@ -3029,12 +3001,12 @@ win_maybe_a_dropdown( steamcompmgr_win_t *w ) - - // Josh: - // The logic here is as follows. The window will be treated as a dropdown if: -- // -+ // - // If this window has a fixed position on the screen + static gravity: - // - If the window has either skipPage or skipTaskbar - // - If the window isn't a dialog, always treat it as a dropdown, as it's - // probably meant to be some form of popup. -- // - If the window is a dialog -+ // - If the window is a dialog - // - If the window has transient for, disregard it, as it is trying to redirecting us elsewhere - // ie. a settings menu dialog popup or something. - // - If the window has both skip taskbar and pager, treat it as a dialog. -@@ -3126,7 +3098,7 @@ static bool is_good_override_candidate( steamcompmgr_win_t *override, steamcompm - return false; - - return override != focus && override->GetGeometry().nX >= 0 && override->GetGeometry().nY >= 0; --} -+} - - static bool - pick_primary_focus_and_override(focus_t *out, Window focusControlWindow, const std::vector& vecPossibleFocusWindows, bool globalFocus, const std::vector& ctxFocusControlAppIDs) -@@ -3267,7 +3239,7 @@ found:; - - if ( focus ) - { -- if ( window_has_commits( focus ) ) -+ if ( window_has_commits( focus ) ) - out->focusWindow = focus; - else - focus->outdatedInteractiveFocus = true; -@@ -3310,9 +3282,9 @@ found:; - override_focus = fake_override; - goto found2; - } -- } -+ } - } -- -+ - found2:; - resolveTransientOverrides( true ); - } -@@ -4574,7 +4546,7 @@ finish_destroy_win(xwayland_ctx_t *ctx, Window id, bool gone) - { - if (gone) - finish_unmap_win (ctx, w); -- -+ - { - std::unique_lock lock( ctx->list_mutex ); - *prev = w->xwayland().next; -@@ -4631,7 +4603,7 @@ destroy_win(xwayland_ctx_t *ctx, Window id, bool gone, bool fade) - global_focus.overrideWindow = nullptr; - if (x11_win(global_focus.fadeWindow) == id && gone) - global_focus.fadeWindow = nullptr; -- -+ - MakeFocusDirty(); - - finish_destroy_win(ctx, id, gone); -@@ -5243,7 +5215,7 @@ handle_property_notify(xwayland_ctx_t *ctx, XPropertyEvent *ev) - { - get_win_type(ctx, w); - MakeFocusDirty(); -- } -+ } - } - if (ev->atom == ctx->atoms.sizeHintsAtom) - { -@@ -6153,7 +6125,7 @@ void handle_done_commits_xdg( bool vblank, uint64_t vblank_idx ) - commits_before_their_time.push_back( entry ); - continue; - } -- -+ - if (!entry.earliestPresentTime) - { - entry.earliestPresentTime = next_refresh_time; -@@ -7143,7 +7115,7 @@ void update_mode_atoms(xwayland_ctx_t *root_ctx, bool* needs_flush = nullptr) - } - XChangeProperty(root_ctx->dpy, root_ctx->root, root_ctx->atoms.gamescopeDisplayModeListExternal, XA_STRING, 8, PropModeReplace, - (unsigned char *)modes, strlen(modes) + 1 ); -- -+ - uint32_t one = 1; - XChangeProperty(root_ctx->dpy, root_ctx->root, root_ctx->atoms.gamescopeDisplayIsExternal, XA_CARDINAL, 32, PropModeReplace, - (unsigned char *)&one, 1 ); -@@ -7975,7 +7947,7 @@ void steamcompmgr_send_frame_done_to_focus_window() - { - wlserver_lock(); - wlserver_send_frame_done( global_focus.focusWindow->xwayland().surface.main_surface , &now ); -- wlserver_unlock(); -+ wlserver_unlock(); - } - } - --- -2.46.0 - - -From 672fd75c5ac7bb1226c418270fa134ad6fe07b30 Mon Sep 17 00:00:00 2001 -From: Kyle Gospodnetich -Date: Tue, 2 Jul 2024 14:12:47 -0700 -Subject: [PATCH 20/21] Only change refresh rates on internal displays - ---- - src/Backends/DRMBackend.cpp | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/src/Backends/DRMBackend.cpp b/src/Backends/DRMBackend.cpp -index bd5ffec..03b9b4c 100644 ---- a/src/Backends/DRMBackend.cpp -+++ b/src/Backends/DRMBackend.cpp -@@ -2162,7 +2162,7 @@ namespace gamescope - ( m_Mutable.szMakePNP == "VLV"sv && m_Mutable.szModel == "Jupiter"sv ) || - ( m_Mutable.szMakePNP == "VLV"sv && m_Mutable.szModel == "Galileo"sv ); - -- if ( g_customRefreshRates.size() > 0 ) { -+ if ( g_customRefreshRates.size() > 0 && GetScreenType() == GAMESCOPE_SCREEN_TYPE_INTERNAL ) { - m_Mutable.ValidDynamicRefreshRates = std::span(g_customRefreshRates); - return; - } --- -2.46.0 - - -From 0cada655064b2bb2c0b5540f4c249a3c6a1254ed Mon Sep 17 00:00:00 2001 -From: Kyle Gospodnetich -Date: Tue, 2 Jul 2024 15:14:23 -0700 -Subject: [PATCH 21/21] Also check g_bExternalForced - ---- - src/Backends/DRMBackend.cpp | 6 +++--- - 1 file changed, 3 insertions(+), 3 deletions(-) - -diff --git a/src/Backends/DRMBackend.cpp b/src/Backends/DRMBackend.cpp -index 03b9b4c..24e81b1 100644 ---- a/src/Backends/DRMBackend.cpp -+++ b/src/Backends/DRMBackend.cpp -@@ -2162,9 +2162,9 @@ namespace gamescope - ( m_Mutable.szMakePNP == "VLV"sv && m_Mutable.szModel == "Jupiter"sv ) || - ( m_Mutable.szMakePNP == "VLV"sv && m_Mutable.szModel == "Galileo"sv ); - -- if ( g_customRefreshRates.size() > 0 && GetScreenType() == GAMESCOPE_SCREEN_TYPE_INTERNAL ) { -- m_Mutable.ValidDynamicRefreshRates = std::span(g_customRefreshRates); -- return; -+ if ( g_customRefreshRates.size() > 0 && ( GetScreenType() == GAMESCOPE_SCREEN_TYPE_INTERNAL || g_bExternalForced ) ) { -+ m_Mutable.ValidDynamicRefreshRates = std::span(g_customRefreshRates); -+ return; - } - if ( bSteamDeckDisplay ) - { --- -2.46.0 diff --git a/anda/games/gamescope/disable-steam-touch-click-atom.patch b/anda/games/gamescope/disable-steam-touch-click-atom.patch deleted file mode 100644 index cb10172b28..0000000000 --- a/anda/games/gamescope/disable-steam-touch-click-atom.patch +++ /dev/null @@ -1,51 +0,0 @@ -diff --git a/src/main.cpp b/src/main.cpp -index 119e043..6c46d97 100644 ---- a/src/main.cpp -+++ b/src/main.cpp -@@ -148,6 +148,8 @@ const struct option *gamescope_options = (struct option[]){ - { "reshade-effect", required_argument, nullptr, 0 }, - { "reshade-technique-idx", required_argument, nullptr, 0 }, - -+ { "disable-touch-click", no_argument, nullptr, 0 }, -+ - // Steam Deck options - { "mura-map", required_argument, nullptr, 0 }, - -@@ -193,6 +195,7 @@ const char usage[] = - " -e, --steam enable Steam integration\n" - " --bypass-steam-resolution bypass Steam's default 720p/800p default resolution\n" - " --touch-gestures enable touch gestures for Steam menus\n" -+ " --disable-touch-click disable touchscreen tap acting as a click\n" - " --xwayland-count create N xwayland servers\n" - " --prefer-vk-device prefer Vulkan device for compositing (ex: 1002:7300)\n" - " --force-orientation rotate the internal display (left, right, normal, upsidedown)\n" -diff --git a/src/steamcompmgr.cpp b/src/steamcompmgr.cpp -index 92bf617..d7498e5 100644 ---- a/src/steamcompmgr.cpp -+++ b/src/steamcompmgr.cpp -@@ -347,6 +347,7 @@ bool g_bHDRItmEnable = false; - int g_nCurrentRefreshRate_CachedValue = 0; - gamescope::ConVar cv_bypass_steam_resolution{ "bypass_steam_resolution", false, "Workaround the 720p/800p limits Steam uses for games" }; - -+gamescope::ConVar cv_disable_touch_click{ "disable_touch_click", false, "Prevents touchscreen taps acting as clicks" }; - - static void - update_color_mgmt() -@@ -5128,7 +5129,7 @@ handle_property_notify(xwayland_ctx_t *ctx, XPropertyEvent *ev) - MakeFocusDirty(); - } - } -- if (ev->atom == ctx->atoms.steamTouchClickModeAtom ) -+ if (ev->atom == ctx->atoms.steamTouchClickModeAtom && !cv_disable_touch_click) - { - gamescope::cv_touch_click_mode = (gamescope::TouchClickMode) get_prop(ctx, ctx->root, ctx->atoms.steamTouchClickModeAtom, 0u ); - } -@@ -7301,6 +7302,8 @@ steamcompmgr_main(int argc, char **argv) - g_reshade_technique_idx = atoi(optarg); - } else if (strcmp(opt_name, "mura-map") == 0) { - set_mura_overlay(optarg); -+ } else if (strcmp(opt_name, "disable-touch-click") == 0) { -+ cv_disable_touch_click = true; - } - break; - case '?': \ No newline at end of file diff --git a/anda/games/gamescope/gamescope-legacy.sh b/anda/games/gamescope/gamescope-legacy.sh deleted file mode 100644 index 34f2e1573e..0000000000 --- a/anda/games/gamescope/gamescope-legacy.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/bin/sh - -# Wrapper script to run Gamescope with legacy options for older GPUs - -gamescope_path="/usr/bin/gamescope" - - -# check if $BACKEND is already defined -# todo: Probably want to patch gamescope-session-plus for this instead meow - -# For compatibility, let's add the argument for nested backends too - -LEGACY_BACKEND_ARGS="" -NESTED_BACKEND_ARGS="" -if [ -z "$BACKEND" ]; then - LEGACY_BACKEND_ARGS="--backend=sdl" - NESTED_BACKEND_ARGS="--backend=sdl" -else - # Only added for nested sessions, as $BACKEND should be defined only for legacy - NESTED_BACKEND_ARGS="--backend=$BACKEND" -fi - - -if [ -z "$DISPLAY" ]; then - $gamescope_path $LEGACY_BACKEND_ARGS $@ -else - $gamescope_path $NESTED_BACKEND_ARGS $@ -fi - diff --git a/anda/games/gamescope/terra-gamescope.spec b/anda/games/gamescope/gamescope.spec old mode 100644 new mode 100755 similarity index 64% rename from anda/games/gamescope/terra-gamescope.spec rename to anda/games/gamescope/gamescope.spec index 01dedba6e7..91dc951745 --- a/anda/games/gamescope/terra-gamescope.spec +++ b/anda/games/gamescope/gamescope.spec @@ -1,43 +1,34 @@ -%if 0%{?fedora} >= 41 -%global libliftoff_minver 0.5.0 -%else %global libliftoff_minver 0.4.1 -%endif -%global toolchain clang %global _default_patch_fuzz 2 -%global gamescope_tag 3.16.1 +%global build_timestamp %(date +"%Y%m%d") +#global gamescope_tag 3.15.11 +%global gamescope_commit d3174928d47f7e353e7daca63cf882d65660cc7c +%define short_commit %(echo %{gamescope_commit} | cut -c1-8) -Name: terra-gamescope -Version: 100.%{gamescope_tag} +Name: gamescope +#Version: 100.%{gamescope_tag} +Version: 104.%{short_commit} Release: 1%?dist -Summary: Micro-compositor for video games on Wayland - Terra patch, please read the full description +Summary: Micro-compositor for video games on Wayland License: BSD URL: https://github.com/ValveSoftware/gamescope # Create stb.pc to satisfy dependency('stb') Source0: stb.pc -Source1: gamescope-legacy.sh Patch0: 0001-cstdint.patch -# https://github.com/ChimeraOS/gamescope -Patch1: chimeraos.patch # https://hhd.dev/ -Patch2: disable-steam-touch-click-atom.patch -Patch3: v2-0001-always-send-ctrl-1-2-to-steam-s-wayland-session.patch - -# Set default backend to SDL instead of Wayland, to avoid issues with GPUs that do not support -# Vulkan DRM modifiers. -# See also: gamescope-legacy package -# https://github.com/ValveSoftware/gamescope/issues/1218#issuecomment-2123801764 -Patch6: 1483.patch +# https://github.com/ChimeraOS/gamescope +Patch1: handheld.patch BuildRequires: meson >= 0.54.0 BuildRequires: ninja-build BuildRequires: cmake -BuildRequires: clang +BuildRequires: gcc +BuildRequires: gcc-c++ BuildRequires: glm-devel BuildRequires: google-benchmark-devel BuildRequires: libXmu-devel @@ -65,10 +56,11 @@ BuildRequires: pkgconfig(sdl2) BuildRequires: pkgconfig(libpipewire-0.3) BuildRequires: pkgconfig(libavif) BuildRequires: pkgconfig(wlroots) -BuildRequires: pkgconfig(libliftoff) >= 0.4.1 +BuildRequires: pkgconfig(libliftoff) BuildRequires: pkgconfig(libcap) BuildRequires: pkgconfig(hwdata) BuildRequires: pkgconfig(lcms2) +BuildRequires: pkgconfig(luajit) BuildRequires: spirv-headers-devel # Enforce the the minimum EVR to contain fixes for all of: # CVE-2021-28021 CVE-2021-42715 CVE-2021-42716 CVE-2022-28041 CVE-2023-43898 @@ -90,29 +82,24 @@ BuildRequires: git # libliftoff hasn't bumped soname, but API/ABI has changed for 0.2.0 release Requires: libliftoff%{?_isa} >= %{libliftoff_minver} Requires: xorg-x11-server-Xwayland -Requires: terra-gamescope-libs = %{version}-%{release} -Requires: terra-gamescope-libs(x86-32) = %{version}-%{release} +Requires: gamescope-libs = %{version}-%{release} +Requires: gamescope-libs(x86-32) = %{version}-%{release} Recommends: mesa-dri-drivers Recommends: mesa-vulkan-drivers -Provides: gamescope-legacy -Obsoletes: gamescope-legacy < 3.14.2 - %description -Gamescope is the micro-compositor optimized for running video games on Wayland. - -This specific build of Gamescope is patched to use SDL as the default backend instead of Wayland, and -includes a legacy wrapper script for older GPUs and extra configuration options. Please see -https://developer.fyralabs.com/terra/gamescope for more information. +%{name} is the micro-compositor optimized for running video games on Wayland. %package libs -Summary: libs for Gamescope +Summary: libs for %{name} %description libs %summary %prep -git clone --depth 1 --branch %{gamescope_tag} %{url}.git +# git clone --depth 1 --branch %%{gamescope_tag} %%{url}.git +git clone %{url}.git cd gamescope +git checkout %{gamescope_commit} git submodule update --init --recursive mkdir -p pkgconfig cp %{SOURCE0} pkgconfig/stb.pc @@ -125,35 +112,28 @@ sed -i 's^../thirdparty/SPIRV-Headers/include/spirv/^/usr/include/spirv/^' src/m %build cd gamescope export PKG_CONFIG_PATH=pkgconfig -%if %{__isa_bits} == 64 -%meson --auto-features=enabled -Dforce_fallback_for=vkroots,wlroots,libliftoff -%else -%meson -Denable_gamescope=false -Denable_gamescope_wsi_layer=true -%endif +%meson \ + --auto-features=enabled \ + -Dforce_fallback_for=vkroots,wlroots,libliftoff %meson_build %install cd gamescope %meson_install --skip-subprojects -%if %{__isa_bits} == 64 -install -Dm755 %{SOURCE1} %{buildroot}%{_bindir}/gamescope-legacy -%endif - %files %license gamescope/LICENSE %doc gamescope/README.md -%if %{__isa_bits} == 64 %caps(cap_sys_nice=eip) %{_bindir}/gamescope %{_bindir}/gamescopectl %{_bindir}/gamescopestream %{_bindir}/gamescopereaper -%{_bindir}/gamescope-legacy -%endif +%{_datadir}/gamescope/* %files libs %{_libdir}/libVkLayer_FROG_gamescope_wsi_*.so %{_datadir}/vulkan/implicit_layer.d/VkLayer_FROG_gamescope_wsi.*.json %changelog -%autochangelog +* Thu Jan 2 2025 Owen-sz +- Package gamescope, port from Bazzite diff --git a/anda/games/gamescope/handheld.patch b/anda/games/gamescope/handheld.patch new file mode 100755 index 0000000000..8db7c5ae35 --- /dev/null +++ b/anda/games/gamescope/handheld.patch @@ -0,0 +1,2439 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Antheas Kapenekakis +Date: Fri, 22 Nov 2024 01:37:48 +0100 +Subject: [NA] add dev script + +--- + sync.sh | 21 +++++++++++++++++++++ + 1 file changed, 21 insertions(+) + create mode 100755 sync.sh + +diff --git a/sync.sh b/sync.sh +new file mode 100755 +index 0000000..878bf6c +--- /dev/null ++++ b/sync.sh +@@ -0,0 +1,21 @@ ++if [ -z "$1" ]; then ++ echo "Usage: $0 " ++ exit 1 ++fi ++ ++HOST=$1 ++RSYNC="rsync -rv --exclude .git --exclude venv --exclude __pycache__'" ++USER=${USER:-bazzite} ++ ++set -e ++ ++meson build/ -Dforce_fallback_for=stb,libdisplay-info,libliftoff,wlroots,vkroots ++ninja -C build/ ++scp build/src/gamescope ${HOST}:gamescope ++ ++ssh $HOST /bin/bash << EOF ++ sudo rpm-ostree usroverlay --hotfix ++ sudo mv ~/gamescope /usr/bin/gamescope ++ bazzite-session-select gamescope ++ # sudo reboot ++EOF +-- +2.47.1 + + +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Matthew Anderson +Date: Fri, 17 May 2024 21:56:55 -0500 +Subject: feat: add --custom-refresh-rates option (+ fixes) + +Commit originally by Matthew, external fixes by Kyle, and new system check +move by Antheas. + +Co-authored-by: Kyle Gospodnetich +Co-authored-by: Antheas Kapenekakis +--- + src/Backends/DRMBackend.cpp | 5 +++++ + src/main.cpp | 31 +++++++++++++++++++++++++++++++ + src/main.hpp | 2 ++ + 3 files changed, 38 insertions(+) + +diff --git a/src/Backends/DRMBackend.cpp b/src/Backends/DRMBackend.cpp +index 0b121e8..75c3258 100644 +--- a/src/Backends/DRMBackend.cpp ++++ b/src/Backends/DRMBackend.cpp +@@ -2243,6 +2243,11 @@ namespace gamescope + bHasKnownHDRInfo = true; + } + } ++ else if ( g_customRefreshRates.size() > 0 && GetScreenType() == GAMESCOPE_SCREEN_TYPE_INTERNAL ) { ++ // Only apply custom refresh rates as a fallback, allowing a graceful transition to the new system. ++ m_Mutable.ValidDynamicRefreshRates = g_customRefreshRates; ++ return; ++ } + } + + if ( !bHasKnownColorimetry ) +diff --git a/src/main.cpp b/src/main.cpp +index 9dff5c4..8381889 100644 +--- a/src/main.cpp ++++ b/src/main.cpp +@@ -129,6 +129,7 @@ const struct option *gamescope_options = (struct option[]){ + { "fade-out-duration", required_argument, nullptr, 0 }, + { "force-orientation", required_argument, nullptr, 0 }, + { "force-windows-fullscreen", no_argument, nullptr, 0 }, ++ { "custom-refresh-rates", required_argument, nullptr, 0 }, + + { "disable-color-management", no_argument, nullptr, 0 }, + { "sdr-gamut-wideness", required_argument, nullptr, 0 }, +@@ -202,6 +203,7 @@ const char usage[] = + " --hdr-itm-target-nits set the target luminace of the inverse tone mapping process.\n" + " Default: 1000 nits, Max: 10000 nits\n" + " --framerate-limit Set a simple framerate limit. Used as a divisor of the refresh rate, rounds down eg 60 / 59 -> 60fps, 60 / 25 -> 30fps. Default: 0, disabled.\n" ++ " --custom-refresh-rates Set custom refresh rates for the output. eg: 60,90,110-120\n" + " --mangoapp Launch with the mangoapp (mangohud) performance overlay enabled. You should use this instead of using mangohud on the game or gamescope.\n" + "\n" + "Nested mode options:\n" +@@ -425,6 +427,33 @@ static enum gamescope::GamescopeBackend parse_backend_name(const char *str) + } + } + ++std::vector g_customRefreshRates; ++// eg: 60,60,90,110-120 ++static std::vector parse_custom_refresh_rates( const char *str ) ++{ ++ std::vector rates; ++ char *token = strtok( strdup(str), ","); ++ while (token) ++ { ++ char *dash = strchr(token, '-'); ++ if (dash) ++ { ++ uint32_t start = atoi(token); ++ uint32_t end = atoi(dash + 1); ++ for (uint32_t i = start; i <= end; i++) ++ { ++ rates.push_back(i); ++ } ++ } ++ else ++ { ++ rates.push_back(atoi(token)); ++ } ++ token = strtok(nullptr, ","); ++ } ++ return rates; ++} ++ + struct sigaction handle_signal_action = {}; + + void ShutdownGamescope() +@@ -746,6 +775,8 @@ int main(int argc, char **argv) + g_eGamescopeModeGeneration = parse_gamescope_mode_generation( optarg ); + } else if (strcmp(opt_name, "force-orientation") == 0) { + g_DesiredInternalOrientation = force_orientation( optarg ); ++ } else if (strcmp(opt_name, "custom-refresh-rates") == 0) { ++ g_customRefreshRates = parse_custom_refresh_rates( optarg ); + } else if (strcmp(opt_name, "sharpness") == 0 || + strcmp(opt_name, "fsr-sharpness") == 0) { + g_upscaleFilterSharpness = atoi( optarg ); +diff --git a/src/main.hpp b/src/main.hpp +index 2e6fb83..390c04a 100644 +--- a/src/main.hpp ++++ b/src/main.hpp +@@ -3,6 +3,7 @@ + #include + + #include ++#include + + extern const char *gamescope_optstring; + extern const struct option *gamescope_options; +@@ -28,6 +29,7 @@ extern bool g_bGrabbed; + + extern float g_mouseSensitivity; + extern const char *g_sOutputName; ++extern std::vector g_customRefreshRates; + + enum class GamescopeUpscaleFilter : uint32_t + { +-- +2.47.1 + + +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Alesh Slovak +Date: Thu, 26 Sep 2024 07:13:24 -0400 +Subject: fix(vrr): Revert "steamcompmgr: Move outdatedInteractiveFocus to + window" + +This reverts commit 299bc3410dcfd46da5e3c988354b60ed3a356900. +--- + src/steamcompmgr.cpp | 39 +++++++++++++++++++++++-------------- + src/steamcompmgr_shared.hpp | 1 - + 2 files changed, 24 insertions(+), 16 deletions(-) + +diff --git a/src/steamcompmgr.cpp b/src/steamcompmgr.cpp +index 11a7cad..df7616d 100644 +--- a/src/steamcompmgr.cpp ++++ b/src/steamcompmgr.cpp +@@ -3299,7 +3299,7 @@ found:; + if ( window_has_commits( focus ) ) + out->focusWindow = focus; + else +- focus->outdatedInteractiveFocus = true; ++ out->outdatedInteractiveFocus = true; + + // Always update X's idea of focus, but still dirty + // the it being outdated so we can resolve that globally later. +@@ -6044,28 +6044,37 @@ bool handle_done_commit( steamcompmgr_win_t *w, xwayland_ctx_t *ctx, uint64_t co + // Window just got a new available commit, determine if that's worth a repaint + + // If this is an overlay that we're presenting, repaint +- if ( w == global_focus.overlayWindow && w->opacity != TRANSLUCENT ) ++ if ( gameFocused ) + { +- hasRepaintNonBasePlane = true; +- } ++ if ( w == global_focus.overlayWindow && w->opacity != TRANSLUCENT ) ++ { ++ hasRepaintNonBasePlane = true; ++ } + +- if ( w == global_focus.notificationWindow && w->opacity != TRANSLUCENT ) +- { +- hasRepaintNonBasePlane = true; ++ if ( w == global_focus.notificationWindow && w->opacity != TRANSLUCENT ) ++ { ++ hasRepaintNonBasePlane = true; ++ } + } +- +- // If this is an external overlay, repaint +- if ( w == global_focus.externalOverlayWindow && w->opacity != TRANSLUCENT ) ++ if ( ctx ) + { +- hasRepaintNonBasePlane = true; ++ if ( ctx->focus.outdatedInteractiveFocus ) ++ { ++ MakeFocusDirty(); ++ ctx->focus.outdatedInteractiveFocus = false; ++ } + } +- +- if ( w->outdatedInteractiveFocus ) ++ if ( global_focus.outdatedInteractiveFocus ) + { + MakeFocusDirty(); +- w->outdatedInteractiveFocus = false; +- } ++ global_focus.outdatedInteractiveFocus = false; + ++ // If this is an external overlay, repaint ++ if ( w == global_focus.externalOverlayWindow && w->opacity != TRANSLUCENT ) ++ { ++ hasRepaintNonBasePlane = true; ++ } ++ } + // If this is the main plane, repaint + if ( w == global_focus.focusWindow && !w->isSteamStreamingClient ) + { +diff --git a/src/steamcompmgr_shared.hpp b/src/steamcompmgr_shared.hpp +index 095694e..e41fad9 100644 +--- a/src/steamcompmgr_shared.hpp ++++ b/src/steamcompmgr_shared.hpp +@@ -125,7 +125,6 @@ struct steamcompmgr_win_t { + unsigned int requestedHeight = 0; + bool is_dialog = false; + bool maybe_a_dropdown = false; +- bool outdatedInteractiveFocus = false; + + bool hasHwndStyle = false; + uint32_t hwndStyle = 0; +-- +2.47.1 + + +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Renn <8340896+AkazaRenn@users.noreply.github.com> +Date: Fri, 11 Oct 2024 17:48:26 +0200 +Subject: fix(deck): Use super + 1/2 for Overlay/QAM + +Replaces the patch for CTRL + 1/2 for Overlay/QAM with Super + 1/2 and +allows for CTRL for a smooth transition. + +Suggested-by: Antheas Kapenekakis +--- + src/wlserver.cpp | 19 ++++++++++++++++++- + 1 file changed, 18 insertions(+), 1 deletion(-) + +diff --git a/src/wlserver.cpp b/src/wlserver.cpp +index 78a86ee..99df8aa 100644 +--- a/src/wlserver.cpp ++++ b/src/wlserver.cpp +@@ -290,6 +290,9 @@ static void wlserver_handle_modifiers(struct wl_listener *listener, void *data) + bump_input_counter(); + } + ++// false if GS_ENABLE_CTRL_12 exists and is 0, true otherwise ++bool env_gs_enable_ctrl_12 = getenv("GS_ENABLE_CTRL_12") ? (getenv("GS_ENABLE_CTRL_12")[0] != '0') : true; ++ + static void wlserver_handle_key(struct wl_listener *listener, void *data) + { + struct wlserver_keyboard *keyboard = wl_container_of( listener, keyboard, key ); +@@ -310,7 +313,14 @@ static void wlserver_handle_key(struct wl_listener *listener, void *data) + keysym == XKB_KEY_XF86AudioLowerVolume || + keysym == XKB_KEY_XF86AudioRaiseVolume || + keysym == XKB_KEY_XF86PowerOff; +- if ( ( event->state == WL_KEYBOARD_KEY_STATE_PRESSED || event->state == WL_KEYBOARD_KEY_STATE_RELEASED ) && forbidden_key ) ++ ++ // Check for steam overlay key (ctrl/super + 1/2) ++ bool is_steamshortcut = ++ ((env_gs_enable_ctrl_12 && (keyboard->wlr->modifiers.depressed & WLR_MODIFIER_CTRL)) || ++ (keyboard->wlr->modifiers.depressed & WLR_MODIFIER_LOGO)) && ++ (keysym == XKB_KEY_1 || keysym == XKB_KEY_2); ++ ++ if ( ( event->state == WL_KEYBOARD_KEY_STATE_PRESSED || event->state == WL_KEYBOARD_KEY_STATE_RELEASED ) && (forbidden_key || is_steamshortcut) ) + { + // Always send volume+/- to root server only, to avoid it reaching the game. + struct wlr_surface *old_kb_surf = wlserver.kb_focus_surface; +@@ -319,6 +329,13 @@ static void wlserver_handle_key(struct wl_listener *listener, void *data) + { + wlserver_keyboardfocus( new_kb_surf, false ); + wlr_seat_set_keyboard( wlserver.wlr.seat, keyboard->wlr ); ++ if (is_steamshortcut) ++ { ++ // send ctrl down modifier to trigger the overlay ++ wlr_keyboard_modifiers ctrl_down_modifier; ++ ctrl_down_modifier.depressed = WLR_MODIFIER_CTRL; ++ wlr_seat_keyboard_notify_modifiers(wlserver.wlr.seat, &ctrl_down_modifier); ++ } + wlr_seat_keyboard_notify_key( wlserver.wlr.seat, event->time_msec, event->keycode, event->state ); + wlserver_keyboardfocus( old_kb_surf, false ); + return; +-- +2.47.1 + + +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Antheas Kapenekakis +Date: Fri, 11 Oct 2024 17:52:48 +0200 +Subject: fix: allow for disabling touch atom click + +Causes issues in certain devices (or not anymore?). + +Parameter option by Kyle. + +Co-authored-by: Kyle Gospodnetich +--- + src/main.cpp | 2 ++ + src/steamcompmgr.cpp | 5 ++++- + 2 files changed, 6 insertions(+), 1 deletion(-) + +diff --git a/src/main.cpp b/src/main.cpp +index 8381889..a76b51b 100644 +--- a/src/main.cpp ++++ b/src/main.cpp +@@ -128,6 +128,7 @@ const struct option *gamescope_options = (struct option[]){ + { "disable-xres", no_argument, nullptr, 'x' }, + { "fade-out-duration", required_argument, nullptr, 0 }, + { "force-orientation", required_argument, nullptr, 0 }, ++ { "disable-touch-click", no_argument, nullptr, 0 }, + { "force-windows-fullscreen", no_argument, nullptr, 0 }, + { "custom-refresh-rates", required_argument, nullptr, 0 }, + +@@ -188,6 +189,7 @@ const char usage[] = + " -T, --stats-path write statistics to path\n" + " -C, --hide-cursor-delay hide cursor image after delay\n" + " -e, --steam enable Steam integration\n" ++ " --disable-touch-click disable touchscreen tap acting as a click\n" + " --xwayland-count create N xwayland servers\n" + " --prefer-vk-device prefer Vulkan device for compositing (ex: 1002:7300)\n" + " --force-orientation rotate the internal display (left, right, normal, upsidedown)\n" +diff --git a/src/steamcompmgr.cpp b/src/steamcompmgr.cpp +index df7616d..4a17499 100644 +--- a/src/steamcompmgr.cpp ++++ b/src/steamcompmgr.cpp +@@ -197,6 +197,7 @@ update_runtime_info(); + gamescope::ConVar cv_adaptive_sync( "adaptive_sync", false, "Whether or not adaptive sync is enabled if available." ); + gamescope::ConVar cv_adaptive_sync_ignore_overlay( "adaptive_sync_ignore_overlay", false, "Whether or not to ignore overlay planes for pushing commits with adaptive sync." ); + gamescope::ConVar cv_adaptive_sync_overlay_cycles( "adaptive_sync_overlay_cycles", 1, "Number of vblank cycles to ignore overlay repaints before forcing a commit with adaptive sync." ); ++gamescope::ConVar cv_disable_touch_click{ "disable_touch_click", false, "Prevents touchscreen taps acting as clicks" }; + + uint64_t g_SteamCompMgrLimitedAppRefreshCycle = 16'666'666; + uint64_t g_SteamCompMgrAppRefreshCycle = 16'666'666; +@@ -5185,7 +5186,7 @@ handle_property_notify(xwayland_ctx_t *ctx, XPropertyEvent *ev) + MakeFocusDirty(); + } + } +- if (ev->atom == ctx->atoms.steamTouchClickModeAtom ) ++ if (ev->atom == ctx->atoms.steamTouchClickModeAtom && !cv_disable_touch_click ) + { + gamescope::cv_touch_click_mode = (gamescope::TouchClickMode) get_prop(ctx, ctx->root, ctx->atoms.steamTouchClickModeAtom, 0u ); + } +@@ -7476,6 +7477,8 @@ steamcompmgr_main(int argc, char **argv) + g_reshade_technique_idx = atoi(optarg); + } else if (strcmp(opt_name, "mura-map") == 0) { + set_mura_overlay(optarg); ++ } else if (strcmp(opt_name, "disable-touch-click") == 0) { ++ cv_disable_touch_click = true; + } + break; + case '?': +-- +2.47.1 + + +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Antheas Kapenekakis +Date: Fri, 11 Oct 2024 21:56:54 +0200 +Subject: fix(intel-gpu): allow for (enabling) hacky texture + +Disabling hacky texture will use more hardware planes, causing some devices to composite yielding lower fps. Required for intel to work +--- + src/main.cpp | 2 ++ + src/steamcompmgr.cpp | 5 ++++- + 2 files changed, 6 insertions(+), 1 deletion(-) + +diff --git a/src/main.cpp b/src/main.cpp +index a76b51b..84e05a9 100644 +--- a/src/main.cpp ++++ b/src/main.cpp +@@ -128,6 +128,7 @@ const struct option *gamescope_options = (struct option[]){ + { "disable-xres", no_argument, nullptr, 'x' }, + { "fade-out-duration", required_argument, nullptr, 0 }, + { "force-orientation", required_argument, nullptr, 0 }, ++ { "enable-hacky-texture", no_argument, nullptr, 0 }, + { "disable-touch-click", no_argument, nullptr, 0 }, + { "force-windows-fullscreen", no_argument, nullptr, 0 }, + { "custom-refresh-rates", required_argument, nullptr, 0 }, +@@ -189,6 +190,7 @@ const char usage[] = + " -T, --stats-path write statistics to path\n" + " -C, --hide-cursor-delay hide cursor image after delay\n" + " -e, --steam enable Steam integration\n" ++ " --enable-hacky-texture enable hacky texture on hw that support it\n" + " --disable-touch-click disable touchscreen tap acting as a click\n" + " --xwayland-count create N xwayland servers\n" + " --prefer-vk-device prefer Vulkan device for compositing (ex: 1002:7300)\n" +diff --git a/src/steamcompmgr.cpp b/src/steamcompmgr.cpp +index 4a17499..da3115f 100644 +--- a/src/steamcompmgr.cpp ++++ b/src/steamcompmgr.cpp +@@ -147,6 +147,7 @@ static lut3d_t g_tmpLut3d; + extern int g_nDynamicRefreshHz; + + bool g_bForceHDRSupportDebug = false; ++bool g_bHackyEnabled = false; + extern float g_flInternalDisplayBrightnessNits; + extern float g_flHDRItmSdrNits; + extern float g_flHDRItmTargetNits; +@@ -2412,7 +2413,7 @@ paint_all(bool async) + if ( overlay == global_focus.inputFocusWindow ) + update_touch_scaling( &frameInfo ); + } +- else if ( !GetBackend()->UsesVulkanSwapchain() && GetBackend()->IsSessionBased() ) ++ else if ( g_bHackyEnabled && !GetBackend()->UsesVulkanSwapchain() && GetBackend()->IsSessionBased() ) + { + auto tex = vulkan_get_hacky_blank_texture(); + if ( tex != nullptr ) +@@ -7479,6 +7480,8 @@ steamcompmgr_main(int argc, char **argv) + set_mura_overlay(optarg); + } else if (strcmp(opt_name, "disable-touch-click") == 0) { + cv_disable_touch_click = true; ++ } else if (strcmp(opt_name, "enable-hacky-texture") == 0) { ++ g_bHackyEnabled = true; + } + break; + case '?': +-- +2.47.1 + + +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Antheas Kapenekakis +Date: Fri, 11 Oct 2024 23:01:13 +0200 +Subject: fix: re-add external orientation options to not break current + sessions (incl. applying ext. orientation) + +--- + src/main.cpp | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/src/main.cpp b/src/main.cpp +index 84e05a9..2398535 100644 +--- a/src/main.cpp ++++ b/src/main.cpp +@@ -129,6 +129,8 @@ const struct option *gamescope_options = (struct option[]){ + { "fade-out-duration", required_argument, nullptr, 0 }, + { "force-orientation", required_argument, nullptr, 0 }, + { "enable-hacky-texture", no_argument, nullptr, 0 }, ++ { "force-panel-type", required_argument, nullptr, 0 }, ++ { "force-external-orientation", required_argument, nullptr, 0 }, + { "disable-touch-click", no_argument, nullptr, 0 }, + { "force-windows-fullscreen", no_argument, nullptr, 0 }, + { "custom-refresh-rates", required_argument, nullptr, 0 }, +@@ -777,7 +779,7 @@ int main(int argc, char **argv) + gamescope::cv_touch_click_mode = (gamescope::TouchClickMode) atoi( optarg ); + } else if (strcmp(opt_name, "generate-drm-mode") == 0) { + g_eGamescopeModeGeneration = parse_gamescope_mode_generation( optarg ); +- } else if (strcmp(opt_name, "force-orientation") == 0) { ++ } else if (strcmp(opt_name, "force-orientation") == 0 || strcmp(opt_name, "force-external-orientation") == 0) { + g_DesiredInternalOrientation = force_orientation( optarg ); + } else if (strcmp(opt_name, "custom-refresh-rates") == 0) { + g_customRefreshRates = parse_custom_refresh_rates( optarg ); +-- +2.47.1 + + +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: honjow +Date: Wed, 16 Oct 2024 00:23:58 +0800 +Subject: fix(external): fix crash when using external touchscreens + +--- + src/wlserver.cpp | 8 ++++++-- + 1 file changed, 6 insertions(+), 2 deletions(-) + +diff --git a/src/wlserver.cpp b/src/wlserver.cpp +index 99df8aa..5e8f516 100644 +--- a/src/wlserver.cpp ++++ b/src/wlserver.cpp +@@ -2492,8 +2492,12 @@ static void apply_touchscreen_orientation(double *x, double *y ) + double tx = 0; + double ty = 0; + +- // Use internal screen always for orientation purposes. +- switch ( GetBackend()->GetConnector( gamescope::GAMESCOPE_SCREEN_TYPE_INTERNAL )->GetCurrentOrientation() ) ++ auto orientation = GAMESCOPE_PANEL_ORIENTATION_AUTO; ++ if ( GetBackend() && GetBackend()->GetCurrentConnector( ) ) ++ { ++ orientation = GetBackend()->GetCurrentConnector()->GetCurrentOrientation(); ++ } ++ switch ( orientation ) + { + default: + case GAMESCOPE_PANEL_ORIENTATION_AUTO: +-- +2.47.1 + + +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Antheas Kapenekakis +Date: Fri, 11 Oct 2024 23:47:59 +0200 +Subject: feat(vrr): allow for setting refresh rate if the internal display + allows + +For the Ally, we have a set of VFP that work to set the refresh rate. +They can also be used for VRR but gamescope does not currently allow for +it. Therefore, bypass some checks to allow it to work just for this usecase. +--- + src/main.cpp | 2 ++ + src/steamcompmgr.cpp | 7 +++++-- + 2 files changed, 7 insertions(+), 2 deletions(-) + +diff --git a/src/main.cpp b/src/main.cpp +index 2398535..0621c65 100644 +--- a/src/main.cpp ++++ b/src/main.cpp +@@ -132,6 +132,7 @@ const struct option *gamescope_options = (struct option[]){ + { "force-panel-type", required_argument, nullptr, 0 }, + { "force-external-orientation", required_argument, nullptr, 0 }, + { "disable-touch-click", no_argument, nullptr, 0 }, ++ { "enable-vrr-modesetting", no_argument, nullptr, 0 }, + { "force-windows-fullscreen", no_argument, nullptr, 0 }, + { "custom-refresh-rates", required_argument, nullptr, 0 }, + +@@ -194,6 +195,7 @@ const char usage[] = + " -e, --steam enable Steam integration\n" + " --enable-hacky-texture enable hacky texture on hw that support it\n" + " --disable-touch-click disable touchscreen tap acting as a click\n" ++ " --enable-vrr-modesetting enable setting framerate while VRR is on in the internal display\n" + " --xwayland-count create N xwayland servers\n" + " --prefer-vk-device prefer Vulkan device for compositing (ex: 1002:7300)\n" + " --force-orientation rotate the internal display (left, right, normal, upsidedown)\n" +diff --git a/src/steamcompmgr.cpp b/src/steamcompmgr.cpp +index da3115f..69fd348 100644 +--- a/src/steamcompmgr.cpp ++++ b/src/steamcompmgr.cpp +@@ -148,6 +148,7 @@ extern int g_nDynamicRefreshHz; + + bool g_bForceHDRSupportDebug = false; + bool g_bHackyEnabled = false; ++bool g_bVRRModesetting = false; + extern float g_flInternalDisplayBrightnessNits; + extern float g_flHDRItmSdrNits; + extern float g_flHDRItmTargetNits; +@@ -899,7 +900,7 @@ bool g_bChangeDynamicRefreshBasedOnGameOpenRatherThanActive = false; + bool steamcompmgr_window_should_limit_fps( steamcompmgr_win_t *w ) + { + // VRR + FPS Limit needs another approach. +- if ( GetBackend()->IsVRRActive() ) ++ if ( GetBackend()->IsVRRActive() && !(g_bVRRModesetting && GetBackend()->GetScreenType() == gamescope::GAMESCOPE_SCREEN_TYPE_INTERNAL) ) + return false; + + return w && !window_is_steam( w ) && !w->isOverlay && !w->isExternalOverlay; +@@ -923,7 +924,7 @@ steamcompmgr_user_has_any_game_open() + + bool steamcompmgr_window_should_refresh_switch( steamcompmgr_win_t *w ) + { +- if ( GetBackend()->IsVRRActive() ) ++ if ( GetBackend()->IsVRRActive() && !(g_bVRRModesetting && GetBackend()->GetScreenType() == gamescope::GAMESCOPE_SCREEN_TYPE_INTERNAL)) + return false; + + if ( g_bChangeDynamicRefreshBasedOnGameOpenRatherThanActive ) +@@ -7480,6 +7481,8 @@ steamcompmgr_main(int argc, char **argv) + set_mura_overlay(optarg); + } else if (strcmp(opt_name, "disable-touch-click") == 0) { + cv_disable_touch_click = true; ++ } else if (strcmp(opt_name, "enable-vrr-modesetting") == 0) { ++ g_bVRRModesetting = true; + } else if (strcmp(opt_name, "enable-hacky-texture") == 0) { + g_bHackyEnabled = true; + } +-- +2.47.1 + + +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Antheas Kapenekakis +Date: Fri, 11 Oct 2024 19:09:05 +0200 +Subject: feat: add external option that now only lies to steam + +Previously, there was a force-panel option that allowed for VRR. +However, this is no longer the case and VRR works fine. +This option still allows for scaling the display though. So, create a +variant of the patch that only does that. +--- + src/main.cpp | 16 ++++++++++++++++ + src/steamcompmgr.cpp | 2 +- + src/steamcompmgr.hpp | 1 + + src/wlserver.cpp | 2 +- + src/wlserver.hpp | 1 + + 5 files changed, 20 insertions(+), 2 deletions(-) + +diff --git a/src/main.cpp b/src/main.cpp +index 0621c65..056e1c1 100644 +--- a/src/main.cpp ++++ b/src/main.cpp +@@ -199,6 +199,7 @@ const char usage[] = + " --xwayland-count create N xwayland servers\n" + " --prefer-vk-device prefer Vulkan device for compositing (ex: 1002:7300)\n" + " --force-orientation rotate the internal display (left, right, normal, upsidedown)\n" ++ " --force-panel-type lie to steam that the screen is external\n" + " --force-windows-fullscreen force windows inside of gamescope to be the size of the nested display (fullscreen)\n" + " --cursor-scale-height if specified, sets a base output height to linearly scale the cursor against.\n" + " --hdr-enabled enable HDR output (needs Gamescope WSI layer enabled for support from clients)\n" +@@ -373,6 +374,19 @@ static GamescopePanelOrientation force_orientation(const char *str) + } + } + ++bool g_FakeExternal = false; ++static bool force_panel_type_external(const char *str) ++{ ++ if (strcmp(str, "internal") == 0) { ++ return false; ++ } else if (strcmp(str, "external") == 0) { ++ return true; ++ } else { ++ fprintf( stderr, "gamescope: invalid value for --force-panel-type\n" ); ++ exit(1); ++ } ++} ++ + static enum GamescopeUpscaleScaler parse_upscaler_scaler(const char *str) + { + if (strcmp(str, "auto") == 0) { +@@ -783,6 +797,8 @@ int main(int argc, char **argv) + g_eGamescopeModeGeneration = parse_gamescope_mode_generation( optarg ); + } else if (strcmp(opt_name, "force-orientation") == 0 || strcmp(opt_name, "force-external-orientation") == 0) { + g_DesiredInternalOrientation = force_orientation( optarg ); ++ } else if (strcmp(opt_name, "force-panel-type") == 0) { ++ g_FakeExternal = force_panel_type_external( optarg ); + } else if (strcmp(opt_name, "custom-refresh-rates") == 0) { + g_customRefreshRates = parse_custom_refresh_rates( optarg ); + } else if (strcmp(opt_name, "sharpness") == 0 || +diff --git a/src/steamcompmgr.cpp b/src/steamcompmgr.cpp +index 69fd348..3dd64f8 100644 +--- a/src/steamcompmgr.cpp ++++ b/src/steamcompmgr.cpp +@@ -7192,7 +7192,7 @@ void update_mode_atoms(xwayland_ctx_t *root_ctx, bool* needs_flush = nullptr) + if (needs_flush) + *needs_flush = true; + +- if ( GetBackend()->GetCurrentConnector() && GetBackend()->GetCurrentConnector()->GetScreenType() == gamescope::GAMESCOPE_SCREEN_TYPE_INTERNAL ) ++ if ( !g_FakeExternal && GetBackend()->GetCurrentConnector() && GetBackend()->GetCurrentConnector()->GetScreenType() == gamescope::GAMESCOPE_SCREEN_TYPE_INTERNAL ) + { + XDeleteProperty(root_ctx->dpy, root_ctx->root, root_ctx->atoms.gamescopeDisplayModeListExternal); + +diff --git a/src/steamcompmgr.hpp b/src/steamcompmgr.hpp +index 9f384c4..30e48e8 100644 +--- a/src/steamcompmgr.hpp ++++ b/src/steamcompmgr.hpp +@@ -127,6 +127,7 @@ extern float focusedWindowScaleY; + extern float focusedWindowOffsetX; + extern float focusedWindowOffsetY; + ++extern bool g_FakeExternal; + extern bool g_bFSRActive; + + extern uint32_t inputCounter; +diff --git a/src/wlserver.cpp b/src/wlserver.cpp +index 5e8f516..1eeaa25 100644 +--- a/src/wlserver.cpp ++++ b/src/wlserver.cpp +@@ -1078,7 +1078,7 @@ static uint32_t get_conn_display_info_flags() + return 0; + + uint32_t flags = 0; +- if ( pConn->GetScreenType() == gamescope::GAMESCOPE_SCREEN_TYPE_INTERNAL ) ++ if ( pConn->GetScreenType() == gamescope::GAMESCOPE_SCREEN_TYPE_INTERNAL && !g_FakeExternal ) + flags |= GAMESCOPE_CONTROL_DISPLAY_FLAG_INTERNAL_DISPLAY; + if ( pConn->SupportsVRR() ) + flags |= GAMESCOPE_CONTROL_DISPLAY_FLAG_SUPPORTS_VRR; +diff --git a/src/wlserver.hpp b/src/wlserver.hpp +index 0569472..104f7a2 100644 +--- a/src/wlserver.hpp ++++ b/src/wlserver.hpp +@@ -190,6 +190,7 @@ struct wlserver_t { + }; + + extern struct wlserver_t wlserver; ++extern bool g_FakeExternal; + + std::vector wlserver_xdg_commit_queue(); + +-- +2.47.1 + + +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Antheas Kapenekakis +Date: Mon, 14 Oct 2024 22:42:20 +0200 +Subject: fix(display-config): always fill in mutable refresh rates + +Assume the user is not lying to us when they fill in dynamic_refresh_rates +and that gamescope will work with e.g., CVT, so accept it even if no +custom modeline generation has been provided. +--- + src/Backends/DRMBackend.cpp | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/src/Backends/DRMBackend.cpp b/src/Backends/DRMBackend.cpp +index 75c3258..f014be9 100644 +--- a/src/Backends/DRMBackend.cpp ++++ b/src/Backends/DRMBackend.cpp +@@ -2161,7 +2161,9 @@ namespace gamescope + sol::optional otDynamicRefreshRates = tTable["dynamic_refresh_rates"]; + sol::optional ofnDynamicModegen = tTable["dynamic_modegen"]; + +- if ( otDynamicRefreshRates && ofnDynamicModegen ) ++ if ( otDynamicRefreshRates && !ofnDynamicModegen ) ++ m_Mutable.ValidDynamicRefreshRates = TableToVector( *otDynamicRefreshRates ); ++ else if ( otDynamicRefreshRates && ofnDynamicModegen ) + { + m_Mutable.ValidDynamicRefreshRates = TableToVector( *otDynamicRefreshRates ); + +-- +2.47.1 + + +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Antheas Kapenekakis +Date: Fri, 25 Oct 2024 21:22:10 +0200 +Subject: fix(vrr): allow frame limiter to work with VRR enabled + +Down to 48hz, modeset the correct framerate. Below 48hz, +disable VRR and use the classic frame limiter. +--- + src/steamcompmgr.cpp | 30 ++++++++++++++++++++++++++++-- + 1 file changed, 28 insertions(+), 2 deletions(-) + +diff --git a/src/steamcompmgr.cpp b/src/steamcompmgr.cpp +index 3dd64f8..7dacfe7 100644 +--- a/src/steamcompmgr.cpp ++++ b/src/steamcompmgr.cpp +@@ -165,6 +165,7 @@ uint32_t g_reshade_technique_idx = 0; + + bool g_bSteamIsActiveWindow = false; + bool g_bForceInternal = false; ++bool g_bVRRRequested = false; + + static std::vector< steamcompmgr_win_t* > GetGlobalPossibleFocusWindows(); + static bool +@@ -827,6 +828,28 @@ static void _update_app_target_refresh_cycle() + { + auto rates = GetBackend()->GetCurrentConnector()->GetValidDynamicRefreshRates(); + ++ if (g_bVRRModesetting) { ++ if (g_bVRRRequested) { ++ // If modeset VRR, go upwards to match the refresh rate 1-1. Refresh ++ // doubling would hurt us here by breaking the frame limiter. ++ for ( auto rate = rates.begin(); rate != rates.end(); rate++ ) ++ { ++ if ((int)*rate == target_fps) ++ { ++ g_nDynamicRefreshRate[ type ] = *rate; ++ // Enable VRR as we have the correct refresh rate ++ cv_adaptive_sync = true; ++ return; ++ } ++ } ++ // Otherwise, disable VRR as we can't match the refresh rate 1-1 ++ // (e.g., below 48hz). ++ cv_adaptive_sync = false; ++ } else { ++ cv_adaptive_sync = false; ++ } ++ } ++ + // Find highest mode to do refresh doubling with. + for ( auto rate = rates.rbegin(); rate != rates.rend(); rate++ ) + { +@@ -5522,8 +5545,11 @@ handle_property_notify(xwayland_ctx_t *ctx, XPropertyEvent *ev) + } + if ( ev->atom == ctx->atoms.gamescopeVRREnabled ) + { +- bool enabled = !!get_prop( ctx, ctx->root, ctx->atoms.gamescopeVRREnabled, 0 ); +- cv_adaptive_sync = enabled; ++ g_bVRRRequested = !!get_prop( ctx, ctx->root, ctx->atoms.gamescopeVRREnabled, 0 ); ++ // Try to match refresh rate and have that set the cv_adaptive_sync only if it can ++ if (g_bVRRModesetting) update_app_target_refresh_cycle(); ++ // otherwise, fall back to original behavior ++ else cv_adaptive_sync = g_bVRRRequested; + } + if ( ev->atom == ctx->atoms.gamescopeDisplayForceInternal ) + { +-- +2.47.1 + + +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Antheas Kapenekakis +Date: Wed, 30 Oct 2024 00:39:03 +0100 +Subject: fix(battery): run at half hz while at steamUI and disable VRR V2 + + param + +--- + src/steamcompmgr.cpp | 43 ++++++++++++++++++++++++++++++++----------- + 1 file changed, 32 insertions(+), 11 deletions(-) + +diff --git a/src/steamcompmgr.cpp b/src/steamcompmgr.cpp +index 7dacfe7..4098c44 100644 +--- a/src/steamcompmgr.cpp ++++ b/src/steamcompmgr.cpp +@@ -166,6 +166,9 @@ uint32_t g_reshade_technique_idx = 0; + bool g_bSteamIsActiveWindow = false; + bool g_bForceInternal = false; + bool g_bVRRRequested = false; ++bool g_bVRRCanEnable = false; ++bool b_bForceFrameLimit = false; ++bool g_bRefreshHalveEnable = false; + + static std::vector< steamcompmgr_win_t* > GetGlobalPossibleFocusWindows(); + static bool +@@ -793,6 +796,7 @@ uint64_t g_uCurrentBasePlaneCommitID = 0; + bool g_bCurrentBasePlaneIsFifo = false; + + static int g_nSteamCompMgrTargetFPS = 0; ++static int g_nSteamCompMgrTargetFPSreq = 0; + static uint64_t g_uDynamicRefreshEqualityTime = 0; + static int g_nDynamicRefreshRate[gamescope::GAMESCOPE_SCREEN_TYPE_COUNT] = { 0, 0 }; + // Delay to stop modes flickering back and forth. +@@ -812,7 +816,7 @@ static void _update_app_target_refresh_cycle() + int target_fps = g_nCombinedAppRefreshCycleOverride[type]; + + g_nDynamicRefreshRate[ type ] = 0; +- g_nSteamCompMgrTargetFPS = 0; ++ g_nSteamCompMgrTargetFPSreq = 0; + + if ( !target_fps ) + { +@@ -821,7 +825,7 @@ static void _update_app_target_refresh_cycle() + + if ( g_nCombinedAppRefreshCycleChangeFPS[ type ] ) + { +- g_nSteamCompMgrTargetFPS = target_fps; ++ g_nSteamCompMgrTargetFPSreq = target_fps; + } + + if ( g_nCombinedAppRefreshCycleChangeRefresh[ type ] ) +@@ -838,15 +842,15 @@ static void _update_app_target_refresh_cycle() + { + g_nDynamicRefreshRate[ type ] = *rate; + // Enable VRR as we have the correct refresh rate +- cv_adaptive_sync = true; ++ g_bVRRCanEnable = true; + return; + } + } + // Otherwise, disable VRR as we can't match the refresh rate 1-1 + // (e.g., below 48hz). +- cv_adaptive_sync = false; ++ g_bVRRCanEnable = false; + } else { +- cv_adaptive_sync = false; ++ g_bVRRCanEnable = false; + } + } + +@@ -864,9 +868,9 @@ static void _update_app_target_refresh_cycle() + + static void update_app_target_refresh_cycle() + { +- int nPrevFPSLimit = g_nSteamCompMgrTargetFPS; ++ int nPrevFPSLimit = g_nSteamCompMgrTargetFPSreq; + _update_app_target_refresh_cycle(); +- if ( !!g_nSteamCompMgrTargetFPS != !!nPrevFPSLimit ) ++ if ( !!g_nSteamCompMgrTargetFPSreq != !!nPrevFPSLimit ) + update_runtime_info(); + } + +@@ -5052,7 +5056,7 @@ update_runtime_info() + if ( g_nRuntimeInfoFd < 0 ) + return; + +- uint32_t limiter_enabled = g_nSteamCompMgrTargetFPS != 0 ? 1 : 0; ++ uint32_t limiter_enabled = g_nSteamCompMgrTargetFPSreq != 0 ? 1 : 0; + pwrite( g_nRuntimeInfoFd, &limiter_enabled, sizeof( limiter_enabled ), 0 ); + } + +@@ -5109,7 +5113,7 @@ static bool steamcompmgr_should_vblank_window( bool bShouldLimitFPS, uint64_t vb + + int nRefreshHz = gamescope::ConvertmHzToHz( g_nNestedRefresh ? g_nNestedRefresh : g_nOutputRefresh ); + int nTargetFPS = g_nSteamCompMgrTargetFPS; +- if ( g_nSteamCompMgrTargetFPS && bShouldLimitFPS && nRefreshHz > nTargetFPS ) ++ if ( g_nSteamCompMgrTargetFPS && (bShouldLimitFPS || b_bForceFrameLimit) && nRefreshHz > nTargetFPS ) + { + int nVblankDivisor = nRefreshHz / nTargetFPS; + +@@ -5485,7 +5489,7 @@ handle_property_notify(xwayland_ctx_t *ctx, XPropertyEvent *ev) + } + if ( ev->atom == ctx->atoms.gamescopeFPSLimit ) + { +- g_nSteamCompMgrTargetFPS = get_prop( ctx, ctx->root, ctx->atoms.gamescopeFPSLimit, 0 ); ++ g_nSteamCompMgrTargetFPSreq = get_prop( ctx, ctx->root, ctx->atoms.gamescopeFPSLimit, 0 ); + update_runtime_info(); + } + for (int i = 0; i < gamescope::GAMESCOPE_SCREEN_TYPE_COUNT; i++) +@@ -5549,7 +5553,7 @@ handle_property_notify(xwayland_ctx_t *ctx, XPropertyEvent *ev) + // Try to match refresh rate and have that set the cv_adaptive_sync only if it can + if (g_bVRRModesetting) update_app_target_refresh_cycle(); + // otherwise, fall back to original behavior +- else cv_adaptive_sync = g_bVRRRequested; ++ else g_bVRRCanEnable = g_bVRRRequested; + } + if ( ev->atom == ctx->atoms.gamescopeDisplayForceInternal ) + { +@@ -7628,6 +7632,23 @@ steamcompmgr_main(int argc, char **argv) + // as a question. + const bool bIsVBlankFromTimer = vblank; + ++ if ( g_bRefreshHalveEnable && window_is_steam( global_focus.focusWindow ) ) { ++ // Halve refresh rate and disable vrr on SteamUI ++ cv_adaptive_sync = false; ++ int nRealRefreshHz = gamescope::ConvertmHzToHz( g_nNestedRefresh ? g_nNestedRefresh : g_nOutputRefresh ); ++ if (nRealRefreshHz > 100 && g_nSteamCompMgrTargetFPSreq > 34) { ++ g_nSteamCompMgrTargetFPS = nRealRefreshHz / 2; ++ b_bForceFrameLimit = true; ++ } else { ++ g_nSteamCompMgrTargetFPS = g_nSteamCompMgrTargetFPSreq; ++ b_bForceFrameLimit = false; ++ } ++ } else { ++ cv_adaptive_sync = g_bVRRCanEnable; ++ g_nSteamCompMgrTargetFPS = g_nSteamCompMgrTargetFPSreq; ++ b_bForceFrameLimit = false; ++ } ++ + // We can always vblank if VRR. + const bool bVRR = GetBackend()->IsVRRActive(); + if ( bVRR ) +-- +2.47.1 + + +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Antheas Kapenekakis +Date: Fri, 1 Nov 2024 17:27:54 +0100 +Subject: feat(battery): add atom for controlling frame halving + +--- + src/steamcompmgr.cpp | 6 ++++++ + src/xwayland_ctx.hpp | 2 ++ + 2 files changed, 8 insertions(+) + +diff --git a/src/steamcompmgr.cpp b/src/steamcompmgr.cpp +index 4098c44..6c8ce74 100644 +--- a/src/steamcompmgr.cpp ++++ b/src/steamcompmgr.cpp +@@ -5919,6 +5919,10 @@ handle_property_notify(xwayland_ctx_t *ctx, XPropertyEvent *ev) + MakeFocusDirty(); + } + } ++ if ( ev->atom == ctx->atoms.gamescopeFrameHalveAtom ) ++ { ++ g_bRefreshHalveEnable = !!get_prop( ctx, ctx->root, ctx->atoms.gamescopeFrameHalveAtom, 0 ); ++ } + } + + static int +@@ -7089,6 +7093,8 @@ void init_xwayland_ctx(uint32_t serverId, gamescope_xwayland_server_t *xwayland_ + ctx->atoms.primarySelection = XInternAtom(ctx->dpy, "PRIMARY", false); + ctx->atoms.targets = XInternAtom(ctx->dpy, "TARGETS", false); + ++ ctx->atoms.gamescopeFrameHalveAtom = XInternAtom( ctx->dpy, "GAMESCOPE_STEAMUI_HALFHZ", false );; ++ + ctx->root_width = DisplayWidth(ctx->dpy, ctx->scr); + ctx->root_height = DisplayHeight(ctx->dpy, ctx->scr); + +diff --git a/src/xwayland_ctx.hpp b/src/xwayland_ctx.hpp +index df2af70..e4eec9f 100644 +--- a/src/xwayland_ctx.hpp ++++ b/src/xwayland_ctx.hpp +@@ -246,6 +246,8 @@ struct xwayland_ctx_t final : public gamescope::IWaitable + Atom clipboard; + Atom primarySelection; + Atom targets; ++ ++ Atom gamescopeFrameHalveAtom; + } atoms; + + bool HasQueuedEvents(); +-- +2.47.1 + + +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Antheas Kapenekakis +Date: Wed, 13 Nov 2024 17:22:05 +0100 +Subject: feat: add DPMS support through an Atom + +--- + src/Backends/DRMBackend.cpp | 16 +++++++++++++--- + src/rendervulkan.hpp | 2 ++ + src/steamcompmgr.cpp | 15 ++++++++++++--- + src/xwayland_ctx.hpp | 1 + + 4 files changed, 28 insertions(+), 6 deletions(-) + +diff --git a/src/Backends/DRMBackend.cpp b/src/Backends/DRMBackend.cpp +index f014be9..6bb0b88 100644 +--- a/src/Backends/DRMBackend.cpp ++++ b/src/Backends/DRMBackend.cpp +@@ -2669,6 +2669,9 @@ int drm_prepare( struct drm_t *drm, bool async, const struct FrameInfo_t *frameI + drm->needs_modeset = true; + } + ++ if (drm->pCRTC && drm->pCRTC->GetProperties().ACTIVE->GetCurrentValue() != !frameInfo->dpms) ++ drm->needs_modeset = true; ++ + drm_colorspace uColorimetry = DRM_MODE_COLORIMETRY_DEFAULT; + + const bool bWantsHDR10 = g_bOutputHDREnabled && frameInfo->outputEncodingEOTF == EOTF_PQ; +@@ -2724,7 +2727,7 @@ int drm_prepare( struct drm_t *drm, bool async, const struct FrameInfo_t *frameI + uint32_t flags = DRM_MODE_ATOMIC_NONBLOCK; + + // We do internal refcounting with these events +- if ( drm->pCRTC != nullptr ) ++ if ( !frameInfo->dpms && drm->pCRTC != nullptr) + flags |= DRM_MODE_PAGE_FLIP_EVENT; + + if ( async || g_bForceAsyncFlips ) +@@ -2797,7 +2800,13 @@ int drm_prepare( struct drm_t *drm, bool async, const struct FrameInfo_t *frameI + + if ( drm->pCRTC ) + { +- drm->pCRTC->GetProperties().ACTIVE->SetPendingValue( drm->req, 1u, true ); ++ if ( frameInfo->dpms ) { ++ // We can't disable a CRTC if it's already disabled ++ if (drm->pCRTC->GetProperties().ACTIVE->GetCurrentValue() != 0) ++ drm->pCRTC->GetProperties().ACTIVE->SetPendingValue(drm->req, 0, true); ++ } ++ else ++ drm->pCRTC->GetProperties().ACTIVE->SetPendingValue( drm->req, 1u, true ); + drm->pCRTC->GetProperties().MODE_ID->SetPendingValue( drm->req, drm->pending.mode_id ? drm->pending.mode_id->GetBlobValue() : 0lu, true ); + + if ( drm->pCRTC->GetProperties().VRR_ENABLED ) +@@ -2828,7 +2837,7 @@ int drm_prepare( struct drm_t *drm, bool async, const struct FrameInfo_t *frameI + drm->flags = flags; + + int ret; +- if ( drm->pCRTC == nullptr ) { ++ if (frameInfo->dpms || drm->pCRTC == nullptr ) { + ret = 0; + } else if ( drm->bUseLiftoff ) { + ret = drm_prepare_liftoff( drm, frameInfo, needs_modeset ); +@@ -3391,6 +3400,7 @@ namespace gamescope + + FrameInfo_t presentCompFrameInfo = {}; + presentCompFrameInfo.allowVRR = pFrameInfo->allowVRR; ++ presentCompFrameInfo.dpms = pFrameInfo->dpms; + presentCompFrameInfo.outputEncodingEOTF = pFrameInfo->outputEncodingEOTF; + + if ( bNeedsFullComposite ) +diff --git a/src/rendervulkan.hpp b/src/rendervulkan.hpp +index b537170..ccabd88 100644 +--- a/src/rendervulkan.hpp ++++ b/src/rendervulkan.hpp +@@ -281,6 +281,8 @@ struct FrameInfo_t + bool applyOutputColorMgmt; // drm only + EOTF outputEncodingEOTF; + ++ bool dpms; ++ + int layerCount; + struct Layer_t + { +diff --git a/src/steamcompmgr.cpp b/src/steamcompmgr.cpp +index 6c8ce74..dfee904 100644 +--- a/src/steamcompmgr.cpp ++++ b/src/steamcompmgr.cpp +@@ -169,6 +169,8 @@ bool g_bVRRRequested = false; + bool g_bVRRCanEnable = false; + bool b_bForceFrameLimit = false; + bool g_bRefreshHalveEnable = false; ++bool g_bDPMS = false; ++bool g_bDPMS_set = false; + + static std::vector< steamcompmgr_win_t* > GetGlobalPossibleFocusWindows(); + static bool +@@ -2271,7 +2273,7 @@ bool ShouldDrawCursor() + } + + static void +-paint_all(bool async) ++paint_all(bool async, bool dpms) + { + gamescope_xwayland_server_t *root_server = wlserver_get_xwayland_server(0); + xwayland_ctx_t *root_ctx = root_server->ctx.get(); +@@ -2322,6 +2324,7 @@ paint_all(bool async) + frameInfo.outputEncodingEOTF = g_ColorMgmt.pending.outputEncodingEOTF; + frameInfo.allowVRR = cv_adaptive_sync; + frameInfo.bFadingOut = fadingOut; ++ frameInfo.dpms = dpms; + + // If the window we'd paint as the base layer is the streaming client, + // find the video underlay and put it up first in the scenegraph +@@ -5923,6 +5926,10 @@ handle_property_notify(xwayland_ctx_t *ctx, XPropertyEvent *ev) + { + g_bRefreshHalveEnable = !!get_prop( ctx, ctx->root, ctx->atoms.gamescopeFrameHalveAtom, 0 ); + } ++ if (ev->atom == ctx->atoms.gamescopeDPMS) ++ { ++ g_bDPMS = !!get_prop(ctx, ctx->root, ctx->atoms.gamescopeDPMS, 0); ++ } + } + + static int +@@ -7094,6 +7101,7 @@ void init_xwayland_ctx(uint32_t serverId, gamescope_xwayland_server_t *xwayland_ + ctx->atoms.targets = XInternAtom(ctx->dpy, "TARGETS", false); + + ctx->atoms.gamescopeFrameHalveAtom = XInternAtom( ctx->dpy, "GAMESCOPE_STEAMUI_HALFHZ", false );; ++ ctx->atoms.gamescopeDPMS = XInternAtom(ctx->dpy, "GAMESCOPE_DPMS", false); + + ctx->root_width = DisplayWidth(ctx->dpy, ctx->scr); + ctx->root_height = DisplayHeight(ctx->dpy, ctx->scr); +@@ -8061,9 +8069,10 @@ steamcompmgr_main(int argc, char **argv) + bShouldPaint = false; + } + +- if ( bShouldPaint ) ++ if (bShouldPaint || (g_bDPMS != g_bDPMS_set && vblank)) + { +- paint_all( eFlipType == FlipType::Async ); ++ g_bDPMS_set = g_bDPMS; ++ paint_all( eFlipType == FlipType::Async, g_bDPMS ); + + hasRepaint = false; + hasRepaintNonBasePlane = false; +diff --git a/src/xwayland_ctx.hpp b/src/xwayland_ctx.hpp +index e4eec9f..2347cbb 100644 +--- a/src/xwayland_ctx.hpp ++++ b/src/xwayland_ctx.hpp +@@ -248,6 +248,7 @@ struct xwayland_ctx_t final : public gamescope::IWaitable + Atom targets; + + Atom gamescopeFrameHalveAtom; ++ Atom gamescopeDPMS; + } atoms; + + bool HasQueuedEvents(); +-- +2.47.1 + + +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Joshua Tam <297250+joshuatam@users.noreply.github.com> +Date: Fri, 6 Dec 2024 16:51:02 +0800 +Subject: feat: add rotation shader for rotating output + +--- + src/Backends/DRMBackend.cpp | 29 +++++++- + src/main.cpp | 6 ++ + src/main.hpp | 1 + + src/meson.build | 1 + + src/rendervulkan.cpp | 126 ++++++++++++++++++++++++++++++----- + src/rendervulkan.hpp | 6 +- + src/shaders/cs_rotation.comp | 53 +++++++++++++++ + src/wlserver.cpp | 5 ++ + 8 files changed, 208 insertions(+), 19 deletions(-) + create mode 100644 src/shaders/cs_rotation.comp + +diff --git a/src/Backends/DRMBackend.cpp b/src/Backends/DRMBackend.cpp +index 6bb0b88..506963d 100644 +--- a/src/Backends/DRMBackend.cpp ++++ b/src/Backends/DRMBackend.cpp +@@ -1752,7 +1752,7 @@ LiftoffStateCacheEntry FrameInfoToLiftoffStateCacheEntry( struct drm_t *drm, con + uint64_t crtcW = srcWidth / frameInfo->layers[ i ].scale.x; + uint64_t crtcH = srcHeight / frameInfo->layers[ i ].scale.y; + +- if (g_bRotated) ++ if (g_bRotated && !g_bUseRotationShader) + { + int64_t imageH = frameInfo->layers[ i ].tex->contentHeight() / frameInfo->layers[ i ].scale.y; + +@@ -2045,6 +2045,17 @@ namespace gamescope + + void CDRMConnector::UpdateEffectiveOrientation( const drmModeModeInfo *pMode ) + { ++ if (g_bUseRotationShader) ++ { ++ drm_log.infof("Using rotation shader"); ++ if (g_DesiredInternalOrientation == GAMESCOPE_PANEL_ORIENTATION_270) { ++ m_ChosenOrientation = GAMESCOPE_PANEL_ORIENTATION_180; ++ } else { ++ m_ChosenOrientation = GAMESCOPE_PANEL_ORIENTATION_0; ++ } ++ return; ++ } ++ + if ( this->GetScreenType() == GAMESCOPE_SCREEN_TYPE_INTERNAL && g_DesiredInternalOrientation != GAMESCOPE_PANEL_ORIENTATION_AUTO ) + { + m_ChosenOrientation = g_DesiredInternalOrientation; +@@ -3035,6 +3046,15 @@ bool drm_set_mode( struct drm_t *drm, const drmModeModeInfo *mode ) + g_bRotated = false; + g_nOutputWidth = mode->hdisplay; + g_nOutputHeight = mode->vdisplay; ++ ++ if (g_bUseRotationShader && drm->pConnector->GetScreenType() == gamescope::GAMESCOPE_SCREEN_TYPE_INTERNAL) { ++ g_bRotated = true; ++ g_nOutputWidth = mode->vdisplay; ++ g_nOutputHeight = mode->hdisplay; ++ } else { ++ g_bUseRotationShader = false; ++ } ++ + break; + case GAMESCOPE_PANEL_ORIENTATION_90: + case GAMESCOPE_PANEL_ORIENTATION_270: +@@ -3294,6 +3314,11 @@ namespace gamescope + + bNeedsFullComposite |= !!(g_uCompositeDebug & CompositeDebugFlag::Heatmap); + ++ if (g_bUseRotationShader) ++ { ++ bNeedsFullComposite = true; ++ } ++ + bool bDoComposite = true; + if ( !bNeedsFullComposite && !bWantsPartialComposite ) + { +@@ -3384,7 +3409,7 @@ namespace gamescope + if ( bDefer && !!( g_uCompositeDebug & CompositeDebugFlag::Markers ) ) + g_uCompositeDebug |= CompositeDebugFlag::Markers_Partial; + +- std::optional oCompositeResult = vulkan_composite( &compositeFrameInfo, nullptr, !bNeedsFullComposite ); ++ std::optional oCompositeResult = vulkan_composite( &compositeFrameInfo, nullptr, !bNeedsFullComposite, nullptr, true, nullptr, g_bUseRotationShader ); + + m_bWasCompositing = true; + +diff --git a/src/main.cpp b/src/main.cpp +index 056e1c1..f61c88f 100644 +--- a/src/main.cpp ++++ b/src/main.cpp +@@ -127,6 +127,7 @@ const struct option *gamescope_options = (struct option[]){ + { "composite-debug", no_argument, nullptr, 0 }, + { "disable-xres", no_argument, nullptr, 'x' }, + { "fade-out-duration", required_argument, nullptr, 0 }, ++ { "use-rotation-shader", required_argument, nullptr, 0 }, + { "force-orientation", required_argument, nullptr, 0 }, + { "enable-hacky-texture", no_argument, nullptr, 0 }, + { "force-panel-type", required_argument, nullptr, 0 }, +@@ -198,6 +199,7 @@ const char usage[] = + " --enable-vrr-modesetting enable setting framerate while VRR is on in the internal display\n" + " --xwayland-count create N xwayland servers\n" + " --prefer-vk-device prefer Vulkan device for compositing (ex: 1002:7300)\n" ++ " --use-rotation-shader use rotation shader for rotating the screen\n" + " --force-orientation rotate the internal display (left, right, normal, upsidedown)\n" + " --force-panel-type lie to steam that the screen is external\n" + " --force-windows-fullscreen force windows inside of gamescope to be the size of the nested display (fullscreen)\n" +@@ -357,6 +359,8 @@ static gamescope::GamescopeModeGeneration parse_gamescope_mode_generation( const + } + } + ++bool g_bUseRotationShader = false; ++ + GamescopePanelOrientation g_DesiredInternalOrientation = GAMESCOPE_PANEL_ORIENTATION_AUTO; + static GamescopePanelOrientation force_orientation(const char *str) + { +@@ -797,6 +801,8 @@ int main(int argc, char **argv) + g_eGamescopeModeGeneration = parse_gamescope_mode_generation( optarg ); + } else if (strcmp(opt_name, "force-orientation") == 0 || strcmp(opt_name, "force-external-orientation") == 0) { + g_DesiredInternalOrientation = force_orientation( optarg ); ++ } else if (strcmp(opt_name, "use-rotation-shader") == 0) { ++ g_bUseRotationShader = true; + } else if (strcmp(opt_name, "force-panel-type") == 0) { + g_FakeExternal = force_panel_type_external( optarg ); + } else if (strcmp(opt_name, "custom-refresh-rates") == 0) { +diff --git a/src/main.hpp b/src/main.hpp +index 390c04a..2464afa 100644 +--- a/src/main.hpp ++++ b/src/main.hpp +@@ -22,6 +22,7 @@ extern bool g_bForceRelativeMouse; + extern int g_nOutputRefresh; // mHz + extern bool g_bOutputHDREnabled; + extern bool g_bForceInternal; ++extern bool g_bUseRotationShader; + + extern bool g_bFullscreen; + +diff --git a/src/meson.build b/src/meson.build +index 74fc033..d4ff3ea 100644 +--- a/src/meson.build ++++ b/src/meson.build +@@ -70,6 +70,7 @@ shader_src = [ + 'shaders/cs_nis.comp', + 'shaders/cs_nis_fp16.comp', + 'shaders/cs_rgb_to_nv12.comp', ++ 'shaders/cs_rotation.comp', + ] + + spirv_shaders = glsl_generator.process(shader_src) +diff --git a/src/rendervulkan.cpp b/src/rendervulkan.cpp +index 54d7608..10d6c78 100644 +--- a/src/rendervulkan.cpp ++++ b/src/rendervulkan.cpp +@@ -48,6 +48,7 @@ + #include "cs_nis.h" + #include "cs_nis_fp16.h" + #include "cs_rgb_to_nv12.h" ++#include "cs_rotation.h" + + #define A_CPU + #include "shaders/ffx_a.h" +@@ -898,6 +899,7 @@ bool CVulkanDevice::createShaders() + SHADER(NIS, cs_nis); + } + SHADER(RGB_TO_NV12, cs_rgb_to_nv12); ++ SHADER(ROTATION, cs_rotation); + #undef SHADER + + for (uint32_t i = 0; i < shaderInfos.size(); i++) +@@ -1128,6 +1130,7 @@ void CVulkanDevice::compileAllPipelines() + SHADER(EASU, 1, 1, 1); + SHADER(NIS, 1, 1, 1); + SHADER(RGB_TO_NV12, 1, 1, 1); ++ SHADER(ROTATION, k_nMaxLayers, k_nMaxYcbcrMask_ToPreCompile, k_nMaxBlurLayers); + #undef SHADER + + for (auto& info : pipelineInfos) { +@@ -3214,8 +3217,16 @@ static bool vulkan_make_output_images( VulkanOutput_t *pOutput ) + + uint32_t uDRMFormat = pOutput->uOutputFormat; + ++ uint32_t l_nOutputWidth = g_nOutputWidth; ++ uint32_t l_nOutputHeight = g_nOutputHeight; ++ ++ if (g_bUseRotationShader) { ++ l_nOutputWidth = g_nOutputHeight; ++ l_nOutputHeight = g_nOutputWidth; ++ } ++ + pOutput->outputImages[0] = new CVulkanTexture(); +- bool bSuccess = pOutput->outputImages[0]->BInit( g_nOutputWidth, g_nOutputHeight, 1u, uDRMFormat, outputImageflags ); ++ bool bSuccess = pOutput->outputImages[0]->BInit( l_nOutputWidth, l_nOutputHeight, 1u, uDRMFormat, outputImageflags ); + if ( bSuccess != true ) + { + vk_log.errorf( "failed to allocate buffer for KMS" ); +@@ -3223,7 +3234,7 @@ static bool vulkan_make_output_images( VulkanOutput_t *pOutput ) + } + + pOutput->outputImages[1] = new CVulkanTexture(); +- bSuccess = pOutput->outputImages[1]->BInit( g_nOutputWidth, g_nOutputHeight, 1u, uDRMFormat, outputImageflags ); ++ bSuccess = pOutput->outputImages[1]->BInit( l_nOutputWidth, l_nOutputHeight, 1u, uDRMFormat, outputImageflags ); + if ( bSuccess != true ) + { + vk_log.errorf( "failed to allocate buffer for KMS" ); +@@ -3231,7 +3242,7 @@ static bool vulkan_make_output_images( VulkanOutput_t *pOutput ) + } + + pOutput->outputImages[2] = new CVulkanTexture(); +- bSuccess = pOutput->outputImages[2]->BInit( g_nOutputWidth, g_nOutputHeight, 1u, uDRMFormat, outputImageflags ); ++ bSuccess = pOutput->outputImages[2]->BInit( l_nOutputWidth, l_nOutputHeight, 1u, uDRMFormat, outputImageflags ); + if ( bSuccess != true ) + { + vk_log.errorf( "failed to allocate buffer for KMS" ); +@@ -3246,7 +3257,7 @@ static bool vulkan_make_output_images( VulkanOutput_t *pOutput ) + uint32_t uPartialDRMFormat = pOutput->uOutputFormatOverlay; + + pOutput->outputImagesPartialOverlay[0] = new CVulkanTexture(); +- bool bSuccess = pOutput->outputImagesPartialOverlay[0]->BInit( g_nOutputWidth, g_nOutputHeight, 1u, uPartialDRMFormat, outputImageflags, nullptr, 0, 0, pOutput->outputImages[0].get() ); ++ bool bSuccess = pOutput->outputImagesPartialOverlay[0]->BInit( l_nOutputWidth, l_nOutputHeight, 1u, uPartialDRMFormat, outputImageflags, nullptr, 0, 0, pOutput->outputImages[0].get() ); + if ( bSuccess != true ) + { + vk_log.errorf( "failed to allocate buffer for KMS" ); +@@ -3254,7 +3265,7 @@ static bool vulkan_make_output_images( VulkanOutput_t *pOutput ) + } + + pOutput->outputImagesPartialOverlay[1] = new CVulkanTexture(); +- bSuccess = pOutput->outputImagesPartialOverlay[1]->BInit( g_nOutputWidth, g_nOutputHeight, 1u, uPartialDRMFormat, outputImageflags, nullptr, 0, 0, pOutput->outputImages[1].get() ); ++ bSuccess = pOutput->outputImagesPartialOverlay[1]->BInit( l_nOutputWidth, l_nOutputHeight, 1u, uPartialDRMFormat, outputImageflags, nullptr, 0, 0, pOutput->outputImages[1].get() ); + if ( bSuccess != true ) + { + vk_log.errorf( "failed to allocate buffer for KMS" ); +@@ -3262,7 +3273,7 @@ static bool vulkan_make_output_images( VulkanOutput_t *pOutput ) + } + + pOutput->outputImagesPartialOverlay[2] = new CVulkanTexture(); +- bSuccess = pOutput->outputImagesPartialOverlay[2]->BInit( g_nOutputWidth, g_nOutputHeight, 1u, uPartialDRMFormat, outputImageflags, nullptr, 0, 0, pOutput->outputImages[2].get() ); ++ bSuccess = pOutput->outputImagesPartialOverlay[2]->BInit( l_nOutputWidth, l_nOutputHeight, 1u, uPartialDRMFormat, outputImageflags, nullptr, 0, 0, pOutput->outputImages[2].get() ); + if ( bSuccess != true ) + { + vk_log.errorf( "failed to allocate buffer for KMS" ); +@@ -3392,6 +3403,28 @@ static void update_tmp_images( uint32_t width, uint32_t height ) + } + } + ++static void update_rotated_images( uint32_t width, uint32_t height ) ++{ ++ if ( g_output.rotatedOutput != nullptr ++ && width == g_output.rotatedOutput->width() ++ && height == g_output.rotatedOutput->height() ) ++ { ++ return; ++ } ++ ++ CVulkanTexture::createFlags createFlags; ++ createFlags.bSampled = true; ++ createFlags.bStorage = true; ++ ++ g_output.rotatedOutput = new CVulkanTexture(); ++ bool bSuccess = g_output.rotatedOutput->BInit( width, height, 1u, DRM_FORMAT_ARGB8888, createFlags, nullptr ); ++ ++ if ( !bSuccess ) ++ { ++ vk_log.errorf( "failed to create rotated output" ); ++ return; ++ } ++} + + static bool init_nis_data() + { +@@ -3856,7 +3889,7 @@ std::optional vulkan_screenshot( const struct FrameInfo_t *frameInfo, + extern std::string g_reshade_effect; + extern uint32_t g_reshade_technique_idx; + +-std::optional vulkan_composite( struct FrameInfo_t *frameInfo, gamescope::Rc pPipewireTexture, bool partial, gamescope::Rc pOutputOverride, bool increment, std::unique_ptr pInCommandBuffer ) ++std::optional vulkan_composite( struct FrameInfo_t *frameInfo, gamescope::Rc pPipewireTexture, bool partial, gamescope::Rc pOutputOverride, bool increment, std::unique_ptr pInCommandBuffer, bool applyRotation ) + { + EOTF outputTF = frameInfo->outputEncodingEOTF; + if (!frameInfo->applyOutputColorMgmt) +@@ -3928,7 +3961,15 @@ std::optional vulkan_composite( struct FrameInfo_t *frameInfo, gamesco + cmdBuffer->setTextureSrgb(0, true); + cmdBuffer->setSamplerUnnormalized(0, false); + cmdBuffer->setSamplerNearest(0, false); +- cmdBuffer->bindTarget(compositeImage); ++ ++ if (applyRotation) { ++ // Make a rotatedOutput with normal dimensions ++ update_rotated_images(currentOutputWidth, currentOutputHeight); // 2560x1600 ++ cmdBuffer->bindTarget(g_output.rotatedOutput); ++ } else { ++ cmdBuffer->bindTarget(compositeImage); ++ } ++ + cmdBuffer->uploadConstants(frameInfo, g_upscaleFilterSharpness / 10.0f); + + cmdBuffer->dispatch(div_roundup(currentOutputWidth, pixelsPerGroup), div_roundup(currentOutputHeight, pixelsPerGroup)); +@@ -3971,7 +4012,15 @@ std::optional vulkan_composite( struct FrameInfo_t *frameInfo, gamesco + + cmdBuffer->bindPipeline( g_device.pipeline(SHADER_TYPE_BLIT, nisFrameInfo.layerCount, nisFrameInfo.ycbcrMask(), 0u, nisFrameInfo.colorspaceMask(), outputTF )); + bind_all_layers(cmdBuffer.get(), &nisFrameInfo); +- cmdBuffer->bindTarget(compositeImage); ++ ++ if (applyRotation) { ++ // Make a rotatedOutput with normal dimensions ++ update_rotated_images(currentOutputWidth, currentOutputHeight); // 2560x1600 ++ cmdBuffer->bindTarget(g_output.rotatedOutput); ++ } else { ++ cmdBuffer->bindTarget(compositeImage); ++ } ++ + cmdBuffer->uploadConstants(&nisFrameInfo); + + int pixelsPerGroup = 8; +@@ -4009,7 +4058,15 @@ std::optional vulkan_composite( struct FrameInfo_t *frameInfo, gamesco + type = frameInfo->blurLayer0 == BLUR_MODE_COND ? SHADER_TYPE_BLUR_COND : SHADER_TYPE_BLUR; + cmdBuffer->bindPipeline(g_device.pipeline(type, frameInfo->layerCount, frameInfo->ycbcrMask(), blur_layer_count, frameInfo->colorspaceMask(), outputTF )); + bind_all_layers(cmdBuffer.get(), frameInfo); +- cmdBuffer->bindTarget(compositeImage); ++ ++ if (applyRotation) { ++ // Make a rotatedOutput with normal dimensions ++ update_rotated_images(currentOutputWidth, currentOutputHeight); // 2560x1600 ++ cmdBuffer->bindTarget(g_output.rotatedOutput); ++ } else { ++ cmdBuffer->bindTarget(compositeImage); ++ } ++ + cmdBuffer->bindTexture(VKR_BLUR_EXTRA_SLOT, g_output.tmpOutput); + cmdBuffer->setTextureSrgb(VKR_BLUR_EXTRA_SLOT, !useSrgbView); // Inverted because it chooses whether to view as linear (sRGB view) or sRGB (raw view). It's horrible. I need to change it. + cmdBuffer->setSamplerUnnormalized(VKR_BLUR_EXTRA_SLOT, true); +@@ -4019,14 +4076,51 @@ std::optional vulkan_composite( struct FrameInfo_t *frameInfo, gamesco + } + else + { +- cmdBuffer->bindPipeline( g_device.pipeline(SHADER_TYPE_BLIT, frameInfo->layerCount, frameInfo->ycbcrMask(), 0u, frameInfo->colorspaceMask(), outputTF )); +- bind_all_layers(cmdBuffer.get(), frameInfo); +- cmdBuffer->bindTarget(compositeImage); +- cmdBuffer->uploadConstants(frameInfo); ++ if (applyRotation) { ++ cmdBuffer->bindPipeline( g_device.pipeline(SHADER_TYPE_ROTATION, frameInfo->layerCount, frameInfo->ycbcrMask(), 0u, frameInfo->colorspaceMask(), outputTF )); ++ bind_all_layers(cmdBuffer.get(), frameInfo); ++ cmdBuffer->bindTarget(compositeImage); ++ cmdBuffer->uploadConstants(frameInfo); + +- const int pixelsPerGroup = 8; ++ const int pixelsPerGroup = 8; + +- cmdBuffer->dispatch(div_roundup(currentOutputWidth, pixelsPerGroup), div_roundup(currentOutputHeight, pixelsPerGroup)); ++ cmdBuffer->dispatch(div_roundup(currentOutputWidth, pixelsPerGroup), div_roundup(currentOutputHeight, pixelsPerGroup)); ++ } else { ++ cmdBuffer->bindPipeline( g_device.pipeline(SHADER_TYPE_BLIT, frameInfo->layerCount, frameInfo->ycbcrMask(), 0u, frameInfo->colorspaceMask(), outputTF )); ++ bind_all_layers(cmdBuffer.get(), frameInfo); ++ cmdBuffer->bindTarget(compositeImage); ++ cmdBuffer->uploadConstants(frameInfo); ++ ++ const int pixelsPerGroup = 8; ++ ++ cmdBuffer->dispatch(div_roundup(currentOutputWidth, pixelsPerGroup), div_roundup(currentOutputHeight, pixelsPerGroup)); ++ } ++ } ++ ++ if (applyRotation) ++ { ++ if (g_output.rotatedOutput != nullptr) { ++ // Rotate the final output ++ // TODO: may need rework with another rotation shader for blur, fsr and nis ++ cmdBuffer->bindPipeline( g_device.pipeline(SHADER_TYPE_ROTATION, frameInfo->layerCount, frameInfo->ycbcrMask(), 0u, frameInfo->colorspaceMask(), outputTF)); ++ bind_all_layers(cmdBuffer.get(), frameInfo); ++ ++ // if (frameInfo->blurLayer0) { ++ // bool useSrgbView = frameInfo->layers[0].colorspace == GAMESCOPE_APP_TEXTURE_COLORSPACE_LINEAR; ++ // ++ // cmdBuffer->bindTexture(VKR_BLUR_EXTRA_SLOT, g_output.rotatedOutput); ++ // cmdBuffer->setTextureSrgb(VKR_BLUR_EXTRA_SLOT, !useSrgbView); ++ // cmdBuffer->setSamplerUnnormalized(VKR_BLUR_EXTRA_SLOT, true); ++ // cmdBuffer->setSamplerNearest(VKR_BLUR_EXTRA_SLOT, false); ++ // } ++ ++ cmdBuffer->bindTarget(compositeImage); ++ cmdBuffer->uploadConstants(frameInfo); ++ ++ const int pixelsPerGroup = 8; ++ ++ cmdBuffer->dispatch(div_roundup(currentOutputWidth, pixelsPerGroup), div_roundup(currentOutputHeight, pixelsPerGroup)); ++ } + } + + if ( pPipewireTexture != nullptr ) +diff --git a/src/rendervulkan.hpp b/src/rendervulkan.hpp +index ccabd88..51a62bc 100644 +--- a/src/rendervulkan.hpp ++++ b/src/rendervulkan.hpp +@@ -393,7 +393,7 @@ gamescope::OwningRc vulkan_create_texture_from_dmabuf( struct wl + gamescope::OwningRc vulkan_create_texture_from_bits( uint32_t width, uint32_t height, uint32_t contentWidth, uint32_t contentHeight, uint32_t drmFormat, CVulkanTexture::createFlags texCreateFlags, void *bits ); + gamescope::OwningRc vulkan_create_texture_from_wlr_buffer( struct wlr_buffer *buf, gamescope::OwningRc pBackendFb ); + +-std::optional vulkan_composite( struct FrameInfo_t *frameInfo, gamescope::Rc pScreenshotTexture, bool partial, gamescope::Rc pOutputOverride = nullptr, bool increment = true, std::unique_ptr pInCommandBuffer = nullptr ); ++std::optional vulkan_composite( struct FrameInfo_t *frameInfo, gamescope::Rc pScreenshotTexture, bool partial, gamescope::Rc pOutputOverride = nullptr, bool increment = true, std::unique_ptr pInCommandBuffer = nullptr, bool applyRotation = false ); + void vulkan_wait( uint64_t ulSeqNo, bool bReset ); + gamescope::Rc vulkan_get_last_output_image( bool partial, bool defer ); + gamescope::Rc vulkan_acquire_screenshot_texture(uint32_t width, uint32_t height, bool exportable, uint32_t drmFormat, EStreamColorspace colorspace = k_EStreamColorspace_Unknown); +@@ -522,6 +522,9 @@ struct VulkanOutput_t + // NIS + gamescope::OwningRc nisScalerImage; + gamescope::OwningRc nisUsmImage; ++ ++ // Rotated ++ gamescope::OwningRc rotatedOutput; + }; + + +@@ -534,6 +537,7 @@ enum ShaderType { + SHADER_TYPE_RCAS, + SHADER_TYPE_NIS, + SHADER_TYPE_RGB_TO_NV12, ++ SHADER_TYPE_ROTATION, + + SHADER_TYPE_COUNT + }; +diff --git a/src/shaders/cs_rotation.comp b/src/shaders/cs_rotation.comp +new file mode 100644 +index 0000000..1a47fd5 +--- /dev/null ++++ b/src/shaders/cs_rotation.comp +@@ -0,0 +1,53 @@ ++#version 450 ++ ++#extension GL_GOOGLE_include_directive : require ++#extension GL_EXT_scalar_block_layout : require ++ ++#include "descriptor_set.h" ++ ++layout( ++ local_size_x = 8, ++ local_size_y = 8, ++ local_size_z = 1) in; ++ ++#include "blit_push_data.h" ++#include "composite.h" ++ ++vec4 sampleLayer(uint layerIdx, vec2 uv) { ++ if ((c_ycbcrMask & (1 << layerIdx)) != 0) ++ return sampleLayer(s_ycbcr_samplers[layerIdx], layerIdx, uv, false); ++ return sampleLayer(s_samplers[layerIdx], layerIdx, uv, true); ++} ++ ++void main() { ++ uvec2 coord = uvec2(gl_GlobalInvocationID.x, gl_GlobalInvocationID.y); ++ uvec2 outSize = imageSize(dst); ++ float outWidth = outSize.y; ++ float outHeight = outSize.x; ++ ++ vec2 uv = vec2(coord); ++ vec4 outputValue = vec4(255.0f); ++ ++ if (c_layerCount > 0) { ++ outputValue = sampleLayer(0, uv) * u_opacity[0]; ++ } ++ ++ for (int i = 1; i < c_layerCount; i++) { ++ vec4 layerColor = sampleLayer(i, uv); ++ // wl_surfaces come with premultiplied alpha, so that's them being ++ // premultiplied by layerColor.a. ++ // We need to then multiply that by the layer's opacity to get to our ++ // final premultiplied state. ++ // For the other side of things, we need to multiply by (1.0f - (layerColor.a * opacity)) ++ float opacity = u_opacity[i]; ++ float layerAlpha = opacity * layerColor.a; ++ outputValue = layerColor * opacity + outputValue * (1.0f - layerAlpha); ++ } ++ ++ outputValue.rgb = encodeOutputColor(outputValue.rgb); ++ ++ // Rotate the pixel coordinates counter-clockwise by 90 degrees ++ ivec2 rotatedCoord = ivec2(coord.y, outWidth - coord.x - 1); ++ ++ imageStore(dst, rotatedCoord, outputValue); ++} +diff --git a/src/wlserver.cpp b/src/wlserver.cpp +index 1eeaa25..5aa986a 100644 +--- a/src/wlserver.cpp ++++ b/src/wlserver.cpp +@@ -2519,6 +2519,11 @@ static void apply_touchscreen_orientation(double *x, double *y ) + break; + } + ++ if (g_bUseRotationShader) { ++ tx = 1.0 - *y; ++ ty = *x; ++ } ++ + *x = tx; + *y = ty; + } +-- +2.47.1 + + +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: "Ruan E. Formigoni" +Date: Thu, 2 Jan 2025 17:07:36 +0100 +Subject: feat: implement bicubic downscaling + +From https://github.com/ValveSoftware/gamescope/pull/740 +--- + README.md | 1 + + src/Backends/DRMBackend.cpp | 1 + + src/Backends/OpenVRBackend.cpp | 1 + + src/Backends/SDLBackend.cpp | 4 + + src/Backends/WaylandBackend.cpp | 1 + + src/main.cpp | 54 +++++++++- + src/main.hpp | 15 +++ + src/meson.build | 1 + + src/rendervulkan.cpp | 61 ++++++++++- + src/rendervulkan.hpp | 2 + + src/shaders/bicubic.h | 44 ++++++++ + src/shaders/cs_bicubic.comp | 177 ++++++++++++++++++++++++++++++++ + src/shaders/descriptor_set.h | 1 + + src/steamcompmgr.cpp | 23 +++++ + src/steamcompmgr.hpp | 1 + + src/xwayland_ctx.hpp | 1 + + 16 files changed, 386 insertions(+), 2 deletions(-) + create mode 100644 src/shaders/bicubic.h + create mode 100644 src/shaders/cs_bicubic.comp + +diff --git a/README.md b/README.md +index 97dea45..fefb2a0 100644 +--- a/README.md ++++ b/README.md +@@ -66,6 +66,7 @@ See `gamescope --help` for a full list of options. + * `-o`: set a frame-rate limit for the game when unfocused. Specified in frames per second. Defaults to unlimited. + * `-F fsr`: use AMD FidelityFX™ Super Resolution 1.0 for upscaling + * `-F nis`: use NVIDIA Image Scaling v1.0.3 for upscaling ++* `-F bicubic`: use a bicubic filter for downscaling + * `-S integer`: use integer scaling. + * `-S stretch`: use stretch scaling, the game will fill the window. (e.g. 4:3 to 16:9) + * `-b`: create a border-less window. +diff --git a/src/Backends/DRMBackend.cpp b/src/Backends/DRMBackend.cpp +index 506963d..98bdb71 100644 +--- a/src/Backends/DRMBackend.cpp ++++ b/src/Backends/DRMBackend.cpp +@@ -3293,6 +3293,7 @@ namespace gamescope + bNeedsFullComposite |= bWasFirstFrame; + bNeedsFullComposite |= pFrameInfo->useFSRLayer0; + bNeedsFullComposite |= pFrameInfo->useNISLayer0; ++ bNeedsFullComposite |= pFrameInfo->useBICUBICLayer0; + bNeedsFullComposite |= pFrameInfo->blurLayer0; + bNeedsFullComposite |= bNeedsCompositeFromFilter; + bNeedsFullComposite |= !k_bUseCursorPlane && bDrewCursor; +diff --git a/src/Backends/OpenVRBackend.cpp b/src/Backends/OpenVRBackend.cpp +index c39caa5..96a3d01 100644 +--- a/src/Backends/OpenVRBackend.cpp ++++ b/src/Backends/OpenVRBackend.cpp +@@ -554,6 +554,7 @@ namespace gamescope + bNeedsFullComposite |= cv_composite_force; + bNeedsFullComposite |= pFrameInfo->useFSRLayer0; + bNeedsFullComposite |= pFrameInfo->useNISLayer0; ++ bNeedsFullComposite |= pFrameInfo->useBICUBICLayer0; + bNeedsFullComposite |= pFrameInfo->blurLayer0; + bNeedsFullComposite |= bNeedsCompositeFromFilter; + bNeedsFullComposite |= g_bColorSliderInUse; +diff --git a/src/Backends/SDLBackend.cpp b/src/Backends/SDLBackend.cpp +index 6d50f8d..c24b864 100644 +--- a/src/Backends/SDLBackend.cpp ++++ b/src/Backends/SDLBackend.cpp +@@ -719,6 +719,10 @@ namespace gamescope + case KEY_B: + g_wantedUpscaleFilter = GamescopeUpscaleFilter::LINEAR; + break; ++ case KEY_K: ++ g_wantedDownscaleFilter = (g_wantedDownscaleFilter == GamescopeDownscaleFilter::BICUBIC) ? ++ GamescopeDownscaleFilter::LINEAR : GamescopeDownscaleFilter::BICUBIC; ++ break; + case KEY_U: + g_wantedUpscaleFilter = (g_wantedUpscaleFilter == GamescopeUpscaleFilter::FSR) ? + GamescopeUpscaleFilter::LINEAR : GamescopeUpscaleFilter::FSR; +diff --git a/src/Backends/WaylandBackend.cpp b/src/Backends/WaylandBackend.cpp +index 3226400..7ae273f 100644 +--- a/src/Backends/WaylandBackend.cpp ++++ b/src/Backends/WaylandBackend.cpp +@@ -1614,6 +1614,7 @@ namespace gamescope + bNeedsFullComposite |= cv_composite_force; + bNeedsFullComposite |= pFrameInfo->useFSRLayer0; + bNeedsFullComposite |= pFrameInfo->useNISLayer0; ++ bNeedsFullComposite |= pFrameInfo->useBICUBICLayer0; + bNeedsFullComposite |= pFrameInfo->blurLayer0; + bNeedsFullComposite |= bNeedsCompositeFromFilter; + bNeedsFullComposite |= g_bColorSliderInUse; +diff --git a/src/main.cpp b/src/main.cpp +index f61c88f..06a3bca 100644 +--- a/src/main.cpp ++++ b/src/main.cpp +@@ -7,6 +7,7 @@ + #include + #include + #include ++#include + #include + #if defined(__linux__) + #include +@@ -303,11 +304,14 @@ bool g_bGrabbed = false; + float g_mouseSensitivity = 1.0; + + GamescopeUpscaleFilter g_upscaleFilter = GamescopeUpscaleFilter::LINEAR; ++GamescopeDownscaleFilter g_downscaleFilter = GamescopeDownscaleFilter::LINEAR; + GamescopeUpscaleScaler g_upscaleScaler = GamescopeUpscaleScaler::AUTO; + + GamescopeUpscaleFilter g_wantedUpscaleFilter = GamescopeUpscaleFilter::LINEAR; ++GamescopeDownscaleFilter g_wantedDownscaleFilter = GamescopeDownscaleFilter::LINEAR; + GamescopeUpscaleScaler g_wantedUpscaleScaler = GamescopeUpscaleScaler::AUTO; + int g_upscaleFilterSharpness = 2; ++GamescopeBicubicParams g_bicubicParams; + + gamescope::GamescopeModeGeneration g_eGamescopeModeGeneration = gamescope::GAMESCOPE_MODE_GENERATE_CVT; + +@@ -427,6 +431,54 @@ static enum GamescopeUpscaleFilter parse_upscaler_filter(const char *str) + } + } + ++static enum GamescopeDownscaleFilter parse_downscaler_filter(const char *str) ++{ ++ std::string_view arg{str}; ++ ++ // If the string is just 'bicubic' use default values ++ if ( arg == "bicubic" ) { ++ return GamescopeDownscaleFilter::BICUBIC; ++ } ++ ++ // Arguments start after ':' ++ if ( auto search = arg.find(':'); search == std::string::npos ) { ++ fprintf( stderr, "gamescope: invalid argument for --filter=bicubic:float,float\n" ); ++ exit(1); ++ } else { ++ arg = std::string_view(arg.data() + search + 1); ++ } ++ ++ // Push arguments to stream ++ std::stringstream ss; ++ ss << arg; ++ ++ // Validate arguments from stream ++ double b, c; ++ char comma; ++ if ((ss >> b >> comma >> c) && (comma == ',')) { ++ // clamp values ++ b = std::clamp(b, 0.0, 1.0); ++ c = std::clamp(c, 0.0, 1.0); ++ // Ovewrite default global parameters ++ g_bicubicParams.b = b; ++ g_bicubicParams.c = c; ++ // Set downscaler filters ++ return GamescopeDownscaleFilter::BICUBIC; ++ } ++ ++ fprintf( stderr, "gamescope: invalid value for --filter\n" ); ++ exit(1); ++} ++ ++static void parse_filter(const char *str) ++{ ++ if (std::string_view{str}.starts_with("bicubic")) { ++ g_wantedDownscaleFilter = parse_downscaler_filter(str); ++ } else { ++ g_wantedUpscaleFilter = parse_upscaler_filter(str); ++ } ++} ++ + static enum gamescope::GamescopeBackend parse_backend_name(const char *str) + { + if (strcmp(str, "auto") == 0) { +@@ -756,7 +808,7 @@ int main(int argc, char **argv) + g_wantedUpscaleScaler = parse_upscaler_scaler(optarg); + break; + case 'F': +- g_wantedUpscaleFilter = parse_upscaler_filter(optarg); ++ parse_filter(optarg); + break; + case 'b': + g_bBorderlessOutputWindow = true; +diff --git a/src/main.hpp b/src/main.hpp +index 2464afa..040d04c 100644 +--- a/src/main.hpp ++++ b/src/main.hpp +@@ -43,6 +43,18 @@ enum class GamescopeUpscaleFilter : uint32_t + FROM_VIEW = 0xF, // internal + }; + ++enum class GamescopeDownscaleFilter : uint32_t ++{ ++ LINEAR = 0, ++ BICUBIC, ++}; ++ ++struct GamescopeBicubicParams ++{ ++ float b = 0.3f; ++ float c = 0.3f; ++}; ++ + static constexpr bool DoesHardwareSupportUpscaleFilter( GamescopeUpscaleFilter eFilter ) + { + // Could do nearest someday... AMDGPU DC supports custom tap placement to an extent. +@@ -60,10 +72,13 @@ enum class GamescopeUpscaleScaler : uint32_t + }; + + extern GamescopeUpscaleFilter g_upscaleFilter; ++extern GamescopeDownscaleFilter g_downscaleFilter; + extern GamescopeUpscaleScaler g_upscaleScaler; + extern GamescopeUpscaleFilter g_wantedUpscaleFilter; ++extern GamescopeDownscaleFilter g_wantedDownscaleFilter; + extern GamescopeUpscaleScaler g_wantedUpscaleScaler; + extern int g_upscaleFilterSharpness; ++extern GamescopeBicubicParams g_bicubicParams; + + extern bool g_bBorderlessOutputWindow; + +diff --git a/src/meson.build b/src/meson.build +index d4ff3ea..341bace 100644 +--- a/src/meson.build ++++ b/src/meson.build +@@ -66,6 +66,7 @@ shader_src = [ + 'shaders/cs_composite_rcas.comp', + 'shaders/cs_easu.comp', + 'shaders/cs_easu_fp16.comp', ++ 'shaders/cs_bicubic.comp', + 'shaders/cs_gaussian_blur_horizontal.comp', + 'shaders/cs_nis.comp', + 'shaders/cs_nis_fp16.comp', +diff --git a/src/rendervulkan.cpp b/src/rendervulkan.cpp +index 10d6c78..8b31c1e 100644 +--- a/src/rendervulkan.cpp ++++ b/src/rendervulkan.cpp +@@ -44,6 +44,7 @@ + #include "cs_composite_rcas.h" + #include "cs_easu.h" + #include "cs_easu_fp16.h" ++#include "cs_bicubic.h" + #include "cs_gaussian_blur_horizontal.h" + #include "cs_nis.h" + #include "cs_nis_fp16.h" +@@ -53,6 +54,7 @@ + #define A_CPU + #include "shaders/ffx_a.h" + #include "shaders/ffx_fsr1.h" ++#include "shaders/bicubic.h" + + #include "reshade_effect_manager.hpp" + +@@ -888,6 +890,7 @@ bool CVulkanDevice::createShaders() + SHADER(BLUR_COND, cs_composite_blur_cond); + SHADER(BLUR_FIRST_PASS, cs_gaussian_blur_horizontal); + SHADER(RCAS, cs_composite_rcas); ++ SHADER(BICUBIC, cs_bicubic); + if (m_bSupportsFp16) + { + SHADER(EASU, cs_easu_fp16); +@@ -1128,6 +1131,7 @@ void CVulkanDevice::compileAllPipelines() + SHADER(BLUR_FIRST_PASS, 1, 2, 1); + SHADER(RCAS, k_nMaxLayers, k_nMaxYcbcrMask_ToPreCompile, 1); + SHADER(EASU, 1, 1, 1); ++ SHADER(BICUBIC, 1, 1, 1); + SHADER(NIS, 1, 1, 1); + SHADER(RGB_TO_NV12, 1, 1, 1); + SHADER(ROTATION, k_nMaxLayers, k_nMaxYcbcrMask_ToPreCompile, k_nMaxBlurLayers); +@@ -3724,6 +3728,17 @@ struct EasuPushData_t + } + }; + ++struct BicubicPushData_t ++{ ++ uvec4_t Const0; ++ uvec4_t Const1; ++ ++ BicubicPushData_t(float B, float C, uint32_t inputX, uint32_t inputY, uint32_t tempX, uint32_t tempY, uint32_t winX, uint32_t winY) ++ { ++ BicubicCon(&Const0.x, &Const1.x, B*10, C*10, inputX, inputY, tempX, tempY, winX, winY); ++ } ++}; ++ + struct RcasPushData_t + { + uvec2_t u_layer0Offset; +@@ -3933,7 +3948,51 @@ std::optional vulkan_composite( struct FrameInfo_t *frameInfo, gamesco + for (uint32_t i = 0; i < EOTF_Count; i++) + cmdBuffer->bindColorMgmtLuts(i, frameInfo->shaperLut[i], frameInfo->lut3D[i]); + +- if ( frameInfo->useFSRLayer0 ) ++ if ( frameInfo->useBICUBICLayer0 ) ++ { ++ uint32_t inputX = frameInfo->layers[0].tex->width(); ++ uint32_t inputY = frameInfo->layers[0].tex->height(); ++ ++ uint32_t tempX = frameInfo->layers[0].integerWidth(); ++ uint32_t tempY = frameInfo->layers[0].integerHeight(); ++ ++ update_tmp_images(tempX, tempY); ++ ++ cmdBuffer->bindPipeline( g_device.pipeline(SHADER_TYPE_BICUBIC, frameInfo->layerCount, frameInfo->ycbcrMask())); ++ cmdBuffer->bindTarget(g_output.tmpOutput); ++ cmdBuffer->bindTexture(0, frameInfo->layers[0].tex); ++ cmdBuffer->setTextureSrgb(0, true); ++ cmdBuffer->setSamplerUnnormalized(0, false); ++ cmdBuffer->setSamplerNearest(0, false); ++ cmdBuffer->uploadConstants(g_bicubicParams.b ++ , g_bicubicParams.c ++ , inputX ++ , inputY ++ , tempX ++ , tempY ++ , currentOutputWidth ++ , currentOutputHeight ++ ); ++ ++ int pixelsPerGroup = 16; ++ ++ cmdBuffer->dispatch(div_roundup(tempX, pixelsPerGroup), div_roundup(tempY, pixelsPerGroup)); ++ ++ struct FrameInfo_t bicFrameInfo = *frameInfo; ++ bicFrameInfo.layers[0].tex = g_output.tmpOutput; ++ bicFrameInfo.layers[0].scale.x = 1.0f; ++ bicFrameInfo.layers[0].scale.y = 1.0f; ++ ++ cmdBuffer->bindPipeline( g_device.pipeline(SHADER_TYPE_BLIT, bicFrameInfo.layerCount, bicFrameInfo.ycbcrMask())); ++ bind_all_layers(cmdBuffer.get(), &bicFrameInfo); ++ cmdBuffer->bindTarget(compositeImage); ++ cmdBuffer->uploadConstants(&bicFrameInfo); ++ ++ pixelsPerGroup = 8; ++ ++ cmdBuffer->dispatch(div_roundup(currentOutputWidth, pixelsPerGroup), div_roundup(currentOutputHeight, pixelsPerGroup)); ++ } ++ else if ( frameInfo->useFSRLayer0 ) + { + uint32_t inputX = frameInfo->layers[0].tex->width(); + uint32_t inputY = frameInfo->layers[0].tex->height(); +diff --git a/src/rendervulkan.hpp b/src/rendervulkan.hpp +index 51a62bc..eeb304c 100644 +--- a/src/rendervulkan.hpp ++++ b/src/rendervulkan.hpp +@@ -270,6 +270,7 @@ struct FrameInfo_t + { + bool useFSRLayer0; + bool useNISLayer0; ++ bool useBICUBICLayer0; + bool bFadingOut; + BlurMode blurLayer0; + int blurRadius; +@@ -536,6 +537,7 @@ enum ShaderType { + SHADER_TYPE_EASU, + SHADER_TYPE_RCAS, + SHADER_TYPE_NIS, ++ SHADER_TYPE_BICUBIC, + SHADER_TYPE_RGB_TO_NV12, + SHADER_TYPE_ROTATION, + +diff --git a/src/shaders/bicubic.h b/src/shaders/bicubic.h +new file mode 100644 +index 0000000..8117e87 +--- /dev/null ++++ b/src/shaders/bicubic.h +@@ -0,0 +1,44 @@ ++//_____________________________________________________________/\_______________________________________________________________ ++//============================================================================================================================== ++// ++// ++// BICUBIC IMAGE SCALING ++// ++// ++//------------------------------------------------------------------------------------------------------------------------------ ++//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ++//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ++//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ++//_____________________________________________________________/\_______________________________________________________________ ++//============================================================================================================================== ++// CONSTANT SETUP ++//============================================================================================================================== ++// Call to setup required constant values (works on CPU or GPU). ++A_STATIC void BicubicCon( ++outAU4 con0, ++outAU4 con1, ++// Configurable parameters ++AU1 B, ++AU1 C, ++// This the rendered image resolution ++AF1 inputRenderedSizeX, ++AF1 inputRenderedSizeY, ++// This is the resolution of the resource containing the input image (useful for dynamic resolution) ++AF1 inputCurrentSizeX, ++AF1 inputCurrentSizeY, ++// This is the window width / height ++AF1 outputTargetSizeX, ++AF1 outputTargetSizeY) ++{ ++ // Input/Output size information ++ con0[0]=AU1_AF1(inputRenderedSizeX); ++ con0[1]=AU1_AF1(inputRenderedSizeY); ++ con0[2]=AU1_AF1(inputCurrentSizeX); ++ con0[3]=AU1_AF1(inputCurrentSizeY); ++ ++ // Viewport pixel position to normalized image space. ++ con1[0]=AU1_AF1(outputTargetSizeX); ++ con1[1]=AU1_AF1(outputTargetSizeY); ++ con1[2]=B; ++ con1[3]=C; ++} +diff --git a/src/shaders/cs_bicubic.comp b/src/shaders/cs_bicubic.comp +new file mode 100644 +index 0000000..2b6dfb8 +--- /dev/null ++++ b/src/shaders/cs_bicubic.comp +@@ -0,0 +1,177 @@ ++// References ++// https://www.codeproject.com/Articles/236394/Bi-Cubic-and-Bi-Linear-Interpolation-with-GLSL ++// https://stackoverflow.com/questions/13501081/efficient-bicubic-filtering-code-in-glsl ++// https://web.archive.org/web/20180927181721/http://www.java-gaming.org/index.php?topic=35123.0 ++// https://gist.github.com/TheRealMJP/c83b8c0f46b63f3a88a5986f4fa982b1 ++ ++#version 460 ++ ++#extension GL_GOOGLE_include_directive : require ++#extension GL_EXT_shader_explicit_arithmetic_types_float16 : require ++#extension GL_EXT_scalar_block_layout : require ++ ++#include "descriptor_set.h" ++ ++layout( ++ local_size_x = 64, ++ local_size_y = 1, ++ local_size_z = 1) in; ++ ++// Push constant is a mechanism in modern OpenGL that allows passing small amounts of frequently ++// updated data to the shader without needing to bind a buffer ++layout(binding = 0, scalar) ++uniform layers_t { ++ uvec4 c0, c1; ++}; ++ ++#define A_GPU 1 ++#define A_GLSL 1 ++#define A_HALF 1 ++#include "ffx_a.h" ++#include "bicubic.h" ++ ++// The Mitchell–Netravali filters or BC-splines ++// https://en.wikipedia.org/wiki/Mitchell%E2%80%93Netravali_filters ++// Conditionals are slow in GPU code, so to represent 0 <= f < 1 and 1 <= f < 2 ++// the P(d) form shown in the wikipedia page is used ++vec4 mitchellNetravaliWeights(float f, float B, float C) ++{ ++ float w0 = ((12.0 - 9.0 * B - 6.0 * C) * pow(f, 3.0)) + ++ ((-18.0 + 12.0 * B + 6.0 * C) * pow(f, 2.0)) + ++ (6.0 - 2.0 * B); ++ ++ float w1 = ((-B - 6.0 * C) * pow(f - 1.0, 3.0)) + ++ ((6.0 * B + 30.0 * C) * pow(f - 1.0, 2.0)) + ++ ((-12.0 * B - 48.0 * C) * (f - 1.0)) + ++ (8.0 * B + 24.0 * C); ++ ++ float w2 = ((12.0 - 9.0 * B - 6.0 * C) * pow(1.0 - f, 3.0)) + ++ ((-18.0 + 12.0 * B + 6.0 * C) * pow(1.0 - f, 2.0)) + ++ (6.0 - 2.0 * B); ++ ++ float w3 = ((-B - 6.0 * C) * pow(2.0 - f, 3.0)) + ++ ((6.0 * B + 30.0 * C) * pow(2.0 - f, 2.0)) + ++ ((-12.0 * B - 48.0 * C) * (2.0 - f)) + ++ (8.0 * B + 24.0 * C); ++ ++ return vec4(w0, w1, w2, w3); ++} ++ ++// https://stackoverflow.com/questions/13501081/efficient-bicubic-filtering-code-in-glsl ++// https://web.archive.org/web/20180927181721/http://www.java-gaming.org/index.php?topic=35123.0 ++// This is an efficient method to implement bicubic filtering, it takes ++// advantage of the fact that the bilinear approach gives the weighted average ++// of a 2x2 area. ++vec4 textureBicubic(sampler2D splr, vec2 texCoords) ++{ ++ vec2 texSize = textureSize(splr, 0); ++ vec2 invTexSize = 1.0 / texSize; ++ ++ // Converts normalized coordinates into pixel-space coordinate ++ // Example: If texCoords is (0.5, 0.5), and the texture size is (1920, 1080), the result will be ++ // (960, 540)—the center of the texture in pixel space. ++ // Subtracting 0.5 ensures that you're sampling from the center of the texel rather than its corner ++ // Example: Assume we have a 3x3 texture and texCoords = (0.5, 0.5): ++ // [0,0][1,0][2,0] ++ // [0,1][1,1][2,1] ++ // [0,2][1,2][2,2] ++ // texCoords * texSize - 0.5 maps to (1.5, 1.5), which is between (1,1) and (2,2), then ++ // subtracts 0.5 to move it to (1.0, 1.0)—the center of the texel ++ texCoords = texCoords * texSize - 0.5; ++ ++ // Get B and C that were pushed from the user input (or default values) ++ float B = c1[2] / 10.0f; ++ float C = c1[3] / 10.0f; ++ ++ // Get the fractional part of the coordinates ++ // They are used in Mitchell Netravali's strategy to calculate the interpolation weights, ++ // i.e., how much influence the neighboring vertices have on the final pixel value ++ vec2 fxy = fract(texCoords); ++ texCoords -= fxy; ++ ++ // Calculate bicubic weights ++ // These weights represent how much influence each neighboring texel in the 4x4 grid will have ++ // on the final interpolated pixel value ++ vec4 xweights = mitchellNetravaliWeights(fxy.x, B, C); ++ vec4 yweights = mitchellNetravaliWeights(fxy.y, B, C); ++ ++ // Modify the current texture coordinates to have an offset in texels for each coordinate ++ // E.g. texCoords + vec(-1.0, 0.0) is a texel to the left ++ // texCoords + vec(1.0, 0.0) is a texel to the right ++ // texCoords + vec(0.0, 1.0) is a texel downwards ++ // texCoords + vec(0.0, -1.0) is a texel upwards ++ vec4 offsetTexels = texCoords.xxyy; ++ offsetTexels += vec2 (-1.0, +1.0).xyxy; ++ // Normalize weights to range between (0,1) ++ // vec4 sumWeights = vec4(xweights.xz + xweights.yw, yweights.xz + yweights.yw); ++ // vec4 normalizedWeights = vec4 (xweights.yw, yweights.yw) / sumWeights; ++ vec4 sumWeights = vec4(xweights.x + xweights.y, xweights.z + xweights.w, yweights.x + yweights.y, yweights.z + yweights.w); ++ vec4 normalizedWeights = vec4 (xweights.y, xweights.w, yweights.y, yweights.w) / sumWeights; ++ // Use the weights to influence the sampling position inside each texel ++ // Each texel has a size from (0,1) ++ vec4 offsetSampler = offsetTexels + normalizedWeights; ++ // Go back to normalized space ++ offsetSampler *= invTexSize.xxyy; ++ // Perform the sampling ++ vec4 sample0 = texture(splr, offsetSampler.xz); ++ vec4 sample1 = texture(splr, offsetSampler.yz); ++ vec4 sample2 = texture(splr, offsetSampler.xw); ++ vec4 sample3 = texture(splr, offsetSampler.yw); ++ ++ // Now we perform linear interpolation in the selected points ++ // The mix(a, b, t) function in GLSL performs linear interpolation between a and b based on the ++ // parameter t, t is between 0 and 1 ++ // https://registry.khronos.org/OpenGL-Refpages/gl4/html/mix.xhtml ++ ++ // Here we want to normalize sx and sy to between 0 and 1 (t value) ++ float sx = sumWeights.x / (sumWeights.x + sumWeights.y); ++ float sy = sumWeights.z / (sumWeights.z + sumWeights.w); ++ ++ return mix( ++ mix(sample3, sample2, sx), mix(sample1, sample0, sx) ++ , sy); ++} ++ ++void bicPass(uvec2 pos) ++{ ++ // Retrieve pushed values ++ AF2 inputRenderedSize = AF2_AU2(c0.xy); ++ AF2 inputCurrentSize = AF2_AU2(c0.zw); ++ AF2 outputTargetSize = AF2_AU2(c1.xy); ++ ++ // ARcpF1(x) == 1.0 / x ++ // scaleFactor is the division between the rendered image and the size it should have at the end ++ // E.g.: Rendered 1920x1080, window size is 960x540, then scaleFactor is 2x2 ++ AF2 scaleFactor = inputRenderedSize * vec2(ARcpF1(inputCurrentSize.x), ARcpF1(inputCurrentSize.y)); ++ ++ // The parameter pos of this function is used to iterate over the output image (e.g. 960x540) ++ // The position of the processed pixel should be taken from the rendered image (e.g. 1920x1080) ++ // 10x10 in the output, corresponds to 20x20 in the original image ++ AF2 positionPixel=AF2(pos)*scaleFactor; ++ ++ // Normalize the image space to be between [0,1] ++ positionPixel=positionPixel*vec2(ARcpF1(inputRenderedSize.x),ARcpF1(inputRenderedSize.y)); ++ ++ // Apply the bicubic algorithm in the normalized pixel position ++ vec4 bicPass = textureBicubic(s_samplers[0], positionPixel); ++ ++ imageStore(dst, ivec2(pos), bicPass); ++} ++ ++ ++void main() ++{ ++ // AMD recommends to use this swizzle and to process 4 pixel per invocation ++ // for better cache utilisation ++ uvec2 pos = ARmp8x8(gl_LocalInvocationID.x) + uvec2(gl_WorkGroupID.x << 4u, gl_WorkGroupID.y << 4u); ++ ++ bicPass(pos); ++ pos.x += 8u; ++ bicPass(pos); ++ pos.y += 8u; ++ bicPass(pos); ++ pos.x -= 8u; ++ bicPass(pos); ++} ++ ++/* vim: set expandtab ft=cpp fdm=marker ts=4 sw=4 tw=100 et :*/ +diff --git a/src/shaders/descriptor_set.h b/src/shaders/descriptor_set.h +index f2b8527..64cc1c9 100644 +--- a/src/shaders/descriptor_set.h ++++ b/src/shaders/descriptor_set.h +@@ -21,6 +21,7 @@ const int filter_nearest = 1; + const int filter_fsr = 2; + const int filter_nis = 3; + const int filter_pixel = 4; ++const int filter_bicubic = 5; + const int filter_from_view = 255; + + const int EOTF_Gamma22 = 0; +diff --git a/src/steamcompmgr.cpp b/src/steamcompmgr.cpp +index dfee904..c2111d2 100644 +--- a/src/steamcompmgr.cpp ++++ b/src/steamcompmgr.cpp +@@ -906,6 +906,7 @@ gamescope::ConCommand cc_debug_set_fps_limit( "debug_set_fps_limit", "Set refres + static int g_nRuntimeInfoFd = -1; + + bool g_bFSRActive = false; ++bool g_bBicubicActive = false; + + BlurMode g_BlurMode = BLUR_MODE_OFF; + BlurMode g_BlurModeOld = BLUR_MODE_OFF; +@@ -2389,6 +2390,10 @@ paint_all(bool async, bool dpms) + paint_window(w, w, &frameInfo, global_focus.cursor, PaintWindowFlag::BasePlane | PaintWindowFlag::DrawBorders, 1.0f, override); + + bool needsScaling = frameInfo.layers[0].scale.x < 0.999f && frameInfo.layers[0].scale.y < 0.999f; ++ // Temporarily allow upscaling as well ++ // bool needsDownScaling = frameInfo.layers[0].scale.x > 1.001f && frameInfo.layers[0].scale.y > 1.001f; ++ bool needsDownScaling = true; ++ frameInfo.useBICUBICLayer0 = g_downscaleFilter == GamescopeDownscaleFilter::BICUBIC && needsDownScaling; + frameInfo.useFSRLayer0 = g_upscaleFilter == GamescopeUpscaleFilter::FSR && needsScaling; + frameInfo.useNISLayer0 = g_upscaleFilter == GamescopeUpscaleFilter::NIS && needsScaling; + } +@@ -2521,10 +2526,12 @@ paint_all(bool async, bool dpms) + } + + frameInfo.useFSRLayer0 = false; ++ frameInfo.useBICUBICLayer0 = false; + frameInfo.useNISLayer0 = false; + } + + g_bFSRActive = frameInfo.useFSRLayer0; ++ g_bBicubicActive = frameInfo.useBICUBICLayer0; + + g_bFirstFrame = false; + +@@ -5445,6 +5452,9 @@ handle_property_notify(xwayland_ctx_t *ctx, XPropertyEvent *ev) + g_wantedUpscaleScaler = GamescopeUpscaleScaler::AUTO; + g_wantedUpscaleFilter = GamescopeUpscaleFilter::NIS; + break; ++ case 5: ++ g_wantedDownscaleFilter = GamescopeDownscaleFilter::BICUBIC; ++ break; + } + hasRepaint = true; + } +@@ -7017,6 +7027,7 @@ void init_xwayland_ctx(uint32_t serverId, gamescope_xwayland_server_t *xwayland_ + ctx->atoms.gamescopeLowLatency = XInternAtom( ctx->dpy, "GAMESCOPE_LOW_LATENCY", false ); + + ctx->atoms.gamescopeFSRFeedback = XInternAtom( ctx->dpy, "GAMESCOPE_FSR_FEEDBACK", false ); ++ ctx->atoms.gamescopeBicubicFeedback = XInternAtom( ctx->dpy, "GAMESCOPE_BICUBIC_FEEDBACK", false ); + + ctx->atoms.gamescopeBlurMode = XInternAtom( ctx->dpy, "GAMESCOPE_BLUR_MODE", false ); + ctx->atoms.gamescopeBlurRadius = XInternAtom( ctx->dpy, "GAMESCOPE_BLUR_RADIUS", false ); +@@ -7275,6 +7286,7 @@ extern int g_nPreferredOutputWidth; + extern int g_nPreferredOutputHeight; + + static bool g_bWasFSRActive = false; ++static bool g_bWasBicubicActive = false; + + bool g_bAppWantsHDRCached = false; + +@@ -7689,6 +7701,16 @@ steamcompmgr_main(int argc, char **argv) + flush_root = true; + } + ++ if ( g_bBicubicActive != g_bWasBicubicActive ) ++ { ++ uint32_t active = g_bBicubicActive ? 1 : 0; ++ XChangeProperty( root_ctx->dpy, root_ctx->root, root_ctx->atoms.gamescopeBicubicFeedback, XA_CARDINAL, 32, PropModeReplace, ++ (unsigned char *)&active, 1 ); ++ ++ g_bWasBicubicActive = g_bBicubicActive; ++ flush_root = true; ++ } ++ + if (global_focus.IsDirty()) + determine_and_apply_focus(); + +@@ -7925,6 +7947,7 @@ steamcompmgr_main(int argc, char **argv) + g_bSteamIsActiveWindow = false; + g_upscaleScaler = g_wantedUpscaleScaler; + g_upscaleFilter = g_wantedUpscaleFilter; ++ g_downscaleFilter = g_wantedDownscaleFilter; + } + + // If we're in the middle of a fade, then keep us +diff --git a/src/steamcompmgr.hpp b/src/steamcompmgr.hpp +index 30e48e8..5679a0c 100644 +--- a/src/steamcompmgr.hpp ++++ b/src/steamcompmgr.hpp +@@ -129,6 +129,7 @@ extern float focusedWindowOffsetY; + + extern bool g_FakeExternal; + extern bool g_bFSRActive; ++extern bool g_bBicubicActive; + + extern uint32_t inputCounter; + extern uint64_t g_lastWinSeq; +diff --git a/src/xwayland_ctx.hpp b/src/xwayland_ctx.hpp +index 2347cbb..bc38c98 100644 +--- a/src/xwayland_ctx.hpp ++++ b/src/xwayland_ctx.hpp +@@ -164,6 +164,7 @@ struct xwayland_ctx_t final : public gamescope::IWaitable + Atom gamescopeLowLatency; + + Atom gamescopeFSRFeedback; ++ Atom gamescopeBicubicFeedback; + + Atom gamescopeBlurMode; + Atom gamescopeBlurRadius; +-- +2.47.1 + + +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: brainantifreeze +Date: Thu, 19 Dec 2024 09:16:15 +0000 +Subject: fix(nvidia): allow disabling Vulkan extension for nvidia to work + +This adds a workaround for #1592 which removes the +VkPhysicalDevicePresentWaitFeaturesKHR extension in +the layer if the environment variable +GAMESCOPE_WSI_HIDE_PRESENT_WAIT_EXT is set. + +This resolves the current freezing issue on nVidia +in dx12 (without having to set +VKD3D_DISABLE_EXTENSIONS), dx11 (without having +to patch DXVK not to use the extension) and in +native vulkan games. +--- + layer/VkLayer_FROG_gamescope_wsi.cpp | 20 +++++++++++++++++++- + 1 file changed, 19 insertions(+), 1 deletion(-) + +diff --git a/layer/VkLayer_FROG_gamescope_wsi.cpp b/layer/VkLayer_FROG_gamescope_wsi.cpp +index c817880..407dae8 100644 +--- a/layer/VkLayer_FROG_gamescope_wsi.cpp ++++ b/layer/VkLayer_FROG_gamescope_wsi.cpp +@@ -496,7 +496,11 @@ namespace GamescopeWSILayer { + createInfo.ppEnabledExtensionNames = enabledExts.data(); + + setenv("vk_xwayland_wait_ready", "false", 0); +- setenv("vk_khr_present_wait", "true", 0); ++ if (getHidePresentWait()) { ++ setenv("vk_khr_present_wait", "false", 0); ++ } else { ++ setenv("vk_khr_present_wait", "true", 0); ++ } + + VkResult result = pfnCreateInstanceProc(&createInfo, pAllocator, pInstance); + if (result != VK_SUCCESS) +@@ -801,6 +805,10 @@ namespace GamescopeWSILayer { + const vkroots::VkInstanceDispatch* pDispatch, + VkPhysicalDevice physicalDevice, + VkPhysicalDeviceFeatures2* pFeatures) { ++ if (getHidePresentWait()) { ++ fprintf(stderr, "[Gamescope WSI] Removing VkPhysicalDevicePresentWaitFeaturesKHR because GAMESCOPE_WSI_HIDE_PRESENT_WAIT_EXT is set\n"); ++ vkroots::RemoveFromChain(pFeatures); ++ } + pDispatch->GetPhysicalDeviceFeatures2(physicalDevice, pFeatures); + } + +@@ -1015,6 +1023,16 @@ namespace GamescopeWSILayer { + return value; + } + ++ static bool getHidePresentWait() { ++ static bool s_hidePresentWait = []() -> bool { ++ if (auto hide = parseEnv("GAMESCOPE_WSI_HIDE_PRESENT_WAIT_EXT")) { ++ return *hide == 1; ++ } ++ return false; ++ }(); ++ return s_hidePresentWait; ++ } ++ + static uint32_t getMinImageCount() { + static uint32_t s_minImageCount = []() -> uint32_t { + if (auto minCount = parseEnv("GAMESCOPE_WSI_MIN_IMAGE_COUNT")) { +-- +2.47.1 + diff --git a/anda/games/gamescope/revert-299bc34.patch b/anda/games/gamescope/revert-299bc34.patch deleted file mode 100644 index 550870fe82..0000000000 --- a/anda/games/gamescope/revert-299bc34.patch +++ /dev/null @@ -1,65 +0,0 @@ -diff --git a/src/steamcompmgr.cpp b/src/steamcompmgr.cpp -index d7498e5..d1800a8 100644 ---- a/src/steamcompmgr.cpp -+++ b/src/steamcompmgr.cpp -@@ -3271,7 +3271,7 @@ found:; - if ( window_has_commits( focus ) ) - out->focusWindow = focus; - else -- focus->outdatedInteractiveFocus = true; -+ out->outdatedInteractiveFocus = true; - - // Always update X's idea of focus, but still dirty - // the it being outdated so we can resolve that globally later. -@@ -5995,28 +5995,37 @@ bool handle_done_commit( steamcompmgr_win_t *w, xwayland_ctx_t *ctx, uint64_t co - // Window just got a new available commit, determine if that's worth a repaint - - // If this is an overlay that we're presenting, repaint -- if ( w == global_focus.overlayWindow && w->opacity != TRANSLUCENT ) -+ if ( gameFocused ) - { -- hasRepaintNonBasePlane = true; -- } -+ if ( w == global_focus.overlayWindow && w->opacity != TRANSLUCENT ) -+ { -+ hasRepaintNonBasePlane = true; -+ } - -- if ( w == global_focus.notificationWindow && w->opacity != TRANSLUCENT ) -- { -- hasRepaintNonBasePlane = true; -+ if ( w == global_focus.notificationWindow && w->opacity != TRANSLUCENT ) -+ { -+ hasRepaintNonBasePlane = true; -+ } - } -- -- // If this is an external overlay, repaint -- if ( w == global_focus.externalOverlayWindow && w->opacity != TRANSLUCENT ) -+ if ( ctx ) - { -- hasRepaintNonBasePlane = true; -+ if ( ctx->focus.outdatedInteractiveFocus ) -+ { -+ MakeFocusDirty(); -+ ctx->focus.outdatedInteractiveFocus = false; -+ } - } -- -- if ( w->outdatedInteractiveFocus ) -+ if ( global_focus.outdatedInteractiveFocus ) - { - MakeFocusDirty(); -- w->outdatedInteractiveFocus = false; -- } -+ global_focus.outdatedInteractiveFocus = false; - -+ // If this is an external overlay, repaint -+ if ( w == global_focus.externalOverlayWindow && w->opacity != TRANSLUCENT ) -+ { -+ hasRepaintNonBasePlane = true; -+ } -+ } - // If this is the main plane, repaint - if ( w == global_focus.focusWindow && !w->isSteamStreamingClient ) - { diff --git a/anda/games/gamescope/stb.pc b/anda/games/gamescope/stb.pc old mode 100644 new mode 100755 index 71d2f8a703..02c304a9fa --- a/anda/games/gamescope/stb.pc +++ b/anda/games/gamescope/stb.pc @@ -4,4 +4,4 @@ includedir=${prefix}/include/stb Name: stb Description: Single-file public domain libraries for C/C++ Version: 0.1.0 -Cflags: -I${includedir} \ No newline at end of file +Cflags: -I${includedir} diff --git a/anda/games/gamescope/update-patch.sh b/anda/games/gamescope/update-patch.sh deleted file mode 100755 index 9238e8c5ce..0000000000 --- a/anda/games/gamescope/update-patch.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -curl -o ./1483.patch https://patch-diff.githubusercontent.com/raw/ValveSoftware/gamescope/pull/1483.patch \ No newline at end of file diff --git a/anda/games/gamescope/update.rhai b/anda/games/gamescope/update.rhai deleted file mode 100644 index 74a18ee692..0000000000 --- a/anda/games/gamescope/update.rhai +++ /dev/null @@ -1 +0,0 @@ -rpm.global("gamescope_tag", gh_tag("ValveSoftware/gamescope")); diff --git a/anda/games/gamescope/v2-0001-always-send-ctrl-1-2-to-steam-s-wayland-session.patch b/anda/games/gamescope/v2-0001-always-send-ctrl-1-2-to-steam-s-wayland-session.patch deleted file mode 100644 index ad97a5fcc0..0000000000 --- a/anda/games/gamescope/v2-0001-always-send-ctrl-1-2-to-steam-s-wayland-session.patch +++ /dev/null @@ -1,39 +0,0 @@ -From 35e001dc59a44227d670c667a85a6ef5472eee58 Mon Sep 17 00:00:00 2001 -From: antheas -Date: Sat, 20 Jul 2024 01:23:19 +0300 -Subject: [PATCH v2] always send ctrl+1/2 to steam's wayland session - ---- - src/wlserver.cpp | 10 +++++++++- - 1 file changed, 9 insertions(+), 1 deletion(-) - -diff --git a/src/wlserver.cpp b/src/wlserver.cpp -index 1852be9..7de737d 100644 ---- a/src/wlserver.cpp -+++ b/src/wlserver.cpp -@@ -369,7 +369,12 @@ static void wlserver_handle_key(struct wl_listener *listener, void *data) - keysym == XKB_KEY_XF86AudioLowerVolume || - keysym == XKB_KEY_XF86AudioRaiseVolume || - keysym == XKB_KEY_XF86PowerOff; -- if ( ( event->state == WL_KEYBOARD_KEY_STATE_PRESSED || event->state == WL_KEYBOARD_KEY_STATE_RELEASED ) && forbidden_key ) -+ -+ // Check for steam keys (ctrl + 1/2) -+ bool is_steamshortcut = (keyboard->wlr->modifiers.depressed & WLR_MODIFIER_CTRL) && (keysym == XKB_KEY_1 || -+ keysym == XKB_KEY_2); -+ -+ if ( ( event->state == WL_KEYBOARD_KEY_STATE_PRESSED || event->state == WL_KEYBOARD_KEY_STATE_RELEASED ) && (forbidden_key || is_steamshortcut) ) - { - // Always send volume+/- to root server only, to avoid it reaching the game. - struct wlr_surface *old_kb_surf = wlserver.kb_focus_surface; -@@ -378,6 +383,9 @@ static void wlserver_handle_key(struct wl_listener *listener, void *data) - { - wlserver_keyboardfocus( new_kb_surf, false ); - wlr_seat_set_keyboard( wlserver.wlr.seat, keyboard->wlr ); -+ // Send modifiers to steam for it to work -+ if (is_steamshortcut) -+ wlr_seat_keyboard_notify_modifiers(wlserver.wlr.seat, &keyboard->wlr->modifiers); - wlr_seat_keyboard_notify_key( wlserver.wlr.seat, event->time_msec, event->keycode, event->state ); - wlserver_keyboardfocus( old_kb_surf, false ); - return; --- -2.45.2