From 8e070e8b1c6d267d743cb2b135de419057bcc660 Mon Sep 17 00:00:00 2001 From: digant Date: Mon, 6 Jan 2025 17:28:14 +0100 Subject: [PATCH] update --- rpcs3/Emu/CMakeLists.txt | 1 + rpcs3/Emu/Cell/lv2/sys_process.cpp | 3 + rpcs3/Emu/RSX/GL/GLGSRender.cpp | 8 + rpcs3/Emu/RSX/GL/GLGSRender.h | 1 + rpcs3/Emu/RSX/GSFrameBase.h | 1 + rpcs3/Emu/RSX/GSRender.cpp | 7 +- rpcs3/Emu/RSX/GSRender.h | 3 + .../HomeMenu/overlay_home_menu_main_menu.cpp | 2 + .../HomeMenu/overlay_home_menu_savestate.cpp | 3 + rpcs3/Emu/System.cpp | 152 +++++++----------- rpcs3/Emu/System.h | 16 +- rpcs3/Emu/localized_string_id.h | 1 + rpcs3/Emu/savestate_utils.cpp | 5 +- rpcs3/Emu/system_progress.cpp | 85 ++++++---- rpcs3/Emu/system_progress.hpp | 9 +- rpcs3/emucore.vcxproj | 1 + rpcs3/emucore.vcxproj.filters | 3 + rpcs3/headless_application.cpp | 1 + rpcs3/main_application.cpp | 4 +- rpcs3/main_application.h | 5 +- rpcs3/rpcs3qt/gl_gs_frame.cpp | 5 + rpcs3/rpcs3qt/gl_gs_frame.h | 1 + rpcs3/rpcs3qt/gs_frame.cpp | 18 +++ rpcs3/rpcs3qt/gs_frame.h | 9 ++ rpcs3/rpcs3qt/gui_application.cpp | 61 +++++-- rpcs3/rpcs3qt/localized_emu.h | 1 + rpcs3/rpcs3qt/main_window.cpp | 3 + 27 files changed, 263 insertions(+), 146 deletions(-) diff --git a/rpcs3/Emu/CMakeLists.txt b/rpcs3/Emu/CMakeLists.txt index 4cd083ab90..04ed28e10f 100644 --- a/rpcs3/Emu/CMakeLists.txt +++ b/rpcs3/Emu/CMakeLists.txt @@ -172,6 +172,7 @@ target_link_libraries(rpcs3_emu # Cell target_sources(rpcs3_emu PRIVATE + Cell/ErrorCodes.cpp Cell/MFC.cpp Cell/PPUAnalyser.cpp Cell/PPUDisAsm.cpp diff --git a/rpcs3/Emu/Cell/lv2/sys_process.cpp b/rpcs3/Emu/Cell/lv2/sys_process.cpp index 8038ffc248..45eb5c1858 100644 --- a/rpcs3/Emu/Cell/lv2/sys_process.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_process.cpp @@ -498,6 +498,9 @@ void lv2_exitspawn(ppu_thread& ppu, std::vector& argv, std::vector< }; signal_system_cache_can_stay(); + + // Make sure we keep the game window opened + Emu.SetContinuousMode(true); Emu.Kill(false); }); diff --git a/rpcs3/Emu/RSX/GL/GLGSRender.cpp b/rpcs3/Emu/RSX/GL/GLGSRender.cpp index 3e60af9f68..a2d080c86e 100644 --- a/rpcs3/Emu/RSX/GL/GLGSRender.cpp +++ b/rpcs3/Emu/RSX/GL/GLGSRender.cpp @@ -52,6 +52,14 @@ GLGSRender::GLGSRender(utils::serial* ar) noexcept : GSRender(ar) backend_config.supports_normalized_barycentrics = true; } +GLGSRender::~GLGSRender() +{ + if (m_frame) + { + m_frame->reset(); + } +} + extern CellGcmContextData current_context; void GLGSRender::set_viewport() diff --git a/rpcs3/Emu/RSX/GL/GLGSRender.h b/rpcs3/Emu/RSX/GL/GLGSRender.h index 866fe288e6..c8c6ba89dd 100644 --- a/rpcs3/Emu/RSX/GL/GLGSRender.h +++ b/rpcs3/Emu/RSX/GL/GLGSRender.h @@ -159,6 +159,7 @@ public: GLGSRender(utils::serial* ar) noexcept; GLGSRender() noexcept : GLGSRender(nullptr) {} + virtual ~GLGSRender(); private: diff --git a/rpcs3/Emu/RSX/GSFrameBase.h b/rpcs3/Emu/RSX/GSFrameBase.h index f345f6255e..fd91244ffe 100644 --- a/rpcs3/Emu/RSX/GSFrameBase.h +++ b/rpcs3/Emu/RSX/GSFrameBase.h @@ -14,6 +14,7 @@ public: virtual ~GSFrameBase() = default; virtual void close() = 0; + virtual void reset() = 0; virtual bool shown() = 0; virtual void hide() = 0; virtual void show() = 0; diff --git a/rpcs3/Emu/RSX/GSRender.cpp b/rpcs3/Emu/RSX/GSRender.cpp index 917a772a34..2025842dab 100644 --- a/rpcs3/Emu/RSX/GSRender.cpp +++ b/rpcs3/Emu/RSX/GSRender.cpp @@ -18,7 +18,7 @@ GSRender::~GSRender() { m_context = nullptr; - if (m_frame) + if (m_frame && !m_continuous_mode) { m_frame->close(); } @@ -39,7 +39,10 @@ void GSRender::on_exit() if (m_frame) { - m_frame->hide(); + if (!m_continuous_mode) + { + m_frame->hide(); + } m_frame->delete_context(m_context); m_context = nullptr; } diff --git a/rpcs3/Emu/RSX/GSRender.h b/rpcs3/Emu/RSX/GSRender.h index eea040bf29..d2a6fd9c5f 100644 --- a/rpcs3/Emu/RSX/GSRender.h +++ b/rpcs3/Emu/RSX/GSRender.h @@ -21,12 +21,15 @@ class GSRender : public rsx::thread protected: GSFrameBase* m_frame; draw_context_t m_context = nullptr; + bool m_continuous_mode = false; public: ~GSRender() override; GSRender(utils::serial* ar) noexcept; + void set_continuous_mode(bool continuous_mode) { m_continuous_mode = continuous_mode; } + void on_init_thread() override; void on_exit() override; 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 d02b3ffbe2..6706e241c8 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 @@ -114,6 +114,8 @@ namespace rsx Emu.CallFromMainThread([]() { + // Make sure we keep the game window opened + Emu.SetContinuousMode(true); Emu.Restart(false); }); return page_navigation::exit; diff --git a/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_savestate.cpp b/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_savestate.cpp index 2ff9711d5b..e224bc5727 100644 --- a/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_savestate.cpp +++ b/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_savestate.cpp @@ -26,6 +26,9 @@ namespace rsx if (!suspend_mode) { Emu.after_kill_callback = []() { Emu.Restart(); }; + + // Make sure we keep the game window opened + Emu.SetContinuousMode(true); } Emu.Kill(false, true); }); diff --git a/rpcs3/Emu/System.cpp b/rpcs3/Emu/System.cpp index 551e7de36d..dd41a5f71d 100644 --- a/rpcs3/Emu/System.cpp +++ b/rpcs3/Emu/System.cpp @@ -60,13 +60,13 @@ #include "Emu/RSX/VK/VulkanAPI.h" #endif +#include "Emu/RSX/GSRender.h" + LOG_CHANNEL(sys_log, "SYS"); // Preallocate 32 MiB stx::manual_typemap g_fixed_typemap; -bool g_log_all_errors = false; - bool g_use_rtm = false; u64 g_rtm_tx_limit1 = 0; u64 g_rtm_tx_limit2 = 0; @@ -964,6 +964,11 @@ game_boot_result Emulator::BootGame(const std::string& path, const std::string& std::tie(m_path, m_path_original, argv, envp, data, disc, klic, hdd1, m_config_mode, m_config_path) = std::move(save_args); }; } + + if (result != game_boot_result::no_errors) + { + GetCallbacks().close_gs_frame(); + } } return result; @@ -1005,6 +1010,16 @@ void Emulator::SetForceBoot(bool force_boot) m_force_boot = force_boot; } +void Emulator::SetContinuousMode(bool continuous_mode) +{ + m_continuous_mode = continuous_mode; + + if (GSRender* render = static_cast(g_fxo->try_get())) + { + render->set_continuous_mode(continuous_mode); + } +} + game_boot_result Emulator::Load(const std::string& title_id, bool is_disc_patch, usz recursion_count) { if (recursion_count == 0 && m_restrict_emu_state_change) @@ -1132,7 +1147,7 @@ game_boot_result Emulator::Load(const std::string& title_id, bool is_disc_patch, bool resolve_path_as_vfs_path = false; - const bool from_dev_flash = IsPathInsideDir(m_path, g_cfg_vfs.get_dev_flash()); + const bool from_dev_flash = IsPathInsideDir(m_path, g_cfg_vfs.get_dev_flash()); std::string savestate_build_version; std::string savestate_creation_date; @@ -2895,8 +2910,14 @@ u64 get_sysutil_cb_manager_read_count(); void qt_events_aware_op(int repeat_duration_ms, std::function wrapped_op); -void Emulator::GracefulShutdown(bool allow_autoexit, bool async_op, bool savestate) +void Emulator::GracefulShutdown(bool allow_autoexit, bool async_op, bool savestate, bool continuous_mode) { + // Make sure we close the game window + if (!continuous_mode) + { + Emu.SetContinuousMode(false); + } + // Ensure no game has booted inbetween const auto guard = Emu.MakeEmulationStateGuard(); @@ -3040,6 +3061,22 @@ void Emulator::Kill(bool allow_autoexit, bool savestate, savestate_stage* save_s *pause_thread = make_ptr(new named_thread("Savestate Prepare Thread"sv, [pause_thread, allow_autoexit, this]() mutable { + struct scoped_success_guard + { + bool save_state_success = false; + ~scoped_success_guard() + { + if (!save_state_success) + { + // Reset continuous mode on savestate error + Emu.SetContinuousMode(false); + + // Reset after_kill_callback (which is usually used for Emu.Restart in combination with savestates) + Emu.after_kill_callback = nullptr; + } + } + } success_guard {}; + std::vector>, u32>> paused_spus; if (!try_lock_spu_threads_in_a_state_compatible_with_savestates(false, &paused_spus)) @@ -3110,6 +3147,8 @@ void Emulator::Kill(bool allow_autoexit, bool savestate, savestate_stage* save_s return; } + success_guard.save_state_success = true; + CallFromMainThread([allow_autoexit, this, paused_spus]() { savestate_stage stage{}; @@ -3185,15 +3224,15 @@ void Emulator::Kill(bool allow_autoexit, bool savestate, savestate_stage* save_s sys_log.notice("Stopping emulator..."); + const bool continuous_savestate_mode = savestate && !g_cfg.savestate.suspend_emu; + + // Show visual feedback to the user in case that stopping takes a while. + // This needs to be done before actually stopping, because otherwise the necessary threads will be terminated before we can show an image. + if (g_fxo->try_get>() && (continuous_savestate_mode || g_progr_text.operator bool())) { - // Show visual feedback to the user in case that stopping takes a while. - // This needs to be done before actually stopping, because otherwise the necessary threads will be terminated before we can show an image. - if (auto progress_dialog = g_fxo->try_get>(); progress_dialog && g_progr_text.operator bool()) - { - // We are currently showing a progress dialog. Notify it that we are going to stop emulation. - g_system_progress_stopping = true; - std::this_thread::sleep_for(20ms); // Enough for one frame to be rendered - } + // Notify progress dialog that we are going to stop emulation + g_system_progress_stopping = continuous_savestate_mode ? system_progress_stop_state::stop_state_continuous_savestate : system_progress_stop_state::stop_state_stopping; + std::this_thread::sleep_for(30ms); // Enough for one frame to be rendered } // Signal threads @@ -3265,7 +3304,10 @@ void Emulator::Kill(bool allow_autoexit, bool savestate, savestate_stage* save_s if (auto ar_ptr = to_ar->load()) { // Total amount of waiting: about 10s - GetCallbacks().on_save_state_progress(closed_sucessfully, ar_ptr, verbose_message.get(), init_mtx); + if (g_cfg.savestate.suspend_emu) + { + GetCallbacks().on_save_state_progress(closed_sucessfully, ar_ptr, verbose_message.get(), init_mtx); + } while (thread_ctrl::state() != thread_state::aborting) { @@ -3278,7 +3320,6 @@ void Emulator::Kill(bool allow_autoexit, bool savestate, savestate_stage* save_s thread_ctrl::wait_for(5'000); } - *closed_sucessfully = true; })); @@ -3849,89 +3890,6 @@ std::string Emulator::GetFormattedTitle(double fps) const return rpcs3::get_formatted_title(title_data); } -s32 error_code::error_report(s32 result, const logs::message* channel, const char* fmt, const fmt_type_info* sup, const u64* args) -{ - static thread_local std::string g_tls_error_str; - static thread_local std::unordered_map g_tls_error_stats; - - if (!channel) - { - channel = &sys_log.error; - } - - if (!sup && !args) - { - if (!fmt) - { - // Report and clean error state - for (auto&& pair : g_tls_error_stats) - { - if (pair.second > 3) - { - channel->operator()("Stat: %s [x%u]", pair.first, pair.second); - } - } - - g_tls_error_stats.clear(); - return 0; - } - } - - ensure(fmt); - - const char* func = "Unknown function"; - - if (auto ppu = get_current_cpu_thread()) - { - if (auto current = ppu->current_function) - { - func = current; - } - } - else if (auto spu = get_current_cpu_thread()) - { - if (auto current = spu->current_func; current && spu->start_time) - { - func = current; - } - } - - // Format log message (use preallocated buffer) - g_tls_error_str.clear(); - - fmt::append(g_tls_error_str, "'%s' failed with 0x%08x", func, result); - - // Add spacer between error and fmt if necessary - if (fmt[0] != ' ') - g_tls_error_str += " : "; - - fmt::raw_append(g_tls_error_str, fmt, sup, args); - - // Update stats and check log threshold - - if (g_log_all_errors) [[unlikely]] - { - if (!g_tls_error_stats.empty()) - { - // Report and clean error state - error_report(0, nullptr, nullptr, nullptr, nullptr); - } - - channel->operator()("%s", g_tls_error_str); - } - else - { - const auto stat = ++g_tls_error_stats[g_tls_error_str]; - - if (stat <= 3) - { - channel->operator()("%s [%u]", g_tls_error_str, stat); - } - } - - return result; -} - void Emulator::ConfigurePPUCache() const { auto& _main = g_fxo->get>(); diff --git a/rpcs3/Emu/System.h b/rpcs3/Emu/System.h index d9d1991b3e..bd127aff4f 100644 --- a/rpcs3/Emu/System.h +++ b/rpcs3/Emu/System.h @@ -86,6 +86,7 @@ struct EmuCallbacks std::function init_pad_handler; std::function update_emu_settings; std::function save_emu_settings; + std::function close_gs_frame; std::function()> get_gs_frame; std::function()> get_camera_handler; std::function()> get_music_handler; @@ -154,6 +155,7 @@ class Emulator final // 2. It signifies that we don't want to exit on Kill(), for example if we want to transition to another application. bool m_force_boot = false; + bool m_continuous_mode = false; bool m_has_gui = true; bool m_state_inspection_savestate = false; @@ -346,6 +348,15 @@ public: return m_config_mode == cfg_mode::continuous; } + bool ContinuousModeEnabled(bool reset) + { + if (reset) + { + return std::exchange(m_continuous_mode, false); + } + return m_continuous_mode; + } + class emulation_state_guard_t { class Emulator* _this = nullptr; @@ -385,6 +396,7 @@ public: bool BootRsxCapture(const std::string& path); void SetForceBoot(bool force_boot); + void SetContinuousMode(bool continuous_mode); game_boot_result Load(const std::string& title_id = "", bool is_disc_patch = false, usz recursion_count = 0); void Run(bool start_playtime); @@ -407,7 +419,7 @@ public: bool Pause(bool freeze_emulation = false, bool show_resume_message = true); void Resume(); - void GracefulShutdown(bool allow_autoexit = true, bool async_op = false, bool savestate = false); + void GracefulShutdown(bool allow_autoexit = true, bool async_op = false, bool savestate = false, bool continuous_mode = false); void Kill(bool allow_autoexit = true, bool savestate = false, savestate_stage* stage = nullptr); game_boot_result Restart(bool graceful = true); bool Quit(bool force_quit); @@ -456,8 +468,6 @@ public: extern Emulator Emu; -extern bool g_log_all_errors; - extern bool g_use_rtm; extern u64 g_rtm_tx_limit1; extern u64 g_rtm_tx_limit2; diff --git a/rpcs3/Emu/localized_string_id.h b/rpcs3/Emu/localized_string_id.h index 21437762bb..d1f576051d 100644 --- a/rpcs3/Emu/localized_string_id.h +++ b/rpcs3/Emu/localized_string_id.h @@ -295,6 +295,7 @@ enum class localized_string_id PROGRESS_DIALOG_OF, PROGRESS_DIALOG_PLEASE_WAIT, PROGRESS_DIALOG_STOPPING_PLEASE_WAIT, + PROGRESS_DIALOG_SAVESTATE_PLEASE_WAIT, PROGRESS_DIALOG_SCANNING_PPU_EXECUTABLE, PROGRESS_DIALOG_ANALYZING_PPU_EXECUTABLE, PROGRESS_DIALOG_SCANNING_PPU_MODULES, diff --git a/rpcs3/Emu/savestate_utils.cpp b/rpcs3/Emu/savestate_utils.cpp index 85a2a82574..a40cda95c3 100644 --- a/rpcs3/Emu/savestate_utils.cpp +++ b/rpcs3/Emu/savestate_utils.cpp @@ -322,7 +322,10 @@ bool boot_last_savestate(bool testing) if (result) { sys_log.success("Booting the most recent savestate \'%s\' using the Reload shortcut.", savestate_path); - Emu.GracefulShutdown(false); + + // Make sure we keep the game window opened + Emu.SetContinuousMode(true); + Emu.GracefulShutdown(false, false, false, true); if (game_boot_result error = Emu.BootGame(savestate_path, "", true); error != game_boot_result::no_errors) { diff --git a/rpcs3/Emu/system_progress.cpp b/rpcs3/Emu/system_progress.cpp index 66d59af65b..52e49516fb 100644 --- a/rpcs3/Emu/system_progress.cpp +++ b/rpcs3/Emu/system_progress.cpp @@ -26,7 +26,7 @@ atomic_t g_progr_pdone{0}; atomic_t g_system_progress_canceled{false}; // For showing feedback while stopping emulation -atomic_t g_system_progress_stopping{false}; +atomic_t g_system_progress_stopping{system_progress_stop_state::stop_state_disabled}; namespace rsx::overlays { @@ -40,7 +40,7 @@ namespace rsx::overlays void progress_dialog_server::operator()() { std::shared_ptr native_dlg; - g_system_progress_stopping = false; + g_system_progress_stopping = system_progress_stop_state::stop_state_disabled; g_system_progress_canceled = false; const auto get_state = []() @@ -63,6 +63,41 @@ void progress_dialog_server::operator()() return whole_state; }; + const auto create_native_dialog = [&native_dlg](const std::string& text, bool* show_overlay_message) + { + if (const auto renderer = rsx::get_current_renderer()) + { + // Some backends like OpenGL actually initialize a lot of driver objects in the "on_init" method. + // Wait for init to complete within reasonable time. Abort just in case we have hardware/driver issues. + renderer->is_initialized.wait(0, atomic_wait_timeout(5 * 1000000000ull)); + + auto manager = g_fxo->try_get(); + + if (show_overlay_message) + { + *show_overlay_message = g_fxo->get().show_overlay_message_only; + if (*show_overlay_message) + { + return; + } + } + + if (manager) + { + MsgDialogType type{}; + type.se_mute_on = true; + type.se_normal = true; + type.bg_invisible = true; + type.disable_cancel = true; + type.progress_bar_count = 1; + + native_dlg = manager->create(true); + native_dlg->show(false, text, type, msg_dialog_source::sys_progress, nullptr); + native_dlg->progress_bar_set_message(0, get_localized_string(localized_string_id::PROGRESS_DIALOG_PLEASE_WAIT)); + } + } + }; + while (!g_system_progress_stopping && thread_ctrl::state() != thread_state::aborting) { // Wait for the start condition @@ -113,29 +148,7 @@ void progress_dialog_server::operator()() bool show_overlay_message = false; // Only show an overlay message after initial loading is done. std::shared_ptr dlg; - if (const auto renderer = rsx::get_current_renderer()) - { - // Some backends like OpenGL actually initialize a lot of driver objects in the "on_init" method. - // Wait for init to complete within reasonable time. Abort just in case we have hardware/driver issues. - renderer->is_initialized.wait(0, atomic_wait_timeout(5 * 1000000000ull)); - - auto manager = g_fxo->try_get(); - show_overlay_message = g_fxo->get().show_overlay_message_only; - - if (manager && !show_overlay_message) - { - MsgDialogType type{}; - type.se_mute_on = true; - type.se_normal = true; - type.bg_invisible = true; - type.disable_cancel = true; - type.progress_bar_count = 1; - - native_dlg = manager->create(true); - native_dlg->show(false, text0, type, msg_dialog_source::sys_progress, nullptr); - native_dlg->progress_bar_set_message(0, get_localized_string(localized_string_id::PROGRESS_DIALOG_PLEASE_WAIT)); - } - } + create_native_dialog(text0, &show_overlay_message); if (!show_overlay_message && !native_dlg && (dlg = Emu.GetCallbacks().get_msg_dialog())) { @@ -392,6 +405,7 @@ void progress_dialog_server::operator()() else if (native_dlg) { native_dlg->close(false, false); + native_dlg.reset(); } else if (dlg) { @@ -411,10 +425,25 @@ void progress_dialog_server::operator()() g_progr_ptotal.notify_all(); } - if (native_dlg && g_system_progress_stopping) + if (g_system_progress_stopping) { - native_dlg->set_text(get_localized_string(localized_string_id::PROGRESS_DIALOG_STOPPING_PLEASE_WAIT)); - native_dlg->refresh(); + const std::string text = get_localized_string( + g_system_progress_stopping == system_progress_stop_state::stop_state_continuous_savestate + ? localized_string_id::PROGRESS_DIALOG_SAVESTATE_PLEASE_WAIT + : localized_string_id::PROGRESS_DIALOG_STOPPING_PLEASE_WAIT + ); + if (native_dlg) + { + native_dlg->set_text(text); + } + else + { + create_native_dialog(text, nullptr); + } + if (native_dlg) + { + native_dlg->refresh(); + } } if (g_progr_ptotal.exchange(0)) diff --git a/rpcs3/Emu/system_progress.hpp b/rpcs3/Emu/system_progress.hpp index 13ae8d13eb..67e4e68df1 100644 --- a/rpcs3/Emu/system_progress.hpp +++ b/rpcs3/Emu/system_progress.hpp @@ -36,6 +36,13 @@ struct alignas(16) progress_dialog_string_t } }; +enum system_progress_stop_state : u32 +{ + stop_state_disabled = 0, + stop_state_stopping, + stop_state_continuous_savestate +}; + extern progress_dialog_string_t g_progr_text; extern atomic_t g_progr_ftotal; extern atomic_t g_progr_fdone; @@ -44,7 +51,7 @@ extern atomic_t g_progr_fknown_bits; extern atomic_t g_progr_ptotal; extern atomic_t g_progr_pdone; extern atomic_t g_system_progress_canceled; -extern atomic_t g_system_progress_stopping; +extern atomic_t g_system_progress_stopping; // Initialize progress dialog (can be recursive) class scoped_progress_dialog final diff --git a/rpcs3/emucore.vcxproj b/rpcs3/emucore.vcxproj index 0c54d02978..6834ed5530 100644 --- a/rpcs3/emucore.vcxproj +++ b/rpcs3/emucore.vcxproj @@ -66,6 +66,7 @@ true + diff --git a/rpcs3/emucore.vcxproj.filters b/rpcs3/emucore.vcxproj.filters index 90df56b77e..f36b57bb03 100644 --- a/rpcs3/emucore.vcxproj.filters +++ b/rpcs3/emucore.vcxproj.filters @@ -1327,6 +1327,9 @@ Emu\Audio + + Emu\Cell + diff --git a/rpcs3/headless_application.cpp b/rpcs3/headless_application.cpp index b12b6d87e2..e82bcc12dc 100644 --- a/rpcs3/headless_application.cpp +++ b/rpcs3/headless_application.cpp @@ -120,6 +120,7 @@ void headless_application::InitializeCallbacks() return nullptr; }; + callbacks.close_gs_frame = [](){}; callbacks.get_gs_frame = []() -> std::unique_ptr { if (g_cfg.video.renderer != video_renderer::null) diff --git a/rpcs3/main_application.cpp b/rpcs3/main_application.cpp index 0040bb235e..d86d6523b2 100644 --- a/rpcs3/main_application.cpp +++ b/rpcs3/main_application.cpp @@ -133,7 +133,7 @@ EmuCallbacks main_application::CreateCallbacks() basic_keyboard_handler* ret = g_fxo->init(Emu.DeserialManager()); ensure(ret); ret->moveToThread(get_thread()); - ret->SetTargetWindow(m_game_window); + ret->SetTargetWindow(reinterpret_cast(m_game_window)); break; } } @@ -170,7 +170,7 @@ EmuCallbacks main_application::CreateCallbacks() basic_mouse_handler* ret = g_fxo->init(Emu.DeserialManager()); ensure(ret); ret->moveToThread(get_thread()); - ret->SetTargetWindow(m_game_window); + ret->SetTargetWindow(reinterpret_cast(m_game_window)); break; } case mouse_handler::raw: diff --git a/rpcs3/main_application.h b/rpcs3/main_application.h index e378eb5c53..dd0806970d 100644 --- a/rpcs3/main_application.h +++ b/rpcs3/main_application.h @@ -1,9 +1,10 @@ #pragma once #include -#include +#include struct EmuCallbacks; +class gs_frame; class main_application { @@ -25,5 +26,5 @@ protected: EmuCallbacks CreateCallbacks(); std::string m_active_user; - QWindow* m_game_window = nullptr; // (Currently) only needed so that pad handlers have a valid target for event filtering. + gs_frame* m_game_window = nullptr; }; diff --git a/rpcs3/rpcs3qt/gl_gs_frame.cpp b/rpcs3/rpcs3qt/gl_gs_frame.cpp index 1e3f6f8fc4..94b6aa964f 100644 --- a/rpcs3/rpcs3qt/gl_gs_frame.cpp +++ b/rpcs3/rpcs3qt/gl_gs_frame.cpp @@ -28,6 +28,11 @@ gl_gs_frame::gl_gs_frame(QScreen* screen, const QRect& geometry, const QIcon& ap show(); } +void gl_gs_frame::reset() +{ + m_primary_context = nullptr; +} + draw_context_t gl_gs_frame::make_context() { auto context = new GLContext(); diff --git a/rpcs3/rpcs3qt/gl_gs_frame.h b/rpcs3/rpcs3qt/gl_gs_frame.h index bc6e9ad65e..d1129f8e3a 100644 --- a/rpcs3/rpcs3qt/gl_gs_frame.h +++ b/rpcs3/rpcs3qt/gl_gs_frame.h @@ -20,6 +20,7 @@ private: public: explicit gl_gs_frame(QScreen* screen, const QRect& geometry, const QIcon& appIcon, std::shared_ptr gui_settings, bool force_fullscreen); + void reset() override; draw_context_t make_context() override; void set_current(draw_context_t ctx) override; void delete_context(draw_context_t ctx) override; diff --git a/rpcs3/rpcs3qt/gs_frame.cpp b/rpcs3/rpcs3qt/gs_frame.cpp index 8af0b84757..5a9da7dad6 100644 --- a/rpcs3/rpcs3qt/gs_frame.cpp +++ b/rpcs3/rpcs3qt/gs_frame.cpp @@ -73,6 +73,7 @@ gs_frame::gs_frame(QScreen* screen, const QRect& geometry, const QIcon& appIcon, , m_initial_geometry(geometry) , m_gui_settings(std::move(gui_settings)) , m_start_games_fullscreen(force_fullscreen) + , m_renderer(g_cfg.video.renderer) { load_gui_settings(); @@ -328,6 +329,9 @@ void gs_frame::handle_shortcut(gui::shortcuts::shortcut shortcut_key, const QKey { Emu.Restart(); }; + + // Make sure we keep the game window opened + Emu.SetContinuousMode(true); } Emu.Kill(false, true); @@ -602,6 +606,11 @@ void gs_frame::close() gui_log.notice("Closing game window"); + if (m_ignore_stop_events) + { + return; + } + Emu.CallFromMainThread([this]() { // Hide window if necessary @@ -623,6 +632,10 @@ void gs_frame::close() }); } +void gs_frame::reset() +{ +} + bool gs_frame::shown() { return QWindow::isVisible(); @@ -1134,6 +1147,11 @@ bool gs_frame::event(QEvent* ev) gui_log.notice("Game window close event issued"); + if (m_ignore_stop_events) + { + return QWindow::event(ev); + } + if (Emu.IsStopped()) { // This should be unreachable, but never say never. Properly close the window anyway. diff --git a/rpcs3/rpcs3qt/gs_frame.h b/rpcs3/rpcs3qt/gs_frame.h index 97a777360c..8d6316bd88 100644 --- a/rpcs3/rpcs3qt/gs_frame.h +++ b/rpcs3/rpcs3qt/gs_frame.h @@ -15,6 +15,7 @@ #include class gui_settings; +enum class video_renderer; class gs_frame : public QWindow, public GSFrameBase { @@ -45,6 +46,7 @@ private: u32 m_hide_mouse_idletime = 2000; // ms bool m_flip_showed_frame = false; bool m_start_games_fullscreen = false; + bool m_ignore_stop_events = false; std::shared_ptr m_video_encoder{}; @@ -52,6 +54,10 @@ public: explicit gs_frame(QScreen* screen, const QRect& geometry, const QIcon& appIcon, std::shared_ptr gui_settings, bool force_fullscreen); ~gs_frame(); + video_renderer renderer() const { return m_renderer; }; + + void ignore_stop_events() { m_ignore_stop_events = true; } + draw_context_t make_context() override; void set_current(draw_context_t context) override; void delete_context(draw_context_t context) override; @@ -76,10 +82,13 @@ public: void take_screenshot(std::vector data, u32 sshot_width, u32 sshot_height, bool is_bgra) override; protected: + video_renderer m_renderer; + void paintEvent(QPaintEvent *event) override; void showEvent(QShowEvent *event) override; void close() override; + void reset() override; bool shown() override; void hide() override; diff --git a/rpcs3/rpcs3qt/gui_application.cpp b/rpcs3/rpcs3qt/gui_application.cpp index 0a3ff110ce..b5c2041527 100644 --- a/rpcs3/rpcs3qt/gui_application.cpp +++ b/rpcs3/rpcs3qt/gui_application.cpp @@ -348,6 +348,34 @@ void gui_application::InitializeConnects() std::unique_ptr gui_application::get_gs_frame() { + // Load AppIcon + const QIcon app_icon = m_main_window ? m_main_window->GetAppIcon() : gui::utils::get_app_icon_from_path(Emu.GetBoot(), Emu.GetTitleID()); + + if (m_game_window) + { + // Check if the continuous mode is enabled. We reset the mode after each use in order to ensure that it is only used when explicitly needed. + const bool continuous_mode_enabled = Emu.ContinuousModeEnabled(true); + + // Make sure we run the same config + const bool is_same_renderer = m_game_window->renderer() == g_cfg.video.renderer; + + if (is_same_renderer && (Emu.IsChildProcess() || continuous_mode_enabled)) + { + gui_log.notice("gui_application: Re-using old game window (IsChildProcess=%d, ContinuousModeEnabled=%d)", Emu.IsChildProcess(), continuous_mode_enabled); + + if (!app_icon.isNull()) + { + m_game_window->setIcon(app_icon); + } + return std::unique_ptr(m_game_window); + } + + // Clean-up old game window. This should only happen if the renderer changed or there was an unexpected error during boot. + Emu.GetCallbacks().close_gs_frame(); + } + + gui_log.notice("gui_application: Creating new game window"); + extern const std::unordered_map, value_hash> g_video_out_resolution_map; auto [w, h] = ::at32(g_video_out_resolution_map, g_cfg.video.resolution); @@ -424,9 +452,6 @@ std::unique_ptr gui_application::get_gs_frame() frame_geometry.setSize(QSize(w, h)); } - // Load AppIcon - const QIcon app_icon = m_main_window ? m_main_window->GetAppIcon() : gui::utils::get_app_icon_from_path(Emu.GetBoot(), Emu.GetTitleID()); - gs_frame* frame = nullptr; switch (g_cfg.video.renderer.get()) @@ -446,6 +471,12 @@ std::unique_ptr gui_application::get_gs_frame() m_game_window = frame; + connect(m_game_window, &gs_frame::destroyed, this, [this]() + { + gui_log.notice("gui_application: Deleting old game window"); + m_game_window = nullptr; + }); + return std::unique_ptr(frame); } @@ -539,6 +570,16 @@ void gui_application::InitializeCallbacks() return nullptr; }; + callbacks.close_gs_frame = [this]() + { + if (m_game_window) + { + gui_log.warning("gui_application: Closing old game window"); + m_game_window->ignore_stop_events(); + delete m_game_window; + m_game_window = nullptr; + } + }; callbacks.get_gs_frame = [this]() -> std::unique_ptr { return get_gs_frame(); }; callbacks.get_msg_dialog = [this]() -> std::shared_ptr { return m_show_gui ? std::make_shared() : nullptr; }; callbacks.get_osk_dialog = [this]() -> std::shared_ptr { return m_show_gui ? std::make_shared() : nullptr; }; @@ -582,10 +623,10 @@ void gui_application::InitializeCallbacks() { switch (type) { - case 0: static_cast(m_game_window)->progress_reset(value); break; - case 1: static_cast(m_game_window)->progress_increment(value); break; - case 2: static_cast(m_game_window)->progress_set_limit(value); break; - case 3: static_cast(m_game_window)->progress_set_value(value); break; + case 0: m_game_window->progress_reset(value); break; + case 1: m_game_window->progress_increment(value); break; + case 2: m_game_window->progress_set_limit(value); break; + case 3: m_game_window->progress_set_value(value); break; default: gui_log.fatal("Unknown type in handle_taskbar_progress(type=%d, value=%d)", type, value); break; } } @@ -770,7 +811,7 @@ void gui_application::InitializeCallbacks() verbose_message += ". "; } - verbose_message += "If Stuck, Report To Developers"; + verbose_message += tr("If Stuck, Report To Developers").toStdString(); } else { @@ -1045,7 +1086,7 @@ void gui_application::OnShortcutChange() { if (m_game_window) { - static_cast(m_game_window)->update_shortcuts(); + m_game_window->update_shortcuts(); } } @@ -1074,7 +1115,7 @@ void gui_application::OnAppStateChanged(Qt::ApplicationState state) } const auto emu_state = Emu.GetStatus(); - const bool is_active = state == Qt::ApplicationActive; + const bool is_active = state & Qt::ApplicationActive; if (emu_state != system_state::paused && emu_state != system_state::running) { diff --git a/rpcs3/rpcs3qt/localized_emu.h b/rpcs3/rpcs3qt/localized_emu.h index 8ea21f6d03..7e8ae3f803 100644 --- a/rpcs3/rpcs3qt/localized_emu.h +++ b/rpcs3/rpcs3qt/localized_emu.h @@ -314,6 +314,7 @@ private: case localized_string_id::PROGRESS_DIALOG_OF: return tr("of"); case localized_string_id::PROGRESS_DIALOG_PLEASE_WAIT: return tr("Please wait"); case localized_string_id::PROGRESS_DIALOG_STOPPING_PLEASE_WAIT: return tr("Stopping. Please wait..."); + case localized_string_id::PROGRESS_DIALOG_SAVESTATE_PLEASE_WAIT: return tr("Creating savestate. Please wait..."); case localized_string_id::PROGRESS_DIALOG_SCANNING_PPU_EXECUTABLE: return tr("Scanning PPU Executable..."); case localized_string_id::PROGRESS_DIALOG_ANALYZING_PPU_EXECUTABLE: return tr("Analyzing PPU Executable..."); case localized_string_id::PROGRESS_DIALOG_SCANNING_PPU_MODULES: return tr("Scanning PPU Modules..."); diff --git a/rpcs3/rpcs3qt/main_window.cpp b/rpcs3/rpcs3qt/main_window.cpp index f499cd6474..46e056e43a 100644 --- a/rpcs3/rpcs3qt/main_window.cpp +++ b/rpcs3/rpcs3qt/main_window.cpp @@ -2590,6 +2590,9 @@ void main_window::CreateConnects() { Emu.Restart(); }; + + // Make sure we keep the game window opened + Emu.SetContinuousMode(true); } Emu.Kill(false, true);