Skip to content

fix(gui): Pace whole-screen fade loop through the frame pacer#2848

Open
bobtista wants to merge 2 commits into
TheSuperHackers:mainfrom
bobtista:bobtista/fix/fade-loop-frame-pacer
Open

fix(gui): Pace whole-screen fade loop through the frame pacer#2848
bobtista wants to merge 2 commits into
TheSuperHackers:mainfrom
bobtista:bobtista/fix/fade-loop-frame-pacer

Conversation

@bobtista

Copy link
Copy Markdown

Follow-up to #2840.

The whole-screen fade loop in GameLogic::tryStartNewGame paced itself with a hardcoded Sleep(33). This froze the frame pacer for the duration of the fade, so the frame-rate-decoupled transition step from #2056 (getBaseOverUpdateFpsRatio()) read a stale delta and advanced at a fixed, frame-rate-dependent rate.

This change replaces the delay with TheFramePacer->update(), which caps the framerate and measures real elapsed time. The fade now advances at a consistent rate, and the magic 33 ms delay is gone now that transition speed is variable.

Todo

  • testing
  • replicate to Generals

@greptile-apps

greptile-apps Bot commented Jun 30, 2026

Copy link
Copy Markdown

Greptile Summary

Replaces the hardcoded Sleep(33) in the whole-screen fade loop inside tryStartNewGame with TheFramePacer->update(), making the fade advance at a consistent wall-clock rate regardless of frame rate.

  • The change is applied symmetrically to both the Generals and Zero Hour (GeneralsMD) variants of GameLogic.cpp.
  • TheFramePacer is constructed in GameMain() before the engine initializes and is guaranteed non-null for the entire engine lifetime, so no null-pointer risk exists here.

Confidence Score: 5/5

Safe to merge — two one-line identical fixes with no logic added or removed.

The change swaps a hardcoded OS sleep for the existing frame pacer in a tight transition loop. TheFramePacer is constructed unconditionally in GameMain() before the engine starts and lives for the entire game lifetime, so the call site is safe. The same pattern (TheFramePacer->update() in a tight loop) is already used in GameEngine.cpp. Both game variants are updated identically.

No files require special attention.

Important Files Changed

Filename Overview
Generals/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp Single-line fix replacing Sleep(33) with TheFramePacer->update() in the FadeWholeScreen transition loop; the pacer is always initialized before this code runs.
GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp Identical fix replicated to the Zero Hour variant; symmetric and consistent with the Generals change.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant GL as GameLogic::tryStartNewGame
    participant TH as TheTransitionHandler
    participant WM as TheWindowManager
    participant D as TheDisplay
    participant FP as TheFramePacer

    GL->>TH: setGroup("FadeWholeScreen")
    loop until isFinished()
        GL->>WM: update()
        alt not finished
            GL->>D: draw()
            GL->>GL: setFPMode()
            GL->>FP: update() [was Sleep(33)]
            Note over FP: Caps FPS + measures<br/>real elapsed time
        end
    end
    GL->>GL: deleteLoadScreen()
Loading
%%{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 GL as GameLogic::tryStartNewGame
    participant TH as TheTransitionHandler
    participant WM as TheWindowManager
    participant D as TheDisplay
    participant FP as TheFramePacer

    GL->>TH: setGroup("FadeWholeScreen")
    loop until isFinished()
        GL->>WM: update()
        alt not finished
            GL->>D: draw()
            GL->>GL: setFPMode()
            GL->>FP: update() [was Sleep(33)]
            Note over FP: Caps FPS + measures<br/>real elapsed time
        end
    end
    GL->>GL: deleteLoadScreen()
Loading

Reviews (2): Last reviewed commit: "fix(gui): Replicate fade-loop frame pace..." | Re-trigger Greptile

Comment thread Generals/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp Outdated
Comment thread Generals/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp Outdated
@xezon xezon added GUI For graphical user interface Minor Severity: Minor < Major < Critical < Blocker Gen Relates to Generals ZH Relates to Zero Hour Fix Is fixing something, but is not user facing labels Jul 1, 2026
@Caball009

Caball009 commented Jul 1, 2026

Copy link
Copy Markdown

Does this work properly? If your fps is 30, this would still sleep for 33 msec, right? That doesn't make sense to me if the menu transition speed is say 15.

@xezon

xezon commented Jul 1, 2026

Copy link
Copy Markdown

I tested this locally and it is waiting for completion of fade to black. I think frame pacing makes sense.

@Caball009

Caball009 commented Jul 1, 2026

Copy link
Copy Markdown

I tested this locally and it is waiting for completion of fade to black. I think frame pacing makes sense.

Did you test with #2840 and e.g. a window transition speed of 15?

EDIT: I'd expect it to work like this:

X = 1000 / (1000 / (fps * window_speed))
sleep(X)

@xezon

xezon commented Jul 1, 2026

Copy link
Copy Markdown

No I tested with defaults only.

@bobtista bobtista force-pushed the bobtista/fix/fade-loop-frame-pacer branch from 6219f68 to 472a8c6 Compare July 1, 2026 19:05
@bobtista

bobtista commented Jul 2, 2026

Copy link
Copy Markdown
Author

I tested with speed 1 and render fps 30 and 120, then again with speed 15 and render fps 30 and 120. The speed multiplier works and stays consistent across render speeds.

@bobtista

bobtista commented Jul 2, 2026

Copy link
Copy Markdown
Author

I tested this locally and it is waiting for completion of fade to black. I think frame pacing makes sense.

Did you test with #2840 and e.g. a window transition speed of 15?

EDIT: I'd expect it to work like this:

X = 1000 / (1000 / (fps * window_speed))
sleep(X)

I think that sleep(X) formula assumes the old model, fixed 1 frame per iteration, vary the sleep to control speed. The fade now advances a fixed number of transition-frames per real second regardless of frame rate eg at 120 fps it steps ~4× as often but ~4× smaller, and the total wall-clock duration matches 30 fps (just smoother). Duration is frameLength / BaseFps seconds. With #2840 the speed multiplier scales that already-time-normalized step, so speed 15 is 15× faster at any frame rate (frameLength / (BaseFps * speed)), independent of the wait.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Fix Is fixing something, but is not user facing Gen Relates to Generals GUI For graphical user interface Minor Severity: Minor < Major < Critical < Blocker ZH Relates to Zero Hour

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants