Cache display geometry within each GPU execution context

This commit is contained in:
Shokunin
2026-10-21 23:31:13 -07:00
parent fe593b8277
commit 36108c9bc3
3 changed files with 115 additions and 1 deletions
+17
View File
@@ -2260,3 +2260,20 @@ common emulation/context regression work while retaining the bar, or explicitly
revise load/bar. No further code changes or pushes; all code is committed.
Receipts: R4 local brief5-worker-idle and brief5-loaded-profile-diff (including
unprofiled-prefix-proof.json). Cadence, title ctest and both pushes remain pending.
2026-10-21 BRIEF-5 scope resumed: Claude authorizes the common-cost comparison.
An exact da09fac/8130e6fd archive build with production options, loaded setting 0,
frames 3450..4350, has 60.96% sampled source CPU and unprofiled-prefix p99 67.4%
(32 windows >=60), versus current 59.39% / 65.9% (18). Native execution is
positive with zero interpreted calls/misses; the historical 58.7% is not
reproduced. Reuse the existing current profile rather than repeat it. The initial
archive profile used netplay OFF and a wrong stop variable; exclude it.
The paired profile shows gpu_get_display_info at 1.003% current wall samples
versus zero separate old leaf samples. Cache the derived display geometry per
execution context/reader until GP1, reset or snapshot restore changes registers;
private invalidation does not touch live readers. The production GPU fixture
checks all resolution modes, repeated queries, private/source separation,
recapture, checkpoint and savestate restore, reset and power-on PAL. It passes.
Continue failing loaded gate, cadence, title ctest and both pushes; quiet remains
passed. No acceptance or current blocker. Receipts: R4 local
brief5-stage2-regression/valid-* (first run explicitly INVALID).
+38 -1
View File
@@ -50,6 +50,7 @@
#include "ws_screen_mask.h"
#include "ws_radial_screen_mask.h"
#include <stdint.h>
#include <stdatomic.h>
#include <math.h>
#include <limits.h>
#include <stdio.h>
@@ -69,14 +70,30 @@ extern uint32_t psx_read_word(uint32_t addr);
/* ---- VRAM ---- */
/* Default execution and render-thread queries keep the live objects. Only
* a bound replay thread selects private regions; no live state is swapped. */
typedef struct GpuDisplayCache {
uint64_t epoch;
GpuDisplayInfo info;
} GpuDisplayCache;
struct GpuExecutionSnapshot {
uint64_t frame;
int recent_mdec;
void *slots[323];
void *storage;
int ready;
uint64_t display_epoch;
GpuDisplayCache display_cache;
};
static PSX_THREAD_LOCAL GpuExecutionSnapshot *s_gpu_execution;
/* Display registers change at GP1/state boundaries, not per primitive. Keep
* the derived view local to its reader; worker invalidation never touches the
* source epoch. The epoch also invalidates other live readers' local caches. */
static _Atomic uint64_t s_display_epoch = 1;
static PSX_THREAD_LOCAL GpuDisplayCache t_display_cache;
static void gpu_display_state_changed(void) {
if (s_gpu_execution) ++s_gpu_execution->display_epoch;
else atomic_fetch_add_explicit(&s_display_epoch, 1, memory_order_release);
}
static uint16_t vram[1024 * 512];
typedef uint16_t GpuContextType_vram[524288];
static inline GpuContextType_vram *gpu_context_vram(void) {
@@ -5265,6 +5282,7 @@ static inline GpuContextType_g_pollhack_vblank_count *gpu_context_g_pollhack_vbl
/* ---- Initialization ---- */
static void gpu_reset_state(int clear_vram) {
gpu_display_state_changed();
ws_reset_scene_history();
if (clear_vram) {
memset((*gpu_context_vram()), 0, sizeof((*gpu_context_vram())));
@@ -5365,12 +5383,15 @@ void gpu_init(void) {
gpu_reset_state(1);
(*gpu_context_video_mode()) = (*gpu_context_s_power_on_video_mode());
(void)psx_video_timing_set_pal((int)(*gpu_context_video_mode()));
gpu_display_state_changed();
}
void gpu_set_power_on_video_mode(int pal) {
gpu_display_state_changed();
(*gpu_context_s_power_on_video_mode()) = pal ? 1u : 0u;
(*gpu_context_video_mode()) = (*gpu_context_s_power_on_video_mode());
(void)psx_video_timing_set_pal((int)(*gpu_context_video_mode()));
gpu_display_state_changed();
}
/* ---- GPUSTAT read (0x1F801814) ---- */
@@ -5932,7 +5953,7 @@ int gpu_display_is_depth24(void) {
return (int)((*gpu_context_display_depth()) & 1u);
}
void gpu_get_display_info(GpuDisplayInfo* out) {
static void gpu_build_display_info(GpuDisplayInfo* out) {
out->display_x = (*gpu_context_display_area_x());
out->display_y = (*gpu_context_display_area_y());
out->depth24 = (int)((*gpu_context_display_depth()) & 1u);
@@ -6012,6 +6033,18 @@ void gpu_get_display_info(GpuDisplayInfo* out) {
if ((*gpu_context_vres())) out->screen_offset_y *= 2;
}
void gpu_get_display_info(GpuDisplayInfo* out) {
GpuExecutionSnapshot *execution = s_gpu_execution;
uint64_t epoch = execution ? execution->display_epoch :
atomic_load_explicit(&s_display_epoch, memory_order_acquire);
GpuDisplayCache *cache = execution ? &execution->display_cache : &t_display_cache;
if (cache->epoch != epoch) {
gpu_build_display_info(&cache->info);
cache->epoch = epoch;
}
*out = cache->info;
}
/* Debug accessors for GP1 display-range / mode (TCP gpu_state). */
void gpu_get_crtc_debug(uint32_t *x1, uint32_t *x2, uint32_t *y1, uint32_t *y2,
uint32_t *hres1_out, uint32_t *hres2_out) {
@@ -9421,6 +9454,7 @@ static inline GpuContextType_gpu_local_gpu_write_gp1_reason *gpu_context_gpu_loc
return s_gpu_execution ? (GpuContextType_gpu_local_gpu_write_gp1_reason *)s_gpu_execution->slots[322] : &gpu_local_gpu_write_gp1_reason;
}
void gpu_write_gp1(uint32_t val) {
gpu_display_state_changed();
uint32_t cmd = (val >> 24) & 0x3F;
switch (cmd) {
@@ -9445,6 +9479,7 @@ void gpu_write_gp1(uint32_t val) {
psx_fatal_halt((*gpu_context_gpu_local_gpu_write_gp1_reason()));
}
}
gpu_display_state_changed();
}
/* ---- boot snapshot: complete GPU register state (see boot_state.h) ---- */
@@ -9526,6 +9561,7 @@ static int gpu_snap_emit(PstW *w) {
return 1;
}
static int gpu_snap_parse(PstR *r) {
gpu_display_state_changed();
uint32_t u; int32_t i; uint16_t h;
#define RU(f) do { if (!pst_r_u32(r, &u)) return 0; (f) = u; } while (0)
#define RI(f) do { if (!pst_r_i32(r, &i)) return 0; (f) = i; } while (0)
@@ -10225,6 +10261,7 @@ int gpu_execution_snapshot_capture(GpuExecutionSnapshot *snapshot) {
ColorLut **lut = (ColorLut **)gpu_execution_region(snapshot, &s_screen_lut);
if (*lut) color_lut_destroy(*lut);
snapshot->ready = 0;
++snapshot->display_epoch;
snapshot->frame = s_frame_count;
snapshot->recent_mdec = mdec_recently_active(WS_FMV_HYSTERESIS);
for (size_t i = 0; i < sizeof s_gpu_execution_regions / sizeof s_gpu_execution_regions[0]; ++i) {
+60
View File
@@ -33,5 +33,65 @@ int main(void) {
gpu_pass_checkpoint_restore();
assert(gpu_execution_snapshot_capture(image));
gpu_execution_snapshot_destroy(image);
/* Derived display caches follow register writes, state restore and private
* snapshot ownership. Compare each cached result with the uncached view. */
gpu_init();
GpuDisplayInfo actual, display_expected, source;
const uint32_t modes[] = {0, 1, 2, 3, 0x40, 0x0C, 0x18};
for (unsigned i = 0; i < sizeof modes / sizeof modes[0]; ++i) {
gpu_write_gp1(0x08000000u | modes[i]);
gpu_write_gp1(0x0500180Cu);
gpu_write_gp1(0x060C0200u);
gpu_write_gp1(0x07040010u);
gpu_build_display_info(&display_expected);
gpu_get_display_info(&actual);
assert(!memcmp(&actual, &display_expected, sizeof actual));
gpu_get_display_info(&actual);
assert(!memcmp(&actual, &display_expected, sizeof actual));
}
source = actual;
image = gpu_execution_snapshot_create();
assert(image && gpu_execution_snapshot_capture(image));
assert(gpu_execution_snapshot_bind(image));
gpu_get_display_info(&actual);
assert(!memcmp(&actual, &source, sizeof actual));
gpu_write_gp1(0x08000003u);
gpu_write_gp1(0x05000029u);
gpu_build_display_info(&display_expected);
gpu_get_display_info(&actual);
assert(!memcmp(&actual, &display_expected, sizeof actual));
assert(actual.display_x == 41);
assert(gpu_pass_checkpoint_save());
gpu_write_gp1(0);
gpu_get_display_info(&actual);
assert(actual.display_x == 0);
gpu_pass_checkpoint_restore();
gpu_get_display_info(&actual);
assert(!memcmp(&actual, &display_expected, sizeof actual));
gpu_execution_snapshot_unbind();
gpu_get_display_info(&actual);
assert(!memcmp(&actual, &source, sizeof actual));
gpu_write_gp1(0x08000001u);
assert(gpu_execution_snapshot_capture(image));
gpu_get_display_info(&source);
assert(gpu_execution_snapshot_bind(image));
gpu_get_display_info(&actual);
assert(!memcmp(&actual, &source, sizeof actual));
gpu_execution_snapshot_unbind();
gpu_execution_snapshot_destroy(image);
uint32_t bytes = gpu_snapshot_bytes();
uint8_t *saved = malloc(bytes);
assert(saved);
gpu_snapshot_write(saved);
gpu_write_gp1(0);
gpu_get_display_info(&actual);
assert(gpu_snapshot_read(saved, bytes));
gpu_get_display_info(&actual);
assert(!memcmp(&actual, &source, sizeof actual));
free(saved);
gpu_set_power_on_video_mode(1);
gpu_build_display_info(&display_expected);
gpu_get_display_info(&actual);
assert(!memcmp(&actual, &display_expected, sizeof actual));
return 0;
}