diff options
Diffstat (limited to 'src')
63 files changed, 863 insertions, 757 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 39d038493..39ae573b2 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt | |||
| @@ -36,7 +36,6 @@ if (MSVC) | |||
| 36 | # /GT - Supports fiber safety for data allocated using static thread-local storage | 36 | # /GT - Supports fiber safety for data allocated using static thread-local storage |
| 37 | add_compile_options( | 37 | add_compile_options( |
| 38 | /MP | 38 | /MP |
| 39 | /Zi | ||
| 40 | /Zm200 | 39 | /Zm200 |
| 41 | /Zo | 40 | /Zo |
| 42 | /permissive- | 41 | /permissive- |
| @@ -79,6 +78,13 @@ if (MSVC) | |||
| 79 | /we5245 # 'function': unreferenced function with internal linkage has been removed | 78 | /we5245 # 'function': unreferenced function with internal linkage has been removed |
| 80 | ) | 79 | ) |
| 81 | 80 | ||
| 81 | if (USE_CCACHE) | ||
| 82 | # when caching, we need to use /Z7 to downgrade debug info to use an older but more cachable format | ||
| 83 | add_compile_options(/Z7) | ||
| 84 | else() | ||
| 85 | add_compile_options(/Zi) | ||
| 86 | endif() | ||
| 87 | |||
| 82 | if (ARCHITECTURE_x86_64) | 88 | if (ARCHITECTURE_x86_64) |
| 83 | add_compile_options(/QIntel-jcc-erratum) | 89 | add_compile_options(/QIntel-jcc-erratum) |
| 84 | endif() | 90 | endif() |
diff --git a/src/audio_core/audio_renderer.cpp b/src/audio_core/audio_renderer.cpp index 2ee0a96ed..9191ca093 100644 --- a/src/audio_core/audio_renderer.cpp +++ b/src/audio_core/audio_renderer.cpp | |||
| @@ -2,6 +2,7 @@ | |||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #include <limits> | 4 | #include <limits> |
| 5 | #include <optional> | ||
| 5 | #include <vector> | 6 | #include <vector> |
| 6 | 7 | ||
| 7 | #include "audio_core/audio_out.h" | 8 | #include "audio_core/audio_out.h" |
| @@ -88,9 +89,12 @@ AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing_, Core::Memor | |||
| 88 | stream = audio_out->OpenStream( | 89 | stream = audio_out->OpenStream( |
| 89 | core_timing, params.sample_rate, AudioCommon::STREAM_NUM_CHANNELS, | 90 | core_timing, params.sample_rate, AudioCommon::STREAM_NUM_CHANNELS, |
| 90 | fmt::format("AudioRenderer-Instance{}", instance_number), std::move(release_callback)); | 91 | fmt::format("AudioRenderer-Instance{}", instance_number), std::move(release_callback)); |
| 91 | process_event = Core::Timing::CreateEvent( | 92 | process_event = |
| 92 | fmt::format("AudioRenderer-Instance{}-Process", instance_number), | 93 | Core::Timing::CreateEvent(fmt::format("AudioRenderer-Instance{}-Process", instance_number), |
| 93 | [this](std::uintptr_t, std::chrono::nanoseconds) { ReleaseAndQueueBuffers(); }); | 94 | [this](std::uintptr_t, s64, std::chrono::nanoseconds) { |
| 95 | ReleaseAndQueueBuffers(); | ||
| 96 | return std::nullopt; | ||
| 97 | }); | ||
| 94 | for (s32 i = 0; i < NUM_BUFFERS; ++i) { | 98 | for (s32 i = 0; i < NUM_BUFFERS; ++i) { |
| 95 | QueueMixedBuffer(i); | 99 | QueueMixedBuffer(i); |
| 96 | } | 100 | } |
diff --git a/src/audio_core/stream.cpp b/src/audio_core/stream.cpp index f8034b04b..cf3d94c53 100644 --- a/src/audio_core/stream.cpp +++ b/src/audio_core/stream.cpp | |||
| @@ -34,9 +34,10 @@ Stream::Stream(Core::Timing::CoreTiming& core_timing_, u32 sample_rate_, Format | |||
| 34 | ReleaseCallback&& release_callback_, SinkStream& sink_stream_, std::string&& name_) | 34 | ReleaseCallback&& release_callback_, SinkStream& sink_stream_, std::string&& name_) |
| 35 | : sample_rate{sample_rate_}, format{format_}, release_callback{std::move(release_callback_)}, | 35 | : sample_rate{sample_rate_}, format{format_}, release_callback{std::move(release_callback_)}, |
| 36 | sink_stream{sink_stream_}, core_timing{core_timing_}, name{std::move(name_)} { | 36 | sink_stream{sink_stream_}, core_timing{core_timing_}, name{std::move(name_)} { |
| 37 | release_event = | 37 | release_event = Core::Timing::CreateEvent( |
| 38 | Core::Timing::CreateEvent(name, [this](std::uintptr_t, std::chrono::nanoseconds ns_late) { | 38 | name, [this](std::uintptr_t, s64 time, std::chrono::nanoseconds ns_late) { |
| 39 | ReleaseActiveBuffer(ns_late); | 39 | ReleaseActiveBuffer(ns_late); |
| 40 | return std::nullopt; | ||
| 40 | }); | 41 | }); |
| 41 | } | 42 | } |
| 42 | 43 | ||
diff --git a/src/common/bit_field.h b/src/common/bit_field.h index 16d805694..7e1df62b1 100644 --- a/src/common/bit_field.h +++ b/src/common/bit_field.h | |||
| @@ -146,7 +146,16 @@ public: | |||
| 146 | } | 146 | } |
| 147 | 147 | ||
| 148 | constexpr void Assign(const T& value) { | 148 | constexpr void Assign(const T& value) { |
| 149 | #ifdef _MSC_VER | ||
| 149 | storage = static_cast<StorageType>((storage & ~mask) | FormatValue(value)); | 150 | storage = static_cast<StorageType>((storage & ~mask) | FormatValue(value)); |
| 151 | #else | ||
| 152 | // Explicitly reload with memcpy to avoid compiler aliasing quirks | ||
| 153 | // regarding optimization: GCC/Clang clobber chained stores to | ||
| 154 | // different bitfields in the same struct with the last value. | ||
| 155 | StorageTypeWithEndian storage_; | ||
| 156 | std::memcpy(&storage_, &storage, sizeof(storage_)); | ||
| 157 | storage = static_cast<StorageType>((storage_ & ~mask) | FormatValue(value)); | ||
| 158 | #endif | ||
| 150 | } | 159 | } |
| 151 | 160 | ||
| 152 | [[nodiscard]] constexpr T Value() const { | 161 | [[nodiscard]] constexpr T Value() const { |
diff --git a/src/common/common_funcs.h b/src/common/common_funcs.h index adc31c758..e1e2a90fc 100644 --- a/src/common/common_funcs.h +++ b/src/common/common_funcs.h | |||
| @@ -18,14 +18,16 @@ | |||
| 18 | /// Helper macros to insert unused bytes or words to properly align structs. These values will be | 18 | /// Helper macros to insert unused bytes or words to properly align structs. These values will be |
| 19 | /// zero-initialized. | 19 | /// zero-initialized. |
| 20 | #define INSERT_PADDING_BYTES(num_bytes) \ | 20 | #define INSERT_PADDING_BYTES(num_bytes) \ |
| 21 | std::array<u8, num_bytes> CONCAT2(pad, __LINE__) {} | 21 | [[maybe_unused]] std::array<u8, num_bytes> CONCAT2(pad, __LINE__) {} |
| 22 | #define INSERT_PADDING_WORDS(num_words) \ | 22 | #define INSERT_PADDING_WORDS(num_words) \ |
| 23 | std::array<u32, num_words> CONCAT2(pad, __LINE__) {} | 23 | [[maybe_unused]] std::array<u32, num_words> CONCAT2(pad, __LINE__) {} |
| 24 | 24 | ||
| 25 | /// These are similar to the INSERT_PADDING_* macros but do not zero-initialize the contents. | 25 | /// These are similar to the INSERT_PADDING_* macros but do not zero-initialize the contents. |
| 26 | /// This keeps the structure trivial to construct. | 26 | /// This keeps the structure trivial to construct. |
| 27 | #define INSERT_PADDING_BYTES_NOINIT(num_bytes) std::array<u8, num_bytes> CONCAT2(pad, __LINE__) | 27 | #define INSERT_PADDING_BYTES_NOINIT(num_bytes) \ |
| 28 | #define INSERT_PADDING_WORDS_NOINIT(num_words) std::array<u32, num_words> CONCAT2(pad, __LINE__) | 28 | [[maybe_unused]] std::array<u8, num_bytes> CONCAT2(pad, __LINE__) |
| 29 | #define INSERT_PADDING_WORDS_NOINIT(num_words) \ | ||
| 30 | [[maybe_unused]] std::array<u32, num_words> CONCAT2(pad, __LINE__) | ||
| 29 | 31 | ||
| 30 | #ifndef _MSC_VER | 32 | #ifndef _MSC_VER |
| 31 | 33 | ||
diff --git a/src/common/fiber.cpp b/src/common/fiber.cpp index f9aeb692a..bc92b360b 100644 --- a/src/common/fiber.cpp +++ b/src/common/fiber.cpp | |||
| @@ -20,10 +20,8 @@ struct Fiber::FiberImpl { | |||
| 20 | VirtualBuffer<u8> rewind_stack; | 20 | VirtualBuffer<u8> rewind_stack; |
| 21 | 21 | ||
| 22 | std::mutex guard; | 22 | std::mutex guard; |
| 23 | std::function<void(void*)> entry_point; | 23 | std::function<void()> entry_point; |
| 24 | std::function<void(void*)> rewind_point; | 24 | std::function<void()> rewind_point; |
| 25 | void* rewind_parameter{}; | ||
| 26 | void* start_parameter{}; | ||
| 27 | std::shared_ptr<Fiber> previous_fiber; | 25 | std::shared_ptr<Fiber> previous_fiber; |
| 28 | bool is_thread_fiber{}; | 26 | bool is_thread_fiber{}; |
| 29 | bool released{}; | 27 | bool released{}; |
| @@ -34,13 +32,8 @@ struct Fiber::FiberImpl { | |||
| 34 | boost::context::detail::fcontext_t rewind_context{}; | 32 | boost::context::detail::fcontext_t rewind_context{}; |
| 35 | }; | 33 | }; |
| 36 | 34 | ||
| 37 | void Fiber::SetStartParameter(void* new_parameter) { | 35 | void Fiber::SetRewindPoint(std::function<void()>&& rewind_func) { |
| 38 | impl->start_parameter = new_parameter; | ||
| 39 | } | ||
| 40 | |||
| 41 | void Fiber::SetRewindPoint(std::function<void(void*)>&& rewind_func, void* rewind_param) { | ||
| 42 | impl->rewind_point = std::move(rewind_func); | 36 | impl->rewind_point = std::move(rewind_func); |
| 43 | impl->rewind_parameter = rewind_param; | ||
| 44 | } | 37 | } |
| 45 | 38 | ||
| 46 | void Fiber::Start(boost::context::detail::transfer_t& transfer) { | 39 | void Fiber::Start(boost::context::detail::transfer_t& transfer) { |
| @@ -48,7 +41,7 @@ void Fiber::Start(boost::context::detail::transfer_t& transfer) { | |||
| 48 | impl->previous_fiber->impl->context = transfer.fctx; | 41 | impl->previous_fiber->impl->context = transfer.fctx; |
| 49 | impl->previous_fiber->impl->guard.unlock(); | 42 | impl->previous_fiber->impl->guard.unlock(); |
| 50 | impl->previous_fiber.reset(); | 43 | impl->previous_fiber.reset(); |
| 51 | impl->entry_point(impl->start_parameter); | 44 | impl->entry_point(); |
| 52 | UNREACHABLE(); | 45 | UNREACHABLE(); |
| 53 | } | 46 | } |
| 54 | 47 | ||
| @@ -59,7 +52,7 @@ void Fiber::OnRewind([[maybe_unused]] boost::context::detail::transfer_t& transf | |||
| 59 | u8* tmp = impl->stack_limit; | 52 | u8* tmp = impl->stack_limit; |
| 60 | impl->stack_limit = impl->rewind_stack_limit; | 53 | impl->stack_limit = impl->rewind_stack_limit; |
| 61 | impl->rewind_stack_limit = tmp; | 54 | impl->rewind_stack_limit = tmp; |
| 62 | impl->rewind_point(impl->rewind_parameter); | 55 | impl->rewind_point(); |
| 63 | UNREACHABLE(); | 56 | UNREACHABLE(); |
| 64 | } | 57 | } |
| 65 | 58 | ||
| @@ -73,10 +66,8 @@ void Fiber::RewindStartFunc(boost::context::detail::transfer_t transfer) { | |||
| 73 | fiber->OnRewind(transfer); | 66 | fiber->OnRewind(transfer); |
| 74 | } | 67 | } |
| 75 | 68 | ||
| 76 | Fiber::Fiber(std::function<void(void*)>&& entry_point_func, void* start_parameter) | 69 | Fiber::Fiber(std::function<void()>&& entry_point_func) : impl{std::make_unique<FiberImpl>()} { |
| 77 | : impl{std::make_unique<FiberImpl>()} { | ||
| 78 | impl->entry_point = std::move(entry_point_func); | 70 | impl->entry_point = std::move(entry_point_func); |
| 79 | impl->start_parameter = start_parameter; | ||
| 80 | impl->stack_limit = impl->stack.data(); | 71 | impl->stack_limit = impl->stack.data(); |
| 81 | impl->rewind_stack_limit = impl->rewind_stack.data(); | 72 | impl->rewind_stack_limit = impl->rewind_stack.data(); |
| 82 | u8* stack_base = impl->stack_limit + default_stack_size; | 73 | u8* stack_base = impl->stack_limit + default_stack_size; |
diff --git a/src/common/fiber.h b/src/common/fiber.h index 873604bc6..f24d333a3 100644 --- a/src/common/fiber.h +++ b/src/common/fiber.h | |||
| @@ -29,7 +29,7 @@ namespace Common { | |||
| 29 | */ | 29 | */ |
| 30 | class Fiber { | 30 | class Fiber { |
| 31 | public: | 31 | public: |
| 32 | Fiber(std::function<void(void*)>&& entry_point_func, void* start_parameter); | 32 | Fiber(std::function<void()>&& entry_point_func); |
| 33 | ~Fiber(); | 33 | ~Fiber(); |
| 34 | 34 | ||
| 35 | Fiber(const Fiber&) = delete; | 35 | Fiber(const Fiber&) = delete; |
| @@ -43,16 +43,13 @@ public: | |||
| 43 | static void YieldTo(std::weak_ptr<Fiber> weak_from, Fiber& to); | 43 | static void YieldTo(std::weak_ptr<Fiber> weak_from, Fiber& to); |
| 44 | [[nodiscard]] static std::shared_ptr<Fiber> ThreadToFiber(); | 44 | [[nodiscard]] static std::shared_ptr<Fiber> ThreadToFiber(); |
| 45 | 45 | ||
| 46 | void SetRewindPoint(std::function<void(void*)>&& rewind_func, void* rewind_param); | 46 | void SetRewindPoint(std::function<void()>&& rewind_func); |
| 47 | 47 | ||
| 48 | void Rewind(); | 48 | void Rewind(); |
| 49 | 49 | ||
| 50 | /// Only call from main thread's fiber | 50 | /// Only call from main thread's fiber |
| 51 | void Exit(); | 51 | void Exit(); |
| 52 | 52 | ||
| 53 | /// Changes the start parameter of the fiber. Has no effect if the fiber already started | ||
| 54 | void SetStartParameter(void* new_parameter); | ||
| 55 | |||
| 56 | private: | 53 | private: |
| 57 | Fiber(); | 54 | Fiber(); |
| 58 | 55 | ||
diff --git a/src/common/settings.h b/src/common/settings.h index a507744a2..3583a2e70 100644 --- a/src/common/settings.h +++ b/src/common/settings.h | |||
| @@ -101,15 +101,15 @@ struct ResolutionScalingInfo { | |||
| 101 | } | 101 | } |
| 102 | }; | 102 | }; |
| 103 | 103 | ||
| 104 | /** The BasicSetting class is a simple resource manager. It defines a label and default value | 104 | /** The Setting class is a simple resource manager. It defines a label and default value alongside |
| 105 | * alongside the actual value of the setting for simpler and less-error prone use with frontend | 105 | * the actual value of the setting for simpler and less-error prone use with frontend |
| 106 | * configurations. Setting a default value and label is required, though subclasses may deviate from | 106 | * configurations. Specifying a default value and label is required. A minimum and maximum range can |
| 107 | * this requirement. | 107 | * be specified for sanitization. |
| 108 | */ | 108 | */ |
| 109 | template <typename Type> | 109 | template <typename Type> |
| 110 | class BasicSetting { | 110 | class Setting { |
| 111 | protected: | 111 | protected: |
| 112 | BasicSetting() = default; | 112 | Setting() = default; |
| 113 | 113 | ||
| 114 | /** | 114 | /** |
| 115 | * Only sets the setting to the given initializer, leaving the other members to their default | 115 | * Only sets the setting to the given initializer, leaving the other members to their default |
| @@ -117,7 +117,7 @@ protected: | |||
| 117 | * | 117 | * |
| 118 | * @param global_val Initial value of the setting | 118 | * @param global_val Initial value of the setting |
| 119 | */ | 119 | */ |
| 120 | explicit BasicSetting(const Type& global_val) : global{global_val} {} | 120 | explicit Setting(const Type& val) : value{val} {} |
| 121 | 121 | ||
| 122 | public: | 122 | public: |
| 123 | /** | 123 | /** |
| @@ -126,9 +126,22 @@ public: | |||
| 126 | * @param default_val Intial value of the setting, and default value of the setting | 126 | * @param default_val Intial value of the setting, and default value of the setting |
| 127 | * @param name Label for the setting | 127 | * @param name Label for the setting |
| 128 | */ | 128 | */ |
| 129 | explicit BasicSetting(const Type& default_val, const std::string& name) | 129 | explicit Setting(const Type& default_val, const std::string& name) |
| 130 | : default_value{default_val}, global{default_val}, label{name} {} | 130 | : value{default_val}, default_value{default_val}, ranged{false}, label{name} {} |
| 131 | virtual ~BasicSetting() = default; | 131 | virtual ~Setting() = default; |
| 132 | |||
| 133 | /** | ||
| 134 | * Sets a default value, minimum value, maximum value, and label. | ||
| 135 | * | ||
| 136 | * @param default_val Intial value of the setting, and default value of the setting | ||
| 137 | * @param min_val Sets the minimum allowed value of the setting | ||
| 138 | * @param max_val Sets the maximum allowed value of the setting | ||
| 139 | * @param name Label for the setting | ||
| 140 | */ | ||
| 141 | explicit Setting(const Type& default_val, const Type& min_val, const Type& max_val, | ||
| 142 | const std::string& name) | ||
| 143 | : value{default_val}, default_value{default_val}, maximum{max_val}, minimum{min_val}, | ||
| 144 | ranged{true}, label{name} {} | ||
| 132 | 145 | ||
| 133 | /** | 146 | /** |
| 134 | * Returns a reference to the setting's value. | 147 | * Returns a reference to the setting's value. |
| @@ -136,17 +149,17 @@ public: | |||
| 136 | * @returns A reference to the setting | 149 | * @returns A reference to the setting |
| 137 | */ | 150 | */ |
| 138 | [[nodiscard]] virtual const Type& GetValue() const { | 151 | [[nodiscard]] virtual const Type& GetValue() const { |
| 139 | return global; | 152 | return value; |
| 140 | } | 153 | } |
| 141 | 154 | ||
| 142 | /** | 155 | /** |
| 143 | * Sets the setting to the given value. | 156 | * Sets the setting to the given value. |
| 144 | * | 157 | * |
| 145 | * @param value The desired value | 158 | * @param val The desired value |
| 146 | */ | 159 | */ |
| 147 | virtual void SetValue(const Type& value) { | 160 | virtual void SetValue(const Type& val) { |
| 148 | Type temp{value}; | 161 | Type temp{(ranged) ? std::clamp(val, minimum, maximum) : val}; |
| 149 | std::swap(global, temp); | 162 | std::swap(value, temp); |
| 150 | } | 163 | } |
| 151 | 164 | ||
| 152 | /** | 165 | /** |
| @@ -170,14 +183,14 @@ public: | |||
| 170 | /** | 183 | /** |
| 171 | * Assigns a value to the setting. | 184 | * Assigns a value to the setting. |
| 172 | * | 185 | * |
| 173 | * @param value The desired setting value | 186 | * @param val The desired setting value |
| 174 | * | 187 | * |
| 175 | * @returns A reference to the setting | 188 | * @returns A reference to the setting |
| 176 | */ | 189 | */ |
| 177 | virtual const Type& operator=(const Type& value) { | 190 | virtual const Type& operator=(const Type& val) { |
| 178 | Type temp{value}; | 191 | Type temp{(ranged) ? std::clamp(val, minimum, maximum) : val}; |
| 179 | std::swap(global, temp); | 192 | std::swap(value, temp); |
| 180 | return global; | 193 | return value; |
| 181 | } | 194 | } |
| 182 | 195 | ||
| 183 | /** | 196 | /** |
| @@ -186,72 +199,28 @@ public: | |||
| 186 | * @returns A reference to the setting | 199 | * @returns A reference to the setting |
| 187 | */ | 200 | */ |
| 188 | explicit virtual operator const Type&() const { | 201 | explicit virtual operator const Type&() const { |
| 189 | return global; | 202 | return value; |
| 190 | } | 203 | } |
| 191 | 204 | ||
| 192 | protected: | 205 | protected: |
| 206 | Type value{}; ///< The setting | ||
| 193 | const Type default_value{}; ///< The default value | 207 | const Type default_value{}; ///< The default value |
| 194 | Type global{}; ///< The setting | 208 | const Type maximum{}; ///< Maximum allowed value of the setting |
| 209 | const Type minimum{}; ///< Minimum allowed value of the setting | ||
| 210 | const bool ranged; ///< The setting has sanitization ranges | ||
| 195 | const std::string label{}; ///< The setting's label | 211 | const std::string label{}; ///< The setting's label |
| 196 | }; | 212 | }; |
| 197 | 213 | ||
| 198 | /** | 214 | /** |
| 199 | * BasicRangedSetting class is intended for use with quantifiable settings that need a more | 215 | * The SwitchableSetting class is a slightly more complex version of the Setting class. This adds a |
| 200 | * restrictive range than implicitly defined by its type. Implements a minimum and maximum that is | ||
| 201 | * simply used to sanitize SetValue and the assignment overload. | ||
| 202 | */ | ||
| 203 | template <typename Type> | ||
| 204 | class BasicRangedSetting : virtual public BasicSetting<Type> { | ||
| 205 | public: | ||
| 206 | /** | ||
| 207 | * Sets a default value, minimum value, maximum value, and label. | ||
| 208 | * | ||
| 209 | * @param default_val Intial value of the setting, and default value of the setting | ||
| 210 | * @param min_val Sets the minimum allowed value of the setting | ||
| 211 | * @param max_val Sets the maximum allowed value of the setting | ||
| 212 | * @param name Label for the setting | ||
| 213 | */ | ||
| 214 | explicit BasicRangedSetting(const Type& default_val, const Type& min_val, const Type& max_val, | ||
| 215 | const std::string& name) | ||
| 216 | : BasicSetting<Type>{default_val, name}, minimum{min_val}, maximum{max_val} {} | ||
| 217 | virtual ~BasicRangedSetting() = default; | ||
| 218 | |||
| 219 | /** | ||
| 220 | * Like BasicSetting's SetValue, except value is clamped to the range of the setting. | ||
| 221 | * | ||
| 222 | * @param value The desired value | ||
| 223 | */ | ||
| 224 | void SetValue(const Type& value) override { | ||
| 225 | this->global = std::clamp(value, minimum, maximum); | ||
| 226 | } | ||
| 227 | |||
| 228 | /** | ||
| 229 | * Like BasicSetting's assignment overload, except value is clamped to the range of the setting. | ||
| 230 | * | ||
| 231 | * @param value The desired value | ||
| 232 | * @returns A reference to the setting's value | ||
| 233 | */ | ||
| 234 | const Type& operator=(const Type& value) override { | ||
| 235 | this->global = std::clamp(value, minimum, maximum); | ||
| 236 | return this->global; | ||
| 237 | } | ||
| 238 | |||
| 239 | const Type minimum; ///< Minimum allowed value of the setting | ||
| 240 | const Type maximum; ///< Maximum allowed value of the setting | ||
| 241 | }; | ||
| 242 | |||
| 243 | /** | ||
| 244 | * The Setting class is a slightly more complex version of the BasicSetting class. This adds a | ||
| 245 | * custom setting to switch to when a guest application specifically requires it. The effect is that | 216 | * custom setting to switch to when a guest application specifically requires it. The effect is that |
| 246 | * other components of the emulator can access the setting's intended value without any need for the | 217 | * other components of the emulator can access the setting's intended value without any need for the |
| 247 | * component to ask whether the custom or global setting is needed at the moment. | 218 | * component to ask whether the custom or global setting is needed at the moment. |
| 248 | * | 219 | * |
| 249 | * By default, the global setting is used. | 220 | * By default, the global setting is used. |
| 250 | * | ||
| 251 | * Like the BasicSetting, this requires setting a default value and label to use. | ||
| 252 | */ | 221 | */ |
| 253 | template <typename Type> | 222 | template <typename Type> |
| 254 | class Setting : virtual public BasicSetting<Type> { | 223 | class SwitchableSetting : virtual public Setting<Type> { |
| 255 | public: | 224 | public: |
| 256 | /** | 225 | /** |
| 257 | * Sets a default value, label, and setting value. | 226 | * Sets a default value, label, and setting value. |
| @@ -259,9 +228,21 @@ public: | |||
| 259 | * @param default_val Intial value of the setting, and default value of the setting | 228 | * @param default_val Intial value of the setting, and default value of the setting |
| 260 | * @param name Label for the setting | 229 | * @param name Label for the setting |
| 261 | */ | 230 | */ |
| 262 | explicit Setting(const Type& default_val, const std::string& name) | 231 | explicit SwitchableSetting(const Type& default_val, const std::string& name) |
| 263 | : BasicSetting<Type>(default_val, name) {} | 232 | : Setting<Type>{default_val, name} {} |
| 264 | virtual ~Setting() = default; | 233 | virtual ~SwitchableSetting() = default; |
| 234 | |||
| 235 | /** | ||
| 236 | * Sets a default value, minimum value, maximum value, and label. | ||
| 237 | * | ||
| 238 | * @param default_val Intial value of the setting, and default value of the setting | ||
| 239 | * @param min_val Sets the minimum allowed value of the setting | ||
| 240 | * @param max_val Sets the maximum allowed value of the setting | ||
| 241 | * @param name Label for the setting | ||
| 242 | */ | ||
| 243 | explicit SwitchableSetting(const Type& default_val, const Type& min_val, const Type& max_val, | ||
| 244 | const std::string& name) | ||
| 245 | : Setting<Type>{default_val, min_val, max_val, name} {} | ||
| 265 | 246 | ||
| 266 | /** | 247 | /** |
| 267 | * Tells this setting to represent either the global or custom setting when other member | 248 | * Tells this setting to represent either the global or custom setting when other member |
| @@ -292,13 +273,13 @@ public: | |||
| 292 | */ | 273 | */ |
| 293 | [[nodiscard]] virtual const Type& GetValue() const override { | 274 | [[nodiscard]] virtual const Type& GetValue() const override { |
| 294 | if (use_global) { | 275 | if (use_global) { |
| 295 | return this->global; | 276 | return this->value; |
| 296 | } | 277 | } |
| 297 | return custom; | 278 | return custom; |
| 298 | } | 279 | } |
| 299 | [[nodiscard]] virtual const Type& GetValue(bool need_global) const { | 280 | [[nodiscard]] virtual const Type& GetValue(bool need_global) const { |
| 300 | if (use_global || need_global) { | 281 | if (use_global || need_global) { |
| 301 | return this->global; | 282 | return this->value; |
| 302 | } | 283 | } |
| 303 | return custom; | 284 | return custom; |
| 304 | } | 285 | } |
| @@ -306,12 +287,12 @@ public: | |||
| 306 | /** | 287 | /** |
| 307 | * Sets the current setting value depending on the global state. | 288 | * Sets the current setting value depending on the global state. |
| 308 | * | 289 | * |
| 309 | * @param value The new value | 290 | * @param val The new value |
| 310 | */ | 291 | */ |
| 311 | void SetValue(const Type& value) override { | 292 | void SetValue(const Type& val) override { |
| 312 | Type temp{value}; | 293 | Type temp{(this->ranged) ? std::clamp(val, this->minimum, this->maximum) : val}; |
| 313 | if (use_global) { | 294 | if (use_global) { |
| 314 | std::swap(this->global, temp); | 295 | std::swap(this->value, temp); |
| 315 | } else { | 296 | } else { |
| 316 | std::swap(custom, temp); | 297 | std::swap(custom, temp); |
| 317 | } | 298 | } |
| @@ -320,15 +301,15 @@ public: | |||
| 320 | /** | 301 | /** |
| 321 | * Assigns the current setting value depending on the global state. | 302 | * Assigns the current setting value depending on the global state. |
| 322 | * | 303 | * |
| 323 | * @param value The new value | 304 | * @param val The new value |
| 324 | * | 305 | * |
| 325 | * @returns A reference to the current setting value | 306 | * @returns A reference to the current setting value |
| 326 | */ | 307 | */ |
| 327 | const Type& operator=(const Type& value) override { | 308 | const Type& operator=(const Type& val) override { |
| 328 | Type temp{value}; | 309 | Type temp{(this->ranged) ? std::clamp(val, this->minimum, this->maximum) : val}; |
| 329 | if (use_global) { | 310 | if (use_global) { |
| 330 | std::swap(this->global, temp); | 311 | std::swap(this->value, temp); |
| 331 | return this->global; | 312 | return this->value; |
| 332 | } | 313 | } |
| 333 | std::swap(custom, temp); | 314 | std::swap(custom, temp); |
| 334 | return custom; | 315 | return custom; |
| @@ -341,7 +322,7 @@ public: | |||
| 341 | */ | 322 | */ |
| 342 | virtual explicit operator const Type&() const override { | 323 | virtual explicit operator const Type&() const override { |
| 343 | if (use_global) { | 324 | if (use_global) { |
| 344 | return this->global; | 325 | return this->value; |
| 345 | } | 326 | } |
| 346 | return custom; | 327 | return custom; |
| 347 | } | 328 | } |
| @@ -352,75 +333,6 @@ protected: | |||
| 352 | }; | 333 | }; |
| 353 | 334 | ||
| 354 | /** | 335 | /** |
| 355 | * RangedSetting is a Setting that implements a maximum and minimum value for its setting. Intended | ||
| 356 | * for use with quantifiable settings. | ||
| 357 | */ | ||
| 358 | template <typename Type> | ||
| 359 | class RangedSetting final : public BasicRangedSetting<Type>, public Setting<Type> { | ||
| 360 | public: | ||
| 361 | /** | ||
| 362 | * Sets a default value, minimum value, maximum value, and label. | ||
| 363 | * | ||
| 364 | * @param default_val Intial value of the setting, and default value of the setting | ||
| 365 | * @param min_val Sets the minimum allowed value of the setting | ||
| 366 | * @param max_val Sets the maximum allowed value of the setting | ||
| 367 | * @param name Label for the setting | ||
| 368 | */ | ||
| 369 | explicit RangedSetting(const Type& default_val, const Type& min_val, const Type& max_val, | ||
| 370 | const std::string& name) | ||
| 371 | : BasicSetting<Type>{default_val, name}, | ||
| 372 | BasicRangedSetting<Type>{default_val, min_val, max_val, name}, Setting<Type>{default_val, | ||
| 373 | name} {} | ||
| 374 | virtual ~RangedSetting() = default; | ||
| 375 | |||
| 376 | // The following are needed to avoid a MSVC bug | ||
| 377 | // (source: https://stackoverflow.com/questions/469508) | ||
| 378 | [[nodiscard]] const Type& GetValue() const override { | ||
| 379 | return Setting<Type>::GetValue(); | ||
| 380 | } | ||
| 381 | [[nodiscard]] const Type& GetValue(bool need_global) const override { | ||
| 382 | return Setting<Type>::GetValue(need_global); | ||
| 383 | } | ||
| 384 | explicit operator const Type&() const override { | ||
| 385 | if (this->use_global) { | ||
| 386 | return this->global; | ||
| 387 | } | ||
| 388 | return this->custom; | ||
| 389 | } | ||
| 390 | |||
| 391 | /** | ||
| 392 | * Like BasicSetting's SetValue, except value is clamped to the range of the setting. Sets the | ||
| 393 | * appropriate value depending on the global state. | ||
| 394 | * | ||
| 395 | * @param value The desired value | ||
| 396 | */ | ||
| 397 | void SetValue(const Type& value) override { | ||
| 398 | const Type temp = std::clamp(value, this->minimum, this->maximum); | ||
| 399 | if (this->use_global) { | ||
| 400 | this->global = temp; | ||
| 401 | } | ||
| 402 | this->custom = temp; | ||
| 403 | } | ||
| 404 | |||
| 405 | /** | ||
| 406 | * Like BasicSetting's assignment overload, except value is clamped to the range of the setting. | ||
| 407 | * Uses the appropriate value depending on the global state. | ||
| 408 | * | ||
| 409 | * @param value The desired value | ||
| 410 | * @returns A reference to the setting's value | ||
| 411 | */ | ||
| 412 | const Type& operator=(const Type& value) override { | ||
| 413 | const Type temp = std::clamp(value, this->minimum, this->maximum); | ||
| 414 | if (this->use_global) { | ||
| 415 | this->global = temp; | ||
| 416 | return this->global; | ||
| 417 | } | ||
| 418 | this->custom = temp; | ||
| 419 | return this->custom; | ||
| 420 | } | ||
| 421 | }; | ||
| 422 | |||
| 423 | /** | ||
| 424 | * The InputSetting class allows for getting a reference to either the global or custom members. | 336 | * The InputSetting class allows for getting a reference to either the global or custom members. |
| 425 | * This is required as we cannot easily modify the values of user-defined types within containers | 337 | * This is required as we cannot easily modify the values of user-defined types within containers |
| 426 | * using the SetValue() member function found in the Setting class. The primary purpose of this | 338 | * using the SetValue() member function found in the Setting class. The primary purpose of this |
| @@ -431,7 +343,7 @@ template <typename Type> | |||
| 431 | class InputSetting final { | 343 | class InputSetting final { |
| 432 | public: | 344 | public: |
| 433 | InputSetting() = default; | 345 | InputSetting() = default; |
| 434 | explicit InputSetting(Type val) : BasicSetting<Type>(val) {} | 346 | explicit InputSetting(Type val) : Setting<Type>(val) {} |
| 435 | ~InputSetting() = default; | 347 | ~InputSetting() = default; |
| 436 | void SetGlobal(bool to_global) { | 348 | void SetGlobal(bool to_global) { |
| 437 | use_global = to_global; | 349 | use_global = to_global; |
| @@ -459,175 +371,175 @@ struct TouchFromButtonMap { | |||
| 459 | 371 | ||
| 460 | struct Values { | 372 | struct Values { |
| 461 | // Audio | 373 | // Audio |
| 462 | BasicSetting<std::string> audio_device_id{"auto", "output_device"}; | 374 | Setting<std::string> audio_device_id{"auto", "output_device"}; |
| 463 | BasicSetting<std::string> sink_id{"auto", "output_engine"}; | 375 | Setting<std::string> sink_id{"auto", "output_engine"}; |
| 464 | BasicSetting<bool> audio_muted{false, "audio_muted"}; | 376 | Setting<bool> audio_muted{false, "audio_muted"}; |
| 465 | RangedSetting<u8> volume{100, 0, 100, "volume"}; | 377 | SwitchableSetting<u8> volume{100, 0, 100, "volume"}; |
| 466 | 378 | ||
| 467 | // Core | 379 | // Core |
| 468 | Setting<bool> use_multi_core{true, "use_multi_core"}; | 380 | SwitchableSetting<bool> use_multi_core{true, "use_multi_core"}; |
| 469 | Setting<bool> use_extended_memory_layout{false, "use_extended_memory_layout"}; | 381 | SwitchableSetting<bool> use_extended_memory_layout{false, "use_extended_memory_layout"}; |
| 470 | 382 | ||
| 471 | // Cpu | 383 | // Cpu |
| 472 | RangedSetting<CPUAccuracy> cpu_accuracy{CPUAccuracy::Auto, CPUAccuracy::Auto, | 384 | SwitchableSetting<CPUAccuracy> cpu_accuracy{CPUAccuracy::Auto, CPUAccuracy::Auto, |
| 473 | CPUAccuracy::Paranoid, "cpu_accuracy"}; | 385 | CPUAccuracy::Paranoid, "cpu_accuracy"}; |
| 474 | // TODO: remove cpu_accuracy_first_time, migration setting added 8 July 2021 | 386 | // TODO: remove cpu_accuracy_first_time, migration setting added 8 July 2021 |
| 475 | BasicSetting<bool> cpu_accuracy_first_time{true, "cpu_accuracy_first_time"}; | 387 | Setting<bool> cpu_accuracy_first_time{true, "cpu_accuracy_first_time"}; |
| 476 | BasicSetting<bool> cpu_debug_mode{false, "cpu_debug_mode"}; | 388 | Setting<bool> cpu_debug_mode{false, "cpu_debug_mode"}; |
| 477 | 389 | ||
| 478 | BasicSetting<bool> cpuopt_page_tables{true, "cpuopt_page_tables"}; | 390 | Setting<bool> cpuopt_page_tables{true, "cpuopt_page_tables"}; |
| 479 | BasicSetting<bool> cpuopt_block_linking{true, "cpuopt_block_linking"}; | 391 | Setting<bool> cpuopt_block_linking{true, "cpuopt_block_linking"}; |
| 480 | BasicSetting<bool> cpuopt_return_stack_buffer{true, "cpuopt_return_stack_buffer"}; | 392 | Setting<bool> cpuopt_return_stack_buffer{true, "cpuopt_return_stack_buffer"}; |
| 481 | BasicSetting<bool> cpuopt_fast_dispatcher{true, "cpuopt_fast_dispatcher"}; | 393 | Setting<bool> cpuopt_fast_dispatcher{true, "cpuopt_fast_dispatcher"}; |
| 482 | BasicSetting<bool> cpuopt_context_elimination{true, "cpuopt_context_elimination"}; | 394 | Setting<bool> cpuopt_context_elimination{true, "cpuopt_context_elimination"}; |
| 483 | BasicSetting<bool> cpuopt_const_prop{true, "cpuopt_const_prop"}; | 395 | Setting<bool> cpuopt_const_prop{true, "cpuopt_const_prop"}; |
| 484 | BasicSetting<bool> cpuopt_misc_ir{true, "cpuopt_misc_ir"}; | 396 | Setting<bool> cpuopt_misc_ir{true, "cpuopt_misc_ir"}; |
| 485 | BasicSetting<bool> cpuopt_reduce_misalign_checks{true, "cpuopt_reduce_misalign_checks"}; | 397 | Setting<bool> cpuopt_reduce_misalign_checks{true, "cpuopt_reduce_misalign_checks"}; |
| 486 | BasicSetting<bool> cpuopt_fastmem{true, "cpuopt_fastmem"}; | 398 | Setting<bool> cpuopt_fastmem{true, "cpuopt_fastmem"}; |
| 487 | BasicSetting<bool> cpuopt_fastmem_exclusives{true, "cpuopt_fastmem_exclusives"}; | 399 | Setting<bool> cpuopt_fastmem_exclusives{true, "cpuopt_fastmem_exclusives"}; |
| 488 | BasicSetting<bool> cpuopt_recompile_exclusives{true, "cpuopt_recompile_exclusives"}; | 400 | Setting<bool> cpuopt_recompile_exclusives{true, "cpuopt_recompile_exclusives"}; |
| 489 | 401 | ||
| 490 | Setting<bool> cpuopt_unsafe_unfuse_fma{true, "cpuopt_unsafe_unfuse_fma"}; | 402 | SwitchableSetting<bool> cpuopt_unsafe_unfuse_fma{true, "cpuopt_unsafe_unfuse_fma"}; |
| 491 | Setting<bool> cpuopt_unsafe_reduce_fp_error{true, "cpuopt_unsafe_reduce_fp_error"}; | 403 | SwitchableSetting<bool> cpuopt_unsafe_reduce_fp_error{true, "cpuopt_unsafe_reduce_fp_error"}; |
| 492 | Setting<bool> cpuopt_unsafe_ignore_standard_fpcr{true, "cpuopt_unsafe_ignore_standard_fpcr"}; | 404 | SwitchableSetting<bool> cpuopt_unsafe_ignore_standard_fpcr{ |
| 493 | Setting<bool> cpuopt_unsafe_inaccurate_nan{true, "cpuopt_unsafe_inaccurate_nan"}; | 405 | true, "cpuopt_unsafe_ignore_standard_fpcr"}; |
| 494 | Setting<bool> cpuopt_unsafe_fastmem_check{true, "cpuopt_unsafe_fastmem_check"}; | 406 | SwitchableSetting<bool> cpuopt_unsafe_inaccurate_nan{true, "cpuopt_unsafe_inaccurate_nan"}; |
| 495 | Setting<bool> cpuopt_unsafe_ignore_global_monitor{true, "cpuopt_unsafe_ignore_global_monitor"}; | 407 | SwitchableSetting<bool> cpuopt_unsafe_fastmem_check{true, "cpuopt_unsafe_fastmem_check"}; |
| 408 | SwitchableSetting<bool> cpuopt_unsafe_ignore_global_monitor{ | ||
| 409 | true, "cpuopt_unsafe_ignore_global_monitor"}; | ||
| 496 | 410 | ||
| 497 | // Renderer | 411 | // Renderer |
| 498 | RangedSetting<RendererBackend> renderer_backend{ | 412 | SwitchableSetting<RendererBackend> renderer_backend{ |
| 499 | RendererBackend::Vulkan, RendererBackend::OpenGL, RendererBackend::Vulkan, "backend"}; | 413 | RendererBackend::Vulkan, RendererBackend::OpenGL, RendererBackend::Vulkan, "backend"}; |
| 500 | BasicSetting<bool> renderer_debug{false, "debug"}; | 414 | Setting<bool> renderer_debug{false, "debug"}; |
| 501 | BasicSetting<bool> renderer_shader_feedback{false, "shader_feedback"}; | 415 | Setting<bool> renderer_shader_feedback{false, "shader_feedback"}; |
| 502 | BasicSetting<bool> enable_nsight_aftermath{false, "nsight_aftermath"}; | 416 | Setting<bool> enable_nsight_aftermath{false, "nsight_aftermath"}; |
| 503 | BasicSetting<bool> disable_shader_loop_safety_checks{false, | 417 | Setting<bool> disable_shader_loop_safety_checks{false, "disable_shader_loop_safety_checks"}; |
| 504 | "disable_shader_loop_safety_checks"}; | 418 | SwitchableSetting<int> vulkan_device{0, "vulkan_device"}; |
| 505 | Setting<int> vulkan_device{0, "vulkan_device"}; | ||
| 506 | 419 | ||
| 507 | ResolutionScalingInfo resolution_info{}; | 420 | ResolutionScalingInfo resolution_info{}; |
| 508 | Setting<ResolutionSetup> resolution_setup{ResolutionSetup::Res1X, "resolution_setup"}; | 421 | SwitchableSetting<ResolutionSetup> resolution_setup{ResolutionSetup::Res1X, "resolution_setup"}; |
| 509 | Setting<ScalingFilter> scaling_filter{ScalingFilter::Bilinear, "scaling_filter"}; | 422 | SwitchableSetting<ScalingFilter> scaling_filter{ScalingFilter::Bilinear, "scaling_filter"}; |
| 510 | Setting<AntiAliasing> anti_aliasing{AntiAliasing::None, "anti_aliasing"}; | 423 | SwitchableSetting<AntiAliasing> anti_aliasing{AntiAliasing::None, "anti_aliasing"}; |
| 511 | // *nix platforms may have issues with the borderless windowed fullscreen mode. | 424 | // *nix platforms may have issues with the borderless windowed fullscreen mode. |
| 512 | // Default to exclusive fullscreen on these platforms for now. | 425 | // Default to exclusive fullscreen on these platforms for now. |
| 513 | RangedSetting<FullscreenMode> fullscreen_mode{ | 426 | SwitchableSetting<FullscreenMode> fullscreen_mode{ |
| 514 | #ifdef _WIN32 | 427 | #ifdef _WIN32 |
| 515 | FullscreenMode::Borderless, | 428 | FullscreenMode::Borderless, |
| 516 | #else | 429 | #else |
| 517 | FullscreenMode::Exclusive, | 430 | FullscreenMode::Exclusive, |
| 518 | #endif | 431 | #endif |
| 519 | FullscreenMode::Borderless, FullscreenMode::Exclusive, "fullscreen_mode"}; | 432 | FullscreenMode::Borderless, FullscreenMode::Exclusive, "fullscreen_mode"}; |
| 520 | RangedSetting<int> aspect_ratio{0, 0, 3, "aspect_ratio"}; | 433 | SwitchableSetting<int> aspect_ratio{0, 0, 3, "aspect_ratio"}; |
| 521 | RangedSetting<int> max_anisotropy{0, 0, 5, "max_anisotropy"}; | 434 | SwitchableSetting<int> max_anisotropy{0, 0, 5, "max_anisotropy"}; |
| 522 | Setting<bool> use_speed_limit{true, "use_speed_limit"}; | 435 | SwitchableSetting<bool> use_speed_limit{true, "use_speed_limit"}; |
| 523 | RangedSetting<u16> speed_limit{100, 0, 9999, "speed_limit"}; | 436 | SwitchableSetting<u16> speed_limit{100, 0, 9999, "speed_limit"}; |
| 524 | Setting<bool> use_disk_shader_cache{true, "use_disk_shader_cache"}; | 437 | SwitchableSetting<bool> use_disk_shader_cache{true, "use_disk_shader_cache"}; |
| 525 | RangedSetting<GPUAccuracy> gpu_accuracy{GPUAccuracy::High, GPUAccuracy::Normal, | 438 | SwitchableSetting<GPUAccuracy> gpu_accuracy{GPUAccuracy::High, GPUAccuracy::Normal, |
| 526 | GPUAccuracy::Extreme, "gpu_accuracy"}; | 439 | GPUAccuracy::Extreme, "gpu_accuracy"}; |
| 527 | Setting<bool> use_asynchronous_gpu_emulation{true, "use_asynchronous_gpu_emulation"}; | 440 | SwitchableSetting<bool> use_asynchronous_gpu_emulation{true, "use_asynchronous_gpu_emulation"}; |
| 528 | Setting<NvdecEmulation> nvdec_emulation{NvdecEmulation::GPU, "nvdec_emulation"}; | 441 | SwitchableSetting<NvdecEmulation> nvdec_emulation{NvdecEmulation::GPU, "nvdec_emulation"}; |
| 529 | Setting<bool> accelerate_astc{true, "accelerate_astc"}; | 442 | SwitchableSetting<bool> accelerate_astc{true, "accelerate_astc"}; |
| 530 | Setting<bool> use_vsync{true, "use_vsync"}; | 443 | SwitchableSetting<bool> use_vsync{true, "use_vsync"}; |
| 531 | RangedSetting<u16> fps_cap{1000, 1, 1000, "fps_cap"}; | 444 | SwitchableSetting<u16> fps_cap{1000, 1, 1000, "fps_cap"}; |
| 532 | BasicSetting<bool> disable_fps_limit{false, "disable_fps_limit"}; | 445 | Setting<bool> disable_fps_limit{false, "disable_fps_limit"}; |
| 533 | RangedSetting<ShaderBackend> shader_backend{ShaderBackend::GLASM, ShaderBackend::GLSL, | 446 | SwitchableSetting<ShaderBackend> shader_backend{ShaderBackend::GLASM, ShaderBackend::GLSL, |
| 534 | ShaderBackend::SPIRV, "shader_backend"}; | 447 | ShaderBackend::SPIRV, "shader_backend"}; |
| 535 | Setting<bool> use_asynchronous_shaders{false, "use_asynchronous_shaders"}; | 448 | SwitchableSetting<bool> use_asynchronous_shaders{false, "use_asynchronous_shaders"}; |
| 536 | Setting<bool> use_fast_gpu_time{true, "use_fast_gpu_time"}; | 449 | SwitchableSetting<bool> use_fast_gpu_time{true, "use_fast_gpu_time"}; |
| 537 | 450 | ||
| 538 | Setting<u8> bg_red{0, "bg_red"}; | 451 | SwitchableSetting<u8> bg_red{0, "bg_red"}; |
| 539 | Setting<u8> bg_green{0, "bg_green"}; | 452 | SwitchableSetting<u8> bg_green{0, "bg_green"}; |
| 540 | Setting<u8> bg_blue{0, "bg_blue"}; | 453 | SwitchableSetting<u8> bg_blue{0, "bg_blue"}; |
| 541 | 454 | ||
| 542 | // System | 455 | // System |
| 543 | Setting<std::optional<u32>> rng_seed{std::optional<u32>(), "rng_seed"}; | 456 | SwitchableSetting<std::optional<u32>> rng_seed{std::optional<u32>(), "rng_seed"}; |
| 544 | // Measured in seconds since epoch | 457 | // Measured in seconds since epoch |
| 545 | std::optional<s64> custom_rtc; | 458 | std::optional<s64> custom_rtc; |
| 546 | // Set on game boot, reset on stop. Seconds difference between current time and `custom_rtc` | 459 | // Set on game boot, reset on stop. Seconds difference between current time and `custom_rtc` |
| 547 | s64 custom_rtc_differential; | 460 | s64 custom_rtc_differential; |
| 548 | 461 | ||
| 549 | BasicSetting<s32> current_user{0, "current_user"}; | 462 | Setting<s32> current_user{0, "current_user"}; |
| 550 | RangedSetting<s32> language_index{1, 0, 17, "language_index"}; | 463 | SwitchableSetting<s32> language_index{1, 0, 17, "language_index"}; |
| 551 | RangedSetting<s32> region_index{1, 0, 6, "region_index"}; | 464 | SwitchableSetting<s32> region_index{1, 0, 6, "region_index"}; |
| 552 | RangedSetting<s32> time_zone_index{0, 0, 45, "time_zone_index"}; | 465 | SwitchableSetting<s32> time_zone_index{0, 0, 45, "time_zone_index"}; |
| 553 | RangedSetting<s32> sound_index{1, 0, 2, "sound_index"}; | 466 | SwitchableSetting<s32> sound_index{1, 0, 2, "sound_index"}; |
| 554 | 467 | ||
| 555 | // Controls | 468 | // Controls |
| 556 | InputSetting<std::array<PlayerInput, 10>> players; | 469 | InputSetting<std::array<PlayerInput, 10>> players; |
| 557 | 470 | ||
| 558 | Setting<bool> use_docked_mode{true, "use_docked_mode"}; | 471 | SwitchableSetting<bool> use_docked_mode{true, "use_docked_mode"}; |
| 559 | 472 | ||
| 560 | BasicSetting<bool> enable_raw_input{false, "enable_raw_input"}; | 473 | Setting<bool> enable_raw_input{false, "enable_raw_input"}; |
| 561 | BasicSetting<bool> controller_navigation{true, "controller_navigation"}; | 474 | Setting<bool> controller_navigation{true, "controller_navigation"}; |
| 562 | 475 | ||
| 563 | Setting<bool> vibration_enabled{true, "vibration_enabled"}; | 476 | SwitchableSetting<bool> vibration_enabled{true, "vibration_enabled"}; |
| 564 | Setting<bool> enable_accurate_vibrations{false, "enable_accurate_vibrations"}; | 477 | SwitchableSetting<bool> enable_accurate_vibrations{false, "enable_accurate_vibrations"}; |
| 565 | 478 | ||
| 566 | Setting<bool> motion_enabled{true, "motion_enabled"}; | 479 | SwitchableSetting<bool> motion_enabled{true, "motion_enabled"}; |
| 567 | BasicSetting<std::string> udp_input_servers{"127.0.0.1:26760", "udp_input_servers"}; | 480 | Setting<std::string> udp_input_servers{"127.0.0.1:26760", "udp_input_servers"}; |
| 568 | BasicSetting<bool> enable_udp_controller{false, "enable_udp_controller"}; | 481 | Setting<bool> enable_udp_controller{false, "enable_udp_controller"}; |
| 569 | 482 | ||
| 570 | BasicSetting<bool> pause_tas_on_load{true, "pause_tas_on_load"}; | 483 | Setting<bool> pause_tas_on_load{true, "pause_tas_on_load"}; |
| 571 | BasicSetting<bool> tas_enable{false, "tas_enable"}; | 484 | Setting<bool> tas_enable{false, "tas_enable"}; |
| 572 | BasicSetting<bool> tas_loop{false, "tas_loop"}; | 485 | Setting<bool> tas_loop{false, "tas_loop"}; |
| 573 | 486 | ||
| 574 | BasicSetting<bool> mouse_panning{false, "mouse_panning"}; | 487 | Setting<bool> mouse_panning{false, "mouse_panning"}; |
| 575 | BasicRangedSetting<u8> mouse_panning_sensitivity{10, 1, 100, "mouse_panning_sensitivity"}; | 488 | Setting<u8> mouse_panning_sensitivity{10, 1, 100, "mouse_panning_sensitivity"}; |
| 576 | BasicSetting<bool> mouse_enabled{false, "mouse_enabled"}; | 489 | Setting<bool> mouse_enabled{false, "mouse_enabled"}; |
| 577 | 490 | ||
| 578 | BasicSetting<bool> emulate_analog_keyboard{false, "emulate_analog_keyboard"}; | 491 | Setting<bool> emulate_analog_keyboard{false, "emulate_analog_keyboard"}; |
| 579 | BasicSetting<bool> keyboard_enabled{false, "keyboard_enabled"}; | 492 | Setting<bool> keyboard_enabled{false, "keyboard_enabled"}; |
| 580 | 493 | ||
| 581 | BasicSetting<bool> debug_pad_enabled{false, "debug_pad_enabled"}; | 494 | Setting<bool> debug_pad_enabled{false, "debug_pad_enabled"}; |
| 582 | ButtonsRaw debug_pad_buttons; | 495 | ButtonsRaw debug_pad_buttons; |
| 583 | AnalogsRaw debug_pad_analogs; | 496 | AnalogsRaw debug_pad_analogs; |
| 584 | 497 | ||
| 585 | TouchscreenInput touchscreen; | 498 | TouchscreenInput touchscreen; |
| 586 | 499 | ||
| 587 | BasicSetting<std::string> touch_device{"min_x:100,min_y:50,max_x:1800,max_y:850", | 500 | Setting<std::string> touch_device{"min_x:100,min_y:50,max_x:1800,max_y:850", "touch_device"}; |
| 588 | "touch_device"}; | 501 | Setting<int> touch_from_button_map_index{0, "touch_from_button_map"}; |
| 589 | BasicSetting<int> touch_from_button_map_index{0, "touch_from_button_map"}; | ||
| 590 | std::vector<TouchFromButtonMap> touch_from_button_maps; | 502 | std::vector<TouchFromButtonMap> touch_from_button_maps; |
| 591 | 503 | ||
| 592 | BasicSetting<bool> enable_ring_controller{true, "enable_ring_controller"}; | 504 | Setting<bool> enable_ring_controller{true, "enable_ring_controller"}; |
| 593 | RingconRaw ringcon_analogs; | 505 | RingconRaw ringcon_analogs; |
| 594 | 506 | ||
| 595 | // Data Storage | 507 | // Data Storage |
| 596 | BasicSetting<bool> use_virtual_sd{true, "use_virtual_sd"}; | 508 | Setting<bool> use_virtual_sd{true, "use_virtual_sd"}; |
| 597 | BasicSetting<bool> gamecard_inserted{false, "gamecard_inserted"}; | 509 | Setting<bool> gamecard_inserted{false, "gamecard_inserted"}; |
| 598 | BasicSetting<bool> gamecard_current_game{false, "gamecard_current_game"}; | 510 | Setting<bool> gamecard_current_game{false, "gamecard_current_game"}; |
| 599 | BasicSetting<std::string> gamecard_path{std::string(), "gamecard_path"}; | 511 | Setting<std::string> gamecard_path{std::string(), "gamecard_path"}; |
| 600 | 512 | ||
| 601 | // Debugging | 513 | // Debugging |
| 602 | bool record_frame_times; | 514 | bool record_frame_times; |
| 603 | BasicSetting<bool> use_gdbstub{false, "use_gdbstub"}; | 515 | Setting<bool> use_gdbstub{false, "use_gdbstub"}; |
| 604 | BasicSetting<u16> gdbstub_port{6543, "gdbstub_port"}; | 516 | Setting<u16> gdbstub_port{6543, "gdbstub_port"}; |
| 605 | BasicSetting<std::string> program_args{std::string(), "program_args"}; | 517 | Setting<std::string> program_args{std::string(), "program_args"}; |
| 606 | BasicSetting<bool> dump_exefs{false, "dump_exefs"}; | 518 | Setting<bool> dump_exefs{false, "dump_exefs"}; |
| 607 | BasicSetting<bool> dump_nso{false, "dump_nso"}; | 519 | Setting<bool> dump_nso{false, "dump_nso"}; |
| 608 | BasicSetting<bool> dump_shaders{false, "dump_shaders"}; | 520 | Setting<bool> dump_shaders{false, "dump_shaders"}; |
| 609 | BasicSetting<bool> dump_macros{false, "dump_macros"}; | 521 | Setting<bool> dump_macros{false, "dump_macros"}; |
| 610 | BasicSetting<bool> enable_fs_access_log{false, "enable_fs_access_log"}; | 522 | Setting<bool> enable_fs_access_log{false, "enable_fs_access_log"}; |
| 611 | BasicSetting<bool> reporting_services{false, "reporting_services"}; | 523 | Setting<bool> reporting_services{false, "reporting_services"}; |
| 612 | BasicSetting<bool> quest_flag{false, "quest_flag"}; | 524 | Setting<bool> quest_flag{false, "quest_flag"}; |
| 613 | BasicSetting<bool> disable_macro_jit{false, "disable_macro_jit"}; | 525 | Setting<bool> disable_macro_jit{false, "disable_macro_jit"}; |
| 614 | BasicSetting<bool> extended_logging{false, "extended_logging"}; | 526 | Setting<bool> extended_logging{false, "extended_logging"}; |
| 615 | BasicSetting<bool> use_debug_asserts{false, "use_debug_asserts"}; | 527 | Setting<bool> use_debug_asserts{false, "use_debug_asserts"}; |
| 616 | BasicSetting<bool> use_auto_stub{false, "use_auto_stub"}; | 528 | Setting<bool> use_auto_stub{false, "use_auto_stub"}; |
| 617 | BasicSetting<bool> enable_all_controllers{false, "enable_all_controllers"}; | 529 | Setting<bool> enable_all_controllers{false, "enable_all_controllers"}; |
| 618 | 530 | ||
| 619 | // Miscellaneous | 531 | // Miscellaneous |
| 620 | BasicSetting<std::string> log_filter{"*:Info", "log_filter"}; | 532 | Setting<std::string> log_filter{"*:Info", "log_filter"}; |
| 621 | BasicSetting<bool> use_dev_keys{false, "use_dev_keys"}; | 533 | Setting<bool> use_dev_keys{false, "use_dev_keys"}; |
| 622 | 534 | ||
| 623 | // Network | 535 | // Network |
| 624 | BasicSetting<std::string> network_interface{std::string(), "network_interface"}; | 536 | Setting<std::string> network_interface{std::string(), "network_interface"}; |
| 625 | 537 | ||
| 626 | // WebService | 538 | // WebService |
| 627 | BasicSetting<bool> enable_telemetry{true, "enable_telemetry"}; | 539 | Setting<bool> enable_telemetry{true, "enable_telemetry"}; |
| 628 | BasicSetting<std::string> web_api_url{"https://api.yuzu-emu.org", "web_api_url"}; | 540 | Setting<std::string> web_api_url{"https://api.yuzu-emu.org", "web_api_url"}; |
| 629 | BasicSetting<std::string> yuzu_username{std::string(), "yuzu_username"}; | 541 | Setting<std::string> yuzu_username{std::string(), "yuzu_username"}; |
| 630 | BasicSetting<std::string> yuzu_token{std::string(), "yuzu_token"}; | 542 | Setting<std::string> yuzu_token{std::string(), "yuzu_token"}; |
| 631 | 543 | ||
| 632 | // Add-Ons | 544 | // Add-Ons |
| 633 | std::map<u64, std::vector<std::string>> disabled_addons; | 545 | std::map<u64, std::vector<std::string>> disabled_addons; |
diff --git a/src/common/thread.cpp b/src/common/thread.cpp index f932a7290..919e33af9 100644 --- a/src/common/thread.cpp +++ b/src/common/thread.cpp | |||
| @@ -47,6 +47,9 @@ void SetCurrentThreadPriority(ThreadPriority new_priority) { | |||
| 47 | case ThreadPriority::VeryHigh: | 47 | case ThreadPriority::VeryHigh: |
| 48 | windows_priority = THREAD_PRIORITY_HIGHEST; | 48 | windows_priority = THREAD_PRIORITY_HIGHEST; |
| 49 | break; | 49 | break; |
| 50 | case ThreadPriority::Critical: | ||
| 51 | windows_priority = THREAD_PRIORITY_TIME_CRITICAL; | ||
| 52 | break; | ||
| 50 | default: | 53 | default: |
| 51 | windows_priority = THREAD_PRIORITY_NORMAL; | 54 | windows_priority = THREAD_PRIORITY_NORMAL; |
| 52 | break; | 55 | break; |
| @@ -59,9 +62,10 @@ void SetCurrentThreadPriority(ThreadPriority new_priority) { | |||
| 59 | void SetCurrentThreadPriority(ThreadPriority new_priority) { | 62 | void SetCurrentThreadPriority(ThreadPriority new_priority) { |
| 60 | pthread_t this_thread = pthread_self(); | 63 | pthread_t this_thread = pthread_self(); |
| 61 | 64 | ||
| 62 | s32 max_prio = sched_get_priority_max(SCHED_OTHER); | 65 | const auto scheduling_type = SCHED_OTHER; |
| 63 | s32 min_prio = sched_get_priority_min(SCHED_OTHER); | 66 | s32 max_prio = sched_get_priority_max(scheduling_type); |
| 64 | u32 level = static_cast<u32>(new_priority) + 1; | 67 | s32 min_prio = sched_get_priority_min(scheduling_type); |
| 68 | u32 level = std::max(static_cast<u32>(new_priority) + 1, 4U); | ||
| 65 | 69 | ||
| 66 | struct sched_param params; | 70 | struct sched_param params; |
| 67 | if (max_prio > min_prio) { | 71 | if (max_prio > min_prio) { |
| @@ -70,7 +74,7 @@ void SetCurrentThreadPriority(ThreadPriority new_priority) { | |||
| 70 | params.sched_priority = min_prio - ((min_prio - max_prio) * level) / 4; | 74 | params.sched_priority = min_prio - ((min_prio - max_prio) * level) / 4; |
| 71 | } | 75 | } |
| 72 | 76 | ||
| 73 | pthread_setschedparam(this_thread, SCHED_OTHER, ¶ms); | 77 | pthread_setschedparam(this_thread, scheduling_type, ¶ms); |
| 74 | } | 78 | } |
| 75 | 79 | ||
| 76 | #endif | 80 | #endif |
diff --git a/src/common/thread.h b/src/common/thread.h index a63122516..1552f58e0 100644 --- a/src/common/thread.h +++ b/src/common/thread.h | |||
| @@ -92,6 +92,7 @@ enum class ThreadPriority : u32 { | |||
| 92 | Normal = 1, | 92 | Normal = 1, |
| 93 | High = 2, | 93 | High = 2, |
| 94 | VeryHigh = 3, | 94 | VeryHigh = 3, |
| 95 | Critical = 4, | ||
| 95 | }; | 96 | }; |
| 96 | 97 | ||
| 97 | void SetCurrentThreadPriority(ThreadPriority new_priority); | 98 | void SetCurrentThreadPriority(ThreadPriority new_priority); |
diff --git a/src/common/uint128.h b/src/common/uint128.h index f890ffec2..199d0f55e 100644 --- a/src/common/uint128.h +++ b/src/common/uint128.h | |||
| @@ -31,12 +31,17 @@ namespace Common { | |||
| 31 | return _udiv128(r[1], r[0], d, &remainder); | 31 | return _udiv128(r[1], r[0], d, &remainder); |
| 32 | #endif | 32 | #endif |
| 33 | #else | 33 | #else |
| 34 | #ifdef __SIZEOF_INT128__ | ||
| 35 | const auto product = static_cast<unsigned __int128>(a) * static_cast<unsigned __int128>(b); | ||
| 36 | return static_cast<u64>(product / d); | ||
| 37 | #else | ||
| 34 | const u64 diva = a / d; | 38 | const u64 diva = a / d; |
| 35 | const u64 moda = a % d; | 39 | const u64 moda = a % d; |
| 36 | const u64 divb = b / d; | 40 | const u64 divb = b / d; |
| 37 | const u64 modb = b % d; | 41 | const u64 modb = b % d; |
| 38 | return diva * b + moda * divb + moda * modb / d; | 42 | return diva * b + moda * divb + moda * modb / d; |
| 39 | #endif | 43 | #endif |
| 44 | #endif | ||
| 40 | } | 45 | } |
| 41 | 46 | ||
| 42 | // This function multiplies 2 u64 values and produces a u128 value; | 47 | // This function multiplies 2 u64 values and produces a u128 value; |
diff --git a/src/common/x64/native_clock.cpp b/src/common/x64/native_clock.cpp index 1b7194503..6aaa8cdf9 100644 --- a/src/common/x64/native_clock.cpp +++ b/src/common/x64/native_clock.cpp | |||
| @@ -75,8 +75,8 @@ NativeClock::NativeClock(u64 emulated_cpu_frequency_, u64 emulated_clock_frequen | |||
| 75 | } | 75 | } |
| 76 | 76 | ||
| 77 | u64 NativeClock::GetRTSC() { | 77 | u64 NativeClock::GetRTSC() { |
| 78 | TimePoint new_time_point{}; | ||
| 79 | TimePoint current_time_point{}; | 78 | TimePoint current_time_point{}; |
| 79 | TimePoint new_time_point{}; | ||
| 80 | 80 | ||
| 81 | current_time_point.pack = Common::AtomicLoad128(time_point.pack.data()); | 81 | current_time_point.pack = Common::AtomicLoad128(time_point.pack.data()); |
| 82 | do { | 82 | do { |
| @@ -89,8 +89,7 @@ u64 NativeClock::GetRTSC() { | |||
| 89 | new_time_point.inner.accumulated_ticks = current_time_point.inner.accumulated_ticks + diff; | 89 | new_time_point.inner.accumulated_ticks = current_time_point.inner.accumulated_ticks + diff; |
| 90 | } while (!Common::AtomicCompareAndSwap(time_point.pack.data(), new_time_point.pack, | 90 | } while (!Common::AtomicCompareAndSwap(time_point.pack.data(), new_time_point.pack, |
| 91 | current_time_point.pack, current_time_point.pack)); | 91 | current_time_point.pack, current_time_point.pack)); |
| 92 | /// The clock cannot be more precise than the guest timer, remove the lower bits | 92 | return new_time_point.inner.accumulated_ticks; |
| 93 | return new_time_point.inner.accumulated_ticks & inaccuracy_mask; | ||
| 94 | } | 93 | } |
| 95 | 94 | ||
| 96 | void NativeClock::Pause(bool is_paused) { | 95 | void NativeClock::Pause(bool is_paused) { |
diff --git a/src/common/x64/native_clock.h b/src/common/x64/native_clock.h index 30d2ba2e9..38ae7a462 100644 --- a/src/common/x64/native_clock.h +++ b/src/common/x64/native_clock.h | |||
| @@ -37,12 +37,8 @@ private: | |||
| 37 | } inner; | 37 | } inner; |
| 38 | }; | 38 | }; |
| 39 | 39 | ||
| 40 | /// value used to reduce the native clocks accuracy as some apss rely on | ||
| 41 | /// undefined behavior where the level of accuracy in the clock shouldn't | ||
| 42 | /// be higher. | ||
| 43 | static constexpr u64 inaccuracy_mask = ~(UINT64_C(0x400) - 1); | ||
| 44 | |||
| 45 | TimePoint time_point; | 40 | TimePoint time_point; |
| 41 | |||
| 46 | // factors | 42 | // factors |
| 47 | u64 clock_rtsc_factor{}; | 43 | u64 clock_rtsc_factor{}; |
| 48 | u64 cpu_rtsc_factor{}; | 44 | u64 cpu_rtsc_factor{}; |
diff --git a/src/core/arm/arm_interface.cpp b/src/core/arm/arm_interface.cpp index 8e095cdcd..cef79b245 100644 --- a/src/core/arm/arm_interface.cpp +++ b/src/core/arm/arm_interface.cpp | |||
| @@ -1,6 +1,10 @@ | |||
| 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 | #ifndef _MSC_VER | ||
| 5 | #include <cxxabi.h> | ||
| 6 | #endif | ||
| 7 | |||
| 4 | #include <map> | 8 | #include <map> |
| 5 | #include <optional> | 9 | #include <optional> |
| 6 | #include "common/bit_field.h" | 10 | #include "common/bit_field.h" |
| @@ -68,8 +72,19 @@ void ARM_Interface::SymbolicateBacktrace(Core::System& system, std::vector<Backt | |||
| 68 | if (symbol_set != symbols.end()) { | 72 | if (symbol_set != symbols.end()) { |
| 69 | const auto symbol = Symbols::GetSymbolName(symbol_set->second, entry.offset); | 73 | const auto symbol = Symbols::GetSymbolName(symbol_set->second, entry.offset); |
| 70 | if (symbol.has_value()) { | 74 | if (symbol.has_value()) { |
| 75 | #ifdef _MSC_VER | ||
| 71 | // TODO(DarkLordZach): Add demangling of symbol names. | 76 | // TODO(DarkLordZach): Add demangling of symbol names. |
| 72 | entry.name = *symbol; | 77 | entry.name = *symbol; |
| 78 | #else | ||
| 79 | int status{-1}; | ||
| 80 | char* demangled{abi::__cxa_demangle(symbol->c_str(), nullptr, nullptr, &status)}; | ||
| 81 | if (status == 0 && demangled != nullptr) { | ||
| 82 | entry.name = demangled; | ||
| 83 | std::free(demangled); | ||
| 84 | } else { | ||
| 85 | entry.name = *symbol; | ||
| 86 | } | ||
| 87 | #endif | ||
| 73 | } | 88 | } |
| 74 | } | 89 | } |
| 75 | } | 90 | } |
| @@ -119,16 +134,23 @@ void ARM_Interface::Run() { | |||
| 119 | } | 134 | } |
| 120 | system.ExitDynarmicProfile(); | 135 | system.ExitDynarmicProfile(); |
| 121 | 136 | ||
| 122 | // Notify the debugger and go to sleep if a breakpoint was hit. | 137 | // Notify the debugger and go to sleep if a breakpoint was hit, |
| 123 | if (Has(hr, breakpoint)) { | 138 | // or if the thread is unable to continue for any reason. |
| 139 | if (Has(hr, breakpoint) || Has(hr, no_execute)) { | ||
| 124 | RewindBreakpointInstruction(); | 140 | RewindBreakpointInstruction(); |
| 125 | system.GetDebugger().NotifyThreadStopped(current_thread); | 141 | if (system.DebuggerEnabled()) { |
| 126 | current_thread->RequestSuspend(SuspendType::Debug); | 142 | system.GetDebugger().NotifyThreadStopped(current_thread); |
| 143 | } | ||
| 144 | current_thread->RequestSuspend(Kernel::SuspendType::Debug); | ||
| 127 | break; | 145 | break; |
| 128 | } | 146 | } |
| 147 | |||
| 148 | // Notify the debugger and go to sleep if a watchpoint was hit. | ||
| 129 | if (Has(hr, watchpoint)) { | 149 | if (Has(hr, watchpoint)) { |
| 130 | RewindBreakpointInstruction(); | 150 | RewindBreakpointInstruction(); |
| 131 | system.GetDebugger().NotifyThreadWatchpoint(current_thread, *HaltedWatchpoint()); | 151 | if (system.DebuggerEnabled()) { |
| 152 | system.GetDebugger().NotifyThreadWatchpoint(current_thread, *HaltedWatchpoint()); | ||
| 153 | } | ||
| 132 | current_thread->RequestSuspend(SuspendType::Debug); | 154 | current_thread->RequestSuspend(SuspendType::Debug); |
| 133 | break; | 155 | break; |
| 134 | } | 156 | } |
diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h index 4e431e27a..8a066ed91 100644 --- a/src/core/arm/arm_interface.h +++ b/src/core/arm/arm_interface.h | |||
| @@ -204,6 +204,7 @@ public: | |||
| 204 | static constexpr Dynarmic::HaltReason svc_call = Dynarmic::HaltReason::UserDefined3; | 204 | static constexpr Dynarmic::HaltReason svc_call = Dynarmic::HaltReason::UserDefined3; |
| 205 | static constexpr Dynarmic::HaltReason breakpoint = Dynarmic::HaltReason::UserDefined4; | 205 | static constexpr Dynarmic::HaltReason breakpoint = Dynarmic::HaltReason::UserDefined4; |
| 206 | static constexpr Dynarmic::HaltReason watchpoint = Dynarmic::HaltReason::UserDefined5; | 206 | static constexpr Dynarmic::HaltReason watchpoint = Dynarmic::HaltReason::UserDefined5; |
| 207 | static constexpr Dynarmic::HaltReason no_execute = Dynarmic::HaltReason::UserDefined6; | ||
| 207 | 208 | ||
| 208 | protected: | 209 | protected: |
| 209 | /// System context that this ARM interface is running under. | 210 | /// System context that this ARM interface is running under. |
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp index 8c90c8be0..1be5fe1c1 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp | |||
| @@ -48,6 +48,12 @@ public: | |||
| 48 | CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Read); | 48 | CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Read); |
| 49 | return memory.Read64(vaddr); | 49 | return memory.Read64(vaddr); |
| 50 | } | 50 | } |
| 51 | std::optional<u32> MemoryReadCode(u32 vaddr) override { | ||
| 52 | if (!memory.IsValidVirtualAddressRange(vaddr, sizeof(u32))) { | ||
| 53 | return std::nullopt; | ||
| 54 | } | ||
| 55 | return MemoryRead32(vaddr); | ||
| 56 | } | ||
| 51 | 57 | ||
| 52 | void MemoryWrite8(u32 vaddr, u8 value) override { | 58 | void MemoryWrite8(u32 vaddr, u8 value) override { |
| 53 | if (CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write)) { | 59 | if (CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write)) { |
| @@ -89,21 +95,28 @@ public: | |||
| 89 | 95 | ||
| 90 | void InterpreterFallback(u32 pc, std::size_t num_instructions) override { | 96 | void InterpreterFallback(u32 pc, std::size_t num_instructions) override { |
| 91 | parent.LogBacktrace(); | 97 | parent.LogBacktrace(); |
| 92 | UNIMPLEMENTED_MSG("This should never happen, pc = {:08X}, code = {:08X}", pc, | 98 | LOG_ERROR(Core_ARM, |
| 93 | MemoryReadCode(pc)); | 99 | "Unimplemented instruction @ 0x{:X} for {} instructions (instr = {:08X})", pc, |
| 100 | num_instructions, MemoryRead32(pc)); | ||
| 94 | } | 101 | } |
| 95 | 102 | ||
| 96 | void ExceptionRaised(u32 pc, Dynarmic::A32::Exception exception) override { | 103 | void ExceptionRaised(u32 pc, Dynarmic::A32::Exception exception) override { |
| 97 | if (debugger_enabled) { | 104 | switch (exception) { |
| 98 | parent.SaveContext(parent.breakpoint_context); | 105 | case Dynarmic::A32::Exception::NoExecuteFault: |
| 99 | parent.jit.load()->HaltExecution(ARM_Interface::breakpoint); | 106 | LOG_CRITICAL(Core_ARM, "Cannot execute instruction at unmapped address {:#08x}", pc); |
| 107 | ReturnException(pc, ARM_Interface::no_execute); | ||
| 100 | return; | 108 | return; |
| 101 | } | 109 | default: |
| 110 | if (debugger_enabled) { | ||
| 111 | ReturnException(pc, ARM_Interface::breakpoint); | ||
| 112 | return; | ||
| 113 | } | ||
| 102 | 114 | ||
| 103 | parent.LogBacktrace(); | 115 | parent.LogBacktrace(); |
| 104 | LOG_CRITICAL(Core_ARM, | 116 | LOG_CRITICAL(Core_ARM, |
| 105 | "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X}, thumb = {})", | 117 | "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X}, thumb = {})", |
| 106 | exception, pc, MemoryReadCode(pc), parent.IsInThumbMode()); | 118 | exception, pc, MemoryRead32(pc), parent.IsInThumbMode()); |
| 119 | } | ||
| 107 | } | 120 | } |
| 108 | 121 | ||
| 109 | void CallSVC(u32 swi) override { | 122 | void CallSVC(u32 swi) override { |
| @@ -141,15 +154,20 @@ public: | |||
| 141 | 154 | ||
| 142 | const auto match{parent.MatchingWatchpoint(addr, size, type)}; | 155 | const auto match{parent.MatchingWatchpoint(addr, size, type)}; |
| 143 | if (match) { | 156 | if (match) { |
| 144 | parent.SaveContext(parent.breakpoint_context); | ||
| 145 | parent.jit.load()->HaltExecution(ARM_Interface::watchpoint); | ||
| 146 | parent.halted_watchpoint = match; | 157 | parent.halted_watchpoint = match; |
| 158 | ReturnException(parent.jit.load()->Regs()[15], ARM_Interface::watchpoint); | ||
| 147 | return false; | 159 | return false; |
| 148 | } | 160 | } |
| 149 | 161 | ||
| 150 | return true; | 162 | return true; |
| 151 | } | 163 | } |
| 152 | 164 | ||
| 165 | void ReturnException(u32 pc, Dynarmic::HaltReason hr) { | ||
| 166 | parent.SaveContext(parent.breakpoint_context); | ||
| 167 | parent.breakpoint_context.cpu_registers[15] = pc; | ||
| 168 | parent.jit.load()->HaltExecution(hr); | ||
| 169 | } | ||
| 170 | |||
| 153 | ARM_Dynarmic_32& parent; | 171 | ARM_Dynarmic_32& parent; |
| 154 | Core::Memory::Memory& memory; | 172 | Core::Memory::Memory& memory; |
| 155 | std::size_t num_interpreted_instructions{}; | 173 | std::size_t num_interpreted_instructions{}; |
| @@ -409,18 +427,38 @@ void ARM_Dynarmic_32::PageTableChanged(Common::PageTable& page_table, | |||
| 409 | } | 427 | } |
| 410 | 428 | ||
| 411 | std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_32::GetBacktrace(Core::System& system, | 429 | std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_32::GetBacktrace(Core::System& system, |
| 412 | u64 sp, u64 lr) { | 430 | u64 fp, u64 lr, u64 pc) { |
| 413 | // No way to get accurate stack traces in A32 yet | 431 | std::vector<BacktraceEntry> out; |
| 414 | return {}; | 432 | auto& memory = system.Memory(); |
| 433 | |||
| 434 | out.push_back({"", 0, pc, 0, ""}); | ||
| 435 | |||
| 436 | // fp (= r11) points to the last frame record. | ||
| 437 | // Frame records are two words long: | ||
| 438 | // fp+0 : pointer to previous frame record | ||
| 439 | // fp+4 : value of lr for frame | ||
| 440 | while (true) { | ||
| 441 | out.push_back({"", 0, lr, 0, ""}); | ||
| 442 | if (!fp || (fp % 4 != 0) || !memory.IsValidVirtualAddressRange(fp, 8)) { | ||
| 443 | break; | ||
| 444 | } | ||
| 445 | lr = memory.Read32(fp + 4); | ||
| 446 | fp = memory.Read32(fp); | ||
| 447 | } | ||
| 448 | |||
| 449 | SymbolicateBacktrace(system, out); | ||
| 450 | |||
| 451 | return out; | ||
| 415 | } | 452 | } |
| 416 | 453 | ||
| 417 | std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_32::GetBacktraceFromContext( | 454 | std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_32::GetBacktraceFromContext( |
| 418 | System& system, const ThreadContext32& ctx) { | 455 | System& system, const ThreadContext32& ctx) { |
| 419 | return GetBacktrace(system, ctx.cpu_registers[13], ctx.cpu_registers[14]); | 456 | const auto& reg = ctx.cpu_registers; |
| 457 | return GetBacktrace(system, reg[11], reg[14], reg[15]); | ||
| 420 | } | 458 | } |
| 421 | 459 | ||
| 422 | std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_32::GetBacktrace() const { | 460 | std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_32::GetBacktrace() const { |
| 423 | return GetBacktrace(system, GetReg(13), GetReg(14)); | 461 | return GetBacktrace(system, GetReg(11), GetReg(14), GetReg(15)); |
| 424 | } | 462 | } |
| 425 | 463 | ||
| 426 | } // namespace Core | 464 | } // namespace Core |
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.h b/src/core/arm/dynarmic/arm_dynarmic_32.h index fcbe24f0c..346e9abf8 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.h +++ b/src/core/arm/dynarmic/arm_dynarmic_32.h | |||
| @@ -78,7 +78,7 @@ protected: | |||
| 78 | private: | 78 | private: |
| 79 | std::shared_ptr<Dynarmic::A32::Jit> MakeJit(Common::PageTable* page_table) const; | 79 | std::shared_ptr<Dynarmic::A32::Jit> MakeJit(Common::PageTable* page_table) const; |
| 80 | 80 | ||
| 81 | static std::vector<BacktraceEntry> GetBacktrace(Core::System& system, u64 sp, u64 lr); | 81 | static std::vector<BacktraceEntry> GetBacktrace(Core::System& system, u64 fp, u64 lr, u64 pc); |
| 82 | 82 | ||
| 83 | using JitCacheKey = std::pair<Common::PageTable*, std::size_t>; | 83 | using JitCacheKey = std::pair<Common::PageTable*, std::size_t>; |
| 84 | using JitCacheType = | 84 | using JitCacheType = |
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp index 4370ca294..c437f24b8 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp | |||
| @@ -52,6 +52,12 @@ public: | |||
| 52 | CheckMemoryAccess(vaddr, 16, Kernel::DebugWatchpointType::Read); | 52 | CheckMemoryAccess(vaddr, 16, Kernel::DebugWatchpointType::Read); |
| 53 | return {memory.Read64(vaddr), memory.Read64(vaddr + 8)}; | 53 | return {memory.Read64(vaddr), memory.Read64(vaddr + 8)}; |
| 54 | } | 54 | } |
| 55 | std::optional<u32> MemoryReadCode(u64 vaddr) override { | ||
| 56 | if (!memory.IsValidVirtualAddressRange(vaddr, sizeof(u32))) { | ||
| 57 | return std::nullopt; | ||
| 58 | } | ||
| 59 | return MemoryRead32(vaddr); | ||
| 60 | } | ||
| 55 | 61 | ||
| 56 | void MemoryWrite8(u64 vaddr, u8 value) override { | 62 | void MemoryWrite8(u64 vaddr, u8 value) override { |
| 57 | if (CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write)) { | 63 | if (CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write)) { |
| @@ -105,7 +111,7 @@ public: | |||
| 105 | parent.LogBacktrace(); | 111 | parent.LogBacktrace(); |
| 106 | LOG_ERROR(Core_ARM, | 112 | LOG_ERROR(Core_ARM, |
| 107 | "Unimplemented instruction @ 0x{:X} for {} instructions (instr = {:08X})", pc, | 113 | "Unimplemented instruction @ 0x{:X} for {} instructions (instr = {:08X})", pc, |
| 108 | num_instructions, MemoryReadCode(pc)); | 114 | num_instructions, MemoryRead32(pc)); |
| 109 | } | 115 | } |
| 110 | 116 | ||
| 111 | void InstructionCacheOperationRaised(Dynarmic::A64::InstructionCacheOperation op, | 117 | void InstructionCacheOperationRaised(Dynarmic::A64::InstructionCacheOperation op, |
| @@ -138,16 +144,19 @@ public: | |||
| 138 | case Dynarmic::A64::Exception::SendEventLocal: | 144 | case Dynarmic::A64::Exception::SendEventLocal: |
| 139 | case Dynarmic::A64::Exception::Yield: | 145 | case Dynarmic::A64::Exception::Yield: |
| 140 | return; | 146 | return; |
| 147 | case Dynarmic::A64::Exception::NoExecuteFault: | ||
| 148 | LOG_CRITICAL(Core_ARM, "Cannot execute instruction at unmapped address {:#016x}", pc); | ||
| 149 | ReturnException(pc, ARM_Interface::no_execute); | ||
| 150 | return; | ||
| 141 | default: | 151 | default: |
| 142 | if (debugger_enabled) { | 152 | if (debugger_enabled) { |
| 143 | parent.SaveContext(parent.breakpoint_context); | 153 | ReturnException(pc, ARM_Interface::breakpoint); |
| 144 | parent.jit.load()->HaltExecution(ARM_Interface::breakpoint); | ||
| 145 | return; | 154 | return; |
| 146 | } | 155 | } |
| 147 | 156 | ||
| 148 | parent.LogBacktrace(); | 157 | parent.LogBacktrace(); |
| 149 | ASSERT_MSG(false, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X})", | 158 | LOG_CRITICAL(Core_ARM, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X})", |
| 150 | static_cast<std::size_t>(exception), pc, MemoryReadCode(pc)); | 159 | static_cast<std::size_t>(exception), pc, MemoryRead32(pc)); |
| 151 | } | 160 | } |
| 152 | } | 161 | } |
| 153 | 162 | ||
| @@ -188,15 +197,20 @@ public: | |||
| 188 | 197 | ||
| 189 | const auto match{parent.MatchingWatchpoint(addr, size, type)}; | 198 | const auto match{parent.MatchingWatchpoint(addr, size, type)}; |
| 190 | if (match) { | 199 | if (match) { |
| 191 | parent.SaveContext(parent.breakpoint_context); | ||
| 192 | parent.jit.load()->HaltExecution(ARM_Interface::watchpoint); | ||
| 193 | parent.halted_watchpoint = match; | 200 | parent.halted_watchpoint = match; |
| 201 | ReturnException(parent.jit.load()->GetPC(), ARM_Interface::watchpoint); | ||
| 194 | return false; | 202 | return false; |
| 195 | } | 203 | } |
| 196 | 204 | ||
| 197 | return true; | 205 | return true; |
| 198 | } | 206 | } |
| 199 | 207 | ||
| 208 | void ReturnException(u64 pc, Dynarmic::HaltReason hr) { | ||
| 209 | parent.SaveContext(parent.breakpoint_context); | ||
| 210 | parent.breakpoint_context.pc = pc; | ||
| 211 | parent.jit.load()->HaltExecution(hr); | ||
| 212 | } | ||
| 213 | |||
| 200 | ARM_Dynarmic_64& parent; | 214 | ARM_Dynarmic_64& parent; |
| 201 | Core::Memory::Memory& memory; | 215 | Core::Memory::Memory& memory; |
| 202 | u64 tpidrro_el0 = 0; | 216 | u64 tpidrro_el0 = 0; |
| @@ -480,22 +494,22 @@ void ARM_Dynarmic_64::PageTableChanged(Common::PageTable& page_table, | |||
| 480 | } | 494 | } |
| 481 | 495 | ||
| 482 | std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_64::GetBacktrace(Core::System& system, | 496 | std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_64::GetBacktrace(Core::System& system, |
| 483 | u64 fp, u64 lr) { | 497 | u64 fp, u64 lr, u64 pc) { |
| 484 | std::vector<BacktraceEntry> out; | 498 | std::vector<BacktraceEntry> out; |
| 485 | auto& memory = system.Memory(); | 499 | auto& memory = system.Memory(); |
| 486 | 500 | ||
| 487 | // fp (= r29) points to the last frame record. | 501 | out.push_back({"", 0, pc, 0, ""}); |
| 488 | // Note that this is the frame record for the *previous* frame, not the current one. | 502 | |
| 489 | // Note we need to subtract 4 from our last read to get the proper address | 503 | // fp (= x29) points to the previous frame record. |
| 490 | // Frame records are two words long: | 504 | // Frame records are two words long: |
| 491 | // fp+0 : pointer to previous frame record | 505 | // fp+0 : pointer to previous frame record |
| 492 | // fp+8 : value of lr for frame | 506 | // fp+8 : value of lr for frame |
| 493 | while (true) { | 507 | while (true) { |
| 494 | out.push_back({"", 0, lr, 0, ""}); | 508 | out.push_back({"", 0, lr, 0, ""}); |
| 495 | if (!fp) { | 509 | if (!fp || (fp % 4 != 0) || !memory.IsValidVirtualAddressRange(fp, 16)) { |
| 496 | break; | 510 | break; |
| 497 | } | 511 | } |
| 498 | lr = memory.Read64(fp + 8) - 4; | 512 | lr = memory.Read64(fp + 8); |
| 499 | fp = memory.Read64(fp); | 513 | fp = memory.Read64(fp); |
| 500 | } | 514 | } |
| 501 | 515 | ||
| @@ -506,11 +520,12 @@ std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_64::GetBacktrace(Core::S | |||
| 506 | 520 | ||
| 507 | std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_64::GetBacktraceFromContext( | 521 | std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_64::GetBacktraceFromContext( |
| 508 | System& system, const ThreadContext64& ctx) { | 522 | System& system, const ThreadContext64& ctx) { |
| 509 | return GetBacktrace(system, ctx.cpu_registers[29], ctx.cpu_registers[30]); | 523 | const auto& reg = ctx.cpu_registers; |
| 524 | return GetBacktrace(system, reg[29], reg[30], ctx.pc); | ||
| 510 | } | 525 | } |
| 511 | 526 | ||
| 512 | std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_64::GetBacktrace() const { | 527 | std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_64::GetBacktrace() const { |
| 513 | return GetBacktrace(system, GetReg(29), GetReg(30)); | 528 | return GetBacktrace(system, GetReg(29), GetReg(30), GetPC()); |
| 514 | } | 529 | } |
| 515 | 530 | ||
| 516 | } // namespace Core | 531 | } // namespace Core |
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.h b/src/core/arm/dynarmic/arm_dynarmic_64.h index 71dbaac5e..c77a83ad7 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.h +++ b/src/core/arm/dynarmic/arm_dynarmic_64.h | |||
| @@ -73,7 +73,7 @@ private: | |||
| 73 | std::shared_ptr<Dynarmic::A64::Jit> MakeJit(Common::PageTable* page_table, | 73 | std::shared_ptr<Dynarmic::A64::Jit> MakeJit(Common::PageTable* page_table, |
| 74 | std::size_t address_space_bits) const; | 74 | std::size_t address_space_bits) const; |
| 75 | 75 | ||
| 76 | static std::vector<BacktraceEntry> GetBacktrace(Core::System& system, u64 fp, u64 lr); | 76 | static std::vector<BacktraceEntry> GetBacktrace(Core::System& system, u64 fp, u64 lr, u64 pc); |
| 77 | 77 | ||
| 78 | using JitCacheKey = std::pair<Common::PageTable*, std::size_t>; | 78 | using JitCacheKey = std::pair<Common::PageTable*, std::size_t>; |
| 79 | using JitCacheType = | 79 | using JitCacheType = |
diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp index 29e7dba9b..5425637f5 100644 --- a/src/core/core_timing.cpp +++ b/src/core/core_timing.cpp | |||
| @@ -6,7 +6,9 @@ | |||
| 6 | #include <string> | 6 | #include <string> |
| 7 | #include <tuple> | 7 | #include <tuple> |
| 8 | 8 | ||
| 9 | #include "common/logging/log.h" | ||
| 9 | #include "common/microprofile.h" | 10 | #include "common/microprofile.h" |
| 11 | #include "common/thread.h" | ||
| 10 | #include "core/core_timing.h" | 12 | #include "core/core_timing.h" |
| 11 | #include "core/core_timing_util.h" | 13 | #include "core/core_timing_util.h" |
| 12 | #include "core/hardware_properties.h" | 14 | #include "core/hardware_properties.h" |
| @@ -20,10 +22,11 @@ std::shared_ptr<EventType> CreateEvent(std::string name, TimedCallback&& callbac | |||
| 20 | } | 22 | } |
| 21 | 23 | ||
| 22 | struct CoreTiming::Event { | 24 | struct CoreTiming::Event { |
| 23 | u64 time; | 25 | s64 time; |
| 24 | u64 fifo_order; | 26 | u64 fifo_order; |
| 25 | std::uintptr_t user_data; | 27 | std::uintptr_t user_data; |
| 26 | std::weak_ptr<EventType> type; | 28 | std::weak_ptr<EventType> type; |
| 29 | s64 reschedule_time; | ||
| 27 | 30 | ||
| 28 | // Sort by time, unless the times are the same, in which case sort by | 31 | // Sort by time, unless the times are the same, in which case sort by |
| 29 | // the order added to the queue | 32 | // the order added to the queue |
| @@ -41,11 +44,11 @@ CoreTiming::CoreTiming() | |||
| 41 | 44 | ||
| 42 | CoreTiming::~CoreTiming() = default; | 45 | CoreTiming::~CoreTiming() = default; |
| 43 | 46 | ||
| 44 | void CoreTiming::ThreadEntry(CoreTiming& instance) { | 47 | void CoreTiming::ThreadEntry(CoreTiming& instance, size_t id) { |
| 45 | constexpr char name[] = "yuzu:HostTiming"; | 48 | const std::string name = "yuzu:HostTiming_" + std::to_string(id); |
| 46 | MicroProfileOnThreadCreate(name); | 49 | MicroProfileOnThreadCreate(name.c_str()); |
| 47 | Common::SetCurrentThreadName(name); | 50 | Common::SetCurrentThreadName(name.c_str()); |
| 48 | Common::SetCurrentThreadPriority(Common::ThreadPriority::VeryHigh); | 51 | Common::SetCurrentThreadPriority(Common::ThreadPriority::Critical); |
| 49 | instance.on_thread_init(); | 52 | instance.on_thread_init(); |
| 50 | instance.ThreadLoop(); | 53 | instance.ThreadLoop(); |
| 51 | MicroProfileOnThreadExit(); | 54 | MicroProfileOnThreadExit(); |
| @@ -56,71 +59,131 @@ void CoreTiming::Initialize(std::function<void()>&& on_thread_init_) { | |||
| 56 | event_fifo_id = 0; | 59 | event_fifo_id = 0; |
| 57 | shutting_down = false; | 60 | shutting_down = false; |
| 58 | ticks = 0; | 61 | ticks = 0; |
| 59 | const auto empty_timed_callback = [](std::uintptr_t, std::chrono::nanoseconds) {}; | 62 | const auto empty_timed_callback = [](std::uintptr_t, u64, std::chrono::nanoseconds) |
| 63 | -> std::optional<std::chrono::nanoseconds> { return std::nullopt; }; | ||
| 60 | ev_lost = CreateEvent("_lost_event", empty_timed_callback); | 64 | ev_lost = CreateEvent("_lost_event", empty_timed_callback); |
| 61 | if (is_multicore) { | 65 | if (is_multicore) { |
| 62 | timer_thread = std::make_unique<std::thread>(ThreadEntry, std::ref(*this)); | 66 | worker_threads.emplace_back(ThreadEntry, std::ref(*this), 0); |
| 63 | } | 67 | } |
| 64 | } | 68 | } |
| 65 | 69 | ||
| 66 | void CoreTiming::Shutdown() { | 70 | void CoreTiming::Shutdown() { |
| 67 | paused = true; | 71 | is_paused = true; |
| 68 | shutting_down = true; | 72 | shutting_down = true; |
| 69 | pause_event.Set(); | 73 | std::atomic_thread_fence(std::memory_order_release); |
| 70 | event.Set(); | 74 | |
| 71 | if (timer_thread) { | 75 | event_cv.notify_all(); |
| 72 | timer_thread->join(); | 76 | wait_pause_cv.notify_all(); |
| 77 | for (auto& thread : worker_threads) { | ||
| 78 | thread.join(); | ||
| 73 | } | 79 | } |
| 80 | worker_threads.clear(); | ||
| 81 | pause_callbacks.clear(); | ||
| 74 | ClearPendingEvents(); | 82 | ClearPendingEvents(); |
| 75 | timer_thread.reset(); | ||
| 76 | has_started = false; | 83 | has_started = false; |
| 77 | } | 84 | } |
| 78 | 85 | ||
| 79 | void CoreTiming::Pause(bool is_paused) { | 86 | void CoreTiming::Pause(bool is_paused_) { |
| 80 | paused = is_paused; | 87 | std::unique_lock main_lock(event_mutex); |
| 81 | pause_event.Set(); | 88 | if (is_paused_ == paused_state.load(std::memory_order_relaxed)) { |
| 89 | return; | ||
| 90 | } | ||
| 91 | if (is_multicore) { | ||
| 92 | is_paused = is_paused_; | ||
| 93 | event_cv.notify_all(); | ||
| 94 | if (!is_paused_) { | ||
| 95 | wait_pause_cv.notify_all(); | ||
| 96 | } | ||
| 97 | } | ||
| 98 | paused_state.store(is_paused_, std::memory_order_relaxed); | ||
| 99 | |||
| 100 | if (!is_paused_) { | ||
| 101 | pause_end_time = GetGlobalTimeNs().count(); | ||
| 102 | } | ||
| 103 | |||
| 104 | for (auto& cb : pause_callbacks) { | ||
| 105 | cb(is_paused_); | ||
| 106 | } | ||
| 82 | } | 107 | } |
| 83 | 108 | ||
| 84 | void CoreTiming::SyncPause(bool is_paused) { | 109 | void CoreTiming::SyncPause(bool is_paused_) { |
| 85 | if (is_paused == paused && paused_set == paused) { | 110 | std::unique_lock main_lock(event_mutex); |
| 111 | if (is_paused_ == paused_state.load(std::memory_order_relaxed)) { | ||
| 86 | return; | 112 | return; |
| 87 | } | 113 | } |
| 88 | Pause(is_paused); | 114 | |
| 89 | if (timer_thread) { | 115 | if (is_multicore) { |
| 90 | if (!is_paused) { | 116 | is_paused = is_paused_; |
| 91 | pause_event.Set(); | 117 | event_cv.notify_all(); |
| 118 | if (!is_paused_) { | ||
| 119 | wait_pause_cv.notify_all(); | ||
| 120 | } | ||
| 121 | } | ||
| 122 | paused_state.store(is_paused_, std::memory_order_relaxed); | ||
| 123 | if (is_multicore) { | ||
| 124 | if (is_paused_) { | ||
| 125 | wait_signal_cv.wait(main_lock, [this] { return pause_count == worker_threads.size(); }); | ||
| 126 | } else { | ||
| 127 | wait_signal_cv.wait(main_lock, [this] { return pause_count == 0; }); | ||
| 92 | } | 128 | } |
| 93 | event.Set(); | 129 | } |
| 94 | while (paused_set != is_paused) | 130 | |
| 95 | ; | 131 | if (!is_paused_) { |
| 132 | pause_end_time = GetGlobalTimeNs().count(); | ||
| 133 | } | ||
| 134 | |||
| 135 | for (auto& cb : pause_callbacks) { | ||
| 136 | cb(is_paused_); | ||
| 96 | } | 137 | } |
| 97 | } | 138 | } |
| 98 | 139 | ||
| 99 | bool CoreTiming::IsRunning() const { | 140 | bool CoreTiming::IsRunning() const { |
| 100 | return !paused_set; | 141 | return !paused_state.load(std::memory_order_acquire); |
| 101 | } | 142 | } |
| 102 | 143 | ||
| 103 | bool CoreTiming::HasPendingEvents() const { | 144 | bool CoreTiming::HasPendingEvents() const { |
| 104 | return !(wait_set && event_queue.empty()); | 145 | std::unique_lock main_lock(event_mutex); |
| 146 | return !event_queue.empty() || pending_events.load(std::memory_order_relaxed) != 0; | ||
| 105 | } | 147 | } |
| 106 | 148 | ||
| 107 | void CoreTiming::ScheduleEvent(std::chrono::nanoseconds ns_into_future, | 149 | void CoreTiming::ScheduleEvent(std::chrono::nanoseconds ns_into_future, |
| 108 | const std::shared_ptr<EventType>& event_type, | 150 | const std::shared_ptr<EventType>& event_type, |
| 109 | std::uintptr_t user_data) { | 151 | std::uintptr_t user_data, bool absolute_time) { |
| 110 | { | 152 | |
| 111 | std::scoped_lock scope{basic_lock}; | 153 | std::unique_lock main_lock(event_mutex); |
| 112 | const u64 timeout = static_cast<u64>((GetGlobalTimeNs() + ns_into_future).count()); | 154 | const auto next_time{absolute_time ? ns_into_future : GetGlobalTimeNs() + ns_into_future}; |
| 113 | 155 | ||
| 114 | event_queue.emplace_back(Event{timeout, event_fifo_id++, user_data, event_type}); | 156 | event_queue.emplace_back(Event{next_time.count(), event_fifo_id++, user_data, event_type, 0}); |
| 157 | pending_events.fetch_add(1, std::memory_order_relaxed); | ||
| 115 | 158 | ||
| 116 | std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>()); | 159 | std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>()); |
| 160 | |||
| 161 | if (is_multicore) { | ||
| 162 | event_cv.notify_one(); | ||
| 163 | } | ||
| 164 | } | ||
| 165 | |||
| 166 | void CoreTiming::ScheduleLoopingEvent(std::chrono::nanoseconds start_time, | ||
| 167 | std::chrono::nanoseconds resched_time, | ||
| 168 | const std::shared_ptr<EventType>& event_type, | ||
| 169 | std::uintptr_t user_data, bool absolute_time) { | ||
| 170 | std::unique_lock main_lock(event_mutex); | ||
| 171 | const auto next_time{absolute_time ? start_time : GetGlobalTimeNs() + start_time}; | ||
| 172 | |||
| 173 | event_queue.emplace_back( | ||
| 174 | Event{next_time.count(), event_fifo_id++, user_data, event_type, resched_time.count()}); | ||
| 175 | pending_events.fetch_add(1, std::memory_order_relaxed); | ||
| 176 | |||
| 177 | std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>()); | ||
| 178 | |||
| 179 | if (is_multicore) { | ||
| 180 | event_cv.notify_one(); | ||
| 117 | } | 181 | } |
| 118 | event.Set(); | ||
| 119 | } | 182 | } |
| 120 | 183 | ||
| 121 | void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type, | 184 | void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type, |
| 122 | std::uintptr_t user_data) { | 185 | std::uintptr_t user_data) { |
| 123 | std::scoped_lock scope{basic_lock}; | 186 | std::unique_lock main_lock(event_mutex); |
| 124 | const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) { | 187 | const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) { |
| 125 | return e.type.lock().get() == event_type.get() && e.user_data == user_data; | 188 | return e.type.lock().get() == event_type.get() && e.user_data == user_data; |
| 126 | }); | 189 | }); |
| @@ -129,6 +192,7 @@ void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type, | |||
| 129 | if (itr != event_queue.end()) { | 192 | if (itr != event_queue.end()) { |
| 130 | event_queue.erase(itr, event_queue.end()); | 193 | event_queue.erase(itr, event_queue.end()); |
| 131 | std::make_heap(event_queue.begin(), event_queue.end(), std::greater<>()); | 194 | std::make_heap(event_queue.begin(), event_queue.end(), std::greater<>()); |
| 195 | pending_events.fetch_sub(1, std::memory_order_relaxed); | ||
| 132 | } | 196 | } |
| 133 | } | 197 | } |
| 134 | 198 | ||
| @@ -168,11 +232,12 @@ u64 CoreTiming::GetClockTicks() const { | |||
| 168 | } | 232 | } |
| 169 | 233 | ||
| 170 | void CoreTiming::ClearPendingEvents() { | 234 | void CoreTiming::ClearPendingEvents() { |
| 235 | std::unique_lock main_lock(event_mutex); | ||
| 171 | event_queue.clear(); | 236 | event_queue.clear(); |
| 172 | } | 237 | } |
| 173 | 238 | ||
| 174 | void CoreTiming::RemoveEvent(const std::shared_ptr<EventType>& event_type) { | 239 | void CoreTiming::RemoveEvent(const std::shared_ptr<EventType>& event_type) { |
| 175 | std::scoped_lock lock{basic_lock}; | 240 | std::unique_lock main_lock(event_mutex); |
| 176 | 241 | ||
| 177 | const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) { | 242 | const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) { |
| 178 | return e.type.lock().get() == event_type.get(); | 243 | return e.type.lock().get() == event_type.get(); |
| @@ -185,22 +250,48 @@ void CoreTiming::RemoveEvent(const std::shared_ptr<EventType>& event_type) { | |||
| 185 | } | 250 | } |
| 186 | } | 251 | } |
| 187 | 252 | ||
| 253 | void CoreTiming::RegisterPauseCallback(PauseCallback&& callback) { | ||
| 254 | std::unique_lock main_lock(event_mutex); | ||
| 255 | pause_callbacks.emplace_back(std::move(callback)); | ||
| 256 | } | ||
| 257 | |||
| 188 | std::optional<s64> CoreTiming::Advance() { | 258 | std::optional<s64> CoreTiming::Advance() { |
| 189 | std::scoped_lock lock{advance_lock, basic_lock}; | ||
| 190 | global_timer = GetGlobalTimeNs().count(); | 259 | global_timer = GetGlobalTimeNs().count(); |
| 191 | 260 | ||
| 261 | std::unique_lock main_lock(event_mutex); | ||
| 192 | while (!event_queue.empty() && event_queue.front().time <= global_timer) { | 262 | while (!event_queue.empty() && event_queue.front().time <= global_timer) { |
| 193 | Event evt = std::move(event_queue.front()); | 263 | Event evt = std::move(event_queue.front()); |
| 194 | std::pop_heap(event_queue.begin(), event_queue.end(), std::greater<>()); | 264 | std::pop_heap(event_queue.begin(), event_queue.end(), std::greater<>()); |
| 195 | event_queue.pop_back(); | 265 | event_queue.pop_back(); |
| 196 | basic_lock.unlock(); | ||
| 197 | 266 | ||
| 198 | if (const auto event_type{evt.type.lock()}) { | 267 | if (const auto event_type{evt.type.lock()}) { |
| 199 | event_type->callback( | 268 | event_mutex.unlock(); |
| 200 | evt.user_data, std::chrono::nanoseconds{static_cast<s64>(global_timer - evt.time)}); | 269 | |
| 270 | const auto new_schedule_time{event_type->callback( | ||
| 271 | evt.user_data, evt.time, | ||
| 272 | std::chrono::nanoseconds{GetGlobalTimeNs().count() - evt.time})}; | ||
| 273 | |||
| 274 | event_mutex.lock(); | ||
| 275 | pending_events.fetch_sub(1, std::memory_order_relaxed); | ||
| 276 | |||
| 277 | if (evt.reschedule_time != 0) { | ||
| 278 | // If this event was scheduled into a pause, its time now is going to be way behind. | ||
| 279 | // Re-set this event to continue from the end of the pause. | ||
| 280 | auto next_time{evt.time + evt.reschedule_time}; | ||
| 281 | if (evt.time < pause_end_time) { | ||
| 282 | next_time = pause_end_time + evt.reschedule_time; | ||
| 283 | } | ||
| 284 | |||
| 285 | const auto next_schedule_time{new_schedule_time.has_value() | ||
| 286 | ? new_schedule_time.value().count() | ||
| 287 | : evt.reschedule_time}; | ||
| 288 | event_queue.emplace_back( | ||
| 289 | Event{next_time, event_fifo_id++, evt.user_data, evt.type, next_schedule_time}); | ||
| 290 | pending_events.fetch_add(1, std::memory_order_relaxed); | ||
| 291 | std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>()); | ||
| 292 | } | ||
| 201 | } | 293 | } |
| 202 | 294 | ||
| 203 | basic_lock.lock(); | ||
| 204 | global_timer = GetGlobalTimeNs().count(); | 295 | global_timer = GetGlobalTimeNs().count(); |
| 205 | } | 296 | } |
| 206 | 297 | ||
| @@ -213,26 +304,34 @@ std::optional<s64> CoreTiming::Advance() { | |||
| 213 | } | 304 | } |
| 214 | 305 | ||
| 215 | void CoreTiming::ThreadLoop() { | 306 | void CoreTiming::ThreadLoop() { |
| 307 | const auto predicate = [this] { return !event_queue.empty() || is_paused; }; | ||
| 216 | has_started = true; | 308 | has_started = true; |
| 217 | while (!shutting_down) { | 309 | while (!shutting_down) { |
| 218 | while (!paused) { | 310 | while (!is_paused && !shutting_down) { |
| 219 | paused_set = false; | ||
| 220 | const auto next_time = Advance(); | 311 | const auto next_time = Advance(); |
| 221 | if (next_time) { | 312 | if (next_time) { |
| 222 | if (*next_time > 0) { | 313 | if (*next_time > 0) { |
| 223 | std::chrono::nanoseconds next_time_ns = std::chrono::nanoseconds(*next_time); | 314 | std::chrono::nanoseconds next_time_ns = std::chrono::nanoseconds(*next_time); |
| 224 | event.WaitFor(next_time_ns); | 315 | std::unique_lock main_lock(event_mutex); |
| 316 | event_cv.wait_for(main_lock, next_time_ns, predicate); | ||
| 225 | } | 317 | } |
| 226 | } else { | 318 | } else { |
| 227 | wait_set = true; | 319 | std::unique_lock main_lock(event_mutex); |
| 228 | event.Wait(); | 320 | event_cv.wait(main_lock, predicate); |
| 229 | } | 321 | } |
| 230 | wait_set = false; | ||
| 231 | } | 322 | } |
| 232 | paused_set = true; | 323 | std::unique_lock main_lock(event_mutex); |
| 233 | clock->Pause(true); | 324 | pause_count++; |
| 234 | pause_event.Wait(); | 325 | if (pause_count == worker_threads.size()) { |
| 235 | clock->Pause(false); | 326 | clock->Pause(true); |
| 327 | wait_signal_cv.notify_all(); | ||
| 328 | } | ||
| 329 | wait_pause_cv.wait(main_lock, [this] { return !is_paused || shutting_down; }); | ||
| 330 | pause_count--; | ||
| 331 | if (pause_count == 0) { | ||
| 332 | clock->Pause(false); | ||
| 333 | wait_signal_cv.notify_all(); | ||
| 334 | } | ||
| 236 | } | 335 | } |
| 237 | } | 336 | } |
| 238 | 337 | ||
diff --git a/src/core/core_timing.h b/src/core/core_timing.h index d27773009..09b6ed81a 100644 --- a/src/core/core_timing.h +++ b/src/core/core_timing.h | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | 5 | ||
| 6 | #include <atomic> | 6 | #include <atomic> |
| 7 | #include <chrono> | 7 | #include <chrono> |
| 8 | #include <condition_variable> | ||
| 8 | #include <functional> | 9 | #include <functional> |
| 9 | #include <memory> | 10 | #include <memory> |
| 10 | #include <mutex> | 11 | #include <mutex> |
| @@ -14,14 +15,14 @@ | |||
| 14 | #include <vector> | 15 | #include <vector> |
| 15 | 16 | ||
| 16 | #include "common/common_types.h" | 17 | #include "common/common_types.h" |
| 17 | #include "common/thread.h" | ||
| 18 | #include "common/wall_clock.h" | 18 | #include "common/wall_clock.h" |
| 19 | 19 | ||
| 20 | namespace Core::Timing { | 20 | namespace Core::Timing { |
| 21 | 21 | ||
| 22 | /// A callback that may be scheduled for a particular core timing event. | 22 | /// A callback that may be scheduled for a particular core timing event. |
| 23 | using TimedCallback = | 23 | using TimedCallback = std::function<std::optional<std::chrono::nanoseconds>( |
| 24 | std::function<void(std::uintptr_t user_data, std::chrono::nanoseconds ns_late)>; | 24 | std::uintptr_t user_data, s64 time, std::chrono::nanoseconds ns_late)>; |
| 25 | using PauseCallback = std::function<void(bool paused)>; | ||
| 25 | 26 | ||
| 26 | /// Contains the characteristics of a particular event. | 27 | /// Contains the characteristics of a particular event. |
| 27 | struct EventType { | 28 | struct EventType { |
| @@ -93,7 +94,15 @@ public: | |||
| 93 | 94 | ||
| 94 | /// Schedules an event in core timing | 95 | /// Schedules an event in core timing |
| 95 | void ScheduleEvent(std::chrono::nanoseconds ns_into_future, | 96 | void ScheduleEvent(std::chrono::nanoseconds ns_into_future, |
| 96 | const std::shared_ptr<EventType>& event_type, std::uintptr_t user_data = 0); | 97 | const std::shared_ptr<EventType>& event_type, std::uintptr_t user_data = 0, |
| 98 | bool absolute_time = false); | ||
| 99 | |||
| 100 | /// Schedules an event which will automatically re-schedule itself with the given time, until | ||
| 101 | /// unscheduled | ||
| 102 | void ScheduleLoopingEvent(std::chrono::nanoseconds start_time, | ||
| 103 | std::chrono::nanoseconds resched_time, | ||
| 104 | const std::shared_ptr<EventType>& event_type, | ||
| 105 | std::uintptr_t user_data = 0, bool absolute_time = false); | ||
| 97 | 106 | ||
| 98 | void UnscheduleEvent(const std::shared_ptr<EventType>& event_type, std::uintptr_t user_data); | 107 | void UnscheduleEvent(const std::shared_ptr<EventType>& event_type, std::uintptr_t user_data); |
| 99 | 108 | ||
| @@ -125,18 +134,21 @@ public: | |||
| 125 | /// Checks for events manually and returns time in nanoseconds for next event, threadsafe. | 134 | /// Checks for events manually and returns time in nanoseconds for next event, threadsafe. |
| 126 | std::optional<s64> Advance(); | 135 | std::optional<s64> Advance(); |
| 127 | 136 | ||
| 137 | /// Register a callback function to be called when coretiming pauses. | ||
| 138 | void RegisterPauseCallback(PauseCallback&& callback); | ||
| 139 | |||
| 128 | private: | 140 | private: |
| 129 | struct Event; | 141 | struct Event; |
| 130 | 142 | ||
| 131 | /// Clear all pending events. This should ONLY be done on exit. | 143 | /// Clear all pending events. This should ONLY be done on exit. |
| 132 | void ClearPendingEvents(); | 144 | void ClearPendingEvents(); |
| 133 | 145 | ||
| 134 | static void ThreadEntry(CoreTiming& instance); | 146 | static void ThreadEntry(CoreTiming& instance, size_t id); |
| 135 | void ThreadLoop(); | 147 | void ThreadLoop(); |
| 136 | 148 | ||
| 137 | std::unique_ptr<Common::WallClock> clock; | 149 | std::unique_ptr<Common::WallClock> clock; |
| 138 | 150 | ||
| 139 | u64 global_timer = 0; | 151 | s64 global_timer = 0; |
| 140 | 152 | ||
| 141 | // The queue is a min-heap using std::make_heap/push_heap/pop_heap. | 153 | // The queue is a min-heap using std::make_heap/push_heap/pop_heap. |
| 142 | // We don't use std::priority_queue because we need to be able to serialize, unserialize and | 154 | // We don't use std::priority_queue because we need to be able to serialize, unserialize and |
| @@ -144,25 +156,31 @@ private: | |||
| 144 | // accomodated by the standard adaptor class. | 156 | // accomodated by the standard adaptor class. |
| 145 | std::vector<Event> event_queue; | 157 | std::vector<Event> event_queue; |
| 146 | u64 event_fifo_id = 0; | 158 | u64 event_fifo_id = 0; |
| 159 | std::atomic<size_t> pending_events{}; | ||
| 147 | 160 | ||
| 148 | std::shared_ptr<EventType> ev_lost; | 161 | std::shared_ptr<EventType> ev_lost; |
| 149 | Common::Event event{}; | ||
| 150 | Common::Event pause_event{}; | ||
| 151 | std::mutex basic_lock; | ||
| 152 | std::mutex advance_lock; | ||
| 153 | std::unique_ptr<std::thread> timer_thread; | ||
| 154 | std::atomic<bool> paused{}; | ||
| 155 | std::atomic<bool> paused_set{}; | ||
| 156 | std::atomic<bool> wait_set{}; | ||
| 157 | std::atomic<bool> shutting_down{}; | ||
| 158 | std::atomic<bool> has_started{}; | 162 | std::atomic<bool> has_started{}; |
| 159 | std::function<void()> on_thread_init{}; | 163 | std::function<void()> on_thread_init{}; |
| 160 | 164 | ||
| 165 | std::vector<std::thread> worker_threads; | ||
| 166 | |||
| 167 | std::condition_variable event_cv; | ||
| 168 | std::condition_variable wait_pause_cv; | ||
| 169 | std::condition_variable wait_signal_cv; | ||
| 170 | mutable std::mutex event_mutex; | ||
| 171 | |||
| 172 | std::atomic<bool> paused_state{}; | ||
| 173 | bool is_paused{}; | ||
| 174 | bool shutting_down{}; | ||
| 161 | bool is_multicore{}; | 175 | bool is_multicore{}; |
| 176 | size_t pause_count{}; | ||
| 177 | s64 pause_end_time{}; | ||
| 162 | 178 | ||
| 163 | /// Cycle timing | 179 | /// Cycle timing |
| 164 | u64 ticks{}; | 180 | u64 ticks{}; |
| 165 | s64 downcount{}; | 181 | s64 downcount{}; |
| 182 | |||
| 183 | std::vector<PauseCallback> pause_callbacks{}; | ||
| 166 | }; | 184 | }; |
| 167 | 185 | ||
| 168 | /// Creates a core timing event with the given name and callback. | 186 | /// Creates a core timing event with the given name and callback. |
diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp index fd6928105..37d3d83b9 100644 --- a/src/core/cpu_manager.cpp +++ b/src/core/cpu_manager.cpp | |||
| @@ -41,51 +41,32 @@ void CpuManager::Shutdown() { | |||
| 41 | } | 41 | } |
| 42 | } | 42 | } |
| 43 | 43 | ||
| 44 | std::function<void(void*)> CpuManager::GetGuestThreadStartFunc() { | 44 | void CpuManager::GuestThreadFunction() { |
| 45 | return GuestThreadFunction; | 45 | if (is_multicore) { |
| 46 | } | 46 | MultiCoreRunGuestThread(); |
| 47 | |||
| 48 | std::function<void(void*)> CpuManager::GetIdleThreadStartFunc() { | ||
| 49 | return IdleThreadFunction; | ||
| 50 | } | ||
| 51 | |||
| 52 | std::function<void(void*)> CpuManager::GetShutdownThreadStartFunc() { | ||
| 53 | return ShutdownThreadFunction; | ||
| 54 | } | ||
| 55 | |||
| 56 | void CpuManager::GuestThreadFunction(void* cpu_manager_) { | ||
| 57 | CpuManager* cpu_manager = static_cast<CpuManager*>(cpu_manager_); | ||
| 58 | if (cpu_manager->is_multicore) { | ||
| 59 | cpu_manager->MultiCoreRunGuestThread(); | ||
| 60 | } else { | 47 | } else { |
| 61 | cpu_manager->SingleCoreRunGuestThread(); | 48 | SingleCoreRunGuestThread(); |
| 62 | } | 49 | } |
| 63 | } | 50 | } |
| 64 | 51 | ||
| 65 | void CpuManager::GuestRewindFunction(void* cpu_manager_) { | 52 | void CpuManager::GuestRewindFunction() { |
| 66 | CpuManager* cpu_manager = static_cast<CpuManager*>(cpu_manager_); | 53 | if (is_multicore) { |
| 67 | if (cpu_manager->is_multicore) { | 54 | MultiCoreRunGuestLoop(); |
| 68 | cpu_manager->MultiCoreRunGuestLoop(); | ||
| 69 | } else { | 55 | } else { |
| 70 | cpu_manager->SingleCoreRunGuestLoop(); | 56 | SingleCoreRunGuestLoop(); |
| 71 | } | 57 | } |
| 72 | } | 58 | } |
| 73 | 59 | ||
| 74 | void CpuManager::IdleThreadFunction(void* cpu_manager_) { | 60 | void CpuManager::IdleThreadFunction() { |
| 75 | CpuManager* cpu_manager = static_cast<CpuManager*>(cpu_manager_); | 61 | if (is_multicore) { |
| 76 | if (cpu_manager->is_multicore) { | 62 | MultiCoreRunIdleThread(); |
| 77 | cpu_manager->MultiCoreRunIdleThread(); | ||
| 78 | } else { | 63 | } else { |
| 79 | cpu_manager->SingleCoreRunIdleThread(); | 64 | SingleCoreRunIdleThread(); |
| 80 | } | 65 | } |
| 81 | } | 66 | } |
| 82 | 67 | ||
| 83 | void CpuManager::ShutdownThreadFunction(void* cpu_manager) { | 68 | void CpuManager::ShutdownThreadFunction() { |
| 84 | static_cast<CpuManager*>(cpu_manager)->ShutdownThread(); | 69 | ShutdownThread(); |
| 85 | } | ||
| 86 | |||
| 87 | void* CpuManager::GetStartFuncParameter() { | ||
| 88 | return this; | ||
| 89 | } | 70 | } |
| 90 | 71 | ||
| 91 | /////////////////////////////////////////////////////////////////////////////// | 72 | /////////////////////////////////////////////////////////////////////////////// |
| @@ -97,7 +78,7 @@ void CpuManager::MultiCoreRunGuestThread() { | |||
| 97 | kernel.CurrentScheduler()->OnThreadStart(); | 78 | kernel.CurrentScheduler()->OnThreadStart(); |
| 98 | auto* thread = kernel.CurrentScheduler()->GetSchedulerCurrentThread(); | 79 | auto* thread = kernel.CurrentScheduler()->GetSchedulerCurrentThread(); |
| 99 | auto& host_context = thread->GetHostContext(); | 80 | auto& host_context = thread->GetHostContext(); |
| 100 | host_context->SetRewindPoint(GuestRewindFunction, this); | 81 | host_context->SetRewindPoint([this] { GuestRewindFunction(); }); |
| 101 | MultiCoreRunGuestLoop(); | 82 | MultiCoreRunGuestLoop(); |
| 102 | } | 83 | } |
| 103 | 84 | ||
| @@ -134,7 +115,7 @@ void CpuManager::SingleCoreRunGuestThread() { | |||
| 134 | kernel.CurrentScheduler()->OnThreadStart(); | 115 | kernel.CurrentScheduler()->OnThreadStart(); |
| 135 | auto* thread = kernel.CurrentScheduler()->GetSchedulerCurrentThread(); | 116 | auto* thread = kernel.CurrentScheduler()->GetSchedulerCurrentThread(); |
| 136 | auto& host_context = thread->GetHostContext(); | 117 | auto& host_context = thread->GetHostContext(); |
| 137 | host_context->SetRewindPoint(GuestRewindFunction, this); | 118 | host_context->SetRewindPoint([this] { GuestRewindFunction(); }); |
| 138 | SingleCoreRunGuestLoop(); | 119 | SingleCoreRunGuestLoop(); |
| 139 | } | 120 | } |
| 140 | 121 | ||
| @@ -194,7 +175,9 @@ void CpuManager::PreemptSingleCore(bool from_running_enviroment) { | |||
| 194 | { | 175 | { |
| 195 | auto& scheduler = system.Kernel().Scheduler(current_core); | 176 | auto& scheduler = system.Kernel().Scheduler(current_core); |
| 196 | scheduler.Reload(scheduler.GetSchedulerCurrentThread()); | 177 | scheduler.Reload(scheduler.GetSchedulerCurrentThread()); |
| 197 | idle_count = 0; | 178 | if (!scheduler.IsIdle()) { |
| 179 | idle_count = 0; | ||
| 180 | } | ||
| 198 | } | 181 | } |
| 199 | } | 182 | } |
| 200 | 183 | ||
diff --git a/src/core/cpu_manager.h b/src/core/cpu_manager.h index f0751fc58..76dc58ee1 100644 --- a/src/core/cpu_manager.h +++ b/src/core/cpu_manager.h | |||
| @@ -50,10 +50,15 @@ public: | |||
| 50 | void Initialize(); | 50 | void Initialize(); |
| 51 | void Shutdown(); | 51 | void Shutdown(); |
| 52 | 52 | ||
| 53 | static std::function<void(void*)> GetGuestThreadStartFunc(); | 53 | std::function<void()> GetGuestThreadStartFunc() { |
| 54 | static std::function<void(void*)> GetIdleThreadStartFunc(); | 54 | return [this] { GuestThreadFunction(); }; |
| 55 | static std::function<void(void*)> GetShutdownThreadStartFunc(); | 55 | } |
| 56 | void* GetStartFuncParameter(); | 56 | std::function<void()> GetIdleThreadStartFunc() { |
| 57 | return [this] { IdleThreadFunction(); }; | ||
| 58 | } | ||
| 59 | std::function<void()> GetShutdownThreadStartFunc() { | ||
| 60 | return [this] { ShutdownThreadFunction(); }; | ||
| 61 | } | ||
| 57 | 62 | ||
| 58 | void PreemptSingleCore(bool from_running_enviroment = true); | 63 | void PreemptSingleCore(bool from_running_enviroment = true); |
| 59 | 64 | ||
| @@ -62,10 +67,10 @@ public: | |||
| 62 | } | 67 | } |
| 63 | 68 | ||
| 64 | private: | 69 | private: |
| 65 | static void GuestThreadFunction(void* cpu_manager); | 70 | void GuestThreadFunction(); |
| 66 | static void GuestRewindFunction(void* cpu_manager); | 71 | void GuestRewindFunction(); |
| 67 | static void IdleThreadFunction(void* cpu_manager); | 72 | void IdleThreadFunction(); |
| 68 | static void ShutdownThreadFunction(void* cpu_manager); | 73 | void ShutdownThreadFunction(); |
| 69 | 74 | ||
| 70 | void MultiCoreRunGuestThread(); | 75 | void MultiCoreRunGuestThread(); |
| 71 | void MultiCoreRunGuestLoop(); | 76 | void MultiCoreRunGuestLoop(); |
diff --git a/src/core/debugger/gdbstub_arch.cpp b/src/core/debugger/gdbstub_arch.cpp index 750c353b9..4bef09bd7 100644 --- a/src/core/debugger/gdbstub_arch.cpp +++ b/src/core/debugger/gdbstub_arch.cpp | |||
| @@ -191,8 +191,10 @@ std::string GDBStubA64::RegRead(const Kernel::KThread* thread, size_t id) const | |||
| 191 | const auto& gprs{context.cpu_registers}; | 191 | const auto& gprs{context.cpu_registers}; |
| 192 | const auto& fprs{context.vector_registers}; | 192 | const auto& fprs{context.vector_registers}; |
| 193 | 193 | ||
| 194 | if (id <= SP_REGISTER) { | 194 | if (id < SP_REGISTER) { |
| 195 | return ValueToHex(gprs[id]); | 195 | return ValueToHex(gprs[id]); |
| 196 | } else if (id == SP_REGISTER) { | ||
| 197 | return ValueToHex(context.sp); | ||
| 196 | } else if (id == PC_REGISTER) { | 198 | } else if (id == PC_REGISTER) { |
| 197 | return ValueToHex(context.pc); | 199 | return ValueToHex(context.pc); |
| 198 | } else if (id == PSTATE_REGISTER) { | 200 | } else if (id == PSTATE_REGISTER) { |
| @@ -215,8 +217,10 @@ void GDBStubA64::RegWrite(Kernel::KThread* thread, size_t id, std::string_view v | |||
| 215 | 217 | ||
| 216 | auto& context{thread->GetContext64()}; | 218 | auto& context{thread->GetContext64()}; |
| 217 | 219 | ||
| 218 | if (id <= SP_REGISTER) { | 220 | if (id < SP_REGISTER) { |
| 219 | context.cpu_registers[id] = HexToValue<u64>(value); | 221 | context.cpu_registers[id] = HexToValue<u64>(value); |
| 222 | } else if (id == SP_REGISTER) { | ||
| 223 | context.sp = HexToValue<u64>(value); | ||
| 220 | } else if (id == PC_REGISTER) { | 224 | } else if (id == PC_REGISTER) { |
| 221 | context.pc = HexToValue<u64>(value); | 225 | context.pc = HexToValue<u64>(value); |
| 222 | } else if (id == PSTATE_REGISTER) { | 226 | } else if (id == PSTATE_REGISTER) { |
diff --git a/src/core/hardware_interrupt_manager.cpp b/src/core/hardware_interrupt_manager.cpp index d2d968a76..d08cc3315 100644 --- a/src/core/hardware_interrupt_manager.cpp +++ b/src/core/hardware_interrupt_manager.cpp | |||
| @@ -11,11 +11,14 @@ namespace Core::Hardware { | |||
| 11 | 11 | ||
| 12 | InterruptManager::InterruptManager(Core::System& system_in) : system(system_in) { | 12 | InterruptManager::InterruptManager(Core::System& system_in) : system(system_in) { |
| 13 | gpu_interrupt_event = Core::Timing::CreateEvent( | 13 | gpu_interrupt_event = Core::Timing::CreateEvent( |
| 14 | "GPUInterrupt", [this](std::uintptr_t message, std::chrono::nanoseconds) { | 14 | "GPUInterrupt", |
| 15 | [this](std::uintptr_t message, u64 time, | ||
| 16 | std::chrono::nanoseconds) -> std::optional<std::chrono::nanoseconds> { | ||
| 15 | auto nvdrv = system.ServiceManager().GetService<Service::Nvidia::NVDRV>("nvdrv"); | 17 | auto nvdrv = system.ServiceManager().GetService<Service::Nvidia::NVDRV>("nvdrv"); |
| 16 | const u32 syncpt = static_cast<u32>(message >> 32); | 18 | const u32 syncpt = static_cast<u32>(message >> 32); |
| 17 | const u32 value = static_cast<u32>(message); | 19 | const u32 value = static_cast<u32>(message); |
| 18 | nvdrv->SignalGPUInterruptSyncpt(syncpt, value); | 20 | nvdrv->SignalGPUInterruptSyncpt(syncpt, value); |
| 21 | return std::nullopt; | ||
| 19 | }); | 22 | }); |
| 20 | } | 23 | } |
| 21 | 24 | ||
diff --git a/src/core/hid/hid_types.h b/src/core/hid/hid_types.h index 9f76f9bcb..e49223016 100644 --- a/src/core/hid/hid_types.h +++ b/src/core/hid/hid_types.h | |||
| @@ -272,6 +272,7 @@ enum class VibrationDeviceType : u32 { | |||
| 272 | Unknown = 0, | 272 | Unknown = 0, |
| 273 | LinearResonantActuator = 1, | 273 | LinearResonantActuator = 1, |
| 274 | GcErm = 2, | 274 | GcErm = 2, |
| 275 | N64 = 3, | ||
| 275 | }; | 276 | }; |
| 276 | 277 | ||
| 277 | // This is nn::hid::VibrationGcErmCommand | 278 | // This is nn::hid::VibrationGcErmCommand |
diff --git a/src/core/hle/kernel/k_code_memory.h b/src/core/hle/kernel/k_code_memory.h index 2410f74a3..2e7e1436a 100644 --- a/src/core/hle/kernel/k_code_memory.h +++ b/src/core/hle/kernel/k_code_memory.h | |||
| @@ -30,19 +30,19 @@ public: | |||
| 30 | explicit KCodeMemory(KernelCore& kernel_); | 30 | explicit KCodeMemory(KernelCore& kernel_); |
| 31 | 31 | ||
| 32 | Result Initialize(Core::DeviceMemory& device_memory, VAddr address, size_t size); | 32 | Result Initialize(Core::DeviceMemory& device_memory, VAddr address, size_t size); |
| 33 | void Finalize(); | 33 | void Finalize() override; |
| 34 | 34 | ||
| 35 | Result Map(VAddr address, size_t size); | 35 | Result Map(VAddr address, size_t size); |
| 36 | Result Unmap(VAddr address, size_t size); | 36 | Result Unmap(VAddr address, size_t size); |
| 37 | Result MapToOwner(VAddr address, size_t size, Svc::MemoryPermission perm); | 37 | Result MapToOwner(VAddr address, size_t size, Svc::MemoryPermission perm); |
| 38 | Result UnmapFromOwner(VAddr address, size_t size); | 38 | Result UnmapFromOwner(VAddr address, size_t size); |
| 39 | 39 | ||
| 40 | bool IsInitialized() const { | 40 | bool IsInitialized() const override { |
| 41 | return m_is_initialized; | 41 | return m_is_initialized; |
| 42 | } | 42 | } |
| 43 | static void PostDestroy([[maybe_unused]] uintptr_t arg) {} | 43 | static void PostDestroy([[maybe_unused]] uintptr_t arg) {} |
| 44 | 44 | ||
| 45 | KProcess* GetOwner() const { | 45 | KProcess* GetOwner() const override { |
| 46 | return m_owner; | 46 | return m_owner; |
| 47 | } | 47 | } |
| 48 | VAddr GetSourceAddress() const { | 48 | VAddr GetSourceAddress() const { |
diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp index 183c693e3..b662788b3 100644 --- a/src/core/hle/kernel/k_process.cpp +++ b/src/core/hle/kernel/k_process.cpp | |||
| @@ -161,7 +161,7 @@ bool KProcess::ReleaseUserException(KThread* thread) { | |||
| 161 | std::addressof(num_waiters), | 161 | std::addressof(num_waiters), |
| 162 | reinterpret_cast<uintptr_t>(std::addressof(exception_thread))); | 162 | reinterpret_cast<uintptr_t>(std::addressof(exception_thread))); |
| 163 | next != nullptr) { | 163 | next != nullptr) { |
| 164 | next->SetState(ThreadState::Runnable); | 164 | next->EndWait(ResultSuccess); |
| 165 | } | 165 | } |
| 166 | 166 | ||
| 167 | KScheduler::SetSchedulerUpdateNeeded(kernel); | 167 | KScheduler::SetSchedulerUpdateNeeded(kernel); |
diff --git a/src/core/hle/kernel/k_scheduler.cpp b/src/core/hle/kernel/k_scheduler.cpp index d586b3f5c..d599d2bcb 100644 --- a/src/core/hle/kernel/k_scheduler.cpp +++ b/src/core/hle/kernel/k_scheduler.cpp | |||
| @@ -622,7 +622,7 @@ void KScheduler::YieldToAnyThread(KernelCore& kernel) { | |||
| 622 | } | 622 | } |
| 623 | 623 | ||
| 624 | KScheduler::KScheduler(Core::System& system_, s32 core_id_) : system{system_}, core_id{core_id_} { | 624 | KScheduler::KScheduler(Core::System& system_, s32 core_id_) : system{system_}, core_id{core_id_} { |
| 625 | switch_fiber = std::make_shared<Common::Fiber>(OnSwitch, this); | 625 | switch_fiber = std::make_shared<Common::Fiber>([this] { SwitchToCurrent(); }); |
| 626 | state.needs_scheduling.store(true); | 626 | state.needs_scheduling.store(true); |
| 627 | state.interrupt_task_thread_runnable = false; | 627 | state.interrupt_task_thread_runnable = false; |
| 628 | state.should_count_idle = false; | 628 | state.should_count_idle = false; |
| @@ -778,11 +778,6 @@ void KScheduler::ScheduleImpl() { | |||
| 778 | next_scheduler.SwitchContextStep2(); | 778 | next_scheduler.SwitchContextStep2(); |
| 779 | } | 779 | } |
| 780 | 780 | ||
| 781 | void KScheduler::OnSwitch(void* this_scheduler) { | ||
| 782 | KScheduler* sched = static_cast<KScheduler*>(this_scheduler); | ||
| 783 | sched->SwitchToCurrent(); | ||
| 784 | } | ||
| 785 | |||
| 786 | void KScheduler::SwitchToCurrent() { | 781 | void KScheduler::SwitchToCurrent() { |
| 787 | while (true) { | 782 | while (true) { |
| 788 | { | 783 | { |
diff --git a/src/core/hle/kernel/k_scheduler.h b/src/core/hle/kernel/k_scheduler.h index 3f90656ee..6a4760eca 100644 --- a/src/core/hle/kernel/k_scheduler.h +++ b/src/core/hle/kernel/k_scheduler.h | |||
| @@ -55,6 +55,11 @@ public: | |||
| 55 | return idle_thread; | 55 | return idle_thread; |
| 56 | } | 56 | } |
| 57 | 57 | ||
| 58 | /// Returns true if the scheduler is idle | ||
| 59 | [[nodiscard]] bool IsIdle() const { | ||
| 60 | return GetSchedulerCurrentThread() == idle_thread; | ||
| 61 | } | ||
| 62 | |||
| 58 | /// Gets the timestamp for the last context switch in ticks. | 63 | /// Gets the timestamp for the last context switch in ticks. |
| 59 | [[nodiscard]] u64 GetLastContextSwitchTicks() const; | 64 | [[nodiscard]] u64 GetLastContextSwitchTicks() const; |
| 60 | 65 | ||
| @@ -165,7 +170,6 @@ private: | |||
| 165 | */ | 170 | */ |
| 166 | void UpdateLastContextSwitchTime(KThread* thread, KProcess* process); | 171 | void UpdateLastContextSwitchTime(KThread* thread, KProcess* process); |
| 167 | 172 | ||
| 168 | static void OnSwitch(void* this_scheduler); | ||
| 169 | void SwitchToCurrent(); | 173 | void SwitchToCurrent(); |
| 170 | 174 | ||
| 171 | KThread* prev_thread{}; | 175 | KThread* prev_thread{}; |
diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp index 8d7faa662..90de86770 100644 --- a/src/core/hle/kernel/k_thread.cpp +++ b/src/core/hle/kernel/k_thread.cpp | |||
| @@ -246,14 +246,12 @@ Result KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_stack | |||
| 246 | 246 | ||
| 247 | Result KThread::InitializeThread(KThread* thread, KThreadFunction func, uintptr_t arg, | 247 | Result KThread::InitializeThread(KThread* thread, KThreadFunction func, uintptr_t arg, |
| 248 | VAddr user_stack_top, s32 prio, s32 core, KProcess* owner, | 248 | VAddr user_stack_top, s32 prio, s32 core, KProcess* owner, |
| 249 | ThreadType type, std::function<void(void*)>&& init_func, | 249 | ThreadType type, std::function<void()>&& init_func) { |
| 250 | void* init_func_parameter) { | ||
| 251 | // Initialize the thread. | 250 | // Initialize the thread. |
| 252 | R_TRY(thread->Initialize(func, arg, user_stack_top, prio, core, owner, type)); | 251 | R_TRY(thread->Initialize(func, arg, user_stack_top, prio, core, owner, type)); |
| 253 | 252 | ||
| 254 | // Initialize emulation parameters. | 253 | // Initialize emulation parameters. |
| 255 | thread->host_context = | 254 | thread->host_context = std::make_shared<Common::Fiber>(std::move(init_func)); |
| 256 | std::make_shared<Common::Fiber>(std::move(init_func), init_func_parameter); | ||
| 257 | thread->is_single_core = !Settings::values.use_multi_core.GetValue(); | 255 | thread->is_single_core = !Settings::values.use_multi_core.GetValue(); |
| 258 | 256 | ||
| 259 | return ResultSuccess; | 257 | return ResultSuccess; |
| @@ -265,15 +263,13 @@ Result KThread::InitializeDummyThread(KThread* thread) { | |||
| 265 | 263 | ||
| 266 | Result KThread::InitializeIdleThread(Core::System& system, KThread* thread, s32 virt_core) { | 264 | Result KThread::InitializeIdleThread(Core::System& system, KThread* thread, s32 virt_core) { |
| 267 | return InitializeThread(thread, {}, {}, {}, IdleThreadPriority, virt_core, {}, ThreadType::Main, | 265 | return InitializeThread(thread, {}, {}, {}, IdleThreadPriority, virt_core, {}, ThreadType::Main, |
| 268 | Core::CpuManager::GetIdleThreadStartFunc(), | 266 | system.GetCpuManager().GetIdleThreadStartFunc()); |
| 269 | system.GetCpuManager().GetStartFuncParameter()); | ||
| 270 | } | 267 | } |
| 271 | 268 | ||
| 272 | Result KThread::InitializeHighPriorityThread(Core::System& system, KThread* thread, | 269 | Result KThread::InitializeHighPriorityThread(Core::System& system, KThread* thread, |
| 273 | KThreadFunction func, uintptr_t arg, s32 virt_core) { | 270 | KThreadFunction func, uintptr_t arg, s32 virt_core) { |
| 274 | return InitializeThread(thread, func, arg, {}, {}, virt_core, nullptr, ThreadType::HighPriority, | 271 | return InitializeThread(thread, func, arg, {}, {}, virt_core, nullptr, ThreadType::HighPriority, |
| 275 | Core::CpuManager::GetShutdownThreadStartFunc(), | 272 | system.GetCpuManager().GetShutdownThreadStartFunc()); |
| 276 | system.GetCpuManager().GetStartFuncParameter()); | ||
| 277 | } | 273 | } |
| 278 | 274 | ||
| 279 | Result KThread::InitializeUserThread(Core::System& system, KThread* thread, KThreadFunction func, | 275 | Result KThread::InitializeUserThread(Core::System& system, KThread* thread, KThreadFunction func, |
| @@ -281,8 +277,7 @@ Result KThread::InitializeUserThread(Core::System& system, KThread* thread, KThr | |||
| 281 | KProcess* owner) { | 277 | KProcess* owner) { |
| 282 | system.Kernel().GlobalSchedulerContext().AddThread(thread); | 278 | system.Kernel().GlobalSchedulerContext().AddThread(thread); |
| 283 | return InitializeThread(thread, func, arg, user_stack_top, prio, virt_core, owner, | 279 | return InitializeThread(thread, func, arg, user_stack_top, prio, virt_core, owner, |
| 284 | ThreadType::User, Core::CpuManager::GetGuestThreadStartFunc(), | 280 | ThreadType::User, system.GetCpuManager().GetGuestThreadStartFunc()); |
| 285 | system.GetCpuManager().GetStartFuncParameter()); | ||
| 286 | } | 281 | } |
| 287 | 282 | ||
| 288 | void KThread::PostDestroy(uintptr_t arg) { | 283 | void KThread::PostDestroy(uintptr_t arg) { |
| @@ -313,14 +308,20 @@ void KThread::Finalize() { | |||
| 313 | 308 | ||
| 314 | auto it = waiter_list.begin(); | 309 | auto it = waiter_list.begin(); |
| 315 | while (it != waiter_list.end()) { | 310 | while (it != waiter_list.end()) { |
| 316 | // Clear the lock owner | 311 | // Get the thread. |
| 317 | it->SetLockOwner(nullptr); | 312 | KThread* const waiter = std::addressof(*it); |
| 313 | |||
| 314 | // The thread shouldn't be a kernel waiter. | ||
| 315 | ASSERT(!IsKernelAddressKey(waiter->GetAddressKey())); | ||
| 316 | |||
| 317 | // Clear the lock owner. | ||
| 318 | waiter->SetLockOwner(nullptr); | ||
| 318 | 319 | ||
| 319 | // Erase the waiter from our list. | 320 | // Erase the waiter from our list. |
| 320 | it = waiter_list.erase(it); | 321 | it = waiter_list.erase(it); |
| 321 | 322 | ||
| 322 | // Cancel the thread's wait. | 323 | // Cancel the thread's wait. |
| 323 | it->CancelWait(ResultInvalidState, true); | 324 | waiter->CancelWait(ResultInvalidState, true); |
| 324 | } | 325 | } |
| 325 | } | 326 | } |
| 326 | 327 | ||
| @@ -485,9 +486,7 @@ void KThread::Unpin() { | |||
| 485 | 486 | ||
| 486 | // Resume any threads that began waiting on us while we were pinned. | 487 | // Resume any threads that began waiting on us while we were pinned. |
| 487 | for (auto it = pinned_waiter_list.begin(); it != pinned_waiter_list.end(); ++it) { | 488 | for (auto it = pinned_waiter_list.begin(); it != pinned_waiter_list.end(); ++it) { |
| 488 | if (it->GetState() == ThreadState::Waiting) { | 489 | it->EndWait(ResultSuccess); |
| 489 | it->SetState(ThreadState::Runnable); | ||
| 490 | } | ||
| 491 | } | 490 | } |
| 492 | } | 491 | } |
| 493 | 492 | ||
| @@ -882,6 +881,7 @@ void KThread::AddWaiterImpl(KThread* thread) { | |||
| 882 | // Keep track of how many kernel waiters we have. | 881 | // Keep track of how many kernel waiters we have. |
| 883 | if (IsKernelAddressKey(thread->GetAddressKey())) { | 882 | if (IsKernelAddressKey(thread->GetAddressKey())) { |
| 884 | ASSERT((num_kernel_waiters++) >= 0); | 883 | ASSERT((num_kernel_waiters++) >= 0); |
| 884 | KScheduler::SetSchedulerUpdateNeeded(kernel); | ||
| 885 | } | 885 | } |
| 886 | 886 | ||
| 887 | // Insert the waiter. | 887 | // Insert the waiter. |
| @@ -895,6 +895,7 @@ void KThread::RemoveWaiterImpl(KThread* thread) { | |||
| 895 | // Keep track of how many kernel waiters we have. | 895 | // Keep track of how many kernel waiters we have. |
| 896 | if (IsKernelAddressKey(thread->GetAddressKey())) { | 896 | if (IsKernelAddressKey(thread->GetAddressKey())) { |
| 897 | ASSERT((num_kernel_waiters--) > 0); | 897 | ASSERT((num_kernel_waiters--) > 0); |
| 898 | KScheduler::SetSchedulerUpdateNeeded(kernel); | ||
| 898 | } | 899 | } |
| 899 | 900 | ||
| 900 | // Remove the waiter. | 901 | // Remove the waiter. |
| @@ -970,6 +971,7 @@ KThread* KThread::RemoveWaiterByKey(s32* out_num_waiters, VAddr key) { | |||
| 970 | // Keep track of how many kernel waiters we have. | 971 | // Keep track of how many kernel waiters we have. |
| 971 | if (IsKernelAddressKey(thread->GetAddressKey())) { | 972 | if (IsKernelAddressKey(thread->GetAddressKey())) { |
| 972 | ASSERT((num_kernel_waiters--) > 0); | 973 | ASSERT((num_kernel_waiters--) > 0); |
| 974 | KScheduler::SetSchedulerUpdateNeeded(kernel); | ||
| 973 | } | 975 | } |
| 974 | it = waiter_list.erase(it); | 976 | it = waiter_list.erase(it); |
| 975 | 977 | ||
diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h index 94c4cd1c8..28cd7ecb0 100644 --- a/src/core/hle/kernel/k_thread.h +++ b/src/core/hle/kernel/k_thread.h | |||
| @@ -729,8 +729,7 @@ private: | |||
| 729 | [[nodiscard]] static Result InitializeThread(KThread* thread, KThreadFunction func, | 729 | [[nodiscard]] static Result InitializeThread(KThread* thread, KThreadFunction func, |
| 730 | uintptr_t arg, VAddr user_stack_top, s32 prio, | 730 | uintptr_t arg, VAddr user_stack_top, s32 prio, |
| 731 | s32 core, KProcess* owner, ThreadType type, | 731 | s32 core, KProcess* owner, ThreadType type, |
| 732 | std::function<void(void*)>&& init_func, | 732 | std::function<void()>&& init_func); |
| 733 | void* init_func_parameter); | ||
| 734 | 733 | ||
| 735 | static void RestorePriority(KernelCore& kernel_ctx, KThread* thread); | 734 | static void RestorePriority(KernelCore& kernel_ctx, KThread* thread); |
| 736 | 735 | ||
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 0009193be..7307cf262 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp | |||
| @@ -234,17 +234,18 @@ struct KernelCore::Impl { | |||
| 234 | 234 | ||
| 235 | void InitializePreemption(KernelCore& kernel) { | 235 | void InitializePreemption(KernelCore& kernel) { |
| 236 | preemption_event = Core::Timing::CreateEvent( | 236 | preemption_event = Core::Timing::CreateEvent( |
| 237 | "PreemptionCallback", [this, &kernel](std::uintptr_t, std::chrono::nanoseconds) { | 237 | "PreemptionCallback", |
| 238 | [this, &kernel](std::uintptr_t, s64 time, | ||
| 239 | std::chrono::nanoseconds) -> std::optional<std::chrono::nanoseconds> { | ||
| 238 | { | 240 | { |
| 239 | KScopedSchedulerLock lock(kernel); | 241 | KScopedSchedulerLock lock(kernel); |
| 240 | global_scheduler_context->PreemptThreads(); | 242 | global_scheduler_context->PreemptThreads(); |
| 241 | } | 243 | } |
| 242 | const auto time_interval = std::chrono::nanoseconds{std::chrono::milliseconds(10)}; | 244 | return std::nullopt; |
| 243 | system.CoreTiming().ScheduleEvent(time_interval, preemption_event); | ||
| 244 | }); | 245 | }); |
| 245 | 246 | ||
| 246 | const auto time_interval = std::chrono::nanoseconds{std::chrono::milliseconds(10)}; | 247 | const auto time_interval = std::chrono::nanoseconds{std::chrono::milliseconds(10)}; |
| 247 | system.CoreTiming().ScheduleEvent(time_interval, preemption_event); | 248 | system.CoreTiming().ScheduleLoopingEvent(time_interval, time_interval, preemption_event); |
| 248 | } | 249 | } |
| 249 | 250 | ||
| 250 | void InitializeShutdownThreads() { | 251 | void InitializeShutdownThreads() { |
diff --git a/src/core/hle/kernel/time_manager.cpp b/src/core/hle/kernel/time_manager.cpp index 2724c3782..5ee72c432 100644 --- a/src/core/hle/kernel/time_manager.cpp +++ b/src/core/hle/kernel/time_manager.cpp | |||
| @@ -11,15 +11,17 @@ | |||
| 11 | namespace Kernel { | 11 | namespace Kernel { |
| 12 | 12 | ||
| 13 | TimeManager::TimeManager(Core::System& system_) : system{system_} { | 13 | TimeManager::TimeManager(Core::System& system_) : system{system_} { |
| 14 | time_manager_event_type = | 14 | time_manager_event_type = Core::Timing::CreateEvent( |
| 15 | Core::Timing::CreateEvent("Kernel::TimeManagerCallback", | 15 | "Kernel::TimeManagerCallback", |
| 16 | [this](std::uintptr_t thread_handle, std::chrono::nanoseconds) { | 16 | [this](std::uintptr_t thread_handle, s64 time, |
| 17 | KThread* thread = reinterpret_cast<KThread*>(thread_handle); | 17 | std::chrono::nanoseconds) -> std::optional<std::chrono::nanoseconds> { |
| 18 | { | 18 | KThread* thread = reinterpret_cast<KThread*>(thread_handle); |
| 19 | KScopedSchedulerLock sl(system.Kernel()); | 19 | { |
| 20 | thread->OnTimer(); | 20 | KScopedSchedulerLock sl(system.Kernel()); |
| 21 | } | 21 | thread->OnTimer(); |
| 22 | }); | 22 | } |
| 23 | return std::nullopt; | ||
| 24 | }); | ||
| 23 | } | 25 | } |
| 24 | 26 | ||
| 25 | void TimeManager::ScheduleTimeEvent(KThread* thread, s64 nanoseconds) { | 27 | void TimeManager::ScheduleTimeEvent(KThread* thread, s64 nanoseconds) { |
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index c08b0a5dc..3c28dee76 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp | |||
| @@ -49,28 +49,41 @@ bool Controller_NPad::IsNpadIdValid(Core::HID::NpadIdType npad_id) { | |||
| 49 | } | 49 | } |
| 50 | } | 50 | } |
| 51 | 51 | ||
| 52 | bool Controller_NPad::IsDeviceHandleValid(const Core::HID::VibrationDeviceHandle& device_handle) { | 52 | Result Controller_NPad::IsDeviceHandleValid(const Core::HID::VibrationDeviceHandle& device_handle) { |
| 53 | const auto npad_id = IsNpadIdValid(static_cast<Core::HID::NpadIdType>(device_handle.npad_id)); | 53 | const auto npad_id = IsNpadIdValid(static_cast<Core::HID::NpadIdType>(device_handle.npad_id)); |
| 54 | const bool npad_type = device_handle.npad_type < Core::HID::NpadStyleIndex::MaxNpadType; | 54 | const bool npad_type = device_handle.npad_type < Core::HID::NpadStyleIndex::MaxNpadType; |
| 55 | const bool device_index = device_handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex; | 55 | const bool device_index = device_handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex; |
| 56 | return npad_id && npad_type && device_index; | 56 | |
| 57 | if (!npad_type) { | ||
| 58 | return VibrationInvalidStyleIndex; | ||
| 59 | } | ||
| 60 | if (!npad_id) { | ||
| 61 | return VibrationInvalidNpadId; | ||
| 62 | } | ||
| 63 | if (!device_index) { | ||
| 64 | return VibrationDeviceIndexOutOfRange; | ||
| 65 | } | ||
| 66 | |||
| 67 | return ResultSuccess; | ||
| 57 | } | 68 | } |
| 58 | 69 | ||
| 59 | Result Controller_NPad::VerifyValidSixAxisSensorHandle( | 70 | Result Controller_NPad::VerifyValidSixAxisSensorHandle( |
| 60 | const Core::HID::SixAxisSensorHandle& device_handle) { | 71 | const Core::HID::SixAxisSensorHandle& device_handle) { |
| 61 | const auto npad_id = IsNpadIdValid(static_cast<Core::HID::NpadIdType>(device_handle.npad_id)); | 72 | const auto npad_id = IsNpadIdValid(static_cast<Core::HID::NpadIdType>(device_handle.npad_id)); |
| 73 | const bool device_index = device_handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex; | ||
| 74 | const bool npad_type = device_handle.npad_type < Core::HID::NpadStyleIndex::MaxNpadType; | ||
| 75 | |||
| 62 | if (!npad_id) { | 76 | if (!npad_id) { |
| 63 | return InvalidNpadId; | 77 | return InvalidNpadId; |
| 64 | } | 78 | } |
| 65 | const bool device_index = device_handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex; | ||
| 66 | if (!device_index) { | 79 | if (!device_index) { |
| 67 | return NpadDeviceIndexOutOfRange; | 80 | return NpadDeviceIndexOutOfRange; |
| 68 | } | 81 | } |
| 69 | // This doesn't get validated on nnsdk | 82 | // This doesn't get validated on nnsdk |
| 70 | const bool npad_type = device_handle.npad_type < Core::HID::NpadStyleIndex::MaxNpadType; | ||
| 71 | if (!npad_type) { | 83 | if (!npad_type) { |
| 72 | return NpadInvalidHandle; | 84 | return NpadInvalidHandle; |
| 73 | } | 85 | } |
| 86 | |||
| 74 | return ResultSuccess; | 87 | return ResultSuccess; |
| 75 | } | 88 | } |
| 76 | 89 | ||
| @@ -705,6 +718,11 @@ Controller_NPad::NpadJoyHoldType Controller_NPad::GetHoldType() const { | |||
| 705 | } | 718 | } |
| 706 | 719 | ||
| 707 | void Controller_NPad::SetNpadHandheldActivationMode(NpadHandheldActivationMode activation_mode) { | 720 | void Controller_NPad::SetNpadHandheldActivationMode(NpadHandheldActivationMode activation_mode) { |
| 721 | if (activation_mode >= NpadHandheldActivationMode::MaxActivationMode) { | ||
| 722 | ASSERT_MSG(false, "Activation mode should be always None, Single or Dual"); | ||
| 723 | return; | ||
| 724 | } | ||
| 725 | |||
| 708 | handheld_activation_mode = activation_mode; | 726 | handheld_activation_mode = activation_mode; |
| 709 | } | 727 | } |
| 710 | 728 | ||
| @@ -820,11 +838,11 @@ bool Controller_NPad::VibrateControllerAtIndex(Core::HID::NpadIdType npad_id, | |||
| 820 | 838 | ||
| 821 | const auto now = steady_clock::now(); | 839 | const auto now = steady_clock::now(); |
| 822 | 840 | ||
| 823 | // Filter out non-zero vibrations that are within 10ms of each other. | 841 | // Filter out non-zero vibrations that are within 15ms of each other. |
| 824 | if ((vibration_value.low_amplitude != 0.0f || vibration_value.high_amplitude != 0.0f) && | 842 | if ((vibration_value.low_amplitude != 0.0f || vibration_value.high_amplitude != 0.0f) && |
| 825 | duration_cast<milliseconds>( | 843 | duration_cast<milliseconds>( |
| 826 | now - controller.vibration[device_index].last_vibration_timepoint) < | 844 | now - controller.vibration[device_index].last_vibration_timepoint) < |
| 827 | milliseconds(10)) { | 845 | milliseconds(15)) { |
| 828 | return false; | 846 | return false; |
| 829 | } | 847 | } |
| 830 | 848 | ||
| @@ -840,7 +858,7 @@ bool Controller_NPad::VibrateControllerAtIndex(Core::HID::NpadIdType npad_id, | |||
| 840 | void Controller_NPad::VibrateController( | 858 | void Controller_NPad::VibrateController( |
| 841 | const Core::HID::VibrationDeviceHandle& vibration_device_handle, | 859 | const Core::HID::VibrationDeviceHandle& vibration_device_handle, |
| 842 | const Core::HID::VibrationValue& vibration_value) { | 860 | const Core::HID::VibrationValue& vibration_value) { |
| 843 | if (!IsDeviceHandleValid(vibration_device_handle)) { | 861 | if (IsDeviceHandleValid(vibration_device_handle).IsError()) { |
| 844 | return; | 862 | return; |
| 845 | } | 863 | } |
| 846 | 864 | ||
| @@ -903,7 +921,7 @@ void Controller_NPad::VibrateControllers( | |||
| 903 | 921 | ||
| 904 | Core::HID::VibrationValue Controller_NPad::GetLastVibration( | 922 | Core::HID::VibrationValue Controller_NPad::GetLastVibration( |
| 905 | const Core::HID::VibrationDeviceHandle& vibration_device_handle) const { | 923 | const Core::HID::VibrationDeviceHandle& vibration_device_handle) const { |
| 906 | if (!IsDeviceHandleValid(vibration_device_handle)) { | 924 | if (IsDeviceHandleValid(vibration_device_handle).IsError()) { |
| 907 | return {}; | 925 | return {}; |
| 908 | } | 926 | } |
| 909 | 927 | ||
| @@ -914,7 +932,7 @@ Core::HID::VibrationValue Controller_NPad::GetLastVibration( | |||
| 914 | 932 | ||
| 915 | void Controller_NPad::InitializeVibrationDevice( | 933 | void Controller_NPad::InitializeVibrationDevice( |
| 916 | const Core::HID::VibrationDeviceHandle& vibration_device_handle) { | 934 | const Core::HID::VibrationDeviceHandle& vibration_device_handle) { |
| 917 | if (!IsDeviceHandleValid(vibration_device_handle)) { | 935 | if (IsDeviceHandleValid(vibration_device_handle).IsError()) { |
| 918 | return; | 936 | return; |
| 919 | } | 937 | } |
| 920 | 938 | ||
| @@ -941,7 +959,7 @@ void Controller_NPad::SetPermitVibrationSession(bool permit_vibration_session) { | |||
| 941 | 959 | ||
| 942 | bool Controller_NPad::IsVibrationDeviceMounted( | 960 | bool Controller_NPad::IsVibrationDeviceMounted( |
| 943 | const Core::HID::VibrationDeviceHandle& vibration_device_handle) const { | 961 | const Core::HID::VibrationDeviceHandle& vibration_device_handle) const { |
| 944 | if (!IsDeviceHandleValid(vibration_device_handle)) { | 962 | if (IsDeviceHandleValid(vibration_device_handle).IsError()) { |
| 945 | return false; | 963 | return false; |
| 946 | } | 964 | } |
| 947 | 965 | ||
diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h index 8b54724ed..1a589cca2 100644 --- a/src/core/hle/service/hid/controllers/npad.h +++ b/src/core/hle/service/hid/controllers/npad.h | |||
| @@ -81,6 +81,7 @@ public: | |||
| 81 | Dual = 0, | 81 | Dual = 0, |
| 82 | Single = 1, | 82 | Single = 1, |
| 83 | None = 2, | 83 | None = 2, |
| 84 | MaxActivationMode = 3, | ||
| 84 | }; | 85 | }; |
| 85 | 86 | ||
| 86 | // This is nn::hid::NpadCommunicationMode | 87 | // This is nn::hid::NpadCommunicationMode |
| @@ -196,7 +197,7 @@ public: | |||
| 196 | Core::HID::NpadButton GetAndResetPressState(); | 197 | Core::HID::NpadButton GetAndResetPressState(); |
| 197 | 198 | ||
| 198 | static bool IsNpadIdValid(Core::HID::NpadIdType npad_id); | 199 | static bool IsNpadIdValid(Core::HID::NpadIdType npad_id); |
| 199 | static bool IsDeviceHandleValid(const Core::HID::VibrationDeviceHandle& device_handle); | 200 | static Result IsDeviceHandleValid(const Core::HID::VibrationDeviceHandle& device_handle); |
| 200 | static Result VerifyValidSixAxisSensorHandle( | 201 | static Result VerifyValidSixAxisSensorHandle( |
| 201 | const Core::HID::SixAxisSensorHandle& device_handle); | 202 | const Core::HID::SixAxisSensorHandle& device_handle); |
| 202 | 203 | ||
diff --git a/src/core/hle/service/hid/errors.h b/src/core/hle/service/hid/errors.h index 615c23b84..46282f42e 100644 --- a/src/core/hle/service/hid/errors.h +++ b/src/core/hle/service/hid/errors.h | |||
| @@ -9,6 +9,9 @@ namespace Service::HID { | |||
| 9 | 9 | ||
| 10 | constexpr Result NpadInvalidHandle{ErrorModule::HID, 100}; | 10 | constexpr Result NpadInvalidHandle{ErrorModule::HID, 100}; |
| 11 | constexpr Result NpadDeviceIndexOutOfRange{ErrorModule::HID, 107}; | 11 | constexpr Result NpadDeviceIndexOutOfRange{ErrorModule::HID, 107}; |
| 12 | constexpr Result VibrationInvalidStyleIndex{ErrorModule::HID, 122}; | ||
| 13 | constexpr Result VibrationInvalidNpadId{ErrorModule::HID, 123}; | ||
| 14 | constexpr Result VibrationDeviceIndexOutOfRange{ErrorModule::HID, 124}; | ||
| 12 | constexpr Result InvalidSixAxisFusionRange{ErrorModule::HID, 423}; | 15 | constexpr Result InvalidSixAxisFusionRange{ErrorModule::HID, 423}; |
| 13 | constexpr Result NpadIsDualJoycon{ErrorModule::HID, 601}; | 16 | constexpr Result NpadIsDualJoycon{ErrorModule::HID, 601}; |
| 14 | constexpr Result NpadIsSameType{ErrorModule::HID, 602}; | 17 | constexpr Result NpadIsSameType{ErrorModule::HID, 602}; |
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index dc5d0366d..89bb12442 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp | |||
| @@ -74,26 +74,34 @@ IAppletResource::IAppletResource(Core::System& system_, | |||
| 74 | // Register update callbacks | 74 | // Register update callbacks |
| 75 | pad_update_event = Core::Timing::CreateEvent( | 75 | pad_update_event = Core::Timing::CreateEvent( |
| 76 | "HID::UpdatePadCallback", | 76 | "HID::UpdatePadCallback", |
| 77 | [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { | 77 | [this](std::uintptr_t user_data, s64 time, |
| 78 | std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { | ||
| 78 | const auto guard = LockService(); | 79 | const auto guard = LockService(); |
| 79 | UpdateControllers(user_data, ns_late); | 80 | UpdateControllers(user_data, ns_late); |
| 81 | return std::nullopt; | ||
| 80 | }); | 82 | }); |
| 81 | mouse_keyboard_update_event = Core::Timing::CreateEvent( | 83 | mouse_keyboard_update_event = Core::Timing::CreateEvent( |
| 82 | "HID::UpdateMouseKeyboardCallback", | 84 | "HID::UpdateMouseKeyboardCallback", |
| 83 | [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { | 85 | [this](std::uintptr_t user_data, s64 time, |
| 86 | std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { | ||
| 84 | const auto guard = LockService(); | 87 | const auto guard = LockService(); |
| 85 | UpdateMouseKeyboard(user_data, ns_late); | 88 | UpdateMouseKeyboard(user_data, ns_late); |
| 89 | return std::nullopt; | ||
| 86 | }); | 90 | }); |
| 87 | motion_update_event = Core::Timing::CreateEvent( | 91 | motion_update_event = Core::Timing::CreateEvent( |
| 88 | "HID::UpdateMotionCallback", | 92 | "HID::UpdateMotionCallback", |
| 89 | [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { | 93 | [this](std::uintptr_t user_data, s64 time, |
| 94 | std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { | ||
| 90 | const auto guard = LockService(); | 95 | const auto guard = LockService(); |
| 91 | UpdateMotion(user_data, ns_late); | 96 | UpdateMotion(user_data, ns_late); |
| 97 | return std::nullopt; | ||
| 92 | }); | 98 | }); |
| 93 | 99 | ||
| 94 | system.CoreTiming().ScheduleEvent(pad_update_ns, pad_update_event); | 100 | system.CoreTiming().ScheduleLoopingEvent(pad_update_ns, pad_update_ns, pad_update_event); |
| 95 | system.CoreTiming().ScheduleEvent(mouse_keyboard_update_ns, mouse_keyboard_update_event); | 101 | system.CoreTiming().ScheduleLoopingEvent(mouse_keyboard_update_ns, mouse_keyboard_update_ns, |
| 96 | system.CoreTiming().ScheduleEvent(motion_update_ns, motion_update_event); | 102 | mouse_keyboard_update_event); |
| 103 | system.CoreTiming().ScheduleLoopingEvent(motion_update_ns, motion_update_ns, | ||
| 104 | motion_update_event); | ||
| 97 | 105 | ||
| 98 | system.HIDCore().ReloadInputDevices(); | 106 | system.HIDCore().ReloadInputDevices(); |
| 99 | } | 107 | } |
| @@ -135,13 +143,6 @@ void IAppletResource::UpdateControllers(std::uintptr_t user_data, | |||
| 135 | } | 143 | } |
| 136 | controller->OnUpdate(core_timing); | 144 | controller->OnUpdate(core_timing); |
| 137 | } | 145 | } |
| 138 | |||
| 139 | // If ns_late is higher than the update rate ignore the delay | ||
| 140 | if (ns_late > pad_update_ns) { | ||
| 141 | ns_late = {}; | ||
| 142 | } | ||
| 143 | |||
| 144 | core_timing.ScheduleEvent(pad_update_ns - ns_late, pad_update_event); | ||
| 145 | } | 146 | } |
| 146 | 147 | ||
| 147 | void IAppletResource::UpdateMouseKeyboard(std::uintptr_t user_data, | 148 | void IAppletResource::UpdateMouseKeyboard(std::uintptr_t user_data, |
| @@ -150,26 +151,12 @@ void IAppletResource::UpdateMouseKeyboard(std::uintptr_t user_data, | |||
| 150 | 151 | ||
| 151 | controllers[static_cast<size_t>(HidController::Mouse)]->OnUpdate(core_timing); | 152 | controllers[static_cast<size_t>(HidController::Mouse)]->OnUpdate(core_timing); |
| 152 | controllers[static_cast<size_t>(HidController::Keyboard)]->OnUpdate(core_timing); | 153 | controllers[static_cast<size_t>(HidController::Keyboard)]->OnUpdate(core_timing); |
| 153 | |||
| 154 | // If ns_late is higher than the update rate ignore the delay | ||
| 155 | if (ns_late > mouse_keyboard_update_ns) { | ||
| 156 | ns_late = {}; | ||
| 157 | } | ||
| 158 | |||
| 159 | core_timing.ScheduleEvent(mouse_keyboard_update_ns - ns_late, mouse_keyboard_update_event); | ||
| 160 | } | 154 | } |
| 161 | 155 | ||
| 162 | void IAppletResource::UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { | 156 | void IAppletResource::UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { |
| 163 | auto& core_timing = system.CoreTiming(); | 157 | auto& core_timing = system.CoreTiming(); |
| 164 | 158 | ||
| 165 | controllers[static_cast<size_t>(HidController::NPad)]->OnMotionUpdate(core_timing); | 159 | controllers[static_cast<size_t>(HidController::NPad)]->OnMotionUpdate(core_timing); |
| 166 | |||
| 167 | // If ns_late is higher than the update rate ignore the delay | ||
| 168 | if (ns_late > motion_update_ns) { | ||
| 169 | ns_late = {}; | ||
| 170 | } | ||
| 171 | |||
| 172 | core_timing.ScheduleEvent(motion_update_ns - ns_late, motion_update_event); | ||
| 173 | } | 160 | } |
| 174 | 161 | ||
| 175 | class IActiveVibrationDeviceList final : public ServiceFramework<IActiveVibrationDeviceList> { | 162 | class IActiveVibrationDeviceList final : public ServiceFramework<IActiveVibrationDeviceList> { |
| @@ -778,7 +765,7 @@ void Hid::IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx) { | |||
| 778 | 765 | ||
| 779 | bool is_at_rest{}; | 766 | bool is_at_rest{}; |
| 780 | auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); | 767 | auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); |
| 781 | const auto result = controller.IsSixAxisSensorAtRest(parameters.sixaxis_handle, is_at_rest); | 768 | controller.IsSixAxisSensorAtRest(parameters.sixaxis_handle, is_at_rest); |
| 782 | 769 | ||
| 783 | LOG_DEBUG(Service_HID, | 770 | LOG_DEBUG(Service_HID, |
| 784 | "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", | 771 | "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", |
| @@ -786,7 +773,7 @@ void Hid::IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx) { | |||
| 786 | parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); | 773 | parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); |
| 787 | 774 | ||
| 788 | IPC::ResponseBuilder rb{ctx, 3}; | 775 | IPC::ResponseBuilder rb{ctx, 3}; |
| 789 | rb.Push(result); | 776 | rb.Push(ResultSuccess); |
| 790 | rb.Push(is_at_rest); | 777 | rb.Push(is_at_rest); |
| 791 | } | 778 | } |
| 792 | 779 | ||
| @@ -803,8 +790,8 @@ void Hid::IsFirmwareUpdateAvailableForSixAxisSensor(Kernel::HLERequestContext& c | |||
| 803 | 790 | ||
| 804 | bool is_firmware_available{}; | 791 | bool is_firmware_available{}; |
| 805 | auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); | 792 | auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); |
| 806 | const auto result = controller.IsFirmwareUpdateAvailableForSixAxisSensor( | 793 | controller.IsFirmwareUpdateAvailableForSixAxisSensor(parameters.sixaxis_handle, |
| 807 | parameters.sixaxis_handle, is_firmware_available); | 794 | is_firmware_available); |
| 808 | 795 | ||
| 809 | LOG_WARNING( | 796 | LOG_WARNING( |
| 810 | Service_HID, | 797 | Service_HID, |
| @@ -813,7 +800,7 @@ void Hid::IsFirmwareUpdateAvailableForSixAxisSensor(Kernel::HLERequestContext& c | |||
| 813 | parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); | 800 | parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); |
| 814 | 801 | ||
| 815 | IPC::ResponseBuilder rb{ctx, 3}; | 802 | IPC::ResponseBuilder rb{ctx, 3}; |
| 816 | rb.Push(result); | 803 | rb.Push(ResultSuccess); |
| 817 | rb.Push(is_firmware_available); | 804 | rb.Push(is_firmware_available); |
| 818 | } | 805 | } |
| 819 | 806 | ||
| @@ -1083,13 +1070,13 @@ void Hid::DisconnectNpad(Kernel::HLERequestContext& ctx) { | |||
| 1083 | const auto parameters{rp.PopRaw<Parameters>()}; | 1070 | const auto parameters{rp.PopRaw<Parameters>()}; |
| 1084 | 1071 | ||
| 1085 | auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); | 1072 | auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); |
| 1086 | const auto result = controller.DisconnectNpad(parameters.npad_id); | 1073 | controller.DisconnectNpad(parameters.npad_id); |
| 1087 | 1074 | ||
| 1088 | LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id, | 1075 | LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id, |
| 1089 | parameters.applet_resource_user_id); | 1076 | parameters.applet_resource_user_id); |
| 1090 | 1077 | ||
| 1091 | IPC::ResponseBuilder rb{ctx, 2}; | 1078 | IPC::ResponseBuilder rb{ctx, 2}; |
| 1092 | rb.Push(result); | 1079 | rb.Push(ResultSuccess); |
| 1093 | } | 1080 | } |
| 1094 | 1081 | ||
| 1095 | void Hid::GetPlayerLedPattern(Kernel::HLERequestContext& ctx) { | 1082 | void Hid::GetPlayerLedPattern(Kernel::HLERequestContext& ctx) { |
| @@ -1165,15 +1152,14 @@ void Hid::SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx | |||
| 1165 | const auto parameters{rp.PopRaw<Parameters>()}; | 1152 | const auto parameters{rp.PopRaw<Parameters>()}; |
| 1166 | 1153 | ||
| 1167 | auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); | 1154 | auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); |
| 1168 | const auto result = | 1155 | controller.SetNpadMode(parameters.npad_id, Controller_NPad::NpadJoyDeviceType::Left, |
| 1169 | controller.SetNpadMode(parameters.npad_id, Controller_NPad::NpadJoyDeviceType::Left, | 1156 | Controller_NPad::NpadJoyAssignmentMode::Single); |
| 1170 | Controller_NPad::NpadJoyAssignmentMode::Single); | ||
| 1171 | 1157 | ||
| 1172 | LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id, | 1158 | LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id, |
| 1173 | parameters.applet_resource_user_id); | 1159 | parameters.applet_resource_user_id); |
| 1174 | 1160 | ||
| 1175 | IPC::ResponseBuilder rb{ctx, 2}; | 1161 | IPC::ResponseBuilder rb{ctx, 2}; |
| 1176 | rb.Push(result); | 1162 | rb.Push(ResultSuccess); |
| 1177 | } | 1163 | } |
| 1178 | 1164 | ||
| 1179 | void Hid::SetNpadJoyAssignmentModeSingle(Kernel::HLERequestContext& ctx) { | 1165 | void Hid::SetNpadJoyAssignmentModeSingle(Kernel::HLERequestContext& ctx) { |
| @@ -1189,15 +1175,15 @@ void Hid::SetNpadJoyAssignmentModeSingle(Kernel::HLERequestContext& ctx) { | |||
| 1189 | const auto parameters{rp.PopRaw<Parameters>()}; | 1175 | const auto parameters{rp.PopRaw<Parameters>()}; |
| 1190 | 1176 | ||
| 1191 | auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); | 1177 | auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); |
| 1192 | const auto result = controller.SetNpadMode(parameters.npad_id, parameters.npad_joy_device_type, | 1178 | controller.SetNpadMode(parameters.npad_id, parameters.npad_joy_device_type, |
| 1193 | Controller_NPad::NpadJoyAssignmentMode::Single); | 1179 | Controller_NPad::NpadJoyAssignmentMode::Single); |
| 1194 | 1180 | ||
| 1195 | LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}", | 1181 | LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}", |
| 1196 | parameters.npad_id, parameters.applet_resource_user_id, | 1182 | parameters.npad_id, parameters.applet_resource_user_id, |
| 1197 | parameters.npad_joy_device_type); | 1183 | parameters.npad_joy_device_type); |
| 1198 | 1184 | ||
| 1199 | IPC::ResponseBuilder rb{ctx, 2}; | 1185 | IPC::ResponseBuilder rb{ctx, 2}; |
| 1200 | rb.Push(result); | 1186 | rb.Push(ResultSuccess); |
| 1201 | } | 1187 | } |
| 1202 | 1188 | ||
| 1203 | void Hid::SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) { | 1189 | void Hid::SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) { |
| @@ -1212,14 +1198,13 @@ void Hid::SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) { | |||
| 1212 | const auto parameters{rp.PopRaw<Parameters>()}; | 1198 | const auto parameters{rp.PopRaw<Parameters>()}; |
| 1213 | 1199 | ||
| 1214 | auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); | 1200 | auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); |
| 1215 | const auto result = controller.SetNpadMode(parameters.npad_id, {}, | 1201 | controller.SetNpadMode(parameters.npad_id, {}, Controller_NPad::NpadJoyAssignmentMode::Dual); |
| 1216 | Controller_NPad::NpadJoyAssignmentMode::Dual); | ||
| 1217 | 1202 | ||
| 1218 | LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id, | 1203 | LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id, |
| 1219 | parameters.applet_resource_user_id); | 1204 | parameters.applet_resource_user_id); |
| 1220 | 1205 | ||
| 1221 | IPC::ResponseBuilder rb{ctx, 2}; | 1206 | IPC::ResponseBuilder rb{ctx, 2}; |
| 1222 | rb.Push(result); | 1207 | rb.Push(ResultSuccess); |
| 1223 | } | 1208 | } |
| 1224 | 1209 | ||
| 1225 | void Hid::MergeSingleJoyAsDualJoy(Kernel::HLERequestContext& ctx) { | 1210 | void Hid::MergeSingleJoyAsDualJoy(Kernel::HLERequestContext& ctx) { |
| @@ -1412,8 +1397,11 @@ void Hid::ClearNpadCaptureButtonAssignment(Kernel::HLERequestContext& ctx) { | |||
| 1412 | void Hid::GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) { | 1397 | void Hid::GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) { |
| 1413 | IPC::RequestParser rp{ctx}; | 1398 | IPC::RequestParser rp{ctx}; |
| 1414 | const auto vibration_device_handle{rp.PopRaw<Core::HID::VibrationDeviceHandle>()}; | 1399 | const auto vibration_device_handle{rp.PopRaw<Core::HID::VibrationDeviceHandle>()}; |
| 1400 | const auto& controller = | ||
| 1401 | GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); | ||
| 1415 | 1402 | ||
| 1416 | Core::HID::VibrationDeviceInfo vibration_device_info; | 1403 | Core::HID::VibrationDeviceInfo vibration_device_info; |
| 1404 | bool check_device_index = false; | ||
| 1417 | 1405 | ||
| 1418 | switch (vibration_device_handle.npad_type) { | 1406 | switch (vibration_device_handle.npad_type) { |
| 1419 | case Core::HID::NpadStyleIndex::ProController: | 1407 | case Core::HID::NpadStyleIndex::ProController: |
| @@ -1421,34 +1409,46 @@ void Hid::GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) { | |||
| 1421 | case Core::HID::NpadStyleIndex::JoyconDual: | 1409 | case Core::HID::NpadStyleIndex::JoyconDual: |
| 1422 | case Core::HID::NpadStyleIndex::JoyconLeft: | 1410 | case Core::HID::NpadStyleIndex::JoyconLeft: |
| 1423 | case Core::HID::NpadStyleIndex::JoyconRight: | 1411 | case Core::HID::NpadStyleIndex::JoyconRight: |
| 1424 | default: | ||
| 1425 | vibration_device_info.type = Core::HID::VibrationDeviceType::LinearResonantActuator; | 1412 | vibration_device_info.type = Core::HID::VibrationDeviceType::LinearResonantActuator; |
| 1413 | check_device_index = true; | ||
| 1426 | break; | 1414 | break; |
| 1427 | case Core::HID::NpadStyleIndex::GameCube: | 1415 | case Core::HID::NpadStyleIndex::GameCube: |
| 1428 | vibration_device_info.type = Core::HID::VibrationDeviceType::GcErm; | 1416 | vibration_device_info.type = Core::HID::VibrationDeviceType::GcErm; |
| 1429 | break; | 1417 | break; |
| 1430 | case Core::HID::NpadStyleIndex::Pokeball: | 1418 | case Core::HID::NpadStyleIndex::N64: |
| 1419 | vibration_device_info.type = Core::HID::VibrationDeviceType::N64; | ||
| 1420 | break; | ||
| 1421 | default: | ||
| 1431 | vibration_device_info.type = Core::HID::VibrationDeviceType::Unknown; | 1422 | vibration_device_info.type = Core::HID::VibrationDeviceType::Unknown; |
| 1432 | break; | 1423 | break; |
| 1433 | } | 1424 | } |
| 1434 | 1425 | ||
| 1435 | switch (vibration_device_handle.device_index) { | 1426 | vibration_device_info.position = Core::HID::VibrationDevicePosition::None; |
| 1436 | case Core::HID::DeviceIndex::Left: | 1427 | if (check_device_index) { |
| 1437 | vibration_device_info.position = Core::HID::VibrationDevicePosition::Left; | 1428 | switch (vibration_device_handle.device_index) { |
| 1438 | break; | 1429 | case Core::HID::DeviceIndex::Left: |
| 1439 | case Core::HID::DeviceIndex::Right: | 1430 | vibration_device_info.position = Core::HID::VibrationDevicePosition::Left; |
| 1440 | vibration_device_info.position = Core::HID::VibrationDevicePosition::Right; | 1431 | break; |
| 1441 | break; | 1432 | case Core::HID::DeviceIndex::Right: |
| 1442 | case Core::HID::DeviceIndex::None: | 1433 | vibration_device_info.position = Core::HID::VibrationDevicePosition::Right; |
| 1443 | default: | 1434 | break; |
| 1444 | ASSERT_MSG(false, "DeviceIndex should never be None!"); | 1435 | case Core::HID::DeviceIndex::None: |
| 1445 | vibration_device_info.position = Core::HID::VibrationDevicePosition::None; | 1436 | default: |
| 1446 | break; | 1437 | ASSERT_MSG(false, "DeviceIndex should never be None!"); |
| 1438 | break; | ||
| 1439 | } | ||
| 1447 | } | 1440 | } |
| 1448 | 1441 | ||
| 1449 | LOG_DEBUG(Service_HID, "called, vibration_device_type={}, vibration_device_position={}", | 1442 | LOG_DEBUG(Service_HID, "called, vibration_device_type={}, vibration_device_position={}", |
| 1450 | vibration_device_info.type, vibration_device_info.position); | 1443 | vibration_device_info.type, vibration_device_info.position); |
| 1451 | 1444 | ||
| 1445 | const auto result = controller.IsDeviceHandleValid(vibration_device_handle); | ||
| 1446 | if (result.IsError()) { | ||
| 1447 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 1448 | rb.Push(result); | ||
| 1449 | return; | ||
| 1450 | } | ||
| 1451 | |||
| 1452 | IPC::ResponseBuilder rb{ctx, 4}; | 1452 | IPC::ResponseBuilder rb{ctx, 4}; |
| 1453 | rb.Push(ResultSuccess); | 1453 | rb.Push(ResultSuccess); |
| 1454 | rb.PushRaw(vibration_device_info); | 1454 | rb.PushRaw(vibration_device_info); |
diff --git a/src/core/hle/service/hid/hidbus.cpp b/src/core/hle/service/hid/hidbus.cpp index fa6153b4c..e5e50845f 100644 --- a/src/core/hle/service/hid/hidbus.cpp +++ b/src/core/hle/service/hid/hidbus.cpp | |||
| @@ -50,12 +50,15 @@ HidBus::HidBus(Core::System& system_) | |||
| 50 | // Register update callbacks | 50 | // Register update callbacks |
| 51 | hidbus_update_event = Core::Timing::CreateEvent( | 51 | hidbus_update_event = Core::Timing::CreateEvent( |
| 52 | "Hidbus::UpdateCallback", | 52 | "Hidbus::UpdateCallback", |
| 53 | [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { | 53 | [this](std::uintptr_t user_data, s64 time, |
| 54 | std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { | ||
| 54 | const auto guard = LockService(); | 55 | const auto guard = LockService(); |
| 55 | UpdateHidbus(user_data, ns_late); | 56 | UpdateHidbus(user_data, ns_late); |
| 57 | return std::nullopt; | ||
| 56 | }); | 58 | }); |
| 57 | 59 | ||
| 58 | system_.CoreTiming().ScheduleEvent(hidbus_update_ns, hidbus_update_event); | 60 | system_.CoreTiming().ScheduleLoopingEvent(hidbus_update_ns, hidbus_update_ns, |
| 61 | hidbus_update_event); | ||
| 59 | } | 62 | } |
| 60 | 63 | ||
| 61 | HidBus::~HidBus() { | 64 | HidBus::~HidBus() { |
| @@ -63,8 +66,6 @@ HidBus::~HidBus() { | |||
| 63 | } | 66 | } |
| 64 | 67 | ||
| 65 | void HidBus::UpdateHidbus(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { | 68 | void HidBus::UpdateHidbus(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { |
| 66 | auto& core_timing = system.CoreTiming(); | ||
| 67 | |||
| 68 | if (is_hidbus_enabled) { | 69 | if (is_hidbus_enabled) { |
| 69 | for (std::size_t i = 0; i < devices.size(); ++i) { | 70 | for (std::size_t i = 0; i < devices.size(); ++i) { |
| 70 | if (!devices[i].is_device_initializated) { | 71 | if (!devices[i].is_device_initializated) { |
| @@ -82,13 +83,6 @@ void HidBus::UpdateHidbus(std::uintptr_t user_data, std::chrono::nanoseconds ns_ | |||
| 82 | sizeof(HidbusStatusManagerEntry)); | 83 | sizeof(HidbusStatusManagerEntry)); |
| 83 | } | 84 | } |
| 84 | } | 85 | } |
| 85 | |||
| 86 | // If ns_late is higher than the update rate ignore the delay | ||
| 87 | if (ns_late > hidbus_update_ns) { | ||
| 88 | ns_late = {}; | ||
| 89 | } | ||
| 90 | |||
| 91 | core_timing.ScheduleEvent(hidbus_update_ns - ns_late, hidbus_update_event); | ||
| 92 | } | 86 | } |
| 93 | 87 | ||
| 94 | std::optional<std::size_t> HidBus::GetDeviceIndexFromHandle(BusHandle handle) const { | 88 | std::optional<std::size_t> HidBus::GetDeviceIndexFromHandle(BusHandle handle) const { |
diff --git a/src/core/hle/service/nvflinger/binder.h b/src/core/hle/service/nvflinger/binder.h index 21aaa40cd..157333ff8 100644 --- a/src/core/hle/service/nvflinger/binder.h +++ b/src/core/hle/service/nvflinger/binder.h | |||
| @@ -34,6 +34,7 @@ enum class TransactionId { | |||
| 34 | 34 | ||
| 35 | class IBinder { | 35 | class IBinder { |
| 36 | public: | 36 | public: |
| 37 | virtual ~IBinder() = default; | ||
| 37 | virtual void Transact(Kernel::HLERequestContext& ctx, android::TransactionId code, | 38 | virtual void Transact(Kernel::HLERequestContext& ctx, android::TransactionId code, |
| 38 | u32 flags) = 0; | 39 | u32 flags) = 0; |
| 39 | virtual Kernel::KReadableEvent& GetNativeHandle() = 0; | 40 | virtual Kernel::KReadableEvent& GetNativeHandle() = 0; |
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp index 2b2985a2d..5f69c8c2c 100644 --- a/src/core/hle/service/nvflinger/nvflinger.cpp +++ b/src/core/hle/service/nvflinger/nvflinger.cpp | |||
| @@ -67,21 +67,20 @@ NVFlinger::NVFlinger(Core::System& system_, HosBinderDriverServer& hos_binder_dr | |||
| 67 | 67 | ||
| 68 | // Schedule the screen composition events | 68 | // Schedule the screen composition events |
| 69 | composition_event = Core::Timing::CreateEvent( | 69 | composition_event = Core::Timing::CreateEvent( |
| 70 | "ScreenComposition", [this](std::uintptr_t, std::chrono::nanoseconds ns_late) { | 70 | "ScreenComposition", |
| 71 | [this](std::uintptr_t, s64 time, | ||
| 72 | std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { | ||
| 71 | const auto lock_guard = Lock(); | 73 | const auto lock_guard = Lock(); |
| 72 | Compose(); | 74 | Compose(); |
| 73 | 75 | ||
| 74 | const auto ticks = std::chrono::nanoseconds{GetNextTicks()}; | 76 | return std::max(std::chrono::nanoseconds::zero(), |
| 75 | const auto ticks_delta = ticks - ns_late; | 77 | std::chrono::nanoseconds(GetNextTicks()) - ns_late); |
| 76 | const auto future_ns = std::max(std::chrono::nanoseconds::zero(), ticks_delta); | ||
| 77 | |||
| 78 | this->system.CoreTiming().ScheduleEvent(future_ns, composition_event); | ||
| 79 | }); | 78 | }); |
| 80 | 79 | ||
| 81 | if (system.IsMulticore()) { | 80 | if (system.IsMulticore()) { |
| 82 | vsync_thread = std::jthread([this](std::stop_token token) { SplitVSync(token); }); | 81 | vsync_thread = std::jthread([this](std::stop_token token) { SplitVSync(token); }); |
| 83 | } else { | 82 | } else { |
| 84 | system.CoreTiming().ScheduleEvent(frame_ns, composition_event); | 83 | system.CoreTiming().ScheduleLoopingEvent(frame_ns, frame_ns, composition_event); |
| 85 | } | 84 | } |
| 86 | } | 85 | } |
| 87 | 86 | ||
diff --git a/src/core/memory/cheat_engine.cpp b/src/core/memory/cheat_engine.cpp index 5f71f0ff5..ffdbacc18 100644 --- a/src/core/memory/cheat_engine.cpp +++ b/src/core/memory/cheat_engine.cpp | |||
| @@ -184,10 +184,12 @@ CheatEngine::~CheatEngine() { | |||
| 184 | void CheatEngine::Initialize() { | 184 | void CheatEngine::Initialize() { |
| 185 | event = Core::Timing::CreateEvent( | 185 | event = Core::Timing::CreateEvent( |
| 186 | "CheatEngine::FrameCallback::" + Common::HexToString(metadata.main_nso_build_id), | 186 | "CheatEngine::FrameCallback::" + Common::HexToString(metadata.main_nso_build_id), |
| 187 | [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { | 187 | [this](std::uintptr_t user_data, s64 time, |
| 188 | std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { | ||
| 188 | FrameCallback(user_data, ns_late); | 189 | FrameCallback(user_data, ns_late); |
| 190 | return std::nullopt; | ||
| 189 | }); | 191 | }); |
| 190 | core_timing.ScheduleEvent(CHEAT_ENGINE_NS, event); | 192 | core_timing.ScheduleLoopingEvent(CHEAT_ENGINE_NS, CHEAT_ENGINE_NS, event); |
| 191 | 193 | ||
| 192 | metadata.process_id = system.CurrentProcess()->GetProcessID(); | 194 | metadata.process_id = system.CurrentProcess()->GetProcessID(); |
| 193 | metadata.title_id = system.GetCurrentProcessProgramID(); | 195 | metadata.title_id = system.GetCurrentProcessProgramID(); |
| @@ -237,8 +239,6 @@ void CheatEngine::FrameCallback(std::uintptr_t, std::chrono::nanoseconds ns_late | |||
| 237 | MICROPROFILE_SCOPE(Cheat_Engine); | 239 | MICROPROFILE_SCOPE(Cheat_Engine); |
| 238 | 240 | ||
| 239 | vm.Execute(metadata); | 241 | vm.Execute(metadata); |
| 240 | |||
| 241 | core_timing.ScheduleEvent(CHEAT_ENGINE_NS - ns_late, event); | ||
| 242 | } | 242 | } |
| 243 | 243 | ||
| 244 | } // namespace Core::Memory | 244 | } // namespace Core::Memory |
diff --git a/src/core/tools/freezer.cpp b/src/core/tools/freezer.cpp index 5cc99fbe4..98ebbbf32 100644 --- a/src/core/tools/freezer.cpp +++ b/src/core/tools/freezer.cpp | |||
| @@ -53,8 +53,10 @@ Freezer::Freezer(Core::Timing::CoreTiming& core_timing_, Core::Memory::Memory& m | |||
| 53 | : core_timing{core_timing_}, memory{memory_} { | 53 | : core_timing{core_timing_}, memory{memory_} { |
| 54 | event = Core::Timing::CreateEvent( | 54 | event = Core::Timing::CreateEvent( |
| 55 | "MemoryFreezer::FrameCallback", | 55 | "MemoryFreezer::FrameCallback", |
| 56 | [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { | 56 | [this](std::uintptr_t user_data, s64 time, |
| 57 | std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { | ||
| 57 | FrameCallback(user_data, ns_late); | 58 | FrameCallback(user_data, ns_late); |
| 59 | return std::nullopt; | ||
| 58 | }); | 60 | }); |
| 59 | core_timing.ScheduleEvent(memory_freezer_ns, event); | 61 | core_timing.ScheduleEvent(memory_freezer_ns, event); |
| 60 | } | 62 | } |
diff --git a/src/input_common/drivers/sdl_driver.cpp b/src/input_common/drivers/sdl_driver.cpp index 446c027d3..00474ac77 100644 --- a/src/input_common/drivers/sdl_driver.cpp +++ b/src/input_common/drivers/sdl_driver.cpp | |||
| @@ -438,10 +438,17 @@ SDLDriver::SDLDriver(std::string input_engine_) : InputEngine(std::move(input_en | |||
| 438 | using namespace std::chrono_literals; | 438 | using namespace std::chrono_literals; |
| 439 | while (initialized) { | 439 | while (initialized) { |
| 440 | SDL_PumpEvents(); | 440 | SDL_PumpEvents(); |
| 441 | SendVibrations(); | ||
| 442 | std::this_thread::sleep_for(1ms); | 441 | std::this_thread::sleep_for(1ms); |
| 443 | } | 442 | } |
| 444 | }); | 443 | }); |
| 444 | vibration_thread = std::thread([this] { | ||
| 445 | Common::SetCurrentThreadName("yuzu:input:SDL_Vibration"); | ||
| 446 | using namespace std::chrono_literals; | ||
| 447 | while (initialized) { | ||
| 448 | SendVibrations(); | ||
| 449 | std::this_thread::sleep_for(10ms); | ||
| 450 | } | ||
| 451 | }); | ||
| 445 | } | 452 | } |
| 446 | // Because the events for joystick connection happens before we have our event watcher added, we | 453 | // Because the events for joystick connection happens before we have our event watcher added, we |
| 447 | // can just open all the joysticks right here | 454 | // can just open all the joysticks right here |
| @@ -457,6 +464,7 @@ SDLDriver::~SDLDriver() { | |||
| 457 | initialized = false; | 464 | initialized = false; |
| 458 | if (start_thread) { | 465 | if (start_thread) { |
| 459 | poll_thread.join(); | 466 | poll_thread.join(); |
| 467 | vibration_thread.join(); | ||
| 460 | SDL_QuitSubSystem(SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER); | 468 | SDL_QuitSubSystem(SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER); |
| 461 | } | 469 | } |
| 462 | } | 470 | } |
diff --git a/src/input_common/drivers/sdl_driver.h b/src/input_common/drivers/sdl_driver.h index 0846fbb50..7dc7a93c7 100644 --- a/src/input_common/drivers/sdl_driver.h +++ b/src/input_common/drivers/sdl_driver.h | |||
| @@ -128,5 +128,6 @@ private: | |||
| 128 | std::atomic<bool> initialized = false; | 128 | std::atomic<bool> initialized = false; |
| 129 | 129 | ||
| 130 | std::thread poll_thread; | 130 | std::thread poll_thread; |
| 131 | std::thread vibration_thread; | ||
| 131 | }; | 132 | }; |
| 132 | } // namespace InputCommon | 133 | } // namespace InputCommon |
diff --git a/src/tests/common/fibers.cpp b/src/tests/common/fibers.cpp index cfc84d423..4e29f9199 100644 --- a/src/tests/common/fibers.cpp +++ b/src/tests/common/fibers.cpp | |||
| @@ -43,7 +43,15 @@ class TestControl1 { | |||
| 43 | public: | 43 | public: |
| 44 | TestControl1() = default; | 44 | TestControl1() = default; |
| 45 | 45 | ||
| 46 | void DoWork(); | 46 | void DoWork() { |
| 47 | const u32 id = thread_ids.Get(); | ||
| 48 | u32 value = items[id]; | ||
| 49 | for (u32 i = 0; i < id; i++) { | ||
| 50 | value++; | ||
| 51 | } | ||
| 52 | results[id] = value; | ||
| 53 | Fiber::YieldTo(work_fibers[id], *thread_fibers[id]); | ||
| 54 | } | ||
| 47 | 55 | ||
| 48 | void ExecuteThread(u32 id); | 56 | void ExecuteThread(u32 id); |
| 49 | 57 | ||
| @@ -54,35 +62,16 @@ public: | |||
| 54 | std::vector<u32> results; | 62 | std::vector<u32> results; |
| 55 | }; | 63 | }; |
| 56 | 64 | ||
| 57 | static void WorkControl1(void* control) { | ||
| 58 | auto* test_control = static_cast<TestControl1*>(control); | ||
| 59 | test_control->DoWork(); | ||
| 60 | } | ||
| 61 | |||
| 62 | void TestControl1::DoWork() { | ||
| 63 | const u32 id = thread_ids.Get(); | ||
| 64 | u32 value = items[id]; | ||
| 65 | for (u32 i = 0; i < id; i++) { | ||
| 66 | value++; | ||
| 67 | } | ||
| 68 | results[id] = value; | ||
| 69 | Fiber::YieldTo(work_fibers[id], *thread_fibers[id]); | ||
| 70 | } | ||
| 71 | |||
| 72 | void TestControl1::ExecuteThread(u32 id) { | 65 | void TestControl1::ExecuteThread(u32 id) { |
| 73 | thread_ids.Register(id); | 66 | thread_ids.Register(id); |
| 74 | auto thread_fiber = Fiber::ThreadToFiber(); | 67 | auto thread_fiber = Fiber::ThreadToFiber(); |
| 75 | thread_fibers[id] = thread_fiber; | 68 | thread_fibers[id] = thread_fiber; |
| 76 | work_fibers[id] = std::make_shared<Fiber>(std::function<void(void*)>{WorkControl1}, this); | 69 | work_fibers[id] = std::make_shared<Fiber>([this] { DoWork(); }); |
| 77 | items[id] = rand() % 256; | 70 | items[id] = rand() % 256; |
| 78 | Fiber::YieldTo(thread_fibers[id], *work_fibers[id]); | 71 | Fiber::YieldTo(thread_fibers[id], *work_fibers[id]); |
| 79 | thread_fibers[id]->Exit(); | 72 | thread_fibers[id]->Exit(); |
| 80 | } | 73 | } |
| 81 | 74 | ||
| 82 | static void ThreadStart1(u32 id, TestControl1& test_control) { | ||
| 83 | test_control.ExecuteThread(id); | ||
| 84 | } | ||
| 85 | |||
| 86 | /** This test checks for fiber setup configuration and validates that fibers are | 75 | /** This test checks for fiber setup configuration and validates that fibers are |
| 87 | * doing all the work required. | 76 | * doing all the work required. |
| 88 | */ | 77 | */ |
| @@ -95,7 +84,7 @@ TEST_CASE("Fibers::Setup", "[common]") { | |||
| 95 | test_control.results.resize(num_threads, 0); | 84 | test_control.results.resize(num_threads, 0); |
| 96 | std::vector<std::thread> threads; | 85 | std::vector<std::thread> threads; |
| 97 | for (u32 i = 0; i < num_threads; i++) { | 86 | for (u32 i = 0; i < num_threads; i++) { |
| 98 | threads.emplace_back(ThreadStart1, i, std::ref(test_control)); | 87 | threads.emplace_back([&test_control, i] { test_control.ExecuteThread(i); }); |
| 99 | } | 88 | } |
| 100 | for (u32 i = 0; i < num_threads; i++) { | 89 | for (u32 i = 0; i < num_threads; i++) { |
| 101 | threads[i].join(); | 90 | threads[i].join(); |
| @@ -167,21 +156,6 @@ public: | |||
| 167 | std::shared_ptr<Common::Fiber> fiber3; | 156 | std::shared_ptr<Common::Fiber> fiber3; |
| 168 | }; | 157 | }; |
| 169 | 158 | ||
| 170 | static void WorkControl2_1(void* control) { | ||
| 171 | auto* test_control = static_cast<TestControl2*>(control); | ||
| 172 | test_control->DoWork1(); | ||
| 173 | } | ||
| 174 | |||
| 175 | static void WorkControl2_2(void* control) { | ||
| 176 | auto* test_control = static_cast<TestControl2*>(control); | ||
| 177 | test_control->DoWork2(); | ||
| 178 | } | ||
| 179 | |||
| 180 | static void WorkControl2_3(void* control) { | ||
| 181 | auto* test_control = static_cast<TestControl2*>(control); | ||
| 182 | test_control->DoWork3(); | ||
| 183 | } | ||
| 184 | |||
| 185 | void TestControl2::ExecuteThread(u32 id) { | 159 | void TestControl2::ExecuteThread(u32 id) { |
| 186 | thread_ids.Register(id); | 160 | thread_ids.Register(id); |
| 187 | auto thread_fiber = Fiber::ThreadToFiber(); | 161 | auto thread_fiber = Fiber::ThreadToFiber(); |
| @@ -193,18 +167,6 @@ void TestControl2::Exit() { | |||
| 193 | thread_fibers[id]->Exit(); | 167 | thread_fibers[id]->Exit(); |
| 194 | } | 168 | } |
| 195 | 169 | ||
| 196 | static void ThreadStart2_1(u32 id, TestControl2& test_control) { | ||
| 197 | test_control.ExecuteThread(id); | ||
| 198 | test_control.CallFiber1(); | ||
| 199 | test_control.Exit(); | ||
| 200 | } | ||
| 201 | |||
| 202 | static void ThreadStart2_2(u32 id, TestControl2& test_control) { | ||
| 203 | test_control.ExecuteThread(id); | ||
| 204 | test_control.CallFiber2(); | ||
| 205 | test_control.Exit(); | ||
| 206 | } | ||
| 207 | |||
| 208 | /** This test checks for fiber thread exchange configuration and validates that fibers are | 170 | /** This test checks for fiber thread exchange configuration and validates that fibers are |
| 209 | * that a fiber has been successfully transferred from one thread to another and that the TLS | 171 | * that a fiber has been successfully transferred from one thread to another and that the TLS |
| 210 | * region of the thread is kept while changing fibers. | 172 | * region of the thread is kept while changing fibers. |
| @@ -212,14 +174,19 @@ static void ThreadStart2_2(u32 id, TestControl2& test_control) { | |||
| 212 | TEST_CASE("Fibers::InterExchange", "[common]") { | 174 | TEST_CASE("Fibers::InterExchange", "[common]") { |
| 213 | TestControl2 test_control{}; | 175 | TestControl2 test_control{}; |
| 214 | test_control.thread_fibers.resize(2); | 176 | test_control.thread_fibers.resize(2); |
| 215 | test_control.fiber1 = | 177 | test_control.fiber1 = std::make_shared<Fiber>([&test_control] { test_control.DoWork1(); }); |
| 216 | std::make_shared<Fiber>(std::function<void(void*)>{WorkControl2_1}, &test_control); | 178 | test_control.fiber2 = std::make_shared<Fiber>([&test_control] { test_control.DoWork2(); }); |
| 217 | test_control.fiber2 = | 179 | test_control.fiber3 = std::make_shared<Fiber>([&test_control] { test_control.DoWork3(); }); |
| 218 | std::make_shared<Fiber>(std::function<void(void*)>{WorkControl2_2}, &test_control); | 180 | std::thread thread1{[&test_control] { |
| 219 | test_control.fiber3 = | 181 | test_control.ExecuteThread(0); |
| 220 | std::make_shared<Fiber>(std::function<void(void*)>{WorkControl2_3}, &test_control); | 182 | test_control.CallFiber1(); |
| 221 | std::thread thread1(ThreadStart2_1, 0, std::ref(test_control)); | 183 | test_control.Exit(); |
| 222 | std::thread thread2(ThreadStart2_2, 1, std::ref(test_control)); | 184 | }}; |
| 185 | std::thread thread2{[&test_control] { | ||
| 186 | test_control.ExecuteThread(1); | ||
| 187 | test_control.CallFiber2(); | ||
| 188 | test_control.Exit(); | ||
| 189 | }}; | ||
| 223 | thread1.join(); | 190 | thread1.join(); |
| 224 | thread2.join(); | 191 | thread2.join(); |
| 225 | REQUIRE(test_control.assert1); | 192 | REQUIRE(test_control.assert1); |
| @@ -270,16 +237,6 @@ public: | |||
| 270 | std::shared_ptr<Common::Fiber> fiber2; | 237 | std::shared_ptr<Common::Fiber> fiber2; |
| 271 | }; | 238 | }; |
| 272 | 239 | ||
| 273 | static void WorkControl3_1(void* control) { | ||
| 274 | auto* test_control = static_cast<TestControl3*>(control); | ||
| 275 | test_control->DoWork1(); | ||
| 276 | } | ||
| 277 | |||
| 278 | static void WorkControl3_2(void* control) { | ||
| 279 | auto* test_control = static_cast<TestControl3*>(control); | ||
| 280 | test_control->DoWork2(); | ||
| 281 | } | ||
| 282 | |||
| 283 | void TestControl3::ExecuteThread(u32 id) { | 240 | void TestControl3::ExecuteThread(u32 id) { |
| 284 | thread_ids.Register(id); | 241 | thread_ids.Register(id); |
| 285 | auto thread_fiber = Fiber::ThreadToFiber(); | 242 | auto thread_fiber = Fiber::ThreadToFiber(); |
| @@ -291,12 +248,6 @@ void TestControl3::Exit() { | |||
| 291 | thread_fibers[id]->Exit(); | 248 | thread_fibers[id]->Exit(); |
| 292 | } | 249 | } |
| 293 | 250 | ||
| 294 | static void ThreadStart3(u32 id, TestControl3& test_control) { | ||
| 295 | test_control.ExecuteThread(id); | ||
| 296 | test_control.CallFiber1(); | ||
| 297 | test_control.Exit(); | ||
| 298 | } | ||
| 299 | |||
| 300 | /** This test checks for one two threads racing for starting the same fiber. | 251 | /** This test checks for one two threads racing for starting the same fiber. |
| 301 | * It checks execution occurred in an ordered manner and by no time there were | 252 | * It checks execution occurred in an ordered manner and by no time there were |
| 302 | * two contexts at the same time. | 253 | * two contexts at the same time. |
| @@ -304,12 +255,15 @@ static void ThreadStart3(u32 id, TestControl3& test_control) { | |||
| 304 | TEST_CASE("Fibers::StartRace", "[common]") { | 255 | TEST_CASE("Fibers::StartRace", "[common]") { |
| 305 | TestControl3 test_control{}; | 256 | TestControl3 test_control{}; |
| 306 | test_control.thread_fibers.resize(2); | 257 | test_control.thread_fibers.resize(2); |
| 307 | test_control.fiber1 = | 258 | test_control.fiber1 = std::make_shared<Fiber>([&test_control] { test_control.DoWork1(); }); |
| 308 | std::make_shared<Fiber>(std::function<void(void*)>{WorkControl3_1}, &test_control); | 259 | test_control.fiber2 = std::make_shared<Fiber>([&test_control] { test_control.DoWork2(); }); |
| 309 | test_control.fiber2 = | 260 | const auto race_function{[&test_control](u32 id) { |
| 310 | std::make_shared<Fiber>(std::function<void(void*)>{WorkControl3_2}, &test_control); | 261 | test_control.ExecuteThread(id); |
| 311 | std::thread thread1(ThreadStart3, 0, std::ref(test_control)); | 262 | test_control.CallFiber1(); |
| 312 | std::thread thread2(ThreadStart3, 1, std::ref(test_control)); | 263 | test_control.Exit(); |
| 264 | }}; | ||
| 265 | std::thread thread1([&] { race_function(0); }); | ||
| 266 | std::thread thread2([&] { race_function(1); }); | ||
| 313 | thread1.join(); | 267 | thread1.join(); |
| 314 | thread2.join(); | 268 | thread2.join(); |
| 315 | REQUIRE(test_control.value1 == 1); | 269 | REQUIRE(test_control.value1 == 1); |
| @@ -319,12 +273,10 @@ TEST_CASE("Fibers::StartRace", "[common]") { | |||
| 319 | 273 | ||
| 320 | class TestControl4; | 274 | class TestControl4; |
| 321 | 275 | ||
| 322 | static void WorkControl4(void* control); | ||
| 323 | |||
| 324 | class TestControl4 { | 276 | class TestControl4 { |
| 325 | public: | 277 | public: |
| 326 | TestControl4() { | 278 | TestControl4() { |
| 327 | fiber1 = std::make_shared<Fiber>(std::function<void(void*)>{WorkControl4}, this); | 279 | fiber1 = std::make_shared<Fiber>([this] { DoWork(); }); |
| 328 | goal_reached = false; | 280 | goal_reached = false; |
| 329 | rewinded = false; | 281 | rewinded = false; |
| 330 | } | 282 | } |
| @@ -336,7 +288,7 @@ public: | |||
| 336 | } | 288 | } |
| 337 | 289 | ||
| 338 | void DoWork() { | 290 | void DoWork() { |
| 339 | fiber1->SetRewindPoint(std::function<void(void*)>{WorkControl4}, this); | 291 | fiber1->SetRewindPoint([this] { DoWork(); }); |
| 340 | if (rewinded) { | 292 | if (rewinded) { |
| 341 | goal_reached = true; | 293 | goal_reached = true; |
| 342 | Fiber::YieldTo(fiber1, *thread_fiber); | 294 | Fiber::YieldTo(fiber1, *thread_fiber); |
| @@ -351,11 +303,6 @@ public: | |||
| 351 | bool rewinded; | 303 | bool rewinded; |
| 352 | }; | 304 | }; |
| 353 | 305 | ||
| 354 | static void WorkControl4(void* control) { | ||
| 355 | auto* test_control = static_cast<TestControl4*>(control); | ||
| 356 | test_control->DoWork(); | ||
| 357 | } | ||
| 358 | |||
| 359 | TEST_CASE("Fibers::Rewind", "[common]") { | 306 | TEST_CASE("Fibers::Rewind", "[common]") { |
| 360 | TestControl4 test_control{}; | 307 | TestControl4 test_control{}; |
| 361 | test_control.Execute(); | 308 | test_control.Execute(); |
diff --git a/src/tests/core/core_timing.cpp b/src/tests/core/core_timing.cpp index 8358d36b5..894975e6f 100644 --- a/src/tests/core/core_timing.cpp +++ b/src/tests/core/core_timing.cpp | |||
| @@ -8,6 +8,8 @@ | |||
| 8 | #include <chrono> | 8 | #include <chrono> |
| 9 | #include <cstdlib> | 9 | #include <cstdlib> |
| 10 | #include <memory> | 10 | #include <memory> |
| 11 | #include <mutex> | ||
| 12 | #include <optional> | ||
| 11 | #include <string> | 13 | #include <string> |
| 12 | 14 | ||
| 13 | #include "core/core.h" | 15 | #include "core/core.h" |
| @@ -21,15 +23,18 @@ std::array<s64, 5> delays{}; | |||
| 21 | 23 | ||
| 22 | std::bitset<CB_IDS.size()> callbacks_ran_flags; | 24 | std::bitset<CB_IDS.size()> callbacks_ran_flags; |
| 23 | u64 expected_callback = 0; | 25 | u64 expected_callback = 0; |
| 26 | std::mutex control_mutex; | ||
| 24 | 27 | ||
| 25 | template <unsigned int IDX> | 28 | template <unsigned int IDX> |
| 26 | void HostCallbackTemplate(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { | 29 | std::optional<std::chrono::nanoseconds> HostCallbackTemplate(std::uintptr_t user_data, s64 time, |
| 30 | std::chrono::nanoseconds ns_late) { | ||
| 31 | std::unique_lock<std::mutex> lk(control_mutex); | ||
| 27 | static_assert(IDX < CB_IDS.size(), "IDX out of range"); | 32 | static_assert(IDX < CB_IDS.size(), "IDX out of range"); |
| 28 | callbacks_ran_flags.set(IDX); | 33 | callbacks_ran_flags.set(IDX); |
| 29 | REQUIRE(CB_IDS[IDX] == user_data); | 34 | REQUIRE(CB_IDS[IDX] == user_data); |
| 30 | REQUIRE(CB_IDS[IDX] == CB_IDS[calls_order[expected_callback]]); | ||
| 31 | delays[IDX] = ns_late.count(); | 35 | delays[IDX] = ns_late.count(); |
| 32 | ++expected_callback; | 36 | ++expected_callback; |
| 37 | return std::nullopt; | ||
| 33 | } | 38 | } |
| 34 | 39 | ||
| 35 | struct ScopeInit final { | 40 | struct ScopeInit final { |
diff --git a/src/video_core/compatible_formats.cpp b/src/video_core/compatible_formats.cpp index 014d880e2..4e75f33ca 100644 --- a/src/video_core/compatible_formats.cpp +++ b/src/video_core/compatible_formats.cpp | |||
| @@ -131,9 +131,12 @@ constexpr std::array VIEW_CLASS_ASTC_8x8_RGBA{ | |||
| 131 | // PixelFormat::ASTC_2D_10X5_SRGB | 131 | // PixelFormat::ASTC_2D_10X5_SRGB |
| 132 | 132 | ||
| 133 | // Missing formats: | 133 | // Missing formats: |
| 134 | // PixelFormat::ASTC_2D_10X6_UNORM | ||
| 135 | // PixelFormat::ASTC_2D_10X6_SRGB | 134 | // PixelFormat::ASTC_2D_10X6_SRGB |
| 136 | 135 | ||
| 136 | constexpr std::array VIEW_CLASS_ASTC_10x6_RGBA{ | ||
| 137 | PixelFormat::ASTC_2D_10X6_UNORM, | ||
| 138 | }; | ||
| 139 | |||
| 137 | constexpr std::array VIEW_CLASS_ASTC_10x8_RGBA{ | 140 | constexpr std::array VIEW_CLASS_ASTC_10x8_RGBA{ |
| 138 | PixelFormat::ASTC_2D_10X8_UNORM, | 141 | PixelFormat::ASTC_2D_10X8_UNORM, |
| 139 | PixelFormat::ASTC_2D_10X8_SRGB, | 142 | PixelFormat::ASTC_2D_10X8_SRGB, |
| @@ -226,6 +229,7 @@ constexpr Table MakeViewTable() { | |||
| 226 | EnableRange(view, VIEW_CLASS_ASTC_6x6_RGBA); | 229 | EnableRange(view, VIEW_CLASS_ASTC_6x6_RGBA); |
| 227 | EnableRange(view, VIEW_CLASS_ASTC_8x5_RGBA); | 230 | EnableRange(view, VIEW_CLASS_ASTC_8x5_RGBA); |
| 228 | EnableRange(view, VIEW_CLASS_ASTC_8x8_RGBA); | 231 | EnableRange(view, VIEW_CLASS_ASTC_8x8_RGBA); |
| 232 | EnableRange(view, VIEW_CLASS_ASTC_10x6_RGBA); | ||
| 229 | EnableRange(view, VIEW_CLASS_ASTC_10x8_RGBA); | 233 | EnableRange(view, VIEW_CLASS_ASTC_10x8_RGBA); |
| 230 | EnableRange(view, VIEW_CLASS_ASTC_10x10_RGBA); | 234 | EnableRange(view, VIEW_CLASS_ASTC_10x10_RGBA); |
| 231 | EnableRange(view, VIEW_CLASS_ASTC_12x12_RGBA); | 235 | EnableRange(view, VIEW_CLASS_ASTC_12x12_RGBA); |
diff --git a/src/video_core/renderer_opengl/maxwell_to_gl.h b/src/video_core/renderer_opengl/maxwell_to_gl.h index 644b60d73..9a72d0d6d 100644 --- a/src/video_core/renderer_opengl/maxwell_to_gl.h +++ b/src/video_core/renderer_opengl/maxwell_to_gl.h | |||
| @@ -98,6 +98,7 @@ constexpr std::array<FormatTuple, VideoCore::Surface::MaxPixelFormat> FORMAT_TAB | |||
| 98 | {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR}, // ASTC_2D_10X8_SRGB | 98 | {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR}, // ASTC_2D_10X8_SRGB |
| 99 | {GL_COMPRESSED_RGBA_ASTC_6x6_KHR}, // ASTC_2D_6X6_UNORM | 99 | {GL_COMPRESSED_RGBA_ASTC_6x6_KHR}, // ASTC_2D_6X6_UNORM |
| 100 | {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR}, // ASTC_2D_6X6_SRGB | 100 | {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR}, // ASTC_2D_6X6_SRGB |
| 101 | {GL_COMPRESSED_RGBA_ASTC_10x6_KHR}, // ASTC_2D_10X6_UNORM | ||
| 101 | {GL_COMPRESSED_RGBA_ASTC_10x10_KHR}, // ASTC_2D_10X10_UNORM | 102 | {GL_COMPRESSED_RGBA_ASTC_10x10_KHR}, // ASTC_2D_10X10_UNORM |
| 102 | {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR}, // ASTC_2D_10X10_SRGB | 103 | {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR}, // ASTC_2D_10X10_SRGB |
| 103 | {GL_COMPRESSED_RGBA_ASTC_12x12_KHR}, // ASTC_2D_12X12_UNORM | 104 | {GL_COMPRESSED_RGBA_ASTC_12x12_KHR}, // ASTC_2D_12X12_UNORM |
diff --git a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp index 193cbe15e..689164a6a 100644 --- a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp +++ b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp | |||
| @@ -195,6 +195,7 @@ struct FormatTuple { | |||
| 195 | {VK_FORMAT_ASTC_10x8_SRGB_BLOCK}, // ASTC_2D_10X8_SRGB | 195 | {VK_FORMAT_ASTC_10x8_SRGB_BLOCK}, // ASTC_2D_10X8_SRGB |
| 196 | {VK_FORMAT_ASTC_6x6_UNORM_BLOCK}, // ASTC_2D_6X6_UNORM | 196 | {VK_FORMAT_ASTC_6x6_UNORM_BLOCK}, // ASTC_2D_6X6_UNORM |
| 197 | {VK_FORMAT_ASTC_6x6_SRGB_BLOCK}, // ASTC_2D_6X6_SRGB | 197 | {VK_FORMAT_ASTC_6x6_SRGB_BLOCK}, // ASTC_2D_6X6_SRGB |
| 198 | {VK_FORMAT_ASTC_10x6_UNORM_BLOCK}, // ASTC_2D_10X6_UNORM | ||
| 198 | {VK_FORMAT_ASTC_10x10_UNORM_BLOCK}, // ASTC_2D_10X10_UNORM | 199 | {VK_FORMAT_ASTC_10x10_UNORM_BLOCK}, // ASTC_2D_10X10_UNORM |
| 199 | {VK_FORMAT_ASTC_10x10_SRGB_BLOCK}, // ASTC_2D_10X10_SRGB | 200 | {VK_FORMAT_ASTC_10x10_SRGB_BLOCK}, // ASTC_2D_10X10_SRGB |
| 200 | {VK_FORMAT_ASTC_12x12_UNORM_BLOCK}, // ASTC_2D_12X12_UNORM | 201 | {VK_FORMAT_ASTC_12x12_UNORM_BLOCK}, // ASTC_2D_12X12_UNORM |
diff --git a/src/video_core/surface.cpp b/src/video_core/surface.cpp index 69c1b1e6d..eecd0deff 100644 --- a/src/video_core/surface.cpp +++ b/src/video_core/surface.cpp | |||
| @@ -247,6 +247,7 @@ bool IsPixelFormatASTC(PixelFormat format) { | |||
| 247 | case PixelFormat::ASTC_2D_10X8_SRGB: | 247 | case PixelFormat::ASTC_2D_10X8_SRGB: |
| 248 | case PixelFormat::ASTC_2D_6X6_UNORM: | 248 | case PixelFormat::ASTC_2D_6X6_UNORM: |
| 249 | case PixelFormat::ASTC_2D_6X6_SRGB: | 249 | case PixelFormat::ASTC_2D_6X6_SRGB: |
| 250 | case PixelFormat::ASTC_2D_10X6_UNORM: | ||
| 250 | case PixelFormat::ASTC_2D_10X10_UNORM: | 251 | case PixelFormat::ASTC_2D_10X10_UNORM: |
| 251 | case PixelFormat::ASTC_2D_10X10_SRGB: | 252 | case PixelFormat::ASTC_2D_10X10_SRGB: |
| 252 | case PixelFormat::ASTC_2D_12X12_UNORM: | 253 | case PixelFormat::ASTC_2D_12X12_UNORM: |
diff --git a/src/video_core/surface.h b/src/video_core/surface.h index 75e055592..0175432ff 100644 --- a/src/video_core/surface.h +++ b/src/video_core/surface.h | |||
| @@ -94,6 +94,7 @@ enum class PixelFormat { | |||
| 94 | ASTC_2D_10X8_SRGB, | 94 | ASTC_2D_10X8_SRGB, |
| 95 | ASTC_2D_6X6_UNORM, | 95 | ASTC_2D_6X6_UNORM, |
| 96 | ASTC_2D_6X6_SRGB, | 96 | ASTC_2D_6X6_SRGB, |
| 97 | ASTC_2D_10X6_UNORM, | ||
| 97 | ASTC_2D_10X10_UNORM, | 98 | ASTC_2D_10X10_UNORM, |
| 98 | ASTC_2D_10X10_SRGB, | 99 | ASTC_2D_10X10_SRGB, |
| 99 | ASTC_2D_12X12_UNORM, | 100 | ASTC_2D_12X12_UNORM, |
| @@ -227,6 +228,7 @@ constexpr std::array<u8, MaxPixelFormat> BLOCK_WIDTH_TABLE = {{ | |||
| 227 | 10, // ASTC_2D_10X8_SRGB | 228 | 10, // ASTC_2D_10X8_SRGB |
| 228 | 6, // ASTC_2D_6X6_UNORM | 229 | 6, // ASTC_2D_6X6_UNORM |
| 229 | 6, // ASTC_2D_6X6_SRGB | 230 | 6, // ASTC_2D_6X6_SRGB |
| 231 | 10, // ASTC_2D_10X6_UNORM | ||
| 230 | 10, // ASTC_2D_10X10_UNORM | 232 | 10, // ASTC_2D_10X10_UNORM |
| 231 | 10, // ASTC_2D_10X10_SRGB | 233 | 10, // ASTC_2D_10X10_SRGB |
| 232 | 12, // ASTC_2D_12X12_UNORM | 234 | 12, // ASTC_2D_12X12_UNORM |
| @@ -329,6 +331,7 @@ constexpr std::array<u8, MaxPixelFormat> BLOCK_HEIGHT_TABLE = {{ | |||
| 329 | 8, // ASTC_2D_10X8_SRGB | 331 | 8, // ASTC_2D_10X8_SRGB |
| 330 | 6, // ASTC_2D_6X6_UNORM | 332 | 6, // ASTC_2D_6X6_UNORM |
| 331 | 6, // ASTC_2D_6X6_SRGB | 333 | 6, // ASTC_2D_6X6_SRGB |
| 334 | 6, // ASTC_2D_10X6_UNORM | ||
| 332 | 10, // ASTC_2D_10X10_UNORM | 335 | 10, // ASTC_2D_10X10_UNORM |
| 333 | 10, // ASTC_2D_10X10_SRGB | 336 | 10, // ASTC_2D_10X10_SRGB |
| 334 | 12, // ASTC_2D_12X12_UNORM | 337 | 12, // ASTC_2D_12X12_UNORM |
| @@ -431,6 +434,7 @@ constexpr std::array<u8, MaxPixelFormat> BITS_PER_BLOCK_TABLE = {{ | |||
| 431 | 128, // ASTC_2D_10X8_SRGB | 434 | 128, // ASTC_2D_10X8_SRGB |
| 432 | 128, // ASTC_2D_6X6_UNORM | 435 | 128, // ASTC_2D_6X6_UNORM |
| 433 | 128, // ASTC_2D_6X6_SRGB | 436 | 128, // ASTC_2D_6X6_SRGB |
| 437 | 128, // ASTC_2D_10X6_UNORM | ||
| 434 | 128, // ASTC_2D_10X10_UNORM | 438 | 128, // ASTC_2D_10X10_UNORM |
| 435 | 128, // ASTC_2D_10X10_SRGB | 439 | 128, // ASTC_2D_10X10_SRGB |
| 436 | 128, // ASTC_2D_12X12_UNORM | 440 | 128, // ASTC_2D_12X12_UNORM |
diff --git a/src/video_core/texture_cache/format_lookup_table.cpp b/src/video_core/texture_cache/format_lookup_table.cpp index 0937768d6..1412aa076 100644 --- a/src/video_core/texture_cache/format_lookup_table.cpp +++ b/src/video_core/texture_cache/format_lookup_table.cpp | |||
| @@ -206,6 +206,8 @@ PixelFormat PixelFormatFromTextureInfo(TextureFormat format, ComponentType red, | |||
| 206 | return PixelFormat::ASTC_2D_6X6_UNORM; | 206 | return PixelFormat::ASTC_2D_6X6_UNORM; |
| 207 | case Hash(TextureFormat::ASTC_2D_6X6, UNORM, SRGB): | 207 | case Hash(TextureFormat::ASTC_2D_6X6, UNORM, SRGB): |
| 208 | return PixelFormat::ASTC_2D_6X6_SRGB; | 208 | return PixelFormat::ASTC_2D_6X6_SRGB; |
| 209 | case Hash(TextureFormat::ASTC_2D_10X6, UNORM, LINEAR): | ||
| 210 | return PixelFormat::ASTC_2D_10X6_UNORM; | ||
| 209 | case Hash(TextureFormat::ASTC_2D_10X10, UNORM, LINEAR): | 211 | case Hash(TextureFormat::ASTC_2D_10X10, UNORM, LINEAR): |
| 210 | return PixelFormat::ASTC_2D_10X10_UNORM; | 212 | return PixelFormat::ASTC_2D_10X10_UNORM; |
| 211 | case Hash(TextureFormat::ASTC_2D_10X10, UNORM, SRGB): | 213 | case Hash(TextureFormat::ASTC_2D_10X10, UNORM, SRGB): |
diff --git a/src/video_core/texture_cache/formatter.h b/src/video_core/texture_cache/formatter.h index 1b78ed445..95a572604 100644 --- a/src/video_core/texture_cache/formatter.h +++ b/src/video_core/texture_cache/formatter.h | |||
| @@ -175,6 +175,8 @@ struct fmt::formatter<VideoCore::Surface::PixelFormat> : fmt::formatter<fmt::str | |||
| 175 | return "ASTC_2D_6X6_UNORM"; | 175 | return "ASTC_2D_6X6_UNORM"; |
| 176 | case PixelFormat::ASTC_2D_6X6_SRGB: | 176 | case PixelFormat::ASTC_2D_6X6_SRGB: |
| 177 | return "ASTC_2D_6X6_SRGB"; | 177 | return "ASTC_2D_6X6_SRGB"; |
| 178 | case PixelFormat::ASTC_2D_10X6_UNORM: | ||
| 179 | return "ASTC_2D_10X6_UNORM"; | ||
| 178 | case PixelFormat::ASTC_2D_10X10_UNORM: | 180 | case PixelFormat::ASTC_2D_10X10_UNORM: |
| 179 | return "ASTC_2D_10X10_UNORM"; | 181 | return "ASTC_2D_10X10_UNORM"; |
| 180 | case PixelFormat::ASTC_2D_10X10_SRGB: | 182 | case PixelFormat::ASTC_2D_10X10_SRGB: |
diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index 11ce865a7..743ac09f6 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp | |||
| @@ -669,17 +669,6 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR | |||
| 669 | const bool is_amd = | 669 | const bool is_amd = |
| 670 | driver_id == VK_DRIVER_ID_AMD_PROPRIETARY || driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE; | 670 | driver_id == VK_DRIVER_ID_AMD_PROPRIETARY || driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE; |
| 671 | if (is_amd) { | 671 | if (is_amd) { |
| 672 | // TODO(lat9nq): Add an upper bound when AMD fixes their VK_KHR_push_descriptor | ||
| 673 | const bool has_broken_push_descriptor = VK_VERSION_MAJOR(properties.driverVersion) == 2 && | ||
| 674 | VK_VERSION_MINOR(properties.driverVersion) == 0 && | ||
| 675 | VK_VERSION_PATCH(properties.driverVersion) >= 226; | ||
| 676 | if (khr_push_descriptor && has_broken_push_descriptor) { | ||
| 677 | LOG_WARNING( | ||
| 678 | Render_Vulkan, | ||
| 679 | "Disabling AMD driver 2.0.226 and later from broken VK_KHR_push_descriptor"); | ||
| 680 | khr_push_descriptor = false; | ||
| 681 | } | ||
| 682 | |||
| 683 | // AMD drivers need a higher amount of Sets per Pool in certain circunstances like in XC2. | 672 | // AMD drivers need a higher amount of Sets per Pool in certain circunstances like in XC2. |
| 684 | sets_per_pool = 96; | 673 | sets_per_pool = 96; |
| 685 | // Disable VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT on AMD GCN4 and lower as it is broken. | 674 | // Disable VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT on AMD GCN4 and lower as it is broken. |
diff --git a/src/yuzu/applets/qt_web_browser.cpp b/src/yuzu/applets/qt_web_browser.cpp index 283c04cd5..89bd482e0 100644 --- a/src/yuzu/applets/qt_web_browser.cpp +++ b/src/yuzu/applets/qt_web_browser.cpp | |||
| @@ -2,6 +2,8 @@ | |||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #ifdef YUZU_USE_QT_WEB_ENGINE | 4 | #ifdef YUZU_USE_QT_WEB_ENGINE |
| 5 | #include <bit> | ||
| 6 | |||
| 5 | #include <QApplication> | 7 | #include <QApplication> |
| 6 | #include <QKeyEvent> | 8 | #include <QKeyEvent> |
| 7 | 9 | ||
| @@ -52,8 +54,8 @@ QtNXWebEngineView::QtNXWebEngineView(QWidget* parent, Core::System& system, | |||
| 52 | : QWebEngineView(parent), input_subsystem{input_subsystem_}, | 54 | : QWebEngineView(parent), input_subsystem{input_subsystem_}, |
| 53 | url_interceptor(std::make_unique<UrlRequestInterceptor>()), | 55 | url_interceptor(std::make_unique<UrlRequestInterceptor>()), |
| 54 | input_interpreter(std::make_unique<InputInterpreter>(system)), | 56 | input_interpreter(std::make_unique<InputInterpreter>(system)), |
| 55 | default_profile{QWebEngineProfile::defaultProfile()}, | 57 | default_profile{QWebEngineProfile::defaultProfile()}, global_settings{ |
| 56 | global_settings{QWebEngineSettings::globalSettings()} { | 58 | default_profile->settings()} { |
| 57 | default_profile->setPersistentStoragePath(QString::fromStdString(Common::FS::PathToUTF8String( | 59 | default_profile->setPersistentStoragePath(QString::fromStdString(Common::FS::PathToUTF8String( |
| 58 | Common::FS::GetYuzuPath(Common::FS::YuzuPath::YuzuDir) / "qtwebengine"))); | 60 | Common::FS::GetYuzuPath(Common::FS::YuzuPath::YuzuDir) / "qtwebengine"))); |
| 59 | 61 | ||
| @@ -78,7 +80,7 @@ QtNXWebEngineView::QtNXWebEngineView(QWidget* parent, Core::System& system, | |||
| 78 | default_profile->scripts()->insert(gamepad); | 80 | default_profile->scripts()->insert(gamepad); |
| 79 | default_profile->scripts()->insert(window_nx); | 81 | default_profile->scripts()->insert(window_nx); |
| 80 | 82 | ||
| 81 | default_profile->setRequestInterceptor(url_interceptor.get()); | 83 | default_profile->setUrlRequestInterceptor(url_interceptor.get()); |
| 82 | 84 | ||
| 83 | global_settings->setAttribute(QWebEngineSettings::LocalContentCanAccessRemoteUrls, true); | 85 | global_settings->setAttribute(QWebEngineSettings::LocalContentCanAccessRemoteUrls, true); |
| 84 | global_settings->setAttribute(QWebEngineSettings::FullScreenSupportEnabled, true); | 86 | global_settings->setAttribute(QWebEngineSettings::FullScreenSupportEnabled, true); |
| @@ -211,8 +213,10 @@ template <Core::HID::NpadButton... T> | |||
| 211 | void QtNXWebEngineView::HandleWindowFooterButtonPressedOnce() { | 213 | void QtNXWebEngineView::HandleWindowFooterButtonPressedOnce() { |
| 212 | const auto f = [this](Core::HID::NpadButton button) { | 214 | const auto f = [this](Core::HID::NpadButton button) { |
| 213 | if (input_interpreter->IsButtonPressedOnce(button)) { | 215 | if (input_interpreter->IsButtonPressedOnce(button)) { |
| 216 | const auto button_index = std::countr_zero(static_cast<u64>(button)); | ||
| 217 | |||
| 214 | page()->runJavaScript( | 218 | page()->runJavaScript( |
| 215 | QStringLiteral("yuzu_key_callbacks[%1] == null;").arg(static_cast<u8>(button)), | 219 | QStringLiteral("yuzu_key_callbacks[%1] == null;").arg(button_index), |
| 216 | [this, button](const QVariant& variant) { | 220 | [this, button](const QVariant& variant) { |
| 217 | if (variant.toBool()) { | 221 | if (variant.toBool()) { |
| 218 | switch (button) { | 222 | switch (button) { |
| @@ -236,7 +240,7 @@ void QtNXWebEngineView::HandleWindowFooterButtonPressedOnce() { | |||
| 236 | 240 | ||
| 237 | page()->runJavaScript( | 241 | page()->runJavaScript( |
| 238 | QStringLiteral("if (yuzu_key_callbacks[%1] != null) { yuzu_key_callbacks[%1](); }") | 242 | QStringLiteral("if (yuzu_key_callbacks[%1] != null) { yuzu_key_callbacks[%1](); }") |
| 239 | .arg(static_cast<u8>(button))); | 243 | .arg(button_index)); |
| 240 | } | 244 | } |
| 241 | }; | 245 | }; |
| 242 | 246 | ||
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 9df4752be..9686412d0 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp | |||
| @@ -133,7 +133,7 @@ void Config::Initialize(const std::string& config_name) { | |||
| 133 | // Explicit std::string definition: Qt can't implicitly convert a std::string to a QVariant, nor | 133 | // Explicit std::string definition: Qt can't implicitly convert a std::string to a QVariant, nor |
| 134 | // can it implicitly convert a QVariant back to a {std::,Q}string | 134 | // can it implicitly convert a QVariant back to a {std::,Q}string |
| 135 | template <> | 135 | template <> |
| 136 | void Config::ReadBasicSetting(Settings::BasicSetting<std::string>& setting) { | 136 | void Config::ReadBasicSetting(Settings::Setting<std::string>& setting) { |
| 137 | const QString name = QString::fromStdString(setting.GetLabel()); | 137 | const QString name = QString::fromStdString(setting.GetLabel()); |
| 138 | const auto default_value = QString::fromStdString(setting.GetDefault()); | 138 | const auto default_value = QString::fromStdString(setting.GetDefault()); |
| 139 | if (qt_config->value(name + QStringLiteral("/default"), false).toBool()) { | 139 | if (qt_config->value(name + QStringLiteral("/default"), false).toBool()) { |
| @@ -144,7 +144,7 @@ void Config::ReadBasicSetting(Settings::BasicSetting<std::string>& setting) { | |||
| 144 | } | 144 | } |
| 145 | 145 | ||
| 146 | template <typename Type> | 146 | template <typename Type> |
| 147 | void Config::ReadBasicSetting(Settings::BasicSetting<Type>& setting) { | 147 | void Config::ReadBasicSetting(Settings::Setting<Type>& setting) { |
| 148 | const QString name = QString::fromStdString(setting.GetLabel()); | 148 | const QString name = QString::fromStdString(setting.GetLabel()); |
| 149 | const Type default_value = setting.GetDefault(); | 149 | const Type default_value = setting.GetDefault(); |
| 150 | if (qt_config->value(name + QStringLiteral("/default"), false).toBool()) { | 150 | if (qt_config->value(name + QStringLiteral("/default"), false).toBool()) { |
| @@ -157,7 +157,7 @@ void Config::ReadBasicSetting(Settings::BasicSetting<Type>& setting) { | |||
| 157 | 157 | ||
| 158 | // Explicit std::string definition: Qt can't implicitly convert a std::string to a QVariant | 158 | // Explicit std::string definition: Qt can't implicitly convert a std::string to a QVariant |
| 159 | template <> | 159 | template <> |
| 160 | void Config::WriteBasicSetting(const Settings::BasicSetting<std::string>& setting) { | 160 | void Config::WriteBasicSetting(const Settings::Setting<std::string>& setting) { |
| 161 | const QString name = QString::fromStdString(setting.GetLabel()); | 161 | const QString name = QString::fromStdString(setting.GetLabel()); |
| 162 | const std::string& value = setting.GetValue(); | 162 | const std::string& value = setting.GetValue(); |
| 163 | qt_config->setValue(name + QStringLiteral("/default"), value == setting.GetDefault()); | 163 | qt_config->setValue(name + QStringLiteral("/default"), value == setting.GetDefault()); |
| @@ -165,7 +165,7 @@ void Config::WriteBasicSetting(const Settings::BasicSetting<std::string>& settin | |||
| 165 | } | 165 | } |
| 166 | 166 | ||
| 167 | template <typename Type> | 167 | template <typename Type> |
| 168 | void Config::WriteBasicSetting(const Settings::BasicSetting<Type>& setting) { | 168 | void Config::WriteBasicSetting(const Settings::Setting<Type>& setting) { |
| 169 | const QString name = QString::fromStdString(setting.GetLabel()); | 169 | const QString name = QString::fromStdString(setting.GetLabel()); |
| 170 | const Type value = setting.GetValue(); | 170 | const Type value = setting.GetValue(); |
| 171 | qt_config->setValue(name + QStringLiteral("/default"), value == setting.GetDefault()); | 171 | qt_config->setValue(name + QStringLiteral("/default"), value == setting.GetDefault()); |
| @@ -173,7 +173,7 @@ void Config::WriteBasicSetting(const Settings::BasicSetting<Type>& setting) { | |||
| 173 | } | 173 | } |
| 174 | 174 | ||
| 175 | template <typename Type> | 175 | template <typename Type> |
| 176 | void Config::WriteGlobalSetting(const Settings::Setting<Type>& setting) { | 176 | void Config::WriteGlobalSetting(const Settings::SwitchableSetting<Type>& setting) { |
| 177 | const QString name = QString::fromStdString(setting.GetLabel()); | 177 | const QString name = QString::fromStdString(setting.GetLabel()); |
| 178 | const Type& value = setting.GetValue(global); | 178 | const Type& value = setting.GetValue(global); |
| 179 | if (!global) { | 179 | if (!global) { |
| @@ -1422,7 +1422,7 @@ QVariant Config::ReadSetting(const QString& name, const QVariant& default_value) | |||
| 1422 | } | 1422 | } |
| 1423 | 1423 | ||
| 1424 | template <typename Type> | 1424 | template <typename Type> |
| 1425 | void Config::ReadGlobalSetting(Settings::Setting<Type>& setting) { | 1425 | void Config::ReadGlobalSetting(Settings::SwitchableSetting<Type>& setting) { |
| 1426 | QString name = QString::fromStdString(setting.GetLabel()); | 1426 | QString name = QString::fromStdString(setting.GetLabel()); |
| 1427 | const bool use_global = qt_config->value(name + QStringLiteral("/use_global"), true).toBool(); | 1427 | const bool use_global = qt_config->value(name + QStringLiteral("/use_global"), true).toBool(); |
| 1428 | setting.SetGlobal(use_global); | 1428 | setting.SetGlobal(use_global); |
diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h index f0ab6bdaa..9ca878d23 100644 --- a/src/yuzu/configuration/config.h +++ b/src/yuzu/configuration/config.h | |||
| @@ -160,7 +160,7 @@ private: | |||
| 160 | * @param The setting | 160 | * @param The setting |
| 161 | */ | 161 | */ |
| 162 | template <typename Type> | 162 | template <typename Type> |
| 163 | void ReadGlobalSetting(Settings::Setting<Type>& setting); | 163 | void ReadGlobalSetting(Settings::SwitchableSetting<Type>& setting); |
| 164 | 164 | ||
| 165 | /** | 165 | /** |
| 166 | * Sets a value to the qt_config using the setting's label and default value. If the config is a | 166 | * Sets a value to the qt_config using the setting's label and default value. If the config is a |
| @@ -169,7 +169,7 @@ private: | |||
| 169 | * @param The setting | 169 | * @param The setting |
| 170 | */ | 170 | */ |
| 171 | template <typename Type> | 171 | template <typename Type> |
| 172 | void WriteGlobalSetting(const Settings::Setting<Type>& setting); | 172 | void WriteGlobalSetting(const Settings::SwitchableSetting<Type>& setting); |
| 173 | 173 | ||
| 174 | /** | 174 | /** |
| 175 | * Reads a value from the qt_config using the setting's label and default value and applies the | 175 | * Reads a value from the qt_config using the setting's label and default value and applies the |
| @@ -178,14 +178,14 @@ private: | |||
| 178 | * @param The setting | 178 | * @param The setting |
| 179 | */ | 179 | */ |
| 180 | template <typename Type> | 180 | template <typename Type> |
| 181 | void ReadBasicSetting(Settings::BasicSetting<Type>& setting); | 181 | void ReadBasicSetting(Settings::Setting<Type>& setting); |
| 182 | 182 | ||
| 183 | /** Sets a value from the setting in the qt_config using the setting's label and default value. | 183 | /** Sets a value from the setting in the qt_config using the setting's label and default value. |
| 184 | * | 184 | * |
| 185 | * @param The setting | 185 | * @param The setting |
| 186 | */ | 186 | */ |
| 187 | template <typename Type> | 187 | template <typename Type> |
| 188 | void WriteBasicSetting(const Settings::BasicSetting<Type>& setting); | 188 | void WriteBasicSetting(const Settings::Setting<Type>& setting); |
| 189 | 189 | ||
| 190 | ConfigType type; | 190 | ConfigType type; |
| 191 | std::unique_ptr<QSettings> qt_config; | 191 | std::unique_ptr<QSettings> qt_config; |
diff --git a/src/yuzu/configuration/configuration_shared.cpp b/src/yuzu/configuration/configuration_shared.cpp index 5190bd18b..dd4959417 100644 --- a/src/yuzu/configuration/configuration_shared.cpp +++ b/src/yuzu/configuration/configuration_shared.cpp | |||
| @@ -9,7 +9,7 @@ | |||
| 9 | #include "yuzu/configuration/configuration_shared.h" | 9 | #include "yuzu/configuration/configuration_shared.h" |
| 10 | #include "yuzu/configuration/configure_per_game.h" | 10 | #include "yuzu/configuration/configure_per_game.h" |
| 11 | 11 | ||
| 12 | void ConfigurationShared::ApplyPerGameSetting(Settings::Setting<bool>* setting, | 12 | void ConfigurationShared::ApplyPerGameSetting(Settings::SwitchableSetting<bool>* setting, |
| 13 | const QCheckBox* checkbox, | 13 | const QCheckBox* checkbox, |
| 14 | const CheckState& tracker) { | 14 | const CheckState& tracker) { |
| 15 | if (Settings::IsConfiguringGlobal() && setting->UsingGlobal()) { | 15 | if (Settings::IsConfiguringGlobal() && setting->UsingGlobal()) { |
| @@ -25,7 +25,7 @@ void ConfigurationShared::ApplyPerGameSetting(Settings::Setting<bool>* setting, | |||
| 25 | } | 25 | } |
| 26 | 26 | ||
| 27 | void ConfigurationShared::SetPerGameSetting(QCheckBox* checkbox, | 27 | void ConfigurationShared::SetPerGameSetting(QCheckBox* checkbox, |
| 28 | const Settings::Setting<bool>* setting) { | 28 | const Settings::SwitchableSetting<bool>* setting) { |
| 29 | if (setting->UsingGlobal()) { | 29 | if (setting->UsingGlobal()) { |
| 30 | checkbox->setCheckState(Qt::PartiallyChecked); | 30 | checkbox->setCheckState(Qt::PartiallyChecked); |
| 31 | } else { | 31 | } else { |
| @@ -45,7 +45,7 @@ void ConfigurationShared::SetHighlight(QWidget* widget, bool highlighted) { | |||
| 45 | } | 45 | } |
| 46 | 46 | ||
| 47 | void ConfigurationShared::SetColoredTristate(QCheckBox* checkbox, | 47 | void ConfigurationShared::SetColoredTristate(QCheckBox* checkbox, |
| 48 | const Settings::Setting<bool>& setting, | 48 | const Settings::SwitchableSetting<bool>& setting, |
| 49 | CheckState& tracker) { | 49 | CheckState& tracker) { |
| 50 | if (setting.UsingGlobal()) { | 50 | if (setting.UsingGlobal()) { |
| 51 | tracker = CheckState::Global; | 51 | tracker = CheckState::Global; |
diff --git a/src/yuzu/configuration/configuration_shared.h b/src/yuzu/configuration/configuration_shared.h index 903a9baae..77802a367 100644 --- a/src/yuzu/configuration/configuration_shared.h +++ b/src/yuzu/configuration/configuration_shared.h | |||
| @@ -25,10 +25,10 @@ enum class CheckState { | |||
| 25 | // Global-aware apply and set functions | 25 | // Global-aware apply and set functions |
| 26 | 26 | ||
| 27 | // ApplyPerGameSetting, given a Settings::Setting and a Qt UI element, properly applies a Setting | 27 | // ApplyPerGameSetting, given a Settings::Setting and a Qt UI element, properly applies a Setting |
| 28 | void ApplyPerGameSetting(Settings::Setting<bool>* setting, const QCheckBox* checkbox, | 28 | void ApplyPerGameSetting(Settings::SwitchableSetting<bool>* setting, const QCheckBox* checkbox, |
| 29 | const CheckState& tracker); | 29 | const CheckState& tracker); |
| 30 | template <typename Type> | 30 | template <typename Type> |
| 31 | void ApplyPerGameSetting(Settings::Setting<Type>* setting, const QComboBox* combobox) { | 31 | void ApplyPerGameSetting(Settings::SwitchableSetting<Type>* setting, const QComboBox* combobox) { |
| 32 | if (Settings::IsConfiguringGlobal() && setting->UsingGlobal()) { | 32 | if (Settings::IsConfiguringGlobal() && setting->UsingGlobal()) { |
| 33 | setting->SetValue(static_cast<Type>(combobox->currentIndex())); | 33 | setting->SetValue(static_cast<Type>(combobox->currentIndex())); |
| 34 | } else if (!Settings::IsConfiguringGlobal()) { | 34 | } else if (!Settings::IsConfiguringGlobal()) { |
| @@ -43,10 +43,10 @@ void ApplyPerGameSetting(Settings::Setting<Type>* setting, const QComboBox* comb | |||
| 43 | } | 43 | } |
| 44 | 44 | ||
| 45 | // Sets a Qt UI element given a Settings::Setting | 45 | // Sets a Qt UI element given a Settings::Setting |
| 46 | void SetPerGameSetting(QCheckBox* checkbox, const Settings::Setting<bool>* setting); | 46 | void SetPerGameSetting(QCheckBox* checkbox, const Settings::SwitchableSetting<bool>* setting); |
| 47 | 47 | ||
| 48 | template <typename Type> | 48 | template <typename Type> |
| 49 | void SetPerGameSetting(QComboBox* combobox, const Settings::Setting<Type>* setting) { | 49 | void SetPerGameSetting(QComboBox* combobox, const Settings::SwitchableSetting<Type>* setting) { |
| 50 | combobox->setCurrentIndex(setting->UsingGlobal() ? ConfigurationShared::USE_GLOBAL_INDEX | 50 | combobox->setCurrentIndex(setting->UsingGlobal() ? ConfigurationShared::USE_GLOBAL_INDEX |
| 51 | : static_cast<int>(setting->GetValue()) + | 51 | : static_cast<int>(setting->GetValue()) + |
| 52 | ConfigurationShared::USE_GLOBAL_OFFSET); | 52 | ConfigurationShared::USE_GLOBAL_OFFSET); |
| @@ -56,7 +56,7 @@ void SetPerGameSetting(QComboBox* combobox, const Settings::Setting<Type>* setti | |||
| 56 | void SetHighlight(QWidget* widget, bool highlighted); | 56 | void SetHighlight(QWidget* widget, bool highlighted); |
| 57 | 57 | ||
| 58 | // Sets up a QCheckBox like a tristate one, given a Setting | 58 | // Sets up a QCheckBox like a tristate one, given a Setting |
| 59 | void SetColoredTristate(QCheckBox* checkbox, const Settings::Setting<bool>& setting, | 59 | void SetColoredTristate(QCheckBox* checkbox, const Settings::SwitchableSetting<bool>& setting, |
| 60 | CheckState& tracker); | 60 | CheckState& tracker); |
| 61 | void SetColoredTristate(QCheckBox* checkbox, bool global, bool state, bool global_state, | 61 | void SetColoredTristate(QCheckBox* checkbox, bool global, bool state, bool global_state, |
| 62 | CheckState& tracker); | 62 | CheckState& tracker); |
diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h index c64d87ace..044d88ca6 100644 --- a/src/yuzu/uisettings.h +++ b/src/yuzu/uisettings.h | |||
| @@ -64,28 +64,28 @@ struct Values { | |||
| 64 | QByteArray gamelist_header_state; | 64 | QByteArray gamelist_header_state; |
| 65 | 65 | ||
| 66 | QByteArray microprofile_geometry; | 66 | QByteArray microprofile_geometry; |
| 67 | Settings::BasicSetting<bool> microprofile_visible{false, "microProfileDialogVisible"}; | 67 | Settings::Setting<bool> microprofile_visible{false, "microProfileDialogVisible"}; |
| 68 | 68 | ||
| 69 | Settings::BasicSetting<bool> single_window_mode{true, "singleWindowMode"}; | 69 | Settings::Setting<bool> single_window_mode{true, "singleWindowMode"}; |
| 70 | Settings::BasicSetting<bool> fullscreen{false, "fullscreen"}; | 70 | Settings::Setting<bool> fullscreen{false, "fullscreen"}; |
| 71 | Settings::BasicSetting<bool> display_titlebar{true, "displayTitleBars"}; | 71 | Settings::Setting<bool> display_titlebar{true, "displayTitleBars"}; |
| 72 | Settings::BasicSetting<bool> show_filter_bar{true, "showFilterBar"}; | 72 | Settings::Setting<bool> show_filter_bar{true, "showFilterBar"}; |
| 73 | Settings::BasicSetting<bool> show_status_bar{true, "showStatusBar"}; | 73 | Settings::Setting<bool> show_status_bar{true, "showStatusBar"}; |
| 74 | 74 | ||
| 75 | Settings::BasicSetting<bool> confirm_before_closing{true, "confirmClose"}; | 75 | Settings::Setting<bool> confirm_before_closing{true, "confirmClose"}; |
| 76 | Settings::BasicSetting<bool> first_start{true, "firstStart"}; | 76 | Settings::Setting<bool> first_start{true, "firstStart"}; |
| 77 | Settings::BasicSetting<bool> pause_when_in_background{false, "pauseWhenInBackground"}; | 77 | Settings::Setting<bool> pause_when_in_background{false, "pauseWhenInBackground"}; |
| 78 | Settings::BasicSetting<bool> mute_when_in_background{false, "muteWhenInBackground"}; | 78 | Settings::Setting<bool> mute_when_in_background{false, "muteWhenInBackground"}; |
| 79 | Settings::BasicSetting<bool> hide_mouse{true, "hideInactiveMouse"}; | 79 | Settings::Setting<bool> hide_mouse{true, "hideInactiveMouse"}; |
| 80 | // Set when Vulkan is known to crash the application | 80 | // Set when Vulkan is known to crash the application |
| 81 | Settings::BasicSetting<bool> has_broken_vulkan{false, "has_broken_vulkan"}; | 81 | Settings::Setting<bool> has_broken_vulkan{false, "has_broken_vulkan"}; |
| 82 | 82 | ||
| 83 | Settings::BasicSetting<bool> select_user_on_boot{false, "select_user_on_boot"}; | 83 | Settings::Setting<bool> select_user_on_boot{false, "select_user_on_boot"}; |
| 84 | 84 | ||
| 85 | // Discord RPC | 85 | // Discord RPC |
| 86 | Settings::BasicSetting<bool> enable_discord_presence{true, "enable_discord_presence"}; | 86 | Settings::Setting<bool> enable_discord_presence{true, "enable_discord_presence"}; |
| 87 | 87 | ||
| 88 | Settings::BasicSetting<bool> enable_screenshot_save_as{true, "enable_screenshot_save_as"}; | 88 | Settings::Setting<bool> enable_screenshot_save_as{true, "enable_screenshot_save_as"}; |
| 89 | 89 | ||
| 90 | QString roms_path; | 90 | QString roms_path; |
| 91 | QString symbols_path; | 91 | QString symbols_path; |
| @@ -100,25 +100,25 @@ struct Values { | |||
| 100 | // Shortcut name <Shortcut, context> | 100 | // Shortcut name <Shortcut, context> |
| 101 | std::vector<Shortcut> shortcuts; | 101 | std::vector<Shortcut> shortcuts; |
| 102 | 102 | ||
| 103 | Settings::BasicSetting<uint32_t> callout_flags{0, "calloutFlags"}; | 103 | Settings::Setting<uint32_t> callout_flags{0, "calloutFlags"}; |
| 104 | 104 | ||
| 105 | // logging | 105 | // logging |
| 106 | Settings::BasicSetting<bool> show_console{false, "showConsole"}; | 106 | Settings::Setting<bool> show_console{false, "showConsole"}; |
| 107 | 107 | ||
| 108 | // Game List | 108 | // Game List |
| 109 | Settings::BasicSetting<bool> show_add_ons{true, "show_add_ons"}; | 109 | Settings::Setting<bool> show_add_ons{true, "show_add_ons"}; |
| 110 | Settings::BasicSetting<uint32_t> game_icon_size{64, "game_icon_size"}; | 110 | Settings::Setting<uint32_t> game_icon_size{64, "game_icon_size"}; |
| 111 | Settings::BasicSetting<uint32_t> folder_icon_size{48, "folder_icon_size"}; | 111 | Settings::Setting<uint32_t> folder_icon_size{48, "folder_icon_size"}; |
| 112 | Settings::BasicSetting<uint8_t> row_1_text_id{3, "row_1_text_id"}; | 112 | Settings::Setting<uint8_t> row_1_text_id{3, "row_1_text_id"}; |
| 113 | Settings::BasicSetting<uint8_t> row_2_text_id{2, "row_2_text_id"}; | 113 | Settings::Setting<uint8_t> row_2_text_id{2, "row_2_text_id"}; |
| 114 | std::atomic_bool is_game_list_reload_pending{false}; | 114 | std::atomic_bool is_game_list_reload_pending{false}; |
| 115 | Settings::BasicSetting<bool> cache_game_list{true, "cache_game_list"}; | 115 | Settings::Setting<bool> cache_game_list{true, "cache_game_list"}; |
| 116 | Settings::BasicSetting<bool> favorites_expanded{true, "favorites_expanded"}; | 116 | Settings::Setting<bool> favorites_expanded{true, "favorites_expanded"}; |
| 117 | QVector<u64> favorited_ids; | 117 | QVector<u64> favorited_ids; |
| 118 | 118 | ||
| 119 | bool configuration_applied; | 119 | bool configuration_applied; |
| 120 | bool reset_to_defaults; | 120 | bool reset_to_defaults; |
| 121 | Settings::BasicSetting<bool> disable_web_applet{true, "disable_web_applet"}; | 121 | Settings::Setting<bool> disable_web_applet{true, "disable_web_applet"}; |
| 122 | }; | 122 | }; |
| 123 | 123 | ||
| 124 | extern Values values; | 124 | extern Values values; |
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp index fc4744fb0..903e02297 100644 --- a/src/yuzu_cmd/config.cpp +++ b/src/yuzu_cmd/config.cpp | |||
| @@ -90,17 +90,17 @@ static const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> | |||
| 90 | }}; | 90 | }}; |
| 91 | 91 | ||
| 92 | template <> | 92 | template <> |
| 93 | void Config::ReadSetting(const std::string& group, Settings::BasicSetting<std::string>& setting) { | 93 | void Config::ReadSetting(const std::string& group, Settings::Setting<std::string>& setting) { |
| 94 | setting = sdl2_config->Get(group, setting.GetLabel(), setting.GetDefault()); | 94 | setting = sdl2_config->Get(group, setting.GetLabel(), setting.GetDefault()); |
| 95 | } | 95 | } |
| 96 | 96 | ||
| 97 | template <> | 97 | template <> |
| 98 | void Config::ReadSetting(const std::string& group, Settings::BasicSetting<bool>& setting) { | 98 | void Config::ReadSetting(const std::string& group, Settings::Setting<bool>& setting) { |
| 99 | setting = sdl2_config->GetBoolean(group, setting.GetLabel(), setting.GetDefault()); | 99 | setting = sdl2_config->GetBoolean(group, setting.GetLabel(), setting.GetDefault()); |
| 100 | } | 100 | } |
| 101 | 101 | ||
| 102 | template <typename Type> | 102 | template <typename Type> |
| 103 | void Config::ReadSetting(const std::string& group, Settings::BasicSetting<Type>& setting) { | 103 | void Config::ReadSetting(const std::string& group, Settings::Setting<Type>& setting) { |
| 104 | setting = static_cast<Type>(sdl2_config->GetInteger(group, setting.GetLabel(), | 104 | setting = static_cast<Type>(sdl2_config->GetInteger(group, setting.GetLabel(), |
| 105 | static_cast<long>(setting.GetDefault()))); | 105 | static_cast<long>(setting.GetDefault()))); |
| 106 | } | 106 | } |
diff --git a/src/yuzu_cmd/config.h b/src/yuzu_cmd/config.h index f61ba23ec..ccf77d668 100644 --- a/src/yuzu_cmd/config.h +++ b/src/yuzu_cmd/config.h | |||
| @@ -28,11 +28,11 @@ public: | |||
| 28 | 28 | ||
| 29 | private: | 29 | private: |
| 30 | /** | 30 | /** |
| 31 | * Applies a value read from the sdl2_config to a BasicSetting. | 31 | * Applies a value read from the sdl2_config to a Setting. |
| 32 | * | 32 | * |
| 33 | * @param group The name of the INI group | 33 | * @param group The name of the INI group |
| 34 | * @param setting The yuzu setting to modify | 34 | * @param setting The yuzu setting to modify |
| 35 | */ | 35 | */ |
| 36 | template <typename Type> | 36 | template <typename Type> |
| 37 | void ReadSetting(const std::string& group, Settings::BasicSetting<Type>& setting); | 37 | void ReadSetting(const std::string& group, Settings::Setting<Type>& setting); |
| 38 | }; | 38 | }; |