mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-02-06 18:40:36 +00:00
Savestates/rsx/IO: Resume emulation on long START press, enable "Start Paused" by defaut (#12881)
* Savestates: Enable "Start Paused" by default * Emu/rsx/IO: Resume emulation on long START press * rsx: fix missing graphics with savestates' "Start Paused" setting * rsx/overlays: Add simple reference counting for messages to hide them manually * Move some code in Emulator::Pause() so thread pausing is the first thing done by this function
This commit is contained in:
parent
c8620070b9
commit
c214f45e14
@ -1028,7 +1028,7 @@ void GLGSRender::do_local_task(rsx::FIFO_state state)
|
||||
|
||||
if (m_overlay_manager)
|
||||
{
|
||||
if (!in_begin_end && async_flip_requested & flip_request::native_ui)
|
||||
if (!in_begin_end && async_flip_requested & flip_request::native_ui && !is_stopped())
|
||||
{
|
||||
rsx::display_flip_info_t info{};
|
||||
info.buffer = current_display_buffer;
|
||||
|
@ -7,9 +7,10 @@ namespace rsx
|
||||
namespace overlays
|
||||
{
|
||||
template <typename T>
|
||||
message_item::message_item(T msg_id)
|
||||
message_item::message_item(T msg_id, u64 expiration, std::shared_ptr<atomic_t<u32>> refs)
|
||||
{
|
||||
m_expiration_time = get_system_time() + 5'000'000;
|
||||
m_expiration_time = expiration == umax ? expiration : get_system_time() + expiration;
|
||||
m_refs = std::move(refs);
|
||||
|
||||
m_text.set_font("Arial", 16);
|
||||
m_text.set_text(msg_id);
|
||||
@ -21,12 +22,13 @@ namespace rsx
|
||||
m_fade_animation.duration = 2.f;
|
||||
m_fade_animation.active = true;
|
||||
}
|
||||
template message_item::message_item(std::string msg_id);
|
||||
template message_item::message_item(localized_string_id msg_id);
|
||||
template message_item::message_item(std::string msg_id, u64, std::shared_ptr<atomic_t<u32>>);
|
||||
template message_item::message_item(localized_string_id msg_id, u64, std::shared_ptr<atomic_t<u32>>);
|
||||
|
||||
u64 message_item::get_expiration() const
|
||||
{
|
||||
return m_expiration_time;
|
||||
// If reference counting is enabled and reached 0 consider it expired
|
||||
return m_refs && *m_refs == 0 ? 0 : m_expiration_time;
|
||||
}
|
||||
|
||||
compiled_resource message_item::get_compiled()
|
||||
|
@ -11,7 +11,7 @@ namespace rsx
|
||||
{
|
||||
public:
|
||||
template <typename T>
|
||||
message_item(T msg_id);
|
||||
message_item(T msg_id, u64 expiration, std::shared_ptr<atomic_t<u32>> refs);
|
||||
void update(usz index, u64 time);
|
||||
|
||||
u64 get_expiration() const;
|
||||
@ -22,6 +22,7 @@ namespace rsx
|
||||
animation_color_interpolate m_fade_animation;
|
||||
|
||||
u64 m_expiration_time = 0;
|
||||
std::shared_ptr<atomic_t<u32>> m_refs;
|
||||
bool m_processed = false;
|
||||
usz m_cur_pos = umax;
|
||||
};
|
||||
@ -33,7 +34,7 @@ namespace rsx
|
||||
compiled_resource get_compiled() override;
|
||||
|
||||
template <typename T>
|
||||
void queue_message(T msg_id)
|
||||
void queue_message(T msg_id, u64 expiration, std::shared_ptr<atomic_t<u32>> refs)
|
||||
{
|
||||
std::lock_guard lock(m_mutex_queue);
|
||||
|
||||
@ -41,12 +42,12 @@ namespace rsx
|
||||
{
|
||||
for (auto id : msg_id)
|
||||
{
|
||||
m_queue.emplace_back(id);
|
||||
m_queue.emplace_back(id, expiration, refs);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_queue.emplace_back(msg_id);
|
||||
m_queue.emplace_back(msg_id, expiration, std::move(refs));
|
||||
}
|
||||
|
||||
visible = true;
|
||||
@ -58,7 +59,7 @@ namespace rsx
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
void queue_message(T msg_id)
|
||||
void queue_message(T msg_id, u64 expiration = 5'000'000, std::shared_ptr<atomic_t<u32>> refs = {})
|
||||
{
|
||||
if (auto manager = g_fxo->try_get<rsx::overlays::display_manager>())
|
||||
{
|
||||
@ -68,7 +69,7 @@ namespace rsx
|
||||
msg_overlay = std::make_shared<rsx::overlays::message>();
|
||||
msg_overlay = manager->add(msg_overlay);
|
||||
}
|
||||
msg_overlay->queue_message(msg_id);
|
||||
msg_overlay->queue_message(msg_id, expiration, std::move(refs));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include "Emu/Cell/lv2/sys_time.h"
|
||||
#include "Emu/Cell/Modules/cellGcmSys.h"
|
||||
#include "Overlays/overlay_perf_metrics.h"
|
||||
#include "Overlays/overlay_message.h"
|
||||
#include "Program/GLSLCommon.h"
|
||||
#include "Utilities/date_time.h"
|
||||
#include "Utilities/StrUtil.h"
|
||||
@ -256,6 +257,14 @@ namespace rsx
|
||||
}
|
||||
}
|
||||
|
||||
extern void set_native_ui_flip()
|
||||
{
|
||||
if (auto rsxthr = rsx::get_current_renderer())
|
||||
{
|
||||
rsxthr->async_flip_requested |= rsx::thread::flip_request::native_ui;
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<u32, u32> interleaved_range_info::calculate_required_range(u32 first, u32 count) const
|
||||
{
|
||||
if (single_vertex)
|
||||
@ -542,7 +551,8 @@ namespace rsx
|
||||
|
||||
if (g_cfg.savestate.start_paused)
|
||||
{
|
||||
m_pause_on_first_flip = true;
|
||||
// Allow to render a whole frame within this emulation session so there won't be missing graphics
|
||||
m_pause_after_x_flips = 2;
|
||||
}
|
||||
}
|
||||
|
||||
@ -701,15 +711,15 @@ namespace rsx
|
||||
wait_pause();
|
||||
}
|
||||
|
||||
on_semaphore_acquire_wait();
|
||||
|
||||
if ((state & (cpu_flag::dbg_global_pause + cpu_flag::exit)) == cpu_flag::dbg_global_pause)
|
||||
{
|
||||
// Wait 16ms during emulation pause. This reduces cpu load while still giving us the chance to render overlays.
|
||||
do_local_task(rsx::FIFO_state::paused);
|
||||
thread_ctrl::wait_on(state, old, 16000);
|
||||
}
|
||||
else
|
||||
{
|
||||
on_semaphore_acquire_wait();
|
||||
std::this_thread::yield();
|
||||
}
|
||||
}
|
||||
@ -2670,10 +2680,9 @@ namespace rsx
|
||||
{
|
||||
performance_counters.sampled_frames++;
|
||||
|
||||
if (m_pause_on_first_flip)
|
||||
if (m_pause_after_x_flips && m_pause_after_x_flips-- == 1)
|
||||
{
|
||||
Emu.Pause();
|
||||
m_pause_on_first_flip = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -181,7 +181,8 @@ namespace rsx
|
||||
empty = 1, // PUT == GET
|
||||
spinning = 2, // Puller continuously jumps to self addr (synchronization technique)
|
||||
nop = 3, // Puller is processing a NOP command
|
||||
lock_wait = 4 // Puller is processing a lock acquire
|
||||
lock_wait = 4,// Puller is processing a lock acquire
|
||||
paused = 5, // Puller is paused externallly
|
||||
};
|
||||
|
||||
enum FIFO_hint : u8
|
||||
@ -540,8 +541,8 @@ namespace rsx
|
||||
rsx::profiling_timer m_profiler;
|
||||
frame_statistics_t m_frame_stats;
|
||||
|
||||
// Savestates vrelated
|
||||
bool m_pause_on_first_flip = false;
|
||||
// Savestates related
|
||||
u32 m_pause_after_x_flips = 0;
|
||||
|
||||
public:
|
||||
RsxDmaControl* ctrl = nullptr;
|
||||
|
@ -1710,7 +1710,7 @@ void VKGSRender::do_local_task(rsx::FIFO_state state)
|
||||
|
||||
if (m_overlay_manager)
|
||||
{
|
||||
if (!in_begin_end && async_flip_requested & flip_request::native_ui)
|
||||
if (!in_begin_end && async_flip_requested & flip_request::native_ui && !is_stopped())
|
||||
{
|
||||
flush_command_queue(true);
|
||||
rsx::display_flip_info_t info{};
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include "Emu/title.h"
|
||||
#include "Emu/IdManager.h"
|
||||
#include "Emu/RSX/Capture/rsx_replay.h"
|
||||
#include "Emu/RSX/Overlays/overlay_message.h"
|
||||
|
||||
#include "Loader/PSF.h"
|
||||
#include "Loader/TAR.h"
|
||||
@ -101,6 +102,11 @@ namespace atomic_wait
|
||||
extern void parse_hashtable(bool(*cb)(u64 id, u32 refs, u64 ptr, u32 max_coll));
|
||||
}
|
||||
|
||||
namespace rsx
|
||||
{
|
||||
void set_native_ui_flip();
|
||||
}
|
||||
|
||||
template<>
|
||||
void fmt_class_string<game_boot_result>::format(std::string& out, u64 arg)
|
||||
{
|
||||
@ -2050,8 +2056,53 @@ bool Emulator::Pause(bool freeze_emulation)
|
||||
// Signal profilers to print results (if enabled)
|
||||
cpu_thread::flush_profilers();
|
||||
|
||||
auto on_select = [](u32, cpu_thread& cpu)
|
||||
{
|
||||
cpu.state += cpu_flag::dbg_global_pause;
|
||||
};
|
||||
|
||||
idm::select<named_thread<ppu_thread>>(on_select);
|
||||
idm::select<named_thread<spu_thread>>(on_select);
|
||||
|
||||
if (auto rsx = g_fxo->try_get<rsx::thread>())
|
||||
{
|
||||
rsx->state += cpu_flag::dbg_global_pause;
|
||||
}
|
||||
|
||||
GetCallbacks().on_pause();
|
||||
|
||||
BlockingCallFromMainThread([this]()
|
||||
{
|
||||
if (IsStopped())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto msg_ref = std::make_shared<atomic_t<u32>>(1);
|
||||
|
||||
// No timeout
|
||||
rsx::overlays::queue_message(localized_string_id::EMULATION_PAUSED_RESUME_WITH_START, -1, msg_ref);
|
||||
m_pause_msgs_refs.emplace_back(msg_ref);
|
||||
|
||||
auto refresh_l = [this, msg_ref]()
|
||||
{
|
||||
while (*msg_ref && IsPaused())
|
||||
{
|
||||
// Refresh Native UI
|
||||
rsx::set_native_ui_flip();
|
||||
thread_ctrl::wait_for(33'000);
|
||||
}
|
||||
};
|
||||
|
||||
struct thread_t
|
||||
{
|
||||
std::unique_ptr<named_thread<decltype(refresh_l)>> m_thread;
|
||||
};
|
||||
|
||||
g_fxo->get<thread_t>().m_thread.reset();
|
||||
g_fxo->get<thread_t>().m_thread = std::make_unique<named_thread<decltype(refresh_l)>>("Pause Message Thread"sv, std::move(refresh_l));
|
||||
});
|
||||
|
||||
static atomic_t<u32> pause_mark = 0;
|
||||
|
||||
if (freeze_emulation)
|
||||
@ -2069,19 +2120,6 @@ bool Emulator::Pause(bool freeze_emulation)
|
||||
sys_log.error("Emulator::Pause() error: concurrent access");
|
||||
}
|
||||
|
||||
auto on_select = [](u32, cpu_thread& cpu)
|
||||
{
|
||||
cpu.state += cpu_flag::dbg_global_pause;
|
||||
};
|
||||
|
||||
idm::select<named_thread<ppu_thread>>(on_select);
|
||||
idm::select<named_thread<spu_thread>>(on_select);
|
||||
|
||||
if (auto rsx = g_fxo->try_get<rsx::thread>())
|
||||
{
|
||||
rsx->state += cpu_flag::dbg_global_pause;
|
||||
}
|
||||
|
||||
// Always Enable display sleep, not only if it was prevented.
|
||||
enable_display_sleep();
|
||||
|
||||
@ -2167,6 +2205,17 @@ void Emulator::Resume()
|
||||
|
||||
sys_log.success("Emulation has been resumed!");
|
||||
|
||||
BlockingCallFromMainThread([this]()
|
||||
{
|
||||
for (auto& ref : m_pause_msgs_refs)
|
||||
{
|
||||
// Delete the message queued on pause
|
||||
*ref = 0;
|
||||
}
|
||||
|
||||
m_pause_msgs_refs.clear();
|
||||
});
|
||||
|
||||
if (g_cfg.misc.prevent_display_sleep)
|
||||
{
|
||||
disable_display_sleep();
|
||||
|
@ -144,6 +144,8 @@ class Emulator final
|
||||
|
||||
bool m_state_inspection_savestate = false;
|
||||
|
||||
std::vector<std::shared_ptr<atomic_t<u32>>> m_pause_msgs_refs;
|
||||
|
||||
std::vector<std::function<void()>> deferred_deserialization;
|
||||
|
||||
void ExecDeserializationRemnants()
|
||||
|
@ -146,4 +146,7 @@ enum class localized_string_id
|
||||
RPCN_ERROR_INVALID_PROTOCOL_VERSION,
|
||||
RPCN_ERROR_UNKNOWN,
|
||||
RPCN_SUCCESS_LOGGED_ON,
|
||||
|
||||
EMULATION_PAUSED_RESUME_WITH_START,
|
||||
EMULATION_RESUMING,
|
||||
};
|
||||
|
@ -315,7 +315,7 @@ struct cfg_root : cfg::node
|
||||
{
|
||||
node_savestate(cfg::node* _this) : cfg::node(_this, "Savestate") {}
|
||||
|
||||
cfg::_bool start_paused{ this, "Start Paused" }; // Pause on first frame
|
||||
cfg::_bool start_paused{ this, "Start Paused 2", true }; // Pause on first frame
|
||||
cfg::_bool suspend_emu{ this, "Suspend Emulation Savestate Mode", false }; // Close emulation when saving, delete save after loading
|
||||
cfg::_bool state_inspection_mode{ this, "Inspection Mode Savestates" }; // Save memory stored in executable files, thus allowing to view state without any files (for debugging)
|
||||
cfg::_bool save_disc_game_data{ this, "Save Disc Game Data", false };
|
||||
|
@ -16,10 +16,12 @@
|
||||
#include "Emu/Io/pad_config.h"
|
||||
#include "Emu/System.h"
|
||||
#include "Emu/system_config.h"
|
||||
#include "Emu/RSX/Overlays/overlay_message.h"
|
||||
#include "Utilities/Thread.h"
|
||||
#include "util/atomic.hpp"
|
||||
|
||||
LOG_CHANNEL(input_log, "Input");
|
||||
LOG_CHANNEL(sys_log, "SYS");
|
||||
|
||||
extern bool is_input_allowed();
|
||||
|
||||
@ -33,6 +35,11 @@ namespace pad
|
||||
atomic_t<bool> g_enabled{true};
|
||||
}
|
||||
|
||||
namespace rsx
|
||||
{
|
||||
void set_native_ui_flip();
|
||||
}
|
||||
|
||||
struct pad_setting
|
||||
{
|
||||
u32 port_status = 0;
|
||||
@ -272,15 +279,22 @@ void pad_thread::operator()()
|
||||
{
|
||||
while (thread_ctrl::state() != thread_state::aborting)
|
||||
{
|
||||
if (!pad::g_enabled || Emu.IsPaused() || !is_input_allowed())
|
||||
if (!pad::g_enabled || !is_input_allowed())
|
||||
{
|
||||
thread_ctrl::wait_for(10'000);
|
||||
thread_ctrl::wait_for(30'000);
|
||||
continue;
|
||||
}
|
||||
|
||||
handler->process();
|
||||
|
||||
thread_ctrl::wait_for(g_cfg.io.pad_sleep);
|
||||
u64 pad_sleep = g_cfg.io.pad_sleep;
|
||||
|
||||
if (Emu.IsPaused())
|
||||
{
|
||||
pad_sleep = std::max<u64>(pad_sleep, 30'000);
|
||||
}
|
||||
|
||||
thread_ctrl::wait_for(pad_sleep);
|
||||
}
|
||||
}));
|
||||
}
|
||||
@ -290,9 +304,11 @@ void pad_thread::operator()()
|
||||
|
||||
while (thread_ctrl::state() != thread_state::aborting)
|
||||
{
|
||||
if (!pad::g_enabled || Emu.IsPaused() || !is_input_allowed())
|
||||
if (!pad::g_enabled || !is_input_allowed())
|
||||
{
|
||||
thread_ctrl::wait_for(10000);
|
||||
m_resume_emulation_flag = false;
|
||||
m_mask_start_press_to_unpause = 0;
|
||||
thread_ctrl::wait_for(30'000);
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -353,7 +369,7 @@ void pad_thread::operator()()
|
||||
if (!(pad->m_port_status & CELL_PAD_STATUS_CONNECTED))
|
||||
continue;
|
||||
|
||||
for (auto& button : pad->m_buttons)
|
||||
for (const auto& button : pad->m_buttons)
|
||||
{
|
||||
if (button.m_pressed && (
|
||||
button.m_outKeyCode == CELL_PAD_CTRL_CROSS ||
|
||||
@ -375,7 +391,81 @@ void pad_thread::operator()()
|
||||
}
|
||||
}
|
||||
|
||||
thread_ctrl::wait_for(g_cfg.io.pad_sleep);
|
||||
if (m_resume_emulation_flag)
|
||||
{
|
||||
m_resume_emulation_flag = false;
|
||||
|
||||
Emu.BlockingCallFromMainThread([]()
|
||||
{
|
||||
Emu.Resume();
|
||||
});
|
||||
}
|
||||
|
||||
u64 pad_sleep = g_cfg.io.pad_sleep;
|
||||
|
||||
if (Emu.IsPaused())
|
||||
{
|
||||
pad_sleep = std::max<u64>(pad_sleep, 30'000);
|
||||
|
||||
u64 timestamp = get_system_time();
|
||||
u32 pressed_mask = 0;
|
||||
|
||||
for (usz i = 0; i < m_pads.size(); i++)
|
||||
{
|
||||
const auto& pad = m_pads[i];
|
||||
|
||||
if (!(pad->m_port_status & CELL_PAD_STATUS_CONNECTED))
|
||||
continue;
|
||||
|
||||
for (const auto& button : pad->m_buttons)
|
||||
{
|
||||
if (button.m_offset == CELL_PAD_BTN_OFFSET_DIGITAL1 && button.m_outKeyCode == CELL_PAD_CTRL_START && button.m_pressed)
|
||||
{
|
||||
pressed_mask |= 1u << i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_mask_start_press_to_unpause &= pressed_mask;
|
||||
|
||||
if (!pressed_mask || timestamp - m_track_start_press_begin_timestamp >= 1'000'000)
|
||||
{
|
||||
m_track_start_press_begin_timestamp = timestamp;
|
||||
|
||||
if (std::exchange(m_mask_start_press_to_unpause, u32{umax}))
|
||||
{
|
||||
m_mask_start_press_to_unpause = 0;
|
||||
m_track_start_press_begin_timestamp = 0;
|
||||
|
||||
sys_log.success("Unpausing emulation using the START button in a few seconds...");
|
||||
rsx::overlays::queue_message(localized_string_id::EMULATION_RESUMING, 2'000'000);
|
||||
|
||||
m_resume_emulation_flag = true;
|
||||
|
||||
for (u32 i = 0; i < 40; i++)
|
||||
{
|
||||
if (!Emu.IsPaused())
|
||||
{
|
||||
// Abort if emulation has been resumed by other means
|
||||
m_resume_emulation_flag = false;
|
||||
break;
|
||||
}
|
||||
|
||||
thread_ctrl::wait_for(50'000);
|
||||
rsx::set_native_ui_flip();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Reset unpause control if caught a state of unpaused emulation
|
||||
m_mask_start_press_to_unpause = 0;
|
||||
m_track_start_press_begin_timestamp = 0;
|
||||
}
|
||||
|
||||
thread_ctrl::wait_for(pad_sleep);
|
||||
}
|
||||
|
||||
stop_threads();
|
||||
|
@ -52,6 +52,11 @@ protected:
|
||||
std::array<std::shared_ptr<Pad>, CELL_PAD_MAX_PORT_NUM> m_pads;
|
||||
|
||||
u32 num_ldd_pad = 0;
|
||||
|
||||
private:
|
||||
u32 m_mask_start_press_to_unpause = 0;
|
||||
u64 m_track_start_press_begin_timestamp = 0;
|
||||
bool m_resume_emulation_flag = false;
|
||||
};
|
||||
|
||||
namespace pad
|
||||
|
@ -167,6 +167,8 @@ private:
|
||||
case localized_string_id::RPCN_ERROR_INVALID_PROTOCOL_VERSION: return tr("RPCN Misc Error: Protocol Version Error (outdated RPCS3?)");
|
||||
case localized_string_id::RPCN_ERROR_UNKNOWN: return tr("RPCN: Unknown Error");
|
||||
case localized_string_id::RPCN_SUCCESS_LOGGED_ON: return tr("Successfully logged on RPCN!");
|
||||
case localized_string_id::EMULATION_PAUSED_RESUME_WITH_START: return tr("Press and hold the START button to resume");
|
||||
case localized_string_id::EMULATION_RESUMING: return tr("Resuming...!");
|
||||
case localized_string_id::INVALID: return tr("Invalid");
|
||||
default: return tr("Unknown");
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user