diff options
Diffstat (limited to 'src')
63 files changed, 863 insertions, 757 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 39d038493..39ae573b2 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -36,7 +36,6 @@ if (MSVC) # /GT - Supports fiber safety for data allocated using static thread-local storage add_compile_options( /MP - /Zi /Zm200 /Zo /permissive- @@ -79,6 +78,13 @@ if (MSVC) /we5245 # 'function': unreferenced function with internal linkage has been removed ) + if (USE_CCACHE) + # when caching, we need to use /Z7 to downgrade debug info to use an older but more cachable format + add_compile_options(/Z7) + else() + add_compile_options(/Zi) + endif() + if (ARCHITECTURE_x86_64) add_compile_options(/QIntel-jcc-erratum) 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 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include <limits> +#include <optional> #include <vector> #include "audio_core/audio_out.h" @@ -88,9 +89,12 @@ AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing_, Core::Memor stream = audio_out->OpenStream( core_timing, params.sample_rate, AudioCommon::STREAM_NUM_CHANNELS, fmt::format("AudioRenderer-Instance{}", instance_number), std::move(release_callback)); - process_event = Core::Timing::CreateEvent( - fmt::format("AudioRenderer-Instance{}-Process", instance_number), - [this](std::uintptr_t, std::chrono::nanoseconds) { ReleaseAndQueueBuffers(); }); + process_event = + Core::Timing::CreateEvent(fmt::format("AudioRenderer-Instance{}-Process", instance_number), + [this](std::uintptr_t, s64, std::chrono::nanoseconds) { + ReleaseAndQueueBuffers(); + return std::nullopt; + }); for (s32 i = 0; i < NUM_BUFFERS; ++i) { QueueMixedBuffer(i); } 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 ReleaseCallback&& release_callback_, SinkStream& sink_stream_, std::string&& name_) : sample_rate{sample_rate_}, format{format_}, release_callback{std::move(release_callback_)}, sink_stream{sink_stream_}, core_timing{core_timing_}, name{std::move(name_)} { - release_event = - Core::Timing::CreateEvent(name, [this](std::uintptr_t, std::chrono::nanoseconds ns_late) { + release_event = Core::Timing::CreateEvent( + name, [this](std::uintptr_t, s64 time, std::chrono::nanoseconds ns_late) { ReleaseActiveBuffer(ns_late); + return std::nullopt; }); } 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: } constexpr void Assign(const T& value) { +#ifdef _MSC_VER storage = static_cast<StorageType>((storage & ~mask) | FormatValue(value)); +#else + // Explicitly reload with memcpy to avoid compiler aliasing quirks + // regarding optimization: GCC/Clang clobber chained stores to + // different bitfields in the same struct with the last value. + StorageTypeWithEndian storage_; + std::memcpy(&storage_, &storage, sizeof(storage_)); + storage = static_cast<StorageType>((storage_ & ~mask) | FormatValue(value)); +#endif } [[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 @@ /// Helper macros to insert unused bytes or words to properly align structs. These values will be /// zero-initialized. #define INSERT_PADDING_BYTES(num_bytes) \ - std::array<u8, num_bytes> CONCAT2(pad, __LINE__) {} + [[maybe_unused]] std::array<u8, num_bytes> CONCAT2(pad, __LINE__) {} #define INSERT_PADDING_WORDS(num_words) \ - std::array<u32, num_words> CONCAT2(pad, __LINE__) {} + [[maybe_unused]] std::array<u32, num_words> CONCAT2(pad, __LINE__) {} /// These are similar to the INSERT_PADDING_* macros but do not zero-initialize the contents. /// This keeps the structure trivial to construct. -#define INSERT_PADDING_BYTES_NOINIT(num_bytes) std::array<u8, num_bytes> CONCAT2(pad, __LINE__) -#define INSERT_PADDING_WORDS_NOINIT(num_words) std::array<u32, num_words> CONCAT2(pad, __LINE__) +#define INSERT_PADDING_BYTES_NOINIT(num_bytes) \ + [[maybe_unused]] std::array<u8, num_bytes> CONCAT2(pad, __LINE__) +#define INSERT_PADDING_WORDS_NOINIT(num_words) \ + [[maybe_unused]] std::array<u32, num_words> CONCAT2(pad, __LINE__) #ifndef _MSC_VER 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 { VirtualBuffer<u8> rewind_stack; std::mutex guard; - std::function<void(void*)> entry_point; - std::function<void(void*)> rewind_point; - void* rewind_parameter{}; - void* start_parameter{}; + std::function<void()> entry_point; + std::function<void()> rewind_point; std::shared_ptr<Fiber> previous_fiber; bool is_thread_fiber{}; bool released{}; @@ -34,13 +32,8 @@ struct Fiber::FiberImpl { boost::context::detail::fcontext_t rewind_context{}; }; -void Fiber::SetStartParameter(void* new_parameter) { - impl->start_parameter = new_parameter; -} - -void Fiber::SetRewindPoint(std::function<void(void*)>&& rewind_func, void* rewind_param) { +void Fiber::SetRewindPoint(std::function<void()>&& rewind_func) { impl->rewind_point = std::move(rewind_func); - impl->rewind_parameter = rewind_param; } void Fiber::Start(boost::context::detail::transfer_t& transfer) { @@ -48,7 +41,7 @@ void Fiber::Start(boost::context::detail::transfer_t& transfer) { impl->previous_fiber->impl->context = transfer.fctx; impl->previous_fiber->impl->guard.unlock(); impl->previous_fiber.reset(); - impl->entry_point(impl->start_parameter); + impl->entry_point(); UNREACHABLE(); } @@ -59,7 +52,7 @@ void Fiber::OnRewind([[maybe_unused]] boost::context::detail::transfer_t& transf u8* tmp = impl->stack_limit; impl->stack_limit = impl->rewind_stack_limit; impl->rewind_stack_limit = tmp; - impl->rewind_point(impl->rewind_parameter); + impl->rewind_point(); UNREACHABLE(); } @@ -73,10 +66,8 @@ void Fiber::RewindStartFunc(boost::context::detail::transfer_t transfer) { fiber->OnRewind(transfer); } -Fiber::Fiber(std::function<void(void*)>&& entry_point_func, void* start_parameter) - : impl{std::make_unique<FiberImpl>()} { +Fiber::Fiber(std::function<void()>&& entry_point_func) : impl{std::make_unique<FiberImpl>()} { impl->entry_point = std::move(entry_point_func); - impl->start_parameter = start_parameter; impl->stack_limit = impl->stack.data(); impl->rewind_stack_limit = impl->rewind_stack.data(); 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 { */ class Fiber { public: - Fiber(std::function<void(void*)>&& entry_point_func, void* start_parameter); + Fiber(std::function<void()>&& entry_point_func); ~Fiber(); Fiber(const Fiber&) = delete; @@ -43,16 +43,13 @@ public: static void YieldTo(std::weak_ptr<Fiber> weak_from, Fiber& to); [[nodiscard]] static std::shared_ptr<Fiber> ThreadToFiber(); - void SetRewindPoint(std::function<void(void*)>&& rewind_func, void* rewind_param); + void SetRewindPoint(std::function<void()>&& rewind_func); void Rewind(); /// Only call from main thread's fiber void Exit(); - /// Changes the start parameter of the fiber. Has no effect if the fiber already started - void SetStartParameter(void* new_parameter); - private: Fiber(); diff --git a/src/common/settings.h b/src/common/settings.h index a507744a2..3583a2e70 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -101,15 +101,15 @@ struct ResolutionScalingInfo { } }; -/** The BasicSetting class is a simple resource manager. It defines a label and default value - * alongside the actual value of the setting for simpler and less-error prone use with frontend - * configurations. Setting a default value and label is required, though subclasses may deviate from - * this requirement. +/** The Setting class is a simple resource manager. It defines a label and default value alongside + * the actual value of the setting for simpler and less-error prone use with frontend + * configurations. Specifying a default value and label is required. A minimum and maximum range can + * be specified for sanitization. */ template <typename Type> -class BasicSetting { +class Setting { protected: - BasicSetting() = default; + Setting() = default; /** * Only sets the setting to the given initializer, leaving the other members to their default @@ -117,7 +117,7 @@ protected: * * @param global_val Initial value of the setting */ - explicit BasicSetting(const Type& global_val) : global{global_val} {} + explicit Setting(const Type& val) : value{val} {} public: /** @@ -126,9 +126,22 @@ public: * @param default_val Intial value of the setting, and default value of the setting * @param name Label for the setting */ - explicit BasicSetting(const Type& default_val, const std::string& name) - : default_value{default_val}, global{default_val}, label{name} {} - virtual ~BasicSetting() = default; + explicit Setting(const Type& default_val, const std::string& name) + : value{default_val}, default_value{default_val}, ranged{false}, label{name} {} + virtual ~Setting() = default; + + /** + * Sets a default value, minimum value, maximum value, and label. + * + * @param default_val Intial value of the setting, and default value of the setting + * @param min_val Sets the minimum allowed value of the setting + * @param max_val Sets the maximum allowed value of the setting + * @param name Label for the setting + */ + explicit Setting(const Type& default_val, const Type& min_val, const Type& max_val, + const std::string& name) + : value{default_val}, default_value{default_val}, maximum{max_val}, minimum{min_val}, + ranged{true}, label{name} {} /** * Returns a reference to the setting's value. @@ -136,17 +149,17 @@ public: * @returns A reference to the setting */ [[nodiscard]] virtual const Type& GetValue() const { - return global; + return value; } /** * Sets the setting to the given value. * - * @param value The desired value + * @param val The desired value */ - virtual void SetValue(const Type& value) { - Type temp{value}; - std::swap(global, temp); + virtual void SetValue(const Type& val) { + Type temp{(ranged) ? std::clamp(val, minimum, maximum) : val}; + std::swap(value, temp); } /** @@ -170,14 +183,14 @@ public: /** * Assigns a value to the setting. * - * @param value The desired setting value + * @param val The desired setting value * * @returns A reference to the setting */ - virtual const Type& operator=(const Type& value) { - Type temp{value}; - std::swap(global, temp); - return global; + virtual const Type& operator=(const Type& val) { + Type temp{(ranged) ? std::clamp(val, minimum, maximum) : val}; + std::swap(value, temp); + return value; } /** @@ -186,72 +199,28 @@ public: * @returns A reference to the setting */ explicit virtual operator const Type&() const { - return global; + return value; } protected: + Type value{}; ///< The setting const Type default_value{}; ///< The default value - Type global{}; ///< The setting + const Type maximum{}; ///< Maximum allowed value of the setting + const Type minimum{}; ///< Minimum allowed value of the setting + const bool ranged; ///< The setting has sanitization ranges const std::string label{}; ///< The setting's label }; /** - * BasicRangedSetting class is intended for use with quantifiable settings that need a more - * restrictive range than implicitly defined by its type. Implements a minimum and maximum that is - * simply used to sanitize SetValue and the assignment overload. - */ -template <typename Type> -class BasicRangedSetting : virtual public BasicSetting<Type> { -public: - /** - * Sets a default value, minimum value, maximum value, and label. - * - * @param default_val Intial value of the setting, and default value of the setting - * @param min_val Sets the minimum allowed value of the setting - * @param max_val Sets the maximum allowed value of the setting - * @param name Label for the setting - */ - explicit BasicRangedSetting(const Type& default_val, const Type& min_val, const Type& max_val, - const std::string& name) - : BasicSetting<Type>{default_val, name}, minimum{min_val}, maximum{max_val} {} - virtual ~BasicRangedSetting() = default; - - /** - * Like BasicSetting's SetValue, except value is clamped to the range of the setting. - * - * @param value The desired value - */ - void SetValue(const Type& value) override { - this->global = std::clamp(value, minimum, maximum); - } - - /** - * Like BasicSetting's assignment overload, except value is clamped to the range of the setting. - * - * @param value The desired value - * @returns A reference to the setting's value - */ - const Type& operator=(const Type& value) override { - this->global = std::clamp(value, minimum, maximum); - return this->global; - } - - const Type minimum; ///< Minimum allowed value of the setting - const Type maximum; ///< Maximum allowed value of the setting -}; - -/** - * The Setting class is a slightly more complex version of the BasicSetting class. This adds a + * The SwitchableSetting class is a slightly more complex version of the Setting class. This adds a * custom setting to switch to when a guest application specifically requires it. The effect is that * other components of the emulator can access the setting's intended value without any need for the * component to ask whether the custom or global setting is needed at the moment. * * By default, the global setting is used. - * - * Like the BasicSetting, this requires setting a default value and label to use. */ template <typename Type> -class Setting : virtual public BasicSetting<Type> { +class SwitchableSetting : virtual public Setting<Type> { public: /** * Sets a default value, label, and setting value. @@ -259,9 +228,21 @@ public: * @param default_val Intial value of the setting, and default value of the setting * @param name Label for the setting */ - explicit Setting(const Type& default_val, const std::string& name) - : BasicSetting<Type>(default_val, name) {} - virtual ~Setting() = default; + explicit SwitchableSetting(const Type& default_val, const std::string& name) + : Setting<Type>{default_val, name} {} + virtual ~SwitchableSetting() = default; + + /** + * Sets a default value, minimum value, maximum value, and label. + * + * @param default_val Intial value of the setting, and default value of the setting + * @param min_val Sets the minimum allowed value of the setting + * @param max_val Sets the maximum allowed value of the setting + * @param name Label for the setting + */ + explicit SwitchableSetting(const Type& default_val, const Type& min_val, const Type& max_val, + const std::string& name) + : Setting<Type>{default_val, min_val, max_val, name} {} /** * Tells this setting to represent either the global or custom setting when other member @@ -292,13 +273,13 @@ public: */ [[nodiscard]] virtual const Type& GetValue() const override { if (use_global) { - return this->global; + return this->value; } return custom; } [[nodiscard]] virtual const Type& GetValue(bool need_global) const { if (use_global || need_global) { - return this->global; + return this->value; } return custom; } @@ -306,12 +287,12 @@ public: /** * Sets the current setting value depending on the global state. * - * @param value The new value + * @param val The new value */ - void SetValue(const Type& value) override { - Type temp{value}; + void SetValue(const Type& val) override { + Type temp{(this->ranged) ? std::clamp(val, this->minimum, this->maximum) : val}; if (use_global) { - std::swap(this->global, temp); + std::swap(this->value, temp); } else { std::swap(custom, temp); } @@ -320,15 +301,15 @@ public: /** * Assigns the current setting value depending on the global state. * - * @param value The new value + * @param val The new value * * @returns A reference to the current setting value */ - const Type& operator=(const Type& value) override { - Type temp{value}; + const Type& operator=(const Type& val) override { + Type temp{(this->ranged) ? std::clamp(val, this->minimum, this->maximum) : val}; if (use_global) { - std::swap(this->global, temp); - return this->global; + std::swap(this->value, temp); + return this->value; } std::swap(custom, temp); return custom; @@ -341,7 +322,7 @@ public: */ virtual explicit operator const Type&() const override { if (use_global) { - return this->global; + return this->value; } return custom; } @@ -352,75 +333,6 @@ protected: }; /** - * RangedSetting is a Setting that implements a maximum and minimum value for its setting. Intended - * for use with quantifiable settings. - */ -template <typename Type> -class RangedSetting final : public BasicRangedSetting<Type>, public Setting<Type> { -public: - /** - * Sets a default value, minimum value, maximum value, and label. - * - * @param default_val Intial value of the setting, and default value of the setting - * @param min_val Sets the minimum allowed value of the setting - * @param max_val Sets the maximum allowed value of the setting - * @param name Label for the setting - */ - explicit RangedSetting(const Type& default_val, const Type& min_val, const Type& max_val, - const std::string& name) - : BasicSetting<Type>{default_val, name}, - BasicRangedSetting<Type>{default_val, min_val, max_val, name}, Setting<Type>{default_val, - name} {} - virtual ~RangedSetting() = default; - - // The following are needed to avoid a MSVC bug - // (source: https://stackoverflow.com/questions/469508) - [[nodiscard]] const Type& GetValue() const override { - return Setting<Type>::GetValue(); - } - [[nodiscard]] const Type& GetValue(bool need_global) const override { - return Setting<Type>::GetValue(need_global); - } - explicit operator const Type&() const override { - if (this->use_global) { - return this->global; - } - return this->custom; - } - - /** - * Like BasicSetting's SetValue, except value is clamped to the range of the setting. Sets the - * appropriate value depending on the global state. - * - * @param value The desired value - */ - void SetValue(const Type& value) override { - const Type temp = std::clamp(value, this->minimum, this->maximum); - if (this->use_global) { - this->global = temp; - } - this->custom = temp; - } - - /** - * Like BasicSetting's assignment overload, except value is clamped to the range of the setting. - * Uses the appropriate value depending on the global state. - * - * @param value The desired value - * @returns A reference to the setting's value - */ - const Type& operator=(const Type& value) override { - const Type temp = std::clamp(value, this->minimum, this->maximum); - if (this->use_global) { - this->global = temp; - return this->global; - } - this->custom = temp; - return this->custom; - } -}; - -/** * The InputSetting class allows for getting a reference to either the global or custom members. * This is required as we cannot easily modify the values of user-defined types within containers * using the SetValue() member function found in the Setting class. The primary purpose of this @@ -431,7 +343,7 @@ template <typename Type> class InputSetting final { public: InputSetting() = default; - explicit InputSetting(Type val) : BasicSetting<Type>(val) {} + explicit InputSetting(Type val) : Setting<Type>(val) {} ~InputSetting() = default; void SetGlobal(bool to_global) { use_global = to_global; @@ -459,175 +371,175 @@ struct TouchFromButtonMap { struct Values { // Audio - BasicSetting<std::string> audio_device_id{"auto", "output_device"}; - BasicSetting<std::string> sink_id{"auto", "output_engine"}; - BasicSetting<bool> audio_muted{false, "audio_muted"}; - RangedSetting<u8> volume{100, 0, 100, "volume"}; + Setting<std::string> audio_device_id{"auto", "output_device"}; + Setting<std::string> sink_id{"auto", "output_engine"}; + Setting<bool> audio_muted{false, "audio_muted"}; + SwitchableSetting<u8> volume{100, 0, 100, "volume"}; // Core - Setting<bool> use_multi_core{true, "use_multi_core"}; - Setting<bool> use_extended_memory_layout{false, "use_extended_memory_layout"}; + SwitchableSetting<bool> use_multi_core{true, "use_multi_core"}; + SwitchableSetting<bool> use_extended_memory_layout{false, "use_extended_memory_layout"}; // Cpu - RangedSetting<CPUAccuracy> cpu_accuracy{CPUAccuracy::Auto, CPUAccuracy::Auto, - CPUAccuracy::Paranoid, "cpu_accuracy"}; + SwitchableSetting<CPUAccuracy> cpu_accuracy{CPUAccuracy::Auto, CPUAccuracy::Auto, + CPUAccuracy::Paranoid, "cpu_accuracy"}; // TODO: remove cpu_accuracy_first_time, migration setting added 8 July 2021 - BasicSetting<bool> cpu_accuracy_first_time{true, "cpu_accuracy_first_time"}; - BasicSetting<bool> cpu_debug_mode{false, "cpu_debug_mode"}; - - BasicSetting<bool> cpuopt_page_tables{true, "cpuopt_page_tables"}; - BasicSetting<bool> cpuopt_block_linking{true, "cpuopt_block_linking"}; - BasicSetting<bool> cpuopt_return_stack_buffer{true, "cpuopt_return_stack_buffer"}; - BasicSetting<bool> cpuopt_fast_dispatcher{true, "cpuopt_fast_dispatcher"}; - BasicSetting<bool> cpuopt_context_elimination{true, "cpuopt_context_elimination"}; - BasicSetting<bool> cpuopt_const_prop{true, "cpuopt_const_prop"}; - BasicSetting<bool> cpuopt_misc_ir{true, "cpuopt_misc_ir"}; - BasicSetting<bool> cpuopt_reduce_misalign_checks{true, "cpuopt_reduce_misalign_checks"}; - BasicSetting<bool> cpuopt_fastmem{true, "cpuopt_fastmem"}; - BasicSetting<bool> cpuopt_fastmem_exclusives{true, "cpuopt_fastmem_exclusives"}; - BasicSetting<bool> cpuopt_recompile_exclusives{true, "cpuopt_recompile_exclusives"}; - - Setting<bool> cpuopt_unsafe_unfuse_fma{true, "cpuopt_unsafe_unfuse_fma"}; - Setting<bool> cpuopt_unsafe_reduce_fp_error{true, "cpuopt_unsafe_reduce_fp_error"}; - Setting<bool> cpuopt_unsafe_ignore_standard_fpcr{true, "cpuopt_unsafe_ignore_standard_fpcr"}; - Setting<bool> cpuopt_unsafe_inaccurate_nan{true, "cpuopt_unsafe_inaccurate_nan"}; - Setting<bool> cpuopt_unsafe_fastmem_check{true, "cpuopt_unsafe_fastmem_check"}; - Setting<bool> cpuopt_unsafe_ignore_global_monitor{true, "cpuopt_unsafe_ignore_global_monitor"}; + Setting<bool> cpu_accuracy_first_time{true, "cpu_accuracy_first_time"}; + Setting<bool> cpu_debug_mode{false, "cpu_debug_mode"}; + + Setting<bool> cpuopt_page_tables{true, "cpuopt_page_tables"}; + Setting<bool> cpuopt_block_linking{true, "cpuopt_block_linking"}; + Setting<bool> cpuopt_return_stack_buffer{true, "cpuopt_return_stack_buffer"}; + Setting<bool> cpuopt_fast_dispatcher{true, "cpuopt_fast_dispatcher"}; + Setting<bool> cpuopt_context_elimination{true, "cpuopt_context_elimination"}; + Setting<bool> cpuopt_const_prop{true, "cpuopt_const_prop"}; + Setting<bool> cpuopt_misc_ir{true, "cpuopt_misc_ir"}; + Setting<bool> cpuopt_reduce_misalign_checks{true, "cpuopt_reduce_misalign_checks"}; + Setting<bool> cpuopt_fastmem{true, "cpuopt_fastmem"}; + Setting<bool> cpuopt_fastmem_exclusives{true, "cpuopt_fastmem_exclusives"}; + Setting<bool> cpuopt_recompile_exclusives{true, "cpuopt_recompile_exclusives"}; + + SwitchableSetting<bool> cpuopt_unsafe_unfuse_fma{true, "cpuopt_unsafe_unfuse_fma"}; + SwitchableSetting<bool> cpuopt_unsafe_reduce_fp_error{true, "cpuopt_unsafe_reduce_fp_error"}; + SwitchableSetting<bool> cpuopt_unsafe_ignore_standard_fpcr{ + true, "cpuopt_unsafe_ignore_standard_fpcr"}; + SwitchableSetting<bool> cpuopt_unsafe_inaccurate_nan{true, "cpuopt_unsafe_inaccurate_nan"}; + SwitchableSetting<bool> cpuopt_unsafe_fastmem_check{true, "cpuopt_unsafe_fastmem_check"}; + SwitchableSetting<bool> cpuopt_unsafe_ignore_global_monitor{ + true, "cpuopt_unsafe_ignore_global_monitor"}; // Renderer - RangedSetting<RendererBackend> renderer_backend{ + SwitchableSetting<RendererBackend> renderer_backend{ RendererBackend::Vulkan, RendererBackend::OpenGL, RendererBackend::Vulkan, "backend"}; - BasicSetting<bool> renderer_debug{false, "debug"}; - BasicSetting<bool> renderer_shader_feedback{false, "shader_feedback"}; - BasicSetting<bool> enable_nsight_aftermath{false, "nsight_aftermath"}; - BasicSetting<bool> disable_shader_loop_safety_checks{false, - "disable_shader_loop_safety_checks"}; - Setting<int> vulkan_device{0, "vulkan_device"}; + Setting<bool> renderer_debug{false, "debug"}; + Setting<bool> renderer_shader_feedback{false, "shader_feedback"}; + Setting<bool> enable_nsight_aftermath{false, "nsight_aftermath"}; + Setting<bool> disable_shader_loop_safety_checks{false, "disable_shader_loop_safety_checks"}; + SwitchableSetting<int> vulkan_device{0, "vulkan_device"}; ResolutionScalingInfo resolution_info{}; - Setting<ResolutionSetup> resolution_setup{ResolutionSetup::Res1X, "resolution_setup"}; - Setting<ScalingFilter> scaling_filter{ScalingFilter::Bilinear, "scaling_filter"}; - Setting<AntiAliasing> anti_aliasing{AntiAliasing::None, "anti_aliasing"}; + SwitchableSetting<ResolutionSetup> resolution_setup{ResolutionSetup::Res1X, "resolution_setup"}; + SwitchableSetting<ScalingFilter> scaling_filter{ScalingFilter::Bilinear, "scaling_filter"}; + SwitchableSetting<AntiAliasing> anti_aliasing{AntiAliasing::None, "anti_aliasing"}; // *nix platforms may have issues with the borderless windowed fullscreen mode. // Default to exclusive fullscreen on these platforms for now. - RangedSetting<FullscreenMode> fullscreen_mode{ + SwitchableSetting<FullscreenMode> fullscreen_mode{ #ifdef _WIN32 FullscreenMode::Borderless, #else FullscreenMode::Exclusive, #endif FullscreenMode::Borderless, FullscreenMode::Exclusive, "fullscreen_mode"}; - RangedSetting<int> aspect_ratio{0, 0, 3, "aspect_ratio"}; - RangedSetting<int> max_anisotropy{0, 0, 5, "max_anisotropy"}; - Setting<bool> use_speed_limit{true, "use_speed_limit"}; - RangedSetting<u16> speed_limit{100, 0, 9999, "speed_limit"}; - Setting<bool> use_disk_shader_cache{true, "use_disk_shader_cache"}; - RangedSetting<GPUAccuracy> gpu_accuracy{GPUAccuracy::High, GPUAccuracy::Normal, - GPUAccuracy::Extreme, "gpu_accuracy"}; - Setting<bool> use_asynchronous_gpu_emulation{true, "use_asynchronous_gpu_emulation"}; - Setting<NvdecEmulation> nvdec_emulation{NvdecEmulation::GPU, "nvdec_emulation"}; - Setting<bool> accelerate_astc{true, "accelerate_astc"}; - Setting<bool> use_vsync{true, "use_vsync"}; - RangedSetting<u16> fps_cap{1000, 1, 1000, "fps_cap"}; - BasicSetting<bool> disable_fps_limit{false, "disable_fps_limit"}; - RangedSetting<ShaderBackend> shader_backend{ShaderBackend::GLASM, ShaderBackend::GLSL, - ShaderBackend::SPIRV, "shader_backend"}; - Setting<bool> use_asynchronous_shaders{false, "use_asynchronous_shaders"}; - Setting<bool> use_fast_gpu_time{true, "use_fast_gpu_time"}; - - Setting<u8> bg_red{0, "bg_red"}; - Setting<u8> bg_green{0, "bg_green"}; - Setting<u8> bg_blue{0, "bg_blue"}; + SwitchableSetting<int> aspect_ratio{0, 0, 3, "aspect_ratio"}; + SwitchableSetting<int> max_anisotropy{0, 0, 5, "max_anisotropy"}; + SwitchableSetting<bool> use_speed_limit{true, "use_speed_limit"}; + SwitchableSetting<u16> speed_limit{100, 0, 9999, "speed_limit"}; + SwitchableSetting<bool> use_disk_shader_cache{true, "use_disk_shader_cache"}; + SwitchableSetting<GPUAccuracy> gpu_accuracy{GPUAccuracy::High, GPUAccuracy::Normal, + GPUAccuracy::Extreme, "gpu_accuracy"}; + SwitchableSetting<bool> use_asynchronous_gpu_emulation{true, "use_asynchronous_gpu_emulation"}; + SwitchableSetting<NvdecEmulation> nvdec_emulation{NvdecEmulation::GPU, "nvdec_emulation"}; + SwitchableSetting<bool> accelerate_astc{true, "accelerate_astc"}; + SwitchableSetting<bool> use_vsync{true, "use_vsync"}; + SwitchableSetting<u16> fps_cap{1000, 1, 1000, "fps_cap"}; + Setting<bool> disable_fps_limit{false, "disable_fps_limit"}; + SwitchableSetting<ShaderBackend> shader_backend{ShaderBackend::GLASM, ShaderBackend::GLSL, + ShaderBackend::SPIRV, "shader_backend"}; + SwitchableSetting<bool> use_asynchronous_shaders{false, "use_asynchronous_shaders"}; + SwitchableSetting<bool> use_fast_gpu_time{true, "use_fast_gpu_time"}; + + SwitchableSetting<u8> bg_red{0, "bg_red"}; + SwitchableSetting<u8> bg_green{0, "bg_green"}; + SwitchableSetting<u8> bg_blue{0, "bg_blue"}; // System - Setting<std::optional<u32>> rng_seed{std::optional<u32>(), "rng_seed"}; + SwitchableSetting<std::optional<u32>> rng_seed{std::optional<u32>(), "rng_seed"}; // Measured in seconds since epoch std::optional<s64> custom_rtc; // Set on game boot, reset on stop. Seconds difference between current time and `custom_rtc` s64 custom_rtc_differential; - BasicSetting<s32> current_user{0, "current_user"}; - RangedSetting<s32> language_index{1, 0, 17, "language_index"}; - RangedSetting<s32> region_index{1, 0, 6, "region_index"}; - RangedSetting<s32> time_zone_index{0, 0, 45, "time_zone_index"}; - RangedSetting<s32> sound_index{1, 0, 2, "sound_index"}; + Setting<s32> current_user{0, "current_user"}; + SwitchableSetting<s32> language_index{1, 0, 17, "language_index"}; + SwitchableSetting<s32> region_index{1, 0, 6, "region_index"}; + SwitchableSetting<s32> time_zone_index{0, 0, 45, "time_zone_index"}; + SwitchableSetting<s32> sound_index{1, 0, 2, "sound_index"}; // Controls InputSetting<std::array<PlayerInput, 10>> players; - Setting<bool> use_docked_mode{true, "use_docked_mode"}; + SwitchableSetting<bool> use_docked_mode{true, "use_docked_mode"}; - BasicSetting<bool> enable_raw_input{false, "enable_raw_input"}; - BasicSetting<bool> controller_navigation{true, "controller_navigation"}; + Setting<bool> enable_raw_input{false, "enable_raw_input"}; + Setting<bool> controller_navigation{true, "controller_navigation"}; - Setting<bool> vibration_enabled{true, "vibration_enabled"}; - Setting<bool> enable_accurate_vibrations{false, "enable_accurate_vibrations"}; + SwitchableSetting<bool> vibration_enabled{true, "vibration_enabled"}; + SwitchableSetting<bool> enable_accurate_vibrations{false, "enable_accurate_vibrations"}; - Setting<bool> motion_enabled{true, "motion_enabled"}; - BasicSetting<std::string> udp_input_servers{"127.0.0.1:26760", "udp_input_servers"}; - BasicSetting<bool> enable_udp_controller{false, "enable_udp_controller"}; + SwitchableSetting<bool> motion_enabled{true, "motion_enabled"}; + Setting<std::string> udp_input_servers{"127.0.0.1:26760", "udp_input_servers"}; + Setting<bool> enable_udp_controller{false, "enable_udp_controller"}; - BasicSetting<bool> pause_tas_on_load{true, "pause_tas_on_load"}; - BasicSetting<bool> tas_enable{false, "tas_enable"}; - BasicSetting<bool> tas_loop{false, "tas_loop"}; + Setting<bool> pause_tas_on_load{true, "pause_tas_on_load"}; + Setting<bool> tas_enable{false, "tas_enable"}; + Setting<bool> tas_loop{false, "tas_loop"}; - BasicSetting<bool> mouse_panning{false, "mouse_panning"}; - BasicRangedSetting<u8> mouse_panning_sensitivity{10, 1, 100, "mouse_panning_sensitivity"}; - BasicSetting<bool> mouse_enabled{false, "mouse_enabled"}; + Setting<bool> mouse_panning{false, "mouse_panning"}; + Setting<u8> mouse_panning_sensitivity{10, 1, 100, "mouse_panning_sensitivity"}; + Setting<bool> mouse_enabled{false, "mouse_enabled"}; - BasicSetting<bool> emulate_analog_keyboard{false, "emulate_analog_keyboard"}; - BasicSetting<bool> keyboard_enabled{false, "keyboard_enabled"}; + Setting<bool> emulate_analog_keyboard{false, "emulate_analog_keyboard"}; + Setting<bool> keyboard_enabled{false, "keyboard_enabled"}; - BasicSetting<bool> debug_pad_enabled{false, "debug_pad_enabled"}; + Setting<bool> debug_pad_enabled{false, "debug_pad_enabled"}; ButtonsRaw debug_pad_buttons; AnalogsRaw debug_pad_analogs; TouchscreenInput touchscreen; - BasicSetting<std::string> touch_device{"min_x:100,min_y:50,max_x:1800,max_y:850", - "touch_device"}; - BasicSetting<int> touch_from_button_map_index{0, "touch_from_button_map"}; + Setting<std::string> touch_device{"min_x:100,min_y:50,max_x:1800,max_y:850", "touch_device"}; + Setting<int> touch_from_button_map_index{0, "touch_from_button_map"}; std::vector<TouchFromButtonMap> touch_from_button_maps; - BasicSetting<bool> enable_ring_controller{true, "enable_ring_controller"}; + Setting<bool> enable_ring_controller{true, "enable_ring_controller"}; RingconRaw ringcon_analogs; // Data Storage - BasicSetting<bool> use_virtual_sd{true, "use_virtual_sd"}; - BasicSetting<bool> gamecard_inserted{false, "gamecard_inserted"}; - BasicSetting<bool> gamecard_current_game{false, "gamecard_current_game"}; - BasicSetting<std::string> gamecard_path{std::string(), "gamecard_path"}; + Setting<bool> use_virtual_sd{true, "use_virtual_sd"}; + Setting<bool> gamecard_inserted{false, "gamecard_inserted"}; + Setting<bool> gamecard_current_game{false, "gamecard_current_game"}; + Setting<std::string> gamecard_path{std::string(), "gamecard_path"}; // Debugging bool record_frame_times; - BasicSetting<bool> use_gdbstub{false, "use_gdbstub"}; - BasicSetting<u16> gdbstub_port{6543, "gdbstub_port"}; - BasicSetting<std::string> program_args{std::string(), "program_args"}; - BasicSetting<bool> dump_exefs{false, "dump_exefs"}; - BasicSetting<bool> dump_nso{false, "dump_nso"}; - BasicSetting<bool> dump_shaders{false, "dump_shaders"}; - BasicSetting<bool> dump_macros{false, "dump_macros"}; - BasicSetting<bool> enable_fs_access_log{false, "enable_fs_access_log"}; - BasicSetting<bool> reporting_services{false, "reporting_services"}; - BasicSetting<bool> quest_flag{false, "quest_flag"}; - BasicSetting<bool> disable_macro_jit{false, "disable_macro_jit"}; - BasicSetting<bool> extended_logging{false, "extended_logging"}; - BasicSetting<bool> use_debug_asserts{false, "use_debug_asserts"}; - BasicSetting<bool> use_auto_stub{false, "use_auto_stub"}; - BasicSetting<bool> enable_all_controllers{false, "enable_all_controllers"}; + Setting<bool> use_gdbstub{false, "use_gdbstub"}; + Setting<u16> gdbstub_port{6543, "gdbstub_port"}; + Setting<std::string> program_args{std::string(), "program_args"}; + Setting<bool> dump_exefs{false, "dump_exefs"}; + Setting<bool> dump_nso{false, "dump_nso"}; + Setting<bool> dump_shaders{false, "dump_shaders"}; + Setting<bool> dump_macros{false, "dump_macros"}; + Setting<bool> enable_fs_access_log{false, "enable_fs_access_log"}; + Setting<bool> reporting_services{false, "reporting_services"}; + Setting<bool> quest_flag{false, "quest_flag"}; + Setting<bool> disable_macro_jit{false, "disable_macro_jit"}; + Setting<bool> extended_logging{false, "extended_logging"}; + Setting<bool> use_debug_asserts{false, "use_debug_asserts"}; + Setting<bool> use_auto_stub{false, "use_auto_stub"}; + Setting<bool> enable_all_controllers{false, "enable_all_controllers"}; // Miscellaneous - BasicSetting<std::string> log_filter{"*:Info", "log_filter"}; - BasicSetting<bool> use_dev_keys{false, "use_dev_keys"}; + Setting<std::string> log_filter{"*:Info", "log_filter"}; + Setting<bool> use_dev_keys{false, "use_dev_keys"}; // Network - BasicSetting<std::string> network_interface{std::string(), "network_interface"}; + Setting<std::string> network_interface{std::string(), "network_interface"}; // WebService - BasicSetting<bool> enable_telemetry{true, "enable_telemetry"}; - BasicSetting<std::string> web_api_url{"https://api.yuzu-emu.org", "web_api_url"}; - BasicSetting<std::string> yuzu_username{std::string(), "yuzu_username"}; - BasicSetting<std::string> yuzu_token{std::string(), "yuzu_token"}; + Setting<bool> enable_telemetry{true, "enable_telemetry"}; + Setting<std::string> web_api_url{"https://api.yuzu-emu.org", "web_api_url"}; + Setting<std::string> yuzu_username{std::string(), "yuzu_username"}; + Setting<std::string> yuzu_token{std::string(), "yuzu_token"}; // Add-Ons std::map<u64, std::vector<std::string>> disabled_addons; diff --git a/src/common/thread.cpp b/src/common/thread.cpp index f932a7290..919e33af9 100644 --- a/src/common/thread.cpp +++ b/src/common/thread.cpp @@ -47,6 +47,9 @@ void SetCurrentThreadPriority(ThreadPriority new_priority) { case ThreadPriority::VeryHigh: windows_priority = THREAD_PRIORITY_HIGHEST; break; + case ThreadPriority::Critical: + windows_priority = THREAD_PRIORITY_TIME_CRITICAL; + break; default: windows_priority = THREAD_PRIORITY_NORMAL; break; @@ -59,9 +62,10 @@ void SetCurrentThreadPriority(ThreadPriority new_priority) { void SetCurrentThreadPriority(ThreadPriority new_priority) { pthread_t this_thread = pthread_self(); - s32 max_prio = sched_get_priority_max(SCHED_OTHER); - s32 min_prio = sched_get_priority_min(SCHED_OTHER); - u32 level = static_cast<u32>(new_priority) + 1; + const auto scheduling_type = SCHED_OTHER; + s32 max_prio = sched_get_priority_max(scheduling_type); + s32 min_prio = sched_get_priority_min(scheduling_type); + u32 level = std::max(static_cast<u32>(new_priority) + 1, 4U); struct sched_param params; if (max_prio > min_prio) { @@ -70,7 +74,7 @@ void SetCurrentThreadPriority(ThreadPriority new_priority) { params.sched_priority = min_prio - ((min_prio - max_prio) * level) / 4; } - pthread_setschedparam(this_thread, SCHED_OTHER, ¶ms); + pthread_setschedparam(this_thread, scheduling_type, ¶ms); } #endif diff --git a/src/common/thread.h b/src/common/thread.h index a63122516..1552f58e0 100644 --- a/src/common/thread.h +++ b/src/common/thread.h @@ -92,6 +92,7 @@ enum class ThreadPriority : u32 { Normal = 1, High = 2, VeryHigh = 3, + Critical = 4, }; void SetCurrentThreadPriority(ThreadPriority new_priority); diff --git a/src/common/uint128.h b/src/common/uint128.h index f890ffec2..199d0f55e 100644 --- a/src/common/uint128.h +++ b/src/common/uint128.h @@ -31,12 +31,17 @@ namespace Common { return _udiv128(r[1], r[0], d, &remainder); #endif #else +#ifdef __SIZEOF_INT128__ + const auto product = static_cast<unsigned __int128>(a) * static_cast<unsigned __int128>(b); + return static_cast<u64>(product / d); +#else const u64 diva = a / d; const u64 moda = a % d; const u64 divb = b / d; const u64 modb = b % d; return diva * b + moda * divb + moda * modb / d; #endif +#endif } // This function multiplies 2 u64 values and produces a u128 value; diff --git a/src/common/x64/native_clock.cpp b/src/common/x64/native_clock.cpp index 1b7194503..6aaa8cdf9 100644 --- a/src/common/x64/native_clock.cpp +++ b/src/common/x64/native_clock.cpp @@ -75,8 +75,8 @@ NativeClock::NativeClock(u64 emulated_cpu_frequency_, u64 emulated_clock_frequen } u64 NativeClock::GetRTSC() { - TimePoint new_time_point{}; TimePoint current_time_point{}; + TimePoint new_time_point{}; current_time_point.pack = Common::AtomicLoad128(time_point.pack.data()); do { @@ -89,8 +89,7 @@ u64 NativeClock::GetRTSC() { new_time_point.inner.accumulated_ticks = current_time_point.inner.accumulated_ticks + diff; } while (!Common::AtomicCompareAndSwap(time_point.pack.data(), new_time_point.pack, current_time_point.pack, current_time_point.pack)); - /// The clock cannot be more precise than the guest timer, remove the lower bits - return new_time_point.inner.accumulated_ticks & inaccuracy_mask; + return new_time_point.inner.accumulated_ticks; } void NativeClock::Pause(bool is_paused) { diff --git a/src/common/x64/native_clock.h b/src/common/x64/native_clock.h index 30d2ba2e9..38ae7a462 100644 --- a/src/common/x64/native_clock.h +++ b/src/common/x64/native_clock.h @@ -37,12 +37,8 @@ private: } inner; }; - /// value used to reduce the native clocks accuracy as some apss rely on - /// undefined behavior where the level of accuracy in the clock shouldn't - /// be higher. - static constexpr u64 inaccuracy_mask = ~(UINT64_C(0x400) - 1); - TimePoint time_point; + // factors u64 clock_rtsc_factor{}; u64 cpu_rtsc_factor{}; diff --git a/src/core/arm/arm_interface.cpp b/src/core/arm/arm_interface.cpp index 8e095cdcd..cef79b245 100644 --- a/src/core/arm/arm_interface.cpp +++ b/src/core/arm/arm_interface.cpp @@ -1,6 +1,10 @@ // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#ifndef _MSC_VER +#include <cxxabi.h> +#endif + #include <map> #include <optional> #include "common/bit_field.h" @@ -68,8 +72,19 @@ void ARM_Interface::SymbolicateBacktrace(Core::System& system, std::vector<Backt if (symbol_set != symbols.end()) { const auto symbol = Symbols::GetSymbolName(symbol_set->second, entry.offset); if (symbol.has_value()) { +#ifdef _MSC_VER // TODO(DarkLordZach): Add demangling of symbol names. entry.name = *symbol; +#else + int status{-1}; + char* demangled{abi::__cxa_demangle(symbol->c_str(), nullptr, nullptr, &status)}; + if (status == 0 && demangled != nullptr) { + entry.name = demangled; + std::free(demangled); + } else { + entry.name = *symbol; + } +#endif } } } @@ -119,16 +134,23 @@ void ARM_Interface::Run() { } system.ExitDynarmicProfile(); - // Notify the debugger and go to sleep if a breakpoint was hit. - if (Has(hr, breakpoint)) { + // Notify the debugger and go to sleep if a breakpoint was hit, + // or if the thread is unable to continue for any reason. + if (Has(hr, breakpoint) || Has(hr, no_execute)) { RewindBreakpointInstruction(); - system.GetDebugger().NotifyThreadStopped(current_thread); - current_thread->RequestSuspend(SuspendType::Debug); + if (system.DebuggerEnabled()) { + system.GetDebugger().NotifyThreadStopped(current_thread); + } + current_thread->RequestSuspend(Kernel::SuspendType::Debug); break; } + + // Notify the debugger and go to sleep if a watchpoint was hit. if (Has(hr, watchpoint)) { RewindBreakpointInstruction(); - system.GetDebugger().NotifyThreadWatchpoint(current_thread, *HaltedWatchpoint()); + if (system.DebuggerEnabled()) { + system.GetDebugger().NotifyThreadWatchpoint(current_thread, *HaltedWatchpoint()); + } current_thread->RequestSuspend(SuspendType::Debug); break; } 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: static constexpr Dynarmic::HaltReason svc_call = Dynarmic::HaltReason::UserDefined3; static constexpr Dynarmic::HaltReason breakpoint = Dynarmic::HaltReason::UserDefined4; static constexpr Dynarmic::HaltReason watchpoint = Dynarmic::HaltReason::UserDefined5; + static constexpr Dynarmic::HaltReason no_execute = Dynarmic::HaltReason::UserDefined6; protected: /// System context that this ARM interface is running under. diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp index 8c90c8be0..1be5fe1c1 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp @@ -48,6 +48,12 @@ public: CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Read); return memory.Read64(vaddr); } + std::optional<u32> MemoryReadCode(u32 vaddr) override { + if (!memory.IsValidVirtualAddressRange(vaddr, sizeof(u32))) { + return std::nullopt; + } + return MemoryRead32(vaddr); + } void MemoryWrite8(u32 vaddr, u8 value) override { if (CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write)) { @@ -89,21 +95,28 @@ public: void InterpreterFallback(u32 pc, std::size_t num_instructions) override { parent.LogBacktrace(); - UNIMPLEMENTED_MSG("This should never happen, pc = {:08X}, code = {:08X}", pc, - MemoryReadCode(pc)); + LOG_ERROR(Core_ARM, + "Unimplemented instruction @ 0x{:X} for {} instructions (instr = {:08X})", pc, + num_instructions, MemoryRead32(pc)); } void ExceptionRaised(u32 pc, Dynarmic::A32::Exception exception) override { - if (debugger_enabled) { - parent.SaveContext(parent.breakpoint_context); - parent.jit.load()->HaltExecution(ARM_Interface::breakpoint); + switch (exception) { + case Dynarmic::A32::Exception::NoExecuteFault: + LOG_CRITICAL(Core_ARM, "Cannot execute instruction at unmapped address {:#08x}", pc); + ReturnException(pc, ARM_Interface::no_execute); return; - } + default: + if (debugger_enabled) { + ReturnException(pc, ARM_Interface::breakpoint); + return; + } - parent.LogBacktrace(); - LOG_CRITICAL(Core_ARM, - "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X}, thumb = {})", - exception, pc, MemoryReadCode(pc), parent.IsInThumbMode()); + parent.LogBacktrace(); + LOG_CRITICAL(Core_ARM, + "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X}, thumb = {})", + exception, pc, MemoryRead32(pc), parent.IsInThumbMode()); + } } void CallSVC(u32 swi) override { @@ -141,15 +154,20 @@ public: const auto match{parent.MatchingWatchpoint(addr, size, type)}; if (match) { - parent.SaveContext(parent.breakpoint_context); - parent.jit.load()->HaltExecution(ARM_Interface::watchpoint); parent.halted_watchpoint = match; + ReturnException(parent.jit.load()->Regs()[15], ARM_Interface::watchpoint); return false; } return true; } + void ReturnException(u32 pc, Dynarmic::HaltReason hr) { + parent.SaveContext(parent.breakpoint_context); + parent.breakpoint_context.cpu_registers[15] = pc; + parent.jit.load()->HaltExecution(hr); + } + ARM_Dynarmic_32& parent; Core::Memory::Memory& memory; std::size_t num_interpreted_instructions{}; @@ -409,18 +427,38 @@ void ARM_Dynarmic_32::PageTableChanged(Common::PageTable& page_table, } std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_32::GetBacktrace(Core::System& system, - u64 sp, u64 lr) { - // No way to get accurate stack traces in A32 yet - return {}; + u64 fp, u64 lr, u64 pc) { + std::vector<BacktraceEntry> out; + auto& memory = system.Memory(); + + out.push_back({"", 0, pc, 0, ""}); + + // fp (= r11) points to the last frame record. + // Frame records are two words long: + // fp+0 : pointer to previous frame record + // fp+4 : value of lr for frame + while (true) { + out.push_back({"", 0, lr, 0, ""}); + if (!fp || (fp % 4 != 0) || !memory.IsValidVirtualAddressRange(fp, 8)) { + break; + } + lr = memory.Read32(fp + 4); + fp = memory.Read32(fp); + } + + SymbolicateBacktrace(system, out); + + return out; } std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_32::GetBacktraceFromContext( System& system, const ThreadContext32& ctx) { - return GetBacktrace(system, ctx.cpu_registers[13], ctx.cpu_registers[14]); + const auto& reg = ctx.cpu_registers; + return GetBacktrace(system, reg[11], reg[14], reg[15]); } std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_32::GetBacktrace() const { - return GetBacktrace(system, GetReg(13), GetReg(14)); + return GetBacktrace(system, GetReg(11), GetReg(14), GetReg(15)); } } // 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: private: std::shared_ptr<Dynarmic::A32::Jit> MakeJit(Common::PageTable* page_table) const; - static std::vector<BacktraceEntry> GetBacktrace(Core::System& system, u64 sp, u64 lr); + static std::vector<BacktraceEntry> GetBacktrace(Core::System& system, u64 fp, u64 lr, u64 pc); using JitCacheKey = std::pair<Common::PageTable*, std::size_t>; using JitCacheType = diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp index 4370ca294..c437f24b8 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp @@ -52,6 +52,12 @@ public: CheckMemoryAccess(vaddr, 16, Kernel::DebugWatchpointType::Read); return {memory.Read64(vaddr), memory.Read64(vaddr + 8)}; } + std::optional<u32> MemoryReadCode(u64 vaddr) override { + if (!memory.IsValidVirtualAddressRange(vaddr, sizeof(u32))) { + return std::nullopt; + } + return MemoryRead32(vaddr); + } void MemoryWrite8(u64 vaddr, u8 value) override { if (CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write)) { @@ -105,7 +111,7 @@ public: parent.LogBacktrace(); LOG_ERROR(Core_ARM, "Unimplemented instruction @ 0x{:X} for {} instructions (instr = {:08X})", pc, - num_instructions, MemoryReadCode(pc)); + num_instructions, MemoryRead32(pc)); } void InstructionCacheOperationRaised(Dynarmic::A64::InstructionCacheOperation op, @@ -138,16 +144,19 @@ public: case Dynarmic::A64::Exception::SendEventLocal: case Dynarmic::A64::Exception::Yield: return; + case Dynarmic::A64::Exception::NoExecuteFault: + LOG_CRITICAL(Core_ARM, "Cannot execute instruction at unmapped address {:#016x}", pc); + ReturnException(pc, ARM_Interface::no_execute); + return; default: if (debugger_enabled) { - parent.SaveContext(parent.breakpoint_context); - parent.jit.load()->HaltExecution(ARM_Interface::breakpoint); + ReturnException(pc, ARM_Interface::breakpoint); return; } parent.LogBacktrace(); - ASSERT_MSG(false, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X})", - static_cast<std::size_t>(exception), pc, MemoryReadCode(pc)); + LOG_CRITICAL(Core_ARM, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X})", + static_cast<std::size_t>(exception), pc, MemoryRead32(pc)); } } @@ -188,15 +197,20 @@ public: const auto match{parent.MatchingWatchpoint(addr, size, type)}; if (match) { - parent.SaveContext(parent.breakpoint_context); - parent.jit.load()->HaltExecution(ARM_Interface::watchpoint); parent.halted_watchpoint = match; + ReturnException(parent.jit.load()->GetPC(), ARM_Interface::watchpoint); return false; } return true; } + void ReturnException(u64 pc, Dynarmic::HaltReason hr) { + parent.SaveContext(parent.breakpoint_context); + parent.breakpoint_context.pc = pc; + parent.jit.load()->HaltExecution(hr); + } + ARM_Dynarmic_64& parent; Core::Memory::Memory& memory; u64 tpidrro_el0 = 0; @@ -480,22 +494,22 @@ void ARM_Dynarmic_64::PageTableChanged(Common::PageTable& page_table, } std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_64::GetBacktrace(Core::System& system, - u64 fp, u64 lr) { + u64 fp, u64 lr, u64 pc) { std::vector<BacktraceEntry> out; auto& memory = system.Memory(); - // fp (= r29) points to the last frame record. - // Note that this is the frame record for the *previous* frame, not the current one. - // Note we need to subtract 4 from our last read to get the proper address + out.push_back({"", 0, pc, 0, ""}); + + // fp (= x29) points to the previous frame record. // Frame records are two words long: // fp+0 : pointer to previous frame record // fp+8 : value of lr for frame while (true) { out.push_back({"", 0, lr, 0, ""}); - if (!fp) { + if (!fp || (fp % 4 != 0) || !memory.IsValidVirtualAddressRange(fp, 16)) { break; } - lr = memory.Read64(fp + 8) - 4; + lr = memory.Read64(fp + 8); fp = memory.Read64(fp); } @@ -506,11 +520,12 @@ std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_64::GetBacktrace(Core::S std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_64::GetBacktraceFromContext( System& system, const ThreadContext64& ctx) { - return GetBacktrace(system, ctx.cpu_registers[29], ctx.cpu_registers[30]); + const auto& reg = ctx.cpu_registers; + return GetBacktrace(system, reg[29], reg[30], ctx.pc); } std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_64::GetBacktrace() const { - return GetBacktrace(system, GetReg(29), GetReg(30)); + return GetBacktrace(system, GetReg(29), GetReg(30), GetPC()); } } // 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: std::shared_ptr<Dynarmic::A64::Jit> MakeJit(Common::PageTable* page_table, std::size_t address_space_bits) const; - static std::vector<BacktraceEntry> GetBacktrace(Core::System& system, u64 fp, u64 lr); + static std::vector<BacktraceEntry> GetBacktrace(Core::System& system, u64 fp, u64 lr, u64 pc); using JitCacheKey = std::pair<Common::PageTable*, std::size_t>; using JitCacheType = diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp index 29e7dba9b..5425637f5 100644 --- a/src/core/core_timing.cpp +++ b/src/core/core_timing.cpp @@ -6,7 +6,9 @@ #include <string> #include <tuple> +#include "common/logging/log.h" #include "common/microprofile.h" +#include "common/thread.h" #include "core/core_timing.h" #include "core/core_timing_util.h" #include "core/hardware_properties.h" @@ -20,10 +22,11 @@ std::shared_ptr<EventType> CreateEvent(std::string name, TimedCallback&& callbac } struct CoreTiming::Event { - u64 time; + s64 time; u64 fifo_order; std::uintptr_t user_data; std::weak_ptr<EventType> type; + s64 reschedule_time; // Sort by time, unless the times are the same, in which case sort by // the order added to the queue @@ -41,11 +44,11 @@ CoreTiming::CoreTiming() CoreTiming::~CoreTiming() = default; -void CoreTiming::ThreadEntry(CoreTiming& instance) { - constexpr char name[] = "yuzu:HostTiming"; - MicroProfileOnThreadCreate(name); - Common::SetCurrentThreadName(name); - Common::SetCurrentThreadPriority(Common::ThreadPriority::VeryHigh); +void CoreTiming::ThreadEntry(CoreTiming& instance, size_t id) { + const std::string name = "yuzu:HostTiming_" + std::to_string(id); + MicroProfileOnThreadCreate(name.c_str()); + Common::SetCurrentThreadName(name.c_str()); + Common::SetCurrentThreadPriority(Common::ThreadPriority::Critical); instance.on_thread_init(); instance.ThreadLoop(); MicroProfileOnThreadExit(); @@ -56,71 +59,131 @@ void CoreTiming::Initialize(std::function<void()>&& on_thread_init_) { event_fifo_id = 0; shutting_down = false; ticks = 0; - const auto empty_timed_callback = [](std::uintptr_t, std::chrono::nanoseconds) {}; + const auto empty_timed_callback = [](std::uintptr_t, u64, std::chrono::nanoseconds) + -> std::optional<std::chrono::nanoseconds> { return std::nullopt; }; ev_lost = CreateEvent("_lost_event", empty_timed_callback); if (is_multicore) { - timer_thread = std::make_unique<std::thread>(ThreadEntry, std::ref(*this)); + worker_threads.emplace_back(ThreadEntry, std::ref(*this), 0); } } void CoreTiming::Shutdown() { - paused = true; + is_paused = true; shutting_down = true; - pause_event.Set(); - event.Set(); - if (timer_thread) { - timer_thread->join(); + std::atomic_thread_fence(std::memory_order_release); + + event_cv.notify_all(); + wait_pause_cv.notify_all(); + for (auto& thread : worker_threads) { + thread.join(); } + worker_threads.clear(); + pause_callbacks.clear(); ClearPendingEvents(); - timer_thread.reset(); has_started = false; } -void CoreTiming::Pause(bool is_paused) { - paused = is_paused; - pause_event.Set(); +void CoreTiming::Pause(bool is_paused_) { + std::unique_lock main_lock(event_mutex); + if (is_paused_ == paused_state.load(std::memory_order_relaxed)) { + return; + } + if (is_multicore) { + is_paused = is_paused_; + event_cv.notify_all(); + if (!is_paused_) { + wait_pause_cv.notify_all(); + } + } + paused_state.store(is_paused_, std::memory_order_relaxed); + + if (!is_paused_) { + pause_end_time = GetGlobalTimeNs().count(); + } + + for (auto& cb : pause_callbacks) { + cb(is_paused_); + } } -void CoreTiming::SyncPause(bool is_paused) { - if (is_paused == paused && paused_set == paused) { +void CoreTiming::SyncPause(bool is_paused_) { + std::unique_lock main_lock(event_mutex); + if (is_paused_ == paused_state.load(std::memory_order_relaxed)) { return; } - Pause(is_paused); - if (timer_thread) { - if (!is_paused) { - pause_event.Set(); + + if (is_multicore) { + is_paused = is_paused_; + event_cv.notify_all(); + if (!is_paused_) { + wait_pause_cv.notify_all(); + } + } + paused_state.store(is_paused_, std::memory_order_relaxed); + if (is_multicore) { + if (is_paused_) { + wait_signal_cv.wait(main_lock, [this] { return pause_count == worker_threads.size(); }); + } else { + wait_signal_cv.wait(main_lock, [this] { return pause_count == 0; }); } - event.Set(); - while (paused_set != is_paused) - ; + } + + if (!is_paused_) { + pause_end_time = GetGlobalTimeNs().count(); + } + + for (auto& cb : pause_callbacks) { + cb(is_paused_); } } bool CoreTiming::IsRunning() const { - return !paused_set; + return !paused_state.load(std::memory_order_acquire); } bool CoreTiming::HasPendingEvents() const { - return !(wait_set && event_queue.empty()); + std::unique_lock main_lock(event_mutex); + return !event_queue.empty() || pending_events.load(std::memory_order_relaxed) != 0; } void CoreTiming::ScheduleEvent(std::chrono::nanoseconds ns_into_future, const std::shared_ptr<EventType>& event_type, - std::uintptr_t user_data) { - { - std::scoped_lock scope{basic_lock}; - const u64 timeout = static_cast<u64>((GetGlobalTimeNs() + ns_into_future).count()); + std::uintptr_t user_data, bool absolute_time) { + + std::unique_lock main_lock(event_mutex); + const auto next_time{absolute_time ? ns_into_future : GetGlobalTimeNs() + ns_into_future}; - event_queue.emplace_back(Event{timeout, event_fifo_id++, user_data, event_type}); + event_queue.emplace_back(Event{next_time.count(), event_fifo_id++, user_data, event_type, 0}); + pending_events.fetch_add(1, std::memory_order_relaxed); - std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>()); + std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>()); + + if (is_multicore) { + event_cv.notify_one(); + } +} + +void CoreTiming::ScheduleLoopingEvent(std::chrono::nanoseconds start_time, + std::chrono::nanoseconds resched_time, + const std::shared_ptr<EventType>& event_type, + std::uintptr_t user_data, bool absolute_time) { + std::unique_lock main_lock(event_mutex); + const auto next_time{absolute_time ? start_time : GetGlobalTimeNs() + start_time}; + + event_queue.emplace_back( + Event{next_time.count(), event_fifo_id++, user_data, event_type, resched_time.count()}); + pending_events.fetch_add(1, std::memory_order_relaxed); + + std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>()); + + if (is_multicore) { + event_cv.notify_one(); } - event.Set(); } void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type, std::uintptr_t user_data) { - std::scoped_lock scope{basic_lock}; + std::unique_lock main_lock(event_mutex); const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) { return e.type.lock().get() == event_type.get() && e.user_data == user_data; }); @@ -129,6 +192,7 @@ void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type, if (itr != event_queue.end()) { event_queue.erase(itr, event_queue.end()); std::make_heap(event_queue.begin(), event_queue.end(), std::greater<>()); + pending_events.fetch_sub(1, std::memory_order_relaxed); } } @@ -168,11 +232,12 @@ u64 CoreTiming::GetClockTicks() const { } void CoreTiming::ClearPendingEvents() { + std::unique_lock main_lock(event_mutex); event_queue.clear(); } void CoreTiming::RemoveEvent(const std::shared_ptr<EventType>& event_type) { - std::scoped_lock lock{basic_lock}; + std::unique_lock main_lock(event_mutex); const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) { return e.type.lock().get() == event_type.get(); @@ -185,22 +250,48 @@ void CoreTiming::RemoveEvent(const std::shared_ptr<EventType>& event_type) { } } +void CoreTiming::RegisterPauseCallback(PauseCallback&& callback) { + std::unique_lock main_lock(event_mutex); + pause_callbacks.emplace_back(std::move(callback)); +} + std::optional<s64> CoreTiming::Advance() { - std::scoped_lock lock{advance_lock, basic_lock}; global_timer = GetGlobalTimeNs().count(); + std::unique_lock main_lock(event_mutex); while (!event_queue.empty() && event_queue.front().time <= global_timer) { Event evt = std::move(event_queue.front()); std::pop_heap(event_queue.begin(), event_queue.end(), std::greater<>()); event_queue.pop_back(); - basic_lock.unlock(); if (const auto event_type{evt.type.lock()}) { - event_type->callback( - evt.user_data, std::chrono::nanoseconds{static_cast<s64>(global_timer - evt.time)}); + event_mutex.unlock(); + + const auto new_schedule_time{event_type->callback( + evt.user_data, evt.time, + std::chrono::nanoseconds{GetGlobalTimeNs().count() - evt.time})}; + + event_mutex.lock(); + pending_events.fetch_sub(1, std::memory_order_relaxed); + + if (evt.reschedule_time != 0) { + // If this event was scheduled into a pause, its time now is going to be way behind. + // Re-set this event to continue from the end of the pause. + auto next_time{evt.time + evt.reschedule_time}; + if (evt.time < pause_end_time) { + next_time = pause_end_time + evt.reschedule_time; + } + + const auto next_schedule_time{new_schedule_time.has_value() + ? new_schedule_time.value().count() + : evt.reschedule_time}; + event_queue.emplace_back( + Event{next_time, event_fifo_id++, evt.user_data, evt.type, next_schedule_time}); + pending_events.fetch_add(1, std::memory_order_relaxed); + std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>()); + } } - basic_lock.lock(); global_timer = GetGlobalTimeNs().count(); } @@ -213,26 +304,34 @@ std::optional<s64> CoreTiming::Advance() { } void CoreTiming::ThreadLoop() { + const auto predicate = [this] { return !event_queue.empty() || is_paused; }; has_started = true; while (!shutting_down) { - while (!paused) { - paused_set = false; + while (!is_paused && !shutting_down) { const auto next_time = Advance(); if (next_time) { if (*next_time > 0) { std::chrono::nanoseconds next_time_ns = std::chrono::nanoseconds(*next_time); - event.WaitFor(next_time_ns); + std::unique_lock main_lock(event_mutex); + event_cv.wait_for(main_lock, next_time_ns, predicate); } } else { - wait_set = true; - event.Wait(); + std::unique_lock main_lock(event_mutex); + event_cv.wait(main_lock, predicate); } - wait_set = false; } - paused_set = true; - clock->Pause(true); - pause_event.Wait(); - clock->Pause(false); + std::unique_lock main_lock(event_mutex); + pause_count++; + if (pause_count == worker_threads.size()) { + clock->Pause(true); + wait_signal_cv.notify_all(); + } + wait_pause_cv.wait(main_lock, [this] { return !is_paused || shutting_down; }); + pause_count--; + if (pause_count == 0) { + clock->Pause(false); + wait_signal_cv.notify_all(); + } } } diff --git a/src/core/core_timing.h b/src/core/core_timing.h index d27773009..09b6ed81a 100644 --- a/src/core/core_timing.h +++ b/src/core/core_timing.h @@ -5,6 +5,7 @@ #include <atomic> #include <chrono> +#include <condition_variable> #include <functional> #include <memory> #include <mutex> @@ -14,14 +15,14 @@ #include <vector> #include "common/common_types.h" -#include "common/thread.h" #include "common/wall_clock.h" namespace Core::Timing { /// A callback that may be scheduled for a particular core timing event. -using TimedCallback = - std::function<void(std::uintptr_t user_data, std::chrono::nanoseconds ns_late)>; +using TimedCallback = std::function<std::optional<std::chrono::nanoseconds>( + std::uintptr_t user_data, s64 time, std::chrono::nanoseconds ns_late)>; +using PauseCallback = std::function<void(bool paused)>; /// Contains the characteristics of a particular event. struct EventType { @@ -93,7 +94,15 @@ public: /// Schedules an event in core timing void ScheduleEvent(std::chrono::nanoseconds ns_into_future, - const std::shared_ptr<EventType>& event_type, std::uintptr_t user_data = 0); + const std::shared_ptr<EventType>& event_type, std::uintptr_t user_data = 0, + bool absolute_time = false); + + /// Schedules an event which will automatically re-schedule itself with the given time, until + /// unscheduled + void ScheduleLoopingEvent(std::chrono::nanoseconds start_time, + std::chrono::nanoseconds resched_time, + const std::shared_ptr<EventType>& event_type, + std::uintptr_t user_data = 0, bool absolute_time = false); void UnscheduleEvent(const std::shared_ptr<EventType>& event_type, std::uintptr_t user_data); @@ -125,18 +134,21 @@ public: /// Checks for events manually and returns time in nanoseconds for next event, threadsafe. std::optional<s64> Advance(); + /// Register a callback function to be called when coretiming pauses. + void RegisterPauseCallback(PauseCallback&& callback); + private: struct Event; /// Clear all pending events. This should ONLY be done on exit. void ClearPendingEvents(); - static void ThreadEntry(CoreTiming& instance); + static void ThreadEntry(CoreTiming& instance, size_t id); void ThreadLoop(); std::unique_ptr<Common::WallClock> clock; - u64 global_timer = 0; + s64 global_timer = 0; // The queue is a min-heap using std::make_heap/push_heap/pop_heap. // We don't use std::priority_queue because we need to be able to serialize, unserialize and @@ -144,25 +156,31 @@ private: // accomodated by the standard adaptor class. std::vector<Event> event_queue; u64 event_fifo_id = 0; + std::atomic<size_t> pending_events{}; std::shared_ptr<EventType> ev_lost; - Common::Event event{}; - Common::Event pause_event{}; - std::mutex basic_lock; - std::mutex advance_lock; - std::unique_ptr<std::thread> timer_thread; - std::atomic<bool> paused{}; - std::atomic<bool> paused_set{}; - std::atomic<bool> wait_set{}; - std::atomic<bool> shutting_down{}; std::atomic<bool> has_started{}; std::function<void()> on_thread_init{}; + std::vector<std::thread> worker_threads; + + std::condition_variable event_cv; + std::condition_variable wait_pause_cv; + std::condition_variable wait_signal_cv; + mutable std::mutex event_mutex; + + std::atomic<bool> paused_state{}; + bool is_paused{}; + bool shutting_down{}; bool is_multicore{}; + size_t pause_count{}; + s64 pause_end_time{}; /// Cycle timing u64 ticks{}; s64 downcount{}; + + std::vector<PauseCallback> pause_callbacks{}; }; /// 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() { } } -std::function<void(void*)> CpuManager::GetGuestThreadStartFunc() { - return GuestThreadFunction; -} - -std::function<void(void*)> CpuManager::GetIdleThreadStartFunc() { - return IdleThreadFunction; -} - -std::function<void(void*)> CpuManager::GetShutdownThreadStartFunc() { - return ShutdownThreadFunction; -} - -void CpuManager::GuestThreadFunction(void* cpu_manager_) { - CpuManager* cpu_manager = static_cast<CpuManager*>(cpu_manager_); - if (cpu_manager->is_multicore) { - cpu_manager->MultiCoreRunGuestThread(); +void CpuManager::GuestThreadFunction() { + if (is_multicore) { + MultiCoreRunGuestThread(); } else { - cpu_manager->SingleCoreRunGuestThread(); + SingleCoreRunGuestThread(); } } -void CpuManager::GuestRewindFunction(void* cpu_manager_) { - CpuManager* cpu_manager = static_cast<CpuManager*>(cpu_manager_); - if (cpu_manager->is_multicore) { - cpu_manager->MultiCoreRunGuestLoop(); +void CpuManager::GuestRewindFunction() { + if (is_multicore) { + MultiCoreRunGuestLoop(); } else { - cpu_manager->SingleCoreRunGuestLoop(); + SingleCoreRunGuestLoop(); } } -void CpuManager::IdleThreadFunction(void* cpu_manager_) { - CpuManager* cpu_manager = static_cast<CpuManager*>(cpu_manager_); - if (cpu_manager->is_multicore) { - cpu_manager->MultiCoreRunIdleThread(); +void CpuManager::IdleThreadFunction() { + if (is_multicore) { + MultiCoreRunIdleThread(); } else { - cpu_manager->SingleCoreRunIdleThread(); + SingleCoreRunIdleThread(); } } -void CpuManager::ShutdownThreadFunction(void* cpu_manager) { - static_cast<CpuManager*>(cpu_manager)->ShutdownThread(); -} - -void* CpuManager::GetStartFuncParameter() { - return this; +void CpuManager::ShutdownThreadFunction() { + ShutdownThread(); } /////////////////////////////////////////////////////////////////////////////// @@ -97,7 +78,7 @@ void CpuManager::MultiCoreRunGuestThread() { kernel.CurrentScheduler()->OnThreadStart(); auto* thread = kernel.CurrentScheduler()->GetSchedulerCurrentThread(); auto& host_context = thread->GetHostContext(); - host_context->SetRewindPoint(GuestRewindFunction, this); + host_context->SetRewindPoint([this] { GuestRewindFunction(); }); MultiCoreRunGuestLoop(); } @@ -134,7 +115,7 @@ void CpuManager::SingleCoreRunGuestThread() { kernel.CurrentScheduler()->OnThreadStart(); auto* thread = kernel.CurrentScheduler()->GetSchedulerCurrentThread(); auto& host_context = thread->GetHostContext(); - host_context->SetRewindPoint(GuestRewindFunction, this); + host_context->SetRewindPoint([this] { GuestRewindFunction(); }); SingleCoreRunGuestLoop(); } @@ -194,7 +175,9 @@ void CpuManager::PreemptSingleCore(bool from_running_enviroment) { { auto& scheduler = system.Kernel().Scheduler(current_core); scheduler.Reload(scheduler.GetSchedulerCurrentThread()); - idle_count = 0; + if (!scheduler.IsIdle()) { + idle_count = 0; + } } } 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: void Initialize(); void Shutdown(); - static std::function<void(void*)> GetGuestThreadStartFunc(); - static std::function<void(void*)> GetIdleThreadStartFunc(); - static std::function<void(void*)> GetShutdownThreadStartFunc(); - void* GetStartFuncParameter(); + std::function<void()> GetGuestThreadStartFunc() { + return [this] { GuestThreadFunction(); }; + } + std::function<void()> GetIdleThreadStartFunc() { + return [this] { IdleThreadFunction(); }; + } + std::function<void()> GetShutdownThreadStartFunc() { + return [this] { ShutdownThreadFunction(); }; + } void PreemptSingleCore(bool from_running_enviroment = true); @@ -62,10 +67,10 @@ public: } private: - static void GuestThreadFunction(void* cpu_manager); - static void GuestRewindFunction(void* cpu_manager); - static void IdleThreadFunction(void* cpu_manager); - static void ShutdownThreadFunction(void* cpu_manager); + void GuestThreadFunction(); + void GuestRewindFunction(); + void IdleThreadFunction(); + void ShutdownThreadFunction(); void MultiCoreRunGuestThread(); 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 const auto& gprs{context.cpu_registers}; const auto& fprs{context.vector_registers}; - if (id <= SP_REGISTER) { + if (id < SP_REGISTER) { return ValueToHex(gprs[id]); + } else if (id == SP_REGISTER) { + return ValueToHex(context.sp); } else if (id == PC_REGISTER) { return ValueToHex(context.pc); } else if (id == PSTATE_REGISTER) { @@ -215,8 +217,10 @@ void GDBStubA64::RegWrite(Kernel::KThread* thread, size_t id, std::string_view v auto& context{thread->GetContext64()}; - if (id <= SP_REGISTER) { + if (id < SP_REGISTER) { context.cpu_registers[id] = HexToValue<u64>(value); + } else if (id == SP_REGISTER) { + context.sp = HexToValue<u64>(value); } else if (id == PC_REGISTER) { context.pc = HexToValue<u64>(value); } 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 { InterruptManager::InterruptManager(Core::System& system_in) : system(system_in) { gpu_interrupt_event = Core::Timing::CreateEvent( - "GPUInterrupt", [this](std::uintptr_t message, std::chrono::nanoseconds) { + "GPUInterrupt", + [this](std::uintptr_t message, u64 time, + std::chrono::nanoseconds) -> std::optional<std::chrono::nanoseconds> { auto nvdrv = system.ServiceManager().GetService<Service::Nvidia::NVDRV>("nvdrv"); const u32 syncpt = static_cast<u32>(message >> 32); const u32 value = static_cast<u32>(message); nvdrv->SignalGPUInterruptSyncpt(syncpt, value); + return std::nullopt; }); } 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 { Unknown = 0, LinearResonantActuator = 1, GcErm = 2, + N64 = 3, }; // 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: explicit KCodeMemory(KernelCore& kernel_); Result Initialize(Core::DeviceMemory& device_memory, VAddr address, size_t size); - void Finalize(); + void Finalize() override; Result Map(VAddr address, size_t size); Result Unmap(VAddr address, size_t size); Result MapToOwner(VAddr address, size_t size, Svc::MemoryPermission perm); Result UnmapFromOwner(VAddr address, size_t size); - bool IsInitialized() const { + bool IsInitialized() const override { return m_is_initialized; } static void PostDestroy([[maybe_unused]] uintptr_t arg) {} - KProcess* GetOwner() const { + KProcess* GetOwner() const override { return m_owner; } 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) { std::addressof(num_waiters), reinterpret_cast<uintptr_t>(std::addressof(exception_thread))); next != nullptr) { - next->SetState(ThreadState::Runnable); + next->EndWait(ResultSuccess); } 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) { } KScheduler::KScheduler(Core::System& system_, s32 core_id_) : system{system_}, core_id{core_id_} { - switch_fiber = std::make_shared<Common::Fiber>(OnSwitch, this); + switch_fiber = std::make_shared<Common::Fiber>([this] { SwitchToCurrent(); }); state.needs_scheduling.store(true); state.interrupt_task_thread_runnable = false; state.should_count_idle = false; @@ -778,11 +778,6 @@ void KScheduler::ScheduleImpl() { next_scheduler.SwitchContextStep2(); } -void KScheduler::OnSwitch(void* this_scheduler) { - KScheduler* sched = static_cast<KScheduler*>(this_scheduler); - sched->SwitchToCurrent(); -} - void KScheduler::SwitchToCurrent() { while (true) { { 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: return idle_thread; } + /// Returns true if the scheduler is idle + [[nodiscard]] bool IsIdle() const { + return GetSchedulerCurrentThread() == idle_thread; + } + /// Gets the timestamp for the last context switch in ticks. [[nodiscard]] u64 GetLastContextSwitchTicks() const; @@ -165,7 +170,6 @@ private: */ void UpdateLastContextSwitchTime(KThread* thread, KProcess* process); - static void OnSwitch(void* this_scheduler); void SwitchToCurrent(); 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 Result KThread::InitializeThread(KThread* thread, KThreadFunction func, uintptr_t arg, VAddr user_stack_top, s32 prio, s32 core, KProcess* owner, - ThreadType type, std::function<void(void*)>&& init_func, - void* init_func_parameter) { + ThreadType type, std::function<void()>&& init_func) { // Initialize the thread. R_TRY(thread->Initialize(func, arg, user_stack_top, prio, core, owner, type)); // Initialize emulation parameters. - thread->host_context = - std::make_shared<Common::Fiber>(std::move(init_func), init_func_parameter); + thread->host_context = std::make_shared<Common::Fiber>(std::move(init_func)); thread->is_single_core = !Settings::values.use_multi_core.GetValue(); return ResultSuccess; @@ -265,15 +263,13 @@ Result KThread::InitializeDummyThread(KThread* thread) { Result KThread::InitializeIdleThread(Core::System& system, KThread* thread, s32 virt_core) { return InitializeThread(thread, {}, {}, {}, IdleThreadPriority, virt_core, {}, ThreadType::Main, - Core::CpuManager::GetIdleThreadStartFunc(), - system.GetCpuManager().GetStartFuncParameter()); + system.GetCpuManager().GetIdleThreadStartFunc()); } Result KThread::InitializeHighPriorityThread(Core::System& system, KThread* thread, KThreadFunction func, uintptr_t arg, s32 virt_core) { return InitializeThread(thread, func, arg, {}, {}, virt_core, nullptr, ThreadType::HighPriority, - Core::CpuManager::GetShutdownThreadStartFunc(), - system.GetCpuManager().GetStartFuncParameter()); + system.GetCpuManager().GetShutdownThreadStartFunc()); } Result KThread::InitializeUserThread(Core::System& system, KThread* thread, KThreadFunction func, @@ -281,8 +277,7 @@ Result KThread::InitializeUserThread(Core::System& system, KThread* thread, KThr KProcess* owner) { system.Kernel().GlobalSchedulerContext().AddThread(thread); return InitializeThread(thread, func, arg, user_stack_top, prio, virt_core, owner, - ThreadType::User, Core::CpuManager::GetGuestThreadStartFunc(), - system.GetCpuManager().GetStartFuncParameter()); + ThreadType::User, system.GetCpuManager().GetGuestThreadStartFunc()); } void KThread::PostDestroy(uintptr_t arg) { @@ -313,14 +308,20 @@ void KThread::Finalize() { auto it = waiter_list.begin(); while (it != waiter_list.end()) { - // Clear the lock owner - it->SetLockOwner(nullptr); + // Get the thread. + KThread* const waiter = std::addressof(*it); + + // The thread shouldn't be a kernel waiter. + ASSERT(!IsKernelAddressKey(waiter->GetAddressKey())); + + // Clear the lock owner. + waiter->SetLockOwner(nullptr); // Erase the waiter from our list. it = waiter_list.erase(it); // Cancel the thread's wait. - it->CancelWait(ResultInvalidState, true); + waiter->CancelWait(ResultInvalidState, true); } } @@ -485,9 +486,7 @@ void KThread::Unpin() { // Resume any threads that began waiting on us while we were pinned. for (auto it = pinned_waiter_list.begin(); it != pinned_waiter_list.end(); ++it) { - if (it->GetState() == ThreadState::Waiting) { - it->SetState(ThreadState::Runnable); - } + it->EndWait(ResultSuccess); } } @@ -882,6 +881,7 @@ void KThread::AddWaiterImpl(KThread* thread) { // Keep track of how many kernel waiters we have. if (IsKernelAddressKey(thread->GetAddressKey())) { ASSERT((num_kernel_waiters++) >= 0); + KScheduler::SetSchedulerUpdateNeeded(kernel); } // Insert the waiter. @@ -895,6 +895,7 @@ void KThread::RemoveWaiterImpl(KThread* thread) { // Keep track of how many kernel waiters we have. if (IsKernelAddressKey(thread->GetAddressKey())) { ASSERT((num_kernel_waiters--) > 0); + KScheduler::SetSchedulerUpdateNeeded(kernel); } // Remove the waiter. @@ -970,6 +971,7 @@ KThread* KThread::RemoveWaiterByKey(s32* out_num_waiters, VAddr key) { // Keep track of how many kernel waiters we have. if (IsKernelAddressKey(thread->GetAddressKey())) { ASSERT((num_kernel_waiters--) > 0); + KScheduler::SetSchedulerUpdateNeeded(kernel); } it = waiter_list.erase(it); 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: [[nodiscard]] static Result InitializeThread(KThread* thread, KThreadFunction func, uintptr_t arg, VAddr user_stack_top, s32 prio, s32 core, KProcess* owner, ThreadType type, - std::function<void(void*)>&& init_func, - void* init_func_parameter); + std::function<void()>&& init_func); static void RestorePriority(KernelCore& kernel_ctx, KThread* thread); 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 { void InitializePreemption(KernelCore& kernel) { preemption_event = Core::Timing::CreateEvent( - "PreemptionCallback", [this, &kernel](std::uintptr_t, std::chrono::nanoseconds) { + "PreemptionCallback", + [this, &kernel](std::uintptr_t, s64 time, + std::chrono::nanoseconds) -> std::optional<std::chrono::nanoseconds> { { KScopedSchedulerLock lock(kernel); global_scheduler_context->PreemptThreads(); } - const auto time_interval = std::chrono::nanoseconds{std::chrono::milliseconds(10)}; - system.CoreTiming().ScheduleEvent(time_interval, preemption_event); + return std::nullopt; }); const auto time_interval = std::chrono::nanoseconds{std::chrono::milliseconds(10)}; - system.CoreTiming().ScheduleEvent(time_interval, preemption_event); + system.CoreTiming().ScheduleLoopingEvent(time_interval, time_interval, preemption_event); } 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 @@ namespace Kernel { TimeManager::TimeManager(Core::System& system_) : system{system_} { - time_manager_event_type = - Core::Timing::CreateEvent("Kernel::TimeManagerCallback", - [this](std::uintptr_t thread_handle, std::chrono::nanoseconds) { - KThread* thread = reinterpret_cast<KThread*>(thread_handle); - { - KScopedSchedulerLock sl(system.Kernel()); - thread->OnTimer(); - } - }); + time_manager_event_type = Core::Timing::CreateEvent( + "Kernel::TimeManagerCallback", + [this](std::uintptr_t thread_handle, s64 time, + std::chrono::nanoseconds) -> std::optional<std::chrono::nanoseconds> { + KThread* thread = reinterpret_cast<KThread*>(thread_handle); + { + KScopedSchedulerLock sl(system.Kernel()); + thread->OnTimer(); + } + return std::nullopt; + }); } void TimeManager::ScheduleTimeEvent(KThread* thread, s64 nanoseconds) { diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index c08b0a5dc..3c28dee76 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp @@ -49,28 +49,41 @@ bool Controller_NPad::IsNpadIdValid(Core::HID::NpadIdType npad_id) { } } -bool Controller_NPad::IsDeviceHandleValid(const Core::HID::VibrationDeviceHandle& device_handle) { +Result Controller_NPad::IsDeviceHandleValid(const Core::HID::VibrationDeviceHandle& device_handle) { const auto npad_id = IsNpadIdValid(static_cast<Core::HID::NpadIdType>(device_handle.npad_id)); const bool npad_type = device_handle.npad_type < Core::HID::NpadStyleIndex::MaxNpadType; const bool device_index = device_handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex; - return npad_id && npad_type && device_index; + + if (!npad_type) { + return VibrationInvalidStyleIndex; + } + if (!npad_id) { + return VibrationInvalidNpadId; + } + if (!device_index) { + return VibrationDeviceIndexOutOfRange; + } + + return ResultSuccess; } Result Controller_NPad::VerifyValidSixAxisSensorHandle( const Core::HID::SixAxisSensorHandle& device_handle) { const auto npad_id = IsNpadIdValid(static_cast<Core::HID::NpadIdType>(device_handle.npad_id)); + const bool device_index = device_handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex; + const bool npad_type = device_handle.npad_type < Core::HID::NpadStyleIndex::MaxNpadType; + if (!npad_id) { return InvalidNpadId; } - const bool device_index = device_handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex; if (!device_index) { return NpadDeviceIndexOutOfRange; } // This doesn't get validated on nnsdk - const bool npad_type = device_handle.npad_type < Core::HID::NpadStyleIndex::MaxNpadType; if (!npad_type) { return NpadInvalidHandle; } + return ResultSuccess; } @@ -705,6 +718,11 @@ Controller_NPad::NpadJoyHoldType Controller_NPad::GetHoldType() const { } void Controller_NPad::SetNpadHandheldActivationMode(NpadHandheldActivationMode activation_mode) { + if (activation_mode >= NpadHandheldActivationMode::MaxActivationMode) { + ASSERT_MSG(false, "Activation mode should be always None, Single or Dual"); + return; + } + handheld_activation_mode = activation_mode; } @@ -820,11 +838,11 @@ bool Controller_NPad::VibrateControllerAtIndex(Core::HID::NpadIdType npad_id, const auto now = steady_clock::now(); - // Filter out non-zero vibrations that are within 10ms of each other. + // Filter out non-zero vibrations that are within 15ms of each other. if ((vibration_value.low_amplitude != 0.0f || vibration_value.high_amplitude != 0.0f) && duration_cast<milliseconds>( now - controller.vibration[device_index].last_vibration_timepoint) < - milliseconds(10)) { + milliseconds(15)) { return false; } @@ -840,7 +858,7 @@ bool Controller_NPad::VibrateControllerAtIndex(Core::HID::NpadIdType npad_id, void Controller_NPad::VibrateController( const Core::HID::VibrationDeviceHandle& vibration_device_handle, const Core::HID::VibrationValue& vibration_value) { - if (!IsDeviceHandleValid(vibration_device_handle)) { + if (IsDeviceHandleValid(vibration_device_handle).IsError()) { return; } @@ -903,7 +921,7 @@ void Controller_NPad::VibrateControllers( Core::HID::VibrationValue Controller_NPad::GetLastVibration( const Core::HID::VibrationDeviceHandle& vibration_device_handle) const { - if (!IsDeviceHandleValid(vibration_device_handle)) { + if (IsDeviceHandleValid(vibration_device_handle).IsError()) { return {}; } @@ -914,7 +932,7 @@ Core::HID::VibrationValue Controller_NPad::GetLastVibration( void Controller_NPad::InitializeVibrationDevice( const Core::HID::VibrationDeviceHandle& vibration_device_handle) { - if (!IsDeviceHandleValid(vibration_device_handle)) { + if (IsDeviceHandleValid(vibration_device_handle).IsError()) { return; } @@ -941,7 +959,7 @@ void Controller_NPad::SetPermitVibrationSession(bool permit_vibration_session) { bool Controller_NPad::IsVibrationDeviceMounted( const Core::HID::VibrationDeviceHandle& vibration_device_handle) const { - if (!IsDeviceHandleValid(vibration_device_handle)) { + if (IsDeviceHandleValid(vibration_device_handle).IsError()) { return false; } 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: Dual = 0, Single = 1, None = 2, + MaxActivationMode = 3, }; // This is nn::hid::NpadCommunicationMode @@ -196,7 +197,7 @@ public: Core::HID::NpadButton GetAndResetPressState(); static bool IsNpadIdValid(Core::HID::NpadIdType npad_id); - static bool IsDeviceHandleValid(const Core::HID::VibrationDeviceHandle& device_handle); + static Result IsDeviceHandleValid(const Core::HID::VibrationDeviceHandle& device_handle); static Result VerifyValidSixAxisSensorHandle( const Core::HID::SixAxisSensorHandle& device_handle); 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 { constexpr Result NpadInvalidHandle{ErrorModule::HID, 100}; constexpr Result NpadDeviceIndexOutOfRange{ErrorModule::HID, 107}; +constexpr Result VibrationInvalidStyleIndex{ErrorModule::HID, 122}; +constexpr Result VibrationInvalidNpadId{ErrorModule::HID, 123}; +constexpr Result VibrationDeviceIndexOutOfRange{ErrorModule::HID, 124}; constexpr Result InvalidSixAxisFusionRange{ErrorModule::HID, 423}; constexpr Result NpadIsDualJoycon{ErrorModule::HID, 601}; constexpr Result NpadIsSameType{ErrorModule::HID, 602}; diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index dc5d0366d..89bb12442 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -74,26 +74,34 @@ IAppletResource::IAppletResource(Core::System& system_, // Register update callbacks pad_update_event = Core::Timing::CreateEvent( "HID::UpdatePadCallback", - [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { + [this](std::uintptr_t user_data, s64 time, + std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { const auto guard = LockService(); UpdateControllers(user_data, ns_late); + return std::nullopt; }); mouse_keyboard_update_event = Core::Timing::CreateEvent( "HID::UpdateMouseKeyboardCallback", - [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { + [this](std::uintptr_t user_data, s64 time, + std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { const auto guard = LockService(); UpdateMouseKeyboard(user_data, ns_late); + return std::nullopt; }); motion_update_event = Core::Timing::CreateEvent( "HID::UpdateMotionCallback", - [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { + [this](std::uintptr_t user_data, s64 time, + std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { const auto guard = LockService(); UpdateMotion(user_data, ns_late); + return std::nullopt; }); - system.CoreTiming().ScheduleEvent(pad_update_ns, pad_update_event); - system.CoreTiming().ScheduleEvent(mouse_keyboard_update_ns, mouse_keyboard_update_event); - system.CoreTiming().ScheduleEvent(motion_update_ns, motion_update_event); + system.CoreTiming().ScheduleLoopingEvent(pad_update_ns, pad_update_ns, pad_update_event); + system.CoreTiming().ScheduleLoopingEvent(mouse_keyboard_update_ns, mouse_keyboard_update_ns, + mouse_keyboard_update_event); + system.CoreTiming().ScheduleLoopingEvent(motion_update_ns, motion_update_ns, + motion_update_event); system.HIDCore().ReloadInputDevices(); } @@ -135,13 +143,6 @@ void IAppletResource::UpdateControllers(std::uintptr_t user_data, } controller->OnUpdate(core_timing); } - - // If ns_late is higher than the update rate ignore the delay - if (ns_late > pad_update_ns) { - ns_late = {}; - } - - core_timing.ScheduleEvent(pad_update_ns - ns_late, pad_update_event); } void IAppletResource::UpdateMouseKeyboard(std::uintptr_t user_data, @@ -150,26 +151,12 @@ void IAppletResource::UpdateMouseKeyboard(std::uintptr_t user_data, controllers[static_cast<size_t>(HidController::Mouse)]->OnUpdate(core_timing); controllers[static_cast<size_t>(HidController::Keyboard)]->OnUpdate(core_timing); - - // If ns_late is higher than the update rate ignore the delay - if (ns_late > mouse_keyboard_update_ns) { - ns_late = {}; - } - - core_timing.ScheduleEvent(mouse_keyboard_update_ns - ns_late, mouse_keyboard_update_event); } void IAppletResource::UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { auto& core_timing = system.CoreTiming(); controllers[static_cast<size_t>(HidController::NPad)]->OnMotionUpdate(core_timing); - - // If ns_late is higher than the update rate ignore the delay - if (ns_late > motion_update_ns) { - ns_late = {}; - } - - core_timing.ScheduleEvent(motion_update_ns - ns_late, motion_update_event); } class IActiveVibrationDeviceList final : public ServiceFramework<IActiveVibrationDeviceList> { @@ -778,7 +765,7 @@ void Hid::IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx) { bool is_at_rest{}; auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); - const auto result = controller.IsSixAxisSensorAtRest(parameters.sixaxis_handle, is_at_rest); + controller.IsSixAxisSensorAtRest(parameters.sixaxis_handle, is_at_rest); LOG_DEBUG(Service_HID, "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", @@ -786,7 +773,7 @@ void Hid::IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx) { parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(result); + rb.Push(ResultSuccess); rb.Push(is_at_rest); } @@ -803,8 +790,8 @@ void Hid::IsFirmwareUpdateAvailableForSixAxisSensor(Kernel::HLERequestContext& c bool is_firmware_available{}; auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); - const auto result = controller.IsFirmwareUpdateAvailableForSixAxisSensor( - parameters.sixaxis_handle, is_firmware_available); + controller.IsFirmwareUpdateAvailableForSixAxisSensor(parameters.sixaxis_handle, + is_firmware_available); LOG_WARNING( Service_HID, @@ -813,7 +800,7 @@ void Hid::IsFirmwareUpdateAvailableForSixAxisSensor(Kernel::HLERequestContext& c parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(result); + rb.Push(ResultSuccess); rb.Push(is_firmware_available); } @@ -1083,13 +1070,13 @@ void Hid::DisconnectNpad(Kernel::HLERequestContext& ctx) { const auto parameters{rp.PopRaw<Parameters>()}; auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); - const auto result = controller.DisconnectNpad(parameters.npad_id); + controller.DisconnectNpad(parameters.npad_id); LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id, parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); + rb.Push(ResultSuccess); } void Hid::GetPlayerLedPattern(Kernel::HLERequestContext& ctx) { @@ -1165,15 +1152,14 @@ void Hid::SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx const auto parameters{rp.PopRaw<Parameters>()}; auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); - const auto result = - controller.SetNpadMode(parameters.npad_id, Controller_NPad::NpadJoyDeviceType::Left, - Controller_NPad::NpadJoyAssignmentMode::Single); + controller.SetNpadMode(parameters.npad_id, Controller_NPad::NpadJoyDeviceType::Left, + Controller_NPad::NpadJoyAssignmentMode::Single); LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id, parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); + rb.Push(ResultSuccess); } void Hid::SetNpadJoyAssignmentModeSingle(Kernel::HLERequestContext& ctx) { @@ -1189,15 +1175,15 @@ void Hid::SetNpadJoyAssignmentModeSingle(Kernel::HLERequestContext& ctx) { const auto parameters{rp.PopRaw<Parameters>()}; auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); - const auto result = controller.SetNpadMode(parameters.npad_id, parameters.npad_joy_device_type, - Controller_NPad::NpadJoyAssignmentMode::Single); + controller.SetNpadMode(parameters.npad_id, parameters.npad_joy_device_type, + Controller_NPad::NpadJoyAssignmentMode::Single); LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}", parameters.npad_id, parameters.applet_resource_user_id, parameters.npad_joy_device_type); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); + rb.Push(ResultSuccess); } void Hid::SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) { @@ -1212,14 +1198,13 @@ void Hid::SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) { const auto parameters{rp.PopRaw<Parameters>()}; auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); - const auto result = controller.SetNpadMode(parameters.npad_id, {}, - Controller_NPad::NpadJoyAssignmentMode::Dual); + controller.SetNpadMode(parameters.npad_id, {}, Controller_NPad::NpadJoyAssignmentMode::Dual); LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id, parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); + rb.Push(ResultSuccess); } void Hid::MergeSingleJoyAsDualJoy(Kernel::HLERequestContext& ctx) { @@ -1412,8 +1397,11 @@ void Hid::ClearNpadCaptureButtonAssignment(Kernel::HLERequestContext& ctx) { void Hid::GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto vibration_device_handle{rp.PopRaw<Core::HID::VibrationDeviceHandle>()}; + const auto& controller = + GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); Core::HID::VibrationDeviceInfo vibration_device_info; + bool check_device_index = false; switch (vibration_device_handle.npad_type) { case Core::HID::NpadStyleIndex::ProController: @@ -1421,34 +1409,46 @@ void Hid::GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) { case Core::HID::NpadStyleIndex::JoyconDual: case Core::HID::NpadStyleIndex::JoyconLeft: case Core::HID::NpadStyleIndex::JoyconRight: - default: vibration_device_info.type = Core::HID::VibrationDeviceType::LinearResonantActuator; + check_device_index = true; break; case Core::HID::NpadStyleIndex::GameCube: vibration_device_info.type = Core::HID::VibrationDeviceType::GcErm; break; - case Core::HID::NpadStyleIndex::Pokeball: + case Core::HID::NpadStyleIndex::N64: + vibration_device_info.type = Core::HID::VibrationDeviceType::N64; + break; + default: vibration_device_info.type = Core::HID::VibrationDeviceType::Unknown; break; } - switch (vibration_device_handle.device_index) { - case Core::HID::DeviceIndex::Left: - vibration_device_info.position = Core::HID::VibrationDevicePosition::Left; - break; - case Core::HID::DeviceIndex::Right: - vibration_device_info.position = Core::HID::VibrationDevicePosition::Right; - break; - case Core::HID::DeviceIndex::None: - default: - ASSERT_MSG(false, "DeviceIndex should never be None!"); - vibration_device_info.position = Core::HID::VibrationDevicePosition::None; - break; + vibration_device_info.position = Core::HID::VibrationDevicePosition::None; + if (check_device_index) { + switch (vibration_device_handle.device_index) { + case Core::HID::DeviceIndex::Left: + vibration_device_info.position = Core::HID::VibrationDevicePosition::Left; + break; + case Core::HID::DeviceIndex::Right: + vibration_device_info.position = Core::HID::VibrationDevicePosition::Right; + break; + case Core::HID::DeviceIndex::None: + default: + ASSERT_MSG(false, "DeviceIndex should never be None!"); + break; + } } LOG_DEBUG(Service_HID, "called, vibration_device_type={}, vibration_device_position={}", vibration_device_info.type, vibration_device_info.position); + const auto result = controller.IsDeviceHandleValid(vibration_device_handle); + if (result.IsError()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); + return; + } + IPC::ResponseBuilder rb{ctx, 4}; rb.Push(ResultSuccess); 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_) // Register update callbacks hidbus_update_event = Core::Timing::CreateEvent( "Hidbus::UpdateCallback", - [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { + [this](std::uintptr_t user_data, s64 time, + std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { const auto guard = LockService(); UpdateHidbus(user_data, ns_late); + return std::nullopt; }); - system_.CoreTiming().ScheduleEvent(hidbus_update_ns, hidbus_update_event); + system_.CoreTiming().ScheduleLoopingEvent(hidbus_update_ns, hidbus_update_ns, + hidbus_update_event); } HidBus::~HidBus() { @@ -63,8 +66,6 @@ HidBus::~HidBus() { } void HidBus::UpdateHidbus(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { - auto& core_timing = system.CoreTiming(); - if (is_hidbus_enabled) { for (std::size_t i = 0; i < devices.size(); ++i) { if (!devices[i].is_device_initializated) { @@ -82,13 +83,6 @@ void HidBus::UpdateHidbus(std::uintptr_t user_data, std::chrono::nanoseconds ns_ sizeof(HidbusStatusManagerEntry)); } } - - // If ns_late is higher than the update rate ignore the delay - if (ns_late > hidbus_update_ns) { - ns_late = {}; - } - - core_timing.ScheduleEvent(hidbus_update_ns - ns_late, hidbus_update_event); } std::optional<std::size_t> HidBus::GetDeviceIndexFromHandle(BusHandle handle) const { diff --git a/src/core/hle/service/nvflinger/binder.h b/src/core/hle/service/nvflinger/binder.h index 21aaa40cd..157333ff8 100644 --- a/src/core/hle/service/nvflinger/binder.h +++ b/src/core/hle/service/nvflinger/binder.h @@ -34,6 +34,7 @@ enum class TransactionId { class IBinder { public: + virtual ~IBinder() = default; virtual void Transact(Kernel::HLERequestContext& ctx, android::TransactionId code, u32 flags) = 0; 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 // Schedule the screen composition events composition_event = Core::Timing::CreateEvent( - "ScreenComposition", [this](std::uintptr_t, std::chrono::nanoseconds ns_late) { + "ScreenComposition", + [this](std::uintptr_t, s64 time, + std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { const auto lock_guard = Lock(); Compose(); - const auto ticks = std::chrono::nanoseconds{GetNextTicks()}; - const auto ticks_delta = ticks - ns_late; - const auto future_ns = std::max(std::chrono::nanoseconds::zero(), ticks_delta); - - this->system.CoreTiming().ScheduleEvent(future_ns, composition_event); + return std::max(std::chrono::nanoseconds::zero(), + std::chrono::nanoseconds(GetNextTicks()) - ns_late); }); if (system.IsMulticore()) { vsync_thread = std::jthread([this](std::stop_token token) { SplitVSync(token); }); } else { - system.CoreTiming().ScheduleEvent(frame_ns, composition_event); + system.CoreTiming().ScheduleLoopingEvent(frame_ns, frame_ns, composition_event); } } 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() { void CheatEngine::Initialize() { event = Core::Timing::CreateEvent( "CheatEngine::FrameCallback::" + Common::HexToString(metadata.main_nso_build_id), - [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { + [this](std::uintptr_t user_data, s64 time, + std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { FrameCallback(user_data, ns_late); + return std::nullopt; }); - core_timing.ScheduleEvent(CHEAT_ENGINE_NS, event); + core_timing.ScheduleLoopingEvent(CHEAT_ENGINE_NS, CHEAT_ENGINE_NS, event); metadata.process_id = system.CurrentProcess()->GetProcessID(); metadata.title_id = system.GetCurrentProcessProgramID(); @@ -237,8 +239,6 @@ void CheatEngine::FrameCallback(std::uintptr_t, std::chrono::nanoseconds ns_late MICROPROFILE_SCOPE(Cheat_Engine); vm.Execute(metadata); - - core_timing.ScheduleEvent(CHEAT_ENGINE_NS - ns_late, event); } } // 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 : core_timing{core_timing_}, memory{memory_} { event = Core::Timing::CreateEvent( "MemoryFreezer::FrameCallback", - [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { + [this](std::uintptr_t user_data, s64 time, + std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { FrameCallback(user_data, ns_late); + return std::nullopt; }); core_timing.ScheduleEvent(memory_freezer_ns, event); } 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 using namespace std::chrono_literals; while (initialized) { SDL_PumpEvents(); - SendVibrations(); std::this_thread::sleep_for(1ms); } }); + vibration_thread = std::thread([this] { + Common::SetCurrentThreadName("yuzu:input:SDL_Vibration"); + using namespace std::chrono_literals; + while (initialized) { + SendVibrations(); + std::this_thread::sleep_for(10ms); + } + }); } // Because the events for joystick connection happens before we have our event watcher added, we // can just open all the joysticks right here @@ -457,6 +464,7 @@ SDLDriver::~SDLDriver() { initialized = false; if (start_thread) { poll_thread.join(); + vibration_thread.join(); SDL_QuitSubSystem(SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER); } } 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: std::atomic<bool> initialized = false; std::thread poll_thread; + std::thread vibration_thread; }; } // 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 { public: TestControl1() = default; - void DoWork(); + void DoWork() { + const u32 id = thread_ids.Get(); + u32 value = items[id]; + for (u32 i = 0; i < id; i++) { + value++; + } + results[id] = value; + Fiber::YieldTo(work_fibers[id], *thread_fibers[id]); + } void ExecuteThread(u32 id); @@ -54,35 +62,16 @@ public: std::vector<u32> results; }; -static void WorkControl1(void* control) { - auto* test_control = static_cast<TestControl1*>(control); - test_control->DoWork(); -} - -void TestControl1::DoWork() { - const u32 id = thread_ids.Get(); - u32 value = items[id]; - for (u32 i = 0; i < id; i++) { - value++; - } - results[id] = value; - Fiber::YieldTo(work_fibers[id], *thread_fibers[id]); -} - void TestControl1::ExecuteThread(u32 id) { thread_ids.Register(id); auto thread_fiber = Fiber::ThreadToFiber(); thread_fibers[id] = thread_fiber; - work_fibers[id] = std::make_shared<Fiber>(std::function<void(void*)>{WorkControl1}, this); + work_fibers[id] = std::make_shared<Fiber>([this] { DoWork(); }); items[id] = rand() % 256; Fiber::YieldTo(thread_fibers[id], *work_fibers[id]); thread_fibers[id]->Exit(); } -static void ThreadStart1(u32 id, TestControl1& test_control) { - test_control.ExecuteThread(id); -} - /** This test checks for fiber setup configuration and validates that fibers are * doing all the work required. */ @@ -95,7 +84,7 @@ TEST_CASE("Fibers::Setup", "[common]") { test_control.results.resize(num_threads, 0); std::vector<std::thread> threads; for (u32 i = 0; i < num_threads; i++) { - threads.emplace_back(ThreadStart1, i, std::ref(test_control)); + threads.emplace_back([&test_control, i] { test_control.ExecuteThread(i); }); } for (u32 i = 0; i < num_threads; i++) { threads[i].join(); @@ -167,21 +156,6 @@ public: std::shared_ptr<Common::Fiber> fiber3; }; -static void WorkControl2_1(void* control) { - auto* test_control = static_cast<TestControl2*>(control); - test_control->DoWork1(); -} - -static void WorkControl2_2(void* control) { - auto* test_control = static_cast<TestControl2*>(control); - test_control->DoWork2(); -} - -static void WorkControl2_3(void* control) { - auto* test_control = static_cast<TestControl2*>(control); - test_control->DoWork3(); -} - void TestControl2::ExecuteThread(u32 id) { thread_ids.Register(id); auto thread_fiber = Fiber::ThreadToFiber(); @@ -193,18 +167,6 @@ void TestControl2::Exit() { thread_fibers[id]->Exit(); } -static void ThreadStart2_1(u32 id, TestControl2& test_control) { - test_control.ExecuteThread(id); - test_control.CallFiber1(); - test_control.Exit(); -} - -static void ThreadStart2_2(u32 id, TestControl2& test_control) { - test_control.ExecuteThread(id); - test_control.CallFiber2(); - test_control.Exit(); -} - /** This test checks for fiber thread exchange configuration and validates that fibers are * that a fiber has been successfully transferred from one thread to another and that the TLS * region of the thread is kept while changing fibers. @@ -212,14 +174,19 @@ static void ThreadStart2_2(u32 id, TestControl2& test_control) { TEST_CASE("Fibers::InterExchange", "[common]") { TestControl2 test_control{}; test_control.thread_fibers.resize(2); - test_control.fiber1 = - std::make_shared<Fiber>(std::function<void(void*)>{WorkControl2_1}, &test_control); - test_control.fiber2 = - std::make_shared<Fiber>(std::function<void(void*)>{WorkControl2_2}, &test_control); - test_control.fiber3 = - std::make_shared<Fiber>(std::function<void(void*)>{WorkControl2_3}, &test_control); - std::thread thread1(ThreadStart2_1, 0, std::ref(test_control)); - std::thread thread2(ThreadStart2_2, 1, std::ref(test_control)); + test_control.fiber1 = std::make_shared<Fiber>([&test_control] { test_control.DoWork1(); }); + test_control.fiber2 = std::make_shared<Fiber>([&test_control] { test_control.DoWork2(); }); + test_control.fiber3 = std::make_shared<Fiber>([&test_control] { test_control.DoWork3(); }); + std::thread thread1{[&test_control] { + test_control.ExecuteThread(0); + test_control.CallFiber1(); + test_control.Exit(); + }}; + std::thread thread2{[&test_control] { + test_control.ExecuteThread(1); + test_control.CallFiber2(); + test_control.Exit(); + }}; thread1.join(); thread2.join(); REQUIRE(test_control.assert1); @@ -270,16 +237,6 @@ public: std::shared_ptr<Common::Fiber> fiber2; }; -static void WorkControl3_1(void* control) { - auto* test_control = static_cast<TestControl3*>(control); - test_control->DoWork1(); -} - -static void WorkControl3_2(void* control) { - auto* test_control = static_cast<TestControl3*>(control); - test_control->DoWork2(); -} - void TestControl3::ExecuteThread(u32 id) { thread_ids.Register(id); auto thread_fiber = Fiber::ThreadToFiber(); @@ -291,12 +248,6 @@ void TestControl3::Exit() { thread_fibers[id]->Exit(); } -static void ThreadStart3(u32 id, TestControl3& test_control) { - test_control.ExecuteThread(id); - test_control.CallFiber1(); - test_control.Exit(); -} - /** This test checks for one two threads racing for starting the same fiber. * It checks execution occurred in an ordered manner and by no time there were * two contexts at the same time. @@ -304,12 +255,15 @@ static void ThreadStart3(u32 id, TestControl3& test_control) { TEST_CASE("Fibers::StartRace", "[common]") { TestControl3 test_control{}; test_control.thread_fibers.resize(2); - test_control.fiber1 = - std::make_shared<Fiber>(std::function<void(void*)>{WorkControl3_1}, &test_control); - test_control.fiber2 = - std::make_shared<Fiber>(std::function<void(void*)>{WorkControl3_2}, &test_control); - std::thread thread1(ThreadStart3, 0, std::ref(test_control)); - std::thread thread2(ThreadStart3, 1, std::ref(test_control)); + test_control.fiber1 = std::make_shared<Fiber>([&test_control] { test_control.DoWork1(); }); + test_control.fiber2 = std::make_shared<Fiber>([&test_control] { test_control.DoWork2(); }); + const auto race_function{[&test_control](u32 id) { + test_control.ExecuteThread(id); + test_control.CallFiber1(); + test_control.Exit(); + }}; + std::thread thread1([&] { race_function(0); }); + std::thread thread2([&] { race_function(1); }); thread1.join(); thread2.join(); REQUIRE(test_control.value1 == 1); @@ -319,12 +273,10 @@ TEST_CASE("Fibers::StartRace", "[common]") { class TestControl4; -static void WorkControl4(void* control); - class TestControl4 { public: TestControl4() { - fiber1 = std::make_shared<Fiber>(std::function<void(void*)>{WorkControl4}, this); + fiber1 = std::make_shared<Fiber>([this] { DoWork(); }); goal_reached = false; rewinded = false; } @@ -336,7 +288,7 @@ public: } void DoWork() { - fiber1->SetRewindPoint(std::function<void(void*)>{WorkControl4}, this); + fiber1->SetRewindPoint([this] { DoWork(); }); if (rewinded) { goal_reached = true; Fiber::YieldTo(fiber1, *thread_fiber); @@ -351,11 +303,6 @@ public: bool rewinded; }; -static void WorkControl4(void* control) { - auto* test_control = static_cast<TestControl4*>(control); - test_control->DoWork(); -} - TEST_CASE("Fibers::Rewind", "[common]") { TestControl4 test_control{}; test_control.Execute(); diff --git a/src/tests/core/core_timing.cpp b/src/tests/core/core_timing.cpp index 8358d36b5..894975e6f 100644 --- a/src/tests/core/core_timing.cpp +++ b/src/tests/core/core_timing.cpp @@ -8,6 +8,8 @@ #include <chrono> #include <cstdlib> #include <memory> +#include <mutex> +#include <optional> #include <string> #include "core/core.h" @@ -21,15 +23,18 @@ std::array<s64, 5> delays{}; std::bitset<CB_IDS.size()> callbacks_ran_flags; u64 expected_callback = 0; +std::mutex control_mutex; template <unsigned int IDX> -void HostCallbackTemplate(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { +std::optional<std::chrono::nanoseconds> HostCallbackTemplate(std::uintptr_t user_data, s64 time, + std::chrono::nanoseconds ns_late) { + std::unique_lock<std::mutex> lk(control_mutex); static_assert(IDX < CB_IDS.size(), "IDX out of range"); callbacks_ran_flags.set(IDX); REQUIRE(CB_IDS[IDX] == user_data); - REQUIRE(CB_IDS[IDX] == CB_IDS[calls_order[expected_callback]]); delays[IDX] = ns_late.count(); ++expected_callback; + return std::nullopt; } struct ScopeInit final { diff --git a/src/video_core/compatible_formats.cpp b/src/video_core/compatible_formats.cpp index 014d880e2..4e75f33ca 100644 --- a/src/video_core/compatible_formats.cpp +++ b/src/video_core/compatible_formats.cpp @@ -131,9 +131,12 @@ constexpr std::array VIEW_CLASS_ASTC_8x8_RGBA{ // PixelFormat::ASTC_2D_10X5_SRGB // Missing formats: -// PixelFormat::ASTC_2D_10X6_UNORM // PixelFormat::ASTC_2D_10X6_SRGB +constexpr std::array VIEW_CLASS_ASTC_10x6_RGBA{ + PixelFormat::ASTC_2D_10X6_UNORM, +}; + constexpr std::array VIEW_CLASS_ASTC_10x8_RGBA{ PixelFormat::ASTC_2D_10X8_UNORM, PixelFormat::ASTC_2D_10X8_SRGB, @@ -226,6 +229,7 @@ constexpr Table MakeViewTable() { EnableRange(view, VIEW_CLASS_ASTC_6x6_RGBA); EnableRange(view, VIEW_CLASS_ASTC_8x5_RGBA); EnableRange(view, VIEW_CLASS_ASTC_8x8_RGBA); + EnableRange(view, VIEW_CLASS_ASTC_10x6_RGBA); EnableRange(view, VIEW_CLASS_ASTC_10x8_RGBA); EnableRange(view, VIEW_CLASS_ASTC_10x10_RGBA); 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 {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR}, // ASTC_2D_10X8_SRGB {GL_COMPRESSED_RGBA_ASTC_6x6_KHR}, // ASTC_2D_6X6_UNORM {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR}, // ASTC_2D_6X6_SRGB + {GL_COMPRESSED_RGBA_ASTC_10x6_KHR}, // ASTC_2D_10X6_UNORM {GL_COMPRESSED_RGBA_ASTC_10x10_KHR}, // ASTC_2D_10X10_UNORM {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR}, // ASTC_2D_10X10_SRGB {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 { {VK_FORMAT_ASTC_10x8_SRGB_BLOCK}, // ASTC_2D_10X8_SRGB {VK_FORMAT_ASTC_6x6_UNORM_BLOCK}, // ASTC_2D_6X6_UNORM {VK_FORMAT_ASTC_6x6_SRGB_BLOCK}, // ASTC_2D_6X6_SRGB + {VK_FORMAT_ASTC_10x6_UNORM_BLOCK}, // ASTC_2D_10X6_UNORM {VK_FORMAT_ASTC_10x10_UNORM_BLOCK}, // ASTC_2D_10X10_UNORM {VK_FORMAT_ASTC_10x10_SRGB_BLOCK}, // ASTC_2D_10X10_SRGB {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) { case PixelFormat::ASTC_2D_10X8_SRGB: case PixelFormat::ASTC_2D_6X6_UNORM: case PixelFormat::ASTC_2D_6X6_SRGB: + case PixelFormat::ASTC_2D_10X6_UNORM: case PixelFormat::ASTC_2D_10X10_UNORM: case PixelFormat::ASTC_2D_10X10_SRGB: 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 { ASTC_2D_10X8_SRGB, ASTC_2D_6X6_UNORM, ASTC_2D_6X6_SRGB, + ASTC_2D_10X6_UNORM, ASTC_2D_10X10_UNORM, ASTC_2D_10X10_SRGB, ASTC_2D_12X12_UNORM, @@ -227,6 +228,7 @@ constexpr std::array<u8, MaxPixelFormat> BLOCK_WIDTH_TABLE = {{ 10, // ASTC_2D_10X8_SRGB 6, // ASTC_2D_6X6_UNORM 6, // ASTC_2D_6X6_SRGB + 10, // ASTC_2D_10X6_UNORM 10, // ASTC_2D_10X10_UNORM 10, // ASTC_2D_10X10_SRGB 12, // ASTC_2D_12X12_UNORM @@ -329,6 +331,7 @@ constexpr std::array<u8, MaxPixelFormat> BLOCK_HEIGHT_TABLE = {{ 8, // ASTC_2D_10X8_SRGB 6, // ASTC_2D_6X6_UNORM 6, // ASTC_2D_6X6_SRGB + 6, // ASTC_2D_10X6_UNORM 10, // ASTC_2D_10X10_UNORM 10, // ASTC_2D_10X10_SRGB 12, // ASTC_2D_12X12_UNORM @@ -431,6 +434,7 @@ constexpr std::array<u8, MaxPixelFormat> BITS_PER_BLOCK_TABLE = {{ 128, // ASTC_2D_10X8_SRGB 128, // ASTC_2D_6X6_UNORM 128, // ASTC_2D_6X6_SRGB + 128, // ASTC_2D_10X6_UNORM 128, // ASTC_2D_10X10_UNORM 128, // ASTC_2D_10X10_SRGB 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, return PixelFormat::ASTC_2D_6X6_UNORM; case Hash(TextureFormat::ASTC_2D_6X6, UNORM, SRGB): return PixelFormat::ASTC_2D_6X6_SRGB; + case Hash(TextureFormat::ASTC_2D_10X6, UNORM, LINEAR): + return PixelFormat::ASTC_2D_10X6_UNORM; case Hash(TextureFormat::ASTC_2D_10X10, UNORM, LINEAR): return PixelFormat::ASTC_2D_10X10_UNORM; 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 return "ASTC_2D_6X6_UNORM"; case PixelFormat::ASTC_2D_6X6_SRGB: return "ASTC_2D_6X6_SRGB"; + case PixelFormat::ASTC_2D_10X6_UNORM: + return "ASTC_2D_10X6_UNORM"; case PixelFormat::ASTC_2D_10X10_UNORM: return "ASTC_2D_10X10_UNORM"; case PixelFormat::ASTC_2D_10X10_SRGB: diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index 11ce865a7..743ac09f6 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -669,17 +669,6 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR const bool is_amd = driver_id == VK_DRIVER_ID_AMD_PROPRIETARY || driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE; if (is_amd) { - // TODO(lat9nq): Add an upper bound when AMD fixes their VK_KHR_push_descriptor - const bool has_broken_push_descriptor = VK_VERSION_MAJOR(properties.driverVersion) == 2 && - VK_VERSION_MINOR(properties.driverVersion) == 0 && - VK_VERSION_PATCH(properties.driverVersion) >= 226; - if (khr_push_descriptor && has_broken_push_descriptor) { - LOG_WARNING( - Render_Vulkan, - "Disabling AMD driver 2.0.226 and later from broken VK_KHR_push_descriptor"); - khr_push_descriptor = false; - } - // AMD drivers need a higher amount of Sets per Pool in certain circunstances like in XC2. sets_per_pool = 96; // Disable VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT on AMD GCN4 and lower as it is broken. diff --git a/src/yuzu/applets/qt_web_browser.cpp b/src/yuzu/applets/qt_web_browser.cpp index 283c04cd5..89bd482e0 100644 --- a/src/yuzu/applets/qt_web_browser.cpp +++ b/src/yuzu/applets/qt_web_browser.cpp @@ -2,6 +2,8 @@ // SPDX-License-Identifier: GPL-2.0-or-later #ifdef YUZU_USE_QT_WEB_ENGINE +#include <bit> + #include <QApplication> #include <QKeyEvent> @@ -52,8 +54,8 @@ QtNXWebEngineView::QtNXWebEngineView(QWidget* parent, Core::System& system, : QWebEngineView(parent), input_subsystem{input_subsystem_}, url_interceptor(std::make_unique<UrlRequestInterceptor>()), input_interpreter(std::make_unique<InputInterpreter>(system)), - default_profile{QWebEngineProfile::defaultProfile()}, - global_settings{QWebEngineSettings::globalSettings()} { + default_profile{QWebEngineProfile::defaultProfile()}, global_settings{ + default_profile->settings()} { default_profile->setPersistentStoragePath(QString::fromStdString(Common::FS::PathToUTF8String( Common::FS::GetYuzuPath(Common::FS::YuzuPath::YuzuDir) / "qtwebengine"))); @@ -78,7 +80,7 @@ QtNXWebEngineView::QtNXWebEngineView(QWidget* parent, Core::System& system, default_profile->scripts()->insert(gamepad); default_profile->scripts()->insert(window_nx); - default_profile->setRequestInterceptor(url_interceptor.get()); + default_profile->setUrlRequestInterceptor(url_interceptor.get()); global_settings->setAttribute(QWebEngineSettings::LocalContentCanAccessRemoteUrls, true); global_settings->setAttribute(QWebEngineSettings::FullScreenSupportEnabled, true); @@ -211,8 +213,10 @@ template <Core::HID::NpadButton... T> void QtNXWebEngineView::HandleWindowFooterButtonPressedOnce() { const auto f = [this](Core::HID::NpadButton button) { if (input_interpreter->IsButtonPressedOnce(button)) { + const auto button_index = std::countr_zero(static_cast<u64>(button)); + page()->runJavaScript( - QStringLiteral("yuzu_key_callbacks[%1] == null;").arg(static_cast<u8>(button)), + QStringLiteral("yuzu_key_callbacks[%1] == null;").arg(button_index), [this, button](const QVariant& variant) { if (variant.toBool()) { switch (button) { @@ -236,7 +240,7 @@ void QtNXWebEngineView::HandleWindowFooterButtonPressedOnce() { page()->runJavaScript( QStringLiteral("if (yuzu_key_callbacks[%1] != null) { yuzu_key_callbacks[%1](); }") - .arg(static_cast<u8>(button))); + .arg(button_index)); } }; diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 9df4752be..9686412d0 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -133,7 +133,7 @@ void Config::Initialize(const std::string& config_name) { // Explicit std::string definition: Qt can't implicitly convert a std::string to a QVariant, nor // can it implicitly convert a QVariant back to a {std::,Q}string template <> -void Config::ReadBasicSetting(Settings::BasicSetting<std::string>& setting) { +void Config::ReadBasicSetting(Settings::Setting<std::string>& setting) { const QString name = QString::fromStdString(setting.GetLabel()); const auto default_value = QString::fromStdString(setting.GetDefault()); if (qt_config->value(name + QStringLiteral("/default"), false).toBool()) { @@ -144,7 +144,7 @@ void Config::ReadBasicSetting(Settings::BasicSetting<std::string>& setting) { } template <typename Type> -void Config::ReadBasicSetting(Settings::BasicSetting<Type>& setting) { +void Config::ReadBasicSetting(Settings::Setting<Type>& setting) { const QString name = QString::fromStdString(setting.GetLabel()); const Type default_value = setting.GetDefault(); if (qt_config->value(name + QStringLiteral("/default"), false).toBool()) { @@ -157,7 +157,7 @@ void Config::ReadBasicSetting(Settings::BasicSetting<Type>& setting) { // Explicit std::string definition: Qt can't implicitly convert a std::string to a QVariant template <> -void Config::WriteBasicSetting(const Settings::BasicSetting<std::string>& setting) { +void Config::WriteBasicSetting(const Settings::Setting<std::string>& setting) { const QString name = QString::fromStdString(setting.GetLabel()); const std::string& value = setting.GetValue(); qt_config->setValue(name + QStringLiteral("/default"), value == setting.GetDefault()); @@ -165,7 +165,7 @@ void Config::WriteBasicSetting(const Settings::BasicSetting<std::string>& settin } template <typename Type> -void Config::WriteBasicSetting(const Settings::BasicSetting<Type>& setting) { +void Config::WriteBasicSetting(const Settings::Setting<Type>& setting) { const QString name = QString::fromStdString(setting.GetLabel()); const Type value = setting.GetValue(); qt_config->setValue(name + QStringLiteral("/default"), value == setting.GetDefault()); @@ -173,7 +173,7 @@ void Config::WriteBasicSetting(const Settings::BasicSetting<Type>& setting) { } template <typename Type> -void Config::WriteGlobalSetting(const Settings::Setting<Type>& setting) { +void Config::WriteGlobalSetting(const Settings::SwitchableSetting<Type>& setting) { const QString name = QString::fromStdString(setting.GetLabel()); const Type& value = setting.GetValue(global); if (!global) { @@ -1422,7 +1422,7 @@ QVariant Config::ReadSetting(const QString& name, const QVariant& default_value) } template <typename Type> -void Config::ReadGlobalSetting(Settings::Setting<Type>& setting) { +void Config::ReadGlobalSetting(Settings::SwitchableSetting<Type>& setting) { QString name = QString::fromStdString(setting.GetLabel()); const bool use_global = qt_config->value(name + QStringLiteral("/use_global"), true).toBool(); setting.SetGlobal(use_global); diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h index f0ab6bdaa..9ca878d23 100644 --- a/src/yuzu/configuration/config.h +++ b/src/yuzu/configuration/config.h @@ -160,7 +160,7 @@ private: * @param The setting */ template <typename Type> - void ReadGlobalSetting(Settings::Setting<Type>& setting); + void ReadGlobalSetting(Settings::SwitchableSetting<Type>& setting); /** * Sets a value to the qt_config using the setting's label and default value. If the config is a @@ -169,7 +169,7 @@ private: * @param The setting */ template <typename Type> - void WriteGlobalSetting(const Settings::Setting<Type>& setting); + void WriteGlobalSetting(const Settings::SwitchableSetting<Type>& setting); /** * Reads a value from the qt_config using the setting's label and default value and applies the @@ -178,14 +178,14 @@ private: * @param The setting */ template <typename Type> - void ReadBasicSetting(Settings::BasicSetting<Type>& setting); + void ReadBasicSetting(Settings::Setting<Type>& setting); /** Sets a value from the setting in the qt_config using the setting's label and default value. * * @param The setting */ template <typename Type> - void WriteBasicSetting(const Settings::BasicSetting<Type>& setting); + void WriteBasicSetting(const Settings::Setting<Type>& setting); ConfigType type; 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 @@ #include "yuzu/configuration/configuration_shared.h" #include "yuzu/configuration/configure_per_game.h" -void ConfigurationShared::ApplyPerGameSetting(Settings::Setting<bool>* setting, +void ConfigurationShared::ApplyPerGameSetting(Settings::SwitchableSetting<bool>* setting, const QCheckBox* checkbox, const CheckState& tracker) { if (Settings::IsConfiguringGlobal() && setting->UsingGlobal()) { @@ -25,7 +25,7 @@ void ConfigurationShared::ApplyPerGameSetting(Settings::Setting<bool>* setting, } void ConfigurationShared::SetPerGameSetting(QCheckBox* checkbox, - const Settings::Setting<bool>* setting) { + const Settings::SwitchableSetting<bool>* setting) { if (setting->UsingGlobal()) { checkbox->setCheckState(Qt::PartiallyChecked); } else { @@ -45,7 +45,7 @@ void ConfigurationShared::SetHighlight(QWidget* widget, bool highlighted) { } void ConfigurationShared::SetColoredTristate(QCheckBox* checkbox, - const Settings::Setting<bool>& setting, + const Settings::SwitchableSetting<bool>& setting, CheckState& tracker) { if (setting.UsingGlobal()) { tracker = CheckState::Global; diff --git a/src/yuzu/configuration/configuration_shared.h b/src/yuzu/configuration/configuration_shared.h index 903a9baae..77802a367 100644 --- a/src/yuzu/configuration/configuration_shared.h +++ b/src/yuzu/configuration/configuration_shared.h @@ -25,10 +25,10 @@ enum class CheckState { // Global-aware apply and set functions // ApplyPerGameSetting, given a Settings::Setting and a Qt UI element, properly applies a Setting -void ApplyPerGameSetting(Settings::Setting<bool>* setting, const QCheckBox* checkbox, +void ApplyPerGameSetting(Settings::SwitchableSetting<bool>* setting, const QCheckBox* checkbox, const CheckState& tracker); template <typename Type> -void ApplyPerGameSetting(Settings::Setting<Type>* setting, const QComboBox* combobox) { +void ApplyPerGameSetting(Settings::SwitchableSetting<Type>* setting, const QComboBox* combobox) { if (Settings::IsConfiguringGlobal() && setting->UsingGlobal()) { setting->SetValue(static_cast<Type>(combobox->currentIndex())); } else if (!Settings::IsConfiguringGlobal()) { @@ -43,10 +43,10 @@ void ApplyPerGameSetting(Settings::Setting<Type>* setting, const QComboBox* comb } // Sets a Qt UI element given a Settings::Setting -void SetPerGameSetting(QCheckBox* checkbox, const Settings::Setting<bool>* setting); +void SetPerGameSetting(QCheckBox* checkbox, const Settings::SwitchableSetting<bool>* setting); template <typename Type> -void SetPerGameSetting(QComboBox* combobox, const Settings::Setting<Type>* setting) { +void SetPerGameSetting(QComboBox* combobox, const Settings::SwitchableSetting<Type>* setting) { combobox->setCurrentIndex(setting->UsingGlobal() ? ConfigurationShared::USE_GLOBAL_INDEX : static_cast<int>(setting->GetValue()) + ConfigurationShared::USE_GLOBAL_OFFSET); @@ -56,7 +56,7 @@ void SetPerGameSetting(QComboBox* combobox, const Settings::Setting<Type>* setti void SetHighlight(QWidget* widget, bool highlighted); // Sets up a QCheckBox like a tristate one, given a Setting -void SetColoredTristate(QCheckBox* checkbox, const Settings::Setting<bool>& setting, +void SetColoredTristate(QCheckBox* checkbox, const Settings::SwitchableSetting<bool>& setting, CheckState& tracker); void SetColoredTristate(QCheckBox* checkbox, bool global, bool state, bool global_state, 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 { QByteArray gamelist_header_state; QByteArray microprofile_geometry; - Settings::BasicSetting<bool> microprofile_visible{false, "microProfileDialogVisible"}; - - Settings::BasicSetting<bool> single_window_mode{true, "singleWindowMode"}; - Settings::BasicSetting<bool> fullscreen{false, "fullscreen"}; - Settings::BasicSetting<bool> display_titlebar{true, "displayTitleBars"}; - Settings::BasicSetting<bool> show_filter_bar{true, "showFilterBar"}; - Settings::BasicSetting<bool> show_status_bar{true, "showStatusBar"}; - - Settings::BasicSetting<bool> confirm_before_closing{true, "confirmClose"}; - Settings::BasicSetting<bool> first_start{true, "firstStart"}; - Settings::BasicSetting<bool> pause_when_in_background{false, "pauseWhenInBackground"}; - Settings::BasicSetting<bool> mute_when_in_background{false, "muteWhenInBackground"}; - Settings::BasicSetting<bool> hide_mouse{true, "hideInactiveMouse"}; + Settings::Setting<bool> microprofile_visible{false, "microProfileDialogVisible"}; + + Settings::Setting<bool> single_window_mode{true, "singleWindowMode"}; + Settings::Setting<bool> fullscreen{false, "fullscreen"}; + Settings::Setting<bool> display_titlebar{true, "displayTitleBars"}; + Settings::Setting<bool> show_filter_bar{true, "showFilterBar"}; + Settings::Setting<bool> show_status_bar{true, "showStatusBar"}; + + Settings::Setting<bool> confirm_before_closing{true, "confirmClose"}; + Settings::Setting<bool> first_start{true, "firstStart"}; + Settings::Setting<bool> pause_when_in_background{false, "pauseWhenInBackground"}; + Settings::Setting<bool> mute_when_in_background{false, "muteWhenInBackground"}; + Settings::Setting<bool> hide_mouse{true, "hideInactiveMouse"}; // Set when Vulkan is known to crash the application - Settings::BasicSetting<bool> has_broken_vulkan{false, "has_broken_vulkan"}; + Settings::Setting<bool> has_broken_vulkan{false, "has_broken_vulkan"}; - Settings::BasicSetting<bool> select_user_on_boot{false, "select_user_on_boot"}; + Settings::Setting<bool> select_user_on_boot{false, "select_user_on_boot"}; // Discord RPC - Settings::BasicSetting<bool> enable_discord_presence{true, "enable_discord_presence"}; + Settings::Setting<bool> enable_discord_presence{true, "enable_discord_presence"}; - Settings::BasicSetting<bool> enable_screenshot_save_as{true, "enable_screenshot_save_as"}; + Settings::Setting<bool> enable_screenshot_save_as{true, "enable_screenshot_save_as"}; QString roms_path; QString symbols_path; @@ -100,25 +100,25 @@ struct Values { // Shortcut name <Shortcut, context> std::vector<Shortcut> shortcuts; - Settings::BasicSetting<uint32_t> callout_flags{0, "calloutFlags"}; + Settings::Setting<uint32_t> callout_flags{0, "calloutFlags"}; // logging - Settings::BasicSetting<bool> show_console{false, "showConsole"}; + Settings::Setting<bool> show_console{false, "showConsole"}; // Game List - Settings::BasicSetting<bool> show_add_ons{true, "show_add_ons"}; - Settings::BasicSetting<uint32_t> game_icon_size{64, "game_icon_size"}; - Settings::BasicSetting<uint32_t> folder_icon_size{48, "folder_icon_size"}; - Settings::BasicSetting<uint8_t> row_1_text_id{3, "row_1_text_id"}; - Settings::BasicSetting<uint8_t> row_2_text_id{2, "row_2_text_id"}; + Settings::Setting<bool> show_add_ons{true, "show_add_ons"}; + Settings::Setting<uint32_t> game_icon_size{64, "game_icon_size"}; + Settings::Setting<uint32_t> folder_icon_size{48, "folder_icon_size"}; + Settings::Setting<uint8_t> row_1_text_id{3, "row_1_text_id"}; + Settings::Setting<uint8_t> row_2_text_id{2, "row_2_text_id"}; std::atomic_bool is_game_list_reload_pending{false}; - Settings::BasicSetting<bool> cache_game_list{true, "cache_game_list"}; - Settings::BasicSetting<bool> favorites_expanded{true, "favorites_expanded"}; + Settings::Setting<bool> cache_game_list{true, "cache_game_list"}; + Settings::Setting<bool> favorites_expanded{true, "favorites_expanded"}; QVector<u64> favorited_ids; bool configuration_applied; bool reset_to_defaults; - Settings::BasicSetting<bool> disable_web_applet{true, "disable_web_applet"}; + Settings::Setting<bool> disable_web_applet{true, "disable_web_applet"}; }; extern Values values; diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp index fc4744fb0..903e02297 100644 --- a/src/yuzu_cmd/config.cpp +++ b/src/yuzu_cmd/config.cpp @@ -90,17 +90,17 @@ static const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> }}; template <> -void Config::ReadSetting(const std::string& group, Settings::BasicSetting<std::string>& setting) { +void Config::ReadSetting(const std::string& group, Settings::Setting<std::string>& setting) { setting = sdl2_config->Get(group, setting.GetLabel(), setting.GetDefault()); } template <> -void Config::ReadSetting(const std::string& group, Settings::BasicSetting<bool>& setting) { +void Config::ReadSetting(const std::string& group, Settings::Setting<bool>& setting) { setting = sdl2_config->GetBoolean(group, setting.GetLabel(), setting.GetDefault()); } template <typename Type> -void Config::ReadSetting(const std::string& group, Settings::BasicSetting<Type>& setting) { +void Config::ReadSetting(const std::string& group, Settings::Setting<Type>& setting) { setting = static_cast<Type>(sdl2_config->GetInteger(group, setting.GetLabel(), static_cast<long>(setting.GetDefault()))); } diff --git a/src/yuzu_cmd/config.h b/src/yuzu_cmd/config.h index f61ba23ec..ccf77d668 100644 --- a/src/yuzu_cmd/config.h +++ b/src/yuzu_cmd/config.h @@ -28,11 +28,11 @@ public: private: /** - * Applies a value read from the sdl2_config to a BasicSetting. + * Applies a value read from the sdl2_config to a Setting. * * @param group The name of the INI group * @param setting The yuzu setting to modify */ template <typename Type> - void ReadSetting(const std::string& group, Settings::BasicSetting<Type>& setting); + void ReadSetting(const std::string& group, Settings::Setting<Type>& setting); }; |