From bcb0d9cd4f5c4ae4cf3e91fc6b8ee0fca582fd76 Mon Sep 17 00:00:00 2001 From: pineappleEA Date: Fri, 20 Aug 2021 23:54:30 +0200 Subject: [PATCH] early-access version 2005 --- README.md | 2 +- src/common/logging/backend.cpp | 358 +++++++----------- src/common/logging/backend.h | 113 +++++- src/common/lru_cache.h | 141 +++++++ src/core/core.cpp | 9 +- src/core/core.h | 9 +- src/tests/common/param_package.cpp | 2 - src/video_core/buffer_cache/buffer_base.h | 13 +- src/video_core/buffer_cache/buffer_cache.h | 61 +-- src/video_core/memory_manager.cpp | 1 + src/video_core/texture_cache/image_base.h | 2 +- src/video_core/texture_cache/texture_cache.h | 89 ++--- .../texture_cache/texture_cache_base.h | 8 +- src/yuzu/debugger/console.cpp | 11 +- src/yuzu/main.cpp | 19 +- src/yuzu_cmd/yuzu.cpp | 22 +- 16 files changed, 511 insertions(+), 349 deletions(-) create mode 100755 src/common/lru_cache.h diff --git a/README.md b/README.md index 6a66c1dda..96a1c5c9d 100755 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ yuzu emulator early access ============= -This is the source code for early-access 2004. +This is the source code for early-access 2005. ## Legal Notice diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp index 13edda9c9..61dddab3f 100755 --- a/src/common/logging/backend.cpp +++ b/src/common/logging/backend.cpp @@ -2,9 +2,13 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include #include #include #include +#include +#include +#include #include #include @@ -12,173 +16,28 @@ #include // 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 { - -/** - * Interface for logging backends. - */ -class Backend { -public: - 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); - } - } - - void Flush() override { - // stderr shouldn't be buffered - } - - void EnableForStacktrace() override { - enabled = true; - } - - 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(FS::RemoveFile(old_filename)); - static_cast(FS::RenameFile(filename, old_filename)); - - file = std::make_unique(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; - } - -private: - std::unique_ptr file; - bool enabled = true; - std::size_t bytes_written = 0; -}; - -/** - * Backend that writes to Visual Studio's output window - */ -class DebuggerBackend final : public Backend { -public: - explicit DebuggerBackend() = default; - - ~DebuggerBackend() override = default; - - void Write(const Entry& entry) override { -#ifdef _WIN32 - ::OutputDebugStringW(UTF8ToUTF16W(FormatLogMessage(entry).append(1, '\n')).c_str()); -#endif - } - - void Flush() override {} - - void EnableForStacktrace() override {} -}; - -bool initialization_in_progress_suppress_logging = false; - /** * Static state as a singleton. */ class Impl { public: static Impl& Instance() { - if (!instance) { - abort(); - } - return *instance; - } - - static void Initialize() { - if (instance) { - abort(); - } - using namespace Common::FS; - initialization_in_progress_suppress_logging = true; - const auto& log_dir = GetYuzuPath(YuzuPath::LogDir); - void(CreateDir(log_dir)); - Filter filter; - filter.ParseFilterString(Settings::values.log_filter.GetValue()); - instance = std::unique_ptr(new Impl(log_dir / LOG_FILE, filter), - Deleter); - initialization_in_progress_suppress_logging = false; + static Impl backend; + return backend; } Impl(const Impl&) = delete; @@ -187,54 +46,74 @@ public: Impl(Impl&&) = delete; Impl& operator=(Impl&&) = delete; + 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 AddBackend(std::unique_ptr backend) { + std::lock_guard lock{writing_mutex}; + backends.push_back(std::move(backend)); + } + + void RemoveBackend(std::string_view backend_name) { + std::lock_guard lock{writing_mutex}; + + std::erase_if(backends, [&backend_name](const auto& backend) { + return backend_name == backend->GetName(); + }); + } + + const Filter& GetGlobalFilter() const { + return filter; + } + void SetGlobalFilter(const Filter& f) { filter = f; } - 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); + 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(); } private: - 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(); - } - })} {} + 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() { - StopBackendThread(); + // 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); + } + }); } - void StopBackendThread() { - Entry stop_entry{}; - stop_entry.final_entry = true; - message_queue.Push(stop_entry); + ~Impl() { + Entry entry; + entry.final_entry = true; + message_queue.Push(entry); backend_thread.join(); } @@ -256,51 +135,100 @@ private: }; } - void ForEachBackend(auto lambda) { - lambda(static_cast(debugger_backend)); - lambda(static_cast(color_console_backend)); - lambda(static_cast(file_backend)); - } - - static void Deleter(Impl* ptr) { - delete ptr; - } - - static inline std::unique_ptr instance{nullptr, Deleter}; - - Filter filter; - DebuggerBackend debugger_backend{}; - ColorConsoleBackend color_console_backend{}; - FileBackend file_backend; - + std::mutex writing_mutex; std::thread backend_thread; - MPSCQueue message_queue{}; + std::vector> backends; + MPSCQueue message_queue; + Filter filter; std::chrono::steady_clock::time_point time_origin{std::chrono::steady_clock::now()}; }; -} // namespace -void Initialize() { - Impl::Initialize(); +ConsoleBackend::~ConsoleBackend() = default; + +void ConsoleBackend::Write(const Entry& entry) { + PrintMessage(entry); } -void DisableLoggingInTests() { - initialization_in_progress_suppress_logging = true; +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(filename, FS::FileAccessMode::Write, FS::FileType::TextFile); +} + +FileBackend::~FileBackend() = default; + +void FileBackend::Write(const Entry& entry) { + if (!file->IsOpen()) { + return; + } + + 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) { + file->Flush(); + } +} + +DebuggerBackend::~DebuggerBackend() = default; + +void DebuggerBackend::Write(const Entry& entry) { +#ifdef _WIN32 + ::OutputDebugStringW(UTF8ToUTF16W(FormatLogMessage(entry).append(1, '\n')).c_str()); +#endif } void SetGlobalFilter(const Filter& filter) { Impl::Instance().SetGlobalFilter(filter); } -void SetColorConsoleBackendEnabled(bool enabled) { - Impl::Instance().SetColorConsoleBackendEnabled(enabled); +void AddBackend(std::unique_ptr 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 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) { - if (!initialization_in_progress_suppress_logging) { - Impl::Instance().PushEntry(log_class, log_level, filename, line_num, function, - fmt::vformat(format, 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)); } } // namespace Common::Log diff --git a/src/common/logging/backend.h b/src/common/logging/backend.h index cb7839ee9..4b9a910c1 100755 --- a/src/common/logging/backend.h +++ b/src/common/logging/backend.h @@ -5,21 +5,120 @@ #pragma once #include +#include +#include +#include #include "common/logging/filter.h" +#include "common/logging/log.h" + +namespace Common::FS { +class IOFile; +} namespace Common::Log { class Filter; -/// Initializes the logging system. This should be the first thing called in main. -void Initialize(); +/** + * 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; -void DisableLoggingInTests(); + 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; +}; /** - * The global filter will prevent any messages from even being processed if they are filtered. + * 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; +}; + +/** + * 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 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); + +void RemoveBackend(std::string_view backend_name); + +Backend* GetBackend(std::string_view backend_name); + +/** + * 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 */ void SetGlobalFilter(const Filter& filter); - -void SetColorConsoleBackendEnabled(bool enabled); -} // namespace Common::Log +} // namespace Common::Log \ No newline at end of file diff --git a/src/common/lru_cache.h b/src/common/lru_cache.h new file mode 100755 index 000000000..048e9c3da --- /dev/null +++ b/src/common/lru_cache.h @@ -0,0 +1,141 @@ +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv2+ or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include +#include + +#include "common/common_types.h" + +namespace Common { + +template +class LeastRecentlyUsedCache { + using ObjectType = typename Traits::ObjectType; + using TickType = typename Traits::TickType; + + struct Item { + ObjectType obj; + TickType tick; + Item* next{}; + Item* prev{}; + }; + +public: + LeastRecentlyUsedCache() : first_item{}, last_item{} {} + ~LeastRecentlyUsedCache() = default; + + size_t Insert(ObjectType obj, TickType tick) { + const auto new_id = build(); + auto& item = item_pool[new_id]; + item.obj = obj; + item.tick = tick; + attach(item); + return new_id; + } + + void Touch(size_t id, TickType tick) { + auto& item = item_pool[id]; + if (item.tick >= tick) { + return; + } + item.tick = tick; + if (&item == last_item) { + return; + } + detach(item); + attach(item); + } + + void Free(size_t id) { + auto& item = item_pool[id]; + detach(item); + item.prev = nullptr; + item.next = nullptr; + free_items.push_back(id); + } + + template + void ForEachItemBelow(TickType tick, Func&& func) { + static constexpr bool RETURNS_BOOL = + std::is_same_v, bool>; + Item* iterator = first_item; + while (iterator) { + if (static_cast(tick) - static_cast(iterator->tick) < 0) { + return; + } + Item* next = iterator->next; + if constexpr (RETURNS_BOOL) { + if (func(iterator->obj)) { + return; + } + } else { + func(iterator->obj); + } + iterator = next; + } + } + +private: + size_t build() { + if (free_items.empty()) { + const size_t item_id = item_pool.size(); + item_pool.emplace_back(); + auto& item = item_pool[item_id]; + item.next = nullptr; + item.prev = nullptr; + return item_id; + } + const size_t item_id = free_items.front(); + free_items.pop_front(); + auto& item = item_pool[item_id]; + item.next = nullptr; + item.prev = nullptr; + return item_id; + } + + void attach(Item& item) { + if (!first_item) { + first_item = &item; + } + if (!last_item) { + last_item = &item; + } else { + item.prev = last_item; + last_item->next = &item; + item.next = nullptr; + last_item = &item; + } + } + + void detach(Item& item) { + if (item.prev) { + item.prev->next = item.next; + } + if (item.next) { + item.next->prev = item.prev; + } + if (&item == first_item) { + first_item = item.next; + if (first_item) { + first_item->prev = nullptr; + } + } + if (&item == last_item) { + last_item = item.prev; + if (last_item) { + last_item->next = nullptr; + } + } + } + + std::deque item_pool; + std::deque free_items; + Item* first_item; + Item* last_item; +}; + +} // namespace Common diff --git a/src/core/core.cpp b/src/core/core.cpp index b0dc594d4..5d8a61b3a 100755 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -84,6 +84,8 @@ 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. @@ -423,13 +425,6 @@ struct System::Impl { System::System() : impl{std::make_unique(*this)} {} System::~System() = default; -void System::InitializeGlobalInstance() { - if (s_instance) { - abort(); - } - s_instance = std::unique_ptr(new System); -} - CpuManager& System::GetCpuManager() { return impl->cpu_manager; } diff --git a/src/core/core.h b/src/core/core.h index 65b447a1c..cd9af0c07 100755 --- a/src/core/core.h +++ b/src/core/core.h @@ -121,14 +121,9 @@ public: * @returns Reference to the instance of the System singleton class. */ [[deprecated("Use of the global system instance is deprecated")]] static System& GetInstance() { - if (!s_instance) { - abort(); - } - return *s_instance; + return s_instance; } - static void InitializeGlobalInstance(); - /// Enumeration representing the return values of the System Initialize and Load process. enum class ResultStatus : u32 { Success, ///< Succeeded @@ -398,7 +393,7 @@ private: struct Impl; std::unique_ptr impl; - inline static std::unique_ptr s_instance{}; + static System s_instance; }; } // namespace Core diff --git a/src/tests/common/param_package.cpp b/src/tests/common/param_package.cpp index e31ca3544..4c0f9654f 100755 --- a/src/tests/common/param_package.cpp +++ b/src/tests/common/param_package.cpp @@ -4,13 +4,11 @@ #include #include -#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/buffer_cache/buffer_base.h b/src/video_core/buffer_cache/buffer_base.h index c3318095c..4b696a60f 100755 --- a/src/video_core/buffer_cache/buffer_base.h +++ b/src/video_core/buffer_cache/buffer_base.h @@ -261,16 +261,6 @@ public: stream_score += score; } - /// Sets the new frame tick - void SetFrameTick(u64 new_frame_tick) noexcept { - frame_tick = new_frame_tick; - } - - /// Returns the new frame tick - [[nodiscard]] u64 FrameTick() const noexcept { - return frame_tick; - } - /// Returns the likeliness of this being a stream buffer [[nodiscard]] int StreamScore() const noexcept { return stream_score; @@ -307,6 +297,8 @@ public: return words.size_bytes; } + size_t lru_id; + private: template u64* Array() noexcept { @@ -603,7 +595,6 @@ private: RasterizerInterface* rasterizer = nullptr; VAddr cpu_addr = 0; Words words; - u64 frame_tick = 0; BufferFlagBits flags{}; int stream_score = 0; }; diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index 3b43554f9..a0217908a 100755 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -20,6 +20,7 @@ #include "common/common_types.h" #include "common/div_ceil.h" #include "common/literals.h" +#include "common/lru_cache.h" #include "common/microprofile.h" #include "common/scope_exit.h" #include "common/settings.h" @@ -77,7 +78,7 @@ class BufferCache { static constexpr BufferId NULL_BUFFER_ID{0}; - static constexpr u64 EXPECTED_MEMORY = 512_MiB; + static constexpr u64 EXPECTED_MEMORY = 256_MiB; static constexpr u64 CRITICAL_MEMORY = 1_GiB; using Maxwell = Tegra::Engines::Maxwell3D::Regs; @@ -330,7 +331,7 @@ private: template void ChangeRegister(BufferId buffer_id); - void TouchBuffer(Buffer& buffer) const noexcept; + void TouchBuffer(Buffer& buffer, BufferId buffer_id) noexcept; bool SynchronizeBuffer(Buffer& buffer, VAddr cpu_addr, u32 size); @@ -428,7 +429,11 @@ private: size_t immediate_buffer_capacity = 0; std::unique_ptr immediate_buffer_alloc; - typename SlotVector::Iterator deletion_iterator; + struct LRUItemParams { + using ObjectType = BufferId; + using TickType = u64; + }; + Common::LeastRecentlyUsedCache lru_cache; u64 frame_tick = 0; u64 total_used_memory = 0; @@ -445,7 +450,6 @@ BufferCache

::BufferCache(VideoCore::RasterizerInterface& rasterizer_, kepler_compute{kepler_compute_}, gpu_memory{gpu_memory_}, cpu_memory{cpu_memory_} { // Ensure the first slot is used for the null buffer void(slot_buffers.insert(runtime, NullBufferParams{})); - deletion_iterator = slot_buffers.end(); common_ranges.clear(); } @@ -454,20 +458,17 @@ void BufferCache

::RunGarbageCollector() { const bool aggressive_gc = total_used_memory >= CRITICAL_MEMORY; const u64 ticks_to_destroy = aggressive_gc ? 60 : 120; int num_iterations = aggressive_gc ? 64 : 32; - for (; num_iterations > 0; --num_iterations) { - if (deletion_iterator == slot_buffers.end()) { - deletion_iterator = slot_buffers.begin(); + const auto clean_up = [this, &num_iterations](BufferId buffer_id) { + if (num_iterations == 0) { + return true; } - ++deletion_iterator; - if (deletion_iterator == slot_buffers.end()) { - break; - } - const auto [buffer_id, buffer] = *deletion_iterator; - if (buffer->FrameTick() + ticks_to_destroy < frame_tick) { - DownloadBufferMemory(*buffer); - DeleteBuffer(buffer_id); - } - } + --num_iterations; + auto& buffer = slot_buffers[buffer_id]; + DownloadBufferMemory(buffer); + DeleteBuffer(buffer_id); + return false; + }; + lru_cache.ForEachItemBelow(frame_tick - ticks_to_destroy, clean_up); } template @@ -954,7 +955,7 @@ bool BufferCache

::IsRegionCpuModified(VAddr addr, size_t size) { template void BufferCache

::BindHostIndexBuffer() { Buffer& buffer = slot_buffers[index_buffer.buffer_id]; - TouchBuffer(buffer); + TouchBuffer(buffer, index_buffer.buffer_id); const u32 offset = buffer.Offset(index_buffer.cpu_addr); const u32 size = index_buffer.size; SynchronizeBuffer(buffer, index_buffer.cpu_addr, size); @@ -975,7 +976,7 @@ void BufferCache

::BindHostVertexBuffers() { for (u32 index = 0; index < NUM_VERTEX_BUFFERS; ++index) { const Binding& binding = vertex_buffers[index]; Buffer& buffer = slot_buffers[binding.buffer_id]; - TouchBuffer(buffer); + TouchBuffer(buffer, binding.buffer_id); SynchronizeBuffer(buffer, binding.cpu_addr, binding.size); if (!flags[Dirty::VertexBuffer0 + index]) { continue; @@ -1011,7 +1012,7 @@ void BufferCache

::BindHostGraphicsUniformBuffer(size_t stage, u32 index, u32 const VAddr cpu_addr = binding.cpu_addr; const u32 size = std::min(binding.size, (*uniform_buffer_sizes)[stage][index]); Buffer& buffer = slot_buffers[binding.buffer_id]; - TouchBuffer(buffer); + TouchBuffer(buffer, binding.buffer_id); const bool use_fast_buffer = binding.buffer_id != NULL_BUFFER_ID && size <= uniform_buffer_skip_cache_size && !buffer.IsRegionGpuModified(cpu_addr, size); @@ -1083,7 +1084,7 @@ void BufferCache

::BindHostGraphicsStorageBuffers(size_t stage) { ForEachEnabledBit(enabled_storage_buffers[stage], [&](u32 index) { const Binding& binding = storage_buffers[stage][index]; Buffer& buffer = slot_buffers[binding.buffer_id]; - TouchBuffer(buffer); + TouchBuffer(buffer, binding.buffer_id); const u32 size = binding.size; SynchronizeBuffer(buffer, binding.cpu_addr, size); @@ -1128,7 +1129,7 @@ void BufferCache

::BindHostTransformFeedbackBuffers() { for (u32 index = 0; index < NUM_TRANSFORM_FEEDBACK_BUFFERS; ++index) { const Binding& binding = transform_feedback_buffers[index]; Buffer& buffer = slot_buffers[binding.buffer_id]; - TouchBuffer(buffer); + TouchBuffer(buffer, binding.buffer_id); const u32 size = binding.size; SynchronizeBuffer(buffer, binding.cpu_addr, size); @@ -1148,7 +1149,7 @@ void BufferCache

::BindHostComputeUniformBuffers() { ForEachEnabledBit(enabled_compute_uniform_buffer_mask, [&](u32 index) { const Binding& binding = compute_uniform_buffers[index]; Buffer& buffer = slot_buffers[binding.buffer_id]; - TouchBuffer(buffer); + TouchBuffer(buffer, binding.buffer_id); const u32 size = std::min(binding.size, (*compute_uniform_buffer_sizes)[index]); SynchronizeBuffer(buffer, binding.cpu_addr, size); @@ -1168,7 +1169,7 @@ void BufferCache

::BindHostComputeStorageBuffers() { ForEachEnabledBit(enabled_compute_storage_buffers, [&](u32 index) { const Binding& binding = compute_storage_buffers[index]; Buffer& buffer = slot_buffers[binding.buffer_id]; - TouchBuffer(buffer); + TouchBuffer(buffer, binding.buffer_id); const u32 size = binding.size; SynchronizeBuffer(buffer, binding.cpu_addr, size); @@ -1513,11 +1514,11 @@ BufferId BufferCache

::CreateBuffer(VAddr cpu_addr, u32 wanted_size) { const OverlapResult overlap = ResolveOverlaps(cpu_addr, wanted_size); const u32 size = static_cast(overlap.end - overlap.begin); const BufferId new_buffer_id = slot_buffers.insert(runtime, rasterizer, overlap.begin, size); - TouchBuffer(slot_buffers[new_buffer_id]); for (const BufferId overlap_id : overlap.ids) { JoinOverlap(new_buffer_id, overlap_id, !overlap.has_stream_leap); } Register(new_buffer_id); + TouchBuffer(slot_buffers[new_buffer_id], new_buffer_id); return new_buffer_id; } @@ -1534,12 +1535,14 @@ void BufferCache

::Unregister(BufferId buffer_id) { template template void BufferCache

::ChangeRegister(BufferId buffer_id) { - const Buffer& buffer = slot_buffers[buffer_id]; + Buffer& buffer = slot_buffers[buffer_id]; const auto size = buffer.SizeBytes(); if (insert) { total_used_memory += Common::AlignUp(size, 1024); + buffer.lru_id = lru_cache.Insert(buffer_id, frame_tick); } else { total_used_memory -= Common::AlignUp(size, 1024); + lru_cache.Free(buffer.lru_id); } const VAddr cpu_addr_begin = buffer.CpuAddr(); const VAddr cpu_addr_end = cpu_addr_begin + size; @@ -1555,8 +1558,10 @@ void BufferCache

::ChangeRegister(BufferId buffer_id) { } template -void BufferCache

::TouchBuffer(Buffer& buffer) const noexcept { - buffer.SetFrameTick(frame_tick); +void BufferCache

::TouchBuffer(Buffer& buffer, BufferId buffer_id) noexcept { + if (buffer_id != NULL_BUFFER_ID) { + lru_cache.Touch(buffer.lru_id, frame_tick); + } } template diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp index 882eff880..c60ed6453 100755 --- a/src/video_core/memory_manager.cpp +++ b/src/video_core/memory_manager.cpp @@ -463,6 +463,7 @@ std::vector> 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/texture_cache/image_base.h b/src/video_core/texture_cache/image_base.h index ff1feda9b..662089e3d 100755 --- a/src/video_core/texture_cache/image_base.h +++ b/src/video_core/texture_cache/image_base.h @@ -80,7 +80,7 @@ struct ImageBase { VAddr cpu_addr_end = 0; u64 modification_tick = 0; - u64 frame_tick = 0; + size_t lru_index = ~0; std::array mip_level_offsets{}; diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index a087498ff..c16cc0838 100755 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h @@ -43,8 +43,6 @@ TextureCache

::TextureCache(Runtime& runtime_, VideoCore::RasterizerInterface& void(slot_image_views.insert(runtime, NullImageParams{})); void(slot_samplers.insert(runtime, sampler_descriptor)); - deletion_iterator = slot_images.begin(); - if constexpr (HAS_DEVICE_MEMORY_INFO) { const auto device_memory = runtime.GetDeviceLocalMemory(); const u64 possible_expected_memory = (device_memory * 3) / 10; @@ -64,65 +62,33 @@ template void TextureCache

::RunGarbageCollector() { const bool high_priority_mode = total_used_memory >= expected_memory; const bool aggressive_mode = total_used_memory >= critical_memory; - const u64 ticks_to_destroy = high_priority_mode ? 60 : 100; - int num_iterations = aggressive_mode ? 256 : (high_priority_mode ? 128 : 64); - for (; num_iterations > 0; --num_iterations) { - if (deletion_iterator == slot_images.end()) { - deletion_iterator = slot_images.begin(); - if (deletion_iterator == slot_images.end()) { - break; - } + const u64 ticks_to_destroy = aggressive_mode ? 10ULL : high_priority_mode ? 50ULL : 100ULL; + size_t num_iterations = aggressive_mode ? 10000 : (high_priority_mode ? 50 : 5); + const auto clean_up = [this, &num_iterations, high_priority_mode](ImageId image_id) { + if (num_iterations == 0) { + return true; } - auto [image_id, image_tmp] = *deletion_iterator; - Image* image = image_tmp; // fix clang error. - const bool is_alias = True(image->flags & ImageFlagBits::Alias); - const bool is_bad_overlap = True(image->flags & ImageFlagBits::BadOverlap); - const bool must_download = image->IsSafeDownload(); - bool should_care = is_bad_overlap || is_alias || (high_priority_mode && !must_download); - const u64 ticks_needed = - is_bad_overlap - ? ticks_to_destroy >> 4 - : ((should_care && aggressive_mode) ? ticks_to_destroy >> 1 : ticks_to_destroy); - should_care |= aggressive_mode; - if (should_care && image->frame_tick + ticks_needed < frame_tick) { - if (is_bad_overlap) { - const bool overlap_check = std::ranges::all_of( - image->overlapping_images, [&, image](const ImageId& overlap_id) { - auto& overlap = slot_images[overlap_id]; - return overlap.frame_tick >= image->frame_tick; - }); - if (!overlap_check) { - ++deletion_iterator; - continue; - } - } - if (!is_bad_overlap && must_download) { - const bool alias_check = std::ranges::none_of( - image->aliased_images, [&, image](const AliasedImage& alias) { - auto& alias_image = slot_images[alias.id]; - return (alias_image.frame_tick < image->frame_tick) || - (alias_image.modification_tick < image->modification_tick); - }); - - if (alias_check) { - auto map = runtime.DownloadStagingBuffer(image->unswizzled_size_bytes); - const auto copies = FullDownloadCopies(image->info); - image->DownloadMemory(map, copies); - runtime.Finish(); - SwizzleImage(gpu_memory, image->gpu_addr, image->info, copies, map.mapped_span); - } - } - if (True(image->flags & ImageFlagBits::Tracked)) { - UntrackImage(*image, image_id); - } - UnregisterImage(image_id); - DeleteImage(image_id); - if (is_bad_overlap) { - ++num_iterations; - } + --num_iterations; + auto& image = slot_images[image_id]; + const bool must_download = image.IsSafeDownload(); + if (!high_priority_mode && must_download) { + return false; } - ++deletion_iterator; - } + if (must_download) { + auto map = runtime.DownloadStagingBuffer(image.unswizzled_size_bytes); + const auto copies = FullDownloadCopies(image.info); + image.DownloadMemory(map, copies); + runtime.Finish(); + SwizzleImage(gpu_memory, image.gpu_addr, image.info, copies, map.mapped_span); + } + if (True(image.flags & ImageFlagBits::Tracked)) { + UntrackImage(image, image_id); + } + UnregisterImage(image_id); + DeleteImage(image_id); + return false; + }; + lru_cache.ForEachItemBelow(frame_tick - ticks_to_destroy, clean_up); } template @@ -1078,6 +1044,8 @@ void TextureCache

::RegisterImage(ImageId image_id) { tentative_size = EstimatedDecompressedSize(tentative_size, image.info.format); } total_used_memory += Common::AlignUp(tentative_size, 1024); + image.lru_index = lru_cache.Insert(image_id, frame_tick); + ForEachGPUPage(image.gpu_addr, image.guest_size_bytes, [this, image_id](u64 page) { gpu_page_table[page].push_back(image_id); }); if (False(image.flags & ImageFlagBits::Sparse)) { @@ -1115,6 +1083,7 @@ void TextureCache

::UnregisterImage(ImageId image_id) { tentative_size = EstimatedDecompressedSize(tentative_size, image.info.format); } total_used_memory -= Common::AlignUp(tentative_size, 1024); + lru_cache.Free(image.lru_index); const auto& clear_page_table = [this, image_id]( u64 page, @@ -1384,7 +1353,7 @@ void TextureCache

::PrepareImage(ImageId image_id, bool is_modification, bool if (is_modification) { MarkModification(image); } - image.frame_tick = frame_tick; + lru_cache.Touch(image.lru_index, frame_tick); } template diff --git a/src/video_core/texture_cache/texture_cache_base.h b/src/video_core/texture_cache/texture_cache_base.h index e4ae351cb..d7528ed24 100755 --- a/src/video_core/texture_cache/texture_cache_base.h +++ b/src/video_core/texture_cache/texture_cache_base.h @@ -14,6 +14,7 @@ #include "common/common_types.h" #include "common/literals.h" +#include "common/lru_cache.h" #include "video_core/compatible_formats.h" #include "video_core/delayed_destruction_ring.h" #include "video_core/engines/fermi_2d.h" @@ -370,6 +371,12 @@ private: std::vector uncommitted_downloads; std::queue> committed_downloads; + struct LRUItemParams { + using ObjectType = ImageId; + using TickType = u64; + }; + Common::LeastRecentlyUsedCache lru_cache; + static constexpr size_t TICKS_TO_DESTROY = 6; DelayedDestructionRing sentenced_images; DelayedDestructionRing sentenced_image_view; @@ -379,7 +386,6 @@ private: u64 modification_tick = 0; u64 frame_tick = 0; - typename SlotVector::Iterator deletion_iterator; }; } // namespace VideoCommon diff --git a/src/yuzu/debugger/console.cpp b/src/yuzu/debugger/console.cpp index f89ea8ea7..22ca1285d 100755 --- a/src/yuzu/debugger/console.cpp +++ b/src/yuzu/debugger/console.cpp @@ -21,7 +21,6 @@ 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) { @@ -30,20 +29,24 @@ void ToggleConsole() { freopen_s(&temp, "CONIN$", "r", stdin); freopen_s(&temp, "CONOUT$", "w", stdout); freopen_s(&temp, "CONOUT$", "w", stderr); - SetColorConsoleBackendEnabled(true); + Common::Log::AddBackend(std::make_unique()); } } 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 - SetColorConsoleBackendEnabled(false); + Common::Log::RemoveBackend(Common::Log::ColorConsoleBackend::Name()); freopen_s(&temp, "NUL", "r", stdin); freopen_s(&temp, "NUL", "w", stdout); freopen_s(&temp, "NUL", "w", stderr); } } #else - SetColorConsoleBackendEnabled(UISettings::values.show_console.GetValue()); + if (UISettings::values.show_console) { + Common::Log::AddBackend(std::make_unique()); + } else { + Common::Log::RemoveBackend(Common::Log::ColorConsoleBackend::Name()); + } #endif } } // namespace Debugger diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 7853c2cc7..672e5a419 100755 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -177,6 +177,21 @@ 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_dir / LOG_FILE)); +#ifdef _WIN32 + Log::AddBackend(std::make_unique()); +#endif +} + static void RemoveCachedContents() { const auto cache_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir); const auto offline_fonts = cache_dir / "fonts"; @@ -194,6 +209,8 @@ GMainWindow::GMainWindow() : input_subsystem{std::make_shared()}, config{std::make_unique()}, vfs{std::make_shared()}, provider{std::make_unique()} { + InitializeLogging(); + LoadTranslation(); setAcceptDrops(true); @@ -3446,7 +3463,6 @@ void GMainWindow::SetDiscordEnabled([[maybe_unused]] bool state) { #endif int main(int argc, char* argv[]) { - Common::Log::Initialize(); Common::DetachedTasks detached_tasks; MicroProfileOnThreadCreate("Frontend"); SCOPE_EXIT({ MicroProfileShutdown(); }); @@ -3486,7 +3502,6 @@ 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 ba2c993ba..c10093820 100755 --- a/src/yuzu_cmd/yuzu.cpp +++ b/src/yuzu_cmd/yuzu.cpp @@ -74,14 +74,31 @@ 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(Settings::values.log_filter)); + Log::SetGlobalFilter(log_filter); + + Log::AddBackend(std::make_unique()); + + const auto& log_dir = FS::GetYuzuPath(FS::YuzuPath::LogDir); + void(FS::CreateDir(log_dir)); + Log::AddBackend(std::make_unique(log_dir / LOG_FILE)); +#ifdef _WIN32 + Log::AddBackend(std::make_unique()); +#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); @@ -146,7 +163,6 @@ int main(int argc, char** argv) { return -1; } - Core::System::InitializeGlobalInstance(); auto& system{Core::System::GetInstance()}; InputCommon::InputSubsystem input_subsystem;