From 04d4b6ab802a1238eaca6c0a16d1a739503b81d9 Mon Sep 17 00:00:00 2001
From: lat9nq <22451773+lat9nq@users.noreply.github.com>
Date: Sat, 10 Jun 2023 23:40:39 -0400
Subject: [PATCH] (ui,)settings: Use explicit instantiation

Reduces compile times a tad on clang.
---
 src/audio_core/sink/sink_details.h       |   5 +-
 src/common/CMakeLists.txt                |   2 +
 src/common/settings.cpp                  |  52 +++
 src/common/settings.h                    | 509 ++---------------------
 src/common/settings_common.h             |  78 ++++
 src/common/settings_setting.h            | 413 ++++++++++++++++++
 src/yuzu/configuration/config.cpp        |   6 +-
 src/yuzu/configuration/shared_widget.cpp |   3 +
 src/yuzu/uisettings.cpp                  |  10 +
 src/yuzu/uisettings.h                    |  10 +
 10 files changed, 613 insertions(+), 475 deletions(-)
 create mode 100644 src/common/settings_common.h
 create mode 100644 src/common/settings_setting.h

diff --git a/src/audio_core/sink/sink_details.h b/src/audio_core/sink/sink_details.h
index 44403db71..c8498842b 100644
--- a/src/audio_core/sink/sink_details.h
+++ b/src/audio_core/sink/sink_details.h
@@ -3,13 +3,12 @@
 
 #pragma once
 
+#include <memory>
 #include <string>
 #include <string_view>
 #include <vector>
+#include "common/settings_enums.h"
 
-namespace Settings {
-enum class AudioEngine : u32;
-}
 namespace AudioCore {
 class AudioManager;
 
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index 5902dd617..3c8368bb2 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -110,9 +110,11 @@ add_library(common STATIC
     scratch_buffer.h
     settings.cpp
     settings.h
+    settings_common.h
     settings_enums.h
     settings_input.cpp
     settings_input.h
+    settings_setting.h
     socket_types.h
     spin_lock.cpp
     spin_lock.h
diff --git a/src/common/settings.cpp b/src/common/settings.cpp
index 84e90f893..10cdea844 100644
--- a/src/common/settings.cpp
+++ b/src/common/settings.cpp
@@ -7,10 +7,17 @@
 #include <exception>
 #include <stdexcept>
 #endif
+#include <compare>
+#include <cstddef>
+#include <filesystem>
+#include <forward_list>
 #include <functional>
 #include <string_view>
+#include <type_traits>
+#include <fmt/core.h>
 
 #include "common/assert.h"
+#include "common/fs/fs_util.h"
 #include "common/fs/path_util.h"
 #include "common/logging/log.h"
 #include "common/settings.h"
@@ -18,6 +25,43 @@
 
 namespace Settings {
 
+#define SETTING(TYPE, RANGED) template class Setting<TYPE, RANGED>
+#define SWITCHABLE(TYPE, RANGED) template class SwitchableSetting<TYPE, RANGED>
+
+SETTING(AudioEngine, false);
+SETTING(bool, false);
+SETTING(int, false);
+SETTING(std::string, false);
+SETTING(u16, false);
+SWITCHABLE(AnisotropyMode, true);
+SWITCHABLE(AntiAliasing, false);
+SWITCHABLE(AspectRatio, true);
+SWITCHABLE(AstcDecodeMode, true);
+SWITCHABLE(AstcRecompression, true);
+SWITCHABLE(AudioMode, true);
+SWITCHABLE(CpuAccuracy, true);
+SWITCHABLE(FullscreenMode, true);
+SWITCHABLE(GpuAccuracy, true);
+SWITCHABLE(Language, true);
+SWITCHABLE(NvdecEmulation, false);
+SWITCHABLE(Region, true);
+SWITCHABLE(RendererBackend, true);
+SWITCHABLE(ScalingFilter, false);
+SWITCHABLE(ShaderBackend, true);
+SWITCHABLE(TimeZone, true);
+SETTING(VSyncMode, true);
+SWITCHABLE(bool, false);
+SWITCHABLE(int, false);
+SWITCHABLE(int, true);
+SWITCHABLE(s64, false);
+SWITCHABLE(u16, true);
+SWITCHABLE(u32, false);
+SWITCHABLE(u8, false);
+SWITCHABLE(u8, true);
+
+#undef SETTING
+#undef SWITCHABLE
+
 Values values;
 static bool configuring_global = true;
 
@@ -238,6 +282,14 @@ void UpdateRescalingInfo() {
     info.active = info.up_scale != 1 || info.down_shift != 0;
 }
 
+std::string BasicSetting::ToStringGlobal() const {
+    return {};
+}
+
+bool BasicSetting::UsingGlobal() const {
+    return true;
+}
+
 void RestoreGlobalState(bool is_powered_on) {
     // If a game is running, DO NOT restore the global settings state
     if (is_powered_on) {
diff --git a/src/common/settings.h b/src/common/settings.h
index ec0686120..e03233eaf 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -5,54 +5,21 @@
 
 #include <algorithm>
 #include <array>
-#include <forward_list>
-#include <functional>
 #include <map>
-#include <optional>
+#include <memory>
 #include <stdexcept>
 #include <string>
-#include <typeindex>
-#include <typeinfo>
 #include <utility>
 #include <vector>
 
 #include "common/common_types.h"
+#include "common/settings_common.h"
 #include "common/settings_enums.h"
 #include "common/settings_input.h"
+#include "common/settings_setting.h"
 
 namespace Settings {
 
-enum class Category : u32 {
-    Audio,
-    Core,
-    Cpu,
-    CpuDebug,
-    CpuUnsafe,
-    Renderer,
-    RendererAdvanced,
-    RendererDebug,
-    System,
-    SystemAudio,
-    DataStorage,
-    Debugging,
-    DebuggingGraphics,
-    Miscellaneous,
-    Network,
-    WebService,
-    AddOns,
-    Controls,
-    Ui,
-    UiGeneral,
-    UiLayout,
-    UiGameList,
-    Screenshots,
-    Shortcuts,
-    Multiplayer,
-    Services,
-    Paths,
-    MaxEnum,
-};
-
 const char* TranslateCategory(Settings::Category category);
 
 struct ResolutionScalingInfo {
@@ -78,441 +45,45 @@ struct ResolutionScalingInfo {
     }
 };
 
-class BasicSetting {
-protected:
-    explicit BasicSetting() = default;
+// Instantiate the classes elsewhere (settings.cpp) to reduce compiler/linker work
+#define SETTING(TYPE, RANGED) extern template class Setting<TYPE, RANGED>
+#define SWITCHABLE(TYPE, RANGED) extern template class SwitchableSetting<TYPE, RANGED>
 
-public:
-    virtual ~BasicSetting() = default;
+SETTING(AudioEngine, false);
+SETTING(bool, false);
+SETTING(int, false);
+SETTING(s32, false);
+SETTING(std::string, false);
+SETTING(std::string, false);
+SETTING(u16, false);
+SWITCHABLE(AnisotropyMode, true);
+SWITCHABLE(AntiAliasing, false);
+SWITCHABLE(AspectRatio, true);
+SWITCHABLE(AstcDecodeMode, true);
+SWITCHABLE(AstcRecompression, true);
+SWITCHABLE(AudioMode, true);
+SWITCHABLE(CpuAccuracy, true);
+SWITCHABLE(FullscreenMode, true);
+SWITCHABLE(GpuAccuracy, true);
+SWITCHABLE(Language, true);
+SWITCHABLE(NvdecEmulation, false);
+SWITCHABLE(Region, true);
+SWITCHABLE(RendererBackend, true);
+SWITCHABLE(ScalingFilter, false);
+SWITCHABLE(ShaderBackend, true);
+SWITCHABLE(TimeZone, true);
+SETTING(VSyncMode, true);
+SWITCHABLE(bool, false);
+SWITCHABLE(int, false);
+SWITCHABLE(int, true);
+SWITCHABLE(s64, false);
+SWITCHABLE(u16, true);
+SWITCHABLE(u32, false);
+SWITCHABLE(u8, false);
+SWITCHABLE(u8, true);
 
-    virtual Category Category() const = 0;
-    virtual constexpr bool Switchable() const = 0;
-    virtual std::string ToString() const = 0;
-    virtual std::string ToStringGlobal() const {
-        return {};
-    }
-    virtual void LoadString(const std::string& load) = 0;
-    virtual std::string Canonicalize() const = 0;
-    virtual const std::string& GetLabel() const = 0;
-    virtual std::string DefaultToString() const = 0;
-    virtual bool Save() const = 0;
-    virtual std::type_index TypeId() const = 0;
-    virtual constexpr bool IsEnum() const = 0;
-    virtual bool RuntimeModfiable() const = 0;
-    virtual void SetGlobal(bool global) {}
-    virtual constexpr u32 Id() const = 0;
-    virtual std::string MinVal() const = 0;
-    virtual std::string MaxVal() const = 0;
-    virtual bool UsingGlobal() const {
-        return true;
-    }
-};
-
-class Linkage {
-public:
-    explicit Linkage(u32 initial_count = 0);
-    ~Linkage();
-    std::map<Category, std::forward_list<BasicSetting*>> by_category{};
-    std::vector<std::function<void()>> restore_functions{};
-    u32 count;
-};
-
-/** The Setting class is a simple resource manager. It defines a label and default value
- * alongside the actual value of the setting for simpler and less-error prone use with frontend
- * configurations. Specifying a default value and label is required. A minimum and maximum range
- * can be specified for sanitization.
- */
-template <typename Type, bool ranged = false>
-class Setting : public BasicSetting {
-protected:
-    Setting() = default;
-
-    /**
-     * Only sets the setting to the given initializer, leaving the other members to their default
-     * initializers.
-     *
-     * @param global_val Initial value of the setting
-     */
-    explicit Setting(const Type& val) : value{val} {}
-
-public:
-    /**
-     * Sets a default value, label, and setting value.
-     *
-     * @param linkage Setting registry
-     * @param default_val Initial value of the setting, and default value of the setting
-     * @param name Label for the setting
-     * @param category_ Category of the setting AKA INI group
-     */
-    explicit Setting(Linkage& linkage, const Type& default_val, const std::string& name,
-                     enum Category category_, bool save_ = true, bool runtime_modifiable_ = false)
-        requires(!ranged)
-        : value{default_val}, default_value{default_val}, label{name}, category{category_},
-          id{linkage.count}, save{save_}, runtime_modifiable{runtime_modifiable_} {
-        linkage.by_category[category].push_front(this);
-        linkage.count++;
-    }
-    virtual ~Setting() = default;
-
-    /**
-     * Sets a default value, minimum value, maximum value, and label.
-     *
-     * @param linkage Setting registry
-     * @param default_val Initial value of the setting, and default value of the setting
-     * @param min_val Sets the minimum allowed value of the setting
-     * @param max_val Sets the maximum allowed value of the setting
-     * @param name Label for the setting
-     * @param category_ Category of the setting AKA INI group
-     */
-    explicit Setting(Linkage& linkage, const Type& default_val, const Type& min_val,
-                     const Type& max_val, const std::string& name, enum Category category_,
-                     bool save_ = true, bool runtime_modifiable_ = false)
-        requires(ranged)
-        : value{default_val}, default_value{default_val}, maximum{max_val}, minimum{min_val},
-          label{name}, category{category_}, id{linkage.count}, save{save_},
-          runtime_modifiable{runtime_modifiable_} {
-        linkage.by_category[category].push_front(this);
-        linkage.count++;
-    }
-
-    /**
-     *  Returns a reference to the setting's value.
-     *
-     * @returns A reference to the setting
-     */
-    [[nodiscard]] virtual const Type& GetValue() const {
-        return value;
-    }
-
-    /**
-     * Sets the setting to the given value.
-     *
-     * @param val The desired value
-     */
-    virtual void SetValue(const Type& val) {
-        Type temp{ranged ? std::clamp(val, minimum, maximum) : val};
-        std::swap(value, temp);
-    }
-
-    /**
-     * Returns the value that this setting was created with.
-     *
-     * @returns A reference to the default value
-     */
-    [[nodiscard]] const Type& GetDefault() const {
-        return default_value;
-    }
-
-    /**
-     * Returns the label this setting was created with.
-     *
-     * @returns A reference to the label
-     */
-    [[nodiscard]] const std::string& GetLabel() const override {
-        return label;
-    }
-
-    /**
-     * Returns the setting's category AKA INI group.
-     *
-     * @returns The setting's category
-     */
-    [[nodiscard]] enum Category Category() const override {
-        return category;
-    }
-
-    [[nodiscard]] bool RuntimeModfiable() const override {
-        return runtime_modifiable;
-    }
-
-    [[nodiscard]] constexpr bool IsEnum() const override {
-        return std::is_enum<Type>::value;
-    }
-
-    /**
-     * Returns whether the current setting is Switchable.
-     *
-     * @returns If the setting is a SwitchableSetting
-     */
-    [[nodiscard]] virtual constexpr bool Switchable() const override {
-        return false;
-    }
-
-protected:
-    std::string ToString(const Type& value_) const {
-        if constexpr (std::is_same<Type, std::string>()) {
-            return value_;
-        } else if constexpr (std::is_same<Type, std::optional<u32>>()) {
-            return value_.has_value() ? std::to_string(*value_) : "none";
-        } else if constexpr (std::is_same<Type, bool>()) {
-            return value_ ? "true" : "false";
-        } else if (std::is_same<Type, AudioEngine>()) {
-            return CanonicalizeEnum(value_);
-        } else {
-            return std::to_string(static_cast<u64>(value_));
-        }
-    }
-
-public:
-    /**
-     * Converts the value of the setting to a std::string. Respects the global state if the setting
-     * has one.
-     *
-     * @returns The current setting as a std::string
-     */
-    std::string ToString() const override {
-        return ToString(this->GetValue());
-    }
-
-    /**
-     * Returns the default value of the setting as a std::string.
-     *
-     * @returns The default value as a string.
-     */
-    std::string DefaultToString() const override {
-        return ToString(default_value);
-    }
-
-    /**
-     * Assigns a value to the setting.
-     *
-     * @param val The desired setting value
-     *
-     * @returns A reference to the setting
-     */
-    virtual const Type& operator=(const Type& val) {
-        Type temp{ranged ? std::clamp(val, minimum, maximum) : val};
-        std::swap(value, temp);
-        return value;
-    }
-
-    /**
-     * Returns a reference to the setting.
-     *
-     * @returns A reference to the setting
-     */
-    explicit virtual operator const Type&() const {
-        return value;
-    }
-
-    /**
-     * Converts the given value to the Setting's type of value. Uses SetValue to enter the setting,
-     * thus respecting its constraints.
-     *
-     * @param input The desired value
-     */
-    void LoadString(const std::string& input) override {
-        if (input.empty()) {
-            this->SetValue(this->GetDefault());
-            return;
-        }
-        try {
-            if constexpr (std::is_same<Type, std::string>()) {
-                this->SetValue(input);
-            } else if constexpr (std::is_same<Type, std::optional<u32>>()) {
-                this->SetValue(static_cast<u32>(std::stoul(input)));
-            } else if constexpr (std::is_same<Type, bool>()) {
-                this->SetValue(input == "true");
-            } else if constexpr (std::is_same<Type, AudioEngine>()) {
-                this->SetValue(ToEnum<Type>(input));
-            } else {
-                this->SetValue(static_cast<Type>(std::stoll(input)));
-            }
-        } catch (std::invalid_argument) {
-            this->SetValue(this->GetDefault());
-        }
-    }
-
-    [[nodiscard]] std::string constexpr Canonicalize() const override {
-        if constexpr (std::is_enum<Type>::value) {
-            return CanonicalizeEnum(this->GetValue());
-        }
-        return ToString(this->GetValue());
-    }
-
-    /**
-     * Returns the save preference of the setting i.e. when saving or reading the setting from a
-     * frontend, whether this setting should be skipped.
-     *
-     * @returns The save preference
-     */
-    virtual bool Save() const override {
-        return save;
-    }
-
-    /**
-     * Gives us another way to identify the setting without having to go through a string.
-     *
-     * @returns the type_index of the setting's type
-     */
-    virtual std::type_index TypeId() const override {
-        return std::type_index(typeid(Type));
-    }
-
-    virtual constexpr u32 Id() const override {
-        return id;
-    }
-
-    virtual std::string MinVal() const override {
-        return this->ToString(minimum);
-    }
-    virtual std::string MaxVal() const override {
-        return this->ToString(maximum);
-    }
-
-protected:
-    Type value{};                 ///< The setting
-    const Type default_value{};   ///< The default value
-    const Type maximum{};         ///< Maximum allowed value of the setting
-    const Type minimum{};         ///< Minimum allowed value of the setting
-    const std::string label{};    ///< The setting's label
-    const enum Category category; ///< The setting's category AKA INI group
-    const u32 id;
-    bool save;
-    bool runtime_modifiable;
-};
-
-/**
- * The SwitchableSetting class is a slightly more complex version of the Setting class. This adds a
- * custom setting to switch to when a guest application specifically requires it. The effect is that
- * other components of the emulator can access the setting's intended value without any need for the
- * component to ask whether the custom or global setting is needed at the moment.
- *
- * By default, the global setting is used.
- */
-template <typename Type, bool ranged = false>
-class SwitchableSetting : virtual public Setting<Type, ranged> {
-public:
-    /**
-     * Sets a default value, label, and setting value.
-     *
-     * @param linkage Setting registry
-     * @param default_val Initial value of the setting, and default value of the setting
-     * @param name Label for the setting
-     * @param category_ Category of the setting AKA INI group
-     */
-    explicit SwitchableSetting(Linkage& linkage, const Type& default_val, const std::string& name,
-                               Category category, bool save = true, bool runtime_modifiable = false)
-        requires(!ranged)
-        : Setting<Type, false>{linkage, default_val, name, category, save, runtime_modifiable} {
-        linkage.restore_functions.emplace_back([this]() { this->SetGlobal(true); });
-    }
-    virtual ~SwitchableSetting() = default;
-
-    /**
-     * Sets a default value, minimum value, maximum value, and label.
-     *
-     * @param linkage Setting registry
-     * @param default_val Initial value of the setting, and default value of the setting
-     * @param min_val Sets the minimum allowed value of the setting
-     * @param max_val Sets the maximum allowed value of the setting
-     * @param name Label for the setting
-     * @param category_ Category of the setting AKA INI group
-     */
-    explicit SwitchableSetting(Linkage& linkage, const Type& default_val, const Type& min_val,
-                               const Type& max_val, const std::string& name, Category category,
-                               bool save = true, bool runtime_modifiable = false)
-        requires(ranged)
-        : Setting<Type, true>{linkage, default_val, min_val, max_val,
-                              name,    category,    save,    runtime_modifiable} {
-        linkage.restore_functions.emplace_back([this]() { this->SetGlobal(true); });
-    }
-
-    /**
-     * Tells this setting to represent either the global or custom setting when other member
-     * functions are used.
-     *
-     * @param to_global Whether to use the global or custom setting.
-     */
-    void SetGlobal(bool to_global) override {
-        use_global = to_global;
-    }
-
-    /**
-     * Returns whether this setting is using the global setting or not.
-     *
-     * @returns The global state
-     */
-    [[nodiscard]] bool UsingGlobal() const override {
-        return use_global;
-    }
-
-    /**
-     * Returns either the global or custom setting depending on the values of this setting's global
-     * state or if the global value was specifically requested.
-     *
-     * @param need_global Request global value regardless of setting's state; defaults to false
-     *
-     * @returns The required value of the setting
-     */
-    [[nodiscard]] virtual const Type& GetValue() const override {
-        if (use_global) {
-            return this->value;
-        }
-        return custom;
-    }
-    [[nodiscard]] virtual const Type& GetValue(bool need_global) const {
-        if (use_global || need_global) {
-            return this->value;
-        }
-        return custom;
-    }
-
-    /**
-     * Sets the current setting value depending on the global state.
-     *
-     * @param val The new value
-     */
-    void SetValue(const Type& val) override {
-        Type temp{ranged ? std::clamp(val, this->minimum, this->maximum) : val};
-        if (use_global) {
-            std::swap(this->value, temp);
-        } else {
-            std::swap(custom, temp);
-        }
-    }
-
-    [[nodiscard]] virtual constexpr bool Switchable() const override {
-        return true;
-    }
-
-    [[nodiscard]] virtual std::string ToStringGlobal() const override {
-        return this->ToString(this->value);
-    }
-
-    /**
-     * Assigns the current setting value depending on the global state.
-     *
-     * @param val The new value
-     *
-     * @returns A reference to the current setting value
-     */
-    const Type& operator=(const Type& val) override {
-        Type temp{ranged ? std::clamp(val, this->minimum, this->maximum) : val};
-        if (use_global) {
-            std::swap(this->value, temp);
-            return this->value;
-        }
-        std::swap(custom, temp);
-        return custom;
-    }
-
-    /**
-     * Returns the current setting value depending on the global state.
-     *
-     * @returns A reference to the current setting value
-     */
-    virtual explicit operator const Type&() const override {
-        if (use_global) {
-            return this->value;
-        }
-        return custom;
-    }
-
-protected:
-    bool use_global{true}; ///< The setting's global state
-    Type custom{};         ///< The custom value of the setting
-};
+#undef SETTING
+#undef SWITCHABLE
 
 /**
  * The InputSetting class allows for getting a reference to either the global or custom members.
diff --git a/src/common/settings_common.h b/src/common/settings_common.h
new file mode 100644
index 000000000..93fddeba6
--- /dev/null
+++ b/src/common/settings_common.h
@@ -0,0 +1,78 @@
+#pragma once
+
+#include <forward_list>
+#include <functional>
+#include <map>
+#include <string>
+#include <typeindex>
+#include "common/common_types.h"
+
+namespace Settings {
+
+enum class Category : u32 {
+    Audio,
+    Core,
+    Cpu,
+    CpuDebug,
+    CpuUnsafe,
+    Renderer,
+    RendererAdvanced,
+    RendererDebug,
+    System,
+    SystemAudio,
+    DataStorage,
+    Debugging,
+    DebuggingGraphics,
+    Miscellaneous,
+    Network,
+    WebService,
+    AddOns,
+    Controls,
+    Ui,
+    UiGeneral,
+    UiLayout,
+    UiGameList,
+    Screenshots,
+    Shortcuts,
+    Multiplayer,
+    Services,
+    Paths,
+    MaxEnum,
+};
+
+class BasicSetting {
+protected:
+    explicit BasicSetting() = default;
+
+public:
+    virtual ~BasicSetting() = default;
+
+    virtual Category Category() const = 0;
+    virtual constexpr bool Switchable() const = 0;
+    virtual std::string ToString() const = 0;
+    virtual std::string ToStringGlobal() const;
+    virtual void LoadString(const std::string& load) = 0;
+    virtual std::string Canonicalize() const = 0;
+    virtual const std::string& GetLabel() const = 0;
+    virtual std::string DefaultToString() const = 0;
+    virtual bool Save() const = 0;
+    virtual std::type_index TypeId() const = 0;
+    virtual constexpr bool IsEnum() const = 0;
+    virtual bool RuntimeModfiable() const = 0;
+    virtual void SetGlobal(bool global) {}
+    virtual constexpr u32 Id() const = 0;
+    virtual std::string MinVal() const = 0;
+    virtual std::string MaxVal() const = 0;
+    virtual bool UsingGlobal() const;
+};
+
+class Linkage {
+public:
+    explicit Linkage(u32 initial_count = 0);
+    ~Linkage();
+    std::map<Category, std::forward_list<BasicSetting*>> by_category{};
+    std::vector<std::function<void()>> restore_functions{};
+    u32 count;
+};
+
+} // namespace Settings
diff --git a/src/common/settings_setting.h b/src/common/settings_setting.h
new file mode 100644
index 000000000..f226e38d4
--- /dev/null
+++ b/src/common/settings_setting.h
@@ -0,0 +1,413 @@
+#pragma once
+
+#include <map>
+#include <optional>
+#include <string>
+#include <typeindex>
+#include <typeinfo>
+#include "common/common_types.h"
+#include "common/settings_common.h"
+#include "common/settings_enums.h"
+
+namespace Settings {
+
+/** The Setting class is a simple resource manager. It defines a label and default value
+ * alongside the actual value of the setting for simpler and less-error prone use with frontend
+ * configurations. Specifying a default value and label is required. A minimum and maximum range
+ * can be specified for sanitization.
+ */
+template <typename Type, bool ranged = false>
+class Setting : public BasicSetting {
+protected:
+    Setting() = default;
+
+    /**
+     * Only sets the setting to the given initializer, leaving the other members to their default
+     * initializers.
+     *
+     * @param global_val Initial value of the setting
+     */
+    explicit Setting(const Type& val)
+        : value{val},
+          default_value{}, maximum{}, minimum{}, label{}, category{Category::Miscellaneous}, id{} {}
+
+public:
+    /**
+     * Sets a default value, label, and setting value.
+     *
+     * @param linkage Setting registry
+     * @param default_val Initial value of the setting, and default value of the setting
+     * @param name Label for the setting
+     * @param category_ Category of the setting AKA INI group
+     */
+    explicit Setting(Linkage& linkage, const Type& default_val, const std::string& name,
+                     enum Category category_, bool save_ = true, bool runtime_modifiable_ = false)
+        requires(!ranged)
+        : value{default_val}, default_value{default_val}, label{name}, category{category_},
+          id{linkage.count}, save{save_}, runtime_modifiable{runtime_modifiable_} {
+        linkage.by_category[category].push_front(this);
+        linkage.count++;
+    }
+    virtual ~Setting() = default;
+
+    /**
+     * Sets a default value, minimum value, maximum value, and label.
+     *
+     * @param linkage Setting registry
+     * @param default_val Initial value of the setting, and default value of the setting
+     * @param min_val Sets the minimum allowed value of the setting
+     * @param max_val Sets the maximum allowed value of the setting
+     * @param name Label for the setting
+     * @param category_ Category of the setting AKA INI group
+     */
+    explicit Setting(Linkage& linkage, const Type& default_val, const Type& min_val,
+                     const Type& max_val, const std::string& name, enum Category category_,
+                     bool save_ = true, bool runtime_modifiable_ = false)
+        requires(ranged)
+        : value{default_val}, default_value{default_val}, maximum{max_val}, minimum{min_val},
+          label{name}, category{category_}, id{linkage.count}, save{save_},
+          runtime_modifiable{runtime_modifiable_} {
+        linkage.by_category[category].push_front(this);
+        linkage.count++;
+    }
+
+    /**
+     *  Returns a reference to the setting's value.
+     *
+     * @returns A reference to the setting
+     */
+    [[nodiscard]] virtual const Type& GetValue() const {
+        return value;
+    }
+
+    /**
+     * Sets the setting to the given value.
+     *
+     * @param val The desired value
+     */
+    virtual void SetValue(const Type& val) {
+        Type temp{ranged ? std::clamp(val, minimum, maximum) : val};
+        std::swap(value, temp);
+    }
+
+    /**
+     * Returns the value that this setting was created with.
+     *
+     * @returns A reference to the default value
+     */
+    [[nodiscard]] const Type& GetDefault() const {
+        return default_value;
+    }
+
+    /**
+     * Returns the label this setting was created with.
+     *
+     * @returns A reference to the label
+     */
+    [[nodiscard]] const std::string& GetLabel() const override {
+        return label;
+    }
+
+    /**
+     * Returns the setting's category AKA INI group.
+     *
+     * @returns The setting's category
+     */
+    [[nodiscard]] enum Category Category() const override {
+        return category;
+    }
+
+    [[nodiscard]] bool RuntimeModfiable() const override {
+        return runtime_modifiable;
+    }
+
+    [[nodiscard]] constexpr bool IsEnum() const override {
+        return std::is_enum<Type>::value;
+    }
+
+    /**
+     * Returns whether the current setting is Switchable.
+     *
+     * @returns If the setting is a SwitchableSetting
+     */
+    [[nodiscard]] virtual constexpr bool Switchable() const override {
+        return false;
+    }
+
+protected:
+    std::string ToString(const Type& value_) const {
+        if constexpr (std::is_same<Type, std::string>()) {
+            return value_;
+        } else if constexpr (std::is_same<Type, std::optional<u32>>()) {
+            return value_.has_value() ? std::to_string(*value_) : "none";
+        } else if constexpr (std::is_same<Type, bool>()) {
+            return value_ ? "true" : "false";
+        } else if (std::is_same<Type, AudioEngine>()) {
+            return CanonicalizeEnum(value_);
+        } else {
+            return std::to_string(static_cast<u64>(value_));
+        }
+    }
+
+public:
+    /**
+     * Converts the value of the setting to a std::string. Respects the global state if the setting
+     * has one.
+     *
+     * @returns The current setting as a std::string
+     */
+    std::string ToString() const override {
+        return ToString(this->GetValue());
+    }
+
+    /**
+     * Returns the default value of the setting as a std::string.
+     *
+     * @returns The default value as a string.
+     */
+    std::string DefaultToString() const override {
+        return ToString(default_value);
+    }
+
+    /**
+     * Assigns a value to the setting.
+     *
+     * @param val The desired setting value
+     *
+     * @returns A reference to the setting
+     */
+    virtual const Type& operator=(const Type& val) {
+        Type temp{ranged ? std::clamp(val, minimum, maximum) : val};
+        std::swap(value, temp);
+        return value;
+    }
+
+    /**
+     * Returns a reference to the setting.
+     *
+     * @returns A reference to the setting
+     */
+    explicit virtual operator const Type&() const {
+        return value;
+    }
+
+    /**
+     * Converts the given value to the Setting's type of value. Uses SetValue to enter the setting,
+     * thus respecting its constraints.
+     *
+     * @param input The desired value
+     */
+    void LoadString(const std::string& input) override {
+        if (input.empty()) {
+            this->SetValue(this->GetDefault());
+            return;
+        }
+        try {
+            if constexpr (std::is_same<Type, std::string>()) {
+                this->SetValue(input);
+            } else if constexpr (std::is_same<Type, std::optional<u32>>()) {
+                this->SetValue(static_cast<u32>(std::stoul(input)));
+            } else if constexpr (std::is_same<Type, bool>()) {
+                this->SetValue(input == "true");
+            } else if constexpr (std::is_same<Type, AudioEngine>()) {
+                this->SetValue(ToEnum<Type>(input));
+            } else {
+                this->SetValue(static_cast<Type>(std::stoll(input)));
+            }
+        } catch (std::invalid_argument) {
+            this->SetValue(this->GetDefault());
+        }
+    }
+
+    [[nodiscard]] std::string constexpr Canonicalize() const override {
+        if constexpr (std::is_enum<Type>::value) {
+            return CanonicalizeEnum(this->GetValue());
+        }
+        return ToString(this->GetValue());
+    }
+
+    /**
+     * Returns the save preference of the setting i.e. when saving or reading the setting from a
+     * frontend, whether this setting should be skipped.
+     *
+     * @returns The save preference
+     */
+    virtual bool Save() const override {
+        return save;
+    }
+
+    /**
+     * Gives us another way to identify the setting without having to go through a string.
+     *
+     * @returns the type_index of the setting's type
+     */
+    virtual std::type_index TypeId() const override {
+        return std::type_index(typeid(Type));
+    }
+
+    virtual constexpr u32 Id() const override {
+        return id;
+    }
+
+    virtual std::string MinVal() const override {
+        return this->ToString(minimum);
+    }
+    virtual std::string MaxVal() const override {
+        return this->ToString(maximum);
+    }
+
+protected:
+    Type value{};                 ///< The setting
+    const Type default_value{};   ///< The default value
+    const Type maximum{};         ///< Maximum allowed value of the setting
+    const Type minimum{};         ///< Minimum allowed value of the setting
+    const std::string label{};    ///< The setting's label
+    const enum Category category; ///< The setting's category AKA INI group
+    const u32 id;
+    bool save;
+    bool runtime_modifiable;
+};
+
+/**
+ * The SwitchableSetting class is a slightly more complex version of the Setting class. This adds a
+ * custom setting to switch to when a guest application specifically requires it. The effect is that
+ * other components of the emulator can access the setting's intended value without any need for the
+ * component to ask whether the custom or global setting is needed at the moment.
+ *
+ * By default, the global setting is used.
+ */
+template <typename Type, bool ranged = false>
+class SwitchableSetting : virtual public Setting<Type, ranged> {
+public:
+    /**
+     * Sets a default value, label, and setting value.
+     *
+     * @param linkage Setting registry
+     * @param default_val Initial value of the setting, and default value of the setting
+     * @param name Label for the setting
+     * @param category_ Category of the setting AKA INI group
+     */
+    explicit SwitchableSetting(Linkage& linkage, const Type& default_val, const std::string& name,
+                               Category category, bool save = true, bool runtime_modifiable = false)
+        requires(!ranged)
+        : Setting<Type, false>{linkage, default_val, name, category, save, runtime_modifiable} {
+        linkage.restore_functions.emplace_back([this]() { this->SetGlobal(true); });
+    }
+    virtual ~SwitchableSetting() = default;
+
+    /**
+     * Sets a default value, minimum value, maximum value, and label.
+     *
+     * @param linkage Setting registry
+     * @param default_val Initial value of the setting, and default value of the setting
+     * @param min_val Sets the minimum allowed value of the setting
+     * @param max_val Sets the maximum allowed value of the setting
+     * @param name Label for the setting
+     * @param category_ Category of the setting AKA INI group
+     */
+    explicit SwitchableSetting(Linkage& linkage, const Type& default_val, const Type& min_val,
+                               const Type& max_val, const std::string& name, Category category,
+                               bool save = true, bool runtime_modifiable = false)
+        requires(ranged)
+        : Setting<Type, true>{linkage, default_val, min_val, max_val,
+                              name,    category,    save,    runtime_modifiable} {
+        linkage.restore_functions.emplace_back([this]() { this->SetGlobal(true); });
+    }
+
+    /**
+     * Tells this setting to represent either the global or custom setting when other member
+     * functions are used.
+     *
+     * @param to_global Whether to use the global or custom setting.
+     */
+    void SetGlobal(bool to_global) override {
+        use_global = to_global;
+    }
+
+    /**
+     * Returns whether this setting is using the global setting or not.
+     *
+     * @returns The global state
+     */
+    [[nodiscard]] bool UsingGlobal() const override {
+        return use_global;
+    }
+
+    /**
+     * Returns either the global or custom setting depending on the values of this setting's global
+     * state or if the global value was specifically requested.
+     *
+     * @param need_global Request global value regardless of setting's state; defaults to false
+     *
+     * @returns The required value of the setting
+     */
+    [[nodiscard]] virtual const Type& GetValue() const override {
+        if (use_global) {
+            return this->value;
+        }
+        return custom;
+    }
+    [[nodiscard]] virtual const Type& GetValue(bool need_global) const {
+        if (use_global || need_global) {
+            return this->value;
+        }
+        return custom;
+    }
+
+    /**
+     * Sets the current setting value depending on the global state.
+     *
+     * @param val The new value
+     */
+    void SetValue(const Type& val) override {
+        Type temp{ranged ? std::clamp(val, this->minimum, this->maximum) : val};
+        if (use_global) {
+            std::swap(this->value, temp);
+        } else {
+            std::swap(custom, temp);
+        }
+    }
+
+    [[nodiscard]] virtual constexpr bool Switchable() const override {
+        return true;
+    }
+
+    [[nodiscard]] virtual std::string ToStringGlobal() const override {
+        return this->ToString(this->value);
+    }
+
+    /**
+     * Assigns the current setting value depending on the global state.
+     *
+     * @param val The new value
+     *
+     * @returns A reference to the current setting value
+     */
+    const Type& operator=(const Type& val) override {
+        Type temp{ranged ? std::clamp(val, this->minimum, this->maximum) : val};
+        if (use_global) {
+            std::swap(this->value, temp);
+            return this->value;
+        }
+        std::swap(custom, temp);
+        return custom;
+    }
+
+    /**
+     * Returns the current setting value depending on the global state.
+     *
+     * @returns A reference to the current setting value
+     */
+    virtual explicit operator const Type&() const override {
+        if (use_global) {
+            return this->value;
+        }
+        return custom;
+    }
+
+protected:
+    bool use_global{true}; ///< The setting's global state
+    Type custom{};         ///< The custom value of the setting
+};
+
+} // namespace Settings
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 051756452..ba1847976 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -102,9 +102,9 @@ const std::map<Settings::RendererBackend, QString> Config::renderer_backend_text
 };
 
 const std::map<Settings::ShaderBackend, QString> Config::shader_backend_texts_map = {
-    {Settings::ShaderBackend::GLSL, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "GLSL"))},
-    {Settings::ShaderBackend::GLASM, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "GLASM"))},
-    {Settings::ShaderBackend::SPIRV, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "SPIRV"))},
+    {Settings::ShaderBackend::Glsl, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "GLSL"))},
+    {Settings::ShaderBackend::Glasm, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "GLASM"))},
+    {Settings::ShaderBackend::SpirV, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "SPIRV"))},
 };
 
 // This shouldn't have anything except static initializers (no functions). So
diff --git a/src/yuzu/configuration/shared_widget.cpp b/src/yuzu/configuration/shared_widget.cpp
index efb3b329c..6142c3cb9 100644
--- a/src/yuzu/configuration/shared_widget.cpp
+++ b/src/yuzu/configuration/shared_widget.cpp
@@ -21,7 +21,10 @@
 #include <QStyle>
 #include <QValidator>
 #include <QWidget>
+#include <fmt/core.h>
+#include "common/assert.h"
 #include "common/common_types.h"
+#include "common/logging/log.h"
 #include "common/settings.h"
 #include "yuzu/configuration/configuration_shared.h"
 #include "yuzu/configuration/shared_translation.h"
diff --git a/src/yuzu/uisettings.cpp b/src/yuzu/uisettings.cpp
index 2c1b547fb..2a02a27bc 100644
--- a/src/yuzu/uisettings.cpp
+++ b/src/yuzu/uisettings.cpp
@@ -3,6 +3,16 @@
 
 #include "yuzu/uisettings.h"
 
+namespace Settings {
+template class Setting<bool>;
+template class Setting<std::string>;
+template class Setting<u16, true>;
+template class Setting<u32>;
+template class Setting<u8, true>;
+template class Setting<u8>;
+template class Setting<unsigned long long>;
+} // namespace Settings
+
 namespace UISettings {
 
 const Themes themes{{
diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h
index a734513ea..2152b0b3b 100644
--- a/src/yuzu/uisettings.h
+++ b/src/yuzu/uisettings.h
@@ -17,6 +17,16 @@
 using Settings::Category;
 using Settings::Setting;
 
+namespace Settings {
+extern template class Setting<bool>;
+extern template class Setting<std::string>;
+extern template class Setting<u16, true>;
+extern template class Setting<u32>;
+extern template class Setting<u8, true>;
+extern template class Setting<u8>;
+extern template class Setting<unsigned long long>;
+} // namespace Settings
+
 namespace UISettings {
 
 bool IsDarkTheme();