From 1cff5b35d438bd27df39ed494d5d0ad15bf3a490 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Tue, 21 Jan 2025 02:56:45 +0100 Subject: [PATCH 01/14] input: add vibration multiplier --- rpcs3/Emu/Io/pad_config.cpp | 14 +++++++ rpcs3/Emu/Io/pad_config.h | 7 +++- rpcs3/Input/ds3_pad_handler.cpp | 7 +--- rpcs3/Input/ds4_pad_handler.cpp | 7 +--- rpcs3/Input/dualsense_pad_handler.cpp | 7 +--- rpcs3/Input/evdev_joystick_handler.cpp | 6 +-- rpcs3/Input/ps_move_handler.cpp | 4 +- rpcs3/Input/sdl_pad_handler.cpp | 7 +--- rpcs3/Input/xinput_pad_handler.cpp | 7 +--- rpcs3/rpcs3qt/pad_settings_dialog.cpp | 52 +++++++++++--------------- rpcs3/rpcs3qt/pad_settings_dialog.h | 6 +-- rpcs3/rpcs3qt/pad_settings_dialog.ui | 22 +++++------ 12 files changed, 68 insertions(+), 78 deletions(-) diff --git a/rpcs3/Emu/Io/pad_config.cpp b/rpcs3/Emu/Io/pad_config.cpp index cdca3f6101..fb0b140aeb 100644 --- a/rpcs3/Emu/Io/pad_config.cpp +++ b/rpcs3/Emu/Io/pad_config.cpp @@ -32,6 +32,20 @@ std::string cfg_pad::get_buttons(std::vector vec) return fmt::merge(vec, ","); } +u8 cfg_pad::get_large_motor_speed(const std::array& motor_speed) const +{ + const u8 idx = switch_vibration_motors ? 1 : 0; + const f32 multiplier = multiplier_vibration_motor_large / 100.0f; + return static_cast(std::clamp(motor_speed[idx].m_value * multiplier, 0.0f, 255.0f)); +} + +u8 cfg_pad::get_small_motor_speed(const std::array& motor_speed) const +{ + const u8 idx = switch_vibration_motors ? 0 : 1; + const f32 multiplier = multiplier_vibration_motor_small / 100.0f; + return static_cast(std::clamp(motor_speed[idx].m_value * multiplier, 0.0f, 255.0f)); +} + bool cfg_input::load(const std::string& title_id, const std::string& config_file, bool strict) { input_log.notice("Loading pad config (title_id='%s', config_file='%s', strict=%d)", title_id, config_file, strict); diff --git a/rpcs3/Emu/Io/pad_config.h b/rpcs3/Emu/Io/pad_config.h index 5a7517aa72..7c39a79411 100644 --- a/rpcs3/Emu/Io/pad_config.h +++ b/rpcs3/Emu/Io/pad_config.h @@ -28,6 +28,9 @@ struct cfg_pad final : cfg::node static std::vector get_buttons(const std::string& str); static std::string get_buttons(std::vector vec); + u8 get_large_motor_speed(const std::array& motor_speed) const; + u8 get_small_motor_speed(const std::array& motor_speed) const; + cfg::string ls_left{ this, "Left Stick Left", "" }; cfg::string ls_down{ this, "Left Stick Down", "" }; cfg::string ls_right{ this, "Left Stick Right", "" }; @@ -96,8 +99,8 @@ struct cfg_pad final : cfg::node cfg::uint<0, 100> led_battery_indicator_brightness{ this, "LED battery indicator brightness", 50 }; cfg::_bool player_led_enabled{ this, "Player LED enabled", true }; - cfg::_bool enable_vibration_motor_large{ this, "Enable Large Vibration Motor", true }; - cfg::_bool enable_vibration_motor_small{ this, "Enable Small Vibration Motor", true }; + cfg::uint<0, 200> multiplier_vibration_motor_large{ this, "Large Vibration Motor Multiplier", 100 }; + cfg::uint<0, 200> multiplier_vibration_motor_small{ this, "Small Vibration Motor Multiplier", 100 }; cfg::_bool switch_vibration_motors{ this, "Switch Vibration Motors", false }; cfg::_enum mouse_move_mode{ this, "Mouse Movement Mode", mouse_movement_mode::relative }; diff --git a/rpcs3/Input/ds3_pad_handler.cpp b/rpcs3/Input/ds3_pad_handler.cpp index d5dc2ef7d8..0ea5aad19f 100644 --- a/rpcs3/Input/ds3_pad_handler.cpp +++ b/rpcs3/Input/ds3_pad_handler.cpp @@ -551,11 +551,8 @@ void ds3_pad_handler::apply_pad_data(const pad_ensemble& binding) cfg_pad* config = dev->config; - const int idx_l = config->switch_vibration_motors ? 1 : 0; - const int idx_s = config->switch_vibration_motors ? 0 : 1; - - const u8 speed_large = config->enable_vibration_motor_large ? pad->m_vibrateMotors[idx_l].m_value : 0; - const u8 speed_small = config->enable_vibration_motor_small ? pad->m_vibrateMotors[idx_s].m_value : 0; + const u8 speed_large = config->get_large_motor_speed(pad->m_vibrateMotors); + const u8 speed_small = config->get_small_motor_speed(pad->m_vibrateMotors); const bool wireless = dev->cable_state == 0; const bool low_battery = dev->battery_level < 25; diff --git a/rpcs3/Input/ds4_pad_handler.cpp b/rpcs3/Input/ds4_pad_handler.cpp index d7fc6dd9ae..a24351fad9 100644 --- a/rpcs3/Input/ds4_pad_handler.cpp +++ b/rpcs3/Input/ds4_pad_handler.cpp @@ -920,11 +920,8 @@ void ds4_pad_handler::apply_pad_data(const pad_ensemble& binding) cfg_pad* config = dev->config; // Attempt to send rumble no matter what - const int idx_l = config->switch_vibration_motors ? 1 : 0; - const int idx_s = config->switch_vibration_motors ? 0 : 1; - - const u8 speed_large = config->enable_vibration_motor_large ? pad->m_vibrateMotors[idx_l].m_value : 0; - const u8 speed_small = config->enable_vibration_motor_small ? pad->m_vibrateMotors[idx_s].m_value : 0; + const u8 speed_large = config->get_large_motor_speed(pad->m_vibrateMotors); + const u8 speed_small = config->get_small_motor_speed(pad->m_vibrateMotors); const bool wireless = dev->cable_state == 0; const bool low_battery = dev->battery_level < 2; diff --git a/rpcs3/Input/dualsense_pad_handler.cpp b/rpcs3/Input/dualsense_pad_handler.cpp index e7b34b45bf..4c0253adba 100644 --- a/rpcs3/Input/dualsense_pad_handler.cpp +++ b/rpcs3/Input/dualsense_pad_handler.cpp @@ -941,11 +941,8 @@ void dualsense_pad_handler::apply_pad_data(const pad_ensemble& binding) cfg_pad* config = dev->config; // Attempt to send rumble no matter what - const int idx_l = config->switch_vibration_motors ? 1 : 0; - const int idx_s = config->switch_vibration_motors ? 0 : 1; - - const u8 speed_large = config->enable_vibration_motor_large ? pad->m_vibrateMotors[idx_l].m_value : 0; - const u8 speed_small = config->enable_vibration_motor_small ? pad->m_vibrateMotors[idx_s].m_value : 0; + const u8 speed_large = config->get_large_motor_speed(pad->m_vibrateMotors); + const u8 speed_small = config->get_small_motor_speed(pad->m_vibrateMotors); const bool wireless = dev->cable_state == 0; const bool low_battery = dev->battery_level <= 1; diff --git a/rpcs3/Input/evdev_joystick_handler.cpp b/rpcs3/Input/evdev_joystick_handler.cpp index 09872203d6..d101d74173 100644 --- a/rpcs3/Input/evdev_joystick_handler.cpp +++ b/rpcs3/Input/evdev_joystick_handler.cpp @@ -1271,10 +1271,8 @@ void evdev_joystick_handler::apply_pad_data(const pad_ensemble& binding) return; // Handle vibration - const int idx_l = cfg->switch_vibration_motors ? 1 : 0; - const int idx_s = cfg->switch_vibration_motors ? 0 : 1; - const u8 force_large = cfg->enable_vibration_motor_large ? pad->m_vibrateMotors[idx_l].m_value * 257 : 0; - const u8 force_small = cfg->enable_vibration_motor_small ? pad->m_vibrateMotors[idx_s].m_value * 257 : 0; + const u8 force_large = cfg->get_large_motor_speed(pad->m_vibrateMotors); + const u8 force_small = cfg->get_small_motor_speed(pad->m_vibrateMotors); SetRumble(evdev_device, force_large, force_small); } diff --git a/rpcs3/Input/ps_move_handler.cpp b/rpcs3/Input/ps_move_handler.cpp index b53b4bcd7e..4acf46cf5f 100644 --- a/rpcs3/Input/ps_move_handler.cpp +++ b/rpcs3/Input/ps_move_handler.cpp @@ -820,9 +820,7 @@ void ps_move_handler::apply_pad_data(const pad_ensemble& binding) cfg_pad* config = dev->config; - const int idx_l = config->switch_vibration_motors ? 1 : 0; - - const u8 speed_large = config->enable_vibration_motor_large ? pad->m_vibrateMotors[idx_l].m_value : 0; + const u8 speed_large = config->get_large_motor_speed(pad->m_vibrateMotors); dev->new_output_data |= dev->large_motor != speed_large; dev->large_motor = speed_large; diff --git a/rpcs3/Input/sdl_pad_handler.cpp b/rpcs3/Input/sdl_pad_handler.cpp index 202eca4038..562085f037 100644 --- a/rpcs3/Input/sdl_pad_handler.cpp +++ b/rpcs3/Input/sdl_pad_handler.cpp @@ -800,13 +800,10 @@ void sdl_pad_handler::apply_pad_data(const pad_ensemble& binding) // The left motor is the low-frequency rumble motor. The right motor is the high-frequency rumble motor. // The two motors are not the same, and they create different vibration effects. Values range between 0 to 65535. - const usz idx_l = cfg->switch_vibration_motors ? 1 : 0; - const usz idx_s = cfg->switch_vibration_motors ? 0 : 1; - if (dev->sdl.has_rumble || dev->sdl.has_rumble_triggers) { - const u8 speed_large = cfg->enable_vibration_motor_large ? pad->m_vibrateMotors[idx_l].m_value : 0; - const u8 speed_small = cfg->enable_vibration_motor_small ? pad->m_vibrateMotors[idx_s].m_value : 0; + const u8 speed_large = cfg->get_large_motor_speed(pad->m_vibrateMotors); + const u8 speed_small = cfg->get_small_motor_speed(pad->m_vibrateMotors); dev->new_output_data |= dev->large_motor != speed_large || dev->small_motor != speed_small; diff --git a/rpcs3/Input/xinput_pad_handler.cpp b/rpcs3/Input/xinput_pad_handler.cpp index d7d3c7991f..49c2467faf 100644 --- a/rpcs3/Input/xinput_pad_handler.cpp +++ b/rpcs3/Input/xinput_pad_handler.cpp @@ -575,11 +575,8 @@ void xinput_pad_handler::apply_pad_data(const pad_ensemble& binding) // The left motor is the low-frequency rumble motor. The right motor is the high-frequency rumble motor. // The two motors are not the same, and they create different vibration effects. Values range between 0 to 65535. - const usz idx_l = cfg->switch_vibration_motors ? 1 : 0; - const usz idx_s = cfg->switch_vibration_motors ? 0 : 1; - - const u8 speed_large = cfg->enable_vibration_motor_large ? pad->m_vibrateMotors[idx_l].m_value : 0; - const u8 speed_small = cfg->enable_vibration_motor_small ? pad->m_vibrateMotors[idx_s].m_value : 0; + const u8 speed_large = cfg->get_large_motor_speed(pad->m_vibrateMotors); + const u8 speed_small = cfg->get_small_motor_speed(pad->m_vibrateMotors); dev->new_output_data |= dev->large_motor != speed_large || dev->small_motor != speed_small; diff --git a/rpcs3/rpcs3qt/pad_settings_dialog.cpp b/rpcs3/rpcs3qt/pad_settings_dialog.cpp index 616d6577aa..01904e5048 100644 --- a/rpcs3/rpcs3qt/pad_settings_dialog.cpp +++ b/rpcs3/rpcs3qt/pad_settings_dialog.cpp @@ -332,33 +332,25 @@ void pad_settings_dialog::InitButtons() } }); - connect(ui->chb_vibration_large, &QCheckBox::clicked, this, [this](bool checked) + connect(ui->sb_vibration_large, &QSpinBox::valueChanged, this, [this](int value) { - if (!checked) - { - return; - } + const u8 force = static_cast(std::clamp(m_max_force * (value / 100.0f), 0.0f, 255.0f)); + ui->chb_vibration_switch->isChecked() ? SetPadData(m_min_force, force) + : SetPadData(force, m_min_force); - ui->chb_vibration_switch->isChecked() ? SetPadData(m_min_force, m_max_force) - : SetPadData(m_max_force, m_min_force); - - QTimer::singleShot(300, [this]() + QTimer::singleShot(300, this, [this]() { SetPadData(m_min_force, m_min_force); }); }); - connect(ui->chb_vibration_small, &QCheckBox::clicked, this, [this](bool checked) + connect(ui->sb_vibration_small, &QSpinBox::valueChanged, this, [this](int value) { - if (!checked) - { - return; - } + const u8 force = static_cast(std::clamp(m_max_force * (value / 100.0f), 0.0f, 255.0f)); + ui->chb_vibration_switch->isChecked() ? SetPadData(force, m_min_force) + : SetPadData(m_min_force, force); - ui->chb_vibration_switch->isChecked() ? SetPadData(m_max_force, m_min_force) - : SetPadData(m_min_force, m_max_force); - - QTimer::singleShot(300, [this]() + QTimer::singleShot(300, this, [this]() { SetPadData(m_min_force, m_min_force); }); @@ -369,12 +361,12 @@ void pad_settings_dialog::InitButtons() checked ? SetPadData(m_min_force, m_max_force) : SetPadData(m_max_force, m_min_force); - QTimer::singleShot(200, [this, checked]() + QTimer::singleShot(200, this, [this, checked]() { checked ? SetPadData(m_max_force, m_min_force) : SetPadData(m_min_force, m_max_force); - QTimer::singleShot(200, [this]() + QTimer::singleShot(200, this, [this]() { SetPadData(m_min_force, m_min_force); }); @@ -618,12 +610,12 @@ void pad_settings_dialog::RefreshPads() } } -void pad_settings_dialog::SetPadData(u32 large_motor, u32 small_motor, bool led_battery_indicator) +void pad_settings_dialog::SetPadData(u8 large_motor, u8 small_motor, bool led_battery_indicator) { - ensure(m_handler); const cfg_pad& cfg = GetPlayerConfig(); std::lock_guard lock(m_handler_mutex); + ensure(m_handler); m_handler->SetPadData(m_device_name, GetPlayerIndex(), large_motor, small_motor, cfg.colorR, cfg.colorG, cfg.colorB, cfg.player_led_enabled.get(), led_battery_indicator, cfg.led_battery_indicator_brightness); } @@ -1116,8 +1108,12 @@ void pad_settings_dialog::UpdateLabels(bool is_reset) } } - ui->chb_vibration_large->setChecked(cfg.enable_vibration_motor_large.get()); - ui->chb_vibration_small->setChecked(cfg.enable_vibration_motor_small.get()); + ui->sb_vibration_large->setRange(cfg.multiplier_vibration_motor_large.min, cfg.multiplier_vibration_motor_large.max); + ui->sb_vibration_large->setValue(cfg.multiplier_vibration_motor_large.get()); + + ui->sb_vibration_small->setRange(cfg.multiplier_vibration_motor_small.min, cfg.multiplier_vibration_motor_small.max); + ui->sb_vibration_small->setValue(cfg.multiplier_vibration_motor_small.get()); + ui->chb_vibration_switch->setChecked(cfg.switch_vibration_motors.get()); // Update Trigger Thresholds @@ -1448,10 +1444,6 @@ void pad_settings_dialog::ChangeHandler() } ui->l_description->setText(m_description); - // Update parameters - m_min_force = 0; - m_max_force = 255; - // Reset parameters m_lx = 0; m_ly = 0; @@ -1873,8 +1865,8 @@ void pad_settings_dialog::ApplyCurrentPlayerConfig(int new_player_id) if (m_handler->has_rumble()) { - cfg.enable_vibration_motor_large.set(ui->chb_vibration_large->isChecked()); - cfg.enable_vibration_motor_small.set(ui->chb_vibration_small->isChecked()); + cfg.multiplier_vibration_motor_large.set(ui->sb_vibration_large->value()); + cfg.multiplier_vibration_motor_small.set(ui->sb_vibration_small->value()); cfg.switch_vibration_motors.set(ui->chb_vibration_switch->isChecked()); } diff --git a/rpcs3/rpcs3qt/pad_settings_dialog.h b/rpcs3/rpcs3qt/pad_settings_dialog.h index 9902dde9ff..c87bf70f95 100644 --- a/rpcs3/rpcs3qt/pad_settings_dialog.h +++ b/rpcs3/rpcs3qt/pad_settings_dialog.h @@ -139,8 +139,8 @@ private: int m_ry = 0; // Rumble - s32 m_min_force = 0; - s32 m_max_force = 0; + static constexpr u8 m_min_force = 0; + static constexpr u8 m_max_force = 255; // Backup for standard button palette QPalette m_palette; @@ -190,7 +190,7 @@ private: void CancelExit(); // Set vibrate data while keeping the current color - void SetPadData(u32 large_motor, u32 small_motor, bool led_battery_indicator = false); + void SetPadData(u8 large_motor, u8 small_motor, bool led_battery_indicator = false); /** Update all the Button Labels with current button mapping */ void UpdateLabels(bool is_reset = false); diff --git a/rpcs3/rpcs3qt/pad_settings_dialog.ui b/rpcs3/rpcs3qt/pad_settings_dialog.ui index a886f16b31..0ff621c6a2 100644 --- a/rpcs3/rpcs3qt/pad_settings_dialog.ui +++ b/rpcs3/rpcs3qt/pad_settings_dialog.ui @@ -937,7 +937,7 @@ Enable Vibration - + 5 @@ -951,22 +951,22 @@ 5 - - - Large + + + % - - true + + Large - - - Small + + + % - - true + + Small From 6805c360048950b5521efdc0c9223405c2940e62 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Fri, 17 Jan 2025 00:25:18 +0100 Subject: [PATCH 02/14] Decrease log level of config file move error --- rpcs3/Emu/System.cpp | 2 +- rpcs3/Emu/games_config.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/rpcs3/Emu/System.cpp b/rpcs3/Emu/System.cpp index 179eb7f1ad..cb31823026 100644 --- a/rpcs3/Emu/System.cpp +++ b/rpcs3/Emu/System.cpp @@ -417,7 +417,7 @@ void Emulator::Init() if (!fs::rename(old_path, cfg_path, false)) { - sys_log.error("Failed to move '%s' to '%s' (error='%s')", old_path, cfg_path, fs::g_tls_error); + (fs::g_tls_error == fs::error::exist ? sys_log.warning : sys_log.error)("Failed to move '%s' to '%s' (error='%s')", old_path, cfg_path, fs::g_tls_error); } } #endif diff --git a/rpcs3/Emu/games_config.cpp b/rpcs3/Emu/games_config.cpp index 27c086abdf..cfa43f6e15 100644 --- a/rpcs3/Emu/games_config.cpp +++ b/rpcs3/Emu/games_config.cpp @@ -159,7 +159,7 @@ void games_config::load() if (!fs::rename(old_path, path, false)) { - cfg_log.error("Failed to move '%s' to '%s' (error='%s')", old_path, path, fs::g_tls_error); + (fs::g_tls_error == fs::error::exist ? cfg_log.warning : cfg_log.error)("Failed to move '%s' to '%s' (error='%s')", old_path, path, fs::g_tls_error); } } #endif From 85de7432a24af0508e38196567e306e0fea48177 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Mon, 20 Jan 2025 22:45:42 +0100 Subject: [PATCH 03/14] cellGem: improve convert_image_format logging --- rpcs3/Emu/Cell/Modules/cellGem.cpp | 26 +++++++++++++------------- rpcs3/Input/ps_move_tracker.cpp | 4 ++-- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/rpcs3/Emu/Cell/Modules/cellGem.cpp b/rpcs3/Emu/Cell/Modules/cellGem.cpp index da3c7f6261..c66c79577f 100644 --- a/rpcs3/Emu/Cell/Modules/cellGem.cpp +++ b/rpcs3/Emu/Cell/Modules/cellGem.cpp @@ -685,7 +685,7 @@ namespace gem bool convert_image_format(CellCameraFormat input_format, CellGemVideoConvertFormatEnum output_format, const std::vector& video_data_in, u32 width, u32 height, - u8* video_data_out, u32 video_data_out_size) + u8* video_data_out, u32 video_data_out_size, std::string_view caller) { if (output_format != CELL_GEM_NO_VIDEO_OUTPUT && !video_data_out) { @@ -697,13 +697,13 @@ namespace gem if (video_data_in.size() != required_in_size) { - cellGem.error("convert: in_size mismatch: required=%d, actual=%d", required_in_size, video_data_in.size()); + cellGem.error("convert: in_size mismatch: required=%d, actual=%d (called from %s)", required_in_size, video_data_in.size(), caller); return false; } if (required_out_size < 0 || video_data_out_size != static_cast(required_out_size)) { - cellGem.error("convert: out_size unknown: required=%d, format %d", required_out_size, output_format); + cellGem.error("convert: out_size unknown: required=%d, actual=%d, format %d (called from %s)", required_out_size, video_data_out_size, output_format, caller); return false; } @@ -763,7 +763,7 @@ namespace gem } default: { - cellGem.error("Unimplemented: Converting %s to %s", input_format, output_format); + cellGem.error("Unimplemented: Converting %s to %s (called from %s)", input_format, output_format, caller); std::memcpy(video_data_out, video_data_in.data(), std::min(required_in_size, required_out_size)); return false; } @@ -778,7 +778,7 @@ namespace gem } else { - cellGem.error("Unimplemented: Converting %s to %s", input_format, output_format); + cellGem.error("Unimplemented: Converting %s to %s (called from %s)", input_format, output_format, caller); return false; } break; @@ -860,7 +860,7 @@ namespace gem } default: { - cellGem.error("Unimplemented: Converting %s to %s", input_format, output_format); + cellGem.error("Unimplemented: Converting %s to %s (called from %s)", input_format, output_format, caller); std::memcpy(video_data_out, video_data_in.data(), std::min(required_in_size, required_out_size)); return false; } @@ -950,7 +950,7 @@ namespace gem } default: { - cellGem.error("Unimplemented: Converting %s to %s", input_format, output_format); + cellGem.error("Unimplemented: Converting %s to %s (called from %s)", input_format, output_format, caller); std::memcpy(video_data_out, video_data_in.data(), std::min(required_in_size, required_out_size)); return false; } @@ -1060,7 +1060,7 @@ namespace gem } default: { - cellGem.error("Unimplemented: Converting %s to %s", input_format, output_format); + cellGem.error("Unimplemented: Converting %s to %s (called from %s)", input_format, output_format, caller); std::memcpy(video_data_out, video_data_in.data(), std::min(required_in_size, required_out_size)); return false; } @@ -1122,7 +1122,7 @@ namespace gem } default: { - cellGem.error("Unimplemented: Converting %s to %s", input_format, output_format); + cellGem.error("Unimplemented: Converting %s to %s (called from %s)", input_format, output_format, caller); std::memcpy(video_data_out, video_data_in.data(), std::min(required_in_size, required_out_size)); return false; } @@ -1132,18 +1132,18 @@ namespace gem case CELL_GEM_BAYER_RESTORED_RGGB: // Restored Bayer output, 2x2 pixels rearranged into 320x240 RG1G2B case CELL_GEM_BAYER_RESTORED_RASTERIZED: // Restored Bayer output, R,G1,G2,B rearranged into 4 contiguous 320x240 1-channel rasters { - cellGem.error("Unimplemented: Converting %s to %s", input_format, output_format); + cellGem.error("Unimplemented: Converting %s to %s (called from %s)", input_format, output_format, caller); std::memcpy(video_data_out, video_data_in.data(), std::min(required_in_size, required_out_size)); return false; } case CELL_GEM_NO_VIDEO_OUTPUT: // Disable video output { - cellGem.trace("Ignoring frame conversion for CELL_GEM_NO_VIDEO_OUTPUT"); + cellGem.trace("Ignoring frame conversion for CELL_GEM_NO_VIDEO_OUTPUT (called from %s)", caller); break; } default: { - cellGem.error("Trying to convert %s to %s", input_format, output_format); + cellGem.error("Trying to convert %s to %s (called from %s)", input_format, output_format, caller); return false; } } @@ -1310,7 +1310,7 @@ void gem_config_data::operator()() const auto& shared_data = g_fxo->get(); - if (gem::convert_image_format(shared_data.format, vc.output_format, video_data_in, shared_data.width, shared_data.height, vc_attribute.video_data_out ? vc_attribute.video_data_out.get_ptr() : nullptr, video_data_out_size)) + if (gem::convert_image_format(shared_data.format, vc.output_format, video_data_in, shared_data.width, shared_data.height, vc_attribute.video_data_out ? vc_attribute.video_data_out.get_ptr() : nullptr, video_data_out_size, "cellGem")) { cellGem.trace("Converted video frame of format %s to %s", shared_data.format.load(), vc.output_format.get()); diff --git a/rpcs3/Input/ps_move_tracker.cpp b/rpcs3/Input/ps_move_tracker.cpp index b831e87967..d3deead687 100644 --- a/rpcs3/Input/ps_move_tracker.cpp +++ b/rpcs3/Input/ps_move_tracker.cpp @@ -15,7 +15,7 @@ namespace gem { extern bool convert_image_format(CellCameraFormat input_format, CellGemVideoConvertFormatEnum output_format, const std::vector& video_data_in, u32 width, u32 height, - u8* video_data_out, u32 video_data_out_size); + u8* video_data_out, u32 video_data_out_size, std::string_view caller); } template @@ -238,7 +238,7 @@ void ps_move_tracker::convert_image(s32 output_format) m_image_binary[index].resize(size); } - if (gem::convert_image_format(CellCameraFormat{m_format}, CellGemVideoConvertFormatEnum{output_format}, m_image_data, width, height, m_image_rgba.data(), ::size32(m_image_rgba))) + if (gem::convert_image_format(CellCameraFormat{m_format}, CellGemVideoConvertFormatEnum{output_format}, m_image_data, width, height, m_image_rgba.data(), ::size32(m_image_rgba), "gemTracker")) { ps_move.trace("Converted video frame of format %s to %s", CellCameraFormat{m_format}, CellGemVideoConvertFormatEnum{output_format}); } From ade2698bb40874c46dd39b6a7a210186c652f8cc Mon Sep 17 00:00:00 2001 From: Megamouse Date: Mon, 20 Jan 2025 22:51:38 +0100 Subject: [PATCH 04/14] cellCamera: start camera when loading savestates --- rpcs3/Emu/Cell/Modules/cellCamera.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/rpcs3/Emu/Cell/Modules/cellCamera.cpp b/rpcs3/Emu/Cell/Modules/cellCamera.cpp index 64135ca5fa..e8a6b656d7 100644 --- a/rpcs3/Emu/Cell/Modules/cellCamera.cpp +++ b/rpcs3/Emu/Cell/Modules/cellCamera.cpp @@ -151,6 +151,21 @@ void camera_context::save(utils::serial& ar) GET_OR_USE_SERIALIZATION_VERSION(ar.is_writing(), cellCamera); ar(notify_data_map, start_timestamp_us, read_mode, is_streaming, is_attached, is_open, info, attr, frame_num); + + if (!ar.is_writing()) + { + if (is_open) + { + if (!open_camera()) + { + cellCamera.error("Failed to open camera while loading savestate"); + } + else if (is_streaming && !start_camera()) + { + cellCamera.error("Failed to start camera while loading savestate"); + } + } + } } static bool check_dev_num(s32 dev_num) From 23483a80c7f604f4111d36ffe13d354973174234 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Mon, 20 Jan 2025 22:54:26 +0100 Subject: [PATCH 05/14] cellGem: Fix camera image when loading savestates --- rpcs3/Emu/Cell/Modules/cellCamera.cpp | 15 +++++++++++++++ rpcs3/Emu/Cell/Modules/cellCamera.h | 7 +++++++ rpcs3/Emu/Cell/Modules/cellGem.cpp | 27 +++++++++++++++++++++++++-- rpcs3/Emu/savestate_utils.cpp | 4 ++-- 4 files changed, 49 insertions(+), 4 deletions(-) diff --git a/rpcs3/Emu/Cell/Modules/cellCamera.cpp b/rpcs3/Emu/Cell/Modules/cellCamera.cpp index e8a6b656d7..4d6bb038d0 100644 --- a/rpcs3/Emu/Cell/Modules/cellCamera.cpp +++ b/rpcs3/Emu/Cell/Modules/cellCamera.cpp @@ -1961,3 +1961,18 @@ bool camera_context::on_handler_state(camera_handler_base::camera_handler_state return true; } + +gem_camera_shared::gem_camera_shared(utils::serial& ar) +{ + save(ar); +} + +void gem_camera_shared::save(utils::serial& ar) +{ + const s32 version = GET_OR_USE_SERIALIZATION_VERSION(ar.is_writing(), cellCamera); + + if (ar.is_writing() || version >= 2) + { + ar(frame_timestamp_us, width, height, size, format); + } +} diff --git a/rpcs3/Emu/Cell/Modules/cellCamera.h b/rpcs3/Emu/Cell/Modules/cellCamera.h index ae4786cdcc..a6ce62d334 100644 --- a/rpcs3/Emu/Cell/Modules/cellCamera.h +++ b/rpcs3/Emu/Cell/Modules/cellCamera.h @@ -471,6 +471,13 @@ using camera_thread = named_thread; /// Shared data between cellGem and cellCamera struct gem_camera_shared { + gem_camera_shared() {} + gem_camera_shared(utils::serial& ar); + + void save(utils::serial& ar); + + SAVESTATE_INIT_POS(7); + atomic_t frame_timestamp_us{}; // latest read timestamp from cellCamera (cellCameraRead(Ex)) atomic_t width{640}; atomic_t height{480}; diff --git a/rpcs3/Emu/Cell/Modules/cellGem.cpp b/rpcs3/Emu/Cell/Modules/cellGem.cpp index c66c79577f..5c32a299b3 100644 --- a/rpcs3/Emu/Cell/Modules/cellGem.cpp +++ b/rpcs3/Emu/Cell/Modules/cellGem.cpp @@ -292,6 +292,12 @@ public: bool wait_for_result(ppu_thread& ppu) { + // Notify gem thread that the initial state after loading a savestate can be updated. + if (m_done.compare_and_swap_test(2, 0)) + { + m_done.notify_one(); + } + while (!m_done && !ppu.is_stopped()) { thread_ctrl::wait_on(m_done, 0); @@ -540,7 +546,7 @@ public: load_configs(); }; - SAVESTATE_INIT_POS(15); + SAVESTATE_INIT_POS(16.1); // Depends on cellCamera void save(utils::serial& ar) { @@ -581,6 +587,11 @@ public: } ar(connected_controllers, updating, camera_frame, memory_ptr, start_timestamp_us); + + if (ar.is_writing() || version >= 3) + { + ar(video_conversion_in_progress, video_data_out_size); + } } gem_config_data(utils::serial& ar) @@ -1253,6 +1264,18 @@ void gem_config_data::operator()() u64 last_update_us = 0; + // Handle initial state after loading a savestate + if (state && video_conversion_in_progress) + { + // Wait for cellGemConvertVideoFinish. The initial savestate loading may take a while. + m_done = 2; // Use special value 2 for this case + thread_ctrl::wait_on(m_done, 2, 5'000'000); + + // Just mark this conversion as complete (there's no real downside to this, except for a black image) + video_conversion_in_progress = false; + done(); + } + while (thread_ctrl::state() != thread_state::aborting && !Emu.IsStopped()) { u64 timeout = umax; @@ -2646,7 +2669,7 @@ error_code cellGemGetImageState(u32 gem_num, vm::ptr gem_imag if (g_cfg.io.move != move_handler::null) { - auto& shared_data = g_fxo->get(); + const auto& shared_data = g_fxo->get(); auto& controller = gem.controllers[gem_num]; gem_image_state->frame_timestamp = shared_data.frame_timestamp_us.load(); diff --git a/rpcs3/Emu/savestate_utils.cpp b/rpcs3/Emu/savestate_utils.cpp index a40cda95c3..bb3e440564 100644 --- a/rpcs3/Emu/savestate_utils.cpp +++ b/rpcs3/Emu/savestate_utils.cpp @@ -71,8 +71,8 @@ SERIALIZATION_VER(sceNp, 11) SERIALIZATION_VER(cellVdec, 12, 1) SERIALIZATION_VER(cellAudio, 13, 1) -SERIALIZATION_VER(cellCamera, 14, 1) -SERIALIZATION_VER(cellGem, 15, 1, 2/*calibration_status_flags*/) +SERIALIZATION_VER(cellCamera, 14, 1, 2/*gem_camera_shared*/) +SERIALIZATION_VER(cellGem, 15, 1, 2/*calibration_status_flags*/, 3/*video_conversion*/) SERIALIZATION_VER(sceNpTrophy, 16, 1) SERIALIZATION_VER(cellMusic, 17, 1) SERIALIZATION_VER(cellVoice, 18, 1) From 74540844d230b597a47f46dd6e03b10d0d12e500 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Mon, 20 Jan 2025 22:55:21 +0100 Subject: [PATCH 06/14] cellGem: ignore missing pad handler This is a hacky way to fix cellGem savestates --- rpcs3/Emu/Cell/Modules/cellGem.cpp | 33 +++++++++++----------- rpcs3/Emu/Cell/Modules/cellPad.cpp | 40 +++++++++++++-------------- rpcs3/Emu/Io/Buzz.cpp | 2 +- rpcs3/Emu/Io/GHLtar.cpp | 2 +- rpcs3/Emu/Io/GameTablet.cpp | 2 +- rpcs3/Emu/Io/GunCon3.cpp | 2 +- rpcs3/Emu/Io/TopShotElite.cpp | 2 +- rpcs3/Emu/Io/TopShotFearmaster.cpp | 2 +- rpcs3/Emu/Io/Turntable.cpp | 2 +- rpcs3/Emu/Io/usio.cpp | 4 +-- rpcs3/Emu/RSX/Overlays/overlays.cpp | 2 +- rpcs3/Input/pad_thread.cpp | 6 ++-- rpcs3/Input/pad_thread.h | 10 +++---- rpcs3/rpcs3qt/pad_settings_dialog.cpp | 2 +- 14 files changed, 56 insertions(+), 55 deletions(-) diff --git a/rpcs3/Emu/Cell/Modules/cellGem.cpp b/rpcs3/Emu/Cell/Modules/cellGem.cpp index 5c32a299b3..28c5c3450b 100644 --- a/rpcs3/Emu/Cell/Modules/cellGem.cpp +++ b/rpcs3/Emu/Cell/Modules/cellGem.cpp @@ -329,7 +329,8 @@ public: connected_controllers = 0; std::lock_guard lock(pad::g_pad_mutex); - const auto handler = pad::get_current_handler(); + const auto handler = pad::get_pad_thread(true); + if (!handler) break; for (u32 i = 0; i < CELL_GEM_MAX_NUM; i++) { @@ -398,7 +399,7 @@ public: if (g_cfg.io.move == move_handler::real) { std::lock_guard pad_lock(pad::g_pad_mutex); - const auto handler = pad::get_current_handler(); + const auto handler = pad::get_pad_thread(); const auto& pad = ::at32(handler->GetPads(), pad_num(gem_num)); if (pad && pad->m_pad_handler == pad_handler::move) { @@ -428,7 +429,7 @@ public: if (g_cfg.io.move == move_handler::real) { std::lock_guard pad_lock(pad::g_pad_mutex); - const auto handler = pad::get_current_handler(); + const auto handler = pad::get_pad_thread(); const auto& pad = ::at32(handler->GetPads(), pad_num(gem_num)); if (pad && pad->m_pad_handler == pad_handler::move) { @@ -458,7 +459,7 @@ public: { connected_controllers = 0; std::lock_guard lock(pad::g_pad_mutex); - const auto handler = pad::get_current_handler(); + const auto handler = pad::get_pad_thread(); for (u32 i = 0; i < std::min(attribute.max_connect, CELL_GEM_MAX_NUM); i++) { const auto& pad = ::at32(handler->GetPads(), pad_num(i)); @@ -479,7 +480,7 @@ public: { connected_controllers = 0; std::lock_guard lock(pad::g_pad_mutex); - const auto handler = pad::get_current_handler(); + const auto handler = pad::get_pad_thread(); for (u32 i = 0; i < std::min(attribute.max_connect, CELL_GEM_MAX_NUM); i++) { const auto& pad = ::at32(handler->GetPads(), pad_num(i)); @@ -1482,7 +1483,7 @@ public: // Update PS Move LED colors { std::lock_guard lock(pad::g_pad_mutex); - const auto handler = pad::get_current_handler(); + const auto handler = pad::get_pad_thread(); auto& handlers = handler->get_handlers(); if (auto it = handlers.find(pad_handler::move); it != handlers.end() && it->second) { @@ -1744,7 +1745,7 @@ static void ds3_input_to_pad(const u32 gem_num, be_t& digital_buttons, be_t std::lock_guard lock(pad::g_pad_mutex); - const auto handler = pad::get_current_handler(); + const auto handler = pad::get_pad_thread(); const auto& pad = ::at32(handler->GetPads(), pad_num(gem_num)); if (!(pad->m_port_status & CELL_PAD_STATUS_CONNECTED)) @@ -1837,7 +1838,7 @@ static void ds3_pos_to_gem_state(u32 gem_num, gem_config::gem_controller& contro std::lock_guard lock(pad::g_pad_mutex); - const auto handler = pad::get_current_handler(); + const auto handler = pad::get_pad_thread(); const auto& pad = ::at32(handler->GetPads(), pad_num(gem_num)); if (!(pad->m_port_status & CELL_PAD_STATUS_CONNECTED)) @@ -1868,7 +1869,7 @@ static void ps_move_pos_to_gem_state(u32 gem_num, gem_config::gem_controller& co std::lock_guard lock(pad::g_pad_mutex); - const auto handler = pad::get_current_handler(); + const auto handler = pad::get_pad_thread(); const auto& pad = ::at32(handler->GetPads(), pad_num(gem_num)); if (pad->m_pad_handler != pad_handler::move || !(pad->m_port_status & CELL_PAD_STATUS_CONNECTED)) @@ -1913,7 +1914,7 @@ static void ds3_input_to_ext(u32 gem_num, gem_config::gem_controller& controller std::lock_guard lock(pad::g_pad_mutex); - const auto handler = pad::get_current_handler(); + const auto handler = pad::get_pad_thread(); const auto& pad = ::at32(handler->GetPads(), pad_num(gem_num)); if (!(pad->m_port_status & CELL_PAD_STATUS_CONNECTED)) @@ -2373,7 +2374,7 @@ error_code cellGemEnableMagnetometer(u32 gem_num, u32 enable) { std::lock_guard lock(pad::g_pad_mutex); - const auto handler = pad::get_current_handler(); + const auto handler = pad::get_pad_thread(); const auto& pad = ::at32(handler->GetPads(), pad_num(gem_num)); if (pad && pad->m_pad_handler == pad_handler::move) @@ -2421,7 +2422,7 @@ error_code cellGemEnableMagnetometer2(u32 gem_num, u32 enable) { std::lock_guard lock(pad::g_pad_mutex); - const auto handler = pad::get_current_handler(); + const auto handler = pad::get_pad_thread(); const auto& pad = ::at32(handler->GetPads(), pad_num(gem_num)); if (pad && pad->m_pad_handler == pad_handler::move) @@ -2746,7 +2747,7 @@ error_code cellGemGetInertialState(u32 gem_num, u32 state_flag, u64 timestamp, v { std::lock_guard lock(pad::g_pad_mutex); - const auto handler = pad::get_current_handler(); + const auto handler = pad::get_pad_thread(); const auto& pad = ::at32(handler->GetPads(), pad_num(gem_num)); if (pad && (pad->m_port_status & CELL_PAD_STATUS_CONNECTED)) @@ -3333,7 +3334,7 @@ error_code cellGemReadExternalPortDeviceInfo(u32 gem_num, vm::ptr ext_id, v { std::lock_guard lock(pad::g_pad_mutex); - const auto handler = pad::get_current_handler(); + const auto handler = pad::get_pad_thread(); const auto& pad = ::at32(handler->GetPads(), pad_num(gem_num)); if (pad->m_pad_handler != pad_handler::move || !(pad->m_port_status & CELL_PAD_STATUS_CONNECTED)) @@ -3417,7 +3418,7 @@ error_code cellGemSetRumble(u32 gem_num, u8 rumble) if (g_cfg.io.move == move_handler::real) { std::lock_guard pad_lock(pad::g_pad_mutex); - const auto handler = pad::get_current_handler(); + const auto handler = pad::get_pad_thread(); auto& handlers = handler->get_handlers(); if (auto it = handlers.find(pad_handler::move); it != handlers.end() && it->second) { @@ -3647,7 +3648,7 @@ error_code cellGemWriteExternalPort(u32 gem_num, vm::ptrGetPads(), pad_num(gem_num)); if (pad->m_pad_handler != pad_handler::move || !(pad->m_port_status & CELL_PAD_STATUS_CONNECTED)) diff --git a/rpcs3/Emu/Cell/Modules/cellPad.cpp b/rpcs3/Emu/Cell/Modules/cellPad.cpp index 771bb04a6e..74b3c5fc60 100644 --- a/rpcs3/Emu/Cell/Modules/cellPad.cpp +++ b/rpcs3/Emu/Cell/Modules/cellPad.cpp @@ -196,7 +196,7 @@ bool cellPad_NotifyStateChange(usz index, u64 /*state*/, bool locked, bool is_bl return true; } - const auto handler = pad::get_current_handler(); + const auto handler = pad::get_pad_thread(); const auto& pads = handler->GetPads(); const auto& pad = pads[index]; @@ -268,7 +268,7 @@ error_code cellPadInit(ppu_thread& ppu, u32 max_connect) config.port_setting.fill(CELL_PAD_SETTING_PRESS_OFF | CELL_PAD_SETTING_SENSOR_OFF); config.reported_info = {}; - const auto handler = pad::get_current_handler(); + const auto handler = pad::get_pad_thread(); const auto& pads = handler->GetPads(); for (usz i = 0; i < config.get_max_connect(); ++i) @@ -336,7 +336,7 @@ error_code cellPadClearBuf(u32 port_no) if (port_no >= config.get_max_connect()) return CELL_PAD_ERROR_NO_DEVICE; - const auto handler = pad::get_current_handler(); + const auto handler = pad::get_pad_thread(); const auto& pads = handler->GetPads(); const auto& pad = pads[port_no]; @@ -351,7 +351,7 @@ error_code cellPadClearBuf(u32 port_no) void pad_get_data(u32 port_no, CellPadData* data, bool get_periph_data = false) { auto& config = g_fxo->get(); - const auto handler = pad::get_current_handler(); + const auto handler = pad::get_pad_thread(); const auto& pad = handler->GetPads()[port_no]; const PadInfo& rinfo = handler->GetInfo(); @@ -709,7 +709,7 @@ error_code cellPadGetData(u32 port_no, vm::ptr data) if (port_no >= config.get_max_connect()) return CELL_PAD_ERROR_NO_DEVICE; - const auto handler = pad::get_current_handler(); + const auto handler = pad::get_pad_thread(); const auto& pads = handler->GetPads(); const auto& pad = pads[port_no]; @@ -740,7 +740,7 @@ error_code cellPadPeriphGetInfo(vm::ptr info) if (!info) return CELL_PAD_ERROR_INVALID_PARAMETER; - const auto handler = pad::get_current_handler(); + const auto handler = pad::get_pad_thread(); const PadInfo& rinfo = handler->GetInfo(); std::memset(info.get_ptr(), 0, sizeof(CellPadPeriphInfo)); @@ -795,7 +795,7 @@ error_code cellPadPeriphGetData(u32 port_no, vm::ptr data) if (port_no >= config.get_max_connect()) return CELL_PAD_ERROR_NO_DEVICE; - const auto handler = pad::get_current_handler(); + const auto handler = pad::get_pad_thread(); const auto& pads = handler->GetPads(); const auto& pad = pads[port_no]; @@ -827,7 +827,7 @@ error_code cellPadGetRawData(u32 port_no, vm::ptr data) if (port_no >= config.get_max_connect()) return CELL_PAD_ERROR_NO_DEVICE; - const auto handler = pad::get_current_handler(); + const auto handler = pad::get_pad_thread(); const auto& pads = handler->GetPads(); const auto& pad = pads[port_no]; @@ -891,7 +891,7 @@ error_code cellPadSetActDirect(u32 port_no, vm::ptr param) if (port_no >= config.get_max_connect()) return CELL_PAD_ERROR_NO_DEVICE; - const auto handler = pad::get_current_handler(); + const auto handler = pad::get_pad_thread(); const auto& pads = handler->GetPads(); const auto& pad = pads[port_no]; @@ -923,7 +923,7 @@ error_code cellPadGetInfo(vm::ptr info) std::memset(info.get_ptr(), 0, sizeof(CellPadInfo)); - const auto handler = pad::get_current_handler(); + const auto handler = pad::get_pad_thread(); const PadInfo& rinfo = handler->GetInfo(); info->max_connect = config.max_connect; info->system_info = rinfo.system_info; @@ -968,7 +968,7 @@ error_code cellPadGetInfo2(vm::ptr info) std::memset(info.get_ptr(), 0, sizeof(CellPadInfo2)); - const auto handler = pad::get_current_handler(); + const auto handler = pad::get_pad_thread(); const PadInfo& rinfo = handler->GetInfo(); info->max_connect = config.get_max_connect(); // Here it is forcibly clamped info->system_info = rinfo.system_info; @@ -1018,7 +1018,7 @@ error_code cellPadGetCapabilityInfo(u32 port_no, vm::ptr if (port_no >= config.get_max_connect()) return CELL_PAD_ERROR_NO_DEVICE; - const auto handler = pad::get_current_handler(); + const auto handler = pad::get_pad_thread(); const auto& pads = handler->GetPads(); const auto& pad = pads[port_no]; @@ -1074,7 +1074,7 @@ error_code cellPadInfoPressMode(u32 port_no) if (port_no >= config.get_max_connect()) return CELL_PAD_ERROR_NO_DEVICE; - const auto handler = pad::get_current_handler(); + const auto handler = pad::get_pad_thread(); const auto& pads = handler->GetPads(); const auto& pad = pads[port_no]; @@ -1101,7 +1101,7 @@ error_code cellPadInfoSensorMode(u32 port_no) if (port_no >= config.get_max_connect()) return CELL_PAD_ERROR_NO_DEVICE; - const auto handler = pad::get_current_handler(); + const auto handler = pad::get_pad_thread(); const auto& pads = handler->GetPads(); const auto& pad = pads[port_no]; @@ -1129,7 +1129,7 @@ error_code cellPadSetPressMode(u32 port_no, u32 mode) if (port_no >= CELL_PAD_MAX_PORT_NUM) return CELL_OK; - const auto handler = pad::get_current_handler(); + const auto handler = pad::get_pad_thread(); const auto& pads = handler->GetPads(); const auto& pad = pads[port_no]; @@ -1163,7 +1163,7 @@ error_code cellPadSetSensorMode(u32 port_no, u32 mode) if (port_no >= CELL_PAD_MAX_PORT_NUM) return CELL_OK; - const auto handler = pad::get_current_handler(); + const auto handler = pad::get_pad_thread(); const auto& pads = handler->GetPads(); const auto& pad = pads[port_no]; @@ -1190,7 +1190,7 @@ error_code cellPadLddRegisterController() if (!config.max_connect) return CELL_PAD_ERROR_UNINITIALIZED; - const auto handler = pad::get_current_handler(); + const auto handler = pad::get_pad_thread(); const s32 handle = handler->AddLddPad(); @@ -1215,7 +1215,7 @@ error_code cellPadLddDataInsert(s32 handle, vm::ptr data) if (!config.max_connect) return CELL_PAD_ERROR_UNINITIALIZED; - const auto handler = pad::get_current_handler(); + const auto handler = pad::get_pad_thread(); auto& pads = handler->GetPads(); if (handle < 0 || static_cast(handle) >= pads.size() || !data) // data == NULL stalls on decr @@ -1240,7 +1240,7 @@ error_code cellPadLddGetPortNo(s32 handle) if (!config.max_connect) return CELL_PAD_ERROR_UNINITIALIZED; - const auto handler = pad::get_current_handler(); + const auto handler = pad::get_pad_thread(); auto& pads = handler->GetPads(); if (handle < 0 || static_cast(handle) >= pads.size()) @@ -1264,7 +1264,7 @@ error_code cellPadLddUnregisterController(s32 handle) if (!config.max_connect) return CELL_PAD_ERROR_UNINITIALIZED; - const auto handler = pad::get_current_handler(); + const auto handler = pad::get_pad_thread(); const auto& pads = handler->GetPads(); if (handle < 0 || static_cast(handle) >= pads.size()) diff --git a/rpcs3/Emu/Io/Buzz.cpp b/rpcs3/Emu/Io/Buzz.cpp index 108bdec0ab..575c5585b1 100644 --- a/rpcs3/Emu/Io/Buzz.cpp +++ b/rpcs3/Emu/Io/Buzz.cpp @@ -146,7 +146,7 @@ void usb_device_buzz::interrupt_transfer(u32 buf_size, u8* buf, u32 /*endpoint*/ } std::lock_guard lock(pad::g_pad_mutex); - const auto handler = pad::get_current_handler(); + const auto handler = pad::get_pad_thread(); const auto& pads = handler->GetPads(); ensure(pads.size() > m_last_controller); ensure(g_cfg_buzz.players.size() > m_last_controller); diff --git a/rpcs3/Emu/Io/GHLtar.cpp b/rpcs3/Emu/Io/GHLtar.cpp index d6fe87e9fc..db60cccd5b 100644 --- a/rpcs3/Emu/Io/GHLtar.cpp +++ b/rpcs3/Emu/Io/GHLtar.cpp @@ -138,7 +138,7 @@ void usb_device_ghltar::interrupt_transfer(u32 buf_size, u8* buf, u32 /*endpoint } std::lock_guard lock(pad::g_pad_mutex); - const auto handler = pad::get_current_handler(); + const auto handler = pad::get_pad_thread(); const auto& pad = ::at32(handler->GetPads(), m_controller_index); if (!(pad->m_port_status & CELL_PAD_STATUS_CONNECTED)) diff --git a/rpcs3/Emu/Io/GameTablet.cpp b/rpcs3/Emu/Io/GameTablet.cpp index 56286a8bff..31ed81f3af 100644 --- a/rpcs3/Emu/Io/GameTablet.cpp +++ b/rpcs3/Emu/Io/GameTablet.cpp @@ -195,7 +195,7 @@ void usb_device_gametablet::interrupt_transfer(u32 buf_size, u8* buf, u32 /*endp { std::lock_guard lock(pad::g_pad_mutex); - const auto gamepad_handler = pad::get_current_handler(); + const auto gamepad_handler = pad::get_pad_thread(); const auto& pads = gamepad_handler->GetPads(); const auto& pad = ::at32(pads, m_controller_index); if (pad->m_port_status & CELL_PAD_STATUS_CONNECTED) diff --git a/rpcs3/Emu/Io/GunCon3.cpp b/rpcs3/Emu/Io/GunCon3.cpp index 7eef591ecc..d229693907 100644 --- a/rpcs3/Emu/Io/GunCon3.cpp +++ b/rpcs3/Emu/Io/GunCon3.cpp @@ -255,7 +255,7 @@ void usb_device_guncon3::interrupt_transfer(u32 buf_size, u8* buf, u32 endpoint, { std::lock_guard lock(pad::g_pad_mutex); - const auto gamepad_handler = pad::get_current_handler(); + const auto gamepad_handler = pad::get_pad_thread(); const auto& pads = gamepad_handler->GetPads(); const auto& pad = ::at32(pads, m_controller_index); if (pad->m_port_status & CELL_PAD_STATUS_CONNECTED) diff --git a/rpcs3/Emu/Io/TopShotElite.cpp b/rpcs3/Emu/Io/TopShotElite.cpp index b170027a9f..06cddab9f0 100644 --- a/rpcs3/Emu/Io/TopShotElite.cpp +++ b/rpcs3/Emu/Io/TopShotElite.cpp @@ -315,7 +315,7 @@ void usb_device_topshotelite::interrupt_transfer(u32 buf_size, u8* buf, u32 /*en { std::lock_guard lock(pad::g_pad_mutex); - const auto gamepad_handler = pad::get_current_handler(); + const auto gamepad_handler = pad::get_pad_thread(); const auto& pads = gamepad_handler->GetPads(); const auto& pad = ::at32(pads, m_controller_index); if (pad->m_port_status & CELL_PAD_STATUS_CONNECTED) diff --git a/rpcs3/Emu/Io/TopShotFearmaster.cpp b/rpcs3/Emu/Io/TopShotFearmaster.cpp index aba831fa9c..98b54700d9 100644 --- a/rpcs3/Emu/Io/TopShotFearmaster.cpp +++ b/rpcs3/Emu/Io/TopShotFearmaster.cpp @@ -339,7 +339,7 @@ void usb_device_topshotfearmaster::interrupt_transfer(u32 buf_size, u8* buf, u32 { std::lock_guard lock(pad::g_pad_mutex); - const auto gamepad_handler = pad::get_current_handler(); + const auto gamepad_handler = pad::get_pad_thread(); const auto& pads = gamepad_handler->GetPads(); const auto& pad = ::at32(pads, m_controller_index); if (pad->m_port_status & CELL_PAD_STATUS_CONNECTED) diff --git a/rpcs3/Emu/Io/Turntable.cpp b/rpcs3/Emu/Io/Turntable.cpp index 63bb815a1d..0ef979725e 100644 --- a/rpcs3/Emu/Io/Turntable.cpp +++ b/rpcs3/Emu/Io/Turntable.cpp @@ -151,7 +151,7 @@ void usb_device_turntable::interrupt_transfer(u32 buf_size, u8* buf, u32 /*endpo // All other bufs are always 0x00 std::lock_guard lock(pad::g_pad_mutex); - const auto handler = pad::get_current_handler(); + const auto handler = pad::get_pad_thread(); const auto& pads = handler->GetPads(); const auto& pad = ::at32(pads, m_controller_index); diff --git a/rpcs3/Emu/Io/usio.cpp b/rpcs3/Emu/Io/usio.cpp index faecec177d..077f6c7d62 100644 --- a/rpcs3/Emu/Io/usio.cpp +++ b/rpcs3/Emu/Io/usio.cpp @@ -189,7 +189,7 @@ void usb_device_usio::save_backup() void usb_device_usio::translate_input_taiko() { std::lock_guard lock(pad::g_pad_mutex); - const auto handler = pad::get_current_handler(); + const auto handler = pad::get_pad_thread(); std::vector input_buf(0x60); constexpr le_t c_hit = 0x1800; @@ -273,7 +273,7 @@ void usb_device_usio::translate_input_taiko() void usb_device_usio::translate_input_tekken() { std::lock_guard lock(pad::g_pad_mutex); - const auto handler = pad::get_current_handler(); + const auto handler = pad::get_pad_thread(); std::vector input_buf(0x180); le_t digital_input[2]{}; diff --git a/rpcs3/Emu/RSX/Overlays/overlays.cpp b/rpcs3/Emu/RSX/Overlays/overlays.cpp index e118b3a68c..a1fe40d8ce 100644 --- a/rpcs3/Emu/RSX/Overlays/overlays.cpp +++ b/rpcs3/Emu/RSX/Overlays/overlays.cpp @@ -193,7 +193,7 @@ namespace rsx // Get gamepad input std::lock_guard lock(pad::g_pad_mutex); - const auto handler = pad::get_current_handler(); + const auto handler = pad::get_pad_thread(); const PadInfo& rinfo = handler->GetInfo(); const bool ignore_gamepad_input = (!rinfo.now_connect || !input::g_pads_intercepted); diff --git a/rpcs3/Input/pad_thread.cpp b/rpcs3/Input/pad_thread.cpp index 0c207d6595..70f0e25618 100644 --- a/rpcs3/Input/pad_thread.cpp +++ b/rpcs3/Input/pad_thread.cpp @@ -37,7 +37,7 @@ extern std::string g_input_config_override; namespace pad { - atomic_t g_current = nullptr; + atomic_t g_pad_thread = nullptr; shared_mutex g_pad_mutex; std::string g_title_id; atomic_t g_started{false}; @@ -62,13 +62,13 @@ struct pad_setting pad_thread::pad_thread(void* curthread, void* curwindow, std::string_view title_id) : m_curthread(curthread), m_curwindow(curwindow) { pad::g_title_id = title_id; - pad::g_current = this; + pad::g_pad_thread = this; pad::g_started = false; } pad_thread::~pad_thread() { - pad::g_current = nullptr; + pad::g_pad_thread = nullptr; } void pad_thread::Init() diff --git a/rpcs3/Input/pad_thread.h b/rpcs3/Input/pad_thread.h index fe9fa015c5..1939ce0104 100644 --- a/rpcs3/Input/pad_thread.h +++ b/rpcs3/Input/pad_thread.h @@ -70,7 +70,7 @@ private: namespace pad { - extern atomic_t g_current; + extern atomic_t g_pad_thread; extern shared_mutex g_pad_mutex; extern std::string g_title_id; extern atomic_t g_enabled; @@ -78,14 +78,14 @@ namespace pad extern atomic_t g_started; extern atomic_t g_home_menu_requested; - static inline class pad_thread* get_current_handler(bool relaxed = false) + static inline class pad_thread* get_pad_thread(bool relaxed = false) { if (relaxed) { - return g_current.observe(); + return g_pad_thread.observe(); } - return ensure(g_current.load()); + return ensure(g_pad_thread.load()); } static inline void set_enabled(bool enabled) @@ -102,7 +102,7 @@ namespace pad static inline void SetIntercepted(bool intercepted) { std::lock_guard lock(g_pad_mutex); - const auto handler = get_current_handler(); + const auto handler = get_pad_thread(); handler->SetIntercepted(intercepted); } } diff --git a/rpcs3/rpcs3qt/pad_settings_dialog.cpp b/rpcs3/rpcs3qt/pad_settings_dialog.cpp index 01904e5048..2a2194d1b6 100644 --- a/rpcs3/rpcs3qt/pad_settings_dialog.cpp +++ b/rpcs3/rpcs3qt/pad_settings_dialog.cpp @@ -2017,7 +2017,7 @@ bool pad_settings_dialog::GetIsLddPad(u32 index) const if (!Emu.IsStopped() && (m_title_id.empty() || m_title_id == Emu.GetTitleID())) { std::lock_guard lock(pad::g_pad_mutex); - if (const auto handler = pad::get_current_handler(true)) + if (const auto handler = pad::get_pad_thread(true)) { ensure(index < handler->GetPads().size()); From dc700ad620f9ec6e932acc45502f3fee38eab77b Mon Sep 17 00:00:00 2001 From: Megamouse Date: Tue, 21 Jan 2025 05:31:22 +0100 Subject: [PATCH 07/14] cellCamera: close Qt camera on error (e.g. when usb is pulled) --- rpcs3/rpcs3qt/qt_camera_handler.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/rpcs3/rpcs3qt/qt_camera_handler.cpp b/rpcs3/rpcs3qt/qt_camera_handler.cpp index 88d8c15963..4703cf12b6 100644 --- a/rpcs3/rpcs3qt/qt_camera_handler.cpp +++ b/rpcs3/rpcs3qt/qt_camera_handler.cpp @@ -113,6 +113,7 @@ void qt_camera_handler::handle_camera_active(bool is_active) void qt_camera_handler::handle_camera_error(QCamera::Error error, const QString& errorString) { camera_log.error("Error event: \"%s\" (error=%d)", errorString, static_cast(error)); + set_state(camera_handler_state::closed); } void qt_camera_handler::open_camera() From fcac6a93226fa4367f95ca6a779d1926c3338930 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Tue, 21 Jan 2025 05:32:33 +0100 Subject: [PATCH 08/14] sys_event: remove unnecessary copy --- rpcs3/Emu/Cell/lv2/sys_event.cpp | 4 +--- rpcs3/Emu/Cell/lv2/sys_process.cpp | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/rpcs3/Emu/Cell/lv2/sys_event.cpp b/rpcs3/Emu/Cell/lv2/sys_event.cpp index d5ad126142..db72972b91 100644 --- a/rpcs3/Emu/Cell/lv2/sys_event.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_event.cpp @@ -420,10 +420,8 @@ error_code sys_event_queue_tryreceive(ppu_thread& ppu, u32 equeue_id, vm::ptrevents.empty()) { auto& dest = events[count++]; - const auto event = queue->events.front(); + std::tie(dest.source, dest.data1, dest.data2, dest.data3) = queue->events.front(); queue->events.pop_front(); - - std::tie(dest.source, dest.data1, dest.data2, dest.data3) = event; } lock.unlock(); diff --git a/rpcs3/Emu/Cell/lv2/sys_process.cpp b/rpcs3/Emu/Cell/lv2/sys_process.cpp index 45eb5c1858..b914408ec9 100644 --- a/rpcs3/Emu/Cell/lv2/sys_process.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_process.cpp @@ -271,7 +271,7 @@ error_code _sys_process_get_paramsfo(vm::ptr buffer) { sys_process.warning("_sys_process_get_paramsfo(buffer=0x%x)", buffer); - if (!Emu.GetTitleID().length()) + if (Emu.GetTitleID().empty()) { return CELL_ENOENT; } From fbc7ad6788d772c76613692a6eba390739a6a83d Mon Sep 17 00:00:00 2001 From: Megamouse Date: Tue, 21 Jan 2025 05:34:16 +0100 Subject: [PATCH 09/14] cellGem: move some locks around wait loops --- rpcs3/Emu/Cell/Modules/cellGem.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/rpcs3/Emu/Cell/Modules/cellGem.cpp b/rpcs3/Emu/Cell/Modules/cellGem.cpp index 28c5c3450b..8475b57b04 100644 --- a/rpcs3/Emu/Cell/Modules/cellGem.cpp +++ b/rpcs3/Emu/Cell/Modules/cellGem.cpp @@ -2440,7 +2440,7 @@ error_code cellGemEnd(ppu_thread& ppu) auto& gem = g_fxo->get(); - std::scoped_lock lock(gem.mtx); + std::unique_lock lock(gem.mtx); if (gem.state.compare_and_swap_test(1, 0)) { @@ -2452,6 +2452,8 @@ error_code cellGemEnd(ppu_thread& ppu) return CELL_OK; } + lock.unlock(); + auto& tracker = g_fxo->get>(); if (!tracker.wait_for_tracker_result(ppu)) { @@ -2488,7 +2490,7 @@ error_code cellGemFilterState(u32 gem_num, u32 enable) error_code cellGemForceRGB(u32 gem_num, f32 r, f32 g, f32 b) { - cellGem.todo("cellGemForceRGB(gem_num=%d, r=%f, g=%f, b=%f)", gem_num, r, g, b); + cellGem.warning("cellGemForceRGB(gem_num=%d, r=%f, g=%f, b=%f)", gem_num, r, g, b); auto& gem = g_fxo->get(); @@ -3557,8 +3559,6 @@ error_code cellGemUpdateFinish(ppu_thread& ppu) return CELL_GEM_ERROR_UNINITIALIZED; } - std::scoped_lock lock(gem.mtx); - if (!gem.updating) { return CELL_GEM_ERROR_UPDATE_NOT_STARTED; @@ -3570,6 +3570,8 @@ error_code cellGemUpdateFinish(ppu_thread& ppu) return {}; } + std::scoped_lock lock(gem.mtx); + gem.updating = false; if (!gem.camera_frame) From 79d79aa80cf3265e30bab3cbffea08ad48add26c Mon Sep 17 00:00:00 2001 From: Megamouse Date: Tue, 21 Jan 2025 05:45:08 +0100 Subject: [PATCH 10/14] cellCamera: move attach event to thread Time crisis doesn't seem to like the immediate push to the queue. --- rpcs3/Emu/Cell/Modules/cellCamera.cpp | 67 ++++++++++++++++----------- rpcs3/Emu/Cell/Modules/cellCamera.h | 1 + 2 files changed, 40 insertions(+), 28 deletions(-) diff --git a/rpcs3/Emu/Cell/Modules/cellCamera.cpp b/rpcs3/Emu/Cell/Modules/cellCamera.cpp index 4d6bb038d0..95cf0a56f9 100644 --- a/rpcs3/Emu/Cell/Modules/cellCamera.cpp +++ b/rpcs3/Emu/Cell/Modules/cellCamera.cpp @@ -148,10 +148,15 @@ void camera_context::save(utils::serial& ar) return; } - GET_OR_USE_SERIALIZATION_VERSION(ar.is_writing(), cellCamera); + const s32 version = GET_OR_USE_SERIALIZATION_VERSION(ar.is_writing(), cellCamera); ar(notify_data_map, start_timestamp_us, read_mode, is_streaming, is_attached, is_open, info, attr, frame_num); + if (ar.is_writing() || version >= 2) + { + ar(is_attached_dirty); + } + if (!ar.is_writing()) { if (is_open) @@ -168,6 +173,21 @@ void camera_context::save(utils::serial& ar) } } +gem_camera_shared::gem_camera_shared(utils::serial& ar) +{ + save(ar); +} + +void gem_camera_shared::save(utils::serial& ar) +{ + const s32 version = GET_OR_USE_SERIALIZATION_VERSION(ar.is_writing(), cellCamera); + + if (ar.is_writing() || version >= 2) + { + ar(frame_timestamp_us, width, height, size, format); + } +} + static bool check_dev_num(s32 dev_num) { return dev_num == 0; @@ -450,7 +470,6 @@ error_code cellCameraInit() g_camera.attr[CELL_CAMERA_USBLOAD] = { 4 }; break; } - case fake_camera_type::eyetoy2: { g_camera.attr[CELL_CAMERA_SATURATION] = { 64 }; @@ -470,7 +489,6 @@ error_code cellCameraInit() g_camera.attr[CELL_CAMERA_AGCHIGH] = { 64 }; break; } - case fake_camera_type::uvc1_1: { g_camera.attr[CELL_CAMERA_DEVICEID] = { 0x5ca, 0x18d0 }; // KBCR-S01MU @@ -478,14 +496,14 @@ error_code cellCameraInit() g_camera.attr[CELL_CAMERA_NUMFRAME] = { 1 }; // Amount of supported resolutions break; } - default: cellCamera.todo("Trying to init cellCamera with un-researched camera type."); + break; } // TODO: Some other default attributes? Need to check the actual behaviour on a real PS3. - g_camera.is_attached = true; + g_camera.is_attached = g_cfg.io.camera != camera_handler::null; g_camera.init = 1; return CELL_OK; } @@ -831,8 +849,8 @@ s32 cellCameraIsAttached(s32 dev_num) // normally should be attached immediately after event queue is registered, but just to be sure if (!is_attached) { - g_camera.send_attach_state(true); - is_attached = g_camera.is_attached; + g_camera.is_attached = is_attached = true; + g_camera.is_attached_dirty = true; } } @@ -1621,9 +1639,15 @@ void camera_context::operator()() { while (thread_ctrl::state() != thread_state::aborting && !Emu.IsStopped()) { + // send ATTACH event + if (init && is_attached_dirty && !Emu.IsPaused()) + { + send_attach_state(is_attached); + } + const s32 fps = info.framerate; - if (!fps || Emu.IsPaused() || g_cfg.io.camera == camera_handler::null) + if (!init || !fps || Emu.IsPaused() || g_cfg.io.camera == camera_handler::null) { thread_ctrl::wait_for(1000); // hack continue; @@ -1798,6 +1822,7 @@ void camera_context::reset_state() read_mode = CELL_CAMERA_READ_FUNCCALL; is_streaming = false; is_attached = false; + is_attached_dirty = false; is_open = false; info.framerate = 0; std::memset(&attr, 0, sizeof(attr)); @@ -1843,6 +1868,7 @@ void camera_context::send_attach_state(bool attached) // We're not expected to send any events for attaching/detaching is_attached = attached; + is_attached_dirty = false; } void camera_context::set_attr(s32 attrib, u32 arg1, u32 arg2) @@ -1877,15 +1903,13 @@ void camera_context::set_attr(s32 attrib, u32 arg1, u32 arg2) void camera_context::add_queue(u64 key, u64 source, u64 flag) { - std::lock_guard lock(mutex); { std::lock_guard lock_data_map(mutex_notify_data_map); notify_data_map[key] = { source, flag }; } - // send ATTACH event - HACKY - send_attach_state(is_attached); + is_attached_dirty = true; } void camera_context::remove_queue(u64 key) @@ -1912,7 +1936,8 @@ bool camera_context::on_handler_state(camera_handler_base::camera_handler_state { if (is_attached) { - send_attach_state(false); + is_attached = false; + is_attached_dirty = true; } if (handler) { @@ -1953,7 +1978,8 @@ bool camera_context::on_handler_state(camera_handler_base::camera_handler_state if (!is_attached) { cellCamera.warning("Camera handler not attached. Sending attach event...", static_cast(state)); - send_attach_state(true); + is_attached = true; + is_attached_dirty = true; } break; } @@ -1961,18 +1987,3 @@ bool camera_context::on_handler_state(camera_handler_base::camera_handler_state return true; } - -gem_camera_shared::gem_camera_shared(utils::serial& ar) -{ - save(ar); -} - -void gem_camera_shared::save(utils::serial& ar) -{ - const s32 version = GET_OR_USE_SERIALIZATION_VERSION(ar.is_writing(), cellCamera); - - if (ar.is_writing() || version >= 2) - { - ar(frame_timestamp_us, width, height, size, format); - } -} diff --git a/rpcs3/Emu/Cell/Modules/cellCamera.h b/rpcs3/Emu/Cell/Modules/cellCamera.h index a6ce62d334..28e8a3152c 100644 --- a/rpcs3/Emu/Cell/Modules/cellCamera.h +++ b/rpcs3/Emu/Cell/Modules/cellCamera.h @@ -427,6 +427,7 @@ public: atomic_t read_mode{CELL_CAMERA_READ_FUNCCALL}; atomic_t is_streaming{false}; atomic_t is_attached{false}; + atomic_t is_attached_dirty{false}; atomic_t is_open{false}; CellCameraInfoEx info{}; From 6fddb31a078cde1849626aa74cc1b45c6cd29a88 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Wed, 22 Jan 2025 18:53:45 +0100 Subject: [PATCH 11/14] Qt/input: reset gui pad thread when saving pad settings --- rpcs3/Input/gui_pad_thread.cpp | 22 +++++++++++++++++----- rpcs3/Input/gui_pad_thread.h | 6 ++++++ rpcs3/rpcs3qt/pad_settings_dialog.cpp | 3 +++ 3 files changed, 26 insertions(+), 5 deletions(-) diff --git a/rpcs3/Input/gui_pad_thread.cpp b/rpcs3/Input/gui_pad_thread.cpp index 87fa0999f3..7b69b24209 100644 --- a/rpcs3/Input/gui_pad_thread.cpp +++ b/rpcs3/Input/gui_pad_thread.cpp @@ -40,6 +40,8 @@ LOG_CHANNEL(gui_log, "GUI"); +atomic_t gui_pad_thread::m_reset = false; + gui_pad_thread::gui_pad_thread() { m_thread = std::make_unique(&gui_pad_thread::run, this); @@ -145,6 +147,11 @@ bool gui_pad_thread::init() gui_log.notice("gui_pad_thread: Pad %d: device='%s', handler=%s, VID=0x%x, PID=0x%x, class_type=0x%x, class_profile=0x%x", i, cfg->device.to_string(), m_pad->m_pad_handler, m_pad->m_vendor_id, m_pad->m_product_id, m_pad->m_class_type, m_pad->m_class_profile); + if (handler_type != pad_handler::null) + { + input_log.notice("gui_pad_thread %d: config=\n%s", i, cfg->to_string()); + } + // We only use one pad break; } @@ -251,14 +258,19 @@ void gui_pad_thread::run() gui_log.notice("gui_pad_thread: Pad thread started"); - if (!init()) - { - gui_log.warning("gui_pad_thread: Pad thread stopped (init failed)"); - return; - } + m_reset = true; while (!m_terminate) { + if (m_reset && m_reset.exchange(false)) + { + if (!init()) + { + gui_log.warning("gui_pad_thread: Pad thread stopped (init failed during reset)"); + return; + } + } + // Only process input if there is an active window if (m_handler && m_pad && (m_allow_global_input || QApplication::activeWindow())) { diff --git a/rpcs3/Input/gui_pad_thread.h b/rpcs3/Input/gui_pad_thread.h index 4bc0414200..235d97e8b1 100644 --- a/rpcs3/Input/gui_pad_thread.h +++ b/rpcs3/Input/gui_pad_thread.h @@ -23,6 +23,11 @@ public: static std::shared_ptr GetHandler(pad_handler type); static void InitPadConfig(cfg_pad& cfg, pad_handler type, std::shared_ptr& handler); + static void reset() + { + m_reset = true; + } + protected: bool init(); void run(); @@ -60,6 +65,7 @@ protected: std::unique_ptr m_thread; atomic_t m_terminate = false; atomic_t m_allow_global_input = false; + static atomic_t m_reset; std::array(pad_button::pad_button_max_enum)> m_last_button_state{}; diff --git a/rpcs3/rpcs3qt/pad_settings_dialog.cpp b/rpcs3/rpcs3qt/pad_settings_dialog.cpp index 2a2194d1b6..9591514dc3 100644 --- a/rpcs3/rpcs3qt/pad_settings_dialog.cpp +++ b/rpcs3/rpcs3qt/pad_settings_dialog.cpp @@ -20,6 +20,7 @@ #include "Utilities/File.h" #include "Input/pad_thread.h" +#include "Input/gui_pad_thread.h" #include "Input/product_info.h" #include "Input/keyboard_pad_handler.h" @@ -245,6 +246,8 @@ pad_settings_dialog::~pad_settings_dialog() *m_input_thread = thread_state::finished; } + gui_pad_thread::reset(); + if (!Emu.IsStopped()) { pad::reset(Emu.GetTitleID()); From b30ea34d056753de126ed6988255f7e03df37eb3 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Fri, 17 Jan 2025 00:32:37 +0100 Subject: [PATCH 12/14] cellGem: mark device as not visible at the screen borders --- rpcs3/Emu/Cell/Modules/cellGem.cpp | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/rpcs3/Emu/Cell/Modules/cellGem.cpp b/rpcs3/Emu/Cell/Modules/cellGem.cpp index 8475b57b04..6eb68e49e1 100644 --- a/rpcs3/Emu/Cell/Modules/cellGem.cpp +++ b/rpcs3/Emu/Cell/Modules/cellGem.cpp @@ -247,7 +247,7 @@ public: u32 hue = 0; // Tracking hue of the motion controller f32 distance_mm{3000.0f}; // Distance from the camera in mm f32 radius{5.0f}; // Radius of the sphere in camera pixels - bool radius_valid = true; // If the radius and distance of the sphere was computed. + bool radius_valid = false; // If the radius and distance of the sphere was computed. Also used for visibility. bool is_calibrating{false}; // Whether or not we are currently calibrating u64 calibration_start_us{0}; // The start timestamp of the calibration in microseconds @@ -1598,7 +1598,7 @@ static inline void draw_overlay_cursor(u32 gem_num, const gem_config::gem_contro rsx::overlays::set_cursor(rsx::overlays::cursor_offset::cell_gem + gem_num, x, y, color, 2'000'000, false); } -static inline void pos_to_gem_image_state(u32 gem_num, const gem_config::gem_controller& controller, vm::ptr& gem_image_state, s32 x_pos, s32 y_pos, s32 x_max, s32 y_max) +static inline void pos_to_gem_image_state(u32 gem_num, gem_config::gem_controller& controller, vm::ptr& gem_image_state, s32 x_pos, s32 y_pos, s32 x_max, s32 y_max) { const auto& shared_data = g_fxo->get(); @@ -1629,6 +1629,13 @@ static inline void pos_to_gem_image_state(u32 gem_num, const gem_config::gem_con gem_image_state->projectionx = camera_x / controller.distance_mm; gem_image_state->projectiony = camera_y / controller.distance_mm; + // Update visibility for fake handlers + if (g_cfg.io.move != move_handler::real) + { + // Let's say the sphere is not visible if the position is at the edge of the screen + controller.radius_valid = x_pos > 0 && x_pos < x_max && y_pos > 0 && y_pos < y_max; + } + if (g_cfg.io.show_move_cursor) { draw_overlay_cursor(gem_num, controller, x_pos, y_pos, x_max, y_max); @@ -1712,6 +1719,13 @@ static inline void pos_to_gem_state(u32 gem_num, gem_config::gem_controller& con gem_state->quat[3] = q_w; } + // Update visibility for fake handlers + if (g_cfg.io.move != move_handler::real) + { + // Let's say the sphere is not visible if the position is at the edge of the screen + controller.radius_valid = x_pos > 0 && x_pos < x_max && y_pos > 0 && y_pos < y_max; + } + if (g_cfg.io.show_move_cursor) { draw_overlay_cursor(gem_num, controller, x_pos, y_pos, x_max, y_max); @@ -2657,6 +2671,7 @@ error_code cellGemGetImageState(u32 gem_num, vm::ptr gem_imag cellGem.warning("cellGemGetImageState(gem_num=%d, image_state=&0x%x)", gem_num, gem_image_state); auto& gem = g_fxo->get(); + std::scoped_lock lock(gem.mtx); if (!gem.state) { @@ -2677,10 +2692,6 @@ error_code cellGemGetImageState(u32 gem_num, vm::ptr gem_imag gem_image_state->frame_timestamp = shared_data.frame_timestamp_us.load(); gem_image_state->timestamp = gem_image_state->frame_timestamp + 10; - gem_image_state->r = controller.radius; // Radius in camera pixels - gem_image_state->distance = controller.distance_mm; - gem_image_state->visible = gem.is_controller_ready(gem_num); - gem_image_state->r_valid = controller.radius_valid; switch (g_cfg.io.move) { @@ -2702,6 +2713,11 @@ error_code cellGemGetImageState(u32 gem_num, vm::ptr gem_imag case move_handler::null: fmt::throw_exception("Unreachable"); } + + gem_image_state->r = controller.radius; // Radius in camera pixels + gem_image_state->distance = controller.distance_mm; + gem_image_state->visible = controller.radius_valid && gem.is_controller_ready(gem_num); + gem_image_state->r_valid = controller.radius_valid; } return CELL_OK; From f537a62ca6b33acaf9a6140005dd8d1076534708 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Wed, 22 Jan 2025 21:08:06 +0100 Subject: [PATCH 13/14] cellGem: Set 255 as value for analog_t --- rpcs3/Emu/Cell/Modules/cellGem.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rpcs3/Emu/Cell/Modules/cellGem.cpp b/rpcs3/Emu/Cell/Modules/cellGem.cpp index 6eb68e49e1..18d22e8537 100644 --- a/rpcs3/Emu/Cell/Modules/cellGem.cpp +++ b/rpcs3/Emu/Cell/Modules/cellGem.cpp @@ -2109,7 +2109,7 @@ static bool mouse_input_to_pad(u32 mouse_no, be_t& digital_buttons, be_t& digital_buttons, be_t& if (gun.handler.get_button(gem_no, gun_button::btn_6) == 1) digital_buttons |= CELL_GEM_CTRL_SQUARE; - analog_t = gun.handler.get_button(gem_no, gun_button::btn_left) ? 0xFFFF : 0; + analog_t = gun.handler.get_button(gem_no, gun_button::btn_left) ? 255 : 0; return true; } From e83eeb3cca14848a8a44e2496a01d2852ad586a1 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Wed, 22 Jan 2025 21:49:13 +0100 Subject: [PATCH 14/14] cellGem: Move the cursor out of the screen (Time Crisis 4 needs this) --- rpcs3/Emu/Cell/Modules/cellGem.cpp | 35 ++++++++++++++++-------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/rpcs3/Emu/Cell/Modules/cellGem.cpp b/rpcs3/Emu/Cell/Modules/cellGem.cpp index 18d22e8537..d9585f1dcd 100644 --- a/rpcs3/Emu/Cell/Modules/cellGem.cpp +++ b/rpcs3/Emu/Cell/Modules/cellGem.cpp @@ -1605,6 +1605,10 @@ static inline void pos_to_gem_image_state(u32 gem_num, gem_config::gem_controlle if (x_max <= 0) x_max = shared_data.width; if (y_max <= 0) y_max = shared_data.height; + // Move the cursor out of the screen if we're at the screen border (Time Crisis 4 needs this) + if (x_pos <= 0) x_pos -= x_max / 10; else if (x_pos >= x_max) x_pos += x_max / 10; + if (y_pos <= 0) y_pos -= y_max / 10; else if (y_pos >= y_max) y_pos += y_max / 10; + const f32 scaling_width = x_max / static_cast(shared_data.width); const f32 scaling_height = y_max / static_cast(shared_data.height); const f32 mmPerPixel = CELL_GEM_SPHERE_RADIUS_MM / controller.radius; @@ -1654,6 +1658,10 @@ static inline void pos_to_gem_state(u32 gem_num, gem_config::gem_controller& con if (x_max <= 0) x_max = shared_data.width; if (y_max <= 0) y_max = shared_data.height; + // Move the cursor out of the screen if we're at the screen border (Time Crisis 4 needs this) + if (x_pos <= 0) x_pos -= x_max / 10; else if (x_pos >= x_max) x_pos += x_max / 10; + if (y_pos <= 0) y_pos -= y_max / 10; else if (y_pos >= y_max) y_pos += y_max / 10; + const f32 scaling_width = x_max / static_cast(shared_data.width); const f32 scaling_height = y_max / static_cast(shared_data.height); const f32 mmPerPixel = CELL_GEM_SPHERE_RADIUS_MM / controller.radius; @@ -1744,7 +1752,7 @@ extern bool is_input_allowed(); * Unavoidably buttons conflict with DS3 mappings, which is problematic for some games. * \param gem_num gem index to use * \param digital_buttons Bitmask filled with CELL_GEM_CTRL_* values - * \param analog_t Analog value of Move's Trigger. Currently mapped to R2. + * \param analog_t Analog value of Move's Trigger. * \return true on success, false if controller is disconnected */ static void ds3_input_to_pad(const u32 gem_num, be_t& digital_buttons, be_t& analog_t) @@ -1824,22 +1832,17 @@ static inline void ds3_get_stick_values(u32 gem_num, const std::shared_ptr& const auto& cfg = ::at32(g_cfg_gem_fake.players, gem_num); cfg->handle_input(pad, true, [&](gem_btn btn, pad_button /*pad_btn*/, u16 value, bool pressed, bool& /*abort*/) - { - if (!pressed) - return; + { + if (!pressed) + return; - switch (btn) - { - case gem_btn::x_axis: - x_pos = value; - break; - case gem_btn::y_axis: - y_pos = value; - break; - default: - break; - } - }); + switch (btn) + { + case gem_btn::x_axis: x_pos = value; break; + case gem_btn::y_axis: y_pos = value; break; + default: break; + } + }); } template