mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-26 18:25:35 -04:00
Merge pull request #38 from mstan/merge/oversized-hardware-primitive-rejection
Reject oversized hardware primitives
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
# PR #16 GPU primitive-size rejection provenance
|
||||
|
||||
This branch isolates Martin Penkava's hardware primitive-size rejection from
|
||||
[`mstan/psxrecomp` PR #16](https://github.com/mstan/psxrecomp/pull/16), exact
|
||||
source commit
|
||||
[`cfc5d0eab94c7ddfd30ed23c4d3beecdc737c835`](https://github.com/mstan/psxrecomp/commit/cfc5d0eab94c7ddfd30ed23c4d3beecdc737c835).
|
||||
|
||||
Last evaluated: 2026-07-14
|
||||
|
||||
| Item | Value |
|
||||
| --- | --- |
|
||||
| Head repository/branch | `shaneomac1337/psxrecomp`, `smackdown2-fixes` |
|
||||
| Pull request/source commit | `mstan/psxrecomp` PR #16, `cfc5d0eab94c7ddfd30ed23c4d3beecdc737c835` |
|
||||
| Local base | `7085721afe338a03cb114321a3576cdff420b732` (`origin/master`) |
|
||||
| Local branch | `fix/pr16-gpu-primitive-size-reject-mpenkava` |
|
||||
|
||||
The 1023-pixel horizontal and 511-pixel vertical limits are PS1 hardware rules
|
||||
documented by No$PSX and implemented by independent emulators including Beetle
|
||||
and DuckStation. The framework checks parsed coordinates before widescreen and
|
||||
draw-offset transforms. Every flat, shaded, textured, and shaded-textured
|
||||
polygon path is covered; quads reject each rendered triangle independently.
|
||||
Mono/shaded lines and both polyline continuations use the same limits. Textured
|
||||
commands still latch their texture-page state before a size rejection.
|
||||
|
||||
The source commit's game name and observed scenes are validation evidence only;
|
||||
no title identifier, address, or configuration is included here.
|
||||
|
||||
Source authorship is retained with:
|
||||
|
||||
```text
|
||||
Co-authored-by: Martin Penkava <mpenkava1337@gmail.com>
|
||||
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
|
||||
```
|
||||
@@ -0,0 +1,34 @@
|
||||
#ifndef PSX_GPU_PRIMITIVE_REJECT_H
|
||||
#define PSX_GPU_PRIMITIVE_REJECT_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/* PS1 hardware primitive-size rejection. Parsed coordinates are checked before
|
||||
* widescreen transforms and draw offsets; offsets do not change distances.
|
||||
* Quads are tested as their two rendered triangles independently. */
|
||||
static inline int psx_gpu_triangle_oversize(const int32_t* vx,
|
||||
const int32_t* vy,
|
||||
int a, int b, int c) {
|
||||
int32_t minx = vx[a], maxx = vx[a];
|
||||
if (vx[b] < minx) minx = vx[b];
|
||||
if (vx[b] > maxx) maxx = vx[b];
|
||||
if (vx[c] < minx) minx = vx[c];
|
||||
if (vx[c] > maxx) maxx = vx[c];
|
||||
if (maxx - minx > 1023) return 1;
|
||||
|
||||
int32_t miny = vy[a], maxy = vy[a];
|
||||
if (vy[b] < miny) miny = vy[b];
|
||||
if (vy[b] > maxy) maxy = vy[b];
|
||||
if (vy[c] < miny) miny = vy[c];
|
||||
if (vy[c] > maxy) maxy = vy[c];
|
||||
return maxy - miny > 511;
|
||||
}
|
||||
|
||||
static inline int psx_gpu_line_oversize(int32_t x0, int32_t y0,
|
||||
int32_t x1, int32_t y1) {
|
||||
int32_t dx = x0 > x1 ? x0 - x1 : x1 - x0;
|
||||
int32_t dy = y0 > y1 ? y0 - y1 : y1 - y0;
|
||||
return dx > 1023 || dy > 511;
|
||||
}
|
||||
|
||||
#endif
|
||||
+60
-31
@@ -11,6 +11,7 @@
|
||||
*/
|
||||
|
||||
#include "gpu.h"
|
||||
#include "gpu_primitive_reject.h"
|
||||
#include "gpu_sw_renderer.h"
|
||||
#include "gpu_render.h"
|
||||
#include "text_xlate.h"
|
||||
@@ -2142,6 +2143,7 @@ static void gp0_exec_mono_tri(void) {
|
||||
for (int i = 0; i < 3; i++) {
|
||||
parse_vertex(gp0_cmd_buf[1 + i], &vx[i], &vy[i]);
|
||||
}
|
||||
if (psx_gpu_triangle_oversize(vx, vy, 0, 1, 2)) return;
|
||||
ws_nw_hud_shift_vertices(vx, 3);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
vx[i] += draw_offset_x;
|
||||
@@ -2158,6 +2160,9 @@ static void gp0_exec_mono_quad(void) {
|
||||
int32_t vx[4], vy[4];
|
||||
for (int i = 0; i < 4; i++)
|
||||
parse_vertex(gp0_cmd_buf[1 + i], &vx[i], &vy[i]);
|
||||
int rej_a = psx_gpu_triangle_oversize(vx, vy, 0, 1, 2);
|
||||
int rej_b = psx_gpu_triangle_oversize(vx, vy, 2, 1, 3);
|
||||
if (rej_a && rej_b) return;
|
||||
|
||||
/* Full-screen filters are commonly encoded as an axis-aligned quad. Drawing
|
||||
* a semi-transparent quad as two independent triangles blends their shared
|
||||
@@ -2187,8 +2192,10 @@ static void gp0_exec_mono_quad(void) {
|
||||
vy[i] += draw_offset_y;
|
||||
}
|
||||
gr_set_semi_transparency(semi_trans, (int)semi_transparency);
|
||||
gr_draw_flat_triangle(vx[0], vy[0], vx[1], vy[1], vx[2], vy[2], color);
|
||||
gr_draw_flat_triangle(vx[2], vy[2], vx[1], vy[1], vx[3], vy[3], color);
|
||||
if (!rej_a)
|
||||
gr_draw_flat_triangle(vx[0], vy[0], vx[1], vy[1], vx[2], vy[2], color);
|
||||
if (!rej_b)
|
||||
gr_draw_flat_triangle(vx[2], vy[2], vx[1], vy[1], vx[3], vy[3], color);
|
||||
}
|
||||
|
||||
/* Execute shaded triangle (GP0 0x30-0x33) — Gouraud shaded */
|
||||
@@ -2201,6 +2208,7 @@ static void gp0_exec_shaded_tri(void) {
|
||||
c[i] = rgb888_to_rgb555(gp0_cmd_buf[i * 2] & 0xFFFFFFu);
|
||||
parse_vertex(gp0_cmd_buf[1 + i * 2], &vx[i], &vy[i]);
|
||||
}
|
||||
if (psx_gpu_triangle_oversize(vx, vy, 0, 1, 2)) return;
|
||||
ws_nw_hud_shift_vertices(vx, 3);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
vx[i] += draw_offset_x;
|
||||
@@ -2229,6 +2237,9 @@ static void gp0_exec_shaded_quad(void) {
|
||||
c[i] = rgb888_to_rgb555(gp0_cmd_buf[i * 2] & 0xFFFFFFu);
|
||||
parse_vertex(gp0_cmd_buf[1 + i * 2], &vx[i], &vy[i]);
|
||||
}
|
||||
int rej_a = psx_gpu_triangle_oversize(vx, vy, 0, 1, 2);
|
||||
int rej_b = psx_gpu_triangle_oversize(vx, vy, 2, 1, 3);
|
||||
if (rej_a && rej_b) return;
|
||||
ws_nw_backdrop_stretch_quad(vx, vy); /* full-frame 2D backdrop stretch (sky gradient; no-op else) */
|
||||
ws_nw_hud_shift_vertices(vx, 4);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
@@ -2244,12 +2255,14 @@ static void gp0_exec_shaded_quad(void) {
|
||||
}
|
||||
}
|
||||
gr_set_semi_transparency(semi_trans, (int)semi_transparency);
|
||||
gr_draw_gouraud_triangle(vx[0], vy[0], c[0],
|
||||
vx[1], vy[1], c[1],
|
||||
vx[2], vy[2], c[2]);
|
||||
gr_draw_gouraud_triangle(vx[2], vy[2], c[2],
|
||||
vx[1], vy[1], c[1],
|
||||
vx[3], vy[3], c[3]);
|
||||
if (!rej_a)
|
||||
gr_draw_gouraud_triangle(vx[0], vy[0], c[0],
|
||||
vx[1], vy[1], c[1],
|
||||
vx[2], vy[2], c[2]);
|
||||
if (!rej_b)
|
||||
gr_draw_gouraud_triangle(vx[2], vy[2], c[2],
|
||||
vx[1], vy[1], c[1],
|
||||
vx[3], vy[3], c[3]);
|
||||
}
|
||||
|
||||
/* Helper: build texpage word from GPU state for SW renderer.
|
||||
@@ -2305,7 +2318,8 @@ static void gp0_exec_textured_tri(void) {
|
||||
/* 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);
|
||||
set_tpage_from_poly(tpage_word); /* latches even for size-rejected polys */
|
||||
if (psx_gpu_triangle_oversize(vx, vy, 0, 1, 2)) return;
|
||||
|
||||
ws_nw_hud_shift_vertices(vx, 3);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
@@ -2341,7 +2355,10 @@ static void gp0_exec_textured_quad(void) {
|
||||
uint16_t clut_y = (clut >> 6) & 0x1FF;
|
||||
uint16_t tpage_word = (uint16_t)(gp0_cmd_buf[4] >> 16);
|
||||
uint16_t tpage = tpage_word & 0x1FF;
|
||||
set_tpage_from_poly(tpage_word);
|
||||
set_tpage_from_poly(tpage_word); /* latches even for size-rejected polys */
|
||||
int rej_a = psx_gpu_triangle_oversize(vx, vy, 0, 1, 2);
|
||||
int rej_b = psx_gpu_triangle_oversize(vx, vy, 2, 1, 3);
|
||||
if (rej_a && rej_b) return;
|
||||
|
||||
/* Widescreen: tagged billboard quads carry CPU-computed pixel offsets the
|
||||
* GTE squash never saw — re-squash every X around the prim's anchor. */
|
||||
@@ -2385,14 +2402,16 @@ static void gp0_exec_textured_quad(void) {
|
||||
}
|
||||
}
|
||||
|
||||
gr_draw_textured_triangle(vx[0], vy[0], u[0], v[0],
|
||||
vx[1], vy[1], u[1], v[1],
|
||||
vx[2], vy[2], u[2], v[2],
|
||||
clut_x, clut_y, tpage);
|
||||
gr_draw_textured_triangle(vx[2], vy[2], u[2], v[2],
|
||||
vx[1], vy[1], u[1], v[1],
|
||||
vx[3], vy[3], u[3], v[3],
|
||||
clut_x, clut_y, tpage);
|
||||
if (!rej_a)
|
||||
gr_draw_textured_triangle(vx[0], vy[0], u[0], v[0],
|
||||
vx[1], vy[1], u[1], v[1],
|
||||
vx[2], vy[2], u[2], v[2],
|
||||
clut_x, clut_y, tpage);
|
||||
if (!rej_b)
|
||||
gr_draw_textured_triangle(vx[2], vy[2], u[2], v[2],
|
||||
vx[1], vy[1], u[1], v[1],
|
||||
vx[3], vy[3], u[3], v[3],
|
||||
clut_x, clut_y, tpage);
|
||||
}
|
||||
|
||||
/* Execute shaded textured triangle (GP0 0x34-0x37) */
|
||||
@@ -2417,7 +2436,8 @@ static void gp0_exec_shaded_textured_tri(void) {
|
||||
uint16_t clut_y = (clut >> 6) & 0x1FF;
|
||||
uint16_t tpage_word = (uint16_t)(gp0_cmd_buf[5] >> 16);
|
||||
uint16_t tpage = tpage_word & 0x1FF;
|
||||
set_tpage_from_poly(tpage_word);
|
||||
set_tpage_from_poly(tpage_word); /* latches even for size-rejected polys */
|
||||
if (psx_gpu_triangle_oversize(vx, vy, 0, 1, 2)) return;
|
||||
|
||||
ws_nw_hud_shift_vertices(vx, 3);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
@@ -2457,7 +2477,10 @@ static void gp0_exec_shaded_textured_quad(void) {
|
||||
uint16_t clut_y = (clut >> 6) & 0x1FF;
|
||||
uint16_t tpage_word = (uint16_t)(gp0_cmd_buf[5] >> 16);
|
||||
uint16_t tpage = tpage_word & 0x1FF;
|
||||
set_tpage_from_poly(tpage_word);
|
||||
set_tpage_from_poly(tpage_word); /* latches even for size-rejected polys */
|
||||
int rej_a = psx_gpu_triangle_oversize(vx, vy, 0, 1, 2);
|
||||
int rej_b = psx_gpu_triangle_oversize(vx, vy, 2, 1, 3);
|
||||
if (rej_a && rej_b) return;
|
||||
|
||||
ws_nw_hud_shift_vertices(vx, 4);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
@@ -2466,14 +2489,16 @@ static void gp0_exec_shaded_textured_quad(void) {
|
||||
}
|
||||
|
||||
gr_set_semi_transparency(semi_trans, (int)semi_transparency);
|
||||
gr_draw_shaded_textured_triangle(vx[0], vy[0], u[0], v[0], c[0],
|
||||
vx[1], vy[1], u[1], v[1], c[1],
|
||||
vx[2], vy[2], u[2], v[2], c[2],
|
||||
clut_x, clut_y, tpage, raw_texture);
|
||||
gr_draw_shaded_textured_triangle(vx[2], vy[2], u[2], v[2], c[2],
|
||||
vx[1], vy[1], u[1], v[1], c[1],
|
||||
vx[3], vy[3], u[3], v[3], c[3],
|
||||
clut_x, clut_y, tpage, raw_texture);
|
||||
if (!rej_a)
|
||||
gr_draw_shaded_textured_triangle(vx[0], vy[0], u[0], v[0], c[0],
|
||||
vx[1], vy[1], u[1], v[1], c[1],
|
||||
vx[2], vy[2], u[2], v[2], c[2],
|
||||
clut_x, clut_y, tpage, raw_texture);
|
||||
if (!rej_b)
|
||||
gr_draw_shaded_textured_triangle(vx[2], vy[2], u[2], v[2], c[2],
|
||||
vx[1], vy[1], u[1], v[1], c[1],
|
||||
vx[3], vy[3], u[3], v[3], c[3],
|
||||
clut_x, clut_y, tpage, raw_texture);
|
||||
}
|
||||
|
||||
/* Execute mono line (GP0 0x40-0x47) — Bresenham */
|
||||
@@ -2483,6 +2508,7 @@ static void gp0_exec_mono_line(void) {
|
||||
int32_t x0, y0, x1, y1;
|
||||
parse_vertex(gp0_cmd_buf[1], &x0, &y0);
|
||||
parse_vertex(gp0_cmd_buf[2], &x1, &y1);
|
||||
if (psx_gpu_line_oversize(x0, y0, x1, y1)) return;
|
||||
int32_t vx[2] = { x0, x1 };
|
||||
ws_nw_hud_shift_vertices(vx, 2);
|
||||
x0 = vx[0]; x1 = vx[1];
|
||||
@@ -2500,6 +2526,7 @@ static void gp0_exec_shaded_line(void) {
|
||||
int32_t x0, y0, x1, y1;
|
||||
parse_vertex(gp0_cmd_buf[1], &x0, &y0);
|
||||
parse_vertex(gp0_cmd_buf[3], &x1, &y1);
|
||||
if (psx_gpu_line_oversize(x0, y0, x1, y1)) return;
|
||||
int32_t vx[2] = { x0, x1 };
|
||||
ws_nw_hud_shift_vertices(vx, 2);
|
||||
x0 = vx[0]; x1 = vx[1];
|
||||
@@ -3574,7 +3601,8 @@ static void gpu_write_gp0_body(uint32_t val) {
|
||||
int32_t x, y;
|
||||
parse_vertex(val, &x, &y);
|
||||
x += draw_offset_x; y += draw_offset_y;
|
||||
if (polyline_has_prev) {
|
||||
if (polyline_has_prev &&
|
||||
!psx_gpu_line_oversize(polyline_prev_x, polyline_prev_y, x, y)) {
|
||||
gr_draw_line(polyline_prev_x, polyline_prev_y, x, y, polyline_color);
|
||||
}
|
||||
polyline_prev_x = x; polyline_prev_y = y;
|
||||
@@ -3616,8 +3644,9 @@ static void gpu_write_gp0_body(uint32_t val) {
|
||||
int32_t x, y;
|
||||
parse_vertex(val, &x, &y);
|
||||
x += draw_offset_x; y += draw_offset_y;
|
||||
gr_draw_shaded_line(polyline_prev_x, polyline_prev_y, polyline_prev_c,
|
||||
x, y, polyline_color);
|
||||
if (!psx_gpu_line_oversize(polyline_prev_x, polyline_prev_y, x, y))
|
||||
gr_draw_shaded_line(polyline_prev_x, polyline_prev_y,
|
||||
polyline_prev_c, x, y, polyline_color);
|
||||
polyline_prev_x = x; polyline_prev_y = y;
|
||||
polyline_prev_c = polyline_color;
|
||||
polyline_has_prev = 1;
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
#include "gpu_primitive_reject.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
static int failures;
|
||||
|
||||
static void check(int condition, const char* name) {
|
||||
if (!condition) {
|
||||
fprintf(stderr, "FAIL: %s\n", name);
|
||||
failures++;
|
||||
}
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
int32_t x_ok[3] = { -512, 511, 0 };
|
||||
int32_t y_ok[3] = { -255, 256, 0 };
|
||||
int32_t x_wide[3] = { -512, 512, 0 };
|
||||
int32_t y_tall[3] = { -256, 256, 0 };
|
||||
|
||||
check(!psx_gpu_triangle_oversize(x_ok, y_ok, 0, 1, 2),
|
||||
"inclusive 1023x511 boundary is accepted");
|
||||
check(psx_gpu_triangle_oversize(x_wide, y_ok, 0, 1, 2),
|
||||
"triangle wider than 1023 is rejected");
|
||||
check(psx_gpu_triangle_oversize(x_ok, y_tall, 0, 1, 2),
|
||||
"triangle taller than 511 is rejected");
|
||||
check(!psx_gpu_line_oversize(-512, -255, 511, 256),
|
||||
"line boundary is accepted");
|
||||
check(psx_gpu_line_oversize(-512, 0, 512, 0),
|
||||
"oversize horizontal line is rejected");
|
||||
check(psx_gpu_line_oversize(0, -256, 0, 256),
|
||||
"oversize vertical line is rejected");
|
||||
|
||||
if (failures) return 1;
|
||||
puts("PASS: PS1 primitive size rejection boundaries");
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user