summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt8
-rw-r--r--src/audio_core/audio_renderer.cpp10
-rw-r--r--src/audio_core/stream.cpp5
-rw-r--r--src/common/bit_field.h9
-rw-r--r--src/common/common_funcs.h10
-rw-r--r--src/common/fiber.cpp21
-rw-r--r--src/common/fiber.h7
-rw-r--r--src/common/logging/filter.cpp2
-rw-r--r--src/common/logging/types.h2
-rw-r--r--src/common/settings.h455
-rw-r--r--src/common/wall_clock.cpp2
-rw-r--r--src/common/x64/cpu_detect.cpp16
-rw-r--r--src/common/x64/cpu_detect.h5
-rw-r--r--src/core/CMakeLists.txt4
-rw-r--r--src/core/arm/arm_interface.cpp32
-rw-r--r--src/core/arm/arm_interface.h1
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_32.cpp72
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_32.h2
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.cpp45
-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/cpu_manager.cpp55
-rw-r--r--src/core/cpu_manager.h21
-rw-r--r--src/core/debugger/gdbstub_arch.cpp8
-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_code_memory.h6
-rw-r--r--src/core/hle/kernel/k_process.cpp2
-rw-r--r--src/core/hle/kernel/k_scheduler.cpp7
-rw-r--r--src/core/hle/kernel/k_scheduler.h6
-rw-r--r--src/core/hle/kernel/k_thread.cpp34
-rw-r--r--src/core/hle/kernel/k_thread.h3
-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.cpp38
-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/nifm/nifm.cpp42
-rw-r--r--src/core/hle/service/nvflinger/binder.h1
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.cpp13
-rw-r--r--src/core/hle/service/ptm/psm.cpp122
-rw-r--r--src/core/hle/service/ptm/psm.h31
-rw-r--r--src/core/hle/service/ptm/ptm.cpp18
-rw-r--r--src/core/hle/service/ptm/ptm.h18
-rw-r--r--src/core/hle/service/ptm/ts.cpp41
-rw-r--r--src/core/hle/service/ptm/ts.h25
-rw-r--r--src/core/hle/service/service.cpp4
-rw-r--r--src/core/memory/cheat_engine.cpp8
-rw-r--r--src/core/tools/freezer.cpp4
-rw-r--r--src/input_common/drivers/sdl_driver.cpp10
-rw-r--r--src/input_common/drivers/sdl_driver.h1
-rw-r--r--src/tests/common/fibers.cpp123
-rw-r--r--src/tests/core/core_timing.cpp5
-rw-r--r--src/video_core/compatible_formats.cpp6
-rw-r--r--src/video_core/renderer_opengl/maxwell_to_gl.h1
-rw-r--r--src/video_core/renderer_vulkan/maxwell_to_vk.cpp1
-rw-r--r--src/video_core/surface.cpp1
-rw-r--r--src/video_core/surface.h4
-rw-r--r--src/video_core/texture_cache/format_lookup_table.cpp2
-rw-r--r--src/video_core/texture_cache/formatter.h2
-rw-r--r--src/yuzu/applets/qt_web_browser.cpp14
-rw-r--r--src/yuzu/configuration/config.cpp20
-rw-r--r--src/yuzu/configuration/config.h16
-rw-r--r--src/yuzu/configuration/configuration_shared.cpp6
-rw-r--r--src/yuzu/configuration/configuration_shared.h16
-rw-r--r--src/yuzu/uisettings.h54
-rw-r--r--src/yuzu_cmd/config.cpp8
-rw-r--r--src/yuzu_cmd/config.h6
71 files changed, 1007 insertions, 789 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 39d038493..39ae573b2 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -36,7 +36,6 @@ if (MSVC)
36 # /GT - Supports fiber safety for data allocated using static thread-local storage 36 # /GT - Supports fiber safety for data allocated using static thread-local storage
37 add_compile_options( 37 add_compile_options(
38 /MP 38 /MP
39 /Zi
40 /Zm200 39 /Zm200
41 /Zo 40 /Zo
42 /permissive- 41 /permissive-
@@ -79,6 +78,13 @@ if (MSVC)
79 /we5245 # 'function': unreferenced function with internal linkage has been removed 78 /we5245 # 'function': unreferenced function with internal linkage has been removed
80 ) 79 )
81 80
81 if (USE_CCACHE)
82 # when caching, we need to use /Z7 to downgrade debug info to use an older but more cachable format
83 add_compile_options(/Z7)
84 else()
85 add_compile_options(/Zi)
86 endif()
87
82 if (ARCHITECTURE_x86_64) 88 if (ARCHITECTURE_x86_64)
83 add_compile_options(/QIntel-jcc-erratum) 89 add_compile_options(/QIntel-jcc-erratum)
84 endif() 90 endif()
diff --git a/src/audio_core/audio_renderer.cpp b/src/audio_core/audio_renderer.cpp
index 2ee0a96ed..9191ca093 100644
--- a/src/audio_core/audio_renderer.cpp
+++ b/src/audio_core/audio_renderer.cpp
@@ -2,6 +2,7 @@
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <limits> 4#include <limits>
5#include <optional>
5#include <vector> 6#include <vector>
6 7
7#include "audio_core/audio_out.h" 8#include "audio_core/audio_out.h"
@@ -88,9 +89,12 @@ AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing_, Core::Memor
88 stream = audio_out->OpenStream( 89 stream = audio_out->OpenStream(
89 core_timing, params.sample_rate, AudioCommon::STREAM_NUM_CHANNELS, 90 core_timing, params.sample_rate, AudioCommon::STREAM_NUM_CHANNELS,
90 fmt::format("AudioRenderer-Instance{}", instance_number), std::move(release_callback)); 91 fmt::format("AudioRenderer-Instance{}", instance_number), std::move(release_callback));
91 process_event = Core::Timing::CreateEvent( 92 process_event =
92 fmt::format("AudioRenderer-Instance{}-Process", instance_number), 93 Core::Timing::CreateEvent(fmt::format("AudioRenderer-Instance{}-Process", instance_number),
93 [this](std::uintptr_t, std::chrono::nanoseconds) { ReleaseAndQueueBuffers(); }); 94 [this](std::uintptr_t, s64, std::chrono::nanoseconds) {
95 ReleaseAndQueueBuffers();
96 return std::nullopt;
97 });
94 for (s32 i = 0; i < NUM_BUFFERS; ++i) { 98 for (s32 i = 0; i < NUM_BUFFERS; ++i) {
95 QueueMixedBuffer(i); 99 QueueMixedBuffer(i);
96 } 100 }
diff --git a/src/audio_core/stream.cpp b/src/audio_core/stream.cpp
index f8034b04b..cf3d94c53 100644
--- a/src/audio_core/stream.cpp
+++ b/src/audio_core/stream.cpp
@@ -34,9 +34,10 @@ Stream::Stream(Core::Timing::CoreTiming& core_timing_, u32 sample_rate_, Format
34 ReleaseCallback&& release_callback_, SinkStream& sink_stream_, std::string&& name_) 34 ReleaseCallback&& release_callback_, SinkStream& sink_stream_, std::string&& name_)
35 : sample_rate{sample_rate_}, format{format_}, release_callback{std::move(release_callback_)}, 35 : sample_rate{sample_rate_}, format{format_}, release_callback{std::move(release_callback_)},
36 sink_stream{sink_stream_}, core_timing{core_timing_}, name{std::move(name_)} { 36 sink_stream{sink_stream_}, core_timing{core_timing_}, name{std::move(name_)} {
37 release_event = 37 release_event = Core::Timing::CreateEvent(
38 Core::Timing::CreateEvent(name, [this](std::uintptr_t, std::chrono::nanoseconds ns_late) { 38 name, [this](std::uintptr_t, s64 time, std::chrono::nanoseconds ns_late) {
39 ReleaseActiveBuffer(ns_late); 39 ReleaseActiveBuffer(ns_late);
40 return std::nullopt;
40 }); 41 });
41} 42}
42 43
diff --git a/src/common/bit_field.h b/src/common/bit_field.h
index 16d805694..7e1df62b1 100644
--- a/src/common/bit_field.h
+++ b/src/common/bit_field.h
@@ -146,7 +146,16 @@ public:
146 } 146 }
147 147
148 constexpr void Assign(const T& value) { 148 constexpr void Assign(const T& value) {
149#ifdef _MSC_VER
149 storage = static_cast<StorageType>((storage & ~mask) | FormatValue(value)); 150 storage = static_cast<StorageType>((storage & ~mask) | FormatValue(value));
151#else
152 // Explicitly reload with memcpy to avoid compiler aliasing quirks
153 // regarding optimization: GCC/Clang clobber chained stores to
154 // different bitfields in the same struct with the last value.
155 StorageTypeWithEndian storage_;
156 std::memcpy(&storage_, &storage, sizeof(storage_));
157 storage = static_cast<StorageType>((storage_ & ~mask) | FormatValue(value));
158#endif
150 } 159 }
151 160
152 [[nodiscard]] constexpr T Value() const { 161 [[nodiscard]] constexpr T Value() const {
diff --git a/src/common/common_funcs.h b/src/common/common_funcs.h
index adc31c758..e1e2a90fc 100644
--- a/src/common/common_funcs.h
+++ b/src/common/common_funcs.h
@@ -18,14 +18,16 @@
18/// Helper macros to insert unused bytes or words to properly align structs. These values will be 18/// Helper macros to insert unused bytes or words to properly align structs. These values will be
19/// zero-initialized. 19/// zero-initialized.
20#define INSERT_PADDING_BYTES(num_bytes) \ 20#define INSERT_PADDING_BYTES(num_bytes) \
21 std::array<u8, num_bytes> CONCAT2(pad, __LINE__) {} 21 [[maybe_unused]] std::array<u8, num_bytes> CONCAT2(pad, __LINE__) {}
22#define INSERT_PADDING_WORDS(num_words) \ 22#define INSERT_PADDING_WORDS(num_words) \
23 std::array<u32, num_words> CONCAT2(pad, __LINE__) {} 23 [[maybe_unused]] std::array<u32, num_words> CONCAT2(pad, __LINE__) {}
24 24
25/// These are similar to the INSERT_PADDING_* macros but do not zero-initialize the contents. 25/// These are similar to the INSERT_PADDING_* macros but do not zero-initialize the contents.
26/// This keeps the structure trivial to construct. 26/// This keeps the structure trivial to construct.
27#define INSERT_PADDING_BYTES_NOINIT(num_bytes) std::array<u8, num_bytes> CONCAT2(pad, __LINE__) 27#define INSERT_PADDING_BYTES_NOINIT(num_bytes) \
28#define INSERT_PADDING_WORDS_NOINIT(num_words) std::array<u32, num_words> CONCAT2(pad, __LINE__) 28 [[maybe_unused]] std::array<u8, num_bytes> CONCAT2(pad, __LINE__)
29#define INSERT_PADDING_WORDS_NOINIT(num_words) \
30 [[maybe_unused]] std::array<u32, num_words> CONCAT2(pad, __LINE__)
29 31
30#ifndef _MSC_VER 32#ifndef _MSC_VER
31 33
diff --git a/src/common/fiber.cpp b/src/common/fiber.cpp
index f9aeb692a..bc92b360b 100644
--- a/src/common/fiber.cpp
+++ b/src/common/fiber.cpp
@@ -20,10 +20,8 @@ struct Fiber::FiberImpl {
20 VirtualBuffer<u8> rewind_stack; 20 VirtualBuffer<u8> rewind_stack;
21 21
22 std::mutex guard; 22 std::mutex guard;
23 std::function<void(void*)> entry_point; 23 std::function<void()> entry_point;
24 std::function<void(void*)> rewind_point; 24 std::function<void()> rewind_point;
25 void* rewind_parameter{};
26 void* start_parameter{};
27 std::shared_ptr<Fiber> previous_fiber; 25 std::shared_ptr<Fiber> previous_fiber;
28 bool is_thread_fiber{}; 26 bool is_thread_fiber{};
29 bool released{}; 27 bool released{};
@@ -34,13 +32,8 @@ struct Fiber::FiberImpl {
34 boost::context::detail::fcontext_t rewind_context{}; 32 boost::context::detail::fcontext_t rewind_context{};
35}; 33};
36 34
37void Fiber::SetStartParameter(void* new_parameter) { 35void Fiber::SetRewindPoint(std::function<void()>&& rewind_func) {
38 impl->start_parameter = new_parameter;
39}
40
41void Fiber::SetRewindPoint(std::function<void(void*)>&& rewind_func, void* rewind_param) {
42 impl->rewind_point = std::move(rewind_func); 36 impl->rewind_point = std::move(rewind_func);
43 impl->rewind_parameter = rewind_param;
44} 37}
45 38
46void Fiber::Start(boost::context::detail::transfer_t& transfer) { 39void Fiber::Start(boost::context::detail::transfer_t& transfer) {
@@ -48,7 +41,7 @@ void Fiber::Start(boost::context::detail::transfer_t& transfer) {
48 impl->previous_fiber->impl->context = transfer.fctx; 41 impl->previous_fiber->impl->context = transfer.fctx;
49 impl->previous_fiber->impl->guard.unlock(); 42 impl->previous_fiber->impl->guard.unlock();
50 impl->previous_fiber.reset(); 43 impl->previous_fiber.reset();
51 impl->entry_point(impl->start_parameter); 44 impl->entry_point();
52 UNREACHABLE(); 45 UNREACHABLE();
53} 46}
54 47
@@ -59,7 +52,7 @@ void Fiber::OnRewind([[maybe_unused]] boost::context::detail::transfer_t& transf
59 u8* tmp = impl->stack_limit; 52 u8* tmp = impl->stack_limit;
60 impl->stack_limit = impl->rewind_stack_limit; 53 impl->stack_limit = impl->rewind_stack_limit;
61 impl->rewind_stack_limit = tmp; 54 impl->rewind_stack_limit = tmp;
62 impl->rewind_point(impl->rewind_parameter); 55 impl->rewind_point();
63 UNREACHABLE(); 56 UNREACHABLE();
64} 57}
65 58
@@ -73,10 +66,8 @@ void Fiber::RewindStartFunc(boost::context::detail::transfer_t transfer) {
73 fiber->OnRewind(transfer); 66 fiber->OnRewind(transfer);
74} 67}
75 68
76Fiber::Fiber(std::function<void(void*)>&& entry_point_func, void* start_parameter) 69Fiber::Fiber(std::function<void()>&& entry_point_func) : impl{std::make_unique<FiberImpl>()} {
77 : impl{std::make_unique<FiberImpl>()} {
78 impl->entry_point = std::move(entry_point_func); 70 impl->entry_point = std::move(entry_point_func);
79 impl->start_parameter = start_parameter;
80 impl->stack_limit = impl->stack.data(); 71 impl->stack_limit = impl->stack.data();
81 impl->rewind_stack_limit = impl->rewind_stack.data(); 72 impl->rewind_stack_limit = impl->rewind_stack.data();
82 u8* stack_base = impl->stack_limit + default_stack_size; 73 u8* stack_base = impl->stack_limit + default_stack_size;
diff --git a/src/common/fiber.h b/src/common/fiber.h
index 873604bc6..f24d333a3 100644
--- a/src/common/fiber.h
+++ b/src/common/fiber.h
@@ -29,7 +29,7 @@ namespace Common {
29 */ 29 */
30class Fiber { 30class Fiber {
31public: 31public:
32 Fiber(std::function<void(void*)>&& entry_point_func, void* start_parameter); 32 Fiber(std::function<void()>&& entry_point_func);
33 ~Fiber(); 33 ~Fiber();
34 34
35 Fiber(const Fiber&) = delete; 35 Fiber(const Fiber&) = delete;
@@ -43,16 +43,13 @@ public:
43 static void YieldTo(std::weak_ptr<Fiber> weak_from, Fiber& to); 43 static void YieldTo(std::weak_ptr<Fiber> weak_from, Fiber& to);
44 [[nodiscard]] static std::shared_ptr<Fiber> ThreadToFiber(); 44 [[nodiscard]] static std::shared_ptr<Fiber> ThreadToFiber();
45 45
46 void SetRewindPoint(std::function<void(void*)>&& rewind_func, void* rewind_param); 46 void SetRewindPoint(std::function<void()>&& rewind_func);
47 47
48 void Rewind(); 48 void Rewind();
49 49
50 /// Only call from main thread's fiber 50 /// Only call from main thread's fiber
51 void Exit(); 51 void Exit();
52 52
53 /// Changes the start parameter of the fiber. Has no effect if the fiber already started
54 void SetStartParameter(void* new_parameter);
55
56private: 53private:
57 Fiber(); 54 Fiber();
58 55
diff --git a/src/common/logging/filter.cpp b/src/common/logging/filter.cpp
index 4acbff649..6de9bacbf 100644
--- a/src/common/logging/filter.cpp
+++ b/src/common/logging/filter.cpp
@@ -128,7 +128,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
128 SUB(Service, PM) \ 128 SUB(Service, PM) \
129 SUB(Service, PREPO) \ 129 SUB(Service, PREPO) \
130 SUB(Service, PSC) \ 130 SUB(Service, PSC) \
131 SUB(Service, PSM) \ 131 SUB(Service, PTM) \
132 SUB(Service, SET) \ 132 SUB(Service, SET) \
133 SUB(Service, SM) \ 133 SUB(Service, SM) \
134 SUB(Service, SPL) \ 134 SUB(Service, SPL) \
diff --git a/src/common/logging/types.h b/src/common/logging/types.h
index cabb4db8e..595c15ada 100644
--- a/src/common/logging/types.h
+++ b/src/common/logging/types.h
@@ -95,7 +95,7 @@ enum class Class : u8 {
95 Service_PM, ///< The PM service 95 Service_PM, ///< The PM service
96 Service_PREPO, ///< The PREPO (Play report) service 96 Service_PREPO, ///< The PREPO (Play report) service
97 Service_PSC, ///< The PSC service 97 Service_PSC, ///< The PSC service
98 Service_PSM, ///< The PSM service 98 Service_PTM, ///< The PTM service
99 Service_SET, ///< The SET (Settings) service 99 Service_SET, ///< The SET (Settings) service
100 Service_SM, ///< The SM (Service manager) service 100 Service_SM, ///< The SM (Service manager) service
101 Service_SPL, ///< The SPL service 101 Service_SPL, ///< The SPL service
diff --git a/src/common/settings.h b/src/common/settings.h
index a507744a2..368046e87 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, bool ranged = false>
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) requires(!ranged)
130 : default_value{default_val}, global{default_val}, label{name} {} 130 : value{default_val}, default_value{default_val}, 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) requires(ranged)
143 : value{default_val},
144 default_value{default_val}, maximum{max_val}, minimum{min_val}, 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,27 @@ 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
195 const std::string label{}; ///< The setting's label 210 const std::string label{}; ///< The setting's label
196}; 211};
197 212
198/** 213/**
199 * BasicRangedSetting class is intended for use with quantifiable settings that need a more 214 * 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 215 * 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 216 * 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. 217 * component to ask whether the custom or global setting is needed at the moment.
248 * 218 *
249 * By default, the global setting is used. 219 * By default, the global setting is used.
250 *
251 * Like the BasicSetting, this requires setting a default value and label to use.
252 */ 220 */
253template <typename Type> 221template <typename Type, bool ranged = false>
254class Setting : virtual public BasicSetting<Type> { 222class SwitchableSetting : virtual public Setting<Type, ranged> {
255public: 223public:
256 /** 224 /**
257 * Sets a default value, label, and setting value. 225 * Sets a default value, label, and setting value.
@@ -259,9 +227,21 @@ public:
259 * @param default_val Intial value of the setting, and default value of the setting 227 * @param default_val Intial value of the setting, and default value of the setting
260 * @param name Label for the setting 228 * @param name Label for the setting
261 */ 229 */
262 explicit Setting(const Type& default_val, const std::string& name) 230 explicit SwitchableSetting(const Type& default_val, const std::string& name) requires(!ranged)
263 : BasicSetting<Type>(default_val, name) {} 231 : Setting<Type>{default_val, name} {}
264 virtual ~Setting() = default; 232 virtual ~SwitchableSetting() = default;
233
234 /**
235 * Sets a default value, minimum value, maximum value, and label.
236 *
237 * @param default_val Intial value of the setting, and default value of the setting
238 * @param min_val Sets the minimum allowed value of the setting
239 * @param max_val Sets the maximum allowed value of the setting
240 * @param name Label for the setting
241 */
242 explicit SwitchableSetting(const Type& default_val, const Type& min_val, const Type& max_val,
243 const std::string& name) requires(ranged)
244 : Setting<Type, true>{default_val, min_val, max_val, name} {}
265 245
266 /** 246 /**
267 * Tells this setting to represent either the global or custom setting when other member 247 * Tells this setting to represent either the global or custom setting when other member
@@ -292,13 +272,13 @@ public:
292 */ 272 */
293 [[nodiscard]] virtual const Type& GetValue() const override { 273 [[nodiscard]] virtual const Type& GetValue() const override {
294 if (use_global) { 274 if (use_global) {
295 return this->global; 275 return this->value;
296 } 276 }
297 return custom; 277 return custom;
298 } 278 }
299 [[nodiscard]] virtual const Type& GetValue(bool need_global) const { 279 [[nodiscard]] virtual const Type& GetValue(bool need_global) const {
300 if (use_global || need_global) { 280 if (use_global || need_global) {
301 return this->global; 281 return this->value;
302 } 282 }
303 return custom; 283 return custom;
304 } 284 }
@@ -306,12 +286,12 @@ public:
306 /** 286 /**
307 * Sets the current setting value depending on the global state. 287 * Sets the current setting value depending on the global state.
308 * 288 *
309 * @param value The new value 289 * @param val The new value
310 */ 290 */
311 void SetValue(const Type& value) override { 291 void SetValue(const Type& val) override {
312 Type temp{value}; 292 Type temp{ranged ? std::clamp(val, this->minimum, this->maximum) : val};
313 if (use_global) { 293 if (use_global) {
314 std::swap(this->global, temp); 294 std::swap(this->value, temp);
315 } else { 295 } else {
316 std::swap(custom, temp); 296 std::swap(custom, temp);
317 } 297 }
@@ -320,15 +300,15 @@ public:
320 /** 300 /**
321 * Assigns the current setting value depending on the global state. 301 * Assigns the current setting value depending on the global state.
322 * 302 *
323 * @param value The new value 303 * @param val The new value
324 * 304 *
325 * @returns A reference to the current setting value 305 * @returns A reference to the current setting value
326 */ 306 */
327 const Type& operator=(const Type& value) override { 307 const Type& operator=(const Type& val) override {
328 Type temp{value}; 308 Type temp{ranged ? std::clamp(val, this->minimum, this->maximum) : val};
329 if (use_global) { 309 if (use_global) {
330 std::swap(this->global, temp); 310 std::swap(this->value, temp);
331 return this->global; 311 return this->value;
332 } 312 }
333 std::swap(custom, temp); 313 std::swap(custom, temp);
334 return custom; 314 return custom;
@@ -341,7 +321,7 @@ public:
341 */ 321 */
342 virtual explicit operator const Type&() const override { 322 virtual explicit operator const Type&() const override {
343 if (use_global) { 323 if (use_global) {
344 return this->global; 324 return this->value;
345 } 325 }
346 return custom; 326 return custom;
347 } 327 }
@@ -352,75 +332,6 @@ protected:
352}; 332};
353 333
354/** 334/**
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. 335 * 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 336 * 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 337 * using the SetValue() member function found in the Setting class. The primary purpose of this
@@ -431,7 +342,7 @@ template <typename Type>
431class InputSetting final { 342class InputSetting final {
432public: 343public:
433 InputSetting() = default; 344 InputSetting() = default;
434 explicit InputSetting(Type val) : BasicSetting<Type>(val) {} 345 explicit InputSetting(Type val) : Setting<Type>(val) {}
435 ~InputSetting() = default; 346 ~InputSetting() = default;
436 void SetGlobal(bool to_global) { 347 void SetGlobal(bool to_global) {
437 use_global = to_global; 348 use_global = to_global;
@@ -459,175 +370,175 @@ struct TouchFromButtonMap {
459 370
460struct Values { 371struct Values {
461 // Audio 372 // Audio
462 BasicSetting<std::string> audio_device_id{"auto", "output_device"}; 373 Setting<std::string> audio_device_id{"auto", "output_device"};
463 BasicSetting<std::string> sink_id{"auto", "output_engine"}; 374 Setting<std::string> sink_id{"auto", "output_engine"};
464 BasicSetting<bool> audio_muted{false, "audio_muted"}; 375 Setting<bool> audio_muted{false, "audio_muted"};
465 RangedSetting<u8> volume{100, 0, 100, "volume"}; 376 SwitchableSetting<u8, true> volume{100, 0, 100, "volume"};
466 377
467 // Core 378 // Core
468 Setting<bool> use_multi_core{true, "use_multi_core"}; 379 SwitchableSetting<bool> use_multi_core{true, "use_multi_core"};
469 Setting<bool> use_extended_memory_layout{false, "use_extended_memory_layout"}; 380 SwitchableSetting<bool> use_extended_memory_layout{false, "use_extended_memory_layout"};
470 381
471 // Cpu 382 // Cpu
472 RangedSetting<CPUAccuracy> cpu_accuracy{CPUAccuracy::Auto, CPUAccuracy::Auto, 383 SwitchableSetting<CPUAccuracy, true> cpu_accuracy{CPUAccuracy::Auto, CPUAccuracy::Auto,
473 CPUAccuracy::Paranoid, "cpu_accuracy"}; 384 CPUAccuracy::Paranoid, "cpu_accuracy"};
474 // TODO: remove cpu_accuracy_first_time, migration setting added 8 July 2021 385 // TODO: remove cpu_accuracy_first_time, migration setting added 8 July 2021
475 BasicSetting<bool> cpu_accuracy_first_time{true, "cpu_accuracy_first_time"}; 386 Setting<bool> cpu_accuracy_first_time{true, "cpu_accuracy_first_time"};
476 BasicSetting<bool> cpu_debug_mode{false, "cpu_debug_mode"}; 387 Setting<bool> cpu_debug_mode{false, "cpu_debug_mode"};
477 388
478 BasicSetting<bool> cpuopt_page_tables{true, "cpuopt_page_tables"}; 389 Setting<bool> cpuopt_page_tables{true, "cpuopt_page_tables"};
479 BasicSetting<bool> cpuopt_block_linking{true, "cpuopt_block_linking"}; 390 Setting<bool> cpuopt_block_linking{true, "cpuopt_block_linking"};
480 BasicSetting<bool> cpuopt_return_stack_buffer{true, "cpuopt_return_stack_buffer"}; 391 Setting<bool> cpuopt_return_stack_buffer{true, "cpuopt_return_stack_buffer"};
481 BasicSetting<bool> cpuopt_fast_dispatcher{true, "cpuopt_fast_dispatcher"}; 392 Setting<bool> cpuopt_fast_dispatcher{true, "cpuopt_fast_dispatcher"};
482 BasicSetting<bool> cpuopt_context_elimination{true, "cpuopt_context_elimination"}; 393 Setting<bool> cpuopt_context_elimination{true, "cpuopt_context_elimination"};
483 BasicSetting<bool> cpuopt_const_prop{true, "cpuopt_const_prop"}; 394 Setting<bool> cpuopt_const_prop{true, "cpuopt_const_prop"};
484 BasicSetting<bool> cpuopt_misc_ir{true, "cpuopt_misc_ir"}; 395 Setting<bool> cpuopt_misc_ir{true, "cpuopt_misc_ir"};
485 BasicSetting<bool> cpuopt_reduce_misalign_checks{true, "cpuopt_reduce_misalign_checks"}; 396 Setting<bool> cpuopt_reduce_misalign_checks{true, "cpuopt_reduce_misalign_checks"};
486 BasicSetting<bool> cpuopt_fastmem{true, "cpuopt_fastmem"}; 397 Setting<bool> cpuopt_fastmem{true, "cpuopt_fastmem"};
487 BasicSetting<bool> cpuopt_fastmem_exclusives{true, "cpuopt_fastmem_exclusives"}; 398 Setting<bool> cpuopt_fastmem_exclusives{true, "cpuopt_fastmem_exclusives"};
488 BasicSetting<bool> cpuopt_recompile_exclusives{true, "cpuopt_recompile_exclusives"}; 399 Setting<bool> cpuopt_recompile_exclusives{true, "cpuopt_recompile_exclusives"};
489 400
490 Setting<bool> cpuopt_unsafe_unfuse_fma{true, "cpuopt_unsafe_unfuse_fma"}; 401 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"}; 402 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"}; 403 SwitchableSetting<bool> cpuopt_unsafe_ignore_standard_fpcr{
493 Setting<bool> cpuopt_unsafe_inaccurate_nan{true, "cpuopt_unsafe_inaccurate_nan"}; 404 true, "cpuopt_unsafe_ignore_standard_fpcr"};
494 Setting<bool> cpuopt_unsafe_fastmem_check{true, "cpuopt_unsafe_fastmem_check"}; 405 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"}; 406 SwitchableSetting<bool> cpuopt_unsafe_fastmem_check{true, "cpuopt_unsafe_fastmem_check"};
407 SwitchableSetting<bool> cpuopt_unsafe_ignore_global_monitor{
408 true, "cpuopt_unsafe_ignore_global_monitor"};
496 409
497 // Renderer 410 // Renderer
498 RangedSetting<RendererBackend> renderer_backend{ 411 SwitchableSetting<RendererBackend, true> renderer_backend{
499 RendererBackend::Vulkan, RendererBackend::OpenGL, RendererBackend::Vulkan, "backend"}; 412 RendererBackend::Vulkan, RendererBackend::OpenGL, RendererBackend::Vulkan, "backend"};
500 BasicSetting<bool> renderer_debug{false, "debug"}; 413 Setting<bool> renderer_debug{false, "debug"};
501 BasicSetting<bool> renderer_shader_feedback{false, "shader_feedback"}; 414 Setting<bool> renderer_shader_feedback{false, "shader_feedback"};
502 BasicSetting<bool> enable_nsight_aftermath{false, "nsight_aftermath"}; 415 Setting<bool> enable_nsight_aftermath{false, "nsight_aftermath"};
503 BasicSetting<bool> disable_shader_loop_safety_checks{false, 416 Setting<bool> disable_shader_loop_safety_checks{false, "disable_shader_loop_safety_checks"};
504 "disable_shader_loop_safety_checks"}; 417 SwitchableSetting<int> vulkan_device{0, "vulkan_device"};
505 Setting<int> vulkan_device{0, "vulkan_device"};
506 418
507 ResolutionScalingInfo resolution_info{}; 419 ResolutionScalingInfo resolution_info{};
508 Setting<ResolutionSetup> resolution_setup{ResolutionSetup::Res1X, "resolution_setup"}; 420 SwitchableSetting<ResolutionSetup> resolution_setup{ResolutionSetup::Res1X, "resolution_setup"};
509 Setting<ScalingFilter> scaling_filter{ScalingFilter::Bilinear, "scaling_filter"}; 421 SwitchableSetting<ScalingFilter> scaling_filter{ScalingFilter::Bilinear, "scaling_filter"};
510 Setting<AntiAliasing> anti_aliasing{AntiAliasing::None, "anti_aliasing"}; 422 SwitchableSetting<AntiAliasing> anti_aliasing{AntiAliasing::None, "anti_aliasing"};
511 // *nix platforms may have issues with the borderless windowed fullscreen mode. 423 // *nix platforms may have issues with the borderless windowed fullscreen mode.
512 // Default to exclusive fullscreen on these platforms for now. 424 // Default to exclusive fullscreen on these platforms for now.
513 RangedSetting<FullscreenMode> fullscreen_mode{ 425 SwitchableSetting<FullscreenMode, true> fullscreen_mode{
514#ifdef _WIN32 426#ifdef _WIN32
515 FullscreenMode::Borderless, 427 FullscreenMode::Borderless,
516#else 428#else
517 FullscreenMode::Exclusive, 429 FullscreenMode::Exclusive,
518#endif 430#endif
519 FullscreenMode::Borderless, FullscreenMode::Exclusive, "fullscreen_mode"}; 431 FullscreenMode::Borderless, FullscreenMode::Exclusive, "fullscreen_mode"};
520 RangedSetting<int> aspect_ratio{0, 0, 3, "aspect_ratio"}; 432 SwitchableSetting<int, true> aspect_ratio{0, 0, 3, "aspect_ratio"};
521 RangedSetting<int> max_anisotropy{0, 0, 5, "max_anisotropy"}; 433 SwitchableSetting<int, true> max_anisotropy{0, 0, 5, "max_anisotropy"};
522 Setting<bool> use_speed_limit{true, "use_speed_limit"}; 434 SwitchableSetting<bool> use_speed_limit{true, "use_speed_limit"};
523 RangedSetting<u16> speed_limit{100, 0, 9999, "speed_limit"}; 435 SwitchableSetting<u16, true> speed_limit{100, 0, 9999, "speed_limit"};
524 Setting<bool> use_disk_shader_cache{true, "use_disk_shader_cache"}; 436 SwitchableSetting<bool> use_disk_shader_cache{true, "use_disk_shader_cache"};
525 RangedSetting<GPUAccuracy> gpu_accuracy{GPUAccuracy::High, GPUAccuracy::Normal, 437 SwitchableSetting<GPUAccuracy, true> gpu_accuracy{GPUAccuracy::High, GPUAccuracy::Normal,
526 GPUAccuracy::Extreme, "gpu_accuracy"}; 438 GPUAccuracy::Extreme, "gpu_accuracy"};
527 Setting<bool> use_asynchronous_gpu_emulation{true, "use_asynchronous_gpu_emulation"}; 439 SwitchableSetting<bool> use_asynchronous_gpu_emulation{true, "use_asynchronous_gpu_emulation"};
528 Setting<NvdecEmulation> nvdec_emulation{NvdecEmulation::GPU, "nvdec_emulation"}; 440 SwitchableSetting<NvdecEmulation> nvdec_emulation{NvdecEmulation::GPU, "nvdec_emulation"};
529 Setting<bool> accelerate_astc{true, "accelerate_astc"}; 441 SwitchableSetting<bool> accelerate_astc{true, "accelerate_astc"};
530 Setting<bool> use_vsync{true, "use_vsync"}; 442 SwitchableSetting<bool> use_vsync{true, "use_vsync"};
531 RangedSetting<u16> fps_cap{1000, 1, 1000, "fps_cap"}; 443 SwitchableSetting<u16, true> fps_cap{1000, 1, 1000, "fps_cap"};
532 BasicSetting<bool> disable_fps_limit{false, "disable_fps_limit"}; 444 Setting<bool> disable_fps_limit{false, "disable_fps_limit"};
533 RangedSetting<ShaderBackend> shader_backend{ShaderBackend::GLASM, ShaderBackend::GLSL, 445 SwitchableSetting<ShaderBackend, true> shader_backend{ShaderBackend::GLASM, ShaderBackend::GLSL,
534 ShaderBackend::SPIRV, "shader_backend"}; 446 ShaderBackend::SPIRV, "shader_backend"};
535 Setting<bool> use_asynchronous_shaders{false, "use_asynchronous_shaders"}; 447 SwitchableSetting<bool> use_asynchronous_shaders{false, "use_asynchronous_shaders"};
536 Setting<bool> use_fast_gpu_time{true, "use_fast_gpu_time"}; 448 SwitchableSetting<bool> use_fast_gpu_time{true, "use_fast_gpu_time"};
537 449
538 Setting<u8> bg_red{0, "bg_red"}; 450 SwitchableSetting<u8> bg_red{0, "bg_red"};
539 Setting<u8> bg_green{0, "bg_green"}; 451 SwitchableSetting<u8> bg_green{0, "bg_green"};
540 Setting<u8> bg_blue{0, "bg_blue"}; 452 SwitchableSetting<u8> bg_blue{0, "bg_blue"};
541 453
542 // System 454 // System
543 Setting<std::optional<u32>> rng_seed{std::optional<u32>(), "rng_seed"}; 455 SwitchableSetting<std::optional<u32>> rng_seed{std::optional<u32>(), "rng_seed"};
544 // Measured in seconds since epoch 456 // Measured in seconds since epoch
545 std::optional<s64> custom_rtc; 457 std::optional<s64> custom_rtc;
546 // Set on game boot, reset on stop. Seconds difference between current time and `custom_rtc` 458 // Set on game boot, reset on stop. Seconds difference between current time and `custom_rtc`
547 s64 custom_rtc_differential; 459 s64 custom_rtc_differential;
548 460
549 BasicSetting<s32> current_user{0, "current_user"}; 461 Setting<s32> current_user{0, "current_user"};
550 RangedSetting<s32> language_index{1, 0, 17, "language_index"}; 462 SwitchableSetting<s32, true> language_index{1, 0, 17, "language_index"};
551 RangedSetting<s32> region_index{1, 0, 6, "region_index"}; 463 SwitchableSetting<s32, true> region_index{1, 0, 6, "region_index"};
552 RangedSetting<s32> time_zone_index{0, 0, 45, "time_zone_index"}; 464 SwitchableSetting<s32, true> time_zone_index{0, 0, 45, "time_zone_index"};
553 RangedSetting<s32> sound_index{1, 0, 2, "sound_index"}; 465 SwitchableSetting<s32, true> sound_index{1, 0, 2, "sound_index"};
554 466
555 // Controls 467 // Controls
556 InputSetting<std::array<PlayerInput, 10>> players; 468 InputSetting<std::array<PlayerInput, 10>> players;
557 469
558 Setting<bool> use_docked_mode{true, "use_docked_mode"}; 470 SwitchableSetting<bool> use_docked_mode{true, "use_docked_mode"};
559 471
560 BasicSetting<bool> enable_raw_input{false, "enable_raw_input"}; 472 Setting<bool> enable_raw_input{false, "enable_raw_input"};
561 BasicSetting<bool> controller_navigation{true, "controller_navigation"}; 473 Setting<bool> controller_navigation{true, "controller_navigation"};
562 474
563 Setting<bool> vibration_enabled{true, "vibration_enabled"}; 475 SwitchableSetting<bool> vibration_enabled{true, "vibration_enabled"};
564 Setting<bool> enable_accurate_vibrations{false, "enable_accurate_vibrations"}; 476 SwitchableSetting<bool> enable_accurate_vibrations{false, "enable_accurate_vibrations"};
565 477
566 Setting<bool> motion_enabled{true, "motion_enabled"}; 478 SwitchableSetting<bool> motion_enabled{true, "motion_enabled"};
567 BasicSetting<std::string> udp_input_servers{"127.0.0.1:26760", "udp_input_servers"}; 479 Setting<std::string> udp_input_servers{"127.0.0.1:26760", "udp_input_servers"};
568 BasicSetting<bool> enable_udp_controller{false, "enable_udp_controller"}; 480 Setting<bool> enable_udp_controller{false, "enable_udp_controller"};
569 481
570 BasicSetting<bool> pause_tas_on_load{true, "pause_tas_on_load"}; 482 Setting<bool> pause_tas_on_load{true, "pause_tas_on_load"};
571 BasicSetting<bool> tas_enable{false, "tas_enable"}; 483 Setting<bool> tas_enable{false, "tas_enable"};
572 BasicSetting<bool> tas_loop{false, "tas_loop"}; 484 Setting<bool> tas_loop{false, "tas_loop"};
573 485
574 BasicSetting<bool> mouse_panning{false, "mouse_panning"}; 486 Setting<bool> mouse_panning{false, "mouse_panning"};
575 BasicRangedSetting<u8> mouse_panning_sensitivity{10, 1, 100, "mouse_panning_sensitivity"}; 487 Setting<u8, true> mouse_panning_sensitivity{10, 1, 100, "mouse_panning_sensitivity"};
576 BasicSetting<bool> mouse_enabled{false, "mouse_enabled"}; 488 Setting<bool> mouse_enabled{false, "mouse_enabled"};
577 489
578 BasicSetting<bool> emulate_analog_keyboard{false, "emulate_analog_keyboard"}; 490 Setting<bool> emulate_analog_keyboard{false, "emulate_analog_keyboard"};
579 BasicSetting<bool> keyboard_enabled{false, "keyboard_enabled"}; 491 Setting<bool> keyboard_enabled{false, "keyboard_enabled"};
580 492
581 BasicSetting<bool> debug_pad_enabled{false, "debug_pad_enabled"}; 493 Setting<bool> debug_pad_enabled{false, "debug_pad_enabled"};
582 ButtonsRaw debug_pad_buttons; 494 ButtonsRaw debug_pad_buttons;
583 AnalogsRaw debug_pad_analogs; 495 AnalogsRaw debug_pad_analogs;
584 496
585 TouchscreenInput touchscreen; 497 TouchscreenInput touchscreen;
586 498
587 BasicSetting<std::string> touch_device{"min_x:100,min_y:50,max_x:1800,max_y:850", 499 Setting<std::string> touch_device{"min_x:100,min_y:50,max_x:1800,max_y:850", "touch_device"};
588 "touch_device"}; 500 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; 501 std::vector<TouchFromButtonMap> touch_from_button_maps;
591 502
592 BasicSetting<bool> enable_ring_controller{true, "enable_ring_controller"}; 503 Setting<bool> enable_ring_controller{true, "enable_ring_controller"};
593 RingconRaw ringcon_analogs; 504 RingconRaw ringcon_analogs;
594 505
595 // Data Storage 506 // Data Storage
596 BasicSetting<bool> use_virtual_sd{true, "use_virtual_sd"}; 507 Setting<bool> use_virtual_sd{true, "use_virtual_sd"};
597 BasicSetting<bool> gamecard_inserted{false, "gamecard_inserted"}; 508 Setting<bool> gamecard_inserted{false, "gamecard_inserted"};
598 BasicSetting<bool> gamecard_current_game{false, "gamecard_current_game"}; 509 Setting<bool> gamecard_current_game{false, "gamecard_current_game"};
599 BasicSetting<std::string> gamecard_path{std::string(), "gamecard_path"}; 510 Setting<std::string> gamecard_path{std::string(), "gamecard_path"};
600 511
601 // Debugging 512 // Debugging
602 bool record_frame_times; 513 bool record_frame_times;
603 BasicSetting<bool> use_gdbstub{false, "use_gdbstub"}; 514 Setting<bool> use_gdbstub{false, "use_gdbstub"};
604 BasicSetting<u16> gdbstub_port{6543, "gdbstub_port"}; 515 Setting<u16> gdbstub_port{6543, "gdbstub_port"};
605 BasicSetting<std::string> program_args{std::string(), "program_args"}; 516 Setting<std::string> program_args{std::string(), "program_args"};
606 BasicSetting<bool> dump_exefs{false, "dump_exefs"}; 517 Setting<bool> dump_exefs{false, "dump_exefs"};
607 BasicSetting<bool> dump_nso{false, "dump_nso"}; 518 Setting<bool> dump_nso{false, "dump_nso"};
608 BasicSetting<bool> dump_shaders{false, "dump_shaders"}; 519 Setting<bool> dump_shaders{false, "dump_shaders"};
609 BasicSetting<bool> dump_macros{false, "dump_macros"}; 520 Setting<bool> dump_macros{false, "dump_macros"};
610 BasicSetting<bool> enable_fs_access_log{false, "enable_fs_access_log"}; 521 Setting<bool> enable_fs_access_log{false, "enable_fs_access_log"};
611 BasicSetting<bool> reporting_services{false, "reporting_services"}; 522 Setting<bool> reporting_services{false, "reporting_services"};
612 BasicSetting<bool> quest_flag{false, "quest_flag"}; 523 Setting<bool> quest_flag{false, "quest_flag"};
613 BasicSetting<bool> disable_macro_jit{false, "disable_macro_jit"}; 524 Setting<bool> disable_macro_jit{false, "disable_macro_jit"};
614 BasicSetting<bool> extended_logging{false, "extended_logging"}; 525 Setting<bool> extended_logging{false, "extended_logging"};
615 BasicSetting<bool> use_debug_asserts{false, "use_debug_asserts"}; 526 Setting<bool> use_debug_asserts{false, "use_debug_asserts"};
616 BasicSetting<bool> use_auto_stub{false, "use_auto_stub"}; 527 Setting<bool> use_auto_stub{false, "use_auto_stub"};
617 BasicSetting<bool> enable_all_controllers{false, "enable_all_controllers"}; 528 Setting<bool> enable_all_controllers{false, "enable_all_controllers"};
618 529
619 // Miscellaneous 530 // Miscellaneous
620 BasicSetting<std::string> log_filter{"*:Info", "log_filter"}; 531 Setting<std::string> log_filter{"*:Info", "log_filter"};
621 BasicSetting<bool> use_dev_keys{false, "use_dev_keys"}; 532 Setting<bool> use_dev_keys{false, "use_dev_keys"};
622 533
623 // Network 534 // Network
624 BasicSetting<std::string> network_interface{std::string(), "network_interface"}; 535 Setting<std::string> network_interface{std::string(), "network_interface"};
625 536
626 // WebService 537 // WebService
627 BasicSetting<bool> enable_telemetry{true, "enable_telemetry"}; 538 Setting<bool> enable_telemetry{true, "enable_telemetry"};
628 BasicSetting<std::string> web_api_url{"https://api.yuzu-emu.org", "web_api_url"}; 539 Setting<std::string> web_api_url{"https://api.yuzu-emu.org", "web_api_url"};
629 BasicSetting<std::string> yuzu_username{std::string(), "yuzu_username"}; 540 Setting<std::string> yuzu_username{std::string(), "yuzu_username"};
630 BasicSetting<std::string> yuzu_token{std::string(), "yuzu_token"}; 541 Setting<std::string> yuzu_token{std::string(), "yuzu_token"};
631 542
632 // Add-Ons 543 // Add-Ons
633 std::map<u64, std::vector<std::string>> disabled_addons; 544 std::map<u64, std::vector<std::string>> disabled_addons;
diff --git a/src/common/wall_clock.cpp b/src/common/wall_clock.cpp
index b4fb3a59f..ae07f2811 100644
--- a/src/common/wall_clock.cpp
+++ b/src/common/wall_clock.cpp
@@ -67,7 +67,7 @@ std::unique_ptr<WallClock> CreateBestMatchingClock(u64 emulated_cpu_frequency,
67 const auto& caps = GetCPUCaps(); 67 const auto& caps = GetCPUCaps();
68 u64 rtsc_frequency = 0; 68 u64 rtsc_frequency = 0;
69 if (caps.invariant_tsc) { 69 if (caps.invariant_tsc) {
70 rtsc_frequency = EstimateRDTSCFrequency(); 70 rtsc_frequency = caps.tsc_frequency ? caps.tsc_frequency : EstimateRDTSCFrequency();
71 } 71 }
72 72
73 // Fallback to StandardWallClock if the hardware TSC does not have the precision greater than: 73 // Fallback to StandardWallClock if the hardware TSC does not have the precision greater than:
diff --git a/src/common/x64/cpu_detect.cpp b/src/common/x64/cpu_detect.cpp
index 322aa1f08..1a27532d4 100644
--- a/src/common/x64/cpu_detect.cpp
+++ b/src/common/x64/cpu_detect.cpp
@@ -161,6 +161,22 @@ static CPUCaps Detect() {
161 caps.invariant_tsc = Common::Bit<8>(cpu_id[3]); 161 caps.invariant_tsc = Common::Bit<8>(cpu_id[3]);
162 } 162 }
163 163
164 if (max_std_fn >= 0x15) {
165 __cpuid(cpu_id, 0x15);
166 caps.tsc_crystal_ratio_denominator = cpu_id[0];
167 caps.tsc_crystal_ratio_numerator = cpu_id[1];
168 caps.crystal_frequency = cpu_id[2];
169 // Some CPU models might not return a crystal frequency.
170 // The CPU model can be detected to use the values from turbostat
171 // https://github.com/torvalds/linux/blob/master/tools/power/x86/turbostat/turbostat.c#L5569
172 // but it's easier to just estimate the TSC tick rate for these cases.
173 if (caps.tsc_crystal_ratio_denominator) {
174 caps.tsc_frequency = static_cast<u64>(caps.crystal_frequency) *
175 caps.tsc_crystal_ratio_numerator /
176 caps.tsc_crystal_ratio_denominator;
177 }
178 }
179
164 if (max_std_fn >= 0x16) { 180 if (max_std_fn >= 0x16) {
165 __cpuid(cpu_id, 0x16); 181 __cpuid(cpu_id, 0x16);
166 caps.base_frequency = cpu_id[0]; 182 caps.base_frequency = cpu_id[0];
diff --git a/src/common/x64/cpu_detect.h b/src/common/x64/cpu_detect.h
index 9bdc9dbfa..6830f3795 100644
--- a/src/common/x64/cpu_detect.h
+++ b/src/common/x64/cpu_detect.h
@@ -30,6 +30,11 @@ struct CPUCaps {
30 u32 max_frequency; 30 u32 max_frequency;
31 u32 bus_frequency; 31 u32 bus_frequency;
32 32
33 u32 tsc_crystal_ratio_denominator;
34 u32 tsc_crystal_ratio_numerator;
35 u32 crystal_frequency;
36 u64 tsc_frequency; // Derived from the above three values
37
33 bool sse : 1; 38 bool sse : 1;
34 bool sse2 : 1; 39 bool sse2 : 1;
35 bool sse3 : 1; 40 bool sse3 : 1;
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index d9357138f..11d554bad 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -605,6 +605,10 @@ add_library(core STATIC
605 hle/service/psc/psc.h 605 hle/service/psc/psc.h
606 hle/service/ptm/psm.cpp 606 hle/service/ptm/psm.cpp
607 hle/service/ptm/psm.h 607 hle/service/ptm/psm.h
608 hle/service/ptm/ptm.cpp
609 hle/service/ptm/ptm.h
610 hle/service/ptm/ts.cpp
611 hle/service/ptm/ts.h
608 hle/service/kernel_helpers.cpp 612 hle/service/kernel_helpers.cpp
609 hle/service/kernel_helpers.h 613 hle/service/kernel_helpers.h
610 hle/service/service.cpp 614 hle/service/service.cpp
diff --git a/src/core/arm/arm_interface.cpp b/src/core/arm/arm_interface.cpp
index 8e095cdcd..cef79b245 100644
--- a/src/core/arm/arm_interface.cpp
+++ b/src/core/arm/arm_interface.cpp
@@ -1,6 +1,10 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#ifndef _MSC_VER
5#include <cxxabi.h>
6#endif
7
4#include <map> 8#include <map>
5#include <optional> 9#include <optional>
6#include "common/bit_field.h" 10#include "common/bit_field.h"
@@ -68,8 +72,19 @@ void ARM_Interface::SymbolicateBacktrace(Core::System& system, std::vector<Backt
68 if (symbol_set != symbols.end()) { 72 if (symbol_set != symbols.end()) {
69 const auto symbol = Symbols::GetSymbolName(symbol_set->second, entry.offset); 73 const auto symbol = Symbols::GetSymbolName(symbol_set->second, entry.offset);
70 if (symbol.has_value()) { 74 if (symbol.has_value()) {
75#ifdef _MSC_VER
71 // TODO(DarkLordZach): Add demangling of symbol names. 76 // TODO(DarkLordZach): Add demangling of symbol names.
72 entry.name = *symbol; 77 entry.name = *symbol;
78#else
79 int status{-1};
80 char* demangled{abi::__cxa_demangle(symbol->c_str(), nullptr, nullptr, &status)};
81 if (status == 0 && demangled != nullptr) {
82 entry.name = demangled;
83 std::free(demangled);
84 } else {
85 entry.name = *symbol;
86 }
87#endif
73 } 88 }
74 } 89 }
75 } 90 }
@@ -119,16 +134,23 @@ void ARM_Interface::Run() {
119 } 134 }
120 system.ExitDynarmicProfile(); 135 system.ExitDynarmicProfile();
121 136
122 // Notify the debugger and go to sleep if a breakpoint was hit. 137 // Notify the debugger and go to sleep if a breakpoint was hit,
123 if (Has(hr, breakpoint)) { 138 // or if the thread is unable to continue for any reason.
139 if (Has(hr, breakpoint) || Has(hr, no_execute)) {
124 RewindBreakpointInstruction(); 140 RewindBreakpointInstruction();
125 system.GetDebugger().NotifyThreadStopped(current_thread); 141 if (system.DebuggerEnabled()) {
126 current_thread->RequestSuspend(SuspendType::Debug); 142 system.GetDebugger().NotifyThreadStopped(current_thread);
143 }
144 current_thread->RequestSuspend(Kernel::SuspendType::Debug);
127 break; 145 break;
128 } 146 }
147
148 // Notify the debugger and go to sleep if a watchpoint was hit.
129 if (Has(hr, watchpoint)) { 149 if (Has(hr, watchpoint)) {
130 RewindBreakpointInstruction(); 150 RewindBreakpointInstruction();
131 system.GetDebugger().NotifyThreadWatchpoint(current_thread, *HaltedWatchpoint()); 151 if (system.DebuggerEnabled()) {
152 system.GetDebugger().NotifyThreadWatchpoint(current_thread, *HaltedWatchpoint());
153 }
132 current_thread->RequestSuspend(SuspendType::Debug); 154 current_thread->RequestSuspend(SuspendType::Debug);
133 break; 155 break;
134 } 156 }
diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h
index 4e431e27a..8a066ed91 100644
--- a/src/core/arm/arm_interface.h
+++ b/src/core/arm/arm_interface.h
@@ -204,6 +204,7 @@ public:
204 static constexpr Dynarmic::HaltReason svc_call = Dynarmic::HaltReason::UserDefined3; 204 static constexpr Dynarmic::HaltReason svc_call = Dynarmic::HaltReason::UserDefined3;
205 static constexpr Dynarmic::HaltReason breakpoint = Dynarmic::HaltReason::UserDefined4; 205 static constexpr Dynarmic::HaltReason breakpoint = Dynarmic::HaltReason::UserDefined4;
206 static constexpr Dynarmic::HaltReason watchpoint = Dynarmic::HaltReason::UserDefined5; 206 static constexpr Dynarmic::HaltReason watchpoint = Dynarmic::HaltReason::UserDefined5;
207 static constexpr Dynarmic::HaltReason no_execute = Dynarmic::HaltReason::UserDefined6;
207 208
208protected: 209protected:
209 /// System context that this ARM interface is running under. 210 /// System context that this ARM interface is running under.
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
index 8c90c8be0..a780cf800 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
@@ -48,6 +48,12 @@ public:
48 CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Read); 48 CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Read);
49 return memory.Read64(vaddr); 49 return memory.Read64(vaddr);
50 } 50 }
51 std::optional<u32> MemoryReadCode(u32 vaddr) override {
52 if (!memory.IsValidVirtualAddressRange(vaddr, sizeof(u32))) {
53 return std::nullopt;
54 }
55 return memory.Read32(vaddr);
56 }
51 57
52 void MemoryWrite8(u32 vaddr, u8 value) override { 58 void MemoryWrite8(u32 vaddr, u8 value) override {
53 if (CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write)) { 59 if (CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write)) {
@@ -89,21 +95,28 @@ public:
89 95
90 void InterpreterFallback(u32 pc, std::size_t num_instructions) override { 96 void InterpreterFallback(u32 pc, std::size_t num_instructions) override {
91 parent.LogBacktrace(); 97 parent.LogBacktrace();
92 UNIMPLEMENTED_MSG("This should never happen, pc = {:08X}, code = {:08X}", pc, 98 LOG_ERROR(Core_ARM,
93 MemoryReadCode(pc)); 99 "Unimplemented instruction @ 0x{:X} for {} instructions (instr = {:08X})", pc,
100 num_instructions, memory.Read32(pc));
94 } 101 }
95 102
96 void ExceptionRaised(u32 pc, Dynarmic::A32::Exception exception) override { 103 void ExceptionRaised(u32 pc, Dynarmic::A32::Exception exception) override {
97 if (debugger_enabled) { 104 switch (exception) {
98 parent.SaveContext(parent.breakpoint_context); 105 case Dynarmic::A32::Exception::NoExecuteFault:
99 parent.jit.load()->HaltExecution(ARM_Interface::breakpoint); 106 LOG_CRITICAL(Core_ARM, "Cannot execute instruction at unmapped address {:#08x}", pc);
107 ReturnException(pc, ARM_Interface::no_execute);
100 return; 108 return;
101 } 109 default:
110 if (debugger_enabled) {
111 ReturnException(pc, ARM_Interface::breakpoint);
112 return;
113 }
102 114
103 parent.LogBacktrace(); 115 parent.LogBacktrace();
104 LOG_CRITICAL(Core_ARM, 116 LOG_CRITICAL(Core_ARM,
105 "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X}, thumb = {})", 117 "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X}, thumb = {})",
106 exception, pc, MemoryReadCode(pc), parent.IsInThumbMode()); 118 exception, pc, memory.Read32(pc), parent.IsInThumbMode());
119 }
107 } 120 }
108 121
109 void CallSVC(u32 swi) override { 122 void CallSVC(u32 swi) override {
@@ -141,15 +154,20 @@ public:
141 154
142 const auto match{parent.MatchingWatchpoint(addr, size, type)}; 155 const auto match{parent.MatchingWatchpoint(addr, size, type)};
143 if (match) { 156 if (match) {
144 parent.SaveContext(parent.breakpoint_context);
145 parent.jit.load()->HaltExecution(ARM_Interface::watchpoint);
146 parent.halted_watchpoint = match; 157 parent.halted_watchpoint = match;
158 ReturnException(parent.jit.load()->Regs()[15], ARM_Interface::watchpoint);
147 return false; 159 return false;
148 } 160 }
149 161
150 return true; 162 return true;
151 } 163 }
152 164
165 void ReturnException(u32 pc, Dynarmic::HaltReason hr) {
166 parent.SaveContext(parent.breakpoint_context);
167 parent.breakpoint_context.cpu_registers[15] = pc;
168 parent.jit.load()->HaltExecution(hr);
169 }
170
153 ARM_Dynarmic_32& parent; 171 ARM_Dynarmic_32& parent;
154 Core::Memory::Memory& memory; 172 Core::Memory::Memory& memory;
155 std::size_t num_interpreted_instructions{}; 173 std::size_t num_interpreted_instructions{};
@@ -409,18 +427,38 @@ void ARM_Dynarmic_32::PageTableChanged(Common::PageTable& page_table,
409} 427}
410 428
411std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_32::GetBacktrace(Core::System& system, 429std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_32::GetBacktrace(Core::System& system,
412 u64 sp, u64 lr) { 430 u64 fp, u64 lr, u64 pc) {
413 // No way to get accurate stack traces in A32 yet 431 std::vector<BacktraceEntry> out;
414 return {}; 432 auto& memory = system.Memory();
433
434 out.push_back({"", 0, pc, 0, ""});
435
436 // fp (= r11) points to the last frame record.
437 // Frame records are two words long:
438 // fp+0 : pointer to previous frame record
439 // fp+4 : value of lr for frame
440 while (true) {
441 out.push_back({"", 0, lr, 0, ""});
442 if (!fp || (fp % 4 != 0) || !memory.IsValidVirtualAddressRange(fp, 8)) {
443 break;
444 }
445 lr = memory.Read32(fp + 4);
446 fp = memory.Read32(fp);
447 }
448
449 SymbolicateBacktrace(system, out);
450
451 return out;
415} 452}
416 453
417std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_32::GetBacktraceFromContext( 454std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_32::GetBacktraceFromContext(
418 System& system, const ThreadContext32& ctx) { 455 System& system, const ThreadContext32& ctx) {
419 return GetBacktrace(system, ctx.cpu_registers[13], ctx.cpu_registers[14]); 456 const auto& reg = ctx.cpu_registers;
457 return GetBacktrace(system, reg[11], reg[14], reg[15]);
420} 458}
421 459
422std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_32::GetBacktrace() const { 460std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_32::GetBacktrace() const {
423 return GetBacktrace(system, GetReg(13), GetReg(14)); 461 return GetBacktrace(system, GetReg(11), GetReg(14), GetReg(15));
424} 462}
425 463
426} // namespace Core 464} // namespace Core
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.h b/src/core/arm/dynarmic/arm_dynarmic_32.h
index fcbe24f0c..346e9abf8 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_32.h
+++ b/src/core/arm/dynarmic/arm_dynarmic_32.h
@@ -78,7 +78,7 @@ protected:
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 4370ca294..1a75312a4 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
@@ -52,6 +52,12 @@ public:
52 CheckMemoryAccess(vaddr, 16, Kernel::DebugWatchpointType::Read); 52 CheckMemoryAccess(vaddr, 16, Kernel::DebugWatchpointType::Read);
53 return {memory.Read64(vaddr), memory.Read64(vaddr + 8)}; 53 return {memory.Read64(vaddr), memory.Read64(vaddr + 8)};
54 } 54 }
55 std::optional<u32> MemoryReadCode(u64 vaddr) override {
56 if (!memory.IsValidVirtualAddressRange(vaddr, sizeof(u32))) {
57 return std::nullopt;
58 }
59 return memory.Read32(vaddr);
60 }
55 61
56 void MemoryWrite8(u64 vaddr, u8 value) override { 62 void MemoryWrite8(u64 vaddr, u8 value) override {
57 if (CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write)) { 63 if (CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write)) {
@@ -105,7 +111,7 @@ public:
105 parent.LogBacktrace(); 111 parent.LogBacktrace();
106 LOG_ERROR(Core_ARM, 112 LOG_ERROR(Core_ARM,
107 "Unimplemented instruction @ 0x{:X} for {} instructions (instr = {:08X})", pc, 113 "Unimplemented instruction @ 0x{:X} for {} instructions (instr = {:08X})", pc,
108 num_instructions, MemoryReadCode(pc)); 114 num_instructions, memory.Read32(pc));
109 } 115 }
110 116
111 void InstructionCacheOperationRaised(Dynarmic::A64::InstructionCacheOperation op, 117 void InstructionCacheOperationRaised(Dynarmic::A64::InstructionCacheOperation op,
@@ -138,16 +144,19 @@ public:
138 case Dynarmic::A64::Exception::SendEventLocal: 144 case Dynarmic::A64::Exception::SendEventLocal:
139 case Dynarmic::A64::Exception::Yield: 145 case Dynarmic::A64::Exception::Yield:
140 return; 146 return;
147 case Dynarmic::A64::Exception::NoExecuteFault:
148 LOG_CRITICAL(Core_ARM, "Cannot execute instruction at unmapped address {:#016x}", pc);
149 ReturnException(pc, ARM_Interface::no_execute);
150 return;
141 default: 151 default:
142 if (debugger_enabled) { 152 if (debugger_enabled) {
143 parent.SaveContext(parent.breakpoint_context); 153 ReturnException(pc, ARM_Interface::breakpoint);
144 parent.jit.load()->HaltExecution(ARM_Interface::breakpoint);
145 return; 154 return;
146 } 155 }
147 156
148 parent.LogBacktrace(); 157 parent.LogBacktrace();
149 ASSERT_MSG(false, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X})", 158 LOG_CRITICAL(Core_ARM, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X})",
150 static_cast<std::size_t>(exception), pc, MemoryReadCode(pc)); 159 static_cast<std::size_t>(exception), pc, memory.Read32(pc));
151 } 160 }
152 } 161 }
153 162
@@ -188,15 +197,20 @@ public:
188 197
189 const auto match{parent.MatchingWatchpoint(addr, size, type)}; 198 const auto match{parent.MatchingWatchpoint(addr, size, type)};
190 if (match) { 199 if (match) {
191 parent.SaveContext(parent.breakpoint_context);
192 parent.jit.load()->HaltExecution(ARM_Interface::watchpoint);
193 parent.halted_watchpoint = match; 200 parent.halted_watchpoint = match;
201 ReturnException(parent.jit.load()->GetPC(), ARM_Interface::watchpoint);
194 return false; 202 return false;
195 } 203 }
196 204
197 return true; 205 return true;
198 } 206 }
199 207
208 void ReturnException(u64 pc, Dynarmic::HaltReason hr) {
209 parent.SaveContext(parent.breakpoint_context);
210 parent.breakpoint_context.pc = pc;
211 parent.jit.load()->HaltExecution(hr);
212 }
213
200 ARM_Dynarmic_64& parent; 214 ARM_Dynarmic_64& parent;
201 Core::Memory::Memory& memory; 215 Core::Memory::Memory& memory;
202 u64 tpidrro_el0 = 0; 216 u64 tpidrro_el0 = 0;
@@ -480,22 +494,22 @@ void ARM_Dynarmic_64::PageTableChanged(Common::PageTable& page_table,
480} 494}
481 495
482std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_64::GetBacktrace(Core::System& system, 496std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_64::GetBacktrace(Core::System& system,
483 u64 fp, u64 lr) { 497 u64 fp, u64 lr, u64 pc) {
484 std::vector<BacktraceEntry> out; 498 std::vector<BacktraceEntry> out;
485 auto& memory = system.Memory(); 499 auto& memory = system.Memory();
486 500
487 // fp (= r29) points to the last frame record. 501 out.push_back({"", 0, pc, 0, ""});
488 // Note that this is the frame record for the *previous* frame, not the current one. 502
489 // Note we need to subtract 4 from our last read to get the proper address 503 // fp (= x29) points to the previous frame record.
490 // Frame records are two words long: 504 // Frame records are two words long:
491 // fp+0 : pointer to previous frame record 505 // fp+0 : pointer to previous frame record
492 // fp+8 : value of lr for frame 506 // fp+8 : value of lr for frame
493 while (true) { 507 while (true) {
494 out.push_back({"", 0, lr, 0, ""}); 508 out.push_back({"", 0, lr, 0, ""});
495 if (!fp) { 509 if (!fp || (fp % 4 != 0) || !memory.IsValidVirtualAddressRange(fp, 16)) {
496 break; 510 break;
497 } 511 }
498 lr = memory.Read64(fp + 8) - 4; 512 lr = memory.Read64(fp + 8);
499 fp = memory.Read64(fp); 513 fp = memory.Read64(fp);
500 } 514 }
501 515
@@ -506,11 +520,12 @@ std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_64::GetBacktrace(Core::S
506 520
507std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_64::GetBacktraceFromContext( 521std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_64::GetBacktraceFromContext(
508 System& system, const ThreadContext64& ctx) { 522 System& system, const ThreadContext64& ctx) {
509 return GetBacktrace(system, ctx.cpu_registers[29], ctx.cpu_registers[30]); 523 const auto& reg = ctx.cpu_registers;
524 return GetBacktrace(system, reg[29], reg[30], ctx.pc);
510} 525}
511 526
512std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_64::GetBacktrace() const { 527std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_64::GetBacktrace() const {
513 return GetBacktrace(system, GetReg(29), GetReg(30)); 528 return GetBacktrace(system, GetReg(29), GetReg(30), GetPC());
514} 529}
515 530
516} // namespace Core 531} // namespace Core
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.h b/src/core/arm/dynarmic/arm_dynarmic_64.h
index 71dbaac5e..c77a83ad7 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_64.h
+++ b/src/core/arm/dynarmic/arm_dynarmic_64.h
@@ -73,7 +73,7 @@ private:
73 std::shared_ptr<Dynarmic::A64::Jit> MakeJit(Common::PageTable* page_table, 73 std::shared_ptr<Dynarmic::A64::Jit> MakeJit(Common::PageTable* page_table,
74 std::size_t address_space_bits) const; 74 std::size_t address_space_bits) const;
75 75
76 static std::vector<BacktraceEntry> GetBacktrace(Core::System& system, u64 fp, u64 lr); 76 static std::vector<BacktraceEntry> GetBacktrace(Core::System& system, u64 fp, u64 lr, u64 pc);
77 77
78 using JitCacheKey = std::pair<Common::PageTable*, std::size_t>; 78 using JitCacheKey = std::pair<Common::PageTable*, std::size_t>;
79 using JitCacheType = 79 using JitCacheType =
diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp
index 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/cpu_manager.cpp b/src/core/cpu_manager.cpp
index fd6928105..37d3d83b9 100644
--- a/src/core/cpu_manager.cpp
+++ b/src/core/cpu_manager.cpp
@@ -41,51 +41,32 @@ void CpuManager::Shutdown() {
41 } 41 }
42} 42}
43 43
44std::function<void(void*)> CpuManager::GetGuestThreadStartFunc() { 44void CpuManager::GuestThreadFunction() {
45 return GuestThreadFunction; 45 if (is_multicore) {
46} 46 MultiCoreRunGuestThread();
47
48std::function<void(void*)> CpuManager::GetIdleThreadStartFunc() {
49 return IdleThreadFunction;
50}
51
52std::function<void(void*)> CpuManager::GetShutdownThreadStartFunc() {
53 return ShutdownThreadFunction;
54}
55
56void CpuManager::GuestThreadFunction(void* cpu_manager_) {
57 CpuManager* cpu_manager = static_cast<CpuManager*>(cpu_manager_);
58 if (cpu_manager->is_multicore) {
59 cpu_manager->MultiCoreRunGuestThread();
60 } else { 47 } else {
61 cpu_manager->SingleCoreRunGuestThread(); 48 SingleCoreRunGuestThread();
62 } 49 }
63} 50}
64 51
65void CpuManager::GuestRewindFunction(void* cpu_manager_) { 52void CpuManager::GuestRewindFunction() {
66 CpuManager* cpu_manager = static_cast<CpuManager*>(cpu_manager_); 53 if (is_multicore) {
67 if (cpu_manager->is_multicore) { 54 MultiCoreRunGuestLoop();
68 cpu_manager->MultiCoreRunGuestLoop();
69 } else { 55 } else {
70 cpu_manager->SingleCoreRunGuestLoop(); 56 SingleCoreRunGuestLoop();
71 } 57 }
72} 58}
73 59
74void CpuManager::IdleThreadFunction(void* cpu_manager_) { 60void CpuManager::IdleThreadFunction() {
75 CpuManager* cpu_manager = static_cast<CpuManager*>(cpu_manager_); 61 if (is_multicore) {
76 if (cpu_manager->is_multicore) { 62 MultiCoreRunIdleThread();
77 cpu_manager->MultiCoreRunIdleThread();
78 } else { 63 } else {
79 cpu_manager->SingleCoreRunIdleThread(); 64 SingleCoreRunIdleThread();
80 } 65 }
81} 66}
82 67
83void CpuManager::ShutdownThreadFunction(void* cpu_manager) { 68void CpuManager::ShutdownThreadFunction() {
84 static_cast<CpuManager*>(cpu_manager)->ShutdownThread(); 69 ShutdownThread();
85}
86
87void* CpuManager::GetStartFuncParameter() {
88 return this;
89} 70}
90 71
91/////////////////////////////////////////////////////////////////////////////// 72///////////////////////////////////////////////////////////////////////////////
@@ -97,7 +78,7 @@ void CpuManager::MultiCoreRunGuestThread() {
97 kernel.CurrentScheduler()->OnThreadStart(); 78 kernel.CurrentScheduler()->OnThreadStart();
98 auto* thread = kernel.CurrentScheduler()->GetSchedulerCurrentThread(); 79 auto* thread = kernel.CurrentScheduler()->GetSchedulerCurrentThread();
99 auto& host_context = thread->GetHostContext(); 80 auto& host_context = thread->GetHostContext();
100 host_context->SetRewindPoint(GuestRewindFunction, this); 81 host_context->SetRewindPoint([this] { GuestRewindFunction(); });
101 MultiCoreRunGuestLoop(); 82 MultiCoreRunGuestLoop();
102} 83}
103 84
@@ -134,7 +115,7 @@ void CpuManager::SingleCoreRunGuestThread() {
134 kernel.CurrentScheduler()->OnThreadStart(); 115 kernel.CurrentScheduler()->OnThreadStart();
135 auto* thread = kernel.CurrentScheduler()->GetSchedulerCurrentThread(); 116 auto* thread = kernel.CurrentScheduler()->GetSchedulerCurrentThread();
136 auto& host_context = thread->GetHostContext(); 117 auto& host_context = thread->GetHostContext();
137 host_context->SetRewindPoint(GuestRewindFunction, this); 118 host_context->SetRewindPoint([this] { GuestRewindFunction(); });
138 SingleCoreRunGuestLoop(); 119 SingleCoreRunGuestLoop();
139} 120}
140 121
@@ -194,7 +175,9 @@ void CpuManager::PreemptSingleCore(bool from_running_enviroment) {
194 { 175 {
195 auto& scheduler = system.Kernel().Scheduler(current_core); 176 auto& scheduler = system.Kernel().Scheduler(current_core);
196 scheduler.Reload(scheduler.GetSchedulerCurrentThread()); 177 scheduler.Reload(scheduler.GetSchedulerCurrentThread());
197 idle_count = 0; 178 if (!scheduler.IsIdle()) {
179 idle_count = 0;
180 }
198 } 181 }
199} 182}
200 183
diff --git a/src/core/cpu_manager.h b/src/core/cpu_manager.h
index f0751fc58..76dc58ee1 100644
--- a/src/core/cpu_manager.h
+++ b/src/core/cpu_manager.h
@@ -50,10 +50,15 @@ public:
50 void Initialize(); 50 void Initialize();
51 void Shutdown(); 51 void Shutdown();
52 52
53 static std::function<void(void*)> GetGuestThreadStartFunc(); 53 std::function<void()> GetGuestThreadStartFunc() {
54 static std::function<void(void*)> GetIdleThreadStartFunc(); 54 return [this] { GuestThreadFunction(); };
55 static std::function<void(void*)> GetShutdownThreadStartFunc(); 55 }
56 void* GetStartFuncParameter(); 56 std::function<void()> GetIdleThreadStartFunc() {
57 return [this] { IdleThreadFunction(); };
58 }
59 std::function<void()> GetShutdownThreadStartFunc() {
60 return [this] { ShutdownThreadFunction(); };
61 }
57 62
58 void PreemptSingleCore(bool from_running_enviroment = true); 63 void PreemptSingleCore(bool from_running_enviroment = true);
59 64
@@ -62,10 +67,10 @@ public:
62 } 67 }
63 68
64private: 69private:
65 static void GuestThreadFunction(void* cpu_manager); 70 void GuestThreadFunction();
66 static void GuestRewindFunction(void* cpu_manager); 71 void GuestRewindFunction();
67 static void IdleThreadFunction(void* cpu_manager); 72 void IdleThreadFunction();
68 static void ShutdownThreadFunction(void* cpu_manager); 73 void ShutdownThreadFunction();
69 74
70 void MultiCoreRunGuestThread(); 75 void MultiCoreRunGuestThread();
71 void MultiCoreRunGuestLoop(); 76 void MultiCoreRunGuestLoop();
diff --git a/src/core/debugger/gdbstub_arch.cpp b/src/core/debugger/gdbstub_arch.cpp
index 750c353b9..4bef09bd7 100644
--- a/src/core/debugger/gdbstub_arch.cpp
+++ b/src/core/debugger/gdbstub_arch.cpp
@@ -191,8 +191,10 @@ std::string GDBStubA64::RegRead(const Kernel::KThread* thread, size_t id) const
191 const auto& gprs{context.cpu_registers}; 191 const auto& gprs{context.cpu_registers};
192 const auto& fprs{context.vector_registers}; 192 const auto& fprs{context.vector_registers};
193 193
194 if (id <= SP_REGISTER) { 194 if (id < SP_REGISTER) {
195 return ValueToHex(gprs[id]); 195 return ValueToHex(gprs[id]);
196 } else if (id == SP_REGISTER) {
197 return ValueToHex(context.sp);
196 } else if (id == PC_REGISTER) { 198 } else if (id == PC_REGISTER) {
197 return ValueToHex(context.pc); 199 return ValueToHex(context.pc);
198 } else if (id == PSTATE_REGISTER) { 200 } else if (id == PSTATE_REGISTER) {
@@ -215,8 +217,10 @@ void GDBStubA64::RegWrite(Kernel::KThread* thread, size_t id, std::string_view v
215 217
216 auto& context{thread->GetContext64()}; 218 auto& context{thread->GetContext64()};
217 219
218 if (id <= SP_REGISTER) { 220 if (id < SP_REGISTER) {
219 context.cpu_registers[id] = HexToValue<u64>(value); 221 context.cpu_registers[id] = HexToValue<u64>(value);
222 } else if (id == SP_REGISTER) {
223 context.sp = HexToValue<u64>(value);
220 } else if (id == PC_REGISTER) { 224 } else if (id == PC_REGISTER) {
221 context.pc = HexToValue<u64>(value); 225 context.pc = HexToValue<u64>(value);
222 } else if (id == PSTATE_REGISTER) { 226 } else if (id == PSTATE_REGISTER) {
diff --git a/src/core/hardware_interrupt_manager.cpp b/src/core/hardware_interrupt_manager.cpp
index d2d968a76..d08cc3315 100644
--- a/src/core/hardware_interrupt_manager.cpp
+++ b/src/core/hardware_interrupt_manager.cpp
@@ -11,11 +11,14 @@ namespace Core::Hardware {
11 11
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_code_memory.h b/src/core/hle/kernel/k_code_memory.h
index 2410f74a3..2e7e1436a 100644
--- a/src/core/hle/kernel/k_code_memory.h
+++ b/src/core/hle/kernel/k_code_memory.h
@@ -30,19 +30,19 @@ public:
30 explicit KCodeMemory(KernelCore& kernel_); 30 explicit KCodeMemory(KernelCore& kernel_);
31 31
32 Result Initialize(Core::DeviceMemory& device_memory, VAddr address, size_t size); 32 Result Initialize(Core::DeviceMemory& device_memory, VAddr address, size_t size);
33 void Finalize(); 33 void Finalize() override;
34 34
35 Result Map(VAddr address, size_t size); 35 Result Map(VAddr address, size_t size);
36 Result Unmap(VAddr address, size_t size); 36 Result Unmap(VAddr address, size_t size);
37 Result MapToOwner(VAddr address, size_t size, Svc::MemoryPermission perm); 37 Result MapToOwner(VAddr address, size_t size, Svc::MemoryPermission perm);
38 Result UnmapFromOwner(VAddr address, size_t size); 38 Result UnmapFromOwner(VAddr address, size_t size);
39 39
40 bool IsInitialized() const { 40 bool IsInitialized() const override {
41 return m_is_initialized; 41 return m_is_initialized;
42 } 42 }
43 static void PostDestroy([[maybe_unused]] uintptr_t arg) {} 43 static void PostDestroy([[maybe_unused]] uintptr_t arg) {}
44 44
45 KProcess* GetOwner() const { 45 KProcess* GetOwner() const override {
46 return m_owner; 46 return m_owner;
47 } 47 }
48 VAddr GetSourceAddress() const { 48 VAddr GetSourceAddress() const {
diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp
index 183c693e3..b662788b3 100644
--- a/src/core/hle/kernel/k_process.cpp
+++ b/src/core/hle/kernel/k_process.cpp
@@ -161,7 +161,7 @@ bool KProcess::ReleaseUserException(KThread* thread) {
161 std::addressof(num_waiters), 161 std::addressof(num_waiters),
162 reinterpret_cast<uintptr_t>(std::addressof(exception_thread))); 162 reinterpret_cast<uintptr_t>(std::addressof(exception_thread)));
163 next != nullptr) { 163 next != nullptr) {
164 next->SetState(ThreadState::Runnable); 164 next->EndWait(ResultSuccess);
165 } 165 }
166 166
167 KScheduler::SetSchedulerUpdateNeeded(kernel); 167 KScheduler::SetSchedulerUpdateNeeded(kernel);
diff --git a/src/core/hle/kernel/k_scheduler.cpp b/src/core/hle/kernel/k_scheduler.cpp
index d586b3f5c..d599d2bcb 100644
--- a/src/core/hle/kernel/k_scheduler.cpp
+++ b/src/core/hle/kernel/k_scheduler.cpp
@@ -622,7 +622,7 @@ void KScheduler::YieldToAnyThread(KernelCore& kernel) {
622} 622}
623 623
624KScheduler::KScheduler(Core::System& system_, s32 core_id_) : system{system_}, core_id{core_id_} { 624KScheduler::KScheduler(Core::System& system_, s32 core_id_) : system{system_}, core_id{core_id_} {
625 switch_fiber = std::make_shared<Common::Fiber>(OnSwitch, this); 625 switch_fiber = std::make_shared<Common::Fiber>([this] { SwitchToCurrent(); });
626 state.needs_scheduling.store(true); 626 state.needs_scheduling.store(true);
627 state.interrupt_task_thread_runnable = false; 627 state.interrupt_task_thread_runnable = false;
628 state.should_count_idle = false; 628 state.should_count_idle = false;
@@ -778,11 +778,6 @@ void KScheduler::ScheduleImpl() {
778 next_scheduler.SwitchContextStep2(); 778 next_scheduler.SwitchContextStep2();
779} 779}
780 780
781void KScheduler::OnSwitch(void* this_scheduler) {
782 KScheduler* sched = static_cast<KScheduler*>(this_scheduler);
783 sched->SwitchToCurrent();
784}
785
786void KScheduler::SwitchToCurrent() { 781void KScheduler::SwitchToCurrent() {
787 while (true) { 782 while (true) {
788 { 783 {
diff --git a/src/core/hle/kernel/k_scheduler.h b/src/core/hle/kernel/k_scheduler.h
index 3f90656ee..6a4760eca 100644
--- a/src/core/hle/kernel/k_scheduler.h
+++ b/src/core/hle/kernel/k_scheduler.h
@@ -55,6 +55,11 @@ public:
55 return idle_thread; 55 return idle_thread;
56 } 56 }
57 57
58 /// Returns true if the scheduler is idle
59 [[nodiscard]] bool IsIdle() const {
60 return GetSchedulerCurrentThread() == idle_thread;
61 }
62
58 /// Gets the timestamp for the last context switch in ticks. 63 /// Gets the timestamp for the last context switch in ticks.
59 [[nodiscard]] u64 GetLastContextSwitchTicks() const; 64 [[nodiscard]] u64 GetLastContextSwitchTicks() const;
60 65
@@ -165,7 +170,6 @@ private:
165 */ 170 */
166 void UpdateLastContextSwitchTime(KThread* thread, KProcess* process); 171 void UpdateLastContextSwitchTime(KThread* thread, KProcess* process);
167 172
168 static void OnSwitch(void* this_scheduler);
169 void SwitchToCurrent(); 173 void SwitchToCurrent();
170 174
171 KThread* prev_thread{}; 175 KThread* prev_thread{};
diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp
index 8d7faa662..90de86770 100644
--- a/src/core/hle/kernel/k_thread.cpp
+++ b/src/core/hle/kernel/k_thread.cpp
@@ -246,14 +246,12 @@ Result KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_stack
246 246
247Result KThread::InitializeThread(KThread* thread, KThreadFunction func, uintptr_t arg, 247Result KThread::InitializeThread(KThread* thread, KThreadFunction func, uintptr_t arg,
248 VAddr user_stack_top, s32 prio, s32 core, KProcess* owner, 248 VAddr user_stack_top, s32 prio, s32 core, KProcess* owner,
249 ThreadType type, std::function<void(void*)>&& init_func, 249 ThreadType type, std::function<void()>&& init_func) {
250 void* init_func_parameter) {
251 // Initialize the thread. 250 // Initialize the thread.
252 R_TRY(thread->Initialize(func, arg, user_stack_top, prio, core, owner, type)); 251 R_TRY(thread->Initialize(func, arg, user_stack_top, prio, core, owner, type));
253 252
254 // Initialize emulation parameters. 253 // Initialize emulation parameters.
255 thread->host_context = 254 thread->host_context = std::make_shared<Common::Fiber>(std::move(init_func));
256 std::make_shared<Common::Fiber>(std::move(init_func), init_func_parameter);
257 thread->is_single_core = !Settings::values.use_multi_core.GetValue(); 255 thread->is_single_core = !Settings::values.use_multi_core.GetValue();
258 256
259 return ResultSuccess; 257 return ResultSuccess;
@@ -265,15 +263,13 @@ Result KThread::InitializeDummyThread(KThread* thread) {
265 263
266Result KThread::InitializeIdleThread(Core::System& system, KThread* thread, s32 virt_core) { 264Result KThread::InitializeIdleThread(Core::System& system, KThread* thread, s32 virt_core) {
267 return InitializeThread(thread, {}, {}, {}, IdleThreadPriority, virt_core, {}, ThreadType::Main, 265 return InitializeThread(thread, {}, {}, {}, IdleThreadPriority, virt_core, {}, ThreadType::Main,
268 Core::CpuManager::GetIdleThreadStartFunc(), 266 system.GetCpuManager().GetIdleThreadStartFunc());
269 system.GetCpuManager().GetStartFuncParameter());
270} 267}
271 268
272Result KThread::InitializeHighPriorityThread(Core::System& system, KThread* thread, 269Result KThread::InitializeHighPriorityThread(Core::System& system, KThread* thread,
273 KThreadFunction func, uintptr_t arg, s32 virt_core) { 270 KThreadFunction func, uintptr_t arg, s32 virt_core) {
274 return InitializeThread(thread, func, arg, {}, {}, virt_core, nullptr, ThreadType::HighPriority, 271 return InitializeThread(thread, func, arg, {}, {}, virt_core, nullptr, ThreadType::HighPriority,
275 Core::CpuManager::GetShutdownThreadStartFunc(), 272 system.GetCpuManager().GetShutdownThreadStartFunc());
276 system.GetCpuManager().GetStartFuncParameter());
277} 273}
278 274
279Result KThread::InitializeUserThread(Core::System& system, KThread* thread, KThreadFunction func, 275Result KThread::InitializeUserThread(Core::System& system, KThread* thread, KThreadFunction func,
@@ -281,8 +277,7 @@ Result KThread::InitializeUserThread(Core::System& system, KThread* thread, KThr
281 KProcess* owner) { 277 KProcess* owner) {
282 system.Kernel().GlobalSchedulerContext().AddThread(thread); 278 system.Kernel().GlobalSchedulerContext().AddThread(thread);
283 return InitializeThread(thread, func, arg, user_stack_top, prio, virt_core, owner, 279 return InitializeThread(thread, func, arg, user_stack_top, prio, virt_core, owner,
284 ThreadType::User, Core::CpuManager::GetGuestThreadStartFunc(), 280 ThreadType::User, system.GetCpuManager().GetGuestThreadStartFunc());
285 system.GetCpuManager().GetStartFuncParameter());
286} 281}
287 282
288void KThread::PostDestroy(uintptr_t arg) { 283void KThread::PostDestroy(uintptr_t arg) {
@@ -313,14 +308,20 @@ void KThread::Finalize() {
313 308
314 auto it = waiter_list.begin(); 309 auto it = waiter_list.begin();
315 while (it != waiter_list.end()) { 310 while (it != waiter_list.end()) {
316 // Clear the lock owner 311 // Get the thread.
317 it->SetLockOwner(nullptr); 312 KThread* const waiter = std::addressof(*it);
313
314 // The thread shouldn't be a kernel waiter.
315 ASSERT(!IsKernelAddressKey(waiter->GetAddressKey()));
316
317 // Clear the lock owner.
318 waiter->SetLockOwner(nullptr);
318 319
319 // Erase the waiter from our list. 320 // Erase the waiter from our list.
320 it = waiter_list.erase(it); 321 it = waiter_list.erase(it);
321 322
322 // Cancel the thread's wait. 323 // Cancel the thread's wait.
323 it->CancelWait(ResultInvalidState, true); 324 waiter->CancelWait(ResultInvalidState, true);
324 } 325 }
325 } 326 }
326 327
@@ -485,9 +486,7 @@ void KThread::Unpin() {
485 486
486 // Resume any threads that began waiting on us while we were pinned. 487 // Resume any threads that began waiting on us while we were pinned.
487 for (auto it = pinned_waiter_list.begin(); it != pinned_waiter_list.end(); ++it) { 488 for (auto it = pinned_waiter_list.begin(); it != pinned_waiter_list.end(); ++it) {
488 if (it->GetState() == ThreadState::Waiting) { 489 it->EndWait(ResultSuccess);
489 it->SetState(ThreadState::Runnable);
490 }
491 } 490 }
492} 491}
493 492
@@ -882,6 +881,7 @@ void KThread::AddWaiterImpl(KThread* thread) {
882 // Keep track of how many kernel waiters we have. 881 // Keep track of how many kernel waiters we have.
883 if (IsKernelAddressKey(thread->GetAddressKey())) { 882 if (IsKernelAddressKey(thread->GetAddressKey())) {
884 ASSERT((num_kernel_waiters++) >= 0); 883 ASSERT((num_kernel_waiters++) >= 0);
884 KScheduler::SetSchedulerUpdateNeeded(kernel);
885 } 885 }
886 886
887 // Insert the waiter. 887 // Insert the waiter.
@@ -895,6 +895,7 @@ void KThread::RemoveWaiterImpl(KThread* thread) {
895 // Keep track of how many kernel waiters we have. 895 // Keep track of how many kernel waiters we have.
896 if (IsKernelAddressKey(thread->GetAddressKey())) { 896 if (IsKernelAddressKey(thread->GetAddressKey())) {
897 ASSERT((num_kernel_waiters--) > 0); 897 ASSERT((num_kernel_waiters--) > 0);
898 KScheduler::SetSchedulerUpdateNeeded(kernel);
898 } 899 }
899 900
900 // Remove the waiter. 901 // Remove the waiter.
@@ -970,6 +971,7 @@ KThread* KThread::RemoveWaiterByKey(s32* out_num_waiters, VAddr key) {
970 // Keep track of how many kernel waiters we have. 971 // Keep track of how many kernel waiters we have.
971 if (IsKernelAddressKey(thread->GetAddressKey())) { 972 if (IsKernelAddressKey(thread->GetAddressKey())) {
972 ASSERT((num_kernel_waiters--) > 0); 973 ASSERT((num_kernel_waiters--) > 0);
974 KScheduler::SetSchedulerUpdateNeeded(kernel);
973 } 975 }
974 it = waiter_list.erase(it); 976 it = waiter_list.erase(it);
975 977
diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h
index 94c4cd1c8..28cd7ecb0 100644
--- a/src/core/hle/kernel/k_thread.h
+++ b/src/core/hle/kernel/k_thread.h
@@ -729,8 +729,7 @@ private:
729 [[nodiscard]] static Result InitializeThread(KThread* thread, KThreadFunction func, 729 [[nodiscard]] static Result InitializeThread(KThread* thread, KThreadFunction func,
730 uintptr_t arg, VAddr user_stack_top, s32 prio, 730 uintptr_t arg, VAddr user_stack_top, s32 prio,
731 s32 core, KProcess* owner, ThreadType type, 731 s32 core, KProcess* owner, ThreadType type,
732 std::function<void(void*)>&& init_func, 732 std::function<void()>&& init_func);
733 void* init_func_parameter);
734 733
735 static void RestorePriority(KernelCore& kernel_ctx, KThread* thread); 734 static void RestorePriority(KernelCore& kernel_ctx, KThread* thread);
736 735
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 0009193be..7307cf262 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -234,17 +234,18 @@ struct KernelCore::Impl {
234 234
235 void InitializePreemption(KernelCore& kernel) { 235 void InitializePreemption(KernelCore& kernel) {
236 preemption_event = Core::Timing::CreateEvent( 236 preemption_event = Core::Timing::CreateEvent(
237 "PreemptionCallback", [this, &kernel](std::uintptr_t, std::chrono::nanoseconds) { 237 "PreemptionCallback",
238 [this, &kernel](std::uintptr_t, s64 time,
239 std::chrono::nanoseconds) -> std::optional<std::chrono::nanoseconds> {
238 { 240 {
239 KScopedSchedulerLock lock(kernel); 241 KScopedSchedulerLock lock(kernel);
240 global_scheduler_context->PreemptThreads(); 242 global_scheduler_context->PreemptThreads();
241 } 243 }
242 const auto time_interval = std::chrono::nanoseconds{std::chrono::milliseconds(10)}; 244 return std::nullopt;
243 system.CoreTiming().ScheduleEvent(time_interval, preemption_event);
244 }); 245 });
245 246
246 const auto time_interval = std::chrono::nanoseconds{std::chrono::milliseconds(10)}; 247 const auto time_interval = std::chrono::nanoseconds{std::chrono::milliseconds(10)};
247 system.CoreTiming().ScheduleEvent(time_interval, preemption_event); 248 system.CoreTiming().ScheduleLoopingEvent(time_interval, time_interval, preemption_event);
248 } 249 }
249 250
250 void InitializeShutdownThreads() { 251 void InitializeShutdownThreads() {
diff --git a/src/core/hle/kernel/time_manager.cpp b/src/core/hle/kernel/time_manager.cpp
index 2724c3782..5ee72c432 100644
--- a/src/core/hle/kernel/time_manager.cpp
+++ b/src/core/hle/kernel/time_manager.cpp
@@ -11,15 +11,17 @@
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..3c28dee76 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -49,28 +49,41 @@ bool Controller_NPad::IsNpadIdValid(Core::HID::NpadIdType npad_id) {
49 } 49 }
50} 50}
51 51
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
@@ -820,11 +838,11 @@ bool Controller_NPad::VibrateControllerAtIndex(Core::HID::NpadIdType npad_id,
820 838
821 const auto now = steady_clock::now(); 839 const auto now = steady_clock::now();
822 840
823 // Filter out non-zero vibrations that are within 10ms of each other. 841 // Filter out non-zero vibrations that are within 15ms of each other.
824 if ((vibration_value.low_amplitude != 0.0f || vibration_value.high_amplitude != 0.0f) && 842 if ((vibration_value.low_amplitude != 0.0f || vibration_value.high_amplitude != 0.0f) &&
825 duration_cast<milliseconds>( 843 duration_cast<milliseconds>(
826 now - controller.vibration[device_index].last_vibration_timepoint) < 844 now - controller.vibration[device_index].last_vibration_timepoint) <
827 milliseconds(10)) { 845 milliseconds(15)) {
828 return false; 846 return false;
829 } 847 }
830 848
@@ -840,7 +858,7 @@ bool Controller_NPad::VibrateControllerAtIndex(Core::HID::NpadIdType npad_id,
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/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp
index 0310ce883..7055ea93e 100644
--- a/src/core/hle/service/nifm/nifm.cpp
+++ b/src/core/hle/service/nifm/nifm.cpp
@@ -30,6 +30,19 @@ enum class RequestState : u32 {
30 Connected = 3, 30 Connected = 3,
31}; 31};
32 32
33enum class InternetConnectionType : u8 {
34 WiFi = 1,
35 Ethernet = 2,
36};
37
38enum class InternetConnectionStatus : u8 {
39 ConnectingUnknown1,
40 ConnectingUnknown2,
41 ConnectingUnknown3,
42 ConnectingUnknown4,
43 Connected,
44};
45
33struct IpAddressSetting { 46struct IpAddressSetting {
34 bool is_automatic{}; 47 bool is_automatic{};
35 Network::IPv4Address current_address{}; 48 Network::IPv4Address current_address{};
@@ -271,6 +284,7 @@ private:
271 rb.Push(ResultSuccess); 284 rb.Push(ResultSuccess);
272 rb.Push<u64>(client_id); // Client ID needs to be non zero otherwise it's considered invalid 285 rb.Push<u64>(client_id); // Client ID needs to be non zero otherwise it's considered invalid
273 } 286 }
287
274 void CreateScanRequest(Kernel::HLERequestContext& ctx) { 288 void CreateScanRequest(Kernel::HLERequestContext& ctx) {
275 LOG_DEBUG(Service_NIFM, "called"); 289 LOG_DEBUG(Service_NIFM, "called");
276 290
@@ -279,6 +293,7 @@ private:
279 rb.Push(ResultSuccess); 293 rb.Push(ResultSuccess);
280 rb.PushIpcInterface<IScanRequest>(system); 294 rb.PushIpcInterface<IScanRequest>(system);
281 } 295 }
296
282 void CreateRequest(Kernel::HLERequestContext& ctx) { 297 void CreateRequest(Kernel::HLERequestContext& ctx) {
283 LOG_DEBUG(Service_NIFM, "called"); 298 LOG_DEBUG(Service_NIFM, "called");
284 299
@@ -287,6 +302,7 @@ private:
287 rb.Push(ResultSuccess); 302 rb.Push(ResultSuccess);
288 rb.PushIpcInterface<IRequest>(system); 303 rb.PushIpcInterface<IRequest>(system);
289 } 304 }
305
290 void GetCurrentNetworkProfile(Kernel::HLERequestContext& ctx) { 306 void GetCurrentNetworkProfile(Kernel::HLERequestContext& ctx) {
291 LOG_WARNING(Service_NIFM, "(STUBBED) called"); 307 LOG_WARNING(Service_NIFM, "(STUBBED) called");
292 308
@@ -335,12 +351,14 @@ private:
335 IPC::ResponseBuilder rb{ctx, 2}; 351 IPC::ResponseBuilder rb{ctx, 2};
336 rb.Push(ResultSuccess); 352 rb.Push(ResultSuccess);
337 } 353 }
354
338 void RemoveNetworkProfile(Kernel::HLERequestContext& ctx) { 355 void RemoveNetworkProfile(Kernel::HLERequestContext& ctx) {
339 LOG_WARNING(Service_NIFM, "(STUBBED) called"); 356 LOG_WARNING(Service_NIFM, "(STUBBED) called");
340 357
341 IPC::ResponseBuilder rb{ctx, 2}; 358 IPC::ResponseBuilder rb{ctx, 2};
342 rb.Push(ResultSuccess); 359 rb.Push(ResultSuccess);
343 } 360 }
361
344 void GetCurrentIpAddress(Kernel::HLERequestContext& ctx) { 362 void GetCurrentIpAddress(Kernel::HLERequestContext& ctx) {
345 LOG_WARNING(Service_NIFM, "(STUBBED) called"); 363 LOG_WARNING(Service_NIFM, "(STUBBED) called");
346 364
@@ -354,6 +372,7 @@ private:
354 rb.Push(ResultSuccess); 372 rb.Push(ResultSuccess);
355 rb.PushRaw(*ipv4); 373 rb.PushRaw(*ipv4);
356 } 374 }
375
357 void CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx) { 376 void CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx) {
358 LOG_DEBUG(Service_NIFM, "called"); 377 LOG_DEBUG(Service_NIFM, "called");
359 378
@@ -369,6 +388,7 @@ private:
369 rb.PushIpcInterface<INetworkProfile>(system); 388 rb.PushIpcInterface<INetworkProfile>(system);
370 rb.PushRaw<u128>(uuid); 389 rb.PushRaw<u128>(uuid);
371 } 390 }
391
372 void GetCurrentIpConfigInfo(Kernel::HLERequestContext& ctx) { 392 void GetCurrentIpConfigInfo(Kernel::HLERequestContext& ctx) {
373 LOG_WARNING(Service_NIFM, "(STUBBED) called"); 393 LOG_WARNING(Service_NIFM, "(STUBBED) called");
374 394
@@ -405,6 +425,7 @@ private:
405 rb.Push(ResultSuccess); 425 rb.Push(ResultSuccess);
406 rb.PushRaw<IpConfigInfo>(ip_config_info); 426 rb.PushRaw<IpConfigInfo>(ip_config_info);
407 } 427 }
428
408 void IsWirelessCommunicationEnabled(Kernel::HLERequestContext& ctx) { 429 void IsWirelessCommunicationEnabled(Kernel::HLERequestContext& ctx) {
409 LOG_WARNING(Service_NIFM, "(STUBBED) called"); 430 LOG_WARNING(Service_NIFM, "(STUBBED) called");
410 431
@@ -412,6 +433,24 @@ private:
412 rb.Push(ResultSuccess); 433 rb.Push(ResultSuccess);
413 rb.Push<u8>(0); 434 rb.Push<u8>(0);
414 } 435 }
436
437 void GetInternetConnectionStatus(Kernel::HLERequestContext& ctx) {
438 LOG_WARNING(Service_NIFM, "(STUBBED) called");
439
440 struct Output {
441 InternetConnectionType type{InternetConnectionType::WiFi};
442 u8 wifi_strength{3};
443 InternetConnectionStatus state{InternetConnectionStatus::Connected};
444 };
445 static_assert(sizeof(Output) == 0x3, "Output has incorrect size.");
446
447 constexpr Output out{};
448
449 IPC::ResponseBuilder rb{ctx, 3};
450 rb.Push(ResultSuccess);
451 rb.PushRaw(out);
452 }
453
415 void IsEthernetCommunicationEnabled(Kernel::HLERequestContext& ctx) { 454 void IsEthernetCommunicationEnabled(Kernel::HLERequestContext& ctx) {
416 LOG_WARNING(Service_NIFM, "(STUBBED) called"); 455 LOG_WARNING(Service_NIFM, "(STUBBED) called");
417 456
@@ -423,6 +462,7 @@ private:
423 rb.Push<u8>(0); 462 rb.Push<u8>(0);
424 } 463 }
425 } 464 }
465
426 void IsAnyInternetRequestAccepted(Kernel::HLERequestContext& ctx) { 466 void IsAnyInternetRequestAccepted(Kernel::HLERequestContext& ctx) {
427 LOG_WARNING(Service_NIFM, "(STUBBED) called"); 467 LOG_WARNING(Service_NIFM, "(STUBBED) called");
428 468
@@ -456,7 +496,7 @@ IGeneralService::IGeneralService(Core::System& system_)
456 {15, &IGeneralService::GetCurrentIpConfigInfo, "GetCurrentIpConfigInfo"}, 496 {15, &IGeneralService::GetCurrentIpConfigInfo, "GetCurrentIpConfigInfo"},
457 {16, nullptr, "SetWirelessCommunicationEnabled"}, 497 {16, nullptr, "SetWirelessCommunicationEnabled"},
458 {17, &IGeneralService::IsWirelessCommunicationEnabled, "IsWirelessCommunicationEnabled"}, 498 {17, &IGeneralService::IsWirelessCommunicationEnabled, "IsWirelessCommunicationEnabled"},
459 {18, nullptr, "GetInternetConnectionStatus"}, 499 {18, &IGeneralService::GetInternetConnectionStatus, "GetInternetConnectionStatus"},
460 {19, nullptr, "SetEthernetCommunicationEnabled"}, 500 {19, nullptr, "SetEthernetCommunicationEnabled"},
461 {20, &IGeneralService::IsEthernetCommunicationEnabled, "IsEthernetCommunicationEnabled"}, 501 {20, &IGeneralService::IsEthernetCommunicationEnabled, "IsEthernetCommunicationEnabled"},
462 {21, &IGeneralService::IsAnyInternetRequestAccepted, "IsAnyInternetRequestAccepted"}, 502 {21, &IGeneralService::IsAnyInternetRequestAccepted, "IsAnyInternetRequestAccepted"},
diff --git a/src/core/hle/service/nvflinger/binder.h b/src/core/hle/service/nvflinger/binder.h
index 21aaa40cd..157333ff8 100644
--- a/src/core/hle/service/nvflinger/binder.h
+++ b/src/core/hle/service/nvflinger/binder.h
@@ -34,6 +34,7 @@ enum class TransactionId {
34 34
35class IBinder { 35class IBinder {
36public: 36public:
37 virtual ~IBinder() = default;
37 virtual void Transact(Kernel::HLERequestContext& ctx, android::TransactionId code, 38 virtual void Transact(Kernel::HLERequestContext& ctx, android::TransactionId code,
38 u32 flags) = 0; 39 u32 flags) = 0;
39 virtual Kernel::KReadableEvent& GetNativeHandle() = 0; 40 virtual Kernel::KReadableEvent& GetNativeHandle() = 0;
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp
index 2b2985a2d..5f69c8c2c 100644
--- a/src/core/hle/service/nvflinger/nvflinger.cpp
+++ b/src/core/hle/service/nvflinger/nvflinger.cpp
@@ -67,21 +67,20 @@ NVFlinger::NVFlinger(Core::System& system_, HosBinderDriverServer& hos_binder_dr
67 67
68 // Schedule the screen composition events 68 // Schedule the screen composition events
69 composition_event = Core::Timing::CreateEvent( 69 composition_event = Core::Timing::CreateEvent(
70 "ScreenComposition", [this](std::uintptr_t, std::chrono::nanoseconds ns_late) { 70 "ScreenComposition",
71 [this](std::uintptr_t, s64 time,
72 std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
71 const auto lock_guard = Lock(); 73 const auto lock_guard = Lock();
72 Compose(); 74 Compose();
73 75
74 const auto ticks = std::chrono::nanoseconds{GetNextTicks()}; 76 return std::max(std::chrono::nanoseconds::zero(),
75 const auto ticks_delta = ticks - ns_late; 77 std::chrono::nanoseconds(GetNextTicks()) - ns_late);
76 const auto future_ns = std::max(std::chrono::nanoseconds::zero(), ticks_delta);
77
78 this->system.CoreTiming().ScheduleEvent(future_ns, composition_event);
79 }); 78 });
80 79
81 if (system.IsMulticore()) { 80 if (system.IsMulticore()) {
82 vsync_thread = std::jthread([this](std::stop_token token) { SplitVSync(token); }); 81 vsync_thread = std::jthread([this](std::stop_token token) { SplitVSync(token); });
83 } else { 82 } else {
84 system.CoreTiming().ScheduleEvent(frame_ns, composition_event); 83 system.CoreTiming().ScheduleLoopingEvent(frame_ns, frame_ns, composition_event);
85 } 84 }
86} 85}
87 86
diff --git a/src/core/hle/service/ptm/psm.cpp b/src/core/hle/service/ptm/psm.cpp
index 9e0eb6ac0..2c31e9485 100644
--- a/src/core/hle/service/ptm/psm.cpp
+++ b/src/core/hle/service/ptm/psm.cpp
@@ -9,10 +9,8 @@
9#include "core/hle/kernel/k_event.h" 9#include "core/hle/kernel/k_event.h"
10#include "core/hle/service/kernel_helpers.h" 10#include "core/hle/service/kernel_helpers.h"
11#include "core/hle/service/ptm/psm.h" 11#include "core/hle/service/ptm/psm.h"
12#include "core/hle/service/service.h"
13#include "core/hle/service/sm/sm.h"
14 12
15namespace Service::PSM { 13namespace Service::PTM {
16 14
17class IPsmSession final : public ServiceFramework<IPsmSession> { 15class IPsmSession final : public ServiceFramework<IPsmSession> {
18public: 16public:
@@ -57,7 +55,7 @@ public:
57 55
58private: 56private:
59 void BindStateChangeEvent(Kernel::HLERequestContext& ctx) { 57 void BindStateChangeEvent(Kernel::HLERequestContext& ctx) {
60 LOG_DEBUG(Service_PSM, "called"); 58 LOG_DEBUG(Service_PTM, "called");
61 59
62 should_signal = true; 60 should_signal = true;
63 61
@@ -67,7 +65,7 @@ private:
67 } 65 }
68 66
69 void UnbindStateChangeEvent(Kernel::HLERequestContext& ctx) { 67 void UnbindStateChangeEvent(Kernel::HLERequestContext& ctx) {
70 LOG_DEBUG(Service_PSM, "called"); 68 LOG_DEBUG(Service_PTM, "called");
71 69
72 should_signal = false; 70 should_signal = false;
73 71
@@ -78,7 +76,7 @@ private:
78 void SetChargerTypeChangeEventEnabled(Kernel::HLERequestContext& ctx) { 76 void SetChargerTypeChangeEventEnabled(Kernel::HLERequestContext& ctx) {
79 IPC::RequestParser rp{ctx}; 77 IPC::RequestParser rp{ctx};
80 const auto state = rp.Pop<bool>(); 78 const auto state = rp.Pop<bool>();
81 LOG_DEBUG(Service_PSM, "called, state={}", state); 79 LOG_DEBUG(Service_PTM, "called, state={}", state);
82 80
83 should_signal_charger_type = state; 81 should_signal_charger_type = state;
84 82
@@ -89,7 +87,7 @@ private:
89 void SetPowerSupplyChangeEventEnabled(Kernel::HLERequestContext& ctx) { 87 void SetPowerSupplyChangeEventEnabled(Kernel::HLERequestContext& ctx) {
90 IPC::RequestParser rp{ctx}; 88 IPC::RequestParser rp{ctx};
91 const auto state = rp.Pop<bool>(); 89 const auto state = rp.Pop<bool>();
92 LOG_DEBUG(Service_PSM, "called, state={}", state); 90 LOG_DEBUG(Service_PTM, "called, state={}", state);
93 91
94 should_signal_power_supply = state; 92 should_signal_power_supply = state;
95 93
@@ -100,7 +98,7 @@ private:
100 void SetBatteryVoltageStateChangeEventEnabled(Kernel::HLERequestContext& ctx) { 98 void SetBatteryVoltageStateChangeEventEnabled(Kernel::HLERequestContext& ctx) {
101 IPC::RequestParser rp{ctx}; 99 IPC::RequestParser rp{ctx};
102 const auto state = rp.Pop<bool>(); 100 const auto state = rp.Pop<bool>();
103 LOG_DEBUG(Service_PSM, "called, state={}", state); 101 LOG_DEBUG(Service_PTM, "called, state={}", state);
104 102
105 should_signal_battery_voltage = state; 103 should_signal_battery_voltage = state;
106 104
@@ -117,76 +115,58 @@ private:
117 Kernel::KEvent* state_change_event; 115 Kernel::KEvent* state_change_event;
118}; 116};
119 117
120class PSM final : public ServiceFramework<PSM> { 118PSM::PSM(Core::System& system_) : ServiceFramework{system_, "psm"} {
121public: 119 // clang-format off
122 explicit PSM(Core::System& system_) : ServiceFramework{system_, "psm"} { 120 static const FunctionInfo functions[] = {
123 // clang-format off 121 {0, &PSM::GetBatteryChargePercentage, "GetBatteryChargePercentage"},
124 static const FunctionInfo functions[] = { 122 {1, &PSM::GetChargerType, "GetChargerType"},
125 {0, &PSM::GetBatteryChargePercentage, "GetBatteryChargePercentage"}, 123 {2, nullptr, "EnableBatteryCharging"},
126 {1, &PSM::GetChargerType, "GetChargerType"}, 124 {3, nullptr, "DisableBatteryCharging"},
127 {2, nullptr, "EnableBatteryCharging"}, 125 {4, nullptr, "IsBatteryChargingEnabled"},
128 {3, nullptr, "DisableBatteryCharging"}, 126 {5, nullptr, "AcquireControllerPowerSupply"},
129 {4, nullptr, "IsBatteryChargingEnabled"}, 127 {6, nullptr, "ReleaseControllerPowerSupply"},
130 {5, nullptr, "AcquireControllerPowerSupply"}, 128 {7, &PSM::OpenSession, "OpenSession"},
131 {6, nullptr, "ReleaseControllerPowerSupply"}, 129 {8, nullptr, "EnableEnoughPowerChargeEmulation"},
132 {7, &PSM::OpenSession, "OpenSession"}, 130 {9, nullptr, "DisableEnoughPowerChargeEmulation"},
133 {8, nullptr, "EnableEnoughPowerChargeEmulation"}, 131 {10, nullptr, "EnableFastBatteryCharging"},
134 {9, nullptr, "DisableEnoughPowerChargeEmulation"}, 132 {11, nullptr, "DisableFastBatteryCharging"},
135 {10, nullptr, "EnableFastBatteryCharging"}, 133 {12, nullptr, "GetBatteryVoltageState"},
136 {11, nullptr, "DisableFastBatteryCharging"}, 134 {13, nullptr, "GetRawBatteryChargePercentage"},
137 {12, nullptr, "GetBatteryVoltageState"}, 135 {14, nullptr, "IsEnoughPowerSupplied"},
138 {13, nullptr, "GetRawBatteryChargePercentage"}, 136 {15, nullptr, "GetBatteryAgePercentage"},
139 {14, nullptr, "IsEnoughPowerSupplied"}, 137 {16, nullptr, "GetBatteryChargeInfoEvent"},
140 {15, nullptr, "GetBatteryAgePercentage"}, 138 {17, nullptr, "GetBatteryChargeInfoFields"},
141 {16, nullptr, "GetBatteryChargeInfoEvent"}, 139 {18, nullptr, "GetBatteryChargeCalibratedEvent"},
142 {17, nullptr, "GetBatteryChargeInfoFields"}, 140 };
143 {18, nullptr, "GetBatteryChargeCalibratedEvent"}, 141 // clang-format on
144 };
145 // clang-format on
146
147 RegisterHandlers(functions);
148 }
149
150 ~PSM() override = default;
151
152private:
153 void GetBatteryChargePercentage(Kernel::HLERequestContext& ctx) {
154 LOG_DEBUG(Service_PSM, "called");
155 142
156 IPC::ResponseBuilder rb{ctx, 3}; 143 RegisterHandlers(functions);
157 rb.Push(ResultSuccess); 144}
158 rb.Push<u32>(battery_charge_percentage);
159 }
160 145
161 void GetChargerType(Kernel::HLERequestContext& ctx) { 146PSM::~PSM() = default;
162 LOG_DEBUG(Service_PSM, "called");
163 147
164 IPC::ResponseBuilder rb{ctx, 3}; 148void PSM::GetBatteryChargePercentage(Kernel::HLERequestContext& ctx) {
165 rb.Push(ResultSuccess); 149 LOG_DEBUG(Service_PTM, "called");
166 rb.PushEnum(charger_type);
167 }
168 150
169 void OpenSession(Kernel::HLERequestContext& ctx) { 151 IPC::ResponseBuilder rb{ctx, 3};
170 LOG_DEBUG(Service_PSM, "called"); 152 rb.Push(ResultSuccess);
153 rb.Push<u32>(battery_charge_percentage);
154}
171 155
172 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 156void PSM::GetChargerType(Kernel::HLERequestContext& ctx) {
173 rb.Push(ResultSuccess); 157 LOG_DEBUG(Service_PTM, "called");
174 rb.PushIpcInterface<IPsmSession>(system);
175 }
176 158
177 enum class ChargerType : u32 { 159 IPC::ResponseBuilder rb{ctx, 3};
178 Unplugged = 0, 160 rb.Push(ResultSuccess);
179 RegularCharger = 1, 161 rb.PushEnum(charger_type);
180 LowPowerCharger = 2, 162}
181 Unknown = 3,
182 };
183 163
184 u32 battery_charge_percentage{100}; // 100% 164void PSM::OpenSession(Kernel::HLERequestContext& ctx) {
185 ChargerType charger_type{ChargerType::RegularCharger}; 165 LOG_DEBUG(Service_PTM, "called");
186};
187 166
188void InstallInterfaces(SM::ServiceManager& sm, Core::System& system) { 167 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
189 std::make_shared<PSM>(system)->InstallAsService(sm); 168 rb.Push(ResultSuccess);
169 rb.PushIpcInterface<IPsmSession>(system);
190} 170}
191 171
192} // namespace Service::PSM 172} // namespace Service::PTM
diff --git a/src/core/hle/service/ptm/psm.h b/src/core/hle/service/ptm/psm.h
index 94a1044db..f674ba8bc 100644
--- a/src/core/hle/service/ptm/psm.h
+++ b/src/core/hle/service/ptm/psm.h
@@ -3,16 +3,29 @@
3 3
4#pragma once 4#pragma once
5 5
6namespace Core { 6#include "core/hle/service/service.h"
7class System;
8}
9 7
10namespace Service::SM { 8namespace Service::PTM {
11class ServiceManager;
12}
13 9
14namespace Service::PSM { 10class PSM final : public ServiceFramework<PSM> {
11public:
12 explicit PSM(Core::System& system_);
13 ~PSM() override;
15 14
16void InstallInterfaces(SM::ServiceManager& sm, Core::System& system); 15private:
16 enum class ChargerType : u32 {
17 Unplugged = 0,
18 RegularCharger = 1,
19 LowPowerCharger = 2,
20 Unknown = 3,
21 };
17 22
18} // namespace Service::PSM 23 void GetBatteryChargePercentage(Kernel::HLERequestContext& ctx);
24 void GetChargerType(Kernel::HLERequestContext& ctx);
25 void OpenSession(Kernel::HLERequestContext& ctx);
26
27 u32 battery_charge_percentage{100};
28 ChargerType charger_type{ChargerType::RegularCharger};
29};
30
31} // namespace Service::PTM
diff --git a/src/core/hle/service/ptm/ptm.cpp b/src/core/hle/service/ptm/ptm.cpp
new file mode 100644
index 000000000..4bea995c6
--- /dev/null
+++ b/src/core/hle/service/ptm/ptm.cpp
@@ -0,0 +1,18 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include <memory>
5
6#include "core/core.h"
7#include "core/hle/service/ptm/psm.h"
8#include "core/hle/service/ptm/ptm.h"
9#include "core/hle/service/ptm/ts.h"
10
11namespace Service::PTM {
12
13void InstallInterfaces(SM::ServiceManager& sm, Core::System& system) {
14 std::make_shared<PSM>(system)->InstallAsService(sm);
15 std::make_shared<TS>(system)->InstallAsService(sm);
16}
17
18} // namespace Service::PTM
diff --git a/src/core/hle/service/ptm/ptm.h b/src/core/hle/service/ptm/ptm.h
new file mode 100644
index 000000000..06224a24e
--- /dev/null
+++ b/src/core/hle/service/ptm/ptm.h
@@ -0,0 +1,18 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6namespace Core {
7class System;
8}
9
10namespace Service::SM {
11class ServiceManager;
12}
13
14namespace Service::PTM {
15
16void InstallInterfaces(SM::ServiceManager& sm, Core::System& system);
17
18} // namespace Service::PTM
diff --git a/src/core/hle/service/ptm/ts.cpp b/src/core/hle/service/ptm/ts.cpp
new file mode 100644
index 000000000..65c3f135f
--- /dev/null
+++ b/src/core/hle/service/ptm/ts.cpp
@@ -0,0 +1,41 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include <memory>
5
6#include "core/core.h"
7#include "core/hle/ipc_helpers.h"
8#include "core/hle/service/ptm/ts.h"
9
10namespace Service::PTM {
11
12TS::TS(Core::System& system_) : ServiceFramework{system_, "ts"} {
13 // clang-format off
14 static const FunctionInfo functions[] = {
15 {0, nullptr, "GetTemperatureRange"},
16 {1, &TS::GetTemperature, "GetTemperature"},
17 {2, nullptr, "SetMeasurementMode"},
18 {3, nullptr, "GetTemperatureMilliC"},
19 {4, nullptr, "OpenSession"},
20 };
21 // clang-format on
22
23 RegisterHandlers(functions);
24}
25
26TS::~TS() = default;
27
28void TS::GetTemperature(Kernel::HLERequestContext& ctx) {
29 IPC::RequestParser rp{ctx};
30 const auto location{rp.PopEnum<Location>()};
31
32 LOG_WARNING(Service_HID, "(STUBBED) called. location={}", location);
33
34 const s32 temperature = location == Location::Internal ? 35 : 20;
35
36 IPC::ResponseBuilder rb{ctx, 3};
37 rb.Push(ResultSuccess);
38 rb.Push(temperature);
39}
40
41} // namespace Service::PTM
diff --git a/src/core/hle/service/ptm/ts.h b/src/core/hle/service/ptm/ts.h
new file mode 100644
index 000000000..39a734ef7
--- /dev/null
+++ b/src/core/hle/service/ptm/ts.h
@@ -0,0 +1,25 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include "common/common_types.h"
7#include "core/hle/service/service.h"
8
9namespace Service::PTM {
10
11class TS final : public ServiceFramework<TS> {
12public:
13 explicit TS(Core::System& system_);
14 ~TS() override;
15
16private:
17 enum class Location : u8 {
18 Internal,
19 External,
20 };
21
22 void GetTemperature(Kernel::HLERequestContext& ctx);
23};
24
25} // namespace Service::PTM
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index 318009e4f..c64291e7f 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -58,7 +58,7 @@
58#include "core/hle/service/pm/pm.h" 58#include "core/hle/service/pm/pm.h"
59#include "core/hle/service/prepo/prepo.h" 59#include "core/hle/service/prepo/prepo.h"
60#include "core/hle/service/psc/psc.h" 60#include "core/hle/service/psc/psc.h"
61#include "core/hle/service/ptm/psm.h" 61#include "core/hle/service/ptm/ptm.h"
62#include "core/hle/service/service.h" 62#include "core/hle/service/service.h"
63#include "core/hle/service/set/settings.h" 63#include "core/hle/service/set/settings.h"
64#include "core/hle/service/sm/sm.h" 64#include "core/hle/service/sm/sm.h"
@@ -287,7 +287,7 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system
287 PlayReport::InstallInterfaces(*sm, system); 287 PlayReport::InstallInterfaces(*sm, system);
288 PM::InstallInterfaces(system); 288 PM::InstallInterfaces(system);
289 PSC::InstallInterfaces(*sm, system); 289 PSC::InstallInterfaces(*sm, system);
290 PSM::InstallInterfaces(*sm, system); 290 PTM::InstallInterfaces(*sm, system);
291 Set::InstallInterfaces(*sm, system); 291 Set::InstallInterfaces(*sm, system);
292 Sockets::InstallInterfaces(*sm, system); 292 Sockets::InstallInterfaces(*sm, system);
293 SPL::InstallInterfaces(*sm, system); 293 SPL::InstallInterfaces(*sm, system);
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/input_common/drivers/sdl_driver.cpp b/src/input_common/drivers/sdl_driver.cpp
index 446c027d3..00474ac77 100644
--- a/src/input_common/drivers/sdl_driver.cpp
+++ b/src/input_common/drivers/sdl_driver.cpp
@@ -438,10 +438,17 @@ SDLDriver::SDLDriver(std::string input_engine_) : InputEngine(std::move(input_en
438 using namespace std::chrono_literals; 438 using namespace std::chrono_literals;
439 while (initialized) { 439 while (initialized) {
440 SDL_PumpEvents(); 440 SDL_PumpEvents();
441 SendVibrations();
442 std::this_thread::sleep_for(1ms); 441 std::this_thread::sleep_for(1ms);
443 } 442 }
444 }); 443 });
444 vibration_thread = std::thread([this] {
445 Common::SetCurrentThreadName("yuzu:input:SDL_Vibration");
446 using namespace std::chrono_literals;
447 while (initialized) {
448 SendVibrations();
449 std::this_thread::sleep_for(10ms);
450 }
451 });
445 } 452 }
446 // Because the events for joystick connection happens before we have our event watcher added, we 453 // Because the events for joystick connection happens before we have our event watcher added, we
447 // can just open all the joysticks right here 454 // can just open all the joysticks right here
@@ -457,6 +464,7 @@ SDLDriver::~SDLDriver() {
457 initialized = false; 464 initialized = false;
458 if (start_thread) { 465 if (start_thread) {
459 poll_thread.join(); 466 poll_thread.join();
467 vibration_thread.join();
460 SDL_QuitSubSystem(SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER); 468 SDL_QuitSubSystem(SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER);
461 } 469 }
462} 470}
diff --git a/src/input_common/drivers/sdl_driver.h b/src/input_common/drivers/sdl_driver.h
index 0846fbb50..7dc7a93c7 100644
--- a/src/input_common/drivers/sdl_driver.h
+++ b/src/input_common/drivers/sdl_driver.h
@@ -128,5 +128,6 @@ private:
128 std::atomic<bool> initialized = false; 128 std::atomic<bool> initialized = false;
129 129
130 std::thread poll_thread; 130 std::thread poll_thread;
131 std::thread vibration_thread;
131}; 132};
132} // namespace InputCommon 133} // namespace InputCommon
diff --git a/src/tests/common/fibers.cpp b/src/tests/common/fibers.cpp
index cfc84d423..4e29f9199 100644
--- a/src/tests/common/fibers.cpp
+++ b/src/tests/common/fibers.cpp
@@ -43,7 +43,15 @@ class TestControl1 {
43public: 43public:
44 TestControl1() = default; 44 TestControl1() = default;
45 45
46 void DoWork(); 46 void DoWork() {
47 const u32 id = thread_ids.Get();
48 u32 value = items[id];
49 for (u32 i = 0; i < id; i++) {
50 value++;
51 }
52 results[id] = value;
53 Fiber::YieldTo(work_fibers[id], *thread_fibers[id]);
54 }
47 55
48 void ExecuteThread(u32 id); 56 void ExecuteThread(u32 id);
49 57
@@ -54,35 +62,16 @@ public:
54 std::vector<u32> results; 62 std::vector<u32> results;
55}; 63};
56 64
57static void WorkControl1(void* control) {
58 auto* test_control = static_cast<TestControl1*>(control);
59 test_control->DoWork();
60}
61
62void TestControl1::DoWork() {
63 const u32 id = thread_ids.Get();
64 u32 value = items[id];
65 for (u32 i = 0; i < id; i++) {
66 value++;
67 }
68 results[id] = value;
69 Fiber::YieldTo(work_fibers[id], *thread_fibers[id]);
70}
71
72void TestControl1::ExecuteThread(u32 id) { 65void TestControl1::ExecuteThread(u32 id) {
73 thread_ids.Register(id); 66 thread_ids.Register(id);
74 auto thread_fiber = Fiber::ThreadToFiber(); 67 auto thread_fiber = Fiber::ThreadToFiber();
75 thread_fibers[id] = thread_fiber; 68 thread_fibers[id] = thread_fiber;
76 work_fibers[id] = std::make_shared<Fiber>(std::function<void(void*)>{WorkControl1}, this); 69 work_fibers[id] = std::make_shared<Fiber>([this] { DoWork(); });
77 items[id] = rand() % 256; 70 items[id] = rand() % 256;
78 Fiber::YieldTo(thread_fibers[id], *work_fibers[id]); 71 Fiber::YieldTo(thread_fibers[id], *work_fibers[id]);
79 thread_fibers[id]->Exit(); 72 thread_fibers[id]->Exit();
80} 73}
81 74
82static void ThreadStart1(u32 id, TestControl1& test_control) {
83 test_control.ExecuteThread(id);
84}
85
86/** This test checks for fiber setup configuration and validates that fibers are 75/** This test checks for fiber setup configuration and validates that fibers are
87 * doing all the work required. 76 * doing all the work required.
88 */ 77 */
@@ -95,7 +84,7 @@ TEST_CASE("Fibers::Setup", "[common]") {
95 test_control.results.resize(num_threads, 0); 84 test_control.results.resize(num_threads, 0);
96 std::vector<std::thread> threads; 85 std::vector<std::thread> threads;
97 for (u32 i = 0; i < num_threads; i++) { 86 for (u32 i = 0; i < num_threads; i++) {
98 threads.emplace_back(ThreadStart1, i, std::ref(test_control)); 87 threads.emplace_back([&test_control, i] { test_control.ExecuteThread(i); });
99 } 88 }
100 for (u32 i = 0; i < num_threads; i++) { 89 for (u32 i = 0; i < num_threads; i++) {
101 threads[i].join(); 90 threads[i].join();
@@ -167,21 +156,6 @@ public:
167 std::shared_ptr<Common::Fiber> fiber3; 156 std::shared_ptr<Common::Fiber> fiber3;
168}; 157};
169 158
170static void WorkControl2_1(void* control) {
171 auto* test_control = static_cast<TestControl2*>(control);
172 test_control->DoWork1();
173}
174
175static void WorkControl2_2(void* control) {
176 auto* test_control = static_cast<TestControl2*>(control);
177 test_control->DoWork2();
178}
179
180static void WorkControl2_3(void* control) {
181 auto* test_control = static_cast<TestControl2*>(control);
182 test_control->DoWork3();
183}
184
185void TestControl2::ExecuteThread(u32 id) { 159void TestControl2::ExecuteThread(u32 id) {
186 thread_ids.Register(id); 160 thread_ids.Register(id);
187 auto thread_fiber = Fiber::ThreadToFiber(); 161 auto thread_fiber = Fiber::ThreadToFiber();
@@ -193,18 +167,6 @@ void TestControl2::Exit() {
193 thread_fibers[id]->Exit(); 167 thread_fibers[id]->Exit();
194} 168}
195 169
196static void ThreadStart2_1(u32 id, TestControl2& test_control) {
197 test_control.ExecuteThread(id);
198 test_control.CallFiber1();
199 test_control.Exit();
200}
201
202static void ThreadStart2_2(u32 id, TestControl2& test_control) {
203 test_control.ExecuteThread(id);
204 test_control.CallFiber2();
205 test_control.Exit();
206}
207
208/** This test checks for fiber thread exchange configuration and validates that fibers are 170/** This test checks for fiber thread exchange configuration and validates that fibers are
209 * that a fiber has been successfully transferred from one thread to another and that the TLS 171 * that a fiber has been successfully transferred from one thread to another and that the TLS
210 * region of the thread is kept while changing fibers. 172 * region of the thread is kept while changing fibers.
@@ -212,14 +174,19 @@ static void ThreadStart2_2(u32 id, TestControl2& test_control) {
212TEST_CASE("Fibers::InterExchange", "[common]") { 174TEST_CASE("Fibers::InterExchange", "[common]") {
213 TestControl2 test_control{}; 175 TestControl2 test_control{};
214 test_control.thread_fibers.resize(2); 176 test_control.thread_fibers.resize(2);
215 test_control.fiber1 = 177 test_control.fiber1 = std::make_shared<Fiber>([&test_control] { test_control.DoWork1(); });
216 std::make_shared<Fiber>(std::function<void(void*)>{WorkControl2_1}, &test_control); 178 test_control.fiber2 = std::make_shared<Fiber>([&test_control] { test_control.DoWork2(); });
217 test_control.fiber2 = 179 test_control.fiber3 = std::make_shared<Fiber>([&test_control] { test_control.DoWork3(); });
218 std::make_shared<Fiber>(std::function<void(void*)>{WorkControl2_2}, &test_control); 180 std::thread thread1{[&test_control] {
219 test_control.fiber3 = 181 test_control.ExecuteThread(0);
220 std::make_shared<Fiber>(std::function<void(void*)>{WorkControl2_3}, &test_control); 182 test_control.CallFiber1();
221 std::thread thread1(ThreadStart2_1, 0, std::ref(test_control)); 183 test_control.Exit();
222 std::thread thread2(ThreadStart2_2, 1, std::ref(test_control)); 184 }};
185 std::thread thread2{[&test_control] {
186 test_control.ExecuteThread(1);
187 test_control.CallFiber2();
188 test_control.Exit();
189 }};
223 thread1.join(); 190 thread1.join();
224 thread2.join(); 191 thread2.join();
225 REQUIRE(test_control.assert1); 192 REQUIRE(test_control.assert1);
@@ -270,16 +237,6 @@ public:
270 std::shared_ptr<Common::Fiber> fiber2; 237 std::shared_ptr<Common::Fiber> fiber2;
271}; 238};
272 239
273static void WorkControl3_1(void* control) {
274 auto* test_control = static_cast<TestControl3*>(control);
275 test_control->DoWork1();
276}
277
278static void WorkControl3_2(void* control) {
279 auto* test_control = static_cast<TestControl3*>(control);
280 test_control->DoWork2();
281}
282
283void TestControl3::ExecuteThread(u32 id) { 240void TestControl3::ExecuteThread(u32 id) {
284 thread_ids.Register(id); 241 thread_ids.Register(id);
285 auto thread_fiber = Fiber::ThreadToFiber(); 242 auto thread_fiber = Fiber::ThreadToFiber();
@@ -291,12 +248,6 @@ void TestControl3::Exit() {
291 thread_fibers[id]->Exit(); 248 thread_fibers[id]->Exit();
292} 249}
293 250
294static void ThreadStart3(u32 id, TestControl3& test_control) {
295 test_control.ExecuteThread(id);
296 test_control.CallFiber1();
297 test_control.Exit();
298}
299
300/** This test checks for one two threads racing for starting the same fiber. 251/** This test checks for one two threads racing for starting the same fiber.
301 * It checks execution occurred in an ordered manner and by no time there were 252 * It checks execution occurred in an ordered manner and by no time there were
302 * two contexts at the same time. 253 * two contexts at the same time.
@@ -304,12 +255,15 @@ static void ThreadStart3(u32 id, TestControl3& test_control) {
304TEST_CASE("Fibers::StartRace", "[common]") { 255TEST_CASE("Fibers::StartRace", "[common]") {
305 TestControl3 test_control{}; 256 TestControl3 test_control{};
306 test_control.thread_fibers.resize(2); 257 test_control.thread_fibers.resize(2);
307 test_control.fiber1 = 258 test_control.fiber1 = std::make_shared<Fiber>([&test_control] { test_control.DoWork1(); });
308 std::make_shared<Fiber>(std::function<void(void*)>{WorkControl3_1}, &test_control); 259 test_control.fiber2 = std::make_shared<Fiber>([&test_control] { test_control.DoWork2(); });
309 test_control.fiber2 = 260 const auto race_function{[&test_control](u32 id) {
310 std::make_shared<Fiber>(std::function<void(void*)>{WorkControl3_2}, &test_control); 261 test_control.ExecuteThread(id);
311 std::thread thread1(ThreadStart3, 0, std::ref(test_control)); 262 test_control.CallFiber1();
312 std::thread thread2(ThreadStart3, 1, std::ref(test_control)); 263 test_control.Exit();
264 }};
265 std::thread thread1([&] { race_function(0); });
266 std::thread thread2([&] { race_function(1); });
313 thread1.join(); 267 thread1.join();
314 thread2.join(); 268 thread2.join();
315 REQUIRE(test_control.value1 == 1); 269 REQUIRE(test_control.value1 == 1);
@@ -319,12 +273,10 @@ TEST_CASE("Fibers::StartRace", "[common]") {
319 273
320class TestControl4; 274class TestControl4;
321 275
322static void WorkControl4(void* control);
323
324class TestControl4 { 276class TestControl4 {
325public: 277public:
326 TestControl4() { 278 TestControl4() {
327 fiber1 = std::make_shared<Fiber>(std::function<void(void*)>{WorkControl4}, this); 279 fiber1 = std::make_shared<Fiber>([this] { DoWork(); });
328 goal_reached = false; 280 goal_reached = false;
329 rewinded = false; 281 rewinded = false;
330 } 282 }
@@ -336,7 +288,7 @@ public:
336 } 288 }
337 289
338 void DoWork() { 290 void DoWork() {
339 fiber1->SetRewindPoint(std::function<void(void*)>{WorkControl4}, this); 291 fiber1->SetRewindPoint([this] { DoWork(); });
340 if (rewinded) { 292 if (rewinded) {
341 goal_reached = true; 293 goal_reached = true;
342 Fiber::YieldTo(fiber1, *thread_fiber); 294 Fiber::YieldTo(fiber1, *thread_fiber);
@@ -351,11 +303,6 @@ public:
351 bool rewinded; 303 bool rewinded;
352}; 304};
353 305
354static void WorkControl4(void* control) {
355 auto* test_control = static_cast<TestControl4*>(control);
356 test_control->DoWork();
357}
358
359TEST_CASE("Fibers::Rewind", "[common]") { 306TEST_CASE("Fibers::Rewind", "[common]") {
360 TestControl4 test_control{}; 307 TestControl4 test_control{};
361 test_control.Execute(); 308 test_control.Execute();
diff --git a/src/tests/core/core_timing.cpp b/src/tests/core/core_timing.cpp
index 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/video_core/compatible_formats.cpp b/src/video_core/compatible_formats.cpp
index 014d880e2..4e75f33ca 100644
--- a/src/video_core/compatible_formats.cpp
+++ b/src/video_core/compatible_formats.cpp
@@ -131,9 +131,12 @@ constexpr std::array VIEW_CLASS_ASTC_8x8_RGBA{
131// PixelFormat::ASTC_2D_10X5_SRGB 131// PixelFormat::ASTC_2D_10X5_SRGB
132 132
133// Missing formats: 133// Missing formats:
134// PixelFormat::ASTC_2D_10X6_UNORM
135// PixelFormat::ASTC_2D_10X6_SRGB 134// PixelFormat::ASTC_2D_10X6_SRGB
136 135
136constexpr std::array VIEW_CLASS_ASTC_10x6_RGBA{
137 PixelFormat::ASTC_2D_10X6_UNORM,
138};
139
137constexpr std::array VIEW_CLASS_ASTC_10x8_RGBA{ 140constexpr std::array VIEW_CLASS_ASTC_10x8_RGBA{
138 PixelFormat::ASTC_2D_10X8_UNORM, 141 PixelFormat::ASTC_2D_10X8_UNORM,
139 PixelFormat::ASTC_2D_10X8_SRGB, 142 PixelFormat::ASTC_2D_10X8_SRGB,
@@ -226,6 +229,7 @@ constexpr Table MakeViewTable() {
226 EnableRange(view, VIEW_CLASS_ASTC_6x6_RGBA); 229 EnableRange(view, VIEW_CLASS_ASTC_6x6_RGBA);
227 EnableRange(view, VIEW_CLASS_ASTC_8x5_RGBA); 230 EnableRange(view, VIEW_CLASS_ASTC_8x5_RGBA);
228 EnableRange(view, VIEW_CLASS_ASTC_8x8_RGBA); 231 EnableRange(view, VIEW_CLASS_ASTC_8x8_RGBA);
232 EnableRange(view, VIEW_CLASS_ASTC_10x6_RGBA);
229 EnableRange(view, VIEW_CLASS_ASTC_10x8_RGBA); 233 EnableRange(view, VIEW_CLASS_ASTC_10x8_RGBA);
230 EnableRange(view, VIEW_CLASS_ASTC_10x10_RGBA); 234 EnableRange(view, VIEW_CLASS_ASTC_10x10_RGBA);
231 EnableRange(view, VIEW_CLASS_ASTC_12x12_RGBA); 235 EnableRange(view, VIEW_CLASS_ASTC_12x12_RGBA);
diff --git a/src/video_core/renderer_opengl/maxwell_to_gl.h b/src/video_core/renderer_opengl/maxwell_to_gl.h
index 644b60d73..9a72d0d6d 100644
--- a/src/video_core/renderer_opengl/maxwell_to_gl.h
+++ b/src/video_core/renderer_opengl/maxwell_to_gl.h
@@ -98,6 +98,7 @@ constexpr std::array<FormatTuple, VideoCore::Surface::MaxPixelFormat> FORMAT_TAB
98 {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR}, // ASTC_2D_10X8_SRGB 98 {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR}, // ASTC_2D_10X8_SRGB
99 {GL_COMPRESSED_RGBA_ASTC_6x6_KHR}, // ASTC_2D_6X6_UNORM 99 {GL_COMPRESSED_RGBA_ASTC_6x6_KHR}, // ASTC_2D_6X6_UNORM
100 {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR}, // ASTC_2D_6X6_SRGB 100 {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR}, // ASTC_2D_6X6_SRGB
101 {GL_COMPRESSED_RGBA_ASTC_10x6_KHR}, // ASTC_2D_10X6_UNORM
101 {GL_COMPRESSED_RGBA_ASTC_10x10_KHR}, // ASTC_2D_10X10_UNORM 102 {GL_COMPRESSED_RGBA_ASTC_10x10_KHR}, // ASTC_2D_10X10_UNORM
102 {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR}, // ASTC_2D_10X10_SRGB 103 {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR}, // ASTC_2D_10X10_SRGB
103 {GL_COMPRESSED_RGBA_ASTC_12x12_KHR}, // ASTC_2D_12X12_UNORM 104 {GL_COMPRESSED_RGBA_ASTC_12x12_KHR}, // ASTC_2D_12X12_UNORM
diff --git a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
index 193cbe15e..689164a6a 100644
--- a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
+++ b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
@@ -195,6 +195,7 @@ struct FormatTuple {
195 {VK_FORMAT_ASTC_10x8_SRGB_BLOCK}, // ASTC_2D_10X8_SRGB 195 {VK_FORMAT_ASTC_10x8_SRGB_BLOCK}, // ASTC_2D_10X8_SRGB
196 {VK_FORMAT_ASTC_6x6_UNORM_BLOCK}, // ASTC_2D_6X6_UNORM 196 {VK_FORMAT_ASTC_6x6_UNORM_BLOCK}, // ASTC_2D_6X6_UNORM
197 {VK_FORMAT_ASTC_6x6_SRGB_BLOCK}, // ASTC_2D_6X6_SRGB 197 {VK_FORMAT_ASTC_6x6_SRGB_BLOCK}, // ASTC_2D_6X6_SRGB
198 {VK_FORMAT_ASTC_10x6_UNORM_BLOCK}, // ASTC_2D_10X6_UNORM
198 {VK_FORMAT_ASTC_10x10_UNORM_BLOCK}, // ASTC_2D_10X10_UNORM 199 {VK_FORMAT_ASTC_10x10_UNORM_BLOCK}, // ASTC_2D_10X10_UNORM
199 {VK_FORMAT_ASTC_10x10_SRGB_BLOCK}, // ASTC_2D_10X10_SRGB 200 {VK_FORMAT_ASTC_10x10_SRGB_BLOCK}, // ASTC_2D_10X10_SRGB
200 {VK_FORMAT_ASTC_12x12_UNORM_BLOCK}, // ASTC_2D_12X12_UNORM 201 {VK_FORMAT_ASTC_12x12_UNORM_BLOCK}, // ASTC_2D_12X12_UNORM
diff --git a/src/video_core/surface.cpp b/src/video_core/surface.cpp
index 69c1b1e6d..eecd0deff 100644
--- a/src/video_core/surface.cpp
+++ b/src/video_core/surface.cpp
@@ -247,6 +247,7 @@ bool IsPixelFormatASTC(PixelFormat format) {
247 case PixelFormat::ASTC_2D_10X8_SRGB: 247 case PixelFormat::ASTC_2D_10X8_SRGB:
248 case PixelFormat::ASTC_2D_6X6_UNORM: 248 case PixelFormat::ASTC_2D_6X6_UNORM:
249 case PixelFormat::ASTC_2D_6X6_SRGB: 249 case PixelFormat::ASTC_2D_6X6_SRGB:
250 case PixelFormat::ASTC_2D_10X6_UNORM:
250 case PixelFormat::ASTC_2D_10X10_UNORM: 251 case PixelFormat::ASTC_2D_10X10_UNORM:
251 case PixelFormat::ASTC_2D_10X10_SRGB: 252 case PixelFormat::ASTC_2D_10X10_SRGB:
252 case PixelFormat::ASTC_2D_12X12_UNORM: 253 case PixelFormat::ASTC_2D_12X12_UNORM:
diff --git a/src/video_core/surface.h b/src/video_core/surface.h
index 75e055592..0175432ff 100644
--- a/src/video_core/surface.h
+++ b/src/video_core/surface.h
@@ -94,6 +94,7 @@ enum class PixelFormat {
94 ASTC_2D_10X8_SRGB, 94 ASTC_2D_10X8_SRGB,
95 ASTC_2D_6X6_UNORM, 95 ASTC_2D_6X6_UNORM,
96 ASTC_2D_6X6_SRGB, 96 ASTC_2D_6X6_SRGB,
97 ASTC_2D_10X6_UNORM,
97 ASTC_2D_10X10_UNORM, 98 ASTC_2D_10X10_UNORM,
98 ASTC_2D_10X10_SRGB, 99 ASTC_2D_10X10_SRGB,
99 ASTC_2D_12X12_UNORM, 100 ASTC_2D_12X12_UNORM,
@@ -227,6 +228,7 @@ constexpr std::array<u8, MaxPixelFormat> BLOCK_WIDTH_TABLE = {{
227 10, // ASTC_2D_10X8_SRGB 228 10, // ASTC_2D_10X8_SRGB
228 6, // ASTC_2D_6X6_UNORM 229 6, // ASTC_2D_6X6_UNORM
229 6, // ASTC_2D_6X6_SRGB 230 6, // ASTC_2D_6X6_SRGB
231 10, // ASTC_2D_10X6_UNORM
230 10, // ASTC_2D_10X10_UNORM 232 10, // ASTC_2D_10X10_UNORM
231 10, // ASTC_2D_10X10_SRGB 233 10, // ASTC_2D_10X10_SRGB
232 12, // ASTC_2D_12X12_UNORM 234 12, // ASTC_2D_12X12_UNORM
@@ -329,6 +331,7 @@ constexpr std::array<u8, MaxPixelFormat> BLOCK_HEIGHT_TABLE = {{
329 8, // ASTC_2D_10X8_SRGB 331 8, // ASTC_2D_10X8_SRGB
330 6, // ASTC_2D_6X6_UNORM 332 6, // ASTC_2D_6X6_UNORM
331 6, // ASTC_2D_6X6_SRGB 333 6, // ASTC_2D_6X6_SRGB
334 6, // ASTC_2D_10X6_UNORM
332 10, // ASTC_2D_10X10_UNORM 335 10, // ASTC_2D_10X10_UNORM
333 10, // ASTC_2D_10X10_SRGB 336 10, // ASTC_2D_10X10_SRGB
334 12, // ASTC_2D_12X12_UNORM 337 12, // ASTC_2D_12X12_UNORM
@@ -431,6 +434,7 @@ constexpr std::array<u8, MaxPixelFormat> BITS_PER_BLOCK_TABLE = {{
431 128, // ASTC_2D_10X8_SRGB 434 128, // ASTC_2D_10X8_SRGB
432 128, // ASTC_2D_6X6_UNORM 435 128, // ASTC_2D_6X6_UNORM
433 128, // ASTC_2D_6X6_SRGB 436 128, // ASTC_2D_6X6_SRGB
437 128, // ASTC_2D_10X6_UNORM
434 128, // ASTC_2D_10X10_UNORM 438 128, // ASTC_2D_10X10_UNORM
435 128, // ASTC_2D_10X10_SRGB 439 128, // ASTC_2D_10X10_SRGB
436 128, // ASTC_2D_12X12_UNORM 440 128, // ASTC_2D_12X12_UNORM
diff --git a/src/video_core/texture_cache/format_lookup_table.cpp b/src/video_core/texture_cache/format_lookup_table.cpp
index 0937768d6..1412aa076 100644
--- a/src/video_core/texture_cache/format_lookup_table.cpp
+++ b/src/video_core/texture_cache/format_lookup_table.cpp
@@ -206,6 +206,8 @@ PixelFormat PixelFormatFromTextureInfo(TextureFormat format, ComponentType red,
206 return PixelFormat::ASTC_2D_6X6_UNORM; 206 return PixelFormat::ASTC_2D_6X6_UNORM;
207 case Hash(TextureFormat::ASTC_2D_6X6, UNORM, SRGB): 207 case Hash(TextureFormat::ASTC_2D_6X6, UNORM, SRGB):
208 return PixelFormat::ASTC_2D_6X6_SRGB; 208 return PixelFormat::ASTC_2D_6X6_SRGB;
209 case Hash(TextureFormat::ASTC_2D_10X6, UNORM, LINEAR):
210 return PixelFormat::ASTC_2D_10X6_UNORM;
209 case Hash(TextureFormat::ASTC_2D_10X10, UNORM, LINEAR): 211 case Hash(TextureFormat::ASTC_2D_10X10, UNORM, LINEAR):
210 return PixelFormat::ASTC_2D_10X10_UNORM; 212 return PixelFormat::ASTC_2D_10X10_UNORM;
211 case Hash(TextureFormat::ASTC_2D_10X10, UNORM, SRGB): 213 case Hash(TextureFormat::ASTC_2D_10X10, UNORM, SRGB):
diff --git a/src/video_core/texture_cache/formatter.h b/src/video_core/texture_cache/formatter.h
index 1b78ed445..95a572604 100644
--- a/src/video_core/texture_cache/formatter.h
+++ b/src/video_core/texture_cache/formatter.h
@@ -175,6 +175,8 @@ struct fmt::formatter<VideoCore::Surface::PixelFormat> : fmt::formatter<fmt::str
175 return "ASTC_2D_6X6_UNORM"; 175 return "ASTC_2D_6X6_UNORM";
176 case PixelFormat::ASTC_2D_6X6_SRGB: 176 case PixelFormat::ASTC_2D_6X6_SRGB:
177 return "ASTC_2D_6X6_SRGB"; 177 return "ASTC_2D_6X6_SRGB";
178 case PixelFormat::ASTC_2D_10X6_UNORM:
179 return "ASTC_2D_10X6_UNORM";
178 case PixelFormat::ASTC_2D_10X10_UNORM: 180 case PixelFormat::ASTC_2D_10X10_UNORM:
179 return "ASTC_2D_10X10_UNORM"; 181 return "ASTC_2D_10X10_UNORM";
180 case PixelFormat::ASTC_2D_10X10_SRGB: 182 case PixelFormat::ASTC_2D_10X10_SRGB:
diff --git a/src/yuzu/applets/qt_web_browser.cpp b/src/yuzu/applets/qt_web_browser.cpp
index 283c04cd5..89bd482e0 100644
--- a/src/yuzu/applets/qt_web_browser.cpp
+++ b/src/yuzu/applets/qt_web_browser.cpp
@@ -2,6 +2,8 @@
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#ifdef YUZU_USE_QT_WEB_ENGINE 4#ifdef YUZU_USE_QT_WEB_ENGINE
5#include <bit>
6
5#include <QApplication> 7#include <QApplication>
6#include <QKeyEvent> 8#include <QKeyEvent>
7 9
@@ -52,8 +54,8 @@ QtNXWebEngineView::QtNXWebEngineView(QWidget* parent, Core::System& system,
52 : QWebEngineView(parent), input_subsystem{input_subsystem_}, 54 : QWebEngineView(parent), input_subsystem{input_subsystem_},
53 url_interceptor(std::make_unique<UrlRequestInterceptor>()), 55 url_interceptor(std::make_unique<UrlRequestInterceptor>()),
54 input_interpreter(std::make_unique<InputInterpreter>(system)), 56 input_interpreter(std::make_unique<InputInterpreter>(system)),
55 default_profile{QWebEngineProfile::defaultProfile()}, 57 default_profile{QWebEngineProfile::defaultProfile()}, global_settings{
56 global_settings{QWebEngineSettings::globalSettings()} { 58 default_profile->settings()} {
57 default_profile->setPersistentStoragePath(QString::fromStdString(Common::FS::PathToUTF8String( 59 default_profile->setPersistentStoragePath(QString::fromStdString(Common::FS::PathToUTF8String(
58 Common::FS::GetYuzuPath(Common::FS::YuzuPath::YuzuDir) / "qtwebengine"))); 60 Common::FS::GetYuzuPath(Common::FS::YuzuPath::YuzuDir) / "qtwebengine")));
59 61
@@ -78,7 +80,7 @@ QtNXWebEngineView::QtNXWebEngineView(QWidget* parent, Core::System& system,
78 default_profile->scripts()->insert(gamepad); 80 default_profile->scripts()->insert(gamepad);
79 default_profile->scripts()->insert(window_nx); 81 default_profile->scripts()->insert(window_nx);
80 82
81 default_profile->setRequestInterceptor(url_interceptor.get()); 83 default_profile->setUrlRequestInterceptor(url_interceptor.get());
82 84
83 global_settings->setAttribute(QWebEngineSettings::LocalContentCanAccessRemoteUrls, true); 85 global_settings->setAttribute(QWebEngineSettings::LocalContentCanAccessRemoteUrls, true);
84 global_settings->setAttribute(QWebEngineSettings::FullScreenSupportEnabled, true); 86 global_settings->setAttribute(QWebEngineSettings::FullScreenSupportEnabled, true);
@@ -211,8 +213,10 @@ template <Core::HID::NpadButton... T>
211void QtNXWebEngineView::HandleWindowFooterButtonPressedOnce() { 213void QtNXWebEngineView::HandleWindowFooterButtonPressedOnce() {
212 const auto f = [this](Core::HID::NpadButton button) { 214 const auto f = [this](Core::HID::NpadButton button) {
213 if (input_interpreter->IsButtonPressedOnce(button)) { 215 if (input_interpreter->IsButtonPressedOnce(button)) {
216 const auto button_index = std::countr_zero(static_cast<u64>(button));
217
214 page()->runJavaScript( 218 page()->runJavaScript(
215 QStringLiteral("yuzu_key_callbacks[%1] == null;").arg(static_cast<u8>(button)), 219 QStringLiteral("yuzu_key_callbacks[%1] == null;").arg(button_index),
216 [this, button](const QVariant& variant) { 220 [this, button](const QVariant& variant) {
217 if (variant.toBool()) { 221 if (variant.toBool()) {
218 switch (button) { 222 switch (button) {
@@ -236,7 +240,7 @@ void QtNXWebEngineView::HandleWindowFooterButtonPressedOnce() {
236 240
237 page()->runJavaScript( 241 page()->runJavaScript(
238 QStringLiteral("if (yuzu_key_callbacks[%1] != null) { yuzu_key_callbacks[%1](); }") 242 QStringLiteral("if (yuzu_key_callbacks[%1] != null) { yuzu_key_callbacks[%1](); }")
239 .arg(static_cast<u8>(button))); 243 .arg(button_index));
240 } 244 }
241 }; 245 };
242 246
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 9df4752be..a57b2e019 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()) {
@@ -143,8 +143,8 @@ void Config::ReadBasicSetting(Settings::BasicSetting<std::string>& setting) {
143 } 143 }
144} 144}
145 145
146template <typename Type> 146template <typename Type, bool ranged>
147void Config::ReadBasicSetting(Settings::BasicSetting<Type>& setting) { 147void Config::ReadBasicSetting(Settings::Setting<Type, ranged>& 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,23 +157,23 @@ 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());
164 qt_config->setValue(name, QString::fromStdString(value)); 164 qt_config->setValue(name, QString::fromStdString(value));
165} 165}
166 166
167template <typename Type> 167template <typename Type, bool ranged>
168void Config::WriteBasicSetting(const Settings::BasicSetting<Type>& setting) { 168void Config::WriteBasicSetting(const Settings::Setting<Type, ranged>& 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());
172 qt_config->setValue(name, value); 172 qt_config->setValue(name, value);
173} 173}
174 174
175template <typename Type> 175template <typename Type, bool ranged>
176void Config::WriteGlobalSetting(const Settings::Setting<Type>& setting) { 176void Config::WriteGlobalSetting(const Settings::SwitchableSetting<Type, ranged>& 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) {
@@ -1421,8 +1421,8 @@ QVariant Config::ReadSetting(const QString& name, const QVariant& default_value)
1421 return result; 1421 return result;
1422} 1422}
1423 1423
1424template <typename Type> 1424template <typename Type, bool ranged>
1425void Config::ReadGlobalSetting(Settings::Setting<Type>& setting) { 1425void Config::ReadGlobalSetting(Settings::SwitchableSetting<Type, ranged>& 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..d511b3dbd 100644
--- a/src/yuzu/configuration/config.h
+++ b/src/yuzu/configuration/config.h
@@ -159,8 +159,8 @@ private:
159 * 159 *
160 * @param The setting 160 * @param The setting
161 */ 161 */
162 template <typename Type> 162 template <typename Type, bool ranged>
163 void ReadGlobalSetting(Settings::Setting<Type>& setting); 163 void ReadGlobalSetting(Settings::SwitchableSetting<Type, ranged>& 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
@@ -168,8 +168,8 @@ private:
168 * 168 *
169 * @param The setting 169 * @param The setting
170 */ 170 */
171 template <typename Type> 171 template <typename Type, bool ranged>
172 void WriteGlobalSetting(const Settings::Setting<Type>& setting); 172 void WriteGlobalSetting(const Settings::SwitchableSetting<Type, ranged>& 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
@@ -177,15 +177,15 @@ private:
177 * 177 *
178 * @param The setting 178 * @param The setting
179 */ 179 */
180 template <typename Type> 180 template <typename Type, bool ranged>
181 void ReadBasicSetting(Settings::BasicSetting<Type>& setting); 181 void ReadBasicSetting(Settings::Setting<Type, ranged>& 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, bool ranged>
188 void WriteBasicSetting(const Settings::BasicSetting<Type>& setting); 188 void WriteBasicSetting(const Settings::Setting<Type, ranged>& 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..56800b6ff 100644
--- a/src/yuzu/configuration/configuration_shared.h
+++ b/src/yuzu/configuration/configuration_shared.h
@@ -25,10 +25,11 @@ 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, bool ranged>
31void ApplyPerGameSetting(Settings::Setting<Type>* setting, const QComboBox* combobox) { 31void ApplyPerGameSetting(Settings::SwitchableSetting<Type, ranged>* setting,
32 const QComboBox* combobox) {
32 if (Settings::IsConfiguringGlobal() && setting->UsingGlobal()) { 33 if (Settings::IsConfiguringGlobal() && setting->UsingGlobal()) {
33 setting->SetValue(static_cast<Type>(combobox->currentIndex())); 34 setting->SetValue(static_cast<Type>(combobox->currentIndex()));
34 } else if (!Settings::IsConfiguringGlobal()) { 35 } else if (!Settings::IsConfiguringGlobal()) {
@@ -43,10 +44,11 @@ void ApplyPerGameSetting(Settings::Setting<Type>* setting, const QComboBox* comb
43} 44}
44 45
45// Sets a Qt UI element given a Settings::Setting 46// Sets a Qt UI element given a Settings::Setting
46void SetPerGameSetting(QCheckBox* checkbox, const Settings::Setting<bool>* setting); 47void SetPerGameSetting(QCheckBox* checkbox, const Settings::SwitchableSetting<bool>* setting);
47 48
48template <typename Type> 49template <typename Type, bool ranged>
49void SetPerGameSetting(QComboBox* combobox, const Settings::Setting<Type>* setting) { 50void SetPerGameSetting(QComboBox* combobox,
51 const Settings::SwitchableSetting<Type, ranged>* setting) {
50 combobox->setCurrentIndex(setting->UsingGlobal() ? ConfigurationShared::USE_GLOBAL_INDEX 52 combobox->setCurrentIndex(setting->UsingGlobal() ? ConfigurationShared::USE_GLOBAL_INDEX
51 : static_cast<int>(setting->GetValue()) + 53 : static_cast<int>(setting->GetValue()) +
52 ConfigurationShared::USE_GLOBAL_OFFSET); 54 ConfigurationShared::USE_GLOBAL_OFFSET);
@@ -56,7 +58,7 @@ void SetPerGameSetting(QComboBox* combobox, const Settings::Setting<Type>* setti
56void SetHighlight(QWidget* widget, bool highlighted); 58void SetHighlight(QWidget* widget, bool highlighted);
57 59
58// Sets up a QCheckBox like a tristate one, given a Setting 60// Sets up a QCheckBox like a tristate one, given a Setting
59void SetColoredTristate(QCheckBox* checkbox, const Settings::Setting<bool>& setting, 61void SetColoredTristate(QCheckBox* checkbox, const Settings::SwitchableSetting<bool>& setting,
60 CheckState& tracker); 62 CheckState& tracker);
61void SetColoredTristate(QCheckBox* checkbox, bool global, bool state, bool global_state, 63void SetColoredTristate(QCheckBox* checkbox, bool global, bool state, bool global_state,
62 CheckState& tracker); 64 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..9db7115a2 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, bool ranged>
103void Config::ReadSetting(const std::string& group, Settings::BasicSetting<Type>& setting) { 103void Config::ReadSetting(const std::string& group, Settings::Setting<Type, ranged>& 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..32c03075f 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, bool ranged>
37 void ReadSetting(const std::string& group, Settings::BasicSetting<Type>& setting); 37 void ReadSetting(const std::string& group, Settings::Setting<Type, ranged>& setting);
38}; 38};