diff --git a/README.md b/README.md index dfec8e508..abe155b4d 100755 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ yuzu emulator early access ============= -This is the source code for early-access 3412. +This is the source code for early-access 3414. ## Legal Notice diff --git a/src/common/scratch_buffer.h b/src/common/scratch_buffer.h index 1245a5086..26d4e76dc 100755 --- a/src/common/scratch_buffer.h +++ b/src/common/scratch_buffer.h @@ -23,6 +23,7 @@ public: buffer{Common::make_unique_for_overwrite(initial_capacity)} {} ~ScratchBuffer() = default; + ScratchBuffer(ScratchBuffer&&) = default; /// This will only grow the buffer's capacity if size is greater than the current capacity. /// The previously held data will remain intact. diff --git a/src/common/settings.cpp b/src/common/settings.cpp index 40668abd2..99d03116c 100755 --- a/src/common/settings.cpp +++ b/src/common/settings.cpp @@ -59,6 +59,7 @@ void LogSettings() { values.use_asynchronous_gpu_emulation.GetValue()); log_setting("Renderer_NvdecEmulation", values.nvdec_emulation.GetValue()); log_setting("Renderer_AccelerateASTC", values.accelerate_astc.GetValue()); + log_setting("Renderer_AsyncASTC", values.async_astc.GetValue()); log_setting("Renderer_UseVsync", values.use_vsync.GetValue()); log_setting("Renderer_ShaderBackend", values.shader_backend.GetValue()); log_setting("Renderer_UseAsynchronousShaders", values.use_asynchronous_shaders.GetValue()); @@ -76,6 +77,13 @@ void LogSettings() { log_setting("Debugging_GDBStub", values.use_gdbstub.GetValue()); log_setting("Input_EnableMotion", values.motion_enabled.GetValue()); log_setting("Input_EnableVibration", values.vibration_enabled.GetValue()); + log_setting("Input_EnableTouch", values.touchscreen.enabled); + log_setting("Input_EnableMouse", values.mouse_enabled.GetValue()); + log_setting("Input_EnableKeyboard", values.keyboard_enabled.GetValue()); + log_setting("Input_EnableRingController", values.enable_ring_controller.GetValue()); + log_setting("Input_EnableIrSensor", values.enable_ir_sensor.GetValue()); + log_setting("Input_EnableCustomJoycon", values.enable_joycon_driver.GetValue()); + log_setting("Input_EnableCustomProController", values.enable_procon_driver.GetValue()); log_setting("Input_EnableRawInput", values.enable_raw_input.GetValue()); } @@ -212,6 +220,7 @@ void RestoreGlobalState(bool is_powered_on) { values.use_asynchronous_gpu_emulation.SetGlobal(true); values.nvdec_emulation.SetGlobal(true); values.accelerate_astc.SetGlobal(true); + values.async_astc.SetGlobal(true); values.use_vsync.SetGlobal(true); values.shader_backend.SetGlobal(true); values.use_asynchronous_shaders.SetGlobal(true); diff --git a/src/common/settings.h b/src/common/settings.h index 370d3ccca..eecd0c5ab 100755 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -453,6 +453,7 @@ struct Values { SwitchableSetting use_asynchronous_gpu_emulation{true, "use_asynchronous_gpu_emulation"}; SwitchableSetting nvdec_emulation{NvdecEmulation::GPU, "nvdec_emulation"}; SwitchableSetting accelerate_astc{true, "accelerate_astc"}; + SwitchableSetting async_astc{false, "async_astc"}; SwitchableSetting use_vsync{true, "use_vsync"}; SwitchableSetting shader_backend{ShaderBackend::GLSL, ShaderBackend::GLSL, ShaderBackend::SPIRV, "shader_backend"}; diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp index 81b0166cd..1e3d93b47 100755 --- a/src/core/hid/emulated_controller.cpp +++ b/src/core/hid/emulated_controller.cpp @@ -363,7 +363,17 @@ void EmulatedController::ReloadInput() { SetMotion(callback, index); }, }); - motion_devices[index]->ForceUpdate(); + + // Restore motion state + auto& emulated_motion = controller.motion_values[index].emulated; + auto& motion = controller.motion_state[index]; + emulated_motion.ResetRotations(); + emulated_motion.ResetQuaternion(); + motion.accel = emulated_motion.GetAcceleration(); + motion.gyro = emulated_motion.GetGyroscope(); + motion.rotation = emulated_motion.GetRotations(); + motion.orientation = emulated_motion.GetOrientation(); + motion.is_at_rest = !emulated_motion.IsMoving(motion_sensitivity); } for (std::size_t index = 0; index < camera_devices.size(); ++index) { diff --git a/src/core/hid/emulated_devices.cpp b/src/core/hid/emulated_devices.cpp index 38720501e..84d4cb7cd 100755 --- a/src/core/hid/emulated_devices.cpp +++ b/src/core/hid/emulated_devices.cpp @@ -19,52 +19,53 @@ void EmulatedDevices::ReloadFromSettings() { void EmulatedDevices::ReloadInput() { // If you load any device here add the equivalent to the UnloadInput() function + + // Native Mouse is mapped on port 1, pad 0 + const Common::ParamPackage mouse_params{"engine:mouse,port:1,pad:0"}; + + // Keyboard keys is mapped on port 1, pad 0 for normal keys, pad 1 for moddifier keys + const Common::ParamPackage keyboard_params{"engine:keyboard,port:1"}; + std::size_t key_index = 0; for (auto& mouse_device : mouse_button_devices) { - Common::ParamPackage mouse_params; - mouse_params.Set("engine", "mouse"); - mouse_params.Set("button", static_cast(key_index)); - mouse_device = Common::Input::CreateInputDevice(mouse_params); + Common::ParamPackage mouse_button_params = mouse_params; + mouse_button_params.Set("button", static_cast(key_index)); + mouse_device = Common::Input::CreateInputDevice(mouse_button_params); key_index++; } - mouse_stick_device = - Common::Input::CreateInputDeviceFromString("engine:mouse,axis_x:0,axis_y:1"); + Common::ParamPackage mouse_position_params = mouse_params; + mouse_position_params.Set("axis_x", 0); + mouse_position_params.Set("axis_y", 1); + mouse_position_params.Set("deadzone", 0.0f); + mouse_position_params.Set("range", 1.0f); + mouse_position_params.Set("threshold", 0.0f); + mouse_stick_device = Common::Input::CreateInputDevice(mouse_position_params); // First two axis are reserved for mouse position key_index = 2; - for (auto& mouse_device : mouse_analog_devices) { - // Mouse axis are only mapped on port 1, pad 0 - Common::ParamPackage mouse_params; - mouse_params.Set("engine", "mouse"); - mouse_params.Set("axis", static_cast(key_index)); - mouse_params.Set("port", 1); - mouse_params.Set("pad", 0); - mouse_device = Common::Input::CreateInputDevice(mouse_params); + for (auto& mouse_device : mouse_wheel_devices) { + Common::ParamPackage mouse_wheel_params = mouse_params; + mouse_wheel_params.Set("axis", static_cast(key_index)); + mouse_device = Common::Input::CreateInputDevice(mouse_wheel_params); key_index++; } key_index = 0; for (auto& keyboard_device : keyboard_devices) { - // Keyboard keys are only mapped on port 1, pad 0 - Common::ParamPackage keyboard_params; - keyboard_params.Set("engine", "keyboard"); - keyboard_params.Set("button", static_cast(key_index)); - keyboard_params.Set("port", 1); - keyboard_params.Set("pad", 0); - keyboard_device = Common::Input::CreateInputDevice(keyboard_params); + Common::ParamPackage keyboard_key_params = keyboard_params; + keyboard_key_params.Set("button", static_cast(key_index)); + keyboard_key_params.Set("pad", 0); + keyboard_device = Common::Input::CreateInputDevice(keyboard_key_params); key_index++; } key_index = 0; for (auto& keyboard_device : keyboard_modifier_devices) { - // Keyboard moddifiers are only mapped on port 1, pad 1 - Common::ParamPackage keyboard_params; - keyboard_params.Set("engine", "keyboard"); - keyboard_params.Set("button", static_cast(key_index)); - keyboard_params.Set("port", 1); - keyboard_params.Set("pad", 1); - keyboard_device = Common::Input::CreateInputDevice(keyboard_params); + Common::ParamPackage keyboard_moddifier_params = keyboard_params; + keyboard_moddifier_params.Set("button", static_cast(key_index)); + keyboard_moddifier_params.Set("pad", 1); + keyboard_device = Common::Input::CreateInputDevice(keyboard_moddifier_params); key_index++; } @@ -80,14 +81,14 @@ void EmulatedDevices::ReloadInput() { }); } - for (std::size_t index = 0; index < mouse_analog_devices.size(); ++index) { - if (!mouse_analog_devices[index]) { + for (std::size_t index = 0; index < mouse_wheel_devices.size(); ++index) { + if (!mouse_wheel_devices[index]) { continue; } - mouse_analog_devices[index]->SetCallback({ + mouse_wheel_devices[index]->SetCallback({ .on_change = [this, index](const Common::Input::CallbackStatus& callback) { - SetMouseAnalog(callback, index); + SetMouseWheel(callback, index); }, }); } @@ -95,7 +96,9 @@ void EmulatedDevices::ReloadInput() { if (mouse_stick_device) { mouse_stick_device->SetCallback({ .on_change = - [this](const Common::Input::CallbackStatus& callback) { SetMouseStick(callback); }, + [this](const Common::Input::CallbackStatus& callback) { + SetMousePosition(callback); + }, }); } @@ -128,7 +131,7 @@ void EmulatedDevices::UnloadInput() { for (auto& button : mouse_button_devices) { button.reset(); } - for (auto& analog : mouse_analog_devices) { + for (auto& analog : mouse_wheel_devices) { analog.reset(); } mouse_stick_device.reset(); @@ -362,18 +365,18 @@ void EmulatedDevices::SetMouseButton(const Common::Input::CallbackStatus& callba TriggerOnChange(DeviceTriggerType::Mouse); } -void EmulatedDevices::SetMouseAnalog(const Common::Input::CallbackStatus& callback, - std::size_t index) { - if (index >= device_status.mouse_analog_values.size()) { +void EmulatedDevices::SetMouseWheel(const Common::Input::CallbackStatus& callback, + std::size_t index) { + if (index >= device_status.mouse_wheel_values.size()) { return; } std::unique_lock lock{mutex}; const auto analog_value = TransformToAnalog(callback); - device_status.mouse_analog_values[index] = analog_value; + device_status.mouse_wheel_values[index] = analog_value; if (is_configuring) { - device_status.mouse_position_state = {}; + device_status.mouse_wheel_state = {}; lock.unlock(); TriggerOnChange(DeviceTriggerType::Mouse); return; @@ -392,7 +395,7 @@ void EmulatedDevices::SetMouseAnalog(const Common::Input::CallbackStatus& callba TriggerOnChange(DeviceTriggerType::Mouse); } -void EmulatedDevices::SetMouseStick(const Common::Input::CallbackStatus& callback) { +void EmulatedDevices::SetMousePosition(const Common::Input::CallbackStatus& callback) { std::unique_lock lock{mutex}; const auto touch_value = TransformToTouch(callback); diff --git a/src/core/hid/emulated_devices.h b/src/core/hid/emulated_devices.h index 850c6c622..9a78136b5 100755 --- a/src/core/hid/emulated_devices.h +++ b/src/core/hid/emulated_devices.h @@ -23,8 +23,8 @@ using KeyboardModifierDevices = std::array; using MouseButtonDevices = std::array, Settings::NativeMouseButton::NumMouseButtons>; -using MouseAnalogDevices = std::array, - Settings::NativeMouseWheel::NumMouseWheels>; +using MouseWheelDevices = std::array, + Settings::NativeMouseWheel::NumMouseWheels>; using MouseStickDevice = std::unique_ptr; using MouseButtonParams = @@ -36,7 +36,7 @@ using KeyboardModifierValues = std::array; using MouseButtonValues = std::array; -using MouseAnalogValues = +using MouseWheelValues = std::array; using MouseStickValue = Common::Input::TouchStatus; @@ -50,7 +50,7 @@ struct DeviceStatus { KeyboardValues keyboard_values{}; KeyboardModifierValues keyboard_moddifier_values{}; MouseButtonValues mouse_button_values{}; - MouseAnalogValues mouse_analog_values{}; + MouseWheelValues mouse_wheel_values{}; MouseStickValue mouse_stick_value{}; // Data for HID serices @@ -111,15 +111,6 @@ public: /// Reverts any mapped changes made that weren't saved void RestoreConfig(); - // Returns the current mapped ring device - Common::ParamPackage GetRingParam() const; - - /** - * Updates the current mapped ring device - * @param param ParamPackage with ring sensor data to be mapped - */ - void SetRingParam(Common::ParamPackage param); - /// Returns the latest status of button input from the keyboard with parameters KeyboardValues GetKeyboardValues() const; @@ -187,19 +178,13 @@ private: * @param callback A CallbackStatus containing the wheel status * @param index wheel ID to be updated */ - void SetMouseAnalog(const Common::Input::CallbackStatus& callback, std::size_t index); + void SetMouseWheel(const Common::Input::CallbackStatus& callback, std::size_t index); /** * Updates the mouse position status of the mouse device * @param callback A CallbackStatus containing the position status */ - void SetMouseStick(const Common::Input::CallbackStatus& callback); - - /** - * Updates the ring analog sensor status of the ring controller - * @param callback A CallbackStatus containing the force status - */ - void SetRingAnalog(const Common::Input::CallbackStatus& callback); + void SetMousePosition(const Common::Input::CallbackStatus& callback); /** * Triggers a callback that something has changed on the device status @@ -212,7 +197,7 @@ private: KeyboardDevices keyboard_devices; KeyboardModifierDevices keyboard_modifier_devices; MouseButtonDevices mouse_button_devices; - MouseAnalogDevices mouse_analog_devices; + MouseWheelDevices mouse_wheel_devices; MouseStickDevice mouse_stick_device; mutable std::mutex mutex; diff --git a/src/core/hid/motion_input.cpp b/src/core/hid/motion_input.cpp index 2e77b8e39..a9a518e11 100755 --- a/src/core/hid/motion_input.cpp +++ b/src/core/hid/motion_input.cpp @@ -10,6 +10,8 @@ MotionInput::MotionInput() { // Initialize PID constants with default values SetPID(0.3f, 0.005f, 0.0f); SetGyroThreshold(ThresholdStandard); + ResetQuaternion(); + ResetRotations(); } void MotionInput::SetPID(f32 new_kp, f32 new_ki, f32 new_kd) { @@ -20,11 +22,19 @@ void MotionInput::SetPID(f32 new_kp, f32 new_ki, f32 new_kd) { void MotionInput::SetAcceleration(const Common::Vec3f& acceleration) { accel = acceleration; + + accel.x = std::clamp(accel.x, -AccelMaxValue, AccelMaxValue); + accel.y = std::clamp(accel.y, -AccelMaxValue, AccelMaxValue); + accel.z = std::clamp(accel.z, -AccelMaxValue, AccelMaxValue); } void MotionInput::SetGyroscope(const Common::Vec3f& gyroscope) { gyro = gyroscope - gyro_bias; + gyro.x = std::clamp(gyro.x, -GyroMaxValue, GyroMaxValue); + gyro.y = std::clamp(gyro.y, -GyroMaxValue, GyroMaxValue); + gyro.z = std::clamp(gyro.z, -GyroMaxValue, GyroMaxValue); + // Auto adjust drift to minimize drift if (!IsMoving(IsAtRestRelaxed)) { gyro_bias = (gyro_bias * 0.9999f) + (gyroscope * 0.0001f); @@ -61,6 +71,10 @@ void MotionInput::ResetRotations() { rotations = {}; } +void MotionInput::ResetQuaternion() { + quat = {{0.0f, 0.0f, -1.0f}, 0.0f}; +} + bool MotionInput::IsMoving(f32 sensitivity) const { return gyro.Length() >= sensitivity || accel.Length() <= 0.9f || accel.Length() >= 1.1f; } diff --git a/src/core/hid/motion_input.h b/src/core/hid/motion_input.h index 9e90d2a57..ebe2560ee 100755 --- a/src/core/hid/motion_input.h +++ b/src/core/hid/motion_input.h @@ -20,6 +20,9 @@ public: static constexpr float IsAtRestStandard = 0.01f; static constexpr float IsAtRestThight = 0.005f; + static constexpr float GyroMaxValue = 5.0f; + static constexpr float AccelMaxValue = 7.0f; + explicit MotionInput(); MotionInput(const MotionInput&) = default; @@ -40,6 +43,7 @@ public: void EnableReset(bool reset); void ResetRotations(); + void ResetQuaternion(); void UpdateRotation(u64 elapsed_time); void UpdateOrientation(u64 elapsed_time); @@ -69,7 +73,7 @@ private: Common::Vec3f derivative_error; // Quaternion containing the device orientation - Common::Quaternion quat{{0.0f, 0.0f, -1.0f}, 0.0f}; + Common::Quaternion quat; // Number of full rotations in each axis Common::Vec3f rotations; diff --git a/src/core/hle/service/hid/controllers/gesture.cpp b/src/core/hle/service/hid/controllers/gesture.cpp index 285ce2587..12837cfdc 100755 --- a/src/core/hle/service/hid/controllers/gesture.cpp +++ b/src/core/hle/service/hid/controllers/gesture.cpp @@ -65,6 +65,11 @@ void Controller_Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing) { } void Controller_Gesture::ReadTouchInput() { + if (!Settings::values.touchscreen.enabled) { + fingers = {}; + return; + } + const auto touch_status = console->GetTouch(); for (std::size_t id = 0; id < fingers.size(); ++id) { fingers[id] = touch_status[id]; diff --git a/src/core/hle/service/hid/controllers/mouse.cpp b/src/core/hle/service/hid/controllers/mouse.cpp index 1dc3c18e8..b83823741 100755 --- a/src/core/hle/service/hid/controllers/mouse.cpp +++ b/src/core/hle/service/hid/controllers/mouse.cpp @@ -33,10 +33,11 @@ void Controller_Mouse::OnUpdate(const Core::Timing::CoreTiming& core_timing) { return; } + next_state = {}; + const auto& last_entry = shared_memory->mouse_lifo.ReadCurrentEntry().state; next_state.sampling_number = last_entry.sampling_number + 1; - next_state.attribute.raw = 0; if (Settings::values.mouse_enabled) { const auto& mouse_button_state = emulated_devices->GetMouseButtons(); const auto& mouse_position_state = emulated_devices->GetMousePosition(); diff --git a/src/core/hle/service/hid/controllers/touchscreen.cpp b/src/core/hle/service/hid/controllers/touchscreen.cpp index 26d0f9932..6868345f0 100755 --- a/src/core/hle/service/hid/controllers/touchscreen.cpp +++ b/src/core/hle/service/hid/controllers/touchscreen.cpp @@ -58,6 +58,11 @@ void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timin } if (!finger.pressed && current_touch.pressed) { + // Ignore all touch fingers if disabled + if (!Settings::values.touchscreen.enabled) { + continue; + } + finger.attribute.start_touch.Assign(1); finger.pressed = true; finger.position = current_touch.position; diff --git a/src/input_common/drivers/mouse.cpp b/src/input_common/drivers/mouse.cpp index 072e846be..8face9c2f 100755 --- a/src/input_common/drivers/mouse.cpp +++ b/src/input_common/drivers/mouse.cpp @@ -10,17 +10,25 @@ #include "input_common/drivers/mouse.h" namespace InputCommon { +constexpr int update_time = 10; +constexpr float default_stick_sensitivity = 0.022f; +constexpr float default_motion_sensitivity = 0.008f; constexpr int mouse_axis_x = 0; constexpr int mouse_axis_y = 1; constexpr int wheel_axis_x = 2; constexpr int wheel_axis_y = 3; -constexpr int motion_wheel_y = 4; constexpr PadIdentifier identifier = { .guid = Common::UUID{}, .port = 0, .pad = 0, }; +constexpr PadIdentifier motion_identifier = { + .guid = Common::UUID{}, + .port = 0, + .pad = 1, +}; + constexpr PadIdentifier real_mouse_identifier = { .guid = Common::UUID{}, .port = 1, @@ -37,47 +45,87 @@ Mouse::Mouse(std::string input_engine_) : InputEngine(std::move(input_engine_)) PreSetController(identifier); PreSetController(real_mouse_identifier); PreSetController(touch_identifier); + PreSetController(motion_identifier); // Initialize all mouse axis PreSetAxis(identifier, mouse_axis_x); PreSetAxis(identifier, mouse_axis_y); PreSetAxis(identifier, wheel_axis_x); PreSetAxis(identifier, wheel_axis_y); - PreSetAxis(identifier, motion_wheel_y); PreSetAxis(real_mouse_identifier, mouse_axis_x); PreSetAxis(real_mouse_identifier, mouse_axis_y); PreSetAxis(touch_identifier, mouse_axis_x); PreSetAxis(touch_identifier, mouse_axis_y); + + // Initialize variables + mouse_origin = {}; + last_mouse_position = {}; + wheel_position = {}; + last_mouse_change = {}; + last_motion_change = {}; + update_thread = std::jthread([this](std::stop_token stop_token) { UpdateThread(stop_token); }); } void Mouse::UpdateThread(std::stop_token stop_token) { Common::SetCurrentThreadName("Mouse"); - constexpr int update_time = 10; + while (!stop_token.stop_requested()) { - if (Settings::values.mouse_panning) { - // Slow movement by 4% - last_mouse_change *= 0.96f; - const float sensitivity = - Settings::values.mouse_panning_sensitivity.GetValue() * 0.022f; - SetAxis(identifier, mouse_axis_x, last_mouse_change.x * sensitivity); - SetAxis(identifier, mouse_axis_y, -last_mouse_change.y * sensitivity); - } + UpdateStickInput(); + UpdateMotionInput(); - SetAxis(identifier, motion_wheel_y, 0.0f); - - if (mouse_panning_timout++ > 20) { + if (mouse_panning_timeout++ > 20) { StopPanning(); } std::this_thread::sleep_for(std::chrono::milliseconds(update_time)); } } +void Mouse::UpdateStickInput() { + if (!Settings::values.mouse_panning) { + return; + } + + const float sensitivity = + Settings::values.mouse_panning_sensitivity.GetValue() * default_stick_sensitivity; + + // Slow movement by 4% + last_mouse_change *= 0.96f; + SetAxis(identifier, mouse_axis_x, last_mouse_change.x * sensitivity); + SetAxis(identifier, mouse_axis_y, -last_mouse_change.y * sensitivity); +} + +void Mouse::UpdateMotionInput() { + const float sensitivity = + Settings::values.mouse_panning_sensitivity.GetValue() * default_motion_sensitivity; + + // Slow movement by 7% + if (Settings::values.mouse_panning) { + last_motion_change *= 0.93f; + } else { + last_motion_change.z *= 0.93f; + } + + const BasicMotion motion_data{ + .gyro_x = last_motion_change.x * sensitivity, + .gyro_y = last_motion_change.y * sensitivity, + .gyro_z = last_motion_change.z * sensitivity, + .accel_x = 0, + .accel_y = 0, + .accel_z = 0, + .delta_timestamp = update_time * 1000, + }; + + SetMotion(motion_identifier, 0, motion_data); +} + void Mouse::Move(int x, int y, int center_x, int center_y) { if (Settings::values.mouse_panning) { + mouse_panning_timeout = 0; + auto mouse_change = (Common::MakeVec(x, y) - Common::MakeVec(center_x, center_y)).Cast(); - mouse_panning_timout = 0; + Common::Vec3 motion_change{-mouse_change.y, -mouse_change.x, last_motion_change.z}; const auto move_distance = mouse_change.Length(); if (move_distance == 0) { @@ -93,6 +141,7 @@ void Mouse::Move(int x, int y, int center_x, int center_y) { // Average mouse movements last_mouse_change = (last_mouse_change * 0.91f) + (mouse_change * 0.09f); + last_motion_change = (last_motion_change * 0.69f) + (motion_change * 0.31f); const auto last_move_distance = last_mouse_change.Length(); @@ -116,6 +165,12 @@ void Mouse::Move(int x, int y, int center_x, int center_y) { const float sensitivity = Settings::values.mouse_panning_sensitivity.GetValue() * 0.0012f; SetAxis(identifier, mouse_axis_x, static_cast(mouse_move.x) * sensitivity); SetAxis(identifier, mouse_axis_y, static_cast(-mouse_move.y) * sensitivity); + + last_motion_change = { + static_cast(-mouse_move.y) / 50.0f, + static_cast(-mouse_move.x) / 50.0f, + last_motion_change.z, + }; } } @@ -157,15 +212,19 @@ void Mouse::ReleaseButton(MouseButton button) { SetAxis(identifier, mouse_axis_x, 0); SetAxis(identifier, mouse_axis_y, 0); } + + last_motion_change.x = 0; + last_motion_change.y = 0; + button_pressed = false; } void Mouse::MouseWheelChange(int x, int y) { wheel_position.x += x; wheel_position.y += y; + last_motion_change.z += static_cast(y) / 100.0f; SetAxis(identifier, wheel_axis_x, static_cast(wheel_position.x)); SetAxis(identifier, wheel_axis_y, static_cast(wheel_position.y)); - SetAxis(identifier, motion_wheel_y, static_cast(y) / 100.0f); } void Mouse::ReleaseAllButtons() { @@ -234,6 +293,9 @@ Common::Input::ButtonNames Mouse::GetUIName(const Common::ParamPackage& params) if (params.Has("axis_x") && params.Has("axis_y") && params.Has("axis_z")) { return Common::Input::ButtonNames::Engine; } + if (params.Has("motion")) { + return Common::Input::ButtonNames::Engine; + } return Common::Input::ButtonNames::Invalid; } diff --git a/src/input_common/drivers/mouse.h b/src/input_common/drivers/mouse.h index 62886124a..4fa2210db 100755 --- a/src/input_common/drivers/mouse.h +++ b/src/input_common/drivers/mouse.h @@ -96,6 +96,8 @@ public: private: void UpdateThread(std::stop_token stop_token); + void UpdateStickInput(); + void UpdateMotionInput(); void StopPanning(); Common::Input::ButtonNames GetUIButtonName(const Common::ParamPackage& params) const; @@ -103,9 +105,10 @@ private: Common::Vec2 mouse_origin; Common::Vec2 last_mouse_position; Common::Vec2 last_mouse_change; + Common::Vec3 last_motion_change; Common::Vec2 wheel_position; bool button_pressed; - int mouse_panning_timout{}; + int mouse_panning_timeout{}; std::jthread update_thread; }; diff --git a/src/input_common/input_mapping.cpp b/src/input_common/input_mapping.cpp index 1004b14c2..b1ef5b3c3 100755 --- a/src/input_common/input_mapping.cpp +++ b/src/input_common/input_mapping.cpp @@ -142,14 +142,10 @@ void MappingFactory::RegisterMotion(const MappingData& data) { new_input.Set("port", static_cast(data.pad.port)); new_input.Set("pad", static_cast(data.pad.pad)); - // If engine is mouse map the mouse position as 3 axis motion + // If engine is mouse map it automatically to mouse motion if (data.engine == "mouse") { - new_input.Set("axis_x", 1); - new_input.Set("invert_x", "-"); - new_input.Set("axis_y", 0); - new_input.Set("axis_z", 4); - new_input.Set("range", 1.0f); - new_input.Set("deadzone", 0.0f); + new_input.Set("motion", 0); + new_input.Set("pad", 1); input_queue.Push(new_input); return; } diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp index 2d6b33fac..a78e3a3ac 100755 --- a/src/video_core/renderer_opengl/gl_texture_cache.cpp +++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp @@ -228,8 +228,9 @@ void ApplySwizzle(GLuint handle, PixelFormat format, std::array TextureCacheRuntime::StagingBuffers::FindBuffer(size_t req Image::Image(TextureCacheRuntime& runtime_, const VideoCommon::ImageInfo& info_, GPUVAddr gpu_addr_, VAddr cpu_addr_) : VideoCommon::ImageBase(info_, gpu_addr_, cpu_addr_), runtime{&runtime_} { - if (CanBeAccelerated(*runtime, info)) { + if (CanBeDecodedAsync(*runtime, info)) { + flags |= ImageFlagBits::AsynchronousDecode; + } else if (CanBeAccelerated(*runtime, info)) { flags |= ImageFlagBits::AcceleratedUpload; } if (IsConverted(runtime->device, info.format, info.type)) { diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index ec3b32d2f..55657907c 100755 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -1262,11 +1262,12 @@ Image::Image(TextureCacheRuntime& runtime_, const ImageInfo& info_, GPUVAddr gpu commit(runtime_.memory_allocator.Commit(original_image, MemoryUsage::DeviceLocal)), aspect_mask(ImageAspectMask(info.format)) { if (IsPixelFormatASTC(info.format) && !runtime->device.IsOptimalAstcSupported()) { - if (Settings::values.accelerate_astc.GetValue()) { + if (Settings::values.async_astc.GetValue()) { + flags |= VideoCommon::ImageFlagBits::AsynchronousDecode; + } else if (Settings::values.accelerate_astc.GetValue()) { flags |= VideoCommon::ImageFlagBits::AcceleratedUpload; - } else { - flags |= VideoCommon::ImageFlagBits::Converted; } + flags |= VideoCommon::ImageFlagBits::Converted; flags |= VideoCommon::ImageFlagBits::CostlyLoad; } if (runtime->device.HasDebuggingToolAttached()) { diff --git a/src/video_core/texture_cache/image_base.h b/src/video_core/texture_cache/image_base.h index f36c3dbb8..b9177c779 100755 --- a/src/video_core/texture_cache/image_base.h +++ b/src/video_core/texture_cache/image_base.h @@ -38,6 +38,9 @@ enum class ImageFlagBits : u32 { Rescaled = 1 << 13, CheckingRescalable = 1 << 14, IsRescalable = 1 << 15, + + AsynchronousDecode = 1 << 16, + IsDecoding = 1 << 17, ///< Is currently being decoded asynchornously. }; DECLARE_ENUM_FLAG_OPERATORS(ImageFlagBits) diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index 6297f10b8..8dd144837 100755 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h @@ -85,6 +85,11 @@ void TextureCache

::RunGarbageCollector() { } --num_iterations; auto& image = slot_images[image_id]; + if (True(image.flags & ImageFlagBits::IsDecoding)) { + // This image is still being decoded, deleting it will invalidate the slot + // used by the async decoder thread. + return false; + } const bool must_download = image.IsSafeDownload() && False(image.flags & ImageFlagBits::BadOverlap); if (!high_priority_mode && @@ -133,6 +138,8 @@ void TextureCache

::TickFrame() { sentenced_images.Tick(); sentenced_framebuffers.Tick(); sentenced_image_view.Tick(); + TickAsyncDecode(); + runtime.TickFrame(); critical_gc = 0; ++frame_tick; @@ -777,6 +784,10 @@ void TextureCache

::RefreshContents(Image& image, ImageId image_id) { LOG_WARNING(HW_GPU, "MSAA image uploads are not implemented"); return; } + if (True(image.flags & ImageFlagBits::AsynchronousDecode)) { + QueueAsyncDecode(image, image_id); + return; + } auto staging = runtime.UploadStagingBuffer(MapSizeBytes(image)); UploadImageContents(image, staging); runtime.InsertUploadMemoryBarrier(); @@ -989,6 +1000,65 @@ u64 TextureCache

::GetScaledImageSizeBytes(const ImageBase& image) { return fitted_size; } +template +void TextureCache

::QueueAsyncDecode(Image& image, ImageId image_id) { + UNIMPLEMENTED_IF(False(image.flags & ImageFlagBits::Converted)); + LOG_INFO(HW_GPU, "Queuing async texture decode"); + + image.flags |= ImageFlagBits::IsDecoding; + auto decode = std::make_unique(); + auto* decode_ptr = decode.get(); + decode->image_id = image_id; + async_decodes.push_back(std::move(decode)); + + Common::ScratchBuffer local_unswizzle_data_buffer(image.unswizzled_size_bytes); + const size_t guest_size_bytes = image.guest_size_bytes; + swizzle_data_buffer.resize_destructive(guest_size_bytes); + gpu_memory->ReadBlockUnsafe(image.gpu_addr, swizzle_data_buffer.data(), guest_size_bytes); + auto copies = UnswizzleImage(*gpu_memory, image.gpu_addr, image.info, swizzle_data_buffer, + local_unswizzle_data_buffer); + const size_t out_size = MapSizeBytes(image); + + auto func = [out_size, copies, info = image.info, + input = std::move(local_unswizzle_data_buffer), + async_decode = decode_ptr]() mutable { + async_decode->decoded_data.resize_destructive(out_size); + std::span copies_span{copies.data(), copies.size()}; + ConvertImage(input, info, async_decode->decoded_data, copies_span); + + // TODO: Do we need this lock? + std::unique_lock lock{async_decode->mutex}; + async_decode->copies = std::move(copies); + async_decode->complete = true; + }; + texture_decode_worker.QueueWork(std::move(func)); +} + +template +void TextureCache

::TickAsyncDecode() { + bool has_uploads{}; + auto i = async_decodes.begin(); + while (i != async_decodes.end()) { + auto* async_decode = i->get(); + std::unique_lock lock{async_decode->mutex}; + if (!async_decode->complete) { + ++i; + continue; + } + Image& image = slot_images[async_decode->image_id]; + auto staging = runtime.UploadStagingBuffer(MapSizeBytes(image)); + std::memcpy(staging.mapped_span.data(), async_decode->decoded_data.data(), + async_decode->decoded_data.size()); + image.UploadMemory(staging, async_decode->copies); + image.flags &= ~ImageFlagBits::IsDecoding; + has_uploads = true; + i = async_decodes.erase(i); + } + if (has_uploads) { + runtime.InsertUploadMemoryBarrier(); + } +} + template bool TextureCache

::ScaleUp(Image& image) { const bool has_copy = image.HasScaled(); diff --git a/src/video_core/texture_cache/texture_cache_base.h b/src/video_core/texture_cache/texture_cache_base.h index 362426d46..ba420fcd5 100755 --- a/src/video_core/texture_cache/texture_cache_base.h +++ b/src/video_core/texture_cache/texture_cache_base.h @@ -3,6 +3,7 @@ #pragma once +#include #include #include #include @@ -18,6 +19,7 @@ #include "common/lru_cache.h" #include "common/polyfill_ranges.h" #include "common/scratch_buffer.h" +#include "common/thread_worker.h" #include "video_core/compatible_formats.h" #include "video_core/control/channel_state_cache.h" #include "video_core/delayed_destruction_ring.h" @@ -54,6 +56,14 @@ struct ImageViewInOut { ImageViewId id{}; }; +struct AsyncDecodeContext { + ImageId image_id; + Common::ScratchBuffer decoded_data; + std::vector copies; + std::mutex mutex; + std::atomic_bool complete; +}; + using TextureCacheGPUMap = std::unordered_map, Common::IdentityHash>; class TextureCacheChannelInfo : public ChannelInfo { @@ -382,6 +392,9 @@ private: bool ScaleDown(Image& image); u64 GetScaledImageSizeBytes(const ImageBase& image); + void QueueAsyncDecode(Image& image, ImageId image_id); + void TickAsyncDecode(); + Runtime& runtime; VideoCore::RasterizerInterface& rasterizer; @@ -435,6 +448,9 @@ private: u64 modification_tick = 0; u64 frame_tick = 0; + + Common::ThreadWorker texture_decode_worker{1, "TextureDecoder"}; + std::vector> async_decodes; }; } // namespace VideoCommon diff --git a/src/video_core/textures/astc.cpp b/src/video_core/textures/astc.cpp index 3d18c16d9..a03e96a0c 100755 --- a/src/video_core/textures/astc.cpp +++ b/src/video_core/textures/astc.cpp @@ -1656,8 +1656,8 @@ void Decompress(std::span data, uint32_t width, uint32_t height, const u32 rows = Common::DivideUp(height, block_height); const u32 cols = Common::DivideUp(width, block_width); - Common::ThreadWorker workers{std::max(std::thread::hardware_concurrency(), 2U) / 2, - "ASTCDecompress"}; + static Common::ThreadWorker workers{std::max(std::thread::hardware_concurrency(), 2U) / 2, + "ASTCDecompress"}; for (u32 z = 0; z < depth; ++z) { const u32 depth_offset = z * height * width * 4; diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 13c0de9b5..b8782bcaf 100755 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -707,6 +707,7 @@ void Config::ReadRendererValues() { ReadGlobalSetting(Settings::values.use_asynchronous_gpu_emulation); ReadGlobalSetting(Settings::values.nvdec_emulation); ReadGlobalSetting(Settings::values.accelerate_astc); + ReadGlobalSetting(Settings::values.async_astc); ReadGlobalSetting(Settings::values.use_vsync); ReadGlobalSetting(Settings::values.shader_backend); ReadGlobalSetting(Settings::values.use_asynchronous_shaders); @@ -1350,6 +1351,7 @@ void Config::SaveRendererValues() { static_cast(Settings::values.nvdec_emulation.GetDefault()), Settings::values.nvdec_emulation.UsingGlobal()); WriteGlobalSetting(Settings::values.accelerate_astc); + WriteGlobalSetting(Settings::values.async_astc); WriteGlobalSetting(Settings::values.use_vsync); WriteSetting(QString::fromStdString(Settings::values.shader_backend.GetLabel()), static_cast(Settings::values.shader_backend.GetValue(global)), diff --git a/src/yuzu/configuration/configure_graphics_advanced.cpp b/src/yuzu/configuration/configure_graphics_advanced.cpp index 4024fe21b..789316793 100755 --- a/src/yuzu/configuration/configure_graphics_advanced.cpp +++ b/src/yuzu/configuration/configure_graphics_advanced.cpp @@ -23,11 +23,13 @@ void ConfigureGraphicsAdvanced::SetConfiguration() { const bool runtime_lock = !system.IsPoweredOn(); ui->use_vsync->setEnabled(runtime_lock); ui->renderer_force_max_clock->setEnabled(runtime_lock); + ui->async_astc->setEnabled(runtime_lock); ui->use_asynchronous_shaders->setEnabled(runtime_lock); ui->anisotropic_filtering_combobox->setEnabled(runtime_lock); ui->renderer_force_max_clock->setChecked(Settings::values.renderer_force_max_clock.GetValue()); ui->use_vsync->setChecked(Settings::values.use_vsync.GetValue()); + ui->async_astc->setChecked(Settings::values.async_astc.GetValue()); ui->use_asynchronous_shaders->setChecked(Settings::values.use_asynchronous_shaders.GetValue()); ui->use_fast_gpu_time->setChecked(Settings::values.use_fast_gpu_time.GetValue()); ui->use_pessimistic_flushes->setChecked(Settings::values.use_pessimistic_flushes.GetValue()); @@ -60,6 +62,8 @@ void ConfigureGraphicsAdvanced::ApplyConfiguration() { ConfigurationShared::ApplyPerGameSetting(&Settings::values.max_anisotropy, ui->anisotropic_filtering_combobox); ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_vsync, ui->use_vsync, use_vsync); + ConfigurationShared::ApplyPerGameSetting(&Settings::values.async_astc, ui->async_astc, + async_astc); ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_asynchronous_shaders, ui->use_asynchronous_shaders, use_asynchronous_shaders); @@ -91,6 +95,7 @@ void ConfigureGraphicsAdvanced::SetupPerGameUI() { ui->renderer_force_max_clock->setEnabled( Settings::values.renderer_force_max_clock.UsingGlobal()); ui->use_vsync->setEnabled(Settings::values.use_vsync.UsingGlobal()); + ui->async_astc->setEnabled(Settings::values.async_astc.UsingGlobal()); ui->use_asynchronous_shaders->setEnabled( Settings::values.use_asynchronous_shaders.UsingGlobal()); ui->use_fast_gpu_time->setEnabled(Settings::values.use_fast_gpu_time.UsingGlobal()); @@ -108,6 +113,8 @@ void ConfigureGraphicsAdvanced::SetupPerGameUI() { Settings::values.renderer_force_max_clock, renderer_force_max_clock); ConfigurationShared::SetColoredTristate(ui->use_vsync, Settings::values.use_vsync, use_vsync); + ConfigurationShared::SetColoredTristate(ui->async_astc, Settings::values.async_astc, + async_astc); ConfigurationShared::SetColoredTristate(ui->use_asynchronous_shaders, Settings::values.use_asynchronous_shaders, use_asynchronous_shaders); diff --git a/src/yuzu/configuration/configure_graphics_advanced.h b/src/yuzu/configuration/configure_graphics_advanced.h index 1e74e6415..c758fa4e6 100755 --- a/src/yuzu/configuration/configure_graphics_advanced.h +++ b/src/yuzu/configuration/configure_graphics_advanced.h @@ -38,6 +38,7 @@ private: ConfigurationShared::CheckState renderer_force_max_clock; ConfigurationShared::CheckState use_vsync; + ConfigurationShared::CheckState async_astc; ConfigurationShared::CheckState use_asynchronous_shaders; ConfigurationShared::CheckState use_fast_gpu_time; ConfigurationShared::CheckState use_pessimistic_flushes; diff --git a/src/yuzu/configuration/configure_graphics_advanced.ui b/src/yuzu/configuration/configure_graphics_advanced.ui index d38d31246..501e1b74e 100755 --- a/src/yuzu/configuration/configure_graphics_advanced.ui +++ b/src/yuzu/configuration/configure_graphics_advanced.ui @@ -89,6 +89,16 @@ + + + + Enables asynchronous ASTC texture decoding, which may reduce load time stutter. This feature is experimental. + + + Decode ASTC textures asynchronously (Hack) + + + diff --git a/src/yuzu/discord_impl.cpp b/src/yuzu/discord_impl.cpp index b07ca8029..2e1a33123 100755 --- a/src/yuzu/discord_impl.cpp +++ b/src/yuzu/discord_impl.cpp @@ -75,6 +75,8 @@ void DiscordImpl::Update() { // New Check for game cover httplib::Client cli(game_cover_url); + cli.set_connection_timeout(std::chrono::seconds(3)); + cli.set_read_timeout(std::chrono::seconds(3)); if (auto res = cli.Head(fmt::format("/images/game/boxart/{}.png", icon_name))) { if (res->status == 200) { diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp index c1881ca97..59bf44135 100755 --- a/src/yuzu_cmd/config.cpp +++ b/src/yuzu_cmd/config.cpp @@ -324,6 +324,7 @@ void Config::ReadValues() { ReadSetting("Renderer", Settings::values.use_asynchronous_shaders); ReadSetting("Renderer", Settings::values.nvdec_emulation); ReadSetting("Renderer", Settings::values.accelerate_astc); + ReadSetting("Renderer", Settings::values.async_astc); ReadSetting("Renderer", Settings::values.use_fast_gpu_time); ReadSetting("Renderer", Settings::values.use_pessimistic_flushes); ReadSetting("Renderer", Settings::values.use_vulkan_driver_pipeline_cache); diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h index ca8af9072..37ea1b4e0 100755 --- a/src/yuzu_cmd/default_ini.h +++ b/src/yuzu_cmd/default_ini.h @@ -342,6 +342,10 @@ nvdec_emulation = # 0: Off, 1 (default): On accelerate_astc = +# Decode ASTC textures asynchronously. +# 0 (default): Off, 1: On +async_astc = + # Turns on the speed limiter, which will limit the emulation speed to the desired speed limit value # 0: Off, 1: On (default) use_speed_limit =