Cache indexed texture page decoding without changing coverage

This commit is contained in:
Shokunin
2026-10-21 05:25:37 -07:00
committed by Matthew Stanley
parent 14e20d67f7
commit a46dd28293
8 changed files with 247 additions and 16 deletions
+4
View File
@@ -35,6 +35,10 @@ Optional bounded benchmark controls:
This serializes the pipeline and is attribution only. Totals cover the
whole replay, including warmup. `.rth.json` records queue waits and recent
context-acquisition reasons before the report's own drain.
`PSX_GL_CPU_PERF=3` instead samples one in 128 high-resolution textured
depth batches: `colour_gpu_sample` and `depth_gpu_sample` finish GPU work
around the same batch's colour and depth draws. Sampling perturbs cadence;
these completion spans are not acceptance frame times.
`PSX_GL_PERF=1` additionally enables GPU queries in production and exports
rolling GPU aggregates to `.gpu.csv`; use this for separate attribution
trials, since queries can perturb the measured workload.
+11
View File
@@ -2044,3 +2044,14 @@ Guest-frame lookup merged multiple extra presentations and could not verify
HUD stability. Added duplicate-frame/freeze/eviction regression coverage and
retained legacy frame lookup. Six focused framework checks and all 21 title
checks pass on Windows and Linux, and both runtime builds link successfully.
### 2026-10-21: Production indexed texture decode reuse
Cache native 1555 indexed-page/CLUT decode results with raw-VRAM tile
generations and deferred-draw layer pinning. No filtering or guest behavior
changes. Hardware fixtures compare five hashes with cache off and forced
eviction, normal/HiW allocation, 1x/4x, render thread off/on; all pass.
R4 recorded-route production probe improves 53.371 to 57.472 guest Hz;
zero-gap acceptance remains open. Matched GPU completion probes also reject
a separate depth shader (no improvement); that experiment was removed.
See title analysis/render-60fps/SLOWDOWNS.md for receipts and limitations.
+3 -3
View File
@@ -571,9 +571,9 @@ int gl_renderer_perf_aggregate(int wide_filter, double out[18]);
/* Bounded benchmark attribution. CPU spans require PSX_GL_CPU_PERF=1;
* reading drains the render queue, so read outside the measured interval. */
typedef struct {
uint64_t counts[5];
uint64_t calls[6], ticks[6], max_ticks[6], frequency;
uint64_t prior_ticks[6], prior_max_ticks[6];
uint64_t counts[8];
uint64_t calls[8], ticks[8], max_ticks[8], frequency;
uint64_t prior_ticks[8], prior_max_ticks[8];
} GlRenderWorkStats;
void gl_renderer_work_stats(GlRenderWorkStats *out);
+158
View File
@@ -0,0 +1,158 @@
/* Exact indexed-page decode cache. Included after TEX_FS and batch helpers.
* Array layers contain native 1555 words, not filtered/display colours. */
#ifndef GL_TEXTURE_2D_ARRAY
#define GL_TEXTURE_2D_ARRAY 0x8C1A
#endif
typedef void (APIENTRY *TcImage3D)(GLenum, GLint, GLint, GLsizei, GLsizei, GLsizei,
GLint, GLenum, GLenum, const void *);
typedef void (APIENTRY *TcAttachLayer)(GLenum, GLenum, GLuint, GLint, GLint);
#define TC_CAP 256
typedef struct {
GLuint source, palette;
int x, y, depth, cx, cy, valid, pinned;
uint64_t stamp, used;
} TexelCacheEntry;
static TexelCacheEntry s_tc[TC_CAP];
static uint64_t s_tc_tiles[32][64], s_tc_epoch, s_tc_clock;
static uint64_t s_tc_hits, s_tc_misses, s_tc_evictions;
static GLuint s_tc_array, s_tc_fbo, s_tc_prog;
static GLint s_tc_page, s_tc_clut, s_tc_depth;
static int s_tc_ready = -1, s_tc_capacity = TC_CAP;
static TcAttachLayer s_tc_attach;
static int s_tc_pins[TC_CAP], s_tc_pin_n;
static void texel_cache_pin(int slot) {
if (!s_tc[slot].pinned) { s_tc[slot].pinned=1; s_tc_pins[s_tc_pin_n++]=slot; }
}
static void texel_cache_unpin(void) {
for (int i=0; i<s_tc_pin_n; ++i) s_tc[s_tc_pins[i]].pinned=0;
s_tc_pin_n=0;
}
static void texel_cache_dirty(int x0, int y0, int x1, int y1) {
if (s_tc_ready != 1) return;
if (x0 < 0) x0 = 0; if (y0 < 0) y0 = 0;
if (x1 > 1023) x1 = 1023; if (y1 > 511) y1 = 511;
if (x1 < x0 || y1 < y0) return;
uint64_t stamp = ++s_tc_epoch;
for (int y=y0/16; y<=y1/16; ++y)
for (int x=x0/16; x<=x1/16; ++x) s_tc_tiles[y][x] = stamp;
}
static uint64_t texel_cache_region(int x, int y, int w, int h) {
uint64_t stamp = 0;
for (int ty=y/16; ty<=(y+h-1)/16; ++ty)
for (int tx=x/16; tx<=(x+w-1)/16; ++tx) {
uint64_t v = s_tc_tiles[ty & 31][tx & 63];
if (v > stamp) stamp = v;
}
return stamp;
}
static void texel_cache_reset(void) {
memset(s_tc, 0, sizeof s_tc);
memset(s_tc_tiles, 0, sizeof s_tc_tiles);
s_tc_epoch = s_tc_clock = s_tc_hits = s_tc_misses = s_tc_evictions = 0;
s_tc_array = s_tc_fbo = s_tc_prog = 0;
s_tc_ready = -1;
s_tc_pin_n = 0;
}
static int texel_cache_init(void) {
if (s_tc_ready >= 0) return s_tc_ready;
s_tc_ready = 0;
const char *e = getenv("PSX_GL_TEXEL_CACHE");
if (e && e[0] == '0') return 0;
e = getenv("PSX_GL_TEXEL_CACHE_LAYERS");
s_tc_capacity = e ? atoi(e) : TC_CAP;
if (s_tc_capacity < 4) s_tc_capacity = 4;
if (s_tc_capacity > TC_CAP) s_tc_capacity = TC_CAP;
TcImage3D image = (TcImage3D)SDL_GL_GetProcAddress("glTexImage3D");
s_tc_attach = (TcAttachLayer)SDL_GL_GetProcAddress("glFramebufferTextureLayer");
if (!image || !s_tc_attach) return 0;
/* Reuse the literal native address/decode functions from TEX_FS. The
* decoder has no window or UV bounds: fetch_texel applies those before
* it samples the decoded layer. This avoids a second decode definition. */
const char *helpers = strstr(TEX_FS, "int vram_at(");
const char *fetch = strstr(TEX_FS, "int fetch_texel(");
const char *native = strstr(TEX_FS, " if (v_depth == 0)");
const char *end = strstr(TEX_FS, "vec3 col5(");
if (!helpers || !fetch || !native || !end) return 0;
char *fs = malloc(strlen(TEX_FS) + 512);
if (!fs) return 0;
snprintf(fs, strlen(TEX_FS) + 512,
"#version 330\nuniform usampler2D u_vram, u_palette;\n"
"uniform ivec2 v_tpage, v_clut; uniform int v_depth;\n"
"layout(location=0) out uint decoded_word;\n%.*s"
"int decode_native(int u,int v){\n%.*s"
"void main(){decoded_word=uint(decode_native(int(gl_FragCoord.x),int(gl_FragCoord.y)));}\n",
(int)(fetch-helpers), helpers, (int)(end-native), native);
s_tc_prog = build_program(PRESENT_VS, fs);
free(fs);
if (!s_tc_prog) return 0;
glGenTextures(1, &s_tc_array);
p_glActiveTexture(PSXGL_TEXTURE0 + 3);
glBindTexture(GL_TEXTURE_2D_ARRAY, s_tc_array);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
p_glBindBuffer(PSXGL_PIXEL_UNPACK_BUFFER, 0);
image(GL_TEXTURE_2D_ARRAY, 0, PSXGL_R16UI, 256, 256, s_tc_capacity, 0,
PSXGL_RED_INTEGER, GL_UNSIGNED_SHORT, NULL);
p_glActiveTexture(PSXGL_TEXTURE0);
if (glGetError() != GL_NO_ERROR) return 0;
p_glGenFramebuffers(1, &s_tc_fbo);
s_tc_page = p_glGetUniformLocation(s_tc_prog, "v_tpage");
s_tc_clut = p_glGetUniformLocation(s_tc_prog, "v_clut");
s_tc_depth = p_glGetUniformLocation(s_tc_prog, "v_depth");
p_glUseProgram(s_tc_prog);
p_glUniform1i(p_glGetUniformLocation(s_tc_prog, "u_vram"), 0);
p_glUniform1i(p_glGetUniformLocation(s_tc_prog, "u_palette"), 1);
s_tc_ready = 1;
return 1;
}
static void texel_cache_bind(void) {
p_glActiveTexture(PSXGL_TEXTURE0 + 3);
glBindTexture(GL_TEXTURE_2D_ARRAY, s_tc_array);
p_glActiveTexture(PSXGL_TEXTURE0);
}
static int texel_cache_get(GLuint source, GLuint palette, int x, int y, int depth, int cx, int cy) {
if (depth >= 2 || !texel_cache_init()) return -1;
uint64_t stamp = source == s_raw_tex ? texel_cache_region(x,y,depth ? 128 : 64,256) : 0;
uint64_t ps = palette == s_raw_tex ? texel_cache_region(cx,cy,depth ? 256 : 16,1) : 0;
if (ps > stamp) stamp = ps;
int slot = -1;
uint64_t oldest = UINT64_MAX;
for (int i=0; i<s_tc_capacity; ++i) {
TexelCacheEntry *c = &s_tc[i];
if (c->valid && c->source==source && c->palette==palette && c->x==x && c->y==y &&
c->depth==depth && c->cx==cx && c->cy==cy) {
if (c->stamp == stamp) { c->used=++s_tc_clock; texel_cache_pin(i); s_tc_hits++; return i; }
}
if (!c->valid) { if (oldest != 0) { slot=i; oldest=0; } }
else if (!c->pinned && c->used < oldest) { oldest=c->used; slot=i; }
}
s_tc_misses++;
if (slot < 0) return -1; /* All layers pinned: preserve the original batch. */
if (s_tc[slot].valid) {
/* Only layers without deferred CPU draw references can be reused.
* GPU readers were already submitted, so command ordering protects
* them. Never split a mask-checked batch to make cache space. */
s_tc_evictions++;
}
p_glBindFramebuffer(PSXGL_FRAMEBUFFER, s_tc_fbo);
s_tc_attach(PSXGL_FRAMEBUFFER, PSXGL_COLOR_ATTACHMENT0, s_tc_array, 0, slot);
if (p_glCheckFramebufferStatus(PSXGL_FRAMEBUFFER) != PSXGL_FRAMEBUFFER_COMPLETE) return -1;
glViewport(0,0,256,256);
glDisable(GL_SCISSOR_TEST); glDisable(GL_BLEND); glDisable(GL_DEPTH_TEST); glDisable(GL_STENCIL_TEST);
glColorMask(GL_TRUE,GL_TRUE,GL_TRUE,GL_TRUE);
p_glUseProgram(s_tc_prog);
p_glUniform2i(s_tc_page,x,y); p_glUniform2i(s_tc_clut,cx,cy); p_glUniform1i(s_tc_depth,depth);
p_glActiveTexture(PSXGL_TEXTURE0); glBindTexture(GL_TEXTURE_2D,source);
p_glActiveTexture(PSXGL_TEXTURE0+1); glBindTexture(GL_TEXTURE_2D,palette);
p_glActiveTexture(PSXGL_TEXTURE0+3); glBindTexture(GL_TEXTURE_2D_ARRAY,0);
p_glBindVertexArray(s_empty_vao); glDrawArrays(GL_TRIANGLES,0,3);
p_glBindFramebuffer(PSXGL_FRAMEBUFFER,s_hr_fbo);
p_glActiveTexture(PSXGL_TEXTURE0);
s_tc[slot]=(TexelCacheEntry){.source=source,.palette=palette,.x=x,.y=y,.depth=depth,
.cx=cx,.cy=cy,.valid=1,.stamp=stamp,.used=++s_tc_clock};
texel_cache_pin(slot);
return slot;
}
+55 -6
View File
@@ -120,14 +120,18 @@ static uint64_t s_fg_flips; /* frame generation (below): game frames seen */
static GlRenderWorkStats s_cpu_work;
static int s_cpu_perf_mode = -1;
static int gl_cpu_probe_gpu(int kind) {
return s_cpu_perf_mode == 2 && kind != 0 && kind != 4;
return s_cpu_perf_mode == 2 && kind > 0 && kind < 6 && kind != 4;
}
static uint64_t gl_cpu_begin(int kind) {
static int gl_cpu_mode(void) {
if (s_cpu_perf_mode < 0) {
const char *e = getenv("PSX_GL_CPU_PERF");
s_cpu_perf_mode = e && (e[0] == '1' || e[0] == '2') ? e[0] - '0' : 0;
s_cpu_perf_mode = e && e[0] >= '1' && e[0] <= '3' ? e[0] - '0' : 0;
}
if (!s_cpu_perf_mode) return 0;
return s_cpu_perf_mode;
}
static uint64_t gl_cpu_begin(int kind) {
if (!gl_cpu_mode()) return 0;
if (gl_cpu_probe_gpu(kind)) {
uint64_t start = SDL_GetPerformanceCounter();
glFinish();
@@ -797,6 +801,10 @@ static GLuint s_hr_tex = 0, s_hr_fbo = 0, s_hr_rb = 0;
static int s_hr_alloc = 0;
/* Native raw-1555 sampling mirror + readback source. */
static GLuint s_raw_tex = 0, s_raw_fbo = 0;
static void texel_cache_dirty(int x0, int y0, int x1, int y1);
static void texel_cache_reset(void);
static void texel_cache_bind(void);
static void texel_cache_unpin(void);
/* CPU->VRAM upload staging (native RGBA8). */
static GLuint s_up_tex = 0;
/* copy_rect staging (hr-sized RGBA8). */
@@ -1421,6 +1429,8 @@ static void present_target_quad(GLuint tex, float tex_w, float tex_h,
int apply_gamma, int src_scale);
static void coh_record(int kind, int x0, int y0, int x1, int y1) {
if (kind == GL_COH_PACK || kind == GL_COH_FLUSH || kind == GL_COH_UPLOAD)
texel_cache_dirty(x0,y0,x1,y1);
GlCohEvent *e = &s_coh_ring[s_coh_seq % GL_COH_RING_CAP];
e->frame = (uint32_t)s_frame_count;
e->kind = (uint8_t)kind;
@@ -2004,6 +2014,7 @@ static const char *TEX_FS =
"uniform sampler2D u_hd_texture;\n"
"uniform usampler2D u_vram;\n"
"uniform usampler2D u_palette;\n"
"uniform usampler2DArray u_decoded;\n"
"uniform int u_semipass; /* 0=all texels, 1=STP=0 only, 2=STP=1 only */\n"
"uniform int u_semimode; /* PS1 blend mode; drives dual-source factors */\n"
"uniform int u_maskset; /* GP0(E6h) set-mask: OR bit15 into output */\n"
@@ -2031,6 +2042,7 @@ static const char *TEX_FS =
" u = clamp(u, v_limits.x, v_limits.z);\n"
" v = clamp(v, v_limits.y, v_limits.w);\n"
" }\n"
" if (v_depth >= 4) return int(texelFetch(u_decoded,ivec3(u,v,v_depth-4),0).r);\n"
" if (v_depth == 0) {\n"
" int px = vram_at(v_tpage.x + (u >> 2), v_tpage.y + v);\n"
" return palette_at(v_clut.x + ((px >> ((u & 3) * 4)) & 0xF), v_clut.y);\n"
@@ -2457,6 +2469,8 @@ static void hr_end(void) {
* calls depth_restore after its draw (tex: textured program bound). */
static GLint s_geo_uZbias = -1, s_tex_uZbias = -1;
static GLint s_tex_uBcls = -1, s_geo_uBcls = -1, s_geo_uBadd = -1;
static uint64_t s_depth_probe_ordinal;
static int s_depth_probe_current, s_hq_replay_active;
static uint64_t s_coverage_depth_draws;
static int coverage_fast_enabled(void) {
static int enabled = -1;
@@ -2534,7 +2548,11 @@ static void depth_restore_ex_timed_impl(int mode, int tex, int n, int semi, int
static void depth_restore_ex(int mode, int tex, int n, int semi, int mask, int first) {
uint64_t start = gl_cpu_begin(0);
uint64_t sample = 0;
if (s_depth_probe_current && tex && mode == 1) { glFinish(); sample = SDL_GetPerformanceCounter(); }
depth_restore_ex_timed_impl(mode, tex, n, semi, mask, first);
if (sample) { glFinish(); gl_cpu_end(6, sample); }
s_depth_probe_current = 0;
gl_cpu_end(0, start);
}
@@ -3688,7 +3706,9 @@ static void bind_textured_resources(GLuint source, GLuint palette, GLuint hd) {
p_glActiveTexture(PSXGL_TEXTURE0 + 2);
glBindTexture(GL_TEXTURE_2D, hd ? hd : s_up_tex); p_glUniform1i(s_uHdTexture, 2);
p_glActiveTexture(PSXGL_TEXTURE0);
texel_cache_bind();
}
#include "gl_texel_cache.inc"
static int s_tb_twin[4] = {0, 0, 0, 0};
static uint64_t s_batch_total = 0, s_batch_reason[8];
@@ -3838,7 +3858,7 @@ static int bcls_fuse_begin(int semi, int dmode, int mask, int check, int b3d, in
/* tb_mask / check are the batch's set-mask and the mask-check state it was
* drawn under; track_stencil lets the canonical pass record deferred stencil
* (a replay into the high-resolution window must not touch that flag). */
static void tex_draw_passes_ex(int nverts, int semi, int tb_mask, int check,
static void tex_draw_passes_impl(int nverts, int semi, int tb_mask, int check,
int track_stencil, int b3d, int dmode, int first) {
int fused = 0;
p_glUniform1i(s_uSemimode, semi < 0 ? 0 : semi);
@@ -3913,6 +3933,16 @@ static void tex_draw_passes_ex(int nverts, int semi, int tb_mask, int check,
}
}
}
static void tex_draw_passes_ex(int nverts, int semi, int tb_mask, int check,
int track_stencil, int b3d, int dmode, int first) {
s_depth_probe_current = gl_cpu_mode() == 3 && dmode == 1 &&
(s_hr_scale > 1 || (s_hiw && s_hq_replay_active)) && (++s_depth_probe_ordinal % 128u) == 0;
uint64_t sample = 0;
if (s_depth_probe_current) { glFinish(); sample = SDL_GetPerformanceCounter(); }
tex_draw_passes_impl(nverts, semi, tb_mask, check, track_stencil, b3d, dmode, first);
if (sample) { glFinish(); gl_cpu_end(7, sample); }
}
static void tex_batch_draw_passes(int nverts, int semi) {
tex_draw_passes_ex(nverts, semi, s_tb_mask, s_mask_check, 1, s_tb_b3d, s_tb_depth, 0);
}
@@ -4323,7 +4353,11 @@ static void hiw_flush_queue_timed_impl(void) {
static void hiw_flush_queue(void) {
uint64_t start = gl_cpu_begin(1);
int previous = s_hq_replay_active;
s_hq_replay_active = 1;
hiw_flush_queue_timed_impl();
s_hq_replay_active = previous;
if (!s_tb_n && !s_hq_n) texel_cache_unpin();
gl_cpu_end(1, start);
}
@@ -4403,6 +4437,7 @@ static void flush_tex_batch_impl(void) {
}
hr_end();
if (--s_cw_flush_depth == 0) s_cw_flush_ms += cw_ms() - cw_t0;
if (!s_tb_n && !s_hq_n) texel_cache_unpin();
}
/* Flat / gouraud GEO batch — MotK title/char-select starfields issue ~30k/s
@@ -5112,6 +5147,13 @@ static void gpu_textured_triangle(const int *xs, const int *ys,
flush_tex_batch();
}
if (s_tb_n + 3 > TEXBATCH_MAXV) { s_batch_reason[6]++; flush_tex_batch(); }
const int cache_bounds = (twx | twy) ||
(lim[0] >= 0 && lim[1] >= 0 && lim[2] <= 255 && lim[3] <= 255 &&
lim[0] <= lim[2] && lim[1] <= lim[3]);
int decoded = hd_tex || !cache_bounds ? -1 : texel_cache_get(
s_selected_bank_tex ? s_selected_bank_tex : s_raw_tex,
s_selected_bank_tex && !s_selected_bank_live_clut ? s_selected_bank_tex : s_raw_tex,
base_x,base_y,depth,clut_x,clut_y);
if (s_tb_n == 0) { /* opening a batch: capture its keyed state */
s_tb_semi = batch_semi; s_tb_mask = s_mask_set; s_tb_filter = filter; s_tb_gate = gate;
s_tb_depth = tdmode;
@@ -5132,7 +5174,8 @@ static void gpu_textured_triangle(const int *xs, const int *ys,
vp[4] = col[i*3+0]; vp[5] = col[i*3+1]; vp[6] = col[i*3+2]; vp[7] = 1.0f;
vp[8] = (float)base_x; vp[9] = (float)base_y; /* a_tpage */
vp[10] = (float)clut_x; vp[11] = (float)clut_y; /* a_clut */
vp[12] = (float)depth; vp[13] = (float)rawtex; /* a_depth, a_raw */
vp[12] = (float)(decoded < 0 ? depth : decoded + 4);
vp[13] = (float)rawtex; /* a_depth, a_raw */
vp[14] = (float)lim[0]; vp[15] = (float)lim[1]; /* a_limits */
vp[16] = (float)lim[2]; vp[17] = (float)lim[3];
vp[18] = (semi >= 0 ? (float)(semi + 1) : 0.0f) /* a_semi code */
@@ -5866,6 +5909,7 @@ static void depth24_clear_skipped_fb(void) {
glViewport(0, 0, VRAM_W, VRAM_H);
glScissor(x0, y0, rw, rh);
glClear(GL_COLOR_BUFFER_BIT);
texel_cache_dirty(x0,y0,x1,y1);
}
glDisable(GL_SCISSOR_TEST);
p_glBindFramebuffer(PSXGL_FRAMEBUFFER, 0);
@@ -6362,6 +6406,7 @@ static int init_gpu_raster(void) {
s_tex_uBcls = p_glGetUniformLocation(s_tex_prog, "u_bcls");
p_glUseProgram(s_tex_prog);
p_glUniform1i(p_glGetUniformLocation(s_tex_prog, "u_coverage_fast"), coverage_fast_enabled());
p_glUniform1i(p_glGetUniformLocation(s_tex_prog, "u_decoded"), 3);
s_geo_uBcls = p_glGetUniformLocation(s_geo_prog, "u_bcls");
s_geo_uBadd = p_glGetUniformLocation(s_geo_prog, "u_badd");
s_uBlitSrc = p_glGetUniformLocation(s_blit_prog, "u_src");
@@ -6683,6 +6728,7 @@ int gl_renderer_select_texture_bank_live_clut(uint16_t id) {
int gl_renderer_init_context(SDL_Window *win) {
s_win = win;
texel_cache_reset();
s_window_owner = window_thread_id();
gl_drawable_refresh();
s_present_w = 0;
@@ -6814,6 +6860,7 @@ void gl_renderer_work_stats(GlRenderWorkStats *out) {
out->counts[0] = s_hq_uploads; out->counts[1] = s_hq_saved_uploads;
out->counts[2] = s_bcls_fused_draws; out->counts[3] = s_coverage_depth_draws;
out->counts[4] = s_hq_center_pruned;
out->counts[5] = s_tc_hits; out->counts[6] = s_tc_misses; out->counts[7] = s_tc_evictions;
}
void gl_renderer_shutdown(void) {
gl_renderer_render_thread_stop(); /* drains; the context comes back to this thread */
@@ -8813,6 +8860,7 @@ static void pass_journal_rollback(void) {
pass_blit(j->hr_fbo, s_hr_fbo, 0, 0, x * S, y * S, w * S, h * S,
GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
pass_blit(j->raw_fbo, s_raw_fbo, 0, 0, x, y, w, h, GL_COLOR_BUFFER_BIT);
texel_cache_dirty(x,y,x+w-1,y+h-1);
}
render_pass_journal_rollback(&s_pj_cpu, s_vram, VRAM_W);
}
@@ -8989,6 +9037,7 @@ static void transaction_restore_ex(int keep_color) {
s_pass_w * S, s_pass_h * S, hr_bits);
pass_blit(s_pb_raw_fbo, s_raw_fbo, 0, 0, s_pass_x, s_pass_y, s_pass_w,
s_pass_h, GL_COLOR_BUFFER_BIT);
texel_cache_dirty(s_pass_x,s_pass_y,s_pass_x+s_pass_w-1,s_pass_y+s_pass_h-1);
if (s_pb_wide_src && s_pb_wide_src == pass_wide_fbo_for(s_pass_x)) {
pass_blit(s_pb_wide_fbo, s_pb_wide_src, 0, 0, 0, s_pass_y * S,
g_wide_w * S, s_pass_h * S, hr_bits);
+5 -5
View File
@@ -5081,13 +5081,13 @@ static void input_replay_frontend_tick(uint64_t frame) {
f = std::fopen(path.c_str(), "w");
if (!f) throw std::runtime_error("cannot write input replay render report");
std::fputs("stat,count,total_us,max_us\n", f);
const char *counts[5] = {"queue_uploads", "replay_draws", "bloom_fused", "coverage_depth", "centre_pruned"};
for (int i=0; i<5; ++i) std::fprintf(f, "%s,%llu,0,0\n", counts[i], (unsigned long long)stats.counts[i]);
const char *spans[6] = {"depth_restore", "queue_replay", "vram_copy", "interp_capture", "interp_interval", "centre_copy"};
const char *counts[8] = {"queue_uploads", "replay_draws", "bloom_fused", "coverage_depth", "centre_pruned", "texel_cache_hits", "texel_cache_misses", "texel_cache_evictions"};
for (int i=0; i<8; ++i) std::fprintf(f, "%s,%llu,0,0\n", counts[i], (unsigned long long)stats.counts[i]);
const char *spans[8] = {"depth_restore", "queue_replay", "vram_copy", "interp_capture", "interp_interval", "centre_copy", "depth_gpu_sample", "colour_gpu_sample"};
double us = stats.frequency ? 1e6 / stats.frequency : 0;
for (int i=0; i<6; ++i) std::fprintf(f, "%s,%llu,%.3f,%.3f\n", spans[i],
for (int i=0; i<8; ++i) std::fprintf(f, "%s,%llu,%.3f,%.3f\n", spans[i],
(unsigned long long)stats.calls[i], stats.ticks[i]*us, stats.max_ticks[i]*us);
for (int i=0; i<6; ++i) std::fprintf(f, "%s_prior_drain,%llu,%.3f,%.3f\n", spans[i],
for (int i=0; i<8; ++i) std::fprintf(f, "%s_prior_drain,%llu,%.3f,%.3f\n", spans[i],
(unsigned long long)stats.calls[i], stats.prior_ticks[i]*us, stats.prior_max_ticks[i]*us);
if (std::fclose(f) != 0) throw std::runtime_error("cannot finish input replay render report");
}
+4 -2
View File
@@ -157,13 +157,15 @@ def main():
ok = False
runs[threaded] = p
if args.wide_present:
for margin, coverage, state, upload, bloom in ((0, 0, 0, 0, 0), (0, 0, 1, 1, 1), (0, 1, 1, 1, 1), (1, 0, 1, 1, 1), (1, 1, 1, 0, 1), (1, 1, 1, 1, 0)):
for margin, coverage, state, upload, bloom, cache, layers in ((0,0,0,0,0,0,256), (0,0,1,1,1,1,256), (0,1,1,1,1,1,256), (1,0,1,1,1,1,256), (1,1,1,0,1,1,256), (1,1,1,1,0,1,256), (1,1,1,1,1,0,256), (1,1,1,1,1,1,4)):
reference = run([probe, s, threaded, args.frames, *mode],
{"PSX_GL_WIDE_MARGIN_SCISSOR": str(margin),
"PSX_GL_COVERAGE_FAST": str(coverage),
"PSX_GL_STATE_QUERY_FAST": str(state),
"PSX_GL_QUEUE_UPLOAD_FAST": str(upload),
"PSX_GL_BLOOM_FUSE_EXTEND": str(bloom)})
"PSX_GL_BLOOM_FUSE_EXTEND": str(bloom),
"PSX_GL_TEXEL_CACHE": str(cache),
"PSX_GL_TEXEL_CACHE_LAYERS": str(layers)})
baseline = parse(reference.stdout)
if reference.returncode or baseline["failures"] != 0 or any(baseline[k] != p[k] for k in KEYS):
print(f"FAIL scale {s}: margin={margin} coverage={coverage} changed rendered output", baseline, p)
+7
View File
@@ -468,11 +468,18 @@ int main(int argc, char **argv) {
(unsigned long long)s_hq_saved_uploads);
}
if (quality_scene && coverage_fast_enabled()) {
check(s_bcls_fused_draws > 0, "bloom stencil fused with colour draws");
printf("fused_draws=%llu\n", (unsigned long long)s_bcls_fused_draws);
check(s_coverage_depth_draws > 0, "coverage-only depth passes ran");
printf("coverage_draws=%llu\n", (unsigned long long)s_coverage_depth_draws);
}
if (!getenv("PSX_GL_TEXEL_CACHE") || getenv("PSX_GL_TEXEL_CACHE")[0] != '0') {
check(s_tc_ready == 1 && s_tc_hits > 0 && s_tc_misses > 0, "decoded indexed texels reused");
printf("texel_cache_hits=%llu misses=%llu evictions=%llu\n",
(unsigned long long)s_tc_hits, (unsigned long long)s_tc_misses,
(unsigned long long)s_tc_evictions);
}
if (threaded) {
RtStats st;
rt_get_stats(&st);