Diag: add BT_SHOT backbuffer dump + correct/park the -net render glitch (task #53)

Scouted the "-net node render glitch". Two prior framings were wrong and are
corrected in the KB: it is NOT device contention (retracted earlier), and it is
NOT a from-load glitch -- the trigger is a RESPAWN (user correction). It's a
post-respawn render-rebuild fault on the MP path.

Could not reproduce in 9 automated 2-node loads + a forced death/respawn (all
clean). Debug uninit is a deterministic 0xCDCDCDCD, so the intermittency implies
a race -- and the task #52 respawn fixes (LoadMission re-entry, viewpoint relink,
death-latch) plausibly already closed it. Parked as WATCH; re-open if it recurs.

Tooling (general, off by default): BT_SHOT=<path> dumps a per-instance backbuffer
PNG from the world-render path (L4VIDEO.cpp) every 90 frames -- lets us capture a
specific node's frame without foregrounding the window. Used with
scratchpad/hunt53.sh (N-run divergence hunter) to verify a fix if the glitch
returns.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
arcattack
2026-07-10 15:48:54 -05:00
co-authored by Claude Opus 4.8
parent 0bfb3d4ab3
commit 7b0b4f64ab
2 changed files with 52 additions and 13 deletions
+24 -13
View File
@@ -37,19 +37,30 @@ all connect. The **CONSOLE** (operator station — ABSENT from every archive) de
emulator** (⚠ `NotationFile::ReadText` expects NUL-SEPARATED lines). [T2]
## Verified milestones (one box, two instances)
-**`-net` NODE RENDER GLITCH — OPEN (observed live 2026-07-09, cause NOT yet found):** in a 2-node
session ONE window comes up visually GLITCHED FROM LOAD (before any death) — shadow clipping, camera
stuck inside/blocked, targeting/firing feels dead — while the OTHER is fine. **Isolated by test, not
theory:** (a) a SOLO instance is perfect; (b) **TWO SOLO instances (no `-net`) are BOTH perfect**
(each logs `MECH under boresight … IN WEAPON RANGE`, no device fallback/error, both hardware HAL
devices). So it is **NOT** a two-D3D9-apps-on-one-GPU / device-contention issue (an earlier note
claimed that — WRONG, retracted) and NOT the warp/respawn (from-load, pre-death). It is **specific
to the MP `-net` path** — the console-driven mission-start ladder / replication doing something to
ONE node's render+input state, asymmetrically (the two nodes differ in launch order, affinity,
net port, BT_MP_NET, BT_MP_FORCE_DMG). CAUSE UNKNOWN — next step: determine WHICH node glitches
(A@1501 vs B@1601) and correlate, then audit the MP viewpoint/camera/HUD setup vs the (clean) solo
path. Does NOT affect the pod (one instance per machine). Dev workflow: trust the SOLO window for
visual/gameplay checks; the 2-node logs are still trustworthy for replication/netcode logic.
-**`-net` node render glitch — LIKELY ALREADY FIXED / WATCHING (task #53):** a 2-node MP session
once showed ONE window come up visually wrong — shadow clipping, camera stuck inside/blocked,
targeting/firing dead — while the other was fine. Two earlier framings were WRONG and are corrected:
(i) the "two-D3D9-apps-on-one-GPU / device contention" theory was retracted — a solo instance, AND
two solo instances, are all clean, so it is not device contention; (ii) "glitched FROM LOAD, before
any death" was ALSO wrong — **the trigger is a RESPAWN** (user correction, 2026-07-10). So it is a
**post-respawn render-rebuild fault on the MP path**, not a load-time one. Does NOT affect the pod
(one instance per machine).
- **What the respawn rebuild does (reproduced 2026-07-10):** on respawn the player's mech is rebuilt
via `MakeEntityRenderables`, which runs a TWO-STEP view sequence — inside skeleton + `COCKPIT
eyepoint`, then outside skeleton + `external chase` (`[view] …` logs, btl4vid.cpp:2158) — and the
external chase camera is NOT re-installed (only ONE `[BTrender] external debug chase camera
installed`, btl4vid.cpp:648, for the whole session despite multiple respawns). Plausible failure:
if that two-step completes only to the COCKPIT step (raced/interrupted on the `-net` ladder), the
node is left stuck INSIDE — body hidden by the inside skeleton, shadow/reticle flagging with it =
the reported bundle. Camera + reticle + view + `tshd` shadow are all gated on `buildDebugChaseCamera
= (view_type == insideEntity)` (btl4vid.cpp:297).
- **Status: could NOT reproduce (2026-07-10) → likely already fixed.** 9 automated 2-node loads PLUS a
forced death/respawn all came up CLEAN (the respawn completed to external chase; clean backbuffer via
the new `BT_SHOT` dump). Since debug uninit is a deterministic `0xCDCDCDCD` (would glitch every run),
the intermittency implies a **race**, and the task #52 respawn fixes (the `LoadMission` re-entry
killer bug, viewpoint relink, death-latch) plausibly ALREADY closed it. **Left as WATCH** — re-open
and capture with `BT_SHOT` if it recurs. Tools kept: **`BT_SHOT=<path>`** (per-instance backbuffer
PNG, off by default, L4VIDEO.cpp) + `scratchpad/hunt53.sh` (N-run divergence hunter).
- **P6 smoke test:** two instances share a world — console egg → TCP mesh → synchronized start →
bidirectional entity replication (each renders its own mech + the peer's REPLICANT), 0 crashes.
- **Movement replication:** A's mech WALKS on B's screen (the replicant tracks the master to ~1.5u via
+28
View File
@@ -7798,6 +7798,34 @@ void DPLRenderer::ExecuteImplementation(RendererComplexity, RendererOrigin::Inte
BTDrawTranslocationSpheres(mDevice, &viewTransform, (float)dT, mTargetRenderTime);
}
// DIAG (off by default): BT_SHOT=<path> dumps this instance's backbuffer (the rendered
// WORLD, before the 2D HUD overlay) to a PNG once ~180 frames in, for non-disruptive
// per-instance frame capture without foregrounding the window. Each instance sets its
// own path. Built for the -net render glitch (task #53) but general.
{
const char *shotPath = getenv("BT_SHOT");
if (shotPath)
{
static int s_btShotN = 0;
++s_btShotN;
// dump (overwriting) every 90 frames once past 90, so the file always holds a
// recent frame regardless of load timing / debug fps.
if (s_btShotN >= 90 && (s_btShotN % 90) == 0)
{
IDirect3DSurface9 *bb = 0;
if (SUCCEEDED(mDevice->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &bb)) && bb)
{
HRESULT hr = D3DXSaveSurfaceToFileA(shotPath, D3DXIFF_PNG, bb, 0, 0);
bb->Release();
DEBUG_STREAM << "[btshot] frame " << s_btShotN << " -> " << shotPath
<< " hr=0x" << std::hex << (unsigned)hr << std::dec << "\n" << std::flush;
}
else
DEBUG_STREAM << "[btshot] GetBackBuffer FAILED frame " << s_btShotN << "\n" << std::flush;
}
}
}
// The BT targeting reticle / weapon pips (2D screen space, cockpit view
// only -- the dpl2d layer; see game/reconstructed/dpl2d.cpp).
{