From a40483a774ca53310c7f2791ea985d1dd13e888d Mon Sep 17 00:00:00 2001 From: Bobby Battista Date: Sat, 27 Jun 2026 16:26:14 -0400 Subject: [PATCH 1/4] tweak: decouple GUI window-move animations from the render frame rate --- .../Include/GameClient/AnimateWindowManager.h | 3 +++ .../GameClient/GUI/AnimateWindowManager.cpp | 26 +++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/GeneralsMD/Code/GameEngine/Include/GameClient/AnimateWindowManager.h b/GeneralsMD/Code/GameEngine/Include/GameClient/AnimateWindowManager.h index f232411213e..9370d50c12f 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameClient/AnimateWindowManager.h +++ b/GeneralsMD/Code/GameEngine/Include/GameClient/AnimateWindowManager.h @@ -178,10 +178,13 @@ class AnimateWindowManager : public SubsystemInterface Bool isReversed(); ///< Returns whether or not we're in our reversed state. Bool isEmpty(); private: + void step(); ///< Runs a single base-rate step of all registered window animations + AnimateWindowList m_winList; ///< A list of AnimationWindows that we don't care if their finished animating AnimateWindowList m_winMustFinishList; ///< A list of AnimationWindows that we do care about Bool m_needsUpdate; ///< If we're done animating all our monitored windows, then this will be false Bool m_reverse; ///< Are we in a reverse state? + Real m_updateAccumulator; ///< Carries fractional base-rate steps between updates ProcessAnimateWindowSlideFromRight *m_slideFromRight; ///< Holds the process in which the windows slide from the right ProcessAnimateWindowSlideFromRightFast *m_slideFromRightFast; ProcessAnimateWindowSlideFromTop *m_slideFromTop; ///< Holds the process in which the windows slide from the Top diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/AnimateWindowManager.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/AnimateWindowManager.cpp index 151464bdc04..e6da4900e8d 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/AnimateWindowManager.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/AnimateWindowManager.cpp @@ -57,6 +57,7 @@ #include "GameClient/GameWindow.h" #include "GameClient/Display.h" #include "GameClient/ProcessAnimateWindow.h" +#include "Common/FramePacer.h" //----------------------------------------------------------------------------- // DEFINES //////////////////////////////////////////////////////////////////// //----------------------------------------------------------------------------- @@ -133,6 +134,7 @@ AnimateWindowManager::AnimateWindowManager() m_winList.clear(); m_needsUpdate = FALSE; m_reverse = FALSE; + m_updateAccumulator = 0.0f; m_winMustFinishList.clear(); } AnimateWindowManager::~AnimateWindowManager() @@ -159,6 +161,7 @@ void AnimateWindowManager::init() clearWinList(m_winMustFinishList); m_needsUpdate = FALSE; m_reverse = FALSE; + m_updateAccumulator = 0.0f; } void AnimateWindowManager::reset() @@ -168,9 +171,32 @@ void AnimateWindowManager::reset() clearWinList(m_winMustFinishList); m_needsUpdate = FALSE; m_reverse = FALSE; + m_updateAccumulator = 0.0f; } +// TheSuperHackers @tweak bobtista 27/06/2026 Decouple GUI window-move animations from the render frame rate void AnimateWindowManager::update() +{ + Real deltaSeconds = TheFramePacer->getUpdateTime(); + + // Clamp the delta so a long stall (load, alt-tab) cannot snap an animation to its end. + const Real maxCatchUpSeconds = 0.2f; + if (deltaSeconds > maxCatchUpSeconds) + { + deltaSeconds = maxCatchUpSeconds; + } + + m_updateAccumulator += deltaSeconds * (Real)BaseFps; + Int steps = (Int)m_updateAccumulator; + m_updateAccumulator -= (Real)steps; + + while (steps-- > 0) + { + step(); + } +} + +void AnimateWindowManager::step() { ProcessAnimateWindow *processAnim = nullptr; From e829b80c4bb0f84cef7e7658bab96f0766c5f531 Mon Sep 17 00:00:00 2001 From: Bobby Battista Date: Sat, 27 Jun 2026 16:27:19 -0400 Subject: [PATCH 2/4] tweak: decouple GUI window-move animations from the render frame rate (Generals) --- .../Include/GameClient/AnimateWindowManager.h | 3 +++ .../GameClient/GUI/AnimateWindowManager.cpp | 26 +++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/Generals/Code/GameEngine/Include/GameClient/AnimateWindowManager.h b/Generals/Code/GameEngine/Include/GameClient/AnimateWindowManager.h index aad9e80b7a2..3d16dc7555c 100644 --- a/Generals/Code/GameEngine/Include/GameClient/AnimateWindowManager.h +++ b/Generals/Code/GameEngine/Include/GameClient/AnimateWindowManager.h @@ -178,10 +178,13 @@ class AnimateWindowManager : public SubsystemInterface Bool isReversed(); ///< Returns whether or not we're in our reversed state. Bool isEmpty(); private: + void step(); ///< Runs a single base-rate step of all registered window animations + AnimateWindowList m_winList; ///< A list of AnimationWindows that we don't care if their finished animating AnimateWindowList m_winMustFinishList; ///< A list of AnimationWindows that we do care about Bool m_needsUpdate; ///< If we're done animating all our monitored windows, then this will be false Bool m_reverse; ///< Are we in a reverse state? + Real m_updateAccumulator; ///< Carries fractional base-rate steps between updates ProcessAnimateWindowSlideFromRight *m_slideFromRight; ///< Holds the process in which the windows slide from the right ProcessAnimateWindowSlideFromRightFast *m_slideFromRightFast; ProcessAnimateWindowSlideFromTop *m_slideFromTop; ///< Holds the process in which the windows slide from the Top diff --git a/Generals/Code/GameEngine/Source/GameClient/GUI/AnimateWindowManager.cpp b/Generals/Code/GameEngine/Source/GameClient/GUI/AnimateWindowManager.cpp index c2b73a16b11..b5f0fff8e36 100644 --- a/Generals/Code/GameEngine/Source/GameClient/GUI/AnimateWindowManager.cpp +++ b/Generals/Code/GameEngine/Source/GameClient/GUI/AnimateWindowManager.cpp @@ -57,6 +57,7 @@ #include "GameClient/GameWindow.h" #include "GameClient/Display.h" #include "GameClient/ProcessAnimateWindow.h" +#include "Common/FramePacer.h" //----------------------------------------------------------------------------- // DEFINES //////////////////////////////////////////////////////////////////// //----------------------------------------------------------------------------- @@ -133,6 +134,7 @@ AnimateWindowManager::AnimateWindowManager() m_winList.clear(); m_needsUpdate = FALSE; m_reverse = FALSE; + m_updateAccumulator = 0.0f; m_winMustFinishList.clear(); } AnimateWindowManager::~AnimateWindowManager() @@ -159,6 +161,7 @@ void AnimateWindowManager::init() clearWinList(m_winMustFinishList); m_needsUpdate = FALSE; m_reverse = FALSE; + m_updateAccumulator = 0.0f; } void AnimateWindowManager::reset() @@ -168,9 +171,32 @@ void AnimateWindowManager::reset() clearWinList(m_winMustFinishList); m_needsUpdate = FALSE; m_reverse = FALSE; + m_updateAccumulator = 0.0f; } +// TheSuperHackers @tweak bobtista 27/06/2026 Decouple GUI window-move animations from the render frame rate void AnimateWindowManager::update() +{ + Real deltaSeconds = TheFramePacer->getUpdateTime(); + + // Clamp the delta so a long stall (load, alt-tab) cannot snap an animation to its end. + const Real maxCatchUpSeconds = 0.2f; + if (deltaSeconds > maxCatchUpSeconds) + { + deltaSeconds = maxCatchUpSeconds; + } + + m_updateAccumulator += deltaSeconds * (Real)BaseFps; + Int steps = (Int)m_updateAccumulator; + m_updateAccumulator -= (Real)steps; + + while (steps-- > 0) + { + step(); + } +} + +void AnimateWindowManager::step() { ProcessAnimateWindow *processAnim = nullptr; From c240eba6f9e8f237cb8327d4df05bb753ecf14ed Mon Sep 17 00:00:00 2001 From: Bobby Battista Date: Wed, 8 Jul 2026 22:35:18 +0400 Subject: [PATCH 3/4] tweak: use frame pacer ratio for animation pacing and pump the manager every frame --- .../Include/GameClient/AnimateWindowManager.h | 2 +- .../GameClient/GUI/AnimateWindowManager.cpp | 21 ++++++------------- .../Source/GameClient/GUI/Shell/Shell.cpp | 6 +++--- 3 files changed, 10 insertions(+), 19 deletions(-) diff --git a/GeneralsMD/Code/GameEngine/Include/GameClient/AnimateWindowManager.h b/GeneralsMD/Code/GameEngine/Include/GameClient/AnimateWindowManager.h index 9370d50c12f..447d60699a4 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameClient/AnimateWindowManager.h +++ b/GeneralsMD/Code/GameEngine/Include/GameClient/AnimateWindowManager.h @@ -184,7 +184,7 @@ class AnimateWindowManager : public SubsystemInterface AnimateWindowList m_winMustFinishList; ///< A list of AnimationWindows that we do care about Bool m_needsUpdate; ///< If we're done animating all our monitored windows, then this will be false Bool m_reverse; ///< Are we in a reverse state? - Real m_updateAccumulator; ///< Carries fractional base-rate steps between updates + Real m_frameAccumulator; ///< Carries fractional base-rate frames between updates ProcessAnimateWindowSlideFromRight *m_slideFromRight; ///< Holds the process in which the windows slide from the right ProcessAnimateWindowSlideFromRightFast *m_slideFromRightFast; ProcessAnimateWindowSlideFromTop *m_slideFromTop; ///< Holds the process in which the windows slide from the Top diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/AnimateWindowManager.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/AnimateWindowManager.cpp index e6da4900e8d..53efa416793 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/AnimateWindowManager.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/AnimateWindowManager.cpp @@ -134,7 +134,7 @@ AnimateWindowManager::AnimateWindowManager() m_winList.clear(); m_needsUpdate = FALSE; m_reverse = FALSE; - m_updateAccumulator = 0.0f; + m_frameAccumulator = 0.0f; m_winMustFinishList.clear(); } AnimateWindowManager::~AnimateWindowManager() @@ -161,7 +161,7 @@ void AnimateWindowManager::init() clearWinList(m_winMustFinishList); m_needsUpdate = FALSE; m_reverse = FALSE; - m_updateAccumulator = 0.0f; + m_frameAccumulator = 0.0f; } void AnimateWindowManager::reset() @@ -171,24 +171,15 @@ void AnimateWindowManager::reset() clearWinList(m_winMustFinishList); m_needsUpdate = FALSE; m_reverse = FALSE; - m_updateAccumulator = 0.0f; + m_frameAccumulator = 0.0f; } // TheSuperHackers @tweak bobtista 27/06/2026 Decouple GUI window-move animations from the render frame rate void AnimateWindowManager::update() { - Real deltaSeconds = TheFramePacer->getUpdateTime(); - - // Clamp the delta so a long stall (load, alt-tab) cannot snap an animation to its end. - const Real maxCatchUpSeconds = 0.2f; - if (deltaSeconds > maxCatchUpSeconds) - { - deltaSeconds = maxCatchUpSeconds; - } - - m_updateAccumulator += deltaSeconds * (Real)BaseFps; - Int steps = (Int)m_updateAccumulator; - m_updateAccumulator -= (Real)steps; + m_frameAccumulator += TheFramePacer->getBaseOverUpdateFpsRatio(); + Int steps = (Int)m_frameAccumulator; + m_frameAccumulator -= (Real)steps; while (steps-- > 0) { diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/Shell/Shell.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/Shell/Shell.cpp index e7d1333905b..f4114c88ca4 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/Shell/Shell.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/Shell/Shell.cpp @@ -205,9 +205,6 @@ void Shell::update() } - // Update the animate window manager - m_animateWindowManager->update(); - m_schemeManager->update(); // mark last time we ran the updates @@ -215,6 +212,9 @@ void Shell::update() } + // TheSuperHackers @tweak bobtista 08/07/2026 Update the animate window manager every frame, it paces itself now + m_animateWindowManager->update(); + } //------------------------------------------------------------------------------------------------- From 9e1972c0dc2862697e9a0f1f4c7c6a5a4dcc2323 Mon Sep 17 00:00:00 2001 From: Bobby Battista Date: Wed, 8 Jul 2026 22:35:18 +0400 Subject: [PATCH 4/4] tweak: use frame pacer ratio for animation pacing and pump the manager every frame (Generals) --- .../Include/GameClient/AnimateWindowManager.h | 2 +- .../GameClient/GUI/AnimateWindowManager.cpp | 21 ++++++------------- .../Source/GameClient/GUI/Shell/Shell.cpp | 6 +++--- 3 files changed, 10 insertions(+), 19 deletions(-) diff --git a/Generals/Code/GameEngine/Include/GameClient/AnimateWindowManager.h b/Generals/Code/GameEngine/Include/GameClient/AnimateWindowManager.h index 3d16dc7555c..a0c16ee1045 100644 --- a/Generals/Code/GameEngine/Include/GameClient/AnimateWindowManager.h +++ b/Generals/Code/GameEngine/Include/GameClient/AnimateWindowManager.h @@ -184,7 +184,7 @@ class AnimateWindowManager : public SubsystemInterface AnimateWindowList m_winMustFinishList; ///< A list of AnimationWindows that we do care about Bool m_needsUpdate; ///< If we're done animating all our monitored windows, then this will be false Bool m_reverse; ///< Are we in a reverse state? - Real m_updateAccumulator; ///< Carries fractional base-rate steps between updates + Real m_frameAccumulator; ///< Carries fractional base-rate frames between updates ProcessAnimateWindowSlideFromRight *m_slideFromRight; ///< Holds the process in which the windows slide from the right ProcessAnimateWindowSlideFromRightFast *m_slideFromRightFast; ProcessAnimateWindowSlideFromTop *m_slideFromTop; ///< Holds the process in which the windows slide from the Top diff --git a/Generals/Code/GameEngine/Source/GameClient/GUI/AnimateWindowManager.cpp b/Generals/Code/GameEngine/Source/GameClient/GUI/AnimateWindowManager.cpp index b5f0fff8e36..f2ea185ecbf 100644 --- a/Generals/Code/GameEngine/Source/GameClient/GUI/AnimateWindowManager.cpp +++ b/Generals/Code/GameEngine/Source/GameClient/GUI/AnimateWindowManager.cpp @@ -134,7 +134,7 @@ AnimateWindowManager::AnimateWindowManager() m_winList.clear(); m_needsUpdate = FALSE; m_reverse = FALSE; - m_updateAccumulator = 0.0f; + m_frameAccumulator = 0.0f; m_winMustFinishList.clear(); } AnimateWindowManager::~AnimateWindowManager() @@ -161,7 +161,7 @@ void AnimateWindowManager::init() clearWinList(m_winMustFinishList); m_needsUpdate = FALSE; m_reverse = FALSE; - m_updateAccumulator = 0.0f; + m_frameAccumulator = 0.0f; } void AnimateWindowManager::reset() @@ -171,24 +171,15 @@ void AnimateWindowManager::reset() clearWinList(m_winMustFinishList); m_needsUpdate = FALSE; m_reverse = FALSE; - m_updateAccumulator = 0.0f; + m_frameAccumulator = 0.0f; } // TheSuperHackers @tweak bobtista 27/06/2026 Decouple GUI window-move animations from the render frame rate void AnimateWindowManager::update() { - Real deltaSeconds = TheFramePacer->getUpdateTime(); - - // Clamp the delta so a long stall (load, alt-tab) cannot snap an animation to its end. - const Real maxCatchUpSeconds = 0.2f; - if (deltaSeconds > maxCatchUpSeconds) - { - deltaSeconds = maxCatchUpSeconds; - } - - m_updateAccumulator += deltaSeconds * (Real)BaseFps; - Int steps = (Int)m_updateAccumulator; - m_updateAccumulator -= (Real)steps; + m_frameAccumulator += TheFramePacer->getBaseOverUpdateFpsRatio(); + Int steps = (Int)m_frameAccumulator; + m_frameAccumulator -= (Real)steps; while (steps-- > 0) { diff --git a/Generals/Code/GameEngine/Source/GameClient/GUI/Shell/Shell.cpp b/Generals/Code/GameEngine/Source/GameClient/GUI/Shell/Shell.cpp index fff2cf12c94..d8170c95f2c 100644 --- a/Generals/Code/GameEngine/Source/GameClient/GUI/Shell/Shell.cpp +++ b/Generals/Code/GameEngine/Source/GameClient/GUI/Shell/Shell.cpp @@ -205,9 +205,6 @@ void Shell::update() } - // Update the animate window manager - m_animateWindowManager->update(); - m_schemeManager->update(); // mark last time we ran the updates @@ -215,6 +212,9 @@ void Shell::update() } + // TheSuperHackers @tweak bobtista 08/07/2026 Update the animate window manager every frame, it paces itself now + m_animateWindowManager->update(); + } //-------------------------------------------------------------------------------------------------