diff --git a/src/main.cpp b/src/main.cpp index 1397028e7..1cf2a9d14 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -45,6 +45,7 @@ using namespace std::literals; EStreamColorspace g_ForcedNV12ColorSpace = k_EStreamColorspace_Unknown; extern gamescope::ConVar cv_adaptive_sync; +extern gamescope::ConVar cv_adaptive_sync_ignore_cursor; extern gamescope::ConVar cv_shutdown_on_primary_child_death; const char *gamescope_optstring = nullptr; @@ -74,6 +75,7 @@ const struct option *gamescope_options = (struct option[]){ { "mouse-sensitivity", required_argument, nullptr, 's' }, { "mangoapp", no_argument, nullptr, 0 }, { "adaptive-sync", no_argument, nullptr, 0 }, + { "adaptive-sync-ignore-cursor", no_argument, nullptr, 0 }, { "backend", required_argument, nullptr, 0 }, @@ -215,6 +217,7 @@ const char usage[] = " --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" " --mangoapp Launch with the mangoapp (mangohud) performance overlay enabled. You should use this instead of using mangohud on the game or gamescope.\n" " --adaptive-sync Enable adaptive sync if available (variable rate refresh)\n" + " --adaptive-sync-ignore-cursor Do not synchronize the cursor to the refresh rate\n" "\n" "Nested mode options:\n" " -o, --nested-unfocused-refresh game refresh rate when unfocused\n" @@ -812,6 +815,8 @@ int main(int argc, char **argv) g_nNestedDisplayIndex = parse_integer( optarg, opt_name ); } else if (strcmp(opt_name, "adaptive-sync") == 0) { cv_adaptive_sync = true; + } else if (strcmp(opt_name, "adaptive-sync-ignore-cursor") == 0) { + cv_adaptive_sync_ignore_cursor = true; } else if (strcmp(opt_name, "expose-wayland") == 0) { g_bExposeWayland = true; } else if (strcmp(opt_name, "backend") == 0) { diff --git a/src/steamcompmgr.cpp b/src/steamcompmgr.cpp index 644833647..52b105185 100644 --- a/src/steamcompmgr.cpp +++ b/src/steamcompmgr.cpp @@ -298,6 +298,8 @@ update_runtime_info(); gamescope::ConVar cv_adaptive_sync( "adaptive_sync", false, "Whether or not adaptive sync is enabled if available." ); gamescope::ConVar cv_adaptive_sync_ignore_overlay( "adaptive_sync_ignore_overlay", false, "Whether or not to ignore overlay planes for pushing commits with adaptive sync." ); gamescope::ConVar cv_adaptive_sync_overlay_cycles( "adaptive_sync_overlay_cycles", 1, "Number of vblank cycles to ignore overlay repaints before forcing a commit with adaptive sync." ); +gamescope::ConVar cv_adaptive_sync_ignore_cursor( "adaptive_sync_ignore_cursor", false, "Whether or not to ignore cursor plane for pushing commits with adaptive sync." ); +gamescope::ConVar cv_adaptive_sync_cursor_cycles( "adaptive_sync_cursor_cycles", 1, "Number of vblank cycles to ignore cursor repaints before forcing a commit with adaptive sync." ); gamescope::ConVar cv_upscale_preemptive( "upscale_preemptive", true, "Allow pre-emptive upscaling" ); gamescope::ConVar cv_upscale_preemptive_debug_force_sync( "upscale_preemptive_debug_force_sync", false, "Force synchronize pre-emptive upscaling" ); @@ -913,6 +915,7 @@ uint32_t lastPublishedInputCounter; std::atomic hasRepaint = false; bool hasRepaintNonBasePlane = false; +std::atomic hasRepaintCursor = false; static gamescope::ConCommand cc_debug_force_repaint( "debug_force_repaint", "Force a repaint", []( std::span args ) @@ -8792,13 +8795,20 @@ steamcompmgr_main(int argc, char **argv) bool bPainted = false; static int nIgnoredOverlayRepaints = 0; + static int nIgnoredCursorRepaints = 0; if ( !hasRepaintNonBasePlane ) nIgnoredOverlayRepaints = 0; + if ( !hasRepaintCursor ) + nIgnoredCursorRepaints = 0; + if ( cv_adaptive_sync_ignore_overlay ) nIgnoredOverlayRepaints = 0; + if ( cv_adaptive_sync_ignore_cursor ) + nIgnoredCursorRepaints = 0; + for ( auto &iter : g_VirtualConnectorFocuses ) { global_focus_t *pPaintFocus = &iter.second; @@ -8866,6 +8876,8 @@ steamcompmgr_main(int argc, char **argv) if ( nIgnoredOverlayRepaints ) eFlipType = FlipType::Normal; + if ( nIgnoredCursorRepaints ) + eFlipType = FlipType::Normal; if ( bHasOverlay ) // Don't tear if the Steam or perf overlay is up atm. eFlipType = FlipType::Normal; if ( GetVBlankTimer().WasCompositing() ) @@ -8920,6 +8932,24 @@ steamcompmgr_main(int argc, char **argv) nIgnoredOverlayRepaints++; } } + + if ( hasRepaintCursor ) + { + if ( nIgnoredCursorRepaints >= cv_adaptive_sync_cursor_cycles ) + { + // If we hit vblank and we previously punted on drawing a cursor update + // we should go ahead and draw now. + bShouldPaint = true; + } + else if ( !bShouldPaint ) + { + // If we hit vblank (ie. fastest refresh cycle since last commit), + // and we aren't painting and we have a pending cursor update, then: + // defer it until the next game update or next true vblank. + if ( !cv_adaptive_sync_ignore_cursor ) + nIgnoredCursorRepaints++; + } + } } // If we have a pending page flip and doing VRR, lets not do another... @@ -8948,6 +8978,7 @@ steamcompmgr_main(int argc, char **argv) { hasRepaint = false; hasRepaintNonBasePlane = false; + hasRepaintCursor = false; nIgnoredOverlayRepaints = 0; { diff --git a/src/wlserver.cpp b/src/wlserver.cpp index 1631ca780..5baa5ad1e 100644 --- a/src/wlserver.cpp +++ b/src/wlserver.cpp @@ -114,6 +114,7 @@ struct wlr_surface *wlserver_surface_to_main_surface( struct wlr_surface *pSurfa bool wlserver_process_hotkeys( wlr_keyboard *keyboard, uint32_t key, bool press ); extern std::atomic hasRepaint; +extern std::atomic hasRepaintCursor; std::vector& gamescope_xwayland_server_t::retrieve_commits() { @@ -2417,7 +2418,7 @@ void wlserver_oncursorevent() if ( !wlserver.bCursorHidden && wlserver.bCursorHasImage ) { - hasRepaint = true; + hasRepaintCursor = true; } } @@ -2490,7 +2491,7 @@ void wlserver_mousehide() if ( wlserver.bCursorHidden != true ) { wlserver.bCursorHidden = true; - hasRepaint = true; + hasRepaintCursor = true; } }