diff options
39 files changed, 262 insertions, 236 deletions
diff --git a/src/audio_core/in/audio_in_system.cpp b/src/audio_core/in/audio_in_system.cpp index 6b7e6715c..4324cafd8 100644 --- a/src/audio_core/in/audio_in_system.cpp +++ b/src/audio_core/in/audio_in_system.cpp | |||
| @@ -56,7 +56,7 @@ Result System::IsConfigValid(const std::string_view device_name, | |||
| 56 | return ResultSuccess; | 56 | return ResultSuccess; |
| 57 | } | 57 | } |
| 58 | 58 | ||
| 59 | Result System::Initialize(std::string& device_name, const AudioInParameter& in_params, | 59 | Result System::Initialize(std::string device_name, const AudioInParameter& in_params, |
| 60 | const u32 handle_, const u64 applet_resource_user_id_) { | 60 | const u32 handle_, const u64 applet_resource_user_id_) { |
| 61 | auto result{IsConfigValid(device_name, in_params)}; | 61 | auto result{IsConfigValid(device_name, in_params)}; |
| 62 | if (result.IsError()) { | 62 | if (result.IsError()) { |
diff --git a/src/audio_core/in/audio_in_system.h b/src/audio_core/in/audio_in_system.h index b9dc0e60f..1c5154638 100644 --- a/src/audio_core/in/audio_in_system.h +++ b/src/audio_core/in/audio_in_system.h | |||
| @@ -97,7 +97,7 @@ public: | |||
| 97 | * @param applet_resource_user_id - Unused. | 97 | * @param applet_resource_user_id - Unused. |
| 98 | * @return Result code. | 98 | * @return Result code. |
| 99 | */ | 99 | */ |
| 100 | Result Initialize(std::string& device_name, const AudioInParameter& in_params, u32 handle, | 100 | Result Initialize(std::string device_name, const AudioInParameter& in_params, u32 handle, |
| 101 | u64 applet_resource_user_id); | 101 | u64 applet_resource_user_id); |
| 102 | 102 | ||
| 103 | /** | 103 | /** |
diff --git a/src/audio_core/out/audio_out_system.cpp b/src/audio_core/out/audio_out_system.cpp index 48a801923..a66208ed9 100644 --- a/src/audio_core/out/audio_out_system.cpp +++ b/src/audio_core/out/audio_out_system.cpp | |||
| @@ -49,8 +49,8 @@ Result System::IsConfigValid(std::string_view device_name, | |||
| 49 | return Service::Audio::ERR_INVALID_CHANNEL_COUNT; | 49 | return Service::Audio::ERR_INVALID_CHANNEL_COUNT; |
| 50 | } | 50 | } |
| 51 | 51 | ||
| 52 | Result System::Initialize(std::string& device_name, const AudioOutParameter& in_params, u32 handle_, | 52 | Result System::Initialize(std::string device_name, const AudioOutParameter& in_params, u32 handle_, |
| 53 | u64& applet_resource_user_id_) { | 53 | u64 applet_resource_user_id_) { |
| 54 | auto result = IsConfigValid(device_name, in_params); | 54 | auto result = IsConfigValid(device_name, in_params); |
| 55 | if (result.IsError()) { | 55 | if (result.IsError()) { |
| 56 | return result; | 56 | return result; |
diff --git a/src/audio_core/out/audio_out_system.h b/src/audio_core/out/audio_out_system.h index 0817b2f37..b95cb91be 100644 --- a/src/audio_core/out/audio_out_system.h +++ b/src/audio_core/out/audio_out_system.h | |||
| @@ -88,8 +88,8 @@ public: | |||
| 88 | * @param applet_resource_user_id - Unused. | 88 | * @param applet_resource_user_id - Unused. |
| 89 | * @return Result code. | 89 | * @return Result code. |
| 90 | */ | 90 | */ |
| 91 | Result Initialize(std::string& device_name, const AudioOutParameter& in_params, u32 handle, | 91 | Result Initialize(std::string device_name, const AudioOutParameter& in_params, u32 handle, |
| 92 | u64& applet_resource_user_id); | 92 | u64 applet_resource_user_id); |
| 93 | 93 | ||
| 94 | /** | 94 | /** |
| 95 | * Start this system. | 95 | * Start this system. |
diff --git a/src/common/concepts.h b/src/common/concepts.h index e8ce30dfe..a9acff3e7 100644 --- a/src/common/concepts.h +++ b/src/common/concepts.h | |||
| @@ -3,24 +3,14 @@ | |||
| 3 | 3 | ||
| 4 | #pragma once | 4 | #pragma once |
| 5 | 5 | ||
| 6 | #include <iterator> | ||
| 6 | #include <type_traits> | 7 | #include <type_traits> |
| 7 | 8 | ||
| 8 | namespace Common { | 9 | namespace Common { |
| 9 | 10 | ||
| 10 | // Check if type is like an STL container | 11 | // Check if type satisfies the ContiguousContainer named requirement. |
| 11 | template <typename T> | 12 | template <typename T> |
| 12 | concept IsSTLContainer = requires(T t) { | 13 | concept IsContiguousContainer = std::contiguous_iterator<typename T::iterator>; |
| 13 | typename T::value_type; | ||
| 14 | typename T::iterator; | ||
| 15 | typename T::const_iterator; | ||
| 16 | // TODO(ogniK): Replace below is std::same_as<void> when MSVC supports it. | ||
| 17 | t.begin(); | ||
| 18 | t.end(); | ||
| 19 | t.cbegin(); | ||
| 20 | t.cend(); | ||
| 21 | t.data(); | ||
| 22 | t.size(); | ||
| 23 | }; | ||
| 24 | 14 | ||
| 25 | // TODO: Replace with std::derived_from when the <concepts> header | 15 | // TODO: Replace with std::derived_from when the <concepts> header |
| 26 | // is available on all supported platforms. | 16 | // is available on all supported platforms. |
diff --git a/src/common/fs/file.h b/src/common/fs/file.h index 69b53384c..167c4d826 100644 --- a/src/common/fs/file.h +++ b/src/common/fs/file.h | |||
| @@ -209,8 +209,8 @@ public: | |||
| 209 | 209 | ||
| 210 | /** | 210 | /** |
| 211 | * Helper function which deduces the value type of a contiguous STL container used in ReadSpan. | 211 | * Helper function which deduces the value type of a contiguous STL container used in ReadSpan. |
| 212 | * If T is not a contiguous STL container as defined by the concept IsSTLContainer, this calls | 212 | * If T is not a contiguous container as defined by the concept IsContiguousContainer, this |
| 213 | * ReadObject and T must be a trivially copyable object. | 213 | * calls ReadObject and T must be a trivially copyable object. |
| 214 | * | 214 | * |
| 215 | * See ReadSpan for more details if T is a contiguous container. | 215 | * See ReadSpan for more details if T is a contiguous container. |
| 216 | * See ReadObject for more details if T is a trivially copyable object. | 216 | * See ReadObject for more details if T is a trivially copyable object. |
| @@ -223,7 +223,7 @@ public: | |||
| 223 | */ | 223 | */ |
| 224 | template <typename T> | 224 | template <typename T> |
| 225 | [[nodiscard]] size_t Read(T& data) const { | 225 | [[nodiscard]] size_t Read(T& data) const { |
| 226 | if constexpr (IsSTLContainer<T>) { | 226 | if constexpr (IsContiguousContainer<T>) { |
| 227 | using ContiguousType = typename T::value_type; | 227 | using ContiguousType = typename T::value_type; |
| 228 | static_assert(std::is_trivially_copyable_v<ContiguousType>, | 228 | static_assert(std::is_trivially_copyable_v<ContiguousType>, |
| 229 | "Data type must be trivially copyable."); | 229 | "Data type must be trivially copyable."); |
| @@ -235,8 +235,8 @@ public: | |||
| 235 | 235 | ||
| 236 | /** | 236 | /** |
| 237 | * Helper function which deduces the value type of a contiguous STL container used in WriteSpan. | 237 | * Helper function which deduces the value type of a contiguous STL container used in WriteSpan. |
| 238 | * If T is not a contiguous STL container as defined by the concept IsSTLContainer, this calls | 238 | * If T is not a contiguous STL container as defined by the concept IsContiguousContainer, this |
| 239 | * WriteObject and T must be a trivially copyable object. | 239 | * calls WriteObject and T must be a trivially copyable object. |
| 240 | * | 240 | * |
| 241 | * See WriteSpan for more details if T is a contiguous container. | 241 | * See WriteSpan for more details if T is a contiguous container. |
| 242 | * See WriteObject for more details if T is a trivially copyable object. | 242 | * See WriteObject for more details if T is a trivially copyable object. |
| @@ -249,7 +249,7 @@ public: | |||
| 249 | */ | 249 | */ |
| 250 | template <typename T> | 250 | template <typename T> |
| 251 | [[nodiscard]] size_t Write(const T& data) const { | 251 | [[nodiscard]] size_t Write(const T& data) const { |
| 252 | if constexpr (IsSTLContainer<T>) { | 252 | if constexpr (IsContiguousContainer<T>) { |
| 253 | using ContiguousType = typename T::value_type; | 253 | using ContiguousType = typename T::value_type; |
| 254 | static_assert(std::is_trivially_copyable_v<ContiguousType>, | 254 | static_assert(std::is_trivially_copyable_v<ContiguousType>, |
| 255 | "Data type must be trivially copyable."); | 255 | "Data type must be trivially copyable."); |
diff --git a/src/common/input.h b/src/common/input.h index b533f3844..cb30b7254 100644 --- a/src/common/input.h +++ b/src/common/input.h | |||
| @@ -100,7 +100,6 @@ enum class CameraError { | |||
| 100 | enum class VibrationAmplificationType { | 100 | enum class VibrationAmplificationType { |
| 101 | Linear, | 101 | Linear, |
| 102 | Exponential, | 102 | Exponential, |
| 103 | Test, | ||
| 104 | }; | 103 | }; |
| 105 | 104 | ||
| 106 | // Analog properties for calibration | 105 | // Analog properties for calibration |
| @@ -325,6 +324,10 @@ public: | |||
| 325 | return VibrationError::NotSupported; | 324 | return VibrationError::NotSupported; |
| 326 | } | 325 | } |
| 327 | 326 | ||
| 327 | virtual bool IsVibrationEnabled() { | ||
| 328 | return false; | ||
| 329 | } | ||
| 330 | |||
| 328 | virtual PollingError SetPollingMode([[maybe_unused]] PollingMode polling_mode) { | 331 | virtual PollingError SetPollingMode([[maybe_unused]] PollingMode polling_mode) { |
| 329 | return PollingError::NotSupported; | 332 | return PollingError::NotSupported; |
| 330 | } | 333 | } |
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp index d1e70f19d..287ba102e 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp | |||
| @@ -450,7 +450,7 @@ std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_32::GetBacktrace(Core::S | |||
| 450 | // Frame records are two words long: | 450 | // Frame records are two words long: |
| 451 | // fp+0 : pointer to previous frame record | 451 | // fp+0 : pointer to previous frame record |
| 452 | // fp+4 : value of lr for frame | 452 | // fp+4 : value of lr for frame |
| 453 | while (true) { | 453 | for (size_t i = 0; i < 256; i++) { |
| 454 | out.push_back({"", 0, lr, 0, ""}); | 454 | out.push_back({"", 0, lr, 0, ""}); |
| 455 | if (!fp || (fp % 4 != 0) || !memory.IsValidVirtualAddressRange(fp, 8)) { | 455 | if (!fp || (fp % 4 != 0) || !memory.IsValidVirtualAddressRange(fp, 8)) { |
| 456 | break; | 456 | break; |
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp index 22b5d5656..afb7fb3a0 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp | |||
| @@ -517,7 +517,7 @@ std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_64::GetBacktrace(Core::S | |||
| 517 | // Frame records are two words long: | 517 | // Frame records are two words long: |
| 518 | // fp+0 : pointer to previous frame record | 518 | // fp+0 : pointer to previous frame record |
| 519 | // fp+8 : value of lr for frame | 519 | // fp+8 : value of lr for frame |
| 520 | while (true) { | 520 | for (size_t i = 0; i < 256; i++) { |
| 521 | out.push_back({"", 0, lr, 0, ""}); | 521 | out.push_back({"", 0, lr, 0, ""}); |
| 522 | if (!fp || (fp % 4 != 0) || !memory.IsValidVirtualAddressRange(fp, 16)) { | 522 | if (!fp || (fp % 4 != 0) || !memory.IsValidVirtualAddressRange(fp, 16)) { |
| 523 | break; | 523 | break; |
diff --git a/src/core/file_sys/control_metadata.cpp b/src/core/file_sys/control_metadata.cpp index be25da2f6..50f44f598 100644 --- a/src/core/file_sys/control_metadata.cpp +++ b/src/core/file_sys/control_metadata.cpp | |||
| @@ -1,6 +1,7 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #include "common/settings.h" | ||
| 4 | #include "common/string_util.h" | 5 | #include "common/string_util.h" |
| 5 | #include "common/swap.h" | 6 | #include "common/swap.h" |
| 6 | #include "core/file_sys/control_metadata.h" | 7 | #include "core/file_sys/control_metadata.h" |
| @@ -37,6 +38,27 @@ std::string LanguageEntry::GetDeveloperName() const { | |||
| 37 | developer_name.size()); | 38 | developer_name.size()); |
| 38 | } | 39 | } |
| 39 | 40 | ||
| 41 | constexpr std::array<Language, 18> language_to_codes = {{ | ||
| 42 | Language::Japanese, | ||
| 43 | Language::AmericanEnglish, | ||
| 44 | Language::French, | ||
| 45 | Language::German, | ||
| 46 | Language::Italian, | ||
| 47 | Language::Spanish, | ||
| 48 | Language::Chinese, | ||
| 49 | Language::Korean, | ||
| 50 | Language::Dutch, | ||
| 51 | Language::Portuguese, | ||
| 52 | Language::Russian, | ||
| 53 | Language::Taiwanese, | ||
| 54 | Language::BritishEnglish, | ||
| 55 | Language::CanadianFrench, | ||
| 56 | Language::LatinAmericanSpanish, | ||
| 57 | Language::Chinese, | ||
| 58 | Language::Taiwanese, | ||
| 59 | Language::BrazilianPortuguese, | ||
| 60 | }}; | ||
| 61 | |||
| 40 | NACP::NACP() = default; | 62 | NACP::NACP() = default; |
| 41 | 63 | ||
| 42 | NACP::NACP(VirtualFile file) { | 64 | NACP::NACP(VirtualFile file) { |
| @@ -45,9 +67,13 @@ NACP::NACP(VirtualFile file) { | |||
| 45 | 67 | ||
| 46 | NACP::~NACP() = default; | 68 | NACP::~NACP() = default; |
| 47 | 69 | ||
| 48 | const LanguageEntry& NACP::GetLanguageEntry(Language language) const { | 70 | const LanguageEntry& NACP::GetLanguageEntry() const { |
| 49 | if (language != Language::Default) { | 71 | Language language = language_to_codes[Settings::values.language_index.GetValue()]; |
| 50 | return raw.language_entries.at(static_cast<u8>(language)); | 72 | |
| 73 | { | ||
| 74 | const auto& language_entry = raw.language_entries.at(static_cast<u8>(language)); | ||
| 75 | if (!language_entry.GetApplicationName().empty()) | ||
| 76 | return language_entry; | ||
| 51 | } | 77 | } |
| 52 | 78 | ||
| 53 | for (const auto& language_entry : raw.language_entries) { | 79 | for (const auto& language_entry : raw.language_entries) { |
| @@ -55,16 +81,15 @@ const LanguageEntry& NACP::GetLanguageEntry(Language language) const { | |||
| 55 | return language_entry; | 81 | return language_entry; |
| 56 | } | 82 | } |
| 57 | 83 | ||
| 58 | // Fallback to English | 84 | return raw.language_entries.at(static_cast<u8>(Language::AmericanEnglish)); |
| 59 | return GetLanguageEntry(Language::AmericanEnglish); | ||
| 60 | } | 85 | } |
| 61 | 86 | ||
| 62 | std::string NACP::GetApplicationName(Language language) const { | 87 | std::string NACP::GetApplicationName() const { |
| 63 | return GetLanguageEntry(language).GetApplicationName(); | 88 | return GetLanguageEntry().GetApplicationName(); |
| 64 | } | 89 | } |
| 65 | 90 | ||
| 66 | std::string NACP::GetDeveloperName(Language language) const { | 91 | std::string NACP::GetDeveloperName() const { |
| 67 | return GetLanguageEntry(language).GetDeveloperName(); | 92 | return GetLanguageEntry().GetDeveloperName(); |
| 68 | } | 93 | } |
| 69 | 94 | ||
| 70 | u64 NACP::GetTitleId() const { | 95 | u64 NACP::GetTitleId() const { |
diff --git a/src/core/file_sys/control_metadata.h b/src/core/file_sys/control_metadata.h index 75295519c..6a81873b1 100644 --- a/src/core/file_sys/control_metadata.h +++ b/src/core/file_sys/control_metadata.h | |||
| @@ -101,9 +101,9 @@ public: | |||
| 101 | explicit NACP(VirtualFile file); | 101 | explicit NACP(VirtualFile file); |
| 102 | ~NACP(); | 102 | ~NACP(); |
| 103 | 103 | ||
| 104 | const LanguageEntry& GetLanguageEntry(Language language = Language::Default) const; | 104 | const LanguageEntry& GetLanguageEntry() const; |
| 105 | std::string GetApplicationName(Language language = Language::Default) const; | 105 | std::string GetApplicationName() const; |
| 106 | std::string GetDeveloperName(Language language = Language::Default) const; | 106 | std::string GetDeveloperName() const; |
| 107 | u64 GetTitleId() const; | 107 | u64 GetTitleId() const; |
| 108 | u64 GetDLCBaseTitleId() const; | 108 | u64 GetDLCBaseTitleId() const; |
| 109 | std::string GetVersionString() const; | 109 | std::string GetVersionString() const; |
diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp index 57eff72fe..ec1364452 100644 --- a/src/core/hid/emulated_controller.cpp +++ b/src/core/hid/emulated_controller.cpp | |||
| @@ -970,14 +970,7 @@ bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue v | |||
| 970 | Common::Input::VibrationError::None; | 970 | Common::Input::VibrationError::None; |
| 971 | } | 971 | } |
| 972 | 972 | ||
| 973 | bool EmulatedController::TestVibration(std::size_t device_index) { | 973 | bool EmulatedController::IsVibrationEnabled(std::size_t device_index) { |
| 974 | if (device_index >= output_devices.size()) { | ||
| 975 | return false; | ||
| 976 | } | ||
| 977 | if (!output_devices[device_index]) { | ||
| 978 | return false; | ||
| 979 | } | ||
| 980 | |||
| 981 | const auto player_index = NpadIdTypeToIndex(npad_id_type); | 974 | const auto player_index = NpadIdTypeToIndex(npad_id_type); |
| 982 | const auto& player = Settings::values.players.GetValue()[player_index]; | 975 | const auto& player = Settings::values.players.GetValue()[player_index]; |
| 983 | 976 | ||
| @@ -985,31 +978,15 @@ bool EmulatedController::TestVibration(std::size_t device_index) { | |||
| 985 | return false; | 978 | return false; |
| 986 | } | 979 | } |
| 987 | 980 | ||
| 988 | const Common::Input::VibrationStatus test_vibration = { | 981 | if (device_index >= output_devices.size()) { |
| 989 | .low_amplitude = 0.001f, | 982 | return false; |
| 990 | .low_frequency = DEFAULT_VIBRATION_VALUE.low_frequency, | 983 | } |
| 991 | .high_amplitude = 0.001f, | ||
| 992 | .high_frequency = DEFAULT_VIBRATION_VALUE.high_frequency, | ||
| 993 | .type = Common::Input::VibrationAmplificationType::Test, | ||
| 994 | }; | ||
| 995 | |||
| 996 | const Common::Input::VibrationStatus zero_vibration = { | ||
| 997 | .low_amplitude = DEFAULT_VIBRATION_VALUE.low_amplitude, | ||
| 998 | .low_frequency = DEFAULT_VIBRATION_VALUE.low_frequency, | ||
| 999 | .high_amplitude = DEFAULT_VIBRATION_VALUE.high_amplitude, | ||
| 1000 | .high_frequency = DEFAULT_VIBRATION_VALUE.high_frequency, | ||
| 1001 | .type = Common::Input::VibrationAmplificationType::Test, | ||
| 1002 | }; | ||
| 1003 | |||
| 1004 | // Send a slight vibration to test for rumble support | ||
| 1005 | output_devices[device_index]->SetVibration(test_vibration); | ||
| 1006 | 984 | ||
| 1007 | // Wait for about 15ms to ensure the controller is ready for the stop command | 985 | if (!output_devices[device_index]) { |
| 1008 | std::this_thread::sleep_for(std::chrono::milliseconds(15)); | 986 | return false; |
| 987 | } | ||
| 1009 | 988 | ||
| 1010 | // Stop any vibration and return the result | 989 | return output_devices[device_index]->IsVibrationEnabled(); |
| 1011 | return output_devices[device_index]->SetVibration(zero_vibration) == | ||
| 1012 | Common::Input::VibrationError::None; | ||
| 1013 | } | 990 | } |
| 1014 | 991 | ||
| 1015 | bool EmulatedController::SetPollingMode(Common::Input::PollingMode polling_mode) { | 992 | bool EmulatedController::SetPollingMode(Common::Input::PollingMode polling_mode) { |
| @@ -1048,6 +1025,7 @@ bool EmulatedController::HasNfc() const { | |||
| 1048 | case NpadStyleIndex::JoyconRight: | 1025 | case NpadStyleIndex::JoyconRight: |
| 1049 | case NpadStyleIndex::JoyconDual: | 1026 | case NpadStyleIndex::JoyconDual: |
| 1050 | case NpadStyleIndex::ProController: | 1027 | case NpadStyleIndex::ProController: |
| 1028 | case NpadStyleIndex::Handheld: | ||
| 1051 | break; | 1029 | break; |
| 1052 | default: | 1030 | default: |
| 1053 | return false; | 1031 | return false; |
| @@ -1234,12 +1212,6 @@ bool EmulatedController::IsConnected(bool get_temporary_value) const { | |||
| 1234 | return is_connected; | 1212 | return is_connected; |
| 1235 | } | 1213 | } |
| 1236 | 1214 | ||
| 1237 | bool EmulatedController::IsVibrationEnabled() const { | ||
| 1238 | const auto player_index = NpadIdTypeToIndex(npad_id_type); | ||
| 1239 | const auto& player = Settings::values.players.GetValue()[player_index]; | ||
| 1240 | return player.vibration_enabled; | ||
| 1241 | } | ||
| 1242 | |||
| 1243 | NpadIdType EmulatedController::GetNpadIdType() const { | 1215 | NpadIdType EmulatedController::GetNpadIdType() const { |
| 1244 | std::scoped_lock lock{mutex}; | 1216 | std::scoped_lock lock{mutex}; |
| 1245 | return npad_id_type; | 1217 | return npad_id_type; |
diff --git a/src/core/hid/emulated_controller.h b/src/core/hid/emulated_controller.h index 319226bf8..d004ca56a 100644 --- a/src/core/hid/emulated_controller.h +++ b/src/core/hid/emulated_controller.h | |||
| @@ -206,9 +206,6 @@ public: | |||
| 206 | */ | 206 | */ |
| 207 | bool IsConnected(bool get_temporary_value = false) const; | 207 | bool IsConnected(bool get_temporary_value = false) const; |
| 208 | 208 | ||
| 209 | /// Returns true if vibration is enabled | ||
| 210 | bool IsVibrationEnabled() const; | ||
| 211 | |||
| 212 | /// Removes all callbacks created from input devices | 209 | /// Removes all callbacks created from input devices |
| 213 | void UnloadInput(); | 210 | void UnloadInput(); |
| 214 | 211 | ||
| @@ -339,7 +336,7 @@ public: | |||
| 339 | * Sends a small vibration to the output device | 336 | * Sends a small vibration to the output device |
| 340 | * @return true if SetVibration was successfull | 337 | * @return true if SetVibration was successfull |
| 341 | */ | 338 | */ |
| 342 | bool TestVibration(std::size_t device_index); | 339 | bool IsVibrationEnabled(std::size_t device_index); |
| 343 | 340 | ||
| 344 | /** | 341 | /** |
| 345 | * Sets the desired data to be polled from a controller | 342 | * Sets the desired data to be polled from a controller |
diff --git a/src/core/hle/kernel/global_scheduler_context.cpp b/src/core/hle/kernel/global_scheduler_context.cpp index 65576b8c4..fd911a3a5 100644 --- a/src/core/hle/kernel/global_scheduler_context.cpp +++ b/src/core/hle/kernel/global_scheduler_context.cpp | |||
| @@ -49,4 +49,26 @@ bool GlobalSchedulerContext::IsLocked() const { | |||
| 49 | return scheduler_lock.IsLockedByCurrentThread(); | 49 | return scheduler_lock.IsLockedByCurrentThread(); |
| 50 | } | 50 | } |
| 51 | 51 | ||
| 52 | void GlobalSchedulerContext::RegisterDummyThreadForWakeup(KThread* thread) { | ||
| 53 | ASSERT(IsLocked()); | ||
| 54 | |||
| 55 | woken_dummy_threads.insert(thread); | ||
| 56 | } | ||
| 57 | |||
| 58 | void GlobalSchedulerContext::UnregisterDummyThreadForWakeup(KThread* thread) { | ||
| 59 | ASSERT(IsLocked()); | ||
| 60 | |||
| 61 | woken_dummy_threads.erase(thread); | ||
| 62 | } | ||
| 63 | |||
| 64 | void GlobalSchedulerContext::WakeupWaitingDummyThreads() { | ||
| 65 | ASSERT(IsLocked()); | ||
| 66 | |||
| 67 | for (auto* thread : woken_dummy_threads) { | ||
| 68 | thread->DummyThreadEndWait(); | ||
| 69 | } | ||
| 70 | |||
| 71 | woken_dummy_threads.clear(); | ||
| 72 | } | ||
| 73 | |||
| 52 | } // namespace Kernel | 74 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/global_scheduler_context.h b/src/core/hle/kernel/global_scheduler_context.h index 67bb9852d..220ed6192 100644 --- a/src/core/hle/kernel/global_scheduler_context.h +++ b/src/core/hle/kernel/global_scheduler_context.h | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | #pragma once | 4 | #pragma once |
| 5 | 5 | ||
| 6 | #include <atomic> | 6 | #include <atomic> |
| 7 | #include <set> | ||
| 7 | #include <vector> | 8 | #include <vector> |
| 8 | 9 | ||
| 9 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| @@ -58,6 +59,10 @@ public: | |||
| 58 | /// Returns true if the global scheduler lock is acquired | 59 | /// Returns true if the global scheduler lock is acquired |
| 59 | bool IsLocked() const; | 60 | bool IsLocked() const; |
| 60 | 61 | ||
| 62 | void UnregisterDummyThreadForWakeup(KThread* thread); | ||
| 63 | void RegisterDummyThreadForWakeup(KThread* thread); | ||
| 64 | void WakeupWaitingDummyThreads(); | ||
| 65 | |||
| 61 | [[nodiscard]] LockType& SchedulerLock() { | 66 | [[nodiscard]] LockType& SchedulerLock() { |
| 62 | return scheduler_lock; | 67 | return scheduler_lock; |
| 63 | } | 68 | } |
| @@ -76,6 +81,9 @@ private: | |||
| 76 | KSchedulerPriorityQueue priority_queue; | 81 | KSchedulerPriorityQueue priority_queue; |
| 77 | LockType scheduler_lock; | 82 | LockType scheduler_lock; |
| 78 | 83 | ||
| 84 | /// Lists dummy threads pending wakeup on lock release | ||
| 85 | std::set<KThread*> woken_dummy_threads; | ||
| 86 | |||
| 79 | /// Lists all thread ids that aren't deleted/etc. | 87 | /// Lists all thread ids that aren't deleted/etc. |
| 80 | std::vector<KThread*> thread_list; | 88 | std::vector<KThread*> thread_list; |
| 81 | std::mutex global_list_guard; | 89 | std::mutex global_list_guard; |
diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h index a0522bca0..1083638a9 100644 --- a/src/core/hle/kernel/hle_ipc.h +++ b/src/core/hle/kernel/hle_ipc.h | |||
| @@ -304,7 +304,7 @@ public: | |||
| 304 | */ | 304 | */ |
| 305 | template <typename T, typename = std::enable_if_t<!std::is_pointer_v<T>>> | 305 | template <typename T, typename = std::enable_if_t<!std::is_pointer_v<T>>> |
| 306 | std::size_t WriteBuffer(const T& data, std::size_t buffer_index = 0) const { | 306 | std::size_t WriteBuffer(const T& data, std::size_t buffer_index = 0) const { |
| 307 | if constexpr (Common::IsSTLContainer<T>) { | 307 | if constexpr (Common::IsContiguousContainer<T>) { |
| 308 | using ContiguousType = typename T::value_type; | 308 | using ContiguousType = typename T::value_type; |
| 309 | static_assert(std::is_trivially_copyable_v<ContiguousType>, | 309 | static_assert(std::is_trivially_copyable_v<ContiguousType>, |
| 310 | "Container to WriteBuffer must contain trivially copyable objects"); | 310 | "Container to WriteBuffer must contain trivially copyable objects"); |
diff --git a/src/core/hle/kernel/k_scheduler.cpp b/src/core/hle/kernel/k_scheduler.cpp index c34ce7a17..b1cabbca0 100644 --- a/src/core/hle/kernel/k_scheduler.cpp +++ b/src/core/hle/kernel/k_scheduler.cpp | |||
| @@ -81,8 +81,8 @@ void KScheduler::RescheduleCurrentHLEThread(KernelCore& kernel) { | |||
| 81 | // HACK: we cannot schedule from this thread, it is not a core thread | 81 | // HACK: we cannot schedule from this thread, it is not a core thread |
| 82 | ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() == 1); | 82 | ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() == 1); |
| 83 | 83 | ||
| 84 | // Special case to ensure dummy threads that are waiting block | 84 | // Ensure dummy threads that are waiting block. |
| 85 | GetCurrentThread(kernel).IfDummyThreadTryWait(); | 85 | GetCurrentThread(kernel).DummyThreadBeginWait(); |
| 86 | 86 | ||
| 87 | ASSERT(GetCurrentThread(kernel).GetState() != ThreadState::Waiting); | 87 | ASSERT(GetCurrentThread(kernel).GetState() != ThreadState::Waiting); |
| 88 | GetCurrentThread(kernel).EnableDispatch(); | 88 | GetCurrentThread(kernel).EnableDispatch(); |
| @@ -314,6 +314,16 @@ u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) { | |||
| 314 | idle_cores &= ~(1ULL << core_id); | 314 | idle_cores &= ~(1ULL << core_id); |
| 315 | } | 315 | } |
| 316 | 316 | ||
| 317 | // HACK: any waiting dummy threads can wake up now. | ||
| 318 | kernel.GlobalSchedulerContext().WakeupWaitingDummyThreads(); | ||
| 319 | |||
| 320 | // HACK: if we are a dummy thread, and we need to go sleep, indicate | ||
| 321 | // that for when the lock is released. | ||
| 322 | KThread* const cur_thread = GetCurrentThreadPointer(kernel); | ||
| 323 | if (cur_thread->IsDummyThread() && cur_thread->GetState() != ThreadState::Runnable) { | ||
| 324 | cur_thread->RequestDummyThreadWait(); | ||
| 325 | } | ||
| 326 | |||
| 317 | return cores_needing_scheduling; | 327 | return cores_needing_scheduling; |
| 318 | } | 328 | } |
| 319 | 329 | ||
| @@ -531,11 +541,23 @@ void KScheduler::OnThreadStateChanged(KernelCore& kernel, KThread* thread, Threa | |||
| 531 | GetPriorityQueue(kernel).Remove(thread); | 541 | GetPriorityQueue(kernel).Remove(thread); |
| 532 | IncrementScheduledCount(thread); | 542 | IncrementScheduledCount(thread); |
| 533 | SetSchedulerUpdateNeeded(kernel); | 543 | SetSchedulerUpdateNeeded(kernel); |
| 544 | |||
| 545 | if (thread->IsDummyThread()) { | ||
| 546 | // HACK: if this is a dummy thread, it should no longer wake up when the | ||
| 547 | // scheduler lock is released. | ||
| 548 | kernel.GlobalSchedulerContext().UnregisterDummyThreadForWakeup(thread); | ||
| 549 | } | ||
| 534 | } else if (cur_state == ThreadState::Runnable) { | 550 | } else if (cur_state == ThreadState::Runnable) { |
| 535 | // If we're now runnable, then we weren't previously, and we should add. | 551 | // If we're now runnable, then we weren't previously, and we should add. |
| 536 | GetPriorityQueue(kernel).PushBack(thread); | 552 | GetPriorityQueue(kernel).PushBack(thread); |
| 537 | IncrementScheduledCount(thread); | 553 | IncrementScheduledCount(thread); |
| 538 | SetSchedulerUpdateNeeded(kernel); | 554 | SetSchedulerUpdateNeeded(kernel); |
| 555 | |||
| 556 | if (thread->IsDummyThread()) { | ||
| 557 | // HACK: if this is a dummy thread, it should wake up when the scheduler | ||
| 558 | // lock is released. | ||
| 559 | kernel.GlobalSchedulerContext().RegisterDummyThreadForWakeup(thread); | ||
| 560 | } | ||
| 539 | } | 561 | } |
| 540 | } | 562 | } |
| 541 | 563 | ||
diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp index b7bfcdce3..d57b42fdf 100644 --- a/src/core/hle/kernel/k_thread.cpp +++ b/src/core/hle/kernel/k_thread.cpp | |||
| @@ -148,7 +148,9 @@ Result KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_stack | |||
| 148 | physical_affinity_mask.SetAffinity(phys_core, true); | 148 | physical_affinity_mask.SetAffinity(phys_core, true); |
| 149 | 149 | ||
| 150 | // Set the thread state. | 150 | // Set the thread state. |
| 151 | thread_state = (type == ThreadType::Main) ? ThreadState::Runnable : ThreadState::Initialized; | 151 | thread_state = (type == ThreadType::Main || type == ThreadType::Dummy) |
| 152 | ? ThreadState::Runnable | ||
| 153 | : ThreadState::Initialized; | ||
| 152 | 154 | ||
| 153 | // Set TLS address. | 155 | // Set TLS address. |
| 154 | tls_address = 0; | 156 | tls_address = 0; |
| @@ -1174,30 +1176,29 @@ Result KThread::Sleep(s64 timeout) { | |||
| 1174 | R_SUCCEED(); | 1176 | R_SUCCEED(); |
| 1175 | } | 1177 | } |
| 1176 | 1178 | ||
| 1177 | void KThread::IfDummyThreadTryWait() { | 1179 | void KThread::RequestDummyThreadWait() { |
| 1178 | if (!IsDummyThread()) { | 1180 | ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(kernel)); |
| 1179 | return; | 1181 | ASSERT(this->IsDummyThread()); |
| 1180 | } | ||
| 1181 | 1182 | ||
| 1182 | if (GetState() != ThreadState::Waiting) { | 1183 | // We will block when the scheduler lock is released. |
| 1183 | return; | 1184 | dummy_thread_runnable.store(false); |
| 1184 | } | 1185 | } |
| 1185 | 1186 | ||
| 1187 | void KThread::DummyThreadBeginWait() { | ||
| 1188 | ASSERT(this->IsDummyThread()); | ||
| 1186 | ASSERT(!kernel.IsPhantomModeForSingleCore()); | 1189 | ASSERT(!kernel.IsPhantomModeForSingleCore()); |
| 1187 | 1190 | ||
| 1188 | // Block until we are no longer waiting. | 1191 | // Block until runnable is no longer false. |
| 1189 | std::unique_lock lk(dummy_wait_lock); | 1192 | dummy_thread_runnable.wait(false); |
| 1190 | dummy_wait_cv.wait( | ||
| 1191 | lk, [&] { return GetState() != ThreadState::Waiting || kernel.IsShuttingDown(); }); | ||
| 1192 | } | 1193 | } |
| 1193 | 1194 | ||
| 1194 | void KThread::IfDummyThreadEndWait() { | 1195 | void KThread::DummyThreadEndWait() { |
| 1195 | if (!IsDummyThread()) { | 1196 | ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(kernel)); |
| 1196 | return; | 1197 | ASSERT(this->IsDummyThread()); |
| 1197 | } | ||
| 1198 | 1198 | ||
| 1199 | // Wake up the waiting thread. | 1199 | // Wake up the waiting thread. |
| 1200 | dummy_wait_cv.notify_one(); | 1200 | dummy_thread_runnable.store(true); |
| 1201 | dummy_thread_runnable.notify_one(); | ||
| 1201 | } | 1202 | } |
| 1202 | 1203 | ||
| 1203 | void KThread::BeginWait(KThreadQueue* queue) { | 1204 | void KThread::BeginWait(KThreadQueue* queue) { |
| @@ -1231,9 +1232,6 @@ void KThread::EndWait(Result wait_result_) { | |||
| 1231 | } | 1232 | } |
| 1232 | 1233 | ||
| 1233 | wait_queue->EndWait(this, wait_result_); | 1234 | wait_queue->EndWait(this, wait_result_); |
| 1234 | |||
| 1235 | // Special case for dummy threads to wakeup if necessary. | ||
| 1236 | IfDummyThreadEndWait(); | ||
| 1237 | } | 1235 | } |
| 1238 | } | 1236 | } |
| 1239 | 1237 | ||
diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h index e2a27d603..30aa10c9a 100644 --- a/src/core/hle/kernel/k_thread.h +++ b/src/core/hle/kernel/k_thread.h | |||
| @@ -643,8 +643,9 @@ public: | |||
| 643 | // therefore will not block on guest kernel synchronization primitives. These methods handle | 643 | // therefore will not block on guest kernel synchronization primitives. These methods handle |
| 644 | // blocking as needed. | 644 | // blocking as needed. |
| 645 | 645 | ||
| 646 | void IfDummyThreadTryWait(); | 646 | void RequestDummyThreadWait(); |
| 647 | void IfDummyThreadEndWait(); | 647 | void DummyThreadBeginWait(); |
| 648 | void DummyThreadEndWait(); | ||
| 648 | 649 | ||
| 649 | [[nodiscard]] uintptr_t GetArgument() const { | 650 | [[nodiscard]] uintptr_t GetArgument() const { |
| 650 | return argument; | 651 | return argument; |
| @@ -777,8 +778,7 @@ private: | |||
| 777 | bool is_single_core{}; | 778 | bool is_single_core{}; |
| 778 | ThreadType thread_type{}; | 779 | ThreadType thread_type{}; |
| 779 | StepState step_state{}; | 780 | StepState step_state{}; |
| 780 | std::mutex dummy_wait_lock; | 781 | std::atomic<bool> dummy_thread_runnable{true}; |
| 781 | std::condition_variable dummy_wait_cv; | ||
| 782 | 782 | ||
| 783 | // For debugging | 783 | // For debugging |
| 784 | std::vector<KSynchronizationObject*> wait_objects_for_debugging; | 784 | std::vector<KSynchronizationObject*> wait_objects_for_debugging; |
diff --git a/src/core/hle/service/audio/audin_u.cpp b/src/core/hle/service/audio/audin_u.cpp index 48a9a73a0..608925dfc 100644 --- a/src/core/hle/service/audio/audin_u.cpp +++ b/src/core/hle/service/audio/audin_u.cpp | |||
| @@ -17,7 +17,7 @@ using namespace AudioCore::AudioIn; | |||
| 17 | class IAudioIn final : public ServiceFramework<IAudioIn> { | 17 | class IAudioIn final : public ServiceFramework<IAudioIn> { |
| 18 | public: | 18 | public: |
| 19 | explicit IAudioIn(Core::System& system_, Manager& manager, size_t session_id, | 19 | explicit IAudioIn(Core::System& system_, Manager& manager, size_t session_id, |
| 20 | std::string& device_name, const AudioInParameter& in_params, u32 handle, | 20 | const std::string& device_name, const AudioInParameter& in_params, u32 handle, |
| 21 | u64 applet_resource_user_id) | 21 | u64 applet_resource_user_id) |
| 22 | : ServiceFramework{system_, "IAudioIn"}, | 22 | : ServiceFramework{system_, "IAudioIn"}, |
| 23 | service_context{system_, "IAudioIn"}, event{service_context.CreateEvent("AudioInEvent")}, | 23 | service_context{system_, "IAudioIn"}, event{service_context.CreateEvent("AudioInEvent")}, |
diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp index 49c092301..122290c6a 100644 --- a/src/core/hle/service/audio/audout_u.cpp +++ b/src/core/hle/service/audio/audout_u.cpp | |||
| @@ -24,7 +24,7 @@ using namespace AudioCore::AudioOut; | |||
| 24 | class IAudioOut final : public ServiceFramework<IAudioOut> { | 24 | class IAudioOut final : public ServiceFramework<IAudioOut> { |
| 25 | public: | 25 | public: |
| 26 | explicit IAudioOut(Core::System& system_, AudioCore::AudioOut::Manager& manager, | 26 | explicit IAudioOut(Core::System& system_, AudioCore::AudioOut::Manager& manager, |
| 27 | size_t session_id, std::string& device_name, | 27 | size_t session_id, const std::string& device_name, |
| 28 | const AudioOutParameter& in_params, u32 handle, u64 applet_resource_user_id) | 28 | const AudioOutParameter& in_params, u32 handle, u64 applet_resource_user_id) |
| 29 | : ServiceFramework{system_, "IAudioOut", ServiceThreadType::CreateNew}, | 29 | : ServiceFramework{system_, "IAudioOut", ServiceThreadType::CreateNew}, |
| 30 | service_context{system_, "IAudioOut"}, event{service_context.CreateEvent( | 30 | service_context{system_, "IAudioOut"}, event{service_context.CreateEvent( |
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index 3b26e96de..2f871de31 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp | |||
| @@ -868,7 +868,7 @@ bool Controller_NPad::VibrateControllerAtIndex(Core::HID::NpadIdType npad_id, | |||
| 868 | return false; | 868 | return false; |
| 869 | } | 869 | } |
| 870 | 870 | ||
| 871 | if (!controller.device->IsVibrationEnabled()) { | 871 | if (!controller.device->IsVibrationEnabled(device_index)) { |
| 872 | if (controller.vibration[device_index].latest_vibration_value.low_amplitude != 0.0f || | 872 | if (controller.vibration[device_index].latest_vibration_value.low_amplitude != 0.0f || |
| 873 | controller.vibration[device_index].latest_vibration_value.high_amplitude != 0.0f) { | 873 | controller.vibration[device_index].latest_vibration_value.high_amplitude != 0.0f) { |
| 874 | // Send an empty vibration to stop any vibrations. | 874 | // Send an empty vibration to stop any vibrations. |
| @@ -1001,7 +1001,7 @@ void Controller_NPad::InitializeVibrationDeviceAtIndex(Core::HID::NpadIdType npa | |||
| 1001 | } | 1001 | } |
| 1002 | 1002 | ||
| 1003 | controller.vibration[device_index].device_mounted = | 1003 | controller.vibration[device_index].device_mounted = |
| 1004 | controller.device->TestVibration(device_index); | 1004 | controller.device->IsVibrationEnabled(device_index); |
| 1005 | } | 1005 | } |
| 1006 | 1006 | ||
| 1007 | void Controller_NPad::SetPermitVibrationSession(bool permit_vibration_session) { | 1007 | void Controller_NPad::SetPermitVibrationSession(bool permit_vibration_session) { |
diff --git a/src/core/hle/service/nfp/amiibo_crypto.cpp b/src/core/hle/service/nfp/amiibo_crypto.cpp index c32a6816b..167e29572 100644 --- a/src/core/hle/service/nfp/amiibo_crypto.cpp +++ b/src/core/hle/service/nfp/amiibo_crypto.cpp | |||
| @@ -9,6 +9,7 @@ | |||
| 9 | #include <mbedtls/hmac_drbg.h> | 9 | #include <mbedtls/hmac_drbg.h> |
| 10 | 10 | ||
| 11 | #include "common/fs/file.h" | 11 | #include "common/fs/file.h" |
| 12 | #include "common/fs/fs.h" | ||
| 12 | #include "common/fs/path_util.h" | 13 | #include "common/fs/path_util.h" |
| 13 | #include "common/logging/log.h" | 14 | #include "common/logging/log.h" |
| 14 | #include "core/hle/service/mii/mii_manager.h" | 15 | #include "core/hle/service/mii/mii_manager.h" |
| @@ -279,7 +280,7 @@ bool LoadKeys(InternalKey& locked_secret, InternalKey& unfixed_info) { | |||
| 279 | Common::FS::FileType::BinaryFile}; | 280 | Common::FS::FileType::BinaryFile}; |
| 280 | 281 | ||
| 281 | if (!keys_file.IsOpen()) { | 282 | if (!keys_file.IsOpen()) { |
| 282 | LOG_ERROR(Service_NFP, "No keys detected"); | 283 | LOG_ERROR(Service_NFP, "Failed to open key file"); |
| 283 | return false; | 284 | return false; |
| 284 | } | 285 | } |
| 285 | 286 | ||
| @@ -295,6 +296,11 @@ bool LoadKeys(InternalKey& locked_secret, InternalKey& unfixed_info) { | |||
| 295 | return true; | 296 | return true; |
| 296 | } | 297 | } |
| 297 | 298 | ||
| 299 | bool IsKeyAvailable() { | ||
| 300 | const auto yuzu_keys_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::KeysDir); | ||
| 301 | return Common::FS::Exists(yuzu_keys_dir / "key_retail.bin"); | ||
| 302 | } | ||
| 303 | |||
| 298 | bool DecodeAmiibo(const EncryptedNTAG215File& encrypted_tag_data, NTAG215File& tag_data) { | 304 | bool DecodeAmiibo(const EncryptedNTAG215File& encrypted_tag_data, NTAG215File& tag_data) { |
| 299 | InternalKey locked_secret{}; | 305 | InternalKey locked_secret{}; |
| 300 | InternalKey unfixed_info{}; | 306 | InternalKey unfixed_info{}; |
diff --git a/src/core/hle/service/nfp/amiibo_crypto.h b/src/core/hle/service/nfp/amiibo_crypto.h index 0175ced91..1fa61174e 100644 --- a/src/core/hle/service/nfp/amiibo_crypto.h +++ b/src/core/hle/service/nfp/amiibo_crypto.h | |||
| @@ -91,6 +91,9 @@ void Cipher(const DerivedKeys& keys, const NTAG215File& in_data, NTAG215File& ou | |||
| 91 | /// Loads both amiibo keys from key_retail.bin | 91 | /// Loads both amiibo keys from key_retail.bin |
| 92 | bool LoadKeys(InternalKey& locked_secret, InternalKey& unfixed_info); | 92 | bool LoadKeys(InternalKey& locked_secret, InternalKey& unfixed_info); |
| 93 | 93 | ||
| 94 | /// Returns true if key_retail.bin exist | ||
| 95 | bool IsKeyAvailable(); | ||
| 96 | |||
| 94 | /// Decodes encripted amiibo data returns true if output is valid | 97 | /// Decodes encripted amiibo data returns true if output is valid |
| 95 | bool DecodeAmiibo(const EncryptedNTAG215File& encrypted_tag_data, NTAG215File& tag_data); | 98 | bool DecodeAmiibo(const EncryptedNTAG215File& encrypted_tag_data, NTAG215File& tag_data); |
| 96 | 99 | ||
diff --git a/src/core/hle/service/nfp/nfp_device.cpp b/src/core/hle/service/nfp/nfp_device.cpp index 76f8a267a..b19672560 100644 --- a/src/core/hle/service/nfp/nfp_device.cpp +++ b/src/core/hle/service/nfp/nfp_device.cpp | |||
| @@ -17,6 +17,7 @@ | |||
| 17 | #include "core/hle/ipc_helpers.h" | 17 | #include "core/hle/ipc_helpers.h" |
| 18 | #include "core/hle/kernel/k_event.h" | 18 | #include "core/hle/kernel/k_event.h" |
| 19 | #include "core/hle/service/mii/mii_manager.h" | 19 | #include "core/hle/service/mii/mii_manager.h" |
| 20 | #include "core/hle/service/mii/types.h" | ||
| 20 | #include "core/hle/service/nfp/amiibo_crypto.h" | 21 | #include "core/hle/service/nfp/amiibo_crypto.h" |
| 21 | #include "core/hle/service/nfp/nfp.h" | 22 | #include "core/hle/service/nfp/nfp.h" |
| 22 | #include "core/hle/service/nfp/nfp_device.h" | 23 | #include "core/hle/service/nfp/nfp_device.h" |
| @@ -233,6 +234,14 @@ Result NfpDevice::Mount(MountTarget mount_target_) { | |||
| 233 | return NotAnAmiibo; | 234 | return NotAnAmiibo; |
| 234 | } | 235 | } |
| 235 | 236 | ||
| 237 | // Mark amiibos as read only when keys are missing | ||
| 238 | if (!AmiiboCrypto::IsKeyAvailable()) { | ||
| 239 | LOG_ERROR(Service_NFP, "No keys detected"); | ||
| 240 | device_state = DeviceState::TagMounted; | ||
| 241 | mount_target = MountTarget::Rom; | ||
| 242 | return ResultSuccess; | ||
| 243 | } | ||
| 244 | |||
| 236 | if (!AmiiboCrypto::DecodeAmiibo(encrypted_tag_data, tag_data)) { | 245 | if (!AmiiboCrypto::DecodeAmiibo(encrypted_tag_data, tag_data)) { |
| 237 | LOG_ERROR(Service_NFP, "Can't decode amiibo {}", device_state); | 246 | LOG_ERROR(Service_NFP, "Can't decode amiibo {}", device_state); |
| 238 | return CorruptedData; | 247 | return CorruptedData; |
diff --git a/src/core/hle/service/nfp/nfp_device.h b/src/core/hle/service/nfp/nfp_device.h index a5b72cf19..76d0e9ae4 100644 --- a/src/core/hle/service/nfp/nfp_device.h +++ b/src/core/hle/service/nfp/nfp_device.h | |||
| @@ -8,7 +8,6 @@ | |||
| 8 | 8 | ||
| 9 | #include "common/common_funcs.h" | 9 | #include "common/common_funcs.h" |
| 10 | #include "core/hle/service/kernel_helpers.h" | 10 | #include "core/hle/service/kernel_helpers.h" |
| 11 | #include "core/hle/service/mii/types.h" | ||
| 12 | #include "core/hle/service/nfp/nfp_types.h" | 11 | #include "core/hle/service/nfp/nfp_types.h" |
| 13 | #include "core/hle/service/service.h" | 12 | #include "core/hle/service/service.h" |
| 14 | 13 | ||
diff --git a/src/core/hle/service/nfp/nfp_types.h b/src/core/hle/service/nfp/nfp_types.h index c09f9ddb6..63d5917cb 100644 --- a/src/core/hle/service/nfp/nfp_types.h +++ b/src/core/hle/service/nfp/nfp_types.h | |||
| @@ -17,11 +17,6 @@ enum class ServiceType : u32 { | |||
| 17 | System, | 17 | System, |
| 18 | }; | 18 | }; |
| 19 | 19 | ||
| 20 | enum class State : u32 { | ||
| 21 | NonInitialized, | ||
| 22 | Initialized, | ||
| 23 | }; | ||
| 24 | |||
| 25 | enum class DeviceState : u32 { | 20 | enum class DeviceState : u32 { |
| 26 | Initialized, | 21 | Initialized, |
| 27 | SearchingForTag, | 22 | SearchingForTag, |
diff --git a/src/core/hle/service/nfp/nfp_user.cpp b/src/core/hle/service/nfp/nfp_user.cpp index 4ed53b534..33e2ef518 100644 --- a/src/core/hle/service/nfp/nfp_user.cpp +++ b/src/core/hle/service/nfp/nfp_user.cpp | |||
| @@ -6,12 +6,9 @@ | |||
| 6 | 6 | ||
| 7 | #include "common/logging/log.h" | 7 | #include "common/logging/log.h" |
| 8 | #include "core/core.h" | 8 | #include "core/core.h" |
| 9 | #include "core/hid/emulated_controller.h" | ||
| 10 | #include "core/hid/hid_core.h" | ||
| 11 | #include "core/hid/hid_types.h" | 9 | #include "core/hid/hid_types.h" |
| 12 | #include "core/hle/ipc_helpers.h" | 10 | #include "core/hle/ipc_helpers.h" |
| 13 | #include "core/hle/kernel/k_event.h" | 11 | #include "core/hle/kernel/k_event.h" |
| 14 | #include "core/hle/service/mii/mii_manager.h" | ||
| 15 | #include "core/hle/service/nfp/nfp_device.h" | 12 | #include "core/hle/service/nfp/nfp_device.h" |
| 16 | #include "core/hle/service/nfp/nfp_result.h" | 13 | #include "core/hle/service/nfp/nfp_result.h" |
| 17 | #include "core/hle/service/nfp/nfp_user.h" | 14 | #include "core/hle/service/nfp/nfp_user.h" |
diff --git a/src/core/hle/service/nfp/nfp_user.h b/src/core/hle/service/nfp/nfp_user.h index 68c60ae82..47aff3695 100644 --- a/src/core/hle/service/nfp/nfp_user.h +++ b/src/core/hle/service/nfp/nfp_user.h | |||
| @@ -4,8 +4,7 @@ | |||
| 4 | #pragma once | 4 | #pragma once |
| 5 | 5 | ||
| 6 | #include "core/hle/service/kernel_helpers.h" | 6 | #include "core/hle/service/kernel_helpers.h" |
| 7 | #include "core/hle/service/nfp/nfp.h" | 7 | #include "core/hle/service/service.h" |
| 8 | #include "core/hle/service/nfp/nfp_types.h" | ||
| 9 | 8 | ||
| 10 | namespace Service::NFP { | 9 | namespace Service::NFP { |
| 11 | class NfpDevice; | 10 | class NfpDevice; |
| @@ -15,6 +14,11 @@ public: | |||
| 15 | explicit IUser(Core::System& system_); | 14 | explicit IUser(Core::System& system_); |
| 16 | 15 | ||
| 17 | private: | 16 | private: |
| 17 | enum class State : u32 { | ||
| 18 | NonInitialized, | ||
| 19 | Initialized, | ||
| 20 | }; | ||
| 21 | |||
| 18 | void Initialize(Kernel::HLERequestContext& ctx); | 22 | void Initialize(Kernel::HLERequestContext& ctx); |
| 19 | void Finalize(Kernel::HLERequestContext& ctx); | 23 | void Finalize(Kernel::HLERequestContext& ctx); |
| 20 | void ListDevices(Kernel::HLERequestContext& ctx); | 24 | void ListDevices(Kernel::HLERequestContext& ctx); |
diff --git a/src/input_common/drivers/gc_adapter.cpp b/src/input_common/drivers/gc_adapter.cpp index f4dd24e7d..826fa2109 100644 --- a/src/input_common/drivers/gc_adapter.cpp +++ b/src/input_common/drivers/gc_adapter.cpp | |||
| @@ -324,7 +324,7 @@ bool GCAdapter::GetGCEndpoint(libusb_device* device) { | |||
| 324 | return true; | 324 | return true; |
| 325 | } | 325 | } |
| 326 | 326 | ||
| 327 | Common::Input::VibrationError GCAdapter::SetRumble( | 327 | Common::Input::VibrationError GCAdapter::SetVibration( |
| 328 | const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) { | 328 | const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) { |
| 329 | const auto mean_amplitude = (vibration.low_amplitude + vibration.high_amplitude) * 0.5f; | 329 | const auto mean_amplitude = (vibration.low_amplitude + vibration.high_amplitude) * 0.5f; |
| 330 | const auto processed_amplitude = | 330 | const auto processed_amplitude = |
| @@ -338,6 +338,10 @@ Common::Input::VibrationError GCAdapter::SetRumble( | |||
| 338 | return Common::Input::VibrationError::None; | 338 | return Common::Input::VibrationError::None; |
| 339 | } | 339 | } |
| 340 | 340 | ||
| 341 | bool GCAdapter::IsVibrationEnabled([[maybe_unused]] const PadIdentifier& identifier) { | ||
| 342 | return rumble_enabled; | ||
| 343 | } | ||
| 344 | |||
| 341 | void GCAdapter::UpdateVibrations() { | 345 | void GCAdapter::UpdateVibrations() { |
| 342 | // Use 8 states to keep the switching between on/off fast enough for | 346 | // Use 8 states to keep the switching between on/off fast enough for |
| 343 | // a human to feel different vibration strenght | 347 | // a human to feel different vibration strenght |
diff --git a/src/input_common/drivers/gc_adapter.h b/src/input_common/drivers/gc_adapter.h index 8682da847..7f81767f7 100644 --- a/src/input_common/drivers/gc_adapter.h +++ b/src/input_common/drivers/gc_adapter.h | |||
| @@ -25,9 +25,11 @@ public: | |||
| 25 | explicit GCAdapter(std::string input_engine_); | 25 | explicit GCAdapter(std::string input_engine_); |
| 26 | ~GCAdapter() override; | 26 | ~GCAdapter() override; |
| 27 | 27 | ||
| 28 | Common::Input::VibrationError SetRumble( | 28 | Common::Input::VibrationError SetVibration( |
| 29 | const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) override; | 29 | const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) override; |
| 30 | 30 | ||
| 31 | bool IsVibrationEnabled(const PadIdentifier& identifier) override; | ||
| 32 | |||
| 31 | /// Used for automapping features | 33 | /// Used for automapping features |
| 32 | std::vector<Common::ParamPackage> GetInputDevices() const override; | 34 | std::vector<Common::ParamPackage> GetInputDevices() const override; |
| 33 | ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params) override; | 35 | ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params) override; |
diff --git a/src/input_common/drivers/sdl_driver.cpp b/src/input_common/drivers/sdl_driver.cpp index c175a8853..45ce588f0 100644 --- a/src/input_common/drivers/sdl_driver.cpp +++ b/src/input_common/drivers/sdl_driver.cpp | |||
| @@ -114,6 +114,20 @@ public: | |||
| 114 | } | 114 | } |
| 115 | return false; | 115 | return false; |
| 116 | } | 116 | } |
| 117 | |||
| 118 | void EnableVibration(bool is_enabled) { | ||
| 119 | has_vibration = is_enabled; | ||
| 120 | is_vibration_tested = true; | ||
| 121 | } | ||
| 122 | |||
| 123 | bool HasVibration() const { | ||
| 124 | return has_vibration; | ||
| 125 | } | ||
| 126 | |||
| 127 | bool IsVibrationTested() const { | ||
| 128 | return is_vibration_tested; | ||
| 129 | } | ||
| 130 | |||
| 117 | /** | 131 | /** |
| 118 | * The Pad identifier of the joystick | 132 | * The Pad identifier of the joystick |
| 119 | */ | 133 | */ |
| @@ -236,6 +250,8 @@ private: | |||
| 236 | u64 last_motion_update{}; | 250 | u64 last_motion_update{}; |
| 237 | bool has_gyro{false}; | 251 | bool has_gyro{false}; |
| 238 | bool has_accel{false}; | 252 | bool has_accel{false}; |
| 253 | bool has_vibration{false}; | ||
| 254 | bool is_vibration_tested{false}; | ||
| 239 | BasicMotion motion; | 255 | BasicMotion motion; |
| 240 | }; | 256 | }; |
| 241 | 257 | ||
| @@ -517,7 +533,7 @@ std::vector<Common::ParamPackage> SDLDriver::GetInputDevices() const { | |||
| 517 | return devices; | 533 | return devices; |
| 518 | } | 534 | } |
| 519 | 535 | ||
| 520 | Common::Input::VibrationError SDLDriver::SetRumble( | 536 | Common::Input::VibrationError SDLDriver::SetVibration( |
| 521 | const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) { | 537 | const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) { |
| 522 | const auto joystick = | 538 | const auto joystick = |
| 523 | GetSDLJoystickByGUID(identifier.guid.RawString(), static_cast<int>(identifier.port)); | 539 | GetSDLJoystickByGUID(identifier.guid.RawString(), static_cast<int>(identifier.port)); |
| @@ -546,13 +562,6 @@ Common::Input::VibrationError SDLDriver::SetRumble( | |||
| 546 | .type = Common::Input::VibrationAmplificationType::Exponential, | 562 | .type = Common::Input::VibrationAmplificationType::Exponential, |
| 547 | }; | 563 | }; |
| 548 | 564 | ||
| 549 | if (vibration.type == Common::Input::VibrationAmplificationType::Test) { | ||
| 550 | if (!joystick->RumblePlay(new_vibration)) { | ||
| 551 | return Common::Input::VibrationError::Unknown; | ||
| 552 | } | ||
| 553 | return Common::Input::VibrationError::None; | ||
| 554 | } | ||
| 555 | |||
| 556 | vibration_queue.Push(VibrationRequest{ | 565 | vibration_queue.Push(VibrationRequest{ |
| 557 | .identifier = identifier, | 566 | .identifier = identifier, |
| 558 | .vibration = new_vibration, | 567 | .vibration = new_vibration, |
| @@ -561,6 +570,45 @@ Common::Input::VibrationError SDLDriver::SetRumble( | |||
| 561 | return Common::Input::VibrationError::None; | 570 | return Common::Input::VibrationError::None; |
| 562 | } | 571 | } |
| 563 | 572 | ||
| 573 | bool SDLDriver::IsVibrationEnabled(const PadIdentifier& identifier) { | ||
| 574 | const auto joystick = | ||
| 575 | GetSDLJoystickByGUID(identifier.guid.RawString(), static_cast<int>(identifier.port)); | ||
| 576 | |||
| 577 | constexpr Common::Input::VibrationStatus test_vibration{ | ||
| 578 | .low_amplitude = 1, | ||
| 579 | .low_frequency = 160.0f, | ||
| 580 | .high_amplitude = 1, | ||
| 581 | .high_frequency = 320.0f, | ||
| 582 | .type = Common::Input::VibrationAmplificationType::Exponential, | ||
| 583 | }; | ||
| 584 | |||
| 585 | constexpr Common::Input::VibrationStatus zero_vibration{ | ||
| 586 | .low_amplitude = 0, | ||
| 587 | .low_frequency = 160.0f, | ||
| 588 | .high_amplitude = 0, | ||
| 589 | .high_frequency = 320.0f, | ||
| 590 | .type = Common::Input::VibrationAmplificationType::Exponential, | ||
| 591 | }; | ||
| 592 | |||
| 593 | if (joystick->IsVibrationTested()) { | ||
| 594 | return joystick->HasVibration(); | ||
| 595 | } | ||
| 596 | |||
| 597 | // First vibration might fail | ||
| 598 | joystick->RumblePlay(test_vibration); | ||
| 599 | |||
| 600 | // Wait for about 15ms to ensure the controller is ready for the stop command | ||
| 601 | std::this_thread::sleep_for(std::chrono::milliseconds(15)); | ||
| 602 | |||
| 603 | if (!joystick->RumblePlay(zero_vibration)) { | ||
| 604 | joystick->EnableVibration(false); | ||
| 605 | return false; | ||
| 606 | } | ||
| 607 | |||
| 608 | joystick->EnableVibration(true); | ||
| 609 | return true; | ||
| 610 | } | ||
| 611 | |||
| 564 | void SDLDriver::SendVibrations() { | 612 | void SDLDriver::SendVibrations() { |
| 565 | while (!vibration_queue.Empty()) { | 613 | while (!vibration_queue.Empty()) { |
| 566 | VibrationRequest request; | 614 | VibrationRequest request; |
diff --git a/src/input_common/drivers/sdl_driver.h b/src/input_common/drivers/sdl_driver.h index fc3a44572..d1b4471cf 100644 --- a/src/input_common/drivers/sdl_driver.h +++ b/src/input_common/drivers/sdl_driver.h | |||
| @@ -61,9 +61,11 @@ public: | |||
| 61 | 61 | ||
| 62 | bool IsStickInverted(const Common::ParamPackage& params) override; | 62 | bool IsStickInverted(const Common::ParamPackage& params) override; |
| 63 | 63 | ||
| 64 | Common::Input::VibrationError SetRumble( | 64 | Common::Input::VibrationError SetVibration( |
| 65 | const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) override; | 65 | const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) override; |
| 66 | 66 | ||
| 67 | bool IsVibrationEnabled(const PadIdentifier& identifier) override; | ||
| 68 | |||
| 67 | private: | 69 | private: |
| 68 | struct VibrationRequest { | 70 | struct VibrationRequest { |
| 69 | PadIdentifier identifier; | 71 | PadIdentifier identifier; |
diff --git a/src/input_common/input_engine.h b/src/input_common/input_engine.h index cfbdb26bd..d4c264a8e 100644 --- a/src/input_common/input_engine.h +++ b/src/input_common/input_engine.h | |||
| @@ -108,12 +108,17 @@ public: | |||
| 108 | [[maybe_unused]] const Common::Input::LedStatus& led_status) {} | 108 | [[maybe_unused]] const Common::Input::LedStatus& led_status) {} |
| 109 | 109 | ||
| 110 | // Sets rumble to a controller | 110 | // Sets rumble to a controller |
| 111 | virtual Common::Input::VibrationError SetRumble( | 111 | virtual Common::Input::VibrationError SetVibration( |
| 112 | [[maybe_unused]] const PadIdentifier& identifier, | 112 | [[maybe_unused]] const PadIdentifier& identifier, |
| 113 | [[maybe_unused]] const Common::Input::VibrationStatus& vibration) { | 113 | [[maybe_unused]] const Common::Input::VibrationStatus& vibration) { |
| 114 | return Common::Input::VibrationError::NotSupported; | 114 | return Common::Input::VibrationError::NotSupported; |
| 115 | } | 115 | } |
| 116 | 116 | ||
| 117 | // Returns true if device supports vibrations | ||
| 118 | virtual bool IsVibrationEnabled([[maybe_unused]] const PadIdentifier& identifier) { | ||
| 119 | return false; | ||
| 120 | } | ||
| 121 | |||
| 117 | // Sets polling mode to a controller | 122 | // Sets polling mode to a controller |
| 118 | virtual Common::Input::PollingError SetPollingMode( | 123 | virtual Common::Input::PollingError SetPollingMode( |
| 119 | [[maybe_unused]] const PadIdentifier& identifier, | 124 | [[maybe_unused]] const PadIdentifier& identifier, |
diff --git a/src/input_common/input_poller.cpp b/src/input_common/input_poller.cpp index ccc3076ca..4ac182147 100644 --- a/src/input_common/input_poller.cpp +++ b/src/input_common/input_poller.cpp | |||
| @@ -763,7 +763,11 @@ public: | |||
| 763 | 763 | ||
| 764 | Common::Input::VibrationError SetVibration( | 764 | Common::Input::VibrationError SetVibration( |
| 765 | const Common::Input::VibrationStatus& vibration_status) override { | 765 | const Common::Input::VibrationStatus& vibration_status) override { |
| 766 | return input_engine->SetRumble(identifier, vibration_status); | 766 | return input_engine->SetVibration(identifier, vibration_status); |
| 767 | } | ||
| 768 | |||
| 769 | bool IsVibrationEnabled() override { | ||
| 770 | return input_engine->IsVibrationEnabled(identifier); | ||
| 767 | } | 771 | } |
| 768 | 772 | ||
| 769 | Common::Input::PollingError SetPollingMode(Common::Input::PollingMode polling_mode) override { | 773 | Common::Input::PollingError SetPollingMode(Common::Input::PollingMode polling_mode) override { |
diff --git a/src/shader_recompiler/frontend/ir/microinstruction.cpp b/src/shader_recompiler/frontend/ir/microinstruction.cpp index 468782eb1..84417980b 100644 --- a/src/shader_recompiler/frontend/ir/microinstruction.cpp +++ b/src/shader_recompiler/frontend/ir/microinstruction.cpp | |||
| @@ -325,11 +325,6 @@ void Inst::AddPhiOperand(Block* predecessor, const Value& value) { | |||
| 325 | phi_args.emplace_back(predecessor, value); | 325 | phi_args.emplace_back(predecessor, value); |
| 326 | } | 326 | } |
| 327 | 327 | ||
| 328 | void Inst::ErasePhiOperand(size_t index) { | ||
| 329 | const auto operand_it{phi_args.begin() + static_cast<ptrdiff_t>(index)}; | ||
| 330 | phi_args.erase(operand_it); | ||
| 331 | } | ||
| 332 | |||
| 333 | void Inst::OrderPhiArgs() { | 328 | void Inst::OrderPhiArgs() { |
| 334 | if (op != Opcode::Phi) { | 329 | if (op != Opcode::Phi) { |
| 335 | throw LogicError("{} is not a Phi instruction", op); | 330 | throw LogicError("{} is not a Phi instruction", op); |
diff --git a/src/shader_recompiler/frontend/ir/value.h b/src/shader_recompiler/frontend/ir/value.h index 1a2e4ccb6..6a673ca05 100644 --- a/src/shader_recompiler/frontend/ir/value.h +++ b/src/shader_recompiler/frontend/ir/value.h | |||
| @@ -178,13 +178,9 @@ public: | |||
| 178 | 178 | ||
| 179 | /// Get a pointer to the block of a phi argument. | 179 | /// Get a pointer to the block of a phi argument. |
| 180 | [[nodiscard]] Block* PhiBlock(size_t index) const; | 180 | [[nodiscard]] Block* PhiBlock(size_t index) const; |
| 181 | |||
| 182 | /// Add phi operand to a phi instruction. | 181 | /// Add phi operand to a phi instruction. |
| 183 | void AddPhiOperand(Block* predecessor, const Value& value); | 182 | void AddPhiOperand(Block* predecessor, const Value& value); |
| 184 | 183 | ||
| 185 | // Erase the phi operand at the given index. | ||
| 186 | void ErasePhiOperand(size_t index); | ||
| 187 | |||
| 188 | /// Orders the Phi arguments from farthest away to nearest. | 184 | /// Orders the Phi arguments from farthest away to nearest. |
| 189 | void OrderPhiArgs(); | 185 | void OrderPhiArgs(); |
| 190 | 186 | ||
diff --git a/src/shader_recompiler/ir_opt/dead_code_elimination_pass.cpp b/src/shader_recompiler/ir_opt/dead_code_elimination_pass.cpp index 9a7d47344..1bd8afd6f 100644 --- a/src/shader_recompiler/ir_opt/dead_code_elimination_pass.cpp +++ b/src/shader_recompiler/ir_opt/dead_code_elimination_pass.cpp | |||
| @@ -1,104 +1,24 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #include <algorithm> | ||
| 5 | |||
| 6 | #include <boost/container/small_vector.hpp> | ||
| 7 | |||
| 8 | #include "shader_recompiler/frontend/ir/basic_block.h" | 4 | #include "shader_recompiler/frontend/ir/basic_block.h" |
| 9 | #include "shader_recompiler/frontend/ir/value.h" | 5 | #include "shader_recompiler/frontend/ir/value.h" |
| 10 | #include "shader_recompiler/ir_opt/passes.h" | 6 | #include "shader_recompiler/ir_opt/passes.h" |
| 11 | 7 | ||
| 12 | namespace Shader::Optimization { | 8 | namespace Shader::Optimization { |
| 13 | namespace { | 9 | |
| 14 | template <bool TEST_USES> | 10 | void DeadCodeEliminationPass(IR::Program& program) { |
| 15 | void DeadInstElimination(IR::Block* const block) { | ||
| 16 | // We iterate over the instructions in reverse order. | 11 | // We iterate over the instructions in reverse order. |
| 17 | // This is because removing an instruction reduces the number of uses for earlier instructions. | 12 | // This is because removing an instruction reduces the number of uses for earlier instructions. |
| 18 | auto it{block->end()}; | 13 | for (IR::Block* const block : program.post_order_blocks) { |
| 19 | while (it != block->begin()) { | 14 | auto it{block->end()}; |
| 20 | --it; | 15 | while (it != block->begin()) { |
| 21 | if constexpr (TEST_USES) { | 16 | --it; |
| 22 | if (it->HasUses() || it->MayHaveSideEffects()) { | 17 | if (!it->HasUses() && !it->MayHaveSideEffects()) { |
| 23 | continue; | 18 | it->Invalidate(); |
| 24 | } | 19 | it = block->Instructions().erase(it); |
| 25 | } | ||
| 26 | it->Invalidate(); | ||
| 27 | it = block->Instructions().erase(it); | ||
| 28 | } | ||
| 29 | } | ||
| 30 | |||
| 31 | void DeletedPhiArgElimination(IR::Program& program, std::span<const IR::Block*> dead_blocks) { | ||
| 32 | for (IR::Block* const block : program.blocks) { | ||
| 33 | for (IR::Inst& phi : *block) { | ||
| 34 | if (!IR::IsPhi(phi)) { | ||
| 35 | continue; | ||
| 36 | } | ||
| 37 | for (size_t i = 0; i < phi.NumArgs(); ++i) { | ||
| 38 | if (std::ranges::find(dead_blocks, phi.PhiBlock(i)) == dead_blocks.end()) { | ||
| 39 | continue; | ||
| 40 | } | ||
| 41 | // Phi operand at this index is an unreachable block | ||
| 42 | phi.ErasePhiOperand(i); | ||
| 43 | --i; | ||
| 44 | } | ||
| 45 | } | ||
| 46 | } | ||
| 47 | } | ||
| 48 | |||
| 49 | void DeadBranchElimination(IR::Program& program) { | ||
| 50 | boost::container::small_vector<const IR::Block*, 3> dead_blocks; | ||
| 51 | const auto begin_it{program.syntax_list.begin()}; | ||
| 52 | for (auto node_it = begin_it; node_it != program.syntax_list.end(); ++node_it) { | ||
| 53 | if (node_it->type != IR::AbstractSyntaxNode::Type::If) { | ||
| 54 | continue; | ||
| 55 | } | ||
| 56 | IR::Inst* const cond_ref{node_it->data.if_node.cond.Inst()}; | ||
| 57 | const IR::U1 cond{cond_ref->Arg(0)}; | ||
| 58 | if (!cond.IsImmediate()) { | ||
| 59 | continue; | ||
| 60 | } | ||
| 61 | if (cond.U1()) { | ||
| 62 | continue; | ||
| 63 | } | ||
| 64 | // False immediate condition. Remove condition ref, erase the entire branch. | ||
| 65 | cond_ref->Invalidate(); | ||
| 66 | // Account for nested if-statements within the if(false) branch | ||
| 67 | u32 nested_ifs{1u}; | ||
| 68 | while (node_it->type != IR::AbstractSyntaxNode::Type::EndIf || nested_ifs > 0) { | ||
| 69 | node_it = program.syntax_list.erase(node_it); | ||
| 70 | switch (node_it->type) { | ||
| 71 | case IR::AbstractSyntaxNode::Type::If: | ||
| 72 | ++nested_ifs; | ||
| 73 | break; | ||
| 74 | case IR::AbstractSyntaxNode::Type::EndIf: | ||
| 75 | --nested_ifs; | ||
| 76 | break; | ||
| 77 | case IR::AbstractSyntaxNode::Type::Block: { | ||
| 78 | IR::Block* const block{node_it->data.block}; | ||
| 79 | DeadInstElimination<false>(block); | ||
| 80 | dead_blocks.push_back(block); | ||
| 81 | break; | ||
| 82 | } | ||
| 83 | default: | ||
| 84 | break; | ||
| 85 | } | 20 | } |
| 86 | } | 21 | } |
| 87 | // Erase EndIf node of the if(false) branch | ||
| 88 | node_it = program.syntax_list.erase(node_it); | ||
| 89 | // Account for loop increment | ||
| 90 | --node_it; | ||
| 91 | } | ||
| 92 | if (!dead_blocks.empty()) { | ||
| 93 | DeletedPhiArgElimination(program, std::span(dead_blocks.data(), dead_blocks.size())); | ||
| 94 | } | ||
| 95 | } | ||
| 96 | } // namespace | ||
| 97 | |||
| 98 | void DeadCodeEliminationPass(IR::Program& program) { | ||
| 99 | DeadBranchElimination(program); | ||
| 100 | for (IR::Block* const block : program.post_order_blocks) { | ||
| 101 | DeadInstElimination<true>(block); | ||
| 102 | } | 22 | } |
| 103 | } | 23 | } |
| 104 | 24 | ||
diff --git a/src/tests/video_core/buffer_base.cpp b/src/tests/video_core/buffer_base.cpp index 71121e42a..f7236afab 100644 --- a/src/tests/video_core/buffer_base.cpp +++ b/src/tests/video_core/buffer_base.cpp | |||
| @@ -44,7 +44,7 @@ public: | |||
| 44 | 44 | ||
| 45 | [[nodiscard]] unsigned Count() const noexcept { | 45 | [[nodiscard]] unsigned Count() const noexcept { |
| 46 | unsigned count = 0; | 46 | unsigned count = 0; |
| 47 | for (const auto [index, value] : page_table) { | 47 | for (const auto& [index, value] : page_table) { |
| 48 | count += value; | 48 | count += value; |
| 49 | } | 49 | } |
| 50 | return count; | 50 | return count; |