Skip to content

[GEN][ZH] Exclusive fullscreen remains device-lost for ~45 seconds after startup #3049

Open

Description

@sailro

Summary

On Windows 11 with an NVIDIA GPU, the clean latest main build can enter a prolonged Direct3D 8 lost-device state shortly after startup while the game is foreground and not minimized.

During the fault, render FPS drops to roughly 5-8 FPS, DX8Wrapper::IsDeviceLost remains true, and DX8Wrapper::FrameCount stays at zero. The process is responsive and game logic is not the bottleneck. The behavior disappears completely in windowed/windowed-fullscreen presentation.

This appears related to the lost-device recovery discussed in #28 / #846, but is not a crash: the game can remain in the recovery loop for approximately 45-50 seconds before recovering by itself.

Tested build

  • Repository: clean TheSuperHackers/GeneralsGameCode main
  • Commit: bb5168c8d0685f0fc0628f6b1b74e49a91258e41
  • Release executable SHA-256: C1069626F2B354C5B0505C9EC314D1D21D5E7C6D705CAB2611C96254856FE785
  • Steam Zero Hour 1.04 data

System

  • Windows 11 Pro 10.0.26200 (build 26200)
  • Intel Core i9-13900KF
  • NVIDIA GeForce RTX 4090
  • NVIDIA drivers reproduced: 610.74 and 610.88 (32.0.16.1088)
  • Desktop/game resolution: 2560x1440 at 239 Hz

Reproduction

  1. Build current main in Release.
  2. Launch generalszh.exe in the default exclusive-fullscreen mode at the desktop resolution.
  3. Keep the window foreground and not minimized.
  4. Within roughly 10-20 seconds, observe repeated device resets followed by a sustained lost-device state and ~5-8 render FPS.

Representative clean-upstream monitor output after activating the game window:

16:51:07 fps=10.2 cap=30 frame=1 lost=0 resets=11 foreground=true
16:51:10 fps=30.0 cap=30 frame=0 lost=1 resets=13 foreground=true
16:51:20 fps=6.4  cap=30 frame=0 lost=1 resets=16 foreground=true
16:51:28 fps=5.9  cap=30 frame=0 lost=1 resets=16 foreground=true
16:51:44 fps=5.5  cap=30 frame=0 lost=1 resets=16 foreground=true

On the GeneralsOnline fork (which contains the same upstream rendering code), monitoring from process creation shows the full recovery cycle:

16:19:39 fps=9.3 cap=60 frame=0 lost=1 foreground=true
16:19:47 fps=6.0 cap=60 frame=0 lost=1 foreground=true
...
16:20:26 fps=4.6 cap=60 frame=0 lost=1 foreground=true
16:20:27 fps=60.0 cap=60 frame=46 lost=0 foreground=true

A/B results

Configuration Result
Clean upstream main, exclusive fullscreen Reproduces
Clean upstream main, -win -nologo -noshellmap Stable 30 FPS, lost=0, no resets
GeneralsOnline latest main, normal launcher Reproduces
GeneralsOnline executable launched directly Reproduces
GeneralsOnline with -nologo Reproduces
GeneralsOnline with -nologo -noshellmap Reproduces
GeneralsOnline with GENERALS_ONLINE_WINDOWED_FULLSCREEN Stable 60 FPS, lost=0, no resets

This rules out the launcher, startup movie, shell map, terrain rendering changes, FPS limiter, CPU/GPU load, and a single NVIDIA driver version.

Source observation

DX8Wrapper::End_Scene() does this after Present() returns D3DERR_DEVICELOST:

hr = _Get_D3D_Device8()->TestCooperativeLevel();
if (hr == D3DERR_DEVICENOTRESET)
    Reset_Device();
else
    ThreadClass::Sleep_Ms(200);

The measured ~5 FPS cadence exactly matches the 200 ms sleep path. During the fault, IsDeviceLost stays true and FrameCount remains zero until recovery.

PR #846 removed the old Reset_D3D_Device(bool active) activation hack and relies on the cooperative-level recovery path. Its discussion already contains a closely related observation:

This is even happening if I launch the game and never leave the game window.

#846 (comment)

Related work:

Expected behavior

Exclusive-fullscreen startup should not remain device-lost for tens of seconds while foreground. Alternatively, fullscreen-at-desktop-resolution could use a supported windowed/borderless presentation path, which avoids the failure in all measurements above.

Activity

  1. sailro commented on Aug 2, 2026

    @sailro
    Author

    cc @xezon given the context.

  2. mirelle7 commented on Aug 2, 2026

    @mirelle7

    About the "Alternatively": DX8 severely increases latency when it does borderless fullscreen. The first version to support this properly is dx9ex. Which also fixes this lost device stuff.

  3. sailro commented on Aug 2, 2026

    @sailro
    Author

    About the "Alternatively": DX8 severely increases latency when it does borderless fullscreen. The first version to support this properly is dx9ex. Which also fixes this lost device stuff.

    Ok I see. I don't really understand why I've only been having this problem recently. Rebooting seems to fix the issue as well, but that makes it pretty annoying.

  4. mrtnptrs commented on Oct 6, 2026

    @mrtnptrs

    I can still reproduce this on latest vanilla Steam ZH, TSH and GO. Does anyone have any info on what might be the specific cause of this issue? I heard on the GO-Discord (Command Center), that this might be due to a Windows Update, but sadly no one can provide specifics. It is also said that more older games suffer from issues like these. So, I was curious if anyone here is able to provide some insights on the cause of this issue?

    I know, switching to windowed mode as a workaround (and likely using a wrapper) works, but I'm more interested to get to know the possible root cause of this issue.

  5. coconutbird commented on Nov 5, 2026

    @coconutbird

    Also reproducing here, on both TheSuperHackers weekly-2026-10-30 (3fcbbb4) and retail Steam game.dat.

    System: Windows 11 10.0.26200, RTX 5070 + AMD Radeon iGPU (both active), Parsec Virtual Display Adapter installed, 2x ASUS 2560x1440 @ 144 Hz, 125% scaling.

    Symptoms (exclusive fullscreen, launched from Steam, no Resolution in Options.ini so 800x600):

    1. Primary monitor switches to 800x600, second monitor re-lays out/glitches.
    2. Screen flashes back to the desktop mode, then returns to the game.
    3. Game appears frozen. The process stays responsive (no AppHang event, no ReleaseCrashInfo written). I ended it with Task Manager after ~45 s (Steam gameprocess_log: exit code 1), so it did not crash on its own, and I didn't wait long enough to see whether it would recover by itself as described above.

    Other observations on the same PC:

    • TSH Generals (generalsv.exe) fullscreen was intermittent: two Steam launches exited with code 1 after 93 s and 34 s, a later one ran fine for hours.
    • Retail Steam Generals game.dat produced an AppHangB1 ("stopped interacting with Windows").

    Workaround: -win + Resolution = 2560 1440 (client area fills the monitor, title bar is pushed off-screen) is stable on both games.

    Extra data:

    • Retail Steam ZH game.dat on this PC crashed with "Uncaught exception in Main::WndProc", access violation reading 0x0 at d3d8!CD3DBase::DeletePixelShader+0xB9 (SysWOW64 d3d8.dll RVA 0x50F89, PDB 1680DBE07E5EC781E2FA8676D9ED4B91/1, symbolized with Microsoft public symbols), about 13 s after launch. That is the old WM_ACTIVATEAPP -> Reset_Device path removed in [GEN][ZH] Fix Alt-tabbing crash #846, so it suggests the window gets an activation change during fullscreen startup.
    • The Steam overlay log for the ZH process shows GetProcAddress Direct3DCreate8 returned wrong module (apphelp.dll), i.e. a Windows compat shim hooks Direct3DCreate8 for ZH's Generals.exe. Not seen for the Generals process. sysmain.sdb has entries for generals.exe/game.dat; the WM_ACTIVATEAPP comment in WinMain.cpp already mentions DXMaximizedWindowedMode.

    Code notes (weekly-2026-10-30):

    • DX8Wrapper::Reset_Device releases all non-managed resources before checking TestCooperativeLevel. On D3DERR_DEVICELOST or a failed Reset() it returns false without reacquiring anything and without logging in Release. All callers ignore the result: End_Scene L1705, Flip_To_Primary L1753, WW3D::Begin_Render L822. So every retry re-releases and re-uploads everything, which would explain the climbing resets= counter.
    • End_Scene L1696-1711 sleeps 200 ms per frame while lost, which gives the ~5 FPS "frozen" picture while the message pump keeps running.
    • Fullscreen uses D3DPRESENT_RATE_DEFAULT and falls back to 800x600 when no Resolution is saved, forcing a full mode change that re-lays out multi-monitor desktops. Not required for the bug (it reproduces at desktop res above), but it adds activation churn on multi-monitor setups.
    • Minor: initializeAppWindows passes WS_EX_TOPMOST (an extended style) in dwStyle for fullscreen. It's a no-op there; topmost is applied later by SetWindowPos(HWND_TOPMOST). Same in Generals/Code/Main/WinMain.cpp.
  6. coconutbird commented on Nov 5, 2026

    @coconutbird

    Follow-up to my comment above: I found what keeps the device lost on my system and opened #3656.

    In fullscreen the game uses a D3DFMT_A8R8G8B8 back buffer, while the display mode is D3DFMT_X8R8G8B8. CSwapChain::Reset in d3d8.dll compares the back buffer format with the current display format to decide whether the display mode has to change. The two never match, so every fullscreen Reset() calls ChangeDisplaySettingsExA, even when the display already is in that mode. On my system a display mode change loses the device again shortly afterwards, so the game resets forever and never presents a frame. Every Reset() returns D3D_OK: 66 resets in about 30 seconds. A minimal D3D8 app shows the same loop with an A8R8G8B8 back buffer and runs stable with X8R8G8B8. Details and the disassembly are in #3656.

    #3656 switches the back buffer to the display format when a fullscreen reset does not last until a frame is presented. On my system the game then starts in fullscreen after 2 resets.

    If you can reproduce this issue, testing #3656 would help, especially on setups different from mine (single monitor, 239 Hz like the original report). Once CI has run, the build artifacts of the PR can be used. Worth checking:

    • whether startup still loops or stays device-lost
    • alt-tabbing out and back in
    • that only the soft water edge / shoreline blending goes away after the switch (expected)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions