mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-25 18:05:12 -04:00
PGXP depth: no cost when off; render-thread-safe setters; Smooth motion honours depth
- gpu.c sends gr_set_depth_triangle only while a PGXP renderer feature is on (gl_renderer_pgxp_render_wanted): no RTH_DEPTH per triangle otherwise. - The SZ rides in the textured vertex's unused colour alpha, so TEXV stays 26 floats for every title. - gl_renderer_set_pgxp_depth / _threshold / pgxp_render_stats sync with a live render thread (GL_RT_SYNC) like their neighbours. - A depth-mode change is its own batch-break reason (7); batch_diag returns all eight reasons. - Smooth-motion in-between frames replay RTH_DEPTH and test depth from their own clear; gl_frame_gen_test gains a PGXP pass (overlapping 3D triangles out of painter order) where the in-between frame must match the real one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1270,6 +1270,18 @@ differ by 801 / 92 / 25 / 0 px, none in the tachometer area.
|
||||
Diagnostic: `PSX_PGXP_TRI_LOG=<file>` logs every triangle (depth mode, x y
|
||||
SZ) while `<file>.on` exists.
|
||||
|
||||
**Off costs nothing; Smooth motion matches.** gpu.c sends
|
||||
`gr_set_depth_triangle` only while a PGXP renderer feature is on
|
||||
(`gl_renderer_pgxp_render_wanted`), so with them off the render thread
|
||||
records no extra `RTH_DEPTH` per triangle. The SZ rides in the textured
|
||||
vertex's unused colour alpha (negative when present), so the vertex stays 26
|
||||
floats for every title. The setters sync with a live render thread
|
||||
(`GL_RT_SYNC`) like their neighbours. A depth-mode change ends a textured
|
||||
batch as its own reason (`batch_diag` entry 8). Smooth-motion in-between
|
||||
frames replay `RTH_DEPTH` and test depth like the real frame, from their
|
||||
own clear: `gl_frame_gen_test` draws overlapping PGXP triangles out of painter
|
||||
order and requires the in-between frame at phase 1 to equal the real one.
|
||||
|
||||
## IR1 — Internal resolution presets (Native … 8K) and the GL scale ceiling (2026-09-26)
|
||||
|
||||
**What the player gets.** Settings → Display → **Internal resolution**: Native,
|
||||
|
||||
@@ -267,6 +267,9 @@ float gl_renderer_get_post_gamma(void);
|
||||
void gl_renderer_set_wide_fast(int on);
|
||||
/* PGXP renderer features (docs/ENHANCEMENTS.md G1.14), all off by default:
|
||||
* depth buffer for opaque 3D polygons and the depth clear threshold in SZ units (DuckStation's default 4096). */
|
||||
/* Nonzero when a PGXP renderer feature is on: only then does gpu.c send
|
||||
* gr_set_depth_triangle (no extra command per triangle otherwise). Any thread. */
|
||||
int gl_renderer_pgxp_render_wanted(void);
|
||||
void gl_renderer_set_pgxp_depth(int on);
|
||||
int gl_renderer_get_pgxp_depth(void);
|
||||
void gl_renderer_set_pgxp_depth_threshold(float sz);
|
||||
@@ -489,7 +492,7 @@ void gl_renderer_draw_projected_triangle(const PSXProjectedVertex vertices[3],
|
||||
int perspective);
|
||||
/* Cumulative textured-batch diagnostics: total, then flushes caused by
|
||||
* isolation, blend-mode, mask, filter, backdrop-gate, texture-window, capacity. */
|
||||
void gl_renderer_batch_diag(uint64_t out[8]);
|
||||
void gl_renderer_batch_diag(uint64_t out[9]);
|
||||
|
||||
/* Texture-window batching ([video] texture_window_batching; default off).
|
||||
* On: textured prims with different GP0(E2h) texture windows share a batch
|
||||
|
||||
@@ -8574,11 +8574,11 @@ static void handle_frame_perf(int id, const char *json)
|
||||
double wcanon = wide[5] - wide[10]; if (wcanon < 0) wcanon = 0;
|
||||
double wmpp = wide[12] > 0 ? wide[10] * 1000.0 / wide[12] : 0.0;
|
||||
double tex_frac = 0.0; gl_renderer_perf_prim_split(&tex_frac);
|
||||
uint64_t br[8]; extern void gl_renderer_batch_diag(uint64_t out[8]);
|
||||
uint64_t br[9]; extern void gl_renderer_batch_diag(uint64_t out[9]);
|
||||
gl_renderer_batch_diag(br);
|
||||
send_fmt("{\"id\":%d,\"ok\":true,\"samples\":%d,\"wide_frames\":%d,\"frames_4_3\":%d,"
|
||||
"\"tex_frac\":%.3f,\"ws_ablate\":%d,"
|
||||
"\"batch_diag\":[%llu,%llu,%llu,%llu,%llu,%llu,%llu,%llu],"
|
||||
"\"batch_diag\":[%llu,%llu,%llu,%llu,%llu,%llu,%llu,%llu,%llu],"
|
||||
"\"all\":{\"total_ms_avg\":%.3f,\"total_ms_max\":%.3f,\"emu_cpu_ms_avg\":%.3f,"
|
||||
"\"present_wall_ms_avg\":%.3f,\"scene_gpu_ms_avg\":%.3f,\"scene_gpu_ms_max\":%.3f,"
|
||||
"\"present_gpu_ms_avg\":%.3f,\"present_gpu_ms_max\":%.3f,\"prims_avg\":%.0f},"
|
||||
@@ -8598,6 +8598,7 @@ static void handle_frame_perf(int id, const char *json)
|
||||
(unsigned long long)br[2], (unsigned long long)br[3],
|
||||
(unsigned long long)br[4], (unsigned long long)br[5],
|
||||
(unsigned long long)br[6], (unsigned long long)br[7],
|
||||
(unsigned long long)br[8],
|
||||
all[1], all[2], all[3], all[4], all[5], all[6], all[7], all[8], all[9],
|
||||
(int)wide[0], wide[1], wide[3], wide[5], wide[6], wide[7], wide[9], wpp,
|
||||
wide[10], wide[11], wcanon, wide[12], wmpp,
|
||||
|
||||
+5
-2
@@ -4612,7 +4612,10 @@ static void prepare_precise_triangle(int i0, int i1, int i2,
|
||||
gl_renderer_fg_source(id, pc, hd, vx, vy);
|
||||
}
|
||||
gr_set_perspective_triangle(0, 0.0f, 0.0f, 0.0f);
|
||||
gr_set_depth_triangle(0, 0.0f, 0.0f, 0.0f);
|
||||
/* PGXP depth (G1.14) only reaches the renderer while a PGXP renderer
|
||||
* feature is on: with them off no depth command is sent per triangle. */
|
||||
const int want_depth = gl_renderer_pgxp_render_wanted();
|
||||
if (want_depth) gr_set_depth_triangle(0, 0.0f, 0.0f, 0.0f);
|
||||
const int geometry = gte_geometry_correction_enabled();
|
||||
if (!geometry && !s_native_wide_projection_correction) {
|
||||
gr_set_precise_triangle(0, 0,0, 0,0, 0,0);
|
||||
@@ -4670,7 +4673,7 @@ static void prepare_precise_triangle(int i0, int i1, int i2,
|
||||
return;
|
||||
}
|
||||
gr_set_precise_triangle(1, fx[0],fy[0], fx[1],fy[1], fx[2],fy[2]);
|
||||
if (vz[0] && vz[1] && vz[2])
|
||||
if (want_depth && vz[0] && vz[1] && vz[2])
|
||||
gr_set_depth_triangle(1, (float)vz[0], (float)vz[1], (float)vz[2]);
|
||||
}
|
||||
|
||||
|
||||
@@ -715,7 +715,7 @@ static GLuint s_tex_prog = 0, s_tex_vao = 0, s_tex_vbo = 0;
|
||||
* fragment shader read the noperspective varying — i.e. bit-identical to the
|
||||
* pre-feature pipeline. twin is the prim's GP0(E2h) texture window, its low 20
|
||||
* bits as a whole float (mask x, mask y, offset x, offset y; 5 bits each). */
|
||||
#define TEXV 27 /* + a_pz at 26 (PGXP depth, G1.14) */
|
||||
#define TEXV 26
|
||||
static GLuint s_blit_prog = 0, s_blit_vao = 0, s_blit_vbo = 0;
|
||||
static GLuint s_blit_hi_prog = 0; /* windowed hi surface blit */
|
||||
static GLint s_uBhSrc = -1, s_uBhPass = -1, s_uBhMaskset = -1, s_uBhSrcDiv = -1;
|
||||
@@ -1596,7 +1596,6 @@ static const char *TEX_VS =
|
||||
"layout(location=10) in float a_twin; /* GP0(E2h) bits 0..19 */\n"
|
||||
"layout(location=11) in vec4 a_hd_source; /* page origin + native extent */\n"
|
||||
"layout(location=12) in float a_hd_mode;\n"
|
||||
"layout(location=13) in float a_pz; /* PGXP: sz; 0 = none */\n"
|
||||
"uniform float u_shift;\n"
|
||||
"uniform float u_xoff; /* native-wide x translation (px); 0 canonical */\n"
|
||||
"uniform float u_xhalf; /* x clip half-extent (px); 512 canonical */\n"
|
||||
@@ -1631,7 +1630,9 @@ static const char *TEX_VS =
|
||||
" * the rasterizer interpolates the smooth varying hyperbolically. With\n"
|
||||
" * a_q == 0 (feature off) w is exactly 1.0 and this is the old expression. */\n"
|
||||
" float w = (a_q > 0.0) ? (1.0 / a_q) : 1.0;\n"
|
||||
" float sz = a_pz, zn = 0.0;\n"
|
||||
" /* PGXP (G1.14): a_col.a is 1.0 (unused) unless a PGXP 3D vertex\n"
|
||||
" * stores -sz there, so the vertex stays TEXV floats for every title. */\n"
|
||||
" float sz = a_col.a < 0.0 ? -a_col.a : 0.0, zn = 0.0;\n"
|
||||
" if (sz > 0.5) { zn = 1.0 - 512.0 / (max(sz * (1.0 - u_zbias) - (u_zbias > 0.0 ? 48.0 : 0.0), 1.0) + 256.0); }\n"
|
||||
" vec2 ndc = vec2((xb+u_shift+u_xoff)/u_xhalf - 1.0, (a_pos.y+u_shift)/256.0 - 1.0);\n"
|
||||
" gl_Position = vec4(ndc * w, zn * w, w); }\n";
|
||||
@@ -2921,15 +2922,29 @@ void gl_renderer_set_wide_fast(int on) {
|
||||
GL_RT_SYNC("set_wide_fast"); s_wide_fast = on ? 1 : 0; }
|
||||
int gl_renderer_get_wide_fast(void) { return s_wide_fast; }
|
||||
|
||||
/* PGXP renderer features (G1.14). Plain flags read at append time, so they
|
||||
* can flip between any two primitives. */
|
||||
/* PGXP renderer features (G1.14). Flags read at append time, so they can
|
||||
* flip between any two primitives. Like their neighbours the setters sync
|
||||
* with a live render thread first (GL_RT_SYNC), so a change never lands in
|
||||
* the middle of a replayed frame; they are safe at session_reboot. */
|
||||
static int s_pgxp_render_wanted = 0; /* any PGXP renderer feature on */
|
||||
static void pgxp_render_wanted_update(void) {
|
||||
__atomic_store_n(&s_pgxp_render_wanted, s_pgxp_depth ? 1 : 0, __ATOMIC_RELEASE);
|
||||
}
|
||||
int gl_renderer_pgxp_render_wanted(void) {
|
||||
return __atomic_load_n(&s_pgxp_render_wanted, __ATOMIC_ACQUIRE);
|
||||
}
|
||||
void gl_renderer_set_pgxp_depth(int on) {
|
||||
GL_RT_SYNC("set_pgxp_depth");
|
||||
s_pgxp_depth = on ? 1 : 0;
|
||||
s_depth_need_clear = 1;
|
||||
s_pz_valid = 0;
|
||||
pgxp_render_wanted_update();
|
||||
}
|
||||
int gl_renderer_get_pgxp_depth(void) { return s_pgxp_depth; }
|
||||
void gl_renderer_set_pgxp_depth_threshold(float sz) { s_pgxp_depth_threshold = sz; }
|
||||
void gl_renderer_set_pgxp_depth_threshold(float sz) {
|
||||
GL_RT_SYNC("set_pgxp_depth_threshold"); s_pgxp_depth_threshold = sz; }
|
||||
void gl_renderer_pgxp_render_stats(uint64_t *depth_tris, uint64_t *depth_clears) {
|
||||
GL_RT_SYNC("pgxp_render_stats");
|
||||
if (depth_tris) *depth_tris = s_depth_tris;
|
||||
if (depth_clears) *depth_clears = s_depth_clears;
|
||||
}
|
||||
@@ -3128,10 +3143,10 @@ void gl_renderer_set_texture_window_batching(int on) {
|
||||
GL_RT_SYNC("set_texture_window_batching"); s_twin_batching = on ? 1 : 0; }
|
||||
int gl_renderer_get_texture_window_batching(void) { return s_twin_batching; }
|
||||
|
||||
void gl_renderer_batch_diag(uint64_t out[8]) {
|
||||
void gl_renderer_batch_diag(uint64_t out[9]) {
|
||||
GL_RT_SYNC("batch_diag");
|
||||
out[0] = s_batch_total;
|
||||
for (int i = 0; i < 7; i++) out[i + 1] = s_batch_reason[i];
|
||||
for (int i = 0; i < 8; i++) out[i + 1] = s_batch_reason[i];
|
||||
}
|
||||
|
||||
/* Draw the queued textured batch with correct PSX mask-bit handling AND correct
|
||||
@@ -4169,7 +4184,7 @@ static void gpu_textured_triangle(const int *xs, const int *ys,
|
||||
else if ((!s_twin_batching || s_mask_check) &&
|
||||
(twx != s_tb_twin[0] || twy != s_tb_twin[1] ||
|
||||
tox != s_tb_twin[2] || toy != s_tb_twin[3])) reason = 5;
|
||||
else if (tdmode != s_tb_depth) reason = 6;
|
||||
else if (tdmode != s_tb_depth) reason = 7;
|
||||
}
|
||||
if (reason >= 0) {
|
||||
s_batch_reason[reason]++;
|
||||
@@ -4204,12 +4219,14 @@ static void gpu_textured_triangle(const int *xs, const int *ys,
|
||||
vp[21] = (float)hd.origin_u; vp[22] = (float)hd.origin_v;
|
||||
vp[23] = (float)hd.source_width; vp[24] = (float)hd.source_height;
|
||||
vp[25] = (float)hd.alpha_mode;
|
||||
vp[26] = 0.0f; /* a_pz */
|
||||
}
|
||||
if (pgxp_tri_is_3d()) {
|
||||
float *t0 = &s_tb[s_tb_n * TEXV];
|
||||
float z[3] = { s_pz[0], s_pz[1], s_pz[2] };
|
||||
for (int i = 0; i < 3; i++) t0[i * TEXV + 26] = pgxp_vertex_code(z[i]);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
const float code = pgxp_vertex_code(z[i]);
|
||||
if (code > 0.0f) t0[i * TEXV + 7] = -code; /* a_col.a (TEX_VS) */
|
||||
}
|
||||
}
|
||||
pgxp_tri_log_tri('T', tdmode, semi, &s_tb[s_tb_n * TEXV], TEXV);
|
||||
s_tb_n += 3;
|
||||
@@ -5407,7 +5424,6 @@ static int init_gpu_raster(void) {
|
||||
p_glVertexAttribPointer(10, 1, GL_FLOAT, GL_FALSE, st, (void*)(20*sizeof(float))); p_glEnableVertexAttribArray(10); /* twin */
|
||||
p_glVertexAttribPointer(11, 4, GL_FLOAT, GL_FALSE, st, (void*)(21*sizeof(float))); p_glEnableVertexAttribArray(11); /* HD source */
|
||||
p_glVertexAttribPointer(12, 1, GL_FLOAT, GL_FALSE, st, (void*)(25*sizeof(float))); p_glEnableVertexAttribArray(12); /* HD alpha mode */
|
||||
p_glVertexAttribPointer(13, 1, GL_FLOAT, GL_FALSE, st, (void*)(26*sizeof(float))); p_glEnableVertexAttribArray(13); /* pz */
|
||||
}
|
||||
|
||||
p_glGenVertexArrays(1, &s_blit_vao);
|
||||
@@ -10897,7 +10913,9 @@ static void fg_draw_tri_at(uint16_t op, const int32_t *v, const void *payload,
|
||||
a[3], a[4], a[5]);
|
||||
return;
|
||||
}
|
||||
if (!integral) {
|
||||
/* A PGXP depth triangle (s_pz_valid) is 3D only with sub-pixel
|
||||
* positions, as in the real frame: keep them even when integral. */
|
||||
if (!integral || s_pz_valid) {
|
||||
glb_set_precise_triangle(1, (int32_t)lrintf(x[0] * 65536.0f), (int32_t)lrintf(y[0] * 65536.0f),
|
||||
(int32_t)lrintf(x[1] * 65536.0f), (int32_t)lrintf(y[1] * 65536.0f),
|
||||
(int32_t)lrintf(x[2] * 65536.0f), (int32_t)lrintf(y[2] * 65536.0f));
|
||||
@@ -10980,7 +10998,12 @@ static void fg_replay(const FgList *a, const FgList *b, double t, GLuint gen_wid
|
||||
glb_set_perspective_triangle(v[0], q[0], q[1], q[2]);
|
||||
break;
|
||||
}
|
||||
case RTH_DEPTH: break; /* generated frames draw without PGXP depth */
|
||||
case RTH_DEPTH: { /* PGXP depth (G1.14): as the real frame */
|
||||
float z[3];
|
||||
memcpy(z, &v[1], sizeof z);
|
||||
glb_set_depth_triangle(v[0], z[0], z[1], z[2]);
|
||||
break;
|
||||
}
|
||||
case RTH_AREA: glb_set_draw_area(v[0], v[1], v[2], v[3]); break;
|
||||
case RTH_OFFSET: glb_set_draw_offset(v[0], v[1]); break;
|
||||
case RTH_STATE: s_rths_flat_bd = v[0]; s_rths_vp_w = v[1]; s_rths_bg_full = v[2]; break;
|
||||
@@ -11034,6 +11057,7 @@ static void fg_replay(const FgList *a, const FgList *b, double t, GLuint gen_wid
|
||||
const int pi = b->rec2prim ? b->rec2prim[r] : -1;
|
||||
if (pi < 0) { /* outside the display: drawn into VRAM already */
|
||||
pc_pending = 0;
|
||||
precise_consumed();
|
||||
break;
|
||||
}
|
||||
const FgPrim *pb = &b->prims.v[pi];
|
||||
@@ -11046,6 +11070,7 @@ static void fg_replay(const FgList *a, const FgList *b, double t, GLuint gen_wid
|
||||
}
|
||||
if (gone) { /* crossed the camera plane: not in this frame */
|
||||
pc_pending = 0;
|
||||
precise_consumed();
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
@@ -11127,7 +11152,14 @@ static int fg_generate(double t, int swap) {
|
||||
fg_state_capture(&real);
|
||||
const GLuint real_wide_cur = g_wide_cur;
|
||||
const int real_pc = s_pc_valid, real_pq = s_pq_valid, real_pz = s_pz_valid;
|
||||
s_pz_valid = 0; /* generated frames draw without PGXP depth */
|
||||
/* PGXP depth (G1.14): the generated frame tests depth like the real one,
|
||||
* from its own clear (the surface's depth is not the real frame's), and
|
||||
* leaves the real stream's depth bookkeeping and counters untouched. */
|
||||
const int real_dneed = s_depth_need_clear, real_dused = s_depth_used;
|
||||
const float real_davg = s_depth_last_avg;
|
||||
const uint64_t real_dtris = s_depth_tris, real_dclears = s_depth_clears;
|
||||
s_pz_valid = 0;
|
||||
s_depth_need_clear = 1;
|
||||
DirtyRect pack = s_pack_dirty, sten = s_stencil_stale, cpu = s_cpu_dirty;
|
||||
const int sten_valid = s_stencil_valid, gpu_dirty = s_gpu_dirty;
|
||||
uint64_t pres_dirty[PRES_ROWS];
|
||||
@@ -11208,6 +11240,8 @@ static int fg_generate(double t, int swap) {
|
||||
}
|
||||
fg_state_apply(&real, real_wide_cur);
|
||||
s_pc_valid = real_pc; s_pq_valid = real_pq; s_pz_valid = real_pz;
|
||||
s_depth_need_clear = real_dneed; s_depth_used = real_dused; s_depth_last_avg = real_davg;
|
||||
s_depth_tris = real_dtris; s_depth_clears = real_dclears;
|
||||
s_pack_dirty = pack; s_stencil_stale = sten; s_cpu_dirty = cpu;
|
||||
s_stencil_valid = sten_valid; s_gpu_dirty = gpu_dirty;
|
||||
memcpy(s_present_dirty, pres_dirty, sizeof pres_dirty);
|
||||
|
||||
@@ -425,6 +425,8 @@ void gl_renderer_note_wide_triangle_recovery(int enabled) {
|
||||
}
|
||||
/* Smooth motion (frame generation) vertex sources: off in these fixtures. */
|
||||
int gl_renderer_frame_generation(void) { return 0; }
|
||||
/* PGXP renderer features: off in these fixtures. */
|
||||
int gl_renderer_pgxp_render_wanted(void) { return 0; }
|
||||
void gl_renderer_fg_source(const uint32_t id[3], const int32_t pc[9], const int32_t h[3],
|
||||
const int32_t x[3], const int32_t y[3]) {
|
||||
(void)id; (void)pc; (void)h; (void)x; (void)y;
|
||||
|
||||
@@ -28,6 +28,8 @@ WIN_LIBS = ["opengl32", "kernel32", "user32", "gdi32", "winmm", "imm32", "ole32"
|
||||
SKIP_EXIT = 77
|
||||
WINDOWS = os.name == "nt" or platform.system().startswith(("MINGW", "MSYS", "CYGWIN"))
|
||||
KEYS = ("real",)
|
||||
# FG_PGXP values: the scene without PGXP renderer features first.
|
||||
PGXP_FEATURES = ("none", "depth")
|
||||
|
||||
|
||||
def parse(stdout):
|
||||
@@ -187,6 +189,38 @@ def main():
|
||||
if worst > 1 or worst_px > 0.0005:
|
||||
print(f"FAIL scale {s} {path} {timing} fg={fg}: present thread changed real frames")
|
||||
ok = False
|
||||
# PGXP renderer features (docs/ENHANCEMENTS.md G1.14): the scene as PGXP
|
||||
# 3D triangles that overlap out of painter order. Per feature set, the
|
||||
# real frames are the same with generation off and on, and the fixture's
|
||||
# own check holds: the generated frame at phase 1 equals the real one
|
||||
# (generated frames honour the features). Each feature changes the
|
||||
# real image (it is visible in this scene), so the comparison means
|
||||
# something.
|
||||
for s in [int(v) for v in args.scales.split(",") if v]:
|
||||
digests = {}
|
||||
for feats in PGXP_FEATURES:
|
||||
runs = {}
|
||||
for fg in (0, 1):
|
||||
sub = dest / f"pgxp-{s}-{feats}-{fg}"
|
||||
sub.mkdir(exist_ok=True)
|
||||
cmd = [str(c) for c in (probe, s, fg, "vram", args.frames, "flip")]
|
||||
r = subprocess.run(cmd, cwd=sub, capture_output=True, text=True, encoding="utf-8",
|
||||
errors="replace", env=dict(os.environ, FG_PGXP=feats))
|
||||
p = parse(r.stdout)
|
||||
tail = r.stdout.strip().splitlines()[-5:]
|
||||
print(f"scale {s} pgxp={feats} frame_generation={fg}: exit={r.returncode}", tail,
|
||||
r.stderr.strip()[-800:])
|
||||
if r.returncode or p["failures"] != 0 or p["real"] is None:
|
||||
ok = False
|
||||
runs[fg] = p["real"]
|
||||
if runs[0] != runs[1]:
|
||||
print(f"FAIL scale {s} pgxp={feats}: the real frames differ with generation on")
|
||||
ok = False
|
||||
digests[feats] = runs[0]
|
||||
for feats in PGXP_FEATURES[1:]:
|
||||
if digests[feats] == digests[PGXP_FEATURES[0]]:
|
||||
print(f"FAIL scale {s} pgxp={feats}: the feature does not change the real image")
|
||||
ok = False
|
||||
print("PASS" if ok else "FAIL")
|
||||
return 0 if ok else 1
|
||||
|
||||
|
||||
@@ -179,6 +179,14 @@ static void textures(void) {
|
||||
}
|
||||
|
||||
static int wide_mode;
|
||||
/* FG_PGXP=<features> (run_gl_frame_gen.py's PGXP pass): every triangle is a
|
||||
* PGXP 3D triangle (sub-pixel positions and per-vertex GTE SZ, as gpu.c
|
||||
* sends them while a PGXP renderer feature is on), packed so neighbours
|
||||
* overlap with later triangles farther away: the depth buffer, not painter
|
||||
* order, decides the overlaps. "depth" turns the depth buffer on; "none"
|
||||
* draws the same scene without it. The generated frame at phase 1 must still
|
||||
* equal the real one. */
|
||||
static int g_pgxp, g_pgxp_depth;
|
||||
/* What gpu.c records for a GTE-projected triangle (gl_renderer_fg_source):
|
||||
* vertex identities and camera-space positions that project (H = z = 1000)
|
||||
* to the triangle's screen positions relative to its buffer. */
|
||||
@@ -200,6 +208,12 @@ static void fixture_sources(int i, int bx, int x, int y) {
|
||||
pc[3 * k] = xs[k] - bx; pc[3 * k + 1] = ys[k]; pc[3 * k + 2] = 1000; h[k] = 1000;
|
||||
}
|
||||
gl_renderer_fg_source(id, pc, h, xs, ys);
|
||||
if (g_pgxp) {
|
||||
gr_set_precise_triangle(1, xs[0] * 65536, ys[0] * 65536, xs[1] * 65536, ys[1] * 65536,
|
||||
xs[2] * 65536, ys[2] * 65536);
|
||||
const float z = 2000.0f + 400.0f * (float)i; /* steps beyond the 2% tolerance */
|
||||
gr_set_depth_triangle(1, z, z + 7.0f * (float)(i % 3), z + 3.0f);
|
||||
}
|
||||
}
|
||||
|
||||
/* One game frame drawn into buffer bx. Positions move by whole pixels with
|
||||
@@ -218,7 +232,7 @@ static void game_frame_part(int g, int bx, int part) {
|
||||
for (int i = part ? 20 : 0; i < (part ? 40 : 20); i++) {
|
||||
/* The camera pans one pixel per game frame (every triangle moves
|
||||
* alike); triangle 5 is an object moving on its own. */
|
||||
int x = bx + 10 + (i % 8) * 36 + (i == 5 ? (g * 3) % 9 : g % 9) - 4;
|
||||
int x = bx + 10 + (i % 8) * (g_pgxp ? 20 : 36) + (i == 5 ? (g * 3) % 9 : g % 9) - 4;
|
||||
int y = 12 + (i / 8) * 42 + (i == 5 ? g % 5 : 0);
|
||||
if (i == 0 && g % 5 == 0) { x = bx + 250 - (g * 37) % 200; y = 180; }
|
||||
fixture_sources(i, bx, x, y);
|
||||
@@ -251,6 +265,11 @@ int main(int argc, char **argv) {
|
||||
* the next one (as R4 does), so a flip is not a list boundary. */
|
||||
int late = argc > 5 && !strcmp(argv[5], "late");
|
||||
pt_mode = argc > 6 && !strcmp(argv[6], "pt");
|
||||
{
|
||||
const char *e = getenv("FG_PGXP");
|
||||
g_pgxp = e && *e;
|
||||
g_pgxp_depth = g_pgxp && strstr(e, "depth") != NULL;
|
||||
}
|
||||
if (SDL_Init(SDL_INIT_VIDEO) != 0) {
|
||||
fprintf(stderr, "SKIP no video (%s)\n", SDL_GetError());
|
||||
return 77;
|
||||
@@ -283,6 +302,10 @@ int main(int argc, char **argv) {
|
||||
textures();
|
||||
if (wide_mode) gr_wide_configure(426, 53);
|
||||
if (pt_mode) gl_renderer_set_present_thread(1, 3);
|
||||
if (g_pgxp) {
|
||||
gl_renderer_set_pgxp_depth(g_pgxp_depth);
|
||||
check(gl_renderer_pgxp_render_wanted() == g_pgxp_depth, "PGXP renderer features as asked");
|
||||
}
|
||||
check(gl_renderer_render_thread_start(2) == 1, "render thread started");
|
||||
if (pt_mode) check(gl_renderer_present_thread_active(), "present thread started");
|
||||
if (fg) {
|
||||
@@ -371,6 +394,12 @@ int main(int argc, char **argv) {
|
||||
check(n_gen > 0, "generated frames were presented");
|
||||
}
|
||||
check(glGetError() == GL_NO_ERROR, "GL error");
|
||||
if (g_pgxp_depth) {
|
||||
uint64_t dt = 0, dc = 0;
|
||||
gl_renderer_pgxp_render_stats(&dt, &dc);
|
||||
printf("pgxp depth_tris=%llu depth_clears=%llu\n", (unsigned long long)dt, (unsigned long long)dc);
|
||||
check(dt > 0 && dc > 0, "PGXP depth tested the triangles");
|
||||
}
|
||||
printf("real=%016llx\n", (unsigned long long)real_seq);
|
||||
printf("presents=%llu generated=%llu\n", (unsigned long long)n_real, (unsigned long long)n_gen);
|
||||
gl_renderer_shutdown(); /* stops the render and present threads: all shown */
|
||||
|
||||
@@ -294,6 +294,7 @@ void gl_renderer_note_wide_triangle_recovery(int enabled)
|
||||
{ if (enabled) abort(); }
|
||||
/* Smooth motion vertex sources are an OpenGL path: never reached here. */
|
||||
int gl_renderer_frame_generation(void) { return 0; }
|
||||
int gl_renderer_pgxp_render_wanted(void) { return 0; }
|
||||
void gl_renderer_fg_source(const uint32_t id[3], const int32_t pc[9], const int32_t h[3],
|
||||
const int32_t x[3], const int32_t y[3])
|
||||
{ (void)id; (void)pc; (void)h; (void)x; (void)y; abort(); }
|
||||
|
||||
Reference in New Issue
Block a user