mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-25 18:05:12 -04:00
gpu: textured polygons update draw-mode state from their texpage word
Real GPU copies the texpage attribute word carried inside every textured polygon (GP0 0x24-0x3F, texture bit set) into the draw-mode state (GPUSTAT bits 0-8), exactly like GP0(E1) bits 0-8 — Beetle runs SetTPage(CB[4 + ((cc>>4)&1)] >> 16) on every such command (beetle-psx mednafen/psx/gpu.cpp:1070). So the polygon's OWN word (not the last E1) selects its semi-transparency mode, and later rect/sprite prims (which carry no texpage word) consume the state the polygon left behind. We fetched texels from the prim's page but blended with the stale E1 global, and never updated the page state rects depend on. Effect: additive glows blended with whatever mode the last E1 happened to set, flipping bright light halos into dark/black blobs intermittently (OT bucket order decides which prim precedes, hence camera/distance dependence). Proven on Ape Escape attract frame 60052: nine op-0x2E firework quads at the artifact coordinates carry prim mode 1 (B+F) while the last E1 set mode 0 (B/2+F/2) — rendered as dark maroon blobs; Beetle renders the same beat as bright fireworks. Post-fix sweep shows siren-hat glow halos, MONKEY PARK marquee bloom, and lava glow restored across two full attract cycles. Poly words do not touch dither/draw-to-display (E1-only bits 9-10) nor texture-disable (bit 11 latches only under GP1(09h) allow-change). All backends inherit the fix via gr_set_semi_transparency / current_texpage(). Faithful-core fix for all titles; documented as D12 in accuracy/axis5_gpu.md (the audit had marked semi-transparency FAITHFUL — the modes were, their state source was not). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -216,6 +216,25 @@ vs `beetle-psx/mednafen/psx/<file>:line` (oracle).
|
||||
and can scribble across VRAM where hardware would have dropped it).
|
||||
- **Effect**: rare; mainly a robustness/timing divergence. Low priority.
|
||||
|
||||
### D12 — Textured-polygon texpage attribute did not update GPU draw-mode state — FIXED 2026-07-05
|
||||
- **Oracle**: every textured polygon's texpage word is copied into the GPU
|
||||
draw-mode state before drawing — `SetTPage(CB[4 + ((cc>>4)&1)] >> 16)` runs
|
||||
for all GP0 0x20-0x3F with the texture bit (gpu.cpp:1070-1072). The poly's
|
||||
own word (not the last E1) selects its semi-transparency mode, and later
|
||||
rect/sprite prims (no texpage word) consume the state the poly left behind.
|
||||
Poly words cannot alter dither/draw-to-display (E1-only bits 9-10) nor
|
||||
texture-disable (bit 11 latches only under GP1(09h) TexDisableAllowChange).
|
||||
- **Ours (pre-fix)**: textured polys fetched texels from their own tpage but
|
||||
blended with the stale E1-set global `semi_transparency`, and never updated
|
||||
`texpage_x/y/colors` for subsequent rects.
|
||||
- **Effect**: additive glows (mode 1 in the prim word) blended with whatever
|
||||
mode the last E1 set — Ape Escape's headlamp halos / siren flares / firework
|
||||
bursts rendered as dark blobs or black starbursts, intermittently (depends
|
||||
on OT bucket order, hence camera/distance). Proven on Ape attract frame
|
||||
60052: nine op-0x2E quads at the artifact coords carried prim mode 1 vs E1
|
||||
mode 0. Fixed in `gpu.c set_tpage_from_poly()`, called by all four textured
|
||||
poly handlers; all backends inherit via `gr_set_semi_transparency`.
|
||||
|
||||
### Items that are FAITHFUL (no action)
|
||||
- Semi-transparency 4-mode selection + STP-bit gating (SW and GL).
|
||||
- Mask set/check including in copies (SW and GL).
|
||||
|
||||
+26
-4
@@ -1680,6 +1680,21 @@ static uint16_t current_texpage(void) {
|
||||
(semi_transparency << 5) | (texpage_colors << 7));
|
||||
}
|
||||
|
||||
/* Hardware: the texpage attribute word carried inside every textured polygon
|
||||
* (GP0 0x24-0x3F with the texture bit) is copied into the GPU draw-mode state
|
||||
* (GPUSTAT bits 0-8) exactly like GP0(E1) bits 0-8 — the poly's own word, not
|
||||
* the last E1, decides its semi-transparency mode, and later rectangle/sprite
|
||||
* prims (which carry no texpage word) consume the state the poly left behind.
|
||||
* Beetle: SetTPage(CB[4 + ((cc>>4)&1)] >> 16) on every textured poly.
|
||||
* Bits 9-10 (dither / draw-to-display) exist only in E1; bit 11 (texture
|
||||
* disable) only latches when GP1(09h) allowed it, which polys can't grant. */
|
||||
static void set_tpage_from_poly(uint16_t tpage_word) {
|
||||
texpage_x = tpage_word & 0xF;
|
||||
texpage_y = (tpage_word >> 4) & 1;
|
||||
semi_transparency = (tpage_word >> 5) & 3;
|
||||
texpage_colors = (tpage_word >> 7) & 3;
|
||||
}
|
||||
|
||||
/* Helper: set up SW renderer state before a textured draw.
|
||||
* semi_trans: whether the primitive has semi-transparency enabled (from opcode bit)
|
||||
* raw_texture: 1 if this is a raw-texture opcode (bit 0 of GP0 opcode set) */
|
||||
@@ -1708,9 +1723,10 @@ static void gp0_exec_textured_tri(void) {
|
||||
uint16_t clut = (uint16_t)(gp0_cmd_buf[2] >> 16);
|
||||
uint16_t clut_x = (clut & 0x3F) * 16;
|
||||
uint16_t clut_y = (clut >> 6) & 0x1FF;
|
||||
/* Texpage from word 4 bits 16-31, or use current state */
|
||||
/* Texpage from word 4 bits 16-31 */
|
||||
uint16_t tpage_word = (uint16_t)(gp0_cmd_buf[4] >> 16);
|
||||
uint16_t tpage = tpage_word & 0x1FF;
|
||||
set_tpage_from_poly(tpage_word);
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
vx[i] += draw_offset_x;
|
||||
@@ -1743,7 +1759,9 @@ static void gp0_exec_textured_quad(void) {
|
||||
uint16_t clut = (uint16_t)(gp0_cmd_buf[2] >> 16);
|
||||
uint16_t clut_x = (clut & 0x3F) * 16;
|
||||
uint16_t clut_y = (clut >> 6) & 0x1FF;
|
||||
uint16_t tpage = (uint16_t)(gp0_cmd_buf[4] >> 16) & 0x1FF;
|
||||
uint16_t tpage_word = (uint16_t)(gp0_cmd_buf[4] >> 16);
|
||||
uint16_t tpage = tpage_word & 0x1FF;
|
||||
set_tpage_from_poly(tpage_word);
|
||||
|
||||
/* Widescreen: tagged billboard quads carry CPU-computed pixel offsets the
|
||||
* GTE squash never saw — re-squash every X around the prim's anchor. */
|
||||
@@ -1815,7 +1833,9 @@ static void gp0_exec_shaded_textured_tri(void) {
|
||||
uint16_t clut = (uint16_t)(gp0_cmd_buf[2] >> 16);
|
||||
uint16_t clut_x = (clut & 0x3F) * 16;
|
||||
uint16_t clut_y = (clut >> 6) & 0x1FF;
|
||||
uint16_t tpage = (uint16_t)(gp0_cmd_buf[5] >> 16) & 0x1FF;
|
||||
uint16_t tpage_word = (uint16_t)(gp0_cmd_buf[5] >> 16);
|
||||
uint16_t tpage = tpage_word & 0x1FF;
|
||||
set_tpage_from_poly(tpage_word);
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
vx[i] += draw_offset_x;
|
||||
@@ -1852,7 +1872,9 @@ static void gp0_exec_shaded_textured_quad(void) {
|
||||
uint16_t clut = (uint16_t)(gp0_cmd_buf[2] >> 16);
|
||||
uint16_t clut_x = (clut & 0x3F) * 16;
|
||||
uint16_t clut_y = (clut >> 6) & 0x1FF;
|
||||
uint16_t tpage = (uint16_t)(gp0_cmd_buf[5] >> 16) & 0x1FF;
|
||||
uint16_t tpage_word = (uint16_t)(gp0_cmd_buf[5] >> 16);
|
||||
uint16_t tpage = tpage_word & 0x1FF;
|
||||
set_tpage_from_poly(tpage_word);
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
vx[i] += draw_offset_x;
|
||||
|
||||
Reference in New Issue
Block a user