From 079956094bc79c321b506477bc566dd5afb6b03f Mon Sep 17 00:00:00 2001 From: Matthew Schwartz Date: Wed, 8 Apr 2026 14:29:11 -0700 Subject: [PATCH] steamcompmgr: throttle overlay frame callbacks to output refresh under VRR Under VRR, the main loop forces vblank=true every iteration so games can get commit wake-ups at their uncapped rate. Overlays fall through the FPS-limit throttle, so mangoapp receives a frame callback every main loop iteration and renders flat-out, spiking power usage. Throttle overlays on the latch path only, leaving the commit-done path alone so overlay commits are still marked done immediately and the next game-commit scanout picks up the freshest frame. --- src/steamcompmgr.cpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/steamcompmgr.cpp b/src/steamcompmgr.cpp index ff6b0e7e55..205d5efc06 100644 --- a/src/steamcompmgr.cpp +++ b/src/steamcompmgr.cpp @@ -5789,6 +5789,24 @@ static bool steamcompmgr_should_vblank_window( bool bShouldLimitFPS, uint64_t vb static bool steamcompmgr_should_vblank_window( steamcompmgr_win_t *w, uint64_t vblank_idx, uint64_t now ) { + // Overlays skip the FPS-limit throttle, so cap them to the output refresh under VRR. + if ( w && w->IsAnyOverlay() && + GetBackend()->GetCurrentConnector() && GetBackend()->GetCurrentConnector()->IsVRRActive() ) + { + uint64_t schedule = w->last_commit_first_latch_time + g_SteamCompMgrAppRefreshCycle; + + static constexpr uint64_t k_ulVRRScheduleFudge = 200'000; // 0.2ms + if ( now + k_ulVRRScheduleFudge < schedule ) + { + if ( !s_oLowestFPSLimitScheduleVRR ) + s_oLowestFPSLimitScheduleVRR = schedule; + else + s_oLowestFPSLimitScheduleVRR = std::min( *s_oLowestFPSLimitScheduleVRR, schedule ); + + return false; + } + } + return steamcompmgr_should_vblank_window( steamcompmgr_window_should_limit_fps( w ), vblank_idx, w, now ); }