From 24fb22ca80e639764ccf6f4245097ee957b9a88e Mon Sep 17 00:00:00 2001 From: James Dansereau <106631954+jkdansereau@users.noreply.github.com> Date: Tue, 15 Sep 2026 23:44:07 -0400 Subject: [PATCH] D243: live playthrough decouples shake from D146; add GE_D243CAM probe Live Dam abseil cutscene captures (2 runs, both diagnostic probes on): the visible camera shake reproduces every time, but the D146 stale-memory burst fired on neither run -- the two symptoms M-107/M-109 treated as related are actually independent. Added an env-gated GE_D243CAM probe to bondviewFrozenCameraTick's pad-orbit CAMERAMODE_POSEND branch to check for g_GlobalTimerDelta jitter; zero hits rules out that branch entirely. The Dam cutscene uses one of the other two CAMERAMODE_POSEND sub-paths (g_CameraLookAtBondPad or gBondViewCutscene), neither instrumented yet -- next session's concrete next step per docs/HANDOFF.md. Claude-Session: https://claude.ai/code/session_01KwZViAFaXua3PKgHHjNgvi --- docs/dev/findings-index.csv | 2 +- docs/dev/findings.md | 4 +++- src/game/bondview2.c | 21 +++++++++++++++++++++ 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/docs/dev/findings-index.csv b/docs/dev/findings-index.csv index 6246e918..86f532aa 100644 --- a/docs/dev/findings-index.csv +++ b/docs/dev/findings-index.csv @@ -139,7 +139,7 @@ D239,?,"QoL ask (M-106, user QA):",CLOSED — executed in the v0.2.0 prep pass ( D240,OPEN,"Player gunshot SFX timing/cadence reportedly does not match the N64 original's rate (M-106, user QA report).","OPEN — not root-caused; static call-site tracing only, no runtime verification possible in this environment." D241,OPEN,Player gunshot SFX goes silent under some conditions —,"OPEN — not newly root-caused; high confidence this is D207 reproduced, not an independent bug." D242,?,Player-death sequence UX regression vs N64 original:,NOT A PORT-LOGIC BUG as far as static analysis can tell. -D243,OPEN,"Dam end-of-level cutscene (the abseil/bungee sequence, D148/D160's subject):",OPEN — M-109 re-analysis of the same session's raw logs corrects the timing/pattern claims above; the D134/pendingGfx race hypothesis is likely wrong. +D243,OPEN,"Dam end-of-level cutscene (the abseil/bungee sequence, D148/D160's subject):","OPEN — M-142 live playthrough (2 runs, both probes on):" D245,OPEN,IsWater water: two competing patterns with a visible seam that moves as the player looks around (mouse/aim) —,"OPEN — REOPENED, M-142 headless probe found the tc-shift k measurably flips during ordinary view rotation (new root-cause evidence); an opt-in GE_D245_FIXEDSHIFT candidate is build-verified but needs…" D246,OPEN,"Tiny pixel strips at the left and right edges of the screen, ""like overscan"" —","OPEN — one candidate ruled out (M-116 follow-up), root cause not found." D247,RESOLVED,Black bars top and bottom of screen —,"NOT A BUG — confirmed faithful, unmodified N64 decomp behaviour, no code change made." diff --git a/docs/dev/findings.md b/docs/dev/findings.md index 046a9fc4..871fee92 100644 --- a/docs/dev/findings.md +++ b/docs/dev/findings.md @@ -546,7 +546,7 @@ covers D24–D69; the log continues in §H (D32 procedure, D70–D121). | D240 | **Player gunshot SFX timing/cadence reportedly does not match the N64 original's rate (M-106, user QA report).** — full `## D240` entry at file tail | **OPEN — not root-caused; static call-site tracing only, no runtime verification possible in this environment.** Confidence LOW-MEDIUM that this is a *new* bug distinct from the D155/D186/D193/D204/D209 timing-keystone family (all of which gate the identical `deltaFrames→speedgraphframes→g_ClockTimer→g_GlobalTimer` chain this SFX trigger consumes).… | | D241 | **Player gunshot SFX goes silent under some conditions — reported on Bunker with the alarm sounding, "or some other factor" — but only for PP7 and AK47, Klobb…** — full `## D241` entry at file tail | **OPEN — not newly root-caused; high confidence this is D207 reproduced, not an independent bug.** Recommend folding this repro into D207 (or closing D241 as a duplicate once D207 is reopened) rather than tracking separately.… | | D242 | **Player-death sequence UX regression vs N64 original: PC requires an LMB click to proceed to the mission-failed/file-select screen; user recalls N64 auto-plays…** — full `## D242` entry at file tail | **NOT A PORT-LOGIC BUG as far as static analysis can tell.** Confidence HIGH that the LMB-click skip path is faithful original behavior (Z_TRIG-mapped, exists unmodified in `bondview2.c`), not a defect to remove.… | -| D243 | **Dam end-of-level cutscene (the abseil/bungee sequence, D148/D160's subject): the camera violently shakes/"spazzes" during each of the scripted camera-angle…** — full `## D243` entry at file tail | **OPEN — M-109 re-analysis of the same session's raw logs corrects the timing/pattern claims above; the D134/pendingGfx race hypothesis is likely wrong.** Confidence MEDIUM on the correction, LOW-MEDIUM on the new lead.… | +| D243 | **Dam end-of-level cutscene (the abseil/bungee sequence, D148/D160's subject): the camera violently shakes/"spazzes" during each of the scripted camera-angle…** — full `## D243` entry at file tail | OPEN — M-142 live playthrough (2 runs, both probes on): shake reproduces every time, D146 burst fired on NEITHER run (decouples the two symptoms). Camera path narrowed to 2 of 3 `bondviewFrozenCameraTick` branches (orbit-fallback ruled out via a new `GE_D243CAM` probe, zero hits); next session instruments the remaining two.… | | D245 | **IsWater water: two competing patterns with a visible seam that moves as the player looks around (mouse/aim) — "looks almost exactly like skybox" (M-116, user…** — full `## D245` entry at file tail | OPEN — REOPENED, M-142 headless probe found the tc-shift `k` measurably flips during ordinary view rotation (new root-cause evidence); an opt-in `GE_D245_FIXEDSHIFT` candidate is build-verified but needs a live A/B, not shipped as default.… | | D246 | **Tiny pixel strips at the left and right edges of the screen, "like overscan" — look like raw pixels, not 3D graphics (M-116, user QA report).** — full `## D246` entry at file tail | **OPEN — one candidate ruled out (M-116 follow-up), root cause not found.** Step (1) from the original plan is done… | | D247 | **Black bars top and bottom of screen — possible aspect-ratio defect (M-116, user QA report).** — full `## D247` entry at file tail | **NOT A BUG — confirmed faithful, unmodified N64 decomp behaviour, no code change made.** `GE_PCDUMP` captures (both against the pre-existing save and a fresh-EEPROM run, to rule out a stale `Screen` option from earlier test sessions) show the exact same inset on Frigate… | @@ -10697,6 +10697,8 @@ crash class C2 (Facility `-level_34`, Runway `-level_35`): `import_texture_i8` A **Dam end-of-level cutscene (the abseil/bungee sequence, D148/D160's subject): the camera violently shakes/"spazzes" during each of the scripted camera-angle cuts (M-107, live `GE_D160=1` trace, two interactive `-level_33` runs + one fresh-save normal-progression run).** **First, corrects D160's own premise: the cutscene is NOT skipped on the current build.** Both debug-loaded runs show exactly the AI-script-scripted sequence firing: `bondviewSetCameraMode(7)` (`CAMERAMODE_POSEND`, the cutscene camera-lock state) called 2-3 times, matching `ai_17`'s three `camera_switch` commands in `UsetupdamZ.c`'s abseil ailist (`assets/obseg/setup/UsetupdamZ.c:2656`+), followed by `bossReturnTitleStage`. So D160's original (b)/(c) hypotheses (AI trigger never fires / puppet renders invisibly) are **refuted on this build** — the mechanism runs; what's broken is what it renders. **Root cause candidate, directly observed:** right in the camera-cut window, fast3d's D146 catch-all (`port/fast3d/gfx_pc.cpp:3037`, "unknown GBI opcode … ending DL") fired a tight burst — same 4 memory addresses repeating every frame for ~10 frames, then clean — with payload bytes `0xfafbfbfcfcfbfdfc`/`0xfcfafbfbfafbfafb`: a **freed/uninitialized-memory fill pattern**, not real GBI commands. This reads as a stale-pointer display-list read, not a decode bug — some segment/scene buffer backing the outgoing camera shot's geometry got freed or recycled while a still-in-flight display list (queued ahead in the port's double-buffered render pipeline — see D134's "pendingGfx" note, `port/src/libultra.c:733-746`, on how many frames of latency the sched round-trip can carry) still referenced it. **Independent evidence of a race, not a fixed-order bug:** the two `-level_33` debug-load runs show the camera_switch(7) calls landing in a *different order relative to* `bossReturnTitleStage` between runs (run 1: three switches, then the stage-return call; run 2: two switches, the stage-return call, *then* a third switch arriving after) — and the D146 corruption burst appeared in run 1 but not run 2 at all, non-deterministically. That pattern (symptom present/absent, call order shuffling) is the signature of a genuine timing race between the AI-list's cutscene commands and the level-exit/report-transition code, not a deterministic logic bug. **User-confirmed on real (non-debug-boot) play, not just the debug harness:** a fresh-save normal playthrough into Dam still hit the camera glitch on this same cutscene — ruling out "artifact of the `-level_33` debug boot" for this specific symptom (unlike the secondary "no report screen, straight to file-select" symptom seen only on the debug-loaded runs, which did NOT reproduce on the fresh-save run and is therefore most likely a `-level_N` debug-boot artifact per the D190 precedent, not filed as its own bug here). +**M-142 update: live playthrough with both probes on, real signal, still not root-caused.** User played Dam to the abseil cutscene twice this session with `GE_D243=1 GE_D160=1` set (first run), then again with a new `GE_D243CAM` probe added. **Key new fact: the visible "violent shake" symptom reproduced on BOTH runs, but the `D146` stale-memory burst fired on NEITHER run** (log captured well past the historical 1600-3900-frame window both times, zero `D146:` lines) — this decouples the two symptoms M-107/M-109 had been treating as related. The shake is real, reproduces every time, and has nothing observable to do with the D146 mechanism; whatever drives it must be a silent bad-value computation (camera position/angle math), not a logged error. **Camera code path narrowed (partially):** `bondviewFrozenCameraTick`'s `CAMERAMODE_POSEND` branch (`bondview2.c:1537+`) has three sub-paths — `g_CameraLookAtBondPad` look-at, a `gBondViewCutscene`-struct spherical-angle path, and a pad-orbit fallback (`flt_CODE_bss_80079A00` angle accumulated by `g_GlobalTimerDelta` each tick). Added a `GE_D243CAM` probe (`#ifdef PORT`) to the orbit-fallback branch to check whether `g_GlobalTimerDelta` jitter there explains the shake — **it fired zero times**, meaning the Dam abseil cutscene does NOT use that branch; the real driver is one of the two earlier `return`-before-my-probe branches (`g_CameraLookAtBondPad` or `gBondViewCutscene`), not yet instrumented. Next session: add the same style of probe to those two branches (log `pos`/`pos2`/`gBondViewCutscene->theta,verta` every call) and re-capture — same repro (Dam, Agent, to the abseil cutscene, `GE_D160=1` to confirm mode(7) still fires 3x) works reliably and takes only a few minutes end to end. + **Status (from §F table):** **OPEN — M-109 re-analysis of the same session's raw logs corrects the timing/pattern claims above; the D134/pendingGfx race hypothesis is likely wrong.** Confidence MEDIUM on the correction, LOW-MEDIUM on the new lead. Re-reading `data/playtest_d160.log`/`_run2.log` at the `g_framesRendered` level (not just grepping for `D146`) shows the D146 burst fires **~1600-3900 frames (tens of real seconds) *after*** `bossReturnTitleStage`/the `camera_switch(7)` calls, not during them, in **both** runs — "right in the camera-cut window" and "run 1 only" are both wrong as stated above. This also weakens the D134/`pendingGfx` citation: `osSpTaskStartGo` runs `gfx_run()` synchronously and inline (`port/src/libultra.c:1232-1258`), so nothing from the cutscene's own DL submission is still in flight by the time any teardown code runs — the port doesn't have the async-RSP race the hypothesis above assumes. **New lead:** whatever screen renders in that multi-thousand-frame gap after mission-complete is the more likely site — probably `MENU_MISSION_COMPLETE` (`front.c:7267`, `constructor_menu0D_missioncomplete`) or an adjacent briefing/file-select screen in that chain, same general class as D75 (front-end model absence) / D141 (menu bad-pointer bug), not a cutscene-render race. Of the 4 repeating fault addresses, 3 read as literal zero words and only 1 matches the `0xfafbfbfc…` pattern, arguing against a deliberate poison-fill (none found anywhere in `port/`) and for either genuinely-unwritten memory or a decode landing inside unrelated valid data. Next: an env-gated `front.c` menu-transition trace (`MENU_*` id + `g_framesRendered` on every transition after `bossReturnTitleStage`) plus a `GE_PCDUMP` window over frames ~4500-8000 (not the camera-cut window) to name the exact screen/segment. Cross-ref D160 (this supersedes its stale "cutscene skipped" framing with a "cutscene renders corrupted" one, now further corrected to "corruption isn't in the cutscene render at all") and D173 (Cradle's Bond-floating/spinning cutscene bug — same subsystem family, unconfirmed shared root cause). **M-120: the `GE_D243` trace above was BUILT (statically, i.e. written) on branch `investigate/d243-cutscene-race` (`764a1f26`, pushed, not merged) — `src/game/front.c` only, `#ifdef PORT`, logs the target `MENU_*` name + a local per-tick counter on every `menu_init()` state transition. NOT build-verified: two independent sessions (the dispatched subagent, then this session re-verifying in a fresh worktree) both hit the same reproducible failure — `cmake`'s C-compiler ABI check fails with "Cannot create temporary file in C:\Windows\: Permission denied" when gcc is invoked *through* CMake/ninja, even though a bare `gcc -c` invocation from the same shell, same session, succeeds and correctly resolves its temp dir to `%TEMP%`. This is a genuine, reproducible environment issue distinct from the diff's content (static review of the diff found nothing wrong: enum cases all verified to exist in `bondconstants.h`, brace/`#ifdef` balance checked, follows the existing `GE_D160`/`GE_D178` env-gated trace idiom used elsewhere in the same file) — worth a session with a working toolchain investigating the CMake/ninja-vs-bare-gcc environment discrepancy itself as its own item, separate from D243. Next: build `investigate/d243-cutscene-race` on a session where `./build-pc.sh` actually completes, then run the `-level_33` Dam repro with `GE_D243=1` (and the existing `GE_D160`/`GE_PCDUMP` mechanisms) to correlate the trace against the D146 burst's frame range. **M-141: the M-120 toolchain blocker is EXPLAINED and worked around, GE_D243 is now build-verified.** The "Cannot create temporary file in C:\Windows\" failure isn't specific to CMake/ninja vs. bare `gcc -c` — it's this Claude Code session's `Bash` tool shell (Git-for-Windows Bash, not a real MSYS2 MINGW64 window) not exposing a usable `TMP`/`TEMP` to child processes the way an interactive MSYS2 shell does; a bare `gcc -c` from the same shell happened to not hit the temp-file path M-120 tested, which is why it looked clean while the full build wasn't. Fix: explicitly `export TMP=... TEMP=...` (Windows-style path) alongside the already-known `PATH=/c/msys64/mingw64/bin:/c/msys64/usr/bin:$PATH` fix, both before invoking `./build-pc.sh`/`ninja`/`cmake` — now also recorded in `porting-notes.md` §E as a general session-start gotcha, not just a D243-specific note. Cherry-picked the `GE_D243` trace commit (`764a1f26`) cleanly onto `release/v0.3.0` (branch `investigate/d243-fresh`), fixed one thing the original diff got wrong per this project's own D250 rule (`menu_init()` runs ~once/frame during front-end states — the diff's three `getenv("GE_D243")` calls were uncached; now a single cached `static int s_d243` check), and confirmed it builds clean. **Smoke-tested, not fully exercised:** a bare `-level_33` boot runs 1100+ frames with zero crashes and (correctly) zero `D243:` log lines — bare `-level_XX` skips the front end entirely (per this doc's own M-24-era note), so `menu_init()` never runs; this only proves the probe doesn't regress normal boot, not that it captures anything useful yet. **Still blocked on the same thing M-107/M-109/M-120 all were: an actual interactive playthrough** (main menu → Dam → abseil cutscene → mission-complete → whatever screen the D146 burst fires on, ~1600-3900 frames later) with both `GE_D243=1` and `GE_D160=1` set, which needs a human with input, not available in this session. **Possible connection to D173 worth checking in that same playthrough** (per user, M-141): D173 (floating/spinning Bond puppet in cutscenes, e.g. Cradle/Trevelyan) and D243 (stale-memory DL read some seconds after a cutscene-driven `bossReturnTitleStage`) both sit in the scripted-cutscene/level-transition neighborhood and both ultimately touch "Bond's (or a puppet's) in-world model" — no code-level link found this session (D173's own M-106 pass already ruled out the two most likely struct-aliasing sites, and D243's mechanism looks like a stale/freed buffer read in a *different* subsystem, front-end/menu memory rather than a chr/puppet transform), so treat this as an unconfirmed hypothesis to keep in mind while capturing the next live trace, not an established shared root cause. **Steam Deck playtest confirmation, same M-141 session** — user independently reported "cutscene at end of dam level is badly glitched" without prompting, matching this row's own "camera violently shakes" symptom exactly. Second independent confirmation this is real and reproducible, not a one-off; still needs the interactive `GE_D243=1`+`GE_D160=1` capture this row already calls for. ## D244 — Self-hosted CI runner (`corpyvt6agent01`) link failure: `undefined reference to 'objectiveGetStatus_WEAK'` (2026-09-12, first-ever build attempt on the… diff --git a/src/game/bondview2.c b/src/game/bondview2.c index d9614756..25f67f23 100644 --- a/src/game/bondview2.c +++ b/src/game/bondview2.c @@ -1616,6 +1616,27 @@ void bondviewFrozenCameraTick(u16 buttons, u16 oldbuttons, struct coord3d *pos, pos->f[1] = setupPad->pos.f[1] + flt_CODE_bss_80079A10 + flt_CODE_bss_80079A0C; pos->f[2] = setupPad->pos.f[2] + (cosf(flt_CODE_bss_80079A00) * flt_CODE_bss_80079A08) + sinf(flt_CODE_bss_80079A00) * 0.0f; +#ifdef PORT + /* D243 (M-142): the abseil/end-of-Dam cutscene's camera is this + * orbit -- a fixed look-at (pos2, above setupPad) with the eye (pos) + * circling it at radius flt_CODE_bss_80079A08, angle accumulated by + * flt_CODE_bss_80079A04 * g_GlobalTimerDelta each tick. User-confirmed + * live (M-142): the visible "violent shake" reproduces even on a run + * where the separate D146 stale-memory burst never fires at all -- + * decouples the two symptoms M-107 had been treating as related. + * This dumps the angle delta and computed eye position every call so + * a live capture can show whether g_GlobalTimerDelta (the D155/D193 + * wall-clock sim-tick count, normally clamped to <=6 but not + * necessarily steady at 1) is jittering frame-to-frame in a way that + * would show up as this exact judder. */ + if (getenv("GE_D243CAM")) { + osSyncPrintf("D243CAM: dt=%.4f angle=%.4f pos=%.2f,%.2f,%.2f pad=%d\n", + (double) g_GlobalTimerDelta, (double) flt_CODE_bss_80079A00, + (double) pos->f[0], (double) pos->f[1], (double) pos->f[2], + (int) dword_CODE_bss_80079A14); + } +#endif + flt_CODE_bss_80079A00 += flt_CODE_bss_80079A04 * g_GlobalTimerDelta; while (flt_CODE_bss_80079A00 >= M_TAU_F)