From 315992f254114716574e55828a86f8918abd586e Mon Sep 17 00:00:00 2001 From: Filoppi Date: Mon, 18 Dec 2023 00:33:00 +0200 Subject: [PATCH 01/21] Fix sharp bilinear using ceil instead of floor --- Data/Sys/Shaders/default_pre_post_process.glsl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Data/Sys/Shaders/default_pre_post_process.glsl b/Data/Sys/Shaders/default_pre_post_process.glsl index f6854d11fd..5df2de9986 100644 --- a/Data/Sys/Shaders/default_pre_post_process.glsl +++ b/Data/Sys/Shaders/default_pre_post_process.glsl @@ -173,7 +173,7 @@ float4 SharpBilinearSample(float3 uvw, float gamma) float2 texel = uvw.xy * source_size; float2 texel_floored = floor(texel); float2 s = fract(texel); - float scale = ceil(max(target_size.x * inverted_source_size.x, target_size.y * inverted_source_size.y)); + float scale = max(floor(max(target_size.x * inverted_source_size.x, target_size.y * inverted_source_size.y)), 1.f); float region_range = 0.5 - (0.5 / scale); // Figure out where in the texel to sample to get correct pre-scaled bilinear. From e456bef163c1b667ff5a045a4bda66b30326ae90 Mon Sep 17 00:00:00 2001 From: Filoppi Date: Wed, 24 May 2023 22:58:30 +0300 Subject: [PATCH 02/21] Input: Improve Controller Interface devices threading This specific issue was already addressed by https://github.com/dolphin-emu/dolphin/pull/11635 though I felt like there was something more we could do, and wasn't too happy with the likelihood of devices update calls being skipped (due to `m_devices_population_mutex` being locked). --- .../ControllerInterface.cpp | 75 +++++++++++++------ .../ControllerInterface/ControllerInterface.h | 1 - .../ControllerInterface/CoreDevice.h | 11 ++- .../DInput/DInputJoystick.cpp | 4 +- .../DInput/DInputJoystick.h | 2 +- .../DInput/DInputKeyboardMouse.cpp | 4 +- .../DInput/DInputKeyboardMouse.h | 2 +- .../DualShockUDPClient/DualShockUDPClient.cpp | 6 +- .../ControllerInterface/InputBackend.cpp | 2 +- .../ControllerInterface/InputBackend.h | 13 +++- .../ControllerInterface/Pipes/Pipes.cpp | 3 +- .../ControllerInterface/Pipes/Pipes.h | 2 +- .../Quartz/QuartzKeyboardAndMouse.h | 2 +- .../Quartz/QuartzKeyboardAndMouse.mm | 4 +- .../ControllerInterface/SDL/SDL.cpp | 9 ++- .../SteamDeck/SteamDeck.cpp | 10 ++- .../ControllerInterface/WGInput/WGInput.cpp | 4 +- .../Wiimote/WiimoteController.cpp | 10 +-- .../Wiimote/WiimoteController.h | 2 +- .../ControllerInterface/XInput/XInput.cpp | 4 +- .../ControllerInterface/XInput/XInput.h | 2 +- .../ControllerInterface/Xlib/XInput2.cpp | 4 +- .../ControllerInterface/Xlib/XInput2.h | 2 +- .../ControllerInterface/evdev/evdev.cpp | 3 +- .../ControllerInterface/evdev/evdev.h | 2 +- 25 files changed, 125 insertions(+), 58 deletions(-) diff --git a/Source/Core/InputCommon/ControllerInterface/ControllerInterface.cpp b/Source/Core/InputCommon/ControllerInterface/ControllerInterface.cpp index 7ba11fc5b7..b7a9c7f307 100644 --- a/Source/Core/InputCommon/ControllerInterface/ControllerInterface.cpp +++ b/Source/Core/InputCommon/ControllerInterface/ControllerInterface.cpp @@ -45,6 +45,8 @@ ControllerInterface g_controller_interface; // will never interfere with game threads. static thread_local ciface::InputChannel tls_input_channel = ciface::InputChannel::Host; +static thread_local bool tls_is_updating_devices = false; + void ControllerInterface::Initialize(const WindowSystemInfo& wsi) { if (m_is_init) @@ -122,8 +124,8 @@ void ControllerInterface::RefreshDevices(RefreshReason reason) // We lock m_devices_population_mutex here to make everything simpler. // Multiple devices classes have their own "hotplug" thread, and can add/remove devices at any // time, while actual writes to "m_devices" are safe, the order in which they happen is not. That - // means a thread could be adding devices while we are removing them, or removing them as we are - // populating them (causing missing or duplicate devices). + // means a thread could be adding devices while we are removing them from a different thread, + // or removing them as we are populating them (causing missing or duplicate devices). std::lock_guard lk_population(m_devices_population_mutex); #if defined(CIFACE_USE_WIN32) && !defined(CIFACE_USE_XLIB) && !defined(CIFACE_USE_OSX) @@ -271,6 +273,10 @@ bool ControllerInterface::AddDevice(std::shared_ptr device if (!m_is_init) return false; + ASSERT_MSG(CONTROLLERINTERFACE, !tls_is_updating_devices, + "Devices shouldn't be added within input update calls, there is a risk of deadlock " + "if another thread was already here"); + std::lock_guard lk_population(m_devices_population_mutex); { @@ -328,6 +334,10 @@ void ControllerInterface::RemoveDevice(std::function> devices_to_remove; - // Lock this first to avoid deadlock with m_devices_mutex in certain cases (such as a Wii Remote - // getting disconnected) - if (!m_devices_population_mutex.try_lock()) - return; - - std::lock_guard population_lock(m_devices_population_mutex, std::adopt_lock); - - if (!m_devices_mutex.try_lock()) - return; - - std::lock_guard lk(m_devices_mutex, std::adopt_lock); - - for (auto& backend : m_input_backends) - backend->UpdateInput(); - - for (const auto& d : m_devices) { - // Theoretically we could avoid updating input on devices that don't have any references to - // them, but in practice a few devices types could break in different ways, so we don't - d->UpdateInput(); + // TODO: if we are an emulation input channel, we should probably always lock. + // Prefer outdated values over blocking UI or CPU thread (this avoids short but noticeable frame + // drops) + if (!m_devices_mutex.try_lock()) + return; + + std::lock_guard lk_devices(m_devices_mutex, std::adopt_lock); + + tls_is_updating_devices = true; + + for (auto& backend : m_input_backends) + backend->UpdateInput(devices_to_remove); + + for (const auto& d : m_devices) + { + // Theoretically we could avoid updating input on devices that don't have any references to + // them, but in practice a few devices types could break in different ways, so we don't + if (d->UpdateInput() == ciface::Core::DeviceRemoval::Remove) + devices_to_remove.push_back(d); + } + + tls_is_updating_devices = false; + } + + if (devices_to_remove.size() > 0) + { + RemoveDevice([&](const ciface::Core::Device* device) { + return std::any_of(devices_to_remove.begin(), devices_to_remove.end(), + [device](const std::weak_ptr& d) { + return d.lock().get() == device; + }); + }); } } diff --git a/Source/Core/InputCommon/ControllerInterface/ControllerInterface.h b/Source/Core/InputCommon/ControllerInterface/ControllerInterface.h index 6dc0afafe8..837e5135d5 100644 --- a/Source/Core/InputCommon/ControllerInterface/ControllerInterface.h +++ b/Source/Core/InputCommon/ControllerInterface/ControllerInterface.h @@ -127,7 +127,6 @@ private: std::list> m_devices_changed_callbacks; mutable std::recursive_mutex m_devices_population_mutex; - mutable std::mutex m_pre_population_mutex; mutable std::mutex m_callbacks_mutex; std::atomic m_is_init; // This is now always protected by m_devices_population_mutex, so diff --git a/Source/Core/InputCommon/ControllerInterface/CoreDevice.h b/Source/Core/InputCommon/ControllerInterface/CoreDevice.h index a2176637ab..1667b42a00 100644 --- a/Source/Core/InputCommon/ControllerInterface/CoreDevice.h +++ b/Source/Core/InputCommon/ControllerInterface/CoreDevice.h @@ -36,6 +36,12 @@ constexpr ControlState BATTERY_INPUT_MAX_VALUE = 100.0; namespace Core { +enum class DeviceRemoval +{ + Remove, + Keep, +}; + class Device { public: @@ -118,7 +124,7 @@ public: virtual std::string GetName() const = 0; virtual std::string GetSource() const = 0; std::string GetQualifiedName() const; - virtual void UpdateInput() {} + virtual DeviceRemoval UpdateInput() { return DeviceRemoval::Keep; } // May be overridden to implement hotplug removal. // Currently handled on a per-backend basis but this could change. @@ -242,7 +248,8 @@ public: std::recursive_mutex& GetDevicesMutex() const { return m_devices_mutex; } protected: - // Exclusively needed when reading/writing "m_devices" + // Exclusively needed when reading/writing the "m_devices" array. + // Not needed when individually readring/writing a single device ptr. mutable std::recursive_mutex m_devices_mutex; std::vector> m_devices; }; diff --git a/Source/Core/InputCommon/ControllerInterface/DInput/DInputJoystick.cpp b/Source/Core/InputCommon/ControllerInterface/DInput/DInputJoystick.cpp index eb60721b2f..f095818287 100644 --- a/Source/Core/InputCommon/ControllerInterface/DInput/DInputJoystick.cpp +++ b/Source/Core/InputCommon/ControllerInterface/DInput/DInputJoystick.cpp @@ -222,7 +222,7 @@ bool Joystick::IsValid() const return SUCCEEDED(m_device->Acquire()); } -void Joystick::UpdateInput() +Core::DeviceRemoval Joystick::UpdateInput() { HRESULT hr = 0; @@ -261,6 +261,8 @@ void Joystick::UpdateInput() // try reacquire if input lost if (DIERR_INPUTLOST == hr || DIERR_NOTACQUIRED == hr) m_device->Acquire(); + + return Core::DeviceRemoval::Keep; } // get name diff --git a/Source/Core/InputCommon/ControllerInterface/DInput/DInputJoystick.h b/Source/Core/InputCommon/ControllerInterface/DInput/DInputJoystick.h index f69b95293c..cb73f373f8 100644 --- a/Source/Core/InputCommon/ControllerInterface/DInput/DInputJoystick.h +++ b/Source/Core/InputCommon/ControllerInterface/DInput/DInputJoystick.h @@ -57,7 +57,7 @@ private: }; public: - void UpdateInput() override; + Core::DeviceRemoval UpdateInput() override; Joystick(const LPDIRECTINPUTDEVICE8 device); ~Joystick(); diff --git a/Source/Core/InputCommon/ControllerInterface/DInput/DInputKeyboardMouse.cpp b/Source/Core/InputCommon/ControllerInterface/DInput/DInputKeyboardMouse.cpp index 551afde423..3c6a82dcd8 100644 --- a/Source/Core/InputCommon/ControllerInterface/DInput/DInputKeyboardMouse.cpp +++ b/Source/Core/InputCommon/ControllerInterface/DInput/DInputKeyboardMouse.cpp @@ -205,7 +205,7 @@ void KeyboardMouse::UpdateCursorInput() m_state_in.cursor.y = (ControlState(point.y) / win_height * 2 - 1) * window_scale.y; } -void KeyboardMouse::UpdateInput() +Core::DeviceRemoval KeyboardMouse::UpdateInput() { UpdateCursorInput(); @@ -254,6 +254,8 @@ void KeyboardMouse::UpdateInput() else INFO_LOG_FMT(CONTROLLERINTERFACE, "Keyboard device failed to re-acquire, we'll retry later"); } + + return Core::DeviceRemoval::Keep; } std::string KeyboardMouse::GetName() const diff --git a/Source/Core/InputCommon/ControllerInterface/DInput/DInputKeyboardMouse.h b/Source/Core/InputCommon/ControllerInterface/DInput/DInputKeyboardMouse.h index eba4c8ab93..e7187849f6 100644 --- a/Source/Core/InputCommon/ControllerInterface/DInput/DInputKeyboardMouse.h +++ b/Source/Core/InputCommon/ControllerInterface/DInput/DInputKeyboardMouse.h @@ -94,7 +94,7 @@ private: }; public: - void UpdateInput() override; + Core::DeviceRemoval UpdateInput() override; KeyboardMouse(const LPDIRECTINPUTDEVICE8 kb_device, const LPDIRECTINPUTDEVICE8 mo_device); ~KeyboardMouse(); diff --git a/Source/Core/InputCommon/ControllerInterface/DualShockUDPClient/DualShockUDPClient.cpp b/Source/Core/InputCommon/ControllerInterface/DualShockUDPClient/DualShockUDPClient.cpp index d3030a60fa..8a5abb16d9 100644 --- a/Source/Core/InputCommon/ControllerInterface/DualShockUDPClient/DualShockUDPClient.cpp +++ b/Source/Core/InputCommon/ControllerInterface/DualShockUDPClient/DualShockUDPClient.cpp @@ -128,7 +128,7 @@ private: }; public: - void UpdateInput() override; + Core::DeviceRemoval UpdateInput() override; Device(std::string name, int index, std::string server_address, u16 server_port, u32 client_uid); @@ -614,7 +614,7 @@ std::string Device::GetSource() const return std::string(DUALSHOCKUDP_SOURCE_NAME); } -void Device::UpdateInput() +Core::DeviceRemoval Device::UpdateInput() { // Regularly tell the UDP server to feed us controller data const auto now = SteadyClock::now(); @@ -660,6 +660,8 @@ void Device::UpdateInput() m_prev_touch_valid = true; } } + + return Core::DeviceRemoval::Keep; } std::optional Device::GetPreferredId() const diff --git a/Source/Core/InputCommon/ControllerInterface/InputBackend.cpp b/Source/Core/InputCommon/ControllerInterface/InputBackend.cpp index 91685eea00..422d7e911c 100644 --- a/Source/Core/InputCommon/ControllerInterface/InputBackend.cpp +++ b/Source/Core/InputCommon/ControllerInterface/InputBackend.cpp @@ -12,7 +12,7 @@ InputBackend::InputBackend(ControllerInterface* controller_interface) InputBackend::~InputBackend() = default; -void InputBackend::UpdateInput() +void InputBackend::UpdateInput(std::vector>& devices_to_remove) { } diff --git a/Source/Core/InputCommon/ControllerInterface/InputBackend.h b/Source/Core/InputCommon/ControllerInterface/InputBackend.h index 653bc16df1..80ced7e194 100644 --- a/Source/Core/InputCommon/ControllerInterface/InputBackend.h +++ b/Source/Core/InputCommon/ControllerInterface/InputBackend.h @@ -3,10 +3,19 @@ #pragma once +#include +#include + class ControllerInterface; namespace ciface { + +namespace Core +{ +class Device; +} + class InputBackend { public: @@ -15,7 +24,9 @@ public: virtual ~InputBackend(); virtual void PopulateDevices() = 0; - virtual void UpdateInput(); + // Do NOT directly add/remove devices within here, + // just add them to the removal list if necessary. + virtual void UpdateInput(std::vector>& devices_to_remove); ControllerInterface& GetControllerInterface(); diff --git a/Source/Core/InputCommon/ControllerInterface/Pipes/Pipes.cpp b/Source/Core/InputCommon/ControllerInterface/Pipes/Pipes.cpp index 5c71adec23..af7ad7dc6f 100644 --- a/Source/Core/InputCommon/ControllerInterface/Pipes/Pipes.cpp +++ b/Source/Core/InputCommon/ControllerInterface/Pipes/Pipes.cpp @@ -86,7 +86,7 @@ PipeDevice::~PipeDevice() close(m_fd); } -void PipeDevice::UpdateInput() +Core::DeviceRemoval PipeDevice::UpdateInput() { // Read any pending characters off the pipe. If we hit a newline, // then dequeue a command off the front of m_buf and parse it. @@ -105,6 +105,7 @@ void PipeDevice::UpdateInput() m_buf.erase(0, newline + 1); newline = m_buf.find("\n"); } + return Core::DeviceRemoval::Keep; } void PipeDevice::AddAxis(const std::string& name, double value) diff --git a/Source/Core/InputCommon/ControllerInterface/Pipes/Pipes.h b/Source/Core/InputCommon/ControllerInterface/Pipes/Pipes.h index ab9e987c32..53fefd0cb5 100644 --- a/Source/Core/InputCommon/ControllerInterface/Pipes/Pipes.h +++ b/Source/Core/InputCommon/ControllerInterface/Pipes/Pipes.h @@ -29,7 +29,7 @@ public: PipeDevice(int fd, const std::string& name); ~PipeDevice(); - void UpdateInput() override; + Core::DeviceRemoval UpdateInput() override; std::string GetName() const override { return m_name; } std::string GetSource() const override { return "Pipe"; } diff --git a/Source/Core/InputCommon/ControllerInterface/Quartz/QuartzKeyboardAndMouse.h b/Source/Core/InputCommon/ControllerInterface/Quartz/QuartzKeyboardAndMouse.h index e4bdb6e5fb..07292f9039 100644 --- a/Source/Core/InputCommon/ControllerInterface/Quartz/QuartzKeyboardAndMouse.h +++ b/Source/Core/InputCommon/ControllerInterface/Quartz/QuartzKeyboardAndMouse.h @@ -62,7 +62,7 @@ private: }; public: - void UpdateInput() override; + Core::DeviceRemoval UpdateInput() override; explicit KeyboardAndMouse(void* view); ~KeyboardAndMouse() override; diff --git a/Source/Core/InputCommon/ControllerInterface/Quartz/QuartzKeyboardAndMouse.mm b/Source/Core/InputCommon/ControllerInterface/Quartz/QuartzKeyboardAndMouse.mm index 5ea4ff2624..e41c370edf 100644 --- a/Source/Core/InputCommon/ControllerInterface/Quartz/QuartzKeyboardAndMouse.mm +++ b/Source/Core/InputCommon/ControllerInterface/Quartz/QuartzKeyboardAndMouse.mm @@ -236,7 +236,7 @@ void KeyboardAndMouse::MainThreadInitialization(void* view) m_window_pos_observer = [[DolWindowPositionObserver alloc] initWithView:cocoa_view]; } -void KeyboardAndMouse::UpdateInput() +Core::DeviceRemoval KeyboardAndMouse::UpdateInput() { NSRect bounds = [m_window_pos_observer frame]; @@ -268,6 +268,8 @@ void KeyboardAndMouse::UpdateInput() m_cursor.x = (loc.x / window_width * 2 - 1.0) * window_scale.x; m_cursor.y = (loc.y / window_height * 2 - 1.0) * -window_scale.y; } + + return Core::DeviceRemoval::Keep; } std::string KeyboardAndMouse::GetName() const diff --git a/Source/Core/InputCommon/ControllerInterface/SDL/SDL.cpp b/Source/Core/InputCommon/ControllerInterface/SDL/SDL.cpp index ac0123aedd..3bf4020ad5 100644 --- a/Source/Core/InputCommon/ControllerInterface/SDL/SDL.cpp +++ b/Source/Core/InputCommon/ControllerInterface/SDL/SDL.cpp @@ -18,6 +18,11 @@ #include #endif +namespace ciface::Core +{ +class Device; +} + namespace ciface::SDL { static std::string GetJoystickName(int index) @@ -35,7 +40,7 @@ public: InputBackend(ControllerInterface* controller_interface); ~InputBackend(); void PopulateDevices() override; - void UpdateInput() override; + void UpdateInput(std::vector>& devices_to_remove) override; private: void OpenAndAddDevice(int index); @@ -637,7 +642,7 @@ void Joystick::Motor::SetState(ControlState state) } #endif -void InputBackend::UpdateInput() +void InputBackend::UpdateInput(std::vector>& devices_to_remove) { SDL_JoystickUpdate(); } diff --git a/Source/Core/InputCommon/ControllerInterface/SteamDeck/SteamDeck.cpp b/Source/Core/InputCommon/ControllerInterface/SteamDeck/SteamDeck.cpp index 0a0dc88387..7cd76d0710 100644 --- a/Source/Core/InputCommon/ControllerInterface/SteamDeck/SteamDeck.cpp +++ b/Source/Core/InputCommon/ControllerInterface/SteamDeck/SteamDeck.cpp @@ -113,7 +113,7 @@ public: Device(hid_device* device); std::string GetName() const final override; std::string GetSource() const final override; - void UpdateInput() override; + Core::DeviceRemoval UpdateInput() override; private: hid_device* m_device; @@ -279,7 +279,7 @@ std::string Device::GetSource() const return std::string(STEAMDECK_SOURCE_NAME); } -void Device::UpdateInput() +Core::DeviceRemoval Device::UpdateInput() { // As of a certain mid-2023 update to the Steam client, // Steam will disable gyro data if gyro is not mapped in Steam Input. @@ -308,16 +308,18 @@ void Device::UpdateInput() } // In case there were no reports available to be read, bail early. if (!got_anything) - return; + return Core::DeviceRemoval::Keep; if (rpt.major_ver != 0x01 || rpt.minor_ver != 0x00 || rpt.report_type != 0x09 || rpt.report_sz != sizeof(rpt)) { ERROR_LOG_FMT(CONTROLLERINTERFACE, "Steam Deck bad report"); - return; + return Core::DeviceRemoval::Keep; } m_latest_input = rpt; + + return Core::DeviceRemoval::Keep; } } // namespace ciface::SteamDeck diff --git a/Source/Core/InputCommon/ControllerInterface/WGInput/WGInput.cpp b/Source/Core/InputCommon/ControllerInterface/WGInput/WGInput.cpp index b31c5f1f8e..14c1072992 100644 --- a/Source/Core/InputCommon/ControllerInterface/WGInput/WGInput.cpp +++ b/Source/Core/InputCommon/ControllerInterface/WGInput/WGInput.cpp @@ -490,7 +490,7 @@ private: std::string GetSource() const override { return std::string(SOURCE_NAME); } - void UpdateInput() override + Core::DeviceRemoval UpdateInput() override { // IRawGameController: static_assert(sizeof(bool) == sizeof(ButtonValueType)); @@ -527,6 +527,8 @@ private: // IGameControllerBatteryInfo: if (!UpdateBatteryLevel()) DEBUG_LOG_FMT(CONTROLLERINTERFACE, "WGInput: UpdateBatteryLevel failed."); + + return Core::DeviceRemoval::Keep; } void UpdateMotors() diff --git a/Source/Core/InputCommon/ControllerInterface/Wiimote/WiimoteController.cpp b/Source/Core/InputCommon/ControllerInterface/Wiimote/WiimoteController.cpp index 6b92bb9b16..bc2db53328 100644 --- a/Source/Core/InputCommon/ControllerInterface/Wiimote/WiimoteController.cpp +++ b/Source/Core/InputCommon/ControllerInterface/Wiimote/WiimoteController.cpp @@ -1395,14 +1395,10 @@ void Device::UpdateRumble() QueueReport(OutputReportRumble{}); } -void Device::UpdateInput() +Core::DeviceRemoval Device::UpdateInput() { if (!m_wiimote->IsConnected()) - { - g_controller_interface.RemoveDevice( - [this](const Core::Device* device) { return device == this; }); - return; - } + return Core::DeviceRemoval::Remove; UpdateRumble(); RunTasks(); @@ -1413,6 +1409,8 @@ void Device::UpdateInput() ProcessInputReport(report); RunTasks(); } + + return Core::DeviceRemoval::Keep; } void Device::MotionPlusState::ProcessData(const WiimoteEmu::MotionPlus::DataFormat& data) diff --git a/Source/Core/InputCommon/ControllerInterface/Wiimote/WiimoteController.h b/Source/Core/InputCommon/ControllerInterface/Wiimote/WiimoteController.h index f650f68b46..bbc07e58be 100644 --- a/Source/Core/InputCommon/ControllerInterface/Wiimote/WiimoteController.h +++ b/Source/Core/InputCommon/ControllerInterface/Wiimote/WiimoteController.h @@ -35,7 +35,7 @@ public: std::string GetSource() const override; int GetSortPriority() const override; - void UpdateInput() override; + Core::DeviceRemoval UpdateInput() override; private: using Clock = std::chrono::steady_clock; diff --git a/Source/Core/InputCommon/ControllerInterface/XInput/XInput.cpp b/Source/Core/InputCommon/ControllerInterface/XInput/XInput.cpp index 82769cb4f4..ebe79f8984 100644 --- a/Source/Core/InputCommon/ControllerInterface/XInput/XInput.cpp +++ b/Source/Core/InputCommon/ControllerInterface/XInput/XInput.cpp @@ -264,7 +264,7 @@ std::string Device::GetSource() const return "XInput"; } -void Device::UpdateInput() +Core::DeviceRemoval Device::UpdateInput() { PXInputGetState(m_index, &m_state_in); @@ -286,6 +286,8 @@ void Device::UpdateInput() break; } } + + return Core::DeviceRemoval::Keep; } void Device::UpdateMotors() diff --git a/Source/Core/InputCommon/ControllerInterface/XInput/XInput.h b/Source/Core/InputCommon/ControllerInterface/XInput/XInput.h index 1673f795ed..95fe6a3c6f 100644 --- a/Source/Core/InputCommon/ControllerInterface/XInput/XInput.h +++ b/Source/Core/InputCommon/ControllerInterface/XInput/XInput.h @@ -33,7 +33,7 @@ public: std::optional GetPreferredId() const override; int GetSortPriority() const override { return -2; } - void UpdateInput() override; + Core::DeviceRemoval UpdateInput() override; void UpdateMotors(); diff --git a/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.cpp b/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.cpp index d90139fe03..f041dd16a1 100644 --- a/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.cpp +++ b/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.cpp @@ -275,7 +275,7 @@ void KeyboardMouse::UpdateCursor(bool should_center_mouse) m_state.cursor.y = (win_y / win_height * 2 - 1) * window_scale.y; } -void KeyboardMouse::UpdateInput() +Core::DeviceRemoval KeyboardMouse::UpdateInput() { XFlush(m_display); @@ -369,6 +369,8 @@ void KeyboardMouse::UpdateInput() if (update_keyboard) XQueryKeymap(m_display, m_state.keyboard.data()); + + return Core::DeviceRemoval::Keep; } std::string KeyboardMouse::GetName() const diff --git a/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.h b/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.h index 6a4f8436f1..a8960c1d23 100644 --- a/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.h +++ b/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.h @@ -111,7 +111,7 @@ private: void UpdateCursor(bool should_center_mouse); public: - void UpdateInput() override; + Core::DeviceRemoval UpdateInput() override; KeyboardMouse(Window window, int opcode, int pointer_deviceid, int keyboard_deviceid, double scroll_increment); diff --git a/Source/Core/InputCommon/ControllerInterface/evdev/evdev.cpp b/Source/Core/InputCommon/ControllerInterface/evdev/evdev.cpp index cf811e5b41..6e9764fda9 100644 --- a/Source/Core/InputCommon/ControllerInterface/evdev/evdev.cpp +++ b/Source/Core/InputCommon/ControllerInterface/evdev/evdev.cpp @@ -674,7 +674,7 @@ void InputBackend::RemoveDevnodeObject(const std::string& node) m_devnode_objects.erase(node); } -void evdevDevice::UpdateInput() +Core::DeviceRemoval evdevDevice::UpdateInput() { // Run through all evdev events // libevdev will keep track of the actual controller state internally which can be queried @@ -691,6 +691,7 @@ void evdevDevice::UpdateInput() rc = libevdev_next_event(node.device, LIBEVDEV_READ_FLAG_NORMAL, &ev); } } + return Core::DeviceRemoval::Keep; } bool evdevDevice::IsValid() const diff --git a/Source/Core/InputCommon/ControllerInterface/evdev/evdev.h b/Source/Core/InputCommon/ControllerInterface/evdev/evdev.h index ff5ab8a72a..f32567af8c 100644 --- a/Source/Core/InputCommon/ControllerInterface/evdev/evdev.h +++ b/Source/Core/InputCommon/ControllerInterface/evdev/evdev.h @@ -72,7 +72,7 @@ private: }; public: - void UpdateInput() override; + Core::DeviceRemoval UpdateInput() override; bool IsValid() const override; evdevDevice(InputBackend* input_backend); From ddb4566a418c1d0cf2a0067241b1b288e4558c9f Mon Sep 17 00:00:00 2001 From: Tillmann Karras Date: Tue, 19 Dec 2023 15:07:15 +0000 Subject: [PATCH 03/21] VideoCommon: apply "force 24-bit color" to EFB-to-VRAM copies as well --- .../VideoCommon/TextureConverterShaderGen.cpp | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/Source/Core/VideoCommon/TextureConverterShaderGen.cpp b/Source/Core/VideoCommon/TextureConverterShaderGen.cpp index 92d30895fb..da2e247a7f 100644 --- a/Source/Core/VideoCommon/TextureConverterShaderGen.cpp +++ b/Source/Core/VideoCommon/TextureConverterShaderGen.cpp @@ -19,6 +19,26 @@ TCShaderUid GetShaderUid(EFBCopyFormat dst_format, bool is_depth_copy, bool is_i TCShaderUid out; UidData* const uid_data = out.GetUidData(); + if (g_ActiveConfig.bForceTrueColor) + { + // Increase the precision of EFB copies where it's likely to be safe. + switch (dst_format) + { + case EFBCopyFormat::RGB565: + // HACK: XFB is RGB8. + // Don't blindly do this in other places though, + // the enum value is used to identify XFB copies. + // The important thing here is that we need alpha = 1. + dst_format = EFBCopyFormat::XFB; + break; + case EFBCopyFormat::RGB5A3: + dst_format = EFBCopyFormat::RGBA8; + break; + default: + // Let's not touch the other formats for now, seems risky. + break; + } + } uid_data->dst_format = dst_format; uid_data->efb_has_alpha = bpmem.zcontrol.pixel_format == PixelFormat::RGBA6_Z24; uid_data->is_depth_copy = is_depth_copy; From d667fca8d32df385f7b7e2a9d19cdb00bb24fbe6 Mon Sep 17 00:00:00 2001 From: BlakDulz Date: Sat, 23 Dec 2023 20:42:00 +0700 Subject: [PATCH 04/21] Implement Refresh on DocumentProvider "When interacting with DocumentUI or the built-in Android System Internal Files Manager app and performing Create, Rename, and Delete operations, DocumentsUI will not automatically refresh the changes. Previously, users had to manually pull down from the top to refresh the changes. This commit aims to fix this issue by automatically notifying the system that changes have occurred and triggering a requery." --- .../dolphinemu/features/DocumentProvider.kt | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/DocumentProvider.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/DocumentProvider.kt index b637962177..434b344417 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/DocumentProvider.kt +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/DocumentProvider.kt @@ -9,8 +9,10 @@ package org.dolphinemu.dolphinemu.features import android.annotation.TargetApi +import android.content.Context import android.database.Cursor import android.database.MatrixCursor +import android.net.Uri import android.os.Build import android.os.CancellationSignal import android.os.ParcelFileDescriptor @@ -93,6 +95,8 @@ class DocumentProvider : DocumentsProvider() { appendDocument(file, result) } } + result.setNotificationUri(context!!.contentResolver, DocumentsContract.buildChildDocumentsUri( + "${context!!.packageName}.user", parentDocumentId)) return result } @@ -121,6 +125,7 @@ class DocumentProvider : DocumentsProvider() { } else { file.createNewFile() } + refreshDocument(parentDocumentId) return pathToDocumentId(file) } @@ -128,7 +133,9 @@ class DocumentProvider : DocumentsProvider() { rootDirectory ?: return val file = documentIdToPath(documentId) + val fileParent = file.parentFile file.deleteRecursively() + refreshDocument(pathToDocumentId(fileParent!!)) } override fun renameDocument(documentId: String, displayName: String): String? { @@ -137,9 +144,19 @@ class DocumentProvider : DocumentsProvider() { val file = documentIdToPath(documentId) val dest = findFileNameForNewFile(File(file.parentFile, displayName)) file.renameTo(dest) + refreshDocument(pathToDocumentId(file.parentFile!!)) return pathToDocumentId(dest) } + private fun refreshDocument(parentDocumentId: String) { + val parentUri: Uri = + DocumentsContract.buildChildDocumentsUri( + "${context!!.packageName}.user", + parentDocumentId + ) + context!!.contentResolver.notifyChange(parentUri, null) + } + override fun isChildDocument(parentDocumentId: String, documentId: String): Boolean = documentId.startsWith(parentDocumentId) From 45d3c41d68b466115e2fe877305f32cb271aa6c0 Mon Sep 17 00:00:00 2001 From: BlakDulz Date: Sun, 24 Dec 2023 15:34:10 +0700 Subject: [PATCH 05/21] Make DocumentProvider Support Thumbnail This will make DocumentUI or the built-in Android System Internal Files Manager app showing Thumbnail of Image file instead of image type icon. --- .../dolphinemu/features/DocumentProvider.kt | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/DocumentProvider.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/DocumentProvider.kt index 434b344417..63d72160c5 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/DocumentProvider.kt +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/DocumentProvider.kt @@ -9,9 +9,10 @@ package org.dolphinemu.dolphinemu.features import android.annotation.TargetApi -import android.content.Context +import android.content.res.AssetFileDescriptor import android.database.Cursor import android.database.MatrixCursor +import android.graphics.Point import android.net.Uri import android.os.Build import android.os.CancellationSignal @@ -29,7 +30,7 @@ class DocumentProvider : DocumentsProvider() { private var rootDirectory: File? = null companion object { - public const val ROOT_ID = "root" + const val ROOT_ID = "root" private val DEFAULT_ROOT_PROJECTION = arrayOf( DocumentsContract.Root.COLUMN_ROOT_ID, @@ -111,6 +112,16 @@ class DocumentProvider : DocumentsProvider() { return ParcelFileDescriptor.open(file, ParcelFileDescriptor.parseMode(mode)) } + override fun openDocumentThumbnail( + documentId: String, + sizeHint: Point, + signal: CancellationSignal + ): AssetFileDescriptor { + val file = documentIdToPath(documentId) + val pfd = ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY) + return AssetFileDescriptor(pfd, 0, AssetFileDescriptor.UNKNOWN_LENGTH) + } + override fun createDocument( parentDocumentId: String, mimeType: String, @@ -178,6 +189,10 @@ class DocumentProvider : DocumentsProvider() { } else { file.name } + val mimeType = getTypeForFile(file) + if (file.exists() && mimeType.startsWith("image/")) { + flags = flags or DocumentsContract.Document.FLAG_SUPPORTS_THUMBNAIL + } cursor.newRow().apply { add(DocumentsContract.Document.COLUMN_DOCUMENT_ID, pathToDocumentId(file)) add(DocumentsContract.Document.COLUMN_MIME_TYPE, getTypeForFile(file)) From dd1f5f9726c2610870b6481122d445e564ebe6e8 Mon Sep 17 00:00:00 2001 From: Dentomologist Date: Thu, 21 Dec 2023 15:24:31 -0800 Subject: [PATCH 06/21] NetKDRequestDevice: Fix use-after-free crash Explicitly shut down work queues in NetKDRequestDevice's destructor to prevent their threads from accessing members after they've been freed. This crash would occur sporadically if NetKDRequestDevice's periodic download or mail checks happened to overlap with emulation shutdown in the wrong way. An example sequence of events that could cause the crash: * m_scheduler_timer_thread queues a periodic Download event in m_scheduler_work_queue, then waits for m_shutdown_event. * A request to stop emulation results in s_ios being reset by the CPU thread. This triggers NetKDRequestDevice's destructor which sets m_shutdown_event and joins m_scheduler_timer_thread. * m_scheduler_timer_thread wakes from m_shutdown_event and returns from its thread function, ending the thread. * The CPU thread resumes execution at the end of NetKDRequestDevice's destructor and begins destroying NetKDRequestDevice's members in reverse declaration order. * m_http is declared after m_scheduler_work_queue and is therefore destroyed earlier. * m_scheduler_work_queue's destructor calls its Shutdown function, which by default finishes the work items in the queue. * The queued Download event calls KDDownload which calls m_http.Get() which calls Fetch() which passes garbage data from the freed m_curl into curl_easy_setopt(). * Curl promptly crashes. Shutting down the work queues manually in the destructor prevents the above because m_http and the other members don't get freed until after the queue threads finish. --- Source/Core/Core/IOS/Network/KD/NetKDRequest.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Source/Core/Core/IOS/Network/KD/NetKDRequest.cpp b/Source/Core/Core/IOS/Network/KD/NetKDRequest.cpp index 3d4dc35613..c2420a9dd1 100644 --- a/Source/Core/Core/IOS/Network/KD/NetKDRequest.cpp +++ b/Source/Core/Core/IOS/Network/KD/NetKDRequest.cpp @@ -190,6 +190,8 @@ NetKDRequestDevice::~NetKDRequestDevice() } m_scheduler_timer_thread.join(); + m_scheduler_work_queue.Shutdown(); + m_work_queue.Shutdown(); } void NetKDRequestDevice::Update() From ecf4f1b1f9309151ce82c2016181aa10d1a7f2de Mon Sep 17 00:00:00 2001 From: Dentomologist Date: Sun, 24 Dec 2023 14:55:11 -0800 Subject: [PATCH 07/21] NetKDRequestDevice: Fix nullptr dereference crash Keep a shared_ptr to NetKDTimeDevice inside NetKDRequestDevice. This allows the KDDownload task to finish its work without potentially trying to dereference nullptr, which can potentially come from either GetIOS() or GetDeviceByName() if EmulationKernel's destructor has started running. --- Source/Core/Core/IOS/IOS.cpp | 7 +++++-- .../Core/Core/IOS/Network/KD/NetKDRequest.cpp | 17 ++++++----------- Source/Core/Core/IOS/Network/KD/NetKDRequest.h | 6 +++++- 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/Source/Core/Core/IOS/IOS.cpp b/Source/Core/Core/IOS/IOS.cpp index 9e73644a22..35c06626a3 100644 --- a/Source/Core/Core/IOS/IOS.cpp +++ b/Source/Core/Core/IOS/IOS.cpp @@ -577,8 +577,11 @@ void EmulationKernel::AddStaticDevices() } if (HasFeature(features, Feature::KD)) { - AddDevice(std::make_unique(*this, "/dev/net/kd/request")); - AddDevice(std::make_unique(*this, "/dev/net/kd/time")); + constexpr auto time_device_name = "/dev/net/kd/time"; + AddDevice(std::make_unique(*this, time_device_name)); + const auto time_device = + std::static_pointer_cast(GetDeviceByName(time_device_name)); + AddDevice(std::make_unique(*this, "/dev/net/kd/request", time_device)); } if (HasFeature(features, Feature::NCD)) { diff --git a/Source/Core/Core/IOS/Network/KD/NetKDRequest.cpp b/Source/Core/Core/IOS/Network/KD/NetKDRequest.cpp index c2420a9dd1..56bda89f65 100644 --- a/Source/Core/Core/IOS/Network/KD/NetKDRequest.cpp +++ b/Source/Core/Core/IOS/Network/KD/NetKDRequest.cpp @@ -153,9 +153,10 @@ s32 NWC24MakeUserID(u64* nwc24_id, u32 hollywood_id, u16 id_ctr, HardwareModel h } } // Anonymous namespace -NetKDRequestDevice::NetKDRequestDevice(EmulationKernel& ios, const std::string& device_name) +NetKDRequestDevice::NetKDRequestDevice(EmulationKernel& ios, const std::string& device_name, + const std::shared_ptr& time_device) : EmulationDevice(ios, device_name), m_config{ios.GetFS()}, m_dl_list{ios.GetFS()}, - m_send_list{ios.GetFS()}, m_friend_list{ios.GetFS()} + m_send_list{ios.GetFS()}, m_friend_list{ios.GetFS()}, m_time_device{time_device} { // Enable all NWC24 permissions m_scheduler_buffer[1] = Common::swap32(-1); @@ -443,9 +444,7 @@ NWC24::ErrorCode NetKDRequestDevice::DetermineDownloadTask(u16* entry_index, // As the scheduler does not tell us which entry to download, we must determine that. // A correct entry is one that hasn't been downloaded the longest compared to other entries. // We first need current UTC. - const auto time_device = - std::static_pointer_cast(GetIOS()->GetDeviceByName("/dev/net/kd/time")); - const u64 current_utc = time_device->GetAdjustedUTC(); + const u64 current_utc = m_time_device->GetAdjustedUTC(); u64 lowest_timestamp = std::numeric_limits::max(); for (u16 i = 0; i < static_cast(NWC24::NWC24Dl::MAX_ENTRIES); i++) @@ -495,9 +494,7 @@ NWC24::ErrorCode NetKDRequestDevice::DetermineSubtask(u16 entry_index, if (m_dl_list.IsSubtaskDownloadDisabled(entry_index)) return NWC24::WC24_ERR_DISABLED; - const auto time_device = - std::static_pointer_cast(GetIOS()->GetDeviceByName("/dev/net/kd/time")); - const u64 current_utc = time_device->GetAdjustedUTC(); + const u64 current_utc = m_time_device->GetAdjustedUTC(); for (u8 i = 0; i < 32; i++) { if (!m_dl_list.IsValidSubtask(entry_index, i)) @@ -647,9 +644,7 @@ NWC24::ErrorCode NetKDRequestDevice::KDDownload(const u16 entry_index, { bool success = false; Common::ScopeGuard state_guard([&] { - const auto time_device = - std::static_pointer_cast(GetIOS()->GetDeviceByName("/dev/net/kd/time")); - const u64 current_utc = time_device->GetAdjustedUTC(); + const u64 current_utc = m_time_device->GetAdjustedUTC(); if (success) { // Set the next download time to the dl_margin diff --git a/Source/Core/Core/IOS/Network/KD/NetKDRequest.h b/Source/Core/Core/IOS/Network/KD/NetKDRequest.h index 0fcd43f52d..848454fa71 100644 --- a/Source/Core/Core/IOS/Network/KD/NetKDRequest.h +++ b/Source/Core/Core/IOS/Network/KD/NetKDRequest.h @@ -4,6 +4,7 @@ #pragma once #include +#include #include #include @@ -17,6 +18,7 @@ #include "Core/IOS/Network/KD/Mail/WC24Send.h" #include "Core/IOS/Network/KD/NWC24Config.h" #include "Core/IOS/Network/KD/NWC24DL.h" +#include "Core/IOS/Network/KD/NetKDTime.h" namespace IOS::HLE { @@ -26,7 +28,8 @@ namespace IOS::HLE class NetKDRequestDevice : public EmulationDevice { public: - NetKDRequestDevice(EmulationKernel& ios, const std::string& device_name); + NetKDRequestDevice(EmulationKernel& ios, const std::string& device_name, + const std::shared_ptr& time_device); IPCReply HandleNWC24DownloadNowEx(const IOCtlRequest& request); NWC24::ErrorCode KDDownload(const u16 entry_index, const std::optional subtask_id); IPCReply HandleNWC24CheckMailNow(const IOCtlRequest& request); @@ -114,6 +117,7 @@ private: std::queue m_async_replies; u32 m_error_count = 0; std::array m_scheduler_buffer{}; + std::shared_ptr m_time_device; // TODO: Maybe move away from Common::HttpRequest? Common::HttpRequest m_http{std::chrono::minutes{1}}; u32 m_download_span = 2; From 8fcf9969ebd53d6e5e13fa70e5d034ccb13af6ad Mon Sep 17 00:00:00 2001 From: JosJuice Date: Wed, 27 Dec 2023 16:39:00 +0100 Subject: [PATCH 08/21] Jit: Reload RMEM/MEM_REG on ISI exception Aims to fix https://bugs.dolphin-emu.org/issues/13444. --- Source/Core/Core/PowerPC/Jit64/JitAsm.cpp | 3 +++ Source/Core/Core/PowerPC/JitArm64/JitAsm.cpp | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/Source/Core/Core/PowerPC/Jit64/JitAsm.cpp b/Source/Core/Core/PowerPC/Jit64/JitAsm.cpp index 525d65cf70..7b0e3e8242 100644 --- a/Source/Core/Core/PowerPC/Jit64/JitAsm.cpp +++ b/Source/Core/Core/PowerPC/Jit64/JitAsm.cpp @@ -221,6 +221,9 @@ void Jit64AsmRoutineManager::Generate() ABI_CallFunction(JitTrampoline); ABI_PopRegistersAndAdjustStack({}, 0); + // If jitting triggered an ISI exception, MSR.DR may have changed + MOV(64, R(RMEM), PPCSTATE(mem_ptr)); + JMP(dispatcher_no_check, Jump::Near); SetJumpTarget(bail); diff --git a/Source/Core/Core/PowerPC/JitArm64/JitAsm.cpp b/Source/Core/Core/PowerPC/JitArm64/JitAsm.cpp index 87f8f3ae2e..cd93fccebf 100644 --- a/Source/Core/Core/PowerPC/JitArm64/JitAsm.cpp +++ b/Source/Core/Core/PowerPC/JitArm64/JitAsm.cpp @@ -177,7 +177,12 @@ void JitArm64::GenerateAsm() // Call JIT ResetStack(); ABI_CallFunction(&JitTrampoline, this, DISPATCHER_PC); + LDR(IndexType::Unsigned, DISPATCHER_PC, PPC_REG, PPCSTATE_OFF(pc)); + + // If jitting triggered an ISI exception, MSR.DR may have changed + EmitUpdateMembase(); + B(dispatcher_no_check); SetJumpTarget(bail); From 1c68f4231b98faa5a5b286f3029471220b388d0d Mon Sep 17 00:00:00 2001 From: Patrick Ferry <8967997+PatrickFerry@users.noreply.github.com> Date: Sat, 23 Dec 2023 23:31:52 +0000 Subject: [PATCH 09/21] Qt: Handle Overridden Graphics Backend This fixes an issue where the game specific graphics backend would be saved as the global setting after playing a game. This also now displays the currently running graphics backend when looking in the graphics configuration window. --- .../DolphinQt/Config/Graphics/GeneralWidget.cpp | 13 +++++++++++-- .../DolphinQt/Config/Graphics/GraphicsWindow.cpp | 1 - 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/Source/Core/DolphinQt/Config/Graphics/GeneralWidget.cpp b/Source/Core/DolphinQt/Config/Graphics/GeneralWidget.cpp index aa4a7c5563..5981c21348 100644 --- a/Source/Core/DolphinQt/Config/Graphics/GeneralWidget.cpp +++ b/Source/Core/DolphinQt/Config/Graphics/GeneralWidget.cpp @@ -182,7 +182,10 @@ void GeneralWidget::SaveSettings() { // Video Backend const auto current_backend = m_backend_combo->currentData().toString().toStdString(); - if (Config::Get(Config::MAIN_GFX_BACKEND) != current_backend) + if (Config::Get(Config::MAIN_GFX_BACKEND) == current_backend) + return; + + if (Config::GetActiveLayerForConfig(Config::MAIN_GFX_BACKEND) == Config::LayerType::Base) { auto warningMessage = VideoBackendBase::GetAvailableBackends()[m_backend_combo->currentIndex()] ->GetWarningMessage(); @@ -203,8 +206,10 @@ void GeneralWidget::SaveSettings() return; } } - emit BackendChanged(QString::fromStdString(current_backend)); } + + Config::SetBaseOrCurrent(Config::MAIN_GFX_BACKEND, current_backend); + emit BackendChanged(QString::fromStdString(current_backend)); } void GeneralWidget::OnEmulationStateChanged(bool running) @@ -215,6 +220,10 @@ void GeneralWidget::OnEmulationStateChanged(bool running) const bool supports_adapters = !g_Config.backend_info.Adapters.empty(); m_adapter_combo->setEnabled(!running && supports_adapters); + + std::string current_backend = m_backend_combo->currentData().toString().toStdString(); + if (Config::Get(Config::MAIN_GFX_BACKEND) != current_backend) + emit BackendChanged(QString::fromStdString(Config::Get(Config::MAIN_GFX_BACKEND))); } void GeneralWidget::AddDescriptions() diff --git a/Source/Core/DolphinQt/Config/Graphics/GraphicsWindow.cpp b/Source/Core/DolphinQt/Config/Graphics/GraphicsWindow.cpp index 7b31cc7f6e..4edbea1594 100644 --- a/Source/Core/DolphinQt/Config/Graphics/GraphicsWindow.cpp +++ b/Source/Core/DolphinQt/Config/Graphics/GraphicsWindow.cpp @@ -67,7 +67,6 @@ void GraphicsWindow::CreateMainLayout() void GraphicsWindow::OnBackendChanged(const QString& backend_name) { - Config::SetBase(Config::MAIN_GFX_BACKEND, backend_name.toStdString()); VideoBackendBase::PopulateBackendInfoFromUI(m_main_window->GetWindowSystemInfo()); setWindowTitle( From ca69c60e1bb74798410242f1d9f68b8079b59ace Mon Sep 17 00:00:00 2001 From: Tillmann Karras Date: Thu, 28 Dec 2023 23:57:42 +0100 Subject: [PATCH 10/21] DSPHLE/Zelda: add another 32-bit getter/setter (NFC) --- Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp index ec5f4ef2c6..16360bbccb 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp +++ b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp @@ -950,6 +950,7 @@ struct ReverbPB // Base address of the circular buffer in MRAM. u16 circular_buffer_base_h; u16 circular_buffer_base_l; + DEFINE_32BIT_ACCESSOR(circular_buffer_base, CircularBufferBase) struct Destination { @@ -1059,8 +1060,7 @@ void ZeldaAudioRenderer::ApplyReverb(bool post_rendering) u16 mram_buffer_idx = m_reverb_pb_frames_count[rpb_idx]; - u32 mram_addr = ((rpb.circular_buffer_base_h << 16) | rpb.circular_buffer_base_l) + - mram_buffer_idx * 0x50 * sizeof(s16); + u32 mram_addr = rpb.GetCircularBufferBase() + mram_buffer_idx * 0x50 * sizeof(s16); s16* mram_ptr = (s16*)HLEMemory_Get_Pointer(mram_addr); if (!post_rendering) From 594b55c448af3f610129374027a8c6dd149bb243 Mon Sep 17 00:00:00 2001 From: Tillmann Karras Date: Fri, 29 Dec 2023 02:24:37 +0100 Subject: [PATCH 11/21] DSPHLE/Zelda: fix use of wrong reverb buffer --- Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp index 16360bbccb..c6d2fdb907 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp +++ b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp @@ -991,7 +991,7 @@ void ZeldaAudioRenderer::PrepareFrame() 0xB820); AddBuffersWithVolume(m_buf_front_left_reverb.data(), m_buf_back_right_reverb.data() + 0x28, 0x28, 0xB820); - AddBuffersWithVolume(m_buf_front_right_reverb.data(), m_buf_back_left_reverb.data() + 0x28, 0x28, + AddBuffersWithVolume(m_buf_front_right_reverb.data(), m_buf_back_right_reverb.data() + 0x28, 0x28, 0x7FFF); m_buf_back_left_reverb.fill(0); m_buf_back_right_reverb.fill(0); From 0c7359e150f609f054f805723ede91f94e7cf344 Mon Sep 17 00:00:00 2001 From: JosJuice Date: Fri, 29 Dec 2023 14:11:35 +0100 Subject: [PATCH 12/21] Common: Fix encoding handling in GetWin32ErrorString These messages can be localized, so we can't just assume it's all ASCII. --- Source/Core/Common/CommonFuncs.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Source/Core/Common/CommonFuncs.cpp b/Source/Core/Common/CommonFuncs.cpp index 0a546428b7..aff87a9258 100644 --- a/Source/Core/Common/CommonFuncs.cpp +++ b/Source/Core/Common/CommonFuncs.cpp @@ -10,7 +10,10 @@ #ifdef _WIN32 #include + #define strerror_r(err, buf, len) strerror_s(buf, len, err) + +#include "Common/StringUtil.h" #endif namespace Common @@ -59,11 +62,11 @@ std::string GetLastErrorString() // Like GetLastErrorString() but if you have already queried the error code. std::string GetWin32ErrorString(DWORD error_code) { - char error_message[BUFFER_SIZE]; + wchar_t error_message[BUFFER_SIZE]; - FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, nullptr, error_code, + FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM, nullptr, error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), error_message, BUFFER_SIZE, nullptr); - return std::string(error_message); + return WStringToUTF8(error_message); } // Obtains a full path to the specified module. From 4f04ac5858052fe32598c4650b58e3e26b292c0e Mon Sep 17 00:00:00 2001 From: "Admiral H. Curtiss" Date: Fri, 29 Dec 2023 19:50:55 +0100 Subject: [PATCH 13/21] Common/StringUtil: Use internal linkage for codepage conversion functions. --- Source/Core/Common/StringUtil.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Core/Common/StringUtil.cpp b/Source/Core/Common/StringUtil.cpp index c9100ff11a..f960d6fcc6 100644 --- a/Source/Core/Common/StringUtil.cpp +++ b/Source/Core/Common/StringUtil.cpp @@ -426,7 +426,7 @@ size_t StringUTF8CodePointCount(std::string_view str) #ifdef _WIN32 -std::wstring CPToUTF16(u32 code_page, std::string_view input) +static std::wstring CPToUTF16(u32 code_page, std::string_view input) { auto const size = MultiByteToWideChar(code_page, 0, input.data(), static_cast(input.size()), nullptr, 0); @@ -444,7 +444,7 @@ std::wstring CPToUTF16(u32 code_page, std::string_view input) return output; } -std::string UTF16ToCP(u32 code_page, std::wstring_view input) +static std::string UTF16ToCP(u32 code_page, std::wstring_view input) { if (input.empty()) return {}; From 6b166f1819701a997e52e7fc740b3eb45524a758 Mon Sep 17 00:00:00 2001 From: luc-git <102831178+luc-git@users.noreply.github.com> Date: Tue, 28 Nov 2023 19:53:25 +0100 Subject: [PATCH 14/21] DolphinQt/Mapping: Add "Use Mouse Controlled Pointing" button. --- .../Config/Mapping/MappingWidget.cpp | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/Source/Core/DolphinQt/Config/Mapping/MappingWidget.cpp b/Source/Core/DolphinQt/Config/Mapping/MappingWidget.cpp index cfb9483ba6..f9c4ffb29c 100644 --- a/Source/Core/DolphinQt/Config/Mapping/MappingWidget.cpp +++ b/Source/Core/DolphinQt/Config/Mapping/MappingWidget.cpp @@ -3,6 +3,8 @@ #include "DolphinQt/Config/Mapping/MappingWidget.h" +#include + #include #include #include @@ -25,6 +27,7 @@ #include "InputCommon/ControllerEmu/ControllerEmu.h" #include "InputCommon/ControllerEmu/Setting/NumericSetting.h" #include "InputCommon/ControllerEmu/StickGate.h" +#include "InputCommon/ControllerInterface/ControllerInterface.h" MappingWidget::MappingWidget(MappingWindow* parent) : m_parent(parent) { @@ -160,6 +163,26 @@ QGroupBox* MappingWidget::CreateGroupBox(const QString& name, ControllerEmu::Con [this, group] { ShowAdvancedControlGroupDialog(group); }); } + if (group->type == ControllerEmu::GroupType::Cursor) + { + QPushButton* mouse_button = new QPushButton(tr("Use Mouse Controlled Pointing")); + form_layout->insertRow(2, mouse_button); + connect(mouse_button, &QCheckBox::clicked, [this, group] { + std::string default_device = g_controller_interface.GetDefaultDeviceString() + ":"; + const std::string controller_device = GetController()->GetDefaultDevice().ToString() + ":"; + if (default_device == controller_device) + { + default_device.clear(); + } + group->SetControlExpression(0, fmt::format("`{}Cursor Y-`", default_device)); + group->SetControlExpression(1, fmt::format("`{}Cursor Y+`", default_device)); + group->SetControlExpression(2, fmt::format("`{}Cursor X-`", default_device)); + group->SetControlExpression(3, fmt::format("`{}Cursor X+`", default_device)); + emit ConfigChanged(); + GetController()->UpdateReferences(g_controller_interface); + }); + } + return group_box; } From 684b3dfd4a2053ff4afe6ace8c09105afd3a0eab Mon Sep 17 00:00:00 2001 From: JosJuice Date: Sat, 30 Dec 2023 14:18:49 +0100 Subject: [PATCH 15/21] JitArm64: Don't fall back to interpreter on low DCBZ hack I missed this in 16eb188f1d. --- Source/Core/Core/PowerPC/JitArm64/JitArm64_LoadStore.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/Source/Core/Core/PowerPC/JitArm64/JitArm64_LoadStore.cpp b/Source/Core/Core/PowerPC/JitArm64/JitArm64_LoadStore.cpp index 1d8d3d1898..06d8d65b95 100644 --- a/Source/Core/Core/PowerPC/JitArm64/JitArm64_LoadStore.cpp +++ b/Source/Core/Core/PowerPC/JitArm64/JitArm64_LoadStore.cpp @@ -939,7 +939,6 @@ void JitArm64::dcbz(UGeckoInstruction inst) { INSTRUCTION_START JITDISABLE(bJITLoadStoreOff); - FALLBACK_IF(m_low_dcbz_hack); int a = inst.RA, b = inst.RB; From 465f17a882ce21023d4289b1d3e4059b19322e68 Mon Sep 17 00:00:00 2001 From: JosJuice Date: Thu, 28 Dec 2023 22:57:10 +0100 Subject: [PATCH 16/21] PowerPC: Add constants for the two TLB indices Just for readability. --- Source/Core/Core/PowerPC/MMU.cpp | 10 ++++++---- Source/Core/Core/PowerPC/PowerPC.h | 2 ++ 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/Source/Core/Core/PowerPC/MMU.cpp b/Source/Core/Core/PowerPC/MMU.cpp index fdd370acc4..4574657bad 100644 --- a/Source/Core/Core/PowerPC/MMU.cpp +++ b/Source/Core/Core/PowerPC/MMU.cpp @@ -1343,7 +1343,8 @@ static TLBLookupResult LookupTLBPageAddress(PowerPC::PowerPCState& ppc_state, u32* paddr, bool* wi) { const u32 tag = vpa >> HW_PAGE_INDEX_SHIFT; - TLBEntry& tlbe = ppc_state.tlb[IsOpcodeFlag(flag)][tag & HW_PAGE_INDEX_MASK]; + const size_t tlb_index = IsOpcodeFlag(flag) ? PowerPC::INST_TLB_INDEX : PowerPC::DATA_TLB_INDEX; + TLBEntry& tlbe = ppc_state.tlb[tlb_index][tag & HW_PAGE_INDEX_MASK]; if (tlbe.tag[0] == tag && tlbe.vsid[0] == vsid) { @@ -1401,7 +1402,8 @@ static void UpdateTLBEntry(PowerPC::PowerPCState& ppc_state, const XCheckTLBFlag return; const u32 tag = address >> HW_PAGE_INDEX_SHIFT; - TLBEntry& tlbe = ppc_state.tlb[IsOpcodeFlag(flag)][tag & HW_PAGE_INDEX_MASK]; + const size_t tlb_index = IsOpcodeFlag(flag) ? PowerPC::INST_TLB_INDEX : PowerPC::DATA_TLB_INDEX; + TLBEntry& tlbe = ppc_state.tlb[tlb_index][tag & HW_PAGE_INDEX_MASK]; const u32 index = tlbe.recent == 0 && tlbe.tag[0] != TLBEntry::INVALID_TAG; tlbe.recent = index; tlbe.paddr[index] = pte2.RPN << HW_PAGE_INDEX_SHIFT; @@ -1414,8 +1416,8 @@ void MMU::InvalidateTLBEntry(u32 address) { const u32 entry_index = (address >> HW_PAGE_INDEX_SHIFT) & HW_PAGE_INDEX_MASK; - m_ppc_state.tlb[0][entry_index].Invalidate(); - m_ppc_state.tlb[1][entry_index].Invalidate(); + m_ppc_state.tlb[PowerPC::DATA_TLB_INDEX][entry_index].Invalidate(); + m_ppc_state.tlb[PowerPC::INST_TLB_INDEX][entry_index].Invalidate(); } // Page Address Translation diff --git a/Source/Core/Core/PowerPC/PowerPC.h b/Source/Core/Core/PowerPC/PowerPC.h index afcc0ac8bf..c473f20fd0 100644 --- a/Source/Core/Core/PowerPC/PowerPC.h +++ b/Source/Core/Core/PowerPC/PowerPC.h @@ -53,6 +53,8 @@ enum class CoreMode constexpr size_t TLB_SIZE = 128; constexpr size_t NUM_TLBS = 2; constexpr size_t TLB_WAYS = 2; +constexpr size_t DATA_TLB_INDEX = 0; +constexpr size_t INST_TLB_INDEX = 1; struct TLBEntry { From 01e534a681b5ed308fa8fbbc46ccbea3b0589686 Mon Sep 17 00:00:00 2001 From: mitaclaw <140017135+mitaclaw@users.noreply.github.com> Date: Sat, 30 Dec 2023 23:44:27 -0800 Subject: [PATCH 17/21] Fix Logic Inefficiency in Arm64GPRCache::FlushRegisters This was introduced in 6a9f565ac4ddc65b2fdbf2293282ddd432ba89ae. --- Source/Core/Core/PowerPC/JitArm64/JitArm64_RegCache.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Source/Core/Core/PowerPC/JitArm64/JitArm64_RegCache.cpp b/Source/Core/Core/PowerPC/JitArm64/JitArm64_RegCache.cpp index 8538f2c040..cd270880f9 100644 --- a/Source/Core/Core/PowerPC/JitArm64/JitArm64_RegCache.cpp +++ b/Source/Core/Core/PowerPC/JitArm64/JitArm64_RegCache.cpp @@ -243,8 +243,9 @@ void Arm64GPRCache::FlushRegister(size_t index, bool maintain_state, ARM64Reg tm void Arm64GPRCache::FlushRegisters(BitSet32 regs, bool maintain_state, ARM64Reg tmp_reg) { - for (int i : regs) + for (auto iter = regs.begin(); iter != regs.end(); ++iter) { + const int i = *iter; ASSERT_MSG(DYNA_REC, m_guest_registers[GUEST_GPR_OFFSET + i].GetType() != RegType::Discarded, "Attempted to flush discarded register"); @@ -269,7 +270,7 @@ void Arm64GPRCache::FlushRegisters(BitSet32 regs, bool maintain_state, ARM64Reg reg1.Flush(); reg2.Flush(); } - ++i; + ++iter; continue; } } From 58c5ae3de92015f8b4c3ae4b63e23b8669798edc Mon Sep 17 00:00:00 2001 From: Dentomologist Date: Sun, 31 Dec 2023 02:02:22 -0800 Subject: [PATCH 18/21] UnitTests: Refactor BitSetTest Group numbers and their bitcounts together in pairs, which allows for range-based loop iteration. --- Source/UnitTests/Common/BitSetTest.cpp | 37 +++++++++++++++++--------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/Source/UnitTests/Common/BitSetTest.cpp b/Source/UnitTests/Common/BitSetTest.cpp index 21c211516c..6ec2b74bdb 100644 --- a/Source/UnitTests/Common/BitSetTest.cpp +++ b/Source/UnitTests/Common/BitSetTest.cpp @@ -1,6 +1,9 @@ // Copyright 2014 Dolphin Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include +#include + #include #include "Common/BitSet.h" @@ -29,23 +32,31 @@ TEST(BitSet, BitGetSet) TEST(BitSet, Count) { - u32 random_numbers[] = {0x2cb0b5f3, 0x81ab32a6, 0xd9030dc5, 0x325ffe26, 0xb2fcaee3, - 0x4ccf188a, 0xf8be36dc, 0xb2fcecd5, 0xb750c2e5, 0x31d19074, - 0xf267644a, 0xac00a719, 0x6d45f19b, 0xf7e91c5b, 0xf687e694, - 0x9057c24e, 0x5eb65c39, 0x85d3038b, 0x101f4e66, 0xc202d136}; - u32 counts[] = {17, 14, 14, 19, 20, 14, 20, 20, 16, 13, 16, 12, 18, 20, 18, 14, 18, 14, 14, 12}; - for (size_t i = 0; i < 20; i++) + constexpr std::array, 20> random_32bit_number_bitcount_pairs = { + {{0x2cb0b5f3, 17}, {0x81ab32a6, 14}, {0xd9030dc5, 14}, {0x325ffe26, 19}, {0xb2fcaee3, 20}, + {0x4ccf188a, 14}, {0xf8be36dc, 20}, {0xb2fcecd5, 20}, {0xb750c2e5, 16}, {0x31d19074, 13}, + {0xf267644a, 16}, {0xac00a719, 12}, {0x6d45f19b, 18}, {0xf7e91c5b, 20}, {0xf687e694, 18}, + {0x9057c24e, 14}, {0x5eb65c39, 18}, {0x85d3038b, 14}, {0x101f4e66, 14}, {0xc202d136, 12}}}; + for (const auto& [number, bitcount] : random_32bit_number_bitcount_pairs) { - EXPECT_EQ(counts[i], BitSet32(random_numbers[i]).Count()); + const auto bitset = BitSet32(number); + EXPECT_EQ(bitset.Count(), bitcount); } - u64 random_numbers_64[] = {0xf86cd6f6ef09d7d4ULL, 0x6f2d8533255ead3cULL, 0x9da7941e0e52b345ULL, - 0x06e4189be67d2b17ULL, 0x3eb0681f65cb6d25ULL, 0xccab8a7c74a51203ULL, - 0x09d470516694c64bULL, 0x38cd077e075c778fULL, 0xd69ebfa6355ebfdeULL}; - u32 counts_64[] = {39, 34, 31, 32, 33, 29, 27, 35, 43}; - for (size_t i = 0; i < 9; i++) + constexpr std::array, 9> random_64bit_number_bitcount_pairs = { + {{0xf86cd6f6ef09d7d4ULL, 39}, + {0x6f2d8533255ead3cULL, 34}, + {0x9da7941e0e52b345ULL, 31}, + {0x06e4189be67d2b17ULL, 32}, + {0x3eb0681f65cb6d25ULL, 33}, + {0xccab8a7c74a51203ULL, 29}, + {0x09d470516694c64bULL, 27}, + {0x38cd077e075c778fULL, 35}, + {0xd69ebfa6355ebfdeULL, 43}}}; + for (const auto& [number, bitcount] : random_64bit_number_bitcount_pairs) { - EXPECT_EQ(counts_64[i], BitSet64(random_numbers_64[i]).Count()); + const auto bitset = BitSet64(number); + EXPECT_EQ(bitset.Count(), bitcount); } } From abb484a1013c5042ff4029756dbbeeedce787457 Mon Sep 17 00:00:00 2001 From: Dentomologist Date: Sun, 31 Dec 2023 12:37:22 -0800 Subject: [PATCH 19/21] BitSet: Use direct initialization instead of c-style casts --- Source/Core/Common/BitSet.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/Core/Common/BitSet.h b/Source/Core/Common/BitSet.h index 5d2c88294d..cc658a19f7 100644 --- a/Source/Core/Common/BitSet.h +++ b/Source/Core/Common/BitSet.h @@ -98,15 +98,15 @@ public: constexpr BitSet(std::initializer_list init) { for (int bit : init) - m_val |= (IntTy)1 << bit; + m_val |= IntTy{1} << bit; } constexpr static BitSet AllTrue(size_t count) { - return BitSet(count == sizeof(IntTy) * 8 ? ~(IntTy)0 : (((IntTy)1 << count) - 1)); + return BitSet(count == sizeof(IntTy) * 8 ? ~IntTy{0} : ((IntTy{1} << count) - 1)); } - Ref operator[](size_t bit) { return Ref(this, (IntTy)1 << bit); } + Ref operator[](size_t bit) { return Ref(this, IntTy{1} << bit); } constexpr const Ref operator[](size_t bit) const { return (*const_cast(this))[bit]; } constexpr bool operator==(BitSet other) const { return m_val == other.m_val; } constexpr bool operator!=(BitSet other) const { return m_val != other.m_val; } From 7dbf463ddf4a89ec7158c955b421adb77670d37e Mon Sep 17 00:00:00 2001 From: Dentomologist Date: Sun, 31 Dec 2023 11:57:16 -0800 Subject: [PATCH 20/21] BitSet64: Fix iterator incrementation Use 1 of the same type as the stored value when shifting left. This prevents undefined behavior caused by shifting an int more than 31 bits. Previously iterator incrementation could either hang or prematurely report it had reached the end of the bitset. --- Source/Core/Common/BitSet.h | 2 +- Source/UnitTests/Common/BitSetTest.cpp | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/Source/Core/Common/BitSet.h b/Source/Core/Common/BitSet.h index 5d2c88294d..f14ebea9e2 100644 --- a/Source/Core/Common/BitSet.h +++ b/Source/Core/Common/BitSet.h @@ -73,7 +73,7 @@ public: else { int bit = std::countr_zero(m_val); - m_val &= ~(1 << bit); + m_val &= ~(IntTy{1} << bit); m_bit = bit; } return *this; diff --git a/Source/UnitTests/Common/BitSetTest.cpp b/Source/UnitTests/Common/BitSetTest.cpp index 6ec2b74bdb..397f9fecbe 100644 --- a/Source/UnitTests/Common/BitSetTest.cpp +++ b/Source/UnitTests/Common/BitSetTest.cpp @@ -41,6 +41,10 @@ TEST(BitSet, Count) { const auto bitset = BitSet32(number); EXPECT_EQ(bitset.Count(), bitcount); + u32 iterating_count = 0; + for (auto iter = bitset.begin(); iter != bitset.end(); ++iter) + ++iterating_count; + EXPECT_EQ(iterating_count, bitcount); } constexpr std::array, 9> random_64bit_number_bitcount_pairs = { @@ -57,6 +61,10 @@ TEST(BitSet, Count) { const auto bitset = BitSet64(number); EXPECT_EQ(bitset.Count(), bitcount); + u32 iterating_count = 0; + for (auto iter = bitset.begin(); iter != bitset.end(); ++iter) + ++iterating_count; + EXPECT_EQ(iterating_count, bitcount); } } From 381c2702f633e7413258bca2637b4301e231542e Mon Sep 17 00:00:00 2001 From: Tillmann Karras Date: Mon, 1 Jan 2024 21:45:28 +0000 Subject: [PATCH 21/21] DSPHLE/Zelda: fix reverb volume being multiplied by current volume twice --- Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp index c6d2fdb907..80b7ac72b9 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp +++ b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp @@ -1217,11 +1217,9 @@ void ZeldaAudioRenderer::AddVoice(u16 voice_id) // Compute reverb volume and ramp deltas. s16 reverb_volumes[4], reverb_volume_deltas[4]; - s16 reverb_volume_factor = - (vpb.dolby_volume_current * vpb.dolby_reverb_factor) >> (shift_factor - 1); for (size_t i = 0; i < 4; ++i) { - reverb_volumes[i] = (quadrant_volumes[i] * reverb_volume_factor) >> shift_factor; + reverb_volumes[i] = (quadrant_volumes[i] * vpb.dolby_reverb_factor) >> shift_factor; reverb_volume_deltas[i] = (volume_deltas[i] * vpb.dolby_reverb_factor) >> shift_factor; }