diff options
Diffstat (limited to 'src')
21 files changed, 351 insertions, 182 deletions
diff --git a/src/common/polyfill_thread.h b/src/common/polyfill_thread.h index b2c929d2f..b4c94a5fc 100644 --- a/src/common/polyfill_thread.h +++ b/src/common/polyfill_thread.h | |||
| @@ -41,17 +41,18 @@ bool StoppableTimedWait(std::stop_token token, const std::chrono::duration<Rep, | |||
| 41 | #include <chrono> | 41 | #include <chrono> |
| 42 | #include <condition_variable> | 42 | #include <condition_variable> |
| 43 | #include <functional> | 43 | #include <functional> |
| 44 | #include <list> | 44 | #include <map> |
| 45 | #include <memory> | 45 | #include <memory> |
| 46 | #include <mutex> | 46 | #include <mutex> |
| 47 | #include <optional> | 47 | #include <optional> |
| 48 | #include <thread> | 48 | #include <thread> |
| 49 | #include <type_traits> | 49 | #include <type_traits> |
| 50 | #include <utility> | ||
| 50 | 51 | ||
| 51 | namespace std { | 52 | namespace std { |
| 52 | namespace polyfill { | 53 | namespace polyfill { |
| 53 | 54 | ||
| 54 | using stop_state_callbacks = list<function<void()>>; | 55 | using stop_state_callback = size_t; |
| 55 | 56 | ||
| 56 | class stop_state { | 57 | class stop_state { |
| 57 | public: | 58 | public: |
| @@ -59,61 +60,69 @@ public: | |||
| 59 | ~stop_state() = default; | 60 | ~stop_state() = default; |
| 60 | 61 | ||
| 61 | bool request_stop() { | 62 | bool request_stop() { |
| 62 | stop_state_callbacks callbacks; | 63 | unique_lock lk{m_lock}; |
| 63 | 64 | ||
| 64 | { | 65 | if (m_stop_requested) { |
| 65 | scoped_lock lk{m_lock}; | 66 | // Already set, nothing to do. |
| 67 | return false; | ||
| 68 | } | ||
| 66 | 69 | ||
| 67 | if (m_stop_requested.load()) { | 70 | // Mark stop requested. |
| 68 | // Already set, nothing to do | 71 | m_stop_requested = true; |
| 69 | return false; | ||
| 70 | } | ||
| 71 | 72 | ||
| 72 | // Set as requested | 73 | while (!m_callbacks.empty()) { |
| 73 | m_stop_requested = true; | 74 | // Get an iterator to the first element. |
| 75 | const auto it = m_callbacks.begin(); | ||
| 74 | 76 | ||
| 75 | // Copy callback list | 77 | // Move the callback function out of the map. |
| 76 | callbacks = m_callbacks; | 78 | function<void()> f; |
| 77 | } | 79 | swap(it->second, f); |
| 80 | |||
| 81 | // Erase the now-empty map element. | ||
| 82 | m_callbacks.erase(it); | ||
| 78 | 83 | ||
| 79 | for (auto callback : callbacks) { | 84 | // Run the callback. |
| 80 | callback(); | 85 | if (f) { |
| 86 | f(); | ||
| 87 | } | ||
| 81 | } | 88 | } |
| 82 | 89 | ||
| 83 | return true; | 90 | return true; |
| 84 | } | 91 | } |
| 85 | 92 | ||
| 86 | bool stop_requested() const { | 93 | bool stop_requested() const { |
| 87 | return m_stop_requested.load(); | 94 | unique_lock lk{m_lock}; |
| 95 | return m_stop_requested; | ||
| 88 | } | 96 | } |
| 89 | 97 | ||
| 90 | stop_state_callbacks::const_iterator insert_callback(function<void()> f) { | 98 | stop_state_callback insert_callback(function<void()> f) { |
| 91 | stop_state_callbacks::const_iterator ret{}; | 99 | unique_lock lk{m_lock}; |
| 92 | bool should_run{}; | ||
| 93 | |||
| 94 | { | ||
| 95 | scoped_lock lk{m_lock}; | ||
| 96 | should_run = m_stop_requested.load(); | ||
| 97 | m_callbacks.push_front(f); | ||
| 98 | ret = m_callbacks.begin(); | ||
| 99 | } | ||
| 100 | 100 | ||
| 101 | if (should_run) { | 101 | if (m_stop_requested) { |
| 102 | f(); | 102 | // Stop already requested. Don't insert anything, |
| 103 | // just run the callback synchronously. | ||
| 104 | if (f) { | ||
| 105 | f(); | ||
| 106 | } | ||
| 107 | return 0; | ||
| 103 | } | 108 | } |
| 104 | 109 | ||
| 110 | // Insert the callback. | ||
| 111 | stop_state_callback ret = ++m_next_callback; | ||
| 112 | m_callbacks.emplace(ret, move(f)); | ||
| 105 | return ret; | 113 | return ret; |
| 106 | } | 114 | } |
| 107 | 115 | ||
| 108 | void remove_callback(stop_state_callbacks::const_iterator it) { | 116 | void remove_callback(stop_state_callback cb) { |
| 109 | scoped_lock lk{m_lock}; | 117 | unique_lock lk{m_lock}; |
| 110 | m_callbacks.erase(it); | 118 | m_callbacks.erase(cb); |
| 111 | } | 119 | } |
| 112 | 120 | ||
| 113 | private: | 121 | private: |
| 114 | mutex m_lock; | 122 | mutable recursive_mutex m_lock; |
| 115 | atomic<bool> m_stop_requested; | 123 | map<stop_state_callback, function<void()>> m_callbacks; |
| 116 | stop_state_callbacks m_callbacks; | 124 | stop_state_callback m_next_callback{0}; |
| 125 | bool m_stop_requested{false}; | ||
| 117 | }; | 126 | }; |
| 118 | 127 | ||
| 119 | } // namespace polyfill | 128 | } // namespace polyfill |
| @@ -223,7 +232,7 @@ public: | |||
| 223 | } | 232 | } |
| 224 | ~stop_callback() { | 233 | ~stop_callback() { |
| 225 | if (m_stop_state && m_callback) { | 234 | if (m_stop_state && m_callback) { |
| 226 | m_stop_state->remove_callback(*m_callback); | 235 | m_stop_state->remove_callback(m_callback); |
| 227 | } | 236 | } |
| 228 | } | 237 | } |
| 229 | 238 | ||
| @@ -234,7 +243,7 @@ public: | |||
| 234 | 243 | ||
| 235 | private: | 244 | private: |
| 236 | shared_ptr<polyfill::stop_state> m_stop_state; | 245 | shared_ptr<polyfill::stop_state> m_stop_state; |
| 237 | optional<polyfill::stop_state_callbacks::const_iterator> m_callback; | 246 | polyfill::stop_state_callback m_callback; |
| 238 | }; | 247 | }; |
| 239 | 248 | ||
| 240 | template <typename Callback> | 249 | template <typename Callback> |
diff --git a/src/input_common/helpers/joycon_protocol/calibration.cpp b/src/input_common/helpers/joycon_protocol/calibration.cpp index f6e7e97d5..d8f040f75 100644 --- a/src/input_common/helpers/joycon_protocol/calibration.cpp +++ b/src/input_common/helpers/joycon_protocol/calibration.cpp | |||
| @@ -13,33 +13,33 @@ CalibrationProtocol::CalibrationProtocol(std::shared_ptr<JoyconHandle> handle) | |||
| 13 | 13 | ||
| 14 | DriverResult CalibrationProtocol::GetLeftJoyStickCalibration(JoyStickCalibration& calibration) { | 14 | DriverResult CalibrationProtocol::GetLeftJoyStickCalibration(JoyStickCalibration& calibration) { |
| 15 | ScopedSetBlocking sb(this); | 15 | ScopedSetBlocking sb(this); |
| 16 | std::vector<u8> buffer; | ||
| 17 | DriverResult result{DriverResult::Success}; | 16 | DriverResult result{DriverResult::Success}; |
| 17 | JoystickLeftSpiCalibration spi_calibration{}; | ||
| 18 | bool has_user_calibration = false; | ||
| 18 | calibration = {}; | 19 | calibration = {}; |
| 19 | 20 | ||
| 20 | result = ReadSPI(CalAddr::USER_LEFT_MAGIC, sizeof(u16), buffer); | ||
| 21 | |||
| 22 | if (result == DriverResult::Success) { | 21 | if (result == DriverResult::Success) { |
| 23 | const bool has_user_calibration = buffer[0] == 0xB2 && buffer[1] == 0xA1; | 22 | result = HasUserCalibration(SpiAddress::USER_LEFT_MAGIC, has_user_calibration); |
| 24 | if (has_user_calibration) { | ||
| 25 | result = ReadSPI(CalAddr::USER_LEFT_DATA, 9, buffer); | ||
| 26 | } else { | ||
| 27 | result = ReadSPI(CalAddr::FACT_LEFT_DATA, 9, buffer); | ||
| 28 | } | ||
| 29 | } | 23 | } |
| 30 | 24 | ||
| 31 | if (result == DriverResult::Success) { | 25 | // Read User defined calibration |
| 32 | calibration.x.max = static_cast<u16>(((buffer[1] & 0x0F) << 8) | buffer[0]); | 26 | if (result == DriverResult::Success && has_user_calibration) { |
| 33 | calibration.y.max = static_cast<u16>((buffer[2] << 4) | (buffer[1] >> 4)); | 27 | result = ReadSPI(SpiAddress::USER_LEFT_DATA, spi_calibration); |
| 34 | calibration.x.center = static_cast<u16>(((buffer[4] & 0x0F) << 8) | buffer[3]); | ||
| 35 | calibration.y.center = static_cast<u16>((buffer[5] << 4) | (buffer[4] >> 4)); | ||
| 36 | calibration.x.min = static_cast<u16>(((buffer[7] & 0x0F) << 8) | buffer[6]); | ||
| 37 | calibration.y.min = static_cast<u16>((buffer[8] << 4) | (buffer[7] >> 4)); | ||
| 38 | } | 28 | } |
| 39 | 29 | ||
| 40 | // Nintendo fix for drifting stick | 30 | // Read Factory calibration |
| 41 | // result = ReadSPI(0x60, 0x86 ,buffer, 16); | 31 | if (result == DriverResult::Success && !has_user_calibration) { |
| 42 | // calibration.deadzone = (u16)((buffer[4] << 8) & 0xF00 | buffer[3]); | 32 | result = ReadSPI(SpiAddress::FACT_LEFT_DATA, spi_calibration); |
| 33 | } | ||
| 34 | |||
| 35 | if (result == DriverResult::Success) { | ||
| 36 | calibration.x.center = GetXAxisCalibrationValue(spi_calibration.center); | ||
| 37 | calibration.y.center = GetYAxisCalibrationValue(spi_calibration.center); | ||
| 38 | calibration.x.min = GetXAxisCalibrationValue(spi_calibration.min); | ||
| 39 | calibration.y.min = GetYAxisCalibrationValue(spi_calibration.min); | ||
| 40 | calibration.x.max = GetXAxisCalibrationValue(spi_calibration.max); | ||
| 41 | calibration.y.max = GetYAxisCalibrationValue(spi_calibration.max); | ||
| 42 | } | ||
| 43 | 43 | ||
| 44 | // Set a valid default calibration if data is missing | 44 | // Set a valid default calibration if data is missing |
| 45 | ValidateCalibration(calibration); | 45 | ValidateCalibration(calibration); |
| @@ -49,33 +49,33 @@ DriverResult CalibrationProtocol::GetLeftJoyStickCalibration(JoyStickCalibration | |||
| 49 | 49 | ||
| 50 | DriverResult CalibrationProtocol::GetRightJoyStickCalibration(JoyStickCalibration& calibration) { | 50 | DriverResult CalibrationProtocol::GetRightJoyStickCalibration(JoyStickCalibration& calibration) { |
| 51 | ScopedSetBlocking sb(this); | 51 | ScopedSetBlocking sb(this); |
| 52 | std::vector<u8> buffer; | ||
| 53 | DriverResult result{DriverResult::Success}; | 52 | DriverResult result{DriverResult::Success}; |
| 53 | JoystickRightSpiCalibration spi_calibration{}; | ||
| 54 | bool has_user_calibration = false; | ||
| 54 | calibration = {}; | 55 | calibration = {}; |
| 55 | 56 | ||
| 56 | result = ReadSPI(CalAddr::USER_RIGHT_MAGIC, sizeof(u16), buffer); | ||
| 57 | |||
| 58 | if (result == DriverResult::Success) { | 57 | if (result == DriverResult::Success) { |
| 59 | const bool has_user_calibration = buffer[0] == 0xB2 && buffer[1] == 0xA1; | 58 | result = HasUserCalibration(SpiAddress::USER_RIGHT_MAGIC, has_user_calibration); |
| 60 | if (has_user_calibration) { | ||
| 61 | result = ReadSPI(CalAddr::USER_RIGHT_DATA, 9, buffer); | ||
| 62 | } else { | ||
| 63 | result = ReadSPI(CalAddr::FACT_RIGHT_DATA, 9, buffer); | ||
| 64 | } | ||
| 65 | } | 59 | } |
| 66 | 60 | ||
| 67 | if (result == DriverResult::Success) { | 61 | // Read User defined calibration |
| 68 | calibration.x.center = static_cast<u16>(((buffer[1] & 0x0F) << 8) | buffer[0]); | 62 | if (result == DriverResult::Success && has_user_calibration) { |
| 69 | calibration.y.center = static_cast<u16>((buffer[2] << 4) | (buffer[1] >> 4)); | 63 | result = ReadSPI(SpiAddress::USER_RIGHT_DATA, spi_calibration); |
| 70 | calibration.x.min = static_cast<u16>(((buffer[4] & 0x0F) << 8) | buffer[3]); | 64 | } |
| 71 | calibration.y.min = static_cast<u16>((buffer[5] << 4) | (buffer[4] >> 4)); | 65 | |
| 72 | calibration.x.max = static_cast<u16>(((buffer[7] & 0x0F) << 8) | buffer[6]); | 66 | // Read Factory calibration |
| 73 | calibration.y.max = static_cast<u16>((buffer[8] << 4) | (buffer[7] >> 4)); | 67 | if (result == DriverResult::Success && !has_user_calibration) { |
| 68 | result = ReadSPI(SpiAddress::FACT_RIGHT_DATA, spi_calibration); | ||
| 74 | } | 69 | } |
| 75 | 70 | ||
| 76 | // Nintendo fix for drifting stick | 71 | if (result == DriverResult::Success) { |
| 77 | // buffer = ReadSPI(0x60, 0x98 , 16); | 72 | calibration.x.center = GetXAxisCalibrationValue(spi_calibration.center); |
| 78 | // joystick.deadzone = (u16)((buffer[4] << 8) & 0xF00 | buffer[3]); | 73 | calibration.y.center = GetYAxisCalibrationValue(spi_calibration.center); |
| 74 | calibration.x.min = GetXAxisCalibrationValue(spi_calibration.min); | ||
| 75 | calibration.y.min = GetYAxisCalibrationValue(spi_calibration.min); | ||
| 76 | calibration.x.max = GetXAxisCalibrationValue(spi_calibration.max); | ||
| 77 | calibration.y.max = GetYAxisCalibrationValue(spi_calibration.max); | ||
| 78 | } | ||
| 79 | 79 | ||
| 80 | // Set a valid default calibration if data is missing | 80 | // Set a valid default calibration if data is missing |
| 81 | ValidateCalibration(calibration); | 81 | ValidateCalibration(calibration); |
| @@ -85,39 +85,41 @@ DriverResult CalibrationProtocol::GetRightJoyStickCalibration(JoyStickCalibratio | |||
| 85 | 85 | ||
| 86 | DriverResult CalibrationProtocol::GetImuCalibration(MotionCalibration& calibration) { | 86 | DriverResult CalibrationProtocol::GetImuCalibration(MotionCalibration& calibration) { |
| 87 | ScopedSetBlocking sb(this); | 87 | ScopedSetBlocking sb(this); |
| 88 | std::vector<u8> buffer; | ||
| 89 | DriverResult result{DriverResult::Success}; | 88 | DriverResult result{DriverResult::Success}; |
| 89 | ImuSpiCalibration spi_calibration{}; | ||
| 90 | bool has_user_calibration = false; | ||
| 90 | calibration = {}; | 91 | calibration = {}; |
| 91 | 92 | ||
| 92 | result = ReadSPI(CalAddr::USER_IMU_MAGIC, sizeof(u16), buffer); | ||
| 93 | |||
| 94 | if (result == DriverResult::Success) { | 93 | if (result == DriverResult::Success) { |
| 95 | const bool has_user_calibration = buffer[0] == 0xB2 && buffer[1] == 0xA1; | 94 | result = HasUserCalibration(SpiAddress::USER_IMU_MAGIC, has_user_calibration); |
| 96 | if (has_user_calibration) { | 95 | } |
| 97 | result = ReadSPI(CalAddr::USER_IMU_DATA, sizeof(IMUCalibration), buffer); | 96 | |
| 98 | } else { | 97 | // Read User defined calibration |
| 99 | result = ReadSPI(CalAddr::FACT_IMU_DATA, sizeof(IMUCalibration), buffer); | 98 | if (result == DriverResult::Success && has_user_calibration) { |
| 100 | } | 99 | result = ReadSPI(SpiAddress::USER_IMU_DATA, spi_calibration); |
| 100 | } | ||
| 101 | |||
| 102 | // Read Factory calibration | ||
| 103 | if (result == DriverResult::Success && !has_user_calibration) { | ||
| 104 | result = ReadSPI(SpiAddress::FACT_IMU_DATA, spi_calibration); | ||
| 101 | } | 105 | } |
| 102 | 106 | ||
| 103 | if (result == DriverResult::Success) { | 107 | if (result == DriverResult::Success) { |
| 104 | IMUCalibration device_calibration{}; | 108 | calibration.accelerometer[0].offset = spi_calibration.accelerometer_offset[0]; |
| 105 | memcpy(&device_calibration, buffer.data(), sizeof(IMUCalibration)); | 109 | calibration.accelerometer[1].offset = spi_calibration.accelerometer_offset[1]; |
| 106 | calibration.accelerometer[0].offset = device_calibration.accelerometer_offset[0]; | 110 | calibration.accelerometer[2].offset = spi_calibration.accelerometer_offset[2]; |
| 107 | calibration.accelerometer[1].offset = device_calibration.accelerometer_offset[1]; | ||
| 108 | calibration.accelerometer[2].offset = device_calibration.accelerometer_offset[2]; | ||
| 109 | 111 | ||
| 110 | calibration.accelerometer[0].scale = device_calibration.accelerometer_scale[0]; | 112 | calibration.accelerometer[0].scale = spi_calibration.accelerometer_scale[0]; |
| 111 | calibration.accelerometer[1].scale = device_calibration.accelerometer_scale[1]; | 113 | calibration.accelerometer[1].scale = spi_calibration.accelerometer_scale[1]; |
| 112 | calibration.accelerometer[2].scale = device_calibration.accelerometer_scale[2]; | 114 | calibration.accelerometer[2].scale = spi_calibration.accelerometer_scale[2]; |
| 113 | 115 | ||
| 114 | calibration.gyro[0].offset = device_calibration.gyroscope_offset[0]; | 116 | calibration.gyro[0].offset = spi_calibration.gyroscope_offset[0]; |
| 115 | calibration.gyro[1].offset = device_calibration.gyroscope_offset[1]; | 117 | calibration.gyro[1].offset = spi_calibration.gyroscope_offset[1]; |
| 116 | calibration.gyro[2].offset = device_calibration.gyroscope_offset[2]; | 118 | calibration.gyro[2].offset = spi_calibration.gyroscope_offset[2]; |
| 117 | 119 | ||
| 118 | calibration.gyro[0].scale = device_calibration.gyroscope_scale[0]; | 120 | calibration.gyro[0].scale = spi_calibration.gyroscope_scale[0]; |
| 119 | calibration.gyro[1].scale = device_calibration.gyroscope_scale[1]; | 121 | calibration.gyro[1].scale = spi_calibration.gyroscope_scale[1]; |
| 120 | calibration.gyro[2].scale = device_calibration.gyroscope_scale[2]; | 122 | calibration.gyro[2].scale = spi_calibration.gyroscope_scale[2]; |
| 121 | } | 123 | } |
| 122 | 124 | ||
| 123 | ValidateCalibration(calibration); | 125 | ValidateCalibration(calibration); |
| @@ -127,10 +129,12 @@ DriverResult CalibrationProtocol::GetImuCalibration(MotionCalibration& calibrati | |||
| 127 | 129 | ||
| 128 | DriverResult CalibrationProtocol::GetRingCalibration(RingCalibration& calibration, | 130 | DriverResult CalibrationProtocol::GetRingCalibration(RingCalibration& calibration, |
| 129 | s16 current_value) { | 131 | s16 current_value) { |
| 132 | constexpr s16 DefaultRingRange{800}; | ||
| 133 | |||
| 130 | // TODO: Get default calibration form ring itself | 134 | // TODO: Get default calibration form ring itself |
| 131 | if (ring_data_max == 0 && ring_data_min == 0) { | 135 | if (ring_data_max == 0 && ring_data_min == 0) { |
| 132 | ring_data_max = current_value + 800; | 136 | ring_data_max = current_value + DefaultRingRange; |
| 133 | ring_data_min = current_value - 800; | 137 | ring_data_min = current_value - DefaultRingRange; |
| 134 | ring_data_default = current_value; | 138 | ring_data_default = current_value; |
| 135 | } | 139 | } |
| 136 | ring_data_max = std::max(ring_data_max, current_value); | 140 | ring_data_max = std::max(ring_data_max, current_value); |
| @@ -143,42 +147,72 @@ DriverResult CalibrationProtocol::GetRingCalibration(RingCalibration& calibratio | |||
| 143 | return DriverResult::Success; | 147 | return DriverResult::Success; |
| 144 | } | 148 | } |
| 145 | 149 | ||
| 150 | DriverResult CalibrationProtocol::HasUserCalibration(SpiAddress address, | ||
| 151 | bool& has_user_calibration) { | ||
| 152 | MagicSpiCalibration spi_magic{}; | ||
| 153 | const DriverResult result{ReadSPI(address, spi_magic)}; | ||
| 154 | has_user_calibration = false; | ||
| 155 | if (result == DriverResult::Success) { | ||
| 156 | has_user_calibration = spi_magic.first == CalibrationMagic::USR_MAGIC_0 && | ||
| 157 | spi_magic.second == CalibrationMagic::USR_MAGIC_1; | ||
| 158 | } | ||
| 159 | return result; | ||
| 160 | } | ||
| 161 | |||
| 162 | u16 CalibrationProtocol::GetXAxisCalibrationValue(std::span<u8> block) const { | ||
| 163 | return static_cast<u16>(((block[1] & 0x0F) << 8) | block[0]); | ||
| 164 | } | ||
| 165 | |||
| 166 | u16 CalibrationProtocol::GetYAxisCalibrationValue(std::span<u8> block) const { | ||
| 167 | return static_cast<u16>((block[2] << 4) | (block[1] >> 4)); | ||
| 168 | } | ||
| 169 | |||
| 146 | void CalibrationProtocol::ValidateCalibration(JoyStickCalibration& calibration) { | 170 | void CalibrationProtocol::ValidateCalibration(JoyStickCalibration& calibration) { |
| 147 | constexpr u16 DefaultStickCenter{2048}; | 171 | constexpr u16 DefaultStickCenter{0x800}; |
| 148 | constexpr u16 DefaultStickRange{1740}; | 172 | constexpr u16 DefaultStickRange{0x6cc}; |
| 149 | 173 | ||
| 150 | if (calibration.x.center == 0xFFF || calibration.x.center == 0) { | 174 | calibration.x.center = ValidateValue(calibration.x.center, DefaultStickCenter); |
| 151 | calibration.x.center = DefaultStickCenter; | 175 | calibration.x.max = ValidateValue(calibration.x.max, DefaultStickRange); |
| 152 | } | 176 | calibration.x.min = ValidateValue(calibration.x.min, DefaultStickRange); |
| 153 | if (calibration.x.max == 0xFFF || calibration.x.max == 0) { | 177 | |
| 154 | calibration.x.max = DefaultStickRange; | 178 | calibration.y.center = ValidateValue(calibration.y.center, DefaultStickCenter); |
| 179 | calibration.y.max = ValidateValue(calibration.y.max, DefaultStickRange); | ||
| 180 | calibration.y.min = ValidateValue(calibration.y.min, DefaultStickRange); | ||
| 181 | } | ||
| 182 | |||
| 183 | void CalibrationProtocol::ValidateCalibration(MotionCalibration& calibration) { | ||
| 184 | constexpr s16 DefaultAccelerometerScale{0x4000}; | ||
| 185 | constexpr s16 DefaultGyroScale{0x3be7}; | ||
| 186 | constexpr s16 DefaultOffset{0}; | ||
| 187 | |||
| 188 | for (auto& sensor : calibration.accelerometer) { | ||
| 189 | sensor.scale = ValidateValue(sensor.scale, DefaultAccelerometerScale); | ||
| 190 | sensor.offset = ValidateValue(sensor.offset, DefaultOffset); | ||
| 155 | } | 191 | } |
| 156 | if (calibration.x.min == 0xFFF || calibration.x.min == 0) { | 192 | for (auto& sensor : calibration.gyro) { |
| 157 | calibration.x.min = DefaultStickRange; | 193 | sensor.scale = ValidateValue(sensor.scale, DefaultGyroScale); |
| 194 | sensor.offset = ValidateValue(sensor.offset, DefaultOffset); | ||
| 158 | } | 195 | } |
| 196 | } | ||
| 159 | 197 | ||
| 160 | if (calibration.y.center == 0xFFF || calibration.y.center == 0) { | 198 | u16 CalibrationProtocol::ValidateValue(u16 value, u16 default_value) const { |
| 161 | calibration.y.center = DefaultStickCenter; | 199 | if (value == 0) { |
| 162 | } | 200 | return default_value; |
| 163 | if (calibration.y.max == 0xFFF || calibration.y.max == 0) { | ||
| 164 | calibration.y.max = DefaultStickRange; | ||
| 165 | } | 201 | } |
| 166 | if (calibration.y.min == 0xFFF || calibration.y.min == 0) { | 202 | if (value == 0xFFF) { |
| 167 | calibration.y.min = DefaultStickRange; | 203 | return default_value; |
| 168 | } | 204 | } |
| 205 | return value; | ||
| 169 | } | 206 | } |
| 170 | 207 | ||
| 171 | void CalibrationProtocol::ValidateCalibration(MotionCalibration& calibration) { | 208 | s16 CalibrationProtocol::ValidateValue(s16 value, s16 default_value) const { |
| 172 | for (auto& sensor : calibration.accelerometer) { | 209 | if (value == 0) { |
| 173 | if (sensor.scale == 0) { | 210 | return default_value; |
| 174 | sensor.scale = 0x4000; | ||
| 175 | } | ||
| 176 | } | 211 | } |
| 177 | for (auto& sensor : calibration.gyro) { | 212 | if (value == 0xFFF) { |
| 178 | if (sensor.scale == 0) { | 213 | return default_value; |
| 179 | sensor.scale = 0x3be7; | ||
| 180 | } | ||
| 181 | } | 214 | } |
| 215 | return value; | ||
| 182 | } | 216 | } |
| 183 | 217 | ||
| 184 | } // namespace InputCommon::Joycon | 218 | } // namespace InputCommon::Joycon |
diff --git a/src/input_common/helpers/joycon_protocol/calibration.h b/src/input_common/helpers/joycon_protocol/calibration.h index afb52a36a..c6fd0f729 100644 --- a/src/input_common/helpers/joycon_protocol/calibration.h +++ b/src/input_common/helpers/joycon_protocol/calibration.h | |||
| @@ -53,9 +53,27 @@ public: | |||
| 53 | DriverResult GetRingCalibration(RingCalibration& calibration, s16 current_value); | 53 | DriverResult GetRingCalibration(RingCalibration& calibration, s16 current_value); |
| 54 | 54 | ||
| 55 | private: | 55 | private: |
| 56 | /// Returns true if the specified address corresponds to the magic value of user calibration | ||
| 57 | DriverResult HasUserCalibration(SpiAddress address, bool& has_user_calibration); | ||
| 58 | |||
| 59 | /// Converts a raw calibration block to an u16 value containing the x axis value | ||
| 60 | u16 GetXAxisCalibrationValue(std::span<u8> block) const; | ||
| 61 | |||
| 62 | /// Converts a raw calibration block to an u16 value containing the y axis value | ||
| 63 | u16 GetYAxisCalibrationValue(std::span<u8> block) const; | ||
| 64 | |||
| 65 | /// Ensures that all joystick calibration values are set | ||
| 56 | void ValidateCalibration(JoyStickCalibration& calibration); | 66 | void ValidateCalibration(JoyStickCalibration& calibration); |
| 67 | |||
| 68 | /// Ensures that all motion calibration values are set | ||
| 57 | void ValidateCalibration(MotionCalibration& calibration); | 69 | void ValidateCalibration(MotionCalibration& calibration); |
| 58 | 70 | ||
| 71 | /// Returns the default value if the value is either zero or 0xFFF | ||
| 72 | u16 ValidateValue(u16 value, u16 default_value) const; | ||
| 73 | |||
| 74 | /// Returns the default value if the value is either zero or 0xFFF | ||
| 75 | s16 ValidateValue(s16 value, s16 default_value) const; | ||
| 76 | |||
| 59 | s16 ring_data_max = 0; | 77 | s16 ring_data_max = 0; |
| 60 | s16 ring_data_default = 0; | 78 | s16 ring_data_default = 0; |
| 61 | s16 ring_data_min = 0; | 79 | s16 ring_data_min = 0; |
diff --git a/src/input_common/helpers/joycon_protocol/common_protocol.cpp b/src/input_common/helpers/joycon_protocol/common_protocol.cpp index 417d0dcc5..0ef240344 100644 --- a/src/input_common/helpers/joycon_protocol/common_protocol.cpp +++ b/src/input_common/helpers/joycon_protocol/common_protocol.cpp | |||
| @@ -22,8 +22,8 @@ void JoyconCommonProtocol::SetNonBlocking() { | |||
| 22 | } | 22 | } |
| 23 | 23 | ||
| 24 | DriverResult JoyconCommonProtocol::GetDeviceType(ControllerType& controller_type) { | 24 | DriverResult JoyconCommonProtocol::GetDeviceType(ControllerType& controller_type) { |
| 25 | std::vector<u8> buffer; | 25 | std::array<u8, 1> buffer{}; |
| 26 | const auto result = ReadSPI(CalAddr::DEVICE_TYPE, 1, buffer); | 26 | const auto result = ReadRawSPI(SpiAddress::DEVICE_TYPE, buffer); |
| 27 | controller_type = ControllerType::None; | 27 | controller_type = ControllerType::None; |
| 28 | 28 | ||
| 29 | if (result == DriverResult::Success) { | 29 | if (result == DriverResult::Success) { |
| @@ -148,11 +148,13 @@ DriverResult JoyconCommonProtocol::SendVibrationReport(std::span<const u8> buffe | |||
| 148 | return SendData(local_buffer); | 148 | return SendData(local_buffer); |
| 149 | } | 149 | } |
| 150 | 150 | ||
| 151 | DriverResult JoyconCommonProtocol::ReadSPI(CalAddr addr, u8 size, std::vector<u8>& output) { | 151 | DriverResult JoyconCommonProtocol::ReadRawSPI(SpiAddress addr, std::span<u8> output) { |
| 152 | constexpr std::size_t HeaderSize = 20; | ||
| 152 | constexpr std::size_t MaxTries = 10; | 153 | constexpr std::size_t MaxTries = 10; |
| 154 | const auto size = output.size(); | ||
| 153 | std::size_t tries = 0; | 155 | std::size_t tries = 0; |
| 154 | std::array<u8, 5> buffer = {0x00, 0x00, 0x00, 0x00, size}; | 156 | std::array<u8, 5> buffer = {0x00, 0x00, 0x00, 0x00, static_cast<u8>(size)}; |
| 155 | std::vector<u8> local_buffer(size + 20); | 157 | std::vector<u8> local_buffer{}; |
| 156 | 158 | ||
| 157 | buffer[0] = static_cast<u8>(static_cast<u16>(addr) & 0x00FF); | 159 | buffer[0] = static_cast<u8>(static_cast<u16>(addr) & 0x00FF); |
| 158 | buffer[1] = static_cast<u8>((static_cast<u16>(addr) & 0xFF00) >> 8); | 160 | buffer[1] = static_cast<u8>((static_cast<u16>(addr) & 0xFF00) >> 8); |
| @@ -167,8 +169,12 @@ DriverResult JoyconCommonProtocol::ReadSPI(CalAddr addr, u8 size, std::vector<u8 | |||
| 167 | } | 169 | } |
| 168 | } while (local_buffer[15] != buffer[0] || local_buffer[16] != buffer[1]); | 170 | } while (local_buffer[15] != buffer[0] || local_buffer[16] != buffer[1]); |
| 169 | 171 | ||
| 172 | if (local_buffer.size() < size + HeaderSize) { | ||
| 173 | return DriverResult::WrongReply; | ||
| 174 | } | ||
| 175 | |||
| 170 | // Remove header from output | 176 | // Remove header from output |
| 171 | output = std::vector<u8>(local_buffer.begin() + 20, local_buffer.begin() + 20 + size); | 177 | memcpy(output.data(), local_buffer.data() + HeaderSize, size); |
| 172 | return DriverResult::Success; | 178 | return DriverResult::Success; |
| 173 | } | 179 | } |
| 174 | 180 | ||
diff --git a/src/input_common/helpers/joycon_protocol/common_protocol.h b/src/input_common/helpers/joycon_protocol/common_protocol.h index 903bcf402..75d3f20a4 100644 --- a/src/input_common/helpers/joycon_protocol/common_protocol.h +++ b/src/input_common/helpers/joycon_protocol/common_protocol.h | |||
| @@ -97,10 +97,29 @@ public: | |||
| 97 | /** | 97 | /** |
| 98 | * Reads the SPI memory stored on the joycon | 98 | * Reads the SPI memory stored on the joycon |
| 99 | * @param Initial address location | 99 | * @param Initial address location |
| 100 | * @param size in bytes to be read | ||
| 101 | * @returns output buffer containing the responce | 100 | * @returns output buffer containing the responce |
| 102 | */ | 101 | */ |
| 103 | DriverResult ReadSPI(CalAddr addr, u8 size, std::vector<u8>& output); | 102 | DriverResult ReadRawSPI(SpiAddress addr, std::span<u8> output); |
| 103 | |||
| 104 | /** | ||
| 105 | * Reads the SPI memory stored on the joycon | ||
| 106 | * @param Initial address location | ||
| 107 | * @returns output object containing the responce | ||
| 108 | */ | ||
| 109 | template <typename Output> | ||
| 110 | requires std::is_trivially_copyable_v<Output> DriverResult ReadSPI(SpiAddress addr, | ||
| 111 | Output& output) { | ||
| 112 | std::array<u8, sizeof(Output)> buffer; | ||
| 113 | output = {}; | ||
| 114 | |||
| 115 | const auto result = ReadRawSPI(addr, buffer); | ||
| 116 | if (result != DriverResult::Success) { | ||
| 117 | return result; | ||
| 118 | } | ||
| 119 | |||
| 120 | std::memcpy(&output, buffer.data(), sizeof(Output)); | ||
| 121 | return DriverResult::Success; | ||
| 122 | } | ||
| 104 | 123 | ||
| 105 | /** | 124 | /** |
| 106 | * Enables MCU chip on the joycon | 125 | * Enables MCU chip on the joycon |
diff --git a/src/input_common/helpers/joycon_protocol/generic_functions.cpp b/src/input_common/helpers/joycon_protocol/generic_functions.cpp index 63cfb1369..484c208e6 100644 --- a/src/input_common/helpers/joycon_protocol/generic_functions.cpp +++ b/src/input_common/helpers/joycon_protocol/generic_functions.cpp | |||
| @@ -71,8 +71,8 @@ DriverResult GenericProtocol::GetBattery(u32& battery_level) { | |||
| 71 | 71 | ||
| 72 | DriverResult GenericProtocol::GetColor(Color& color) { | 72 | DriverResult GenericProtocol::GetColor(Color& color) { |
| 73 | ScopedSetBlocking sb(this); | 73 | ScopedSetBlocking sb(this); |
| 74 | std::vector<u8> buffer; | 74 | std::array<u8, 12> buffer{}; |
| 75 | const auto result = ReadSPI(CalAddr::COLOR_DATA, 12, buffer); | 75 | const auto result = ReadRawSPI(SpiAddress::COLOR_DATA, buffer); |
| 76 | 76 | ||
| 77 | color = {}; | 77 | color = {}; |
| 78 | if (result == DriverResult::Success) { | 78 | if (result == DriverResult::Success) { |
| @@ -87,8 +87,8 @@ DriverResult GenericProtocol::GetColor(Color& color) { | |||
| 87 | 87 | ||
| 88 | DriverResult GenericProtocol::GetSerialNumber(SerialNumber& serial_number) { | 88 | DriverResult GenericProtocol::GetSerialNumber(SerialNumber& serial_number) { |
| 89 | ScopedSetBlocking sb(this); | 89 | ScopedSetBlocking sb(this); |
| 90 | std::vector<u8> buffer; | 90 | std::array<u8, 16> buffer{}; |
| 91 | const auto result = ReadSPI(CalAddr::SERIAL_NUMBER, 16, buffer); | 91 | const auto result = ReadRawSPI(SpiAddress::SERIAL_NUMBER, buffer); |
| 92 | 92 | ||
| 93 | serial_number = {}; | 93 | serial_number = {}; |
| 94 | if (result == DriverResult::Success) { | 94 | if (result == DriverResult::Success) { |
diff --git a/src/input_common/helpers/joycon_protocol/joycon_types.h b/src/input_common/helpers/joycon_protocol/joycon_types.h index 182d2c15b..14b07bfb5 100644 --- a/src/input_common/helpers/joycon_protocol/joycon_types.h +++ b/src/input_common/helpers/joycon_protocol/joycon_types.h | |||
| @@ -159,13 +159,12 @@ enum class UsbSubCommand : u8 { | |||
| 159 | SEND_UART = 0x92, | 159 | SEND_UART = 0x92, |
| 160 | }; | 160 | }; |
| 161 | 161 | ||
| 162 | enum class CalMagic : u8 { | 162 | enum class CalibrationMagic : u8 { |
| 163 | USR_MAGIC_0 = 0xB2, | 163 | USR_MAGIC_0 = 0xB2, |
| 164 | USR_MAGIC_1 = 0xA1, | 164 | USR_MAGIC_1 = 0xA1, |
| 165 | USRR_MAGI_SIZE = 2, | ||
| 166 | }; | 165 | }; |
| 167 | 166 | ||
| 168 | enum class CalAddr { | 167 | enum class SpiAddress { |
| 169 | SERIAL_NUMBER = 0X6000, | 168 | SERIAL_NUMBER = 0X6000, |
| 170 | DEVICE_TYPE = 0X6012, | 169 | DEVICE_TYPE = 0X6012, |
| 171 | COLOR_EXIST = 0X601B, | 170 | COLOR_EXIST = 0X601B, |
| @@ -396,10 +395,35 @@ struct MotionData { | |||
| 396 | u64 delta_timestamp{}; | 395 | u64 delta_timestamp{}; |
| 397 | }; | 396 | }; |
| 398 | 397 | ||
| 398 | // Output from SPI read command containing user calibration magic | ||
| 399 | struct MagicSpiCalibration { | ||
| 400 | CalibrationMagic first; | ||
| 401 | CalibrationMagic second; | ||
| 402 | }; | ||
| 403 | static_assert(sizeof(MagicSpiCalibration) == 0x2, "MagicSpiCalibration is an invalid size"); | ||
| 404 | |||
| 405 | // Output from SPI read command containing left joystick calibration | ||
| 406 | struct JoystickLeftSpiCalibration { | ||
| 407 | std::array<u8, 3> max; | ||
| 408 | std::array<u8, 3> center; | ||
| 409 | std::array<u8, 3> min; | ||
| 410 | }; | ||
| 411 | static_assert(sizeof(JoystickLeftSpiCalibration) == 0x9, | ||
| 412 | "JoystickLeftSpiCalibration is an invalid size"); | ||
| 413 | |||
| 414 | // Output from SPI read command containing right joystick calibration | ||
| 415 | struct JoystickRightSpiCalibration { | ||
| 416 | std::array<u8, 3> center; | ||
| 417 | std::array<u8, 3> min; | ||
| 418 | std::array<u8, 3> max; | ||
| 419 | }; | ||
| 420 | static_assert(sizeof(JoystickRightSpiCalibration) == 0x9, | ||
| 421 | "JoystickRightSpiCalibration is an invalid size"); | ||
| 422 | |||
| 399 | struct JoyStickAxisCalibration { | 423 | struct JoyStickAxisCalibration { |
| 400 | u16 max{1}; | 424 | u16 max; |
| 401 | u16 min{1}; | 425 | u16 min; |
| 402 | u16 center{0}; | 426 | u16 center; |
| 403 | }; | 427 | }; |
| 404 | 428 | ||
| 405 | struct JoyStickCalibration { | 429 | struct JoyStickCalibration { |
| @@ -407,6 +431,14 @@ struct JoyStickCalibration { | |||
| 407 | JoyStickAxisCalibration y; | 431 | JoyStickAxisCalibration y; |
| 408 | }; | 432 | }; |
| 409 | 433 | ||
| 434 | struct ImuSpiCalibration { | ||
| 435 | std::array<s16, 3> accelerometer_offset; | ||
| 436 | std::array<s16, 3> accelerometer_scale; | ||
| 437 | std::array<s16, 3> gyroscope_offset; | ||
| 438 | std::array<s16, 3> gyroscope_scale; | ||
| 439 | }; | ||
| 440 | static_assert(sizeof(ImuSpiCalibration) == 0x18, "ImuSpiCalibration is an invalid size"); | ||
| 441 | |||
| 410 | struct RingCalibration { | 442 | struct RingCalibration { |
| 411 | s16 default_value; | 443 | s16 default_value; |
| 412 | s16 max_value; | 444 | s16 max_value; |
| @@ -488,14 +520,6 @@ struct InputReportNfcIr { | |||
| 488 | static_assert(sizeof(InputReportNfcIr) == 0x29, "InputReportNfcIr is an invalid size"); | 520 | static_assert(sizeof(InputReportNfcIr) == 0x29, "InputReportNfcIr is an invalid size"); |
| 489 | #pragma pack(pop) | 521 | #pragma pack(pop) |
| 490 | 522 | ||
| 491 | struct IMUCalibration { | ||
| 492 | std::array<s16, 3> accelerometer_offset; | ||
| 493 | std::array<s16, 3> accelerometer_scale; | ||
| 494 | std::array<s16, 3> gyroscope_offset; | ||
| 495 | std::array<s16, 3> gyroscope_scale; | ||
| 496 | }; | ||
| 497 | static_assert(sizeof(IMUCalibration) == 0x18, "IMUCalibration is an invalid size"); | ||
| 498 | |||
| 499 | struct NFCReadBlock { | 523 | struct NFCReadBlock { |
| 500 | u8 start; | 524 | u8 start; |
| 501 | u8 end; | 525 | u8 end; |
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_image.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_image.cpp index cf6065208..b7bc11416 100644 --- a/src/shader_recompiler/backend/glasm/emit_glasm_image.cpp +++ b/src/shader_recompiler/backend/glasm/emit_glasm_image.cpp | |||
| @@ -532,7 +532,7 @@ void EmitImageFetch(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, | |||
| 532 | } | 532 | } |
| 533 | 533 | ||
| 534 | void EmitImageQueryDimensions(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, | 534 | void EmitImageQueryDimensions(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, |
| 535 | ScalarS32 lod) { | 535 | ScalarS32 lod, [[maybe_unused]] const IR::Value& skip_mips) { |
| 536 | const auto info{inst.Flags<IR::TextureInstInfo>()}; | 536 | const auto info{inst.Flags<IR::TextureInstInfo>()}; |
| 537 | const std::string texture{Texture(ctx, info, index)}; | 537 | const std::string texture{Texture(ctx, info, index)}; |
| 538 | const std::string_view type{TextureType(info)}; | 538 | const std::string_view type{TextureType(info)}; |
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_instructions.h b/src/shader_recompiler/backend/glasm/emit_glasm_instructions.h index 415a249e4..1a1ea61d5 100644 --- a/src/shader_recompiler/backend/glasm/emit_glasm_instructions.h +++ b/src/shader_recompiler/backend/glasm/emit_glasm_instructions.h | |||
| @@ -581,7 +581,7 @@ void EmitImageGatherDref(EmitContext& ctx, IR::Inst& inst, const IR::Value& inde | |||
| 581 | void EmitImageFetch(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, | 581 | void EmitImageFetch(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, |
| 582 | const IR::Value& coord, const IR::Value& offset, ScalarS32 lod, ScalarS32 ms); | 582 | const IR::Value& coord, const IR::Value& offset, ScalarS32 lod, ScalarS32 ms); |
| 583 | void EmitImageQueryDimensions(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, | 583 | void EmitImageQueryDimensions(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, |
| 584 | ScalarS32 lod); | 584 | ScalarS32 lod, const IR::Value& skip_mips); |
| 585 | void EmitImageQueryLod(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord); | 585 | void EmitImageQueryLod(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord); |
| 586 | void EmitImageGradient(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, | 586 | void EmitImageGradient(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, |
| 587 | const IR::Value& coord, const IR::Value& derivatives, | 587 | const IR::Value& coord, const IR::Value& derivatives, |
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp index d8874b0cc..4be2c25ec 100644 --- a/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp +++ b/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp | |||
| @@ -460,27 +460,27 @@ void EmitImageFetch(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, | |||
| 460 | } | 460 | } |
| 461 | 461 | ||
| 462 | void EmitImageQueryDimensions(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, | 462 | void EmitImageQueryDimensions(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, |
| 463 | std::string_view lod) { | 463 | std::string_view lod, const IR::Value& skip_mips_val) { |
| 464 | const auto info{inst.Flags<IR::TextureInstInfo>()}; | 464 | const auto info{inst.Flags<IR::TextureInstInfo>()}; |
| 465 | const auto texture{Texture(ctx, info, index)}; | 465 | const auto texture{Texture(ctx, info, index)}; |
| 466 | const bool skip_mips{skip_mips_val.U1()}; | ||
| 467 | const auto mips{ | ||
| 468 | [&] { return skip_mips ? "0u" : fmt::format("uint(textureQueryLevels({}))", texture); }}; | ||
| 466 | switch (info.type) { | 469 | switch (info.type) { |
| 467 | case TextureType::Color1D: | 470 | case TextureType::Color1D: |
| 468 | return ctx.AddU32x4( | 471 | return ctx.AddU32x4("{}=uvec4(uint(textureSize({},int({}))),0u,0u,{});", inst, texture, lod, |
| 469 | "{}=uvec4(uint(textureSize({},int({}))),0u,0u,uint(textureQueryLevels({})));", inst, | 472 | mips()); |
| 470 | texture, lod, texture); | ||
| 471 | case TextureType::ColorArray1D: | 473 | case TextureType::ColorArray1D: |
| 472 | case TextureType::Color2D: | 474 | case TextureType::Color2D: |
| 473 | case TextureType::ColorCube: | 475 | case TextureType::ColorCube: |
| 474 | case TextureType::Color2DRect: | 476 | case TextureType::Color2DRect: |
| 475 | return ctx.AddU32x4( | 477 | return ctx.AddU32x4("{}=uvec4(uvec2(textureSize({},int({}))),0u,{});", inst, texture, lod, |
| 476 | "{}=uvec4(uvec2(textureSize({},int({}))),0u,uint(textureQueryLevels({})));", inst, | 478 | mips()); |
| 477 | texture, lod, texture); | ||
| 478 | case TextureType::ColorArray2D: | 479 | case TextureType::ColorArray2D: |
| 479 | case TextureType::Color3D: | 480 | case TextureType::Color3D: |
| 480 | case TextureType::ColorArrayCube: | 481 | case TextureType::ColorArrayCube: |
| 481 | return ctx.AddU32x4( | 482 | return ctx.AddU32x4("{}=uvec4(uvec3(textureSize({},int({}))),{});", inst, texture, lod, |
| 482 | "{}=uvec4(uvec3(textureSize({},int({}))),uint(textureQueryLevels({})));", inst, texture, | 483 | mips()); |
| 483 | lod, texture); | ||
| 484 | case TextureType::Buffer: | 484 | case TextureType::Buffer: |
| 485 | throw NotImplementedException("EmitImageQueryDimensions Texture buffers"); | 485 | throw NotImplementedException("EmitImageQueryDimensions Texture buffers"); |
| 486 | } | 486 | } |
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_instructions.h b/src/shader_recompiler/backend/glsl/emit_glsl_instructions.h index c6df1dba7..8d0a65047 100644 --- a/src/shader_recompiler/backend/glsl/emit_glsl_instructions.h +++ b/src/shader_recompiler/backend/glsl/emit_glsl_instructions.h | |||
| @@ -654,7 +654,7 @@ void EmitImageFetch(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, | |||
| 654 | std::string_view coords, std::string_view offset, std::string_view lod, | 654 | std::string_view coords, std::string_view offset, std::string_view lod, |
| 655 | std::string_view ms); | 655 | std::string_view ms); |
| 656 | void EmitImageQueryDimensions(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, | 656 | void EmitImageQueryDimensions(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, |
| 657 | std::string_view lod); | 657 | std::string_view lod, const IR::Value& skip_mips); |
| 658 | void EmitImageQueryLod(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, | 658 | void EmitImageQueryLod(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, |
| 659 | std::string_view coords); | 659 | std::string_view coords); |
| 660 | void EmitImageGradient(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, | 660 | void EmitImageGradient(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, |
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp index c898ce12f..3b969d915 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp | |||
| @@ -445,11 +445,13 @@ Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id c | |||
| 445 | TextureImage(ctx, info, index), coords, operands.MaskOptional(), operands.Span()); | 445 | TextureImage(ctx, info, index), coords, operands.MaskOptional(), operands.Span()); |
| 446 | } | 446 | } |
| 447 | 447 | ||
| 448 | Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id lod) { | 448 | Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id lod, |
| 449 | const IR::Value& skip_mips_val) { | ||
| 449 | const auto info{inst->Flags<IR::TextureInstInfo>()}; | 450 | const auto info{inst->Flags<IR::TextureInstInfo>()}; |
| 450 | const Id image{TextureImage(ctx, info, index)}; | 451 | const Id image{TextureImage(ctx, info, index)}; |
| 451 | const Id zero{ctx.u32_zero_value}; | 452 | const Id zero{ctx.u32_zero_value}; |
| 452 | const auto mips{[&] { return ctx.OpImageQueryLevels(ctx.U32[1], image); }}; | 453 | const bool skip_mips{skip_mips_val.U1()}; |
| 454 | const auto mips{[&] { return skip_mips ? zero : ctx.OpImageQueryLevels(ctx.U32[1], image); }}; | ||
| 453 | switch (info.type) { | 455 | switch (info.type) { |
| 454 | case TextureType::Color1D: | 456 | case TextureType::Color1D: |
| 455 | return ctx.OpCompositeConstruct(ctx.U32[4], ctx.OpImageQuerySizeLod(ctx.U32[1], image, lod), | 457 | return ctx.OpCompositeConstruct(ctx.U32[4], ctx.OpImageQuerySizeLod(ctx.U32[1], image, lod), |
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h index db12e8176..a440b557d 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h +++ b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h | |||
| @@ -539,7 +539,8 @@ Id EmitImageGatherDref(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, | |||
| 539 | const IR::Value& offset, const IR::Value& offset2, Id dref); | 539 | const IR::Value& offset, const IR::Value& offset2, Id dref); |
| 540 | Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id offset, | 540 | Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id offset, |
| 541 | Id lod, Id ms); | 541 | Id lod, Id ms); |
| 542 | Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id lod); | 542 | Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id lod, |
| 543 | const IR::Value& skip_mips); | ||
| 543 | Id EmitImageQueryLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords); | 544 | Id EmitImageQueryLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords); |
| 544 | Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, | 545 | Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, |
| 545 | Id derivates, Id offset, Id lod_clamp); | 546 | Id derivates, Id offset, Id lod_clamp); |
diff --git a/src/shader_recompiler/frontend/ir/ir_emitter.cpp b/src/shader_recompiler/frontend/ir/ir_emitter.cpp index 430797d23..b7caa4246 100644 --- a/src/shader_recompiler/frontend/ir/ir_emitter.cpp +++ b/src/shader_recompiler/frontend/ir/ir_emitter.cpp | |||
| @@ -1846,15 +1846,16 @@ Value IREmitter::ImageFetch(const Value& handle, const Value& coords, const Valu | |||
| 1846 | return Inst(op, Flags{info}, handle, coords, offset, lod, multisampling); | 1846 | return Inst(op, Flags{info}, handle, coords, offset, lod, multisampling); |
| 1847 | } | 1847 | } |
| 1848 | 1848 | ||
| 1849 | Value IREmitter::ImageQueryDimension(const Value& handle, const IR::U32& lod) { | 1849 | Value IREmitter::ImageQueryDimension(const Value& handle, const IR::U32& lod, |
| 1850 | const IR::U1& skip_mips) { | ||
| 1850 | const Opcode op{handle.IsImmediate() ? Opcode::BoundImageQueryDimensions | 1851 | const Opcode op{handle.IsImmediate() ? Opcode::BoundImageQueryDimensions |
| 1851 | : Opcode::BindlessImageQueryDimensions}; | 1852 | : Opcode::BindlessImageQueryDimensions}; |
| 1852 | return Inst(op, handle, lod); | 1853 | return Inst(op, handle, lod, skip_mips); |
| 1853 | } | 1854 | } |
| 1854 | 1855 | ||
| 1855 | Value IREmitter::ImageQueryDimension(const Value& handle, const IR::U32& lod, | 1856 | Value IREmitter::ImageQueryDimension(const Value& handle, const IR::U32& lod, |
| 1856 | TextureInstInfo info) { | 1857 | const IR::U1& skip_mips, TextureInstInfo info) { |
| 1857 | return Inst(Opcode::ImageQueryDimensions, Flags{info}, handle, lod); | 1858 | return Inst(Opcode::ImageQueryDimensions, Flags{info}, handle, lod, skip_mips); |
| 1858 | } | 1859 | } |
| 1859 | 1860 | ||
| 1860 | Value IREmitter::ImageQueryLod(const Value& handle, const Value& coords, TextureInstInfo info) { | 1861 | Value IREmitter::ImageQueryLod(const Value& handle, const Value& coords, TextureInstInfo info) { |
diff --git a/src/shader_recompiler/frontend/ir/ir_emitter.h b/src/shader_recompiler/frontend/ir/ir_emitter.h index 7aaaa4ab0..df158c928 100644 --- a/src/shader_recompiler/frontend/ir/ir_emitter.h +++ b/src/shader_recompiler/frontend/ir/ir_emitter.h | |||
| @@ -320,9 +320,10 @@ public: | |||
| 320 | [[nodiscard]] F32 ImageSampleDrefExplicitLod(const Value& handle, const Value& coords, | 320 | [[nodiscard]] F32 ImageSampleDrefExplicitLod(const Value& handle, const Value& coords, |
| 321 | const F32& dref, const F32& lod, | 321 | const F32& dref, const F32& lod, |
| 322 | const Value& offset, TextureInstInfo info); | 322 | const Value& offset, TextureInstInfo info); |
| 323 | [[nodiscard]] Value ImageQueryDimension(const Value& handle, const IR::U32& lod); | ||
| 324 | [[nodiscard]] Value ImageQueryDimension(const Value& handle, const IR::U32& lod, | 323 | [[nodiscard]] Value ImageQueryDimension(const Value& handle, const IR::U32& lod, |
| 325 | TextureInstInfo info); | 324 | const IR::U1& skip_mips); |
| 325 | [[nodiscard]] Value ImageQueryDimension(const Value& handle, const IR::U32& lod, | ||
| 326 | const IR::U1& skip_mips, TextureInstInfo info); | ||
| 326 | 327 | ||
| 327 | [[nodiscard]] Value ImageQueryLod(const Value& handle, const Value& coords, | 328 | [[nodiscard]] Value ImageQueryLod(const Value& handle, const Value& coords, |
| 328 | TextureInstInfo info); | 329 | TextureInstInfo info); |
diff --git a/src/shader_recompiler/frontend/ir/opcodes.inc b/src/shader_recompiler/frontend/ir/opcodes.inc index 24e82f802..4447d67b0 100644 --- a/src/shader_recompiler/frontend/ir/opcodes.inc +++ b/src/shader_recompiler/frontend/ir/opcodes.inc | |||
| @@ -482,7 +482,7 @@ OPCODE(BindlessImageSampleDrefExplicitLod, F32, U32, | |||
| 482 | OPCODE(BindlessImageGather, F32x4, U32, Opaque, Opaque, Opaque, ) | 482 | OPCODE(BindlessImageGather, F32x4, U32, Opaque, Opaque, Opaque, ) |
| 483 | OPCODE(BindlessImageGatherDref, F32x4, U32, Opaque, Opaque, Opaque, F32, ) | 483 | OPCODE(BindlessImageGatherDref, F32x4, U32, Opaque, Opaque, Opaque, F32, ) |
| 484 | OPCODE(BindlessImageFetch, F32x4, U32, Opaque, Opaque, U32, Opaque, ) | 484 | OPCODE(BindlessImageFetch, F32x4, U32, Opaque, Opaque, U32, Opaque, ) |
| 485 | OPCODE(BindlessImageQueryDimensions, U32x4, U32, U32, ) | 485 | OPCODE(BindlessImageQueryDimensions, U32x4, U32, U32, U1, ) |
| 486 | OPCODE(BindlessImageQueryLod, F32x4, U32, Opaque, ) | 486 | OPCODE(BindlessImageQueryLod, F32x4, U32, Opaque, ) |
| 487 | OPCODE(BindlessImageGradient, F32x4, U32, Opaque, Opaque, Opaque, Opaque, ) | 487 | OPCODE(BindlessImageGradient, F32x4, U32, Opaque, Opaque, Opaque, Opaque, ) |
| 488 | OPCODE(BindlessImageRead, U32x4, U32, Opaque, ) | 488 | OPCODE(BindlessImageRead, U32x4, U32, Opaque, ) |
| @@ -495,7 +495,7 @@ OPCODE(BoundImageSampleDrefExplicitLod, F32, U32, | |||
| 495 | OPCODE(BoundImageGather, F32x4, U32, Opaque, Opaque, Opaque, ) | 495 | OPCODE(BoundImageGather, F32x4, U32, Opaque, Opaque, Opaque, ) |
| 496 | OPCODE(BoundImageGatherDref, F32x4, U32, Opaque, Opaque, Opaque, F32, ) | 496 | OPCODE(BoundImageGatherDref, F32x4, U32, Opaque, Opaque, Opaque, F32, ) |
| 497 | OPCODE(BoundImageFetch, F32x4, U32, Opaque, Opaque, U32, Opaque, ) | 497 | OPCODE(BoundImageFetch, F32x4, U32, Opaque, Opaque, U32, Opaque, ) |
| 498 | OPCODE(BoundImageQueryDimensions, U32x4, U32, U32, ) | 498 | OPCODE(BoundImageQueryDimensions, U32x4, U32, U32, U1, ) |
| 499 | OPCODE(BoundImageQueryLod, F32x4, U32, Opaque, ) | 499 | OPCODE(BoundImageQueryLod, F32x4, U32, Opaque, ) |
| 500 | OPCODE(BoundImageGradient, F32x4, U32, Opaque, Opaque, Opaque, Opaque, ) | 500 | OPCODE(BoundImageGradient, F32x4, U32, Opaque, Opaque, Opaque, Opaque, ) |
| 501 | OPCODE(BoundImageRead, U32x4, U32, Opaque, ) | 501 | OPCODE(BoundImageRead, U32x4, U32, Opaque, ) |
| @@ -508,7 +508,7 @@ OPCODE(ImageSampleDrefExplicitLod, F32, Opaq | |||
| 508 | OPCODE(ImageGather, F32x4, Opaque, Opaque, Opaque, Opaque, ) | 508 | OPCODE(ImageGather, F32x4, Opaque, Opaque, Opaque, Opaque, ) |
| 509 | OPCODE(ImageGatherDref, F32x4, Opaque, Opaque, Opaque, Opaque, F32, ) | 509 | OPCODE(ImageGatherDref, F32x4, Opaque, Opaque, Opaque, Opaque, F32, ) |
| 510 | OPCODE(ImageFetch, F32x4, Opaque, Opaque, Opaque, U32, Opaque, ) | 510 | OPCODE(ImageFetch, F32x4, Opaque, Opaque, Opaque, U32, Opaque, ) |
| 511 | OPCODE(ImageQueryDimensions, U32x4, Opaque, U32, ) | 511 | OPCODE(ImageQueryDimensions, U32x4, Opaque, U32, U1, ) |
| 512 | OPCODE(ImageQueryLod, F32x4, Opaque, Opaque, ) | 512 | OPCODE(ImageQueryLod, F32x4, Opaque, Opaque, ) |
| 513 | OPCODE(ImageGradient, F32x4, Opaque, Opaque, Opaque, Opaque, Opaque, ) | 513 | OPCODE(ImageGradient, F32x4, Opaque, Opaque, Opaque, Opaque, Opaque, ) |
| 514 | OPCODE(ImageRead, U32x4, Opaque, Opaque, ) | 514 | OPCODE(ImageRead, U32x4, Opaque, Opaque, ) |
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/texture_query.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/texture_query.cpp index f8cfd4ab6..39af62559 100644 --- a/src/shader_recompiler/frontend/maxwell/translate/impl/texture_query.cpp +++ b/src/shader_recompiler/frontend/maxwell/translate/impl/texture_query.cpp | |||
| @@ -15,11 +15,13 @@ enum class Mode : u64 { | |||
| 15 | SamplePos = 5, | 15 | SamplePos = 5, |
| 16 | }; | 16 | }; |
| 17 | 17 | ||
| 18 | IR::Value Query(TranslatorVisitor& v, const IR::U32& handle, Mode mode, IR::Reg src_reg) { | 18 | IR::Value Query(TranslatorVisitor& v, const IR::U32& handle, Mode mode, IR::Reg src_reg, u64 mask) { |
| 19 | switch (mode) { | 19 | switch (mode) { |
| 20 | case Mode::Dimension: { | 20 | case Mode::Dimension: { |
| 21 | const bool needs_num_mips{((mask >> 3) & 1) != 0}; | ||
| 22 | const IR::U1 skip_mips{v.ir.Imm1(!needs_num_mips)}; | ||
| 21 | const IR::U32 lod{v.X(src_reg)}; | 23 | const IR::U32 lod{v.X(src_reg)}; |
| 22 | return v.ir.ImageQueryDimension(handle, lod); | 24 | return v.ir.ImageQueryDimension(handle, lod, skip_mips); |
| 23 | } | 25 | } |
| 24 | case Mode::TextureType: | 26 | case Mode::TextureType: |
| 25 | case Mode::SamplePos: | 27 | case Mode::SamplePos: |
| @@ -46,7 +48,7 @@ void Impl(TranslatorVisitor& v, u64 insn, std::optional<u32> cbuf_offset) { | |||
| 46 | handle = v.X(src_reg); | 48 | handle = v.X(src_reg); |
| 47 | ++src_reg; | 49 | ++src_reg; |
| 48 | } | 50 | } |
| 49 | const IR::Value query{Query(v, handle, txq.mode, src_reg)}; | 51 | const IR::Value query{Query(v, handle, txq.mode, src_reg, txq.mask)}; |
| 50 | IR::Reg dest_reg{txq.dest_reg}; | 52 | IR::Reg dest_reg{txq.dest_reg}; |
| 51 | for (int element = 0; element < 4; ++element) { | 53 | for (int element = 0; element < 4; ++element) { |
| 52 | if (((txq.mask >> element) & 1) == 0) { | 54 | if (((txq.mask >> element) & 1) == 0) { |
diff --git a/src/shader_recompiler/ir_opt/texture_pass.cpp b/src/shader_recompiler/ir_opt/texture_pass.cpp index 36d6de8d7..401aeb89f 100644 --- a/src/shader_recompiler/ir_opt/texture_pass.cpp +++ b/src/shader_recompiler/ir_opt/texture_pass.cpp | |||
| @@ -452,7 +452,8 @@ void PatchImageSampleImplicitLod(IR::Block& block, IR::Inst& inst) { | |||
| 452 | const IR::Value coord(inst.Arg(1)); | 452 | const IR::Value coord(inst.Arg(1)); |
| 453 | const IR::Value handle(ir.Imm32(0)); | 453 | const IR::Value handle(ir.Imm32(0)); |
| 454 | const IR::U32 lod{ir.Imm32(0)}; | 454 | const IR::U32 lod{ir.Imm32(0)}; |
| 455 | const IR::Value texture_size = ir.ImageQueryDimension(handle, lod, info); | 455 | const IR::U1 skip_mips{ir.Imm1(true)}; |
| 456 | const IR::Value texture_size = ir.ImageQueryDimension(handle, lod, skip_mips, info); | ||
| 456 | inst.SetArg( | 457 | inst.SetArg( |
| 457 | 1, ir.CompositeConstruct( | 458 | 1, ir.CompositeConstruct( |
| 458 | ir.FPMul(IR::F32(ir.CompositeExtract(coord, 0)), | 459 | ir.FPMul(IR::F32(ir.CompositeExtract(coord, 0)), |
diff --git a/src/video_core/texture_cache/image_info.cpp b/src/video_core/texture_cache/image_info.cpp index 852ec2519..e9100091e 100644 --- a/src/video_core/texture_cache/image_info.cpp +++ b/src/video_core/texture_cache/image_info.cpp | |||
| @@ -100,6 +100,10 @@ ImageInfo::ImageInfo(const TICEntry& config) noexcept { | |||
| 100 | ASSERT_MSG(false, "Invalid texture_type={}", static_cast<int>(config.texture_type.Value())); | 100 | ASSERT_MSG(false, "Invalid texture_type={}", static_cast<int>(config.texture_type.Value())); |
| 101 | break; | 101 | break; |
| 102 | } | 102 | } |
| 103 | if (num_samples > 1) { | ||
| 104 | size.width *= NumSamplesX(config.msaa_mode); | ||
| 105 | size.height *= NumSamplesY(config.msaa_mode); | ||
| 106 | } | ||
| 103 | if (type != ImageType::Linear) { | 107 | if (type != ImageType::Linear) { |
| 104 | // FIXME: Call this without passing *this | 108 | // FIXME: Call this without passing *this |
| 105 | layer_stride = CalculateLayerStride(*this); | 109 | layer_stride = CalculateLayerStride(*this); |
diff --git a/src/video_core/texture_cache/samples_helper.h b/src/video_core/texture_cache/samples_helper.h index d552bccf0..203ac1b11 100644 --- a/src/video_core/texture_cache/samples_helper.h +++ b/src/video_core/texture_cache/samples_helper.h | |||
| @@ -51,4 +51,48 @@ namespace VideoCommon { | |||
| 51 | return 1; | 51 | return 1; |
| 52 | } | 52 | } |
| 53 | 53 | ||
| 54 | [[nodiscard]] inline int NumSamplesX(Tegra::Texture::MsaaMode msaa_mode) { | ||
| 55 | using Tegra::Texture::MsaaMode; | ||
| 56 | switch (msaa_mode) { | ||
| 57 | case MsaaMode::Msaa1x1: | ||
| 58 | return 1; | ||
| 59 | case MsaaMode::Msaa2x1: | ||
| 60 | case MsaaMode::Msaa2x1_D3D: | ||
| 61 | case MsaaMode::Msaa2x2: | ||
| 62 | case MsaaMode::Msaa2x2_VC4: | ||
| 63 | case MsaaMode::Msaa2x2_VC12: | ||
| 64 | return 2; | ||
| 65 | case MsaaMode::Msaa4x2: | ||
| 66 | case MsaaMode::Msaa4x2_D3D: | ||
| 67 | case MsaaMode::Msaa4x2_VC8: | ||
| 68 | case MsaaMode::Msaa4x2_VC24: | ||
| 69 | case MsaaMode::Msaa4x4: | ||
| 70 | return 4; | ||
| 71 | } | ||
| 72 | ASSERT_MSG(false, "Invalid MSAA mode={}", static_cast<int>(msaa_mode)); | ||
| 73 | return 1; | ||
| 74 | } | ||
| 75 | |||
| 76 | [[nodiscard]] inline int NumSamplesY(Tegra::Texture::MsaaMode msaa_mode) { | ||
| 77 | using Tegra::Texture::MsaaMode; | ||
| 78 | switch (msaa_mode) { | ||
| 79 | case MsaaMode::Msaa1x1: | ||
| 80 | case MsaaMode::Msaa2x1: | ||
| 81 | case MsaaMode::Msaa2x1_D3D: | ||
| 82 | return 1; | ||
| 83 | case MsaaMode::Msaa2x2: | ||
| 84 | case MsaaMode::Msaa2x2_VC4: | ||
| 85 | case MsaaMode::Msaa2x2_VC12: | ||
| 86 | case MsaaMode::Msaa4x2: | ||
| 87 | case MsaaMode::Msaa4x2_D3D: | ||
| 88 | case MsaaMode::Msaa4x2_VC8: | ||
| 89 | case MsaaMode::Msaa4x2_VC24: | ||
| 90 | return 2; | ||
| 91 | case MsaaMode::Msaa4x4: | ||
| 92 | return 4; | ||
| 93 | } | ||
| 94 | ASSERT_MSG(false, "Invalid MSAA mode={}", static_cast<int>(msaa_mode)); | ||
| 95 | return 1; | ||
| 96 | } | ||
| 97 | |||
| 54 | } // namespace VideoCommon | 98 | } // namespace VideoCommon |
diff --git a/src/yuzu/configuration/input_profiles.cpp b/src/yuzu/configuration/input_profiles.cpp index 9bb69cab1..41ef4250a 100644 --- a/src/yuzu/configuration/input_profiles.cpp +++ b/src/yuzu/configuration/input_profiles.cpp | |||
| @@ -58,13 +58,16 @@ std::vector<std::string> InputProfiles::GetInputProfileNames() { | |||
| 58 | std::vector<std::string> profile_names; | 58 | std::vector<std::string> profile_names; |
| 59 | profile_names.reserve(map_profiles.size()); | 59 | profile_names.reserve(map_profiles.size()); |
| 60 | 60 | ||
| 61 | for (const auto& [profile_name, config] : map_profiles) { | 61 | auto it = map_profiles.cbegin(); |
| 62 | while (it != map_profiles.cend()) { | ||
| 63 | const auto& [profile_name, config] = *it; | ||
| 62 | if (!ProfileExistsInFilesystem(profile_name)) { | 64 | if (!ProfileExistsInFilesystem(profile_name)) { |
| 63 | DeleteProfile(profile_name); | 65 | it = map_profiles.erase(it); |
| 64 | continue; | 66 | continue; |
| 65 | } | 67 | } |
| 66 | 68 | ||
| 67 | profile_names.push_back(profile_name); | 69 | profile_names.push_back(profile_name); |
| 70 | ++it; | ||
| 68 | } | 71 | } |
| 69 | 72 | ||
| 70 | std::stable_sort(profile_names.begin(), profile_names.end()); | 73 | std::stable_sort(profile_names.begin(), profile_names.end()); |