forked from Ports/goldeneye-pc-port
docs(D217/D211): M-86 — D217 root cause characterised; D211 aim-zoom verified
D217: two defects found. (1) The visible green grip/hand is upstream of fast3d — the grip's count=1 model G_LOADTLUT is fed different source bytes per level (Facility 1a 00 -> 0x1A00 green; Bunker 00 01 -> black); every larger model TLUT carries a 2-entry leading zero pad, so the port's model-TLUT source pointer is likely 4 bytes low (or a d43_emit.py blob offset). fast3d byteswap is correct (proven by Bunker). (2) A real CI texture-cache collision (no palette-content key) — fix landed on branch fix/d217-model-texture-palette f6fc0399 (palette FNV-1a hash in the CI key); owes a 21-level sweep; does not fix (1). porting-notes + graphics- backlog updated. D211: aim-zoom compose verified correct by construction (single post-lerp multiply at the fr.c chokepoint; zoom ratio preserved). Only a low-risk cosmetic eyeball remains (viewmodel/frustum-cull use unscaled fovy). Claude-Session: https://claude.ai/code/session_016TaKy6AAsVUTfjkhrX6CjG
This commit is contained in:
1 parent
2cdecd3aa6
commit
6b66c9ddc9
4 files changed
+21
-3
No files matched your search
@@ -22,6 +22,7 @@ once the game is playable start-to-finish without crashes.
|
||||
| **D178** | Objectives not listed correctly on the pre-mission briefing screen (the mission overview shown before starting a level). | **M-36 triage: the objective lines are BLANK, not wrong.** `GE_STARTMENU=10` Dam briefing renders the header fine ("OHMSS / Agent: James Bond / Mission 1: Arkangelsk / Part i: Dam / PRIMARY OBJECTIVES:") then just the bullet "a." with no text and no b/c/d. → **D143/D151 blank-text family** (`langGet()` → NULL → silent blank), *not* a propDef walk / difficulty decode. **Family B** in `docs/dev/M-36-TRIAGE.md` — find which lang bank the front-end briefing flow fails to load. Re-confirm the exact shape in a *real* briefing (the `GE_STARTMENU` boot skips real save state). Close relatives: D151 (watch objective text blank — type-35/23 `text` slot u16→u32 fix, M-28; interactive re-verify was still pending), D143 (briefing/objective text blank via `langGet` NULL), D157 (objective difficulty byte offset — could make difficulty-gated objectives appear/disappear wrongly). The pre-start briefing is a *different surface* from the in-level watch, so D151's fix may not cover it. **Next:** ask user which level + what exactly shows vs expected; compare against `docs/dev/LEVEL-OBJECTIVES.md` (regenerated post-D157); if lines are blank → D143/D151 family (`langGet` NULL guard = silent blank), if wrong objectives listed → propDef type-23 record walk / difficulty decode. |
|
||||
| **D175** | In-game stutter / brief hang during normal play — observed opening a door on Runway (mission 3, `-level_35`) and also on Surface. No backtrace captured (gdb attach missed the window; it self-recovered). | **Observed, not investigated** (user QA report). Matches known transient-hang classes rather than looking new: D155 (post-slow-load catch-up spiral — clamped), D156 (anim NaN loop — guarded, prints one-shot stderr diag if it fires), D134 (SP/DP task-done event swallowed — fixed M-22, Runway was an original victim), D147/D152 (audio `s_imLock` steal = ~2 s hiccup). Door-open specifically = new room first visible → one-off texture-import/frame spike (likely benign). **Next:** reproduce under `tools_pc/debug.ps1` with the box idle; check console for the D156 stderr diag; if it recurs, attach gdb mid-hang and pattern-match `bt` (import_texture = benign slow frame; model.c:3131 spin = D156; pthread_mutex_lock on s_imLock = D147/D152). |
|
||||
| **D176** | Surface exterior renders wrong (two co-located defects). **(a)** Sky is solid **black** — the skybox / sky gradient does not draw at all (reference: warm sunset cloud gradient). **(b)** The perimeter **tree-line backdrop renders as grey TV-static / comb interlacing** instead of snow-laden pines — the geometry is present and roughly the right silhouette but every tree texture is scrambled noise. Ground, tunnel and the dish/bunker building draw fine. | **Observed, screenshots committed** (M-35, user QA; `docs/img/bugs/d176-surface-exterior-our-build.png` vs `-reference.png`). Level is the shared Surface exterior — user reached it Dam→Surface on Agent so this is **Surface (i) `-level_36`** (confirm it also affects `-level_43`). **(a)** black sky: **M-36 partial (findings.md "D176(a)").** Env data is correct (`Sky.Clouds=1`, `CloudRGB=240,120,30`, `SkyImageId=0`); `skyRender` runs the cloud path and classifies corners correctly (switch case 12), but `skyRenderTri`/`skyRenderFull` emit nothing — not even the `env->RGB` fill shows. Not root-caused: next is probing `sub_GAME_7F097388` projected coords, verifying `skywaterimages[0]` resolves on PC (`image_bank.c` GIMG offset `0xFB4`), and a fast3d sky-tri trace. `GE_D176=1` probe shipped. **M-37: ROOT-CAUSED** — `skyRenderTri`/`skyRenderFull` build geometry *exclusively* as `G_RDPHALF_*` immediates, which `gfx_pc.cpp` deliberately no-ops (identical no-op in the PD port); fix = decode the RDPHALF tri-raster stream in fast3d (~1 session, genuine new code). **M-42: `-level_22` (Statue) confirmed a clean headless repro** — bare boot, frame ~360, upper half pure black; use it to verify the decoder. **M-47 + read-only consolidation:** Path B draft exists (`scratchpad/sky.patch`, `skyPortRenderPoly` PORT hook — geometry+texture reach GL, two unit defects open) and the full RDPHALF wire format is now decoded (S15.1 half-packing, 8-component perspective-interpolated vertex vectors); next session is mechanical per `docs/dev/D176a-SKY-NOTES.md`. `-level_36`/`-43` intro cams face down (not a sky test without `GE_INPUTSCRIPT`). **M-82: PR #18 rebased onto main + Defect-1 fix applied** — cloud tile is `s_skywaterimages[0]` = 64×64 IA8; the patch's `tc = S·32` scale is correct (matches GE's `width<<5` idiom, `glass2.c:678`), the bug was `(s16)` overflow on the ±20k-texel horizon S/T; fix = per-primitive phase-fold to the 64-texel period (`sky.c` `skyPortRenderPoly`, `#ifdef PORT`). Builds clean, no crash (bunker1/statue/surface1 verify PASS). **Unverified visually** — owed: one `-level_22` screenshot (see `D176a-SKY-NOTES.md` "verification ask"). Defect 2 (horizon-band coverage / straddling tris) still open → adaptive tessellation. **(b)** tree/rock static: **M-36 triage — NOT the D159 family.** `texSwapAltRowBytes` is a full `#ifdef PORT` no-op (`image.c:2199`), it cannot cause this. Real cause: `port/fast3d/gfx_pc.cpp` `import_texture_*` assume `line_size_bytes == full_image_line_size_bytes` (`SUPPORT_CHECK`; rgba16's is commented out *"trips ... with a garbage size"* `:658`) → non-PoT / sub-rect tiles get a flat linear read → diagonal row shear = grey static. **Family A** in `docs/dev/M-36-TRIAGE.md`. Fix: honour `line_size_bytes` as the real source pitch. Same defect as D182(2). |
|
||||
| **D217** | 1P weapon viewmodel + character-model CI textures render with a wrong palette on many levels / multiple weapons — PP7 grip + hand render flat green on Facility & Depot, "weird or missing" gun textures elsewhere; Bond's watch face too. Surfaced by D215 (viewmodel now draws). Clean on Bunker1. | **Root cause characterised (M-86), needs an upstream/asset fix — full detail in `findings.md` §F D217.** Two defects: ① **visible green = wrong palette bytes arriving from the model-DL / segment path** (the grip's `count=1` `G_LOADTLUT` reads `1a 00`→`0x1A00` green on Facility vs `00 01`→black on Bunker; every larger model TLUT has a 2-entry leading zero pad ⇒ port's model-TLUT source pointer likely 4 bytes low, or a `d43_emit.py` blob-placement offset — fast3d byteswap itself is correct). ② **CI texture-cache collision** (no palette-content key) — proven, **fix landed on branch `fix/d217-model-texture-palette` `f6fc0399`** (palette FNV-1a hash in the CI cache key), owes a 21-level `verify.sh sweep`; does not fix ①. |
|
||||
| **D174** | "No blood effect" — user reports no visible blood when shooting guards / into surfaces. | **User QA report; likely D120, unverified.** Decals: the D120 `PointUsage[]` converter fix (M-30) was never interactively verified and only takes effect after a full sidecar regen (`d43 && d69 && d88 --regen`) — check whether the playtest build had regenerated sidecars before concluding the fix failed. Spray: D172 observed the particle spray *drawing* (wrong color, magenta/cyan) on Silo — if the user sees no spray either, that's new info (different weapon/range, or the D172 decode is worse than "wrong color"). First step: BUNKER1 firefight with regenerated sidecars; if decals still absent, instrument the `chr.c:3328` decal walk. |
|
||||
| **D173** | Third-person Bond model (the player-representing figure shown at level start) spawns too high — floats well above the ground "a lot of the time" while the actual player spawn is correct. Same symptom on the Cuba end credits: JB stands ~6 ft in the air above Natalya. | **Confirmed still present across the full 21-level playthrough (M-49).** **Observed, not investigated** (user QA report). D75 animated-model family but a *position* defect, not absence — M-33 confirmed the gun-barrel Bond renders fine upright. Suspects: (a) intro/credits puppet chr spawn position vs the model's base transform; (b) anim root-motion accumulation (`modelSetAnimFrame2WithChrStuff`, D156 area) offsetting the rendered joints above the body origin; (c) a `coord3d`/packed-float decode in the intro camera/model setup. "Player spawning is OK" localises it to the render-side model, not `g_CurrentPlayer` state. |
|
||||
| **D182** | SELECT FILE screen degrades after navigating away and back (main menu → file select, or backing out of file select and re-entering). On the fresh screen the 007 gun-barrel spiral / Bond silhouette renders behind the file boxes; after a round-trip **the Bond image stops drawing entirely** and the whole background gains the **comb / interlacing** artifact (horizontal line scramble). File boxes, text and the Copy/Erase icons still draw. | **Observed, screenshot committed** (M-35, user QA; `docs/img/bugs/d182-file-select-backout-20260902.png`). Two symptoms, likely linked: **(1)** background 3D image stops rendering after re-entry → a front-end model/DL whose `render_pos` / segment binding is only valid on first construction (D149 / D75 `constructor_menuXX` family — the dossier/wallet models have the same "renders once" behavior). **(2)** interlacing = **M-36 triage: Family A**, the `import_texture_*` `line` vs `full_image_line` shear (see D176(b) / `docs/dev/M-36-TRIAGE.md`) — **not** the D159 `texSwapAltRowBytes` path (that is a full PORT no-op). Open question: why only *after* a round-trip — first import may land `line == width`, re-import may not, or the tile descriptor is stale on re-entry. Repro: `GE_STARTMENU` to file-select, back out with ESC/B, re-enter; `GE_DTEX` on the spiral texture across both visits. Shares defect (2) with D176(b). |
|
||||
|
||||
@@ -59,7 +59,7 @@ D213,PARTIAL,On-screen FPS readout —,"LANDED, port-only. Build clean; bunker1
|
||||
D214,PARTIAL,"Keyboard rebinding — [Input.Bind] config section (M-83, port-only, PD parity, Phase 4 debt).",PARTIAL — M-84: code PASS (alt/ctrl); one doc-only fix.
|
||||
D215,FIXED,1P weapon viewmodel never drew in-level —,"FIXED (src/game/gunfire.c, #ifdef PORT)."
|
||||
D216,PARTIAL,"Game.SkipIntro — boot straight to the file-select menu (M-83, port + one #ifdef PORT game-code line, PD parity, Phase 4 quick win).",LANDED + VERIFIED this session.
|
||||
D217,OPEN,1P weapon viewmodel + third-person Bond model textures render garbled —,OPEN — repro pinned to FACILITY (M-85).
|
||||
D217,OPEN,1P weapon viewmodel + third-person Bond model textures render garbled —,OPEN — root cause characterised (M-86); needs an upstream/asset-pipeline fix.
|
||||
D218,PROPOSED,Wide FOV exposes the per-level draw-distance / fog boundary —,"Logged (M-84), fix proposed + parked."
|
||||
D85,OPEN,room primary/secondary DL → texLoadFromGdl garbage,"OPEN (safety-netted; widen/pool fixes landed, geometry now renders)"
|
||||
D86 · D87,RESOLVED,modelInitRwData truncated ptr · attract-demo BE ramromfilestructure,resolved
|
||||
|
||||
|
@@ -427,13 +427,13 @@ covers D24–D69; the log continues in §H (D32 procedure, D70–D121).
|
||||
| D180 | **Native-PC input pass (M-XX QoL run, `qol/native-pc-input-menu`, port-only).** Three parts, all config-gated with default = prior behaviour: **(WI-1) `Input.MouseCaptureMode`** (0 = legacy always-grab; 1 = Quake-style click-to-lock) in `port/src/input.c` + `port/src/video.c` — in mode 1 the OS cursor is free until a click lands in the game window (`SDL_MOUSEBUTTONDOWN` → `inputNotifyClick`), and ESC / focus-loss / entering any front-end menu (`current_menu != MENU_RUN_STAGE`) frees it again; re-entering a stage while still "armed" re-locks so unpausing needs no click. `reconcileGrab()` runs once per controller-0 poll. Mouse buttons are suppressed from the game while the cursor is free in-stage (no phantom fire). Controller paths untouched. **(WI-2) absolute-cursor menu tracking** — when capture mode is on and the cursor is free in a menu, the D165/D169 pointer P-controller takes its target from the real cursor's absolute window position mapped onto the live virtual front-end rect (`getPlayer_c_screen*`), giving true 1:1 tracking instead of the relative-delta estimator. Relative path unchanged for legacy/grabbed. **(B3) `Input.MouseAimSpeed` default 25 → 16** (aim mode still overshot at 25 per the backlog). | LANDED, port-only, no `#ifdef PORT`. Build 241/241, `-level_09` framediff 3/3 within threshold (unregressed), `GE_STARTMENU` menu boot crash-free. **Feel-checks owed** (headless can't drive the mouse): click-to-lock ergonomics, absolute menu tracking, the new aim-speed default. |
|
||||
| D181 | **`Game.ScreenShakeIntensity` — first route-(b) `src/` gameplay-cosmetic hook (M-XX QoL run).** `src/fr.c viShake()` gains a single `#ifdef PORT` line — `intensity *= portScreenShakeScale;` (extern `f32`, defined + `configRegisterFloat`'d 0.0–10.0 in `port/src/video.c`) — before the existing clamp. Default `1.0f` ⇒ exact no-op, every headless golden dump byte-unaffected; `0.0` disables explosion/effect screen shake, up to `10.0` exaggerates it. N64 build (no `-DPORT`) keeps the original line verbatim. Precedent per `docs/BACKLOG.md` "Fun features → screen-shake intensity slider" and AGENTS.md #2's documented-exception path. Same policy class as a future FOV hook. | LANDED behind config, default = original. Build green. |
|
||||
| D184 | **F10 port-layer options overlay — the approach-(C) surface from `OPTIONS-MENU-PLAN.md` (M-37).** New `port/src/optionsoverlay.c` + `port/include/optionsoverlay.h`: an immediate-mode overlay drawn as its own fast3d 2D DL, appended after the game DL in `gfx_pc.cpp gfx_run()` (`optionsOverlayEmit()` → NULL when closed ⇒ **zero bytes appended, golden dumps byte-identical**). F10 toggles (`video.c videoPumpEvents`, next to F12); while open, `input.c inputComputePad(0)` returns a neutral pad and routes arrows/enter/wheel/mouse-buttons to `optionsOverlayHandleInput()` (mirrors the WI-1/D180 swallow). ESC closes; `configSave()` on close. `config.c` gains `configForEachOption()` + a `configSetOptionMeta()` label/step/enum side table (no per-key knowledge in config.c). Live knobs (VSync/FpsCap/TextureFilter via `videoRequestLiveConfig()`; mouse + screen-shake via the registered pointer) apply immediately; MSAA tagged "(restart)". v1 rows per plan §3: VSync, FpsCap, MSAA, TextureFilter, MouseAimSpeed, MouseTurnSpeed, MouseInvertY, MouseCaptureMode, ScreenShakeIntensity. | **LANDED, port-only, no `#ifdef PORT` in `src/`.** Build links 241/241. **Runtime verification NOT run this session** — the headless env kills the GUI process after ~8 s and `gfx_opengl_dump_bound_fbo` writes 0-byte PPMs here, so framediff / 60 s soak / overlay-frame eyeball are all owed on an interactive machine. Overlay-closed no-op is code-evident (emit returns NULL before touching the DL buffer). |
|
||||
| D211 | **PC FOV slider — `Video.FovScale` (M-83 QoL wave, port-only, `WIDESCREEN-FOV-PLAN.md` Phase 4).** fast3d applies a multiplicative half-angle scale to every *perspective* projection matrix as it is loaded (`gfx_apply_fov_scale()` in `gfx_pc.cpp gfx_sp_matrix`, on the `G_MTX_PROJECTION | G_MTX_LOAD` path only — GE always reloads its view projection via `guPerspectiveF` at `fr.c:725`, so a relative `G_MTX_MUL` onto an already-scaled matrix cannot compound). Columns 0 and 1 are scaled by the same factor `tan(half_orig)/tan(half_adj)` → H and V FOV widen uniformly, aspect ratio exactly preserved (no stretch), and because the game rewrites fovy every frame for aim-zoom the scale composes with zoom for free. Ortho matrices (`P[3][3]==1`) are left untouched so HUD/menu 2D is unaffected. `Video.FovScale` = percent of the original vertical FOV, 50–150, **default 100 ⇒ `g_fov_scale == 1.0f` ⇒ guarded early-return, zero FP ops on the matrix ⇒ golden dumps byte-identical**. F10 overlay row "FOV scale %". Math checked offline (`s=1.25 → fovy 60°→75°`, aspect held at 1.3333). | **PARTIAL — M-84 refix: game-side FOV scale (`src/fr.c`, `#ifdef PORT`), front-end gated, user-verified.** Original M-83 note follows. LANDED, port-only; build clean; `bunker1` framediff worst_cell 12.219 vs 12.221 baseline (= render noise, no-op confirmed). **M-84 refix — the fast3d hook only scaled *pure* perspective loads; GE draws the world through `g_CurrentPlayer->field_10E0`, a projection × view matrix pre-combined on the CPU (`bondview2.c:8224`) that failed the `gfx_apply_fov_scale` guard, so only the pause/watch model + sky FOV changed (user report).** Moved the scale game-side to the `guPerspectiveF` chokepoint (`fr.c:718`, `#ifdef PORT`, `frFovY *= portFovScale`, clamp 160°), which lands *before* the CPU combine; fast3d `gfx_apply_fov_scale`/`gfx_set_fov_scale` removed; `port/src/video.c` sets `f32 portFovScale`. Also gated to `lvlGetCurrentStageToLoad() != LEVELID_TITLE` — the front end (main menu / file+mission select / briefing) renders through the same path (`lv.c lvlRender`) and its 3D is authored at a fixed FOV. **User-verified: front end unaffected + in-level view widens (Dam tunnel at high FovScale).** M-85 headless nit check (`-level_09`, FovScale 100 vs 120): **HUD-static confirmed** — the ammo counter ("7 | 93") sits at the identical bottom-right screen position in both, and the door-arrow stays square (no 4:3 stretch); viewmodel scales down + stays bottom-right anchored. **aim-zoom-compose still owed** (needs the aim button held — not scriptable headlessly here). Caveat: `currentPlayerSetCameraScale` frustum-cull / screen↔world math stays at nominal FOV → edge geometry culls a hair early at large scale (cosmetic). Wide FOV also exposes the per-level draw-distance boundary → **D218** (logged, parked). |
|
||||
| D211 | **PC FOV slider — `Video.FovScale` (M-83 QoL wave, port-only, `WIDESCREEN-FOV-PLAN.md` Phase 4).** fast3d applies a multiplicative half-angle scale to every *perspective* projection matrix as it is loaded (`gfx_apply_fov_scale()` in `gfx_pc.cpp gfx_sp_matrix`, on the `G_MTX_PROJECTION | G_MTX_LOAD` path only — GE always reloads its view projection via `guPerspectiveF` at `fr.c:725`, so a relative `G_MTX_MUL` onto an already-scaled matrix cannot compound). Columns 0 and 1 are scaled by the same factor `tan(half_orig)/tan(half_adj)` → H and V FOV widen uniformly, aspect ratio exactly preserved (no stretch), and because the game rewrites fovy every frame for aim-zoom the scale composes with zoom for free. Ortho matrices (`P[3][3]==1`) are left untouched so HUD/menu 2D is unaffected. `Video.FovScale` = percent of the original vertical FOV, 50–150, **default 100 ⇒ `g_fov_scale == 1.0f` ⇒ guarded early-return, zero FP ops on the matrix ⇒ golden dumps byte-identical**. F10 overlay row "FOV scale %". Math checked offline (`s=1.25 → fovy 60°→75°`, aspect held at 1.3333). | **PARTIAL — M-84 refix: game-side FOV scale (`src/fr.c`, `#ifdef PORT`), front-end gated, user-verified.** Original M-83 note follows. LANDED, port-only; build clean; `bunker1` framediff worst_cell 12.219 vs 12.221 baseline (= render noise, no-op confirmed). **M-84 refix — the fast3d hook only scaled *pure* perspective loads; GE draws the world through `g_CurrentPlayer->field_10E0`, a projection × view matrix pre-combined on the CPU (`bondview2.c:8224`) that failed the `gfx_apply_fov_scale` guard, so only the pause/watch model + sky FOV changed (user report).** Moved the scale game-side to the `guPerspectiveF` chokepoint (`fr.c:718`, `#ifdef PORT`, `frFovY *= portFovScale`, clamp 160°), which lands *before* the CPU combine; fast3d `gfx_apply_fov_scale`/`gfx_set_fov_scale` removed; `port/src/video.c` sets `f32 portFovScale`. Also gated to `lvlGetCurrentStageToLoad() != LEVELID_TITLE` — the front end (main menu / file+mission select / briefing) renders through the same path (`lv.c lvlRender`) and its 3D is authored at a fixed FOV. **User-verified: front end unaffected + in-level view widens (Dam tunnel at high FovScale).** M-85 headless nit check (`-level_09`, FovScale 100 vs 120): **HUD-static confirmed** — the ammo counter ("7 | 93") sits at the identical bottom-right screen position in both, and the door-arrow stays square (no 4:3 stretch); viewmodel scales down + stays bottom-right anchored. **aim-zoom-compose — M-86 static analysis: correct by construction.** `bondview2.c:3123` lerps `zoominfovy` then `viSetFovY()` stores it in `g_ViBackData->fovy`; `fr.c:737` applies `frFovY = fovy * portFovScale` **once**, at the per-frame chokepoint, *after* the zoom lerp. Hipfire and zoomed states scale by the same factor so the zoom magnification ratio is preserved exactly; no double-application; the smooth zoom interpolation is unaffected (scale is post-lerp). Residual human eyeball (low risk, cosmetic) at FovScale ~120–150 + full aim-zoom: the viewmodel (`c_perspfovy`, `bondview.c:644`) and frustum-cull (`currentPlayerSetCameraScale`) use the *unscaled* fovy, so a subtle viewmodel-scale or edge-cull-early mismatch is possible; the scope reticle is 2D screen-space, unaffected. Caveat (same root): edge geometry culls a hair early at large scale (cosmetic). Wide FOV also exposes the per-level draw-distance boundary → **D218** (logged, parked). |
|
||||
| D212 | **Anisotropic-filtering config wiring — `Video.Anisotropy` (M-83 QoL wave, port-only).** The fast3d GL backend already implemented `set_anisotropy_level` / `get_max_anisotropy_level` and applied `GL_TEXTURE_MAX_ANISOTROPY` on mipmapped tiles with a hardcoded `current_anisotropy_level = 4`; there was no way to configure it. Added the `gfx_set_anisotropy_level()` extern-C wrapper in `gfx_pc.cpp` (clamps to `[1, GL max]`, `reset_texture_state()` so live changes take), declared in `gfx_api.h`, and `Video.Anisotropy` (1–16) in `port/src/video.c` wired through `videoApplyImageOptions()` on init + live-config. **Default 4 = the value fast3d already applied ⇒ no visual delta at defaults.** F10 overlay row "Anisotropic". | **LANDED, port-only.** Build clean. **Owed:** eyeball at 16× on a grazing surface (Depot roof / Dam walkway) vs 1×. |
|
||||
| D213 | **On-screen FPS readout — `Video.DisplayFPS` (M-83 QoL wave, port-only, PD parity).** `port/src/optionsoverlay.c` samples frame time once per emitted frame (`fpsTick()` via `sysGetMicroseconds()`, ~0.5 s window) and, when enabled, emits a small top-right green "N FPS" DL even while the F10 panel is closed. Config-only knob (kept off the 13-row panel, which is at its documented layout limit — matches PD, whose `DisplayFPS` is also file-only). **Default 0 ⇒ `optionsOverlayEmit()` still returns NULL when the panel is closed ⇒ golden dumps byte-identical.** | **LANDED, port-only.** Build clean; `bunker1` no-op confirmed. **Owed:** eyeball that the counter reads a sane ~30 and doesn't overlap the HUD. |
|
||||
| D214 | **Keyboard rebinding — `[Input.Bind]` config section (M-83, port-only, PD parity, Phase 4 debt).** `port/src/input.c` gains 12 gameplay actions (Forward/Back/StrafeLeft/StrafeRight/TurnLeft/TurnRight/Fire/Aim/Action/Cancel/LeanLeft/Start), each a comma-separated list of up to 4 SDL scancode names (`Input.Bind.Forward = W,Up` …). `inputRebuildBinds()` (called from `inputInit()`, after `configLoad()`) parses them via `SDL_GetScancodeFromName`; unknown names log a warning and are skipped. The old hardcoded `keyDown(ks, SDL_SCANCODE_*)` block in `inputComputePad` is replaced by `actHeld(ks, IA_*)`. Mouse buttons (fire = LMB, aim = RMB) stay hardwired; the mouse-wheel weapon-cycle A-pulse is untouched. **Defaults reproduce the previous FPS layout exactly — verified: all 20 default key names round-trip to the identical `SDL_Scancode` the old code used (`SDL_GetScancodeFromName` test, 0 mismatches).** Config-file only (no in-overlay text entry — matches PD, whose rebinding is also file-driven); a fresh `ge007.ini` gets the full `[Input.Bind]` block with the defaults visible. | **PARTIAL — M-84: code PASS (alt/ctrl); one doc-only fix.** LANDED, port-only, no `#ifdef PORT`; build clean. One doc bug fixed (no code change): `config.c` groups keys by the substring *before the last `.`*, so the section header is **`[Input.Bind]`**, not `[Bind]` — earlier finding/commit/QOL text was wrong. Working syntax: put `Fire = Left Alt` under `[Input.Bind]`, or write a fully-qualified `Input.Bind.Fire = Left Alt` line anywhere (`config.c:307`). Separately: the pause→**watch menu** does not wire Esc→back (still Enter to open/close/back) — pre-existing, `options.c`, minor. |
|
||||
| D215 | **1P weapon viewmodel never drew in-level — `gunRenderFirstPersonGunModels` builds its `ModelRenderData` from a reinterpret across separately-declared adjacent globals that only works on N64 (M-83, D3x/ABI class; retires VIEWMODEL-RESEARCH.md).** The N64 line `renderdata = *(ModelRenderData *)&D_80035CC0;` (`gunfire.c`) casts the lone `u32 D_80035CC0 = 0;` to a whole `ModelRenderData` and reads on into the next declared global, `u32 D_80035CC4[] = {1, 3, 0, 0, …}` (`gun.c`) — yielding `{ basemtx=0, zbufferenabled=1, flags=3, rest=0 }` **only because the N64 linker lays the two globals out contiguously in declaration order and pointers are 4 B**. On x86-64 it breaks twice: `ModelRenderData`'s three pointer members (`basemtx`/`gdl`/`mtxlist`) widen 4→8 B so `flags` is no longer at struct offset 8, and C guarantees nothing about the two globals' relative placement. Measured (`GE_DVM=1`, BUNKER1): `renderdata.flags == 0`, so **every** weapon-model DL node was gated off at `modelRenderNodeGundl` (`(renderdata->flags & 1)`), and `subdraw()` for the weapon emitted just its `gSPSegment` — 1 gfx command instead of ~150. All three VIEWMODEL-RESEARCH.md ranked suspects were ruled out by the same probe first: `field_87F==1` (gate open), `RootNode` valid, `numRecords 22 < 192` (pool fine), node walk visits 24+ valid `DL`/`SWITCH`/`BSP` nodes. **Fix:** `#ifdef PORT` → set the three fields explicitly (`zbufferenabled=TRUE; flags=3;`), N64 line verbatim under `#else`. ABI/layout only, no logic change. | **FIXED (`src/game/gunfire.c`, `#ifdef PORT`).** Build clean. `GE_DVM=1` post-fix: `flags=0x3`, weapon `subdraw()` now emits **153** gfx commands (was 1). Regression sweep crash-free. `GE_DVM` gate+render probes left in, env-gated (cf. `GE_D193*`). **Owed:** one BUNKER1 eyeball that the gun model is actually visible + correctly posed (headless can't see pixels; the DL is now provably generated). The committed `bunker1` golden will need re-baselining — the gun now renders in-frame. |
|
||||
| D216 | **`Game.SkipIntro` — boot straight to the file-select menu (M-83, port + one `#ifdef PORT` game-code line, PD parity, Phase 4 quick win).** New `s32 portSkipIntro` (`port/src/video.c`, `configRegisterInt("Game.SkipIntro", 0, 1)`), read once in `src/game/lv.c` at `LEVELID_TITLE` stage load — in the `else` of the existing `GE_STARTMENU` `#ifdef PORT` block. When set it does what every intro stage's own `!is_first_time_on_main_menu` branch does (`front.c`): `is_first_time_on_main_menu = FALSE; prev_keypresses = TRUE; maybe_is_in_menu = TRUE; menu_update = MENU_FILE_SELECT;` — i.e. it reuses the game's own post-intro route rather than inventing one, so the legal screen + Nintendo/Rare/GoldenEye logo attract loop is skipped. `GE_STARTMENU` still wins when both are set. Default 0 ⇒ untouched. | **LANDED + VERIFIED this session.** Build clean. Headless: default boot frame 90 = the TWYCROSS classification screen (10% non-black), `SkipIntro=1` frame 90 = the **SELECT FILE menu** (folders/photos/cursor all render, 89% non-black), no crash across repeated boots. Screenshots captured + eyeballed. **Owed:** nothing critical — a human boot to confirm the menu is fully interactive (cursor moves, a save slot opens). |
|
||||
| D217 | **1P weapon viewmodel + third-person Bond model textures render garbled — wrong/stale palette on model-DL CI textures (M-84, user QA; surfaced by D215 now that the viewmodel draws).** BUNKER1/Facility: the PP7 viewmodel has yellow+blue scrambled texels and a solid **green** patch by the hammer; Bond's in-game **watch face** shows the same green mismatch; user reports the **third-person Bond world model** does this "sometimes" too. Signature matches the **D161 family** — a CI-format tile drawn without the expected TLUT state, so fast3d's `import_texture` indexes a **stale `rdp.palette`**. D161 fixed the explicit `G_TT_NONE` case; this looks like model DLs not emitting `gDPLoadTLUT` through the port path, or the palette segment not resolving for weapon/character geometry. **Not a D215 regression** — D215 only un-gated the DL. | **OPEN — repro pinned to FACILITY (M-85).** Clean on `-level_09` BUNKER1 (any FovScale / TextureFilter / FixMipTextures / Anisotropy). **Reproduces on `-level_34` FACILITY, ~frame 300 (~5 s in, right after the vent-drop intro cutscene hands control to the player):** the PP7 **grip is a solid dark-green block**, a **green ring** at the barrel/slide joint, and the **hand has a mottled green cast** — while the **slide/barrel upper renders correct black**. So *some* of the weapon+hand model's CI tiles decode against a wrong palette and others don't. `GE_TEXI` shows the weapon tiles do import as `fmt=2 palfmt=0x8000 palidx=0` with a loaded TLUT — `gDPLoadTLUT` **does** reach `gfx_pc.cpp` for model DLs, so "model DLs don't emit LoadTLUT" is disproven. The green-tinted **hand** (a skin texture reading a green palette entry gives exactly this look) points at a **wrong palette**, not corrupt index data. **M-85 probe** (`GE_D217` temp log in `gfx_dp_load_tlut` + the CI branch of `import_texture`, since reverted; logs in session scratchpad `d217_fac.log` / `d217_bnk.log`): (i) **user confirms it is not Facility-specific — guns on many levels, and multiple weapon types, render with wrong or missing textures**; the Facility PP7 is just a clean headless repro. (ii) GE weapon/character model DLs issue **partial-count `G_LOADTLUT` commands** — `count` = 1, 28, 53, 103, 123, 125, 172, 193, … (never `<<2`-encoded, so the decode is reading the raw count field of `gsDPLoadTLUTCmd` correctly; 256 also appears for world tiles). A `count`=125 load writes `rdp.palette[0..124]` and leaves `[125..255]` holding **whatever the previous TLUT load left** — and those stale upper entries differ per level (Facility `pal[128,200,255]=8c1b eedf f7f5` vs BUNKER1 `841f 9b15 fa53`). (iii) The weapon TLUT load **sequence + counts are identical on BUNKER1 and FACILITY** (same PP7), and the weapon CI8 tiles import with `palidx=0` + `cache=HIT` on the steady-state frames. So the delta is purely the **stale upper-palette contents** a weapon CI8 texel lands in when it indexes ≥ its palette's `count`. (iv) **`GE_D217Z` experiment — zeroing `rdp.palette[count..255]` after every partial load did NOT change the green grip/ring/hand** → the bad texels index **within** the loaded `[0..count)` range, so the *palette content bound to the grip tile is wrong*, not stale-tail overflow. (v) Best remaining theory: **`TextureCacheKey` collision / missing CI-cache invalidation on palette change** (`gfx_pc.cpp:990` — key = `{orig_addr, palette_addrs[0/1], fmt, siz, palidx, size_bytes}`, no palette *content* hash; `rdp.textures_changed` is set by `gfx_dp_load_tlut` but may not evict CI entries). Facility has **green-uniformed guards rendered right before the viewmodel**; if the grip tile takes a stale `cache=HIT` (or its `palette_addrs` collide with the guards' TLUT source) it decodes against the **guard-fatigue palette** → green grip + green-cast hand, barrel (different material/palette, imported clean) stays black. Fits "widespread, weapon- and proximity-dependent." Next: log CI-cache hit/miss + a cheap palette checksum per weapon tile on Facility; test making the CI cache key include a palette-content hash (or evicting CI entries on `textures_changed`) — **must regression-sweep all 21 levels' world CI textures**, this is a hot path. **Wants a dedicated debug session / scoped subagent.** Screenshots: session scratchpad `fac_gun_crop.png`; add to `docs/dev/GRAPHICS-BACKLOG.md`. |
|
||||
| D217 | **1P weapon viewmodel + third-person Bond model textures render garbled — wrong/stale palette on model-DL CI textures (M-84, user QA; surfaced by D215 now that the viewmodel draws).** BUNKER1/Facility: the PP7 viewmodel has yellow+blue scrambled texels and a solid **green** patch by the hammer; Bond's in-game **watch face** shows the same green mismatch; user reports the **third-person Bond world model** does this "sometimes" too. Signature matches the **D161 family** — a CI-format tile drawn without the expected TLUT state, so fast3d's `import_texture` indexes a **stale `rdp.palette`**. D161 fixed the explicit `G_TT_NONE` case; this looks like model DLs not emitting `gDPLoadTLUT` through the port path, or the palette segment not resolving for weapon/character geometry. **Not a D215 regression** — D215 only un-gated the DL. | **OPEN — root cause characterised (M-86); needs an upstream/asset-pipeline fix.** Headless repro: `-level_34` FACILITY ~frame 300–400, PP7 **grip = flat green block** + green ring at the barrel joint + green-cast hand; barrel/slide upper correct black. Also green on `-level_30` DEPOT. **Clean on `-level_09` BUNKER1** (A/B control — same PP7, identical DL). **Two separate defects found:** ① **Wrong palette bound (the visible green — upstream of fast3d).** The grip is a CI4 solid-fill of palette index 0. Green `(24,65,0)` == RGBA5551 `0x1A00` == `rdp.palette[0]` at grip-import time. The grip's `count=1` model `G_LOADTLUT` is fed **different source bytes per level** for the same tile: BUNKER `00 01`→`PD_BE16`→`0x0001` (black ✓), FACILITY `1a 00`→`0x1A00` (green ✗). fast3d byteswaps + stores faithfully (proven correct by BUNKER) — the wrong bytes *arrive* from the model-DL / segment-address path. Strong tell: every *larger* model TLUT has `raw[0]==raw[1]==0x0000` with real colour from index 2 ⇒ GE's model-TLUT payload likely carries a **4-byte (2-entry) leading zero pad**, OR the port's model-TLUT source pointer is **4 bytes / 2 entries too low** (then the `count=1` grip load reads the tail of the preceding CI blob — BUNKER lucky, FACILITY not; fits "widespread across levels + weapons"). Ruled out this session: not tail-overflow (index 0 is in-range; matches M-85 `GE_D217Z`), not the fast3d byteswap. **Next:** dump the grip TLUT `G_SETTIMG` target vs the ROM/`pcmodels` sidecar PP7-grip material bytes; confirm the 4-byte offset vs N64; fix in model-DL segment resolution or `tools_pc/d43_emit.py` blob placement (sidecar regen). ② **CI texture-cache collision (proven, real, separate; fix landed).** `gfx_texture_cache_lookup` (`gfx_pc.cpp:522`) keyed CI textures on `{addr, palette_addrs[0/1], palidx, size_bytes}` with **no palette-content check**; GE reissues `gDPLoadTLUT` from repeated scratch-arena addresses with different content, so a later material could take a stale HIT and sample another material's decoded palette. **Fix on branch `fix/d217-model-texture-palette` (commit `f6fc0399`):** FNV-1a hash of the 512-byte `rdp.palette` computed only in `gfx_dp_load_tlut()`, added to the CI cache key (`port/fast3d/gfx_pc.{cpp,h}`, fast3d-internal, no `#ifdef`). Cheap, cannot worsen a decode; **owes a full 21-level `verify.sh sweep`** before merge (touches every palettized world texture's import path — cache-churn risk only). Does **not** fix defect ①. Screenshots: session scratchpad `d217/`, `fac_gun_crop.png`. Cross-ref `docs/dev/GRAPHICS-BACKLOG.md`. |
|
||||
| D218 | **Wide FOV exposes the per-level draw-distance / fog boundary — "blue artifacting" down long peripheral sightlines (M-84, user QA on the D211 FOV slider).** BUNKER→**Dam main tunnel** at `FovScale` ~150: geometry / backdrop past the level's fog-end distance shows at the widened frustum edges (blue = Dam's fog tint). Not a render bug — `fogLoadCurrentEnvironment` (`bgfog.c:305`) does `viSetZRange(Visibility.BlendMultiplier, Visibility.FarFog)`, so **`Visibility.FarFog` is both the far clip plane and the fog-saturation distance**; levels tune it for the stock ~60° FOV. | **Logged (M-84), fix proposed + parked.** Plan: `Video.DrawDistance` (percent, 100–400, default 100 ⇒ no-op) `#ifdef PORT` multiplier on `FarFog` at `bgfog.c:305` (+ the direct `arg0->Visibility.FarFog` reads just below, so fog stays consistent) plus `Video.DrawDistanceAutoFov` (0/1, default 1 — couples draw distance to `FovScale` unless `DrawDistance` is set explicitly). Clamp effective multiplier ≲2.5× (small znear ⇒ far-field z-fighting risk). |
|
||||
| D85 | room primary/secondary DL → `texLoadFromGdl` garbage | OPEN (safety-netted; widen/pool fixes landed, geometry now renders) |
|
||||
| D86 · D87 | modelInitRwData truncated ptr · attract-demo BE `ramromfilestructure` | resolved |
|
||||
|
||||
@@ -469,6 +469,23 @@ through a converter or a runtime bswap fixup reads scrambled.
|
||||
`import_texture()` dispatched purely on `tile.fmt` and did a palette lookup
|
||||
against a stale `rdp.palette` → garbage (D161, GE Depot ceiling = blue
|
||||
speckle). Fix: when `rdp.palette_fmt == G_TT_NONE`, route CI4/CI8 → I4/I8.
|
||||
- **CI texture cache must key on palette CONTENT, not just the TLUT source
|
||||
address (D217).** GE assembles weapon/character model DLs in scratch arena
|
||||
RAM and reissues `gDPLoadTLUT` from a *repeated* source address with different
|
||||
palette content between materials and frames. fast3d keyed CI textures on
|
||||
`{texel addr, palette_addrs[0/1], palette_index, size_bytes}` with no content
|
||||
check → a later material takes a stale cache HIT and samples the GL texture
|
||||
decoded for an earlier palette. Fix: FNV-1a the 512-byte `rdp.palette` in
|
||||
`gfx_dp_load_tlut` (rare vs draws) and fold that hash into the CI cache key.
|
||||
- **GE model-DL TLUT payloads carry a 2-entry leading zero pad (D217, still
|
||||
open).** Every larger weapon/chr model TLUT decodes with `entry[0]==entry[1]
|
||||
==0x0000` and real colour from index 2. `count=1` model TLUTs that feed
|
||||
palette index 0 (solid-fill weapon parts — the PP7 grip) are the canary: if
|
||||
the port's model-TLUT source pointer is even 4 bytes off, index 0 reads
|
||||
adjacent texel/blob bytes and the part renders a wrong flat colour (green on
|
||||
Facility/Depot, black-by-luck on Bunker1) while larger tiles look plausible
|
||||
because they never index the low entries. Fix is upstream (segment resolution
|
||||
or `tools_pc/d43_emit.py` blob placement), not in the fast3d decode.
|
||||
- **RC3 / D167 — GL `GL_REPEAT` wraps at the uploaded image size; the N64 RDP
|
||||
wraps a render tile at `1<<masks` (`= ceil(log2(dim))` for GE, `texDimensionToMask`).**
|
||||
Equal for power-of-two textures, so this is invisible almost everywhere — but a
|
||||
|
||||
Reference in new issue
Block a user