diff --git a/runtime/CMakeLists.txt b/runtime/CMakeLists.txt index 2cc5aafa..9bd296bb 100644 --- a/runtime/CMakeLists.txt +++ b/runtime/CMakeLists.txt @@ -1,2140 +1,2146 @@ -cmake_minimum_required(VERSION 3.20) -project(psxrecomp-runtime C CXX) -include(CTest) - -set(CMAKE_C_STANDARD 99) -set(CMAKE_CXX_STANDARD 17) - -# ---- Native/oracle runtime targets ---- - -set(PSXRECOMP_ROOT "${CMAKE_SOURCE_DIR}/..") -include("${CMAKE_SOURCE_DIR}/runtime.cmake") - -psxrecomp_add_runtime_target(psx-runtime - DEBUG_PORT 4370 - WINDOW_TITLE "psxrecomp - BIOS" - DEFAULT_BIOS_PATH "${PSXRECOMP_ROOT}/bios/SCPH1001.BIN" - EXE_NAME "PSXRecomp" -) - -# With -DPSX_PGXP_VARIANT=ON, psxrecomp_add_runtime_target auto-clones a -# psx-runtime-pgxp sibling (PSXRecomp_pgxp.exe) compiled with -DPSX_PGXP=1 — -# the framework-dev validation surface for the PGXP value-propagation engine -# (docs/ENHANCEMENTS.md G1.10). Titles get their _pgxp the same way. - -if(BUILD_TESTING) - add_executable(video_texture_filter_test tests/test_video_texture_filter.c src/video_texture_filter.c) - target_include_directories(video_texture_filter_test PRIVATE include) - add_test(NAME video_texture_filter_test COMMAND video_texture_filter_test) - add_executable(mod_netplay_test tests/test_mod_netplay.c - src/mod_netplay.c src/netplay_state_digest.c src/crc32.c src/psx_sha256.c) - target_include_directories(mod_netplay_test PRIVATE include) - add_test(NAME mod_netplay_test COMMAND mod_netplay_test) - add_test(NAME mod_netplay_wide_test COMMAND mod_netplay_test --wide) - add_test(NAME mod_netplay_optional_bank_test COMMAND mod_netplay_test --no-banks) - add_test(NAME mod_netplay_options_policy_test COMMAND mod_netplay_test --options) - add_executable(savestate_title_policy_test tests/test_savestate_title_policy.c - src/mod_netplay.c src/psx_sha256.c) - target_include_directories(savestate_title_policy_test PRIVATE include) - add_test(NAME savestate_title_policy_test COMMAND savestate_title_policy_test) - add_executable(mod_capcom_bg2d_test tests/test_mod_capcom_bg2d.c) - target_include_directories(mod_capcom_bg2d_test PRIVATE include) - add_test(NAME mod_capcom_bg2d_test COMMAND mod_capcom_bg2d_test) - # White-box failure injection checks restart after partial native hashing. - foreach(_hash_variant native portable) - add_executable(sha256_file_${_hash_variant}_test - tests/test_psx_sha256_file.cpp src/psx_sha256.c) - target_include_directories(sha256_file_${_hash_variant}_test PRIVATE include) - if(_hash_variant STREQUAL "portable") - target_compile_definitions(sha256_file_${_hash_variant}_test PRIVATE - PSX_SHA256_FILE_PORTABLE=1) - elseif(WIN32) - target_link_libraries(sha256_file_${_hash_variant}_test PRIVATE bcrypt) - elseif(TARGET OpenSSL::Crypto) - target_compile_definitions(sha256_file_${_hash_variant}_test PRIVATE - PSX_SHA256_FILE_OPENSSL=1) - target_link_libraries(sha256_file_${_hash_variant}_test PRIVATE OpenSSL::Crypto) - endif() - add_test(NAME sha256_file_${_hash_variant}_test - COMMAND sha256_file_${_hash_variant}_test) - endforeach() - find_package(Threads REQUIRED) - add_executable(host_launch_timing_test - tests/test_host_launch_timing.cpp tests/host_launch_timing_helper.cpp) - target_include_directories(host_launch_timing_test PRIVATE include) - target_link_libraries(host_launch_timing_test PRIVATE Threads::Threads) - add_test(NAME host_launch_timing_test COMMAND host_launch_timing_test) - add_executable(disasm_test tests/test_disasm.cpp src/disasm_shim.cpp - ../recompiler/src/mips_decoder.cpp) - target_include_directories(disasm_test PRIVATE include ../recompiler/include) - add_test(NAME disasm_test COMMAND disasm_test) - add_executable(local_mouse_policy_test - tests/test_local_mouse_policy.cpp src/local_mouse_policy.cpp) - target_include_directories(local_mouse_policy_test PRIVATE include src) - add_test(NAME local_mouse_policy_test COMMAND local_mouse_policy_test) - # Netplay: which BIOS a match boots, and its wire helpers. - add_executable(netplay_bios_settle_test - tests/test_netplay_bios_settle.c - src/netplay_bios_settle.c) - target_include_directories(netplay_bios_settle_test PRIVATE include) - add_test(NAME netplay_bios_settle_test COMMAND netplay_bios_settle_test) - # Netplay: why a match ended, and when a boot-digest mismatch is final. - add_executable(netplay_exit_reason_test - tests/test_netplay_exit_reason.c - src/netplay_exit_reason.c) - target_include_directories(netplay_exit_reason_test PRIVATE include) - add_test(NAME netplay_exit_reason_test COMMAND netplay_exit_reason_test) - add_executable(netplay_sim_pad_cache_test tests/test_netplay_sim_pad_cache.c) - target_include_directories(netplay_sim_pad_cache_test PRIVATE include) - target_compile_definitions(netplay_sim_pad_cache_test PRIVATE PSX_MAX_PLAYERS=4) - add_test(NAME netplay_sim_pad_cache_test COMMAND netplay_sim_pad_cache_test) - add_executable(lobby_launch_transport_test tests/test_lobby_launch_transport.c) - target_include_directories(lobby_launch_transport_test PRIVATE include) - add_test(NAME lobby_launch_transport_test COMMAND lobby_launch_transport_test) - add_executable(netplay_fmv_confirm_test tests/test_netplay_fmv_confirm.c) - target_include_directories(netplay_fmv_confirm_test PRIVATE include) - add_test(NAME netplay_fmv_confirm_test COMMAND netplay_fmv_confirm_test) - add_executable(netplay_local_view_test tests/test_netplay_local_view.c) - target_include_directories(netplay_local_view_test PRIVATE include) - add_test(NAME netplay_local_view_test COMMAND netplay_local_view_test) - # Host relay over ICE: transport choice + lobby seat -> session slot map. - add_executable(netplay_ice_hub_test - tests/test_netplay_ice_hub.c - src/netplay_ice_hub.c) - target_include_directories(netplay_ice_hub_test PRIVATE include) - add_test(NAME netplay_ice_hub_test COMMAND netplay_ice_hub_test) - add_executable(ws_scene_hold_test tests/test_ws_scene_hold.c) - target_include_directories(ws_scene_hold_test PRIVATE include) - add_test(NAME ws_scene_hold_test COMMAND ws_scene_hold_test) - add_executable(ws_backdrop_extent_test tests/test_ws_backdrop_extent.c) - target_include_directories(ws_backdrop_extent_test PRIVATE include) - add_test(NAME ws_backdrop_extent_test COMMAND ws_backdrop_extent_test) - add_executable(mdec_snapshot_activity_test tests/test_mdec_snapshot_activity.c) - target_include_directories(mdec_snapshot_activity_test PRIVATE include src) - add_test(NAME mdec_snapshot_activity_test COMMAND mdec_snapshot_activity_test) - add_executable(mod_texture_banks_test tests/test_mod_texture_banks.c src/mod_texture_banks.c) - target_include_directories(mod_texture_banks_test PRIVATE include) - add_test(NAME mod_texture_banks_test COMMAND mod_texture_banks_test) - # First-call capture / later-call restore behind main.cpp's - # reset_mod_owned_presentation() (header-only, no runtime link). - add_executable(mod_session_baseline_test tests/test_mod_session_baseline.c) - target_include_directories(mod_session_baseline_test PRIVATE include) - add_test(NAME mod_session_baseline_test COMMAND mod_session_baseline_test) - # Netplay: libjuice resolves from third_party/ with the network sealed, - # and a recomp-net pinning another libjuice stops the configure. - foreach(_psx_juice_case same other) - add_test(NAME netplay_libjuice_offline_${_psx_juice_case} - COMMAND ${CMAKE_COMMAND} - -S "${CMAKE_CURRENT_SOURCE_DIR}/tests/netplay_libjuice_offline" - -B "${CMAKE_CURRENT_BINARY_DIR}/netplay_libjuice_offline_${_psx_juice_case}" - -G "${CMAKE_GENERATOR}" - "-DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}" - -DPSX_DEPS_OFFLINE=ON - "-DFAKE_RECOMP_NET=${CMAKE_CURRENT_SOURCE_DIR}/tests/netplay_libjuice_offline/recomp-net-${_psx_juice_case}") - set_tests_properties(netplay_libjuice_offline_${_psx_juice_case} PROPERTIES - ENVIRONMENT "HTTP_PROXY=http://127.0.0.1:9;HTTPS_PROXY=http://127.0.0.1:9;ALL_PROXY=http://127.0.0.1:9;NO_PROXY=") - endforeach() - set_tests_properties(netplay_libjuice_offline_other PROPERTIES - PASS_REGULAR_EXPRESSION "recomp-net pins libjuice SHA256") - if(NOT MSVC) - add_executable(host_snapshot_guard_test tests/test_host_snapshot_guard.c) - add_executable(fntrace_restore_handoff_test - tests/test_fntrace_restore_handoff.c src/fntrace.c) - foreach(test_target host_snapshot_guard_test fntrace_restore_handoff_test) - target_include_directories(${test_target} PRIVATE include src) - target_compile_options(${test_target} PRIVATE -O2 -flto -ffunction-sections -fdata-sections) - target_link_options(${test_target} PRIVATE -flto -Wl,--gc-sections) - add_test(NAME ${test_target} COMMAND ${test_target}) - endforeach() - add_executable(ws_world_scene_test tests/test_ws_world_scene.c - src/psx_video_timing.c) - target_include_directories(ws_world_scene_test PRIVATE include src) - target_compile_options(ws_world_scene_test PRIVATE -O2 -flto -ffunction-sections -fdata-sections) - target_link_options(ws_world_scene_test PRIVATE -flto -Wl,--gc-sections) - add_test(NAME ws_world_scene_test COMMAND ws_world_scene_test) - add_executable(ws_backdrop_scene_test tests/test_ws_backdrop_scene.c - src/psx_video_timing.c) - target_include_directories(ws_backdrop_scene_test PRIVATE include src) - target_compile_options(ws_backdrop_scene_test PRIVATE -O2 -flto -ffunction-sections -fdata-sections) - target_link_options(ws_backdrop_scene_test PRIVATE -flto -Wl,--gc-sections) - add_test(NAME ws_backdrop_scene_test COMMAND ws_backdrop_scene_test) - add_executable(boot_state_handoff_test tests/test_boot_state_handoff.c) - target_include_directories(boot_state_handoff_test PRIVATE include src) - target_compile_definitions(boot_state_handoff_test PRIVATE PSX_COSIM=1) - target_compile_options(boot_state_handoff_test PRIVATE -O2 -flto -ffunction-sections -fdata-sections -UNDEBUG) - target_link_options(boot_state_handoff_test PRIVATE -flto -Wl,--gc-sections) - target_link_libraries(boot_state_handoff_test PRIVATE ZLIB::ZLIB) - add_test(NAME boot_state_handoff_test COMMAND boot_state_handoff_test) - add_executable(boot_state_impl_tag_test tests/test_boot_state_handoff.c) - target_include_directories(boot_state_impl_tag_test PRIVATE include src) - target_compile_definitions(boot_state_impl_tag_test PRIVATE PSX_COSIM=1 PSX_SAVESTATE_IMPL_TAG=0x54474801u) - target_compile_options(boot_state_impl_tag_test PRIVATE -O2 -flto -ffunction-sections -fdata-sections -UNDEBUG) - target_link_options(boot_state_impl_tag_test PRIVATE -flto -Wl,--gc-sections) - target_link_libraries(boot_state_impl_tag_test PRIVATE ZLIB::ZLIB) - add_test(NAME boot_state_impl_tag_test COMMAND boot_state_impl_tag_test) - - add_executable(boot_state_execution_test tests/test_boot_state_handoff.c) - target_include_directories(boot_state_execution_test PRIVATE include src) - target_compile_definitions(boot_state_execution_test PRIVATE PSX_COSIM=1) - target_compile_options(boot_state_execution_test PRIVATE -O2 -flto -ffunction-sections -fdata-sections -UNDEBUG) - target_link_options(boot_state_execution_test PRIVATE -flto -Wl,--gc-sections) - target_link_libraries(boot_state_execution_test PRIVATE ZLIB::ZLIB) - psxrecomp_execution_profile(boot_state_execution_test PROFILE ENHANCED) - add_test(NAME boot_state_execution_test COMMAND boot_state_execution_test) - - add_executable(static_match_cache_test tests/test_static_match_cache.c - src/overlay_loader.c src/crc32.c) - target_include_directories(static_match_cache_test PRIVATE include) - target_compile_definitions(static_match_cache_test PRIVATE - PSX_HAS_OVERLAY_DISPATCH PSX_NO_DEBUG_TOOLS) - target_compile_options(static_match_cache_test PRIVATE -O2 -flto -ffunction-sections -fdata-sections -UNDEBUG) - target_link_options(static_match_cache_test PRIVATE -flto -Wl,--gc-sections) - add_test(NAME static_match_cache_test COMMAND static_match_cache_test) - - add_executable(mod_memory_snapshot_test tests/test_mod_memory_snapshot.c - src/psx_ram_geometry.c) - target_include_directories(mod_memory_snapshot_test PRIVATE include src) - target_compile_options(mod_memory_snapshot_test PRIVATE -O2 -flto -ffunction-sections -fdata-sections) - target_link_options(mod_memory_snapshot_test PRIVATE -flto -Wl,--gc-sections) - add_test(NAME mod_memory_snapshot_test COMMAND mod_memory_snapshot_test) - endif() - add_executable(sio_card_irq_integrity_test tests/test_sio_card_irq_integrity.c) - target_include_directories(sio_card_irq_integrity_test PRIVATE include) - foreach(scenario absent deselect pending coarse) - add_test(NAME sio_card_irq_integrity_${scenario} - COMMAND sio_card_irq_integrity_test ${scenario}) - # The retired environment variable must not reactivate the workaround. - set_tests_properties(sio_card_irq_integrity_${scenario} PROPERTIES - ENVIRONMENT "PSX_APE_CARD_UNSTICK=1") - endforeach() - # Optional real-hardware regression. Configure its dependencies explicitly; - # absence must remain visible, never counted as a passing GPU test. - set(PSX_GL_READBACK_SDL_ROOT "" CACHE PATH "SDL3 static build root for GL readback test") - get_filename_component(_gl_test_default_bin "${CMAKE_C_COMPILER}" DIRECTORY) - set(PSX_GL_READBACK_COMPILER_BIN "${_gl_test_default_bin}" CACHE PATH "GCC/MinGW bin for GL fixture") - find_package(Python3 QUIET COMPONENTS Interpreter) - if(Python3_Interpreter_FOUND) - add_test(NAME guest_implementation_binding_test COMMAND "${Python3_EXECUTABLE}" - "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_guest_implementation.py" - --cmake "${CMAKE_COMMAND}" --compiler "${CMAKE_C_COMPILER}" - --generator "${CMAKE_GENERATOR}" - --work-root "${CMAKE_CURRENT_BINARY_DIR}/hle-binding-fixtures") - find_program(PSX_PROFILE_TEST_NINJA NAMES ninja ninja.exe) - if(PSX_PROFILE_TEST_NINJA) - add_test(NAME execution_profile_test COMMAND "${Python3_EXECUTABLE}" - "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_execution_profile.py" - --cmake "${CMAKE_COMMAND}" --cc "${CMAKE_C_COMPILER}" - --ninja "${PSX_PROFILE_TEST_NINJA}") - set_tests_properties(execution_profile_test PROPERTIES RUN_SERIAL TRUE) - endif() - add_test(NAME host_launch_timings_protocol_test COMMAND ${CMAKE_COMMAND} -E env - "CC=${CMAKE_C_COMPILER}" "CXX=${CMAKE_CXX_COMPILER}" "${Python3_EXECUTABLE}" - "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_host_launch_timings_protocol.py") - set_tests_properties(host_launch_timings_protocol_test PROPERTIES SKIP_RETURN_CODE 77) - add_test(NAME tcp_command_index_check COMMAND "${Python3_EXECUTABLE}" - "${PSXRECOMP_ROOT}/tools/gen_tcp_commands.py" --check) - add_test(NAME wtrace_dump_test COMMAND ${CMAKE_COMMAND} -E env - "CC=${CMAKE_C_COMPILER}" "${Python3_EXECUTABLE}" - "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_wtrace_dump.py") - set_tests_properties(wtrace_dump_test PROPERTIES SKIP_RETURN_CODE 77) - add_test(NAME counted_mips_relocations_test COMMAND "${Python3_EXECUTABLE}" - "${PSXRECOMP_ROOT}/tools/tests/test_counted_mips_relocations.py") - add_test(NAME interp_mod_entry_guards_test COMMAND "${Python3_EXECUTABLE}" - "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_interp_mod_entry_guards.py") - add_test(NAME mirror_pc_preserved_guards_test COMMAND "${Python3_EXECUTABLE}" - "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_mirror_pc_preserved_guards.py") - add_test(NAME segment_miss_wiring_test COMMAND "${Python3_EXECUTABLE}" - "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_segment_miss_wiring.py") - if(CMAKE_C_COMPILER_ID MATCHES "GNU|Clang") - add_test(NAME ws_screen_tags_exec_test COMMAND "${Python3_EXECUTABLE}" - "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_ws_screen_tags_exec.py" - --compiler "${CMAKE_C_COMPILER}") - add_test(NAME ws_activation_bias_exec_test COMMAND "${Python3_EXECUTABLE}" - "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_ws_activation_bias_exec.py" "${CMAKE_C_COMPILER}") - endif() - add_test(NAME gpu_textured_dot_nw_shift_test COMMAND "${Python3_EXECUTABLE}" - "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_gpu_textured_dot_nw_shift.py") - add_test(NAME gl_readback_runner_test COMMAND "${Python3_EXECUTABLE}" - "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_gl_readback_runner.py") - endif() - if(MINGW AND PSX_GL_READBACK_SDL_ROOT - AND EXISTS "${PSX_GL_READBACK_COMPILER_BIN}/gcc.exe" - AND EXISTS "${PSX_GL_READBACK_COMPILER_BIN}/g++.exe" - AND IS_DIRECTORY "${PSX_GL_READBACK_SDL_ROOT}/sdl3-src/include" - AND EXISTS "${PSX_GL_READBACK_SDL_ROOT}/sdl3-build/libSDL3.a") - find_package(Python3 REQUIRED COMPONENTS Interpreter) - add_test(NAME gl_readback_region_test - COMMAND "${Python3_EXECUTABLE}" - "${CMAKE_CURRENT_SOURCE_DIR}/tests/run_gl_readback_region.py" - --fixture "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_gl_readback_region.c" - --compiler-bin "${PSX_GL_READBACK_COMPILER_BIN}" - --sdl-root "${PSX_GL_READBACK_SDL_ROOT}" - --output "${CMAKE_CURRENT_BINARY_DIR}/gl-readback-results") - set_tests_properties(gl_readback_region_test PROPERTIES LABELS "gpu;opengl;hardware" TIMEOUT 180) - else() - message(STATUS "gl_readback_region_test: not registered (requires MinGW, SDL3 headers/static library in PSX_GL_READBACK_SDL_ROOT and gcc.exe/g++.exe in PSX_GL_READBACK_COMPILER_BIN)") - endif() - # Real-GL internal-resolution invariance (macOS, Linux and Windows/MinGW, - # hidden window): the guest-visible VRAM is identical at every scale, lines - # stay one native pixel thick, and an over-limit request clamps inside GL. - # Needs the SDL3 this tree fetched and a GPU session; labelled hardware like - # the readback test above. A host with no window/GL 3.3 context (headless, - # or Windows over SSH) exits 77 and is reported as skipped. - if((NOT WIN32 OR MINGW) AND Python3_Interpreter_FOUND AND TARGET SDL3::SDL3) - if(DEFINED sdl3_SOURCE_DIR) - set(_gl_scale_sdl_inc "${sdl3_SOURCE_DIR}/include") - else() - set(_gl_scale_sdl_inc "$") - endif() - add_test(NAME gl_scale_invariance_test - COMMAND "${Python3_EXECUTABLE}" - "${CMAKE_CURRENT_SOURCE_DIR}/tests/run_gl_scale_invariance.py" - --fixture "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_gl_scale_invariance.c" - --cc "${CMAKE_C_COMPILER}" - --sdl-include "${_gl_scale_sdl_inc}" - --sdl-library "$" - --output "${CMAKE_CURRENT_BINARY_DIR}/gl-scale-results") - set_tests_properties(gl_scale_invariance_test PROPERTIES - LABELS "gpu;opengl;hardware" TIMEOUT 300 SKIP_RETURN_CODE 77) - # Render thread ([video] render_thread) on vs off: the values the - # guest reads back, native VRAM, the frame at S and the native-wide - # surface are identical for a scripted stream with readbacks, mask - # bits, uploads, ring wraps and per-primitive widescreen tags. A host - # with no window/GL 3.3 context (headless, Windows over SSH) exits 77: - # reported as skipped. - add_test(NAME gl_render_thread_test - COMMAND "${Python3_EXECUTABLE}" - "${CMAKE_CURRENT_SOURCE_DIR}/tests/run_gl_render_thread.py" - --fixture "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_gl_render_thread.c" - --cc "${CMAKE_C_COMPILER}" - --sdl-include "${_gl_scale_sdl_inc}" - --sdl-library "$" - --output "${CMAKE_CURRENT_BINARY_DIR}/gl-render-thread-results") - set_tests_properties(gl_render_thread_test PROPERTIES - LABELS "gpu;opengl;hardware" TIMEOUT 300 SKIP_RETURN_CODE 77) - # Frame generation ([video] frame_generation): the real presented - # images are identical with it off and on (VRAM and native-wide - # presents), generated frames are presented, and an in-between frame - # at phase 1 / 0 equals the newer / older real frame. - add_test(NAME gl_frame_gen_test - COMMAND "${Python3_EXECUTABLE}" - "${CMAKE_CURRENT_SOURCE_DIR}/tests/run_gl_frame_gen.py" - --fixture "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_gl_frame_gen.c" - --cc "${CMAKE_C_COMPILER}" - --sdl-include "${_gl_scale_sdl_inc}" - --sdl-library "$" - --output "${CMAKE_CURRENT_BINARY_DIR}/gl-frame-gen-results") - set_tests_properties(gl_frame_gen_test PROPERTIES - LABELS "gpu;opengl;hardware" TIMEOUT 300 SKIP_RETURN_CODE 77) - # [video] texture_lod / anisotropic_filtering: off and untracked/ - # magnified prims are unchanged; minified world textures average - # inside their UV bounds; render thread and PGXP depth agree. - add_test(NAME gl_texture_lod_test - COMMAND "${Python3_EXECUTABLE}" - "${CMAKE_CURRENT_SOURCE_DIR}/tests/run_gl_render_thread.py" --single - --fixture "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_gl_texture_lod.c" - --cc "${CMAKE_C_COMPILER}" - --sdl-include "${_gl_scale_sdl_inc}" - --sdl-library "$" - --output "${CMAKE_CURRENT_BINARY_DIR}/gl-texture-lod-results") - set_tests_properties(gl_texture_lod_test PROPERTIES - LABELS "gpu;opengl;hardware" TIMEOUT 180 SKIP_RETURN_CODE 77) - # PS1 semi-transparency modes and mask bit at 4x with PGXP depth and - # the render thread, against the formulas and the software reference - # ([video] accurate_blending on; off is measured and printed). - add_test(NAME gl_blend_accuracy_test - COMMAND "${Python3_EXECUTABLE}" - "${CMAKE_CURRENT_SOURCE_DIR}/tests/run_gl_render_thread.py" --single - --fixture "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_gl_blend_accuracy.c" - --cc "${CMAKE_C_COMPILER}" - --sdl-include "${_gl_scale_sdl_inc}" - --sdl-library "$" - --output "${CMAKE_CURRENT_BINARY_DIR}/gl-blend-accuracy-results") - set_tests_properties(gl_blend_accuracy_test PROPERTIES - LABELS "gpu;opengl;hardware" TIMEOUT 180 SKIP_RETURN_CODE 77) - # texture_filtering = "xbr": diagonals lose the staircase, straight - # edges, flat areas, cutouts and 1:1 draws stay nearest; RT agrees. - add_test(NAME gl_xbr_filter_test - COMMAND "${Python3_EXECUTABLE}" - "${CMAKE_CURRENT_SOURCE_DIR}/tests/run_gl_render_thread.py" --single - --fixture "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_gl_xbr.c" - --cc "${CMAKE_C_COMPILER}" - --sdl-include "${_gl_scale_sdl_inc}" - --sdl-library "$" - --output "${CMAKE_CURRENT_BINARY_DIR}/gl-xbr-results") - set_tests_properties(gl_xbr_filter_test PROPERTIES - LABELS "gpu;opengl;hardware" TIMEOUT 180 SKIP_RETURN_CODE 77) - # [video] dithering: off ignores the guest's dither bit (byte-identical - # true colour); on/scaled add the PS1 pattern to shaded/modulated draws - # only, through the facade, the render thread, PGXP depth and wide. - add_test(NAME gl_dithering_test - COMMAND "${Python3_EXECUTABLE}" - "${CMAKE_CURRENT_SOURCE_DIR}/tests/run_gl_render_thread.py" --single - --fixture "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_gl_dithering.c" - --cc "${CMAKE_C_COMPILER}" - --sdl-include "${_gl_scale_sdl_inc}" - --sdl-library "$" - --output "${CMAKE_CURRENT_BINARY_DIR}/gl-dithering-results") - set_tests_properties(gl_dithering_test PROPERTIES - LABELS "gpu;opengl;hardware" TIMEOUT 180 SKIP_RETURN_CODE 77) - unset(_gl_scale_sdl_inc) - else() - message(STATUS "gl_scale_invariance_test: not registered (needs macOS, Linux or MinGW, Python 3 and SDL3)") - endif() - if(Python3_Interpreter_FOUND AND TARGET SDL3::SDL3-static) - add_test(NAME gl_texture_filter_test - COMMAND "${Python3_EXECUTABLE}" - "${CMAKE_CURRENT_SOURCE_DIR}/tests/run_gl_texture_filter.py" - --fixture "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_gl_texture_filter.c" - --cc "${CMAKE_C_COMPILER}" - --sdl-include "$" - --sdl-library "$" - --output "${CMAKE_CURRENT_BINARY_DIR}/gl-filter-results") - set_tests_properties(gl_texture_filter_test PROPERTIES LABELS "gpu;opengl;hardware" TIMEOUT 180) - add_test(NAME gl_post_aa_test - COMMAND "${Python3_EXECUTABLE}" - "${CMAKE_CURRENT_SOURCE_DIR}/tests/run_gl_texture_filter.py" - --fixture "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_gl_post_aa.c" - --cc "${CMAKE_C_COMPILER}" - --sdl-include "$" - --sdl-library "$" - --output "${CMAKE_CURRENT_BINARY_DIR}/gl-post-aa-results") - set_tests_properties(gl_post_aa_test PROPERTIES - LABELS "gpu;opengl;hardware" TIMEOUT 180 SKIP_RETURN_CODE 77) - add_test(NAME gl_bloom_test - COMMAND "${Python3_EXECUTABLE}" - "${CMAKE_CURRENT_SOURCE_DIR}/tests/run_gl_texture_filter.py" - --fixture "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_gl_bloom.c" - --cc "${CMAKE_C_COMPILER}" - --sdl-include "$" - --sdl-library "$" - --output "${CMAKE_CURRENT_BINARY_DIR}/gl-bloom-results") - set_tests_properties(gl_bloom_test PROPERTIES - LABELS "gpu;opengl;hardware" TIMEOUT 180 SKIP_RETURN_CODE 77) - add_test(NAME gl_hd_texture_pack_test - COMMAND "${Python3_EXECUTABLE}" - "${CMAKE_CURRENT_SOURCE_DIR}/tests/run_gl_texture_filter.py" - --hd-pack --fixture "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_gl_hd_texture_pack.c" - --cc "${CMAKE_C_COMPILER}" --cxx "${CMAKE_CXX_COMPILER}" - --webp-include "$" - --webp-decoder "$" - --sdl-include "$" - --sdl-library "$" - --output "${CMAKE_CURRENT_BINARY_DIR}/gl-hd-texture-results") - set_tests_properties(gl_hd_texture_pack_test PROPERTIES LABELS "gpu;opengl;hardware" TIMEOUT 180) - # [video] fmv_chroma_smoothing: 24-bit presents lose their 2x2 chroma - # blocks, luma kept; 15-bit presents and off are byte-identical. - add_test(NAME gl_fmv_chroma_test - COMMAND "${Python3_EXECUTABLE}" - "${CMAKE_CURRENT_SOURCE_DIR}/tests/run_gl_texture_filter.py" - --fixture "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_gl_fmv_chroma.c" - --cc "${CMAKE_C_COMPILER}" - --sdl-include "$" - --sdl-library "$" - --output "${CMAKE_CURRENT_BINARY_DIR}/gl-fmv-chroma-results") - set_tests_properties(gl_fmv_chroma_test PROPERTIES - LABELS "gpu;opengl;hardware" TIMEOUT 180 SKIP_RETURN_CODE 77) - # Debug present ring: GL names do not outlive a renderer restart. - add_test(NAME gl_present_ring_restart_test - COMMAND "${Python3_EXECUTABLE}" - "${CMAKE_CURRENT_SOURCE_DIR}/tests/run_gl_texture_filter.py" - --debug-tools - --fixture "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_gl_present_ring_restart.c" - --cc "${CMAKE_C_COMPILER}" - --sdl-include "$" - --sdl-library "$" - --output "${CMAKE_CURRENT_BINARY_DIR}/gl-present-ring-restart-results") - set_tests_properties(gl_present_ring_restart_test PROPERTIES - LABELS "gpu;opengl;hardware" TIMEOUT 180 SKIP_RETURN_CODE 77) - endif() - add_executable(launcher_device_roundtrip_test - tests/test_launcher_device_roundtrip.cpp) - target_include_directories(launcher_device_roundtrip_test PRIVATE include) - add_test(NAME launcher_device_roundtrip_test - COMMAND launcher_device_roundtrip_test) - - # Pad-mode resolution at the launcher exit seam: a lock_mode title must - # boot the pad type its game.toml declared whatever the launcher hands - # back, an active mod controller-mode override must outrank the lock, and - # neither may let an unsupported mode reach settings.toml. Header-only, so - # it needs nothing but runtime/include. - add_executable(launcher_pad_mode_resolve_test - tests/test_launcher_pad_mode_resolve.cpp) - target_include_directories(launcher_pad_mode_resolve_test PRIVATE include) - add_test(NAME launcher_pad_mode_resolve_test - COMMAND launcher_pad_mode_resolve_test) - - # Presentation-only mod input must not be sourced from disconnected, - # peer-synchronized, or rollback-replayed pad state. - add_executable(mod_local_input_scope_test - tests/test_mod_local_input_scope.c) - target_include_directories(mod_local_input_scope_test PRIVATE src) - add_test(NAME mod_local_input_scope_test - COMMAND mod_local_input_scope_test) - - # COP0.CAUSE IP2 must mirror the INTC line combinationally, never latch. - add_executable(cause_ip2_combinational_test - tests/test_cause_ip2_combinational.c) - add_test(NAME cause_ip2_combinational_test - COMMAND cause_ip2_combinational_test) - - add_executable(psx_cyc_batch_test tests/test_psx_cyc_batch.c) - target_include_directories(psx_cyc_batch_test PRIVATE include) - target_compile_definitions(psx_cyc_batch_test PRIVATE - PSX_ENABLE_BLOCK_CYCLES=1) - add_test(NAME psx_cyc_batch_test COMMAND psx_cyc_batch_test) - - # [timing] guest_cycle_scale divides instruction charges only (1 = faithful). - add_executable(psx_guest_cycle_scale_test tests/test_psx_guest_cycle_scale.c) - target_include_directories(psx_guest_cycle_scale_test PRIVATE include) - target_compile_definitions(psx_guest_cycle_scale_test PRIVATE - PSX_ENABLE_BLOCK_CYCLES=1) - add_test(NAME psx_guest_cycle_scale_test COMMAND psx_guest_cycle_scale_test) - - # One CPU charge for every tier, the declarative RAM gate and the mod gate, - # and rollback re-simulation, against the real psx_cycles.c. - add_executable(psx_guest_cycle_scale_gate_test - tests/test_psx_guest_cycle_scale_gate.c - src/psx_cycles.c) - target_include_directories(psx_guest_cycle_scale_gate_test PRIVATE include) - target_compile_definitions(psx_guest_cycle_scale_gate_test PRIVATE - PSX_ENABLE_BLOCK_CYCLES=1) - add_test(NAME psx_guest_cycle_scale_gate_test COMMAND psx_guest_cycle_scale_gate_test) - - # Segment misses in static game code (docs/SEGMENT_AWARE_CODE.md §5.5): - # classification against the dispatch rows, the ring and the per-PC - # summary the TCP command and the exit report read. - add_executable(psx_segment_miss_test - tests/test_psx_segment_miss.c - src/psx_segment_miss.c - ) - target_include_directories(psx_segment_miss_test PRIVATE include) - add_test(NAME psx_segment_miss_test COMMAND psx_segment_miss_test) - - add_executable(psx_icache_fastpath_test - tests/test_psx_icache_fastpath.c - src/psx_icache.c - ) - target_include_directories(psx_icache_fastpath_test PRIVATE include) - # PSX_OVERLAY_DLL_BUILD is the seam psx_cycles.h already provides for a TU - # that supplies its own psx_advance_cycles (overlay DLLs forward it through - # a callback shim). Without it the header inlines the production definition - # and this test's counting stub is a redefinition — the whole runtime test - # suite stopped building at "redefinition of 'psx_advance_cycles'", which - # also blocked every test registered after it. - target_compile_definitions(psx_icache_fastpath_test PRIVATE - PSX_ENABLE_BLOCK_CYCLES=1 - PSX_OVERLAY_DLL_BUILD=1) - add_test(NAME psx_icache_fastpath_test COMMAND psx_icache_fastpath_test) - - add_executable(psx_icache_elision_guard_test - tests/test_psx_icache_elision_guard.c src/psx_icache.c) - target_include_directories(psx_icache_elision_guard_test PRIVATE include) - target_compile_definitions(psx_icache_elision_guard_test PRIVATE - PSX_ENABLE_BLOCK_CYCLES=1 PSX_OVERLAY_DLL_BUILD=1) - add_test(NAME psx_icache_elision_guard_test COMMAND psx_icache_elision_guard_test) - - foreach(_ram_geometry IN ITEMS retail expanded) - add_executable(psx_memory_geometry_${_ram_geometry}_test - tests/test_psx_memory_geometry.c) - target_include_directories(psx_memory_geometry_${_ram_geometry}_test - PRIVATE include) - if(_ram_geometry STREQUAL "expanded") - target_compile_definitions(psx_memory_geometry_${_ram_geometry}_test - PRIVATE PSX_MEMORY_GEOMETRY_TEST_EXPANDED=1) - endif() - add_test(NAME psx_memory_geometry_${_ram_geometry}_test - COMMAND psx_memory_geometry_${_ram_geometry}_test) - endforeach() - - # Live 2/8 MiB geometry end to end through the REAL memory.c accessors, - # dma.c channel execution and boot_state.c save/load (including the - # refuse-before-mutation contract). Section GC, as for - # mod_memory_snapshot_test, drops the hardware routing the test never - # reaches; psx_ram_runtime_map_stubs.c doubles the rest. - if(NOT MSVC) - add_executable(psx_ram_runtime_map_test - tests/test_psx_ram_runtime_map.c - tests/psx_ram_runtime_map_stubs.c - src/memory.c - src/dma.c - src/gpu_timeline.c - src/boot_state.c - src/psx_ram_geometry.c) - target_include_directories(psx_ram_runtime_map_test PRIVATE include src) - target_compile_options(psx_ram_runtime_map_test PRIVATE - -O2 -flto -ffunction-sections -fdata-sections -Wno-lto-type-mismatch) - target_link_options(psx_ram_runtime_map_test PRIVATE - -flto -Wl,--gc-sections -Wno-lto-type-mismatch) - target_link_libraries(psx_ram_runtime_map_test PRIVATE ZLIB::ZLIB) - add_test(NAME psx_ram_runtime_map_test COMMAND psx_ram_runtime_map_test) - - # Cache-isolated (SR.IsC) CPU stores through the REAL memory.c store - # paths and the REAL psx_icache.c tag model, against Beetle's - # WriteMemory IsC branch: FlushCache invalidation, tag-test valid bits, - # the D-cache scratchpad write, the BIU and DMA exclusions. Same link - # doubles as psx_ram_runtime_map_test, minus the I-cache ones. - add_executable(isc_store_test - tests/test_isc_store.c - tests/psx_ram_runtime_map_stubs.c - src/memory.c - src/dma.c - src/gpu_timeline.c - src/boot_state.c - src/psx_icache.c - src/psx_ram_geometry.c) - target_include_directories(isc_store_test PRIVATE include src) - target_compile_definitions(isc_store_test PRIVATE - PSX_ENABLE_BLOCK_CYCLES=1 PSX_STUBS_REAL_ICACHE=1) - target_compile_options(isc_store_test PRIVATE - -O2 -flto -ffunction-sections -fdata-sections -Wno-lto-type-mismatch) - target_link_options(isc_store_test PRIVATE -flto -Wno-lto-type-mismatch - $,-Wl$-dead_strip,-Wl$--gc-sections>) - target_link_libraries(isc_store_test PRIVATE ZLIB::ZLIB) - add_test(NAME isc_store_test COMMAND isc_store_test) - - # DMA and host stores drop the PGXP shadow of the words they touch - # (an identical word is not proof of the same projection): the REAL - # memory.c store paths against the REAL pgxp.cpp engine. Same link - # doubles as isc_store_test, minus the PGXP one. - add_executable(pgxp_untracked_store_test - tests/test_pgxp_untracked_store.c - tests/psx_ram_runtime_map_stubs.c - src/memory.c - src/dma.c - src/gpu_timeline.c - src/boot_state.c - src/pgxp.cpp - src/psx_ram_geometry.c) - target_include_directories(pgxp_untracked_store_test PRIVATE include src) - target_compile_definitions(pgxp_untracked_store_test PRIVATE - PSX_STUBS_REAL_PGXP=1) - target_compile_options(pgxp_untracked_store_test PRIVATE - -O2 -flto -ffunction-sections -fdata-sections -Wno-lto-type-mismatch) - target_link_options(pgxp_untracked_store_test PRIVATE -flto -Wno-lto-type-mismatch - $,-Wl$-dead_strip,-Wl$--gc-sections>) - target_link_libraries(pgxp_untracked_store_test PRIVATE ZLIB::ZLIB) - add_test(NAME pgxp_untracked_store_test COMMAND pgxp_untracked_store_test) - endif() - - # Loadable BIOS backends (psx_bios_module.h): the host loader against - # three fake modules built here from one source -- good, wrong ABI tag, - # wrong codegen hash. Real dlopen/LoadLibrary, real registry, stubbed core. - if(NOT MSVC) - foreach(_fake IN ITEMS good bad_abi bad_hash) - add_library(bios_module_fake_${_fake} SHARED tests/bios_module_fake.c) - target_include_directories(bios_module_fake_${_fake} PRIVATE include) - target_compile_definitions(bios_module_fake_${_fake} PRIVATE - PSX_OVERLAY_DLL_BUILD PSX_NO_DEBUG_TOOLS) - set_target_properties(bios_module_fake_${_fake} PROPERTIES PREFIX "") - endforeach() - target_compile_definitions(bios_module_fake_bad_abi PRIVATE FAKE_ABI_TAG=0x7fff) - target_compile_definitions(bios_module_fake_bad_hash PRIVATE FAKE_CODEGEN_HASH=0x1u) - add_executable(bios_module_host_test - tests/test_bios_module_host.c - src/psx_bios_module.c - src/psx_bios_backend.c - src/overlay_posix.c - src/crc32.c) - target_include_directories(bios_module_host_test PRIVATE include src) - target_compile_definitions(bios_module_host_test PRIVATE - PSX_NO_DEBUG_TOOLS - FAKE_GOOD="$" - FAKE_BAD_ABI="$" - FAKE_BAD_HASH="$") - target_link_libraries(bios_module_host_test PRIVATE ${CMAKE_DL_LIBS}) - add_dependencies(bios_module_host_test - bios_module_fake_good bios_module_fake_bad_abi bios_module_fake_bad_hash) - add_test(NAME bios_module_host_test COMMAND bios_module_host_test) - endif() - - add_executable(host_keymap_test - tests/test_host_keymap.c - src/host_keymap.c) - target_include_directories(host_keymap_test PRIVATE - include ${PSX_SDL_INCLUDE_DIRS}) - target_compile_definitions(host_keymap_test PRIVATE - SDL_MAIN_HANDLED - $<$:PSX_SDL3=1>) - target_link_libraries(host_keymap_test PRIVATE ${PSX_SDL_LIBRARIES}) - add_test(NAME host_keymap_test COMMAND host_keymap_test) - - add_executable(host_osd_diagnostics_test - tests/test_host_osd_diagnostics.c src/host_osd.c) - target_include_directories(host_osd_diagnostics_test PRIVATE - include ${PSX_SDL_INCLUDE_DIRS}) - target_compile_definitions(host_osd_diagnostics_test PRIVATE - SDL_MAIN_HANDLED RECOMP_LAUNCHER=1 PSX_SDL_NO_RENDER=1 - $<$:PSX_SDL3=1>) - target_link_libraries(host_osd_diagnostics_test PRIVATE ${PSX_SDL_LIBRARIES}) - add_test(NAME host_osd_diagnostics_test COMMAND host_osd_diagnostics_test) - - add_executable(keyboard_pad_chord_test - tests/test_keyboard_pad_chord.c - src/psx_keybinds.c src/host_keymap.c) - target_include_directories(keyboard_pad_chord_test PRIVATE - include ${PSX_SDL_INCLUDE_DIRS}) - target_compile_definitions(keyboard_pad_chord_test PRIVATE - SDL_MAIN_HANDLED - $<$:PSX_SDL3=1>) - target_link_libraries(keyboard_pad_chord_test PRIVATE ${PSX_SDL_LIBRARIES}) - add_test(NAME keyboard_pad_chord_test COMMAND keyboard_pad_chord_test) - - add_executable(mouse_binding_suppression_test tests/test_mouse_binding_suppression.c) - target_include_directories(mouse_binding_suppression_test PRIVATE include ${PSX_SDL_INCLUDE_DIRS}) - target_compile_definitions(mouse_binding_suppression_test PRIVATE - SDL_MAIN_HANDLED $<$:PSX_SDL3=1>) - target_link_libraries(mouse_binding_suppression_test PRIVATE ${PSX_SDL_LIBRARIES}) - add_test(NAME mouse_binding_suppression_test COMMAND mouse_binding_suppression_test) - - # Generic external replacement-pack core. The renderer integration is a - # separate consumer; this target pins pack discovery, deterministic keying, - # VRAM residency, portable tracking state, and bounded asynchronous decode. - add_executable(hd_texture_pack_test - tests/test_hd_texture_pack.cpp - src/hd_texture_pack.cpp) - target_include_directories(hd_texture_pack_test PRIVATE include) - find_package(Threads REQUIRED) - target_link_libraries(hd_texture_pack_test PRIVATE Threads::Threads) - add_test(NAME hd_texture_pack_test COMMAND hd_texture_pack_test) - add_executable(duckstation_texture_pack_test - tests/test_duckstation_texture_pack.cpp - src/duckstation_texture_pack.cpp) - target_include_directories(duckstation_texture_pack_test PRIVATE include) - target_link_libraries(duckstation_texture_pack_test PRIVATE Threads::Threads) - target_link_libraries(duckstation_texture_pack_test PRIVATE psx_texture_image_decode) - add_test(NAME duckstation_texture_pack_test COMMAND duckstation_texture_pack_test) - add_executable(texture_image_decode_test tests/test_texture_image_decode.cpp) - target_link_libraries(texture_image_decode_test PRIVATE psx_texture_image_decode) - add_test(NAME texture_image_decode_test COMMAND texture_image_decode_test - "${CMAKE_CURRENT_SOURCE_DIR}/tests/fixtures/texture_images") - - # Standalone serial-pad protocol tests use explicit SIO/device stubs; they - # cover both DualShock rumble and JogCon wire/state behavior. - add_executable(sio_dualshock_rumble_test - tests/test_sio_dualshock_rumble.c - src/sio.c) - target_include_directories(sio_dualshock_rumble_test PRIVATE include) - add_test(NAME sio_dualshock_rumble_test COMMAND sio_dualshock_rumble_test) - add_executable(sio_jogcon_protocol_test - tests/test_sio_jogcon_protocol.c - src/sio.c) - target_include_directories(sio_jogcon_protocol_test PRIVATE include) - add_test(NAME sio_jogcon_protocol_test COMMAND sio_jogcon_protocol_test) - add_executable(sio_negcon_protocol_test - tests/test_sio_negcon_protocol.c - src/sio.c) - target_include_directories(sio_negcon_protocol_test PRIVATE include) - add_test(NAME sio_negcon_protocol_test COMMAND sio_negcon_protocol_test) - add_executable(sio_negcon_protocol_multitap_test - tests/test_sio_negcon_protocol.c - src/sio.c) - target_include_directories(sio_negcon_protocol_multitap_test PRIVATE include) - target_compile_definitions(sio_negcon_protocol_multitap_test PRIVATE - PSX_MAX_PLAYERS=5) - add_test(NAME sio_negcon_protocol_multitap_test - COMMAND sio_negcon_protocol_multitap_test) - # The shared PsxNetPad -> SIO apply and normalization, over the real SIO. - add_executable(psx_pad_apply_test - tests/test_psx_pad_apply.c - src/psx_pad_apply.c - src/psx_net_pad.c - src/sio.c) - target_include_directories(psx_pad_apply_test PRIVATE include) - add_test(NAME psx_pad_apply_test COMMAND psx_pad_apply_test) - - # Title-supplied host rumble (psx_mod_set_host_rumble) merge and expiry. - add_executable(host_rumble_test tests/test_host_rumble.c) - target_include_directories(host_rumble_test PRIVATE include) - add_test(NAME host_rumble_test COMMAND host_rumble_test) - - add_executable(controller_type_test tests/test_controller_type.cpp) - target_include_directories(controller_type_test PRIVATE include) - add_test(NAME controller_type_test COMMAND controller_type_test) - add_executable(psx_stick_test tests/test_psx_stick.cpp src/psx_stick.c) - target_include_directories(psx_stick_test PRIVATE include) - add_test(NAME psx_stick_test COMMAND psx_stick_test) - - add_executable(openxr_input_test tests/test_openxr_input.c src/psx_openxr.c) - target_include_directories(openxr_input_test PRIVATE include) - if(NOT MSVC) - target_link_libraries(openxr_input_test PRIVATE m) - endif() - add_test(NAME openxr_input_test COMMAND openxr_input_test) - - add_executable(vr_pose_math_test tests/test_vr_pose_math.c src/vr_pose_math.c) - target_include_directories(vr_pose_math_test PRIVATE include) - if(NOT MSVC) - target_link_libraries(vr_pose_math_test PRIVATE m) - endif() - add_test(NAME vr_pose_math_test COMMAND vr_pose_math_test) - - add_executable(projection_scale_config_test - tests/test_projection_scale_config.cpp src/projection_scale_config.cpp) - target_include_directories(projection_scale_config_test PRIVATE - include ../recompiler/lib/toml11) - add_test(NAME projection_scale_config_test COMMAND projection_scale_config_test) - - add_executable(mod_controller_source_test tests/test_mod_controller_source.c src/mod_controller_source.c) - target_include_directories(mod_controller_source_test PRIVATE include) - add_test(NAME mod_controller_source_test COMMAND mod_controller_source_test) - - add_executable(mod_pad_transform_test - tests/test_mod_pad_transform.c src/mod_pad_transform.c) - target_include_directories(mod_pad_transform_test PRIVATE include) - add_test(NAME mod_pad_transform_test COMMAND mod_pad_transform_test) - add_executable(psx_hotkey_pad_test tests/test_psx_hotkey_pad.c) - target_include_directories(psx_hotkey_pad_test PRIVATE include) - add_test(NAME psx_hotkey_pad_test COMMAND psx_hotkey_pad_test) - add_executable(psx_trigger_values_test tests/test_psx_trigger_values.c) - target_include_directories(psx_trigger_values_test PRIVATE include) - add_test(NAME psx_trigger_values_test COMMAND psx_trigger_values_test) - add_executable(pad_external_input_test - tests/test_pad_external_input.c src/pad_external_input.c) - target_include_directories(pad_external_input_test PRIVATE include src) - add_test(NAME pad_external_input_test COMMAND pad_external_input_test) - add_executable(depth24_present_hold_exec_test - tests/test_depth24_present_hold_exec.c src/ws_ui_group.c - src/psx_video_timing.c src/gpu_timeline.c) - target_include_directories(depth24_present_hold_exec_test PRIVATE include src) - if(MINGW) - target_compile_options(depth24_present_hold_exec_test PRIVATE -flto) - target_link_options(depth24_present_hold_exec_test PRIVATE -flto) - endif() - if(CMAKE_C_COMPILER_ID MATCHES "GNU|Clang") - target_compile_options(depth24_present_hold_exec_test PRIVATE -ffunction-sections -fdata-sections) - target_link_options(depth24_present_hold_exec_test PRIVATE -Wl,--gc-sections) - endif() - add_test(NAME depth24_present_hold_exec_test COMMAND depth24_present_hold_exec_test) - - add_executable(debug_socket_shutdown_test tests/test_debug_socket_shutdown.cpp) - target_include_directories(debug_socket_shutdown_test PRIVATE src) - find_package(Threads REQUIRED) - target_link_libraries(debug_socket_shutdown_test PRIVATE Threads::Threads) - if(WIN32) - target_link_libraries(debug_socket_shutdown_test PRIVATE ws2_32) - endif() - add_test(NAME debug_socket_shutdown_test COMMAND debug_socket_shutdown_test) - set_tests_properties(debug_socket_shutdown_test PROPERTIES TIMEOUT 15) - - add_executable(host_overlay_test tests/test_host_overlay.c src/host_overlay.c src/video_texture_filter.c) - target_include_directories(host_overlay_test PRIVATE include src) - find_package(Threads REQUIRED) - target_link_libraries(host_overlay_test PRIVATE Threads::Threads) - if(WIN32) - target_link_libraries(host_overlay_test PRIVATE shell32) - endif() - add_test(NAME host_overlay_test COMMAND host_overlay_test) - add_executable(psx_menu_host_test tests/test_psx_menu_host.c src/psx_menu_host.c) - target_include_directories(psx_menu_host_test PRIVATE include src) - add_test(NAME psx_menu_host_test COMMAND psx_menu_host_test) - - add_executable(gte_register_access_test - tests/test_gte_register_access.cpp - src/gte.cpp - src/pgxp.cpp - src/psx_ram_geometry.c - ) - target_include_directories(gte_register_access_test PRIVATE include) - target_compile_definitions(gte_register_access_test PRIVATE - PSX_NO_DEBUG_TOOLS=1 - PSX_GTE_REGISTER_TEST=1 - PSX_ENABLE_BLOCK_CYCLES=1) - add_test(NAME gte_register_access_test COMMAND gte_register_access_test) - # Same ABI/result corpus for the native operation entry and the reference. - add_executable(gte_hle_contract_test - tests/test_gte_register_access.cpp src/gte.cpp src/pgxp.cpp src/psx_ram_geometry.c) - target_include_directories(gte_hle_contract_test PRIVATE include) - target_compile_definitions(gte_hle_contract_test PRIVATE - PSX_NO_DEBUG_TOOLS=1 PSX_GTE_REGISTER_TEST=1 PSX_ENABLE_BLOCK_CYCLES=1) - psxrecomp_execution_profile(gte_hle_contract_test PROFILE ENHANCED) - add_test(NAME gte_hle_contract_test COMMAND gte_hle_contract_test) - - # PGXP value-propagation engine (docs/ENHANCEMENTS.md G1.2/G1.3): shadow - # load/store/COP2 roundtrips, half-word semantics, validate-on-read - # (foreign overwrite + DMA-overwrite rejection), MOVE/repack arithmetic, - # suppression bracket, invalidate-all, and GetPreciseVertex safeguards - # (truncation agreement + tolerance clamp). White-box: includes src/pgxp.cpp - # and stubs the gte.cpp fallback-cache extern. - add_executable(pgxp_test tests/test_pgxp.cpp src/pgxp.cpp - src/psx_ram_geometry.c) - target_include_directories(pgxp_test PRIVATE include) - add_test(NAME pgxp_test COMMAND pgxp_test) - - add_executable(pgxp_word_shadow_test tests/test_pgxp_word_shadow.cpp - src/pgxp.cpp src/psx_ram_geometry.c) - target_include_directories(pgxp_word_shadow_test PRIVATE include) - add_test(NAME pgxp_word_shadow_test COMMAND pgxp_word_shadow_test) - - # psx.enhancement.pgxp at session start (docs/ENHANCEMENTS.md G1.11): the - # real builtin activation (mod API stubbed) and the pgxp_session.h - # resolver main.cpp's renderer setup uses, in main.cpp's order. Pins that - # the [video] baseline no longer undoes the mod, that a cleared (netplay) - # plan keeps the baseline, and that the env overrides win. - # The session arming main.cpp runs (src/pgxp_session.cpp) against the - # real mod runtime, the real builtin plugin and the builtin's own - # manifest: default-on, netplay clear, the off switch, options, env - # overrides and [video] pgxp_mod_only. - # Run the same session fixture in both real compilation flavors. - foreach(_pgxp_session_test IN ITEMS pgxp_session_test pgxp_session_hooks_test) - add_executable(${_pgxp_session_test} tests/test_pgxp_session.cpp - src/pgxp_session.cpp src/mod_builtin_pgxp.c src/pgxp.cpp - src/psx_ram_geometry.c - src/mod_runtime.cpp - src/mod_packages.cpp - src/mod_media.cpp - src/disc_path.cpp - src/cue_sheet.cpp - src/iso_reader.cpp - src/crc32.c - src/psx_sha256.c) - target_include_directories(${_pgxp_session_test} PRIVATE - include - ../recompiler/include - ../recompiler/lib/toml11) - target_compile_definitions(${_pgxp_session_test} PRIVATE - PSX_BUILTIN_PGXP_MANIFEST="${CMAKE_CURRENT_SOURCE_DIR}/../mods/builtin/packages/psx.enhancement.pgxp/1.0.0/manifest.toml") - target_link_libraries(${_pgxp_session_test} PRIVATE chdr-static) - target_sources(${_pgxp_session_test} PRIVATE src/mod_netplay.c) - psxrecomp_sha256_file(${_pgxp_session_test}) - if(WIN32) - if(MSVC) - target_link_options(${_pgxp_session_test} PRIVATE /STACK:16777216) - else() - target_link_options(${_pgxp_session_test} PRIVATE -Wl,--stack,16777216) - endif() - endif() - add_test(NAME ${_pgxp_session_test} COMMAND ${_pgxp_session_test}) - if(_pgxp_session_test STREQUAL "pgxp_session_hooks_test") - target_compile_definitions(${_pgxp_session_test} PRIVATE PSX_PGXP=1) - endif() - endforeach() - - foreach(_pgxp_isolated_test - gte_register_access_test gte_hle_contract_test pgxp_test pgxp_session_test) - target_sources(${_pgxp_isolated_test} PRIVATE tests/pgxp_test_mod_memory.cpp) - endforeach() - - add_executable(psx_instr_cost_test tests/test_psx_instr_cost.cpp) - target_include_directories(psx_instr_cost_test PRIVATE include) - add_test(NAME psx_instr_cost_test COMMAND psx_instr_cost_test) - - add_executable(ws_aspect_cone_math_test - tests/test_ws_aspect_cone_math.c - src/ws_aspect_cone_math.c) - target_include_directories(ws_aspect_cone_math_test PRIVATE include) - if(NOT MSVC) - target_link_libraries(ws_aspect_cone_math_test PRIVATE m) - endif() - add_test(NAME ws_aspect_cone_math_test COMMAND ws_aspect_cone_math_test) - - add_executable(ws_ui_group_test - tests/test_ws_ui_group.c - src/ws_ui_group.c) - target_include_directories(ws_ui_group_test PRIVATE include) - add_test(NAME ws_ui_group_test COMMAND ws_ui_group_test) - - add_executable(ws_prepass_guard_test - tests/test_ws_prepass_guard.c) - target_include_directories(ws_prepass_guard_test PRIVATE include) - add_test(NAME ws_prepass_guard_test COMMAND ws_prepass_guard_test) - - add_executable(ws_hud_anchor_test - tests/test_ws_hud_anchor.c) - target_include_directories(ws_hud_anchor_test PRIVATE include) - add_test(NAME ws_hud_anchor_test COMMAND ws_hud_anchor_test) - - add_executable(gpu_textured_dot_nw_shift_exec_test - tests/test_gpu_textured_dot_nw_shift_exec.c - src/psx_video_timing.c - src/gpu_timeline.c) - target_include_directories(gpu_textured_dot_nw_shift_exec_test PRIVATE - include - src) - if(CMAKE_C_COMPILER_ID MATCHES "GNU|Clang") - target_compile_options(gpu_textured_dot_nw_shift_exec_test PRIVATE - -ffunction-sections -fdata-sections) - # Apple ld has no --gc-sections; -dead_strip is its equivalent (as - # for isc_store_test above), so the test links and runs on macOS. - target_link_options(gpu_textured_dot_nw_shift_exec_test PRIVATE - $,-Wl$-dead_strip,-Wl$--gc-sections>) - endif() - add_test(NAME gpu_textured_dot_nw_shift_exec_test - COMMAND gpu_textured_dot_nw_shift_exec_test) - - # auto_ui_squash corrects untextured HUD fills with their textured frames. - add_executable(ws_auto_ui_untextured_exec_test - tests/test_ws_auto_ui_untextured_exec.c - src/ws_ui_group.c - src/psx_video_timing.c - src/gpu_timeline.c) - target_include_directories(ws_auto_ui_untextured_exec_test PRIVATE - include - src) - if(CMAKE_C_COMPILER_ID MATCHES "GNU|Clang") - target_compile_options(ws_auto_ui_untextured_exec_test PRIVATE - -ffunction-sections -fdata-sections) - target_link_options(ws_auto_ui_untextured_exec_test PRIVATE - -Wl,--gc-sections) - endif() - add_test(NAME ws_auto_ui_untextured_exec_test - COMMAND ws_auto_ui_untextured_exec_test) - - add_executable(ws_shared_split_ui_exec_test - tests/test_ws_shared_split_ui_exec.c - src/ws_ui_group.c - src/psx_video_timing.c - src/gpu_timeline.c) - target_include_directories(ws_shared_split_ui_exec_test PRIVATE include src) - if(CMAKE_C_COMPILER_ID MATCHES "GNU|Clang") - target_compile_options(ws_shared_split_ui_exec_test PRIVATE - -ffunction-sections -fdata-sections) - target_link_options(ws_shared_split_ui_exec_test PRIVATE -Wl,--gc-sections) - endif() - add_test(NAME ws_shared_split_ui_exec_test COMMAND ws_shared_split_ui_exec_test) - - add_executable(ws_local_view_margin_test - tests/test_ws_local_view_margin.c - src/ws_ui_group.c - src/psx_video_timing.c - src/gpu_timeline.c) - target_include_directories(ws_local_view_margin_test PRIVATE include src) - if(CMAKE_C_COMPILER_ID MATCHES "GNU|Clang") - target_compile_options(ws_local_view_margin_test PRIVATE - -ffunction-sections -fdata-sections) - target_link_options(ws_local_view_margin_test PRIVATE - $,-Wl$-dead_strip,-Wl$--gc-sections>) - endif() - add_test(NAME ws_local_view_margin_test COMMAND ws_local_view_margin_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 - src/gpu_timeline.c) - add_executable(native_wide_projection_test tests/test_native_wide_projection.c - src/psx_video_timing.c - src/gpu_timeline.c) - 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) - if(NOT APPLE) - target_link_options(${_anchor_test}_test PRIVATE -Wl,--gc-sections) - endif() - endif() - if(NOT WIN32) - target_link_libraries(${_anchor_test}_test PRIVATE m) - endif() - endforeach() - 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) - add_test(NAME ws_repeat_rect_test COMMAND ws_repeat_rect_test) - add_executable(ws_screen_mask_test tests/test_ws_screen_mask.c) - target_include_directories(ws_screen_mask_test PRIVATE include) - if(UNIX) - target_link_libraries(ws_screen_mask_test PRIVATE m) - endif() - add_test(NAME ws_screen_mask_test COMMAND ws_screen_mask_test) - - add_executable(mod_gpu_dma_aperture_test - tests/test_mod_gpu_dma_aperture.c - src/psx_ram_geometry.c) - target_include_directories(mod_gpu_dma_aperture_test PRIVATE include) - add_test(NAME mod_gpu_dma_aperture_test - COMMAND mod_gpu_dma_aperture_test) - - # The real-handler fixture relies on LTO pruning its unused syscall paths. - # Enable only the toolchain on which that link contract is verified; keep - # the coverage gap visible in CTest for every other compiler/platform. - if(WIN32 AND MINGW AND CMAKE_SIZEOF_VOID_P EQUAL 8 - AND CMAKE_C_COMPILER_ID STREQUAL "GNU" - AND CMAKE_C_COMPILER_VERSION VERSION_EQUAL "16.1.0") - add_executable(exit_critical_section_test - tests/test_exit_critical_section.c src/traps.c) - target_include_directories(exit_critical_section_test PRIVATE include) - target_compile_definitions(exit_critical_section_test PRIVATE PSX_NO_DEBUG_TOOLS=1) - target_compile_options(exit_critical_section_test PRIVATE -O3 -flto -ffunction-sections) - target_link_options(exit_critical_section_test PRIVATE -flto -Wl,--gc-sections) - add_test(NAME exit_critical_section_test COMMAND exit_critical_section_test) - else() - add_test(NAME exit_critical_section_test - COMMAND "${CMAKE_COMMAND}" -E echo - "SKIP: real-handler LTO fixture is verified only on Windows x64 MinGW GCC 16.1.0; current compiler is ${CMAKE_C_COMPILER_ID} ${CMAKE_C_COMPILER_VERSION}") - set_tests_properties(exit_critical_section_test PROPERTIES - SKIP_REGULAR_EXPRESSION "^SKIP:") - endif() - - # #636 / PR #660: restore-vs-live IRQ delivery at the restore edge, over - # the real scheduler (traps.c) and IRQ delivery (interrupts.c). Compiled - # game, BIOS ROM, CPS continuation and interpreted resumes; no pending - # IRQ; I_MASK/SR gating; same-edge dedup; null-PC recovery. - add_executable(restore_edge_irq_replay_test - tests/test_restore_edge_irq_replay.c - tests/restore_edge_irq_stubs.c - src/traps.c - src/interrupts.c) - target_include_directories(restore_edge_irq_replay_test PRIVATE include src) - add_test(NAME restore_edge_irq_replay_test COMMAND restore_edge_irq_replay_test) - set_tests_properties(restore_edge_irq_replay_test PROPERTIES TIMEOUT 60) - - add_executable(dma_gpu_linked_list_timing_test - tests/test_dma_gpu_linked_list_timing.c - src/dma_gpu_ll.c) - target_include_directories(dma_gpu_linked_list_timing_test PRIVATE include) - add_test(NAME dma_gpu_linked_list_timing_test - COMMAND dma_gpu_linked_list_timing_test) - - # Clean-room SPU DSP fidelity (issue #103): SPU IRQ on every RAM access - # class, reverb (write gating + work-area wrap + CD-only path), noise - # generator, volume sweeps / direct decode, capture buffers, and - # savestate coverage of all the new state. Includes src/spu.c directly - # (white-box) and stubs its externs. - # src/crc32.c is linked rather than stubbed: spu.c's capture/shadow paths - # feed real CRCs that this test checks, so a zero-returning stub would make - # those assertions vacuous. (This target did not link at all before — - # crc32_update was undefined on origin/master too.) - add_executable(spu_fidelity_test tests/test_spu_fidelity.c src/crc32.c) - target_include_directories(spu_fidelity_test PRIVATE include) - add_test(NAME spu_fidelity_test COMMAND spu_fidelity_test) - # Same story as sio_dualshock_rumble_test: registered but unlinkable on - # origin/master (crc32_update undefined), so it never ran. With crc32 linked - # it now runs 137 checks and fails 4, all in one ADSR sweep region - # (mid > 0, late > mid, fall_mid < 0x7FFE, fall_late < fall_mid). Rebuilding - # it against a zero-returning crc32 stub reproduces exactly those 4, so the - # crc32 linkage is not the cause — the envelope sweep genuinely disagrees. - set_tests_properties(spu_fidelity_test PROPERTIES DISABLED TRUE) - - add_executable(overlay_capture_retry_test - tests/test_overlay_capture_retry.cpp - src/overlay_capture.c - src/psx_ram_geometry.c) - target_include_directories(overlay_capture_retry_test PRIVATE - include ${PSX_SDL_INCLUDE_DIRS}) - target_compile_definitions(overlay_capture_retry_test PRIVATE - SDL_MAIN_HANDLED PSX_OVERLAY_CAPTURE_TEST=1 - $<$:PSX_SDL3=1>) - target_link_libraries(overlay_capture_retry_test PRIVATE ${PSX_SDL_LIBRARIES}) - add_test(NAME overlay_capture_retry_test COMMAND overlay_capture_retry_test) - - add_executable(overlay_path_canon_test - tests/test_overlay_path_canon.c - src/overlay_path_canon.c) - target_include_directories(overlay_path_canon_test PRIVATE include) - add_test(NAME overlay_path_canon_test COMMAND overlay_path_canon_test) - - # The kernel-bless verifier must accept a body whose DECLARED patch range - # the guest has overwritten (Psy-Q's install stubs land inside compiled - # kernel bodies) and still reject one changed anywhere else. Before this, - # the whole-body memcmp failed forever on the first install and the BIOS - # exception handler interpreted for the life of the process. - add_executable(kernel_patch_ranges_test - tests/test_kernel_patch_ranges.c - src/kernel_patch_ranges.c) - target_include_directories(kernel_patch_ranges_test PRIVATE include) - add_test(NAME kernel_patch_ranges_test COMMAND kernel_patch_ranges_test) - - # The player-facing "skip the BIOS" flag must resolve to the same boot - # behaviour on retail SCPH-1001 and on the bundled OpenBIOS, whose - # kernel-call HLE is (correctly) refused for want of a DeliverEvent anchor. - add_executable(bios_hle_plan_test - tests/test_bios_hle_plan.c - src/bios_hle_plan.c) - target_include_directories(bios_hle_plan_test PRIVATE include) - add_test(NAME bios_hle_plan_test COMMAND bios_hle_plan_test) - - # A cue, conventional raw image, or Steam .car image is a valid pick; each - # must mount the same disc and identify off the same data track. - add_executable(sbi_setup_test tests/test_sbi_setup.cpp src/psx_sha256.c) - target_include_directories(sbi_setup_test PRIVATE include) - add_test(NAME sbi_setup_test COMMAND sbi_setup_test ${CMAKE_CURRENT_BINARY_DIR}/sbi-setup-fixture) - - add_executable(disc_path_resolve_test - tests/test_disc_path_resolve.cpp - src/disc_path.cpp - src/cue_sheet.cpp - src/iso_reader.cpp) - target_include_directories(disc_path_resolve_test PRIVATE include ${PSXRECOMP_ROOT}/recompiler/include) - target_link_libraries(disc_path_resolve_test PRIVATE chdr-static) - add_test(NAME disc_path_resolve_test COMMAND disc_path_resolve_test) - - # Both BIOS filename conventions (bare vs region-qualified) resolve, in - # both directions, so a config and a dump folder may disagree. - add_executable(bios_rom_alias_test tests/test_bios_rom_alias.cpp) - target_include_directories(bios_rom_alias_test PRIVATE - ${PSXRECOMP_ROOT}/recompiler/include) - add_test(NAME bios_rom_alias_test COMMAND bios_rom_alias_test) - - # The game window opens at a normal size, not maximised. - add_executable(window_size_test tests/test_window_size.cpp) - target_include_directories(window_size_test PRIVATE include) - add_test(NAME window_size_test COMMAND window_size_test) - option(PSX_WINDOW_FULLSCREEN_TEST "Run fullscreen transitions on a real desktop" OFF) - if(PSX_WINDOW_FULLSCREEN_TEST) - add_executable(window_fullscreen_test tests/test_window_fullscreen.cpp) - target_include_directories(window_fullscreen_test PRIVATE include) - target_compile_definitions(window_fullscreen_test PRIVATE - SDL_MAIN_HANDLED=1 $<$:PSX_SDL3=1>) - target_link_libraries(window_fullscreen_test PRIVATE ${PSX_SDL_LIBRARIES}) - add_test(NAME window_fullscreen_test COMMAND window_fullscreen_test) - set_tests_properties(window_fullscreen_test PROPERTIES LABELS "window;hardware" TIMEOUT 30) - endif() - - # Multi-file cue TOC mapping (data track + CD-DA in separate bins). - add_executable(iso_reader_cdda_test - tests/test_iso_reader_cdda.cpp - src/iso_reader.cpp - src/cue_sheet.cpp) - target_include_directories(iso_reader_cdda_test PRIVATE include ${PSXRECOMP_ROOT}/recompiler/include) - target_link_libraries(iso_reader_cdda_test PRIVATE chdr-static) - add_test(NAME iso_reader_cdda_test COMMAND iso_reader_cdda_test) - - # Cue PREGAP/POSTGAP (gaps the dump does not store) shift the disc layout. - add_executable(iso_reader_pregap_test - tests/test_iso_reader_pregap.cpp - src/iso_reader.cpp - src/cue_sheet.cpp) - target_include_directories(iso_reader_pregap_test PRIVATE include ${PSXRECOMP_ROOT}/recompiler/include) - target_link_libraries(iso_reader_pregap_test PRIVATE chdr-static) - add_test(NAME iso_reader_pregap_test COMMAND iso_reader_pregap_test) - - # Raw vs cooked geometry comes from the cue TRACK mode / sector-0 sync, - # never from file length. - add_executable(iso_reader_geometry_test - tests/test_iso_reader_geometry.cpp - src/iso_reader.cpp - src/cue_sheet.cpp) - target_include_directories(iso_reader_geometry_test PRIVATE include ${PSXRECOMP_ROOT}/recompiler/include) - target_link_libraries(iso_reader_geometry_test PRIVATE chdr-static) - add_test(NAME iso_reader_geometry_test COMMAND iso_reader_geometry_test) - - add_executable(iso_reader_sbi_test - tests/test_iso_reader_sbi.cpp - src/iso_reader.cpp - src/cue_sheet.cpp) - target_include_directories(iso_reader_sbi_test PRIVATE include ${PSXRECOMP_ROOT}/recompiler/include) - target_link_libraries(iso_reader_sbi_test PRIVATE chdr-static) - add_test(NAME iso_reader_sbi_test COMMAND iso_reader_sbi_test) - - add_executable(mod_packages_test - tests/test_mod_packages.cpp - src/mod_packages.cpp - src/mod_media.cpp src/iso_reader.cpp src/cue_sheet.cpp - src/crc32.c - src/psx_sha256.c) - target_include_directories(mod_packages_test PRIVATE - include - ../recompiler/include - ../recompiler/lib/toml11) - target_link_libraries(mod_packages_test PRIVATE chdr-static) - add_test(NAME mod_packages_test COMMAND mod_packages_test) - - add_executable(builtin_mod_manifests_test - tests/test_builtin_mod_manifests.cpp - src/mod_packages.cpp - src/mod_media.cpp src/iso_reader.cpp src/cue_sheet.cpp - src/crc32.c - src/psx_sha256.c) - target_include_directories(builtin_mod_manifests_test PRIVATE - include - ../recompiler/include - ../recompiler/lib/toml11) - target_link_libraries(builtin_mod_manifests_test PRIVATE chdr-static) - add_test(NAME builtin_mod_manifests_test - COMMAND builtin_mod_manifests_test - "${CMAKE_CURRENT_SOURCE_DIR}/../mods/builtin/packages") - - add_executable(mod_media_test - tests/test_mod_media.cpp src/mod_media.cpp src/iso_reader.cpp - src/cue_sheet.cpp src/psx_sha256.c) - target_include_directories(mod_media_test PRIVATE include ../recompiler/include) - target_link_libraries(mod_media_test PRIVATE chdr-static) - add_test(NAME mod_media_test COMMAND mod_media_test) - - add_executable(text_xlate_fix_only_test - tests/test_text_xlate_fix_only.cpp - src/text_xlate.cpp - src/psx_ram_geometry.c) - target_include_directories(text_xlate_fix_only_test PRIVATE - include - ../recompiler/lib/toml11) - add_test(NAME text_xlate_fix_only_test COMMAND text_xlate_fix_only_test) - - add_executable(netplay_peer_frontier_test tests/test_netplay_peer_frontier.c) - target_include_directories(netplay_peer_frontier_test PRIVATE include) - add_test(NAME netplay_peer_frontier_test COMMAND netplay_peer_frontier_test) - - add_executable(netplay_content_gate_test tests/test_netplay_content_gate.c) - target_include_directories(netplay_content_gate_test PRIVATE include) - add_test(NAME netplay_content_gate_test COMMAND netplay_content_gate_test) - - add_executable(mod_runtime_test - tests/test_mod_runtime.cpp - src/iso_reader_c.cpp - src/mod_runtime.cpp - src/mod_netplay.c - src/mod_packages.cpp - src/mod_media.cpp - src/disc_path.cpp - src/cue_sheet.cpp - src/iso_reader.cpp - src/crc32.c - src/psx_sha256.c) - target_include_directories(mod_runtime_test PRIVATE - include - ../recompiler/include - ../recompiler/lib/toml11) - target_link_libraries(mod_runtime_test PRIVATE chdr-static) - psxrecomp_sha256_file(mod_runtime_test) - # Match the runtime's stack allowance for the remaining disc/mod helpers. - if(WIN32) - if(MSVC) - target_link_options(mod_runtime_test PRIVATE /STACK:16777216) - else() - target_link_options(mod_runtime_test PRIVATE -Wl,--stack,16777216) - endif() - endif() - add_test(NAME mod_runtime_test COMMAND mod_runtime_test) - add_test(NAME mod_netplay_missing_plugin_test COMMAND mod_runtime_test --missing-profile) - add_test(NAME mod_netplay_missing_renderer_test COMMAND mod_runtime_test --missing-renderer) - add_test(NAME mod_netplay_profile_options_test COMMAND mod_runtime_test --profile-options) - add_test(NAME mod_netplay_option_renderer_missing_test COMMAND mod_runtime_test --option-renderer-missing) - - add_executable(neversoft_helpers_test tests/test_neversoft_helpers.cpp) - target_include_directories(neversoft_helpers_test PRIVATE include) - target_compile_features(neversoft_helpers_test PRIVATE cxx_std_17) - add_test(NAME neversoft_helpers_test COMMAND neversoft_helpers_test) - - add_executable(aligned_archive_test tests/test_aligned_archive.cpp) - target_include_directories(aligned_archive_test PRIVATE include) - target_compile_features(aligned_archive_test PRIVATE cxx_std_17) - add_test(NAME aligned_archive_test COMMAND aligned_archive_test) - - add_executable(resident_sector_test tests/test_resident_sector.cpp) - target_include_directories(resident_sector_test PRIVATE include) - target_compile_features(resident_sector_test PRIVATE cxx_std_17) - add_test(NAME resident_sector_test COMMAND resident_sector_test) - add_executable(netplay_view_mods_test - tests/test_netplay_view_mods.cpp - src/iso_reader_c.cpp - src/mod_runtime.cpp - src/mod_netplay.c - src/mod_packages.cpp - src/mod_media.cpp - src/disc_path.cpp - src/cue_sheet.cpp - src/iso_reader.cpp - src/crc32.c - src/psx_sha256.c - src/sio.c) - target_include_directories(netplay_view_mods_test PRIVATE - include - ../recompiler/include - ../recompiler/lib/toml11) - target_link_libraries(netplay_view_mods_test PRIVATE chdr-static) - psxrecomp_sha256_file(netplay_view_mods_test) - if(WIN32) - if(MSVC) - target_link_options(netplay_view_mods_test PRIVATE /STACK:16777216) - else() - target_link_options(netplay_view_mods_test PRIVATE -Wl,--stack,16777216) - endif() - endif() - add_test(NAME netplay_view_mods_test COMMAND netplay_view_mods_test) - get_target_property(mod_runtime_sources mod_runtime_test SOURCES) - add_executable(mod_runtime_cache_test ${mod_runtime_sources}) - target_include_directories(mod_runtime_cache_test PRIVATE - include ../recompiler/include ../recompiler/lib/toml11) - target_compile_definitions(mod_runtime_cache_test PRIVATE - PSX_LAUNCHER_MOD_COMMIT_WORKER_SAFE=1) - target_link_libraries(mod_runtime_cache_test PRIVATE chdr-static) - psxrecomp_sha256_file(mod_runtime_cache_test) - if(WIN32) - if(MSVC) - target_link_options(mod_runtime_cache_test PRIVATE /STACK:16777216) - else() - target_link_options(mod_runtime_cache_test PRIVATE -Wl,--stack,16777216) - endif() - endif() - add_test(NAME mod_runtime_cache_test COMMAND mod_runtime_cache_test) - - add_executable(mod_resident_test - tests/test_mod_resident.cpp - src/mod_resident.cpp - src/mod_runtime.cpp - src/mod_netplay.c - src/mod_packages.cpp - src/mod_media.cpp - src/disc_path.cpp - src/cue_sheet.cpp - src/iso_reader.cpp - src/crc32.c - src/psx_sha256.c) - target_include_directories(mod_resident_test PRIVATE - include - ../recompiler/include - ../recompiler/lib/toml11) - target_link_libraries(mod_resident_test PRIVATE chdr-static) - psxrecomp_sha256_file(mod_resident_test) - if(WIN32) - if(MSVC) - target_link_options(mod_resident_test PRIVATE /STACK:16777216) - else() - target_link_options(mod_resident_test PRIVATE -Wl,--stack,16777216) - endif() - endif() - add_test(NAME mod_resident_test COMMAND mod_resident_test) - - if(WIN32) - add_executable(autocompile_publication_test - tests/test_autocompile_publication.c - src/autocompile.c) - target_include_directories(autocompile_publication_test PRIVATE include) - target_compile_definitions(autocompile_publication_test PRIVATE - PSX_AUTOCOMPILE_TEST=1) - add_test(NAME autocompile_publication_test - COMMAND autocompile_publication_test) - - # A misconfigured overlay_autocompile_cmd must be reported on a channel - # that exists: the stdout warnings reach nobody under -mwindows, so a - # broken autocompile silently degrades the whole run to the interpreter. - add_executable(autocompile_degraded_test - tests/test_autocompile_degraded.c - src/autocompile.c) - target_include_directories(autocompile_degraded_test PRIVATE include) - add_test(NAME autocompile_degraded_test - COMMAND autocompile_degraded_test) - endif() - - # ---- previously-unregistered standalone tests ------------------------- - # These 16 existed in runtime/tests/ with no add_test() entry. Their only - # build instructions were a gcc command line in each file's header comment, - # which nothing executes — so when the runtime API moved under them they - # broke silently and stayed broken. Four had already rotted when they were - # collected here (2026-08-11): - # - # test_netplay_snap_ring.c duplicate TU in the documented command; - # no stubs for the VRAM-incremental - # boot_state queries (PR #134) - # test_psx_cycle_event_boundaries.c - # psx_cycles_resync_after_restore() grew a - # CPUState* parameter; psx_cycles.c grew - # three externs the stub block never did - # test_spu_end_without_repeat.c spu.c grew psx_irq_raise / crc32_update - # test_sio_card_protocol.c repaired below: the harness now walks - # shift/ACK edges and clears INTC IRQ7 - # - # Registering them is only half the fix; check_test_registration.cmake at - # the end of this file is what stops the next one from going unnoticed. - # Each test's build recipe now lives HERE and nowhere else — the header - # comments were reduced to a `ctest -R ` pointer so there is exactly - # one description of how a test is built, and it is one the build executes. - - add_executable(cdrom_irq_mask_test tests/test_cdrom_irq_mask.c) - target_include_directories(cdrom_irq_mask_test PRIVATE src) - add_test(NAME cdrom_irq_mask_test COMMAND cdrom_irq_mask_test) - - add_executable(cdrom_sector_fifo_test tests/test_cdrom_sector_fifo.c) - target_include_directories(cdrom_sector_fifo_test PRIVATE include src) - add_test(NAME cdrom_sector_fifo_test COMMAND cdrom_sector_fifo_test) - - add_executable(cdrom_lid_state_test tests/test_cdrom_lid_state.c) - target_include_directories(cdrom_lid_state_test PRIVATE src) - add_test(NAME cdrom_lid_state_test COMMAND cdrom_lid_state_test) - - # Cross-device I_STAT ownership: a memory-card ACK IRQ (SIO0) must not - # clear another device's pending source — pinned on SPU (I_STAT bit 9) - # staying set across card ACK delivery until the guest acknowledges it. - # Scratch working dir for the same reason as sio_card_protocol_test: the - # test self-provisions test_dir/card1.mcd relative to its cwd. - add_executable(sio_spu_istat_ownership_test - tests/test_sio_spu_istat_ownership.c - src/sio.c - src/memcard.c) - target_include_directories(sio_spu_istat_ownership_test PRIVATE include src) - file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/sio_spu_istat_scratch) - add_test(NAME sio_spu_istat_ownership_test COMMAND sio_spu_istat_ownership_test - WORKING_DIRECTORY - ${CMAKE_CURRENT_BINARY_DIR}/sio_spu_istat_scratch) - - # Mirrors func_00005A08 (BIOS card READ chain handler) against a mock CPU, - # so it pins the disassembly reading without linking the recompiled BIOS. - add_executable(chain_handler_check_test tests/test_chain_handler_check.c) - target_include_directories(chain_handler_check_test PRIVATE include src) - add_test(NAME chain_handler_check_test COMMAND chain_handler_check_test) - - # Bug B: the pacing decision must take ONE counter sample, so the unsigned - # subtraction cannot underflow into a ~24.7-day SDL_Delay. - add_executable(frame_pacing_test tests/test_frame_pacing.c) - target_include_directories(frame_pacing_test PRIVATE include) - add_test(NAME frame_pacing_test COMMAND frame_pacing_test) - # Render thread core ([video] render_thread, docs/RENDER_THREAD.md): - # ordering across ring wraps, the frame bound, stale-frame detection and - # the drain + context hand-off sync point, against a fake executor. - # The fixture itself uses POSIX threads and nanosleep (MinGW has both; - # the core takes its Win32 path there). - if(NOT MSVC) - add_executable(render_thread_test tests/test_render_thread.c src/render_thread.c) - target_include_directories(render_thread_test PRIVATE include) - find_package(Threads REQUIRED) - target_link_libraries(render_thread_test PRIVATE Threads::Threads) - add_test(NAME render_thread_test COMMAND render_thread_test) - # Present thread core ([video] present_thread): submission order is - # presentation order, slots are never composed while queued or on - # screen, done tokens reach the next composer, stop drains. - add_executable(present_thread_test tests/test_present_thread.c src/present_thread.c) - target_include_directories(present_thread_test PRIVATE include) - target_link_libraries(present_thread_test PRIVATE Threads::Threads) - add_test(NAME present_thread_test COMMAND present_thread_test) - endif() - - # Same family of bug as frame_pacing: the starvation watchdog's staleness - # gap must not wrap when the (cross-thread) heartbeat is newer than the - # clock sample taken by the emu thread. - add_executable(starvation_watchdog_test tests/test_starvation_watchdog.c) - target_include_directories(starvation_watchdog_test PRIVATE include) - add_test(NAME starvation_watchdog_test COMMAND starvation_watchdog_test) - - add_executable(freeze_dump_policy_test - tests/test_freeze_dump_policy.c - src/freeze_dump_policy.c) - target_include_directories(freeze_dump_policy_test PRIVATE src) - add_test(NAME freeze_dump_policy_test COMMAND freeze_dump_policy_test) - - add_executable(frame_interpolation_schedule_test - tests/test_frame_interpolation_schedule.c - src/frame_interpolation.c) - target_include_directories(frame_interpolation_schedule_test PRIVATE include) - if(NOT MSVC) - target_link_libraries(frame_interpolation_schedule_test PRIVATE m) - endif() - add_test(NAME frame_interpolation_schedule_test - COMMAND frame_interpolation_schedule_test) - - add_executable(render_pass_plan_test - tests/test_render_pass_plan.c - src/render_pass_plan.c) - target_include_directories(render_pass_plan_test PRIVATE include) - if(NOT MSVC) - target_link_libraries(render_pass_plan_test PRIVATE m) - endif() - add_test(NAME render_pass_plan_test COMMAND render_pass_plan_test) - - add_executable(mod_internal_resolution_test - tests/test_mod_internal_resolution.c - src/mod_internal_resolution.c) - target_include_directories(mod_internal_resolution_test PRIVATE include) - add_test(NAME mod_internal_resolution_test COMMAND mod_internal_resolution_test) - - # Motion kit and per-frame driver for render-pass plugins: blending, - # identity matching, limits and the crossfade fallback, on mock RAM. - add_executable(render_pass_motion_test - tests/test_render_pass_motion.c - src/render_pass_motion.c - src/render_pass_frame.c) - target_include_directories(render_pass_motion_test PRIVATE include) - if(NOT MSVC) - target_link_libraries(render_pass_motion_test PRIVATE m) - endif() - add_test(NAME render_pass_motion_test COMMAND render_pass_motion_test) - add_executable(render_pass_projection_test - tests/test_render_pass_projection.cpp - src/render_pass_projection.cpp - src/render_pass_motion.c) - target_include_directories(render_pass_projection_test PRIVATE include) - if(NOT MSVC) - target_link_libraries(render_pass_projection_test PRIVATE m) - endif() - add_test(NAME render_pass_projection_test COMMAND render_pass_projection_test) - # Dynamic resolution's controller against synthetic load: step rules, - # hysteresis, transient spikes, CPU-bound stretches, holds, the floor. - add_executable(dynamic_resolution_test - tests/test_dynamic_resolution.c - src/dynamic_resolution.c) - target_include_directories(dynamic_resolution_test PRIVATE include) - if(NOT MSVC) - target_link_libraries(dynamic_resolution_test PRIVATE m) - endif() - add_test(NAME dynamic_resolution_test COMMAND dynamic_resolution_test) - # The render-thread controller (dynrt_*) against a synthetic pipeline. - add_executable(dynamic_resolution_rt_test - tests/test_dynamic_resolution_rt.c - src/dynamic_resolution.c) - target_include_directories(dynamic_resolution_rt_test PRIVATE include) - if(NOT MSVC) - target_link_libraries(dynamic_resolution_rt_test PRIVATE m) - endif() - add_test(NAME dynamic_resolution_rt_test COMMAND dynamic_resolution_rt_test) - # Frame generation's matching, interpolation, plan and breaker. - add_executable(frame_gen_test tests/test_frame_gen.c src/frame_gen.c) - target_include_directories(frame_gen_test PRIVATE include) - if(NOT MSVC) - target_link_libraries(frame_gen_test PRIVATE m) - endif() - add_test(NAME frame_gen_test COMMAND frame_gen_test) - - # Post-effect chain presets (postfx_chain.c alone; no GL). - add_executable(postfx_chain_test tests/test_postfx_chain.c src/postfx_chain.c) - target_include_directories(postfx_chain_test PRIVATE include) - add_test(NAME postfx_chain_test COMMAND postfx_chain_test - WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}") - - # Render-pass time freeze against mock devices (psx_cycles.c only). - add_executable(render_pass_freeze_test - tests/test_render_pass_freeze.c - src/psx_cycles.c) - target_include_directories(render_pass_freeze_test PRIVATE include) - add_test(NAME render_pass_freeze_test COMMAND render_pass_freeze_test) - - # Uncharged guest-time spans (psx_mod_call_guest_uncharged's time layer) - # against the same mock devices. - add_executable(uncharged_span_test - tests/test_uncharged_span.c - src/psx_cycles.c) - target_include_directories(uncharged_span_test PRIVATE include) - add_test(NAME uncharged_span_test COMMAND uncharged_span_test) - - # Render-pass watchdog abort from nested guest frames: the real - # render_pass.c and psx_cycles.c against mock memory, devices and - # presenter (the frames use GCC/Clang cleanup handlers, as generated - # code does). - if(NOT MSVC) - add_executable(render_pass_abort_test - tests/test_render_pass_abort.c - src/render_pass.c - src/psx_cycles.c) - target_include_directories(render_pass_abort_test PRIVATE include) - add_test(NAME render_pass_abort_test COMMAND render_pass_abort_test) - endif() - - # Render-pass sandbox: the store policy memory.c uses, a real pass - # (render_pass.c, psx_cycles.c, timers.c) that makes device stores and - # runs frozen cycles, and the out-of-rect VRAM journal. - add_executable(render_pass_sandbox_test - tests/test_render_pass_sandbox.c - src/render_pass.c - src/render_pass_plan.c - src/psx_cycles.c - src/timers.c - src/psx_video_timing.c) - target_include_directories(render_pass_sandbox_test PRIVATE include) - if(NOT MSVC) - target_link_libraries(render_pass_sandbox_test PRIVATE m) - endif() - add_test(NAME render_pass_sandbox_test COMMAND render_pass_sandbox_test) - - add_executable(render_pass_ot_test tests/test_render_pass_ot.cpp) - target_include_directories(render_pass_ot_test PRIVATE include) - target_compile_features(render_pass_ot_test PRIVATE cxx_std_17) - add_test(NAME render_pass_ot_test COMMAND render_pass_ot_test) - - foreach(_submit_profile ENHANCED REFERENCE) - string(TOLOWER "${_submit_profile}" _submit_suffix) - set(_submit_test "render_pass_gpu_submit_${_submit_suffix}_test") - add_executable(${_submit_test} ../tests/test_render_pass_gpu_submit.cpp src/dma_gpu_ll.c) - target_include_directories(${_submit_test} PRIVATE include) - psxrecomp_execution_profile(${_submit_test} PROFILE ${_submit_profile}) - add_test(NAME ${_submit_test} COMMAND ${_submit_test}) - endforeach() - - add_executable(gpu_primitive_reject_test tests/test_gpu_primitive_reject.c) - target_include_directories(gpu_primitive_reject_test PRIVATE include) - add_test(NAME gpu_primitive_reject_test COMMAND gpu_primitive_reject_test) - - add_executable(gpu_sw_edges_test tests/test_gpu_sw_edges.c) - target_include_directories(gpu_sw_edges_test PRIVATE src) - add_test(NAME gpu_sw_edges_test COMMAND gpu_sw_edges_test) - - add_executable(gpu_uv_test tests/test_gpu_uv.c) - target_include_directories(gpu_uv_test PRIVATE src) - add_test(NAME gpu_uv_test COMMAND gpu_uv_test) - - # Netplay CPU-authoritative VRAM ignores PGXP overrides and texture - # filtering (sw_set_faithful_authority). - add_executable(sw_faithful_authority_test tests/test_sw_faithful_authority.c) - target_include_directories(sw_faithful_authority_test PRIVATE include src) - if(NOT WIN32) - target_link_libraries(sw_faithful_authority_test PRIVATE m) - endif() - add_test(NAME sw_faithful_authority_test COMMAND sw_faithful_authority_test) - - add_executable(gpu_vk_upload_alignment_test - tests/test_gpu_vk_upload_alignment.c) - target_include_directories(gpu_vk_upload_alignment_test PRIVATE src) - add_test(NAME gpu_vk_upload_alignment_test - COMMAND gpu_vk_upload_alignment_test) - - # Portable rollback helpers — required for MotK netplay unit tests that use - # the netplay_* compatibility shims. The peer-facing ones (hash_confirm, - # input_hist, rb_post) live in recomp-net; the snap ring in retcomm-rbengine. - set(_RBE_ROOT "${RECOMP_RBENGINE_ROOT}") - if(NOT _RBE_ROOT) - set(_RBE_ROOT "${PSXRECOMP_ROOT}/lib/retcomm-rbengine") - endif() - set(_RNET_ROOT "${RECOMP_NET_ROOT}") - if(NOT _RNET_ROOT) - set(_RNET_ROOT "${PSXRECOMP_ROOT}/lib/recomp-net") - endif() - if(EXISTS "${_RBE_ROOT}/CMakeLists.txt" AND EXISTS "${_RNET_ROOT}/CMakeLists.txt") - if(NOT TARGET recomp_net) - set(RNET_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE) - set(RNET_BUILD_TESTS OFF CACHE BOOL "" FORCE) - set(RNET_ENABLE_ICE OFF CACHE BOOL "" FORCE) - add_subdirectory("${_RNET_ROOT}" "${CMAKE_BINARY_DIR}/recomp-net-tests") - endif() - if(NOT TARGET retcomm_rbengine) - set(RBE_BUILD_TESTS OFF CACHE BOOL "" FORCE) - set(RECOMP_NET_ROOT "${_RNET_ROOT}" CACHE PATH "" FORCE) - add_subdirectory("${_RBE_ROOT}" "${CMAKE_BINARY_DIR}/retcomm-rbengine-tests") - endif() - set(PSXRECOMP_TEST_HAS_RBENGINE TRUE) - else() - set(PSXRECOMP_TEST_HAS_RBENGINE FALSE) - message(STATUS - "netplay_*_tests: lib/retcomm-rbengine or lib/recomp-net absent - skipped") - endif() - - add_executable(netplay_load_probe_test - tests/test_netplay_load_probe.c) - target_include_directories(netplay_load_probe_test PRIVATE include) - add_test(NAME netplay_load_probe_test COMMAND netplay_load_probe_test) - - if(PSXRECOMP_TEST_HAS_RBENGINE) - add_executable(netplay_hash_confirm_test - tests/test_netplay_hash_confirm.c) - target_include_directories(netplay_hash_confirm_test PRIVATE include) - target_link_libraries(netplay_hash_confirm_test PRIVATE recomp_net) - add_test(NAME netplay_hash_confirm_test COMMAND netplay_hash_confirm_test) - - # save/load glue #included from the test; ring ops come from rbengine. - add_executable(netplay_snap_ring_test tests/test_netplay_snap_ring.c) - target_include_directories(netplay_snap_ring_test PRIVATE include) - target_compile_definitions(netplay_snap_ring_test PRIVATE - NETPLAY_SNAP_RING_TEST_STUB_BOOT_STATE - PSX_HAS_RECOMP_NET=1) - target_link_libraries(netplay_snap_ring_test PRIVATE retcomm_rbengine) - add_test(NAME netplay_snap_ring_test COMMAND netplay_snap_ring_test) - - # Lobby chat ring. #includes psx_lobby_client.c, so it needs the same - # guard define and include roots the runtime build uses; the two WS - # calls that TU makes are stubbed in the test and abort if reached. - if(EXISTS "${PSXRECOMP_ROOT}/runtime/src/lobby_ws/rnet_ws.c") - add_executable(lobby_chat_ring_test tests/test_lobby_chat_ring.c - src/mod_netplay.c src/psx_sha256.c) - target_include_directories(lobby_chat_ring_test PRIVATE - include - src - "${PSXRECOMP_ROOT}/runtime/src/lobby_ws" - ${_RNET_ROOT}/include) - target_compile_definitions(lobby_chat_ring_test PRIVATE - PSX_HAS_LOBBY_CLIENT=1 - PSX_HAS_RECOMP_NET=1) - # Links the REAL recomp_net and the real WS helpers rather than a - # wall of stubs. Nothing in this test reaches a socket, so no fake - # is needed and none can drift from what the runtime links. - target_sources(lobby_chat_ring_test PRIVATE - "${PSXRECOMP_ROOT}/runtime/src/lobby_ws/rnet_ws.c" - "${PSXRECOMP_ROOT}/runtime/src/lobby_ws/rnet_sha1.c" - "${PSXRECOMP_ROOT}/runtime/src/netplay_bios_settle.c") - target_link_libraries(lobby_chat_ring_test PRIVATE recomp_net) - add_test(NAME lobby_chat_ring_test COMMAND lobby_chat_ring_test) - add_executable(netplay_bios_wire_test - tests/test_netplay_bios_wire.c - src/mod_netplay.c - src/psx_sha256.c - src/netplay_bios_settle.c - src/lobby_ws/rnet_ws.c - src/lobby_ws/rnet_sha1.c) - target_include_directories(netplay_bios_wire_test PRIVATE - include src src/lobby_ws ${_RNET_ROOT}/include) - target_compile_definitions(netplay_bios_wire_test PRIVATE - PSX_HAS_LOBBY_CLIENT=1 PSX_HAS_RECOMP_NET=1) - target_link_libraries(netplay_bios_wire_test PRIVATE recomp_net) - add_test(NAME netplay_bios_wire_test COMMAND netplay_bios_wire_test) - endif() - - add_executable(rb_post_tip_filter_test tests/test_rb_post_tip_filter.c) - target_include_directories(rb_post_tip_filter_test PRIVATE include) - target_link_libraries(rb_post_tip_filter_test PRIVATE recomp_net) - add_test(NAME rb_post_tip_filter_test COMMAND rb_post_tip_filter_test) - - add_executable(netplay_input_hist_test - tests/test_netplay_input_hist.c - src/netplay_input_hist.c - src/psx_net_pad.c) - target_include_directories(netplay_input_hist_test PRIVATE - include - ${_RNET_ROOT}/include) - target_compile_definitions(netplay_input_hist_test PRIVATE - PSX_HAS_RECOMP_NET=1) - target_link_libraries(netplay_input_hist_test PRIVATE recomp_net) - add_test(NAME netplay_input_hist_test COMMAND netplay_input_hist_test) - endif() - - add_executable(overlay_store_cycle_barrier_test - tests/test_overlay_store_cycle_barrier.c) - target_include_directories(overlay_store_cycle_barrier_test PRIVATE include) - add_test(NAME overlay_store_cycle_barrier_test - COMMAND overlay_store_cycle_barrier_test) - - # Overlay stores update the host's store-PC breadcrumb (ABI v24). Built - # with the defines compile_overlays.py gives every shard. - add_executable(overlay_store_pc_forwarding_test - tests/test_overlay_store_pc_forwarding.c) - target_include_directories(overlay_store_pc_forwarding_test PRIVATE include) - target_compile_definitions(overlay_store_pc_forwarding_test PRIVATE - PSX_OVERLAY_DLL_BUILD PSX_NO_DEBUG_TOOLS PSX_ENABLE_BLOCK_CYCLES=1) - add_test(NAME overlay_store_pc_forwarding_test - COMMAND overlay_store_pc_forwarding_test) - - # frame_fingerprint judge columns are blind to device-write service order. - add_executable(frame_fingerprint_test tests/test_frame_fingerprint.c) - target_include_directories(frame_fingerprint_test PRIVATE include) - add_test(NAME frame_fingerprint_test COMMAND frame_fingerprint_test) - - add_executable(psx_cycle_event_boundaries_test - tests/test_psx_cycle_event_boundaries.c - src/psx_cycles.c) - target_include_directories(psx_cycle_event_boundaries_test PRIVATE include) - add_test(NAME psx_cycle_event_boundaries_test - COMMAND psx_cycle_event_boundaries_test) - - add_executable(psx_vblank_clock_test - tests/test_psx_vblank_clock.c - src/psx_vblank_clock.c) - target_include_directories(psx_vblank_clock_test PRIVATE include) - add_test(NAME psx_vblank_clock_test COMMAND psx_vblank_clock_test) - - add_executable(psx_video_timing_test - tests/test_psx_video_timing.c - src/psx_video_timing.c) - target_include_directories(psx_video_timing_test PRIVATE include) - add_test(NAME psx_video_timing_test COMMAND psx_video_timing_test) - - add_executable(spu_end_without_repeat_test - tests/test_spu_end_without_repeat.c) - target_include_directories(spu_end_without_repeat_test PRIVATE include) - add_test(NAME spu_end_without_repeat_test - COMMAND spu_end_without_repeat_test) - - add_executable(spu_pitch_zero_test - tests/test_spu_pitch_zero.c) - target_include_directories(spu_pitch_zero_test PRIVATE include) - add_test(NAME spu_pitch_zero_test - COMMAND spu_pitch_zero_test) - - # Guest-clocked SPU: accesses advance it to the current cycle (beads-eio.3.321). - add_executable(spu_guest_clock_test tests/test_spu_guest_clock.c) - target_include_directories(spu_guest_clock_test PRIVATE include) - add_test(NAME spu_guest_clock_test COMMAND spu_guest_clock_test) - add_executable(spu_sample_banks_test tests/test_spu_sample_banks.c) - target_include_directories(spu_sample_banks_test PRIVATE include) - add_test(NAME spu_sample_banks_test COMMAND spu_sample_banks_test) - - add_executable(spu_gaussian_test tests/test_spu_gaussian.c) - target_include_directories(spu_gaussian_test PRIVATE include) - add_test(NAME spu_gaussian_test COMMAND spu_gaussian_test) - - find_package(Python3 COMPONENTS Interpreter) - if(Python3_Interpreter_FOUND) - if(CMAKE_C_COMPILER_ID MATCHES "GNU|Clang" AND NOT MSVC - AND NOT CMAKE_CROSSCOMPILING) - add_test(NAME audio_trace_pcm_guard_test - COMMAND ${Python3_EXECUTABLE} - ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_audio_trace_pcm_guard.py - --cc ${CMAKE_C_COMPILER}) - add_test(NAME gpu_gp0_history_guard_test - COMMAND ${Python3_EXECUTABLE} - ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_gpu_gp0_history_guard.py - --cc ${CMAKE_C_COMPILER}) - add_test(NAME memory_debug_trace_guard_test - COMMAND ${Python3_EXECUTABLE} - ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_memory_debug_trace_guard.py - --cc ${CMAKE_C_COMPILER}) - endif() - add_test(NAME fntrace_c_linkage_test COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_fntrace_c_linkage.py) - add_test(NAME depth24_present_hold_vblank_test - COMMAND ${Python3_EXECUTABLE} - ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_depth24_present_hold_vblank.py) - add_test(NAME netplay_load_apply_flow_test - COMMAND ${Python3_EXECUTABLE} - ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_netplay_load_apply_flow.py) - if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") - add_test(NAME menu_video_snapshot_test COMMAND ${Python3_EXECUTABLE} - ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_menu_video_snapshot.py - --cxx ${CMAKE_CXX_COMPILER}) - endif() - if(CMAKE_C_COMPILER_ID MATCHES "GNU|Clang") - add_test(NAME netplay_rb_verify_deadline_test COMMAND ${Python3_EXECUTABLE} - ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_netplay_rb_verify_deadline.py - --compiler ${CMAKE_C_COMPILER}) - endif() - add_test(NAME restore_edge_irq_check_test - COMMAND ${Python3_EXECUTABLE} - ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_restore_edge_irq_check.py) - if(CMAKE_C_COMPILER_ID MATCHES "GNU|Clang") - add_test(NAME netplay_rb_session_reset_test - COMMAND ${Python3_EXECUTABLE} - ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_netplay_rb_session_reset.py - --compiler ${CMAKE_C_COMPILER}) - add_test(NAME overlay_retry_c11_test COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_overlay_retry_c11.py --compiler ${CMAKE_C_COMPILER}) - endif() - add_test(NAME setup_bios_assets_test COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_setup_bios_assets.py) - if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") - add_test(NAME launch_disc_path_test COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_launch_disc_path.py --compiler ${CMAKE_CXX_COMPILER}) - add_test(NAME persisted_settings_paths_test COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_persisted_settings_paths.py --compiler ${CMAKE_CXX_COMPILER}) - endif() - add_test(NAME disc_picker_ascii_test COMMAND ${Python3_EXECUTABLE} - ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_disc_picker_ascii.py) - add_test(NAME disc_companion_test COMMAND ${Python3_EXECUTABLE} ${PSXRECOMP_ROOT}/tools/tests/test_disc_companion.py) - add_test(NAME sbi_registry_test COMMAND ${Python3_EXECUTABLE} ${PSXRECOMP_ROOT}/tools/tests/test_sbi_registry.py) - add_test(NAME perf_campaign_test COMMAND ${Python3_EXECUTABLE} ${PSXRECOMP_ROOT}/tools/tests/test_perf_campaign.py) - add_test(NAME perf_host_test COMMAND ${Python3_EXECUTABLE} ${PSXRECOMP_ROOT}/tools/tests/test_perf_host.py) - add_test(NAME frame_interpolation_context_ownership_test - COMMAND ${Python3_EXECUTABLE} - ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_frame_interpolation_context_ownership.py) - add_test(NAME present_skip_cadence_guard_test - COMMAND ${Python3_EXECUTABLE} - ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_present_skip_cadence_guard.py) - add_test(NAME mod_bezel_test - COMMAND ${Python3_EXECUTABLE} - ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_mod_bezel.py) - add_test(NAME rewind_toggle_combo_test - COMMAND ${Python3_EXECUTABLE} - ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_rewind_toggle_combo.py) - add_test(NAME savestate_status_protocol_test - COMMAND ${Python3_EXECUTABLE} - ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_savestate_status_protocol.py) - add_test(NAME overlay_region_floor_config_test - COMMAND ${Python3_EXECUTABLE} - ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_overlay_region_floor_config.py) - add_test(NAME screenshot_hires_pitch_test - COMMAND ${Python3_EXECUTABLE} - ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_screenshot_hires_pitch.py) - add_test(NAME gl_depth24_upload_order_test - COMMAND ${Python3_EXECUTABLE} - ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_gl_depth24_upload_order.py) - add_test(NAME pal_cadence_host_refresh_test - COMMAND ${Python3_EXECUTABLE} - ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_pal_cadence_host_refresh.py) - add_test(NAME mixed_refresh_display_cadence_test - COMMAND ${Python3_EXECUTABLE} - ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_mixed_refresh_display_cadence.py) - add_test(NAME cdrom_lid_integration_test - COMMAND ${Python3_EXECUTABLE} - ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_cdrom_lid_integration.py) - add_test(NAME bios_registry_zero_entry_guard_test - COMMAND ${Python3_EXECUTABLE} - ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_bios_registry_zero_entry.py) - # Exercises runtime/psx_check_mod_catalog.cmake -- the build-time guard - # that fails a build when a title's mod packages land in the legacy - # mods/packages layout instead of mods/bundled (bead beads-eio.3.101). - # Needs the cmake executable, since the guard is a cmake -P script. - add_test(NAME mod_catalog_layout_test - COMMAND ${Python3_EXECUTABLE} - ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_mod_catalog_layout.py - --cmake ${CMAKE_COMMAND}) - # psxrecomp_add_game_runtime(... PGXP): the real function body under - # cmake -P, forwarding the hook-flavor option for the title's one - # runtime (docs/ENHANCEMENTS.md G1.11). - add_test(NAME game_runtime_pgxp_test - COMMAND ${Python3_EXECUTABLE} - ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_game_runtime_pgxp.py - --cmake ${CMAKE_COMMAND}) - # tools/mod_channel_filter.py strips developer features from a staged - # catalog; every entry that served one ([[requirement]] included) must - # leave with it. - add_test(NAME mod_channel_filter_test - COMMAND ${Python3_EXECUTABLE} - ${PSXRECOMP_ROOT}/tools/tests/test_mod_channel_filter.py) - # The overlay cache tag may be formatted in exactly two files. Three - # release packagers hand-built it instead, the flavor-suffix fix reached - # one of them, and the other two could not stage a matching Linux cache - # at all -- Tomba 2 v0.0.9 shipped with no AppImage as a result. - # bead beads-eio.3.102. This test is also the SHARED check a title's own - # suite runs against its own tree (pass --root ), so the rule has - # one implementation like the staging it guards. - add_test(NAME packagers_never_format_cache_tag_test - COMMAND ${Python3_EXECUTABLE} - ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_packagers_never_format_cache_tag.py) - # Behavioural half of the same bead: tools/release_stage.py must select - # shards by the tag DIRECTORY's identity (the old `-path "*//*"` - # substring match rejected the real ..._f0 directory and staged zero), - # must never ship .abi_.ok / .c / .pair-lock, and must fail rather - # than warn on an empty selection. Hermetic: synthetic cache trees, no - # recompiler or captures needed. - add_test(NAME release_stage_cache_test - COMMAND ${Python3_EXECUTABLE} - ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_release_stage_cache.py) - find_program(PSX_PACKAGE_TEST_BASH NAMES bash - HINTS "C:/Program Files/Git/bin") - add_test(NAME package_execution_stage_test - COMMAND ${Python3_EXECUTABLE} - ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_package_execution_stage.py - --bash "${PSX_PACKAGE_TEST_BASH}") - if(NOT PSX_PACKAGE_TEST_BASH) - set_tests_properties(package_execution_stage_test PROPERTIES DISABLED TRUE) - endif() - add_test(NAME package_appimage_test - COMMAND ${Python3_EXECUTABLE} - ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_package_appimage.py) - if(NOT CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux") - set_tests_properties(package_appimage_test PROPERTIES DISABLED TRUE) - endif() - # The Linux release's bundled tcc + musl headers build and load a real - # shard with nothing from the host (x86_64 Linux; skips elsewhere). - add_test(NAME linux_tcc_bundle_smoke_test - COMMAND ${Python3_EXECUTABLE} - ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_linux_tcc_bundle_smoke.py) - set_tests_properties(linux_tcc_bundle_smoke_test PROPERTIES - SKIP_RETURN_CODE 77 TIMEOUT 900) - # Overlay shards and BIOS modules are compiled for the RUNTIME's - # architecture: an x86_64 macOS runtime under Rosetta used to get arm64 - # shards (arm64-only Xcode clang, no -arch) in its macos-x64 cache and - # ran every overlay interpreted. Pins the PSX_OVERLAY_ARCH_ABI handoff - # (autocompile.c), -arch on macOS, the refusal when the Python cannot - # load the runtime's architecture, and unchanged Windows/Linux commands; - # with a real cc it builds and loads each slice this Mac can run. - add_test(NAME overlay_compile_arch_test - COMMAND ${Python3_EXECUTABLE} - ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_overlay_compile_arch.py - --cc ${CMAKE_C_COMPILER}) - endif() - - # Full card protocol regression: read, write, address echo, per-slot state, - # pad interruption, terminal ACK behavior and persistent FLAG semantics. - add_executable(sio_card_protocol_test - tests/test_sio_card_protocol.c - src/sio.c - src/memcard.c) - target_include_directories(sio_card_protocol_test PRIVATE include src) - # Runs in a build-tree scratch dir, NOT in tests/: the test writes - # test_dir/card1.mcd relative to the working directory (self-provisioning it - # via mkdir if absent), and tests/test_dir/card1.mcd is a TRACKED fixture — - # running it from tests/ overwrites that file and leaves the repo dirty. - file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/sio_card_protocol_scratch) - add_test(NAME sio_card_protocol_test COMMAND sio_card_protocol_test - WORKING_DIRECTORY - ${CMAKE_CURRENT_BINARY_DIR}/sio_card_protocol_scratch) - - # POSIX-only: dlopen()s a fixture .so out of a synthetic cache tree, so it - # cannot link under MinGW/MSVC (-ldl). run_overlay_posix_test.sh already - # stages the cache tree, builds the fixture and the test, and runs it — so - # register THAT rather than reimplementing its fixture staging here. - if(UNIX) - find_program(BASH_EXECUTABLE bash) - if(BASH_EXECUTABLE) - add_test(NAME overlay_posix_test - COMMAND ${BASH_EXECUTABLE} - ${CMAKE_CURRENT_SOURCE_DIR}/tests/run_overlay_posix_test.sh) - else() - message(STATUS "overlay_posix_test: bash not found - skipped") - endif() - endif() -endif() - -# ---- test-registration completeness guard (must stay LAST) ------------------ -# Fails configure if a file in runtime/tests/ or recompiler/tests/ is neither -# registered as a test nor listed as a deliberate exception. See the module -# header for the rot story this exists to prevent. -include(${CMAKE_CURRENT_SOURCE_DIR}/check_test_registration.cmake) -psxrecomp_check_all_tests_registered() - -if(NOT MSVC) - psxrecomp_add_runtime_target(psx-oracle - ORACLE - DEBUG_PORT 4371 - WINDOW_TITLE "psxrecomp - Oracle" - DEFAULT_BIOS_PATH "${PSXRECOMP_ROOT}/bios/SCPH1001.BIN" - ) -endif() - -# ---- psx-beetle target (Beetle PSX libretro standalone; MinGW, Linux, macOS) ---- -# -# Standalone binary that runs Beetle PSX (mednafen-psx libretro core) with -# a debug server exposing the SAME wire-protocol commands as psx-runtime, -# on its own port (default 4382, set in beetle_main.cpp; --port N). No recomp -# code linked. -# -# Requires beetle-psx with the docs/beetle_*.patch hooks, built via: -# cd beetle-psx && make platform=mingw_x86_64 STATIC_LINKING=1 HAVE_LIGHTREC=0 -# (platform=unix on Linux, platform=osx on macOS; there the archive is named -# mednafen_psx_libretro.so/.dylib; copy it to libmednafen_psx.a). Recipes: -# docs/beetle-linux.md, docs/beetle-macos.md. - -set(BEETLE_LIB "${CMAKE_SOURCE_DIR}/../beetle-psx/libmednafen_psx.a") - -if(NOT MSVC AND EXISTS "${BEETLE_LIB}") - message(STATUS "psx-beetle: found ${BEETLE_LIB}") - - set(BEETLE_DIR "${CMAKE_SOURCE_DIR}/../beetle-psx") - set(LRC "${BEETLE_DIR}/libretro-common") - set(LIBRETRO_COMMON_SOURCES - ${LRC}/streams/file_stream.c - ${LRC}/streams/file_stream_transforms.c - ${LRC}/vfs/vfs_implementation.c - ${LRC}/file/file_path.c - ${LRC}/compat/compat_strl.c - ${LRC}/compat/fopen_utf8.c - ${LRC}/compat/compat_posix_string.c - ${LRC}/string/stdstring.c - ${LRC}/lists/string_list.c - ${LRC}/encodings/encoding_utf.c - ${LRC}/time/rtime.c - ${LRC}/rthreads/rthreads.c - ${LRC}/memmap/memalign.c - ${LRC}/hash/rhash.c - ) - - add_executable(psx-beetle - src/beetle_main.cpp - src/beetle_libretro.cpp - src/beetle_debug_server.c - src/frame_pacing.c - src/host_time.c - src/parity_trace.c - src/device_trace.c - src/audio_trace.c - ${LIBRETRO_COMMON_SOURCES} - ) - - target_include_directories(psx-beetle PRIVATE - include - ${PSX_SDL_INCLUDE_DIRS} - ${CMAKE_SOURCE_DIR}/../beetle-psx/libretro-common/include - ${CMAKE_SOURCE_DIR}/../beetle-psx - ) - - target_compile_definitions(psx-beetle PRIVATE - PSX_BEETLE_BUILD=1 - DEFAULT_DEBUG_PORT=4380 - $<$:PSX_SDL3=1> - ) - if(NOT PSX_DEBUG_TOOLS) - target_compile_definitions(psx-beetle PRIVATE PSX_NO_DEBUG_TOOLS=1) - endif() - - target_link_libraries(psx-beetle PRIVATE - "${BEETLE_LIB}" - ${PSX_SDL_LIBRARIES} - ) - - if(WIN32 OR MINGW) - target_link_libraries(psx-beetle PRIVATE ws2_32 -lpthread) - endif() - - if(MINGW) - target_link_options(psx-beetle PRIVATE -Wl,--stack,67108864) - endif() -elseif(NOT MSVC) - message(STATUS "psx-beetle: libmednafen_psx.a not found - skipping psx-beetle") -endif() +cmake_minimum_required(VERSION 3.20) +project(psxrecomp-runtime C CXX) +include(CTest) + +set(CMAKE_C_STANDARD 99) +set(CMAKE_CXX_STANDARD 17) + +# ---- Native/oracle runtime targets ---- + +set(PSXRECOMP_ROOT "${CMAKE_SOURCE_DIR}/..") +include("${CMAKE_SOURCE_DIR}/runtime.cmake") + +psxrecomp_add_runtime_target(psx-runtime + DEBUG_PORT 4370 + WINDOW_TITLE "psxrecomp - BIOS" + DEFAULT_BIOS_PATH "${PSXRECOMP_ROOT}/bios/SCPH1001.BIN" + EXE_NAME "PSXRecomp" +) + +# With -DPSX_PGXP_VARIANT=ON, psxrecomp_add_runtime_target auto-clones a +# psx-runtime-pgxp sibling (PSXRecomp_pgxp.exe) compiled with -DPSX_PGXP=1 — +# the framework-dev validation surface for the PGXP value-propagation engine +# (docs/ENHANCEMENTS.md G1.10). Titles get their _pgxp the same way. + +if(BUILD_TESTING) + add_executable(video_texture_filter_test tests/test_video_texture_filter.c src/video_texture_filter.c) + target_include_directories(video_texture_filter_test PRIVATE include) + add_test(NAME video_texture_filter_test COMMAND video_texture_filter_test) + add_executable(mod_netplay_test tests/test_mod_netplay.c + src/mod_netplay.c src/netplay_state_digest.c src/crc32.c src/psx_sha256.c) + target_include_directories(mod_netplay_test PRIVATE include) + add_test(NAME mod_netplay_test COMMAND mod_netplay_test) + add_test(NAME mod_netplay_wide_test COMMAND mod_netplay_test --wide) + add_test(NAME mod_netplay_optional_bank_test COMMAND mod_netplay_test --no-banks) + add_test(NAME mod_netplay_options_policy_test COMMAND mod_netplay_test --options) + add_executable(savestate_title_policy_test tests/test_savestate_title_policy.c + src/mod_netplay.c src/psx_sha256.c) + target_include_directories(savestate_title_policy_test PRIVATE include) + add_test(NAME savestate_title_policy_test COMMAND savestate_title_policy_test) + add_executable(mod_capcom_bg2d_test tests/test_mod_capcom_bg2d.c) + target_include_directories(mod_capcom_bg2d_test PRIVATE include) + add_test(NAME mod_capcom_bg2d_test COMMAND mod_capcom_bg2d_test) + # White-box failure injection checks restart after partial native hashing. + foreach(_hash_variant native portable) + add_executable(sha256_file_${_hash_variant}_test + tests/test_psx_sha256_file.cpp src/psx_sha256.c) + target_include_directories(sha256_file_${_hash_variant}_test PRIVATE include) + if(_hash_variant STREQUAL "portable") + target_compile_definitions(sha256_file_${_hash_variant}_test PRIVATE + PSX_SHA256_FILE_PORTABLE=1) + elseif(WIN32) + target_link_libraries(sha256_file_${_hash_variant}_test PRIVATE bcrypt) + elseif(TARGET OpenSSL::Crypto) + target_compile_definitions(sha256_file_${_hash_variant}_test PRIVATE + PSX_SHA256_FILE_OPENSSL=1) + target_link_libraries(sha256_file_${_hash_variant}_test PRIVATE OpenSSL::Crypto) + endif() + add_test(NAME sha256_file_${_hash_variant}_test + COMMAND sha256_file_${_hash_variant}_test) + endforeach() + find_package(Threads REQUIRED) + add_executable(host_launch_timing_test + tests/test_host_launch_timing.cpp tests/host_launch_timing_helper.cpp) + target_include_directories(host_launch_timing_test PRIVATE include) + target_link_libraries(host_launch_timing_test PRIVATE Threads::Threads) + add_test(NAME host_launch_timing_test COMMAND host_launch_timing_test) + add_executable(disasm_test tests/test_disasm.cpp src/disasm_shim.cpp + ../recompiler/src/mips_decoder.cpp) + target_include_directories(disasm_test PRIVATE include ../recompiler/include) + add_test(NAME disasm_test COMMAND disasm_test) + add_executable(local_mouse_policy_test + tests/test_local_mouse_policy.cpp src/local_mouse_policy.cpp) + target_include_directories(local_mouse_policy_test PRIVATE include src) + add_test(NAME local_mouse_policy_test COMMAND local_mouse_policy_test) + # Netplay: which BIOS a match boots, and its wire helpers. + add_executable(netplay_bios_settle_test + tests/test_netplay_bios_settle.c + src/netplay_bios_settle.c) + target_include_directories(netplay_bios_settle_test PRIVATE include) + add_test(NAME netplay_bios_settle_test COMMAND netplay_bios_settle_test) + # Netplay: why a match ended, and when a boot-digest mismatch is final. + add_executable(netplay_exit_reason_test + tests/test_netplay_exit_reason.c + src/netplay_exit_reason.c) + target_include_directories(netplay_exit_reason_test PRIVATE include) + add_test(NAME netplay_exit_reason_test COMMAND netplay_exit_reason_test) + add_executable(netplay_sim_pad_cache_test tests/test_netplay_sim_pad_cache.c) + target_include_directories(netplay_sim_pad_cache_test PRIVATE include) + target_compile_definitions(netplay_sim_pad_cache_test PRIVATE PSX_MAX_PLAYERS=4) + add_test(NAME netplay_sim_pad_cache_test COMMAND netplay_sim_pad_cache_test) + add_executable(lobby_launch_transport_test tests/test_lobby_launch_transport.c) + target_include_directories(lobby_launch_transport_test PRIVATE include) + add_test(NAME lobby_launch_transport_test COMMAND lobby_launch_transport_test) + add_executable(netplay_fmv_confirm_test tests/test_netplay_fmv_confirm.c) + target_include_directories(netplay_fmv_confirm_test PRIVATE include) + add_test(NAME netplay_fmv_confirm_test COMMAND netplay_fmv_confirm_test) + add_executable(netplay_local_view_test tests/test_netplay_local_view.c) + target_include_directories(netplay_local_view_test PRIVATE include) + add_test(NAME netplay_local_view_test COMMAND netplay_local_view_test) + # Host relay over ICE: transport choice + lobby seat -> session slot map. + add_executable(netplay_ice_hub_test + tests/test_netplay_ice_hub.c + src/netplay_ice_hub.c) + target_include_directories(netplay_ice_hub_test PRIVATE include) + add_test(NAME netplay_ice_hub_test COMMAND netplay_ice_hub_test) + add_executable(ws_scene_hold_test tests/test_ws_scene_hold.c) + target_include_directories(ws_scene_hold_test PRIVATE include) + add_test(NAME ws_scene_hold_test COMMAND ws_scene_hold_test) + add_executable(ws_backdrop_extent_test tests/test_ws_backdrop_extent.c) + target_include_directories(ws_backdrop_extent_test PRIVATE include) + add_test(NAME ws_backdrop_extent_test COMMAND ws_backdrop_extent_test) + add_executable(mdec_snapshot_activity_test tests/test_mdec_snapshot_activity.c) + target_include_directories(mdec_snapshot_activity_test PRIVATE include src) + add_test(NAME mdec_snapshot_activity_test COMMAND mdec_snapshot_activity_test) + add_executable(mod_texture_banks_test tests/test_mod_texture_banks.c src/mod_texture_banks.c) + target_include_directories(mod_texture_banks_test PRIVATE include) + add_test(NAME mod_texture_banks_test COMMAND mod_texture_banks_test) + # First-call capture / later-call restore behind main.cpp's + # reset_mod_owned_presentation() (header-only, no runtime link). + add_executable(mod_session_baseline_test tests/test_mod_session_baseline.c) + target_include_directories(mod_session_baseline_test PRIVATE include) + add_test(NAME mod_session_baseline_test COMMAND mod_session_baseline_test) + # Netplay: libjuice resolves from third_party/ with the network sealed, + # and a recomp-net pinning another libjuice stops the configure. + foreach(_psx_juice_case same other) + add_test(NAME netplay_libjuice_offline_${_psx_juice_case} + COMMAND ${CMAKE_COMMAND} + -S "${CMAKE_CURRENT_SOURCE_DIR}/tests/netplay_libjuice_offline" + -B "${CMAKE_CURRENT_BINARY_DIR}/netplay_libjuice_offline_${_psx_juice_case}" + -G "${CMAKE_GENERATOR}" + "-DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}" + -DPSX_DEPS_OFFLINE=ON + "-DFAKE_RECOMP_NET=${CMAKE_CURRENT_SOURCE_DIR}/tests/netplay_libjuice_offline/recomp-net-${_psx_juice_case}") + set_tests_properties(netplay_libjuice_offline_${_psx_juice_case} PROPERTIES + ENVIRONMENT "HTTP_PROXY=http://127.0.0.1:9;HTTPS_PROXY=http://127.0.0.1:9;ALL_PROXY=http://127.0.0.1:9;NO_PROXY=") + endforeach() + set_tests_properties(netplay_libjuice_offline_other PROPERTIES + PASS_REGULAR_EXPRESSION "recomp-net pins libjuice SHA256") + if(NOT MSVC) + add_executable(host_snapshot_guard_test tests/test_host_snapshot_guard.c) + add_executable(fntrace_restore_handoff_test + tests/test_fntrace_restore_handoff.c src/fntrace.c) + foreach(test_target host_snapshot_guard_test fntrace_restore_handoff_test) + target_include_directories(${test_target} PRIVATE include src) + target_compile_options(${test_target} PRIVATE -O2 -flto -ffunction-sections -fdata-sections) + target_link_options(${test_target} PRIVATE -flto -Wl,--gc-sections) + add_test(NAME ${test_target} COMMAND ${test_target}) + endforeach() + add_executable(ws_world_scene_test tests/test_ws_world_scene.c + src/psx_video_timing.c) + target_include_directories(ws_world_scene_test PRIVATE include src) + target_compile_options(ws_world_scene_test PRIVATE -O2 -flto -ffunction-sections -fdata-sections) + target_link_options(ws_world_scene_test PRIVATE -flto -Wl,--gc-sections) + add_test(NAME ws_world_scene_test COMMAND ws_world_scene_test) + add_executable(ws_backdrop_scene_test tests/test_ws_backdrop_scene.c + src/psx_video_timing.c) + target_include_directories(ws_backdrop_scene_test PRIVATE include src) + target_compile_options(ws_backdrop_scene_test PRIVATE -O2 -flto -ffunction-sections -fdata-sections) + target_link_options(ws_backdrop_scene_test PRIVATE -flto -Wl,--gc-sections) + add_test(NAME ws_backdrop_scene_test COMMAND ws_backdrop_scene_test) + add_executable(boot_state_handoff_test tests/test_boot_state_handoff.c) + target_include_directories(boot_state_handoff_test PRIVATE include src) + target_compile_definitions(boot_state_handoff_test PRIVATE PSX_COSIM=1) + target_compile_options(boot_state_handoff_test PRIVATE -O2 -flto -ffunction-sections -fdata-sections -UNDEBUG) + target_link_options(boot_state_handoff_test PRIVATE -flto -Wl,--gc-sections) + target_link_libraries(boot_state_handoff_test PRIVATE ZLIB::ZLIB) + add_test(NAME boot_state_handoff_test COMMAND boot_state_handoff_test) + add_executable(boot_state_impl_tag_test tests/test_boot_state_handoff.c) + target_include_directories(boot_state_impl_tag_test PRIVATE include src) + target_compile_definitions(boot_state_impl_tag_test PRIVATE PSX_COSIM=1 PSX_SAVESTATE_IMPL_TAG=0x54474801u) + target_compile_options(boot_state_impl_tag_test PRIVATE -O2 -flto -ffunction-sections -fdata-sections -UNDEBUG) + target_link_options(boot_state_impl_tag_test PRIVATE -flto -Wl,--gc-sections) + target_link_libraries(boot_state_impl_tag_test PRIVATE ZLIB::ZLIB) + add_test(NAME boot_state_impl_tag_test COMMAND boot_state_impl_tag_test) + + add_executable(boot_state_execution_test tests/test_boot_state_handoff.c) + target_include_directories(boot_state_execution_test PRIVATE include src) + target_compile_definitions(boot_state_execution_test PRIVATE PSX_COSIM=1) + target_compile_options(boot_state_execution_test PRIVATE -O2 -flto -ffunction-sections -fdata-sections -UNDEBUG) + target_link_options(boot_state_execution_test PRIVATE -flto -Wl,--gc-sections) + target_link_libraries(boot_state_execution_test PRIVATE ZLIB::ZLIB) + psxrecomp_execution_profile(boot_state_execution_test PROFILE ENHANCED) + add_test(NAME boot_state_execution_test COMMAND boot_state_execution_test) + + add_executable(static_match_cache_test tests/test_static_match_cache.c + src/overlay_loader.c src/crc32.c) + target_include_directories(static_match_cache_test PRIVATE include) + target_compile_definitions(static_match_cache_test PRIVATE + PSX_HAS_OVERLAY_DISPATCH PSX_NO_DEBUG_TOOLS) + target_compile_options(static_match_cache_test PRIVATE -O2 -flto -ffunction-sections -fdata-sections -UNDEBUG) + target_link_options(static_match_cache_test PRIVATE -flto -Wl,--gc-sections) + add_test(NAME static_match_cache_test COMMAND static_match_cache_test) + + add_executable(mod_memory_snapshot_test tests/test_mod_memory_snapshot.c + src/psx_ram_geometry.c) + target_include_directories(mod_memory_snapshot_test PRIVATE include src) + target_compile_options(mod_memory_snapshot_test PRIVATE -O2 -flto -ffunction-sections -fdata-sections) + target_link_options(mod_memory_snapshot_test PRIVATE -flto -Wl,--gc-sections) + add_test(NAME mod_memory_snapshot_test COMMAND mod_memory_snapshot_test) + endif() + add_executable(sio_card_irq_integrity_test tests/test_sio_card_irq_integrity.c) + target_include_directories(sio_card_irq_integrity_test PRIVATE include) + foreach(scenario absent deselect pending coarse) + add_test(NAME sio_card_irq_integrity_${scenario} + COMMAND sio_card_irq_integrity_test ${scenario}) + # The retired environment variable must not reactivate the workaround. + set_tests_properties(sio_card_irq_integrity_${scenario} PROPERTIES + ENVIRONMENT "PSX_APE_CARD_UNSTICK=1") + endforeach() + # Optional real-hardware regression. Configure its dependencies explicitly; + # absence must remain visible, never counted as a passing GPU test. + set(PSX_GL_READBACK_SDL_ROOT "" CACHE PATH "SDL3 static build root for GL readback test") + get_filename_component(_gl_test_default_bin "${CMAKE_C_COMPILER}" DIRECTORY) + set(PSX_GL_READBACK_COMPILER_BIN "${_gl_test_default_bin}" CACHE PATH "GCC/MinGW bin for GL fixture") + find_package(Python3 QUIET COMPONENTS Interpreter) + if(Python3_Interpreter_FOUND) + add_test(NAME guest_implementation_binding_test COMMAND "${Python3_EXECUTABLE}" + "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_guest_implementation.py" + --cmake "${CMAKE_COMMAND}" --compiler "${CMAKE_C_COMPILER}" + --generator "${CMAKE_GENERATOR}" + --work-root "${CMAKE_CURRENT_BINARY_DIR}/hle-binding-fixtures") + find_program(PSX_PROFILE_TEST_NINJA NAMES ninja ninja.exe) + if(PSX_PROFILE_TEST_NINJA) + add_test(NAME execution_profile_test COMMAND "${Python3_EXECUTABLE}" + "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_execution_profile.py" + --cmake "${CMAKE_COMMAND}" --cc "${CMAKE_C_COMPILER}" + --ninja "${PSX_PROFILE_TEST_NINJA}") + set_tests_properties(execution_profile_test PROPERTIES RUN_SERIAL TRUE) + endif() + add_test(NAME host_launch_timings_protocol_test COMMAND ${CMAKE_COMMAND} -E env + "CC=${CMAKE_C_COMPILER}" "CXX=${CMAKE_CXX_COMPILER}" "${Python3_EXECUTABLE}" + "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_host_launch_timings_protocol.py") + set_tests_properties(host_launch_timings_protocol_test PROPERTIES SKIP_RETURN_CODE 77) + add_test(NAME tcp_command_index_check COMMAND "${Python3_EXECUTABLE}" + "${PSXRECOMP_ROOT}/tools/gen_tcp_commands.py" --check) + add_test(NAME wtrace_dump_test COMMAND ${CMAKE_COMMAND} -E env + "CC=${CMAKE_C_COMPILER}" "${Python3_EXECUTABLE}" + "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_wtrace_dump.py") + set_tests_properties(wtrace_dump_test PROPERTIES SKIP_RETURN_CODE 77) + add_test(NAME counted_mips_relocations_test COMMAND "${Python3_EXECUTABLE}" + "${PSXRECOMP_ROOT}/tools/tests/test_counted_mips_relocations.py") + add_test(NAME interp_mod_entry_guards_test COMMAND "${Python3_EXECUTABLE}" + "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_interp_mod_entry_guards.py") + add_test(NAME mirror_pc_preserved_guards_test COMMAND "${Python3_EXECUTABLE}" + "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_mirror_pc_preserved_guards.py") + add_test(NAME segment_miss_wiring_test COMMAND "${Python3_EXECUTABLE}" + "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_segment_miss_wiring.py") + if(CMAKE_C_COMPILER_ID MATCHES "GNU|Clang") + add_test(NAME ws_screen_tags_exec_test COMMAND "${Python3_EXECUTABLE}" + "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_ws_screen_tags_exec.py" + --compiler "${CMAKE_C_COMPILER}") + add_test(NAME ws_activation_bias_exec_test COMMAND "${Python3_EXECUTABLE}" + "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_ws_activation_bias_exec.py" "${CMAKE_C_COMPILER}") + endif() + add_test(NAME gpu_textured_dot_nw_shift_test COMMAND "${Python3_EXECUTABLE}" + "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_gpu_textured_dot_nw_shift.py") + add_test(NAME gl_readback_runner_test COMMAND "${Python3_EXECUTABLE}" + "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_gl_readback_runner.py") + endif() + if(MINGW AND PSX_GL_READBACK_SDL_ROOT + AND EXISTS "${PSX_GL_READBACK_COMPILER_BIN}/gcc.exe" + AND EXISTS "${PSX_GL_READBACK_COMPILER_BIN}/g++.exe" + AND IS_DIRECTORY "${PSX_GL_READBACK_SDL_ROOT}/sdl3-src/include" + AND EXISTS "${PSX_GL_READBACK_SDL_ROOT}/sdl3-build/libSDL3.a") + find_package(Python3 REQUIRED COMPONENTS Interpreter) + add_test(NAME gl_readback_region_test + COMMAND "${Python3_EXECUTABLE}" + "${CMAKE_CURRENT_SOURCE_DIR}/tests/run_gl_readback_region.py" + --fixture "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_gl_readback_region.c" + --compiler-bin "${PSX_GL_READBACK_COMPILER_BIN}" + --sdl-root "${PSX_GL_READBACK_SDL_ROOT}" + --output "${CMAKE_CURRENT_BINARY_DIR}/gl-readback-results") + set_tests_properties(gl_readback_region_test PROPERTIES LABELS "gpu;opengl;hardware" TIMEOUT 180) + else() + message(STATUS "gl_readback_region_test: not registered (requires MinGW, SDL3 headers/static library in PSX_GL_READBACK_SDL_ROOT and gcc.exe/g++.exe in PSX_GL_READBACK_COMPILER_BIN)") + endif() + # Real-GL internal-resolution invariance (macOS, Linux and Windows/MinGW, + # hidden window): the guest-visible VRAM is identical at every scale, lines + # stay one native pixel thick, and an over-limit request clamps inside GL. + # Needs the SDL3 this tree fetched and a GPU session; labelled hardware like + # the readback test above. A host with no window/GL 3.3 context (headless, + # or Windows over SSH) exits 77 and is reported as skipped. + if((NOT WIN32 OR MINGW) AND Python3_Interpreter_FOUND AND TARGET SDL3::SDL3) + if(DEFINED sdl3_SOURCE_DIR) + set(_gl_scale_sdl_inc "${sdl3_SOURCE_DIR}/include") + else() + set(_gl_scale_sdl_inc "$") + endif() + add_test(NAME gl_scale_invariance_test + COMMAND "${Python3_EXECUTABLE}" + "${CMAKE_CURRENT_SOURCE_DIR}/tests/run_gl_scale_invariance.py" + --fixture "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_gl_scale_invariance.c" + --cc "${CMAKE_C_COMPILER}" + --sdl-include "${_gl_scale_sdl_inc}" + --sdl-library "$" + --output "${CMAKE_CURRENT_BINARY_DIR}/gl-scale-results") + set_tests_properties(gl_scale_invariance_test PROPERTIES + LABELS "gpu;opengl;hardware" TIMEOUT 300 SKIP_RETURN_CODE 77) + # Render thread ([video] render_thread) on vs off: the values the + # guest reads back, native VRAM, the frame at S and the native-wide + # surface are identical for a scripted stream with readbacks, mask + # bits, uploads, ring wraps and per-primitive widescreen tags. A host + # with no window/GL 3.3 context (headless, Windows over SSH) exits 77: + # reported as skipped. + add_test(NAME gl_render_thread_test + COMMAND "${Python3_EXECUTABLE}" + "${CMAKE_CURRENT_SOURCE_DIR}/tests/run_gl_render_thread.py" + --fixture "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_gl_render_thread.c" + --cc "${CMAKE_C_COMPILER}" + --sdl-include "${_gl_scale_sdl_inc}" + --sdl-library "$" + --output "${CMAKE_CURRENT_BINARY_DIR}/gl-render-thread-results") + set_tests_properties(gl_render_thread_test PROPERTIES + LABELS "gpu;opengl;hardware" TIMEOUT 300 SKIP_RETURN_CODE 77) + # Frame generation ([video] frame_generation): the real presented + # images are identical with it off and on (VRAM and native-wide + # presents), generated frames are presented, and an in-between frame + # at phase 1 / 0 equals the newer / older real frame. + add_test(NAME gl_frame_gen_test + COMMAND "${Python3_EXECUTABLE}" + "${CMAKE_CURRENT_SOURCE_DIR}/tests/run_gl_frame_gen.py" + --fixture "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_gl_frame_gen.c" + --cc "${CMAKE_C_COMPILER}" + --sdl-include "${_gl_scale_sdl_inc}" + --sdl-library "$" + --output "${CMAKE_CURRENT_BINARY_DIR}/gl-frame-gen-results") + set_tests_properties(gl_frame_gen_test PROPERTIES + LABELS "gpu;opengl;hardware" TIMEOUT 300 SKIP_RETURN_CODE 77) + # [video] texture_lod / anisotropic_filtering: off and untracked/ + # magnified prims are unchanged; minified world textures average + # inside their UV bounds; render thread and PGXP depth agree. + add_test(NAME gl_texture_lod_test + COMMAND "${Python3_EXECUTABLE}" + "${CMAKE_CURRENT_SOURCE_DIR}/tests/run_gl_render_thread.py" --single + --fixture "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_gl_texture_lod.c" + --cc "${CMAKE_C_COMPILER}" + --sdl-include "${_gl_scale_sdl_inc}" + --sdl-library "$" + --output "${CMAKE_CURRENT_BINARY_DIR}/gl-texture-lod-results") + set_tests_properties(gl_texture_lod_test PROPERTIES + LABELS "gpu;opengl;hardware" TIMEOUT 180 SKIP_RETURN_CODE 77) + # PS1 semi-transparency modes and mask bit at 4x with PGXP depth and + # the render thread, against the formulas and the software reference + # ([video] accurate_blending on; off is measured and printed). + add_test(NAME gl_blend_accuracy_test + COMMAND "${Python3_EXECUTABLE}" + "${CMAKE_CURRENT_SOURCE_DIR}/tests/run_gl_render_thread.py" --single + --fixture "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_gl_blend_accuracy.c" + --cc "${CMAKE_C_COMPILER}" + --sdl-include "${_gl_scale_sdl_inc}" + --sdl-library "$" + --output "${CMAKE_CURRENT_BINARY_DIR}/gl-blend-accuracy-results") + set_tests_properties(gl_blend_accuracy_test PROPERTIES + LABELS "gpu;opengl;hardware" TIMEOUT 180 SKIP_RETURN_CODE 77) + # texture_filtering = "xbr": diagonals lose the staircase, straight + # edges, flat areas, cutouts and 1:1 draws stay nearest; RT agrees. + add_test(NAME gl_xbr_filter_test + COMMAND "${Python3_EXECUTABLE}" + "${CMAKE_CURRENT_SOURCE_DIR}/tests/run_gl_render_thread.py" --single + --fixture "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_gl_xbr.c" + --cc "${CMAKE_C_COMPILER}" + --sdl-include "${_gl_scale_sdl_inc}" + --sdl-library "$" + --output "${CMAKE_CURRENT_BINARY_DIR}/gl-xbr-results") + set_tests_properties(gl_xbr_filter_test PROPERTIES + LABELS "gpu;opengl;hardware" TIMEOUT 180 SKIP_RETURN_CODE 77) + # [video] dithering: off ignores the guest's dither bit (byte-identical + # true colour); on/scaled add the PS1 pattern to shaded/modulated draws + # only, through the facade, the render thread, PGXP depth and wide. + add_test(NAME gl_dithering_test + COMMAND "${Python3_EXECUTABLE}" + "${CMAKE_CURRENT_SOURCE_DIR}/tests/run_gl_render_thread.py" --single + --fixture "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_gl_dithering.c" + --cc "${CMAKE_C_COMPILER}" + --sdl-include "${_gl_scale_sdl_inc}" + --sdl-library "$" + --output "${CMAKE_CURRENT_BINARY_DIR}/gl-dithering-results") + set_tests_properties(gl_dithering_test PROPERTIES + LABELS "gpu;opengl;hardware" TIMEOUT 180 SKIP_RETURN_CODE 77) + unset(_gl_scale_sdl_inc) + else() + message(STATUS "gl_scale_invariance_test: not registered (needs macOS, Linux or MinGW, Python 3 and SDL3)") + endif() + if(Python3_Interpreter_FOUND AND TARGET SDL3::SDL3-static) + add_test(NAME gl_texture_filter_test + COMMAND "${Python3_EXECUTABLE}" + "${CMAKE_CURRENT_SOURCE_DIR}/tests/run_gl_texture_filter.py" + --fixture "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_gl_texture_filter.c" + --cc "${CMAKE_C_COMPILER}" + --sdl-include "$" + --sdl-library "$" + --output "${CMAKE_CURRENT_BINARY_DIR}/gl-filter-results") + set_tests_properties(gl_texture_filter_test PROPERTIES LABELS "gpu;opengl;hardware" TIMEOUT 180) + add_test(NAME gl_post_aa_test + COMMAND "${Python3_EXECUTABLE}" + "${CMAKE_CURRENT_SOURCE_DIR}/tests/run_gl_texture_filter.py" + --fixture "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_gl_post_aa.c" + --cc "${CMAKE_C_COMPILER}" + --sdl-include "$" + --sdl-library "$" + --output "${CMAKE_CURRENT_BINARY_DIR}/gl-post-aa-results") + set_tests_properties(gl_post_aa_test PROPERTIES + LABELS "gpu;opengl;hardware" TIMEOUT 180 SKIP_RETURN_CODE 77) + add_test(NAME gl_bloom_test + COMMAND "${Python3_EXECUTABLE}" + "${CMAKE_CURRENT_SOURCE_DIR}/tests/run_gl_texture_filter.py" + --fixture "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_gl_bloom.c" + --cc "${CMAKE_C_COMPILER}" + --sdl-include "$" + --sdl-library "$" + --output "${CMAKE_CURRENT_BINARY_DIR}/gl-bloom-results") + set_tests_properties(gl_bloom_test PROPERTIES + LABELS "gpu;opengl;hardware" TIMEOUT 180 SKIP_RETURN_CODE 77) + add_test(NAME gl_hd_texture_pack_test + COMMAND "${Python3_EXECUTABLE}" + "${CMAKE_CURRENT_SOURCE_DIR}/tests/run_gl_texture_filter.py" + --hd-pack --fixture "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_gl_hd_texture_pack.c" + --cc "${CMAKE_C_COMPILER}" --cxx "${CMAKE_CXX_COMPILER}" + --webp-include "$" + --webp-decoder "$" + --sdl-include "$" + --sdl-library "$" + --output "${CMAKE_CURRENT_BINARY_DIR}/gl-hd-texture-results") + set_tests_properties(gl_hd_texture_pack_test PROPERTIES LABELS "gpu;opengl;hardware" TIMEOUT 180) + # [video] fmv_chroma_smoothing: 24-bit presents lose their 2x2 chroma + # blocks, luma kept; 15-bit presents and off are byte-identical. + add_test(NAME gl_fmv_chroma_test + COMMAND "${Python3_EXECUTABLE}" + "${CMAKE_CURRENT_SOURCE_DIR}/tests/run_gl_texture_filter.py" + --fixture "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_gl_fmv_chroma.c" + --cc "${CMAKE_C_COMPILER}" + --sdl-include "$" + --sdl-library "$" + --output "${CMAKE_CURRENT_BINARY_DIR}/gl-fmv-chroma-results") + set_tests_properties(gl_fmv_chroma_test PROPERTIES + LABELS "gpu;opengl;hardware" TIMEOUT 180 SKIP_RETURN_CODE 77) + # Debug present ring: GL names do not outlive a renderer restart. + add_test(NAME gl_present_ring_restart_test + COMMAND "${Python3_EXECUTABLE}" + "${CMAKE_CURRENT_SOURCE_DIR}/tests/run_gl_texture_filter.py" + --debug-tools + --fixture "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_gl_present_ring_restart.c" + --cc "${CMAKE_C_COMPILER}" + --sdl-include "$" + --sdl-library "$" + --output "${CMAKE_CURRENT_BINARY_DIR}/gl-present-ring-restart-results") + set_tests_properties(gl_present_ring_restart_test PROPERTIES + LABELS "gpu;opengl;hardware" TIMEOUT 180 SKIP_RETURN_CODE 77) + endif() + add_executable(launcher_device_roundtrip_test + tests/test_launcher_device_roundtrip.cpp) + target_include_directories(launcher_device_roundtrip_test PRIVATE include) + add_test(NAME launcher_device_roundtrip_test + COMMAND launcher_device_roundtrip_test) + + # Pad-mode resolution at the launcher exit seam: a lock_mode title must + # boot the pad type its game.toml declared whatever the launcher hands + # back, an active mod controller-mode override must outrank the lock, and + # neither may let an unsupported mode reach settings.toml. Header-only, so + # it needs nothing but runtime/include. + add_executable(launcher_pad_mode_resolve_test + tests/test_launcher_pad_mode_resolve.cpp) + target_include_directories(launcher_pad_mode_resolve_test PRIVATE include) + add_test(NAME launcher_pad_mode_resolve_test + COMMAND launcher_pad_mode_resolve_test) + + # Presentation-only mod input must not be sourced from disconnected, + # peer-synchronized, or rollback-replayed pad state. + add_executable(mod_local_input_scope_test + tests/test_mod_local_input_scope.c) + target_include_directories(mod_local_input_scope_test PRIVATE src) + add_test(NAME mod_local_input_scope_test + COMMAND mod_local_input_scope_test) + + # COP0.CAUSE IP2 must mirror the INTC line combinationally, never latch. + add_executable(cause_ip2_combinational_test + tests/test_cause_ip2_combinational.c) + add_test(NAME cause_ip2_combinational_test + COMMAND cause_ip2_combinational_test) + + add_executable(psx_cyc_batch_test tests/test_psx_cyc_batch.c) + target_include_directories(psx_cyc_batch_test PRIVATE include) + target_compile_definitions(psx_cyc_batch_test PRIVATE + PSX_ENABLE_BLOCK_CYCLES=1) + add_test(NAME psx_cyc_batch_test COMMAND psx_cyc_batch_test) + + # [timing] guest_cycle_scale divides instruction charges only (1 = faithful). + add_executable(psx_guest_cycle_scale_test tests/test_psx_guest_cycle_scale.c) + target_include_directories(psx_guest_cycle_scale_test PRIVATE include) + target_compile_definitions(psx_guest_cycle_scale_test PRIVATE + PSX_ENABLE_BLOCK_CYCLES=1) + add_test(NAME psx_guest_cycle_scale_test COMMAND psx_guest_cycle_scale_test) + + # One CPU charge for every tier, the declarative RAM gate and the mod gate, + # and rollback re-simulation, against the real psx_cycles.c. + add_executable(psx_guest_cycle_scale_gate_test + tests/test_psx_guest_cycle_scale_gate.c + src/psx_cycles.c) + target_include_directories(psx_guest_cycle_scale_gate_test PRIVATE include) + target_compile_definitions(psx_guest_cycle_scale_gate_test PRIVATE + PSX_ENABLE_BLOCK_CYCLES=1) + add_test(NAME psx_guest_cycle_scale_gate_test COMMAND psx_guest_cycle_scale_gate_test) + + # Segment misses in static game code (docs/SEGMENT_AWARE_CODE.md §5.5): + # classification against the dispatch rows, the ring and the per-PC + # summary the TCP command and the exit report read. + add_executable(psx_segment_miss_test + tests/test_psx_segment_miss.c + src/psx_segment_miss.c + ) + target_include_directories(psx_segment_miss_test PRIVATE include) + add_test(NAME psx_segment_miss_test COMMAND psx_segment_miss_test) + + add_executable(psx_icache_fastpath_test + tests/test_psx_icache_fastpath.c + src/psx_icache.c + ) + target_include_directories(psx_icache_fastpath_test PRIVATE include) + # PSX_OVERLAY_DLL_BUILD is the seam psx_cycles.h already provides for a TU + # that supplies its own psx_advance_cycles (overlay DLLs forward it through + # a callback shim). Without it the header inlines the production definition + # and this test's counting stub is a redefinition — the whole runtime test + # suite stopped building at "redefinition of 'psx_advance_cycles'", which + # also blocked every test registered after it. + target_compile_definitions(psx_icache_fastpath_test PRIVATE + PSX_ENABLE_BLOCK_CYCLES=1 + PSX_OVERLAY_DLL_BUILD=1) + add_test(NAME psx_icache_fastpath_test COMMAND psx_icache_fastpath_test) + + add_executable(psx_icache_elision_guard_test + tests/test_psx_icache_elision_guard.c src/psx_icache.c) + target_include_directories(psx_icache_elision_guard_test PRIVATE include) + target_compile_definitions(psx_icache_elision_guard_test PRIVATE + PSX_ENABLE_BLOCK_CYCLES=1 PSX_OVERLAY_DLL_BUILD=1) + add_test(NAME psx_icache_elision_guard_test COMMAND psx_icache_elision_guard_test) + + foreach(_ram_geometry IN ITEMS retail expanded) + add_executable(psx_memory_geometry_${_ram_geometry}_test + tests/test_psx_memory_geometry.c) + target_include_directories(psx_memory_geometry_${_ram_geometry}_test + PRIVATE include) + if(_ram_geometry STREQUAL "expanded") + target_compile_definitions(psx_memory_geometry_${_ram_geometry}_test + PRIVATE PSX_MEMORY_GEOMETRY_TEST_EXPANDED=1) + endif() + add_test(NAME psx_memory_geometry_${_ram_geometry}_test + COMMAND psx_memory_geometry_${_ram_geometry}_test) + endforeach() + + # Live 2/8 MiB geometry end to end through the REAL memory.c accessors, + # dma.c channel execution and boot_state.c save/load (including the + # refuse-before-mutation contract). Section GC, as for + # mod_memory_snapshot_test, drops the hardware routing the test never + # reaches; psx_ram_runtime_map_stubs.c doubles the rest. + if(NOT MSVC) + add_executable(psx_ram_runtime_map_test + tests/test_psx_ram_runtime_map.c + tests/psx_ram_runtime_map_stubs.c + src/memory.c + src/dma.c + src/gpu_timeline.c + src/boot_state.c + src/psx_ram_geometry.c) + target_include_directories(psx_ram_runtime_map_test PRIVATE include src) + target_compile_options(psx_ram_runtime_map_test PRIVATE + -O2 -flto -ffunction-sections -fdata-sections -Wno-lto-type-mismatch) + target_link_options(psx_ram_runtime_map_test PRIVATE + -flto -Wl,--gc-sections -Wno-lto-type-mismatch) + target_link_libraries(psx_ram_runtime_map_test PRIVATE ZLIB::ZLIB) + add_test(NAME psx_ram_runtime_map_test COMMAND psx_ram_runtime_map_test) + + # Cache-isolated (SR.IsC) CPU stores through the REAL memory.c store + # paths and the REAL psx_icache.c tag model, against Beetle's + # WriteMemory IsC branch: FlushCache invalidation, tag-test valid bits, + # the D-cache scratchpad write, the BIU and DMA exclusions. Same link + # doubles as psx_ram_runtime_map_test, minus the I-cache ones. + add_executable(isc_store_test + tests/test_isc_store.c + tests/psx_ram_runtime_map_stubs.c + src/memory.c + src/dma.c + src/gpu_timeline.c + src/boot_state.c + src/psx_icache.c + src/psx_ram_geometry.c) + target_include_directories(isc_store_test PRIVATE include src) + target_compile_definitions(isc_store_test PRIVATE + PSX_ENABLE_BLOCK_CYCLES=1 PSX_STUBS_REAL_ICACHE=1) + target_compile_options(isc_store_test PRIVATE + -O2 -flto -ffunction-sections -fdata-sections -Wno-lto-type-mismatch) + target_link_options(isc_store_test PRIVATE -flto -Wno-lto-type-mismatch + $,-Wl$-dead_strip,-Wl$--gc-sections>) + target_link_libraries(isc_store_test PRIVATE ZLIB::ZLIB) + add_test(NAME isc_store_test COMMAND isc_store_test) + + # DMA and host stores drop the PGXP shadow of the words they touch + # (an identical word is not proof of the same projection): the REAL + # memory.c store paths against the REAL pgxp.cpp engine. Same link + # doubles as isc_store_test, minus the PGXP one. + add_executable(pgxp_untracked_store_test + tests/test_pgxp_untracked_store.c + tests/psx_ram_runtime_map_stubs.c + src/memory.c + src/dma.c + src/gpu_timeline.c + src/boot_state.c + src/pgxp.cpp + src/psx_ram_geometry.c) + target_include_directories(pgxp_untracked_store_test PRIVATE include src) + target_compile_definitions(pgxp_untracked_store_test PRIVATE + PSX_STUBS_REAL_PGXP=1) + target_compile_options(pgxp_untracked_store_test PRIVATE + -O2 -flto -ffunction-sections -fdata-sections -Wno-lto-type-mismatch) + target_link_options(pgxp_untracked_store_test PRIVATE -flto -Wno-lto-type-mismatch + $,-Wl$-dead_strip,-Wl$--gc-sections>) + target_link_libraries(pgxp_untracked_store_test PRIVATE ZLIB::ZLIB) + add_test(NAME pgxp_untracked_store_test COMMAND pgxp_untracked_store_test) + endif() + + # Loadable BIOS backends (psx_bios_module.h): the host loader against + # three fake modules built here from one source -- good, wrong ABI tag, + # wrong codegen hash. Real dlopen/LoadLibrary, real registry, stubbed core. + if(NOT MSVC) + foreach(_fake IN ITEMS good bad_abi bad_hash) + add_library(bios_module_fake_${_fake} SHARED tests/bios_module_fake.c) + target_include_directories(bios_module_fake_${_fake} PRIVATE include) + target_compile_definitions(bios_module_fake_${_fake} PRIVATE + PSX_OVERLAY_DLL_BUILD PSX_NO_DEBUG_TOOLS) + set_target_properties(bios_module_fake_${_fake} PROPERTIES PREFIX "") + endforeach() + target_compile_definitions(bios_module_fake_bad_abi PRIVATE FAKE_ABI_TAG=0x7fff) + target_compile_definitions(bios_module_fake_bad_hash PRIVATE FAKE_CODEGEN_HASH=0x1u) + add_executable(bios_module_host_test + tests/test_bios_module_host.c + src/psx_bios_module.c + src/psx_bios_backend.c + src/overlay_posix.c + src/crc32.c) + target_include_directories(bios_module_host_test PRIVATE include src) + target_compile_definitions(bios_module_host_test PRIVATE + PSX_NO_DEBUG_TOOLS + FAKE_GOOD="$" + FAKE_BAD_ABI="$" + FAKE_BAD_HASH="$") + target_link_libraries(bios_module_host_test PRIVATE ${CMAKE_DL_LIBS}) + add_dependencies(bios_module_host_test + bios_module_fake_good bios_module_fake_bad_abi bios_module_fake_bad_hash) + add_test(NAME bios_module_host_test COMMAND bios_module_host_test) + endif() + + add_executable(host_keymap_test + tests/test_host_keymap.c + src/host_keymap.c) + target_include_directories(host_keymap_test PRIVATE + include ${PSX_SDL_INCLUDE_DIRS}) + target_compile_definitions(host_keymap_test PRIVATE + SDL_MAIN_HANDLED + $<$:PSX_SDL3=1>) + target_link_libraries(host_keymap_test PRIVATE ${PSX_SDL_LIBRARIES}) + add_test(NAME host_keymap_test COMMAND host_keymap_test) + + add_executable(host_osd_diagnostics_test + tests/test_host_osd_diagnostics.c src/host_osd.c) + target_include_directories(host_osd_diagnostics_test PRIVATE + include ${PSX_SDL_INCLUDE_DIRS}) + target_compile_definitions(host_osd_diagnostics_test PRIVATE + SDL_MAIN_HANDLED RECOMP_LAUNCHER=1 PSX_SDL_NO_RENDER=1 + $<$:PSX_SDL3=1>) + target_link_libraries(host_osd_diagnostics_test PRIVATE ${PSX_SDL_LIBRARIES}) + add_test(NAME host_osd_diagnostics_test COMMAND host_osd_diagnostics_test) + + add_executable(keyboard_pad_chord_test + tests/test_keyboard_pad_chord.c + src/psx_keybinds.c src/host_keymap.c) + target_include_directories(keyboard_pad_chord_test PRIVATE + include ${PSX_SDL_INCLUDE_DIRS}) + target_compile_definitions(keyboard_pad_chord_test PRIVATE + SDL_MAIN_HANDLED + $<$:PSX_SDL3=1>) + target_link_libraries(keyboard_pad_chord_test PRIVATE ${PSX_SDL_LIBRARIES}) + add_test(NAME keyboard_pad_chord_test COMMAND keyboard_pad_chord_test) + + add_executable(mouse_binding_suppression_test tests/test_mouse_binding_suppression.c) + target_include_directories(mouse_binding_suppression_test PRIVATE include ${PSX_SDL_INCLUDE_DIRS}) + target_compile_definitions(mouse_binding_suppression_test PRIVATE + SDL_MAIN_HANDLED $<$:PSX_SDL3=1>) + target_link_libraries(mouse_binding_suppression_test PRIVATE ${PSX_SDL_LIBRARIES}) + add_test(NAME mouse_binding_suppression_test COMMAND mouse_binding_suppression_test) + + # Generic external replacement-pack core. The renderer integration is a + # separate consumer; this target pins pack discovery, deterministic keying, + # VRAM residency, portable tracking state, and bounded asynchronous decode. + add_executable(hd_texture_pack_test + tests/test_hd_texture_pack.cpp + src/hd_texture_pack.cpp) + target_include_directories(hd_texture_pack_test PRIVATE include) + find_package(Threads REQUIRED) + target_link_libraries(hd_texture_pack_test PRIVATE Threads::Threads) + add_test(NAME hd_texture_pack_test COMMAND hd_texture_pack_test) + add_executable(duckstation_texture_pack_test + tests/test_duckstation_texture_pack.cpp + src/duckstation_texture_pack.cpp) + target_include_directories(duckstation_texture_pack_test PRIVATE include) + target_link_libraries(duckstation_texture_pack_test PRIVATE Threads::Threads) + target_link_libraries(duckstation_texture_pack_test PRIVATE psx_texture_image_decode) + add_test(NAME duckstation_texture_pack_test COMMAND duckstation_texture_pack_test) + add_executable(texture_image_decode_test tests/test_texture_image_decode.cpp) + target_link_libraries(texture_image_decode_test PRIVATE psx_texture_image_decode) + add_test(NAME texture_image_decode_test COMMAND texture_image_decode_test + "${CMAKE_CURRENT_SOURCE_DIR}/tests/fixtures/texture_images") + + # Standalone serial-pad protocol tests use explicit SIO/device stubs; they + # cover both DualShock rumble and JogCon wire/state behavior. + add_executable(sio_dualshock_rumble_test + tests/test_sio_dualshock_rumble.c + src/sio.c) + target_include_directories(sio_dualshock_rumble_test PRIVATE include) + add_test(NAME sio_dualshock_rumble_test COMMAND sio_dualshock_rumble_test) + add_executable(sio_jogcon_protocol_test + tests/test_sio_jogcon_protocol.c + src/sio.c) + target_include_directories(sio_jogcon_protocol_test PRIVATE include) + add_test(NAME sio_jogcon_protocol_test COMMAND sio_jogcon_protocol_test) + add_executable(sio_negcon_protocol_test + tests/test_sio_negcon_protocol.c + src/sio.c) + target_include_directories(sio_negcon_protocol_test PRIVATE include) + add_test(NAME sio_negcon_protocol_test COMMAND sio_negcon_protocol_test) + add_executable(sio_negcon_protocol_multitap_test + tests/test_sio_negcon_protocol.c + src/sio.c) + target_include_directories(sio_negcon_protocol_multitap_test PRIVATE include) + target_compile_definitions(sio_negcon_protocol_multitap_test PRIVATE + PSX_MAX_PLAYERS=5) + add_test(NAME sio_negcon_protocol_multitap_test + COMMAND sio_negcon_protocol_multitap_test) + # The shared PsxNetPad -> SIO apply and normalization, over the real SIO. + add_executable(psx_pad_apply_test + tests/test_psx_pad_apply.c + src/psx_pad_apply.c + src/psx_net_pad.c + src/sio.c) + target_include_directories(psx_pad_apply_test PRIVATE include) + add_test(NAME psx_pad_apply_test COMMAND psx_pad_apply_test) + + # Title-supplied host rumble (psx_mod_set_host_rumble) merge and expiry. + add_executable(host_rumble_test tests/test_host_rumble.c) + target_include_directories(host_rumble_test PRIVATE include) + add_test(NAME host_rumble_test COMMAND host_rumble_test) + + add_executable(controller_type_test tests/test_controller_type.cpp) + target_include_directories(controller_type_test PRIVATE include) + add_test(NAME controller_type_test COMMAND controller_type_test) + add_executable(psx_stick_test tests/test_psx_stick.cpp src/psx_stick.c) + target_include_directories(psx_stick_test PRIVATE include) + add_test(NAME psx_stick_test COMMAND psx_stick_test) + + add_executable(openxr_input_test tests/test_openxr_input.c src/psx_openxr.c) + target_include_directories(openxr_input_test PRIVATE include) + if(NOT MSVC) + target_link_libraries(openxr_input_test PRIVATE m) + endif() + add_test(NAME openxr_input_test COMMAND openxr_input_test) + + add_executable(vr_pose_math_test tests/test_vr_pose_math.c src/vr_pose_math.c) + target_include_directories(vr_pose_math_test PRIVATE include) + if(NOT MSVC) + target_link_libraries(vr_pose_math_test PRIVATE m) + endif() + add_test(NAME vr_pose_math_test COMMAND vr_pose_math_test) + + add_executable(projection_scale_config_test + tests/test_projection_scale_config.cpp src/projection_scale_config.cpp) + target_include_directories(projection_scale_config_test PRIVATE + include ../recompiler/lib/toml11) + add_test(NAME projection_scale_config_test COMMAND projection_scale_config_test) + + add_executable(mod_controller_source_test tests/test_mod_controller_source.c src/mod_controller_source.c) + target_include_directories(mod_controller_source_test PRIVATE include) + add_test(NAME mod_controller_source_test COMMAND mod_controller_source_test) + + add_executable(mod_pad_transform_test + tests/test_mod_pad_transform.c src/mod_pad_transform.c) + target_include_directories(mod_pad_transform_test PRIVATE include) + add_test(NAME mod_pad_transform_test COMMAND mod_pad_transform_test) + add_executable(psx_hotkey_pad_test tests/test_psx_hotkey_pad.c) + target_include_directories(psx_hotkey_pad_test PRIVATE include) + add_test(NAME psx_hotkey_pad_test COMMAND psx_hotkey_pad_test) + add_executable(psx_trigger_values_test tests/test_psx_trigger_values.c) + target_include_directories(psx_trigger_values_test PRIVATE include) + add_test(NAME psx_trigger_values_test COMMAND psx_trigger_values_test) + add_executable(pad_external_input_test + tests/test_pad_external_input.c src/pad_external_input.c) + target_include_directories(pad_external_input_test PRIVATE include src) + add_test(NAME pad_external_input_test COMMAND pad_external_input_test) + add_executable(debug_socket_shutdown_test tests/test_debug_socket_shutdown.cpp) + target_include_directories(debug_socket_shutdown_test PRIVATE src) + find_package(Threads REQUIRED) + target_link_libraries(debug_socket_shutdown_test PRIVATE Threads::Threads) + if(WIN32) + target_link_libraries(debug_socket_shutdown_test PRIVATE ws2_32) + endif() + add_test(NAME debug_socket_shutdown_test COMMAND debug_socket_shutdown_test) + set_tests_properties(debug_socket_shutdown_test PROPERTIES TIMEOUT 15) + + add_executable(host_overlay_test tests/test_host_overlay.c src/host_overlay.c src/video_texture_filter.c) + target_include_directories(host_overlay_test PRIVATE include src) + find_package(Threads REQUIRED) + target_link_libraries(host_overlay_test PRIVATE Threads::Threads) + if(WIN32) + target_link_libraries(host_overlay_test PRIVATE shell32) + endif() + add_test(NAME host_overlay_test COMMAND host_overlay_test) + add_executable(psx_menu_host_test tests/test_psx_menu_host.c src/psx_menu_host.c) + target_include_directories(psx_menu_host_test PRIVATE include src) + add_test(NAME psx_menu_host_test COMMAND psx_menu_host_test) + + add_executable(gte_register_access_test + tests/test_gte_register_access.cpp + src/gte.cpp + src/pgxp.cpp + src/psx_ram_geometry.c + ) + target_include_directories(gte_register_access_test PRIVATE include) + target_compile_definitions(gte_register_access_test PRIVATE + PSX_NO_DEBUG_TOOLS=1 + PSX_GTE_REGISTER_TEST=1 + PSX_ENABLE_BLOCK_CYCLES=1) + add_test(NAME gte_register_access_test COMMAND gte_register_access_test) + # Same ABI/result corpus for the native operation entry and the reference. + add_executable(gte_hle_contract_test + tests/test_gte_register_access.cpp src/gte.cpp src/pgxp.cpp src/psx_ram_geometry.c) + target_include_directories(gte_hle_contract_test PRIVATE include) + target_compile_definitions(gte_hle_contract_test PRIVATE + PSX_NO_DEBUG_TOOLS=1 PSX_GTE_REGISTER_TEST=1 PSX_ENABLE_BLOCK_CYCLES=1) + psxrecomp_execution_profile(gte_hle_contract_test PROFILE ENHANCED) + add_test(NAME gte_hle_contract_test COMMAND gte_hle_contract_test) + + # PGXP value-propagation engine (docs/ENHANCEMENTS.md G1.2/G1.3): shadow + # load/store/COP2 roundtrips, half-word semantics, validate-on-read + # (foreign overwrite + DMA-overwrite rejection), MOVE/repack arithmetic, + # suppression bracket, invalidate-all, and GetPreciseVertex safeguards + # (truncation agreement + tolerance clamp). White-box: includes src/pgxp.cpp + # and stubs the gte.cpp fallback-cache extern. + add_executable(pgxp_test tests/test_pgxp.cpp src/pgxp.cpp + src/psx_ram_geometry.c) + target_include_directories(pgxp_test PRIVATE include) + add_test(NAME pgxp_test COMMAND pgxp_test) + + add_executable(pgxp_word_shadow_test tests/test_pgxp_word_shadow.cpp + src/pgxp.cpp src/psx_ram_geometry.c) + target_include_directories(pgxp_word_shadow_test PRIVATE include) + add_test(NAME pgxp_word_shadow_test COMMAND pgxp_word_shadow_test) + + # psx.enhancement.pgxp at session start (docs/ENHANCEMENTS.md G1.11): the + # real builtin activation (mod API stubbed) and the pgxp_session.h + # resolver main.cpp's renderer setup uses, in main.cpp's order. Pins that + # the [video] baseline no longer undoes the mod, that a cleared (netplay) + # plan keeps the baseline, and that the env overrides win. + # The session arming main.cpp runs (src/pgxp_session.cpp) against the + # real mod runtime, the real builtin plugin and the builtin's own + # manifest: default-on, netplay clear, the off switch, options, env + # overrides and [video] pgxp_mod_only. + # Run the same session fixture in both real compilation flavors. + foreach(_pgxp_session_test IN ITEMS pgxp_session_test pgxp_session_hooks_test) + add_executable(${_pgxp_session_test} tests/test_pgxp_session.cpp + src/pgxp_session.cpp src/mod_builtin_pgxp.c src/pgxp.cpp + src/psx_ram_geometry.c + src/mod_runtime.cpp + src/mod_packages.cpp + src/mod_media.cpp + src/disc_path.cpp + src/cue_sheet.cpp + src/iso_reader.cpp + src/crc32.c + src/psx_sha256.c) + target_include_directories(${_pgxp_session_test} PRIVATE + include + ../recompiler/include + ../recompiler/lib/toml11) + target_compile_definitions(${_pgxp_session_test} PRIVATE + PSX_BUILTIN_PGXP_MANIFEST="${CMAKE_CURRENT_SOURCE_DIR}/../mods/builtin/packages/psx.enhancement.pgxp/1.0.0/manifest.toml") + target_link_libraries(${_pgxp_session_test} PRIVATE chdr-static) + target_sources(${_pgxp_session_test} PRIVATE src/mod_netplay.c) + psxrecomp_sha256_file(${_pgxp_session_test}) + if(WIN32) + if(MSVC) + target_link_options(${_pgxp_session_test} PRIVATE /STACK:16777216) + else() + target_link_options(${_pgxp_session_test} PRIVATE -Wl,--stack,16777216) + endif() + endif() + add_test(NAME ${_pgxp_session_test} COMMAND ${_pgxp_session_test}) + if(_pgxp_session_test STREQUAL "pgxp_session_hooks_test") + target_compile_definitions(${_pgxp_session_test} PRIVATE PSX_PGXP=1) + endif() + endforeach() + + foreach(_pgxp_isolated_test + gte_register_access_test gte_hle_contract_test pgxp_test pgxp_session_test) + target_sources(${_pgxp_isolated_test} PRIVATE tests/pgxp_test_mod_memory.cpp) + endforeach() + + add_executable(psx_instr_cost_test tests/test_psx_instr_cost.cpp) + target_include_directories(psx_instr_cost_test PRIVATE include) + add_test(NAME psx_instr_cost_test COMMAND psx_instr_cost_test) + + add_executable(ws_aspect_cone_math_test + tests/test_ws_aspect_cone_math.c + src/ws_aspect_cone_math.c) + target_include_directories(ws_aspect_cone_math_test PRIVATE include) + if(NOT MSVC) + target_link_libraries(ws_aspect_cone_math_test PRIVATE m) + endif() + add_test(NAME ws_aspect_cone_math_test COMMAND ws_aspect_cone_math_test) + + add_executable(ws_ui_group_test + tests/test_ws_ui_group.c + src/ws_ui_group.c) + target_include_directories(ws_ui_group_test PRIVATE include) + add_test(NAME ws_ui_group_test COMMAND ws_ui_group_test) + + add_executable(ws_prepass_guard_test + tests/test_ws_prepass_guard.c) + target_include_directories(ws_prepass_guard_test PRIVATE include) + add_test(NAME ws_prepass_guard_test COMMAND ws_prepass_guard_test) + + add_executable(ws_hud_anchor_test + tests/test_ws_hud_anchor.c) + target_include_directories(ws_hud_anchor_test PRIVATE include) + add_test(NAME ws_hud_anchor_test COMMAND ws_hud_anchor_test) + + add_executable(gpu_textured_dot_nw_shift_exec_test + tests/test_gpu_textured_dot_nw_shift_exec.c + src/psx_video_timing.c + src/gpu_timeline.c) + target_include_directories(gpu_textured_dot_nw_shift_exec_test PRIVATE + include + src) + if(CMAKE_C_COMPILER_ID MATCHES "GNU|Clang") + target_compile_options(gpu_textured_dot_nw_shift_exec_test PRIVATE + -ffunction-sections -fdata-sections) + # Apple ld has no --gc-sections; -dead_strip is its equivalent (as + # for isc_store_test above), so the test links and runs on macOS. + target_link_options(gpu_textured_dot_nw_shift_exec_test PRIVATE + $,-Wl$-dead_strip,-Wl$--gc-sections>) + endif() + add_test(NAME gpu_textured_dot_nw_shift_exec_test + COMMAND gpu_textured_dot_nw_shift_exec_test) + + # auto_ui_squash corrects untextured HUD fills with their textured frames. + add_executable(ws_auto_ui_untextured_exec_test + tests/test_ws_auto_ui_untextured_exec.c + src/ws_ui_group.c + src/psx_video_timing.c + src/gpu_timeline.c) + target_include_directories(ws_auto_ui_untextured_exec_test PRIVATE + include + src) + if(CMAKE_C_COMPILER_ID MATCHES "GNU|Clang") + target_compile_options(ws_auto_ui_untextured_exec_test PRIVATE + -ffunction-sections -fdata-sections) + target_link_options(ws_auto_ui_untextured_exec_test PRIVATE + -Wl,--gc-sections) + endif() + add_test(NAME ws_auto_ui_untextured_exec_test + COMMAND ws_auto_ui_untextured_exec_test) + + add_executable(ws_shared_split_ui_exec_test + tests/test_ws_shared_split_ui_exec.c + src/ws_ui_group.c + src/psx_video_timing.c + src/gpu_timeline.c) + target_include_directories(ws_shared_split_ui_exec_test PRIVATE include src) + if(CMAKE_C_COMPILER_ID MATCHES "GNU|Clang") + target_compile_options(ws_shared_split_ui_exec_test PRIVATE + -ffunction-sections -fdata-sections) + target_link_options(ws_shared_split_ui_exec_test PRIVATE -Wl,--gc-sections) + endif() + add_test(NAME ws_shared_split_ui_exec_test COMMAND ws_shared_split_ui_exec_test) + + add_executable(ws_local_view_margin_test + tests/test_ws_local_view_margin.c + src/ws_ui_group.c + src/psx_video_timing.c + src/gpu_timeline.c) + target_include_directories(ws_local_view_margin_test PRIVATE include src) + if(CMAKE_C_COMPILER_ID MATCHES "GNU|Clang") + target_compile_options(ws_local_view_margin_test PRIVATE + -ffunction-sections -fdata-sections) + target_link_options(ws_local_view_margin_test PRIVATE + $,-Wl$-dead_strip,-Wl$--gc-sections>) + endif() + add_test(NAME ws_local_view_margin_test COMMAND ws_local_view_margin_test) + + # Depth24 GP1(07h) present hold (#636): hold count, upload span and the + # staged frame agree across windowed/headless/deferred presents, rollback + # replay and disk loads; stale trailing FMV pixels never reach a frame. + add_executable(depth24_present_hold_exec_test + tests/test_depth24_present_hold_exec.c + src/psx_video_timing.c + src/gpu_timeline.c) + target_include_directories(depth24_present_hold_exec_test PRIVATE include src) + if(MINGW) + target_compile_options(depth24_present_hold_exec_test PRIVATE -flto) + target_link_options(depth24_present_hold_exec_test PRIVATE -flto) + endif() + if(CMAKE_C_COMPILER_ID MATCHES "GNU|Clang") + target_compile_options(depth24_present_hold_exec_test PRIVATE + -ffunction-sections -fdata-sections) + target_link_options(depth24_present_hold_exec_test PRIVATE + $,-Wl$-dead_strip,-Wl$--gc-sections>) + endif() + add_test(NAME depth24_present_hold_exec_test COMMAND depth24_present_hold_exec_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 + src/gpu_timeline.c) + add_executable(native_wide_projection_test tests/test_native_wide_projection.c + src/psx_video_timing.c + src/gpu_timeline.c) + 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) + if(NOT APPLE) + target_link_options(${_anchor_test}_test PRIVATE -Wl,--gc-sections) + endif() + endif() + if(NOT WIN32) + target_link_libraries(${_anchor_test}_test PRIVATE m) + endif() + endforeach() + 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) + add_test(NAME ws_repeat_rect_test COMMAND ws_repeat_rect_test) + add_executable(ws_screen_mask_test tests/test_ws_screen_mask.c) + target_include_directories(ws_screen_mask_test PRIVATE include) + if(UNIX) + target_link_libraries(ws_screen_mask_test PRIVATE m) + endif() + add_test(NAME ws_screen_mask_test COMMAND ws_screen_mask_test) + + add_executable(mod_gpu_dma_aperture_test + tests/test_mod_gpu_dma_aperture.c + src/psx_ram_geometry.c) + target_include_directories(mod_gpu_dma_aperture_test PRIVATE include) + add_test(NAME mod_gpu_dma_aperture_test + COMMAND mod_gpu_dma_aperture_test) + + # The real-handler fixture relies on LTO pruning its unused syscall paths. + # Enable only the toolchain on which that link contract is verified; keep + # the coverage gap visible in CTest for every other compiler/platform. + if(WIN32 AND MINGW AND CMAKE_SIZEOF_VOID_P EQUAL 8 + AND CMAKE_C_COMPILER_ID STREQUAL "GNU" + AND CMAKE_C_COMPILER_VERSION VERSION_EQUAL "16.1.0") + add_executable(exit_critical_section_test + tests/test_exit_critical_section.c src/traps.c) + target_include_directories(exit_critical_section_test PRIVATE include) + target_compile_definitions(exit_critical_section_test PRIVATE PSX_NO_DEBUG_TOOLS=1) + target_compile_options(exit_critical_section_test PRIVATE -O3 -flto -ffunction-sections) + target_link_options(exit_critical_section_test PRIVATE -flto -Wl,--gc-sections) + add_test(NAME exit_critical_section_test COMMAND exit_critical_section_test) + else() + add_test(NAME exit_critical_section_test + COMMAND "${CMAKE_COMMAND}" -E echo + "SKIP: real-handler LTO fixture is verified only on Windows x64 MinGW GCC 16.1.0; current compiler is ${CMAKE_C_COMPILER_ID} ${CMAKE_C_COMPILER_VERSION}") + set_tests_properties(exit_critical_section_test PROPERTIES + SKIP_REGULAR_EXPRESSION "^SKIP:") + endif() + + # #636 / PR #660: restore-vs-live IRQ delivery at the restore edge, over + # the real scheduler (traps.c) and IRQ delivery (interrupts.c). Compiled + # game, BIOS ROM, CPS continuation and interpreted resumes; no pending + # IRQ; I_MASK/SR gating; same-edge dedup; null-PC recovery. + add_executable(restore_edge_irq_replay_test + tests/test_restore_edge_irq_replay.c + tests/restore_edge_irq_stubs.c + src/traps.c + src/interrupts.c) + target_include_directories(restore_edge_irq_replay_test PRIVATE include src) + add_test(NAME restore_edge_irq_replay_test COMMAND restore_edge_irq_replay_test) + set_tests_properties(restore_edge_irq_replay_test PROPERTIES TIMEOUT 60) + + add_executable(dma_gpu_linked_list_timing_test + tests/test_dma_gpu_linked_list_timing.c + src/dma_gpu_ll.c) + target_include_directories(dma_gpu_linked_list_timing_test PRIVATE include) + add_test(NAME dma_gpu_linked_list_timing_test + COMMAND dma_gpu_linked_list_timing_test) + + # Clean-room SPU DSP fidelity (issue #103): SPU IRQ on every RAM access + # class, reverb (write gating + work-area wrap + CD-only path), noise + # generator, volume sweeps / direct decode, capture buffers, and + # savestate coverage of all the new state. Includes src/spu.c directly + # (white-box) and stubs its externs. + # src/crc32.c is linked rather than stubbed: spu.c's capture/shadow paths + # feed real CRCs that this test checks, so a zero-returning stub would make + # those assertions vacuous. (This target did not link at all before — + # crc32_update was undefined on origin/master too.) + add_executable(spu_fidelity_test tests/test_spu_fidelity.c src/crc32.c) + target_include_directories(spu_fidelity_test PRIVATE include) + add_test(NAME spu_fidelity_test COMMAND spu_fidelity_test) + # Same story as sio_dualshock_rumble_test: registered but unlinkable on + # origin/master (crc32_update undefined), so it never ran. With crc32 linked + # it now runs 137 checks and fails 4, all in one ADSR sweep region + # (mid > 0, late > mid, fall_mid < 0x7FFE, fall_late < fall_mid). Rebuilding + # it against a zero-returning crc32 stub reproduces exactly those 4, so the + # crc32 linkage is not the cause — the envelope sweep genuinely disagrees. + set_tests_properties(spu_fidelity_test PROPERTIES DISABLED TRUE) + + add_executable(overlay_capture_retry_test + tests/test_overlay_capture_retry.cpp + src/overlay_capture.c + src/psx_ram_geometry.c) + target_include_directories(overlay_capture_retry_test PRIVATE + include ${PSX_SDL_INCLUDE_DIRS}) + target_compile_definitions(overlay_capture_retry_test PRIVATE + SDL_MAIN_HANDLED PSX_OVERLAY_CAPTURE_TEST=1 + $<$:PSX_SDL3=1>) + target_link_libraries(overlay_capture_retry_test PRIVATE ${PSX_SDL_LIBRARIES}) + add_test(NAME overlay_capture_retry_test COMMAND overlay_capture_retry_test) + + add_executable(overlay_path_canon_test + tests/test_overlay_path_canon.c + src/overlay_path_canon.c) + target_include_directories(overlay_path_canon_test PRIVATE include) + add_test(NAME overlay_path_canon_test COMMAND overlay_path_canon_test) + + # The kernel-bless verifier must accept a body whose DECLARED patch range + # the guest has overwritten (Psy-Q's install stubs land inside compiled + # kernel bodies) and still reject one changed anywhere else. Before this, + # the whole-body memcmp failed forever on the first install and the BIOS + # exception handler interpreted for the life of the process. + add_executable(kernel_patch_ranges_test + tests/test_kernel_patch_ranges.c + src/kernel_patch_ranges.c) + target_include_directories(kernel_patch_ranges_test PRIVATE include) + add_test(NAME kernel_patch_ranges_test COMMAND kernel_patch_ranges_test) + + # The player-facing "skip the BIOS" flag must resolve to the same boot + # behaviour on retail SCPH-1001 and on the bundled OpenBIOS, whose + # kernel-call HLE is (correctly) refused for want of a DeliverEvent anchor. + add_executable(bios_hle_plan_test + tests/test_bios_hle_plan.c + src/bios_hle_plan.c) + target_include_directories(bios_hle_plan_test PRIVATE include) + add_test(NAME bios_hle_plan_test COMMAND bios_hle_plan_test) + + # A cue, conventional raw image, or Steam .car image is a valid pick; each + # must mount the same disc and identify off the same data track. + add_executable(sbi_setup_test tests/test_sbi_setup.cpp src/psx_sha256.c) + target_include_directories(sbi_setup_test PRIVATE include) + add_test(NAME sbi_setup_test COMMAND sbi_setup_test ${CMAKE_CURRENT_BINARY_DIR}/sbi-setup-fixture) + + add_executable(disc_path_resolve_test + tests/test_disc_path_resolve.cpp + src/disc_path.cpp + src/cue_sheet.cpp + src/iso_reader.cpp) + target_include_directories(disc_path_resolve_test PRIVATE include ${PSXRECOMP_ROOT}/recompiler/include) + target_link_libraries(disc_path_resolve_test PRIVATE chdr-static) + add_test(NAME disc_path_resolve_test COMMAND disc_path_resolve_test) + + # Both BIOS filename conventions (bare vs region-qualified) resolve, in + # both directions, so a config and a dump folder may disagree. + add_executable(bios_rom_alias_test tests/test_bios_rom_alias.cpp) + target_include_directories(bios_rom_alias_test PRIVATE + ${PSXRECOMP_ROOT}/recompiler/include) + add_test(NAME bios_rom_alias_test COMMAND bios_rom_alias_test) + + # The game window opens at a normal size, not maximised. + add_executable(window_size_test tests/test_window_size.cpp) + target_include_directories(window_size_test PRIVATE include) + add_test(NAME window_size_test COMMAND window_size_test) + option(PSX_WINDOW_FULLSCREEN_TEST "Run fullscreen transitions on a real desktop" OFF) + if(PSX_WINDOW_FULLSCREEN_TEST) + add_executable(window_fullscreen_test tests/test_window_fullscreen.cpp) + target_include_directories(window_fullscreen_test PRIVATE include) + target_compile_definitions(window_fullscreen_test PRIVATE + SDL_MAIN_HANDLED=1 $<$:PSX_SDL3=1>) + target_link_libraries(window_fullscreen_test PRIVATE ${PSX_SDL_LIBRARIES}) + add_test(NAME window_fullscreen_test COMMAND window_fullscreen_test) + set_tests_properties(window_fullscreen_test PROPERTIES LABELS "window;hardware" TIMEOUT 30) + endif() + + # Multi-file cue TOC mapping (data track + CD-DA in separate bins). + add_executable(iso_reader_cdda_test + tests/test_iso_reader_cdda.cpp + src/iso_reader.cpp + src/cue_sheet.cpp) + target_include_directories(iso_reader_cdda_test PRIVATE include ${PSXRECOMP_ROOT}/recompiler/include) + target_link_libraries(iso_reader_cdda_test PRIVATE chdr-static) + add_test(NAME iso_reader_cdda_test COMMAND iso_reader_cdda_test) + + # Cue PREGAP/POSTGAP (gaps the dump does not store) shift the disc layout. + add_executable(iso_reader_pregap_test + tests/test_iso_reader_pregap.cpp + src/iso_reader.cpp + src/cue_sheet.cpp) + target_include_directories(iso_reader_pregap_test PRIVATE include ${PSXRECOMP_ROOT}/recompiler/include) + target_link_libraries(iso_reader_pregap_test PRIVATE chdr-static) + add_test(NAME iso_reader_pregap_test COMMAND iso_reader_pregap_test) + + # Raw vs cooked geometry comes from the cue TRACK mode / sector-0 sync, + # never from file length. + add_executable(iso_reader_geometry_test + tests/test_iso_reader_geometry.cpp + src/iso_reader.cpp + src/cue_sheet.cpp) + target_include_directories(iso_reader_geometry_test PRIVATE include ${PSXRECOMP_ROOT}/recompiler/include) + target_link_libraries(iso_reader_geometry_test PRIVATE chdr-static) + add_test(NAME iso_reader_geometry_test COMMAND iso_reader_geometry_test) + + add_executable(iso_reader_sbi_test + tests/test_iso_reader_sbi.cpp + src/iso_reader.cpp + src/cue_sheet.cpp) + target_include_directories(iso_reader_sbi_test PRIVATE include ${PSXRECOMP_ROOT}/recompiler/include) + target_link_libraries(iso_reader_sbi_test PRIVATE chdr-static) + add_test(NAME iso_reader_sbi_test COMMAND iso_reader_sbi_test) + + add_executable(mod_packages_test + tests/test_mod_packages.cpp + src/mod_packages.cpp + src/mod_media.cpp src/iso_reader.cpp src/cue_sheet.cpp + src/crc32.c + src/psx_sha256.c) + target_include_directories(mod_packages_test PRIVATE + include + ../recompiler/include + ../recompiler/lib/toml11) + target_link_libraries(mod_packages_test PRIVATE chdr-static) + add_test(NAME mod_packages_test COMMAND mod_packages_test) + + add_executable(builtin_mod_manifests_test + tests/test_builtin_mod_manifests.cpp + src/mod_packages.cpp + src/mod_media.cpp src/iso_reader.cpp src/cue_sheet.cpp + src/crc32.c + src/psx_sha256.c) + target_include_directories(builtin_mod_manifests_test PRIVATE + include + ../recompiler/include + ../recompiler/lib/toml11) + target_link_libraries(builtin_mod_manifests_test PRIVATE chdr-static) + add_test(NAME builtin_mod_manifests_test + COMMAND builtin_mod_manifests_test + "${CMAKE_CURRENT_SOURCE_DIR}/../mods/builtin/packages") + + add_executable(mod_media_test + tests/test_mod_media.cpp src/mod_media.cpp src/iso_reader.cpp + src/cue_sheet.cpp src/psx_sha256.c) + target_include_directories(mod_media_test PRIVATE include ../recompiler/include) + target_link_libraries(mod_media_test PRIVATE chdr-static) + add_test(NAME mod_media_test COMMAND mod_media_test) + + add_executable(text_xlate_fix_only_test + tests/test_text_xlate_fix_only.cpp + src/text_xlate.cpp + src/psx_ram_geometry.c) + target_include_directories(text_xlate_fix_only_test PRIVATE + include + ../recompiler/lib/toml11) + add_test(NAME text_xlate_fix_only_test COMMAND text_xlate_fix_only_test) + + add_executable(netplay_peer_frontier_test tests/test_netplay_peer_frontier.c) + target_include_directories(netplay_peer_frontier_test PRIVATE include) + add_test(NAME netplay_peer_frontier_test COMMAND netplay_peer_frontier_test) + + add_executable(netplay_content_gate_test tests/test_netplay_content_gate.c) + target_include_directories(netplay_content_gate_test PRIVATE include) + add_test(NAME netplay_content_gate_test COMMAND netplay_content_gate_test) + + add_executable(mod_runtime_test + tests/test_mod_runtime.cpp + src/iso_reader_c.cpp + src/mod_runtime.cpp + src/mod_netplay.c + src/mod_packages.cpp + src/mod_media.cpp + src/disc_path.cpp + src/cue_sheet.cpp + src/iso_reader.cpp + src/crc32.c + src/psx_sha256.c) + target_include_directories(mod_runtime_test PRIVATE + include + ../recompiler/include + ../recompiler/lib/toml11) + target_link_libraries(mod_runtime_test PRIVATE chdr-static) + psxrecomp_sha256_file(mod_runtime_test) + # Match the runtime's stack allowance for the remaining disc/mod helpers. + if(WIN32) + if(MSVC) + target_link_options(mod_runtime_test PRIVATE /STACK:16777216) + else() + target_link_options(mod_runtime_test PRIVATE -Wl,--stack,16777216) + endif() + endif() + add_test(NAME mod_runtime_test COMMAND mod_runtime_test) + add_test(NAME mod_netplay_missing_plugin_test COMMAND mod_runtime_test --missing-profile) + add_test(NAME mod_netplay_missing_renderer_test COMMAND mod_runtime_test --missing-renderer) + add_test(NAME mod_netplay_profile_options_test COMMAND mod_runtime_test --profile-options) + add_test(NAME mod_netplay_option_renderer_missing_test COMMAND mod_runtime_test --option-renderer-missing) + + add_executable(neversoft_helpers_test tests/test_neversoft_helpers.cpp) + target_include_directories(neversoft_helpers_test PRIVATE include) + target_compile_features(neversoft_helpers_test PRIVATE cxx_std_17) + add_test(NAME neversoft_helpers_test COMMAND neversoft_helpers_test) + + add_executable(aligned_archive_test tests/test_aligned_archive.cpp) + target_include_directories(aligned_archive_test PRIVATE include) + target_compile_features(aligned_archive_test PRIVATE cxx_std_17) + add_test(NAME aligned_archive_test COMMAND aligned_archive_test) + + add_executable(resident_sector_test tests/test_resident_sector.cpp) + target_include_directories(resident_sector_test PRIVATE include) + target_compile_features(resident_sector_test PRIVATE cxx_std_17) + add_test(NAME resident_sector_test COMMAND resident_sector_test) + add_executable(netplay_view_mods_test + tests/test_netplay_view_mods.cpp + src/iso_reader_c.cpp + src/mod_runtime.cpp + src/mod_netplay.c + src/mod_packages.cpp + src/mod_media.cpp + src/disc_path.cpp + src/cue_sheet.cpp + src/iso_reader.cpp + src/crc32.c + src/psx_sha256.c + src/sio.c) + target_include_directories(netplay_view_mods_test PRIVATE + include + ../recompiler/include + ../recompiler/lib/toml11) + target_link_libraries(netplay_view_mods_test PRIVATE chdr-static) + psxrecomp_sha256_file(netplay_view_mods_test) + if(WIN32) + if(MSVC) + target_link_options(netplay_view_mods_test PRIVATE /STACK:16777216) + else() + target_link_options(netplay_view_mods_test PRIVATE -Wl,--stack,16777216) + endif() + endif() + add_test(NAME netplay_view_mods_test COMMAND netplay_view_mods_test) + get_target_property(mod_runtime_sources mod_runtime_test SOURCES) + add_executable(mod_runtime_cache_test ${mod_runtime_sources}) + target_include_directories(mod_runtime_cache_test PRIVATE + include ../recompiler/include ../recompiler/lib/toml11) + target_compile_definitions(mod_runtime_cache_test PRIVATE + PSX_LAUNCHER_MOD_COMMIT_WORKER_SAFE=1) + target_link_libraries(mod_runtime_cache_test PRIVATE chdr-static) + psxrecomp_sha256_file(mod_runtime_cache_test) + if(WIN32) + if(MSVC) + target_link_options(mod_runtime_cache_test PRIVATE /STACK:16777216) + else() + target_link_options(mod_runtime_cache_test PRIVATE -Wl,--stack,16777216) + endif() + endif() + add_test(NAME mod_runtime_cache_test COMMAND mod_runtime_cache_test) + + add_executable(mod_resident_test + tests/test_mod_resident.cpp + src/mod_resident.cpp + src/mod_runtime.cpp + src/mod_netplay.c + src/mod_packages.cpp + src/mod_media.cpp + src/disc_path.cpp + src/cue_sheet.cpp + src/iso_reader.cpp + src/crc32.c + src/psx_sha256.c) + target_include_directories(mod_resident_test PRIVATE + include + ../recompiler/include + ../recompiler/lib/toml11) + target_link_libraries(mod_resident_test PRIVATE chdr-static) + psxrecomp_sha256_file(mod_resident_test) + if(WIN32) + if(MSVC) + target_link_options(mod_resident_test PRIVATE /STACK:16777216) + else() + target_link_options(mod_resident_test PRIVATE -Wl,--stack,16777216) + endif() + endif() + add_test(NAME mod_resident_test COMMAND mod_resident_test) + + if(WIN32) + add_executable(autocompile_publication_test + tests/test_autocompile_publication.c + src/autocompile.c) + target_include_directories(autocompile_publication_test PRIVATE include) + target_compile_definitions(autocompile_publication_test PRIVATE + PSX_AUTOCOMPILE_TEST=1) + add_test(NAME autocompile_publication_test + COMMAND autocompile_publication_test) + + # A misconfigured overlay_autocompile_cmd must be reported on a channel + # that exists: the stdout warnings reach nobody under -mwindows, so a + # broken autocompile silently degrades the whole run to the interpreter. + add_executable(autocompile_degraded_test + tests/test_autocompile_degraded.c + src/autocompile.c) + target_include_directories(autocompile_degraded_test PRIVATE include) + add_test(NAME autocompile_degraded_test + COMMAND autocompile_degraded_test) + endif() + + # ---- previously-unregistered standalone tests ------------------------- + # These 16 existed in runtime/tests/ with no add_test() entry. Their only + # build instructions were a gcc command line in each file's header comment, + # which nothing executes — so when the runtime API moved under them they + # broke silently and stayed broken. Four had already rotted when they were + # collected here (2026-08-11): + # + # test_netplay_snap_ring.c duplicate TU in the documented command; + # no stubs for the VRAM-incremental + # boot_state queries (PR #134) + # test_psx_cycle_event_boundaries.c + # psx_cycles_resync_after_restore() grew a + # CPUState* parameter; psx_cycles.c grew + # three externs the stub block never did + # test_spu_end_without_repeat.c spu.c grew psx_irq_raise / crc32_update + # test_sio_card_protocol.c repaired below: the harness now walks + # shift/ACK edges and clears INTC IRQ7 + # + # Registering them is only half the fix; check_test_registration.cmake at + # the end of this file is what stops the next one from going unnoticed. + # Each test's build recipe now lives HERE and nowhere else — the header + # comments were reduced to a `ctest -R ` pointer so there is exactly + # one description of how a test is built, and it is one the build executes. + + add_executable(cdrom_irq_mask_test tests/test_cdrom_irq_mask.c) + target_include_directories(cdrom_irq_mask_test PRIVATE src) + add_test(NAME cdrom_irq_mask_test COMMAND cdrom_irq_mask_test) + + add_executable(cdrom_sector_fifo_test tests/test_cdrom_sector_fifo.c) + target_include_directories(cdrom_sector_fifo_test PRIVATE include src) + add_test(NAME cdrom_sector_fifo_test COMMAND cdrom_sector_fifo_test) + + add_executable(cdrom_lid_state_test tests/test_cdrom_lid_state.c) + target_include_directories(cdrom_lid_state_test PRIVATE src) + add_test(NAME cdrom_lid_state_test COMMAND cdrom_lid_state_test) + + # Cross-device I_STAT ownership: a memory-card ACK IRQ (SIO0) must not + # clear another device's pending source — pinned on SPU (I_STAT bit 9) + # staying set across card ACK delivery until the guest acknowledges it. + # Scratch working dir for the same reason as sio_card_protocol_test: the + # test self-provisions test_dir/card1.mcd relative to its cwd. + add_executable(sio_spu_istat_ownership_test + tests/test_sio_spu_istat_ownership.c + src/sio.c + src/memcard.c) + target_include_directories(sio_spu_istat_ownership_test PRIVATE include src) + file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/sio_spu_istat_scratch) + add_test(NAME sio_spu_istat_ownership_test COMMAND sio_spu_istat_ownership_test + WORKING_DIRECTORY + ${CMAKE_CURRENT_BINARY_DIR}/sio_spu_istat_scratch) + + # Mirrors func_00005A08 (BIOS card READ chain handler) against a mock CPU, + # so it pins the disassembly reading without linking the recompiled BIOS. + add_executable(chain_handler_check_test tests/test_chain_handler_check.c) + target_include_directories(chain_handler_check_test PRIVATE include src) + add_test(NAME chain_handler_check_test COMMAND chain_handler_check_test) + + # Bug B: the pacing decision must take ONE counter sample, so the unsigned + # subtraction cannot underflow into a ~24.7-day SDL_Delay. + add_executable(frame_pacing_test tests/test_frame_pacing.c) + target_include_directories(frame_pacing_test PRIVATE include) + add_test(NAME frame_pacing_test COMMAND frame_pacing_test) + # Render thread core ([video] render_thread, docs/RENDER_THREAD.md): + # ordering across ring wraps, the frame bound, stale-frame detection and + # the drain + context hand-off sync point, against a fake executor. + # The fixture itself uses POSIX threads and nanosleep (MinGW has both; + # the core takes its Win32 path there). + if(NOT MSVC) + add_executable(render_thread_test tests/test_render_thread.c src/render_thread.c) + target_include_directories(render_thread_test PRIVATE include) + find_package(Threads REQUIRED) + target_link_libraries(render_thread_test PRIVATE Threads::Threads) + add_test(NAME render_thread_test COMMAND render_thread_test) + # Present thread core ([video] present_thread): submission order is + # presentation order, slots are never composed while queued or on + # screen, done tokens reach the next composer, stop drains. + add_executable(present_thread_test tests/test_present_thread.c src/present_thread.c) + target_include_directories(present_thread_test PRIVATE include) + target_link_libraries(present_thread_test PRIVATE Threads::Threads) + add_test(NAME present_thread_test COMMAND present_thread_test) + endif() + + # Same family of bug as frame_pacing: the starvation watchdog's staleness + # gap must not wrap when the (cross-thread) heartbeat is newer than the + # clock sample taken by the emu thread. + add_executable(starvation_watchdog_test tests/test_starvation_watchdog.c) + target_include_directories(starvation_watchdog_test PRIVATE include) + add_test(NAME starvation_watchdog_test COMMAND starvation_watchdog_test) + + add_executable(freeze_dump_policy_test + tests/test_freeze_dump_policy.c + src/freeze_dump_policy.c) + target_include_directories(freeze_dump_policy_test PRIVATE src) + add_test(NAME freeze_dump_policy_test COMMAND freeze_dump_policy_test) + + add_executable(frame_interpolation_schedule_test + tests/test_frame_interpolation_schedule.c + src/frame_interpolation.c) + target_include_directories(frame_interpolation_schedule_test PRIVATE include) + if(NOT MSVC) + target_link_libraries(frame_interpolation_schedule_test PRIVATE m) + endif() + add_test(NAME frame_interpolation_schedule_test + COMMAND frame_interpolation_schedule_test) + + add_executable(render_pass_plan_test + tests/test_render_pass_plan.c + src/render_pass_plan.c) + target_include_directories(render_pass_plan_test PRIVATE include) + if(NOT MSVC) + target_link_libraries(render_pass_plan_test PRIVATE m) + endif() + add_test(NAME render_pass_plan_test COMMAND render_pass_plan_test) + + add_executable(mod_internal_resolution_test + tests/test_mod_internal_resolution.c + src/mod_internal_resolution.c) + target_include_directories(mod_internal_resolution_test PRIVATE include) + add_test(NAME mod_internal_resolution_test COMMAND mod_internal_resolution_test) + + # Motion kit and per-frame driver for render-pass plugins: blending, + # identity matching, limits and the crossfade fallback, on mock RAM. + add_executable(render_pass_motion_test + tests/test_render_pass_motion.c + src/render_pass_motion.c + src/render_pass_frame.c) + target_include_directories(render_pass_motion_test PRIVATE include) + if(NOT MSVC) + target_link_libraries(render_pass_motion_test PRIVATE m) + endif() + add_test(NAME render_pass_motion_test COMMAND render_pass_motion_test) + add_executable(render_pass_projection_test + tests/test_render_pass_projection.cpp + src/render_pass_projection.cpp + src/render_pass_motion.c) + target_include_directories(render_pass_projection_test PRIVATE include) + if(NOT MSVC) + target_link_libraries(render_pass_projection_test PRIVATE m) + endif() + add_test(NAME render_pass_projection_test COMMAND render_pass_projection_test) + # Dynamic resolution's controller against synthetic load: step rules, + # hysteresis, transient spikes, CPU-bound stretches, holds, the floor. + add_executable(dynamic_resolution_test + tests/test_dynamic_resolution.c + src/dynamic_resolution.c) + target_include_directories(dynamic_resolution_test PRIVATE include) + if(NOT MSVC) + target_link_libraries(dynamic_resolution_test PRIVATE m) + endif() + add_test(NAME dynamic_resolution_test COMMAND dynamic_resolution_test) + # The render-thread controller (dynrt_*) against a synthetic pipeline. + add_executable(dynamic_resolution_rt_test + tests/test_dynamic_resolution_rt.c + src/dynamic_resolution.c) + target_include_directories(dynamic_resolution_rt_test PRIVATE include) + if(NOT MSVC) + target_link_libraries(dynamic_resolution_rt_test PRIVATE m) + endif() + add_test(NAME dynamic_resolution_rt_test COMMAND dynamic_resolution_rt_test) + # Frame generation's matching, interpolation, plan and breaker. + add_executable(frame_gen_test tests/test_frame_gen.c src/frame_gen.c) + target_include_directories(frame_gen_test PRIVATE include) + if(NOT MSVC) + target_link_libraries(frame_gen_test PRIVATE m) + endif() + add_test(NAME frame_gen_test COMMAND frame_gen_test) + + # Post-effect chain presets (postfx_chain.c alone; no GL). + add_executable(postfx_chain_test tests/test_postfx_chain.c src/postfx_chain.c) + target_include_directories(postfx_chain_test PRIVATE include) + add_test(NAME postfx_chain_test COMMAND postfx_chain_test + WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}") + + # Render-pass time freeze against mock devices (psx_cycles.c only). + add_executable(render_pass_freeze_test + tests/test_render_pass_freeze.c + src/psx_cycles.c) + target_include_directories(render_pass_freeze_test PRIVATE include) + add_test(NAME render_pass_freeze_test COMMAND render_pass_freeze_test) + + # Uncharged guest-time spans (psx_mod_call_guest_uncharged's time layer) + # against the same mock devices. + add_executable(uncharged_span_test + tests/test_uncharged_span.c + src/psx_cycles.c) + target_include_directories(uncharged_span_test PRIVATE include) + add_test(NAME uncharged_span_test COMMAND uncharged_span_test) + + # Render-pass watchdog abort from nested guest frames: the real + # render_pass.c and psx_cycles.c against mock memory, devices and + # presenter (the frames use GCC/Clang cleanup handlers, as generated + # code does). + if(NOT MSVC) + add_executable(render_pass_abort_test + tests/test_render_pass_abort.c + src/render_pass.c + src/psx_cycles.c) + target_include_directories(render_pass_abort_test PRIVATE include) + add_test(NAME render_pass_abort_test COMMAND render_pass_abort_test) + endif() + + # Render-pass sandbox: the store policy memory.c uses, a real pass + # (render_pass.c, psx_cycles.c, timers.c) that makes device stores and + # runs frozen cycles, and the out-of-rect VRAM journal. + add_executable(render_pass_sandbox_test + tests/test_render_pass_sandbox.c + src/render_pass.c + src/render_pass_plan.c + src/psx_cycles.c + src/timers.c + src/psx_video_timing.c) + target_include_directories(render_pass_sandbox_test PRIVATE include) + if(NOT MSVC) + target_link_libraries(render_pass_sandbox_test PRIVATE m) + endif() + add_test(NAME render_pass_sandbox_test COMMAND render_pass_sandbox_test) + + add_executable(render_pass_ot_test tests/test_render_pass_ot.cpp) + target_include_directories(render_pass_ot_test PRIVATE include) + target_compile_features(render_pass_ot_test PRIVATE cxx_std_17) + add_test(NAME render_pass_ot_test COMMAND render_pass_ot_test) + + foreach(_submit_profile ENHANCED REFERENCE) + string(TOLOWER "${_submit_profile}" _submit_suffix) + set(_submit_test "render_pass_gpu_submit_${_submit_suffix}_test") + add_executable(${_submit_test} ../tests/test_render_pass_gpu_submit.cpp src/dma_gpu_ll.c) + target_include_directories(${_submit_test} PRIVATE include) + psxrecomp_execution_profile(${_submit_test} PROFILE ${_submit_profile}) + add_test(NAME ${_submit_test} COMMAND ${_submit_test}) + endforeach() + + add_executable(gpu_primitive_reject_test tests/test_gpu_primitive_reject.c) + target_include_directories(gpu_primitive_reject_test PRIVATE include) + add_test(NAME gpu_primitive_reject_test COMMAND gpu_primitive_reject_test) + + add_executable(gpu_sw_edges_test tests/test_gpu_sw_edges.c) + target_include_directories(gpu_sw_edges_test PRIVATE src) + add_test(NAME gpu_sw_edges_test COMMAND gpu_sw_edges_test) + + add_executable(gpu_uv_test tests/test_gpu_uv.c) + target_include_directories(gpu_uv_test PRIVATE src) + add_test(NAME gpu_uv_test COMMAND gpu_uv_test) + + # Netplay CPU-authoritative VRAM ignores PGXP overrides and texture + # filtering (sw_set_faithful_authority). + add_executable(sw_faithful_authority_test tests/test_sw_faithful_authority.c) + target_include_directories(sw_faithful_authority_test PRIVATE include src) + if(NOT WIN32) + target_link_libraries(sw_faithful_authority_test PRIVATE m) + endif() + add_test(NAME sw_faithful_authority_test COMMAND sw_faithful_authority_test) + + add_executable(gpu_vk_upload_alignment_test + tests/test_gpu_vk_upload_alignment.c) + target_include_directories(gpu_vk_upload_alignment_test PRIVATE src) + add_test(NAME gpu_vk_upload_alignment_test + COMMAND gpu_vk_upload_alignment_test) + + # Portable rollback helpers — required for MotK netplay unit tests that use + # the netplay_* compatibility shims. The peer-facing ones (hash_confirm, + # input_hist, rb_post) live in recomp-net; the snap ring in retcomm-rbengine. + set(_RBE_ROOT "${RECOMP_RBENGINE_ROOT}") + if(NOT _RBE_ROOT) + set(_RBE_ROOT "${PSXRECOMP_ROOT}/lib/retcomm-rbengine") + endif() + set(_RNET_ROOT "${RECOMP_NET_ROOT}") + if(NOT _RNET_ROOT) + set(_RNET_ROOT "${PSXRECOMP_ROOT}/lib/recomp-net") + endif() + if(EXISTS "${_RBE_ROOT}/CMakeLists.txt" AND EXISTS "${_RNET_ROOT}/CMakeLists.txt") + if(NOT TARGET recomp_net) + set(RNET_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE) + set(RNET_BUILD_TESTS OFF CACHE BOOL "" FORCE) + set(RNET_ENABLE_ICE OFF CACHE BOOL "" FORCE) + add_subdirectory("${_RNET_ROOT}" "${CMAKE_BINARY_DIR}/recomp-net-tests") + endif() + if(NOT TARGET retcomm_rbengine) + set(RBE_BUILD_TESTS OFF CACHE BOOL "" FORCE) + set(RECOMP_NET_ROOT "${_RNET_ROOT}" CACHE PATH "" FORCE) + add_subdirectory("${_RBE_ROOT}" "${CMAKE_BINARY_DIR}/retcomm-rbengine-tests") + endif() + set(PSXRECOMP_TEST_HAS_RBENGINE TRUE) + else() + set(PSXRECOMP_TEST_HAS_RBENGINE FALSE) + message(STATUS + "netplay_*_tests: lib/retcomm-rbengine or lib/recomp-net absent - skipped") + endif() + + add_executable(netplay_load_probe_test + tests/test_netplay_load_probe.c) + target_include_directories(netplay_load_probe_test PRIVATE include) + add_test(NAME netplay_load_probe_test COMMAND netplay_load_probe_test) + + if(PSXRECOMP_TEST_HAS_RBENGINE) + add_executable(netplay_hash_confirm_test + tests/test_netplay_hash_confirm.c) + target_include_directories(netplay_hash_confirm_test PRIVATE include) + target_link_libraries(netplay_hash_confirm_test PRIVATE recomp_net) + add_test(NAME netplay_hash_confirm_test COMMAND netplay_hash_confirm_test) + + # save/load glue #included from the test; ring ops come from rbengine. + add_executable(netplay_snap_ring_test tests/test_netplay_snap_ring.c) + target_include_directories(netplay_snap_ring_test PRIVATE include) + target_compile_definitions(netplay_snap_ring_test PRIVATE + NETPLAY_SNAP_RING_TEST_STUB_BOOT_STATE + PSX_HAS_RECOMP_NET=1) + target_link_libraries(netplay_snap_ring_test PRIVATE retcomm_rbengine) + add_test(NAME netplay_snap_ring_test COMMAND netplay_snap_ring_test) + + # Lobby chat ring. #includes psx_lobby_client.c, so it needs the same + # guard define and include roots the runtime build uses; the two WS + # calls that TU makes are stubbed in the test and abort if reached. + if(EXISTS "${PSXRECOMP_ROOT}/runtime/src/lobby_ws/rnet_ws.c") + add_executable(lobby_chat_ring_test tests/test_lobby_chat_ring.c + src/mod_netplay.c src/psx_sha256.c) + target_include_directories(lobby_chat_ring_test PRIVATE + include + src + "${PSXRECOMP_ROOT}/runtime/src/lobby_ws" + ${_RNET_ROOT}/include) + target_compile_definitions(lobby_chat_ring_test PRIVATE + PSX_HAS_LOBBY_CLIENT=1 + PSX_HAS_RECOMP_NET=1) + # Links the REAL recomp_net and the real WS helpers rather than a + # wall of stubs. Nothing in this test reaches a socket, so no fake + # is needed and none can drift from what the runtime links. + target_sources(lobby_chat_ring_test PRIVATE + "${PSXRECOMP_ROOT}/runtime/src/lobby_ws/rnet_ws.c" + "${PSXRECOMP_ROOT}/runtime/src/lobby_ws/rnet_sha1.c" + "${PSXRECOMP_ROOT}/runtime/src/netplay_bios_settle.c") + target_link_libraries(lobby_chat_ring_test PRIVATE recomp_net) + add_test(NAME lobby_chat_ring_test COMMAND lobby_chat_ring_test) + add_executable(netplay_bios_wire_test + tests/test_netplay_bios_wire.c + src/mod_netplay.c + src/psx_sha256.c + src/netplay_bios_settle.c + src/lobby_ws/rnet_ws.c + src/lobby_ws/rnet_sha1.c) + target_include_directories(netplay_bios_wire_test PRIVATE + include src src/lobby_ws ${_RNET_ROOT}/include) + target_compile_definitions(netplay_bios_wire_test PRIVATE + PSX_HAS_LOBBY_CLIENT=1 PSX_HAS_RECOMP_NET=1) + target_link_libraries(netplay_bios_wire_test PRIVATE recomp_net) + add_test(NAME netplay_bios_wire_test COMMAND netplay_bios_wire_test) + endif() + + add_executable(rb_post_tip_filter_test tests/test_rb_post_tip_filter.c) + target_include_directories(rb_post_tip_filter_test PRIVATE include) + target_link_libraries(rb_post_tip_filter_test PRIVATE recomp_net) + add_test(NAME rb_post_tip_filter_test COMMAND rb_post_tip_filter_test) + + add_executable(netplay_input_hist_test + tests/test_netplay_input_hist.c + src/netplay_input_hist.c + src/psx_net_pad.c) + target_include_directories(netplay_input_hist_test PRIVATE + include + ${_RNET_ROOT}/include) + target_compile_definitions(netplay_input_hist_test PRIVATE + PSX_HAS_RECOMP_NET=1) + target_link_libraries(netplay_input_hist_test PRIVATE recomp_net) + add_test(NAME netplay_input_hist_test COMMAND netplay_input_hist_test) + endif() + + add_executable(overlay_store_cycle_barrier_test + tests/test_overlay_store_cycle_barrier.c) + target_include_directories(overlay_store_cycle_barrier_test PRIVATE include) + add_test(NAME overlay_store_cycle_barrier_test + COMMAND overlay_store_cycle_barrier_test) + + # Overlay stores update the host's store-PC breadcrumb (ABI v24). Built + # with the defines compile_overlays.py gives every shard. + add_executable(overlay_store_pc_forwarding_test + tests/test_overlay_store_pc_forwarding.c) + target_include_directories(overlay_store_pc_forwarding_test PRIVATE include) + target_compile_definitions(overlay_store_pc_forwarding_test PRIVATE + PSX_OVERLAY_DLL_BUILD PSX_NO_DEBUG_TOOLS PSX_ENABLE_BLOCK_CYCLES=1) + add_test(NAME overlay_store_pc_forwarding_test + COMMAND overlay_store_pc_forwarding_test) + + # frame_fingerprint judge columns are blind to device-write service order. + add_executable(frame_fingerprint_test tests/test_frame_fingerprint.c) + target_include_directories(frame_fingerprint_test PRIVATE include) + add_test(NAME frame_fingerprint_test COMMAND frame_fingerprint_test) + + add_executable(psx_cycle_event_boundaries_test + tests/test_psx_cycle_event_boundaries.c + src/psx_cycles.c) + target_include_directories(psx_cycle_event_boundaries_test PRIVATE include) + add_test(NAME psx_cycle_event_boundaries_test + COMMAND psx_cycle_event_boundaries_test) + + add_executable(psx_vblank_clock_test + tests/test_psx_vblank_clock.c + src/psx_vblank_clock.c) + target_include_directories(psx_vblank_clock_test PRIVATE include) + add_test(NAME psx_vblank_clock_test COMMAND psx_vblank_clock_test) + + add_executable(psx_video_timing_test + tests/test_psx_video_timing.c + src/psx_video_timing.c) + target_include_directories(psx_video_timing_test PRIVATE include) + add_test(NAME psx_video_timing_test COMMAND psx_video_timing_test) + + add_executable(spu_end_without_repeat_test + tests/test_spu_end_without_repeat.c) + target_include_directories(spu_end_without_repeat_test PRIVATE include) + add_test(NAME spu_end_without_repeat_test + COMMAND spu_end_without_repeat_test) + + add_executable(spu_pitch_zero_test + tests/test_spu_pitch_zero.c) + target_include_directories(spu_pitch_zero_test PRIVATE include) + add_test(NAME spu_pitch_zero_test + COMMAND spu_pitch_zero_test) + + # Guest-clocked SPU: accesses advance it to the current cycle (beads-eio.3.321). + add_executable(spu_guest_clock_test tests/test_spu_guest_clock.c) + target_include_directories(spu_guest_clock_test PRIVATE include) + add_test(NAME spu_guest_clock_test COMMAND spu_guest_clock_test) + add_executable(spu_sample_banks_test tests/test_spu_sample_banks.c) + target_include_directories(spu_sample_banks_test PRIVATE include) + add_test(NAME spu_sample_banks_test COMMAND spu_sample_banks_test) + + add_executable(spu_gaussian_test tests/test_spu_gaussian.c) + target_include_directories(spu_gaussian_test PRIVATE include) + add_test(NAME spu_gaussian_test COMMAND spu_gaussian_test) + + find_package(Python3 COMPONENTS Interpreter) + if(Python3_Interpreter_FOUND) + if(CMAKE_C_COMPILER_ID MATCHES "GNU|Clang" AND NOT MSVC + AND NOT CMAKE_CROSSCOMPILING) + add_test(NAME audio_trace_pcm_guard_test + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_audio_trace_pcm_guard.py + --cc ${CMAKE_C_COMPILER}) + add_test(NAME gpu_gp0_history_guard_test + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_gpu_gp0_history_guard.py + --cc ${CMAKE_C_COMPILER}) + add_test(NAME memory_debug_trace_guard_test + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_memory_debug_trace_guard.py + --cc ${CMAKE_C_COMPILER}) + endif() + add_test(NAME fntrace_c_linkage_test COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_fntrace_c_linkage.py) + add_test(NAME depth24_present_hold_vblank_test + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_depth24_present_hold_vblank.py) + add_test(NAME netplay_load_apply_flow_test + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_netplay_load_apply_flow.py) + if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") + add_test(NAME menu_video_snapshot_test COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_menu_video_snapshot.py + --cxx ${CMAKE_CXX_COMPILER}) + endif() + if(CMAKE_C_COMPILER_ID MATCHES "GNU|Clang") + add_test(NAME netplay_rb_verify_deadline_test COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_netplay_rb_verify_deadline.py + --compiler ${CMAKE_C_COMPILER}) + endif() + add_test(NAME restore_edge_irq_check_test + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_restore_edge_irq_check.py) + if(CMAKE_C_COMPILER_ID MATCHES "GNU|Clang") + add_test(NAME netplay_rb_session_reset_test + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_netplay_rb_session_reset.py + --compiler ${CMAKE_C_COMPILER}) + add_test(NAME overlay_retry_c11_test COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_overlay_retry_c11.py --compiler ${CMAKE_C_COMPILER}) + endif() + add_test(NAME setup_bios_assets_test COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_setup_bios_assets.py) + if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") + add_test(NAME launch_disc_path_test COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_launch_disc_path.py --compiler ${CMAKE_CXX_COMPILER}) + add_test(NAME persisted_settings_paths_test COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_persisted_settings_paths.py --compiler ${CMAKE_CXX_COMPILER}) + endif() + add_test(NAME disc_picker_ascii_test COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_disc_picker_ascii.py) + add_test(NAME disc_companion_test COMMAND ${Python3_EXECUTABLE} ${PSXRECOMP_ROOT}/tools/tests/test_disc_companion.py) + add_test(NAME sbi_registry_test COMMAND ${Python3_EXECUTABLE} ${PSXRECOMP_ROOT}/tools/tests/test_sbi_registry.py) + add_test(NAME perf_campaign_test COMMAND ${Python3_EXECUTABLE} ${PSXRECOMP_ROOT}/tools/tests/test_perf_campaign.py) + add_test(NAME perf_host_test COMMAND ${Python3_EXECUTABLE} ${PSXRECOMP_ROOT}/tools/tests/test_perf_host.py) + add_test(NAME frame_interpolation_context_ownership_test + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_frame_interpolation_context_ownership.py) + add_test(NAME present_skip_cadence_guard_test + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_present_skip_cadence_guard.py) + add_test(NAME mod_bezel_test + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_mod_bezel.py) + add_test(NAME rewind_toggle_combo_test + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_rewind_toggle_combo.py) + add_test(NAME savestate_status_protocol_test + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_savestate_status_protocol.py) + add_test(NAME overlay_region_floor_config_test + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_overlay_region_floor_config.py) + add_test(NAME screenshot_hires_pitch_test + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_screenshot_hires_pitch.py) + add_test(NAME gl_depth24_upload_order_test + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_gl_depth24_upload_order.py) + add_test(NAME pal_cadence_host_refresh_test + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_pal_cadence_host_refresh.py) + add_test(NAME mixed_refresh_display_cadence_test + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_mixed_refresh_display_cadence.py) + add_test(NAME cdrom_lid_integration_test + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_cdrom_lid_integration.py) + add_test(NAME bios_registry_zero_entry_guard_test + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_bios_registry_zero_entry.py) + # Exercises runtime/psx_check_mod_catalog.cmake -- the build-time guard + # that fails a build when a title's mod packages land in the legacy + # mods/packages layout instead of mods/bundled (bead beads-eio.3.101). + # Needs the cmake executable, since the guard is a cmake -P script. + add_test(NAME mod_catalog_layout_test + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_mod_catalog_layout.py + --cmake ${CMAKE_COMMAND}) + # psxrecomp_add_game_runtime(... PGXP): the real function body under + # cmake -P, forwarding the hook-flavor option for the title's one + # runtime (docs/ENHANCEMENTS.md G1.11). + add_test(NAME game_runtime_pgxp_test + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_game_runtime_pgxp.py + --cmake ${CMAKE_COMMAND}) + # tools/mod_channel_filter.py strips developer features from a staged + # catalog; every entry that served one ([[requirement]] included) must + # leave with it. + add_test(NAME mod_channel_filter_test + COMMAND ${Python3_EXECUTABLE} + ${PSXRECOMP_ROOT}/tools/tests/test_mod_channel_filter.py) + # The overlay cache tag may be formatted in exactly two files. Three + # release packagers hand-built it instead, the flavor-suffix fix reached + # one of them, and the other two could not stage a matching Linux cache + # at all -- Tomba 2 v0.0.9 shipped with no AppImage as a result. + # bead beads-eio.3.102. This test is also the SHARED check a title's own + # suite runs against its own tree (pass --root ), so the rule has + # one implementation like the staging it guards. + add_test(NAME packagers_never_format_cache_tag_test + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_packagers_never_format_cache_tag.py) + # Behavioural half of the same bead: tools/release_stage.py must select + # shards by the tag DIRECTORY's identity (the old `-path "*//*"` + # substring match rejected the real ..._f0 directory and staged zero), + # must never ship .abi_.ok / .c / .pair-lock, and must fail rather + # than warn on an empty selection. Hermetic: synthetic cache trees, no + # recompiler or captures needed. + add_test(NAME release_stage_cache_test + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_release_stage_cache.py) + find_program(PSX_PACKAGE_TEST_BASH NAMES bash + HINTS "C:/Program Files/Git/bin") + add_test(NAME package_execution_stage_test + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_package_execution_stage.py + --bash "${PSX_PACKAGE_TEST_BASH}") + if(NOT PSX_PACKAGE_TEST_BASH) + set_tests_properties(package_execution_stage_test PROPERTIES DISABLED TRUE) + endif() + add_test(NAME package_appimage_test + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_package_appimage.py) + if(NOT CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux") + set_tests_properties(package_appimage_test PROPERTIES DISABLED TRUE) + endif() + # The Linux release's bundled tcc + musl headers build and load a real + # shard with nothing from the host (x86_64 Linux; skips elsewhere). + add_test(NAME linux_tcc_bundle_smoke_test + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_linux_tcc_bundle_smoke.py) + set_tests_properties(linux_tcc_bundle_smoke_test PROPERTIES + SKIP_RETURN_CODE 77 TIMEOUT 900) + # Overlay shards and BIOS modules are compiled for the RUNTIME's + # architecture: an x86_64 macOS runtime under Rosetta used to get arm64 + # shards (arm64-only Xcode clang, no -arch) in its macos-x64 cache and + # ran every overlay interpreted. Pins the PSX_OVERLAY_ARCH_ABI handoff + # (autocompile.c), -arch on macOS, the refusal when the Python cannot + # load the runtime's architecture, and unchanged Windows/Linux commands; + # with a real cc it builds and loads each slice this Mac can run. + add_test(NAME overlay_compile_arch_test + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_overlay_compile_arch.py + --cc ${CMAKE_C_COMPILER}) + endif() + + # Full card protocol regression: read, write, address echo, per-slot state, + # pad interruption, terminal ACK behavior and persistent FLAG semantics. + add_executable(sio_card_protocol_test + tests/test_sio_card_protocol.c + src/sio.c + src/memcard.c) + target_include_directories(sio_card_protocol_test PRIVATE include src) + # Runs in a build-tree scratch dir, NOT in tests/: the test writes + # test_dir/card1.mcd relative to the working directory (self-provisioning it + # via mkdir if absent), and tests/test_dir/card1.mcd is a TRACKED fixture — + # running it from tests/ overwrites that file and leaves the repo dirty. + file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/sio_card_protocol_scratch) + add_test(NAME sio_card_protocol_test COMMAND sio_card_protocol_test + WORKING_DIRECTORY + ${CMAKE_CURRENT_BINARY_DIR}/sio_card_protocol_scratch) + + # POSIX-only: dlopen()s a fixture .so out of a synthetic cache tree, so it + # cannot link under MinGW/MSVC (-ldl). run_overlay_posix_test.sh already + # stages the cache tree, builds the fixture and the test, and runs it — so + # register THAT rather than reimplementing its fixture staging here. + if(UNIX) + find_program(BASH_EXECUTABLE bash) + if(BASH_EXECUTABLE) + add_test(NAME overlay_posix_test + COMMAND ${BASH_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/run_overlay_posix_test.sh) + else() + message(STATUS "overlay_posix_test: bash not found - skipped") + endif() + endif() +endif() + +# ---- test-registration completeness guard (must stay LAST) ------------------ +# Fails configure if a file in runtime/tests/ or recompiler/tests/ is neither +# registered as a test nor listed as a deliberate exception. See the module +# header for the rot story this exists to prevent. +include(${CMAKE_CURRENT_SOURCE_DIR}/check_test_registration.cmake) +psxrecomp_check_all_tests_registered() + +if(NOT MSVC) + psxrecomp_add_runtime_target(psx-oracle + ORACLE + DEBUG_PORT 4371 + WINDOW_TITLE "psxrecomp - Oracle" + DEFAULT_BIOS_PATH "${PSXRECOMP_ROOT}/bios/SCPH1001.BIN" + ) +endif() + +# ---- psx-beetle target (Beetle PSX libretro standalone; MinGW, Linux, macOS) ---- +# +# Standalone binary that runs Beetle PSX (mednafen-psx libretro core) with +# a debug server exposing the SAME wire-protocol commands as psx-runtime, +# on its own port (default 4382, set in beetle_main.cpp; --port N). No recomp +# code linked. +# +# Requires beetle-psx with the docs/beetle_*.patch hooks, built via: +# cd beetle-psx && make platform=mingw_x86_64 STATIC_LINKING=1 HAVE_LIGHTREC=0 +# (platform=unix on Linux, platform=osx on macOS; there the archive is named +# mednafen_psx_libretro.so/.dylib; copy it to libmednafen_psx.a). Recipes: +# docs/beetle-linux.md, docs/beetle-macos.md. + +set(BEETLE_LIB "${CMAKE_SOURCE_DIR}/../beetle-psx/libmednafen_psx.a") + +if(NOT MSVC AND EXISTS "${BEETLE_LIB}") + message(STATUS "psx-beetle: found ${BEETLE_LIB}") + + set(BEETLE_DIR "${CMAKE_SOURCE_DIR}/../beetle-psx") + set(LRC "${BEETLE_DIR}/libretro-common") + set(LIBRETRO_COMMON_SOURCES + ${LRC}/streams/file_stream.c + ${LRC}/streams/file_stream_transforms.c + ${LRC}/vfs/vfs_implementation.c + ${LRC}/file/file_path.c + ${LRC}/compat/compat_strl.c + ${LRC}/compat/fopen_utf8.c + ${LRC}/compat/compat_posix_string.c + ${LRC}/string/stdstring.c + ${LRC}/lists/string_list.c + ${LRC}/encodings/encoding_utf.c + ${LRC}/time/rtime.c + ${LRC}/rthreads/rthreads.c + ${LRC}/memmap/memalign.c + ${LRC}/hash/rhash.c + ) + + add_executable(psx-beetle + src/beetle_main.cpp + src/beetle_libretro.cpp + src/beetle_debug_server.c + src/frame_pacing.c + src/host_time.c + src/parity_trace.c + src/device_trace.c + src/audio_trace.c + ${LIBRETRO_COMMON_SOURCES} + ) + + target_include_directories(psx-beetle PRIVATE + include + ${PSX_SDL_INCLUDE_DIRS} + ${CMAKE_SOURCE_DIR}/../beetle-psx/libretro-common/include + ${CMAKE_SOURCE_DIR}/../beetle-psx + ) + + target_compile_definitions(psx-beetle PRIVATE + PSX_BEETLE_BUILD=1 + DEFAULT_DEBUG_PORT=4380 + $<$:PSX_SDL3=1> + ) + if(NOT PSX_DEBUG_TOOLS) + target_compile_definitions(psx-beetle PRIVATE PSX_NO_DEBUG_TOOLS=1) + endif() + + target_link_libraries(psx-beetle PRIVATE + "${BEETLE_LIB}" + ${PSX_SDL_LIBRARIES} + ) + + if(WIN32 OR MINGW) + target_link_libraries(psx-beetle PRIVATE ws2_32 -lpthread) + endif() + + if(MINGW) + target_link_options(psx-beetle PRIVATE -Wl,--stack,67108864) + endif() +elseif(NOT MSVC) + message(STATUS "psx-beetle: libmednafen_psx.a not found - skipping psx-beetle") +endif() diff --git a/runtime/include/gpu.h b/runtime/include/gpu.h index 868b8506..fa51c73d 100644 --- a/runtime/include/gpu.h +++ b/runtime/include/gpu.h @@ -75,15 +75,22 @@ uint32_t gpu_display_pixel_argb(const GpuDisplayInfo* di, uint32_t x, uint32_t y * overhead. `out` must hold at least `count` uint32_t ARGB entries. */ void gpu_depth24_present_row(const GpuDisplayInfo* di, uint32_t y, uint32_t* out, uint32_t count); +/* Stage a depth24 frame into a w x di->screen_height ARGB canvas and + * black-fill trailing columns no upload covered (see gpu.c). Returns the + * source rows staged. */ +uint32_t gpu_depth24_stage_scanout(const GpuDisplayInfo* di, uint32_t* buf, + uint32_t w, uint32_t margin); /* Depth24: RGB columns covered by CPU→VRAM uploads since the last reset. * Returns 0 when no uploads yet, crtc_w when coverage is full/unknown-beyond, * else the covered RGB width. Present black-fills [limit, crtc_w) without * shrinking the CRTC-derived draw width (avoids a flickering black pillar). */ uint32_t gpu_depth24_rgb_limit(uint32_t display_x, uint32_t crtc_w); void gpu_depth24_upload_span_reset(void); -/* MotK intro cuts retarget GP1(07h). While hold > 0, present should skip - * Swap (keep prior frame) so stale trailing VRAM never flashes. One tick - * per vblank; returns non-zero while the hold is still active after tick. */ +/* MotK intro cuts retarget GP1(07h): the guest-state hold counts down at + * guest VBlank (gpu_vblank_tick). Returns non-zero while the latest guest + * VBlank was held (read-only; guest state in the GPU snap): presents should + * skip Swap (keep the prior frame) so stale trailing + * VRAM never flashes. */ int gpu_depth24_present_hold_tick(void); /* After savestate restore: clear ephemeral hold so the restored depth24 * frame can present immediately (span/prev_h come from the GPU snap). */ diff --git a/runtime/src/gpu.c b/runtime/src/gpu.c index 63702000..4a3bba83 100644 --- a/runtime/src/gpu.c +++ b/runtime/src/gpu.c @@ -3435,11 +3435,13 @@ static uint16_t vram_write_pixels[1024 * 512]; * gpu_depth24_rgb_limit — declared early so gpu_reset_state can clear it. */ static uint32_t s_d24_upload_x1 = 0; static int s_d24_present_hold = 0; /* vblanks to skip Swap after GP1(07h) */ -/* Host side of the hold: whether the present after the latest guest VBlank - * skips its Swap. The count itself is guest state (in the GPU snap) and moves - * only at guest VBlank, so a live run, a rollback replay and a headless run - * all hold the same span (#636: a present-ticked hold stuck at 3 in headless - * live play and was zeroed by a rollback load, so replay != live). */ +/* Whether the latest guest VBlank was held: presents after it skip their + * Swap. Both the count and this flag are guest state, set only at guest + * VBlank and carried in the GPU snap (packed into the hold word), and a + * present only reads them, so a windowed, headless or frame-skipping run and + * a rollback replay hold the same span and restore the same output (#636: a + * present-ticked hold stuck at 3 in headless live play and was zeroed by a + * rollback load, so replay != live). */ static int s_d24_present_skip = 0; static uint32_t s_d24_prev_disp_h = 0; /* last GP1(07h) band height */ static void depth24_note_upload(uint32_t x, uint32_t w); @@ -4269,9 +4271,7 @@ void gpu_depth24_upload_span_reset(void) { } int gpu_depth24_present_hold_tick(void) { - int skip = s_d24_present_skip; - s_d24_present_skip = 0; /* one skipped Swap per guest VBlank */ - return skip; + return s_d24_present_skip; } void gpu_depth24_on_savestate_loaded(void) { @@ -4420,6 +4420,42 @@ void gpu_depth24_present_row(const GpuDisplayInfo* di, uint32_t y, uint32_t* out out[x] = 0xFF000000u; } +/* Depth24 present staging, shared by every CPU present path: compose the + * GP1(07h) intersection with active video into the PAL/NTSC canvas (rows + * off-screen are clipping, not a shift), then black-fill the trailing RGB + * columns no CPU->VRAM upload has covered since the last span reset, so stale + * VRAM from a prior movie never shows past the new blit. With no span yet + * (lim == 0) only the last `margin` columns are blanked: blanking [0..w) left + * MotK's FMV permanently black after the GP1(07h) hold reset the span. + * Returns the source rows staged. */ +uint32_t gpu_depth24_stage_scanout(const GpuDisplayInfo *di, uint32_t *buf, + uint32_t w, uint32_t margin) { + if (!di || !buf || w == 0u || di->screen_height == 0u) + return 0u; + const size_t count = (size_t)w * di->screen_height; + for (size_t i = 0; i < count; i++) + buf[i] = 0xFF000000u; + if (di->screen_origin_y >= di->screen_height) + return 0u; + uint32_t rows = di->height; + if (rows > di->screen_height - di->screen_origin_y) + rows = di->screen_height - di->screen_origin_y; + uint32_t *source = buf + (size_t)di->screen_origin_y * w; + for (uint32_t y = 0; y < rows; y++) + gpu_depth24_present_row(di, di->screen_source_skip_y + y, + source + (size_t)y * w, w); + if (rows == 0u || w < 8u || margin == 0u || margin >= w) + return rows; + uint32_t start = w - margin; + uint32_t lim = gpu_depth24_rgb_limit(di->display_x, w); + if (lim > 0u && lim < w && lim < start) + start = lim; + for (uint32_t y = 0; y < rows; y++) + for (uint32_t x = start; x < w; x++) + source[(size_t)y * w + x] = 0xFF000000u; + return rows; +} + int gpu_display_is_depth24(void) { return (int)(display_depth & 1u); } @@ -7850,7 +7886,8 @@ static int gpu_snap_emit(PstW *w) { WI(vram_read_active); WH(vram_read_x); WH(vram_read_y); WH(vram_read_w); WH(vram_read_h); WH(vram_read_col); WH(vram_read_row); /* Depth24 present helpers (MotK FMV) — must resume with upload span. */ - WU(s_d24_upload_x1); WI(s_d24_present_hold); WU(s_d24_prev_disp_h); + WU(s_d24_upload_x1); WI(s_d24_present_hold | (s_d24_present_skip ? 0x100 : 0)); + WU(s_d24_prev_disp_h); #undef WU #undef WI #undef WH @@ -7884,6 +7921,10 @@ static int gpu_snap_parse(PstR *r) { RI(vram_read_active); RH(vram_read_x); RH(vram_read_y); RH(vram_read_w); RH(vram_read_h); RH(vram_read_col); RH(vram_read_row); RU(s_d24_upload_x1); RI(s_d24_present_hold); RU(s_d24_prev_disp_h); + /* Bit 8: the latest VBlank was held. Older snaps lack it; a positive + * count after a VBlank implies the VBlank was held. */ + s_d24_present_skip = (s_d24_present_hold & 0x100) || (s_d24_present_hold & 0xFF) > 0; + s_d24_present_hold &= 0xFF; #undef RU #undef RI #undef RH diff --git a/runtime/src/main.cpp b/runtime/src/main.cpp index cf8183d6..67ce99ab 100644 --- a/runtime/src/main.cpp +++ b/runtime/src/main.cpp @@ -7483,55 +7483,20 @@ static void depth24_cutover_tick(int depth24) { * * Optional short cutover blank: full-frame black for 1–2 presents on movie * start / long idle resume only (see depth24_cutover_tick). */ -static void depth24_fix_trailing_margin(uint32_t *buf, uint32_t w, uint32_t h, - uint32_t display_x) { - if (!buf || w < 8u || h == 0u) return; - - if (s_d24_cutover_blank > 0) { - s_d24_cutover_blank--; - const uint32_t n = w * h; - for (uint32_t i = 0; i < n; i++) - buf[i] = 0xFF000000u; - return; - } - - /* Default: last 8 columns. If the upload span is known and ends earlier - * inside that margin, start blanking from the span edge instead. */ - const uint32_t margin = (uint32_t)g_video_depth24_trailing_margin; - if (margin == 0u || margin >= w) return; - uint32_t start = w - margin; - uint32_t lim = gpu_depth24_rgb_limit(display_x, w); - if (lim > 0u && lim < w && lim < start) - start = lim; - for (uint32_t y = 0; y < h; y++) { - for (uint32_t x = start; x < w; x++) - buf[y * w + x] = 0xFF000000u; - } -} - -/* Compose visible depth24 rows into the PAL/NTSC active canvas. Decode only - * the GP1(07h) intersection with active video. This preserves every visible - * source row and represents off-screen rows as clipping, not as an in-place - * shift that discards additional FMV content. */ static void depth24_stage_scanout(const GpuDisplayInfo *di, uint32_t *buf, uint32_t w) { - if (!di || !buf || w == 0u || di->screen_height == 0u) + if (!di || !buf || w == 0u) return; - + /* gpu.c stages the visible rows and black-fills the uncovered trailing + * columns (the same code depth24_present_hold_exec_test renders). */ + const uint32_t rows = gpu_depth24_stage_scanout( + di, buf, w, (uint32_t)g_video_depth24_trailing_margin); + if (rows == 0u || w < 8u || s_d24_cutover_blank <= 0) + return; + s_d24_cutover_blank--; const size_t count = (size_t)w * di->screen_height; for (size_t i = 0; i < count; i++) buf[i] = 0xFF000000u; - - if (di->screen_origin_y >= di->screen_height) - return; - uint32_t rows = di->height; - if (rows > di->screen_height - di->screen_origin_y) - rows = di->screen_height - di->screen_origin_y; - uint32_t *source = buf + (size_t)di->screen_origin_y * w; - for (uint32_t y = 0; y < rows; y++) - gpu_depth24_present_row(di, di->screen_source_skip_y + y, - source + (size_t)y * w, w); - depth24_fix_trailing_margin(source, w, rows, di->display_x); } enum { diff --git a/runtime/tests/test_depth24_present_hold_exec.c b/runtime/tests/test_depth24_present_hold_exec.c index cb162430..f8cc4b40 100644 --- a/runtime/tests/test_depth24_present_hold_exec.c +++ b/runtime/tests/test_depth24_present_hold_exec.c @@ -1,49 +1,321 @@ #ifdef NDEBUG #undef NDEBUG #endif + +/* Executable regression for the depth24 GP1(07h) present hold (#636). + * + * Drives the real gpu.c through a scripted depth24 FMV: 15-bit setup, a + * switch to 24-bit, a full-width movie A, a GP1(07h) band retarget to a + * narrower movie B (the hold), display-mode transitions and a second + * retarget. Each frame is: guest GP0/GP1 writes, gpu_vblank_tick(), then + * (per present policy) the frontend present, modelled exactly as + * sdl_vblank_present_body does it: skip the Swap when + * gpu_depth24_present_hold_tick() says so, else stage the frame through + * gpu_depth24_stage_scanout() (the code every CPU depth24 present runs). + * + * Checks: + * - the hold count, its latched skips and the upload span are identical + * per guest tick whether presents run every VBlank (windowed), never + * (headless) or every other VBlank (deferred/skipped presents), and so + * are the GPU snapshot bytes; + * - the hold spans exactly three VBlanks (3 -> 2 -> 1 -> 0) and skips + * exactly those presents; uploads during it do not grow the span; + * - GP1(07h) in 15-bit mode, or an unchanged band, does not arm it; + * - rendering: no staged frame after a retarget shows a single pixel of + * the stale trailing movie-A columns, movie B is visible (not a black + * screen), and staging that ignored the hold would expose the stale + * columns (so the check has teeth); + * - rollback: loading the snapshot of any tick (hold and held-VBlank flag + * restored from the snap, whatever the pre-load flag was) and + * replaying reproduces the live run tick for tick, including the present + * of the restored tick and every staged frame; + * - disk load: the hold and skip clear, the span comes back from the + * snapshot and the restored frame presents at once. + */ #include +#include #include -#define GPU_EXEC_REAL_UI_GROUP +#include +#include + #include "../src/gpu.c" + #include "gpu_exec_stubs.inc" -static void arm(void) { - gpu_reset_state(0); - display_depth = 1; - gp1_v_display_range((260u<<10)|20u); - gp1_v_display_range((244u<<10)|20u); - assert(s_d24_present_hold==3); +enum { FRAMES = 30, PRESENT_W = 320, MAX_ROWS = 600 }; +enum { POLICY_EVERY, POLICY_NONE, POLICY_EVERY_OTHER, POLICY_COUNT }; + +#define MOVIE_A 0xAAu /* stale full-width movie bytes */ +#define MOVIE_B 0x33u /* narrower movie after the retarget */ +#define B_HALFWORDS 384u /* 256 RGB pixels */ +#define A_HALFWORDS 480u /* 320 RGB pixels */ +#define RETARGET_1 7 +#define MODE_15_FRAME 16 +#define MODE_24_FRAME 17 +#define RETARGET_2 21 + +typedef struct { + int hold; + int skip_latch; + uint32_t span; + uint64_t snap_hash; + int presented; /* -1: no present ran, 0: Swap skipped, 1: staged */ + uint64_t frame_hash; +} Tick; + +static uint32_t canvas[PRESENT_W * MAX_ROWS]; + +static uint64_t fnv(const void *p, size_t n) { + const uint8_t *b = (const uint8_t *)p; + uint64_t h = 1469598103934665603ull; + for (size_t i = 0; i < n; i++) { h ^= b[i]; h *= 1099511628211ull; } + return h; } -int main(void) { - for (int host_present=0; host_present<2; ++host_present) { - arm(); - for (int vb=0;vb<3;++vb) { - depth24_note_upload(0,480); - assert(s_d24_upload_x1==0); - gpu_vblank_tick(); - assert(s_d24_present_hold==2-vb); - if (host_present) { - assert(gpu_depth24_present_hold_tick()==1); - assert(gpu_depth24_present_hold_tick()==0); + +static uint64_t snap_hash(void) { + uint32_t n = gpu_snapshot_bytes(); + uint8_t *buf = (uint8_t *)malloc(n); + assert(buf); + gpu_snapshot_write(buf); + uint64_t h = fnv(buf, n); + free(buf); + return h; +} + +static void gp1_vrange(uint32_t h) { + gpu_write_gp1(0x07000000u | 0x10u | ((0x10u + h) << 10)); +} + +static void upload(uint8_t byte, uint32_t halfwords, uint32_t rows) { + const uint32_t hw = (uint32_t)byte | ((uint32_t)byte << 8); + gpu_write_gp0(0xA0000000u); + gpu_write_gp0(0u); /* x = 0, y = 0 */ + gpu_write_gp0((rows << 16) | halfwords); + for (uint32_t i = 0; i < halfwords * rows / 2u; i++) + gpu_write_gp0(hw | (hw << 16)); + assert(gp0_state == GP0_IDLE); +} + +/* Guest work for frame f (identical in every run and every replay). */ +static void guest_frame(int f) { + switch (f) { + case 0: + gpu_write_gp1(0x08000001u); /* 15-bit, 320 */ + gpu_write_gp1(0x05000000u); /* display area 0,0 */ + gpu_write_gp1(0x06000000u | 0x260u | (0xC60u << 12)); + gp1_vrange(64); + break; + case 1: gp1_vrange(48); break; /* 15-bit: no hold */ + case 2: gp1_vrange(64); break; + case 3: gpu_write_gp1(0x08000011u); break; /* -> 24-bit */ + case 4: gp1_vrange(64); break; /* same band: no hold */ + case RETARGET_1: gp1_vrange(48); break; /* MotK-style retarget */ + case MODE_15_FRAME: gpu_write_gp1(0x08000001u); break; + case MODE_24_FRAME: gpu_write_gp1(0x08000011u); break; + case RETARGET_2: gp1_vrange(40); break; + default: break; + } + if (f >= 3 && f < RETARGET_1) + upload(MOVIE_A, A_HALFWORDS, 64); + else if (f >= RETARGET_1) + upload(MOVIE_B, B_HALFWORDS, 48); +} + +static int present(uint64_t *frame_hash, int honor_hold) { + int skip = gpu_depth24_present_hold_tick(); + if (skip && honor_hold) + return 0; + GpuDisplayInfo di; + gpu_get_display_info(&di); + if (!di.depth24) { *frame_hash = 0; return 1; } + assert(di.screen_height <= MAX_ROWS); + uint32_t rows = gpu_depth24_stage_scanout(&di, canvas, PRESENT_W, 8u); + assert(rows > 0u); + *frame_hash = fnv(canvas, sizeof(uint32_t) * PRESENT_W * di.screen_height); + /* Rendering check on the staged frame. */ + const uint32_t stale = 0xFF000000u | (MOVIE_A << 16) | (MOVIE_A << 8) | MOVIE_A; + const uint32_t fresh = 0xFF000000u | (MOVIE_B << 16) | (MOVIE_B << 8) | MOVIE_B; + uint32_t n_stale = 0, n_fresh = 0; + for (uint32_t i = 0; i < PRESENT_W * di.screen_height; i++) { + n_stale += canvas[i] == stale; + n_fresh += canvas[i] == fresh; + } + return 1 + (int)(n_stale > 0) * 2 + (int)(n_fresh > 0) * 4; +} + +static void reset_gpu(void) { + gpu_reset_state(1); + memset(test_ram, 0, sizeof(test_ram)); +} + +static void record(Tick *t) { + t->hold = s_d24_present_hold; + t->skip_latch = s_d24_present_skip; + t->span = s_d24_upload_x1; + t->snap_hash = snap_hash(); +} + +/* Present step; returns -1/0/1 and checks the staged image. */ +static int present_checked(int f, uint64_t *frame_hash) { + int r = present(frame_hash, 1); + if (r == 0) return 0; + if (f >= RETARGET_1) { + if (r & 2) { + fprintf(stderr, "frame %d: stale movie-A pixels exposed\n", f); + abort(); + } + if (s_d24_upload_x1 != 0u && display_depth) + assert(r & 4); /* movie B on screen */ + } + return 1; +} + +static void run(int policy, Tick out[FRAMES]) { + reset_gpu(); + for (int f = 0; f < FRAMES; f++) { + guest_frame(f); + gpu_vblank_tick(); + record(&out[f]); + out[f].presented = -1; + out[f].frame_hash = 0; + int do_present = policy == POLICY_EVERY || + (policy == POLICY_EVERY_OTHER && (f & 1)); + if (do_present) + out[f].presented = present_checked(f, &out[f].frame_hash); + } +} + +static void check_policies_agree(void) { + static Tick t[POLICY_COUNT][FRAMES]; + for (int p = 0; p < POLICY_COUNT; p++) + run(p, t[p]); + for (int p = 1; p < POLICY_COUNT; p++) + for (int f = 0; f < FRAMES; f++) { + assert(t[p][f].hold == t[0][f].hold); + assert(t[p][f].skip_latch == t[0][f].skip_latch); + assert(t[p][f].span == t[0][f].span); + assert(t[p][f].snap_hash == t[0][f].snap_hash); + } + + const Tick *w = t[POLICY_EVERY]; + /* 15-bit retargets and an unchanged band never arm the hold. */ + for (int f = 0; f < RETARGET_1; f++) assert(w[f].hold == 0 && !w[f].skip_latch); + /* All three VBlanks of each hold: 2, 1, 0 after the ticks, three skips. */ + const int starts[2] = { RETARGET_1, RETARGET_2 }; + for (int k = 0; k < 2; k++) { + int s = starts[k]; + assert(w[s].hold == 2 && w[s + 1].hold == 1 && w[s + 2].hold == 0); + assert(w[s + 3].hold == 0); + for (int i = 0; i < 3; i++) { + assert(w[s + i].presented == 0); /* Swap skipped */ + assert(w[s + i].span == 0u); /* held uploads ignored */ + } + assert(w[s + 3].presented == 1); /* first staged frame */ + assert(w[s + 3].span == B_HALFWORDS); + } + int skipped = 0; + for (int f = 0; f < FRAMES; f++) skipped += w[f].presented == 0; + assert(skipped == 6); + /* Display-mode transitions: leaving 24-bit drops the span; coming back + * gets it from that frame's upload. */ + assert(w[MODE_15_FRAME].span == 0u); + assert(w[MODE_24_FRAME].span == B_HALFWORDS); + /* Before the retarget movie A is legitimately full width. */ + assert(w[RETARGET_1 - 1].presented == 1); + /* Deferred presents (odd ticks only): a held tick's present skips, the + * first present after the hold stages a clean frame. */ + const Tick *d = t[POLICY_EVERY_OTHER]; + assert(d[RETARGET_1].presented == 0 && d[RETARGET_1 + 1].presented == -1); + assert(d[RETARGET_1 + 2].presented == 0 && d[RETARGET_1 + 4].presented == 1); +} + +/* Teeth: a present that ignored the hold right after the retarget would + * show stale movie-A columns (the span was reset, lim == 0). */ +static void check_hold_is_needed(void) { + reset_gpu(); + for (int f = 0; f <= RETARGET_1; f++) { guest_frame(f); gpu_vblank_tick(); } + uint64_t h; + int r = present(&h, 0); + assert(r & 2); +} + +static void check_rollback_and_disk(void) { + static Tick live[FRAMES]; + static uint8_t snaps[FRAMES][4096]; + static uint16_t vrams[FRAMES][1024 * 512]; + uint32_t n = gpu_snapshot_bytes(); + assert(n <= sizeof snaps[0]); + + reset_gpu(); + for (int f = 0; f < FRAMES; f++) { + guest_frame(f); + gpu_vblank_tick(); + record(&live[f]); + gpu_snapshot_write(snaps[f]); + memcpy(vrams[f], vram, sizeof vram); + live[f].presented = present_checked(f, &live[f].frame_hash); + } + + for (int k = RETARGET_1 - 2; k < FRAMES - 1; k++) { + for (int latch = 0; latch < 2; latch++) { + s_d24_present_skip = latch; /* whatever the tip had */ + memcpy(vram, vrams[k], sizeof vram); + assert(gpu_snapshot_read(snaps[k], n)); + /* psx_frontend_on_rb_snap_loaded keeps the hold. */ + assert(s_d24_present_hold == live[k].hold); + assert(s_d24_upload_x1 == live[k].span); + /* Restored output/skip: the present of the restored tick does + * what the live present of that tick did. */ + uint64_t h = 0; + int r = present_checked(k, &h); + assert(r == live[k].presented); + if (r == 1) assert(h == live[k].frame_hash); + for (int f = k + 1; f < FRAMES; f++) { + Tick t; + guest_frame(f); + gpu_vblank_tick(); + record(&t); + assert(t.hold == live[f].hold); + assert(t.skip_latch == live[f].skip_latch); + assert(t.span == live[f].span); + assert(t.snap_hash == live[f].snap_hash); + t.frame_hash = 0; + t.presented = present_checked(f, &t.frame_hash); + assert(t.presented == live[f].presented); + assert(t.frame_hash == live[f].frame_hash); } } - depth24_note_upload(0,480); - assert(s_d24_upload_x1==480); - gpu_vblank_tick(); - assert(gpu_depth24_present_hold_tick()==0); } - arm(); gpu_vblank_tick(); - unsigned char saved[4096]; - uint32_t n=gpu_snapshot_bytes(); - assert(n<=sizeof saved); - gpu_snapshot_write(saved); - gpu_vblank_tick(); gpu_vblank_tick(); - assert(s_d24_present_hold==0); - assert(gpu_snapshot_read(saved,n)); - assert(s_d24_present_hold==2); - gpu_vblank_tick(); assert(s_d24_present_hold==1); + + /* Disk savestate load mid-hold: hold and skip clear, span from the snap, + * the restored frame presents at once. */ + for (int k = RETARGET_2; k < RETARGET_2 + 3; k++) { + s_d24_present_skip = 1; + memcpy(vram, vrams[k], sizeof vram); + assert(gpu_snapshot_read(snaps[k], n)); + gpu_depth24_on_savestate_loaded(); + assert(s_d24_present_hold == 0); + assert(s_d24_upload_x1 == live[k].span); + uint64_t h; + assert(present(&h, 1) != 0); + /* The next VBlank latches no skip: the cleared hold stays cleared. */ + gpu_vblank_tick(); + assert(gpu_depth24_present_hold_tick() == 0); + } + /* A disk load after the hold keeps the restored span. */ + memcpy(vram, vrams[RETARGET_1 + 3], sizeof vram); + assert(gpu_snapshot_read(snaps[RETARGET_1 + 3], n)); gpu_depth24_on_savestate_loaded(); - assert(s_d24_present_hold==0 && gpu_depth24_present_hold_tick()==0); - puts("depth24 hold: retarget, headless/present cadence, upload span and snapshot restore PASS"); + assert(s_d24_upload_x1 == B_HALFWORDS); + uint64_t h = 0; + int r = present_checked(RETARGET_1 + 3, &h); + assert(r == live[RETARGET_1 + 3].presented && h == live[RETARGET_1 + 3].frame_hash); +} + +int main(void) { + check_policies_agree(); + check_hold_is_needed(); + check_rollback_and_disk(); + puts("depth24_present_hold_exec_test: ok"); return 0; }