summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar Morph2020-10-10 09:03:47 -0400
committerGravatar Morph2020-11-15 23:33:20 -0500
commit9b501af8e3d0f6457fafb0fdfbcc11f6da4f0e8a (patch)
tree3a9cc70c7caa50eb7f3c5ede1bdddd4fa928c118 /src
parentconfigure_input: Hook up the vibration percentage spinbox (diff)
downloadyuzu-9b501af8e3d0f6457fafb0fdfbcc11f6da4f0e8a.tar.gz
yuzu-9b501af8e3d0f6457fafb0fdfbcc11f6da4f0e8a.tar.xz
yuzu-9b501af8e3d0f6457fafb0fdfbcc11f6da4f0e8a.zip
controllers/npad: Add heuristics to reduce rumble state changes
Sending too many state changes in a short period of time can cause massive performance issues. As a result, we have to use several heuristics to reduce the number of state changes to minimize/eliminate this performance impact while maintaining the quality of these vibrations as much as possible.
Diffstat (limited to 'src')
-rw-r--r--src/core/frontend/input.h2
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp51
-rw-r--r--src/input_common/sdl/sdl_impl.cpp54
3 files changed, 72 insertions, 35 deletions
diff --git a/src/core/frontend/input.h b/src/core/frontend/input.h
index 277b70e53..fb2ce2514 100644
--- a/src/core/frontend/input.h
+++ b/src/core/frontend/input.h
@@ -33,7 +33,7 @@ public:
33 virtual bool GetAnalogDirectionStatus(AnalogDirection direction) const { 33 virtual bool GetAnalogDirectionStatus(AnalogDirection direction) const {
34 return {}; 34 return {};
35 } 35 }
36 virtual bool SetRumblePlay(f32 amp_high, f32 amp_low, f32 freq_high, f32 freq_low) const { 36 virtual bool SetRumblePlay(f32 amp_low, f32 freq_low, f32 amp_high, f32 freq_high) const {
37 return {}; 37 return {};
38 } 38 }
39}; 39};
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index 924f209c0..cc54b164d 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -698,16 +698,57 @@ void Controller_NPad::VibrateController(const std::vector<DeviceHandle>& vibrati
698 continue; 698 continue;
699 } 699 }
700 700
701 // Filter out vibrations with equivalent values to reduce unnecessary state changes.
702 if (vibration_values[i].amp_low ==
703 latest_vibration_values[npad_index][device_index].amp_low &&
704 vibration_values[i].amp_high ==
705 latest_vibration_values[npad_index][device_index].amp_high) {
706 continue;
707 }
708
709 // Filter out non-zero vibrations that are within 0.015625 absolute amplitude of each other.
710 if ((vibration_values[i].amp_low != 0.0f || vibration_values[i].amp_high != 0.0f) &&
711 (latest_vibration_values[npad_index][device_index].amp_low != 0.0f ||
712 latest_vibration_values[npad_index][device_index].amp_high != 0.0f) &&
713 (abs(vibration_values[i].amp_low -
714 latest_vibration_values[npad_index][device_index].amp_low) < 0.015625f &&
715 abs(vibration_values[i].amp_high -
716 latest_vibration_values[npad_index][device_index].amp_high) < 0.015625f)) {
717 continue;
718 }
719
701 using namespace Settings::NativeButton; 720 using namespace Settings::NativeButton;
702 const auto& button_state = buttons[npad_index]; 721 const auto& button_state = buttons[npad_index];
703 722
704 // TODO: Vibrate left/right vibration motors independently if possible. 723 // TODO: Vibrate left/right vibration motors independently if possible.
705 button_state[A - BUTTON_HID_BEGIN]->SetRumblePlay( 724 const bool success = button_state[A - BUTTON_HID_BEGIN]->SetRumblePlay(
706 vibration_values[i].amp_high * Settings::values.vibration_strength.GetValue() / 100,
707 vibration_values[i].amp_low * Settings::values.vibration_strength.GetValue() / 100, 725 vibration_values[i].amp_low * Settings::values.vibration_strength.GetValue() / 100,
708 vibration_values[i].freq_high, vibration_values[i].freq_low); 726 vibration_values[i].freq_low,
709 727 vibration_values[i].amp_high * Settings::values.vibration_strength.GetValue() / 100,
710 latest_vibration_values[npad_index][device_index] = vibration_values[i]; 728 vibration_values[i].freq_high);
729
730 if (success) {
731 switch (connected_controllers[npad_index].type) {
732 case NPadControllerType::None:
733 UNREACHABLE();
734 break;
735 case NPadControllerType::ProController:
736 case NPadControllerType::Handheld:
737 case NPadControllerType::JoyDual:
738 // Since we can't vibrate motors independently yet, we can reduce state changes by
739 // assigning all 3 device indices the current vibration value.
740 latest_vibration_values[npad_index][0] = vibration_values[i];
741 latest_vibration_values[npad_index][1] = vibration_values[i];
742 latest_vibration_values[npad_index][2] = vibration_values[i];
743 break;
744 case NPadControllerType::JoyLeft:
745 case NPadControllerType::JoyRight:
746 case NPadControllerType::Pokeball:
747 default:
748 latest_vibration_values[npad_index][device_index] = vibration_values[i];
749 break;
750 }
751 }
711 } 752 }
712} 753}
713 754
diff --git a/src/input_common/sdl/sdl_impl.cpp b/src/input_common/sdl/sdl_impl.cpp
index 10883e2d9..18fb2ac5e 100644
--- a/src/input_common/sdl/sdl_impl.cpp
+++ b/src/input_common/sdl/sdl_impl.cpp
@@ -80,30 +80,24 @@ public:
80 return static_cast<float>(state.axes.at(axis)) / (32767.0f * range); 80 return static_cast<float>(state.axes.at(axis)) / (32767.0f * range);
81 } 81 }
82 82
83 bool RumblePlay(f32 amp_low, f32 amp_high, u32 time) { 83 bool RumblePlay(u16 amp_low, u16 amp_high) {
84 const u16 raw_amp_low = static_cast<u16>(amp_low * 0xFFFF); 84 using std::chrono::duration_cast;
85 const u16 raw_amp_high = static_cast<u16>(amp_high * 0xFFFF); 85 using std::chrono::milliseconds;
86 // Lower drastically the number of state changes 86 using std::chrono::steady_clock;
87 if (raw_amp_low >> 11 == last_state_rumble_low >> 11 && 87
88 raw_amp_high >> 11 == last_state_rumble_high >> 11) { 88 // Prevent vibrations less than 10ms apart from each other.
89 if (raw_amp_low + raw_amp_high != 0 || 89 if (duration_cast<milliseconds>(steady_clock::now() - last_vibration) < milliseconds(10)) {
90 last_state_rumble_low + last_state_rumble_high == 0) { 90 return false;
91 return false; 91 };
92 } 92
93 } 93 last_vibration = steady_clock::now();
94 // Don't change state if last vibration was < 20ms 94
95 const auto now = std::chrono::system_clock::now(); 95 if (sdl_controller != nullptr) {
96 if (std::chrono::duration_cast<std::chrono::milliseconds>(now - last_vibration) < 96 return SDL_GameControllerRumble(sdl_controller.get(), amp_low, amp_high, 0) == 0;
97 std::chrono::milliseconds(20)) { 97 } else if (sdl_joystick != nullptr) {
98 return raw_amp_low + raw_amp_high == 0; 98 return SDL_JoystickRumble(sdl_joystick.get(), amp_low, amp_high, 0) == 0;
99 } 99 }
100 100
101 last_vibration = now;
102 last_state_rumble_low = raw_amp_low;
103 last_state_rumble_high = raw_amp_high;
104 if (sdl_joystick) {
105 SDL_JoystickRumble(sdl_joystick.get(), raw_amp_low, raw_amp_high, time);
106 }
107 return false; 101 return false;
108 } 102 }
109 103
@@ -172,13 +166,13 @@ private:
172 } state; 166 } state;
173 std::string guid; 167 std::string guid;
174 int port; 168 int port;
175 u16 last_state_rumble_high = 0;
176 u16 last_state_rumble_low = 0;
177 std::chrono::time_point<std::chrono::system_clock> last_vibration;
178 std::unique_ptr<SDL_Joystick, decltype(&SDL_JoystickClose)> sdl_joystick; 169 std::unique_ptr<SDL_Joystick, decltype(&SDL_JoystickClose)> sdl_joystick;
179 std::unique_ptr<SDL_GameController, decltype(&SDL_GameControllerClose)> sdl_controller; 170 std::unique_ptr<SDL_GameController, decltype(&SDL_GameControllerClose)> sdl_controller;
180 mutable std::mutex mutex; 171 mutable std::mutex mutex;
181 172
173 // This is the timepoint of the last vibration and is used to ensure vibrations are 10ms apart.
174 std::chrono::steady_clock::time_point last_vibration;
175
182 // Motion is initialized without PID values as motion input is not aviable for SDL2 176 // Motion is initialized without PID values as motion input is not aviable for SDL2
183 MotionInput motion{0.0f, 0.0f, 0.0f}; 177 MotionInput motion{0.0f, 0.0f, 0.0f};
184}; 178};
@@ -327,10 +321,12 @@ public:
327 return joystick->GetButton(button); 321 return joystick->GetButton(button);
328 } 322 }
329 323
330 bool SetRumblePlay(f32 amp_high, f32 amp_low, f32 freq_high, f32 freq_low) const override { 324 bool SetRumblePlay(f32 amp_low, f32 freq_low, f32 amp_high, f32 freq_high) const override {
331 const f32 new_amp_low = pow(amp_low, 0.5f) * (3.0f - 2.0f * pow(amp_low, 0.15f)); 325 const u16 processed_amp_low =
332 const f32 new_amp_high = pow(amp_high, 0.5f) * (3.0f - 2.0f * pow(amp_high, 0.15f)); 326 static_cast<u16>(pow(amp_low, 0.5f) * (3.0f - 2.0f * pow(amp_low, 0.15f)) * 0xFFFF);
333 return joystick->RumblePlay(new_amp_low, new_amp_high, 250); 327 const u16 processed_amp_high =
328 static_cast<u16>(pow(amp_high, 0.5f) * (3.0f - 2.0f * pow(amp_high, 0.15f)) * 0xFFFF);
329 return joystick->RumblePlay(processed_amp_low, processed_amp_high);
334 } 330 }
335 331
336private: 332private: