Merge pull request #10839 from lat9nq/pgc-plus

general: Reimplement per-game configurations
master
liamwhite 2023-08-02 14:25:52 +07:00 committed by GitHub
commit fca7d975fd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
78 changed files with 4614 additions and 4820 deletions

@ -3,4 +3,4 @@
[codespell] [codespell]
skip = ./.git,./build,./dist,./Doxyfile,./externals,./LICENSES,./src/android/app/src/main/res skip = ./.git,./build,./dist,./Doxyfile,./externals,./LICENSES,./src/android/app/src/main/res
ignore-words-list = aci,allright,ba,deques,froms,hda,inout,lod,masia,nam,nax,nd,optin,pullrequests,pullrequest,te,transfered,unstall,uscaled,zink ignore-words-list = aci,allright,ba,canonicalizations,deques,froms,hda,inout,lod,masia,nam,nax,nd,optin,pullrequests,pullrequest,te,transfered,unstall,uscaled,zink

@ -35,6 +35,7 @@ if (MSVC)
# /volatile:iso - Use strict standards-compliant volatile semantics. # /volatile:iso - Use strict standards-compliant volatile semantics.
# /Zc:externConstexpr - Allow extern constexpr variables to have external linkage, like the standard mandates # /Zc:externConstexpr - Allow extern constexpr variables to have external linkage, like the standard mandates
# /Zc:inline - Let codegen omit inline functions in object files # /Zc:inline - Let codegen omit inline functions in object files
# /Zc:preprocessor - Enable standards-conforming preprocessor
# /Zc:throwingNew - Let codegen assume `operator new` (without std::nothrow) will never return null # /Zc:throwingNew - Let codegen assume `operator new` (without std::nothrow) will never return null
# /GT - Supports fiber safety for data allocated using static thread-local storage # /GT - Supports fiber safety for data allocated using static thread-local storage
add_compile_options( add_compile_options(
@ -48,6 +49,7 @@ if (MSVC)
/volatile:iso /volatile:iso
/Zc:externConstexpr /Zc:externConstexpr
/Zc:inline /Zc:inline
/Zc:preprocessor
/Zc:throwingNew /Zc:throwingNew
/GT /GT

@ -150,15 +150,17 @@ void Config::ReadValues() {
if (rng_seed_enabled) { if (rng_seed_enabled) {
Settings::values.rng_seed.SetValue(config->GetInteger("System", "rng_seed", 0)); Settings::values.rng_seed.SetValue(config->GetInteger("System", "rng_seed", 0));
} else { } else {
Settings::values.rng_seed.SetValue(std::nullopt); Settings::values.rng_seed.SetValue(0);
} }
Settings::values.rng_seed_enabled.SetValue(rng_seed_enabled);
const auto custom_rtc_enabled = config->GetBoolean("System", "custom_rtc_enabled", false); const auto custom_rtc_enabled = config->GetBoolean("System", "custom_rtc_enabled", false);
if (custom_rtc_enabled) { if (custom_rtc_enabled) {
Settings::values.custom_rtc = config->GetInteger("System", "custom_rtc", 0); Settings::values.custom_rtc = config->GetInteger("System", "custom_rtc", 0);
} else { } else {
Settings::values.custom_rtc = std::nullopt; Settings::values.custom_rtc = 0;
} }
Settings::values.custom_rtc_enabled = custom_rtc_enabled;
ReadSetting("System", Settings::values.language_index); ReadSetting("System", Settings::values.language_index);
ReadSetting("System", Settings::values.region_index); ReadSetting("System", Settings::values.region_index);
@ -167,7 +169,7 @@ void Config::ReadValues() {
// Core // Core
ReadSetting("Core", Settings::values.use_multi_core); ReadSetting("Core", Settings::values.use_multi_core);
ReadSetting("Core", Settings::values.use_unsafe_extended_memory_layout); ReadSetting("Core", Settings::values.memory_layout_mode);
// Cpu // Cpu
ReadSetting("Cpu", Settings::values.cpu_accuracy); ReadSetting("Cpu", Settings::values.cpu_accuracy);
@ -222,14 +224,17 @@ void Config::ReadValues() {
ReadSetting("Renderer", Settings::values.bg_blue); ReadSetting("Renderer", Settings::values.bg_blue);
// Use GPU accuracy normal by default on Android // Use GPU accuracy normal by default on Android
Settings::values.gpu_accuracy = static_cast<Settings::GPUAccuracy>(config->GetInteger( Settings::values.gpu_accuracy = static_cast<Settings::GpuAccuracy>(config->GetInteger(
"Renderer", "gpu_accuracy", static_cast<u32>(Settings::GPUAccuracy::Normal))); "Renderer", "gpu_accuracy", static_cast<u32>(Settings::GpuAccuracy::Normal)));
// Use GPU default anisotropic filtering on Android // Use GPU default anisotropic filtering on Android
Settings::values.max_anisotropy = config->GetInteger("Renderer", "max_anisotropy", 1); Settings::values.max_anisotropy =
static_cast<Settings::AnisotropyMode>(config->GetInteger("Renderer", "max_anisotropy", 1));
// Disable ASTC compute by default on Android // Disable ASTC compute by default on Android
Settings::values.accelerate_astc = config->GetBoolean("Renderer", "accelerate_astc", false); Settings::values.accelerate_astc.SetValue(
config->GetBoolean("Renderer", "accelerate_astc", false) ? Settings::AstcDecodeMode::Gpu
: Settings::AstcDecodeMode::Cpu);
// Enable asynchronous presentation by default on Android // Enable asynchronous presentation by default on Android
Settings::values.async_presentation = Settings::values.async_presentation =

@ -15,6 +15,7 @@
#endif #endif
#include "audio_core/sink/null_sink.h" #include "audio_core/sink/null_sink.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "common/settings_enums.h"
namespace AudioCore::Sink { namespace AudioCore::Sink {
namespace { namespace {
@ -24,7 +25,7 @@ struct SinkDetails {
using LatencyFn = u32 (*)(); using LatencyFn = u32 (*)();
/// Name for this sink. /// Name for this sink.
std::string_view id; Settings::AudioEngine id;
/// A method to call to construct an instance of this type of sink. /// A method to call to construct an instance of this type of sink.
FactoryFn factory; FactoryFn factory;
/// A method to call to list available devices. /// A method to call to list available devices.
@ -37,7 +38,7 @@ struct SinkDetails {
constexpr SinkDetails sink_details[] = { constexpr SinkDetails sink_details[] = {
#ifdef HAVE_CUBEB #ifdef HAVE_CUBEB
SinkDetails{ SinkDetails{
"cubeb", Settings::AudioEngine::Cubeb,
[](std::string_view device_id) -> std::unique_ptr<Sink> { [](std::string_view device_id) -> std::unique_ptr<Sink> {
return std::make_unique<CubebSink>(device_id); return std::make_unique<CubebSink>(device_id);
}, },
@ -47,7 +48,7 @@ constexpr SinkDetails sink_details[] = {
#endif #endif
#ifdef HAVE_SDL2 #ifdef HAVE_SDL2
SinkDetails{ SinkDetails{
"sdl2", Settings::AudioEngine::Sdl2,
[](std::string_view device_id) -> std::unique_ptr<Sink> { [](std::string_view device_id) -> std::unique_ptr<Sink> {
return std::make_unique<SDLSink>(device_id); return std::make_unique<SDLSink>(device_id);
}, },
@ -55,46 +56,47 @@ constexpr SinkDetails sink_details[] = {
&GetSDLLatency, &GetSDLLatency,
}, },
#endif #endif
SinkDetails{"null", SinkDetails{Settings::AudioEngine::Null,
[](std::string_view device_id) -> std::unique_ptr<Sink> { [](std::string_view device_id) -> std::unique_ptr<Sink> {
return std::make_unique<NullSink>(device_id); return std::make_unique<NullSink>(device_id);
}, },
[](bool capture) { return std::vector<std::string>{"null"}; }, []() { return 0u; }}, [](bool capture) { return std::vector<std::string>{"null"}; }, []() { return 0u; }},
}; };
const SinkDetails& GetOutputSinkDetails(std::string_view sink_id) { const SinkDetails& GetOutputSinkDetails(Settings::AudioEngine sink_id) {
const auto find_backend{[](std::string_view id) { const auto find_backend{[](Settings::AudioEngine id) {
return std::find_if(std::begin(sink_details), std::end(sink_details), return std::find_if(std::begin(sink_details), std::end(sink_details),
[&id](const auto& sink_detail) { return sink_detail.id == id; }); [&id](const auto& sink_detail) { return sink_detail.id == id; });
}}; }};
auto iter = find_backend(sink_id); auto iter = find_backend(sink_id);
if (sink_id == "auto") { if (sink_id == Settings::AudioEngine::Auto) {
// Auto-select a backend. Prefer CubeB, but it may report a large minimum latency which // Auto-select a backend. Prefer CubeB, but it may report a large minimum latency which
// causes audio issues, in that case go with SDL. // causes audio issues, in that case go with SDL.
#if defined(HAVE_CUBEB) && defined(HAVE_SDL2) #if defined(HAVE_CUBEB) && defined(HAVE_SDL2)
iter = find_backend("cubeb"); iter = find_backend(Settings::AudioEngine::Cubeb);
if (iter->latency() > TargetSampleCount * 3) { if (iter->latency() > TargetSampleCount * 3) {
iter = find_backend("sdl2"); iter = find_backend(Settings::AudioEngine::Sdl2);
} }
#else #else
iter = std::begin(sink_details); iter = std::begin(sink_details);
#endif #endif
LOG_INFO(Service_Audio, "Auto-selecting the {} backend", iter->id); LOG_INFO(Service_Audio, "Auto-selecting the {} backend",
Settings::CanonicalizeEnum(iter->id));
} }
if (iter == std::end(sink_details)) { if (iter == std::end(sink_details)) {
LOG_ERROR(Audio, "Invalid sink_id {}", sink_id); LOG_ERROR(Audio, "Invalid sink_id {}", Settings::CanonicalizeEnum(sink_id));
iter = find_backend("null"); iter = find_backend(Settings::AudioEngine::Null);
} }
return *iter; return *iter;
} }
} // Anonymous namespace } // Anonymous namespace
std::vector<std::string_view> GetSinkIDs() { std::vector<Settings::AudioEngine> GetSinkIDs() {
std::vector<std::string_view> sink_ids(std::size(sink_details)); std::vector<Settings::AudioEngine> sink_ids(std::size(sink_details));
std::transform(std::begin(sink_details), std::end(sink_details), std::begin(sink_ids), std::transform(std::begin(sink_details), std::end(sink_details), std::begin(sink_ids),
[](const auto& sink) { return sink.id; }); [](const auto& sink) { return sink.id; });
@ -102,11 +104,11 @@ std::vector<std::string_view> GetSinkIDs() {
return sink_ids; return sink_ids;
} }
std::vector<std::string> GetDeviceListForSink(std::string_view sink_id, bool capture) { std::vector<std::string> GetDeviceListForSink(Settings::AudioEngine sink_id, bool capture) {
return GetOutputSinkDetails(sink_id).list_devices(capture); return GetOutputSinkDetails(sink_id).list_devices(capture);
} }
std::unique_ptr<Sink> CreateSinkFromID(std::string_view sink_id, std::string_view device_id) { std::unique_ptr<Sink> CreateSinkFromID(Settings::AudioEngine sink_id, std::string_view device_id) {
return GetOutputSinkDetails(sink_id).factory(device_id); return GetOutputSinkDetails(sink_id).factory(device_id);
} }

@ -3,9 +3,11 @@
#pragma once #pragma once
#include <memory>
#include <string> #include <string>
#include <string_view> #include <string_view>
#include <vector> #include <vector>
#include "common/settings_enums.h"
namespace AudioCore { namespace AudioCore {
class AudioManager; class AudioManager;
@ -19,7 +21,7 @@ class Sink;
* *
* @return Vector of available sink names. * @return Vector of available sink names.
*/ */
std::vector<std::string_view> GetSinkIDs(); std::vector<Settings::AudioEngine> GetSinkIDs();
/** /**
* Gets the list of devices for a particular sink identified by the given ID. * Gets the list of devices for a particular sink identified by the given ID.
@ -28,7 +30,7 @@ std::vector<std::string_view> GetSinkIDs();
* @param capture - Get capture (input) devices, or output devices? * @param capture - Get capture (input) devices, or output devices?
* @return Vector of device names. * @return Vector of device names.
*/ */
std::vector<std::string> GetDeviceListForSink(std::string_view sink_id, bool capture); std::vector<std::string> GetDeviceListForSink(Settings::AudioEngine sink_id, bool capture);
/** /**
* Creates an audio sink identified by the given device ID. * Creates an audio sink identified by the given device ID.
@ -37,7 +39,7 @@ std::vector<std::string> GetDeviceListForSink(std::string_view sink_id, bool cap
* @param device_id - Name of the device to create. * @param device_id - Name of the device to create.
* @return Pointer to the created sink. * @return Pointer to the created sink.
*/ */
std::unique_ptr<Sink> CreateSinkFromID(std::string_view sink_id, std::string_view device_id); std::unique_ptr<Sink> CreateSinkFromID(Settings::AudioEngine sink_id, std::string_view device_id);
} // namespace Sink } // namespace Sink
} // namespace AudioCore } // namespace AudioCore

@ -110,8 +110,12 @@ add_library(common STATIC
scratch_buffer.h scratch_buffer.h
settings.cpp settings.cpp
settings.h settings.h
settings_common.cpp
settings_common.h
settings_enums.h
settings_input.cpp settings_input.cpp
settings_input.h settings_input.h
settings_setting.h
socket_types.h socket_types.h
spin_lock.cpp spin_lock.cpp
spin_lock.h spin_lock.h
@ -193,9 +197,16 @@ if (MSVC)
/we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data /we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data
/we4800 # Implicit conversion from 'type' to bool. Possible information loss /we4800 # Implicit conversion from 'type' to bool. Possible information loss
) )
else() endif()
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
target_compile_options(common PRIVATE target_compile_options(common PRIVATE
$<$<CXX_COMPILER_ID:Clang>:-fsized-deallocation> -fsized-deallocation
-Werror=unreachable-code-aggressive
)
target_compile_definitions(common PRIVATE
# Clang 14 and earlier have errors when explicitly instantiating Settings::Setting
$<$<VERSION_LESS:$<CXX_COMPILER_VERSION>,15>:CANNOT_EXPLICITLY_INSTANTIATE>
) )
endif() endif()

@ -108,7 +108,7 @@ public:
using namespace Common::Literals; using namespace Common::Literals;
// Prevent logs from exceeding a set maximum size in the event that log entries are spammed. // Prevent logs from exceeding a set maximum size in the event that log entries are spammed.
const auto write_limit = Settings::values.extended_logging ? 1_GiB : 100_MiB; const auto write_limit = Settings::values.extended_logging.GetValue() ? 1_GiB : 100_MiB;
const bool write_limit_exceeded = bytes_written > write_limit; const bool write_limit_exceeded = bytes_written > write_limit;
if (entry.log_level >= Level::Error || write_limit_exceeded) { if (entry.log_level >= Level::Error || write_limit_exceeded) {
if (write_limit_exceeded) { if (write_limit_exceeded) {

@ -7,9 +7,16 @@
#include <exception> #include <exception>
#include <stdexcept> #include <stdexcept>
#endif #endif
#include <compare>
#include <cstddef>
#include <filesystem>
#include <functional>
#include <string_view> #include <string_view>
#include <type_traits>
#include <fmt/core.h>
#include "common/assert.h" #include "common/assert.h"
#include "common/fs/fs_util.h"
#include "common/fs/path_util.h" #include "common/fs/path_util.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "common/settings.h" #include "common/settings.h"
@ -17,11 +24,50 @@
namespace Settings { namespace Settings {
Values values; // Clang 14 and earlier have errors when explicitly instantiating these classes
static bool configuring_global = true; #ifndef CANNOT_EXPLICITLY_INSTANTIATE
#define SETTING(TYPE, RANGED) template class Setting<TYPE, RANGED>
#define SWITCHABLE(TYPE, RANGED) template class SwitchableSetting<TYPE, RANGED>
std::string GetTimeZoneString() { SETTING(AudioEngine, false);
const auto time_zone_index = static_cast<std::size_t>(values.time_zone_index.GetValue()); SETTING(bool, false);
SETTING(int, false);
SETTING(std::string, false);
SETTING(u16, false);
SWITCHABLE(AnisotropyMode, true);
SWITCHABLE(AntiAliasing, false);
SWITCHABLE(AspectRatio, true);
SWITCHABLE(AstcDecodeMode, true);
SWITCHABLE(AstcRecompression, true);
SWITCHABLE(AudioMode, true);
SWITCHABLE(CpuAccuracy, true);
SWITCHABLE(FullscreenMode, true);
SWITCHABLE(GpuAccuracy, true);
SWITCHABLE(Language, true);
SWITCHABLE(NvdecEmulation, false);
SWITCHABLE(Region, true);
SWITCHABLE(RendererBackend, true);
SWITCHABLE(ScalingFilter, false);
SWITCHABLE(ShaderBackend, true);
SWITCHABLE(TimeZone, true);
SETTING(VSyncMode, true);
SWITCHABLE(bool, false);
SWITCHABLE(int, false);
SWITCHABLE(int, true);
SWITCHABLE(s64, false);
SWITCHABLE(u16, true);
SWITCHABLE(u32, false);
SWITCHABLE(u8, false);
SWITCHABLE(u8, true);
#undef SETTING
#undef SWITCHABLE
#endif
Values values;
std::string GetTimeZoneString(TimeZone time_zone) {
const auto time_zone_index = static_cast<std::size_t>(time_zone);
ASSERT(time_zone_index < Common::TimeZone::GetTimeZoneStrings().size()); ASSERT(time_zone_index < Common::TimeZone::GetTimeZoneStrings().size());
std::string location_name; std::string location_name;
@ -61,73 +107,35 @@ void LogSettings() {
}; };
LOG_INFO(Config, "yuzu Configuration:"); LOG_INFO(Config, "yuzu Configuration:");
log_setting("Controls_UseDockedMode", values.use_docked_mode.GetValue()); for (auto& [category, settings] : values.linkage.by_category) {
log_setting("System_RngSeed", values.rng_seed.GetValue().value_or(0)); for (const auto& setting : settings) {
log_setting("System_DeviceName", values.device_name.GetValue()); if (setting->Id() == values.yuzu_token.Id()) {
log_setting("System_CurrentUser", values.current_user.GetValue()); // Hide the token secret, for security reasons.
log_setting("System_LanguageIndex", values.language_index.GetValue()); continue;
log_setting("System_RegionIndex", values.region_index.GetValue()); }
log_setting("System_TimeZoneIndex", values.time_zone_index.GetValue());
log_setting("System_UnsafeMemoryLayout", values.use_unsafe_extended_memory_layout.GetValue()); const auto name = fmt::format(
log_setting("Core_UseMultiCore", values.use_multi_core.GetValue()); "{:c}{:c} {}.{}", setting->ToString() == setting->DefaultToString() ? '-' : 'M',
log_setting("CPU_Accuracy", values.cpu_accuracy.GetValue()); setting->UsingGlobal() ? '-' : 'C', TranslateCategory(category),
log_setting("Renderer_UseResolutionScaling", values.resolution_setup.GetValue()); setting->GetLabel());
log_setting("Renderer_ScalingFilter", values.scaling_filter.GetValue());
log_setting("Renderer_FSRSlider", values.fsr_sharpening_slider.GetValue()); log_setting(name, setting->Canonicalize());
log_setting("Renderer_AntiAliasing", values.anti_aliasing.GetValue()); }
log_setting("Renderer_UseSpeedLimit", values.use_speed_limit.GetValue()); }
log_setting("Renderer_SpeedLimit", values.speed_limit.GetValue());
log_setting("Renderer_UseDiskShaderCache", values.use_disk_shader_cache.GetValue());
log_setting("Renderer_GPUAccuracyLevel", values.gpu_accuracy.GetValue());
log_setting("Renderer_UseAsynchronousGpuEmulation",
values.use_asynchronous_gpu_emulation.GetValue());
log_setting("Renderer_NvdecEmulation", values.nvdec_emulation.GetValue());
log_setting("Renderer_AccelerateASTC", values.accelerate_astc.GetValue());
log_setting("Renderer_AsyncASTC", values.async_astc.GetValue());
log_setting("Renderer_AstcRecompression", values.astc_recompression.GetValue());
log_setting("Renderer_UseVsync", values.vsync_mode.GetValue());
log_setting("Renderer_UseReactiveFlushing", values.use_reactive_flushing.GetValue());
log_setting("Renderer_ShaderBackend", values.shader_backend.GetValue());
log_setting("Renderer_UseAsynchronousShaders", values.use_asynchronous_shaders.GetValue());
log_setting("Renderer_AnisotropicFilteringLevel", values.max_anisotropy.GetValue());
log_setting("Audio_OutputEngine", values.sink_id.GetValue());
log_setting("Audio_OutputDevice", values.audio_output_device_id.GetValue());
log_setting("Audio_InputDevice", values.audio_input_device_id.GetValue());
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.GetValue());
log_setting("Debugging_GDBStub", values.use_gdbstub.GetValue());
log_setting("Input_EnableMotion", values.motion_enabled.GetValue());
log_setting("Input_EnableVibration", values.vibration_enabled.GetValue());
log_setting("Input_EnableTouch", values.touchscreen.enabled);
log_setting("Input_EnableMouse", values.mouse_enabled.GetValue());
log_setting("Input_EnableKeyboard", values.keyboard_enabled.GetValue());
log_setting("Input_EnableRingController", values.enable_ring_controller.GetValue());
log_setting("Input_EnableIrSensor", values.enable_ir_sensor.GetValue());
log_setting("Input_EnableCustomJoycon", values.enable_joycon_driver.GetValue());
log_setting("Input_EnableCustomProController", values.enable_procon_driver.GetValue());
log_setting("Input_EnableRawInput", values.enable_raw_input.GetValue());
}
bool IsConfiguringGlobal() {
return configuring_global;
}
void SetConfiguringGlobal(bool is_global) {
configuring_global = is_global;
} }
bool IsGPULevelExtreme() { bool IsGPULevelExtreme() {
return values.gpu_accuracy.GetValue() == GPUAccuracy::Extreme; return values.gpu_accuracy.GetValue() == GpuAccuracy::Extreme;
} }
bool IsGPULevelHigh() { bool IsGPULevelHigh() {
return values.gpu_accuracy.GetValue() == GPUAccuracy::Extreme || return values.gpu_accuracy.GetValue() == GpuAccuracy::Extreme ||
values.gpu_accuracy.GetValue() == GPUAccuracy::High; values.gpu_accuracy.GetValue() == GpuAccuracy::High;
} }
bool IsFastmemEnabled() { bool IsFastmemEnabled() {
@ -144,6 +152,61 @@ float Volume() {
return values.volume.GetValue() / static_cast<f32>(values.volume.GetDefault()); return values.volume.GetValue() / static_cast<f32>(values.volume.GetDefault());
} }
const char* TranslateCategory(Category category) {
switch (category) {
case Category::Audio:
return "Audio";
case Category::Core:
return "Core";
case Category::Cpu:
case Category::CpuDebug:
case Category::CpuUnsafe:
return "Cpu";
case Category::Renderer:
case Category::RendererAdvanced:
case Category::RendererDebug:
return "Renderer";
case Category::System:
case Category::SystemAudio:
return "System";
case Category::DataStorage:
return "Data Storage";
case Category::Debugging:
case Category::DebuggingGraphics:
return "Debugging";
case Category::Miscellaneous:
return "Miscellaneous";
case Category::Network:
return "Network";
case Category::WebService:
return "WebService";
case Category::AddOns:
return "DisabledAddOns";
case Category::Controls:
return "Controls";
case Category::Ui:
case Category::UiGeneral:
return "UI";
case Category::UiLayout:
return "UiLayout";
case Category::UiGameList:
return "UiGameList";
case Category::Screenshots:
return "Screenshots";
case Category::Shortcuts:
return "Shortcuts";
case Category::Multiplayer:
return "Multiplayer";
case Category::Services:
return "Services";
case Category::Paths:
return "Paths";
case Category::MaxEnum:
break;
}
return "Miscellaneous";
}
void UpdateRescalingInfo() { void UpdateRescalingInfo() {
const auto setup = values.resolution_setup.GetValue(); const auto setup = values.resolution_setup.GetValue();
auto& info = values.resolution_info; auto& info = values.resolution_info;
@ -212,66 +275,19 @@ void RestoreGlobalState(bool is_powered_on) {
return; return;
} }
// Audio for (const auto& reset : values.linkage.restore_functions) {
values.volume.SetGlobal(true); reset();
}
}
// Core static bool configuring_global = true;
values.use_multi_core.SetGlobal(true);
values.use_unsafe_extended_memory_layout.SetGlobal(true);
// CPU bool IsConfiguringGlobal() {
values.cpu_accuracy.SetGlobal(true); return configuring_global;
values.cpuopt_unsafe_unfuse_fma.SetGlobal(true); }
values.cpuopt_unsafe_reduce_fp_error.SetGlobal(true);
values.cpuopt_unsafe_ignore_standard_fpcr.SetGlobal(true);
values.cpuopt_unsafe_inaccurate_nan.SetGlobal(true);
values.cpuopt_unsafe_fastmem_check.SetGlobal(true);
values.cpuopt_unsafe_ignore_global_monitor.SetGlobal(true);
// Renderer void SetConfiguringGlobal(bool is_global) {
values.fsr_sharpening_slider.SetGlobal(true); configuring_global = is_global;
values.renderer_backend.SetGlobal(true);
values.async_presentation.SetGlobal(true);
values.renderer_force_max_clock.SetGlobal(true);
values.vulkan_device.SetGlobal(true);
values.fullscreen_mode.SetGlobal(true);
values.aspect_ratio.SetGlobal(true);
values.resolution_setup.SetGlobal(true);
values.scaling_filter.SetGlobal(true);
values.anti_aliasing.SetGlobal(true);
values.max_anisotropy.SetGlobal(true);
values.use_speed_limit.SetGlobal(true);
values.speed_limit.SetGlobal(true);
values.use_disk_shader_cache.SetGlobal(true);
values.gpu_accuracy.SetGlobal(true);
values.use_asynchronous_gpu_emulation.SetGlobal(true);
values.nvdec_emulation.SetGlobal(true);
values.accelerate_astc.SetGlobal(true);
values.async_astc.SetGlobal(true);
values.astc_recompression.SetGlobal(true);
values.use_reactive_flushing.SetGlobal(true);
values.shader_backend.SetGlobal(true);
values.use_asynchronous_shaders.SetGlobal(true);
values.use_fast_gpu_time.SetGlobal(true);
values.use_vulkan_driver_pipeline_cache.SetGlobal(true);
values.bg_red.SetGlobal(true);
values.bg_green.SetGlobal(true);
values.bg_blue.SetGlobal(true);
values.enable_compute_pipelines.SetGlobal(true);
values.use_video_framerate.SetGlobal(true);
// System
values.language_index.SetGlobal(true);
values.region_index.SetGlobal(true);
values.time_zone_index.SetGlobal(true);
values.rng_seed.SetGlobal(true);
values.sound_index.SetGlobal(true);
// Controls
values.players.SetGlobal(true);
values.use_docked_mode.SetGlobal(true);
values.vibration_enabled.SetGlobal(true);
values.motion_enabled.SetGlobal(true);
} }
} // namespace Settings } // namespace Settings

@ -6,95 +6,21 @@
#include <algorithm> #include <algorithm>
#include <array> #include <array>
#include <map> #include <map>
#include <optional> #include <memory>
#include <stdexcept>
#include <string> #include <string>
#include <utility> #include <utility>
#include <vector> #include <vector>
#include "common/common_types.h" #include "common/common_types.h"
#include "common/settings_common.h"
#include "common/settings_enums.h"
#include "common/settings_input.h" #include "common/settings_input.h"
#include "common/settings_setting.h"
namespace Settings { namespace Settings {
enum class VSyncMode : u32 { const char* TranslateCategory(Settings::Category category);
Immediate = 0,
Mailbox = 1,
FIFO = 2,
FIFORelaxed = 3,
};
enum class RendererBackend : u32 {
OpenGL = 0,
Vulkan = 1,
Null = 2,
};
enum class ShaderBackend : u32 {
GLSL = 0,
GLASM = 1,
SPIRV = 2,
};
enum class GPUAccuracy : u32 {
Normal = 0,
High = 1,
Extreme = 2,
};
enum class CPUAccuracy : u32 {
Auto = 0,
Accurate = 1,
Unsafe = 2,
Paranoid = 3,
};
enum class FullscreenMode : u32 {
Borderless = 0,
Exclusive = 1,
};
enum class NvdecEmulation : u32 {
Off = 0,
CPU = 1,
GPU = 2,
};
enum class ResolutionSetup : u32 {
Res1_2X = 0,
Res3_4X = 1,
Res1X = 2,
Res3_2X = 3,
Res2X = 4,
Res3X = 5,
Res4X = 6,
Res5X = 7,
Res6X = 8,
Res7X = 9,
Res8X = 10,
};
enum class ScalingFilter : u32 {
NearestNeighbor = 0,
Bilinear = 1,
Bicubic = 2,
Gaussian = 3,
ScaleForce = 4,
Fsr = 5,
LastFilter = Fsr,
};
enum class AntiAliasing : u32 {
None = 0,
Fxaa = 1,
Smaa = 2,
LastAA = Smaa,
};
enum class AstcRecompression : u32 {
Uncompressed = 0,
Bc1 = 1,
Bc3 = 2,
};
struct ResolutionScalingInfo { struct ResolutionScalingInfo {
u32 up_scale{1}; u32 up_scale{1};
@ -119,239 +45,47 @@ struct ResolutionScalingInfo {
} }
}; };
/** The Setting class is a simple resource manager. It defines a label and default value alongside #ifndef CANNOT_EXPLICITLY_INSTANTIATE
* the actual value of the setting for simpler and less-error prone use with frontend // Instantiate the classes elsewhere (settings.cpp) to reduce compiler/linker work
* configurations. Specifying a default value and label is required. A minimum and maximum range can #define SETTING(TYPE, RANGED) extern template class Setting<TYPE, RANGED>
* be specified for sanitization. #define SWITCHABLE(TYPE, RANGED) extern template class SwitchableSetting<TYPE, RANGED>
*/
template <typename Type, bool ranged = false>
class Setting {
protected:
Setting() = default;
/** SETTING(AudioEngine, false);
* Only sets the setting to the given initializer, leaving the other members to their default SETTING(bool, false);
* initializers. SETTING(int, false);
* SETTING(s32, false);
* @param global_val Initial value of the setting SETTING(std::string, false);
*/ SETTING(std::string, false);
explicit Setting(const Type& val) : value{val} {} SETTING(u16, false);
SWITCHABLE(AnisotropyMode, true);
SWITCHABLE(AntiAliasing, false);
SWITCHABLE(AspectRatio, true);
SWITCHABLE(AstcDecodeMode, true);
SWITCHABLE(AstcRecompression, true);
SWITCHABLE(AudioMode, true);
SWITCHABLE(CpuAccuracy, true);
SWITCHABLE(FullscreenMode, true);
SWITCHABLE(GpuAccuracy, true);
SWITCHABLE(Language, true);
SWITCHABLE(NvdecEmulation, false);
SWITCHABLE(Region, true);
SWITCHABLE(RendererBackend, true);
SWITCHABLE(ScalingFilter, false);
SWITCHABLE(ShaderBackend, true);
SWITCHABLE(TimeZone, true);
SETTING(VSyncMode, true);
SWITCHABLE(bool, false);
SWITCHABLE(int, false);
SWITCHABLE(int, true);
SWITCHABLE(s64, false);
SWITCHABLE(u16, true);
SWITCHABLE(u32, false);
SWITCHABLE(u8, false);
SWITCHABLE(u8, true);
public: #undef SETTING
/** #undef SWITCHABLE
* Sets a default value, label, and setting value. #endif
*
* @param default_val Initial 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)
requires(!ranged)
: value{default_val}, default_value{default_val}, label{name} {}
virtual ~Setting() = default;
/**
* Sets a default value, minimum value, maximum value, and label.
*
* @param default_val Initial value of the setting, and default value of the setting
* @param min_val Sets the minimum allowed value of the setting
* @param max_val Sets the maximum allowed value of the setting
* @param name Label for the setting
*/
explicit Setting(const Type& default_val, const Type& min_val, const Type& max_val,
const std::string& name)
requires(ranged)
: value{default_val},
default_value{default_val}, maximum{max_val}, minimum{min_val}, label{name} {}
/**
* Returns a reference to the setting's value.
*
* @returns A reference to the setting
*/
[[nodiscard]] virtual const Type& GetValue() const {
return value;
}
/**
* Sets the setting to the given value.
*
* @param val The desired value
*/
virtual void SetValue(const Type& val) {
Type temp{ranged ? std::clamp(val, minimum, maximum) : val};
std::swap(value, 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 val The desired setting value
*
* @returns A reference to the setting
*/
virtual const Type& operator=(const Type& val) {
Type temp{ranged ? std::clamp(val, minimum, maximum) : val};
std::swap(value, temp);
return value;
}
/**
* Returns a reference to the setting.
*
* @returns A reference to the setting
*/
explicit virtual operator const Type&() const {
return value;
}
protected:
Type value{}; ///< The setting
const Type default_value{}; ///< The default value
const Type maximum{}; ///< Maximum allowed value of the setting
const Type minimum{}; ///< Minimum allowed value of the setting
const std::string label{}; ///< The setting's label
};
/**
* The SwitchableSetting class is a slightly more complex version of the Setting class. This adds a
* custom setting to switch to when a guest application specifically requires it. The effect is that
* other components of the emulator can access the setting's intended value without any need for the
* component to ask whether the custom or global setting is needed at the moment.
*
* By default, the global setting is used.
*/
template <typename Type, bool ranged = false>
class SwitchableSetting : virtual public Setting<Type, ranged> {
public:
/**
* Sets a default value, label, and setting value.
*
* @param default_val Initial value of the setting, and default value of the setting
* @param name Label for the setting
*/
explicit SwitchableSetting(const Type& default_val, const std::string& name)
requires(!ranged)
: Setting<Type>{default_val, name} {}
virtual ~SwitchableSetting() = default;
/**
* Sets a default value, minimum value, maximum value, and label.
*
* @param default_val Initial value of the setting, and default value of the setting
* @param min_val Sets the minimum allowed value of the setting
* @param max_val Sets the maximum allowed value of the setting
* @param name Label for the setting
*/
explicit SwitchableSetting(const Type& default_val, const Type& min_val, const Type& max_val,
const std::string& name)
requires(ranged)
: Setting<Type, true>{default_val, min_val, max_val, name} {}
/**
* Tells this setting to represent either the global or custom setting when other member
* functions are used.
*
* @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]] virtual const Type& GetValue() const override {
if (use_global) {
return this->value;
}
return custom;
}
[[nodiscard]] virtual const Type& GetValue(bool need_global) const {
if (use_global || need_global) {
return this->value;
}
return custom;
}
/**
* Sets the current setting value depending on the global state.
*
* @param val The new value
*/
void SetValue(const Type& val) override {
Type temp{ranged ? std::clamp(val, this->minimum, this->maximum) : val};
if (use_global) {
std::swap(this->value, temp);
} else {
std::swap(custom, temp);
}
}
/**
* Assigns the current setting value depending on the global state.
*
* @param val The new value
*
* @returns A reference to the current setting value
*/
const Type& operator=(const Type& val) override {
Type temp{ranged ? std::clamp(val, this->minimum, this->maximum) : val};
if (use_global) {
std::swap(this->value, temp);
return this->value;
}
std::swap(custom, temp);
return custom;
}
/**
* Returns the current setting value depending on the global state.
*
* @returns A reference to the current setting value
*/
virtual explicit operator const Type&() const override {
if (use_global) {
return this->value;
}
return custom;
}
protected:
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. * The InputSetting class allows for getting a reference to either the global or custom members.
@ -391,208 +125,388 @@ struct TouchFromButtonMap {
}; };
struct Values { struct Values {
Linkage linkage{};
// Audio // Audio
Setting<std::string> sink_id{"auto", "output_engine"}; Setting<AudioEngine> sink_id{linkage, AudioEngine::Auto, "output_engine", Category::Audio,
Setting<std::string> audio_output_device_id{"auto", "output_device"}; Specialization::RuntimeList};
Setting<std::string> audio_input_device_id{"auto", "input_device"}; Setting<std::string> audio_output_device_id{linkage, "auto", "output_device", Category::Audio,
Setting<bool> audio_muted{false, "audio_muted"}; Specialization::RuntimeList};
SwitchableSetting<u8, true> volume{100, 0, 200, "volume"}; Setting<std::string> audio_input_device_id{linkage, "auto", "input_device", Category::Audio,
Setting<bool> dump_audio_commands{false, "dump_audio_commands"}; Specialization::RuntimeList};
SwitchableSetting<AudioMode, true> sound_index{
linkage, AudioMode::Stereo, AudioMode::Mono, AudioMode::Surround,
"sound_index", Category::SystemAudio, Specialization::Default, true,
true};
SwitchableSetting<u8, true> volume{linkage,
100,
0,
200,
"volume",
Category::Audio,
Specialization::Scalar | Specialization::Percentage,
true,
true};
Setting<bool, false> audio_muted{
linkage, false, "audio_muted", Category::Audio, Specialization::Default, false, true};
Setting<bool, false> dump_audio_commands{
linkage, false, "dump_audio_commands", Category::Audio, Specialization::Default, false};
// Core // Core
SwitchableSetting<bool> use_multi_core{true, "use_multi_core"}; SwitchableSetting<bool> use_multi_core{linkage, true, "use_multi_core", Category::Core};
SwitchableSetting<bool> use_unsafe_extended_memory_layout{false, SwitchableSetting<MemoryLayout, true> memory_layout_mode{linkage,
"use_unsafe_extended_memory_layout"}; MemoryLayout::Memory_4Gb,
MemoryLayout::Memory_4Gb,
MemoryLayout::Memory_8Gb,
"memory_layout_mode",
Category::Core};
SwitchableSetting<bool> use_speed_limit{
linkage, true, "use_speed_limit", Category::Core, Specialization::Paired, false, true};
SwitchableSetting<u16, true> speed_limit{linkage,
100,
0,
9999,
"speed_limit",
Category::Core,
Specialization::Countable | Specialization::Percentage,
true,
true,
&use_speed_limit};
// Cpu // Cpu
SwitchableSetting<CPUAccuracy, true> cpu_accuracy{CPUAccuracy::Auto, CPUAccuracy::Auto, SwitchableSetting<CpuAccuracy, true> cpu_accuracy{linkage, CpuAccuracy::Auto,
CPUAccuracy::Paranoid, "cpu_accuracy"}; CpuAccuracy::Auto, CpuAccuracy::Paranoid,
// TODO: remove cpu_accuracy_first_time, migration setting added 8 July 2021 "cpu_accuracy", Category::Cpu};
Setting<bool> cpu_accuracy_first_time{true, "cpu_accuracy_first_time"}; Setting<bool> cpu_debug_mode{linkage, false, "cpu_debug_mode", Category::CpuDebug};
Setting<bool> cpu_debug_mode{false, "cpu_debug_mode"};
Setting<bool> cpuopt_page_tables{true, "cpuopt_page_tables"}; Setting<bool> cpuopt_page_tables{linkage, true, "cpuopt_page_tables", Category::CpuDebug};
Setting<bool> cpuopt_block_linking{true, "cpuopt_block_linking"}; Setting<bool> cpuopt_block_linking{linkage, true, "cpuopt_block_linking", Category::CpuDebug};
Setting<bool> cpuopt_return_stack_buffer{true, "cpuopt_return_stack_buffer"}; Setting<bool> cpuopt_return_stack_buffer{linkage, true, "cpuopt_return_stack_buffer",
Setting<bool> cpuopt_fast_dispatcher{true, "cpuopt_fast_dispatcher"}; Category::CpuDebug};
Setting<bool> cpuopt_context_elimination{true, "cpuopt_context_elimination"}; Setting<bool> cpuopt_fast_dispatcher{linkage, true, "cpuopt_fast_dispatcher",
Setting<bool> cpuopt_const_prop{true, "cpuopt_const_prop"}; Category::CpuDebug};
Setting<bool> cpuopt_misc_ir{true, "cpuopt_misc_ir"}; Setting<bool> cpuopt_context_elimination{linkage, true, "cpuopt_context_elimination",
Setting<bool> cpuopt_reduce_misalign_checks{true, "cpuopt_reduce_misalign_checks"}; Category::CpuDebug};
Setting<bool> cpuopt_fastmem{true, "cpuopt_fastmem"}; Setting<bool> cpuopt_const_prop{linkage, true, "cpuopt_const_prop", Category::CpuDebug};
Setting<bool> cpuopt_fastmem_exclusives{true, "cpuopt_fastmem_exclusives"}; Setting<bool> cpuopt_misc_ir{linkage, true, "cpuopt_misc_ir", Category::CpuDebug};
Setting<bool> cpuopt_recompile_exclusives{true, "cpuopt_recompile_exclusives"}; Setting<bool> cpuopt_reduce_misalign_checks{linkage, true, "cpuopt_reduce_misalign_checks",
Setting<bool> cpuopt_ignore_memory_aborts{true, "cpuopt_ignore_memory_aborts"}; Category::CpuDebug};
Setting<bool> cpuopt_fastmem{linkage, true, "cpuopt_fastmem", Category::CpuDebug};
Setting<bool> cpuopt_fastmem_exclusives{linkage, true, "cpuopt_fastmem_exclusives",
Category::CpuDebug};
Setting<bool> cpuopt_recompile_exclusives{linkage, true, "cpuopt_recompile_exclusives",
Category::CpuDebug};
Setting<bool> cpuopt_ignore_memory_aborts{linkage, true, "cpuopt_ignore_memory_aborts",
Category::CpuDebug};
SwitchableSetting<bool> cpuopt_unsafe_unfuse_fma{true, "cpuopt_unsafe_unfuse_fma"}; SwitchableSetting<bool> cpuopt_unsafe_unfuse_fma{linkage, true, "cpuopt_unsafe_unfuse_fma",
SwitchableSetting<bool> cpuopt_unsafe_reduce_fp_error{true, "cpuopt_unsafe_reduce_fp_error"}; Category::CpuUnsafe};
SwitchableSetting<bool> cpuopt_unsafe_reduce_fp_error{
linkage, true, "cpuopt_unsafe_reduce_fp_error", Category::CpuUnsafe};
SwitchableSetting<bool> cpuopt_unsafe_ignore_standard_fpcr{ SwitchableSetting<bool> cpuopt_unsafe_ignore_standard_fpcr{
true, "cpuopt_unsafe_ignore_standard_fpcr"}; linkage, true, "cpuopt_unsafe_ignore_standard_fpcr", Category::CpuUnsafe};
SwitchableSetting<bool> cpuopt_unsafe_inaccurate_nan{true, "cpuopt_unsafe_inaccurate_nan"}; SwitchableSetting<bool> cpuopt_unsafe_inaccurate_nan{
SwitchableSetting<bool> cpuopt_unsafe_fastmem_check{true, "cpuopt_unsafe_fastmem_check"}; linkage, true, "cpuopt_unsafe_inaccurate_nan", Category::CpuUnsafe};
SwitchableSetting<bool> cpuopt_unsafe_fastmem_check{
linkage, true, "cpuopt_unsafe_fastmem_check", Category::CpuUnsafe};
SwitchableSetting<bool> cpuopt_unsafe_ignore_global_monitor{ SwitchableSetting<bool> cpuopt_unsafe_ignore_global_monitor{
true, "cpuopt_unsafe_ignore_global_monitor"}; linkage, true, "cpuopt_unsafe_ignore_global_monitor", Category::CpuUnsafe};
// Renderer // Renderer
SwitchableSetting<RendererBackend, true> renderer_backend{ SwitchableSetting<RendererBackend, true> renderer_backend{
RendererBackend::Vulkan, RendererBackend::OpenGL, RendererBackend::Null, "backend"}; linkage, RendererBackend::Vulkan, RendererBackend::OpenGL, RendererBackend::Null,
SwitchableSetting<bool> async_presentation{false, "async_presentation"}; "backend", Category::Renderer};
SwitchableSetting<bool> renderer_force_max_clock{false, "force_max_clock"}; SwitchableSetting<ShaderBackend, true> shader_backend{
Setting<bool> renderer_debug{false, "debug"}; linkage, ShaderBackend::Glsl, ShaderBackend::Glsl, ShaderBackend::SpirV,
Setting<bool> renderer_shader_feedback{false, "shader_feedback"}; "shader_backend", Category::Renderer, Specialization::RuntimeList};
Setting<bool> enable_nsight_aftermath{false, "nsight_aftermath"}; SwitchableSetting<int> vulkan_device{linkage, 0, "vulkan_device", Category::Renderer,
Setting<bool> disable_shader_loop_safety_checks{false, "disable_shader_loop_safety_checks"}; Specialization::RuntimeList};
SwitchableSetting<int> vulkan_device{0, "vulkan_device"};
ResolutionScalingInfo resolution_info{}; SwitchableSetting<bool> use_disk_shader_cache{linkage, true, "use_disk_shader_cache",
SwitchableSetting<ResolutionSetup> resolution_setup{ResolutionSetup::Res1X, "resolution_setup"}; Category::Renderer};
SwitchableSetting<ScalingFilter> scaling_filter{ScalingFilter::Bilinear, "scaling_filter"}; SwitchableSetting<bool> use_asynchronous_gpu_emulation{
SwitchableSetting<int, true> fsr_sharpening_slider{25, 0, 200, "fsr_sharpening_slider"}; linkage, true, "use_asynchronous_gpu_emulation", Category::Renderer};
SwitchableSetting<AntiAliasing> anti_aliasing{AntiAliasing::None, "anti_aliasing"}; SwitchableSetting<AstcDecodeMode, true> accelerate_astc{linkage,
AstcDecodeMode::Gpu,
AstcDecodeMode::Cpu,
AstcDecodeMode::CpuAsynchronous,
"accelerate_astc",
Category::Renderer};
Setting<VSyncMode, true> vsync_mode{
linkage, VSyncMode::Fifo, VSyncMode::Immediate, VSyncMode::FifoRelaxed,
"use_vsync", Category::Renderer, Specialization::RuntimeList, true,
true};
SwitchableSetting<NvdecEmulation> nvdec_emulation{linkage, NvdecEmulation::Gpu,
"nvdec_emulation", Category::Renderer};
// *nix platforms may have issues with the borderless windowed fullscreen mode. // *nix platforms may have issues with the borderless windowed fullscreen mode.
// Default to exclusive fullscreen on these platforms for now. // Default to exclusive fullscreen on these platforms for now.
SwitchableSetting<FullscreenMode, true> fullscreen_mode{ SwitchableSetting<FullscreenMode, true> fullscreen_mode{linkage,
#ifdef _WIN32 #ifdef _WIN32
FullscreenMode::Borderless, FullscreenMode::Borderless,
#else #else
FullscreenMode::Exclusive, FullscreenMode::Exclusive,
#endif #endif
FullscreenMode::Borderless, FullscreenMode::Exclusive, "fullscreen_mode"}; FullscreenMode::Borderless,
SwitchableSetting<int, true> aspect_ratio{0, 0, 4, "aspect_ratio"}; FullscreenMode::Exclusive,
SwitchableSetting<int, true> max_anisotropy{0, 0, 5, "max_anisotropy"}; "fullscreen_mode",
SwitchableSetting<bool> use_speed_limit{true, "use_speed_limit"}; Category::Renderer,
SwitchableSetting<u16, true> speed_limit{100, 0, 9999, "speed_limit"}; Specialization::Default,
SwitchableSetting<bool> use_disk_shader_cache{true, "use_disk_shader_cache"}; true,
SwitchableSetting<GPUAccuracy, true> gpu_accuracy{GPUAccuracy::High, GPUAccuracy::Normal, true};
GPUAccuracy::Extreme, "gpu_accuracy"}; SwitchableSetting<AspectRatio, true> aspect_ratio{linkage,
SwitchableSetting<bool> use_asynchronous_gpu_emulation{true, "use_asynchronous_gpu_emulation"}; AspectRatio::R16_9,
SwitchableSetting<NvdecEmulation> nvdec_emulation{NvdecEmulation::GPU, "nvdec_emulation"}; AspectRatio::R16_9,
SwitchableSetting<bool> accelerate_astc{true, "accelerate_astc"}; AspectRatio::Stretch,
SwitchableSetting<bool> async_astc{false, "async_astc"}; "aspect_ratio",
Setting<VSyncMode, true> vsync_mode{VSyncMode::FIFO, VSyncMode::Immediate, Category::Renderer,
VSyncMode::FIFORelaxed, "use_vsync"}; Specialization::Default,
SwitchableSetting<bool> use_reactive_flushing{true, "use_reactive_flushing"}; true,
SwitchableSetting<ShaderBackend, true> shader_backend{ShaderBackend::GLSL, ShaderBackend::GLSL, true};
ShaderBackend::SPIRV, "shader_backend"};
SwitchableSetting<bool> use_asynchronous_shaders{false, "use_asynchronous_shaders"};
SwitchableSetting<bool> use_fast_gpu_time{true, "use_fast_gpu_time"};
SwitchableSetting<bool> use_vulkan_driver_pipeline_cache{true,
"use_vulkan_driver_pipeline_cache"};
SwitchableSetting<bool> enable_compute_pipelines{false, "enable_compute_pipelines"};
SwitchableSetting<AstcRecompression, true> astc_recompression{
AstcRecompression::Uncompressed, AstcRecompression::Uncompressed, AstcRecompression::Bc3,
"astc_recompression"};
SwitchableSetting<bool> use_video_framerate{false, "use_video_framerate"};
SwitchableSetting<bool> barrier_feedback_loops{true, "barrier_feedback_loops"};
SwitchableSetting<u8> bg_red{0, "bg_red"}; ResolutionScalingInfo resolution_info{};
SwitchableSetting<u8> bg_green{0, "bg_green"}; SwitchableSetting<ResolutionSetup> resolution_setup{linkage, ResolutionSetup::Res1X,
SwitchableSetting<u8> bg_blue{0, "bg_blue"}; "resolution_setup", Category::Renderer};
SwitchableSetting<ScalingFilter> scaling_filter{linkage,
ScalingFilter::Bilinear,
"scaling_filter",
Category::Renderer,
Specialization::Default,
true,
true};
SwitchableSetting<AntiAliasing> anti_aliasing{linkage,
AntiAliasing::None,
"anti_aliasing",
Category::Renderer,
Specialization::Default,
true,
true};
SwitchableSetting<int, true> fsr_sharpening_slider{linkage,
25,
0,
200,
"fsr_sharpening_slider",
Category::Renderer,
Specialization::Scalar |
Specialization::Percentage,
true,
true};
SwitchableSetting<u8, false> bg_red{
linkage, 0, "bg_red", Category::Renderer, Specialization::Default, true, true};
SwitchableSetting<u8, false> bg_green{
linkage, 0, "bg_green", Category::Renderer, Specialization::Default, true, true};
SwitchableSetting<u8, false> bg_blue{
linkage, 0, "bg_blue", Category::Renderer, Specialization::Default, true, true};
SwitchableSetting<GpuAccuracy, true> gpu_accuracy{linkage,
GpuAccuracy::High,
GpuAccuracy::Normal,
GpuAccuracy::Extreme,
"gpu_accuracy",
Category::RendererAdvanced,
Specialization::Default,
true,
true};
SwitchableSetting<AnisotropyMode, true> max_anisotropy{
linkage, AnisotropyMode::Automatic, AnisotropyMode::Automatic, AnisotropyMode::X16,
"max_anisotropy", Category::RendererAdvanced};
SwitchableSetting<AstcRecompression, true> astc_recompression{linkage,
AstcRecompression::Uncompressed,
AstcRecompression::Uncompressed,
AstcRecompression::Bc3,
"astc_recompression",
Category::RendererAdvanced};
SwitchableSetting<bool> async_presentation{linkage, false, "async_presentation",
Category::RendererAdvanced};
SwitchableSetting<bool> renderer_force_max_clock{linkage, false, "force_max_clock",
Category::RendererAdvanced};
SwitchableSetting<bool> use_reactive_flushing{linkage, true, "use_reactive_flushing",
Category::RendererAdvanced};
SwitchableSetting<bool> use_asynchronous_shaders{linkage, false, "use_asynchronous_shaders",
Category::RendererAdvanced};
SwitchableSetting<bool> use_fast_gpu_time{
linkage, true, "use_fast_gpu_time", Category::RendererAdvanced, Specialization::Default,
true, true};
SwitchableSetting<bool> use_vulkan_driver_pipeline_cache{linkage,
true,
"use_vulkan_driver_pipeline_cache",
Category::RendererAdvanced,
Specialization::Default,
true,
true};
SwitchableSetting<bool> enable_compute_pipelines{linkage, false, "enable_compute_pipelines",
Category::RendererAdvanced};
SwitchableSetting<bool> use_video_framerate{linkage, false, "use_video_framerate",
Category::RendererAdvanced};
SwitchableSetting<bool> barrier_feedback_loops{linkage, true, "barrier_feedback_loops",
Category::RendererAdvanced};
Setting<bool> renderer_debug{linkage, false, "debug", Category::RendererDebug};
Setting<bool> renderer_shader_feedback{linkage, false, "shader_feedback",
Category::RendererDebug};
Setting<bool> enable_nsight_aftermath{linkage, false, "nsight_aftermath",
Category::RendererDebug};
Setting<bool> disable_shader_loop_safety_checks{
linkage, false, "disable_shader_loop_safety_checks", Category::RendererDebug};
// System // System
SwitchableSetting<std::optional<u32>> rng_seed{std::optional<u32>(), "rng_seed"}; SwitchableSetting<Language, true> language_index{linkage,
Setting<std::string> device_name{"Yuzu", "device_name"}; Language::EnglishAmerican,
Language::Japanese,
Language::PortugueseBrazilian,
"language_index",
Category::System};
SwitchableSetting<Region, true> region_index{linkage, Region::Usa, Region::Japan,
Region::Taiwan, "region_index", Category::System};
SwitchableSetting<TimeZone, true> time_zone_index{linkage, TimeZone::Auto,
TimeZone::Auto, TimeZone::Zulu,
"time_zone_index", Category::System};
// Measured in seconds since epoch // Measured in seconds since epoch
std::optional<s64> custom_rtc; SwitchableSetting<bool> custom_rtc_enabled{
linkage, false, "custom_rtc_enabled", Category::System, Specialization::Paired, true, true};
SwitchableSetting<s64> custom_rtc{
linkage, 0, "custom_rtc", Category::System, Specialization::Time,
true, true, &custom_rtc_enabled};
// 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`
s64 custom_rtc_differential; s64 custom_rtc_differential;
SwitchableSetting<bool> rng_seed_enabled{
linkage, false, "rng_seed_enabled", Category::System, Specialization::Paired, true, true};
SwitchableSetting<u32> rng_seed{
linkage, 0, "rng_seed", Category::System, Specialization::Hex,
true, true, &rng_seed_enabled};
Setting<std::string> device_name{
linkage, "yuzu", "device_name", Category::System, Specialization::Default, true, true};
Setting<s32> current_user{0, "current_user"}; Setting<s32> current_user{linkage, 0, "current_user", Category::System};
SwitchableSetting<s32, true> language_index{1, 0, 17, "language_index"};
SwitchableSetting<s32, true> region_index{1, 0, 6, "region_index"}; SwitchableSetting<bool> use_docked_mode{linkage, true, "use_docked_mode", Category::System};
SwitchableSetting<s32, true> time_zone_index{0, 0, 45, "time_zone_index"};
SwitchableSetting<s32, true> sound_index{1, 0, 2, "sound_index"};
// Controls // Controls
InputSetting<std::array<PlayerInput, 10>> players; InputSetting<std::array<PlayerInput, 10>> players;
SwitchableSetting<bool> use_docked_mode{true, "use_docked_mode"}; Setting<bool> enable_raw_input{
linkage, false, "enable_raw_input", Category::Controls, Specialization::Default,
// Only read/write enable_raw_input on Windows platforms
#ifdef _WIN32
true
#else
false
#endif
};
Setting<bool> controller_navigation{linkage, true, "controller_navigation", Category::Controls};
Setting<bool> enable_joycon_driver{linkage, true, "enable_joycon_driver", Category::Controls};
Setting<bool> enable_procon_driver{linkage, false, "enable_procon_driver", Category::Controls};
Setting<bool> enable_raw_input{false, "enable_raw_input"}; SwitchableSetting<bool> vibration_enabled{linkage, true, "vibration_enabled",
Setting<bool> controller_navigation{true, "controller_navigation"}; Category::Controls};
Setting<bool> enable_joycon_driver{true, "enable_joycon_driver"}; SwitchableSetting<bool> enable_accurate_vibrations{linkage, false, "enable_accurate_vibrations",
Setting<bool> enable_procon_driver{false, "enable_procon_driver"}; Category::Controls};
SwitchableSetting<bool> vibration_enabled{true, "vibration_enabled"}; SwitchableSetting<bool> motion_enabled{linkage, true, "motion_enabled", Category::Controls};
SwitchableSetting<bool> enable_accurate_vibrations{false, "enable_accurate_vibrations"}; Setting<std::string> udp_input_servers{linkage, "127.0.0.1:26760", "udp_input_servers",
Category::Controls};
Setting<bool> enable_udp_controller{linkage, false, "enable_udp_controller",
Category::Controls};
SwitchableSetting<bool> motion_enabled{true, "motion_enabled"}; Setting<bool> pause_tas_on_load{linkage, true, "pause_tas_on_load", Category::Controls};
Setting<std::string> udp_input_servers{"127.0.0.1:26760", "udp_input_servers"}; Setting<bool> tas_enable{linkage, false, "tas_enable", Category::Controls};
Setting<bool> enable_udp_controller{false, "enable_udp_controller"}; Setting<bool> tas_loop{linkage, false, "tas_loop", Category::Controls};
Setting<bool> pause_tas_on_load{true, "pause_tas_on_load"}; Setting<bool> mouse_panning{
Setting<bool> tas_enable{false, "tas_enable"}; linkage, false, "mouse_panning", Category::Controls, Specialization::Default, false};
Setting<bool> tas_loop{false, "tas_loop"}; Setting<u8, true> mouse_panning_sensitivity{
linkage, 50, 1, 100, "mouse_panning_sensitivity", Category::Controls};
Setting<bool> mouse_enabled{linkage, false, "mouse_enabled", Category::Controls};
Setting<bool> mouse_panning{false, "mouse_panning"}; Setting<u8, true> mouse_panning_x_sensitivity{
Setting<u8, true> mouse_panning_x_sensitivity{50, 1, 100, "mouse_panning_x_sensitivity"}; linkage, 50, 1, 100, "mouse_panning_x_sensitivity", Category::Controls};
Setting<u8, true> mouse_panning_y_sensitivity{50, 1, 100, "mouse_panning_y_sensitivity"}; Setting<u8, true> mouse_panning_y_sensitivity{
Setting<u8, true> mouse_panning_deadzone_counterweight{20, 0, 100, linkage, 50, 1, 100, "mouse_panning_y_sensitivity", Category::Controls};
"mouse_panning_deadzone_counterweight"}; Setting<u8, true> mouse_panning_deadzone_counterweight{
Setting<u8, true> mouse_panning_decay_strength{18, 0, 100, "mouse_panning_decay_strength"}; linkage, 20, 0, 100, "mouse_panning_deadzone_counterweight", Category::Controls};
Setting<u8, true> mouse_panning_min_decay{6, 0, 100, "mouse_panning_min_decay"}; Setting<u8, true> mouse_panning_decay_strength{
linkage, 18, 0, 100, "mouse_panning_decay_strength", Category::Controls};
Setting<u8, true> mouse_panning_min_decay{
linkage, 6, 0, 100, "mouse_panning_min_decay", Category::Controls};
Setting<bool> mouse_enabled{false, "mouse_enabled"}; Setting<bool> emulate_analog_keyboard{linkage, false, "emulate_analog_keyboard",
Setting<bool> emulate_analog_keyboard{false, "emulate_analog_keyboard"}; Category::Controls};
Setting<bool> keyboard_enabled{false, "keyboard_enabled"}; Setting<bool> keyboard_enabled{linkage, false, "keyboard_enabled", Category::Controls};
Setting<bool> debug_pad_enabled{false, "debug_pad_enabled"}; Setting<bool> debug_pad_enabled{linkage, false, "debug_pad_enabled", Category::Controls};
ButtonsRaw debug_pad_buttons; ButtonsRaw debug_pad_buttons;
AnalogsRaw debug_pad_analogs; AnalogsRaw debug_pad_analogs;
TouchscreenInput touchscreen; TouchscreenInput touchscreen;
Setting<std::string> touch_device{"min_x:100,min_y:50,max_x:1800,max_y:850", "touch_device"}; Setting<std::string> touch_device{linkage, "min_x:100,min_y:50,max_x:1800,max_y:850",
Setting<int> touch_from_button_map_index{0, "touch_from_button_map"}; "touch_device", Category::Controls};
Setting<int> touch_from_button_map_index{linkage, 0, "touch_from_button_map",
Category::Controls};
std::vector<TouchFromButtonMap> touch_from_button_maps; std::vector<TouchFromButtonMap> touch_from_button_maps;
Setting<bool> enable_ring_controller{true, "enable_ring_controller"}; Setting<bool> enable_ring_controller{linkage, true, "enable_ring_controller",
Category::Controls};
RingconRaw ringcon_analogs; RingconRaw ringcon_analogs;
Setting<bool> enable_ir_sensor{false, "enable_ir_sensor"}; Setting<bool> enable_ir_sensor{linkage, false, "enable_ir_sensor", Category::Controls};
Setting<std::string> ir_sensor_device{"auto", "ir_sensor_device"}; Setting<std::string> ir_sensor_device{linkage, "auto", "ir_sensor_device", Category::Controls};
Setting<bool> random_amiibo_id{false, "random_amiibo_id"}; Setting<bool> random_amiibo_id{linkage, false, "random_amiibo_id", Category::Controls};
// Data Storage // Data Storage
Setting<bool> use_virtual_sd{true, "use_virtual_sd"}; Setting<bool> use_virtual_sd{linkage, true, "use_virtual_sd", Category::DataStorage};
Setting<bool> gamecard_inserted{false, "gamecard_inserted"}; Setting<bool> gamecard_inserted{linkage, false, "gamecard_inserted", Category::DataStorage};
Setting<bool> gamecard_current_game{false, "gamecard_current_game"}; Setting<bool> gamecard_current_game{linkage, false, "gamecard_current_game",
Setting<std::string> gamecard_path{std::string(), "gamecard_path"}; Category::DataStorage};
Setting<std::string> gamecard_path{linkage, std::string(), "gamecard_path",
Category::DataStorage};
// Debugging // Debugging
bool record_frame_times; bool record_frame_times;
Setting<bool> use_gdbstub{false, "use_gdbstub"}; Setting<bool> use_gdbstub{linkage, false, "use_gdbstub", Category::Debugging};
Setting<u16> gdbstub_port{6543, "gdbstub_port"}; Setting<u16> gdbstub_port{linkage, 6543, "gdbstub_port", Category::Debugging};
Setting<std::string> program_args{std::string(), "program_args"}; Setting<std::string> program_args{linkage, std::string(), "program_args", Category::Debugging};
Setting<bool> dump_exefs{false, "dump_exefs"}; Setting<bool> dump_exefs{linkage, false, "dump_exefs", Category::Debugging};
Setting<bool> dump_nso{false, "dump_nso"}; Setting<bool> dump_nso{linkage, false, "dump_nso", Category::Debugging};
Setting<bool> dump_shaders{false, "dump_shaders"}; Setting<bool> dump_shaders{
Setting<bool> dump_macros{false, "dump_macros"}; linkage, false, "dump_shaders", Category::DebuggingGraphics, Specialization::Default,
Setting<bool> enable_fs_access_log{false, "enable_fs_access_log"}; false};
Setting<bool> reporting_services{false, "reporting_services"}; Setting<bool> dump_macros{
Setting<bool> quest_flag{false, "quest_flag"}; linkage, false, "dump_macros", Category::DebuggingGraphics, Specialization::Default, false};
Setting<bool> disable_macro_jit{false, "disable_macro_jit"}; Setting<bool> enable_fs_access_log{linkage, false, "enable_fs_access_log", Category::Debugging};
Setting<bool> disable_macro_hle{false, "disable_macro_hle"}; Setting<bool> reporting_services{
Setting<bool> extended_logging{false, "extended_logging"}; linkage, false, "reporting_services", Category::Debugging, Specialization::Default, false};
Setting<bool> use_debug_asserts{false, "use_debug_asserts"}; Setting<bool> quest_flag{linkage, false, "quest_flag", Category::Debugging};
Setting<bool> use_auto_stub{false, "use_auto_stub"}; Setting<bool> disable_macro_jit{linkage, false, "disable_macro_jit",
Setting<bool> enable_all_controllers{false, "enable_all_controllers"}; Category::DebuggingGraphics};
Setting<bool> create_crash_dumps{false, "create_crash_dumps"}; Setting<bool> disable_macro_hle{linkage, false, "disable_macro_hle",
Setting<bool> perform_vulkan_check{true, "perform_vulkan_check"}; Category::DebuggingGraphics};
Setting<bool> extended_logging{
linkage, false, "extended_logging", Category::Debugging, Specialization::Default, false};
Setting<bool> use_debug_asserts{linkage, false, "use_debug_asserts", Category::Debugging};
Setting<bool> use_auto_stub{
linkage, false, "use_auto_stub", Category::Debugging, Specialization::Default, false};
Setting<bool> enable_all_controllers{linkage, false, "enable_all_controllers",
Category::Debugging};
Setting<bool> create_crash_dumps{linkage, false, "create_crash_dumps", Category::Debugging};
Setting<bool> perform_vulkan_check{linkage, true, "perform_vulkan_check", Category::Debugging};
// Miscellaneous // Miscellaneous
Setting<std::string> log_filter{"*:Info", "log_filter"}; Setting<std::string> log_filter{linkage, "*:Info", "log_filter", Category::Miscellaneous};
Setting<bool> use_dev_keys{false, "use_dev_keys"}; Setting<bool> use_dev_keys{linkage, false, "use_dev_keys", Category::Miscellaneous};
// Network // Network
Setting<std::string> network_interface{std::string(), "network_interface"}; Setting<std::string> network_interface{linkage, std::string(), "network_interface",
Category::Network};
// WebService // WebService
Setting<bool> enable_telemetry{true, "enable_telemetry"}; Setting<bool> enable_telemetry{linkage, true, "enable_telemetry", Category::WebService};
Setting<std::string> web_api_url{"https://api.yuzu-emu.org", "web_api_url"}; Setting<std::string> web_api_url{linkage, "https://api.yuzu-emu.org", "web_api_url",
Setting<std::string> yuzu_username{std::string(), "yuzu_username"}; Category::WebService};
Setting<std::string> yuzu_token{std::string(), "yuzu_token"}; Setting<std::string> yuzu_username{linkage, std::string(), "yuzu_username",
Category::WebService};
Setting<std::string> yuzu_token{linkage, std::string(), "yuzu_token", Category::WebService};
// Add-Ons // Add-Ons
std::map<u64, std::vector<std::string>> disabled_addons; std::map<u64, std::vector<std::string>> disabled_addons;
@ -600,9 +514,6 @@ struct Values {
extern Values values; extern Values values;
bool IsConfiguringGlobal();
void SetConfiguringGlobal(bool is_global);
bool IsGPULevelExtreme(); bool IsGPULevelExtreme();
bool IsGPULevelHigh(); bool IsGPULevelHigh();
@ -610,7 +521,7 @@ bool IsFastmemEnabled();
float Volume(); float Volume();
std::string GetTimeZoneString(); std::string GetTimeZoneString(TimeZone time_zone);
void LogSettings(); void LogSettings();
@ -619,4 +530,7 @@ void UpdateRescalingInfo();
// Restore the global state of all applicable settings in the Values struct // Restore the global state of all applicable settings in the Values struct
void RestoreGlobalState(bool is_powered_on); void RestoreGlobalState(bool is_powered_on);
bool IsConfiguringGlobal();
void SetConfiguringGlobal(bool is_global);
} // namespace Settings } // namespace Settings

@ -0,0 +1,58 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <string>
#include "common/settings_common.h"
namespace Settings {
BasicSetting::BasicSetting(Linkage& linkage, const std::string& name, enum Category category_,
bool save_, bool runtime_modifiable_, u32 specialization_,
BasicSetting* other_setting_)
: label{name}, category{category_}, id{linkage.count}, save{save_},
runtime_modifiable{runtime_modifiable_}, specialization{specialization_},
other_setting{other_setting_} {
linkage.by_category[category].push_back(this);
linkage.count++;
}
BasicSetting::~BasicSetting() = default;
std::string BasicSetting::ToStringGlobal() const {
return this->ToString();
}
bool BasicSetting::UsingGlobal() const {
return true;
}
void BasicSetting::SetGlobal(bool global) {}
bool BasicSetting::Save() const {
return save;
}
bool BasicSetting::RuntimeModfiable() const {
return runtime_modifiable;
}
Category BasicSetting::GetCategory() const {
return category;
}
u32 BasicSetting::Specialization() const {
return specialization;
}
BasicSetting* BasicSetting::PairedSetting() const {
return other_setting;
}
const std::string& BasicSetting::GetLabel() const {
return label;
}
Linkage::Linkage(u32 initial_count) : count{initial_count} {}
Linkage::~Linkage() = default;
} // namespace Settings

@ -0,0 +1,256 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <functional>
#include <map>
#include <string>
#include <typeindex>
#include "common/common_types.h"
namespace Settings {
enum class Category : u32 {
Audio,
Core,
Cpu,
CpuDebug,
CpuUnsafe,
Renderer,
RendererAdvanced,
RendererDebug,
System,
SystemAudio,
DataStorage,
Debugging,
DebuggingGraphics,
Miscellaneous,
Network,
WebService,
AddOns,
Controls,
Ui,
UiGeneral,
UiLayout,
UiGameList,
Screenshots,
Shortcuts,
Multiplayer,
Services,
Paths,
MaxEnum,
};
constexpr u8 SpecializationTypeMask = 0xf;
constexpr u8 SpecializationAttributeMask = 0xf0;
constexpr u8 SpecializationAttributeOffset = 4;
// Scalar and countable could have better names
enum Specialization : u8 {
Default = 0,
Time = 1, // Duration or specific moment in time
Hex = 2, // Hexadecimal number
List = 3, // Setting has specific members
RuntimeList = 4, // Members of the list are determined during runtime
Scalar = 5, // Values are continuous
Countable = 6, // Can be stepped through
Paired = 7, // Another setting is associated with this setting
Percentage = (1 << SpecializationAttributeOffset), // Should be represented as a percentage
};
class BasicSetting;
class Linkage {
public:
explicit Linkage(u32 initial_count = 0);
~Linkage();
std::map<Category, std::vector<BasicSetting*>> by_category{};
std::vector<std::function<void()>> restore_functions{};
u32 count;
};
/**
* BasicSetting is an abstract class that only keeps track of metadata. The string methods are
* available to get data values out.
*/
class BasicSetting {
protected:
explicit BasicSetting(Linkage& linkage, const std::string& name, Category category_, bool save_,
bool runtime_modifiable_, u32 specialization,
BasicSetting* other_setting);
public:
virtual ~BasicSetting();
/*
* Data retrieval
*/
/**
* Returns a string representation of the internal data. If the Setting is Switchable, it
* respects the internal global state: it is based on GetValue().
*
* @returns A string representation of the internal data.
*/
[[nodiscard]] virtual std::string ToString() const = 0;
/**
* Returns a string representation of the global version of internal data. If the Setting is
* not Switchable, it behaves like ToString.
*
* @returns A string representation of the global version of internal data.
*/
[[nodiscard]] virtual std::string ToStringGlobal() const;
/**
* @returns A string representation of the Setting's default value.
*/
[[nodiscard]] virtual std::string DefaultToString() const = 0;
/**
* Returns a string representation of the minimum value of the setting. If the Setting is not
* ranged, the string represents the default initialization of the data type.
*
* @returns A string representation of the minimum value of the setting.
*/
[[nodiscard]] virtual std::string MinVal() const = 0;
/**
* Returns a string representation of the maximum value of the setting. If the Setting is not
* ranged, the string represents the default initialization of the data type.
*
* @returns A string representation of the maximum value of the setting.
*/
[[nodiscard]] virtual std::string MaxVal() const = 0;
/**
* Takes a string input, converts it to the internal data type if necessary, and then runs
* SetValue with it.
*
* @param load String of the input data.
*/
virtual void LoadString(const std::string& load) = 0;
/**
* Returns a string representation of the data. If the data is an enum, it returns a string of
* the enum value. If the internal data type is not an enum, this is equivalent to ToString.
*
* e.g. renderer_backend.Canonicalize() == "OpenGL"
*
* @returns Canonicalized string representation of the internal data
*/
[[nodiscard]] virtual std::string Canonicalize() const = 0;
/*
* Metadata
*/
/**
* @returns A unique identifier for the Setting's internal data type.
*/
[[nodiscard]] virtual std::type_index TypeId() const = 0;
/**
* Returns true if the Setting's internal data type is an enum.
*
* @returns True if the Setting's internal data type is an enum
*/
[[nodiscard]] virtual constexpr bool IsEnum() const = 0;
/**
* Returns true if the current setting is Switchable.
*
* @returns If the setting is a SwitchableSetting
*/
[[nodiscard]] virtual constexpr bool Switchable() const {
return false;
}
/**
* Returns true to suggest that a frontend can read or write the setting to a configuration
* file.
*
* @returns The save preference
*/
[[nodiscard]] bool Save() const;
/**
* @returns true if the current setting can be changed while the guest is running.
*/
[[nodiscard]] bool RuntimeModfiable() const;
/**
* @returns A unique number corresponding to the setting.
*/
[[nodiscard]] constexpr u32 Id() const {
return id;
}
/**
* Returns the setting's category AKA INI group.
*
* @returns The setting's category
*/
[[nodiscard]] Category GetCategory() const;
/**
* @returns Extra metadata for data representation in frontend implementations.
*/
[[nodiscard]] u32 Specialization() const;
/**
* @returns Another BasicSetting if one is paired, or nullptr otherwise.
*/
[[nodiscard]] BasicSetting* PairedSetting() const;
/**
* Returns the label this setting was created with.
*
* @returns A reference to the label
*/
[[nodiscard]] const std::string& GetLabel() const;
/**
* @returns If the Setting checks input values for valid ranges.
*/
[[nodiscard]] virtual constexpr bool Ranged() const = 0;
/**
* @returns The index of the enum if the underlying setting type is an enum, else max of u32.
*/
[[nodiscard]] virtual constexpr u32 EnumIndex() const = 0;
/*
* Switchable settings
*/
/**
* Sets a setting's global state. True means use the normal setting, false to use a custom
* value. Has no effect if the Setting is not Switchable.
*
* @param global The desired state
*/
virtual void SetGlobal(bool global);
/**
* Returns true if the setting is using the normal setting value. Always true if the setting is
* not Switchable.
*
* @returns The Setting's global state
*/
[[nodiscard]] virtual bool UsingGlobal() const;
private:
const std::string label; ///< The setting's label
const Category category; ///< The setting's category AKA INI group
const u32 id; ///< Unique integer for the setting
const bool save; ///< Suggests if the setting should be saved and read to a frontend config
const bool
runtime_modifiable; ///< Suggests if the setting can be modified while a guest is running
const u32 specialization; ///< Extra data to identify representation of a setting
BasicSetting* const other_setting; ///< A paired setting
};
} // namespace Settings

@ -0,0 +1,214 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <string>
#include <utility>
#include <vector>
#include "common/common_types.h"
namespace Settings {
template <typename T>
struct EnumMetadata {
static constexpr std::vector<std::pair<std::string, T>> Canonicalizations();
static constexpr u32 Index();
};
#define PAIR_45(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_46(N, __VA_ARGS__))
#define PAIR_44(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_45(N, __VA_ARGS__))
#define PAIR_43(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_44(N, __VA_ARGS__))
#define PAIR_42(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_43(N, __VA_ARGS__))
#define PAIR_41(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_42(N, __VA_ARGS__))
#define PAIR_40(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_41(N, __VA_ARGS__))
#define PAIR_39(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_40(N, __VA_ARGS__))
#define PAIR_38(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_39(N, __VA_ARGS__))
#define PAIR_37(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_38(N, __VA_ARGS__))
#define PAIR_36(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_37(N, __VA_ARGS__))
#define PAIR_35(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_36(N, __VA_ARGS__))
#define PAIR_34(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_35(N, __VA_ARGS__))
#define PAIR_33(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_34(N, __VA_ARGS__))
#define PAIR_32(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_33(N, __VA_ARGS__))
#define PAIR_31(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_32(N, __VA_ARGS__))
#define PAIR_30(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_31(N, __VA_ARGS__))
#define PAIR_29(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_30(N, __VA_ARGS__))
#define PAIR_28(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_29(N, __VA_ARGS__))
#define PAIR_27(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_28(N, __VA_ARGS__))
#define PAIR_26(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_27(N, __VA_ARGS__))
#define PAIR_25(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_26(N, __VA_ARGS__))
#define PAIR_24(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_25(N, __VA_ARGS__))
#define PAIR_23(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_24(N, __VA_ARGS__))
#define PAIR_22(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_23(N, __VA_ARGS__))
#define PAIR_21(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_22(N, __VA_ARGS__))
#define PAIR_20(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_21(N, __VA_ARGS__))
#define PAIR_19(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_20(N, __VA_ARGS__))
#define PAIR_18(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_19(N, __VA_ARGS__))
#define PAIR_17(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_18(N, __VA_ARGS__))
#define PAIR_16(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_17(N, __VA_ARGS__))
#define PAIR_15(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_16(N, __VA_ARGS__))
#define PAIR_14(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_15(N, __VA_ARGS__))
#define PAIR_13(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_14(N, __VA_ARGS__))
#define PAIR_12(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_13(N, __VA_ARGS__))
#define PAIR_11(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_12(N, __VA_ARGS__))
#define PAIR_10(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_11(N, __VA_ARGS__))
#define PAIR_9(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_10(N, __VA_ARGS__))
#define PAIR_8(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_9(N, __VA_ARGS__))
#define PAIR_7(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_8(N, __VA_ARGS__))
#define PAIR_6(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_7(N, __VA_ARGS__))
#define PAIR_5(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_6(N, __VA_ARGS__))
#define PAIR_4(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_5(N, __VA_ARGS__))
#define PAIR_3(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_4(N, __VA_ARGS__))
#define PAIR_2(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_3(N, __VA_ARGS__))
#define PAIR_1(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_2(N, __VA_ARGS__))
#define PAIR(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_1(N, __VA_ARGS__))
#define ENUM(NAME, ...) \
enum class NAME : u32 { __VA_ARGS__ }; \
template <> \
constexpr std::vector<std::pair<std::string, NAME>> EnumMetadata<NAME>::Canonicalizations() { \
return {PAIR(NAME, __VA_ARGS__)}; \
} \
template <> \
constexpr u32 EnumMetadata<NAME>::Index() { \
return __COUNTER__; \
}
// AudioEngine must be specified discretely due to having existing but slightly different
// canonicalizations
// TODO (lat9nq): Remove explicit definition of AudioEngine/sink_id
enum class AudioEngine : u32 {
Auto,
Cubeb,
Sdl2,
Null,
};
template <>
constexpr std::vector<std::pair<std::string, AudioEngine>>
EnumMetadata<AudioEngine>::Canonicalizations() {
return {
{"auto", AudioEngine::Auto},
{"cubeb", AudioEngine::Cubeb},
{"sdl2", AudioEngine::Sdl2},
{"null", AudioEngine::Null},
};
}
template <>
constexpr u32 EnumMetadata<AudioEngine>::Index() {
// This is just a sufficiently large number that is more than the number of other enums declared
// here
return 100;
}
ENUM(AudioMode, Mono, Stereo, Surround);
ENUM(Language, Japanese, EnglishAmerican, French, German, Italian, Spanish, Chinese, Korean, Dutch,
Portuguese, Russian, Taiwanese, EnglishBritish, FrenchCanadian, SpanishLatin,
ChineseSimplified, ChineseTraditional, PortugueseBrazilian);
ENUM(Region, Japan, Usa, Europe, Australia, China, Korea, Taiwan);
ENUM(TimeZone, Auto, Default, Cet, Cst6Cdt, Cuba, Eet, Egypt, Eire, Est, Est5Edt, Gb, GbEire, Gmt,
GmtPlusZero, GmtMinusZero, GmtZero, Greenwich, Hongkong, Hst, Iceland, Iran, Israel, Jamaica,
Japan, Kwajalein, Libya, Met, Mst, Mst7Mdt, Navajo, Nz, NzChat, Poland, Portugal, Prc, Pst8Pdt,
Roc, Rok, Singapore, Turkey, Uct, Universal, Utc, WSu, Wet, Zulu);
ENUM(AnisotropyMode, Automatic, Default, X2, X4, X8, X16);
ENUM(AstcDecodeMode, Cpu, Gpu, CpuAsynchronous);
ENUM(AstcRecompression, Uncompressed, Bc1, Bc3);
ENUM(VSyncMode, Immediate, Mailbox, Fifo, FifoRelaxed);
ENUM(RendererBackend, OpenGL, Vulkan, Null);
ENUM(ShaderBackend, Glsl, Glasm, SpirV);
ENUM(GpuAccuracy, Normal, High, Extreme);
ENUM(CpuAccuracy, Auto, Accurate, Unsafe, Paranoid);
ENUM(MemoryLayout, Memory_4Gb, Memory_6Gb, Memory_8Gb);
ENUM(FullscreenMode, Borderless, Exclusive);
ENUM(NvdecEmulation, Off, Cpu, Gpu);
ENUM(ResolutionSetup, Res1_2X, Res3_4X, Res1X, Res3_2X, Res2X, Res3X, Res4X, Res5X, Res6X, Res7X,
Res8X);
ENUM(ScalingFilter, NearestNeighbor, Bilinear, Bicubic, Gaussian, ScaleForce, Fsr, MaxEnum);
ENUM(AntiAliasing, None, Fxaa, Smaa, MaxEnum);
ENUM(AspectRatio, R16_9, R4_3, R21_9, R16_10, Stretch);
template <typename Type>
constexpr std::string CanonicalizeEnum(Type id) {
const auto group = EnumMetadata<Type>::Canonicalizations();
for (auto& [name, value] : group) {
if (value == id) {
return name;
}
}
return "unknown";
}
template <typename Type>
constexpr Type ToEnum(const std::string& canonicalization) {
const auto group = EnumMetadata<Type>::Canonicalizations();
for (auto& [name, value] : group) {
if (name == canonicalization) {
return value;
}
}
return {};
}
} // namespace Settings
#undef ENUM
#undef PAIR
#undef PAIR_1
#undef PAIR_2
#undef PAIR_3
#undef PAIR_4
#undef PAIR_5
#undef PAIR_6
#undef PAIR_7
#undef PAIR_8
#undef PAIR_9
#undef PAIR_10
#undef PAIR_12
#undef PAIR_13
#undef PAIR_14
#undef PAIR_15
#undef PAIR_16
#undef PAIR_17
#undef PAIR_18
#undef PAIR_19
#undef PAIR_20
#undef PAIR_22
#undef PAIR_23
#undef PAIR_24
#undef PAIR_25
#undef PAIR_26
#undef PAIR_27
#undef PAIR_28
#undef PAIR_29
#undef PAIR_30
#undef PAIR_32
#undef PAIR_33
#undef PAIR_34
#undef PAIR_35
#undef PAIR_36
#undef PAIR_37
#undef PAIR_38
#undef PAIR_39
#undef PAIR_40
#undef PAIR_42
#undef PAIR_43
#undef PAIR_44
#undef PAIR_45

@ -0,0 +1,394 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <limits>
#include <map>
#include <optional>
#include <stdexcept>
#include <string>
#include <typeindex>
#include <typeinfo>
#include "common/common_types.h"
#include "common/settings_common.h"
#include "common/settings_enums.h"
namespace Settings {
/** The Setting class is a simple resource manager. It defines a label and default value
* alongside the actual value of the setting for simpler and less-error prone use with frontend
* configurations. Specifying a default value and label is required. A minimum and maximum range
* can be specified for sanitization.
*/
template <typename Type, bool ranged = false>
class Setting : public BasicSetting {
protected:
Setting() = default;
public:
/**
* Sets a default value, label, and setting value.
*
* @param linkage Setting registry
* @param default_val Initial value of the setting, and default value of the setting
* @param name Label for the setting
* @param category_ Category of the setting AKA INI group
* @param specialization_ Suggestion for how frontend implementations represent this in a config
* @param save_ Suggests that this should or should not be saved to a frontend config file
* @param runtime_modifiable_ Suggests whether this is modifiable while a guest is loaded
* @param other_setting_ A second Setting to associate to this one in metadata
*/
explicit Setting(Linkage& linkage, const Type& default_val, const std::string& name,
Category category_, u32 specialization_ = Specialization::Default,
bool save_ = true, bool runtime_modifiable_ = false,
BasicSetting* other_setting_ = nullptr)
requires(!ranged)
: BasicSetting(linkage, name, category_, save_, runtime_modifiable_, specialization_,
other_setting_),
value{default_val}, default_value{default_val} {}
virtual ~Setting() = default;
/**
* Sets a default value, minimum value, maximum value, and label.
*
* @param linkage Setting registry
* @param default_val Initial value of the setting, and default value of the setting
* @param min_val Sets the minimum allowed value of the setting
* @param max_val Sets the maximum allowed value of the setting
* @param name Label for the setting
* @param category_ Category of the setting AKA INI group
* @param specialization_ Suggestion for how frontend implementations represent this in a config
* @param save_ Suggests that this should or should not be saved to a frontend config file
* @param runtime_modifiable_ Suggests whether this is modifiable while a guest is loaded
* @param other_setting_ A second Setting to associate to this one in metadata
*/
explicit Setting(Linkage& linkage, const Type& default_val, const Type& min_val,
const Type& max_val, const std::string& name, Category category_,
u32 specialization_ = Specialization::Default, bool save_ = true,
bool runtime_modifiable_ = false, BasicSetting* other_setting_ = nullptr)
requires(ranged)
: BasicSetting(linkage, name, category_, save_, runtime_modifiable_, specialization_,
other_setting_),
value{default_val}, default_value{default_val}, maximum{max_val}, minimum{min_val} {}
/**
* Returns a reference to the setting's value.
*
* @returns A reference to the setting
*/
[[nodiscard]] virtual const Type& GetValue() const {
return value;
}
/**
* Sets the setting to the given value.
*
* @param val The desired value
*/
virtual void SetValue(const Type& val) {
Type temp{ranged ? std::clamp(val, minimum, maximum) : val};
std::swap(value, 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;
}
[[nodiscard]] constexpr bool IsEnum() const override {
return std::is_enum_v<Type>;
}
protected:
[[nodiscard]] std::string ToString(const Type& value_) const {
if constexpr (std::is_same_v<Type, std::string>) {
return value_;
} else if constexpr (std::is_same_v<Type, std::optional<u32>>) {
return value_.has_value() ? std::to_string(*value_) : "none";
} else if constexpr (std::is_same_v<Type, bool>) {
return value_ ? "true" : "false";
} else if constexpr (std::is_same_v<Type, AudioEngine>) {
// Compatibility with old AudioEngine setting being a string
return CanonicalizeEnum(value_);
} else {
return std::to_string(static_cast<u64>(value_));
}
}
public:
/**
* Converts the value of the setting to a std::string. Respects the global state if the setting
* has one.
*
* @returns The current setting as a std::string
*/
[[nodiscard]] std::string ToString() const override {
return ToString(this->GetValue());
}
/**
* Returns the default value of the setting as a std::string.
*
* @returns The default value as a string.
*/
[[nodiscard]] std::string DefaultToString() const override {
return ToString(default_value);
}
/**
* Assigns a value to the setting.
*
* @param val The desired setting value
*
* @returns A reference to the setting
*/
virtual const Type& operator=(const Type& val) {
Type temp{ranged ? std::clamp(val, minimum, maximum) : val};
std::swap(value, temp);
return value;
}
/**
* Returns a reference to the setting.
*
* @returns A reference to the setting
*/
explicit virtual operator const Type&() const {
return value;
}
/**
* Converts the given value to the Setting's type of value. Uses SetValue to enter the setting,
* thus respecting its constraints.
*
* @param input The desired value
*/
void LoadString(const std::string& input) override final {
if (input.empty()) {
this->SetValue(this->GetDefault());
return;
}
try {
if constexpr (std::is_same_v<Type, std::string>) {
this->SetValue(input);
} else if constexpr (std::is_same_v<Type, std::optional<u32>>) {
this->SetValue(static_cast<u32>(std::stoul(input)));
} else if constexpr (std::is_same_v<Type, bool>) {
this->SetValue(input == "true");
} else if constexpr (std::is_same_v<Type, AudioEngine>) {
this->SetValue(ToEnum<Type>(input));
} else {
this->SetValue(static_cast<Type>(std::stoll(input)));
}
} catch (std::invalid_argument&) {
this->SetValue(this->GetDefault());
}
}
[[nodiscard]] std::string constexpr Canonicalize() const override final {
if constexpr (std::is_enum_v<Type>) {
return CanonicalizeEnum(this->GetValue());
} else {
return ToString(this->GetValue());
}
}
/**
* Gives us another way to identify the setting without having to go through a string.
*
* @returns the type_index of the setting's type
*/
[[nodiscard]] std::type_index TypeId() const override final {
return std::type_index(typeid(Type));
}
[[nodiscard]] constexpr u32 EnumIndex() const override final {
if constexpr (std::is_enum_v<Type>) {
return EnumMetadata<Type>::Index();
} else {
return std::numeric_limits<u32>::max();
}
}
[[nodiscard]] std::string MinVal() const override final {
return this->ToString(minimum);
}
[[nodiscard]] std::string MaxVal() const override final {
return this->ToString(maximum);
}
[[nodiscard]] constexpr bool Ranged() const override {
return ranged;
}
protected:
Type value{}; ///< The setting
const Type default_value{}; ///< The default value
const Type maximum{}; ///< Maximum allowed value of the setting
const Type minimum{}; ///< Minimum allowed value of the setting
};
/**
* The SwitchableSetting class is a slightly more complex version of the Setting class. This adds a
* custom setting to switch to when a guest application specifically requires it. The effect is that
* other components of the emulator can access the setting's intended value without any need for the
* component to ask whether the custom or global setting is needed at the moment.
*
* By default, the global setting is used.
*/
template <typename Type, bool ranged = false>
class SwitchableSetting : virtual public Setting<Type, ranged> {
public:
/**
* Sets a default value, label, and setting value.
*
* @param linkage Setting registry
* @param default_val Initial value of the setting, and default value of the setting
* @param name Label for the setting
* @param category_ Category of the setting AKA INI group
* @param specialization_ Suggestion for how frontend implementations represent this in a config
* @param save_ Suggests that this should or should not be saved to a frontend config file
* @param runtime_modifiable_ Suggests whether this is modifiable while a guest is loaded
* @param other_setting_ A second Setting to associate to this one in metadata
*/
explicit SwitchableSetting(Linkage& linkage, const Type& default_val, const std::string& name,
Category category_, u32 specialization_ = Specialization::Default,
bool save_ = true, bool runtime_modifiable_ = false,
BasicSetting* other_setting_ = nullptr)
requires(!ranged)
: Setting<Type, false>{
linkage, default_val, name, category_, specialization_,
save_, runtime_modifiable_, other_setting_} {
linkage.restore_functions.emplace_back([this]() { this->SetGlobal(true); });
}
virtual ~SwitchableSetting() = default;
/**
* Sets a default value, minimum value, maximum value, and label.
*
* @param linkage Setting registry
* @param default_val Initial value of the setting, and default value of the setting
* @param min_val Sets the minimum allowed value of the setting
* @param max_val Sets the maximum allowed value of the setting
* @param name Label for the setting
* @param category_ Category of the setting AKA INI group
* @param specialization_ Suggestion for how frontend implementations represent this in a config
* @param save_ Suggests that this should or should not be saved to a frontend config file
* @param runtime_modifiable_ Suggests whether this is modifiable while a guest is loaded
* @param other_setting_ A second Setting to associate to this one in metadata
*/
explicit SwitchableSetting(Linkage& linkage, const Type& default_val, const Type& min_val,
const Type& max_val, const std::string& name, Category category_,
u32 specialization_ = Specialization::Default, bool save_ = true,
bool runtime_modifiable_ = false,
BasicSetting* other_setting_ = nullptr)
requires(ranged)
: Setting<Type, true>{linkage, default_val, min_val,
max_val, name, category_,
specialization_, save_, runtime_modifiable_,
other_setting_} {
linkage.restore_functions.emplace_back([this]() { this->SetGlobal(true); });
}
/**
* Tells this setting to represent either the global or custom setting when other member
* functions are used.
*
* @param to_global Whether to use the global or custom setting.
*/
void SetGlobal(bool to_global) override final {
use_global = to_global;
}
/**
* Returns whether this setting is using the global setting or not.
*
* @returns The global state
*/
[[nodiscard]] bool UsingGlobal() const override final {
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() const override final {
if (use_global) {
return this->value;
}
return custom;
}
[[nodiscard]] const Type& GetValue(bool need_global) const {
if (use_global || need_global) {
return this->value;
}
return custom;
}
/**
* Sets the current setting value depending on the global state.
*
* @param val The new value
*/
void SetValue(const Type& val) override final {
Type temp{ranged ? std::clamp(val, this->minimum, this->maximum) : val};
if (use_global) {
std::swap(this->value, temp);
} else {
std::swap(custom, temp);
}
}
[[nodiscard]] constexpr bool Switchable() const override final {
return true;
}
[[nodiscard]] std::string ToStringGlobal() const override final {
return this->ToString(this->value);
}
/**
* Assigns the current setting value depending on the global state.
*
* @param val The new value
*
* @returns A reference to the current setting value
*/
const Type& operator=(const Type& val) override final {
Type temp{ranged ? std::clamp(val, this->minimum, this->maximum) : val};
if (use_global) {
std::swap(this->value, temp);
return this->value;
}
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 override final {
if (use_global) {
return this->value;
}
return custom;
}
protected:
bool use_global{true}; ///< The setting's global state
Type custom{}; ///< The custom value of the setting
};
} // namespace Settings

@ -287,7 +287,7 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
} }
} else { } else {
// Unsafe optimizations // Unsafe optimizations
if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Unsafe) { if (Settings::values.cpu_accuracy.GetValue() == Settings::CpuAccuracy::Unsafe) {
config.unsafe_optimizations = true; config.unsafe_optimizations = true;
if (Settings::values.cpuopt_unsafe_unfuse_fma) { if (Settings::values.cpuopt_unsafe_unfuse_fma) {
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA; config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
@ -307,7 +307,7 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
} }
// Curated optimizations // Curated optimizations
if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Auto) { if (Settings::values.cpu_accuracy.GetValue() == Settings::CpuAccuracy::Auto) {
config.unsafe_optimizations = true; config.unsafe_optimizations = true;
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA; config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreStandardFPCRValue; config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreStandardFPCRValue;
@ -316,7 +316,7 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
} }
// Paranoia mode for debugging optimizations // Paranoia mode for debugging optimizations
if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Paranoid) { if (Settings::values.cpu_accuracy.GetValue() == Settings::CpuAccuracy::Paranoid) {
config.unsafe_optimizations = false; config.unsafe_optimizations = false;
config.optimizations = Dynarmic::no_optimizations; config.optimizations = Dynarmic::no_optimizations;
} }

@ -347,7 +347,7 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
} }
} else { } else {
// Unsafe optimizations // Unsafe optimizations
if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Unsafe) { if (Settings::values.cpu_accuracy.GetValue() == Settings::CpuAccuracy::Unsafe) {
config.unsafe_optimizations = true; config.unsafe_optimizations = true;
if (Settings::values.cpuopt_unsafe_unfuse_fma) { if (Settings::values.cpuopt_unsafe_unfuse_fma) {
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA; config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
@ -367,7 +367,7 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
} }
// Curated optimizations // Curated optimizations
if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Auto) { if (Settings::values.cpu_accuracy.GetValue() == Settings::CpuAccuracy::Auto) {
config.unsafe_optimizations = true; config.unsafe_optimizations = true;
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA; config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
config.fastmem_address_space_bits = 64; config.fastmem_address_space_bits = 64;
@ -375,7 +375,7 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
} }
// Paranoia mode for debugging optimizations // Paranoia mode for debugging optimizations
if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Paranoid) { if (Settings::values.cpu_accuracy.GetValue() == Settings::CpuAccuracy::Paranoid) {
config.unsafe_optimizations = false; config.unsafe_optimizations = false;
config.optimizations = Dynarmic::no_optimizations; config.optimizations = Dynarmic::no_optimizations;
} }

@ -12,6 +12,7 @@
#include "common/logging/log.h" #include "common/logging/log.h"
#include "common/microprofile.h" #include "common/microprofile.h"
#include "common/settings.h" #include "common/settings.h"
#include "common/settings_enums.h"
#include "common/string_util.h" #include "common/string_util.h"
#include "core/arm/exclusive_monitor.h" #include "core/arm/exclusive_monitor.h"
#include "core/core.h" #include "core/core.h"
@ -140,16 +141,13 @@ struct System::Impl {
device_memory = std::make_unique<Core::DeviceMemory>(); device_memory = std::make_unique<Core::DeviceMemory>();
is_multicore = Settings::values.use_multi_core.GetValue(); is_multicore = Settings::values.use_multi_core.GetValue();
extended_memory_layout = Settings::values.use_unsafe_extended_memory_layout.GetValue(); extended_memory_layout =
Settings::values.memory_layout_mode.GetValue() != Settings::MemoryLayout::Memory_4Gb;
core_timing.SetMulticore(is_multicore); core_timing.SetMulticore(is_multicore);
core_timing.Initialize([&system]() { system.RegisterHostThread(); }); core_timing.Initialize([&system]() { system.RegisterHostThread(); });
const auto posix_time = std::chrono::system_clock::now().time_since_epoch(); RefreshTime();
const auto current_time =
std::chrono::duration_cast<std::chrono::seconds>(posix_time).count();
Settings::values.custom_rtc_differential =
Settings::values.custom_rtc.value_or(current_time) - current_time;
// Create a default fs if one doesn't already exist. // Create a default fs if one doesn't already exist.
if (virtual_filesystem == nullptr) { if (virtual_filesystem == nullptr) {
@ -172,7 +170,8 @@ struct System::Impl {
void ReinitializeIfNecessary(System& system) { void ReinitializeIfNecessary(System& system) {
const bool must_reinitialize = const bool must_reinitialize =
is_multicore != Settings::values.use_multi_core.GetValue() || is_multicore != Settings::values.use_multi_core.GetValue() ||
extended_memory_layout != Settings::values.use_unsafe_extended_memory_layout.GetValue(); extended_memory_layout != (Settings::values.memory_layout_mode.GetValue() !=
Settings::MemoryLayout::Memory_4Gb);
if (!must_reinitialize) { if (!must_reinitialize) {
return; return;
@ -181,11 +180,22 @@ struct System::Impl {
LOG_DEBUG(Kernel, "Re-initializing"); LOG_DEBUG(Kernel, "Re-initializing");
is_multicore = Settings::values.use_multi_core.GetValue(); is_multicore = Settings::values.use_multi_core.GetValue();
extended_memory_layout = Settings::values.use_unsafe_extended_memory_layout.GetValue(); extended_memory_layout =
Settings::values.memory_layout_mode.GetValue() != Settings::MemoryLayout::Memory_4Gb;
Initialize(system); Initialize(system);
} }
void RefreshTime() {
const auto posix_time = std::chrono::system_clock::now().time_since_epoch();
const auto current_time =
std::chrono::duration_cast<std::chrono::seconds>(posix_time).count();
Settings::values.custom_rtc_differential =
(Settings::values.custom_rtc_enabled ? Settings::values.custom_rtc.GetValue()
: current_time) -
current_time;
}
void Run() { void Run() {
std::unique_lock<std::mutex> lk(suspend_guard); std::unique_lock<std::mutex> lk(suspend_guard);
@ -1028,6 +1038,8 @@ void System::Exit() {
} }
void System::ApplySettings() { void System::ApplySettings() {
impl->RefreshTime();
if (IsPoweredOn()) { if (IsPoweredOn()) {
Renderer().RefreshBaseSettings(); Renderer().RefreshBaseSettings();
} }

@ -68,7 +68,8 @@ NACP::NACP(VirtualFile file) {
NACP::~NACP() = default; NACP::~NACP() = default;
const LanguageEntry& NACP::GetLanguageEntry() const { const LanguageEntry& NACP::GetLanguageEntry() const {
Language language = language_to_codes[Settings::values.language_index.GetValue()]; Language language =
language_to_codes[static_cast<s32>(Settings::values.language_index.GetValue())];
{ {
const auto& language_entry = raw.language_entries.at(static_cast<u8>(language)); const auto& language_entry = raw.language_entries.at(static_cast<u8>(language));

@ -626,8 +626,8 @@ PatchManager::Metadata PatchManager::ParseControlNCA(const NCA& nca) const {
auto nacp = nacp_file == nullptr ? nullptr : std::make_unique<NACP>(nacp_file); auto nacp = nacp_file == nullptr ? nullptr : std::make_unique<NACP>(nacp_file);
// Get language code from settings // Get language code from settings
const auto language_code = const auto language_code = Service::Set::GetLanguageCodeFromIndex(
Service::Set::GetLanguageCodeFromIndex(Settings::values.language_index.GetValue()); static_cast<u32>(Settings::values.language_index.GetValue()));
// Convert to application language and get priority list // Convert to application language and get priority list
const auto application_language = const auto application_language =

@ -35,13 +35,27 @@ namespace {
using namespace Common::Literals; using namespace Common::Literals;
u32 GetMemorySizeForInit() { u32 GetMemorySizeForInit() {
return Settings::values.use_unsafe_extended_memory_layout ? Smc::MemorySize_8GB switch (Settings::values.memory_layout_mode.GetValue()) {
: Smc::MemorySize_4GB; case Settings::MemoryLayout::Memory_4Gb:
return Smc::MemorySize_4GB;
case Settings::MemoryLayout::Memory_6Gb:
return Smc::MemorySize_6GB;
case Settings::MemoryLayout::Memory_8Gb:
return Smc::MemorySize_8GB;
}
return Smc::MemorySize_4GB;
} }
Smc::MemoryArrangement GetMemoryArrangeForInit() { Smc::MemoryArrangement GetMemoryArrangeForInit() {
return Settings::values.use_unsafe_extended_memory_layout ? Smc::MemoryArrangement_8GB switch (Settings::values.memory_layout_mode.GetValue()) {
: Smc::MemoryArrangement_4GB; case Settings::MemoryLayout::Memory_4Gb:
return Smc::MemoryArrangement_4GB;
case Settings::MemoryLayout::Memory_6Gb:
return Smc::MemoryArrangement_6GB;
case Settings::MemoryLayout::Memory_8Gb:
return Smc::MemoryArrangement_8GB;
}
return Smc::MemoryArrangement_4GB;
} }
} // namespace } // namespace

@ -81,7 +81,8 @@ Result KProcess::Initialize(KProcess* process, Core::System& system, std::string
process->m_capabilities.InitializeForMetadatalessProcess(); process->m_capabilities.InitializeForMetadatalessProcess();
process->m_is_initialized = true; process->m_is_initialized = true;
std::mt19937 rng(Settings::values.rng_seed.GetValue().value_or(std::time(nullptr))); std::mt19937 rng(Settings::values.rng_seed_enabled ? Settings::values.rng_seed.GetValue()
: static_cast<u32>(std::time(nullptr)));
std::uniform_int_distribution<u64> distribution; std::uniform_int_distribution<u64> distribution;
std::generate(process->m_random_entropy.begin(), process->m_random_entropy.end(), std::generate(process->m_random_entropy.begin(), process->m_random_entropy.end(),
[&] { return distribution(rng); }); [&] { return distribution(rng); });

@ -409,7 +409,7 @@ ResultVal<u8> IApplicationManagerInterface::GetApplicationDesiredLanguage(
// Get language code from settings // Get language code from settings
const auto language_code = const auto language_code =
Set::GetLanguageCodeFromIndex(Settings::values.language_index.GetValue()); Set::GetLanguageCodeFromIndex(static_cast<s32>(Settings::values.language_index.GetValue()));
// Convert to application language, get priority list // Convert to application language, get priority list
const auto application_language = ConvertToApplicationLanguage(language_code); const auto application_language = ConvertToApplicationLanguage(language_code);

@ -93,7 +93,8 @@ void GetAvailableLanguageCodesImpl(HLERequestContext& ctx, std::size_t max_entri
} }
void GetKeyCodeMapImpl(HLERequestContext& ctx) { void GetKeyCodeMapImpl(HLERequestContext& ctx) {
const auto language_code = available_language_codes[Settings::values.language_index.GetValue()]; const auto language_code =
available_language_codes[static_cast<s32>(Settings::values.language_index.GetValue())];
const auto key_code = const auto key_code =
std::find_if(language_to_layout.cbegin(), language_to_layout.cend(), std::find_if(language_to_layout.cbegin(), language_to_layout.cend(),
[=](const auto& element) { return element.first == language_code; }); [=](const auto& element) { return element.first == language_code; });
@ -162,7 +163,7 @@ void SET::GetQuestFlag(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.GetValue())); rb.Push(static_cast<s32>(Settings::values.quest_flag.GetValue()));
} }
void SET::GetLanguageCode(HLERequestContext& ctx) { void SET::GetLanguageCode(HLERequestContext& ctx) {
@ -170,7 +171,8 @@ void SET::GetLanguageCode(HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 4}; IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.PushEnum(available_language_codes[Settings::values.language_index.GetValue()]); rb.PushEnum(
available_language_codes[static_cast<s32>(Settings::values.language_index.GetValue())]);
} }
void SET::GetRegionCode(HLERequestContext& ctx) { void SET::GetRegionCode(HLERequestContext& ctx) {
@ -178,7 +180,7 @@ void SET::GetRegionCode(HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 3}; IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.Push(Settings::values.region_index.GetValue()); rb.Push(static_cast<u32>(Settings::values.region_index.GetValue()));
} }
void SET::GetKeyCodeMap(HLERequestContext& ctx) { void SET::GetKeyCodeMap(HLERequestContext& ctx) {

@ -19,7 +19,8 @@ namespace Service::SPL {
Module::Interface::Interface(Core::System& system_, std::shared_ptr<Module> module_, Module::Interface::Interface(Core::System& system_, std::shared_ptr<Module> module_,
const char* name) const char* name)
: ServiceFramework{system_, name}, module{std::move(module_)}, : ServiceFramework{system_, name}, module{std::move(module_)},
rng(Settings::values.rng_seed.GetValue().value_or(std::time(nullptr))) {} rng(Settings::values.rng_seed_enabled ? Settings::values.rng_seed.GetValue()
: static_cast<u32>(std::time(nullptr))) {}
Module::Interface::~Interface() = default; Module::Interface::~Interface() = default;

@ -78,7 +78,8 @@ TimeZoneContentManager::TimeZoneContentManager(Core::System& system_)
location_name_cache{BuildLocationNameCache(time_zone_binary)} {} location_name_cache{BuildLocationNameCache(time_zone_binary)} {}
void TimeZoneContentManager::Initialize(TimeManager& time_manager) { void TimeZoneContentManager::Initialize(TimeManager& time_manager) {
const auto timezone_setting = Settings::GetTimeZoneString(); const auto timezone_setting =
Settings::GetTimeZoneString(Settings::values.time_zone_index.GetValue());
if (FileSys::VirtualFile vfs_file; if (FileSys::VirtualFile vfs_file;
GetTimeZoneInfoFile(timezone_setting, vfs_file) == ResultSuccess) { GetTimeZoneInfoFile(timezone_setting, vfs_file) == ResultSuccess) {

@ -61,13 +61,13 @@ static const char* TranslateRenderer(Settings::RendererBackend backend) {
return "Unknown"; return "Unknown";
} }
static const char* TranslateGPUAccuracyLevel(Settings::GPUAccuracy backend) { static const char* TranslateGPUAccuracyLevel(Settings::GpuAccuracy backend) {
switch (backend) { switch (backend) {
case Settings::GPUAccuracy::Normal: case Settings::GpuAccuracy::Normal:
return "Normal"; return "Normal";
case Settings::GPUAccuracy::High: case Settings::GpuAccuracy::High:
return "High"; return "High";
case Settings::GPUAccuracy::Extreme: case Settings::GpuAccuracy::Extreme:
return "Extreme"; return "Extreme";
} }
return "Unknown"; return "Unknown";
@ -77,9 +77,9 @@ static const char* TranslateNvdecEmulation(Settings::NvdecEmulation backend) {
switch (backend) { switch (backend) {
case Settings::NvdecEmulation::Off: case Settings::NvdecEmulation::Off:
return "Off"; return "Off";
case Settings::NvdecEmulation::CPU: case Settings::NvdecEmulation::Cpu:
return "CPU"; return "CPU";
case Settings::NvdecEmulation::GPU: case Settings::NvdecEmulation::Gpu:
return "GPU"; return "GPU";
} }
return "Unknown"; return "Unknown";
@ -91,14 +91,26 @@ static constexpr const char* TranslateVSyncMode(Settings::VSyncMode mode) {
return "Immediate"; return "Immediate";
case Settings::VSyncMode::Mailbox: case Settings::VSyncMode::Mailbox:
return "Mailbox"; return "Mailbox";
case Settings::VSyncMode::FIFO: case Settings::VSyncMode::Fifo:
return "FIFO"; return "FIFO";
case Settings::VSyncMode::FIFORelaxed: case Settings::VSyncMode::FifoRelaxed:
return "FIFO Relaxed"; return "FIFO Relaxed";
} }
return "Unknown"; return "Unknown";
} }
static constexpr const char* TranslateASTCDecodeMode(Settings::AstcDecodeMode mode) {
switch (mode) {
case Settings::AstcDecodeMode::Cpu:
return "CPU";
case Settings::AstcDecodeMode::Gpu:
return "GPU";
case Settings::AstcDecodeMode::CpuAsynchronous:
return "CPU Asynchronous";
}
return "Unknown";
}
u64 GetTelemetryId() { u64 GetTelemetryId() {
u64 telemetry_id{}; u64 telemetry_id{};
const auto filename = Common::FS::GetYuzuPath(Common::FS::YuzuPath::ConfigDir) / "telemetry_id"; const auto filename = Common::FS::GetYuzuPath(Common::FS::YuzuPath::ConfigDir) / "telemetry_id";
@ -240,7 +252,8 @@ 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.GetValue()); AddField(field_type, "Audio_SinkId",
Settings::CanonicalizeEnum(Settings::values.sink_id.GetValue()));
AddField(field_type, "Core_UseMultiCore", Settings::values.use_multi_core.GetValue()); AddField(field_type, "Core_UseMultiCore", Settings::values.use_multi_core.GetValue());
AddField(field_type, "Renderer_Backend", AddField(field_type, "Renderer_Backend",
TranslateRenderer(Settings::values.renderer_backend.GetValue())); TranslateRenderer(Settings::values.renderer_backend.GetValue()));
@ -254,7 +267,8 @@ void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader,
Settings::values.use_asynchronous_gpu_emulation.GetValue()); Settings::values.use_asynchronous_gpu_emulation.GetValue());
AddField(field_type, "Renderer_NvdecEmulation", AddField(field_type, "Renderer_NvdecEmulation",
TranslateNvdecEmulation(Settings::values.nvdec_emulation.GetValue())); TranslateNvdecEmulation(Settings::values.nvdec_emulation.GetValue()));
AddField(field_type, "Renderer_AccelerateASTC", Settings::values.accelerate_astc.GetValue()); AddField(field_type, "Renderer_AccelerateASTC",
TranslateASTCDecodeMode(Settings::values.accelerate_astc.GetValue()));
AddField(field_type, "Renderer_UseVsync", AddField(field_type, "Renderer_UseVsync",
TranslateVSyncMode(Settings::values.vsync_mode.GetValue())); TranslateVSyncMode(Settings::values.vsync_mode.GetValue()));
AddField(field_type, "Renderer_ShaderBackend", AddField(field_type, "Renderer_ShaderBackend",

@ -247,7 +247,7 @@ void Codec::Initialize() {
av_codec = avcodec_find_decoder(codec); av_codec = avcodec_find_decoder(codec);
InitializeAvCodecContext(); InitializeAvCodecContext();
if (Settings::values.nvdec_emulation.GetValue() == Settings::NvdecEmulation::GPU) { if (Settings::values.nvdec_emulation.GetValue() == Settings::NvdecEmulation::Gpu) {
InitializeGpuDecoder(); InitializeGpuDecoder();
} }
if (const int res = avcodec_open2(av_codec_ctx, av_codec, nullptr); res < 0) { if (const int res = avcodec_open2(av_codec_ctx, av_codec, nullptr); res < 0) {

@ -84,7 +84,7 @@ std::span<const u8> H264::ComposeFrame(const Host1x::NvdecCommon::NvdecRegisters
// TODO (ameerj): Where do we get this number, it seems to be particular for each stream // TODO (ameerj): Where do we get this number, it seems to be particular for each stream
const auto nvdec_decoding = Settings::values.nvdec_emulation.GetValue(); const auto nvdec_decoding = Settings::values.nvdec_emulation.GetValue();
const bool uses_gpu_decoding = nvdec_decoding == Settings::NvdecEmulation::GPU; const bool uses_gpu_decoding = nvdec_decoding == Settings::NvdecEmulation::Gpu;
const u32 max_num_ref_frames = uses_gpu_decoding ? 6u : 16u; const u32 max_num_ref_frames = uses_gpu_decoding ? 6u : 16u;
writer.WriteUe(max_num_ref_frames); writer.WriteUe(max_num_ref_frames);
writer.WriteBit(false); writer.WriteBit(false);

@ -34,13 +34,13 @@ ComputePipeline::ComputePipeline(const Device& device, TextureCache& texture_cac
: texture_cache{texture_cache_}, buffer_cache{buffer_cache_}, : texture_cache{texture_cache_}, buffer_cache{buffer_cache_},
program_manager{program_manager_}, info{info_} { program_manager{program_manager_}, info{info_} {
switch (device.GetShaderBackend()) { switch (device.GetShaderBackend()) {
case Settings::ShaderBackend::GLSL: case Settings::ShaderBackend::Glsl:
source_program = CreateProgram(code, GL_COMPUTE_SHADER); source_program = CreateProgram(code, GL_COMPUTE_SHADER);
break; break;
case Settings::ShaderBackend::GLASM: case Settings::ShaderBackend::Glasm:
assembly_program = CompileProgram(code, GL_COMPUTE_PROGRAM_NV); assembly_program = CompileProgram(code, GL_COMPUTE_PROGRAM_NV);
break; break;
case Settings::ShaderBackend::SPIRV: case Settings::ShaderBackend::SpirV:
source_program = CreateProgram(code_v, GL_COMPUTE_SHADER); source_program = CreateProgram(code_v, GL_COMPUTE_SHADER);
break; break;
} }

@ -177,15 +177,15 @@ Device::Device(Core::Frontend::EmuWindow& emu_window) {
has_fast_buffer_sub_data = is_nvidia && !disable_fast_buffer_sub_data; has_fast_buffer_sub_data = is_nvidia && !disable_fast_buffer_sub_data;
shader_backend = Settings::values.shader_backend.GetValue(); shader_backend = Settings::values.shader_backend.GetValue();
use_assembly_shaders = shader_backend == Settings::ShaderBackend::GLASM && use_assembly_shaders = shader_backend == Settings::ShaderBackend::Glasm &&
GLAD_GL_NV_gpu_program5 && GLAD_GL_NV_compute_program5 && GLAD_GL_NV_gpu_program5 && GLAD_GL_NV_compute_program5 &&
GLAD_GL_NV_transform_feedback && GLAD_GL_NV_transform_feedback2; GLAD_GL_NV_transform_feedback && GLAD_GL_NV_transform_feedback2;
if (shader_backend == Settings::ShaderBackend::GLASM && !use_assembly_shaders) { if (shader_backend == Settings::ShaderBackend::Glasm && !use_assembly_shaders) {
LOG_ERROR(Render_OpenGL, "Assembly shaders enabled but not supported"); LOG_ERROR(Render_OpenGL, "Assembly shaders enabled but not supported");
shader_backend = Settings::ShaderBackend::GLSL; shader_backend = Settings::ShaderBackend::Glsl;
} }
if (shader_backend == Settings::ShaderBackend::GLSL && is_nvidia) { if (shader_backend == Settings::ShaderBackend::Glsl && is_nvidia) {
const std::string_view driver_version = version.substr(13); const std::string_view driver_version = version.substr(13);
const int version_major = const int version_major =
std::atoi(driver_version.substr(0, driver_version.find(".")).data()); std::atoi(driver_version.substr(0, driver_version.find(".")).data());

@ -236,18 +236,18 @@ GraphicsPipeline::GraphicsPipeline(const Device& device, TextureCache& texture_c
force_context_flush](ShaderContext::Context*) mutable { force_context_flush](ShaderContext::Context*) mutable {
for (size_t stage = 0; stage < 5; ++stage) { for (size_t stage = 0; stage < 5; ++stage) {
switch (backend) { switch (backend) {
case Settings::ShaderBackend::GLSL: case Settings::ShaderBackend::Glsl:
if (!sources_[stage].empty()) { if (!sources_[stage].empty()) {
source_programs[stage] = CreateProgram(sources_[stage], Stage(stage)); source_programs[stage] = CreateProgram(sources_[stage], Stage(stage));
} }
break; break;
case Settings::ShaderBackend::GLASM: case Settings::ShaderBackend::Glasm:
if (!sources_[stage].empty()) { if (!sources_[stage].empty()) {
assembly_programs[stage] = assembly_programs[stage] =
CompileProgram(sources_[stage], AssemblyStage(stage)); CompileProgram(sources_[stage], AssemblyStage(stage));
} }
break; break;
case Settings::ShaderBackend::SPIRV: case Settings::ShaderBackend::SpirV:
if (!sources_spirv_[stage].empty()) { if (!sources_spirv_[stage].empty()) {
source_programs[stage] = CreateProgram(sources_spirv_[stage], Stage(stage)); source_programs[stage] = CreateProgram(sources_spirv_[stage], Stage(stage));
} }

@ -522,14 +522,14 @@ std::unique_ptr<GraphicsPipeline> ShaderCache::CreateGraphicsPipeline(
const auto runtime_info{ const auto runtime_info{
MakeRuntimeInfo(key, program, previous_program, glasm_use_storage_buffers, use_glasm)}; MakeRuntimeInfo(key, program, previous_program, glasm_use_storage_buffers, use_glasm)};
switch (device.GetShaderBackend()) { switch (device.GetShaderBackend()) {
case Settings::ShaderBackend::GLSL: case Settings::ShaderBackend::Glsl:
ConvertLegacyToGeneric(program, runtime_info); ConvertLegacyToGeneric(program, runtime_info);
sources[stage_index] = EmitGLSL(profile, runtime_info, program, binding); sources[stage_index] = EmitGLSL(profile, runtime_info, program, binding);
break; break;
case Settings::ShaderBackend::GLASM: case Settings::ShaderBackend::Glasm:
sources[stage_index] = EmitGLASM(profile, runtime_info, program, binding); sources[stage_index] = EmitGLASM(profile, runtime_info, program, binding);
break; break;
case Settings::ShaderBackend::SPIRV: case Settings::ShaderBackend::SpirV:
ConvertLegacyToGeneric(program, runtime_info); ConvertLegacyToGeneric(program, runtime_info);
sources_spirv[stage_index] = EmitSPIRV(profile, runtime_info, program, binding); sources_spirv[stage_index] = EmitSPIRV(profile, runtime_info, program, binding);
break; break;
@ -582,13 +582,13 @@ std::unique_ptr<ComputePipeline> ShaderCache::CreateComputePipeline(
std::string code{}; std::string code{};
std::vector<u32> code_spirv; std::vector<u32> code_spirv;
switch (device.GetShaderBackend()) { switch (device.GetShaderBackend()) {
case Settings::ShaderBackend::GLSL: case Settings::ShaderBackend::Glsl:
code = EmitGLSL(profile, program); code = EmitGLSL(profile, program);
break; break;
case Settings::ShaderBackend::GLASM: case Settings::ShaderBackend::Glasm:
code = EmitGLASM(profile, info, program); code = EmitGLASM(profile, info, program);
break; break;
case Settings::ShaderBackend::SPIRV: case Settings::ShaderBackend::SpirV:
code_spirv = EmitSPIRV(profile, program); code_spirv = EmitSPIRV(profile, program);
break; break;
} }

@ -232,10 +232,9 @@ void ApplySwizzle(GLuint handle, PixelFormat format, std::array<SwizzleSource, 4
[[nodiscard]] bool CanBeAccelerated(const TextureCacheRuntime& runtime, [[nodiscard]] bool CanBeAccelerated(const TextureCacheRuntime& runtime,
const VideoCommon::ImageInfo& info) { const VideoCommon::ImageInfo& info) {
if (IsPixelFormatASTC(info.format) && info.size.depth == 1 && !runtime.HasNativeASTC()) { if (IsPixelFormatASTC(info.format) && info.size.depth == 1 && !runtime.HasNativeASTC()) {
return Settings::values.accelerate_astc.GetValue() && return Settings::values.accelerate_astc.GetValue() == Settings::AstcDecodeMode::Gpu &&
Settings::values.astc_recompression.GetValue() == Settings::values.astc_recompression.GetValue() ==
Settings::AstcRecompression::Uncompressed && Settings::AstcRecompression::Uncompressed;
!Settings::values.async_astc.GetValue();
} }
// Disable other accelerated uploads for now as they don't implement swizzled uploads // Disable other accelerated uploads for now as they don't implement swizzled uploads
return false; return false;
@ -267,7 +266,8 @@ void ApplySwizzle(GLuint handle, PixelFormat format, std::array<SwizzleSource, 4
[[nodiscard]] bool CanBeDecodedAsync(const TextureCacheRuntime& runtime, [[nodiscard]] bool CanBeDecodedAsync(const TextureCacheRuntime& runtime,
const VideoCommon::ImageInfo& info) { const VideoCommon::ImageInfo& info) {
if (IsPixelFormatASTC(info.format) && !runtime.HasNativeASTC()) { if (IsPixelFormatASTC(info.format) && !runtime.HasNativeASTC()) {
return Settings::values.async_astc.GetValue(); return Settings::values.accelerate_astc.GetValue() ==
Settings::AstcDecodeMode::CpuAsynchronous;
} }
return false; return false;
} }

@ -473,7 +473,7 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
glBindTextureUnit(0, screen_info.display_texture); glBindTextureUnit(0, screen_info.display_texture);
auto anti_aliasing = Settings::values.anti_aliasing.GetValue(); auto anti_aliasing = Settings::values.anti_aliasing.GetValue();
if (anti_aliasing > Settings::AntiAliasing::LastAA) { if (anti_aliasing >= Settings::AntiAliasing::MaxEnum) {
LOG_ERROR(Render_OpenGL, "Invalid antialiasing option selected {}", anti_aliasing); LOG_ERROR(Render_OpenGL, "Invalid antialiasing option selected {}", anti_aliasing);
anti_aliasing = Settings::AntiAliasing::None; anti_aliasing = Settings::AntiAliasing::None;
Settings::values.anti_aliasing.SetValue(anti_aliasing); Settings::values.anti_aliasing.SetValue(anti_aliasing);

@ -45,8 +45,8 @@ static VkPresentModeKHR ChooseSwapPresentMode(bool has_imm, bool has_mailbox,
return mode; return mode;
} }
switch (mode) { switch (mode) {
case Settings::VSyncMode::FIFO: case Settings::VSyncMode::Fifo:
case Settings::VSyncMode::FIFORelaxed: case Settings::VSyncMode::FifoRelaxed:
if (has_mailbox) { if (has_mailbox) {
return Settings::VSyncMode::Mailbox; return Settings::VSyncMode::Mailbox;
} else if (has_imm) { } else if (has_imm) {
@ -59,8 +59,8 @@ static VkPresentModeKHR ChooseSwapPresentMode(bool has_imm, bool has_mailbox,
}(); }();
if ((setting == Settings::VSyncMode::Mailbox && !has_mailbox) || if ((setting == Settings::VSyncMode::Mailbox && !has_mailbox) ||
(setting == Settings::VSyncMode::Immediate && !has_imm) || (setting == Settings::VSyncMode::Immediate && !has_imm) ||
(setting == Settings::VSyncMode::FIFORelaxed && !has_fifo_relaxed)) { (setting == Settings::VSyncMode::FifoRelaxed && !has_fifo_relaxed)) {
setting = Settings::VSyncMode::FIFO; setting = Settings::VSyncMode::Fifo;
} }
switch (setting) { switch (setting) {
@ -68,9 +68,9 @@ static VkPresentModeKHR ChooseSwapPresentMode(bool has_imm, bool has_mailbox,
return VK_PRESENT_MODE_IMMEDIATE_KHR; return VK_PRESENT_MODE_IMMEDIATE_KHR;
case Settings::VSyncMode::Mailbox: case Settings::VSyncMode::Mailbox:
return VK_PRESENT_MODE_MAILBOX_KHR; return VK_PRESENT_MODE_MAILBOX_KHR;
case Settings::VSyncMode::FIFO: case Settings::VSyncMode::Fifo:
return VK_PRESENT_MODE_FIFO_KHR; return VK_PRESENT_MODE_FIFO_KHR;
case Settings::VSyncMode::FIFORelaxed: case Settings::VSyncMode::FifoRelaxed:
return VK_PRESENT_MODE_FIFO_RELAXED_KHR; return VK_PRESENT_MODE_FIFO_RELAXED_KHR;
default: default:
return VK_PRESENT_MODE_FIFO_KHR; return VK_PRESENT_MODE_FIFO_KHR;

@ -817,7 +817,7 @@ TextureCacheRuntime::TextureCacheRuntime(const Device& device_, Scheduler& sched
: device{device_}, scheduler{scheduler_}, memory_allocator{memory_allocator_}, : device{device_}, scheduler{scheduler_}, memory_allocator{memory_allocator_},
staging_buffer_pool{staging_buffer_pool_}, blit_image_helper{blit_image_helper_}, staging_buffer_pool{staging_buffer_pool_}, blit_image_helper{blit_image_helper_},
render_pass_cache{render_pass_cache_}, resolution{Settings::values.resolution_info} { render_pass_cache{render_pass_cache_}, resolution{Settings::values.resolution_info} {
if (Settings::values.accelerate_astc) { if (Settings::values.accelerate_astc.GetValue() == Settings::AstcDecodeMode::Gpu) {
astc_decoder_pass.emplace(device, scheduler, descriptor_pool, staging_buffer_pool, astc_decoder_pass.emplace(device, scheduler, descriptor_pool, staging_buffer_pool,
compute_pass_descriptor_queue, memory_allocator); compute_pass_descriptor_queue, memory_allocator);
} }
@ -1301,12 +1301,19 @@ Image::Image(TextureCacheRuntime& runtime_, const ImageInfo& info_, GPUVAddr gpu
runtime->ViewFormats(info.format))), runtime->ViewFormats(info.format))),
aspect_mask(ImageAspectMask(info.format)) { aspect_mask(ImageAspectMask(info.format)) {
if (IsPixelFormatASTC(info.format) && !runtime->device.IsOptimalAstcSupported()) { if (IsPixelFormatASTC(info.format) && !runtime->device.IsOptimalAstcSupported()) {
if (Settings::values.async_astc.GetValue()) { switch (Settings::values.accelerate_astc.GetValue()) {
case Settings::AstcDecodeMode::Gpu:
if (Settings::values.astc_recompression.GetValue() ==
Settings::AstcRecompression::Uncompressed &&
info.size.depth == 1) {
flags |= VideoCommon::ImageFlagBits::AcceleratedUpload;
}
break;
case Settings::AstcDecodeMode::CpuAsynchronous:
flags |= VideoCommon::ImageFlagBits::AsynchronousDecode; flags |= VideoCommon::ImageFlagBits::AsynchronousDecode;
} else if (Settings::values.astc_recompression.GetValue() == break;
Settings::AstcRecompression::Uncompressed && default:
Settings::values.accelerate_astc.GetValue() && info.size.depth == 1) { break;
flags |= VideoCommon::ImageFlagBits::AcceleratedUpload;
} }
flags |= VideoCommon::ImageFlagBits::Converted; flags |= VideoCommon::ImageFlagBits::Converted;
flags |= VideoCommon::ImageFlagBits::CostlyLoad; flags |= VideoCommon::ImageFlagBits::CostlyLoad;

@ -72,12 +72,12 @@ float TSCEntry::MaxAnisotropy() const noexcept {
} }
const auto anisotropic_settings = Settings::values.max_anisotropy.GetValue(); const auto anisotropic_settings = Settings::values.max_anisotropy.GetValue();
s32 added_anisotropic{}; s32 added_anisotropic{};
if (anisotropic_settings == 0) { if (anisotropic_settings == Settings::AnisotropyMode::Automatic) {
added_anisotropic = Settings::values.resolution_info.up_scale >> added_anisotropic = Settings::values.resolution_info.up_scale >>
Settings::values.resolution_info.down_shift; Settings::values.resolution_info.down_shift;
added_anisotropic = std::max(added_anisotropic - 1, 0); added_anisotropic = std::max(added_anisotropic - 1, 0);
} else { } else {
added_anisotropic = Settings::values.max_anisotropy.GetValue() - 1U; added_anisotropic = static_cast<u32>(Settings::values.max_anisotropy.GetValue()) - 1U;
} }
return static_cast<float>(1U << (max_anisotropy + added_anisotropic)); return static_cast<float>(1U << (max_anisotropy + added_anisotropic));
} }

@ -143,6 +143,10 @@ add_executable(yuzu
configuration/configure_web.ui configuration/configure_web.ui
configuration/input_profiles.cpp configuration/input_profiles.cpp
configuration/input_profiles.h configuration/input_profiles.h
configuration/shared_translation.cpp
configuration/shared_translation.h
configuration/shared_widget.cpp
configuration/shared_widget.h
debugger/console.cpp debugger/console.cpp
debugger/console.h debugger/console.h
debugger/controller.cpp debugger/controller.cpp
@ -231,6 +235,12 @@ if (WIN32 AND YUZU_CRASH_DUMPS)
target_compile_definitions(yuzu PRIVATE -DYUZU_DBGHELP) target_compile_definitions(yuzu PRIVATE -DYUZU_DBGHELP)
endif() endif()
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
target_compile_definitions(yuzu PRIVATE
$<$<VERSION_LESS:$<CXX_COMPILER_VERSION>,15>:CANNOT_EXPLICITLY_INSTANTIATE>
)
endif()
file(GLOB COMPAT_LIST file(GLOB COMPAT_LIST
${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.qrc ${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.qrc
${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.json) ${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.json)

File diff suppressed because it is too large Load Diff

@ -52,7 +52,7 @@ public:
static const std::map<Settings::AntiAliasing, QString> anti_aliasing_texts_map; static const std::map<Settings::AntiAliasing, QString> anti_aliasing_texts_map;
static const std::map<Settings::ScalingFilter, QString> scaling_filter_texts_map; static const std::map<Settings::ScalingFilter, QString> scaling_filter_texts_map;
static const std::map<bool, QString> use_docked_mode_texts_map; static const std::map<bool, QString> use_docked_mode_texts_map;
static const std::map<Settings::GPUAccuracy, QString> gpu_accuracy_texts_map; static const std::map<Settings::GpuAccuracy, QString> gpu_accuracy_texts_map;
static const std::map<Settings::RendererBackend, QString> renderer_backend_texts_map; static const std::map<Settings::RendererBackend, QString> renderer_backend_texts_map;
static const std::map<Settings::ShaderBackend, QString> shader_backend_texts_map; static const std::map<Settings::ShaderBackend, QString> shader_backend_texts_map;
@ -74,7 +74,6 @@ private:
void ReadKeyboardValues(); void ReadKeyboardValues();
void ReadMouseValues(); void ReadMouseValues();
void ReadTouchscreenValues(); void ReadTouchscreenValues();
void ReadMousePanningValues();
void ReadMotionTouchValues(); void ReadMotionTouchValues();
void ReadHidbusValues(); void ReadHidbusValues();
void ReadIrCameraValues(); void ReadIrCameraValues();
@ -99,13 +98,13 @@ private:
void ReadUILayoutValues(); void ReadUILayoutValues();
void ReadWebServiceValues(); void ReadWebServiceValues();
void ReadMultiplayerValues(); void ReadMultiplayerValues();
void ReadNetworkValues();
void SaveValues(); void SaveValues();
void SavePlayerValue(std::size_t player_index); void SavePlayerValue(std::size_t player_index);
void SaveDebugValues(); void SaveDebugValues();
void SaveMouseValues(); void SaveMouseValues();
void SaveTouchscreenValues(); void SaveTouchscreenValues();
void SaveMousePanningValues();
void SaveMotionTouchValues(); void SaveMotionTouchValues();
void SaveHidbusValues(); void SaveHidbusValues();
void SaveIrCameraValues(); void SaveIrCameraValues();
@ -140,18 +139,6 @@ private:
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;
/**
* Only reads a setting from the qt_config if the current config is a global config, or if the
* current config is a custom config and the setting is overriding the global setting. Otherwise
* it does nothing.
*
* @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>
void ReadSettingGlobal(Type& setting, const QString& name, const QVariant& default_value) const;
/** /**
* Writes a setting to the qt_config. * Writes a setting to the qt_config.
* *
@ -166,50 +153,20 @@ private:
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); bool use_global);
/** void ReadCategory(Settings::Category category);
* Reads a value from the qt_config and applies it to the setting, using its label and default void WriteCategory(Settings::Category category);
* value. If the config is a custom config, this will also read the global state of the setting void ReadSettingGeneric(Settings::BasicSetting* const setting);
* and apply that information to it. void WriteSettingGeneric(Settings::BasicSetting* const setting) const;
*
* @param The setting
*/
template <typename Type, bool ranged>
void ReadGlobalSetting(Settings::SwitchableSetting<Type, ranged>& setting);
/** const ConfigType type;
* 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, bool ranged>
void WriteGlobalSetting(const Settings::SwitchableSetting<Type, ranged>& setting);
/**
* 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, bool ranged>
void ReadBasicSetting(Settings::Setting<Type, ranged>& 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, bool ranged>
void WriteBasicSetting(const Settings::Setting<Type, ranged>& setting);
ConfigType type;
std::unique_ptr<QSettings> qt_config; std::unique_ptr<QSettings> qt_config;
std::string qt_config_loc; std::string qt_config_loc;
bool global; const bool global;
}; };
// These metatype declarations cannot be in common/settings.h because core is devoid of QT // These metatype declarations cannot be in common/settings.h because core is devoid of QT
Q_DECLARE_METATYPE(Settings::CPUAccuracy); Q_DECLARE_METATYPE(Settings::CpuAccuracy);
Q_DECLARE_METATYPE(Settings::GPUAccuracy); Q_DECLARE_METATYPE(Settings::GpuAccuracy);
Q_DECLARE_METATYPE(Settings::FullscreenMode); Q_DECLARE_METATYPE(Settings::FullscreenMode);
Q_DECLARE_METATYPE(Settings::NvdecEmulation); Q_DECLARE_METATYPE(Settings::NvdecEmulation);
Q_DECLARE_METATYPE(Settings::ResolutionSetup); Q_DECLARE_METATYPE(Settings::ResolutionSetup);
@ -218,3 +175,4 @@ Q_DECLARE_METATYPE(Settings::AntiAliasing);
Q_DECLARE_METATYPE(Settings::RendererBackend); Q_DECLARE_METATYPE(Settings::RendererBackend);
Q_DECLARE_METATYPE(Settings::ShaderBackend); Q_DECLARE_METATYPE(Settings::ShaderBackend);
Q_DECLARE_METATYPE(Settings::AstcRecompression); Q_DECLARE_METATYPE(Settings::AstcRecompression);
Q_DECLARE_METATYPE(Settings::AstcDecodeMode);

@ -1,104 +1,19 @@
// SPDX-FileCopyrightText: 2016 Citra Emulator Project // SPDX-FileCopyrightText: 2016 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <QCheckBox> #include <memory>
#include <QObject> #include <type_traits>
#include <QString> #include <vector>
#include "common/settings.h"
#include "yuzu/configuration/configuration_shared.h" #include "yuzu/configuration/configuration_shared.h"
#include "yuzu/configuration/configure_per_game.h"
void ConfigurationShared::ApplyPerGameSetting(Settings::SwitchableSetting<bool>* setting, namespace ConfigurationShared {
const QCheckBox* checkbox,
const CheckState& tracker) { Tab::Tab(std::shared_ptr<std::vector<Tab*>> group, QWidget* parent) : QWidget(parent) {
if (Settings::IsConfiguringGlobal() && setting->UsingGlobal()) { if (group != nullptr) {
setting->SetValue(checkbox->checkState()); group->push_back(this);
} else if (!Settings::IsConfiguringGlobal()) {
if (tracker == CheckState::Global) {
setting->SetGlobal(true);
} else {
setting->SetGlobal(false);
setting->SetValue(checkbox->checkState());
}
} }
} }
void ConfigurationShared::SetPerGameSetting(QCheckBox* checkbox, Tab::~Tab() = default;
const Settings::SwitchableSetting<bool>* setting) {
if (setting->UsingGlobal()) {
checkbox->setCheckState(Qt::PartiallyChecked);
} else {
checkbox->setCheckState(setting->GetValue() ? Qt::Checked : Qt::Unchecked);
}
}
void ConfigurationShared::SetHighlight(QWidget* widget, bool highlighted) { } // namespace ConfigurationShared
if (highlighted) {
widget->setStyleSheet(QStringLiteral("QWidget#%1 { background-color:rgba(0,203,255,0.5) }")
.arg(widget->objectName()));
} else {
widget->setStyleSheet(QStringLiteral("QWidget#%1 { background-color:rgba(0,0,0,0) }")
.arg(widget->objectName()));
}
widget->show();
}
void ConfigurationShared::SetColoredTristate(QCheckBox* checkbox,
const Settings::SwitchableSetting<bool>& setting,
CheckState& tracker) {
if (setting.UsingGlobal()) {
tracker = CheckState::Global;
} else {
tracker = (setting.GetValue() == setting.GetValue(true)) ? CheckState::On : CheckState::Off;
}
SetHighlight(checkbox, tracker != CheckState::Global);
QObject::connect(checkbox, &QCheckBox::clicked, checkbox, [checkbox, setting, &tracker] {
tracker = static_cast<CheckState>((static_cast<int>(tracker) + 1) %
static_cast<int>(CheckState::Count));
if (tracker == CheckState::Global) {
checkbox->setChecked(setting.GetValue(true));
}
SetHighlight(checkbox, tracker != CheckState::Global);
});
}
void ConfigurationShared::SetColoredTristate(QCheckBox* checkbox, bool global, bool state,
bool global_state, CheckState& tracker) {
if (global) {
tracker = CheckState::Global;
} else {
tracker = (state == global_state) ? CheckState::On : CheckState::Off;
}
SetHighlight(checkbox, tracker != CheckState::Global);
QObject::connect(checkbox, &QCheckBox::clicked, checkbox, [checkbox, global_state, &tracker] {
tracker = static_cast<CheckState>((static_cast<int>(tracker) + 1) %
static_cast<int>(CheckState::Count));
if (tracker == CheckState::Global) {
checkbox->setChecked(global_state);
}
SetHighlight(checkbox, tracker != CheckState::Global);
});
}
void ConfigurationShared::SetColoredComboBox(QComboBox* combobox, QWidget* target, int global) {
InsertGlobalItem(combobox, global);
QObject::connect(combobox, qOverload<int>(&QComboBox::activated), target,
[target](int index) { SetHighlight(target, index != 0); });
}
void ConfigurationShared::InsertGlobalItem(QComboBox* combobox, int global_index) {
const QString use_global_text =
ConfigurePerGame::tr("Use global configuration (%1)").arg(combobox->itemText(global_index));
combobox->insertItem(ConfigurationShared::USE_GLOBAL_INDEX, use_global_text);
combobox->insertSeparator(ConfigurationShared::USE_GLOBAL_SEPARATOR_INDEX);
}
int ConfigurationShared::GetComboboxIndex(int global_setting_index, const QComboBox* combobox) {
if (Settings::IsConfiguringGlobal()) {
return combobox->currentIndex();
}
if (combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
return global_setting_index;
}
return combobox->currentIndex() - ConfigurationShared::USE_GLOBAL_OFFSET;
}

@ -3,73 +3,25 @@
#pragma once #pragma once
#include <QCheckBox> #include <memory>
#include <QComboBox> #include <vector>
#include "common/settings.h" #include <QString>
#include <QWidget>
#include <qobjectdefs.h>
class QObject;
namespace ConfigurationShared { namespace ConfigurationShared {
constexpr int USE_GLOBAL_INDEX = 0; class Tab : public QWidget {
constexpr int USE_GLOBAL_SEPARATOR_INDEX = 1; Q_OBJECT
constexpr int USE_GLOBAL_OFFSET = 2;
// CheckBoxes require a tracker for their state since we emulate a tristate CheckBox public:
enum class CheckState { explicit Tab(std::shared_ptr<std::vector<Tab*>> group, QWidget* parent = nullptr);
Off, // Checkbox overrides to off/false ~Tab();
On, // Checkbox overrides to on/true
Global, // Checkbox defers to the global state virtual void ApplyConfiguration() = 0;
Count, // Simply the number of states, not a valid checkbox state virtual void SetConfiguration() = 0;
}; };
// Global-aware apply and set functions
// ApplyPerGameSetting, given a Settings::Setting and a Qt UI element, properly applies a Setting
void ApplyPerGameSetting(Settings::SwitchableSetting<bool>* setting, const QCheckBox* checkbox,
const CheckState& tracker);
template <typename Type, bool ranged>
void ApplyPerGameSetting(Settings::SwitchableSetting<Type, ranged>* setting,
const QComboBox* combobox) {
if (Settings::IsConfiguringGlobal() && setting->UsingGlobal()) {
setting->SetValue(static_cast<Type>(combobox->currentIndex()));
} else if (!Settings::IsConfiguringGlobal()) {
if (combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
setting->SetGlobal(true);
} else {
setting->SetGlobal(false);
setting->SetValue(static_cast<Type>(combobox->currentIndex() -
ConfigurationShared::USE_GLOBAL_OFFSET));
}
}
}
// Sets a Qt UI element given a Settings::Setting
void SetPerGameSetting(QCheckBox* checkbox, const Settings::SwitchableSetting<bool>* setting);
template <typename Type, bool ranged>
void SetPerGameSetting(QComboBox* combobox,
const Settings::SwitchableSetting<Type, ranged>* setting) {
combobox->setCurrentIndex(setting->UsingGlobal() ? ConfigurationShared::USE_GLOBAL_INDEX
: static_cast<int>(setting->GetValue()) +
ConfigurationShared::USE_GLOBAL_OFFSET);
}
// (Un)highlights a Qt UI element
void SetHighlight(QWidget* widget, bool highlighted);
// Sets up a QCheckBox like a tristate one, given a Setting
void SetColoredTristate(QCheckBox* checkbox, const Settings::SwitchableSetting<bool>& setting,
CheckState& tracker);
void SetColoredTristate(QCheckBox* checkbox, bool global, bool state, bool global_state,
CheckState& tracker);
// Sets up coloring of a QWidget `target` based on the state of a QComboBox, and calls
// InsertGlobalItem
void SetColoredComboBox(QComboBox* combobox, QWidget* target, int global);
// Adds the "Use Global Configuration" selection and separator to the beginning of a QComboBox
void InsertGlobalItem(QComboBox* combobox, int global_index);
// Returns the correct index of a QComboBox taking into account global configuration
int GetComboboxIndex(int global_setting_index, const QComboBox* combobox);
} // namespace ConfigurationShared } // namespace ConfigurationShared

@ -48,11 +48,34 @@
</layout> </layout>
</item> </item>
<item> <item>
<widget class="QDialogButtonBox" name="buttonBox"> <layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="standardButtons"> <property name="leftMargin">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> <number>0</number>
</property> </property>
</widget> <property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Some settings are only available when a game is not running.</string>
</property>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</item> </item>
</layout> </layout>
</widget> </widget>

@ -1,87 +1,112 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <map>
#include <memory> #include <memory>
#include <vector>
#include <QComboBox>
#include "audio_core/sink/sink.h" #include "audio_core/sink/sink.h"
#include "audio_core/sink/sink_details.h" #include "audio_core/sink/sink_details.h"
#include "common/common_types.h"
#include "common/settings.h" #include "common/settings.h"
#include "common/settings_common.h"
#include "core/core.h" #include "core/core.h"
#include "ui_configure_audio.h" #include "ui_configure_audio.h"
#include "yuzu/configuration/configuration_shared.h" #include "yuzu/configuration/configuration_shared.h"
#include "yuzu/configuration/configure_audio.h" #include "yuzu/configuration/configure_audio.h"
#include "yuzu/configuration/shared_translation.h"
#include "yuzu/configuration/shared_widget.h"
#include "yuzu/uisettings.h" #include "yuzu/uisettings.h"
ConfigureAudio::ConfigureAudio(const Core::System& system_, QWidget* parent) ConfigureAudio::ConfigureAudio(const Core::System& system_,
: QWidget(parent), ui(std::make_unique<Ui::ConfigureAudio>()), system{system_} { std::shared_ptr<std::vector<ConfigurationShared::Tab*>> group_,
const ConfigurationShared::Builder& builder, QWidget* parent)
: Tab(group_, parent), ui(std::make_unique<Ui::ConfigureAudio>()), system{system_} {
ui->setupUi(this); ui->setupUi(this);
Setup(builder);
InitializeAudioSinkComboBox();
connect(ui->volume_slider, &QSlider::valueChanged, this,
&ConfigureAudio::SetVolumeIndicatorText);
connect(ui->sink_combo_box, qOverload<int>(&QComboBox::currentIndexChanged), this,
&ConfigureAudio::UpdateAudioDevices);
ui->volume_label->setVisible(Settings::IsConfiguringGlobal());
ui->volume_combo_box->setVisible(!Settings::IsConfiguringGlobal());
SetupPerGameUI();
SetConfiguration(); SetConfiguration();
const bool is_powered_on = system_.IsPoweredOn();
ui->sink_combo_box->setEnabled(!is_powered_on);
ui->output_combo_box->setEnabled(!is_powered_on);
ui->input_combo_box->setEnabled(!is_powered_on);
} }
ConfigureAudio::~ConfigureAudio() = default; ConfigureAudio::~ConfigureAudio() = default;
void ConfigureAudio::Setup(const ConfigurationShared::Builder& builder) {
auto& layout = *ui->audio_widget->layout();
std::vector<Settings::BasicSetting*> settings;
std::map<u32, QWidget*> hold;
auto push = [&](Settings::Category category) {
for (auto* setting : Settings::values.linkage.by_category[category]) {
settings.push_back(setting);
}
};
push(Settings::Category::Audio);
push(Settings::Category::SystemAudio);
for (auto* setting : settings) {
auto* widget = builder.BuildWidget(setting, apply_funcs);
if (widget == nullptr) {
continue;
}
if (!widget->Valid()) {
widget->deleteLater();
continue;
}
hold.emplace(std::pair{setting->Id(), widget});
if (setting->Id() == Settings::values.sink_id.Id()) {
// TODO (lat9nq): Let the system manage sink_id
sink_combo_box = widget->combobox;
InitializeAudioSinkComboBox();
connect(sink_combo_box, qOverload<int>(&QComboBox::currentIndexChanged), this,
&ConfigureAudio::UpdateAudioDevices);
} else if (setting->Id() == Settings::values.audio_output_device_id.Id()) {
// Keep track of output (and input) device comboboxes to populate them with system
// devices, which are determined at run time
output_device_combo_box = widget->combobox;
} else if (setting->Id() == Settings::values.audio_input_device_id.Id()) {
input_device_combo_box = widget->combobox;
}
}
for (const auto& [id, widget] : hold) {
layout.addWidget(widget);
}
}
void ConfigureAudio::SetConfiguration() { void ConfigureAudio::SetConfiguration() {
if (!Settings::IsConfiguringGlobal()) {
return;
}
SetOutputSinkFromSinkID(); SetOutputSinkFromSinkID();
// The device list cannot be pre-populated (nor listed) until the output sink is known. // The device list cannot be pre-populated (nor listed) until the output sink is known.
UpdateAudioDevices(ui->sink_combo_box->currentIndex()); UpdateAudioDevices(sink_combo_box->currentIndex());
SetAudioDevicesFromDeviceID(); SetAudioDevicesFromDeviceID();
const auto volume_value = static_cast<int>(Settings::values.volume.GetValue());
ui->volume_slider->setValue(volume_value);
ui->toggle_background_mute->setChecked(UISettings::values.mute_when_in_background.GetValue());
if (!Settings::IsConfiguringGlobal()) {
if (Settings::values.volume.UsingGlobal()) {
ui->volume_combo_box->setCurrentIndex(0);
ui->volume_slider->setEnabled(false);
} else {
ui->volume_combo_box->setCurrentIndex(1);
ui->volume_slider->setEnabled(true);
}
ConfigurationShared::SetPerGameSetting(ui->combo_sound, &Settings::values.sound_index);
ConfigurationShared::SetHighlight(ui->mode_label,
!Settings::values.sound_index.UsingGlobal());
ConfigurationShared::SetHighlight(ui->volume_layout,
!Settings::values.volume.UsingGlobal());
} else {
ui->combo_sound->setCurrentIndex(Settings::values.sound_index.GetValue());
}
SetVolumeIndicatorText(ui->volume_slider->sliderPosition());
} }
void ConfigureAudio::SetOutputSinkFromSinkID() { void ConfigureAudio::SetOutputSinkFromSinkID() {
[[maybe_unused]] const QSignalBlocker blocker(ui->sink_combo_box); [[maybe_unused]] const QSignalBlocker blocker(sink_combo_box);
int new_sink_index = 0; int new_sink_index = 0;
const QString sink_id = QString::fromStdString(Settings::values.sink_id.GetValue()); const QString sink_id = QString::fromStdString(Settings::values.sink_id.ToString());
for (int index = 0; index < ui->sink_combo_box->count(); index++) { for (int index = 0; index < sink_combo_box->count(); index++) {
if (ui->sink_combo_box->itemText(index) == sink_id) { if (sink_combo_box->itemText(index) == sink_id) {
new_sink_index = index; new_sink_index = index;
break; break;
} }
} }
ui->sink_combo_box->setCurrentIndex(new_sink_index); sink_combo_box->setCurrentIndex(new_sink_index);
} }
void ConfigureAudio::SetAudioDevicesFromDeviceID() { void ConfigureAudio::SetAudioDevicesFromDeviceID() {
@ -89,57 +114,42 @@ void ConfigureAudio::SetAudioDevicesFromDeviceID() {
const QString output_device_id = const QString output_device_id =
QString::fromStdString(Settings::values.audio_output_device_id.GetValue()); QString::fromStdString(Settings::values.audio_output_device_id.GetValue());
for (int index = 0; index < ui->output_combo_box->count(); index++) { for (int index = 0; index < output_device_combo_box->count(); index++) {
if (ui->output_combo_box->itemText(index) == output_device_id) { if (output_device_combo_box->itemText(index) == output_device_id) {
new_device_index = index; new_device_index = index;
break; break;
} }
} }
ui->output_combo_box->setCurrentIndex(new_device_index); output_device_combo_box->setCurrentIndex(new_device_index);
new_device_index = -1; new_device_index = -1;
const QString input_device_id = const QString input_device_id =
QString::fromStdString(Settings::values.audio_input_device_id.GetValue()); QString::fromStdString(Settings::values.audio_input_device_id.GetValue());
for (int index = 0; index < ui->input_combo_box->count(); index++) { for (int index = 0; index < input_device_combo_box->count(); index++) {
if (ui->input_combo_box->itemText(index) == input_device_id) { if (input_device_combo_box->itemText(index) == input_device_id) {
new_device_index = index; new_device_index = index;
break; break;
} }
} }
ui->input_combo_box->setCurrentIndex(new_device_index); input_device_combo_box->setCurrentIndex(new_device_index);
}
void ConfigureAudio::SetVolumeIndicatorText(int percentage) {
ui->volume_indicator->setText(tr("%1%", "Volume percentage (e.g. 50%)").arg(percentage));
} }
void ConfigureAudio::ApplyConfiguration() { void ConfigureAudio::ApplyConfiguration() {
ConfigurationShared::ApplyPerGameSetting(&Settings::values.sound_index, ui->combo_sound); const bool is_powered_on = system.IsPoweredOn();
for (const auto& apply_func : apply_funcs) {
apply_func(is_powered_on);
}
if (Settings::IsConfiguringGlobal()) { if (Settings::IsConfiguringGlobal()) {
Settings::values.sink_id = Settings::values.sink_id.LoadString(
ui->sink_combo_box->itemText(ui->sink_combo_box->currentIndex()).toStdString(); sink_combo_box->itemText(sink_combo_box->currentIndex()).toStdString());
Settings::values.audio_output_device_id.SetValue( Settings::values.audio_output_device_id.SetValue(
ui->output_combo_box->itemText(ui->output_combo_box->currentIndex()).toStdString()); output_device_combo_box->itemText(output_device_combo_box->currentIndex())
.toStdString());
Settings::values.audio_input_device_id.SetValue( Settings::values.audio_input_device_id.SetValue(
ui->input_combo_box->itemText(ui->input_combo_box->currentIndex()).toStdString()); input_device_combo_box->itemText(input_device_combo_box->currentIndex()).toStdString());
UISettings::values.mute_when_in_background = ui->toggle_background_mute->isChecked();
// Guard if during game and set to game-specific value
if (Settings::values.volume.UsingGlobal()) {
const auto volume = static_cast<u8>(ui->volume_slider->value());
Settings::values.volume.SetValue(volume);
}
} else {
if (ui->volume_combo_box->currentIndex() == 0) {
Settings::values.volume.SetGlobal(true);
} else {
Settings::values.volume.SetGlobal(false);
const auto volume = static_cast<u8>(ui->volume_slider->value());
Settings::values.volume.SetValue(volume);
}
} }
} }
@ -152,54 +162,31 @@ void ConfigureAudio::changeEvent(QEvent* event) {
} }
void ConfigureAudio::UpdateAudioDevices(int sink_index) { void ConfigureAudio::UpdateAudioDevices(int sink_index) {
ui->output_combo_box->clear(); output_device_combo_box->clear();
ui->output_combo_box->addItem(QString::fromUtf8(AudioCore::Sink::auto_device_name)); output_device_combo_box->addItem(QString::fromUtf8(AudioCore::Sink::auto_device_name));
const std::string sink_id = ui->sink_combo_box->itemText(sink_index).toStdString(); const auto sink_id =
Settings::ToEnum<Settings::AudioEngine>(sink_combo_box->itemText(sink_index).toStdString());
for (const auto& device : AudioCore::Sink::GetDeviceListForSink(sink_id, false)) { for (const auto& device : AudioCore::Sink::GetDeviceListForSink(sink_id, false)) {
ui->output_combo_box->addItem(QString::fromStdString(device)); output_device_combo_box->addItem(QString::fromStdString(device));
} }
ui->input_combo_box->clear(); input_device_combo_box->clear();
ui->input_combo_box->addItem(QString::fromUtf8(AudioCore::Sink::auto_device_name)); input_device_combo_box->addItem(QString::fromUtf8(AudioCore::Sink::auto_device_name));
for (const auto& device : AudioCore::Sink::GetDeviceListForSink(sink_id, true)) { for (const auto& device : AudioCore::Sink::GetDeviceListForSink(sink_id, true)) {
ui->input_combo_box->addItem(QString::fromStdString(device)); input_device_combo_box->addItem(QString::fromStdString(device));
} }
} }
void ConfigureAudio::InitializeAudioSinkComboBox() { void ConfigureAudio::InitializeAudioSinkComboBox() {
ui->sink_combo_box->clear(); sink_combo_box->clear();
ui->sink_combo_box->addItem(QString::fromUtf8(AudioCore::Sink::auto_device_name)); sink_combo_box->addItem(QString::fromUtf8(AudioCore::Sink::auto_device_name));
for (const auto& id : AudioCore::Sink::GetSinkIDs()) { for (const auto& id : AudioCore::Sink::GetSinkIDs()) {
ui->sink_combo_box->addItem(QString::fromUtf8(id.data(), static_cast<s32>(id.length()))); sink_combo_box->addItem(QString::fromStdString(Settings::CanonicalizeEnum(id)));
} }
} }
void ConfigureAudio::RetranslateUI() { void ConfigureAudio::RetranslateUI() {
ui->retranslateUi(this); ui->retranslateUi(this);
SetVolumeIndicatorText(ui->volume_slider->sliderPosition());
}
void ConfigureAudio::SetupPerGameUI() {
if (Settings::IsConfiguringGlobal()) {
ui->combo_sound->setEnabled(Settings::values.sound_index.UsingGlobal());
ui->volume_slider->setEnabled(Settings::values.volume.UsingGlobal());
return;
}
ConfigurationShared::SetColoredComboBox(ui->combo_sound, ui->mode_label,
Settings::values.sound_index.GetValue(true));
connect(ui->volume_combo_box, qOverload<int>(&QComboBox::activated), this, [this](int index) {
ui->volume_slider->setEnabled(index == 1);
ConfigurationShared::SetHighlight(ui->volume_layout, index == 1);
});
ui->sink_combo_box->setVisible(false);
ui->sink_label->setVisible(false);
ui->output_combo_box->setVisible(false);
ui->output_label->setVisible(false);
ui->input_combo_box->setVisible(false);
ui->input_label->setVisible(false);
} }

@ -3,30 +3,35 @@
#pragma once #pragma once
#include <functional>
#include <memory> #include <memory>
#include <vector>
#include <QWidget> #include <QWidget>
#include "yuzu/configuration/configuration_shared.h"
class QComboBox;
namespace Core { namespace Core {
class System; class System;
} }
namespace ConfigurationShared {
enum class CheckState;
}
namespace Ui { namespace Ui {
class ConfigureAudio; class ConfigureAudio;
} }
class ConfigureAudio : public QWidget { namespace ConfigurationShared {
Q_OBJECT class Builder;
}
class ConfigureAudio : public ConfigurationShared::Tab {
public: public:
explicit ConfigureAudio(const Core::System& system_, QWidget* parent = nullptr); explicit ConfigureAudio(const Core::System& system_,
std::shared_ptr<std::vector<ConfigurationShared::Tab*>> group,
const ConfigurationShared::Builder& builder, QWidget* parent = nullptr);
~ConfigureAudio() override; ~ConfigureAudio() override;
void ApplyConfiguration(); void ApplyConfiguration() override;
void SetConfiguration(); void SetConfiguration() override;
private: private:
void changeEvent(QEvent* event) override; void changeEvent(QEvent* event) override;
@ -39,11 +44,16 @@ private:
void SetOutputSinkFromSinkID(); void SetOutputSinkFromSinkID();
void SetAudioDevicesFromDeviceID(); void SetAudioDevicesFromDeviceID();
void SetVolumeIndicatorText(int percentage);
void SetupPerGameUI(); void Setup(const ConfigurationShared::Builder& builder);
std::unique_ptr<Ui::ConfigureAudio> ui; std::unique_ptr<Ui::ConfigureAudio> ui;
const Core::System& system; const Core::System& system;
std::vector<std::function<void(bool)>> apply_funcs{};
QComboBox* sink_combo_box;
QComboBox* output_device_combo_box;
QComboBox* input_device_combo_box;
}; };

@ -21,80 +21,14 @@
</property> </property>
<layout class="QVBoxLayout"> <layout class="QVBoxLayout">
<item> <item>
<layout class="QHBoxLayout" name="engine_layout"> <widget class="QWidget" name="audio_widget" native="true">
<item> <property name="maximumSize">
<widget class="QLabel" name="sink_label"> <size>
<property name="text"> <width>16777215</width>
<string>Output Engine:</string> <height>16777213</height>
</property> </size>
</widget> </property>
</item> <layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QComboBox" name="sink_combo_box"/>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="output_layout">
<item>
<widget class="QLabel" name="output_label">
<property name="text">
<string>Output Device:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="output_combo_box"/>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="input_layout">
<item>
<widget class="QLabel" name="input_label">
<property name="text">
<string>Input Device:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="input_combo_box"/>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="mode_layout">
<item>
<widget class="QLabel" name="mode_label">
<property name="text">
<string>Sound Output Mode:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="combo_sound">
<item>
<property name="text">
<string>Mono</string>
</property>
</item>
<item>
<property name="text">
<string>Stereo</string>
</property>
</item>
<item>
<property name="text">
<string>Surround</string>
</property>
</item>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QWidget" name="volume_layout" native="true">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="leftMargin"> <property name="leftMargin">
<number>0</number> <number>0</number>
</property> </property>
@ -107,89 +41,9 @@
<property name="bottomMargin"> <property name="bottomMargin">
<number>0</number> <number>0</number>
</property> </property>
<item>
<widget class="QComboBox" name="volume_combo_box">
<item>
<property name="text">
<string>Use global volume</string>
</property>
</item>
<item>
<property name="text">
<string>Set volume:</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="QLabel" name="volume_label">
<property name="text">
<string>Volume:</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>30</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QSlider" name="volume_slider">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximum">
<number>200</number>
</property>
<property name="pageStep">
<number>5</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="volume_indicator">
<property name="minimumSize">
<size>
<width>32</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>0 %</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
</item> </item>
<item>
<layout class="QHBoxLayout" name="mute_layout">
<item>
<widget class="QCheckBox" name="toggle_background_mute">
<property name="text">
<string>Mute audio when in background</string>
</property>
</widget>
</item>
</layout>
</item>
</layout> </layout>
</widget> </widget>
</item> </item>

@ -1,88 +1,92 @@
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <memory>
#include <typeinfo>
#include <vector>
#include <QComboBox>
#include "common/common_types.h" #include "common/common_types.h"
#include "common/settings.h" #include "common/settings.h"
#include "common/settings_enums.h"
#include "configuration/shared_widget.h"
#include "core/core.h" #include "core/core.h"
#include "ui_configure_cpu.h" #include "ui_configure_cpu.h"
#include "yuzu/configuration/configuration_shared.h" #include "yuzu/configuration/configuration_shared.h"
#include "yuzu/configuration/configure_cpu.h" #include "yuzu/configuration/configure_cpu.h"
ConfigureCpu::ConfigureCpu(const Core::System& system_, QWidget* parent) ConfigureCpu::ConfigureCpu(const Core::System& system_,
: QWidget(parent), ui{std::make_unique<Ui::ConfigureCpu>()}, system{system_} { std::shared_ptr<std::vector<ConfigurationShared::Tab*>> group_,
const ConfigurationShared::Builder& builder, QWidget* parent)
: Tab(group_, parent), ui{std::make_unique<Ui::ConfigureCpu>()}, system{system_},
combobox_translations(builder.ComboboxTranslations()) {
ui->setupUi(this); ui->setupUi(this);
SetupPerGameUI(); Setup(builder);
SetConfiguration(); SetConfiguration();
connect(ui->accuracy, qOverload<int>(&QComboBox::currentIndexChanged), this, connect(accuracy_combobox, qOverload<int>(&QComboBox::currentIndexChanged), this,
&ConfigureCpu::UpdateGroup); &ConfigureCpu::UpdateGroup);
} }
ConfigureCpu::~ConfigureCpu() = default; ConfigureCpu::~ConfigureCpu() = default;
void ConfigureCpu::SetConfiguration() { void ConfigureCpu::SetConfiguration() {}
const bool runtime_lock = !system.IsPoweredOn(); void ConfigureCpu::Setup(const ConfigurationShared::Builder& builder) {
auto* accuracy_layout = ui->widget_accuracy->layout();
auto* unsafe_layout = ui->unsafe_widget->layout();
std::map<u32, QWidget*> unsafe_hold{};
ui->accuracy->setEnabled(runtime_lock); std::vector<Settings::BasicSetting*> settings;
ui->cpuopt_unsafe_unfuse_fma->setEnabled(runtime_lock); const auto push = [&](Settings::Category category) {
ui->cpuopt_unsafe_reduce_fp_error->setEnabled(runtime_lock); for (const auto setting : Settings::values.linkage.by_category[category]) {
ui->cpuopt_unsafe_ignore_standard_fpcr->setEnabled(runtime_lock); settings.push_back(setting);
ui->cpuopt_unsafe_inaccurate_nan->setEnabled(runtime_lock); }
ui->cpuopt_unsafe_fastmem_check->setEnabled(runtime_lock); };
ui->cpuopt_unsafe_ignore_global_monitor->setEnabled(runtime_lock);
ui->cpuopt_unsafe_unfuse_fma->setChecked(Settings::values.cpuopt_unsafe_unfuse_fma.GetValue()); push(Settings::Category::Cpu);
ui->cpuopt_unsafe_reduce_fp_error->setChecked( push(Settings::Category::CpuUnsafe);
Settings::values.cpuopt_unsafe_reduce_fp_error.GetValue());
ui->cpuopt_unsafe_ignore_standard_fpcr->setChecked(
Settings::values.cpuopt_unsafe_ignore_standard_fpcr.GetValue());
ui->cpuopt_unsafe_inaccurate_nan->setChecked(
Settings::values.cpuopt_unsafe_inaccurate_nan.GetValue());
ui->cpuopt_unsafe_fastmem_check->setChecked(
Settings::values.cpuopt_unsafe_fastmem_check.GetValue());
ui->cpuopt_unsafe_ignore_global_monitor->setChecked(
Settings::values.cpuopt_unsafe_ignore_global_monitor.GetValue());
if (Settings::IsConfiguringGlobal()) { for (const auto setting : settings) {
ui->accuracy->setCurrentIndex(static_cast<int>(Settings::values.cpu_accuracy.GetValue())); auto* widget = builder.BuildWidget(setting, apply_funcs);
} else {
ConfigurationShared::SetPerGameSetting(ui->accuracy, &Settings::values.cpu_accuracy); if (widget == nullptr) {
ConfigurationShared::SetHighlight(ui->widget_accuracy, continue;
!Settings::values.cpu_accuracy.UsingGlobal()); }
if (!widget->Valid()) {
widget->deleteLater();
continue;
}
if (setting->Id() == Settings::values.cpu_accuracy.Id()) {
// Keep track of cpu_accuracy combobox to display/hide the unsafe settings
accuracy_layout->addWidget(widget);
accuracy_combobox = widget->combobox;
} else {
// Presently, all other settings here are unsafe checkboxes
unsafe_hold.insert({setting->Id(), widget});
}
} }
UpdateGroup(ui->accuracy->currentIndex());
for (const auto& [label, widget] : unsafe_hold) {
unsafe_layout->addWidget(widget);
}
UpdateGroup(accuracy_combobox->currentIndex());
} }
void ConfigureCpu::UpdateGroup(int index) { void ConfigureCpu::UpdateGroup(int index) {
if (!Settings::IsConfiguringGlobal()) { const auto accuracy = static_cast<Settings::CpuAccuracy>(
index -= ConfigurationShared::USE_GLOBAL_OFFSET; combobox_translations.at(Settings::EnumMetadata<Settings::CpuAccuracy>::Index())[index]
} .first);
const auto accuracy = static_cast<Settings::CPUAccuracy>(index); ui->unsafe_group->setVisible(accuracy == Settings::CpuAccuracy::Unsafe);
ui->unsafe_group->setVisible(accuracy == Settings::CPUAccuracy::Unsafe);
} }
void ConfigureCpu::ApplyConfiguration() { void ConfigureCpu::ApplyConfiguration() {
ConfigurationShared::ApplyPerGameSetting(&Settings::values.cpu_accuracy, ui->accuracy); const bool is_powered_on = system.IsPoweredOn();
ConfigurationShared::ApplyPerGameSetting(&Settings::values.cpuopt_unsafe_unfuse_fma, for (const auto& apply_func : apply_funcs) {
ui->cpuopt_unsafe_unfuse_fma, apply_func(is_powered_on);
cpuopt_unsafe_unfuse_fma); }
ConfigurationShared::ApplyPerGameSetting(&Settings::values.cpuopt_unsafe_reduce_fp_error,
ui->cpuopt_unsafe_reduce_fp_error,
cpuopt_unsafe_reduce_fp_error);
ConfigurationShared::ApplyPerGameSetting(&Settings::values.cpuopt_unsafe_ignore_standard_fpcr,
ui->cpuopt_unsafe_ignore_standard_fpcr,
cpuopt_unsafe_ignore_standard_fpcr);
ConfigurationShared::ApplyPerGameSetting(&Settings::values.cpuopt_unsafe_inaccurate_nan,
ui->cpuopt_unsafe_inaccurate_nan,
cpuopt_unsafe_inaccurate_nan);
ConfigurationShared::ApplyPerGameSetting(&Settings::values.cpuopt_unsafe_fastmem_check,
ui->cpuopt_unsafe_fastmem_check,
cpuopt_unsafe_fastmem_check);
ConfigurationShared::ApplyPerGameSetting(&Settings::values.cpuopt_unsafe_ignore_global_monitor,
ui->cpuopt_unsafe_ignore_global_monitor,
cpuopt_unsafe_ignore_global_monitor);
} }
void ConfigureCpu::changeEvent(QEvent* event) { void ConfigureCpu::changeEvent(QEvent* event) {
@ -96,32 +100,3 @@ void ConfigureCpu::changeEvent(QEvent* event) {
void ConfigureCpu::RetranslateUI() { void ConfigureCpu::RetranslateUI() {
ui->retranslateUi(this); ui->retranslateUi(this);
} }
void ConfigureCpu::SetupPerGameUI() {
if (Settings::IsConfiguringGlobal()) {
return;
}
ConfigurationShared::SetColoredComboBox(
ui->accuracy, ui->widget_accuracy,
static_cast<u32>(Settings::values.cpu_accuracy.GetValue(true)));
ConfigurationShared::SetColoredTristate(ui->cpuopt_unsafe_unfuse_fma,
Settings::values.cpuopt_unsafe_unfuse_fma,
cpuopt_unsafe_unfuse_fma);
ConfigurationShared::SetColoredTristate(ui->cpuopt_unsafe_reduce_fp_error,
Settings::values.cpuopt_unsafe_reduce_fp_error,
cpuopt_unsafe_reduce_fp_error);
ConfigurationShared::SetColoredTristate(ui->cpuopt_unsafe_ignore_standard_fpcr,
Settings::values.cpuopt_unsafe_ignore_standard_fpcr,
cpuopt_unsafe_ignore_standard_fpcr);
ConfigurationShared::SetColoredTristate(ui->cpuopt_unsafe_inaccurate_nan,
Settings::values.cpuopt_unsafe_inaccurate_nan,
cpuopt_unsafe_inaccurate_nan);
ConfigurationShared::SetColoredTristate(ui->cpuopt_unsafe_fastmem_check,
Settings::values.cpuopt_unsafe_fastmem_check,
cpuopt_unsafe_fastmem_check);
ConfigurationShared::SetColoredTristate(ui->cpuopt_unsafe_ignore_global_monitor,
Settings::values.cpuopt_unsafe_ignore_global_monitor,
cpuopt_unsafe_ignore_global_monitor);
}

@ -4,29 +4,34 @@
#pragma once #pragma once
#include <memory> #include <memory>
#include <vector>
#include <QWidget> #include <QWidget>
#include "yuzu/configuration/configuration_shared.h"
#include "yuzu/configuration/shared_translation.h"
class QComboBox;
namespace Core { namespace Core {
class System; class System;
} }
namespace ConfigurationShared {
enum class CheckState;
}
namespace Ui { namespace Ui {
class ConfigureCpu; class ConfigureCpu;
} }
class ConfigureCpu : public QWidget { namespace ConfigurationShared {
Q_OBJECT class Builder;
}
class ConfigureCpu : public ConfigurationShared::Tab {
public: public:
explicit ConfigureCpu(const Core::System& system_, QWidget* parent = nullptr); explicit ConfigureCpu(const Core::System& system_,
std::shared_ptr<std::vector<ConfigurationShared::Tab*>> group,
const ConfigurationShared::Builder& builder, QWidget* parent = nullptr);
~ConfigureCpu() override; ~ConfigureCpu() override;
void ApplyConfiguration(); void ApplyConfiguration() override;
void SetConfiguration(); void SetConfiguration() override;
private: private:
void changeEvent(QEvent* event) override; void changeEvent(QEvent* event) override;
@ -34,16 +39,14 @@ private:
void UpdateGroup(int index); void UpdateGroup(int index);
void SetupPerGameUI(); void Setup(const ConfigurationShared::Builder& builder);
std::unique_ptr<Ui::ConfigureCpu> ui; std::unique_ptr<Ui::ConfigureCpu> ui;
ConfigurationShared::CheckState cpuopt_unsafe_unfuse_fma;
ConfigurationShared::CheckState cpuopt_unsafe_reduce_fp_error;
ConfigurationShared::CheckState cpuopt_unsafe_ignore_standard_fpcr;
ConfigurationShared::CheckState cpuopt_unsafe_inaccurate_nan;
ConfigurationShared::CheckState cpuopt_unsafe_fastmem_check;
ConfigurationShared::CheckState cpuopt_unsafe_ignore_global_monitor;
const Core::System& system; const Core::System& system;
const ConfigurationShared::ComboboxTranslationMap& combobox_translations;
std::vector<std::function<void(bool)>> apply_funcs{};
QComboBox* accuracy_combobox;
}; };

@ -16,9 +16,12 @@
<property name="accessibleName"> <property name="accessibleName">
<string>CPU</string> <string>CPU</string>
</property> </property>
<layout class="QVBoxLayout"> <layout class="QVBoxLayout" name="vboxlayout_2" stretch="0">
<item> <item>
<layout class="QVBoxLayout"> <layout class="QVBoxLayout" name="vboxlayout">
<property name="bottomMargin">
<number>0</number>
</property>
<item> <item>
<widget class="QGroupBox" name="groupBox"> <widget class="QGroupBox" name="groupBox">
<property name="title"> <property name="title">
@ -27,38 +30,19 @@
<layout class="QVBoxLayout"> <layout class="QVBoxLayout">
<item> <item>
<widget class="QWidget" name="widget_accuracy" native="true"> <widget class="QWidget" name="widget_accuracy" native="true">
<layout class="QHBoxLayout" name="layout_accuracy"> <layout class="QVBoxLayout" name="verticalLayout">
<item> <property name="leftMargin">
<widget class="QLabel" name="label_accuracy"> <number>0</number>
<property name="text"> </property>
<string>Accuracy:</string> <property name="topMargin">
</property> <number>0</number>
</widget> </property>
</item> <property name="rightMargin">
<item> <number>0</number>
<widget class="QComboBox" name="accuracy"> </property>
<item> <property name="bottomMargin">
<property name="text"> <number>0</number>
<string>Auto</string> </property>
</property>
</item>
<item>
<property name="text">
<string>Accurate</string>
</property>
</item>
<item>
<property name="text">
<string>Unsafe</string>
</property>
</item>
<item>
<property name="text">
<string>Paranoid (disables most optimizations)</string>
</property>
</item>
</widget>
</item>
</layout> </layout>
</widget> </widget>
</item> </item>
@ -75,10 +59,6 @@
</layout> </layout>
</widget> </widget>
</item> </item>
</layout>
</item>
<item>
<layout class="QVBoxLayout">
<item> <item>
<widget class="QGroupBox" name="unsafe_group"> <widget class="QGroupBox" name="unsafe_group">
<property name="title"> <property name="title">
@ -96,105 +76,44 @@
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QCheckBox" name="cpuopt_unsafe_unfuse_fma"> <widget class="QWidget" name="unsafe_widget" native="true">
<property name="toolTip"> <layout class="QVBoxLayout" name="unsafe_layout">
<string> <property name="leftMargin">
&lt;div&gt;This option improves speed by reducing accuracy of fused-multiply-add instructions on CPUs without native FMA support.&lt;/div&gt; <number>0</number>
</string> </property>
</property> <property name="topMargin">
<property name="text"> <number>0</number>
<string>Unfuse FMA (improve performance on CPUs without FMA)</string> </property>
</property> <property name="rightMargin">
</widget> <number>0</number>
</item> </property>
<item> <property name="bottomMargin">
<widget class="QCheckBox" name="cpuopt_unsafe_reduce_fp_error"> <number>0</number>
<property name="toolTip"> </property>
<string> </layout>
&lt;div&gt;This option improves the speed of some approximate floating-point functions by using less accurate native approximations.&lt;/div&gt;
</string>
</property>
<property name="text">
<string>Faster FRSQRTE and FRECPE</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="cpuopt_unsafe_ignore_standard_fpcr">
<property name="toolTip">
<string>
&lt;div&gt;This option improves the speed of 32 bits ASIMD floating-point functions by running with incorrect rounding modes.&lt;/div&gt;
</string>
</property>
<property name="text">
<string>Faster ASIMD instructions (32 bits only)</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="cpuopt_unsafe_inaccurate_nan">
<property name="toolTip">
<string>
&lt;div&gt;This option improves speed by removing NaN checking. Please note this also reduces accuracy of certain floating-point instructions.&lt;/div&gt;
</string>
</property>
<property name="text">
<string>Inaccurate NaN handling</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="cpuopt_unsafe_fastmem_check">
<property name="toolTip">
<string>
&lt;div&gt;This option improves speed by eliminating a safety check before every memory read/write in guest. Disabling it may allow a game to read/write the emulator's memory.&lt;/div&gt;
</string>
</property>
<property name="text">
<string>Disable address space checks</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="cpuopt_unsafe_ignore_global_monitor">
<property name="toolTip">
<string>
&lt;div&gt;This option improves speed by relying only on the semantics of cmpxchg to ensure safety of exclusive access instructions. Please note this may result in deadlocks and other race conditions.&lt;/div&gt;
</string>
</property>
<property name="text">
<string>Ignore global monitor</string>
</property>
</widget> </widget>
</item> </item>
</layout> </layout>
</widget> </widget>
</item> </item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Expanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout> </layout>
</item> </item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label_disable_info">
<property name="text">
<string>CPU settings are available only when game is not running.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
<resources/> <resources/>

@ -2,360 +2,549 @@
<ui version="4.0"> <ui version="4.0">
<class>ConfigureDebug</class> <class>ConfigureDebug</class>
<widget class="QScrollArea" name="ConfigureDebug"> <widget class="QScrollArea" name="ConfigureDebug">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>831</width>
<height>760</height>
</rect>
</property>
<property name="widgetResizable"> <property name="widgetResizable">
<bool>true</bool> <bool>true</bool>
</property> </property>
<widget class="QWidget"> <widget class="QWidget" name="widget">
<layout class="QVBoxLayout" name="verticalLayout_1"> <property name="geometry">
<item> <rect>
<layout class="QVBoxLayout" name="verticalLayout_2"> <x>0</x>
<item> <y>0</y>
<widget class="QGroupBox" name="groupBox"> <width>829</width>
<property name="title"> <height>758</height>
<string>Debugger</string> </rect>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_3"> <layout class="QVBoxLayout" name="verticalLayout_1">
<item> <item>
<layout class="QHBoxLayout" name="horizontalLayout_11"> <layout class="QHBoxLayout" name="horizontalLayout">
<item> <property name="topMargin">
<widget class="QCheckBox" name="toggle_gdbstub"> <number>0</number>
<property name="text"> </property>
<string>Enable GDB Stub</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label_11">
<property name="text">
<string>Port:</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="gdbport_spinbox">
<property name="minimum">
<number>1024</number>
</property>
<property name="maximum">
<number>65535</number>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Logging</string>
</property>
<layout class="QGridLayout" name="gridLayout_1">
<item row="0" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout_1">
<item>
<widget class="QLabel" name="label_1">
<property name="text">
<string>Global Log Filter</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="log_filter_edit"/>
</item>
</layout>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="toggle_console">
<property name="text">
<string>Show Log in Console</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QPushButton" name="open_log_button">
<property name="text">
<string>Open Log Location</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="extended_logging">
<property name="enabled">
<bool>true</bool>
</property>
<property name="toolTip">
<string>When checked, the max size of the log increases from 100 MB to 1 GB</string>
</property>
<property name="text">
<string>Enable Extended Logging**</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_3">
<property name="title">
<string>Homebrew</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_5">
<item> <item>
<layout class="QHBoxLayout" name="horizontalLayout_4"> <widget class="QGroupBox" name="groupBox">
<item> <property name="sizePolicy">
<widget class="QLabel" name="label_3"> <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<property name="text"> <horstretch>0</horstretch>
<string>Arguments String</string> <verstretch>0</verstretch>
</property> </sizepolicy>
</widget>
</item>
<item>
<widget class="QLineEdit" name="homebrew_args_edit"/>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_4">
<property name="title">
<string>Graphics</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<widget class="QCheckBox" name="enable_graphics_debugging">
<property name="enabled">
<bool>true</bool>
</property> </property>
<property name="toolTip"> <property name="title">
<string>When checked, the graphics API enters a slower debugging mode</string> <string>Debugger</string>
</property> </property>
<property name="text"> <property name="alignment">
<string>Enable Graphics Debugging</string> <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property> </property>
<property name="flat">
<bool>false</bool>
</property>
<property name="checkable">
<bool>false</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QWidget" name="debug_widget" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="sizeConstraint">
<enum>QLayout::SetDefaultConstraint</enum>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QCheckBox" name="toggle_gdbstub">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Enable GDB Stub</string>
</property>
</widget>
</item>
<item>
<widget class="QWidget" name="horizontalWidget_3" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="label_11">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Port:</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="gdbport_spinbox">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimum">
<number>1024</number>
</property>
<property name="maximum">
<number>65535</number>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget> </widget>
</item> </item>
<item row="2" column="0"> <item>
<widget class="QCheckBox" name="enable_nsight_aftermath"> <widget class="QGroupBox" name="groupBox_2">
<property name="toolTip"> <property name="sizePolicy">
<string>When checked, it enables Nsight Aftermath crash dumps</string> <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property> </property>
<property name="text"> <property name="title">
<string>Enable Nsight Aftermath</string> <string>Logging</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QCheckBox" name="dump_shaders">
<property name="enabled">
<bool>true</bool>
</property>
<property name="toolTip">
<string>When checked, it will dump all the original assembler shaders from the disk shader cache or game as found</string>
</property>
<property name="text">
<string>Dump Game Shaders</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QCheckBox" name="dump_macros">
<property name="enabled">
<bool>true</bool>
</property>
<property name="toolTip">
<string>When checked, it will dump all the macro programs of the GPU</string>
</property>
<property name="text">
<string>Dump Maxwell Macros</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="disable_macro_jit">
<property name="enabled">
<bool>true</bool>
</property>
<property name="toolTip">
<string>When checked, it disables the macro Just In Time compiler. Enabling this makes games run slower</string>
</property>
<property name="text">
<string>Disable Macro JIT</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QCheckBox" name="disable_macro_hle">
<property name="enabled">
<bool>true</bool>
</property>
<property name="toolTip">
<string>When checked, it disables the macro HLE functions. Enabling this makes games run slower</string>
</property>
<property name="text">
<string>Disable Macro HLE</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="enable_shader_feedback">
<property name="toolTip">
<string>When checked, yuzu will log statistics about the compiled pipeline cache</string>
</property>
<property name="text">
<string>Enable Shader Feedback</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="disable_loop_safety_checks">
<property name="toolTip">
<string>When checked, it executes shaders without loop logic changes</string>
</property>
<property name="text">
<string>Disable Loop safety checks</string>
</property> </property>
<layout class="QGridLayout" name="gridLayout_1">
<item row="1" column="1">
<widget class="QPushButton" name="open_log_button">
<property name="text">
<string>Open Log Location</string>
</property>
</widget>
</item>
<item row="0" column="0" colspan="2">
<widget class="QWidget" name="logging_widget" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_1">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="label_1">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Global Log Filter</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="log_filter_edit"/>
</item>
</layout>
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="extended_logging">
<property name="enabled">
<bool>true</bool>
</property>
<property name="toolTip">
<string>When checked, the max size of the log increases from 100 MB to 1 GB</string>
</property>
<property name="text">
<string>Enable Extended Logging**</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="toggle_console">
<property name="text">
<string>Show Log in Console</string>
</property>
</widget>
</item>
</layout>
</widget> </widget>
</item> </item>
</layout> </layout>
</widget> </item>
</item> <item>
<item> <widget class="QGroupBox" name="groupBox_3">
<widget class="QGroupBox" name="groupBox_5"> <property name="title">
<property name="title"> <string>Homebrew</string>
<string>Debugging</string> </property>
</property> <layout class="QVBoxLayout" name="verticalLayout_5">
<layout class="QGridLayout" name="gridLayout_3"> <item>
<item row="2" column="0"> <layout class="QHBoxLayout" name="horizontalLayout_4">
<widget class="QCheckBox" name="reporting_services"> <item>
<property name="text"> <widget class="QLabel" name="label_3">
<string>Enable Verbose Reporting Services**</string> <property name="text">
<string>Arguments String</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="homebrew_args_edit"/>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QGroupBox" name="groupBox_4">
<property name="title">
<string>Graphics</string>
</property> </property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="3" column="0">
<widget class="QCheckBox" name="disable_loop_safety_checks">
<property name="toolTip">
<string>When checked, it executes shaders without loop logic changes</string>
</property>
<property name="text">
<string>Disable Loop safety checks</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QCheckBox" name="dump_shaders">
<property name="enabled">
<bool>true</bool>
</property>
<property name="toolTip">
<string>When checked, it will dump all the original assembler shaders from the disk shader cache or game as found</string>
</property>
<property name="text">
<string>Dump Game Shaders</string>
</property>
</widget>
</item>
<item row="7" column="0">
<widget class="QCheckBox" name="disable_macro_hle">
<property name="enabled">
<bool>true</bool>
</property>
<property name="toolTip">
<string>When checked, it disables the macro HLE functions. Enabling this makes games run slower</string>
</property>
<property name="text">
<string>Disable Macro HLE</string>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QCheckBox" name="disable_macro_jit">
<property name="enabled">
<bool>true</bool>
</property>
<property name="toolTip">
<string>When checked, it disables the macro Just In Time compiler. Enabling this makes games run slower</string>
</property>
<property name="text">
<string>Disable Macro JIT</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QCheckBox" name="enable_graphics_debugging">
<property name="enabled">
<bool>true</bool>
</property>
<property name="toolTip">
<string>When checked, the graphics API enters a slower debugging mode</string>
</property>
<property name="text">
<string>Enable Graphics Debugging</string>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QCheckBox" name="dump_macros">
<property name="enabled">
<bool>true</bool>
</property>
<property name="toolTip">
<string>When checked, it will dump all the macro programs of the GPU</string>
</property>
<property name="text">
<string>Dump Maxwell Macros</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="enable_shader_feedback">
<property name="toolTip">
<string>When checked, yuzu will log statistics about the compiled pipeline cache</string>
</property>
<property name="text">
<string>Enable Shader Feedback</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="enable_nsight_aftermath">
<property name="toolTip">
<string>When checked, it enables Nsight Aftermath crash dumps</string>
</property>
<property name="text">
<string>Enable Nsight Aftermath</string>
</property>
</widget>
</item>
<item row="8" column="0">
<spacer name="verticalSpacer_5">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Preferred</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget> </widget>
</item> </item>
<item row="0" column="0"> <item>
<widget class="QCheckBox" name="fs_access_log"> <widget class="QGroupBox" name="groupBox_6">
<property name="text"> <property name="title">
<string>Enable FS Access Log</string> <string>Advanced</string>
</property> </property>
<layout class="QGridLayout" name="gridLayout_4">
<item row="3" column="0">
<widget class="QCheckBox" name="perform_vulkan_check">
<property name="toolTip">
<string>Enables yuzu to check for a working Vulkan environment when the program starts up. Disable this if this is causing issues with external programs seeing yuzu.</string>
</property>
<property name="text">
<string>Perform Startup Vulkan Check</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QCheckBox" name="disable_web_applet">
<property name="text">
<string>Disable Web Applet</string>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QCheckBox" name="enable_all_controllers">
<property name="text">
<string>Enable All Controller Types</string>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QCheckBox" name="use_auto_stub">
<property name="text">
<string>Enable Auto-Stub**</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QCheckBox" name="quest_flag">
<property name="text">
<string>Kiosk (Quest) Mode</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="enable_cpu_debugging">
<property name="text">
<string>Enable CPU Debugging</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="use_debug_asserts">
<property name="text">
<string>Enable Debug Asserts</string>
</property>
</widget>
</item>
<item row="7" column="0">
<spacer name="verticalSpacer_4">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Expanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget> </widget>
</item> </item>
<item row="0" column="1"> <item>
<widget class="QCheckBox" name="dump_audio_commands"> <widget class="QGroupBox" name="groupBox_5">
<property name="toolTip"> <property name="title">
<string>Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer.</string> <string>Debugging</string>
</property>
<property name="text">
<string>Dump Audio Commands To Console**</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QCheckBox" name="create_crash_dumps">
<property name="text">
<string>Create Minidump After Crash</string>
</property> </property>
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="0">
<widget class="QCheckBox" name="fs_access_log">
<property name="text">
<string>Enable FS Access Log</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QCheckBox" name="create_crash_dumps">
<property name="text">
<string>Create Minidump After Crash</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QCheckBox" name="dump_audio_commands">
<property name="toolTip">
<string>Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer.</string>
</property>
<property name="text">
<string>Dump Audio Commands To Console**</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="reporting_services">
<property name="text">
<string>Enable Verbose Reporting Services**</string>
</property>
</widget>
</item>
<item row="5" column="0">
<spacer name="verticalSpacer_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Expanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget> </widget>
</item> </item>
</layout> </layout>
</widget> </item>
</item> <item>
<item> <spacer name="verticalSpacer">
<widget class="QGroupBox" name="groupBox_6"> <property name="orientation">
<property name="title"> <enum>Qt::Vertical</enum>
<string>Advanced</string> </property>
</property> <property name="sizeHint" stdset="0">
<layout class="QGridLayout" name="gridLayout_4"> <size>
<item row="0" column="0"> <width>20</width>
<widget class="QCheckBox" name="quest_flag"> <height>0</height>
<property name="text"> </size>
<string>Kiosk (Quest) Mode</string> </property>
</property> </spacer>
</widget> </item>
</item> <item>
<item row="1" column="0"> <widget class="QLabel" name="label_5">
<widget class="QCheckBox" name="enable_cpu_debugging"> <property name="sizePolicy">
<property name="text"> <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<string>Enable CPU Debugging</string> <horstretch>0</horstretch>
</property> <verstretch>0</verstretch>
</widget> </sizepolicy>
</item> </property>
<item row="2" column="0"> <property name="font">
<widget class="QCheckBox" name="use_debug_asserts"> <font>
<property name="text"> <italic>true</italic>
<string>Enable Debug Asserts</string> </font>
</property> </property>
</widget> <property name="text">
</item> <string>**This will be reset automatically when yuzu closes.</string>
<item row="0" column="1"> </property>
<widget class="QCheckBox" name="use_auto_stub"> <property name="indent">
<property name="text"> <number>20</number>
<string>Enable Auto-Stub**</string> </property>
</property> </widget>
</widget> </item>
</item> </layout>
<item row="1" column="1"> </widget>
<widget class="QCheckBox" name="enable_all_controllers">
<property name="text">
<string>Enable All Controller Types</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QCheckBox" name="disable_web_applet">
<property name="text">
<string>Disable Web Applet</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QCheckBox" name="perform_vulkan_check">
<property name="toolTip">
<string>Enables yuzu to check for a working Vulkan environment when the program starts up. Disable this if this is causing issues with external programs seeing yuzu.</string>
</property>
<property name="text">
<string>Perform Startup Vulkan Check</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QLabel" name="label_5">
<property name="font">
<font>
<italic>true</italic>
</font>
</property>
<property name="text">
<string>**This will be reset automatically when yuzu closes.</string>
</property>
<property name="indent">
<number>20</number>
</property>
</widget>
</item>
</layout>
</widget>
</widget> </widget>
<tabstops> <tabstops>
<tabstop>log_filter_edit</tabstop> <tabstop>log_filter_edit</tabstop>
@ -366,14 +555,11 @@
<tabstop>enable_graphics_debugging</tabstop> <tabstop>enable_graphics_debugging</tabstop>
<tabstop>enable_shader_feedback</tabstop> <tabstop>enable_shader_feedback</tabstop>
<tabstop>enable_nsight_aftermath</tabstop> <tabstop>enable_nsight_aftermath</tabstop>
<tabstop>disable_macro_jit</tabstop>
<tabstop>disable_loop_safety_checks</tabstop>
<tabstop>fs_access_log</tabstop> <tabstop>fs_access_log</tabstop>
<tabstop>reporting_services</tabstop> <tabstop>reporting_services</tabstop>
<tabstop>quest_flag</tabstop> <tabstop>quest_flag</tabstop>
<tabstop>enable_cpu_debugging</tabstop> <tabstop>enable_cpu_debugging</tabstop>
<tabstop>use_debug_asserts</tabstop> <tabstop>use_debug_asserts</tabstop>
<tabstop>use_auto_stub</tabstop>
</tabstops> </tabstops>
<resources/> <resources/>
<connections/> <connections/>

@ -32,21 +32,23 @@ ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry_,
std::vector<VkDeviceInfo::Record>& vk_device_records, std::vector<VkDeviceInfo::Record>& vk_device_records,
Core::System& system_, bool enable_web_config) Core::System& system_, bool enable_web_config)
: QDialog(parent), ui{std::make_unique<Ui::ConfigureDialog>()}, : QDialog(parent), ui{std::make_unique<Ui::ConfigureDialog>()},
registry(registry_), system{system_}, audio_tab{std::make_unique<ConfigureAudio>(system_, registry(registry_), system{system_}, builder{std::make_unique<ConfigurationShared::Builder>(
this)}, this, !system_.IsPoweredOn())},
cpu_tab{std::make_unique<ConfigureCpu>(system_, this)}, audio_tab{std::make_unique<ConfigureAudio>(system_, nullptr, *builder, this)},
cpu_tab{std::make_unique<ConfigureCpu>(system_, nullptr, *builder, this)},
debug_tab_tab{std::make_unique<ConfigureDebugTab>(system_, this)}, debug_tab_tab{std::make_unique<ConfigureDebugTab>(system_, this)},
filesystem_tab{std::make_unique<ConfigureFilesystem>(this)}, filesystem_tab{std::make_unique<ConfigureFilesystem>(this)},
general_tab{std::make_unique<ConfigureGeneral>(system_, this)}, general_tab{std::make_unique<ConfigureGeneral>(system_, nullptr, *builder, this)},
graphics_advanced_tab{std::make_unique<ConfigureGraphicsAdvanced>(system_, this)}, graphics_advanced_tab{
std::make_unique<ConfigureGraphicsAdvanced>(system_, nullptr, *builder, this)},
graphics_tab{std::make_unique<ConfigureGraphics>( graphics_tab{std::make_unique<ConfigureGraphics>(
system_, vk_device_records, [&]() { graphics_advanced_tab->ExposeComputeOption(); }, system_, vk_device_records, [&]() { graphics_advanced_tab->ExposeComputeOption(); },
this)}, nullptr, *builder, this)},
hotkeys_tab{std::make_unique<ConfigureHotkeys>(system_.HIDCore(), this)}, hotkeys_tab{std::make_unique<ConfigureHotkeys>(system_.HIDCore(), this)},
input_tab{std::make_unique<ConfigureInput>(system_, this)}, input_tab{std::make_unique<ConfigureInput>(system_, this)},
network_tab{std::make_unique<ConfigureNetwork>(system_, this)}, network_tab{std::make_unique<ConfigureNetwork>(system_, this)},
profile_tab{std::make_unique<ConfigureProfileManager>(system_, this)}, profile_tab{std::make_unique<ConfigureProfileManager>(system_, this)},
system_tab{std::make_unique<ConfigureSystem>(system_, this)}, system_tab{std::make_unique<ConfigureSystem>(system_, nullptr, *builder, this)},
ui_tab{std::make_unique<ConfigureUi>(system_, this)}, web_tab{std::make_unique<ConfigureWeb>( ui_tab{std::make_unique<ConfigureUi>(system_, this)}, web_tab{std::make_unique<ConfigureWeb>(
this)} { this)} {
Settings::SetConfiguringGlobal(true); Settings::SetConfiguringGlobal(true);
@ -95,6 +97,9 @@ ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry_,
adjustSize(); adjustSize();
ui->selectorList->setCurrentRow(0); ui->selectorList->setCurrentRow(0);
// Selects the leftmost button on the bottom bar (Cancel as of writing)
ui->buttonBox->setFocus();
} }
ConfigureDialog::~ConfigureDialog() = default; ConfigureDialog::~ConfigureDialog() = default;

@ -6,6 +6,9 @@
#include <memory> #include <memory>
#include <vector> #include <vector>
#include <QDialog> #include <QDialog>
#include "configuration/shared_widget.h"
#include "yuzu/configuration/configuration_shared.h"
#include "yuzu/configuration/shared_translation.h"
#include "yuzu/vk_device_info.h" #include "yuzu/vk_device_info.h"
namespace Core { namespace Core {
@ -69,6 +72,8 @@ private:
HotkeyRegistry& registry; HotkeyRegistry& registry;
Core::System& system; Core::System& system;
std::unique_ptr<ConfigurationShared::Builder> builder;
std::vector<ConfigurationShared::Tab*> tab_group;
std::unique_ptr<ConfigureAudio> audio_tab; std::unique_ptr<ConfigureAudio> audio_tab;
std::unique_ptr<ConfigureCpu> cpu_tab; std::unique_ptr<ConfigureCpu> cpu_tab;

@ -3,57 +3,60 @@
#include <functional> #include <functional>
#include <utility> #include <utility>
#include <vector>
#include <QMessageBox> #include <QMessageBox>
#include "common/settings.h" #include "common/settings.h"
#include "core/core.h" #include "core/core.h"
#include "ui_configure_general.h" #include "ui_configure_general.h"
#include "yuzu/configuration/configuration_shared.h" #include "yuzu/configuration/configuration_shared.h"
#include "yuzu/configuration/configure_general.h" #include "yuzu/configuration/configure_general.h"
#include "yuzu/configuration/shared_widget.h"
#include "yuzu/uisettings.h" #include "yuzu/uisettings.h"
ConfigureGeneral::ConfigureGeneral(const Core::System& system_, QWidget* parent) ConfigureGeneral::ConfigureGeneral(const Core::System& system_,
: QWidget(parent), ui{std::make_unique<Ui::ConfigureGeneral>()}, system{system_} { std::shared_ptr<std::vector<ConfigurationShared::Tab*>> group_,
const ConfigurationShared::Builder& builder, QWidget* parent)
: Tab(group_, parent), ui{std::make_unique<Ui::ConfigureGeneral>()}, system{system_} {
ui->setupUi(this); ui->setupUi(this);
SetupPerGameUI(); Setup(builder);
SetConfiguration(); SetConfiguration();
if (Settings::IsConfiguringGlobal()) {
connect(ui->toggle_speed_limit, &QCheckBox::clicked, ui->speed_limit,
[this]() { ui->speed_limit->setEnabled(ui->toggle_speed_limit->isChecked()); });
}
connect(ui->button_reset_defaults, &QPushButton::clicked, this, connect(ui->button_reset_defaults, &QPushButton::clicked, this,
&ConfigureGeneral::ResetDefaults); &ConfigureGeneral::ResetDefaults);
if (!Settings::IsConfiguringGlobal()) {
ui->button_reset_defaults->setVisible(false);
}
} }
ConfigureGeneral::~ConfigureGeneral() = default; ConfigureGeneral::~ConfigureGeneral() = default;
void ConfigureGeneral::SetConfiguration() { void ConfigureGeneral::SetConfiguration() {}
const bool runtime_lock = !system.IsPoweredOn();
ui->use_multi_core->setEnabled(runtime_lock); void ConfigureGeneral::Setup(const ConfigurationShared::Builder& builder) {
ui->use_multi_core->setChecked(Settings::values.use_multi_core.GetValue()); QLayout& layout = *ui->general_widget->layout();
ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing.GetValue()); std::map<u32, QWidget*> hold{};
ui->toggle_user_on_boot->setChecked(UISettings::values.select_user_on_boot.GetValue());
ui->toggle_background_pause->setChecked(UISettings::values.pause_when_in_background.GetValue());
ui->toggle_hide_mouse->setChecked(UISettings::values.hide_mouse.GetValue());
ui->toggle_controller_applet_disabled->setEnabled(runtime_lock);
ui->toggle_controller_applet_disabled->setChecked(
UISettings::values.controller_applet_disabled.GetValue());
ui->toggle_speed_limit->setChecked(Settings::values.use_speed_limit.GetValue()); for (const auto setting :
ui->speed_limit->setValue(Settings::values.speed_limit.GetValue()); UISettings::values.linkage.by_category[Settings::Category::UiGeneral]) {
auto* widget = builder.BuildWidget(setting, apply_funcs);
ui->button_reset_defaults->setEnabled(runtime_lock); if (widget == nullptr) {
continue;
}
if (!widget->Valid()) {
widget->deleteLater();
continue;
}
if (Settings::IsConfiguringGlobal()) { hold.emplace(setting->Id(), widget);
ui->speed_limit->setEnabled(Settings::values.use_speed_limit.GetValue()); }
} else {
ui->speed_limit->setEnabled(Settings::values.use_speed_limit.GetValue() && for (const auto& [id, widget] : hold) {
use_speed_limit != ConfigurationShared::CheckState::Global); layout.addWidget(widget);
} }
} }
@ -77,32 +80,9 @@ void ConfigureGeneral::ResetDefaults() {
} }
void ConfigureGeneral::ApplyConfiguration() { void ConfigureGeneral::ApplyConfiguration() {
ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_multi_core, ui->use_multi_core, bool powered_on = system.IsPoweredOn();
use_multi_core); for (const auto& func : apply_funcs) {
func(powered_on);
if (Settings::IsConfiguringGlobal()) {
UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked();
UISettings::values.select_user_on_boot = ui->toggle_user_on_boot->isChecked();
UISettings::values.pause_when_in_background = ui->toggle_background_pause->isChecked();
UISettings::values.hide_mouse = ui->toggle_hide_mouse->isChecked();
UISettings::values.controller_applet_disabled =
ui->toggle_controller_applet_disabled->isChecked();
// Guard if during game and set to game-specific value
if (Settings::values.use_speed_limit.UsingGlobal()) {
Settings::values.use_speed_limit.SetValue(ui->toggle_speed_limit->checkState() ==
Qt::Checked);
Settings::values.speed_limit.SetValue(ui->speed_limit->value());
}
} else {
bool global_speed_limit = use_speed_limit == ConfigurationShared::CheckState::Global;
Settings::values.use_speed_limit.SetGlobal(global_speed_limit);
Settings::values.speed_limit.SetGlobal(global_speed_limit);
if (!global_speed_limit) {
Settings::values.use_speed_limit.SetValue(ui->toggle_speed_limit->checkState() ==
Qt::Checked);
Settings::values.speed_limit.SetValue(ui->speed_limit->value());
}
} }
} }
@ -117,33 +97,3 @@ void ConfigureGeneral::changeEvent(QEvent* event) {
void ConfigureGeneral::RetranslateUI() { void ConfigureGeneral::RetranslateUI() {
ui->retranslateUi(this); ui->retranslateUi(this);
} }
void ConfigureGeneral::SetupPerGameUI() {
if (Settings::IsConfiguringGlobal()) {
// Disables each setting if:
// - A game is running (thus settings in use), and
// - A non-global setting is applied.
ui->toggle_speed_limit->setEnabled(Settings::values.use_speed_limit.UsingGlobal());
ui->speed_limit->setEnabled(Settings::values.speed_limit.UsingGlobal());
return;
}
ui->toggle_check_exit->setVisible(false);
ui->toggle_user_on_boot->setVisible(false);
ui->toggle_background_pause->setVisible(false);
ui->toggle_hide_mouse->setVisible(false);
ui->toggle_controller_applet_disabled->setVisible(false);
ui->button_reset_defaults->setVisible(false);
ConfigurationShared::SetColoredTristate(ui->toggle_speed_limit,
Settings::values.use_speed_limit, use_speed_limit);
ConfigurationShared::SetColoredTristate(ui->use_multi_core, Settings::values.use_multi_core,
use_multi_core);
connect(ui->toggle_speed_limit, &QCheckBox::clicked, ui->speed_limit, [this]() {
ui->speed_limit->setEnabled(ui->toggle_speed_limit->isChecked() &&
(use_speed_limit != ConfigurationShared::CheckState::Global));
});
}

@ -5,48 +5,49 @@
#include <functional> #include <functional>
#include <memory> #include <memory>
#include <vector>
#include <QWidget> #include <QWidget>
#include "yuzu/configuration/configuration_shared.h"
namespace Core { namespace Core {
class System; class System;
} }
class ConfigureDialog; class ConfigureDialog;
namespace ConfigurationShared {
enum class CheckState;
}
class HotkeyRegistry; class HotkeyRegistry;
namespace Ui { namespace Ui {
class ConfigureGeneral; class ConfigureGeneral;
} }
class ConfigureGeneral : public QWidget { namespace ConfigurationShared {
Q_OBJECT class Builder;
}
class ConfigureGeneral : public ConfigurationShared::Tab {
public: public:
explicit ConfigureGeneral(const Core::System& system_, QWidget* parent = nullptr); explicit ConfigureGeneral(const Core::System& system_,
std::shared_ptr<std::vector<ConfigurationShared::Tab*>> group,
const ConfigurationShared::Builder& builder,
QWidget* parent = nullptr);
~ConfigureGeneral() override; ~ConfigureGeneral() override;
void SetResetCallback(std::function<void()> callback); void SetResetCallback(std::function<void()> callback);
void ResetDefaults(); void ResetDefaults();
void ApplyConfiguration(); void ApplyConfiguration() override;
void SetConfiguration(); void SetConfiguration() override;
private: private:
void Setup(const ConfigurationShared::Builder& builder);
void changeEvent(QEvent* event) override; void changeEvent(QEvent* event) override;
void RetranslateUI(); void RetranslateUI();
void SetupPerGameUI();
std::function<void()> reset_callback; std::function<void()> reset_callback;
std::unique_ptr<Ui::ConfigureGeneral> ui; std::unique_ptr<Ui::ConfigureGeneral> ui;
ConfigurationShared::CheckState use_speed_limit; std::vector<std::function<void(bool)>> apply_funcs{};
ConfigurationShared::CheckState use_multi_core;
const Core::System& system; const Core::System& system;
}; };

@ -26,77 +26,22 @@
</property> </property>
<layout class="QHBoxLayout" name="GeneralHorizontalLayout"> <layout class="QHBoxLayout" name="GeneralHorizontalLayout">
<item> <item>
<layout class="QVBoxLayout" name="GeneralVerticalLayout"> <widget class="QWidget" name="general_widget" native="true">
<item> <layout class="QVBoxLayout" name="GeneralVerticalLayout">
<layout class="QHBoxLayout" name="horizontalLayout_2"> <property name="leftMargin">
<item> <number>0</number>
<widget class="QCheckBox" name="toggle_speed_limit"> </property>
<property name="text"> <property name="topMargin">
<string>Limit Speed Percent</string> <number>0</number>
</property> </property>
</widget> <property name="rightMargin">
</item> <number>0</number>
<item> </property>
<widget class="QSpinBox" name="speed_limit"> <property name="bottomMargin">
<property name="suffix"> <number>0</number>
<string>%</string> </property>
</property> </layout>
<property name="minimum"> </widget>
<number>1</number>
</property>
<property name="maximum">
<number>9999</number>
</property>
<property name="value">
<number>100</number>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QCheckBox" name="use_multi_core">
<property name="text">
<string>Multicore CPU Emulation</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="toggle_check_exit">
<property name="text">
<string>Confirm exit while emulation is running</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="toggle_user_on_boot">
<property name="text">
<string>Prompt for user on game boot</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="toggle_background_pause">
<property name="text">
<string>Pause emulation when in background</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="toggle_hide_mouse">
<property name="text">
<string>Hide mouse on inactivity</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="toggle_controller_applet_disabled">
<property name="text">
<string>Disable controller applet</string>
</property>
</widget>
</item>
</layout>
</item> </item>
</layout> </layout>
</widget> </widget>

@ -7,6 +7,7 @@
#include <iterator> #include <iterator>
#include <string> #include <string>
#include <tuple> #include <tuple>
#include <typeinfo>
#include <utility> #include <utility>
#include <vector> #include <vector>
#include <QBoxLayout> #include <QBoxLayout>
@ -15,23 +16,29 @@
#include <QComboBox> #include <QComboBox>
#include <QIcon> #include <QIcon>
#include <QLabel> #include <QLabel>
#include <QLineEdit>
#include <QPixmap> #include <QPixmap>
#include <QPushButton> #include <QPushButton>
#include <QSlider> #include <QSlider>
#include <QStringLiteral> #include <QStringLiteral>
#include <QtCore/qobjectdefs.h> #include <QtCore/qobjectdefs.h>
#include <qabstractbutton.h>
#include <qboxlayout.h>
#include <qcoreevent.h> #include <qcoreevent.h>
#include <qglobal.h> #include <qglobal.h>
#include <qgridlayout.h>
#include <vulkan/vulkan_core.h> #include <vulkan/vulkan_core.h>
#include "common/common_types.h" #include "common/common_types.h"
#include "common/dynamic_library.h" #include "common/dynamic_library.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "common/settings.h" #include "common/settings.h"
#include "common/settings_enums.h"
#include "core/core.h" #include "core/core.h"
#include "ui_configure_graphics.h" #include "ui_configure_graphics.h"
#include "yuzu/configuration/configuration_shared.h" #include "yuzu/configuration/configuration_shared.h"
#include "yuzu/configuration/configure_graphics.h" #include "yuzu/configuration/configure_graphics.h"
#include "yuzu/configuration/shared_widget.h"
#include "yuzu/qt_common.h" #include "yuzu/qt_common.h"
#include "yuzu/uisettings.h" #include "yuzu/uisettings.h"
#include "yuzu/vk_device_info.h" #include "yuzu/vk_device_info.h"
@ -46,9 +53,9 @@ static constexpr VkPresentModeKHR VSyncSettingToMode(Settings::VSyncMode mode) {
return VK_PRESENT_MODE_IMMEDIATE_KHR; return VK_PRESENT_MODE_IMMEDIATE_KHR;
case Settings::VSyncMode::Mailbox: case Settings::VSyncMode::Mailbox:
return VK_PRESENT_MODE_MAILBOX_KHR; return VK_PRESENT_MODE_MAILBOX_KHR;
case Settings::VSyncMode::FIFO: case Settings::VSyncMode::Fifo:
return VK_PRESENT_MODE_FIFO_KHR; return VK_PRESENT_MODE_FIFO_KHR;
case Settings::VSyncMode::FIFORelaxed: case Settings::VSyncMode::FifoRelaxed:
return VK_PRESENT_MODE_FIFO_RELAXED_KHR; return VK_PRESENT_MODE_FIFO_RELAXED_KHR;
default: default:
return VK_PRESENT_MODE_FIFO_KHR; return VK_PRESENT_MODE_FIFO_KHR;
@ -62,50 +69,67 @@ static constexpr Settings::VSyncMode PresentModeToSetting(VkPresentModeKHR mode)
case VK_PRESENT_MODE_MAILBOX_KHR: case VK_PRESENT_MODE_MAILBOX_KHR:
return Settings::VSyncMode::Mailbox; return Settings::VSyncMode::Mailbox;
case VK_PRESENT_MODE_FIFO_KHR: case VK_PRESENT_MODE_FIFO_KHR:
return Settings::VSyncMode::FIFO; return Settings::VSyncMode::Fifo;
case VK_PRESENT_MODE_FIFO_RELAXED_KHR: case VK_PRESENT_MODE_FIFO_RELAXED_KHR:
return Settings::VSyncMode::FIFORelaxed; return Settings::VSyncMode::FifoRelaxed;
default: default:
return Settings::VSyncMode::FIFO; return Settings::VSyncMode::Fifo;
} }
} }
ConfigureGraphics::ConfigureGraphics(const Core::System& system_, ConfigureGraphics::ConfigureGraphics(const Core::System& system_,
std::vector<VkDeviceInfo::Record>& records_, std::vector<VkDeviceInfo::Record>& records_,
const std::function<void()>& expose_compute_option_, const std::function<void()>& expose_compute_option_,
QWidget* parent) std::shared_ptr<std::vector<ConfigurationShared::Tab*>> group_,
: QWidget(parent), ui{std::make_unique<Ui::ConfigureGraphics>()}, records{records_}, const ConfigurationShared::Builder& builder, QWidget* parent)
expose_compute_option{expose_compute_option_}, system{system_} { : ConfigurationShared::Tab(group_, parent), ui{std::make_unique<Ui::ConfigureGraphics>()},
records{records_}, expose_compute_option{expose_compute_option_}, system{system_},
combobox_translations{builder.ComboboxTranslations()},
shader_mapping{
combobox_translations.at(Settings::EnumMetadata<Settings::ShaderBackend>::Index())} {
vulkan_device = Settings::values.vulkan_device.GetValue(); vulkan_device = Settings::values.vulkan_device.GetValue();
RetrieveVulkanDevices(); RetrieveVulkanDevices();
ui->setupUi(this); ui->setupUi(this);
Setup(builder);
for (const auto& device : vulkan_devices) { for (const auto& device : vulkan_devices) {
ui->device->addItem(device); vulkan_device_combobox->addItem(device);
} }
ui->backend->addItem(QStringLiteral("GLSL")); UpdateBackgroundColorButton(QColor::fromRgb(Settings::values.bg_red.GetValue(),
ui->backend->addItem(tr("GLASM (Assembly Shaders, NVIDIA Only)")); Settings::values.bg_green.GetValue(),
ui->backend->addItem(tr("SPIR-V (Experimental, Mesa Only)")); Settings::values.bg_blue.GetValue()));
UpdateAPILayout();
PopulateVSyncModeSelection(); //< must happen after UpdateAPILayout
SetupPerGameUI(); // VSync setting needs to be determined after populating the VSync combobox
if (Settings::IsConfiguringGlobal()) {
const auto vsync_mode_setting = Settings::values.vsync_mode.GetValue();
const auto vsync_mode = VSyncSettingToMode(vsync_mode_setting);
int index{};
for (const auto mode : vsync_mode_combobox_enum_map) {
if (mode == vsync_mode) {
break;
}
index++;
}
if (static_cast<unsigned long>(index) < vsync_mode_combobox_enum_map.size()) {
vsync_mode_combobox->setCurrentIndex(index);
}
}
SetConfiguration(); connect(api_combobox, qOverload<int>(&QComboBox::activated), this, [this] {
connect(ui->api, qOverload<int>(&QComboBox::currentIndexChanged), this, [this] {
UpdateAPILayout(); UpdateAPILayout();
PopulateVSyncModeSelection(); PopulateVSyncModeSelection();
if (!Settings::IsConfiguringGlobal()) {
ConfigurationShared::SetHighlight(
ui->api_widget, ui->api->currentIndex() != ConfigurationShared::USE_GLOBAL_INDEX);
}
}); });
connect(ui->device, qOverload<int>(&QComboBox::activated), this, [this](int device) { connect(vulkan_device_combobox, qOverload<int>(&QComboBox::activated), this,
UpdateDeviceSelection(device); [this](int device) {
PopulateVSyncModeSelection(); UpdateDeviceSelection(device);
}); PopulateVSyncModeSelection();
connect(ui->backend, qOverload<int>(&QComboBox::activated), this, });
connect(shader_backend_combobox, qOverload<int>(&QComboBox::activated), this,
[this](int backend) { UpdateShaderBackendSelection(backend); }); [this](int backend) { UpdateShaderBackendSelection(backend); });
connect(ui->bg_button, &QPushButton::clicked, this, [this] { connect(ui->bg_button, &QPushButton::clicked, this, [this] {
@ -116,39 +140,45 @@ ConfigureGraphics::ConfigureGraphics(const Core::System& system_,
UpdateBackgroundColorButton(new_bg_color); UpdateBackgroundColorButton(new_bg_color);
}); });
ui->api->setEnabled(!UISettings::values.has_broken_vulkan && ui->api->isEnabled()); api_combobox->setEnabled(!UISettings::values.has_broken_vulkan && api_combobox->isEnabled());
ui->api_widget->setEnabled( ui->api_widget->setEnabled(
(!UISettings::values.has_broken_vulkan || Settings::IsConfiguringGlobal()) && (!UISettings::values.has_broken_vulkan || Settings::IsConfiguringGlobal()) &&
ui->api_widget->isEnabled()); ui->api_widget->isEnabled());
ui->bg_label->setVisible(Settings::IsConfiguringGlobal());
ui->bg_combobox->setVisible(!Settings::IsConfiguringGlobal());
connect(ui->fsr_sharpening_slider, &QSlider::valueChanged, this, if (Settings::IsConfiguringGlobal()) {
&ConfigureGraphics::SetFSRIndicatorText); ui->bg_widget->setEnabled(Settings::values.bg_red.UsingGlobal());
ui->fsr_sharpening_combobox->setVisible(!Settings::IsConfiguringGlobal()); }
ui->fsr_sharpening_label->setVisible(Settings::IsConfiguringGlobal());
} }
void ConfigureGraphics::PopulateVSyncModeSelection() { void ConfigureGraphics::PopulateVSyncModeSelection() {
const Settings::RendererBackend backend{GetCurrentGraphicsBackend()}; if (!Settings::IsConfiguringGlobal()) {
if (backend == Settings::RendererBackend::Null) {
ui->vsync_mode_combobox->setEnabled(false);
return; return;
} }
ui->vsync_mode_combobox->setEnabled(true);
const Settings::RendererBackend backend{GetCurrentGraphicsBackend()};
if (backend == Settings::RendererBackend::Null) {
vsync_mode_combobox->setEnabled(false);
return;
}
vsync_mode_combobox->setEnabled(true);
const int current_index = //< current selected vsync mode from combobox const int current_index = //< current selected vsync mode from combobox
ui->vsync_mode_combobox->currentIndex(); vsync_mode_combobox->currentIndex();
const auto current_mode = //< current selected vsync mode as a VkPresentModeKHR const auto current_mode = //< current selected vsync mode as a VkPresentModeKHR
current_index == -1 ? VSyncSettingToMode(Settings::values.vsync_mode.GetValue()) current_index == -1 ? VSyncSettingToMode(Settings::values.vsync_mode.GetValue())
: vsync_mode_combobox_enum_map[current_index]; : vsync_mode_combobox_enum_map[current_index];
int index{}; int index{};
const int device{ui->device->currentIndex()}; //< current selected Vulkan device const int device{vulkan_device_combobox->currentIndex()}; //< current selected Vulkan device
if (device == -1) {
// Invalid device
return;
}
const auto& present_modes = //< relevant vector of present modes for the selected device or API const auto& present_modes = //< relevant vector of present modes for the selected device or API
backend == Settings::RendererBackend::Vulkan ? device_present_modes[device] backend == Settings::RendererBackend::Vulkan ? device_present_modes[device]
: default_present_modes; : default_present_modes;
ui->vsync_mode_combobox->clear(); vsync_mode_combobox->clear();
vsync_mode_combobox_enum_map.clear(); vsync_mode_combobox_enum_map.clear();
vsync_mode_combobox_enum_map.reserve(present_modes.size()); vsync_mode_combobox_enum_map.reserve(present_modes.size());
for (const auto present_mode : present_modes) { for (const auto present_mode : present_modes) {
@ -157,10 +187,10 @@ void ConfigureGraphics::PopulateVSyncModeSelection() {
continue; continue;
} }
ui->vsync_mode_combobox->insertItem(index, mode_name); vsync_mode_combobox->insertItem(index, mode_name);
vsync_mode_combobox_enum_map.push_back(present_mode); vsync_mode_combobox_enum_map.push_back(present_mode);
if (present_mode == current_mode) { if (present_mode == current_mode) {
ui->vsync_mode_combobox->setCurrentIndex(index); vsync_mode_combobox->setCurrentIndex(index);
} }
index++; index++;
} }
@ -186,112 +216,124 @@ void ConfigureGraphics::UpdateShaderBackendSelection(int backend) {
ConfigureGraphics::~ConfigureGraphics() = default; ConfigureGraphics::~ConfigureGraphics() = default;
void ConfigureGraphics::SetConfiguration() { void ConfigureGraphics::SetConfiguration() {}
const bool runtime_lock = !system.IsPoweredOn();
ui->api_widget->setEnabled(runtime_lock); void ConfigureGraphics::Setup(const ConfigurationShared::Builder& builder) {
ui->use_asynchronous_gpu_emulation->setEnabled(runtime_lock); QLayout* api_layout = ui->api_widget->layout();
ui->use_disk_shader_cache->setEnabled(runtime_lock); QWidget* api_grid_widget = new QWidget(this);
ui->nvdec_emulation_widget->setEnabled(runtime_lock); QVBoxLayout* api_grid_layout = new QVBoxLayout(api_grid_widget);
ui->resolution_combobox->setEnabled(runtime_lock); api_grid_layout->setContentsMargins(0, 0, 0, 0);
ui->accelerate_astc->setEnabled(runtime_lock); api_layout->addWidget(api_grid_widget);
ui->vsync_mode_layout->setEnabled(runtime_lock ||
Settings::values.renderer_backend.GetValue() ==
Settings::RendererBackend::Vulkan);
ui->use_disk_shader_cache->setChecked(Settings::values.use_disk_shader_cache.GetValue());
ui->use_asynchronous_gpu_emulation->setChecked(
Settings::values.use_asynchronous_gpu_emulation.GetValue());
ui->accelerate_astc->setChecked(Settings::values.accelerate_astc.GetValue());
if (Settings::IsConfiguringGlobal()) { QLayout& graphics_layout = *ui->graphics_widget->layout();
ui->api->setCurrentIndex(static_cast<int>(Settings::values.renderer_backend.GetValue()));
ui->fullscreen_mode_combobox->setCurrentIndex(
static_cast<int>(Settings::values.fullscreen_mode.GetValue()));
ui->nvdec_emulation->setCurrentIndex(
static_cast<int>(Settings::values.nvdec_emulation.GetValue()));
ui->aspect_ratio_combobox->setCurrentIndex(Settings::values.aspect_ratio.GetValue());
ui->resolution_combobox->setCurrentIndex(
static_cast<int>(Settings::values.resolution_setup.GetValue()));
ui->scaling_filter_combobox->setCurrentIndex(
static_cast<int>(Settings::values.scaling_filter.GetValue()));
ui->fsr_sharpening_slider->setValue(Settings::values.fsr_sharpening_slider.GetValue());
ui->anti_aliasing_combobox->setCurrentIndex(
static_cast<int>(Settings::values.anti_aliasing.GetValue()));
} else {
ConfigurationShared::SetPerGameSetting(ui->api, &Settings::values.renderer_backend);
ConfigurationShared::SetHighlight(ui->api_widget,
!Settings::values.renderer_backend.UsingGlobal());
ConfigurationShared::SetPerGameSetting(ui->nvdec_emulation, std::map<u32, QWidget*> hold_graphics;
&Settings::values.nvdec_emulation); std::vector<QWidget*> hold_api;
ConfigurationShared::SetHighlight(ui->nvdec_emulation_widget,
!Settings::values.nvdec_emulation.UsingGlobal());
ConfigurationShared::SetPerGameSetting(ui->fullscreen_mode_combobox, for (const auto setting : Settings::values.linkage.by_category[Settings::Category::Renderer]) {
&Settings::values.fullscreen_mode); ConfigurationShared::Widget* widget = [&]() {
ConfigurationShared::SetHighlight(ui->fullscreen_mode_label, if (setting->Id() == Settings::values.fsr_sharpening_slider.Id()) {
!Settings::values.fullscreen_mode.UsingGlobal()); // FSR needs a reversed slider and a 0.5 multiplier
return builder.BuildWidget(
ConfigurationShared::SetPerGameSetting(ui->aspect_ratio_combobox, setting, apply_funcs, ConfigurationShared::RequestType::ReverseSlider, true,
&Settings::values.aspect_ratio); 0.5f, nullptr, tr("%", "FSR sharpening percentage (e.g. 50%)"));
ConfigurationShared::SetHighlight(ui->ar_label, } else {
!Settings::values.aspect_ratio.UsingGlobal()); return builder.BuildWidget(setting, apply_funcs);
ConfigurationShared::SetPerGameSetting(ui->resolution_combobox,
&Settings::values.resolution_setup);
ConfigurationShared::SetHighlight(ui->resolution_label,
!Settings::values.resolution_setup.UsingGlobal());
ConfigurationShared::SetPerGameSetting(ui->scaling_filter_combobox,
&Settings::values.scaling_filter);
ConfigurationShared::SetHighlight(ui->scaling_filter_label,
!Settings::values.scaling_filter.UsingGlobal());
ConfigurationShared::SetPerGameSetting(ui->anti_aliasing_combobox,
&Settings::values.anti_aliasing);
ConfigurationShared::SetHighlight(ui->anti_aliasing_label,
!Settings::values.anti_aliasing.UsingGlobal());
ui->fsr_sharpening_combobox->setCurrentIndex(
Settings::values.fsr_sharpening_slider.UsingGlobal() ? 0 : 1);
ui->fsr_sharpening_slider->setEnabled(
!Settings::values.fsr_sharpening_slider.UsingGlobal());
ui->fsr_sharpening_value->setEnabled(!Settings::values.fsr_sharpening_slider.UsingGlobal());
ConfigurationShared::SetHighlight(ui->fsr_sharpening_layout,
!Settings::values.fsr_sharpening_slider.UsingGlobal());
ui->fsr_sharpening_slider->setValue(Settings::values.fsr_sharpening_slider.GetValue());
ui->bg_combobox->setCurrentIndex(Settings::values.bg_red.UsingGlobal() ? 0 : 1);
ui->bg_button->setEnabled(!Settings::values.bg_red.UsingGlobal());
ConfigurationShared::SetHighlight(ui->bg_layout, !Settings::values.bg_red.UsingGlobal());
}
UpdateBackgroundColorButton(QColor::fromRgb(Settings::values.bg_red.GetValue(),
Settings::values.bg_green.GetValue(),
Settings::values.bg_blue.GetValue()));
UpdateAPILayout();
PopulateVSyncModeSelection(); //< must happen after UpdateAPILayout
SetFSRIndicatorText(ui->fsr_sharpening_slider->sliderPosition());
// VSync setting needs to be determined after populating the VSync combobox
if (Settings::IsConfiguringGlobal()) {
const auto vsync_mode_setting = Settings::values.vsync_mode.GetValue();
const auto vsync_mode = VSyncSettingToMode(vsync_mode_setting);
int index{};
for (const auto mode : vsync_mode_combobox_enum_map) {
if (mode == vsync_mode) {
break;
} }
index++; }();
if (widget == nullptr) {
continue;
} }
if (static_cast<unsigned long>(index) < vsync_mode_combobox_enum_map.size()) { if (!widget->Valid()) {
ui->vsync_mode_combobox->setCurrentIndex(index); widget->deleteLater();
continue;
}
if (setting->Id() == Settings::values.renderer_backend.Id()) {
// Add the renderer combobox now so it's at the top
api_grid_layout->addWidget(widget);
api_combobox = widget->combobox;
api_restore_global_button = widget->restore_button;
if (!Settings::IsConfiguringGlobal()) {
QObject::connect(api_restore_global_button, &QAbstractButton::clicked,
[this](bool) { UpdateAPILayout(); });
// Detach API's restore button and place it where we want
// Lets us put it on the side, and it will automatically scale if there's a
// second combobox (shader_backend, vulkan_device)
widget->layout()->removeWidget(api_restore_global_button);
api_layout->addWidget(api_restore_global_button);
}
} else if (setting->Id() == Settings::values.vulkan_device.Id()) {
// Keep track of vulkan_device's combobox so we can populate it
hold_api.push_back(widget);
vulkan_device_combobox = widget->combobox;
vulkan_device_widget = widget;
} else if (setting->Id() == Settings::values.shader_backend.Id()) {
// Keep track of shader_backend's combobox so we can populate it
hold_api.push_back(widget);
shader_backend_combobox = widget->combobox;
shader_backend_widget = widget;
} else if (setting->Id() == Settings::values.vsync_mode.Id()) {
// Keep track of vsync_mode's combobox so we can populate it
vsync_mode_combobox = widget->combobox;
hold_graphics.emplace(setting->Id(), widget);
} else {
hold_graphics.emplace(setting->Id(), widget);
} }
} }
}
void ConfigureGraphics::SetFSRIndicatorText(int percentage) { for (const auto& [id, widget] : hold_graphics) {
ui->fsr_sharpening_value->setText( graphics_layout.addWidget(widget);
tr("%1%", "FSR sharpening percentage (e.g. 50%)").arg(100 - (percentage / 2))); }
for (auto widget : hold_api) {
api_grid_layout->addWidget(widget);
}
// Background color is too specific to build into the new system, so we manage it here
// (3 settings, all collected into a single widget with a QColor to manage on top)
if (Settings::IsConfiguringGlobal()) {
apply_funcs.push_back([this](bool powered_on) {
Settings::values.bg_red.SetValue(static_cast<u8>(bg_color.red()));
Settings::values.bg_green.SetValue(static_cast<u8>(bg_color.green()));
Settings::values.bg_blue.SetValue(static_cast<u8>(bg_color.blue()));
});
} else {
QPushButton* bg_restore_button = ConfigurationShared::Widget::CreateRestoreGlobalButton(
Settings::values.bg_red.UsingGlobal(), ui->bg_widget);
ui->bg_widget->layout()->addWidget(bg_restore_button);
QObject::connect(bg_restore_button, &QAbstractButton::clicked,
[bg_restore_button, this](bool) {
const int r = Settings::values.bg_red.GetValue(true);
const int g = Settings::values.bg_green.GetValue(true);
const int b = Settings::values.bg_blue.GetValue(true);
UpdateBackgroundColorButton(QColor::fromRgb(r, g, b));
bg_restore_button->setVisible(false);
bg_restore_button->setEnabled(false);
});
QObject::connect(ui->bg_button, &QAbstractButton::clicked, [bg_restore_button](bool) {
bg_restore_button->setVisible(true);
bg_restore_button->setEnabled(true);
});
apply_funcs.push_back([bg_restore_button, this](bool powered_on) {
const bool using_global = !bg_restore_button->isEnabled();
Settings::values.bg_red.SetGlobal(using_global);
Settings::values.bg_green.SetGlobal(using_global);
Settings::values.bg_blue.SetGlobal(using_global);
if (!using_global) {
Settings::values.bg_red.SetValue(static_cast<u8>(bg_color.red()));
Settings::values.bg_green.SetValue(static_cast<u8>(bg_color.green()));
Settings::values.bg_blue.SetValue(static_cast<u8>(bg_color.blue()));
}
});
}
} }
const QString ConfigureGraphics::TranslateVSyncMode(VkPresentModeKHR mode, const QString ConfigureGraphics::TranslateVSyncMode(VkPresentModeKHR mode,
@ -315,130 +357,48 @@ const QString ConfigureGraphics::TranslateVSyncMode(VkPresentModeKHR mode,
} }
} }
int ConfigureGraphics::FindIndex(u32 enumeration, int value) const {
for (u32 i = 0; i < combobox_translations.at(enumeration).size(); i++) {
if (combobox_translations.at(enumeration)[i].first == static_cast<u32>(value)) {
return i;
}
}
return -1;
}
void ConfigureGraphics::ApplyConfiguration() { void ConfigureGraphics::ApplyConfiguration() {
const auto resolution_setup = static_cast<Settings::ResolutionSetup>( const bool powered_on = system.IsPoweredOn();
ui->resolution_combobox->currentIndex() - for (const auto& func : apply_funcs) {
((Settings::IsConfiguringGlobal()) ? 0 : ConfigurationShared::USE_GLOBAL_OFFSET)); func(powered_on);
}
const auto scaling_filter = static_cast<Settings::ScalingFilter>(
ui->scaling_filter_combobox->currentIndex() -
((Settings::IsConfiguringGlobal()) ? 0 : ConfigurationShared::USE_GLOBAL_OFFSET));
const auto anti_aliasing = static_cast<Settings::AntiAliasing>(
ui->anti_aliasing_combobox->currentIndex() -
((Settings::IsConfiguringGlobal()) ? 0 : ConfigurationShared::USE_GLOBAL_OFFSET));
ConfigurationShared::ApplyPerGameSetting(&Settings::values.fullscreen_mode,
ui->fullscreen_mode_combobox);
ConfigurationShared::ApplyPerGameSetting(&Settings::values.aspect_ratio,
ui->aspect_ratio_combobox);
ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_disk_shader_cache,
ui->use_disk_shader_cache, use_disk_shader_cache);
ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_asynchronous_gpu_emulation,
ui->use_asynchronous_gpu_emulation,
use_asynchronous_gpu_emulation);
ConfigurationShared::ApplyPerGameSetting(&Settings::values.accelerate_astc, ui->accelerate_astc,
accelerate_astc);
if (Settings::IsConfiguringGlobal()) { if (Settings::IsConfiguringGlobal()) {
// Guard if during game and set to game-specific value const auto mode = vsync_mode_combobox_enum_map[vsync_mode_combobox->currentIndex()];
if (Settings::values.renderer_backend.UsingGlobal()) {
Settings::values.renderer_backend.SetValue(GetCurrentGraphicsBackend());
}
if (Settings::values.nvdec_emulation.UsingGlobal()) {
Settings::values.nvdec_emulation.SetValue(GetCurrentNvdecEmulation());
}
if (Settings::values.shader_backend.UsingGlobal()) {
Settings::values.shader_backend.SetValue(shader_backend);
}
if (Settings::values.vulkan_device.UsingGlobal()) {
Settings::values.vulkan_device.SetValue(vulkan_device);
}
if (Settings::values.bg_red.UsingGlobal()) {
Settings::values.bg_red.SetValue(static_cast<u8>(bg_color.red()));
Settings::values.bg_green.SetValue(static_cast<u8>(bg_color.green()));
Settings::values.bg_blue.SetValue(static_cast<u8>(bg_color.blue()));
}
if (Settings::values.resolution_setup.UsingGlobal()) {
Settings::values.resolution_setup.SetValue(resolution_setup);
}
if (Settings::values.scaling_filter.UsingGlobal()) {
Settings::values.scaling_filter.SetValue(scaling_filter);
}
if (Settings::values.anti_aliasing.UsingGlobal()) {
Settings::values.anti_aliasing.SetValue(anti_aliasing);
}
Settings::values.fsr_sharpening_slider.SetValue(ui->fsr_sharpening_slider->value());
const auto mode = vsync_mode_combobox_enum_map[ui->vsync_mode_combobox->currentIndex()];
const auto vsync_mode = PresentModeToSetting(mode); const auto vsync_mode = PresentModeToSetting(mode);
Settings::values.vsync_mode.SetValue(vsync_mode); Settings::values.vsync_mode.SetValue(vsync_mode);
} else { }
if (ui->resolution_combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
Settings::values.resolution_setup.SetGlobal(true);
} else {
Settings::values.resolution_setup.SetGlobal(false);
Settings::values.resolution_setup.SetValue(resolution_setup);
}
if (ui->scaling_filter_combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
Settings::values.scaling_filter.SetGlobal(true);
} else {
Settings::values.scaling_filter.SetGlobal(false);
Settings::values.scaling_filter.SetValue(scaling_filter);
}
if (ui->anti_aliasing_combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
Settings::values.anti_aliasing.SetGlobal(true);
} else {
Settings::values.anti_aliasing.SetGlobal(false);
Settings::values.anti_aliasing.SetValue(anti_aliasing);
}
if (ui->api->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
Settings::values.renderer_backend.SetGlobal(true);
Settings::values.shader_backend.SetGlobal(true);
Settings::values.vulkan_device.SetGlobal(true);
} else {
Settings::values.renderer_backend.SetGlobal(false);
Settings::values.renderer_backend.SetValue(GetCurrentGraphicsBackend());
switch (GetCurrentGraphicsBackend()) {
case Settings::RendererBackend::OpenGL:
case Settings::RendererBackend::Null:
Settings::values.shader_backend.SetGlobal(false);
Settings::values.vulkan_device.SetGlobal(true);
Settings::values.shader_backend.SetValue(shader_backend);
break;
case Settings::RendererBackend::Vulkan:
Settings::values.shader_backend.SetGlobal(true);
Settings::values.vulkan_device.SetGlobal(false);
Settings::values.vulkan_device.SetValue(vulkan_device);
break;
}
}
if (ui->nvdec_emulation->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) { Settings::values.vulkan_device.SetGlobal(true);
Settings::values.nvdec_emulation.SetGlobal(true); Settings::values.shader_backend.SetGlobal(true);
} else { if (Settings::IsConfiguringGlobal() ||
Settings::values.nvdec_emulation.SetGlobal(false); (!Settings::IsConfiguringGlobal() && api_restore_global_button->isEnabled())) {
Settings::values.nvdec_emulation.SetValue(GetCurrentNvdecEmulation()); auto backend = static_cast<Settings::RendererBackend>(
} combobox_translations
.at(Settings::EnumMetadata<
if (ui->bg_combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) { Settings::RendererBackend>::Index())[api_combobox->currentIndex()]
Settings::values.bg_red.SetGlobal(true); .first);
Settings::values.bg_green.SetGlobal(true); switch (backend) {
Settings::values.bg_blue.SetGlobal(true); case Settings::RendererBackend::OpenGL:
} else { Settings::values.shader_backend.SetGlobal(Settings::IsConfiguringGlobal());
Settings::values.bg_red.SetGlobal(false); Settings::values.shader_backend.SetValue(static_cast<Settings::ShaderBackend>(
Settings::values.bg_green.SetGlobal(false); shader_mapping[shader_backend_combobox->currentIndex()].first));
Settings::values.bg_blue.SetGlobal(false); break;
Settings::values.bg_red.SetValue(static_cast<u8>(bg_color.red())); case Settings::RendererBackend::Vulkan:
Settings::values.bg_green.SetValue(static_cast<u8>(bg_color.green())); Settings::values.vulkan_device.SetGlobal(Settings::IsConfiguringGlobal());
Settings::values.bg_blue.SetValue(static_cast<u8>(bg_color.blue())); Settings::values.vulkan_device.SetValue(vulkan_device_combobox->currentIndex());
} break;
case Settings::RendererBackend::Null:
if (ui->fsr_sharpening_combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) { break;
Settings::values.fsr_sharpening_slider.SetGlobal(true);
} else {
Settings::values.fsr_sharpening_slider.SetGlobal(false);
Settings::values.fsr_sharpening_slider.SetValue(ui->fsr_sharpening_slider->value());
} }
} }
} }
@ -466,36 +426,26 @@ void ConfigureGraphics::UpdateBackgroundColorButton(QColor color) {
} }
void ConfigureGraphics::UpdateAPILayout() { void ConfigureGraphics::UpdateAPILayout() {
if (!Settings::IsConfiguringGlobal() && bool runtime_lock = !system.IsPoweredOn();
ui->api->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) { bool need_global = !(Settings::IsConfiguringGlobal() || api_restore_global_button->isEnabled());
vulkan_device = Settings::values.vulkan_device.GetValue(true); vulkan_device = Settings::values.vulkan_device.GetValue(need_global);
shader_backend = Settings::values.shader_backend.GetValue(true); shader_backend = Settings::values.shader_backend.GetValue(need_global);
ui->device_widget->setEnabled(false); vulkan_device_widget->setEnabled(!need_global && runtime_lock);
ui->backend_widget->setEnabled(false); shader_backend_widget->setEnabled(!need_global && runtime_lock);
} else {
vulkan_device = Settings::values.vulkan_device.GetValue();
shader_backend = Settings::values.shader_backend.GetValue();
ui->device_widget->setEnabled(true);
ui->backend_widget->setEnabled(true);
}
switch (GetCurrentGraphicsBackend()) { const auto current_backend = GetCurrentGraphicsBackend();
case Settings::RendererBackend::OpenGL: const bool is_opengl = current_backend == Settings::RendererBackend::OpenGL;
ui->backend->setCurrentIndex(static_cast<u32>(shader_backend)); const bool is_vulkan = current_backend == Settings::RendererBackend::Vulkan;
ui->device_widget->setVisible(false);
ui->backend_widget->setVisible(true); vulkan_device_widget->setVisible(is_vulkan);
break; shader_backend_widget->setVisible(is_opengl);
case Settings::RendererBackend::Vulkan:
if (static_cast<int>(vulkan_device) < ui->device->count()) { if (is_opengl) {
ui->device->setCurrentIndex(vulkan_device); shader_backend_combobox->setCurrentIndex(
} FindIndex(Settings::EnumMetadata<Settings::ShaderBackend>::Index(),
ui->device_widget->setVisible(true); static_cast<int>(shader_backend)));
ui->backend_widget->setVisible(false); } else if (is_vulkan && static_cast<int>(vulkan_device) < vulkan_device_combobox->count()) {
break; vulkan_device_combobox->setCurrentIndex(vulkan_device);
case Settings::RendererBackend::Null:
ui->device_widget->setVisible(false);
ui->backend_widget->setVisible(false);
break;
} }
} }
@ -515,92 +465,11 @@ void ConfigureGraphics::RetrieveVulkanDevices() {
} }
Settings::RendererBackend ConfigureGraphics::GetCurrentGraphicsBackend() const { Settings::RendererBackend ConfigureGraphics::GetCurrentGraphicsBackend() const {
if (Settings::IsConfiguringGlobal()) { if (!Settings::IsConfiguringGlobal() && !api_restore_global_button->isEnabled()) {
return static_cast<Settings::RendererBackend>(ui->api->currentIndex()); return Settings::values.renderer_backend.GetValue(true);
} }
return static_cast<Settings::RendererBackend>(
if (ui->api->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) { combobox_translations.at(Settings::EnumMetadata<Settings::RendererBackend>::Index())
Settings::values.renderer_backend.SetGlobal(true); .at(api_combobox->currentIndex())
return Settings::values.renderer_backend.GetValue(); .first);
}
Settings::values.renderer_backend.SetGlobal(false);
return static_cast<Settings::RendererBackend>(ui->api->currentIndex() -
ConfigurationShared::USE_GLOBAL_OFFSET);
}
Settings::NvdecEmulation ConfigureGraphics::GetCurrentNvdecEmulation() const {
if (Settings::IsConfiguringGlobal()) {
return static_cast<Settings::NvdecEmulation>(ui->nvdec_emulation->currentIndex());
}
if (ui->nvdec_emulation->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
Settings::values.nvdec_emulation.SetGlobal(true);
return Settings::values.nvdec_emulation.GetValue();
}
Settings::values.nvdec_emulation.SetGlobal(false);
return static_cast<Settings::NvdecEmulation>(ui->nvdec_emulation->currentIndex() -
ConfigurationShared::USE_GLOBAL_OFFSET);
}
void ConfigureGraphics::SetupPerGameUI() {
if (Settings::IsConfiguringGlobal()) {
ui->api->setEnabled(Settings::values.renderer_backend.UsingGlobal());
ui->device->setEnabled(Settings::values.renderer_backend.UsingGlobal());
ui->fullscreen_mode_combobox->setEnabled(Settings::values.fullscreen_mode.UsingGlobal());
ui->aspect_ratio_combobox->setEnabled(Settings::values.aspect_ratio.UsingGlobal());
ui->resolution_combobox->setEnabled(Settings::values.resolution_setup.UsingGlobal());
ui->scaling_filter_combobox->setEnabled(Settings::values.scaling_filter.UsingGlobal());
ui->fsr_sharpening_slider->setEnabled(Settings::values.fsr_sharpening_slider.UsingGlobal());
ui->anti_aliasing_combobox->setEnabled(Settings::values.anti_aliasing.UsingGlobal());
ui->use_asynchronous_gpu_emulation->setEnabled(
Settings::values.use_asynchronous_gpu_emulation.UsingGlobal());
ui->nvdec_emulation->setEnabled(Settings::values.nvdec_emulation.UsingGlobal());
ui->accelerate_astc->setEnabled(Settings::values.accelerate_astc.UsingGlobal());
ui->use_disk_shader_cache->setEnabled(Settings::values.use_disk_shader_cache.UsingGlobal());
ui->bg_button->setEnabled(Settings::values.bg_red.UsingGlobal());
ui->fsr_slider_layout->setEnabled(Settings::values.fsr_sharpening_slider.UsingGlobal());
return;
}
connect(ui->bg_combobox, qOverload<int>(&QComboBox::activated), this, [this](int index) {
ui->bg_button->setEnabled(index == 1);
ConfigurationShared::SetHighlight(ui->bg_layout, index == 1);
});
connect(ui->fsr_sharpening_combobox, qOverload<int>(&QComboBox::activated), this,
[this](int index) {
ui->fsr_sharpening_slider->setEnabled(index == 1);
ui->fsr_sharpening_value->setEnabled(index == 1);
ConfigurationShared::SetHighlight(ui->fsr_sharpening_layout, index == 1);
});
ConfigurationShared::SetColoredTristate(
ui->use_disk_shader_cache, Settings::values.use_disk_shader_cache, use_disk_shader_cache);
ConfigurationShared::SetColoredTristate(ui->accelerate_astc, Settings::values.accelerate_astc,
accelerate_astc);
ConfigurationShared::SetColoredTristate(ui->use_asynchronous_gpu_emulation,
Settings::values.use_asynchronous_gpu_emulation,
use_asynchronous_gpu_emulation);
ConfigurationShared::SetColoredComboBox(ui->aspect_ratio_combobox, ui->ar_label,
Settings::values.aspect_ratio.GetValue(true));
ConfigurationShared::SetColoredComboBox(
ui->fullscreen_mode_combobox, ui->fullscreen_mode_label,
static_cast<int>(Settings::values.fullscreen_mode.GetValue(true)));
ConfigurationShared::SetColoredComboBox(
ui->resolution_combobox, ui->resolution_label,
static_cast<int>(Settings::values.resolution_setup.GetValue(true)));
ConfigurationShared::SetColoredComboBox(
ui->scaling_filter_combobox, ui->scaling_filter_label,
static_cast<int>(Settings::values.scaling_filter.GetValue(true)));
ConfigurationShared::SetColoredComboBox(
ui->anti_aliasing_combobox, ui->anti_aliasing_label,
static_cast<int>(Settings::values.anti_aliasing.GetValue(true)));
ConfigurationShared::InsertGlobalItem(
ui->api, static_cast<int>(Settings::values.renderer_backend.GetValue(true)));
ConfigurationShared::InsertGlobalItem(
ui->nvdec_emulation, static_cast<int>(Settings::values.nvdec_emulation.GetValue(true)));
ui->vsync_mode_layout->setVisible(false);
} }

@ -5,6 +5,8 @@
#include <functional> #include <functional>
#include <memory> #include <memory>
#include <type_traits>
#include <typeindex>
#include <vector> #include <vector>
#include <QColor> #include <QColor>
#include <QString> #include <QString>
@ -12,10 +14,14 @@
#include <qobjectdefs.h> #include <qobjectdefs.h>
#include <vulkan/vulkan_core.h> #include <vulkan/vulkan_core.h>
#include "common/common_types.h" #include "common/common_types.h"
#include "configuration/shared_translation.h"
#include "vk_device_info.h" #include "vk_device_info.h"
#include "yuzu/configuration/configuration_shared.h"
class QPushButton;
class QEvent; class QEvent;
class QObject; class QObject;
class QComboBox;
namespace Settings { namespace Settings {
enum class NvdecEmulation : u32; enum class NvdecEmulation : u32;
@ -27,31 +33,33 @@ namespace Core {
class System; class System;
} }
namespace ConfigurationShared {
enum class CheckState;
}
namespace Ui { namespace Ui {
class ConfigureGraphics; class ConfigureGraphics;
} }
class ConfigureGraphics : public QWidget { namespace ConfigurationShared {
Q_OBJECT class Builder;
}
class ConfigureGraphics : public ConfigurationShared::Tab {
public: public:
explicit ConfigureGraphics(const Core::System& system_, explicit ConfigureGraphics(const Core::System& system_,
std::vector<VkDeviceInfo::Record>& records, std::vector<VkDeviceInfo::Record>& records,
const std::function<void()>& expose_compute_option_, const std::function<void()>& expose_compute_option_,
std::shared_ptr<std::vector<ConfigurationShared::Tab*>> group,
const ConfigurationShared::Builder& builder,
QWidget* parent = nullptr); QWidget* parent = nullptr);
~ConfigureGraphics() override; ~ConfigureGraphics() override;
void ApplyConfiguration(); void ApplyConfiguration() override;
void SetConfiguration(); void SetConfiguration() override;
private: private:
void changeEvent(QEvent* event) override; void changeEvent(QEvent* event) override;
void RetranslateUI(); void RetranslateUI();
void Setup(const ConfigurationShared::Builder& builder);
void PopulateVSyncModeSelection(); void PopulateVSyncModeSelection();
void UpdateBackgroundColorButton(QColor color); void UpdateBackgroundColorButton(QColor color);
void UpdateAPILayout(); void UpdateAPILayout();
@ -60,34 +68,40 @@ private:
void RetrieveVulkanDevices(); void RetrieveVulkanDevices();
void SetFSRIndicatorText(int percentage);
/* Turns a Vulkan present mode into a textual string for a UI /* Turns a Vulkan present mode into a textual string for a UI
* (and eventually for a human to read) */ * (and eventually for a human to read) */
const QString TranslateVSyncMode(VkPresentModeKHR mode, const QString TranslateVSyncMode(VkPresentModeKHR mode,
Settings::RendererBackend backend) const; Settings::RendererBackend backend) const;
void SetupPerGameUI();
Settings::RendererBackend GetCurrentGraphicsBackend() const; Settings::RendererBackend GetCurrentGraphicsBackend() const;
Settings::NvdecEmulation GetCurrentNvdecEmulation() const;
int FindIndex(u32 enumeration, int value) const;
std::unique_ptr<Ui::ConfigureGraphics> ui; std::unique_ptr<Ui::ConfigureGraphics> ui;
QColor bg_color; QColor bg_color;
ConfigurationShared::CheckState use_nvdec_emulation; std::vector<std::function<void(bool)>> apply_funcs{};
ConfigurationShared::CheckState accelerate_astc;
ConfigurationShared::CheckState use_disk_shader_cache;
ConfigurationShared::CheckState use_asynchronous_gpu_emulation;
std::vector<VkDeviceInfo::Record>& records; std::vector<VkDeviceInfo::Record>& records;
std::vector<QString> vulkan_devices; std::vector<QString> vulkan_devices;
std::vector<std::vector<VkPresentModeKHR>> device_present_modes; std::vector<std::vector<VkPresentModeKHR>> device_present_modes;
std::vector<VkPresentModeKHR> std::vector<VkPresentModeKHR>
vsync_mode_combobox_enum_map; //< Keeps track of which present mode corresponds to which vsync_mode_combobox_enum_map{}; //< Keeps track of which present mode corresponds to which
// selection in the combobox // selection in the combobox
u32 vulkan_device{}; u32 vulkan_device{};
Settings::ShaderBackend shader_backend{}; Settings::ShaderBackend shader_backend{};
const std::function<void()>& expose_compute_option; const std::function<void()>& expose_compute_option;
const Core::System& system; const Core::System& system;
const ConfigurationShared::ComboboxTranslationMap& combobox_translations;
const std::vector<std::pair<u32, QString>>& shader_mapping;
QPushButton* api_restore_global_button;
QComboBox* vulkan_device_combobox;
QComboBox* api_combobox;
QComboBox* shader_backend_combobox;
QComboBox* vsync_mode_combobox;
QWidget* vulkan_device_widget;
QWidget* api_widget;
QWidget* shader_backend_widget;
}; };

@ -27,7 +27,7 @@
<layout class="QVBoxLayout" name="verticalLayout_3"> <layout class="QVBoxLayout" name="verticalLayout_3">
<item> <item>
<widget class="QWidget" name="api_widget" native="true"> <widget class="QWidget" name="api_widget" native="true">
<layout class="QGridLayout" name="gridLayout"> <layout class="QHBoxLayout" name="horizontalLayout">
<property name="leftMargin"> <property name="leftMargin">
<number>0</number> <number>0</number>
</property> </property>
@ -40,115 +40,6 @@
<property name="bottomMargin"> <property name="bottomMargin">
<number>0</number> <number>0</number>
</property> </property>
<property name="horizontalSpacing">
<number>6</number>
</property>
<item row="4" column="0">
<widget class="QWidget" name="backend_widget" native="true">
<layout class="QHBoxLayout" name="backend_layout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="backend_label">
<property name="text">
<string>Shader Backend:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="backend"/>
</item>
</layout>
</widget>
</item>
<item row="2" column="0">
<widget class="QWidget" name="device_widget" native="true">
<layout class="QHBoxLayout" name="device_layout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="device_label">
<property name="text">
<string>Device:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="device"/>
</item>
</layout>
</widget>
</item>
<item row="0" column="0">
<widget class="QWidget" name="api_layout_2" native="true">
<layout class="QHBoxLayout" name="api_layout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="api_label">
<property name="text">
<string>API:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="api">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<item>
<property name="text">
<string notr="true">OpenGL</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">Vulkan</string>
</property>
</item>
<item>
<property name="text">
<string>None</string>
</property>
</item>
</widget>
</item>
</layout>
</widget>
</item>
</layout> </layout>
</widget> </widget>
</item> </item>
@ -168,29 +59,8 @@
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_4"> <layout class="QVBoxLayout" name="verticalLayout_4">
<item> <item>
<widget class="QCheckBox" name="use_disk_shader_cache"> <widget class="QWidget" name="graphics_widget" native="true">
<property name="text"> <layout class="QVBoxLayout" name="verticalLayout">
<string>Use disk pipeline cache</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="use_asynchronous_gpu_emulation">
<property name="text">
<string>Use asynchronous GPU emulation</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="accelerate_astc">
<property name="text">
<string>Accelerate ASTC texture decoding</string>
</property>
</widget>
</item>
<item>
<widget class="QWidget" name="vsync_mode_layout" native="true">
<layout class="QHBoxLayout" name="horizontalLayout_4">
<property name="leftMargin"> <property name="leftMargin">
<number>0</number> <number>0</number>
</property> </property>
@ -203,32 +73,12 @@
<property name="bottomMargin"> <property name="bottomMargin">
<number>0</number> <number>0</number>
</property> </property>
<item>
<widget class="QLabel" name="vsync_mode_label">
<property name="text">
<string>VSync Mode:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="vsync_mode_combobox">
<property name="toolTip">
<string>FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen refresh rate.
FIFO Relaxed is similar to FIFO but allows tearing as it recovers from a slow down.
Mailbox can have lower latency than FIFO and does not tear but may drop frames.
Immediate (no synchronization) just presents whatever is available and can exhibit tearing.</string>
</property>
<property name="currentText">
<string/>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QWidget" name="nvdec_emulation_widget" native="true"> <widget class="QWidget" name="bg_widget" native="true">
<layout class="QHBoxLayout" name="nvdec_emulation_layout"> <layout class="QHBoxLayout" name="bg_layout">
<property name="leftMargin"> <property name="leftMargin">
<number>0</number> <number>0</number>
</property> </property>
@ -242,535 +92,35 @@ Immediate (no synchronization) just presents whatever is available and can exhib
<number>0</number> <number>0</number>
</property> </property>
<item> <item>
<widget class="QLabel" name="nvdec_emulation_label"> <widget class="QLabel" name="label">
<property name="text"> <property name="sizePolicy">
<string>NVDEC emulation:</string> <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property> </property>
</widget>
</item>
<item>
<widget class="QComboBox" name="nvdec_emulation">
<item>
<property name="text">
<string>No Video Output</string>
</property>
</item>
<item>
<property name="text">
<string>CPU Video Decoding</string>
</property>
</item>
<item>
<property name="text">
<string>GPU Video Decoding (Default)</string>
</property>
</item>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QWidget" name="fullscreen_mode_layout" native="true">
<layout class="QHBoxLayout" name="horizontalLayout_1">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="fullscreen_mode_label">
<property name="text">
<string>Fullscreen Mode:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="fullscreen_mode_combobox">
<item>
<property name="text">
<string>Borderless Windowed</string>
</property>
</item>
<item>
<property name="text">
<string>Exclusive Fullscreen</string>
</property>
</item>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QWidget" name="aspect_ratio_layout" native="true">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="ar_label">
<property name="text">
<string>Aspect Ratio:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="aspect_ratio_combobox">
<item>
<property name="text">
<string>Default (16:9)</string>
</property>
</item>
<item>
<property name="text">
<string>Force 4:3</string>
</property>
</item>
<item>
<property name="text">
<string>Force 21:9</string>
</property>
</item>
<item>
<property name="text">
<string>Force 16:10</string>
</property>
</item>
<item>
<property name="text">
<string>Stretch to Window</string>
</property>
</item>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QWidget" name="resolution_layout" native="true">
<layout class="QHBoxLayout" name="horizontalLayout_5">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="resolution_label">
<property name="text">
<string>Resolution:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="resolution_combobox">
<item>
<property name="text">
<string>0.5X (360p/540p) [EXPERIMENTAL]</string>
</property>
</item>
<item>
<property name="text">
<string>0.75X (540p/810p) [EXPERIMENTAL]</string>
</property>
</item>
<item>
<property name="text">
<string>1X (720p/1080p)</string>
</property>
</item>
<item>
<property name="text">
<string>1.5X (1080p/1620p) [EXPERIMENTAL]</string>
</property>
</item>
<item>
<property name="text">
<string>2X (1440p/2160p)</string>
</property>
</item>
<item>
<property name="text">
<string>3X (2160p/3240p)</string>
</property>
</item>
<item>
<property name="text">
<string>4X (2880p/4320p)</string>
</property>
</item>
<item>
<property name="text">
<string>5X (3600p/5400p)</string>
</property>
</item>
<item>
<property name="text">
<string>6X (4320p/6480p)</string>
</property>
</item>
<item>
<property name="text">
<string>7X (5040p/7560p)</string>
</property>
</item>
<item>
<property name="text">
<string>8X (5760p/8640p)</string>
</property>
</item>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QWidget" name="scaling_filter_layout" native="true">
<layout class="QHBoxLayout" name="horizontalLayout_6">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="scaling_filter_label">
<property name="text">
<string>Window Adapting Filter:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="scaling_filter_combobox">
<item>
<property name="text">
<string>Nearest Neighbor</string>
</property>
</item>
<item>
<property name="text">
<string>Bilinear</string>
</property>
</item>
<item>
<property name="text">
<string>Bicubic</string>
</property>
</item>
<item>
<property name="text">
<string>Gaussian</string>
</property>
</item>
<item>
<property name="text">
<string>ScaleForce</string>
</property>
</item>
<item>
<property name="text">
<string>AMD FidelityFX™ Super Resolution</string>
</property>
</item>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QWidget" name="anti_aliasing_layout" native="true">
<layout class="QHBoxLayout" name="horizontalLayout_7">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="anti_aliasing_label">
<property name="text">
<string>Anti-Aliasing Method:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="anti_aliasing_combobox">
<item>
<property name="text">
<string>None</string>
</property>
</item>
<item>
<property name="text">
<string>FXAA</string>
</property>
</item>
<item>
<property name="text">
<string>SMAA</string>
</property>
</item>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QWidget" name="fsr_sharpening_layout" native="true">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>6</number>
</property>
<property name="sizeConstraint">
<enum>QLayout::SetDefaultConstraint</enum>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<layout class="QHBoxLayout" name="fsr_sharpening_label_group">
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QComboBox" name="fsr_sharpening_combobox">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<item>
<property name="text">
<string>Use global FSR Sharpness</string>
</property>
</item>
<item>
<property name="text">
<string>Set FSR Sharpness</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="QLabel" name="fsr_sharpening_label">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>FSR Sharpness:</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="fsr_slider_layout">
<property name="spacing">
<number>6</number>
</property>
<item>
<widget class="QSlider" name="fsr_sharpening_slider">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="baseSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="maximum">
<number>200</number>
</property>
<property name="sliderPosition">
<number>25</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="invertedAppearance">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="fsr_sharpening_value">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>32</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>100%</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QWidget" name="bg_layout" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<property name="spacing">
<number>6</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QComboBox" name="bg_combobox">
<property name="currentText">
<string>Use global background color</string>
</property>
<property name="currentIndex">
<number>0</number>
</property>
<property name="maxVisibleItems">
<number>10</number>
</property>
<item>
<property name="text">
<string>Use global background color</string>
</property>
</item>
<item>
<property name="text">
<string>Set background color:</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="QLabel" name="bg_label">
<property name="text"> <property name="text">
<string>Background Color:</string> <string>Background Color:</string>
</property> </property>
</widget> </widget>
</item> </item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item> <item>
<widget class="QPushButton" name="bg_button"> <widget class="QPushButton" name="bg_button">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize"> <property name="maximumSize">
<size> <size>
<width>40</width> <width>40</width>
<height>16777215</height> <height>16777215</height>
</size> </size>
</property> </property>
<property name="text">
<string/>
</property>
</widget> </widget>
</item> </item>
</layout> </layout>

@ -1,104 +1,68 @@
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <vector>
#include <QLabel>
#include <qnamespace.h>
#include "common/settings.h" #include "common/settings.h"
#include "core/core.h" #include "core/core.h"
#include "ui_configure_graphics_advanced.h" #include "ui_configure_graphics_advanced.h"
#include "yuzu/configuration/configuration_shared.h" #include "yuzu/configuration/configuration_shared.h"
#include "yuzu/configuration/configure_graphics_advanced.h" #include "yuzu/configuration/configure_graphics_advanced.h"
#include "yuzu/configuration/shared_translation.h"
#include "yuzu/configuration/shared_widget.h"
ConfigureGraphicsAdvanced::ConfigureGraphicsAdvanced(const Core::System& system_, QWidget* parent) ConfigureGraphicsAdvanced::ConfigureGraphicsAdvanced(
: QWidget(parent), ui{std::make_unique<Ui::ConfigureGraphicsAdvanced>()}, system{system_} { const Core::System& system_, std::shared_ptr<std::vector<ConfigurationShared::Tab*>> group_,
const ConfigurationShared::Builder& builder, QWidget* parent)
: Tab(group_, parent), ui{std::make_unique<Ui::ConfigureGraphicsAdvanced>()}, system{system_} {
ui->setupUi(this); ui->setupUi(this);
SetupPerGameUI(); Setup(builder);
SetConfiguration(); SetConfiguration();
ui->enable_compute_pipelines_checkbox->setVisible(false); checkbox_enable_compute_pipelines->setVisible(false);
} }
ConfigureGraphicsAdvanced::~ConfigureGraphicsAdvanced() = default; ConfigureGraphicsAdvanced::~ConfigureGraphicsAdvanced() = default;
void ConfigureGraphicsAdvanced::SetConfiguration() { void ConfigureGraphicsAdvanced::SetConfiguration() {}
const bool runtime_lock = !system.IsPoweredOn();
ui->use_reactive_flushing->setEnabled(runtime_lock);
ui->async_present->setEnabled(runtime_lock);
ui->renderer_force_max_clock->setEnabled(runtime_lock);
ui->async_astc->setEnabled(runtime_lock);
ui->astc_recompression_combobox->setEnabled(runtime_lock);
ui->use_asynchronous_shaders->setEnabled(runtime_lock);
ui->anisotropic_filtering_combobox->setEnabled(runtime_lock);
ui->enable_compute_pipelines_checkbox->setEnabled(runtime_lock);
ui->async_present->setChecked(Settings::values.async_presentation.GetValue()); void ConfigureGraphicsAdvanced::Setup(const ConfigurationShared::Builder& builder) {
ui->renderer_force_max_clock->setChecked(Settings::values.renderer_force_max_clock.GetValue()); auto& layout = *ui->populate_target->layout();
ui->use_reactive_flushing->setChecked(Settings::values.use_reactive_flushing.GetValue()); std::map<u32, QWidget*> hold{}; // A map will sort the data for us
ui->async_astc->setChecked(Settings::values.async_astc.GetValue());
ui->use_asynchronous_shaders->setChecked(Settings::values.use_asynchronous_shaders.GetValue());
ui->use_fast_gpu_time->setChecked(Settings::values.use_fast_gpu_time.GetValue());
ui->use_vulkan_driver_pipeline_cache->setChecked(
Settings::values.use_vulkan_driver_pipeline_cache.GetValue());
ui->enable_compute_pipelines_checkbox->setChecked(
Settings::values.enable_compute_pipelines.GetValue());
ui->use_video_framerate_checkbox->setChecked(Settings::values.use_video_framerate.GetValue());
ui->barrier_feedback_loops_checkbox->setChecked(
Settings::values.barrier_feedback_loops.GetValue());
if (Settings::IsConfiguringGlobal()) { for (auto setting :
ui->gpu_accuracy->setCurrentIndex( Settings::values.linkage.by_category[Settings::Category::RendererAdvanced]) {
static_cast<int>(Settings::values.gpu_accuracy.GetValue())); ConfigurationShared::Widget* widget = builder.BuildWidget(setting, apply_funcs);
ui->anisotropic_filtering_combobox->setCurrentIndex(
Settings::values.max_anisotropy.GetValue()); if (widget == nullptr) {
ui->astc_recompression_combobox->setCurrentIndex( continue;
static_cast<int>(Settings::values.astc_recompression.GetValue())); }
} else { if (!widget->Valid()) {
ConfigurationShared::SetPerGameSetting(ui->gpu_accuracy, &Settings::values.gpu_accuracy); widget->deleteLater();
ConfigurationShared::SetPerGameSetting(ui->anisotropic_filtering_combobox, continue;
&Settings::values.max_anisotropy); }
ConfigurationShared::SetPerGameSetting(ui->astc_recompression_combobox,
&Settings::values.astc_recompression); hold.emplace(setting->Id(), widget);
ConfigurationShared::SetHighlight(ui->label_gpu_accuracy,
!Settings::values.gpu_accuracy.UsingGlobal()); // Keep track of enable_compute_pipelines so we can display it when needed
ConfigurationShared::SetHighlight(ui->af_label, if (setting->Id() == Settings::values.enable_compute_pipelines.Id()) {
!Settings::values.max_anisotropy.UsingGlobal()); checkbox_enable_compute_pipelines = widget;
ConfigurationShared::SetHighlight(ui->label_astc_recompression, }
!Settings::values.astc_recompression.UsingGlobal()); }
for (const auto& [id, widget] : hold) {
layout.addWidget(widget);
} }
} }
void ConfigureGraphicsAdvanced::ApplyConfiguration() { void ConfigureGraphicsAdvanced::ApplyConfiguration() {
ConfigurationShared::ApplyPerGameSetting(&Settings::values.gpu_accuracy, ui->gpu_accuracy); const bool is_powered_on = system.IsPoweredOn();
ConfigurationShared::ApplyPerGameSetting(&Settings::values.async_presentation, for (const auto& func : apply_funcs) {
ui->async_present, async_present); func(is_powered_on);
ConfigurationShared::ApplyPerGameSetting(&Settings::values.renderer_force_max_clock, }
ui->renderer_force_max_clock,
renderer_force_max_clock);
ConfigurationShared::ApplyPerGameSetting(&Settings::values.max_anisotropy,
ui->anisotropic_filtering_combobox);
ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_reactive_flushing,
ui->use_reactive_flushing, use_reactive_flushing);
ConfigurationShared::ApplyPerGameSetting(&Settings::values.async_astc, ui->async_astc,
async_astc);
ConfigurationShared::ApplyPerGameSetting(&Settings::values.astc_recompression,
ui->astc_recompression_combobox);
ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_asynchronous_shaders,
ui->use_asynchronous_shaders,
use_asynchronous_shaders);
ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_fast_gpu_time,
ui->use_fast_gpu_time, use_fast_gpu_time);
ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_vulkan_driver_pipeline_cache,
ui->use_vulkan_driver_pipeline_cache,
use_vulkan_driver_pipeline_cache);
ConfigurationShared::ApplyPerGameSetting(&Settings::values.enable_compute_pipelines,
ui->enable_compute_pipelines_checkbox,
enable_compute_pipelines);
ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_video_framerate,
ui->use_video_framerate_checkbox, use_video_framerate);
ConfigurationShared::ApplyPerGameSetting(&Settings::values.barrier_feedback_loops,
ui->barrier_feedback_loops_checkbox,
barrier_feedback_loops);
} }
void ConfigureGraphicsAdvanced::changeEvent(QEvent* event) { void ConfigureGraphicsAdvanced::changeEvent(QEvent* event) {
@ -113,71 +77,6 @@ void ConfigureGraphicsAdvanced::RetranslateUI() {
ui->retranslateUi(this); ui->retranslateUi(this);
} }
void ConfigureGraphicsAdvanced::SetupPerGameUI() {
// Disable if not global (only happens during game)
if (Settings::IsConfiguringGlobal()) {
ui->gpu_accuracy->setEnabled(Settings::values.gpu_accuracy.UsingGlobal());
ui->async_present->setEnabled(Settings::values.async_presentation.UsingGlobal());
ui->renderer_force_max_clock->setEnabled(
Settings::values.renderer_force_max_clock.UsingGlobal());
ui->use_reactive_flushing->setEnabled(Settings::values.use_reactive_flushing.UsingGlobal());
ui->async_astc->setEnabled(Settings::values.async_astc.UsingGlobal());
ui->astc_recompression_combobox->setEnabled(
Settings::values.astc_recompression.UsingGlobal());
ui->use_asynchronous_shaders->setEnabled(
Settings::values.use_asynchronous_shaders.UsingGlobal());
ui->use_fast_gpu_time->setEnabled(Settings::values.use_fast_gpu_time.UsingGlobal());
ui->use_vulkan_driver_pipeline_cache->setEnabled(
Settings::values.use_vulkan_driver_pipeline_cache.UsingGlobal());
ui->anisotropic_filtering_combobox->setEnabled(
Settings::values.max_anisotropy.UsingGlobal());
ui->enable_compute_pipelines_checkbox->setEnabled(
Settings::values.enable_compute_pipelines.UsingGlobal());
ui->use_video_framerate_checkbox->setEnabled(
Settings::values.use_video_framerate.UsingGlobal());
ui->barrier_feedback_loops_checkbox->setEnabled(
Settings::values.barrier_feedback_loops.UsingGlobal());
return;
}
ConfigurationShared::SetColoredTristate(ui->async_present, Settings::values.async_presentation,
async_present);
ConfigurationShared::SetColoredTristate(ui->renderer_force_max_clock,
Settings::values.renderer_force_max_clock,
renderer_force_max_clock);
ConfigurationShared::SetColoredTristate(
ui->use_reactive_flushing, Settings::values.use_reactive_flushing, use_reactive_flushing);
ConfigurationShared::SetColoredTristate(ui->async_astc, Settings::values.async_astc,
async_astc);
ConfigurationShared::SetColoredTristate(ui->use_asynchronous_shaders,
Settings::values.use_asynchronous_shaders,
use_asynchronous_shaders);
ConfigurationShared::SetColoredTristate(ui->use_fast_gpu_time,
Settings::values.use_fast_gpu_time, use_fast_gpu_time);
ConfigurationShared::SetColoredTristate(ui->use_vulkan_driver_pipeline_cache,
Settings::values.use_vulkan_driver_pipeline_cache,
use_vulkan_driver_pipeline_cache);
ConfigurationShared::SetColoredTristate(ui->enable_compute_pipelines_checkbox,
Settings::values.enable_compute_pipelines,
enable_compute_pipelines);
ConfigurationShared::SetColoredTristate(ui->use_video_framerate_checkbox,
Settings::values.use_video_framerate,
use_video_framerate);
ConfigurationShared::SetColoredTristate(ui->barrier_feedback_loops_checkbox,
Settings::values.barrier_feedback_loops,
barrier_feedback_loops);
ConfigurationShared::SetColoredComboBox(
ui->gpu_accuracy, ui->label_gpu_accuracy,
static_cast<int>(Settings::values.gpu_accuracy.GetValue(true)));
ConfigurationShared::SetColoredComboBox(
ui->anisotropic_filtering_combobox, ui->af_label,
static_cast<int>(Settings::values.max_anisotropy.GetValue(true)));
ConfigurationShared::SetColoredComboBox(
ui->astc_recompression_combobox, ui->label_astc_recompression,
static_cast<int>(Settings::values.astc_recompression.GetValue(true)));
}
void ConfigureGraphicsAdvanced::ExposeComputeOption() { void ConfigureGraphicsAdvanced::ExposeComputeOption() {
ui->enable_compute_pipelines_checkbox->setVisible(true); checkbox_enable_compute_pipelines->setVisible(true);
} }

@ -4,51 +4,44 @@
#pragma once #pragma once
#include <memory> #include <memory>
#include <vector>
#include <QWidget> #include <QWidget>
#include "yuzu/configuration/configuration_shared.h"
namespace Core { namespace Core {
class System; class System;
} }
namespace ConfigurationShared {
enum class CheckState;
}
namespace Ui { namespace Ui {
class ConfigureGraphicsAdvanced; class ConfigureGraphicsAdvanced;
} }
class ConfigureGraphicsAdvanced : public QWidget { namespace ConfigurationShared {
Q_OBJECT class Builder;
}
class ConfigureGraphicsAdvanced : public ConfigurationShared::Tab {
public: public:
explicit ConfigureGraphicsAdvanced(const Core::System& system_, QWidget* parent = nullptr); explicit ConfigureGraphicsAdvanced(
const Core::System& system_, std::shared_ptr<std::vector<ConfigurationShared::Tab*>> group,
const ConfigurationShared::Builder& builder, QWidget* parent = nullptr);
~ConfigureGraphicsAdvanced() override; ~ConfigureGraphicsAdvanced() override;
void ApplyConfiguration(); void ApplyConfiguration() override;
void SetConfiguration(); void SetConfiguration() override;
void ExposeComputeOption(); void ExposeComputeOption();
private: private:
void Setup(const ConfigurationShared::Builder& builder);
void changeEvent(QEvent* event) override; void changeEvent(QEvent* event) override;
void RetranslateUI(); void RetranslateUI();
void SetupPerGameUI();
std::unique_ptr<Ui::ConfigureGraphicsAdvanced> ui; std::unique_ptr<Ui::ConfigureGraphicsAdvanced> ui;
ConfigurationShared::CheckState async_present;
ConfigurationShared::CheckState renderer_force_max_clock;
ConfigurationShared::CheckState use_vsync;
ConfigurationShared::CheckState async_astc;
ConfigurationShared::CheckState use_reactive_flushing;
ConfigurationShared::CheckState use_asynchronous_shaders;
ConfigurationShared::CheckState use_fast_gpu_time;
ConfigurationShared::CheckState use_vulkan_driver_pipeline_cache;
ConfigurationShared::CheckState enable_compute_pipelines;
ConfigurationShared::CheckState use_video_framerate;
ConfigurationShared::CheckState barrier_feedback_loops;
const Core::System& system; const Core::System& system;
std::vector<std::function<void(bool)>> apply_funcs;
QWidget* checkbox_enable_compute_pipelines{};
}; };

@ -26,8 +26,8 @@
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_3"> <layout class="QVBoxLayout" name="verticalLayout_3">
<item> <item>
<widget class="QWidget" name="gpu_accuracy_layout" native="true"> <widget class="QWidget" name="populate_target" native="true">
<layout class="QHBoxLayout" name="horizontalLayout_2"> <layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin"> <property name="leftMargin">
<number>0</number> <number>0</number>
</property> </property>
@ -40,233 +40,6 @@
<property name="bottomMargin"> <property name="bottomMargin">
<number>0</number> <number>0</number>
</property> </property>
<item>
<widget class="QLabel" name="label_gpu_accuracy">
<property name="text">
<string>Accuracy Level:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="gpu_accuracy">
<item>
<property name="text">
<string notr="true">Normal</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">High</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">Extreme(very slow)</string>
</property>
</item>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QWidget" name="astc_recompression_layout" native="true">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="label_astc_recompression">
<property name="text">
<string>ASTC recompression:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="astc_recompression_combobox">
<item>
<property name="text">
<string>Uncompressed (Best quality)</string>
</property>
</item>
<item>
<property name="text">
<string>BC1 (Low quality)</string>
</property>
</item>
<item>
<property name="text">
<string>BC3 (Medium quality)</string>
</property>
</item>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QCheckBox" name="async_present">
<property name="text">
<string>Enable asynchronous presentation (Vulkan only)</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="renderer_force_max_clock">
<property name="toolTip">
<string>Runs work in the background while waiting for graphics commands to keep the GPU from lowering its clock speed.</string>
</property>
<property name="text">
<string>Force maximum clocks (Vulkan only)</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="async_astc">
<property name="toolTip">
<string>Enables asynchronous ASTC texture decoding, which may reduce load time stutter. This feature is experimental.</string>
</property>
<property name="text">
<string>Decode ASTC textures asynchronously (Hack)</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="use_reactive_flushing">
<property name="toolTip">
<string>Uses reactive flushing instead of predictive flushing. Allowing a more accurate syncing of memory.</string>
</property>
<property name="text">
<string>Enable Reactive Flushing</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="use_asynchronous_shaders">
<property name="toolTip">
<string>Enables asynchronous shader compilation, which may reduce shader stutter. This feature is experimental.</string>
</property>
<property name="text">
<string>Use asynchronous shader building (Hack)</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="use_fast_gpu_time">
<property name="toolTip">
<string>Enables Fast GPU Time. This option will force most games to run at their highest native resolution.</string>
</property>
<property name="text">
<string>Use Fast GPU Time (Hack)</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="use_vulkan_driver_pipeline_cache">
<property name="toolTip">
<string>Enables GPU vendor-specific pipeline cache. This option can improve shader loading time significantly in cases where the Vulkan driver does not store pipeline cache files internally.</string>
</property>
<property name="text">
<string>Use Vulkan pipeline cache</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="enable_compute_pipelines_checkbox">
<property name="toolTip">
<string>Enable compute pipelines, required by some games. This setting only exists for Intel proprietary drivers, and may crash if enabled.
Compute pipelines are always enabled on all other drivers.</string>
</property>
<property name="text">
<string>Enable Compute Pipelines (Intel Vulkan only)</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="use_video_framerate_checkbox">
<property name="toolTip">
<string>Run the game at normal speed during video playback, even when the framerate is unlocked.</string>
</property>
<property name="text">
<string>Sync to framerate of video playback</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="barrier_feedback_loops_checkbox">
<property name="toolTip">
<string>Improves rendering of transparency effects in specific games.</string>
</property>
<property name="text">
<string>Barrier feedback loops</string>
</property>
</widget>
</item>
<item>
<widget class="QWidget" name="af_layout" native="true">
<layout class="QHBoxLayout" name="horizontalLayout_1">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="af_label">
<property name="text">
<string>Anisotropic Filtering:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="anisotropic_filtering_combobox">
<item>
<property name="text">
<string>Automatic</string>
</property>
</item>
<item>
<property name="text">
<string>Default</string>
</property>
</item>
<item>
<property name="text">
<string>2x</string>
</property>
</item>
<item>
<property name="text">
<string>4x</string>
</property>
</item>
<item>
<property name="text">
<string>8x</string>
</property>
</item>
<item>
<property name="text">
<string>16x</string>
</property>
</item>
</widget>
</item>
</layout> </layout>
</widget> </widget>
</item> </item>

@ -17,6 +17,7 @@
#include <QTimer> #include <QTimer>
#include "common/fs/fs_util.h" #include "common/fs/fs_util.h"
#include "configuration/shared_widget.h"
#include "core/core.h" #include "core/core.h"
#include "core/file_sys/control_metadata.h" #include "core/file_sys/control_metadata.h"
#include "core/file_sys/patch_manager.h" #include "core/file_sys/patch_manager.h"
@ -24,9 +25,9 @@
#include "core/loader/loader.h" #include "core/loader/loader.h"
#include "ui_configure_per_game.h" #include "ui_configure_per_game.h"
#include "yuzu/configuration/config.h" #include "yuzu/configuration/config.h"
#include "yuzu/configuration/configuration_shared.h"
#include "yuzu/configuration/configure_audio.h" #include "yuzu/configuration/configure_audio.h"
#include "yuzu/configuration/configure_cpu.h" #include "yuzu/configuration/configure_cpu.h"
#include "yuzu/configuration/configure_general.h"
#include "yuzu/configuration/configure_graphics.h" #include "yuzu/configuration/configure_graphics.h"
#include "yuzu/configuration/configure_graphics_advanced.h" #include "yuzu/configuration/configure_graphics_advanced.h"
#include "yuzu/configuration/configure_input_per_game.h" #include "yuzu/configuration/configure_input_per_game.h"
@ -41,26 +42,28 @@ ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id_, const std::st
std::vector<VkDeviceInfo::Record>& vk_device_records, std::vector<VkDeviceInfo::Record>& vk_device_records,
Core::System& system_) Core::System& system_)
: QDialog(parent), : QDialog(parent),
ui(std::make_unique<Ui::ConfigurePerGame>()), title_id{title_id_}, system{system_} { ui(std::make_unique<Ui::ConfigurePerGame>()), title_id{title_id_}, system{system_},
builder{std::make_unique<ConfigurationShared::Builder>(this, !system_.IsPoweredOn())},
tab_group{std::make_shared<std::vector<ConfigurationShared::Tab*>>()} {
const auto file_path = std::filesystem::path(Common::FS::ToU8String(file_name)); const auto file_path = std::filesystem::path(Common::FS::ToU8String(file_name));
const auto config_file_name = title_id == 0 ? Common::FS::PathToUTF8String(file_path.filename()) const auto config_file_name = title_id == 0 ? Common::FS::PathToUTF8String(file_path.filename())
: fmt::format("{:016X}", title_id); : fmt::format("{:016X}", title_id);
game_config = std::make_unique<Config>(config_file_name, Config::ConfigType::PerGameConfig); game_config = std::make_unique<Config>(config_file_name, Config::ConfigType::PerGameConfig);
addons_tab = std::make_unique<ConfigurePerGameAddons>(system_, this); addons_tab = std::make_unique<ConfigurePerGameAddons>(system_, this);
audio_tab = std::make_unique<ConfigureAudio>(system_, this); audio_tab = std::make_unique<ConfigureAudio>(system_, tab_group, *builder, this);
cpu_tab = std::make_unique<ConfigureCpu>(system_, this); cpu_tab = std::make_unique<ConfigureCpu>(system_, tab_group, *builder, this);
general_tab = std::make_unique<ConfigureGeneral>(system_, this); graphics_advanced_tab =
graphics_advanced_tab = std::make_unique<ConfigureGraphicsAdvanced>(system_, this); std::make_unique<ConfigureGraphicsAdvanced>(system_, tab_group, *builder, this);
graphics_tab = std::make_unique<ConfigureGraphics>( graphics_tab = std::make_unique<ConfigureGraphics>(
system_, vk_device_records, [&]() { graphics_advanced_tab->ExposeComputeOption(); }, this); system_, vk_device_records, [&]() { graphics_advanced_tab->ExposeComputeOption(); },
tab_group, *builder, this);
input_tab = std::make_unique<ConfigureInputPerGame>(system_, game_config.get(), this); input_tab = std::make_unique<ConfigureInputPerGame>(system_, game_config.get(), this);
system_tab = std::make_unique<ConfigureSystem>(system_, this); system_tab = std::make_unique<ConfigureSystem>(system_, tab_group, *builder, this);
ui->setupUi(this); ui->setupUi(this);
ui->tabWidget->addTab(addons_tab.get(), tr("Add-Ons")); ui->tabWidget->addTab(addons_tab.get(), tr("Add-Ons"));
ui->tabWidget->addTab(general_tab.get(), tr("General"));
ui->tabWidget->addTab(system_tab.get(), tr("System")); ui->tabWidget->addTab(system_tab.get(), tr("System"));
ui->tabWidget->addTab(cpu_tab.get(), tr("CPU")); ui->tabWidget->addTab(cpu_tab.get(), tr("CPU"));
ui->tabWidget->addTab(graphics_tab.get(), tr("Graphics")); ui->tabWidget->addTab(graphics_tab.get(), tr("Graphics"));
@ -88,13 +91,10 @@ ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id_, const std::st
ConfigurePerGame::~ConfigurePerGame() = default; ConfigurePerGame::~ConfigurePerGame() = default;
void ConfigurePerGame::ApplyConfiguration() { void ConfigurePerGame::ApplyConfiguration() {
for (const auto tab : *tab_group) {
tab->ApplyConfiguration();
}
addons_tab->ApplyConfiguration(); addons_tab->ApplyConfiguration();
general_tab->ApplyConfiguration();
cpu_tab->ApplyConfiguration();
system_tab->ApplyConfiguration();
graphics_tab->ApplyConfiguration();
graphics_advanced_tab->ApplyConfiguration();
audio_tab->ApplyConfiguration();
input_tab->ApplyConfiguration(); input_tab->ApplyConfiguration();
system.ApplySettings(); system.ApplySettings();

@ -10,9 +10,12 @@
#include <QDialog> #include <QDialog>
#include <QList> #include <QList>
#include "configuration/shared_widget.h"
#include "core/file_sys/vfs_types.h" #include "core/file_sys/vfs_types.h"
#include "vk_device_info.h" #include "vk_device_info.h"
#include "yuzu/configuration/config.h" #include "yuzu/configuration/config.h"
#include "yuzu/configuration/configuration_shared.h"
#include "yuzu/configuration/shared_translation.h"
namespace Core { namespace Core {
class System; class System;
@ -25,7 +28,6 @@ class InputSubsystem;
class ConfigurePerGameAddons; class ConfigurePerGameAddons;
class ConfigureAudio; class ConfigureAudio;
class ConfigureCpu; class ConfigureCpu;
class ConfigureGeneral;
class ConfigureGraphics; class ConfigureGraphics;
class ConfigureGraphicsAdvanced; class ConfigureGraphicsAdvanced;
class ConfigureInputPerGame; class ConfigureInputPerGame;
@ -73,11 +75,12 @@ private:
std::unique_ptr<Config> game_config; std::unique_ptr<Config> game_config;
Core::System& system; Core::System& system;
std::unique_ptr<ConfigurationShared::Builder> builder;
std::shared_ptr<std::vector<ConfigurationShared::Tab*>> tab_group;
std::unique_ptr<ConfigurePerGameAddons> addons_tab; std::unique_ptr<ConfigurePerGameAddons> addons_tab;
std::unique_ptr<ConfigureAudio> audio_tab; std::unique_ptr<ConfigureAudio> audio_tab;
std::unique_ptr<ConfigureCpu> cpu_tab; std::unique_ptr<ConfigureCpu> cpu_tab;
std::unique_ptr<ConfigureGeneral> general_tab;
std::unique_ptr<ConfigureGraphicsAdvanced> graphics_advanced_tab; std::unique_ptr<ConfigureGraphicsAdvanced> graphics_advanced_tab;
std::unique_ptr<ConfigureGraphics> graphics_tab; std::unique_ptr<ConfigureGraphics> graphics_tab;
std::unique_ptr<ConfigureInputPerGame> input_tab; std::unique_ptr<ConfigureInputPerGame> input_tab;

@ -2,6 +2,14 @@
<ui version="4.0"> <ui version="4.0">
<class>ConfigurePerGame</class> <class>ConfigurePerGame</class>
<widget class="QDialog" name="ConfigurePerGame"> <widget class="QDialog" name="ConfigurePerGame">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>900</width>
<height>607</height>
</rect>
</property>
<property name="minimumSize"> <property name="minimumSize">
<size> <size>
<width>900</width> <width>900</width>
@ -225,20 +233,31 @@
</layout> </layout>
</item> </item>
<item> <item>
<widget class="QDialogButtonBox" name="buttonBox"> <layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="sizePolicy"> <item>
<sizepolicy hsizetype="Preferred" vsizetype="Preferred"> <widget class="QLabel" name="label_8">
<horstretch>0</horstretch> <property name="text">
<verstretch>0</verstretch> <string>Some settings are only available when a game is not running.</string>
</sizepolicy> </property>
</property> </widget>
<property name="orientation"> </item>
<enum>Qt::Horizontal</enum> <item>
</property> <widget class="QDialogButtonBox" name="buttonBox">
<property name="standardButtons"> <property name="sizePolicy">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
</property> <horstretch>0</horstretch>
</widget> <verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</item> </item>
</layout> </layout>
</widget> </widget>

@ -3,16 +3,23 @@
#include <chrono> #include <chrono>
#include <optional> #include <optional>
#include <vector>
#include <QCheckBox>
#include <QComboBox>
#include <QDateTimeEdit>
#include <QFileDialog> #include <QFileDialog>
#include <QGraphicsItem> #include <QGraphicsItem>
#include <QLineEdit>
#include <QMessageBox> #include <QMessageBox>
#include "common/settings.h" #include "common/settings.h"
#include "core/core.h" #include "core/core.h"
#include "core/hle/service/time/time_manager.h" #include "core/hle/service/time/time_manager.h"
#include "ui_configure_system.h" #include "ui_configure_system.h"
#include "yuzu/configuration/config.h"
#include "yuzu/configuration/configuration_shared.h" #include "yuzu/configuration/configuration_shared.h"
#include "yuzu/configuration/configure_system.h" #include "yuzu/configuration/configure_system.h"
#include "yuzu/configuration/shared_widget.h"
constexpr std::array<u32, 7> LOCALE_BLOCKLIST{ constexpr std::array<u32, 7> LOCALE_BLOCKLIST{
// pzzefezrpnkzeidfej // pzzefezrpnkzeidfej
@ -37,44 +44,32 @@ static bool IsValidLocale(u32 region_index, u32 language_index) {
return ((LOCALE_BLOCKLIST.at(region_index) >> language_index) & 1) == 0; return ((LOCALE_BLOCKLIST.at(region_index) >> language_index) & 1) == 0;
} }
ConfigureSystem::ConfigureSystem(Core::System& system_, QWidget* parent) ConfigureSystem::ConfigureSystem(Core::System& system_,
: QWidget(parent), ui{std::make_unique<Ui::ConfigureSystem>()}, system{system_} { std::shared_ptr<std::vector<ConfigurationShared::Tab*>> group_,
const ConfigurationShared::Builder& builder, QWidget* parent)
: Tab(group_, parent), ui{std::make_unique<Ui::ConfigureSystem>()}, system{system_} {
ui->setupUi(this); ui->setupUi(this);
connect(ui->rng_seed_checkbox, &QCheckBox::stateChanged, this, [this](int state) { Setup(builder);
ui->rng_seed_edit->setEnabled(state == Qt::Checked);
if (state != Qt::Checked) {
ui->rng_seed_edit->setText(QStringLiteral("00000000"));
}
});
connect(ui->custom_rtc_checkbox, &QCheckBox::stateChanged, this, [this](int state) { const auto locale_check = [this]() {
ui->custom_rtc_edit->setEnabled(state == Qt::Checked); const auto region_index = combo_region->currentIndex();
if (state != Qt::Checked) { const auto language_index = combo_language->currentIndex();
ui->custom_rtc_edit->setDateTime(QDateTime::currentDateTime());
}
});
const auto locale_check = [this](int index) {
const auto region_index = ConfigurationShared::GetComboboxIndex(
Settings::values.region_index.GetValue(true), ui->combo_region);
const auto language_index = ConfigurationShared::GetComboboxIndex(
Settings::values.language_index.GetValue(true), ui->combo_language);
const bool valid_locale = IsValidLocale(region_index, language_index); const bool valid_locale = IsValidLocale(region_index, language_index);
ui->label_warn_invalid_locale->setVisible(!valid_locale); ui->label_warn_invalid_locale->setVisible(!valid_locale);
if (!valid_locale) { if (!valid_locale) {
ui->label_warn_invalid_locale->setText( ui->label_warn_invalid_locale->setText(
tr("Warning: \"%1\" is not a valid language for region \"%2\"") tr("Warning: \"%1\" is not a valid language for region \"%2\"")
.arg(ui->combo_language->currentText()) .arg(combo_language->currentText())
.arg(ui->combo_region->currentText())); .arg(combo_region->currentText()));
} }
}; };
connect(ui->combo_language, qOverload<int>(&QComboBox::currentIndexChanged), this, connect(combo_language, qOverload<int>(&QComboBox::currentIndexChanged), this, locale_check);
locale_check); connect(combo_region, qOverload<int>(&QComboBox::currentIndexChanged), this, locale_check);
connect(ui->combo_region, qOverload<int>(&QComboBox::currentIndexChanged), this, locale_check);
SetupPerGameUI(); ui->label_warn_invalid_locale->setVisible(false);
locale_check();
SetConfiguration(); SetConfiguration();
} }
@ -93,137 +88,66 @@ void ConfigureSystem::RetranslateUI() {
ui->retranslateUi(this); ui->retranslateUi(this);
} }
void ConfigureSystem::SetConfiguration() { void ConfigureSystem::Setup(const ConfigurationShared::Builder& builder) {
enabled = !system.IsPoweredOn(); auto& core_layout = *ui->core_widget->layout();
const auto rng_seed = auto& system_layout = *ui->system_widget->layout();
QStringLiteral("%1")
.arg(Settings::values.rng_seed.GetValue().value_or(0), 8, 16, QLatin1Char{'0'})
.toUpper();
const auto rtc_time = Settings::values.custom_rtc.value_or(QDateTime::currentSecsSinceEpoch());
ui->rng_seed_checkbox->setChecked(Settings::values.rng_seed.GetValue().has_value()); std::map<u32, QWidget*> core_hold{};
ui->rng_seed_edit->setEnabled(Settings::values.rng_seed.GetValue().has_value() && std::map<u32, QWidget*> system_hold{};
Settings::values.rng_seed.UsingGlobal());
ui->rng_seed_edit->setText(rng_seed);
ui->custom_rtc_checkbox->setChecked(Settings::values.custom_rtc.has_value()); std::vector<Settings::BasicSetting*> settings;
ui->custom_rtc_edit->setEnabled(Settings::values.custom_rtc.has_value()); auto push = [&settings](auto& list) {
ui->custom_rtc_edit->setDateTime(QDateTime::fromSecsSinceEpoch(rtc_time)); for (auto setting : list) {
ui->device_name_edit->setText( settings.push_back(setting);
QString::fromUtf8(Settings::values.device_name.GetValue().c_str())); }
ui->use_unsafe_extended_memory_layout->setEnabled(enabled); };
ui->use_unsafe_extended_memory_layout->setChecked(
Settings::values.use_unsafe_extended_memory_layout.GetValue());
if (Settings::IsConfiguringGlobal()) { push(Settings::values.linkage.by_category[Settings::Category::Core]);
ui->combo_language->setCurrentIndex(Settings::values.language_index.GetValue()); push(Settings::values.linkage.by_category[Settings::Category::System]);
ui->combo_region->setCurrentIndex(Settings::values.region_index.GetValue());
ui->combo_time_zone->setCurrentIndex(Settings::values.time_zone_index.GetValue());
} else {
ConfigurationShared::SetPerGameSetting(ui->combo_language,
&Settings::values.language_index);
ConfigurationShared::SetPerGameSetting(ui->combo_region, &Settings::values.region_index);
ConfigurationShared::SetPerGameSetting(ui->combo_time_zone,
&Settings::values.time_zone_index);
ConfigurationShared::SetHighlight(ui->label_language, for (auto setting : settings) {
!Settings::values.language_index.UsingGlobal()); ConfigurationShared::Widget* widget = builder.BuildWidget(setting, apply_funcs);
ConfigurationShared::SetHighlight(ui->label_region,
!Settings::values.region_index.UsingGlobal()); if (widget == nullptr) {
ConfigurationShared::SetHighlight(ui->label_timezone, continue;
!Settings::values.time_zone_index.UsingGlobal()); }
if (!widget->Valid()) {
widget->deleteLater();
continue;
}
if (setting->Id() == Settings::values.region_index.Id()) {
// Keep track of the region_index (and langauge_index) combobox to validate the selected
// settings
combo_region = widget->combobox;
} else if (setting->Id() == Settings::values.language_index.Id()) {
combo_language = widget->combobox;
}
switch (setting->GetCategory()) {
case Settings::Category::Core:
core_hold.emplace(setting->Id(), widget);
break;
case Settings::Category::System:
system_hold.emplace(setting->Id(), widget);
break;
default:
widget->deleteLater();
}
}
for (const auto& [label, widget] : core_hold) {
core_layout.addWidget(widget);
}
for (const auto& [id, widget] : system_hold) {
system_layout.addWidget(widget);
} }
} }
void ConfigureSystem::ReadSystemSettings() {} void ConfigureSystem::SetConfiguration() {}
void ConfigureSystem::ApplyConfiguration() { void ConfigureSystem::ApplyConfiguration() {
// Allow setting custom RTC even if system is powered on, const bool powered_on = system.IsPoweredOn();
// to allow in-game time to be fast forwarded for (const auto& func : apply_funcs) {
if (Settings::IsConfiguringGlobal()) { func(powered_on);
if (ui->custom_rtc_checkbox->isChecked()) {
Settings::values.custom_rtc = ui->custom_rtc_edit->dateTime().toSecsSinceEpoch();
if (system.IsPoweredOn()) {
const s64 posix_time{*Settings::values.custom_rtc};
system.GetTimeManager().UpdateLocalSystemClockTime(posix_time);
}
} else {
Settings::values.custom_rtc = std::nullopt;
}
}
Settings::values.device_name = ui->device_name_edit->text().toStdString();
if (!enabled) {
return;
}
ConfigurationShared::ApplyPerGameSetting(&Settings::values.language_index, ui->combo_language);
ConfigurationShared::ApplyPerGameSetting(&Settings::values.region_index, ui->combo_region);
ConfigurationShared::ApplyPerGameSetting(&Settings::values.time_zone_index,
ui->combo_time_zone);
ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_unsafe_extended_memory_layout,
ui->use_unsafe_extended_memory_layout,
use_unsafe_extended_memory_layout);
if (Settings::IsConfiguringGlobal()) {
// Guard if during game and set to game-specific value
if (Settings::values.rng_seed.UsingGlobal()) {
if (ui->rng_seed_checkbox->isChecked()) {
Settings::values.rng_seed.SetValue(ui->rng_seed_edit->text().toUInt(nullptr, 16));
} else {
Settings::values.rng_seed.SetValue(std::nullopt);
}
}
} else {
switch (use_rng_seed) {
case ConfigurationShared::CheckState::On:
case ConfigurationShared::CheckState::Off:
Settings::values.rng_seed.SetGlobal(false);
if (ui->rng_seed_checkbox->isChecked()) {
Settings::values.rng_seed.SetValue(ui->rng_seed_edit->text().toUInt(nullptr, 16));
} else {
Settings::values.rng_seed.SetValue(std::nullopt);
}
break;
case ConfigurationShared::CheckState::Global:
Settings::values.rng_seed.SetGlobal(false);
Settings::values.rng_seed.SetValue(std::nullopt);
Settings::values.rng_seed.SetGlobal(true);
break;
case ConfigurationShared::CheckState::Count:
break;
}
} }
} }
void ConfigureSystem::SetupPerGameUI() {
if (Settings::IsConfiguringGlobal()) {
ui->combo_language->setEnabled(Settings::values.language_index.UsingGlobal());
ui->combo_region->setEnabled(Settings::values.region_index.UsingGlobal());
ui->combo_time_zone->setEnabled(Settings::values.time_zone_index.UsingGlobal());
ui->rng_seed_checkbox->setEnabled(Settings::values.rng_seed.UsingGlobal());
ui->rng_seed_edit->setEnabled(Settings::values.rng_seed.UsingGlobal());
return;
}
ConfigurationShared::SetColoredComboBox(ui->combo_language, ui->label_language,
Settings::values.language_index.GetValue(true));
ConfigurationShared::SetColoredComboBox(ui->combo_region, ui->label_region,
Settings::values.region_index.GetValue(true));
ConfigurationShared::SetColoredComboBox(ui->combo_time_zone, ui->label_timezone,
Settings::values.time_zone_index.GetValue(true));
ConfigurationShared::SetColoredTristate(
ui->rng_seed_checkbox, Settings::values.rng_seed.UsingGlobal(),
Settings::values.rng_seed.GetValue().has_value(),
Settings::values.rng_seed.GetValue(true).has_value(), use_rng_seed);
ConfigurationShared::SetColoredTristate(ui->use_unsafe_extended_memory_layout,
Settings::values.use_unsafe_extended_memory_layout,
use_unsafe_extended_memory_layout);
ui->custom_rtc_checkbox->setVisible(false);
ui->custom_rtc_edit->setVisible(false);
}

@ -3,45 +3,53 @@
#pragma once #pragma once
#include <functional>
#include <memory> #include <memory>
#include <vector>
#include <QWidget> #include <QWidget>
#include "yuzu/configuration/configuration_shared.h"
class QCheckBox;
class QLineEdit;
class QComboBox;
class QDateTimeEdit;
namespace Core { namespace Core {
class System; class System;
} }
namespace ConfigurationShared {
enum class CheckState;
}
namespace Ui { namespace Ui {
class ConfigureSystem; class ConfigureSystem;
} }
class ConfigureSystem : public QWidget { namespace ConfigurationShared {
Q_OBJECT class Builder;
}
class ConfigureSystem : public ConfigurationShared::Tab {
public: public:
explicit ConfigureSystem(Core::System& system_, QWidget* parent = nullptr); explicit ConfigureSystem(Core::System& system_,
std::shared_ptr<std::vector<ConfigurationShared::Tab*>> group,
const ConfigurationShared::Builder& builder,
QWidget* parent = nullptr);
~ConfigureSystem() override; ~ConfigureSystem() override;
void ApplyConfiguration(); void ApplyConfiguration() override;
void SetConfiguration(); void SetConfiguration() override;
private: private:
void changeEvent(QEvent* event) override; void changeEvent(QEvent* event) override;
void RetranslateUI(); void RetranslateUI();
void ReadSystemSettings(); void Setup(const ConfigurationShared::Builder& builder);
void SetupPerGameUI(); std::vector<std::function<void(bool)>> apply_funcs{};
std::unique_ptr<Ui::ConfigureSystem> ui; std::unique_ptr<Ui::ConfigureSystem> ui;
bool enabled = false; bool enabled = false;
ConfigurationShared::CheckState use_rng_seed;
ConfigurationShared::CheckState use_unsafe_extended_memory_layout;
Core::System& system; Core::System& system;
QComboBox* combo_region;
QComboBox* combo_language;
}; };

@ -6,7 +6,7 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>366</width> <width>605</width>
<height>483</height> <height>483</height>
</rect> </rect>
</property> </property>
@ -22,470 +22,63 @@
<item> <item>
<widget class="QGroupBox" name="group_system_settings"> <widget class="QGroupBox" name="group_system_settings">
<property name="title"> <property name="title">
<string>System Settings</string> <string>System</string>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_2"> <layout class="QVBoxLayout" name="verticalLayout_2">
<item> <item>
<layout class="QGridLayout" name="gridLayout_2"> <widget class="QWidget" name="system_widget" native="true">
<item row="1" column="0"> <layout class="QVBoxLayout" name="verticalLayout_3">
<widget class="QLabel" name="label_region"> <property name="leftMargin">
<property name="text"> <number>0</number>
<string>Region:</string> </property>
</property> <property name="topMargin">
</widget> <number>0</number>
</item> </property>
<item row="2" column="1"> <property name="rightMargin">
<widget class="QComboBox" name="combo_time_zone"> <number>0</number>
<item> </property>
<property name="text"> <property name="bottomMargin">
<string>Auto</string> <number>0</number>
</property> </property>
</item> </layout>
<item> </widget>
<property name="text"> </item>
<string>Default</string> <item>
</property> <widget class="QLabel" name="label_warn_invalid_locale">
</item> <property name="text">
<item> <string/>
<property name="text"> </property>
<string>CET</string> <property name="wordWrap">
</property> <bool>true</bool>
</item> </property>
<item> </widget>
<property name="text"> </item>
<string>CST6CDT</string> </layout>
</property> </widget>
</item> </item>
<item> <item>
<property name="text"> <widget class="QGroupBox" name="groupBox">
<string>Cuba</string> <property name="title">
</property> <string>Core</string>
</item> </property>
<item> <layout class="QVBoxLayout" name="verticalLayout_6">
<property name="text"> <item>
<string>EET</string> <widget class="QWidget" name="core_widget" native="true">
</property> <layout class="QVBoxLayout" name="verticalLayout_5">
</item> <property name="leftMargin">
<item> <number>0</number>
<property name="text"> </property>
<string>Egypt</string> <property name="topMargin">
</property> <number>0</number>
</item> </property>
<item> <property name="rightMargin">
<property name="text"> <number>0</number>
<string>Eire</string> </property>
</property> <property name="bottomMargin">
</item> <number>0</number>
<item> </property>
<property name="text"> </layout>
<string>EST</string> </widget>
</property>
</item>
<item>
<property name="text">
<string>EST5EDT</string>
</property>
</item>
<item>
<property name="text">
<string>GB</string>
</property>
</item>
<item>
<property name="text">
<string>GB-Eire</string>
</property>
</item>
<item>
<property name="text">
<string>GMT</string>
</property>
</item>
<item>
<property name="text">
<string>GMT+0</string>
</property>
</item>
<item>
<property name="text">
<string>GMT-0</string>
</property>
</item>
<item>
<property name="text">
<string>GMT0</string>
</property>
</item>
<item>
<property name="text">
<string>Greenwich</string>
</property>
</item>
<item>
<property name="text">
<string>Hongkong</string>
</property>
</item>
<item>
<property name="text">
<string>HST</string>
</property>
</item>
<item>
<property name="text">
<string>Iceland</string>
</property>
</item>
<item>
<property name="text">
<string>Iran</string>
</property>
</item>
<item>
<property name="text">
<string>Israel</string>
</property>
</item>
<item>
<property name="text">
<string>Jamaica</string>
</property>
</item>
<item>
<property name="text">
<string>Japan</string>
</property>
</item>
<item>
<property name="text">
<string>Kwajalein</string>
</property>
</item>
<item>
<property name="text">
<string>Libya</string>
</property>
</item>
<item>
<property name="text">
<string>MET</string>
</property>
</item>
<item>
<property name="text">
<string>MST</string>
</property>
</item>
<item>
<property name="text">
<string>MST7MDT</string>
</property>
</item>
<item>
<property name="text">
<string>Navajo</string>
</property>
</item>
<item>
<property name="text">
<string>NZ</string>
</property>
</item>
<item>
<property name="text">
<string>NZ-CHAT</string>
</property>
</item>
<item>
<property name="text">
<string>Poland</string>
</property>
</item>
<item>
<property name="text">
<string>Portugal</string>
</property>
</item>
<item>
<property name="text">
<string>PRC</string>
</property>
</item>
<item>
<property name="text">
<string>PST8PDT</string>
</property>
</item>
<item>
<property name="text">
<string>ROC</string>
</property>
</item>
<item>
<property name="text">
<string>ROK</string>
</property>
</item>
<item>
<property name="text">
<string>Singapore</string>
</property>
</item>
<item>
<property name="text">
<string>Turkey</string>
</property>
</item>
<item>
<property name="text">
<string>UCT</string>
</property>
</item>
<item>
<property name="text">
<string>Universal</string>
</property>
</item>
<item>
<property name="text">
<string>UTC</string>
</property>
</item>
<item>
<property name="text">
<string>W-SU</string>
</property>
</item>
<item>
<property name="text">
<string>WET</string>
</property>
</item>
<item>
<property name="text">
<string>Zulu</string>
</property>
</item>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="combo_region">
<item>
<property name="text">
<string>Japan</string>
</property>
</item>
<item>
<property name="text">
<string>USA</string>
</property>
</item>
<item>
<property name="text">
<string>Europe</string>
</property>
</item>
<item>
<property name="text">
<string>Australia</string>
</property>
</item>
<item>
<property name="text">
<string>China</string>
</property>
</item>
<item>
<property name="text">
<string>Korea</string>
</property>
</item>
<item>
<property name="text">
<string>Taiwan</string>
</property>
</item>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_timezone">
<property name="text">
<string>Time Zone:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="combo_language">
<property name="toolTip">
<string>Note: this can be overridden when region setting is auto-select</string>
</property>
<item>
<property name="text">
<string>Japanese (日本語)</string>
</property>
</item>
<item>
<property name="text">
<string>American English</string>
</property>
</item>
<item>
<property name="text">
<string>French (français)</string>
</property>
</item>
<item>
<property name="text">
<string>German (Deutsch)</string>
</property>
</item>
<item>
<property name="text">
<string>Italian (italiano)</string>
</property>
</item>
<item>
<property name="text">
<string>Spanish (español)</string>
</property>
</item>
<item>
<property name="text">
<string>Chinese</string>
</property>
</item>
<item>
<property name="text">
<string>Korean (한국어)</string>
</property>
</item>
<item>
<property name="text">
<string>Dutch (Nederlands)</string>
</property>
</item>
<item>
<property name="text">
<string>Portuguese (português)</string>
</property>
</item>
<item>
<property name="text">
<string>Russian (Русский)</string>
</property>
</item>
<item>
<property name="text">
<string>Taiwanese</string>
</property>
</item>
<item>
<property name="text">
<string>British English</string>
</property>
</item>
<item>
<property name="text">
<string>Canadian French</string>
</property>
</item>
<item>
<property name="text">
<string>Latin American Spanish</string>
</property>
</item>
<item>
<property name="text">
<string>Simplified Chinese</string>
</property>
</item>
<item>
<property name="text">
<string>Traditional Chinese (正體中文)</string>
</property>
</item>
<item>
<property name="text">
<string>Brazilian Portuguese (português do Brasil)</string>
</property>
</item>
</widget>
</item>
<item row="4" column="0">
<widget class="QCheckBox" name="custom_rtc_checkbox">
<property name="text">
<string>Custom RTC</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_language">
<property name="text">
<string>Language</string>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QCheckBox" name="rng_seed_checkbox">
<property name="text">
<string>RNG Seed</string>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="device_name_label">
<property name="text">
<string>Device Name</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QDateTimeEdit" name="custom_rtc_edit">
<property name="minimumDate">
<date>
<year>1970</year>
<month>1</month>
<day>1</day>
</date>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QLineEdit" name="device_name_edit">
<property name="maxLength">
<number>128</number>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QLineEdit" name="rng_seed_edit">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<family>Lucida Console</family>
</font>
</property>
<property name="inputMask">
<string notr="true">HHHHHHHH</string>
</property>
<property name="maxLength">
<number>8</number>
</property>
</widget>
</item>
<item row="7" column="0">
<widget class="QCheckBox" name="use_unsafe_extended_memory_layout">
<property name="text">
<string>Unsafe extended memory layout (8GB DRAM)</string>
</property>
</widget>
</item>
</layout>
</item> </item>
</layout> </layout>
</widget> </widget>
@ -503,26 +96,6 @@
</property> </property>
</spacer> </spacer>
</item> </item>
<item>
<widget class="QLabel" name="label_warn_invalid_locale">
<property name="text">
<string></string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_disable_info">
<property name="text">
<string>System settings are available only when game is not running.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout> </layout>
</item> </item>
</layout> </layout>

@ -0,0 +1,388 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/time_zone.h"
#include "yuzu/configuration/shared_translation.h"
#include <map>
#include <memory>
#include <tuple>
#include <utility>
#include <QWidget>
#include "common/settings.h"
#include "common/settings_enums.h"
#include "common/settings_setting.h"
#include "yuzu/uisettings.h"
namespace ConfigurationShared {
std::unique_ptr<TranslationMap> InitializeTranslations(QWidget* parent) {
std::unique_ptr<TranslationMap> translations = std::make_unique<TranslationMap>();
const auto& tr = [parent](const char* text) -> QString { return parent->tr(text); };
#define INSERT(SETTINGS, ID, NAME, TOOLTIP) \
translations->insert(std::pair{SETTINGS::values.ID.Id(), std::pair{tr((NAME)), tr((TOOLTIP))}})
// A setting can be ignored by giving it a blank name
// Audio
INSERT(Settings, sink_id, "Output Engine:", "");
INSERT(Settings, audio_output_device_id, "Output Device:", "");
INSERT(Settings, audio_input_device_id, "Input Device:", "");
INSERT(Settings, audio_muted, "Mute audio when in background", "");
INSERT(Settings, volume, "Volume:", "");
INSERT(Settings, dump_audio_commands, "", "");
// Core
INSERT(Settings, use_multi_core, "Multicore CPU Emulation", "");
INSERT(Settings, memory_layout_mode, "Memory Layout", "");
INSERT(Settings, use_speed_limit, "", "");
INSERT(Settings, speed_limit, "Limit Speed Percent", "");
// Cpu
INSERT(Settings, cpu_accuracy, "Accuracy:", "");
// Cpu Debug
// Cpu Unsafe
INSERT(Settings, cpuopt_unsafe_unfuse_fma,
"Unfuse FMA (improve performance on CPUs without FMA)",
"This option improves speed by reducing accuracy of fused-multiply-add instructions on "
"CPUs without native FMA support.");
INSERT(Settings, cpuopt_unsafe_reduce_fp_error, "Faster FRSQRTE and FRECPE",
"This option improves the speed of some approximate floating-point functions by using "
"less accurate native approximations.");
INSERT(Settings, cpuopt_unsafe_ignore_standard_fpcr, "Faster ASIMD instructions (32 bits only)",
"This option improves the speed of 32 bits ASIMD floating-point functions by running "
"with incorrect rounding modes.");
INSERT(Settings, cpuopt_unsafe_inaccurate_nan, "Inaccurate NaN handling",
"This option improves speed by removing NaN checking. Please note this also reduces "
"accuracy of certain floating-point instructions.");
INSERT(
Settings, cpuopt_unsafe_fastmem_check, "Disable address space checks",
"This option improves speed by eliminating a safety check before every memory read/write "
"in guest. Disabling it may allow a game to read/write the emulator's memory.");
INSERT(Settings, cpuopt_unsafe_ignore_global_monitor, "Ignore global monitor",
"This option improves speed by relying only on the semantics of cmpxchg to ensure "
"safety of exclusive access instructions. Please note this may result in deadlocks and "
"other race conditions.");
// Renderer
INSERT(Settings, renderer_backend, "API:", "");
INSERT(Settings, vulkan_device, "Device:", "");
INSERT(Settings, shader_backend, "Shader Backend:", "");
INSERT(Settings, resolution_setup, "Resolution:", "");
INSERT(Settings, scaling_filter, "Window Adapting Filter:", "");
INSERT(Settings, fsr_sharpening_slider, "FSR Sharpness:", "");
INSERT(Settings, anti_aliasing, "Anti-Aliasing Method:", "");
INSERT(Settings, fullscreen_mode, "Fullscreen Mode:", "");
INSERT(Settings, aspect_ratio, "Aspect Ratio:", "");
INSERT(Settings, use_disk_shader_cache, "Use disk pipeline cache", "");
INSERT(Settings, use_asynchronous_gpu_emulation, "Use asynchronous GPU emulation", "");
INSERT(Settings, nvdec_emulation, "NVDEC emulation:", "");
INSERT(Settings, accelerate_astc, "ASTC Decoding Method:", "");
INSERT(Settings, astc_recompression, "ASTC Recompression Method:", "");
INSERT(Settings, vsync_mode, "VSync Mode:",
"FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen "
"refresh rate.\nFIFO Relaxed is similar to FIFO but allows tearing as it recovers from "
"a slow down.\nMailbox can have lower latency than FIFO and does not tear but may drop "
"frames.\nImmediate (no synchronization) just presents whatever is available and can "
"exhibit tearing.");
INSERT(Settings, bg_red, "", "");
INSERT(Settings, bg_green, "", "");
INSERT(Settings, bg_blue, "", "");
// Renderer (Advanced Graphics)
INSERT(Settings, async_presentation, "Enable asynchronous presentation (Vulkan only)", "");
INSERT(Settings, renderer_force_max_clock, "Force maximum clocks (Vulkan only)",
"Runs work in the background while waiting for graphics commands to keep the GPU from "
"lowering its clock speed.");
INSERT(Settings, max_anisotropy, "Anisotropic Filtering:", "");
INSERT(Settings, gpu_accuracy, "Accuracy Level:", "");
INSERT(Settings, use_asynchronous_shaders, "Use asynchronous shader building (Hack)",
"Enables asynchronous shader compilation, which may reduce shader stutter. This feature "
"is experimental.");
INSERT(Settings, use_fast_gpu_time, "Use Fast GPU Time (Hack)",
"Enables Fast GPU Time. This option will force most games to run at their highest "
"native resolution.");
INSERT(Settings, use_vulkan_driver_pipeline_cache, "Use Vulkan pipeline cache",
"Enables GPU vendor-specific pipeline cache. This option can improve shader loading "
"time significantly in cases where the Vulkan driver does not store pipeline cache "
"files internally.");
INSERT(Settings, enable_compute_pipelines, "Enable Compute Pipelines (Intel Vulkan Only)",
"Enable compute pipelines, required by some games.\nThis setting only exists for Intel "
"proprietary drivers, and may crash if enabled.\nCompute pipelines are always enabled "
"on all other drivers.");
INSERT(Settings, use_reactive_flushing, "Enable Reactive Flushing",
"Uses reactive flushing instead of predictive flushing, allowing more accurate memory "
"syncing.");
INSERT(Settings, use_video_framerate, "Sync to framerate of video playback",
"Run the game at normal speed during video playback, even when the framerate is "
"unlocked.");
INSERT(Settings, barrier_feedback_loops, "Barrier feedback loops",
"Improves rendering of transparency effects in specific games.");
// Renderer (Debug)
// System
INSERT(Settings, rng_seed, "RNG Seed", "");
INSERT(Settings, rng_seed_enabled, "", "");
INSERT(Settings, device_name, "Device Name", "");
INSERT(Settings, custom_rtc, "Custom RTC", "");
INSERT(Settings, custom_rtc_enabled, "", "");
INSERT(Settings, language_index,
"Language:", "Note: this can be overridden when region setting is auto-select");
INSERT(Settings, region_index, "Region:", "");
INSERT(Settings, time_zone_index, "Time Zone:", "");
INSERT(Settings, sound_index, "Sound Output Mode:", "");
INSERT(Settings, use_docked_mode, "", "");
INSERT(Settings, current_user, "", "");
// Controls
// Data Storage
// Debugging
// Debugging Graphics
// Network
// Web Service
// Ui
// Ui General
INSERT(UISettings, select_user_on_boot, "Prompt for user on game boot", "");
INSERT(UISettings, pause_when_in_background, "Pause emulation when in background", "");
INSERT(UISettings, confirm_before_closing, "Confirm exit while emulation is running", "");
INSERT(UISettings, hide_mouse, "Hide mouse on inactivity", "");
INSERT(UISettings, controller_applet_disabled, "Disable controller applet", "");
// Ui Debugging
// Ui Multiplayer
// Ui Games list
#undef INSERT
return translations;
}
std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QWidget* parent) {
std::unique_ptr<ComboboxTranslationMap> translations =
std::make_unique<ComboboxTranslationMap>();
const auto& tr = [&](const char* text, const char* context = "") {
return parent->tr(text, context);
};
#define PAIR(ENUM, VALUE, TRANSLATION) \
{ static_cast<u32>(Settings::ENUM::VALUE), tr(TRANSLATION) }
#define CTX_PAIR(ENUM, VALUE, TRANSLATION, CONTEXT) \
{ static_cast<u32>(Settings::ENUM::VALUE), tr(TRANSLATION, CONTEXT) }
// Intentionally skipping VSyncMode to let the UI fill that one out
translations->insert({Settings::EnumMetadata<Settings::AstcDecodeMode>::Index(),
{
PAIR(AstcDecodeMode, Cpu, "CPU"),
PAIR(AstcDecodeMode, Gpu, "GPU"),
PAIR(AstcDecodeMode, CpuAsynchronous, "CPU Asynchronous"),
}});
translations->insert({Settings::EnumMetadata<Settings::AstcRecompression>::Index(),
{
PAIR(AstcRecompression, Uncompressed, "Uncompressed (Best quality)"),
PAIR(AstcRecompression, Bc1, "BC1 (Low quality)"),
PAIR(AstcRecompression, Bc3, "BC3 (Medium quality)"),
}});
translations->insert({Settings::EnumMetadata<Settings::RendererBackend>::Index(),
{
#ifdef HAS_OPENGL
PAIR(RendererBackend, OpenGL, "OpenGL"),
#endif
PAIR(RendererBackend, Vulkan, "Vulkan"),
PAIR(RendererBackend, Null, "Null"),
}});
translations->insert({Settings::EnumMetadata<Settings::ShaderBackend>::Index(),
{
PAIR(ShaderBackend, Glsl, "GLSL"),
PAIR(ShaderBackend, Glasm, "GLASM (Assembly Shaders, NVIDIA Only)"),
PAIR(ShaderBackend, SpirV, "SPIR-V (Experimental, Mesa Only)"),
}});
translations->insert({Settings::EnumMetadata<Settings::GpuAccuracy>::Index(),
{
PAIR(GpuAccuracy, Normal, "Normal"),
PAIR(GpuAccuracy, High, "High"),
PAIR(GpuAccuracy, Extreme, "Extreme"),
}});
translations->insert({Settings::EnumMetadata<Settings::CpuAccuracy>::Index(),
{
PAIR(CpuAccuracy, Auto, "Auto"),
PAIR(CpuAccuracy, Accurate, "Accurate"),
PAIR(CpuAccuracy, Unsafe, "Unsafe"),
PAIR(CpuAccuracy, Paranoid, "Paranoid (disables most optimizations)"),
}});
translations->insert({Settings::EnumMetadata<Settings::FullscreenMode>::Index(),
{
PAIR(FullscreenMode, Borderless, "Borderless Windowed"),
PAIR(FullscreenMode, Exclusive, "Exclusive Fullscreen"),
}});
translations->insert({Settings::EnumMetadata<Settings::NvdecEmulation>::Index(),
{
PAIR(NvdecEmulation, Off, "No Video Output"),
PAIR(NvdecEmulation, Cpu, "CPU Video Decoding"),
PAIR(NvdecEmulation, Gpu, "GPU Video Decoding (Default)"),
}});
translations->insert({Settings::EnumMetadata<Settings::ResolutionSetup>::Index(),
{
PAIR(ResolutionSetup, Res1_2X, "0.5X (360p/540p) [EXPERIMENTAL]"),
PAIR(ResolutionSetup, Res3_4X, "0.75X (540p/810p) [EXPERIMENTAL]"),
PAIR(ResolutionSetup, Res1X, "1X (720p/1080p)"),
PAIR(ResolutionSetup, Res3_2X, "1.5X (1080p/1620p) [EXPERIMENTAL]"),
PAIR(ResolutionSetup, Res2X, "2X (1440p/2160p)"),
PAIR(ResolutionSetup, Res3X, "3X (2160p/3240p)"),
PAIR(ResolutionSetup, Res4X, "4X (2880p/4320p)"),
PAIR(ResolutionSetup, Res5X, "5X (3600p/5400p)"),
PAIR(ResolutionSetup, Res6X, "6X (4320p/6480p)"),
PAIR(ResolutionSetup, Res7X, "7X (5040p/7560p)"),
PAIR(ResolutionSetup, Res8X, "8X (5760p/8640p)"),
}});
translations->insert({Settings::EnumMetadata<Settings::ScalingFilter>::Index(),
{
PAIR(ScalingFilter, NearestNeighbor, "Nearest Neighbor"),
PAIR(ScalingFilter, Bilinear, "Bilinear"),
PAIR(ScalingFilter, Bicubic, "Bicubic"),
PAIR(ScalingFilter, Gaussian, "Gaussian"),
PAIR(ScalingFilter, ScaleForce, "ScaleForce"),
PAIR(ScalingFilter, Fsr, "AMD FidelityFX™ Super Resolution"),
}});
translations->insert({Settings::EnumMetadata<Settings::AntiAliasing>::Index(),
{
PAIR(AntiAliasing, None, "None"),
PAIR(AntiAliasing, Fxaa, "FXAA"),
PAIR(AntiAliasing, Smaa, "SMAA"),
}});
translations->insert({Settings::EnumMetadata<Settings::AspectRatio>::Index(),
{
PAIR(AspectRatio, R16_9, "Default (16:9)"),
PAIR(AspectRatio, R4_3, "Force 4:3"),
PAIR(AspectRatio, R21_9, "Force 21:9"),
PAIR(AspectRatio, R16_10, "Force 16:10"),
PAIR(AspectRatio, Stretch, "Stretch to Window"),
}});
translations->insert({Settings::EnumMetadata<Settings::AnisotropyMode>::Index(),
{
PAIR(AnisotropyMode, Automatic, "Automatic"),
PAIR(AnisotropyMode, Default, "Default"),
PAIR(AnisotropyMode, X2, "2x"),
PAIR(AnisotropyMode, X4, "4x"),
PAIR(AnisotropyMode, X8, "8x"),
PAIR(AnisotropyMode, X16, "16x"),
}});
translations->insert(
{Settings::EnumMetadata<Settings::Language>::Index(),
{
PAIR(Language, Japanese, "Japanese (日本語)"),
PAIR(Language, EnglishAmerican, "American English"),
PAIR(Language, French, "French (français)"),
PAIR(Language, German, "German (Deutsch)"),
PAIR(Language, Italian, "Italian (italiano)"),
PAIR(Language, Spanish, "Spanish (español)"),
PAIR(Language, Chinese, "Chinese"),
PAIR(Language, Korean, "Korean (한국어)"),
PAIR(Language, Dutch, "Dutch (Nederlands)"),
PAIR(Language, Portuguese, "Portuguese (português)"),
PAIR(Language, Russian, "Russian (Русский)"),
PAIR(Language, Taiwanese, "Taiwanese"),
PAIR(Language, EnglishBritish, "British English"),
PAIR(Language, FrenchCanadian, "Canadian French"),
PAIR(Language, SpanishLatin, "Latin American Spanish"),
PAIR(Language, ChineseSimplified, "Simplified Chinese"),
PAIR(Language, ChineseTraditional, "Traditional Chinese (正體中文)"),
PAIR(Language, PortugueseBrazilian, "Brazilian Portuguese (português do Brasil)"),
}});
translations->insert({Settings::EnumMetadata<Settings::Region>::Index(),
{
PAIR(Region, Japan, "Japan"),
PAIR(Region, Usa, "USA"),
PAIR(Region, Europe, "Europe"),
PAIR(Region, Australia, "Australia"),
PAIR(Region, China, "China"),
PAIR(Region, Korea, "Korea"),
PAIR(Region, Taiwan, "Taiwan"),
}});
translations->insert(
{Settings::EnumMetadata<Settings::TimeZone>::Index(),
{
{static_cast<u32>(Settings::TimeZone::Auto),
tr("Auto (%1)", "Auto select time zone")
.arg(QString::fromStdString(
Settings::GetTimeZoneString(Settings::TimeZone::Auto)))},
{static_cast<u32>(Settings::TimeZone::Default),
tr("Default (%1)", "Default time zone")
.arg(QString::fromStdString(Common::TimeZone::GetDefaultTimeZone()))},
PAIR(TimeZone, Cet, "CET"),
PAIR(TimeZone, Cst6Cdt, "CST6CDT"),
PAIR(TimeZone, Cuba, "Cuba"),
PAIR(TimeZone, Eet, "EET"),
PAIR(TimeZone, Egypt, "Egypt"),
PAIR(TimeZone, Eire, "Eire"),
PAIR(TimeZone, Est, "EST"),
PAIR(TimeZone, Est5Edt, "EST5EDT"),
PAIR(TimeZone, Gb, "GB"),
PAIR(TimeZone, GbEire, "GB-Eire"),
PAIR(TimeZone, Gmt, "GMT"),
PAIR(TimeZone, GmtPlusZero, "GMT+0"),
PAIR(TimeZone, GmtMinusZero, "GMT-0"),
PAIR(TimeZone, GmtZero, "GMT0"),
PAIR(TimeZone, Greenwich, "Greenwich"),
PAIR(TimeZone, Hongkong, "Hongkong"),
PAIR(TimeZone, Hst, "HST"),
PAIR(TimeZone, Iceland, "Iceland"),
PAIR(TimeZone, Iran, "Iran"),
PAIR(TimeZone, Israel, "Israel"),
PAIR(TimeZone, Jamaica, "Jamaica"),
PAIR(TimeZone, Japan, "Japan"),
PAIR(TimeZone, Kwajalein, "Kwajalein"),
PAIR(TimeZone, Libya, "Libya"),
PAIR(TimeZone, Met, "MET"),
PAIR(TimeZone, Mst, "MST"),
PAIR(TimeZone, Mst7Mdt, "MST7MDT"),
PAIR(TimeZone, Navajo, "Navajo"),
PAIR(TimeZone, Nz, "NZ"),
PAIR(TimeZone, NzChat, "NZ-CHAT"),
PAIR(TimeZone, Poland, "Poland"),
PAIR(TimeZone, Portugal, "Portugal"),
PAIR(TimeZone, Prc, "PRC"),
PAIR(TimeZone, Pst8Pdt, "PST8PDT"),
PAIR(TimeZone, Roc, "ROC"),
PAIR(TimeZone, Rok, "ROK"),
PAIR(TimeZone, Singapore, "Singapore"),
PAIR(TimeZone, Turkey, "Turkey"),
PAIR(TimeZone, Uct, "UCT"),
PAIR(TimeZone, Universal, "Universal"),
PAIR(TimeZone, Utc, "UTC"),
PAIR(TimeZone, WSu, "W-SU"),
PAIR(TimeZone, Wet, "WET"),
PAIR(TimeZone, Zulu, "Zulu"),
}});
translations->insert({Settings::EnumMetadata<Settings::AudioMode>::Index(),
{
PAIR(AudioMode, Mono, "Mono"),
PAIR(AudioMode, Stereo, "Stereo"),
PAIR(AudioMode, Surround, "Surround"),
}});
translations->insert({Settings::EnumMetadata<Settings::MemoryLayout>::Index(),
{
PAIR(MemoryLayout, Memory_4Gb, "4GB DRAM (Default)"),
PAIR(MemoryLayout, Memory_6Gb, "6GB DRAM (Unsafe)"),
PAIR(MemoryLayout, Memory_8Gb, "8GB DRAM (Unsafe)"),
}});
#undef PAIR
#undef CTX_PAIR
return translations;
}
} // namespace ConfigurationShared

@ -0,0 +1,25 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <map>
#include <memory>
#include <typeindex>
#include <utility>
#include <vector>
#include <QString>
#include "common/common_types.h"
class QWidget;
namespace ConfigurationShared {
using TranslationMap = std::map<u32, std::pair<QString, QString>>;
using ComboboxTranslations = std::vector<std::pair<u32, QString>>;
using ComboboxTranslationMap = std::map<u32, ComboboxTranslations>;
std::unique_ptr<TranslationMap> InitializeTranslations(QWidget* parent);
std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QWidget* parent);
} // namespace ConfigurationShared

@ -0,0 +1,642 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "yuzu/configuration/shared_widget.h"
#include <functional>
#include <limits>
#include <typeindex>
#include <typeinfo>
#include <utility>
#include <vector>
#include <QAbstractButton>
#include <QAbstractSlider>
#include <QBoxLayout>
#include <QCheckBox>
#include <QComboBox>
#include <QDateTime>
#include <QDateTimeEdit>
#include <QIcon>
#include <QLabel>
#include <QLayout>
#include <QLineEdit>
#include <QObject>
#include <QPushButton>
#include <QRegularExpression>
#include <QSizePolicy>
#include <QSlider>
#include <QSpinBox>
#include <QStyle>
#include <QValidator>
#include <QVariant>
#include <QtCore/qglobal.h>
#include <QtCore/qobjectdefs.h>
#include <fmt/core.h>
#include <qglobal.h>
#include <qnamespace.h>
#include "common/assert.h"
#include "common/common_types.h"
#include "common/logging/log.h"
#include "common/settings.h"
#include "common/settings_common.h"
#include "yuzu/configuration/shared_translation.h"
namespace ConfigurationShared {
static int restore_button_count = 0;
static std::string RelevantDefault(const Settings::BasicSetting& setting) {
return Settings::IsConfiguringGlobal() ? setting.DefaultToString() : setting.ToStringGlobal();
}
static QString DefaultSuffix(QWidget* parent, Settings::BasicSetting& setting) {
const auto tr = [parent](const char* text, const char* context) {
return parent->tr(text, context);
};
if ((setting.Specialization() & Settings::SpecializationAttributeMask) ==
Settings::Specialization::Percentage) {
std::string context{fmt::format("{} percentage (e.g. 50%)", setting.GetLabel())};
return tr("%", context.c_str());
}
return QStringLiteral("");
}
QPushButton* Widget::CreateRestoreGlobalButton(bool using_global, QWidget* parent) {
restore_button_count++;
QStyle* style = parent->style();
QIcon* icon = new QIcon(style->standardIcon(QStyle::SP_LineEditClearButton));
QPushButton* restore_button = new QPushButton(*icon, QStringLiteral(""), parent);
restore_button->setObjectName(QStringLiteral("RestoreButton%1").arg(restore_button_count));
restore_button->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
// Workaround for dark theme causing min-width to be much larger than 0
restore_button->setStyleSheet(
QStringLiteral("QAbstractButton#%1 { min-width: 0px }").arg(restore_button->objectName()));
QSizePolicy sp_retain = restore_button->sizePolicy();
sp_retain.setRetainSizeWhenHidden(true);
restore_button->setSizePolicy(sp_retain);
restore_button->setEnabled(!using_global);
restore_button->setVisible(!using_global);
return restore_button;
}
QLabel* Widget::CreateLabel(const QString& text) {
QLabel* qt_label = new QLabel(text, this->parent);
qt_label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
return qt_label;
}
QWidget* Widget::CreateCheckBox(Settings::BasicSetting* bool_setting, const QString& label,
std::function<std::string()>& serializer,
std::function<void()>& restore_func,
const std::function<void()>& touch) {
checkbox = new QCheckBox(label, this);
checkbox->setCheckState(bool_setting->ToString() == "true" ? Qt::CheckState::Checked
: Qt::CheckState::Unchecked);
checkbox->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
if (!bool_setting->Save() && !Settings::IsConfiguringGlobal() && runtime_lock) {
checkbox->setEnabled(false);
}
serializer = [this]() {
return checkbox->checkState() == Qt::CheckState::Checked ? "true" : "false";
};
restore_func = [this, bool_setting]() {
checkbox->setCheckState(RelevantDefault(*bool_setting) == "true" ? Qt::Checked
: Qt::Unchecked);
};
if (!Settings::IsConfiguringGlobal()) {
QObject::connect(checkbox, &QCheckBox::clicked, [touch]() { touch(); });
}
return checkbox;
}
QWidget* Widget::CreateCombobox(std::function<std::string()>& serializer,
std::function<void()>& restore_func,
const std::function<void()>& touch) {
const auto type = setting.EnumIndex();
combobox = new QComboBox(this);
combobox->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
const ComboboxTranslations* enumeration{nullptr};
if (combobox_enumerations.contains(type)) {
enumeration = &combobox_enumerations.at(type);
for (const auto& [id, name] : *enumeration) {
combobox->addItem(name);
}
} else {
return combobox;
}
const auto find_index = [=](u32 value) -> int {
for (u32 i = 0; i < enumeration->size(); i++) {
if (enumeration->at(i).first == value) {
return i;
}
}
return -1;
};
const u32 setting_value = std::stoi(setting.ToString());
combobox->setCurrentIndex(find_index(setting_value));
serializer = [this, enumeration]() {
int current = combobox->currentIndex();
return std::to_string(enumeration->at(current).first);
};
restore_func = [this, find_index]() {
const u32 global_value = std::stoi(RelevantDefault(setting));
combobox->setCurrentIndex(find_index(global_value));
};
if (!Settings::IsConfiguringGlobal()) {
QObject::connect(combobox, QOverload<int>::of(&QComboBox::activated),
[touch]() { touch(); });
}
return combobox;
}
QWidget* Widget::CreateLineEdit(std::function<std::string()>& serializer,
std::function<void()>& restore_func,
const std::function<void()>& touch, bool managed) {
const QString text = QString::fromStdString(setting.ToString());
line_edit = new QLineEdit(this);
line_edit->setText(text);
serializer = [this]() { return line_edit->text().toStdString(); };
if (!managed) {
return line_edit;
}
restore_func = [this]() {
line_edit->setText(QString::fromStdString(RelevantDefault(setting)));
};
if (!Settings::IsConfiguringGlobal()) {
QObject::connect(line_edit, &QLineEdit::textChanged, [touch]() { touch(); });
}
return line_edit;
}
QWidget* Widget::CreateSlider(bool reversed, float multiplier, const QString& given_suffix,
std::function<std::string()>& serializer,
std::function<void()>& restore_func,
const std::function<void()>& touch) {
if (!setting.Ranged()) {
LOG_ERROR(Frontend, "\"{}\" is not a ranged setting, but a slider was requested.",
setting.GetLabel());
return nullptr;
}
QWidget* container = new QWidget(this);
QHBoxLayout* layout = new QHBoxLayout(container);
slider = new QSlider(Qt::Horizontal, this);
QLabel* feedback = new QLabel(this);
layout->addWidget(slider);
layout->addWidget(feedback);
container->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
layout->setContentsMargins(0, 0, 0, 0);
int max_val = std::stoi(setting.MaxVal());
QString suffix =
given_suffix == QStringLiteral("") ? DefaultSuffix(this, setting) : given_suffix;
const QString use_format = QStringLiteral("%1").append(suffix);
QObject::connect(slider, &QAbstractSlider::valueChanged, [=](int value) {
int present = (reversed ? max_val - value : value) * multiplier + 0.5f;
feedback->setText(use_format.arg(QVariant::fromValue(present).value<QString>()));
});
slider->setMinimum(std::stoi(setting.MinVal()));
slider->setMaximum(max_val);
slider->setValue(std::stoi(setting.ToString()));
slider->setInvertedAppearance(reversed);
serializer = [this]() { return std::to_string(slider->value()); };
restore_func = [this]() { slider->setValue(std::stoi(RelevantDefault(setting))); };
if (!Settings::IsConfiguringGlobal()) {
QObject::connect(slider, &QAbstractSlider::actionTriggered, [touch]() { touch(); });
}
return container;
}
QWidget* Widget::CreateSpinBox(const QString& given_suffix,
std::function<std::string()>& serializer,
std::function<void()>& restore_func,
const std::function<void()>& touch) {
const int min_val =
setting.Ranged() ? std::stoi(setting.MinVal()) : std::numeric_limits<int>::min();
const int max_val =
setting.Ranged() ? std::stoi(setting.MaxVal()) : std::numeric_limits<int>::max();
const int default_val = std::stoi(setting.ToString());
QString suffix =
given_suffix == QStringLiteral("") ? DefaultSuffix(this, setting) : given_suffix;
spinbox = new QSpinBox(this);
spinbox->setRange(min_val, max_val);
spinbox->setValue(default_val);
spinbox->setSuffix(suffix);
spinbox->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
serializer = [this]() { return std::to_string(spinbox->value()); };
restore_func = [this]() {
auto value{std::stol(RelevantDefault(setting))};
spinbox->setValue(value);
};
if (!Settings::IsConfiguringGlobal()) {
QObject::connect(spinbox, QOverload<int>::of(&QSpinBox::valueChanged), [this, touch]() {
if (spinbox->value() != std::stoi(setting.ToStringGlobal())) {
touch();
}
});
}
return spinbox;
}
QWidget* Widget::CreateHexEdit(std::function<std::string()>& serializer,
std::function<void()>& restore_func,
const std::function<void()>& touch) {
auto* data_component = CreateLineEdit(serializer, restore_func, touch, false);
if (data_component == nullptr) {
return nullptr;
}
auto to_hex = [=](const std::string& input) {
return QString::fromStdString(fmt::format("{:08x}", std::stoul(input)));
};
QRegularExpressionValidator* regex = new QRegularExpressionValidator(
QRegularExpression{QStringLiteral("^[0-9a-fA-F]{0,8}$")}, line_edit);
const QString default_val = to_hex(setting.ToString());
line_edit->setText(default_val);
line_edit->setMaxLength(8);
line_edit->setValidator(regex);
auto hex_to_dec = [this]() -> std::string {
return std::to_string(std::stoul(line_edit->text().toStdString(), nullptr, 16));
};
serializer = [hex_to_dec]() { return hex_to_dec(); };
restore_func = [this, to_hex]() { line_edit->setText(to_hex(RelevantDefault(setting))); };
if (!Settings::IsConfiguringGlobal()) {
QObject::connect(line_edit, &QLineEdit::textChanged, [touch]() { touch(); });
}
return line_edit;
}
QWidget* Widget::CreateDateTimeEdit(bool disabled, bool restrict,
std::function<std::string()>& serializer,
std::function<void()>& restore_func,
const std::function<void()>& touch) {
const long long current_time = QDateTime::currentSecsSinceEpoch();
const s64 the_time = disabled ? current_time : std::stoll(setting.ToString());
const auto default_val = QDateTime::fromSecsSinceEpoch(the_time);
date_time_edit = new QDateTimeEdit(this);
date_time_edit->setDateTime(default_val);
date_time_edit->setMinimumDateTime(QDateTime::fromSecsSinceEpoch(0));
date_time_edit->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
serializer = [this]() { return std::to_string(date_time_edit->dateTime().toSecsSinceEpoch()); };
auto get_clear_val = [this, restrict, current_time]() {
return QDateTime::fromSecsSinceEpoch([this, restrict, current_time]() {
if (restrict && checkbox->checkState() == Qt::Checked) {
return std::stoll(RelevantDefault(setting));
}
return current_time;
}());
};
restore_func = [this, get_clear_val]() { date_time_edit->setDateTime(get_clear_val()); };
if (!Settings::IsConfiguringGlobal()) {
QObject::connect(date_time_edit, &QDateTimeEdit::editingFinished,
[this, get_clear_val, touch]() {
if (date_time_edit->dateTime() != get_clear_val()) {
touch();
}
});
}
return date_time_edit;
}
void Widget::SetupComponent(const QString& label, std::function<void()>& load_func, bool managed,
RequestType request, float multiplier,
Settings::BasicSetting* other_setting, const QString& suffix) {
created = true;
const auto type = setting.TypeId();
QLayout* layout = new QHBoxLayout(this);
layout->setContentsMargins(0, 0, 0, 0);
if (other_setting == nullptr) {
other_setting = setting.PairedSetting();
}
const bool require_checkbox =
other_setting != nullptr && other_setting->TypeId() == typeid(bool);
if (other_setting != nullptr && other_setting->TypeId() != typeid(bool)) {
LOG_WARNING(
Frontend,
"Extra setting \"{}\" specified but is not bool, refusing to create checkbox for it.",
other_setting->GetLabel());
}
std::function<std::string()> checkbox_serializer = []() -> std::string { return {}; };
std::function<void()> checkbox_restore_func = []() {};
std::function<void()> touch = []() {};
std::function<std::string()> serializer = []() -> std::string { return {}; };
std::function<void()> restore_func = []() {};
QWidget* data_component{nullptr};
request = [&]() {
if (request != RequestType::Default) {
return request;
}
switch (setting.Specialization() & Settings::SpecializationTypeMask) {
case Settings::Specialization::Default:
return RequestType::Default;
case Settings::Specialization::Time:
return RequestType::DateTimeEdit;
case Settings::Specialization::Hex:
return RequestType::HexEdit;
case Settings::Specialization::RuntimeList:
managed = false;
[[fallthrough]];
case Settings::Specialization::List:
return RequestType::ComboBox;
case Settings::Specialization::Scalar:
return RequestType::Slider;
case Settings::Specialization::Countable:
return RequestType::SpinBox;
default:
break;
}
return request;
}();
if (!Settings::IsConfiguringGlobal() && managed) {
restore_button = CreateRestoreGlobalButton(setting.UsingGlobal(), this);
touch = [this]() {
LOG_DEBUG(Frontend, "Enabling custom setting for \"{}\"", setting.GetLabel());
restore_button->setEnabled(true);
restore_button->setVisible(true);
};
}
if (require_checkbox) {
QWidget* lhs =
CreateCheckBox(other_setting, label, checkbox_serializer, checkbox_restore_func, touch);
layout->addWidget(lhs);
} else if (setting.TypeId() != typeid(bool)) {
QLabel* qt_label = CreateLabel(label);
layout->addWidget(qt_label);
}
if (setting.TypeId() == typeid(bool)) {
data_component = CreateCheckBox(&setting, label, serializer, restore_func, touch);
} else if (setting.IsEnum()) {
data_component = CreateCombobox(serializer, restore_func, touch);
} else if (type == typeid(u32) || type == typeid(int) || type == typeid(u16) ||
type == typeid(s64) || type == typeid(u8)) {
switch (request) {
case RequestType::Slider:
case RequestType::ReverseSlider:
data_component = CreateSlider(request == RequestType::ReverseSlider, multiplier, suffix,
serializer, restore_func, touch);
break;
case RequestType::Default:
case RequestType::LineEdit:
data_component = CreateLineEdit(serializer, restore_func, touch);
break;
case RequestType::DateTimeEdit:
data_component = CreateDateTimeEdit(other_setting->ToString() != "true", true,
serializer, restore_func, touch);
break;
case RequestType::SpinBox:
data_component = CreateSpinBox(suffix, serializer, restore_func, touch);
break;
case RequestType::HexEdit:
data_component = CreateHexEdit(serializer, restore_func, touch);
break;
case RequestType::ComboBox:
data_component = CreateCombobox(serializer, restore_func, touch);
break;
default:
UNIMPLEMENTED();
}
} else if (type == typeid(std::string)) {
switch (request) {
case RequestType::Default:
case RequestType::LineEdit:
data_component = CreateLineEdit(serializer, restore_func, touch);
break;
case RequestType::ComboBox:
data_component = CreateCombobox(serializer, restore_func, touch);
break;
default:
UNIMPLEMENTED();
}
}
if (data_component == nullptr) {
LOG_ERROR(Frontend, "Failed to create widget for \"{}\"", setting.GetLabel());
created = false;
return;
}
layout->addWidget(data_component);
if (!managed) {
return;
}
if (Settings::IsConfiguringGlobal()) {
load_func = [this, serializer, checkbox_serializer, require_checkbox, other_setting]() {
if (require_checkbox && other_setting->UsingGlobal()) {
other_setting->LoadString(checkbox_serializer());
}
if (setting.UsingGlobal()) {
setting.LoadString(serializer());
}
};
} else {
layout->addWidget(restore_button);
QObject::connect(restore_button, &QAbstractButton::clicked,
[this, restore_func, checkbox_restore_func](bool) {
LOG_DEBUG(Frontend, "Restore global state for \"{}\"",
setting.GetLabel());
restore_button->setEnabled(false);
restore_button->setVisible(false);
checkbox_restore_func();
restore_func();
});
load_func = [this, serializer, require_checkbox, checkbox_serializer, other_setting]() {
bool using_global = !restore_button->isEnabled();
setting.SetGlobal(using_global);
if (!using_global) {
setting.LoadString(serializer());
}
if (require_checkbox) {
other_setting->SetGlobal(using_global);
if (!using_global) {
other_setting->LoadString(checkbox_serializer());
}
}
};
}
if (other_setting != nullptr) {
const auto reset = [restore_func, data_component](int state) {
data_component->setEnabled(state == Qt::Checked);
if (state != Qt::Checked) {
restore_func();
}
};
connect(checkbox, &QCheckBox::stateChanged, reset);
reset(checkbox->checkState());
}
}
bool Widget::Valid() const {
return created;
}
Widget::~Widget() = default;
Widget::Widget(Settings::BasicSetting* setting_, const TranslationMap& translations_,
const ComboboxTranslationMap& combobox_translations_, QWidget* parent_,
bool runtime_lock_, std::vector<std::function<void(bool)>>& apply_funcs_,
RequestType request, bool managed, float multiplier,
Settings::BasicSetting* other_setting, const QString& suffix)
: QWidget(parent_), parent{parent_}, translations{translations_},
combobox_enumerations{combobox_translations_}, setting{*setting_}, apply_funcs{apply_funcs_},
runtime_lock{runtime_lock_} {
if (!Settings::IsConfiguringGlobal() && !setting.Switchable()) {
LOG_DEBUG(Frontend, "\"{}\" is not switchable, skipping...", setting.GetLabel());
return;
}
const int id = setting.Id();
const auto [label, tooltip] = [&]() {
const auto& setting_label = setting.GetLabel();
if (translations.contains(id)) {
return std::pair{translations.at(id).first, translations.at(id).second};
}
LOG_WARNING(Frontend, "Translation table lacks entry for \"{}\"", setting_label);
return std::pair{QString::fromStdString(setting_label), QStringLiteral("")};
}();
if (label == QStringLiteral("")) {
LOG_DEBUG(Frontend, "Translation table has empty entry for \"{}\", skipping...",
setting.GetLabel());
return;
}
std::function<void()> load_func = []() {};
SetupComponent(label, load_func, managed, request, multiplier, other_setting, suffix);
if (!created) {
LOG_WARNING(Frontend, "No widget was created for \"{}\"", setting.GetLabel());
return;
}
apply_funcs.push_back([load_func, setting_](bool powered_on) {
if (setting_->RuntimeModfiable() || !powered_on) {
load_func();
}
});
bool enable = runtime_lock || setting.RuntimeModfiable();
if (setting.Switchable() && Settings::IsConfiguringGlobal() && !runtime_lock) {
enable &= setting.UsingGlobal();
}
this->setEnabled(enable);
this->setToolTip(tooltip);
}
Builder::Builder(QWidget* parent_, bool runtime_lock_)
: translations{InitializeTranslations(parent_)},
combobox_translations{ComboboxEnumeration(parent_)}, parent{parent_}, runtime_lock{
runtime_lock_} {}
Builder::~Builder() = default;
Widget* Builder::BuildWidget(Settings::BasicSetting* setting,
std::vector<std::function<void(bool)>>& apply_funcs,
RequestType request, bool managed, float multiplier,
Settings::BasicSetting* other_setting, const QString& suffix) const {
if (!Settings::IsConfiguringGlobal() && !setting->Switchable()) {
return nullptr;
}
if (setting->Specialization() == Settings::Specialization::Paired) {
LOG_DEBUG(Frontend, "\"{}\" has specialization Paired: ignoring", setting->GetLabel());
return nullptr;
}
return new Widget(setting, *translations, *combobox_translations, parent, runtime_lock,
apply_funcs, request, managed, multiplier, other_setting, suffix);
}
Widget* Builder::BuildWidget(Settings::BasicSetting* setting,
std::vector<std::function<void(bool)>>& apply_funcs,
Settings::BasicSetting* other_setting, RequestType request,
const QString& suffix) const {
return BuildWidget(setting, apply_funcs, request, true, 1.0f, other_setting, suffix);
}
const ComboboxTranslationMap& Builder::ComboboxTranslations() const {
return *combobox_translations;
}
} // namespace ConfigurationShared

@ -0,0 +1,161 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <functional>
#include <memory>
#include <string>
#include <vector>
#include <QString>
#include <QStringLiteral>
#include <QWidget>
#include <qobjectdefs.h>
#include "yuzu/configuration/shared_translation.h"
class QCheckBox;
class QComboBox;
class QDateTimeEdit;
class QLabel;
class QLineEdit;
class QObject;
class QPushButton;
class QSlider;
class QSpinBox;
namespace Settings {
class BasicSetting;
} // namespace Settings
namespace ConfigurationShared {
enum class RequestType {
Default,
ComboBox,
SpinBox,
Slider,
ReverseSlider,
LineEdit,
HexEdit,
DateTimeEdit,
MaxEnum,
};
class Widget : public QWidget {
Q_OBJECT
public:
/**
* @param setting The primary Setting to create the Widget for
* @param translations Map of translations to display on the left side label/checkbox
* @param combobox_translations Map of translations for enumerating combo boxes
* @param parent Qt parent
* @param runtime_lock Emulated guest powered on state, for use on settings that should be
* configured during guest execution
* @param apply_funcs_ List to append, functions to run to apply the widget state to the setting
* @param request What type of data representation component to create -- not always respected
* for the Setting data type
* @param managed Set true if the caller will set up component data and handling
* @param multiplier Value to multiply the slider feedback label
* @param other_setting Second setting to modify, to replace the label with a checkbox
* @param suffix Set to specify formats for Slider feedback labels or SpinBox
*/
explicit Widget(Settings::BasicSetting* setting, const TranslationMap& translations,
const ComboboxTranslationMap& combobox_translations, QWidget* parent,
bool runtime_lock, std::vector<std::function<void(bool)>>& apply_funcs_,
RequestType request = RequestType::Default, bool managed = true,
float multiplier = 1.0f, Settings::BasicSetting* other_setting = nullptr,
const QString& suffix = QStringLiteral(""));
virtual ~Widget();
/**
* @returns True if the Widget successfully created the components for the setting
*/
bool Valid() const;
/**
* Creates a button to appear when a setting has been modified. This exists for custom
* configurations and wasn't designed to work for the global configuration. It has public access
* for settings that need to be unmanaged but can be custom.
*
* @param using_global The global state of the setting this button is for
* @param parent QWidget parent
*/
[[nodiscard]] static QPushButton* CreateRestoreGlobalButton(bool using_global, QWidget* parent);
// Direct handles to sub components created
QPushButton* restore_button{}; ///< Restore button for custom configurations
QLineEdit* line_edit{}; ///< QLineEdit, used for LineEdit and HexEdit
QSpinBox* spinbox{};
QCheckBox* checkbox{};
QSlider* slider{};
QComboBox* combobox{};
QDateTimeEdit* date_time_edit{};
private:
void SetupComponent(const QString& label, std::function<void()>& load_func, bool managed,
RequestType request, float multiplier,
Settings::BasicSetting* other_setting, const QString& suffix);
QLabel* CreateLabel(const QString& text);
QWidget* CreateCheckBox(Settings::BasicSetting* bool_setting, const QString& label,
std::function<std::string()>& serializer,
std::function<void()>& restore_func,
const std::function<void()>& touch);
QWidget* CreateCombobox(std::function<std::string()>& serializer,
std::function<void()>& restore_func,
const std::function<void()>& touch);
QWidget* CreateLineEdit(std::function<std::string()>& serializer,
std::function<void()>& restore_func, const std::function<void()>& touch,
bool managed = true);
QWidget* CreateHexEdit(std::function<std::string()>& serializer,
std::function<void()>& restore_func, const std::function<void()>& touch);
QWidget* CreateSlider(bool reversed, float multiplier, const QString& suffix,
std::function<std::string()>& serializer,
std::function<void()>& restore_func, const std::function<void()>& touch);
QWidget* CreateDateTimeEdit(bool disabled, bool restrict,
std::function<std::string()>& serializer,
std::function<void()>& restore_func,
const std::function<void()>& touch);
QWidget* CreateSpinBox(const QString& suffix, std::function<std::string()>& serializer,
std::function<void()>& restore_func, const std::function<void()>& touch);
QWidget* parent;
const TranslationMap& translations;
const ComboboxTranslationMap& combobox_enumerations;
Settings::BasicSetting& setting;
std::vector<std::function<void(bool)>>& apply_funcs;
bool created{false};
bool runtime_lock{false};
};
class Builder {
public:
explicit Builder(QWidget* parent, bool runtime_lock);
~Builder();
Widget* BuildWidget(Settings::BasicSetting* setting,
std::vector<std::function<void(bool)>>& apply_funcs,
RequestType request = RequestType::Default, bool managed = true,
float multiplier = 1.0f, Settings::BasicSetting* other_setting = nullptr,
const QString& suffix = QStringLiteral("")) const;
Widget* BuildWidget(Settings::BasicSetting* setting,
std::vector<std::function<void(bool)>>& apply_funcs,
Settings::BasicSetting* other_setting,
RequestType request = RequestType::Default,
const QString& suffix = QStringLiteral("")) const;
const ComboboxTranslationMap& ComboboxTranslations() const;
private:
std::unique_ptr<TranslationMap> translations;
std::unique_ptr<ComboboxTranslationMap> combobox_translations;
QWidget* parent;
const bool runtime_lock;
};
} // namespace ConfigurationShared

@ -24,6 +24,7 @@
#include "applets/qt_software_keyboard.h" #include "applets/qt_software_keyboard.h"
#include "applets/qt_web_browser.h" #include "applets/qt_web_browser.h"
#include "common/nvidia_flags.h" #include "common/nvidia_flags.h"
#include "common/settings_enums.h"
#include "configuration/configure_input.h" #include "configuration/configure_input.h"
#include "configuration/configure_per_game.h" #include "configuration/configure_per_game.h"
#include "configuration/configure_tas.h" #include "configuration/configure_tas.h"
@ -1095,10 +1096,9 @@ void GMainWindow::InitializeWidgets() {
aa_status_button->setFocusPolicy(Qt::NoFocus); aa_status_button->setFocusPolicy(Qt::NoFocus);
connect(aa_status_button, &QPushButton::clicked, [&] { connect(aa_status_button, &QPushButton::clicked, [&] {
auto aa_mode = Settings::values.anti_aliasing.GetValue(); auto aa_mode = Settings::values.anti_aliasing.GetValue();
if (aa_mode == Settings::AntiAliasing::LastAA) { aa_mode = static_cast<Settings::AntiAliasing>(static_cast<u32>(aa_mode) + 1);
if (aa_mode == Settings::AntiAliasing::MaxEnum) {
aa_mode = Settings::AntiAliasing::None; aa_mode = Settings::AntiAliasing::None;
} else {
aa_mode = static_cast<Settings::AntiAliasing>(static_cast<u32>(aa_mode) + 1);
} }
Settings::values.anti_aliasing.SetValue(aa_mode); Settings::values.anti_aliasing.SetValue(aa_mode);
aa_status_button->setChecked(true); aa_status_button->setChecked(true);
@ -1183,7 +1183,7 @@ void GMainWindow::InitializeWidgets() {
QMenu context_menu; QMenu context_menu;
for (auto const& gpu_accuracy_pair : Config::gpu_accuracy_texts_map) { for (auto const& gpu_accuracy_pair : Config::gpu_accuracy_texts_map) {
if (gpu_accuracy_pair.first == Settings::GPUAccuracy::Extreme) { if (gpu_accuracy_pair.first == Settings::GpuAccuracy::Extreme) {
continue; continue;
} }
context_menu.addAction(gpu_accuracy_pair.second, [this, gpu_accuracy_pair] { context_menu.addAction(gpu_accuracy_pair.second, [this, gpu_accuracy_pair] {
@ -3651,14 +3651,14 @@ void GMainWindow::OnToggleDockedMode() {
void GMainWindow::OnToggleGpuAccuracy() { void GMainWindow::OnToggleGpuAccuracy() {
switch (Settings::values.gpu_accuracy.GetValue()) { switch (Settings::values.gpu_accuracy.GetValue()) {
case Settings::GPUAccuracy::High: { case Settings::GpuAccuracy::High: {
Settings::values.gpu_accuracy.SetValue(Settings::GPUAccuracy::Normal); Settings::values.gpu_accuracy.SetValue(Settings::GpuAccuracy::Normal);
break; break;
} }
case Settings::GPUAccuracy::Normal: case Settings::GpuAccuracy::Normal:
case Settings::GPUAccuracy::Extreme: case Settings::GpuAccuracy::Extreme:
default: { default: {
Settings::values.gpu_accuracy.SetValue(Settings::GPUAccuracy::High); Settings::values.gpu_accuracy.SetValue(Settings::GpuAccuracy::High);
break; break;
} }
} }
@ -3702,10 +3702,9 @@ void GMainWindow::OnIncreaseVolume() {
void GMainWindow::OnToggleAdaptingFilter() { void GMainWindow::OnToggleAdaptingFilter() {
auto filter = Settings::values.scaling_filter.GetValue(); auto filter = Settings::values.scaling_filter.GetValue();
if (filter == Settings::ScalingFilter::LastFilter) { filter = static_cast<Settings::ScalingFilter>(static_cast<u32>(filter) + 1);
if (filter == Settings::ScalingFilter::MaxEnum) {
filter = Settings::ScalingFilter::NearestNeighbor; filter = Settings::ScalingFilter::NearestNeighbor;
} else {
filter = static_cast<Settings::ScalingFilter>(static_cast<u32>(filter) + 1);
} }
Settings::values.scaling_filter.SetValue(filter); Settings::values.scaling_filter.SetValue(filter);
filter_status_button->setChecked(true); filter_status_button->setChecked(true);
@ -4071,7 +4070,7 @@ void GMainWindow::UpdateGPUAccuracyButton() {
const auto gpu_accuracy = Settings::values.gpu_accuracy.GetValue(); const auto gpu_accuracy = Settings::values.gpu_accuracy.GetValue();
const auto gpu_accuracy_text = Config::gpu_accuracy_texts_map.find(gpu_accuracy)->second; const auto gpu_accuracy_text = Config::gpu_accuracy_texts_map.find(gpu_accuracy)->second;
gpu_accuracy_button->setText(gpu_accuracy_text.toUpper()); gpu_accuracy_button->setText(gpu_accuracy_text.toUpper());
gpu_accuracy_button->setChecked(gpu_accuracy != Settings::GPUAccuracy::Normal); gpu_accuracy_button->setChecked(gpu_accuracy != Settings::GpuAccuracy::Normal);
} }
void GMainWindow::UpdateDockedButton() { void GMainWindow::UpdateDockedButton() {

@ -34,13 +34,14 @@ DirectConnectWindow::DirectConnectWindow(Core::System& system_, QWidget* parent)
connect(watcher, &QFutureWatcher<void>::finished, this, &DirectConnectWindow::OnConnection); connect(watcher, &QFutureWatcher<void>::finished, this, &DirectConnectWindow::OnConnection);
ui->nickname->setValidator(validation.GetNickname()); ui->nickname->setValidator(validation.GetNickname());
ui->nickname->setText(UISettings::values.multiplayer_nickname.GetValue()); ui->nickname->setText(
QString::fromStdString(UISettings::values.multiplayer_nickname.GetValue()));
if (ui->nickname->text().isEmpty() && !Settings::values.yuzu_username.GetValue().empty()) { if (ui->nickname->text().isEmpty() && !Settings::values.yuzu_username.GetValue().empty()) {
// Use yuzu Web Service user name as nickname by default // Use yuzu Web Service user name as nickname by default
ui->nickname->setText(QString::fromStdString(Settings::values.yuzu_username.GetValue())); ui->nickname->setText(QString::fromStdString(Settings::values.yuzu_username.GetValue()));
} }
ui->ip->setValidator(validation.GetIP()); ui->ip->setValidator(validation.GetIP());
ui->ip->setText(UISettings::values.multiplayer_ip.GetValue()); ui->ip->setText(QString::fromStdString(UISettings::values.multiplayer_ip.GetValue()));
ui->port->setValidator(validation.GetPort()); ui->port->setValidator(validation.GetPort());
ui->port->setText(QString::number(UISettings::values.multiplayer_port.GetValue())); ui->port->setText(QString::number(UISettings::values.multiplayer_port.GetValue()));
@ -91,8 +92,8 @@ void DirectConnectWindow::Connect() {
} }
// Store settings // Store settings
UISettings::values.multiplayer_nickname = ui->nickname->text(); UISettings::values.multiplayer_nickname = ui->nickname->text().toStdString();
UISettings::values.multiplayer_ip = ui->ip->text(); UISettings::values.multiplayer_ip = ui->ip->text().toStdString();
if (ui->port->isModified() && !ui->port->text().isEmpty()) { if (ui->port->isModified() && !ui->port->text().isEmpty()) {
UISettings::values.multiplayer_port = ui->port->text().toInt(); UISettings::values.multiplayer_port = ui->port->text().toInt();
} else { } else {

@ -55,12 +55,14 @@ HostRoomWindow::HostRoomWindow(QWidget* parent, QStandardItemModel* list,
connect(ui->host, &QPushButton::clicked, this, &HostRoomWindow::Host); connect(ui->host, &QPushButton::clicked, this, &HostRoomWindow::Host);
// Restore the settings: // Restore the settings:
ui->username->setText(UISettings::values.multiplayer_room_nickname.GetValue()); ui->username->setText(
QString::fromStdString(UISettings::values.multiplayer_room_nickname.GetValue()));
if (ui->username->text().isEmpty() && !Settings::values.yuzu_username.GetValue().empty()) { if (ui->username->text().isEmpty() && !Settings::values.yuzu_username.GetValue().empty()) {
// Use yuzu Web Service user name as nickname by default // Use yuzu Web Service user name as nickname by default
ui->username->setText(QString::fromStdString(Settings::values.yuzu_username.GetValue())); ui->username->setText(QString::fromStdString(Settings::values.yuzu_username.GetValue()));
} }
ui->room_name->setText(UISettings::values.multiplayer_room_name.GetValue()); ui->room_name->setText(
QString::fromStdString(UISettings::values.multiplayer_room_name.GetValue()));
ui->port->setText(QString::number(UISettings::values.multiplayer_room_port.GetValue())); ui->port->setText(QString::number(UISettings::values.multiplayer_room_port.GetValue()));
ui->max_player->setValue(UISettings::values.multiplayer_max_player.GetValue()); ui->max_player->setValue(UISettings::values.multiplayer_max_player.GetValue());
int index = UISettings::values.multiplayer_host_type.GetValue(); int index = UISettings::values.multiplayer_host_type.GetValue();
@ -72,7 +74,8 @@ HostRoomWindow::HostRoomWindow(QWidget* parent, QStandardItemModel* list,
if (index != -1) { if (index != -1) {
ui->game_list->setCurrentIndex(index); ui->game_list->setCurrentIndex(index);
} }
ui->room_description->setText(UISettings::values.multiplayer_room_description.GetValue()); ui->room_description->setText(
QString::fromStdString(UISettings::values.multiplayer_room_description.GetValue()));
} }
HostRoomWindow::~HostRoomWindow() = default; HostRoomWindow::~HostRoomWindow() = default;
@ -218,8 +221,8 @@ void HostRoomWindow::Host() {
Network::NoPreferredIP, password, token); Network::NoPreferredIP, password, token);
// Store settings // Store settings
UISettings::values.multiplayer_room_nickname = ui->username->text(); UISettings::values.multiplayer_room_nickname = ui->username->text().toStdString();
UISettings::values.multiplayer_room_name = ui->room_name->text(); UISettings::values.multiplayer_room_name = ui->room_name->text().toStdString();
UISettings::values.multiplayer_game_id = UISettings::values.multiplayer_game_id =
ui->game_list->currentData(GameListItemPath::ProgramIdRole).toLongLong(); ui->game_list->currentData(GameListItemPath::ProgramIdRole).toLongLong();
UISettings::values.multiplayer_max_player = ui->max_player->value(); UISettings::values.multiplayer_max_player = ui->max_player->value();
@ -230,7 +233,8 @@ void HostRoomWindow::Host() {
} else { } else {
UISettings::values.multiplayer_room_port = Network::DefaultRoomPort; UISettings::values.multiplayer_room_port = Network::DefaultRoomPort;
} }
UISettings::values.multiplayer_room_description = ui->room_description->toPlainText(); UISettings::values.multiplayer_room_description =
ui->room_description->toPlainText().toStdString();
ui->host->setEnabled(true); ui->host->setEnabled(true);
emit SaveConfig(); emit SaveConfig();
close(); close();

@ -60,7 +60,8 @@ Lobby::Lobby(QWidget* parent, QStandardItemModel* list,
ui->room_list->setContextMenuPolicy(Qt::CustomContextMenu); ui->room_list->setContextMenuPolicy(Qt::CustomContextMenu);
ui->nickname->setValidator(validation.GetNickname()); ui->nickname->setValidator(validation.GetNickname());
ui->nickname->setText(UISettings::values.multiplayer_nickname.GetValue()); ui->nickname->setText(
QString::fromStdString(UISettings::values.multiplayer_nickname.GetValue()));
// Try find the best nickname by default // Try find the best nickname by default
if (ui->nickname->text().isEmpty() || ui->nickname->text() == QStringLiteral("yuzu")) { if (ui->nickname->text().isEmpty() || ui->nickname->text() == QStringLiteral("yuzu")) {
@ -202,9 +203,9 @@ void Lobby::OnJoinRoom(const QModelIndex& source) {
// TODO(jroweboy): disable widgets and display a connecting while we wait // TODO(jroweboy): disable widgets and display a connecting while we wait
// Save settings // Save settings
UISettings::values.multiplayer_nickname = ui->nickname->text(); UISettings::values.multiplayer_nickname = ui->nickname->text().toStdString();
UISettings::values.multiplayer_ip = UISettings::values.multiplayer_ip =
proxy->data(connection_index, LobbyItemHost::HostIPRole).toString(); proxy->data(connection_index, LobbyItemHost::HostIPRole).value<QString>().toStdString();
UISettings::values.multiplayer_port = UISettings::values.multiplayer_port =
proxy->data(connection_index, LobbyItemHost::HostPortRole).toInt(); proxy->data(connection_index, LobbyItemHost::HostPortRole).toInt();
emit SaveConfig(); emit SaveConfig();

@ -3,6 +3,18 @@
#include "yuzu/uisettings.h" #include "yuzu/uisettings.h"
#ifndef CANNOT_EXPLICITLY_INSTANTIATE
namespace Settings {
template class Setting<bool>;
template class Setting<std::string>;
template class Setting<u16, true>;
template class Setting<u32>;
template class Setting<u8, true>;
template class Setting<u8>;
template class Setting<unsigned long long>;
} // namespace Settings
#endif
namespace UISettings { namespace UISettings {
const Themes themes{{ const Themes themes{{

@ -14,6 +14,21 @@
#include "common/common_types.h" #include "common/common_types.h"
#include "common/settings.h" #include "common/settings.h"
using Settings::Category;
using Settings::Setting;
#ifndef CANNOT_EXPLICITLY_INSTANTIATE
namespace Settings {
extern template class Setting<bool>;
extern template class Setting<std::string>;
extern template class Setting<u16, true>;
extern template class Setting<u32>;
extern template class Setting<u8, true>;
extern template class Setting<u8>;
extern template class Setting<unsigned long long>;
} // namespace Settings
#endif
namespace UISettings { namespace UISettings {
bool IsDarkTheme(); bool IsDarkTheme();
@ -56,6 +71,8 @@ struct GameDir {
}; };
struct Values { struct Values {
Settings::Linkage linkage{1000};
QByteArray geometry; QByteArray geometry;
QByteArray state; QByteArray state;
@ -64,30 +81,54 @@ struct Values {
QByteArray gamelist_header_state; QByteArray gamelist_header_state;
QByteArray microprofile_geometry; QByteArray microprofile_geometry;
Settings::Setting<bool> microprofile_visible{false, "microProfileDialogVisible"}; Setting<bool> microprofile_visible{linkage, false, "microProfileDialogVisible",
Category::UiLayout};
Settings::Setting<bool> single_window_mode{true, "singleWindowMode"}; Setting<bool> single_window_mode{linkage, true, "singleWindowMode", Category::Ui};
Settings::Setting<bool> fullscreen{false, "fullscreen"}; Setting<bool> fullscreen{linkage, false, "fullscreen", Category::Ui};
Settings::Setting<bool> display_titlebar{true, "displayTitleBars"}; Setting<bool> display_titlebar{linkage, true, "displayTitleBars", Category::Ui};
Settings::Setting<bool> show_filter_bar{true, "showFilterBar"}; Setting<bool> show_filter_bar{linkage, true, "showFilterBar", Category::Ui};
Settings::Setting<bool> show_status_bar{true, "showStatusBar"}; Setting<bool> show_status_bar{linkage, true, "showStatusBar", Category::Ui};
Settings::Setting<bool> confirm_before_closing{true, "confirmClose"};
Settings::Setting<bool> first_start{true, "firstStart"};
Settings::Setting<bool> pause_when_in_background{false, "pauseWhenInBackground"};
Settings::Setting<bool> mute_when_in_background{false, "muteWhenInBackground"};
Settings::Setting<bool> hide_mouse{true, "hideInactiveMouse"};
Settings::Setting<bool> controller_applet_disabled{false, "disableControllerApplet"};
Setting<bool> confirm_before_closing{
linkage, true, "confirmClose", Category::UiGeneral, Settings::Specialization::Default,
true, true};
Setting<bool> first_start{linkage, true, "firstStart", Category::Ui};
Setting<bool> pause_when_in_background{linkage,
false,
"pauseWhenInBackground",
Category::UiGeneral,
Settings::Specialization::Default,
true,
true};
Setting<bool> mute_when_in_background{
linkage, false, "muteWhenInBackground", Category::Ui, Settings::Specialization::Default,
true, true};
Setting<bool> hide_mouse{
linkage, true, "hideInactiveMouse", Category::UiGeneral, Settings::Specialization::Default,
true, true};
Setting<bool> controller_applet_disabled{linkage, false, "disableControllerApplet",
Category::UiGeneral};
// Set when Vulkan is known to crash the application // Set when Vulkan is known to crash the application
bool has_broken_vulkan = false; bool has_broken_vulkan = false;
Settings::Setting<bool> select_user_on_boot{false, "select_user_on_boot"}; Setting<bool> select_user_on_boot{linkage,
false,
"select_user_on_boot",
Category::UiGeneral,
Settings::Specialization::Default,
true,
true};
Setting<bool> disable_web_applet{linkage, true, "disable_web_applet", Category::Ui};
// Discord RPC // Discord RPC
Settings::Setting<bool> enable_discord_presence{true, "enable_discord_presence"}; Setting<bool> enable_discord_presence{linkage, true, "enable_discord_presence", Category::Ui};
Settings::Setting<bool> enable_screenshot_save_as{true, "enable_screenshot_save_as"}; // logging
Setting<bool> show_console{linkage, false, "showConsole", Category::Ui};
Setting<bool> enable_screenshot_save_as{linkage, true, "enable_screenshot_save_as",
Category::Screenshots};
QString roms_path; QString roms_path;
QString symbols_path; QString symbols_path;
@ -102,47 +143,46 @@ struct Values {
// Shortcut name <Shortcut, context> // Shortcut name <Shortcut, context>
std::vector<Shortcut> shortcuts; std::vector<Shortcut> shortcuts;
Settings::Setting<uint32_t> callout_flags{0, "calloutFlags"}; Setting<u32> callout_flags{linkage, 0, "calloutFlags", Category::Ui};
// multiplayer settings // multiplayer settings
Settings::Setting<QString> multiplayer_nickname{{}, "nickname"}; Setting<std::string> multiplayer_nickname{linkage, {}, "nickname", Category::Multiplayer};
Settings::Setting<QString> multiplayer_ip{{}, "ip"}; Setting<std::string> multiplayer_ip{linkage, {}, "ip", Category::Multiplayer};
Settings::SwitchableSetting<uint, true> multiplayer_port{24872, 0, UINT16_MAX, "port"}; Setting<u16, true> multiplayer_port{linkage, 24872, 0,
Settings::Setting<QString> multiplayer_room_nickname{{}, "room_nickname"}; UINT16_MAX, "port", Category::Multiplayer};
Settings::Setting<QString> multiplayer_room_name{{}, "room_name"}; Setting<std::string> multiplayer_room_nickname{
Settings::SwitchableSetting<uint, true> multiplayer_max_player{8, 0, 8, "max_player"}; linkage, {}, "room_nickname", Category::Multiplayer};
Settings::SwitchableSetting<uint, true> multiplayer_room_port{24872, 0, UINT16_MAX, Setting<std::string> multiplayer_room_name{linkage, {}, "room_name", Category::Multiplayer};
"room_port"}; Setting<u8, true> multiplayer_max_player{linkage, 8, 0, 8, "max_player", Category::Multiplayer};
Settings::SwitchableSetting<uint, true> multiplayer_host_type{0, 0, 1, "host_type"}; Setting<u16, true> multiplayer_room_port{linkage, 24872, 0,
Settings::Setting<qulonglong> multiplayer_game_id{{}, "game_id"}; UINT16_MAX, "room_port", Category::Multiplayer};
Settings::Setting<QString> multiplayer_room_description{{}, "room_description"}; Setting<u8, true> multiplayer_host_type{linkage, 0, 0, 1, "host_type", Category::Multiplayer};
Setting<unsigned long long> multiplayer_game_id{linkage, {}, "game_id", Category::Multiplayer};
Setting<std::string> multiplayer_room_description{
linkage, {}, "room_description", Category::Multiplayer};
std::pair<std::vector<std::string>, std::vector<std::string>> multiplayer_ban_list; std::pair<std::vector<std::string>, std::vector<std::string>> multiplayer_ban_list;
// logging
Settings::Setting<bool> show_console{false, "showConsole"};
// Game List // Game List
Settings::Setting<bool> show_add_ons{true, "show_add_ons"}; Setting<bool> show_add_ons{linkage, true, "show_add_ons", Category::UiGameList};
Settings::Setting<uint32_t> game_icon_size{64, "game_icon_size"}; Setting<u32> game_icon_size{linkage, 64, "game_icon_size", Category::UiGameList};
Settings::Setting<uint32_t> folder_icon_size{48, "folder_icon_size"}; Setting<u32> folder_icon_size{linkage, 48, "folder_icon_size", Category::UiGameList};
Settings::Setting<uint8_t> row_1_text_id{3, "row_1_text_id"}; Setting<u8> row_1_text_id{linkage, 3, "row_1_text_id", Category::UiGameList};
Settings::Setting<uint8_t> row_2_text_id{2, "row_2_text_id"}; Setting<u8> row_2_text_id{linkage, 2, "row_2_text_id", Category::UiGameList};
std::atomic_bool is_game_list_reload_pending{false}; std::atomic_bool is_game_list_reload_pending{false};
Settings::Setting<bool> cache_game_list{true, "cache_game_list"}; Setting<bool> cache_game_list{linkage, true, "cache_game_list", Category::UiGameList};
Settings::Setting<bool> favorites_expanded{true, "favorites_expanded"}; Setting<bool> favorites_expanded{linkage, true, "favorites_expanded", Category::UiGameList};
QVector<u64> favorited_ids; QVector<u64> favorited_ids;
// Compatibility List // Compatibility List
Settings::Setting<bool> show_compat{false, "show_compat"}; Setting<bool> show_compat{linkage, false, "show_compat", Category::UiGameList};
// Size & File Types Column // Size & File Types Column
Settings::Setting<bool> show_size{true, "show_size"}; Setting<bool> show_size{linkage, true, "show_size", Category::UiGameList};
Settings::Setting<bool> show_types{true, "show_types"}; Setting<bool> show_types{linkage, true, "show_types", Category::UiGameList};
bool configuration_applied; bool configuration_applied;
bool reset_to_defaults; bool reset_to_defaults;
bool shortcut_already_warned{false}; bool shortcut_already_warned{false};
Settings::Setting<bool> disable_web_applet{true, "disable_web_applet"};
}; };
extern Values values; extern Values values;

@ -98,8 +98,26 @@ void Config::ReadSetting(const std::string& group, Settings::Setting<Type, range
static_cast<long>(setting.GetDefault()))); static_cast<long>(setting.GetDefault())));
} }
void Config::ReadCategory(Settings::Category category) {
for (const auto setting : Settings::values.linkage.by_category[category]) {
const char* category_name = [&]() {
if (category == Settings::Category::Controls) {
// For compatibility with older configs
return "ControlsGeneral";
} else {
return Settings::TranslateCategory(category);
}
}();
std::string setting_value =
sdl2_config->Get(category_name, setting->GetLabel(), setting->DefaultToString());
setting->LoadString(setting_value);
}
}
void Config::ReadValues() { void Config::ReadValues() {
// Controls // Controls
ReadCategory(Settings::Category::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) {
auto& player = Settings::values.players.GetValue()[p]; auto& player = Settings::values.players.GetValue()[p];
@ -139,13 +157,6 @@ void Config::ReadValues() {
player.connected = sdl2_config->GetBoolean(group, "connected", false); player.connected = sdl2_config->GetBoolean(group, "connected", false);
} }
ReadSetting("ControlsGeneral", Settings::values.mouse_enabled);
ReadSetting("ControlsGeneral", Settings::values.touch_device);
ReadSetting("ControlsGeneral", Settings::values.keyboard_enabled);
ReadSetting("ControlsGeneral", Settings::values.debug_pad_enabled);
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(
@ -166,14 +177,6 @@ void Config::ReadValues() {
Settings::values.debug_pad_analogs[i] = default_param; Settings::values.debug_pad_analogs[i] = default_param;
} }
ReadSetting("ControlsGeneral", Settings::values.enable_raw_input);
ReadSetting("ControlsGeneral", Settings::values.enable_joycon_driver);
ReadSetting("ControlsGeneral", Settings::values.enable_procon_driver);
ReadSetting("ControlsGeneral", Settings::values.random_amiibo_id);
ReadSetting("ControlsGeneral", Settings::values.emulate_analog_keyboard);
ReadSetting("ControlsGeneral", Settings::values.vibration_enabled);
ReadSetting("ControlsGeneral", Settings::values.enable_accurate_vibrations);
ReadSetting("ControlsGeneral", Settings::values.motion_enabled);
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 =
@ -217,10 +220,24 @@ void Config::ReadValues() {
Settings::values.touch_from_button_map_index = std::clamp( Settings::values.touch_from_button_map_index = std::clamp(
Settings::values.touch_from_button_map_index.GetValue(), 0, num_touch_from_button_maps - 1); Settings::values.touch_from_button_map_index.GetValue(), 0, num_touch_from_button_maps - 1);
ReadSetting("ControlsGeneral", Settings::values.udp_input_servers); ReadCategory(Settings::Category::Audio);
ReadCategory(Settings::Category::Core);
ReadCategory(Settings::Category::Cpu);
ReadCategory(Settings::Category::CpuDebug);
ReadCategory(Settings::Category::CpuUnsafe);
ReadCategory(Settings::Category::Renderer);
ReadCategory(Settings::Category::RendererAdvanced);
ReadCategory(Settings::Category::RendererDebug);
ReadCategory(Settings::Category::System);
ReadCategory(Settings::Category::SystemAudio);
ReadCategory(Settings::Category::DataStorage);
ReadCategory(Settings::Category::Debugging);
ReadCategory(Settings::Category::DebuggingGraphics);
ReadCategory(Settings::Category::Miscellaneous);
ReadCategory(Settings::Category::Network);
ReadCategory(Settings::Category::WebService);
// Data Storage // Data Storage
ReadSetting("Data Storage", Settings::values.use_virtual_sd);
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)));
@ -233,124 +250,10 @@ 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)));
ReadSetting("Data Storage", Settings::values.gamecard_inserted);
ReadSetting("Data Storage", Settings::values.gamecard_current_game);
ReadSetting("Data Storage", Settings::values.gamecard_path);
// System
ReadSetting("System", Settings::values.use_docked_mode);
ReadSetting("System", Settings::values.current_user);
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);
if (rng_seed_enabled) {
Settings::values.rng_seed.SetValue(sdl2_config->GetInteger("System", "rng_seed", 0));
} else {
Settings::values.rng_seed.SetValue(std::nullopt);
}
const auto custom_rtc_enabled = sdl2_config->GetBoolean("System", "custom_rtc_enabled", false);
if (custom_rtc_enabled) {
Settings::values.custom_rtc = sdl2_config->GetInteger("System", "custom_rtc", 0);
} else {
Settings::values.custom_rtc = std::nullopt;
}
ReadSetting("System", Settings::values.language_index);
ReadSetting("System", Settings::values.region_index);
ReadSetting("System", Settings::values.time_zone_index);
ReadSetting("System", Settings::values.sound_index);
// Core
ReadSetting("Core", Settings::values.use_multi_core);
ReadSetting("Core", Settings::values.use_unsafe_extended_memory_layout);
// Cpu
ReadSetting("Cpu", Settings::values.cpu_accuracy);
ReadSetting("Cpu", Settings::values.cpu_debug_mode);
ReadSetting("Cpu", Settings::values.cpuopt_page_tables);
ReadSetting("Cpu", Settings::values.cpuopt_block_linking);
ReadSetting("Cpu", Settings::values.cpuopt_return_stack_buffer);
ReadSetting("Cpu", Settings::values.cpuopt_fast_dispatcher);
ReadSetting("Cpu", Settings::values.cpuopt_context_elimination);
ReadSetting("Cpu", Settings::values.cpuopt_const_prop);
ReadSetting("Cpu", Settings::values.cpuopt_misc_ir);
ReadSetting("Cpu", Settings::values.cpuopt_reduce_misalign_checks);
ReadSetting("Cpu", Settings::values.cpuopt_fastmem);
ReadSetting("Cpu", Settings::values.cpuopt_fastmem_exclusives);
ReadSetting("Cpu", Settings::values.cpuopt_recompile_exclusives);
ReadSetting("Cpu", Settings::values.cpuopt_ignore_memory_aborts);
ReadSetting("Cpu", Settings::values.cpuopt_unsafe_unfuse_fma);
ReadSetting("Cpu", Settings::values.cpuopt_unsafe_reduce_fp_error);
ReadSetting("Cpu", Settings::values.cpuopt_unsafe_ignore_standard_fpcr);
ReadSetting("Cpu", Settings::values.cpuopt_unsafe_inaccurate_nan);
ReadSetting("Cpu", Settings::values.cpuopt_unsafe_fastmem_check);
ReadSetting("Cpu", Settings::values.cpuopt_unsafe_ignore_global_monitor);
// Renderer
ReadSetting("Renderer", Settings::values.renderer_backend);
ReadSetting("Renderer", Settings::values.async_presentation);
ReadSetting("Renderer", Settings::values.renderer_force_max_clock);
ReadSetting("Renderer", Settings::values.renderer_debug);
ReadSetting("Renderer", Settings::values.renderer_shader_feedback);
ReadSetting("Renderer", Settings::values.enable_nsight_aftermath);
ReadSetting("Renderer", Settings::values.disable_shader_loop_safety_checks);
ReadSetting("Renderer", Settings::values.vulkan_device);
ReadSetting("Renderer", Settings::values.resolution_setup);
ReadSetting("Renderer", Settings::values.scaling_filter);
ReadSetting("Renderer", Settings::values.fsr_sharpening_slider);
ReadSetting("Renderer", Settings::values.anti_aliasing);
ReadSetting("Renderer", Settings::values.fullscreen_mode);
ReadSetting("Renderer", Settings::values.aspect_ratio);
ReadSetting("Renderer", Settings::values.max_anisotropy);
ReadSetting("Renderer", Settings::values.use_speed_limit);
ReadSetting("Renderer", Settings::values.speed_limit);
ReadSetting("Renderer", Settings::values.use_disk_shader_cache);
ReadSetting("Renderer", Settings::values.gpu_accuracy);
ReadSetting("Renderer", Settings::values.use_asynchronous_gpu_emulation);
ReadSetting("Renderer", Settings::values.vsync_mode);
ReadSetting("Renderer", Settings::values.shader_backend);
ReadSetting("Renderer", Settings::values.use_reactive_flushing);
ReadSetting("Renderer", Settings::values.use_asynchronous_shaders);
ReadSetting("Renderer", Settings::values.nvdec_emulation);
ReadSetting("Renderer", Settings::values.accelerate_astc);
ReadSetting("Renderer", Settings::values.async_astc);
ReadSetting("Renderer", Settings::values.astc_recompression);
ReadSetting("Renderer", Settings::values.use_fast_gpu_time);
ReadSetting("Renderer", Settings::values.use_vulkan_driver_pipeline_cache);
ReadSetting("Renderer", Settings::values.bg_red);
ReadSetting("Renderer", Settings::values.bg_green);
ReadSetting("Renderer", Settings::values.bg_blue);
// Audio
ReadSetting("Audio", Settings::values.sink_id);
ReadSetting("Audio", Settings::values.audio_output_device_id);
ReadSetting("Audio", Settings::values.volume);
// Miscellaneous
// log_filter has a different default here than from common
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);
ReadSetting("Debugging", Settings::values.dump_exefs);
ReadSetting("Debugging", Settings::values.dump_nso);
ReadSetting("Debugging", Settings::values.enable_fs_access_log);
ReadSetting("Debugging", Settings::values.reporting_services);
ReadSetting("Debugging", Settings::values.quest_flag);
ReadSetting("Debugging", Settings::values.use_debug_asserts);
ReadSetting("Debugging", Settings::values.use_auto_stub);
ReadSetting("Debugging", Settings::values.disable_macro_jit);
ReadSetting("Debugging", Settings::values.disable_macro_hle);
ReadSetting("Debugging", Settings::values.use_gdbstub);
ReadSetting("Debugging", Settings::values.gdbstub_port);
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);
@ -368,15 +271,6 @@ void Config::ReadValues() {
Settings::values.disabled_addons.insert_or_assign(title_id, out); Settings::values.disabled_addons.insert_or_assign(title_id, out);
} }
// Web Service
ReadSetting("WebService", Settings::values.enable_telemetry);
ReadSetting("WebService", Settings::values.web_api_url);
ReadSetting("WebService", Settings::values.yuzu_username);
ReadSetting("WebService", Settings::values.yuzu_token);
// Network
ReadSetting("Network", Settings::values.network_interface);
} }
void Config::Reload() { void Config::Reload() {

@ -34,4 +34,5 @@ private:
*/ */
template <typename Type, bool ranged> template <typename Type, bool ranged>
void ReadSetting(const std::string& group, Settings::Setting<Type, ranged>& setting); void ReadSetting(const std::string& group, Settings::Setting<Type, ranged>& setting);
void ReadCategory(Settings::Category category);
}; };