diff --git a/docs/PCPortResearch.md b/docs/PCPortResearch.md index 41151e5f..1277b072 100644 --- a/docs/PCPortResearch.md +++ b/docs/PCPortResearch.md @@ -807,7 +807,8 @@ covers D24–D69; the log continues in §H (D32 procedure, D70–D121). | D159 | front-end wallet-Bond photo "interlaced"/combed (RC1 / D149) — `texSwapAltRowBytes` odd-row 8-byte pre-swap (N64 RDP odd-line TMEM XOR compensation) not reversed by fast3d | FIXED (`#ifdef PORT` no-op the swap in `image.c`) | | D161 | Depot (`-level_30`) ceiling = bright-blue speckle + radial rays (B2). A CI8 tile drawn with `gsDPSetTextureLUT(G_TT_NONE)` was decoded against the stale `rdp.palette` → garbage. Fix: `#ifdef`-free narrow route CI→I when `palette_fmt == G_TT_NONE` in `gfx_pc.cpp import_texture()`. | FIXED (`port/fast3d/gfx_pc.cpp`) | | D165 · D166 | input-layer polish (M-31, port-only, `port/src/input.c`): D165 front-end cursor is now a true ~1:1 pointer (P-controller estimating the game cursor via front.c's own integrator) instead of velocity²; D166 hipfire mouse pitch emits speed-proportional C-button pulses instead of a fixed digital threshold. | FIXED | -| D167 | D152 addendum (M-31): static audit of every compiled-audio `osSetIntMask(OS_IM_NONE)` — **all balanced**, no unbalanced early-return. Real fixes: `sndSetSfxSlotVolume` now holds the mask across its list walk (matches its twin `sndDeactivateAllSfxByFlag`) + `sndApplyVolumeAllSfxSlot` batches the slot loop under one recursive hold (kills the fade-out lock-acquire storm); `portThreadWrapper` calls `imThreadExitRelease()` on thread exit (kills the "transient thread acquired `OS_IM_NONE` and died" leak + the pthread-id-reuse re-wedge). Steal-lock kept as backstop. | FIXED (`src/snd.c`, `port/src/libultra.c`, `#ifdef PORT`) — fade-out repro playtest-gated, not headless-verified | +| D152+ | D152 addendum (M-31): static audit of every compiled-audio `osSetIntMask(OS_IM_NONE)` — **all balanced**, no unbalanced early-return. Real fixes: `sndSetSfxSlotVolume` now holds the mask across its list walk (matches its twin `sndDeactivateAllSfxByFlag`) + `sndApplyVolumeAllSfxSlot` batches the slot loop under one recursive hold (kills the fade-out lock-acquire storm); `portThreadWrapper` calls `imThreadExitRelease()` on thread exit (kills the "transient thread acquired `OS_IM_NONE` and died" leak + the pthread-id-reuse re-wedge). Steal-lock kept as backstop. | FIXED (`src/snd.c`, `port/src/libultra.c`, `#ifdef PORT`) — fade-out repro playtest-gated, not headless-verified | +| RC3 · D167 | **Non-power-of-two texture wrap period (`docs/TEXTURE-GLITCH-ANALYSIS.md` §6 RC3 — "textures repeat oddly", residual Depot-ceiling noise after D161).** The N64 RDP masks the texel coordinate of a wrapping render tile at `1< pre-fmod the UVs at the window size. */ + /* (a) sub-tile window: wrap tile (clamp bit not set) whose + * uls..lrs window is smaller than the uploaded image -> pre-fmod + * the UVs at the window size. */ if (!(cms & G_TX_CLAMP) && tex_width2[i] > 0 && tex_width2[i] < tex_width[i]) { wrap_s[i] = true; wrap_tw[i] = (float)tex_width2[i]; @@ -1580,6 +1582,33 @@ static void gfx_sp_tri1(uint8_t vtx1_idx, uint8_t vtx2_idx, uint8_t vtx3_idx, bo wrap_th[i] = (float)tex_height2[i]; wrap_ult[i] = rdp.texture_tile[tile].ult / 4.0f; } + /* (b) RC3 non-power-of-two wrap period: the N64 RDP masks the + * texel coordinate at 1<= 1 && mks <= 14) { + float period = (float)(1u << mks); + if (period != (float)tex_width[i] && tex_width[i] > 0) { + wrap_s[i] = true; + wrap_tw[i] = period; + wrap_uls[i] = 0.0f; + } + } + if (!wrap_t[i] && !(cmt & G_TX_CLAMP) && mkt >= 1 && mkt <= 14) { + float period = (float)(1u << mkt); + if (period != (float)tex_height[i] && tex_height[i] > 0) { + wrap_t[i] = true; + wrap_th[i] = period; + wrap_ult[i] = 0.0f; + } + } + } } if ((cms & G_TX_CLAMP) && ((cms & G_TX_MIRROR) || tex_width1 != tex_width2[i])) { @@ -1681,11 +1710,18 @@ static void gfx_sp_tri1(uint8_t vtx1_idx, uint8_t vtx2_idx, uint8_t vtx3_idx, bo u = fmodf(u, wrap_tw[t]); if (u < 0.0f) u += wrap_tw[t]; u += wrap_uls[t]; + /* RC3: when the N64 wrap period (wrap_tw) exceeds the uploaded + * image width, the [width, period) band has no real texels -- + * clamp it to the edge rather than let GL_REPEAT restart the + * image early. Never triggers for the sub-tile-window case + * (wrap_tw + wrap_uls stay within tex_width). */ + if (tex_width[t] > 0 && u > (float)tex_width[t]) u = (float)tex_width[t] - 0.5f; } if (wrap_t[t]) { v = fmodf(v, wrap_th[t]); if (v < 0.0f) v += wrap_th[t]; v += wrap_ult[t]; + if (tex_height[t] > 0 && v > (float)tex_height[t]) v = (float)tex_height[t] - 0.5f; } if (!is_rect) { @@ -2041,6 +2077,8 @@ static void gfx_dp_set_tile(uint8_t fmt, uint32_t siz, uint32_t line, uint32_t t rdp.texture_tile[tile].siz = siz; rdp.texture_tile[tile].cms = cms; rdp.texture_tile[tile].cmt = cmt; + rdp.texture_tile[tile].masks = masks; /* RC3 */ + rdp.texture_tile[tile].maskt = maskt; /* RC3 */ rdp.texture_tile[tile].shifts = shifts; rdp.texture_tile[tile].shiftt = shiftt; rdp.texture_tile[tile].line_size_bytes = line * 8; @@ -3055,7 +3093,10 @@ extern "C" void gfx_set_mipmap_filter(enum MipmapFilteringMode mode) { } extern "C" void gfx_set_fix_mip_textures(int on) { g_fix_mip_textures = !!on; } -extern "C" void gfx_set_wrap_fix(int on) { g_wrap_fix = !!on; } +extern "C" void gfx_set_wrap_fix(int on) { + const char* e = getenv("GE_WRAPFIX"); /* RC3 test override */ + g_wrap_fix = e ? (atoi(e) != 0) : !!on; +} extern "C" int gfx_create_framebuffer(uint32_t width, uint32_t height, int upscale, int autoresize) { int fb = gfx_rapi->create_framebuffer(); diff --git a/port/src/video.c b/port/src/video.c index 29906b52..8de2018c 100644 --- a/port/src/video.c +++ b/port/src/video.c @@ -56,7 +56,7 @@ static int cfgFpsCap = 0; /* frame cap in fps; 0 = uncapped (vsync) static int cfgMSAA = 1; /* 1/2/4/8 samples; 1 = off */ static int cfgTexFilter = 1; /* 0 = nearest, 1 = bilinear (default), 2 = N64 3-point + trilinear */ static int cfgFixMipTex = 1; /* RC2: clip mip-contaminated texture uploads to base height */ -static int cfgWrapFix = 0; /* D74: sub-tile UV pre-wrap (opt-in, never-run path) */ +static int cfgWrapFix = 0; /* D74 sub-tile UV pre-wrap + RC3/D167 non-PoT mask-period wrap (opt-in; GE_WRAPFIX env overrides) */ static int cfgFullscreen = 0; /* 0 = windowed, 1 = borderless fullscreen */ /*