Displays: -tident identify mode, panel re-entry fix, desktop-order log
Follow-up to the gos-displays.txt enumeration trace. The expanded log arrived from the Intel tester and settled the failure - and disproved the hypothesis it was written to test. What the log showed ------------------- The consistency check printed "No duplicate device assignments", so the suspected -tmon clash was NOT the cause. The actual evidence was that HSH_EnterFullScreen2 runs TWICE: the first entry brings every panel up DD_OK, the second fails on the same device with DDERR_EXCLUSIVEMODEALREADYSET and then DDERR_NOCOOPERATIVELEVELSET. Two independent causes, one operational and one a real defect. Cause 1 - Windows Display Settings numbers are not DirectDraw indices -------------------------------------------------------------------- The operator set -tmon by reading the numbers off the Display Settings arrangement diagram. On that machine all three numbering systems differ: Settings 3 -> \\.\DISPLAY1 -> device 0 (primary, 800x600, main) Settings 4 -> \\.\DISPLAY4 -> device 3 (USB adapter, radar) Settings 2 -> \\.\DISPLAY2 -> device 1 (mfd1) Settings 1 -> \\.\DISPLAY3 -> device 2 (mfd2) A permutation with two accidental fixed points - no derivable rule, and the Settings ordinal is not exposed by any documented API, so it cannot be translated in code. -tmon 1,4,2,3 worked first try (user-confirmed). This is why Windows ships an Identify button rather than publishing the mapping. Cause 2 - panels re-opened without being released (REAL BUG, all machines) ------------------------------------------------------------------------- EnterFullScreenMode() calls HSH_EnterFullScreen2() on every mode change and every lost-front-buffer recovery, but the teardown HSH_DirectDrawRelease2() was only wired to DirectDrawRelease(), i.e. full shutdown. CHSH_Device::InitFirst() therefore overwrote pDD with a fresh IDirectDraw7 while the previous one still held exclusive fullscreen, leaking it and its exclusive claim for the life of the process. It only bites when a panel sits on the Windows primary: secondary outputs grant exclusive mode again, the primary does not. Every working pod happens to have the main display on the primary, so no panel is ever there - which is the whole reason this looked hardware-specific. FIX: HSH_EnterFullScreen2() now releases first when hsh_initialized || hsh_mrdev_initialized. Tagged [panelreinit]. Reuses the existing teardown, which already restores the display mode, drops the coop level and clears the flags for both the MFD/radar and cameraship paths. New: -tident, the game's own Identify ------------------------------------- MW4.exe -tident [3..120, default 20] fills every display with a distinct colour and prints, huge, the number to type into -tmon, plus its device index and the role currently assigned to it. Then exits. Deliberately uses DDSCL_NORMAL and paints via GDI on the primary surface: no exclusive mode, no display mode change. Taking exclusive fullscreen on several devices at once is the very failure being diagnosed, and a diagnostic that trips over that fault is worthless - this works even on a pod where the MFD modes are broken. It opens the real DirectDraw devices rather than positioning GDI windows by HMONITOR, so it proves the device-index -> physical-output association through the same path the panels use. Positioning by DirectDraw's own reported HMONITOR would be circular. Implemented as IdentifyDisplays() in VideoCard.cpp, called at the end of FindVideoCards() followed by ExitProcess(0). Everything it paints is also written to gos-displays.txt, so the mapping survives even if a monitor is dead. Also added: desktop-order block ------------------------------- gos-displays.txt now prints the monitors sorted left-to-right by desktop position with their device indices, which maps directly onto the picture in Display Settings. For the reporting machine it reads out as -tmon 1,4,2,3 with no derivation required. Documentation ------------- * -help: new -tident entry. The -tmon text now states outright that these are NOT Display Settings numbers and points at -tident. Its old "-tmon 1,2,3,4" example was actively inviting the mistake that caused this report, so that section was rewritten rather than appended to. * Release notes (md + html, both hand-maintained): -tident section with sample output and the reason it has to exist; -tmon warning; new sections for the CLASH report and the re-entry fix in operator terms; switch-table row; expanded log description with the copy-before-relaunch warning; upgrade-checklist step to run -tident once after upgrading. * CLAUDE.md: STEP 12 with the full engineering record, including the three-way numbering table and both causes. * OPTIONS-INI.md: videodriverindex note now points at -tident. Cost ---- -tident is opt-in and exits immediately after. The desktop-order block is a handful of extra startup writes. Nothing added is reachable from the frame loop. Testing ------- -tmon 1,4,2,3 confirmed working on the reporting machine, which validates the diagnosis. The -tident and re-entry changes are NOT yet built or run; both need a rebuild of MW4.exe (Release + Profile) as they touch CoreTech GameOS. Worth checking on the W4100 bench first that CreateSurface(PRIMARYSURFACE) under DDSCL_NORMAL succeeds on secondary devices through dgVoodoo2 - if a panel comes up blank the log names the failing call. Co-authored-by: Claude Opus 5 (Anthropic) <noreply@anthropic.com> Co-authored-by: GitHub Copilot <copilot@github.com>
This commit is contained in:
co-authored by
Claude Opus 5
GitHub Copilot
parent
2e85cd2066
commit
4e04fd1fb2
@@ -1167,6 +1167,91 @@ Every hypothesis reasoned forward from the code was wrong again (blit erasing ov
|
||||
channel; per-GPU rendering). The `-tmr` ladder plus the draw-call counter settled it in two runs.
|
||||
Consistent with the rest of STEP 10: **instrument, don't infer.**
|
||||
|
||||
## STEP 12: Intel iGPU + USB display MFD failure — was `-tmon` misread + a real
|
||||
## panel re-entry leak (2026-08-05)
|
||||
|
||||
Reported as *"MFD modes work on a W4100 and a Quadro, and with the radar on a USB adapter, but
|
||||
fail on a machine with CPU-integrated Intel graphics"* — 3 displays (main + 2 MFDs) on an
|
||||
Intel HD 630, radar on a Trigger 6 USB adapter, Win10 + dgVoodoo2. Second MFD panel returned
|
||||
`DDERR_EXCLUSIVEMODEALREADYSET`. **Neither the Intel iGPU nor the mixed-adapter setup was at
|
||||
fault.** Two independent causes, both now fixed/instrumented.
|
||||
|
||||
### Cause 1 — Windows Display Settings numbers are NOT the DirectDraw device order
|
||||
The operator set `-tmon 3,4,2,1` by reading the numbers off the Windows Display Settings
|
||||
arrangement diagram. Those numbers are an **undocumented UI ordinal that no API exposes** and
|
||||
that need not match `\\.\DISPLAYn` *or* the DirectDraw index. On this box all three differed:
|
||||
|
||||
| Settings | `\\.\DISPLAYn` | DDraw device | desktop X | role wanted |
|
||||
|---|---|---|---|---|
|
||||
| 3 (primary, 800x600) | DISPLAY1 | 0 | 0 | main |
|
||||
| 4 | DISPLAY4 | 3 | 800 | radar |
|
||||
| 2 | DISPLAY2 | 1 | 1440 | mfd1 |
|
||||
| 1 | DISPLAY3 | 2 | 2080 | mfd2 |
|
||||
|
||||
A permutation with two accidental fixed points — no derivable rule, so it cannot be corrected
|
||||
in code. `-tmon 1,4,2,3` (correct DirectDraw indices) worked first try, user-confirmed.
|
||||
**This is why Windows itself ships an *Identify* button rather than publishing the mapping.**
|
||||
|
||||
### Cause 2 — panels were re-opened without being released (REAL BUG, all machines)
|
||||
`EnterFullScreenMode()` (`DXRasterizer.cpp:1132`) calls `HSH_EnterFullScreen2()` on **every**
|
||||
mode change and every lost-front-buffer recovery, but the teardown `HSH_DirectDrawRelease2()`
|
||||
was only wired to `DirectDrawRelease()` — i.e. full shutdown. So `CHSH_Device::InitFirst()`
|
||||
overwrote `pDD` with a fresh `IDirectDraw7` while the previous one still held exclusive
|
||||
fullscreen, **leaking it and its exclusive claim for the life of the process**.
|
||||
- The log shows it plainly: `HSH_EnterFullScreen2` runs **twice**; the first entry initialises
|
||||
all panels `DD_OK`, the second fails on the same device.
|
||||
- Only bites when a panel sits on the **Windows primary** — secondary outputs grant exclusive
|
||||
mode again, the primary does not. Every working pod happens to have the *main display* on the
|
||||
primary, so no panel is ever there. That is the whole reason it looked hardware-specific.
|
||||
- **FIX:** `render.cpp` `HSH_EnterFullScreen2()` now calls `HSH_DirectDrawRelease2()` first when
|
||||
`hsh_initialized || hsh_mrdev_initialized`. Tagged `[panelreinit]`.
|
||||
|
||||
### ✅ `gos-displays.txt` rewritten as a full startup trace (`VideoCard.cpp`)
|
||||
Was: final device list + role numbers. Now: every stage in the order the game does it —
|
||||
command line (with `-tmon` shown already converted 1-based→0-based), **Windows desktop topology**
|
||||
(`EnumDisplayDevicesA`/`EnumDisplaySettingsA`), each enumeration callback with its GUID and
|
||||
`HMONITOR` resolved to `\\.\DISPLAYn` + rect + primary flag, per-device ACCEPTED/REJECTED, the
|
||||
device table **before and after** the NULL-device merge with `DeviceGUID` **and**
|
||||
`guidDeviceIdentifier` and an **adapter grouping**, every index shift in the merge, every
|
||||
role-selection decision *with the reason each device was skipped*, each `-tmon` override
|
||||
(applied/rejected + what auto-detect had chosen), a **desktop left-to-right order** block, and a
|
||||
**consistency check that names two roles landing on one device as a CLASH**.
|
||||
- **Fixed a real desync while doing it:** the merge shifted `DeviceArray` but nothing tracked
|
||||
which monitor each slot drove → `g_ahDevMonitor[]` is now shifted in lockstep. `HMONITOR` was
|
||||
previously discarded entirely (`videoDevices` gained `hMonitor`; `BufferDevice` takes it).
|
||||
- Startup-only, ~255 open-append-close writes (~25–130 ms), nothing on the frame path. Kept
|
||||
unbuffered deliberately — this is the crash-survivable log (`gos-fps.txt` is the buffered one).
|
||||
- APIs resolved via `GetProcAddress` with locally-declared `MONITORINFOEXA`/`DISPLAY_DEVICEA`
|
||||
layouts, so nothing depends on the 1998 SDK having multimon headers.
|
||||
|
||||
### ✅ NEW: `-tident` — the game's own Identify
|
||||
`MW4.exe -tident [3..120, default 20]` fills **every** display with a distinct colour and prints,
|
||||
huge, the number to type into `-tmon`, plus its DirectDraw device index and the role currently
|
||||
assigned to it. Then exits. This is the only reliable way to map monitors, precisely because the
|
||||
Settings ordinal is unreadable.
|
||||
- **Uses `DDSCL_NORMAL` + GDI on the primary surface — no exclusive mode, no mode change.**
|
||||
Taking exclusive fullscreen on several devices at once is the very failure being diagnosed; a
|
||||
diagnostic that trips over it is worthless. It paints onto each monitor's existing desktop, so
|
||||
it works even on a pod where the MFD modes are broken.
|
||||
- Opens the **real DirectDraw devices** rather than positioning GDI windows by `HMONITOR` — that
|
||||
proves the device-index→physical-output association through the same path the panels use.
|
||||
(Positioning by DirectDraw's own reported `HMONITOR` would be circular.)
|
||||
- Implemented in `VideoCard.cpp` `IdentifyDisplays()`, called at the end of `FindVideoCards()`
|
||||
then `ExitProcess(0)`. Everything it does is also written to `gos-displays.txt`.
|
||||
|
||||
### Docs
|
||||
`-help` for `-tmon` now states outright that these are **not** Windows Display Settings numbers
|
||||
and points at `-tident`/`gos-displays.txt`; the old `-tmon 1,2,3,4` example was actively inviting
|
||||
the mistake. Release notes gained a `-tident` section, the clash/re-entry fixes, and an upgrade
|
||||
checklist step.
|
||||
|
||||
### Method note
|
||||
Same lesson as STEPs 10 and 11, and it caught me out again: my forward-reasoned hypothesis
|
||||
(duplicate `-tmon` assignment) was **wrong**, and the new logging disproved it in one run — the
|
||||
consistency check explicitly printed *"No duplicate device assignments"*. The double
|
||||
`HSH_EnterFullScreen2` was only visible because the log covers the whole start-up rather than a
|
||||
summary. **Instrument, don't infer.**
|
||||
|
||||
## Next steps (proposed)
|
||||
- [ ] (Decision pending) Borderless-windowed migration — see the staged assessment in STEP 10.
|
||||
Step 2 (one panel) is the cheap, decisive experiment. Note this is the only route that would
|
||||
|
||||
Reference in New Issue
Block a user