From a2b2f78dd1ca58b8815009944aeba22031263e30 Mon Sep 17 00:00:00 2001 From: James Dansereau <106631954+jkdansereau@users.noreply.github.com> Date: Sun, 27 Sep 2026 22:04:03 -0400 Subject: [PATCH] =?UTF-8?q?port:=20D397=20=E2=80=94=20file-select=20backgr?= =?UTF-8?q?ound=20+=20gun-barrel=20comb=20look:=20stale=20RENDERTILE=20imp?= =?UTF-8?q?ort=20(fix=20in=20tree,=20by-eye=20owed)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Root-caused the D397 interlaced/comb symptom on the file-select background and intro gun-barrel (first display, not round-trip): titleRenderFolderMenuBackgroundLines loads 299 one-pixel 440-byte I8 rows with gDPLoadTextureBlock(I, 8b) and samples G_TX_RENDERTILE but never emits gSPSetTile, so the tile's fmt/siz is whatever the previous screen left (CI/4b spectrum-screen leftover, D116 probe). fast3d ran the I8 bytes through import_texture_ci4 against a stale/all-zero 16-entry palette -> periodic black rows (GL texture-cache artifact, not a ground-truth dither — no ROM RLE decode needed). - gfx_pc.cpp import_texture: route the import through the source's fmt/siz when a CI/RGBA tile genuinely disagrees with an I8 8b load (mirrors the D229 case); real CI/RGBA textures untouched. - gfx_opengl.cpp: GE_D397SH env-gated shader-formula probe for the file-select gunbarrel combine (label initially written as D398, renamed to avoid the PC-Options-label finding collision). - findings: D397 row -> FIX LANDED (by-eye + PCDUMP verification owed); tail section notes the fix. CSV regenerated. Cross-ref D182(2) (same symptom family), D159/D183 (disproven leads), D229 (mirror-case precedent). --- docs/dev/findings-index.csv | 2 +- docs/dev/findings.md | 4 +++- port/fast3d/gfx_opengl.cpp | 30 ++++++++++++++++++++++++++++++ port/fast3d/gfx_pc.cpp | 16 ++++++++++++++++ 4 files changed, 50 insertions(+), 2 deletions(-) diff --git a/docs/dev/findings-index.csv b/docs/dev/findings-index.csv index 144a00b3..75edd865 100644 --- a/docs/dev/findings-index.csv +++ b/docs/dev/findings-index.csv @@ -273,7 +273,7 @@ D393,FIXED,Controller B/Y both crouch; no in-game use button —,FIXED + user li D394,PARTIAL,Xbox 1.1 Jinx controller parity —,PARTIAL (port 1.1 gameplay button roles user-tested; exact gadget-category parity and other selectable presets outstanding; RB HD toggle unavailable). D395,PARTIAL,Controller trapped in keyboard/mouse-only binding capture —,"PARTIAL (both options UIs block pad-initiated capture; pad B can cancel an active modal; NTSC build linked, controller-only retest owed)." D396,PARTIAL,"F10 pad B/A used as slider decrement/increment, not back/select —","PARTIAL (B backs one F10 page, closes at root; A/X accept/toggle on edge but never shift a slider; D-pad/stick left/right still adjust; NTSC linked, pad retest owed)." -D397,OPEN,SELECT FILE background + intro gun-barrel show an interlaced/comb look —,OPEN (re-scoped: D159 odd-row swap no-op'd (not cause); D183 pitch shear disproven; needs a live GE_PCDUMP capture + ROM ground-truth decode of the gun-barrel RLE image to settle ground-truth dither… +D397,PARTIAL,SELECT FILE background + intro gun-barrel show an interlaced/comb look —,FIX LANDED (in tree; by-eye + PCDUMP verification owed): D398,FIXED,PC Options label: D348's procedural slider glyph replaced with an existing mainfolderimages texture (2026-09-27).,"SUPERSEDED by D400 (icon removed, text-only label; purple-hover bug moot; the D400 textMeasure arg-swap bug was found and fixed there)." D399,PARTIAL,File-select bottom row (SELECT FILE text + Copy/Erase + PC Options dot/label) shifted 15px left as a block (user sign-off 2026-09-27).,"LANDED (port-only, D343 class):" D400,PARTIAL,"File-select ""PC Options"" label:","LANDED (port-only, frontoptions.c):" diff --git a/docs/dev/findings.md b/docs/dev/findings.md index ef67eb3b..77f19fb3 100644 --- a/docs/dev/findings.md +++ b/docs/dev/findings.md @@ -681,7 +681,7 @@ covers D24–D69; the log continues in §H (D32 procedure, D70–D121). | D394 | **Xbox 1.1 Jinx controller parity** — user supplied the Xbox 1.1–1.4 control table; B is gadgets, stick clicks crouch, LB aim, RB HD toggle (not weapon cycling). | PARTIAL (port 1.1 gameplay button roles user-tested; exact gadget-category parity and other selectable presets outstanding; RB HD toggle unavailable). | | D395 | **Controller trapped in keyboard/mouse-only binding capture** — pad could enter the binding modal but could not finish or cancel without keyboard. | PARTIAL (both options UIs block pad-initiated capture; pad B can cancel an active modal; NTSC build linked, controller-only retest owed). | | D396 | **F10 pad B/A used as slider decrement/increment, not back/select** — B/Y fed Left and A/X fed Right on every row. | PARTIAL (B backs one F10 page, closes at root; A/X accept/toggle on edge but never shift a slider; D-pad/stick left/right still adjust; NTSC linked, pad retest owed). | -| D397 | **SELECT FILE background + intro gun-barrel show an interlaced/comb look** — D182(2) family, but present on FIRST display, not only after re-entry (2026-09-27). Full `## D397` entry at file tail. | OPEN (re-scoped: D159 odd-row swap no-op'd (not cause); D183 pitch shear disproven; needs a live GE_PCDUMP capture + ROM ground-truth decode of the gun-barrel RLE image to settle ground-truth dither vs GL artifact. No blind fix made). | +| D397 | **SELECT FILE background + intro gun-barrel show an interlaced/comb look** — D182(2) family, but present on FIRST display, not only after re-entry (2026-09-27). Full `## D397` entry at file tail. | FIX LANDED (in tree; by-eye + PCDUMP verification owed): root-caused — `titleRenderFolderMenuBackgroundLines` loads 299 one-pixel 440-byte I8 rows with `gDPLoadTextureBlock(I, 8b)` and samples `G_TX_RENDERTILE` but never emits `gSPSetTile`, so the tile's fmt/siz is whatever the previous screen left (CI/4b spectrum-screen leftover, D116 probe); fast3d ran the I8 bytes through `import_texture_ci4` against a stale/all-zero 16-entry palette → periodic black rows. Fix: `gfx_pc.cpp` `import_texture` routes the import through the source's fmt/siz when a CI/RGBA tile genuinely disagrees with an I8 8b load (mirrors the D229 case); `GE_D397SH` shader-formula probe added in `gfx_opengl.cpp`. D159/D183 leads stand disproven; no ROM RLE decode needed. | | D398 | **PC Options label: D348's procedural slider glyph replaced with an existing mainfolderimages texture (2026-09-27).** Full `## D398` entry at file tail. | SUPERSEDED by D400 (icon removed, text-only label; purple-hover bug moot; the D400 `textMeasure` arg-swap bug was found and fixed there). | | D399 | **File-select bottom row (SELECT FILE text + Copy/Erase + PC Options dot/label) shifted 15px left as a block (user sign-off 2026-09-27).** Full `## D399` entry at file tail. | LANDED (port-only, D343 class): `selectpos.f[0]` 110→95 (new `#ifdef PORT` in front.c), Copy 200/222→185/207, Erase 282/304→267/289 (D343 PORT block), `LABEL_X` 358→343 (`frontoptions.c`); hit bounds recompute from the shifted positions. Clean build, PCDUMP frame 195 no overlaps; by-eye check owed. | | D400 | **File-select "PC Options" label: icon removed, text-only; fixed a swapped textMeasure() arg bug and a 7px vertical misalignment vs Copy/Erase (user sign-off 2026-09-27).** Full `## D400` entry at file tail. | LANDED (port-only, `frontoptions.c`): D398's dot icon dropped on user instruction ("most straightforward design"); label now a plain `textRender` word, GAP 28 after Erase. Two measurement bugs fixed: (1) `textMeasure(textheight, textwidth)` args passed swapped -> label 35px above the bar, hot band transposed; (2) bare-label height measures 0 (vs 14 for newline-terminated Copy/Erase) -> 7px lower tops; fixed by restoring the `kLabelNL` height measure. PCDUMP frame 195: tops aligned 279.8 vs 279.3, 31px clearance from Erase; by-eye check owed. | @@ -15175,6 +15175,8 @@ I logged all of this as **D397** (`docs/dev/findings.md`, index CSV, and the D18 **Owed (concrete next steps, single session):** (a) one `GE_PCDUMP` capture of the fresh file-select screen + the intro gun-barrel (not a round-trip); (b) ROM ground-truth decode of the `unknown2` gun-barrel RLE image (`tools_pc` extract of `assets/ge007.u.2A4D50.usedby7F008DE4.bin`) to settle cause 1 vs 2; (c) if cause 2, an `GE_DTEX` sweep of the 299 row imports to confirm cache/STRIDED behaviour. Cross-refs: D182(2) (same symptom, round-trip trigger), D176(b) (Family A, disproven), D159 (no-op'd), D183 (pitch de-stride, latent no-op here). +**Fix landed (in tree, 2026-09-28) — status: awaiting by-eye + PCDUMP verification.** The live capture found the tile was imported as **CI/4b — the spectrum-screen leftover** (D116 probe), i.e. cause 2 (GL texture-cache artifact), not a ground-truth dither: `titleRenderFolderMenuBackgroundLines` samples `G_TX_RENDERTILE` but never emits `gSPSetTile`, so the tile's fmt/siz is whatever the previous screen left; fast3d then ran the I8 bytes through `import_texture_ci4` against a stale/all-zero 16-entry palette → periodic black rows. Fix in `gfx_pc.cpp` `import_texture`: route the import through the **source's** fmt/siz when a CI/RGBA tile genuinely disagrees with an I8 8b load (mirrors the D229 mirror case above it); real CI/RGBA textures are untouched because the guard only fires on a genuine format disagreement. A `GE_D397SH` shader-formula probe (`gfx_opengl.cpp`, env-gated) was added to inspect the file-select gunbarrel combine; the label was initially written as "D398", which collided with the PC-Options-label finding and was renamed to D397. No ROM RLE decode (step b) is needed to verify: the PCDUMP of the file-select + intro gun-barrel screens against the pre-fix capture is the check. + ## D398 — PC Options label: IMG_DOT texture replaces the D348 procedural slider glyph (2026-09-27) diff --git a/port/fast3d/gfx_opengl.cpp b/port/fast3d/gfx_opengl.cpp index eb8423d7..8dadcfc7 100644 --- a/port/fast3d/gfx_opengl.cpp +++ b/port/fast3d/gfx_opengl.cpp @@ -560,6 +560,36 @@ static struct ShaderProgram* gfx_opengl_create_and_load_new_shader(uint64_t shad vs_buf[vs_len] = '\0'; fs_buf[fs_len] = '\0'; +#ifdef PORT + /* D397 TEMP probe (env-gated): dump the generated texel formula per + * shader so the file-select gunbarrel combine can be inspected. */ + { + static int d397_on = -1; + if (d397_on < 0) d397_on = getenv("GE_D397SH") != NULL; + if (d397_on) { + fprintf(stderr, + "D397SH: id0=%016llx id1=%08x 2cyc=%d alpha=%d noise=%d edge=%d blur=%d grey=%d " + "c0r=(%u,%u,%u,%u) c0a=(%u,%u,%u,%u) c1r=(%u,%u,%u,%u) c1a=(%u,%u,%u,%u) " + "single(%d,%d/%d,%d) mult(%d,%d/%d,%d) mix(%d,%d/%d,%d)\n", + (unsigned long long)shader_id0, (unsigned)shader_id1, + (int)cc_features.opt_2cyc, (int)cc_features.opt_alpha, (int)cc_features.opt_noise, + (int)cc_features.opt_texture_edge, (int)cc_features.opt_blur, (int)cc_features.opt_grayscale, + cc_features.c[0][0][0], cc_features.c[0][0][1], cc_features.c[0][0][2], cc_features.c[0][0][3], + cc_features.c[0][1][0], cc_features.c[0][1][1], cc_features.c[0][1][2], cc_features.c[0][1][3], + cc_features.c[1][0][0], cc_features.c[1][0][1], cc_features.c[1][0][2], cc_features.c[1][0][3], + cc_features.c[1][1][0], cc_features.c[1][1][1], cc_features.c[1][1][2], cc_features.c[1][1][3], + (int)cc_features.do_single[0][0], (int)cc_features.do_single[0][1], + (int)cc_features.do_single[1][0], (int)cc_features.do_single[1][1], + (int)cc_features.do_multiply[0][0], (int)cc_features.do_multiply[0][1], + (int)cc_features.do_multiply[1][0], (int)cc_features.do_multiply[1][1], + (int)cc_features.do_mix[0][0], (int)cc_features.do_mix[0][1], + (int)cc_features.do_mix[1][0], (int)cc_features.do_mix[1][1]); + const char* tf = strstr(fs_buf, "texel = "); + if (tf) fprintf(stderr, "D397SHF: %s;\n", tf); + } + } +#endif + const GLchar* sources[2] = { vs_buf, fs_buf }; const GLint lengths[2] = { (GLint)vs_len, (GLint)fs_len }; GLint success; diff --git a/port/fast3d/gfx_pc.cpp b/port/fast3d/gfx_pc.cpp index 5d7e83b1..ff72a947 100644 --- a/port/fast3d/gfx_pc.cpp +++ b/port/fast3d/gfx_pc.cpp @@ -1329,6 +1329,22 @@ static void import_texture(int i, int tile, bool importReplacement) { * palette path instead -- the port-layer equivalent of the ucode's * expand-at-load. Only fires when the tile format genuinely disagrees * with the loaded source, so real RGBA16 textures are untouched. */ + /* D397: comb/interlace grain on the file-select background + intro + * gun-barrel. titleRenderFolderMenuBackgroundLines loads 299 one-pixel + * 440-byte I8 rows with gDPLoadTextureBlock(I, 8b) and samples + * G_TX_RENDERTILE but never emits gSPSetTile, so the tile's fmt/siz is + * whatever the previous screen left (D116 probe: CI/4b -- the + * spectrum-screen leftover). fast3d then ran the I8 bytes through + * import_texture_ci4 against a 16-entry palette (stale / all-zero) + * -> periodic black rows. Route the import through the source's + * format, exactly like the D229 mirror case above. Only fires when + * the tile format genuinely disagrees with the loaded source (CI or + * RGBA tile over an I8 load), so real CI/RGBA textures are untouched. */ + if (loaded_texture.src_fmt == G_IM_FMT_I && siz == G_IM_SIZ_8b && + (fmt_eff == G_IM_FMT_CI || fmt_eff == G_IM_FMT_RGBA)) { + fmt_eff = G_IM_FMT_I; + siz_eff = G_IM_SIZ_8b; + } if (fmt_eff == G_IM_FMT_RGBA && siz == G_IM_SIZ_16b && loaded_texture.src_fmt == G_IM_FMT_CI) { #ifdef PORT