summaryrefslogtreecommitdiffstats
path: root/src/common
diff options
context:
space:
mode:
Diffstat (limited to 'src/common')
-rw-r--r--src/common/CMakeLists.txt70
-rw-r--r--src/common/common_sizes.h43
-rw-r--r--src/common/detached_tasks.cpp2
-rw-r--r--src/common/fs/file.cpp33
-rw-r--r--src/common/fs/file.h21
-rw-r--r--src/common/fs/fs.cpp5
-rw-r--r--src/common/fs/fs.h30
-rw-r--r--src/common/fs/fs_util.cpp4
-rw-r--r--src/common/fs/fs_util.h11
-rw-r--r--src/common/hex_util.h3
-rw-r--r--src/common/host_memory.cpp2
-rw-r--r--src/common/literals.h31
-rw-r--r--src/common/logging/backend.cpp30
-rw-r--r--src/common/logging/filter.cpp4
-rw-r--r--src/common/logging/types.h6
-rw-r--r--src/common/scm_rev.cpp.in2
-rw-r--r--src/common/settings.cpp35
-rw-r--r--src/common/settings.h426
-rw-r--r--src/common/thread_worker.cpp58
-rw-r--r--src/common/thread_worker.h103
-rw-r--r--src/common/unique_function.h62
-rw-r--r--src/common/uuid.h5
22 files changed, 636 insertions, 350 deletions
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index 7534eb8f1..57922b51c 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -1,8 +1,3 @@
-# Add a custom command to generate a new shader_cache_version hash when any of the following files change
-# NOTE: This is an approximation of what files affect shader generation, its possible something else
-# could affect the result, but much more unlikely than the following files. Keeping a list of files
-# like this allows for much better caching since it doesn't force the user to recompile binary shaders every update
-set(VIDEO_CORE "${CMAKE_SOURCE_DIR}/src/video_core")
if (DEFINED ENV{AZURECIREPO})
set(BUILD_REPOSITORY $ENV{AZURECIREPO})
endif()
@@ -30,64 +25,7 @@ add_custom_command(OUTPUT scm_rev.cpp
-DGIT_EXECUTABLE=${GIT_EXECUTABLE}
-P ${CMAKE_SOURCE_DIR}/CMakeModules/GenerateSCMRev.cmake
DEPENDS
- # WARNING! It was too much work to try and make a common location for this list,
- # so if you need to change it, please update CMakeModules/GenerateSCMRev.cmake as well
- "${VIDEO_CORE}/renderer_opengl/gl_arb_decompiler.cpp"
- "${VIDEO_CORE}/renderer_opengl/gl_arb_decompiler.h"
- "${VIDEO_CORE}/renderer_opengl/gl_shader_cache.cpp"
- "${VIDEO_CORE}/renderer_opengl/gl_shader_cache.h"
- "${VIDEO_CORE}/renderer_opengl/gl_shader_decompiler.cpp"
- "${VIDEO_CORE}/renderer_opengl/gl_shader_decompiler.h"
- "${VIDEO_CORE}/renderer_opengl/gl_shader_disk_cache.cpp"
- "${VIDEO_CORE}/renderer_opengl/gl_shader_disk_cache.h"
- "${VIDEO_CORE}/shader/decode/arithmetic.cpp"
- "${VIDEO_CORE}/shader/decode/arithmetic_half.cpp"
- "${VIDEO_CORE}/shader/decode/arithmetic_half_immediate.cpp"
- "${VIDEO_CORE}/shader/decode/arithmetic_immediate.cpp"
- "${VIDEO_CORE}/shader/decode/arithmetic_integer.cpp"
- "${VIDEO_CORE}/shader/decode/arithmetic_integer_immediate.cpp"
- "${VIDEO_CORE}/shader/decode/bfe.cpp"
- "${VIDEO_CORE}/shader/decode/bfi.cpp"
- "${VIDEO_CORE}/shader/decode/conversion.cpp"
- "${VIDEO_CORE}/shader/decode/ffma.cpp"
- "${VIDEO_CORE}/shader/decode/float_set.cpp"
- "${VIDEO_CORE}/shader/decode/float_set_predicate.cpp"
- "${VIDEO_CORE}/shader/decode/half_set.cpp"
- "${VIDEO_CORE}/shader/decode/half_set_predicate.cpp"
- "${VIDEO_CORE}/shader/decode/hfma2.cpp"
- "${VIDEO_CORE}/shader/decode/image.cpp"
- "${VIDEO_CORE}/shader/decode/integer_set.cpp"
- "${VIDEO_CORE}/shader/decode/integer_set_predicate.cpp"
- "${VIDEO_CORE}/shader/decode/memory.cpp"
- "${VIDEO_CORE}/shader/decode/texture.cpp"
- "${VIDEO_CORE}/shader/decode/other.cpp"
- "${VIDEO_CORE}/shader/decode/predicate_set_predicate.cpp"
- "${VIDEO_CORE}/shader/decode/predicate_set_register.cpp"
- "${VIDEO_CORE}/shader/decode/register_set_predicate.cpp"
- "${VIDEO_CORE}/shader/decode/shift.cpp"
- "${VIDEO_CORE}/shader/decode/video.cpp"
- "${VIDEO_CORE}/shader/decode/warp.cpp"
- "${VIDEO_CORE}/shader/decode/xmad.cpp"
- "${VIDEO_CORE}/shader/ast.cpp"
- "${VIDEO_CORE}/shader/ast.h"
- "${VIDEO_CORE}/shader/compiler_settings.cpp"
- "${VIDEO_CORE}/shader/compiler_settings.h"
- "${VIDEO_CORE}/shader/control_flow.cpp"
- "${VIDEO_CORE}/shader/control_flow.h"
- "${VIDEO_CORE}/shader/decode.cpp"
- "${VIDEO_CORE}/shader/expr.cpp"
- "${VIDEO_CORE}/shader/expr.h"
- "${VIDEO_CORE}/shader/node.h"
- "${VIDEO_CORE}/shader/node_helper.cpp"
- "${VIDEO_CORE}/shader/node_helper.h"
- "${VIDEO_CORE}/shader/registry.cpp"
- "${VIDEO_CORE}/shader/registry.h"
- "${VIDEO_CORE}/shader/shader_ir.cpp"
- "${VIDEO_CORE}/shader/shader_ir.h"
- "${VIDEO_CORE}/shader/track.cpp"
- "${VIDEO_CORE}/shader/transform_feedback.cpp"
- "${VIDEO_CORE}/shader/transform_feedback.h"
- # and also check that the scm_rev files haven't changed
+ # Check that the scm_rev files haven't changed
"${CMAKE_CURRENT_SOURCE_DIR}/scm_rev.cpp.in"
"${CMAKE_CURRENT_SOURCE_DIR}/scm_rev.h"
# technically we should regenerate if the git version changed, but its not worth the effort imo
@@ -110,7 +48,6 @@ add_library(common STATIC
cityhash.cpp
cityhash.h
common_funcs.h
- common_sizes.h
common_types.h
concepts.h
div_ceil.h
@@ -134,6 +71,7 @@ add_library(common STATIC
host_memory.cpp
host_memory.h
intrusive_red_black_tree.h
+ literals.h
logging/backend.cpp
logging/backend.h
logging/filter.cpp
@@ -180,7 +118,6 @@ add_library(common STATIC
thread.cpp
thread.h
thread_queue_list.h
- thread_worker.cpp
thread_worker.h
threadsafe_queue.h
time_zone.cpp
@@ -188,6 +125,7 @@ add_library(common STATIC
tiny_mt.h
tree.h
uint128.h
+ unique_function.h
uuid.cpp
uuid.h
vector_math.h
@@ -231,7 +169,7 @@ endif()
create_target_directory_groups(common)
-target_link_libraries(common PUBLIC ${Boost_LIBRARIES} fmt::fmt microprofile)
+target_link_libraries(common PUBLIC ${Boost_LIBRARIES} fmt::fmt microprofile Threads::Threads)
target_link_libraries(common PRIVATE lz4::lz4 xbyak)
if (MSVC)
target_link_libraries(common PRIVATE zstd::zstd)
diff --git a/src/common/common_sizes.h b/src/common/common_sizes.h
deleted file mode 100644
index 7e9fd968b..000000000
--- a/src/common/common_sizes.h
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright 2021 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#pragma once
-
-#include <limits>
-
-#include "common/common_types.h"
-
-namespace Common {
-
-enum : u64 {
- Size_1_KB = 0x400ULL,
- Size_64_KB = 64ULL * Size_1_KB,
- Size_128_KB = 128ULL * Size_1_KB,
- Size_1_MB = 0x100000ULL,
- Size_2_MB = 2ULL * Size_1_MB,
- Size_4_MB = 4ULL * Size_1_MB,
- Size_5_MB = 5ULL * Size_1_MB,
- Size_14_MB = 14ULL * Size_1_MB,
- Size_32_MB = 32ULL * Size_1_MB,
- Size_33_MB = 33ULL * Size_1_MB,
- Size_128_MB = 128ULL * Size_1_MB,
- Size_448_MB = 448ULL * Size_1_MB,
- Size_507_MB = 507ULL * Size_1_MB,
- Size_562_MB = 562ULL * Size_1_MB,
- Size_1554_MB = 1554ULL * Size_1_MB,
- Size_2048_MB = 2048ULL * Size_1_MB,
- Size_2193_MB = 2193ULL * Size_1_MB,
- Size_3285_MB = 3285ULL * Size_1_MB,
- Size_4916_MB = 4916ULL * Size_1_MB,
- Size_1_GB = 0x40000000ULL,
- Size_2_GB = 2ULL * Size_1_GB,
- Size_4_GB = 4ULL * Size_1_GB,
- Size_6_GB = 6ULL * Size_1_GB,
- Size_8_GB = 8ULL * Size_1_GB,
- Size_64_GB = 64ULL * Size_1_GB,
- Size_512_GB = 512ULL * Size_1_GB,
- Size_Invalid = std::numeric_limits<u64>::max(),
-};
-
-} // namespace Common
diff --git a/src/common/detached_tasks.cpp b/src/common/detached_tasks.cpp
index f2b4939df..c1362631e 100644
--- a/src/common/detached_tasks.cpp
+++ b/src/common/detached_tasks.cpp
@@ -21,6 +21,8 @@ void DetachedTasks::WaitForAllTasks() {
}
DetachedTasks::~DetachedTasks() {
+ WaitForAllTasks();
+
std::unique_lock lock{mutex};
ASSERT(count == 0);
instance = nullptr;
diff --git a/src/common/fs/file.cpp b/src/common/fs/file.cpp
index 710e88b39..274f57659 100644
--- a/src/common/fs/file.cpp
+++ b/src/common/fs/file.cpp
@@ -172,7 +172,7 @@ std::string ReadStringFromFile(const std::filesystem::path& path, FileType type)
size_t WriteStringToFile(const std::filesystem::path& path, FileType type,
std::string_view string) {
- if (!IsFile(path)) {
+ if (Exists(path) && !IsFile(path)) {
return 0;
}
@@ -183,7 +183,7 @@ size_t WriteStringToFile(const std::filesystem::path& path, FileType type,
size_t AppendStringToFile(const std::filesystem::path& path, FileType type,
std::string_view string) {
- if (!IsFile(path)) {
+ if (Exists(path) && !IsFile(path)) {
return 0;
}
@@ -306,9 +306,9 @@ bool IOFile::Flush() const {
errno = 0;
#ifdef _WIN32
- const auto flush_result = std::fflush(file) == 0 && _commit(fileno(file)) == 0;
+ const auto flush_result = std::fflush(file) == 0;
#else
- const auto flush_result = std::fflush(file) == 0 && fsync(fileno(file)) == 0;
+ const auto flush_result = std::fflush(file) == 0;
#endif
if (!flush_result) {
@@ -320,6 +320,28 @@ bool IOFile::Flush() const {
return flush_result;
}
+bool IOFile::Commit() const {
+ if (!IsOpen()) {
+ return false;
+ }
+
+ errno = 0;
+
+#ifdef _WIN32
+ const auto commit_result = std::fflush(file) == 0 && _commit(fileno(file)) == 0;
+#else
+ const auto commit_result = std::fflush(file) == 0 && fsync(fileno(file)) == 0;
+#endif
+
+ if (!commit_result) {
+ const auto ec = std::error_code{errno, std::generic_category()};
+ LOG_ERROR(Common_Filesystem, "Failed to commit the file at path={}, ec_message={}",
+ PathToUTF8String(file_path), ec.message());
+ }
+
+ return commit_result;
+}
+
bool IOFile::SetSize(u64 size) const {
if (!IsOpen()) {
return false;
@@ -347,6 +369,9 @@ u64 IOFile::GetSize() const {
return 0;
}
+ // Flush any unwritten buffered data into the file prior to retrieving the file size.
+ std::fflush(file);
+
std::error_code ec;
const auto file_size = fs::file_size(file_path, ec);
diff --git a/src/common/fs/file.h b/src/common/fs/file.h
index 0f10b6003..2c4ab4332 100644
--- a/src/common/fs/file.h
+++ b/src/common/fs/file.h
@@ -49,7 +49,7 @@ void OpenFileStream(FileStream& file_stream, const Path& path, std::ios_base::op
/**
* Reads an entire file at path and returns a string of the contents read from the file.
- * If the filesystem object at path is not a file, this function returns an empty string.
+ * If the filesystem object at path is not a regular file, this function returns an empty string.
*
* @param path Filesystem path
* @param type File type
@@ -72,7 +72,8 @@ template <typename Path>
/**
* Writes a string to a file at path and returns the number of characters successfully written.
* If a file already exists at path, its contents will be erased.
- * If the filesystem object at path is not a file, this function returns 0.
+ * If a file does not exist at path, it creates and opens a new empty file for writing.
+ * If the filesystem object at path exists and is not a regular file, this function returns 0.
*
* @param path Filesystem path
* @param type File type
@@ -95,7 +96,8 @@ template <typename Path>
/**
* Appends a string to a file at path and returns the number of characters successfully written.
- * If the filesystem object at path is not a file, this function returns 0.
+ * If a file does not exist at path, it creates and opens a new empty file for appending.
+ * If the filesystem object at path exists and is not a regular file, this function returns 0.
*
* @param path Filesystem path
* @param type File type
@@ -394,11 +396,20 @@ public:
[[nodiscard]] size_t WriteString(std::span<const char> string) const;
/**
- * Flushes any unwritten buffered data into the file.
+ * Attempts to flush any unwritten buffered data into the file.
*
* @returns True if the flush was successful, false otherwise.
*/
- [[nodiscard]] bool Flush() const;
+ bool Flush() const;
+
+ /**
+ * Attempts to commit the file into the disk.
+ * Note that this is an expensive operation as this forces the operating system to write
+ * the contents of the file associated with the file descriptor into the disk.
+ *
+ * @returns True if the commit was successful, false otherwise.
+ */
+ bool Commit() const;
/**
* Resizes the file to a given size.
diff --git a/src/common/fs/fs.cpp b/src/common/fs/fs.cpp
index d3159e908..9089cad67 100644
--- a/src/common/fs/fs.cpp
+++ b/src/common/fs/fs.cpp
@@ -135,8 +135,9 @@ std::shared_ptr<IOFile> FileOpen(const fs::path& path, FileAccessMode mode, File
return nullptr;
}
- if (!IsFile(path)) {
- LOG_ERROR(Common_Filesystem, "Filesystem object at path={} is not a file",
+ if (Exists(path) && !IsFile(path)) {
+ LOG_ERROR(Common_Filesystem,
+ "Filesystem object at path={} exists and is not a regular file",
PathToUTF8String(path));
return nullptr;
}
diff --git a/src/common/fs/fs.h b/src/common/fs/fs.h
index f6f256349..183126de3 100644
--- a/src/common/fs/fs.h
+++ b/src/common/fs/fs.h
@@ -48,18 +48,18 @@ template <typename Path>
*
* Failures occur when:
* - Input path is not valid
- * - Filesystem object at path is not a file
+ * - Filesystem object at path is not a regular file
* - Filesystem at path is read only
*
* @param path Filesystem path
*
* @returns True if file removal succeeds or file does not exist, false otherwise.
*/
-[[nodiscard]] bool RemoveFile(const std::filesystem::path& path);
+bool RemoveFile(const std::filesystem::path& path);
#ifdef _WIN32
template <typename Path>
-[[nodiscard]] bool RemoveFile(const Path& path) {
+bool RemoveFile(const Path& path) {
if constexpr (IsChar<typename Path::value_type>) {
return RemoveFile(ToU8String(path));
} else {
@@ -74,7 +74,7 @@ template <typename Path>
* Failures occur when:
* - One or both input path(s) is not valid
* - Filesystem object at old_path does not exist
- * - Filesystem object at old_path is not a file
+ * - Filesystem object at old_path is not a regular file
* - Filesystem object at new_path exists
* - Filesystem at either path is read only
*
@@ -110,8 +110,8 @@ template <typename Path1, typename Path2>
*
* Failures occur when:
* - Input path is not valid
- * - Filesystem object at path is not a file
- * - The file is not opened
+ * - Filesystem object at path exists and is not a regular file
+ * - The file is not open
*
* @param path Filesystem path
* @param mode File access mode
@@ -251,11 +251,11 @@ template <typename Path>
*
* @returns True if directory removal succeeds or directory does not exist, false otherwise.
*/
-[[nodiscard]] bool RemoveDir(const std::filesystem::path& path);
+bool RemoveDir(const std::filesystem::path& path);
#ifdef _WIN32
template <typename Path>
-[[nodiscard]] bool RemoveDir(const Path& path) {
+bool RemoveDir(const Path& path) {
if constexpr (IsChar<typename Path::value_type>) {
return RemoveDir(ToU8String(path));
} else {
@@ -276,11 +276,11 @@ template <typename Path>
*
* @returns True if the directory and all of its contents are removed successfully, false otherwise.
*/
-[[nodiscard]] bool RemoveDirRecursively(const std::filesystem::path& path);
+bool RemoveDirRecursively(const std::filesystem::path& path);
#ifdef _WIN32
template <typename Path>
-[[nodiscard]] bool RemoveDirRecursively(const Path& path) {
+bool RemoveDirRecursively(const Path& path) {
if constexpr (IsChar<typename Path::value_type>) {
return RemoveDirRecursively(ToU8String(path));
} else {
@@ -301,11 +301,11 @@ template <typename Path>
*
* @returns True if all of the directory's contents are removed successfully, false otherwise.
*/
-[[nodiscard]] bool RemoveDirContentsRecursively(const std::filesystem::path& path);
+bool RemoveDirContentsRecursively(const std::filesystem::path& path);
#ifdef _WIN32
template <typename Path>
-[[nodiscard]] bool RemoveDirContentsRecursively(const Path& path) {
+bool RemoveDirContentsRecursively(const Path& path) {
if constexpr (IsChar<typename Path::value_type>) {
return RemoveDirContentsRecursively(ToU8String(path));
} else {
@@ -435,11 +435,13 @@ template <typename Path>
#endif
/**
- * Returns whether a filesystem object at path is a file.
+ * Returns whether a filesystem object at path is a regular file.
+ * A regular file is a file that stores text or binary data.
+ * It is not a directory, symlink, FIFO, socket, block device, or character device.
*
* @param path Filesystem path
*
- * @returns True if a filesystem object at path is a file, false otherwise.
+ * @returns True if a filesystem object at path is a regular file, false otherwise.
*/
[[nodiscard]] bool IsFile(const std::filesystem::path& path);
diff --git a/src/common/fs/fs_util.cpp b/src/common/fs/fs_util.cpp
index 357cf5855..9f8671982 100644
--- a/src/common/fs/fs_util.cpp
+++ b/src/common/fs/fs_util.cpp
@@ -20,6 +20,10 @@ std::string ToUTF8String(std::u8string_view u8_string) {
return std::string{u8_string.begin(), u8_string.end()};
}
+std::string BufferToUTF8String(std::span<const u8> buffer) {
+ return std::string{buffer.begin(), std::ranges::find(buffer, u8{0})};
+}
+
std::string PathToUTF8String(const std::filesystem::path& path) {
return ToUTF8String(path.u8string());
}
diff --git a/src/common/fs/fs_util.h b/src/common/fs/fs_util.h
index ec9950ee7..1ec82eb35 100644
--- a/src/common/fs/fs_util.h
+++ b/src/common/fs/fs_util.h
@@ -47,6 +47,17 @@ concept IsChar = std::same_as<T, char>;
[[nodiscard]] std::string ToUTF8String(std::u8string_view u8_string);
/**
+ * Converts a buffer of bytes to a UTF8-encoded std::string.
+ * This converts from the start of the buffer until the first encountered null-terminator.
+ * If no null-terminator is found, this converts the entire buffer instead.
+ *
+ * @param buffer Buffer of bytes
+ *
+ * @returns UTF-8 encoded std::string.
+ */
+[[nodiscard]] std::string BufferToUTF8String(std::span<const u8> buffer);
+
+/**
* Converts a filesystem path to a UTF-8 encoded std::string.
*
* @param path Filesystem path
diff --git a/src/common/hex_util.h b/src/common/hex_util.h
index a8d414fb8..f5f9e4507 100644
--- a/src/common/hex_util.h
+++ b/src/common/hex_util.h
@@ -53,8 +53,9 @@ template <typename ContiguousContainer>
std::string out;
out.reserve(std::size(data) * pad_width);
+ const auto format_str = fmt::runtime(upper ? "{:02X}" : "{:02x}");
for (const u8 c : data) {
- out += fmt::format(upper ? "{:02X}" : "{:02x}", c);
+ out += fmt::format(format_str, c);
}
return out;
diff --git a/src/common/host_memory.cpp b/src/common/host_memory.cpp
index 8bd70abc7..2a5a7596c 100644
--- a/src/common/host_memory.cpp
+++ b/src/common/host_memory.cpp
@@ -34,7 +34,7 @@ constexpr size_t HugePageSize = 0x200000;
// Manually imported for MinGW compatibility
#ifndef MEM_RESERVE_PLACEHOLDER
-#define MEM_RESERVE_PLACEHOLDER 0x0004000
+#define MEM_RESERVE_PLACEHOLDER 0x00040000
#endif
#ifndef MEM_REPLACE_PLACEHOLDER
#define MEM_REPLACE_PLACEHOLDER 0x00004000
diff --git a/src/common/literals.h b/src/common/literals.h
new file mode 100644
index 000000000..d55fed40b
--- /dev/null
+++ b/src/common/literals.h
@@ -0,0 +1,31 @@
+// Copyright 2021 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "common/common_types.h"
+
+namespace Common::Literals {
+
+constexpr u64 operator""_KiB(unsigned long long int x) {
+ return 1024ULL * x;
+}
+
+constexpr u64 operator""_MiB(unsigned long long int x) {
+ return 1024_KiB * x;
+}
+
+constexpr u64 operator""_GiB(unsigned long long int x) {
+ return 1024_MiB * x;
+}
+
+constexpr u64 operator""_TiB(unsigned long long int x) {
+ return 1024_GiB * x;
+}
+
+constexpr u64 operator""_PiB(unsigned long long int x) {
+ return 1024_TiB * x;
+}
+
+} // namespace Common::Literals
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp
index d5cff400f..61dddab3f 100644
--- a/src/common/logging/backend.cpp
+++ b/src/common/logging/backend.cpp
@@ -19,6 +19,8 @@
#include "common/assert.h"
#include "common/fs/file.h"
#include "common/fs/fs.h"
+#include "common/literals.h"
+
#include "common/logging/backend.h"
#include "common/logging/log.h"
#include "common/logging/text_formatter.h"
@@ -98,8 +100,8 @@ private:
write_logs(entry);
}
- // 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.
+ // 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)) {
@@ -159,7 +161,7 @@ FileBackend::FileBackend(const std::filesystem::path& filename) {
// Existence checks are done within the functions themselves.
// We don't particularly care if these succeed or not.
- void(FS::RemoveFile(old_filename));
+ FS::RemoveFile(old_filename);
void(FS::RenameFile(filename, old_filename));
file =
@@ -169,24 +171,28 @@ FileBackend::FileBackend(const std::filesystem::path& filename) {
FileBackend::~FileBackend() = default;
void FileBackend::Write(const Entry& entry) {
- // prevent logs from going over the maximum size (in case its spamming and the user doesn't
- // know)
- constexpr std::size_t MAX_BYTES_WRITTEN = 100 * 1024 * 1024;
- constexpr std::size_t MAX_BYTES_WRITTEN_EXTENDED = 1024 * 1024 * 1024;
-
if (!file->IsOpen()) {
return;
}
- if (Settings::values.extended_logging && bytes_written > MAX_BYTES_WRITTEN_EXTENDED) {
- return;
- } else if (!Settings::values.extended_logging && bytes_written > MAX_BYTES_WRITTEN) {
+ 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;
+
+ const bool write_limit_exceeded =
+ bytes_written > MAX_BYTES_WRITTEN_EXTENDED ||
+ (bytes_written > MAX_BYTES_WRITTEN && !Settings::values.extended_logging);
+
+ // Close the file after the write limit is exceeded.
+ if (write_limit_exceeded) {
+ file->Close();
return;
}
bytes_written += file->WriteString(FormatLogMessage(entry).append(1, '\n'));
if (entry.log_level >= Level::Error) {
- void(file->Flush());
+ file->Flush();
}
}
diff --git a/src/common/logging/filter.cpp b/src/common/logging/filter.cpp
index 4f2cc29e1..f055f0e11 100644
--- a/src/common/logging/filter.cpp
+++ b/src/common/logging/filter.cpp
@@ -144,6 +144,10 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
SUB(Render, Software) \
SUB(Render, OpenGL) \
SUB(Render, Vulkan) \
+ CLS(Shader) \
+ SUB(Shader, SPIRV) \
+ SUB(Shader, GLASM) \
+ SUB(Shader, GLSL) \
CLS(Audio) \
SUB(Audio, DSP) \
SUB(Audio, Sink) \
diff --git a/src/common/logging/types.h b/src/common/logging/types.h
index ee9a1ed84..7ad0334fc 100644
--- a/src/common/logging/types.h
+++ b/src/common/logging/types.h
@@ -2,6 +2,8 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#pragma once
+
#include <chrono>
#include "common/common_types.h"
@@ -112,6 +114,10 @@ enum class Class : u8 {
Render_Software, ///< Software renderer backend
Render_OpenGL, ///< OpenGL backend
Render_Vulkan, ///< Vulkan backend
+ Shader, ///< Shader recompiler
+ Shader_SPIRV, ///< Shader SPIR-V code generation
+ Shader_GLASM, ///< Shader GLASM code generation
+ Shader_GLSL, ///< Shader GLSL code generation
Audio, ///< Audio emulation
Audio_DSP, ///< The HLE implementation of the DSP
Audio_Sink, ///< Emulator audio output backend
diff --git a/src/common/scm_rev.cpp.in b/src/common/scm_rev.cpp.in
index 5f126f324..cc88994c6 100644
--- a/src/common/scm_rev.cpp.in
+++ b/src/common/scm_rev.cpp.in
@@ -14,7 +14,6 @@
#define BUILD_ID "@BUILD_ID@"
#define TITLE_BAR_FORMAT_IDLE "@TITLE_BAR_FORMAT_IDLE@"
#define TITLE_BAR_FORMAT_RUNNING "@TITLE_BAR_FORMAT_RUNNING@"
-#define SHADER_CACHE_VERSION "@SHADER_CACHE_VERSION@"
namespace Common {
@@ -28,7 +27,6 @@ const char g_build_version[] = BUILD_VERSION;
const char g_build_id[] = BUILD_ID;
const char g_title_bar_format_idle[] = TITLE_BAR_FORMAT_IDLE;
const char g_title_bar_format_running[] = TITLE_BAR_FORMAT_RUNNING;
-const char g_shader_cache_version[] = SHADER_CACHE_VERSION;
} // namespace
diff --git a/src/common/settings.cpp b/src/common/settings.cpp
index 9ec71eced..996315999 100644
--- a/src/common/settings.cpp
+++ b/src/common/settings.cpp
@@ -41,15 +41,15 @@ void LogSettings() {
LOG_INFO(Config, "yuzu Configuration:");
log_setting("Controls_UseDockedMode", values.use_docked_mode.GetValue());
log_setting("System_RngSeed", values.rng_seed.GetValue().value_or(0));
- log_setting("System_CurrentUser", values.current_user);
+ log_setting("System_CurrentUser", values.current_user.GetValue());
log_setting("System_LanguageIndex", values.language_index.GetValue());
log_setting("System_RegionIndex", values.region_index.GetValue());
log_setting("System_TimeZoneIndex", values.time_zone_index.GetValue());
log_setting("Core_UseMultiCore", values.use_multi_core.GetValue());
log_setting("CPU_Accuracy", values.cpu_accuracy.GetValue());
log_setting("Renderer_UseResolutionFactor", values.resolution_factor.GetValue());
- log_setting("Renderer_UseFrameLimit", values.use_frame_limit.GetValue());
- log_setting("Renderer_FrameLimit", values.frame_limit.GetValue());
+ log_setting("Renderer_UseSpeedLimit", values.use_speed_limit.GetValue());
+ log_setting("Renderer_SpeedLimit", values.speed_limit.GetValue());
log_setting("Renderer_UseDiskShaderCache", values.use_disk_shader_cache.GetValue());
log_setting("Renderer_GPUAccuracyLevel", values.gpu_accuracy.GetValue());
log_setting("Renderer_UseAsynchronousGpuEmulation",
@@ -57,21 +57,22 @@ void LogSettings() {
log_setting("Renderer_UseNvdecEmulation", values.use_nvdec_emulation.GetValue());
log_setting("Renderer_AccelerateASTC", values.accelerate_astc.GetValue());
log_setting("Renderer_UseVsync", values.use_vsync.GetValue());
- log_setting("Renderer_UseAssemblyShaders", values.use_assembly_shaders.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);
+ 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);
- log_setting("DataStorage_UseVirtualSd", values.use_virtual_sd);
+ 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));
log_path("DataStorage_ConfigDir", Common::FS::GetYuzuPath(Common::FS::YuzuPath::ConfigDir));
log_path("DataStorage_LoadDir", Common::FS::GetYuzuPath(Common::FS::YuzuPath::LoadDir));
log_path("DataStorage_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);
- log_setting("Services_BCATBackend", values.bcat_backend);
- log_setting("Services_BCATBoxcatLocal", values.bcat_boxcat_local);
+ 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());
}
bool IsConfiguringGlobal() {
@@ -92,8 +93,8 @@ bool IsGPULevelHigh() {
}
bool IsFastmemEnabled() {
- if (values.cpu_accuracy.GetValue() == CPUAccuracy::DebugMode) {
- return values.cpuopt_fastmem;
+ if (values.cpu_debug_mode) {
+ return static_cast<bool>(values.cpuopt_fastmem);
}
return true;
}
@@ -102,7 +103,7 @@ float Volume() {
if (values.audio_muted) {
return 0.0f;
}
- return values.volume.GetValue();
+ return values.volume.GetValue() / 100.0f;
}
void RestoreGlobalState(bool is_powered_on) {
@@ -122,6 +123,7 @@ void RestoreGlobalState(bool is_powered_on) {
values.cpu_accuracy.SetGlobal(true);
values.cpuopt_unsafe_unfuse_fma.SetGlobal(true);
values.cpuopt_unsafe_reduce_fp_error.SetGlobal(true);
+ values.cpuopt_unsafe_ignore_standard_fpcr.SetGlobal(true);
values.cpuopt_unsafe_inaccurate_nan.SetGlobal(true);
values.cpuopt_unsafe_fastmem_check.SetGlobal(true);
@@ -130,17 +132,18 @@ void RestoreGlobalState(bool is_powered_on) {
values.vulkan_device.SetGlobal(true);
values.aspect_ratio.SetGlobal(true);
values.max_anisotropy.SetGlobal(true);
- values.use_frame_limit.SetGlobal(true);
- values.frame_limit.SetGlobal(true);
+ values.use_speed_limit.SetGlobal(true);
+ values.speed_limit.SetGlobal(true);
values.use_disk_shader_cache.SetGlobal(true);
values.gpu_accuracy.SetGlobal(true);
values.use_asynchronous_gpu_emulation.SetGlobal(true);
values.use_nvdec_emulation.SetGlobal(true);
values.accelerate_astc.SetGlobal(true);
values.use_vsync.SetGlobal(true);
- values.use_assembly_shaders.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 6198f2d9f..d8730f515 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -10,10 +10,12 @@
#include <map>
#include <optional>
#include <string>
+#include <utility>
#include <vector>
#include "common/common_types.h"
#include "common/settings_input.h"
+#include "input_common/udp/client.h"
namespace Settings {
@@ -22,6 +24,12 @@ enum class RendererBackend : u32 {
Vulkan = 1,
};
+enum class ShaderBackend : u32 {
+ GLSL = 0,
+ GLASM = 1,
+ SPIRV = 2,
+};
+
enum class GPUAccuracy : u32 {
Normal = 0,
High = 1,
@@ -29,73 +37,240 @@ enum class GPUAccuracy : u32 {
};
enum class CPUAccuracy : u32 {
- Accurate = 0,
- Unsafe = 1,
- DebugMode = 2,
+ Auto = 0,
+ Accurate = 1,
+ Unsafe = 2,
+};
+
+/** The BasicSetting class is a simple resource manager. It defines a label and default value
+ * alongside the actual value of the setting for simpler and less-error prone use with frontend
+ * configurations. Setting a default value and label is required, though subclasses may deviate from
+ * this requirement.
+ */
+template <typename Type>
+class BasicSetting {
+protected:
+ BasicSetting() = default;
+
+ /**
+ * Only sets the setting to the given initializer, leaving the other members to their default
+ * initializers.
+ *
+ * @param global_val Initial value of the setting
+ */
+ explicit BasicSetting(const Type& global_val) : global{global_val} {}
+
+public:
+ /**
+ * Sets a default value, label, and setting value.
+ *
+ * @param default_val Intial value of the setting, and default value of the setting
+ * @param name Label for the setting
+ */
+ explicit BasicSetting(const Type& default_val, const std::string& name)
+ : default_value{default_val}, global{default_val}, label{name} {}
+ ~BasicSetting() = default;
+
+ /**
+ * Returns a reference to the setting's value.
+ *
+ * @returns A reference to the setting
+ */
+ [[nodiscard]] const Type& GetValue() const {
+ return global;
+ }
+
+ /**
+ * Sets the setting to the given value.
+ *
+ * @param value The desired value
+ */
+ void SetValue(const Type& value) {
+ Type temp{value};
+ std::swap(global, temp);
+ }
+
+ /**
+ * Returns the value that this setting was created with.
+ *
+ * @returns A reference to the default value
+ */
+ [[nodiscard]] const Type& GetDefault() const {
+ return default_value;
+ }
+
+ /**
+ * Returns the label this setting was created with.
+ *
+ * @returns A reference to the label
+ */
+ [[nodiscard]] const std::string& GetLabel() const {
+ return label;
+ }
+
+ /**
+ * Assigns a value to the setting.
+ *
+ * @param value The desired setting value
+ *
+ * @returns A reference to the setting
+ */
+ const Type& operator=(const Type& value) {
+ Type temp{value};
+ std::swap(global, temp);
+ return global;
+ }
+
+ /**
+ * Returns a reference to the setting.
+ *
+ * @returns A reference to the setting
+ */
+ explicit operator const Type&() const {
+ return global;
+ }
+
+protected:
+ const Type default_value{}; ///< The default value
+ Type global{}; ///< The setting
+ const std::string label{}; ///< The setting's label
};
+/**
+ * The Setting class is a slightly more complex version of the BasicSetting class. This adds a
+ * custom setting to switch to when a guest application specifically requires it. The effect is that
+ * other components of the emulator can access the setting's intended value without any need for the
+ * component to ask whether the custom or global setting is needed at the moment.
+ *
+ * By default, the global setting is used.
+ *
+ * Like the BasicSetting, this requires setting a default value and label to use.
+ */
template <typename Type>
-class Setting final {
+class Setting final : public BasicSetting<Type> {
public:
- Setting() = default;
- explicit Setting(Type val) : global{val} {}
+ /**
+ * Sets a default value, label, and setting value.
+ *
+ * @param default_val Intial value of the setting, and default value of the setting
+ * @param name Label for the setting
+ */
+ explicit Setting(const Type& default_val, const std::string& name)
+ : BasicSetting<Type>(default_val, name) {}
~Setting() = default;
+
+ /**
+ * Tells this setting to represent either the global or custom setting when other member
+ * functions are used.
+ *
+ * @param to_global Whether to use the global or custom setting.
+ */
void SetGlobal(bool to_global) {
use_global = to_global;
}
- bool UsingGlobal() const {
+
+ /**
+ * Returns whether this setting is using the global setting or not.
+ *
+ * @returns The global state
+ */
+ [[nodiscard]] bool UsingGlobal() const {
return use_global;
}
- Type GetValue(bool need_global = false) const {
+
+ /**
+ * Returns either the global or custom setting depending on the values of this setting's global
+ * state or if the global value was specifically requested.
+ *
+ * @param need_global Request global value regardless of setting's state; defaults to false
+ *
+ * @returns The required value of the setting
+ */
+ [[nodiscard]] const Type& GetValue(bool need_global = false) const {
if (use_global || need_global) {
- return global;
+ return this->global;
}
- return local;
+ return custom;
}
+
+ /**
+ * Sets the current setting value depending on the global state.
+ *
+ * @param value The new value
+ */
void SetValue(const Type& value) {
+ Type temp{value};
if (use_global) {
- global = value;
+ std::swap(this->global, temp);
} else {
- local = value;
+ std::swap(custom, temp);
+ }
+ }
+
+ /**
+ * Assigns the current setting value depending on the global state.
+ *
+ * @param value The new value
+ *
+ * @returns A reference to the current setting value
+ */
+ const Type& operator=(const Type& value) {
+ Type temp{value};
+ if (use_global) {
+ std::swap(this->global, temp);
+ return this->global;
+ }
+ std::swap(custom, temp);
+ return custom;
+ }
+
+ /**
+ * Returns the current setting value depending on the global state.
+ *
+ * @returns A reference to the current setting value
+ */
+ explicit operator const Type&() const {
+ if (use_global) {
+ return this->global;
}
+ return custom;
}
private:
- bool use_global = true;
- Type global{};
- Type local{};
+ bool use_global{true}; ///< The setting's global state
+ Type custom{}; ///< The custom value of the setting
};
/**
- * The InputSetting class allows for getting a reference to either the global or local members.
+ * 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
- * class is to store an array of 10 PlayerInput structs for both the global and local (per-game)
- * setting and allows for easily accessing and modifying both settings.
+ * class is to store an array of 10 PlayerInput structs for both the global and custom setting and
+ * allows for easily accessing and modifying both settings.
*/
template <typename Type>
class InputSetting final {
public:
InputSetting() = default;
- explicit InputSetting(Type val) : global{val} {}
+ explicit InputSetting(Type val) : BasicSetting<Type>(val) {}
~InputSetting() = default;
void SetGlobal(bool to_global) {
use_global = to_global;
}
- bool UsingGlobal() const {
+ [[nodiscard]] bool UsingGlobal() const {
return use_global;
}
- Type& GetValue(bool need_global = false) {
+ [[nodiscard]] Type& GetValue(bool need_global = false) {
if (use_global || need_global) {
return global;
}
- return local;
+ return custom;
}
private:
- bool use_global = true;
- Type global{};
- Type local{};
+ bool use_global{true}; ///< The setting's global state
+ Type global{}; ///< The setting
+ Type custom{}; ///< The custom setting value
};
struct TouchFromButtonMap {
@@ -105,141 +280,162 @@ struct TouchFromButtonMap {
struct Values {
// Audio
- std::string audio_device_id;
- std::string sink_id;
- bool audio_muted;
- Setting<bool> enable_audio_stretching;
- Setting<float> volume;
+ 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"};
// Core
- Setting<bool> use_multi_core;
+ Setting<bool> use_multi_core{true, "use_multi_core"};
// Cpu
- Setting<CPUAccuracy> cpu_accuracy;
-
- bool cpuopt_page_tables;
- bool cpuopt_block_linking;
- bool cpuopt_return_stack_buffer;
- bool cpuopt_fast_dispatcher;
- bool cpuopt_context_elimination;
- bool cpuopt_const_prop;
- bool cpuopt_misc_ir;
- bool cpuopt_reduce_misalign_checks;
- bool cpuopt_fastmem;
-
- Setting<bool> cpuopt_unsafe_unfuse_fma;
- Setting<bool> cpuopt_unsafe_reduce_fp_error;
- Setting<bool> cpuopt_unsafe_inaccurate_nan;
- Setting<bool> cpuopt_unsafe_fastmem_check;
+ Setting<CPUAccuracy> cpu_accuracy{CPUAccuracy::Auto, "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"};
+
+ BasicSetting<bool> cpuopt_page_tables{true, "cpuopt_page_tables"};
+ BasicSetting<bool> cpuopt_block_linking{true, "cpuopt_block_linking"};
+ BasicSetting<bool> cpuopt_return_stack_buffer{true, "cpuopt_return_stack_buffer"};
+ BasicSetting<bool> cpuopt_fast_dispatcher{true, "cpuopt_fast_dispatcher"};
+ BasicSetting<bool> cpuopt_context_elimination{true, "cpuopt_context_elimination"};
+ BasicSetting<bool> cpuopt_const_prop{true, "cpuopt_const_prop"};
+ BasicSetting<bool> cpuopt_misc_ir{true, "cpuopt_misc_ir"};
+ BasicSetting<bool> cpuopt_reduce_misalign_checks{true, "cpuopt_reduce_misalign_checks"};
+ BasicSetting<bool> cpuopt_fastmem{true, "cpuopt_fastmem"};
+
+ Setting<bool> cpuopt_unsafe_unfuse_fma{true, "cpuopt_unsafe_unfuse_fma"};
+ Setting<bool> cpuopt_unsafe_reduce_fp_error{true, "cpuopt_unsafe_reduce_fp_error"};
+ Setting<bool> cpuopt_unsafe_ignore_standard_fpcr{true, "cpuopt_unsafe_ignore_standard_fpcr"};
+ Setting<bool> cpuopt_unsafe_inaccurate_nan{true, "cpuopt_unsafe_inaccurate_nan"};
+ Setting<bool> cpuopt_unsafe_fastmem_check{true, "cpuopt_unsafe_fastmem_check"};
// Renderer
- Setting<RendererBackend> renderer_backend;
- bool renderer_debug;
- Setting<int> vulkan_device;
-
- Setting<u16> resolution_factor{1};
- Setting<int> fullscreen_mode;
- Setting<int> aspect_ratio;
- Setting<int> max_anisotropy;
- Setting<bool> use_frame_limit;
- Setting<u16> frame_limit;
- Setting<bool> use_disk_shader_cache;
- Setting<GPUAccuracy> gpu_accuracy;
- Setting<bool> use_asynchronous_gpu_emulation;
- Setting<bool> use_nvdec_emulation;
- Setting<bool> accelerate_astc;
- Setting<bool> use_vsync;
- Setting<bool> use_assembly_shaders;
- Setting<bool> use_asynchronous_shaders;
- Setting<bool> use_fast_gpu_time;
-
- Setting<float> bg_red;
- Setting<float> bg_green;
- Setting<float> bg_blue;
+ Setting<RendererBackend> renderer_backend{RendererBackend::OpenGL, "backend"};
+ BasicSetting<bool> renderer_debug{false, "debug"};
+ BasicSetting<bool> enable_nsight_aftermath{false, "nsight_aftermath"};
+ BasicSetting<bool> disable_shader_loop_safety_checks{false,
+ "disable_shader_loop_safety_checks"};
+ Setting<int> vulkan_device{0, "vulkan_device"};
+
+ 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<int> fullscreen_mode{
+#ifdef _WIN32
+ 0,
+#else
+ 1,
+#endif
+ "fullscreen_mode"};
+ Setting<int> aspect_ratio{0, "aspect_ratio"};
+ Setting<int> max_anisotropy{0, "max_anisotropy"};
+ Setting<bool> use_speed_limit{true, "use_speed_limit"};
+ Setting<u16> speed_limit{100, "speed_limit"};
+ Setting<bool> use_disk_shader_cache{true, "use_disk_shader_cache"};
+ Setting<GPUAccuracy> gpu_accuracy{GPUAccuracy::High, "gpu_accuracy"};
+ Setting<bool> use_asynchronous_gpu_emulation{true, "use_asynchronous_gpu_emulation"};
+ Setting<bool> use_nvdec_emulation{true, "use_nvdec_emulation"};
+ Setting<bool> accelerate_astc{true, "accelerate_astc"};
+ Setting<bool> use_vsync{true, "use_vsync"};
+ BasicSetting<u16> fps_cap{1000, "fps_cap"};
+ BasicSetting<bool> disable_fps_limit{false, "disable_fps_limit"};
+ Setting<ShaderBackend> shader_backend{ShaderBackend::GLASM, "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"};
+ Setting<u8> bg_blue{0, "bg_blue"};
// System
- Setting<std::optional<u32>> rng_seed;
+ Setting<std::optional<u32>> rng_seed{std::optional<u32>(), "rng_seed"};
// Measured in seconds since epoch
std::optional<std::chrono::seconds> custom_rtc;
// Set on game boot, reset on stop. Seconds difference between current time and `custom_rtc`
std::chrono::seconds custom_rtc_differential;
- s32 current_user;
- Setting<s32> language_index;
- Setting<s32> region_index;
- Setting<s32> time_zone_index;
- Setting<s32> sound_index;
+ 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"};
// Controls
InputSetting<std::array<PlayerInput, 10>> players;
- Setting<bool> use_docked_mode;
+ Setting<bool> use_docked_mode{true, "use_docked_mode"};
- Setting<bool> vibration_enabled;
- Setting<bool> enable_accurate_vibrations;
+ Setting<bool> vibration_enabled{true, "vibration_enabled"};
+ Setting<bool> enable_accurate_vibrations{false, "enable_accurate_vibrations"};
- Setting<bool> motion_enabled;
- std::string motion_device;
- std::string udp_input_servers;
+ 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"};
- bool mouse_panning;
- float mouse_panning_sensitivity;
- bool mouse_enabled;
+ BasicSetting<bool> mouse_panning{false, "mouse_panning"};
+ BasicSetting<u8> mouse_panning_sensitivity{10, "mouse_panning_sensitivity"};
+ BasicSetting<bool> mouse_enabled{false, "mouse_enabled"};
std::string mouse_device;
MouseButtonsRaw mouse_buttons;
- bool emulate_analog_keyboard;
- bool keyboard_enabled;
+ BasicSetting<bool> emulate_analog_keyboard{false, "emulate_analog_keyboard"};
+ BasicSetting<bool> keyboard_enabled{false, "keyboard_enabled"};
KeyboardKeysRaw keyboard_keys;
KeyboardModsRaw keyboard_mods;
- bool debug_pad_enabled;
+ BasicSetting<bool> debug_pad_enabled{false, "debug_pad_enabled"};
ButtonsRaw debug_pad_buttons;
AnalogsRaw debug_pad_analogs;
TouchscreenInput touchscreen;
- bool use_touch_from_button;
- std::string touch_device;
- int touch_from_button_map_index;
+ BasicSetting<bool> use_touch_from_button{false, "use_touch_from_button"};
+ BasicSetting<std::string> touch_device{"min_x:100,min_y:50,max_x:1800,max_y:850",
+ "touch_device"};
+ BasicSetting<int> touch_from_button_map_index{0, "touch_from_button_map"};
std::vector<TouchFromButtonMap> touch_from_button_maps;
std::atomic_bool is_device_reload_pending{true};
// Data Storage
- bool use_virtual_sd;
- bool gamecard_inserted;
- bool gamecard_current_game;
- std::string gamecard_path;
+ BasicSetting<bool> use_virtual_sd{true, "use_virtual_sd"};
+ BasicSetting<bool> gamecard_inserted{false, "gamecard_inserted"};
+ BasicSetting<bool> gamecard_current_game{false, "gamecard_current_game"};
+ BasicSetting<std::string> gamecard_path{std::string(), "gamecard_path"};
// Debugging
bool record_frame_times;
- bool use_gdbstub;
- u16 gdbstub_port;
- std::string program_args;
- bool dump_exefs;
- bool dump_nso;
- bool enable_fs_access_log;
- bool reporting_services;
- bool quest_flag;
- bool disable_macro_jit;
- bool extended_logging;
- bool use_debug_asserts;
- bool use_auto_stub;
+ BasicSetting<bool> use_gdbstub{false, "use_gdbstub"};
+ BasicSetting<u16> gdbstub_port{0, "gdbstub_port"};
+ BasicSetting<std::string> program_args{std::string(), "program_args"};
+ BasicSetting<bool> dump_exefs{false, "dump_exefs"};
+ BasicSetting<bool> dump_nso{false, "dump_nso"};
+ BasicSetting<bool> enable_fs_access_log{false, "enable_fs_access_log"};
+ BasicSetting<bool> reporting_services{false, "reporting_services"};
+ BasicSetting<bool> quest_flag{false, "quest_flag"};
+ BasicSetting<bool> disable_macro_jit{false, "disable_macro_jit"};
+ BasicSetting<bool> extended_logging{false, "extended_logging"};
+ BasicSetting<bool> use_debug_asserts{false, "use_debug_asserts"};
+ BasicSetting<bool> use_auto_stub{false, "use_auto_stub"};
// Miscellaneous
- std::string log_filter;
- bool use_dev_keys;
+ BasicSetting<std::string> log_filter{"*:Info", "log_filter"};
+ BasicSetting<bool> use_dev_keys{false, "use_dev_keys"};
// Services
- std::string bcat_backend;
- bool bcat_boxcat_local;
+ BasicSetting<std::string> bcat_backend{"none", "bcat_backend"};
+ BasicSetting<bool> bcat_boxcat_local{false, "bcat_boxcat_local"};
// WebService
- bool enable_telemetry;
- std::string web_api_url;
- std::string yuzu_username;
- std::string yuzu_token;
+ BasicSetting<bool> enable_telemetry{true, "enable_telemetry"};
+ BasicSetting<std::string> web_api_url{"https://api.yuzu-emu.org", "web_api_url"};
+ BasicSetting<std::string> yuzu_username{std::string(), "yuzu_username"};
+ BasicSetting<std::string> yuzu_token{std::string(), "yuzu_token"};
// Add-Ons
std::map<u64, std::vector<std::string>> disabled_addons;
diff --git a/src/common/thread_worker.cpp b/src/common/thread_worker.cpp
deleted file mode 100644
index 8f9bf447a..000000000
--- a/src/common/thread_worker.cpp
+++ /dev/null
@@ -1,58 +0,0 @@
-// Copyright 2020 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#include "common/thread.h"
-#include "common/thread_worker.h"
-
-namespace Common {
-
-ThreadWorker::ThreadWorker(std::size_t num_workers, const std::string& name) {
- for (std::size_t i = 0; i < num_workers; ++i)
- threads.emplace_back([this, thread_name{std::string{name}}] {
- Common::SetCurrentThreadName(thread_name.c_str());
-
- // Wait for first request
- {
- std::unique_lock lock{queue_mutex};
- condition.wait(lock, [this] { return stop || !requests.empty(); });
- }
-
- while (true) {
- std::function<void()> task;
-
- {
- std::unique_lock lock{queue_mutex};
- condition.wait(lock, [this] { return stop || !requests.empty(); });
- if (stop || requests.empty()) {
- return;
- }
- task = std::move(requests.front());
- requests.pop();
- }
-
- task();
- }
- });
-}
-
-ThreadWorker::~ThreadWorker() {
- {
- std::unique_lock lock{queue_mutex};
- stop = true;
- }
- condition.notify_all();
- for (std::thread& thread : threads) {
- thread.join();
- }
-}
-
-void ThreadWorker::QueueWork(std::function<void()>&& work) {
- {
- std::unique_lock lock{queue_mutex};
- requests.emplace(work);
- }
- condition.notify_one();
-}
-
-} // namespace Common
diff --git a/src/common/thread_worker.h b/src/common/thread_worker.h
index f1859971f..cd0017726 100644
--- a/src/common/thread_worker.h
+++ b/src/common/thread_worker.h
@@ -5,26 +5,113 @@
#pragma once
#include <atomic>
+#include <condition_variable>
#include <functional>
#include <mutex>
+#include <stop_token>
#include <string>
+#include <thread>
+#include <type_traits>
#include <vector>
#include <queue>
+#include "common/thread.h"
+#include "common/unique_function.h"
+
namespace Common {
-class ThreadWorker final {
+template <class StateType = void>
+class StatefulThreadWorker {
+ static constexpr bool with_state = !std::is_same_v<StateType, void>;
+
+ struct DummyCallable {
+ int operator()() const noexcept {
+ return 0;
+ }
+ };
+
+ using Task =
+ std::conditional_t<with_state, UniqueFunction<void, StateType*>, UniqueFunction<void>>;
+ using StateMaker = std::conditional_t<with_state, std::function<StateType()>, DummyCallable>;
+
public:
- explicit ThreadWorker(std::size_t num_workers, const std::string& name);
- ~ThreadWorker();
- void QueueWork(std::function<void()>&& work);
+ explicit StatefulThreadWorker(size_t num_workers, std::string name, StateMaker func = {})
+ : workers_queued{num_workers}, thread_name{std::move(name)} {
+ const auto lambda = [this, func](std::stop_token stop_token) {
+ Common::SetCurrentThreadName(thread_name.c_str());
+ {
+ [[maybe_unused]] std::conditional_t<with_state, StateType, int> state{func()};
+ while (!stop_token.stop_requested()) {
+ Task task;
+ {
+ std::unique_lock lock{queue_mutex};
+ if (requests.empty()) {
+ wait_condition.notify_all();
+ }
+ condition.wait(lock, stop_token, [this] { return !requests.empty(); });
+ if (stop_token.stop_requested()) {
+ break;
+ }
+ task = std::move(requests.front());
+ requests.pop();
+ }
+ if constexpr (with_state) {
+ task(&state);
+ } else {
+ task();
+ }
+ ++work_done;
+ }
+ }
+ ++workers_stopped;
+ wait_condition.notify_all();
+ };
+ threads.reserve(num_workers);
+ for (size_t i = 0; i < num_workers; ++i) {
+ threads.emplace_back(lambda);
+ }
+ }
+
+ StatefulThreadWorker& operator=(const StatefulThreadWorker&) = delete;
+ StatefulThreadWorker(const StatefulThreadWorker&) = delete;
+
+ StatefulThreadWorker& operator=(StatefulThreadWorker&&) = delete;
+ StatefulThreadWorker(StatefulThreadWorker&&) = delete;
+
+ void QueueWork(Task work) {
+ {
+ std::unique_lock lock{queue_mutex};
+ requests.emplace(std::move(work));
+ ++work_scheduled;
+ }
+ condition.notify_one();
+ }
+
+ void WaitForRequests(std::stop_token stop_token = {}) {
+ std::stop_callback callback(stop_token, [this] {
+ for (auto& thread : threads) {
+ thread.request_stop();
+ }
+ });
+ std::unique_lock lock{queue_mutex};
+ wait_condition.wait(lock, [this] {
+ return workers_stopped >= workers_queued || work_done >= work_scheduled;
+ });
+ }
private:
- std::vector<std::thread> threads;
- std::queue<std::function<void()>> requests;
+ std::queue<Task> requests;
std::mutex queue_mutex;
- std::condition_variable condition;
- std::atomic_bool stop{};
+ std::condition_variable_any condition;
+ std::condition_variable wait_condition;
+ std::atomic<size_t> work_scheduled{};
+ std::atomic<size_t> work_done{};
+ std::atomic<size_t> workers_stopped{};
+ std::atomic<size_t> workers_queued{};
+ std::string thread_name;
+ std::vector<std::jthread> threads;
};
+using ThreadWorker = StatefulThreadWorker<>;
+
} // namespace Common
diff --git a/src/common/unique_function.h b/src/common/unique_function.h
new file mode 100644
index 000000000..ca0559071
--- /dev/null
+++ b/src/common/unique_function.h
@@ -0,0 +1,62 @@
+// Copyright 2021 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <memory>
+#include <utility>
+
+namespace Common {
+
+/// General purpose function wrapper similar to std::function.
+/// Unlike std::function, the captured values don't have to be copyable.
+/// This class can be moved but not copied.
+template <typename ResultType, typename... Args>
+class UniqueFunction {
+ class CallableBase {
+ public:
+ virtual ~CallableBase() = default;
+ virtual ResultType operator()(Args&&...) = 0;
+ };
+
+ template <typename Functor>
+ class Callable final : public CallableBase {
+ public:
+ Callable(Functor&& functor_) : functor{std::move(functor_)} {}
+ ~Callable() override = default;
+
+ ResultType operator()(Args&&... args) override {
+ return functor(std::forward<Args>(args)...);
+ }
+
+ private:
+ Functor functor;
+ };
+
+public:
+ UniqueFunction() = default;
+
+ template <typename Functor>
+ UniqueFunction(Functor&& functor)
+ : callable{std::make_unique<Callable<Functor>>(std::move(functor))} {}
+
+ UniqueFunction& operator=(UniqueFunction&& rhs) noexcept = default;
+ UniqueFunction(UniqueFunction&& rhs) noexcept = default;
+
+ UniqueFunction& operator=(const UniqueFunction&) = delete;
+ UniqueFunction(const UniqueFunction&) = delete;
+
+ ResultType operator()(Args&&... args) const {
+ return (*callable)(std::forward<Args>(args)...);
+ }
+
+ explicit operator bool() const noexcept {
+ return static_cast<bool>(callable);
+ }
+
+private:
+ std::unique_ptr<CallableBase> callable;
+};
+
+} // namespace Common
diff --git a/src/common/uuid.h b/src/common/uuid.h
index 2e7a18405..0ffa37e7c 100644
--- a/src/common/uuid.h
+++ b/src/common/uuid.h
@@ -20,12 +20,11 @@ struct UUID {
constexpr explicit UUID(const u64 lo, const u64 hi) : uuid{{lo, hi}} {}
[[nodiscard]] constexpr explicit operator bool() const {
- return uuid[0] != INVALID_UUID[0] && uuid[1] != INVALID_UUID[1];
+ return uuid != INVALID_UUID;
}
[[nodiscard]] constexpr bool operator==(const UUID& rhs) const {
- // TODO(DarkLordZach): Replace with uuid == rhs.uuid with C++20
- return uuid[0] == rhs.uuid[0] && uuid[1] == rhs.uuid[1];
+ return uuid == rhs.uuid;
}
[[nodiscard]] constexpr bool operator!=(const UUID& rhs) const {