summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/audio_core/audio_renderer.cpp10
-rw-r--r--src/audio_core/stream.cpp5
-rw-r--r--src/common/settings.h452
-rw-r--r--src/core/arm/arm_interface.cpp15
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_32.cpp30
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_32.h2
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.cpp17
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.h2
-rw-r--r--src/core/core_timing.cpp85
-rw-r--r--src/core/core_timing.h25
-rw-r--r--src/core/hardware_interrupt_manager.cpp5
-rw-r--r--src/core/hid/hid_types.h1
-rw-r--r--src/core/hle/kernel/k_process.cpp2
-rw-r--r--src/core/hle/kernel/k_thread.cpp7
-rw-r--r--src/core/hle/kernel/kernel.cpp9
-rw-r--r--src/core/hle/kernel/time_manager.cpp20
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp34
-rw-r--r--src/core/hle/service/hid/controllers/npad.h3
-rw-r--r--src/core/hle/service/hid/errors.h3
-rw-r--r--src/core/hle/service/hid/hid.cpp116
-rw-r--r--src/core/hle/service/hid/hidbus.cpp16
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.cpp13
-rw-r--r--src/core/memory/cheat_engine.cpp8
-rw-r--r--src/core/tools/freezer.cpp4
-rw-r--r--src/tests/core/core_timing.cpp5
-rw-r--r--src/yuzu/configuration/config.cpp12
-rw-r--r--src/yuzu/configuration/config.h8
-rw-r--r--src/yuzu/configuration/configuration_shared.cpp6
-rw-r--r--src/yuzu/configuration/configuration_shared.h10
-rw-r--r--src/yuzu/uisettings.h54
-rw-r--r--src/yuzu_cmd/config.cpp6
-rw-r--r--src/yuzu_cmd/config.h4
32 files changed, 517 insertions, 472 deletions
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/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 */
109template <typename Type> 109template <typename Type>
110class BasicSetting { 110class Setting {
111protected: 111protected:
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
122public: 122public:
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
192protected: 205protected:
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 */
203template <typename Type>
204class BasicRangedSetting : virtual public BasicSetting<Type> {
205public:
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 */
253template <typename Type> 222template <typename Type>
254class Setting : virtual public BasicSetting<Type> { 223class SwitchableSetting : virtual public Setting<Type> {
255public: 224public:
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 */
358template <typename Type>
359class RangedSetting final : public BasicRangedSetting<Type>, public Setting<Type> {
360public:
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>
431class InputSetting final { 343class InputSetting final {
432public: 344public:
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
460struct Values { 372struct 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/core/arm/arm_interface.cpp b/src/core/arm/arm_interface.cpp
index 0efc3732f..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 }
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
index 10cf72a45..1be5fe1c1 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
@@ -427,18 +427,38 @@ void ARM_Dynarmic_32::PageTableChanged(Common::PageTable& page_table,
427} 427}
428 428
429std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_32::GetBacktrace(Core::System& system, 429std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_32::GetBacktrace(Core::System& system,
430 u64 sp, u64 lr) { 430 u64 fp, u64 lr, u64 pc) {
431 // No way to get accurate stack traces in A32 yet 431 std::vector<BacktraceEntry> out;
432 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;
433} 452}
434 453
435std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_32::GetBacktraceFromContext( 454std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_32::GetBacktraceFromContext(
436 System& system, const ThreadContext32& ctx) { 455 System& system, const ThreadContext32& ctx) {
437 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]);
438} 458}
439 459
440std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_32::GetBacktrace() const { 460std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_32::GetBacktrace() const {
441 return GetBacktrace(system, GetReg(13), GetReg(14)); 461 return GetBacktrace(system, GetReg(11), GetReg(14), GetReg(15));
442} 462}
443 463
444} // 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:
78private: 78private:
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 92266aa9e..c437f24b8 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
@@ -494,22 +494,22 @@ void ARM_Dynarmic_64::PageTableChanged(Common::PageTable& page_table,
494} 494}
495 495
496std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_64::GetBacktrace(Core::System& system, 496std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_64::GetBacktrace(Core::System& system,
497 u64 fp, u64 lr) { 497 u64 fp, u64 lr, u64 pc) {
498 std::vector<BacktraceEntry> out; 498 std::vector<BacktraceEntry> out;
499 auto& memory = system.Memory(); 499 auto& memory = system.Memory();
500 500
501 // fp (= r29) points to the last frame record. 501 out.push_back({"", 0, pc, 0, ""});
502 // Note that this is the frame record for the *previous* frame, not the current one. 502
503 // Note we need to subtract 4 from our last read to get the proper address 503 // fp (= x29) points to the previous frame record.
504 // Frame records are two words long: 504 // Frame records are two words long:
505 // fp+0 : pointer to previous frame record 505 // fp+0 : pointer to previous frame record
506 // fp+8 : value of lr for frame 506 // fp+8 : value of lr for frame
507 while (true) { 507 while (true) {
508 out.push_back({"", 0, lr, 0, ""}); 508 out.push_back({"", 0, lr, 0, ""});
509 if (!fp) { 509 if (!fp || (fp % 4 != 0) || !memory.IsValidVirtualAddressRange(fp, 16)) {
510 break; 510 break;
511 } 511 }
512 lr = memory.Read64(fp + 8) - 4; 512 lr = memory.Read64(fp + 8);
513 fp = memory.Read64(fp); 513 fp = memory.Read64(fp);
514 } 514 }
515 515
@@ -520,11 +520,12 @@ std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_64::GetBacktrace(Core::S
520 520
521std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_64::GetBacktraceFromContext( 521std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_64::GetBacktraceFromContext(
522 System& system, const ThreadContext64& ctx) { 522 System& system, const ThreadContext64& ctx) {
523 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);
524} 525}
525 526
526std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_64::GetBacktrace() const { 527std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_64::GetBacktrace() const {
527 return GetBacktrace(system, GetReg(29), GetReg(30)); 528 return GetBacktrace(system, GetReg(29), GetReg(30), GetPC());
528} 529}
529 530
530} // 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 140578069..5425637f5 100644
--- a/src/core/core_timing.cpp
+++ b/src/core/core_timing.cpp
@@ -22,10 +22,11 @@ std::shared_ptr<EventType> CreateEvent(std::string name, TimedCallback&& callbac
22} 22}
23 23
24struct CoreTiming::Event { 24struct CoreTiming::Event {
25 u64 time; 25 s64 time;
26 u64 fifo_order; 26 u64 fifo_order;
27 std::uintptr_t user_data; 27 std::uintptr_t user_data;
28 std::weak_ptr<EventType> type; 28 std::weak_ptr<EventType> type;
29 s64 reschedule_time;
29 30
30 // 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
31 // the order added to the queue 32 // the order added to the queue
@@ -58,15 +59,11 @@ void CoreTiming::Initialize(std::function<void()>&& on_thread_init_) {
58 event_fifo_id = 0; 59 event_fifo_id = 0;
59 shutting_down = false; 60 shutting_down = false;
60 ticks = 0; 61 ticks = 0;
61 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; };
62 ev_lost = CreateEvent("_lost_event", empty_timed_callback); 64 ev_lost = CreateEvent("_lost_event", empty_timed_callback);
63 if (is_multicore) { 65 if (is_multicore) {
64 const auto hardware_concurrency = std::thread::hardware_concurrency(); 66 worker_threads.emplace_back(ThreadEntry, std::ref(*this), 0);
65 size_t id = 0;
66 worker_threads.emplace_back(ThreadEntry, std::ref(*this), id++);
67 if (hardware_concurrency > 8) {
68 worker_threads.emplace_back(ThreadEntry, std::ref(*this), id++);
69 }
70 } 67 }
71} 68}
72 69
@@ -81,6 +78,7 @@ void CoreTiming::Shutdown() {
81 thread.join(); 78 thread.join();
82 } 79 }
83 worker_threads.clear(); 80 worker_threads.clear();
81 pause_callbacks.clear();
84 ClearPendingEvents(); 82 ClearPendingEvents();
85 has_started = false; 83 has_started = false;
86} 84}
@@ -98,6 +96,14 @@ void CoreTiming::Pause(bool is_paused_) {
98 } 96 }
99 } 97 }
100 paused_state.store(is_paused_, std::memory_order_relaxed); 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 }
101} 107}
102 108
103void CoreTiming::SyncPause(bool is_paused_) { 109void CoreTiming::SyncPause(bool is_paused_) {
@@ -121,6 +127,14 @@ void CoreTiming::SyncPause(bool is_paused_) {
121 wait_signal_cv.wait(main_lock, [this] { return pause_count == 0; }); 127 wait_signal_cv.wait(main_lock, [this] { return pause_count == 0; });
122 } 128 }
123 } 129 }
130
131 if (!is_paused_) {
132 pause_end_time = GetGlobalTimeNs().count();
133 }
134
135 for (auto& cb : pause_callbacks) {
136 cb(is_paused_);
137 }
124} 138}
125 139
126bool CoreTiming::IsRunning() const { 140bool CoreTiming::IsRunning() const {
@@ -134,12 +148,30 @@ bool CoreTiming::HasPendingEvents() const {
134 148
135void CoreTiming::ScheduleEvent(std::chrono::nanoseconds ns_into_future, 149void CoreTiming::ScheduleEvent(std::chrono::nanoseconds ns_into_future,
136 const std::shared_ptr<EventType>& event_type, 150 const std::shared_ptr<EventType>& event_type,
137 std::uintptr_t user_data) { 151 std::uintptr_t user_data, bool absolute_time) {
138 152
139 std::unique_lock main_lock(event_mutex); 153 std::unique_lock main_lock(event_mutex);
140 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};
141 155
142 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);
158
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
166void 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()});
143 pending_events.fetch_add(1, std::memory_order_relaxed); 175 pending_events.fetch_add(1, std::memory_order_relaxed);
144 176
145 std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>()); 177 std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>());
@@ -218,6 +250,11 @@ void CoreTiming::RemoveEvent(const std::shared_ptr<EventType>& event_type) {
218 } 250 }
219} 251}
220 252
253void CoreTiming::RegisterPauseCallback(PauseCallback&& callback) {
254 std::unique_lock main_lock(event_mutex);
255 pause_callbacks.emplace_back(std::move(callback));
256}
257
221std::optional<s64> CoreTiming::Advance() { 258std::optional<s64> CoreTiming::Advance() {
222 global_timer = GetGlobalTimeNs().count(); 259 global_timer = GetGlobalTimeNs().count();
223 260
@@ -228,17 +265,31 @@ std::optional<s64> CoreTiming::Advance() {
228 event_queue.pop_back(); 265 event_queue.pop_back();
229 266
230 if (const auto event_type{evt.type.lock()}) { 267 if (const auto event_type{evt.type.lock()}) {
231 sequence_mutex.lock();
232 event_mutex.unlock(); 268 event_mutex.unlock();
233 269
234 event_type->guard.lock(); 270 const auto new_schedule_time{event_type->callback(
235 sequence_mutex.unlock(); 271 evt.user_data, evt.time,
236 const s64 delay = static_cast<s64>(GetGlobalTimeNs().count() - evt.time); 272 std::chrono::nanoseconds{GetGlobalTimeNs().count() - evt.time})};
237 event_type->callback(evt.user_data, std::chrono::nanoseconds{delay});
238 event_type->guard.unlock();
239 273
240 event_mutex.lock(); 274 event_mutex.lock();
241 pending_events.fetch_sub(1, std::memory_order_relaxed); 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 }
242 } 293 }
243 294
244 global_timer = GetGlobalTimeNs().count(); 295 global_timer = GetGlobalTimeNs().count();
diff --git a/src/core/core_timing.h b/src/core/core_timing.h
index a86553e08..09b6ed81a 100644
--- a/src/core/core_timing.h
+++ b/src/core/core_timing.h
@@ -20,8 +20,9 @@
20namespace Core::Timing { 20namespace 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.
23using TimedCallback = 23using 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)>;
25using PauseCallback = std::function<void(bool paused)>;
25 26
26/// Contains the characteristics of a particular event. 27/// Contains the characteristics of a particular event.
27struct EventType { 28struct EventType {
@@ -32,7 +33,6 @@ struct EventType {
32 TimedCallback callback; 33 TimedCallback callback;
33 /// A pointer to the name of the event. 34 /// A pointer to the name of the event.
34 const std::string name; 35 const std::string name;
35 mutable std::mutex guard;
36}; 36};
37 37
38/** 38/**
@@ -94,7 +94,15 @@ public:
94 94
95 /// Schedules an event in core timing 95 /// Schedules an event in core timing
96 void ScheduleEvent(std::chrono::nanoseconds ns_into_future, 96 void ScheduleEvent(std::chrono::nanoseconds ns_into_future,
97 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);
98 106
99 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);
100 108
@@ -126,6 +134,9 @@ public:
126 /// 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.
127 std::optional<s64> Advance(); 135 std::optional<s64> Advance();
128 136
137 /// Register a callback function to be called when coretiming pauses.
138 void RegisterPauseCallback(PauseCallback&& callback);
139
129private: 140private:
130 struct Event; 141 struct Event;
131 142
@@ -137,7 +148,7 @@ private:
137 148
138 std::unique_ptr<Common::WallClock> clock; 149 std::unique_ptr<Common::WallClock> clock;
139 150
140 u64 global_timer = 0; 151 s64 global_timer = 0;
141 152
142 // 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.
143 // 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
@@ -157,17 +168,19 @@ private:
157 std::condition_variable wait_pause_cv; 168 std::condition_variable wait_pause_cv;
158 std::condition_variable wait_signal_cv; 169 std::condition_variable wait_signal_cv;
159 mutable std::mutex event_mutex; 170 mutable std::mutex event_mutex;
160 mutable std::mutex sequence_mutex;
161 171
162 std::atomic<bool> paused_state{}; 172 std::atomic<bool> paused_state{};
163 bool is_paused{}; 173 bool is_paused{};
164 bool shutting_down{}; 174 bool shutting_down{};
165 bool is_multicore{}; 175 bool is_multicore{};
166 size_t pause_count{}; 176 size_t pause_count{};
177 s64 pause_end_time{};
167 178
168 /// Cycle timing 179 /// Cycle timing
169 u64 ticks{}; 180 u64 ticks{};
170 s64 downcount{}; 181 s64 downcount{};
182
183 std::vector<PauseCallback> pause_callbacks{};
171}; 184};
172 185
173/// 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/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
12InterruptManager::InterruptManager(Core::System& system_in) : system(system_in) { 12InterruptManager::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_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_thread.cpp b/src/core/hle/kernel/k_thread.cpp
index 23bf7425a..50cb5fc90 100644
--- a/src/core/hle/kernel/k_thread.cpp
+++ b/src/core/hle/kernel/k_thread.cpp
@@ -480,9 +480,7 @@ void KThread::Unpin() {
480 480
481 // Resume any threads that began waiting on us while we were pinned. 481 // Resume any threads that began waiting on us while we were pinned.
482 for (auto it = pinned_waiter_list.begin(); it != pinned_waiter_list.end(); ++it) { 482 for (auto it = pinned_waiter_list.begin(); it != pinned_waiter_list.end(); ++it) {
483 if (it->GetState() == ThreadState::Waiting) { 483 it->EndWait(ResultSuccess);
484 it->SetState(ThreadState::Runnable);
485 }
486 } 484 }
487} 485}
488 486
@@ -877,6 +875,7 @@ void KThread::AddWaiterImpl(KThread* thread) {
877 // Keep track of how many kernel waiters we have. 875 // Keep track of how many kernel waiters we have.
878 if (IsKernelAddressKey(thread->GetAddressKey())) { 876 if (IsKernelAddressKey(thread->GetAddressKey())) {
879 ASSERT((num_kernel_waiters++) >= 0); 877 ASSERT((num_kernel_waiters++) >= 0);
878 KScheduler::SetSchedulerUpdateNeeded(kernel);
880 } 879 }
881 880
882 // Insert the waiter. 881 // Insert the waiter.
@@ -890,6 +889,7 @@ void KThread::RemoveWaiterImpl(KThread* thread) {
890 // Keep track of how many kernel waiters we have. 889 // Keep track of how many kernel waiters we have.
891 if (IsKernelAddressKey(thread->GetAddressKey())) { 890 if (IsKernelAddressKey(thread->GetAddressKey())) {
892 ASSERT((num_kernel_waiters--) > 0); 891 ASSERT((num_kernel_waiters--) > 0);
892 KScheduler::SetSchedulerUpdateNeeded(kernel);
893 } 893 }
894 894
895 // Remove the waiter. 895 // Remove the waiter.
@@ -965,6 +965,7 @@ KThread* KThread::RemoveWaiterByKey(s32* out_num_waiters, VAddr key) {
965 // Keep track of how many kernel waiters we have. 965 // Keep track of how many kernel waiters we have.
966 if (IsKernelAddressKey(thread->GetAddressKey())) { 966 if (IsKernelAddressKey(thread->GetAddressKey())) {
967 ASSERT((num_kernel_waiters--) > 0); 967 ASSERT((num_kernel_waiters--) > 0);
968 KScheduler::SetSchedulerUpdateNeeded(kernel);
968 } 969 }
969 it = waiter_list.erase(it); 970 it = waiter_list.erase(it);
970 971
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 @@
11namespace Kernel { 11namespace Kernel {
12 12
13TimeManager::TimeManager(Core::System& system_) : system{system_} { 13TimeManager::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
25void TimeManager::ScheduleTimeEvent(KThread* thread, s64 nanoseconds) { 27void 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..aa7189bda 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
52bool Controller_NPad::IsDeviceHandleValid(const Core::HID::VibrationDeviceHandle& device_handle) { 52Result 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
59Result Controller_NPad::VerifyValidSixAxisSensorHandle( 70Result 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
707void Controller_NPad::SetNpadHandheldActivationMode(NpadHandheldActivationMode activation_mode) { 720void 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
@@ -840,7 +858,7 @@ bool Controller_NPad::VibrateControllerAtIndex(Core::HID::NpadIdType npad_id,
840void Controller_NPad::VibrateController( 858void 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
904Core::HID::VibrationValue Controller_NPad::GetLastVibration( 922Core::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
915void Controller_NPad::InitializeVibrationDevice( 933void 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
942bool Controller_NPad::IsVibrationDeviceMounted( 960bool 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
10constexpr Result NpadInvalidHandle{ErrorModule::HID, 100}; 10constexpr Result NpadInvalidHandle{ErrorModule::HID, 100};
11constexpr Result NpadDeviceIndexOutOfRange{ErrorModule::HID, 107}; 11constexpr Result NpadDeviceIndexOutOfRange{ErrorModule::HID, 107};
12constexpr Result VibrationInvalidStyleIndex{ErrorModule::HID, 122};
13constexpr Result VibrationInvalidNpadId{ErrorModule::HID, 123};
14constexpr Result VibrationDeviceIndexOutOfRange{ErrorModule::HID, 124};
12constexpr Result InvalidSixAxisFusionRange{ErrorModule::HID, 423}; 15constexpr Result InvalidSixAxisFusionRange{ErrorModule::HID, 423};
13constexpr Result NpadIsDualJoycon{ErrorModule::HID, 601}; 16constexpr Result NpadIsDualJoycon{ErrorModule::HID, 601};
14constexpr Result NpadIsSameType{ErrorModule::HID, 602}; 17constexpr 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
147void IAppletResource::UpdateMouseKeyboard(std::uintptr_t user_data, 148void 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
162void IAppletResource::UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { 156void 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
175class IActiveVibrationDeviceList final : public ServiceFramework<IActiveVibrationDeviceList> { 162class 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
1095void Hid::GetPlayerLedPattern(Kernel::HLERequestContext& ctx) { 1082void 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
1179void Hid::SetNpadJoyAssignmentModeSingle(Kernel::HLERequestContext& ctx) { 1165void 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
1203void Hid::SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) { 1189void 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
1225void Hid::MergeSingleJoyAsDualJoy(Kernel::HLERequestContext& ctx) { 1210void Hid::MergeSingleJoyAsDualJoy(Kernel::HLERequestContext& ctx) {
@@ -1412,8 +1397,11 @@ void Hid::ClearNpadCaptureButtonAssignment(Kernel::HLERequestContext& ctx) {
1412void Hid::GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) { 1397void 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
61HidBus::~HidBus() { 64HidBus::~HidBus() {
@@ -63,8 +66,6 @@ HidBus::~HidBus() {
63} 66}
64 67
65void HidBus::UpdateHidbus(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { 68void 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
94std::optional<std::size_t> HidBus::GetDeviceIndexFromHandle(BusHandle handle) const { 88std::optional<std::size_t> HidBus::GetDeviceIndexFromHandle(BusHandle handle) const {
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() {
184void CheatEngine::Initialize() { 184void 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/tests/core/core_timing.cpp b/src/tests/core/core_timing.cpp
index e687416a8..894975e6f 100644
--- a/src/tests/core/core_timing.cpp
+++ b/src/tests/core/core_timing.cpp
@@ -9,6 +9,7 @@
9#include <cstdlib> 9#include <cstdlib>
10#include <memory> 10#include <memory>
11#include <mutex> 11#include <mutex>
12#include <optional>
12#include <string> 13#include <string>
13 14
14#include "core/core.h" 15#include "core/core.h"
@@ -25,13 +26,15 @@ u64 expected_callback = 0;
25std::mutex control_mutex; 26std::mutex control_mutex;
26 27
27template <unsigned int IDX> 28template <unsigned int IDX>
28void HostCallbackTemplate(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { 29std::optional<std::chrono::nanoseconds> HostCallbackTemplate(std::uintptr_t user_data, s64 time,
30 std::chrono::nanoseconds ns_late) {
29 std::unique_lock<std::mutex> lk(control_mutex); 31 std::unique_lock<std::mutex> lk(control_mutex);
30 static_assert(IDX < CB_IDS.size(), "IDX out of range"); 32 static_assert(IDX < CB_IDS.size(), "IDX out of range");
31 callbacks_ran_flags.set(IDX); 33 callbacks_ran_flags.set(IDX);
32 REQUIRE(CB_IDS[IDX] == user_data); 34 REQUIRE(CB_IDS[IDX] == user_data);
33 delays[IDX] = ns_late.count(); 35 delays[IDX] = ns_late.count();
34 ++expected_callback; 36 ++expected_callback;
37 return std::nullopt;
35} 38}
36 39
37struct ScopeInit final { 40struct ScopeInit final {
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
135template <> 135template <>
136void Config::ReadBasicSetting(Settings::BasicSetting<std::string>& setting) { 136void 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
146template <typename Type> 146template <typename Type>
147void Config::ReadBasicSetting(Settings::BasicSetting<Type>& setting) { 147void 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
159template <> 159template <>
160void Config::WriteBasicSetting(const Settings::BasicSetting<std::string>& setting) { 160void 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
167template <typename Type> 167template <typename Type>
168void Config::WriteBasicSetting(const Settings::BasicSetting<Type>& setting) { 168void 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
175template <typename Type> 175template <typename Type>
176void Config::WriteGlobalSetting(const Settings::Setting<Type>& setting) { 176void 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
1424template <typename Type> 1424template <typename Type>
1425void Config::ReadGlobalSetting(Settings::Setting<Type>& setting) { 1425void 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
12void ConfigurationShared::ApplyPerGameSetting(Settings::Setting<bool>* setting, 12void 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
27void ConfigurationShared::SetPerGameSetting(QCheckBox* checkbox, 27void 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
47void ConfigurationShared::SetColoredTristate(QCheckBox* checkbox, 47void 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
28void ApplyPerGameSetting(Settings::Setting<bool>* setting, const QCheckBox* checkbox, 28void ApplyPerGameSetting(Settings::SwitchableSetting<bool>* setting, const QCheckBox* checkbox,
29 const CheckState& tracker); 29 const CheckState& tracker);
30template <typename Type> 30template <typename Type>
31void ApplyPerGameSetting(Settings::Setting<Type>* setting, const QComboBox* combobox) { 31void 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
46void SetPerGameSetting(QCheckBox* checkbox, const Settings::Setting<bool>* setting); 46void SetPerGameSetting(QCheckBox* checkbox, const Settings::SwitchableSetting<bool>* setting);
47 47
48template <typename Type> 48template <typename Type>
49void SetPerGameSetting(QComboBox* combobox, const Settings::Setting<Type>* setting) { 49void 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
56void SetHighlight(QWidget* widget, bool highlighted); 56void 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
59void SetColoredTristate(QCheckBox* checkbox, const Settings::Setting<bool>& setting, 59void SetColoredTristate(QCheckBox* checkbox, const Settings::SwitchableSetting<bool>& setting,
60 CheckState& tracker); 60 CheckState& tracker);
61void SetColoredTristate(QCheckBox* checkbox, bool global, bool state, bool global_state, 61void 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
124extern Values values; 124extern 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
92template <> 92template <>
93void Config::ReadSetting(const std::string& group, Settings::BasicSetting<std::string>& setting) { 93void 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
97template <> 97template <>
98void Config::ReadSetting(const std::string& group, Settings::BasicSetting<bool>& setting) { 98void 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
102template <typename Type> 102template <typename Type>
103void Config::ReadSetting(const std::string& group, Settings::BasicSetting<Type>& setting) { 103void 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
29private: 29private:
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};