From 5a5c73de56708f70bfe024bb09fe263436dc64d5 Mon Sep 17 00:00:00 2001 From: James Dansereau <106631954+jkdansereau@users.noreply.github.com> Date: Fri, 28 Aug 2026 21:06:37 -0400 Subject: [PATCH] =?UTF-8?q?D105:=20emit=20G=5FCLEAR=5FDEPTH=5FEXT=20?= =?UTF-8?q?=E2=80=94=20depth=20buffer=20was=20never=20cleared=20in-level?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit zbufClearCurrentPlayer (viewport.c) clears the N64 Z buffer with the 'point the colour image at the Z buffer and gDPFillRectangle it' idiom. fast3d does not emulate that: gfx_dp_fill_rectangle bails when color_image_address == z_buf_address assuming glClear already cleared depth, but the frame-start clear_framebuffer(true,false) is colour-only. fast3d expects G_CLEAR_DEPTH_EXT (0x44) for a real depth clear and GE never emits it. Here the two addresses don't even match (gDPSetDepthImage(z_buffer & ~0x3F) vs gDPSetColorImage(K0_TO_PHYSICAL)) so the packed-Z fill colour is scribbled onto the framebuffer instead. Net: every in-level frame rendered against a stale/garbage depth buffer, all ~160k room triangles failed the Z test, only skyRender's background fill survived -> flat dark-blue screen + HUD. PORT branch in zbufClearCurrentPlayer emits a bare G_CLEAR_DEPTH_EXT and returns, skipping the N64 idiom (a no-op / harmful in fast3d anyway). Verified -level_09: 90% flat sky-colour -> 2585 distinct colours, sky 16%; recognisable BUNKER1 (textured walls, storage racks, floor). 70s crash-free. Adds GE_D104 room-render-pass probe in bg.c (gated, capped). Remaining: dark-blue wedge (geometry gap) + brown band top third (ceiling tex); then struct player weapon/offset pass; then D75(b). --- docs/HANDOFF.md | 18 ++++++++++++---- docs/PCPortResearch.md | 48 ++++++++++++++++++++++++++++++++++++++---- src/game/bg.c | 30 ++++++++++++++++++++++++++ src/game/viewport.c | 15 +++++++++++++ 4 files changed, 103 insertions(+), 8 deletions(-) diff --git a/docs/HANDOFF.md b/docs/HANDOFF.md index 645c0f68..36612645 100644 --- a/docs/HANDOFF.md +++ b/docs/HANDOFF.md @@ -16,10 +16,20 @@ single native-resolution/viewport bug (`osViSetMode` hard-coded height 480 → `RATIO_Y` half of `RATIO_X`), not per-model. Frame is now full (91.7 %, correct letterbox + HUD placement). See §F "D103". -**What that revealed:** BUNKER1 renders only a **flat dark-blue fill + -HUD** — the blue was always the whole picture, just squished. So the real -remaining work, in order: -1. **D85 — room geometry** (walls/floor draw as nothing; `GE_D85DUMP`). +**D105 (session M-4) fixed room geometry** — `zbufClearCurrentPlayer` +(`viewport.c`) used the N64 "fill the Z buffer as a colour image" idiom, +which fast3d doesn't emulate, and nothing emitted `G_CLEAR_DEPTH_EXT`, so +the depth buffer was never cleared and all ~160 k room triangles failed +the Z test (only `skyRender`'s background fill survived). PORT branch now +emits `G_CLEAR_DEPTH_EXT`. See §F "D104"/"D105". BUNKER1 now renders +recognisably — textured walls, storage racks, floor (2585 colours, sky +16 %), 70 s crash-free. + +**Real remaining work, in order:** +1. **BUNKER1 render polish** — a dark-blue wedge lower-right (sky through + a geometry gap: unloaded room / missing wall / portal not culled) and + a blurry brown band across the top ~third (mis-rendered ceiling tex / + texfilter). `GE_D104` room-render-pass probe + `GE_D85DUMP` in `bg.c`. 2. **`struct player` offset pass** — weapon model doesn't draw; gate to playability (landmine below + D102). 3. **D75(b)** — animated/skeletal character models (see §F "D75" and diff --git a/docs/PCPortResearch.md b/docs/PCPortResearch.md index e3a1e825..14ac29a8 100644 --- a/docs/PCPortResearch.md +++ b/docs/PCPortResearch.md @@ -3295,10 +3295,50 @@ SCREEN_HEIGHT_MAX(480)`, `fr.c:417`) with a `pal ? 272 : 240` fallback; `-level_09` `GE_PCDUMP` frames go from ~46 % / lower-band to **91.7 % / bbox (0,20)-(639,459)** — the full 10..230 viewport ×2 with correct letterbox, HUD ammo counter correctly placed top-right. Purely a -transform fix — no per-model work. **Reveals the next layer:** BUNKER1 -still shows only a flat dark-blue fill + HUD, i.e. **no room geometry -(D85) and no weapon model draw at all** — the blue was always the whole -picture, just squished. Next: D85 room GDL (walls/floor not drawing). +transform fix — no per-model work. + +**D104 (session M-4) — investigation, folded into D105.** Chased "flat +dark-blue fill + HUD, no room geometry" after D103. Ruled out (probes + +subagent trace): segment 0x0E vtx resolution, room-DL chaining/execution, +per-room scissor (`GE_D104` shows bbox `(1,10)-(319,230)` full-view, +`viewleft/top/x/y = 0/10/320/220`), the view-projection matrix +(`field_10E0` decodes to a sane perspective×lookat — rows ≈ unit-scale +3×3, translate row `518,-22,430,459`), and the room GDL decode (valid +F3DEX2, no embedded `G_MTX`). A temporary fast3d tri counter proved +~160 k room triangles WERE being submitted to GL each frame — yet the +final image was 90 % exactly `(0,16,65)` (BUNKER1's `env->{Red,Green, +Blue}` sky-fill from `skyRender`, `sky.c:326`, the 1P no-clouds +`gDPFillRectangle` path). Geometry drawn, invisible → depth. `GE_D104` +room-render-pass probe left in `bg.c` (capped, gated). + +**D105 (session M-4) — depth buffer never cleared, FIXED.** +`zbufClearCurrentPlayer` (`src/game/viewport.c:89`) clears the N64 Z +buffer with the classic "point the colour image at the Z buffer and +`gDPFillRectangle` it with a packed-Z fill colour" idiom. fast3d does +**not** emulate that: `gfx_dp_fill_rectangle` (`gfx_pc.cpp:2281`) bails +when `color_image_address == z_buf_address` on the assumption the depth +clear already happened via `glClear` — but the frame-start clear is +`clear_framebuffer(true, false)` (`gfx_pc.cpp:2855`), **colour only**. +fast3d expects the game to emit `G_CLEAR_DEPTH_EXT` (opcode `0x44`, +`gfx_pc.cpp:2678`) for a real depth clear, and **nothing in GE ever +emits it** (grep-confirmed). Worse, here the two addresses don't even +match (`gDPSetDepthImage(z_buffer & ~0x3F)` vs `gDPSetColorImage( +OS_K0_TO_PHYSICAL(z_buffer))`), so the bail isn't taken and the packed-Z +fill colour gets scribbled onto the real framebuffer instead. Net: every +in-level frame renders against a stale/garbage depth buffer, all ~160 k +room triangles fail the Z test, and only `skyRender`'s background fill +survives. Fix: `#ifdef PORT` branch in `zbufClearCurrentPlayer` emits a +bare `G_CLEAR_DEPTH_EXT` Gfx word and returns, skipping the N64 idiom +(which is a no-op / actively harmful in fast3d anyway). Verified +`-level_09`: frame goes from 90 % flat sky-colour to **2585 distinct +colours, sky down to 16 %** — recognisable BUNKER1: textured walls, +storage racks, floor. 70 s run, crash-free. + +**Remaining BUNKER1 render issues (post-D105):** (1) a solid dark-blue +wedge lower-right — sky showing through a geometry gap (unloaded room / +missing wall / portal not culled); (2) a blurry brown band across the +top ~third (mis-rendered ceiling texture, or texfilter/LOD). Then the +`struct player` weapon-model / offset pass, then D75(b) skeletal models. **`data/` deletion + recovery (session M-3).** `git worktree remove --force` on an agent worktree that had a directory *junction* diff --git a/src/game/bg.c b/src/game/bg.c index 5847baa6..ec0f7174 100644 --- a/src/game/bg.c +++ b/src/game/bg.c @@ -634,6 +634,36 @@ Gfx *sub_GAME_7F0B3C8C(Gfx *gdl) } gSPMatrix(gdl++, osVirtualToPhysical((void*)get_BONDdata_field_10E0()), (G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_PROJECTION)); +#ifdef PORT + /* TEMP D104: room render-pass geometry-visibility probe. */ + if (getenv("GE_D104")) { + static s32 d104n = 0; + if (d104n < 40) { + s32 *mp = (s32 *)(uintptr_t)(u32)get_BONDdata_field_10E0(); + s32 *pp = (s32 *)(uintptr_t)(u32)(uintptr_t)currentPlayerGetProjectionMatrix(); + osSyncPrintf("D104 room=%d bbox=(%d,%d)-(%d,%d) view l=%d t=%d x=%d y=%d screensize=(%d,%d)-(%d,%d)\n", + dword_CODE_bss_8007FFA0[j].roomid, + (s32)dword_CODE_bss_8007FFA0[j].bbox.min.x, (s32)dword_CODE_bss_8007FFA0[j].bbox.min.y, + (s32)dword_CODE_bss_8007FFA0[j].bbox.max.x, (s32)dword_CODE_bss_8007FFA0[j].bbox.max.y, + (s32)g_CurrentPlayer->viewleft, (s32)g_CurrentPlayer->viewtop, + (s32)g_CurrentPlayer->viewx, (s32)g_CurrentPlayer->viewy, + (s32)g_CurrentPlayer->screensize.min.x, (s32)g_CurrentPlayer->screensize.min.y, + (s32)g_CurrentPlayer->screensize.max.x, (s32)g_CurrentPlayer->screensize.max.y); + if (mp) { + s32 r; + for (r = 0; r < 4; r++) { + f32 e0 = (f32)(s32)((mp[r*2+0] & 0xffff0000) | ((u32)mp[8+r*2+0] >> 16)) / 65536.0f; + f32 e1 = (f32)(s32)((mp[r*2+0] << 16) | (mp[8+r*2+0] & 0xffff)) / 65536.0f; + f32 e2 = (f32)(s32)((mp[r*2+1] & 0xffff0000) | ((u32)mp[8+r*2+1] >> 16)) / 65536.0f; + f32 e3 = (f32)(s32)((mp[r*2+1] << 16) | (mp[8+r*2+1] & 0xffff)) / 65536.0f; + osSyncPrintf("D104 row%d = % .4f % .4f % .4f % .4f\n", r, e0, e1, e2, e3); + } + } + osSyncPrintf("D104 f10E0=%p projm=%p\n", (void *)mp, (void *)pp); + d104n++; + } + } +#endif gdl = fogSetRenderFogColor( bgScissorCurrentPlayerViewF( gdl++, diff --git a/src/game/viewport.c b/src/game/viewport.c index cd8b5c11..4d0d49ef 100644 --- a/src/game/viewport.c +++ b/src/game/viewport.c @@ -89,6 +89,21 @@ Gfx *zbufInit(Gfx *gdl) { Gfx *zbufClearCurrentPlayer(Gfx *gdl) { s32 start_x; s32 end_x; +#ifdef PORT + /* D105: fast3d clears only the colour buffer at frame start + * (gfx_pc.cpp: clear_framebuffer(true, false)) and treats the N64 + * "point the colour image at the Z buffer and FILL it" idiom below as a + * no-op (gfx_dp_fill_rectangle bails when colour==zbuf; here the K0/masked + * address encodings don't even match, so it would instead scribble the + * packed-Z fill colour onto the real framebuffer). Without a real depth + * clear every in-level frame renders against a stale/garbage depth buffer + * and all room geometry fails the Z test -> only skyRender's background + * fill survives. Emit the port's dedicated depth-clear opcode instead. */ + gdl->words.w0 = _SHIFTL(0x44 /* G_CLEAR_DEPTH_EXT */, 24, 8); + gdl->words.w1 = 0; + gdl++; + return gdl; +#endif gDPPipeSync(gdl++); gDPSetRenderMode(gdl++, G_RM_NOOP, G_RM_NOOP2); gDPSetColorImage(gdl++, G_IM_FMT_RGBA, G_IM_SIZ_16b, z_buffer_width, OS_K0_TO_PHYSICAL(z_buffer));