From f17400092dbd3a30063bd38d5628b8766e03237f Mon Sep 17 00:00:00 2001 From: elad335 <18193363+elad335@users.noreply.github.com> Date: Wed, 7 May 2025 19:25:14 +0300 Subject: [PATCH 01/14] Fixup SPU Interpreters --- rpcs3/Emu/Cell/SPUThread.cpp | 87 +++++++++++++++--------------------- rpcs3/Emu/Cell/SPUThread.h | 1 + 2 files changed, 38 insertions(+), 50 deletions(-) diff --git a/rpcs3/Emu/Cell/SPUThread.cpp b/rpcs3/Emu/Cell/SPUThread.cpp index f66919c106..0396068018 100644 --- a/rpcs3/Emu/Cell/SPUThread.cpp +++ b/rpcs3/Emu/Cell/SPUThread.cpp @@ -2129,6 +2129,41 @@ u8* spu_thread::map_ls(utils::shm& shm, void* ptr) return ls; } +void spu_thread::init_spu_decoder() +{ + ensure(!jit); + +#if !defined(ARCH_X64) && !defined(ARCH_ARM64) +#error "Unimplemented" +#else + const spu_decoder_type spu_decoder = g_cfg.core.spu_decoder; + +#if defined(ARCH_X64) + if (spu_decoder == spu_decoder_type::asmjit) + { + jit = spu_recompiler_base::make_asmjit_recompiler(); + } + else +#endif + if (spu_decoder == spu_decoder_type::llvm) + { +#if defined(ARCH_X64) + jit = spu_recompiler_base::make_fast_llvm_recompiler(); +#elif defined(ARCH_ARM64) + jit = spu_recompiler_base::make_llvm_recompiler(); +#endif + } + else if (spu_decoder == spu_decoder_type::_static || spu_decoder == spu_decoder_type::dynamic) + { + // + } + else + { + fmt::throw_exception("Unsupported spu decoder '%s'", g_cfg.core.spu_decoder); + } +#endif +} + spu_thread::spu_thread(lv2_spu_group* group, u32 index, std::string_view name, u32 lv2_id, bool is_isolated, u32 option) : cpu_thread(idm::last_id()) , group(group) @@ -2140,31 +2175,7 @@ spu_thread::spu_thread(lv2_spu_group* group, u32 index, std::string_view name, u , lv2_id(lv2_id) , spu_tname(make_single(name)) { -#if defined(ARCH_X64) - if (g_cfg.core.spu_decoder == spu_decoder_type::asmjit) - { - jit = spu_recompiler_base::make_asmjit_recompiler(); - } - else if (g_cfg.core.spu_decoder == spu_decoder_type::llvm) - { - jit = spu_recompiler_base::make_fast_llvm_recompiler(); - } - else - { - fmt::throw_exception("Unsupported spu decoder '%s'", g_cfg.core.spu_decoder); - } -#elif defined(ARCH_ARM64) - if (g_cfg.core.spu_decoder == spu_decoder_type::llvm) - { - jit = spu_recompiler_base::make_llvm_recompiler(); - } - else - { - fmt::throw_exception("Unsupported spu decoder '%s'", g_cfg.core.spu_decoder); - } -#else -#error "Unimplemented" -#endif + init_spu_decoder(); if (g_cfg.core.mfc_debug) { @@ -2226,31 +2237,7 @@ spu_thread::spu_thread(utils::serial& ar, lv2_spu_group* group) , lv2_id(ar) , spu_tname(make_single(ar.operator std::string())) { -#if defined(ARCH_X64) - if (g_cfg.core.spu_decoder == spu_decoder_type::asmjit) - { - jit = spu_recompiler_base::make_asmjit_recompiler(); - } - else if (g_cfg.core.spu_decoder == spu_decoder_type::llvm) - { - jit = spu_recompiler_base::make_fast_llvm_recompiler(); - } - else - { - fmt::throw_exception("Unsupported spu decoder '%s'", g_cfg.core.spu_decoder); - } -#elif defined(ARCH_ARM64) - if (g_cfg.core.spu_decoder == spu_decoder_type::llvm) - { - jit = spu_recompiler_base::make_llvm_recompiler(); - } - else - { - fmt::throw_exception("Unsupported spu decoder '%s'", g_cfg.core.spu_decoder); - } -#else -#error "Unimplemented" -#endif + init_spu_decoder(); if (g_cfg.core.mfc_debug) { diff --git a/rpcs3/Emu/Cell/SPUThread.h b/rpcs3/Emu/Cell/SPUThread.h index a5495899f2..e0cc9ca0e7 100644 --- a/rpcs3/Emu/Cell/SPUThread.h +++ b/rpcs3/Emu/Cell/SPUThread.h @@ -638,6 +638,7 @@ public: virtual ~spu_thread() override; void cleanup(); void cpu_init(); + void init_spu_decoder(); static const u32 id_base = 0x02000000; // TODO (used to determine thread type) static const u32 id_step = 1; From aa79afd7ae390572f4373fbba07ea0f54bc0ebe7 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Mon, 5 May 2025 22:44:38 +0200 Subject: [PATCH 02/14] StrUtil: Add more unit tests --- rpcs3/Emu/RSX/VK/VKRenderTargets.h | 1 - rpcs3/tests/test_fmt.cpp | 42 +++++++++++++++++++++++------- 2 files changed, 33 insertions(+), 10 deletions(-) diff --git a/rpcs3/Emu/RSX/VK/VKRenderTargets.h b/rpcs3/Emu/RSX/VK/VKRenderTargets.h index 64ff78533a..caa85dcc84 100644 --- a/rpcs3/Emu/RSX/VK/VKRenderTargets.h +++ b/rpcs3/Emu/RSX/VK/VKRenderTargets.h @@ -683,4 +683,3 @@ namespace vk void trim(vk::command_buffer& cmd, rsx::problem_severity memory_pressure); }; } -//h diff --git a/rpcs3/tests/test_fmt.cpp b/rpcs3/tests/test_fmt.cpp index e4988b24c6..e44b4adab0 100644 --- a/rpcs3/tests/test_fmt.cpp +++ b/rpcs3/tests/test_fmt.cpp @@ -5,7 +5,7 @@ using namespace std::literals::string_literals; namespace fmt { - TEST(StrUtil, test_trim) + TEST(StrUtil, Trim) { EXPECT_EQ(""s, fmt::trim("", "")); EXPECT_EQ(""s, fmt::trim("", " ")); @@ -25,7 +25,7 @@ namespace fmt EXPECT_EQ("b"s, fmt::trim(" aba ", " a")); } - TEST(StrUtil, test_trim_front) + TEST(StrUtil, TrimFront) { EXPECT_EQ(""s, fmt::trim_front("", "")); EXPECT_EQ(""s, fmt::trim_front("", " ")); @@ -45,7 +45,7 @@ namespace fmt EXPECT_EQ("ba "s, fmt::trim_front(" aba ", " a")); } - TEST(StrUtil, test_trim_back) + TEST(StrUtil, TrimBack) { std::string str; fmt::trim_back(str, ""); @@ -112,7 +112,7 @@ namespace fmt EXPECT_EQ(" ab"s, str); } - TEST(StrUtil, test_to_upper_to_lower) + TEST(StrUtil, ToUpperToLower) { const std::string lowercase = "abcdefghijklmnopqrstuvwxyzäüöß0123456789 .,-<#+"; const std::string uppercase = "ABCDEFGHIJKLMNOPQRSTUVWXYZäüöß0123456789 .,-<#+"; @@ -128,7 +128,7 @@ namespace fmt EXPECT_EQ(uppercase, to_upper_res); } - TEST(StrUtil, test_truncate) + TEST(StrUtil, Truncate) { const std::string str = "abcdefghijklmnopqrstuvwxyzäüöß0123456789 .,-<#+"; @@ -145,7 +145,7 @@ namespace fmt EXPECT_EQ(str, fmt::truncate(str, str.size() + 1)); } - TEST(StrUtil, test_replace_all) + TEST(StrUtil, ReplaceAll) { EXPECT_EQ(""s, fmt::replace_all("", "", "")); EXPECT_EQ(""s, fmt::replace_all("", "", " ")); @@ -191,7 +191,7 @@ namespace fmt EXPECT_EQ("drow drow drow"s, fmt::replace_all("word word word", "word", "drow", -1)); } - TEST(StrUtil, test_split) + TEST(StrUtil, Split) { using vec = std::vector; @@ -340,7 +340,7 @@ namespace fmt EXPECT_EQ(vec({"This", "is", "test!"}), fmt::split(" This is a test! ", {"a", " ", "b"}, true)); } - TEST(StrUtil, test_merge) + TEST(StrUtil, Merge) { using vec = std::vector; using lst = std::initializer_list>; @@ -413,7 +413,7 @@ namespace fmt EXPECT_EQ("a *-* 1 *-* b *-* 2"s, fmt::merge(lst{vec{"a", "1"}, vec{"b", "2"}}, " *-* ")); } - TEST(StrUtil, test_get_file_extension) + TEST(StrUtil, GetFileExtension) { EXPECT_EQ(""s, get_file_extension("")); EXPECT_EQ(""s, get_file_extension(".")); @@ -435,4 +435,28 @@ namespace fmt EXPECT_EQ(""s, get_file_extension("my_file/asd")); EXPECT_EQ("txt"s, get_file_extension("my_file/asd.txt")); } + + TEST(StrUtil, StrcpyTrunc) + { + char dst[13]; + std::memset(dst, 'A', sizeof(dst)); + strcpy_trunc(dst, ""); + EXPECT_TRUE(std::all_of(dst, dst + sizeof(dst), [](char c){ return c == '\0'; })); + + std::memset(dst, 'A', sizeof(dst)); + strcpy_trunc(dst, "Hello"); + EXPECT_EQ('\0', dst[5]); + EXPECT_EQ(std::string(dst), "Hello"); + EXPECT_TRUE(std::all_of(dst + 5, dst + sizeof(dst), [](char c){ return c == '\0'; })); + + std::memset(dst, 'A', sizeof(dst)); + strcpy_trunc(dst, "Hello World!"); + EXPECT_EQ('\0', dst[12]); + EXPECT_EQ(std::string(dst), "Hello World!"); + + std::memset(dst, 'A', sizeof(dst)); + strcpy_trunc(dst, "Hello World! Here I am!"); + EXPECT_EQ('\0', dst[12]); + EXPECT_EQ(std::string(dst), "Hello World!"); + } } From fc3a905c90330da2f531361c51322cf279835457 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Tue, 6 May 2025 09:15:05 +0200 Subject: [PATCH 03/14] Use string_view for log params --- rpcs3/rpcs3.cpp | 2 +- rpcs3/rpcs3qt/log_frame.cpp | 2 +- rpcs3/util/logs.cpp | 8 ++++---- rpcs3/util/logs.hpp | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/rpcs3/rpcs3.cpp b/rpcs3/rpcs3.cpp index 73f6984d51..1942de378d 100644 --- a/rpcs3/rpcs3.cpp +++ b/rpcs3/rpcs3.cpp @@ -303,7 +303,7 @@ struct fatal_error_listener final : logs::listener public: ~fatal_error_listener() override = default; - void log(u64 /*stamp*/, const logs::message& msg, const std::string& prefix, const std::string& text) override + void log(u64 /*stamp*/, const logs::message& msg, std::string_view prefix, std::string_view text) override { if (msg == logs::level::fatal || (msg == logs::level::always && m_log_always)) { diff --git a/rpcs3/rpcs3qt/log_frame.cpp b/rpcs3/rpcs3qt/log_frame.cpp index fd294553ff..4dd664a99e 100644 --- a/rpcs3/rpcs3qt/log_frame.cpp +++ b/rpcs3/rpcs3qt/log_frame.cpp @@ -50,7 +50,7 @@ struct gui_listener : logs::listener { } - void log(u64 stamp, const logs::message& msg, const std::string& prefix, const std::string& text) override + void log(u64 stamp, const logs::message& msg, std::string_view prefix, std::string_view text) override { Q_UNUSED(stamp) diff --git a/rpcs3/util/logs.cpp b/rpcs3/util/logs.cpp index 79ae9e8f4c..f5573c1d65 100644 --- a/rpcs3/util/logs.cpp +++ b/rpcs3/util/logs.cpp @@ -118,7 +118,7 @@ namespace logs ~file_listener() override = default; - void log(u64 stamp, const message& msg, const std::string& prefix, const std::string& text) override; + void log(u64 stamp, const message& msg, std::string_view prefix, std::string_view text) override; void sync() override { @@ -138,7 +138,7 @@ namespace logs ~root_listener() override = default; // Encode level, current thread name, channel name and write log message - void log(u64, const message&, const std::string&, const std::string&) override + void log(u64, const message&, std::string_view, std::string_view) override { // Do nothing } @@ -251,7 +251,7 @@ namespace logs { std::lock_guard lock(g_mutex); - auto found = get_logger()->channels.equal_range(ch_name); + const auto found = get_logger()->channels.equal_range(ch_name); if (found.first != found.second) { @@ -771,7 +771,7 @@ logs::file_listener::file_listener(const std::string& path, u64 max_size) file_writer::log("\xEF\xBB\xBF", 3); } -void logs::file_listener::log(u64 stamp, const logs::message& msg, const std::string& prefix, const std::string& _text) +void logs::file_listener::log(u64 stamp, const logs::message& msg, std::string_view prefix, std::string_view _text) { /*constinit thread_local*/ std::string text; text.reserve(50000); diff --git a/rpcs3/util/logs.hpp b/rpcs3/util/logs.hpp index 21b00c4b35..52d163ed43 100644 --- a/rpcs3/util/logs.hpp +++ b/rpcs3/util/logs.hpp @@ -79,7 +79,7 @@ namespace logs virtual ~listener(); // Process log message - virtual void log(u64 stamp, const message& msg, const std::string& prefix, const std::string& text) = 0; + virtual void log(u64 stamp, const message& msg, std::string_view prefix, std::string_view text) = 0; // Flush contents (file writer) virtual void sync(); From 0146b845d9efb45c3c37f95843fca01e33cdfbf9 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Wed, 7 May 2025 20:20:52 +0200 Subject: [PATCH 04/14] MSVC: Fix weird std::move on const variable warning... --- rpcs3/rpcs3.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/rpcs3/rpcs3.cpp b/rpcs3/rpcs3.cpp index 1942de378d..f7efb7728b 100644 --- a/rpcs3/rpcs3.cpp +++ b/rpcs3/rpcs3.cpp @@ -1192,7 +1192,7 @@ int run_rpcs3(int argc, char** argv) } else if (const QStringList args = parser.positionalArguments(); (!args.isEmpty() || !emu_argv.empty()) && !is_updating && !parser.isSet(arg_installfw) && !parser.isSet(arg_installpkg)) { - const std::string spath = (args.isEmpty() ? emu_argv[0] : ::at32(args, 0).toStdString()); + std::string spath = (args.isEmpty() ? emu_argv[0] : ::at32(args, 0).toStdString()); if (spath.starts_with(Emulator::vfs_boot_prefix)) { @@ -1275,8 +1275,13 @@ int run_rpcs3(int argc, char** argv) } } + if (!spath.starts_with("%RPCS3_")) + { + spath = QFileInfo(::at32(args, 0)).absoluteFilePath().toStdString(); + } + // Postpone startup to main event loop - Emu.CallFromMainThread([path = spath.starts_with("%RPCS3_") ? spath : QFileInfo(::at32(args, 0)).absoluteFilePath().toStdString(), rpcs3_argv = std::move(rpcs3_argv), config_path = std::move(config_path)]() mutable + Emu.CallFromMainThread([path = std::move(spath), rpcs3_argv = std::move(rpcs3_argv), config_path = std::move(config_path)]() mutable { Emu.argv = std::move(rpcs3_argv); Emu.SetForceBoot(true); From 9c7d8da298183b8af39a267cacc8c3a4bb6a0aa2 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Wed, 7 May 2025 20:40:01 +0200 Subject: [PATCH 05/14] Add log message if game ignores exit game request --- rpcs3/Emu/System.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/rpcs3/Emu/System.cpp b/rpcs3/Emu/System.cpp index 1e994d829a..2a6cc73a14 100644 --- a/rpcs3/Emu/System.cpp +++ b/rpcs3/Emu/System.cpp @@ -2873,7 +2873,7 @@ void Emulator::GracefulShutdown(bool allow_autoexit, bool async_op, bool savesta Emu.SetContinuousMode(false); } - // Ensure no game has booted inbetween + // Ensure no game has booted in between const auto guard = Emu.MakeEmulationStateGuard(); stop_counter_t old_emu_id{}; @@ -2922,6 +2922,11 @@ void Emulator::GracefulShutdown(bool allow_autoexit, bool async_op, bool savesta if (old_state == system_state::frozen || savestate || !sysutil_send_system_cmd(0x0101 /* CELL_SYSUTIL_REQUEST_EXITGAME */, 0)) { + if (old_state != system_state::frozen && !savestate) + { + sys_log.warning("The running game ignored the exit request. Forcing termination..."); + } + // The callback has been rudely ignored, we have no other option but to force termination Kill(allow_autoexit && !savestate, savestate); From fdc1a51cba5c3b5b035f35137720df7cee87c03a Mon Sep 17 00:00:00 2001 From: Megamouse Date: Wed, 7 May 2025 20:54:01 +0200 Subject: [PATCH 06/14] Log RPCS3 exit code --- rpcs3/main.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/rpcs3/main.cpp b/rpcs3/main.cpp index 8666b9c49f..1ea2321804 100644 --- a/rpcs3/main.cpp +++ b/rpcs3/main.cpp @@ -1,6 +1,11 @@ +#include "stdafx.h" #include "rpcs3.h" +LOG_CHANNEL(sys_log, "SYS"); + int main(int argc, char** argv) { - return run_rpcs3(argc, argv); + const int exit_code = run_rpcs3(argc, argv); + sys_log.notice("RPCS3 terminated with exit code %d", exit_code); + return exit_code; } From 30a4b26306ad37b2180fc50f6248f1788fa04624 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Wed, 7 May 2025 21:20:09 +0200 Subject: [PATCH 07/14] Add log message when QApplication is being quit --- rpcs3/headless_application.cpp | 3 +++ rpcs3/rpcs3qt/gui_application.cpp | 2 ++ 2 files changed, 5 insertions(+) diff --git a/rpcs3/headless_application.cpp b/rpcs3/headless_application.cpp index 0d0eb8d834..43de0e108f 100644 --- a/rpcs3/headless_application.cpp +++ b/rpcs3/headless_application.cpp @@ -12,6 +12,8 @@ #include +LOG_CHANNEL(sys_log, "SYS"); + [[noreturn]] void report_fatal_error(std::string_view text, bool is_html = false, bool include_help_text = true); // For now, a trivial constructor/destructor. May add command line usage later. @@ -56,6 +58,7 @@ void headless_application::InitializeCallbacks() on_exit(); } + sys_log.notice("Quitting headless application"); quit(); return true; } diff --git a/rpcs3/rpcs3qt/gui_application.cpp b/rpcs3/rpcs3qt/gui_application.cpp index b170737e43..82f29fcdbd 100644 --- a/rpcs3/rpcs3qt/gui_application.cpp +++ b/rpcs3/rpcs3qt/gui_application.cpp @@ -593,6 +593,8 @@ void gui_application::InitializeCallbacks() // Close main window in order to save its window state m_main_window->close(); } + + gui_log.notice("Quitting gui application"); quit(); return true; } From 738d1ef682c024c19426cf05738da42d749bae18 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Wed, 7 May 2025 22:04:19 +0200 Subject: [PATCH 08/14] Emu: Make game termination less confusing --- rpcs3/Emu/System.cpp | 35 +++++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/rpcs3/Emu/System.cpp b/rpcs3/Emu/System.cpp index 2a6cc73a14..b636bfbf9c 100644 --- a/rpcs3/Emu/System.cpp +++ b/rpcs3/Emu/System.cpp @@ -2920,11 +2920,18 @@ void Emulator::GracefulShutdown(bool allow_autoexit, bool async_op, bool savesta const u64 read_counter = get_sysutil_cb_manager_read_count(); - if (old_state == system_state::frozen || savestate || !sysutil_send_system_cmd(0x0101 /* CELL_SYSUTIL_REQUEST_EXITGAME */, 0)) + const bool force_termination = old_state == system_state::frozen || savestate; + + if (!force_termination) { - if (old_state != system_state::frozen && !savestate) + sys_log.notice("Requesting game to exit..."); + } + + if (force_termination || !sysutil_send_system_cmd(0x0101 /* CELL_SYSUTIL_REQUEST_EXITGAME */, 0)) + { + if (!force_termination) { - sys_log.warning("The running game ignored the exit request. Forcing termination..."); + sys_log.warning("The game ignored the exit request. Forcing termination..."); } // The callback has been rudely ignored, we have no other option but to force termination @@ -2941,15 +2948,20 @@ void Emulator::GracefulShutdown(bool allow_autoexit, bool async_op, bool savesta return; } + sys_log.notice("The game was requested to exit. Waiting for its reaction..."); + auto perform_kill = [read_counter, allow_autoexit, this, info = GetEmulationIdentifier()]() { bool read_sysutil_signal = false; - u32 i = 100; + // If EXITGAME signal is not read, force kill after a couple of seconds. + constexpr int loop_timeout_ms = 50; + int kill_timeout_ms = 2000; + int elapsed_ms = 0; - qt_events_aware_op(50, [&]() + qt_events_aware_op(loop_timeout_ms, [&]() { - if (i >= 140) + if (elapsed_ms >= kill_timeout_ms) { return true; } @@ -2960,9 +2972,11 @@ void Emulator::GracefulShutdown(bool allow_autoexit, bool async_op, bool savesta Resume(); }, nullptr, true, read_counter); + // Check if the EXITGAME signal was read. We allow the game to terminate itself if that's the case. if (!read_sysutil_signal && read_counter != get_sysutil_cb_manager_read_count()) { - i -= 100; // Grant 5 seconds (if signal is not read force kill after two second) + sys_log.notice("The game received the exit request. Waiting for it to terminate itself..."); + kill_timeout_ms += 5000; // Grant a couple more seconds read_sysutil_signal = true; } @@ -2972,13 +2986,14 @@ void Emulator::GracefulShutdown(bool allow_autoexit, bool async_op, bool savesta } // Process events - i++; + elapsed_ms += loop_timeout_ms; return false; }); - // An inevitable attempt to terminate the *current* emulation course will be issued after 7s - CallFromMainThread([allow_autoexit, this]() + // An inevitable attempt to terminate the *current* emulation course will be issued after the timeout was reached. + CallFromMainThread([this, allow_autoexit, elapsed_ms, read_sysutil_signal]() { + sys_log.error("The game did not react to the exit request in time. Terminating manually... (read_sysutil_signal=%d, elapsed_ms=%d)", read_sysutil_signal, elapsed_ms); Kill(allow_autoexit); }, info); }; From 73a6141f3fc2d196bc38c35728498a030c925c8e Mon Sep 17 00:00:00 2001 From: Megamouse Date: Wed, 7 May 2025 23:08:25 +0200 Subject: [PATCH 09/14] overlays: Allow auto exit in home menu's exit game function In case the game does not terminate itself properly. --- rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_main_menu.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_main_menu.cpp b/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_main_menu.cpp index f9119a85e1..acd9f196f4 100644 --- a/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_main_menu.cpp +++ b/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_main_menu.cpp @@ -138,7 +138,7 @@ namespace rsx rsx_log.notice("User selected exit game in home menu"); Emu.CallFromMainThread([] { - Emu.GracefulShutdown(false, true); + Emu.GracefulShutdown(true, true); }); return page_navigation::stay; }); From 501643c10a8ae86a1678c3ea59f4d63712e8ec58 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Thu, 8 May 2025 08:16:20 +0200 Subject: [PATCH 10/14] Use const for present_samples buf argument --- rpcs3/Emu/Cell/Modules/cellAudio.cpp | 2 +- rpcs3/Emu/Cell/lv2/sys_rsxaudio.cpp | 2 +- rpcs3/util/video_provider.cpp | 2 +- rpcs3/util/video_provider.h | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/rpcs3/Emu/Cell/Modules/cellAudio.cpp b/rpcs3/Emu/Cell/Modules/cellAudio.cpp index 379c17f112..caad402194 100644 --- a/rpcs3/Emu/Cell/Modules/cellAudio.cpp +++ b/rpcs3/Emu/Cell/Modules/cellAudio.cpp @@ -301,7 +301,7 @@ void audio_ringbuffer::commit_data(f32* buf, u32 sample_cnt) if (g_recording_mode != recording_mode::stopped) { utils::video_provider& provider = g_fxo->get(); - provider.present_samples(reinterpret_cast(buf), sample_cnt, cfg.audio_channels); + provider.present_samples(reinterpret_cast(buf), sample_cnt, cfg.audio_channels); } // Downmix if necessary diff --git a/rpcs3/Emu/Cell/lv2/sys_rsxaudio.cpp b/rpcs3/Emu/Cell/lv2/sys_rsxaudio.cpp index 9fa3a890a8..2ba15b1146 100644 --- a/rpcs3/Emu/Cell/lv2/sys_rsxaudio.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_rsxaudio.cpp @@ -1858,7 +1858,7 @@ u32 rsxaudio_backend_thread::write_data_callback(u32 bytes, void* buf) if (g_recording_mode != recording_mode::stopped) { utils::video_provider& provider = g_fxo->get(); - provider.present_samples(reinterpret_cast(callback_tmp_buf.data()), sample_cnt / cb_cfg.input_ch_cnt, cb_cfg.input_ch_cnt); + provider.present_samples(reinterpret_cast(callback_tmp_buf.data()), sample_cnt / cb_cfg.input_ch_cnt, cb_cfg.input_ch_cnt); } // Downmix if necessary diff --git a/rpcs3/util/video_provider.cpp b/rpcs3/util/video_provider.cpp index 70888447f4..82f133b91e 100644 --- a/rpcs3/util/video_provider.cpp +++ b/rpcs3/util/video_provider.cpp @@ -162,7 +162,7 @@ namespace utils } } - void video_provider::present_samples(u8* buf, u32 sample_count, u16 channels) + void video_provider::present_samples(const u8* buf, u32 sample_count, u16 channels) { if (!buf || !sample_count || !channels || !m_active) { diff --git a/rpcs3/util/video_provider.h b/rpcs3/util/video_provider.h index 0d2c29edfe..e21b5185ee 100644 --- a/rpcs3/util/video_provider.h +++ b/rpcs3/util/video_provider.h @@ -23,7 +23,7 @@ namespace utils bool can_consume_frame(); void present_frame(std::vector& data, u32 pitch, u32 width, u32 height, bool is_bgra); - void present_samples(u8* buf, u32 sample_count, u16 channels); + void present_samples(const u8* buf, u32 sample_count, u16 channels); private: recording_mode check_mode(); From 2963d10325bb1b967350da64d73c19ab62c98426 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Thu, 8 May 2025 08:21:43 +0200 Subject: [PATCH 11/14] cellSysutilCheckCallback: increase read_counter as soon as a callback is about to be called The game might call sys_process_exit in one of the callbacks. This might take a couple of seconds and does not increase the counter. So we have to increase it before in order to give Kill some more time. --- rpcs3/Emu/Cell/Modules/cellSysutil.cpp | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/rpcs3/Emu/Cell/Modules/cellSysutil.cpp b/rpcs3/Emu/Cell/Modules/cellSysutil.cpp index a236398598..a7d15af2d5 100644 --- a/rpcs3/Emu/Cell/Modules/cellSysutil.cpp +++ b/rpcs3/Emu/Cell/Modules/cellSysutil.cpp @@ -580,8 +580,6 @@ error_code cellSysutilCheckCallback(ppu_thread& ppu) auto& cbm = g_fxo->get(); - bool read = false; - for (auto&& func : cbm.registered.pop_all()) { if (func.call_active && !*func.call_active) @@ -589,7 +587,11 @@ error_code cellSysutilCheckCallback(ppu_thread& ppu) continue; } - read = true; + // Increase read counter before we call the callback. + // We use this counter to check if the game reacts to a command during game termination and calls sys_process_exit. + // We would not realize that the game reacted in time and terminate it prematurely if we increased + // the counter after we called the callback and the callback did some time-consuming work. + cbm.read_counter++; if (s32 res = func.func(ppu)) { @@ -603,11 +605,6 @@ error_code cellSysutilCheckCallback(ppu_thread& ppu) } } - if (read) - { - cbm.read_counter++; - } - return CELL_OK; } From f0672bdbc92b1d1c32899f3fbac41693e403bf53 Mon Sep 17 00:00:00 2001 From: Katharine Chui Date: Thu, 8 May 2025 11:48:27 +0200 Subject: [PATCH 12/14] initialize sdl haptic, log haptic device open errors --- rpcs3/Emu/Io/LogitechG27.cpp | 2 +- rpcs3/Input/sdl_instance.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/rpcs3/Emu/Io/LogitechG27.cpp b/rpcs3/Emu/Io/LogitechG27.cpp index 15106714b7..b2311719d8 100644 --- a/rpcs3/Emu/Io/LogitechG27.cpp +++ b/rpcs3/Emu/Io/LogitechG27.cpp @@ -266,7 +266,7 @@ void usb_device_logitech_g27::sdl_refresh() SDL_Haptic* cur_haptic = SDL_OpenHapticFromJoystick(cur_joystick); if (cur_haptic == nullptr) { - logitech_g27_log.error("Failed opening haptic device from selected ffb device %04x:%04x", cur_vendor_id, cur_product_id); + logitech_g27_log.error("Failed opening haptic device from selected ffb device %04x:%04x, %s", cur_vendor_id, cur_product_id, SDL_GetError()); } else { diff --git a/rpcs3/Input/sdl_instance.cpp b/rpcs3/Input/sdl_instance.cpp index 2cc434c006..bfff27a746 100644 --- a/rpcs3/Input/sdl_instance.cpp +++ b/rpcs3/Input/sdl_instance.cpp @@ -69,7 +69,7 @@ bool sdl_instance::initialize() set_hint(SDL_HINT_JOYSTICK_HIDAPI_PS3, "1"); #endif - if (!SDL_Init(SDL_INIT_JOYSTICK | SDL_INIT_GAMEPAD)) + if (!SDL_Init(SDL_INIT_JOYSTICK | SDL_INIT_GAMEPAD | SDL_INIT_HAPTIC)) { sdl_log.error("Could not initialize! SDL Error: %s", SDL_GetError()); return false; From 4840e3575b47297cc8d74f2175d7b5727254db8b Mon Sep 17 00:00:00 2001 From: Katharine Chui Date: Thu, 8 May 2025 13:03:50 +0200 Subject: [PATCH 13/14] remove SDL_INIT_JOYSTICK since SDL_INIT_GAMEPAD implies SDL_INIT_JOYSTICK --- rpcs3/Input/sdl_instance.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rpcs3/Input/sdl_instance.cpp b/rpcs3/Input/sdl_instance.cpp index bfff27a746..e27ed5f8ac 100644 --- a/rpcs3/Input/sdl_instance.cpp +++ b/rpcs3/Input/sdl_instance.cpp @@ -69,7 +69,7 @@ bool sdl_instance::initialize() set_hint(SDL_HINT_JOYSTICK_HIDAPI_PS3, "1"); #endif - if (!SDL_Init(SDL_INIT_JOYSTICK | SDL_INIT_GAMEPAD | SDL_INIT_HAPTIC)) + if (!SDL_Init(SDL_INIT_GAMEPAD | SDL_INIT_HAPTIC)) { sdl_log.error("Could not initialize! SDL Error: %s", SDL_GetError()); return false; From 3ea3ed6672dc9e42a7b2699eb3bce1d59d2c7a58 Mon Sep 17 00:00:00 2001 From: elad335 <18193363+elad335@users.noreply.github.com> Date: Thu, 8 May 2025 13:22:35 +0300 Subject: [PATCH 14/14] Emu: Track game termination progress better --- rpcs3/Emu/System.cpp | 39 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/rpcs3/Emu/System.cpp b/rpcs3/Emu/System.cpp index b636bfbf9c..97a96ce9b3 100644 --- a/rpcs3/Emu/System.cpp +++ b/rpcs3/Emu/System.cpp @@ -2953,10 +2953,11 @@ void Emulator::GracefulShutdown(bool allow_autoexit, bool async_op, bool savesta auto perform_kill = [read_counter, allow_autoexit, this, info = GetEmulationIdentifier()]() { bool read_sysutil_signal = false; + std::vector>> ppu_thread_list; - // If EXITGAME signal is not read, force kill after a couple of seconds. + // If EXITGAME signal is not read, force kill after a second. constexpr int loop_timeout_ms = 50; - int kill_timeout_ms = 2000; + int kill_timeout_ms = 1000; int elapsed_ms = 0; qt_events_aware_op(loop_timeout_ms, [&]() @@ -2978,18 +2979,50 @@ void Emulator::GracefulShutdown(bool allow_autoexit, bool async_op, bool savesta sys_log.notice("The game received the exit request. Waiting for it to terminate itself..."); kill_timeout_ms += 5000; // Grant a couple more seconds read_sysutil_signal = true; + + // Observe PPU threads state since this stage + idm::select>([&](u32 id, cpu_thread&) + { + ppu_thread_list.emplace_back(idm::get_unlocked>(id)); + }); } - if (static_cast(info) != m_stop_ctr) + if (static_cast(info) != m_stop_ctr || Emu.IsStopped()) { return true; } + if (read_sysutil_signal && kill_timeout_ms - elapsed_ms <= 1'500) + { + int thread_exited_count = 0; + + for (auto& ppu : ppu_thread_list) + { + if (ppu && (ppu->state & cpu_flag::exit || ppu->joiner == ppu_join_status::zombie)) + { + ppu.reset(); + thread_exited_count++; + } + } + + if (thread_exited_count) + { + // If some threads exited since last check, grant additional 250 miliseconds + kill_timeout_ms += 250; + sys_log.notice("Threads were terminated.. increasing termination timeout (threads=%d)", thread_exited_count); + } + } + // Process events elapsed_ms += loop_timeout_ms; return false; }); + if (Emu.IsStopped(true)) + { + return; + } + // An inevitable attempt to terminate the *current* emulation course will be issued after the timeout was reached. CallFromMainThread([this, allow_autoexit, elapsed_ms, read_sysutil_signal]() {