mirror of https://github.com/RPCS3/rpcs3.git
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 (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{};
|
rsx::display_flip_info_t info{};
|
||||||
info.buffer = current_display_buffer;
|
info.buffer = current_display_buffer;
|
||||||
|
|
|
@ -7,9 +7,10 @@ namespace rsx
|
||||||
namespace overlays
|
namespace overlays
|
||||||
{
|
{
|
||||||
template <typename T>
|
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_font("Arial", 16);
|
||||||
m_text.set_text(msg_id);
|
m_text.set_text(msg_id);
|
||||||
|
@ -21,12 +22,13 @@ namespace rsx
|
||||||
m_fade_animation.duration = 2.f;
|
m_fade_animation.duration = 2.f;
|
||||||
m_fade_animation.active = true;
|
m_fade_animation.active = true;
|
||||||
}
|
}
|
||||||
template message_item::message_item(std::string 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);
|
template message_item::message_item(localized_string_id msg_id, u64, std::shared_ptr<atomic_t<u32>>);
|
||||||
|
|
||||||
u64 message_item::get_expiration() const
|
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()
|
compiled_resource message_item::get_compiled()
|
||||||
|
|
|
@ -11,7 +11,7 @@ namespace rsx
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
template <typename T>
|
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);
|
void update(usz index, u64 time);
|
||||||
|
|
||||||
u64 get_expiration() const;
|
u64 get_expiration() const;
|
||||||
|
@ -22,6 +22,7 @@ namespace rsx
|
||||||
animation_color_interpolate m_fade_animation;
|
animation_color_interpolate m_fade_animation;
|
||||||
|
|
||||||
u64 m_expiration_time = 0;
|
u64 m_expiration_time = 0;
|
||||||
|
std::shared_ptr<atomic_t<u32>> m_refs;
|
||||||
bool m_processed = false;
|
bool m_processed = false;
|
||||||
usz m_cur_pos = umax;
|
usz m_cur_pos = umax;
|
||||||
};
|
};
|
||||||
|
@ -33,7 +34,7 @@ namespace rsx
|
||||||
compiled_resource get_compiled() override;
|
compiled_resource get_compiled() override;
|
||||||
|
|
||||||
template <typename T>
|
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);
|
std::lock_guard lock(m_mutex_queue);
|
||||||
|
|
||||||
|
@ -41,12 +42,12 @@ namespace rsx
|
||||||
{
|
{
|
||||||
for (auto id : msg_id)
|
for (auto id : msg_id)
|
||||||
{
|
{
|
||||||
m_queue.emplace_back(id);
|
m_queue.emplace_back(id, expiration, refs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
m_queue.emplace_back(msg_id);
|
m_queue.emplace_back(msg_id, expiration, std::move(refs));
|
||||||
}
|
}
|
||||||
|
|
||||||
visible = true;
|
visible = true;
|
||||||
|
@ -58,7 +59,7 @@ namespace rsx
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
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>())
|
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 = std::make_shared<rsx::overlays::message>();
|
||||||
msg_overlay = manager->add(msg_overlay);
|
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/lv2/sys_time.h"
|
||||||
#include "Emu/Cell/Modules/cellGcmSys.h"
|
#include "Emu/Cell/Modules/cellGcmSys.h"
|
||||||
#include "Overlays/overlay_perf_metrics.h"
|
#include "Overlays/overlay_perf_metrics.h"
|
||||||
|
#include "Overlays/overlay_message.h"
|
||||||
#include "Program/GLSLCommon.h"
|
#include "Program/GLSLCommon.h"
|
||||||
#include "Utilities/date_time.h"
|
#include "Utilities/date_time.h"
|
||||||
#include "Utilities/StrUtil.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
|
std::pair<u32, u32> interleaved_range_info::calculate_required_range(u32 first, u32 count) const
|
||||||
{
|
{
|
||||||
if (single_vertex)
|
if (single_vertex)
|
||||||
|
@ -542,7 +551,8 @@ namespace rsx
|
||||||
|
|
||||||
if (g_cfg.savestate.start_paused)
|
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();
|
wait_pause();
|
||||||
}
|
}
|
||||||
|
|
||||||
on_semaphore_acquire_wait();
|
|
||||||
|
|
||||||
if ((state & (cpu_flag::dbg_global_pause + cpu_flag::exit)) == cpu_flag::dbg_global_pause)
|
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.
|
// 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);
|
thread_ctrl::wait_on(state, old, 16000);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
on_semaphore_acquire_wait();
|
||||||
std::this_thread::yield();
|
std::this_thread::yield();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2670,10 +2680,9 @@ namespace rsx
|
||||||
{
|
{
|
||||||
performance_counters.sampled_frames++;
|
performance_counters.sampled_frames++;
|
||||||
|
|
||||||
if (m_pause_on_first_flip)
|
if (m_pause_after_x_flips && m_pause_after_x_flips-- == 1)
|
||||||
{
|
{
|
||||||
Emu.Pause();
|
Emu.Pause();
|
||||||
m_pause_on_first_flip = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -181,7 +181,8 @@ namespace rsx
|
||||||
empty = 1, // PUT == GET
|
empty = 1, // PUT == GET
|
||||||
spinning = 2, // Puller continuously jumps to self addr (synchronization technique)
|
spinning = 2, // Puller continuously jumps to self addr (synchronization technique)
|
||||||
nop = 3, // Puller is processing a NOP command
|
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
|
enum FIFO_hint : u8
|
||||||
|
@ -540,8 +541,8 @@ namespace rsx
|
||||||
rsx::profiling_timer m_profiler;
|
rsx::profiling_timer m_profiler;
|
||||||
frame_statistics_t m_frame_stats;
|
frame_statistics_t m_frame_stats;
|
||||||
|
|
||||||
// Savestates vrelated
|
// Savestates related
|
||||||
bool m_pause_on_first_flip = false;
|
u32 m_pause_after_x_flips = 0;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
RsxDmaControl* ctrl = nullptr;
|
RsxDmaControl* ctrl = nullptr;
|
||||||
|
|
|
@ -1710,7 +1710,7 @@ void VKGSRender::do_local_task(rsx::FIFO_state state)
|
||||||
|
|
||||||
if (m_overlay_manager)
|
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);
|
flush_command_queue(true);
|
||||||
rsx::display_flip_info_t info{};
|
rsx::display_flip_info_t info{};
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
#include "Emu/title.h"
|
#include "Emu/title.h"
|
||||||
#include "Emu/IdManager.h"
|
#include "Emu/IdManager.h"
|
||||||
#include "Emu/RSX/Capture/rsx_replay.h"
|
#include "Emu/RSX/Capture/rsx_replay.h"
|
||||||
|
#include "Emu/RSX/Overlays/overlay_message.h"
|
||||||
|
|
||||||
#include "Loader/PSF.h"
|
#include "Loader/PSF.h"
|
||||||
#include "Loader/TAR.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));
|
extern void parse_hashtable(bool(*cb)(u64 id, u32 refs, u64 ptr, u32 max_coll));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace rsx
|
||||||
|
{
|
||||||
|
void set_native_ui_flip();
|
||||||
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
void fmt_class_string<game_boot_result>::format(std::string& out, u64 arg)
|
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)
|
// Signal profilers to print results (if enabled)
|
||||||
cpu_thread::flush_profilers();
|
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();
|
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;
|
static atomic_t<u32> pause_mark = 0;
|
||||||
|
|
||||||
if (freeze_emulation)
|
if (freeze_emulation)
|
||||||
|
@ -2069,19 +2120,6 @@ bool Emulator::Pause(bool freeze_emulation)
|
||||||
sys_log.error("Emulator::Pause() error: concurrent access");
|
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.
|
// Always Enable display sleep, not only if it was prevented.
|
||||||
enable_display_sleep();
|
enable_display_sleep();
|
||||||
|
|
||||||
|
@ -2167,6 +2205,17 @@ void Emulator::Resume()
|
||||||
|
|
||||||
sys_log.success("Emulation has been resumed!");
|
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)
|
if (g_cfg.misc.prevent_display_sleep)
|
||||||
{
|
{
|
||||||
disable_display_sleep();
|
disable_display_sleep();
|
||||||
|
|
|
@ -144,6 +144,8 @@ class Emulator final
|
||||||
|
|
||||||
bool m_state_inspection_savestate = false;
|
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;
|
std::vector<std::function<void()>> deferred_deserialization;
|
||||||
|
|
||||||
void ExecDeserializationRemnants()
|
void ExecDeserializationRemnants()
|
||||||
|
|
|
@ -146,4 +146,7 @@ enum class localized_string_id
|
||||||
RPCN_ERROR_INVALID_PROTOCOL_VERSION,
|
RPCN_ERROR_INVALID_PROTOCOL_VERSION,
|
||||||
RPCN_ERROR_UNKNOWN,
|
RPCN_ERROR_UNKNOWN,
|
||||||
RPCN_SUCCESS_LOGGED_ON,
|
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") {}
|
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 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 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 };
|
cfg::_bool save_disc_game_data{ this, "Save Disc Game Data", false };
|
||||||
|
|
|
@ -16,10 +16,12 @@
|
||||||
#include "Emu/Io/pad_config.h"
|
#include "Emu/Io/pad_config.h"
|
||||||
#include "Emu/System.h"
|
#include "Emu/System.h"
|
||||||
#include "Emu/system_config.h"
|
#include "Emu/system_config.h"
|
||||||
|
#include "Emu/RSX/Overlays/overlay_message.h"
|
||||||
#include "Utilities/Thread.h"
|
#include "Utilities/Thread.h"
|
||||||
#include "util/atomic.hpp"
|
#include "util/atomic.hpp"
|
||||||
|
|
||||||
LOG_CHANNEL(input_log, "Input");
|
LOG_CHANNEL(input_log, "Input");
|
||||||
|
LOG_CHANNEL(sys_log, "SYS");
|
||||||
|
|
||||||
extern bool is_input_allowed();
|
extern bool is_input_allowed();
|
||||||
|
|
||||||
|
@ -33,6 +35,11 @@ namespace pad
|
||||||
atomic_t<bool> g_enabled{true};
|
atomic_t<bool> g_enabled{true};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace rsx
|
||||||
|
{
|
||||||
|
void set_native_ui_flip();
|
||||||
|
}
|
||||||
|
|
||||||
struct pad_setting
|
struct pad_setting
|
||||||
{
|
{
|
||||||
u32 port_status = 0;
|
u32 port_status = 0;
|
||||||
|
@ -272,15 +279,22 @@ void pad_thread::operator()()
|
||||||
{
|
{
|
||||||
while (thread_ctrl::state() != thread_state::aborting)
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
handler->process();
|
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)
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -353,7 +369,7 @@ void pad_thread::operator()()
|
||||||
if (!(pad->m_port_status & CELL_PAD_STATUS_CONNECTED))
|
if (!(pad->m_port_status & CELL_PAD_STATUS_CONNECTED))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
for (auto& button : pad->m_buttons)
|
for (const auto& button : pad->m_buttons)
|
||||||
{
|
{
|
||||||
if (button.m_pressed && (
|
if (button.m_pressed && (
|
||||||
button.m_outKeyCode == CELL_PAD_CTRL_CROSS ||
|
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();
|
stop_threads();
|
||||||
|
|
|
@ -52,6 +52,11 @@ protected:
|
||||||
std::array<std::shared_ptr<Pad>, CELL_PAD_MAX_PORT_NUM> m_pads;
|
std::array<std::shared_ptr<Pad>, CELL_PAD_MAX_PORT_NUM> m_pads;
|
||||||
|
|
||||||
u32 num_ldd_pad = 0;
|
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
|
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_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_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::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");
|
case localized_string_id::INVALID: return tr("Invalid");
|
||||||
default: return tr("Unknown");
|
default: return tr("Unknown");
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue