From 6cdaf3355901c74f79b40104db054f41b58176d8 Mon Sep 17 00:00:00 2001 From: pineappleEA Date: Sun, 19 Mar 2023 22:38:32 +0100 Subject: [PATCH] early-access version 3466 --- README.md | 2 +- src/common/bounded_threadsafe_queue.h | 323 +++++++++++++++++--------- src/common/logging/backend.cpp | 2 +- src/common/string_util.cpp | 14 +- src/common/string_util.h | 8 +- src/video_core/gpu_thread.cpp | 2 +- 6 files changed, 224 insertions(+), 127 deletions(-) diff --git a/README.md b/README.md index d4dfbb317..5c4f3ace7 100755 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ yuzu emulator early access ============= -This is the source code for early-access 3465. +This is the source code for early-access 3466. ## Legal Notice diff --git a/src/common/bounded_threadsafe_queue.h b/src/common/bounded_threadsafe_queue.h index b0a5a3bad..ef491fde2 100755 --- a/src/common/bounded_threadsafe_queue.h +++ b/src/common/bounded_threadsafe_queue.h @@ -24,112 +24,53 @@ class SPSCQueue { public: bool TryPush(T&& t) { - const size_t write_index = m_write_index.load(); - - // Check if we have free slots to write to. - if ((write_index - m_read_index.load()) == Capacity) { - return false; - } - - // Determine the position to write to. - const size_t pos = write_index % Capacity; - - // Push into the queue. - m_data[pos] = std::move(t); - - // Increment the write index. - ++m_write_index; - - // Notify the consumer that we have pushed into the queue. - std::scoped_lock lock{cv_mutex}; - cv.notify_one(); - - return true; + return Push(std::move(t)); } template - bool TryPush(Args&&... args) { - const size_t write_index = m_write_index.load(); - - // Check if we have free slots to write to. - if ((write_index - m_read_index.load()) == Capacity) { - return false; - } - - // Determine the position to write to. - const size_t pos = write_index % Capacity; - - // Emplace into the queue. - std::construct_at(std::addressof(m_data[pos]), std::forward(args)...); - - // Increment the write index. - ++m_write_index; - - // Notify the consumer that we have pushed into the queue. - std::scoped_lock lock{cv_mutex}; - cv.notify_one(); - - return true; + bool TryEmplace(Args&&... args) { + return Emplace(std::forward(args)...); } - void Push(T&& t) { - const size_t write_index = m_write_index.load(); - - // Wait until we have free slots to write to. - while ((write_index - m_read_index.load()) == Capacity) { - std::this_thread::yield(); - } - - // Determine the position to write to. - const size_t pos = write_index % Capacity; - - // Push into the queue. - m_data[pos] = std::move(t); - - // Increment the write index. - ++m_write_index; - - // Notify the consumer that we have pushed into the queue. - std::scoped_lock lock{cv_mutex}; - cv.notify_one(); + void PushWait(T&& t) { + Push(std::move(t)); } template - void Push(Args&&... args) { - const size_t write_index = m_write_index.load(); + void EmplaceWait(Args&&... args) { + Emplace(std::forward(args)...); + } - // Wait until we have free slots to write to. - while ((write_index - m_read_index.load()) == Capacity) { - std::this_thread::yield(); - } + void PushOverwrite(T&& t) { + Push(std::move(t)); + } - // Determine the position to write to. - const size_t pos = write_index % Capacity; - - // Emplace into the queue. - std::construct_at(std::addressof(m_data[pos]), std::forward(args)...); - - // Increment the write index. - ++m_write_index; - - // Notify the consumer that we have pushed into the queue. - std::scoped_lock lock{cv_mutex}; - cv.notify_one(); + template + void EmplaceOverwrite(Args&&... args) { + Emplace(std::forward(args)...); } bool TryPop(T& t) { - return Pop(t); + return Pop(t); + } + + void PopWait(T& t) { + Pop(t); } void PopWait(T& t, std::stop_token stop_token) { - Wait(stop_token); - Pop(t); + Pop(t, stop_token); + } + + T PopWait() { + T t; + Pop(t); + return t; } T PopWait(std::stop_token stop_token) { - Wait(stop_token); T t; - Pop(t); + Pop(t, stop_token); return t; } @@ -148,6 +89,102 @@ public: } private: + enum class PushMode { + Try, + Wait, + Overwrite, + Count, + }; + + enum class PopMode { + Try, + Wait, + WaitWithStopToken, + Count, + }; + + template + bool Push(T&& t) { + const size_t write_index = m_write_index.load(); + + if constexpr (Mode == PushMode::Try) { + // Check if we have free slots to write to. + if ((write_index - m_read_index.load()) == Capacity) { + return false; + } + } else if constexpr (Mode == PushMode::Wait) { + // Wait until we have free slots to write to. + std::unique_lock lock{producer_cv_mutex}; + producer_cv.wait(lock, [this, write_index] { + return (write_index - m_read_index.load()) < Capacity; + }); + } else if constexpr (Mode == PushMode::Overwrite) { + // Check if we have free slots to write to. + if ((write_index - m_read_index.load()) == Capacity) { + // If we don't, increment the read index. This is effectively a pop operation. + ++m_read_index; + } + } else { + static_assert(Mode < PushMode::Count, "Invalid PushMode."); + } + + // Determine the position to write to. + const size_t pos = write_index % Capacity; + + // Push into the queue. + m_data[pos] = std::move(t); + + // Increment the write index. + ++m_write_index; + + // Notify the consumer that we have pushed into the queue. + std::scoped_lock lock{consumer_cv_mutex}; + consumer_cv.notify_one(); + + return true; + } + + template + bool Emplace(Args&&... args) { + const size_t write_index = m_write_index.load(); + + if constexpr (Mode == PushMode::Try) { + // Check if we have free slots to write to. + if ((write_index - m_read_index.load()) == Capacity) { + return false; + } + } else if constexpr (Mode == PushMode::Wait) { + // Wait until we have free slots to write to. + std::unique_lock lock{producer_cv_mutex}; + producer_cv.wait(lock, [this, write_index] { + return (write_index - m_read_index.load()) < Capacity; + }); + } else if constexpr (Mode == PushMode::Overwrite) { + // Check if we have free slots to write to. + if ((write_index - m_read_index.load()) == Capacity) { + // If we don't, increment the read index. This is effectively a pop operation. + ++m_read_index; + } + } else { + static_assert(Mode < PushMode::Count, "Invalid PushMode."); + } + + // Determine the position to write to. + const size_t pos = write_index % Capacity; + + // Emplace into the queue. + std::construct_at(std::addressof(m_data[pos]), std::forward(args)...); + + // Increment the write index. + ++m_write_index; + + // Notify the consumer that we have pushed into the queue. + std::scoped_lock lock{consumer_cv_mutex}; + consumer_cv.notify_one(); + + return true; + } + void Pop() { const size_t read_index = m_read_index.load(); @@ -164,14 +201,33 @@ private: // Increment the read index. ++m_read_index; + + // Notify the producer that we have popped off the queue. + std::unique_lock lock{producer_cv_mutex}; + producer_cv.notify_one(); } - bool Pop(T& t) { + template + bool Pop(T& t, [[maybe_unused]] std::stop_token stop_token = {}) { const size_t read_index = m_read_index.load(); - // Check if the queue is empty. - if (read_index == m_write_index.load()) { - return false; + if constexpr (Mode == PopMode::Try) { + // Check if the queue is empty. + if (read_index == m_write_index.load()) { + return false; + } + } else if constexpr (Mode == PopMode::Wait) { + // Wait until the queue is not empty. + std::unique_lock lock{consumer_cv_mutex}; + consumer_cv.wait(lock, + [this, read_index] { return read_index != m_write_index.load(); }); + } else if constexpr (Mode == PopMode::WaitWithStopToken) { + // Wait until the queue is not empty. + std::unique_lock lock{consumer_cv_mutex}; + Common::CondvarWait(consumer_cv, lock, stop_token, + [this, read_index] { return read_index != m_write_index.load(); }); + } else { + static_assert(Mode < PopMode::Count, "Invalid PopMode."); } // Determine the position to read from. @@ -183,12 +239,11 @@ private: // Increment the read index. ++m_read_index; - return true; - } + // Notify the producer that we have popped off the queue. + std::unique_lock lock{producer_cv_mutex}; + producer_cv.notify_one(); - void Wait(std::stop_token stop_token) { - std::unique_lock lock{cv_mutex}; - Common::CondvarWait(cv, lock, stop_token, [this] { return !Empty(); }); + return true; } #ifdef __cpp_lib_hardware_interference_size @@ -201,43 +256,64 @@ private: std::array m_data; - std::condition_variable_any cv; - std::mutex cv_mutex; + std::condition_variable_any producer_cv; + std::mutex producer_cv_mutex; + std::condition_variable_any consumer_cv; + std::mutex consumer_cv_mutex; }; template class MPSCQueue { public: - void TryPush(T&& t) { + bool TryPush(T&& t) { std::scoped_lock lock{write_mutex}; - spsc_queue.TryPush(std::move(t)); + return spsc_queue.TryPush(std::move(t)); } template - void TryPush(Args&&... args) { + bool TryEmplace(Args&&... args) { std::scoped_lock lock{write_mutex}; - spsc_queue.TryPush(std::forward(args)...); + return spsc_queue.TryEmplace(std::forward(args)...); } - void Push(T&& t) { + void PushWait(T&& t) { std::scoped_lock lock{write_mutex}; - spsc_queue.Push(std::move(t)); + spsc_queue.PushWait(std::move(t)); } template - void Push(Args&&... args) { + void EmplaceWait(Args&&... args) { std::scoped_lock lock{write_mutex}; - spsc_queue.Push(std::forward(args)...); + spsc_queue.EmplaceWait(std::forward(args)...); + } + + void PushOverwrite(T&& t) { + std::scoped_lock lock{write_mutex}; + spsc_queue.PushOverwrite(std::move(t)); + } + + template + void EmplaceOverwrite(Args&&... args) { + std::scoped_lock lock{write_mutex}; + spsc_queue.EmplaceOverwrite(std::forward(args)...); } bool TryPop(T& t) { return spsc_queue.TryPop(t); } + void PopWait(T& t) { + spsc_queue.PopWait(t); + } + void PopWait(T& t, std::stop_token stop_token) { spsc_queue.PopWait(t, stop_token); } + T PopWait() { + return spsc_queue.PopWait(); + } + T PopWait(std::stop_token stop_token) { return spsc_queue.PopWait(stop_token); } @@ -262,26 +338,37 @@ private: template class MPMCQueue { public: - void TryPush(T&& t) { + bool TryPush(T&& t) { std::scoped_lock lock{write_mutex}; - spsc_queue.TryPush(std::move(t)); + return spsc_queue.TryPush(std::move(t)); } template - void TryPush(Args&&... args) { + bool TryEmplace(Args&&... args) { std::scoped_lock lock{write_mutex}; - spsc_queue.TryPush(std::forward(args)...); + return spsc_queue.TryEmplace(std::forward(args)...); } - void Push(T&& t) { + void PushWait(T&& t) { std::scoped_lock lock{write_mutex}; - spsc_queue.Push(std::move(t)); + spsc_queue.PushWait(std::move(t)); } template - void Push(Args&&... args) { + void EmplaceWait(Args&&... args) { std::scoped_lock lock{write_mutex}; - spsc_queue.Push(std::forward(args)...); + spsc_queue.EmplaceWait(std::forward(args)...); + } + + void PushOverwrite(T&& t) { + std::scoped_lock lock{write_mutex}; + spsc_queue.PushOverwrite(std::move(t)); + } + + template + void EmplaceOverwrite(Args&&... args) { + std::scoped_lock lock{write_mutex}; + spsc_queue.EmplaceOverwrite(std::forward(args)...); } bool TryPop(T& t) { @@ -289,11 +376,21 @@ public: return spsc_queue.TryPop(t); } + void PopWait(T& t) { + std::scoped_lock lock{read_mutex}; + spsc_queue.PopWait(t); + } + void PopWait(T& t, std::stop_token stop_token) { std::scoped_lock lock{read_mutex}; spsc_queue.PopWait(t, stop_token); } + T PopWait() { + std::scoped_lock lock{read_mutex}; + return spsc_queue.PopWait(); + } + T PopWait(std::stop_token stop_token) { std::scoped_lock lock{read_mutex}; return spsc_queue.PopWait(stop_token); diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp index caa707f2f..95c198135 100755 --- a/src/common/logging/backend.cpp +++ b/src/common/logging/backend.cpp @@ -207,7 +207,7 @@ public: if (!filter.CheckMessage(log_class, log_level)) { return; } - message_queue.Push( + message_queue.EmplaceWait( CreateEntry(log_class, log_level, filename, line_num, function, std::move(message))); } diff --git a/src/common/string_util.cpp b/src/common/string_util.cpp index b9e897f3b..d9b68cf12 100755 --- a/src/common/string_util.cpp +++ b/src/common/string_util.cpp @@ -125,18 +125,18 @@ std::string ReplaceAll(std::string result, const std::string& src, const std::st return result; } -std::string UTF16ToUTF8(const std::u16string& input) { +std::string UTF16ToUTF8(std::u16string_view input) { std::wstring_convert, char16_t> convert; - return convert.to_bytes(input); + return convert.to_bytes(input.data(), input.data() + input.size()); } -std::u16string UTF8ToUTF16(const std::string& input) { +std::u16string UTF8ToUTF16(std::string_view input) { std::wstring_convert, char16_t> convert; - return convert.from_bytes(input); + return convert.from_bytes(input.data(), input.data() + input.size()); } #ifdef _WIN32 -static std::wstring CPToUTF16(u32 code_page, const std::string& input) { +static std::wstring CPToUTF16(u32 code_page, std::string_view input) { const auto size = MultiByteToWideChar(code_page, 0, input.data(), static_cast(input.size()), nullptr, 0); @@ -154,7 +154,7 @@ static std::wstring CPToUTF16(u32 code_page, const std::string& input) { return output; } -std::string UTF16ToUTF8(const std::wstring& input) { +std::string UTF16ToUTF8(std::wstring_view input) { const auto size = WideCharToMultiByte(CP_UTF8, 0, input.data(), static_cast(input.size()), nullptr, 0, nullptr, nullptr); if (size == 0) { @@ -172,7 +172,7 @@ std::string UTF16ToUTF8(const std::wstring& input) { return output; } -std::wstring UTF8ToUTF16W(const std::string& input) { +std::wstring UTF8ToUTF16W(std::string_view input) { return CPToUTF16(CP_UTF8, input); } diff --git a/src/common/string_util.h b/src/common/string_util.h index e03bde0a5..e8cc5ef26 100755 --- a/src/common/string_util.h +++ b/src/common/string_util.h @@ -36,12 +36,12 @@ bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _ [[nodiscard]] std::string ReplaceAll(std::string result, const std::string& src, const std::string& dest); -[[nodiscard]] std::string UTF16ToUTF8(const std::u16string& input); -[[nodiscard]] std::u16string UTF8ToUTF16(const std::string& input); +[[nodiscard]] std::string UTF16ToUTF8(std::u16string_view input); +[[nodiscard]] std::u16string UTF8ToUTF16(std::string_view input); #ifdef _WIN32 -[[nodiscard]] std::string UTF16ToUTF8(const std::wstring& input); -[[nodiscard]] std::wstring UTF8ToUTF16W(const std::string& str); +[[nodiscard]] std::string UTF16ToUTF8(std::wstring_view input); +[[nodiscard]] std::wstring UTF8ToUTF16W(std::string_view str); #endif diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp index acba77ba6..31b09755c 100755 --- a/src/video_core/gpu_thread.cpp +++ b/src/video_core/gpu_thread.cpp @@ -121,7 +121,7 @@ u64 ThreadManager::PushCommand(CommandData&& command_data, bool block) { std::unique_lock lk(state.write_lock); const u64 fence{++state.last_fence}; - state.queue.Push(std::move(command_data), fence, block); + state.queue.EmplaceWait(std::move(command_data), fence, block); if (block) { Common::CondvarWait(state.cv, lk, thread.get_stop_token(), [this, fence] {