diff options
Diffstat (limited to '')
-rw-r--r-- | src/common/CMakeLists.txt | 70 | ||||
-rw-r--r-- | src/common/common_sizes.h | 43 | ||||
-rw-r--r-- | src/common/detached_tasks.cpp | 2 | ||||
-rw-r--r-- | src/common/fs/file.cpp | 33 | ||||
-rw-r--r-- | src/common/fs/file.h | 21 | ||||
-rw-r--r-- | src/common/fs/fs.cpp | 5 | ||||
-rw-r--r-- | src/common/fs/fs.h | 30 | ||||
-rw-r--r-- | src/common/fs/fs_util.cpp | 4 | ||||
-rw-r--r-- | src/common/fs/fs_util.h | 11 | ||||
-rw-r--r-- | src/common/hex_util.h | 3 | ||||
-rw-r--r-- | src/common/host_memory.cpp | 2 | ||||
-rw-r--r-- | src/common/literals.h | 31 | ||||
-rw-r--r-- | src/common/logging/backend.cpp | 30 | ||||
-rw-r--r-- | src/common/logging/filter.cpp | 4 | ||||
-rw-r--r-- | src/common/logging/types.h | 6 | ||||
-rw-r--r-- | src/common/scm_rev.cpp.in | 2 | ||||
-rw-r--r-- | src/common/settings.cpp | 35 | ||||
-rw-r--r-- | src/common/settings.h | 426 | ||||
-rw-r--r-- | src/common/thread_worker.cpp | 58 | ||||
-rw-r--r-- | src/common/thread_worker.h | 103 | ||||
-rw-r--r-- | src/common/unique_function.h | 62 | ||||
-rw-r--r-- | src/common/uuid.h | 5 |
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 { |