From d6597038eeb36f6dbb6bb422d6a2edac63a62df6 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Fri, 28 Jan 2022 00:09:11 +0100 Subject: [PATCH] Input: multithreaded handlers Implements naive multithreading for input handlers. --- rpcs3/Emu/system_config.h | 2 + rpcs3/Emu/system_config_types.cpp | 15 +++++ rpcs3/Emu/system_config_types.h | 6 ++ rpcs3/Input/pad_thread.cpp | 91 +++++++++++++++++++++++++++++-- rpcs3/rpcs3qt/emu_settings.cpp | 7 +++ rpcs3/rpcs3qt/emu_settings_type.h | 2 + rpcs3/rpcs3qt/settings_dialog.cpp | 3 + rpcs3/rpcs3qt/settings_dialog.ui | 15 +++++ rpcs3/rpcs3qt/tooltips.h | 3 +- 9 files changed, 136 insertions(+), 8 deletions(-) diff --git a/rpcs3/Emu/system_config.h b/rpcs3/Emu/system_config.h index d329bcfe1f..c1d2b7297c 100644 --- a/rpcs3/Emu/system_config.h +++ b/rpcs3/Emu/system_config.h @@ -253,6 +253,8 @@ struct cfg_root : cfg::node cfg::_enum buzz{ this, "Buzz emulated controller", buzz_handler::null }; cfg::_enum turntable{this, "Turntable emulated controller", turntable_handler::null}; cfg::_enum ghltar{this, "GHLtar emulated controller", ghltar_handler::null}; + cfg::_enum pad_mode{this, "Pad handler mode", pad_handler_mode::single_threaded, true}; + cfg::uint<0, 100'000> pad_sleep{this, "Pad handler sleep (microseconds)", 1'000, true}; } io{ this }; struct node_sys : cfg::node diff --git a/rpcs3/Emu/system_config_types.cpp b/rpcs3/Emu/system_config_types.cpp index ab99907795..9a4d080cd9 100644 --- a/rpcs3/Emu/system_config_types.cpp +++ b/rpcs3/Emu/system_config_types.cpp @@ -385,6 +385,21 @@ void fmt_class_string::format(std::string& out, u64 arg) }); } +template <> +void fmt_class_string::format(std::string& out, u64 arg) +{ + format_enum(out, arg, [](auto value) + { + switch (value) + { + case pad_handler_mode::single_threaded: return "Single-threaded"; + case pad_handler_mode::multi_threaded: return "Multi-threaded"; + } + + return unknown; + }); +} + template <> void fmt_class_string::format(std::string& out, u64 arg) { diff --git a/rpcs3/Emu/system_config_types.h b/rpcs3/Emu/system_config_types.h index 40691f0129..4cef275c43 100644 --- a/rpcs3/Emu/system_config_types.h +++ b/rpcs3/Emu/system_config_types.h @@ -135,6 +135,12 @@ enum class microphone_handler rocksmith, }; +enum class pad_handler_mode +{ + single_threaded, // All pad handlers run on the same thread sequentially. + multi_threaded // Each pad handler has its own thread. +}; + enum class video_resolution { _1080, diff --git a/rpcs3/Input/pad_thread.cpp b/rpcs3/Input/pad_thread.cpp index a58b57e16b..a975d3c478 100644 --- a/rpcs3/Input/pad_thread.cpp +++ b/rpcs3/Input/pad_thread.cpp @@ -15,7 +15,9 @@ #include "Emu/Io/PadHandler.h" #include "Emu/Io/pad_config.h" #include "Emu/System.h" +#include "Emu/system_config.h" #include "Utilities/Thread.h" +#include "util/atomic.hpp" LOG_CHANNEL(input_log, "Input"); @@ -212,6 +214,55 @@ void pad_thread::operator()() { pad::g_reset = true; + atomic_t pad_mode{g_cfg.io.pad_mode.get()}; + std::vector>>> threads; + + const auto stop_threads = [&threads]() + { + for (auto& thread : threads) + { + if (thread) + { + auto& enumeration_thread = *thread; + enumeration_thread = thread_state::aborting; + enumeration_thread(); + } + } + threads.clear(); + }; + + const auto start_threads = [this, &threads, &pad_mode]() + { + if (pad_mode == pad_handler_mode::single_threaded) + { + return; + } + + for (const auto& handler : handlers) + { + if (handler.first == pad_handler::null) + { + continue; + } + + threads.push_back(std::make_unique>>(fmt::format("%s Thread", handler.second->m_type), [&handler = handler.second, &pad_mode]() + { + while (thread_ctrl::state() != thread_state::aborting) + { + if (!pad::g_enabled || Emu.IsPaused()) + { + thread_ctrl::wait_for(10'000); + continue; + } + + handler->ThreadProc(); + + thread_ctrl::wait_for(g_cfg.io.pad_sleep); + } + })); + } + }; + while (thread_ctrl::state() != thread_state::aborting) { if (!pad::g_enabled || Emu.IsPaused()) @@ -220,17 +271,43 @@ void pad_thread::operator()() continue; } - if (pad::g_reset && pad::g_reset.exchange(false)) + // Update variables + const bool needs_reset = pad::g_reset && pad::g_reset.exchange(false); + const bool mode_changed = pad_mode != pad_mode.exchange(g_cfg.io.pad_mode.get()); + + // Reset pad handlers if necessary + if (needs_reset || mode_changed) { - Init(); + stop_threads(); + + if (needs_reset) + { + Init(); + } + else + { + input_log.success("The pad mode was changed to %s", pad_mode.load()); + } + + start_threads(); } u32 connected_devices = 0; - for (auto& cur_pad_handler : handlers) + if (pad_mode == pad_handler_mode::single_threaded) { - cur_pad_handler.second->ThreadProc(); - connected_devices += cur_pad_handler.second->connected_devices; + for (auto& handler : handlers) + { + handler.second->ThreadProc(); + connected_devices += handler.second->connected_devices; + } + } + else + { + for (auto& handler : handlers) + { + connected_devices += handler.second->connected_devices; + } } m_info.now_connect = connected_devices + num_ldd_pad; @@ -271,8 +348,10 @@ void pad_thread::operator()() } } - thread_ctrl::wait_for(1000); + thread_ctrl::wait_for(g_cfg.io.pad_sleep); } + + stop_threads(); } void pad_thread::InitLddPad(u32 handle) diff --git a/rpcs3/rpcs3qt/emu_settings.cpp b/rpcs3/rpcs3qt/emu_settings.cpp index ce245cec29..7c6ca5b391 100644 --- a/rpcs3/rpcs3qt/emu_settings.cpp +++ b/rpcs3/rpcs3qt/emu_settings.cpp @@ -1027,6 +1027,13 @@ QString emu_settings::GetLocalizedSetting(const QString& original, emu_settings_ case camera_handler::qt: return tr("Qt", "Camera handler"); } break; + case emu_settings_type::PadHandlerMode: + switch (static_cast(index)) + { + case pad_handler_mode::single_threaded: return tr("Single-threaded", "Pad handler mode"); + case pad_handler_mode::multi_threaded: return tr("Multi-threaded", "Pad handler mode"); + } + break; case emu_settings_type::Move: switch (static_cast(index)) { diff --git a/rpcs3/rpcs3qt/emu_settings_type.h b/rpcs3/rpcs3qt/emu_settings_type.h index ed0b0c0f54..eff3d29445 100644 --- a/rpcs3/rpcs3qt/emu_settings_type.h +++ b/rpcs3/rpcs3qt/emu_settings_type.h @@ -126,6 +126,7 @@ enum class emu_settings_type MicrophoneDevices, // Input / Output + PadHandlerMode, KeyboardHandler, MouseHandler, Camera, @@ -287,6 +288,7 @@ inline static const QMap settings_location = { emu_settings_type::MicrophoneDevices, { "Audio", "Microphone Devices" }}, // Input / Output + { emu_settings_type::PadHandlerMode, { "Input/Output", "Pad handler mode"}}, { emu_settings_type::KeyboardHandler, { "Input/Output", "Keyboard"}}, { emu_settings_type::MouseHandler, { "Input/Output", "Mouse"}}, { emu_settings_type::Camera, { "Input/Output", "Camera"}}, diff --git a/rpcs3/rpcs3qt/settings_dialog.cpp b/rpcs3/rpcs3qt/settings_dialog.cpp index b602a3c2e7..f11f0c410d 100644 --- a/rpcs3/rpcs3qt/settings_dialog.cpp +++ b/rpcs3/rpcs3qt/settings_dialog.cpp @@ -1063,6 +1063,9 @@ settings_dialog::settings_dialog(std::shared_ptr gui_settings, std SubscribeTooltip(ui->gb_camera_id, tooltips.settings.camera_id); } + m_emu_settings->EnhanceComboBox(ui->padModeBox, emu_settings_type::PadHandlerMode); + SubscribeTooltip(ui->gb_pad_mode, tooltips.settings.pad_mode); + m_emu_settings->EnhanceComboBox(ui->moveBox, emu_settings_type::Move); SubscribeTooltip(ui->gb_move_handler, tooltips.settings.move); diff --git a/rpcs3/rpcs3qt/settings_dialog.ui b/rpcs3/rpcs3qt/settings_dialog.ui index 52b9a48a33..da26fd8aae 100644 --- a/rpcs3/rpcs3qt/settings_dialog.ui +++ b/rpcs3/rpcs3qt/settings_dialog.ui @@ -53,6 +53,9 @@ 0 + + 0 + CPU @@ -1572,6 +1575,18 @@ + + + + Pad Handler Mode + + + + + + + + diff --git a/rpcs3/rpcs3qt/tooltips.h b/rpcs3/rpcs3qt/tooltips.h index 1563d86a20..5567b6d1eb 100644 --- a/rpcs3/rpcs3qt/tooltips.h +++ b/rpcs3/rpcs3qt/tooltips.h @@ -193,8 +193,7 @@ public: // input - const QString pad_handler = tr("If you want to use the keyboard to control, select the Keyboard option.\nIf you have a DualShock 4, select DualShock 4.\nIf you have an Xbox controller, or another compatible device, use XInput.\nOlder controllers such as PS2 controllers with an adapter usually work fine with MMJoystick.\nCheck button mappings in the Windows control panel."); - const QString pad_handler_linux = tr("If you want to use the keyboard to control, select the Keyboard option.\nIf you have a DualShock 4, select DualShock 4."); + const QString pad_mode = tr("Single-threaded: All pad handlers run on the same thread sequentially.\nMulti-threaded: Each pad handler has its own thread.\nOnly use multi-threaded if you can spare the extra threads."); const QString keyboard_handler = tr("Some games support native keyboard input.\nBasic will work in these cases."); const QString mouse_handler = tr("Some games support native mouse input.\nBasic will work in these cases."); const QString camera = tr("Select Qt Camera to use the default camera device of your operating system.");