summaryrefslogtreecommitdiffstats
path: root/src/common
diff options
context:
space:
mode:
Diffstat (limited to 'src/common')
-rw-r--r--src/common/CMakeLists.txt4
-rw-r--r--src/common/alignment.h28
-rw-r--r--src/common/common_funcs.h29
-rw-r--r--src/common/div_ceil.h8
-rw-r--r--src/common/error.cpp (renamed from src/common/misc.cpp)6
-rw-r--r--src/common/error.h21
-rw-r--r--src/common/fs/fs_paths.h1
-rw-r--r--src/common/fs/path_util.cpp28
-rw-r--r--src/common/fs/path_util.h1
-rw-r--r--src/common/host_memory.cpp2
-rw-r--r--src/common/intrusive_red_black_tree.h17
-rw-r--r--src/common/logging/backend.cpp355
-rw-r--r--src/common/logging/backend.h113
-rw-r--r--src/common/logging/filter.cpp1
-rw-r--r--src/common/logging/log.h6
-rw-r--r--src/common/logging/log_entry.h28
-rw-r--r--src/common/logging/text_formatter.cpp1
-rw-r--r--src/common/logging/types.h18
-rw-r--r--src/common/lru_cache.h140
-rw-r--r--src/common/param_package.cpp1
-rw-r--r--src/common/settings.cpp13
-rw-r--r--src/common/settings.h212
-rw-r--r--src/common/string_util.cpp12
-rw-r--r--src/common/string_util.h2
-rw-r--r--src/common/thread.cpp6
-rw-r--r--src/common/threadsafe_queue.h37
-rw-r--r--src/common/uuid.h18
-rw-r--r--src/common/vector_math.h4
-rw-r--r--src/common/x64/xbyak_abi.h2
-rw-r--r--src/common/x64/xbyak_util.h2
30 files changed, 732 insertions, 384 deletions
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index 57922b51c..cb5c0f326 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -53,6 +53,8 @@ add_library(common STATIC
div_ceil.h
dynamic_library.cpp
dynamic_library.h
+ error.cpp
+ error.h
fiber.cpp
fiber.h
fs/file.cpp
@@ -77,6 +79,7 @@ add_library(common STATIC
logging/filter.cpp
logging/filter.h
logging/log.h
+ logging/log_entry.h
logging/text_formatter.cpp
logging/text_formatter.h
logging/types.h
@@ -88,7 +91,6 @@ add_library(common STATIC
microprofile.cpp
microprofile.h
microprofileui.h
- misc.cpp
nvidia_flags.cpp
nvidia_flags.h
page_table.cpp
diff --git a/src/common/alignment.h b/src/common/alignment.h
index 32d796ffa..8570c7d3c 100644
--- a/src/common/alignment.h
+++ b/src/common/alignment.h
@@ -9,41 +9,48 @@
namespace Common {
template <typename T>
-requires std::is_unsigned_v<T>[[nodiscard]] constexpr T AlignUp(T value, size_t size) {
+requires std::is_unsigned_v<T>
+[[nodiscard]] constexpr T AlignUp(T value, size_t size) {
auto mod{static_cast<T>(value % size)};
value -= mod;
return static_cast<T>(mod == T{0} ? value : value + size);
}
template <typename T>
-requires std::is_unsigned_v<T>[[nodiscard]] constexpr T AlignUpLog2(T value, size_t align_log2) {
+requires std::is_unsigned_v<T>
+[[nodiscard]] constexpr T AlignUpLog2(T value, size_t align_log2) {
return static_cast<T>((value + ((1ULL << align_log2) - 1)) >> align_log2 << align_log2);
}
template <typename T>
-requires std::is_unsigned_v<T>[[nodiscard]] constexpr T AlignDown(T value, size_t size) {
+requires std::is_unsigned_v<T>
+[[nodiscard]] constexpr T AlignDown(T value, size_t size) {
return static_cast<T>(value - value % size);
}
template <typename T>
-requires std::is_unsigned_v<T>[[nodiscard]] constexpr bool Is4KBAligned(T value) {
+requires std::is_unsigned_v<T>
+[[nodiscard]] constexpr bool Is4KBAligned(T value) {
return (value & 0xFFF) == 0;
}
template <typename T>
-requires std::is_unsigned_v<T>[[nodiscard]] constexpr bool IsWordAligned(T value) {
+requires std::is_unsigned_v<T>
+[[nodiscard]] constexpr bool IsWordAligned(T value) {
return (value & 0b11) == 0;
}
template <typename T>
-requires std::is_integral_v<T>[[nodiscard]] constexpr bool IsAligned(T value, size_t alignment) {
+requires std::is_integral_v<T>
+[[nodiscard]] constexpr bool IsAligned(T value, size_t alignment) {
using U = typename std::make_unsigned_t<T>;
const U mask = static_cast<U>(alignment - 1);
return (value & mask) == 0;
}
template <typename T, typename U>
-requires std::is_integral_v<T>[[nodiscard]] constexpr T DivideUp(T x, U y) {
+requires std::is_integral_v<T>
+[[nodiscard]] constexpr T DivideUp(T x, U y) {
return (x + (y - 1)) / y;
}
@@ -57,7 +64,7 @@ public:
using propagate_on_container_copy_assignment = std::true_type;
using propagate_on_container_move_assignment = std::true_type;
using propagate_on_container_swap = std::true_type;
- using is_always_equal = std::true_type;
+ using is_always_equal = std::false_type;
constexpr AlignmentAllocator() noexcept = default;
@@ -76,6 +83,11 @@ public:
struct rebind {
using other = AlignmentAllocator<T2, Align>;
};
+
+ template <typename T2, size_t Align2>
+ constexpr bool operator==(const AlignmentAllocator<T2, Align2>&) const noexcept {
+ return std::is_same_v<T, T2> && Align == Align2;
+ }
};
} // namespace Common
diff --git a/src/common/common_funcs.h b/src/common/common_funcs.h
index 53bd7da60..4c1e29de6 100644
--- a/src/common/common_funcs.h
+++ b/src/common/common_funcs.h
@@ -4,9 +4,8 @@
#pragma once
-#include <algorithm>
#include <array>
-#include <string>
+#include <iterator>
#if !defined(ARCHITECTURE_x86_64)
#include <cstdlib> // for exit
@@ -49,16 +48,6 @@ __declspec(dllimport) void __stdcall DebugBreak(void);
#endif // _MSC_VER ndef
-// Generic function to get last error message.
-// Call directly after the command or use the error num.
-// This function might change the error code.
-// Defined in misc.cpp.
-[[nodiscard]] std::string GetLastErrorMsg();
-
-// Like GetLastErrorMsg(), but passing an explicit error code.
-// Defined in misc.cpp.
-[[nodiscard]] std::string NativeErrorToString(int e);
-
#define DECLARE_ENUM_FLAG_OPERATORS(type) \
[[nodiscard]] constexpr type operator|(type a, type b) noexcept { \
using T = std::underlying_type_t<type>; \
@@ -72,6 +61,14 @@ __declspec(dllimport) void __stdcall DebugBreak(void);
using T = std::underlying_type_t<type>; \
return static_cast<type>(static_cast<T>(a) ^ static_cast<T>(b)); \
} \
+ [[nodiscard]] constexpr type operator<<(type a, type b) noexcept { \
+ using T = std::underlying_type_t<type>; \
+ return static_cast<type>(static_cast<T>(a) << static_cast<T>(b)); \
+ } \
+ [[nodiscard]] constexpr type operator>>(type a, type b) noexcept { \
+ using T = std::underlying_type_t<type>; \
+ return static_cast<type>(static_cast<T>(a) >> static_cast<T>(b)); \
+ } \
constexpr type& operator|=(type& a, type b) noexcept { \
a = a | b; \
return a; \
@@ -84,6 +81,14 @@ __declspec(dllimport) void __stdcall DebugBreak(void);
a = a ^ b; \
return a; \
} \
+ constexpr type& operator<<=(type& a, type b) noexcept { \
+ a = a << b; \
+ return a; \
+ } \
+ constexpr type& operator>>=(type& a, type b) noexcept { \
+ a = a >> b; \
+ return a; \
+ } \
[[nodiscard]] constexpr type operator~(type key) noexcept { \
using T = std::underlying_type_t<type>; \
return static_cast<type>(~static_cast<T>(key)); \
diff --git a/src/common/div_ceil.h b/src/common/div_ceil.h
index 95e1489a9..e1db35464 100644
--- a/src/common/div_ceil.h
+++ b/src/common/div_ceil.h
@@ -11,15 +11,15 @@ namespace Common {
/// Ceiled integer division.
template <typename N, typename D>
-requires std::is_integral_v<N>&& std::is_unsigned_v<D>[[nodiscard]] constexpr N DivCeil(N number,
- D divisor) {
+requires std::is_integral_v<N> && std::is_unsigned_v<D>
+[[nodiscard]] constexpr N DivCeil(N number, D divisor) {
return static_cast<N>((static_cast<D>(number) + divisor - 1) / divisor);
}
/// Ceiled integer division with logarithmic divisor in base 2
template <typename N, typename D>
-requires std::is_integral_v<N>&& std::is_unsigned_v<D>[[nodiscard]] constexpr N DivCeilLog2(
- N value, D alignment_log2) {
+requires std::is_integral_v<N> && std::is_unsigned_v<D>
+[[nodiscard]] constexpr N DivCeilLog2(N value, D alignment_log2) {
return static_cast<N>((static_cast<D>(value) + (D(1) << alignment_log2) - 1) >> alignment_log2);
}
diff --git a/src/common/misc.cpp b/src/common/error.cpp
index 495385b9e..d4455e310 100644
--- a/src/common/misc.cpp
+++ b/src/common/error.cpp
@@ -10,7 +10,9 @@
#include <cstring>
#endif
-#include "common/common_funcs.h"
+#include "common/error.h"
+
+namespace Common {
std::string NativeErrorToString(int e) {
#ifdef _WIN32
@@ -50,3 +52,5 @@ std::string GetLastErrorMsg() {
return NativeErrorToString(errno);
#endif
}
+
+} // namespace Common
diff --git a/src/common/error.h b/src/common/error.h
new file mode 100644
index 000000000..e084d4b0f
--- /dev/null
+++ b/src/common/error.h
@@ -0,0 +1,21 @@
+// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <string>
+
+namespace Common {
+
+// Generic function to get last error message.
+// Call directly after the command or use the error num.
+// This function might change the error code.
+// Defined in error.cpp.
+[[nodiscard]] std::string GetLastErrorMsg();
+
+// Like GetLastErrorMsg(), but passing an explicit error code.
+// Defined in error.cpp.
+[[nodiscard]] std::string NativeErrorToString(int e);
+
+} // namespace Common
diff --git a/src/common/fs/fs_paths.h b/src/common/fs/fs_paths.h
index b32614797..5d447f108 100644
--- a/src/common/fs/fs_paths.h
+++ b/src/common/fs/fs_paths.h
@@ -21,6 +21,7 @@
#define SCREENSHOTS_DIR "screenshots"
#define SDMC_DIR "sdmc"
#define SHADER_DIR "shader"
+#define TAS_DIR "tas"
// yuzu-specific files
diff --git a/src/common/fs/path_util.cpp b/src/common/fs/path_util.cpp
index 6cdd14f13..1bcb897b5 100644
--- a/src/common/fs/path_util.cpp
+++ b/src/common/fs/path_util.cpp
@@ -82,32 +82,35 @@ public:
private:
PathManagerImpl() {
+ fs::path yuzu_path;
+ fs::path yuzu_path_cache;
+ fs::path yuzu_path_config;
+
#ifdef _WIN32
- auto yuzu_path = GetExeDirectory() / PORTABLE_DIR;
+ yuzu_path = GetExeDirectory() / PORTABLE_DIR;
if (!IsDir(yuzu_path)) {
yuzu_path = GetAppDataRoamingDirectory() / YUZU_DIR;
}
- GenerateYuzuPath(YuzuPath::YuzuDir, yuzu_path);
- GenerateYuzuPath(YuzuPath::CacheDir, yuzu_path / CACHE_DIR);
- GenerateYuzuPath(YuzuPath::ConfigDir, yuzu_path / CONFIG_DIR);
+ yuzu_path_cache = yuzu_path / CACHE_DIR;
+ yuzu_path_config = yuzu_path / CONFIG_DIR;
#else
- auto yuzu_path = GetCurrentDir() / PORTABLE_DIR;
+ yuzu_path = GetCurrentDir() / PORTABLE_DIR;
if (Exists(yuzu_path) && IsDir(yuzu_path)) {
- GenerateYuzuPath(YuzuPath::YuzuDir, yuzu_path);
- GenerateYuzuPath(YuzuPath::CacheDir, yuzu_path / CACHE_DIR);
- GenerateYuzuPath(YuzuPath::ConfigDir, yuzu_path / CONFIG_DIR);
+ yuzu_path_cache = yuzu_path / CACHE_DIR;
+ yuzu_path_config = yuzu_path / CONFIG_DIR;
} else {
yuzu_path = GetDataDirectory("XDG_DATA_HOME") / YUZU_DIR;
-
- GenerateYuzuPath(YuzuPath::YuzuDir, yuzu_path);
- GenerateYuzuPath(YuzuPath::CacheDir, GetDataDirectory("XDG_CACHE_HOME") / YUZU_DIR);
- GenerateYuzuPath(YuzuPath::ConfigDir, GetDataDirectory("XDG_CONFIG_HOME") / YUZU_DIR);
+ yuzu_path_cache = GetDataDirectory("XDG_CACHE_HOME") / YUZU_DIR;
+ yuzu_path_config = GetDataDirectory("XDG_CONFIG_HOME") / YUZU_DIR;
}
#endif
+ GenerateYuzuPath(YuzuPath::YuzuDir, yuzu_path);
+ GenerateYuzuPath(YuzuPath::CacheDir, yuzu_path_cache);
+ GenerateYuzuPath(YuzuPath::ConfigDir, yuzu_path_config);
GenerateYuzuPath(YuzuPath::DumpDir, yuzu_path / DUMP_DIR);
GenerateYuzuPath(YuzuPath::KeysDir, yuzu_path / KEYS_DIR);
GenerateYuzuPath(YuzuPath::LoadDir, yuzu_path / LOAD_DIR);
@@ -116,6 +119,7 @@ private:
GenerateYuzuPath(YuzuPath::ScreenshotsDir, yuzu_path / SCREENSHOTS_DIR);
GenerateYuzuPath(YuzuPath::SDMCDir, yuzu_path / SDMC_DIR);
GenerateYuzuPath(YuzuPath::ShaderDir, yuzu_path / SHADER_DIR);
+ GenerateYuzuPath(YuzuPath::TASDir, yuzu_path / TAS_DIR);
}
~PathManagerImpl() = default;
diff --git a/src/common/fs/path_util.h b/src/common/fs/path_util.h
index f956ac9a2..0a9e3a145 100644
--- a/src/common/fs/path_util.h
+++ b/src/common/fs/path_util.h
@@ -23,6 +23,7 @@ enum class YuzuPath {
ScreenshotsDir, // Where yuzu screenshots are stored.
SDMCDir, // Where the emulated SDMC is stored.
ShaderDir, // Where shaders are stored.
+ TASDir, // Where TAS scripts are stored.
};
/**
diff --git a/src/common/host_memory.cpp b/src/common/host_memory.cpp
index 6661244cf..b44a44949 100644
--- a/src/common/host_memory.cpp
+++ b/src/common/host_memory.cpp
@@ -314,8 +314,8 @@ private:
}
void UntrackPlaceholder(boost::icl::separate_interval_set<size_t>::iterator it) {
- placeholders.erase(it);
placeholder_host_pointers.erase(it->lower());
+ placeholders.erase(it);
}
/// Return true when a given memory region is a "nieche" and the placeholders don't have to be
diff --git a/src/common/intrusive_red_black_tree.h b/src/common/intrusive_red_black_tree.h
index 1f696fe80..3173cc449 100644
--- a/src/common/intrusive_red_black_tree.h
+++ b/src/common/intrusive_red_black_tree.h
@@ -235,20 +235,19 @@ public:
template <typename T>
concept HasLightCompareType = requires {
- { std::is_same<typename T::LightCompareType, void>::value }
- ->std::convertible_to<bool>;
+ { std::is_same<typename T::LightCompareType, void>::value } -> std::convertible_to<bool>;
};
namespace impl {
-template <typename T, typename Default>
-consteval auto* GetLightCompareType() {
- if constexpr (HasLightCompareType<T>) {
- return static_cast<typename T::LightCompareType*>(nullptr);
- } else {
- return static_cast<Default*>(nullptr);
+ template <typename T, typename Default>
+ consteval auto* GetLightCompareType() {
+ if constexpr (HasLightCompareType<T>) {
+ return static_cast<typename T::LightCompareType*>(nullptr);
+ } else {
+ return static_cast<Default*>(nullptr);
+ }
}
-}
} // namespace impl
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp
index 61dddab3f..0e85a9c1d 100644
--- a/src/common/logging/backend.cpp
+++ b/src/common/logging/backend.cpp
@@ -2,118 +2,244 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include <algorithm>
#include <atomic>
#include <chrono>
#include <climits>
-#include <condition_variable>
-#include <memory>
-#include <mutex>
+#include <exception>
#include <thread>
#include <vector>
+#include <fmt/format.h>
+
#ifdef _WIN32
#include <windows.h> // For OutputDebugStringW
#endif
-#include "common/assert.h"
#include "common/fs/file.h"
#include "common/fs/fs.h"
+#include "common/fs/fs_paths.h"
+#include "common/fs/path_util.h"
#include "common/literals.h"
+#include "common/thread.h"
#include "common/logging/backend.h"
#include "common/logging/log.h"
+#include "common/logging/log_entry.h"
#include "common/logging/text_formatter.h"
#include "common/settings.h"
+#ifdef _WIN32
#include "common/string_util.h"
+#endif
#include "common/threadsafe_queue.h"
namespace Common::Log {
+namespace {
+
/**
- * Static state as a singleton.
+ * Interface for logging backends.
*/
-class Impl {
+class Backend {
public:
- static Impl& Instance() {
- static Impl backend;
- return backend;
+ virtual ~Backend() = default;
+
+ virtual void Write(const Entry& entry) = 0;
+
+ virtual void EnableForStacktrace() = 0;
+
+ virtual void Flush() = 0;
+};
+
+/**
+ * Backend that writes to stderr and with color
+ */
+class ColorConsoleBackend final : public Backend {
+public:
+ explicit ColorConsoleBackend() = default;
+
+ ~ColorConsoleBackend() override = default;
+
+ void Write(const Entry& entry) override {
+ if (enabled.load(std::memory_order_relaxed)) {
+ PrintColoredMessage(entry);
+ }
}
- Impl(const Impl&) = delete;
- Impl& operator=(const Impl&) = delete;
+ void Flush() override {
+ // stderr shouldn't be buffered
+ }
- Impl(Impl&&) = delete;
- Impl& operator=(Impl&&) = delete;
+ void EnableForStacktrace() override {
+ enabled = true;
+ }
- void PushEntry(Class log_class, Level log_level, const char* filename, unsigned int line_num,
- const char* function, std::string message) {
- message_queue.Push(
- CreateEntry(log_class, log_level, filename, line_num, function, std::move(message)));
+ void SetEnabled(bool enabled_) {
+ enabled = enabled_;
}
- void AddBackend(std::unique_ptr<Backend> backend) {
- std::lock_guard lock{writing_mutex};
- backends.push_back(std::move(backend));
+private:
+ std::atomic_bool enabled{false};
+};
+
+/**
+ * Backend that writes to a file passed into the constructor
+ */
+class FileBackend final : public Backend {
+public:
+ explicit FileBackend(const std::filesystem::path& filename) {
+ auto old_filename = filename;
+ old_filename += ".old.txt";
+
+ // Existence checks are done within the functions themselves.
+ // We don't particularly care if these succeed or not.
+ static_cast<void>(FS::RemoveFile(old_filename));
+ static_cast<void>(FS::RenameFile(filename, old_filename));
+
+ file = std::make_unique<FS::IOFile>(filename, FS::FileAccessMode::Write,
+ FS::FileType::TextFile);
+ }
+
+ ~FileBackend() override = default;
+
+ void Write(const Entry& entry) override {
+ if (!enabled) {
+ return;
+ }
+
+ bytes_written += file->WriteString(FormatLogMessage(entry).append(1, '\n'));
+
+ using namespace Common::Literals;
+ // Prevent logs from exceeding a set maximum size in the event that log entries are spammed.
+ const auto write_limit = Settings::values.extended_logging ? 1_GiB : 100_MiB;
+ const bool write_limit_exceeded = bytes_written > write_limit;
+ if (entry.log_level >= Level::Error || write_limit_exceeded) {
+ if (write_limit_exceeded) {
+ // Stop writing after the write limit is exceeded.
+ // Don't close the file so we can print a stacktrace if necessary
+ enabled = false;
+ }
+ file->Flush();
+ }
}
- void RemoveBackend(std::string_view backend_name) {
- std::lock_guard lock{writing_mutex};
+ void Flush() override {
+ file->Flush();
+ }
- std::erase_if(backends, [&backend_name](const auto& backend) {
- return backend_name == backend->GetName();
- });
+ void EnableForStacktrace() override {
+ enabled = true;
+ bytes_written = 0;
}
- const Filter& GetGlobalFilter() const {
- return filter;
+private:
+ std::unique_ptr<FS::IOFile> file;
+ bool enabled = true;
+ std::size_t bytes_written = 0;
+};
+
+/**
+ * Backend that writes to Visual Studio's output window
+ */
+class DebuggerBackend final : public Backend {
+public:
+ explicit DebuggerBackend() = default;
+
+ ~DebuggerBackend() override = default;
+
+ void Write(const Entry& entry) override {
+#ifdef _WIN32
+ ::OutputDebugStringW(UTF8ToUTF16W(FormatLogMessage(entry).append(1, '\n')).c_str());
+#endif
}
+ void Flush() override {}
+
+ void EnableForStacktrace() override {}
+};
+
+bool initialization_in_progress_suppress_logging = true;
+
+/**
+ * Static state as a singleton.
+ */
+class Impl {
+public:
+ static Impl& Instance() {
+ if (!instance) {
+ throw std::runtime_error("Using Logging instance before its initialization");
+ }
+ return *instance;
+ }
+
+ static void Initialize() {
+ if (instance) {
+ LOG_WARNING(Log, "Reinitializing logging backend");
+ return;
+ }
+ using namespace Common::FS;
+ const auto& log_dir = GetYuzuPath(YuzuPath::LogDir);
+ void(CreateDir(log_dir));
+ Filter filter;
+ filter.ParseFilterString(Settings::values.log_filter.GetValue());
+ instance = std::unique_ptr<Impl, decltype(&Deleter)>(new Impl(log_dir / LOG_FILE, filter),
+ Deleter);
+ initialization_in_progress_suppress_logging = false;
+ }
+
+ Impl(const Impl&) = delete;
+ Impl& operator=(const Impl&) = delete;
+
+ Impl(Impl&&) = delete;
+ Impl& operator=(Impl&&) = delete;
+
void SetGlobalFilter(const Filter& f) {
filter = f;
}
- Backend* GetBackend(std::string_view backend_name) {
- const auto it =
- std::find_if(backends.begin(), backends.end(),
- [&backend_name](const auto& i) { return backend_name == i->GetName(); });
- if (it == backends.end())
- return nullptr;
- return it->get();
+ void SetColorConsoleBackendEnabled(bool enabled) {
+ color_console_backend.SetEnabled(enabled);
+ }
+
+ void PushEntry(Class log_class, Level log_level, const char* filename, unsigned int line_num,
+ const char* function, std::string message) {
+ if (!filter.CheckMessage(log_class, log_level))
+ return;
+ const Entry& entry =
+ CreateEntry(log_class, log_level, filename, line_num, function, std::move(message));
+ message_queue.Push(entry);
}
private:
- Impl() {
- backend_thread = std::thread([&] {
- Entry entry;
- auto write_logs = [&](Entry& e) {
- std::lock_guard lock{writing_mutex};
- for (const auto& backend : backends) {
- backend->Write(e);
- }
- };
- while (true) {
- entry = message_queue.PopWait();
- if (entry.final_entry) {
- break;
- }
- write_logs(entry);
- }
+ Impl(const std::filesystem::path& file_backend_filename, const Filter& filter_)
+ : filter{filter_}, file_backend{file_backend_filename}, backend_thread{std::thread([this] {
+ Common::SetCurrentThreadName("yuzu:Log");
+ Entry entry;
+ const auto write_logs = [this, &entry]() {
+ ForEachBackend([&entry](Backend& backend) { backend.Write(entry); });
+ };
+ while (true) {
+ entry = message_queue.PopWait();
+ if (entry.final_entry) {
+ break;
+ }
+ write_logs();
+ }
+ // Drain the logging queue. Only writes out up to MAX_LOGS_TO_WRITE to prevent a
+ // case where a system is repeatedly spamming logs even on close.
+ int max_logs_to_write = filter.IsDebug() ? INT_MAX : 100;
+ while (max_logs_to_write-- && message_queue.Pop(entry)) {
+ write_logs();
+ }
+ })} {}
- // Drain the logging queue. Only writes out up to MAX_LOGS_TO_WRITE to prevent a
- // case where a system is repeatedly spamming logs even on close.
- const int MAX_LOGS_TO_WRITE = filter.IsDebug() ? INT_MAX : 100;
- int logs_written = 0;
- while (logs_written++ < MAX_LOGS_TO_WRITE && message_queue.Pop(entry)) {
- write_logs(entry);
- }
- });
+ ~Impl() {
+ StopBackendThread();
}
- ~Impl() {
- Entry entry;
- entry.final_entry = true;
- message_queue.Push(entry);
+ void StopBackendThread() {
+ Entry stop_entry{};
+ stop_entry.final_entry = true;
+ message_queue.Push(stop_entry);
backend_thread.join();
}
@@ -135,100 +261,51 @@ private:
};
}
- std::mutex writing_mutex;
- std::thread backend_thread;
- std::vector<std::unique_ptr<Backend>> backends;
- MPSCQueue<Entry> message_queue;
- Filter filter;
- std::chrono::steady_clock::time_point time_origin{std::chrono::steady_clock::now()};
-};
-
-ConsoleBackend::~ConsoleBackend() = default;
-
-void ConsoleBackend::Write(const Entry& entry) {
- PrintMessage(entry);
-}
-
-ColorConsoleBackend::~ColorConsoleBackend() = default;
-
-void ColorConsoleBackend::Write(const Entry& entry) {
- PrintColoredMessage(entry);
-}
-
-FileBackend::FileBackend(const std::filesystem::path& filename) {
- auto old_filename = filename;
- old_filename += ".old.txt";
-
- // Existence checks are done within the functions themselves.
- // We don't particularly care if these succeed or not.
- FS::RemoveFile(old_filename);
- void(FS::RenameFile(filename, old_filename));
-
- file =
- std::make_unique<FS::IOFile>(filename, FS::FileAccessMode::Write, FS::FileType::TextFile);
-}
-
-FileBackend::~FileBackend() = default;
+ void ForEachBackend(auto lambda) {
+ lambda(static_cast<Backend&>(debugger_backend));
+ lambda(static_cast<Backend&>(color_console_backend));
+ lambda(static_cast<Backend&>(file_backend));
+ }
-void FileBackend::Write(const Entry& entry) {
- if (!file->IsOpen()) {
- return;
+ static void Deleter(Impl* ptr) {
+ delete ptr;
}
- using namespace Common::Literals;
- // Prevent logs from exceeding a set maximum size in the event that log entries are spammed.
- constexpr std::size_t MAX_BYTES_WRITTEN = 100_MiB;
- constexpr std::size_t MAX_BYTES_WRITTEN_EXTENDED = 1_GiB;
+ static inline std::unique_ptr<Impl, decltype(&Deleter)> instance{nullptr, Deleter};
- const bool write_limit_exceeded =
- bytes_written > MAX_BYTES_WRITTEN_EXTENDED ||
- (bytes_written > MAX_BYTES_WRITTEN && !Settings::values.extended_logging);
+ Filter filter;
+ DebuggerBackend debugger_backend{};
+ ColorConsoleBackend color_console_backend{};
+ FileBackend file_backend;
- // Close the file after the write limit is exceeded.
- if (write_limit_exceeded) {
- file->Close();
- return;
- }
+ std::thread backend_thread;
+ MPSCQueue<Entry> message_queue{};
+ std::chrono::steady_clock::time_point time_origin{std::chrono::steady_clock::now()};
+};
+} // namespace
- bytes_written += file->WriteString(FormatLogMessage(entry).append(1, '\n'));
- if (entry.log_level >= Level::Error) {
- file->Flush();
- }
+void Initialize() {
+ Impl::Initialize();
}
-DebuggerBackend::~DebuggerBackend() = default;
-
-void DebuggerBackend::Write(const Entry& entry) {
-#ifdef _WIN32
- ::OutputDebugStringW(UTF8ToUTF16W(FormatLogMessage(entry).append(1, '\n')).c_str());
-#endif
+void DisableLoggingInTests() {
+ initialization_in_progress_suppress_logging = true;
}
void SetGlobalFilter(const Filter& filter) {
Impl::Instance().SetGlobalFilter(filter);
}
-void AddBackend(std::unique_ptr<Backend> backend) {
- Impl::Instance().AddBackend(std::move(backend));
-}
-
-void RemoveBackend(std::string_view backend_name) {
- Impl::Instance().RemoveBackend(backend_name);
-}
-
-Backend* GetBackend(std::string_view backend_name) {
- return Impl::Instance().GetBackend(backend_name);
+void SetColorConsoleBackendEnabled(bool enabled) {
+ Impl::Instance().SetColorConsoleBackendEnabled(enabled);
}
void FmtLogMessageImpl(Class log_class, Level log_level, const char* filename,
unsigned int line_num, const char* function, const char* format,
const fmt::format_args& args) {
- auto& instance = Impl::Instance();
- const auto& filter = instance.GetGlobalFilter();
- if (!filter.CheckMessage(log_class, log_level))
- return;
-
- instance.PushEntry(log_class, log_level, filename, line_num, function,
- fmt::vformat(format, args));
+ if (!initialization_in_progress_suppress_logging) {
+ Impl::Instance().PushEntry(log_class, log_level, filename, line_num, function,
+ fmt::vformat(format, args));
+ }
}
} // namespace Common::Log
diff --git a/src/common/logging/backend.h b/src/common/logging/backend.h
index 4b9a910c1..cb7839ee9 100644
--- a/src/common/logging/backend.h
+++ b/src/common/logging/backend.h
@@ -5,120 +5,21 @@
#pragma once
#include <filesystem>
-#include <memory>
-#include <string>
-#include <string_view>
#include "common/logging/filter.h"
-#include "common/logging/log.h"
-
-namespace Common::FS {
-class IOFile;
-}
namespace Common::Log {
class Filter;
-/**
- * Interface for logging backends. As loggers can be created and removed at runtime, this can be
- * used by a frontend for adding a custom logging backend as needed
- */
-class Backend {
-public:
- virtual ~Backend() = default;
-
- virtual void SetFilter(const Filter& new_filter) {
- filter = new_filter;
- }
- virtual const char* GetName() const = 0;
- virtual void Write(const Entry& entry) = 0;
-
-private:
- Filter filter;
-};
-
-/**
- * Backend that writes to stderr without any color commands
- */
-class ConsoleBackend : public Backend {
-public:
- ~ConsoleBackend() override;
-
- static const char* Name() {
- return "console";
- }
- const char* GetName() const override {
- return Name();
- }
- void Write(const Entry& entry) override;
-};
-
-/**
- * Backend that writes to stderr and with color
- */
-class ColorConsoleBackend : public Backend {
-public:
- ~ColorConsoleBackend() override;
-
- static const char* Name() {
- return "color_console";
- }
-
- const char* GetName() const override {
- return Name();
- }
- void Write(const Entry& entry) override;
-};
+/// Initializes the logging system. This should be the first thing called in main.
+void Initialize();
-/**
- * Backend that writes to a file passed into the constructor
- */
-class FileBackend : public Backend {
-public:
- explicit FileBackend(const std::filesystem::path& filename);
- ~FileBackend() override;
-
- static const char* Name() {
- return "file";
- }
-
- const char* GetName() const override {
- return Name();
- }
-
- void Write(const Entry& entry) override;
-
-private:
- std::unique_ptr<FS::IOFile> file;
- std::size_t bytes_written = 0;
-};
-
-/**
- * Backend that writes to Visual Studio's output window
- */
-class DebuggerBackend : public Backend {
-public:
- ~DebuggerBackend() override;
-
- static const char* Name() {
- return "debugger";
- }
- const char* GetName() const override {
- return Name();
- }
- void Write(const Entry& entry) override;
-};
-
-void AddBackend(std::unique_ptr<Backend> backend);
-
-void RemoveBackend(std::string_view backend_name);
-
-Backend* GetBackend(std::string_view backend_name);
+void DisableLoggingInTests();
/**
- * The global filter will prevent any messages from even being processed if they are filtered. Each
- * backend can have a filter, but if the level is lower than the global filter, the backend will
- * never get the message
+ * The global filter will prevent any messages from even being processed if they are filtered.
*/
void SetGlobalFilter(const Filter& filter);
-} // namespace Common::Log \ No newline at end of file
+
+void SetColorConsoleBackendEnabled(bool enabled);
+} // namespace Common::Log
diff --git a/src/common/logging/filter.cpp b/src/common/logging/filter.cpp
index f055f0e11..42744c994 100644
--- a/src/common/logging/filter.cpp
+++ b/src/common/logging/filter.cpp
@@ -111,6 +111,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
SUB(Service, NCM) \
SUB(Service, NFC) \
SUB(Service, NFP) \
+ SUB(Service, NGCT) \
SUB(Service, NIFM) \
SUB(Service, NIM) \
SUB(Service, NPNS) \
diff --git a/src/common/logging/log.h b/src/common/logging/log.h
index 8d43eddc7..c186d55ef 100644
--- a/src/common/logging/log.h
+++ b/src/common/logging/log.h
@@ -4,7 +4,11 @@
#pragma once
-#include <fmt/format.h>
+#include <algorithm>
+#include <string_view>
+
+#include <fmt/core.h>
+
#include "common/logging/types.h"
namespace Common::Log {
diff --git a/src/common/logging/log_entry.h b/src/common/logging/log_entry.h
new file mode 100644
index 000000000..dd6f44841
--- /dev/null
+++ b/src/common/logging/log_entry.h
@@ -0,0 +1,28 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <chrono>
+
+#include "common/logging/types.h"
+
+namespace Common::Log {
+
+/**
+ * A log entry. Log entries are store in a structured format to permit more varied output
+ * formatting on different frontends, as well as facilitating filtering and aggregation.
+ */
+struct Entry {
+ std::chrono::microseconds timestamp;
+ Class log_class{};
+ Level log_level{};
+ const char* filename = nullptr;
+ unsigned int line_num = 0;
+ std::string function;
+ std::string message;
+ bool final_entry = false;
+};
+
+} // namespace Common::Log
diff --git a/src/common/logging/text_formatter.cpp b/src/common/logging/text_formatter.cpp
index cfc0d5846..10b2281db 100644
--- a/src/common/logging/text_formatter.cpp
+++ b/src/common/logging/text_formatter.cpp
@@ -13,6 +13,7 @@
#include "common/common_funcs.h"
#include "common/logging/filter.h"
#include "common/logging/log.h"
+#include "common/logging/log_entry.h"
#include "common/logging/text_formatter.h"
#include "common/string_util.h"
diff --git a/src/common/logging/types.h b/src/common/logging/types.h
index 7ad0334fc..2d21fc483 100644
--- a/src/common/logging/types.h
+++ b/src/common/logging/types.h
@@ -4,8 +4,6 @@
#pragma once
-#include <chrono>
-
#include "common/common_types.h"
namespace Common::Log {
@@ -81,6 +79,7 @@ enum class Class : u8 {
Service_NCM, ///< The NCM service
Service_NFC, ///< The NFC (Near-field communication) service
Service_NFP, ///< The NFP service
+ Service_NGCT, ///< The NGCT (No Good Content for Terra) service
Service_NIFM, ///< The NIFM (Network interface) service
Service_NIM, ///< The NIM service
Service_NPNS, ///< The NPNS service
@@ -130,19 +129,4 @@ enum class Class : u8 {
Count ///< Total number of logging classes
};
-/**
- * A log entry. Log entries are store in a structured format to permit more varied output
- * formatting on different frontends, as well as facilitating filtering and aggregation.
- */
-struct Entry {
- std::chrono::microseconds timestamp;
- Class log_class{};
- Level log_level{};
- const char* filename = nullptr;
- unsigned int line_num = 0;
- std::string function;
- std::string message;
- bool final_entry = false;
-};
-
} // namespace Common::Log
diff --git a/src/common/lru_cache.h b/src/common/lru_cache.h
new file mode 100644
index 000000000..365488ba5
--- /dev/null
+++ b/src/common/lru_cache.h
@@ -0,0 +1,140 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2+ or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <deque>
+#include <memory>
+#include <type_traits>
+
+#include "common/common_types.h"
+
+namespace Common {
+
+template <class Traits>
+class LeastRecentlyUsedCache {
+ using ObjectType = typename Traits::ObjectType;
+ using TickType = typename Traits::TickType;
+
+ struct Item {
+ ObjectType obj;
+ TickType tick;
+ Item* next{};
+ Item* prev{};
+ };
+
+public:
+ LeastRecentlyUsedCache() : first_item{}, last_item{} {}
+ ~LeastRecentlyUsedCache() = default;
+
+ size_t Insert(ObjectType obj, TickType tick) {
+ const auto new_id = Build();
+ auto& item = item_pool[new_id];
+ item.obj = obj;
+ item.tick = tick;
+ Attach(item);
+ return new_id;
+ }
+
+ void Touch(size_t id, TickType tick) {
+ auto& item = item_pool[id];
+ if (item.tick >= tick) {
+ return;
+ }
+ item.tick = tick;
+ if (&item == last_item) {
+ return;
+ }
+ Detach(item);
+ Attach(item);
+ }
+
+ void Free(size_t id) {
+ auto& item = item_pool[id];
+ Detach(item);
+ item.prev = nullptr;
+ item.next = nullptr;
+ free_items.push_back(id);
+ }
+
+ template <typename Func>
+ void ForEachItemBelow(TickType tick, Func&& func) {
+ static constexpr bool RETURNS_BOOL =
+ std::is_same_v<std::invoke_result<Func, ObjectType>, bool>;
+ Item* iterator = first_item;
+ while (iterator) {
+ if (static_cast<s64>(tick) - static_cast<s64>(iterator->tick) < 0) {
+ return;
+ }
+ Item* next = iterator->next;
+ if constexpr (RETURNS_BOOL) {
+ if (func(iterator->obj)) {
+ return;
+ }
+ } else {
+ func(iterator->obj);
+ }
+ iterator = next;
+ }
+ }
+
+private:
+ size_t Build() {
+ if (free_items.empty()) {
+ const size_t item_id = item_pool.size();
+ auto& item = item_pool.emplace_back();
+ item.next = nullptr;
+ item.prev = nullptr;
+ return item_id;
+ }
+ const size_t item_id = free_items.front();
+ free_items.pop_front();
+ auto& item = item_pool[item_id];
+ item.next = nullptr;
+ item.prev = nullptr;
+ return item_id;
+ }
+
+ void Attach(Item& item) {
+ if (!first_item) {
+ first_item = &item;
+ }
+ if (!last_item) {
+ last_item = &item;
+ } else {
+ item.prev = last_item;
+ last_item->next = &item;
+ item.next = nullptr;
+ last_item = &item;
+ }
+ }
+
+ void Detach(Item& item) {
+ if (item.prev) {
+ item.prev->next = item.next;
+ }
+ if (item.next) {
+ item.next->prev = item.prev;
+ }
+ if (&item == first_item) {
+ first_item = item.next;
+ if (first_item) {
+ first_item->prev = nullptr;
+ }
+ }
+ if (&item == last_item) {
+ last_item = item.prev;
+ if (last_item) {
+ last_item->next = nullptr;
+ }
+ }
+ }
+
+ std::deque<Item> item_pool;
+ std::deque<size_t> free_items;
+ Item* first_item{};
+ Item* last_item{};
+};
+
+} // namespace Common
diff --git a/src/common/param_package.cpp b/src/common/param_package.cpp
index b916b4866..bbf20f5eb 100644
--- a/src/common/param_package.cpp
+++ b/src/common/param_package.cpp
@@ -3,6 +3,7 @@
// Refer to the license.txt file included.
#include <array>
+#include <stdexcept>
#include <utility>
#include <vector>
diff --git a/src/common/settings.cpp b/src/common/settings.cpp
index 996315999..9dd5e3efb 100644
--- a/src/common/settings.cpp
+++ b/src/common/settings.cpp
@@ -54,15 +54,13 @@ void LogSettings() {
log_setting("Renderer_GPUAccuracyLevel", values.gpu_accuracy.GetValue());
log_setting("Renderer_UseAsynchronousGpuEmulation",
values.use_asynchronous_gpu_emulation.GetValue());
- log_setting("Renderer_UseNvdecEmulation", values.use_nvdec_emulation.GetValue());
+ log_setting("Renderer_NvdecEmulation", values.nvdec_emulation.GetValue());
log_setting("Renderer_AccelerateASTC", values.accelerate_astc.GetValue());
log_setting("Renderer_UseVsync", values.use_vsync.GetValue());
log_setting("Renderer_ShaderBackend", values.shader_backend.GetValue());
log_setting("Renderer_UseAsynchronousShaders", values.use_asynchronous_shaders.GetValue());
- log_setting("Renderer_UseGarbageCollection", values.use_caches_gc.GetValue());
log_setting("Renderer_AnisotropicFilteringLevel", values.max_anisotropy.GetValue());
log_setting("Audio_OutputEngine", values.sink_id.GetValue());
- log_setting("Audio_EnableAudioStretching", values.enable_audio_stretching.GetValue());
log_setting("Audio_OutputDevice", values.audio_device_id.GetValue());
log_setting("DataStorage_UseVirtualSd", values.use_virtual_sd.GetValue());
log_path("DataStorage_CacheDir", Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir));
@@ -71,8 +69,9 @@ void LogSettings() {
log_path("DataStorage_NANDDir", Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir));
log_path("DataStorage_SDMCDir", Common::FS::GetYuzuPath(Common::FS::YuzuPath::SDMCDir));
log_setting("Debugging_ProgramArgs", values.program_args.GetValue());
- log_setting("Services_BCATBackend", values.bcat_backend.GetValue());
- log_setting("Services_BCATBoxcatLocal", values.bcat_boxcat_local.GetValue());
+ log_setting("Input_EnableMotion", values.motion_enabled.GetValue());
+ log_setting("Input_EnableVibration", values.vibration_enabled.GetValue());
+ log_setting("Input_EnableRawInput", values.enable_raw_input.GetValue());
}
bool IsConfiguringGlobal() {
@@ -113,7 +112,6 @@ void RestoreGlobalState(bool is_powered_on) {
}
// Audio
- values.enable_audio_stretching.SetGlobal(true);
values.volume.SetGlobal(true);
// Core
@@ -137,13 +135,12 @@ void RestoreGlobalState(bool is_powered_on) {
values.use_disk_shader_cache.SetGlobal(true);
values.gpu_accuracy.SetGlobal(true);
values.use_asynchronous_gpu_emulation.SetGlobal(true);
- values.use_nvdec_emulation.SetGlobal(true);
+ values.nvdec_emulation.SetGlobal(true);
values.accelerate_astc.SetGlobal(true);
values.use_vsync.SetGlobal(true);
values.shader_backend.SetGlobal(true);
values.use_asynchronous_shaders.SetGlobal(true);
values.use_fast_gpu_time.SetGlobal(true);
- values.use_caches_gc.SetGlobal(true);
values.bg_red.SetGlobal(true);
values.bg_green.SetGlobal(true);
values.bg_blue.SetGlobal(true);
diff --git a/src/common/settings.h b/src/common/settings.h
index a88ee045d..9ff4cf85d 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -4,9 +4,9 @@
#pragma once
+#include <algorithm>
#include <array>
#include <atomic>
-#include <chrono>
#include <map>
#include <optional>
#include <string>
@@ -15,7 +15,6 @@
#include "common/common_types.h"
#include "common/settings_input.h"
-#include "input_common/udp/client.h"
namespace Settings {
@@ -47,6 +46,12 @@ enum class FullscreenMode : u32 {
Exclusive = 1,
};
+enum class NvdecEmulation : u32 {
+ Off = 0,
+ CPU = 1,
+ GPU = 2,
+};
+
/** The BasicSetting class is a simple resource manager. It defines a label and default value
* alongside the actual value of the setting for simpler and less-error prone use with frontend
* configurations. Setting a default value and label is required, though subclasses may deviate from
@@ -74,14 +79,14 @@ public:
*/
explicit BasicSetting(const Type& default_val, const std::string& name)
: default_value{default_val}, global{default_val}, label{name} {}
- ~BasicSetting() = default;
+ virtual ~BasicSetting() = default;
/**
* Returns a reference to the setting's value.
*
* @returns A reference to the setting
*/
- [[nodiscard]] const Type& GetValue() const {
+ [[nodiscard]] virtual const Type& GetValue() const {
return global;
}
@@ -90,7 +95,7 @@ public:
*
* @param value The desired value
*/
- void SetValue(const Type& value) {
+ virtual void SetValue(const Type& value) {
Type temp{value};
std::swap(global, temp);
}
@@ -120,7 +125,7 @@ public:
*
* @returns A reference to the setting
*/
- const Type& operator=(const Type& value) {
+ virtual const Type& operator=(const Type& value) {
Type temp{value};
std::swap(global, temp);
return global;
@@ -131,7 +136,7 @@ public:
*
* @returns A reference to the setting
*/
- explicit operator const Type&() const {
+ explicit virtual operator const Type&() const {
return global;
}
@@ -142,6 +147,51 @@ protected:
};
/**
+ * BasicRangedSetting class is intended for use with quantifiable settings that need a more
+ * restrictive range than implicitly defined by its type. Implements a minimum and maximum that is
+ * simply used to sanitize SetValue and the assignment overload.
+ */
+template <typename Type>
+class BasicRangedSetting : virtual public BasicSetting<Type> {
+public:
+ /**
+ * Sets a default value, minimum value, maximum value, and label.
+ *
+ * @param default_val Intial value of the setting, and default value of the setting
+ * @param min_val Sets the minimum allowed value of the setting
+ * @param max_val Sets the maximum allowed value of the setting
+ * @param name Label for the setting
+ */
+ explicit BasicRangedSetting(const Type& default_val, const Type& min_val, const Type& max_val,
+ const std::string& name)
+ : BasicSetting<Type>{default_val, name}, minimum{min_val}, maximum{max_val} {}
+ virtual ~BasicRangedSetting() = default;
+
+ /**
+ * Like BasicSetting's SetValue, except value is clamped to the range of the setting.
+ *
+ * @param value The desired value
+ */
+ void SetValue(const Type& value) override {
+ this->global = std::clamp(value, minimum, maximum);
+ }
+
+ /**
+ * Like BasicSetting's assignment overload, except value is clamped to the range of the setting.
+ *
+ * @param value The desired value
+ * @returns A reference to the setting's value
+ */
+ const Type& operator=(const Type& value) override {
+ this->global = std::clamp(value, minimum, maximum);
+ return this->global;
+ }
+
+ const Type minimum; ///< Minimum allowed value of the setting
+ const Type maximum; ///< Maximum allowed value of the setting
+};
+
+/**
* The Setting class is a slightly more complex version of the BasicSetting class. This adds a
* 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
@@ -152,7 +202,7 @@ protected:
* Like the BasicSetting, this requires setting a default value and label to use.
*/
template <typename Type>
-class Setting final : public BasicSetting<Type> {
+class Setting : virtual public BasicSetting<Type> {
public:
/**
* Sets a default value, label, and setting value.
@@ -162,7 +212,7 @@ public:
*/
explicit Setting(const Type& default_val, const std::string& name)
: BasicSetting<Type>(default_val, name) {}
- ~Setting() = default;
+ virtual ~Setting() = default;
/**
* Tells this setting to represent either the global or custom setting when other member
@@ -191,7 +241,13 @@ public:
*
* @returns The required value of the setting
*/
- [[nodiscard]] const Type& GetValue(bool need_global = false) const {
+ [[nodiscard]] virtual const Type& GetValue() const override {
+ if (use_global) {
+ return this->global;
+ }
+ return custom;
+ }
+ [[nodiscard]] virtual const Type& GetValue(bool need_global) const {
if (use_global || need_global) {
return this->global;
}
@@ -203,7 +259,7 @@ public:
*
* @param value The new value
*/
- void SetValue(const Type& value) {
+ void SetValue(const Type& value) override {
Type temp{value};
if (use_global) {
std::swap(this->global, temp);
@@ -219,7 +275,7 @@ public:
*
* @returns A reference to the current setting value
*/
- const Type& operator=(const Type& value) {
+ const Type& operator=(const Type& value) override {
Type temp{value};
if (use_global) {
std::swap(this->global, temp);
@@ -234,19 +290,88 @@ public:
*
* @returns A reference to the current setting value
*/
- explicit operator const Type&() const {
+ virtual explicit operator const Type&() const override {
if (use_global) {
return this->global;
}
return custom;
}
-private:
+protected:
bool use_global{true}; ///< The setting's global state
Type custom{}; ///< The custom value of the setting
};
/**
+ * RangedSetting is a Setting that implements a maximum and minimum value for its setting. Intended
+ * for use with quantifiable settings.
+ */
+template <typename Type>
+class RangedSetting final : public BasicRangedSetting<Type>, public Setting<Type> {
+public:
+ /**
+ * Sets a default value, minimum value, maximum value, and label.
+ *
+ * @param default_val Intial value of the setting, and default value of the setting
+ * @param min_val Sets the minimum allowed value of the setting
+ * @param max_val Sets the maximum allowed value of the setting
+ * @param name Label for the setting
+ */
+ explicit RangedSetting(const Type& default_val, const Type& min_val, const Type& max_val,
+ const std::string& name)
+ : BasicSetting<Type>{default_val, name},
+ BasicRangedSetting<Type>{default_val, min_val, max_val, name}, Setting<Type>{default_val,
+ name} {}
+ virtual ~RangedSetting() = default;
+
+ // The following are needed to avoid a MSVC bug
+ // (source: https://stackoverflow.com/questions/469508)
+ [[nodiscard]] const Type& GetValue() const override {
+ return Setting<Type>::GetValue();
+ }
+ [[nodiscard]] const Type& GetValue(bool need_global) const override {
+ return Setting<Type>::GetValue(need_global);
+ }
+ explicit operator const Type&() const override {
+ if (this->use_global) {
+ return this->global;
+ }
+ return this->custom;
+ }
+
+ /**
+ * Like BasicSetting's SetValue, except value is clamped to the range of the setting. Sets the
+ * appropriate value depending on the global state.
+ *
+ * @param value The desired value
+ */
+ void SetValue(const Type& value) override {
+ const Type temp = std::clamp(value, this->minimum, this->maximum);
+ if (this->use_global) {
+ this->global = temp;
+ }
+ this->custom = temp;
+ }
+
+ /**
+ * Like BasicSetting's assignment overload, except value is clamped to the range of the setting.
+ * Uses the appropriate value depending on the global state.
+ *
+ * @param value The desired value
+ * @returns A reference to the setting's value
+ */
+ const Type& operator=(const Type& value) override {
+ const Type temp = std::clamp(value, this->minimum, this->maximum);
+ if (this->use_global) {
+ this->global = temp;
+ return this->global;
+ }
+ this->custom = temp;
+ return this->custom;
+ }
+};
+
+/**
* The InputSetting class allows for getting a reference to either the global or custom members.
* This is required as we cannot easily modify the values of user-defined types within containers
* using the SetValue() member function found in the Setting class. The primary purpose of this
@@ -288,14 +413,14 @@ struct Values {
BasicSetting<std::string> audio_device_id{"auto", "output_device"};
BasicSetting<std::string> sink_id{"auto", "output_engine"};
BasicSetting<bool> audio_muted{false, "audio_muted"};
- Setting<bool> enable_audio_stretching{true, "enable_audio_stretching"};
- Setting<u8> volume{100, "volume"};
+ RangedSetting<u8> volume{100, 0, 100, "volume"};
// Core
Setting<bool> use_multi_core{true, "use_multi_core"};
// Cpu
- Setting<CPUAccuracy> cpu_accuracy{CPUAccuracy::Auto, "cpu_accuracy"};
+ RangedSetting<CPUAccuracy> cpu_accuracy{CPUAccuracy::Auto, CPUAccuracy::Auto,
+ CPUAccuracy::Unsafe, "cpu_accuracy"};
// TODO: remove cpu_accuracy_first_time, migration setting added 8 July 2021
BasicSetting<bool> cpu_accuracy_first_time{true, "cpu_accuracy_first_time"};
BasicSetting<bool> cpu_debug_mode{false, "cpu_debug_mode"};
@@ -317,7 +442,8 @@ struct Values {
Setting<bool> cpuopt_unsafe_fastmem_check{true, "cpuopt_unsafe_fastmem_check"};
// Renderer
- Setting<RendererBackend> renderer_backend{RendererBackend::OpenGL, "backend"};
+ RangedSetting<RendererBackend> renderer_backend{
+ RendererBackend::OpenGL, RendererBackend::OpenGL, RendererBackend::Vulkan, "backend"};
BasicSetting<bool> renderer_debug{false, "debug"};
BasicSetting<bool> renderer_shader_feedback{false, "shader_feedback"};
BasicSetting<bool> enable_nsight_aftermath{false, "nsight_aftermath"};
@@ -328,29 +454,30 @@ struct Values {
Setting<u16> resolution_factor{1, "resolution_factor"};
// *nix platforms may have issues with the borderless windowed fullscreen mode.
// Default to exclusive fullscreen on these platforms for now.
- Setting<FullscreenMode> fullscreen_mode{
+ RangedSetting<FullscreenMode> fullscreen_mode{
#ifdef _WIN32
FullscreenMode::Borderless,
#else
FullscreenMode::Exclusive,
#endif
- "fullscreen_mode"};
- Setting<int> aspect_ratio{0, "aspect_ratio"};
- Setting<int> max_anisotropy{0, "max_anisotropy"};
+ FullscreenMode::Borderless, FullscreenMode::Exclusive, "fullscreen_mode"};
+ RangedSetting<int> aspect_ratio{0, 0, 3, "aspect_ratio"};
+ RangedSetting<int> max_anisotropy{0, 0, 4, "max_anisotropy"};
Setting<bool> use_speed_limit{true, "use_speed_limit"};
- Setting<u16> speed_limit{100, "speed_limit"};
+ RangedSetting<u16> speed_limit{100, 0, 9999, "speed_limit"};
Setting<bool> use_disk_shader_cache{true, "use_disk_shader_cache"};
- Setting<GPUAccuracy> gpu_accuracy{GPUAccuracy::High, "gpu_accuracy"};
+ RangedSetting<GPUAccuracy> gpu_accuracy{GPUAccuracy::High, GPUAccuracy::Normal,
+ GPUAccuracy::Extreme, "gpu_accuracy"};
Setting<bool> use_asynchronous_gpu_emulation{true, "use_asynchronous_gpu_emulation"};
- Setting<bool> use_nvdec_emulation{true, "use_nvdec_emulation"};
+ Setting<NvdecEmulation> nvdec_emulation{NvdecEmulation::GPU, "nvdec_emulation"};
Setting<bool> accelerate_astc{true, "accelerate_astc"};
Setting<bool> use_vsync{true, "use_vsync"};
- BasicSetting<u16> fps_cap{1000, "fps_cap"};
+ BasicRangedSetting<u16> fps_cap{1000, 1, 1000, "fps_cap"};
BasicSetting<bool> disable_fps_limit{false, "disable_fps_limit"};
- Setting<ShaderBackend> shader_backend{ShaderBackend::GLASM, "shader_backend"};
+ RangedSetting<ShaderBackend> shader_backend{ShaderBackend::GLASM, ShaderBackend::GLSL,
+ ShaderBackend::SPIRV, "shader_backend"};
Setting<bool> use_asynchronous_shaders{false, "use_asynchronous_shaders"};
Setting<bool> use_fast_gpu_time{true, "use_fast_gpu_time"};
- Setting<bool> use_caches_gc{false, "use_caches_gc"};
Setting<u8> bg_red{0, "bg_red"};
Setting<u8> bg_green{0, "bg_green"};
@@ -359,32 +486,38 @@ struct Values {
// System
Setting<std::optional<u32>> rng_seed{std::optional<u32>(), "rng_seed"};
// Measured in seconds since epoch
- std::optional<std::chrono::seconds> custom_rtc;
+ std::optional<s64> custom_rtc;
// Set on game boot, reset on stop. Seconds difference between current time and `custom_rtc`
- std::chrono::seconds custom_rtc_differential;
+ s64 custom_rtc_differential;
BasicSetting<s32> current_user{0, "current_user"};
- Setting<s32> language_index{1, "language_index"};
- Setting<s32> region_index{1, "region_index"};
- Setting<s32> time_zone_index{0, "time_zone_index"};
- Setting<s32> sound_index{1, "sound_index"};
+ RangedSetting<s32> language_index{1, 0, 17, "language_index"};
+ RangedSetting<s32> region_index{1, 0, 6, "region_index"};
+ RangedSetting<s32> time_zone_index{0, 0, 45, "time_zone_index"};
+ RangedSetting<s32> sound_index{1, 0, 2, "sound_index"};
// Controls
InputSetting<std::array<PlayerInput, 10>> players;
Setting<bool> use_docked_mode{true, "use_docked_mode"};
+ BasicSetting<bool> enable_raw_input{false, "enable_raw_input"};
+
Setting<bool> vibration_enabled{true, "vibration_enabled"};
Setting<bool> enable_accurate_vibrations{false, "enable_accurate_vibrations"};
Setting<bool> motion_enabled{true, "motion_enabled"};
BasicSetting<std::string> motion_device{"engine:motion_emu,update_period:100,sensitivity:0.01",
"motion_device"};
- BasicSetting<std::string> udp_input_servers{InputCommon::CemuhookUDP::DEFAULT_SRV,
- "udp_input_servers"};
+ BasicSetting<std::string> udp_input_servers{"127.0.0.1:26760", "udp_input_servers"};
+
+ BasicSetting<bool> pause_tas_on_load{true, "pause_tas_on_load"};
+ BasicSetting<bool> tas_enable{false, "tas_enable"};
+ BasicSetting<bool> tas_loop{false, "tas_loop"};
+ BasicSetting<bool> tas_swap_controllers{true, "tas_swap_controllers"};
BasicSetting<bool> mouse_panning{false, "mouse_panning"};
- BasicSetting<u8> mouse_panning_sensitivity{10, "mouse_panning_sensitivity"};
+ BasicRangedSetting<u8> mouse_panning_sensitivity{10, 1, 100, "mouse_panning_sensitivity"};
BasicSetting<bool> mouse_enabled{false, "mouse_enabled"};
std::string mouse_device;
MouseButtonsRaw mouse_buttons;
@@ -433,9 +566,8 @@ struct Values {
BasicSetting<std::string> log_filter{"*:Info", "log_filter"};
BasicSetting<bool> use_dev_keys{false, "use_dev_keys"};
- // Services
- BasicSetting<std::string> bcat_backend{"none", "bcat_backend"};
- BasicSetting<bool> bcat_boxcat_local{false, "bcat_boxcat_local"};
+ // Network
+ BasicSetting<std::string> network_interface{std::string(), "network_interface"};
// WebService
BasicSetting<bool> enable_telemetry{true, "enable_telemetry"};
diff --git a/src/common/string_util.cpp b/src/common/string_util.cpp
index e6344fd41..662171138 100644
--- a/src/common/string_util.cpp
+++ b/src/common/string_util.cpp
@@ -180,20 +180,20 @@ std::wstring UTF8ToUTF16W(const std::string& input) {
#endif
-std::string StringFromFixedZeroTerminatedBuffer(const char* buffer, std::size_t max_len) {
+std::string StringFromFixedZeroTerminatedBuffer(std::string_view buffer, std::size_t max_len) {
std::size_t len = 0;
- while (len < max_len && buffer[len] != '\0')
+ while (len < buffer.length() && len < max_len && buffer[len] != '\0') {
++len;
-
- return std::string(buffer, len);
+ }
+ return std::string(buffer.begin(), buffer.begin() + len);
}
std::u16string UTF16StringFromFixedZeroTerminatedBuffer(std::u16string_view buffer,
std::size_t max_len) {
std::size_t len = 0;
- while (len < max_len && buffer[len] != '\0')
+ while (len < buffer.length() && len < max_len && buffer[len] != '\0') {
++len;
-
+ }
return std::u16string(buffer.begin(), buffer.begin() + len);
}
diff --git a/src/common/string_util.h b/src/common/string_util.h
index 7e90a9ca5..f0dd632ee 100644
--- a/src/common/string_util.h
+++ b/src/common/string_util.h
@@ -63,7 +63,7 @@ template <typename InIt>
* Creates a std::string from a fixed-size NUL-terminated char buffer. If the buffer isn't
* NUL-terminated then the string ends at max_len characters.
*/
-[[nodiscard]] std::string StringFromFixedZeroTerminatedBuffer(const char* buffer,
+[[nodiscard]] std::string StringFromFixedZeroTerminatedBuffer(std::string_view buffer,
std::size_t max_len);
/**
diff --git a/src/common/thread.cpp b/src/common/thread.cpp
index d2c1ac60d..946a1114d 100644
--- a/src/common/thread.cpp
+++ b/src/common/thread.cpp
@@ -2,7 +2,9 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include "common/common_funcs.h"
+#include <string>
+
+#include "common/error.h"
#include "common/logging/log.h"
#include "common/thread.h"
#ifdef __APPLE__
@@ -21,8 +23,6 @@
#include <unistd.h>
#endif
-#include <string>
-
#ifdef __FreeBSD__
#define cpu_set_t cpuset_t
#endif
diff --git a/src/common/threadsafe_queue.h b/src/common/threadsafe_queue.h
index ad04df8ca..2c8c2b90e 100644
--- a/src/common/threadsafe_queue.h
+++ b/src/common/threadsafe_queue.h
@@ -14,7 +14,7 @@
#include <utility>
namespace Common {
-template <typename T>
+template <typename T, bool with_stop_token = false>
class SPSCQueue {
public:
SPSCQueue() {
@@ -46,15 +46,13 @@ public:
ElementPtr* new_ptr = new ElementPtr();
write_ptr->next.store(new_ptr, std::memory_order_release);
write_ptr = new_ptr;
+ ++size;
- const size_t previous_size{size++};
-
- // Acquire the mutex and then immediately release it as a fence.
+ // cv_mutex must be held or else there will be a missed wakeup if the other thread is in the
+ // line before cv.wait
// TODO(bunnei): This can be replaced with C++20 waitable atomics when properly supported.
// See discussion on https://github.com/yuzu-emu/yuzu/pull/3173 for details.
- if (previous_size == 0) {
- std::lock_guard lock{cv_mutex};
- }
+ std::lock_guard lock{cv_mutex};
cv.notify_one();
}
@@ -86,7 +84,7 @@ public:
void Wait() {
if (Empty()) {
std::unique_lock lock{cv_mutex};
- cv.wait(lock, [this]() { return !Empty(); });
+ cv.wait(lock, [this] { return !Empty(); });
}
}
@@ -97,6 +95,19 @@ public:
return t;
}
+ T PopWait(std::stop_token stop_token) {
+ if (Empty()) {
+ std::unique_lock lock{cv_mutex};
+ cv.wait(lock, stop_token, [this] { return !Empty(); });
+ }
+ if (stop_token.stop_requested()) {
+ return T{};
+ }
+ T t;
+ Pop(t);
+ return t;
+ }
+
// not thread-safe
void Clear() {
size.store(0);
@@ -125,13 +136,13 @@ private:
ElementPtr* read_ptr;
std::atomic_size_t size{0};
std::mutex cv_mutex;
- std::condition_variable cv;
+ std::conditional_t<with_stop_token, std::condition_variable_any, std::condition_variable> cv;
};
// a simple thread-safe,
// single reader, multiple writer queue
-template <typename T>
+template <typename T, bool with_stop_token = false>
class MPSCQueue {
public:
[[nodiscard]] std::size_t Size() const {
@@ -168,13 +179,17 @@ public:
return spsc_queue.PopWait();
}
+ T PopWait(std::stop_token stop_token) {
+ return spsc_queue.PopWait(stop_token);
+ }
+
// not thread-safe
void Clear() {
spsc_queue.Clear();
}
private:
- SPSCQueue<T> spsc_queue;
+ SPSCQueue<T, with_stop_token> spsc_queue;
std::mutex write_lock;
};
} // namespace Common
diff --git a/src/common/uuid.h b/src/common/uuid.h
index aeb36939a..8ea01f8da 100644
--- a/src/common/uuid.h
+++ b/src/common/uuid.h
@@ -58,6 +58,13 @@ struct UUID {
uuid = INVALID_UUID;
}
+ [[nodiscard]] constexpr bool IsInvalid() const {
+ return uuid == INVALID_UUID;
+ }
+ [[nodiscard]] constexpr bool IsValid() const {
+ return !IsInvalid();
+ }
+
// TODO(ogniK): Properly generate a Nintendo ID
[[nodiscard]] constexpr u64 GetNintendoID() const {
return uuid[0];
@@ -69,3 +76,14 @@ struct UUID {
static_assert(sizeof(UUID) == 16, "UUID is an invalid size!");
} // namespace Common
+
+namespace std {
+
+template <>
+struct hash<Common::UUID> {
+ size_t operator()(const Common::UUID& uuid) const noexcept {
+ return uuid.uuid[1] ^ uuid.uuid[0];
+ }
+};
+
+} // namespace std
diff --git a/src/common/vector_math.h b/src/common/vector_math.h
index 22dba3c2d..ba7c363c1 100644
--- a/src/common/vector_math.h
+++ b/src/common/vector_math.h
@@ -667,8 +667,8 @@ template <typename T>
// linear interpolation via float: 0.0=begin, 1.0=end
template <typename X>
-[[nodiscard]] constexpr decltype(X{} * float{} + X{} * float{}) Lerp(const X& begin, const X& end,
- const float t) {
+[[nodiscard]] constexpr decltype(X{} * float{} + X{} * float{})
+ Lerp(const X& begin, const X& end, const float t) {
return begin * (1.f - t) + end * t;
}
diff --git a/src/common/x64/xbyak_abi.h b/src/common/x64/xbyak_abi.h
index c2c9b6134..0ddf9b83e 100644
--- a/src/common/x64/xbyak_abi.h
+++ b/src/common/x64/xbyak_abi.h
@@ -6,7 +6,7 @@
#include <bitset>
#include <initializer_list>
-#include <xbyak.h>
+#include <xbyak/xbyak.h>
#include "common/assert.h"
namespace Common::X64 {
diff --git a/src/common/x64/xbyak_util.h b/src/common/x64/xbyak_util.h
index df17f8cbe..44d2558f1 100644
--- a/src/common/x64/xbyak_util.h
+++ b/src/common/x64/xbyak_util.h
@@ -5,7 +5,7 @@
#pragma once
#include <type_traits>
-#include <xbyak.h>
+#include <xbyak/xbyak.h>
#include "common/x64/xbyak_abi.h"
namespace Common::X64 {