diff options
49 files changed, 1026 insertions, 609 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 826bc42c0..0e064ab44 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -701,7 +701,7 @@ if (APPLE) elseif (WIN32) # WSAPoll and SHGetKnownFolderPath (AppData/Roaming) didn't exist before WinNT 6.x (Vista) add_definitions(-D_WIN32_WINNT=0x0600 -DWINVER=0x0600) - set(PLATFORM_LIBRARIES winmm ws2_32) + set(PLATFORM_LIBRARIES winmm ws2_32 iphlpapi) if (MINGW) # PSAPI is the Process Status API set(PLATFORM_LIBRARIES ${PLATFORM_LIBRARIES} psapi imm32 version) diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index 4b8d35548..0c2c059a9 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -7,7 +7,9 @@ include(DownloadExternals) # xbyak if (ARCHITECTURE_x86 OR ARCHITECTURE_x86_64) add_library(xbyak INTERFACE) - target_include_directories(xbyak SYSTEM INTERFACE ./xbyak/xbyak) + file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/xbyak/include) + file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/xbyak/xbyak DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/xbyak/include) + target_include_directories(xbyak SYSTEM INTERFACE ${CMAKE_CURRENT_BINARY_DIR}/xbyak/include) target_compile_definitions(xbyak INTERFACE XBYAK_NO_OP_NAMES) endif() @@ -19,6 +21,7 @@ target_include_directories(catch-single-include INTERFACE catch/single_include) if (ARCHITECTURE_x86_64) set(DYNARMIC_TESTS OFF) set(DYNARMIC_NO_BUNDLED_FMT ON) + set(DYNARMIC_IGNORE_ASSERTS ON CACHE BOOL "" FORCE) add_subdirectory(dynarmic) endif() diff --git a/externals/dynarmic b/externals/dynarmic -Subproject 7946868af49d403fe54c92d2d60ef986513d1fe +Subproject 517e35f845e010788b6febe42fd6ddb187b8c23 diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp index 61dddab3f..949384fd3 100644 --- a/src/common/logging/backend.cpp +++ b/src/common/logging/backend.cpp @@ -2,13 +2,10 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include <algorithm> #include <atomic> #include <chrono> #include <climits> -#include <condition_variable> -#include <memory> -#include <mutex> +#include <exception> #include <thread> #include <vector> @@ -16,104 +13,229 @@ #include <windows.h> // For OutputDebugStringW #endif -#include "common/assert.h" #include "common/fs/file.h" #include "common/fs/fs.h" +#include "common/fs/fs_paths.h" +#include "common/fs/path_util.h" #include "common/literals.h" #include "common/logging/backend.h" #include "common/logging/log.h" #include "common/logging/text_formatter.h" #include "common/settings.h" +#ifdef _WIN32 #include "common/string_util.h" +#endif #include "common/threadsafe_queue.h" namespace Common::Log { +namespace { + /** - * Static state as a singleton. + * Interface for logging backends. */ -class Impl { +class Backend { public: - static Impl& Instance() { - static Impl backend; - return backend; + virtual ~Backend() = default; + + virtual void Write(const Entry& entry) = 0; + + virtual void EnableForStacktrace() = 0; + + virtual void Flush() = 0; +}; + +/** + * Backend that writes to stderr and with color + */ +class ColorConsoleBackend final : public Backend { +public: + explicit ColorConsoleBackend() = default; + + ~ColorConsoleBackend() override = default; + + void Write(const Entry& entry) override { + if (enabled.load(std::memory_order_relaxed)) { + PrintColoredMessage(entry); + } } - Impl(const Impl&) = delete; - Impl& operator=(const Impl&) = delete; + void Flush() override { + // stderr shouldn't be buffered + } - Impl(Impl&&) = delete; - Impl& operator=(Impl&&) = delete; + void EnableForStacktrace() override { + enabled = true; + } - void PushEntry(Class log_class, Level log_level, const char* filename, unsigned int line_num, - const char* function, std::string message) { - message_queue.Push( - CreateEntry(log_class, log_level, filename, line_num, function, std::move(message))); + void SetEnabled(bool enabled_) { + enabled = enabled_; + } + +private: + std::atomic_bool enabled{false}; +}; + +/** + * Backend that writes to a file passed into the constructor + */ +class FileBackend final : public Backend { +public: + explicit FileBackend(const std::filesystem::path& filename) { + auto old_filename = filename; + old_filename += ".old.txt"; + + // Existence checks are done within the functions themselves. + // We don't particularly care if these succeed or not. + static_cast<void>(FS::RemoveFile(old_filename)); + static_cast<void>(FS::RenameFile(filename, old_filename)); + + file = std::make_unique<FS::IOFile>(filename, FS::FileAccessMode::Write, + FS::FileType::TextFile); + } + + ~FileBackend() override = default; + + void Write(const Entry& entry) override { + if (!enabled) { + return; + } + + bytes_written += file->WriteString(FormatLogMessage(entry).append(1, '\n')); + + using namespace Common::Literals; + // Prevent logs from exceeding a set maximum size in the event that log entries are spammed. + const auto write_limit = Settings::values.extended_logging ? 1_GiB : 100_MiB; + const bool write_limit_exceeded = bytes_written > write_limit; + if (entry.log_level >= Level::Error || write_limit_exceeded) { + if (write_limit_exceeded) { + // Stop writing after the write limit is exceeded. + // Don't close the file so we can print a stacktrace if necessary + enabled = false; + } + file->Flush(); + } + } + + void Flush() override { + file->Flush(); + } + + void EnableForStacktrace() override { + enabled = true; + bytes_written = 0; } - void AddBackend(std::unique_ptr<Backend> backend) { - std::lock_guard lock{writing_mutex}; - backends.push_back(std::move(backend)); +private: + std::unique_ptr<FS::IOFile> file; + bool enabled = true; + std::size_t bytes_written = 0; +}; + +/** + * Backend that writes to Visual Studio's output window + */ +class DebuggerBackend final : public Backend { +public: + explicit DebuggerBackend() = default; + + ~DebuggerBackend() override = default; + + void Write(const Entry& entry) override { +#ifdef _WIN32 + ::OutputDebugStringW(UTF8ToUTF16W(FormatLogMessage(entry).append(1, '\n')).c_str()); +#endif } - void RemoveBackend(std::string_view backend_name) { - std::lock_guard lock{writing_mutex}; + void Flush() override {} + + void EnableForStacktrace() override {} +}; + +bool initialization_in_progress_suppress_logging = true; - std::erase_if(backends, [&backend_name](const auto& backend) { - return backend_name == backend->GetName(); - }); +/** + * Static state as a singleton. + */ +class Impl { +public: + static Impl& Instance() { + if (!instance) { + throw std::runtime_error("Using Logging instance before its initialization"); + } + return *instance; } - const Filter& GetGlobalFilter() const { - return filter; + static void Initialize() { + if (instance) { + LOG_WARNING(Log, "Reinitializing logging backend"); + return; + } + using namespace Common::FS; + const auto& log_dir = GetYuzuPath(YuzuPath::LogDir); + void(CreateDir(log_dir)); + Filter filter; + filter.ParseFilterString(Settings::values.log_filter.GetValue()); + instance = std::unique_ptr<Impl, decltype(&Deleter)>(new Impl(log_dir / LOG_FILE, filter), + Deleter); + initialization_in_progress_suppress_logging = false; } + Impl(const Impl&) = delete; + Impl& operator=(const Impl&) = delete; + + Impl(Impl&&) = delete; + Impl& operator=(Impl&&) = delete; + void SetGlobalFilter(const Filter& f) { filter = f; } - Backend* GetBackend(std::string_view backend_name) { - const auto it = - std::find_if(backends.begin(), backends.end(), - [&backend_name](const auto& i) { return backend_name == i->GetName(); }); - if (it == backends.end()) - return nullptr; - return it->get(); + void SetColorConsoleBackendEnabled(bool enabled) { + color_console_backend.SetEnabled(enabled); + } + + void PushEntry(Class log_class, Level log_level, const char* filename, unsigned int line_num, + const char* function, std::string message) { + if (!filter.CheckMessage(log_class, log_level)) + return; + const Entry& entry = + CreateEntry(log_class, log_level, filename, line_num, function, std::move(message)); + message_queue.Push(entry); } private: - Impl() { - backend_thread = std::thread([&] { - Entry entry; - auto write_logs = [&](Entry& e) { - std::lock_guard lock{writing_mutex}; - for (const auto& backend : backends) { - backend->Write(e); - } - }; - while (true) { - entry = message_queue.PopWait(); - if (entry.final_entry) { - break; - } - write_logs(entry); - } + Impl(const std::filesystem::path& file_backend_filename, const Filter& filter_) + : filter{filter_}, file_backend{file_backend_filename}, backend_thread{std::thread([this] { + Common::SetCurrentThreadName("yuzu:Log"); + Entry entry; + const auto write_logs = [this, &entry]() { + ForEachBackend([&entry](Backend& backend) { backend.Write(entry); }); + }; + while (true) { + entry = message_queue.PopWait(); + if (entry.final_entry) { + break; + } + write_logs(); + } + // Drain the logging queue. Only writes out up to MAX_LOGS_TO_WRITE to prevent a + // case where a system is repeatedly spamming logs even on close. + int max_logs_to_write = filter.IsDebug() ? INT_MAX : 100; + while (max_logs_to_write-- && message_queue.Pop(entry)) { + write_logs(); + } + })} {} - // Drain the logging queue. Only writes out up to MAX_LOGS_TO_WRITE to prevent a - // case where a system is repeatedly spamming logs even on close. - const int MAX_LOGS_TO_WRITE = filter.IsDebug() ? INT_MAX : 100; - int logs_written = 0; - while (logs_written++ < MAX_LOGS_TO_WRITE && message_queue.Pop(entry)) { - write_logs(entry); - } - }); + ~Impl() { + StopBackendThread(); } - ~Impl() { - Entry entry; - entry.final_entry = true; - message_queue.Push(entry); + void StopBackendThread() { + Entry stop_entry{}; + stop_entry.final_entry = true; + message_queue.Push(stop_entry); backend_thread.join(); } @@ -135,100 +257,51 @@ private: }; } - std::mutex writing_mutex; - std::thread backend_thread; - std::vector<std::unique_ptr<Backend>> backends; - MPSCQueue<Entry> message_queue; - Filter filter; - std::chrono::steady_clock::time_point time_origin{std::chrono::steady_clock::now()}; -}; - -ConsoleBackend::~ConsoleBackend() = default; - -void ConsoleBackend::Write(const Entry& entry) { - PrintMessage(entry); -} - -ColorConsoleBackend::~ColorConsoleBackend() = default; - -void ColorConsoleBackend::Write(const Entry& entry) { - PrintColoredMessage(entry); -} - -FileBackend::FileBackend(const std::filesystem::path& filename) { - auto old_filename = filename; - old_filename += ".old.txt"; - - // Existence checks are done within the functions themselves. - // We don't particularly care if these succeed or not. - FS::RemoveFile(old_filename); - void(FS::RenameFile(filename, old_filename)); - - file = - std::make_unique<FS::IOFile>(filename, FS::FileAccessMode::Write, FS::FileType::TextFile); -} - -FileBackend::~FileBackend() = default; + void ForEachBackend(auto lambda) { + lambda(static_cast<Backend&>(debugger_backend)); + lambda(static_cast<Backend&>(color_console_backend)); + lambda(static_cast<Backend&>(file_backend)); + } -void FileBackend::Write(const Entry& entry) { - if (!file->IsOpen()) { - return; + static void Deleter(Impl* ptr) { + delete ptr; } - using namespace Common::Literals; - // Prevent logs from exceeding a set maximum size in the event that log entries are spammed. - constexpr std::size_t MAX_BYTES_WRITTEN = 100_MiB; - constexpr std::size_t MAX_BYTES_WRITTEN_EXTENDED = 1_GiB; + static inline std::unique_ptr<Impl, decltype(&Deleter)> instance{nullptr, Deleter}; - const bool write_limit_exceeded = - bytes_written > MAX_BYTES_WRITTEN_EXTENDED || - (bytes_written > MAX_BYTES_WRITTEN && !Settings::values.extended_logging); + Filter filter; + DebuggerBackend debugger_backend{}; + ColorConsoleBackend color_console_backend{}; + FileBackend file_backend; - // Close the file after the write limit is exceeded. - if (write_limit_exceeded) { - file->Close(); - return; - } + std::thread backend_thread; + MPSCQueue<Entry> message_queue{}; + std::chrono::steady_clock::time_point time_origin{std::chrono::steady_clock::now()}; +}; +} // namespace - bytes_written += file->WriteString(FormatLogMessage(entry).append(1, '\n')); - if (entry.log_level >= Level::Error) { - file->Flush(); - } +void Initialize() { + Impl::Initialize(); } -DebuggerBackend::~DebuggerBackend() = default; - -void DebuggerBackend::Write(const Entry& entry) { -#ifdef _WIN32 - ::OutputDebugStringW(UTF8ToUTF16W(FormatLogMessage(entry).append(1, '\n')).c_str()); -#endif +void DisableLoggingInTests() { + initialization_in_progress_suppress_logging = true; } void SetGlobalFilter(const Filter& filter) { Impl::Instance().SetGlobalFilter(filter); } -void AddBackend(std::unique_ptr<Backend> backend) { - Impl::Instance().AddBackend(std::move(backend)); -} - -void RemoveBackend(std::string_view backend_name) { - Impl::Instance().RemoveBackend(backend_name); -} - -Backend* GetBackend(std::string_view backend_name) { - return Impl::Instance().GetBackend(backend_name); +void SetColorConsoleBackendEnabled(bool enabled) { + Impl::Instance().SetColorConsoleBackendEnabled(enabled); } void FmtLogMessageImpl(Class log_class, Level log_level, const char* filename, unsigned int line_num, const char* function, const char* format, const fmt::format_args& args) { - auto& instance = Impl::Instance(); - const auto& filter = instance.GetGlobalFilter(); - if (!filter.CheckMessage(log_class, log_level)) - return; - - instance.PushEntry(log_class, log_level, filename, line_num, function, - fmt::vformat(format, args)); + if (!initialization_in_progress_suppress_logging) { + Impl::Instance().PushEntry(log_class, log_level, filename, line_num, function, + fmt::vformat(format, args)); + } } } // namespace Common::Log diff --git a/src/common/logging/backend.h b/src/common/logging/backend.h index 4b9a910c1..cb7839ee9 100644 --- a/src/common/logging/backend.h +++ b/src/common/logging/backend.h @@ -5,120 +5,21 @@ #pragma once #include <filesystem> -#include <memory> -#include <string> -#include <string_view> #include "common/logging/filter.h" -#include "common/logging/log.h" - -namespace Common::FS { -class IOFile; -} namespace Common::Log { class Filter; -/** - * Interface for logging backends. As loggers can be created and removed at runtime, this can be - * used by a frontend for adding a custom logging backend as needed - */ -class Backend { -public: - virtual ~Backend() = default; - - virtual void SetFilter(const Filter& new_filter) { - filter = new_filter; - } - virtual const char* GetName() const = 0; - virtual void Write(const Entry& entry) = 0; - -private: - Filter filter; -}; - -/** - * Backend that writes to stderr without any color commands - */ -class ConsoleBackend : public Backend { -public: - ~ConsoleBackend() override; - - static const char* Name() { - return "console"; - } - const char* GetName() const override { - return Name(); - } - void Write(const Entry& entry) override; -}; - -/** - * Backend that writes to stderr and with color - */ -class ColorConsoleBackend : public Backend { -public: - ~ColorConsoleBackend() override; - - static const char* Name() { - return "color_console"; - } - - const char* GetName() const override { - return Name(); - } - void Write(const Entry& entry) override; -}; +/// Initializes the logging system. This should be the first thing called in main. +void Initialize(); -/** - * Backend that writes to a file passed into the constructor - */ -class FileBackend : public Backend { -public: - explicit FileBackend(const std::filesystem::path& filename); - ~FileBackend() override; - - static const char* Name() { - return "file"; - } - - const char* GetName() const override { - return Name(); - } - - void Write(const Entry& entry) override; - -private: - std::unique_ptr<FS::IOFile> file; - std::size_t bytes_written = 0; -}; - -/** - * Backend that writes to Visual Studio's output window - */ -class DebuggerBackend : public Backend { -public: - ~DebuggerBackend() override; - - static const char* Name() { - return "debugger"; - } - const char* GetName() const override { - return Name(); - } - void Write(const Entry& entry) override; -}; - -void AddBackend(std::unique_ptr<Backend> backend); - -void RemoveBackend(std::string_view backend_name); - -Backend* GetBackend(std::string_view backend_name); +void DisableLoggingInTests(); /** - * The global filter will prevent any messages from even being processed if they are filtered. Each - * backend can have a filter, but if the level is lower than the global filter, the backend will - * never get the message + * The global filter will prevent any messages from even being processed if they are filtered. */ void SetGlobalFilter(const Filter& filter); -} // namespace Common::Log
\ No newline at end of file + +void SetColorConsoleBackendEnabled(bool enabled); +} // namespace Common::Log diff --git a/src/common/logging/filter.cpp b/src/common/logging/filter.cpp index f055f0e11..42744c994 100644 --- a/src/common/logging/filter.cpp +++ b/src/common/logging/filter.cpp @@ -111,6 +111,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) { SUB(Service, NCM) \ SUB(Service, NFC) \ SUB(Service, NFP) \ + SUB(Service, NGCT) \ SUB(Service, NIFM) \ SUB(Service, NIM) \ SUB(Service, NPNS) \ diff --git a/src/common/logging/types.h b/src/common/logging/types.h index 7ad0334fc..ddf9d27ca 100644 --- a/src/common/logging/types.h +++ b/src/common/logging/types.h @@ -81,6 +81,7 @@ enum class Class : u8 { Service_NCM, ///< The NCM service Service_NFC, ///< The NFC (Near-field communication) service Service_NFP, ///< The NFP service + Service_NGCT, ///< The NGCT (No Good Content for Terra) service Service_NIFM, ///< The NIFM (Network interface) service Service_NIM, ///< The NIM service Service_NPNS, ///< The NPNS service diff --git a/src/common/settings.h b/src/common/settings.h index 1ba9b606c..20769d310 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -489,7 +489,7 @@ struct Values { std::chrono::seconds custom_rtc_differential; BasicSetting<s32> current_user{0, "current_user"}; - RangedSetting<s32> language_index{1, 0, 16, "language_index"}; + RangedSetting<s32> language_index{1, 0, 17, "language_index"}; RangedSetting<s32> region_index{1, 0, 6, "region_index"}; RangedSetting<s32> time_zone_index{0, 0, 45, "time_zone_index"}; RangedSetting<s32> sound_index{1, 0, 2, "sound_index"}; @@ -558,9 +558,10 @@ struct Values { BasicSetting<std::string> log_filter{"*:Info", "log_filter"}; BasicSetting<bool> use_dev_keys{false, "use_dev_keys"}; - // Services + // Network BasicSetting<std::string> bcat_backend{"none", "bcat_backend"}; BasicSetting<bool> bcat_boxcat_local{false, "bcat_boxcat_local"}; + BasicSetting<std::string> network_interface{std::string(), "network_interface"}; // WebService BasicSetting<bool> enable_telemetry{true, "enable_telemetry"}; diff --git a/src/common/x64/xbyak_abi.h b/src/common/x64/xbyak_abi.h index c2c9b6134..0ddf9b83e 100644 --- a/src/common/x64/xbyak_abi.h +++ b/src/common/x64/xbyak_abi.h @@ -6,7 +6,7 @@ #include <bitset> #include <initializer_list> -#include <xbyak.h> +#include <xbyak/xbyak.h> #include "common/assert.h" namespace Common::X64 { diff --git a/src/common/x64/xbyak_util.h b/src/common/x64/xbyak_util.h index df17f8cbe..44d2558f1 100644 --- a/src/common/x64/xbyak_util.h +++ b/src/common/x64/xbyak_util.h @@ -5,7 +5,7 @@ #pragma once #include <type_traits> -#include <xbyak.h> +#include <xbyak/xbyak.h> #include "common/x64/xbyak_abi.h" namespace Common::X64 { diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 5c99c00f5..87d47e2e5 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -452,6 +452,8 @@ add_library(core STATIC hle/service/nfp/nfp.h hle/service/nfp/nfp_user.cpp hle/service/nfp/nfp_user.h + hle/service/ngct/ngct.cpp + hle/service/ngct/ngct.h hle/service/nifm/nifm.cpp hle/service/nifm/nifm.h hle/service/nim/nim.cpp @@ -636,6 +638,8 @@ add_library(core STATIC memory.h network/network.cpp network/network.h + network/network_interface.cpp + network/network_interface.h network/sockets.h perf_stats.cpp perf_stats.h diff --git a/src/core/core.cpp b/src/core/core.cpp index d3e84c4ef..ba4629993 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -4,6 +4,7 @@ #include <array> #include <atomic> +#include <exception> #include <memory> #include <utility> @@ -84,8 +85,6 @@ FileSys::StorageId GetStorageIdForFrontendSlot( } // Anonymous namespace -/*static*/ System System::s_instance; - FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs, const std::string& path) { // To account for split 00+01+etc files. @@ -425,6 +424,20 @@ struct System::Impl { System::System() : impl{std::make_unique<Impl>(*this)} {} System::~System() = default; +System& System::GetInstance() { + if (!s_instance) { + throw std::runtime_error("Using System instance before its initialization"); + } + return *s_instance; +} + +void System::InitializeGlobalInstance() { + if (s_instance) { + throw std::runtime_error("Reinitializing Global System instance."); + } + s_instance = std::unique_ptr<System>(new System); +} + CpuManager& System::GetCpuManager() { return impl->cpu_manager; } diff --git a/src/core/core.h b/src/core/core.h index ea143043c..715ab88e7 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -120,9 +120,9 @@ public: * Gets the instance of the System singleton class. * @returns Reference to the instance of the System singleton class. */ - [[deprecated("Use of the global system instance is deprecated")]] static System& GetInstance() { - return s_instance; - } + [[deprecated("Use of the global system instance is deprecated")]] static System& GetInstance(); + + static void InitializeGlobalInstance(); /// Enumeration representing the return values of the System Initialize and Load process. enum class ResultStatus : u32 { @@ -396,7 +396,7 @@ private: struct Impl; std::unique_ptr<Impl> impl; - static System s_instance; + inline static std::unique_ptr<System> s_instance{}; }; } // namespace Core diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 92fbc5532..bea945301 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -267,20 +267,23 @@ struct KernelCore::Impl { } } - /// Creates a new host thread ID, should only be called by GetHostThreadId - u32 AllocateHostThreadId(std::optional<std::size_t> core_id) { - if (core_id) { - // The first for slots are reserved for CPU core threads - ASSERT(*core_id < Core::Hardware::NUM_CPU_CORES); - return static_cast<u32>(*core_id); - } else { - return next_host_thread_id++; + static inline thread_local u32 host_thread_id = UINT32_MAX; + + /// Gets the host thread ID for the caller, allocating a new one if this is the first time + u32 GetHostThreadId(std::size_t core_id) { + if (host_thread_id == UINT32_MAX) { + // The first four slots are reserved for CPU core threads + ASSERT(core_id < Core::Hardware::NUM_CPU_CORES); + host_thread_id = static_cast<u32>(core_id); } + return host_thread_id; } /// Gets the host thread ID for the caller, allocating a new one if this is the first time - u32 GetHostThreadId(std::optional<std::size_t> core_id = std::nullopt) { - const thread_local auto host_thread_id{AllocateHostThreadId(core_id)}; + u32 GetHostThreadId() { + if (host_thread_id == UINT32_MAX) { + host_thread_id = next_host_thread_id++; + } return host_thread_id; } diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 2eb532472..62fb06c45 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -1078,8 +1078,8 @@ static ResultCode GetThreadContext(Core::System& system, VAddr out_context, Hand for (auto i = 0; i < static_cast<s32>(Core::Hardware::NUM_CPU_CORES); ++i) { if (thread.GetPointerUnsafe() == kernel.Scheduler(i).GetCurrentThread()) { current = true; + break; } - break; } // If the thread is current, retry until it isn't. diff --git a/src/core/hle/service/am/applets/applet_error.cpp b/src/core/hle/service/am/applets/applet_error.cpp index ef6854d62..36a4aa9cd 100644 --- a/src/core/hle/service/am/applets/applet_error.cpp +++ b/src/core/hle/service/am/applets/applet_error.cpp @@ -16,6 +16,30 @@ namespace Service::AM::Applets { +struct ErrorCode { + u32 error_category{}; + u32 error_number{}; + + static constexpr ErrorCode FromU64(u64 error_code) { + return { + .error_category{static_cast<u32>(error_code >> 32)}, + .error_number{static_cast<u32>(error_code & 0xFFFFFFFF)}, + }; + } + + static constexpr ErrorCode FromResultCode(ResultCode result) { + return { + .error_category{2000 + static_cast<u32>(result.module.Value())}, + .error_number{result.description.Value()}, + }; + } + + constexpr ResultCode ToResultCode() const { + return ResultCode{static_cast<ErrorModule>(error_category - 2000), error_number}; + } +}; +static_assert(sizeof(ErrorCode) == 0x8, "ErrorCode has incorrect size."); + #pragma pack(push, 4) struct ShowError { u8 mode; @@ -76,12 +100,7 @@ void CopyArgumentData(const std::vector<u8>& data, T& variable) { } ResultCode Decode64BitError(u64 error) { - const auto description = (error >> 32) & 0x1FFF; - auto module = error & 0x3FF; - if (module >= 2000) - module -= 2000; - module &= 0x1FF; - return {static_cast<ErrorModule>(module), static_cast<u32>(description)}; + return ErrorCode::FromU64(error).ToResultCode(); } } // Anonymous namespace diff --git a/src/core/hle/service/ngct/ngct.cpp b/src/core/hle/service/ngct/ngct.cpp new file mode 100644 index 000000000..deb3abb28 --- /dev/null +++ b/src/core/hle/service/ngct/ngct.cpp @@ -0,0 +1,46 @@ +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included + +#include "common/string_util.h" +#include "core/core.h" +#include "core/hle/ipc_helpers.h" +#include "core/hle/service/ngct/ngct.h" +#include "core/hle/service/service.h" + +namespace Service::NGCT { + +class IService final : public ServiceFramework<IService> { +public: + explicit IService(Core::System& system_) : ServiceFramework{system_, "ngct:u"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "Match"}, + {1, &IService::Filter, "Filter"}, + }; + // clang-format on + + RegisterHandlers(functions); + } + +private: + void Filter(Kernel::HLERequestContext& ctx) { + const auto buffer = ctx.ReadBuffer(); + const auto text = Common::StringFromFixedZeroTerminatedBuffer( + reinterpret_cast<const char*>(buffer.data()), buffer.size()); + + LOG_WARNING(Service_NGCT, "(STUBBED) called, text={}", text); + + // Return the same string since we don't censor anything + ctx.WriteBuffer(buffer); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + } +}; + +void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) { + std::make_shared<IService>(system)->InstallAsService(system.ServiceManager()); +} + +} // namespace Service::NGCT diff --git a/src/core/hle/service/ngct/ngct.h b/src/core/hle/service/ngct/ngct.h new file mode 100644 index 000000000..1f2a47b78 --- /dev/null +++ b/src/core/hle/service/ngct/ngct.h @@ -0,0 +1,20 @@ +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included + +#pragma once + +namespace Core { +class System; +} + +namespace Service::SM { +class ServiceManager; +} + +namespace Service::NGCT { + +/// Registers all NGCT services with the specified service manager. +void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system); + +} // namespace Service::NGCT diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp index e742db48f..9decb9290 100644 --- a/src/core/hle/service/nifm/nifm.cpp +++ b/src/core/hle/service/nifm/nifm.cpp @@ -11,6 +11,7 @@ #include "core/hle/service/nifm/nifm.h" #include "core/hle/service/service.h" #include "core/network/network.h" +#include "core/network/network_interface.h" namespace Service::NIFM { @@ -179,10 +180,10 @@ private: IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - if (Settings::values.bcat_backend.GetValue() == "none") { - rb.PushEnum(RequestState::NotSubmitted); - } else { + if (Network::GetHostIPv4Address().has_value()) { rb.PushEnum(RequestState::Connected); + } else { + rb.PushEnum(RequestState::NotSubmitted); } } @@ -276,37 +277,45 @@ private: void GetCurrentNetworkProfile(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_NIFM, "(STUBBED) called"); - const SfNetworkProfileData network_profile_data{ - .ip_setting_data{ - .ip_address_setting{ - .is_automatic{true}, - .current_address{192, 168, 1, 100}, - .subnet_mask{255, 255, 255, 0}, - .gateway{192, 168, 1, 1}, - }, - .dns_setting{ - .is_automatic{true}, - .primary_dns{1, 1, 1, 1}, - .secondary_dns{1, 0, 0, 1}, + const auto net_iface = Network::GetSelectedNetworkInterface(); + + const SfNetworkProfileData network_profile_data = [&net_iface] { + if (!net_iface) { + return SfNetworkProfileData{}; + } + + return SfNetworkProfileData{ + .ip_setting_data{ + .ip_address_setting{ + .is_automatic{true}, + .current_address{Network::TranslateIPv4(net_iface->ip_address)}, + .subnet_mask{Network::TranslateIPv4(net_iface->subnet_mask)}, + .gateway{Network::TranslateIPv4(net_iface->gateway)}, + }, + .dns_setting{ + .is_automatic{true}, + .primary_dns{1, 1, 1, 1}, + .secondary_dns{1, 0, 0, 1}, + }, + .proxy_setting{ + .enabled{false}, + .port{}, + .proxy_server{}, + .automatic_auth_enabled{}, + .user{}, + .password{}, + }, + .mtu{1500}, }, - .proxy_setting{ - .enabled{false}, - .port{}, - .proxy_server{}, - .automatic_auth_enabled{}, - .user{}, - .password{}, + .uuid{0xdeadbeef, 0xdeadbeef}, + .network_name{"yuzu Network"}, + .wireless_setting_data{ + .ssid_length{12}, + .ssid{"yuzu Network"}, + .passphrase{"yuzupassword"}, }, - .mtu{1500}, - }, - .uuid{0xdeadbeef, 0xdeadbeef}, - .network_name{"yuzu Network"}, - .wireless_setting_data{ - .ssid_length{12}, - .ssid{"yuzu Network"}, - .passphrase{"yuzupassword"}, - }, - }; + }; + }(); ctx.WriteBuffer(network_profile_data); @@ -322,12 +331,15 @@ private: void GetCurrentIpAddress(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_NIFM, "(STUBBED) called"); - const auto [ipv4, error] = Network::GetHostIPv4Address(); - UNIMPLEMENTED_IF(error != Network::Errno::SUCCESS); + auto ipv4 = Network::GetHostIPv4Address(); + if (!ipv4) { + LOG_ERROR(Service_NIFM, "Couldn't get host IPv4 address, defaulting to 0.0.0.0"); + ipv4.emplace(Network::IPv4Address{0, 0, 0, 0}); + } IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - rb.PushRaw(ipv4); + rb.PushRaw(*ipv4); } void CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_NIFM, "called"); @@ -348,25 +360,33 @@ private: LOG_WARNING(Service_NIFM, "(STUBBED) called"); struct IpConfigInfo { - IpAddressSetting ip_address_setting; - DnsSetting dns_setting; + IpAddressSetting ip_address_setting{}; + DnsSetting dns_setting{}; }; static_assert(sizeof(IpConfigInfo) == sizeof(IpAddressSetting) + sizeof(DnsSetting), "IpConfigInfo has incorrect size."); - const IpConfigInfo ip_config_info{ - .ip_address_setting{ - .is_automatic{true}, - .current_address{192, 168, 1, 100}, - .subnet_mask{255, 255, 255, 0}, - .gateway{192, 168, 1, 1}, - }, - .dns_setting{ - .is_automatic{true}, - .primary_dns{1, 1, 1, 1}, - .secondary_dns{1, 0, 0, 1}, - }, - }; + const auto net_iface = Network::GetSelectedNetworkInterface(); + + const IpConfigInfo ip_config_info = [&net_iface] { + if (!net_iface) { + return IpConfigInfo{}; + } + + return IpConfigInfo{ + .ip_address_setting{ + .is_automatic{true}, + .current_address{Network::TranslateIPv4(net_iface->ip_address)}, + .subnet_mask{Network::TranslateIPv4(net_iface->subnet_mask)}, + .gateway{Network::TranslateIPv4(net_iface->gateway)}, + }, + .dns_setting{ + .is_automatic{true}, + .primary_dns{1, 1, 1, 1}, + .secondary_dns{1, 0, 0, 1}, + }, + }; + }(); IPC::ResponseBuilder rb{ctx, 2 + (sizeof(IpConfigInfo) + 3) / sizeof(u32)}; rb.Push(ResultSuccess); @@ -384,10 +404,10 @@ private: IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - if (Settings::values.bcat_backend.GetValue() == "none") { - rb.Push<u8>(0); - } else { + if (Network::GetHostIPv4Address().has_value()) { rb.Push<u8>(1); + } else { + rb.Push<u8>(0); } } void IsAnyInternetRequestAccepted(Kernel::HLERequestContext& ctx) { @@ -395,10 +415,10 @@ private: IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - if (Settings::values.bcat_backend.GetValue() == "none") { - rb.Push<u8>(0); - } else { + if (Network::GetHostIPv4Address().has_value()) { rb.Push<u8>(1); + } else { + rb.Push<u8>(0); } } }; diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index b3e50433b..065133166 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -46,6 +46,7 @@ #include "core/hle/service/ncm/ncm.h" #include "core/hle/service/nfc/nfc.h" #include "core/hle/service/nfp/nfp.h" +#include "core/hle/service/ngct/ngct.h" #include "core/hle/service/nifm/nifm.h" #include "core/hle/service/nim/nim.h" #include "core/hle/service/npns/npns.h" @@ -271,6 +272,7 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system NCM::InstallInterfaces(*sm, system); NFC::InstallInterfaces(*sm, system); NFP::InstallInterfaces(*sm, system); + NGCT::InstallInterfaces(*sm, system); NIFM::InstallInterfaces(*sm, system); NIM::InstallInterfaces(*sm, system); NPNS::InstallInterfaces(*sm, system); diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp index 3e5949d52..8e8fc40ca 100644 --- a/src/core/hle/service/vi/vi.cpp +++ b/src/core/hle/service/vi/vi.cpp @@ -1158,7 +1158,7 @@ private: const auto layer_id = nv_flinger.CreateLayer(display_id); if (!layer_id) { - LOG_ERROR(Service_VI, "Layer not found! layer_id={}", *layer_id); + LOG_ERROR(Service_VI, "Layer not found! display_id={}", display_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ERR_NOT_FOUND); return; diff --git a/src/core/network/network.cpp b/src/core/network/network.cpp index 375bc79ec..4732d4485 100644 --- a/src/core/network/network.cpp +++ b/src/core/network/network.cpp @@ -10,9 +10,10 @@ #include "common/common_funcs.h" #ifdef _WIN32 -#define _WINSOCK_DEPRECATED_NO_WARNINGS // gethostname #include <winsock2.h> +#include <ws2tcpip.h> #elif YUZU_UNIX +#include <arpa/inet.h> #include <errno.h> #include <fcntl.h> #include <netdb.h> @@ -27,7 +28,9 @@ #include "common/assert.h" #include "common/common_types.h" #include "common/logging/log.h" +#include "common/settings.h" #include "core/network/network.h" +#include "core/network/network_interface.h" #include "core/network/sockets.h" namespace Network { @@ -47,11 +50,6 @@ void Finalize() { WSACleanup(); } -constexpr IPv4Address TranslateIPv4(in_addr addr) { - auto& bytes = addr.S_un.S_un_b; - return IPv4Address{bytes.s_b1, bytes.s_b2, bytes.s_b3, bytes.s_b4}; -} - sockaddr TranslateFromSockAddrIn(SockAddrIn input) { sockaddr_in result; @@ -138,12 +136,6 @@ void Initialize() {} void Finalize() {} -constexpr IPv4Address TranslateIPv4(in_addr addr) { - const u32 bytes = addr.s_addr; - return IPv4Address{static_cast<u8>(bytes), static_cast<u8>(bytes >> 8), - static_cast<u8>(bytes >> 16), static_cast<u8>(bytes >> 24)}; -} - sockaddr TranslateFromSockAddrIn(SockAddrIn input) { sockaddr_in result; @@ -182,7 +174,7 @@ linger MakeLinger(bool enable, u32 linger_value) { } bool EnableNonBlock(int fd, bool enable) { - int flags = fcntl(fd, F_GETFD); + int flags = fcntl(fd, F_GETFL); if (flags == -1) { return false; } @@ -191,7 +183,7 @@ bool EnableNonBlock(int fd, bool enable) { } else { flags &= ~O_NONBLOCK; } - return fcntl(fd, F_SETFD, flags) == 0; + return fcntl(fd, F_SETFL, flags) == 0; } Errno TranslateNativeError(int e) { @@ -227,8 +219,12 @@ Errno GetAndLogLastError() { #else int e = errno; #endif + const Errno err = TranslateNativeError(e); + if (err == Errno::AGAIN) { + return err; + } LOG_ERROR(Network, "Socket operation error: {}", NativeErrorToString(e)); - return TranslateNativeError(e); + return err; } int TranslateDomain(Domain domain) { @@ -353,27 +349,29 @@ NetworkInstance::~NetworkInstance() { Finalize(); } -std::pair<IPv4Address, Errno> GetHostIPv4Address() { - std::array<char, 256> name{}; - if (gethostname(name.data(), static_cast<int>(name.size()) - 1) == SOCKET_ERROR) { - return {IPv4Address{}, GetAndLogLastError()}; +std::optional<IPv4Address> GetHostIPv4Address() { + const std::string& selected_network_interface = Settings::values.network_interface.GetValue(); + const auto network_interfaces = Network::GetAvailableNetworkInterfaces(); + if (network_interfaces.size() == 0) { + LOG_ERROR(Network, "GetAvailableNetworkInterfaces returned no interfaces"); + return {}; } - hostent* const ent = gethostbyname(name.data()); - if (!ent) { - return {IPv4Address{}, GetAndLogLastError()}; - } - if (ent->h_addr_list == nullptr) { - UNIMPLEMENTED_MSG("No addr provided in hostent->h_addr_list"); - return {IPv4Address{}, Errno::SUCCESS}; - } - if (ent->h_length != sizeof(in_addr)) { - UNIMPLEMENTED_MSG("Unexpected size={} in hostent->h_length", ent->h_length); - } + const auto res = + std::ranges::find_if(network_interfaces, [&selected_network_interface](const auto& iface) { + return iface.name == selected_network_interface; + }); + + if (res != network_interfaces.end()) { + char ip_addr[16] = {}; + ASSERT(inet_ntop(AF_INET, &res->ip_address, ip_addr, sizeof(ip_addr)) != nullptr); + LOG_INFO(Network, "IP address: {}", ip_addr); - in_addr addr; - std::memcpy(&addr, ent->h_addr_list[0], sizeof(addr)); - return {TranslateIPv4(addr), Errno::SUCCESS}; + return TranslateIPv4(res->ip_address); + } else { + LOG_ERROR(Network, "Couldn't find selected interface \"{}\"", selected_network_interface); + return {}; + } } std::pair<s32, Errno> Poll(std::vector<PollFD>& pollfds, s32 timeout) { diff --git a/src/core/network/network.h b/src/core/network/network.h index bd30f1899..fdd3e4655 100644 --- a/src/core/network/network.h +++ b/src/core/network/network.h @@ -5,11 +5,18 @@ #pragma once #include <array> +#include <optional> #include <utility> #include "common/common_funcs.h" #include "common/common_types.h" +#ifdef _WIN32 +#include <winsock2.h> +#elif YUZU_UNIX +#include <netinet/in.h> +#endif + namespace Network { class Socket; @@ -92,8 +99,21 @@ public: ~NetworkInstance(); }; +#ifdef _WIN32 +constexpr IPv4Address TranslateIPv4(in_addr addr) { + auto& bytes = addr.S_un.S_un_b; + return IPv4Address{bytes.s_b1, bytes.s_b2, bytes.s_b3, bytes.s_b4}; +} +#elif YUZU_UNIX +constexpr IPv4Address TranslateIPv4(in_addr addr) { + const u32 bytes = addr.s_addr; + return IPv4Address{static_cast<u8>(bytes), static_cast<u8>(bytes >> 8), + static_cast<u8>(bytes >> 16), static_cast<u8>(bytes >> 24)}; +} +#endif + /// @brief Returns host's IPv4 address -/// @return Pair of an array of human ordered IPv4 address (e.g. 192.168.0.1) and an error code -std::pair<IPv4Address, Errno> GetHostIPv4Address(); +/// @return human ordered IPv4 address (e.g. 192.168.0.1) as an array +std::optional<IPv4Address> GetHostIPv4Address(); } // namespace Network diff --git a/src/core/network/network_interface.cpp b/src/core/network/network_interface.cpp new file mode 100644 index 000000000..6811f21b1 --- /dev/null +++ b/src/core/network/network_interface.cpp @@ -0,0 +1,210 @@ +// Copyright 2021 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <algorithm> +#include <fstream> +#include <sstream> +#include <vector> + +#include "common/bit_cast.h" +#include "common/common_types.h" +#include "common/logging/log.h" +#include "common/settings.h" +#include "common/string_util.h" +#include "core/network/network_interface.h" + +#ifdef _WIN32 +#include <iphlpapi.h> +#else +#include <cerrno> +#include <ifaddrs.h> +#include <net/if.h> +#endif + +namespace Network { + +#ifdef _WIN32 + +std::vector<NetworkInterface> GetAvailableNetworkInterfaces() { + std::vector<IP_ADAPTER_ADDRESSES> adapter_addresses; + DWORD ret = ERROR_BUFFER_OVERFLOW; + DWORD buf_size = 0; + + // retry up to 5 times + for (int i = 0; i < 5 && ret == ERROR_BUFFER_OVERFLOW; i++) { + ret = GetAdaptersAddresses( + AF_INET, GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_INCLUDE_GATEWAYS, + nullptr, adapter_addresses.data(), &buf_size); + + if (ret != ERROR_BUFFER_OVERFLOW) { + break; + } + + adapter_addresses.resize((buf_size / sizeof(IP_ADAPTER_ADDRESSES)) + 1); + } + + if (ret != NO_ERROR) { + LOG_ERROR(Network, "Failed to get network interfaces with GetAdaptersAddresses"); + return {}; + } + + std::vector<NetworkInterface> result; + + for (auto current_address = adapter_addresses.data(); current_address != nullptr; + current_address = current_address->Next) { + if (current_address->FirstUnicastAddress == nullptr || + current_address->FirstUnicastAddress->Address.lpSockaddr == nullptr) { + continue; + } + + if (current_address->OperStatus != IfOperStatusUp) { + continue; + } + + const auto ip_addr = Common::BitCast<struct sockaddr_in>( + *current_address->FirstUnicastAddress->Address.lpSockaddr) + .sin_addr; + + ULONG mask = 0; + if (ConvertLengthToIpv4Mask(current_address->FirstUnicastAddress->OnLinkPrefixLength, + &mask) != NO_ERROR) { + LOG_ERROR(Network, "Failed to convert IPv4 prefix length to subnet mask"); + continue; + } + + struct in_addr gateway = {.S_un{.S_addr{0}}}; + if (current_address->FirstGatewayAddress != nullptr && + current_address->FirstGatewayAddress->Address.lpSockaddr != nullptr) { + gateway = Common::BitCast<struct sockaddr_in>( + *current_address->FirstGatewayAddress->Address.lpSockaddr) + .sin_addr; + } + + result.emplace_back(NetworkInterface{ + .name{Common::UTF16ToUTF8(std::wstring{current_address->FriendlyName})}, + .ip_address{ip_addr}, + .subnet_mask = in_addr{.S_un{.S_addr{mask}}}, + .gateway = gateway}); + } + + return result; +} + +#else + +std::vector<NetworkInterface> GetAvailableNetworkInterfaces() { + struct ifaddrs* ifaddr = nullptr; + + if (getifaddrs(&ifaddr) != 0) { + LOG_ERROR(Network, "Failed to get network interfaces with getifaddrs: {}", + std::strerror(errno)); + return {}; + } + + std::vector<NetworkInterface> result; + + for (auto ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) { + if (ifa->ifa_addr == nullptr || ifa->ifa_netmask == nullptr) { + continue; + } + + if (ifa->ifa_addr->sa_family != AF_INET) { + continue; + } + + if ((ifa->ifa_flags & IFF_UP) == 0 || (ifa->ifa_flags & IFF_LOOPBACK) != 0) { + continue; + } + + u32 gateway{}; + + std::ifstream file{"/proc/net/route"}; + if (!file.is_open()) { + LOG_ERROR(Network, "Failed to open \"/proc/net/route\""); + + result.emplace_back(NetworkInterface{ + .name{ifa->ifa_name}, + .ip_address{Common::BitCast<struct sockaddr_in>(*ifa->ifa_addr).sin_addr}, + .subnet_mask{Common::BitCast<struct sockaddr_in>(*ifa->ifa_netmask).sin_addr}, + .gateway{in_addr{.s_addr = gateway}}}); + continue; + } + + // ignore header + file.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); + + bool gateway_found = false; + + for (std::string line; std::getline(file, line);) { + std::istringstream iss{line}; + + std::string iface_name; + iss >> iface_name; + if (iface_name != ifa->ifa_name) { + continue; + } + + iss >> std::hex; + + u32 dest{}; + iss >> dest; + if (dest != 0) { + // not the default route + continue; + } + + iss >> gateway; + + u16 flags{}; + iss >> flags; + + // flag RTF_GATEWAY (defined in <linux/route.h>) + if ((flags & 0x2) == 0) { + continue; + } + + gateway_found = true; + break; + } + + if (!gateway_found) { + gateway = 0; + } + + result.emplace_back(NetworkInterface{ + .name{ifa->ifa_name}, + .ip_address{Common::BitCast<struct sockaddr_in>(*ifa->ifa_addr).sin_addr}, + .subnet_mask{Common::BitCast<struct sockaddr_in>(*ifa->ifa_netmask).sin_addr}, + .gateway{in_addr{.s_addr = gateway}}}); + } + + freeifaddrs(ifaddr); + + return result; +} + +#endif + +std::optional<NetworkInterface> GetSelectedNetworkInterface() { + const auto& selected_network_interface = Settings::values.network_interface.GetValue(); + const auto network_interfaces = Network::GetAvailableNetworkInterfaces(); + if (network_interfaces.size() == 0) { + LOG_ERROR(Network, "GetAvailableNetworkInterfaces returned no interfaces"); + return std::nullopt; + } + + const auto res = + std::ranges::find_if(network_interfaces, [&selected_network_interface](const auto& iface) { + return iface.name == selected_network_interface; + }); + + if (res == network_interfaces.end()) { + LOG_ERROR(Network, "Couldn't find selected interface \"{}\"", selected_network_interface); + return std::nullopt; + } + + return *res; +} + +} // namespace Network diff --git a/src/core/network/network_interface.h b/src/core/network/network_interface.h new file mode 100644 index 000000000..980edb2f5 --- /dev/null +++ b/src/core/network/network_interface.h @@ -0,0 +1,29 @@ +// Copyright 2021 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <optional> +#include <string> +#include <vector> + +#ifdef _WIN32 +#include <winsock2.h> +#else +#include <netinet/in.h> +#endif + +namespace Network { + +struct NetworkInterface { + std::string name; + struct in_addr ip_address; + struct in_addr subnet_mask; + struct in_addr gateway; +}; + +std::vector<NetworkInterface> GetAvailableNetworkInterfaces(); +std::optional<NetworkInterface> GetSelectedNetworkInterface(); + +} // namespace Network diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp index fb8c02a77..14c77f162 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp @@ -298,14 +298,10 @@ Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, Id vertex) { if (IR::IsGeneric(attr)) { const u32 index{IR::GenericAttributeIndex(attr)}; const std::optional<AttrInfo> type{AttrTypes(ctx, index)}; - if (!type) { - // Attribute is disabled + if (!type || !ctx.runtime_info.previous_stage_stores.Generic(index, element)) { + // Attribute is disabled or varying component is not written return ctx.Const(element == 3 ? 1.0f : 0.0f); } - if (!ctx.runtime_info.previous_stage_stores.Generic(index, element)) { - // Varying component is not written - return ctx.Const(type && element == 3 ? 1.0f : 0.0f); - } const Id generic_id{ctx.input_generics.at(index)}; const Id pointer{AttrPointer(ctx, type->pointer, vertex, generic_id, ctx.Const(element))}; const Id value{ctx.OpLoad(type->id, pointer)}; diff --git a/src/tests/common/param_package.cpp b/src/tests/common/param_package.cpp index 4c0f9654f..e31ca3544 100644 --- a/src/tests/common/param_package.cpp +++ b/src/tests/common/param_package.cpp @@ -4,11 +4,13 @@ #include <catch2/catch.hpp> #include <math.h> +#include "common/logging/backend.h" #include "common/param_package.h" namespace Common { TEST_CASE("ParamPackage", "[common]") { + Common::Log::DisableLoggingInTests(); ParamPackage original{ {"abc", "xyz"}, {"def", "42"}, diff --git a/src/video_core/command_classes/codecs/vp9.cpp b/src/video_core/command_classes/codecs/vp9.cpp index 70030066a..d7e749485 100644 --- a/src/video_core/command_classes/codecs/vp9.cpp +++ b/src/video_core/command_classes/codecs/vp9.cpp @@ -742,6 +742,7 @@ VpxBitStreamWriter VP9::ComposeUncompressedHeader() { uncomp_writer.WriteDeltaQ(current_frame_info.uv_dc_delta_q); uncomp_writer.WriteDeltaQ(current_frame_info.uv_ac_delta_q); + ASSERT(!current_frame_info.segment_enabled); uncomp_writer.WriteBit(false); // Segmentation enabled (TODO). const s32 min_tile_cols_log2 = CalcMinLog2TileCols(current_frame_info.frame_size.width); diff --git a/src/video_core/command_classes/codecs/vp9_types.h b/src/video_core/command_classes/codecs/vp9_types.h index 87eafdb03..3b1ed4b3a 100644 --- a/src/video_core/command_classes/codecs/vp9_types.h +++ b/src/video_core/command_classes/codecs/vp9_types.h @@ -22,7 +22,7 @@ struct Vp9FrameDimensions { }; static_assert(sizeof(Vp9FrameDimensions) == 0x8, "Vp9 Vp9FrameDimensions is an invalid size"); -enum FrameFlags : u32 { +enum class FrameFlags : u32 { IsKeyFrame = 1 << 0, LastFrameIsKeyFrame = 1 << 1, FrameSizeChanged = 1 << 2, @@ -30,6 +30,7 @@ enum FrameFlags : u32 { LastShowFrame = 1 << 4, IntraOnly = 1 << 5, }; +DECLARE_ENUM_FLAG_OPERATORS(FrameFlags) enum class TxSize { Tx4x4 = 0, // 4x4 transform @@ -92,44 +93,34 @@ struct Vp9EntropyProbs { static_assert(sizeof(Vp9EntropyProbs) == 0x7B4, "Vp9EntropyProbs is an invalid size"); struct Vp9PictureInfo { - bool is_key_frame; - bool intra_only; - bool last_frame_was_key; - bool frame_size_changed; - bool error_resilient_mode; - bool last_frame_shown; - bool show_frame; + u32 bitstream_size; + std::array<u64, 4> frame_offsets; std::array<s8, 4> ref_frame_sign_bias; s32 base_q_index; s32 y_dc_delta_q; s32 uv_dc_delta_q; s32 uv_ac_delta_q; - bool lossless; s32 transform_mode; - bool allow_high_precision_mv; s32 interp_filter; s32 reference_mode; - s8 comp_fixed_ref; - std::array<s8, 2> comp_var_ref; s32 log2_tile_cols; s32 log2_tile_rows; - bool segment_enabled; - bool segment_map_update; - bool segment_map_temporal_update; - s32 segment_abs_delta; - std::array<u32, 8> segment_feature_enable; - std::array<std::array<s16, 4>, 8> segment_feature_data; - bool mode_ref_delta_enabled; - bool use_prev_in_find_mv_refs; std::array<s8, 4> ref_deltas; std::array<s8, 2> mode_deltas; Vp9EntropyProbs entropy; Vp9FrameDimensions frame_size; u8 first_level; u8 sharpness_level; - u32 bitstream_size; - std::array<u64, 4> frame_offsets; - std::array<bool, 4> refresh_frame; + bool is_key_frame; + bool intra_only; + bool last_frame_was_key; + bool error_resilient_mode; + bool last_frame_shown; + bool show_frame; + bool lossless; + bool allow_high_precision_mv; + bool segment_enabled; + bool mode_ref_delta_enabled; }; struct Vp9FrameContainer { @@ -145,7 +136,7 @@ struct PictureInfo { Vp9FrameDimensions golden_frame_size; ///< 0x50 Vp9FrameDimensions alt_frame_size; ///< 0x58 Vp9FrameDimensions current_frame_size; ///< 0x60 - u32 vp9_flags; ///< 0x68 + FrameFlags vp9_flags; ///< 0x68 std::array<s8, 4> ref_frame_sign_bias; ///< 0x6C u8 first_level; ///< 0x70 u8 sharpness_level; ///< 0x71 @@ -158,60 +149,43 @@ struct PictureInfo { u8 allow_high_precision_mv; ///< 0x78 u8 interp_filter; ///< 0x79 u8 reference_mode; ///< 0x7A - s8 comp_fixed_ref; ///< 0x7B - std::array<s8, 2> comp_var_ref; ///< 0x7C + INSERT_PADDING_BYTES_NOINIT(3); ///< 0x7B u8 log2_tile_cols; ///< 0x7E u8 log2_tile_rows; ///< 0x7F Segmentation segmentation; ///< 0x80 LoopFilter loop_filter; ///< 0xE4 - INSERT_PADDING_BYTES_NOINIT(5); ///< 0xEB - u32 surface_params; ///< 0xF0 - INSERT_PADDING_WORDS_NOINIT(3); ///< 0xF4 + INSERT_PADDING_BYTES_NOINIT(21); ///< 0xEB [[nodiscard]] Vp9PictureInfo Convert() const { return { - .is_key_frame = (vp9_flags & FrameFlags::IsKeyFrame) != 0, - .intra_only = (vp9_flags & FrameFlags::IntraOnly) != 0, - .last_frame_was_key = (vp9_flags & FrameFlags::LastFrameIsKeyFrame) != 0, - .frame_size_changed = (vp9_flags & FrameFlags::FrameSizeChanged) != 0, - .error_resilient_mode = (vp9_flags & FrameFlags::ErrorResilientMode) != 0, - .last_frame_shown = (vp9_flags & FrameFlags::LastShowFrame) != 0, - .show_frame = true, + .bitstream_size = bitstream_size, + .frame_offsets{}, .ref_frame_sign_bias = ref_frame_sign_bias, .base_q_index = base_q_index, .y_dc_delta_q = y_dc_delta_q, .uv_dc_delta_q = uv_dc_delta_q, .uv_ac_delta_q = uv_ac_delta_q, - .lossless = lossless != 0, .transform_mode = tx_mode, - .allow_high_precision_mv = allow_high_precision_mv != 0, .interp_filter = interp_filter, .reference_mode = reference_mode, - .comp_fixed_ref = comp_fixed_ref, - .comp_var_ref = comp_var_ref, .log2_tile_cols = log2_tile_cols, .log2_tile_rows = log2_tile_rows, - .segment_enabled = segmentation.enabled != 0, - .segment_map_update = segmentation.update_map != 0, - .segment_map_temporal_update = segmentation.temporal_update != 0, - .segment_abs_delta = segmentation.abs_delta, - .segment_feature_enable = segmentation.feature_mask, - .segment_feature_data = segmentation.feature_data, - .mode_ref_delta_enabled = loop_filter.mode_ref_delta_enabled != 0, - .use_prev_in_find_mv_refs = !(vp9_flags == (FrameFlags::ErrorResilientMode)) && - !(vp9_flags == (FrameFlags::FrameSizeChanged)) && - !(vp9_flags == (FrameFlags::IntraOnly)) && - (vp9_flags == (FrameFlags::LastShowFrame)) && - !(vp9_flags == (FrameFlags::LastFrameIsKeyFrame)), .ref_deltas = loop_filter.ref_deltas, .mode_deltas = loop_filter.mode_deltas, .entropy{}, .frame_size = current_frame_size, .first_level = first_level, .sharpness_level = sharpness_level, - .bitstream_size = bitstream_size, - .frame_offsets{}, - .refresh_frame{}, + .is_key_frame = True(vp9_flags & FrameFlags::IsKeyFrame), + .intra_only = True(vp9_flags & FrameFlags::IntraOnly), + .last_frame_was_key = True(vp9_flags & FrameFlags::LastFrameIsKeyFrame), + .error_resilient_mode = True(vp9_flags & FrameFlags::ErrorResilientMode), + .last_frame_shown = True(vp9_flags & FrameFlags::LastShowFrame), + .show_frame = true, + .lossless = lossless != 0, + .allow_high_precision_mv = allow_high_precision_mv != 0, + .segment_enabled = segmentation.enabled != 0, + .mode_ref_delta_enabled = loop_filter.mode_ref_delta_enabled != 0, }; } }; @@ -316,7 +290,6 @@ ASSERT_POSITION(last_frame_size, 0x48); ASSERT_POSITION(first_level, 0x70); ASSERT_POSITION(segmentation, 0x80); ASSERT_POSITION(loop_filter, 0xE4); -ASSERT_POSITION(surface_params, 0xF0); #undef ASSERT_POSITION #define ASSERT_POSITION(field_name, position) \ diff --git a/src/video_core/macro/macro_jit_x64.h b/src/video_core/macro/macro_jit_x64.h index 7f50ac2f8..d03d480b4 100644 --- a/src/video_core/macro/macro_jit_x64.h +++ b/src/video_core/macro/macro_jit_x64.h @@ -6,7 +6,7 @@ #include <array> #include <bitset> -#include <xbyak.h> +#include <xbyak/xbyak.h> #include "common/bit_field.h" #include "common/common_types.h" #include "common/x64/xbyak_abi.h" diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp index 882eff880..c60ed6453 100644 --- a/src/video_core/memory_manager.cpp +++ b/src/video_core/memory_manager.cpp @@ -463,6 +463,7 @@ std::vector<std::pair<GPUVAddr, std::size_t>> MemoryManager::GetSubmappedRange( ++page_index; page_offset = 0; remaining_size -= num_bytes; + old_page_addr = page_addr; } split(); return result; diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index a37ca1fdf..f316c4f92 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -281,7 +281,7 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, Tegra::Engines::Maxw .supported_spirv = device.IsKhrSpirv1_4Supported() ? 0x00010400U : 0x00010000U, .unified_descriptor_binding = true, .support_descriptor_aliasing = true, - .support_int8 = true, + .support_int8 = device.IsInt8Supported(), .support_int16 = device.IsShaderInt16Supported(), .support_int64 = device.IsShaderInt64Supported(), .support_vertex_instance_id = false, diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp index d2c4a7fcf..24e943e4c 100644 --- a/src/video_core/textures/decoders.cpp +++ b/src/video_core/textures/decoders.cpp @@ -76,56 +76,31 @@ template <bool TO_LINEAR> void Swizzle(std::span<u8> output, std::span<const u8> input, u32 bytes_per_pixel, u32 width, u32 height, u32 depth, u32 block_height, u32 block_depth, u32 stride_alignment) { switch (bytes_per_pixel) { - case 1: - return SwizzleImpl<TO_LINEAR, 1>(output, input, width, height, depth, block_height, +#define BPP_CASE(x) \ + case x: \ + return SwizzleImpl<TO_LINEAR, x>(output, input, width, height, depth, block_height, \ block_depth, stride_alignment); - case 2: - return SwizzleImpl<TO_LINEAR, 2>(output, input, width, height, depth, block_height, - block_depth, stride_alignment); - case 3: - return SwizzleImpl<TO_LINEAR, 3>(output, input, width, height, depth, block_height, - block_depth, stride_alignment); - case 4: - return SwizzleImpl<TO_LINEAR, 4>(output, input, width, height, depth, block_height, - block_depth, stride_alignment); - case 6: - return SwizzleImpl<TO_LINEAR, 6>(output, input, width, height, depth, block_height, - block_depth, stride_alignment); - case 8: - return SwizzleImpl<TO_LINEAR, 8>(output, input, width, height, depth, block_height, - block_depth, stride_alignment); - case 12: - return SwizzleImpl<TO_LINEAR, 12>(output, input, width, height, depth, block_height, - block_depth, stride_alignment); - case 16: - return SwizzleImpl<TO_LINEAR, 16>(output, input, width, height, depth, block_height, - block_depth, stride_alignment); + BPP_CASE(1) + BPP_CASE(2) + BPP_CASE(3) + BPP_CASE(4) + BPP_CASE(6) + BPP_CASE(8) + BPP_CASE(12) + BPP_CASE(16) +#undef BPP_CASE default: UNREACHABLE_MSG("Invalid bytes_per_pixel={}", bytes_per_pixel); } } -} // Anonymous namespace - -void UnswizzleTexture(std::span<u8> output, std::span<const u8> input, u32 bytes_per_pixel, - u32 width, u32 height, u32 depth, u32 block_height, u32 block_depth, - u32 stride_alignment) { - Swizzle<false>(output, input, bytes_per_pixel, width, height, depth, block_height, block_depth, - stride_alignment); -} - -void SwizzleTexture(std::span<u8> output, std::span<const u8> input, u32 bytes_per_pixel, u32 width, - u32 height, u32 depth, u32 block_height, u32 block_depth, - u32 stride_alignment) { - Swizzle<true>(output, input, bytes_per_pixel, width, height, depth, block_height, block_depth, - stride_alignment); -} +template <u32 BYTES_PER_PIXEL> void SwizzleSubrect(u32 subrect_width, u32 subrect_height, u32 source_pitch, u32 swizzled_width, - u32 bytes_per_pixel, u8* swizzled_data, const u8* unswizzled_data, - u32 block_height_bit, u32 offset_x, u32 offset_y) { + u8* swizzled_data, const u8* unswizzled_data, u32 block_height_bit, + u32 offset_x, u32 offset_y) { const u32 block_height = 1U << block_height_bit; const u32 image_width_in_gobs = - (swizzled_width * bytes_per_pixel + (GOB_SIZE_X - 1)) / GOB_SIZE_X; + (swizzled_width * BYTES_PER_PIXEL + (GOB_SIZE_X - 1)) / GOB_SIZE_X; for (u32 line = 0; line < subrect_height; ++line) { const u32 dst_y = line + offset_y; const u32 gob_address_y = @@ -135,20 +110,21 @@ void SwizzleSubrect(u32 subrect_width, u32 subrect_height, u32 source_pitch, u32 for (u32 x = 0; x < subrect_width; ++x) { const u32 dst_x = x + offset_x; const u32 gob_address = - gob_address_y + (dst_x * bytes_per_pixel / GOB_SIZE_X) * GOB_SIZE * block_height; - const u32 swizzled_offset = gob_address + table[(dst_x * bytes_per_pixel) % GOB_SIZE_X]; - const u32 unswizzled_offset = line * source_pitch + x * bytes_per_pixel; + gob_address_y + (dst_x * BYTES_PER_PIXEL / GOB_SIZE_X) * GOB_SIZE * block_height; + const u32 swizzled_offset = gob_address + table[(dst_x * BYTES_PER_PIXEL) % GOB_SIZE_X]; + const u32 unswizzled_offset = line * source_pitch + x * BYTES_PER_PIXEL; const u8* const source_line = unswizzled_data + unswizzled_offset; u8* const dest_addr = swizzled_data + swizzled_offset; - std::memcpy(dest_addr, source_line, bytes_per_pixel); + std::memcpy(dest_addr, source_line, BYTES_PER_PIXEL); } } } -void UnswizzleSubrect(u32 line_length_in, u32 line_count, u32 pitch, u32 width, u32 bytes_per_pixel, - u32 block_height, u32 origin_x, u32 origin_y, u8* output, const u8* input) { - const u32 stride = width * bytes_per_pixel; +template <u32 BYTES_PER_PIXEL> +void UnswizzleSubrect(u32 line_length_in, u32 line_count, u32 pitch, u32 width, u32 block_height, + u32 origin_x, u32 origin_y, u8* output, const u8* input) { + const u32 stride = width * BYTES_PER_PIXEL; const u32 gobs_in_x = (stride + GOB_SIZE_X - 1) / GOB_SIZE_X; const u32 block_size = gobs_in_x << (GOB_SIZE_SHIFT + block_height); @@ -163,24 +139,25 @@ void UnswizzleSubrect(u32 line_length_in, u32 line_count, u32 pitch, u32 width, const u32 src_offset_y = (block_y >> block_height) * block_size + ((block_y & block_height_mask) << GOB_SIZE_SHIFT); for (u32 column = 0; column < line_length_in; ++column) { - const u32 src_x = (column + origin_x) * bytes_per_pixel; + const u32 src_x = (column + origin_x) * BYTES_PER_PIXEL; const u32 src_offset_x = (src_x >> GOB_SIZE_X_SHIFT) << x_shift; const u32 swizzled_offset = src_offset_y + src_offset_x + table[src_x % GOB_SIZE_X]; - const u32 unswizzled_offset = line * pitch + column * bytes_per_pixel; + const u32 unswizzled_offset = line * pitch + column * BYTES_PER_PIXEL; - std::memcpy(output + unswizzled_offset, input + swizzled_offset, bytes_per_pixel); + std::memcpy(output + unswizzled_offset, input + swizzled_offset, BYTES_PER_PIXEL); } } } +template <u32 BYTES_PER_PIXEL> void SwizzleSliceToVoxel(u32 line_length_in, u32 line_count, u32 pitch, u32 width, u32 height, - u32 bytes_per_pixel, u32 block_height, u32 block_depth, u32 origin_x, - u32 origin_y, u8* output, const u8* input) { + u32 block_height, u32 block_depth, u32 origin_x, u32 origin_y, u8* output, + const u8* input) { UNIMPLEMENTED_IF(origin_x > 0); UNIMPLEMENTED_IF(origin_y > 0); - const u32 stride = width * bytes_per_pixel; + const u32 stride = width * BYTES_PER_PIXEL; const u32 gobs_in_x = (stride + GOB_SIZE_X - 1) / GOB_SIZE_X; const u32 block_size = gobs_in_x << (GOB_SIZE_SHIFT + block_height + block_depth); @@ -195,11 +172,93 @@ void SwizzleSliceToVoxel(u32 line_length_in, u32 line_count, u32 pitch, u32 widt for (u32 x = 0; x < line_length_in; ++x) { const u32 dst_offset = ((x / GOB_SIZE_X) << x_shift) + dst_offset_y + table[x % GOB_SIZE_X]; - const u32 src_offset = x * bytes_per_pixel + line * pitch; - std::memcpy(output + dst_offset, input + src_offset, bytes_per_pixel); + const u32 src_offset = x * BYTES_PER_PIXEL + line * pitch; + std::memcpy(output + dst_offset, input + src_offset, BYTES_PER_PIXEL); } } } +} // Anonymous namespace + +void UnswizzleTexture(std::span<u8> output, std::span<const u8> input, u32 bytes_per_pixel, + u32 width, u32 height, u32 depth, u32 block_height, u32 block_depth, + u32 stride_alignment) { + Swizzle<false>(output, input, bytes_per_pixel, width, height, depth, block_height, block_depth, + stride_alignment); +} + +void SwizzleTexture(std::span<u8> output, std::span<const u8> input, u32 bytes_per_pixel, u32 width, + u32 height, u32 depth, u32 block_height, u32 block_depth, + u32 stride_alignment) { + Swizzle<true>(output, input, bytes_per_pixel, width, height, depth, block_height, block_depth, + stride_alignment); +} + +void SwizzleSubrect(u32 subrect_width, u32 subrect_height, u32 source_pitch, u32 swizzled_width, + u32 bytes_per_pixel, u8* swizzled_data, const u8* unswizzled_data, + u32 block_height_bit, u32 offset_x, u32 offset_y) { + switch (bytes_per_pixel) { +#define BPP_CASE(x) \ + case x: \ + return SwizzleSubrect<x>(subrect_width, subrect_height, source_pitch, swizzled_width, \ + swizzled_data, unswizzled_data, block_height_bit, offset_x, \ + offset_y); + BPP_CASE(1) + BPP_CASE(2) + BPP_CASE(3) + BPP_CASE(4) + BPP_CASE(6) + BPP_CASE(8) + BPP_CASE(12) + BPP_CASE(16) +#undef BPP_CASE + default: + UNREACHABLE_MSG("Invalid bytes_per_pixel={}", bytes_per_pixel); + } +} + +void UnswizzleSubrect(u32 line_length_in, u32 line_count, u32 pitch, u32 width, u32 bytes_per_pixel, + u32 block_height, u32 origin_x, u32 origin_y, u8* output, const u8* input) { + switch (bytes_per_pixel) { +#define BPP_CASE(x) \ + case x: \ + return UnswizzleSubrect<x>(line_length_in, line_count, pitch, width, block_height, \ + origin_x, origin_y, output, input); + BPP_CASE(1) + BPP_CASE(2) + BPP_CASE(3) + BPP_CASE(4) + BPP_CASE(6) + BPP_CASE(8) + BPP_CASE(12) + BPP_CASE(16) +#undef BPP_CASE + default: + UNREACHABLE_MSG("Invalid bytes_per_pixel={}", bytes_per_pixel); + } +} + +void SwizzleSliceToVoxel(u32 line_length_in, u32 line_count, u32 pitch, u32 width, u32 height, + u32 bytes_per_pixel, u32 block_height, u32 block_depth, u32 origin_x, + u32 origin_y, u8* output, const u8* input) { + switch (bytes_per_pixel) { +#define BPP_CASE(x) \ + case x: \ + return SwizzleSliceToVoxel<x>(line_length_in, line_count, pitch, width, height, \ + block_height, block_depth, origin_x, origin_y, output, \ + input); + BPP_CASE(1) + BPP_CASE(2) + BPP_CASE(3) + BPP_CASE(4) + BPP_CASE(6) + BPP_CASE(8) + BPP_CASE(12) + BPP_CASE(16) +#undef BPP_CASE + default: + UNREACHABLE_MSG("Invalid bytes_per_pixel={}", bytes_per_pixel); + } +} void SwizzleKepler(const u32 width, const u32 height, const u32 dst_x, const u32 dst_y, const u32 block_height_bit, const std::size_t copy_size, const u8* source_data, @@ -220,7 +279,7 @@ void SwizzleKepler(const u32 width, const u32 height, const u32 dst_x, const u32 u8* dest_addr = swizzle_data + swizzled_offset; count++; - std::memcpy(dest_addr, source_line, 1); + *dest_addr = *source_line; } } } diff --git a/src/video_core/textures/texture.h b/src/video_core/textures/texture.h index 1a9399455..7994cb859 100644 --- a/src/video_core/textures/texture.h +++ b/src/video_core/textures/texture.h @@ -159,7 +159,7 @@ static_assert(sizeof(TextureHandle) == 4, "TextureHandle has wrong size"); return {raw, raw}; } else { const Tegra::Texture::TextureHandle handle{raw}; - return {handle.tic_id, via_header_index ? handle.tic_id : handle.tsc_id}; + return {handle.tic_id, handle.tsc_id}; } } diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index 8e56a89e1..86ca4be54 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -368,18 +368,21 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR }; SetNext(next, demote); - VkPhysicalDeviceFloat16Int8FeaturesKHR float16_int8; - if (is_float16_supported) { - float16_int8 = { + if (is_int8_supported || is_float16_supported) { + VkPhysicalDeviceFloat16Int8FeaturesKHR float16_int8{ .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FLOAT16_INT8_FEATURES_KHR, .pNext = nullptr, - .shaderFloat16 = true, - .shaderInt8 = false, + .shaderFloat16 = is_float16_supported, + .shaderInt8 = is_int8_supported, }; SetNext(next, float16_int8); - } else { + } + if (!is_float16_supported) { LOG_INFO(Render_Vulkan, "Device doesn't support float16 natively"); } + if (!is_int8_supported) { + LOG_INFO(Render_Vulkan, "Device doesn't support int8 natively"); + } if (!nv_viewport_swizzle) { LOG_INFO(Render_Vulkan, "Device doesn't support viewport swizzles"); @@ -909,6 +912,7 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) { physical.GetFeatures2KHR(features); is_float16_supported = float16_int8_features.shaderFloat16; + is_int8_supported = float16_int8_features.shaderInt8; extensions.push_back(VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME); } if (has_ext_subgroup_size_control) { diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index c19f40746..234d74129 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h @@ -139,11 +139,16 @@ public: return is_optimal_astc_supported; } - /// Returns true if the device supports float16 natively + /// Returns true if the device supports float16 natively. bool IsFloat16Supported() const { return is_float16_supported; } + /// Returns true if the device supports int8 natively. + bool IsInt8Supported() const { + return is_int8_supported; + } + /// Returns true if the device warp size can potentially be bigger than guest's warp size. bool IsWarpSizePotentiallyBiggerThanGuest() const { return is_warp_potentially_bigger; @@ -367,7 +372,8 @@ private: u64 device_access_memory{}; ///< Total size of device local memory in bytes. u32 max_push_descriptors{}; ///< Maximum number of push descriptors bool is_optimal_astc_supported{}; ///< Support for native ASTC. - bool is_float16_supported{}; ///< Support for float16 arithmetics. + bool is_float16_supported{}; ///< Support for float16 arithmetic. + bool is_int8_supported{}; ///< Support for int8 arithmetic. bool is_warp_potentially_bigger{}; ///< Host warp size can be bigger than guest. bool is_formatless_image_load_supported{}; ///< Support for shader image read without format. bool is_depth_bounds_supported{}; ///< Support for depth bounds. diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index cb4bdcc7e..19ba0dbba 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt @@ -102,9 +102,9 @@ add_executable(yuzu configuration/configure_profile_manager.cpp configuration/configure_profile_manager.h configuration/configure_profile_manager.ui - configuration/configure_service.cpp - configuration/configure_service.h - configuration/configure_service.ui + configuration/configure_network.cpp + configuration/configure_network.h + configuration/configure_network.ui configuration/configure_system.cpp configuration/configure_system.h configuration/configure_system.ui @@ -182,7 +182,14 @@ if (ENABLE_QT_TRANSLATION) # Update source TS file if enabled if (GENERATE_QT_TRANSLATION) get_target_property(SRCS yuzu SOURCES) - qt5_create_translation(QM_FILES ${SRCS} ${UIS} ${YUZU_QT_LANGUAGES}/en.ts) + qt5_create_translation(QM_FILES + ${SRCS} + ${UIS} + ${YUZU_QT_LANGUAGES}/en.ts + OPTIONS + -source-language en_US + -target-language en_US + ) add_custom_target(translation ALL DEPENDS ${YUZU_QT_LANGUAGES}/en.ts) endif() diff --git a/src/yuzu/applets/qt_software_keyboard.cpp b/src/yuzu/applets/qt_software_keyboard.cpp index 848801cec..8fc0c5a36 100644 --- a/src/yuzu/applets/qt_software_keyboard.cpp +++ b/src/yuzu/applets/qt_software_keyboard.cpp @@ -438,7 +438,7 @@ void QtSoftwareKeyboardDialog::ShowInlineKeyboard( initialize_parameters.key_disable_flags = appear_parameters.key_disable_flags; initialize_parameters.enable_backspace_button = appear_parameters.enable_backspace_button; initialize_parameters.enable_return_button = appear_parameters.enable_return_button; - initialize_parameters.disable_cancel_button = initialize_parameters.disable_cancel_button; + initialize_parameters.disable_cancel_button = appear_parameters.disable_cancel_button; SetKeyboardType(); SetControllerImage(); diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 380379eb4..377795326 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -692,6 +692,7 @@ void Config::ReadServiceValues() { qt_config->beginGroup(QStringLiteral("Services")); ReadBasicSetting(Settings::values.bcat_backend); ReadBasicSetting(Settings::values.bcat_boxcat_local); + ReadBasicSetting(Settings::values.network_interface); qt_config->endGroup(); } @@ -1144,7 +1145,7 @@ void Config::SaveValues() { SaveDataStorageValues(); SaveDebuggingValues(); SaveDisabledAddOnValues(); - SaveServiceValues(); + SaveNetworkValues(); SaveUIValues(); SaveWebServiceValues(); SaveMiscellaneousValues(); @@ -1238,11 +1239,12 @@ void Config::SaveDebuggingValues() { qt_config->endGroup(); } -void Config::SaveServiceValues() { +void Config::SaveNetworkValues() { qt_config->beginGroup(QStringLiteral("Services")); WriteBasicSetting(Settings::values.bcat_backend); WriteBasicSetting(Settings::values.bcat_boxcat_local); + WriteBasicSetting(Settings::values.network_interface); qt_config->endGroup(); } diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h index c1d7feb9f..9555f4498 100644 --- a/src/yuzu/configuration/config.h +++ b/src/yuzu/configuration/config.h @@ -88,7 +88,7 @@ private: void SaveCoreValues(); void SaveDataStorageValues(); void SaveDebuggingValues(); - void SaveServiceValues(); + void SaveNetworkValues(); void SaveDisabledAddOnValues(); void SaveMiscellaneousValues(); void SavePathValues(); diff --git a/src/yuzu/configuration/configure.ui b/src/yuzu/configuration/configure.ui index fca9aed5f..6258dcf20 100644 --- a/src/yuzu/configuration/configure.ui +++ b/src/yuzu/configuration/configure.ui @@ -147,12 +147,12 @@ <string>Web</string> </attribute> </widget> - <widget class="ConfigureService" name="serviceTab"> + <widget class="ConfigureNetwork" name="networkTab"> <property name="accessibleName"> - <string>Services</string> + <string>Network</string> </property> <attribute name="title"> - <string>Services</string> + <string>Network</string> </attribute> </widget> </widget> @@ -242,9 +242,9 @@ <container>1</container> </customwidget> <customwidget> - <class>ConfigureService</class> + <class>ConfigureNetwork</class> <extends>QWidget</extends> - <header>configuration/configure_service.h</header> + <header>configuration/configure_network.h</header> <container>1</container> </customwidget> <customwidget> diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp index bc009b6b3..fe4186157 100644 --- a/src/yuzu/configuration/configure_dialog.cpp +++ b/src/yuzu/configuration/configure_dialog.cpp @@ -67,7 +67,7 @@ void ConfigureDialog::ApplyConfiguration() { ui->audioTab->ApplyConfiguration(); ui->debugTab->ApplyConfiguration(); ui->webTab->ApplyConfiguration(); - ui->serviceTab->ApplyConfiguration(); + ui->networkTab->ApplyConfiguration(); Core::System::GetInstance().ApplySettings(); Settings::LogSettings(); } @@ -103,7 +103,7 @@ Q_DECLARE_METATYPE(QList<QWidget*>); void ConfigureDialog::PopulateSelectionList() { const std::array<std::pair<QString, QList<QWidget*>>, 6> items{ {{tr("General"), {ui->generalTab, ui->hotkeysTab, ui->uiTab, ui->webTab, ui->debugTab}}, - {tr("System"), {ui->systemTab, ui->profileManagerTab, ui->serviceTab, ui->filesystemTab}}, + {tr("System"), {ui->systemTab, ui->profileManagerTab, ui->networkTab, ui->filesystemTab}}, {tr("CPU"), {ui->cpuTab}}, {tr("Graphics"), {ui->graphicsTab, ui->graphicsAdvancedTab}}, {tr("Audio"), {ui->audioTab}}, diff --git a/src/yuzu/configuration/configure_input_player_widget.cpp b/src/yuzu/configuration/configure_input_player_widget.cpp index cd633e45f..9c890ed5d 100644 --- a/src/yuzu/configuration/configure_input_player_widget.cpp +++ b/src/yuzu/configuration/configure_input_player_widget.cpp @@ -647,18 +647,18 @@ void PlayerControlPreview::DrawHandheldController(QPainter& p, const QPointF cen // Face buttons p.setPen(colors.outline); button_color = colors.button; - DrawCircleButton(p, face_center + QPoint(face_distance, 0), button_values[A], face_radius); - DrawCircleButton(p, face_center + QPoint(0, face_distance), button_values[B], face_radius); - DrawCircleButton(p, face_center + QPoint(0, -face_distance), button_values[X], face_radius); - DrawCircleButton(p, face_center + QPoint(-face_distance, 0), button_values[Y], face_radius); + DrawCircleButton(p, face_center + QPointF(face_distance, 0), button_values[A], face_radius); + DrawCircleButton(p, face_center + QPointF(0, face_distance), button_values[B], face_radius); + DrawCircleButton(p, face_center + QPointF(0, -face_distance), button_values[X], face_radius); + DrawCircleButton(p, face_center + QPointF(-face_distance, 0), button_values[Y], face_radius); // Face buttons text p.setPen(colors.transparent); p.setBrush(colors.font); - DrawSymbol(p, face_center + QPoint(face_distance, 0), Symbol::A, text_size); - DrawSymbol(p, face_center + QPoint(0, face_distance), Symbol::B, text_size); - DrawSymbol(p, face_center + QPoint(0, -face_distance), Symbol::X, text_size); - DrawSymbol(p, face_center + QPoint(-face_distance, 1), Symbol::Y, text_size); + DrawSymbol(p, face_center + QPointF(face_distance, 0), Symbol::A, text_size); + DrawSymbol(p, face_center + QPointF(0, face_distance), Symbol::B, text_size); + DrawSymbol(p, face_center + QPointF(0, -face_distance), Symbol::X, text_size); + DrawSymbol(p, face_center + QPointF(-face_distance, 1), Symbol::Y, text_size); // D-pad constants const QPointF dpad_center = center + QPoint(-171, 8); @@ -669,18 +669,20 @@ void PlayerControlPreview::DrawHandheldController(QPainter& p, const QPointF cen // D-pad buttons p.setPen(colors.outline); button_color = colors.button; - DrawCircleButton(p, dpad_center + QPoint(dpad_distance, 0), button_values[DRight], dpad_radius); - DrawCircleButton(p, dpad_center + QPoint(0, dpad_distance), button_values[DDown], dpad_radius); - DrawCircleButton(p, dpad_center + QPoint(0, -dpad_distance), button_values[DUp], dpad_radius); - DrawCircleButton(p, dpad_center + QPoint(-dpad_distance, 0), button_values[DLeft], dpad_radius); + DrawCircleButton(p, dpad_center + QPointF(dpad_distance, 0), button_values[DRight], + dpad_radius); + DrawCircleButton(p, dpad_center + QPointF(0, dpad_distance), button_values[DDown], dpad_radius); + DrawCircleButton(p, dpad_center + QPointF(0, -dpad_distance), button_values[DUp], dpad_radius); + DrawCircleButton(p, dpad_center + QPointF(-dpad_distance, 0), button_values[DLeft], + dpad_radius); // D-pad arrows p.setPen(colors.font2); p.setBrush(colors.font2); - DrawArrow(p, dpad_center + QPoint(dpad_distance, 0), Direction::Right, dpad_arrow_size); - DrawArrow(p, dpad_center + QPoint(0, dpad_distance), Direction::Down, dpad_arrow_size); - DrawArrow(p, dpad_center + QPoint(0, -dpad_distance), Direction::Up, dpad_arrow_size); - DrawArrow(p, dpad_center + QPoint(-dpad_distance, 0), Direction::Left, dpad_arrow_size); + DrawArrow(p, dpad_center + QPointF(dpad_distance, 0), Direction::Right, dpad_arrow_size); + DrawArrow(p, dpad_center + QPointF(0, dpad_distance), Direction::Down, dpad_arrow_size); + DrawArrow(p, dpad_center + QPointF(0, -dpad_distance), Direction::Up, dpad_arrow_size); + DrawArrow(p, dpad_center + QPointF(-dpad_distance, 0), Direction::Left, dpad_arrow_size); // ZL and ZR buttons p.setPen(colors.outline); diff --git a/src/yuzu/configuration/configure_service.cpp b/src/yuzu/configuration/configure_network.cpp index 4aa424803..ae22f1018 100644 --- a/src/yuzu/configuration/configure_service.cpp +++ b/src/yuzu/configuration/configure_network.cpp @@ -5,9 +5,11 @@ #include <QGraphicsItem> #include <QtConcurrent/QtConcurrent> #include "common/settings.h" +#include "core/core.h" #include "core/hle/service/bcat/backend/boxcat.h" -#include "ui_configure_service.h" -#include "yuzu/configuration/configure_service.h" +#include "core/network/network_interface.h" +#include "ui_configure_network.h" +#include "yuzu/configuration/configure_network.h" #ifdef YUZU_ENABLE_BOXCAT namespace { @@ -35,8 +37,8 @@ QString FormatEventStatusString(const Service::BCAT::EventStatus& status) { } // Anonymous namespace #endif -ConfigureService::ConfigureService(QWidget* parent) - : QWidget(parent), ui(std::make_unique<Ui::ConfigureService>()) { +ConfigureNetwork::ConfigureNetwork(QWidget* parent) + : QWidget(parent), ui(std::make_unique<Ui::ConfigureNetwork>()) { ui->setupUi(this); ui->bcat_source->addItem(QStringLiteral("None")); @@ -47,29 +49,42 @@ ConfigureService::ConfigureService(QWidget* parent) ui->bcat_source->addItem(QStringLiteral("Boxcat"), QStringLiteral("boxcat")); #endif + ui->network_interface->addItem(tr("None")); + for (const auto& iface : Network::GetAvailableNetworkInterfaces()) { + ui->network_interface->addItem(QString::fromStdString(iface.name)); + } + connect(ui->bcat_source, QOverload<int>::of(&QComboBox::currentIndexChanged), this, - &ConfigureService::OnBCATImplChanged); + &ConfigureNetwork::OnBCATImplChanged); this->SetConfiguration(); } -ConfigureService::~ConfigureService() = default; +ConfigureNetwork::~ConfigureNetwork() = default; -void ConfigureService::ApplyConfiguration() { +void ConfigureNetwork::ApplyConfiguration() { Settings::values.bcat_backend = ui->bcat_source->currentText().toLower().toStdString(); + Settings::values.network_interface = ui->network_interface->currentText().toStdString(); } -void ConfigureService::RetranslateUi() { +void ConfigureNetwork::RetranslateUi() { ui->retranslateUi(this); } -void ConfigureService::SetConfiguration() { +void ConfigureNetwork::SetConfiguration() { + const bool runtime_lock = !Core::System::GetInstance().IsPoweredOn(); + const int index = ui->bcat_source->findData(QString::fromStdString(Settings::values.bcat_backend.GetValue())); ui->bcat_source->setCurrentIndex(index == -1 ? 0 : index); + + const std::string& network_interface = Settings::values.network_interface.GetValue(); + + ui->network_interface->setCurrentText(QString::fromStdString(network_interface)); + ui->network_interface->setEnabled(runtime_lock); } -std::pair<QString, QString> ConfigureService::BCATDownloadEvents() { +std::pair<QString, QString> ConfigureNetwork::BCATDownloadEvents() { #ifdef YUZU_ENABLE_BOXCAT std::optional<std::string> global; std::map<std::string, Service::BCAT::EventStatus> map; @@ -114,7 +129,7 @@ std::pair<QString, QString> ConfigureService::BCATDownloadEvents() { #endif } -void ConfigureService::OnBCATImplChanged() { +void ConfigureNetwork::OnBCATImplChanged() { #ifdef YUZU_ENABLE_BOXCAT const auto boxcat = ui->bcat_source->currentText() == QStringLiteral("Boxcat"); ui->bcat_empty_header->setHidden(!boxcat); @@ -133,7 +148,7 @@ void ConfigureService::OnBCATImplChanged() { #endif } -void ConfigureService::OnUpdateBCATEmptyLabel(std::pair<QString, QString> string) { +void ConfigureNetwork::OnUpdateBCATEmptyLabel(std::pair<QString, QString> string) { #ifdef YUZU_ENABLE_BOXCAT const auto boxcat = ui->bcat_source->currentText() == QStringLiteral("Boxcat"); if (boxcat) { diff --git a/src/yuzu/configuration/configure_service.h b/src/yuzu/configuration/configure_network.h index f5c1b703a..442b68e6b 100644 --- a/src/yuzu/configuration/configure_service.h +++ b/src/yuzu/configuration/configure_network.h @@ -9,15 +9,15 @@ #include <QWidget> namespace Ui { -class ConfigureService; +class ConfigureNetwork; } -class ConfigureService : public QWidget { +class ConfigureNetwork : public QWidget { Q_OBJECT public: - explicit ConfigureService(QWidget* parent = nullptr); - ~ConfigureService() override; + explicit ConfigureNetwork(QWidget* parent = nullptr); + ~ConfigureNetwork() override; void ApplyConfiguration(); void RetranslateUi(); @@ -29,6 +29,6 @@ private: void OnBCATImplChanged(); void OnUpdateBCATEmptyLabel(std::pair<QString, QString> string); - std::unique_ptr<Ui::ConfigureService> ui; + std::unique_ptr<Ui::ConfigureNetwork> ui; QFutureWatcher<std::pair<QString, QString>> watcher{this}; }; diff --git a/src/yuzu/configuration/configure_service.ui b/src/yuzu/configuration/configure_network.ui index 9668dd557..5f9b7e97b 100644 --- a/src/yuzu/configuration/configure_service.ui +++ b/src/yuzu/configuration/configure_network.ui @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> - <class>ConfigureService</class> - <widget class="QWidget" name="ConfigureService"> + <class>ConfigureNetwork</class> + <widget class="QWidget" name="ConfigureNetwork"> <property name="geometry"> <rect> <x>0</x> @@ -17,21 +17,37 @@ <item> <layout class="QVBoxLayout" name="verticalLayout_3"> <item> + <widget class="QGroupBox" name="groupBox_2"> + <property name="title"> + <string>General</string> + </property> + <layout class="QGridLayout" name="gridLayout_2"> + <item row="1" column="1"> + <widget class="QComboBox" name="network_interface"/> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="label_4"> + <property name="text"> + <string>Network Interface</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> <widget class="QGroupBox" name="groupBox"> <property name="title"> <string>BCAT</string> </property> <layout class="QGridLayout" name="gridLayout"> - <item row="1" column="1" colspan="2"> - <widget class="QLabel" name="label_2"> - <property name="maximumSize"> - <size> - <width>260</width> - <height>16777215</height> - </size> - </property> + <item row="3" column="0"> + <widget class="QLabel" name="bcat_empty_header"> <property name="text"> - <string>BCAT is Nintendo's way of sending data to games to engage its community and unlock additional content.</string> + <string/> + </property> + <property name="alignment"> + <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set> </property> <property name="wordWrap"> <bool>true</bool> @@ -51,11 +67,8 @@ </property> </widget> </item> - <item row="3" column="1" colspan="2"> - <widget class="QLabel" name="bcat_empty_label"> - <property name="enabled"> - <bool>true</bool> - </property> + <item row="1" column="1" colspan="2"> + <widget class="QLabel" name="label_2"> <property name="maximumSize"> <size> <width>260</width> @@ -63,10 +76,7 @@ </size> </property> <property name="text"> - <string/> - </property> - <property name="alignment"> - <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set> + <string>BCAT is Nintendo's way of sending data to games to engage its community and unlock additional content.</string> </property> <property name="wordWrap"> <bool>true</bool> @@ -86,8 +96,17 @@ <item row="0" column="1" colspan="2"> <widget class="QComboBox" name="bcat_source"/> </item> - <item row="3" column="0"> - <widget class="QLabel" name="bcat_empty_header"> + <item row="3" column="1" colspan="2"> + <widget class="QLabel" name="bcat_empty_label"> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="maximumSize"> + <size> + <width>260</width> + <height>16777215</height> + </size> + </property> <property name="text"> <string/> </property> diff --git a/src/yuzu/debugger/console.cpp b/src/yuzu/debugger/console.cpp index 22ca1285d..f89ea8ea7 100644 --- a/src/yuzu/debugger/console.cpp +++ b/src/yuzu/debugger/console.cpp @@ -21,6 +21,7 @@ void ToggleConsole() { console_shown = UISettings::values.show_console.GetValue(); } + using namespace Common::Log; #if defined(_WIN32) && !defined(_DEBUG) FILE* temp; if (UISettings::values.show_console) { @@ -29,24 +30,20 @@ void ToggleConsole() { freopen_s(&temp, "CONIN$", "r", stdin); freopen_s(&temp, "CONOUT$", "w", stdout); freopen_s(&temp, "CONOUT$", "w", stderr); - Common::Log::AddBackend(std::make_unique<Common::Log::ColorConsoleBackend>()); + SetColorConsoleBackendEnabled(true); } } else { if (FreeConsole()) { // In order to close the console, we have to also detach the streams on it. // Just redirect them to NUL if there is no console window - Common::Log::RemoveBackend(Common::Log::ColorConsoleBackend::Name()); + SetColorConsoleBackendEnabled(false); freopen_s(&temp, "NUL", "r", stdin); freopen_s(&temp, "NUL", "w", stdout); freopen_s(&temp, "NUL", "w", stderr); } } #else - if (UISettings::values.show_console) { - Common::Log::AddBackend(std::make_unique<Common::Log::ColorConsoleBackend>()); - } else { - Common::Log::RemoveBackend(Common::Log::ColorConsoleBackend::Name()); - } + SetColorConsoleBackendEnabled(UISettings::values.show_console.GetValue()); #endif } } // namespace Debugger diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 5940e0cfd..e36774cc6 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -175,21 +175,6 @@ void GMainWindow::ShowTelemetryCallout() { const int GMainWindow::max_recent_files_item; -static void InitializeLogging() { - using namespace Common; - - Log::Filter log_filter; - log_filter.ParseFilterString(Settings::values.log_filter.GetValue()); - Log::SetGlobalFilter(log_filter); - - const auto log_dir = FS::GetYuzuPath(FS::YuzuPath::LogDir); - void(FS::CreateDir(log_dir)); - Log::AddBackend(std::make_unique<Log::FileBackend>(log_dir / LOG_FILE)); -#ifdef _WIN32 - Log::AddBackend(std::make_unique<Log::DebuggerBackend>()); -#endif -} - static void RemoveCachedContents() { const auto cache_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir); const auto offline_fonts = cache_dir / "fonts"; @@ -207,8 +192,7 @@ GMainWindow::GMainWindow() : input_subsystem{std::make_shared<InputCommon::InputSubsystem>()}, config{std::make_unique<Config>()}, vfs{std::make_shared<FileSys::RealVfsFilesystem>()}, provider{std::make_unique<FileSys::ManualContentProvider>()} { - InitializeLogging(); - + Common::Log::Initialize(); LoadTranslation(); setAcceptDrops(true); @@ -3437,6 +3421,7 @@ int main(int argc, char* argv[]) { // generating shaders setlocale(LC_ALL, "C"); + Core::System::InitializeGlobalInstance(); GMainWindow main_window; // After settings have been loaded by GMainWindow, apply the filter main_window.show(); diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp index c10093820..ba2c993ba 100644 --- a/src/yuzu_cmd/yuzu.cpp +++ b/src/yuzu_cmd/yuzu.cpp @@ -74,31 +74,14 @@ static void PrintVersion() { std::cout << "yuzu " << Common::g_scm_branch << " " << Common::g_scm_desc << std::endl; } -static void InitializeLogging() { - using namespace Common; - - Log::Filter log_filter(Log::Level::Debug); - log_filter.ParseFilterString(static_cast<std::string>(Settings::values.log_filter)); - Log::SetGlobalFilter(log_filter); - - Log::AddBackend(std::make_unique<Log::ColorConsoleBackend>()); - - const auto& log_dir = FS::GetYuzuPath(FS::YuzuPath::LogDir); - void(FS::CreateDir(log_dir)); - Log::AddBackend(std::make_unique<Log::FileBackend>(log_dir / LOG_FILE)); -#ifdef _WIN32 - Log::AddBackend(std::make_unique<Log::DebuggerBackend>()); -#endif -} - /// Application entry point int main(int argc, char** argv) { + Common::Log::Initialize(); + Common::Log::SetColorConsoleBackendEnabled(true); Common::DetachedTasks detached_tasks; Config config; int option_index = 0; - - InitializeLogging(); #ifdef _WIN32 int argc_w; auto argv_w = CommandLineToArgvW(GetCommandLineW(), &argc_w); @@ -163,6 +146,7 @@ int main(int argc, char** argv) { return -1; } + Core::System::InitializeGlobalInstance(); auto& system{Core::System::GetInstance()}; InputCommon::InputSubsystem input_subsystem; |