Commit Graph
69 Commits
Author SHA1 Message Date
James Dansereau a77bd4e03d D955: at 90+ fps, object motion and the sky are blended between ticks
Guards, doors and props moved at the 60 Hz tick inside a 90 Hz picture. Each object's joint
matrices are now blended, keyed by (Model*, joint) from a game-thread snapshot and rebased onto
the new camera; a chr blends together with its held weapons and hat; room-origin changes, failed
gates and split-screen stay exact. The CPU-projected sky is re-projected to the in-between camera.
Destroyed objects (world-space matrices) stay exact. Port layer only; porting-notes D46.
2026-10-13 22:09:22 -04:00
James Dansereau b7280e5386 D954: at 90+ fps, props (guards, doors, objects, effects) follow the blended camera
In-between presents drew props at the tick's camera while rooms used the blended one, so props
jumped against the world whenever the camera moved (doors and guards flickering at 90 fps, never
at 60). The props' plain perspective now gets the room camera blend (P * R^-1 * Rb) from
bgLevelRender up to the first-person gun. Port layer only (gfx_pc.cpp, interpportal.c).
2026-10-13 22:09:22 -04:00
James Dansereau 2ff406b9a8 GE-ENV-PROBES: rows for the v0.6.0 interpolation, Deck harness and macOS probes 2026-10-10 12:16:27 -04:00
James Dansereau 4b710728bf v0.5.0 playtest fold-in: fog and near-plane clip, crash fixes, polish (D540-D564)
Deck/Windows playtest fixes: fog rework (D540) and RSP-style CPU near-plane
clip with per-vertex N64 fog (D543), with fog recomputed on near-plane-clipped vertices as the RSP does,
so the haze around the player stays steady (D553); colour-combiner clamp for banded walls
(D548); rocket and thrown-item stack overflows (D545, D549); menu
double-trigger mitigation (D541); Dam ending first-shot camera swivel
restored by removing an old port look-at workaround (D552); settings standardisation (D546), live
All unlocked (D547), F10 mouse (D544); static window title and the project
mark (the in-game crosshair shape, site gold) as window/taskbar icon and
favicon (D550). Reference frames re-based
on Windows, Linux and Steam Deck (A/B-confirmed); capture_p7.sh runs on
Windows. Opt-in update check, off by default (D551: one HTTPS request to
GitHub per launch when enabled; winhttp.dll loaded only then; the tag CI
job checks the tag against GE007_VERSION). Release notes, ROADMAP,
internals and security docs updated; tree-hygiene pass.

Refs: D540 D541 D543 D544 D545 D546 D547 D548 D549 D550 D551 D552 D553 D554 D555 D556 D557 D558 D559 D560 D561 D562 D563 D564
2026-10-06 18:35:44 -04:00
James Dansereau d7b9371afb Docs: public copy, site presentation, release-gate review (D533, D534)
Public-copy language pass, homepage restructure and Documentation rename,
404 page, dead-anchor and permalink fixes, release-gate sign-off, and the
pre-release review fixes of the 2026-10-05 batch (probe-doc drift,
build-path hygiene, verify.sh and capture safety).

Refs: D533 D534
2026-10-06 18:35:44 -04:00
James Dansereau aa5a1b81fe v0.5.0: settings UI, control styles, save import, documentation
PD-style F10 menu with six groups, checkbox/slider/dropdown controls (D504),
wording pass and real-unit sliders (D505-D509), overlay/FPS-counter window
scaling (D510, D512), small settings (D511), control scheme Ext/Original
(D513) with the game's own N64 styles (D516-D518), emulator-format save
import on first launch (D514), build arch tag (D515), converter argv quoting
and sidecar size check, one PC options UI (D519) with by-eye follow-ups,
antialiased overlay sliders (D520), README settings paths, ROADMAP trim,
D441 tier-1 all-level sweep (21/21 pass).

Refs: D503-D520 D349 D441
2026-10-06 18:35:43 -04:00
James Dansereau daa2522511 D489-D502: settings persistence, collision, A/B harnesses, D441 census
F10 pad slider persistence (D489), glass collision hull (D490), A/B input
harnesses (1964 virtual pad, GE_INPUTSCRIPT, live one-pad launcher), adjacent
global struct reads (D491), vibration percent, D441 complete pointer-width
census with high-arena tier-1 build, fix batches 1-5 and an ABI warning
ratchet, playtest verification of D493-D502.

Refs: D489 D490 D491 D492-D502 D441
2026-10-06 18:35:42 -04:00
James Dansereau 4b38c2566e D478-D490: crash log, session start, performance, fidelity, regions
Crash-log snprintf (D478), start-mp folder (D479), shader pre-warm (D480),
render worker thread (#92 low-end 45 to 60 fps, D481), lighter first-launch
defaults (D482), sniper crouch/zoom fixes (D483-D485, #136), Rareware text
TMEM swap (D75), D173 residual, turret/explosion fidelity (D486, #126),
21-level Windows golden rebase with a two-tier gate, PAL and JP convert/build/
boot (D258) with PAL frame height (D487) and osTvType (D488).

Refs: D478 D479 D480 D481 D482 D483 D484 D485 D486 D75 D173 D258 D487 D488 #136 #126
2026-10-06 18:35:42 -04:00
James Dansereau 88bcef32a3 Performance work D473-D477; scaffolding batches 3-4
Uncached getenv per vertex batch (D473), fast3d micro-optimisations (D474),
hitch diagnosis (D475), CI4 palette-bank cache key (D476), spike counters,
CI8 palette churn measurement (D477). Dead diagnostic probes removed.

Refs: D473 D474 D475 D476 D477 GE_PERFSTAT
2026-10-06 18:35:41 -04:00
James Dansereauanditaloarruda b735190fd9 Input and audio features D469-D472
Gamepad presets and per-seat rebinding (D469, building on
italoarruda's #109), master volume and output
device (D470), controller-family button names and player LEDs (D471), F10
owner-pad input and widescreen pillarbox polish (D472).

Refs: D469 D470 D471 D472

Co-authored-by: italoarruda <3868006+italoarruda@users.noreply.github.com>
2026-10-06 18:35:41 -04:00
James Dansereauanditaloarruda 9dd98bdb20 Gameplay and rendering fixes D463-D468; D441 pointer-width sites
Zero-line tile texrect guard and texture-import skip (D463), stage-bank red
zone checker (D464), Dam/Caverns water slide (D465, #127), extended draw
distance no longer changing gameplay (D466, #125), player matrix pointers and
Model/PROMOTE/vtxstore rebase (D457 c/d), AI view clamp for ultrawide
(D468), fire-sound path audit (D433, D467 #130), italoarruda's #107 pointer-width
sites (two sites, credited).

Refs: D463 D464 D465 D466 D467 D468 D457 D433 #125 #127 #130 #107

Co-authored-by: italoarruda <3868006+italoarruda@users.noreply.github.com>
2026-10-06 18:35:40 -04:00
James Dansereau 00fdd94c8d 2-4 player split-screen (#99): D416-D419, D422, D428, D448, D450
Virtual seats, per-seat camera and input routing, aim modes, hot-plugged
controllers reopening via a coalesced rescan (D450, dolent's PR #122,
merged as the original commit). Rule-2
sign-off for D417.

Refs: #99 D416 D417 D418 D419 D422 D428 D448 D450 #122
2026-10-06 18:35:40 -04:00
James Dansereau 41d690f7ca 32-to-64-bit pointer-width fixes D451-D462; scaffolding removal batch 1
Tick-based auto-fire gate (D451), knife keyframe pointers (D454), font/text
slots (D456), memp arena start (D453), exe-address truncation census (D455),
global bank/RDRAM and background slots (D457 a/b), item-slot allocation
stride (D461), vtxstore table stride (D462). Mechanical, semantics-preserving
ABI/layout edits only. Diagnostic probes removed (batch 1).

Refs: D451 D453 D454 D455 D456 D457 D461 D462 D408 D294
2026-10-06 18:35:39 -04:00
James Dansereau ad3856892a v0.4.1 sync: build and CI fixes, settings presets D440-D447, outside PRs
build-pc.sh Windows temp-dir re-exec fixes; CI pin actions and no-cache ROM
handling; original-N64 and Deck presets; D440 preset labelling; D442 All
unlocked as a RAM/query-time override; D443 settings values (MSAA 16x,
Restart game); D444 front-end Previous/Next paging; D447 Original-aspect
pillarbox/letterbox. Outside contributions by dolent: PRs #120 and #121 (relabelled D445,
D446), merged as dolent's original commits.

Refs: D440 D441 D442 D443 D444 D445 D446 D447 D387 D283 #120 #121
2026-10-06 18:35:39 -04:00
James Dansereau 0e8c2ce213 docs: fix reconciliation date in GE-ENV-PROBES.md (2026-09-28, not 10-07) 2026-09-28 10:29:52 -04:00
James Dansereau d267d2311e docs: reconcile GE-ENV-PROBES.md for v0.4.0 (31 new rows); fix env-drift checker to see envflag.h wrapper reads; drop README hero row 2026-09-28 10:25:31 -04:00
James Dansereau 5c643e3acb D341: intro gun-barrel blood drip drew static garbage
Two fast3d defects. (1) The 1-cycle blood texture rectangle sampled a
stale tile-1 CI8 declaration: the title leaves G_TL_LOD set and the D236
detail-base rule redirected texunit 0 to tile 1. LOD tile selection is
2-cycle-only on the RDP, so texrects outside 2-cycle now sample their own
tile. (2) The drip image is regenerated in place in the dyn pool (reached
via the KSEG0 mirror); the address-only cache key froze it on its first
image. Dyn-pool textures now key on a content hash. Headless-verified,
golden 3/3. A/B: GE_DYNTEXHASH_OFF.
2026-09-26 19:20:06 -04:00
James Dansereau f242992360 D245: water tiled at the wrong size + s16 sky/water vertices; D242 closed
The IsWater 32x32 CI8 image (drawn through an RGBA16 tile, D229) was
imported with its mip chain as 32x43 and normalised as 16 wide, so the
pattern ran at 2x and wrapped every 43 rows instead of the N64's 32:
fold steps made it jump. Import the base level only and use CI8
geometry in the tri path. Sky/water fans now go through a port-only
G_FLOATVTX_EXT (float clip positions + S/T) instead of s16 Vtx, removing
the tc shift and wScale quantisation jitter. User-verified vs 1964;
golden 3/3; Win + Linux build. A/B: GE_D245_OLDVTX / GE_D245_OLDTEX.
2026-09-26 18:56:52 -04:00
James Dansereau d061ca3d12 D303: model converter copied 1/4 of each star muzzle flash's vertices
The op-22 DisplayListPrimaryRecord count is the number of star-flash
arms; dorottex (and PD's modelRenderNodeStarGunfire) read count*4
vertices. d43_emit/d43_convert sized the array as count vertices, so
3/4 of every muzzle star came from neighbouring sidecar bytes and drew
as long spikes (M16 "shooting up"). Size it 16*4*count. Emitter
round-trip checks all pass; M16 renders a clean star headless.

Also TEMP GE_STARTWEAPON (bondview_r.c) for weapon repros. Re-run
tools_pc/d43_emit.py to regenerate data/pcmodels-*.
2026-09-26 18:15:17 -04:00
James Dansereau 506cd7ed11 D207/D241: shot-sound handle probe; live captures refute drop/starvation
TEMP GE_D207S (gunfire.c, #ifdef PORT, read-only) logs every player
shot-sound decision (H1/H2/SKIP). Four user captures incl. a 7-weapon
stress test at 60 fps: 0 SKIP, 0 voice drops, audio production at rate.
The one captured drop-out ran under GE_AUDIOTRACE, whose audio-path file
I/O is a likely cause. Still open as intermittent.
2026-09-26 17:26:06 -04:00
James Dansereau 8f838316c0 Q7: declare atoi/lround for port TUs; D207: ZHOLD/ZREL harness, findings
optionsoverlay.c called atoi and lround through implicit int declarations
(GE's stub stdlib.h/math.h shadow the CRT, D324 class); atoi joins the
other K&R decls in port/shim/stdlib.h, lround gets a local prototype.
Windows + Linux builds clean of both warnings.

GE_INPUTSCRIPT gains ZHOLD/ZREL (sustained fire) for automatic-weapon
audio repros. D207/D241: headless burst-fire at 60 vs 30 fps with the
alarm forced logged zero voice drops at both caps; owed a real-play
capture. Golden gate 3/3.
2026-09-26 16:53:54 -04:00
James Dansereau 0097469810 D291/D306: close Dam intro truck glitch as faithful; add repro probes
D291 (user-captured) is D306: Dam intro camera 5, truck over the walkway
camera; reproduces at FovScale 100. All PC-specific leads refuted with
evidence: FOV, depth precision/DrawDistance, depth state (every truck
triangle is opaque z-tested), near-plane handling, wheel transforms. The
image is a depth-correct view of the authored model from under the truck.
GE_ZF's earlier "real fighting" was a +-255 z quantisation artefact.

TEMP tools: GE_INTROCAM=<n> pins the level intro's first camera
(bondview_r.c, #ifdef PORT, PRNG draw still consumed); GE_D306C depth-state
census in gfx_pc.cpp. Golden gate 3/3.
2026-09-26 15:54:46 -04:00
James Dansereau f1207e915b D252: re-sync compiled explosion DLs on every stage load; D314 confirmed
gimgSyncCompiledGlobalDLs (D68 shim) only copied texLoad's resolved
texture pointers into the compiled globalDL shadows while a slot still
held the 0xABCD IMAGESEG marker, i.e. only for the first stage (title).
Every later stage kept pointers into the title's freed texture pool, so
explosion smoke/fire/debris rendered as rainbow garbage once the stage
overwrote that memory (Facility: past the lockers). Learn the slots once,
re-sync them every stage. User-verified; golden gate 3/3.

Also: TEMP GE_D252POOL texpool/sync probe; GE_D157 F12 burst and caps
(uncapped colour match made live play 4-6 fps). D314 F10 flicker
user-confirmed fixed. Findings M-201, porting-notes C2.
2026-09-26 11:43:30 -04:00
James Dansereau aedc98e247 D252/D314/D291: explosion-debris investigation, overlay wheel race fix, Dam truck notes
- D252 (M-200): the 'rainbow confetti' is the explosion debris
  (explosionInitFlyingParticles). Statically clean for colour; 4 headless
  repros including the user's grenade-launcher trigger are all clean. Still OPEN.
- d320repro (TEMP): new explode= and cheat= actions for headless explosion
  captures; documented in GE-ENV-PROBES.
- D314: optionsOverlayScroll (host thread) rebuilt s_visIdx/s_visN and wrote
  s_sel while the scheduler thread draws the overlay. Wheel notches are now
  queued atomically and applied in HandleInput (D287 pattern). Emit and
  HandleInput share the scheduler thread. Headless file/mission select is
  pixel-stable; likely fixed together with D287, needs a user check.
- D291: truck AI mapped (ai_9 / 0x040a, path 7); headless walk did not reach it.
2026-09-25 23:20:38 -04:00
James Dansereau 36de85e789 D339 (#92): GE_PERFSTAT perf probe + measured CPU budget (CPU likely not the low-end bottleneck; GPU suspect)
Env-gated per-frame timing: render phases, tris/batches, game-thread busy,
GL_TIME_ELAPSED GPU timer. Measured ~2.1 ms/frame total CPU on the heaviest
levels here; the GPU timer is only meaningful when GPU-bound. Target-hardware
numbers needed before a low-end preset. Golden 3/3; Win + Linux builds.
2026-09-24 11:11:47 -04:00
James Dansereau 2d2f56b050 docs: GE_AIMLOG + scripted mouse/aim tokens in the probe catalogue (D337) 2026-09-24 10:40:26 -04:00
James Dansereau 91564f79b6 docs: porting-notes on dead placeholders after inlining a split struct (D336); probe catalogue 2026-09-24 09:58:09 -04:00
James Dansereau 2a6bc2bb4a D235: headless reload verification; TEMP GE_D235_RELOAD / GE_D235_OFF harness
Scripted watch Abort on -level_09 -> title stage -> GE_D235_RELOAD re-enters
the level via bossSetLoadedStage() (the front end's mission-start call)
after a front-end dwell. With the fix: stale entries purged at every
transition; the reloaded level renders cleanly. A/B with GE_D235_OFF (reset
skipped): 588-788 stale entries survive yet all textures stay correct, for
both an identical and a divergent (intro skipped) first-load order. So the
reported trigger isn't captured headlessly on Bunker 1; the fix stays
correct by construction. D235 stays OPEN pending a real die -> mission
select -> restart playtest (A/B with GE_D235_OFF).

Both switches are TEMP, env-gated, inert when unset; bunker1 golden 3/3.
2026-09-23 11:29:34 -04:00
James Dansereau 0503d33564 D236: strip the investigation probes now that the finding is closed
D236 was root-caused and fixed in the previous commit, so its diagnostic
scaffolding comes out:

  GE_D236RM / GE_D236ORDER / GE_D236ALPHA / GE_D236ZFIX  gfx_sp_tri1
  GE_D236RAW                                             bg.c room load
  GE_D236BT                                              chr.c + model.c
  port_d236bt_dump()                                     host stack backtrace
  g_d236_last_vtx_seg / _raw                             G_VTX trackers

Also drops the <windows.h> include that only the backtrace dumper needed, and
rewords the one D288 comment that cross-referenced GE_D236RM. src/game/bg.c,
chr.c and model.c are back to their pre-probe state -- no game-code diff from
D236 remains. The pass-26 fix in gfx_lod_tile_offset is untouched.

Doc rows removed from GE-ENV-PROBES.md; gen_env_probes.py --check and
gen_findings_index.py --check both pass. Build clean. The Surface 1 spawn
capture is unchanged across the strip (settled frame 560 within threshold;
the intro-pan frames never match run-to-run, D117).
2026-09-21 22:53:04 -04:00
James Dansereau 425237f93f D236 (M-198): fix Surface 1/2 tree cards; root cause was a DETAIL tile binding
GE's texLoadFromGdl picks a handler per G_NOOP texture reference from w0 & 7.
TEXTURETYPE_LOD/MIPMAP (texHandleType0/Type2) issue ONE load block at TMEM 0,
so tiles 0..n are LOD levels of that one image. TEXTURETYPE_DETAIL
(texHandleType1, src/game/tex.c) issues TWO: the detail texture at TMEM 0 and
the real base image at TMEM texGetSizeInBytes(detail,0) on tiles 1+.

gfx_lod_tile_offset folded every G_TL_LOD draw to tile 0 (D107 -- correct for
the first two, wrong for DETAIL), so the port drew the detail texture and never
imported the base image at all. All four Surface 1 treeline cards
(tex1198-tex1201, RGBA5551 64x17 cut-outs) are DETAIL bindings sharing one
detail texture, tex2465: an opaque 32x32 IA8 noise tile. That is the same
texture D267/D276 measured at 1023/1024 texels opaque and called the mystery
"noise painter" -- hence 20+ passes chasing decode, alpha, mip and geometry
theories for what was a binding bug.

Fix (port layer only): under G_TL_LOD, if tile fi+1 differs from tile fi in
BOTH TMEM address and fmt/siz, sample tile 1. Asymmetric on purpose -- LOD
levels of one image always share a format, so a mip chain can never trip it,
and a detail pair that happened to share a format just keeps the old
behaviour. Config Video.DetailBaseTile, default on.

Verified: Surface 1 idle spawn goes from an opaque tiled noise wall to discrete
snow-covered pines with ground/snow unchanged; Surface 2 shows a discrete night
treeline; Depot renders clean; bunker1 golden framediff is bit-identical with
the fix on and off. Offline scan of every level's bg: DETAIL bindings exist
only in sevx (84), depo (39), sevb (4), sev (4), so every other level is
provably untouched. User play-test confirmed.

Also refuted and removed pass 25's fix direction: cut-out mip suppression
changed nothing, and its alpha census showed the wall is painted by a fully
opaque texture -- retiring the mip / 1-bit-alpha / XLU-smear branch. The
GE_D236P25 probe is gone with it.

tools_pc/gen_env_probes.py: stop counting a "GE_FOO[" log-line TAG in the doc
as a documented env var. GE_TEXDUMP_ADDR prints "GE_TEXA[...]", which is not
the name of any getenv(), so the check reported a permanent phantom GONE.

Generalised as docs/porting-notes.md D12; full trail in findings.md D236 pass
26. Carries the pending D75/M-197 sidecar-converter fix and the env-gated
D236/D75 probes, which the doc gates require to land alongside their rows.
2026-09-21 22:48:10 -04:00
James Dansereau 327e6fe1a7 D75 (M-196): refute slot-overwrite hypothesis; narrow logo bug to texture-modulate render path
New env-gated GE_D75D probe (gfx_pc.cpp) captures per-frame V/T token stream
+ per-triangle rejection/emission accounting for the front-end logo window.

Findings (bare front-end boot, logo = frames ~244+, nv=93/nt=1018):
- Slot-overwrite (M-195's leading hypothesis) REFUTED: the DL is streamed
  (V TTT...VTTT interleaved), max triangle vertex index = 15, so no
  cross-batch vertex clobbering can occur.
- Rejection gates are not the cause: of 1018 tris/frame, trivial-reject=0,
  backface-cull ~520 (normal half), ~500 reach GL, all on-screen (NDC in
  [-1,1]^2). Not the invisible flag; vertex colors [0,255]; depth normal.
- PPM analysis: logo window renders 0-33 content px/frame (the 'tiny
  fragment'), vs title quads at 8.36% coverage.
- Narrowed cause: combine mode 0x009ffe4f19ffe4f1 parses to
  rgb a=TEXEL0 c=SHADE (texture-modulate); gfx_generate_cc sets
  used_textures[0]=1, but the material still renders black-on-black. Next
  step: trace where the TEXEL0 sampler is lost between combiner build and
  draw (shader_id0 -> GL program / prg[tm] / texture upload).

Port-layer fast3d only; no rule-2 sign-off needed. Probes env-gated, inert
without GE_D75D (verified: 0 lines in a normal run).
2026-09-21 06:00:28 -04:00
James Dansereau d4440759cd Merge main into release/v0.4.0 (docs: D322 log + mission-count fix) 2026-09-21 05:26:42 -04:00
James Dansereau aebcf178e6 D75 (M-195): GE_D75V per-batch vertex probe + findings/probes doc updates
- gfx_sp_vertex probe (env GE_D75V=lo-hi): logs per G_VTX batch the
  transformed x/y/z/w min/max, raw first-vertex ob, dest_index, and the
  MP translation row. Proves the logo transform is correct and that every
  batch encodes v0=0 (slot-overwrite hypothesis).
- videoGetFrameCount() accessor for frame-gated probes.
- findings.md: M-195 entry in the D75 section (emission + transform
  eliminated; G_VTX v0=0 overwrite is the leading suspect).
- GE-ENV-PROBES.md: register GE_D75V, bump reconciled line.
2026-09-21 05:14:57 -04:00
James Dansereau bb508e5ce4 D235: wire videoResetTextureCache into per-stage reset (stale GL texture cache); D311: arm speed-poison choke-point probes
D235 (wrong textures after dying/restarting a level) -- mechanism found
+ fix (M-193). M-108's refutation of the game-side CI-cache theory stands,
but the port has its own TextureCacheMap (port/fast3d/gfx_pc.cpp) keyed by
(source address, palette addrs, fmt, siz, size_bytes, palette_hash) with NO
content check. Stage-texture source addresses live in MEMPOOL_STAGE, which
the per-stage while(!done) block in boss.c resets on every transition
(death/restart included). After the reset, entries keyed into that arena are
stale; if the texpool places a different image at an address that held
another one in the previous run of the level, the lookup hits and returns
the old pixels -- 'wrong textures' only on the reload path, never on a cold
boot. The N64 has no such cache (RSP re-reads DRAM every frame), so this is
a pure port-side defect: videoResetTextureCache() (port/src/video.c) existed
with zero callers and is now called in that block after memaReset
(#ifdef PORT). Re-uploads are pixel-identical; cost is one-time during the
loading screen. GE_D235=1 logs the purged entry count per transition
(cold boot = 0, death/restart > 0) as mechanism evidence; new
gfx_texture_cache_count() accessor backs it. Full 21-level golden sweep:
21/21 PASS (pixel-identical). Live death/restart confirmation is a playtest
item.

D311 (upstream poison source, M-194): every write of model->speed funnels
through modelSetAnimSpeed (startframe<=0 path) or modelSetAnimation2 -- both
now log 'D311: ... ANOMALY' when the incoming value or the resident speed is
non-finite/|v|>1e4 (#ifdef PORT, unconditional-on-fire like the D156
guards; silent in healthy play). The line captures in/resident/startframe/
oldspeed/newspeed/timespeed + model ptr so the next Caverns occurrence pins
the injecting call. Static pass: sub_GAME_7F06FE90 callers all pass
arg2=16.0f (denominator safe); the old->new lerp only circulates values;
bheadAdjustAnimation is the one path where a live player-velocity
corruption reaches model->speed.
2026-09-20 23:46:43 -04:00
James Dansereau 7bb43260de D322: log issue #87 long-session audio degradation + ship GE_D322 pool-telemetry probe
User report (v0.3.0 full campaign): audio progressively degrades from Silo;
by Caverns/Cradle the OST is inaudible and SFX come and go; restarting the
game restores it. Logged as D322 with static triage: level teardown audit
clean, mixer stateless per frame, D202's covered leak class is one shape
only (ownerless count==-1 loops) validated single-level only, and D305
already records a pool/list accounting desync. Ranked hypotheses:
voice-pool exhaustion/counter drift > queue starvation > evtq saturation.

Probe: GE_D322=1 prints sfx=n/8 (SFX soft cap), evtq=n/64, pv free/lame/alloc
(24 physical synth voices) + pipeline depth/drops every 5 s of wall clock,
read with GE_D204. sndD322PoolSummary() walks the ALSynth lists under the
D285 lock; NB drvr is a pointer field (first build faulted on &drvr).
Smoke-tested 40 s headless -level_09: pools coherent, no crash.

Also adds the missing D321 row to the findings index (entry existed at file
tail with no table row, so it was absent from the CSV too).
2026-09-20 23:10:57 -04:00
James Dansereau 2d9e673129 D322: log issue #87 long-session audio degradation + ship GE_D322 pool-telemetry probe
User report (v0.3.0 full campaign): audio progressively degrades from Silo;
by Caverns/Cradle the OST is inaudible and SFX come and go; restarting the
game restores it. Logged as D322 with static triage: level teardown audit
clean, mixer stateless per frame, D202's covered leak class is one shape
only (ownerless count==-1 loops) validated single-level only, and D305
already records a pool/list accounting desync. Ranked hypotheses:
voice-pool exhaustion/counter drift > queue starvation > evtq saturation.

Probe: GE_D322=1 prints sfx=n/8 (SFX soft cap), evtq=n/64, pv free/lame/alloc
(24 physical synth voices) + pipeline depth/drops every 5 s of wall clock,
read with GE_D204. sndD322PoolSummary() walks the ALSynth lists under the
D285 lock; NB drvr is a pointer field (first build faulted on &drvr).
Smoke-tested 40 s headless -level_09: pools coherent, no crash.

Also adds the missing D321 row to the findings index (entry existed at file
tail with no table row, so it was absent from the CSV too).
2026-09-20 23:09:03 -04:00
James Dansereau 92dbd015b5 Register GE_OBJT in the env-probe index (CI drift check) 2026-09-20 17:24:16 -04:00
James Dansereau 63315030f8 docs: v0.3.0 release prep — final review pass
- GE-ENV-PROBES.md reconciled (18 new vars documented, GE_D243X3 tombstoned
  after the M-170 permanent fix removed it) — CI gen_env_probes.py --check passes
- release notes gif alt text: ~32 s -> ~12 s (matches README/index captions)
- agentic-development.md stats regenerated for the month-one milestone
- docs/index.md status bumped to v0.3.0, honest-status synced with README
- .gitignore: local/ working-junk dir
2026-09-20 10:09:33 -04:00
James Dansereau 8ba1be14f2 D243 M-169: real teleport signal wired in, root mechanism confirmed decomp-faithful
Implements next-steps (b) and (c) from the D243 CONSOLIDATED NEXT STEPS
block. (c): static analysis shows the render_pos-cycling mechanism (chrTick's
cutscene-unaware animation dispatch) is unmodified decomp code with no gate
on either platform, so it's decomp-faithful by construction -- clears the
way to ship a port-layer fix without a rule-2 sign-off. (b): chrai.c's
AI_TRYTeleportingChrToPad now notifies a new epoch counter right after its
real setsuboffset() call; GE_D243X3/GE_D243X4 re-baseline on that signal
instead of freezing forever, targeting the M-168 "camera looks wrong way for
later shots" / "Bond still looks stuck" incompleteness.

Build/link/crash-safety verified headlessly (two 25-30s -level_09 smoke
runs, both crash-free). Live Dam-to-abseil playtest still owed before this
can replace the diagnostic gating with a shipped default.
2026-09-17 13:59:47 -04:00
James Dansereau 53219c18ae D243 M-167: camera shake mechanism confirmed live -- first positive result
M-163's live playtest smoothed prop->pos completely (log-verified) but
the visible camera shake was unchanged -- the camera's look-at filter
doesn't read prop->pos at all. It reads field_488.pos, a separate
variable computed from render_pos (the model's own animated-skeleton
transform), independent of prop->pos even though both happened to show
the same period-4 cycling symptom through a probe that only logged one
of them.

Retargeted the suppression at field_488.pos's actual write site
(bondview2.c:10495-10497, GE_D243X3-gated, freezes it while POSEND is
active). User's live playtest: "yep camera shake is gone now" -- the
first positive result across the whole D243 investigation.

Not a real fix -- GE_D243X3 unconditionally freezes state rather than
correcting why the model's skeleton animation cycles in the first
place. User also reports Bond's own rendered body still visibly
duplicates/glitches, expected since this only freezes the camera-feed
copy, not the render_pos draw-path read -- likely the same underlying
mechanism observed through a different consumer, not yet isolated.

porting-notes.md D11 gets the generalized lesson: confirm the actual
consumer reads the field you fixed before spending a live-test cycle
on it -- two fields can show the same symptom from one shared root
cause while being genuinely independent data paths.
2026-09-17 13:06:57 -04:00
James Dansereau d9c7705654 D243 M-162..M-166: corrected decisive experiment + deep root-cause tracing
Two third-party reviews caught a real flaw in the earlier M-161 negative
experiment: it restored prop->pos AFTER chrTick() returned, but
render_pos/field_488.pos are computed INSIDE that same call -- so M-161's
"no change" result never actually tested M-160's overwrite hypothesis.
Verified this directly against the capture data (a clean period-4
same-frame before/after-chrTick offset proves the lane-advance happens
entirely inside chrTick) before accepting the review.

M-162: corrected save/restore placement (chr.c), Z-only. Live playtest
confirmed the mechanism -- Z went smooth in the log -- but the visible
glitch persisted because that capture's chr cycled on Y instead.

M-163: widened the experiment to the whole prop->pos vector. Built and
smoke-tested; not yet live-playtested (exe was busy/user stepped away) --
this is the ready-to-test build for the next session.

M-164/M-165/M-166 (static tracing, no build needed): chrUpdateAnim reads
the model's root-joint position back into prop->pos after advancing
whatever animation is attached (getsuboffset) -- not an arbitrary
overwrite. The cutscene's teleport (AI_TRYTeleportingChrToPad) re-anchors
the root joint but never touches animation state; the real rappel
animation commands target a separate invisible driver chr (M-145), not
the visible one. Traced the Dam exit script (UsetupdamZ.c) directly and
found no animation-set command for Bond anywhere in it -- his head
animation gets forced back to an idle/walk loop by
bondviewFrozenMoveBond's own tick, but the *phase* within that loop is
inherited, not reset. This raises a real possibility that the mechanism
is decomp-faithful and the actual PC/N64 divergence is upstream of the
cutscene code entirely (which animation phase Bond happens to be on when
the cutscene grabs him). Extended D243M: onscreen with aidx=/animframe=
(reusing GE_D193A's reverse-resolve technique) so the next live capture
can test this directly.

Docs updated throughout (findings.md, findings-index.csv,
GE-ENV-PROBES.md); HANDOFF.md refreshed with full session state (local,
gitignored).
2026-09-17 12:49:19 -04:00
James Dansereau fec396f53b D300: hipfire direct camera look (WI-1); D243 M-159/160/161 investigation
D300: new hipDirectCompute() in port/src/input.c writes vv_theta/vv_verta
directly for hipfire mouse-look, mirroring GEPD's linear px->degree model
and the existing D194 aim-mode pattern -- replaces the N64 stick-curve
routing that saturated at ~13 px/poll (issue #89). Ported GEPD's
dead/watch/pause safety gates. Input.MouseDirectLook=1 default, escape
hatch to the legacy path. User-confirmed better feel in a live playtest.

D243: added a permanent onscreen-visibility probe (D243M: onscreen,
chr.c) to test whether chrTick's posIsOnScreen gate explains the
abseil cutscene's frozen render position (M-159). A live capture
refuted that and found the real mechanism instead: chrTick overwrites
prop->pos with a stale value via its own chrUpdateAnim/modelTickAnim
animation-position logic, racing the AI script's scripted write
(M-160). A decisive throwaway experiment neutralizing that overwrite
produced no visible change in a live playtest (M-161) -- the mechanism
is real but isn't the dominant visible cause. Experiment reverted;
D243 parked pending a new lead after ~15 session-passes without a fix.

Docs: findings.md/findings-index.csv/porting-notes.md/GE-ENV-PROBES.md
updated throughout.
2026-09-17 10:56:41 -04:00
James Dansereau 30dcbea085 D219/D252 M-158: rainbow bug not reproducible in 6 identical dev-box runs; classified flaky/environment-dependent; probes left live for next occurrence 2026-09-16 23:15:45 -04:00
James Dansereau 145d3f46ac D219 M-157 correction: measured G_LIGHTING for glass2.c sparks, theory doesn't hold up
Went back and actually tested the G_LIGHTING hypothesis from the
previous commit instead of leaving it as an untested code-reading
inference. Two things prompted this: (1) noticed a code comment near
the existing GE_D172 probe documenting a live result never written up
in findings.md -- "G_LIGHTING is ruled out (5000/5000 samples off)" for
explosion.c's particles specifically, meaning the sibling case this
session's theory leaned on was already empirically disproven, not
merely unconfirmed; (2) that's exactly the kind of unchecked assumption
this session's own rule-2 taxonomy work was written to prevent.

Added GE_D157 (gfx_pc.cpp, gfx_sp_vertex, env-gated, cached getenv):
logs geometry_mode/LIGHTING + resulting shaded RGB whenever a vertex's
raw color bytes match one of glass2.c's g_BulletSparkColors[] entries.
Re-ran the identical Bunker1 repro that produced the magenta/green/cyan
screenshots -- only 2 hits, both LIGHTING=off, both unaffected white,
no hit at all for the pale-yellow value the door-spark path should
produce. Reading: G_LIGHTING likely isn't the mechanism here either
(consistent with the sibling case), though the probe may simply not be
catching the real spark vertices yet (exact-byte-match is fragile).

Corrected the D219 finding to flag the "cross-validates M-110" framing
from the previous commit as superseded, not delete it -- the reasoning
trail (including the mistake) stays visible so a future session doesn't
repeat it. Root cause of the visually-confirmed rainbow bug remains
open; next steps revised to a texture-identity-keyed probe variant.

Added GE_D157 to GE-ENV-PROBES.md. No src/game changes. findings-index
regen was a no-op.
2026-09-16 22:25:39 -04:00
James Dansereau 23da815dda D236 pass 10: refute "nondeterministic instantiation", it's room occlusion
Per the rule-2-clarification pass-10 brief in findings.md, applied the new
three-bucket taxonomy to D280's leading theory that the 0x0c184b50
discrete-tree-card render-mode class instantiates inconsistently across
runs of the deterministic -level_36 boot (~4 tris vs ~720).

Added a small env-gated diagnostic (GE_D236RM, port/fast3d/gfx_pc.cpp
gfx_sp_tri1) counting that class's per-frame triangle count, heartbeat-
logged every 300 frames even at zero. Three independent -level_36
launches (one with sustained GE_INPUTSCRIPT camera rotation) produced
byte-identical per-frame counts, falsifying the "run-to-run dependent"
premise outright. Cross-referencing with the existing GE_D104 room-census
probe in the same run shows the count drops to zero at the exact frame
the player's curRoom transitions from 12 (17-20 rooms drawn, an outdoor
vista) to 18 (1-5 rooms, an enclosed indoor space) via the boot's own
scripted intro camera — ordinary portal/room occlusion, not a bug. None
of buckets (a)/(b)/(c) apply; no src/game or port/ fix made or warranted.

The original "wall not discrete sprites" symptom is untouched by this
pass — narrowed the real next step to getting a camera into room 12
facing the tree line directly, in docs/dev/findings.md.

Added the generalisable "deterministic boot flag != deterministic camera
path" quirk to porting-notes.md §E. findings-index.csv regenerated.
2026-09-16 17:58:01 -04:00
James Dansereau deac53f892 docs: D243 M-154 fix 2 (second probe crash + final deref-free tick path) 2026-09-16 17:09:36 -04:00
James Dansereau ce8427cff2 docs: D243 M-154 — user confirmed double Bond (one frozen copy per shot); corrected log reading; census probe documented
- findings.md ## D243: M-154 entry. User symptom sharpened (one new
  frozen Bond per camera shot; old copies persist, never animate).
  Corrects M-153: the POSENT window has three phases and in phase 3
  prop->pos ITSELF cycles period-4 through three z-lanes while y falls
  smoothly — the rendered model tracks it (per-lane pose offsets), so
  it teleports every frame rather than freezing. H1 vs H2 named;
  probe design + smoke results recorded. §F table cell updated.
- GE-ENV-PROBES.md: GE_D243M row updated (chrdraw census, new tick
  fields, dedupe note).

HANDOFF.md (gitignored) got the matching LATEST block with the
re-capture command.
2026-09-16 16:47:31 -04:00
James Dansereau fbc2a533ae D288 M-152: static investigation, add GE_D288 near-plane probe
Read gfx_sp_tri1 against D233/D106/D271's existing guards. D233's
trivial-reject fix is ruled out as a direct cause -- GL clips well-formed
clip-space geometry correctly downstream of it. Found a new, previously
uncatalogued unguarded site: the G_CULL_BOTH backface-cull cross-product
divides raw vertex w with no epsilon guard, which can NaN/Inf near w=0
and silently skip both cull branches. Not yet confirmed against a live
repro. Added GE_D288 (cached-getenv, D250-safe) to catch the culprit
triangle's w/bbox at draw time on the next -level_20 run.

No game-logic change; port/fast3d/ diagnostic only, inert unless
GE_D288 is set.
2026-09-16 16:13:33 -04:00
James Dansereau fd065071cc findings: D243 M-151 probe landed; GE-ENV-PROBES + index updated 2026-09-16 15:58:37 -04:00
James Dansereau 06b675b5e8 fix(ci): revert GE-ENV-PROBES.md rows for uncommitted D236 probes
The v0.2.1 docs commit (572ab7bf) added GE-ENV-PROBES.md rows for the
D266-D280 tree-investigation probes, but that probe code itself was
deliberately left uncommitted (still mid-investigation, unswept). CI's
tools_pc/gen_env_probes.py --check caught the mismatch: 13 documented
vars with no live getenv() site in the committed tree. Reverting the
doc rows to match what's actually committed; they'll return together
with the probe code once the D236 investigation lands.

Claude-Session: https://claude.ai/code/session_01KwZViAFaXua3PKgHHjNgvi
2026-09-15 08:59:51 -04:00