early-access version 1840

This commit is contained in:
pineappleEA 2021-07-03 11:00:06 +02:00
parent 755af6b65b
commit cae72a4c69
71 changed files with 1818 additions and 1257 deletions

View file

@ -1,7 +1,7 @@
yuzu emulator early access yuzu emulator early access
============= =============
This is the source code for early-access 1838. This is the source code for early-access 1840.
## Legal Notice ## Legal Notice

View file

@ -30,7 +30,8 @@ StreamPtr AudioOut::OpenStream(Core::Timing::CoreTiming& core_timing, u32 sample
u32 num_channels, std::string&& name, u32 num_channels, std::string&& name,
Stream::ReleaseCallback&& release_callback) { Stream::ReleaseCallback&& release_callback) {
if (!sink) { if (!sink) {
sink = CreateSinkFromID(Settings::values.sink_id, Settings::values.audio_device_id); sink = CreateSinkFromID(Settings::values.sink_id.GetValue(),
Settings::values.audio_device_id.GetValue());
} }
return std::make_shared<Stream>( return std::make_shared<Stream>(

View file

@ -41,7 +41,7 @@ void LogSettings() {
LOG_INFO(Config, "yuzu Configuration:"); LOG_INFO(Config, "yuzu Configuration:");
log_setting("Controls_UseDockedMode", values.use_docked_mode.GetValue()); log_setting("Controls_UseDockedMode", values.use_docked_mode.GetValue());
log_setting("System_RngSeed", values.rng_seed.GetValue().value_or(0)); log_setting("System_RngSeed", values.rng_seed.GetValue().value_or(0));
log_setting("System_CurrentUser", values.current_user); log_setting("System_CurrentUser", values.current_user.GetValue());
log_setting("System_LanguageIndex", values.language_index.GetValue()); log_setting("System_LanguageIndex", values.language_index.GetValue());
log_setting("System_RegionIndex", values.region_index.GetValue()); log_setting("System_RegionIndex", values.region_index.GetValue());
log_setting("System_TimeZoneIndex", values.time_zone_index.GetValue()); log_setting("System_TimeZoneIndex", values.time_zone_index.GetValue());
@ -61,18 +61,18 @@ void LogSettings() {
log_setting("Renderer_UseAsynchronousShaders", values.use_asynchronous_shaders.GetValue()); log_setting("Renderer_UseAsynchronousShaders", values.use_asynchronous_shaders.GetValue());
log_setting("Renderer_UseGarbageCollection", values.use_caches_gc.GetValue()); log_setting("Renderer_UseGarbageCollection", values.use_caches_gc.GetValue());
log_setting("Renderer_AnisotropicFilteringLevel", values.max_anisotropy.GetValue()); log_setting("Renderer_AnisotropicFilteringLevel", values.max_anisotropy.GetValue());
log_setting("Audio_OutputEngine", values.sink_id); log_setting("Audio_OutputEngine", values.sink_id.GetValue());
log_setting("Audio_EnableAudioStretching", values.enable_audio_stretching.GetValue()); log_setting("Audio_EnableAudioStretching", values.enable_audio_stretching.GetValue());
log_setting("Audio_OutputDevice", values.audio_device_id); log_setting("Audio_OutputDevice", values.audio_device_id.GetValue());
log_setting("DataStorage_UseVirtualSd", values.use_virtual_sd); log_setting("DataStorage_UseVirtualSd", values.use_virtual_sd.GetValue());
log_path("DataStorage_CacheDir", Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir)); log_path("DataStorage_CacheDir", Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir));
log_path("DataStorage_ConfigDir", Common::FS::GetYuzuPath(Common::FS::YuzuPath::ConfigDir)); log_path("DataStorage_ConfigDir", Common::FS::GetYuzuPath(Common::FS::YuzuPath::ConfigDir));
log_path("DataStorage_LoadDir", Common::FS::GetYuzuPath(Common::FS::YuzuPath::LoadDir)); log_path("DataStorage_LoadDir", Common::FS::GetYuzuPath(Common::FS::YuzuPath::LoadDir));
log_path("DataStorage_NANDDir", Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir)); log_path("DataStorage_NANDDir", Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir));
log_path("DataStorage_SDMCDir", Common::FS::GetYuzuPath(Common::FS::YuzuPath::SDMCDir)); log_path("DataStorage_SDMCDir", Common::FS::GetYuzuPath(Common::FS::YuzuPath::SDMCDir));
log_setting("Debugging_ProgramArgs", values.program_args); log_setting("Debugging_ProgramArgs", values.program_args.GetValue());
log_setting("Services_BCATBackend", values.bcat_backend); log_setting("Services_BCATBackend", values.bcat_backend.GetValue());
log_setting("Services_BCATBoxcatLocal", values.bcat_boxcat_local); log_setting("Services_BCATBoxcatLocal", values.bcat_boxcat_local.GetValue());
} }
bool IsConfiguringGlobal() { bool IsConfiguringGlobal() {
@ -94,7 +94,7 @@ bool IsGPULevelHigh() {
bool IsFastmemEnabled() { bool IsFastmemEnabled() {
if (values.cpu_accuracy.GetValue() == CPUAccuracy::DebugMode) { if (values.cpu_accuracy.GetValue() == CPUAccuracy::DebugMode) {
return values.cpuopt_fastmem; return static_cast<bool>(values.cpuopt_fastmem);
} }
return true; return true;
} }

View file

@ -10,10 +10,12 @@
#include <map> #include <map>
#include <optional> #include <optional>
#include <string> #include <string>
#include <utility>
#include <vector> #include <vector>
#include "common/common_types.h" #include "common/common_types.h"
#include "common/settings_input.h" #include "common/settings_input.h"
#include "input_common/udp/client.h"
namespace Settings { namespace Settings {
@ -34,68 +36,236 @@ enum class CPUAccuracy : u32 {
DebugMode = 2, DebugMode = 2,
}; };
/** 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.
*/
template <typename Type> template <typename Type>
class Setting final { class BasicSetting {
protected:
BasicSetting() = default;
/**
* Only sets the setting to the given initializer, leaving the other members to their default
* initializers.
*
* @param global_val Initial value of the setting
*/
explicit BasicSetting(const Type& global_val) : global{global_val} {}
public: public:
Setting() = default; /**
explicit Setting(Type val) : global{val} {} * Sets a default value, label, and setting value.
~Setting() = default; *
void SetGlobal(bool to_global) { * @param default_val Intial value of the setting, and default value of the setting
use_global = to_global; * @param name Label for the setting
} */
bool UsingGlobal() const { explicit BasicSetting(const Type& default_val, const std::string& name)
return use_global; : default_value{default_val}, global{default_val}, label{name} {}
} ~BasicSetting() = default;
Type GetValue(bool need_global = false) const {
if (use_global || need_global) { /**
return global; * Returns a reference to the setting's value.
} *
return local; * @returns A reference to the setting
} */
void SetValue(const Type& value) { [[nodiscard]] const Type& GetValue() const {
if (use_global) { return global;
global = value;
} else {
local = value;
}
} }
private: /**
bool use_global = true; * Sets the setting to the given value.
Type global{}; *
Type local{}; * @param value The desired value
*/
void SetValue(const Type& value) {
Type temp{value};
std::swap(global, temp);
}
/**
* Returns the value that this setting was created with.
*
* @returns A reference to the default value
*/
[[nodiscard]] const Type& GetDefault() const {
return default_value;
}
/**
* Returns the label this setting was created with.
*
* @returns A reference to the label
*/
[[nodiscard]] const std::string& GetLabel() const {
return label;
}
/**
* Assigns a value to the setting.
*
* @param value The desired setting value
*
* @returns A reference to the setting
*/
const Type& operator=(const Type& value) {
Type temp{value};
std::swap(global, temp);
return global;
}
/**
* Returns a reference to the setting.
*
* @returns A reference to the setting
*/
explicit operator const Type&() const {
return global;
}
protected:
const Type default_value{}; ///< The default value
Type global{}; ///< The setting
const std::string label{}; ///< The setting's label
}; };
/** /**
* The InputSetting class allows for getting a reference to either the global or local members. * The Setting class is a slightly more complex version of the BasicSetting 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 final : public BasicSetting<Type> {
public:
/**
* Sets a default value, label, and setting value.
*
* @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) {}
~Setting() = default;
/**
* Tells this setting to represent either the global or custom setting when other member
* functions are used. Setting to_global to true means using the global setting, to false
* false for the custom setting.
*
* @param to_global Whether to use the global or custom setting.
*/
void SetGlobal(bool to_global) {
use_global = to_global;
}
/**
* Returns whether this setting is using the global setting or not.
*
* @returns The global state
*/
[[nodiscard]] bool UsingGlobal() const {
return use_global;
}
/**
* Returns either the global or custom setting depending on the values of this setting's global
* state or if the global value was specifically requested.
*
* @param need_global Request global value regardless of setting's state; defaults to false
*
* @returns The required value of the setting
*/
[[nodiscard]] const Type& GetValue(bool need_global = false) const {
if (use_global || need_global) {
return this->global;
}
return custom;
}
/**
* Sets the current setting value depending on the global state.
*
* @param value The new value
*/
void SetValue(const Type& value) {
Type temp{value};
if (use_global) {
std::swap(this->global, temp);
} else {
std::swap(custom, temp);
}
}
/**
* Assigns the current setting value depending on the global state.
*
* @param value The new value
*
* @returns A reference to the current setting value
*/
const Type& operator=(const Type& value) {
Type temp{value};
if (use_global) {
std::swap(this->global, temp);
return this->global;
}
std::swap(custom, temp);
return custom;
}
/**
* Returns the current setting value depending on the global state.
*
* @returns A reference to the current setting value
*/
explicit operator const Type&() const {
if (use_global) {
return this->global;
}
return custom;
}
private:
bool use_global{true}; ///< The setting's global state
Type custom{}; ///< The custom value of the setting
};
/**
* 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 * 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 * using the SetValue() member function found in the Setting class. The primary purpose of this
* class is to store an array of 10 PlayerInput structs for both the global and local (per-game) * class is to store an array of 10 PlayerInput structs for both the global and custom setting and
* setting and allows for easily accessing and modifying both settings. * allows for easily accessing and modifying both settings.
*/ */
template <typename Type> template <typename Type>
class InputSetting final { class InputSetting final {
public: public:
InputSetting() = default; InputSetting() = default;
explicit InputSetting(Type val) : global{val} {} explicit InputSetting(Type val) : BasicSetting<Type>(val) {}
~InputSetting() = default; ~InputSetting() = default;
void SetGlobal(bool to_global) { void SetGlobal(bool to_global) {
use_global = to_global; use_global = to_global;
} }
bool UsingGlobal() const { [[nodiscard]] bool UsingGlobal() const {
return use_global; return use_global;
} }
Type& GetValue(bool need_global = false) { [[nodiscard]] Type& GetValue(bool need_global = false) {
if (use_global || need_global) { if (use_global || need_global) {
return global; return global;
} }
return local; return custom;
} }
private: private:
bool use_global = true; bool use_global{true}; ///< The setting's global state
Type global{}; Type global{}; ///< The setting
Type local{}; Type custom{}; ///< The custom setting value
}; };
struct TouchFromButtonMap { struct TouchFromButtonMap {
@ -105,144 +275,155 @@ struct TouchFromButtonMap {
struct Values { struct Values {
// Audio // Audio
std::string audio_device_id; BasicSetting<std::string> audio_device_id{"auto", "output_device"};
std::string sink_id; BasicSetting<std::string> sink_id{"auto", "output_engine"};
bool audio_muted; BasicSetting<bool> audio_muted{false, "audio_muted"};
Setting<bool> enable_audio_stretching; Setting<bool> enable_audio_stretching{true, "enable_audio_stretching"};
Setting<float> volume; Setting<float> volume{1.0f, "volume"};
// Core // Core
Setting<bool> use_multi_core; Setting<bool> use_multi_core{true, "use_multi_core"};
// Cpu // Cpu
Setting<CPUAccuracy> cpu_accuracy; Setting<CPUAccuracy> cpu_accuracy{CPUAccuracy::Accurate, "cpu_accuracy"};
bool cpuopt_page_tables; BasicSetting<bool> cpuopt_page_tables{true, "cpuopt_page_tables"};
bool cpuopt_block_linking; BasicSetting<bool> cpuopt_block_linking{true, "cpuopt_block_linking"};
bool cpuopt_return_stack_buffer; BasicSetting<bool> cpuopt_return_stack_buffer{true, "cpuopt_return_stack_buffer"};
bool cpuopt_fast_dispatcher; BasicSetting<bool> cpuopt_fast_dispatcher{true, "cpuopt_fast_dispatcher"};
bool cpuopt_context_elimination; BasicSetting<bool> cpuopt_context_elimination{true, "cpuopt_context_elimination"};
bool cpuopt_const_prop; BasicSetting<bool> cpuopt_const_prop{true, "cpuopt_const_prop"};
bool cpuopt_misc_ir; BasicSetting<bool> cpuopt_misc_ir{true, "cpuopt_misc_ir"};
bool cpuopt_reduce_misalign_checks; BasicSetting<bool> cpuopt_reduce_misalign_checks{true, "cpuopt_reduce_misalign_checks"};
bool cpuopt_fastmem; BasicSetting<bool> cpuopt_fastmem{true, "cpuopt_fastmem"};
Setting<bool> cpuopt_unsafe_unfuse_fma; Setting<bool> cpuopt_unsafe_unfuse_fma{true, "cpuopt_unsafe_unfuse_fma"};
Setting<bool> cpuopt_unsafe_reduce_fp_error; Setting<bool> cpuopt_unsafe_reduce_fp_error{true, "cpuopt_unsafe_reduce_fp_error"};
Setting<bool> cpuopt_unsafe_ignore_standard_fpcr; Setting<bool> cpuopt_unsafe_ignore_standard_fpcr{true, "cpuopt_unsafe_ignore_standard_fpcr"};
Setting<bool> cpuopt_unsafe_inaccurate_nan; Setting<bool> cpuopt_unsafe_inaccurate_nan{true, "cpuopt_unsafe_inaccurate_nan"};
Setting<bool> cpuopt_unsafe_fastmem_check; Setting<bool> cpuopt_unsafe_fastmem_check{true, "cpuopt_unsafe_fastmem_check"};
// Renderer // Renderer
Setting<RendererBackend> renderer_backend; Setting<RendererBackend> renderer_backend{RendererBackend::OpenGL, "backend"};
bool renderer_debug; BasicSetting<bool> renderer_debug{false, "debug"};
Setting<int> vulkan_device; Setting<int> vulkan_device{0, "vulkan_device"};
Setting<u16> resolution_factor{1}; Setting<u16> resolution_factor{1, "resolution_factor"};
Setting<int> fullscreen_mode; // *nix platforms may have issues with the borderless windowed fullscreen mode.
Setting<int> aspect_ratio; // Default to exclusive fullscreen on these platforms for now.
Setting<int> max_anisotropy; Setting<int> fullscreen_mode{
Setting<bool> use_frame_limit; #ifdef _WIN32
Setting<u16> frame_limit; 0,
Setting<bool> use_disk_shader_cache; #else
Setting<GPUAccuracy> gpu_accuracy; 1,
Setting<bool> use_asynchronous_gpu_emulation; #endif
Setting<bool> use_nvdec_emulation; "fullscreen_mode"};
Setting<bool> accelerate_astc; Setting<int> aspect_ratio{0, "aspect_ratio"};
Setting<bool> use_vsync; Setting<int> max_anisotropy{0, "max_anisotropy"};
Setting<bool> disable_fps_limit; Setting<bool> use_frame_limit{true, "use_frame_limit"};
Setting<bool> use_assembly_shaders; Setting<u16> frame_limit{100, "frame_limit"};
Setting<bool> use_asynchronous_shaders; Setting<bool> use_disk_shader_cache{true, "use_disk_shader_cache"};
Setting<bool> use_fast_gpu_time; Setting<GPUAccuracy> gpu_accuracy{GPUAccuracy::High, "gpu_accuracy"};
Setting<bool> use_caches_gc; Setting<bool> use_asynchronous_gpu_emulation{true, "use_asynchronous_gpu_emulation"};
Setting<bool> use_nvdec_emulation{true, "use_nvdec_emulation"};
Setting<bool> accelerate_astc{true, "accelerate_astc"};
Setting<bool> use_vsync{true, "use_vsync"};
Setting<bool> disable_fps_limit{false, "disable_fps_limit"};
Setting<bool> use_assembly_shaders{false, "use_assembly_shaders"};
Setting<bool> use_asynchronous_shaders{false, "use_asynchronous_shaders"};
Setting<bool> use_fast_gpu_time{true, "use_fast_gpu_time"};
Setting<bool> use_caches_gc{false, "use_caches_gc"};
Setting<float> bg_red; Setting<float> bg_red{0.0f, "bg_red"};
Setting<float> bg_green; Setting<float> bg_green{0.0f, "bg_green"};
Setting<float> bg_blue; Setting<float> bg_blue{0.0f, "bg_blue"};
// System // System
Setting<std::optional<u32>> rng_seed; Setting<std::optional<u32>> rng_seed{std::optional<u32>(), "rng_seed"};
// Measured in seconds since epoch // Measured in seconds since epoch
std::optional<std::chrono::seconds> custom_rtc; std::optional<std::chrono::seconds> custom_rtc;
// Set on game boot, reset on stop. Seconds difference between current time and `custom_rtc` // Set on game boot, reset on stop. Seconds difference between current time and `custom_rtc`
std::chrono::seconds custom_rtc_differential; std::chrono::seconds custom_rtc_differential;
s32 current_user; BasicSetting<s32> current_user{0, "current_user"};
Setting<s32> language_index; Setting<s32> language_index{1, "language_index"};
Setting<s32> region_index; Setting<s32> region_index{1, "region_index"};
Setting<s32> time_zone_index; Setting<s32> time_zone_index{0, "time_zone_index"};
Setting<s32> sound_index; Setting<s32> sound_index{1, "sound_index"};
// Controls // Controls
InputSetting<std::array<PlayerInput, 10>> players; InputSetting<std::array<PlayerInput, 10>> players;
Setting<bool> use_docked_mode; Setting<bool> use_docked_mode{true, "use_docked_mode"};
Setting<bool> vibration_enabled; Setting<bool> vibration_enabled{true, "vibration_enabled"};
Setting<bool> enable_accurate_vibrations; Setting<bool> enable_accurate_vibrations{false, "enable_accurate_vibrations"};
Setting<bool> motion_enabled; Setting<bool> motion_enabled{true, "motion_enabled"};
std::string motion_device; BasicSetting<std::string> motion_device{"engine:motion_emu,update_period:100,sensitivity:0.01",
std::string udp_input_servers; "motion_device"};
BasicSetting<std::string> udp_input_servers{InputCommon::CemuhookUDP::DEFAULT_SRV,
"udp_input_servers"};
bool mouse_panning; BasicSetting<bool> mouse_panning{false, "mouse_panning"};
float mouse_panning_sensitivity; BasicSetting<float> mouse_panning_sensitivity{1.0f, "mouse_panning_sensitivity"};
bool mouse_enabled; BasicSetting<bool> mouse_enabled{false, "mouse_enabled"};
std::string mouse_device; std::string mouse_device;
MouseButtonsRaw mouse_buttons; MouseButtonsRaw mouse_buttons;
bool emulate_analog_keyboard; BasicSetting<bool> emulate_analog_keyboard{false, "emulate_analog_keyboard"};
bool keyboard_enabled; BasicSetting<bool> keyboard_enabled{false, "keyboard_enabled"};
KeyboardKeysRaw keyboard_keys; KeyboardKeysRaw keyboard_keys;
KeyboardModsRaw keyboard_mods; KeyboardModsRaw keyboard_mods;
bool debug_pad_enabled; BasicSetting<bool> debug_pad_enabled{false, "debug_pad_enabled"};
ButtonsRaw debug_pad_buttons; ButtonsRaw debug_pad_buttons;
AnalogsRaw debug_pad_analogs; AnalogsRaw debug_pad_analogs;
TouchscreenInput touchscreen; TouchscreenInput touchscreen;
bool use_touch_from_button; BasicSetting<bool> use_touch_from_button{false, "use_touch_from_button"};
std::string touch_device; BasicSetting<std::string> touch_device{"min_x:100,min_y:50,max_x:1800,max_y:850",
int touch_from_button_map_index; "touch_device"};
BasicSetting<int> touch_from_button_map_index{0, "touch_from_button_map"};
std::vector<TouchFromButtonMap> touch_from_button_maps; std::vector<TouchFromButtonMap> touch_from_button_maps;
std::atomic_bool is_device_reload_pending{true}; std::atomic_bool is_device_reload_pending{true};
// Data Storage // Data Storage
bool use_virtual_sd; BasicSetting<bool> use_virtual_sd{true, "use_virtual_sd"};
bool gamecard_inserted; BasicSetting<bool> gamecard_inserted{false, "gamecard_inserted"};
bool gamecard_current_game; BasicSetting<bool> gamecard_current_game{false, "gamecard_current_game"};
std::string gamecard_path; BasicSetting<std::string> gamecard_path{std::string(), "gamecard_path"};
// Debugging // Debugging
bool record_frame_times; bool record_frame_times;
bool use_gdbstub; BasicSetting<bool> use_gdbstub{false, "use_gdbstub"};
u16 gdbstub_port; BasicSetting<u16> gdbstub_port{0, "gdbstub_port"};
std::string program_args; BasicSetting<std::string> program_args{std::string(), "program_args"};
bool dump_exefs; BasicSetting<bool> dump_exefs{false, "dump_exefs"};
bool dump_nso; BasicSetting<bool> dump_nso{false, "dump_nso"};
bool enable_fs_access_log; BasicSetting<bool> enable_fs_access_log{false, "enable_fs_access_log"};
bool reporting_services; BasicSetting<bool> reporting_services{false, "reporting_services"};
bool quest_flag; BasicSetting<bool> quest_flag{false, "quest_flag"};
bool disable_macro_jit; BasicSetting<bool> disable_macro_jit{false, "disable_macro_jit"};
bool extended_logging; BasicSetting<bool> extended_logging{false, "extended_logging"};
bool use_debug_asserts; BasicSetting<bool> use_debug_asserts{false, "use_debug_asserts"};
bool use_auto_stub; BasicSetting<bool> use_auto_stub{false, "use_auto_stub"};
// Miscellaneous // Miscellaneous
std::string log_filter; BasicSetting<std::string> log_filter{"*:Info", "log_filter"};
bool use_dev_keys; BasicSetting<bool> use_dev_keys{false, "use_dev_keys"};
// Services // Services
std::string bcat_backend; BasicSetting<std::string> bcat_backend{"none", "bcat_backend"};
bool bcat_boxcat_local; BasicSetting<bool> bcat_boxcat_local{false, "bcat_boxcat_local"};
// WebService // WebService
bool enable_telemetry; BasicSetting<bool> enable_telemetry{true, "enable_telemetry"};
std::string web_api_url; BasicSetting<std::string> web_api_url{"https://api.yuzu-emu.org", "web_api_url"};
std::string yuzu_username; BasicSetting<std::string> yuzu_username{std::string(), "yuzu_username"};
std::string yuzu_token; BasicSetting<std::string> yuzu_token{std::string(), "yuzu_token"};
// Add-Ons // Add-Ons
std::map<u64, std::vector<std::string>> disabled_addons; std::map<u64, std::vector<std::string>> disabled_addons;

View file

@ -517,6 +517,8 @@ add_library(core STATIC
hle/service/psc/psc.h hle/service/psc/psc.h
hle/service/ptm/psm.cpp hle/service/ptm/psm.cpp
hle/service/ptm/psm.h hle/service/ptm/psm.h
hle/service/kernel_helpers.cpp
hle/service/kernel_helpers.h
hle/service/service.cpp hle/service/service.cpp
hle/service/service.h hle/service/service.h
hle/service/set/set.cpp hle/service/set/set.cpp

View file

@ -263,9 +263,9 @@ struct System::Impl {
if (Settings::values.gamecard_inserted) { if (Settings::values.gamecard_inserted) {
if (Settings::values.gamecard_current_game) { if (Settings::values.gamecard_current_game) {
fs_controller.SetGameCard(GetGameFileFromPath(virtual_filesystem, filepath)); fs_controller.SetGameCard(GetGameFileFromPath(virtual_filesystem, filepath));
} else if (!Settings::values.gamecard_path.empty()) { } else if (!Settings::values.gamecard_path.GetValue().empty()) {
fs_controller.SetGameCard( fs_controller.SetGameCard(GetGameFileFromPath(
GetGameFileFromPath(virtual_filesystem, Settings::values.gamecard_path)); virtual_filesystem, static_cast<std::string>(Settings::values.gamecard_path)));
} }
} }

View file

@ -13,7 +13,7 @@ ProfileSelectApplet::~ProfileSelectApplet() = default;
void DefaultProfileSelectApplet::SelectProfile( void DefaultProfileSelectApplet::SelectProfile(
std::function<void(std::optional<Common::UUID>)> callback) const { std::function<void(std::optional<Common::UUID>)> callback) const {
Service::Account::ProfileManager manager; Service::Account::ProfileManager manager;
callback(manager.GetUser(Settings::values.current_user).value_or(Common::UUID{})); callback(manager.GetUser(Settings::values.current_user.GetValue()).value_or(Common::UUID{}));
LOG_INFO(Service_ACC, "called, selecting current user instead of prompting..."); LOG_INFO(Service_ACC, "called, selecting current user instead of prompting...");
} }

View file

@ -58,6 +58,9 @@ bool SessionRequestManager::HasSessionRequestHandler(const HLERequestContext& co
void SessionRequestHandler::ClientConnected(KServerSession* session) { void SessionRequestHandler::ClientConnected(KServerSession* session) {
session->ClientConnected(shared_from_this()); session->ClientConnected(shared_from_this());
// Ensure our server session is tracked globally.
kernel.RegisterServerSession(session);
} }
void SessionRequestHandler::ClientDisconnected(KServerSession* session) { void SessionRequestHandler::ClientDisconnected(KServerSession* session) {

View file

@ -3,6 +3,7 @@
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include "core/hle/kernel/k_auto_object.h" #include "core/hle/kernel/k_auto_object.h"
#include "core/hle/kernel/kernel.h"
namespace Kernel { namespace Kernel {
@ -11,4 +12,12 @@ KAutoObject* KAutoObject::Create(KAutoObject* obj) {
return obj; return obj;
} }
void KAutoObject::RegisterWithKernel() {
kernel.RegisterKernelObject(this);
}
void KAutoObject::UnregisterWithKernel() {
kernel.UnregisterKernelObject(this);
}
} // namespace Kernel } // namespace Kernel

View file

@ -85,8 +85,12 @@ private:
KERNEL_AUTOOBJECT_TRAITS(KAutoObject, KAutoObject); KERNEL_AUTOOBJECT_TRAITS(KAutoObject, KAutoObject);
public: public:
explicit KAutoObject(KernelCore& kernel_) : kernel(kernel_) {} explicit KAutoObject(KernelCore& kernel_) : kernel(kernel_) {
virtual ~KAutoObject() = default; RegisterWithKernel();
}
virtual ~KAutoObject() {
UnregisterWithKernel();
}
static KAutoObject* Create(KAutoObject* ptr); static KAutoObject* Create(KAutoObject* ptr);
@ -166,6 +170,10 @@ public:
} }
} }
private:
void RegisterWithKernel();
void UnregisterWithKernel();
protected: protected:
KernelCore& kernel; KernelCore& kernel;
std::string name; std::string name;

View file

@ -10,6 +10,7 @@
#include "common/alignment.h" #include "common/alignment.h"
#include "common/assert.h" #include "common/assert.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "common/scope_exit.h"
#include "common/settings.h" #include "common/settings.h"
#include "core/core.h" #include "core/core.h"
#include "core/device_memory.h" #include "core/device_memory.h"
@ -43,6 +44,8 @@ void SetupMainThread(Core::System& system, KProcess& owner_process, u32 priority
ASSERT(owner_process.GetResourceLimit()->Reserve(LimitableResource::Threads, 1)); ASSERT(owner_process.GetResourceLimit()->Reserve(LimitableResource::Threads, 1));
KThread* thread = KThread::Create(system.Kernel()); KThread* thread = KThread::Create(system.Kernel());
SCOPE_EXIT({ thread->Close(); });
ASSERT(KThread::InitializeUserThread(system, thread, entry_point, 0, stack_top, priority, ASSERT(KThread::InitializeUserThread(system, thread, entry_point, 0, stack_top, priority,
owner_process.GetIdealCoreId(), &owner_process) owner_process.GetIdealCoreId(), &owner_process)
.IsSuccess()); .IsSuccess());
@ -162,7 +165,7 @@ void KProcess::DecrementThreadCount() {
ASSERT(num_threads > 0); ASSERT(num_threads > 0);
if (const auto count = --num_threads; count == 0) { if (const auto count = --num_threads; count == 0) {
UNIMPLEMENTED_MSG("Process termination is not implemented!"); LOG_WARNING(Kernel, "Process termination is not fully implemented.");
} }
} }
@ -406,6 +409,9 @@ void KProcess::Finalize() {
resource_limit->Close(); resource_limit->Close();
} }
// Finalize the handle table and close any open handles.
handle_table.Finalize();
// Perform inherited finalization. // Perform inherited finalization.
KAutoObjectWithSlabHeapAndContainer<KProcess, KSynchronizationObject>::Finalize(); KAutoObjectWithSlabHeapAndContainer<KProcess, KSynchronizationObject>::Finalize();
} }

View file

@ -28,7 +28,10 @@ namespace Kernel {
KServerSession::KServerSession(KernelCore& kernel_) : KSynchronizationObject{kernel_} {} KServerSession::KServerSession(KernelCore& kernel_) : KSynchronizationObject{kernel_} {}
KServerSession::~KServerSession() {} KServerSession::~KServerSession() {
// Ensure that the global list tracking server sessions does not hold on to a reference.
kernel.UnregisterServerSession(this);
}
void KServerSession::Initialize(KSession* parent_session_, std::string&& name_, void KServerSession::Initialize(KSession* parent_session_, std::string&& name_,
std::shared_ptr<SessionRequestManager> manager_) { std::shared_ptr<SessionRequestManager> manager_) {

View file

@ -61,6 +61,7 @@ struct KernelCore::Impl {
void Initialize(KernelCore& kernel) { void Initialize(KernelCore& kernel) {
global_scheduler_context = std::make_unique<Kernel::GlobalSchedulerContext>(kernel); global_scheduler_context = std::make_unique<Kernel::GlobalSchedulerContext>(kernel);
global_handle_table = std::make_unique<Kernel::KHandleTable>(kernel); global_handle_table = std::make_unique<Kernel::KHandleTable>(kernel);
global_handle_table->Initialize(KHandleTable::MaxTableSize);
is_phantom_mode_for_singlecore = false; is_phantom_mode_for_singlecore = false;
@ -90,9 +91,39 @@ struct KernelCore::Impl {
} }
void Shutdown() { void Shutdown() {
// Shutdown all processes.
if (current_process) {
current_process->Finalize();
current_process->Close();
current_process = nullptr;
}
process_list.clear(); process_list.clear();
// Ensures all service threads gracefully shutdown // Close all open server ports.
std::unordered_set<KServerPort*> server_ports_;
{
std::lock_guard lk(server_ports_lock);
server_ports_ = server_ports;
server_ports.clear();
}
for (auto* server_port : server_ports_) {
server_port->Close();
}
// Close all open server sessions.
std::unordered_set<KServerSession*> server_sessions_;
{
std::lock_guard lk(server_sessions_lock);
server_sessions_ = server_sessions;
server_sessions.clear();
}
for (auto* server_session : server_sessions_) {
server_session->Close();
}
// Ensure that the object list container is finalized and properly shutdown.
object_list_container.Finalize();
// Ensures all service threads gracefully shutdown.
service_threads.clear(); service_threads.clear();
next_object_id = 0; next_object_id = 0;
@ -111,11 +142,7 @@ struct KernelCore::Impl {
cores.clear(); cores.clear();
if (current_process) { global_handle_table->Finalize();
current_process->Close();
current_process = nullptr;
}
global_handle_table.reset(); global_handle_table.reset();
preemption_event = nullptr; preemption_event = nullptr;
@ -142,6 +169,16 @@ struct KernelCore::Impl {
// Next host thead ID to use, 0-3 IDs represent core threads, >3 represent others // Next host thead ID to use, 0-3 IDs represent core threads, >3 represent others
next_host_thread_id = Core::Hardware::NUM_CPU_CORES; next_host_thread_id = Core::Hardware::NUM_CPU_CORES;
// Track kernel objects that were not freed on shutdown
{
std::lock_guard lk(registered_objects_lock);
if (registered_objects.size()) {
LOG_WARNING(Kernel, "{} kernel objects were dangling on shutdown!",
registered_objects.size());
registered_objects.clear();
}
}
} }
void InitializePhysicalCores() { void InitializePhysicalCores() {
@ -630,6 +667,21 @@ struct KernelCore::Impl {
user_slab_heap_size); user_slab_heap_size);
} }
KClientPort* CreateNamedServicePort(std::string name) {
auto search = service_interface_factory.find(name);
if (search == service_interface_factory.end()) {
UNIMPLEMENTED();
return {};
}
KClientPort* port = &search->second(system.ServiceManager(), system);
{
std::lock_guard lk(server_ports_lock);
server_ports.insert(&port->GetParent()->GetServerPort());
}
return port;
}
std::atomic<u32> next_object_id{0}; std::atomic<u32> next_object_id{0};
std::atomic<u64> next_kernel_process_id{KProcess::InitialKIPIDMin}; std::atomic<u64> next_kernel_process_id{KProcess::InitialKIPIDMin};
std::atomic<u64> next_user_process_id{KProcess::ProcessIDMin}; std::atomic<u64> next_user_process_id{KProcess::ProcessIDMin};
@ -656,6 +708,12 @@ struct KernelCore::Impl {
/// the ConnectToPort SVC. /// the ConnectToPort SVC.
std::unordered_map<std::string, ServiceInterfaceFactory> service_interface_factory; std::unordered_map<std::string, ServiceInterfaceFactory> service_interface_factory;
NamedPortTable named_ports; NamedPortTable named_ports;
std::unordered_set<KServerPort*> server_ports;
std::unordered_set<KServerSession*> server_sessions;
std::unordered_set<KAutoObject*> registered_objects;
std::mutex server_ports_lock;
std::mutex server_sessions_lock;
std::mutex registered_objects_lock;
std::unique_ptr<Core::ExclusiveMonitor> exclusive_monitor; std::unique_ptr<Core::ExclusiveMonitor> exclusive_monitor;
std::vector<Kernel::PhysicalCore> cores; std::vector<Kernel::PhysicalCore> cores;
@ -844,12 +902,27 @@ void KernelCore::RegisterNamedService(std::string name, ServiceInterfaceFactory&
} }
KClientPort* KernelCore::CreateNamedServicePort(std::string name) { KClientPort* KernelCore::CreateNamedServicePort(std::string name) {
auto search = impl->service_interface_factory.find(name); return impl->CreateNamedServicePort(std::move(name));
if (search == impl->service_interface_factory.end()) { }
UNIMPLEMENTED();
return {}; void KernelCore::RegisterServerSession(KServerSession* server_session) {
} std::lock_guard lk(impl->server_sessions_lock);
return &search->second(impl->system.ServiceManager(), impl->system); impl->server_sessions.insert(server_session);
}
void KernelCore::UnregisterServerSession(KServerSession* server_session) {
std::lock_guard lk(impl->server_sessions_lock);
impl->server_sessions.erase(server_session);
}
void KernelCore::RegisterKernelObject(KAutoObject* object) {
std::lock_guard lk(impl->registered_objects_lock);
impl->registered_objects.insert(object);
}
void KernelCore::UnregisterKernelObject(KAutoObject* object) {
std::lock_guard lk(impl->registered_objects_lock);
impl->registered_objects.erase(object);
} }
bool KernelCore::IsValidNamedPort(NamedPortTable::const_iterator port) const { bool KernelCore::IsValidNamedPort(NamedPortTable::const_iterator port) const {

View file

@ -45,6 +45,7 @@ class KPort;
class KProcess; class KProcess;
class KResourceLimit; class KResourceLimit;
class KScheduler; class KScheduler;
class KServerSession;
class KSession; class KSession;
class KSharedMemory; class KSharedMemory;
class KThread; class KThread;
@ -185,6 +186,22 @@ public:
/// Opens a port to a service previously registered with RegisterNamedService. /// Opens a port to a service previously registered with RegisterNamedService.
KClientPort* CreateNamedServicePort(std::string name); KClientPort* CreateNamedServicePort(std::string name);
/// Registers a server session with the gobal emulation state, to be freed on shutdown. This is
/// necessary because we do not emulate processes for HLE sessions.
void RegisterServerSession(KServerSession* server_session);
/// Unregisters a server session previously registered with RegisterServerSession when it was
/// destroyed during the current emulation session.
void UnregisterServerSession(KServerSession* server_session);
/// Registers all kernel objects with the global emulation state, this is purely for tracking
/// leaks after emulation has been shutdown.
void RegisterKernelObject(KAutoObject* object);
/// Unregisters a kernel object previously registered with RegisterKernelObject when it was
/// destroyed during the current emulation session.
void UnregisterKernelObject(KAutoObject* object);
/// Determines whether or not the given port is a valid named port. /// Determines whether or not the given port is a valid named port.
bool IsValidNamedPort(NamedPortTable::const_iterator port) const; bool IsValidNamedPort(NamedPortTable::const_iterator port) const;

View file

@ -298,6 +298,7 @@ static ResultCode ConnectToNamedPort(Core::System& system, Handle* out, VAddr po
// Create a session. // Create a session.
KClientSession* session{}; KClientSession* session{};
R_TRY(port->CreateSession(std::addressof(session))); R_TRY(port->CreateSession(std::addressof(session)));
port->Close();
// Register the session in the table, close the extra reference. // Register the session in the table, close the extra reference.
handle_table.Register(*out, session); handle_table.Register(*out, session);

View file

@ -48,7 +48,8 @@ ProfileManager::ProfileManager() {
CreateNewUser(UUID::Generate(), "yuzu"); CreateNewUser(UUID::Generate(), "yuzu");
} }
auto current = std::clamp<int>(Settings::values.current_user, 0, MAX_USERS - 1); auto current =
std::clamp<int>(static_cast<s32>(Settings::values.current_user), 0, MAX_USERS - 1);
// If user index don't exist. Load the first user and change the active user // If user index don't exist. Load the first user and change the active user
if (!UserExistsIndex(current)) { if (!UserExistsIndex(current)) {

View file

@ -1443,7 +1443,7 @@ void IApplicationFunctions::PopLaunchParameter(Kernel::HLERequestContext& ctx) {
params.is_account_selected = 1; params.is_account_selected = 1;
Account::ProfileManager profile_manager{}; Account::ProfileManager profile_manager{};
const auto uuid = profile_manager.GetUser(Settings::values.current_user); const auto uuid = profile_manager.GetUser(static_cast<s32>(Settings::values.current_user));
ASSERT(uuid); ASSERT(uuid);
params.current_user = uuid->uuid; params.current_user = uuid->uuid;

View file

@ -579,7 +579,7 @@ void Module::Interface::CreateDeliveryCacheStorageServiceWithApplicationId(
std::unique_ptr<Backend> CreateBackendFromSettings([[maybe_unused]] Core::System& system, std::unique_ptr<Backend> CreateBackendFromSettings([[maybe_unused]] Core::System& system,
DirectoryGetter getter) { DirectoryGetter getter) {
#ifdef YUZU_ENABLE_BOXCAT #ifdef YUZU_ENABLE_BOXCAT
if (Settings::values.bcat_backend == "boxcat") { if (Settings::values.bcat_backend.GetValue() == "boxcat") {
return std::make_unique<Boxcat>(system.GetAppletManager(), std::move(getter)); return std::make_unique<Boxcat>(system.GetAppletManager(), std::move(getter));
} }
#endif #endif

View file

@ -18,6 +18,7 @@
#include "core/hle/kernel/k_writable_event.h" #include "core/hle/kernel/k_writable_event.h"
#include "core/hle/kernel/kernel.h" #include "core/hle/kernel/kernel.h"
#include "core/hle/service/hid/controllers/npad.h" #include "core/hle/service/hid/controllers/npad.h"
#include "core/hle/service/kernel_helpers.h"
namespace Service::HID { namespace Service::HID {
constexpr s32 HID_JOYSTICK_MAX = 0x7fff; constexpr s32 HID_JOYSTICK_MAX = 0x7fff;
@ -147,7 +148,9 @@ bool Controller_NPad::IsDeviceHandleValid(const DeviceHandle& device_handle) {
device_handle.device_index < DeviceIndex::MaxDeviceIndex; device_handle.device_index < DeviceIndex::MaxDeviceIndex;
} }
Controller_NPad::Controller_NPad(Core::System& system_) : ControllerBase{system_} { Controller_NPad::Controller_NPad(Core::System& system_,
KernelHelpers::ServiceContext& service_context_)
: ControllerBase{system_}, service_context{service_context_} {
latest_vibration_values.fill({DEFAULT_VIBRATION_VALUE, DEFAULT_VIBRATION_VALUE}); latest_vibration_values.fill({DEFAULT_VIBRATION_VALUE, DEFAULT_VIBRATION_VALUE});
} }
@ -253,8 +256,8 @@ void Controller_NPad::InitNewlyAddedController(std::size_t controller_idx) {
void Controller_NPad::OnInit() { void Controller_NPad::OnInit() {
auto& kernel = system.Kernel(); auto& kernel = system.Kernel();
for (std::size_t i = 0; i < styleset_changed_events.size(); ++i) { for (std::size_t i = 0; i < styleset_changed_events.size(); ++i) {
styleset_changed_events[i] = Kernel::KEvent::Create(kernel); styleset_changed_events[i] =
styleset_changed_events[i]->Initialize(fmt::format("npad:NpadStyleSetChanged_{}", i)); service_context.CreateEvent(fmt::format("npad:NpadStyleSetChanged_{}", i));
} }
if (!IsControllerActivated()) { if (!IsControllerActivated()) {
@ -344,8 +347,7 @@ void Controller_NPad::OnRelease() {
} }
for (std::size_t i = 0; i < styleset_changed_events.size(); ++i) { for (std::size_t i = 0; i < styleset_changed_events.size(); ++i) {
styleset_changed_events[i]->Close(); service_context.CloseEvent(styleset_changed_events[i]);
styleset_changed_events[i] = nullptr;
} }
} }

View file

@ -20,6 +20,10 @@ class KEvent;
class KReadableEvent; class KReadableEvent;
} // namespace Kernel } // namespace Kernel
namespace Service::KernelHelpers {
class ServiceContext;
}
namespace Service::HID { namespace Service::HID {
constexpr u32 NPAD_HANDHELD = 32; constexpr u32 NPAD_HANDHELD = 32;
@ -27,7 +31,8 @@ constexpr u32 NPAD_UNKNOWN = 16; // TODO(ogniK): What is this?
class Controller_NPad final : public ControllerBase { class Controller_NPad final : public ControllerBase {
public: public:
explicit Controller_NPad(Core::System& system_); explicit Controller_NPad(Core::System& system_,
KernelHelpers::ServiceContext& service_context_);
~Controller_NPad() override; ~Controller_NPad() override;
// Called when the controller is initialized // Called when the controller is initialized
@ -566,6 +571,7 @@ private:
std::array<std::unique_ptr<Input::MotionDevice>, Settings::NativeMotion::NUM_MOTIONS_HID>, std::array<std::unique_ptr<Input::MotionDevice>, Settings::NativeMotion::NUM_MOTIONS_HID>,
10>; 10>;
KernelHelpers::ServiceContext& service_context;
std::mutex mutex; std::mutex mutex;
ButtonArray buttons; ButtonArray buttons;
StickArray sticks; StickArray sticks;

View file

@ -46,8 +46,9 @@ constexpr auto pad_update_ns = std::chrono::nanoseconds{1000 * 1000}; //
constexpr auto motion_update_ns = std::chrono::nanoseconds{15 * 1000 * 1000}; // (15ms, 66.666Hz) constexpr auto motion_update_ns = std::chrono::nanoseconds{15 * 1000 * 1000}; // (15ms, 66.666Hz)
constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000; constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000;
IAppletResource::IAppletResource(Core::System& system_) IAppletResource::IAppletResource(Core::System& system_,
: ServiceFramework{system_, "IAppletResource"} { KernelHelpers::ServiceContext& service_context_)
: ServiceFramework{system_, "IAppletResource"}, service_context{service_context_} {
static const FunctionInfo functions[] = { static const FunctionInfo functions[] = {
{0, &IAppletResource::GetSharedMemoryHandle, "GetSharedMemoryHandle"}, {0, &IAppletResource::GetSharedMemoryHandle, "GetSharedMemoryHandle"},
}; };
@ -63,7 +64,7 @@ IAppletResource::IAppletResource(Core::System& system_)
MakeController<Controller_Stubbed>(HidController::CaptureButton); MakeController<Controller_Stubbed>(HidController::CaptureButton);
MakeController<Controller_Stubbed>(HidController::InputDetector); MakeController<Controller_Stubbed>(HidController::InputDetector);
MakeController<Controller_Stubbed>(HidController::UniquePad); MakeController<Controller_Stubbed>(HidController::UniquePad);
MakeController<Controller_NPad>(HidController::NPad); MakeControllerWithServiceContext<Controller_NPad>(HidController::NPad);
MakeController<Controller_Gesture>(HidController::Gesture); MakeController<Controller_Gesture>(HidController::Gesture);
MakeController<Controller_ConsoleSixAxis>(HidController::ConsoleSixAxisSensor); MakeController<Controller_ConsoleSixAxis>(HidController::ConsoleSixAxisSensor);
@ -191,13 +192,14 @@ private:
std::shared_ptr<IAppletResource> Hid::GetAppletResource() { std::shared_ptr<IAppletResource> Hid::GetAppletResource() {
if (applet_resource == nullptr) { if (applet_resource == nullptr) {
applet_resource = std::make_shared<IAppletResource>(system); applet_resource = std::make_shared<IAppletResource>(system, service_context);
} }
return applet_resource; return applet_resource;
} }
Hid::Hid(Core::System& system_) : ServiceFramework{system_, "hid"} { Hid::Hid(Core::System& system_)
: ServiceFramework{system_, "hid"}, service_context{system_, service_name} {
// clang-format off // clang-format off
static const FunctionInfo functions[] = { static const FunctionInfo functions[] = {
{0, &Hid::CreateAppletResource, "CreateAppletResource"}, {0, &Hid::CreateAppletResource, "CreateAppletResource"},
@ -347,7 +349,7 @@ void Hid::CreateAppletResource(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
if (applet_resource == nullptr) { if (applet_resource == nullptr) {
applet_resource = std::make_shared<IAppletResource>(system); applet_resource = std::make_shared<IAppletResource>(system, service_context);
} }
IPC::ResponseBuilder rb{ctx, 2, 0, 1}; IPC::ResponseBuilder rb{ctx, 2, 0, 1};

View file

@ -7,6 +7,7 @@
#include <chrono> #include <chrono>
#include "core/hle/service/hid/controllers/controller_base.h" #include "core/hle/service/hid/controllers/controller_base.h"
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/service.h" #include "core/hle/service/service.h"
namespace Core::Timing { namespace Core::Timing {
@ -39,7 +40,8 @@ enum class HidController : std::size_t {
class IAppletResource final : public ServiceFramework<IAppletResource> { class IAppletResource final : public ServiceFramework<IAppletResource> {
public: public:
explicit IAppletResource(Core::System& system_); explicit IAppletResource(Core::System& system_,
KernelHelpers::ServiceContext& service_context_);
~IAppletResource() override; ~IAppletResource() override;
void ActivateController(HidController controller); void ActivateController(HidController controller);
@ -60,11 +62,18 @@ private:
void MakeController(HidController controller) { void MakeController(HidController controller) {
controllers[static_cast<std::size_t>(controller)] = std::make_unique<T>(system); controllers[static_cast<std::size_t>(controller)] = std::make_unique<T>(system);
} }
template <typename T>
void MakeControllerWithServiceContext(HidController controller) {
controllers[static_cast<std::size_t>(controller)] =
std::make_unique<T>(system, service_context);
}
void GetSharedMemoryHandle(Kernel::HLERequestContext& ctx); void GetSharedMemoryHandle(Kernel::HLERequestContext& ctx);
void UpdateControllers(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); void UpdateControllers(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
void UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); void UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
KernelHelpers::ServiceContext& service_context;
std::shared_ptr<Core::Timing::EventType> pad_update_event; std::shared_ptr<Core::Timing::EventType> pad_update_event;
std::shared_ptr<Core::Timing::EventType> motion_update_event; std::shared_ptr<Core::Timing::EventType> motion_update_event;
@ -176,6 +185,8 @@ private:
static_assert(sizeof(VibrationDeviceInfo) == 0x8, "VibrationDeviceInfo has incorrect size."); static_assert(sizeof(VibrationDeviceInfo) == 0x8, "VibrationDeviceInfo has incorrect size.");
std::shared_ptr<IAppletResource> applet_resource; std::shared_ptr<IAppletResource> applet_resource;
KernelHelpers::ServiceContext service_context;
}; };
/// Reload input devices. Used when input configuration changed /// Reload input devices. Used when input configuration changed

View file

@ -0,0 +1,64 @@
// Copyright 2021 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/core.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_readable_event.h"
#include "core/hle/kernel/k_resource_limit.h"
#include "core/hle/kernel/k_scoped_resource_reservation.h"
#include "core/hle/kernel/k_writable_event.h"
#include "core/hle/service/kernel_helpers.h"
namespace Service::KernelHelpers {
ServiceContext::ServiceContext(Core::System& system_, std::string name_)
: kernel(system_.Kernel()) {
process = Kernel::KProcess::Create(kernel);
ASSERT(Kernel::KProcess::Initialize(process, system_, std::move(name_),
Kernel::KProcess::ProcessType::Userland)
.IsSuccess());
}
ServiceContext::~ServiceContext() {
process->Close();
process = nullptr;
}
Kernel::KEvent* ServiceContext::CreateEvent(std::string&& name) {
// Reserve a new event from the process resource limit
Kernel::KScopedResourceReservation event_reservation(process,
Kernel::LimitableResource::Events);
if (!event_reservation.Succeeded()) {
LOG_CRITICAL(Service, "Resource limit reached!");
return {};
}
// Create a new event.
auto* event = Kernel::KEvent::Create(kernel);
if (!event) {
LOG_CRITICAL(Service, "Unable to create event!");
return {};
}
// Initialize the event.
event->Initialize(std::move(name));
// Commit the thread reservation.
event_reservation.Commit();
// Register the event.
Kernel::KEvent::Register(kernel, event);
return event;
}
void ServiceContext::CloseEvent(Kernel::KEvent* event) {
event->GetReadableEvent().Close();
event->GetWritableEvent().Close();
}
} // namespace Service::KernelHelpers

View file

@ -0,0 +1,35 @@
// Copyright 2021 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <string>
namespace Core {
class System;
}
namespace Kernel {
class KernelCore;
class KEvent;
class KProcess;
} // namespace Kernel
namespace Service::KernelHelpers {
class ServiceContext {
public:
ServiceContext(Core::System& system_, std::string name_);
~ServiceContext();
Kernel::KEvent* CreateEvent(std::string&& name);
void CloseEvent(Kernel::KEvent* event);
private:
Kernel::KernelCore& kernel;
Kernel::KProcess* process{};
};
} // namespace Service::KernelHelpers

View file

@ -179,7 +179,7 @@ private:
IPC::ResponseBuilder rb{ctx, 3}; IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
if (Settings::values.bcat_backend == "none") { if (Settings::values.bcat_backend.GetValue() == "none") {
rb.PushEnum(RequestState::NotSubmitted); rb.PushEnum(RequestState::NotSubmitted);
} else { } else {
rb.PushEnum(RequestState::Connected); rb.PushEnum(RequestState::Connected);
@ -384,7 +384,7 @@ private:
IPC::ResponseBuilder rb{ctx, 3}; IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
if (Settings::values.bcat_backend == "none") { if (Settings::values.bcat_backend.GetValue() == "none") {
rb.Push<u8>(0); rb.Push<u8>(0);
} else { } else {
rb.Push<u8>(1); rb.Push<u8>(1);
@ -395,7 +395,7 @@ private:
IPC::ResponseBuilder rb{ctx, 3}; IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
if (Settings::values.bcat_backend == "none") { if (Settings::values.bcat_backend.GetValue() == "none") {
rb.Push<u8>(0); rb.Push<u8>(0);
} else { } else {
rb.Push<u8>(1); rb.Push<u8>(1);

View file

@ -39,11 +39,12 @@ void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger
nvflinger.SetNVDrvInstance(module_); nvflinger.SetNVDrvInstance(module_);
} }
Module::Module(Core::System& system) : syncpoint_manager{system.GPU()} { Module::Module(Core::System& system)
: syncpoint_manager{system.GPU()}, service_context{system, "nvdrv"} {
auto& kernel = system.Kernel(); auto& kernel = system.Kernel();
for (u32 i = 0; i < MaxNvEvents; i++) { for (u32 i = 0; i < MaxNvEvents; i++) {
events_interface.events[i].event = Kernel::KEvent::Create(kernel); events_interface.events[i].event =
events_interface.events[i].event->Initialize(fmt::format("NVDRV::NvEvent_{}", i)); service_context.CreateEvent(fmt::format("NVDRV::NvEvent_{}", i));
events_interface.status[i] = EventState::Free; events_interface.status[i] = EventState::Free;
events_interface.registered[i] = false; events_interface.registered[i] = false;
} }
@ -65,8 +66,7 @@ Module::Module(Core::System& system) : syncpoint_manager{system.GPU()} {
Module::~Module() { Module::~Module() {
for (u32 i = 0; i < MaxNvEvents; i++) { for (u32 i = 0; i < MaxNvEvents; i++) {
events_interface.events[i].event->Close(); service_context.CloseEvent(events_interface.events[i].event);
events_interface.events[i].event = nullptr;
} }
} }

View file

@ -9,6 +9,7 @@
#include <vector> #include <vector>
#include "common/common_types.h" #include "common/common_types.h"
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/nvdrv/nvdata.h" #include "core/hle/service/nvdrv/nvdata.h"
#include "core/hle/service/nvdrv/syncpoint_manager.h" #include "core/hle/service/nvdrv/syncpoint_manager.h"
#include "core/hle/service/service.h" #include "core/hle/service/service.h"
@ -154,6 +155,8 @@ private:
std::unordered_map<std::string, std::shared_ptr<Devices::nvdevice>> devices; std::unordered_map<std::string, std::shared_ptr<Devices::nvdevice>> devices;
EventInterface events_interface; EventInterface events_interface;
KernelHelpers::ServiceContext service_context;
}; };
/// Registers all NVDRV services with the specified service manager. /// Registers all NVDRV services with the specified service manager.

View file

@ -104,23 +104,22 @@ ServiceFrameworkBase::~ServiceFrameworkBase() {
void ServiceFrameworkBase::InstallAsService(SM::ServiceManager& service_manager) { void ServiceFrameworkBase::InstallAsService(SM::ServiceManager& service_manager) {
const auto guard = LockService(); const auto guard = LockService();
ASSERT(!port_installed); ASSERT(!service_registered);
auto port = service_manager.RegisterService(service_name, max_sessions).Unwrap(); service_manager.RegisterService(service_name, max_sessions, shared_from_this());
port->SetSessionHandler(shared_from_this()); service_registered = true;
port_installed = true;
} }
Kernel::KClientPort& ServiceFrameworkBase::CreatePort() { Kernel::KClientPort& ServiceFrameworkBase::CreatePort() {
const auto guard = LockService(); const auto guard = LockService();
ASSERT(!port_installed); ASSERT(!service_registered);
auto* port = Kernel::KPort::Create(kernel); auto* port = Kernel::KPort::Create(kernel);
port->Initialize(max_sessions, false, service_name); port->Initialize(max_sessions, false, service_name);
port->GetServerPort().SetSessionHandler(shared_from_this()); port->GetServerPort().SetSessionHandler(shared_from_this());
port_installed = true; service_registered = true;
return port->GetClientPort(); return port->GetClientPort();
} }

View file

@ -96,6 +96,9 @@ protected:
/// System context that the service operates under. /// System context that the service operates under.
Core::System& system; Core::System& system;
/// Identifier string used to connect to the service.
std::string service_name;
private: private:
template <typename T> template <typename T>
friend class ServiceFramework; friend class ServiceFramework;
@ -117,14 +120,12 @@ private:
void RegisterHandlersBaseTipc(const FunctionInfoBase* functions, std::size_t n); void RegisterHandlersBaseTipc(const FunctionInfoBase* functions, std::size_t n);
void ReportUnimplementedFunction(Kernel::HLERequestContext& ctx, const FunctionInfoBase* info); void ReportUnimplementedFunction(Kernel::HLERequestContext& ctx, const FunctionInfoBase* info);
/// Identifier string used to connect to the service.
std::string service_name;
/// Maximum number of concurrent sessions that this service can handle. /// Maximum number of concurrent sessions that this service can handle.
u32 max_sessions; u32 max_sessions;
/// Flag to store if a port was already create/installed to detect multiple install attempts, /// Flag to store if a port was already create/installed to detect multiple install attempts,
/// which is not supported. /// which is not supported.
bool port_installed = false; bool service_registered = false;
/// Function used to safely up-cast pointers to the derived class before invoking a handler. /// Function used to safely up-cast pointers to the derived class before invoking a handler.
InvokerFn* handler_invoker; InvokerFn* handler_invoker;

View file

@ -160,7 +160,7 @@ void SET::GetQuestFlag(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 3}; IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.Push(static_cast<u32>(Settings::values.quest_flag)); rb.Push(static_cast<u32>(Settings::values.quest_flag.GetValue()));
} }
void SET::GetLanguageCode(Kernel::HLERequestContext& ctx) { void SET::GetLanguageCode(Kernel::HLERequestContext& ctx) {

View file

@ -4,6 +4,7 @@
#include <tuple> #include <tuple>
#include "common/assert.h" #include "common/assert.h"
#include "common/scope_exit.h"
#include "core/core.h" #include "core/core.h"
#include "core/hle/ipc_helpers.h" #include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/k_client_port.h" #include "core/hle/kernel/k_client_port.h"
@ -40,17 +41,13 @@ static ResultCode ValidateServiceName(const std::string& name) {
} }
Kernel::KClientPort& ServiceManager::InterfaceFactory(ServiceManager& self, Core::System& system) { Kernel::KClientPort& ServiceManager::InterfaceFactory(ServiceManager& self, Core::System& system) {
ASSERT(self.sm_interface.expired()); self.sm_interface = std::make_shared<SM>(self, system);
auto sm = std::make_shared<SM>(self, system);
self.sm_interface = sm;
self.controller_interface = std::make_unique<Controller>(system); self.controller_interface = std::make_unique<Controller>(system);
return self.sm_interface->CreatePort();
return sm->CreatePort();
} }
ResultVal<Kernel::KServerPort*> ServiceManager::RegisterService(std::string name, ResultCode ServiceManager::RegisterService(std::string name, u32 max_sessions,
u32 max_sessions) { Kernel::SessionRequestHandlerPtr handler) {
CASCADE_CODE(ValidateServiceName(name)); CASCADE_CODE(ValidateServiceName(name));
@ -59,12 +56,9 @@ ResultVal<Kernel::KServerPort*> ServiceManager::RegisterService(std::string name
return ERR_ALREADY_REGISTERED; return ERR_ALREADY_REGISTERED;
} }
auto* port = Kernel::KPort::Create(kernel); registered_services.emplace(std::move(name), handler);
port->Initialize(max_sessions, false, name);
registered_services.emplace(std::move(name), port); return ResultSuccess;
return MakeResult(&port->GetServerPort());
} }
ResultCode ServiceManager::UnregisterService(const std::string& name) { ResultCode ServiceManager::UnregisterService(const std::string& name) {
@ -76,14 +70,11 @@ ResultCode ServiceManager::UnregisterService(const std::string& name) {
return ERR_SERVICE_NOT_REGISTERED; return ERR_SERVICE_NOT_REGISTERED;
} }
iter->second->Close();
registered_services.erase(iter); registered_services.erase(iter);
return ResultSuccess; return ResultSuccess;
} }
ResultVal<Kernel::KPort*> ServiceManager::GetServicePort(const std::string& name) { ResultVal<Kernel::KPort*> ServiceManager::GetServicePort(const std::string& name) {
CASCADE_CODE(ValidateServiceName(name)); CASCADE_CODE(ValidateServiceName(name));
auto it = registered_services.find(name); auto it = registered_services.find(name);
if (it == registered_services.end()) { if (it == registered_services.end()) {
@ -91,10 +82,13 @@ ResultVal<Kernel::KPort*> ServiceManager::GetServicePort(const std::string& name
return ERR_SERVICE_NOT_REGISTERED; return ERR_SERVICE_NOT_REGISTERED;
} }
return MakeResult(it->second); auto* port = Kernel::KPort::Create(kernel);
} port->Initialize(ServerSessionCountMax, false, name);
auto handler = it->second;
port->GetServerPort().SetSessionHandler(std::move(handler));
SM::~SM() = default; return MakeResult(port);
}
/** /**
* SM::Initialize service function * SM::Initialize service function
@ -156,11 +150,15 @@ ResultVal<Kernel::KClientSession*> SM::GetServiceImpl(Kernel::HLERequestContext&
LOG_ERROR(Service_SM, "called service={} -> error 0x{:08X}", name, port_result.Code().raw); LOG_ERROR(Service_SM, "called service={} -> error 0x{:08X}", name, port_result.Code().raw);
return port_result.Code(); return port_result.Code();
} }
auto& port = port_result.Unwrap()->GetClientPort(); auto& port = port_result.Unwrap();
SCOPE_EXIT({ port->GetClientPort().Close(); });
server_ports.emplace_back(&port->GetServerPort());
// Create a new session. // Create a new session.
Kernel::KClientSession* session{}; Kernel::KClientSession* session{};
if (const auto result = port.CreateSession(std::addressof(session)); result.IsError()) { if (const auto result = port->GetClientPort().CreateSession(std::addressof(session));
result.IsError()) {
LOG_ERROR(Service_SM, "called service={} -> error 0x{:08X}", name, result.raw); LOG_ERROR(Service_SM, "called service={} -> error 0x{:08X}", name, result.raw);
return result; return result;
} }
@ -180,20 +178,21 @@ void SM::RegisterService(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_SM, "called with name={}, max_session_count={}, is_light={}", name, LOG_DEBUG(Service_SM, "called with name={}, max_session_count={}, is_light={}", name,
max_session_count, is_light); max_session_count, is_light);
auto handle = service_manager.RegisterService(name, max_session_count); if (const auto result = service_manager.RegisterService(name, max_session_count, nullptr);
if (handle.Failed()) { result.IsError()) {
LOG_ERROR(Service_SM, "failed to register service with error_code={:08X}", LOG_ERROR(Service_SM, "failed to register service with error_code={:08X}", result.raw);
handle.Code().raw);
IPC::ResponseBuilder rb{ctx, 2}; IPC::ResponseBuilder rb{ctx, 2};
rb.Push(handle.Code()); rb.Push(result);
return; return;
} }
IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles}; auto* port = Kernel::KPort::Create(kernel);
rb.Push(handle.Code()); port->Initialize(ServerSessionCountMax, is_light, name);
SCOPE_EXIT({ port->GetClientPort().Close(); });
auto server_port = handle.Unwrap(); IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles};
rb.PushMoveObjects(server_port); rb.Push(ResultSuccess);
rb.PushMoveObjects(port->GetServerPort());
} }
void SM::UnregisterService(Kernel::HLERequestContext& ctx) { void SM::UnregisterService(Kernel::HLERequestContext& ctx) {
@ -225,4 +224,10 @@ SM::SM(ServiceManager& service_manager_, Core::System& system_)
}); });
} }
SM::~SM() {
for (auto& server_port : server_ports) {
server_port->Close();
}
}
} // namespace Service::SM } // namespace Service::SM

View file

@ -49,6 +49,7 @@ private:
ServiceManager& service_manager; ServiceManager& service_manager;
bool is_initialized{}; bool is_initialized{};
Kernel::KernelCore& kernel; Kernel::KernelCore& kernel;
std::vector<Kernel::KServerPort*> server_ports;
}; };
class ServiceManager { class ServiceManager {
@ -58,7 +59,8 @@ public:
explicit ServiceManager(Kernel::KernelCore& kernel_); explicit ServiceManager(Kernel::KernelCore& kernel_);
~ServiceManager(); ~ServiceManager();
ResultVal<Kernel::KServerPort*> RegisterService(std::string name, u32 max_sessions); ResultCode RegisterService(std::string name, u32 max_sessions,
Kernel::SessionRequestHandlerPtr handler);
ResultCode UnregisterService(const std::string& name); ResultCode UnregisterService(const std::string& name);
ResultVal<Kernel::KPort*> GetServicePort(const std::string& name); ResultVal<Kernel::KPort*> GetServicePort(const std::string& name);
@ -69,21 +71,17 @@ public:
LOG_DEBUG(Service, "Can't find service: {}", service_name); LOG_DEBUG(Service, "Can't find service: {}", service_name);
return nullptr; return nullptr;
} }
auto* port = service->second; return std::static_pointer_cast<T>(service->second);
if (port == nullptr) {
return nullptr;
}
return std::static_pointer_cast<T>(port->GetServerPort().GetSessionRequestHandler());
} }
void InvokeControlRequest(Kernel::HLERequestContext& context); void InvokeControlRequest(Kernel::HLERequestContext& context);
private: private:
std::weak_ptr<SM> sm_interface; std::shared_ptr<SM> sm_interface;
std::unique_ptr<Controller> controller_interface; std::unique_ptr<Controller> controller_interface;
/// Map of registered services, retrieved using GetServicePort. /// Map of registered services, retrieved using GetServicePort.
std::unordered_map<std::string, Kernel::KPort*> registered_services; std::unordered_map<std::string, Kernel::SessionRequestHandlerPtr> registered_services;
/// Kernel context /// Kernel context
Kernel::KernelCore& kernel; Kernel::KernelCore& kernel;

View file

@ -155,8 +155,8 @@ static bool LoadNroImpl(Kernel::KProcess& process, const std::vector<u8>& data)
codeset.segments[i].size = PageAlignSize(nro_header.segments[i].size); codeset.segments[i].size = PageAlignSize(nro_header.segments[i].size);
} }
if (!Settings::values.program_args.empty()) { if (!Settings::values.program_args.GetValue().empty()) {
const auto arg_data = Settings::values.program_args; const auto arg_data = Settings::values.program_args.GetValue();
codeset.DataSegment().size += NSO_ARGUMENT_DATA_ALLOCATION_SIZE; codeset.DataSegment().size += NSO_ARGUMENT_DATA_ALLOCATION_SIZE;
NSOArgumentHeader args_header{ NSOArgumentHeader args_header{
NSO_ARGUMENT_DATA_ALLOCATION_SIZE, static_cast<u32_le>(arg_data.size()), {}}; NSO_ARGUMENT_DATA_ALLOCATION_SIZE, static_cast<u32_le>(arg_data.size()), {}};

View file

@ -104,8 +104,8 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::KProcess& process, Core::
codeset.segments[i].size = nso_header.segments[i].size; codeset.segments[i].size = nso_header.segments[i].size;
} }
if (should_pass_arguments && !Settings::values.program_args.empty()) { if (should_pass_arguments && !Settings::values.program_args.GetValue().empty()) {
const auto arg_data{Settings::values.program_args}; const auto arg_data{Settings::values.program_args.GetValue()};
codeset.DataSegment().size += NSO_ARGUMENT_DATA_ALLOCATION_SIZE; codeset.DataSegment().size += NSO_ARGUMENT_DATA_ALLOCATION_SIZE;
NSOArgumentHeader args_header{ NSOArgumentHeader args_header{

View file

@ -397,7 +397,7 @@ void Reporter::ClearFSAccessLog() const {
} }
bool Reporter::IsReportingEnabled() const { bool Reporter::IsReportingEnabled() const {
return Settings::values.reporting_services; return Settings::values.reporting_services.GetValue();
} }
} // namespace Core } // namespace Core

View file

@ -135,7 +135,7 @@ u64 RegenerateTelemetryId() {
bool VerifyLogin(const std::string& username, const std::string& token) { bool VerifyLogin(const std::string& username, const std::string& token) {
#ifdef ENABLE_WEB_SERVICE #ifdef ENABLE_WEB_SERVICE
return WebService::VerifyLogin(Settings::values.web_api_url, username, token); return WebService::VerifyLogin(Settings::values.web_api_url.GetValue(), username, token);
#else #else
return false; return false;
#endif #endif
@ -152,7 +152,8 @@ TelemetrySession::~TelemetrySession() {
#ifdef ENABLE_WEB_SERVICE #ifdef ENABLE_WEB_SERVICE
auto backend = std::make_unique<WebService::TelemetryJson>( auto backend = std::make_unique<WebService::TelemetryJson>(
Settings::values.web_api_url, Settings::values.yuzu_username, Settings::values.yuzu_token); Settings::values.web_api_url.GetValue(), Settings::values.yuzu_username.GetValue(),
Settings::values.yuzu_token.GetValue());
#else #else
auto backend = std::make_unique<Telemetry::NullVisitor>(); auto backend = std::make_unique<Telemetry::NullVisitor>();
#endif #endif
@ -212,7 +213,7 @@ void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader,
// Log user configuration information // Log user configuration information
constexpr auto field_type = Telemetry::FieldType::UserConfig; constexpr auto field_type = Telemetry::FieldType::UserConfig;
AddField(field_type, "Audio_SinkId", Settings::values.sink_id); AddField(field_type, "Audio_SinkId", Settings::values.sink_id.GetValue());
AddField(field_type, "Audio_EnableAudioStretching", AddField(field_type, "Audio_EnableAudioStretching",
Settings::values.enable_audio_stretching.GetValue()); Settings::values.enable_audio_stretching.GetValue());
AddField(field_type, "Core_UseMultiCore", Settings::values.use_multi_core.GetValue()); AddField(field_type, "Core_UseMultiCore", Settings::values.use_multi_core.GetValue());
@ -242,7 +243,8 @@ void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader,
bool TelemetrySession::SubmitTestcase() { bool TelemetrySession::SubmitTestcase() {
#ifdef ENABLE_WEB_SERVICE #ifdef ENABLE_WEB_SERVICE
auto backend = std::make_unique<WebService::TelemetryJson>( auto backend = std::make_unique<WebService::TelemetryJson>(
Settings::values.web_api_url, Settings::values.yuzu_username, Settings::values.yuzu_token); Settings::values.web_api_url.GetValue(), Settings::values.yuzu_username.GetValue(),
Settings::values.yuzu_token.GetValue());
field_collection.Accept(*backend); field_collection.Accept(*backend);
return backend->SubmitTestcase(); return backend->SubmitTestcase();
#else #else

View file

@ -84,7 +84,8 @@ public:
std::lock_guard lock{mutex}; std::lock_guard lock{mutex};
const auto axis_value = const auto axis_value =
static_cast<float>(mouse_input->GetMouseState(button).axis.at(axis)); static_cast<float>(mouse_input->GetMouseState(button).axis.at(axis));
return axis_value * Settings::values.mouse_panning_sensitivity / (100.0f * range); return axis_value * Settings::values.mouse_panning_sensitivity.GetValue() /
(100.0f * range);
} }
std::pair<float, float> GetAnalog(u32 analog_axis_x, u32 analog_axis_y) const { std::pair<float, float> GetAnalog(u32 analog_axis_x, u32 analog_axis_y) const {

View file

@ -13,7 +13,7 @@ class TouchFromButtonDevice final : public Input::TouchDevice {
public: public:
TouchFromButtonDevice() { TouchFromButtonDevice() {
const auto button_index = const auto button_index =
static_cast<std::size_t>(Settings::values.touch_from_button_map_index); static_cast<u64>(Settings::values.touch_from_button_map_index.GetValue());
const auto& buttons = Settings::values.touch_from_button_maps[button_index].buttons; const auto& buttons = Settings::values.touch_from_button_maps[button_index].buttons;
for (const auto& config_entry : buttons) { for (const auto& config_entry : buttons) {

View file

@ -201,7 +201,7 @@ bool Client::DeviceConnected(std::size_t pad) const {
void Client::ReloadSockets() { void Client::ReloadSockets() {
Reset(); Reset();
std::stringstream servers_ss(Settings::values.udp_input_servers); std::stringstream servers_ss(static_cast<std::string>(Settings::values.udp_input_servers));
std::string server_token; std::string server_token;
std::size_t client = 0; std::size_t client = 0;
while (std::getline(servers_ss, server_token, ',')) { while (std::getline(servers_ss, server_token, ',')) {
@ -370,7 +370,7 @@ std::optional<std::size_t> Client::GetUnusedFingerID() const {
void Client::UpdateTouchInput(Response::TouchPad& touch_pad, std::size_t client, std::size_t id) { void Client::UpdateTouchInput(Response::TouchPad& touch_pad, std::size_t client, std::size_t id) {
// TODO: Use custom calibration per device // TODO: Use custom calibration per device
const Common::ParamPackage touch_param(Settings::values.touch_device); const Common::ParamPackage touch_param(Settings::values.touch_device.GetValue());
const u16 min_x = static_cast<u16>(touch_param.Get("min_x", 100)); const u16 min_x = static_cast<u16>(touch_param.Get("min_x", 100));
const u16 min_y = static_cast<u16>(touch_param.Get("min_y", 50)); const u16 min_y = static_cast<u16>(touch_param.Get("min_y", 50));
const u16 max_x = static_cast<u16>(touch_param.Get("max_x", 1800)); const u16 max_x = static_cast<u16>(touch_param.Get("max_x", 1800));

View file

@ -103,8 +103,7 @@ void CDmaPusher::ExecuteCommand(u32 state_offset, u32 data) {
case ThiMethod::SetMethod1: case ThiMethod::SetMethod1:
LOG_DEBUG(Service_NVDRV, "NVDEC method 0x{:X}", LOG_DEBUG(Service_NVDRV, "NVDEC method 0x{:X}",
static_cast<u32>(nvdec_thi_state.method_0)); static_cast<u32>(nvdec_thi_state.method_0));
nvdec_processor->ProcessMethod(static_cast<Nvdec::Method>(nvdec_thi_state.method_0), nvdec_processor->ProcessMethod(nvdec_thi_state.method_0, data);
data);
break; break;
default: default:
break; break;

View file

@ -23,8 +23,8 @@ void AVFrameDeleter(AVFrame* ptr) {
av_free(ptr); av_free(ptr);
} }
Codec::Codec(GPU& gpu_) Codec::Codec(GPU& gpu_, const NvdecCommon::NvdecRegisters& regs)
: gpu(gpu_), h264_decoder(std::make_unique<Decoder::H264>(gpu)), : gpu(gpu_), state{regs}, h264_decoder(std::make_unique<Decoder::H264>(gpu)),
vp9_decoder(std::make_unique<Decoder::VP9>(gpu)) {} vp9_decoder(std::make_unique<Decoder::VP9>(gpu)) {}
Codec::~Codec() { Codec::~Codec() {
@ -43,46 +43,48 @@ Codec::~Codec() {
avcodec_close(av_codec_ctx); avcodec_close(av_codec_ctx);
} }
void Codec::SetTargetCodec(NvdecCommon::VideoCodec codec) { void Codec::Initialize() {
if (current_codec != codec) { AVCodecID codec{AV_CODEC_ID_NONE};
LOG_INFO(Service_NVDRV, "NVDEC video codec initialized to {}", static_cast<u32>(codec)); switch (current_codec) {
current_codec = codec; case NvdecCommon::VideoCodec::H264:
codec = AV_CODEC_ID_H264;
break;
case NvdecCommon::VideoCodec::Vp9:
codec = AV_CODEC_ID_VP9;
break;
default:
return;
} }
av_codec = avcodec_find_decoder(codec);
av_codec_ctx = avcodec_alloc_context3(av_codec);
av_opt_set(av_codec_ctx->priv_data, "tune", "zerolatency", 0);
// TODO(ameerj): libavcodec gpu hw acceleration
const auto av_error = avcodec_open2(av_codec_ctx, av_codec, nullptr);
if (av_error < 0) {
LOG_ERROR(Service_NVDRV, "avcodec_open2() Failed.");
avcodec_close(av_codec_ctx);
return;
}
initialized = true;
return;
} }
void Codec::StateWrite(u32 offset, u64 arguments) { void Codec::SetTargetCodec(NvdecCommon::VideoCodec codec) {
u8* const state_offset = reinterpret_cast<u8*>(&state) + offset * sizeof(u64); if (current_codec != codec) {
std::memcpy(state_offset, &arguments, sizeof(u64)); current_codec = codec;
LOG_INFO(Service_NVDRV, "NVDEC video codec initialized to {}", GetCurrentCodecName());
}
} }
void Codec::Decode() { void Codec::Decode() {
bool is_first_frame = false; const bool is_first_frame = !initialized;
if (!initialized) { if (!initialized) {
if (current_codec == NvdecCommon::VideoCodec::H264) { Initialize();
av_codec = avcodec_find_decoder(AV_CODEC_ID_H264);
} else if (current_codec == NvdecCommon::VideoCodec::Vp9) {
av_codec = avcodec_find_decoder(AV_CODEC_ID_VP9);
} else {
LOG_ERROR(Service_NVDRV, "Unknown video codec {}", current_codec);
return;
}
av_codec_ctx = avcodec_alloc_context3(av_codec);
av_opt_set(av_codec_ctx->priv_data, "tune", "zerolatency", 0);
// TODO(ameerj): libavcodec gpu hw acceleration
const auto av_error = avcodec_open2(av_codec_ctx, av_codec, nullptr);
if (av_error < 0) {
LOG_ERROR(Service_NVDRV, "avcodec_open2() Failed.");
avcodec_close(av_codec_ctx);
return;
}
initialized = true;
is_first_frame = true;
} }
bool vp9_hidden_frame = false;
bool vp9_hidden_frame = false;
AVPacket packet{}; AVPacket packet{};
av_init_packet(&packet); av_init_packet(&packet);
std::vector<u8> frame_data; std::vector<u8> frame_data;
@ -95,7 +97,7 @@ void Codec::Decode() {
} }
packet.data = frame_data.data(); packet.data = frame_data.data();
packet.size = static_cast<int>(frame_data.size()); packet.size = static_cast<s32>(frame_data.size());
avcodec_send_packet(av_codec_ctx, &packet); avcodec_send_packet(av_codec_ctx, &packet);
@ -127,4 +129,21 @@ NvdecCommon::VideoCodec Codec::GetCurrentCodec() const {
return current_codec; return current_codec;
} }
std::string_view Codec::GetCurrentCodecName() const {
switch (current_codec) {
case NvdecCommon::VideoCodec::None:
return "None";
case NvdecCommon::VideoCodec::H264:
return "H264";
case NvdecCommon::VideoCodec::Vp8:
return "VP8";
case NvdecCommon::VideoCodec::H265:
return "H265";
case NvdecCommon::VideoCodec::Vp9:
return "VP9";
default:
return "Unknown";
}
};
} // namespace Tegra } // namespace Tegra

View file

@ -42,15 +42,15 @@ class VP9;
class Codec { class Codec {
public: public:
explicit Codec(GPU& gpu); explicit Codec(GPU& gpu, const NvdecCommon::NvdecRegisters& regs);
~Codec(); ~Codec();
/// Initialize the codec, returning success or failure
void Initialize();
/// Sets NVDEC video stream codec /// Sets NVDEC video stream codec
void SetTargetCodec(NvdecCommon::VideoCodec codec); void SetTargetCodec(NvdecCommon::VideoCodec codec);
/// Populate NvdecRegisters state with argument value at the provided offset
void StateWrite(u32 offset, u64 arguments);
/// Call decoders to construct headers, decode AVFrame with ffmpeg /// Call decoders to construct headers, decode AVFrame with ffmpeg
void Decode(); void Decode();
@ -59,6 +59,8 @@ public:
/// Returns the value of current_codec /// Returns the value of current_codec
[[nodiscard]] NvdecCommon::VideoCodec GetCurrentCodec() const; [[nodiscard]] NvdecCommon::VideoCodec GetCurrentCodec() const;
/// Return name of the current codec
[[nodiscard]] std::string_view GetCurrentCodecName() const;
private: private:
bool initialized{}; bool initialized{};
@ -68,10 +70,10 @@ private:
AVCodecContext* av_codec_ctx{nullptr}; AVCodecContext* av_codec_ctx{nullptr};
GPU& gpu; GPU& gpu;
const NvdecCommon::NvdecRegisters& state;
std::unique_ptr<Decoder::H264> h264_decoder; std::unique_ptr<Decoder::H264> h264_decoder;
std::unique_ptr<Decoder::VP9> vp9_decoder; std::unique_ptr<Decoder::VP9> vp9_decoder;
NvdecCommon::NvdecRegisters state{};
std::queue<AVFramePtr> av_frames{}; std::queue<AVFramePtr> av_frames{};
}; };

View file

@ -45,135 +45,130 @@ H264::~H264() = default;
const std::vector<u8>& H264::ComposeFrameHeader(const NvdecCommon::NvdecRegisters& state, const std::vector<u8>& H264::ComposeFrameHeader(const NvdecCommon::NvdecRegisters& state,
bool is_first_frame) { bool is_first_frame) {
H264DecoderContext context{}; H264DecoderContext context;
gpu.MemoryManager().ReadBlock(state.picture_info_offset, &context, sizeof(H264DecoderContext)); gpu.MemoryManager().ReadBlock(state.picture_info_offset, &context, sizeof(H264DecoderContext));
const s32 frame_number = static_cast<s32>((context.h264_parameter_set.flags >> 46) & 0x1ffff); const s64 frame_number = context.h264_parameter_set.frame_number.Value();
if (!is_first_frame && frame_number != 0) { if (!is_first_frame && frame_number != 0) {
frame.resize(context.frame_data_size); frame.resize(context.stream_len);
gpu.MemoryManager().ReadBlock(state.frame_bitstream_offset, frame.data(), frame.size()); gpu.MemoryManager().ReadBlock(state.frame_bitstream_offset, frame.data(), frame.size());
} else { return frame;
/// Encode header
H264BitWriter writer{};
writer.WriteU(1, 24);
writer.WriteU(0, 1);
writer.WriteU(3, 2);
writer.WriteU(7, 5);
writer.WriteU(100, 8);
writer.WriteU(0, 8);
writer.WriteU(31, 8);
writer.WriteUe(0);
const auto chroma_format_idc =
static_cast<u32>((context.h264_parameter_set.flags >> 12) & 3);
writer.WriteUe(chroma_format_idc);
if (chroma_format_idc == 3) {
writer.WriteBit(false);
}
writer.WriteUe(0);
writer.WriteUe(0);
writer.WriteBit(false); // QpprimeYZeroTransformBypassFlag
writer.WriteBit(false); // Scaling matrix present flag
const auto order_cnt_type = static_cast<u32>((context.h264_parameter_set.flags >> 14) & 3);
writer.WriteUe(static_cast<u32>((context.h264_parameter_set.flags >> 8) & 0xf));
writer.WriteUe(order_cnt_type);
if (order_cnt_type == 0) {
writer.WriteUe(context.h264_parameter_set.log2_max_pic_order_cnt);
} else if (order_cnt_type == 1) {
writer.WriteBit(context.h264_parameter_set.delta_pic_order_always_zero_flag != 0);
writer.WriteSe(0);
writer.WriteSe(0);
writer.WriteUe(0);
}
const s32 pic_height = context.h264_parameter_set.pic_height_in_map_units /
(context.h264_parameter_set.frame_mbs_only_flag ? 1 : 2);
writer.WriteUe(16);
writer.WriteBit(false);
writer.WriteUe(context.h264_parameter_set.pic_width_in_mbs - 1);
writer.WriteUe(pic_height - 1);
writer.WriteBit(context.h264_parameter_set.frame_mbs_only_flag != 0);
if (!context.h264_parameter_set.frame_mbs_only_flag) {
writer.WriteBit(((context.h264_parameter_set.flags >> 0) & 1) != 0);
}
writer.WriteBit(((context.h264_parameter_set.flags >> 1) & 1) != 0);
writer.WriteBit(false); // Frame cropping flag
writer.WriteBit(false); // VUI parameter present flag
writer.End();
// H264 PPS
writer.WriteU(1, 24);
writer.WriteU(0, 1);
writer.WriteU(3, 2);
writer.WriteU(8, 5);
writer.WriteUe(0);
writer.WriteUe(0);
writer.WriteBit(context.h264_parameter_set.entropy_coding_mode_flag != 0);
writer.WriteBit(false);
writer.WriteUe(0);
writer.WriteUe(context.h264_parameter_set.num_refidx_l0_default_active);
writer.WriteUe(context.h264_parameter_set.num_refidx_l1_default_active);
writer.WriteBit(((context.h264_parameter_set.flags >> 2) & 1) != 0);
writer.WriteU(static_cast<s32>((context.h264_parameter_set.flags >> 32) & 0x3), 2);
s32 pic_init_qp = static_cast<s32>((context.h264_parameter_set.flags >> 16) & 0x3f);
pic_init_qp = (pic_init_qp << 26) >> 26;
writer.WriteSe(pic_init_qp);
writer.WriteSe(0);
s32 chroma_qp_index_offset =
static_cast<s32>((context.h264_parameter_set.flags >> 22) & 0x1f);
chroma_qp_index_offset = (chroma_qp_index_offset << 27) >> 27;
writer.WriteSe(chroma_qp_index_offset);
writer.WriteBit(context.h264_parameter_set.deblocking_filter_control_flag != 0);
writer.WriteBit(((context.h264_parameter_set.flags >> 3) & 1) != 0);
writer.WriteBit(context.h264_parameter_set.redundant_pic_count_flag != 0);
writer.WriteBit(context.h264_parameter_set.transform_8x8_mode_flag != 0);
writer.WriteBit(true);
for (s32 index = 0; index < 6; index++) {
writer.WriteBit(true);
const auto matrix_x4 =
std::vector<u8>(context.scaling_matrix_4.begin(), context.scaling_matrix_4.end());
writer.WriteScalingList(matrix_x4, index * 16, 16);
}
if (context.h264_parameter_set.transform_8x8_mode_flag) {
for (s32 index = 0; index < 2; index++) {
writer.WriteBit(true);
const auto matrix_x8 = std::vector<u8>(context.scaling_matrix_8.begin(),
context.scaling_matrix_8.end());
writer.WriteScalingList(matrix_x8, index * 64, 64);
}
}
s32 chroma_qp_index_offset2 =
static_cast<s32>((context.h264_parameter_set.flags >> 27) & 0x1f);
chroma_qp_index_offset2 = (chroma_qp_index_offset2 << 27) >> 27;
writer.WriteSe(chroma_qp_index_offset2);
writer.End();
const auto& encoded_header = writer.GetByteArray();
frame.resize(encoded_header.size() + context.frame_data_size);
std::memcpy(frame.data(), encoded_header.data(), encoded_header.size());
gpu.MemoryManager().ReadBlock(state.frame_bitstream_offset,
frame.data() + encoded_header.size(),
context.frame_data_size);
} }
// Encode header
H264BitWriter writer{};
writer.WriteU(1, 24);
writer.WriteU(0, 1);
writer.WriteU(3, 2);
writer.WriteU(7, 5);
writer.WriteU(100, 8);
writer.WriteU(0, 8);
writer.WriteU(31, 8);
writer.WriteUe(0);
const u32 chroma_format_idc =
static_cast<u32>(context.h264_parameter_set.chroma_format_idc.Value());
writer.WriteUe(chroma_format_idc);
if (chroma_format_idc == 3) {
writer.WriteBit(false);
}
writer.WriteUe(0);
writer.WriteUe(0);
writer.WriteBit(false); // QpprimeYZeroTransformBypassFlag
writer.WriteBit(false); // Scaling matrix present flag
writer.WriteUe(static_cast<u32>(context.h264_parameter_set.log2_max_frame_num_minus4.Value()));
const auto order_cnt_type =
static_cast<u32>(context.h264_parameter_set.pic_order_cnt_type.Value());
writer.WriteUe(order_cnt_type);
if (order_cnt_type == 0) {
writer.WriteUe(context.h264_parameter_set.log2_max_pic_order_cnt_lsb_minus4);
} else if (order_cnt_type == 1) {
writer.WriteBit(context.h264_parameter_set.delta_pic_order_always_zero_flag != 0);
writer.WriteSe(0);
writer.WriteSe(0);
writer.WriteUe(0);
}
const s32 pic_height = context.h264_parameter_set.frame_height_in_map_units /
(context.h264_parameter_set.frame_mbs_only_flag ? 1 : 2);
writer.WriteUe(16);
writer.WriteBit(false);
writer.WriteUe(context.h264_parameter_set.pic_width_in_mbs - 1);
writer.WriteUe(pic_height - 1);
writer.WriteBit(context.h264_parameter_set.frame_mbs_only_flag != 0);
if (!context.h264_parameter_set.frame_mbs_only_flag) {
writer.WriteBit(context.h264_parameter_set.flags.mbaff_frame.Value() != 0);
}
writer.WriteBit(context.h264_parameter_set.flags.direct_8x8_inference.Value() != 0);
writer.WriteBit(false); // Frame cropping flag
writer.WriteBit(false); // VUI parameter present flag
writer.End();
// H264 PPS
writer.WriteU(1, 24);
writer.WriteU(0, 1);
writer.WriteU(3, 2);
writer.WriteU(8, 5);
writer.WriteUe(0);
writer.WriteUe(0);
writer.WriteBit(context.h264_parameter_set.entropy_coding_mode_flag != 0);
writer.WriteBit(false);
writer.WriteUe(0);
writer.WriteUe(context.h264_parameter_set.num_refidx_l0_default_active);
writer.WriteUe(context.h264_parameter_set.num_refidx_l1_default_active);
writer.WriteBit(context.h264_parameter_set.flags.weighted_pred.Value() != 0);
writer.WriteU(static_cast<s32>(context.h264_parameter_set.weighted_bipred_idc.Value()), 2);
s32 pic_init_qp = static_cast<s32>(context.h264_parameter_set.pic_init_qp_minus26.Value());
writer.WriteSe(pic_init_qp);
writer.WriteSe(0);
s32 chroma_qp_index_offset =
static_cast<s32>(context.h264_parameter_set.chroma_qp_index_offset.Value());
writer.WriteSe(chroma_qp_index_offset);
writer.WriteBit(context.h264_parameter_set.deblocking_filter_control_present_flag != 0);
writer.WriteBit(context.h264_parameter_set.flags.constrained_intra_pred.Value() != 0);
writer.WriteBit(context.h264_parameter_set.redundant_pic_cnt_present_flag != 0);
writer.WriteBit(context.h264_parameter_set.transform_8x8_mode_flag != 0);
writer.WriteBit(true);
for (s32 index = 0; index < 6; index++) {
writer.WriteBit(true);
std::span<const u8> matrix{context.weight_scale};
writer.WriteScalingList(matrix, index * 16, 16);
}
if (context.h264_parameter_set.transform_8x8_mode_flag) {
for (s32 index = 0; index < 2; index++) {
writer.WriteBit(true);
std::span<const u8> matrix{context.weight_scale_8x8};
writer.WriteScalingList(matrix, index * 64, 64);
}
}
s32 chroma_qp_index_offset2 =
static_cast<s32>(context.h264_parameter_set.second_chroma_qp_index_offset.Value());
writer.WriteSe(chroma_qp_index_offset2);
writer.End();
const auto& encoded_header = writer.GetByteArray();
frame.resize(encoded_header.size() + context.stream_len);
std::memcpy(frame.data(), encoded_header.data(), encoded_header.size());
gpu.MemoryManager().ReadBlock(state.frame_bitstream_offset,
frame.data() + encoded_header.size(), context.stream_len);
return frame; return frame;
} }
@ -202,7 +197,7 @@ void H264BitWriter::WriteBit(bool state) {
WriteBits(state ? 1 : 0, 1); WriteBits(state ? 1 : 0, 1);
} }
void H264BitWriter::WriteScalingList(const std::vector<u8>& list, s32 start, s32 count) { void H264BitWriter::WriteScalingList(std::span<const u8> list, s32 start, s32 count) {
std::vector<u8> scan(count); std::vector<u8> scan(count);
if (count == 16) { if (count == 16) {
std::memcpy(scan.data(), zig_zag_scan.data(), scan.size()); std::memcpy(scan.data(), zig_zag_scan.data(), scan.size());

View file

@ -20,7 +20,9 @@
#pragma once #pragma once
#include <span>
#include <vector> #include <vector>
#include "common/bit_field.h"
#include "common/common_funcs.h" #include "common/common_funcs.h"
#include "common/common_types.h" #include "common/common_types.h"
#include "video_core/command_classes/nvdec_common.h" #include "video_core/command_classes/nvdec_common.h"
@ -48,7 +50,7 @@ public:
/// Based on section 7.3.2.1.1.1 and Table 7-4 in the H.264 specification /// Based on section 7.3.2.1.1.1 and Table 7-4 in the H.264 specification
/// Writes the scaling matrices of the sream /// Writes the scaling matrices of the sream
void WriteScalingList(const std::vector<u8>& list, s32 start, s32 count); void WriteScalingList(std::span<const u8> list, s32 start, s32 count);
/// Return the bitstream as a vector. /// Return the bitstream as a vector.
[[nodiscard]] std::vector<u8>& GetByteArray(); [[nodiscard]] std::vector<u8>& GetByteArray();
@ -78,40 +80,110 @@ public:
const NvdecCommon::NvdecRegisters& state, bool is_first_frame = false); const NvdecCommon::NvdecRegisters& state, bool is_first_frame = false);
private: private:
struct H264ParameterSet {
u32 log2_max_pic_order_cnt{};
u32 delta_pic_order_always_zero_flag{};
u32 frame_mbs_only_flag{};
u32 pic_width_in_mbs{};
u32 pic_height_in_map_units{};
INSERT_PADDING_WORDS(1);
u32 entropy_coding_mode_flag{};
u32 bottom_field_pic_order_flag{};
u32 num_refidx_l0_default_active{};
u32 num_refidx_l1_default_active{};
u32 deblocking_filter_control_flag{};
u32 redundant_pic_count_flag{};
u32 transform_8x8_mode_flag{};
INSERT_PADDING_WORDS(9);
u64 flags{};
u32 frame_number{};
u32 frame_number2{};
};
static_assert(sizeof(H264ParameterSet) == 0x68, "H264ParameterSet is an invalid size");
struct H264DecoderContext {
INSERT_PADDING_BYTES(0x48);
u32 frame_data_size{};
INSERT_PADDING_BYTES(0xc);
H264ParameterSet h264_parameter_set{};
INSERT_PADDING_BYTES(0x100);
std::array<u8, 0x60> scaling_matrix_4;
std::array<u8, 0x80> scaling_matrix_8;
};
static_assert(sizeof(H264DecoderContext) == 0x2a0, "H264DecoderContext is an invalid size");
std::vector<u8> frame; std::vector<u8> frame;
GPU& gpu; GPU& gpu;
struct H264ParameterSet {
s32 log2_max_pic_order_cnt_lsb_minus4; ///< 0x00
s32 delta_pic_order_always_zero_flag; ///< 0x04
s32 frame_mbs_only_flag; ///< 0x08
u32 pic_width_in_mbs; ///< 0x0C
u32 frame_height_in_map_units; ///< 0x10
union { ///< 0x14
BitField<0, 2, u32> tile_format;
BitField<2, 3, u32> gob_height;
};
u32 entropy_coding_mode_flag; ///< 0x18
s32 pic_order_present_flag; ///< 0x1C
s32 num_refidx_l0_default_active; ///< 0x20
s32 num_refidx_l1_default_active; ///< 0x24
s32 deblocking_filter_control_present_flag; ///< 0x28
s32 redundant_pic_cnt_present_flag; ///< 0x2C
u32 transform_8x8_mode_flag; ///< 0x30
u32 pitch_luma; ///< 0x34
u32 pitch_chroma; ///< 0x38
u32 luma_top_offset; ///< 0x3C
u32 luma_bot_offset; ///< 0x40
u32 luma_frame_offset; ///< 0x44
u32 chroma_top_offset; ///< 0x48
u32 chroma_bot_offset; ///< 0x4C
u32 chroma_frame_offset; ///< 0x50
u32 hist_buffer_size; ///< 0x54
union { ///< 0x58
union {
BitField<0, 1, u64> mbaff_frame;
BitField<1, 1, u64> direct_8x8_inference;
BitField<2, 1, u64> weighted_pred;
BitField<3, 1, u64> constrained_intra_pred;
BitField<4, 1, u64> ref_pic;
BitField<5, 1, u64> field_pic;
BitField<6, 1, u64> bottom_field;
BitField<7, 1, u64> second_field;
} flags;
BitField<8, 4, u64> log2_max_frame_num_minus4;
BitField<12, 2, u64> chroma_format_idc;
BitField<14, 2, u64> pic_order_cnt_type;
BitField<16, 6, s64> pic_init_qp_minus26;
BitField<22, 5, s64> chroma_qp_index_offset;
BitField<27, 5, s64> second_chroma_qp_index_offset;
BitField<32, 2, u64> weighted_bipred_idc;
BitField<34, 7, u64> curr_pic_idx;
BitField<41, 5, u64> curr_col_idx;
BitField<46, 16, u64> frame_number;
BitField<62, 1, u64> frame_surfaces;
BitField<63, 1, u64> output_memory_layout;
};
};
static_assert(sizeof(H264ParameterSet) == 0x60, "H264ParameterSet is an invalid size");
struct H264DecoderContext {
INSERT_PADDING_WORDS_NOINIT(18); ///< 0x0000
u32 stream_len; ///< 0x0048
INSERT_PADDING_WORDS_NOINIT(3); ///< 0x004C
H264ParameterSet h264_parameter_set; ///< 0x0058
INSERT_PADDING_WORDS_NOINIT(66); ///< 0x00B8
std::array<u8, 0x60> weight_scale; ///< 0x01C0
std::array<u8, 0x80> weight_scale_8x8; ///< 0x0220
};
static_assert(sizeof(H264DecoderContext) == 0x2A0, "H264DecoderContext is an invalid size");
#define ASSERT_POSITION(field_name, position) \
static_assert(offsetof(H264ParameterSet, field_name) == position, \
"Field " #field_name " has invalid position")
ASSERT_POSITION(log2_max_pic_order_cnt_lsb_minus4, 0x00);
ASSERT_POSITION(delta_pic_order_always_zero_flag, 0x04);
ASSERT_POSITION(frame_mbs_only_flag, 0x08);
ASSERT_POSITION(pic_width_in_mbs, 0x0C);
ASSERT_POSITION(frame_height_in_map_units, 0x10);
ASSERT_POSITION(tile_format, 0x14);
ASSERT_POSITION(entropy_coding_mode_flag, 0x18);
ASSERT_POSITION(pic_order_present_flag, 0x1C);
ASSERT_POSITION(num_refidx_l0_default_active, 0x20);
ASSERT_POSITION(num_refidx_l1_default_active, 0x24);
ASSERT_POSITION(deblocking_filter_control_present_flag, 0x28);
ASSERT_POSITION(redundant_pic_cnt_present_flag, 0x2C);
ASSERT_POSITION(transform_8x8_mode_flag, 0x30);
ASSERT_POSITION(pitch_luma, 0x34);
ASSERT_POSITION(pitch_chroma, 0x38);
ASSERT_POSITION(luma_top_offset, 0x3C);
ASSERT_POSITION(luma_bot_offset, 0x40);
ASSERT_POSITION(luma_frame_offset, 0x44);
ASSERT_POSITION(chroma_top_offset, 0x48);
ASSERT_POSITION(chroma_bot_offset, 0x4C);
ASSERT_POSITION(chroma_frame_offset, 0x50);
ASSERT_POSITION(hist_buffer_size, 0x54);
ASSERT_POSITION(flags, 0x58);
#undef ASSERT_POSITION
#define ASSERT_POSITION(field_name, position) \
static_assert(offsetof(H264DecoderContext, field_name) == position, \
"Field " #field_name " has invalid position")
ASSERT_POSITION(stream_len, 0x48);
ASSERT_POSITION(h264_parameter_set, 0x58);
ASSERT_POSITION(weight_scale, 0x1C0);
#undef ASSERT_POSITION
}; };
} // namespace Decoder } // namespace Decoder

View file

@ -354,7 +354,7 @@ void VP9::WriteMvProbabilityUpdate(VpxRangeEncoder& writer, u8 new_prob, u8 old_
} }
Vp9PictureInfo VP9::GetVp9PictureInfo(const NvdecCommon::NvdecRegisters& state) { Vp9PictureInfo VP9::GetVp9PictureInfo(const NvdecCommon::NvdecRegisters& state) {
PictureInfo picture_info{}; PictureInfo picture_info;
gpu.MemoryManager().ReadBlock(state.picture_info_offset, &picture_info, sizeof(PictureInfo)); gpu.MemoryManager().ReadBlock(state.picture_info_offset, &picture_info, sizeof(PictureInfo));
Vp9PictureInfo vp9_info = picture_info.Convert(); Vp9PictureInfo vp9_info = picture_info.Convert();
@ -370,7 +370,7 @@ Vp9PictureInfo VP9::GetVp9PictureInfo(const NvdecCommon::NvdecRegisters& state)
} }
void VP9::InsertEntropy(u64 offset, Vp9EntropyProbs& dst) { void VP9::InsertEntropy(u64 offset, Vp9EntropyProbs& dst) {
EntropyProbs entropy{}; EntropyProbs entropy;
gpu.MemoryManager().ReadBlock(offset, &entropy, sizeof(EntropyProbs)); gpu.MemoryManager().ReadBlock(offset, &entropy, sizeof(EntropyProbs));
entropy.Convert(dst); entropy.Convert(dst);
} }

View file

@ -15,10 +15,10 @@ class GPU;
namespace Decoder { namespace Decoder {
struct Vp9FrameDimensions { struct Vp9FrameDimensions {
s16 width{}; s16 width;
s16 height{}; s16 height;
s16 luma_pitch{}; s16 luma_pitch;
s16 chroma_pitch{}; s16 chroma_pitch;
}; };
static_assert(sizeof(Vp9FrameDimensions) == 0x8, "Vp9 Vp9FrameDimensions is an invalid size"); static_assert(sizeof(Vp9FrameDimensions) == 0x8, "Vp9 Vp9FrameDimensions is an invalid size");
@ -49,87 +49,87 @@ enum class TxMode {
}; };
struct Segmentation { struct Segmentation {
u8 enabled{}; u8 enabled;
u8 update_map{}; u8 update_map;
u8 temporal_update{}; u8 temporal_update;
u8 abs_delta{}; u8 abs_delta;
std::array<u32, 8> feature_mask{}; std::array<u32, 8> feature_mask;
std::array<std::array<s16, 4>, 8> feature_data{}; std::array<std::array<s16, 4>, 8> feature_data;
}; };
static_assert(sizeof(Segmentation) == 0x64, "Segmentation is an invalid size"); static_assert(sizeof(Segmentation) == 0x64, "Segmentation is an invalid size");
struct LoopFilter { struct LoopFilter {
u8 mode_ref_delta_enabled{}; u8 mode_ref_delta_enabled;
std::array<s8, 4> ref_deltas{}; std::array<s8, 4> ref_deltas;
std::array<s8, 2> mode_deltas{}; std::array<s8, 2> mode_deltas;
}; };
static_assert(sizeof(LoopFilter) == 0x7, "LoopFilter is an invalid size"); static_assert(sizeof(LoopFilter) == 0x7, "LoopFilter is an invalid size");
struct Vp9EntropyProbs { struct Vp9EntropyProbs {
std::array<u8, 36> y_mode_prob{}; std::array<u8, 36> y_mode_prob; ///< 0x0000
std::array<u8, 64> partition_prob{}; std::array<u8, 64> partition_prob; ///< 0x0024
std::array<u8, 1728> coef_probs{}; std::array<u8, 1728> coef_probs; ///< 0x0064
std::array<u8, 8> switchable_interp_prob{}; std::array<u8, 8> switchable_interp_prob; ///< 0x0724
std::array<u8, 28> inter_mode_prob{}; std::array<u8, 28> inter_mode_prob; ///< 0x072C
std::array<u8, 4> intra_inter_prob{}; std::array<u8, 4> intra_inter_prob; ///< 0x0748
std::array<u8, 5> comp_inter_prob{}; std::array<u8, 5> comp_inter_prob; ///< 0x074C
std::array<u8, 10> single_ref_prob{}; std::array<u8, 10> single_ref_prob; ///< 0x0751
std::array<u8, 5> comp_ref_prob{}; std::array<u8, 5> comp_ref_prob; ///< 0x075B
std::array<u8, 6> tx_32x32_prob{}; std::array<u8, 6> tx_32x32_prob; ///< 0x0760
std::array<u8, 4> tx_16x16_prob{}; std::array<u8, 4> tx_16x16_prob; ///< 0x0766
std::array<u8, 2> tx_8x8_prob{}; std::array<u8, 2> tx_8x8_prob; ///< 0x076A
std::array<u8, 3> skip_probs{}; std::array<u8, 3> skip_probs; ///< 0x076C
std::array<u8, 3> joints{}; std::array<u8, 3> joints; ///< 0x076F
std::array<u8, 2> sign{}; std::array<u8, 2> sign; ///< 0x0772
std::array<u8, 20> classes{}; std::array<u8, 20> classes; ///< 0x0774
std::array<u8, 2> class_0{}; std::array<u8, 2> class_0; ///< 0x0788
std::array<u8, 20> prob_bits{}; std::array<u8, 20> prob_bits; ///< 0x078A
std::array<u8, 12> class_0_fr{}; std::array<u8, 12> class_0_fr; ///< 0x079E
std::array<u8, 6> fr{}; std::array<u8, 6> fr; ///< 0x07AA
std::array<u8, 2> class_0_hp{}; std::array<u8, 2> class_0_hp; ///< 0x07B0
std::array<u8, 2> high_precision{}; std::array<u8, 2> high_precision; ///< 0x07B2
}; };
static_assert(sizeof(Vp9EntropyProbs) == 0x7B4, "Vp9EntropyProbs is an invalid size"); static_assert(sizeof(Vp9EntropyProbs) == 0x7B4, "Vp9EntropyProbs is an invalid size");
struct Vp9PictureInfo { struct Vp9PictureInfo {
bool is_key_frame{}; bool is_key_frame;
bool intra_only{}; bool intra_only;
bool last_frame_was_key{}; bool last_frame_was_key;
bool frame_size_changed{}; bool frame_size_changed;
bool error_resilient_mode{}; bool error_resilient_mode;
bool last_frame_shown{}; bool last_frame_shown;
bool show_frame{}; bool show_frame;
std::array<s8, 4> ref_frame_sign_bias{}; std::array<s8, 4> ref_frame_sign_bias;
s32 base_q_index{}; s32 base_q_index;
s32 y_dc_delta_q{}; s32 y_dc_delta_q;
s32 uv_dc_delta_q{}; s32 uv_dc_delta_q;
s32 uv_ac_delta_q{}; s32 uv_ac_delta_q;
bool lossless{}; bool lossless;
s32 transform_mode{}; s32 transform_mode;
bool allow_high_precision_mv{}; bool allow_high_precision_mv;
s32 interp_filter{}; s32 interp_filter;
s32 reference_mode{}; s32 reference_mode;
s8 comp_fixed_ref{}; s8 comp_fixed_ref;
std::array<s8, 2> comp_var_ref{}; std::array<s8, 2> comp_var_ref;
s32 log2_tile_cols{}; s32 log2_tile_cols;
s32 log2_tile_rows{}; s32 log2_tile_rows;
bool segment_enabled{}; bool segment_enabled;
bool segment_map_update{}; bool segment_map_update;
bool segment_map_temporal_update{}; bool segment_map_temporal_update;
s32 segment_abs_delta{}; s32 segment_abs_delta;
std::array<u32, 8> segment_feature_enable{}; std::array<u32, 8> segment_feature_enable;
std::array<std::array<s16, 4>, 8> segment_feature_data{}; std::array<std::array<s16, 4>, 8> segment_feature_data;
bool mode_ref_delta_enabled{}; bool mode_ref_delta_enabled;
bool use_prev_in_find_mv_refs{}; bool use_prev_in_find_mv_refs;
std::array<s8, 4> ref_deltas{}; std::array<s8, 4> ref_deltas;
std::array<s8, 2> mode_deltas{}; std::array<s8, 2> mode_deltas;
Vp9EntropyProbs entropy{}; Vp9EntropyProbs entropy;
Vp9FrameDimensions frame_size{}; Vp9FrameDimensions frame_size;
u8 first_level{}; u8 first_level;
u8 sharpness_level{}; u8 sharpness_level;
u32 bitstream_size{}; u32 bitstream_size;
std::array<u64, 4> frame_offsets{}; std::array<u64, 4> frame_offsets;
std::array<bool, 4> refresh_frame{}; std::array<bool, 4> refresh_frame;
}; };
struct Vp9FrameContainer { struct Vp9FrameContainer {
@ -138,35 +138,35 @@ struct Vp9FrameContainer {
}; };
struct PictureInfo { struct PictureInfo {
INSERT_PADDING_WORDS(12); INSERT_PADDING_WORDS_NOINIT(12); ///< 0x00
u32 bitstream_size{}; u32 bitstream_size; ///< 0x30
INSERT_PADDING_WORDS(5); INSERT_PADDING_WORDS_NOINIT(5); ///< 0x34
Vp9FrameDimensions last_frame_size{}; Vp9FrameDimensions last_frame_size; ///< 0x48
Vp9FrameDimensions golden_frame_size{}; Vp9FrameDimensions golden_frame_size; ///< 0x50
Vp9FrameDimensions alt_frame_size{}; Vp9FrameDimensions alt_frame_size; ///< 0x58
Vp9FrameDimensions current_frame_size{}; Vp9FrameDimensions current_frame_size; ///< 0x60
u32 vp9_flags{}; u32 vp9_flags; ///< 0x68
std::array<s8, 4> ref_frame_sign_bias{}; std::array<s8, 4> ref_frame_sign_bias; ///< 0x6C
u8 first_level{}; u8 first_level; ///< 0x70
u8 sharpness_level{}; u8 sharpness_level; ///< 0x71
u8 base_q_index{}; u8 base_q_index; ///< 0x72
u8 y_dc_delta_q{}; u8 y_dc_delta_q; ///< 0x73
u8 uv_ac_delta_q{}; u8 uv_ac_delta_q; ///< 0x74
u8 uv_dc_delta_q{}; u8 uv_dc_delta_q; ///< 0x75
u8 lossless{}; u8 lossless; ///< 0x76
u8 tx_mode{}; u8 tx_mode; ///< 0x77
u8 allow_high_precision_mv{}; u8 allow_high_precision_mv; ///< 0x78
u8 interp_filter{}; u8 interp_filter; ///< 0x79
u8 reference_mode{}; u8 reference_mode; ///< 0x7A
s8 comp_fixed_ref{}; s8 comp_fixed_ref; ///< 0x7B
std::array<s8, 2> comp_var_ref{}; std::array<s8, 2> comp_var_ref; ///< 0x7C
u8 log2_tile_cols{}; u8 log2_tile_cols; ///< 0x7E
u8 log2_tile_rows{}; u8 log2_tile_rows; ///< 0x7F
Segmentation segmentation{}; Segmentation segmentation; ///< 0x80
LoopFilter loop_filter{}; LoopFilter loop_filter; ///< 0xE4
INSERT_PADDING_BYTES(5); INSERT_PADDING_BYTES_NOINIT(5); ///< 0xEB
u32 surface_params{}; u32 surface_params; ///< 0xF0
INSERT_PADDING_WORDS(3); INSERT_PADDING_WORDS_NOINIT(3); ///< 0xF4
[[nodiscard]] Vp9PictureInfo Convert() const { [[nodiscard]] Vp9PictureInfo Convert() const {
return { return {
@ -176,6 +176,7 @@ struct PictureInfo {
.frame_size_changed = (vp9_flags & FrameFlags::FrameSizeChanged) != 0, .frame_size_changed = (vp9_flags & FrameFlags::FrameSizeChanged) != 0,
.error_resilient_mode = (vp9_flags & FrameFlags::ErrorResilientMode) != 0, .error_resilient_mode = (vp9_flags & FrameFlags::ErrorResilientMode) != 0,
.last_frame_shown = (vp9_flags & FrameFlags::LastShowFrame) != 0, .last_frame_shown = (vp9_flags & FrameFlags::LastShowFrame) != 0,
.show_frame = false,
.ref_frame_sign_bias = ref_frame_sign_bias, .ref_frame_sign_bias = ref_frame_sign_bias,
.base_q_index = base_q_index, .base_q_index = base_q_index,
.y_dc_delta_q = y_dc_delta_q, .y_dc_delta_q = y_dc_delta_q,
@ -204,45 +205,48 @@ struct PictureInfo {
!(vp9_flags == (FrameFlags::LastFrameIsKeyFrame)), !(vp9_flags == (FrameFlags::LastFrameIsKeyFrame)),
.ref_deltas = loop_filter.ref_deltas, .ref_deltas = loop_filter.ref_deltas,
.mode_deltas = loop_filter.mode_deltas, .mode_deltas = loop_filter.mode_deltas,
.entropy{},
.frame_size = current_frame_size, .frame_size = current_frame_size,
.first_level = first_level, .first_level = first_level,
.sharpness_level = sharpness_level, .sharpness_level = sharpness_level,
.bitstream_size = bitstream_size, .bitstream_size = bitstream_size,
.frame_offsets{},
.refresh_frame{},
}; };
} }
}; };
static_assert(sizeof(PictureInfo) == 0x100, "PictureInfo is an invalid size"); static_assert(sizeof(PictureInfo) == 0x100, "PictureInfo is an invalid size");
struct EntropyProbs { struct EntropyProbs {
INSERT_PADDING_BYTES(1024); INSERT_PADDING_BYTES_NOINIT(1024); ///< 0x0000
std::array<u8, 28> inter_mode_prob{}; std::array<u8, 28> inter_mode_prob; ///< 0x0400
std::array<u8, 4> intra_inter_prob{}; std::array<u8, 4> intra_inter_prob; ///< 0x041C
INSERT_PADDING_BYTES(80); INSERT_PADDING_BYTES_NOINIT(80); ///< 0x0420
std::array<u8, 2> tx_8x8_prob{}; std::array<u8, 2> tx_8x8_prob; ///< 0x0470
std::array<u8, 4> tx_16x16_prob{}; std::array<u8, 4> tx_16x16_prob; ///< 0x0472
std::array<u8, 6> tx_32x32_prob{}; std::array<u8, 6> tx_32x32_prob; ///< 0x0476
std::array<u8, 4> y_mode_prob_e8{}; std::array<u8, 4> y_mode_prob_e8; ///< 0x047C
std::array<std::array<u8, 8>, 4> y_mode_prob_e0e7{}; std::array<std::array<u8, 8>, 4> y_mode_prob_e0e7; ///< 0x0480
INSERT_PADDING_BYTES(64); INSERT_PADDING_BYTES_NOINIT(64); ///< 0x04A0
std::array<u8, 64> partition_prob{}; std::array<u8, 64> partition_prob; ///< 0x04E0
INSERT_PADDING_BYTES(10); INSERT_PADDING_BYTES_NOINIT(10); ///< 0x0520
std::array<u8, 8> switchable_interp_prob{}; std::array<u8, 8> switchable_interp_prob; ///< 0x052A
std::array<u8, 5> comp_inter_prob{}; std::array<u8, 5> comp_inter_prob; ///< 0x0532
std::array<u8, 3> skip_probs{}; std::array<u8, 3> skip_probs; ///< 0x0537
INSERT_PADDING_BYTES(1); INSERT_PADDING_BYTES_NOINIT(1); ///< 0x053A
std::array<u8, 3> joints{}; std::array<u8, 3> joints; ///< 0x053B
std::array<u8, 2> sign{}; std::array<u8, 2> sign; ///< 0x053E
std::array<u8, 2> class_0{}; std::array<u8, 2> class_0; ///< 0x0540
std::array<u8, 6> fr{}; std::array<u8, 6> fr; ///< 0x0542
std::array<u8, 2> class_0_hp{}; std::array<u8, 2> class_0_hp; ///< 0x0548
std::array<u8, 2> high_precision{}; std::array<u8, 2> high_precision; ///< 0x054A
std::array<u8, 20> classes{}; std::array<u8, 20> classes; ///< 0x054C
std::array<u8, 12> class_0_fr{}; std::array<u8, 12> class_0_fr; ///< 0x0560
std::array<u8, 20> pred_bits{}; std::array<u8, 20> pred_bits; ///< 0x056C
std::array<u8, 10> single_ref_prob{}; std::array<u8, 10> single_ref_prob; ///< 0x0580
std::array<u8, 5> comp_ref_prob{}; std::array<u8, 5> comp_ref_prob; ///< 0x058A
INSERT_PADDING_BYTES(17); INSERT_PADDING_BYTES_NOINIT(17); ///< 0x058F
std::array<u8, 2304> coef_probs{}; std::array<u8, 2304> coef_probs; ///< 0x05A0
void Convert(Vp9EntropyProbs& fc) { void Convert(Vp9EntropyProbs& fc) {
fc.inter_mode_prob = inter_mode_prob; fc.inter_mode_prob = inter_mode_prob;
@ -293,10 +297,45 @@ struct RefPoolElement {
}; };
struct FrameContexts { struct FrameContexts {
s64 from{}; s64 from;
bool adapted{}; bool adapted;
Vp9EntropyProbs probs{}; Vp9EntropyProbs probs;
}; };
#define ASSERT_POSITION(field_name, position) \
static_assert(offsetof(Vp9EntropyProbs, field_name) == position, \
"Field " #field_name " has invalid position")
ASSERT_POSITION(partition_prob, 0x0024);
ASSERT_POSITION(switchable_interp_prob, 0x0724);
ASSERT_POSITION(sign, 0x0772);
ASSERT_POSITION(class_0_fr, 0x079E);
ASSERT_POSITION(high_precision, 0x07B2);
#undef ASSERT_POSITION
#define ASSERT_POSITION(field_name, position) \
static_assert(offsetof(PictureInfo, field_name) == position, \
"Field " #field_name " has invalid position")
ASSERT_POSITION(bitstream_size, 0x30);
ASSERT_POSITION(last_frame_size, 0x48);
ASSERT_POSITION(first_level, 0x70);
ASSERT_POSITION(segmentation, 0x80);
ASSERT_POSITION(loop_filter, 0xE4);
ASSERT_POSITION(surface_params, 0xF0);
#undef ASSERT_POSITION
#define ASSERT_POSITION(field_name, position) \
static_assert(offsetof(EntropyProbs, field_name) == position, \
"Field " #field_name " has invalid position")
ASSERT_POSITION(inter_mode_prob, 0x400);
ASSERT_POSITION(tx_8x8_prob, 0x470);
ASSERT_POSITION(partition_prob, 0x4E0);
ASSERT_POSITION(class_0, 0x540);
ASSERT_POSITION(class_0_fr, 0x560);
ASSERT_POSITION(coef_probs, 0x5A0);
#undef ASSERT_POSITION
}; // namespace Decoder }; // namespace Decoder
}; // namespace Tegra }; // namespace Tegra

View file

@ -8,22 +8,21 @@
namespace Tegra { namespace Tegra {
Nvdec::Nvdec(GPU& gpu_) : gpu(gpu_), codec(std::make_unique<Codec>(gpu)) {} #define NVDEC_REG_INDEX(field_name) \
(offsetof(NvdecCommon::NvdecRegisters, field_name) / sizeof(u64))
Nvdec::Nvdec(GPU& gpu_) : gpu(gpu_), state{}, codec(std::make_unique<Codec>(gpu, state)) {}
Nvdec::~Nvdec() = default; Nvdec::~Nvdec() = default;
void Nvdec::ProcessMethod(Method method, u32 argument) { void Nvdec::ProcessMethod(u32 method, u32 argument) {
if (method == Method::SetVideoCodec) { state.reg_array[method] = static_cast<u64>(argument) << 8;
codec->StateWrite(static_cast<u32>(method), argument);
} else {
codec->StateWrite(static_cast<u32>(method), static_cast<u64>(argument) << 8);
}
switch (method) { switch (method) {
case Method::SetVideoCodec: case NVDEC_REG_INDEX(set_codec_id):
codec->SetTargetCodec(static_cast<NvdecCommon::VideoCodec>(argument)); codec->SetTargetCodec(static_cast<NvdecCommon::VideoCodec>(argument));
break; break;
case Method::Execute: case NVDEC_REG_INDEX(execute):
Execute(); Execute();
break; break;
} }

View file

@ -14,16 +14,11 @@ class GPU;
class Nvdec { class Nvdec {
public: public:
enum class Method : u32 {
SetVideoCodec = 0x80,
Execute = 0xc0,
};
explicit Nvdec(GPU& gpu); explicit Nvdec(GPU& gpu);
~Nvdec(); ~Nvdec();
/// Writes the method into the state, Invoke Execute() if encountered /// Writes the method into the state, Invoke Execute() if encountered
void ProcessMethod(Method method, u32 argument); void ProcessMethod(u32 method, u32 argument);
/// Return most recently decoded frame /// Return most recently decoded frame
[[nodiscard]] AVFramePtr GetFrame(); [[nodiscard]] AVFramePtr GetFrame();
@ -33,6 +28,7 @@ private:
void Execute(); void Execute();
GPU& gpu; GPU& gpu;
NvdecCommon::NvdecRegisters state;
std::unique_ptr<Codec> codec; std::unique_ptr<Codec> codec;
}; };
} // namespace Tegra } // namespace Tegra

View file

@ -4,40 +4,13 @@
#pragma once #pragma once
#include "common/bit_field.h"
#include "common/common_funcs.h" #include "common/common_funcs.h"
#include "common/common_types.h" #include "common/common_types.h"
namespace Tegra::NvdecCommon { namespace Tegra::NvdecCommon {
struct NvdecRegisters { enum class VideoCodec : u64 {
INSERT_PADDING_WORDS(256);
u64 set_codec_id{};
INSERT_PADDING_WORDS(254);
u64 set_platform_id{};
u64 picture_info_offset{};
u64 frame_bitstream_offset{};
u64 frame_number{};
u64 h264_slice_data_offsets{};
u64 h264_mv_dump_offset{};
INSERT_PADDING_WORDS(6);
u64 frame_stats_offset{};
u64 h264_last_surface_luma_offset{};
u64 h264_last_surface_chroma_offset{};
std::array<u64, 17> surface_luma_offset{};
std::array<u64, 17> surface_chroma_offset{};
INSERT_PADDING_WORDS(132);
u64 vp9_entropy_probs_offset{};
u64 vp9_backward_updates_offset{};
u64 vp9_last_frame_segmap_offset{};
u64 vp9_curr_frame_segmap_offset{};
INSERT_PADDING_WORDS(2);
u64 vp9_last_frame_mvs_offset{};
u64 vp9_curr_frame_mvs_offset{};
INSERT_PADDING_WORDS(2);
};
static_assert(sizeof(NvdecRegisters) == (0xBC0), "NvdecRegisters is incorrect size");
enum class VideoCodec : u32 {
None = 0x0, None = 0x0,
H264 = 0x3, H264 = 0x3,
Vp8 = 0x5, Vp8 = 0x5,
@ -45,4 +18,76 @@ enum class VideoCodec : u32 {
Vp9 = 0x9, Vp9 = 0x9,
}; };
// NVDEC should use a 32-bit address space, but is mapped to 64-bit,
// doubling the sizes here is compensating for that.
struct NvdecRegisters {
static constexpr std::size_t NUM_REGS = 0x178;
union {
struct {
INSERT_PADDING_WORDS_NOINIT(256); ///< 0x0000
VideoCodec set_codec_id; ///< 0x0400
INSERT_PADDING_WORDS_NOINIT(126); ///< 0x0408
u64 execute; ///< 0x0600
INSERT_PADDING_WORDS_NOINIT(126); ///< 0x0608
struct { ///< 0x0800
union {
BitField<0, 3, VideoCodec> codec;
BitField<4, 1, u64> gp_timer_on;
BitField<13, 1, u64> mb_timer_on;
BitField<14, 1, u64> intra_frame_pslc;
BitField<17, 1, u64> all_intra_frame;
};
} control_params;
u64 picture_info_offset; ///< 0x0808
u64 frame_bitstream_offset; ///< 0x0810
u64 frame_number; ///< 0x0818
u64 h264_slice_data_offsets; ///< 0x0820
u64 h264_mv_dump_offset; ///< 0x0828
INSERT_PADDING_WORDS_NOINIT(6); ///< 0x0830
u64 frame_stats_offset; ///< 0x0848
u64 h264_last_surface_luma_offset; ///< 0x0850
u64 h264_last_surface_chroma_offset; ///< 0x0858
std::array<u64, 17> surface_luma_offset; ///< 0x0860
std::array<u64, 17> surface_chroma_offset; ///< 0x08E8
INSERT_PADDING_WORDS_NOINIT(132); ///< 0x0970
u64 vp9_entropy_probs_offset; ///< 0x0B80
u64 vp9_backward_updates_offset; ///< 0x0B88
u64 vp9_last_frame_segmap_offset; ///< 0x0B90
u64 vp9_curr_frame_segmap_offset; ///< 0x0B98
INSERT_PADDING_WORDS_NOINIT(2); ///< 0x0BA0
u64 vp9_last_frame_mvs_offset; ///< 0x0BA8
u64 vp9_curr_frame_mvs_offset; ///< 0x0BB0
INSERT_PADDING_WORDS_NOINIT(2); ///< 0x0BB8
};
std::array<u64, NUM_REGS> reg_array;
};
};
static_assert(sizeof(NvdecRegisters) == (0xBC0), "NvdecRegisters is incorrect size");
#define ASSERT_REG_POSITION(field_name, position) \
static_assert(offsetof(NvdecRegisters, field_name) == position * sizeof(u64), \
"Field " #field_name " has invalid position")
ASSERT_REG_POSITION(set_codec_id, 0x80);
ASSERT_REG_POSITION(execute, 0xC0);
ASSERT_REG_POSITION(control_params, 0x100);
ASSERT_REG_POSITION(picture_info_offset, 0x101);
ASSERT_REG_POSITION(frame_bitstream_offset, 0x102);
ASSERT_REG_POSITION(frame_number, 0x103);
ASSERT_REG_POSITION(h264_slice_data_offsets, 0x104);
ASSERT_REG_POSITION(frame_stats_offset, 0x109);
ASSERT_REG_POSITION(h264_last_surface_luma_offset, 0x10A);
ASSERT_REG_POSITION(h264_last_surface_chroma_offset, 0x10B);
ASSERT_REG_POSITION(surface_luma_offset, 0x10C);
ASSERT_REG_POSITION(surface_chroma_offset, 0x11D);
ASSERT_REG_POSITION(vp9_entropy_probs_offset, 0x170);
ASSERT_REG_POSITION(vp9_backward_updates_offset, 0x171);
ASSERT_REG_POSITION(vp9_last_frame_segmap_offset, 0x172);
ASSERT_REG_POSITION(vp9_curr_frame_segmap_offset, 0x173);
ASSERT_REG_POSITION(vp9_last_frame_mvs_offset, 0x175);
ASSERT_REG_POSITION(vp9_curr_frame_mvs_offset, 0x176);
#undef ASSERT_REG_POSITION
} // namespace Tegra::NvdecCommon } // namespace Tegra::NvdecCommon

View file

@ -103,7 +103,7 @@ RendererVulkan::RendererVulkan(Core::TelemetrySession& telemetry_session_,
gpu(gpu_), gpu(gpu_),
library(OpenLibrary()), library(OpenLibrary()),
instance(CreateInstance(library, dld, VK_API_VERSION_1_1, render_window.GetWindowInfo().type, instance(CreateInstance(library, dld, VK_API_VERSION_1_1, render_window.GetWindowInfo().type,
true, Settings::values.renderer_debug)), true, Settings::values.renderer_debug.GetValue())),
debug_callback(Settings::values.renderer_debug ? CreateDebugCallback(instance) : nullptr), debug_callback(Settings::values.renderer_debug ? CreateDebugCallback(instance) : nullptr),
surface(CreateSurface(instance, render_window)), surface(CreateSurface(instance, render_window)),
device(CreateDevice(instance, dld, *surface)), device(CreateDevice(instance, dld, *surface)),

File diff suppressed because it is too large Load diff

View file

@ -102,28 +102,75 @@ private:
void SaveUILayoutValues(); void SaveUILayoutValues();
void SaveWebServiceValues(); void SaveWebServiceValues();
/**
* Reads a setting from the qt_config.
*
* @param name The setting's identifier
* @param default_value The value to use when the setting is not already present in the config
*/
QVariant ReadSetting(const QString& name) const; QVariant ReadSetting(const QString& name) const;
QVariant ReadSetting(const QString& name, const QVariant& default_value) const; QVariant ReadSetting(const QString& name, const QVariant& default_value) const;
// Templated ReadSettingGlobal functions will also look for the use_global setting and set
// both the value and the global state properly /**
template <typename Type> * Only reads a setting from the qt_config if the current config is a global config, or if the
void ReadSettingGlobal(Settings::Setting<Type>& setting, const QString& name); * current config is a custom config and the setting is overriding the global setting. Otherwise
template <typename Type> * it does nothing.
void ReadSettingGlobal(Settings::Setting<Type>& setting, const QString& name, *
const QVariant& default_value); * @param setting The variable to be modified
* @param name The setting's identifier
* @param default_value The value to use when the setting is not already present in the config
*/
template <typename Type> template <typename Type>
void ReadSettingGlobal(Type& setting, const QString& name, const QVariant& default_value) const; void ReadSettingGlobal(Type& setting, const QString& name, const QVariant& default_value) const;
// Templated WriteSettingGlobal functions will also write the global state if needed and will
// skip writing the actual setting if it defers to the global value /**
* Writes a setting to the qt_config.
*
* @param name The setting's idetentifier
* @param value Value of the setting
* @param default_value Default of the setting if not present in qt_config
* @param use_global Specifies if the custom or global config should be in use, for custom
* configs
*/
void WriteSetting(const QString& name, const QVariant& value); void WriteSetting(const QString& name, const QVariant& value);
void WriteSetting(const QString& name, const QVariant& value, const QVariant& default_value); void WriteSetting(const QString& name, const QVariant& value, const QVariant& default_value);
void WriteSetting(const QString& name, const QVariant& value, const QVariant& default_value,
bool use_global);
/**
* Reads a value from the qt_config and applies it to the setting, using its label and default
* value. If the config is a custom config, this will also read the global state of the setting
* and apply that information to it.
*
* @param The setting
*/
template <typename Type> template <typename Type>
void WriteSettingGlobal(const QString& name, const Settings::Setting<Type>& setting); void ReadGlobalSetting(Settings::Setting<Type>& setting);
/**
* Sets a value to the qt_config using the setting's label and default value. If the config is a
* custom config, it will apply the global state, and the custom value if needed.
*
* @param The setting
*/
template <typename Type> template <typename Type>
void WriteSettingGlobal(const QString& name, const Settings::Setting<Type>& setting, void WriteGlobalSetting(const Settings::Setting<Type>& setting);
const QVariant& default_value);
void WriteSettingGlobal(const QString& name, const QVariant& value, bool use_global, /**
const QVariant& default_value); * Reads a value from the qt_config using the setting's label and default value and applies the
* value to the setting.
*
* @param The setting
*/
template <typename Type>
void ReadBasicSetting(Settings::BasicSetting<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);
ConfigType type; ConfigType type;
std::unique_ptr<QSettings> qt_config; std::unique_ptr<QSettings> qt_config;

View file

@ -69,7 +69,7 @@ void ConfigureAudio::SetOutputSinkFromSinkID() {
[[maybe_unused]] const QSignalBlocker blocker(ui->output_sink_combo_box); [[maybe_unused]] const QSignalBlocker blocker(ui->output_sink_combo_box);
int new_sink_index = 0; int new_sink_index = 0;
const QString sink_id = QString::fromStdString(Settings::values.sink_id); const QString sink_id = QString::fromStdString(Settings::values.sink_id.GetValue());
for (int index = 0; index < ui->output_sink_combo_box->count(); index++) { for (int index = 0; index < ui->output_sink_combo_box->count(); index++) {
if (ui->output_sink_combo_box->itemText(index) == sink_id) { if (ui->output_sink_combo_box->itemText(index) == sink_id) {
new_sink_index = index; new_sink_index = index;
@ -83,7 +83,7 @@ void ConfigureAudio::SetOutputSinkFromSinkID() {
void ConfigureAudio::SetAudioDeviceFromDeviceID() { void ConfigureAudio::SetAudioDeviceFromDeviceID() {
int new_device_index = -1; int new_device_index = -1;
const QString device_id = QString::fromStdString(Settings::values.audio_device_id); const QString device_id = QString::fromStdString(Settings::values.audio_device_id.GetValue());
for (int index = 0; index < ui->audio_device_combo_box->count(); index++) { for (int index = 0; index < ui->audio_device_combo_box->count(); index++) {
if (ui->audio_device_combo_box->itemText(index) == device_id) { if (ui->audio_device_combo_box->itemText(index) == device_id) {
new_device_index = index; new_device_index = index;
@ -106,9 +106,9 @@ void ConfigureAudio::ApplyConfiguration() {
Settings::values.sink_id = Settings::values.sink_id =
ui->output_sink_combo_box->itemText(ui->output_sink_combo_box->currentIndex()) ui->output_sink_combo_box->itemText(ui->output_sink_combo_box->currentIndex())
.toStdString(); .toStdString();
Settings::values.audio_device_id = Settings::values.audio_device_id.SetValue(
ui->audio_device_combo_box->itemText(ui->audio_device_combo_box->currentIndex()) ui->audio_device_combo_box->itemText(ui->audio_device_combo_box->currentIndex())
.toStdString(); .toStdString());
// Guard if during game and set to game-specific value // Guard if during game and set to game-specific value
if (Settings::values.volume.UsingGlobal()) { if (Settings::values.volume.UsingGlobal()) {

View file

@ -24,23 +24,26 @@ void ConfigureCpuDebug::SetConfiguration() {
const bool runtime_lock = !Core::System::GetInstance().IsPoweredOn(); const bool runtime_lock = !Core::System::GetInstance().IsPoweredOn();
ui->cpuopt_page_tables->setEnabled(runtime_lock); ui->cpuopt_page_tables->setEnabled(runtime_lock);
ui->cpuopt_page_tables->setChecked(Settings::values.cpuopt_page_tables); ui->cpuopt_page_tables->setChecked(Settings::values.cpuopt_page_tables.GetValue());
ui->cpuopt_block_linking->setEnabled(runtime_lock); ui->cpuopt_block_linking->setEnabled(runtime_lock);
ui->cpuopt_block_linking->setChecked(Settings::values.cpuopt_block_linking); ui->cpuopt_block_linking->setChecked(Settings::values.cpuopt_block_linking.GetValue());
ui->cpuopt_return_stack_buffer->setEnabled(runtime_lock); ui->cpuopt_return_stack_buffer->setEnabled(runtime_lock);
ui->cpuopt_return_stack_buffer->setChecked(Settings::values.cpuopt_return_stack_buffer); ui->cpuopt_return_stack_buffer->setChecked(
Settings::values.cpuopt_return_stack_buffer.GetValue());
ui->cpuopt_fast_dispatcher->setEnabled(runtime_lock); ui->cpuopt_fast_dispatcher->setEnabled(runtime_lock);
ui->cpuopt_fast_dispatcher->setChecked(Settings::values.cpuopt_fast_dispatcher); ui->cpuopt_fast_dispatcher->setChecked(Settings::values.cpuopt_fast_dispatcher.GetValue());
ui->cpuopt_context_elimination->setEnabled(runtime_lock); ui->cpuopt_context_elimination->setEnabled(runtime_lock);
ui->cpuopt_context_elimination->setChecked(Settings::values.cpuopt_context_elimination); ui->cpuopt_context_elimination->setChecked(
Settings::values.cpuopt_context_elimination.GetValue());
ui->cpuopt_const_prop->setEnabled(runtime_lock); ui->cpuopt_const_prop->setEnabled(runtime_lock);
ui->cpuopt_const_prop->setChecked(Settings::values.cpuopt_const_prop); ui->cpuopt_const_prop->setChecked(Settings::values.cpuopt_const_prop.GetValue());
ui->cpuopt_misc_ir->setEnabled(runtime_lock); ui->cpuopt_misc_ir->setEnabled(runtime_lock);
ui->cpuopt_misc_ir->setChecked(Settings::values.cpuopt_misc_ir); ui->cpuopt_misc_ir->setChecked(Settings::values.cpuopt_misc_ir.GetValue());
ui->cpuopt_reduce_misalign_checks->setEnabled(runtime_lock); ui->cpuopt_reduce_misalign_checks->setEnabled(runtime_lock);
ui->cpuopt_reduce_misalign_checks->setChecked(Settings::values.cpuopt_reduce_misalign_checks); ui->cpuopt_reduce_misalign_checks->setChecked(
Settings::values.cpuopt_reduce_misalign_checks.GetValue());
ui->cpuopt_fastmem->setEnabled(runtime_lock); ui->cpuopt_fastmem->setEnabled(runtime_lock);
ui->cpuopt_fastmem->setChecked(Settings::values.cpuopt_fastmem); ui->cpuopt_fastmem->setChecked(Settings::values.cpuopt_fastmem.GetValue());
} }
void ConfigureCpuDebug::ApplyConfiguration() { void ConfigureCpuDebug::ApplyConfiguration() {

View file

@ -31,20 +31,21 @@ void ConfigureDebug::SetConfiguration() {
const bool runtime_lock = !Core::System::GetInstance().IsPoweredOn(); const bool runtime_lock = !Core::System::GetInstance().IsPoweredOn();
ui->toggle_console->setEnabled(runtime_lock); ui->toggle_console->setEnabled(runtime_lock);
ui->toggle_console->setChecked(UISettings::values.show_console); ui->toggle_console->setChecked(UISettings::values.show_console.GetValue());
ui->log_filter_edit->setText(QString::fromStdString(Settings::values.log_filter)); ui->log_filter_edit->setText(QString::fromStdString(Settings::values.log_filter.GetValue()));
ui->homebrew_args_edit->setText(QString::fromStdString(Settings::values.program_args)); ui->homebrew_args_edit->setText(
QString::fromStdString(Settings::values.program_args.GetValue()));
ui->fs_access_log->setEnabled(runtime_lock); ui->fs_access_log->setEnabled(runtime_lock);
ui->fs_access_log->setChecked(Settings::values.enable_fs_access_log); ui->fs_access_log->setChecked(Settings::values.enable_fs_access_log.GetValue());
ui->reporting_services->setChecked(Settings::values.reporting_services); ui->reporting_services->setChecked(Settings::values.reporting_services.GetValue());
ui->quest_flag->setChecked(Settings::values.quest_flag); ui->quest_flag->setChecked(Settings::values.quest_flag.GetValue());
ui->use_debug_asserts->setChecked(Settings::values.use_debug_asserts); ui->use_debug_asserts->setChecked(Settings::values.use_debug_asserts.GetValue());
ui->use_auto_stub->setChecked(Settings::values.use_auto_stub); ui->use_auto_stub->setChecked(Settings::values.use_auto_stub.GetValue());
ui->enable_graphics_debugging->setEnabled(runtime_lock); ui->enable_graphics_debugging->setEnabled(runtime_lock);
ui->enable_graphics_debugging->setChecked(Settings::values.renderer_debug); ui->enable_graphics_debugging->setChecked(Settings::values.renderer_debug.GetValue());
ui->disable_macro_jit->setEnabled(runtime_lock); ui->disable_macro_jit->setEnabled(runtime_lock);
ui->disable_macro_jit->setChecked(Settings::values.disable_macro_jit); ui->disable_macro_jit->setChecked(Settings::values.disable_macro_jit.GetValue());
ui->extended_logging->setChecked(Settings::values.extended_logging); ui->extended_logging->setChecked(Settings::values.extended_logging.GetValue());
} }
void ConfigureDebug::ApplyConfiguration() { void ConfigureDebug::ApplyConfiguration() {
@ -61,7 +62,7 @@ void ConfigureDebug::ApplyConfiguration() {
Settings::values.extended_logging = ui->extended_logging->isChecked(); Settings::values.extended_logging = ui->extended_logging->isChecked();
Debugger::ToggleConsole(); Debugger::ToggleConsole();
Common::Log::Filter filter; Common::Log::Filter filter;
filter.ParseFilterString(Settings::values.log_filter); filter.ParseFilterString(Settings::values.log_filter.GetValue());
Common::Log::SetGlobalFilter(filter); Common::Log::SetGlobalFilter(filter);
} }

View file

@ -43,18 +43,19 @@ void ConfigureFilesystem::setConfiguration() {
QString::fromStdString(Common::FS::GetYuzuPathString(Common::FS::YuzuPath::NANDDir))); QString::fromStdString(Common::FS::GetYuzuPathString(Common::FS::YuzuPath::NANDDir)));
ui->sdmc_directory_edit->setText( ui->sdmc_directory_edit->setText(
QString::fromStdString(Common::FS::GetYuzuPathString(Common::FS::YuzuPath::SDMCDir))); QString::fromStdString(Common::FS::GetYuzuPathString(Common::FS::YuzuPath::SDMCDir)));
ui->gamecard_path_edit->setText(QString::fromStdString(Settings::values.gamecard_path)); ui->gamecard_path_edit->setText(
QString::fromStdString(Settings::values.gamecard_path.GetValue()));
ui->dump_path_edit->setText( ui->dump_path_edit->setText(
QString::fromStdString(Common::FS::GetYuzuPathString(Common::FS::YuzuPath::DumpDir))); QString::fromStdString(Common::FS::GetYuzuPathString(Common::FS::YuzuPath::DumpDir)));
ui->load_path_edit->setText( ui->load_path_edit->setText(
QString::fromStdString(Common::FS::GetYuzuPathString(Common::FS::YuzuPath::LoadDir))); QString::fromStdString(Common::FS::GetYuzuPathString(Common::FS::YuzuPath::LoadDir)));
ui->gamecard_inserted->setChecked(Settings::values.gamecard_inserted); ui->gamecard_inserted->setChecked(Settings::values.gamecard_inserted.GetValue());
ui->gamecard_current_game->setChecked(Settings::values.gamecard_current_game); ui->gamecard_current_game->setChecked(Settings::values.gamecard_current_game.GetValue());
ui->dump_exefs->setChecked(Settings::values.dump_exefs); ui->dump_exefs->setChecked(Settings::values.dump_exefs.GetValue());
ui->dump_nso->setChecked(Settings::values.dump_nso); ui->dump_nso->setChecked(Settings::values.dump_nso.GetValue());
ui->cache_game_list->setChecked(UISettings::values.cache_game_list); ui->cache_game_list->setChecked(UISettings::values.cache_game_list.GetValue());
UpdateEnabledControls(); UpdateEnabledControls();
} }

View file

@ -40,10 +40,10 @@ void ConfigureGeneral::SetConfiguration() {
ui->use_multi_core->setEnabled(runtime_lock); ui->use_multi_core->setEnabled(runtime_lock);
ui->use_multi_core->setChecked(Settings::values.use_multi_core.GetValue()); ui->use_multi_core->setChecked(Settings::values.use_multi_core.GetValue());
ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing); ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing.GetValue());
ui->toggle_user_on_boot->setChecked(UISettings::values.select_user_on_boot); ui->toggle_user_on_boot->setChecked(UISettings::values.select_user_on_boot.GetValue());
ui->toggle_background_pause->setChecked(UISettings::values.pause_when_in_background); ui->toggle_background_pause->setChecked(UISettings::values.pause_when_in_background.GetValue());
ui->toggle_hide_mouse->setChecked(UISettings::values.hide_mouse); ui->toggle_hide_mouse->setChecked(UISettings::values.hide_mouse.GetValue());
ui->toggle_frame_limit->setChecked(Settings::values.use_frame_limit.GetValue()); ui->toggle_frame_limit->setChecked(Settings::values.use_frame_limit.GetValue());
ui->frame_limit->setValue(Settings::values.frame_limit.GetValue()); ui->frame_limit->setValue(Settings::values.frame_limit.GetValue());

View file

@ -148,12 +148,12 @@ void ConfigureInputAdvanced::LoadConfiguration() {
} }
} }
ui->debug_enabled->setChecked(Settings::values.debug_pad_enabled); ui->debug_enabled->setChecked(Settings::values.debug_pad_enabled.GetValue());
ui->mouse_enabled->setChecked(Settings::values.mouse_enabled); ui->mouse_enabled->setChecked(Settings::values.mouse_enabled.GetValue());
ui->keyboard_enabled->setChecked(Settings::values.keyboard_enabled); ui->keyboard_enabled->setChecked(Settings::values.keyboard_enabled.GetValue());
ui->emulate_analog_keyboard->setChecked(Settings::values.emulate_analog_keyboard); ui->emulate_analog_keyboard->setChecked(Settings::values.emulate_analog_keyboard.GetValue());
ui->mouse_panning->setChecked(Settings::values.mouse_panning); ui->mouse_panning->setChecked(Settings::values.mouse_panning.GetValue());
ui->mouse_panning_sensitivity->setValue(Settings::values.mouse_panning_sensitivity); ui->mouse_panning_sensitivity->setValue(Settings::values.mouse_panning_sensitivity.GetValue());
ui->touchscreen_enabled->setChecked(Settings::values.touchscreen.enabled); ui->touchscreen_enabled->setChecked(Settings::values.touchscreen.enabled);
UpdateUIEnabled(); UpdateUIEnabled();

View file

@ -101,15 +101,16 @@ ConfigureMotionTouch::ConfigureMotionTouch(QWidget* parent,
ConfigureMotionTouch::~ConfigureMotionTouch() = default; ConfigureMotionTouch::~ConfigureMotionTouch() = default;
void ConfigureMotionTouch::SetConfiguration() { void ConfigureMotionTouch::SetConfiguration() {
const Common::ParamPackage motion_param(Settings::values.motion_device); const Common::ParamPackage motion_param(Settings::values.motion_device.GetValue());
const Common::ParamPackage touch_param(Settings::values.touch_device); const Common::ParamPackage touch_param(Settings::values.touch_device.GetValue());
ui->touch_from_button_checkbox->setChecked(Settings::values.use_touch_from_button); ui->touch_from_button_checkbox->setChecked(Settings::values.use_touch_from_button.GetValue());
touch_from_button_maps = Settings::values.touch_from_button_maps; touch_from_button_maps = Settings::values.touch_from_button_maps;
for (const auto& touch_map : touch_from_button_maps) { for (const auto& touch_map : touch_from_button_maps) {
ui->touch_from_button_map->addItem(QString::fromStdString(touch_map.name)); ui->touch_from_button_map->addItem(QString::fromStdString(touch_map.name));
} }
ui->touch_from_button_map->setCurrentIndex(Settings::values.touch_from_button_map_index); ui->touch_from_button_map->setCurrentIndex(
Settings::values.touch_from_button_map_index.GetValue());
ui->motion_sensitivity->setValue(motion_param.Get("sensitivity", 0.01f)); ui->motion_sensitivity->setValue(motion_param.Get("sensitivity", 0.01f));
min_x = touch_param.Get("min_x", 100); min_x = touch_param.Get("min_x", 100);
@ -124,7 +125,7 @@ void ConfigureMotionTouch::SetConfiguration() {
udp_server_list_model->setStringList({}); udp_server_list_model->setStringList({});
ui->udp_server_list->setModel(udp_server_list_model); ui->udp_server_list->setModel(udp_server_list_model);
std::stringstream ss(Settings::values.udp_input_servers); std::stringstream ss(Settings::values.udp_input_servers.GetValue());
std::string token; std::string token;
while (std::getline(ss, token, ',')) { while (std::getline(ss, token, ',')) {

View file

@ -166,7 +166,7 @@ void ConfigureProfileManager::PopulateUserList() {
void ConfigureProfileManager::UpdateCurrentUser() { void ConfigureProfileManager::UpdateCurrentUser() {
ui->pm_add->setEnabled(profile_manager->GetUserCount() < Service::Account::MAX_USERS); ui->pm_add->setEnabled(profile_manager->GetUserCount() < Service::Account::MAX_USERS);
const auto& current_user = profile_manager->GetUser(Settings::values.current_user); const auto& current_user = profile_manager->GetUser(Settings::values.current_user.GetValue());
ASSERT(current_user); ASSERT(current_user);
const auto username = GetAccountUsername(*profile_manager, *current_user); const auto username = GetAccountUsername(*profile_manager, *current_user);
@ -245,15 +245,18 @@ void ConfigureProfileManager::DeleteUser() {
this, tr("Confirm Delete"), this, tr("Confirm Delete"),
tr("You are about to delete user with name \"%1\". Are you sure?").arg(username)); tr("You are about to delete user with name \"%1\". Are you sure?").arg(username));
if (confirm == QMessageBox::No) if (confirm == QMessageBox::No) {
return; return;
}
if (Settings::values.current_user == tree_view->currentIndex().row()) if (Settings::values.current_user.GetValue() == tree_view->currentIndex().row()) {
Settings::values.current_user = 0; Settings::values.current_user = 0;
}
UpdateCurrentUser(); UpdateCurrentUser();
if (!profile_manager->RemoveUser(*uuid)) if (!profile_manager->RemoveUser(*uuid)) {
return; return;
}
item_model->removeRows(tree_view->currentIndex().row(), 1); item_model->removeRows(tree_view->currentIndex().row(), 1);
tree_view->clearSelection(); tree_view->clearSelection();

View file

@ -65,7 +65,7 @@ void ConfigureService::RetranslateUi() {
void ConfigureService::SetConfiguration() { void ConfigureService::SetConfiguration() {
const int index = const int index =
ui->bcat_source->findData(QString::fromStdString(Settings::values.bcat_backend)); ui->bcat_source->findData(QString::fromStdString(Settings::values.bcat_backend.GetValue()));
ui->bcat_source->setCurrentIndex(index == -1 ? 0 : index); ui->bcat_source->setCurrentIndex(index == -1 ? 0 : index);
} }

View file

@ -113,11 +113,12 @@ void ConfigureUi::SetConfiguration() {
ui->theme_combobox->setCurrentIndex(ui->theme_combobox->findData(UISettings::values.theme)); ui->theme_combobox->setCurrentIndex(ui->theme_combobox->findData(UISettings::values.theme));
ui->language_combobox->setCurrentIndex( ui->language_combobox->setCurrentIndex(
ui->language_combobox->findData(UISettings::values.language)); ui->language_combobox->findData(UISettings::values.language));
ui->show_add_ons->setChecked(UISettings::values.show_add_ons); ui->show_add_ons->setChecked(UISettings::values.show_add_ons.GetValue());
ui->icon_size_combobox->setCurrentIndex( ui->icon_size_combobox->setCurrentIndex(
ui->icon_size_combobox->findData(UISettings::values.icon_size)); ui->icon_size_combobox->findData(UISettings::values.icon_size.GetValue()));
ui->enable_screenshot_save_as->setChecked(UISettings::values.enable_screenshot_save_as); ui->enable_screenshot_save_as->setChecked(
UISettings::values.enable_screenshot_save_as.GetValue());
ui->screenshot_path_edit->setText(QString::fromStdString( ui->screenshot_path_edit->setText(QString::fromStdString(
Common::FS::GetYuzuPathString(Common::FS::YuzuPath::ScreenshotsDir))); Common::FS::GetYuzuPathString(Common::FS::YuzuPath::ScreenshotsDir)));
} }
@ -178,7 +179,7 @@ void ConfigureUi::InitializeRowComboBoxes() {
void ConfigureUi::UpdateFirstRowComboBox(bool init) { void ConfigureUi::UpdateFirstRowComboBox(bool init) {
const int currentIndex = const int currentIndex =
init ? UISettings::values.row_1_text_id init ? UISettings::values.row_1_text_id.GetValue()
: ui->row_1_text_combobox->findData(ui->row_1_text_combobox->currentData()); : ui->row_1_text_combobox->findData(ui->row_1_text_combobox->currentData());
ui->row_1_text_combobox->clear(); ui->row_1_text_combobox->clear();
@ -197,7 +198,7 @@ void ConfigureUi::UpdateFirstRowComboBox(bool init) {
void ConfigureUi::UpdateSecondRowComboBox(bool init) { void ConfigureUi::UpdateSecondRowComboBox(bool init) {
const int currentIndex = const int currentIndex =
init ? UISettings::values.row_2_text_id init ? UISettings::values.row_2_text_id.GetValue()
: ui->row_2_text_combobox->findData(ui->row_2_text_combobox->currentData()); : ui->row_2_text_combobox->findData(ui->row_2_text_combobox->currentData());
ui->row_2_text_combobox->clear(); ui->row_2_text_combobox->clear();

View file

@ -88,22 +88,22 @@ void ConfigureWeb::SetConfiguration() {
ui->web_signup_link->setOpenExternalLinks(true); ui->web_signup_link->setOpenExternalLinks(true);
ui->web_token_info_link->setOpenExternalLinks(true); ui->web_token_info_link->setOpenExternalLinks(true);
if (Settings::values.yuzu_username.empty()) { if (Settings::values.yuzu_username.GetValue().empty()) {
ui->username->setText(tr("Unspecified")); ui->username->setText(tr("Unspecified"));
} else { } else {
ui->username->setText(QString::fromStdString(Settings::values.yuzu_username)); ui->username->setText(QString::fromStdString(Settings::values.yuzu_username.GetValue()));
} }
ui->toggle_telemetry->setChecked(Settings::values.enable_telemetry); ui->toggle_telemetry->setChecked(Settings::values.enable_telemetry.GetValue());
ui->edit_token->setText(QString::fromStdString( ui->edit_token->setText(QString::fromStdString(GenerateDisplayToken(
GenerateDisplayToken(Settings::values.yuzu_username, Settings::values.yuzu_token))); Settings::values.yuzu_username.GetValue(), Settings::values.yuzu_token.GetValue())));
// Connect after setting the values, to avoid calling OnLoginChanged now // Connect after setting the values, to avoid calling OnLoginChanged now
connect(ui->edit_token, &QLineEdit::textChanged, this, &ConfigureWeb::OnLoginChanged); connect(ui->edit_token, &QLineEdit::textChanged, this, &ConfigureWeb::OnLoginChanged);
user_verified = true; user_verified = true;
ui->toggle_discordrpc->setChecked(UISettings::values.enable_discord_presence); ui->toggle_discordrpc->setChecked(UISettings::values.enable_discord_presence.GetValue());
} }
void ConfigureWeb::ApplyConfiguration() { void ConfigureWeb::ApplyConfiguration() {

View file

@ -15,10 +15,10 @@
namespace Debugger { namespace Debugger {
void ToggleConsole() { void ToggleConsole() {
static bool console_shown = false; static bool console_shown = false;
if (console_shown == UISettings::values.show_console) { if (console_shown == UISettings::values.show_console.GetValue()) {
return; return;
} else { } else {
console_shown = UISettings::values.show_console; console_shown = UISettings::values.show_console.GetValue();
} }
#if defined(_WIN32) && !defined(_DEBUG) #if defined(_WIN32) && !defined(_DEBUG)

View file

@ -244,7 +244,8 @@ void GameList::OnUpdateThemedIcons() {
for (int i = 0; i < item_model->invisibleRootItem()->rowCount(); i++) { for (int i = 0; i < item_model->invisibleRootItem()->rowCount(); i++) {
QStandardItem* child = item_model->invisibleRootItem()->child(i); QStandardItem* child = item_model->invisibleRootItem()->child(i);
const int icon_size = std::min(static_cast<int>(UISettings::values.icon_size), 64); const int icon_size =
std::min(static_cast<int>(UISettings::values.icon_size.GetValue()), 64);
switch (child->data(GameListItem::TypeRole).value<GameListItemType>()) { switch (child->data(GameListItem::TypeRole).value<GameListItemType>()) {
case GameListItemType::SdmcDir: case GameListItemType::SdmcDir:
child->setData( child->setData(

View file

@ -80,7 +80,7 @@ public:
setData(qulonglong(program_id), ProgramIdRole); setData(qulonglong(program_id), ProgramIdRole);
setData(game_type, FileTypeRole); setData(game_type, FileTypeRole);
const u32 size = UISettings::values.icon_size; const u32 size = UISettings::values.icon_size.GetValue();
QPixmap picture; QPixmap picture;
if (!picture.loadFromData(picture_data.data(), static_cast<u32>(picture_data.size()))) { if (!picture.loadFromData(picture_data.data(), static_cast<u32>(picture_data.size()))) {
@ -108,8 +108,8 @@ public:
data(TitleRole).toString(), data(TitleRole).toString(),
}}; }};
const auto& row1 = row_data.at(UISettings::values.row_1_text_id); const auto& row1 = row_data.at(UISettings::values.row_1_text_id.GetValue());
const int row2_id = UISettings::values.row_2_text_id; const int row2_id = UISettings::values.row_2_text_id.GetValue();
if (role == SortRole) { if (role == SortRole) {
return row1.toLower(); return row1.toLower();
@ -233,7 +233,8 @@ public:
UISettings::GameDir* game_dir = &directory; UISettings::GameDir* game_dir = &directory;
setData(QVariant(UISettings::values.game_dirs.indexOf(directory)), GameDirRole); setData(QVariant(UISettings::values.game_dirs.indexOf(directory)), GameDirRole);
const int icon_size = std::min(static_cast<int>(UISettings::values.icon_size), 64); const int icon_size =
std::min(static_cast<int>(UISettings::values.icon_size.GetValue()), 64);
switch (dir_type) { switch (dir_type) {
case GameListItemType::SdmcDir: case GameListItemType::SdmcDir:
setData( setData(
@ -294,7 +295,8 @@ public:
explicit GameListAddDir() { explicit GameListAddDir() {
setData(type(), TypeRole); setData(type(), TypeRole);
const int icon_size = std::min(static_cast<int>(UISettings::values.icon_size), 64); const int icon_size =
std::min(static_cast<int>(UISettings::values.icon_size.GetValue()), 64);
setData(QIcon::fromTheme(QStringLiteral("plus")) setData(QIcon::fromTheme(QStringLiteral("plus"))
.pixmap(icon_size) .pixmap(icon_size)
.scaled(icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), .scaled(icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation),
@ -316,7 +318,8 @@ public:
explicit GameListFavorites() { explicit GameListFavorites() {
setData(type(), TypeRole); setData(type(), TypeRole);
const int icon_size = std::min(static_cast<int>(UISettings::values.icon_size), 64); const int icon_size =
std::min(static_cast<int>(UISettings::values.icon_size.GetValue()), 64);
setData(QIcon::fromTheme(QStringLiteral("star")) setData(QIcon::fromTheme(QStringLiteral("star"))
.pixmap(icon_size) .pixmap(icon_size)
.scaled(icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), .scaled(icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation),

View file

@ -156,11 +156,13 @@ enum class CalloutFlag : uint32_t {
}; };
void GMainWindow::ShowTelemetryCallout() { void GMainWindow::ShowTelemetryCallout() {
if (UISettings::values.callout_flags & static_cast<uint32_t>(CalloutFlag::Telemetry)) { if (UISettings::values.callout_flags.GetValue() &
static_cast<uint32_t>(CalloutFlag::Telemetry)) {
return; return;
} }
UISettings::values.callout_flags |= static_cast<uint32_t>(CalloutFlag::Telemetry); UISettings::values.callout_flags =
UISettings::values.callout_flags.GetValue() | static_cast<uint32_t>(CalloutFlag::Telemetry);
const QString telemetry_message = const QString telemetry_message =
tr("<a href='https://yuzu-emu.org/help/feature/telemetry/'>Anonymous " tr("<a href='https://yuzu-emu.org/help/feature/telemetry/'>Anonymous "
"data is collected</a> to help improve yuzu. " "data is collected</a> to help improve yuzu. "
@ -177,7 +179,7 @@ static void InitializeLogging() {
using namespace Common; using namespace Common;
Log::Filter log_filter; Log::Filter log_filter;
log_filter.ParseFilterString(Settings::values.log_filter); log_filter.ParseFilterString(Settings::values.log_filter.GetValue());
Log::SetGlobalFilter(log_filter); Log::SetGlobalFilter(log_filter);
const auto log_dir = FS::GetYuzuPath(FS::YuzuPath::LogDir); const auto log_dir = FS::GetYuzuPath(FS::YuzuPath::LogDir);
@ -216,7 +218,7 @@ GMainWindow::GMainWindow()
default_theme_paths = QIcon::themeSearchPaths(); default_theme_paths = QIcon::themeSearchPaths();
UpdateUITheme(); UpdateUITheme();
SetDiscordEnabled(UISettings::values.enable_discord_presence); SetDiscordEnabled(UISettings::values.enable_discord_presence.GetValue());
discord_rpc->Update(); discord_rpc->Update();
RegisterMetaTypes(); RegisterMetaTypes();
@ -1060,23 +1062,24 @@ void GMainWindow::RestoreUIState() {
render_window->restoreGeometry(UISettings::values.renderwindow_geometry); render_window->restoreGeometry(UISettings::values.renderwindow_geometry);
#if MICROPROFILE_ENABLED #if MICROPROFILE_ENABLED
microProfileDialog->restoreGeometry(UISettings::values.microprofile_geometry); microProfileDialog->restoreGeometry(UISettings::values.microprofile_geometry);
microProfileDialog->setVisible(UISettings::values.microprofile_visible); microProfileDialog->setVisible(UISettings::values.microprofile_visible.GetValue());
#endif #endif
game_list->LoadInterfaceLayout(); game_list->LoadInterfaceLayout();
ui.action_Single_Window_Mode->setChecked(UISettings::values.single_window_mode); ui.action_Single_Window_Mode->setChecked(UISettings::values.single_window_mode.GetValue());
ToggleWindowMode(); ToggleWindowMode();
ui.action_Fullscreen->setChecked(UISettings::values.fullscreen); ui.action_Fullscreen->setChecked(UISettings::values.fullscreen.GetValue());
ui.action_Display_Dock_Widget_Headers->setChecked(UISettings::values.display_titlebar); ui.action_Display_Dock_Widget_Headers->setChecked(
UISettings::values.display_titlebar.GetValue());
OnDisplayTitleBars(ui.action_Display_Dock_Widget_Headers->isChecked()); OnDisplayTitleBars(ui.action_Display_Dock_Widget_Headers->isChecked());
ui.action_Show_Filter_Bar->setChecked(UISettings::values.show_filter_bar); ui.action_Show_Filter_Bar->setChecked(UISettings::values.show_filter_bar.GetValue());
game_list->SetFilterVisible(ui.action_Show_Filter_Bar->isChecked()); game_list->SetFilterVisible(ui.action_Show_Filter_Bar->isChecked());
ui.action_Show_Status_Bar->setChecked(UISettings::values.show_status_bar); ui.action_Show_Status_Bar->setChecked(UISettings::values.show_status_bar.GetValue());
statusBar()->setVisible(ui.action_Show_Status_Bar->isChecked()); statusBar()->setVisible(ui.action_Show_Status_Bar->isChecked());
Debugger::ToggleConsole(); Debugger::ToggleConsole();
} }
@ -1243,13 +1246,14 @@ bool GMainWindow::LoadROM(const QString& filename, std::size_t program_index) {
const Core::System::ResultStatus result{ const Core::System::ResultStatus result{
system.Load(*render_window, filename.toStdString(), program_index)}; system.Load(*render_window, filename.toStdString(), program_index)};
const auto drd_callout = const auto drd_callout = (UISettings::values.callout_flags.GetValue() &
(UISettings::values.callout_flags & static_cast<u32>(CalloutFlag::DRDDeprecation)) == 0; static_cast<u32>(CalloutFlag::DRDDeprecation)) == 0;
if (result == Core::System::ResultStatus::Success && if (result == Core::System::ResultStatus::Success &&
system.GetAppLoader().GetFileType() == Loader::FileType::DeconstructedRomDirectory && system.GetAppLoader().GetFileType() == Loader::FileType::DeconstructedRomDirectory &&
drd_callout) { drd_callout) {
UISettings::values.callout_flags |= static_cast<u32>(CalloutFlag::DRDDeprecation); UISettings::values.callout_flags = UISettings::values.callout_flags.GetValue() |
static_cast<u32>(CalloutFlag::DRDDeprecation);
QMessageBox::warning( QMessageBox::warning(
this, tr("Warning Outdated Game Format"), this, tr("Warning Outdated Game Format"),
tr("You are using the deconstructed ROM directory format for this game, which is an " tr("You are using the deconstructed ROM directory format for this game, which is an "
@ -2453,7 +2457,8 @@ void GMainWindow::ErrorDisplayDisplayError(QString error_code, QString error_tex
} }
void GMainWindow::OnMenuReportCompatibility() { void GMainWindow::OnMenuReportCompatibility() {
if (!Settings::values.yuzu_token.empty() && !Settings::values.yuzu_username.empty()) { if (!Settings::values.yuzu_token.GetValue().empty() &&
!Settings::values.yuzu_username.GetValue().empty()) {
CompatDB compatdb{this}; CompatDB compatdb{this};
compatdb.exec(); compatdb.exec();
} else { } else {
@ -2618,7 +2623,7 @@ void GMainWindow::ResetWindowSize1080() {
void GMainWindow::OnConfigure() { void GMainWindow::OnConfigure() {
const auto old_theme = UISettings::values.theme; const auto old_theme = UISettings::values.theme;
const bool old_discord_presence = UISettings::values.enable_discord_presence; const bool old_discord_presence = UISettings::values.enable_discord_presence.GetValue();
ConfigureDialog configure_dialog(this, hotkey_registry, input_subsystem.get()); ConfigureDialog configure_dialog(this, hotkey_registry, input_subsystem.get());
connect(&configure_dialog, &ConfigureDialog::LanguageChanged, this, connect(&configure_dialog, &ConfigureDialog::LanguageChanged, this,
@ -2675,8 +2680,8 @@ void GMainWindow::OnConfigure() {
if (UISettings::values.theme != old_theme) { if (UISettings::values.theme != old_theme) {
UpdateUITheme(); UpdateUITheme();
} }
if (UISettings::values.enable_discord_presence != old_discord_presence) { if (UISettings::values.enable_discord_presence.GetValue() != old_discord_presence) {
SetDiscordEnabled(UISettings::values.enable_discord_presence); SetDiscordEnabled(UISettings::values.enable_discord_presence.GetValue());
} }
emit UpdateThemedIcons(); emit UpdateThemedIcons();
@ -2832,7 +2837,8 @@ void GMainWindow::OnCaptureScreenshot() {
} }
} }
#endif #endif
render_window->CaptureScreenshot(UISettings::values.screenshot_resolution_factor, filename); render_window->CaptureScreenshot(UISettings::values.screenshot_resolution_factor.GetValue(),
filename);
OnStartGame(); OnStartGame();
} }

View file

@ -13,6 +13,7 @@
#include <QStringList> #include <QStringList>
#include <QVector> #include <QVector>
#include "common/common_types.h" #include "common/common_types.h"
#include "common/settings.h"
namespace UISettings { namespace UISettings {
@ -48,26 +49,26 @@ struct Values {
QByteArray gamelist_header_state; QByteArray gamelist_header_state;
QByteArray microprofile_geometry; QByteArray microprofile_geometry;
bool microprofile_visible; Settings::BasicSetting<bool> microprofile_visible{false, "microProfileDialogVisible"};
bool single_window_mode; Settings::BasicSetting<bool> single_window_mode{true, "singleWindowMode"};
bool fullscreen; Settings::BasicSetting<bool> fullscreen{false, "fullscreen"};
bool display_titlebar; Settings::BasicSetting<bool> display_titlebar{true, "displayTitleBars"};
bool show_filter_bar; Settings::BasicSetting<bool> show_filter_bar{true, "showFilterBar"};
bool show_status_bar; Settings::BasicSetting<bool> show_status_bar{true, "showStatusBar"};
bool confirm_before_closing; Settings::BasicSetting<bool> confirm_before_closing{true, "confirmClose"};
bool first_start; Settings::BasicSetting<bool> first_start{true, "firstStart"};
bool pause_when_in_background; Settings::BasicSetting<bool> pause_when_in_background{false, "pauseWhenInBackground"};
bool hide_mouse; Settings::BasicSetting<bool> hide_mouse{false, "hideInactiveMouse"};
bool select_user_on_boot; Settings::BasicSetting<bool> select_user_on_boot{false, "select_user_on_boot"};
// Discord RPC // Discord RPC
bool enable_discord_presence; Settings::BasicSetting<bool> enable_discord_presence{true, "enable_discord_presence"};
bool enable_screenshot_save_as; Settings::BasicSetting<bool> enable_screenshot_save_as{true, "enable_screenshot_save_as"};
u16 screenshot_resolution_factor; Settings::BasicSetting<u16> screenshot_resolution_factor{0, "screenshot_resolution_factor"};
QString roms_path; QString roms_path;
QString symbols_path; QString symbols_path;
@ -83,18 +84,18 @@ struct Values {
// Shortcut name <Shortcut, context> // Shortcut name <Shortcut, context>
std::vector<Shortcut> shortcuts; std::vector<Shortcut> shortcuts;
uint32_t callout_flags; Settings::BasicSetting<uint32_t> callout_flags{0, "calloutFlags"};
// logging // logging
bool show_console; Settings::BasicSetting<bool> show_console{false, "showConsole"};
// Game List // Game List
bool show_add_ons; Settings::BasicSetting<bool> show_add_ons{true, "show_add_ons"};
uint32_t icon_size; Settings::BasicSetting<uint32_t> icon_size{64, "icon_size"};
uint8_t row_1_text_id; Settings::BasicSetting<uint8_t> row_1_text_id{3, "row_1_text_id"};
uint8_t row_2_text_id; Settings::BasicSetting<uint8_t> row_2_text_id{2, "row_2_text_id"};
std::atomic_bool is_game_list_reload_pending{false}; std::atomic_bool is_game_list_reload_pending{false};
bool cache_game_list; Settings::BasicSetting<bool> cache_game_list{true, "cache_game_list"};
bool configuration_applied; bool configuration_applied;
bool reset_to_defaults; bool reset_to_defaults;

View file

@ -241,6 +241,24 @@ static const std::array<int, 8> keyboard_mods{
SDL_SCANCODE_RCTRL, SDL_SCANCODE_RSHIFT, SDL_SCANCODE_RALT, SDL_SCANCODE_RGUI, SDL_SCANCODE_RCTRL, SDL_SCANCODE_RSHIFT, SDL_SCANCODE_RALT, SDL_SCANCODE_RGUI,
}; };
template <>
void Config::ReadSetting(const std::string& group, Settings::BasicSetting<float>& setting) {
setting = sdl2_config->GetReal(group, setting.GetLabel(), setting.GetDefault());
}
template <>
void Config::ReadSetting(const std::string& group, Settings::BasicSetting<std::string>& setting) {
setting = sdl2_config->Get(group, setting.GetLabel(), setting.GetDefault());
}
template <>
void Config::ReadSetting(const std::string& group, Settings::BasicSetting<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) {
setting = static_cast<Type>(sdl2_config->GetInteger(group, setting.GetLabel(),
static_cast<long>(setting.GetDefault())));
}
void Config::ReadValues() { void Config::ReadValues() {
// Controls // Controls
for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) { for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) {
@ -264,8 +282,7 @@ void Config::ReadValues() {
} }
} }
Settings::values.mouse_enabled = ReadSetting("ControlsGeneral", Settings::values.mouse_enabled);
sdl2_config->GetBoolean("ControlsGeneral", "mouse_enabled", false);
for (int i = 0; i < Settings::NativeMouseButton::NumMouseButtons; ++i) { for (int i = 0; i < Settings::NativeMouseButton::NumMouseButtons; ++i) {
std::string default_param = InputCommon::GenerateKeyboardParam(default_mouse_buttons[i]); std::string default_param = InputCommon::GenerateKeyboardParam(default_mouse_buttons[i]);
Settings::values.mouse_buttons[i] = sdl2_config->Get( Settings::values.mouse_buttons[i] = sdl2_config->Get(
@ -275,14 +292,11 @@ void Config::ReadValues() {
Settings::values.mouse_buttons[i] = default_param; Settings::values.mouse_buttons[i] = default_param;
} }
Settings::values.motion_device = sdl2_config->Get( ReadSetting("ControlsGeneral", Settings::values.motion_device);
"ControlsGeneral", "motion_device", "engine:motion_emu,update_period:100,sensitivity:0.01");
Settings::values.keyboard_enabled = ReadSetting("ControlsGeneral", Settings::values.keyboard_enabled);
sdl2_config->GetBoolean("ControlsGeneral", "keyboard_enabled", false);
Settings::values.debug_pad_enabled = ReadSetting("ControlsGeneral", Settings::values.debug_pad_enabled);
sdl2_config->GetBoolean("ControlsGeneral", "debug_pad_enabled", false);
for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
Settings::values.debug_pad_buttons[i] = sdl2_config->Get( Settings::values.debug_pad_buttons[i] = sdl2_config->Get(
@ -303,12 +317,9 @@ void Config::ReadValues() {
Settings::values.debug_pad_analogs[i] = default_param; Settings::values.debug_pad_analogs[i] = default_param;
} }
Settings::values.vibration_enabled.SetValue( ReadSetting("ControlsGeneral", Settings::values.vibration_enabled);
sdl2_config->GetBoolean("ControlsGeneral", "vibration_enabled", true)); ReadSetting("ControlsGeneral", Settings::values.enable_accurate_vibrations);
Settings::values.enable_accurate_vibrations.SetValue( ReadSetting("ControlsGeneral", Settings::values.motion_enabled);
sdl2_config->GetBoolean("ControlsGeneral", "enable_accurate_vibrations", false));
Settings::values.motion_enabled.SetValue(
sdl2_config->GetBoolean("ControlsGeneral", "motion_enabled", true));
Settings::values.touchscreen.enabled = Settings::values.touchscreen.enabled =
sdl2_config->GetBoolean("ControlsGeneral", "touch_enabled", true); sdl2_config->GetBoolean("ControlsGeneral", "touch_enabled", true);
Settings::values.touchscreen.rotation_angle = Settings::values.touchscreen.rotation_angle =
@ -349,13 +360,11 @@ void Config::ReadValues() {
Settings::TouchFromButtonMap{"default", {}}); Settings::TouchFromButtonMap{"default", {}});
num_touch_from_button_maps = 1; num_touch_from_button_maps = 1;
} }
Settings::values.use_touch_from_button = ReadSetting("ControlsGeneral", Settings::values.use_touch_from_button);
sdl2_config->GetBoolean("ControlsGeneral", "use_touch_from_button", false); Settings::values.touch_from_button_map_index = std::clamp(
Settings::values.touch_from_button_map_index = Settings::values.touch_from_button_map_index.GetValue(), 0, num_touch_from_button_maps - 1);
std::clamp(Settings::values.touch_from_button_map_index, 0, num_touch_from_button_maps - 1);
Settings::values.udp_input_servers = ReadSetting("ControlsGeneral", Settings::values.udp_input_servers);
sdl2_config->Get("Controls", "udp_input_address", InputCommon::CemuhookUDP::DEFAULT_SRV);
std::transform(keyboard_keys.begin(), keyboard_keys.end(), std::transform(keyboard_keys.begin(), keyboard_keys.end(),
Settings::values.keyboard_keys.begin(), InputCommon::GenerateKeyboardParam); Settings::values.keyboard_keys.begin(), InputCommon::GenerateKeyboardParam);
@ -367,8 +376,7 @@ void Config::ReadValues() {
Settings::values.keyboard_mods.begin(), InputCommon::GenerateKeyboardParam); Settings::values.keyboard_mods.begin(), InputCommon::GenerateKeyboardParam);
// Data Storage // Data Storage
Settings::values.use_virtual_sd = ReadSetting("Data Storage", Settings::values.use_virtual_sd);
sdl2_config->GetBoolean("Data Storage", "use_virtual_sd", true);
FS::SetYuzuPath(FS::YuzuPath::NANDDir, FS::SetYuzuPath(FS::YuzuPath::NANDDir,
sdl2_config->Get("Data Storage", "nand_directory", sdl2_config->Get("Data Storage", "nand_directory",
FS::GetYuzuPathString(FS::YuzuPath::NANDDir))); FS::GetYuzuPathString(FS::YuzuPath::NANDDir)));
@ -381,18 +389,16 @@ void Config::ReadValues() {
FS::SetYuzuPath(FS::YuzuPath::DumpDir, FS::SetYuzuPath(FS::YuzuPath::DumpDir,
sdl2_config->Get("Data Storage", "dump_directory", sdl2_config->Get("Data Storage", "dump_directory",
FS::GetYuzuPathString(FS::YuzuPath::DumpDir))); FS::GetYuzuPathString(FS::YuzuPath::DumpDir)));
Settings::values.gamecard_inserted = ReadSetting("Data Storage", Settings::values.gamecard_inserted);
sdl2_config->GetBoolean("Data Storage", "gamecard_inserted", false); ReadSetting("Data Storage", Settings::values.gamecard_current_game);
Settings::values.gamecard_current_game = ReadSetting("Data Storage", Settings::values.gamecard_path);
sdl2_config->GetBoolean("Data Storage", "gamecard_current_game", false);
Settings::values.gamecard_path = sdl2_config->Get("Data Storage", "gamecard_path", "");
// System // System
Settings::values.use_docked_mode.SetValue( ReadSetting("System", Settings::values.use_docked_mode);
sdl2_config->GetBoolean("System", "use_docked_mode", true));
Settings::values.current_user = std::clamp<int>( ReadSetting("System", Settings::values.current_user);
sdl2_config->GetInteger("System", "current_user", 0), 0, Service::Account::MAX_USERS - 1); Settings::values.current_user = std::clamp<int>(Settings::values.current_user.GetValue(), 0,
Service::Account::MAX_USERS - 1);
const auto rng_seed_enabled = sdl2_config->GetBoolean("System", "rng_seed_enabled", false); const auto rng_seed_enabled = sdl2_config->GetBoolean("System", "rng_seed_enabled", false);
if (rng_seed_enabled) { if (rng_seed_enabled) {
@ -409,89 +415,60 @@ void Config::ReadValues() {
Settings::values.custom_rtc = std::nullopt; Settings::values.custom_rtc = std::nullopt;
} }
Settings::values.language_index.SetValue( ReadSetting("System", Settings::values.language_index);
sdl2_config->GetInteger("System", "language_index", 1)); ReadSetting("System", Settings::values.time_zone_index);
Settings::values.time_zone_index.SetValue(
sdl2_config->GetInteger("System", "time_zone_index", 0));
// Core // Core
Settings::values.use_multi_core.SetValue( ReadSetting("Core", Settings::values.use_multi_core);
sdl2_config->GetBoolean("Core", "use_multi_core", true));
// Renderer // Renderer
const int renderer_backend = sdl2_config->GetInteger( ReadSetting("Renderer", Settings::values.renderer_backend);
"Renderer", "backend", static_cast<int>(Settings::RendererBackend::OpenGL)); ReadSetting("Renderer", Settings::values.renderer_debug);
Settings::values.renderer_backend.SetValue( ReadSetting("Renderer", Settings::values.vulkan_device);
static_cast<Settings::RendererBackend>(renderer_backend));
Settings::values.renderer_debug = sdl2_config->GetBoolean("Renderer", "debug", false);
Settings::values.vulkan_device.SetValue(
sdl2_config->GetInteger("Renderer", "vulkan_device", 0));
Settings::values.aspect_ratio.SetValue( ReadSetting("Renderer", Settings::values.aspect_ratio);
static_cast<int>(sdl2_config->GetInteger("Renderer", "aspect_ratio", 0))); ReadSetting("Renderer", Settings::values.max_anisotropy);
Settings::values.max_anisotropy.SetValue( ReadSetting("Renderer", Settings::values.use_frame_limit);
static_cast<int>(sdl2_config->GetInteger("Renderer", "max_anisotropy", 0))); ReadSetting("Renderer", Settings::values.frame_limit);
Settings::values.use_frame_limit.SetValue( ReadSetting("Renderer", Settings::values.use_disk_shader_cache);
sdl2_config->GetBoolean("Renderer", "use_frame_limit", true)); ReadSetting("Renderer", Settings::values.gpu_accuracy);
Settings::values.frame_limit.SetValue( ReadSetting("Renderer", Settings::values.use_asynchronous_gpu_emulation);
static_cast<u16>(sdl2_config->GetInteger("Renderer", "frame_limit", 100))); ReadSetting("Renderer", Settings::values.use_vsync);
Settings::values.use_disk_shader_cache.SetValue( ReadSetting("Renderer", Settings::values.disable_fps_limit);
sdl2_config->GetBoolean("Renderer", "use_disk_shader_cache", false)); ReadSetting("Renderer", Settings::values.use_assembly_shaders);
const int gpu_accuracy_level = sdl2_config->GetInteger("Renderer", "gpu_accuracy", 1); ReadSetting("Renderer", Settings::values.use_asynchronous_shaders);
Settings::values.gpu_accuracy.SetValue(static_cast<Settings::GPUAccuracy>(gpu_accuracy_level)); ReadSetting("Renderer", Settings::values.use_nvdec_emulation);
Settings::values.use_asynchronous_gpu_emulation.SetValue( ReadSetting("Renderer", Settings::values.accelerate_astc);
sdl2_config->GetBoolean("Renderer", "use_asynchronous_gpu_emulation", true)); ReadSetting("Renderer", Settings::values.use_fast_gpu_time);
Settings::values.use_vsync.SetValue(
static_cast<u16>(sdl2_config->GetInteger("Renderer", "use_vsync", 1)));
Settings::values.disable_fps_limit.SetValue(
sdl2_config->GetBoolean("Renderer", "disable_fps_limit", false));
Settings::values.use_assembly_shaders.SetValue(
sdl2_config->GetBoolean("Renderer", "use_assembly_shaders", true));
Settings::values.use_asynchronous_shaders.SetValue(
sdl2_config->GetBoolean("Renderer", "use_asynchronous_shaders", false));
Settings::values.use_nvdec_emulation.SetValue(
sdl2_config->GetBoolean("Renderer", "use_nvdec_emulation", true));
Settings::values.accelerate_astc.SetValue(
sdl2_config->GetBoolean("Renderer", "accelerate_astc", true));
Settings::values.use_fast_gpu_time.SetValue(
sdl2_config->GetBoolean("Renderer", "use_fast_gpu_time", true));
Settings::values.bg_red.SetValue( ReadSetting("Renderer", Settings::values.bg_red);
static_cast<float>(sdl2_config->GetReal("Renderer", "bg_red", 0.0))); ReadSetting("Renderer", Settings::values.bg_green);
Settings::values.bg_green.SetValue( ReadSetting("Renderer", Settings::values.bg_blue);
static_cast<float>(sdl2_config->GetReal("Renderer", "bg_green", 0.0)));
Settings::values.bg_blue.SetValue(
static_cast<float>(sdl2_config->GetReal("Renderer", "bg_blue", 0.0)));
// Audio // Audio
Settings::values.sink_id = sdl2_config->Get("Audio", "output_engine", "auto"); ReadSetting("Audio", Settings::values.sink_id);
Settings::values.enable_audio_stretching.SetValue( ReadSetting("Audio", Settings::values.enable_audio_stretching);
sdl2_config->GetBoolean("Audio", "enable_audio_stretching", true)); ReadSetting("Audio", Settings::values.audio_device_id);
Settings::values.audio_device_id = sdl2_config->Get("Audio", "output_device", "auto"); ReadSetting("Audio", Settings::values.volume);
Settings::values.volume.SetValue(
static_cast<float>(sdl2_config->GetReal("Audio", "volume", 1)));
// Miscellaneous // Miscellaneous
Settings::values.log_filter = sdl2_config->Get("Miscellaneous", "log_filter", "*:Trace"); // log_filter has a different default here than from common
Settings::values.use_dev_keys = sdl2_config->GetBoolean("Miscellaneous", "use_dev_keys", false); Settings::values.log_filter =
sdl2_config->Get("Miscellaneous", Settings::values.log_filter.GetLabel(), "*:Trace");
ReadSetting("Miscellaneous", Settings::values.use_dev_keys);
// Debugging // Debugging
Settings::values.record_frame_times = Settings::values.record_frame_times =
sdl2_config->GetBoolean("Debugging", "record_frame_times", false); sdl2_config->GetBoolean("Debugging", "record_frame_times", false);
Settings::values.program_args = sdl2_config->Get("Debugging", "program_args", ""); ReadSetting("Debugging", Settings::values.program_args);
Settings::values.dump_exefs = sdl2_config->GetBoolean("Debugging", "dump_exefs", false); ReadSetting("Debugging", Settings::values.dump_exefs);
Settings::values.dump_nso = sdl2_config->GetBoolean("Debugging", "dump_nso", false); ReadSetting("Debugging", Settings::values.dump_nso);
Settings::values.enable_fs_access_log = ReadSetting("Debugging", Settings::values.enable_fs_access_log);
sdl2_config->GetBoolean("Debugging", "enable_fs_access_log", false); ReadSetting("Debugging", Settings::values.reporting_services);
Settings::values.reporting_services = ReadSetting("Debugging", Settings::values.quest_flag);
sdl2_config->GetBoolean("Debugging", "reporting_services", false); ReadSetting("Debugging", Settings::values.use_debug_asserts);
Settings::values.quest_flag = sdl2_config->GetBoolean("Debugging", "quest_flag", false); ReadSetting("Debugging", Settings::values.use_auto_stub);
Settings::values.use_debug_asserts = ReadSetting("Debugging", Settings::values.disable_macro_jit);
sdl2_config->GetBoolean("Debugging", "use_debug_asserts", false);
Settings::values.use_auto_stub = sdl2_config->GetBoolean("Debugging", "use_auto_stub", false);
Settings::values.disable_macro_jit =
sdl2_config->GetBoolean("Debugging", "disable_macro_jit", false);
const auto title_list = sdl2_config->Get("AddOns", "title_ids", ""); const auto title_list = sdl2_config->Get("AddOns", "title_ids", "");
std::stringstream ss(title_list); std::stringstream ss(title_list);
@ -511,17 +488,14 @@ void Config::ReadValues() {
} }
// Web Service // Web Service
Settings::values.enable_telemetry = ReadSetting("WebService", Settings::values.enable_telemetry);
sdl2_config->GetBoolean("WebService", "enable_telemetry", true); ReadSetting("WebService", Settings::values.web_api_url);
Settings::values.web_api_url = ReadSetting("WebService", Settings::values.yuzu_username);
sdl2_config->Get("WebService", "web_api_url", "https://api.yuzu-emu.org"); ReadSetting("WebService", Settings::values.yuzu_token);
Settings::values.yuzu_username = sdl2_config->Get("WebService", "yuzu_username", "");
Settings::values.yuzu_token = sdl2_config->Get("WebService", "yuzu_token", "");
// Services // Services
Settings::values.bcat_backend = sdl2_config->Get("Services", "bcat_backend", "none"); ReadSetting("Services", Settings::values.bcat_backend);
Settings::values.bcat_boxcat_local = ReadSetting("Services", Settings::values.bcat_boxcat_local);
sdl2_config->GetBoolean("Services", "bcat_boxcat_local", false);
} }
void Config::Reload() { void Config::Reload() {

View file

@ -8,6 +8,8 @@
#include <memory> #include <memory>
#include <string> #include <string>
#include "common/settings.h"
class INIReader; class INIReader;
class Config { class Config {
@ -22,4 +24,14 @@ public:
~Config(); ~Config();
void Reload(); void Reload();
private:
/**
* Applies a value read from the sdl2_config to a BasicSetting.
*
* @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);
}; };

View file

@ -78,7 +78,7 @@ static void InitializeLogging() {
using namespace Common; using namespace Common;
Log::Filter log_filter(Log::Level::Debug); Log::Filter log_filter(Log::Level::Debug);
log_filter.ParseFilterString(Settings::values.log_filter); log_filter.ParseFilterString(static_cast<std::string>(Settings::values.log_filter));
Log::SetGlobalFilter(log_filter); Log::SetGlobalFilter(log_filter);
Log::AddBackend(std::make_unique<Log::ColorConsoleBackend>()); Log::AddBackend(std::make_unique<Log::ColorConsoleBackend>());