diff options
Diffstat (limited to '')
32 files changed, 517 insertions, 472 deletions
diff --git a/src/audio_core/audio_renderer.cpp b/src/audio_core/audio_renderer.cpp index 2ee0a96ed..9191ca093 100644 --- a/src/audio_core/audio_renderer.cpp +++ b/src/audio_core/audio_renderer.cpp @@ -2,6 +2,7 @@ // 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/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/core/arm/arm_interface.cpp b/src/core/arm/arm_interface.cpp index 0efc3732f..cef79b245 100644 --- a/src/core/arm/arm_interface.cpp +++ b/src/core/arm/arm_interface.cpp @@ -1,6 +1,10 @@ // 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 } } } diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp index 10cf72a45..1be5fe1c1 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp @@ -427,18 +427,38 @@ void ARM_Dynarmic_32::PageTableChanged(Common::PageTable& page_table, } 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 92266aa9e..c437f24b8 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp @@ -494,22 +494,22 @@ void ARM_Dynarmic_64::PageTableChanged(Common::PageTable& page_table, } 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); } @@ -520,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 140578069..5425637f5 100644 --- a/src/core/core_timing.cpp +++ b/src/core/core_timing.cpp @@ -22,10 +22,11 @@ std::shared_ptr<EventType> CreateEvent(std::string name, TimedCallback&& callbac } 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 @@ -58,15 +59,11 @@ 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) { - const auto hardware_concurrency = std::thread::hardware_concurrency(); - size_t id = 0; - worker_threads.emplace_back(ThreadEntry, std::ref(*this), id++); - if (hardware_concurrency > 8) { - worker_threads.emplace_back(ThreadEntry, std::ref(*this), id++); - } + worker_threads.emplace_back(ThreadEntry, std::ref(*this), 0); } } @@ -81,6 +78,7 @@ void CoreTiming::Shutdown() { thread.join(); } worker_threads.clear(); + pause_callbacks.clear(); ClearPendingEvents(); has_started = false; } @@ -98,6 +96,14 @@ void CoreTiming::Pause(bool is_paused_) { } } 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_) { @@ -121,6 +127,14 @@ void CoreTiming::SyncPause(bool is_paused_) { wait_signal_cv.wait(main_lock, [this] { return pause_count == 0; }); } } + + if (!is_paused_) { + pause_end_time = GetGlobalTimeNs().count(); + } + + for (auto& cb : pause_callbacks) { + cb(is_paused_); + } } bool CoreTiming::IsRunning() const { @@ -134,12 +148,30 @@ bool CoreTiming::HasPendingEvents() const { void CoreTiming::ScheduleEvent(std::chrono::nanoseconds ns_into_future, const std::shared_ptr<EventType>& event_type, - std::uintptr_t user_data) { + std::uintptr_t user_data, bool absolute_time) { std::unique_lock main_lock(event_mutex); - const u64 timeout = static_cast<u64>((GetGlobalTimeNs() + ns_into_future).count()); + 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<>()); + + 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<>()); @@ -218,6 +250,11 @@ 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() { global_timer = GetGlobalTimeNs().count(); @@ -228,17 +265,31 @@ std::optional<s64> CoreTiming::Advance() { event_queue.pop_back(); if (const auto event_type{evt.type.lock()}) { - sequence_mutex.lock(); event_mutex.unlock(); - event_type->guard.lock(); - sequence_mutex.unlock(); - const s64 delay = static_cast<s64>(GetGlobalTimeNs().count() - evt.time); - event_type->callback(evt.user_data, std::chrono::nanoseconds{delay}); - event_type->guard.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<>()); + } } global_timer = GetGlobalTimeNs().count(); diff --git a/src/core/core_timing.h b/src/core/core_timing.h index a86553e08..09b6ed81a 100644 --- a/src/core/core_timing.h +++ b/src/core/core_timing.h @@ -20,8 +20,9 @@ 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 { @@ -32,7 +33,6 @@ struct EventType { TimedCallback callback; /// A pointer to the name of the event. const std::string name; - mutable std::mutex guard; }; /** @@ -94,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); @@ -126,6 +134,9 @@ 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; @@ -137,7 +148,7 @@ private: 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 @@ -157,17 +168,19 @@ private: std::condition_variable wait_pause_cv; std::condition_variable wait_signal_cv; mutable std::mutex event_mutex; - mutable std::mutex sequence_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/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_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_thread.cpp b/src/core/hle/kernel/k_thread.cpp index 23bf7425a..50cb5fc90 100644 --- a/src/core/hle/kernel/k_thread.cpp +++ b/src/core/hle/kernel/k_thread.cpp @@ -480,9 +480,7 @@ void KThread::Unpin() { // 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); } } @@ -877,6 +875,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. @@ -890,6 +889,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. @@ -965,6 +965,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/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..aa7189bda 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp @@ -49,28 +49,41 @@ bool Controller_NPad::IsNpadIdValid(Core::HID::NpadIdType npad_id) { } } -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; } @@ -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/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/tests/core/core_timing.cpp b/src/tests/core/core_timing.cpp index e687416a8..894975e6f 100644 --- a/src/tests/core/core_timing.cpp +++ b/src/tests/core/core_timing.cpp @@ -9,6 +9,7 @@ #include <cstdlib> #include <memory> #include <mutex> +#include <optional> #include <string> #include "core/core.h" @@ -25,13 +26,15 @@ 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); delays[IDX] = ns_late.count(); ++expected_callback; + return std::nullopt; } struct ScopeInit final { diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 9df4752be..9686412d0 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -133,7 +133,7 @@ void Config::Initialize(const std::string& config_name) { // 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); }; |