mirror of
https://github.com/ublue-os/bazzite.git
synced 2025-02-14 15:40:37 +00:00
2440 lines
93 KiB
Diff
2440 lines
93 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||
From: Antheas Kapenekakis <git@antheas.dev>
|
||
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..676d652
|
||
--- /dev/null
|
||
+++ b/sync.sh
|
||
@@ -0,0 +1,21 @@
|
||
+if [ -z "$1" ]; then
|
||
+ echo "Usage: $0 <host>"
|
||
+ 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
|
||
+ 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 <ruinairas1992@gmail.com>
|
||
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 <me@kylegospodneti.ch>
|
||
Co-authored-by: Antheas Kapenekakis <git@antheas.dev>
|
||
---
|
||
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<uint32_t> g_customRefreshRates;
|
||
+// eg: 60,60,90,110-120
|
||
+static std::vector<uint32_t> parse_custom_refresh_rates( const char *str )
|
||
+{
|
||
+ std::vector<uint32_t> 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 <getopt.h>
|
||
|
||
#include <atomic>
|
||
+#include <vector>
|
||
|
||
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<uint32_t> g_customRefreshRates;
|
||
|
||
enum class GamescopeUpscaleFilter : uint32_t
|
||
{
|
||
--
|
||
2.47.1
|
||
|
||
|
||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||
From: Alesh Slovak <alesh@playtron.one>
|
||
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 18be94d..cd7e8ac 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.
|
||
@@ -6050,28 +6050,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 <git@antheas.dev>
|
||
---
|
||
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 <git@antheas.dev>
|
||
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 <me@kylegospodneti.ch>
|
||
---
|
||
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 cd7e8ac..3f3d499 100644
|
||
--- a/src/steamcompmgr.cpp
|
||
+++ b/src/steamcompmgr.cpp
|
||
@@ -197,6 +197,7 @@ update_runtime_info();
|
||
gamescope::ConVar<bool> cv_adaptive_sync( "adaptive_sync", false, "Whether or not adaptive sync is enabled if available." );
|
||
gamescope::ConVar<bool> 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<int> 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<bool> 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 );
|
||
}
|
||
@@ -7482,6 +7483,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 <git@antheas.dev>
|
||
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 3f3d499..5345b55 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 )
|
||
@@ -7485,6 +7486,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 <git@antheas.dev>
|
||
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 <honjow311@gmail.com>
|
||
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 <git@antheas.dev>
|
||
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 5345b55..6544cf0 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 )
|
||
@@ -7486,6 +7487,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 <git@antheas.dev>
|
||
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 6544cf0..d541f70 100644
|
||
--- a/src/steamcompmgr.cpp
|
||
+++ b/src/steamcompmgr.cpp
|
||
@@ -7198,7 +7198,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<ResListEntry_t> wlserver_xdg_commit_queue();
|
||
|
||
--
|
||
2.47.1
|
||
|
||
|
||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||
From: Antheas Kapenekakis <git@antheas.dev>
|
||
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<sol::table> otDynamicRefreshRates = tTable["dynamic_refresh_rates"];
|
||
sol::optional<sol::function> ofnDynamicModegen = tTable["dynamic_modegen"];
|
||
|
||
- if ( otDynamicRefreshRates && ofnDynamicModegen )
|
||
+ if ( otDynamicRefreshRates && !ofnDynamicModegen )
|
||
+ m_Mutable.ValidDynamicRefreshRates = TableToVector<uint32_t>( *otDynamicRefreshRates );
|
||
+ else if ( otDynamicRefreshRates && ofnDynamicModegen )
|
||
{
|
||
m_Mutable.ValidDynamicRefreshRates = TableToVector<uint32_t>( *otDynamicRefreshRates );
|
||
|
||
--
|
||
2.47.1
|
||
|
||
|
||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||
From: Antheas Kapenekakis <git@antheas.dev>
|
||
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 d541f70..8e5ad6a 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 <git@antheas.dev>
|
||
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 8e5ad6a..bc238e4 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 )
|
||
{
|
||
@@ -7634,6 +7638,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 <git@antheas.dev>
|
||
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 bc238e4..d968de5 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
|
||
@@ -7095,6 +7099,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 <git@antheas.dev>
|
||
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 d8a24e9..00f5ece 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 d968de5..86773db 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
|
||
@@ -7100,6 +7107,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);
|
||
@@ -8067,9 +8075,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<uint64_t> vulkan_screenshot( const struct FrameInfo_t *frameInfo,
|
||
extern std::string g_reshade_effect;
|
||
extern uint32_t g_reshade_technique_idx;
|
||
|
||
-std::optional<uint64_t> vulkan_composite( struct FrameInfo_t *frameInfo, gamescope::Rc<CVulkanTexture> pPipewireTexture, bool partial, gamescope::Rc<CVulkanTexture> pOutputOverride, bool increment, std::unique_ptr<CVulkanCmdBuffer> pInCommandBuffer )
|
||
+std::optional<uint64_t> vulkan_composite( struct FrameInfo_t *frameInfo, gamescope::Rc<CVulkanTexture> pPipewireTexture, bool partial, gamescope::Rc<CVulkanTexture> pOutputOverride, bool increment, std::unique_ptr<CVulkanCmdBuffer> pInCommandBuffer, bool applyRotation )
|
||
{
|
||
EOTF outputTF = frameInfo->outputEncodingEOTF;
|
||
if (!frameInfo->applyOutputColorMgmt)
|
||
@@ -3928,7 +3961,15 @@ std::optional<uint64_t> 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<RcasPushData_t>(frameInfo, g_upscaleFilterSharpness / 10.0f);
|
||
|
||
cmdBuffer->dispatch(div_roundup(currentOutputWidth, pixelsPerGroup), div_roundup(currentOutputHeight, pixelsPerGroup));
|
||
@@ -3971,7 +4012,15 @@ std::optional<uint64_t> 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<BlitPushData_t>(&nisFrameInfo);
|
||
|
||
int pixelsPerGroup = 8;
|
||
@@ -4009,7 +4058,15 @@ std::optional<uint64_t> 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<uint64_t> 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<BlitPushData_t>(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<BlitPushData_t>(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<BlitPushData_t>(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<BlitPushData_t>(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 00f5ece..8cffcbe 100644
|
||
--- a/src/rendervulkan.hpp
|
||
+++ b/src/rendervulkan.hpp
|
||
@@ -393,7 +393,7 @@ gamescope::OwningRc<CVulkanTexture> vulkan_create_texture_from_dmabuf( struct wl
|
||
gamescope::OwningRc<CVulkanTexture> 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<CVulkanTexture> vulkan_create_texture_from_wlr_buffer( struct wlr_buffer *buf, gamescope::OwningRc<gamescope::IBackendFb> pBackendFb );
|
||
|
||
-std::optional<uint64_t> vulkan_composite( struct FrameInfo_t *frameInfo, gamescope::Rc<CVulkanTexture> pScreenshotTexture, bool partial, gamescope::Rc<CVulkanTexture> pOutputOverride = nullptr, bool increment = true, std::unique_ptr<CVulkanCmdBuffer> pInCommandBuffer = nullptr );
|
||
+std::optional<uint64_t> vulkan_composite( struct FrameInfo_t *frameInfo, gamescope::Rc<CVulkanTexture> pScreenshotTexture, bool partial, gamescope::Rc<CVulkanTexture> pOutputOverride = nullptr, bool increment = true, std::unique_ptr<CVulkanCmdBuffer> pInCommandBuffer = nullptr, bool applyRotation = false );
|
||
void vulkan_wait( uint64_t ulSeqNo, bool bReset );
|
||
gamescope::Rc<CVulkanTexture> vulkan_get_last_output_image( bool partial, bool defer );
|
||
gamescope::Rc<CVulkanTexture> vulkan_acquire_screenshot_texture(uint32_t width, uint32_t height, bool exportable, uint32_t drmFormat, EStreamColorspace colorspace = k_EStreamColorspace_Unknown);
|
||
@@ -530,6 +530,9 @@ struct VulkanOutput_t
|
||
// NIS
|
||
gamescope::OwningRc<CVulkanTexture> nisScalerImage;
|
||
gamescope::OwningRc<CVulkanTexture> nisUsmImage;
|
||
+
|
||
+ // Rotated
|
||
+ gamescope::OwningRc<CVulkanTexture> rotatedOutput;
|
||
};
|
||
|
||
|
||
@@ -542,6 +545,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: brainantifreeze <you@example.com>
|
||
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 718a260..f33da7f 100644
|
||
--- a/layer/VkLayer_FROG_gamescope_wsi.cpp
|
||
+++ b/layer/VkLayer_FROG_gamescope_wsi.cpp
|
||
@@ -183,6 +183,16 @@ namespace GamescopeWSILayer {
|
||
return s_ensureMinImageCount;
|
||
}
|
||
|
||
+ static bool getHidePresentWait() {
|
||
+ static bool s_hidePresentWait = []() -> bool {
|
||
+ if (auto hide = parseEnv<bool>("GAMESCOPE_WSI_HIDE_PRESENT_WAIT_EXT")) {
|
||
+ return *hide;
|
||
+ }
|
||
+ return false;
|
||
+ }();
|
||
+ return s_hidePresentWait;
|
||
+ }
|
||
+
|
||
// Taken from Mesa, licensed under MIT.
|
||
//
|
||
// No real reason to rewrite this code,
|
||
@@ -588,7 +598,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)
|
||
@@ -893,6 +907,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<VkPhysicalDevicePresentWaitFeaturesKHR>(pFeatures);
|
||
+ }
|
||
pDispatch->GetPhysicalDeviceFeatures2(physicalDevice, pFeatures);
|
||
}
|
||
|
||
--
|
||
2.47.1
|
||
|
||
|
||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||
From: "Ruan E. Formigoni" <ruanformigoni@gmail.com>
|
||
Date: Thu, 2 Jan 2025 17:07:36 +0100
|
||
Subject: feat: implemented 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 f90c096..9f136dd 100644
|
||
--- a/src/Backends/WaylandBackend.cpp
|
||
+++ b/src/Backends/WaylandBackend.cpp
|
||
@@ -1624,6 +1624,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 <mutex>
|
||
#include <vector>
|
||
#include <cstring>
|
||
+#include <sstream>
|
||
#include <string>
|
||
#if defined(__linux__)
|
||
#include <sys/capability.h>
|
||
@@ -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<uint64_t> 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<BicubicPushData_t>(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<BlitPushData_t>(&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 8cffcbe..91e2af7 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;
|
||
@@ -544,6 +545,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 86773db..f88576f 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;
|
||
}
|
||
@@ -7023,6 +7033,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 );
|
||
@@ -7281,6 +7292,7 @@ extern int g_nPreferredOutputWidth;
|
||
extern int g_nPreferredOutputHeight;
|
||
|
||
static bool g_bWasFSRActive = false;
|
||
+static bool g_bWasBicubicActive = false;
|
||
|
||
bool g_bAppWantsHDRCached = false;
|
||
|
||
@@ -7695,6 +7707,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();
|
||
|
||
@@ -7931,6 +7953,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
|
||
|