diff --git a/docs/PCPortResearch.md b/docs/PCPortResearch.md index ea8a592e..b35ed49d 100644 --- a/docs/PCPortResearch.md +++ b/docs/PCPortResearch.md @@ -3643,6 +3643,53 @@ it is testable without determinism (static per-quad property). — need an asymmetric in-world texture or a guard-facing check) to tell a HUD-only (direct-draw) flip from a global one. +**D116 runtime probe part 3 (session M-8, overseer). CONTRADICTION — +every stage from font-bitmap to GL-draw verified non-mirrored at runtime, +yet the on-screen glyphs are unambiguously X-flipped. Investigation +budget-capped; deprioritised (cosmetic, not a playability blocker).** +Confirmed symptom (zoomed `ppm/frame_000300.ppm`): "OBJECTIVE C: FAILED" +(via `textRenderGlyphOutlined`) AND the ammo "83" + spare-clip digit (a +*separate* HUD-number path — `textrelated.c` probes never fire for the +digit indices) both render with word/column order preserved and every +glyph individually horizontally mirrored; screen positions correct. +Runtime probes added (`GE_D116`-gated, `#ifdef PORT`): +- `[D116/vbo]` in `gfx_sp_tri1` after the per-vertex `buf_vbo` writes: + for the OBJECTIVE glyph quads (left edge, `tile.uls=0 lrs=28`, + `cms=2`=CLAMP) it prints `vtx0 x=-0.8125 u=0.0` and `vtx2 x=-0.7625 + u=1.0`. **x-left <-> u=0, x-right <-> u=1. The CPU vertex buffer handed + to GL is correct and non-mirrored.** +- `[D116/i8up]` (since reverted) in `import_texture_i8` dumped the + uploaded bytes for glyph 'D' — **byte-identical** to `curchar->pixeldata` + in memory (`00 00 0f 37 34 0b 00 00` ...), and the bitmap is correctly + oriented (stroke col 1, bowl col 6-7). No flip, no word-swap. +- GL backend: vertex shader is `gl_Position = aVtxPos` (fast3d does all + transform on the CPU — no GL matrix can flip anything); + `vTexCoord = aTexCoord` pass-through fragment path; sampler + `GL_CLAMP_TO_EDGE`/`GL_NEAREST`; `gfx_opengl_copy_framebuffer` has only + a `flip_y`, no X flip; `G_TEXRECT` decode (`gfx_pc.cpp:2602`) extracts + `ulxvbo math, the GL shaders/sampler, + the framebuffer blit (each HIGH, runtime-verified). Where it IS: unknown + (LOW). The contradiction means a stage is being mis-modelled — leading + candidates now: (a) the render-*tile* setup (`gDPSetTile`/`gDPSetTileSize` + S params — probes covered the *load* tile) vs fast3d's tile-window + sampling; (b) a GL-driver-level surprise only a real API trace + (RenderDoc/apitrace) would show; (c) the dedicated ammo-digit renderer + (unidentified — not in `textrelated.c`) emitting mirrored S, with + `textRenderGlyphOutlined` doing likewise via a shared lower-level + helper. NEXT PERSON: capture one glyph texrect in RenderDoc, or find + and read the HUD-number path, before touching fast3d again. Do not + exceed ~30 min without one of those in hand. Probe left in tree: + `[D116/vbo]` in `gfx_pc.cpp` (`GE_D116`, zero-cost). + **D117 (session M-8) — frame-to-frame nondeterminism root-caused; `GE_DETERM` fixed-tick mode assessed NOT-narrow, deferred with a design. `tools_pc/framediff.py` added (structural/tolerant).** diff --git a/port/fast3d/gfx_pc.cpp b/port/fast3d/gfx_pc.cpp index 14abcea3..77cd9ada 100644 --- a/port/fast3d/gfx_pc.cpp +++ b/port/fast3d/gfx_pc.cpp @@ -1571,6 +1571,23 @@ static void gfx_sp_tri1(uint8_t vtx1_idx, uint8_t vtx2_idx, uint8_t vtx3_idx, bo buf_vbo[buf_vbo_len++] = u / tex_width[t]; buf_vbo[buf_vbo_len++] = v / tex_height[t]; +#ifdef PORT + { + static int ge_d116 = -1; + if (ge_d116 < 0) ge_d116 = getenv("GE_D116") ? 1 : 0; + if (ge_d116 && is_rect && t == 0 && tex_width[t] > 0 && tex_width[t] <= 32) { + fprintf(stderr, + "[D116/vbo] rect vtx%d x=%.4f y=%.4f u=%.4f v=%.4f " + "(raw v->u=%.1f texw=%.0f texw2=%u tile.uls=%d lrs=%d cms=%d)\n", + i, v_arr[i]->x, v_arr[i]->y, u / tex_width[t], v / tex_height[t], + v_arr[i]->u, tex_width[t], tex_width2[i], + rdp.texture_tile[rdp.first_tile_index + tile].uls, + rdp.texture_tile[rdp.first_tile_index + tile].lrs, + rdp.texture_tile[rdp.first_tile_index + tile].cms); + } + } +#endif + bool clampS = tm & (1 << 2 * t); bool clampT = tm & (1 << (2 * t + 1));