mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-26 02:11:36 -04:00
Publish GPU inputs without derived scratch and narrow PGXP copy pages
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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
@@ -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. */
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user