From 79e6fc5c8bc8ec2f064b9a9d6c5c3ce5f75e9635 Mon Sep 17 00:00:00 2001 From: WizzardSK <42868978+WizzardSK@users.noreply.github.com> Date: Fri, 28 Aug 2026 21:16:34 +0200 Subject: [PATCH] libretro: don't rebuild the video driver for an unchanged mode libretro/ps2#159 reports God of War crashing shortly after boot on Android. The reporter ran the triage that would have implicated the recompilers - MTVU off, VU1 softfloat on, EE interpreter - and it still crashed, which is what pointed away from CPU emulation entirely. Their log ends immediately after this: SET_SYSTEM_AV_INFO: 640x448, Aspect: 1.3333, FPS: 59.9401, Sample rate: 48000 ... video and audio drivers reinitialised ... SET_SYSTEM_AV_INFO: 640x448, Aspect: 1.3333, FPS: 59.9401, Sample rate: 48000 ... video and audio drivers reinitialised again ... Two announcements carrying identical values, each making the frontend tear down and rebuild its whole video driver, which on the HW-render path means context_destroy and a fresh negotiation. update_av_info() sent the announcement unconditionally whenever pending_update_av_info was set, without comparing against what had last gone out, and without regard for what the GS thread was doing - so the rebuild could land on top of a thread still submitting to the shared Vulkan queue. Both guards are the ones pcee2 already carries for the same defect (adf456e28d): - Announce only on a real timing change. The fps compare needs a tolerance, since NTSC reports 59.94005994 Hz against a 59.94 default and that 0.00006 Hz difference must not rebuild anything. Geometry needs no announcement on the HW-render path: SET_GEOMETRY carries it without a reinit, which is already how the widescreen hint does it, so it is forwarded that way instead. - Drain the GS thread before an announcement that does go out. The CPU thread is already parked at this point - retro_run has not resumed it. Software rendering keeps the old behaviour for a geometry change, matching pcee2 rather than widening the change. This is a hypothesis the log supports rather than a confirmed fix: nothing in that log says what the crash was, because the core has no crash reporter yet (#158), and the reporter cannot get logcat without root. What can be said is that the frontend was rebuilding its video driver for no reason at the moment everything stopped, and that this is a defect either way. --- libretro/main.cpp | 55 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) diff --git a/libretro/main.cpp b/libretro/main.cpp index 516727808d..210bb16129 100644 --- a/libretro/main.cpp +++ b/libretro/main.cpp @@ -18,6 +18,7 @@ #include "libretro_core_options.h" +#include #include "../pcsx2/GS.h" #include "../pcsx2/SPU2/Global.h" #include "../pcsx2/ps2/BiosTools.h" @@ -2825,12 +2826,64 @@ void retro_unload_game(void) retro_set_region(RETRO_REGION_NTSC); /* set back to default */ } +/* SET_SYSTEM_AV_INFO makes the frontend tear down and rebuild its whole video + * driver - on the HW-render path that means context_destroy and a fresh + * negotiation - so it is only worth sending for a change that actually needs + * one. Announcing unconditionally cost a full video and audio driver reinit + * every time the VM reported its mode, including reports identical to the last + * one, and put that rebuild in the way of a GS thread still submitting to the + * shared Vulkan queue. + * + * Two guards, both taken from the same fix in pcee2: + * + * - Only announce on a real timing change. The fps compare needs a tolerance: + * NTSC reports 59.94005994 Hz against a 59.94 default and that 0.00006 Hz + * difference must not rebuild anything. Geometry does not need an announce + * at all on the HW-render path - SET_GEOMETRY carries it without a reinit, + * which is how the widescreen hint already does it. + * - Drain the GS thread before an announce that does go out, so no queued work + * races the frontend's reinit. The CPU thread is already parked here: + * retro_run has not resumed it yet. */ static void update_av_info(void) { retro_system_av_info av_info; retro_get_system_av_info(&av_info); - environ_cb(RETRO_ENVIRONMENT_SET_SYSTEM_AV_INFO, &av_info); pending_update_av_info = false; + + static bool have_last = false; + static double last_fps = 0.0; + static double last_sample_rate = 0.0; + static unsigned last_width = 0; + static unsigned last_height = 0; + static float last_aspect = 0.0f; + + const bool hw_vulkan = (hw_render.context_type == RETRO_HW_CONTEXT_VULKAN); + const bool timing_changed = !have_last + || std::fabs(av_info.timing.fps - last_fps) > 0.25 + || av_info.timing.sample_rate != last_sample_rate; + const bool geometry_changed = !have_last + || av_info.geometry.base_width != last_width + || av_info.geometry.base_height != last_height + || std::fabs(av_info.geometry.aspect_ratio - last_aspect) > 0.001f; + + have_last = true; + last_fps = av_info.timing.fps; + last_sample_rate = av_info.timing.sample_rate; + last_width = av_info.geometry.base_width; + last_height = av_info.geometry.base_height; + last_aspect = av_info.geometry.aspect_ratio; + + if (!timing_changed && (hw_vulkan || !geometry_changed)) + { + if (geometry_changed) + environ_cb(RETRO_ENVIRONMENT_SET_GEOMETRY, &av_info.geometry); + return; + } + + if (hw_vulkan && MTGS::IsOpen()) + MTGS::WaitGS(false); + + environ_cb(RETRO_ENVIRONMENT_SET_SYSTEM_AV_INFO, &av_info); } void retro_run(void)