forked from Ports/goldeneye-pc-port
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).
37 KiB
37 KiB
GE_* environment probes — consolidated list
Every getenv("GE_…") in the tree. The table prose is hand-curated;
tools_pc/gen_env_probes.py re-greps the live sites and reports drift
(NEW / GONE vars + a fresh file:line map) — run it before trusting the
File:line cells. Last reconciled 2026-09-21 (D322 probe added; v0.3.0 release review was 2026-09-20). All are env-gated: unset = zero behavior
change, so they are safe to leave in a release build (they only cost a
getenv on the guarded path). "Dead" below means the finding it was built
for is closed — the probe is strip-candidate scaffolding, not that it does
anything harmful.
Two classes:
- Infra / live tooling — deliberately kept, used by the playtest harness
(
tools_pc/debug.ps1,playtest.sh,level_sweep.sh) or by an investigation still in progress. - Dxx diagnostic — added to chase one finding; most are now closed.
Infra / live tooling
| Env var | File:line | What it does | Status |
|---|---|---|---|
GE_PCDUMP |
port/fast3d/gfx_opengl.cpp:1079 (is_pcdump), gfx_opengl.cpp:700, port/src/video.c:188, port/src/config.c:46 (configGetFrameDump) |
Per-frame framebuffer PPM dump to $PCDUMP dir (verification-ritual golden captures, tools_pc/framediff.py/pixcount.py). Also settable via [Debug] FrameDump ini (env wins). M-33/D168: the writer now emits rows top-to-bottom — captures before that fix are vertically flipped. |
live |
GE_INPUTSCRIPT |
port/src/input.c:306 |
Headless scripted controller-0 input: "<frame>:<tok>,…;…" (SUP/SDOWN/SLEFT/SRIGHT/SNONE sustain, buttons pulse). Sole input source when set. |
live |
GE_INPUTLOG |
port/src/input.c:329, port/src/config.c:53 (configGetInputLog) |
Logs computed pad state (buttons + stick) each poll when non-zero. Also [Debug] InputLog ini. Also traces D165 menuptr est/tgt/eff/stick. |
live |
GE_STARTMENU (+ GE_STARTMENU_PAGE, GE_STARTMENU_DIFF) |
src/game/lv.c:398-402 (#ifdef PORT) |
Boot straight into a front-end menu id (13=MISSION_COMPLETE, 10=BRIEFING, 7=MISSION_SELECT, 12=MISSION_FAILED, 6=MODE_SELECT); _PAGE=folder row (def 1=Dam), _DIFF=0..3. Crash-test a screen fast. |
live |
GE_UNLOCK_ALL |
src/game/debugmenu_handler.c:1098 (get_debug_enable_all_levels_flag, #ifdef PORT) |
Mission-select shows every solo level (playtest jump-to-any-level aid). Cached in a static int c. |
live |
GE_DEBUG_UNLOCKALL |
src/game/file2.c:404 (#ifdef PORT) |
Forces the "all levels unlocked" flag TRUE regardless of the shipped save — same effect as GE_UNLOCK_ALL but at the file2 unlock-query site. Cached in a static int. |
live (playtest aid) |
GE_DEBUGMENU |
src/boss.c:566 (#ifdef PORT) |
Makes the L+R debug menu respond to live controller input (C-up+C-down open/close, stick + buttons read from pad 0) instead of the #ifndef DEBUGMENU path; unset reproduces that path exactly. Cached in a static int. |
live (playtest aid) |
GE_OPTIONSOVERLAY |
port/src/optionsoverlay.c:231 |
Force-enable the F10 port-layer options overlay (D184 / PR #10) regardless of ini. atoi != 0. |
live |
GE_D204 |
port/src/audio.c (audioSetNextBuffer) |
Audio-health monitor (D204). One line per 5 s of wall clock: rt= produced-audio/wall ratio (1.000 healthy; below 1 = the 30 Hz audio retrace is starving and the device is padding with silence), q= SDL queue depth / queueLimit (pinning at the limit with drop= climbing = overproduction, the "eventually no audio" state), drop= blocks discarded, max= largest block vs its g_MaxFrameSize*4 allocation. Deliberately cheap (~30 clock reads/s) — safe to leave on for a full playtest, unlike GE_MIXERTRACE. The instrument of record for D202. |
live |
GE_D204_OLD |
port/src/audio.c (d204OldMode) |
Restores pre-D204 behaviour (osAiGetLength() reports the whole SDL queue depth; queueLimit back to 8192) so before/after can be A/B'd in one binary, without build-to-build variance. Temporary — remove when D204 closes. |
live (D204 A/B) |
GE_AUDIOTRACE |
src/snd.c (sndPlaySfx, sndDeactivate), src/game/propobj.c (door sounds), src/libultra/audio/load.c (alLoadParam) |
Per-sndPlaySfx resolution trace to audiotrace.log: soundIndex → ALSound*/keyMap/wavetable/base/len/book, the returned ALSoundState*, every sndDeactivate, and wavetable bind events. M-65 additions: [VOICE+]/[VOICE-] pairing every voice acquire/release with sound/state/flags (NB: written from two threads to one unbuffered FILE* — occasional torn lines, parse defensively), [SLOTWRITE] door slot ownership, [RETRIGGER-POST], [WAVELOOP] (per-sound ALADPCMloop presence), [DOORSND] every door sound state change incl. an ORPHAN-both-slots-busy verdict. M-66b addition: [EXPIRE] — posted whenever the port-side ownerless-infinite-loop expiration is scheduled (sound/state/delay/fade); this line is permanent (part of the D202 option-C fix), unlike the rest of the probe set which is removable on close. M-67/M-69 additions: [WIRE] t=µs filter=… <- table=… (src/libultra/audio/load.c, every wavetable→decoder bind, µs-timestamped) and [VOICEMAP] (src/libultra/audio/synthesizer.c, full PVoice→decoder/resampler/env-state address map at synth init — this is how offline voicedump slot addresses tie to WIRE filter addresses). M-68 additions: [EVT] t=µs type=N state=P + [STOP-EVT] … deltaUs=N (event types per src/snd.h: 1=PLAY, 2=STOP, 8=VOL, 64=DECAY, 128=END). M-71 additions: [VOL] t=µs state=… rawVol=… playing=… (src/snd.c, per-VOL-event value before the slot-volume law) and [DISTVOL] pos=(…) player=(…) dist=… vol=… (src/game/propobj.c, the distance-attenuation funnel sub_GAME_7F053894 — this is how an SFX's final volume is computed from hit position). NB: lines from the multiple file handles interleave — occasional torn/corrupt lines, parse defensively. dumppos= gives the exact byte offset into audiodump.raw (quantised to the 2880-byte audio block). Low cost. |
live (D202/D205) |
GE_PULLTRACE |
src/libultra/audio/load.c (alAdpcmPull) |
[PASTEND] assertion: fires if a voice's f->sample exceeds its wave's total samples while nOver == 0 (i.e. an unstopped voice keeps decoding past the end instead of zero-filling). Rate-limited to 40 lines. M-65: 0 hits — exhausted voices go silent correctly. |
live (D202) |
GE_DMEMWIPE |
port/src/mixer.c (aClearBufferImpl) |
Zeroes the whole software DMEM at the frame boundary (detected as alMainBusPull's clear of AL_MAIN_L_OUT, addr 1088). Tests whether audio state survives across frames in DMEM. M-65: no-op on output — ruled out. |
live (D202) |
GE_NOWET |
port/src/mixer.c (aSetVolumeImpl) |
Mutes the reverb send (wetamt = 0), isolating the reverb delay lines as a possible source of persistent signal. M-65: drone unchanged — ruled out (NB aPoleFilterImpl is still an unimplemented no-op, D199). |
live (D202) |
GE_OBJT |
src/game/chraction.c (chrSetStageFlags/chrUnsetStageFlags), src/game/propobj.c (GAS_RELEASING destruction) |
D321 stage-flag writer probe: logs every low-16-bit write to objectiveregisters1 (tick, writing chr, actiontype/aioffset, AI-list ID) plus each toxic-gas prop destruction timestamp — used to map the Facility tanks→combat-bit trigger chain. Capped, read-only, inert unset; post-release removal candidate (M-192 item 7). |
live (D321) |
GE_BANKDUMP |
port/src/romdata.c |
BANKSRC lines: the source ROM-layout bank tree per soundIndex (instrument → sound → envelope pointers + decayTime). Used to prove sound 203's decay=-1 is genuine ROM data, not a conversion bug. One-shot at load. |
live (D202) |
GE_FORCEALARM |
src/game/propobj.c (handle_alarm_gas_timer_calldamage, #ifdef PORT) |
D207 diag (temporary). Forces the in-game alarm klaxon (alarmActivate) on a frame counter — on ~2–10 s, off, on again — and emits a per-frame [D207] f=… active=… ptr_alarm_sfx=… playstate=… locked=… line to audiotrace.log. Repro harness for the "alarm SFX overrides other audio" report when a level's own alarm can't be triggered headlessly. Remove when D207 closes. |
live (D207) — temporary |
GE_KEYMAPDUMP |
port/src/romdata.c |
KEYMAP + ENVSRC lines: raw source bytes of every ALKeyMap / ALEnvelope in the ROM-layout blob, for byte-wise comparison against runtime values. One-shot at load. |
live (D202) |
GE_AUDIODUMP |
port/src/audio.c (audioSetNextBuffer) |
Raw-dumps every mixed s16/stereo/22050 Hz buffer to audiodump.raw in the CWD for offline waveform inspection. Length ÷ 88200 = seconds of audio produced. |
live (D202) |
GE_VOICEDUMP |
port/src/mixer.c (aEnvMixerImpl) |
Dumps each voice's resampled mono stream (post-resample/pre-envelope, i.e. the env-mixer input) to voicedump.raw as records of [u32 stateAddr][u32 nSamples][u64 us][s16 × n]. The µs stamp is when the block was MIXED — records are rendered ahead, so a record's samples END at its stamp (M-69: treating stamps as start offsets misplaces content by up to one ~33 ms block). stateAddr = the env-mixer state address from [VOICEMAP]. Used with scratchpad/exact_match.py for full-corpus per-voice sample verification. |
live (D202) |
GE_MIXERTRACE |
port/src/mixer.c, src/audi.c (amDmaCallback), src/libultra/audio/load.c (_decodeChunk, alLoadParam) |
Per-opcode mixer trace to mixertrace.log (DMEM addresses, ADPCM books, decoded frames, [DMAREQ]/[DMAHIT]/[DMAMISS]/[BINDTABLE]). WARNING: unbuffered per-opcode fprintf, ~25 MB/min. It slows the process enough to starve the audio thread — M-63 measured "audio at 13 % of real time" that was entirely this probe's own artifact. NEVER use it for timing-sensitive measurement; use GE_D204. Grep it, never read it whole. |
live (D202) — timing-unsafe |
GE_TEXPITCH |
port/fast3d/gfx_pc.cpp:924 (gfx_tex_pitch_fix, cached) |
=0 disables the D183 defensive de-stride in import_texture (default on). RC2/RC3 texture-shear A/B knob. |
live (RC3 not finished) |
GE_TEXRAW |
port/fast3d/gfx_pc.cpp:1079 |
With GE_TEXDUMP, also dumps the raw pre-import source bytes (texdump/rNNN_f…_s…_WxH.bin) so a decode bug can be told from a source-data bug offline (D183). |
live (RC3 not finished) |
GE_D219RAW |
src/game/image.c (texReadUncompressed/texChannelsToPixels, #ifdef PORT) |
Diagnostic-only, print-only (no behavior change): logs the runtime texture decompressor's bitstream header (texnum/format/width/height/compmethod) for every non-zlib wide-pixel image, plus a dump of the first row of decoded texels exactly as stored in the pool (post-PORT_PIXEL16, i.e. byte-swapped for the 16-bit family) for RGBA16/RGB15/IA16. No texnum filter — lets any suspect texture be cross-referenced against the offline ROM census (scratch/d219_census.txt) without narrowing by hand. |
live (D219, closed — kept for future runtime-decoder triage) |
GE_WRAPFIX |
port/fast3d/gfx_pc.cpp:3166 (gfx_set_wrap_fix) |
RC3 test override for the Video.WrapFix D74/D167 wrap-block path (atoi != 0 wins over the ini). |
live (RC3 not finished) |
GE_SAVELOG |
src/game/file.c:46, src/game/file2.c:16 (SAVELOG macro), src/boss.c:749, src/game/objective_status.c:297,343 |
Traces the campaign save/unlock chain (objective completion → end_of_mission_briefing → fileWriteSave → EEPROM). Used to confirm D157. g_savelogObjOnce gates the per-criterion dump to the bossReturnTitleStage call. |
live (strip once a few more level boundaries are playtested) |
GE_D160 |
src/boss.c:744, src/game/bondview2.c:791, src/aicommands.def:10055,10181,10236,10242,10247 (all #ifdef PORT) |
Dam exit-cutscene (D148/D160) diagnostic: trace points in bossReturnTitleStage, bondviewSetCameraMode, and AI cmds EndLevel/exit_level/CameraLookAtBondFromPad/CameraSwitch. Investigation in progress — user owes a GE_D160=1 Dam run. |
live |
GE_D243 |
src/game/front.c (menu_init, cached s_d243) |
Per-menu-transition trace with a local per-tick frame counter (D243 stale-DL-burst screen identification). Counter is NOT numerically identical to the port's internal frame count — cross-reference by elapsed offset. | live (D243) |
GE_D243CAM |
src/game/bondview2.c (bondviewFrozenCameraTick, three CAMERAMODE_POSEND branches) |
Dumps eye/look-at positions (+theta/verta on the gBondViewCutscene branch, +dt/angle/pad on the orbit branch) per call. Found M-143: the gBondViewCutscene branch fires and its look-at (field_3C4/8/C) sweeps through a filter catch-up after the cutscene warp — the shake mechanism. |
live (D243) |
GE_D243M |
src/game/bondview2.c (solo_char_load, bondviewRemovePlayerBody, playerTick; cached d243mEnabled) + src/game/objecthandler.c (drawjointlist, both region variants) + src/game/chr.c (chrTick, after_position_update:) |
M-151/M-154/M-159 probe for the Dam-abseil "frozen Bonds + shake": D243M: CREATE/REMOVE at both bodyModel sites; D243M: tick (idx/model/propptr/chrptr/cam modes/prop->pos/field_488.pos/field_3B8/field_3C4-8-C/rp = render_pos pointer, never dereferenced — M-154 fix 2: tick-time render_pos holds arbitrary stale slot contents until the first draw pass, so the pre-view root translation is logged by chrdraw only) while the model exists or POSEND/INTRO is active; D243M: draw per on-screen player render submission; D243M: chrdraw — M-154 per-frame model-draw census, one line per CHR render submission (Model* + raw root translation) while a scripted camera mode (POSEND/INTRO/SWIRL/FADESWIRL) is active; a second Bond instance = extra lines with distinct model pointers at the same frame (same model is drawn twice/frame — two passes — dedupe by (frame, model)); D243M: onscreen (M-159/M-162/M-163/M-166, chr.c) — one line per chrTick call while the probe is active: vis= posIsOnScreen's result (M-159's frustum hypothesis, REFUTED by M-160) + the live prop->pos + actiontype=/anim= (M-160/M-164: which of chrTick's branches fired, the attached-animation pointer) + aidx=/animframe= (M-166: the animation reverse-resolved to its animation_table_ptrs1 index, GE_D193A's technique, + its current playback phase — tests whether the visible chr's animation phase at cutscene-entry, not just its identity, is what differs from N64) + x2active=/x2raw= (M-162/M-163, gated on GE_D243X2: whether the decisive save/restore experiment ran this tick, and the raw pre-restore position it overrode — the whole prop->pos vector as of M-163, not just Z). Silent during FPS play, silent on the GE_D243X2 fields unless that env var is also set. Note: field_3B8 x/z read at 10× world units in the smoke capture — not plain world position. |
live (D243) |
GE_D243X2 |
src/game/chr.c (chrTick, gated additionally on d243mProbeActive()) |
Not a probe — a decisive behavioral experiment, off by default, NOT a fix. M-162/M-163: saves prop->pos right before chrTick's position-update dispatch (chr.c:2541-2617) runs, restores it at after_position_update: — before the render-matrix computation later in the same tick can see the cycled value (unlike M-161, which restored too late to have any effect). Tests whether chrTick's own animation-position write (M-160/M-164) is what produces the Dam-abseil shake. M-162 was Z-only (confirmed the mechanism on that axis, but the visible glitch persisted because that capture's chr cycled on Y instead); M-163 widened it to the whole vector. M-163's live result: NEGATIVE for the visible shake — prop->pos genuinely went smooth (log-verified) but the shake was unchanged, because the camera's look-at filter reads a different variable (field_488.pos, see GE_D243X3) that this experiment never touches. Kept in the tree as a real, useful (if ultimately not the visible-symptom fix) data point — always pair with GE_D243M=1 so the x2active=/x2raw= fields on D243M: onscreen show whether/what it overrode. Do not ship. |
live, test-only (D243) |
GE_D243X3 |
src/game/bondview2.c (playerTick, the field_488.pos write site, gated on g_CameraMode == CAMERAMODE_POSEND) |
Not a probe — the experiment that actually confirmed the mechanism, off by default, NOT a fix. M-167: skips the field_488.pos.x/y/z assignment (bondview2.c:10495-10497) entirely while POSEND is active, freezing it at whatever value it held the instant suppression engaged. field_488.pos — not prop->pos — is what the cutscene's leaky-integrator look-at filter (M-143, field_3B8/field_3C4-8-C) actually reads; it comes from render_pos (the model's animated-skeleton readback), an independent data path from prop->pos. Live-confirmed positive: user — "yep camera shake is gone now." Logs a companion D243M: f488write frame=... idx=... x3active=... x3skip=... epoch=... f488pos=x,y,z line (always-on when GE_D243M=1, independent of whether GE_D243X3 itself is set) so the freeze is log-verifiable. M-169: no longer freezes forever — re-baselines (x3skip=0, write allowed through) exactly on the tick a legitimate shot-change teleport fires (d243GetTeleportEpoch(), bumped by chrai.c's AI_TRYTeleportingChrToPad right after its real setsuboffset() call), addressing the M-168 postmortem's "camera looks the wrong way for later shots" incompleteness — untested live yet, next session's first move. Superseded: D243 shipped its permanent fix (4ccab22c, M-170), which removed the X3 suppression block from playerTick — no live site remains. Kept here as a tombstone; the generator flags it GONE. |
dead (D243 closed — probe removed) |
GE_D243X4 |
src/game/chr.c (chrTick, the render_pos snapshot/restore, gated on d243mProbeActive()) |
Not a probe — the M-168 experiment applying X3's technique to the actual draw-path consumer, off by default, NOT a fix. Snapshots model->render_pos[] after a 2-tick warm-up once POSEND engages, then holds it steady by copying the cached contents back over each subsequent tick's freshly-computed buffer — subcalcmatrices() always still runs first (never skipped; the first two attempts skipped it and crashed on a reused-pointer stale -1 tombstone, see findings.md D243 M-168). Addresses Bond's own rendered-body duplication (the consumer X3 never touched). Crash-free, stable, but same forever-freeze incompleteness as X3 pre-M-169. M-169: same teleport-epoch re-baseline as X3 — the warm-up counter now also resets (re-snapshots) whenever d243GetTeleportEpoch() changes, not only on the active/inactive edge. Untested live yet. Revert once a real root-cause fix replaces it; do not ship as-is. |
live, test-only (D243) |
GE_D243SWIRL |
src/game/bondview2.c:10480 (playerTick, cached d243SwirlTarget) |
D243 follow-up probe: GE_D243SWIRL=<chrnum> logs a per-tick line for that chr while the swirl/fade camera path is active (tick counter + state), to verify post-fix behavior of the swirl transition. Inert unless set to a non-negative chr number. |
live (D243 verification) |
GE_D193 |
src/game/frametiming.c (waitForNextFrame, #ifdef PORT) |
Per-wall-second line: render= rendered frames, sim= sim ticks delivered (Σ of the deltaFrames/g_ClockTimer fed downstream), clamped= frames whose catch-up hit FRAMETIMING_PORT_MAX_CATCHUP, maxraw= largest pre-clamp delta. Healthy steady state = render=30 sim=60 clamped=0 maxraw=2 (measured M-80, all levels idle). Use to check whether the wall-clock timer under-advances under real AI load (D193). Cheap; safe for a full playtest. |
live (D193) |
GE_D193A |
src/game/chr.c (chrTick → d193aSample, plus d193aMoved in chrUpdateAnim; #ifdef PORT) |
Per-wall-second per-ticked-chr locomotion telemetry (D193/D209): chr= chrnum, act= ACT_TYPE, speed= world-units travelled/s, gtd= g_GlobalTimerDelta, tier= act_gopos.unk59 SPEED tier (0 walk / 1 run / 2 sprint; -1 when not ACT_GOPOS), gspd= act_gopos.speed (a turn rate, not a travel rate — 0 means running straight), typ= PROP_TYPE (3=CHR, 6=VIEWER), tick=/move=/frz= chrTick entries vs chrUpdateAnim calls vs freeze-gated (move « tick = the chr is being visibility-culled out of its own locomotion; move=0 at high speed = WAYMODE_MAGIC), hid= CHRFLAG_HIDDEN, then the root-motion scalars scale / xlscale / animrate / playspeed / mspeed (model->speed) / endframe / anim ptr / aidx= that pointer reverse-resolved to its animation_table_ptrs1 index (40 walking, 41 sprinting, 42 running, 107 walking_unarmed — map via src/game/initanitable.c). GE_D193A_CHR=<n> restricts to one chrnum. This probe found D209: Cradle showed tier=1 (RUN) with aidx=40/107 (walk anims) on every sample. |
live (D193/D209) |
GE_D193B |
src/game/model.c (sub_GAME_7F06D3F4, #ifdef PORT) |
Rate-limited (1/97) raw anim root-motion decode trace (D193): joint= / flip= / base= mtx channel index / frame= / tmp= decoded s16 translation triple from the anim bitstream / angle= / anim ptr / bd=+bs= the D32 u32 bitDescriptors/bitStream addresses / njoints. M-80 Facility: base=0, angle=0 always (root-motion facing delta never non-zero — top lead), tmp ≈ (±small, ~1086 hip-height, ±small). Compare tmp/angle magnitudes against a clean N64 decomp build of the same anim+frame. |
live (D193) |
GE_D250 |
src/boss.c (bossMainloop, wraps lvlRender(), #ifdef PORT) |
Per-wall-second line: [D250] lvlRender avg=Nus n=M — average + count of lvlRender() (DL-building: culling/animation/scene traversal) calls that wall-second, isolated from gfx_run()'s software-RSP interpretation cost (already covered by the existing "frame rendered in Nus" line). Added M-120 to chase D250 (fps stuck ~45-50 on Dam, ruled resolution/MSAA/vsync/build-flags out already) — found lvlRender itself is cheap (~0.5-1.5ms avg), so it is NOT the missing ~10-15ms/frame either; the still-unexplained cost must be elsewhere in the per-retrace-message loop (scheduler/thread round-trip, audio, or something not yet isolated by a targeted timer). Cheap; safe for a full playtest. |
live (D250) |
GE_D172 |
port/fast3d/gfx_pc.cpp (gfx_sp_tri1 — SETCOMBINE dedup log + non-LOD multitex tri dump; #ifdef PORT) |
Particle magenta/cyan colour (D172). Logs each distinct SETCOMBINE + active cycletype, and — for a 2-cycle tri that consumes TEXEL1 with tex_lod=0 and a distinct 2nd tile — the tile fast3d samples for each texunit vs what the DL configured. Found D172: 40/40 particle tris configured tile 1 @ tmem 392 (fire) but sampled tile 0 (smoke) → smoke*smoke. FIXED (gfx_lod_tile_offset return rdp.tex_lod ? 0 : i). Probe kept inert. |
live (D172 FIXED, probe kept) |
GE_D157 |
port/fast3d/gfx_pc.cpp (gfx_sp_vertex + import_texture; #ifdef PORT) |
D219/D252's "rainbow particle" family — bullet-impact spark on Bunker1 door confirmed by screenshot to cycle magenta/green/cyan (M-157). Three sub-probes: original exact-color-match log (g_BulletSparkColors[] byte match → geometry_mode/LIGHTING + shaded RGB); D157T texture-identity-keyed vertex full-path log (tile 0 holds a loaded texture ≤16 KiB in frame window [90,240] → raw cn[], post-shade RGBA, LIGHTING, geometry_mode, combine_mode, tile-0 tmem/fmt/siz/addr/size/line, uv; cap 400 + summary/200); D157I import census (small textures imported in the window, deduped per source address → addr/fmt/siz/size/tile/tmem). M-158: bug did NOT reproduce in 6 identical dev-box runs — probes left live to capture the full RGBA path on the next occurrence (user Steam Deck run with GE_D157=1). |
live (D219/D252, non-reproducible as of M-158) |
GE_DVM |
src/game/gunfire.c (gunUpdateAndFire gate dump + gunRenderFirstPersonGunModels render dump; #ifdef PORT) |
1P weapon viewmodel (D215). [DVM] line: the field_87F show/hide gate terms + Gun_hand_without_item sub-terms + copy_of_body_obj_header RootNode/Switches/numRecords. [DVMr] line: 87F + cmds_emitted = gfx commands the weapon subdraw() produced (healthy ≈ 150; the D215 bug showed 1). Rate-limited ~1/s per hand. Found + verified D215 (renderdata.flags read 0 → all weapon DL nodes gated off). Probe kept inert. |
live (D215 FIXED, probe kept) |
GE_DTEX |
port/fast3d/gfx_pc.cpp:1001 |
Per-import_texture param log (dims / line / size / lod / gen_mipmaps). Used to root-cause RC2 and D159. Kept as an inert diagnostic. |
live (RC2/RC3 not finished) |
GE_TEXDUMP |
port/fast3d/gfx_pc.cpp:1020, port/fast3d/gfx_opengl.cpp:700 |
PPM dump of every uploaded texture + a fmt/siz/palfmt/palidx/pal[0..3] line. Root-caused D161 (Depot ceiling). Kept as an inert diagnostic. |
live (RC3 not finished) |
GE_D116 |
port/fast3d/gfx_pc.cpp:1710,2362, src/game/textrelated.c:265,541 (cached ge_d116) |
HUD/text "X-mirror" (D114/D116) vbo/uv trace. D114/D116 CLOSED — NOT A BUG (M-33/D168): the captures were upside-down, not mirrored. Probe is now dead scaffolding — strip on the next cleanup pass. | dead (D114/D116 closed) |
GE_D288 |
port/fast3d/gfx_pc.cpp (gfx_sp_tri1, right after the D233 trivial-reject guard; cached ge_d288) |
M-152 diag for the Silo intro stray triangle: logs any triangle whose clip-space NDC bbox covers >15% of the screen or goes non-finite, with each vertex's w and any_behind_camera. Investigation in progress — narrowed to two candidates (an unguarded w-division NaN leak in the backface-cull test just below, or the D106/D271 portal-BFS family), not yet root-caused; needs a live/headless -level_20 run to disambiguate. |
live (D288) |
GE_D236RM |
port/fast3d/gfx_pc.cpp (gfx_sp_tri1, unconditional getenv, no state kept when unset) |
M-155/pass-10 diag for D236: per-frame (num_dls) triangle count of the 0x0c184b50 render-mode class (other_mode_l exact match), heartbeat-logged every 300 frames even at zero. REFRAMED D280's "run-to-run dependent instantiation" as ordinary room-based occlusion, not a CPU-side gating bug — see ## D236 pass-10 writeup. |
live (D236 OPEN, kept for future passes) |
GE_D236ORDER |
port/fast3d/gfx_pc.cpp:1844 (gfx_sp_tri1, cached s_d236order) |
D236 pass-12 diag: per-DL sequence log of the tree-class triangles (DL index, screen-space depth, noise min/max census) to test the draw-order hypothesis. Zero cost unset; remove once D236 concludes. | live (D236 OPEN) |
GE_D236ALPHA |
port/fast3d/gfx_pc.cpp:1896 (gfx_sp_tri1, cached s_d236alpha) |
D236 pass-14 diag: for the 0x0c184b50 tree-class render mode, logs the combine/alpha state of up to 4000 matching triangles (deduped on combine change) so the captured draw can be cross-checked against the source. Zero cost unset; remove once D236 concludes. |
live (D236 OPEN) |
GE_D236ZFIX |
port/fast3d/gfx_pc.cpp:1970 (gfx_sp_tri1, cached s_d236zfix) |
D236 pass-12 test override: forces depth_update=true for the 0x0c184b50 tree-class render mode — a cheap mechanism test before deciding what a real fix looks like. Off by default; remove once D236 concludes. |
live, test-only (D236 OPEN) |
GE_D236RAW |
src/game/bg.c:2437 (#ifdef PORT) |
D236 pass-16 diag: one-shot census of the raw vertex table's alpha-byte distribution (5 buckets + min/max), camera-independent, to check whether any vertex in the whole table exceeds the low ceiling. Zero cost unset; remove once D236 concludes. | live (D236 OPEN) |
GE_D245V |
src/game/sky.c:962 (skyPortRenderPoly-adjacent, #ifdef PORT) |
D245 water-seam diag: per-call min/max of the recomputed S/T texture-coordinate ranges for the water fan vertices (rate-limited), to check whether the seam tracks a tc-shift boundary. Inert unset. | live (D245 OPEN) |
GE_D245_FIXEDSHIFT |
src/game/sky.c:946 (#ifdef PORT) |
D245 test override: pins the water tc shift k to the given integer (0..SKY_TC_MAX_SHIFT) instead of the computed per-vertex value, to A/B the allowShift=TRUE behavior. Off by default (unset = current behavior). |
live, test-only (D245 OPEN) |
GE_D252 |
port/fast3d/gfx_pc.cpp:733 (import_texture, cached s_d252) |
D252 rainbow-particle diag: one-shot per (address,size) import log of the tile state (tmem/fmt/siz/addr/size/line) for small textures so a suspect particle tile can be checked against its source bytes by hand. Remove once D252 is resolved. | live (D252 OPEN — non-reproducible) |
Dxx diagnostic probes — finding closed unless noted
| Env var | File:line | Finding / what it logged | Status |
|---|---|---|---|
GE_D51 |
port/src/libultra.c:496, src/game/model.c:125,244,544 |
msgQ 32-slot overflow watch / ModelSlot layout | dead (D51 closed) |
GE_D54 |
— (all blocks stripped M-32, commit 49ce620a) |
music seq-table ABI / endianness (ALMidiHdr) |
dead (D54 closed) — fully removed (kept here as a tombstone; the generator flags it GONE) |
GE_D154 |
src/game/bg.c:3418,3447,3632 (capped 64 calls) |
bg room-GDL call trace (room / gdlidx / vtxoff / op / raw hdr words) | dead (D154 closed) |
GE_D227V |
src/game/sky.c (skyPortRenderPoly, #ifdef PORT) |
Sky/water per-vertex dump: S/T/w/1/w, screen xy, the chosen tc shift k, the baked tc, and whether the tile capture succeeded. Found D227: one sky quad spans ~30,000 texels in S and T but Vtx.tc is S10.5 in an s16 (+/-1024 texels), so 7 of its 8 texture coordinates overflowed and wrapped -- the starburst/"two sky processes" seam. Root cause was a unit error (unk20/unk24 are already S10.5, not texels; M-82's *32 was spurious). FIXED M-100b. Probe kept inert. |
live (D227 FIXED, probe kept; useful for D229 water) |
GE_D176 |
src/game/bgfog.c:459,471, src/game/sky.c:325,381 (all #ifdef PORT) |
D176 sky/fog env-match + sky-vert trace (Path B, PR #18) | live (D176(a)/(b) OPEN) |
GE_D178 |
src/game/front.c:6586,6594 (#ifdef PORT) |
briefing-data u16 decode trace (blank-objectives, D143/D178) | dead (D178 closed) |
GE_D56 |
src/game/model.c:227,830 |
watch Model raw-offset reads |
dead (D56 closed) |
GE_D60 |
port/src/libultra.c:521,735 |
gfx frame msgQ delivery trace | dead (D60 closed) |
GE_D61 |
port/src/libultra.c:795 (s_d61opened) |
opens a one-shot log file | dead (D61 closed) |
GE_D62 |
port/src/libultra.c:477 |
osRecvMesg trace | dead (D62 closed) |
GE_D63 |
remaining: src/game/bg.c:2525,2908,2953, front.c:1410,1416,7814,7889,8083,8091,8099,8366, image.c:2453, language.c:431, model.c:4340, title.c:607 |
gun-barrel sub-DL clobber hunt (D63/D64/D65/D66 — the labels drift within the blocks). Stripped M-30 (d0789358, gfx_pc.cpp trail). Stripped M-32 (49ce620a): memp.c, bg.c:2880 (incl. the bare non-PORT d63bgprimarycount static + entry log), blood_animation.c, dyn.c, rsp.c. Remaining blocks are all #ifdef PORT + getenv-guarded and inert — strip candidates for a later pass. |
dead (D63 closed) — partially removed |
GE_D69 |
src/game/ob.c:165,227 |
stage-load (D69) object trace | dead (D69 closed) |
GE_D69BB |
src/game/bg.c:2460,2479,3020 |
D69 bg-binary layout trace | dead |
GE_D69STAN |
src/game/stan.c:266,276 |
D69 stan-tile trace | dead |
GE_D85DUMP |
src/game/bg.c:2510 |
D85 bgWidenRoomGdl 8→16B dump |
dead (D85 closed) |
GE_D85TEX |
src/game/image.c:2479, src/game/tex.c:859,1050 |
D85 texpool-full events (Depot analysis) | dead (D85 closed) — was useful for §3 |
GE_D217TEX |
src/game/image.c:234 (texInflateZlib) |
D217: logs texnum/format/numcolours/palette[0..1] straight off the decode bitstream (before any arena/pointer math) — the decode-correctness check that proved the affected texture (grip, texnum=1608) decodes byte-identically across levels, ruling out a decode/ROM-data bug. |
live (D217 open — investigation instrument) |
GE_D229 |
port/fast3d/gfx_pc.cpp (gfx_sp_tri dual-texunit probe + import_texture) |
D229: per-draw dump of both texunits' tile/tmem/fmt/siz/UV state for 2-cycle draws, plus a one-shot confirm line when the RGBA16-over-CI8 import remap fires (the fix path). Inert by default. | live (D229 fixed — probe kept for the Surface 2 / sweep verification) |
GE_D86 |
src/game/model.c:6295, src/game/objecthandler_2.c:143 |
D86 model rwdata trace | dead (D86 closed) |
GE_D87 |
src/game/ramromreplay.c:296,363,381 |
D87 ramromfilestructure endianness |
dead (D87 closed) |
GE_D88 |
src/game/prop.c:1367, src/game/stan.c:3090 |
D88 Usetup*Z propDef stream |
dead (D88 family closed; D88.4 resolved) |
GE_D90 |
remaining: src/game/bondview2.c:2146, src/game/prop.c:1391 (bondview_r.c blocks stripped M-32, 49ce620a) |
D90 bondview / prop NULL trace | dead (D90 closed) — partially removed |
GE_D96 |
src/game/chrprop.c:436,507 (cached probe) |
D96 chrprop trace | dead (D96 closed) |
GE_D104 |
src/game/bg.c:654 (rate-limited, d104c) |
D104 depth-clear trace | dead (D104 closed) |
GE_D71LOG |
port/fast3d/gfx_pc.cpp:645 |
D71 fast3d trace | dead (D71 closed) |
GE_D75 |
src/game/title.c sub_GAME_7F007F30 (capped 8) |
D75 Bug 2 gun-barrel model probe: logs chrModelInstance/gunModelInstance ptr+obj+numMatrices, render_pos, renderData.mtxlist, g_GfxMemPos, osVirtualToPhysical(render_pos) |
LIVE (D75 Bug 2 OPEN — see §F "D75 Bug 2 — RUNTIME PROBE") |
GE_D305 |
src/snd.c:420 (sndHandleEvent desync branch, #ifdef PORT) |
D305: logs the sound-preemption desync state when it is detected (after the NULL-guard fix made the crash itself impossible) — confirms whether Game.AllUnlocked correlates or was coincidental, and whether it recurs. Inert unset. |
live (D305 monitoring) |
GE_D309 |
src/game/chrai.c:810,3424, src/game/chraction.c:10139 (all #ifdef PORT) |
D309 freeze probe: logs AI label-not-found restarts (chrai.c) and the run-to-bond persistent-list step-on path (chraction.c, capped 400 lines) — the diagnostics used to root-cause the elusive complete freeze (D309/D310). Inert unset. |
live (D309/D310 fixed — kept for recurrence) |
GE_D314 |
port/src/optionsoverlay.c:681 (cached s_d314Enabled) |
D314 F10-flicker probe: logs the overlay's viGetY / visible-row count / scroll / selection each poll. Inert unset; strip candidate once D314 is triaged. | live (D314 pending triage) |
GE_D318R |
port/src/random.c:69,147 (#ifdef PORT) |
PRNG golden probe (D284/D318): logs the first 4000 gameplay-PRNG draws with tick + caller return address, and every randomSeed set — verifies the bit-exact port of random.s and which call sites consume the stream. Inert unset. |
live (D284 verification) |
GE_RSEED |
port/src/random.c:134 (#ifdef PORT) |
Test-only boot-seed override: GE_RSEED=<hex64> pins g_randomSeed at first seed instead of the wall-clock-derived value — for determinism and N64-parity tests. Inert unset. |
live, test-only |
GE_RSEED_LV |
port/src/d318watchdog.c:232 (#ifdef PORT) |
One-shot level-start seed log: logs g_randomSeed at the first tick after level start (optionally armed for a specific tick via <hex>@<tick>) — ties a saved/loaded game's PRNG state to a level for D318-class repro. Inert unset. |
live (D318 verification) |
GE_D318T |
port/src/d318watchdog.c:270 (#ifdef PORT) |
D318 watchdog timeline probe: per-tick log of the Facility ai_22 softlock signature window (c78 list/offset/attack state, c67 health) while the watchdog watches. Inert unset. | live (D318 verification) |
GE_D322 |
port/src/audio.c (audioSetNextBuffer), src/snd.c (sndD322PoolSummary) |
D322 pool-telemetry probe: one line per 5 s of wall clock with the three pool layers a long session can exhaust — sfx=count/max (8-voice SFX soft cap), evtq=n/64 (event queue occupancy; full = silent post drops, M-71), pv free/lame/alloc (24 physical synth voices shared by SFX + music; -1s before sndInit), plus the D204 pipeline fields. Cheap (~30 reads/s), campaign-safe; read together with GE_D204=1. |
live (D322 capture) |
GE_D318W |
port/src/d318watchdog.c:136 (#ifdef PORT) |
Not a probe — the D318 port-side watchdog itself, ON by default. Detects the Facility ai_22 hold-pose softlock signature (a latent N64 race, see findings.md D318) for 600 consecutive ticks and re-seeds c78's attack via the game's own fresh-attack entry point. GE_D318W=0 opts out; logs D318W: lines when it acts. Permanent fix, not strip-candidate. |
live (D318 fix — permanent) |
Notes
- The bare non-
#ifdef PORTGE_D63block inbgRenderRoomPrimary(d63bgprimarycountstatic + entry log, was ~bg.c:2876/2880) was removed M-32 (49ce620a). Every remainingGE_D63block is#ifdef PORT/#if defined(PORT)wrapped and inert. [Debug]ini keys (Debug.FrameDump,Debug.InputLog) mirrorGE_PCDUMP/GE_INPUTLOGas a fallback (env var wins) — M-26,port/src/config.c.- Not GE_-prefixed but same category:
GE_DETERMis implemented but experimental — not yet achieving its determinism goal (fixed-tick mode, §F D117, M-52 four-pass writeup);GE_DETERM_TRACE(M-52 4th pass) adds instrumented clock-event logging when used alongside it, for diffing two runs to localize remaining divergence. Both env-gated, default off, zero cost when unset.