tweak: decouple GUI window-move animations from the render frame rate#2833
tweak: decouple GUI window-move animations from the render frame rate#2833bobtista wants to merge 2 commits into
Conversation
|
| Filename | Overview |
|---|---|
| Generals/Code/GameEngine/Source/GameClient/GUI/AnimateWindowManager.cpp | Adds wall-clock accumulator in update() and extracts step(); logic is correct for the render-frame path. Accumulator properly initialized across all lifecycle methods. |
| Generals/Code/GameEngine/Include/GameClient/AnimateWindowManager.h | Adds private step() declaration and Real m_updateAccumulator member; no structural concerns. |
| GeneralsMD/Code/GameEngine/Source/GameClient/GUI/AnimateWindowManager.cpp | Mirrors Generals/ changes exactly; same analysis applies. |
| GeneralsMD/Code/GameEngine/Include/GameClient/AnimateWindowManager.h | Mirrors Generals/ header changes exactly; no concerns. |
Sequence Diagram
%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant Caller as Render Frame or Shell Gate
participant AWM as AnimateWindowManager
participant FP as TheFramePacer
participant Anim as ProcessAnimateWindow
Caller->>AWM: update()
AWM->>FP: getUpdateTime()
FP-->>AWM: deltaSeconds
AWM->>AWM: clamp to 0.2s max
AWM->>AWM: "accumulator += deltaSeconds x 30"
AWM->>AWM: "steps = floor(accumulator)"
AWM->>AWM: "accumulator -= steps"
loop Once per whole base-rate step
AWM->>AWM: step()
AWM->>Anim: updateAnimateWindow or reverseAnimateWindow
Anim-->>AWM: finished or needsUpdate
end
AWM-->>Caller: return
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
participant Caller as Render Frame or Shell Gate
participant AWM as AnimateWindowManager
participant FP as TheFramePacer
participant Anim as ProcessAnimateWindow
Caller->>AWM: update()
AWM->>FP: getUpdateTime()
FP-->>AWM: deltaSeconds
AWM->>AWM: clamp to 0.2s max
AWM->>AWM: "accumulator += deltaSeconds x 30"
AWM->>AWM: "steps = floor(accumulator)"
AWM->>AWM: "accumulator -= steps"
loop Once per whole base-rate step
AWM->>AWM: step()
AWM->>Anim: updateAnimateWindow or reverseAnimateWindow
Anim-->>AWM: finished or needsUpdate
end
AWM-->>Caller: return
Reviews (3): Last reviewed commit: "tweak: decouple GUI window-move animatio..." | Re-trigger Greptile
xezon
left a comment
There was a problem hiding this comment.
Try to use less AI code generation for better code
| ProcessAnimateWindowSlideFromTopFast *m_slideFromTopFast; ///< holds the process in which the windows slide from the top,fast | ||
| ProcessAnimateWindow *getProcessAnimate( AnimTypes animType); ///< returns the process for the kind of animation we need. | ||
|
|
||
| void updateStep(); ///< Runs a single base-rate step of all registered window animations |
| ProcessAnimateWindowSlideFromTopFast *m_slideFromTopFast; ///< holds the process in which the windows slide from the top,fast | ||
| ProcessAnimateWindow *getProcessAnimate( AnimTypes animType); ///< returns the process for the kind of animation we need. | ||
|
|
||
| void updateStep(); ///< Runs a single base-rate step of all registered window animations |
| m_updateAccumulator -= (Real)steps; | ||
|
|
||
| // Cap the catch-up burst so a long stall (load, alt-tab) cannot snap an animation instantly. | ||
| const Int maxSteps = 6; |
| void AnimateWindowManager::update() | ||
| { | ||
| const UnsignedInt now = timeGetTime(); | ||
| if (m_lastUpdateTime == 0) |
There was a problem hiding this comment.
Try to get rid of this condition. Maybe set in init function?
| ProcessAnimateWindowSlideFromTopFast *m_slideFromTopFast; ///< holds the process in which the windows slide from the top,fast | ||
| ProcessAnimateWindow *getProcessAnimate( AnimTypes animType); ///< returns the process for the kind of animation we need. | ||
|
|
||
| void updateStep(); ///< Runs a single base-rate step of all registered window animations |
There was a problem hiding this comment.
Do not put function below member variables.
| // render frame rate. The animation logic runs at the historic base rate; here we run the | ||
| // number of base-rate steps that match the elapsed wall-clock time, carrying the fractional | ||
| // remainder between updates. This is independent of how often update() is called, so callers | ||
| // pumped per render frame and callers throttled to a fixed rate both animate at the same speed. |
| // pumped per render frame and callers throttled to a fixed rate both animate at the same speed. | ||
| void AnimateWindowManager::update() | ||
| { | ||
| const UnsignedInt now = timeGetTime(); |
There was a problem hiding this comment.
Avoid timeGetTime where possible. Use TheFramePacer->getUpdateTime()
76414cb to
e5a23b5
Compare
e5a23b5 to
e829b80
Compare
Addresses #2832, Similar to 2056, which decoupled the
GameWindowTransitions.Problem
Control bar, diplomacy/communicator panels slide animations go too fast when you increase render FPS
Approach
AnimateWindowManager::update()now paces itself by elapsed wall-clock time: it runs the number of base-rate (30 fps) steps matching the real time since the last update, carrying the fractional remainder between updates.Wall-clock pacing is used here (rather than #2056's
getBaseOverUpdateFpsRatio()) on purpose:AnimateWindowManageris pumped on two different clocks. The shell drives it through a fixed ~30 Hz wall-clock gate (Shell::update), while in-game callers drive it once per render frame. A render-frame ratio would double-decouple the already-gated shell path. Wall-clock self-pacing is correct for both with no per-call-site changes. A 6-step catch-up cap keeps a lon snapping an animation straight to its end.TODO:
[x] Testing
[x] Replicate to Generals