mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-26 02:11:36 -04:00
Restore GPU presentation provenance after render passes
This commit is contained in:
@@ -221,6 +221,15 @@ mod; `psx_memory.h`), scratchpad, I-cache tags, I_STAT/I_MASK, timers, DMA
|
||||
and GPU registers (without the widescreen side effects of a savestate load), the
|
||||
VRAM rect (hr colour, mask stencil, raw 16-bit mirror, native-wide band, CPU
|
||||
VRAM rows), the renderer's coherency bookkeeping, and every clock value.
|
||||
|
||||
RAM-keyed GPU presentation provenance is restored too: sprite/HUD/background
|
||||
tags, primitive roles, reveal-clear/repeat/mask proofs, and the automatic UI
|
||||
prepass. A synthetic draw can rewrite and retag the same packet addresses;
|
||||
restoring only their RAM would leave the canonical draw comparing its original
|
||||
words against a synthetic proof. Packet freshness, the full-composite latch,
|
||||
backdrop phase, and scene classification evidence return to their pre-pass
|
||||
values. Diagnostic counters and rings still include synthetic activity.
|
||||
|
||||
A watchdog abort leaves by longjmp from inside guest code, skipping the
|
||||
exits of the frames it leaves, so the host nesting those frames own is put
|
||||
back from the checkpoint too: the cycle-deferral depth, the native overlay
|
||||
|
||||
@@ -586,7 +586,12 @@ if(BUILD_TESTING)
|
||||
add_executable(native_wide_projection_test tests/test_native_wide_projection.c
|
||||
src/psx_video_timing.c
|
||||
src/gpu_timeline.c)
|
||||
foreach(_anchor_test ws_view_anchor ws_view_anchor_gpu native_wide_projection)
|
||||
add_executable(gpu_render_pass_provenance_test tests/test_gpu_render_pass_provenance.c
|
||||
src/ws_ui_group.c
|
||||
src/psx_video_timing.c
|
||||
src/gpu_timeline.c)
|
||||
foreach(_anchor_test ws_view_anchor ws_view_anchor_gpu native_wide_projection
|
||||
gpu_render_pass_provenance)
|
||||
target_include_directories(${_anchor_test}_test PRIVATE include src)
|
||||
if(CMAKE_C_COMPILER_ID MATCHES "GNU|Clang")
|
||||
target_compile_options(${_anchor_test}_test PRIVATE -ffunction-sections -fdata-sections)
|
||||
@@ -601,6 +606,7 @@ if(BUILD_TESTING)
|
||||
add_test(NAME ws_view_anchor_test COMMAND ws_view_anchor_test)
|
||||
add_test(NAME ws_view_anchor_gpu_test COMMAND ws_view_anchor_gpu_test)
|
||||
add_test(NAME native_wide_projection_test COMMAND native_wide_projection_test)
|
||||
add_test(NAME gpu_render_pass_provenance_test COMMAND gpu_render_pass_provenance_test)
|
||||
|
||||
add_executable(ws_repeat_rect_test tests/test_ws_repeat_rect.c)
|
||||
target_include_directories(ws_repeat_rect_test PRIVATE include)
|
||||
|
||||
+110
-3
@@ -7464,10 +7464,110 @@ int gpu_snapshot_read(const uint8_t *p, uint32_t len) {
|
||||
}
|
||||
/* ---- Render-pass GPU checkpoint (render_pass.c) ---------------------------
|
||||
* The same register set as a savestate (gpu_snap_emit), restored WITHOUT
|
||||
* gpu_snapshot_read's side effects (widescreen scene history and HUD anchor
|
||||
* tags belong to the live frame, which a pass must not reset). After the
|
||||
* restore the renderer's mirrored draw state (area, offset, texture window,
|
||||
* gpu_snapshot_read's side effects. Packet provenance belongs to the RAM it
|
||||
* describes: a synthetic builder can rewrite and retag a canonical packet,
|
||||
* so rolling its RAM back must also restore its original tags, not clear them.
|
||||
* The renderer's mirrored draw state (area, offset, texture window,
|
||||
* mask bits, native-wide target) is re-synced exactly as GP0(E2..E6) would. */
|
||||
typedef struct {
|
||||
WsTag ws_tags[WS_TAG_BUCKETS];
|
||||
WsHudAnchorTag ws_hud_anchor_tags[WS_HUD_ANCHOR_TABLE_SIZE];
|
||||
WsHudAnchorTag ws_background_tags[WS_HUD_ANCHOR_TABLE_SIZE];
|
||||
WsHudAnchorTag ws_reveal_clear_tags[WS_HUD_ANCHOR_TABLE_SIZE];
|
||||
WsHudAnchorTag ws_screen_mask_tags[WS_HUD_ANCHOR_TABLE_SIZE];
|
||||
WsRadialScreenMaskTag ws_radial_screen_mask_tags[WS_RADIAL_MASK_TAG_COUNT];
|
||||
WsRepeatRectTag ws_repeat_rect_tags[WS_REPEAT_RECT_TAG_TABLE_SIZE];
|
||||
WsPrimitiveRole ws_primitive_roles[WS_ROLE_BUCKETS];
|
||||
WsUiPrepassItem ws_ui_prepass[WS_UI_PREPASS_MAX];
|
||||
WsUiPrepassNode ws_ui_prepass_nodes[WS_UI_PREPASS_NODE_MAX];
|
||||
uint32_t ws_ui_prepass_count, ws_ui_prepass_node_count;
|
||||
uint16_t ws_ui_prepass_rank;
|
||||
int ws_auto_ui_dense, ws_shared_ui_copy;
|
||||
uint32_t ws_background_tag_frame, ws_last_tag_stamp;
|
||||
int ws_background_tags_used;
|
||||
uint32_t s_bg_phase_frame;
|
||||
int s_bg_phase_over;
|
||||
/* Synthetic GTE/GP0 draws update these signals, and native-wide target
|
||||
* selection reads them even though no guest frame has elapsed. */
|
||||
uint32_t ws_gte_frame, ws_gte_count, ws_gte_prev_verts;
|
||||
uint32_t ws_last_gte_stamp, ws_last_world3d_stamp, ws_sust_world3d_stamp;
|
||||
uint32_t ws_ovh_frame, ws_ovh_count, ws_ovh_prev;
|
||||
uint32_t ws_last_ovh_stamp, ws_sust_ovh_stamp;
|
||||
WsSceneLatch ws_scene_latch;
|
||||
WsSceneHold s_ws_scene_hold;
|
||||
uint32_t s_ws_fmv_frame_cache;
|
||||
int s_ws_fmv_cached;
|
||||
} GpuPassPresentationState;
|
||||
static GpuPassPresentationState s_pass_presentation;
|
||||
|
||||
/* One transfer list keeps save and restore symmetric. The prepass arrays are
|
||||
* read only through their counts, so copy just their populated prefixes.
|
||||
* Counters/rings remain cumulative; configuration and callbacks are unchanged. */
|
||||
static void gpu_pass_provenance_transfer(int restore) {
|
||||
#define PASS_ARRAY(name) do { \
|
||||
if (restore) memcpy(name, s_pass_presentation.name, sizeof(name)); \
|
||||
else memcpy(s_pass_presentation.name, name, sizeof(name)); \
|
||||
} while (0)
|
||||
#define PASS_VALUE(name) do { \
|
||||
if (restore) name = s_pass_presentation.name; \
|
||||
else s_pass_presentation.name = name; \
|
||||
} while (0)
|
||||
PASS_ARRAY(ws_tags);
|
||||
PASS_ARRAY(ws_hud_anchor_tags);
|
||||
PASS_ARRAY(ws_background_tags);
|
||||
PASS_ARRAY(ws_reveal_clear_tags);
|
||||
PASS_ARRAY(ws_screen_mask_tags);
|
||||
PASS_ARRAY(ws_radial_screen_mask_tags);
|
||||
PASS_ARRAY(ws_repeat_rect_tags);
|
||||
PASS_ARRAY(ws_primitive_roles);
|
||||
PASS_VALUE(ws_ui_prepass_count);
|
||||
PASS_VALUE(ws_ui_prepass_node_count);
|
||||
PASS_VALUE(ws_ui_prepass_rank);
|
||||
PASS_VALUE(ws_auto_ui_dense);
|
||||
PASS_VALUE(ws_shared_ui_copy);
|
||||
PASS_VALUE(ws_background_tag_frame);
|
||||
PASS_VALUE(ws_background_tags_used);
|
||||
PASS_VALUE(ws_last_tag_stamp);
|
||||
PASS_VALUE(s_bg_phase_frame);
|
||||
PASS_VALUE(s_bg_phase_over);
|
||||
if (restore) {
|
||||
memcpy(ws_ui_prepass, s_pass_presentation.ws_ui_prepass,
|
||||
ws_ui_prepass_count * sizeof(ws_ui_prepass[0]));
|
||||
memcpy(ws_ui_prepass_nodes, s_pass_presentation.ws_ui_prepass_nodes,
|
||||
ws_ui_prepass_node_count * sizeof(ws_ui_prepass_nodes[0]));
|
||||
} else {
|
||||
memcpy(s_pass_presentation.ws_ui_prepass, ws_ui_prepass,
|
||||
ws_ui_prepass_count * sizeof(ws_ui_prepass[0]));
|
||||
memcpy(s_pass_presentation.ws_ui_prepass_nodes, ws_ui_prepass_nodes,
|
||||
ws_ui_prepass_node_count * sizeof(ws_ui_prepass_nodes[0]));
|
||||
}
|
||||
#undef PASS_ARRAY
|
||||
#undef PASS_VALUE
|
||||
}
|
||||
|
||||
static void gpu_pass_scene_transfer(int restore) {
|
||||
#define PASS_VALUE(name) do { \
|
||||
if (restore) name = s_pass_presentation.name; \
|
||||
else s_pass_presentation.name = name; \
|
||||
} while (0)
|
||||
PASS_VALUE(ws_gte_frame);
|
||||
PASS_VALUE(ws_gte_count);
|
||||
PASS_VALUE(ws_gte_prev_verts);
|
||||
PASS_VALUE(ws_last_gte_stamp);
|
||||
PASS_VALUE(ws_last_world3d_stamp);
|
||||
PASS_VALUE(ws_sust_world3d_stamp);
|
||||
PASS_VALUE(ws_ovh_frame);
|
||||
PASS_VALUE(ws_ovh_count);
|
||||
PASS_VALUE(ws_ovh_prev);
|
||||
PASS_VALUE(ws_last_ovh_stamp);
|
||||
PASS_VALUE(ws_sust_ovh_stamp);
|
||||
PASS_VALUE(ws_scene_latch);
|
||||
PASS_VALUE(s_ws_scene_hold);
|
||||
PASS_VALUE(s_ws_fmv_frame_cache);
|
||||
PASS_VALUE(s_ws_fmv_cached);
|
||||
#undef PASS_VALUE
|
||||
}
|
||||
|
||||
static uint8_t s_pass_regs[512];
|
||||
static uint32_t s_pass_regs_len;
|
||||
static uint32_t s_pass_poll_count;
|
||||
@@ -7487,6 +7587,8 @@ int gpu_pass_checkpoint_save(void) {
|
||||
s_pass_doff_max = g_doff_max_this;
|
||||
s_pass_doff_cnt = g_doff_cnt_this;
|
||||
s_pass_split_this = split_trace_this;
|
||||
gpu_pass_provenance_transfer(0);
|
||||
gpu_pass_scene_transfer(0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -7500,12 +7602,17 @@ void gpu_pass_checkpoint_restore(void) {
|
||||
g_doff_max_this = s_pass_doff_max;
|
||||
g_doff_cnt_this = s_pass_doff_cnt;
|
||||
split_trace_this = s_pass_split_this;
|
||||
gpu_pass_provenance_transfer(1);
|
||||
gpu_pass_scene_transfer(1);
|
||||
gr_set_texture_window(texture_window_value);
|
||||
gr_set_draw_area((int)draw_area_left, (int)draw_area_top,
|
||||
(int)draw_area_right, (int)draw_area_bottom);
|
||||
gr_set_draw_offset(draw_offset_x, draw_offset_y);
|
||||
gr_set_mask_bits((int)set_mask_bit, (int)check_mask_bit);
|
||||
ws_nw_sync_target();
|
||||
/* Target sync may classify the scene before render_pass.c restores RAM.
|
||||
* Do not retain history learned from the synthetic draw or that query. */
|
||||
gpu_pass_scene_transfer(1);
|
||||
s_pass_regs_len = 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,243 @@
|
||||
/* Exercise the real GPU checkpoint and GP0 provenance consumers without a GL
|
||||
* context. RAM rollback deliberately follows GPU restore, as render_pass.c
|
||||
* does: a later canonical DMA must find the proof for its restored words. */
|
||||
#define GPU_EXEC_REAL_UI_GROUP
|
||||
#define main gpu_packet_fixture_main
|
||||
#include "test_gpu_textured_dot_nw_shift_exec.c"
|
||||
#undef main
|
||||
|
||||
static const uint32_t command_addr = 0x10004u;
|
||||
static const uint32_t canonical[] = {
|
||||
0x28020304u, 0, 320, 240u << 16, (240u << 16) | 320u
|
||||
};
|
||||
|
||||
static void packet_to_ram(const uint32_t *words, uint32_t count) {
|
||||
memcpy(&test_ram[command_addr / 4u], words, count * sizeof(words[0]));
|
||||
}
|
||||
|
||||
static void packet_to_gp0(const uint32_t *words, uint32_t count) {
|
||||
gp0_cmd_source_addr = command_addr;
|
||||
gp0_words_needed = (int)count;
|
||||
memcpy(gp0_cmd_buf, words, count * sizeof(words[0]));
|
||||
}
|
||||
|
||||
static void start_scene(void) {
|
||||
reset_gpu_state_for_test();
|
||||
configure_native_wide_16_9();
|
||||
ws_hud_anchor_clear(ws_reveal_clear_tags, WS_HUD_ANCHOR_TABLE_SIZE);
|
||||
ws_hud_anchor_clear(ws_screen_mask_tags, WS_HUD_ANCHOR_TABLE_SIZE);
|
||||
ws_radial_mask_clear(ws_radial_screen_mask_tags);
|
||||
ws_repeat_rect_tag_clear(ws_repeat_rect_tags);
|
||||
memset(ws_primitive_roles, 0, sizeof(ws_primitive_roles));
|
||||
ws_ui_prepass_count = ws_ui_prepass_node_count = 0;
|
||||
ws_ui_prepass_rank = 0xFFFFu;
|
||||
ws_reset_scene_history();
|
||||
ws_nw_phase_backdrop = 0;
|
||||
s_bg_phase_frame = UINT32_MAX;
|
||||
packet_to_ram(canonical, 5);
|
||||
packet_to_gp0(canonical, 5);
|
||||
}
|
||||
|
||||
static void test_retagged_canonical_packet(void) {
|
||||
start_scene();
|
||||
gpu_ws_tag_background_prim(command_addr - 4u);
|
||||
gpu_ws_tag_hud_prim(command_addr - 4u, -1);
|
||||
gpu_ws_tag_world_primitive(command_addr - 4u, 1);
|
||||
gpu_ws_tag_hud_primitive(command_addr - 4u, -1);
|
||||
const int32_t offset = ws_nw_offset();
|
||||
assert(offset > 0);
|
||||
GpuWsTagStats before, after;
|
||||
gpu_ws_get_tag_stats(NULL, &before);
|
||||
|
||||
/* Repeated in-between draws reuse the same packet address and guest frame. */
|
||||
for (uint32_t pass = 1; pass <= 3; ++pass) {
|
||||
uint32_t synthetic[5];
|
||||
memcpy(synthetic, canonical, sizeof(synthetic));
|
||||
synthetic[1] += pass;
|
||||
assert(gpu_pass_checkpoint_save());
|
||||
packet_to_ram(synthetic, 5);
|
||||
packet_to_gp0(synthetic, 5);
|
||||
gpu_ws_tag_background_prim(command_addr - 4u);
|
||||
gpu_ws_tag_hud_prim(command_addr - 4u, 1);
|
||||
gpu_ws_tag_world_primitive(command_addr - 4u, 0);
|
||||
gpu_ws_tag_hud_primitive(command_addr - 4u, 1);
|
||||
int32_t delta;
|
||||
assert(psx_ws_prim_in_backdrop() == 1);
|
||||
assert(ws_nw_explicit_hud_delta(&delta) && delta == offset);
|
||||
|
||||
gpu_pass_checkpoint_restore();
|
||||
packet_to_ram(canonical, 5);
|
||||
packet_to_gp0(canonical, 5);
|
||||
assert(psx_ws_prim_in_backdrop() == 1);
|
||||
assert(ws_nw_explicit_hud_delta(&delta) && delta == -offset);
|
||||
assert(ws_tagged_world_primitive());
|
||||
assert(ws_tagged_hud_edge() == -1);
|
||||
assert(gpu_ws_background_requires_full_composite());
|
||||
packet_to_gp0(synthetic, 5);
|
||||
assert(!ws_nw_explicit_background());
|
||||
assert(!ws_nw_explicit_hud_delta(&delta));
|
||||
packet_to_gp0(canonical, 5);
|
||||
}
|
||||
gpu_ws_get_tag_stats(NULL, &after);
|
||||
assert(after.tag_calls == before.tag_calls + 3);
|
||||
assert(after.hit >= before.hit + 6); /* Diagnostic activity is not rolled back. */
|
||||
}
|
||||
|
||||
static void test_pass_only_tags_and_phase(void) {
|
||||
start_scene();
|
||||
ws_nw_phase_backdrop = 1;
|
||||
ws_bg_phase_note(0x28u);
|
||||
assert(psx_ws_prim_in_backdrop() == 1);
|
||||
assert(!gpu_ws_background_requires_full_composite());
|
||||
assert(gpu_pass_checkpoint_save());
|
||||
gpu_ws_tag_background_prim(command_addr - 4u);
|
||||
gpu_ws_tag_hud_prim(command_addr - 4u, 1);
|
||||
gpu_ws_tag_world_primitive(command_addr - 4u, 1);
|
||||
ws_bg_phase_note(0x30u);
|
||||
assert(gpu_ws_background_requires_full_composite());
|
||||
gpu_pass_checkpoint_restore();
|
||||
packet_to_ram(canonical, 5);
|
||||
packet_to_gp0(canonical, 5);
|
||||
assert(!ws_nw_explicit_background());
|
||||
assert(!ws_nw_explicit_hud_delta(NULL));
|
||||
assert(!gpu_ws_background_stretch_active());
|
||||
assert(!gpu_ws_background_requires_full_composite());
|
||||
assert(!ws_tagged_world_primitive());
|
||||
assert(psx_ws_prim_in_backdrop() == 1); /* Original early-backdrop phase. */
|
||||
}
|
||||
|
||||
static void test_hud_dot_draw(void) {
|
||||
start_scene();
|
||||
set_dot_packet(command_addr, 20, 20);
|
||||
gp0_words_needed = 3;
|
||||
uint32_t dot[3];
|
||||
memcpy(dot, gp0_cmd_buf, sizeof(dot));
|
||||
gpu_ws_tag_hud_prim(command_addr - 4u, -1);
|
||||
assert(gpu_pass_checkpoint_save());
|
||||
set_dot_packet(command_addr, 80, 20);
|
||||
gpu_ws_tag_hud_prim(command_addr - 4u, 1);
|
||||
exec_dot_and_expect(80 + ws_nw_offset(), 20);
|
||||
gpu_pass_checkpoint_restore();
|
||||
packet_to_ram(dot, 3);
|
||||
packet_to_gp0(dot, 3);
|
||||
exec_dot_and_expect(20 - ws_nw_offset(), 20);
|
||||
}
|
||||
|
||||
static void test_screen_mask_packet(void) {
|
||||
start_scene();
|
||||
gpu_ws_tag_screen_mask_quad(command_addr - 4u);
|
||||
gpu_ws_tag_radial_screen_mask_quad(command_addr - 4u, 2.f);
|
||||
assert(gpu_pass_checkpoint_save());
|
||||
uint32_t synthetic[5];
|
||||
memcpy(synthetic, canonical, sizeof(synthetic));
|
||||
synthetic[1] = 16;
|
||||
packet_to_ram(synthetic, 5);
|
||||
gpu_ws_tag_screen_mask_quad(command_addr - 4u);
|
||||
gpu_ws_tag_radial_screen_mask_quad(command_addr - 4u, 3.f);
|
||||
gpu_pass_checkpoint_restore();
|
||||
packet_to_ram(canonical, 5);
|
||||
packet_to_gp0(canonical, 5);
|
||||
assert(ws_hud_anchor_lookup(ws_screen_mask_tags, WS_HUD_ANCHOR_TABLE_SIZE,
|
||||
command_addr, gp0_cmd_buf, 5, (uint32_t)s_frame_count, NULL));
|
||||
int32_t x[4] = {0, 320, 0, 320}, y[4] = {0, 0, 240, 240};
|
||||
assert(ws_nw_radial_mask_transform(x, y));
|
||||
assert(x[0] == -160 && x[1] == 480); /* Canonical scale 2, not pass scale 3. */
|
||||
}
|
||||
|
||||
static void test_repeat_rect_packet(void) {
|
||||
start_scene();
|
||||
const uint32_t rect[] = {0x64808080u, 0, 0, (64u << 16) | 32u};
|
||||
uint32_t synthetic[4];
|
||||
memcpy(synthetic, rect, sizeof(synthetic));
|
||||
synthetic[1] = 8;
|
||||
packet_to_ram(rect, 4);
|
||||
packet_to_gp0(rect, 4);
|
||||
gpu_ws_tag_black_reveal_rect(command_addr - 4u);
|
||||
gpu_ws_tag_repeat_rect(command_addr - 4u, 128);
|
||||
assert(gpu_pass_checkpoint_save());
|
||||
packet_to_ram(synthetic, 4);
|
||||
gpu_ws_tag_black_reveal_rect(command_addr - 4u);
|
||||
gpu_ws_tag_repeat_rect(command_addr - 4u, 64);
|
||||
gpu_pass_checkpoint_restore();
|
||||
packet_to_ram(rect, 4);
|
||||
packet_to_gp0(rect, 4);
|
||||
assert(ws_hud_anchor_lookup(ws_reveal_clear_tags, WS_HUD_ANCHOR_TABLE_SIZE,
|
||||
command_addr, gp0_cmd_buf, 4, (uint32_t)s_frame_count, NULL));
|
||||
assert(ws_repeat_rect_tag_lookup(ws_repeat_rect_tags, command_addr,
|
||||
gp0_cmd_buf, 4, (uint32_t)s_frame_count) == 128);
|
||||
}
|
||||
|
||||
static void test_scene_evidence(void) {
|
||||
start_scene();
|
||||
ws_full_2d = 0;
|
||||
gpu_ws_set_gte_game_mode(1);
|
||||
assert(gpu_ws_present_native_43());
|
||||
assert(gpu_pass_checkpoint_save());
|
||||
psx_ws_note_gte_project(3);
|
||||
assert(!gpu_ws_present_native_43());
|
||||
gpu_pass_checkpoint_restore();
|
||||
packet_to_ram(canonical, 5);
|
||||
assert(gpu_ws_present_native_43());
|
||||
gpu_ws_set_gte_game_mode(0);
|
||||
}
|
||||
|
||||
static void test_sprite_anchor(void) {
|
||||
start_scene();
|
||||
const uint32_t anchor_addr = 0x20000u;
|
||||
ws_anchor_addr = anchor_addr;
|
||||
CPUState cpu = {0};
|
||||
cpu.read_word = psx_read_word;
|
||||
cpu.gpr[4] = command_addr - 4u;
|
||||
test_ram[anchor_addr / 4u] = 40;
|
||||
psx_ws_sprite_tag(&cpu);
|
||||
assert(gpu_pass_checkpoint_save());
|
||||
test_ram[anchor_addr / 4u] = 80;
|
||||
psx_ws_sprite_tag(&cpu);
|
||||
gpu_pass_checkpoint_restore();
|
||||
test_ram[anchor_addr / 4u] = 40;
|
||||
int32_t anchor;
|
||||
assert(ws_tagged_anchor(&anchor) && anchor == 40);
|
||||
}
|
||||
|
||||
static void test_auto_ui_prepass(void) {
|
||||
start_scene();
|
||||
gpu_ws_set_auto_ui_squash(1);
|
||||
const uint32_t hud[] = {
|
||||
0x28802040u, pack_vertex(20, 20), pack_vertex(40, 20),
|
||||
pack_vertex(20, 32), pack_vertex(40, 32)
|
||||
};
|
||||
uint32_t synthetic[5];
|
||||
memcpy(synthetic, hud, sizeof(synthetic));
|
||||
synthetic[1] = pack_vertex(24, 20);
|
||||
synthetic[3] = pack_vertex(24, 32);
|
||||
test_ram[(command_addr - 4u) / 4u] = (5u << 24) | 0xFFFFFFu;
|
||||
packet_to_ram(hud, 5);
|
||||
gpu_ws_prepass_linked_list(command_addr - 4u);
|
||||
packet_to_gp0(hud, 5);
|
||||
int32_t anchor;
|
||||
assert(ws_auto_ui_anchor(&anchor));
|
||||
assert(gpu_pass_checkpoint_save());
|
||||
packet_to_ram(synthetic, 5);
|
||||
gpu_ws_prepass_linked_list(command_addr - 4u);
|
||||
gpu_pass_checkpoint_restore();
|
||||
packet_to_ram(hud, 5);
|
||||
gpu_ws_validate_linked_list_node(command_addr - 4u, 5);
|
||||
packet_to_gp0(hud, 5);
|
||||
assert(ws_auto_ui_anchor(&anchor));
|
||||
packet_to_gp0(synthetic, 5);
|
||||
assert(!ws_auto_ui_anchor(&anchor));
|
||||
gpu_ws_set_auto_ui_squash(0);
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
test_retagged_canonical_packet();
|
||||
test_pass_only_tags_and_phase();
|
||||
test_hud_dot_draw();
|
||||
test_screen_mask_packet();
|
||||
test_repeat_rect_packet();
|
||||
test_sprite_anchor();
|
||||
test_auto_ui_prepass();
|
||||
test_scene_evidence();
|
||||
puts("gpu_render_pass_provenance: PASS");
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user