This commit is contained in:
digant 2025-01-06 17:28:14 +01:00
parent dc2e45bc89
commit 8e070e8b1c
27 changed files with 263 additions and 146 deletions

View file

@ -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

View file

@ -498,6 +498,9 @@ void lv2_exitspawn(ppu_thread& ppu, std::vector<std::string>& argv, std::vector<
};
signal_system_cache_can_stay();
// Make sure we keep the game window opened
Emu.SetContinuousMode(true);
Emu.Kill(false);
});

View file

@ -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()

View file

@ -159,6 +159,7 @@ public:
GLGSRender(utils::serial* ar) noexcept;
GLGSRender() noexcept : GLGSRender(nullptr) {}
virtual ~GLGSRender();
private:

View file

@ -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;

View file

@ -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;
}

View file

@ -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;

View file

@ -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;

View file

@ -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);
});

View file

@ -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<void, 0x20'00000, 128> 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<GSRender*>(g_fxo->try_get<rsx::thread>()))
{
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<bool()> 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<std::pair<shared_ptr<named_thread<spu_thread>>, 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<named_thread<progress_dialog_server>>() && (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<named_thread<progress_dialog_server>>(); 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<std::string, usz> 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<ppu_thread>())
{
if (auto current = ppu->current_function)
{
func = current;
}
}
else if (auto spu = get_current_cpu_thread<spu_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<main_ppu_module<lv2_obj>>();

View file

@ -86,6 +86,7 @@ struct EmuCallbacks
std::function<void(std::string_view title_id)> init_pad_handler;
std::function<void()> update_emu_settings;
std::function<void()> save_emu_settings;
std::function<void()> close_gs_frame;
std::function<std::unique_ptr<class GSFrameBase>()> get_gs_frame;
std::function<std::shared_ptr<class camera_handler_base>()> get_camera_handler;
std::function<std::shared_ptr<class music_handler_base>()> 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;

View file

@ -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,

View file

@ -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)
{

View file

@ -26,7 +26,7 @@ atomic_t<u32> g_progr_pdone{0};
atomic_t<bool> g_system_progress_canceled{false};
// For showing feedback while stopping emulation
atomic_t<bool> g_system_progress_stopping{false};
atomic_t<system_progress_stop_state> 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<rsx::overlays::progress_dialog> 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<rsx::overlays::display_manager>();
if (show_overlay_message)
{
*show_overlay_message = g_fxo->get<progress_dialog_workaround>().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<rsx::overlays::progress_dialog>(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<MsgDialogBase> 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<rsx::overlays::display_manager>();
show_overlay_message = g_fxo->get<progress_dialog_workaround>().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<rsx::overlays::progress_dialog>(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))

View file

@ -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<u32> g_progr_ftotal;
extern atomic_t<u32> g_progr_fdone;
@ -44,7 +51,7 @@ extern atomic_t<u64> g_progr_fknown_bits;
extern atomic_t<u32> g_progr_ptotal;
extern atomic_t<u32> g_progr_pdone;
extern atomic_t<bool> g_system_progress_canceled;
extern atomic_t<bool> g_system_progress_stopping;
extern atomic_t<system_progress_stop_state> g_system_progress_stopping;
// Initialize progress dialog (can be recursive)
class scoped_progress_dialog final

View file

@ -66,6 +66,7 @@
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="Emu\cache_utils.cpp" />
<ClCompile Include="Emu\Cell\ErrorCodes.cpp" />
<ClCompile Include="Emu\Cell\lv2\sys_game.cpp" />
<ClCompile Include="Emu\Cell\Modules\cellMusicSelectionContext.cpp" />
<ClCompile Include="Emu\Cell\Modules\libfs_utility_init.cpp" />

View file

@ -1327,6 +1327,9 @@
<ClCompile Include="Emu\Audio\audio_utils.cpp">
<Filter>Emu\Audio</Filter>
</ClCompile>
<ClCompile Include="Emu\Cell\ErrorCodes.cpp">
<Filter>Emu\Cell</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="Crypto\aes.h">

View file

@ -120,6 +120,7 @@ void headless_application::InitializeCallbacks()
return nullptr;
};
callbacks.close_gs_frame = [](){};
callbacks.get_gs_frame = []() -> std::unique_ptr<GSFrameBase>
{
if (g_cfg.video.renderer != video_renderer::null)

View file

@ -133,7 +133,7 @@ EmuCallbacks main_application::CreateCallbacks()
basic_keyboard_handler* ret = g_fxo->init<KeyboardHandlerBase, basic_keyboard_handler>(Emu.DeserialManager());
ensure(ret);
ret->moveToThread(get_thread());
ret->SetTargetWindow(m_game_window);
ret->SetTargetWindow(reinterpret_cast<QWindow*>(m_game_window));
break;
}
}
@ -170,7 +170,7 @@ EmuCallbacks main_application::CreateCallbacks()
basic_mouse_handler* ret = g_fxo->init<MouseHandlerBase, basic_mouse_handler>(Emu.DeserialManager());
ensure(ret);
ret->moveToThread(get_thread());
ret->SetTargetWindow(m_game_window);
ret->SetTargetWindow(reinterpret_cast<QWindow*>(m_game_window));
break;
}
case mouse_handler::raw:

View file

@ -1,9 +1,10 @@
#pragma once
#include <string>
#include <QWindow>
#include <QThread>
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;
};

View file

@ -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();

View file

@ -20,6 +20,7 @@ private:
public:
explicit gl_gs_frame(QScreen* screen, const QRect& geometry, const QIcon& appIcon, std::shared_ptr<gui_settings> 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;

View file

@ -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.

View file

@ -15,6 +15,7 @@
#include <vector>
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<utils::video_encoder> m_video_encoder{};
@ -52,6 +54,10 @@ public:
explicit gs_frame(QScreen* screen, const QRect& geometry, const QIcon& appIcon, std::shared_ptr<gui_settings> 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<u8> 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;

View file

@ -348,6 +348,34 @@ void gui_application::InitializeConnects()
std::unique_ptr<gs_frame> 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<gs_frame>(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<video_resolution, std::pair<int, int>, value_hash<video_resolution>> 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<gs_frame> 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<gs_frame> 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<gs_frame>(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<GSFrameBase> { return get_gs_frame(); };
callbacks.get_msg_dialog = [this]() -> std::shared_ptr<MsgDialogBase> { return m_show_gui ? std::make_shared<msg_dialog_frame>() : nullptr; };
callbacks.get_osk_dialog = [this]() -> std::shared_ptr<OskDialogBase> { return m_show_gui ? std::make_shared<osk_dialog_frame>() : nullptr; };
@ -582,10 +623,10 @@ void gui_application::InitializeCallbacks()
{
switch (type)
{
case 0: static_cast<gs_frame*>(m_game_window)->progress_reset(value); break;
case 1: static_cast<gs_frame*>(m_game_window)->progress_increment(value); break;
case 2: static_cast<gs_frame*>(m_game_window)->progress_set_limit(value); break;
case 3: static_cast<gs_frame*>(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<gs_frame*>(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)
{

View file

@ -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...");

View file

@ -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);