mirror of
https://github.com/jkdansereau/goldeneye-pc-port.git
synced 2026-10-23 15:52:42 +02:00
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.
This commit is contained in:
1 parent
aedc98e247
commit
f1207e915b
9 files changed
+130
-16
No files matched your search
@@ -58,7 +58,7 @@ Two classes:
|
||||
| `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_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`). **M-201:** F12 opens a ~90-frame full-detail `D157TB` burst; continuous logging cut to a 2000-line baseline + 500 colour-match hits (the uncapped white match ran live play at 4–6 fps). | **TEMP** (D252 fixed M-201; remove after release) |
|
||||
| `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) |
|
||||
@@ -67,6 +67,7 @@ Two classes:
|
||||
| `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) |
|
||||
| `GE_D252POOL` | `src/game/image.c` (`texLoad`), `src/game/initmttex.c`, `port/src/gimgfixup.c` | **TEMP** (M-201): one line per new texture load (pool main/other, free bytes) + `POOL-FULL` events + stage pool range; `D252SYNC: N slots re-synced, M changed` per stage load (the D252 fix at work: M>0 from the 2nd stage on). Load-time only, no fps cost. | **TEMP** (D252 fixed; remove after release) |
|
||||
| `GE_D235` | `src/boss.c` (per-stage `while (!done)` block, after `memaReset`; `#ifdef PORT`) | D235 evidence probe: logs how many GL-texture-cache entries the per-stage `videoResetTextureCache()` call purges at each level load. A cold boot into a level should log 0; a death/restart (or any transition from a level that uploaded textures) logs >0 — direct evidence of the stale-entry mechanism. The clear itself is **not** gated on this var (it's the fix, see below); the var only controls the log line. | **live** (D235) |
|
||||
| `GE_D235_OFF` | `src/boss.c` (same block) | **TEMP** A/B switch: skips the per-stage `videoResetTextureCache()`, i.e. reverts the D235 fix for one run. For comparing a restart with and without the fix. | **TEMP** (D235; remove when D235 closes) |
|
||||
| `GE_D235_RELOAD=<frames>` | `src/boss.c` (same block + `bossMainloop` retrace branch) | **TEMP** reload harness: once the title stage loads after the first level (e.g. after a scripted watch Abort), wait `<frames>` retraces, then re-enter that level via `bossSetLoadedStage()`, the front end's own mission-start call. A headless death/restart without the pointer-menu walk. Pair with `GE_INPUTSCRIPT="800:START;1200:A;1300:RIGHT;1400:A"` (abort) on `-level_XX`. | **TEMP** (D235; remove when D235 closes) |
|
||||
|
||||
@@ -148,7 +148,7 @@ D248,FIXED,"Port runs a stable 30 FPS instead of 60 FPS game-wide (M-117, user a
|
||||
D249,OPEN,"LOD/culling breaks down on large open levels (Streets, Egyptian named specifically) at *default* FOV —","OPEN — M-142 static review ruled out the unscaled-far-clip and LOD-multiplier suspects (both already correct); narrowed to the portal/room-visibility BFS, needs a live screenshot+location, not more c…"
|
||||
D250,FIXED,"FPS is inconsistent/low on many levels after 60 Hz shipped (M-119, user QA; D248 follow-on).","FIXED — per-call getenv() cost was the bottleneck; rock-steady 60-61 fps after, user re-tested 1440p/4K clean."
|
||||
D251,FIXED,F10 options overlay QoL pass:,"FIXED (port-only, port/src/optionsoverlay.c)."
|
||||
D252,PARTIAL,"Particle effects cycle through a rainbow palette (M-124, user playtest of the v0.2.0-pre candidate).","PARTIALLY FIXED — a real, distinct manifestation was found and fixed, but the bug family is not fully closed, and is intermittent on BOTH platforms, not Deck-specific (corrected 2026-09-18)."
|
||||
D252,FIXED,"Particle effects cycle through a rainbow palette (M-124, user playtest of the v0.2.0-pre candidate).","FIXED (M-201, 2026-09-26, user-verified):"
|
||||
D253,FIXED,Facility (and any level with a FORCECROUCH/LADDER linked special tile) SIGSEGV on Linux/Steam Deck —,FIXED — Deck-verified:
|
||||
D254,FIXED,Linux crash handler destroyed its own logs:,FIXED — field-verified.
|
||||
D255,FIXED,Facility firefight SIGSEGV on Steam Deck (v0.2.0-pre CI bundle; D253 fix confirmed working).,"FIXED (M-140) — D3x-class 32-bit pointer truncation (vtxstore_fix_refs read Model* through the mistyped ChrRecord.chrflags 4-byte-enum alias instead of ObjectRecord.model), not the destroy-path race…"
|
||||
@@ -192,7 +192,7 @@ D310,FIXED,"RULE-2-SIGNOFF: chraiitemsize() mis-sizes 1-byte AI_PRINT records (N
|
||||
D311,FIXED,Complete freeze in modelSetAnimFrame2WithChrStuff root-motion loop —,FIXED — root cause D336 (bheadAdjustAnimation read never-written placeholder fields); the #ifdef PORT guard is retained as a safety net.
|
||||
D312,FIXED,"Throwable items (mines, grenades, etc.) can clip through walls in certain spots (user report, v0.3.0 pre-release playtest, 2026-09-18).",FIXED — bgBuildRoomVtxBounds mis-reads the G_VTX vertex count on PC (Gfx_le.dma.par bit-layout shift):
|
||||
D313,FIXED,Bullet impacts do not appear on some walls/surfaces —,"FIXED — same root cause as D312, merged diagnosis; same one-line fix."
|
||||
D314,OPEN,"F10 menu items flicker randomly/intermittently (reported on the main-menu context, user report, v0.3.0 playtest build, 2026-09-18).",OPEN — confirmed still present post-D316 fix (2026-09-18); user deprioritized chasing it further for now.
|
||||
D314,FIXED,"F10 menu items flicker randomly/intermittently (reported on the main-menu context, user report, v0.3.0 playtest build, 2026-09-18).",FIXED (user playtest 2026-09-26) —
|
||||
D315,OPEN,Menu scaling differs between main menu and in-game —,"OPEN — disclosed as a known issue, deferred to v0.3.1 (bigger design question, not a mechanical fix)."
|
||||
D316,FIXED,"Mouse-clicking some menu items glitches and lands on the item below instead (user report, v0.3.0 playtest build, 2026-09-18).",FIXED — root-caused to two compounding bugs (naive mouse->logical mapping ignoring the safe-area crop; asymmetric row hit-test band) in port/src/optionsoverlay.c + port/fast3d/gfx_pc.cpp.
|
||||
D317,CLOSED,"Crash when toggling Game.AllUnlocked back and forth in-game (user report, v0.3.0 playtest build, 2026-09-18) —","CLOSED — symptom of D316 (confirmed instant window close, no WER report; the D316 hit-band bias let a click on ""All unlocked"" fall through to the adjacent ""Quit to desktop"" row)."
|
||||
|
||||
|
+20
-2
@@ -555,7 +555,7 @@ covers D24–D69; the log continues in §H (D32 procedure, D70–D121).
|
||||
| D249 | **LOD/culling breaks down on large open levels (Streets, Egyptian named specifically) at *default* FOV — far-away geometry has visible culling issues (M-119…** — full `## D249` entry at file tail | OPEN — M-142 static review ruled out the unscaled-far-clip and LOD-multiplier suspects (both already correct); narrowed to the portal/room-visibility BFS, needs a live screenshot+location, not more code reading.… |
|
||||
| D250 | **FPS is inconsistent/low on many levels after 60 Hz shipped (M-119, user QA; D248 follow-on).** — full `## D250` entry at file tail | FIXED — per-call `getenv()` cost was the bottleneck; rock-steady 60-61 fps after, user re-tested 1440p/4K clean. |
|
||||
| D251 | ****F10 options overlay QoL pass:** — full `## D251` entry at file tail | **FIXED (port-only, `port/src/optionsoverlay.c`).** (a) The panel now computes how many rows fit the *actual* current 2D height (`maxVisibleRows()` from `viGetY()`) and scrolls… The last open symptom — the bottom row deterministically duplicating the selected item (v0.2.0-pre playtest) — is resolved by D304's `overlayUpdateScroll()` minimal-scroll rewrite; user-confirmed no longer occurring (2026-09-18). |
|
||||
| D252 | **Particle effects cycle through a rainbow palette (M-124, user playtest of the v0.2.0-pre candidate).** — full `## D252` entry at file tail | **PARTIALLY FIXED — a real, distinct manifestation was found and fixed, but the bug family is not fully closed, and is intermittent on BOTH platforms, not Deck-specific (corrected 2026-09-18).** `explosionRenderPart()`'s 4-vs-5-vertex overrun (`src/game/explosion.c`) is fixed and stays in. Initial testing suggested "fixed on Windows, recurs on Deck," but further user testing found it also recurs intermittently on Windows (same Jungle repro, not always reproducing) and is level-dependent (same repro method doesn't trigger it on every level) — not a clean platform split. Repro method (`Game.AllUnlocked` + cheats for an immediate grenade launcher) is flagged as a possible, unconfirmed factor. The unconfirmed secondary lead from this session (stale TMEM-slot `src_fmt` CI8 reroute, `port/fast3d/gfx_pc.cpp` ~line 1250) remains the best-named candidate for the residual. Not re-investigated further this session. |
|
||||
| D252 | **Particle effects cycle through a rainbow palette (M-124, user playtest of the v0.2.0-pre candidate).** — full `## D252` entry at file tail | **FIXED (M-201, 2026-09-26, user-verified): port-side D68 shim `gimgSyncCompiledGlobalDLs` only synced the compiled explosion DLs on the first stage; every later stage kept the title stage's freed texture pointers. Earlier history: PARTIALLY FIXED — a real, distinct manifestation was found and fixed, but the bug family is not fully closed, and is intermittent on BOTH platforms, not Deck-specific (corrected 2026-09-18).** `explosionRenderPart()`'s 4-vs-5-vertex overrun (`src/game/explosion.c`) is fixed and stays in. Initial testing suggested "fixed on Windows, recurs on Deck," but further user testing found it also recurs intermittently on Windows (same Jungle repro, not always reproducing) and is level-dependent (same repro method doesn't trigger it on every level) — not a clean platform split. Repro method (`Game.AllUnlocked` + cheats for an immediate grenade launcher) is flagged as a possible, unconfirmed factor. The unconfirmed secondary lead from this session (stale TMEM-slot `src_fmt` CI8 reroute, `port/fast3d/gfx_pc.cpp` ~line 1250) remains the best-named candidate for the residual. Not re-investigated further this session. |
|
||||
| D253 | **Facility (and any level with a FORCECROUCH/LADDER linked special tile) SIGSEGV on Linux/Steam Deck — `stanCheckLinkedSpecialTile` stores to a garbage…** — full `## D253` entry at file tail | FIXED — Deck-verified: user got past the crouch tile with the re-cut bundle (a *new*, later crash is D255). |
|
||||
| D254 | **Linux crash handler destroyed its own logs: `ge007.crash.log` only ever contained the SIGABRT block, never the original SEGV.** — full `## D254` entry at file tail | FIXED — field-verified. |
|
||||
| D255 | **Facility firefight SIGSEGV on Steam Deck (v0.2.0-pre CI bundle; D253 fix confirmed working).** — full writeup directly below this table (row status was stale; see M-140 update) | **FIXED (M-140)** — D3x-class 32-bit pointer truncation (`vtxstore_fix_refs` read `Model*` through the mistyped `ChrRecord.chrflags` 4-byte-enum alias instead of `ObjectRecord.model`), not the destroy-path race originally suspected. Verified live on real Steam Deck hardware (Facility + Caverns, zero crashes); Windows 21-level sweep clean. |
|
||||
@@ -600,7 +600,7 @@ covers D24–D69; the log continues in §H (D32 procedure, D70–D121).
|
||||
| D311 | **Complete freeze in `modelSetAnimFrame2WithChrStuff` root-motion loop — D156 guard hole: poisoned `framea` (`model->animframe1`) passes the frameb-only clamp and saturates to a ~3.1e9-iteration spin (Caverns, live GDB diagnosis, 2026-09-18).** — full `## D311` entry at file tail | FIXED — root cause D336 (`bheadAdjustAnimation` read never-written placeholder fields); the `#ifdef PORT` guard is retained as a safety net. 2026-09-25 user playtest incl. ~2 min of Caverns combat: 0 D311 ANOMALY / D156 lines in a ~46-min session. |
|
||||
| D312 | **Throwable items (mines, grenades, etc.) can clip through walls in certain spots (user report, v0.3.0 pre-release playtest, 2026-09-18).** — full `## D312` entry at file tail | FIXED — `bgBuildRoomVtxBounds` mis-reads the G_VTX vertex count on PC (`Gfx_le.dma.par` bit-layout shift): n=16 batches collapse to single-point bounds and fail the per-batch AABB pre-test, so their wall triangles are never ray-tested. One-line `#ifdef PORT` fix; verified by repro. |
|
||||
| D313 | **Bullet impacts do not appear on some walls/surfaces — the projectile appears to pass through them; impacts DO work on some surfaces such as doors/windows (user report, v0.3.0 pre-release playtest, 2026-09-18).** — full `## D313` entry at file tail | FIXED — same root cause as D312, merged diagnosis; same one-line fix. Doors/windows use the separate working `bgTestHitOnObj` path, hence the "surface-dependent" appearance. |
|
||||
| D314 | **F10 menu items flicker randomly/intermittently (reported on the main-menu context, user report, v0.3.0 playtest build, 2026-09-18).** — full `## D314` entry at file tail | OPEN — confirmed still present post-D316 fix (2026-09-18); user deprioritized chasing it further for now. Diagnostic probe (`GE_D314=1`) landed in `optionsOverlayHandleInput()` if revisited. |
|
||||
| D314 | **F10 menu items flicker randomly/intermittently (reported on the main-menu context, user report, v0.3.0 playtest build, 2026-09-18).** — full `## D314` entry at file tail | FIXED (user playtest 2026-09-26) — M-200 wheel-queue race fix + D287 window-thread cursor calls. Earlier: confirmed still present post-D316 fix (2026-09-18). Diagnostic probe (`GE_D314=1`) landed in `optionsOverlayHandleInput()` if revisited. |
|
||||
| D315 | **Menu scaling differs between main menu and in-game — main menu scales OK, in-game menu "seems kinda big sometimes" (user report, v0.3.0 playtest build, 2026-09-18).** — full `## D315` entry at file tail | OPEN — disclosed as a known issue, deferred to v0.3.1 (bigger design question, not a mechanical fix). |
|
||||
| D316 | **Mouse-clicking some menu items glitches and lands on the item below instead (user report, v0.3.0 playtest build, 2026-09-18).** — full `## D316` entry at file tail | FIXED — root-caused to two compounding bugs (naive mouse->logical mapping ignoring the safe-area crop; asymmetric row hit-test band) in `port/src/optionsoverlay.c` + `port/fast3d/gfx_pc.cpp`. Build-verified (golden diff PASS, worst_cell=0); interactive click verification owed. |
|
||||
| D317 | **Crash when toggling Game.AllUnlocked back and forth in-game (user report, v0.3.0 playtest build, 2026-09-18) — no dump left behind.** — full `## D317` entry at file tail | CLOSED — symptom of D316 (confirmed instant window close, no WER report; the D316 hit-band bias let a click on "All unlocked" fall through to the adjacent "Quit to desktop" row). Same fix resolves it. |
|
||||
@@ -11935,6 +11935,20 @@ This is a **perfect, smooth white-hot → yellow → orange → deep-red → fad
|
||||
|
||||
**Status: OPEN (PARTIAL) — unchanged; not reproducible headless across 4 configurations including the user's own trigger; debris-particle path statically clean for colour.**
|
||||
|
||||
**M-201 (2026-09-26, v0.4.0): ROOT CAUSE FOUND + FIXED — port-side, `port/src/gimgfixup.c` (the D68 shim). Explosion display lists kept the *first stage's* (title screen's) texture pointers for the rest of the session.**
|
||||
|
||||
- **User trigger that cracked it:** Facility explosions are fine at first and go rainbow for *every* weapon once the player reaches the middle (by the lockers), and stay broken. So it is state-dependent, not per-explosion-type (the tank = `EXPLOSION_MEDIUM` vs grenade `EXPLOSION_DEF_STANDARD` split was a red herring).
|
||||
- **Refuted on the way:** stage texpool exhaustion. `GE_D252POOL` (TEMP, `image.c` texLoad) logged 0 pool-full events; Facility's 650 KB pool is 85% full at spawn and still had ≥61 KB free at the lockers.
|
||||
- **Evidence:** the F12 burst capture (`GE_D157`) showed the debris/smoke/fire textures being read from `0x700ae2e8…0x700bb5d8`, while Facility's texpool was `[0x70102ae0..)`. `0x700ae2e8` is exactly the *title stage's* texpool start + the 8-byte texnum header. The debris quad state itself was clean (grey `cn`, LIGHTING off, MODULATEIA, IA8), so the colour came from the texture bytes.
|
||||
- **Mechanism:** `texReset()` (`image_bank.c`, called from `lv.c` on **every** stage load, after `set_mt_tex_alloc()`) re-copies the global image table from ROM and `texLoadFromDisplayList()` resolves the `globalDL_0x078…0xa50` IMAGESEG words into the new stage's pool. On N64 those ROM-copy DLs are what executes. On PC `explosion.c` executes the *compiled* shadows (`g_ExplosionDisplayLists[]`), and `gimgSyncCompiledGlobalDLs()` copied resolved words into them **only for slots still holding the `0xABCDxxxx` marker**, i.e. only on the first call (the title stage). Every later stage kept pointers into the title's freed pool region. That memory is reused by the new stage's own allocations, so explosions look right until the stage overwrites it (Facility: rooms streamed in around the lockers), then every smoke/fire/debris texture samples garbage.
|
||||
- **Why ~10 headless repros were clean:** `-level_XX` boots straight into the level (its own first stage, so its pointers are correct), and even a harnessed `level → title → level` reload lands both pools at the same address with the same load order, so stale pointers hit identical bytes. Only a real boot → title → level sequence moves the pool.
|
||||
- **Fix:** learn which compiled `G_SETTIMG` slots are IMAGESEG references on the first sync (marker still present), store them in `s_imgslot[][]`, and re-copy those slots on every call. Semantics now match N64 (fresh pointers per stage). Port-only; no game code touched.
|
||||
- **Verified:** user playtest, boot → title → Facility → past the lockers, grenades/mines/tanks all correct ("yeah that fixed it"). `D252SYNC` (TEMP, `GE_D252POOL`) logged `32 texture slots re-synced, 32 changed` at the Facility load, i.e. all 32 were stale before the fix. Golden gate 3/3.
|
||||
- **Scope:** covers every D252-family report that goes through these 17 global DLs (explosion parts, smoke, debris; `globalDL_0x000` too). The M-124/M-157 bullet-impact spark sightings are likely the same stale-pointer effect but were not separately re-tested.
|
||||
- **TEMP probes to remove when the release settles:** `GE_D252POOL` (`image.c`, `initmttex.c`, `D252SYNC` in `gimgfixup.c`); `GE_D157` F12 burst (`gfx_pc.cpp`, `video.c`); `GE_D320R explode=/cheat=`.
|
||||
|
||||
**Status: FIXED (M-201, user-verified 2026-09-26).**
|
||||
|
||||
## D255 — Facility firefight SIGSEGV on Steam Deck (v0.2.0-pre CI bundle; D253 fix confirmed working).
|
||||
|
||||
**Facility firefight SIGSEGV on Steam Deck (v0.2.0-pre CI bundle, D253 fix confirmed working — user got past the opening crouch tile).** Crash during gunplay; abort-unwind backtrace points at `sub_GAME_7F09BAC4` (`vtxstore.c:158`) ← `sub_GAME_7F09BBBC` (`:220`) ← `lvlManageMpGame` (`lv.c:1302`) ← `bossMainloop` (`boss.c:567`). Disassembling the exact CI binary at the fault PC (`0x200db135`) gives `mov 0x14(%rax),%edi; mov 0x10(%rdi),%rax`: the function walks the active PropRecord list keeping `type == 1` — which is **PROP_TYPE_OBJ, not CHR** (the decompiler cast to the union's first member and misnamed the field `ChrRecord.chrflags`; it is actually `ObjectRecord.model` at `0x14`) — then dereferences `model->obj`. Two port-relevant wrinkles: (a) the load is **32-bit zero-extended** because the decomp types the field through the `CHRFLAG` enum, so a model pointer ≥ 4 GB would truncate to garbage; (b) the loop skips records whose `model->obj` is NULL but *dereferences* `model` unconditionally, so an active object with a NULL/freed `model` faults — plausible in a firefight where destructibles (crates etc.) die. Not reproduced on WSL after equivalent play. **Register capture (D254 format, 2026-09-13):** two more Deck crashes — Bunker mid-firefight, then Frigate with ~3 NPCs alive; log: `SIGNAL: 11 / FAULT ADDR: 0x10 / Rax=0x701e5f54 Rdi=(nil) / PC 0x200db135`. `Rdi` = the record's model value = **exactly NULL** → case (b) confirmed: an active PROP_TYPE_OBJ record with a NULL `model`, dereferenced by the unconditional `mov 0x10(%rdi),%rax`; truncation (a) and stale/garbage pointers are ruled out. Faulting PC matches the recorded disassembly (`sub_GAME_7F09BAC4` = `vtxstore_fix_refs`). Grep-verified: nothing in game code ever writes NULL into `ObjectRecord.model` — `clear_model_obj()` nulls `model->obj` only, and `objInit`/spawn paths only activate a record once model ≠ NULL — so the NULL comes from either a fresh/uninitialized record slot being activated or a destroy-path ordering race (model freed/nulled while the record is still active this tick). **Fix constraint:** any guard lands in game code (`vtxstore.c`) → needs rule-#2 sign-off; `port/` cannot intercept this dereference. Next: (1) decision on whether a NULL-model skip in `vtxstore_fix_refs` qualifies as an ABI-class exception (semantics-preserving defensive guard, PD analogue check); (2) if yes, add it plus a `#ifdef PORT` trace at the deactivation/model-free sites to name the path that leaves the record model-less. **Corrected, deterministic repro (v0.2.1 user testing, 2026-09-15, superseding the initial report): the trigger is Facility's computer terminals — the ones you ACTIVATE (interact with, not shoot/destroy) to open a door for level progression.** User confirms this is consistent/reliably reproducible on those specific terminals, on Steam Deck **and** Linux generally (not Deck-hardware-specific). This is a much stronger lead than the original "sometimes during firefights" — a terminal activation deactivating/freeing its associated object model while the record briefly stays active this tick is fully consistent with the destroy-path-ordering theory above, just via a scripted trigger instead of combat destruction, and gives a deterministic repro instead of a probabilistic one. **Next session should reproduce via terminal activation specifically**, not generic combat — much cheaper to instrument. **M-140: added a diagnostic probe (`GE_D255`, off by default, `#ifdef PORT`) — `vtxstore.c`'s `sub_GAME_7F09BAC4` now skips-and-logs instead of dereferencing when it finds a NULL model on an active OBJ record (identity + state fields), and `propobj.c`'s `objFree` logs every free call for correlation. Attempted a live Windows repro this session (5 terminal activations across 3 Facility launches, one direct `-level_34` boot each time): zero probe hits, zero crashes — Windows does not reproduce this, consistent with the existing WSL-negative result. Two negatives (WSL, native Windows) against one positive (Linux/Steam Deck) points at something genuinely platform-specific (allocator/timing), not a Windows-reachable path — don't spend further Windows session time chasing a live repro.** The probes are harmless (env-gated, zero behavior change when unset) and committed so a Linux/Deck session can set `GE_D255=1` and get the trace directly. **User report, 2026-09-15 (same day, later, real Steam Deck hardware on the published v0.2.1 release — unverified by this session, logging as reported, not re-asserting the activation theory as wrong):** Facility crashes **consistently on terminals being destroyed** (shot), not (only) on activation. Frigate crashed once during this same testing pass, specifics not recorded. Shooting terminals elsewhere/other attempts did not reliably reproduce. This is closer to the *original* pre-M-139 framing (combat/destructibles triggering the NULL-model race) than the M-139 "corrected, activation-Line truncated
|
||||
@@ -12493,6 +12507,10 @@ This confirms the M-160/M-164/M-165 hypothesis (`## D173`'s own consolidated-nex
|
||||
**Status:** OPEN, likely fixed (D287 + wheel race), pending the user check above.
|
||||
|
||||
|
||||
**2026-09-26 (v0.4.0, M-201): user-confirmed.** F10 on file-select/mission-select with mouse move/click/wheel, and in-level → abort with F10 open: no flicker. Closed by the M-200 wheel-queue fix plus D287 (cursor/relative-mode calls moved to the window thread).
|
||||
|
||||
**Status: FIXED (user playtest, 2026-09-26).**
|
||||
|
||||
## D315 — Menu scaling differs between main menu and in-game — main menu scales OK, in-game menu "seems kinda big sometimes" (user report, v0.3.0 playtest build, 2026-09-18).
|
||||
|
||||
**Reported:** "Menu scaling can change between main menu / game — main menu scales OK, game seems kinda big sometimes." The "sometimes" is the key word: it tracks some state (video mode? window size? level?) rather than being constant.
|
||||
|
||||
@@ -591,6 +591,17 @@ through a converter or a runtime bswap fixup reads scrambled.
|
||||
fallback silently reset Auto-Aim/Look-Ahead to OFF. The direct-write
|
||||
look paths already gate on this; the fallbacks must too.
|
||||
|
||||
- **A port-side shadow of game data must be re-synced every time the game
|
||||
re-derives the original, not just the first time.** D252: `texReset()`
|
||||
re-resolves the global image DLs' texture pointers on *every* stage load;
|
||||
the compiled shadow copies (`gimgSyncCompiledGlobalDLs`, D68) only accepted
|
||||
a write while a slot still held its unresolved marker, so every stage after
|
||||
the title kept pointers into freed memory. It looked fine until the new
|
||||
stage overwrote that region (rainbow explosions mid-level). Key a sync on
|
||||
*which slots* are synced (learned once), never on "still unresolved".
|
||||
Headless `-level_XX` boots hide this class (the first stage is the level
|
||||
itself): repro through a real boot → title → level sequence.
|
||||
|
||||
## D. N64 hardware idioms fast3d does not emulate
|
||||
|
||||
- **Appending a port-owned 2D overlay to the game frame: hook inside
|
||||
|
||||
+33
-4
@@ -1771,6 +1771,12 @@ static void d75d_note_emit_z(uint32_t f, struct LoadedVertex* const* v_arr, int
|
||||
d75d_env[0]=rdp.env_color.r; d75d_env[1]=rdp.env_color.g; d75d_env[2]=rdp.env_color.b; d75d_env[3]=rdp.env_color.a;
|
||||
}
|
||||
|
||||
/* D252 TEMP (2026-09-26): F12 with GE_D157 set opens a full-detail D157T
|
||||
* burst (every small-texture vertex, no sampling) for ~90 frames, so a live
|
||||
* sighting is captured even after the continuous cap has dropped to 1-in-50.
|
||||
* Armed from videoEndFrame's screenshot path via gfxD157Burst(). */
|
||||
static volatile uint32_t s_d157BurstUntil = 0;
|
||||
|
||||
static void gfx_sp_vertex(size_t n_vertices, size_t dest_index, const Vtx* vertices) {
|
||||
const size_t d75_di0 = dest_index; /* D75 probe: loop below mutates dest_index */
|
||||
SUPPORT_CHECK(n_vertices <= MAX_VERTICES);
|
||||
@@ -1917,8 +1923,15 @@ static void gfx_sp_vertex(size_t n_vertices, size_t dest_index, const Vtx* verti
|
||||
static const uint8_t known[][4] = {
|
||||
{0xFF,0xFF,0xFF,0xFF}, {0xFF,0xFF,0xC8,0xFF}, {0xFF,0x00,0x00,0xFF},
|
||||
};
|
||||
for (const auto& k : known) {
|
||||
/* 2026-09-26: this match was uncapped and white (255,255,255,255)
|
||||
* hits every HUD/text/effect vertex -> ~200k lines in 18 s, 4-6 fps
|
||||
* in live play. Now: first 500 hits, then only inside an F12 burst. */
|
||||
extern uint32_t num_dls;
|
||||
static int d157m_n = 0;
|
||||
const bool d157m_on = d157m_n < 500 || num_dls <= s_d157BurstUntil;
|
||||
if (d157m_on) for (const auto& k : known) {
|
||||
if (v->v.cn[0] == k[0] && v->v.cn[1] == k[1] && v->v.cn[2] == k[2] && v->v.cn[3] == k[3]) {
|
||||
d157m_n++;
|
||||
sysLogPrintf(LOG_NOTE,
|
||||
"D157: bulletspark-color-match cn=(%d,%d,%d,%d) geometry_mode=%08x LIGHTING=%s -> shaded=(%d,%d,%d)",
|
||||
v->v.cn[0], v->v.cn[1], v->v.cn[2], v->v.cn[3], rsp.geometry_mode,
|
||||
@@ -1963,11 +1976,17 @@ static void gfx_sp_vertex(size_t n_vertices, size_t dest_index, const Vtx* verti
|
||||
* so a real, minutes-long play session doesn't drop
|
||||
* to sparse 1-in-200 sampling before the player
|
||||
* actually triggers the bug. */
|
||||
if (d157b_n <= 20000 || (d157b_n % 50) == 0) {
|
||||
static int d157burst_n = 0;
|
||||
const bool burst = num_dls <= s_d157BurstUntil && d157burst_n < 60000;
|
||||
if (burst) d157burst_n++;
|
||||
/* 2026-09-26: continuous logging cut to a 2000-line baseline;
|
||||
* the 20000 + 1-in-50 tail was a big share of the
|
||||
* live-play slowdown. Full detail now comes from F12. */
|
||||
if (burst || d157b_n <= 2000) {
|
||||
sysLogPrintf(LOG_NOTE,
|
||||
"D157T: frame=%u cn=(%d,%d,%d,%d) shaded=(%d,%d,%d) LIGHTING=%s geom=%08x combine=%llx | "
|
||||
"D157T%s: frame=%u cn=(%d,%d,%d,%d) shaded=(%d,%d,%d) LIGHTING=%s geom=%08x combine=%llx | "
|
||||
"tile0=%u tmem=%u fmt=%u siz=%u addr=%p size=%u line=%u | uv=(%d,%d)",
|
||||
num_dls,
|
||||
burst ? "B" : "", num_dls,
|
||||
v->v.cn[0], v->v.cn[1], v->v.cn[2], v->v.cn[3],
|
||||
(int)d->color.r, (int)d->color.g, (int)d->color.b,
|
||||
(rsp.geometry_mode & G_LIGHTING) ? "ON" : "off", rsp.geometry_mode,
|
||||
@@ -4108,6 +4127,16 @@ extern "C" void gfx_start_frame(void) {
|
||||
|
||||
uint32_t num_dls = 0;
|
||||
|
||||
/* D252 TEMP: see s_d157BurstUntil. No-op unless GE_D157 is set. */
|
||||
extern "C" void gfxD157Burst(void) {
|
||||
static int on = -1;
|
||||
if (on < 0) on = getenv("GE_D157") != NULL;
|
||||
if (!on) return;
|
||||
s_d157BurstUntil = num_dls + 90;
|
||||
sysLogPrintf(LOG_NOTE, "D157 burst: F12 at frame=%u, full logging to frame=%u",
|
||||
num_dls, (uint32_t)s_d157BurstUntil);
|
||||
}
|
||||
|
||||
/* F10 port-layer options overlay (port/src/optionsoverlay.c). Returns a
|
||||
* self-contained 2D display list to draw on top of the game's frame, or NULL
|
||||
* when the overlay is closed -- in which case nothing is appended and the
|
||||
|
||||
+40
-6
@@ -11,6 +11,9 @@
|
||||
#include <PR/ultratypes.h>
|
||||
#include <PR/gbi.h>
|
||||
#include "gimgfixup.h"
|
||||
extern char *getenv(const char *name); /* D324: no CRT proto via GE stdlib.h */
|
||||
#include "envflag.h"
|
||||
extern void osSyncPrintf(const char *fmt, ...);
|
||||
|
||||
#define GIMG_REGION_SIZE 0x13F8u
|
||||
|
||||
@@ -99,9 +102,17 @@ void gimgFixupGlobalimagetable(u8 *base)
|
||||
}
|
||||
}
|
||||
|
||||
/* D252 (2026-09-26): which compiled G_SETTIMG slots are IMAGESEG texture
|
||||
* references. Learned on the first sync, while they still hold the 0xABCD
|
||||
* marker; every later sync re-copies exactly these slots. */
|
||||
#define GIMG_MAX_CMDS 256
|
||||
static u8 s_imgslot[N_DLS][GIMG_MAX_CMDS];
|
||||
static int s_imgslot_learned = 0;
|
||||
|
||||
void gimgSyncCompiledGlobalDLs(u8 *base)
|
||||
{
|
||||
int i;
|
||||
int nslots = 0, nchanged = 0; /* D252 TEMP verify counters */
|
||||
u8 *end = base + GIMG_REGION_SIZE;
|
||||
|
||||
for (i = 0; i < N_DLS; i++)
|
||||
@@ -110,21 +121,44 @@ void gimgSyncCompiledGlobalDLs(u8 *base)
|
||||
Gfx *dst = s_dl_syms[i];
|
||||
int j = 0;
|
||||
|
||||
while (p + 8 <= end && p[0] != (u8)G_ENDDL)
|
||||
while (p + 8 <= end && p[0] != (u8)G_ENDDL && j < GIMG_MAX_CMDS)
|
||||
{
|
||||
/* D68/C2: texLoadFromDisplayList() has already replaced every
|
||||
* IMAGESEG w1 word in the ROM copy with a real texture pointer,
|
||||
* so the 0xABCDxxxx marker is gone from `p`. Identify the slots
|
||||
* that need syncing from the COMPILED array instead: a G_SETTIMG
|
||||
* command whose w1 is still an unresolved IMAGESEG marker
|
||||
* (0xABCDxxxx). Copy the ROM copy's resolved word into it. */
|
||||
if ((u8)(dst[j].words.w0 >> 24) == (u8)G_SETTIMG &&
|
||||
* so the 0xABCDxxxx marker is gone from `p`. On the first call,
|
||||
* identify the slots from the COMPILED array instead (a
|
||||
* G_SETTIMG whose w1 is still an unresolved 0xABCDxxxx marker)
|
||||
* and remember them.
|
||||
*
|
||||
* D252: texReset() runs on EVERY stage load and re-resolves the
|
||||
* ROM copy into the new stage's texture pool. The original sync
|
||||
* only ever copied into slots still holding the marker, i.e.
|
||||
* only on the first stage (the title screen). Every later stage
|
||||
* kept the title's pointers into its freed texture pool; once
|
||||
* the new stage's allocations overwrote that memory, explosion
|
||||
* smoke/fire/debris sampled garbage (rainbow particles). Re-copy
|
||||
* the remembered slots on every call, as the N64 does by running
|
||||
* the freshly patched ROM copy directly. */
|
||||
if (!s_imgslot_learned &&
|
||||
(u8)(dst[j].words.w0 >> 24) == (u8)G_SETTIMG &&
|
||||
(dst[j].words.w1 >> 16) == 0xABCDu)
|
||||
{
|
||||
s_imgslot[i][j] = 1;
|
||||
}
|
||||
if (s_imgslot[i][j])
|
||||
{
|
||||
nslots++;
|
||||
if ((u32)dst[j].words.w1 != *(u32 *)(p + 4))
|
||||
nchanged++;
|
||||
dst[j].words.w1 = *(u32 *)(p + 4);
|
||||
}
|
||||
p += 8;
|
||||
j++;
|
||||
}
|
||||
}
|
||||
s_imgslot_learned = 1;
|
||||
/* D252 TEMP: before the fix, every stage after the first left
|
||||
* `nchanged` explosion texture pointers stale. */
|
||||
if (GE_ENVFLAG("GE_D252POOL"))
|
||||
osSyncPrintf("D252SYNC: %d texture slots re-synced, %d changed\n", nslots, nchanged);
|
||||
}
|
||||
@@ -769,6 +769,8 @@ static void videoPreSwapCapture(void)
|
||||
|
||||
if (screenshotReq) {
|
||||
screenshotReq = 0;
|
||||
extern void gfxD157Burst(void); /* D252 TEMP, inert without GE_D157 */
|
||||
gfxD157Burst();
|
||||
static int shotNum = 0;
|
||||
char path[128];
|
||||
GE_MKDIR("ppm");
|
||||
|
||||
@@ -2575,8 +2575,27 @@ void texLoad(s32 *updateword, struct texpool *pool)
|
||||
// pointer to the start of the pool. It'll be garbage data but the
|
||||
// only other option is a crash. GBI commands contain texture IDs
|
||||
// instead of pointers, and they must be replaced with pointers.
|
||||
#if defined(PORT)
|
||||
/* D252 TEMP (2026-09-26): Facility explosions turn rainbow after the
|
||||
* player passes into the middle of the level (user). Log every new
|
||||
* texture load with pool identity + free bytes, and pool-full
|
||||
* events, to test the pool-exhaustion hypothesis. Load-time only. */
|
||||
{
|
||||
static int d252pool = -1;
|
||||
if (d252pool < 0) d252pool = getenv("GE_D252POOL") != NULL;
|
||||
if (d252pool)
|
||||
osSyncPrintf("D252POOL load texnum=%d pool=%s free=%d zlib=%d\n",
|
||||
(int)g_TexNumToLoad,
|
||||
pool == (struct texpool *)&ptr_texture_alloc_start ? "main" : "other",
|
||||
(int)texFreeBytesInBuffer(pool), (int)iszlib);
|
||||
}
|
||||
#endif
|
||||
if ((!iszlib && (texFreeBytesInBuffer(pool) < 0x10CC)) || (iszlib && texFreeBytesInBuffer(pool) < 0xA28)) {
|
||||
#if defined(PORT)
|
||||
if (getenv("GE_D252POOL"))
|
||||
osSyncPrintf("D252POOL POOL-FULL texnum=%d pool=%s free=%d\n", (int)g_TexNumToLoad,
|
||||
pool == (struct texpool *)&ptr_texture_alloc_start ? "main" : "other",
|
||||
(int)texFreeBytesInBuffer(pool));
|
||||
/* D85: env-gated -- a room rendering with placeholder textures
|
||||
* usually means the stage pool filled up here. */
|
||||
if (getenv("GE_D85TEX"))
|
||||
|
||||
@@ -22,7 +22,7 @@ void set_mt_tex_alloc(void)
|
||||
* e.g. rainbow Frigate water) -- see GE_D85TEX's pool-full line. */
|
||||
{
|
||||
extern s32 g_StageNum;
|
||||
if (getenv("GE_D85TEX"))
|
||||
if (getenv("GE_D85TEX") || getenv("GE_D252POOL"))
|
||||
osSyncPrintf("D85TEX stage=%d texpool bytes=%d (-mt token %s) range=[%p..%p)\n", (int)g_StageNum,
|
||||
(int)bytes, tokenFind(1, "-mt") ? "present" : "ABSENT",
|
||||
(void *)ptr_texture_alloc_start.start, (void *)ptr_texture_alloc_start.end);
|
||||
|
||||
Reference in new issue
Block a user