summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar liamwhite2023-08-02 14:25:52 -0400
committerGravatar GitHub2023-08-02 14:25:52 -0400
commitfca7d975fdbeb1c63677b80efc03920affee4b12 (patch)
treebbded6cf80886c6def87bae92cf6784340165de9 /src
parentMerge pull request #11204 from liamwhite/eds3-blend-amd (diff)
parentconfig(qt): Fix name of network category (diff)
downloadyuzu-fca7d975fdbeb1c63677b80efc03920affee4b12.tar.gz
yuzu-fca7d975fdbeb1c63677b80efc03920affee4b12.tar.xz
yuzu-fca7d975fdbeb1c63677b80efc03920affee4b12.zip
Merge pull request #10839 from lat9nq/pgc-plus
general: Reimplement per-game configurations
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt2
-rw-r--r--src/android/app/src/main/jni/config.cpp19
-rw-r--r--src/audio_core/sink/sink_details.cpp34
-rw-r--r--src/audio_core/sink/sink_details.h8
-rw-r--r--src/common/CMakeLists.txt15
-rw-r--r--src/common/logging/backend.cpp2
-rw-r--r--src/common/settings.cpp248
-rw-r--r--src/common/settings.h860
-rw-r--r--src/common/settings_common.cpp58
-rw-r--r--src/common/settings_common.h256
-rw-r--r--src/common/settings_enums.h214
-rw-r--r--src/common/settings_setting.h394
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_32.cpp6
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.cpp6
-rw-r--r--src/core/core.cpp28
-rw-r--r--src/core/file_sys/control_metadata.cpp3
-rw-r--r--src/core/file_sys/patch_manager.cpp4
-rw-r--r--src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp22
-rw-r--r--src/core/hle/kernel/k_process.cpp3
-rw-r--r--src/core/hle/service/ns/ns.cpp2
-rw-r--r--src/core/hle/service/set/set.cpp10
-rw-r--r--src/core/hle/service/spl/spl_module.cpp3
-rw-r--r--src/core/hle/service/time/time_zone_content_manager.cpp3
-rw-r--r--src/core/telemetry_session.cpp34
-rw-r--r--src/video_core/host1x/codecs/codec.cpp2
-rw-r--r--src/video_core/host1x/codecs/h264.cpp2
-rw-r--r--src/video_core/renderer_opengl/gl_compute_pipeline.cpp6
-rw-r--r--src/video_core/renderer_opengl/gl_device.cpp8
-rw-r--r--src/video_core/renderer_opengl/gl_graphics_pipeline.cpp6
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.cpp12
-rw-r--r--src/video_core/renderer_opengl/gl_texture_cache.cpp8
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp2
-rw-r--r--src/video_core/renderer_vulkan/vk_swapchain.cpp12
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.cpp19
-rw-r--r--src/video_core/textures/texture.cpp4
-rw-r--r--src/yuzu/CMakeLists.txt10
-rw-r--r--src/yuzu/configuration/config.cpp714
-rw-r--r--src/yuzu/configuration/config.h64
-rw-r--r--src/yuzu/configuration/configuration_shared.cpp103
-rw-r--r--src/yuzu/configuration/configuration_shared.h78
-rw-r--r--src/yuzu/configuration/configure.ui31
-rw-r--r--src/yuzu/configuration/configure_audio.cpp205
-rw-r--r--src/yuzu/configuration/configure_audio.h32
-rw-r--r--src/yuzu/configuration/configure_audio.ui162
-rw-r--r--src/yuzu/configuration/configure_cpu.cpp149
-rw-r--r--src/yuzu/configuration/configure_cpu.h37
-rw-r--r--src/yuzu/configuration/configure_cpu.ui179
-rw-r--r--src/yuzu/configuration/configure_debug.ui858
-rw-r--r--src/yuzu/configuration/configure_dialog.cpp19
-rw-r--r--src/yuzu/configuration/configure_dialog.h5
-rw-r--r--src/yuzu/configuration/configure_general.cpp116
-rw-r--r--src/yuzu/configuration/configure_general.h29
-rw-r--r--src/yuzu/configuration/configure_general.ui87
-rw-r--r--src/yuzu/configuration/configure_graphics.cpp617
-rw-r--r--src/yuzu/configuration/configure_graphics.h50
-rw-r--r--src/yuzu/configuration/configure_graphics.ui690
-rw-r--r--src/yuzu/configuration/configure_graphics_advanced.cpp187
-rw-r--r--src/yuzu/configuration/configure_graphics_advanced.h39
-rw-r--r--src/yuzu/configuration/configure_graphics_advanced.ui231
-rw-r--r--src/yuzu/configuration/configure_per_game.cpp30
-rw-r--r--src/yuzu/configuration/configure_per_game.h7
-rw-r--r--src/yuzu/configuration/configure_per_game.ui47
-rw-r--r--src/yuzu/configuration/configure_system.cpp212
-rw-r--r--src/yuzu/configuration/configure_system.h36
-rw-r--r--src/yuzu/configuration/configure_system.ui537
-rw-r--r--src/yuzu/configuration/shared_translation.cpp388
-rw-r--r--src/yuzu/configuration/shared_translation.h25
-rw-r--r--src/yuzu/configuration/shared_widget.cpp642
-rw-r--r--src/yuzu/configuration/shared_widget.h161
-rw-r--r--src/yuzu/main.cpp25
-rw-r--r--src/yuzu/multiplayer/direct_connect.cpp9
-rw-r--r--src/yuzu/multiplayer/host_room.cpp16
-rw-r--r--src/yuzu/multiplayer/lobby.cpp7
-rw-r--r--src/yuzu/uisettings.cpp12
-rw-r--r--src/yuzu/uisettings.h128
-rw-r--r--src/yuzu_cmd/config.cpp174
-rw-r--r--src/yuzu_cmd/config.h1
77 files changed, 4629 insertions, 4835 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 0696201df..2da983cad 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -35,6 +35,7 @@ if (MSVC)
35 # /volatile:iso - Use strict standards-compliant volatile semantics. 35 # /volatile:iso - Use strict standards-compliant volatile semantics.
36 # /Zc:externConstexpr - Allow extern constexpr variables to have external linkage, like the standard mandates 36 # /Zc:externConstexpr - Allow extern constexpr variables to have external linkage, like the standard mandates
37 # /Zc:inline - Let codegen omit inline functions in object files 37 # /Zc:inline - Let codegen omit inline functions in object files
38 # /Zc:preprocessor - Enable standards-conforming preprocessor
38 # /Zc:throwingNew - Let codegen assume `operator new` (without std::nothrow) will never return null 39 # /Zc:throwingNew - Let codegen assume `operator new` (without std::nothrow) will never return null
39 # /GT - Supports fiber safety for data allocated using static thread-local storage 40 # /GT - Supports fiber safety for data allocated using static thread-local storage
40 add_compile_options( 41 add_compile_options(
@@ -48,6 +49,7 @@ if (MSVC)
48 /volatile:iso 49 /volatile:iso
49 /Zc:externConstexpr 50 /Zc:externConstexpr
50 /Zc:inline 51 /Zc:inline
52 /Zc:preprocessor
51 /Zc:throwingNew 53 /Zc:throwingNew
52 /GT 54 /GT
53 55
diff --git a/src/android/app/src/main/jni/config.cpp b/src/android/app/src/main/jni/config.cpp
index 43e8aa72a..5e1f10f99 100644
--- a/src/android/app/src/main/jni/config.cpp
+++ b/src/android/app/src/main/jni/config.cpp
@@ -150,15 +150,17 @@ void Config::ReadValues() {
150 if (rng_seed_enabled) { 150 if (rng_seed_enabled) {
151 Settings::values.rng_seed.SetValue(config->GetInteger("System", "rng_seed", 0)); 151 Settings::values.rng_seed.SetValue(config->GetInteger("System", "rng_seed", 0));
152 } else { 152 } else {
153 Settings::values.rng_seed.SetValue(std::nullopt); 153 Settings::values.rng_seed.SetValue(0);
154 } 154 }
155 Settings::values.rng_seed_enabled.SetValue(rng_seed_enabled);
155 156
156 const auto custom_rtc_enabled = config->GetBoolean("System", "custom_rtc_enabled", false); 157 const auto custom_rtc_enabled = config->GetBoolean("System", "custom_rtc_enabled", false);
157 if (custom_rtc_enabled) { 158 if (custom_rtc_enabled) {
158 Settings::values.custom_rtc = config->GetInteger("System", "custom_rtc", 0); 159 Settings::values.custom_rtc = config->GetInteger("System", "custom_rtc", 0);
159 } else { 160 } else {
160 Settings::values.custom_rtc = std::nullopt; 161 Settings::values.custom_rtc = 0;
161 } 162 }
163 Settings::values.custom_rtc_enabled = custom_rtc_enabled;
162 164
163 ReadSetting("System", Settings::values.language_index); 165 ReadSetting("System", Settings::values.language_index);
164 ReadSetting("System", Settings::values.region_index); 166 ReadSetting("System", Settings::values.region_index);
@@ -167,7 +169,7 @@ void Config::ReadValues() {
167 169
168 // Core 170 // Core
169 ReadSetting("Core", Settings::values.use_multi_core); 171 ReadSetting("Core", Settings::values.use_multi_core);
170 ReadSetting("Core", Settings::values.use_unsafe_extended_memory_layout); 172 ReadSetting("Core", Settings::values.memory_layout_mode);
171 173
172 // Cpu 174 // Cpu
173 ReadSetting("Cpu", Settings::values.cpu_accuracy); 175 ReadSetting("Cpu", Settings::values.cpu_accuracy);
@@ -222,14 +224,17 @@ void Config::ReadValues() {
222 ReadSetting("Renderer", Settings::values.bg_blue); 224 ReadSetting("Renderer", Settings::values.bg_blue);
223 225
224 // Use GPU accuracy normal by default on Android 226 // Use GPU accuracy normal by default on Android
225 Settings::values.gpu_accuracy = static_cast<Settings::GPUAccuracy>(config->GetInteger( 227 Settings::values.gpu_accuracy = static_cast<Settings::GpuAccuracy>(config->GetInteger(
226 "Renderer", "gpu_accuracy", static_cast<u32>(Settings::GPUAccuracy::Normal))); 228 "Renderer", "gpu_accuracy", static_cast<u32>(Settings::GpuAccuracy::Normal)));
227 229
228 // Use GPU default anisotropic filtering on Android 230 // Use GPU default anisotropic filtering on Android
229 Settings::values.max_anisotropy = config->GetInteger("Renderer", "max_anisotropy", 1); 231 Settings::values.max_anisotropy =
232 static_cast<Settings::AnisotropyMode>(config->GetInteger("Renderer", "max_anisotropy", 1));
230 233
231 // Disable ASTC compute by default on Android 234 // Disable ASTC compute by default on Android
232 Settings::values.accelerate_astc = config->GetBoolean("Renderer", "accelerate_astc", false); 235 Settings::values.accelerate_astc.SetValue(
236 config->GetBoolean("Renderer", "accelerate_astc", false) ? Settings::AstcDecodeMode::Gpu
237 : Settings::AstcDecodeMode::Cpu);
233 238
234 // Enable asynchronous presentation by default on Android 239 // Enable asynchronous presentation by default on Android
235 Settings::values.async_presentation = 240 Settings::values.async_presentation =
diff --git a/src/audio_core/sink/sink_details.cpp b/src/audio_core/sink/sink_details.cpp
index 39ea6d91b..027bfa517 100644
--- a/src/audio_core/sink/sink_details.cpp
+++ b/src/audio_core/sink/sink_details.cpp
@@ -15,6 +15,7 @@
15#endif 15#endif
16#include "audio_core/sink/null_sink.h" 16#include "audio_core/sink/null_sink.h"
17#include "common/logging/log.h" 17#include "common/logging/log.h"
18#include "common/settings_enums.h"
18 19
19namespace AudioCore::Sink { 20namespace AudioCore::Sink {
20namespace { 21namespace {
@@ -24,7 +25,7 @@ struct SinkDetails {
24 using LatencyFn = u32 (*)(); 25 using LatencyFn = u32 (*)();
25 26
26 /// Name for this sink. 27 /// Name for this sink.
27 std::string_view id; 28 Settings::AudioEngine id;
28 /// A method to call to construct an instance of this type of sink. 29 /// A method to call to construct an instance of this type of sink.
29 FactoryFn factory; 30 FactoryFn factory;
30 /// A method to call to list available devices. 31 /// A method to call to list available devices.
@@ -37,7 +38,7 @@ struct SinkDetails {
37constexpr SinkDetails sink_details[] = { 38constexpr SinkDetails sink_details[] = {
38#ifdef HAVE_CUBEB 39#ifdef HAVE_CUBEB
39 SinkDetails{ 40 SinkDetails{
40 "cubeb", 41 Settings::AudioEngine::Cubeb,
41 [](std::string_view device_id) -> std::unique_ptr<Sink> { 42 [](std::string_view device_id) -> std::unique_ptr<Sink> {
42 return std::make_unique<CubebSink>(device_id); 43 return std::make_unique<CubebSink>(device_id);
43 }, 44 },
@@ -47,7 +48,7 @@ constexpr SinkDetails sink_details[] = {
47#endif 48#endif
48#ifdef HAVE_SDL2 49#ifdef HAVE_SDL2
49 SinkDetails{ 50 SinkDetails{
50 "sdl2", 51 Settings::AudioEngine::Sdl2,
51 [](std::string_view device_id) -> std::unique_ptr<Sink> { 52 [](std::string_view device_id) -> std::unique_ptr<Sink> {
52 return std::make_unique<SDLSink>(device_id); 53 return std::make_unique<SDLSink>(device_id);
53 }, 54 },
@@ -55,46 +56,47 @@ constexpr SinkDetails sink_details[] = {
55 &GetSDLLatency, 56 &GetSDLLatency,
56 }, 57 },
57#endif 58#endif
58 SinkDetails{"null", 59 SinkDetails{Settings::AudioEngine::Null,
59 [](std::string_view device_id) -> std::unique_ptr<Sink> { 60 [](std::string_view device_id) -> std::unique_ptr<Sink> {
60 return std::make_unique<NullSink>(device_id); 61 return std::make_unique<NullSink>(device_id);
61 }, 62 },
62 [](bool capture) { return std::vector<std::string>{"null"}; }, []() { return 0u; }}, 63 [](bool capture) { return std::vector<std::string>{"null"}; }, []() { return 0u; }},
63}; 64};
64 65
65const SinkDetails& GetOutputSinkDetails(std::string_view sink_id) { 66const SinkDetails& GetOutputSinkDetails(Settings::AudioEngine sink_id) {
66 const auto find_backend{[](std::string_view id) { 67 const auto find_backend{[](Settings::AudioEngine id) {
67 return std::find_if(std::begin(sink_details), std::end(sink_details), 68 return std::find_if(std::begin(sink_details), std::end(sink_details),
68 [&id](const auto& sink_detail) { return sink_detail.id == id; }); 69 [&id](const auto& sink_detail) { return sink_detail.id == id; });
69 }}; 70 }};
70 71
71 auto iter = find_backend(sink_id); 72 auto iter = find_backend(sink_id);
72 73
73 if (sink_id == "auto") { 74 if (sink_id == Settings::AudioEngine::Auto) {
74 // Auto-select a backend. Prefer CubeB, but it may report a large minimum latency which 75 // Auto-select a backend. Prefer CubeB, but it may report a large minimum latency which
75 // causes audio issues, in that case go with SDL. 76 // causes audio issues, in that case go with SDL.
76#if defined(HAVE_CUBEB) && defined(HAVE_SDL2) 77#if defined(HAVE_CUBEB) && defined(HAVE_SDL2)
77 iter = find_backend("cubeb"); 78 iter = find_backend(Settings::AudioEngine::Cubeb);
78 if (iter->latency() > TargetSampleCount * 3) { 79 if (iter->latency() > TargetSampleCount * 3) {
79 iter = find_backend("sdl2"); 80 iter = find_backend(Settings::AudioEngine::Sdl2);
80 } 81 }
81#else 82#else
82 iter = std::begin(sink_details); 83 iter = std::begin(sink_details);
83#endif 84#endif
84 LOG_INFO(Service_Audio, "Auto-selecting the {} backend", iter->id); 85 LOG_INFO(Service_Audio, "Auto-selecting the {} backend",
86 Settings::CanonicalizeEnum(iter->id));
85 } 87 }
86 88
87 if (iter == std::end(sink_details)) { 89 if (iter == std::end(sink_details)) {
88 LOG_ERROR(Audio, "Invalid sink_id {}", sink_id); 90 LOG_ERROR(Audio, "Invalid sink_id {}", Settings::CanonicalizeEnum(sink_id));
89 iter = find_backend("null"); 91 iter = find_backend(Settings::AudioEngine::Null);
90 } 92 }
91 93
92 return *iter; 94 return *iter;
93} 95}
94} // Anonymous namespace 96} // Anonymous namespace
95 97
96std::vector<std::string_view> GetSinkIDs() { 98std::vector<Settings::AudioEngine> GetSinkIDs() {
97 std::vector<std::string_view> sink_ids(std::size(sink_details)); 99 std::vector<Settings::AudioEngine> sink_ids(std::size(sink_details));
98 100
99 std::transform(std::begin(sink_details), std::end(sink_details), std::begin(sink_ids), 101 std::transform(std::begin(sink_details), std::end(sink_details), std::begin(sink_ids),
100 [](const auto& sink) { return sink.id; }); 102 [](const auto& sink) { return sink.id; });
@@ -102,11 +104,11 @@ std::vector<std::string_view> GetSinkIDs() {
102 return sink_ids; 104 return sink_ids;
103} 105}
104 106
105std::vector<std::string> GetDeviceListForSink(std::string_view sink_id, bool capture) { 107std::vector<std::string> GetDeviceListForSink(Settings::AudioEngine sink_id, bool capture) {
106 return GetOutputSinkDetails(sink_id).list_devices(capture); 108 return GetOutputSinkDetails(sink_id).list_devices(capture);
107} 109}
108 110
109std::unique_ptr<Sink> CreateSinkFromID(std::string_view sink_id, std::string_view device_id) { 111std::unique_ptr<Sink> CreateSinkFromID(Settings::AudioEngine sink_id, std::string_view device_id) {
110 return GetOutputSinkDetails(sink_id).factory(device_id); 112 return GetOutputSinkDetails(sink_id).factory(device_id);
111} 113}
112 114
diff --git a/src/audio_core/sink/sink_details.h b/src/audio_core/sink/sink_details.h
index e75932898..c8498842b 100644
--- a/src/audio_core/sink/sink_details.h
+++ b/src/audio_core/sink/sink_details.h
@@ -3,9 +3,11 @@
3 3
4#pragma once 4#pragma once
5 5
6#include <memory>
6#include <string> 7#include <string>
7#include <string_view> 8#include <string_view>
8#include <vector> 9#include <vector>
10#include "common/settings_enums.h"
9 11
10namespace AudioCore { 12namespace AudioCore {
11class AudioManager; 13class AudioManager;
@@ -19,7 +21,7 @@ class Sink;
19 * 21 *
20 * @return Vector of available sink names. 22 * @return Vector of available sink names.
21 */ 23 */
22std::vector<std::string_view> GetSinkIDs(); 24std::vector<Settings::AudioEngine> GetSinkIDs();
23 25
24/** 26/**
25 * Gets the list of devices for a particular sink identified by the given ID. 27 * Gets the list of devices for a particular sink identified by the given ID.
@@ -28,7 +30,7 @@ std::vector<std::string_view> GetSinkIDs();
28 * @param capture - Get capture (input) devices, or output devices? 30 * @param capture - Get capture (input) devices, or output devices?
29 * @return Vector of device names. 31 * @return Vector of device names.
30 */ 32 */
31std::vector<std::string> GetDeviceListForSink(std::string_view sink_id, bool capture); 33std::vector<std::string> GetDeviceListForSink(Settings::AudioEngine sink_id, bool capture);
32 34
33/** 35/**
34 * Creates an audio sink identified by the given device ID. 36 * 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
37 * @param device_id - Name of the device to create. 39 * @param device_id - Name of the device to create.
38 * @return Pointer to the created sink. 40 * @return Pointer to the created sink.
39 */ 41 */
40std::unique_ptr<Sink> CreateSinkFromID(std::string_view sink_id, std::string_view device_id); 42std::unique_ptr<Sink> CreateSinkFromID(Settings::AudioEngine sink_id, std::string_view device_id);
41 43
42} // namespace Sink 44} // namespace Sink
43} // namespace AudioCore 45} // namespace AudioCore
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index 3adf13a3f..bf97d9ba2 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -110,8 +110,12 @@ add_library(common STATIC
110 scratch_buffer.h 110 scratch_buffer.h
111 settings.cpp 111 settings.cpp
112 settings.h 112 settings.h
113 settings_common.cpp
114 settings_common.h
115 settings_enums.h
113 settings_input.cpp 116 settings_input.cpp
114 settings_input.h 117 settings_input.h
118 settings_setting.h
115 socket_types.h 119 socket_types.h
116 spin_lock.cpp 120 spin_lock.cpp
117 spin_lock.h 121 spin_lock.h
@@ -193,9 +197,16 @@ if (MSVC)
193 /we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data 197 /we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data
194 /we4800 # Implicit conversion from 'type' to bool. Possible information loss 198 /we4800 # Implicit conversion from 'type' to bool. Possible information loss
195 ) 199 )
196else() 200endif()
201
202if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
197 target_compile_options(common PRIVATE 203 target_compile_options(common PRIVATE
198 $<$<CXX_COMPILER_ID:Clang>:-fsized-deallocation> 204 -fsized-deallocation
205 -Werror=unreachable-code-aggressive
206 )
207 target_compile_definitions(common PRIVATE
208 # Clang 14 and earlier have errors when explicitly instantiating Settings::Setting
209 $<$<VERSION_LESS:$<CXX_COMPILER_VERSION>,15>:CANNOT_EXPLICITLY_INSTANTIATE>
199 ) 210 )
200endif() 211endif()
201 212
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp
index 6e8e8eb36..d4f27197c 100644
--- a/src/common/logging/backend.cpp
+++ b/src/common/logging/backend.cpp
@@ -108,7 +108,7 @@ public:
108 108
109 using namespace Common::Literals; 109 using namespace Common::Literals;
110 // Prevent logs from exceeding a set maximum size in the event that log entries are spammed. 110 // Prevent logs from exceeding a set maximum size in the event that log entries are spammed.
111 const auto write_limit = Settings::values.extended_logging ? 1_GiB : 100_MiB; 111 const auto write_limit = Settings::values.extended_logging.GetValue() ? 1_GiB : 100_MiB;
112 const bool write_limit_exceeded = bytes_written > write_limit; 112 const bool write_limit_exceeded = bytes_written > write_limit;
113 if (entry.log_level >= Level::Error || write_limit_exceeded) { 113 if (entry.log_level >= Level::Error || write_limit_exceeded) {
114 if (write_limit_exceeded) { 114 if (write_limit_exceeded) {
diff --git a/src/common/settings.cpp b/src/common/settings.cpp
index d4e55f988..15fd2e222 100644
--- a/src/common/settings.cpp
+++ b/src/common/settings.cpp
@@ -7,9 +7,16 @@
7#include <exception> 7#include <exception>
8#include <stdexcept> 8#include <stdexcept>
9#endif 9#endif
10#include <compare>
11#include <cstddef>
12#include <filesystem>
13#include <functional>
10#include <string_view> 14#include <string_view>
15#include <type_traits>
16#include <fmt/core.h>
11 17
12#include "common/assert.h" 18#include "common/assert.h"
19#include "common/fs/fs_util.h"
13#include "common/fs/path_util.h" 20#include "common/fs/path_util.h"
14#include "common/logging/log.h" 21#include "common/logging/log.h"
15#include "common/settings.h" 22#include "common/settings.h"
@@ -17,11 +24,50 @@
17 24
18namespace Settings { 25namespace Settings {
19 26
27// Clang 14 and earlier have errors when explicitly instantiating these classes
28#ifndef CANNOT_EXPLICITLY_INSTANTIATE
29#define SETTING(TYPE, RANGED) template class Setting<TYPE, RANGED>
30#define SWITCHABLE(TYPE, RANGED) template class SwitchableSetting<TYPE, RANGED>
31
32SETTING(AudioEngine, false);
33SETTING(bool, false);
34SETTING(int, false);
35SETTING(std::string, false);
36SETTING(u16, false);
37SWITCHABLE(AnisotropyMode, true);
38SWITCHABLE(AntiAliasing, false);
39SWITCHABLE(AspectRatio, true);
40SWITCHABLE(AstcDecodeMode, true);
41SWITCHABLE(AstcRecompression, true);
42SWITCHABLE(AudioMode, true);
43SWITCHABLE(CpuAccuracy, true);
44SWITCHABLE(FullscreenMode, true);
45SWITCHABLE(GpuAccuracy, true);
46SWITCHABLE(Language, true);
47SWITCHABLE(NvdecEmulation, false);
48SWITCHABLE(Region, true);
49SWITCHABLE(RendererBackend, true);
50SWITCHABLE(ScalingFilter, false);
51SWITCHABLE(ShaderBackend, true);
52SWITCHABLE(TimeZone, true);
53SETTING(VSyncMode, true);
54SWITCHABLE(bool, false);
55SWITCHABLE(int, false);
56SWITCHABLE(int, true);
57SWITCHABLE(s64, false);
58SWITCHABLE(u16, true);
59SWITCHABLE(u32, false);
60SWITCHABLE(u8, false);
61SWITCHABLE(u8, true);
62
63#undef SETTING
64#undef SWITCHABLE
65#endif
66
20Values values; 67Values values;
21static bool configuring_global = true;
22 68
23std::string GetTimeZoneString() { 69std::string GetTimeZoneString(TimeZone time_zone) {
24 const auto time_zone_index = static_cast<std::size_t>(values.time_zone_index.GetValue()); 70 const auto time_zone_index = static_cast<std::size_t>(time_zone);
25 ASSERT(time_zone_index < Common::TimeZone::GetTimeZoneStrings().size()); 71 ASSERT(time_zone_index < Common::TimeZone::GetTimeZoneStrings().size());
26 72
27 std::string location_name; 73 std::string location_name;
@@ -61,73 +107,35 @@ void LogSettings() {
61 }; 107 };
62 108
63 LOG_INFO(Config, "yuzu Configuration:"); 109 LOG_INFO(Config, "yuzu Configuration:");
64 log_setting("Controls_UseDockedMode", values.use_docked_mode.GetValue()); 110 for (auto& [category, settings] : values.linkage.by_category) {
65 log_setting("System_RngSeed", values.rng_seed.GetValue().value_or(0)); 111 for (const auto& setting : settings) {
66 log_setting("System_DeviceName", values.device_name.GetValue()); 112 if (setting->Id() == values.yuzu_token.Id()) {
67 log_setting("System_CurrentUser", values.current_user.GetValue()); 113 // Hide the token secret, for security reasons.
68 log_setting("System_LanguageIndex", values.language_index.GetValue()); 114 continue;
69 log_setting("System_RegionIndex", values.region_index.GetValue()); 115 }
70 log_setting("System_TimeZoneIndex", values.time_zone_index.GetValue()); 116
71 log_setting("System_UnsafeMemoryLayout", values.use_unsafe_extended_memory_layout.GetValue()); 117 const auto name = fmt::format(
72 log_setting("Core_UseMultiCore", values.use_multi_core.GetValue()); 118 "{:c}{:c} {}.{}", setting->ToString() == setting->DefaultToString() ? '-' : 'M',
73 log_setting("CPU_Accuracy", values.cpu_accuracy.GetValue()); 119 setting->UsingGlobal() ? '-' : 'C', TranslateCategory(category),
74 log_setting("Renderer_UseResolutionScaling", values.resolution_setup.GetValue()); 120 setting->GetLabel());
75 log_setting("Renderer_ScalingFilter", values.scaling_filter.GetValue()); 121
76 log_setting("Renderer_FSRSlider", values.fsr_sharpening_slider.GetValue()); 122 log_setting(name, setting->Canonicalize());
77 log_setting("Renderer_AntiAliasing", values.anti_aliasing.GetValue()); 123 }
78 log_setting("Renderer_UseSpeedLimit", values.use_speed_limit.GetValue()); 124 }
79 log_setting("Renderer_SpeedLimit", values.speed_limit.GetValue());
80 log_setting("Renderer_UseDiskShaderCache", values.use_disk_shader_cache.GetValue());
81 log_setting("Renderer_GPUAccuracyLevel", values.gpu_accuracy.GetValue());
82 log_setting("Renderer_UseAsynchronousGpuEmulation",
83 values.use_asynchronous_gpu_emulation.GetValue());
84 log_setting("Renderer_NvdecEmulation", values.nvdec_emulation.GetValue());
85 log_setting("Renderer_AccelerateASTC", values.accelerate_astc.GetValue());
86 log_setting("Renderer_AsyncASTC", values.async_astc.GetValue());
87 log_setting("Renderer_AstcRecompression", values.astc_recompression.GetValue());
88 log_setting("Renderer_UseVsync", values.vsync_mode.GetValue());
89 log_setting("Renderer_UseReactiveFlushing", values.use_reactive_flushing.GetValue());
90 log_setting("Renderer_ShaderBackend", values.shader_backend.GetValue());
91 log_setting("Renderer_UseAsynchronousShaders", values.use_asynchronous_shaders.GetValue());
92 log_setting("Renderer_AnisotropicFilteringLevel", values.max_anisotropy.GetValue());
93 log_setting("Audio_OutputEngine", values.sink_id.GetValue());
94 log_setting("Audio_OutputDevice", values.audio_output_device_id.GetValue());
95 log_setting("Audio_InputDevice", values.audio_input_device_id.GetValue());
96 log_setting("DataStorage_UseVirtualSd", values.use_virtual_sd.GetValue());
97 log_path("DataStorage_CacheDir", Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir)); 125 log_path("DataStorage_CacheDir", Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir));
98 log_path("DataStorage_ConfigDir", Common::FS::GetYuzuPath(Common::FS::YuzuPath::ConfigDir)); 126 log_path("DataStorage_ConfigDir", Common::FS::GetYuzuPath(Common::FS::YuzuPath::ConfigDir));
99 log_path("DataStorage_LoadDir", Common::FS::GetYuzuPath(Common::FS::YuzuPath::LoadDir)); 127 log_path("DataStorage_LoadDir", Common::FS::GetYuzuPath(Common::FS::YuzuPath::LoadDir));
100 log_path("DataStorage_NANDDir", Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir)); 128 log_path("DataStorage_NANDDir", Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir));
101 log_path("DataStorage_SDMCDir", Common::FS::GetYuzuPath(Common::FS::YuzuPath::SDMCDir)); 129 log_path("DataStorage_SDMCDir", Common::FS::GetYuzuPath(Common::FS::YuzuPath::SDMCDir));
102 log_setting("Debugging_ProgramArgs", values.program_args.GetValue());
103 log_setting("Debugging_GDBStub", values.use_gdbstub.GetValue());
104 log_setting("Input_EnableMotion", values.motion_enabled.GetValue());
105 log_setting("Input_EnableVibration", values.vibration_enabled.GetValue());
106 log_setting("Input_EnableTouch", values.touchscreen.enabled);
107 log_setting("Input_EnableMouse", values.mouse_enabled.GetValue());
108 log_setting("Input_EnableKeyboard", values.keyboard_enabled.GetValue());
109 log_setting("Input_EnableRingController", values.enable_ring_controller.GetValue());
110 log_setting("Input_EnableIrSensor", values.enable_ir_sensor.GetValue());
111 log_setting("Input_EnableCustomJoycon", values.enable_joycon_driver.GetValue());
112 log_setting("Input_EnableCustomProController", values.enable_procon_driver.GetValue());
113 log_setting("Input_EnableRawInput", values.enable_raw_input.GetValue());
114}
115
116bool IsConfiguringGlobal() {
117 return configuring_global;
118}
119
120void SetConfiguringGlobal(bool is_global) {
121 configuring_global = is_global;
122} 130}
123 131
124bool IsGPULevelExtreme() { 132bool IsGPULevelExtreme() {
125 return values.gpu_accuracy.GetValue() == GPUAccuracy::Extreme; 133 return values.gpu_accuracy.GetValue() == GpuAccuracy::Extreme;
126} 134}
127 135
128bool IsGPULevelHigh() { 136bool IsGPULevelHigh() {
129 return values.gpu_accuracy.GetValue() == GPUAccuracy::Extreme || 137 return values.gpu_accuracy.GetValue() == GpuAccuracy::Extreme ||
130 values.gpu_accuracy.GetValue() == GPUAccuracy::High; 138 values.gpu_accuracy.GetValue() == GpuAccuracy::High;
131} 139}
132 140
133bool IsFastmemEnabled() { 141bool IsFastmemEnabled() {
@@ -144,6 +152,61 @@ float Volume() {
144 return values.volume.GetValue() / static_cast<f32>(values.volume.GetDefault()); 152 return values.volume.GetValue() / static_cast<f32>(values.volume.GetDefault());
145} 153}
146 154
155const char* TranslateCategory(Category category) {
156 switch (category) {
157 case Category::Audio:
158 return "Audio";
159 case Category::Core:
160 return "Core";
161 case Category::Cpu:
162 case Category::CpuDebug:
163 case Category::CpuUnsafe:
164 return "Cpu";
165 case Category::Renderer:
166 case Category::RendererAdvanced:
167 case Category::RendererDebug:
168 return "Renderer";
169 case Category::System:
170 case Category::SystemAudio:
171 return "System";
172 case Category::DataStorage:
173 return "Data Storage";
174 case Category::Debugging:
175 case Category::DebuggingGraphics:
176 return "Debugging";
177 case Category::Miscellaneous:
178 return "Miscellaneous";
179 case Category::Network:
180 return "Network";
181 case Category::WebService:
182 return "WebService";
183 case Category::AddOns:
184 return "DisabledAddOns";
185 case Category::Controls:
186 return "Controls";
187 case Category::Ui:
188 case Category::UiGeneral:
189 return "UI";
190 case Category::UiLayout:
191 return "UiLayout";
192 case Category::UiGameList:
193 return "UiGameList";
194 case Category::Screenshots:
195 return "Screenshots";
196 case Category::Shortcuts:
197 return "Shortcuts";
198 case Category::Multiplayer:
199 return "Multiplayer";
200 case Category::Services:
201 return "Services";
202 case Category::Paths:
203 return "Paths";
204 case Category::MaxEnum:
205 break;
206 }
207 return "Miscellaneous";
208}
209
147void UpdateRescalingInfo() { 210void UpdateRescalingInfo() {
148 const auto setup = values.resolution_setup.GetValue(); 211 const auto setup = values.resolution_setup.GetValue();
149 auto& info = values.resolution_info; 212 auto& info = values.resolution_info;
@@ -212,66 +275,19 @@ void RestoreGlobalState(bool is_powered_on) {
212 return; 275 return;
213 } 276 }
214 277
215 // Audio 278 for (const auto& reset : values.linkage.restore_functions) {
216 values.volume.SetGlobal(true); 279 reset();
217 280 }
218 // Core 281}
219 values.use_multi_core.SetGlobal(true);
220 values.use_unsafe_extended_memory_layout.SetGlobal(true);
221
222 // CPU
223 values.cpu_accuracy.SetGlobal(true);
224 values.cpuopt_unsafe_unfuse_fma.SetGlobal(true);
225 values.cpuopt_unsafe_reduce_fp_error.SetGlobal(true);
226 values.cpuopt_unsafe_ignore_standard_fpcr.SetGlobal(true);
227 values.cpuopt_unsafe_inaccurate_nan.SetGlobal(true);
228 values.cpuopt_unsafe_fastmem_check.SetGlobal(true);
229 values.cpuopt_unsafe_ignore_global_monitor.SetGlobal(true);
230 282
231 // Renderer 283static bool configuring_global = true;
232 values.fsr_sharpening_slider.SetGlobal(true);
233 values.renderer_backend.SetGlobal(true);
234 values.async_presentation.SetGlobal(true);
235 values.renderer_force_max_clock.SetGlobal(true);
236 values.vulkan_device.SetGlobal(true);
237 values.fullscreen_mode.SetGlobal(true);
238 values.aspect_ratio.SetGlobal(true);
239 values.resolution_setup.SetGlobal(true);
240 values.scaling_filter.SetGlobal(true);
241 values.anti_aliasing.SetGlobal(true);
242 values.max_anisotropy.SetGlobal(true);
243 values.use_speed_limit.SetGlobal(true);
244 values.speed_limit.SetGlobal(true);
245 values.use_disk_shader_cache.SetGlobal(true);
246 values.gpu_accuracy.SetGlobal(true);
247 values.use_asynchronous_gpu_emulation.SetGlobal(true);
248 values.nvdec_emulation.SetGlobal(true);
249 values.accelerate_astc.SetGlobal(true);
250 values.async_astc.SetGlobal(true);
251 values.astc_recompression.SetGlobal(true);
252 values.use_reactive_flushing.SetGlobal(true);
253 values.shader_backend.SetGlobal(true);
254 values.use_asynchronous_shaders.SetGlobal(true);
255 values.use_fast_gpu_time.SetGlobal(true);
256 values.use_vulkan_driver_pipeline_cache.SetGlobal(true);
257 values.bg_red.SetGlobal(true);
258 values.bg_green.SetGlobal(true);
259 values.bg_blue.SetGlobal(true);
260 values.enable_compute_pipelines.SetGlobal(true);
261 values.use_video_framerate.SetGlobal(true);
262 284
263 // System 285bool IsConfiguringGlobal() {
264 values.language_index.SetGlobal(true); 286 return configuring_global;
265 values.region_index.SetGlobal(true); 287}
266 values.time_zone_index.SetGlobal(true);
267 values.rng_seed.SetGlobal(true);
268 values.sound_index.SetGlobal(true);
269 288
270 // Controls 289void SetConfiguringGlobal(bool is_global) {
271 values.players.SetGlobal(true); 290 configuring_global = is_global;
272 values.use_docked_mode.SetGlobal(true);
273 values.vibration_enabled.SetGlobal(true);
274 values.motion_enabled.SetGlobal(true);
275} 291}
276 292
277} // namespace Settings 293} // namespace Settings
diff --git a/src/common/settings.h b/src/common/settings.h
index 59e96e74f..b0bc6519a 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -6,95 +6,21 @@
6#include <algorithm> 6#include <algorithm>
7#include <array> 7#include <array>
8#include <map> 8#include <map>
9#include <optional> 9#include <memory>
10#include <stdexcept>
10#include <string> 11#include <string>
11#include <utility> 12#include <utility>
12#include <vector> 13#include <vector>
13 14
14#include "common/common_types.h" 15#include "common/common_types.h"
16#include "common/settings_common.h"
17#include "common/settings_enums.h"
15#include "common/settings_input.h" 18#include "common/settings_input.h"
19#include "common/settings_setting.h"
16 20
17namespace Settings { 21namespace Settings {
18 22
19enum class VSyncMode : u32 { 23const char* TranslateCategory(Settings::Category category);
20 Immediate = 0,
21 Mailbox = 1,
22 FIFO = 2,
23 FIFORelaxed = 3,
24};
25
26enum class RendererBackend : u32 {
27 OpenGL = 0,
28 Vulkan = 1,
29 Null = 2,
30};
31
32enum class ShaderBackend : u32 {
33 GLSL = 0,
34 GLASM = 1,
35 SPIRV = 2,
36};
37
38enum class GPUAccuracy : u32 {
39 Normal = 0,
40 High = 1,
41 Extreme = 2,
42};
43
44enum class CPUAccuracy : u32 {
45 Auto = 0,
46 Accurate = 1,
47 Unsafe = 2,
48 Paranoid = 3,
49};
50
51enum class FullscreenMode : u32 {
52 Borderless = 0,
53 Exclusive = 1,
54};
55
56enum class NvdecEmulation : u32 {
57 Off = 0,
58 CPU = 1,
59 GPU = 2,
60};
61
62enum class ResolutionSetup : u32 {
63 Res1_2X = 0,
64 Res3_4X = 1,
65 Res1X = 2,
66 Res3_2X = 3,
67 Res2X = 4,
68 Res3X = 5,
69 Res4X = 6,
70 Res5X = 7,
71 Res6X = 8,
72 Res7X = 9,
73 Res8X = 10,
74};
75
76enum class ScalingFilter : u32 {
77 NearestNeighbor = 0,
78 Bilinear = 1,
79 Bicubic = 2,
80 Gaussian = 3,
81 ScaleForce = 4,
82 Fsr = 5,
83 LastFilter = Fsr,
84};
85
86enum class AntiAliasing : u32 {
87 None = 0,
88 Fxaa = 1,
89 Smaa = 2,
90 LastAA = Smaa,
91};
92
93enum class AstcRecompression : u32 {
94 Uncompressed = 0,
95 Bc1 = 1,
96 Bc3 = 2,
97};
98 24
99struct ResolutionScalingInfo { 25struct ResolutionScalingInfo {
100 u32 up_scale{1}; 26 u32 up_scale{1};
@@ -119,239 +45,47 @@ struct ResolutionScalingInfo {
119 } 45 }
120}; 46};
121 47
122/** The Setting class is a simple resource manager. It defines a label and default value alongside 48#ifndef CANNOT_EXPLICITLY_INSTANTIATE
123 * the actual value of the setting for simpler and less-error prone use with frontend 49// Instantiate the classes elsewhere (settings.cpp) to reduce compiler/linker work
124 * configurations. Specifying a default value and label is required. A minimum and maximum range can 50#define SETTING(TYPE, RANGED) extern template class Setting<TYPE, RANGED>
125 * be specified for sanitization. 51#define SWITCHABLE(TYPE, RANGED) extern template class SwitchableSetting<TYPE, RANGED>
126 */ 52
127template <typename Type, bool ranged = false> 53SETTING(AudioEngine, false);
128class Setting { 54SETTING(bool, false);
129protected: 55SETTING(int, false);
130 Setting() = default; 56SETTING(s32, false);
131 57SETTING(std::string, false);
132 /** 58SETTING(std::string, false);
133 * Only sets the setting to the given initializer, leaving the other members to their default 59SETTING(u16, false);
134 * initializers. 60SWITCHABLE(AnisotropyMode, true);
135 * 61SWITCHABLE(AntiAliasing, false);
136 * @param global_val Initial value of the setting 62SWITCHABLE(AspectRatio, true);
137 */ 63SWITCHABLE(AstcDecodeMode, true);
138 explicit Setting(const Type& val) : value{val} {} 64SWITCHABLE(AstcRecompression, true);
139 65SWITCHABLE(AudioMode, true);
140public: 66SWITCHABLE(CpuAccuracy, true);
141 /** 67SWITCHABLE(FullscreenMode, true);
142 * Sets a default value, label, and setting value. 68SWITCHABLE(GpuAccuracy, true);
143 * 69SWITCHABLE(Language, true);
144 * @param default_val Initial value of the setting, and default value of the setting 70SWITCHABLE(NvdecEmulation, false);
145 * @param name Label for the setting 71SWITCHABLE(Region, true);
146 */ 72SWITCHABLE(RendererBackend, true);
147 explicit Setting(const Type& default_val, const std::string& name) 73SWITCHABLE(ScalingFilter, false);
148 requires(!ranged) 74SWITCHABLE(ShaderBackend, true);
149 : value{default_val}, default_value{default_val}, label{name} {} 75SWITCHABLE(TimeZone, true);
150 virtual ~Setting() = default; 76SETTING(VSyncMode, true);
151 77SWITCHABLE(bool, false);
152 /** 78SWITCHABLE(int, false);
153 * Sets a default value, minimum value, maximum value, and label. 79SWITCHABLE(int, true);
154 * 80SWITCHABLE(s64, false);
155 * @param default_val Initial value of the setting, and default value of the setting 81SWITCHABLE(u16, true);
156 * @param min_val Sets the minimum allowed value of the setting 82SWITCHABLE(u32, false);
157 * @param max_val Sets the maximum allowed value of the setting 83SWITCHABLE(u8, false);
158 * @param name Label for the setting 84SWITCHABLE(u8, true);
159 */ 85
160 explicit Setting(const Type& default_val, const Type& min_val, const Type& max_val, 86#undef SETTING
161 const std::string& name) 87#undef SWITCHABLE
162 requires(ranged) 88#endif
163 : value{default_val},
164 default_value{default_val}, maximum{max_val}, minimum{min_val}, label{name} {}
165
166 /**
167 * Returns a reference to the setting's value.
168 *
169 * @returns A reference to the setting
170 */
171 [[nodiscard]] virtual const Type& GetValue() const {
172 return value;
173 }
174
175 /**
176 * Sets the setting to the given value.
177 *
178 * @param val The desired value
179 */
180 virtual void SetValue(const Type& val) {
181 Type temp{ranged ? std::clamp(val, minimum, maximum) : val};
182 std::swap(value, temp);
183 }
184
185 /**
186 * Returns the value that this setting was created with.
187 *
188 * @returns A reference to the default value
189 */
190 [[nodiscard]] const Type& GetDefault() const {
191 return default_value;
192 }
193
194 /**
195 * Returns the label this setting was created with.
196 *
197 * @returns A reference to the label
198 */
199 [[nodiscard]] const std::string& GetLabel() const {
200 return label;
201 }
202
203 /**
204 * Assigns a value to the setting.
205 *
206 * @param val The desired setting value
207 *
208 * @returns A reference to the setting
209 */
210 virtual const Type& operator=(const Type& val) {
211 Type temp{ranged ? std::clamp(val, minimum, maximum) : val};
212 std::swap(value, temp);
213 return value;
214 }
215
216 /**
217 * Returns a reference to the setting.
218 *
219 * @returns A reference to the setting
220 */
221 explicit virtual operator const Type&() const {
222 return value;
223 }
224
225protected:
226 Type value{}; ///< The setting
227 const Type default_value{}; ///< The default value
228 const Type maximum{}; ///< Maximum allowed value of the setting
229 const Type minimum{}; ///< Minimum allowed value of the setting
230 const std::string label{}; ///< The setting's label
231};
232
233/**
234 * The SwitchableSetting class is a slightly more complex version of the Setting class. This adds a
235 * custom setting to switch to when a guest application specifically requires it. The effect is that
236 * other components of the emulator can access the setting's intended value without any need for the
237 * component to ask whether the custom or global setting is needed at the moment.
238 *
239 * By default, the global setting is used.
240 */
241template <typename Type, bool ranged = false>
242class SwitchableSetting : virtual public Setting<Type, ranged> {
243public:
244 /**
245 * Sets a default value, label, and setting value.
246 *
247 * @param default_val Initial value of the setting, and default value of the setting
248 * @param name Label for the setting
249 */
250 explicit SwitchableSetting(const Type& default_val, const std::string& name)
251 requires(!ranged)
252 : Setting<Type>{default_val, name} {}
253 virtual ~SwitchableSetting() = default;
254
255 /**
256 * Sets a default value, minimum value, maximum value, and label.
257 *
258 * @param default_val Initial value of the setting, and default value of the setting
259 * @param min_val Sets the minimum allowed value of the setting
260 * @param max_val Sets the maximum allowed value of the setting
261 * @param name Label for the setting
262 */
263 explicit SwitchableSetting(const Type& default_val, const Type& min_val, const Type& max_val,
264 const std::string& name)
265 requires(ranged)
266 : Setting<Type, true>{default_val, min_val, max_val, name} {}
267
268 /**
269 * Tells this setting to represent either the global or custom setting when other member
270 * functions are used.
271 *
272 * @param to_global Whether to use the global or custom setting.
273 */
274 void SetGlobal(bool to_global) {
275 use_global = to_global;
276 }
277
278 /**
279 * Returns whether this setting is using the global setting or not.
280 *
281 * @returns The global state
282 */
283 [[nodiscard]] bool UsingGlobal() const {
284 return use_global;
285 }
286
287 /**
288 * Returns either the global or custom setting depending on the values of this setting's global
289 * state or if the global value was specifically requested.
290 *
291 * @param need_global Request global value regardless of setting's state; defaults to false
292 *
293 * @returns The required value of the setting
294 */
295 [[nodiscard]] virtual const Type& GetValue() const override {
296 if (use_global) {
297 return this->value;
298 }
299 return custom;
300 }
301 [[nodiscard]] virtual const Type& GetValue(bool need_global) const {
302 if (use_global || need_global) {
303 return this->value;
304 }
305 return custom;
306 }
307
308 /**
309 * Sets the current setting value depending on the global state.
310 *
311 * @param val The new value
312 */
313 void SetValue(const Type& val) override {
314 Type temp{ranged ? std::clamp(val, this->minimum, this->maximum) : val};
315 if (use_global) {
316 std::swap(this->value, temp);
317 } else {
318 std::swap(custom, temp);
319 }
320 }
321
322 /**
323 * Assigns the current setting value depending on the global state.
324 *
325 * @param val The new value
326 *
327 * @returns A reference to the current setting value
328 */
329 const Type& operator=(const Type& val) override {
330 Type temp{ranged ? std::clamp(val, this->minimum, this->maximum) : val};
331 if (use_global) {
332 std::swap(this->value, temp);
333 return this->value;
334 }
335 std::swap(custom, temp);
336 return custom;
337 }
338
339 /**
340 * Returns the current setting value depending on the global state.
341 *
342 * @returns A reference to the current setting value
343 */
344 virtual explicit operator const Type&() const override {
345 if (use_global) {
346 return this->value;
347 }
348 return custom;
349 }
350
351protected:
352 bool use_global{true}; ///< The setting's global state
353 Type custom{}; ///< The custom value of the setting
354};
355 89
356/** 90/**
357 * The InputSetting class allows for getting a reference to either the global or custom members. 91 * The InputSetting class allows for getting a reference to either the global or custom members.
@@ -391,208 +125,388 @@ struct TouchFromButtonMap {
391}; 125};
392 126
393struct Values { 127struct Values {
128 Linkage linkage{};
129
394 // Audio 130 // Audio
395 Setting<std::string> sink_id{"auto", "output_engine"}; 131 Setting<AudioEngine> sink_id{linkage, AudioEngine::Auto, "output_engine", Category::Audio,
396 Setting<std::string> audio_output_device_id{"auto", "output_device"}; 132 Specialization::RuntimeList};
397 Setting<std::string> audio_input_device_id{"auto", "input_device"}; 133 Setting<std::string> audio_output_device_id{linkage, "auto", "output_device", Category::Audio,
398 Setting<bool> audio_muted{false, "audio_muted"}; 134 Specialization::RuntimeList};
399 SwitchableSetting<u8, true> volume{100, 0, 200, "volume"}; 135 Setting<std::string> audio_input_device_id{linkage, "auto", "input_device", Category::Audio,
400 Setting<bool> dump_audio_commands{false, "dump_audio_commands"}; 136 Specialization::RuntimeList};
137 SwitchableSetting<AudioMode, true> sound_index{
138 linkage, AudioMode::Stereo, AudioMode::Mono, AudioMode::Surround,
139 "sound_index", Category::SystemAudio, Specialization::Default, true,
140 true};
141 SwitchableSetting<u8, true> volume{linkage,
142 100,
143 0,
144 200,
145 "volume",
146 Category::Audio,
147 Specialization::Scalar | Specialization::Percentage,
148 true,
149 true};
150 Setting<bool, false> audio_muted{
151 linkage, false, "audio_muted", Category::Audio, Specialization::Default, false, true};
152 Setting<bool, false> dump_audio_commands{
153 linkage, false, "dump_audio_commands", Category::Audio, Specialization::Default, false};
401 154
402 // Core 155 // Core
403 SwitchableSetting<bool> use_multi_core{true, "use_multi_core"}; 156 SwitchableSetting<bool> use_multi_core{linkage, true, "use_multi_core", Category::Core};
404 SwitchableSetting<bool> use_unsafe_extended_memory_layout{false, 157 SwitchableSetting<MemoryLayout, true> memory_layout_mode{linkage,
405 "use_unsafe_extended_memory_layout"}; 158 MemoryLayout::Memory_4Gb,
159 MemoryLayout::Memory_4Gb,
160 MemoryLayout::Memory_8Gb,
161 "memory_layout_mode",
162 Category::Core};
163 SwitchableSetting<bool> use_speed_limit{
164 linkage, true, "use_speed_limit", Category::Core, Specialization::Paired, false, true};
165 SwitchableSetting<u16, true> speed_limit{linkage,
166 100,
167 0,
168 9999,
169 "speed_limit",
170 Category::Core,
171 Specialization::Countable | Specialization::Percentage,
172 true,
173 true,
174 &use_speed_limit};
406 175
407 // Cpu 176 // Cpu
408 SwitchableSetting<CPUAccuracy, true> cpu_accuracy{CPUAccuracy::Auto, CPUAccuracy::Auto, 177 SwitchableSetting<CpuAccuracy, true> cpu_accuracy{linkage, CpuAccuracy::Auto,
409 CPUAccuracy::Paranoid, "cpu_accuracy"}; 178 CpuAccuracy::Auto, CpuAccuracy::Paranoid,
410 // TODO: remove cpu_accuracy_first_time, migration setting added 8 July 2021 179 "cpu_accuracy", Category::Cpu};
411 Setting<bool> cpu_accuracy_first_time{true, "cpu_accuracy_first_time"}; 180 Setting<bool> cpu_debug_mode{linkage, false, "cpu_debug_mode", Category::CpuDebug};
412 Setting<bool> cpu_debug_mode{false, "cpu_debug_mode"}; 181
413 182 Setting<bool> cpuopt_page_tables{linkage, true, "cpuopt_page_tables", Category::CpuDebug};
414 Setting<bool> cpuopt_page_tables{true, "cpuopt_page_tables"}; 183 Setting<bool> cpuopt_block_linking{linkage, true, "cpuopt_block_linking", Category::CpuDebug};
415 Setting<bool> cpuopt_block_linking{true, "cpuopt_block_linking"}; 184 Setting<bool> cpuopt_return_stack_buffer{linkage, true, "cpuopt_return_stack_buffer",
416 Setting<bool> cpuopt_return_stack_buffer{true, "cpuopt_return_stack_buffer"}; 185 Category::CpuDebug};
417 Setting<bool> cpuopt_fast_dispatcher{true, "cpuopt_fast_dispatcher"}; 186 Setting<bool> cpuopt_fast_dispatcher{linkage, true, "cpuopt_fast_dispatcher",
418 Setting<bool> cpuopt_context_elimination{true, "cpuopt_context_elimination"}; 187 Category::CpuDebug};
419 Setting<bool> cpuopt_const_prop{true, "cpuopt_const_prop"}; 188 Setting<bool> cpuopt_context_elimination{linkage, true, "cpuopt_context_elimination",
420 Setting<bool> cpuopt_misc_ir{true, "cpuopt_misc_ir"}; 189 Category::CpuDebug};
421 Setting<bool> cpuopt_reduce_misalign_checks{true, "cpuopt_reduce_misalign_checks"}; 190 Setting<bool> cpuopt_const_prop{linkage, true, "cpuopt_const_prop", Category::CpuDebug};
422 Setting<bool> cpuopt_fastmem{true, "cpuopt_fastmem"}; 191 Setting<bool> cpuopt_misc_ir{linkage, true, "cpuopt_misc_ir", Category::CpuDebug};
423 Setting<bool> cpuopt_fastmem_exclusives{true, "cpuopt_fastmem_exclusives"}; 192 Setting<bool> cpuopt_reduce_misalign_checks{linkage, true, "cpuopt_reduce_misalign_checks",
424 Setting<bool> cpuopt_recompile_exclusives{true, "cpuopt_recompile_exclusives"}; 193 Category::CpuDebug};
425 Setting<bool> cpuopt_ignore_memory_aborts{true, "cpuopt_ignore_memory_aborts"}; 194 Setting<bool> cpuopt_fastmem{linkage, true, "cpuopt_fastmem", Category::CpuDebug};
426 195 Setting<bool> cpuopt_fastmem_exclusives{linkage, true, "cpuopt_fastmem_exclusives",
427 SwitchableSetting<bool> cpuopt_unsafe_unfuse_fma{true, "cpuopt_unsafe_unfuse_fma"}; 196 Category::CpuDebug};
428 SwitchableSetting<bool> cpuopt_unsafe_reduce_fp_error{true, "cpuopt_unsafe_reduce_fp_error"}; 197 Setting<bool> cpuopt_recompile_exclusives{linkage, true, "cpuopt_recompile_exclusives",
198 Category::CpuDebug};
199 Setting<bool> cpuopt_ignore_memory_aborts{linkage, true, "cpuopt_ignore_memory_aborts",
200 Category::CpuDebug};
201
202 SwitchableSetting<bool> cpuopt_unsafe_unfuse_fma{linkage, true, "cpuopt_unsafe_unfuse_fma",
203 Category::CpuUnsafe};
204 SwitchableSetting<bool> cpuopt_unsafe_reduce_fp_error{
205 linkage, true, "cpuopt_unsafe_reduce_fp_error", Category::CpuUnsafe};
429 SwitchableSetting<bool> cpuopt_unsafe_ignore_standard_fpcr{ 206 SwitchableSetting<bool> cpuopt_unsafe_ignore_standard_fpcr{
430 true, "cpuopt_unsafe_ignore_standard_fpcr"}; 207 linkage, true, "cpuopt_unsafe_ignore_standard_fpcr", Category::CpuUnsafe};
431 SwitchableSetting<bool> cpuopt_unsafe_inaccurate_nan{true, "cpuopt_unsafe_inaccurate_nan"}; 208 SwitchableSetting<bool> cpuopt_unsafe_inaccurate_nan{
432 SwitchableSetting<bool> cpuopt_unsafe_fastmem_check{true, "cpuopt_unsafe_fastmem_check"}; 209 linkage, true, "cpuopt_unsafe_inaccurate_nan", Category::CpuUnsafe};
210 SwitchableSetting<bool> cpuopt_unsafe_fastmem_check{
211 linkage, true, "cpuopt_unsafe_fastmem_check", Category::CpuUnsafe};
433 SwitchableSetting<bool> cpuopt_unsafe_ignore_global_monitor{ 212 SwitchableSetting<bool> cpuopt_unsafe_ignore_global_monitor{
434 true, "cpuopt_unsafe_ignore_global_monitor"}; 213 linkage, true, "cpuopt_unsafe_ignore_global_monitor", Category::CpuUnsafe};
435 214
436 // Renderer 215 // Renderer
437 SwitchableSetting<RendererBackend, true> renderer_backend{ 216 SwitchableSetting<RendererBackend, true> renderer_backend{
438 RendererBackend::Vulkan, RendererBackend::OpenGL, RendererBackend::Null, "backend"}; 217 linkage, RendererBackend::Vulkan, RendererBackend::OpenGL, RendererBackend::Null,
439 SwitchableSetting<bool> async_presentation{false, "async_presentation"}; 218 "backend", Category::Renderer};
440 SwitchableSetting<bool> renderer_force_max_clock{false, "force_max_clock"}; 219 SwitchableSetting<ShaderBackend, true> shader_backend{
441 Setting<bool> renderer_debug{false, "debug"}; 220 linkage, ShaderBackend::Glsl, ShaderBackend::Glsl, ShaderBackend::SpirV,
442 Setting<bool> renderer_shader_feedback{false, "shader_feedback"}; 221 "shader_backend", Category::Renderer, Specialization::RuntimeList};
443 Setting<bool> enable_nsight_aftermath{false, "nsight_aftermath"}; 222 SwitchableSetting<int> vulkan_device{linkage, 0, "vulkan_device", Category::Renderer,
444 Setting<bool> disable_shader_loop_safety_checks{false, "disable_shader_loop_safety_checks"}; 223 Specialization::RuntimeList};
445 SwitchableSetting<int> vulkan_device{0, "vulkan_device"}; 224
446 225 SwitchableSetting<bool> use_disk_shader_cache{linkage, true, "use_disk_shader_cache",
447 ResolutionScalingInfo resolution_info{}; 226 Category::Renderer};
448 SwitchableSetting<ResolutionSetup> resolution_setup{ResolutionSetup::Res1X, "resolution_setup"}; 227 SwitchableSetting<bool> use_asynchronous_gpu_emulation{
449 SwitchableSetting<ScalingFilter> scaling_filter{ScalingFilter::Bilinear, "scaling_filter"}; 228 linkage, true, "use_asynchronous_gpu_emulation", Category::Renderer};
450 SwitchableSetting<int, true> fsr_sharpening_slider{25, 0, 200, "fsr_sharpening_slider"}; 229 SwitchableSetting<AstcDecodeMode, true> accelerate_astc{linkage,
451 SwitchableSetting<AntiAliasing> anti_aliasing{AntiAliasing::None, "anti_aliasing"}; 230 AstcDecodeMode::Gpu,
231 AstcDecodeMode::Cpu,
232 AstcDecodeMode::CpuAsynchronous,
233 "accelerate_astc",
234 Category::Renderer};
235 Setting<VSyncMode, true> vsync_mode{
236 linkage, VSyncMode::Fifo, VSyncMode::Immediate, VSyncMode::FifoRelaxed,
237 "use_vsync", Category::Renderer, Specialization::RuntimeList, true,
238 true};
239 SwitchableSetting<NvdecEmulation> nvdec_emulation{linkage, NvdecEmulation::Gpu,
240 "nvdec_emulation", Category::Renderer};
452 // *nix platforms may have issues with the borderless windowed fullscreen mode. 241 // *nix platforms may have issues with the borderless windowed fullscreen mode.
453 // Default to exclusive fullscreen on these platforms for now. 242 // Default to exclusive fullscreen on these platforms for now.
454 SwitchableSetting<FullscreenMode, true> fullscreen_mode{ 243 SwitchableSetting<FullscreenMode, true> fullscreen_mode{linkage,
455#ifdef _WIN32 244#ifdef _WIN32
456 FullscreenMode::Borderless, 245 FullscreenMode::Borderless,
457#else 246#else
458 FullscreenMode::Exclusive, 247 FullscreenMode::Exclusive,
459#endif 248#endif
460 FullscreenMode::Borderless, FullscreenMode::Exclusive, "fullscreen_mode"}; 249 FullscreenMode::Borderless,
461 SwitchableSetting<int, true> aspect_ratio{0, 0, 4, "aspect_ratio"}; 250 FullscreenMode::Exclusive,
462 SwitchableSetting<int, true> max_anisotropy{0, 0, 5, "max_anisotropy"}; 251 "fullscreen_mode",
463 SwitchableSetting<bool> use_speed_limit{true, "use_speed_limit"}; 252 Category::Renderer,
464 SwitchableSetting<u16, true> speed_limit{100, 0, 9999, "speed_limit"}; 253 Specialization::Default,
465 SwitchableSetting<bool> use_disk_shader_cache{true, "use_disk_shader_cache"}; 254 true,
466 SwitchableSetting<GPUAccuracy, true> gpu_accuracy{GPUAccuracy::High, GPUAccuracy::Normal, 255 true};
467 GPUAccuracy::Extreme, "gpu_accuracy"}; 256 SwitchableSetting<AspectRatio, true> aspect_ratio{linkage,
468 SwitchableSetting<bool> use_asynchronous_gpu_emulation{true, "use_asynchronous_gpu_emulation"}; 257 AspectRatio::R16_9,
469 SwitchableSetting<NvdecEmulation> nvdec_emulation{NvdecEmulation::GPU, "nvdec_emulation"}; 258 AspectRatio::R16_9,
470 SwitchableSetting<bool> accelerate_astc{true, "accelerate_astc"}; 259 AspectRatio::Stretch,
471 SwitchableSetting<bool> async_astc{false, "async_astc"}; 260 "aspect_ratio",
472 Setting<VSyncMode, true> vsync_mode{VSyncMode::FIFO, VSyncMode::Immediate, 261 Category::Renderer,
473 VSyncMode::FIFORelaxed, "use_vsync"}; 262 Specialization::Default,
474 SwitchableSetting<bool> use_reactive_flushing{true, "use_reactive_flushing"}; 263 true,
475 SwitchableSetting<ShaderBackend, true> shader_backend{ShaderBackend::GLSL, ShaderBackend::GLSL, 264 true};
476 ShaderBackend::SPIRV, "shader_backend"}; 265
477 SwitchableSetting<bool> use_asynchronous_shaders{false, "use_asynchronous_shaders"}; 266 ResolutionScalingInfo resolution_info{};
478 SwitchableSetting<bool> use_fast_gpu_time{true, "use_fast_gpu_time"}; 267 SwitchableSetting<ResolutionSetup> resolution_setup{linkage, ResolutionSetup::Res1X,
479 SwitchableSetting<bool> use_vulkan_driver_pipeline_cache{true, 268 "resolution_setup", Category::Renderer};
480 "use_vulkan_driver_pipeline_cache"}; 269 SwitchableSetting<ScalingFilter> scaling_filter{linkage,
481 SwitchableSetting<bool> enable_compute_pipelines{false, "enable_compute_pipelines"}; 270 ScalingFilter::Bilinear,
482 SwitchableSetting<AstcRecompression, true> astc_recompression{ 271 "scaling_filter",
483 AstcRecompression::Uncompressed, AstcRecompression::Uncompressed, AstcRecompression::Bc3, 272 Category::Renderer,
484 "astc_recompression"}; 273 Specialization::Default,
485 SwitchableSetting<bool> use_video_framerate{false, "use_video_framerate"}; 274 true,
486 SwitchableSetting<bool> barrier_feedback_loops{true, "barrier_feedback_loops"}; 275 true};
487 276 SwitchableSetting<AntiAliasing> anti_aliasing{linkage,
488 SwitchableSetting<u8> bg_red{0, "bg_red"}; 277 AntiAliasing::None,
489 SwitchableSetting<u8> bg_green{0, "bg_green"}; 278 "anti_aliasing",
490 SwitchableSetting<u8> bg_blue{0, "bg_blue"}; 279 Category::Renderer,
280 Specialization::Default,
281 true,
282 true};
283 SwitchableSetting<int, true> fsr_sharpening_slider{linkage,
284 25,
285 0,
286 200,
287 "fsr_sharpening_slider",
288 Category::Renderer,
289 Specialization::Scalar |
290 Specialization::Percentage,
291 true,
292 true};
293
294 SwitchableSetting<u8, false> bg_red{
295 linkage, 0, "bg_red", Category::Renderer, Specialization::Default, true, true};
296 SwitchableSetting<u8, false> bg_green{
297 linkage, 0, "bg_green", Category::Renderer, Specialization::Default, true, true};
298 SwitchableSetting<u8, false> bg_blue{
299 linkage, 0, "bg_blue", Category::Renderer, Specialization::Default, true, true};
300
301 SwitchableSetting<GpuAccuracy, true> gpu_accuracy{linkage,
302 GpuAccuracy::High,
303 GpuAccuracy::Normal,
304 GpuAccuracy::Extreme,
305 "gpu_accuracy",
306 Category::RendererAdvanced,
307 Specialization::Default,
308 true,
309 true};
310 SwitchableSetting<AnisotropyMode, true> max_anisotropy{
311 linkage, AnisotropyMode::Automatic, AnisotropyMode::Automatic, AnisotropyMode::X16,
312 "max_anisotropy", Category::RendererAdvanced};
313 SwitchableSetting<AstcRecompression, true> astc_recompression{linkage,
314 AstcRecompression::Uncompressed,
315 AstcRecompression::Uncompressed,
316 AstcRecompression::Bc3,
317 "astc_recompression",
318 Category::RendererAdvanced};
319 SwitchableSetting<bool> async_presentation{linkage, false, "async_presentation",
320 Category::RendererAdvanced};
321 SwitchableSetting<bool> renderer_force_max_clock{linkage, false, "force_max_clock",
322 Category::RendererAdvanced};
323 SwitchableSetting<bool> use_reactive_flushing{linkage, true, "use_reactive_flushing",
324 Category::RendererAdvanced};
325 SwitchableSetting<bool> use_asynchronous_shaders{linkage, false, "use_asynchronous_shaders",
326 Category::RendererAdvanced};
327 SwitchableSetting<bool> use_fast_gpu_time{
328 linkage, true, "use_fast_gpu_time", Category::RendererAdvanced, Specialization::Default,
329 true, true};
330 SwitchableSetting<bool> use_vulkan_driver_pipeline_cache{linkage,
331 true,
332 "use_vulkan_driver_pipeline_cache",
333 Category::RendererAdvanced,
334 Specialization::Default,
335 true,
336 true};
337 SwitchableSetting<bool> enable_compute_pipelines{linkage, false, "enable_compute_pipelines",
338 Category::RendererAdvanced};
339 SwitchableSetting<bool> use_video_framerate{linkage, false, "use_video_framerate",
340 Category::RendererAdvanced};
341 SwitchableSetting<bool> barrier_feedback_loops{linkage, true, "barrier_feedback_loops",
342 Category::RendererAdvanced};
343
344 Setting<bool> renderer_debug{linkage, false, "debug", Category::RendererDebug};
345 Setting<bool> renderer_shader_feedback{linkage, false, "shader_feedback",
346 Category::RendererDebug};
347 Setting<bool> enable_nsight_aftermath{linkage, false, "nsight_aftermath",
348 Category::RendererDebug};
349 Setting<bool> disable_shader_loop_safety_checks{
350 linkage, false, "disable_shader_loop_safety_checks", Category::RendererDebug};
491 351
492 // System 352 // System
493 SwitchableSetting<std::optional<u32>> rng_seed{std::optional<u32>(), "rng_seed"}; 353 SwitchableSetting<Language, true> language_index{linkage,
494 Setting<std::string> device_name{"Yuzu", "device_name"}; 354 Language::EnglishAmerican,
355 Language::Japanese,
356 Language::PortugueseBrazilian,
357 "language_index",
358 Category::System};
359 SwitchableSetting<Region, true> region_index{linkage, Region::Usa, Region::Japan,
360 Region::Taiwan, "region_index", Category::System};
361 SwitchableSetting<TimeZone, true> time_zone_index{linkage, TimeZone::Auto,
362 TimeZone::Auto, TimeZone::Zulu,
363 "time_zone_index", Category::System};
495 // Measured in seconds since epoch 364 // Measured in seconds since epoch
496 std::optional<s64> custom_rtc; 365 SwitchableSetting<bool> custom_rtc_enabled{
366 linkage, false, "custom_rtc_enabled", Category::System, Specialization::Paired, true, true};
367 SwitchableSetting<s64> custom_rtc{
368 linkage, 0, "custom_rtc", Category::System, Specialization::Time,
369 true, true, &custom_rtc_enabled};
497 // Set on game boot, reset on stop. Seconds difference between current time and `custom_rtc` 370 // Set on game boot, reset on stop. Seconds difference between current time and `custom_rtc`
498 s64 custom_rtc_differential; 371 s64 custom_rtc_differential;
372 SwitchableSetting<bool> rng_seed_enabled{
373 linkage, false, "rng_seed_enabled", Category::System, Specialization::Paired, true, true};
374 SwitchableSetting<u32> rng_seed{
375 linkage, 0, "rng_seed", Category::System, Specialization::Hex,
376 true, true, &rng_seed_enabled};
377 Setting<std::string> device_name{
378 linkage, "yuzu", "device_name", Category::System, Specialization::Default, true, true};
499 379
500 Setting<s32> current_user{0, "current_user"}; 380 Setting<s32> current_user{linkage, 0, "current_user", Category::System};
501 SwitchableSetting<s32, true> language_index{1, 0, 17, "language_index"}; 381
502 SwitchableSetting<s32, true> region_index{1, 0, 6, "region_index"}; 382 SwitchableSetting<bool> use_docked_mode{linkage, true, "use_docked_mode", Category::System};
503 SwitchableSetting<s32, true> time_zone_index{0, 0, 45, "time_zone_index"};
504 SwitchableSetting<s32, true> sound_index{1, 0, 2, "sound_index"};
505 383
506 // Controls 384 // Controls
507 InputSetting<std::array<PlayerInput, 10>> players; 385 InputSetting<std::array<PlayerInput, 10>> players;
508 386
509 SwitchableSetting<bool> use_docked_mode{true, "use_docked_mode"}; 387 Setting<bool> enable_raw_input{
510 388 linkage, false, "enable_raw_input", Category::Controls, Specialization::Default,
511 Setting<bool> enable_raw_input{false, "enable_raw_input"}; 389// Only read/write enable_raw_input on Windows platforms
512 Setting<bool> controller_navigation{true, "controller_navigation"}; 390#ifdef _WIN32
513 Setting<bool> enable_joycon_driver{true, "enable_joycon_driver"}; 391 true
514 Setting<bool> enable_procon_driver{false, "enable_procon_driver"}; 392#else
515 393 false
516 SwitchableSetting<bool> vibration_enabled{true, "vibration_enabled"}; 394#endif
517 SwitchableSetting<bool> enable_accurate_vibrations{false, "enable_accurate_vibrations"}; 395 };
518 396 Setting<bool> controller_navigation{linkage, true, "controller_navigation", Category::Controls};
519 SwitchableSetting<bool> motion_enabled{true, "motion_enabled"}; 397 Setting<bool> enable_joycon_driver{linkage, true, "enable_joycon_driver", Category::Controls};
520 Setting<std::string> udp_input_servers{"127.0.0.1:26760", "udp_input_servers"}; 398 Setting<bool> enable_procon_driver{linkage, false, "enable_procon_driver", Category::Controls};
521 Setting<bool> enable_udp_controller{false, "enable_udp_controller"}; 399
522 400 SwitchableSetting<bool> vibration_enabled{linkage, true, "vibration_enabled",
523 Setting<bool> pause_tas_on_load{true, "pause_tas_on_load"}; 401 Category::Controls};
524 Setting<bool> tas_enable{false, "tas_enable"}; 402 SwitchableSetting<bool> enable_accurate_vibrations{linkage, false, "enable_accurate_vibrations",
525 Setting<bool> tas_loop{false, "tas_loop"}; 403 Category::Controls};
526 404
527 Setting<bool> mouse_panning{false, "mouse_panning"}; 405 SwitchableSetting<bool> motion_enabled{linkage, true, "motion_enabled", Category::Controls};
528 Setting<u8, true> mouse_panning_x_sensitivity{50, 1, 100, "mouse_panning_x_sensitivity"}; 406 Setting<std::string> udp_input_servers{linkage, "127.0.0.1:26760", "udp_input_servers",
529 Setting<u8, true> mouse_panning_y_sensitivity{50, 1, 100, "mouse_panning_y_sensitivity"}; 407 Category::Controls};
530 Setting<u8, true> mouse_panning_deadzone_counterweight{20, 0, 100, 408 Setting<bool> enable_udp_controller{linkage, false, "enable_udp_controller",
531 "mouse_panning_deadzone_counterweight"}; 409 Category::Controls};
532 Setting<u8, true> mouse_panning_decay_strength{18, 0, 100, "mouse_panning_decay_strength"}; 410
533 Setting<u8, true> mouse_panning_min_decay{6, 0, 100, "mouse_panning_min_decay"}; 411 Setting<bool> pause_tas_on_load{linkage, true, "pause_tas_on_load", Category::Controls};
534 412 Setting<bool> tas_enable{linkage, false, "tas_enable", Category::Controls};
535 Setting<bool> mouse_enabled{false, "mouse_enabled"}; 413 Setting<bool> tas_loop{linkage, false, "tas_loop", Category::Controls};
536 Setting<bool> emulate_analog_keyboard{false, "emulate_analog_keyboard"}; 414
537 Setting<bool> keyboard_enabled{false, "keyboard_enabled"}; 415 Setting<bool> mouse_panning{
538 416 linkage, false, "mouse_panning", Category::Controls, Specialization::Default, false};
539 Setting<bool> debug_pad_enabled{false, "debug_pad_enabled"}; 417 Setting<u8, true> mouse_panning_sensitivity{
418 linkage, 50, 1, 100, "mouse_panning_sensitivity", Category::Controls};
419 Setting<bool> mouse_enabled{linkage, false, "mouse_enabled", Category::Controls};
420
421 Setting<u8, true> mouse_panning_x_sensitivity{
422 linkage, 50, 1, 100, "mouse_panning_x_sensitivity", Category::Controls};
423 Setting<u8, true> mouse_panning_y_sensitivity{
424 linkage, 50, 1, 100, "mouse_panning_y_sensitivity", Category::Controls};
425 Setting<u8, true> mouse_panning_deadzone_counterweight{
426 linkage, 20, 0, 100, "mouse_panning_deadzone_counterweight", Category::Controls};
427 Setting<u8, true> mouse_panning_decay_strength{
428 linkage, 18, 0, 100, "mouse_panning_decay_strength", Category::Controls};
429 Setting<u8, true> mouse_panning_min_decay{
430 linkage, 6, 0, 100, "mouse_panning_min_decay", Category::Controls};
431
432 Setting<bool> emulate_analog_keyboard{linkage, false, "emulate_analog_keyboard",
433 Category::Controls};
434 Setting<bool> keyboard_enabled{linkage, false, "keyboard_enabled", Category::Controls};
435
436 Setting<bool> debug_pad_enabled{linkage, false, "debug_pad_enabled", Category::Controls};
540 ButtonsRaw debug_pad_buttons; 437 ButtonsRaw debug_pad_buttons;
541 AnalogsRaw debug_pad_analogs; 438 AnalogsRaw debug_pad_analogs;
542 439
543 TouchscreenInput touchscreen; 440 TouchscreenInput touchscreen;
544 441
545 Setting<std::string> touch_device{"min_x:100,min_y:50,max_x:1800,max_y:850", "touch_device"}; 442 Setting<std::string> touch_device{linkage, "min_x:100,min_y:50,max_x:1800,max_y:850",
546 Setting<int> touch_from_button_map_index{0, "touch_from_button_map"}; 443 "touch_device", Category::Controls};
444 Setting<int> touch_from_button_map_index{linkage, 0, "touch_from_button_map",
445 Category::Controls};
547 std::vector<TouchFromButtonMap> touch_from_button_maps; 446 std::vector<TouchFromButtonMap> touch_from_button_maps;
548 447
549 Setting<bool> enable_ring_controller{true, "enable_ring_controller"}; 448 Setting<bool> enable_ring_controller{linkage, true, "enable_ring_controller",
449 Category::Controls};
550 RingconRaw ringcon_analogs; 450 RingconRaw ringcon_analogs;
551 451
552 Setting<bool> enable_ir_sensor{false, "enable_ir_sensor"}; 452 Setting<bool> enable_ir_sensor{linkage, false, "enable_ir_sensor", Category::Controls};
553 Setting<std::string> ir_sensor_device{"auto", "ir_sensor_device"}; 453 Setting<std::string> ir_sensor_device{linkage, "auto", "ir_sensor_device", Category::Controls};
554 454
555 Setting<bool> random_amiibo_id{false, "random_amiibo_id"}; 455 Setting<bool> random_amiibo_id{linkage, false, "random_amiibo_id", Category::Controls};
556 456
557 // Data Storage 457 // Data Storage
558 Setting<bool> use_virtual_sd{true, "use_virtual_sd"}; 458 Setting<bool> use_virtual_sd{linkage, true, "use_virtual_sd", Category::DataStorage};
559 Setting<bool> gamecard_inserted{false, "gamecard_inserted"}; 459 Setting<bool> gamecard_inserted{linkage, false, "gamecard_inserted", Category::DataStorage};
560 Setting<bool> gamecard_current_game{false, "gamecard_current_game"}; 460 Setting<bool> gamecard_current_game{linkage, false, "gamecard_current_game",
561 Setting<std::string> gamecard_path{std::string(), "gamecard_path"}; 461 Category::DataStorage};
462 Setting<std::string> gamecard_path{linkage, std::string(), "gamecard_path",
463 Category::DataStorage};
562 464
563 // Debugging 465 // Debugging
564 bool record_frame_times; 466 bool record_frame_times;
565 Setting<bool> use_gdbstub{false, "use_gdbstub"}; 467 Setting<bool> use_gdbstub{linkage, false, "use_gdbstub", Category::Debugging};
566 Setting<u16> gdbstub_port{6543, "gdbstub_port"}; 468 Setting<u16> gdbstub_port{linkage, 6543, "gdbstub_port", Category::Debugging};
567 Setting<std::string> program_args{std::string(), "program_args"}; 469 Setting<std::string> program_args{linkage, std::string(), "program_args", Category::Debugging};
568 Setting<bool> dump_exefs{false, "dump_exefs"}; 470 Setting<bool> dump_exefs{linkage, false, "dump_exefs", Category::Debugging};
569 Setting<bool> dump_nso{false, "dump_nso"}; 471 Setting<bool> dump_nso{linkage, false, "dump_nso", Category::Debugging};
570 Setting<bool> dump_shaders{false, "dump_shaders"}; 472 Setting<bool> dump_shaders{
571 Setting<bool> dump_macros{false, "dump_macros"}; 473 linkage, false, "dump_shaders", Category::DebuggingGraphics, Specialization::Default,
572 Setting<bool> enable_fs_access_log{false, "enable_fs_access_log"}; 474 false};
573 Setting<bool> reporting_services{false, "reporting_services"}; 475 Setting<bool> dump_macros{
574 Setting<bool> quest_flag{false, "quest_flag"}; 476 linkage, false, "dump_macros", Category::DebuggingGraphics, Specialization::Default, false};
575 Setting<bool> disable_macro_jit{false, "disable_macro_jit"}; 477 Setting<bool> enable_fs_access_log{linkage, false, "enable_fs_access_log", Category::Debugging};
576 Setting<bool> disable_macro_hle{false, "disable_macro_hle"}; 478 Setting<bool> reporting_services{
577 Setting<bool> extended_logging{false, "extended_logging"}; 479 linkage, false, "reporting_services", Category::Debugging, Specialization::Default, false};
578 Setting<bool> use_debug_asserts{false, "use_debug_asserts"}; 480 Setting<bool> quest_flag{linkage, false, "quest_flag", Category::Debugging};
579 Setting<bool> use_auto_stub{false, "use_auto_stub"}; 481 Setting<bool> disable_macro_jit{linkage, false, "disable_macro_jit",
580 Setting<bool> enable_all_controllers{false, "enable_all_controllers"}; 482 Category::DebuggingGraphics};
581 Setting<bool> create_crash_dumps{false, "create_crash_dumps"}; 483 Setting<bool> disable_macro_hle{linkage, false, "disable_macro_hle",
582 Setting<bool> perform_vulkan_check{true, "perform_vulkan_check"}; 484 Category::DebuggingGraphics};
485 Setting<bool> extended_logging{
486 linkage, false, "extended_logging", Category::Debugging, Specialization::Default, false};
487 Setting<bool> use_debug_asserts{linkage, false, "use_debug_asserts", Category::Debugging};
488 Setting<bool> use_auto_stub{
489 linkage, false, "use_auto_stub", Category::Debugging, Specialization::Default, false};
490 Setting<bool> enable_all_controllers{linkage, false, "enable_all_controllers",
491 Category::Debugging};
492 Setting<bool> create_crash_dumps{linkage, false, "create_crash_dumps", Category::Debugging};
493 Setting<bool> perform_vulkan_check{linkage, true, "perform_vulkan_check", Category::Debugging};
583 494
584 // Miscellaneous 495 // Miscellaneous
585 Setting<std::string> log_filter{"*:Info", "log_filter"}; 496 Setting<std::string> log_filter{linkage, "*:Info", "log_filter", Category::Miscellaneous};
586 Setting<bool> use_dev_keys{false, "use_dev_keys"}; 497 Setting<bool> use_dev_keys{linkage, false, "use_dev_keys", Category::Miscellaneous};
587 498
588 // Network 499 // Network
589 Setting<std::string> network_interface{std::string(), "network_interface"}; 500 Setting<std::string> network_interface{linkage, std::string(), "network_interface",
501 Category::Network};
590 502
591 // WebService 503 // WebService
592 Setting<bool> enable_telemetry{true, "enable_telemetry"}; 504 Setting<bool> enable_telemetry{linkage, true, "enable_telemetry", Category::WebService};
593 Setting<std::string> web_api_url{"https://api.yuzu-emu.org", "web_api_url"}; 505 Setting<std::string> web_api_url{linkage, "https://api.yuzu-emu.org", "web_api_url",
594 Setting<std::string> yuzu_username{std::string(), "yuzu_username"}; 506 Category::WebService};
595 Setting<std::string> yuzu_token{std::string(), "yuzu_token"}; 507 Setting<std::string> yuzu_username{linkage, std::string(), "yuzu_username",
508 Category::WebService};
509 Setting<std::string> yuzu_token{linkage, std::string(), "yuzu_token", Category::WebService};
596 510
597 // Add-Ons 511 // Add-Ons
598 std::map<u64, std::vector<std::string>> disabled_addons; 512 std::map<u64, std::vector<std::string>> disabled_addons;
@@ -600,9 +514,6 @@ struct Values {
600 514
601extern Values values; 515extern Values values;
602 516
603bool IsConfiguringGlobal();
604void SetConfiguringGlobal(bool is_global);
605
606bool IsGPULevelExtreme(); 517bool IsGPULevelExtreme();
607bool IsGPULevelHigh(); 518bool IsGPULevelHigh();
608 519
@@ -610,7 +521,7 @@ bool IsFastmemEnabled();
610 521
611float Volume(); 522float Volume();
612 523
613std::string GetTimeZoneString(); 524std::string GetTimeZoneString(TimeZone time_zone);
614 525
615void LogSettings(); 526void LogSettings();
616 527
@@ -619,4 +530,7 @@ void UpdateRescalingInfo();
619// Restore the global state of all applicable settings in the Values struct 530// Restore the global state of all applicable settings in the Values struct
620void RestoreGlobalState(bool is_powered_on); 531void RestoreGlobalState(bool is_powered_on);
621 532
533bool IsConfiguringGlobal();
534void SetConfiguringGlobal(bool is_global);
535
622} // namespace Settings 536} // namespace Settings
diff --git a/src/common/settings_common.cpp b/src/common/settings_common.cpp
new file mode 100644
index 000000000..dedf5ef90
--- /dev/null
+++ b/src/common/settings_common.cpp
@@ -0,0 +1,58 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <string>
5#include "common/settings_common.h"
6
7namespace Settings {
8
9BasicSetting::BasicSetting(Linkage& linkage, const std::string& name, enum Category category_,
10 bool save_, bool runtime_modifiable_, u32 specialization_,
11 BasicSetting* other_setting_)
12 : label{name}, category{category_}, id{linkage.count}, save{save_},
13 runtime_modifiable{runtime_modifiable_}, specialization{specialization_},
14 other_setting{other_setting_} {
15 linkage.by_category[category].push_back(this);
16 linkage.count++;
17}
18
19BasicSetting::~BasicSetting() = default;
20
21std::string BasicSetting::ToStringGlobal() const {
22 return this->ToString();
23}
24
25bool BasicSetting::UsingGlobal() const {
26 return true;
27}
28
29void BasicSetting::SetGlobal(bool global) {}
30
31bool BasicSetting::Save() const {
32 return save;
33}
34
35bool BasicSetting::RuntimeModfiable() const {
36 return runtime_modifiable;
37}
38
39Category BasicSetting::GetCategory() const {
40 return category;
41}
42
43u32 BasicSetting::Specialization() const {
44 return specialization;
45}
46
47BasicSetting* BasicSetting::PairedSetting() const {
48 return other_setting;
49}
50
51const std::string& BasicSetting::GetLabel() const {
52 return label;
53}
54
55Linkage::Linkage(u32 initial_count) : count{initial_count} {}
56Linkage::~Linkage() = default;
57
58} // namespace Settings
diff --git a/src/common/settings_common.h b/src/common/settings_common.h
new file mode 100644
index 000000000..2efb329b0
--- /dev/null
+++ b/src/common/settings_common.h
@@ -0,0 +1,256 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <functional>
7#include <map>
8#include <string>
9#include <typeindex>
10#include "common/common_types.h"
11
12namespace Settings {
13
14enum class Category : u32 {
15 Audio,
16 Core,
17 Cpu,
18 CpuDebug,
19 CpuUnsafe,
20 Renderer,
21 RendererAdvanced,
22 RendererDebug,
23 System,
24 SystemAudio,
25 DataStorage,
26 Debugging,
27 DebuggingGraphics,
28 Miscellaneous,
29 Network,
30 WebService,
31 AddOns,
32 Controls,
33 Ui,
34 UiGeneral,
35 UiLayout,
36 UiGameList,
37 Screenshots,
38 Shortcuts,
39 Multiplayer,
40 Services,
41 Paths,
42 MaxEnum,
43};
44
45constexpr u8 SpecializationTypeMask = 0xf;
46constexpr u8 SpecializationAttributeMask = 0xf0;
47constexpr u8 SpecializationAttributeOffset = 4;
48
49// Scalar and countable could have better names
50enum Specialization : u8 {
51 Default = 0,
52 Time = 1, // Duration or specific moment in time
53 Hex = 2, // Hexadecimal number
54 List = 3, // Setting has specific members
55 RuntimeList = 4, // Members of the list are determined during runtime
56 Scalar = 5, // Values are continuous
57 Countable = 6, // Can be stepped through
58 Paired = 7, // Another setting is associated with this setting
59
60 Percentage = (1 << SpecializationAttributeOffset), // Should be represented as a percentage
61};
62
63class BasicSetting;
64
65class Linkage {
66public:
67 explicit Linkage(u32 initial_count = 0);
68 ~Linkage();
69 std::map<Category, std::vector<BasicSetting*>> by_category{};
70 std::vector<std::function<void()>> restore_functions{};
71 u32 count;
72};
73
74/**
75 * BasicSetting is an abstract class that only keeps track of metadata. The string methods are
76 * available to get data values out.
77 */
78class BasicSetting {
79protected:
80 explicit BasicSetting(Linkage& linkage, const std::string& name, Category category_, bool save_,
81 bool runtime_modifiable_, u32 specialization,
82 BasicSetting* other_setting);
83
84public:
85 virtual ~BasicSetting();
86
87 /*
88 * Data retrieval
89 */
90
91 /**
92 * Returns a string representation of the internal data. If the Setting is Switchable, it
93 * respects the internal global state: it is based on GetValue().
94 *
95 * @returns A string representation of the internal data.
96 */
97 [[nodiscard]] virtual std::string ToString() const = 0;
98
99 /**
100 * Returns a string representation of the global version of internal data. If the Setting is
101 * not Switchable, it behaves like ToString.
102 *
103 * @returns A string representation of the global version of internal data.
104 */
105 [[nodiscard]] virtual std::string ToStringGlobal() const;
106
107 /**
108 * @returns A string representation of the Setting's default value.
109 */
110 [[nodiscard]] virtual std::string DefaultToString() const = 0;
111
112 /**
113 * Returns a string representation of the minimum value of the setting. If the Setting is not
114 * ranged, the string represents the default initialization of the data type.
115 *
116 * @returns A string representation of the minimum value of the setting.
117 */
118 [[nodiscard]] virtual std::string MinVal() const = 0;
119
120 /**
121 * Returns a string representation of the maximum value of the setting. If the Setting is not
122 * ranged, the string represents the default initialization of the data type.
123 *
124 * @returns A string representation of the maximum value of the setting.
125 */
126 [[nodiscard]] virtual std::string MaxVal() const = 0;
127
128 /**
129 * Takes a string input, converts it to the internal data type if necessary, and then runs
130 * SetValue with it.
131 *
132 * @param load String of the input data.
133 */
134 virtual void LoadString(const std::string& load) = 0;
135
136 /**
137 * Returns a string representation of the data. If the data is an enum, it returns a string of
138 * the enum value. If the internal data type is not an enum, this is equivalent to ToString.
139 *
140 * e.g. renderer_backend.Canonicalize() == "OpenGL"
141 *
142 * @returns Canonicalized string representation of the internal data
143 */
144 [[nodiscard]] virtual std::string Canonicalize() const = 0;
145
146 /*
147 * Metadata
148 */
149
150 /**
151 * @returns A unique identifier for the Setting's internal data type.
152 */
153 [[nodiscard]] virtual std::type_index TypeId() const = 0;
154
155 /**
156 * Returns true if the Setting's internal data type is an enum.
157 *
158 * @returns True if the Setting's internal data type is an enum
159 */
160 [[nodiscard]] virtual constexpr bool IsEnum() const = 0;
161
162 /**
163 * Returns true if the current setting is Switchable.
164 *
165 * @returns If the setting is a SwitchableSetting
166 */
167 [[nodiscard]] virtual constexpr bool Switchable() const {
168 return false;
169 }
170
171 /**
172 * Returns true to suggest that a frontend can read or write the setting to a configuration
173 * file.
174 *
175 * @returns The save preference
176 */
177 [[nodiscard]] bool Save() const;
178
179 /**
180 * @returns true if the current setting can be changed while the guest is running.
181 */
182 [[nodiscard]] bool RuntimeModfiable() const;
183
184 /**
185 * @returns A unique number corresponding to the setting.
186 */
187 [[nodiscard]] constexpr u32 Id() const {
188 return id;
189 }
190
191 /**
192 * Returns the setting's category AKA INI group.
193 *
194 * @returns The setting's category
195 */
196 [[nodiscard]] Category GetCategory() const;
197
198 /**
199 * @returns Extra metadata for data representation in frontend implementations.
200 */
201 [[nodiscard]] u32 Specialization() const;
202
203 /**
204 * @returns Another BasicSetting if one is paired, or nullptr otherwise.
205 */
206 [[nodiscard]] BasicSetting* PairedSetting() const;
207
208 /**
209 * Returns the label this setting was created with.
210 *
211 * @returns A reference to the label
212 */
213 [[nodiscard]] const std::string& GetLabel() const;
214
215 /**
216 * @returns If the Setting checks input values for valid ranges.
217 */
218 [[nodiscard]] virtual constexpr bool Ranged() const = 0;
219
220 /**
221 * @returns The index of the enum if the underlying setting type is an enum, else max of u32.
222 */
223 [[nodiscard]] virtual constexpr u32 EnumIndex() const = 0;
224
225 /*
226 * Switchable settings
227 */
228
229 /**
230 * Sets a setting's global state. True means use the normal setting, false to use a custom
231 * value. Has no effect if the Setting is not Switchable.
232 *
233 * @param global The desired state
234 */
235 virtual void SetGlobal(bool global);
236
237 /**
238 * Returns true if the setting is using the normal setting value. Always true if the setting is
239 * not Switchable.
240 *
241 * @returns The Setting's global state
242 */
243 [[nodiscard]] virtual bool UsingGlobal() const;
244
245private:
246 const std::string label; ///< The setting's label
247 const Category category; ///< The setting's category AKA INI group
248 const u32 id; ///< Unique integer for the setting
249 const bool save; ///< Suggests if the setting should be saved and read to a frontend config
250 const bool
251 runtime_modifiable; ///< Suggests if the setting can be modified while a guest is running
252 const u32 specialization; ///< Extra data to identify representation of a setting
253 BasicSetting* const other_setting; ///< A paired setting
254};
255
256} // namespace Settings
diff --git a/src/common/settings_enums.h b/src/common/settings_enums.h
new file mode 100644
index 000000000..a1a29ebf6
--- /dev/null
+++ b/src/common/settings_enums.h
@@ -0,0 +1,214 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <string>
7#include <utility>
8#include <vector>
9#include "common/common_types.h"
10
11namespace Settings {
12
13template <typename T>
14struct EnumMetadata {
15 static constexpr std::vector<std::pair<std::string, T>> Canonicalizations();
16 static constexpr u32 Index();
17};
18
19#define PAIR_45(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_46(N, __VA_ARGS__))
20#define PAIR_44(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_45(N, __VA_ARGS__))
21#define PAIR_43(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_44(N, __VA_ARGS__))
22#define PAIR_42(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_43(N, __VA_ARGS__))
23#define PAIR_41(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_42(N, __VA_ARGS__))
24#define PAIR_40(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_41(N, __VA_ARGS__))
25#define PAIR_39(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_40(N, __VA_ARGS__))
26#define PAIR_38(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_39(N, __VA_ARGS__))
27#define PAIR_37(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_38(N, __VA_ARGS__))
28#define PAIR_36(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_37(N, __VA_ARGS__))
29#define PAIR_35(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_36(N, __VA_ARGS__))
30#define PAIR_34(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_35(N, __VA_ARGS__))
31#define PAIR_33(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_34(N, __VA_ARGS__))
32#define PAIR_32(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_33(N, __VA_ARGS__))
33#define PAIR_31(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_32(N, __VA_ARGS__))
34#define PAIR_30(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_31(N, __VA_ARGS__))
35#define PAIR_29(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_30(N, __VA_ARGS__))
36#define PAIR_28(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_29(N, __VA_ARGS__))
37#define PAIR_27(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_28(N, __VA_ARGS__))
38#define PAIR_26(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_27(N, __VA_ARGS__))
39#define PAIR_25(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_26(N, __VA_ARGS__))
40#define PAIR_24(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_25(N, __VA_ARGS__))
41#define PAIR_23(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_24(N, __VA_ARGS__))
42#define PAIR_22(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_23(N, __VA_ARGS__))
43#define PAIR_21(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_22(N, __VA_ARGS__))
44#define PAIR_20(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_21(N, __VA_ARGS__))
45#define PAIR_19(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_20(N, __VA_ARGS__))
46#define PAIR_18(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_19(N, __VA_ARGS__))
47#define PAIR_17(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_18(N, __VA_ARGS__))
48#define PAIR_16(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_17(N, __VA_ARGS__))
49#define PAIR_15(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_16(N, __VA_ARGS__))
50#define PAIR_14(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_15(N, __VA_ARGS__))
51#define PAIR_13(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_14(N, __VA_ARGS__))
52#define PAIR_12(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_13(N, __VA_ARGS__))
53#define PAIR_11(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_12(N, __VA_ARGS__))
54#define PAIR_10(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_11(N, __VA_ARGS__))
55#define PAIR_9(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_10(N, __VA_ARGS__))
56#define PAIR_8(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_9(N, __VA_ARGS__))
57#define PAIR_7(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_8(N, __VA_ARGS__))
58#define PAIR_6(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_7(N, __VA_ARGS__))
59#define PAIR_5(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_6(N, __VA_ARGS__))
60#define PAIR_4(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_5(N, __VA_ARGS__))
61#define PAIR_3(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_4(N, __VA_ARGS__))
62#define PAIR_2(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_3(N, __VA_ARGS__))
63#define PAIR_1(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_2(N, __VA_ARGS__))
64#define PAIR(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_1(N, __VA_ARGS__))
65
66#define ENUM(NAME, ...) \
67 enum class NAME : u32 { __VA_ARGS__ }; \
68 template <> \
69 constexpr std::vector<std::pair<std::string, NAME>> EnumMetadata<NAME>::Canonicalizations() { \
70 return {PAIR(NAME, __VA_ARGS__)}; \
71 } \
72 template <> \
73 constexpr u32 EnumMetadata<NAME>::Index() { \
74 return __COUNTER__; \
75 }
76
77// AudioEngine must be specified discretely due to having existing but slightly different
78// canonicalizations
79// TODO (lat9nq): Remove explicit definition of AudioEngine/sink_id
80enum class AudioEngine : u32 {
81 Auto,
82 Cubeb,
83 Sdl2,
84 Null,
85};
86
87template <>
88constexpr std::vector<std::pair<std::string, AudioEngine>>
89EnumMetadata<AudioEngine>::Canonicalizations() {
90 return {
91 {"auto", AudioEngine::Auto},
92 {"cubeb", AudioEngine::Cubeb},
93 {"sdl2", AudioEngine::Sdl2},
94 {"null", AudioEngine::Null},
95 };
96}
97
98template <>
99constexpr u32 EnumMetadata<AudioEngine>::Index() {
100 // This is just a sufficiently large number that is more than the number of other enums declared
101 // here
102 return 100;
103}
104
105ENUM(AudioMode, Mono, Stereo, Surround);
106
107ENUM(Language, Japanese, EnglishAmerican, French, German, Italian, Spanish, Chinese, Korean, Dutch,
108 Portuguese, Russian, Taiwanese, EnglishBritish, FrenchCanadian, SpanishLatin,
109 ChineseSimplified, ChineseTraditional, PortugueseBrazilian);
110
111ENUM(Region, Japan, Usa, Europe, Australia, China, Korea, Taiwan);
112
113ENUM(TimeZone, Auto, Default, Cet, Cst6Cdt, Cuba, Eet, Egypt, Eire, Est, Est5Edt, Gb, GbEire, Gmt,
114 GmtPlusZero, GmtMinusZero, GmtZero, Greenwich, Hongkong, Hst, Iceland, Iran, Israel, Jamaica,
115 Japan, Kwajalein, Libya, Met, Mst, Mst7Mdt, Navajo, Nz, NzChat, Poland, Portugal, Prc, Pst8Pdt,
116 Roc, Rok, Singapore, Turkey, Uct, Universal, Utc, WSu, Wet, Zulu);
117
118ENUM(AnisotropyMode, Automatic, Default, X2, X4, X8, X16);
119
120ENUM(AstcDecodeMode, Cpu, Gpu, CpuAsynchronous);
121
122ENUM(AstcRecompression, Uncompressed, Bc1, Bc3);
123
124ENUM(VSyncMode, Immediate, Mailbox, Fifo, FifoRelaxed);
125
126ENUM(RendererBackend, OpenGL, Vulkan, Null);
127
128ENUM(ShaderBackend, Glsl, Glasm, SpirV);
129
130ENUM(GpuAccuracy, Normal, High, Extreme);
131
132ENUM(CpuAccuracy, Auto, Accurate, Unsafe, Paranoid);
133
134ENUM(MemoryLayout, Memory_4Gb, Memory_6Gb, Memory_8Gb);
135
136ENUM(FullscreenMode, Borderless, Exclusive);
137
138ENUM(NvdecEmulation, Off, Cpu, Gpu);
139
140ENUM(ResolutionSetup, Res1_2X, Res3_4X, Res1X, Res3_2X, Res2X, Res3X, Res4X, Res5X, Res6X, Res7X,
141 Res8X);
142
143ENUM(ScalingFilter, NearestNeighbor, Bilinear, Bicubic, Gaussian, ScaleForce, Fsr, MaxEnum);
144
145ENUM(AntiAliasing, None, Fxaa, Smaa, MaxEnum);
146
147ENUM(AspectRatio, R16_9, R4_3, R21_9, R16_10, Stretch);
148
149template <typename Type>
150constexpr std::string CanonicalizeEnum(Type id) {
151 const auto group = EnumMetadata<Type>::Canonicalizations();
152 for (auto& [name, value] : group) {
153 if (value == id) {
154 return name;
155 }
156 }
157 return "unknown";
158}
159
160template <typename Type>
161constexpr Type ToEnum(const std::string& canonicalization) {
162 const auto group = EnumMetadata<Type>::Canonicalizations();
163 for (auto& [name, value] : group) {
164 if (name == canonicalization) {
165 return value;
166 }
167 }
168 return {};
169}
170} // namespace Settings
171
172#undef ENUM
173#undef PAIR
174#undef PAIR_1
175#undef PAIR_2
176#undef PAIR_3
177#undef PAIR_4
178#undef PAIR_5
179#undef PAIR_6
180#undef PAIR_7
181#undef PAIR_8
182#undef PAIR_9
183#undef PAIR_10
184#undef PAIR_12
185#undef PAIR_13
186#undef PAIR_14
187#undef PAIR_15
188#undef PAIR_16
189#undef PAIR_17
190#undef PAIR_18
191#undef PAIR_19
192#undef PAIR_20
193#undef PAIR_22
194#undef PAIR_23
195#undef PAIR_24
196#undef PAIR_25
197#undef PAIR_26
198#undef PAIR_27
199#undef PAIR_28
200#undef PAIR_29
201#undef PAIR_30
202#undef PAIR_32
203#undef PAIR_33
204#undef PAIR_34
205#undef PAIR_35
206#undef PAIR_36
207#undef PAIR_37
208#undef PAIR_38
209#undef PAIR_39
210#undef PAIR_40
211#undef PAIR_42
212#undef PAIR_43
213#undef PAIR_44
214#undef PAIR_45
diff --git a/src/common/settings_setting.h b/src/common/settings_setting.h
new file mode 100644
index 000000000..a8beb06e9
--- /dev/null
+++ b/src/common/settings_setting.h
@@ -0,0 +1,394 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <limits>
7#include <map>
8#include <optional>
9#include <stdexcept>
10#include <string>
11#include <typeindex>
12#include <typeinfo>
13#include "common/common_types.h"
14#include "common/settings_common.h"
15#include "common/settings_enums.h"
16
17namespace Settings {
18
19/** The Setting class is a simple resource manager. It defines a label and default value
20 * alongside the actual value of the setting for simpler and less-error prone use with frontend
21 * configurations. Specifying a default value and label is required. A minimum and maximum range
22 * can be specified for sanitization.
23 */
24template <typename Type, bool ranged = false>
25class Setting : public BasicSetting {
26protected:
27 Setting() = default;
28
29public:
30 /**
31 * Sets a default value, label, and setting value.
32 *
33 * @param linkage Setting registry
34 * @param default_val Initial value of the setting, and default value of the setting
35 * @param name Label for the setting
36 * @param category_ Category of the setting AKA INI group
37 * @param specialization_ Suggestion for how frontend implementations represent this in a config
38 * @param save_ Suggests that this should or should not be saved to a frontend config file
39 * @param runtime_modifiable_ Suggests whether this is modifiable while a guest is loaded
40 * @param other_setting_ A second Setting to associate to this one in metadata
41 */
42 explicit Setting(Linkage& linkage, const Type& default_val, const std::string& name,
43 Category category_, u32 specialization_ = Specialization::Default,
44 bool save_ = true, bool runtime_modifiable_ = false,
45 BasicSetting* other_setting_ = nullptr)
46 requires(!ranged)
47 : BasicSetting(linkage, name, category_, save_, runtime_modifiable_, specialization_,
48 other_setting_),
49 value{default_val}, default_value{default_val} {}
50 virtual ~Setting() = default;
51
52 /**
53 * Sets a default value, minimum value, maximum value, and label.
54 *
55 * @param linkage Setting registry
56 * @param default_val Initial value of the setting, and default value of the setting
57 * @param min_val Sets the minimum allowed value of the setting
58 * @param max_val Sets the maximum allowed value of the setting
59 * @param name Label for the setting
60 * @param category_ Category of the setting AKA INI group
61 * @param specialization_ Suggestion for how frontend implementations represent this in a config
62 * @param save_ Suggests that this should or should not be saved to a frontend config file
63 * @param runtime_modifiable_ Suggests whether this is modifiable while a guest is loaded
64 * @param other_setting_ A second Setting to associate to this one in metadata
65 */
66 explicit Setting(Linkage& linkage, const Type& default_val, const Type& min_val,
67 const Type& max_val, const std::string& name, Category category_,
68 u32 specialization_ = Specialization::Default, bool save_ = true,
69 bool runtime_modifiable_ = false, BasicSetting* other_setting_ = nullptr)
70 requires(ranged)
71 : BasicSetting(linkage, name, category_, save_, runtime_modifiable_, specialization_,
72 other_setting_),
73 value{default_val}, default_value{default_val}, maximum{max_val}, minimum{min_val} {}
74
75 /**
76 * Returns a reference to the setting's value.
77 *
78 * @returns A reference to the setting
79 */
80 [[nodiscard]] virtual const Type& GetValue() const {
81 return value;
82 }
83
84 /**
85 * Sets the setting to the given value.
86 *
87 * @param val The desired value
88 */
89 virtual void SetValue(const Type& val) {
90 Type temp{ranged ? std::clamp(val, minimum, maximum) : val};
91 std::swap(value, temp);
92 }
93
94 /**
95 * Returns the value that this setting was created with.
96 *
97 * @returns A reference to the default value
98 */
99 [[nodiscard]] const Type& GetDefault() const {
100 return default_value;
101 }
102
103 [[nodiscard]] constexpr bool IsEnum() const override {
104 return std::is_enum_v<Type>;
105 }
106
107protected:
108 [[nodiscard]] std::string ToString(const Type& value_) const {
109 if constexpr (std::is_same_v<Type, std::string>) {
110 return value_;
111 } else if constexpr (std::is_same_v<Type, std::optional<u32>>) {
112 return value_.has_value() ? std::to_string(*value_) : "none";
113 } else if constexpr (std::is_same_v<Type, bool>) {
114 return value_ ? "true" : "false";
115 } else if constexpr (std::is_same_v<Type, AudioEngine>) {
116 // Compatibility with old AudioEngine setting being a string
117 return CanonicalizeEnum(value_);
118 } else {
119 return std::to_string(static_cast<u64>(value_));
120 }
121 }
122
123public:
124 /**
125 * Converts the value of the setting to a std::string. Respects the global state if the setting
126 * has one.
127 *
128 * @returns The current setting as a std::string
129 */
130 [[nodiscard]] std::string ToString() const override {
131 return ToString(this->GetValue());
132 }
133
134 /**
135 * Returns the default value of the setting as a std::string.
136 *
137 * @returns The default value as a string.
138 */
139 [[nodiscard]] std::string DefaultToString() const override {
140 return ToString(default_value);
141 }
142
143 /**
144 * Assigns a value to the setting.
145 *
146 * @param val The desired setting value
147 *
148 * @returns A reference to the setting
149 */
150 virtual const Type& operator=(const Type& val) {
151 Type temp{ranged ? std::clamp(val, minimum, maximum) : val};
152 std::swap(value, temp);
153 return value;
154 }
155
156 /**
157 * Returns a reference to the setting.
158 *
159 * @returns A reference to the setting
160 */
161 explicit virtual operator const Type&() const {
162 return value;
163 }
164
165 /**
166 * Converts the given value to the Setting's type of value. Uses SetValue to enter the setting,
167 * thus respecting its constraints.
168 *
169 * @param input The desired value
170 */
171 void LoadString(const std::string& input) override final {
172 if (input.empty()) {
173 this->SetValue(this->GetDefault());
174 return;
175 }
176 try {
177 if constexpr (std::is_same_v<Type, std::string>) {
178 this->SetValue(input);
179 } else if constexpr (std::is_same_v<Type, std::optional<u32>>) {
180 this->SetValue(static_cast<u32>(std::stoul(input)));
181 } else if constexpr (std::is_same_v<Type, bool>) {
182 this->SetValue(input == "true");
183 } else if constexpr (std::is_same_v<Type, AudioEngine>) {
184 this->SetValue(ToEnum<Type>(input));
185 } else {
186 this->SetValue(static_cast<Type>(std::stoll(input)));
187 }
188 } catch (std::invalid_argument&) {
189 this->SetValue(this->GetDefault());
190 }
191 }
192
193 [[nodiscard]] std::string constexpr Canonicalize() const override final {
194 if constexpr (std::is_enum_v<Type>) {
195 return CanonicalizeEnum(this->GetValue());
196 } else {
197 return ToString(this->GetValue());
198 }
199 }
200
201 /**
202 * Gives us another way to identify the setting without having to go through a string.
203 *
204 * @returns the type_index of the setting's type
205 */
206 [[nodiscard]] std::type_index TypeId() const override final {
207 return std::type_index(typeid(Type));
208 }
209
210 [[nodiscard]] constexpr u32 EnumIndex() const override final {
211 if constexpr (std::is_enum_v<Type>) {
212 return EnumMetadata<Type>::Index();
213 } else {
214 return std::numeric_limits<u32>::max();
215 }
216 }
217
218 [[nodiscard]] std::string MinVal() const override final {
219 return this->ToString(minimum);
220 }
221 [[nodiscard]] std::string MaxVal() const override final {
222 return this->ToString(maximum);
223 }
224
225 [[nodiscard]] constexpr bool Ranged() const override {
226 return ranged;
227 }
228
229protected:
230 Type value{}; ///< The setting
231 const Type default_value{}; ///< The default value
232 const Type maximum{}; ///< Maximum allowed value of the setting
233 const Type minimum{}; ///< Minimum allowed value of the setting
234};
235
236/**
237 * The SwitchableSetting class is a slightly more complex version of the Setting class. This adds a
238 * custom setting to switch to when a guest application specifically requires it. The effect is that
239 * other components of the emulator can access the setting's intended value without any need for the
240 * component to ask whether the custom or global setting is needed at the moment.
241 *
242 * By default, the global setting is used.
243 */
244template <typename Type, bool ranged = false>
245class SwitchableSetting : virtual public Setting<Type, ranged> {
246public:
247 /**
248 * Sets a default value, label, and setting value.
249 *
250 * @param linkage Setting registry
251 * @param default_val Initial value of the setting, and default value of the setting
252 * @param name Label for the setting
253 * @param category_ Category of the setting AKA INI group
254 * @param specialization_ Suggestion for how frontend implementations represent this in a config
255 * @param save_ Suggests that this should or should not be saved to a frontend config file
256 * @param runtime_modifiable_ Suggests whether this is modifiable while a guest is loaded
257 * @param other_setting_ A second Setting to associate to this one in metadata
258 */
259 explicit SwitchableSetting(Linkage& linkage, const Type& default_val, const std::string& name,
260 Category category_, u32 specialization_ = Specialization::Default,
261 bool save_ = true, bool runtime_modifiable_ = false,
262 BasicSetting* other_setting_ = nullptr)
263 requires(!ranged)
264 : Setting<Type, false>{
265 linkage, default_val, name, category_, specialization_,
266 save_, runtime_modifiable_, other_setting_} {
267 linkage.restore_functions.emplace_back([this]() { this->SetGlobal(true); });
268 }
269 virtual ~SwitchableSetting() = default;
270
271 /**
272 * Sets a default value, minimum value, maximum value, and label.
273 *
274 * @param linkage Setting registry
275 * @param default_val Initial value of the setting, and default value of the setting
276 * @param min_val Sets the minimum allowed value of the setting
277 * @param max_val Sets the maximum allowed value of the setting
278 * @param name Label for the setting
279 * @param category_ Category of the setting AKA INI group
280 * @param specialization_ Suggestion for how frontend implementations represent this in a config
281 * @param save_ Suggests that this should or should not be saved to a frontend config file
282 * @param runtime_modifiable_ Suggests whether this is modifiable while a guest is loaded
283 * @param other_setting_ A second Setting to associate to this one in metadata
284 */
285 explicit SwitchableSetting(Linkage& linkage, const Type& default_val, const Type& min_val,
286 const Type& max_val, const std::string& name, Category category_,
287 u32 specialization_ = Specialization::Default, bool save_ = true,
288 bool runtime_modifiable_ = false,
289 BasicSetting* other_setting_ = nullptr)
290 requires(ranged)
291 : Setting<Type, true>{linkage, default_val, min_val,
292 max_val, name, category_,
293 specialization_, save_, runtime_modifiable_,
294 other_setting_} {
295 linkage.restore_functions.emplace_back([this]() { this->SetGlobal(true); });
296 }
297
298 /**
299 * Tells this setting to represent either the global or custom setting when other member
300 * functions are used.
301 *
302 * @param to_global Whether to use the global or custom setting.
303 */
304 void SetGlobal(bool to_global) override final {
305 use_global = to_global;
306 }
307
308 /**
309 * Returns whether this setting is using the global setting or not.
310 *
311 * @returns The global state
312 */
313 [[nodiscard]] bool UsingGlobal() const override final {
314 return use_global;
315 }
316
317 /**
318 * Returns either the global or custom setting depending on the values of this setting's global
319 * state or if the global value was specifically requested.
320 *
321 * @param need_global Request global value regardless of setting's state; defaults to false
322 *
323 * @returns The required value of the setting
324 */
325 [[nodiscard]] const Type& GetValue() const override final {
326 if (use_global) {
327 return this->value;
328 }
329 return custom;
330 }
331 [[nodiscard]] const Type& GetValue(bool need_global) const {
332 if (use_global || need_global) {
333 return this->value;
334 }
335 return custom;
336 }
337
338 /**
339 * Sets the current setting value depending on the global state.
340 *
341 * @param val The new value
342 */
343 void SetValue(const Type& val) override final {
344 Type temp{ranged ? std::clamp(val, this->minimum, this->maximum) : val};
345 if (use_global) {
346 std::swap(this->value, temp);
347 } else {
348 std::swap(custom, temp);
349 }
350 }
351
352 [[nodiscard]] constexpr bool Switchable() const override final {
353 return true;
354 }
355
356 [[nodiscard]] std::string ToStringGlobal() const override final {
357 return this->ToString(this->value);
358 }
359
360 /**
361 * Assigns the current setting value depending on the global state.
362 *
363 * @param val The new value
364 *
365 * @returns A reference to the current setting value
366 */
367 const Type& operator=(const Type& val) override final {
368 Type temp{ranged ? std::clamp(val, this->minimum, this->maximum) : val};
369 if (use_global) {
370 std::swap(this->value, temp);
371 return this->value;
372 }
373 std::swap(custom, temp);
374 return custom;
375 }
376
377 /**
378 * Returns the current setting value depending on the global state.
379 *
380 * @returns A reference to the current setting value
381 */
382 explicit operator const Type&() const override final {
383 if (use_global) {
384 return this->value;
385 }
386 return custom;
387 }
388
389protected:
390 bool use_global{true}; ///< The setting's global state
391 Type custom{}; ///< The custom value of the setting
392};
393
394} // namespace Settings
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
index c97158a71..44a297cdc 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
@@ -287,7 +287,7 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
287 } 287 }
288 } else { 288 } else {
289 // Unsafe optimizations 289 // Unsafe optimizations
290 if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Unsafe) { 290 if (Settings::values.cpu_accuracy.GetValue() == Settings::CpuAccuracy::Unsafe) {
291 config.unsafe_optimizations = true; 291 config.unsafe_optimizations = true;
292 if (Settings::values.cpuopt_unsafe_unfuse_fma) { 292 if (Settings::values.cpuopt_unsafe_unfuse_fma) {
293 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA; 293 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
@@ -307,7 +307,7 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
307 } 307 }
308 308
309 // Curated optimizations 309 // Curated optimizations
310 if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Auto) { 310 if (Settings::values.cpu_accuracy.GetValue() == Settings::CpuAccuracy::Auto) {
311 config.unsafe_optimizations = true; 311 config.unsafe_optimizations = true;
312 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA; 312 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
313 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreStandardFPCRValue; 313 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreStandardFPCRValue;
@@ -316,7 +316,7 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
316 } 316 }
317 317
318 // Paranoia mode for debugging optimizations 318 // Paranoia mode for debugging optimizations
319 if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Paranoid) { 319 if (Settings::values.cpu_accuracy.GetValue() == Settings::CpuAccuracy::Paranoid) {
320 config.unsafe_optimizations = false; 320 config.unsafe_optimizations = false;
321 config.optimizations = Dynarmic::no_optimizations; 321 config.optimizations = Dynarmic::no_optimizations;
322 } 322 }
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
index 791d466ca..2e3674b6d 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
@@ -347,7 +347,7 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
347 } 347 }
348 } else { 348 } else {
349 // Unsafe optimizations 349 // Unsafe optimizations
350 if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Unsafe) { 350 if (Settings::values.cpu_accuracy.GetValue() == Settings::CpuAccuracy::Unsafe) {
351 config.unsafe_optimizations = true; 351 config.unsafe_optimizations = true;
352 if (Settings::values.cpuopt_unsafe_unfuse_fma) { 352 if (Settings::values.cpuopt_unsafe_unfuse_fma) {
353 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA; 353 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
@@ -367,7 +367,7 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
367 } 367 }
368 368
369 // Curated optimizations 369 // Curated optimizations
370 if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Auto) { 370 if (Settings::values.cpu_accuracy.GetValue() == Settings::CpuAccuracy::Auto) {
371 config.unsafe_optimizations = true; 371 config.unsafe_optimizations = true;
372 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA; 372 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
373 config.fastmem_address_space_bits = 64; 373 config.fastmem_address_space_bits = 64;
@@ -375,7 +375,7 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
375 } 375 }
376 376
377 // Paranoia mode for debugging optimizations 377 // Paranoia mode for debugging optimizations
378 if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Paranoid) { 378 if (Settings::values.cpu_accuracy.GetValue() == Settings::CpuAccuracy::Paranoid) {
379 config.unsafe_optimizations = false; 379 config.unsafe_optimizations = false;
380 config.optimizations = Dynarmic::no_optimizations; 380 config.optimizations = Dynarmic::no_optimizations;
381 } 381 }
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 48233d7c8..2f67e60a9 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -12,6 +12,7 @@
12#include "common/logging/log.h" 12#include "common/logging/log.h"
13#include "common/microprofile.h" 13#include "common/microprofile.h"
14#include "common/settings.h" 14#include "common/settings.h"
15#include "common/settings_enums.h"
15#include "common/string_util.h" 16#include "common/string_util.h"
16#include "core/arm/exclusive_monitor.h" 17#include "core/arm/exclusive_monitor.h"
17#include "core/core.h" 18#include "core/core.h"
@@ -140,16 +141,13 @@ struct System::Impl {
140 device_memory = std::make_unique<Core::DeviceMemory>(); 141 device_memory = std::make_unique<Core::DeviceMemory>();
141 142
142 is_multicore = Settings::values.use_multi_core.GetValue(); 143 is_multicore = Settings::values.use_multi_core.GetValue();
143 extended_memory_layout = Settings::values.use_unsafe_extended_memory_layout.GetValue(); 144 extended_memory_layout =
145 Settings::values.memory_layout_mode.GetValue() != Settings::MemoryLayout::Memory_4Gb;
144 146
145 core_timing.SetMulticore(is_multicore); 147 core_timing.SetMulticore(is_multicore);
146 core_timing.Initialize([&system]() { system.RegisterHostThread(); }); 148 core_timing.Initialize([&system]() { system.RegisterHostThread(); });
147 149
148 const auto posix_time = std::chrono::system_clock::now().time_since_epoch(); 150 RefreshTime();
149 const auto current_time =
150 std::chrono::duration_cast<std::chrono::seconds>(posix_time).count();
151 Settings::values.custom_rtc_differential =
152 Settings::values.custom_rtc.value_or(current_time) - current_time;
153 151
154 // Create a default fs if one doesn't already exist. 152 // Create a default fs if one doesn't already exist.
155 if (virtual_filesystem == nullptr) { 153 if (virtual_filesystem == nullptr) {
@@ -172,7 +170,8 @@ struct System::Impl {
172 void ReinitializeIfNecessary(System& system) { 170 void ReinitializeIfNecessary(System& system) {
173 const bool must_reinitialize = 171 const bool must_reinitialize =
174 is_multicore != Settings::values.use_multi_core.GetValue() || 172 is_multicore != Settings::values.use_multi_core.GetValue() ||
175 extended_memory_layout != Settings::values.use_unsafe_extended_memory_layout.GetValue(); 173 extended_memory_layout != (Settings::values.memory_layout_mode.GetValue() !=
174 Settings::MemoryLayout::Memory_4Gb);
176 175
177 if (!must_reinitialize) { 176 if (!must_reinitialize) {
178 return; 177 return;
@@ -181,11 +180,22 @@ struct System::Impl {
181 LOG_DEBUG(Kernel, "Re-initializing"); 180 LOG_DEBUG(Kernel, "Re-initializing");
182 181
183 is_multicore = Settings::values.use_multi_core.GetValue(); 182 is_multicore = Settings::values.use_multi_core.GetValue();
184 extended_memory_layout = Settings::values.use_unsafe_extended_memory_layout.GetValue(); 183 extended_memory_layout =
184 Settings::values.memory_layout_mode.GetValue() != Settings::MemoryLayout::Memory_4Gb;
185 185
186 Initialize(system); 186 Initialize(system);
187 } 187 }
188 188
189 void RefreshTime() {
190 const auto posix_time = std::chrono::system_clock::now().time_since_epoch();
191 const auto current_time =
192 std::chrono::duration_cast<std::chrono::seconds>(posix_time).count();
193 Settings::values.custom_rtc_differential =
194 (Settings::values.custom_rtc_enabled ? Settings::values.custom_rtc.GetValue()
195 : current_time) -
196 current_time;
197 }
198
189 void Run() { 199 void Run() {
190 std::unique_lock<std::mutex> lk(suspend_guard); 200 std::unique_lock<std::mutex> lk(suspend_guard);
191 201
@@ -1028,6 +1038,8 @@ void System::Exit() {
1028} 1038}
1029 1039
1030void System::ApplySettings() { 1040void System::ApplySettings() {
1041 impl->RefreshTime();
1042
1031 if (IsPoweredOn()) { 1043 if (IsPoweredOn()) {
1032 Renderer().RefreshBaseSettings(); 1044 Renderer().RefreshBaseSettings();
1033 } 1045 }
diff --git a/src/core/file_sys/control_metadata.cpp b/src/core/file_sys/control_metadata.cpp
index cd9ac2e75..0697c29ae 100644
--- a/src/core/file_sys/control_metadata.cpp
+++ b/src/core/file_sys/control_metadata.cpp
@@ -68,7 +68,8 @@ NACP::NACP(VirtualFile file) {
68NACP::~NACP() = default; 68NACP::~NACP() = default;
69 69
70const LanguageEntry& NACP::GetLanguageEntry() const { 70const LanguageEntry& NACP::GetLanguageEntry() const {
71 Language language = language_to_codes[Settings::values.language_index.GetValue()]; 71 Language language =
72 language_to_codes[static_cast<s32>(Settings::values.language_index.GetValue())];
72 73
73 { 74 {
74 const auto& language_entry = raw.language_entries.at(static_cast<u8>(language)); 75 const auto& language_entry = raw.language_entries.at(static_cast<u8>(language));
diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp
index d3286b352..2ba1b34a4 100644
--- a/src/core/file_sys/patch_manager.cpp
+++ b/src/core/file_sys/patch_manager.cpp
@@ -626,8 +626,8 @@ PatchManager::Metadata PatchManager::ParseControlNCA(const NCA& nca) const {
626 auto nacp = nacp_file == nullptr ? nullptr : std::make_unique<NACP>(nacp_file); 626 auto nacp = nacp_file == nullptr ? nullptr : std::make_unique<NACP>(nacp_file);
627 627
628 // Get language code from settings 628 // Get language code from settings
629 const auto language_code = 629 const auto language_code = Service::Set::GetLanguageCodeFromIndex(
630 Service::Set::GetLanguageCodeFromIndex(Settings::values.language_index.GetValue()); 630 static_cast<u32>(Settings::values.language_index.GetValue()));
631 631
632 // Convert to application language and get priority list 632 // Convert to application language and get priority list
633 const auto application_language = 633 const auto application_language =
diff --git a/src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp b/src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp
index 49bdc671e..4cfdf4558 100644
--- a/src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp
+++ b/src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp
@@ -35,13 +35,27 @@ namespace {
35using namespace Common::Literals; 35using namespace Common::Literals;
36 36
37u32 GetMemorySizeForInit() { 37u32 GetMemorySizeForInit() {
38 return Settings::values.use_unsafe_extended_memory_layout ? Smc::MemorySize_8GB 38 switch (Settings::values.memory_layout_mode.GetValue()) {
39 : Smc::MemorySize_4GB; 39 case Settings::MemoryLayout::Memory_4Gb:
40 return Smc::MemorySize_4GB;
41 case Settings::MemoryLayout::Memory_6Gb:
42 return Smc::MemorySize_6GB;
43 case Settings::MemoryLayout::Memory_8Gb:
44 return Smc::MemorySize_8GB;
45 }
46 return Smc::MemorySize_4GB;
40} 47}
41 48
42Smc::MemoryArrangement GetMemoryArrangeForInit() { 49Smc::MemoryArrangement GetMemoryArrangeForInit() {
43 return Settings::values.use_unsafe_extended_memory_layout ? Smc::MemoryArrangement_8GB 50 switch (Settings::values.memory_layout_mode.GetValue()) {
44 : Smc::MemoryArrangement_4GB; 51 case Settings::MemoryLayout::Memory_4Gb:
52 return Smc::MemoryArrangement_4GB;
53 case Settings::MemoryLayout::Memory_6Gb:
54 return Smc::MemoryArrangement_6GB;
55 case Settings::MemoryLayout::Memory_8Gb:
56 return Smc::MemoryArrangement_8GB;
57 }
58 return Smc::MemoryArrangement_4GB;
45} 59}
46} // namespace 60} // namespace
47 61
diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp
index 44c7cb22f..e573e2a57 100644
--- a/src/core/hle/kernel/k_process.cpp
+++ b/src/core/hle/kernel/k_process.cpp
@@ -81,7 +81,8 @@ Result KProcess::Initialize(KProcess* process, Core::System& system, std::string
81 process->m_capabilities.InitializeForMetadatalessProcess(); 81 process->m_capabilities.InitializeForMetadatalessProcess();
82 process->m_is_initialized = true; 82 process->m_is_initialized = true;
83 83
84 std::mt19937 rng(Settings::values.rng_seed.GetValue().value_or(std::time(nullptr))); 84 std::mt19937 rng(Settings::values.rng_seed_enabled ? Settings::values.rng_seed.GetValue()
85 : static_cast<u32>(std::time(nullptr)));
85 std::uniform_int_distribution<u64> distribution; 86 std::uniform_int_distribution<u64> distribution;
86 std::generate(process->m_random_entropy.begin(), process->m_random_entropy.end(), 87 std::generate(process->m_random_entropy.begin(), process->m_random_entropy.end(),
87 [&] { return distribution(rng); }); 88 [&] { return distribution(rng); });
diff --git a/src/core/hle/service/ns/ns.cpp b/src/core/hle/service/ns/ns.cpp
index 376067a95..91c5a2182 100644
--- a/src/core/hle/service/ns/ns.cpp
+++ b/src/core/hle/service/ns/ns.cpp
@@ -409,7 +409,7 @@ ResultVal<u8> IApplicationManagerInterface::GetApplicationDesiredLanguage(
409 409
410 // Get language code from settings 410 // Get language code from settings
411 const auto language_code = 411 const auto language_code =
412 Set::GetLanguageCodeFromIndex(Settings::values.language_index.GetValue()); 412 Set::GetLanguageCodeFromIndex(static_cast<s32>(Settings::values.language_index.GetValue()));
413 413
414 // Convert to application language, get priority list 414 // Convert to application language, get priority list
415 const auto application_language = ConvertToApplicationLanguage(language_code); 415 const auto application_language = ConvertToApplicationLanguage(language_code);
diff --git a/src/core/hle/service/set/set.cpp b/src/core/hle/service/set/set.cpp
index f5788b481..83f888c54 100644
--- a/src/core/hle/service/set/set.cpp
+++ b/src/core/hle/service/set/set.cpp
@@ -93,7 +93,8 @@ void GetAvailableLanguageCodesImpl(HLERequestContext& ctx, std::size_t max_entri
93} 93}
94 94
95void GetKeyCodeMapImpl(HLERequestContext& ctx) { 95void GetKeyCodeMapImpl(HLERequestContext& ctx) {
96 const auto language_code = available_language_codes[Settings::values.language_index.GetValue()]; 96 const auto language_code =
97 available_language_codes[static_cast<s32>(Settings::values.language_index.GetValue())];
97 const auto key_code = 98 const auto key_code =
98 std::find_if(language_to_layout.cbegin(), language_to_layout.cend(), 99 std::find_if(language_to_layout.cbegin(), language_to_layout.cend(),
99 [=](const auto& element) { return element.first == language_code; }); 100 [=](const auto& element) { return element.first == language_code; });
@@ -162,7 +163,7 @@ void SET::GetQuestFlag(HLERequestContext& ctx) {
162 163
163 IPC::ResponseBuilder rb{ctx, 3}; 164 IPC::ResponseBuilder rb{ctx, 3};
164 rb.Push(ResultSuccess); 165 rb.Push(ResultSuccess);
165 rb.Push(static_cast<u32>(Settings::values.quest_flag.GetValue())); 166 rb.Push(static_cast<s32>(Settings::values.quest_flag.GetValue()));
166} 167}
167 168
168void SET::GetLanguageCode(HLERequestContext& ctx) { 169void SET::GetLanguageCode(HLERequestContext& ctx) {
@@ -170,7 +171,8 @@ void SET::GetLanguageCode(HLERequestContext& ctx) {
170 171
171 IPC::ResponseBuilder rb{ctx, 4}; 172 IPC::ResponseBuilder rb{ctx, 4};
172 rb.Push(ResultSuccess); 173 rb.Push(ResultSuccess);
173 rb.PushEnum(available_language_codes[Settings::values.language_index.GetValue()]); 174 rb.PushEnum(
175 available_language_codes[static_cast<s32>(Settings::values.language_index.GetValue())]);
174} 176}
175 177
176void SET::GetRegionCode(HLERequestContext& ctx) { 178void SET::GetRegionCode(HLERequestContext& ctx) {
@@ -178,7 +180,7 @@ void SET::GetRegionCode(HLERequestContext& ctx) {
178 180
179 IPC::ResponseBuilder rb{ctx, 3}; 181 IPC::ResponseBuilder rb{ctx, 3};
180 rb.Push(ResultSuccess); 182 rb.Push(ResultSuccess);
181 rb.Push(Settings::values.region_index.GetValue()); 183 rb.Push(static_cast<u32>(Settings::values.region_index.GetValue()));
182} 184}
183 185
184void SET::GetKeyCodeMap(HLERequestContext& ctx) { 186void SET::GetKeyCodeMap(HLERequestContext& ctx) {
diff --git a/src/core/hle/service/spl/spl_module.cpp b/src/core/hle/service/spl/spl_module.cpp
index 0227d4393..cd631b2ea 100644
--- a/src/core/hle/service/spl/spl_module.cpp
+++ b/src/core/hle/service/spl/spl_module.cpp
@@ -19,7 +19,8 @@ namespace Service::SPL {
19Module::Interface::Interface(Core::System& system_, std::shared_ptr<Module> module_, 19Module::Interface::Interface(Core::System& system_, std::shared_ptr<Module> module_,
20 const char* name) 20 const char* name)
21 : ServiceFramework{system_, name}, module{std::move(module_)}, 21 : ServiceFramework{system_, name}, module{std::move(module_)},
22 rng(Settings::values.rng_seed.GetValue().value_or(std::time(nullptr))) {} 22 rng(Settings::values.rng_seed_enabled ? Settings::values.rng_seed.GetValue()
23 : static_cast<u32>(std::time(nullptr))) {}
23 24
24Module::Interface::~Interface() = default; 25Module::Interface::~Interface() = default;
25 26
diff --git a/src/core/hle/service/time/time_zone_content_manager.cpp b/src/core/hle/service/time/time_zone_content_manager.cpp
index 3b6047ad0..1b96de37a 100644
--- a/src/core/hle/service/time/time_zone_content_manager.cpp
+++ b/src/core/hle/service/time/time_zone_content_manager.cpp
@@ -78,7 +78,8 @@ TimeZoneContentManager::TimeZoneContentManager(Core::System& system_)
78 location_name_cache{BuildLocationNameCache(time_zone_binary)} {} 78 location_name_cache{BuildLocationNameCache(time_zone_binary)} {}
79 79
80void TimeZoneContentManager::Initialize(TimeManager& time_manager) { 80void TimeZoneContentManager::Initialize(TimeManager& time_manager) {
81 const auto timezone_setting = Settings::GetTimeZoneString(); 81 const auto timezone_setting =
82 Settings::GetTimeZoneString(Settings::values.time_zone_index.GetValue());
82 83
83 if (FileSys::VirtualFile vfs_file; 84 if (FileSys::VirtualFile vfs_file;
84 GetTimeZoneInfoFile(timezone_setting, vfs_file) == ResultSuccess) { 85 GetTimeZoneInfoFile(timezone_setting, vfs_file) == ResultSuccess) {
diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp
index 7a2f3c90a..62b3f6636 100644
--- a/src/core/telemetry_session.cpp
+++ b/src/core/telemetry_session.cpp
@@ -61,13 +61,13 @@ static const char* TranslateRenderer(Settings::RendererBackend backend) {
61 return "Unknown"; 61 return "Unknown";
62} 62}
63 63
64static const char* TranslateGPUAccuracyLevel(Settings::GPUAccuracy backend) { 64static const char* TranslateGPUAccuracyLevel(Settings::GpuAccuracy backend) {
65 switch (backend) { 65 switch (backend) {
66 case Settings::GPUAccuracy::Normal: 66 case Settings::GpuAccuracy::Normal:
67 return "Normal"; 67 return "Normal";
68 case Settings::GPUAccuracy::High: 68 case Settings::GpuAccuracy::High:
69 return "High"; 69 return "High";
70 case Settings::GPUAccuracy::Extreme: 70 case Settings::GpuAccuracy::Extreme:
71 return "Extreme"; 71 return "Extreme";
72 } 72 }
73 return "Unknown"; 73 return "Unknown";
@@ -77,9 +77,9 @@ static const char* TranslateNvdecEmulation(Settings::NvdecEmulation backend) {
77 switch (backend) { 77 switch (backend) {
78 case Settings::NvdecEmulation::Off: 78 case Settings::NvdecEmulation::Off:
79 return "Off"; 79 return "Off";
80 case Settings::NvdecEmulation::CPU: 80 case Settings::NvdecEmulation::Cpu:
81 return "CPU"; 81 return "CPU";
82 case Settings::NvdecEmulation::GPU: 82 case Settings::NvdecEmulation::Gpu:
83 return "GPU"; 83 return "GPU";
84 } 84 }
85 return "Unknown"; 85 return "Unknown";
@@ -91,14 +91,26 @@ static constexpr const char* TranslateVSyncMode(Settings::VSyncMode mode) {
91 return "Immediate"; 91 return "Immediate";
92 case Settings::VSyncMode::Mailbox: 92 case Settings::VSyncMode::Mailbox:
93 return "Mailbox"; 93 return "Mailbox";
94 case Settings::VSyncMode::FIFO: 94 case Settings::VSyncMode::Fifo:
95 return "FIFO"; 95 return "FIFO";
96 case Settings::VSyncMode::FIFORelaxed: 96 case Settings::VSyncMode::FifoRelaxed:
97 return "FIFO Relaxed"; 97 return "FIFO Relaxed";
98 } 98 }
99 return "Unknown"; 99 return "Unknown";
100} 100}
101 101
102static constexpr const char* TranslateASTCDecodeMode(Settings::AstcDecodeMode mode) {
103 switch (mode) {
104 case Settings::AstcDecodeMode::Cpu:
105 return "CPU";
106 case Settings::AstcDecodeMode::Gpu:
107 return "GPU";
108 case Settings::AstcDecodeMode::CpuAsynchronous:
109 return "CPU Asynchronous";
110 }
111 return "Unknown";
112}
113
102u64 GetTelemetryId() { 114u64 GetTelemetryId() {
103 u64 telemetry_id{}; 115 u64 telemetry_id{};
104 const auto filename = Common::FS::GetYuzuPath(Common::FS::YuzuPath::ConfigDir) / "telemetry_id"; 116 const auto filename = Common::FS::GetYuzuPath(Common::FS::YuzuPath::ConfigDir) / "telemetry_id";
@@ -240,7 +252,8 @@ void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader,
240 252
241 // Log user configuration information 253 // Log user configuration information
242 constexpr auto field_type = Telemetry::FieldType::UserConfig; 254 constexpr auto field_type = Telemetry::FieldType::UserConfig;
243 AddField(field_type, "Audio_SinkId", Settings::values.sink_id.GetValue()); 255 AddField(field_type, "Audio_SinkId",
256 Settings::CanonicalizeEnum(Settings::values.sink_id.GetValue()));
244 AddField(field_type, "Core_UseMultiCore", Settings::values.use_multi_core.GetValue()); 257 AddField(field_type, "Core_UseMultiCore", Settings::values.use_multi_core.GetValue());
245 AddField(field_type, "Renderer_Backend", 258 AddField(field_type, "Renderer_Backend",
246 TranslateRenderer(Settings::values.renderer_backend.GetValue())); 259 TranslateRenderer(Settings::values.renderer_backend.GetValue()));
@@ -254,7 +267,8 @@ void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader,
254 Settings::values.use_asynchronous_gpu_emulation.GetValue()); 267 Settings::values.use_asynchronous_gpu_emulation.GetValue());
255 AddField(field_type, "Renderer_NvdecEmulation", 268 AddField(field_type, "Renderer_NvdecEmulation",
256 TranslateNvdecEmulation(Settings::values.nvdec_emulation.GetValue())); 269 TranslateNvdecEmulation(Settings::values.nvdec_emulation.GetValue()));
257 AddField(field_type, "Renderer_AccelerateASTC", Settings::values.accelerate_astc.GetValue()); 270 AddField(field_type, "Renderer_AccelerateASTC",
271 TranslateASTCDecodeMode(Settings::values.accelerate_astc.GetValue()));
258 AddField(field_type, "Renderer_UseVsync", 272 AddField(field_type, "Renderer_UseVsync",
259 TranslateVSyncMode(Settings::values.vsync_mode.GetValue())); 273 TranslateVSyncMode(Settings::values.vsync_mode.GetValue()));
260 AddField(field_type, "Renderer_ShaderBackend", 274 AddField(field_type, "Renderer_ShaderBackend",
diff --git a/src/video_core/host1x/codecs/codec.cpp b/src/video_core/host1x/codecs/codec.cpp
index da07a556f..220cce28a 100644
--- a/src/video_core/host1x/codecs/codec.cpp
+++ b/src/video_core/host1x/codecs/codec.cpp
@@ -247,7 +247,7 @@ void Codec::Initialize() {
247 av_codec = avcodec_find_decoder(codec); 247 av_codec = avcodec_find_decoder(codec);
248 248
249 InitializeAvCodecContext(); 249 InitializeAvCodecContext();
250 if (Settings::values.nvdec_emulation.GetValue() == Settings::NvdecEmulation::GPU) { 250 if (Settings::values.nvdec_emulation.GetValue() == Settings::NvdecEmulation::Gpu) {
251 InitializeGpuDecoder(); 251 InitializeGpuDecoder();
252 } 252 }
253 if (const int res = avcodec_open2(av_codec_ctx, av_codec, nullptr); res < 0) { 253 if (const int res = avcodec_open2(av_codec_ctx, av_codec, nullptr); res < 0) {
diff --git a/src/video_core/host1x/codecs/h264.cpp b/src/video_core/host1x/codecs/h264.cpp
index 862904e39..ece79b1e2 100644
--- a/src/video_core/host1x/codecs/h264.cpp
+++ b/src/video_core/host1x/codecs/h264.cpp
@@ -84,7 +84,7 @@ std::span<const u8> H264::ComposeFrame(const Host1x::NvdecCommon::NvdecRegisters
84 84
85 // TODO (ameerj): Where do we get this number, it seems to be particular for each stream 85 // TODO (ameerj): Where do we get this number, it seems to be particular for each stream
86 const auto nvdec_decoding = Settings::values.nvdec_emulation.GetValue(); 86 const auto nvdec_decoding = Settings::values.nvdec_emulation.GetValue();
87 const bool uses_gpu_decoding = nvdec_decoding == Settings::NvdecEmulation::GPU; 87 const bool uses_gpu_decoding = nvdec_decoding == Settings::NvdecEmulation::Gpu;
88 const u32 max_num_ref_frames = uses_gpu_decoding ? 6u : 16u; 88 const u32 max_num_ref_frames = uses_gpu_decoding ? 6u : 16u;
89 writer.WriteUe(max_num_ref_frames); 89 writer.WriteUe(max_num_ref_frames);
90 writer.WriteBit(false); 90 writer.WriteBit(false);
diff --git a/src/video_core/renderer_opengl/gl_compute_pipeline.cpp b/src/video_core/renderer_opengl/gl_compute_pipeline.cpp
index f9ca55c36..d70501860 100644
--- a/src/video_core/renderer_opengl/gl_compute_pipeline.cpp
+++ b/src/video_core/renderer_opengl/gl_compute_pipeline.cpp
@@ -34,13 +34,13 @@ ComputePipeline::ComputePipeline(const Device& device, TextureCache& texture_cac
34 : texture_cache{texture_cache_}, buffer_cache{buffer_cache_}, 34 : texture_cache{texture_cache_}, buffer_cache{buffer_cache_},
35 program_manager{program_manager_}, info{info_} { 35 program_manager{program_manager_}, info{info_} {
36 switch (device.GetShaderBackend()) { 36 switch (device.GetShaderBackend()) {
37 case Settings::ShaderBackend::GLSL: 37 case Settings::ShaderBackend::Glsl:
38 source_program = CreateProgram(code, GL_COMPUTE_SHADER); 38 source_program = CreateProgram(code, GL_COMPUTE_SHADER);
39 break; 39 break;
40 case Settings::ShaderBackend::GLASM: 40 case Settings::ShaderBackend::Glasm:
41 assembly_program = CompileProgram(code, GL_COMPUTE_PROGRAM_NV); 41 assembly_program = CompileProgram(code, GL_COMPUTE_PROGRAM_NV);
42 break; 42 break;
43 case Settings::ShaderBackend::SPIRV: 43 case Settings::ShaderBackend::SpirV:
44 source_program = CreateProgram(code_v, GL_COMPUTE_SHADER); 44 source_program = CreateProgram(code_v, GL_COMPUTE_SHADER);
45 break; 45 break;
46 } 46 }
diff --git a/src/video_core/renderer_opengl/gl_device.cpp b/src/video_core/renderer_opengl/gl_device.cpp
index 33e63c17d..ee140c9c2 100644
--- a/src/video_core/renderer_opengl/gl_device.cpp
+++ b/src/video_core/renderer_opengl/gl_device.cpp
@@ -177,15 +177,15 @@ Device::Device(Core::Frontend::EmuWindow& emu_window) {
177 has_fast_buffer_sub_data = is_nvidia && !disable_fast_buffer_sub_data; 177 has_fast_buffer_sub_data = is_nvidia && !disable_fast_buffer_sub_data;
178 178
179 shader_backend = Settings::values.shader_backend.GetValue(); 179 shader_backend = Settings::values.shader_backend.GetValue();
180 use_assembly_shaders = shader_backend == Settings::ShaderBackend::GLASM && 180 use_assembly_shaders = shader_backend == Settings::ShaderBackend::Glasm &&
181 GLAD_GL_NV_gpu_program5 && GLAD_GL_NV_compute_program5 && 181 GLAD_GL_NV_gpu_program5 && GLAD_GL_NV_compute_program5 &&
182 GLAD_GL_NV_transform_feedback && GLAD_GL_NV_transform_feedback2; 182 GLAD_GL_NV_transform_feedback && GLAD_GL_NV_transform_feedback2;
183 if (shader_backend == Settings::ShaderBackend::GLASM && !use_assembly_shaders) { 183 if (shader_backend == Settings::ShaderBackend::Glasm && !use_assembly_shaders) {
184 LOG_ERROR(Render_OpenGL, "Assembly shaders enabled but not supported"); 184 LOG_ERROR(Render_OpenGL, "Assembly shaders enabled but not supported");
185 shader_backend = Settings::ShaderBackend::GLSL; 185 shader_backend = Settings::ShaderBackend::Glsl;
186 } 186 }
187 187
188 if (shader_backend == Settings::ShaderBackend::GLSL && is_nvidia) { 188 if (shader_backend == Settings::ShaderBackend::Glsl && is_nvidia) {
189 const std::string_view driver_version = version.substr(13); 189 const std::string_view driver_version = version.substr(13);
190 const int version_major = 190 const int version_major =
191 std::atoi(driver_version.substr(0, driver_version.find(".")).data()); 191 std::atoi(driver_version.substr(0, driver_version.find(".")).data());
diff --git a/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp
index 71f720c63..f822fa856 100644
--- a/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp
+++ b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp
@@ -236,18 +236,18 @@ GraphicsPipeline::GraphicsPipeline(const Device& device, TextureCache& texture_c
236 force_context_flush](ShaderContext::Context*) mutable { 236 force_context_flush](ShaderContext::Context*) mutable {
237 for (size_t stage = 0; stage < 5; ++stage) { 237 for (size_t stage = 0; stage < 5; ++stage) {
238 switch (backend) { 238 switch (backend) {
239 case Settings::ShaderBackend::GLSL: 239 case Settings::ShaderBackend::Glsl:
240 if (!sources_[stage].empty()) { 240 if (!sources_[stage].empty()) {
241 source_programs[stage] = CreateProgram(sources_[stage], Stage(stage)); 241 source_programs[stage] = CreateProgram(sources_[stage], Stage(stage));
242 } 242 }
243 break; 243 break;
244 case Settings::ShaderBackend::GLASM: 244 case Settings::ShaderBackend::Glasm:
245 if (!sources_[stage].empty()) { 245 if (!sources_[stage].empty()) {
246 assembly_programs[stage] = 246 assembly_programs[stage] =
247 CompileProgram(sources_[stage], AssemblyStage(stage)); 247 CompileProgram(sources_[stage], AssemblyStage(stage));
248 } 248 }
249 break; 249 break;
250 case Settings::ShaderBackend::SPIRV: 250 case Settings::ShaderBackend::SpirV:
251 if (!sources_spirv_[stage].empty()) { 251 if (!sources_spirv_[stage].empty()) {
252 source_programs[stage] = CreateProgram(sources_spirv_[stage], Stage(stage)); 252 source_programs[stage] = CreateProgram(sources_spirv_[stage], Stage(stage));
253 } 253 }
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp
index 7e1d7f92e..618cb6354 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp
@@ -522,14 +522,14 @@ std::unique_ptr<GraphicsPipeline> ShaderCache::CreateGraphicsPipeline(
522 const auto runtime_info{ 522 const auto runtime_info{
523 MakeRuntimeInfo(key, program, previous_program, glasm_use_storage_buffers, use_glasm)}; 523 MakeRuntimeInfo(key, program, previous_program, glasm_use_storage_buffers, use_glasm)};
524 switch (device.GetShaderBackend()) { 524 switch (device.GetShaderBackend()) {
525 case Settings::ShaderBackend::GLSL: 525 case Settings::ShaderBackend::Glsl:
526 ConvertLegacyToGeneric(program, runtime_info); 526 ConvertLegacyToGeneric(program, runtime_info);
527 sources[stage_index] = EmitGLSL(profile, runtime_info, program, binding); 527 sources[stage_index] = EmitGLSL(profile, runtime_info, program, binding);
528 break; 528 break;
529 case Settings::ShaderBackend::GLASM: 529 case Settings::ShaderBackend::Glasm:
530 sources[stage_index] = EmitGLASM(profile, runtime_info, program, binding); 530 sources[stage_index] = EmitGLASM(profile, runtime_info, program, binding);
531 break; 531 break;
532 case Settings::ShaderBackend::SPIRV: 532 case Settings::ShaderBackend::SpirV:
533 ConvertLegacyToGeneric(program, runtime_info); 533 ConvertLegacyToGeneric(program, runtime_info);
534 sources_spirv[stage_index] = EmitSPIRV(profile, runtime_info, program, binding); 534 sources_spirv[stage_index] = EmitSPIRV(profile, runtime_info, program, binding);
535 break; 535 break;
@@ -582,13 +582,13 @@ std::unique_ptr<ComputePipeline> ShaderCache::CreateComputePipeline(
582 std::string code{}; 582 std::string code{};
583 std::vector<u32> code_spirv; 583 std::vector<u32> code_spirv;
584 switch (device.GetShaderBackend()) { 584 switch (device.GetShaderBackend()) {
585 case Settings::ShaderBackend::GLSL: 585 case Settings::ShaderBackend::Glsl:
586 code = EmitGLSL(profile, program); 586 code = EmitGLSL(profile, program);
587 break; 587 break;
588 case Settings::ShaderBackend::GLASM: 588 case Settings::ShaderBackend::Glasm:
589 code = EmitGLASM(profile, info, program); 589 code = EmitGLASM(profile, info, program);
590 break; 590 break;
591 case Settings::ShaderBackend::SPIRV: 591 case Settings::ShaderBackend::SpirV:
592 code_spirv = EmitSPIRV(profile, program); 592 code_spirv = EmitSPIRV(profile, program);
593 break; 593 break;
594 } 594 }
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp
index 3b446be07..9cafd2983 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp
@@ -232,10 +232,9 @@ void ApplySwizzle(GLuint handle, PixelFormat format, std::array<SwizzleSource, 4
232[[nodiscard]] bool CanBeAccelerated(const TextureCacheRuntime& runtime, 232[[nodiscard]] bool CanBeAccelerated(const TextureCacheRuntime& runtime,
233 const VideoCommon::ImageInfo& info) { 233 const VideoCommon::ImageInfo& info) {
234 if (IsPixelFormatASTC(info.format) && info.size.depth == 1 && !runtime.HasNativeASTC()) { 234 if (IsPixelFormatASTC(info.format) && info.size.depth == 1 && !runtime.HasNativeASTC()) {
235 return Settings::values.accelerate_astc.GetValue() && 235 return Settings::values.accelerate_astc.GetValue() == Settings::AstcDecodeMode::Gpu &&
236 Settings::values.astc_recompression.GetValue() == 236 Settings::values.astc_recompression.GetValue() ==
237 Settings::AstcRecompression::Uncompressed && 237 Settings::AstcRecompression::Uncompressed;
238 !Settings::values.async_astc.GetValue();
239 } 238 }
240 // Disable other accelerated uploads for now as they don't implement swizzled uploads 239 // Disable other accelerated uploads for now as they don't implement swizzled uploads
241 return false; 240 return false;
@@ -267,7 +266,8 @@ void ApplySwizzle(GLuint handle, PixelFormat format, std::array<SwizzleSource, 4
267[[nodiscard]] bool CanBeDecodedAsync(const TextureCacheRuntime& runtime, 266[[nodiscard]] bool CanBeDecodedAsync(const TextureCacheRuntime& runtime,
268 const VideoCommon::ImageInfo& info) { 267 const VideoCommon::ImageInfo& info) {
269 if (IsPixelFormatASTC(info.format) && !runtime.HasNativeASTC()) { 268 if (IsPixelFormatASTC(info.format) && !runtime.HasNativeASTC()) {
270 return Settings::values.async_astc.GetValue(); 269 return Settings::values.accelerate_astc.GetValue() ==
270 Settings::AstcDecodeMode::CpuAsynchronous;
271 } 271 }
272 return false; 272 return false;
273} 273}
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index 2a74c1d05..6b8d4e554 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -473,7 +473,7 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
473 glBindTextureUnit(0, screen_info.display_texture); 473 glBindTextureUnit(0, screen_info.display_texture);
474 474
475 auto anti_aliasing = Settings::values.anti_aliasing.GetValue(); 475 auto anti_aliasing = Settings::values.anti_aliasing.GetValue();
476 if (anti_aliasing > Settings::AntiAliasing::LastAA) { 476 if (anti_aliasing >= Settings::AntiAliasing::MaxEnum) {
477 LOG_ERROR(Render_OpenGL, "Invalid antialiasing option selected {}", anti_aliasing); 477 LOG_ERROR(Render_OpenGL, "Invalid antialiasing option selected {}", anti_aliasing);
478 anti_aliasing = Settings::AntiAliasing::None; 478 anti_aliasing = Settings::AntiAliasing::None;
479 Settings::values.anti_aliasing.SetValue(anti_aliasing); 479 Settings::values.anti_aliasing.SetValue(anti_aliasing);
diff --git a/src/video_core/renderer_vulkan/vk_swapchain.cpp b/src/video_core/renderer_vulkan/vk_swapchain.cpp
index d3cddac69..81ef98f61 100644
--- a/src/video_core/renderer_vulkan/vk_swapchain.cpp
+++ b/src/video_core/renderer_vulkan/vk_swapchain.cpp
@@ -45,8 +45,8 @@ static VkPresentModeKHR ChooseSwapPresentMode(bool has_imm, bool has_mailbox,
45 return mode; 45 return mode;
46 } 46 }
47 switch (mode) { 47 switch (mode) {
48 case Settings::VSyncMode::FIFO: 48 case Settings::VSyncMode::Fifo:
49 case Settings::VSyncMode::FIFORelaxed: 49 case Settings::VSyncMode::FifoRelaxed:
50 if (has_mailbox) { 50 if (has_mailbox) {
51 return Settings::VSyncMode::Mailbox; 51 return Settings::VSyncMode::Mailbox;
52 } else if (has_imm) { 52 } else if (has_imm) {
@@ -59,8 +59,8 @@ static VkPresentModeKHR ChooseSwapPresentMode(bool has_imm, bool has_mailbox,
59 }(); 59 }();
60 if ((setting == Settings::VSyncMode::Mailbox && !has_mailbox) || 60 if ((setting == Settings::VSyncMode::Mailbox && !has_mailbox) ||
61 (setting == Settings::VSyncMode::Immediate && !has_imm) || 61 (setting == Settings::VSyncMode::Immediate && !has_imm) ||
62 (setting == Settings::VSyncMode::FIFORelaxed && !has_fifo_relaxed)) { 62 (setting == Settings::VSyncMode::FifoRelaxed && !has_fifo_relaxed)) {
63 setting = Settings::VSyncMode::FIFO; 63 setting = Settings::VSyncMode::Fifo;
64 } 64 }
65 65
66 switch (setting) { 66 switch (setting) {
@@ -68,9 +68,9 @@ static VkPresentModeKHR ChooseSwapPresentMode(bool has_imm, bool has_mailbox,
68 return VK_PRESENT_MODE_IMMEDIATE_KHR; 68 return VK_PRESENT_MODE_IMMEDIATE_KHR;
69 case Settings::VSyncMode::Mailbox: 69 case Settings::VSyncMode::Mailbox:
70 return VK_PRESENT_MODE_MAILBOX_KHR; 70 return VK_PRESENT_MODE_MAILBOX_KHR;
71 case Settings::VSyncMode::FIFO: 71 case Settings::VSyncMode::Fifo:
72 return VK_PRESENT_MODE_FIFO_KHR; 72 return VK_PRESENT_MODE_FIFO_KHR;
73 case Settings::VSyncMode::FIFORelaxed: 73 case Settings::VSyncMode::FifoRelaxed:
74 return VK_PRESENT_MODE_FIFO_RELAXED_KHR; 74 return VK_PRESENT_MODE_FIFO_RELAXED_KHR;
75 default: 75 default:
76 return VK_PRESENT_MODE_FIFO_KHR; 76 return VK_PRESENT_MODE_FIFO_KHR;
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
index bf6ad6c79..53d749bd4 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
@@ -817,7 +817,7 @@ TextureCacheRuntime::TextureCacheRuntime(const Device& device_, Scheduler& sched
817 : device{device_}, scheduler{scheduler_}, memory_allocator{memory_allocator_}, 817 : device{device_}, scheduler{scheduler_}, memory_allocator{memory_allocator_},
818 staging_buffer_pool{staging_buffer_pool_}, blit_image_helper{blit_image_helper_}, 818 staging_buffer_pool{staging_buffer_pool_}, blit_image_helper{blit_image_helper_},
819 render_pass_cache{render_pass_cache_}, resolution{Settings::values.resolution_info} { 819 render_pass_cache{render_pass_cache_}, resolution{Settings::values.resolution_info} {
820 if (Settings::values.accelerate_astc) { 820 if (Settings::values.accelerate_astc.GetValue() == Settings::AstcDecodeMode::Gpu) {
821 astc_decoder_pass.emplace(device, scheduler, descriptor_pool, staging_buffer_pool, 821 astc_decoder_pass.emplace(device, scheduler, descriptor_pool, staging_buffer_pool,
822 compute_pass_descriptor_queue, memory_allocator); 822 compute_pass_descriptor_queue, memory_allocator);
823 } 823 }
@@ -1301,12 +1301,19 @@ Image::Image(TextureCacheRuntime& runtime_, const ImageInfo& info_, GPUVAddr gpu
1301 runtime->ViewFormats(info.format))), 1301 runtime->ViewFormats(info.format))),
1302 aspect_mask(ImageAspectMask(info.format)) { 1302 aspect_mask(ImageAspectMask(info.format)) {
1303 if (IsPixelFormatASTC(info.format) && !runtime->device.IsOptimalAstcSupported()) { 1303 if (IsPixelFormatASTC(info.format) && !runtime->device.IsOptimalAstcSupported()) {
1304 if (Settings::values.async_astc.GetValue()) { 1304 switch (Settings::values.accelerate_astc.GetValue()) {
1305 case Settings::AstcDecodeMode::Gpu:
1306 if (Settings::values.astc_recompression.GetValue() ==
1307 Settings::AstcRecompression::Uncompressed &&
1308 info.size.depth == 1) {
1309 flags |= VideoCommon::ImageFlagBits::AcceleratedUpload;
1310 }
1311 break;
1312 case Settings::AstcDecodeMode::CpuAsynchronous:
1305 flags |= VideoCommon::ImageFlagBits::AsynchronousDecode; 1313 flags |= VideoCommon::ImageFlagBits::AsynchronousDecode;
1306 } else if (Settings::values.astc_recompression.GetValue() == 1314 break;
1307 Settings::AstcRecompression::Uncompressed && 1315 default:
1308 Settings::values.accelerate_astc.GetValue() && info.size.depth == 1) { 1316 break;
1309 flags |= VideoCommon::ImageFlagBits::AcceleratedUpload;
1310 } 1317 }
1311 flags |= VideoCommon::ImageFlagBits::Converted; 1318 flags |= VideoCommon::ImageFlagBits::Converted;
1312 flags |= VideoCommon::ImageFlagBits::CostlyLoad; 1319 flags |= VideoCommon::ImageFlagBits::CostlyLoad;
diff --git a/src/video_core/textures/texture.cpp b/src/video_core/textures/texture.cpp
index d8b88d9bc..39c08b5ae 100644
--- a/src/video_core/textures/texture.cpp
+++ b/src/video_core/textures/texture.cpp
@@ -72,12 +72,12 @@ float TSCEntry::MaxAnisotropy() const noexcept {
72 } 72 }
73 const auto anisotropic_settings = Settings::values.max_anisotropy.GetValue(); 73 const auto anisotropic_settings = Settings::values.max_anisotropy.GetValue();
74 s32 added_anisotropic{}; 74 s32 added_anisotropic{};
75 if (anisotropic_settings == 0) { 75 if (anisotropic_settings == Settings::AnisotropyMode::Automatic) {
76 added_anisotropic = Settings::values.resolution_info.up_scale >> 76 added_anisotropic = Settings::values.resolution_info.up_scale >>
77 Settings::values.resolution_info.down_shift; 77 Settings::values.resolution_info.down_shift;
78 added_anisotropic = std::max(added_anisotropic - 1, 0); 78 added_anisotropic = std::max(added_anisotropic - 1, 0);
79 } else { 79 } else {
80 added_anisotropic = Settings::values.max_anisotropy.GetValue() - 1U; 80 added_anisotropic = static_cast<u32>(Settings::values.max_anisotropy.GetValue()) - 1U;
81 } 81 }
82 return static_cast<float>(1U << (max_anisotropy + added_anisotropic)); 82 return static_cast<float>(1U << (max_anisotropy + added_anisotropic));
83} 83}
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index fe98e3605..2e4da696c 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -143,6 +143,10 @@ add_executable(yuzu
143 configuration/configure_web.ui 143 configuration/configure_web.ui
144 configuration/input_profiles.cpp 144 configuration/input_profiles.cpp
145 configuration/input_profiles.h 145 configuration/input_profiles.h
146 configuration/shared_translation.cpp
147 configuration/shared_translation.h
148 configuration/shared_widget.cpp
149 configuration/shared_widget.h
146 debugger/console.cpp 150 debugger/console.cpp
147 debugger/console.h 151 debugger/console.h
148 debugger/controller.cpp 152 debugger/controller.cpp
@@ -231,6 +235,12 @@ if (WIN32 AND YUZU_CRASH_DUMPS)
231 target_compile_definitions(yuzu PRIVATE -DYUZU_DBGHELP) 235 target_compile_definitions(yuzu PRIVATE -DYUZU_DBGHELP)
232endif() 236endif()
233 237
238if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
239 target_compile_definitions(yuzu PRIVATE
240 $<$<VERSION_LESS:$<CXX_COMPILER_VERSION>,15>:CANNOT_EXPLICITLY_INSTANTIATE>
241 )
242endif()
243
234file(GLOB COMPAT_LIST 244file(GLOB COMPAT_LIST
235 ${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.qrc 245 ${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.qrc
236 ${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.json) 246 ${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.json)
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 195d3556c..b2405f9b8 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -1,12 +1,14 @@
1// SPDX-FileCopyrightText: 2014 Citra Emulator Project 1// SPDX-FileCopyrightText: 2014 Citra Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <algorithm>
4#include <array> 5#include <array>
5#include <QKeySequence> 6#include <QKeySequence>
6#include <QSettings> 7#include <QSettings>
7#include "common/fs/fs.h" 8#include "common/fs/fs.h"
8#include "common/fs/path_util.h" 9#include "common/fs/path_util.h"
9#include "common/settings.h" 10#include "common/settings.h"
11#include "common/settings_common.h"
10#include "core/core.h" 12#include "core/core.h"
11#include "core/hle/service/acc/profile_manager.h" 13#include "core/hle/service/acc/profile_manager.h"
12#include "core/hle/service/hid/controllers/npad.h" 14#include "core/hle/service/hid/controllers/npad.h"
@@ -16,9 +18,8 @@
16 18
17namespace FS = Common::FS; 19namespace FS = Common::FS;
18 20
19Config::Config(const std::string& config_name, ConfigType config_type) : type(config_type) { 21Config::Config(const std::string& config_name, ConfigType config_type)
20 global = config_type == ConfigType::GlobalConfig; 22 : type(config_type), global{config_type == ConfigType::GlobalConfig} {
21
22 Initialize(config_name); 23 Initialize(config_name);
23} 24}
24 25
@@ -89,10 +90,10 @@ const std::map<bool, QString> Config::use_docked_mode_texts_map = {
89 {false, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Handheld"))}, 90 {false, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Handheld"))},
90}; 91};
91 92
92const std::map<Settings::GPUAccuracy, QString> Config::gpu_accuracy_texts_map = { 93const std::map<Settings::GpuAccuracy, QString> Config::gpu_accuracy_texts_map = {
93 {Settings::GPUAccuracy::Normal, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Normal"))}, 94 {Settings::GpuAccuracy::Normal, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Normal"))},
94 {Settings::GPUAccuracy::High, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "High"))}, 95 {Settings::GpuAccuracy::High, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "High"))},
95 {Settings::GPUAccuracy::Extreme, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Extreme"))}, 96 {Settings::GpuAccuracy::Extreme, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Extreme"))},
96}; 97};
97 98
98const std::map<Settings::RendererBackend, QString> Config::renderer_backend_texts_map = { 99const std::map<Settings::RendererBackend, QString> Config::renderer_backend_texts_map = {
@@ -102,9 +103,9 @@ const std::map<Settings::RendererBackend, QString> Config::renderer_backend_text
102}; 103};
103 104
104const std::map<Settings::ShaderBackend, QString> Config::shader_backend_texts_map = { 105const std::map<Settings::ShaderBackend, QString> Config::shader_backend_texts_map = {
105 {Settings::ShaderBackend::GLSL, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "GLSL"))}, 106 {Settings::ShaderBackend::Glsl, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "GLSL"))},
106 {Settings::ShaderBackend::GLASM, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "GLASM"))}, 107 {Settings::ShaderBackend::Glasm, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "GLASM"))},
107 {Settings::ShaderBackend::SPIRV, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "SPIRV"))}, 108 {Settings::ShaderBackend::SpirV, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "SPIRV"))},
108}; 109};
109 110
110// This shouldn't have anything except static initializers (no functions). So 111// This shouldn't have anything except static initializers (no functions). So
@@ -171,66 +172,6 @@ bool Config::IsCustomConfig() {
171 return type == ConfigType::PerGameConfig; 172 return type == ConfigType::PerGameConfig;
172} 173}
173 174
174/* {Read,Write}BasicSetting and WriteGlobalSetting templates must be defined here before their
175 * usages later in this file. This allows explicit definition of some types that don't work
176 * nicely with the general version.
177 */
178
179// Explicit std::string definition: Qt can't implicitly convert a std::string to a QVariant, nor
180// can it implicitly convert a QVariant back to a {std::,Q}string
181template <>
182void Config::ReadBasicSetting(Settings::Setting<std::string>& setting) {
183 const QString name = QString::fromStdString(setting.GetLabel());
184 const auto default_value = QString::fromStdString(setting.GetDefault());
185 if (qt_config->value(name + QStringLiteral("/default"), false).toBool()) {
186 setting.SetValue(default_value.toStdString());
187 } else {
188 setting.SetValue(qt_config->value(name, default_value).toString().toStdString());
189 }
190}
191
192template <typename Type, bool ranged>
193void Config::ReadBasicSetting(Settings::Setting<Type, ranged>& setting) {
194 const QString name = QString::fromStdString(setting.GetLabel());
195 const Type default_value = setting.GetDefault();
196 if (qt_config->value(name + QStringLiteral("/default"), false).toBool()) {
197 setting.SetValue(default_value);
198 } else {
199 setting.SetValue(
200 static_cast<QVariant>(qt_config->value(name, default_value)).value<Type>());
201 }
202}
203
204// Explicit std::string definition: Qt can't implicitly convert a std::string to a QVariant
205template <>
206void Config::WriteBasicSetting(const Settings::Setting<std::string>& setting) {
207 const QString name = QString::fromStdString(setting.GetLabel());
208 const std::string& value = setting.GetValue();
209 qt_config->setValue(name + QStringLiteral("/default"), value == setting.GetDefault());
210 qt_config->setValue(name, QString::fromStdString(value));
211}
212
213template <typename Type, bool ranged>
214void Config::WriteBasicSetting(const Settings::Setting<Type, ranged>& setting) {
215 const QString name = QString::fromStdString(setting.GetLabel());
216 const Type value = setting.GetValue();
217 qt_config->setValue(name + QStringLiteral("/default"), value == setting.GetDefault());
218 qt_config->setValue(name, value);
219}
220
221template <typename Type, bool ranged>
222void Config::WriteGlobalSetting(const Settings::SwitchableSetting<Type, ranged>& setting) {
223 const QString name = QString::fromStdString(setting.GetLabel());
224 const Type& value = setting.GetValue(global);
225 if (!global) {
226 qt_config->setValue(name + QStringLiteral("/use_global"), setting.UsingGlobal());
227 }
228 if (global || !setting.UsingGlobal()) {
229 qt_config->setValue(name + QStringLiteral("/default"), value == setting.GetDefault());
230 qt_config->setValue(name, value);
231 }
232}
233
234void Config::ReadPlayerValue(std::size_t player_index) { 175void Config::ReadPlayerValue(std::size_t player_index) {
235 const QString player_prefix = [this, player_index] { 176 const QString player_prefix = [this, player_index] {
236 if (type == ConfigType::InputProfile) { 177 if (type == ConfigType::InputProfile) {
@@ -351,15 +292,9 @@ void Config::ReadPlayerValue(std::size_t player_index) {
351 player_motions = default_param; 292 player_motions = default_param;
352 } 293 }
353 } 294 }
354
355 if (player_index == 0) {
356 ReadMousePanningValues();
357 }
358} 295}
359 296
360void Config::ReadDebugValues() { 297void Config::ReadDebugValues() {
361 ReadBasicSetting(Settings::values.debug_pad_enabled);
362
363 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { 298 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
364 const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); 299 const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
365 auto& debug_pad_buttons = Settings::values.debug_pad_buttons[i]; 300 auto& debug_pad_buttons = Settings::values.debug_pad_buttons[i];
@@ -393,14 +328,6 @@ void Config::ReadDebugValues() {
393 } 328 }
394} 329}
395 330
396void Config::ReadKeyboardValues() {
397 ReadBasicSetting(Settings::values.keyboard_enabled);
398}
399
400void Config::ReadMouseValues() {
401 ReadBasicSetting(Settings::values.mouse_enabled);
402}
403
404void Config::ReadTouchscreenValues() { 331void Config::ReadTouchscreenValues() {
405 Settings::values.touchscreen.enabled = 332 Settings::values.touchscreen.enabled =
406 ReadSetting(QStringLiteral("touchscreen_enabled"), true).toBool(); 333 ReadSetting(QStringLiteral("touchscreen_enabled"), true).toBool();
@@ -414,9 +341,6 @@ void Config::ReadTouchscreenValues() {
414} 341}
415 342
416void Config::ReadHidbusValues() { 343void Config::ReadHidbusValues() {
417 Settings::values.enable_ring_controller =
418 ReadSetting(QStringLiteral("enable_ring_controller"), true).toBool();
419
420 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( 344 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
421 0, 0, default_ringcon_analogs[0], default_ringcon_analogs[1], 0, 0.05f); 345 0, 0, default_ringcon_analogs[0], default_ringcon_analogs[1], 0, 0.05f);
422 auto& ringcon_analogs = Settings::values.ringcon_analogs; 346 auto& ringcon_analogs = Settings::values.ringcon_analogs;
@@ -430,20 +354,10 @@ void Config::ReadHidbusValues() {
430 } 354 }
431} 355}
432 356
433void Config::ReadIrCameraValues() {
434 ReadBasicSetting(Settings::values.enable_ir_sensor);
435 ReadBasicSetting(Settings::values.ir_sensor_device);
436}
437
438void Config::ReadAudioValues() { 357void Config::ReadAudioValues() {
439 qt_config->beginGroup(QStringLiteral("Audio")); 358 qt_config->beginGroup(QStringLiteral("Audio"));
440 359
441 if (global) { 360 ReadCategory(Settings::Category::Audio);
442 ReadBasicSetting(Settings::values.sink_id);
443 ReadBasicSetting(Settings::values.audio_output_device_id);
444 ReadBasicSetting(Settings::values.audio_input_device_id);
445 }
446 ReadGlobalSetting(Settings::values.volume);
447 361
448 qt_config->endGroup(); 362 qt_config->endGroup();
449} 363}
@@ -451,11 +365,12 @@ void Config::ReadAudioValues() {
451void Config::ReadControlValues() { 365void Config::ReadControlValues() {
452 qt_config->beginGroup(QStringLiteral("Controls")); 366 qt_config->beginGroup(QStringLiteral("Controls"));
453 367
368 ReadCategory(Settings::Category::Controls);
369
454 Settings::values.players.SetGlobal(!IsCustomConfig()); 370 Settings::values.players.SetGlobal(!IsCustomConfig());
455 for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) { 371 for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) {
456 ReadPlayerValue(p); 372 ReadPlayerValue(p);
457 } 373 }
458 ReadGlobalSetting(Settings::values.use_docked_mode);
459 374
460 // Disable docked mode if handheld is selected 375 // Disable docked mode if handheld is selected
461 const auto controller_type = Settings::values.players.GetValue()[0].controller_type; 376 const auto controller_type = Settings::values.players.GetValue()[0].controller_type;
@@ -464,50 +379,18 @@ void Config::ReadControlValues() {
464 Settings::values.use_docked_mode.SetValue(false); 379 Settings::values.use_docked_mode.SetValue(false);
465 } 380 }
466 381
467 ReadGlobalSetting(Settings::values.vibration_enabled);
468 ReadGlobalSetting(Settings::values.enable_accurate_vibrations);
469 ReadGlobalSetting(Settings::values.motion_enabled);
470 if (IsCustomConfig()) { 382 if (IsCustomConfig()) {
471 qt_config->endGroup(); 383 qt_config->endGroup();
472 return; 384 return;
473 } 385 }
474 ReadDebugValues(); 386 ReadDebugValues();
475 ReadKeyboardValues();
476 ReadMouseValues();
477 ReadTouchscreenValues(); 387 ReadTouchscreenValues();
478 ReadMousePanningValues();
479 ReadMotionTouchValues(); 388 ReadMotionTouchValues();
480 ReadHidbusValues(); 389 ReadHidbusValues();
481 ReadIrCameraValues();
482
483#ifdef _WIN32
484 ReadBasicSetting(Settings::values.enable_raw_input);
485#else
486 Settings::values.enable_raw_input = false;
487#endif
488 ReadBasicSetting(Settings::values.emulate_analog_keyboard);
489 ReadBasicSetting(Settings::values.enable_joycon_driver);
490 ReadBasicSetting(Settings::values.enable_procon_driver);
491 ReadBasicSetting(Settings::values.random_amiibo_id);
492
493 ReadBasicSetting(Settings::values.tas_enable);
494 ReadBasicSetting(Settings::values.tas_loop);
495 ReadBasicSetting(Settings::values.pause_tas_on_load);
496
497 ReadBasicSetting(Settings::values.controller_navigation);
498 390
499 qt_config->endGroup(); 391 qt_config->endGroup();
500} 392}
501 393
502void Config::ReadMousePanningValues() {
503 ReadBasicSetting(Settings::values.mouse_panning);
504 ReadBasicSetting(Settings::values.mouse_panning_x_sensitivity);
505 ReadBasicSetting(Settings::values.mouse_panning_y_sensitivity);
506 ReadBasicSetting(Settings::values.mouse_panning_deadzone_counterweight);
507 ReadBasicSetting(Settings::values.mouse_panning_decay_strength);
508 ReadBasicSetting(Settings::values.mouse_panning_min_decay);
509}
510
511void Config::ReadMotionTouchValues() { 394void Config::ReadMotionTouchValues() {
512 int num_touch_from_button_maps = 395 int num_touch_from_button_maps =
513 qt_config->beginReadArray(QStringLiteral("touch_from_button_maps")); 396 qt_config->beginReadArray(QStringLiteral("touch_from_button_maps"));
@@ -541,19 +424,14 @@ void Config::ReadMotionTouchValues() {
541 } 424 }
542 qt_config->endArray(); 425 qt_config->endArray();
543 426
544 ReadBasicSetting(Settings::values.touch_device);
545 ReadBasicSetting(Settings::values.touch_from_button_map_index);
546 Settings::values.touch_from_button_map_index = std::clamp( 427 Settings::values.touch_from_button_map_index = std::clamp(
547 Settings::values.touch_from_button_map_index.GetValue(), 0, num_touch_from_button_maps - 1); 428 Settings::values.touch_from_button_map_index.GetValue(), 0, num_touch_from_button_maps - 1);
548 ReadBasicSetting(Settings::values.udp_input_servers);
549 ReadBasicSetting(Settings::values.enable_udp_controller);
550} 429}
551 430
552void Config::ReadCoreValues() { 431void Config::ReadCoreValues() {
553 qt_config->beginGroup(QStringLiteral("Core")); 432 qt_config->beginGroup(QStringLiteral("Core"));
554 433
555 ReadGlobalSetting(Settings::values.use_multi_core); 434 ReadCategory(Settings::Category::Core);
556 ReadGlobalSetting(Settings::values.use_unsafe_extended_memory_layout);
557 435
558 qt_config->endGroup(); 436 qt_config->endGroup();
559} 437}
@@ -561,7 +439,6 @@ void Config::ReadCoreValues() {
561void Config::ReadDataStorageValues() { 439void Config::ReadDataStorageValues() {
562 qt_config->beginGroup(QStringLiteral("Data Storage")); 440 qt_config->beginGroup(QStringLiteral("Data Storage"));
563 441
564 ReadBasicSetting(Settings::values.use_virtual_sd);
565 FS::SetYuzuPath( 442 FS::SetYuzuPath(
566 FS::YuzuPath::NANDDir, 443 FS::YuzuPath::NANDDir,
567 qt_config 444 qt_config
@@ -597,9 +474,7 @@ void Config::ReadDataStorageValues() {
597 .toString() 474 .toString()
598 .toStdString()); 475 .toStdString());
599 476
600 ReadBasicSetting(Settings::values.gamecard_inserted); 477 ReadCategory(Settings::Category::DataStorage);
601 ReadBasicSetting(Settings::values.gamecard_current_game);
602 ReadBasicSetting(Settings::values.gamecard_path);
603 478
604 qt_config->endGroup(); 479 qt_config->endGroup();
605} 480}
@@ -611,29 +486,17 @@ void Config::ReadDebuggingValues() {
611 Settings::values.record_frame_times = 486 Settings::values.record_frame_times =
612 qt_config->value(QStringLiteral("record_frame_times"), false).toBool(); 487 qt_config->value(QStringLiteral("record_frame_times"), false).toBool();
613 488
614 ReadBasicSetting(Settings::values.use_gdbstub); 489 ReadCategory(Settings::Category::Debugging);
615 ReadBasicSetting(Settings::values.gdbstub_port); 490 ReadCategory(Settings::Category::DebuggingGraphics);
616 ReadBasicSetting(Settings::values.program_args);
617 ReadBasicSetting(Settings::values.dump_exefs);
618 ReadBasicSetting(Settings::values.dump_nso);
619 ReadBasicSetting(Settings::values.enable_fs_access_log);
620 ReadBasicSetting(Settings::values.reporting_services);
621 ReadBasicSetting(Settings::values.quest_flag);
622 ReadBasicSetting(Settings::values.disable_macro_jit);
623 ReadBasicSetting(Settings::values.disable_macro_hle);
624 ReadBasicSetting(Settings::values.extended_logging);
625 ReadBasicSetting(Settings::values.use_debug_asserts);
626 ReadBasicSetting(Settings::values.use_auto_stub);
627 ReadBasicSetting(Settings::values.enable_all_controllers);
628 ReadBasicSetting(Settings::values.create_crash_dumps);
629 ReadBasicSetting(Settings::values.perform_vulkan_check);
630 491
631 qt_config->endGroup(); 492 qt_config->endGroup();
632} 493}
633 494
634void Config::ReadServiceValues() { 495void Config::ReadServiceValues() {
635 qt_config->beginGroup(QStringLiteral("Services")); 496 qt_config->beginGroup(QStringLiteral("Services"));
636 ReadBasicSetting(Settings::values.network_interface); 497
498 ReadCategory(Settings::Category::Services);
499
637 qt_config->endGroup(); 500 qt_config->endGroup();
638} 501}
639 502
@@ -659,8 +522,7 @@ void Config::ReadDisabledAddOnValues() {
659void Config::ReadMiscellaneousValues() { 522void Config::ReadMiscellaneousValues() {
660 qt_config->beginGroup(QStringLiteral("Miscellaneous")); 523 qt_config->beginGroup(QStringLiteral("Miscellaneous"));
661 524
662 ReadBasicSetting(Settings::values.log_filter); 525 ReadCategory(Settings::Category::Miscellaneous);
663 ReadBasicSetting(Settings::values.use_dev_keys);
664 526
665 qt_config->endGroup(); 527 qt_config->endGroup();
666} 528}
@@ -710,36 +572,9 @@ void Config::ReadPathValues() {
710void Config::ReadCpuValues() { 572void Config::ReadCpuValues() {
711 qt_config->beginGroup(QStringLiteral("Cpu")); 573 qt_config->beginGroup(QStringLiteral("Cpu"));
712 574
713 ReadBasicSetting(Settings::values.cpu_accuracy_first_time); 575 ReadCategory(Settings::Category::Cpu);
714 if (Settings::values.cpu_accuracy_first_time) { 576 ReadCategory(Settings::Category::CpuDebug);
715 Settings::values.cpu_accuracy.SetValue(Settings::values.cpu_accuracy.GetDefault()); 577 ReadCategory(Settings::Category::CpuUnsafe);
716 Settings::values.cpu_accuracy_first_time.SetValue(false);
717 } else {
718 ReadGlobalSetting(Settings::values.cpu_accuracy);
719 }
720
721 ReadGlobalSetting(Settings::values.cpuopt_unsafe_unfuse_fma);
722 ReadGlobalSetting(Settings::values.cpuopt_unsafe_reduce_fp_error);
723 ReadGlobalSetting(Settings::values.cpuopt_unsafe_ignore_standard_fpcr);
724 ReadGlobalSetting(Settings::values.cpuopt_unsafe_inaccurate_nan);
725 ReadGlobalSetting(Settings::values.cpuopt_unsafe_fastmem_check);
726 ReadGlobalSetting(Settings::values.cpuopt_unsafe_ignore_global_monitor);
727
728 if (global) {
729 ReadBasicSetting(Settings::values.cpu_debug_mode);
730 ReadBasicSetting(Settings::values.cpuopt_page_tables);
731 ReadBasicSetting(Settings::values.cpuopt_block_linking);
732 ReadBasicSetting(Settings::values.cpuopt_return_stack_buffer);
733 ReadBasicSetting(Settings::values.cpuopt_fast_dispatcher);
734 ReadBasicSetting(Settings::values.cpuopt_context_elimination);
735 ReadBasicSetting(Settings::values.cpuopt_const_prop);
736 ReadBasicSetting(Settings::values.cpuopt_misc_ir);
737 ReadBasicSetting(Settings::values.cpuopt_reduce_misalign_checks);
738 ReadBasicSetting(Settings::values.cpuopt_fastmem);
739 ReadBasicSetting(Settings::values.cpuopt_fastmem_exclusives);
740 ReadBasicSetting(Settings::values.cpuopt_recompile_exclusives);
741 ReadBasicSetting(Settings::values.cpuopt_ignore_memory_aborts);
742 }
743 578
744 qt_config->endGroup(); 579 qt_config->endGroup();
745} 580}
@@ -747,47 +582,9 @@ void Config::ReadCpuValues() {
747void Config::ReadRendererValues() { 582void Config::ReadRendererValues() {
748 qt_config->beginGroup(QStringLiteral("Renderer")); 583 qt_config->beginGroup(QStringLiteral("Renderer"));
749 584
750 ReadGlobalSetting(Settings::values.renderer_backend); 585 ReadCategory(Settings::Category::Renderer);
751 ReadGlobalSetting(Settings::values.async_presentation); 586 ReadCategory(Settings::Category::RendererAdvanced);
752 ReadGlobalSetting(Settings::values.renderer_force_max_clock); 587 ReadCategory(Settings::Category::RendererDebug);
753 ReadGlobalSetting(Settings::values.vulkan_device);
754 ReadGlobalSetting(Settings::values.fullscreen_mode);
755 ReadGlobalSetting(Settings::values.aspect_ratio);
756 ReadGlobalSetting(Settings::values.resolution_setup);
757 ReadGlobalSetting(Settings::values.scaling_filter);
758 ReadGlobalSetting(Settings::values.fsr_sharpening_slider);
759 ReadGlobalSetting(Settings::values.anti_aliasing);
760 ReadGlobalSetting(Settings::values.max_anisotropy);
761 ReadGlobalSetting(Settings::values.speed_limit);
762 ReadGlobalSetting(Settings::values.use_disk_shader_cache);
763 ReadGlobalSetting(Settings::values.gpu_accuracy);
764 ReadGlobalSetting(Settings::values.use_asynchronous_gpu_emulation);
765 ReadGlobalSetting(Settings::values.nvdec_emulation);
766 ReadGlobalSetting(Settings::values.accelerate_astc);
767 ReadGlobalSetting(Settings::values.async_astc);
768 ReadGlobalSetting(Settings::values.astc_recompression);
769 ReadGlobalSetting(Settings::values.use_reactive_flushing);
770 ReadGlobalSetting(Settings::values.shader_backend);
771 ReadGlobalSetting(Settings::values.use_asynchronous_shaders);
772 ReadGlobalSetting(Settings::values.use_fast_gpu_time);
773 ReadGlobalSetting(Settings::values.use_vulkan_driver_pipeline_cache);
774 ReadGlobalSetting(Settings::values.enable_compute_pipelines);
775 ReadGlobalSetting(Settings::values.use_video_framerate);
776 ReadGlobalSetting(Settings::values.barrier_feedback_loops);
777 ReadGlobalSetting(Settings::values.bg_red);
778 ReadGlobalSetting(Settings::values.bg_green);
779 ReadGlobalSetting(Settings::values.bg_blue);
780
781 if (global) {
782 Settings::values.vsync_mode.SetValue(static_cast<Settings::VSyncMode>(
783 ReadSetting(QString::fromStdString(Settings::values.vsync_mode.GetLabel()),
784 static_cast<u32>(Settings::values.vsync_mode.GetDefault()))
785 .value<u32>()));
786 ReadBasicSetting(Settings::values.renderer_debug);
787 ReadBasicSetting(Settings::values.renderer_shader_feedback);
788 ReadBasicSetting(Settings::values.enable_nsight_aftermath);
789 ReadBasicSetting(Settings::values.disable_shader_loop_safety_checks);
790 }
791 588
792 qt_config->endGroup(); 589 qt_config->endGroup();
793} 590}
@@ -834,41 +631,8 @@ void Config::ReadShortcutValues() {
834void Config::ReadSystemValues() { 631void Config::ReadSystemValues() {
835 qt_config->beginGroup(QStringLiteral("System")); 632 qt_config->beginGroup(QStringLiteral("System"));
836 633
837 ReadGlobalSetting(Settings::values.language_index); 634 ReadCategory(Settings::Category::System);
838 635 ReadCategory(Settings::Category::SystemAudio);
839 ReadGlobalSetting(Settings::values.region_index);
840
841 ReadGlobalSetting(Settings::values.time_zone_index);
842
843 bool rng_seed_enabled;
844 ReadSettingGlobal(rng_seed_enabled, QStringLiteral("rng_seed_enabled"), false);
845 bool rng_seed_global =
846 global || qt_config->value(QStringLiteral("rng_seed/use_global"), true).toBool();
847 Settings::values.rng_seed.SetGlobal(rng_seed_global);
848 if (global || !rng_seed_global) {
849 if (rng_seed_enabled) {
850 Settings::values.rng_seed.SetValue(ReadSetting(QStringLiteral("rng_seed"), 0).toUInt());
851 } else {
852 Settings::values.rng_seed.SetValue(std::nullopt);
853 }
854 }
855
856 if (global) {
857 ReadBasicSetting(Settings::values.current_user);
858 Settings::values.current_user = std::clamp<int>(Settings::values.current_user.GetValue(), 0,
859 Service::Account::MAX_USERS - 1);
860
861 const auto custom_rtc_enabled =
862 ReadSetting(QStringLiteral("custom_rtc_enabled"), false).toBool();
863 if (custom_rtc_enabled) {
864 Settings::values.custom_rtc = ReadSetting(QStringLiteral("custom_rtc"), 0).toLongLong();
865 } else {
866 Settings::values.custom_rtc = std::nullopt;
867 }
868 ReadBasicSetting(Settings::values.device_name);
869 }
870
871 ReadGlobalSetting(Settings::values.sound_index);
872 636
873 qt_config->endGroup(); 637 qt_config->endGroup();
874} 638}
@@ -881,8 +645,6 @@ void Config::ReadUIValues() {
881 QStringLiteral("theme"), 645 QStringLiteral("theme"),
882 QString::fromUtf8(UISettings::themes[static_cast<size_t>(default_theme)].second)) 646 QString::fromUtf8(UISettings::themes[static_cast<size_t>(default_theme)].second))
883 .toString(); 647 .toString();
884 ReadBasicSetting(UISettings::values.enable_discord_presence);
885 ReadBasicSetting(UISettings::values.select_user_on_boot);
886 648
887 ReadUIGamelistValues(); 649 ReadUIGamelistValues();
888 ReadUILayoutValues(); 650 ReadUILayoutValues();
@@ -891,20 +653,8 @@ void Config::ReadUIValues() {
891 ReadShortcutValues(); 653 ReadShortcutValues();
892 ReadMultiplayerValues(); 654 ReadMultiplayerValues();
893 655
894 ReadBasicSetting(UISettings::values.single_window_mode); 656 ReadCategory(Settings::Category::Ui);
895 ReadBasicSetting(UISettings::values.fullscreen); 657 ReadCategory(Settings::Category::UiGeneral);
896 ReadBasicSetting(UISettings::values.display_titlebar);
897 ReadBasicSetting(UISettings::values.show_filter_bar);
898 ReadBasicSetting(UISettings::values.show_status_bar);
899 ReadBasicSetting(UISettings::values.confirm_before_closing);
900 ReadBasicSetting(UISettings::values.first_start);
901 ReadBasicSetting(UISettings::values.callout_flags);
902 ReadBasicSetting(UISettings::values.show_console);
903 ReadBasicSetting(UISettings::values.pause_when_in_background);
904 ReadBasicSetting(UISettings::values.mute_when_in_background);
905 ReadBasicSetting(UISettings::values.hide_mouse);
906 ReadBasicSetting(UISettings::values.controller_applet_disabled);
907 ReadBasicSetting(UISettings::values.disable_web_applet);
908 658
909 qt_config->endGroup(); 659 qt_config->endGroup();
910} 660}
@@ -912,16 +662,8 @@ void Config::ReadUIValues() {
912void Config::ReadUIGamelistValues() { 662void Config::ReadUIGamelistValues() {
913 qt_config->beginGroup(QStringLiteral("UIGameList")); 663 qt_config->beginGroup(QStringLiteral("UIGameList"));
914 664
915 ReadBasicSetting(UISettings::values.show_add_ons); 665 ReadCategory(Settings::Category::UiGameList);
916 ReadBasicSetting(UISettings::values.show_compat); 666
917 ReadBasicSetting(UISettings::values.show_size);
918 ReadBasicSetting(UISettings::values.show_types);
919 ReadBasicSetting(UISettings::values.game_icon_size);
920 ReadBasicSetting(UISettings::values.folder_icon_size);
921 ReadBasicSetting(UISettings::values.row_1_text_id);
922 ReadBasicSetting(UISettings::values.row_2_text_id);
923 ReadBasicSetting(UISettings::values.cache_game_list);
924 ReadBasicSetting(UISettings::values.favorites_expanded);
925 const int favorites_size = qt_config->beginReadArray(QStringLiteral("favorites")); 667 const int favorites_size = qt_config->beginReadArray(QStringLiteral("favorites"));
926 for (int i = 0; i < favorites_size; i++) { 668 for (int i = 0; i < favorites_size; i++) {
927 qt_config->setArrayIndex(i); 669 qt_config->setArrayIndex(i);
@@ -944,7 +686,8 @@ void Config::ReadUILayoutValues() {
944 ReadSetting(QStringLiteral("gameListHeaderState")).toByteArray(); 686 ReadSetting(QStringLiteral("gameListHeaderState")).toByteArray();
945 UISettings::values.microprofile_geometry = 687 UISettings::values.microprofile_geometry =
946 ReadSetting(QStringLiteral("microProfileDialogGeometry")).toByteArray(); 688 ReadSetting(QStringLiteral("microProfileDialogGeometry")).toByteArray();
947 ReadBasicSetting(UISettings::values.microprofile_visible); 689
690 ReadCategory(Settings::Category::UiLayout);
948 691
949 qt_config->endGroup(); 692 qt_config->endGroup();
950} 693}
@@ -952,10 +695,7 @@ void Config::ReadUILayoutValues() {
952void Config::ReadWebServiceValues() { 695void Config::ReadWebServiceValues() {
953 qt_config->beginGroup(QStringLiteral("WebService")); 696 qt_config->beginGroup(QStringLiteral("WebService"));
954 697
955 ReadBasicSetting(Settings::values.enable_telemetry); 698 ReadCategory(Settings::Category::WebService);
956 ReadBasicSetting(Settings::values.web_api_url);
957 ReadBasicSetting(Settings::values.yuzu_username);
958 ReadBasicSetting(Settings::values.yuzu_token);
959 699
960 qt_config->endGroup(); 700 qt_config->endGroup();
961} 701}
@@ -963,17 +703,7 @@ void Config::ReadWebServiceValues() {
963void Config::ReadMultiplayerValues() { 703void Config::ReadMultiplayerValues() {
964 qt_config->beginGroup(QStringLiteral("Multiplayer")); 704 qt_config->beginGroup(QStringLiteral("Multiplayer"));
965 705
966 ReadBasicSetting(UISettings::values.multiplayer_nickname); 706 ReadCategory(Settings::Category::Multiplayer);
967 ReadBasicSetting(UISettings::values.multiplayer_ip);
968 ReadBasicSetting(UISettings::values.multiplayer_port);
969 ReadBasicSetting(UISettings::values.multiplayer_room_nickname);
970 ReadBasicSetting(UISettings::values.multiplayer_room_name);
971 ReadBasicSetting(UISettings::values.multiplayer_room_port);
972 ReadBasicSetting(UISettings::values.multiplayer_host_type);
973 ReadBasicSetting(UISettings::values.multiplayer_port);
974 ReadBasicSetting(UISettings::values.multiplayer_max_player);
975 ReadBasicSetting(UISettings::values.multiplayer_game_id);
976 ReadBasicSetting(UISettings::values.multiplayer_room_description);
977 707
978 // Read ban list back 708 // Read ban list back
979 int size = qt_config->beginReadArray(QStringLiteral("username_ban_list")); 709 int size = qt_config->beginReadArray(QStringLiteral("username_ban_list"));
@@ -996,11 +726,20 @@ void Config::ReadMultiplayerValues() {
996 qt_config->endGroup(); 726 qt_config->endGroup();
997} 727}
998 728
729void Config::ReadNetworkValues() {
730 qt_config->beginGroup(QString::fromStdString("Services"));
731
732 ReadCategory(Settings::Category::Network);
733
734 qt_config->endGroup();
735}
736
999void Config::ReadValues() { 737void Config::ReadValues() {
1000 if (global) { 738 if (global) {
1001 ReadDataStorageValues(); 739 ReadDataStorageValues();
1002 ReadDebuggingValues(); 740 ReadDebuggingValues();
1003 ReadDisabledAddOnValues(); 741 ReadDisabledAddOnValues();
742 ReadNetworkValues();
1004 ReadServiceValues(); 743 ReadServiceValues();
1005 ReadUIValues(); 744 ReadUIValues();
1006 ReadWebServiceValues(); 745 ReadWebServiceValues();
@@ -1077,14 +816,9 @@ void Config::SavePlayerValue(std::size_t player_index) {
1077 QString::fromStdString(player.motions[i]), 816 QString::fromStdString(player.motions[i]),
1078 QString::fromStdString(default_param)); 817 QString::fromStdString(default_param));
1079 } 818 }
1080
1081 if (player_index == 0) {
1082 SaveMousePanningValues();
1083 }
1084} 819}
1085 820
1086void Config::SaveDebugValues() { 821void Config::SaveDebugValues() {
1087 WriteBasicSetting(Settings::values.debug_pad_enabled);
1088 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { 822 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
1089 const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); 823 const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
1090 WriteSetting(QStringLiteral("debug_pad_") + 824 WriteSetting(QStringLiteral("debug_pad_") +
@@ -1103,10 +837,6 @@ void Config::SaveDebugValues() {
1103 } 837 }
1104} 838}
1105 839
1106void Config::SaveMouseValues() {
1107 WriteBasicSetting(Settings::values.mouse_enabled);
1108}
1109
1110void Config::SaveTouchscreenValues() { 840void Config::SaveTouchscreenValues() {
1111 const auto& touchscreen = Settings::values.touchscreen; 841 const auto& touchscreen = Settings::values.touchscreen;
1112 842
@@ -1117,21 +847,7 @@ void Config::SaveTouchscreenValues() {
1117 WriteSetting(QStringLiteral("touchscreen_diameter_y"), touchscreen.diameter_y, 15); 847 WriteSetting(QStringLiteral("touchscreen_diameter_y"), touchscreen.diameter_y, 15);
1118} 848}
1119 849
1120void Config::SaveMousePanningValues() {
1121 // Don't overwrite values.mouse_panning
1122 WriteBasicSetting(Settings::values.mouse_panning_x_sensitivity);
1123 WriteBasicSetting(Settings::values.mouse_panning_y_sensitivity);
1124 WriteBasicSetting(Settings::values.mouse_panning_deadzone_counterweight);
1125 WriteBasicSetting(Settings::values.mouse_panning_decay_strength);
1126 WriteBasicSetting(Settings::values.mouse_panning_min_decay);
1127}
1128
1129void Config::SaveMotionTouchValues() { 850void Config::SaveMotionTouchValues() {
1130 WriteBasicSetting(Settings::values.touch_device);
1131 WriteBasicSetting(Settings::values.touch_from_button_map_index);
1132 WriteBasicSetting(Settings::values.udp_input_servers);
1133 WriteBasicSetting(Settings::values.enable_udp_controller);
1134
1135 qt_config->beginWriteArray(QStringLiteral("touch_from_button_maps")); 851 qt_config->beginWriteArray(QStringLiteral("touch_from_button_maps"));
1136 for (std::size_t p = 0; p < Settings::values.touch_from_button_maps.size(); ++p) { 852 for (std::size_t p = 0; p < Settings::values.touch_from_button_maps.size(); ++p) {
1137 qt_config->setArrayIndex(static_cast<int>(p)); 853 qt_config->setArrayIndex(static_cast<int>(p));
@@ -1152,8 +868,6 @@ void Config::SaveMotionTouchValues() {
1152} 868}
1153 869
1154void Config::SaveHidbusValues() { 870void Config::SaveHidbusValues() {
1155 WriteBasicSetting(Settings::values.enable_ring_controller);
1156
1157 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( 871 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
1158 0, 0, default_ringcon_analogs[0], default_ringcon_analogs[1], 0, 0.05f); 872 0, 0, default_ringcon_analogs[0], default_ringcon_analogs[1], 0, 0.05f);
1159 WriteSetting(QStringLiteral("ring_controller"), 873 WriteSetting(QStringLiteral("ring_controller"),
@@ -1161,11 +875,6 @@ void Config::SaveHidbusValues() {
1161 QString::fromStdString(default_param)); 875 QString::fromStdString(default_param));
1162} 876}
1163 877
1164void Config::SaveIrCameraValues() {
1165 WriteBasicSetting(Settings::values.enable_ir_sensor);
1166 WriteBasicSetting(Settings::values.ir_sensor_device);
1167}
1168
1169void Config::SaveValues() { 878void Config::SaveValues() {
1170 if (global) { 879 if (global) {
1171 SaveDataStorageValues(); 880 SaveDataStorageValues();
@@ -1182,18 +891,14 @@ void Config::SaveValues() {
1182 SaveRendererValues(); 891 SaveRendererValues();
1183 SaveAudioValues(); 892 SaveAudioValues();
1184 SaveSystemValues(); 893 SaveSystemValues();
894
1185 qt_config->sync(); 895 qt_config->sync();
1186} 896}
1187 897
1188void Config::SaveAudioValues() { 898void Config::SaveAudioValues() {
1189 qt_config->beginGroup(QStringLiteral("Audio")); 899 qt_config->beginGroup(QStringLiteral("Audio"));
1190 900
1191 if (global) { 901 WriteCategory(Settings::Category::Audio);
1192 WriteBasicSetting(Settings::values.sink_id);
1193 WriteBasicSetting(Settings::values.audio_output_device_id);
1194 WriteBasicSetting(Settings::values.audio_input_device_id);
1195 }
1196 WriteGlobalSetting(Settings::values.volume);
1197 902
1198 qt_config->endGroup(); 903 qt_config->endGroup();
1199} 904}
@@ -1201,6 +906,8 @@ void Config::SaveAudioValues() {
1201void Config::SaveControlValues() { 906void Config::SaveControlValues() {
1202 qt_config->beginGroup(QStringLiteral("Controls")); 907 qt_config->beginGroup(QStringLiteral("Controls"));
1203 908
909 WriteCategory(Settings::Category::Controls);
910
1204 Settings::values.players.SetGlobal(!IsCustomConfig()); 911 Settings::values.players.SetGlobal(!IsCustomConfig());
1205 for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) { 912 for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) {
1206 SavePlayerValue(p); 913 SavePlayerValue(p);
@@ -1210,28 +917,9 @@ void Config::SaveControlValues() {
1210 return; 917 return;
1211 } 918 }
1212 SaveDebugValues(); 919 SaveDebugValues();
1213 SaveMouseValues();
1214 SaveTouchscreenValues(); 920 SaveTouchscreenValues();
1215 SaveMousePanningValues();
1216 SaveMotionTouchValues(); 921 SaveMotionTouchValues();
1217 SaveHidbusValues(); 922 SaveHidbusValues();
1218 SaveIrCameraValues();
1219
1220 WriteGlobalSetting(Settings::values.use_docked_mode);
1221 WriteGlobalSetting(Settings::values.vibration_enabled);
1222 WriteGlobalSetting(Settings::values.enable_accurate_vibrations);
1223 WriteGlobalSetting(Settings::values.motion_enabled);
1224 WriteBasicSetting(Settings::values.enable_raw_input);
1225 WriteBasicSetting(Settings::values.enable_joycon_driver);
1226 WriteBasicSetting(Settings::values.enable_procon_driver);
1227 WriteBasicSetting(Settings::values.random_amiibo_id);
1228 WriteBasicSetting(Settings::values.keyboard_enabled);
1229 WriteBasicSetting(Settings::values.emulate_analog_keyboard);
1230 WriteBasicSetting(Settings::values.controller_navigation);
1231
1232 WriteBasicSetting(Settings::values.tas_enable);
1233 WriteBasicSetting(Settings::values.tas_loop);
1234 WriteBasicSetting(Settings::values.pause_tas_on_load);
1235 923
1236 qt_config->endGroup(); 924 qt_config->endGroup();
1237} 925}
@@ -1239,8 +927,7 @@ void Config::SaveControlValues() {
1239void Config::SaveCoreValues() { 927void Config::SaveCoreValues() {
1240 qt_config->beginGroup(QStringLiteral("Core")); 928 qt_config->beginGroup(QStringLiteral("Core"));
1241 929
1242 WriteGlobalSetting(Settings::values.use_multi_core); 930 WriteCategory(Settings::Category::Core);
1243 WriteGlobalSetting(Settings::values.use_unsafe_extended_memory_layout);
1244 931
1245 qt_config->endGroup(); 932 qt_config->endGroup();
1246} 933}
@@ -1248,7 +935,6 @@ void Config::SaveCoreValues() {
1248void Config::SaveDataStorageValues() { 935void Config::SaveDataStorageValues() {
1249 qt_config->beginGroup(QStringLiteral("Data Storage")); 936 qt_config->beginGroup(QStringLiteral("Data Storage"));
1250 937
1251 WriteBasicSetting(Settings::values.use_virtual_sd);
1252 WriteSetting(QStringLiteral("nand_directory"), 938 WriteSetting(QStringLiteral("nand_directory"),
1253 QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::NANDDir)), 939 QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::NANDDir)),
1254 QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::NANDDir))); 940 QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::NANDDir)));
@@ -1265,9 +951,7 @@ void Config::SaveDataStorageValues() {
1265 QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::TASDir)), 951 QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::TASDir)),
1266 QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::TASDir))); 952 QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::TASDir)));
1267 953
1268 WriteBasicSetting(Settings::values.gamecard_inserted); 954 WriteCategory(Settings::Category::DataStorage);
1269 WriteBasicSetting(Settings::values.gamecard_current_game);
1270 WriteBasicSetting(Settings::values.gamecard_path);
1271 955
1272 qt_config->endGroup(); 956 qt_config->endGroup();
1273} 957}
@@ -1277,19 +961,9 @@ void Config::SaveDebuggingValues() {
1277 961
1278 // Intentionally not using the QT default setting as this is intended to be changed in the ini 962 // Intentionally not using the QT default setting as this is intended to be changed in the ini
1279 qt_config->setValue(QStringLiteral("record_frame_times"), Settings::values.record_frame_times); 963 qt_config->setValue(QStringLiteral("record_frame_times"), Settings::values.record_frame_times);
1280 WriteBasicSetting(Settings::values.use_gdbstub); 964
1281 WriteBasicSetting(Settings::values.gdbstub_port); 965 WriteCategory(Settings::Category::Debugging);
1282 WriteBasicSetting(Settings::values.program_args); 966 WriteCategory(Settings::Category::DebuggingGraphics);
1283 WriteBasicSetting(Settings::values.dump_exefs);
1284 WriteBasicSetting(Settings::values.dump_nso);
1285 WriteBasicSetting(Settings::values.enable_fs_access_log);
1286 WriteBasicSetting(Settings::values.quest_flag);
1287 WriteBasicSetting(Settings::values.use_debug_asserts);
1288 WriteBasicSetting(Settings::values.disable_macro_jit);
1289 WriteBasicSetting(Settings::values.disable_macro_hle);
1290 WriteBasicSetting(Settings::values.enable_all_controllers);
1291 WriteBasicSetting(Settings::values.create_crash_dumps);
1292 WriteBasicSetting(Settings::values.perform_vulkan_check);
1293 967
1294 qt_config->endGroup(); 968 qt_config->endGroup();
1295} 969}
@@ -1297,7 +971,7 @@ void Config::SaveDebuggingValues() {
1297void Config::SaveNetworkValues() { 971void Config::SaveNetworkValues() {
1298 qt_config->beginGroup(QStringLiteral("Services")); 972 qt_config->beginGroup(QStringLiteral("Services"));
1299 973
1300 WriteBasicSetting(Settings::values.network_interface); 974 WriteCategory(Settings::Category::Network);
1301 975
1302 qt_config->endGroup(); 976 qt_config->endGroup();
1303} 977}
@@ -1324,8 +998,7 @@ void Config::SaveDisabledAddOnValues() {
1324void Config::SaveMiscellaneousValues() { 998void Config::SaveMiscellaneousValues() {
1325 qt_config->beginGroup(QStringLiteral("Miscellaneous")); 999 qt_config->beginGroup(QStringLiteral("Miscellaneous"));
1326 1000
1327 WriteBasicSetting(Settings::values.log_filter); 1001 WriteCategory(Settings::Category::Miscellaneous);
1328 WriteBasicSetting(Settings::values.use_dev_keys);
1329 1002
1330 qt_config->endGroup(); 1003 qt_config->endGroup();
1331} 1004}
@@ -1353,34 +1026,9 @@ void Config::SavePathValues() {
1353void Config::SaveCpuValues() { 1026void Config::SaveCpuValues() {
1354 qt_config->beginGroup(QStringLiteral("Cpu")); 1027 qt_config->beginGroup(QStringLiteral("Cpu"));
1355 1028
1356 WriteBasicSetting(Settings::values.cpu_accuracy_first_time); 1029 WriteCategory(Settings::Category::Cpu);
1357 WriteSetting(QStringLiteral("cpu_accuracy"), 1030 WriteCategory(Settings::Category::CpuDebug);
1358 static_cast<u32>(Settings::values.cpu_accuracy.GetValue(global)), 1031 WriteCategory(Settings::Category::CpuUnsafe);
1359 static_cast<u32>(Settings::values.cpu_accuracy.GetDefault()),
1360 Settings::values.cpu_accuracy.UsingGlobal());
1361
1362 WriteGlobalSetting(Settings::values.cpuopt_unsafe_unfuse_fma);
1363 WriteGlobalSetting(Settings::values.cpuopt_unsafe_reduce_fp_error);
1364 WriteGlobalSetting(Settings::values.cpuopt_unsafe_ignore_standard_fpcr);
1365 WriteGlobalSetting(Settings::values.cpuopt_unsafe_inaccurate_nan);
1366 WriteGlobalSetting(Settings::values.cpuopt_unsafe_fastmem_check);
1367 WriteGlobalSetting(Settings::values.cpuopt_unsafe_ignore_global_monitor);
1368
1369 if (global) {
1370 WriteBasicSetting(Settings::values.cpu_debug_mode);
1371 WriteBasicSetting(Settings::values.cpuopt_page_tables);
1372 WriteBasicSetting(Settings::values.cpuopt_block_linking);
1373 WriteBasicSetting(Settings::values.cpuopt_return_stack_buffer);
1374 WriteBasicSetting(Settings::values.cpuopt_fast_dispatcher);
1375 WriteBasicSetting(Settings::values.cpuopt_context_elimination);
1376 WriteBasicSetting(Settings::values.cpuopt_const_prop);
1377 WriteBasicSetting(Settings::values.cpuopt_misc_ir);
1378 WriteBasicSetting(Settings::values.cpuopt_reduce_misalign_checks);
1379 WriteBasicSetting(Settings::values.cpuopt_fastmem);
1380 WriteBasicSetting(Settings::values.cpuopt_fastmem_exclusives);
1381 WriteBasicSetting(Settings::values.cpuopt_recompile_exclusives);
1382 WriteBasicSetting(Settings::values.cpuopt_ignore_memory_aborts);
1383 }
1384 1032
1385 qt_config->endGroup(); 1033 qt_config->endGroup();
1386} 1034}
@@ -1388,76 +1036,9 @@ void Config::SaveCpuValues() {
1388void Config::SaveRendererValues() { 1036void Config::SaveRendererValues() {
1389 qt_config->beginGroup(QStringLiteral("Renderer")); 1037 qt_config->beginGroup(QStringLiteral("Renderer"));
1390 1038
1391 WriteSetting(QString::fromStdString(Settings::values.renderer_backend.GetLabel()), 1039 WriteCategory(Settings::Category::Renderer);
1392 static_cast<u32>(Settings::values.renderer_backend.GetValue(global)), 1040 WriteCategory(Settings::Category::RendererAdvanced);
1393 static_cast<u32>(Settings::values.renderer_backend.GetDefault()), 1041 WriteCategory(Settings::Category::RendererDebug);
1394 Settings::values.renderer_backend.UsingGlobal());
1395 WriteGlobalSetting(Settings::values.async_presentation);
1396 WriteGlobalSetting(Settings::values.renderer_force_max_clock);
1397 WriteGlobalSetting(Settings::values.vulkan_device);
1398 WriteSetting(QString::fromStdString(Settings::values.fullscreen_mode.GetLabel()),
1399 static_cast<u32>(Settings::values.fullscreen_mode.GetValue(global)),
1400 static_cast<u32>(Settings::values.fullscreen_mode.GetDefault()),
1401 Settings::values.fullscreen_mode.UsingGlobal());
1402 WriteGlobalSetting(Settings::values.aspect_ratio);
1403 WriteSetting(QString::fromStdString(Settings::values.resolution_setup.GetLabel()),
1404 static_cast<u32>(Settings::values.resolution_setup.GetValue(global)),
1405 static_cast<u32>(Settings::values.resolution_setup.GetDefault()),
1406 Settings::values.resolution_setup.UsingGlobal());
1407 WriteSetting(QString::fromStdString(Settings::values.scaling_filter.GetLabel()),
1408 static_cast<u32>(Settings::values.scaling_filter.GetValue(global)),
1409 static_cast<u32>(Settings::values.scaling_filter.GetDefault()),
1410 Settings::values.scaling_filter.UsingGlobal());
1411 WriteSetting(QString::fromStdString(Settings::values.fsr_sharpening_slider.GetLabel()),
1412 static_cast<u32>(Settings::values.fsr_sharpening_slider.GetValue(global)),
1413 static_cast<u32>(Settings::values.fsr_sharpening_slider.GetDefault()),
1414 Settings::values.fsr_sharpening_slider.UsingGlobal());
1415 WriteSetting(QString::fromStdString(Settings::values.anti_aliasing.GetLabel()),
1416 static_cast<u32>(Settings::values.anti_aliasing.GetValue(global)),
1417 static_cast<u32>(Settings::values.anti_aliasing.GetDefault()),
1418 Settings::values.anti_aliasing.UsingGlobal());
1419 WriteGlobalSetting(Settings::values.max_anisotropy);
1420 WriteGlobalSetting(Settings::values.speed_limit);
1421 WriteGlobalSetting(Settings::values.use_disk_shader_cache);
1422 WriteSetting(QString::fromStdString(Settings::values.gpu_accuracy.GetLabel()),
1423 static_cast<u32>(Settings::values.gpu_accuracy.GetValue(global)),
1424 static_cast<u32>(Settings::values.gpu_accuracy.GetDefault()),
1425 Settings::values.gpu_accuracy.UsingGlobal());
1426 WriteGlobalSetting(Settings::values.use_asynchronous_gpu_emulation);
1427 WriteSetting(QString::fromStdString(Settings::values.nvdec_emulation.GetLabel()),
1428 static_cast<u32>(Settings::values.nvdec_emulation.GetValue(global)),
1429 static_cast<u32>(Settings::values.nvdec_emulation.GetDefault()),
1430 Settings::values.nvdec_emulation.UsingGlobal());
1431 WriteGlobalSetting(Settings::values.accelerate_astc);
1432 WriteGlobalSetting(Settings::values.async_astc);
1433 WriteSetting(QString::fromStdString(Settings::values.astc_recompression.GetLabel()),
1434 static_cast<u32>(Settings::values.astc_recompression.GetValue(global)),
1435 static_cast<u32>(Settings::values.astc_recompression.GetDefault()),
1436 Settings::values.astc_recompression.UsingGlobal());
1437 WriteGlobalSetting(Settings::values.use_reactive_flushing);
1438 WriteSetting(QString::fromStdString(Settings::values.shader_backend.GetLabel()),
1439 static_cast<u32>(Settings::values.shader_backend.GetValue(global)),
1440 static_cast<u32>(Settings::values.shader_backend.GetDefault()),
1441 Settings::values.shader_backend.UsingGlobal());
1442 WriteGlobalSetting(Settings::values.use_asynchronous_shaders);
1443 WriteGlobalSetting(Settings::values.use_fast_gpu_time);
1444 WriteGlobalSetting(Settings::values.use_vulkan_driver_pipeline_cache);
1445 WriteGlobalSetting(Settings::values.enable_compute_pipelines);
1446 WriteGlobalSetting(Settings::values.use_video_framerate);
1447 WriteGlobalSetting(Settings::values.barrier_feedback_loops);
1448 WriteGlobalSetting(Settings::values.bg_red);
1449 WriteGlobalSetting(Settings::values.bg_green);
1450 WriteGlobalSetting(Settings::values.bg_blue);
1451
1452 if (global) {
1453 WriteSetting(QString::fromStdString(Settings::values.vsync_mode.GetLabel()),
1454 static_cast<u32>(Settings::values.vsync_mode.GetValue()),
1455 static_cast<u32>(Settings::values.vsync_mode.GetDefault()));
1456 WriteBasicSetting(Settings::values.renderer_debug);
1457 WriteBasicSetting(Settings::values.renderer_shader_feedback);
1458 WriteBasicSetting(Settings::values.enable_nsight_aftermath);
1459 WriteBasicSetting(Settings::values.disable_shader_loop_safety_checks);
1460 }
1461 1042
1462 qt_config->endGroup(); 1043 qt_config->endGroup();
1463} 1044}
@@ -1465,9 +1046,9 @@ void Config::SaveRendererValues() {
1465void Config::SaveScreenshotValues() { 1046void Config::SaveScreenshotValues() {
1466 qt_config->beginGroup(QStringLiteral("Screenshots")); 1047 qt_config->beginGroup(QStringLiteral("Screenshots"));
1467 1048
1468 WriteBasicSetting(UISettings::values.enable_screenshot_save_as);
1469 WriteSetting(QStringLiteral("screenshot_path"), 1049 WriteSetting(QStringLiteral("screenshot_path"),
1470 QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::ScreenshotsDir))); 1050 QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::ScreenshotsDir)));
1051 WriteCategory(Settings::Category::Screenshots);
1471 1052
1472 qt_config->endGroup(); 1053 qt_config->endGroup();
1473} 1054}
@@ -1498,27 +1079,8 @@ void Config::SaveShortcutValues() {
1498void Config::SaveSystemValues() { 1079void Config::SaveSystemValues() {
1499 qt_config->beginGroup(QStringLiteral("System")); 1080 qt_config->beginGroup(QStringLiteral("System"));
1500 1081
1501 WriteGlobalSetting(Settings::values.language_index); 1082 WriteCategory(Settings::Category::System);
1502 WriteGlobalSetting(Settings::values.region_index); 1083 WriteCategory(Settings::Category::SystemAudio);
1503 WriteGlobalSetting(Settings::values.time_zone_index);
1504
1505 WriteSetting(QStringLiteral("rng_seed_enabled"),
1506 Settings::values.rng_seed.GetValue(global).has_value(), false,
1507 Settings::values.rng_seed.UsingGlobal());
1508 WriteSetting(QStringLiteral("rng_seed"), Settings::values.rng_seed.GetValue(global).value_or(0),
1509 0, Settings::values.rng_seed.UsingGlobal());
1510
1511 if (global) {
1512 WriteBasicSetting(Settings::values.current_user);
1513
1514 WriteSetting(QStringLiteral("custom_rtc_enabled"), Settings::values.custom_rtc.has_value(),
1515 false);
1516 WriteSetting(QStringLiteral("custom_rtc"),
1517 QVariant::fromValue<long long>(Settings::values.custom_rtc.value_or(0)), 0);
1518 WriteBasicSetting(Settings::values.device_name);
1519 }
1520
1521 WriteGlobalSetting(Settings::values.sound_index);
1522 1084
1523 qt_config->endGroup(); 1085 qt_config->endGroup();
1524} 1086}
@@ -1526,10 +1088,11 @@ void Config::SaveSystemValues() {
1526void Config::SaveUIValues() { 1088void Config::SaveUIValues() {
1527 qt_config->beginGroup(QStringLiteral("UI")); 1089 qt_config->beginGroup(QStringLiteral("UI"));
1528 1090
1091 WriteCategory(Settings::Category::Ui);
1092 WriteCategory(Settings::Category::UiGeneral);
1093
1529 WriteSetting(QStringLiteral("theme"), UISettings::values.theme, 1094 WriteSetting(QStringLiteral("theme"), UISettings::values.theme,
1530 QString::fromUtf8(UISettings::themes[static_cast<size_t>(default_theme)].second)); 1095 QString::fromUtf8(UISettings::themes[static_cast<size_t>(default_theme)].second));
1531 WriteBasicSetting(UISettings::values.enable_discord_presence);
1532 WriteBasicSetting(UISettings::values.select_user_on_boot);
1533 1096
1534 SaveUIGamelistValues(); 1097 SaveUIGamelistValues();
1535 SaveUILayoutValues(); 1098 SaveUILayoutValues();
@@ -1538,37 +1101,14 @@ void Config::SaveUIValues() {
1538 SaveShortcutValues(); 1101 SaveShortcutValues();
1539 SaveMultiplayerValues(); 1102 SaveMultiplayerValues();
1540 1103
1541 WriteBasicSetting(UISettings::values.single_window_mode);
1542 WriteBasicSetting(UISettings::values.fullscreen);
1543 WriteBasicSetting(UISettings::values.display_titlebar);
1544 WriteBasicSetting(UISettings::values.show_filter_bar);
1545 WriteBasicSetting(UISettings::values.show_status_bar);
1546 WriteBasicSetting(UISettings::values.confirm_before_closing);
1547 WriteBasicSetting(UISettings::values.first_start);
1548 WriteBasicSetting(UISettings::values.callout_flags);
1549 WriteBasicSetting(UISettings::values.show_console);
1550 WriteBasicSetting(UISettings::values.pause_when_in_background);
1551 WriteBasicSetting(UISettings::values.mute_when_in_background);
1552 WriteBasicSetting(UISettings::values.hide_mouse);
1553 WriteBasicSetting(UISettings::values.controller_applet_disabled);
1554 WriteBasicSetting(UISettings::values.disable_web_applet);
1555
1556 qt_config->endGroup(); 1104 qt_config->endGroup();
1557} 1105}
1558 1106
1559void Config::SaveUIGamelistValues() { 1107void Config::SaveUIGamelistValues() {
1560 qt_config->beginGroup(QStringLiteral("UIGameList")); 1108 qt_config->beginGroup(QStringLiteral("UIGameList"));
1561 1109
1562 WriteBasicSetting(UISettings::values.show_add_ons); 1110 WriteCategory(Settings::Category::UiGameList);
1563 WriteBasicSetting(UISettings::values.show_compat); 1111
1564 WriteBasicSetting(UISettings::values.show_size);
1565 WriteBasicSetting(UISettings::values.show_types);
1566 WriteBasicSetting(UISettings::values.game_icon_size);
1567 WriteBasicSetting(UISettings::values.folder_icon_size);
1568 WriteBasicSetting(UISettings::values.row_1_text_id);
1569 WriteBasicSetting(UISettings::values.row_2_text_id);
1570 WriteBasicSetting(UISettings::values.cache_game_list);
1571 WriteBasicSetting(UISettings::values.favorites_expanded);
1572 qt_config->beginWriteArray(QStringLiteral("favorites")); 1112 qt_config->beginWriteArray(QStringLiteral("favorites"));
1573 for (int i = 0; i < UISettings::values.favorited_ids.size(); i++) { 1113 for (int i = 0; i < UISettings::values.favorited_ids.size(); i++) {
1574 qt_config->setArrayIndex(i); 1114 qt_config->setArrayIndex(i);
@@ -1589,7 +1129,8 @@ void Config::SaveUILayoutValues() {
1589 WriteSetting(QStringLiteral("gameListHeaderState"), UISettings::values.gamelist_header_state); 1129 WriteSetting(QStringLiteral("gameListHeaderState"), UISettings::values.gamelist_header_state);
1590 WriteSetting(QStringLiteral("microProfileDialogGeometry"), 1130 WriteSetting(QStringLiteral("microProfileDialogGeometry"),
1591 UISettings::values.microprofile_geometry); 1131 UISettings::values.microprofile_geometry);
1592 WriteBasicSetting(UISettings::values.microprofile_visible); 1132
1133 WriteCategory(Settings::Category::UiLayout);
1593 1134
1594 qt_config->endGroup(); 1135 qt_config->endGroup();
1595} 1136}
@@ -1597,10 +1138,7 @@ void Config::SaveUILayoutValues() {
1597void Config::SaveWebServiceValues() { 1138void Config::SaveWebServiceValues() {
1598 qt_config->beginGroup(QStringLiteral("WebService")); 1139 qt_config->beginGroup(QStringLiteral("WebService"));
1599 1140
1600 WriteBasicSetting(Settings::values.enable_telemetry); 1141 WriteCategory(Settings::Category::WebService);
1601 WriteBasicSetting(Settings::values.web_api_url);
1602 WriteBasicSetting(Settings::values.yuzu_username);
1603 WriteBasicSetting(Settings::values.yuzu_token);
1604 1142
1605 qt_config->endGroup(); 1143 qt_config->endGroup();
1606} 1144}
@@ -1608,17 +1146,7 @@ void Config::SaveWebServiceValues() {
1608void Config::SaveMultiplayerValues() { 1146void Config::SaveMultiplayerValues() {
1609 qt_config->beginGroup(QStringLiteral("Multiplayer")); 1147 qt_config->beginGroup(QStringLiteral("Multiplayer"));
1610 1148
1611 WriteBasicSetting(UISettings::values.multiplayer_nickname); 1149 WriteCategory(Settings::Category::Multiplayer);
1612 WriteBasicSetting(UISettings::values.multiplayer_ip);
1613 WriteBasicSetting(UISettings::values.multiplayer_port);
1614 WriteBasicSetting(UISettings::values.multiplayer_room_nickname);
1615 WriteBasicSetting(UISettings::values.multiplayer_room_name);
1616 WriteBasicSetting(UISettings::values.multiplayer_room_port);
1617 WriteBasicSetting(UISettings::values.multiplayer_host_type);
1618 WriteBasicSetting(UISettings::values.multiplayer_port);
1619 WriteBasicSetting(UISettings::values.multiplayer_max_player);
1620 WriteBasicSetting(UISettings::values.multiplayer_game_id);
1621 WriteBasicSetting(UISettings::values.multiplayer_room_description);
1622 1150
1623 // Write ban list 1151 // Write ban list
1624 qt_config->beginWriteArray(QStringLiteral("username_ban_list")); 1152 qt_config->beginWriteArray(QStringLiteral("username_ban_list"));
@@ -1653,27 +1181,6 @@ QVariant Config::ReadSetting(const QString& name, const QVariant& default_value)
1653 return result; 1181 return result;
1654} 1182}
1655 1183
1656template <typename Type, bool ranged>
1657void Config::ReadGlobalSetting(Settings::SwitchableSetting<Type, ranged>& setting) {
1658 QString name = QString::fromStdString(setting.GetLabel());
1659 const bool use_global = qt_config->value(name + QStringLiteral("/use_global"), true).toBool();
1660 setting.SetGlobal(use_global);
1661 if (global || !use_global) {
1662 setting.SetValue(static_cast<QVariant>(
1663 ReadSetting(name, QVariant::fromValue<Type>(setting.GetDefault())))
1664 .value<Type>());
1665 }
1666}
1667
1668template <typename Type>
1669void Config::ReadSettingGlobal(Type& setting, const QString& name,
1670 const QVariant& default_value) const {
1671 const bool use_global = qt_config->value(name + QStringLiteral("/use_global"), true).toBool();
1672 if (global || !use_global) {
1673 setting = ReadSetting(name, default_value).value<Type>();
1674 }
1675}
1676
1677void Config::WriteSetting(const QString& name, const QVariant& value) { 1184void Config::WriteSetting(const QString& name, const QVariant& value) {
1678 qt_config->setValue(name, value); 1185 qt_config->setValue(name, value);
1679} 1186}
@@ -1727,3 +1234,72 @@ void Config::ClearControlPlayerValues() {
1727const std::string& Config::GetConfigFilePath() const { 1234const std::string& Config::GetConfigFilePath() const {
1728 return qt_config_loc; 1235 return qt_config_loc;
1729} 1236}
1237
1238static auto FindRelevantList(Settings::Category category) {
1239 auto& map = Settings::values.linkage.by_category;
1240 if (map.contains(category)) {
1241 return Settings::values.linkage.by_category[category];
1242 }
1243 return UISettings::values.linkage.by_category[category];
1244}
1245
1246void Config::ReadCategory(Settings::Category category) {
1247 const auto& settings = FindRelevantList(category);
1248 std::for_each(settings.begin(), settings.end(),
1249 [&](const auto& setting) { ReadSettingGeneric(setting); });
1250}
1251
1252void Config::WriteCategory(Settings::Category category) {
1253 const auto& settings = FindRelevantList(category);
1254 std::for_each(settings.begin(), settings.end(),
1255 [&](const auto& setting) { WriteSettingGeneric(setting); });
1256}
1257
1258void Config::ReadSettingGeneric(Settings::BasicSetting* const setting) {
1259 if (!setting->Save() || (!setting->Switchable() && !global)) {
1260 return;
1261 }
1262 const QString name = QString::fromStdString(setting->GetLabel());
1263 const auto default_value =
1264 QVariant::fromValue<QString>(QString::fromStdString(setting->DefaultToString()));
1265
1266 bool use_global = true;
1267 if (setting->Switchable() && !global) {
1268 use_global = qt_config->value(name + QStringLiteral("/use_global"), true).value<bool>();
1269 setting->SetGlobal(use_global);
1270 }
1271
1272 if (global || !use_global) {
1273 const bool is_default =
1274 qt_config->value(name + QStringLiteral("/default"), true).value<bool>();
1275 if (!is_default) {
1276 setting->LoadString(
1277 qt_config->value(name, default_value).value<QString>().toStdString());
1278 } else {
1279 // Empty string resets the Setting to default
1280 setting->LoadString("");
1281 }
1282 }
1283}
1284
1285void Config::WriteSettingGeneric(Settings::BasicSetting* const setting) const {
1286 if (!setting->Save()) {
1287 return;
1288 }
1289 const QVariant value = QVariant::fromValue(QString::fromStdString(setting->ToString()));
1290 const QVariant default_value =
1291 QVariant::fromValue(QString::fromStdString(setting->DefaultToString()));
1292 const QString label = QString::fromStdString(setting->GetLabel());
1293 if (setting->Switchable()) {
1294 if (!global) {
1295 qt_config->setValue(label + QStringLiteral("/use_global"), setting->UsingGlobal());
1296 }
1297 if (global || !setting->UsingGlobal()) {
1298 qt_config->setValue(label + QStringLiteral("/default"), value == default_value);
1299 qt_config->setValue(label, value);
1300 }
1301 } else if (global) {
1302 qt_config->setValue(label + QStringLiteral("/default"), value == default_value);
1303 qt_config->setValue(label, value);
1304 }
1305}
diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h
index 1211389d2..0ac74c8e7 100644
--- a/src/yuzu/configuration/config.h
+++ b/src/yuzu/configuration/config.h
@@ -52,7 +52,7 @@ public:
52 static const std::map<Settings::AntiAliasing, QString> anti_aliasing_texts_map; 52 static const std::map<Settings::AntiAliasing, QString> anti_aliasing_texts_map;
53 static const std::map<Settings::ScalingFilter, QString> scaling_filter_texts_map; 53 static const std::map<Settings::ScalingFilter, QString> scaling_filter_texts_map;
54 static const std::map<bool, QString> use_docked_mode_texts_map; 54 static const std::map<bool, QString> use_docked_mode_texts_map;
55 static const std::map<Settings::GPUAccuracy, QString> gpu_accuracy_texts_map; 55 static const std::map<Settings::GpuAccuracy, QString> gpu_accuracy_texts_map;
56 static const std::map<Settings::RendererBackend, QString> renderer_backend_texts_map; 56 static const std::map<Settings::RendererBackend, QString> renderer_backend_texts_map;
57 static const std::map<Settings::ShaderBackend, QString> shader_backend_texts_map; 57 static const std::map<Settings::ShaderBackend, QString> shader_backend_texts_map;
58 58
@@ -74,7 +74,6 @@ private:
74 void ReadKeyboardValues(); 74 void ReadKeyboardValues();
75 void ReadMouseValues(); 75 void ReadMouseValues();
76 void ReadTouchscreenValues(); 76 void ReadTouchscreenValues();
77 void ReadMousePanningValues();
78 void ReadMotionTouchValues(); 77 void ReadMotionTouchValues();
79 void ReadHidbusValues(); 78 void ReadHidbusValues();
80 void ReadIrCameraValues(); 79 void ReadIrCameraValues();
@@ -99,13 +98,13 @@ private:
99 void ReadUILayoutValues(); 98 void ReadUILayoutValues();
100 void ReadWebServiceValues(); 99 void ReadWebServiceValues();
101 void ReadMultiplayerValues(); 100 void ReadMultiplayerValues();
101 void ReadNetworkValues();
102 102
103 void SaveValues(); 103 void SaveValues();
104 void SavePlayerValue(std::size_t player_index); 104 void SavePlayerValue(std::size_t player_index);
105 void SaveDebugValues(); 105 void SaveDebugValues();
106 void SaveMouseValues(); 106 void SaveMouseValues();
107 void SaveTouchscreenValues(); 107 void SaveTouchscreenValues();
108 void SaveMousePanningValues();
109 void SaveMotionTouchValues(); 108 void SaveMotionTouchValues();
110 void SaveHidbusValues(); 109 void SaveHidbusValues();
111 void SaveIrCameraValues(); 110 void SaveIrCameraValues();
@@ -141,18 +140,6 @@ private:
141 QVariant ReadSetting(const QString& name, const QVariant& default_value) const; 140 QVariant ReadSetting(const QString& name, const QVariant& default_value) const;
142 141
143 /** 142 /**
144 * Only reads a setting from the qt_config if the current config is a global config, or if the
145 * current config is a custom config and the setting is overriding the global setting. Otherwise
146 * it does nothing.
147 *
148 * @param setting The variable to be modified
149 * @param name The setting's identifier
150 * @param default_value The value to use when the setting is not already present in the config
151 */
152 template <typename Type>
153 void ReadSettingGlobal(Type& setting, const QString& name, const QVariant& default_value) const;
154
155 /**
156 * Writes a setting to the qt_config. 143 * Writes a setting to the qt_config.
157 * 144 *
158 * @param name The setting's idetentifier 145 * @param name The setting's idetentifier
@@ -166,50 +153,20 @@ private:
166 void WriteSetting(const QString& name, const QVariant& value, const QVariant& default_value, 153 void WriteSetting(const QString& name, const QVariant& value, const QVariant& default_value,
167 bool use_global); 154 bool use_global);
168 155
169 /** 156 void ReadCategory(Settings::Category category);
170 * Reads a value from the qt_config and applies it to the setting, using its label and default 157 void WriteCategory(Settings::Category category);
171 * value. If the config is a custom config, this will also read the global state of the setting 158 void ReadSettingGeneric(Settings::BasicSetting* const setting);
172 * and apply that information to it. 159 void WriteSettingGeneric(Settings::BasicSetting* const setting) const;
173 *
174 * @param The setting
175 */
176 template <typename Type, bool ranged>
177 void ReadGlobalSetting(Settings::SwitchableSetting<Type, ranged>& setting);
178
179 /**
180 * Sets a value to the qt_config using the setting's label and default value. If the config is a
181 * custom config, it will apply the global state, and the custom value if needed.
182 *
183 * @param The setting
184 */
185 template <typename Type, bool ranged>
186 void WriteGlobalSetting(const Settings::SwitchableSetting<Type, ranged>& setting);
187
188 /**
189 * Reads a value from the qt_config using the setting's label and default value and applies the
190 * value to the setting.
191 *
192 * @param The setting
193 */
194 template <typename Type, bool ranged>
195 void ReadBasicSetting(Settings::Setting<Type, ranged>& setting);
196
197 /** Sets a value from the setting in the qt_config using the setting's label and default value.
198 *
199 * @param The setting
200 */
201 template <typename Type, bool ranged>
202 void WriteBasicSetting(const Settings::Setting<Type, ranged>& setting);
203 160
204 ConfigType type; 161 const ConfigType type;
205 std::unique_ptr<QSettings> qt_config; 162 std::unique_ptr<QSettings> qt_config;
206 std::string qt_config_loc; 163 std::string qt_config_loc;
207 bool global; 164 const bool global;
208}; 165};
209 166
210// These metatype declarations cannot be in common/settings.h because core is devoid of QT 167// These metatype declarations cannot be in common/settings.h because core is devoid of QT
211Q_DECLARE_METATYPE(Settings::CPUAccuracy); 168Q_DECLARE_METATYPE(Settings::CpuAccuracy);
212Q_DECLARE_METATYPE(Settings::GPUAccuracy); 169Q_DECLARE_METATYPE(Settings::GpuAccuracy);
213Q_DECLARE_METATYPE(Settings::FullscreenMode); 170Q_DECLARE_METATYPE(Settings::FullscreenMode);
214Q_DECLARE_METATYPE(Settings::NvdecEmulation); 171Q_DECLARE_METATYPE(Settings::NvdecEmulation);
215Q_DECLARE_METATYPE(Settings::ResolutionSetup); 172Q_DECLARE_METATYPE(Settings::ResolutionSetup);
@@ -218,3 +175,4 @@ Q_DECLARE_METATYPE(Settings::AntiAliasing);
218Q_DECLARE_METATYPE(Settings::RendererBackend); 175Q_DECLARE_METATYPE(Settings::RendererBackend);
219Q_DECLARE_METATYPE(Settings::ShaderBackend); 176Q_DECLARE_METATYPE(Settings::ShaderBackend);
220Q_DECLARE_METATYPE(Settings::AstcRecompression); 177Q_DECLARE_METATYPE(Settings::AstcRecompression);
178Q_DECLARE_METATYPE(Settings::AstcDecodeMode);
diff --git a/src/yuzu/configuration/configuration_shared.cpp b/src/yuzu/configuration/configuration_shared.cpp
index ac42cc7fc..0ed6146a0 100644
--- a/src/yuzu/configuration/configuration_shared.cpp
+++ b/src/yuzu/configuration/configuration_shared.cpp
@@ -1,104 +1,19 @@
1// SPDX-FileCopyrightText: 2016 Citra Emulator Project 1// SPDX-FileCopyrightText: 2016 Citra Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <QCheckBox> 4#include <memory>
5#include <QObject> 5#include <type_traits>
6#include <QString> 6#include <vector>
7#include "common/settings.h"
8#include "yuzu/configuration/configuration_shared.h" 7#include "yuzu/configuration/configuration_shared.h"
9#include "yuzu/configuration/configure_per_game.h"
10 8
11void ConfigurationShared::ApplyPerGameSetting(Settings::SwitchableSetting<bool>* setting, 9namespace ConfigurationShared {
12 const QCheckBox* checkbox,
13 const CheckState& tracker) {
14 if (Settings::IsConfiguringGlobal() && setting->UsingGlobal()) {
15 setting->SetValue(checkbox->checkState());
16 } else if (!Settings::IsConfiguringGlobal()) {
17 if (tracker == CheckState::Global) {
18 setting->SetGlobal(true);
19 } else {
20 setting->SetGlobal(false);
21 setting->SetValue(checkbox->checkState());
22 }
23 }
24}
25 10
26void ConfigurationShared::SetPerGameSetting(QCheckBox* checkbox, 11Tab::Tab(std::shared_ptr<std::vector<Tab*>> group, QWidget* parent) : QWidget(parent) {
27 const Settings::SwitchableSetting<bool>* setting) { 12 if (group != nullptr) {
28 if (setting->UsingGlobal()) { 13 group->push_back(this);
29 checkbox->setCheckState(Qt::PartiallyChecked);
30 } else {
31 checkbox->setCheckState(setting->GetValue() ? Qt::Checked : Qt::Unchecked);
32 } 14 }
33} 15}
34 16
35void ConfigurationShared::SetHighlight(QWidget* widget, bool highlighted) { 17Tab::~Tab() = default;
36 if (highlighted) {
37 widget->setStyleSheet(QStringLiteral("QWidget#%1 { background-color:rgba(0,203,255,0.5) }")
38 .arg(widget->objectName()));
39 } else {
40 widget->setStyleSheet(QStringLiteral("QWidget#%1 { background-color:rgba(0,0,0,0) }")
41 .arg(widget->objectName()));
42 }
43 widget->show();
44}
45 18
46void ConfigurationShared::SetColoredTristate(QCheckBox* checkbox, 19} // namespace ConfigurationShared
47 const Settings::SwitchableSetting<bool>& setting,
48 CheckState& tracker) {
49 if (setting.UsingGlobal()) {
50 tracker = CheckState::Global;
51 } else {
52 tracker = (setting.GetValue() == setting.GetValue(true)) ? CheckState::On : CheckState::Off;
53 }
54 SetHighlight(checkbox, tracker != CheckState::Global);
55 QObject::connect(checkbox, &QCheckBox::clicked, checkbox, [checkbox, setting, &tracker] {
56 tracker = static_cast<CheckState>((static_cast<int>(tracker) + 1) %
57 static_cast<int>(CheckState::Count));
58 if (tracker == CheckState::Global) {
59 checkbox->setChecked(setting.GetValue(true));
60 }
61 SetHighlight(checkbox, tracker != CheckState::Global);
62 });
63}
64
65void ConfigurationShared::SetColoredTristate(QCheckBox* checkbox, bool global, bool state,
66 bool global_state, CheckState& tracker) {
67 if (global) {
68 tracker = CheckState::Global;
69 } else {
70 tracker = (state == global_state) ? CheckState::On : CheckState::Off;
71 }
72 SetHighlight(checkbox, tracker != CheckState::Global);
73 QObject::connect(checkbox, &QCheckBox::clicked, checkbox, [checkbox, global_state, &tracker] {
74 tracker = static_cast<CheckState>((static_cast<int>(tracker) + 1) %
75 static_cast<int>(CheckState::Count));
76 if (tracker == CheckState::Global) {
77 checkbox->setChecked(global_state);
78 }
79 SetHighlight(checkbox, tracker != CheckState::Global);
80 });
81}
82
83void ConfigurationShared::SetColoredComboBox(QComboBox* combobox, QWidget* target, int global) {
84 InsertGlobalItem(combobox, global);
85 QObject::connect(combobox, qOverload<int>(&QComboBox::activated), target,
86 [target](int index) { SetHighlight(target, index != 0); });
87}
88
89void ConfigurationShared::InsertGlobalItem(QComboBox* combobox, int global_index) {
90 const QString use_global_text =
91 ConfigurePerGame::tr("Use global configuration (%1)").arg(combobox->itemText(global_index));
92 combobox->insertItem(ConfigurationShared::USE_GLOBAL_INDEX, use_global_text);
93 combobox->insertSeparator(ConfigurationShared::USE_GLOBAL_SEPARATOR_INDEX);
94}
95
96int ConfigurationShared::GetComboboxIndex(int global_setting_index, const QComboBox* combobox) {
97 if (Settings::IsConfiguringGlobal()) {
98 return combobox->currentIndex();
99 }
100 if (combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
101 return global_setting_index;
102 }
103 return combobox->currentIndex() - ConfigurationShared::USE_GLOBAL_OFFSET;
104}
diff --git a/src/yuzu/configuration/configuration_shared.h b/src/yuzu/configuration/configuration_shared.h
index 04c88758c..31897a6b0 100644
--- a/src/yuzu/configuration/configuration_shared.h
+++ b/src/yuzu/configuration/configuration_shared.h
@@ -3,73 +3,25 @@
3 3
4#pragma once 4#pragma once
5 5
6#include <QCheckBox> 6#include <memory>
7#include <QComboBox> 7#include <vector>
8#include "common/settings.h" 8#include <QString>
9#include <QWidget>
10#include <qobjectdefs.h>
9 11
10namespace ConfigurationShared { 12class QObject;
11
12constexpr int USE_GLOBAL_INDEX = 0;
13constexpr int USE_GLOBAL_SEPARATOR_INDEX = 1;
14constexpr int USE_GLOBAL_OFFSET = 2;
15
16// CheckBoxes require a tracker for their state since we emulate a tristate CheckBox
17enum class CheckState {
18 Off, // Checkbox overrides to off/false
19 On, // Checkbox overrides to on/true
20 Global, // Checkbox defers to the global state
21 Count, // Simply the number of states, not a valid checkbox state
22};
23
24// Global-aware apply and set functions
25
26// ApplyPerGameSetting, given a Settings::Setting and a Qt UI element, properly applies a Setting
27void ApplyPerGameSetting(Settings::SwitchableSetting<bool>* setting, const QCheckBox* checkbox,
28 const CheckState& tracker);
29template <typename Type, bool ranged>
30void ApplyPerGameSetting(Settings::SwitchableSetting<Type, ranged>* setting,
31 const QComboBox* combobox) {
32 if (Settings::IsConfiguringGlobal() && setting->UsingGlobal()) {
33 setting->SetValue(static_cast<Type>(combobox->currentIndex()));
34 } else if (!Settings::IsConfiguringGlobal()) {
35 if (combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
36 setting->SetGlobal(true);
37 } else {
38 setting->SetGlobal(false);
39 setting->SetValue(static_cast<Type>(combobox->currentIndex() -
40 ConfigurationShared::USE_GLOBAL_OFFSET));
41 }
42 }
43}
44 13
45// Sets a Qt UI element given a Settings::Setting 14namespace ConfigurationShared {
46void SetPerGameSetting(QCheckBox* checkbox, const Settings::SwitchableSetting<bool>* setting);
47
48template <typename Type, bool ranged>
49void SetPerGameSetting(QComboBox* combobox,
50 const Settings::SwitchableSetting<Type, ranged>* setting) {
51 combobox->setCurrentIndex(setting->UsingGlobal() ? ConfigurationShared::USE_GLOBAL_INDEX
52 : static_cast<int>(setting->GetValue()) +
53 ConfigurationShared::USE_GLOBAL_OFFSET);
54}
55
56// (Un)highlights a Qt UI element
57void SetHighlight(QWidget* widget, bool highlighted);
58
59// Sets up a QCheckBox like a tristate one, given a Setting
60void SetColoredTristate(QCheckBox* checkbox, const Settings::SwitchableSetting<bool>& setting,
61 CheckState& tracker);
62void SetColoredTristate(QCheckBox* checkbox, bool global, bool state, bool global_state,
63 CheckState& tracker);
64 15
65// Sets up coloring of a QWidget `target` based on the state of a QComboBox, and calls 16class Tab : public QWidget {
66// InsertGlobalItem 17 Q_OBJECT
67void SetColoredComboBox(QComboBox* combobox, QWidget* target, int global);
68 18
69// Adds the "Use Global Configuration" selection and separator to the beginning of a QComboBox 19public:
70void InsertGlobalItem(QComboBox* combobox, int global_index); 20 explicit Tab(std::shared_ptr<std::vector<Tab*>> group, QWidget* parent = nullptr);
21 ~Tab();
71 22
72// Returns the correct index of a QComboBox taking into account global configuration 23 virtual void ApplyConfiguration() = 0;
73int GetComboboxIndex(int global_setting_index, const QComboBox* combobox); 24 virtual void SetConfiguration() = 0;
25};
74 26
75} // namespace ConfigurationShared 27} // namespace ConfigurationShared
diff --git a/src/yuzu/configuration/configure.ui b/src/yuzu/configuration/configure.ui
index eb8078467..573c40801 100644
--- a/src/yuzu/configuration/configure.ui
+++ b/src/yuzu/configuration/configure.ui
@@ -48,11 +48,34 @@
48 </layout> 48 </layout>
49 </item> 49 </item>
50 <item> 50 <item>
51 <widget class="QDialogButtonBox" name="buttonBox"> 51 <layout class="QHBoxLayout" name="horizontalLayout_2">
52 <property name="standardButtons"> 52 <property name="leftMargin">
53 <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> 53 <number>0</number>
54 </property> 54 </property>
55 </widget> 55 <property name="topMargin">
56 <number>0</number>
57 </property>
58 <property name="rightMargin">
59 <number>0</number>
60 </property>
61 <property name="bottomMargin">
62 <number>0</number>
63 </property>
64 <item>
65 <widget class="QLabel" name="label">
66 <property name="text">
67 <string>Some settings are only available when a game is not running.</string>
68 </property>
69 </widget>
70 </item>
71 <item>
72 <widget class="QDialogButtonBox" name="buttonBox">
73 <property name="standardButtons">
74 <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
75 </property>
76 </widget>
77 </item>
78 </layout>
56 </item> 79 </item>
57 </layout> 80 </layout>
58 </widget> 81 </widget>
diff --git a/src/yuzu/configuration/configure_audio.cpp b/src/yuzu/configuration/configure_audio.cpp
index fcd6d61a0..9ccfb2435 100644
--- a/src/yuzu/configuration/configure_audio.cpp
+++ b/src/yuzu/configuration/configure_audio.cpp
@@ -1,87 +1,112 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <map>
4#include <memory> 5#include <memory>
6#include <vector>
7#include <QComboBox>
5 8
6#include "audio_core/sink/sink.h" 9#include "audio_core/sink/sink.h"
7#include "audio_core/sink/sink_details.h" 10#include "audio_core/sink/sink_details.h"
11#include "common/common_types.h"
8#include "common/settings.h" 12#include "common/settings.h"
13#include "common/settings_common.h"
9#include "core/core.h" 14#include "core/core.h"
10#include "ui_configure_audio.h" 15#include "ui_configure_audio.h"
11#include "yuzu/configuration/configuration_shared.h" 16#include "yuzu/configuration/configuration_shared.h"
12#include "yuzu/configuration/configure_audio.h" 17#include "yuzu/configuration/configure_audio.h"
18#include "yuzu/configuration/shared_translation.h"
19#include "yuzu/configuration/shared_widget.h"
13#include "yuzu/uisettings.h" 20#include "yuzu/uisettings.h"
14 21
15ConfigureAudio::ConfigureAudio(const Core::System& system_, QWidget* parent) 22ConfigureAudio::ConfigureAudio(const Core::System& system_,
16 : QWidget(parent), ui(std::make_unique<Ui::ConfigureAudio>()), system{system_} { 23 std::shared_ptr<std::vector<ConfigurationShared::Tab*>> group_,
24 const ConfigurationShared::Builder& builder, QWidget* parent)
25 : Tab(group_, parent), ui(std::make_unique<Ui::ConfigureAudio>()), system{system_} {
17 ui->setupUi(this); 26 ui->setupUi(this);
27 Setup(builder);
18 28
19 InitializeAudioSinkComboBox(); 29 SetConfiguration();
30}
20 31
21 connect(ui->volume_slider, &QSlider::valueChanged, this, 32ConfigureAudio::~ConfigureAudio() = default;
22 &ConfigureAudio::SetVolumeIndicatorText);
23 connect(ui->sink_combo_box, qOverload<int>(&QComboBox::currentIndexChanged), this,
24 &ConfigureAudio::UpdateAudioDevices);
25 33
26 ui->volume_label->setVisible(Settings::IsConfiguringGlobal()); 34void ConfigureAudio::Setup(const ConfigurationShared::Builder& builder) {
27 ui->volume_combo_box->setVisible(!Settings::IsConfiguringGlobal()); 35 auto& layout = *ui->audio_widget->layout();
28 36
29 SetupPerGameUI(); 37 std::vector<Settings::BasicSetting*> settings;
30 38
31 SetConfiguration(); 39 std::map<u32, QWidget*> hold;
32 40
33 const bool is_powered_on = system_.IsPoweredOn(); 41 auto push = [&](Settings::Category category) {
34 ui->sink_combo_box->setEnabled(!is_powered_on); 42 for (auto* setting : Settings::values.linkage.by_category[category]) {
35 ui->output_combo_box->setEnabled(!is_powered_on); 43 settings.push_back(setting);
36 ui->input_combo_box->setEnabled(!is_powered_on); 44 }
37} 45 };
38 46
39ConfigureAudio::~ConfigureAudio() = default; 47 push(Settings::Category::Audio);
48 push(Settings::Category::SystemAudio);
40 49
41void ConfigureAudio::SetConfiguration() { 50 for (auto* setting : settings) {
42 SetOutputSinkFromSinkID(); 51 auto* widget = builder.BuildWidget(setting, apply_funcs);
43 52
44 // The device list cannot be pre-populated (nor listed) until the output sink is known. 53 if (widget == nullptr) {
45 UpdateAudioDevices(ui->sink_combo_box->currentIndex()); 54 continue;
55 }
56 if (!widget->Valid()) {
57 widget->deleteLater();
58 continue;
59 }
46 60
47 SetAudioDevicesFromDeviceID(); 61 hold.emplace(std::pair{setting->Id(), widget});
62
63 if (setting->Id() == Settings::values.sink_id.Id()) {
64 // TODO (lat9nq): Let the system manage sink_id
65 sink_combo_box = widget->combobox;
66 InitializeAudioSinkComboBox();
67
68 connect(sink_combo_box, qOverload<int>(&QComboBox::currentIndexChanged), this,
69 &ConfigureAudio::UpdateAudioDevices);
70 } else if (setting->Id() == Settings::values.audio_output_device_id.Id()) {
71 // Keep track of output (and input) device comboboxes to populate them with system
72 // devices, which are determined at run time
73 output_device_combo_box = widget->combobox;
74 } else if (setting->Id() == Settings::values.audio_input_device_id.Id()) {
75 input_device_combo_box = widget->combobox;
76 }
77 }
48 78
49 const auto volume_value = static_cast<int>(Settings::values.volume.GetValue()); 79 for (const auto& [id, widget] : hold) {
50 ui->volume_slider->setValue(volume_value); 80 layout.addWidget(widget);
51 ui->toggle_background_mute->setChecked(UISettings::values.mute_when_in_background.GetValue()); 81 }
82}
52 83
84void ConfigureAudio::SetConfiguration() {
53 if (!Settings::IsConfiguringGlobal()) { 85 if (!Settings::IsConfiguringGlobal()) {
54 if (Settings::values.volume.UsingGlobal()) { 86 return;
55 ui->volume_combo_box->setCurrentIndex(0);
56 ui->volume_slider->setEnabled(false);
57 } else {
58 ui->volume_combo_box->setCurrentIndex(1);
59 ui->volume_slider->setEnabled(true);
60 }
61 ConfigurationShared::SetPerGameSetting(ui->combo_sound, &Settings::values.sound_index);
62 ConfigurationShared::SetHighlight(ui->mode_label,
63 !Settings::values.sound_index.UsingGlobal());
64 ConfigurationShared::SetHighlight(ui->volume_layout,
65 !Settings::values.volume.UsingGlobal());
66 } else {
67 ui->combo_sound->setCurrentIndex(Settings::values.sound_index.GetValue());
68 } 87 }
69 SetVolumeIndicatorText(ui->volume_slider->sliderPosition()); 88
89 SetOutputSinkFromSinkID();
90
91 // The device list cannot be pre-populated (nor listed) until the output sink is known.
92 UpdateAudioDevices(sink_combo_box->currentIndex());
93
94 SetAudioDevicesFromDeviceID();
70} 95}
71 96
72void ConfigureAudio::SetOutputSinkFromSinkID() { 97void ConfigureAudio::SetOutputSinkFromSinkID() {
73 [[maybe_unused]] const QSignalBlocker blocker(ui->sink_combo_box); 98 [[maybe_unused]] const QSignalBlocker blocker(sink_combo_box);
74 99
75 int new_sink_index = 0; 100 int new_sink_index = 0;
76 const QString sink_id = QString::fromStdString(Settings::values.sink_id.GetValue()); 101 const QString sink_id = QString::fromStdString(Settings::values.sink_id.ToString());
77 for (int index = 0; index < ui->sink_combo_box->count(); index++) { 102 for (int index = 0; index < sink_combo_box->count(); index++) {
78 if (ui->sink_combo_box->itemText(index) == sink_id) { 103 if (sink_combo_box->itemText(index) == sink_id) {
79 new_sink_index = index; 104 new_sink_index = index;
80 break; 105 break;
81 } 106 }
82 } 107 }
83 108
84 ui->sink_combo_box->setCurrentIndex(new_sink_index); 109 sink_combo_box->setCurrentIndex(new_sink_index);
85} 110}
86 111
87void ConfigureAudio::SetAudioDevicesFromDeviceID() { 112void ConfigureAudio::SetAudioDevicesFromDeviceID() {
@@ -89,57 +114,42 @@ void ConfigureAudio::SetAudioDevicesFromDeviceID() {
89 114
90 const QString output_device_id = 115 const QString output_device_id =
91 QString::fromStdString(Settings::values.audio_output_device_id.GetValue()); 116 QString::fromStdString(Settings::values.audio_output_device_id.GetValue());
92 for (int index = 0; index < ui->output_combo_box->count(); index++) { 117 for (int index = 0; index < output_device_combo_box->count(); index++) {
93 if (ui->output_combo_box->itemText(index) == output_device_id) { 118 if (output_device_combo_box->itemText(index) == output_device_id) {
94 new_device_index = index; 119 new_device_index = index;
95 break; 120 break;
96 } 121 }
97 } 122 }
98 123
99 ui->output_combo_box->setCurrentIndex(new_device_index); 124 output_device_combo_box->setCurrentIndex(new_device_index);
100 125
101 new_device_index = -1; 126 new_device_index = -1;
102 const QString input_device_id = 127 const QString input_device_id =
103 QString::fromStdString(Settings::values.audio_input_device_id.GetValue()); 128 QString::fromStdString(Settings::values.audio_input_device_id.GetValue());
104 for (int index = 0; index < ui->input_combo_box->count(); index++) { 129 for (int index = 0; index < input_device_combo_box->count(); index++) {
105 if (ui->input_combo_box->itemText(index) == input_device_id) { 130 if (input_device_combo_box->itemText(index) == input_device_id) {
106 new_device_index = index; 131 new_device_index = index;
107 break; 132 break;
108 } 133 }
109 } 134 }
110 135
111 ui->input_combo_box->setCurrentIndex(new_device_index); 136 input_device_combo_box->setCurrentIndex(new_device_index);
112}
113
114void ConfigureAudio::SetVolumeIndicatorText(int percentage) {
115 ui->volume_indicator->setText(tr("%1%", "Volume percentage (e.g. 50%)").arg(percentage));
116} 137}
117 138
118void ConfigureAudio::ApplyConfiguration() { 139void ConfigureAudio::ApplyConfiguration() {
119 ConfigurationShared::ApplyPerGameSetting(&Settings::values.sound_index, ui->combo_sound); 140 const bool is_powered_on = system.IsPoweredOn();
141 for (const auto& apply_func : apply_funcs) {
142 apply_func(is_powered_on);
143 }
120 144
121 if (Settings::IsConfiguringGlobal()) { 145 if (Settings::IsConfiguringGlobal()) {
122 Settings::values.sink_id = 146 Settings::values.sink_id.LoadString(
123 ui->sink_combo_box->itemText(ui->sink_combo_box->currentIndex()).toStdString(); 147 sink_combo_box->itemText(sink_combo_box->currentIndex()).toStdString());
124 Settings::values.audio_output_device_id.SetValue( 148 Settings::values.audio_output_device_id.SetValue(
125 ui->output_combo_box->itemText(ui->output_combo_box->currentIndex()).toStdString()); 149 output_device_combo_box->itemText(output_device_combo_box->currentIndex())
150 .toStdString());
126 Settings::values.audio_input_device_id.SetValue( 151 Settings::values.audio_input_device_id.SetValue(
127 ui->input_combo_box->itemText(ui->input_combo_box->currentIndex()).toStdString()); 152 input_device_combo_box->itemText(input_device_combo_box->currentIndex()).toStdString());
128 UISettings::values.mute_when_in_background = ui->toggle_background_mute->isChecked();
129
130 // Guard if during game and set to game-specific value
131 if (Settings::values.volume.UsingGlobal()) {
132 const auto volume = static_cast<u8>(ui->volume_slider->value());
133 Settings::values.volume.SetValue(volume);
134 }
135 } else {
136 if (ui->volume_combo_box->currentIndex() == 0) {
137 Settings::values.volume.SetGlobal(true);
138 } else {
139 Settings::values.volume.SetGlobal(false);
140 const auto volume = static_cast<u8>(ui->volume_slider->value());
141 Settings::values.volume.SetValue(volume);
142 }
143 } 153 }
144} 154}
145 155
@@ -152,54 +162,31 @@ void ConfigureAudio::changeEvent(QEvent* event) {
152} 162}
153 163
154void ConfigureAudio::UpdateAudioDevices(int sink_index) { 164void ConfigureAudio::UpdateAudioDevices(int sink_index) {
155 ui->output_combo_box->clear(); 165 output_device_combo_box->clear();
156 ui->output_combo_box->addItem(QString::fromUtf8(AudioCore::Sink::auto_device_name)); 166 output_device_combo_box->addItem(QString::fromUtf8(AudioCore::Sink::auto_device_name));
157 167
158 const std::string sink_id = ui->sink_combo_box->itemText(sink_index).toStdString(); 168 const auto sink_id =
169 Settings::ToEnum<Settings::AudioEngine>(sink_combo_box->itemText(sink_index).toStdString());
159 for (const auto& device : AudioCore::Sink::GetDeviceListForSink(sink_id, false)) { 170 for (const auto& device : AudioCore::Sink::GetDeviceListForSink(sink_id, false)) {
160 ui->output_combo_box->addItem(QString::fromStdString(device)); 171 output_device_combo_box->addItem(QString::fromStdString(device));
161 } 172 }
162 173
163 ui->input_combo_box->clear(); 174 input_device_combo_box->clear();
164 ui->input_combo_box->addItem(QString::fromUtf8(AudioCore::Sink::auto_device_name)); 175 input_device_combo_box->addItem(QString::fromUtf8(AudioCore::Sink::auto_device_name));
165 for (const auto& device : AudioCore::Sink::GetDeviceListForSink(sink_id, true)) { 176 for (const auto& device : AudioCore::Sink::GetDeviceListForSink(sink_id, true)) {
166 ui->input_combo_box->addItem(QString::fromStdString(device)); 177 input_device_combo_box->addItem(QString::fromStdString(device));
167 } 178 }
168} 179}
169 180
170void ConfigureAudio::InitializeAudioSinkComboBox() { 181void ConfigureAudio::InitializeAudioSinkComboBox() {
171 ui->sink_combo_box->clear(); 182 sink_combo_box->clear();
172 ui->sink_combo_box->addItem(QString::fromUtf8(AudioCore::Sink::auto_device_name)); 183 sink_combo_box->addItem(QString::fromUtf8(AudioCore::Sink::auto_device_name));
173 184
174 for (const auto& id : AudioCore::Sink::GetSinkIDs()) { 185 for (const auto& id : AudioCore::Sink::GetSinkIDs()) {
175 ui->sink_combo_box->addItem(QString::fromUtf8(id.data(), static_cast<s32>(id.length()))); 186 sink_combo_box->addItem(QString::fromStdString(Settings::CanonicalizeEnum(id)));
176 } 187 }
177} 188}
178 189
179void ConfigureAudio::RetranslateUI() { 190void ConfigureAudio::RetranslateUI() {
180 ui->retranslateUi(this); 191 ui->retranslateUi(this);
181 SetVolumeIndicatorText(ui->volume_slider->sliderPosition());
182}
183
184void ConfigureAudio::SetupPerGameUI() {
185 if (Settings::IsConfiguringGlobal()) {
186 ui->combo_sound->setEnabled(Settings::values.sound_index.UsingGlobal());
187 ui->volume_slider->setEnabled(Settings::values.volume.UsingGlobal());
188 return;
189 }
190
191 ConfigurationShared::SetColoredComboBox(ui->combo_sound, ui->mode_label,
192 Settings::values.sound_index.GetValue(true));
193
194 connect(ui->volume_combo_box, qOverload<int>(&QComboBox::activated), this, [this](int index) {
195 ui->volume_slider->setEnabled(index == 1);
196 ConfigurationShared::SetHighlight(ui->volume_layout, index == 1);
197 });
198
199 ui->sink_combo_box->setVisible(false);
200 ui->sink_label->setVisible(false);
201 ui->output_combo_box->setVisible(false);
202 ui->output_label->setVisible(false);
203 ui->input_combo_box->setVisible(false);
204 ui->input_label->setVisible(false);
205} 192}
diff --git a/src/yuzu/configuration/configure_audio.h b/src/yuzu/configuration/configure_audio.h
index 0d03aae1d..79538e81c 100644
--- a/src/yuzu/configuration/configure_audio.h
+++ b/src/yuzu/configuration/configure_audio.h
@@ -3,30 +3,35 @@
3 3
4#pragma once 4#pragma once
5 5
6#include <functional>
6#include <memory> 7#include <memory>
8#include <vector>
7#include <QWidget> 9#include <QWidget>
10#include "yuzu/configuration/configuration_shared.h"
11
12class QComboBox;
8 13
9namespace Core { 14namespace Core {
10class System; 15class System;
11} 16}
12 17
13namespace ConfigurationShared {
14enum class CheckState;
15}
16
17namespace Ui { 18namespace Ui {
18class ConfigureAudio; 19class ConfigureAudio;
19} 20}
20 21
21class ConfigureAudio : public QWidget { 22namespace ConfigurationShared {
22 Q_OBJECT 23class Builder;
24}
23 25
26class ConfigureAudio : public ConfigurationShared::Tab {
24public: 27public:
25 explicit ConfigureAudio(const Core::System& system_, QWidget* parent = nullptr); 28 explicit ConfigureAudio(const Core::System& system_,
29 std::shared_ptr<std::vector<ConfigurationShared::Tab*>> group,
30 const ConfigurationShared::Builder& builder, QWidget* parent = nullptr);
26 ~ConfigureAudio() override; 31 ~ConfigureAudio() override;
27 32
28 void ApplyConfiguration(); 33 void ApplyConfiguration() override;
29 void SetConfiguration(); 34 void SetConfiguration() override;
30 35
31private: 36private:
32 void changeEvent(QEvent* event) override; 37 void changeEvent(QEvent* event) override;
@@ -39,11 +44,16 @@ private:
39 44
40 void SetOutputSinkFromSinkID(); 45 void SetOutputSinkFromSinkID();
41 void SetAudioDevicesFromDeviceID(); 46 void SetAudioDevicesFromDeviceID();
42 void SetVolumeIndicatorText(int percentage);
43 47
44 void SetupPerGameUI(); 48 void Setup(const ConfigurationShared::Builder& builder);
45 49
46 std::unique_ptr<Ui::ConfigureAudio> ui; 50 std::unique_ptr<Ui::ConfigureAudio> ui;
47 51
48 const Core::System& system; 52 const Core::System& system;
53
54 std::vector<std::function<void(bool)>> apply_funcs{};
55
56 QComboBox* sink_combo_box;
57 QComboBox* output_device_combo_box;
58 QComboBox* input_device_combo_box;
49}; 59};
diff --git a/src/yuzu/configuration/configure_audio.ui b/src/yuzu/configuration/configure_audio.ui
index 4128c83ad..1181aeb00 100644
--- a/src/yuzu/configuration/configure_audio.ui
+++ b/src/yuzu/configuration/configure_audio.ui
@@ -21,80 +21,14 @@
21 </property> 21 </property>
22 <layout class="QVBoxLayout"> 22 <layout class="QVBoxLayout">
23 <item> 23 <item>
24 <layout class="QHBoxLayout" name="engine_layout"> 24 <widget class="QWidget" name="audio_widget" native="true">
25 <item> 25 <property name="maximumSize">
26 <widget class="QLabel" name="sink_label"> 26 <size>
27 <property name="text"> 27 <width>16777215</width>
28 <string>Output Engine:</string> 28 <height>16777213</height>
29 </property> 29 </size>
30 </widget> 30 </property>
31 </item> 31 <layout class="QVBoxLayout" name="verticalLayout">
32 <item>
33 <widget class="QComboBox" name="sink_combo_box"/>
34 </item>
35 </layout>
36 </item>
37 <item>
38 <layout class="QHBoxLayout" name="output_layout">
39 <item>
40 <widget class="QLabel" name="output_label">
41 <property name="text">
42 <string>Output Device:</string>
43 </property>
44 </widget>
45 </item>
46 <item>
47 <widget class="QComboBox" name="output_combo_box"/>
48 </item>
49 </layout>
50 </item>
51 <item>
52 <layout class="QHBoxLayout" name="input_layout">
53 <item>
54 <widget class="QLabel" name="input_label">
55 <property name="text">
56 <string>Input Device:</string>
57 </property>
58 </widget>
59 </item>
60 <item>
61 <widget class="QComboBox" name="input_combo_box"/>
62 </item>
63 </layout>
64 </item>
65 <item>
66 <layout class="QHBoxLayout" name="mode_layout">
67 <item>
68 <widget class="QLabel" name="mode_label">
69 <property name="text">
70 <string>Sound Output Mode:</string>
71 </property>
72 </widget>
73 </item>
74 <item>
75 <widget class="QComboBox" name="combo_sound">
76 <item>
77 <property name="text">
78 <string>Mono</string>
79 </property>
80 </item>
81 <item>
82 <property name="text">
83 <string>Stereo</string>
84 </property>
85 </item>
86 <item>
87 <property name="text">
88 <string>Surround</string>
89 </property>
90 </item>
91 </widget>
92 </item>
93 </layout>
94 </item>
95 <item>
96 <widget class="QWidget" name="volume_layout" native="true">
97 <layout class="QHBoxLayout" name="horizontalLayout_2">
98 <property name="leftMargin"> 32 <property name="leftMargin">
99 <number>0</number> 33 <number>0</number>
100 </property> 34 </property>
@@ -107,89 +41,9 @@
107 <property name="bottomMargin"> 41 <property name="bottomMargin">
108 <number>0</number> 42 <number>0</number>
109 </property> 43 </property>
110 <item>
111 <widget class="QComboBox" name="volume_combo_box">
112 <item>
113 <property name="text">
114 <string>Use global volume</string>
115 </property>
116 </item>
117 <item>
118 <property name="text">
119 <string>Set volume:</string>
120 </property>
121 </item>
122 </widget>
123 </item>
124 <item>
125 <widget class="QLabel" name="volume_label">
126 <property name="text">
127 <string>Volume:</string>
128 </property>
129 </widget>
130 </item>
131 <item>
132 <spacer name="horizontalSpacer">
133 <property name="orientation">
134 <enum>Qt::Horizontal</enum>
135 </property>
136 <property name="sizeHint" stdset="0">
137 <size>
138 <width>30</width>
139 <height>20</height>
140 </size>
141 </property>
142 </spacer>
143 </item>
144 <item>
145 <widget class="QSlider" name="volume_slider">
146 <property name="sizePolicy">
147 <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
148 <horstretch>0</horstretch>
149 <verstretch>0</verstretch>
150 </sizepolicy>
151 </property>
152 <property name="maximum">
153 <number>200</number>
154 </property>
155 <property name="pageStep">
156 <number>5</number>
157 </property>
158 <property name="orientation">
159 <enum>Qt::Horizontal</enum>
160 </property>
161 </widget>
162 </item>
163 <item>
164 <widget class="QLabel" name="volume_indicator">
165 <property name="minimumSize">
166 <size>
167 <width>32</width>
168 <height>0</height>
169 </size>
170 </property>
171 <property name="text">
172 <string>0 %</string>
173 </property>
174 <property name="alignment">
175 <set>Qt::AlignCenter</set>
176 </property>
177 </widget>
178 </item>
179 </layout> 44 </layout>
180 </widget> 45 </widget>
181 </item> 46 </item>
182 <item>
183 <layout class="QHBoxLayout" name="mute_layout">
184 <item>
185 <widget class="QCheckBox" name="toggle_background_mute">
186 <property name="text">
187 <string>Mute audio when in background</string>
188 </property>
189 </widget>
190 </item>
191 </layout>
192 </item>
193 </layout> 47 </layout>
194 </widget> 48 </widget>
195 </item> 49 </item>
diff --git a/src/yuzu/configuration/configure_cpu.cpp b/src/yuzu/configuration/configure_cpu.cpp
index 3d69fb03f..a51359903 100644
--- a/src/yuzu/configuration/configure_cpu.cpp
+++ b/src/yuzu/configuration/configure_cpu.cpp
@@ -1,88 +1,92 @@
1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <memory>
5#include <typeinfo>
6#include <vector>
7#include <QComboBox>
4#include "common/common_types.h" 8#include "common/common_types.h"
5#include "common/settings.h" 9#include "common/settings.h"
10#include "common/settings_enums.h"
11#include "configuration/shared_widget.h"
6#include "core/core.h" 12#include "core/core.h"
7#include "ui_configure_cpu.h" 13#include "ui_configure_cpu.h"
8#include "yuzu/configuration/configuration_shared.h" 14#include "yuzu/configuration/configuration_shared.h"
9#include "yuzu/configuration/configure_cpu.h" 15#include "yuzu/configuration/configure_cpu.h"
10 16
11ConfigureCpu::ConfigureCpu(const Core::System& system_, QWidget* parent) 17ConfigureCpu::ConfigureCpu(const Core::System& system_,
12 : QWidget(parent), ui{std::make_unique<Ui::ConfigureCpu>()}, system{system_} { 18 std::shared_ptr<std::vector<ConfigurationShared::Tab*>> group_,
19 const ConfigurationShared::Builder& builder, QWidget* parent)
20 : Tab(group_, parent), ui{std::make_unique<Ui::ConfigureCpu>()}, system{system_},
21 combobox_translations(builder.ComboboxTranslations()) {
13 ui->setupUi(this); 22 ui->setupUi(this);
14 23
15 SetupPerGameUI(); 24 Setup(builder);
16 25
17 SetConfiguration(); 26 SetConfiguration();
18 27
19 connect(ui->accuracy, qOverload<int>(&QComboBox::currentIndexChanged), this, 28 connect(accuracy_combobox, qOverload<int>(&QComboBox::currentIndexChanged), this,
20 &ConfigureCpu::UpdateGroup); 29 &ConfigureCpu::UpdateGroup);
21} 30}
22 31
23ConfigureCpu::~ConfigureCpu() = default; 32ConfigureCpu::~ConfigureCpu() = default;
24 33
25void ConfigureCpu::SetConfiguration() { 34void ConfigureCpu::SetConfiguration() {}
26 const bool runtime_lock = !system.IsPoweredOn(); 35void ConfigureCpu::Setup(const ConfigurationShared::Builder& builder) {
27 36 auto* accuracy_layout = ui->widget_accuracy->layout();
28 ui->accuracy->setEnabled(runtime_lock); 37 auto* unsafe_layout = ui->unsafe_widget->layout();
29 ui->cpuopt_unsafe_unfuse_fma->setEnabled(runtime_lock); 38 std::map<u32, QWidget*> unsafe_hold{};
30 ui->cpuopt_unsafe_reduce_fp_error->setEnabled(runtime_lock); 39
31 ui->cpuopt_unsafe_ignore_standard_fpcr->setEnabled(runtime_lock); 40 std::vector<Settings::BasicSetting*> settings;
32 ui->cpuopt_unsafe_inaccurate_nan->setEnabled(runtime_lock); 41 const auto push = [&](Settings::Category category) {
33 ui->cpuopt_unsafe_fastmem_check->setEnabled(runtime_lock); 42 for (const auto setting : Settings::values.linkage.by_category[category]) {
34 ui->cpuopt_unsafe_ignore_global_monitor->setEnabled(runtime_lock); 43 settings.push_back(setting);
35 44 }
36 ui->cpuopt_unsafe_unfuse_fma->setChecked(Settings::values.cpuopt_unsafe_unfuse_fma.GetValue()); 45 };
37 ui->cpuopt_unsafe_reduce_fp_error->setChecked( 46
38 Settings::values.cpuopt_unsafe_reduce_fp_error.GetValue()); 47 push(Settings::Category::Cpu);
39 ui->cpuopt_unsafe_ignore_standard_fpcr->setChecked( 48 push(Settings::Category::CpuUnsafe);
40 Settings::values.cpuopt_unsafe_ignore_standard_fpcr.GetValue()); 49
41 ui->cpuopt_unsafe_inaccurate_nan->setChecked( 50 for (const auto setting : settings) {
42 Settings::values.cpuopt_unsafe_inaccurate_nan.GetValue()); 51 auto* widget = builder.BuildWidget(setting, apply_funcs);
43 ui->cpuopt_unsafe_fastmem_check->setChecked( 52
44 Settings::values.cpuopt_unsafe_fastmem_check.GetValue()); 53 if (widget == nullptr) {
45 ui->cpuopt_unsafe_ignore_global_monitor->setChecked( 54 continue;
46 Settings::values.cpuopt_unsafe_ignore_global_monitor.GetValue()); 55 }
47 56 if (!widget->Valid()) {
48 if (Settings::IsConfiguringGlobal()) { 57 widget->deleteLater();
49 ui->accuracy->setCurrentIndex(static_cast<int>(Settings::values.cpu_accuracy.GetValue())); 58 continue;
50 } else { 59 }
51 ConfigurationShared::SetPerGameSetting(ui->accuracy, &Settings::values.cpu_accuracy); 60
52 ConfigurationShared::SetHighlight(ui->widget_accuracy, 61 if (setting->Id() == Settings::values.cpu_accuracy.Id()) {
53 !Settings::values.cpu_accuracy.UsingGlobal()); 62 // Keep track of cpu_accuracy combobox to display/hide the unsafe settings
63 accuracy_layout->addWidget(widget);
64 accuracy_combobox = widget->combobox;
65 } else {
66 // Presently, all other settings here are unsafe checkboxes
67 unsafe_hold.insert({setting->Id(), widget});
68 }
54 } 69 }
55 UpdateGroup(ui->accuracy->currentIndex()); 70
71 for (const auto& [label, widget] : unsafe_hold) {
72 unsafe_layout->addWidget(widget);
73 }
74
75 UpdateGroup(accuracy_combobox->currentIndex());
56} 76}
57 77
58void ConfigureCpu::UpdateGroup(int index) { 78void ConfigureCpu::UpdateGroup(int index) {
59 if (!Settings::IsConfiguringGlobal()) { 79 const auto accuracy = static_cast<Settings::CpuAccuracy>(
60 index -= ConfigurationShared::USE_GLOBAL_OFFSET; 80 combobox_translations.at(Settings::EnumMetadata<Settings::CpuAccuracy>::Index())[index]
61 } 81 .first);
62 const auto accuracy = static_cast<Settings::CPUAccuracy>(index); 82 ui->unsafe_group->setVisible(accuracy == Settings::CpuAccuracy::Unsafe);
63 ui->unsafe_group->setVisible(accuracy == Settings::CPUAccuracy::Unsafe);
64} 83}
65 84
66void ConfigureCpu::ApplyConfiguration() { 85void ConfigureCpu::ApplyConfiguration() {
67 ConfigurationShared::ApplyPerGameSetting(&Settings::values.cpu_accuracy, ui->accuracy); 86 const bool is_powered_on = system.IsPoweredOn();
68 ConfigurationShared::ApplyPerGameSetting(&Settings::values.cpuopt_unsafe_unfuse_fma, 87 for (const auto& apply_func : apply_funcs) {
69 ui->cpuopt_unsafe_unfuse_fma, 88 apply_func(is_powered_on);
70 cpuopt_unsafe_unfuse_fma); 89 }
71 ConfigurationShared::ApplyPerGameSetting(&Settings::values.cpuopt_unsafe_reduce_fp_error,
72 ui->cpuopt_unsafe_reduce_fp_error,
73 cpuopt_unsafe_reduce_fp_error);
74 ConfigurationShared::ApplyPerGameSetting(&Settings::values.cpuopt_unsafe_ignore_standard_fpcr,
75 ui->cpuopt_unsafe_ignore_standard_fpcr,
76 cpuopt_unsafe_ignore_standard_fpcr);
77 ConfigurationShared::ApplyPerGameSetting(&Settings::values.cpuopt_unsafe_inaccurate_nan,
78 ui->cpuopt_unsafe_inaccurate_nan,
79 cpuopt_unsafe_inaccurate_nan);
80 ConfigurationShared::ApplyPerGameSetting(&Settings::values.cpuopt_unsafe_fastmem_check,
81 ui->cpuopt_unsafe_fastmem_check,
82 cpuopt_unsafe_fastmem_check);
83 ConfigurationShared::ApplyPerGameSetting(&Settings::values.cpuopt_unsafe_ignore_global_monitor,
84 ui->cpuopt_unsafe_ignore_global_monitor,
85 cpuopt_unsafe_ignore_global_monitor);
86} 90}
87 91
88void ConfigureCpu::changeEvent(QEvent* event) { 92void ConfigureCpu::changeEvent(QEvent* event) {
@@ -96,32 +100,3 @@ void ConfigureCpu::changeEvent(QEvent* event) {
96void ConfigureCpu::RetranslateUI() { 100void ConfigureCpu::RetranslateUI() {
97 ui->retranslateUi(this); 101 ui->retranslateUi(this);
98} 102}
99
100void ConfigureCpu::SetupPerGameUI() {
101 if (Settings::IsConfiguringGlobal()) {
102 return;
103 }
104
105 ConfigurationShared::SetColoredComboBox(
106 ui->accuracy, ui->widget_accuracy,
107 static_cast<u32>(Settings::values.cpu_accuracy.GetValue(true)));
108
109 ConfigurationShared::SetColoredTristate(ui->cpuopt_unsafe_unfuse_fma,
110 Settings::values.cpuopt_unsafe_unfuse_fma,
111 cpuopt_unsafe_unfuse_fma);
112 ConfigurationShared::SetColoredTristate(ui->cpuopt_unsafe_reduce_fp_error,
113 Settings::values.cpuopt_unsafe_reduce_fp_error,
114 cpuopt_unsafe_reduce_fp_error);
115 ConfigurationShared::SetColoredTristate(ui->cpuopt_unsafe_ignore_standard_fpcr,
116 Settings::values.cpuopt_unsafe_ignore_standard_fpcr,
117 cpuopt_unsafe_ignore_standard_fpcr);
118 ConfigurationShared::SetColoredTristate(ui->cpuopt_unsafe_inaccurate_nan,
119 Settings::values.cpuopt_unsafe_inaccurate_nan,
120 cpuopt_unsafe_inaccurate_nan);
121 ConfigurationShared::SetColoredTristate(ui->cpuopt_unsafe_fastmem_check,
122 Settings::values.cpuopt_unsafe_fastmem_check,
123 cpuopt_unsafe_fastmem_check);
124 ConfigurationShared::SetColoredTristate(ui->cpuopt_unsafe_ignore_global_monitor,
125 Settings::values.cpuopt_unsafe_ignore_global_monitor,
126 cpuopt_unsafe_ignore_global_monitor);
127}
diff --git a/src/yuzu/configuration/configure_cpu.h b/src/yuzu/configuration/configure_cpu.h
index 86d928ca3..61a6de7aa 100644
--- a/src/yuzu/configuration/configure_cpu.h
+++ b/src/yuzu/configuration/configure_cpu.h
@@ -4,29 +4,34 @@
4#pragma once 4#pragma once
5 5
6#include <memory> 6#include <memory>
7#include <vector>
7#include <QWidget> 8#include <QWidget>
9#include "yuzu/configuration/configuration_shared.h"
10#include "yuzu/configuration/shared_translation.h"
11
12class QComboBox;
8 13
9namespace Core { 14namespace Core {
10class System; 15class System;
11} 16}
12 17
13namespace ConfigurationShared {
14enum class CheckState;
15}
16
17namespace Ui { 18namespace Ui {
18class ConfigureCpu; 19class ConfigureCpu;
19} 20}
20 21
21class ConfigureCpu : public QWidget { 22namespace ConfigurationShared {
22 Q_OBJECT 23class Builder;
24}
23 25
26class ConfigureCpu : public ConfigurationShared::Tab {
24public: 27public:
25 explicit ConfigureCpu(const Core::System& system_, QWidget* parent = nullptr); 28 explicit ConfigureCpu(const Core::System& system_,
29 std::shared_ptr<std::vector<ConfigurationShared::Tab*>> group,
30 const ConfigurationShared::Builder& builder, QWidget* parent = nullptr);
26 ~ConfigureCpu() override; 31 ~ConfigureCpu() override;
27 32
28 void ApplyConfiguration(); 33 void ApplyConfiguration() override;
29 void SetConfiguration(); 34 void SetConfiguration() override;
30 35
31private: 36private:
32 void changeEvent(QEvent* event) override; 37 void changeEvent(QEvent* event) override;
@@ -34,16 +39,14 @@ private:
34 39
35 void UpdateGroup(int index); 40 void UpdateGroup(int index);
36 41
37 void SetupPerGameUI(); 42 void Setup(const ConfigurationShared::Builder& builder);
38 43
39 std::unique_ptr<Ui::ConfigureCpu> ui; 44 std::unique_ptr<Ui::ConfigureCpu> ui;
40 45
41 ConfigurationShared::CheckState cpuopt_unsafe_unfuse_fma;
42 ConfigurationShared::CheckState cpuopt_unsafe_reduce_fp_error;
43 ConfigurationShared::CheckState cpuopt_unsafe_ignore_standard_fpcr;
44 ConfigurationShared::CheckState cpuopt_unsafe_inaccurate_nan;
45 ConfigurationShared::CheckState cpuopt_unsafe_fastmem_check;
46 ConfigurationShared::CheckState cpuopt_unsafe_ignore_global_monitor;
47
48 const Core::System& system; 46 const Core::System& system;
47
48 const ConfigurationShared::ComboboxTranslationMap& combobox_translations;
49 std::vector<std::function<void(bool)>> apply_funcs{};
50
51 QComboBox* accuracy_combobox;
49}; 52};
diff --git a/src/yuzu/configuration/configure_cpu.ui b/src/yuzu/configuration/configure_cpu.ui
index 8ae569ee6..f734e842e 100644
--- a/src/yuzu/configuration/configure_cpu.ui
+++ b/src/yuzu/configuration/configure_cpu.ui
@@ -16,9 +16,12 @@
16 <property name="accessibleName"> 16 <property name="accessibleName">
17 <string>CPU</string> 17 <string>CPU</string>
18 </property> 18 </property>
19 <layout class="QVBoxLayout"> 19 <layout class="QVBoxLayout" name="vboxlayout_2" stretch="0">
20 <item> 20 <item>
21 <layout class="QVBoxLayout"> 21 <layout class="QVBoxLayout" name="vboxlayout">
22 <property name="bottomMargin">
23 <number>0</number>
24 </property>
22 <item> 25 <item>
23 <widget class="QGroupBox" name="groupBox"> 26 <widget class="QGroupBox" name="groupBox">
24 <property name="title"> 27 <property name="title">
@@ -27,38 +30,19 @@
27 <layout class="QVBoxLayout"> 30 <layout class="QVBoxLayout">
28 <item> 31 <item>
29 <widget class="QWidget" name="widget_accuracy" native="true"> 32 <widget class="QWidget" name="widget_accuracy" native="true">
30 <layout class="QHBoxLayout" name="layout_accuracy"> 33 <layout class="QVBoxLayout" name="verticalLayout">
31 <item> 34 <property name="leftMargin">
32 <widget class="QLabel" name="label_accuracy"> 35 <number>0</number>
33 <property name="text"> 36 </property>
34 <string>Accuracy:</string> 37 <property name="topMargin">
35 </property> 38 <number>0</number>
36 </widget> 39 </property>
37 </item> 40 <property name="rightMargin">
38 <item> 41 <number>0</number>
39 <widget class="QComboBox" name="accuracy"> 42 </property>
40 <item> 43 <property name="bottomMargin">
41 <property name="text"> 44 <number>0</number>
42 <string>Auto</string> 45 </property>
43 </property>
44 </item>
45 <item>
46 <property name="text">
47 <string>Accurate</string>
48 </property>
49 </item>
50 <item>
51 <property name="text">
52 <string>Unsafe</string>
53 </property>
54 </item>
55 <item>
56 <property name="text">
57 <string>Paranoid (disables most optimizations)</string>
58 </property>
59 </item>
60 </widget>
61 </item>
62 </layout> 46 </layout>
63 </widget> 47 </widget>
64 </item> 48 </item>
@@ -75,10 +59,6 @@
75 </layout> 59 </layout>
76 </widget> 60 </widget>
77 </item> 61 </item>
78 </layout>
79 </item>
80 <item>
81 <layout class="QVBoxLayout">
82 <item> 62 <item>
83 <widget class="QGroupBox" name="unsafe_group"> 63 <widget class="QGroupBox" name="unsafe_group">
84 <property name="title"> 64 <property name="title">
@@ -96,105 +76,44 @@
96 </widget> 76 </widget>
97 </item> 77 </item>
98 <item> 78 <item>
99 <widget class="QCheckBox" name="cpuopt_unsafe_unfuse_fma"> 79 <widget class="QWidget" name="unsafe_widget" native="true">
100 <property name="toolTip"> 80 <layout class="QVBoxLayout" name="unsafe_layout">
101 <string> 81 <property name="leftMargin">
102 &lt;div&gt;This option improves speed by reducing accuracy of fused-multiply-add instructions on CPUs without native FMA support.&lt;/div&gt; 82 <number>0</number>
103 </string> 83 </property>
104 </property> 84 <property name="topMargin">
105 <property name="text"> 85 <number>0</number>
106 <string>Unfuse FMA (improve performance on CPUs without FMA)</string> 86 </property>
107 </property> 87 <property name="rightMargin">
108 </widget> 88 <number>0</number>
109 </item> 89 </property>
110 <item> 90 <property name="bottomMargin">
111 <widget class="QCheckBox" name="cpuopt_unsafe_reduce_fp_error"> 91 <number>0</number>
112 <property name="toolTip"> 92 </property>
113 <string> 93 </layout>
114 &lt;div&gt;This option improves the speed of some approximate floating-point functions by using less accurate native approximations.&lt;/div&gt;
115 </string>
116 </property>
117 <property name="text">
118 <string>Faster FRSQRTE and FRECPE</string>
119 </property>
120 </widget>
121 </item>
122 <item>
123 <widget class="QCheckBox" name="cpuopt_unsafe_ignore_standard_fpcr">
124 <property name="toolTip">
125 <string>
126 &lt;div&gt;This option improves the speed of 32 bits ASIMD floating-point functions by running with incorrect rounding modes.&lt;/div&gt;
127 </string>
128 </property>
129 <property name="text">
130 <string>Faster ASIMD instructions (32 bits only)</string>
131 </property>
132 </widget>
133 </item>
134 <item>
135 <widget class="QCheckBox" name="cpuopt_unsafe_inaccurate_nan">
136 <property name="toolTip">
137 <string>
138 &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;
139 </string>
140 </property>
141 <property name="text">
142 <string>Inaccurate NaN handling</string>
143 </property>
144 </widget>
145 </item>
146 <item>
147 <widget class="QCheckBox" name="cpuopt_unsafe_fastmem_check">
148 <property name="toolTip">
149 <string>
150 &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;
151 </string>
152 </property>
153 <property name="text">
154 <string>Disable address space checks</string>
155 </property>
156 </widget>
157 </item>
158 <item>
159 <widget class="QCheckBox" name="cpuopt_unsafe_ignore_global_monitor">
160 <property name="toolTip">
161 <string>
162 &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;
163 </string>
164 </property>
165 <property name="text">
166 <string>Ignore global monitor</string>
167 </property>
168 </widget> 94 </widget>
169 </item> 95 </item>
170 </layout> 96 </layout>
171 </widget> 97 </widget>
172 </item> 98 </item>
99 <item>
100 <spacer name="verticalSpacer">
101 <property name="orientation">
102 <enum>Qt::Vertical</enum>
103 </property>
104 <property name="sizeType">
105 <enum>QSizePolicy::Expanding</enum>
106 </property>
107 <property name="sizeHint" stdset="0">
108 <size>
109 <width>20</width>
110 <height>40</height>
111 </size>
112 </property>
113 </spacer>
114 </item>
173 </layout> 115 </layout>
174 </item> 116 </item>
175 <item>
176 <spacer name="verticalSpacer">
177 <property name="orientation">
178 <enum>Qt::Vertical</enum>
179 </property>
180 <property name="sizeHint" stdset="0">
181 <size>
182 <width>20</width>
183 <height>40</height>
184 </size>
185 </property>
186 </spacer>
187 </item>
188 <item>
189 <widget class="QLabel" name="label_disable_info">
190 <property name="text">
191 <string>CPU settings are available only when game is not running.</string>
192 </property>
193 <property name="wordWrap">
194 <bool>true</bool>
195 </property>
196 </widget>
197 </item>
198 </layout> 117 </layout>
199 </widget> 118 </widget>
200 <resources/> 119 <resources/>
diff --git a/src/yuzu/configuration/configure_debug.ui b/src/yuzu/configuration/configure_debug.ui
index 15acefe33..97c7d9022 100644
--- a/src/yuzu/configuration/configure_debug.ui
+++ b/src/yuzu/configuration/configure_debug.ui
@@ -2,360 +2,549 @@
2<ui version="4.0"> 2<ui version="4.0">
3 <class>ConfigureDebug</class> 3 <class>ConfigureDebug</class>
4 <widget class="QScrollArea" name="ConfigureDebug"> 4 <widget class="QScrollArea" name="ConfigureDebug">
5 <property name="geometry">
6 <rect>
7 <x>0</x>
8 <y>0</y>
9 <width>831</width>
10 <height>760</height>
11 </rect>
12 </property>
5 <property name="widgetResizable"> 13 <property name="widgetResizable">
6 <bool>true</bool> 14 <bool>true</bool>
7 </property> 15 </property>
8 <widget class="QWidget"> 16 <widget class="QWidget" name="widget">
9 <layout class="QVBoxLayout" name="verticalLayout_1"> 17 <property name="geometry">
10 <item> 18 <rect>
11 <layout class="QVBoxLayout" name="verticalLayout_2"> 19 <x>0</x>
12 <item> 20 <y>0</y>
13 <widget class="QGroupBox" name="groupBox"> 21 <width>829</width>
14 <property name="title"> 22 <height>758</height>
15 <string>Debugger</string> 23 </rect>
16 </property> 24 </property>
17 <layout class="QVBoxLayout" name="verticalLayout_3"> 25 <layout class="QVBoxLayout" name="verticalLayout_1">
18 <item> 26 <item>
19 <layout class="QHBoxLayout" name="horizontalLayout_11"> 27 <layout class="QHBoxLayout" name="horizontalLayout">
20 <item> 28 <property name="topMargin">
21 <widget class="QCheckBox" name="toggle_gdbstub"> 29 <number>0</number>
22 <property name="text"> 30 </property>
23 <string>Enable GDB Stub</string> 31 <item>
24 </property> 32 <widget class="QGroupBox" name="groupBox">
25 </widget> 33 <property name="sizePolicy">
26 </item> 34 <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
27 <item> 35 <horstretch>0</horstretch>
28 <spacer name="horizontalSpacer"> 36 <verstretch>0</verstretch>
29 <property name="orientation"> 37 </sizepolicy>
30 <enum>Qt::Horizontal</enum> 38 </property>
39 <property name="title">
40 <string>Debugger</string>
41 </property>
42 <property name="alignment">
43 <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
44 </property>
45 <property name="flat">
46 <bool>false</bool>
47 </property>
48 <property name="checkable">
49 <bool>false</bool>
50 </property>
51 <layout class="QVBoxLayout" name="verticalLayout_3">
52 <item>
53 <widget class="QWidget" name="debug_widget" native="true">
54 <property name="sizePolicy">
55 <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
56 <horstretch>0</horstretch>
57 <verstretch>0</verstretch>
58 </sizepolicy>
59 </property>
60 <layout class="QVBoxLayout" name="verticalLayout">
61 <property name="sizeConstraint">
62 <enum>QLayout::SetDefaultConstraint</enum>
31 </property> 63 </property>
32 <property name="sizeHint" stdset="0"> 64 <property name="leftMargin">
33 <size> 65 <number>0</number>
34 <width>40</width>
35 <height>20</height>
36 </size>
37 </property> 66 </property>
38 </spacer> 67 <property name="topMargin">
39 </item> 68 <number>0</number>
40 <item>
41 <widget class="QLabel" name="label_11">
42 <property name="text">
43 <string>Port:</string>
44 </property> 69 </property>
45 </widget> 70 <property name="rightMargin">
46 </item> 71 <number>0</number>
47 <item>
48 <widget class="QSpinBox" name="gdbport_spinbox">
49 <property name="minimum">
50 <number>1024</number>
51 </property> 72 </property>
52 <property name="maximum"> 73 <property name="bottomMargin">
53 <number>65535</number> 74 <number>0</number>
54 </property> 75 </property>
55 </widget> 76 <item>
56 </item> 77 <widget class="QCheckBox" name="toggle_gdbstub">
57 </layout> 78 <property name="sizePolicy">
58 </item> 79 <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
59 </layout> 80 <horstretch>0</horstretch>
60 </widget> 81 <verstretch>0</verstretch>
61 </item> 82 </sizepolicy>
62 </layout> 83 </property>
63 </item> 84 <property name="text">
64 <item> 85 <string>Enable GDB Stub</string>
65 <widget class="QGroupBox" name="groupBox_2"> 86 </property>
66 <property name="title"> 87 </widget>
67 <string>Logging</string> 88 </item>
68 </property> 89 <item>
69 <layout class="QGridLayout" name="gridLayout_1"> 90 <widget class="QWidget" name="horizontalWidget_3" native="true">
70 <item row="0" column="0" colspan="2"> 91 <property name="sizePolicy">
71 <layout class="QHBoxLayout" name="horizontalLayout_1"> 92 <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
72 <item> 93 <horstretch>0</horstretch>
73 <widget class="QLabel" name="label_1"> 94 <verstretch>0</verstretch>
74 <property name="text"> 95 </sizepolicy>
75 <string>Global Log Filter</string> 96 </property>
76 </property> 97 <layout class="QHBoxLayout" name="horizontalLayout_3">
77 </widget> 98 <property name="leftMargin">
78 </item> 99 <number>0</number>
79 <item> 100 </property>
80 <widget class="QLineEdit" name="log_filter_edit"/> 101 <property name="topMargin">
81 </item> 102 <number>0</number>
82 </layout> 103 </property>
83 </item> 104 <property name="rightMargin">
84 <item row="1" column="0"> 105 <number>0</number>
85 <widget class="QCheckBox" name="toggle_console"> 106 </property>
86 <property name="text"> 107 <property name="bottomMargin">
87 <string>Show Log in Console</string> 108 <number>0</number>
88 </property> 109 </property>
89 </widget> 110 <item>
90 </item> 111 <widget class="QLabel" name="label_11">
91 <item row="1" column="1"> 112 <property name="sizePolicy">
92 <widget class="QPushButton" name="open_log_button"> 113 <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
93 <property name="text"> 114 <horstretch>0</horstretch>
94 <string>Open Log Location</string> 115 <verstretch>0</verstretch>
95 </property> 116 </sizepolicy>
117 </property>
118 <property name="text">
119 <string>Port:</string>
120 </property>
121 </widget>
122 </item>
123 <item>
124 <widget class="QSpinBox" name="gdbport_spinbox">
125 <property name="sizePolicy">
126 <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
127 <horstretch>0</horstretch>
128 <verstretch>0</verstretch>
129 </sizepolicy>
130 </property>
131 <property name="minimum">
132 <number>1024</number>
133 </property>
134 <property name="maximum">
135 <number>65535</number>
136 </property>
137 </widget>
138 </item>
139 </layout>
140 </widget>
141 </item>
142 </layout>
143 </widget>
144 </item>
145 </layout>
96 </widget> 146 </widget>
97 </item> 147 </item>
98 <item row="2" column="0">
99 <widget class="QCheckBox" name="extended_logging">
100 <property name="enabled">
101 <bool>true</bool>
102 </property>
103 <property name="toolTip">
104 <string>When checked, the max size of the log increases from 100 MB to 1 GB</string>
105 </property>
106 <property name="text">
107 <string>Enable Extended Logging**</string>
108 </property>
109 </widget>
110 </item>
111 </layout>
112 </widget>
113 </item>
114 <item>
115 <widget class="QGroupBox" name="groupBox_3">
116 <property name="title">
117 <string>Homebrew</string>
118 </property>
119 <layout class="QVBoxLayout" name="verticalLayout_5">
120 <item> 148 <item>
121 <layout class="QHBoxLayout" name="horizontalLayout_4"> 149 <widget class="QGroupBox" name="groupBox_2">
122 <item> 150 <property name="sizePolicy">
123 <widget class="QLabel" name="label_3"> 151 <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
124 <property name="text"> 152 <horstretch>0</horstretch>
125 <string>Arguments String</string> 153 <verstretch>0</verstretch>
126 </property> 154 </sizepolicy>
127 </widget> 155 </property>
128 </item> 156 <property name="title">
129 <item> 157 <string>Logging</string>
130 <widget class="QLineEdit" name="homebrew_args_edit"/> 158 </property>
131 </item> 159 <layout class="QGridLayout" name="gridLayout_1">
132 </layout> 160 <item row="1" column="1">
133 </item> 161 <widget class="QPushButton" name="open_log_button">
134 </layout> 162 <property name="text">
135 </widget> 163 <string>Open Log Location</string>
136 </item> 164 </property>
137 <item> 165 </widget>
138 <widget class="QGroupBox" name="groupBox_4"> 166 </item>
139 <property name="title"> 167 <item row="0" column="0" colspan="2">
140 <string>Graphics</string> 168 <widget class="QWidget" name="logging_widget" native="true">
141 </property> 169 <property name="sizePolicy">
142 <layout class="QGridLayout" name="gridLayout_2"> 170 <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
143 <item row="0" column="0"> 171 <horstretch>0</horstretch>
144 <widget class="QCheckBox" name="enable_graphics_debugging"> 172 <verstretch>0</verstretch>
145 <property name="enabled"> 173 </sizepolicy>
146 <bool>true</bool> 174 </property>
147 </property> 175 <layout class="QHBoxLayout" name="horizontalLayout_1">
148 <property name="toolTip"> 176 <property name="leftMargin">
149 <string>When checked, the graphics API enters a slower debugging mode</string> 177 <number>0</number>
150 </property> 178 </property>
151 <property name="text"> 179 <property name="topMargin">
152 <string>Enable Graphics Debugging</string> 180 <number>0</number>
153 </property> 181 </property>
154 </widget> 182 <property name="rightMargin">
155 </item> 183 <number>0</number>
156 <item row="2" column="0"> 184 </property>
157 <widget class="QCheckBox" name="enable_nsight_aftermath"> 185 <property name="bottomMargin">
158 <property name="toolTip"> 186 <number>0</number>
159 <string>When checked, it enables Nsight Aftermath crash dumps</string> 187 </property>
160 </property> 188 <item>
161 <property name="text"> 189 <widget class="QLabel" name="label_1">
162 <string>Enable Nsight Aftermath</string> 190 <property name="sizePolicy">
163 </property> 191 <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
164 </widget> 192 <horstretch>0</horstretch>
165 </item> 193 <verstretch>0</verstretch>
166 <item row="2" column="1"> 194 </sizepolicy>
167 <widget class="QCheckBox" name="dump_shaders"> 195 </property>
168 <property name="enabled"> 196 <property name="text">
169 <bool>true</bool> 197 <string>Global Log Filter</string>
170 </property> 198 </property>
171 <property name="toolTip"> 199 </widget>
172 <string>When checked, it will dump all the original assembler shaders from the disk shader cache or game as found</string> 200 </item>
173 </property> 201 <item>
174 <property name="text"> 202 <widget class="QLineEdit" name="log_filter_edit"/>
175 <string>Dump Game Shaders</string> 203 </item>
176 </property> 204 </layout>
177 </widget> 205 </widget>
178 </item> 206 </item>
179 <item row="1" column="2"> 207 <item row="2" column="0">
180 <widget class="QCheckBox" name="dump_macros"> 208 <widget class="QCheckBox" name="extended_logging">
181 <property name="enabled"> 209 <property name="enabled">
182 <bool>true</bool> 210 <bool>true</bool>
183 </property> 211 </property>
184 <property name="toolTip"> 212 <property name="toolTip">
185 <string>When checked, it will dump all the macro programs of the GPU</string> 213 <string>When checked, the max size of the log increases from 100 MB to 1 GB</string>
186 </property> 214 </property>
187 <property name="text"> 215 <property name="text">
188 <string>Dump Maxwell Macros</string> 216 <string>Enable Extended Logging**</string>
189 </property> 217 </property>
190 </widget> 218 </widget>
191 </item> 219 </item>
192 <item row="0" column="1"> 220 <item row="1" column="0">
193 <widget class="QCheckBox" name="disable_macro_jit"> 221 <widget class="QCheckBox" name="toggle_console">
194 <property name="enabled"> 222 <property name="text">
195 <bool>true</bool> 223 <string>Show Log in Console</string>
196 </property> 224 </property>
197 <property name="toolTip"> 225 </widget>
198 <string>When checked, it disables the macro Just In Time compiler. Enabling this makes games run slower</string> 226 </item>
199 </property> 227 </layout>
200 <property name="text">
201 <string>Disable Macro JIT</string>
202 </property>
203 </widget>
204 </item>
205 <item row="0" column="2">
206 <widget class="QCheckBox" name="disable_macro_hle">
207 <property name="enabled">
208 <bool>true</bool>
209 </property>
210 <property name="toolTip">
211 <string>When checked, it disables the macro HLE functions. Enabling this makes games run slower</string>
212 </property>
213 <property name="text">
214 <string>Disable Macro HLE</string>
215 </property>
216 </widget>
217 </item>
218 <item row="1" column="0">
219 <widget class="QCheckBox" name="enable_shader_feedback">
220 <property name="toolTip">
221 <string>When checked, yuzu will log statistics about the compiled pipeline cache</string>
222 </property>
223 <property name="text">
224 <string>Enable Shader Feedback</string>
225 </property>
226 </widget>
227 </item>
228 <item row="1" column="1">
229 <widget class="QCheckBox" name="disable_loop_safety_checks">
230 <property name="toolTip">
231 <string>When checked, it executes shaders without loop logic changes</string>
232 </property>
233 <property name="text">
234 <string>Disable Loop safety checks</string>
235 </property>
236 </widget>
237 </item>
238 </layout>
239 </widget>
240 </item>
241 <item>
242 <widget class="QGroupBox" name="groupBox_5">
243 <property name="title">
244 <string>Debugging</string>
245 </property>
246 <layout class="QGridLayout" name="gridLayout_3">
247 <item row="2" column="0">
248 <widget class="QCheckBox" name="reporting_services">
249 <property name="text">
250 <string>Enable Verbose Reporting Services**</string>
251 </property>
252 </widget>
253 </item>
254 <item row="0" column="0">
255 <widget class="QCheckBox" name="fs_access_log">
256 <property name="text">
257 <string>Enable FS Access Log</string>
258 </property>
259 </widget>
260 </item>
261 <item row="0" column="1">
262 <widget class="QCheckBox" name="dump_audio_commands">
263 <property name="toolTip">
264 <string>Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer.</string>
265 </property>
266 <property name="text">
267 <string>Dump Audio Commands To Console**</string>
268 </property>
269 </widget>
270 </item>
271 <item row="2" column="1">
272 <widget class="QCheckBox" name="create_crash_dumps">
273 <property name="text">
274 <string>Create Minidump After Crash</string>
275 </property>
276 </widget> 228 </widget>
277 </item> 229 </item>
278 </layout> 230 </layout>
279 </widget> 231 </item>
280 </item> 232 <item>
281 <item> 233 <widget class="QGroupBox" name="groupBox_3">
282 <widget class="QGroupBox" name="groupBox_6"> 234 <property name="title">
283 <property name="title"> 235 <string>Homebrew</string>
284 <string>Advanced</string> 236 </property>
285 </property> 237 <layout class="QVBoxLayout" name="verticalLayout_5">
286 <layout class="QGridLayout" name="gridLayout_4"> 238 <item>
287 <item row="0" column="0"> 239 <layout class="QHBoxLayout" name="horizontalLayout_4">
288 <widget class="QCheckBox" name="quest_flag"> 240 <item>
289 <property name="text"> 241 <widget class="QLabel" name="label_3">
290 <string>Kiosk (Quest) Mode</string> 242 <property name="text">
291 </property> 243 <string>Arguments String</string>
292 </widget> 244 </property>
293 </item> 245 </widget>
294 <item row="1" column="0"> 246 </item>
295 <widget class="QCheckBox" name="enable_cpu_debugging"> 247 <item>
296 <property name="text"> 248 <widget class="QLineEdit" name="homebrew_args_edit"/>
297 <string>Enable CPU Debugging</string> 249 </item>
298 </property> 250 </layout>
299 </widget> 251 </item>
300 </item> 252 </layout>
301 <item row="2" column="0"> 253 </widget>
302 <widget class="QCheckBox" name="use_debug_asserts"> 254 </item>
303 <property name="text"> 255 <item>
304 <string>Enable Debug Asserts</string> 256 <layout class="QHBoxLayout" name="horizontalLayout_2">
305 </property> 257 <item>
306 </widget> 258 <widget class="QGroupBox" name="groupBox_4">
307 </item> 259 <property name="title">
308 <item row="0" column="1"> 260 <string>Graphics</string>
309 <widget class="QCheckBox" name="use_auto_stub"> 261 </property>
310 <property name="text"> 262 <layout class="QGridLayout" name="gridLayout_2">
311 <string>Enable Auto-Stub**</string> 263 <item row="3" column="0">
312 </property> 264 <widget class="QCheckBox" name="disable_loop_safety_checks">
313 </widget> 265 <property name="toolTip">
314 </item> 266 <string>When checked, it executes shaders without loop logic changes</string>
315 <item row="1" column="1"> 267 </property>
316 <widget class="QCheckBox" name="enable_all_controllers"> 268 <property name="text">
317 <property name="text"> 269 <string>Disable Loop safety checks</string>
318 <string>Enable All Controller Types</string> 270 </property>
319 </property> 271 </widget>
272 </item>
273 <item row="4" column="0">
274 <widget class="QCheckBox" name="dump_shaders">
275 <property name="enabled">
276 <bool>true</bool>
277 </property>
278 <property name="toolTip">
279 <string>When checked, it will dump all the original assembler shaders from the disk shader cache or game as found</string>
280 </property>
281 <property name="text">
282 <string>Dump Game Shaders</string>
283 </property>
284 </widget>
285 </item>
286 <item row="7" column="0">
287 <widget class="QCheckBox" name="disable_macro_hle">
288 <property name="enabled">
289 <bool>true</bool>
290 </property>
291 <property name="toolTip">
292 <string>When checked, it disables the macro HLE functions. Enabling this makes games run slower</string>
293 </property>
294 <property name="text">
295 <string>Disable Macro HLE</string>
296 </property>
297 </widget>
298 </item>
299 <item row="5" column="0">
300 <widget class="QCheckBox" name="disable_macro_jit">
301 <property name="enabled">
302 <bool>true</bool>
303 </property>
304 <property name="toolTip">
305 <string>When checked, it disables the macro Just In Time compiler. Enabling this makes games run slower</string>
306 </property>
307 <property name="text">
308 <string>Disable Macro JIT</string>
309 </property>
310 </widget>
311 </item>
312 <item row="0" column="0">
313 <widget class="QCheckBox" name="enable_graphics_debugging">
314 <property name="enabled">
315 <bool>true</bool>
316 </property>
317 <property name="toolTip">
318 <string>When checked, the graphics API enters a slower debugging mode</string>
319 </property>
320 <property name="text">
321 <string>Enable Graphics Debugging</string>
322 </property>
323 </widget>
324 </item>
325 <item row="6" column="0">
326 <widget class="QCheckBox" name="dump_macros">
327 <property name="enabled">
328 <bool>true</bool>
329 </property>
330 <property name="toolTip">
331 <string>When checked, it will dump all the macro programs of the GPU</string>
332 </property>
333 <property name="text">
334 <string>Dump Maxwell Macros</string>
335 </property>
336 </widget>
337 </item>
338 <item row="1" column="0">
339 <widget class="QCheckBox" name="enable_shader_feedback">
340 <property name="toolTip">
341 <string>When checked, yuzu will log statistics about the compiled pipeline cache</string>
342 </property>
343 <property name="text">
344 <string>Enable Shader Feedback</string>
345 </property>
346 </widget>
347 </item>
348 <item row="2" column="0">
349 <widget class="QCheckBox" name="enable_nsight_aftermath">
350 <property name="toolTip">
351 <string>When checked, it enables Nsight Aftermath crash dumps</string>
352 </property>
353 <property name="text">
354 <string>Enable Nsight Aftermath</string>
355 </property>
356 </widget>
357 </item>
358 <item row="8" column="0">
359 <spacer name="verticalSpacer_5">
360 <property name="orientation">
361 <enum>Qt::Vertical</enum>
362 </property>
363 <property name="sizeType">
364 <enum>QSizePolicy::Preferred</enum>
365 </property>
366 <property name="sizeHint" stdset="0">
367 <size>
368 <width>20</width>
369 <height>0</height>
370 </size>
371 </property>
372 </spacer>
373 </item>
374 </layout>
320 </widget> 375 </widget>
321 </item> 376 </item>
322 <item row="2" column="1"> 377 <item>
323 <widget class="QCheckBox" name="disable_web_applet"> 378 <widget class="QGroupBox" name="groupBox_6">
324 <property name="text"> 379 <property name="title">
325 <string>Disable Web Applet</string> 380 <string>Advanced</string>
326 </property> 381 </property>
382 <layout class="QGridLayout" name="gridLayout_4">
383 <item row="3" column="0">
384 <widget class="QCheckBox" name="perform_vulkan_check">
385 <property name="toolTip">
386 <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>
387 </property>
388 <property name="text">
389 <string>Perform Startup Vulkan Check</string>
390 </property>
391 </widget>
392 </item>
393 <item row="4" column="0">
394 <widget class="QCheckBox" name="disable_web_applet">
395 <property name="text">
396 <string>Disable Web Applet</string>
397 </property>
398 </widget>
399 </item>
400 <item row="5" column="0">
401 <widget class="QCheckBox" name="enable_all_controllers">
402 <property name="text">
403 <string>Enable All Controller Types</string>
404 </property>
405 </widget>
406 </item>
407 <item row="6" column="0">
408 <widget class="QCheckBox" name="use_auto_stub">
409 <property name="text">
410 <string>Enable Auto-Stub**</string>
411 </property>
412 </widget>
413 </item>
414 <item row="0" column="0">
415 <widget class="QCheckBox" name="quest_flag">
416 <property name="text">
417 <string>Kiosk (Quest) Mode</string>
418 </property>
419 </widget>
420 </item>
421 <item row="1" column="0">
422 <widget class="QCheckBox" name="enable_cpu_debugging">
423 <property name="text">
424 <string>Enable CPU Debugging</string>
425 </property>
426 </widget>
427 </item>
428 <item row="2" column="0">
429 <widget class="QCheckBox" name="use_debug_asserts">
430 <property name="text">
431 <string>Enable Debug Asserts</string>
432 </property>
433 </widget>
434 </item>
435 <item row="7" column="0">
436 <spacer name="verticalSpacer_4">
437 <property name="orientation">
438 <enum>Qt::Vertical</enum>
439 </property>
440 <property name="sizeType">
441 <enum>QSizePolicy::Expanding</enum>
442 </property>
443 <property name="sizeHint" stdset="0">
444 <size>
445 <width>20</width>
446 <height>0</height>
447 </size>
448 </property>
449 </spacer>
450 </item>
451 </layout>
327 </widget> 452 </widget>
328 </item> 453 </item>
329 <item row="3" column="0"> 454 <item>
330 <widget class="QCheckBox" name="perform_vulkan_check"> 455 <widget class="QGroupBox" name="groupBox_5">
331 <property name="toolTip"> 456 <property name="title">
332 <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> 457 <string>Debugging</string>
333 </property> 458 </property>
334 <property name="text"> 459 <layout class="QGridLayout" name="gridLayout_3">
335 <string>Perform Startup Vulkan Check</string> 460 <item row="0" column="0">
336 </property> 461 <widget class="QCheckBox" name="fs_access_log">
462 <property name="text">
463 <string>Enable FS Access Log</string>
464 </property>
465 </widget>
466 </item>
467 <item row="4" column="0">
468 <widget class="QCheckBox" name="create_crash_dumps">
469 <property name="text">
470 <string>Create Minidump After Crash</string>
471 </property>
472 </widget>
473 </item>
474 <item row="3" column="0">
475 <widget class="QCheckBox" name="dump_audio_commands">
476 <property name="toolTip">
477 <string>Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer.</string>
478 </property>
479 <property name="text">
480 <string>Dump Audio Commands To Console**</string>
481 </property>
482 </widget>
483 </item>
484 <item row="2" column="0">
485 <widget class="QCheckBox" name="reporting_services">
486 <property name="text">
487 <string>Enable Verbose Reporting Services**</string>
488 </property>
489 </widget>
490 </item>
491 <item row="5" column="0">
492 <spacer name="verticalSpacer_3">
493 <property name="orientation">
494 <enum>Qt::Vertical</enum>
495 </property>
496 <property name="sizeType">
497 <enum>QSizePolicy::Expanding</enum>
498 </property>
499 <property name="sizeHint" stdset="0">
500 <size>
501 <width>20</width>
502 <height>0</height>
503 </size>
504 </property>
505 </spacer>
506 </item>
507 </layout>
337 </widget> 508 </widget>
338 </item> 509 </item>
339 </layout> 510 </layout>
340 </widget> 511 </item>
341 </item> 512 <item>
342 <item> 513 <spacer name="verticalSpacer">
343 <widget class="QLabel" name="label_5"> 514 <property name="orientation">
344 <property name="font"> 515 <enum>Qt::Vertical</enum>
345 <font> 516 </property>
346 <italic>true</italic> 517 <property name="sizeHint" stdset="0">
347 </font> 518 <size>
348 </property> 519 <width>20</width>
349 <property name="text"> 520 <height>0</height>
350 <string>**This will be reset automatically when yuzu closes.</string> 521 </size>
351 </property> 522 </property>
352 <property name="indent"> 523 </spacer>
353 <number>20</number> 524 </item>
354 </property> 525 <item>
355 </widget> 526 <widget class="QLabel" name="label_5">
356 </item> 527 <property name="sizePolicy">
357 </layout> 528 <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
358 </widget> 529 <horstretch>0</horstretch>
530 <verstretch>0</verstretch>
531 </sizepolicy>
532 </property>
533 <property name="font">
534 <font>
535 <italic>true</italic>
536 </font>
537 </property>
538 <property name="text">
539 <string>**This will be reset automatically when yuzu closes.</string>
540 </property>
541 <property name="indent">
542 <number>20</number>
543 </property>
544 </widget>
545 </item>
546 </layout>
547 </widget>
359 </widget> 548 </widget>
360 <tabstops> 549 <tabstops>
361 <tabstop>log_filter_edit</tabstop> 550 <tabstop>log_filter_edit</tabstop>
@@ -366,14 +555,11 @@
366 <tabstop>enable_graphics_debugging</tabstop> 555 <tabstop>enable_graphics_debugging</tabstop>
367 <tabstop>enable_shader_feedback</tabstop> 556 <tabstop>enable_shader_feedback</tabstop>
368 <tabstop>enable_nsight_aftermath</tabstop> 557 <tabstop>enable_nsight_aftermath</tabstop>
369 <tabstop>disable_macro_jit</tabstop>
370 <tabstop>disable_loop_safety_checks</tabstop>
371 <tabstop>fs_access_log</tabstop> 558 <tabstop>fs_access_log</tabstop>
372 <tabstop>reporting_services</tabstop> 559 <tabstop>reporting_services</tabstop>
373 <tabstop>quest_flag</tabstop> 560 <tabstop>quest_flag</tabstop>
374 <tabstop>enable_cpu_debugging</tabstop> 561 <tabstop>enable_cpu_debugging</tabstop>
375 <tabstop>use_debug_asserts</tabstop> 562 <tabstop>use_debug_asserts</tabstop>
376 <tabstop>use_auto_stub</tabstop>
377 </tabstops> 563 </tabstops>
378 <resources/> 564 <resources/>
379 <connections/> 565 <connections/>
diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp
index bdf83ebfe..3c6bb3eb1 100644
--- a/src/yuzu/configuration/configure_dialog.cpp
+++ b/src/yuzu/configuration/configure_dialog.cpp
@@ -32,21 +32,23 @@ ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry_,
32 std::vector<VkDeviceInfo::Record>& vk_device_records, 32 std::vector<VkDeviceInfo::Record>& vk_device_records,
33 Core::System& system_, bool enable_web_config) 33 Core::System& system_, bool enable_web_config)
34 : QDialog(parent), ui{std::make_unique<Ui::ConfigureDialog>()}, 34 : QDialog(parent), ui{std::make_unique<Ui::ConfigureDialog>()},
35 registry(registry_), system{system_}, audio_tab{std::make_unique<ConfigureAudio>(system_, 35 registry(registry_), system{system_}, builder{std::make_unique<ConfigurationShared::Builder>(
36 this)}, 36 this, !system_.IsPoweredOn())},
37 cpu_tab{std::make_unique<ConfigureCpu>(system_, this)}, 37 audio_tab{std::make_unique<ConfigureAudio>(system_, nullptr, *builder, this)},
38 cpu_tab{std::make_unique<ConfigureCpu>(system_, nullptr, *builder, this)},
38 debug_tab_tab{std::make_unique<ConfigureDebugTab>(system_, this)}, 39 debug_tab_tab{std::make_unique<ConfigureDebugTab>(system_, this)},
39 filesystem_tab{std::make_unique<ConfigureFilesystem>(this)}, 40 filesystem_tab{std::make_unique<ConfigureFilesystem>(this)},
40 general_tab{std::make_unique<ConfigureGeneral>(system_, this)}, 41 general_tab{std::make_unique<ConfigureGeneral>(system_, nullptr, *builder, this)},
41 graphics_advanced_tab{std::make_unique<ConfigureGraphicsAdvanced>(system_, this)}, 42 graphics_advanced_tab{
43 std::make_unique<ConfigureGraphicsAdvanced>(system_, nullptr, *builder, this)},
42 graphics_tab{std::make_unique<ConfigureGraphics>( 44 graphics_tab{std::make_unique<ConfigureGraphics>(
43 system_, vk_device_records, [&]() { graphics_advanced_tab->ExposeComputeOption(); }, 45 system_, vk_device_records, [&]() { graphics_advanced_tab->ExposeComputeOption(); },
44 this)}, 46 nullptr, *builder, this)},
45 hotkeys_tab{std::make_unique<ConfigureHotkeys>(system_.HIDCore(), this)}, 47 hotkeys_tab{std::make_unique<ConfigureHotkeys>(system_.HIDCore(), this)},
46 input_tab{std::make_unique<ConfigureInput>(system_, this)}, 48 input_tab{std::make_unique<ConfigureInput>(system_, this)},
47 network_tab{std::make_unique<ConfigureNetwork>(system_, this)}, 49 network_tab{std::make_unique<ConfigureNetwork>(system_, this)},
48 profile_tab{std::make_unique<ConfigureProfileManager>(system_, this)}, 50 profile_tab{std::make_unique<ConfigureProfileManager>(system_, this)},
49 system_tab{std::make_unique<ConfigureSystem>(system_, this)}, 51 system_tab{std::make_unique<ConfigureSystem>(system_, nullptr, *builder, this)},
50 ui_tab{std::make_unique<ConfigureUi>(system_, this)}, web_tab{std::make_unique<ConfigureWeb>( 52 ui_tab{std::make_unique<ConfigureUi>(system_, this)}, web_tab{std::make_unique<ConfigureWeb>(
51 this)} { 53 this)} {
52 Settings::SetConfiguringGlobal(true); 54 Settings::SetConfiguringGlobal(true);
@@ -95,6 +97,9 @@ ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry_,
95 97
96 adjustSize(); 98 adjustSize();
97 ui->selectorList->setCurrentRow(0); 99 ui->selectorList->setCurrentRow(0);
100
101 // Selects the leftmost button on the bottom bar (Cancel as of writing)
102 ui->buttonBox->setFocus();
98} 103}
99 104
100ConfigureDialog::~ConfigureDialog() = default; 105ConfigureDialog::~ConfigureDialog() = default;
diff --git a/src/yuzu/configuration/configure_dialog.h b/src/yuzu/configuration/configure_dialog.h
index 2a08b7fee..96e9a8c3e 100644
--- a/src/yuzu/configuration/configure_dialog.h
+++ b/src/yuzu/configuration/configure_dialog.h
@@ -6,6 +6,9 @@
6#include <memory> 6#include <memory>
7#include <vector> 7#include <vector>
8#include <QDialog> 8#include <QDialog>
9#include "configuration/shared_widget.h"
10#include "yuzu/configuration/configuration_shared.h"
11#include "yuzu/configuration/shared_translation.h"
9#include "yuzu/vk_device_info.h" 12#include "yuzu/vk_device_info.h"
10 13
11namespace Core { 14namespace Core {
@@ -69,6 +72,8 @@ private:
69 HotkeyRegistry& registry; 72 HotkeyRegistry& registry;
70 73
71 Core::System& system; 74 Core::System& system;
75 std::unique_ptr<ConfigurationShared::Builder> builder;
76 std::vector<ConfigurationShared::Tab*> tab_group;
72 77
73 std::unique_ptr<ConfigureAudio> audio_tab; 78 std::unique_ptr<ConfigureAudio> audio_tab;
74 std::unique_ptr<ConfigureCpu> cpu_tab; 79 std::unique_ptr<ConfigureCpu> cpu_tab;
diff --git a/src/yuzu/configuration/configure_general.cpp b/src/yuzu/configuration/configure_general.cpp
index 2f55159f5..c727fadd1 100644
--- a/src/yuzu/configuration/configure_general.cpp
+++ b/src/yuzu/configuration/configure_general.cpp
@@ -3,57 +3,60 @@
3 3
4#include <functional> 4#include <functional>
5#include <utility> 5#include <utility>
6#include <vector>
6#include <QMessageBox> 7#include <QMessageBox>
7#include "common/settings.h" 8#include "common/settings.h"
8#include "core/core.h" 9#include "core/core.h"
9#include "ui_configure_general.h" 10#include "ui_configure_general.h"
10#include "yuzu/configuration/configuration_shared.h" 11#include "yuzu/configuration/configuration_shared.h"
11#include "yuzu/configuration/configure_general.h" 12#include "yuzu/configuration/configure_general.h"
13#include "yuzu/configuration/shared_widget.h"
12#include "yuzu/uisettings.h" 14#include "yuzu/uisettings.h"
13 15
14ConfigureGeneral::ConfigureGeneral(const Core::System& system_, QWidget* parent) 16ConfigureGeneral::ConfigureGeneral(const Core::System& system_,
15 : QWidget(parent), ui{std::make_unique<Ui::ConfigureGeneral>()}, system{system_} { 17 std::shared_ptr<std::vector<ConfigurationShared::Tab*>> group_,
18 const ConfigurationShared::Builder& builder, QWidget* parent)
19 : Tab(group_, parent), ui{std::make_unique<Ui::ConfigureGeneral>()}, system{system_} {
16 ui->setupUi(this); 20 ui->setupUi(this);
17 21
18 SetupPerGameUI(); 22 Setup(builder);
19 23
20 SetConfiguration(); 24 SetConfiguration();
21 25
22 if (Settings::IsConfiguringGlobal()) {
23 connect(ui->toggle_speed_limit, &QCheckBox::clicked, ui->speed_limit,
24 [this]() { ui->speed_limit->setEnabled(ui->toggle_speed_limit->isChecked()); });
25 }
26
27 connect(ui->button_reset_defaults, &QPushButton::clicked, this, 26 connect(ui->button_reset_defaults, &QPushButton::clicked, this,
28 &ConfigureGeneral::ResetDefaults); 27 &ConfigureGeneral::ResetDefaults);
28
29 if (!Settings::IsConfiguringGlobal()) {
30 ui->button_reset_defaults->setVisible(false);
31 }
29} 32}
30 33
31ConfigureGeneral::~ConfigureGeneral() = default; 34ConfigureGeneral::~ConfigureGeneral() = default;
32 35
33void ConfigureGeneral::SetConfiguration() { 36void ConfigureGeneral::SetConfiguration() {}
34 const bool runtime_lock = !system.IsPoweredOn(); 37
38void ConfigureGeneral::Setup(const ConfigurationShared::Builder& builder) {
39 QLayout& layout = *ui->general_widget->layout();
35 40
36 ui->use_multi_core->setEnabled(runtime_lock); 41 std::map<u32, QWidget*> hold{};
37 ui->use_multi_core->setChecked(Settings::values.use_multi_core.GetValue());
38 42
39 ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing.GetValue()); 43 for (const auto setting :
40 ui->toggle_user_on_boot->setChecked(UISettings::values.select_user_on_boot.GetValue()); 44 UISettings::values.linkage.by_category[Settings::Category::UiGeneral]) {
41 ui->toggle_background_pause->setChecked(UISettings::values.pause_when_in_background.GetValue()); 45 auto* widget = builder.BuildWidget(setting, apply_funcs);
42 ui->toggle_hide_mouse->setChecked(UISettings::values.hide_mouse.GetValue());
43 ui->toggle_controller_applet_disabled->setEnabled(runtime_lock);
44 ui->toggle_controller_applet_disabled->setChecked(
45 UISettings::values.controller_applet_disabled.GetValue());
46 46
47 ui->toggle_speed_limit->setChecked(Settings::values.use_speed_limit.GetValue()); 47 if (widget == nullptr) {
48 ui->speed_limit->setValue(Settings::values.speed_limit.GetValue()); 48 continue;
49 }
50 if (!widget->Valid()) {
51 widget->deleteLater();
52 continue;
53 }
49 54
50 ui->button_reset_defaults->setEnabled(runtime_lock); 55 hold.emplace(setting->Id(), widget);
56 }
51 57
52 if (Settings::IsConfiguringGlobal()) { 58 for (const auto& [id, widget] : hold) {
53 ui->speed_limit->setEnabled(Settings::values.use_speed_limit.GetValue()); 59 layout.addWidget(widget);
54 } else {
55 ui->speed_limit->setEnabled(Settings::values.use_speed_limit.GetValue() &&
56 use_speed_limit != ConfigurationShared::CheckState::Global);
57 } 60 }
58} 61}
59 62
@@ -77,32 +80,9 @@ void ConfigureGeneral::ResetDefaults() {
77} 80}
78 81
79void ConfigureGeneral::ApplyConfiguration() { 82void ConfigureGeneral::ApplyConfiguration() {
80 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_multi_core, ui->use_multi_core, 83 bool powered_on = system.IsPoweredOn();
81 use_multi_core); 84 for (const auto& func : apply_funcs) {
82 85 func(powered_on);
83 if (Settings::IsConfiguringGlobal()) {
84 UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked();
85 UISettings::values.select_user_on_boot = ui->toggle_user_on_boot->isChecked();
86 UISettings::values.pause_when_in_background = ui->toggle_background_pause->isChecked();
87 UISettings::values.hide_mouse = ui->toggle_hide_mouse->isChecked();
88 UISettings::values.controller_applet_disabled =
89 ui->toggle_controller_applet_disabled->isChecked();
90
91 // Guard if during game and set to game-specific value
92 if (Settings::values.use_speed_limit.UsingGlobal()) {
93 Settings::values.use_speed_limit.SetValue(ui->toggle_speed_limit->checkState() ==
94 Qt::Checked);
95 Settings::values.speed_limit.SetValue(ui->speed_limit->value());
96 }
97 } else {
98 bool global_speed_limit = use_speed_limit == ConfigurationShared::CheckState::Global;
99 Settings::values.use_speed_limit.SetGlobal(global_speed_limit);
100 Settings::values.speed_limit.SetGlobal(global_speed_limit);
101 if (!global_speed_limit) {
102 Settings::values.use_speed_limit.SetValue(ui->toggle_speed_limit->checkState() ==
103 Qt::Checked);
104 Settings::values.speed_limit.SetValue(ui->speed_limit->value());
105 }
106 } 86 }
107} 87}
108 88
@@ -117,33 +97,3 @@ void ConfigureGeneral::changeEvent(QEvent* event) {
117void ConfigureGeneral::RetranslateUI() { 97void ConfigureGeneral::RetranslateUI() {
118 ui->retranslateUi(this); 98 ui->retranslateUi(this);
119} 99}
120
121void ConfigureGeneral::SetupPerGameUI() {
122 if (Settings::IsConfiguringGlobal()) {
123 // Disables each setting if:
124 // - A game is running (thus settings in use), and
125 // - A non-global setting is applied.
126 ui->toggle_speed_limit->setEnabled(Settings::values.use_speed_limit.UsingGlobal());
127 ui->speed_limit->setEnabled(Settings::values.speed_limit.UsingGlobal());
128
129 return;
130 }
131
132 ui->toggle_check_exit->setVisible(false);
133 ui->toggle_user_on_boot->setVisible(false);
134 ui->toggle_background_pause->setVisible(false);
135 ui->toggle_hide_mouse->setVisible(false);
136 ui->toggle_controller_applet_disabled->setVisible(false);
137
138 ui->button_reset_defaults->setVisible(false);
139
140 ConfigurationShared::SetColoredTristate(ui->toggle_speed_limit,
141 Settings::values.use_speed_limit, use_speed_limit);
142 ConfigurationShared::SetColoredTristate(ui->use_multi_core, Settings::values.use_multi_core,
143 use_multi_core);
144
145 connect(ui->toggle_speed_limit, &QCheckBox::clicked, ui->speed_limit, [this]() {
146 ui->speed_limit->setEnabled(ui->toggle_speed_limit->isChecked() &&
147 (use_speed_limit != ConfigurationShared::CheckState::Global));
148 });
149}
diff --git a/src/yuzu/configuration/configure_general.h b/src/yuzu/configuration/configure_general.h
index 7ff63f425..2d953f679 100644
--- a/src/yuzu/configuration/configure_general.h
+++ b/src/yuzu/configuration/configure_general.h
@@ -5,48 +5,49 @@
5 5
6#include <functional> 6#include <functional>
7#include <memory> 7#include <memory>
8#include <vector>
8#include <QWidget> 9#include <QWidget>
10#include "yuzu/configuration/configuration_shared.h"
9 11
10namespace Core { 12namespace Core {
11class System; 13class System;
12} 14}
13 15
14class ConfigureDialog; 16class ConfigureDialog;
15
16namespace ConfigurationShared {
17enum class CheckState;
18}
19
20class HotkeyRegistry; 17class HotkeyRegistry;
21 18
22namespace Ui { 19namespace Ui {
23class ConfigureGeneral; 20class ConfigureGeneral;
24} 21}
25 22
26class ConfigureGeneral : public QWidget { 23namespace ConfigurationShared {
27 Q_OBJECT 24class Builder;
25}
28 26
27class ConfigureGeneral : public ConfigurationShared::Tab {
29public: 28public:
30 explicit ConfigureGeneral(const Core::System& system_, QWidget* parent = nullptr); 29 explicit ConfigureGeneral(const Core::System& system_,
30 std::shared_ptr<std::vector<ConfigurationShared::Tab*>> group,
31 const ConfigurationShared::Builder& builder,
32 QWidget* parent = nullptr);
31 ~ConfigureGeneral() override; 33 ~ConfigureGeneral() override;
32 34
33 void SetResetCallback(std::function<void()> callback); 35 void SetResetCallback(std::function<void()> callback);
34 void ResetDefaults(); 36 void ResetDefaults();
35 void ApplyConfiguration(); 37 void ApplyConfiguration() override;
36 void SetConfiguration(); 38 void SetConfiguration() override;
37 39
38private: 40private:
41 void Setup(const ConfigurationShared::Builder& builder);
42
39 void changeEvent(QEvent* event) override; 43 void changeEvent(QEvent* event) override;
40 void RetranslateUI(); 44 void RetranslateUI();
41 45
42 void SetupPerGameUI();
43
44 std::function<void()> reset_callback; 46 std::function<void()> reset_callback;
45 47
46 std::unique_ptr<Ui::ConfigureGeneral> ui; 48 std::unique_ptr<Ui::ConfigureGeneral> ui;
47 49
48 ConfigurationShared::CheckState use_speed_limit; 50 std::vector<std::function<void(bool)>> apply_funcs{};
49 ConfigurationShared::CheckState use_multi_core;
50 51
51 const Core::System& system; 52 const Core::System& system;
52}; 53};
diff --git a/src/yuzu/configuration/configure_general.ui b/src/yuzu/configuration/configure_general.ui
index fe757d011..a10e7d3a5 100644
--- a/src/yuzu/configuration/configure_general.ui
+++ b/src/yuzu/configuration/configure_general.ui
@@ -26,77 +26,22 @@
26 </property> 26 </property>
27 <layout class="QHBoxLayout" name="GeneralHorizontalLayout"> 27 <layout class="QHBoxLayout" name="GeneralHorizontalLayout">
28 <item> 28 <item>
29 <layout class="QVBoxLayout" name="GeneralVerticalLayout"> 29 <widget class="QWidget" name="general_widget" native="true">
30 <item> 30 <layout class="QVBoxLayout" name="GeneralVerticalLayout">
31 <layout class="QHBoxLayout" name="horizontalLayout_2"> 31 <property name="leftMargin">
32 <item> 32 <number>0</number>
33 <widget class="QCheckBox" name="toggle_speed_limit"> 33 </property>
34 <property name="text"> 34 <property name="topMargin">
35 <string>Limit Speed Percent</string> 35 <number>0</number>
36 </property> 36 </property>
37 </widget> 37 <property name="rightMargin">
38 </item> 38 <number>0</number>
39 <item> 39 </property>
40 <widget class="QSpinBox" name="speed_limit"> 40 <property name="bottomMargin">
41 <property name="suffix"> 41 <number>0</number>
42 <string>%</string> 42 </property>
43 </property> 43 </layout>
44 <property name="minimum"> 44 </widget>
45 <number>1</number>
46 </property>
47 <property name="maximum">
48 <number>9999</number>
49 </property>
50 <property name="value">
51 <number>100</number>
52 </property>
53 </widget>
54 </item>
55 </layout>
56 </item>
57 <item>
58 <widget class="QCheckBox" name="use_multi_core">
59 <property name="text">
60 <string>Multicore CPU Emulation</string>
61 </property>
62 </widget>
63 </item>
64 <item>
65 <widget class="QCheckBox" name="toggle_check_exit">
66 <property name="text">
67 <string>Confirm exit while emulation is running</string>
68 </property>
69 </widget>
70 </item>
71 <item>
72 <widget class="QCheckBox" name="toggle_user_on_boot">
73 <property name="text">
74 <string>Prompt for user on game boot</string>
75 </property>
76 </widget>
77 </item>
78 <item>
79 <widget class="QCheckBox" name="toggle_background_pause">
80 <property name="text">
81 <string>Pause emulation when in background</string>
82 </property>
83 </widget>
84 </item>
85 <item>
86 <widget class="QCheckBox" name="toggle_hide_mouse">
87 <property name="text">
88 <string>Hide mouse on inactivity</string>
89 </property>
90 </widget>
91 </item>
92 <item>
93 <widget class="QCheckBox" name="toggle_controller_applet_disabled">
94 <property name="text">
95 <string>Disable controller applet</string>
96 </property>
97 </widget>
98 </item>
99 </layout>
100 </item> 45 </item>
101 </layout> 46 </layout>
102 </widget> 47 </widget>
diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp
index a4965524a..a94fbc89a 100644
--- a/src/yuzu/configuration/configure_graphics.cpp
+++ b/src/yuzu/configuration/configure_graphics.cpp
@@ -7,6 +7,7 @@
7#include <iterator> 7#include <iterator>
8#include <string> 8#include <string>
9#include <tuple> 9#include <tuple>
10#include <typeinfo>
10#include <utility> 11#include <utility>
11#include <vector> 12#include <vector>
12#include <QBoxLayout> 13#include <QBoxLayout>
@@ -15,23 +16,29 @@
15#include <QComboBox> 16#include <QComboBox>
16#include <QIcon> 17#include <QIcon>
17#include <QLabel> 18#include <QLabel>
19#include <QLineEdit>
18#include <QPixmap> 20#include <QPixmap>
19#include <QPushButton> 21#include <QPushButton>
20#include <QSlider> 22#include <QSlider>
21#include <QStringLiteral> 23#include <QStringLiteral>
22#include <QtCore/qobjectdefs.h> 24#include <QtCore/qobjectdefs.h>
25#include <qabstractbutton.h>
26#include <qboxlayout.h>
23#include <qcoreevent.h> 27#include <qcoreevent.h>
24#include <qglobal.h> 28#include <qglobal.h>
29#include <qgridlayout.h>
25#include <vulkan/vulkan_core.h> 30#include <vulkan/vulkan_core.h>
26 31
27#include "common/common_types.h" 32#include "common/common_types.h"
28#include "common/dynamic_library.h" 33#include "common/dynamic_library.h"
29#include "common/logging/log.h" 34#include "common/logging/log.h"
30#include "common/settings.h" 35#include "common/settings.h"
36#include "common/settings_enums.h"
31#include "core/core.h" 37#include "core/core.h"
32#include "ui_configure_graphics.h" 38#include "ui_configure_graphics.h"
33#include "yuzu/configuration/configuration_shared.h" 39#include "yuzu/configuration/configuration_shared.h"
34#include "yuzu/configuration/configure_graphics.h" 40#include "yuzu/configuration/configure_graphics.h"
41#include "yuzu/configuration/shared_widget.h"
35#include "yuzu/qt_common.h" 42#include "yuzu/qt_common.h"
36#include "yuzu/uisettings.h" 43#include "yuzu/uisettings.h"
37#include "yuzu/vk_device_info.h" 44#include "yuzu/vk_device_info.h"
@@ -46,9 +53,9 @@ static constexpr VkPresentModeKHR VSyncSettingToMode(Settings::VSyncMode mode) {
46 return VK_PRESENT_MODE_IMMEDIATE_KHR; 53 return VK_PRESENT_MODE_IMMEDIATE_KHR;
47 case Settings::VSyncMode::Mailbox: 54 case Settings::VSyncMode::Mailbox:
48 return VK_PRESENT_MODE_MAILBOX_KHR; 55 return VK_PRESENT_MODE_MAILBOX_KHR;
49 case Settings::VSyncMode::FIFO: 56 case Settings::VSyncMode::Fifo:
50 return VK_PRESENT_MODE_FIFO_KHR; 57 return VK_PRESENT_MODE_FIFO_KHR;
51 case Settings::VSyncMode::FIFORelaxed: 58 case Settings::VSyncMode::FifoRelaxed:
52 return VK_PRESENT_MODE_FIFO_RELAXED_KHR; 59 return VK_PRESENT_MODE_FIFO_RELAXED_KHR;
53 default: 60 default:
54 return VK_PRESENT_MODE_FIFO_KHR; 61 return VK_PRESENT_MODE_FIFO_KHR;
@@ -62,50 +69,67 @@ static constexpr Settings::VSyncMode PresentModeToSetting(VkPresentModeKHR mode)
62 case VK_PRESENT_MODE_MAILBOX_KHR: 69 case VK_PRESENT_MODE_MAILBOX_KHR:
63 return Settings::VSyncMode::Mailbox; 70 return Settings::VSyncMode::Mailbox;
64 case VK_PRESENT_MODE_FIFO_KHR: 71 case VK_PRESENT_MODE_FIFO_KHR:
65 return Settings::VSyncMode::FIFO; 72 return Settings::VSyncMode::Fifo;
66 case VK_PRESENT_MODE_FIFO_RELAXED_KHR: 73 case VK_PRESENT_MODE_FIFO_RELAXED_KHR:
67 return Settings::VSyncMode::FIFORelaxed; 74 return Settings::VSyncMode::FifoRelaxed;
68 default: 75 default:
69 return Settings::VSyncMode::FIFO; 76 return Settings::VSyncMode::Fifo;
70 } 77 }
71} 78}
72 79
73ConfigureGraphics::ConfigureGraphics(const Core::System& system_, 80ConfigureGraphics::ConfigureGraphics(const Core::System& system_,
74 std::vector<VkDeviceInfo::Record>& records_, 81 std::vector<VkDeviceInfo::Record>& records_,
75 const std::function<void()>& expose_compute_option_, 82 const std::function<void()>& expose_compute_option_,
76 QWidget* parent) 83 std::shared_ptr<std::vector<ConfigurationShared::Tab*>> group_,
77 : QWidget(parent), ui{std::make_unique<Ui::ConfigureGraphics>()}, records{records_}, 84 const ConfigurationShared::Builder& builder, QWidget* parent)
78 expose_compute_option{expose_compute_option_}, system{system_} { 85 : ConfigurationShared::Tab(group_, parent), ui{std::make_unique<Ui::ConfigureGraphics>()},
86 records{records_}, expose_compute_option{expose_compute_option_}, system{system_},
87 combobox_translations{builder.ComboboxTranslations()},
88 shader_mapping{
89 combobox_translations.at(Settings::EnumMetadata<Settings::ShaderBackend>::Index())} {
79 vulkan_device = Settings::values.vulkan_device.GetValue(); 90 vulkan_device = Settings::values.vulkan_device.GetValue();
80 RetrieveVulkanDevices(); 91 RetrieveVulkanDevices();
81 92
82 ui->setupUi(this); 93 ui->setupUi(this);
83 94
95 Setup(builder);
96
84 for (const auto& device : vulkan_devices) { 97 for (const auto& device : vulkan_devices) {
85 ui->device->addItem(device); 98 vulkan_device_combobox->addItem(device);
86 } 99 }
87 100
88 ui->backend->addItem(QStringLiteral("GLSL")); 101 UpdateBackgroundColorButton(QColor::fromRgb(Settings::values.bg_red.GetValue(),
89 ui->backend->addItem(tr("GLASM (Assembly Shaders, NVIDIA Only)")); 102 Settings::values.bg_green.GetValue(),
90 ui->backend->addItem(tr("SPIR-V (Experimental, Mesa Only)")); 103 Settings::values.bg_blue.GetValue()));
91 104 UpdateAPILayout();
92 SetupPerGameUI(); 105 PopulateVSyncModeSelection(); //< must happen after UpdateAPILayout
93 106
94 SetConfiguration(); 107 // VSync setting needs to be determined after populating the VSync combobox
108 if (Settings::IsConfiguringGlobal()) {
109 const auto vsync_mode_setting = Settings::values.vsync_mode.GetValue();
110 const auto vsync_mode = VSyncSettingToMode(vsync_mode_setting);
111 int index{};
112 for (const auto mode : vsync_mode_combobox_enum_map) {
113 if (mode == vsync_mode) {
114 break;
115 }
116 index++;
117 }
118 if (static_cast<unsigned long>(index) < vsync_mode_combobox_enum_map.size()) {
119 vsync_mode_combobox->setCurrentIndex(index);
120 }
121 }
95 122
96 connect(ui->api, qOverload<int>(&QComboBox::currentIndexChanged), this, [this] { 123 connect(api_combobox, qOverload<int>(&QComboBox::activated), this, [this] {
97 UpdateAPILayout(); 124 UpdateAPILayout();
98 PopulateVSyncModeSelection(); 125 PopulateVSyncModeSelection();
99 if (!Settings::IsConfiguringGlobal()) {
100 ConfigurationShared::SetHighlight(
101 ui->api_widget, ui->api->currentIndex() != ConfigurationShared::USE_GLOBAL_INDEX);
102 }
103 }); 126 });
104 connect(ui->device, qOverload<int>(&QComboBox::activated), this, [this](int device) { 127 connect(vulkan_device_combobox, qOverload<int>(&QComboBox::activated), this,
105 UpdateDeviceSelection(device); 128 [this](int device) {
106 PopulateVSyncModeSelection(); 129 UpdateDeviceSelection(device);
107 }); 130 PopulateVSyncModeSelection();
108 connect(ui->backend, qOverload<int>(&QComboBox::activated), this, 131 });
132 connect(shader_backend_combobox, qOverload<int>(&QComboBox::activated), this,
109 [this](int backend) { UpdateShaderBackendSelection(backend); }); 133 [this](int backend) { UpdateShaderBackendSelection(backend); });
110 134
111 connect(ui->bg_button, &QPushButton::clicked, this, [this] { 135 connect(ui->bg_button, &QPushButton::clicked, this, [this] {
@@ -116,39 +140,45 @@ ConfigureGraphics::ConfigureGraphics(const Core::System& system_,
116 UpdateBackgroundColorButton(new_bg_color); 140 UpdateBackgroundColorButton(new_bg_color);
117 }); 141 });
118 142
119 ui->api->setEnabled(!UISettings::values.has_broken_vulkan && ui->api->isEnabled()); 143 api_combobox->setEnabled(!UISettings::values.has_broken_vulkan && api_combobox->isEnabled());
120 ui->api_widget->setEnabled( 144 ui->api_widget->setEnabled(
121 (!UISettings::values.has_broken_vulkan || Settings::IsConfiguringGlobal()) && 145 (!UISettings::values.has_broken_vulkan || Settings::IsConfiguringGlobal()) &&
122 ui->api_widget->isEnabled()); 146 ui->api_widget->isEnabled());
123 ui->bg_label->setVisible(Settings::IsConfiguringGlobal());
124 ui->bg_combobox->setVisible(!Settings::IsConfiguringGlobal());
125 147
126 connect(ui->fsr_sharpening_slider, &QSlider::valueChanged, this, 148 if (Settings::IsConfiguringGlobal()) {
127 &ConfigureGraphics::SetFSRIndicatorText); 149 ui->bg_widget->setEnabled(Settings::values.bg_red.UsingGlobal());
128 ui->fsr_sharpening_combobox->setVisible(!Settings::IsConfiguringGlobal()); 150 }
129 ui->fsr_sharpening_label->setVisible(Settings::IsConfiguringGlobal());
130} 151}
131 152
132void ConfigureGraphics::PopulateVSyncModeSelection() { 153void ConfigureGraphics::PopulateVSyncModeSelection() {
154 if (!Settings::IsConfiguringGlobal()) {
155 return;
156 }
157
133 const Settings::RendererBackend backend{GetCurrentGraphicsBackend()}; 158 const Settings::RendererBackend backend{GetCurrentGraphicsBackend()};
134 if (backend == Settings::RendererBackend::Null) { 159 if (backend == Settings::RendererBackend::Null) {
135 ui->vsync_mode_combobox->setEnabled(false); 160 vsync_mode_combobox->setEnabled(false);
136 return; 161 return;
137 } 162 }
138 ui->vsync_mode_combobox->setEnabled(true); 163 vsync_mode_combobox->setEnabled(true);
139 164
140 const int current_index = //< current selected vsync mode from combobox 165 const int current_index = //< current selected vsync mode from combobox
141 ui->vsync_mode_combobox->currentIndex(); 166 vsync_mode_combobox->currentIndex();
142 const auto current_mode = //< current selected vsync mode as a VkPresentModeKHR 167 const auto current_mode = //< current selected vsync mode as a VkPresentModeKHR
143 current_index == -1 ? VSyncSettingToMode(Settings::values.vsync_mode.GetValue()) 168 current_index == -1 ? VSyncSettingToMode(Settings::values.vsync_mode.GetValue())
144 : vsync_mode_combobox_enum_map[current_index]; 169 : vsync_mode_combobox_enum_map[current_index];
145 int index{}; 170 int index{};
146 const int device{ui->device->currentIndex()}; //< current selected Vulkan device 171 const int device{vulkan_device_combobox->currentIndex()}; //< current selected Vulkan device
172 if (device == -1) {
173 // Invalid device
174 return;
175 }
176
147 const auto& present_modes = //< relevant vector of present modes for the selected device or API 177 const auto& present_modes = //< relevant vector of present modes for the selected device or API
148 backend == Settings::RendererBackend::Vulkan ? device_present_modes[device] 178 backend == Settings::RendererBackend::Vulkan ? device_present_modes[device]
149 : default_present_modes; 179 : default_present_modes;
150 180
151 ui->vsync_mode_combobox->clear(); 181 vsync_mode_combobox->clear();
152 vsync_mode_combobox_enum_map.clear(); 182 vsync_mode_combobox_enum_map.clear();
153 vsync_mode_combobox_enum_map.reserve(present_modes.size()); 183 vsync_mode_combobox_enum_map.reserve(present_modes.size());
154 for (const auto present_mode : present_modes) { 184 for (const auto present_mode : present_modes) {
@@ -157,10 +187,10 @@ void ConfigureGraphics::PopulateVSyncModeSelection() {
157 continue; 187 continue;
158 } 188 }
159 189
160 ui->vsync_mode_combobox->insertItem(index, mode_name); 190 vsync_mode_combobox->insertItem(index, mode_name);
161 vsync_mode_combobox_enum_map.push_back(present_mode); 191 vsync_mode_combobox_enum_map.push_back(present_mode);
162 if (present_mode == current_mode) { 192 if (present_mode == current_mode) {
163 ui->vsync_mode_combobox->setCurrentIndex(index); 193 vsync_mode_combobox->setCurrentIndex(index);
164 } 194 }
165 index++; 195 index++;
166 } 196 }
@@ -186,112 +216,124 @@ void ConfigureGraphics::UpdateShaderBackendSelection(int backend) {
186 216
187ConfigureGraphics::~ConfigureGraphics() = default; 217ConfigureGraphics::~ConfigureGraphics() = default;
188 218
189void ConfigureGraphics::SetConfiguration() { 219void ConfigureGraphics::SetConfiguration() {}
190 const bool runtime_lock = !system.IsPoweredOn(); 220
191 221void ConfigureGraphics::Setup(const ConfigurationShared::Builder& builder) {
192 ui->api_widget->setEnabled(runtime_lock); 222 QLayout* api_layout = ui->api_widget->layout();
193 ui->use_asynchronous_gpu_emulation->setEnabled(runtime_lock); 223 QWidget* api_grid_widget = new QWidget(this);
194 ui->use_disk_shader_cache->setEnabled(runtime_lock); 224 QVBoxLayout* api_grid_layout = new QVBoxLayout(api_grid_widget);
195 ui->nvdec_emulation_widget->setEnabled(runtime_lock); 225 api_grid_layout->setContentsMargins(0, 0, 0, 0);
196 ui->resolution_combobox->setEnabled(runtime_lock); 226 api_layout->addWidget(api_grid_widget);
197 ui->accelerate_astc->setEnabled(runtime_lock); 227
198 ui->vsync_mode_layout->setEnabled(runtime_lock || 228 QLayout& graphics_layout = *ui->graphics_widget->layout();
199 Settings::values.renderer_backend.GetValue() == 229
200 Settings::RendererBackend::Vulkan); 230 std::map<u32, QWidget*> hold_graphics;
201 ui->use_disk_shader_cache->setChecked(Settings::values.use_disk_shader_cache.GetValue()); 231 std::vector<QWidget*> hold_api;
202 ui->use_asynchronous_gpu_emulation->setChecked( 232
203 Settings::values.use_asynchronous_gpu_emulation.GetValue()); 233 for (const auto setting : Settings::values.linkage.by_category[Settings::Category::Renderer]) {
204 ui->accelerate_astc->setChecked(Settings::values.accelerate_astc.GetValue()); 234 ConfigurationShared::Widget* widget = [&]() {
235 if (setting->Id() == Settings::values.fsr_sharpening_slider.Id()) {
236 // FSR needs a reversed slider and a 0.5 multiplier
237 return builder.BuildWidget(
238 setting, apply_funcs, ConfigurationShared::RequestType::ReverseSlider, true,
239 0.5f, nullptr, tr("%", "FSR sharpening percentage (e.g. 50%)"));
240 } else {
241 return builder.BuildWidget(setting, apply_funcs);
242 }
243 }();
205 244
206 if (Settings::IsConfiguringGlobal()) { 245 if (widget == nullptr) {
207 ui->api->setCurrentIndex(static_cast<int>(Settings::values.renderer_backend.GetValue())); 246 continue;
208 ui->fullscreen_mode_combobox->setCurrentIndex( 247 }
209 static_cast<int>(Settings::values.fullscreen_mode.GetValue())); 248 if (!widget->Valid()) {
210 ui->nvdec_emulation->setCurrentIndex( 249 widget->deleteLater();
211 static_cast<int>(Settings::values.nvdec_emulation.GetValue())); 250 continue;
212 ui->aspect_ratio_combobox->setCurrentIndex(Settings::values.aspect_ratio.GetValue()); 251 }
213 ui->resolution_combobox->setCurrentIndex(
214 static_cast<int>(Settings::values.resolution_setup.GetValue()));
215 ui->scaling_filter_combobox->setCurrentIndex(
216 static_cast<int>(Settings::values.scaling_filter.GetValue()));
217 ui->fsr_sharpening_slider->setValue(Settings::values.fsr_sharpening_slider.GetValue());
218 ui->anti_aliasing_combobox->setCurrentIndex(
219 static_cast<int>(Settings::values.anti_aliasing.GetValue()));
220 } else {
221 ConfigurationShared::SetPerGameSetting(ui->api, &Settings::values.renderer_backend);
222 ConfigurationShared::SetHighlight(ui->api_widget,
223 !Settings::values.renderer_backend.UsingGlobal());
224
225 ConfigurationShared::SetPerGameSetting(ui->nvdec_emulation,
226 &Settings::values.nvdec_emulation);
227 ConfigurationShared::SetHighlight(ui->nvdec_emulation_widget,
228 !Settings::values.nvdec_emulation.UsingGlobal());
229
230 ConfigurationShared::SetPerGameSetting(ui->fullscreen_mode_combobox,
231 &Settings::values.fullscreen_mode);
232 ConfigurationShared::SetHighlight(ui->fullscreen_mode_label,
233 !Settings::values.fullscreen_mode.UsingGlobal());
234
235 ConfigurationShared::SetPerGameSetting(ui->aspect_ratio_combobox,
236 &Settings::values.aspect_ratio);
237 ConfigurationShared::SetHighlight(ui->ar_label,
238 !Settings::values.aspect_ratio.UsingGlobal());
239
240 ConfigurationShared::SetPerGameSetting(ui->resolution_combobox,
241 &Settings::values.resolution_setup);
242 ConfigurationShared::SetHighlight(ui->resolution_label,
243 !Settings::values.resolution_setup.UsingGlobal());
244
245 ConfigurationShared::SetPerGameSetting(ui->scaling_filter_combobox,
246 &Settings::values.scaling_filter);
247 ConfigurationShared::SetHighlight(ui->scaling_filter_label,
248 !Settings::values.scaling_filter.UsingGlobal());
249
250 ConfigurationShared::SetPerGameSetting(ui->anti_aliasing_combobox,
251 &Settings::values.anti_aliasing);
252 ConfigurationShared::SetHighlight(ui->anti_aliasing_label,
253 !Settings::values.anti_aliasing.UsingGlobal());
254
255 ui->fsr_sharpening_combobox->setCurrentIndex(
256 Settings::values.fsr_sharpening_slider.UsingGlobal() ? 0 : 1);
257 ui->fsr_sharpening_slider->setEnabled(
258 !Settings::values.fsr_sharpening_slider.UsingGlobal());
259 ui->fsr_sharpening_value->setEnabled(!Settings::values.fsr_sharpening_slider.UsingGlobal());
260 ConfigurationShared::SetHighlight(ui->fsr_sharpening_layout,
261 !Settings::values.fsr_sharpening_slider.UsingGlobal());
262 ui->fsr_sharpening_slider->setValue(Settings::values.fsr_sharpening_slider.GetValue());
263
264 ui->bg_combobox->setCurrentIndex(Settings::values.bg_red.UsingGlobal() ? 0 : 1);
265 ui->bg_button->setEnabled(!Settings::values.bg_red.UsingGlobal());
266 ConfigurationShared::SetHighlight(ui->bg_layout, !Settings::values.bg_red.UsingGlobal());
267 }
268 UpdateBackgroundColorButton(QColor::fromRgb(Settings::values.bg_red.GetValue(),
269 Settings::values.bg_green.GetValue(),
270 Settings::values.bg_blue.GetValue()));
271 UpdateAPILayout();
272 PopulateVSyncModeSelection(); //< must happen after UpdateAPILayout
273 SetFSRIndicatorText(ui->fsr_sharpening_slider->sliderPosition());
274 252
275 // VSync setting needs to be determined after populating the VSync combobox 253 if (setting->Id() == Settings::values.renderer_backend.Id()) {
276 if (Settings::IsConfiguringGlobal()) { 254 // Add the renderer combobox now so it's at the top
277 const auto vsync_mode_setting = Settings::values.vsync_mode.GetValue(); 255 api_grid_layout->addWidget(widget);
278 const auto vsync_mode = VSyncSettingToMode(vsync_mode_setting); 256 api_combobox = widget->combobox;
279 int index{}; 257 api_restore_global_button = widget->restore_button;
280 for (const auto mode : vsync_mode_combobox_enum_map) { 258
281 if (mode == vsync_mode) { 259 if (!Settings::IsConfiguringGlobal()) {
282 break; 260 QObject::connect(api_restore_global_button, &QAbstractButton::clicked,
261 [this](bool) { UpdateAPILayout(); });
262
263 // Detach API's restore button and place it where we want
264 // Lets us put it on the side, and it will automatically scale if there's a
265 // second combobox (shader_backend, vulkan_device)
266 widget->layout()->removeWidget(api_restore_global_button);
267 api_layout->addWidget(api_restore_global_button);
283 } 268 }
284 index++; 269 } else if (setting->Id() == Settings::values.vulkan_device.Id()) {
285 } 270 // Keep track of vulkan_device's combobox so we can populate it
286 if (static_cast<unsigned long>(index) < vsync_mode_combobox_enum_map.size()) { 271 hold_api.push_back(widget);
287 ui->vsync_mode_combobox->setCurrentIndex(index); 272 vulkan_device_combobox = widget->combobox;
273 vulkan_device_widget = widget;
274 } else if (setting->Id() == Settings::values.shader_backend.Id()) {
275 // Keep track of shader_backend's combobox so we can populate it
276 hold_api.push_back(widget);
277 shader_backend_combobox = widget->combobox;
278 shader_backend_widget = widget;
279 } else if (setting->Id() == Settings::values.vsync_mode.Id()) {
280 // Keep track of vsync_mode's combobox so we can populate it
281 vsync_mode_combobox = widget->combobox;
282 hold_graphics.emplace(setting->Id(), widget);
283 } else {
284 hold_graphics.emplace(setting->Id(), widget);
288 } 285 }
289 } 286 }
290}
291 287
292void ConfigureGraphics::SetFSRIndicatorText(int percentage) { 288 for (const auto& [id, widget] : hold_graphics) {
293 ui->fsr_sharpening_value->setText( 289 graphics_layout.addWidget(widget);
294 tr("%1%", "FSR sharpening percentage (e.g. 50%)").arg(100 - (percentage / 2))); 290 }
291
292 for (auto widget : hold_api) {
293 api_grid_layout->addWidget(widget);
294 }
295
296 // Background color is too specific to build into the new system, so we manage it here
297 // (3 settings, all collected into a single widget with a QColor to manage on top)
298 if (Settings::IsConfiguringGlobal()) {
299 apply_funcs.push_back([this](bool powered_on) {
300 Settings::values.bg_red.SetValue(static_cast<u8>(bg_color.red()));
301 Settings::values.bg_green.SetValue(static_cast<u8>(bg_color.green()));
302 Settings::values.bg_blue.SetValue(static_cast<u8>(bg_color.blue()));
303 });
304 } else {
305 QPushButton* bg_restore_button = ConfigurationShared::Widget::CreateRestoreGlobalButton(
306 Settings::values.bg_red.UsingGlobal(), ui->bg_widget);
307 ui->bg_widget->layout()->addWidget(bg_restore_button);
308
309 QObject::connect(bg_restore_button, &QAbstractButton::clicked,
310 [bg_restore_button, this](bool) {
311 const int r = Settings::values.bg_red.GetValue(true);
312 const int g = Settings::values.bg_green.GetValue(true);
313 const int b = Settings::values.bg_blue.GetValue(true);
314 UpdateBackgroundColorButton(QColor::fromRgb(r, g, b));
315
316 bg_restore_button->setVisible(false);
317 bg_restore_button->setEnabled(false);
318 });
319
320 QObject::connect(ui->bg_button, &QAbstractButton::clicked, [bg_restore_button](bool) {
321 bg_restore_button->setVisible(true);
322 bg_restore_button->setEnabled(true);
323 });
324
325 apply_funcs.push_back([bg_restore_button, this](bool powered_on) {
326 const bool using_global = !bg_restore_button->isEnabled();
327 Settings::values.bg_red.SetGlobal(using_global);
328 Settings::values.bg_green.SetGlobal(using_global);
329 Settings::values.bg_blue.SetGlobal(using_global);
330 if (!using_global) {
331 Settings::values.bg_red.SetValue(static_cast<u8>(bg_color.red()));
332 Settings::values.bg_green.SetValue(static_cast<u8>(bg_color.green()));
333 Settings::values.bg_blue.SetValue(static_cast<u8>(bg_color.blue()));
334 }
335 });
336 }
295} 337}
296 338
297const QString ConfigureGraphics::TranslateVSyncMode(VkPresentModeKHR mode, 339const QString ConfigureGraphics::TranslateVSyncMode(VkPresentModeKHR mode,
@@ -315,130 +357,48 @@ const QString ConfigureGraphics::TranslateVSyncMode(VkPresentModeKHR mode,
315 } 357 }
316} 358}
317 359
360int ConfigureGraphics::FindIndex(u32 enumeration, int value) const {
361 for (u32 i = 0; i < combobox_translations.at(enumeration).size(); i++) {
362 if (combobox_translations.at(enumeration)[i].first == static_cast<u32>(value)) {
363 return i;
364 }
365 }
366 return -1;
367}
368
318void ConfigureGraphics::ApplyConfiguration() { 369void ConfigureGraphics::ApplyConfiguration() {
319 const auto resolution_setup = static_cast<Settings::ResolutionSetup>( 370 const bool powered_on = system.IsPoweredOn();
320 ui->resolution_combobox->currentIndex() - 371 for (const auto& func : apply_funcs) {
321 ((Settings::IsConfiguringGlobal()) ? 0 : ConfigurationShared::USE_GLOBAL_OFFSET)); 372 func(powered_on);
322 373 }
323 const auto scaling_filter = static_cast<Settings::ScalingFilter>(
324 ui->scaling_filter_combobox->currentIndex() -
325 ((Settings::IsConfiguringGlobal()) ? 0 : ConfigurationShared::USE_GLOBAL_OFFSET));
326
327 const auto anti_aliasing = static_cast<Settings::AntiAliasing>(
328 ui->anti_aliasing_combobox->currentIndex() -
329 ((Settings::IsConfiguringGlobal()) ? 0 : ConfigurationShared::USE_GLOBAL_OFFSET));
330
331 ConfigurationShared::ApplyPerGameSetting(&Settings::values.fullscreen_mode,
332 ui->fullscreen_mode_combobox);
333 ConfigurationShared::ApplyPerGameSetting(&Settings::values.aspect_ratio,
334 ui->aspect_ratio_combobox);
335 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_disk_shader_cache,
336 ui->use_disk_shader_cache, use_disk_shader_cache);
337 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_asynchronous_gpu_emulation,
338 ui->use_asynchronous_gpu_emulation,
339 use_asynchronous_gpu_emulation);
340 ConfigurationShared::ApplyPerGameSetting(&Settings::values.accelerate_astc, ui->accelerate_astc,
341 accelerate_astc);
342 374
343 if (Settings::IsConfiguringGlobal()) { 375 if (Settings::IsConfiguringGlobal()) {
344 // Guard if during game and set to game-specific value 376 const auto mode = vsync_mode_combobox_enum_map[vsync_mode_combobox->currentIndex()];
345 if (Settings::values.renderer_backend.UsingGlobal()) {
346 Settings::values.renderer_backend.SetValue(GetCurrentGraphicsBackend());
347 }
348 if (Settings::values.nvdec_emulation.UsingGlobal()) {
349 Settings::values.nvdec_emulation.SetValue(GetCurrentNvdecEmulation());
350 }
351 if (Settings::values.shader_backend.UsingGlobal()) {
352 Settings::values.shader_backend.SetValue(shader_backend);
353 }
354 if (Settings::values.vulkan_device.UsingGlobal()) {
355 Settings::values.vulkan_device.SetValue(vulkan_device);
356 }
357 if (Settings::values.bg_red.UsingGlobal()) {
358 Settings::values.bg_red.SetValue(static_cast<u8>(bg_color.red()));
359 Settings::values.bg_green.SetValue(static_cast<u8>(bg_color.green()));
360 Settings::values.bg_blue.SetValue(static_cast<u8>(bg_color.blue()));
361 }
362 if (Settings::values.resolution_setup.UsingGlobal()) {
363 Settings::values.resolution_setup.SetValue(resolution_setup);
364 }
365 if (Settings::values.scaling_filter.UsingGlobal()) {
366 Settings::values.scaling_filter.SetValue(scaling_filter);
367 }
368 if (Settings::values.anti_aliasing.UsingGlobal()) {
369 Settings::values.anti_aliasing.SetValue(anti_aliasing);
370 }
371 Settings::values.fsr_sharpening_slider.SetValue(ui->fsr_sharpening_slider->value());
372
373 const auto mode = vsync_mode_combobox_enum_map[ui->vsync_mode_combobox->currentIndex()];
374 const auto vsync_mode = PresentModeToSetting(mode); 377 const auto vsync_mode = PresentModeToSetting(mode);
375 Settings::values.vsync_mode.SetValue(vsync_mode); 378 Settings::values.vsync_mode.SetValue(vsync_mode);
376 } else { 379 }
377 if (ui->resolution_combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
378 Settings::values.resolution_setup.SetGlobal(true);
379 } else {
380 Settings::values.resolution_setup.SetGlobal(false);
381 Settings::values.resolution_setup.SetValue(resolution_setup);
382 }
383 if (ui->scaling_filter_combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
384 Settings::values.scaling_filter.SetGlobal(true);
385 } else {
386 Settings::values.scaling_filter.SetGlobal(false);
387 Settings::values.scaling_filter.SetValue(scaling_filter);
388 }
389 if (ui->anti_aliasing_combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
390 Settings::values.anti_aliasing.SetGlobal(true);
391 } else {
392 Settings::values.anti_aliasing.SetGlobal(false);
393 Settings::values.anti_aliasing.SetValue(anti_aliasing);
394 }
395 if (ui->api->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
396 Settings::values.renderer_backend.SetGlobal(true);
397 Settings::values.shader_backend.SetGlobal(true);
398 Settings::values.vulkan_device.SetGlobal(true);
399 } else {
400 Settings::values.renderer_backend.SetGlobal(false);
401 Settings::values.renderer_backend.SetValue(GetCurrentGraphicsBackend());
402 switch (GetCurrentGraphicsBackend()) {
403 case Settings::RendererBackend::OpenGL:
404 case Settings::RendererBackend::Null:
405 Settings::values.shader_backend.SetGlobal(false);
406 Settings::values.vulkan_device.SetGlobal(true);
407 Settings::values.shader_backend.SetValue(shader_backend);
408 break;
409 case Settings::RendererBackend::Vulkan:
410 Settings::values.shader_backend.SetGlobal(true);
411 Settings::values.vulkan_device.SetGlobal(false);
412 Settings::values.vulkan_device.SetValue(vulkan_device);
413 break;
414 }
415 }
416
417 if (ui->nvdec_emulation->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
418 Settings::values.nvdec_emulation.SetGlobal(true);
419 } else {
420 Settings::values.nvdec_emulation.SetGlobal(false);
421 Settings::values.nvdec_emulation.SetValue(GetCurrentNvdecEmulation());
422 }
423
424 if (ui->bg_combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
425 Settings::values.bg_red.SetGlobal(true);
426 Settings::values.bg_green.SetGlobal(true);
427 Settings::values.bg_blue.SetGlobal(true);
428 } else {
429 Settings::values.bg_red.SetGlobal(false);
430 Settings::values.bg_green.SetGlobal(false);
431 Settings::values.bg_blue.SetGlobal(false);
432 Settings::values.bg_red.SetValue(static_cast<u8>(bg_color.red()));
433 Settings::values.bg_green.SetValue(static_cast<u8>(bg_color.green()));
434 Settings::values.bg_blue.SetValue(static_cast<u8>(bg_color.blue()));
435 }
436 380
437 if (ui->fsr_sharpening_combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) { 381 Settings::values.vulkan_device.SetGlobal(true);
438 Settings::values.fsr_sharpening_slider.SetGlobal(true); 382 Settings::values.shader_backend.SetGlobal(true);
439 } else { 383 if (Settings::IsConfiguringGlobal() ||
440 Settings::values.fsr_sharpening_slider.SetGlobal(false); 384 (!Settings::IsConfiguringGlobal() && api_restore_global_button->isEnabled())) {
441 Settings::values.fsr_sharpening_slider.SetValue(ui->fsr_sharpening_slider->value()); 385 auto backend = static_cast<Settings::RendererBackend>(
386 combobox_translations
387 .at(Settings::EnumMetadata<
388 Settings::RendererBackend>::Index())[api_combobox->currentIndex()]
389 .first);
390 switch (backend) {
391 case Settings::RendererBackend::OpenGL:
392 Settings::values.shader_backend.SetGlobal(Settings::IsConfiguringGlobal());
393 Settings::values.shader_backend.SetValue(static_cast<Settings::ShaderBackend>(
394 shader_mapping[shader_backend_combobox->currentIndex()].first));
395 break;
396 case Settings::RendererBackend::Vulkan:
397 Settings::values.vulkan_device.SetGlobal(Settings::IsConfiguringGlobal());
398 Settings::values.vulkan_device.SetValue(vulkan_device_combobox->currentIndex());
399 break;
400 case Settings::RendererBackend::Null:
401 break;
442 } 402 }
443 } 403 }
444} 404}
@@ -466,36 +426,26 @@ void ConfigureGraphics::UpdateBackgroundColorButton(QColor color) {
466} 426}
467 427
468void ConfigureGraphics::UpdateAPILayout() { 428void ConfigureGraphics::UpdateAPILayout() {
469 if (!Settings::IsConfiguringGlobal() && 429 bool runtime_lock = !system.IsPoweredOn();
470 ui->api->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) { 430 bool need_global = !(Settings::IsConfiguringGlobal() || api_restore_global_button->isEnabled());
471 vulkan_device = Settings::values.vulkan_device.GetValue(true); 431 vulkan_device = Settings::values.vulkan_device.GetValue(need_global);
472 shader_backend = Settings::values.shader_backend.GetValue(true); 432 shader_backend = Settings::values.shader_backend.GetValue(need_global);
473 ui->device_widget->setEnabled(false); 433 vulkan_device_widget->setEnabled(!need_global && runtime_lock);
474 ui->backend_widget->setEnabled(false); 434 shader_backend_widget->setEnabled(!need_global && runtime_lock);
475 } else { 435
476 vulkan_device = Settings::values.vulkan_device.GetValue(); 436 const auto current_backend = GetCurrentGraphicsBackend();
477 shader_backend = Settings::values.shader_backend.GetValue(); 437 const bool is_opengl = current_backend == Settings::RendererBackend::OpenGL;
478 ui->device_widget->setEnabled(true); 438 const bool is_vulkan = current_backend == Settings::RendererBackend::Vulkan;
479 ui->backend_widget->setEnabled(true); 439
480 } 440 vulkan_device_widget->setVisible(is_vulkan);
481 441 shader_backend_widget->setVisible(is_opengl);
482 switch (GetCurrentGraphicsBackend()) { 442
483 case Settings::RendererBackend::OpenGL: 443 if (is_opengl) {
484 ui->backend->setCurrentIndex(static_cast<u32>(shader_backend)); 444 shader_backend_combobox->setCurrentIndex(
485 ui->device_widget->setVisible(false); 445 FindIndex(Settings::EnumMetadata<Settings::ShaderBackend>::Index(),
486 ui->backend_widget->setVisible(true); 446 static_cast<int>(shader_backend)));
487 break; 447 } else if (is_vulkan && static_cast<int>(vulkan_device) < vulkan_device_combobox->count()) {
488 case Settings::RendererBackend::Vulkan: 448 vulkan_device_combobox->setCurrentIndex(vulkan_device);
489 if (static_cast<int>(vulkan_device) < ui->device->count()) {
490 ui->device->setCurrentIndex(vulkan_device);
491 }
492 ui->device_widget->setVisible(true);
493 ui->backend_widget->setVisible(false);
494 break;
495 case Settings::RendererBackend::Null:
496 ui->device_widget->setVisible(false);
497 ui->backend_widget->setVisible(false);
498 break;
499 } 449 }
500} 450}
501 451
@@ -515,92 +465,11 @@ void ConfigureGraphics::RetrieveVulkanDevices() {
515} 465}
516 466
517Settings::RendererBackend ConfigureGraphics::GetCurrentGraphicsBackend() const { 467Settings::RendererBackend ConfigureGraphics::GetCurrentGraphicsBackend() const {
518 if (Settings::IsConfiguringGlobal()) { 468 if (!Settings::IsConfiguringGlobal() && !api_restore_global_button->isEnabled()) {
519 return static_cast<Settings::RendererBackend>(ui->api->currentIndex()); 469 return Settings::values.renderer_backend.GetValue(true);
520 }
521
522 if (ui->api->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
523 Settings::values.renderer_backend.SetGlobal(true);
524 return Settings::values.renderer_backend.GetValue();
525 } 470 }
526 Settings::values.renderer_backend.SetGlobal(false); 471 return static_cast<Settings::RendererBackend>(
527 return static_cast<Settings::RendererBackend>(ui->api->currentIndex() - 472 combobox_translations.at(Settings::EnumMetadata<Settings::RendererBackend>::Index())
528 ConfigurationShared::USE_GLOBAL_OFFSET); 473 .at(api_combobox->currentIndex())
529} 474 .first);
530
531Settings::NvdecEmulation ConfigureGraphics::GetCurrentNvdecEmulation() const {
532 if (Settings::IsConfiguringGlobal()) {
533 return static_cast<Settings::NvdecEmulation>(ui->nvdec_emulation->currentIndex());
534 }
535
536 if (ui->nvdec_emulation->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
537 Settings::values.nvdec_emulation.SetGlobal(true);
538 return Settings::values.nvdec_emulation.GetValue();
539 }
540 Settings::values.nvdec_emulation.SetGlobal(false);
541 return static_cast<Settings::NvdecEmulation>(ui->nvdec_emulation->currentIndex() -
542 ConfigurationShared::USE_GLOBAL_OFFSET);
543}
544
545void ConfigureGraphics::SetupPerGameUI() {
546 if (Settings::IsConfiguringGlobal()) {
547 ui->api->setEnabled(Settings::values.renderer_backend.UsingGlobal());
548 ui->device->setEnabled(Settings::values.renderer_backend.UsingGlobal());
549 ui->fullscreen_mode_combobox->setEnabled(Settings::values.fullscreen_mode.UsingGlobal());
550 ui->aspect_ratio_combobox->setEnabled(Settings::values.aspect_ratio.UsingGlobal());
551 ui->resolution_combobox->setEnabled(Settings::values.resolution_setup.UsingGlobal());
552 ui->scaling_filter_combobox->setEnabled(Settings::values.scaling_filter.UsingGlobal());
553 ui->fsr_sharpening_slider->setEnabled(Settings::values.fsr_sharpening_slider.UsingGlobal());
554 ui->anti_aliasing_combobox->setEnabled(Settings::values.anti_aliasing.UsingGlobal());
555 ui->use_asynchronous_gpu_emulation->setEnabled(
556 Settings::values.use_asynchronous_gpu_emulation.UsingGlobal());
557 ui->nvdec_emulation->setEnabled(Settings::values.nvdec_emulation.UsingGlobal());
558 ui->accelerate_astc->setEnabled(Settings::values.accelerate_astc.UsingGlobal());
559 ui->use_disk_shader_cache->setEnabled(Settings::values.use_disk_shader_cache.UsingGlobal());
560 ui->bg_button->setEnabled(Settings::values.bg_red.UsingGlobal());
561 ui->fsr_slider_layout->setEnabled(Settings::values.fsr_sharpening_slider.UsingGlobal());
562
563 return;
564 }
565
566 connect(ui->bg_combobox, qOverload<int>(&QComboBox::activated), this, [this](int index) {
567 ui->bg_button->setEnabled(index == 1);
568 ConfigurationShared::SetHighlight(ui->bg_layout, index == 1);
569 });
570
571 connect(ui->fsr_sharpening_combobox, qOverload<int>(&QComboBox::activated), this,
572 [this](int index) {
573 ui->fsr_sharpening_slider->setEnabled(index == 1);
574 ui->fsr_sharpening_value->setEnabled(index == 1);
575 ConfigurationShared::SetHighlight(ui->fsr_sharpening_layout, index == 1);
576 });
577
578 ConfigurationShared::SetColoredTristate(
579 ui->use_disk_shader_cache, Settings::values.use_disk_shader_cache, use_disk_shader_cache);
580 ConfigurationShared::SetColoredTristate(ui->accelerate_astc, Settings::values.accelerate_astc,
581 accelerate_astc);
582 ConfigurationShared::SetColoredTristate(ui->use_asynchronous_gpu_emulation,
583 Settings::values.use_asynchronous_gpu_emulation,
584 use_asynchronous_gpu_emulation);
585
586 ConfigurationShared::SetColoredComboBox(ui->aspect_ratio_combobox, ui->ar_label,
587 Settings::values.aspect_ratio.GetValue(true));
588 ConfigurationShared::SetColoredComboBox(
589 ui->fullscreen_mode_combobox, ui->fullscreen_mode_label,
590 static_cast<int>(Settings::values.fullscreen_mode.GetValue(true)));
591 ConfigurationShared::SetColoredComboBox(
592 ui->resolution_combobox, ui->resolution_label,
593 static_cast<int>(Settings::values.resolution_setup.GetValue(true)));
594 ConfigurationShared::SetColoredComboBox(
595 ui->scaling_filter_combobox, ui->scaling_filter_label,
596 static_cast<int>(Settings::values.scaling_filter.GetValue(true)));
597 ConfigurationShared::SetColoredComboBox(
598 ui->anti_aliasing_combobox, ui->anti_aliasing_label,
599 static_cast<int>(Settings::values.anti_aliasing.GetValue(true)));
600 ConfigurationShared::InsertGlobalItem(
601 ui->api, static_cast<int>(Settings::values.renderer_backend.GetValue(true)));
602 ConfigurationShared::InsertGlobalItem(
603 ui->nvdec_emulation, static_cast<int>(Settings::values.nvdec_emulation.GetValue(true)));
604
605 ui->vsync_mode_layout->setVisible(false);
606} 475}
diff --git a/src/yuzu/configuration/configure_graphics.h b/src/yuzu/configuration/configure_graphics.h
index be9310b74..02d9b00f1 100644
--- a/src/yuzu/configuration/configure_graphics.h
+++ b/src/yuzu/configuration/configure_graphics.h
@@ -5,6 +5,8 @@
5 5
6#include <functional> 6#include <functional>
7#include <memory> 7#include <memory>
8#include <type_traits>
9#include <typeindex>
8#include <vector> 10#include <vector>
9#include <QColor> 11#include <QColor>
10#include <QString> 12#include <QString>
@@ -12,10 +14,14 @@
12#include <qobjectdefs.h> 14#include <qobjectdefs.h>
13#include <vulkan/vulkan_core.h> 15#include <vulkan/vulkan_core.h>
14#include "common/common_types.h" 16#include "common/common_types.h"
17#include "configuration/shared_translation.h"
15#include "vk_device_info.h" 18#include "vk_device_info.h"
19#include "yuzu/configuration/configuration_shared.h"
16 20
21class QPushButton;
17class QEvent; 22class QEvent;
18class QObject; 23class QObject;
24class QComboBox;
19 25
20namespace Settings { 26namespace Settings {
21enum class NvdecEmulation : u32; 27enum class NvdecEmulation : u32;
@@ -27,31 +33,33 @@ namespace Core {
27class System; 33class System;
28} 34}
29 35
30namespace ConfigurationShared {
31enum class CheckState;
32}
33
34namespace Ui { 36namespace Ui {
35class ConfigureGraphics; 37class ConfigureGraphics;
36} 38}
37 39
38class ConfigureGraphics : public QWidget { 40namespace ConfigurationShared {
39 Q_OBJECT 41class Builder;
42}
40 43
44class ConfigureGraphics : public ConfigurationShared::Tab {
41public: 45public:
42 explicit ConfigureGraphics(const Core::System& system_, 46 explicit ConfigureGraphics(const Core::System& system_,
43 std::vector<VkDeviceInfo::Record>& records, 47 std::vector<VkDeviceInfo::Record>& records,
44 const std::function<void()>& expose_compute_option_, 48 const std::function<void()>& expose_compute_option_,
49 std::shared_ptr<std::vector<ConfigurationShared::Tab*>> group,
50 const ConfigurationShared::Builder& builder,
45 QWidget* parent = nullptr); 51 QWidget* parent = nullptr);
46 ~ConfigureGraphics() override; 52 ~ConfigureGraphics() override;
47 53
48 void ApplyConfiguration(); 54 void ApplyConfiguration() override;
49 void SetConfiguration(); 55 void SetConfiguration() override;
50 56
51private: 57private:
52 void changeEvent(QEvent* event) override; 58 void changeEvent(QEvent* event) override;
53 void RetranslateUI(); 59 void RetranslateUI();
54 60
61 void Setup(const ConfigurationShared::Builder& builder);
62
55 void PopulateVSyncModeSelection(); 63 void PopulateVSyncModeSelection();
56 void UpdateBackgroundColorButton(QColor color); 64 void UpdateBackgroundColorButton(QColor color);
57 void UpdateAPILayout(); 65 void UpdateAPILayout();
@@ -60,34 +68,40 @@ private:
60 68
61 void RetrieveVulkanDevices(); 69 void RetrieveVulkanDevices();
62 70
63 void SetFSRIndicatorText(int percentage);
64 /* Turns a Vulkan present mode into a textual string for a UI 71 /* Turns a Vulkan present mode into a textual string for a UI
65 * (and eventually for a human to read) */ 72 * (and eventually for a human to read) */
66 const QString TranslateVSyncMode(VkPresentModeKHR mode, 73 const QString TranslateVSyncMode(VkPresentModeKHR mode,
67 Settings::RendererBackend backend) const; 74 Settings::RendererBackend backend) const;
68 75
69 void SetupPerGameUI();
70
71 Settings::RendererBackend GetCurrentGraphicsBackend() const; 76 Settings::RendererBackend GetCurrentGraphicsBackend() const;
72 Settings::NvdecEmulation GetCurrentNvdecEmulation() const; 77
78 int FindIndex(u32 enumeration, int value) const;
73 79
74 std::unique_ptr<Ui::ConfigureGraphics> ui; 80 std::unique_ptr<Ui::ConfigureGraphics> ui;
75 QColor bg_color; 81 QColor bg_color;
76 82
77 ConfigurationShared::CheckState use_nvdec_emulation; 83 std::vector<std::function<void(bool)>> apply_funcs{};
78 ConfigurationShared::CheckState accelerate_astc;
79 ConfigurationShared::CheckState use_disk_shader_cache;
80 ConfigurationShared::CheckState use_asynchronous_gpu_emulation;
81 84
82 std::vector<VkDeviceInfo::Record>& records; 85 std::vector<VkDeviceInfo::Record>& records;
83 std::vector<QString> vulkan_devices; 86 std::vector<QString> vulkan_devices;
84 std::vector<std::vector<VkPresentModeKHR>> device_present_modes; 87 std::vector<std::vector<VkPresentModeKHR>> device_present_modes;
85 std::vector<VkPresentModeKHR> 88 std::vector<VkPresentModeKHR>
86 vsync_mode_combobox_enum_map; //< Keeps track of which present mode corresponds to which 89 vsync_mode_combobox_enum_map{}; //< Keeps track of which present mode corresponds to which
87 // selection in the combobox 90 // selection in the combobox
88 u32 vulkan_device{}; 91 u32 vulkan_device{};
89 Settings::ShaderBackend shader_backend{}; 92 Settings::ShaderBackend shader_backend{};
90 const std::function<void()>& expose_compute_option; 93 const std::function<void()>& expose_compute_option;
91 94
92 const Core::System& system; 95 const Core::System& system;
96 const ConfigurationShared::ComboboxTranslationMap& combobox_translations;
97 const std::vector<std::pair<u32, QString>>& shader_mapping;
98
99 QPushButton* api_restore_global_button;
100 QComboBox* vulkan_device_combobox;
101 QComboBox* api_combobox;
102 QComboBox* shader_backend_combobox;
103 QComboBox* vsync_mode_combobox;
104 QWidget* vulkan_device_widget;
105 QWidget* api_widget;
106 QWidget* shader_backend_widget;
93}; 107};
diff --git a/src/yuzu/configuration/configure_graphics.ui b/src/yuzu/configuration/configure_graphics.ui
index 39f70e406..d09415d70 100644
--- a/src/yuzu/configuration/configure_graphics.ui
+++ b/src/yuzu/configuration/configure_graphics.ui
@@ -27,7 +27,7 @@
27 <layout class="QVBoxLayout" name="verticalLayout_3"> 27 <layout class="QVBoxLayout" name="verticalLayout_3">
28 <item> 28 <item>
29 <widget class="QWidget" name="api_widget" native="true"> 29 <widget class="QWidget" name="api_widget" native="true">
30 <layout class="QGridLayout" name="gridLayout"> 30 <layout class="QHBoxLayout" name="horizontalLayout">
31 <property name="leftMargin"> 31 <property name="leftMargin">
32 <number>0</number> 32 <number>0</number>
33 </property> 33 </property>
@@ -40,115 +40,6 @@
40 <property name="bottomMargin"> 40 <property name="bottomMargin">
41 <number>0</number> 41 <number>0</number>
42 </property> 42 </property>
43 <property name="horizontalSpacing">
44 <number>6</number>
45 </property>
46 <item row="4" column="0">
47 <widget class="QWidget" name="backend_widget" native="true">
48 <layout class="QHBoxLayout" name="backend_layout">
49 <property name="leftMargin">
50 <number>0</number>
51 </property>
52 <property name="topMargin">
53 <number>0</number>
54 </property>
55 <property name="rightMargin">
56 <number>0</number>
57 </property>
58 <property name="bottomMargin">
59 <number>0</number>
60 </property>
61 <item>
62 <widget class="QLabel" name="backend_label">
63 <property name="text">
64 <string>Shader Backend:</string>
65 </property>
66 </widget>
67 </item>
68 <item>
69 <widget class="QComboBox" name="backend"/>
70 </item>
71 </layout>
72 </widget>
73 </item>
74 <item row="2" column="0">
75 <widget class="QWidget" name="device_widget" native="true">
76 <layout class="QHBoxLayout" name="device_layout">
77 <property name="leftMargin">
78 <number>0</number>
79 </property>
80 <property name="topMargin">
81 <number>0</number>
82 </property>
83 <property name="rightMargin">
84 <number>0</number>
85 </property>
86 <property name="bottomMargin">
87 <number>0</number>
88 </property>
89 <item>
90 <widget class="QLabel" name="device_label">
91 <property name="text">
92 <string>Device:</string>
93 </property>
94 </widget>
95 </item>
96 <item>
97 <widget class="QComboBox" name="device"/>
98 </item>
99 </layout>
100 </widget>
101 </item>
102 <item row="0" column="0">
103 <widget class="QWidget" name="api_layout_2" native="true">
104 <layout class="QHBoxLayout" name="api_layout">
105 <property name="leftMargin">
106 <number>0</number>
107 </property>
108 <property name="topMargin">
109 <number>0</number>
110 </property>
111 <property name="rightMargin">
112 <number>0</number>
113 </property>
114 <property name="bottomMargin">
115 <number>0</number>
116 </property>
117 <item>
118 <widget class="QLabel" name="api_label">
119 <property name="text">
120 <string>API:</string>
121 </property>
122 </widget>
123 </item>
124 <item>
125 <widget class="QComboBox" name="api">
126 <property name="sizePolicy">
127 <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
128 <horstretch>0</horstretch>
129 <verstretch>0</verstretch>
130 </sizepolicy>
131 </property>
132 <item>
133 <property name="text">
134 <string notr="true">OpenGL</string>
135 </property>
136 </item>
137 <item>
138 <property name="text">
139 <string notr="true">Vulkan</string>
140 </property>
141 </item>
142 <item>
143 <property name="text">
144 <string>None</string>
145 </property>
146 </item>
147 </widget>
148 </item>
149 </layout>
150 </widget>
151 </item>
152 </layout> 43 </layout>
153 </widget> 44 </widget>
154 </item> 45 </item>
@@ -168,111 +59,8 @@
168 </property> 59 </property>
169 <layout class="QVBoxLayout" name="verticalLayout_4"> 60 <layout class="QVBoxLayout" name="verticalLayout_4">
170 <item> 61 <item>
171 <widget class="QCheckBox" name="use_disk_shader_cache"> 62 <widget class="QWidget" name="graphics_widget" native="true">
172 <property name="text"> 63 <layout class="QVBoxLayout" name="verticalLayout">
173 <string>Use disk pipeline cache</string>
174 </property>
175 </widget>
176 </item>
177 <item>
178 <widget class="QCheckBox" name="use_asynchronous_gpu_emulation">
179 <property name="text">
180 <string>Use asynchronous GPU emulation</string>
181 </property>
182 </widget>
183 </item>
184 <item>
185 <widget class="QCheckBox" name="accelerate_astc">
186 <property name="text">
187 <string>Accelerate ASTC texture decoding</string>
188 </property>
189 </widget>
190 </item>
191 <item>
192 <widget class="QWidget" name="vsync_mode_layout" native="true">
193 <layout class="QHBoxLayout" name="horizontalLayout_4">
194 <property name="leftMargin">
195 <number>0</number>
196 </property>
197 <property name="topMargin">
198 <number>0</number>
199 </property>
200 <property name="rightMargin">
201 <number>0</number>
202 </property>
203 <property name="bottomMargin">
204 <number>0</number>
205 </property>
206 <item>
207 <widget class="QLabel" name="vsync_mode_label">
208 <property name="text">
209 <string>VSync Mode:</string>
210 </property>
211 </widget>
212 </item>
213 <item>
214 <widget class="QComboBox" name="vsync_mode_combobox">
215 <property name="toolTip">
216 <string>FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen refresh rate.
217FIFO Relaxed is similar to FIFO but allows tearing as it recovers from a slow down.
218Mailbox can have lower latency than FIFO and does not tear but may drop frames.
219Immediate (no synchronization) just presents whatever is available and can exhibit tearing.</string>
220 </property>
221 <property name="currentText">
222 <string/>
223 </property>
224 </widget>
225 </item>
226 </layout>
227 </widget>
228 </item>
229 <item>
230 <widget class="QWidget" name="nvdec_emulation_widget" native="true">
231 <layout class="QHBoxLayout" name="nvdec_emulation_layout">
232 <property name="leftMargin">
233 <number>0</number>
234 </property>
235 <property name="topMargin">
236 <number>0</number>
237 </property>
238 <property name="rightMargin">
239 <number>0</number>
240 </property>
241 <property name="bottomMargin">
242 <number>0</number>
243 </property>
244 <item>
245 <widget class="QLabel" name="nvdec_emulation_label">
246 <property name="text">
247 <string>NVDEC emulation:</string>
248 </property>
249 </widget>
250 </item>
251 <item>
252 <widget class="QComboBox" name="nvdec_emulation">
253 <item>
254 <property name="text">
255 <string>No Video Output</string>
256 </property>
257 </item>
258 <item>
259 <property name="text">
260 <string>CPU Video Decoding</string>
261 </property>
262 </item>
263 <item>
264 <property name="text">
265 <string>GPU Video Decoding (Default)</string>
266 </property>
267 </item>
268 </widget>
269 </item>
270 </layout>
271 </widget>
272 </item>
273 <item>
274 <widget class="QWidget" name="fullscreen_mode_layout" native="true">
275 <layout class="QHBoxLayout" name="horizontalLayout_1">
276 <property name="leftMargin"> 64 <property name="leftMargin">
277 <number>0</number> 65 <number>0</number>
278 </property> 66 </property>
@@ -285,33 +73,12 @@ Immediate (no synchronization) just presents whatever is available and can exhib
285 <property name="bottomMargin"> 73 <property name="bottomMargin">
286 <number>0</number> 74 <number>0</number>
287 </property> 75 </property>
288 <item>
289 <widget class="QLabel" name="fullscreen_mode_label">
290 <property name="text">
291 <string>Fullscreen Mode:</string>
292 </property>
293 </widget>
294 </item>
295 <item>
296 <widget class="QComboBox" name="fullscreen_mode_combobox">
297 <item>
298 <property name="text">
299 <string>Borderless Windowed</string>
300 </property>
301 </item>
302 <item>
303 <property name="text">
304 <string>Exclusive Fullscreen</string>
305 </property>
306 </item>
307 </widget>
308 </item>
309 </layout> 76 </layout>
310 </widget> 77 </widget>
311 </item> 78 </item>
312 <item> 79 <item>
313 <widget class="QWidget" name="aspect_ratio_layout" native="true"> 80 <widget class="QWidget" name="bg_widget" native="true">
314 <layout class="QHBoxLayout" name="horizontalLayout_2"> 81 <layout class="QHBoxLayout" name="bg_layout">
315 <property name="leftMargin"> 82 <property name="leftMargin">
316 <number>0</number> 83 <number>0</number>
317 </property> 84 </property>
@@ -325,452 +92,35 @@ Immediate (no synchronization) just presents whatever is available and can exhib
325 <number>0</number> 92 <number>0</number>
326 </property> 93 </property>
327 <item> 94 <item>
328 <widget class="QLabel" name="ar_label"> 95 <widget class="QLabel" name="label">
329 <property name="text"> 96 <property name="sizePolicy">
330 <string>Aspect Ratio:</string> 97 <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
98 <horstretch>0</horstretch>
99 <verstretch>0</verstretch>
100 </sizepolicy>
331 </property> 101 </property>
332 </widget>
333 </item>
334 <item>
335 <widget class="QComboBox" name="aspect_ratio_combobox">
336 <item>
337 <property name="text">
338 <string>Default (16:9)</string>
339 </property>
340 </item>
341 <item>
342 <property name="text">
343 <string>Force 4:3</string>
344 </property>
345 </item>
346 <item>
347 <property name="text">
348 <string>Force 21:9</string>
349 </property>
350 </item>
351 <item>
352 <property name="text">
353 <string>Force 16:10</string>
354 </property>
355 </item>
356 <item>
357 <property name="text">
358 <string>Stretch to Window</string>
359 </property>
360 </item>
361 </widget>
362 </item>
363 </layout>
364 </widget>
365 </item>
366 <item>
367 <widget class="QWidget" name="resolution_layout" native="true">
368 <layout class="QHBoxLayout" name="horizontalLayout_5">
369 <property name="leftMargin">
370 <number>0</number>
371 </property>
372 <property name="topMargin">
373 <number>0</number>
374 </property>
375 <property name="rightMargin">
376 <number>0</number>
377 </property>
378 <property name="bottomMargin">
379 <number>0</number>
380 </property>
381 <item>
382 <widget class="QLabel" name="resolution_label">
383 <property name="text">
384 <string>Resolution:</string>
385 </property>
386 </widget>
387 </item>
388 <item>
389 <widget class="QComboBox" name="resolution_combobox">
390 <item>
391 <property name="text">
392 <string>0.5X (360p/540p) [EXPERIMENTAL]</string>
393 </property>
394 </item>
395 <item>
396 <property name="text">
397 <string>0.75X (540p/810p) [EXPERIMENTAL]</string>
398 </property>
399 </item>
400 <item>
401 <property name="text">
402 <string>1X (720p/1080p)</string>
403 </property>
404 </item>
405 <item>
406 <property name="text">
407 <string>1.5X (1080p/1620p) [EXPERIMENTAL]</string>
408 </property>
409 </item>
410 <item>
411 <property name="text">
412 <string>2X (1440p/2160p)</string>
413 </property>
414 </item>
415 <item>
416 <property name="text">
417 <string>3X (2160p/3240p)</string>
418 </property>
419 </item>
420 <item>
421 <property name="text">
422 <string>4X (2880p/4320p)</string>
423 </property>
424 </item>
425 <item>
426 <property name="text">
427 <string>5X (3600p/5400p)</string>
428 </property>
429 </item>
430 <item>
431 <property name="text">
432 <string>6X (4320p/6480p)</string>
433 </property>
434 </item>
435 <item>
436 <property name="text">
437 <string>7X (5040p/7560p)</string>
438 </property>
439 </item>
440 <item>
441 <property name="text">
442 <string>8X (5760p/8640p)</string>
443 </property>
444 </item>
445 </widget>
446 </item>
447 </layout>
448 </widget>
449 </item>
450 <item>
451 <widget class="QWidget" name="scaling_filter_layout" native="true">
452 <layout class="QHBoxLayout" name="horizontalLayout_6">
453 <property name="leftMargin">
454 <number>0</number>
455 </property>
456 <property name="topMargin">
457 <number>0</number>
458 </property>
459 <property name="rightMargin">
460 <number>0</number>
461 </property>
462 <property name="bottomMargin">
463 <number>0</number>
464 </property>
465 <item>
466 <widget class="QLabel" name="scaling_filter_label">
467 <property name="text">
468 <string>Window Adapting Filter:</string>
469 </property>
470 </widget>
471 </item>
472 <item>
473 <widget class="QComboBox" name="scaling_filter_combobox">
474 <item>
475 <property name="text">
476 <string>Nearest Neighbor</string>
477 </property>
478 </item>
479 <item>
480 <property name="text">
481 <string>Bilinear</string>
482 </property>
483 </item>
484 <item>
485 <property name="text">
486 <string>Bicubic</string>
487 </property>
488 </item>
489 <item>
490 <property name="text">
491 <string>Gaussian</string>
492 </property>
493 </item>
494 <item>
495 <property name="text">
496 <string>ScaleForce</string>
497 </property>
498 </item>
499 <item>
500 <property name="text">
501 <string>AMD FidelityFX™️ Super Resolution</string>
502 </property>
503 </item>
504 </widget>
505 </item>
506 </layout>
507 </widget>
508 </item>
509 <item>
510 <widget class="QWidget" name="anti_aliasing_layout" native="true">
511 <layout class="QHBoxLayout" name="horizontalLayout_7">
512 <property name="leftMargin">
513 <number>0</number>
514 </property>
515 <property name="topMargin">
516 <number>0</number>
517 </property>
518 <property name="rightMargin">
519 <number>0</number>
520 </property>
521 <property name="bottomMargin">
522 <number>0</number>
523 </property>
524 <item>
525 <widget class="QLabel" name="anti_aliasing_label">
526 <property name="text">
527 <string>Anti-Aliasing Method:</string>
528 </property>
529 </widget>
530 </item>
531 <item>
532 <widget class="QComboBox" name="anti_aliasing_combobox">
533 <item>
534 <property name="text">
535 <string>None</string>
536 </property>
537 </item>
538 <item>
539 <property name="text">
540 <string>FXAA</string>
541 </property>
542 </item>
543 <item>
544 <property name="text">
545 <string>SMAA</string>
546 </property>
547 </item>
548 </widget>
549 </item>
550 </layout>
551 </widget>
552 </item>
553 <item>
554 <widget class="QWidget" name="fsr_sharpening_layout" native="true">
555 <property name="enabled">
556 <bool>true</bool>
557 </property>
558 <property name="sizePolicy">
559 <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
560 <horstretch>0</horstretch>
561 <verstretch>0</verstretch>
562 </sizepolicy>
563 </property>
564 <layout class="QHBoxLayout" name="horizontalLayout">
565 <property name="spacing">
566 <number>6</number>
567 </property>
568 <property name="sizeConstraint">
569 <enum>QLayout::SetDefaultConstraint</enum>
570 </property>
571 <property name="leftMargin">
572 <number>0</number>
573 </property>
574 <property name="topMargin">
575 <number>0</number>
576 </property>
577 <property name="rightMargin">
578 <number>0</number>
579 </property>
580 <property name="bottomMargin">
581 <number>0</number>
582 </property>
583 <item>
584 <layout class="QHBoxLayout" name="fsr_sharpening_label_group">
585 <property name="rightMargin">
586 <number>0</number>
587 </property>
588 <property name="bottomMargin">
589 <number>0</number>
590 </property>
591 <item>
592 <widget class="QComboBox" name="fsr_sharpening_combobox">
593 <property name="sizePolicy">
594 <sizepolicy hsizetype="Maximum" vsizetype="Fixed">
595 <horstretch>0</horstretch>
596 <verstretch>0</verstretch>
597 </sizepolicy>
598 </property>
599 <item>
600 <property name="text">
601 <string>Use global FSR Sharpness</string>
602 </property>
603 </item>
604 <item>
605 <property name="text">
606 <string>Set FSR Sharpness</string>
607 </property>
608 </item>
609 </widget>
610 </item>
611 <item>
612 <widget class="QLabel" name="fsr_sharpening_label">
613 <property name="sizePolicy">
614 <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
615 <horstretch>0</horstretch>
616 <verstretch>0</verstretch>
617 </sizepolicy>
618 </property>
619 <property name="text">
620 <string>FSR Sharpness:</string>
621 </property>
622 </widget>
623 </item>
624 <item>
625 <spacer name="horizontalSpacer_2">
626 <property name="orientation">
627 <enum>Qt::Horizontal</enum>
628 </property>
629 <property name="sizeHint" stdset="0">
630 <size>
631 <width>40</width>
632 <height>20</height>
633 </size>
634 </property>
635 </spacer>
636 </item>
637 </layout>
638 </item>
639 <item>
640 <layout class="QHBoxLayout" name="fsr_slider_layout">
641 <property name="spacing">
642 <number>6</number>
643 </property>
644 <item>
645 <widget class="QSlider" name="fsr_sharpening_slider">
646 <property name="sizePolicy">
647 <sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
648 <horstretch>0</horstretch>
649 <verstretch>0</verstretch>
650 </sizepolicy>
651 </property>
652 <property name="baseSize">
653 <size>
654 <width>0</width>
655 <height>0</height>
656 </size>
657 </property>
658 <property name="maximum">
659 <number>200</number>
660 </property>
661 <property name="sliderPosition">
662 <number>25</number>
663 </property>
664 <property name="orientation">
665 <enum>Qt::Horizontal</enum>
666 </property>
667 <property name="invertedAppearance">
668 <bool>true</bool>
669 </property>
670 </widget>
671 </item>
672 <item>
673 <widget class="QLabel" name="fsr_sharpening_value">
674 <property name="sizePolicy">
675 <sizepolicy hsizetype="Maximum" vsizetype="Preferred">
676 <horstretch>0</horstretch>
677 <verstretch>0</verstretch>
678 </sizepolicy>
679 </property>
680 <property name="minimumSize">
681 <size>
682 <width>32</width>
683 <height>0</height>
684 </size>
685 </property>
686 <property name="text">
687 <string>100%</string>
688 </property>
689 <property name="alignment">
690 <set>Qt::AlignCenter</set>
691 </property>
692 </widget>
693 </item>
694 </layout>
695 </item>
696 </layout>
697 </widget>
698 </item>
699 <item>
700 <widget class="QWidget" name="bg_layout" native="true">
701 <property name="sizePolicy">
702 <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
703 <horstretch>0</horstretch>
704 <verstretch>0</verstretch>
705 </sizepolicy>
706 </property>
707 <layout class="QHBoxLayout" name="horizontalLayout_3">
708 <property name="spacing">
709 <number>6</number>
710 </property>
711 <property name="leftMargin">
712 <number>0</number>
713 </property>
714 <property name="topMargin">
715 <number>0</number>
716 </property>
717 <property name="rightMargin">
718 <number>0</number>
719 </property>
720 <property name="bottomMargin">
721 <number>0</number>
722 </property>
723 <item>
724 <widget class="QComboBox" name="bg_combobox">
725 <property name="currentText">
726 <string>Use global background color</string>
727 </property>
728 <property name="currentIndex">
729 <number>0</number>
730 </property>
731 <property name="maxVisibleItems">
732 <number>10</number>
733 </property>
734 <item>
735 <property name="text">
736 <string>Use global background color</string>
737 </property>
738 </item>
739 <item>
740 <property name="text">
741 <string>Set background color:</string>
742 </property>
743 </item>
744 </widget>
745 </item>
746 <item>
747 <widget class="QLabel" name="bg_label">
748 <property name="text"> 102 <property name="text">
749 <string>Background Color:</string> 103 <string>Background Color:</string>
750 </property> 104 </property>
751 </widget> 105 </widget>
752 </item> 106 </item>
753 <item> 107 <item>
754 <spacer name="horizontalSpacer">
755 <property name="orientation">
756 <enum>Qt::Horizontal</enum>
757 </property>
758 <property name="sizeHint" stdset="0">
759 <size>
760 <width>40</width>
761 <height>20</height>
762 </size>
763 </property>
764 </spacer>
765 </item>
766 <item>
767 <widget class="QPushButton" name="bg_button"> 108 <widget class="QPushButton" name="bg_button">
109 <property name="sizePolicy">
110 <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
111 <horstretch>0</horstretch>
112 <verstretch>0</verstretch>
113 </sizepolicy>
114 </property>
768 <property name="maximumSize"> 115 <property name="maximumSize">
769 <size> 116 <size>
770 <width>40</width> 117 <width>40</width>
771 <height>16777215</height> 118 <height>16777215</height>
772 </size> 119 </size>
773 </property> 120 </property>
121 <property name="text">
122 <string/>
123 </property>
774 </widget> 124 </widget>
775 </item> 125 </item>
776 </layout> 126 </layout>
diff --git a/src/yuzu/configuration/configure_graphics_advanced.cpp b/src/yuzu/configuration/configure_graphics_advanced.cpp
index c0a044767..4db18673d 100644
--- a/src/yuzu/configuration/configure_graphics_advanced.cpp
+++ b/src/yuzu/configuration/configure_graphics_advanced.cpp
@@ -1,104 +1,68 @@
1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <vector>
5#include <QLabel>
6#include <qnamespace.h>
4#include "common/settings.h" 7#include "common/settings.h"
5#include "core/core.h" 8#include "core/core.h"
6#include "ui_configure_graphics_advanced.h" 9#include "ui_configure_graphics_advanced.h"
7#include "yuzu/configuration/configuration_shared.h" 10#include "yuzu/configuration/configuration_shared.h"
8#include "yuzu/configuration/configure_graphics_advanced.h" 11#include "yuzu/configuration/configure_graphics_advanced.h"
12#include "yuzu/configuration/shared_translation.h"
13#include "yuzu/configuration/shared_widget.h"
9 14
10ConfigureGraphicsAdvanced::ConfigureGraphicsAdvanced(const Core::System& system_, QWidget* parent) 15ConfigureGraphicsAdvanced::ConfigureGraphicsAdvanced(
11 : QWidget(parent), ui{std::make_unique<Ui::ConfigureGraphicsAdvanced>()}, system{system_} { 16 const Core::System& system_, std::shared_ptr<std::vector<ConfigurationShared::Tab*>> group_,
17 const ConfigurationShared::Builder& builder, QWidget* parent)
18 : Tab(group_, parent), ui{std::make_unique<Ui::ConfigureGraphicsAdvanced>()}, system{system_} {
12 19
13 ui->setupUi(this); 20 ui->setupUi(this);
14 21
15 SetupPerGameUI(); 22 Setup(builder);
16 23
17 SetConfiguration(); 24 SetConfiguration();
18 25
19 ui->enable_compute_pipelines_checkbox->setVisible(false); 26 checkbox_enable_compute_pipelines->setVisible(false);
20} 27}
21 28
22ConfigureGraphicsAdvanced::~ConfigureGraphicsAdvanced() = default; 29ConfigureGraphicsAdvanced::~ConfigureGraphicsAdvanced() = default;
23 30
24void ConfigureGraphicsAdvanced::SetConfiguration() { 31void ConfigureGraphicsAdvanced::SetConfiguration() {}
25 const bool runtime_lock = !system.IsPoweredOn(); 32
26 ui->use_reactive_flushing->setEnabled(runtime_lock); 33void ConfigureGraphicsAdvanced::Setup(const ConfigurationShared::Builder& builder) {
27 ui->async_present->setEnabled(runtime_lock); 34 auto& layout = *ui->populate_target->layout();
28 ui->renderer_force_max_clock->setEnabled(runtime_lock); 35 std::map<u32, QWidget*> hold{}; // A map will sort the data for us
29 ui->async_astc->setEnabled(runtime_lock); 36
30 ui->astc_recompression_combobox->setEnabled(runtime_lock); 37 for (auto setting :
31 ui->use_asynchronous_shaders->setEnabled(runtime_lock); 38 Settings::values.linkage.by_category[Settings::Category::RendererAdvanced]) {
32 ui->anisotropic_filtering_combobox->setEnabled(runtime_lock); 39 ConfigurationShared::Widget* widget = builder.BuildWidget(setting, apply_funcs);
33 ui->enable_compute_pipelines_checkbox->setEnabled(runtime_lock); 40
34 41 if (widget == nullptr) {
35 ui->async_present->setChecked(Settings::values.async_presentation.GetValue()); 42 continue;
36 ui->renderer_force_max_clock->setChecked(Settings::values.renderer_force_max_clock.GetValue()); 43 }
37 ui->use_reactive_flushing->setChecked(Settings::values.use_reactive_flushing.GetValue()); 44 if (!widget->Valid()) {
38 ui->async_astc->setChecked(Settings::values.async_astc.GetValue()); 45 widget->deleteLater();
39 ui->use_asynchronous_shaders->setChecked(Settings::values.use_asynchronous_shaders.GetValue()); 46 continue;
40 ui->use_fast_gpu_time->setChecked(Settings::values.use_fast_gpu_time.GetValue()); 47 }
41 ui->use_vulkan_driver_pipeline_cache->setChecked( 48
42 Settings::values.use_vulkan_driver_pipeline_cache.GetValue()); 49 hold.emplace(setting->Id(), widget);
43 ui->enable_compute_pipelines_checkbox->setChecked( 50
44 Settings::values.enable_compute_pipelines.GetValue()); 51 // Keep track of enable_compute_pipelines so we can display it when needed
45 ui->use_video_framerate_checkbox->setChecked(Settings::values.use_video_framerate.GetValue()); 52 if (setting->Id() == Settings::values.enable_compute_pipelines.Id()) {
46 ui->barrier_feedback_loops_checkbox->setChecked( 53 checkbox_enable_compute_pipelines = widget;
47 Settings::values.barrier_feedback_loops.GetValue()); 54 }
48 55 }
49 if (Settings::IsConfiguringGlobal()) { 56 for (const auto& [id, widget] : hold) {
50 ui->gpu_accuracy->setCurrentIndex( 57 layout.addWidget(widget);
51 static_cast<int>(Settings::values.gpu_accuracy.GetValue()));
52 ui->anisotropic_filtering_combobox->setCurrentIndex(
53 Settings::values.max_anisotropy.GetValue());
54 ui->astc_recompression_combobox->setCurrentIndex(
55 static_cast<int>(Settings::values.astc_recompression.GetValue()));
56 } else {
57 ConfigurationShared::SetPerGameSetting(ui->gpu_accuracy, &Settings::values.gpu_accuracy);
58 ConfigurationShared::SetPerGameSetting(ui->anisotropic_filtering_combobox,
59 &Settings::values.max_anisotropy);
60 ConfigurationShared::SetPerGameSetting(ui->astc_recompression_combobox,
61 &Settings::values.astc_recompression);
62 ConfigurationShared::SetHighlight(ui->label_gpu_accuracy,
63 !Settings::values.gpu_accuracy.UsingGlobal());
64 ConfigurationShared::SetHighlight(ui->af_label,
65 !Settings::values.max_anisotropy.UsingGlobal());
66 ConfigurationShared::SetHighlight(ui->label_astc_recompression,
67 !Settings::values.astc_recompression.UsingGlobal());
68 } 58 }
69} 59}
70 60
71void ConfigureGraphicsAdvanced::ApplyConfiguration() { 61void ConfigureGraphicsAdvanced::ApplyConfiguration() {
72 ConfigurationShared::ApplyPerGameSetting(&Settings::values.gpu_accuracy, ui->gpu_accuracy); 62 const bool is_powered_on = system.IsPoweredOn();
73 ConfigurationShared::ApplyPerGameSetting(&Settings::values.async_presentation, 63 for (const auto& func : apply_funcs) {
74 ui->async_present, async_present); 64 func(is_powered_on);
75 ConfigurationShared::ApplyPerGameSetting(&Settings::values.renderer_force_max_clock, 65 }
76 ui->renderer_force_max_clock,
77 renderer_force_max_clock);
78 ConfigurationShared::ApplyPerGameSetting(&Settings::values.max_anisotropy,
79 ui->anisotropic_filtering_combobox);
80 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_reactive_flushing,
81 ui->use_reactive_flushing, use_reactive_flushing);
82 ConfigurationShared::ApplyPerGameSetting(&Settings::values.async_astc, ui->async_astc,
83 async_astc);
84 ConfigurationShared::ApplyPerGameSetting(&Settings::values.astc_recompression,
85 ui->astc_recompression_combobox);
86 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_asynchronous_shaders,
87 ui->use_asynchronous_shaders,
88 use_asynchronous_shaders);
89 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_fast_gpu_time,
90 ui->use_fast_gpu_time, use_fast_gpu_time);
91 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_vulkan_driver_pipeline_cache,
92 ui->use_vulkan_driver_pipeline_cache,
93 use_vulkan_driver_pipeline_cache);
94 ConfigurationShared::ApplyPerGameSetting(&Settings::values.enable_compute_pipelines,
95 ui->enable_compute_pipelines_checkbox,
96 enable_compute_pipelines);
97 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_video_framerate,
98 ui->use_video_framerate_checkbox, use_video_framerate);
99 ConfigurationShared::ApplyPerGameSetting(&Settings::values.barrier_feedback_loops,
100 ui->barrier_feedback_loops_checkbox,
101 barrier_feedback_loops);
102} 66}
103 67
104void ConfigureGraphicsAdvanced::changeEvent(QEvent* event) { 68void ConfigureGraphicsAdvanced::changeEvent(QEvent* event) {
@@ -113,71 +77,6 @@ void ConfigureGraphicsAdvanced::RetranslateUI() {
113 ui->retranslateUi(this); 77 ui->retranslateUi(this);
114} 78}
115 79
116void ConfigureGraphicsAdvanced::SetupPerGameUI() {
117 // Disable if not global (only happens during game)
118 if (Settings::IsConfiguringGlobal()) {
119 ui->gpu_accuracy->setEnabled(Settings::values.gpu_accuracy.UsingGlobal());
120 ui->async_present->setEnabled(Settings::values.async_presentation.UsingGlobal());
121 ui->renderer_force_max_clock->setEnabled(
122 Settings::values.renderer_force_max_clock.UsingGlobal());
123 ui->use_reactive_flushing->setEnabled(Settings::values.use_reactive_flushing.UsingGlobal());
124 ui->async_astc->setEnabled(Settings::values.async_astc.UsingGlobal());
125 ui->astc_recompression_combobox->setEnabled(
126 Settings::values.astc_recompression.UsingGlobal());
127 ui->use_asynchronous_shaders->setEnabled(
128 Settings::values.use_asynchronous_shaders.UsingGlobal());
129 ui->use_fast_gpu_time->setEnabled(Settings::values.use_fast_gpu_time.UsingGlobal());
130 ui->use_vulkan_driver_pipeline_cache->setEnabled(
131 Settings::values.use_vulkan_driver_pipeline_cache.UsingGlobal());
132 ui->anisotropic_filtering_combobox->setEnabled(
133 Settings::values.max_anisotropy.UsingGlobal());
134 ui->enable_compute_pipelines_checkbox->setEnabled(
135 Settings::values.enable_compute_pipelines.UsingGlobal());
136 ui->use_video_framerate_checkbox->setEnabled(
137 Settings::values.use_video_framerate.UsingGlobal());
138 ui->barrier_feedback_loops_checkbox->setEnabled(
139 Settings::values.barrier_feedback_loops.UsingGlobal());
140
141 return;
142 }
143
144 ConfigurationShared::SetColoredTristate(ui->async_present, Settings::values.async_presentation,
145 async_present);
146 ConfigurationShared::SetColoredTristate(ui->renderer_force_max_clock,
147 Settings::values.renderer_force_max_clock,
148 renderer_force_max_clock);
149 ConfigurationShared::SetColoredTristate(
150 ui->use_reactive_flushing, Settings::values.use_reactive_flushing, use_reactive_flushing);
151 ConfigurationShared::SetColoredTristate(ui->async_astc, Settings::values.async_astc,
152 async_astc);
153 ConfigurationShared::SetColoredTristate(ui->use_asynchronous_shaders,
154 Settings::values.use_asynchronous_shaders,
155 use_asynchronous_shaders);
156 ConfigurationShared::SetColoredTristate(ui->use_fast_gpu_time,
157 Settings::values.use_fast_gpu_time, use_fast_gpu_time);
158 ConfigurationShared::SetColoredTristate(ui->use_vulkan_driver_pipeline_cache,
159 Settings::values.use_vulkan_driver_pipeline_cache,
160 use_vulkan_driver_pipeline_cache);
161 ConfigurationShared::SetColoredTristate(ui->enable_compute_pipelines_checkbox,
162 Settings::values.enable_compute_pipelines,
163 enable_compute_pipelines);
164 ConfigurationShared::SetColoredTristate(ui->use_video_framerate_checkbox,
165 Settings::values.use_video_framerate,
166 use_video_framerate);
167 ConfigurationShared::SetColoredTristate(ui->barrier_feedback_loops_checkbox,
168 Settings::values.barrier_feedback_loops,
169 barrier_feedback_loops);
170 ConfigurationShared::SetColoredComboBox(
171 ui->gpu_accuracy, ui->label_gpu_accuracy,
172 static_cast<int>(Settings::values.gpu_accuracy.GetValue(true)));
173 ConfigurationShared::SetColoredComboBox(
174 ui->anisotropic_filtering_combobox, ui->af_label,
175 static_cast<int>(Settings::values.max_anisotropy.GetValue(true)));
176 ConfigurationShared::SetColoredComboBox(
177 ui->astc_recompression_combobox, ui->label_astc_recompression,
178 static_cast<int>(Settings::values.astc_recompression.GetValue(true)));
179}
180
181void ConfigureGraphicsAdvanced::ExposeComputeOption() { 80void ConfigureGraphicsAdvanced::ExposeComputeOption() {
182 ui->enable_compute_pipelines_checkbox->setVisible(true); 81 checkbox_enable_compute_pipelines->setVisible(true);
183} 82}
diff --git a/src/yuzu/configuration/configure_graphics_advanced.h b/src/yuzu/configuration/configure_graphics_advanced.h
index 369a7c83e..78b5389c3 100644
--- a/src/yuzu/configuration/configure_graphics_advanced.h
+++ b/src/yuzu/configuration/configure_graphics_advanced.h
@@ -4,51 +4,44 @@
4#pragma once 4#pragma once
5 5
6#include <memory> 6#include <memory>
7#include <vector>
7#include <QWidget> 8#include <QWidget>
9#include "yuzu/configuration/configuration_shared.h"
8 10
9namespace Core { 11namespace Core {
10class System; 12class System;
11} 13}
12 14
13namespace ConfigurationShared {
14enum class CheckState;
15}
16
17namespace Ui { 15namespace Ui {
18class ConfigureGraphicsAdvanced; 16class ConfigureGraphicsAdvanced;
19} 17}
20 18
21class ConfigureGraphicsAdvanced : public QWidget { 19namespace ConfigurationShared {
22 Q_OBJECT 20class Builder;
21}
23 22
23class ConfigureGraphicsAdvanced : public ConfigurationShared::Tab {
24public: 24public:
25 explicit ConfigureGraphicsAdvanced(const Core::System& system_, QWidget* parent = nullptr); 25 explicit ConfigureGraphicsAdvanced(
26 const Core::System& system_, std::shared_ptr<std::vector<ConfigurationShared::Tab*>> group,
27 const ConfigurationShared::Builder& builder, QWidget* parent = nullptr);
26 ~ConfigureGraphicsAdvanced() override; 28 ~ConfigureGraphicsAdvanced() override;
27 29
28 void ApplyConfiguration(); 30 void ApplyConfiguration() override;
29 void SetConfiguration(); 31 void SetConfiguration() override;
30 32
31 void ExposeComputeOption(); 33 void ExposeComputeOption();
32 34
33private: 35private:
36 void Setup(const ConfigurationShared::Builder& builder);
34 void changeEvent(QEvent* event) override; 37 void changeEvent(QEvent* event) override;
35 void RetranslateUI(); 38 void RetranslateUI();
36 39
37 void SetupPerGameUI();
38
39 std::unique_ptr<Ui::ConfigureGraphicsAdvanced> ui; 40 std::unique_ptr<Ui::ConfigureGraphicsAdvanced> ui;
40 41
41 ConfigurationShared::CheckState async_present;
42 ConfigurationShared::CheckState renderer_force_max_clock;
43 ConfigurationShared::CheckState use_vsync;
44 ConfigurationShared::CheckState async_astc;
45 ConfigurationShared::CheckState use_reactive_flushing;
46 ConfigurationShared::CheckState use_asynchronous_shaders;
47 ConfigurationShared::CheckState use_fast_gpu_time;
48 ConfigurationShared::CheckState use_vulkan_driver_pipeline_cache;
49 ConfigurationShared::CheckState enable_compute_pipelines;
50 ConfigurationShared::CheckState use_video_framerate;
51 ConfigurationShared::CheckState barrier_feedback_loops;
52
53 const Core::System& system; 42 const Core::System& system;
43
44 std::vector<std::function<void(bool)>> apply_funcs;
45
46 QWidget* checkbox_enable_compute_pipelines{};
54}; 47};
diff --git a/src/yuzu/configuration/configure_graphics_advanced.ui b/src/yuzu/configuration/configure_graphics_advanced.ui
index d527a6f38..37a854ca3 100644
--- a/src/yuzu/configuration/configure_graphics_advanced.ui
+++ b/src/yuzu/configuration/configure_graphics_advanced.ui
@@ -26,8 +26,8 @@
26 </property> 26 </property>
27 <layout class="QVBoxLayout" name="verticalLayout_3"> 27 <layout class="QVBoxLayout" name="verticalLayout_3">
28 <item> 28 <item>
29 <widget class="QWidget" name="gpu_accuracy_layout" native="true"> 29 <widget class="QWidget" name="populate_target" native="true">
30 <layout class="QHBoxLayout" name="horizontalLayout_2"> 30 <layout class="QVBoxLayout" name="verticalLayout">
31 <property name="leftMargin"> 31 <property name="leftMargin">
32 <number>0</number> 32 <number>0</number>
33 </property> 33 </property>
@@ -40,233 +40,6 @@
40 <property name="bottomMargin"> 40 <property name="bottomMargin">
41 <number>0</number> 41 <number>0</number>
42 </property> 42 </property>
43 <item>
44 <widget class="QLabel" name="label_gpu_accuracy">
45 <property name="text">
46 <string>Accuracy Level:</string>
47 </property>
48 </widget>
49 </item>
50 <item>
51 <widget class="QComboBox" name="gpu_accuracy">
52 <item>
53 <property name="text">
54 <string notr="true">Normal</string>
55 </property>
56 </item>
57 <item>
58 <property name="text">
59 <string notr="true">High</string>
60 </property>
61 </item>
62 <item>
63 <property name="text">
64 <string notr="true">Extreme(very slow)</string>
65 </property>
66 </item>
67 </widget>
68 </item>
69 </layout>
70 </widget>
71 </item>
72 <item>
73 <widget class="QWidget" name="astc_recompression_layout" native="true">
74 <layout class="QHBoxLayout" name="horizontalLayout_3">
75 <property name="leftMargin">
76 <number>0</number>
77 </property>
78 <property name="topMargin">
79 <number>0</number>
80 </property>
81 <property name="rightMargin">
82 <number>0</number>
83 </property>
84 <property name="bottomMargin">
85 <number>0</number>
86 </property>
87 <item>
88 <widget class="QLabel" name="label_astc_recompression">
89 <property name="text">
90 <string>ASTC recompression:</string>
91 </property>
92 </widget>
93 </item>
94 <item>
95 <widget class="QComboBox" name="astc_recompression_combobox">
96 <item>
97 <property name="text">
98 <string>Uncompressed (Best quality)</string>
99 </property>
100 </item>
101 <item>
102 <property name="text">
103 <string>BC1 (Low quality)</string>
104 </property>
105 </item>
106 <item>
107 <property name="text">
108 <string>BC3 (Medium quality)</string>
109 </property>
110 </item>
111 </widget>
112 </item>
113 </layout>
114 </widget>
115 </item>
116 <item>
117 <widget class="QCheckBox" name="async_present">
118 <property name="text">
119 <string>Enable asynchronous presentation (Vulkan only)</string>
120 </property>
121 </widget>
122 </item>
123 <item>
124 <widget class="QCheckBox" name="renderer_force_max_clock">
125 <property name="toolTip">
126 <string>Runs work in the background while waiting for graphics commands to keep the GPU from lowering its clock speed.</string>
127 </property>
128 <property name="text">
129 <string>Force maximum clocks (Vulkan only)</string>
130 </property>
131 </widget>
132 </item>
133 <item>
134 <widget class="QCheckBox" name="async_astc">
135 <property name="toolTip">
136 <string>Enables asynchronous ASTC texture decoding, which may reduce load time stutter. This feature is experimental.</string>
137 </property>
138 <property name="text">
139 <string>Decode ASTC textures asynchronously (Hack)</string>
140 </property>
141 </widget>
142 </item>
143 <item>
144 <widget class="QCheckBox" name="use_reactive_flushing">
145 <property name="toolTip">
146 <string>Uses reactive flushing instead of predictive flushing. Allowing a more accurate syncing of memory.</string>
147 </property>
148 <property name="text">
149 <string>Enable Reactive Flushing</string>
150 </property>
151 </widget>
152 </item>
153 <item>
154 <widget class="QCheckBox" name="use_asynchronous_shaders">
155 <property name="toolTip">
156 <string>Enables asynchronous shader compilation, which may reduce shader stutter. This feature is experimental.</string>
157 </property>
158 <property name="text">
159 <string>Use asynchronous shader building (Hack)</string>
160 </property>
161 </widget>
162 </item>
163 <item>
164 <widget class="QCheckBox" name="use_fast_gpu_time">
165 <property name="toolTip">
166 <string>Enables Fast GPU Time. This option will force most games to run at their highest native resolution.</string>
167 </property>
168 <property name="text">
169 <string>Use Fast GPU Time (Hack)</string>
170 </property>
171 </widget>
172 </item>
173 <item>
174 <widget class="QCheckBox" name="use_vulkan_driver_pipeline_cache">
175 <property name="toolTip">
176 <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>
177 </property>
178 <property name="text">
179 <string>Use Vulkan pipeline cache</string>
180 </property>
181 </widget>
182 </item>
183 <item>
184 <widget class="QCheckBox" name="enable_compute_pipelines_checkbox">
185 <property name="toolTip">
186 <string>Enable compute pipelines, required by some games. This setting only exists for Intel proprietary drivers, and may crash if enabled.
187Compute pipelines are always enabled on all other drivers.</string>
188 </property>
189 <property name="text">
190 <string>Enable Compute Pipelines (Intel Vulkan only)</string>
191 </property>
192 </widget>
193 </item>
194 <item>
195 <widget class="QCheckBox" name="use_video_framerate_checkbox">
196 <property name="toolTip">
197 <string>Run the game at normal speed during video playback, even when the framerate is unlocked.</string>
198 </property>
199 <property name="text">
200 <string>Sync to framerate of video playback</string>
201 </property>
202 </widget>
203 </item>
204 <item>
205 <widget class="QCheckBox" name="barrier_feedback_loops_checkbox">
206 <property name="toolTip">
207 <string>Improves rendering of transparency effects in specific games.</string>
208 </property>
209 <property name="text">
210 <string>Barrier feedback loops</string>
211 </property>
212 </widget>
213 </item>
214 <item>
215 <widget class="QWidget" name="af_layout" native="true">
216 <layout class="QHBoxLayout" name="horizontalLayout_1">
217 <property name="leftMargin">
218 <number>0</number>
219 </property>
220 <property name="topMargin">
221 <number>0</number>
222 </property>
223 <property name="rightMargin">
224 <number>0</number>
225 </property>
226 <property name="bottomMargin">
227 <number>0</number>
228 </property>
229 <item>
230 <widget class="QLabel" name="af_label">
231 <property name="text">
232 <string>Anisotropic Filtering:</string>
233 </property>
234 </widget>
235 </item>
236 <item>
237 <widget class="QComboBox" name="anisotropic_filtering_combobox">
238 <item>
239 <property name="text">
240 <string>Automatic</string>
241 </property>
242 </item>
243 <item>
244 <property name="text">
245 <string>Default</string>
246 </property>
247 </item>
248 <item>
249 <property name="text">
250 <string>2x</string>
251 </property>
252 </item>
253 <item>
254 <property name="text">
255 <string>4x</string>
256 </property>
257 </item>
258 <item>
259 <property name="text">
260 <string>8x</string>
261 </property>
262 </item>
263 <item>
264 <property name="text">
265 <string>16x</string>
266 </property>
267 </item>
268 </widget>
269 </item>
270 </layout> 43 </layout>
271 </widget> 44 </widget>
272 </item> 45 </item>
diff --git a/src/yuzu/configuration/configure_per_game.cpp b/src/yuzu/configuration/configure_per_game.cpp
index eb96e6068..cd8b3012e 100644
--- a/src/yuzu/configuration/configure_per_game.cpp
+++ b/src/yuzu/configuration/configure_per_game.cpp
@@ -17,6 +17,7 @@
17#include <QTimer> 17#include <QTimer>
18 18
19#include "common/fs/fs_util.h" 19#include "common/fs/fs_util.h"
20#include "configuration/shared_widget.h"
20#include "core/core.h" 21#include "core/core.h"
21#include "core/file_sys/control_metadata.h" 22#include "core/file_sys/control_metadata.h"
22#include "core/file_sys/patch_manager.h" 23#include "core/file_sys/patch_manager.h"
@@ -24,9 +25,9 @@
24#include "core/loader/loader.h" 25#include "core/loader/loader.h"
25#include "ui_configure_per_game.h" 26#include "ui_configure_per_game.h"
26#include "yuzu/configuration/config.h" 27#include "yuzu/configuration/config.h"
28#include "yuzu/configuration/configuration_shared.h"
27#include "yuzu/configuration/configure_audio.h" 29#include "yuzu/configuration/configure_audio.h"
28#include "yuzu/configuration/configure_cpu.h" 30#include "yuzu/configuration/configure_cpu.h"
29#include "yuzu/configuration/configure_general.h"
30#include "yuzu/configuration/configure_graphics.h" 31#include "yuzu/configuration/configure_graphics.h"
31#include "yuzu/configuration/configure_graphics_advanced.h" 32#include "yuzu/configuration/configure_graphics_advanced.h"
32#include "yuzu/configuration/configure_input_per_game.h" 33#include "yuzu/configuration/configure_input_per_game.h"
@@ -41,26 +42,28 @@ ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id_, const std::st
41 std::vector<VkDeviceInfo::Record>& vk_device_records, 42 std::vector<VkDeviceInfo::Record>& vk_device_records,
42 Core::System& system_) 43 Core::System& system_)
43 : QDialog(parent), 44 : QDialog(parent),
44 ui(std::make_unique<Ui::ConfigurePerGame>()), title_id{title_id_}, system{system_} { 45 ui(std::make_unique<Ui::ConfigurePerGame>()), title_id{title_id_}, system{system_},
46 builder{std::make_unique<ConfigurationShared::Builder>(this, !system_.IsPoweredOn())},
47 tab_group{std::make_shared<std::vector<ConfigurationShared::Tab*>>()} {
45 const auto file_path = std::filesystem::path(Common::FS::ToU8String(file_name)); 48 const auto file_path = std::filesystem::path(Common::FS::ToU8String(file_name));
46 const auto config_file_name = title_id == 0 ? Common::FS::PathToUTF8String(file_path.filename()) 49 const auto config_file_name = title_id == 0 ? Common::FS::PathToUTF8String(file_path.filename())
47 : fmt::format("{:016X}", title_id); 50 : fmt::format("{:016X}", title_id);
48 game_config = std::make_unique<Config>(config_file_name, Config::ConfigType::PerGameConfig); 51 game_config = std::make_unique<Config>(config_file_name, Config::ConfigType::PerGameConfig);
49 52
50 addons_tab = std::make_unique<ConfigurePerGameAddons>(system_, this); 53 addons_tab = std::make_unique<ConfigurePerGameAddons>(system_, this);
51 audio_tab = std::make_unique<ConfigureAudio>(system_, this); 54 audio_tab = std::make_unique<ConfigureAudio>(system_, tab_group, *builder, this);
52 cpu_tab = std::make_unique<ConfigureCpu>(system_, this); 55 cpu_tab = std::make_unique<ConfigureCpu>(system_, tab_group, *builder, this);
53 general_tab = std::make_unique<ConfigureGeneral>(system_, this); 56 graphics_advanced_tab =
54 graphics_advanced_tab = std::make_unique<ConfigureGraphicsAdvanced>(system_, this); 57 std::make_unique<ConfigureGraphicsAdvanced>(system_, tab_group, *builder, this);
55 graphics_tab = std::make_unique<ConfigureGraphics>( 58 graphics_tab = std::make_unique<ConfigureGraphics>(
56 system_, vk_device_records, [&]() { graphics_advanced_tab->ExposeComputeOption(); }, this); 59 system_, vk_device_records, [&]() { graphics_advanced_tab->ExposeComputeOption(); },
60 tab_group, *builder, this);
57 input_tab = std::make_unique<ConfigureInputPerGame>(system_, game_config.get(), this); 61 input_tab = std::make_unique<ConfigureInputPerGame>(system_, game_config.get(), this);
58 system_tab = std::make_unique<ConfigureSystem>(system_, this); 62 system_tab = std::make_unique<ConfigureSystem>(system_, tab_group, *builder, this);
59 63
60 ui->setupUi(this); 64 ui->setupUi(this);
61 65
62 ui->tabWidget->addTab(addons_tab.get(), tr("Add-Ons")); 66 ui->tabWidget->addTab(addons_tab.get(), tr("Add-Ons"));
63 ui->tabWidget->addTab(general_tab.get(), tr("General"));
64 ui->tabWidget->addTab(system_tab.get(), tr("System")); 67 ui->tabWidget->addTab(system_tab.get(), tr("System"));
65 ui->tabWidget->addTab(cpu_tab.get(), tr("CPU")); 68 ui->tabWidget->addTab(cpu_tab.get(), tr("CPU"));
66 ui->tabWidget->addTab(graphics_tab.get(), tr("Graphics")); 69 ui->tabWidget->addTab(graphics_tab.get(), tr("Graphics"));
@@ -88,13 +91,10 @@ ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id_, const std::st
88ConfigurePerGame::~ConfigurePerGame() = default; 91ConfigurePerGame::~ConfigurePerGame() = default;
89 92
90void ConfigurePerGame::ApplyConfiguration() { 93void ConfigurePerGame::ApplyConfiguration() {
94 for (const auto tab : *tab_group) {
95 tab->ApplyConfiguration();
96 }
91 addons_tab->ApplyConfiguration(); 97 addons_tab->ApplyConfiguration();
92 general_tab->ApplyConfiguration();
93 cpu_tab->ApplyConfiguration();
94 system_tab->ApplyConfiguration();
95 graphics_tab->ApplyConfiguration();
96 graphics_advanced_tab->ApplyConfiguration();
97 audio_tab->ApplyConfiguration();
98 input_tab->ApplyConfiguration(); 98 input_tab->ApplyConfiguration();
99 99
100 system.ApplySettings(); 100 system.ApplySettings();
diff --git a/src/yuzu/configuration/configure_per_game.h b/src/yuzu/configuration/configure_per_game.h
index 7ec1ded06..1a727f32c 100644
--- a/src/yuzu/configuration/configure_per_game.h
+++ b/src/yuzu/configuration/configure_per_game.h
@@ -10,9 +10,12 @@
10#include <QDialog> 10#include <QDialog>
11#include <QList> 11#include <QList>
12 12
13#include "configuration/shared_widget.h"
13#include "core/file_sys/vfs_types.h" 14#include "core/file_sys/vfs_types.h"
14#include "vk_device_info.h" 15#include "vk_device_info.h"
15#include "yuzu/configuration/config.h" 16#include "yuzu/configuration/config.h"
17#include "yuzu/configuration/configuration_shared.h"
18#include "yuzu/configuration/shared_translation.h"
16 19
17namespace Core { 20namespace Core {
18class System; 21class System;
@@ -25,7 +28,6 @@ class InputSubsystem;
25class ConfigurePerGameAddons; 28class ConfigurePerGameAddons;
26class ConfigureAudio; 29class ConfigureAudio;
27class ConfigureCpu; 30class ConfigureCpu;
28class ConfigureGeneral;
29class ConfigureGraphics; 31class ConfigureGraphics;
30class ConfigureGraphicsAdvanced; 32class ConfigureGraphicsAdvanced;
31class ConfigureInputPerGame; 33class ConfigureInputPerGame;
@@ -73,11 +75,12 @@ private:
73 std::unique_ptr<Config> game_config; 75 std::unique_ptr<Config> game_config;
74 76
75 Core::System& system; 77 Core::System& system;
78 std::unique_ptr<ConfigurationShared::Builder> builder;
79 std::shared_ptr<std::vector<ConfigurationShared::Tab*>> tab_group;
76 80
77 std::unique_ptr<ConfigurePerGameAddons> addons_tab; 81 std::unique_ptr<ConfigurePerGameAddons> addons_tab;
78 std::unique_ptr<ConfigureAudio> audio_tab; 82 std::unique_ptr<ConfigureAudio> audio_tab;
79 std::unique_ptr<ConfigureCpu> cpu_tab; 83 std::unique_ptr<ConfigureCpu> cpu_tab;
80 std::unique_ptr<ConfigureGeneral> general_tab;
81 std::unique_ptr<ConfigureGraphicsAdvanced> graphics_advanced_tab; 84 std::unique_ptr<ConfigureGraphicsAdvanced> graphics_advanced_tab;
82 std::unique_ptr<ConfigureGraphics> graphics_tab; 85 std::unique_ptr<ConfigureGraphics> graphics_tab;
83 std::unique_ptr<ConfigureInputPerGame> input_tab; 86 std::unique_ptr<ConfigureInputPerGame> input_tab;
diff --git a/src/yuzu/configuration/configure_per_game.ui b/src/yuzu/configuration/configure_per_game.ui
index 85c86e107..99ba2fd18 100644
--- a/src/yuzu/configuration/configure_per_game.ui
+++ b/src/yuzu/configuration/configure_per_game.ui
@@ -2,6 +2,14 @@
2<ui version="4.0"> 2<ui version="4.0">
3 <class>ConfigurePerGame</class> 3 <class>ConfigurePerGame</class>
4 <widget class="QDialog" name="ConfigurePerGame"> 4 <widget class="QDialog" name="ConfigurePerGame">
5 <property name="geometry">
6 <rect>
7 <x>0</x>
8 <y>0</y>
9 <width>900</width>
10 <height>607</height>
11 </rect>
12 </property>
5 <property name="minimumSize"> 13 <property name="minimumSize">
6 <size> 14 <size>
7 <width>900</width> 15 <width>900</width>
@@ -225,20 +233,31 @@
225 </layout> 233 </layout>
226 </item> 234 </item>
227 <item> 235 <item>
228 <widget class="QDialogButtonBox" name="buttonBox"> 236 <layout class="QHBoxLayout" name="horizontalLayout_2">
229 <property name="sizePolicy"> 237 <item>
230 <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> 238 <widget class="QLabel" name="label_8">
231 <horstretch>0</horstretch> 239 <property name="text">
232 <verstretch>0</verstretch> 240 <string>Some settings are only available when a game is not running.</string>
233 </sizepolicy> 241 </property>
234 </property> 242 </widget>
235 <property name="orientation"> 243 </item>
236 <enum>Qt::Horizontal</enum> 244 <item>
237 </property> 245 <widget class="QDialogButtonBox" name="buttonBox">
238 <property name="standardButtons"> 246 <property name="sizePolicy">
239 <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> 247 <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
240 </property> 248 <horstretch>0</horstretch>
241 </widget> 249 <verstretch>0</verstretch>
250 </sizepolicy>
251 </property>
252 <property name="orientation">
253 <enum>Qt::Horizontal</enum>
254 </property>
255 <property name="standardButtons">
256 <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
257 </property>
258 </widget>
259 </item>
260 </layout>
242 </item> 261 </item>
243 </layout> 262 </layout>
244 </widget> 263 </widget>
diff --git a/src/yuzu/configuration/configure_system.cpp b/src/yuzu/configuration/configure_system.cpp
index f1ae312c6..c4833f4e7 100644
--- a/src/yuzu/configuration/configure_system.cpp
+++ b/src/yuzu/configuration/configure_system.cpp
@@ -3,16 +3,23 @@
3 3
4#include <chrono> 4#include <chrono>
5#include <optional> 5#include <optional>
6#include <vector>
6 7
8#include <QCheckBox>
9#include <QComboBox>
10#include <QDateTimeEdit>
7#include <QFileDialog> 11#include <QFileDialog>
8#include <QGraphicsItem> 12#include <QGraphicsItem>
13#include <QLineEdit>
9#include <QMessageBox> 14#include <QMessageBox>
10#include "common/settings.h" 15#include "common/settings.h"
11#include "core/core.h" 16#include "core/core.h"
12#include "core/hle/service/time/time_manager.h" 17#include "core/hle/service/time/time_manager.h"
13#include "ui_configure_system.h" 18#include "ui_configure_system.h"
19#include "yuzu/configuration/config.h"
14#include "yuzu/configuration/configuration_shared.h" 20#include "yuzu/configuration/configuration_shared.h"
15#include "yuzu/configuration/configure_system.h" 21#include "yuzu/configuration/configure_system.h"
22#include "yuzu/configuration/shared_widget.h"
16 23
17constexpr std::array<u32, 7> LOCALE_BLOCKLIST{ 24constexpr std::array<u32, 7> LOCALE_BLOCKLIST{
18 // pzzefezrpnkzeidfej 25 // pzzefezrpnkzeidfej
@@ -37,44 +44,32 @@ static bool IsValidLocale(u32 region_index, u32 language_index) {
37 return ((LOCALE_BLOCKLIST.at(region_index) >> language_index) & 1) == 0; 44 return ((LOCALE_BLOCKLIST.at(region_index) >> language_index) & 1) == 0;
38} 45}
39 46
40ConfigureSystem::ConfigureSystem(Core::System& system_, QWidget* parent) 47ConfigureSystem::ConfigureSystem(Core::System& system_,
41 : QWidget(parent), ui{std::make_unique<Ui::ConfigureSystem>()}, system{system_} { 48 std::shared_ptr<std::vector<ConfigurationShared::Tab*>> group_,
49 const ConfigurationShared::Builder& builder, QWidget* parent)
50 : Tab(group_, parent), ui{std::make_unique<Ui::ConfigureSystem>()}, system{system_} {
42 ui->setupUi(this); 51 ui->setupUi(this);
43 52
44 connect(ui->rng_seed_checkbox, &QCheckBox::stateChanged, this, [this](int state) { 53 Setup(builder);
45 ui->rng_seed_edit->setEnabled(state == Qt::Checked);
46 if (state != Qt::Checked) {
47 ui->rng_seed_edit->setText(QStringLiteral("00000000"));
48 }
49 });
50
51 connect(ui->custom_rtc_checkbox, &QCheckBox::stateChanged, this, [this](int state) {
52 ui->custom_rtc_edit->setEnabled(state == Qt::Checked);
53 if (state != Qt::Checked) {
54 ui->custom_rtc_edit->setDateTime(QDateTime::currentDateTime());
55 }
56 });
57 54
58 const auto locale_check = [this](int index) { 55 const auto locale_check = [this]() {
59 const auto region_index = ConfigurationShared::GetComboboxIndex( 56 const auto region_index = combo_region->currentIndex();
60 Settings::values.region_index.GetValue(true), ui->combo_region); 57 const auto language_index = combo_language->currentIndex();
61 const auto language_index = ConfigurationShared::GetComboboxIndex(
62 Settings::values.language_index.GetValue(true), ui->combo_language);
63 const bool valid_locale = IsValidLocale(region_index, language_index); 58 const bool valid_locale = IsValidLocale(region_index, language_index);
64 ui->label_warn_invalid_locale->setVisible(!valid_locale); 59 ui->label_warn_invalid_locale->setVisible(!valid_locale);
65 if (!valid_locale) { 60 if (!valid_locale) {
66 ui->label_warn_invalid_locale->setText( 61 ui->label_warn_invalid_locale->setText(
67 tr("Warning: \"%1\" is not a valid language for region \"%2\"") 62 tr("Warning: \"%1\" is not a valid language for region \"%2\"")
68 .arg(ui->combo_language->currentText()) 63 .arg(combo_language->currentText())
69 .arg(ui->combo_region->currentText())); 64 .arg(combo_region->currentText()));
70 } 65 }
71 }; 66 };
72 67
73 connect(ui->combo_language, qOverload<int>(&QComboBox::currentIndexChanged), this, 68 connect(combo_language, qOverload<int>(&QComboBox::currentIndexChanged), this, locale_check);
74 locale_check); 69 connect(combo_region, qOverload<int>(&QComboBox::currentIndexChanged), this, locale_check);
75 connect(ui->combo_region, qOverload<int>(&QComboBox::currentIndexChanged), this, locale_check);
76 70
77 SetupPerGameUI(); 71 ui->label_warn_invalid_locale->setVisible(false);
72 locale_check();
78 73
79 SetConfiguration(); 74 SetConfiguration();
80} 75}
@@ -93,137 +88,66 @@ void ConfigureSystem::RetranslateUI() {
93 ui->retranslateUi(this); 88 ui->retranslateUi(this);
94} 89}
95 90
96void ConfigureSystem::SetConfiguration() { 91void ConfigureSystem::Setup(const ConfigurationShared::Builder& builder) {
97 enabled = !system.IsPoweredOn(); 92 auto& core_layout = *ui->core_widget->layout();
98 const auto rng_seed = 93 auto& system_layout = *ui->system_widget->layout();
99 QStringLiteral("%1")
100 .arg(Settings::values.rng_seed.GetValue().value_or(0), 8, 16, QLatin1Char{'0'})
101 .toUpper();
102 const auto rtc_time = Settings::values.custom_rtc.value_or(QDateTime::currentSecsSinceEpoch());
103
104 ui->rng_seed_checkbox->setChecked(Settings::values.rng_seed.GetValue().has_value());
105 ui->rng_seed_edit->setEnabled(Settings::values.rng_seed.GetValue().has_value() &&
106 Settings::values.rng_seed.UsingGlobal());
107 ui->rng_seed_edit->setText(rng_seed);
108
109 ui->custom_rtc_checkbox->setChecked(Settings::values.custom_rtc.has_value());
110 ui->custom_rtc_edit->setEnabled(Settings::values.custom_rtc.has_value());
111 ui->custom_rtc_edit->setDateTime(QDateTime::fromSecsSinceEpoch(rtc_time));
112 ui->device_name_edit->setText(
113 QString::fromUtf8(Settings::values.device_name.GetValue().c_str()));
114 ui->use_unsafe_extended_memory_layout->setEnabled(enabled);
115 ui->use_unsafe_extended_memory_layout->setChecked(
116 Settings::values.use_unsafe_extended_memory_layout.GetValue());
117
118 if (Settings::IsConfiguringGlobal()) {
119 ui->combo_language->setCurrentIndex(Settings::values.language_index.GetValue());
120 ui->combo_region->setCurrentIndex(Settings::values.region_index.GetValue());
121 ui->combo_time_zone->setCurrentIndex(Settings::values.time_zone_index.GetValue());
122 } else {
123 ConfigurationShared::SetPerGameSetting(ui->combo_language,
124 &Settings::values.language_index);
125 ConfigurationShared::SetPerGameSetting(ui->combo_region, &Settings::values.region_index);
126 ConfigurationShared::SetPerGameSetting(ui->combo_time_zone,
127 &Settings::values.time_zone_index);
128
129 ConfigurationShared::SetHighlight(ui->label_language,
130 !Settings::values.language_index.UsingGlobal());
131 ConfigurationShared::SetHighlight(ui->label_region,
132 !Settings::values.region_index.UsingGlobal());
133 ConfigurationShared::SetHighlight(ui->label_timezone,
134 !Settings::values.time_zone_index.UsingGlobal());
135 }
136}
137 94
138void ConfigureSystem::ReadSystemSettings() {} 95 std::map<u32, QWidget*> core_hold{};
96 std::map<u32, QWidget*> system_hold{};
139 97
140void ConfigureSystem::ApplyConfiguration() { 98 std::vector<Settings::BasicSetting*> settings;
141 // Allow setting custom RTC even if system is powered on, 99 auto push = [&settings](auto& list) {
142 // to allow in-game time to be fast forwarded 100 for (auto setting : list) {
143 if (Settings::IsConfiguringGlobal()) { 101 settings.push_back(setting);
144 if (ui->custom_rtc_checkbox->isChecked()) {
145 Settings::values.custom_rtc = ui->custom_rtc_edit->dateTime().toSecsSinceEpoch();
146 if (system.IsPoweredOn()) {
147 const s64 posix_time{*Settings::values.custom_rtc};
148 system.GetTimeManager().UpdateLocalSystemClockTime(posix_time);
149 }
150 } else {
151 Settings::values.custom_rtc = std::nullopt;
152 } 102 }
153 } 103 };
154 104
155 Settings::values.device_name = ui->device_name_edit->text().toStdString(); 105 push(Settings::values.linkage.by_category[Settings::Category::Core]);
106 push(Settings::values.linkage.by_category[Settings::Category::System]);
156 107
157 if (!enabled) { 108 for (auto setting : settings) {
158 return; 109 ConfigurationShared::Widget* widget = builder.BuildWidget(setting, apply_funcs);
159 }
160 110
161 ConfigurationShared::ApplyPerGameSetting(&Settings::values.language_index, ui->combo_language); 111 if (widget == nullptr) {
162 ConfigurationShared::ApplyPerGameSetting(&Settings::values.region_index, ui->combo_region); 112 continue;
163 ConfigurationShared::ApplyPerGameSetting(&Settings::values.time_zone_index,
164 ui->combo_time_zone);
165 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_unsafe_extended_memory_layout,
166 ui->use_unsafe_extended_memory_layout,
167 use_unsafe_extended_memory_layout);
168
169 if (Settings::IsConfiguringGlobal()) {
170 // Guard if during game and set to game-specific value
171 if (Settings::values.rng_seed.UsingGlobal()) {
172 if (ui->rng_seed_checkbox->isChecked()) {
173 Settings::values.rng_seed.SetValue(ui->rng_seed_edit->text().toUInt(nullptr, 16));
174 } else {
175 Settings::values.rng_seed.SetValue(std::nullopt);
176 }
177 } 113 }
178 } else { 114 if (!widget->Valid()) {
179 switch (use_rng_seed) { 115 widget->deleteLater();
180 case ConfigurationShared::CheckState::On: 116 continue;
181 case ConfigurationShared::CheckState::Off: 117 }
182 Settings::values.rng_seed.SetGlobal(false); 118
183 if (ui->rng_seed_checkbox->isChecked()) { 119 if (setting->Id() == Settings::values.region_index.Id()) {
184 Settings::values.rng_seed.SetValue(ui->rng_seed_edit->text().toUInt(nullptr, 16)); 120 // Keep track of the region_index (and langauge_index) combobox to validate the selected
185 } else { 121 // settings
186 Settings::values.rng_seed.SetValue(std::nullopt); 122 combo_region = widget->combobox;
187 } 123 } else if (setting->Id() == Settings::values.language_index.Id()) {
188 break; 124 combo_language = widget->combobox;
189 case ConfigurationShared::CheckState::Global: 125 }
190 Settings::values.rng_seed.SetGlobal(false); 126
191 Settings::values.rng_seed.SetValue(std::nullopt); 127 switch (setting->GetCategory()) {
192 Settings::values.rng_seed.SetGlobal(true); 128 case Settings::Category::Core:
129 core_hold.emplace(setting->Id(), widget);
193 break; 130 break;
194 case ConfigurationShared::CheckState::Count: 131 case Settings::Category::System:
132 system_hold.emplace(setting->Id(), widget);
195 break; 133 break;
134 default:
135 widget->deleteLater();
196 } 136 }
197 } 137 }
138 for (const auto& [label, widget] : core_hold) {
139 core_layout.addWidget(widget);
140 }
141 for (const auto& [id, widget] : system_hold) {
142 system_layout.addWidget(widget);
143 }
198} 144}
199 145
200void ConfigureSystem::SetupPerGameUI() { 146void ConfigureSystem::SetConfiguration() {}
201 if (Settings::IsConfiguringGlobal()) {
202 ui->combo_language->setEnabled(Settings::values.language_index.UsingGlobal());
203 ui->combo_region->setEnabled(Settings::values.region_index.UsingGlobal());
204 ui->combo_time_zone->setEnabled(Settings::values.time_zone_index.UsingGlobal());
205 ui->rng_seed_checkbox->setEnabled(Settings::values.rng_seed.UsingGlobal());
206 ui->rng_seed_edit->setEnabled(Settings::values.rng_seed.UsingGlobal());
207 147
208 return; 148void ConfigureSystem::ApplyConfiguration() {
149 const bool powered_on = system.IsPoweredOn();
150 for (const auto& func : apply_funcs) {
151 func(powered_on);
209 } 152 }
210
211 ConfigurationShared::SetColoredComboBox(ui->combo_language, ui->label_language,
212 Settings::values.language_index.GetValue(true));
213 ConfigurationShared::SetColoredComboBox(ui->combo_region, ui->label_region,
214 Settings::values.region_index.GetValue(true));
215 ConfigurationShared::SetColoredComboBox(ui->combo_time_zone, ui->label_timezone,
216 Settings::values.time_zone_index.GetValue(true));
217
218 ConfigurationShared::SetColoredTristate(
219 ui->rng_seed_checkbox, Settings::values.rng_seed.UsingGlobal(),
220 Settings::values.rng_seed.GetValue().has_value(),
221 Settings::values.rng_seed.GetValue(true).has_value(), use_rng_seed);
222
223 ConfigurationShared::SetColoredTristate(ui->use_unsafe_extended_memory_layout,
224 Settings::values.use_unsafe_extended_memory_layout,
225 use_unsafe_extended_memory_layout);
226
227 ui->custom_rtc_checkbox->setVisible(false);
228 ui->custom_rtc_edit->setVisible(false);
229} 153}
diff --git a/src/yuzu/configuration/configure_system.h b/src/yuzu/configuration/configure_system.h
index ce1a91601..eab99a48a 100644
--- a/src/yuzu/configuration/configure_system.h
+++ b/src/yuzu/configuration/configure_system.h
@@ -3,45 +3,53 @@
3 3
4#pragma once 4#pragma once
5 5
6#include <functional>
6#include <memory> 7#include <memory>
8#include <vector>
7 9
8#include <QWidget> 10#include <QWidget>
11#include "yuzu/configuration/configuration_shared.h"
9 12
13class QCheckBox;
14class QLineEdit;
15class QComboBox;
16class QDateTimeEdit;
10namespace Core { 17namespace Core {
11class System; 18class System;
12} 19}
13 20
14namespace ConfigurationShared {
15enum class CheckState;
16}
17
18namespace Ui { 21namespace Ui {
19class ConfigureSystem; 22class ConfigureSystem;
20} 23}
21 24
22class ConfigureSystem : public QWidget { 25namespace ConfigurationShared {
23 Q_OBJECT 26class Builder;
27}
24 28
29class ConfigureSystem : public ConfigurationShared::Tab {
25public: 30public:
26 explicit ConfigureSystem(Core::System& system_, QWidget* parent = nullptr); 31 explicit ConfigureSystem(Core::System& system_,
32 std::shared_ptr<std::vector<ConfigurationShared::Tab*>> group,
33 const ConfigurationShared::Builder& builder,
34 QWidget* parent = nullptr);
27 ~ConfigureSystem() override; 35 ~ConfigureSystem() override;
28 36
29 void ApplyConfiguration(); 37 void ApplyConfiguration() override;
30 void SetConfiguration(); 38 void SetConfiguration() override;
31 39
32private: 40private:
33 void changeEvent(QEvent* event) override; 41 void changeEvent(QEvent* event) override;
34 void RetranslateUI(); 42 void RetranslateUI();
35 43
36 void ReadSystemSettings(); 44 void Setup(const ConfigurationShared::Builder& builder);
37 45
38 void SetupPerGameUI(); 46 std::vector<std::function<void(bool)>> apply_funcs{};
39 47
40 std::unique_ptr<Ui::ConfigureSystem> ui; 48 std::unique_ptr<Ui::ConfigureSystem> ui;
41 bool enabled = false; 49 bool enabled = false;
42 50
43 ConfigurationShared::CheckState use_rng_seed;
44 ConfigurationShared::CheckState use_unsafe_extended_memory_layout;
45
46 Core::System& system; 51 Core::System& system;
52
53 QComboBox* combo_region;
54 QComboBox* combo_language;
47}; 55};
diff --git a/src/yuzu/configuration/configure_system.ui b/src/yuzu/configuration/configure_system.ui
index e0caecd5e..2a735836e 100644
--- a/src/yuzu/configuration/configure_system.ui
+++ b/src/yuzu/configuration/configure_system.ui
@@ -6,7 +6,7 @@
6 <rect> 6 <rect>
7 <x>0</x> 7 <x>0</x>
8 <y>0</y> 8 <y>0</y>
9 <width>366</width> 9 <width>605</width>
10 <height>483</height> 10 <height>483</height>
11 </rect> 11 </rect>
12 </property> 12 </property>
@@ -22,470 +22,63 @@
22 <item> 22 <item>
23 <widget class="QGroupBox" name="group_system_settings"> 23 <widget class="QGroupBox" name="group_system_settings">
24 <property name="title"> 24 <property name="title">
25 <string>System Settings</string> 25 <string>System</string>
26 </property> 26 </property>
27 <layout class="QVBoxLayout" name="verticalLayout_2"> 27 <layout class="QVBoxLayout" name="verticalLayout_2">
28 <item> 28 <item>
29 <layout class="QGridLayout" name="gridLayout_2"> 29 <widget class="QWidget" name="system_widget" native="true">
30 <item row="1" column="0"> 30 <layout class="QVBoxLayout" name="verticalLayout_3">
31 <widget class="QLabel" name="label_region"> 31 <property name="leftMargin">
32 <property name="text"> 32 <number>0</number>
33 <string>Region:</string> 33 </property>
34 </property> 34 <property name="topMargin">
35 </widget> 35 <number>0</number>
36 </item> 36 </property>
37 <item row="2" column="1"> 37 <property name="rightMargin">
38 <widget class="QComboBox" name="combo_time_zone"> 38 <number>0</number>
39 <item> 39 </property>
40 <property name="text"> 40 <property name="bottomMargin">
41 <string>Auto</string> 41 <number>0</number>
42 </property> 42 </property>
43 </item> 43 </layout>
44 <item> 44 </widget>
45 <property name="text"> 45 </item>
46 <string>Default</string> 46 <item>
47 </property> 47 <widget class="QLabel" name="label_warn_invalid_locale">
48 </item> 48 <property name="text">
49 <item> 49 <string/>
50 <property name="text"> 50 </property>
51 <string>CET</string> 51 <property name="wordWrap">
52 </property> 52 <bool>true</bool>
53 </item> 53 </property>
54 <item> 54 </widget>
55 <property name="text"> 55 </item>
56 <string>CST6CDT</string> 56 </layout>
57 </property> 57 </widget>
58 </item> 58 </item>
59 <item> 59 <item>
60 <property name="text"> 60 <widget class="QGroupBox" name="groupBox">
61 <string>Cuba</string> 61 <property name="title">
62 </property> 62 <string>Core</string>
63 </item> 63 </property>
64 <item> 64 <layout class="QVBoxLayout" name="verticalLayout_6">
65 <property name="text"> 65 <item>
66 <string>EET</string> 66 <widget class="QWidget" name="core_widget" native="true">
67 </property> 67 <layout class="QVBoxLayout" name="verticalLayout_5">
68 </item> 68 <property name="leftMargin">
69 <item> 69 <number>0</number>
70 <property name="text"> 70 </property>
71 <string>Egypt</string> 71 <property name="topMargin">
72 </property> 72 <number>0</number>
73 </item> 73 </property>
74 <item> 74 <property name="rightMargin">
75 <property name="text"> 75 <number>0</number>
76 <string>Eire</string> 76 </property>
77 </property> 77 <property name="bottomMargin">
78 </item> 78 <number>0</number>
79 <item> 79 </property>
80 <property name="text"> 80 </layout>
81 <string>EST</string> 81 </widget>
82 </property>
83 </item>
84 <item>
85 <property name="text">
86 <string>EST5EDT</string>
87 </property>
88 </item>
89 <item>
90 <property name="text">
91 <string>GB</string>
92 </property>
93 </item>
94 <item>
95 <property name="text">
96 <string>GB-Eire</string>
97 </property>
98 </item>
99 <item>
100 <property name="text">
101 <string>GMT</string>
102 </property>
103 </item>
104 <item>
105 <property name="text">
106 <string>GMT+0</string>
107 </property>
108 </item>
109 <item>
110 <property name="text">
111 <string>GMT-0</string>
112 </property>
113 </item>
114 <item>
115 <property name="text">
116 <string>GMT0</string>
117 </property>
118 </item>
119 <item>
120 <property name="text">
121 <string>Greenwich</string>
122 </property>
123 </item>
124 <item>
125 <property name="text">
126 <string>Hongkong</string>
127 </property>
128 </item>
129 <item>
130 <property name="text">
131 <string>HST</string>
132 </property>
133 </item>
134 <item>
135 <property name="text">
136 <string>Iceland</string>
137 </property>
138 </item>
139 <item>
140 <property name="text">
141 <string>Iran</string>
142 </property>
143 </item>
144 <item>
145 <property name="text">
146 <string>Israel</string>
147 </property>
148 </item>
149 <item>
150 <property name="text">
151 <string>Jamaica</string>
152 </property>
153 </item>
154 <item>
155 <property name="text">
156 <string>Japan</string>
157 </property>
158 </item>
159 <item>
160 <property name="text">
161 <string>Kwajalein</string>
162 </property>
163 </item>
164 <item>
165 <property name="text">
166 <string>Libya</string>
167 </property>
168 </item>
169 <item>
170 <property name="text">
171 <string>MET</string>
172 </property>
173 </item>
174 <item>
175 <property name="text">
176 <string>MST</string>
177 </property>
178 </item>
179 <item>
180 <property name="text">
181 <string>MST7MDT</string>
182 </property>
183 </item>
184 <item>
185 <property name="text">
186 <string>Navajo</string>
187 </property>
188 </item>
189 <item>
190 <property name="text">
191 <string>NZ</string>
192 </property>
193 </item>
194 <item>
195 <property name="text">
196 <string>NZ-CHAT</string>
197 </property>
198 </item>
199 <item>
200 <property name="text">
201 <string>Poland</string>
202 </property>
203 </item>
204 <item>
205 <property name="text">
206 <string>Portugal</string>
207 </property>
208 </item>
209 <item>
210 <property name="text">
211 <string>PRC</string>
212 </property>
213 </item>
214 <item>
215 <property name="text">
216 <string>PST8PDT</string>
217 </property>
218 </item>
219 <item>
220 <property name="text">
221 <string>ROC</string>
222 </property>
223 </item>
224 <item>
225 <property name="text">
226 <string>ROK</string>
227 </property>
228 </item>
229 <item>
230 <property name="text">
231 <string>Singapore</string>
232 </property>
233 </item>
234 <item>
235 <property name="text">
236 <string>Turkey</string>
237 </property>
238 </item>
239 <item>
240 <property name="text">
241 <string>UCT</string>
242 </property>
243 </item>
244 <item>
245 <property name="text">
246 <string>Universal</string>
247 </property>
248 </item>
249 <item>
250 <property name="text">
251 <string>UTC</string>
252 </property>
253 </item>
254 <item>
255 <property name="text">
256 <string>W-SU</string>
257 </property>
258 </item>
259 <item>
260 <property name="text">
261 <string>WET</string>
262 </property>
263 </item>
264 <item>
265 <property name="text">
266 <string>Zulu</string>
267 </property>
268 </item>
269 </widget>
270 </item>
271 <item row="1" column="1">
272 <widget class="QComboBox" name="combo_region">
273 <item>
274 <property name="text">
275 <string>Japan</string>
276 </property>
277 </item>
278 <item>
279 <property name="text">
280 <string>USA</string>
281 </property>
282 </item>
283 <item>
284 <property name="text">
285 <string>Europe</string>
286 </property>
287 </item>
288 <item>
289 <property name="text">
290 <string>Australia</string>
291 </property>
292 </item>
293 <item>
294 <property name="text">
295 <string>China</string>
296 </property>
297 </item>
298 <item>
299 <property name="text">
300 <string>Korea</string>
301 </property>
302 </item>
303 <item>
304 <property name="text">
305 <string>Taiwan</string>
306 </property>
307 </item>
308 </widget>
309 </item>
310 <item row="2" column="0">
311 <widget class="QLabel" name="label_timezone">
312 <property name="text">
313 <string>Time Zone:</string>
314 </property>
315 </widget>
316 </item>
317 <item row="0" column="1">
318 <widget class="QComboBox" name="combo_language">
319 <property name="toolTip">
320 <string>Note: this can be overridden when region setting is auto-select</string>
321 </property>
322 <item>
323 <property name="text">
324 <string>Japanese (日本語)</string>
325 </property>
326 </item>
327 <item>
328 <property name="text">
329 <string>American English</string>
330 </property>
331 </item>
332 <item>
333 <property name="text">
334 <string>French (français)</string>
335 </property>
336 </item>
337 <item>
338 <property name="text">
339 <string>German (Deutsch)</string>
340 </property>
341 </item>
342 <item>
343 <property name="text">
344 <string>Italian (italiano)</string>
345 </property>
346 </item>
347 <item>
348 <property name="text">
349 <string>Spanish (español)</string>
350 </property>
351 </item>
352 <item>
353 <property name="text">
354 <string>Chinese</string>
355 </property>
356 </item>
357 <item>
358 <property name="text">
359 <string>Korean (한국어)</string>
360 </property>
361 </item>
362 <item>
363 <property name="text">
364 <string>Dutch (Nederlands)</string>
365 </property>
366 </item>
367 <item>
368 <property name="text">
369 <string>Portuguese (português)</string>
370 </property>
371 </item>
372 <item>
373 <property name="text">
374 <string>Russian (Русский)</string>
375 </property>
376 </item>
377 <item>
378 <property name="text">
379 <string>Taiwanese</string>
380 </property>
381 </item>
382 <item>
383 <property name="text">
384 <string>British English</string>
385 </property>
386 </item>
387 <item>
388 <property name="text">
389 <string>Canadian French</string>
390 </property>
391 </item>
392 <item>
393 <property name="text">
394 <string>Latin American Spanish</string>
395 </property>
396 </item>
397 <item>
398 <property name="text">
399 <string>Simplified Chinese</string>
400 </property>
401 </item>
402 <item>
403 <property name="text">
404 <string>Traditional Chinese (正體中文)</string>
405 </property>
406 </item>
407 <item>
408 <property name="text">
409 <string>Brazilian Portuguese (português do Brasil)</string>
410 </property>
411 </item>
412 </widget>
413 </item>
414 <item row="4" column="0">
415 <widget class="QCheckBox" name="custom_rtc_checkbox">
416 <property name="text">
417 <string>Custom RTC</string>
418 </property>
419 </widget>
420 </item>
421 <item row="0" column="0">
422 <widget class="QLabel" name="label_language">
423 <property name="text">
424 <string>Language</string>
425 </property>
426 </widget>
427 </item>
428 <item row="5" column="0">
429 <widget class="QCheckBox" name="rng_seed_checkbox">
430 <property name="text">
431 <string>RNG Seed</string>
432 </property>
433 </widget>
434 </item>
435 <item row="6" column="0">
436 <widget class="QLabel" name="device_name_label">
437 <property name="text">
438 <string>Device Name</string>
439 </property>
440 </widget>
441 </item>
442 <item row="4" column="1">
443 <widget class="QDateTimeEdit" name="custom_rtc_edit">
444 <property name="minimumDate">
445 <date>
446 <year>1970</year>
447 <month>1</month>
448 <day>1</day>
449 </date>
450 </property>
451 </widget>
452 </item>
453 <item row="6" column="1">
454 <widget class="QLineEdit" name="device_name_edit">
455 <property name="maxLength">
456 <number>128</number>
457 </property>
458 </widget>
459 </item>
460 <item row="5" column="1">
461 <widget class="QLineEdit" name="rng_seed_edit">
462 <property name="sizePolicy">
463 <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
464 <horstretch>0</horstretch>
465 <verstretch>0</verstretch>
466 </sizepolicy>
467 </property>
468 <property name="font">
469 <font>
470 <family>Lucida Console</family>
471 </font>
472 </property>
473 <property name="inputMask">
474 <string notr="true">HHHHHHHH</string>
475 </property>
476 <property name="maxLength">
477 <number>8</number>
478 </property>
479 </widget>
480 </item>
481 <item row="7" column="0">
482 <widget class="QCheckBox" name="use_unsafe_extended_memory_layout">
483 <property name="text">
484 <string>Unsafe extended memory layout (8GB DRAM)</string>
485 </property>
486 </widget>
487 </item>
488 </layout>
489 </item> 82 </item>
490 </layout> 83 </layout>
491 </widget> 84 </widget>
@@ -503,26 +96,6 @@
503 </property> 96 </property>
504 </spacer> 97 </spacer>
505 </item> 98 </item>
506 <item>
507 <widget class="QLabel" name="label_warn_invalid_locale">
508 <property name="text">
509 <string></string>
510 </property>
511 <property name="wordWrap">
512 <bool>true</bool>
513 </property>
514 </widget>
515 </item>
516 <item>
517 <widget class="QLabel" name="label_disable_info">
518 <property name="text">
519 <string>System settings are available only when game is not running.</string>
520 </property>
521 <property name="wordWrap">
522 <bool>true</bool>
523 </property>
524 </widget>
525 </item>
526 </layout> 99 </layout>
527 </item> 100 </item>
528 </layout> 101 </layout>
diff --git a/src/yuzu/configuration/shared_translation.cpp b/src/yuzu/configuration/shared_translation.cpp
new file mode 100644
index 000000000..335810788
--- /dev/null
+++ b/src/yuzu/configuration/shared_translation.cpp
@@ -0,0 +1,388 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/time_zone.h"
5#include "yuzu/configuration/shared_translation.h"
6
7#include <map>
8#include <memory>
9#include <tuple>
10#include <utility>
11#include <QWidget>
12#include "common/settings.h"
13#include "common/settings_enums.h"
14#include "common/settings_setting.h"
15#include "yuzu/uisettings.h"
16
17namespace ConfigurationShared {
18
19std::unique_ptr<TranslationMap> InitializeTranslations(QWidget* parent) {
20 std::unique_ptr<TranslationMap> translations = std::make_unique<TranslationMap>();
21 const auto& tr = [parent](const char* text) -> QString { return parent->tr(text); };
22
23#define INSERT(SETTINGS, ID, NAME, TOOLTIP) \
24 translations->insert(std::pair{SETTINGS::values.ID.Id(), std::pair{tr((NAME)), tr((TOOLTIP))}})
25
26 // A setting can be ignored by giving it a blank name
27
28 // Audio
29 INSERT(Settings, sink_id, "Output Engine:", "");
30 INSERT(Settings, audio_output_device_id, "Output Device:", "");
31 INSERT(Settings, audio_input_device_id, "Input Device:", "");
32 INSERT(Settings, audio_muted, "Mute audio when in background", "");
33 INSERT(Settings, volume, "Volume:", "");
34 INSERT(Settings, dump_audio_commands, "", "");
35
36 // Core
37 INSERT(Settings, use_multi_core, "Multicore CPU Emulation", "");
38 INSERT(Settings, memory_layout_mode, "Memory Layout", "");
39 INSERT(Settings, use_speed_limit, "", "");
40 INSERT(Settings, speed_limit, "Limit Speed Percent", "");
41
42 // Cpu
43 INSERT(Settings, cpu_accuracy, "Accuracy:", "");
44
45 // Cpu Debug
46
47 // Cpu Unsafe
48 INSERT(Settings, cpuopt_unsafe_unfuse_fma,
49 "Unfuse FMA (improve performance on CPUs without FMA)",
50 "This option improves speed by reducing accuracy of fused-multiply-add instructions on "
51 "CPUs without native FMA support.");
52 INSERT(Settings, cpuopt_unsafe_reduce_fp_error, "Faster FRSQRTE and FRECPE",
53 "This option improves the speed of some approximate floating-point functions by using "
54 "less accurate native approximations.");
55 INSERT(Settings, cpuopt_unsafe_ignore_standard_fpcr, "Faster ASIMD instructions (32 bits only)",
56 "This option improves the speed of 32 bits ASIMD floating-point functions by running "
57 "with incorrect rounding modes.");
58 INSERT(Settings, cpuopt_unsafe_inaccurate_nan, "Inaccurate NaN handling",
59 "This option improves speed by removing NaN checking. Please note this also reduces "
60 "accuracy of certain floating-point instructions.");
61 INSERT(
62 Settings, cpuopt_unsafe_fastmem_check, "Disable address space checks",
63 "This option improves speed by eliminating a safety check before every memory read/write "
64 "in guest. Disabling it may allow a game to read/write the emulator's memory.");
65 INSERT(Settings, cpuopt_unsafe_ignore_global_monitor, "Ignore global monitor",
66 "This option improves speed by relying only on the semantics of cmpxchg to ensure "
67 "safety of exclusive access instructions. Please note this may result in deadlocks and "
68 "other race conditions.");
69
70 // Renderer
71 INSERT(Settings, renderer_backend, "API:", "");
72 INSERT(Settings, vulkan_device, "Device:", "");
73 INSERT(Settings, shader_backend, "Shader Backend:", "");
74 INSERT(Settings, resolution_setup, "Resolution:", "");
75 INSERT(Settings, scaling_filter, "Window Adapting Filter:", "");
76 INSERT(Settings, fsr_sharpening_slider, "FSR Sharpness:", "");
77 INSERT(Settings, anti_aliasing, "Anti-Aliasing Method:", "");
78 INSERT(Settings, fullscreen_mode, "Fullscreen Mode:", "");
79 INSERT(Settings, aspect_ratio, "Aspect Ratio:", "");
80 INSERT(Settings, use_disk_shader_cache, "Use disk pipeline cache", "");
81 INSERT(Settings, use_asynchronous_gpu_emulation, "Use asynchronous GPU emulation", "");
82 INSERT(Settings, nvdec_emulation, "NVDEC emulation:", "");
83 INSERT(Settings, accelerate_astc, "ASTC Decoding Method:", "");
84 INSERT(Settings, astc_recompression, "ASTC Recompression Method:", "");
85 INSERT(Settings, vsync_mode, "VSync Mode:",
86 "FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen "
87 "refresh rate.\nFIFO Relaxed is similar to FIFO but allows tearing as it recovers from "
88 "a slow down.\nMailbox can have lower latency than FIFO and does not tear but may drop "
89 "frames.\nImmediate (no synchronization) just presents whatever is available and can "
90 "exhibit tearing.");
91 INSERT(Settings, bg_red, "", "");
92 INSERT(Settings, bg_green, "", "");
93 INSERT(Settings, bg_blue, "", "");
94
95 // Renderer (Advanced Graphics)
96 INSERT(Settings, async_presentation, "Enable asynchronous presentation (Vulkan only)", "");
97 INSERT(Settings, renderer_force_max_clock, "Force maximum clocks (Vulkan only)",
98 "Runs work in the background while waiting for graphics commands to keep the GPU from "
99 "lowering its clock speed.");
100 INSERT(Settings, max_anisotropy, "Anisotropic Filtering:", "");
101 INSERT(Settings, gpu_accuracy, "Accuracy Level:", "");
102 INSERT(Settings, use_asynchronous_shaders, "Use asynchronous shader building (Hack)",
103 "Enables asynchronous shader compilation, which may reduce shader stutter. This feature "
104 "is experimental.");
105 INSERT(Settings, use_fast_gpu_time, "Use Fast GPU Time (Hack)",
106 "Enables Fast GPU Time. This option will force most games to run at their highest "
107 "native resolution.");
108 INSERT(Settings, use_vulkan_driver_pipeline_cache, "Use Vulkan pipeline cache",
109 "Enables GPU vendor-specific pipeline cache. This option can improve shader loading "
110 "time significantly in cases where the Vulkan driver does not store pipeline cache "
111 "files internally.");
112 INSERT(Settings, enable_compute_pipelines, "Enable Compute Pipelines (Intel Vulkan Only)",
113 "Enable compute pipelines, required by some games.\nThis setting only exists for Intel "
114 "proprietary drivers, and may crash if enabled.\nCompute pipelines are always enabled "
115 "on all other drivers.");
116 INSERT(Settings, use_reactive_flushing, "Enable Reactive Flushing",
117 "Uses reactive flushing instead of predictive flushing, allowing more accurate memory "
118 "syncing.");
119 INSERT(Settings, use_video_framerate, "Sync to framerate of video playback",
120 "Run the game at normal speed during video playback, even when the framerate is "
121 "unlocked.");
122 INSERT(Settings, barrier_feedback_loops, "Barrier feedback loops",
123 "Improves rendering of transparency effects in specific games.");
124
125 // Renderer (Debug)
126
127 // System
128 INSERT(Settings, rng_seed, "RNG Seed", "");
129 INSERT(Settings, rng_seed_enabled, "", "");
130 INSERT(Settings, device_name, "Device Name", "");
131 INSERT(Settings, custom_rtc, "Custom RTC", "");
132 INSERT(Settings, custom_rtc_enabled, "", "");
133 INSERT(Settings, language_index,
134 "Language:", "Note: this can be overridden when region setting is auto-select");
135 INSERT(Settings, region_index, "Region:", "");
136 INSERT(Settings, time_zone_index, "Time Zone:", "");
137 INSERT(Settings, sound_index, "Sound Output Mode:", "");
138 INSERT(Settings, use_docked_mode, "", "");
139 INSERT(Settings, current_user, "", "");
140
141 // Controls
142
143 // Data Storage
144
145 // Debugging
146
147 // Debugging Graphics
148
149 // Network
150
151 // Web Service
152
153 // Ui
154
155 // Ui General
156 INSERT(UISettings, select_user_on_boot, "Prompt for user on game boot", "");
157 INSERT(UISettings, pause_when_in_background, "Pause emulation when in background", "");
158 INSERT(UISettings, confirm_before_closing, "Confirm exit while emulation is running", "");
159 INSERT(UISettings, hide_mouse, "Hide mouse on inactivity", "");
160 INSERT(UISettings, controller_applet_disabled, "Disable controller applet", "");
161
162 // Ui Debugging
163
164 // Ui Multiplayer
165
166 // Ui Games list
167
168#undef INSERT
169
170 return translations;
171}
172
173std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QWidget* parent) {
174 std::unique_ptr<ComboboxTranslationMap> translations =
175 std::make_unique<ComboboxTranslationMap>();
176 const auto& tr = [&](const char* text, const char* context = "") {
177 return parent->tr(text, context);
178 };
179
180#define PAIR(ENUM, VALUE, TRANSLATION) \
181 { static_cast<u32>(Settings::ENUM::VALUE), tr(TRANSLATION) }
182#define CTX_PAIR(ENUM, VALUE, TRANSLATION, CONTEXT) \
183 { static_cast<u32>(Settings::ENUM::VALUE), tr(TRANSLATION, CONTEXT) }
184
185 // Intentionally skipping VSyncMode to let the UI fill that one out
186
187 translations->insert({Settings::EnumMetadata<Settings::AstcDecodeMode>::Index(),
188 {
189 PAIR(AstcDecodeMode, Cpu, "CPU"),
190 PAIR(AstcDecodeMode, Gpu, "GPU"),
191 PAIR(AstcDecodeMode, CpuAsynchronous, "CPU Asynchronous"),
192 }});
193 translations->insert({Settings::EnumMetadata<Settings::AstcRecompression>::Index(),
194 {
195 PAIR(AstcRecompression, Uncompressed, "Uncompressed (Best quality)"),
196 PAIR(AstcRecompression, Bc1, "BC1 (Low quality)"),
197 PAIR(AstcRecompression, Bc3, "BC3 (Medium quality)"),
198 }});
199 translations->insert({Settings::EnumMetadata<Settings::RendererBackend>::Index(),
200 {
201#ifdef HAS_OPENGL
202 PAIR(RendererBackend, OpenGL, "OpenGL"),
203#endif
204 PAIR(RendererBackend, Vulkan, "Vulkan"),
205 PAIR(RendererBackend, Null, "Null"),
206 }});
207 translations->insert({Settings::EnumMetadata<Settings::ShaderBackend>::Index(),
208 {
209 PAIR(ShaderBackend, Glsl, "GLSL"),
210 PAIR(ShaderBackend, Glasm, "GLASM (Assembly Shaders, NVIDIA Only)"),
211 PAIR(ShaderBackend, SpirV, "SPIR-V (Experimental, Mesa Only)"),
212 }});
213 translations->insert({Settings::EnumMetadata<Settings::GpuAccuracy>::Index(),
214 {
215 PAIR(GpuAccuracy, Normal, "Normal"),
216 PAIR(GpuAccuracy, High, "High"),
217 PAIR(GpuAccuracy, Extreme, "Extreme"),
218 }});
219 translations->insert({Settings::EnumMetadata<Settings::CpuAccuracy>::Index(),
220 {
221 PAIR(CpuAccuracy, Auto, "Auto"),
222 PAIR(CpuAccuracy, Accurate, "Accurate"),
223 PAIR(CpuAccuracy, Unsafe, "Unsafe"),
224 PAIR(CpuAccuracy, Paranoid, "Paranoid (disables most optimizations)"),
225 }});
226 translations->insert({Settings::EnumMetadata<Settings::FullscreenMode>::Index(),
227 {
228 PAIR(FullscreenMode, Borderless, "Borderless Windowed"),
229 PAIR(FullscreenMode, Exclusive, "Exclusive Fullscreen"),
230 }});
231 translations->insert({Settings::EnumMetadata<Settings::NvdecEmulation>::Index(),
232 {
233 PAIR(NvdecEmulation, Off, "No Video Output"),
234 PAIR(NvdecEmulation, Cpu, "CPU Video Decoding"),
235 PAIR(NvdecEmulation, Gpu, "GPU Video Decoding (Default)"),
236 }});
237 translations->insert({Settings::EnumMetadata<Settings::ResolutionSetup>::Index(),
238 {
239 PAIR(ResolutionSetup, Res1_2X, "0.5X (360p/540p) [EXPERIMENTAL]"),
240 PAIR(ResolutionSetup, Res3_4X, "0.75X (540p/810p) [EXPERIMENTAL]"),
241 PAIR(ResolutionSetup, Res1X, "1X (720p/1080p)"),
242 PAIR(ResolutionSetup, Res3_2X, "1.5X (1080p/1620p) [EXPERIMENTAL]"),
243 PAIR(ResolutionSetup, Res2X, "2X (1440p/2160p)"),
244 PAIR(ResolutionSetup, Res3X, "3X (2160p/3240p)"),
245 PAIR(ResolutionSetup, Res4X, "4X (2880p/4320p)"),
246 PAIR(ResolutionSetup, Res5X, "5X (3600p/5400p)"),
247 PAIR(ResolutionSetup, Res6X, "6X (4320p/6480p)"),
248 PAIR(ResolutionSetup, Res7X, "7X (5040p/7560p)"),
249 PAIR(ResolutionSetup, Res8X, "8X (5760p/8640p)"),
250 }});
251 translations->insert({Settings::EnumMetadata<Settings::ScalingFilter>::Index(),
252 {
253 PAIR(ScalingFilter, NearestNeighbor, "Nearest Neighbor"),
254 PAIR(ScalingFilter, Bilinear, "Bilinear"),
255 PAIR(ScalingFilter, Bicubic, "Bicubic"),
256 PAIR(ScalingFilter, Gaussian, "Gaussian"),
257 PAIR(ScalingFilter, ScaleForce, "ScaleForce"),
258 PAIR(ScalingFilter, Fsr, "AMD FidelityFX™️ Super Resolution"),
259 }});
260 translations->insert({Settings::EnumMetadata<Settings::AntiAliasing>::Index(),
261 {
262 PAIR(AntiAliasing, None, "None"),
263 PAIR(AntiAliasing, Fxaa, "FXAA"),
264 PAIR(AntiAliasing, Smaa, "SMAA"),
265 }});
266 translations->insert({Settings::EnumMetadata<Settings::AspectRatio>::Index(),
267 {
268 PAIR(AspectRatio, R16_9, "Default (16:9)"),
269 PAIR(AspectRatio, R4_3, "Force 4:3"),
270 PAIR(AspectRatio, R21_9, "Force 21:9"),
271 PAIR(AspectRatio, R16_10, "Force 16:10"),
272 PAIR(AspectRatio, Stretch, "Stretch to Window"),
273 }});
274 translations->insert({Settings::EnumMetadata<Settings::AnisotropyMode>::Index(),
275 {
276 PAIR(AnisotropyMode, Automatic, "Automatic"),
277 PAIR(AnisotropyMode, Default, "Default"),
278 PAIR(AnisotropyMode, X2, "2x"),
279 PAIR(AnisotropyMode, X4, "4x"),
280 PAIR(AnisotropyMode, X8, "8x"),
281 PAIR(AnisotropyMode, X16, "16x"),
282 }});
283 translations->insert(
284 {Settings::EnumMetadata<Settings::Language>::Index(),
285 {
286 PAIR(Language, Japanese, "Japanese (日本語)"),
287 PAIR(Language, EnglishAmerican, "American English"),
288 PAIR(Language, French, "French (français)"),
289 PAIR(Language, German, "German (Deutsch)"),
290 PAIR(Language, Italian, "Italian (italiano)"),
291 PAIR(Language, Spanish, "Spanish (español)"),
292 PAIR(Language, Chinese, "Chinese"),
293 PAIR(Language, Korean, "Korean (한국어)"),
294 PAIR(Language, Dutch, "Dutch (Nederlands)"),
295 PAIR(Language, Portuguese, "Portuguese (português)"),
296 PAIR(Language, Russian, "Russian (Русский)"),
297 PAIR(Language, Taiwanese, "Taiwanese"),
298 PAIR(Language, EnglishBritish, "British English"),
299 PAIR(Language, FrenchCanadian, "Canadian French"),
300 PAIR(Language, SpanishLatin, "Latin American Spanish"),
301 PAIR(Language, ChineseSimplified, "Simplified Chinese"),
302 PAIR(Language, ChineseTraditional, "Traditional Chinese (正體中文)"),
303 PAIR(Language, PortugueseBrazilian, "Brazilian Portuguese (português do Brasil)"),
304 }});
305 translations->insert({Settings::EnumMetadata<Settings::Region>::Index(),
306 {
307 PAIR(Region, Japan, "Japan"),
308 PAIR(Region, Usa, "USA"),
309 PAIR(Region, Europe, "Europe"),
310 PAIR(Region, Australia, "Australia"),
311 PAIR(Region, China, "China"),
312 PAIR(Region, Korea, "Korea"),
313 PAIR(Region, Taiwan, "Taiwan"),
314 }});
315 translations->insert(
316 {Settings::EnumMetadata<Settings::TimeZone>::Index(),
317 {
318 {static_cast<u32>(Settings::TimeZone::Auto),
319 tr("Auto (%1)", "Auto select time zone")
320 .arg(QString::fromStdString(
321 Settings::GetTimeZoneString(Settings::TimeZone::Auto)))},
322 {static_cast<u32>(Settings::TimeZone::Default),
323 tr("Default (%1)", "Default time zone")
324 .arg(QString::fromStdString(Common::TimeZone::GetDefaultTimeZone()))},
325 PAIR(TimeZone, Cet, "CET"),
326 PAIR(TimeZone, Cst6Cdt, "CST6CDT"),
327 PAIR(TimeZone, Cuba, "Cuba"),
328 PAIR(TimeZone, Eet, "EET"),
329 PAIR(TimeZone, Egypt, "Egypt"),
330 PAIR(TimeZone, Eire, "Eire"),
331 PAIR(TimeZone, Est, "EST"),
332 PAIR(TimeZone, Est5Edt, "EST5EDT"),
333 PAIR(TimeZone, Gb, "GB"),
334 PAIR(TimeZone, GbEire, "GB-Eire"),
335 PAIR(TimeZone, Gmt, "GMT"),
336 PAIR(TimeZone, GmtPlusZero, "GMT+0"),
337 PAIR(TimeZone, GmtMinusZero, "GMT-0"),
338 PAIR(TimeZone, GmtZero, "GMT0"),
339 PAIR(TimeZone, Greenwich, "Greenwich"),
340 PAIR(TimeZone, Hongkong, "Hongkong"),
341 PAIR(TimeZone, Hst, "HST"),
342 PAIR(TimeZone, Iceland, "Iceland"),
343 PAIR(TimeZone, Iran, "Iran"),
344 PAIR(TimeZone, Israel, "Israel"),
345 PAIR(TimeZone, Jamaica, "Jamaica"),
346 PAIR(TimeZone, Japan, "Japan"),
347 PAIR(TimeZone, Kwajalein, "Kwajalein"),
348 PAIR(TimeZone, Libya, "Libya"),
349 PAIR(TimeZone, Met, "MET"),
350 PAIR(TimeZone, Mst, "MST"),
351 PAIR(TimeZone, Mst7Mdt, "MST7MDT"),
352 PAIR(TimeZone, Navajo, "Navajo"),
353 PAIR(TimeZone, Nz, "NZ"),
354 PAIR(TimeZone, NzChat, "NZ-CHAT"),
355 PAIR(TimeZone, Poland, "Poland"),
356 PAIR(TimeZone, Portugal, "Portugal"),
357 PAIR(TimeZone, Prc, "PRC"),
358 PAIR(TimeZone, Pst8Pdt, "PST8PDT"),
359 PAIR(TimeZone, Roc, "ROC"),
360 PAIR(TimeZone, Rok, "ROK"),
361 PAIR(TimeZone, Singapore, "Singapore"),
362 PAIR(TimeZone, Turkey, "Turkey"),
363 PAIR(TimeZone, Uct, "UCT"),
364 PAIR(TimeZone, Universal, "Universal"),
365 PAIR(TimeZone, Utc, "UTC"),
366 PAIR(TimeZone, WSu, "W-SU"),
367 PAIR(TimeZone, Wet, "WET"),
368 PAIR(TimeZone, Zulu, "Zulu"),
369 }});
370 translations->insert({Settings::EnumMetadata<Settings::AudioMode>::Index(),
371 {
372 PAIR(AudioMode, Mono, "Mono"),
373 PAIR(AudioMode, Stereo, "Stereo"),
374 PAIR(AudioMode, Surround, "Surround"),
375 }});
376 translations->insert({Settings::EnumMetadata<Settings::MemoryLayout>::Index(),
377 {
378 PAIR(MemoryLayout, Memory_4Gb, "4GB DRAM (Default)"),
379 PAIR(MemoryLayout, Memory_6Gb, "6GB DRAM (Unsafe)"),
380 PAIR(MemoryLayout, Memory_8Gb, "8GB DRAM (Unsafe)"),
381 }});
382
383#undef PAIR
384#undef CTX_PAIR
385
386 return translations;
387}
388} // namespace ConfigurationShared
diff --git a/src/yuzu/configuration/shared_translation.h b/src/yuzu/configuration/shared_translation.h
new file mode 100644
index 000000000..99a0e808c
--- /dev/null
+++ b/src/yuzu/configuration/shared_translation.h
@@ -0,0 +1,25 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <map>
7#include <memory>
8#include <typeindex>
9#include <utility>
10#include <vector>
11#include <QString>
12#include "common/common_types.h"
13
14class QWidget;
15
16namespace ConfigurationShared {
17using TranslationMap = std::map<u32, std::pair<QString, QString>>;
18using ComboboxTranslations = std::vector<std::pair<u32, QString>>;
19using ComboboxTranslationMap = std::map<u32, ComboboxTranslations>;
20
21std::unique_ptr<TranslationMap> InitializeTranslations(QWidget* parent);
22
23std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QWidget* parent);
24
25} // namespace ConfigurationShared
diff --git a/src/yuzu/configuration/shared_widget.cpp b/src/yuzu/configuration/shared_widget.cpp
new file mode 100644
index 000000000..bdb38c8ea
--- /dev/null
+++ b/src/yuzu/configuration/shared_widget.cpp
@@ -0,0 +1,642 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "yuzu/configuration/shared_widget.h"
5
6#include <functional>
7#include <limits>
8#include <typeindex>
9#include <typeinfo>
10#include <utility>
11#include <vector>
12
13#include <QAbstractButton>
14#include <QAbstractSlider>
15#include <QBoxLayout>
16#include <QCheckBox>
17#include <QComboBox>
18#include <QDateTime>
19#include <QDateTimeEdit>
20#include <QIcon>
21#include <QLabel>
22#include <QLayout>
23#include <QLineEdit>
24#include <QObject>
25#include <QPushButton>
26#include <QRegularExpression>
27#include <QSizePolicy>
28#include <QSlider>
29#include <QSpinBox>
30#include <QStyle>
31#include <QValidator>
32#include <QVariant>
33#include <QtCore/qglobal.h>
34#include <QtCore/qobjectdefs.h>
35#include <fmt/core.h>
36#include <qglobal.h>
37#include <qnamespace.h>
38
39#include "common/assert.h"
40#include "common/common_types.h"
41#include "common/logging/log.h"
42#include "common/settings.h"
43#include "common/settings_common.h"
44#include "yuzu/configuration/shared_translation.h"
45
46namespace ConfigurationShared {
47
48static int restore_button_count = 0;
49
50static std::string RelevantDefault(const Settings::BasicSetting& setting) {
51 return Settings::IsConfiguringGlobal() ? setting.DefaultToString() : setting.ToStringGlobal();
52}
53
54static QString DefaultSuffix(QWidget* parent, Settings::BasicSetting& setting) {
55 const auto tr = [parent](const char* text, const char* context) {
56 return parent->tr(text, context);
57 };
58
59 if ((setting.Specialization() & Settings::SpecializationAttributeMask) ==
60 Settings::Specialization::Percentage) {
61 std::string context{fmt::format("{} percentage (e.g. 50%)", setting.GetLabel())};
62 return tr("%", context.c_str());
63 }
64
65 return QStringLiteral("");
66}
67
68QPushButton* Widget::CreateRestoreGlobalButton(bool using_global, QWidget* parent) {
69 restore_button_count++;
70
71 QStyle* style = parent->style();
72 QIcon* icon = new QIcon(style->standardIcon(QStyle::SP_LineEditClearButton));
73 QPushButton* restore_button = new QPushButton(*icon, QStringLiteral(""), parent);
74 restore_button->setObjectName(QStringLiteral("RestoreButton%1").arg(restore_button_count));
75 restore_button->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
76
77 // Workaround for dark theme causing min-width to be much larger than 0
78 restore_button->setStyleSheet(
79 QStringLiteral("QAbstractButton#%1 { min-width: 0px }").arg(restore_button->objectName()));
80
81 QSizePolicy sp_retain = restore_button->sizePolicy();
82 sp_retain.setRetainSizeWhenHidden(true);
83 restore_button->setSizePolicy(sp_retain);
84
85 restore_button->setEnabled(!using_global);
86 restore_button->setVisible(!using_global);
87
88 return restore_button;
89}
90
91QLabel* Widget::CreateLabel(const QString& text) {
92 QLabel* qt_label = new QLabel(text, this->parent);
93 qt_label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
94 return qt_label;
95}
96
97QWidget* Widget::CreateCheckBox(Settings::BasicSetting* bool_setting, const QString& label,
98 std::function<std::string()>& serializer,
99 std::function<void()>& restore_func,
100 const std::function<void()>& touch) {
101 checkbox = new QCheckBox(label, this);
102 checkbox->setCheckState(bool_setting->ToString() == "true" ? Qt::CheckState::Checked
103 : Qt::CheckState::Unchecked);
104 checkbox->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
105
106 if (!bool_setting->Save() && !Settings::IsConfiguringGlobal() && runtime_lock) {
107 checkbox->setEnabled(false);
108 }
109
110 serializer = [this]() {
111 return checkbox->checkState() == Qt::CheckState::Checked ? "true" : "false";
112 };
113
114 restore_func = [this, bool_setting]() {
115 checkbox->setCheckState(RelevantDefault(*bool_setting) == "true" ? Qt::Checked
116 : Qt::Unchecked);
117 };
118
119 if (!Settings::IsConfiguringGlobal()) {
120 QObject::connect(checkbox, &QCheckBox::clicked, [touch]() { touch(); });
121 }
122
123 return checkbox;
124}
125
126QWidget* Widget::CreateCombobox(std::function<std::string()>& serializer,
127 std::function<void()>& restore_func,
128 const std::function<void()>& touch) {
129 const auto type = setting.EnumIndex();
130
131 combobox = new QComboBox(this);
132 combobox->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
133
134 const ComboboxTranslations* enumeration{nullptr};
135 if (combobox_enumerations.contains(type)) {
136 enumeration = &combobox_enumerations.at(type);
137 for (const auto& [id, name] : *enumeration) {
138 combobox->addItem(name);
139 }
140 } else {
141 return combobox;
142 }
143
144 const auto find_index = [=](u32 value) -> int {
145 for (u32 i = 0; i < enumeration->size(); i++) {
146 if (enumeration->at(i).first == value) {
147 return i;
148 }
149 }
150 return -1;
151 };
152
153 const u32 setting_value = std::stoi(setting.ToString());
154 combobox->setCurrentIndex(find_index(setting_value));
155
156 serializer = [this, enumeration]() {
157 int current = combobox->currentIndex();
158 return std::to_string(enumeration->at(current).first);
159 };
160
161 restore_func = [this, find_index]() {
162 const u32 global_value = std::stoi(RelevantDefault(setting));
163 combobox->setCurrentIndex(find_index(global_value));
164 };
165
166 if (!Settings::IsConfiguringGlobal()) {
167 QObject::connect(combobox, QOverload<int>::of(&QComboBox::activated),
168 [touch]() { touch(); });
169 }
170
171 return combobox;
172}
173
174QWidget* Widget::CreateLineEdit(std::function<std::string()>& serializer,
175 std::function<void()>& restore_func,
176 const std::function<void()>& touch, bool managed) {
177 const QString text = QString::fromStdString(setting.ToString());
178 line_edit = new QLineEdit(this);
179 line_edit->setText(text);
180
181 serializer = [this]() { return line_edit->text().toStdString(); };
182
183 if (!managed) {
184 return line_edit;
185 }
186
187 restore_func = [this]() {
188 line_edit->setText(QString::fromStdString(RelevantDefault(setting)));
189 };
190
191 if (!Settings::IsConfiguringGlobal()) {
192 QObject::connect(line_edit, &QLineEdit::textChanged, [touch]() { touch(); });
193 }
194
195 return line_edit;
196}
197
198QWidget* Widget::CreateSlider(bool reversed, float multiplier, const QString& given_suffix,
199 std::function<std::string()>& serializer,
200 std::function<void()>& restore_func,
201 const std::function<void()>& touch) {
202 if (!setting.Ranged()) {
203 LOG_ERROR(Frontend, "\"{}\" is not a ranged setting, but a slider was requested.",
204 setting.GetLabel());
205 return nullptr;
206 }
207
208 QWidget* container = new QWidget(this);
209 QHBoxLayout* layout = new QHBoxLayout(container);
210
211 slider = new QSlider(Qt::Horizontal, this);
212 QLabel* feedback = new QLabel(this);
213
214 layout->addWidget(slider);
215 layout->addWidget(feedback);
216
217 container->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
218
219 layout->setContentsMargins(0, 0, 0, 0);
220
221 int max_val = std::stoi(setting.MaxVal());
222
223 QString suffix =
224 given_suffix == QStringLiteral("") ? DefaultSuffix(this, setting) : given_suffix;
225
226 const QString use_format = QStringLiteral("%1").append(suffix);
227
228 QObject::connect(slider, &QAbstractSlider::valueChanged, [=](int value) {
229 int present = (reversed ? max_val - value : value) * multiplier + 0.5f;
230 feedback->setText(use_format.arg(QVariant::fromValue(present).value<QString>()));
231 });
232
233 slider->setMinimum(std::stoi(setting.MinVal()));
234 slider->setMaximum(max_val);
235 slider->setValue(std::stoi(setting.ToString()));
236
237 slider->setInvertedAppearance(reversed);
238
239 serializer = [this]() { return std::to_string(slider->value()); };
240 restore_func = [this]() { slider->setValue(std::stoi(RelevantDefault(setting))); };
241
242 if (!Settings::IsConfiguringGlobal()) {
243 QObject::connect(slider, &QAbstractSlider::actionTriggered, [touch]() { touch(); });
244 }
245
246 return container;
247}
248
249QWidget* Widget::CreateSpinBox(const QString& given_suffix,
250 std::function<std::string()>& serializer,
251 std::function<void()>& restore_func,
252 const std::function<void()>& touch) {
253 const int min_val =
254 setting.Ranged() ? std::stoi(setting.MinVal()) : std::numeric_limits<int>::min();
255 const int max_val =
256 setting.Ranged() ? std::stoi(setting.MaxVal()) : std::numeric_limits<int>::max();
257 const int default_val = std::stoi(setting.ToString());
258
259 QString suffix =
260 given_suffix == QStringLiteral("") ? DefaultSuffix(this, setting) : given_suffix;
261
262 spinbox = new QSpinBox(this);
263 spinbox->setRange(min_val, max_val);
264 spinbox->setValue(default_val);
265 spinbox->setSuffix(suffix);
266 spinbox->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
267
268 serializer = [this]() { return std::to_string(spinbox->value()); };
269
270 restore_func = [this]() {
271 auto value{std::stol(RelevantDefault(setting))};
272 spinbox->setValue(value);
273 };
274
275 if (!Settings::IsConfiguringGlobal()) {
276 QObject::connect(spinbox, QOverload<int>::of(&QSpinBox::valueChanged), [this, touch]() {
277 if (spinbox->value() != std::stoi(setting.ToStringGlobal())) {
278 touch();
279 }
280 });
281 }
282
283 return spinbox;
284}
285
286QWidget* Widget::CreateHexEdit(std::function<std::string()>& serializer,
287 std::function<void()>& restore_func,
288 const std::function<void()>& touch) {
289 auto* data_component = CreateLineEdit(serializer, restore_func, touch, false);
290 if (data_component == nullptr) {
291 return nullptr;
292 }
293
294 auto to_hex = [=](const std::string& input) {
295 return QString::fromStdString(fmt::format("{:08x}", std::stoul(input)));
296 };
297
298 QRegularExpressionValidator* regex = new QRegularExpressionValidator(
299 QRegularExpression{QStringLiteral("^[0-9a-fA-F]{0,8}$")}, line_edit);
300
301 const QString default_val = to_hex(setting.ToString());
302
303 line_edit->setText(default_val);
304 line_edit->setMaxLength(8);
305 line_edit->setValidator(regex);
306
307 auto hex_to_dec = [this]() -> std::string {
308 return std::to_string(std::stoul(line_edit->text().toStdString(), nullptr, 16));
309 };
310
311 serializer = [hex_to_dec]() { return hex_to_dec(); };
312
313 restore_func = [this, to_hex]() { line_edit->setText(to_hex(RelevantDefault(setting))); };
314
315 if (!Settings::IsConfiguringGlobal()) {
316
317 QObject::connect(line_edit, &QLineEdit::textChanged, [touch]() { touch(); });
318 }
319
320 return line_edit;
321}
322
323QWidget* Widget::CreateDateTimeEdit(bool disabled, bool restrict,
324 std::function<std::string()>& serializer,
325 std::function<void()>& restore_func,
326 const std::function<void()>& touch) {
327 const long long current_time = QDateTime::currentSecsSinceEpoch();
328 const s64 the_time = disabled ? current_time : std::stoll(setting.ToString());
329 const auto default_val = QDateTime::fromSecsSinceEpoch(the_time);
330
331 date_time_edit = new QDateTimeEdit(this);
332 date_time_edit->setDateTime(default_val);
333 date_time_edit->setMinimumDateTime(QDateTime::fromSecsSinceEpoch(0));
334 date_time_edit->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
335
336 serializer = [this]() { return std::to_string(date_time_edit->dateTime().toSecsSinceEpoch()); };
337
338 auto get_clear_val = [this, restrict, current_time]() {
339 return QDateTime::fromSecsSinceEpoch([this, restrict, current_time]() {
340 if (restrict && checkbox->checkState() == Qt::Checked) {
341 return std::stoll(RelevantDefault(setting));
342 }
343 return current_time;
344 }());
345 };
346
347 restore_func = [this, get_clear_val]() { date_time_edit->setDateTime(get_clear_val()); };
348
349 if (!Settings::IsConfiguringGlobal()) {
350 QObject::connect(date_time_edit, &QDateTimeEdit::editingFinished,
351 [this, get_clear_val, touch]() {
352 if (date_time_edit->dateTime() != get_clear_val()) {
353 touch();
354 }
355 });
356 }
357
358 return date_time_edit;
359}
360
361void Widget::SetupComponent(const QString& label, std::function<void()>& load_func, bool managed,
362 RequestType request, float multiplier,
363 Settings::BasicSetting* other_setting, const QString& suffix) {
364 created = true;
365 const auto type = setting.TypeId();
366
367 QLayout* layout = new QHBoxLayout(this);
368 layout->setContentsMargins(0, 0, 0, 0);
369
370 if (other_setting == nullptr) {
371 other_setting = setting.PairedSetting();
372 }
373
374 const bool require_checkbox =
375 other_setting != nullptr && other_setting->TypeId() == typeid(bool);
376
377 if (other_setting != nullptr && other_setting->TypeId() != typeid(bool)) {
378 LOG_WARNING(
379 Frontend,
380 "Extra setting \"{}\" specified but is not bool, refusing to create checkbox for it.",
381 other_setting->GetLabel());
382 }
383
384 std::function<std::string()> checkbox_serializer = []() -> std::string { return {}; };
385 std::function<void()> checkbox_restore_func = []() {};
386
387 std::function<void()> touch = []() {};
388 std::function<std::string()> serializer = []() -> std::string { return {}; };
389 std::function<void()> restore_func = []() {};
390
391 QWidget* data_component{nullptr};
392
393 request = [&]() {
394 if (request != RequestType::Default) {
395 return request;
396 }
397 switch (setting.Specialization() & Settings::SpecializationTypeMask) {
398 case Settings::Specialization::Default:
399 return RequestType::Default;
400 case Settings::Specialization::Time:
401 return RequestType::DateTimeEdit;
402 case Settings::Specialization::Hex:
403 return RequestType::HexEdit;
404 case Settings::Specialization::RuntimeList:
405 managed = false;
406 [[fallthrough]];
407 case Settings::Specialization::List:
408 return RequestType::ComboBox;
409 case Settings::Specialization::Scalar:
410 return RequestType::Slider;
411 case Settings::Specialization::Countable:
412 return RequestType::SpinBox;
413 default:
414 break;
415 }
416 return request;
417 }();
418
419 if (!Settings::IsConfiguringGlobal() && managed) {
420 restore_button = CreateRestoreGlobalButton(setting.UsingGlobal(), this);
421
422 touch = [this]() {
423 LOG_DEBUG(Frontend, "Enabling custom setting for \"{}\"", setting.GetLabel());
424 restore_button->setEnabled(true);
425 restore_button->setVisible(true);
426 };
427 }
428
429 if (require_checkbox) {
430 QWidget* lhs =
431 CreateCheckBox(other_setting, label, checkbox_serializer, checkbox_restore_func, touch);
432 layout->addWidget(lhs);
433 } else if (setting.TypeId() != typeid(bool)) {
434 QLabel* qt_label = CreateLabel(label);
435 layout->addWidget(qt_label);
436 }
437
438 if (setting.TypeId() == typeid(bool)) {
439 data_component = CreateCheckBox(&setting, label, serializer, restore_func, touch);
440 } else if (setting.IsEnum()) {
441 data_component = CreateCombobox(serializer, restore_func, touch);
442 } else if (type == typeid(u32) || type == typeid(int) || type == typeid(u16) ||
443 type == typeid(s64) || type == typeid(u8)) {
444 switch (request) {
445 case RequestType::Slider:
446 case RequestType::ReverseSlider:
447 data_component = CreateSlider(request == RequestType::ReverseSlider, multiplier, suffix,
448 serializer, restore_func, touch);
449 break;
450 case RequestType::Default:
451 case RequestType::LineEdit:
452 data_component = CreateLineEdit(serializer, restore_func, touch);
453 break;
454 case RequestType::DateTimeEdit:
455 data_component = CreateDateTimeEdit(other_setting->ToString() != "true", true,
456 serializer, restore_func, touch);
457 break;
458 case RequestType::SpinBox:
459 data_component = CreateSpinBox(suffix, serializer, restore_func, touch);
460 break;
461 case RequestType::HexEdit:
462 data_component = CreateHexEdit(serializer, restore_func, touch);
463 break;
464 case RequestType::ComboBox:
465 data_component = CreateCombobox(serializer, restore_func, touch);
466 break;
467 default:
468 UNIMPLEMENTED();
469 }
470 } else if (type == typeid(std::string)) {
471 switch (request) {
472 case RequestType::Default:
473 case RequestType::LineEdit:
474 data_component = CreateLineEdit(serializer, restore_func, touch);
475 break;
476 case RequestType::ComboBox:
477 data_component = CreateCombobox(serializer, restore_func, touch);
478 break;
479 default:
480 UNIMPLEMENTED();
481 }
482 }
483
484 if (data_component == nullptr) {
485 LOG_ERROR(Frontend, "Failed to create widget for \"{}\"", setting.GetLabel());
486 created = false;
487 return;
488 }
489
490 layout->addWidget(data_component);
491
492 if (!managed) {
493 return;
494 }
495
496 if (Settings::IsConfiguringGlobal()) {
497 load_func = [this, serializer, checkbox_serializer, require_checkbox, other_setting]() {
498 if (require_checkbox && other_setting->UsingGlobal()) {
499 other_setting->LoadString(checkbox_serializer());
500 }
501 if (setting.UsingGlobal()) {
502 setting.LoadString(serializer());
503 }
504 };
505 } else {
506 layout->addWidget(restore_button);
507
508 QObject::connect(restore_button, &QAbstractButton::clicked,
509 [this, restore_func, checkbox_restore_func](bool) {
510 LOG_DEBUG(Frontend, "Restore global state for \"{}\"",
511 setting.GetLabel());
512
513 restore_button->setEnabled(false);
514 restore_button->setVisible(false);
515
516 checkbox_restore_func();
517 restore_func();
518 });
519
520 load_func = [this, serializer, require_checkbox, checkbox_serializer, other_setting]() {
521 bool using_global = !restore_button->isEnabled();
522 setting.SetGlobal(using_global);
523 if (!using_global) {
524 setting.LoadString(serializer());
525 }
526 if (require_checkbox) {
527 other_setting->SetGlobal(using_global);
528 if (!using_global) {
529 other_setting->LoadString(checkbox_serializer());
530 }
531 }
532 };
533 }
534
535 if (other_setting != nullptr) {
536 const auto reset = [restore_func, data_component](int state) {
537 data_component->setEnabled(state == Qt::Checked);
538 if (state != Qt::Checked) {
539 restore_func();
540 }
541 };
542 connect(checkbox, &QCheckBox::stateChanged, reset);
543 reset(checkbox->checkState());
544 }
545}
546
547bool Widget::Valid() const {
548 return created;
549}
550
551Widget::~Widget() = default;
552
553Widget::Widget(Settings::BasicSetting* setting_, const TranslationMap& translations_,
554 const ComboboxTranslationMap& combobox_translations_, QWidget* parent_,
555 bool runtime_lock_, std::vector<std::function<void(bool)>>& apply_funcs_,
556 RequestType request, bool managed, float multiplier,
557 Settings::BasicSetting* other_setting, const QString& suffix)
558 : QWidget(parent_), parent{parent_}, translations{translations_},
559 combobox_enumerations{combobox_translations_}, setting{*setting_}, apply_funcs{apply_funcs_},
560 runtime_lock{runtime_lock_} {
561 if (!Settings::IsConfiguringGlobal() && !setting.Switchable()) {
562 LOG_DEBUG(Frontend, "\"{}\" is not switchable, skipping...", setting.GetLabel());
563 return;
564 }
565
566 const int id = setting.Id();
567
568 const auto [label, tooltip] = [&]() {
569 const auto& setting_label = setting.GetLabel();
570 if (translations.contains(id)) {
571 return std::pair{translations.at(id).first, translations.at(id).second};
572 }
573 LOG_WARNING(Frontend, "Translation table lacks entry for \"{}\"", setting_label);
574 return std::pair{QString::fromStdString(setting_label), QStringLiteral("")};
575 }();
576
577 if (label == QStringLiteral("")) {
578 LOG_DEBUG(Frontend, "Translation table has empty entry for \"{}\", skipping...",
579 setting.GetLabel());
580 return;
581 }
582
583 std::function<void()> load_func = []() {};
584
585 SetupComponent(label, load_func, managed, request, multiplier, other_setting, suffix);
586
587 if (!created) {
588 LOG_WARNING(Frontend, "No widget was created for \"{}\"", setting.GetLabel());
589 return;
590 }
591
592 apply_funcs.push_back([load_func, setting_](bool powered_on) {
593 if (setting_->RuntimeModfiable() || !powered_on) {
594 load_func();
595 }
596 });
597
598 bool enable = runtime_lock || setting.RuntimeModfiable();
599 if (setting.Switchable() && Settings::IsConfiguringGlobal() && !runtime_lock) {
600 enable &= setting.UsingGlobal();
601 }
602 this->setEnabled(enable);
603
604 this->setToolTip(tooltip);
605}
606
607Builder::Builder(QWidget* parent_, bool runtime_lock_)
608 : translations{InitializeTranslations(parent_)},
609 combobox_translations{ComboboxEnumeration(parent_)}, parent{parent_}, runtime_lock{
610 runtime_lock_} {}
611
612Builder::~Builder() = default;
613
614Widget* Builder::BuildWidget(Settings::BasicSetting* setting,
615 std::vector<std::function<void(bool)>>& apply_funcs,
616 RequestType request, bool managed, float multiplier,
617 Settings::BasicSetting* other_setting, const QString& suffix) const {
618 if (!Settings::IsConfiguringGlobal() && !setting->Switchable()) {
619 return nullptr;
620 }
621
622 if (setting->Specialization() == Settings::Specialization::Paired) {
623 LOG_DEBUG(Frontend, "\"{}\" has specialization Paired: ignoring", setting->GetLabel());
624 return nullptr;
625 }
626
627 return new Widget(setting, *translations, *combobox_translations, parent, runtime_lock,
628 apply_funcs, request, managed, multiplier, other_setting, suffix);
629}
630
631Widget* Builder::BuildWidget(Settings::BasicSetting* setting,
632 std::vector<std::function<void(bool)>>& apply_funcs,
633 Settings::BasicSetting* other_setting, RequestType request,
634 const QString& suffix) const {
635 return BuildWidget(setting, apply_funcs, request, true, 1.0f, other_setting, suffix);
636}
637
638const ComboboxTranslationMap& Builder::ComboboxTranslations() const {
639 return *combobox_translations;
640}
641
642} // namespace ConfigurationShared
diff --git a/src/yuzu/configuration/shared_widget.h b/src/yuzu/configuration/shared_widget.h
new file mode 100644
index 000000000..e64693bab
--- /dev/null
+++ b/src/yuzu/configuration/shared_widget.h
@@ -0,0 +1,161 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <functional>
7#include <memory>
8#include <string>
9#include <vector>
10#include <QString>
11#include <QStringLiteral>
12#include <QWidget>
13#include <qobjectdefs.h>
14#include "yuzu/configuration/shared_translation.h"
15
16class QCheckBox;
17class QComboBox;
18class QDateTimeEdit;
19class QLabel;
20class QLineEdit;
21class QObject;
22class QPushButton;
23class QSlider;
24class QSpinBox;
25
26namespace Settings {
27class BasicSetting;
28} // namespace Settings
29
30namespace ConfigurationShared {
31
32enum class RequestType {
33 Default,
34 ComboBox,
35 SpinBox,
36 Slider,
37 ReverseSlider,
38 LineEdit,
39 HexEdit,
40 DateTimeEdit,
41 MaxEnum,
42};
43
44class Widget : public QWidget {
45 Q_OBJECT
46
47public:
48 /**
49 * @param setting The primary Setting to create the Widget for
50 * @param translations Map of translations to display on the left side label/checkbox
51 * @param combobox_translations Map of translations for enumerating combo boxes
52 * @param parent Qt parent
53 * @param runtime_lock Emulated guest powered on state, for use on settings that should be
54 * configured during guest execution
55 * @param apply_funcs_ List to append, functions to run to apply the widget state to the setting
56 * @param request What type of data representation component to create -- not always respected
57 * for the Setting data type
58 * @param managed Set true if the caller will set up component data and handling
59 * @param multiplier Value to multiply the slider feedback label
60 * @param other_setting Second setting to modify, to replace the label with a checkbox
61 * @param suffix Set to specify formats for Slider feedback labels or SpinBox
62 */
63 explicit Widget(Settings::BasicSetting* setting, const TranslationMap& translations,
64 const ComboboxTranslationMap& combobox_translations, QWidget* parent,
65 bool runtime_lock, std::vector<std::function<void(bool)>>& apply_funcs_,
66 RequestType request = RequestType::Default, bool managed = true,
67 float multiplier = 1.0f, Settings::BasicSetting* other_setting = nullptr,
68 const QString& suffix = QStringLiteral(""));
69 virtual ~Widget();
70
71 /**
72 * @returns True if the Widget successfully created the components for the setting
73 */
74 bool Valid() const;
75
76 /**
77 * Creates a button to appear when a setting has been modified. This exists for custom
78 * configurations and wasn't designed to work for the global configuration. It has public access
79 * for settings that need to be unmanaged but can be custom.
80 *
81 * @param using_global The global state of the setting this button is for
82 * @param parent QWidget parent
83 */
84 [[nodiscard]] static QPushButton* CreateRestoreGlobalButton(bool using_global, QWidget* parent);
85
86 // Direct handles to sub components created
87 QPushButton* restore_button{}; ///< Restore button for custom configurations
88 QLineEdit* line_edit{}; ///< QLineEdit, used for LineEdit and HexEdit
89 QSpinBox* spinbox{};
90 QCheckBox* checkbox{};
91 QSlider* slider{};
92 QComboBox* combobox{};
93 QDateTimeEdit* date_time_edit{};
94
95private:
96 void SetupComponent(const QString& label, std::function<void()>& load_func, bool managed,
97 RequestType request, float multiplier,
98 Settings::BasicSetting* other_setting, const QString& suffix);
99
100 QLabel* CreateLabel(const QString& text);
101 QWidget* CreateCheckBox(Settings::BasicSetting* bool_setting, const QString& label,
102 std::function<std::string()>& serializer,
103 std::function<void()>& restore_func,
104 const std::function<void()>& touch);
105
106 QWidget* CreateCombobox(std::function<std::string()>& serializer,
107 std::function<void()>& restore_func,
108 const std::function<void()>& touch);
109 QWidget* CreateLineEdit(std::function<std::string()>& serializer,
110 std::function<void()>& restore_func, const std::function<void()>& touch,
111 bool managed = true);
112 QWidget* CreateHexEdit(std::function<std::string()>& serializer,
113 std::function<void()>& restore_func, const std::function<void()>& touch);
114 QWidget* CreateSlider(bool reversed, float multiplier, const QString& suffix,
115 std::function<std::string()>& serializer,
116 std::function<void()>& restore_func, const std::function<void()>& touch);
117 QWidget* CreateDateTimeEdit(bool disabled, bool restrict,
118 std::function<std::string()>& serializer,
119 std::function<void()>& restore_func,
120 const std::function<void()>& touch);
121 QWidget* CreateSpinBox(const QString& suffix, std::function<std::string()>& serializer,
122 std::function<void()>& restore_func, const std::function<void()>& touch);
123
124 QWidget* parent;
125 const TranslationMap& translations;
126 const ComboboxTranslationMap& combobox_enumerations;
127 Settings::BasicSetting& setting;
128 std::vector<std::function<void(bool)>>& apply_funcs;
129
130 bool created{false};
131 bool runtime_lock{false};
132};
133
134class Builder {
135public:
136 explicit Builder(QWidget* parent, bool runtime_lock);
137 ~Builder();
138
139 Widget* BuildWidget(Settings::BasicSetting* setting,
140 std::vector<std::function<void(bool)>>& apply_funcs,
141 RequestType request = RequestType::Default, bool managed = true,
142 float multiplier = 1.0f, Settings::BasicSetting* other_setting = nullptr,
143 const QString& suffix = QStringLiteral("")) const;
144
145 Widget* BuildWidget(Settings::BasicSetting* setting,
146 std::vector<std::function<void(bool)>>& apply_funcs,
147 Settings::BasicSetting* other_setting,
148 RequestType request = RequestType::Default,
149 const QString& suffix = QStringLiteral("")) const;
150
151 const ComboboxTranslationMap& ComboboxTranslations() const;
152
153private:
154 std::unique_ptr<TranslationMap> translations;
155 std::unique_ptr<ComboboxTranslationMap> combobox_translations;
156
157 QWidget* parent;
158 const bool runtime_lock;
159};
160
161} // namespace ConfigurationShared
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 6cd557c29..97ae9e49a 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -24,6 +24,7 @@
24#include "applets/qt_software_keyboard.h" 24#include "applets/qt_software_keyboard.h"
25#include "applets/qt_web_browser.h" 25#include "applets/qt_web_browser.h"
26#include "common/nvidia_flags.h" 26#include "common/nvidia_flags.h"
27#include "common/settings_enums.h"
27#include "configuration/configure_input.h" 28#include "configuration/configure_input.h"
28#include "configuration/configure_per_game.h" 29#include "configuration/configure_per_game.h"
29#include "configuration/configure_tas.h" 30#include "configuration/configure_tas.h"
@@ -1095,10 +1096,9 @@ void GMainWindow::InitializeWidgets() {
1095 aa_status_button->setFocusPolicy(Qt::NoFocus); 1096 aa_status_button->setFocusPolicy(Qt::NoFocus);
1096 connect(aa_status_button, &QPushButton::clicked, [&] { 1097 connect(aa_status_button, &QPushButton::clicked, [&] {
1097 auto aa_mode = Settings::values.anti_aliasing.GetValue(); 1098 auto aa_mode = Settings::values.anti_aliasing.GetValue();
1098 if (aa_mode == Settings::AntiAliasing::LastAA) { 1099 aa_mode = static_cast<Settings::AntiAliasing>(static_cast<u32>(aa_mode) + 1);
1100 if (aa_mode == Settings::AntiAliasing::MaxEnum) {
1099 aa_mode = Settings::AntiAliasing::None; 1101 aa_mode = Settings::AntiAliasing::None;
1100 } else {
1101 aa_mode = static_cast<Settings::AntiAliasing>(static_cast<u32>(aa_mode) + 1);
1102 } 1102 }
1103 Settings::values.anti_aliasing.SetValue(aa_mode); 1103 Settings::values.anti_aliasing.SetValue(aa_mode);
1104 aa_status_button->setChecked(true); 1104 aa_status_button->setChecked(true);
@@ -1183,7 +1183,7 @@ void GMainWindow::InitializeWidgets() {
1183 QMenu context_menu; 1183 QMenu context_menu;
1184 1184
1185 for (auto const& gpu_accuracy_pair : Config::gpu_accuracy_texts_map) { 1185 for (auto const& gpu_accuracy_pair : Config::gpu_accuracy_texts_map) {
1186 if (gpu_accuracy_pair.first == Settings::GPUAccuracy::Extreme) { 1186 if (gpu_accuracy_pair.first == Settings::GpuAccuracy::Extreme) {
1187 continue; 1187 continue;
1188 } 1188 }
1189 context_menu.addAction(gpu_accuracy_pair.second, [this, gpu_accuracy_pair] { 1189 context_menu.addAction(gpu_accuracy_pair.second, [this, gpu_accuracy_pair] {
@@ -3651,14 +3651,14 @@ void GMainWindow::OnToggleDockedMode() {
3651 3651
3652void GMainWindow::OnToggleGpuAccuracy() { 3652void GMainWindow::OnToggleGpuAccuracy() {
3653 switch (Settings::values.gpu_accuracy.GetValue()) { 3653 switch (Settings::values.gpu_accuracy.GetValue()) {
3654 case Settings::GPUAccuracy::High: { 3654 case Settings::GpuAccuracy::High: {
3655 Settings::values.gpu_accuracy.SetValue(Settings::GPUAccuracy::Normal); 3655 Settings::values.gpu_accuracy.SetValue(Settings::GpuAccuracy::Normal);
3656 break; 3656 break;
3657 } 3657 }
3658 case Settings::GPUAccuracy::Normal: 3658 case Settings::GpuAccuracy::Normal:
3659 case Settings::GPUAccuracy::Extreme: 3659 case Settings::GpuAccuracy::Extreme:
3660 default: { 3660 default: {
3661 Settings::values.gpu_accuracy.SetValue(Settings::GPUAccuracy::High); 3661 Settings::values.gpu_accuracy.SetValue(Settings::GpuAccuracy::High);
3662 break; 3662 break;
3663 } 3663 }
3664 } 3664 }
@@ -3702,10 +3702,9 @@ void GMainWindow::OnIncreaseVolume() {
3702 3702
3703void GMainWindow::OnToggleAdaptingFilter() { 3703void GMainWindow::OnToggleAdaptingFilter() {
3704 auto filter = Settings::values.scaling_filter.GetValue(); 3704 auto filter = Settings::values.scaling_filter.GetValue();
3705 if (filter == Settings::ScalingFilter::LastFilter) { 3705 filter = static_cast<Settings::ScalingFilter>(static_cast<u32>(filter) + 1);
3706 if (filter == Settings::ScalingFilter::MaxEnum) {
3706 filter = Settings::ScalingFilter::NearestNeighbor; 3707 filter = Settings::ScalingFilter::NearestNeighbor;
3707 } else {
3708 filter = static_cast<Settings::ScalingFilter>(static_cast<u32>(filter) + 1);
3709 } 3708 }
3710 Settings::values.scaling_filter.SetValue(filter); 3709 Settings::values.scaling_filter.SetValue(filter);
3711 filter_status_button->setChecked(true); 3710 filter_status_button->setChecked(true);
@@ -4071,7 +4070,7 @@ void GMainWindow::UpdateGPUAccuracyButton() {
4071 const auto gpu_accuracy = Settings::values.gpu_accuracy.GetValue(); 4070 const auto gpu_accuracy = Settings::values.gpu_accuracy.GetValue();
4072 const auto gpu_accuracy_text = Config::gpu_accuracy_texts_map.find(gpu_accuracy)->second; 4071 const auto gpu_accuracy_text = Config::gpu_accuracy_texts_map.find(gpu_accuracy)->second;
4073 gpu_accuracy_button->setText(gpu_accuracy_text.toUpper()); 4072 gpu_accuracy_button->setText(gpu_accuracy_text.toUpper());
4074 gpu_accuracy_button->setChecked(gpu_accuracy != Settings::GPUAccuracy::Normal); 4073 gpu_accuracy_button->setChecked(gpu_accuracy != Settings::GpuAccuracy::Normal);
4075} 4074}
4076 4075
4077void GMainWindow::UpdateDockedButton() { 4076void GMainWindow::UpdateDockedButton() {
diff --git a/src/yuzu/multiplayer/direct_connect.cpp b/src/yuzu/multiplayer/direct_connect.cpp
index d71cc23a7..a415a953f 100644
--- a/src/yuzu/multiplayer/direct_connect.cpp
+++ b/src/yuzu/multiplayer/direct_connect.cpp
@@ -34,13 +34,14 @@ DirectConnectWindow::DirectConnectWindow(Core::System& system_, QWidget* parent)
34 connect(watcher, &QFutureWatcher<void>::finished, this, &DirectConnectWindow::OnConnection); 34 connect(watcher, &QFutureWatcher<void>::finished, this, &DirectConnectWindow::OnConnection);
35 35
36 ui->nickname->setValidator(validation.GetNickname()); 36 ui->nickname->setValidator(validation.GetNickname());
37 ui->nickname->setText(UISettings::values.multiplayer_nickname.GetValue()); 37 ui->nickname->setText(
38 QString::fromStdString(UISettings::values.multiplayer_nickname.GetValue()));
38 if (ui->nickname->text().isEmpty() && !Settings::values.yuzu_username.GetValue().empty()) { 39 if (ui->nickname->text().isEmpty() && !Settings::values.yuzu_username.GetValue().empty()) {
39 // Use yuzu Web Service user name as nickname by default 40 // Use yuzu Web Service user name as nickname by default
40 ui->nickname->setText(QString::fromStdString(Settings::values.yuzu_username.GetValue())); 41 ui->nickname->setText(QString::fromStdString(Settings::values.yuzu_username.GetValue()));
41 } 42 }
42 ui->ip->setValidator(validation.GetIP()); 43 ui->ip->setValidator(validation.GetIP());
43 ui->ip->setText(UISettings::values.multiplayer_ip.GetValue()); 44 ui->ip->setText(QString::fromStdString(UISettings::values.multiplayer_ip.GetValue()));
44 ui->port->setValidator(validation.GetPort()); 45 ui->port->setValidator(validation.GetPort());
45 ui->port->setText(QString::number(UISettings::values.multiplayer_port.GetValue())); 46 ui->port->setText(QString::number(UISettings::values.multiplayer_port.GetValue()));
46 47
@@ -91,8 +92,8 @@ void DirectConnectWindow::Connect() {
91 } 92 }
92 93
93 // Store settings 94 // Store settings
94 UISettings::values.multiplayer_nickname = ui->nickname->text(); 95 UISettings::values.multiplayer_nickname = ui->nickname->text().toStdString();
95 UISettings::values.multiplayer_ip = ui->ip->text(); 96 UISettings::values.multiplayer_ip = ui->ip->text().toStdString();
96 if (ui->port->isModified() && !ui->port->text().isEmpty()) { 97 if (ui->port->isModified() && !ui->port->text().isEmpty()) {
97 UISettings::values.multiplayer_port = ui->port->text().toInt(); 98 UISettings::values.multiplayer_port = ui->port->text().toInt();
98 } else { 99 } else {
diff --git a/src/yuzu/multiplayer/host_room.cpp b/src/yuzu/multiplayer/host_room.cpp
index a8faa5b24..ef364ee43 100644
--- a/src/yuzu/multiplayer/host_room.cpp
+++ b/src/yuzu/multiplayer/host_room.cpp
@@ -55,12 +55,14 @@ HostRoomWindow::HostRoomWindow(QWidget* parent, QStandardItemModel* list,
55 connect(ui->host, &QPushButton::clicked, this, &HostRoomWindow::Host); 55 connect(ui->host, &QPushButton::clicked, this, &HostRoomWindow::Host);
56 56
57 // Restore the settings: 57 // Restore the settings:
58 ui->username->setText(UISettings::values.multiplayer_room_nickname.GetValue()); 58 ui->username->setText(
59 QString::fromStdString(UISettings::values.multiplayer_room_nickname.GetValue()));
59 if (ui->username->text().isEmpty() && !Settings::values.yuzu_username.GetValue().empty()) { 60 if (ui->username->text().isEmpty() && !Settings::values.yuzu_username.GetValue().empty()) {
60 // Use yuzu Web Service user name as nickname by default 61 // Use yuzu Web Service user name as nickname by default
61 ui->username->setText(QString::fromStdString(Settings::values.yuzu_username.GetValue())); 62 ui->username->setText(QString::fromStdString(Settings::values.yuzu_username.GetValue()));
62 } 63 }
63 ui->room_name->setText(UISettings::values.multiplayer_room_name.GetValue()); 64 ui->room_name->setText(
65 QString::fromStdString(UISettings::values.multiplayer_room_name.GetValue()));
64 ui->port->setText(QString::number(UISettings::values.multiplayer_room_port.GetValue())); 66 ui->port->setText(QString::number(UISettings::values.multiplayer_room_port.GetValue()));
65 ui->max_player->setValue(UISettings::values.multiplayer_max_player.GetValue()); 67 ui->max_player->setValue(UISettings::values.multiplayer_max_player.GetValue());
66 int index = UISettings::values.multiplayer_host_type.GetValue(); 68 int index = UISettings::values.multiplayer_host_type.GetValue();
@@ -72,7 +74,8 @@ HostRoomWindow::HostRoomWindow(QWidget* parent, QStandardItemModel* list,
72 if (index != -1) { 74 if (index != -1) {
73 ui->game_list->setCurrentIndex(index); 75 ui->game_list->setCurrentIndex(index);
74 } 76 }
75 ui->room_description->setText(UISettings::values.multiplayer_room_description.GetValue()); 77 ui->room_description->setText(
78 QString::fromStdString(UISettings::values.multiplayer_room_description.GetValue()));
76} 79}
77 80
78HostRoomWindow::~HostRoomWindow() = default; 81HostRoomWindow::~HostRoomWindow() = default;
@@ -218,8 +221,8 @@ void HostRoomWindow::Host() {
218 Network::NoPreferredIP, password, token); 221 Network::NoPreferredIP, password, token);
219 222
220 // Store settings 223 // Store settings
221 UISettings::values.multiplayer_room_nickname = ui->username->text(); 224 UISettings::values.multiplayer_room_nickname = ui->username->text().toStdString();
222 UISettings::values.multiplayer_room_name = ui->room_name->text(); 225 UISettings::values.multiplayer_room_name = ui->room_name->text().toStdString();
223 UISettings::values.multiplayer_game_id = 226 UISettings::values.multiplayer_game_id =
224 ui->game_list->currentData(GameListItemPath::ProgramIdRole).toLongLong(); 227 ui->game_list->currentData(GameListItemPath::ProgramIdRole).toLongLong();
225 UISettings::values.multiplayer_max_player = ui->max_player->value(); 228 UISettings::values.multiplayer_max_player = ui->max_player->value();
@@ -230,7 +233,8 @@ void HostRoomWindow::Host() {
230 } else { 233 } else {
231 UISettings::values.multiplayer_room_port = Network::DefaultRoomPort; 234 UISettings::values.multiplayer_room_port = Network::DefaultRoomPort;
232 } 235 }
233 UISettings::values.multiplayer_room_description = ui->room_description->toPlainText(); 236 UISettings::values.multiplayer_room_description =
237 ui->room_description->toPlainText().toStdString();
234 ui->host->setEnabled(true); 238 ui->host->setEnabled(true);
235 emit SaveConfig(); 239 emit SaveConfig();
236 close(); 240 close();
diff --git a/src/yuzu/multiplayer/lobby.cpp b/src/yuzu/multiplayer/lobby.cpp
index 387f6f7c9..603e9ae3d 100644
--- a/src/yuzu/multiplayer/lobby.cpp
+++ b/src/yuzu/multiplayer/lobby.cpp
@@ -60,7 +60,8 @@ Lobby::Lobby(QWidget* parent, QStandardItemModel* list,
60 ui->room_list->setContextMenuPolicy(Qt::CustomContextMenu); 60 ui->room_list->setContextMenuPolicy(Qt::CustomContextMenu);
61 61
62 ui->nickname->setValidator(validation.GetNickname()); 62 ui->nickname->setValidator(validation.GetNickname());
63 ui->nickname->setText(UISettings::values.multiplayer_nickname.GetValue()); 63 ui->nickname->setText(
64 QString::fromStdString(UISettings::values.multiplayer_nickname.GetValue()));
64 65
65 // Try find the best nickname by default 66 // Try find the best nickname by default
66 if (ui->nickname->text().isEmpty() || ui->nickname->text() == QStringLiteral("yuzu")) { 67 if (ui->nickname->text().isEmpty() || ui->nickname->text() == QStringLiteral("yuzu")) {
@@ -202,9 +203,9 @@ void Lobby::OnJoinRoom(const QModelIndex& source) {
202 // TODO(jroweboy): disable widgets and display a connecting while we wait 203 // TODO(jroweboy): disable widgets and display a connecting while we wait
203 204
204 // Save settings 205 // Save settings
205 UISettings::values.multiplayer_nickname = ui->nickname->text(); 206 UISettings::values.multiplayer_nickname = ui->nickname->text().toStdString();
206 UISettings::values.multiplayer_ip = 207 UISettings::values.multiplayer_ip =
207 proxy->data(connection_index, LobbyItemHost::HostIPRole).toString(); 208 proxy->data(connection_index, LobbyItemHost::HostIPRole).value<QString>().toStdString();
208 UISettings::values.multiplayer_port = 209 UISettings::values.multiplayer_port =
209 proxy->data(connection_index, LobbyItemHost::HostPortRole).toInt(); 210 proxy->data(connection_index, LobbyItemHost::HostPortRole).toInt();
210 emit SaveConfig(); 211 emit SaveConfig();
diff --git a/src/yuzu/uisettings.cpp b/src/yuzu/uisettings.cpp
index 2c1b547fb..f03dc01dd 100644
--- a/src/yuzu/uisettings.cpp
+++ b/src/yuzu/uisettings.cpp
@@ -3,6 +3,18 @@
3 3
4#include "yuzu/uisettings.h" 4#include "yuzu/uisettings.h"
5 5
6#ifndef CANNOT_EXPLICITLY_INSTANTIATE
7namespace Settings {
8template class Setting<bool>;
9template class Setting<std::string>;
10template class Setting<u16, true>;
11template class Setting<u32>;
12template class Setting<u8, true>;
13template class Setting<u8>;
14template class Setting<unsigned long long>;
15} // namespace Settings
16#endif
17
6namespace UISettings { 18namespace UISettings {
7 19
8const Themes themes{{ 20const Themes themes{{
diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h
index 20a517d34..c9c89cee4 100644
--- a/src/yuzu/uisettings.h
+++ b/src/yuzu/uisettings.h
@@ -14,6 +14,21 @@
14#include "common/common_types.h" 14#include "common/common_types.h"
15#include "common/settings.h" 15#include "common/settings.h"
16 16
17using Settings::Category;
18using Settings::Setting;
19
20#ifndef CANNOT_EXPLICITLY_INSTANTIATE
21namespace Settings {
22extern template class Setting<bool>;
23extern template class Setting<std::string>;
24extern template class Setting<u16, true>;
25extern template class Setting<u32>;
26extern template class Setting<u8, true>;
27extern template class Setting<u8>;
28extern template class Setting<unsigned long long>;
29} // namespace Settings
30#endif
31
17namespace UISettings { 32namespace UISettings {
18 33
19bool IsDarkTheme(); 34bool IsDarkTheme();
@@ -56,6 +71,8 @@ struct GameDir {
56}; 71};
57 72
58struct Values { 73struct Values {
74 Settings::Linkage linkage{1000};
75
59 QByteArray geometry; 76 QByteArray geometry;
60 QByteArray state; 77 QByteArray state;
61 78
@@ -64,30 +81,54 @@ struct Values {
64 QByteArray gamelist_header_state; 81 QByteArray gamelist_header_state;
65 82
66 QByteArray microprofile_geometry; 83 QByteArray microprofile_geometry;
67 Settings::Setting<bool> microprofile_visible{false, "microProfileDialogVisible"}; 84 Setting<bool> microprofile_visible{linkage, false, "microProfileDialogVisible",
68 85 Category::UiLayout};
69 Settings::Setting<bool> single_window_mode{true, "singleWindowMode"}; 86
70 Settings::Setting<bool> fullscreen{false, "fullscreen"}; 87 Setting<bool> single_window_mode{linkage, true, "singleWindowMode", Category::Ui};
71 Settings::Setting<bool> display_titlebar{true, "displayTitleBars"}; 88 Setting<bool> fullscreen{linkage, false, "fullscreen", Category::Ui};
72 Settings::Setting<bool> show_filter_bar{true, "showFilterBar"}; 89 Setting<bool> display_titlebar{linkage, true, "displayTitleBars", Category::Ui};
73 Settings::Setting<bool> show_status_bar{true, "showStatusBar"}; 90 Setting<bool> show_filter_bar{linkage, true, "showFilterBar", Category::Ui};
74 91 Setting<bool> show_status_bar{linkage, true, "showStatusBar", Category::Ui};
75 Settings::Setting<bool> confirm_before_closing{true, "confirmClose"}; 92
76 Settings::Setting<bool> first_start{true, "firstStart"}; 93 Setting<bool> confirm_before_closing{
77 Settings::Setting<bool> pause_when_in_background{false, "pauseWhenInBackground"}; 94 linkage, true, "confirmClose", Category::UiGeneral, Settings::Specialization::Default,
78 Settings::Setting<bool> mute_when_in_background{false, "muteWhenInBackground"}; 95 true, true};
79 Settings::Setting<bool> hide_mouse{true, "hideInactiveMouse"}; 96 Setting<bool> first_start{linkage, true, "firstStart", Category::Ui};
80 Settings::Setting<bool> controller_applet_disabled{false, "disableControllerApplet"}; 97 Setting<bool> pause_when_in_background{linkage,
81 98 false,
99 "pauseWhenInBackground",
100 Category::UiGeneral,
101 Settings::Specialization::Default,
102 true,
103 true};
104 Setting<bool> mute_when_in_background{
105 linkage, false, "muteWhenInBackground", Category::Ui, Settings::Specialization::Default,
106 true, true};
107 Setting<bool> hide_mouse{
108 linkage, true, "hideInactiveMouse", Category::UiGeneral, Settings::Specialization::Default,
109 true, true};
110 Setting<bool> controller_applet_disabled{linkage, false, "disableControllerApplet",
111 Category::UiGeneral};
82 // Set when Vulkan is known to crash the application 112 // Set when Vulkan is known to crash the application
83 bool has_broken_vulkan = false; 113 bool has_broken_vulkan = false;
84 114
85 Settings::Setting<bool> select_user_on_boot{false, "select_user_on_boot"}; 115 Setting<bool> select_user_on_boot{linkage,
116 false,
117 "select_user_on_boot",
118 Category::UiGeneral,
119 Settings::Specialization::Default,
120 true,
121 true};
122 Setting<bool> disable_web_applet{linkage, true, "disable_web_applet", Category::Ui};
86 123
87 // Discord RPC 124 // Discord RPC
88 Settings::Setting<bool> enable_discord_presence{true, "enable_discord_presence"}; 125 Setting<bool> enable_discord_presence{linkage, true, "enable_discord_presence", Category::Ui};
89 126
90 Settings::Setting<bool> enable_screenshot_save_as{true, "enable_screenshot_save_as"}; 127 // logging
128 Setting<bool> show_console{linkage, false, "showConsole", Category::Ui};
129
130 Setting<bool> enable_screenshot_save_as{linkage, true, "enable_screenshot_save_as",
131 Category::Screenshots};
91 132
92 QString roms_path; 133 QString roms_path;
93 QString symbols_path; 134 QString symbols_path;
@@ -102,47 +143,46 @@ struct Values {
102 // Shortcut name <Shortcut, context> 143 // Shortcut name <Shortcut, context>
103 std::vector<Shortcut> shortcuts; 144 std::vector<Shortcut> shortcuts;
104 145
105 Settings::Setting<uint32_t> callout_flags{0, "calloutFlags"}; 146 Setting<u32> callout_flags{linkage, 0, "calloutFlags", Category::Ui};
106 147
107 // multiplayer settings 148 // multiplayer settings
108 Settings::Setting<QString> multiplayer_nickname{{}, "nickname"}; 149 Setting<std::string> multiplayer_nickname{linkage, {}, "nickname", Category::Multiplayer};
109 Settings::Setting<QString> multiplayer_ip{{}, "ip"}; 150 Setting<std::string> multiplayer_ip{linkage, {}, "ip", Category::Multiplayer};
110 Settings::SwitchableSetting<uint, true> multiplayer_port{24872, 0, UINT16_MAX, "port"}; 151 Setting<u16, true> multiplayer_port{linkage, 24872, 0,
111 Settings::Setting<QString> multiplayer_room_nickname{{}, "room_nickname"}; 152 UINT16_MAX, "port", Category::Multiplayer};
112 Settings::Setting<QString> multiplayer_room_name{{}, "room_name"}; 153 Setting<std::string> multiplayer_room_nickname{
113 Settings::SwitchableSetting<uint, true> multiplayer_max_player{8, 0, 8, "max_player"}; 154 linkage, {}, "room_nickname", Category::Multiplayer};
114 Settings::SwitchableSetting<uint, true> multiplayer_room_port{24872, 0, UINT16_MAX, 155 Setting<std::string> multiplayer_room_name{linkage, {}, "room_name", Category::Multiplayer};
115 "room_port"}; 156 Setting<u8, true> multiplayer_max_player{linkage, 8, 0, 8, "max_player", Category::Multiplayer};
116 Settings::SwitchableSetting<uint, true> multiplayer_host_type{0, 0, 1, "host_type"}; 157 Setting<u16, true> multiplayer_room_port{linkage, 24872, 0,
117 Settings::Setting<qulonglong> multiplayer_game_id{{}, "game_id"}; 158 UINT16_MAX, "room_port", Category::Multiplayer};
118 Settings::Setting<QString> multiplayer_room_description{{}, "room_description"}; 159 Setting<u8, true> multiplayer_host_type{linkage, 0, 0, 1, "host_type", Category::Multiplayer};
160 Setting<unsigned long long> multiplayer_game_id{linkage, {}, "game_id", Category::Multiplayer};
161 Setting<std::string> multiplayer_room_description{
162 linkage, {}, "room_description", Category::Multiplayer};
119 std::pair<std::vector<std::string>, std::vector<std::string>> multiplayer_ban_list; 163 std::pair<std::vector<std::string>, std::vector<std::string>> multiplayer_ban_list;
120 164
121 // logging
122 Settings::Setting<bool> show_console{false, "showConsole"};
123
124 // Game List 165 // Game List
125 Settings::Setting<bool> show_add_ons{true, "show_add_ons"}; 166 Setting<bool> show_add_ons{linkage, true, "show_add_ons", Category::UiGameList};
126 Settings::Setting<uint32_t> game_icon_size{64, "game_icon_size"}; 167 Setting<u32> game_icon_size{linkage, 64, "game_icon_size", Category::UiGameList};
127 Settings::Setting<uint32_t> folder_icon_size{48, "folder_icon_size"}; 168 Setting<u32> folder_icon_size{linkage, 48, "folder_icon_size", Category::UiGameList};
128 Settings::Setting<uint8_t> row_1_text_id{3, "row_1_text_id"}; 169 Setting<u8> row_1_text_id{linkage, 3, "row_1_text_id", Category::UiGameList};
129 Settings::Setting<uint8_t> row_2_text_id{2, "row_2_text_id"}; 170 Setting<u8> row_2_text_id{linkage, 2, "row_2_text_id", Category::UiGameList};
130 std::atomic_bool is_game_list_reload_pending{false}; 171 std::atomic_bool is_game_list_reload_pending{false};
131 Settings::Setting<bool> cache_game_list{true, "cache_game_list"}; 172 Setting<bool> cache_game_list{linkage, true, "cache_game_list", Category::UiGameList};
132 Settings::Setting<bool> favorites_expanded{true, "favorites_expanded"}; 173 Setting<bool> favorites_expanded{linkage, true, "favorites_expanded", Category::UiGameList};
133 QVector<u64> favorited_ids; 174 QVector<u64> favorited_ids;
134 175
135 // Compatibility List 176 // Compatibility List
136 Settings::Setting<bool> show_compat{false, "show_compat"}; 177 Setting<bool> show_compat{linkage, false, "show_compat", Category::UiGameList};
137 178
138 // Size & File Types Column 179 // Size & File Types Column
139 Settings::Setting<bool> show_size{true, "show_size"}; 180 Setting<bool> show_size{linkage, true, "show_size", Category::UiGameList};
140 Settings::Setting<bool> show_types{true, "show_types"}; 181 Setting<bool> show_types{linkage, true, "show_types", Category::UiGameList};
141 182
142 bool configuration_applied; 183 bool configuration_applied;
143 bool reset_to_defaults; 184 bool reset_to_defaults;
144 bool shortcut_already_warned{false}; 185 bool shortcut_already_warned{false};
145 Settings::Setting<bool> disable_web_applet{true, "disable_web_applet"};
146}; 186};
147 187
148extern Values values; 188extern Values values;
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp
index c5bc472ca..c42d98709 100644
--- a/src/yuzu_cmd/config.cpp
+++ b/src/yuzu_cmd/config.cpp
@@ -98,8 +98,26 @@ void Config::ReadSetting(const std::string& group, Settings::Setting<Type, range
98 static_cast<long>(setting.GetDefault()))); 98 static_cast<long>(setting.GetDefault())));
99} 99}
100 100
101void Config::ReadCategory(Settings::Category category) {
102 for (const auto setting : Settings::values.linkage.by_category[category]) {
103 const char* category_name = [&]() {
104 if (category == Settings::Category::Controls) {
105 // For compatibility with older configs
106 return "ControlsGeneral";
107 } else {
108 return Settings::TranslateCategory(category);
109 }
110 }();
111 std::string setting_value =
112 sdl2_config->Get(category_name, setting->GetLabel(), setting->DefaultToString());
113 setting->LoadString(setting_value);
114 }
115}
116
101void Config::ReadValues() { 117void Config::ReadValues() {
102 // Controls 118 // Controls
119 ReadCategory(Settings::Category::Controls);
120
103 for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) { 121 for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) {
104 auto& player = Settings::values.players.GetValue()[p]; 122 auto& player = Settings::values.players.GetValue()[p];
105 123
@@ -139,13 +157,6 @@ void Config::ReadValues() {
139 player.connected = sdl2_config->GetBoolean(group, "connected", false); 157 player.connected = sdl2_config->GetBoolean(group, "connected", false);
140 } 158 }
141 159
142 ReadSetting("ControlsGeneral", Settings::values.mouse_enabled);
143
144 ReadSetting("ControlsGeneral", Settings::values.touch_device);
145
146 ReadSetting("ControlsGeneral", Settings::values.keyboard_enabled);
147
148 ReadSetting("ControlsGeneral", Settings::values.debug_pad_enabled);
149 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { 160 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
150 std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); 161 std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
151 Settings::values.debug_pad_buttons[i] = sdl2_config->Get( 162 Settings::values.debug_pad_buttons[i] = sdl2_config->Get(
@@ -166,14 +177,6 @@ void Config::ReadValues() {
166 Settings::values.debug_pad_analogs[i] = default_param; 177 Settings::values.debug_pad_analogs[i] = default_param;
167 } 178 }
168 179
169 ReadSetting("ControlsGeneral", Settings::values.enable_raw_input);
170 ReadSetting("ControlsGeneral", Settings::values.enable_joycon_driver);
171 ReadSetting("ControlsGeneral", Settings::values.enable_procon_driver);
172 ReadSetting("ControlsGeneral", Settings::values.random_amiibo_id);
173 ReadSetting("ControlsGeneral", Settings::values.emulate_analog_keyboard);
174 ReadSetting("ControlsGeneral", Settings::values.vibration_enabled);
175 ReadSetting("ControlsGeneral", Settings::values.enable_accurate_vibrations);
176 ReadSetting("ControlsGeneral", Settings::values.motion_enabled);
177 Settings::values.touchscreen.enabled = 180 Settings::values.touchscreen.enabled =
178 sdl2_config->GetBoolean("ControlsGeneral", "touch_enabled", true); 181 sdl2_config->GetBoolean("ControlsGeneral", "touch_enabled", true);
179 Settings::values.touchscreen.rotation_angle = 182 Settings::values.touchscreen.rotation_angle =
@@ -217,10 +220,24 @@ void Config::ReadValues() {
217 Settings::values.touch_from_button_map_index = std::clamp( 220 Settings::values.touch_from_button_map_index = std::clamp(
218 Settings::values.touch_from_button_map_index.GetValue(), 0, num_touch_from_button_maps - 1); 221 Settings::values.touch_from_button_map_index.GetValue(), 0, num_touch_from_button_maps - 1);
219 222
220 ReadSetting("ControlsGeneral", Settings::values.udp_input_servers); 223 ReadCategory(Settings::Category::Audio);
224 ReadCategory(Settings::Category::Core);
225 ReadCategory(Settings::Category::Cpu);
226 ReadCategory(Settings::Category::CpuDebug);
227 ReadCategory(Settings::Category::CpuUnsafe);
228 ReadCategory(Settings::Category::Renderer);
229 ReadCategory(Settings::Category::RendererAdvanced);
230 ReadCategory(Settings::Category::RendererDebug);
231 ReadCategory(Settings::Category::System);
232 ReadCategory(Settings::Category::SystemAudio);
233 ReadCategory(Settings::Category::DataStorage);
234 ReadCategory(Settings::Category::Debugging);
235 ReadCategory(Settings::Category::DebuggingGraphics);
236 ReadCategory(Settings::Category::Miscellaneous);
237 ReadCategory(Settings::Category::Network);
238 ReadCategory(Settings::Category::WebService);
221 239
222 // Data Storage 240 // Data Storage
223 ReadSetting("Data Storage", Settings::values.use_virtual_sd);
224 FS::SetYuzuPath(FS::YuzuPath::NANDDir, 241 FS::SetYuzuPath(FS::YuzuPath::NANDDir,
225 sdl2_config->Get("Data Storage", "nand_directory", 242 sdl2_config->Get("Data Storage", "nand_directory",
226 FS::GetYuzuPathString(FS::YuzuPath::NANDDir))); 243 FS::GetYuzuPathString(FS::YuzuPath::NANDDir)));
@@ -233,124 +250,10 @@ void Config::ReadValues() {
233 FS::SetYuzuPath(FS::YuzuPath::DumpDir, 250 FS::SetYuzuPath(FS::YuzuPath::DumpDir,
234 sdl2_config->Get("Data Storage", "dump_directory", 251 sdl2_config->Get("Data Storage", "dump_directory",
235 FS::GetYuzuPathString(FS::YuzuPath::DumpDir))); 252 FS::GetYuzuPathString(FS::YuzuPath::DumpDir)));
236 ReadSetting("Data Storage", Settings::values.gamecard_inserted);
237 ReadSetting("Data Storage", Settings::values.gamecard_current_game);
238 ReadSetting("Data Storage", Settings::values.gamecard_path);
239
240 // System
241 ReadSetting("System", Settings::values.use_docked_mode);
242
243 ReadSetting("System", Settings::values.current_user);
244 Settings::values.current_user = std::clamp<int>(Settings::values.current_user.GetValue(), 0,
245 Service::Account::MAX_USERS - 1);
246
247 const auto rng_seed_enabled = sdl2_config->GetBoolean("System", "rng_seed_enabled", false);
248 if (rng_seed_enabled) {
249 Settings::values.rng_seed.SetValue(sdl2_config->GetInteger("System", "rng_seed", 0));
250 } else {
251 Settings::values.rng_seed.SetValue(std::nullopt);
252 }
253
254 const auto custom_rtc_enabled = sdl2_config->GetBoolean("System", "custom_rtc_enabled", false);
255 if (custom_rtc_enabled) {
256 Settings::values.custom_rtc = sdl2_config->GetInteger("System", "custom_rtc", 0);
257 } else {
258 Settings::values.custom_rtc = std::nullopt;
259 }
260
261 ReadSetting("System", Settings::values.language_index);
262 ReadSetting("System", Settings::values.region_index);
263 ReadSetting("System", Settings::values.time_zone_index);
264 ReadSetting("System", Settings::values.sound_index);
265
266 // Core
267 ReadSetting("Core", Settings::values.use_multi_core);
268 ReadSetting("Core", Settings::values.use_unsafe_extended_memory_layout);
269
270 // Cpu
271 ReadSetting("Cpu", Settings::values.cpu_accuracy);
272 ReadSetting("Cpu", Settings::values.cpu_debug_mode);
273 ReadSetting("Cpu", Settings::values.cpuopt_page_tables);
274 ReadSetting("Cpu", Settings::values.cpuopt_block_linking);
275 ReadSetting("Cpu", Settings::values.cpuopt_return_stack_buffer);
276 ReadSetting("Cpu", Settings::values.cpuopt_fast_dispatcher);
277 ReadSetting("Cpu", Settings::values.cpuopt_context_elimination);
278 ReadSetting("Cpu", Settings::values.cpuopt_const_prop);
279 ReadSetting("Cpu", Settings::values.cpuopt_misc_ir);
280 ReadSetting("Cpu", Settings::values.cpuopt_reduce_misalign_checks);
281 ReadSetting("Cpu", Settings::values.cpuopt_fastmem);
282 ReadSetting("Cpu", Settings::values.cpuopt_fastmem_exclusives);
283 ReadSetting("Cpu", Settings::values.cpuopt_recompile_exclusives);
284 ReadSetting("Cpu", Settings::values.cpuopt_ignore_memory_aborts);
285 ReadSetting("Cpu", Settings::values.cpuopt_unsafe_unfuse_fma);
286 ReadSetting("Cpu", Settings::values.cpuopt_unsafe_reduce_fp_error);
287 ReadSetting("Cpu", Settings::values.cpuopt_unsafe_ignore_standard_fpcr);
288 ReadSetting("Cpu", Settings::values.cpuopt_unsafe_inaccurate_nan);
289 ReadSetting("Cpu", Settings::values.cpuopt_unsafe_fastmem_check);
290 ReadSetting("Cpu", Settings::values.cpuopt_unsafe_ignore_global_monitor);
291
292 // Renderer
293 ReadSetting("Renderer", Settings::values.renderer_backend);
294 ReadSetting("Renderer", Settings::values.async_presentation);
295 ReadSetting("Renderer", Settings::values.renderer_force_max_clock);
296 ReadSetting("Renderer", Settings::values.renderer_debug);
297 ReadSetting("Renderer", Settings::values.renderer_shader_feedback);
298 ReadSetting("Renderer", Settings::values.enable_nsight_aftermath);
299 ReadSetting("Renderer", Settings::values.disable_shader_loop_safety_checks);
300 ReadSetting("Renderer", Settings::values.vulkan_device);
301
302 ReadSetting("Renderer", Settings::values.resolution_setup);
303 ReadSetting("Renderer", Settings::values.scaling_filter);
304 ReadSetting("Renderer", Settings::values.fsr_sharpening_slider);
305 ReadSetting("Renderer", Settings::values.anti_aliasing);
306 ReadSetting("Renderer", Settings::values.fullscreen_mode);
307 ReadSetting("Renderer", Settings::values.aspect_ratio);
308 ReadSetting("Renderer", Settings::values.max_anisotropy);
309 ReadSetting("Renderer", Settings::values.use_speed_limit);
310 ReadSetting("Renderer", Settings::values.speed_limit);
311 ReadSetting("Renderer", Settings::values.use_disk_shader_cache);
312 ReadSetting("Renderer", Settings::values.gpu_accuracy);
313 ReadSetting("Renderer", Settings::values.use_asynchronous_gpu_emulation);
314 ReadSetting("Renderer", Settings::values.vsync_mode);
315 ReadSetting("Renderer", Settings::values.shader_backend);
316 ReadSetting("Renderer", Settings::values.use_reactive_flushing);
317 ReadSetting("Renderer", Settings::values.use_asynchronous_shaders);
318 ReadSetting("Renderer", Settings::values.nvdec_emulation);
319 ReadSetting("Renderer", Settings::values.accelerate_astc);
320 ReadSetting("Renderer", Settings::values.async_astc);
321 ReadSetting("Renderer", Settings::values.astc_recompression);
322 ReadSetting("Renderer", Settings::values.use_fast_gpu_time);
323 ReadSetting("Renderer", Settings::values.use_vulkan_driver_pipeline_cache);
324
325 ReadSetting("Renderer", Settings::values.bg_red);
326 ReadSetting("Renderer", Settings::values.bg_green);
327 ReadSetting("Renderer", Settings::values.bg_blue);
328
329 // Audio
330 ReadSetting("Audio", Settings::values.sink_id);
331 ReadSetting("Audio", Settings::values.audio_output_device_id);
332 ReadSetting("Audio", Settings::values.volume);
333
334 // Miscellaneous
335 // log_filter has a different default here than from common
336 Settings::values.log_filter =
337 sdl2_config->Get("Miscellaneous", Settings::values.log_filter.GetLabel(), "*:Trace");
338 ReadSetting("Miscellaneous", Settings::values.use_dev_keys);
339 253
340 // Debugging 254 // Debugging
341 Settings::values.record_frame_times = 255 Settings::values.record_frame_times =
342 sdl2_config->GetBoolean("Debugging", "record_frame_times", false); 256 sdl2_config->GetBoolean("Debugging", "record_frame_times", false);
343 ReadSetting("Debugging", Settings::values.dump_exefs);
344 ReadSetting("Debugging", Settings::values.dump_nso);
345 ReadSetting("Debugging", Settings::values.enable_fs_access_log);
346 ReadSetting("Debugging", Settings::values.reporting_services);
347 ReadSetting("Debugging", Settings::values.quest_flag);
348 ReadSetting("Debugging", Settings::values.use_debug_asserts);
349 ReadSetting("Debugging", Settings::values.use_auto_stub);
350 ReadSetting("Debugging", Settings::values.disable_macro_jit);
351 ReadSetting("Debugging", Settings::values.disable_macro_hle);
352 ReadSetting("Debugging", Settings::values.use_gdbstub);
353 ReadSetting("Debugging", Settings::values.gdbstub_port);
354 257
355 const auto title_list = sdl2_config->Get("AddOns", "title_ids", ""); 258 const auto title_list = sdl2_config->Get("AddOns", "title_ids", "");
356 std::stringstream ss(title_list); 259 std::stringstream ss(title_list);
@@ -368,15 +271,6 @@ void Config::ReadValues() {
368 271
369 Settings::values.disabled_addons.insert_or_assign(title_id, out); 272 Settings::values.disabled_addons.insert_or_assign(title_id, out);
370 } 273 }
371
372 // Web Service
373 ReadSetting("WebService", Settings::values.enable_telemetry);
374 ReadSetting("WebService", Settings::values.web_api_url);
375 ReadSetting("WebService", Settings::values.yuzu_username);
376 ReadSetting("WebService", Settings::values.yuzu_token);
377
378 // Network
379 ReadSetting("Network", Settings::values.network_interface);
380} 274}
381 275
382void Config::Reload() { 276void Config::Reload() {
diff --git a/src/yuzu_cmd/config.h b/src/yuzu_cmd/config.h
index 021438b17..512591a39 100644
--- a/src/yuzu_cmd/config.h
+++ b/src/yuzu_cmd/config.h
@@ -34,4 +34,5 @@ private:
34 */ 34 */
35 template <typename Type, bool ranged> 35 template <typename Type, bool ranged>
36 void ReadSetting(const std::string& group, Settings::Setting<Type, ranged>& setting); 36 void ReadSetting(const std::string& group, Settings::Setting<Type, ranged>& setting);
37 void ReadCategory(Settings::Category category);
37}; 38};