Publish GPU inputs without derived scratch and narrow PGXP copy pages

This commit is contained in:
Shokunin
2026-10-21 22:16:14 -07:00
parent 2ef58c5471
commit c30fc917c1
7 changed files with 102 additions and 8 deletions
+12
View File
@@ -2213,3 +2213,15 @@ breadcrumb pointer. Make host callback tables thread-owned and bind each DLL
on its executing source thread, as the worker already does. Production-loader
cross-thread lifetime checks pass O0/O2. Refresh and ordered gates continue;
no push. Invalid cache/profile attempts are retained separately in R4 receipts.
2026-10-21 BRIEF-5 critical-window profile: callback fix retains original-off
identity; quiet 60.000 Hz / zero hitches / 64.0% p99 remains failed. The paired
3000..4200 source profiles have identical native calls, zero misses, mean CPU
53.63% off / 52.05% on. Publication alone adds 2.375% wall samples: RAM streaming
copy, PGXP pages and GPU regions. Remove the GPU upload/checkpoint scratch
copies (2.38 MiB; both are fully populated before use); refuse capture of an
active checkpoint. Narrow PGXP publication pages from 256 to 64 words to avoid
10 KiB copies per isolated store. Partial-transfer/private-checkpoint, provenance,
page-boundary/reuse and O0/O2 upload history tests pass. Correct the shared GPU
fixture TLS declaration found by that check. No general hot-path changes. Continue
ordered production qualification; no push.
+17
View File
@@ -1113,6 +1113,23 @@ if(BUILD_TESTING)
endif()
add_test(NAME depth24_present_hold_exec_test COMMAND depth24_present_hold_exec_test)
add_executable(gpu_snapshot_scratch_test
tests/test_gpu_snapshot_scratch.c
src/psx_video_timing.c
src/gpu_timeline.c)
target_include_directories(gpu_snapshot_scratch_test PRIVATE include src)
if(MINGW)
target_compile_options(gpu_snapshot_scratch_test PRIVATE -flto)
target_link_options(gpu_snapshot_scratch_test PRIVATE -flto)
endif()
if(CMAKE_C_COMPILER_ID MATCHES "GNU|Clang")
target_compile_options(gpu_snapshot_scratch_test PRIVATE
-ffunction-sections -fdata-sections)
target_link_options(gpu_snapshot_scratch_test PRIVATE
$<IF:$<PLATFORM_ID:Darwin>,-Wl$<COMMA>-dead_strip,-Wl$<COMMA>--gc-sections>)
endif()
add_test(NAME gpu_snapshot_scratch_test COMMAND gpu_snapshot_scratch_test)
add_executable(ws_view_anchor_test tests/test_ws_view_anchor.c)
add_executable(ws_view_anchor_gpu_test tests/test_ws_view_anchor_gpu.c
src/psx_video_timing.c
+12 -3
View File
@@ -10221,14 +10221,23 @@ void gpu_execution_snapshot_destroy(GpuExecutionSnapshot *snapshot) {
free(snapshot);
}
int gpu_execution_snapshot_capture(GpuExecutionSnapshot *snapshot) {
if (!snapshot || s_gpu_execution) return 0;
if (!snapshot || s_gpu_execution || s_pass_regs_len) return 0;
ColorLut **lut = (ColorLut **)gpu_execution_region(snapshot, &s_screen_lut);
if (*lut) color_lut_destroy(*lut);
snapshot->ready = 0;
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)
memcpy(snapshot->slots[i], s_gpu_execution_regions[i].live, s_gpu_execution_regions[i].bytes);
for (size_t i = 0; i < sizeof s_gpu_execution_regions / sizeof s_gpu_execution_regions[0]; ++i) {
const void *live = s_gpu_execution_regions[i].live;
/* Derived working storage belongs to the consumer: CPU upload commit
* fills every used pixel from VRAM, and checkpoint save fills the
* provenance image before restore can read it. Do not publish either
* multi-megabyte scratch buffer from the source owner. */
if (live == &vram_write_pixels || live == &s_pass_presentation || live == &s_pass_regs)
continue;
memcpy(snapshot->slots[i], live, s_gpu_execution_regions[i].bytes);
}
*(uint32_t *)gpu_execution_region(snapshot, &s_pass_regs_len) = 0;
/* Host allocations are not machine registers. Build a private colour LUT
* lazily if a replay requests scanout, and omit the live diagnostic census.
* The scene predicate is immutable code and executes against bound state. */
+2 -1
View File
@@ -96,7 +96,8 @@ struct PGXPValue {
#define PGXP_REG_LO 33
static PSX_THREAD_LOCAL PGXPValue *s_ram = nullptr; /* lazily allocated: RAM + mod regions */
enum { PGXP_EXEC_PAGE_WORDS = 256,
/* Fine publication pages avoid copying 10 KiB for one changed shadow word. */
enum { PGXP_EXEC_PAGE_WORDS = 64,
PGXP_EXEC_PAGES = (PGXP_SHADOW_WORDS + PGXP_EXEC_PAGE_WORDS - 1) / PGXP_EXEC_PAGE_WORDS };
/* Tracking starts only when a replay consumer requests a snapshot. Ordinary
* execution does not allocate version tables. A replay marks its modified
+1
View File
@@ -6,6 +6,7 @@
* Draw calls that tests observe are recorded below. */
uint64_t s_frame_count;
PSX_THREAD_LOCAL int g_psx_render_pass_active;
PSX_THREAD_LOCAL uint64_t psx_cycle_count; /* gpu_timeline.c stamps notes with it */
int g_exec_phase;
PSX_THREAD_LOCAL uint32_t g_debug_current_func_addr;
+37
View File
@@ -0,0 +1,37 @@
/* Derived upload/checkpoint storage is private scratch, not source input. */
#ifdef NDEBUG
#undef NDEBUG
#endif
#include <assert.h>
#include "../src/gpu.c"
#include "gpu_exec_stubs.inc"
int main(void) {
/* Publish half-way through a transfer. Completed upload reconstructs all
* staging pixels, including the prefix received by the source before it
* published. Neither old staging contents nor later source writes leak. */
gpu_write_gp0(0xA0000000u); gpu_write_gp0(0); gpu_write_gp0(0x00010004u);
gpu_write_gp0(0x22221111u);
memset(vram_write_pixels, 0xCD, sizeof vram_write_pixels);
GpuExecutionSnapshot *image = gpu_execution_snapshot_create();
assert(image && gpu_execution_snapshot_capture(image));
vram[0] = 0x5555;
assert(gpu_execution_snapshot_bind(image));
gpu_write_gp0(0x44443333u);
assert((*gpu_context_gp0_state()) == GP0_IDLE);
const uint16_t expected[] = {0x1111,0x2222,0x3333,0x4444};
assert(!memcmp(*gpu_context_vram_write_pixels(), expected, sizeof expected));
/* Checkpoint save initializes the private provenance scratch before use. */
(*gpu_context_texture_window_value()) = 17;
assert(gpu_pass_checkpoint_save());
(*gpu_context_texture_window_value()) = 29;
gpu_pass_checkpoint_restore();
assert((*gpu_context_texture_window_value()) == 17);
gpu_execution_snapshot_unbind();
assert(vram[0] == 0x5555 && gp0_state == GP0_VRAM_WRITE);
assert(gpu_pass_checkpoint_save());
assert(!gpu_execution_snapshot_capture(image));
gpu_pass_checkpoint_restore();
assert(gpu_execution_snapshot_capture(image));
gpu_execution_snapshot_destroy(image);
return 0;
}
+21 -4
View File
@@ -8,14 +8,14 @@ extern "C" int gte_geometry_correction_lookup(uint32_t, int32_t*, int32_t*) { re
extern "C" int gte_geometry_correction_lookup_probe(uint32_t, int32_t*, int32_t*) { return 0; }
extern "C" int psx_mod_gpu_dma_memory_contains(uint32_t, uint32_t) { return 0; }
static constexpr uint32_t address = 0x80001000, packed = 0x000C0008;
static void seed(int32_t x) {
static void seed(int32_t x, uint32_t at = address) {
pgxp_gte_push_sxy(x, 12 << 16, 300, packed);
pgxp_store_gte_reg(address, 15);
pgxp_store_gte_reg(at, 15);
}
static void check(int32_t x) {
static void check(int32_t x, uint32_t at = address) {
int32_t actual_x, y;
uint16_t z;
CHECK(pgxp_load_precise_word(address, packed, &actual_x, &y, &z));
CHECK(pgxp_load_precise_word(at, packed, &actual_x, &y, &z));
CHECK(actual_x == x && y == 12 << 16 && z == 300);
}
int main() {
@@ -86,6 +86,23 @@ int main() {
pgxp_execution_snapshot_unbind();
});
invalidated.join();
/* Adjacent words across a publication-page boundary remain independent,
* including worker writes to a page the source has not changed. */
seed(8 << 16, address + 252); seed(9 << 16, address + 256);
CHECK(pgxp_execution_snapshot_capture(snapshot));
std::thread boundary([&] {
CHECK(pgxp_execution_snapshot_bind(snapshot));
check(8 << 16, address + 252); check(9 << 16, address + 256);
seed(10 << 16, address + 252); seed(11 << 16, address + 256);
pgxp_execution_snapshot_unbind();
}); boundary.join();
seed(12 << 16, address + 256);
CHECK(pgxp_execution_snapshot_capture(snapshot));
std::thread recapture([&] {
CHECK(pgxp_execution_snapshot_bind(snapshot));
check(8 << 16, address + 252); check(12 << 16, address + 256);
pgxp_execution_snapshot_unbind();
}); recapture.join();
pgxp_execution_snapshot_destroy(other);
pgxp_execution_snapshot_destroy(snapshot);
return 0;