mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-25 18:05:12 -04:00
gpu: use exclusive PS1 software-raster edges
Adapt PR #14's biased 32.32 triangle edge DDA and half-open bottom/right coverage across the software raster paths, with a focused pure edge-math regression test. Source: https://github.com/mstan/psxrecomp/pull/14 Source-commit: https://github.com/mstan/psxrecomp/commit/91a654f61dff18806f3eea43f72a30cb34430695 Co-authored-by: Kareem Olim <kareemolim@gmail.com>
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
# PR 14 software-raster triangle coverage
|
||||
|
||||
Evaluated and adapted on 2026-07-14 from Kareem Olim's
|
||||
[PR #14](https://github.com/mstan/psxrecomp/pull/14), exact commit
|
||||
[`91a654f61dff18806f3eea43f72a30cb34430695`](https://github.com/mstan/psxrecomp/commit/91a654f61dff18806f3eea43f72a30cb34430695).
|
||||
|
||||
Adapted scope: the software rasterizer's biased 32.32 edge DDA and half-open
|
||||
bottom/right triangle coverage for flat, Gouraud, textured, and shaded-textured
|
||||
triangles. The reusable edge math was moved to a small tested internal header.
|
||||
|
||||
Excluded: all title references, WebAssembly work, CD/SPU changes, GTE subpixel
|
||||
and perspective experiments, presentation blending, and every other PR #14
|
||||
change. This branch changes only software triangle coverage.
|
||||
@@ -0,0 +1,39 @@
|
||||
#ifndef PSXRECOMP_GPU_SW_EDGES_H
|
||||
#define PSXRECOMP_GPU_SW_EDGES_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/* PS1 polygon coverage uses a biased 32.32 edge DDA with exclusive bottom
|
||||
* and right edges. Float-truncated inclusive spans make adjacent polygons
|
||||
* disagree by a pixel and draw a shared diagonal twice. */
|
||||
static inline int64_t psx_edge_fp(int x) {
|
||||
return ((int64_t)x << 32) + ((1LL << 32) - (1 << 11));
|
||||
}
|
||||
|
||||
static inline int64_t psx_edge_step(int dx, int dy) {
|
||||
int64_t rounding = dx < 0 ? -(int64_t)(dy - 1)
|
||||
: (dx > 0 ? (int64_t)(dy - 1) : 0);
|
||||
return (((int64_t)dx << 32) + rounding) / dy;
|
||||
}
|
||||
|
||||
static inline int psx_edge_unfp(int64_t fp) {
|
||||
return (int)((uint64_t)fp >> 32);
|
||||
}
|
||||
|
||||
/* Vertices must already be sorted by Y. y is in [y0,y2). */
|
||||
static inline void psx_triangle_edges_at_y(int x0, int y0, int x1, int y1,
|
||||
int x2, int y2, int y,
|
||||
int *long_x, int *short_x) {
|
||||
int64_t long_step = psx_edge_step(x2 - x0, y2 - y0);
|
||||
*long_x = psx_edge_unfp(psx_edge_fp(x0) + long_step * (y - y0));
|
||||
|
||||
if (y < y1) {
|
||||
int64_t short_step = psx_edge_step(x1 - x0, y1 - y0);
|
||||
*short_x = psx_edge_unfp(psx_edge_fp(x0) + short_step * (y - y0));
|
||||
} else {
|
||||
int64_t short_step = psx_edge_step(x2 - x1, y2 - y1);
|
||||
*short_x = psx_edge_unfp(psx_edge_fp(x1) + short_step * (y - y1));
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -28,6 +28,7 @@
|
||||
*/
|
||||
|
||||
#include "gpu_sw_renderer.h"
|
||||
#include "gpu_sw_edges.h"
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
@@ -662,33 +663,18 @@ static void raster_flat_triangle(const RTarget *t,
|
||||
int dy_total = y2 - y0;
|
||||
if (dy_total == 0) return;
|
||||
|
||||
for (int y = y0; y <= y2; y++) {
|
||||
for (int y = y0; y < y2; y++) {
|
||||
if (y < t->cy1 || y > t->cy2) continue;
|
||||
|
||||
int second_half = (y >= y1);
|
||||
int seg_height = second_half ? (y2 - y1) : (y1 - y0);
|
||||
if (seg_height == 0) seg_height = 1;
|
||||
|
||||
float alpha = (float)(y - y0) / (float)dy_total;
|
||||
float beta;
|
||||
if (second_half)
|
||||
beta = (float)(y - y1) / (float)seg_height;
|
||||
else
|
||||
beta = (float)(y - y0) / (float)seg_height;
|
||||
|
||||
int xa = x0 + (int)((float)(x2 - x0) * alpha);
|
||||
int xb;
|
||||
if (second_half)
|
||||
xb = x1 + (int)((float)(x2 - x1) * beta);
|
||||
else
|
||||
xb = x0 + (int)((float)(x1 - x0) * beta);
|
||||
int xa, xb;
|
||||
psx_triangle_edges_at_y(x0, y0, x1, y1, x2, y2, y, &xa, &xb);
|
||||
|
||||
if (xa > xb) { int tt = xa; xa = xb; xb = tt; }
|
||||
|
||||
int sx = max_i(xa, t->cx1);
|
||||
int ex = min_i(xb, t->cx2);
|
||||
int ex = min_i(xb, t->cx2 + 1);
|
||||
|
||||
for (int x = sx; x <= ex; x++) {
|
||||
for (int x = sx; x < ex; x++) {
|
||||
put_opaque(t, x, y, color);
|
||||
}
|
||||
}
|
||||
@@ -741,7 +727,7 @@ static void raster_gouraud_triangle(const RTarget *t,
|
||||
int dy_total = y2 - y0;
|
||||
if (dy_total == 0) return;
|
||||
|
||||
for (int y = y0; y <= y2; y++) {
|
||||
for (int y = y0; y < y2; y++) {
|
||||
if (y < t->cy1 || y > t->cy2) continue;
|
||||
|
||||
int second_half = (y >= y1);
|
||||
@@ -756,12 +742,8 @@ static void raster_gouraud_triangle(const RTarget *t,
|
||||
beta = (float)(y - y0) / (float)seg_height;
|
||||
|
||||
/* Interpolate X along edges */
|
||||
int xa = x0 + (int)((float)(x2 - x0) * alpha);
|
||||
int xb;
|
||||
if (second_half)
|
||||
xb = x1 + (int)((float)(x2 - x1) * beta);
|
||||
else
|
||||
xb = x0 + (int)((float)(x1 - x0) * beta);
|
||||
int xa, xb;
|
||||
psx_triangle_edges_at_y(x0, y0, x1, y1, x2, y2, y, &xa, &xb);
|
||||
|
||||
/* Interpolate colors along edges (in 5-bit space) */
|
||||
int ra = r0 + (int)((float)(r2 - r0) * alpha);
|
||||
@@ -788,10 +770,10 @@ static void raster_gouraud_triangle(const RTarget *t,
|
||||
}
|
||||
|
||||
int sx = max_i(xa, t->cx1);
|
||||
int ex = min_i(xb, t->cx2);
|
||||
int ex = min_i(xb, t->cx2 + 1);
|
||||
int span = xb - xa;
|
||||
|
||||
for (int x = sx; x <= ex; x++) {
|
||||
for (int x = sx; x < ex; x++) {
|
||||
/* Interpolate color across the scanline */
|
||||
uint16_t color;
|
||||
if (span > 0) {
|
||||
@@ -859,7 +841,7 @@ static void raster_textured_triangle(const RTarget *t,
|
||||
int dy_total = y2 - y0;
|
||||
if (dy_total == 0) return;
|
||||
|
||||
for (int y = y0; y <= y2; y++) {
|
||||
for (int y = y0; y < y2; y++) {
|
||||
if (y < t->cy1 || y > t->cy2) continue;
|
||||
|
||||
int second_half = (y >= y1);
|
||||
@@ -873,12 +855,8 @@ static void raster_textured_triangle(const RTarget *t,
|
||||
else
|
||||
beta = (float)(y - y0) / (float)seg_height;
|
||||
|
||||
int xa = x0 + (int)((float)(x2 - x0) * alpha);
|
||||
int xb;
|
||||
if (second_half)
|
||||
xb = x1 + (int)((float)(x2 - x1) * beta);
|
||||
else
|
||||
xb = x0 + (int)((float)(x1 - x0) * beta);
|
||||
int xa, xb;
|
||||
psx_triangle_edges_at_y(x0, y0, x1, y1, x2, y2, y, &xa, &xb);
|
||||
|
||||
float ua = u0 + (float)(u2 - u0) * alpha;
|
||||
float va = v0 + (float)(v2 - v0) * alpha;
|
||||
@@ -902,9 +880,9 @@ static void raster_textured_triangle(const RTarget *t,
|
||||
if (span == 0) span = 1;
|
||||
|
||||
int sx = max_i(xa, t->cx1);
|
||||
int ex = min_i(xb, t->cx2);
|
||||
int ex = min_i(xb, t->cx2 + 1);
|
||||
|
||||
for (int x = sx; x <= ex; x++) {
|
||||
for (int x = sx; x < ex; x++) {
|
||||
float t_val = (float)(x - xa) / (float)span;
|
||||
float fu = ua + (ub - ua) * t_val;
|
||||
float fv = va + (vb - va) * t_val;
|
||||
@@ -990,7 +968,7 @@ static void raster_shaded_textured_triangle(const RTarget *t,
|
||||
int dy_total = y2 - y0;
|
||||
if (dy_total == 0) return;
|
||||
|
||||
for (int y = y0; y <= y2; y++) {
|
||||
for (int y = y0; y < y2; y++) {
|
||||
if (y < t->cy1 || y > t->cy2) continue;
|
||||
|
||||
int second_half = (y >= y1);
|
||||
@@ -1004,12 +982,8 @@ static void raster_shaded_textured_triangle(const RTarget *t,
|
||||
else
|
||||
beta = (float)(y - y0) / (float)seg_height;
|
||||
|
||||
int xa = x0 + (int)((float)(x2 - x0) * alpha);
|
||||
int xb;
|
||||
if (second_half)
|
||||
xb = x1 + (int)((float)(x2 - x1) * beta);
|
||||
else
|
||||
xb = x0 + (int)((float)(x1 - x0) * beta);
|
||||
int xa, xb;
|
||||
psx_triangle_edges_at_y(x0, y0, x1, y1, x2, y2, y, &xa, &xb);
|
||||
|
||||
float ua = u0 + (float)(u2 - u0) * alpha;
|
||||
float va = v0 + (float)(v2 - v0) * alpha;
|
||||
@@ -1046,9 +1020,9 @@ static void raster_shaded_textured_triangle(const RTarget *t,
|
||||
if (span == 0) span = 1;
|
||||
|
||||
int sx = max_i(xa, t->cx1);
|
||||
int ex = min_i(xb, t->cx2);
|
||||
int ex = min_i(xb, t->cx2 + 1);
|
||||
|
||||
for (int x = sx; x <= ex; x++) {
|
||||
for (int x = sx; x < ex; x++) {
|
||||
float t_val = (float)(x - xa) / (float)span;
|
||||
float fu = ua + (ub - ua) * t_val;
|
||||
float fv = va + (vb - va) * t_val;
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
#include "../src/gpu_sw_edges.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
static int failures;
|
||||
#define CHECK(condition, message) do { \
|
||||
if (!(condition)) { fprintf(stderr, "FAIL: %s\n", message); failures++; } \
|
||||
} while (0)
|
||||
|
||||
int main(void) {
|
||||
int long_x = 0, short_x = 0;
|
||||
|
||||
/* A right triangle's final scanline is y2-1; y2 itself is excluded by the
|
||||
* caller. Pin the DDA values used to produce the half-open X span. */
|
||||
psx_triangle_edges_at_y(0, 0, 8, 0, 0, 8, 0, &long_x, &short_x);
|
||||
CHECK(long_x == 0 && short_x == 8, "top scanline spans [0,8)");
|
||||
psx_triangle_edges_at_y(0, 0, 8, 0, 0, 8, 7, &long_x, &short_x);
|
||||
CHECK(long_x == 0 && short_x == 1, "last scanline spans one pixel");
|
||||
|
||||
CHECK(psx_edge_step(8, 8) == (1LL << 32),
|
||||
"positive unit slope has exact 32.32 step");
|
||||
CHECK(psx_edge_step(-8, 8) == -(1LL << 32),
|
||||
"negative unit slope has exact 32.32 step");
|
||||
|
||||
if (failures) return 1;
|
||||
puts("ALL PASS");
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user