mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-26 10:19:47 -04:00
diagnostics: per-prim PGXP census, texcorr-miss census, live overclock; xlate fixes-only files
- pgxp_census / pgxp_texcensus (debug server): per-primitive resolution classes and UV-correction miss reasons with screen bboxes for the last complete frame, double-buffered by frame parity. These attributed the swimming-wall and flickering-ship classes down to individual packets. - overclock command: live CPU overclock set/get for anchored same-scene A/Bs (the 100%-vs-900% pacing measurements). - text_xlate: load translation files that carry only vram_patch/glyph entries (previously gated on a non-empty "entry" array), enabling data-fix-only files like the WipEout 3 palette-gap patch. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -147,6 +147,27 @@ uint64_t gpu_gp0_ring_total(void);
|
||||
uint32_t gpu_gp0_ring_capacity(void);
|
||||
uint32_t gpu_gp0_ring_max_words(void);
|
||||
int gpu_gp0_ring_dump_frame(uint32_t frame, GpuGp0RingEntry *out, int max_out);
|
||||
|
||||
/* PGXP per-prim resolution census: one entry per prepared precise triangle
|
||||
* whose vertices did NOT all resolve through the dataflow shadow. cls[] is
|
||||
* per-vertex: 0 = shadow miss (addr known, no valid entry), 1 = geometry-cache
|
||||
* fallback, 2 = dataflow, 3 = no packet address (CPU-built submission path).
|
||||
* Draw-space bbox, so it overlays the display half directly. */
|
||||
typedef struct {
|
||||
int16_t x0, y0, x1, y1;
|
||||
uint8_t cls[3];
|
||||
uint8_t _pad;
|
||||
uint32_t src;
|
||||
uint32_t frame;
|
||||
} PgxpCensusEnt;
|
||||
/* Copies the census of the most recent COMPLETE frame; returns entry count.
|
||||
* tris/clean report that frame's totals (all triangles vs all-dataflow). */
|
||||
int gpu_pgxp_census_dump(uint32_t *frame_out, uint32_t *tris, uint32_t *clean,
|
||||
PgxpCensusEnt *out, int max_out);
|
||||
/* Same shape for texture-UV correction misses: cls[0] carries the reason
|
||||
* (0 = no_source, 1 = no_depth), cls[1] the offending vertex index. */
|
||||
int gpu_pgxp_texcensus_dump(uint32_t *frame_out, PgxpCensusEnt *out, int max_out);
|
||||
|
||||
void gpu_gp0_ring_frame_span(uint32_t *out_oldest, uint32_t *out_newest);
|
||||
|
||||
/* Vblank presentation callback — called from gpu_vblank_tick().
|
||||
|
||||
@@ -8213,6 +8213,67 @@ static void handle_savestate(int id, const char *json)
|
||||
send_fmt("{\"id\":%d,\"ok\":true,\"op\":\"%s\",\"slot\":%d}", id, op, slot);
|
||||
}
|
||||
|
||||
/* Live CPU overclock for anchored A/B: animation-rate artifacts cannot be
|
||||
* attributed to the overclock without flipping it on the exact same scene. */
|
||||
/* Census of imperfect PGXP triangles for the last complete frame: which
|
||||
* screen prims are NOT fully dataflow-corrected, and why, per vertex. */
|
||||
static void handle_pgxp_census(int id, const char *json)
|
||||
{
|
||||
(void)json;
|
||||
static PgxpCensusEnt ents[8192];
|
||||
uint32_t frame = 0, tris = 0, clean = 0;
|
||||
int n = gpu_pgxp_census_dump(&frame, &tris, &clean, ents, 8192);
|
||||
size_t buf_sz = 256 + (size_t)n * 96u;
|
||||
char *buf = (char *)malloc(buf_sz);
|
||||
if (!buf) { send_err(id, "alloc failed"); return; }
|
||||
size_t pos = (size_t)snprintf(buf, buf_sz,
|
||||
"{\"id\":%d,\"ok\":true,\"frame\":%u,\"tris\":%u,\"clean\":%u,"
|
||||
"\"imperfect\":%d,\"entries\":[", id, frame, tris, clean, n);
|
||||
for (int i = 0; i < n && pos < buf_sz - 128; i++) {
|
||||
PgxpCensusEnt *e = &ents[i];
|
||||
pos += (size_t)snprintf(buf + pos, buf_sz - pos,
|
||||
"%s{\"bb\":[%d,%d,%d,%d],\"cls\":[%u,%u,%u],\"src\":\"0x%08X\"}",
|
||||
i ? "," : "", e->x0, e->y0, e->x1, e->y1,
|
||||
e->cls[0], e->cls[1], e->cls[2], e->src);
|
||||
}
|
||||
snprintf(buf + pos, buf_sz - pos, "]}");
|
||||
debug_server_send_line(buf);
|
||||
free(buf);
|
||||
}
|
||||
|
||||
static void handle_pgxp_texcensus(int id, const char *json)
|
||||
{
|
||||
(void)json;
|
||||
static PgxpCensusEnt ents[8192];
|
||||
uint32_t frame = 0;
|
||||
int n = gpu_pgxp_texcensus_dump(&frame, ents, 8192);
|
||||
size_t buf_sz = 256 + (size_t)n * 96u;
|
||||
char *buf = (char *)malloc(buf_sz);
|
||||
if (!buf) { send_err(id, "alloc failed"); return; }
|
||||
size_t pos = (size_t)snprintf(buf, buf_sz,
|
||||
"{\"id\":%d,\"ok\":true,\"frame\":%u,\"misses\":%d,\"entries\":[",
|
||||
id, frame, n);
|
||||
for (int i = 0; i < n && pos < buf_sz - 128; i++) {
|
||||
PgxpCensusEnt *e = &ents[i];
|
||||
pos += (size_t)snprintf(buf + pos, buf_sz - pos,
|
||||
"%s{\"bb\":[%d,%d,%d,%d],\"reason\":%u,\"vtx\":%u,\"why\":%u,\"src\":\"0x%08X\"}",
|
||||
i ? "," : "", e->x0, e->y0, e->x1, e->y1,
|
||||
e->cls[0], e->cls[1], e->cls[2], e->src);
|
||||
}
|
||||
snprintf(buf + pos, buf_sz - pos, "]}");
|
||||
debug_server_send_line(buf);
|
||||
free(buf);
|
||||
}
|
||||
|
||||
static void handle_overclock(int id, const char *json)
|
||||
{
|
||||
int pct = json_get_int(json, "percent", -1);
|
||||
if (pct > 0)
|
||||
psx_set_cpu_overclock((uint32_t)pct);
|
||||
send_fmt("{\"id\":%d,\"ok\":true,\"percent\":%u}",
|
||||
id, psx_get_cpu_overclock());
|
||||
}
|
||||
|
||||
static void handle_turbo(int id, const char *json)
|
||||
{
|
||||
int enabled = json_get_int(json, "enabled", -1);
|
||||
@@ -13645,6 +13706,9 @@ static const CmdEntry s_commands[] = {
|
||||
{ "input_route_stop", handle_input_route_stop },
|
||||
{ "input_route_status",handle_input_route_status },
|
||||
{ "savestate", handle_savestate },
|
||||
{ "pgxp_census", handle_pgxp_census },
|
||||
{ "pgxp_texcensus", handle_pgxp_texcensus },
|
||||
{ "overclock", handle_overclock },
|
||||
{ "turbo", handle_turbo },
|
||||
{ "turbo_state", handle_turbo_state },
|
||||
{ "pause", handle_pause },
|
||||
|
||||
+145
-7
@@ -3411,6 +3411,31 @@ uint32_t gpu_texture_correction_hits(void) {
|
||||
* it, so shared edges between neighbouring triangles cannot disagree by more
|
||||
* than the sub-pixel fraction. The integer delta folds in draw offsets and
|
||||
* any widescreen adjustment already applied by the caller. */
|
||||
/* PGXP resolution census (G1 coverage gap). Double-buffered by frame parity
|
||||
* so the debug thread can read the completed frame while this one builds.
|
||||
* Only imperfect triangles get entries; totals give the denominator. */
|
||||
#define PGXP_CENSUS_CAP 8192
|
||||
static PgxpCensusEnt s_pgxp_census[2][PGXP_CENSUS_CAP];
|
||||
static uint32_t s_pgxp_census_n[2], s_pgxp_census_frame[2];
|
||||
static uint32_t s_pgxp_census_tris[2], s_pgxp_census_clean[2];
|
||||
|
||||
int gpu_pgxp_census_dump(uint32_t *frame_out, uint32_t *tris, uint32_t *clean,
|
||||
PgxpCensusEnt *out, int max_out) {
|
||||
/* newest COMPLETE frame: never the one still building, else the fresher */
|
||||
uint32_t cur = (uint32_t)s_frame_count;
|
||||
int slot;
|
||||
if (s_pgxp_census_frame[0] == cur) slot = 1;
|
||||
else if (s_pgxp_census_frame[1] == cur) slot = 0;
|
||||
else slot = (s_pgxp_census_frame[0] > s_pgxp_census_frame[1]) ? 0 : 1;
|
||||
if (frame_out) *frame_out = s_pgxp_census_frame[slot];
|
||||
if (tris) *tris = s_pgxp_census_tris[slot];
|
||||
if (clean) *clean = s_pgxp_census_clean[slot];
|
||||
int n = (int)s_pgxp_census_n[slot];
|
||||
if (n > max_out) n = max_out;
|
||||
for (int i = 0; i < n; i++) out[i] = s_pgxp_census[slot][i];
|
||||
return n;
|
||||
}
|
||||
|
||||
static void prepare_precise_triangle(int i0, int i1, int i2,
|
||||
const int32_t vx[3], const int32_t vy[3]) {
|
||||
gr_set_perspective_triangle(0, 0.0f, 0.0f, 0.0f);
|
||||
@@ -3421,6 +3446,7 @@ static void prepare_precise_triangle(int i0, int i1, int i2,
|
||||
const int idx[3] = { i0, i1, i2 };
|
||||
int32_t fx[3], fy[3];
|
||||
int any_precise = 0;
|
||||
uint8_t cls[3];
|
||||
for (int i = 0; i < 3; i++) {
|
||||
uint32_t word = gp0_cmd_buf[idx[i]];
|
||||
int32_t raw_x, raw_y;
|
||||
@@ -3430,12 +3456,44 @@ static void prepare_precise_triangle(int i0, int i1, int i2,
|
||||
: gp0_cmd_source_addr + (uint32_t)idx[i] * 4u;
|
||||
int32_t px, py;
|
||||
uint16_t sz;
|
||||
if (pgxp_get_precise_vertex(addr, word, raw_x, raw_y,
|
||||
&px, &py, &sz) != PGXP_SRC_NATIVE)
|
||||
int src = pgxp_get_precise_vertex(addr, word, raw_x, raw_y,
|
||||
&px, &py, &sz);
|
||||
if (src != PGXP_SRC_NATIVE)
|
||||
any_precise = 1;
|
||||
cls[i] = (src == PGXP_SRC_NATIVE && addr == 0xFFFFFFFFu) ? 3u
|
||||
: (uint8_t)src;
|
||||
fx[i] = (int32_t)((int64_t)px + (int64_t)(vx[i] - raw_x) * 65536);
|
||||
fy[i] = (int32_t)((int64_t)py + (int64_t)(vy[i] - raw_y) * 65536);
|
||||
}
|
||||
{
|
||||
uint32_t f = (uint32_t)s_frame_count;
|
||||
int slot = (int)(f & 1);
|
||||
if (s_pgxp_census_frame[slot] != f) {
|
||||
s_pgxp_census_frame[slot] = f;
|
||||
s_pgxp_census_n[slot] = 0;
|
||||
s_pgxp_census_tris[slot] = 0;
|
||||
s_pgxp_census_clean[slot] = 0;
|
||||
}
|
||||
s_pgxp_census_tris[slot]++;
|
||||
if (cls[0] == 2 && cls[1] == 2 && cls[2] == 2) {
|
||||
s_pgxp_census_clean[slot]++;
|
||||
} else if (s_pgxp_census_n[slot] < PGXP_CENSUS_CAP) {
|
||||
PgxpCensusEnt *e = &s_pgxp_census[slot][s_pgxp_census_n[slot]++];
|
||||
int32_t x0 = vx[0], x1 = vx[0], y0 = vy[0], y1 = vy[0];
|
||||
for (int i = 1; i < 3; i++) {
|
||||
if (vx[i] < x0) x0 = vx[i];
|
||||
if (vx[i] > x1) x1 = vx[i];
|
||||
if (vy[i] < y0) y0 = vy[i];
|
||||
if (vy[i] > y1) y1 = vy[i];
|
||||
}
|
||||
e->x0 = (int16_t)x0; e->y0 = (int16_t)y0;
|
||||
e->x1 = (int16_t)x1; e->y1 = (int16_t)y1;
|
||||
e->cls[0] = cls[0]; e->cls[1] = cls[1]; e->cls[2] = cls[2];
|
||||
e->_pad = 0;
|
||||
e->src = gp0_cmd_source_addr;
|
||||
e->frame = f;
|
||||
}
|
||||
}
|
||||
if (!any_precise) {
|
||||
gr_set_precise_triangle(0, 0,0, 0,0, 0,0);
|
||||
return;
|
||||
@@ -3458,6 +3516,7 @@ static struct {
|
||||
uint64_t no_correction; /* texture correction off */
|
||||
uint64_t no_source; /* CPU-built primitive, no packet address */
|
||||
uint64_t no_depth; /* a vertex had no recorded Z, or Z == 0 */
|
||||
uint64_t z_interp; /* armed with mean-filled Z for clipped vertices */
|
||||
} s_texcorr;
|
||||
|
||||
void gpu_texture_correction_stats(uint64_t *attempts, uint64_t *armed,
|
||||
@@ -3473,22 +3532,101 @@ void gpu_texture_correction_stats(uint64_t *attempts, uint64_t *armed,
|
||||
/* Enable perspective UVs only when every position word came from an exact
|
||||
* SWC2 projection store at that same DMA packet address. This preserves the
|
||||
* association through ordering-table reordering and rejects CPU-built UI. */
|
||||
static PgxpCensusEnt s_pgxp_texcensus[2][PGXP_CENSUS_CAP];
|
||||
static uint32_t s_pgxp_texcensus_n[2], s_pgxp_texcensus_frame[2];
|
||||
|
||||
/* Record a texture-UV correction miss with the prim's rough screen bbox
|
||||
* (raw packet coords + draw offset; close enough to localize the surface). */
|
||||
static void pgxp_texcensus_note(const int *indices, uint8_t reason, uint8_t vtx,
|
||||
uint8_t why) {
|
||||
uint32_t f = (uint32_t)s_frame_count;
|
||||
int slot = (int)(f & 1);
|
||||
if (s_pgxp_texcensus_frame[slot] != f) {
|
||||
s_pgxp_texcensus_frame[slot] = f;
|
||||
s_pgxp_texcensus_n[slot] = 0;
|
||||
}
|
||||
if (s_pgxp_texcensus_n[slot] >= PGXP_CENSUS_CAP) return;
|
||||
PgxpCensusEnt *e = &s_pgxp_texcensus[slot][s_pgxp_texcensus_n[slot]++];
|
||||
int32_t x0 = 0, y0 = 0, x1 = 0, y1 = 0;
|
||||
for (int i = 0; i < 3; i++) {
|
||||
int32_t rx, ry;
|
||||
parse_vertex(gp0_cmd_buf[indices[i]], &rx, &ry);
|
||||
rx += draw_offset_x; ry += draw_offset_y;
|
||||
if (i == 0) { x0 = x1 = rx; y0 = y1 = ry; }
|
||||
else {
|
||||
if (rx < x0) x0 = rx;
|
||||
if (rx > x1) x1 = rx;
|
||||
if (ry < y0) y0 = ry;
|
||||
if (ry > y1) y1 = ry;
|
||||
}
|
||||
}
|
||||
e->x0 = (int16_t)x0; e->y0 = (int16_t)y0;
|
||||
e->x1 = (int16_t)x1; e->y1 = (int16_t)y1;
|
||||
e->cls[0] = reason; e->cls[1] = vtx; e->cls[2] = why; e->_pad = 0;
|
||||
e->src = gp0_cmd_source_addr;
|
||||
e->frame = f;
|
||||
}
|
||||
|
||||
int gpu_pgxp_texcensus_dump(uint32_t *frame_out, PgxpCensusEnt *out, int max_out) {
|
||||
uint32_t cur = (uint32_t)s_frame_count;
|
||||
int slot; /* prefer the newest COMPLETE frame, not merely opposite parity */
|
||||
if (s_pgxp_texcensus_frame[0] == cur) slot = 1;
|
||||
else if (s_pgxp_texcensus_frame[1] == cur) slot = 0;
|
||||
else slot = (s_pgxp_texcensus_frame[0] > s_pgxp_texcensus_frame[1]) ? 0 : 1;
|
||||
if (frame_out) *frame_out = s_pgxp_texcensus_frame[slot];
|
||||
int n = (int)s_pgxp_texcensus_n[slot];
|
||||
if (n > max_out) n = max_out;
|
||||
for (int i = 0; i < n; i++) out[i] = s_pgxp_texcensus[slot][i];
|
||||
return n;
|
||||
}
|
||||
|
||||
static void prepare_texture_triangle(int i0, int i1, int i2) {
|
||||
gr_set_perspective_triangle(0, 0.0f, 0.0f, 0.0f);
|
||||
gr_set_depth_triangle(0, 0.0f, 0.0f, 0.0f);
|
||||
s_texcorr.attempts++;
|
||||
if (!s_texture_correction_enabled) { s_texcorr.no_correction++; return; }
|
||||
if (gp0_cmd_source_addr == 0xFFFFFFFFu) { s_texcorr.no_source++; return; }
|
||||
int indices[3] = { i0, i1, i2 };
|
||||
if (gp0_cmd_source_addr == 0xFFFFFFFFu) {
|
||||
s_texcorr.no_source++;
|
||||
pgxp_texcensus_note(indices, 0u, 0u, 0u);
|
||||
return;
|
||||
}
|
||||
uint16_t z[3];
|
||||
int have_z[3];
|
||||
int n_have = 0;
|
||||
int noted = 0;
|
||||
for (int i = 0; i < 3; i++) {
|
||||
uint32_t addr = psx_ram_map_read((gp0_cmd_source_addr + (uint32_t)indices[i] * 4u) & 0x1FFFFFFFu) & ~3u;
|
||||
if (!gte_precision_load_word(addr, gp0_cmd_buf[indices[i]], NULL, NULL, &z[i]) ||
|
||||
z[i] == 0) {
|
||||
s_texcorr.no_depth++;
|
||||
return;
|
||||
have_z[i] = gte_precision_load_word(addr, gp0_cmd_buf[indices[i]],
|
||||
NULL, NULL, &z[i]) && z[i] != 0;
|
||||
if (have_z[i]) n_have++;
|
||||
else if (!noted) {
|
||||
noted = 1;
|
||||
pgxp_texcensus_note(indices, 1u, (uint8_t)indices[i],
|
||||
(uint8_t)pgxp_debug_shadow_class(addr, gp0_cmd_buf[indices[i]]));
|
||||
}
|
||||
}
|
||||
if (n_have == 0) {
|
||||
/* No depth anywhere: affine is the only honest option. */
|
||||
s_texcorr.no_depth++;
|
||||
return;
|
||||
}
|
||||
if (n_have < 3) {
|
||||
/* Clip-generated vertices: the game's near/edge clipper computes new
|
||||
* screen coords with mult/div interpolation, which no exact-provenance
|
||||
* shadow can follow, so their depth is unknowable here. Their TRUE
|
||||
* view depth lies between the polygon's surviving projected vertices,
|
||||
* so the mean of the known depths is a bounded approximation — and
|
||||
* perspective UVs with an approximate q beat the affine fallback,
|
||||
* whose error is what reads as texture swimming on every large
|
||||
* clipped wall (the class lives on near-field walls/pillars). */
|
||||
uint32_t sum = 0;
|
||||
for (int i = 0; i < 3; i++) if (have_z[i]) sum += z[i];
|
||||
uint16_t fill = (uint16_t)(sum / (uint32_t)n_have);
|
||||
if (fill == 0) fill = 1;
|
||||
for (int i = 0; i < 3; i++) if (!have_z[i]) z[i] = fill;
|
||||
s_texcorr.z_interp++;
|
||||
}
|
||||
float q[3] = { 1.0f / (float)z[0], 1.0f / (float)z[1], 1.0f / (float)z[2] };
|
||||
float qmax = q[0];
|
||||
if (q[1] > qmax) qmax = q[1];
|
||||
|
||||
@@ -1262,19 +1262,26 @@ static const char *REPL_FS =
|
||||
"uniform int u_debug;\n"
|
||||
"void main(){\n"
|
||||
" vec2 uv = (v_persp != 0) ? v_uv_p : v_uv;\n"
|
||||
" uv = clamp(uv, vec2(v_limits.xy), vec2(v_limits.zw));\n"
|
||||
/* +0.5 is texel CENTRE, and it is load-bearing here in a way it never was
|
||||
* on the PS1 path. An integer u addresses texel u, whose span in the
|
||||
* replacement is [u, u+1); sampling at u alone lands on its left EDGE. With
|
||||
* NEAREST that still resolves to the right texel, so the omission was
|
||||
* invisible — but this path is LINEAR (a pack image carries detail the
|
||||
* texel grid does not), and there a half-texel shift blends in whatever
|
||||
* sits one texel over. On a font atlas that neighbour is the adjacent
|
||||
* glyph's cell, so every glyph quad drew a hairline of its neighbour along
|
||||
* its own edge: background-coloured slivers cutting through letters and
|
||||
* ink-coloured ones in the gaps. Clamp to v_limits.zw (not +0.999) so the
|
||||
* far edge lands on the last texel's centre rather than past it. */
|
||||
" vec4 t = texture(u_repl, (uv - u_origin + vec2(0.5)) / u_src);\n"
|
||||
/* Continuous source-space sampling with a half-IMAGE-pixel interior clamp.
|
||||
*
|
||||
* The interpolated uv is already a CONTINUOUS texel-space position (a
|
||||
* fragment in the middle of texel u carries uv ~ u+0.5), so adding a
|
||||
* blanket +0.5 texel here double-shifted every sample by half a source
|
||||
* texel. Invisible on big art; fatal on the segment-atlas strips, whose
|
||||
* source rows are ONE texel tall: half a texel is half the image, so the
|
||||
* strip drew with its top half cut off and the wrapped top row appended
|
||||
* at the bottom (the press-start/further-information offset). At glyph
|
||||
* edges the same shift pushed LINEAR samples into the neighbouring atlas
|
||||
* cell (the stray marks below G / right of O and E on loading text).
|
||||
*
|
||||
* The sliver bug the +0.5 originally fixed is handled the right way: the
|
||||
* clamp keeps samples half an IMAGE pixel inside the prim UV cell
|
||||
* ([lo, hi] inclusive texels => continuous cell edge at hi+1), so LINEAR
|
||||
* can never blend across the cell boundary no matter the pack scale. */
|
||||
" vec2 halfimg = 0.5 * u_src / vec2(textureSize(u_repl, 0));\n"
|
||||
" uv = clamp(uv, vec2(v_limits.xy) + halfimg,\n"
|
||||
" vec2(v_limits.zw) + vec2(1.0) - halfimg);\n"
|
||||
" vec4 t = texture(u_repl, (uv - u_origin) / u_src);\n"
|
||||
/* Alpha carries the texel's meaning as the dumper wrote it: 0 = colour
|
||||
* index 0, the cutout hole; 127 = opaque with STP set; 255 = opaque with
|
||||
* STP clear. It is not coverage. */
|
||||
|
||||
@@ -1142,6 +1142,7 @@ static int g_video_perspective_texturing = 0;
|
||||
/* [video] pgxp_depth — depth-test using PGXP's recovered W instead of relying
|
||||
* solely on the ordering table. See gl_renderer_set_pgxp_depth. */
|
||||
static int g_video_pgxp_depth = 0;
|
||||
extern "C" void gl_renderer_set_depth_always(int on);
|
||||
static int g_video_pgxp_cpu_mode = 0;
|
||||
static float g_video_pgxp_tolerance = 0.5f;
|
||||
static int g_video_renderer = PSXRecompV4::DEFAULT_VIDEO_RENDERER;
|
||||
@@ -13273,6 +13274,13 @@ session_reboot:
|
||||
* stretch), squash mode stretches the 4:3 frame into it. */
|
||||
gl_renderer_set_display_aspect(g_video_aspect_num, g_video_aspect_den);
|
||||
gl_renderer_set_pgxp_depth(g_video_pgxp_depth);
|
||||
if (g_video_pgxp_depth) {
|
||||
/* Unarmed prims (no recovered depth: ship model, HUD, CPU-built
|
||||
* effects) must ALWAYS PASS, or the depth test culls them against
|
||||
* world Z they never took part in — measured live: the player ship
|
||||
* and trackside monitors vanish without this. */
|
||||
gl_renderer_set_depth_always(1);
|
||||
}
|
||||
if (g_video_pgxp_depth)
|
||||
std::fprintf(stdout, "psxrecomp: PGXP depth buffer on (ordering-table "
|
||||
"sort augmented by recovered per-vertex W)\n");
|
||||
|
||||
@@ -509,10 +509,11 @@ void load_tables_locked() {
|
||||
try { data = toml::parse(de.path().string()); }
|
||||
catch (...) { continue; }
|
||||
++files;
|
||||
if (!data.contains("entry")) continue;
|
||||
const auto& arr = toml::find(data, "entry");
|
||||
if (!arr.is_array()) continue;
|
||||
for (const auto& e : arr.as_array()) {
|
||||
/* A fixes-only file (vram_patch / glyph-label / msg blocks, no string
|
||||
* table) is valid: gate each block on its own key, never the file. */
|
||||
const auto arr = data.contains("entry") ? toml::find(data, "entry")
|
||||
: toml::value(toml::array{});
|
||||
if (arr.is_array()) for (const auto& e : arr.as_array()) {
|
||||
if (!e.contains("src_hex")) continue;
|
||||
std::string hex = toml::find_or<std::string>(e, "src_hex", "");
|
||||
auto bytes = hex_to_bytes(hex);
|
||||
|
||||
Reference in New Issue
Block a user