diff options
Diffstat (limited to 'src/common')
-rw-r--r-- | src/common/CMakeLists.txt | 6 | ||||
-rw-r--r-- | src/common/assert.h | 18 | ||||
-rw-r--r-- | src/common/bit_util.h | 47 | ||||
-rw-r--r-- | src/common/detached_tasks.cpp | 8 | ||||
-rw-r--r-- | src/common/logging/backend.cpp | 6 | ||||
-rw-r--r-- | src/common/lz4_compression.cpp | 76 | ||||
-rw-r--r-- | src/common/lz4_compression.h | 55 | ||||
-rw-r--r-- | src/common/multi_level_queue.h | 337 | ||||
-rw-r--r-- | src/common/thread.cpp | 37 | ||||
-rw-r--r-- | src/common/thread.h | 14 | ||||
-rw-r--r-- | src/common/threadsafe_queue.h | 4 | ||||
-rw-r--r-- | src/common/zstd_compression.cpp | 53 | ||||
-rw-r--r-- | src/common/zstd_compression.h | 42 |
13 files changed, 644 insertions, 59 deletions
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 43ae8a9e7..1e8e1b215 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -91,6 +91,8 @@ add_library(common STATIC logging/log.h logging/text_formatter.cpp logging/text_formatter.h + lz4_compression.cpp + lz4_compression.h math_util.h memory_hook.cpp memory_hook.h @@ -98,6 +100,7 @@ add_library(common STATIC microprofile.h microprofileui.h misc.cpp + multi_level_queue.h page_table.cpp page_table.h param_package.cpp @@ -122,6 +125,8 @@ add_library(common STATIC uint128.h vector_math.h web_result.h + zstd_compression.cpp + zstd_compression.h ) if(ARCHITECTURE_x86_64) @@ -135,3 +140,4 @@ endif() create_target_directory_groups(common) target_link_libraries(common PUBLIC Boost::boost fmt microprofile) +target_link_libraries(common PRIVATE lz4_static libzstd_static) diff --git a/src/common/assert.h b/src/common/assert.h index 6002f7ab1..4b0e3f64e 100644 --- a/src/common/assert.h +++ b/src/common/assert.h @@ -57,3 +57,21 @@ __declspec(noinline, noreturn) #define UNIMPLEMENTED_IF(cond) ASSERT_MSG(!(cond), "Unimplemented code!") #define UNIMPLEMENTED_IF_MSG(cond, ...) ASSERT_MSG(!(cond), __VA_ARGS__) + +// If the assert is ignored, execute _b_ +#define ASSERT_OR_EXECUTE(_a_, _b_) \ + do { \ + ASSERT(_a_); \ + if (!(_a_)) { \ + _b_ \ + } \ + } while (0) + +// If the assert is ignored, execute _b_ +#define ASSERT_OR_EXECUTE_MSG(_a_, _b_, ...) \ + do { \ + ASSERT_MSG(_a_, __VA_ARGS__); \ + if (!(_a_)) { \ + _b_ \ + } \ + } while (0) diff --git a/src/common/bit_util.h b/src/common/bit_util.h index 1eea17ba1..d032df413 100644 --- a/src/common/bit_util.h +++ b/src/common/bit_util.h @@ -32,7 +32,7 @@ inline u32 CountLeadingZeroes32(u32 value) { return 32; } -inline u64 CountLeadingZeroes64(u64 value) { +inline u32 CountLeadingZeroes64(u64 value) { unsigned long leading_zero = 0; if (_BitScanReverse64(&leading_zero, value) != 0) { @@ -47,15 +47,54 @@ inline u32 CountLeadingZeroes32(u32 value) { return 32; } - return __builtin_clz(value); + return static_cast<u32>(__builtin_clz(value)); } -inline u64 CountLeadingZeroes64(u64 value) { +inline u32 CountLeadingZeroes64(u64 value) { if (value == 0) { return 64; } - return __builtin_clzll(value); + return static_cast<u32>(__builtin_clzll(value)); } #endif + +#ifdef _MSC_VER +inline u32 CountTrailingZeroes32(u32 value) { + unsigned long trailing_zero = 0; + + if (_BitScanForward(&trailing_zero, value) != 0) { + return trailing_zero; + } + + return 32; +} + +inline u32 CountTrailingZeroes64(u64 value) { + unsigned long trailing_zero = 0; + + if (_BitScanForward64(&trailing_zero, value) != 0) { + return trailing_zero; + } + + return 64; +} +#else +inline u32 CountTrailingZeroes32(u32 value) { + if (value == 0) { + return 32; + } + + return static_cast<u32>(__builtin_ctz(value)); +} + +inline u32 CountTrailingZeroes64(u64 value) { + if (value == 0) { + return 64; + } + + return static_cast<u32>(__builtin_ctzll(value)); +} +#endif + } // namespace Common diff --git a/src/common/detached_tasks.cpp b/src/common/detached_tasks.cpp index a347d9e02..f268d6021 100644 --- a/src/common/detached_tasks.cpp +++ b/src/common/detached_tasks.cpp @@ -16,22 +16,22 @@ DetachedTasks::DetachedTasks() { } void DetachedTasks::WaitForAllTasks() { - std::unique_lock<std::mutex> lock(mutex); + std::unique_lock lock{mutex}; cv.wait(lock, [this]() { return count == 0; }); } DetachedTasks::~DetachedTasks() { - std::unique_lock<std::mutex> lock(mutex); + std::unique_lock lock{mutex}; ASSERT(count == 0); instance = nullptr; } void DetachedTasks::AddTask(std::function<void()> task) { - std::unique_lock<std::mutex> lock(instance->mutex); + std::unique_lock lock{instance->mutex}; ++instance->count; std::thread([task{std::move(task)}]() { task(); - std::unique_lock<std::mutex> lock(instance->mutex); + std::unique_lock lock{instance->mutex}; --instance->count; std::notify_all_at_thread_exit(instance->cv, std::move(lock)); }) diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp index 4462ff3fb..a03179520 100644 --- a/src/common/logging/backend.cpp +++ b/src/common/logging/backend.cpp @@ -46,12 +46,12 @@ public: } void AddBackend(std::unique_ptr<Backend> backend) { - std::lock_guard<std::mutex> lock(writing_mutex); + std::lock_guard lock{writing_mutex}; backends.push_back(std::move(backend)); } void RemoveBackend(std::string_view backend_name) { - std::lock_guard<std::mutex> lock(writing_mutex); + std::lock_guard lock{writing_mutex}; const auto it = std::remove_if(backends.begin(), backends.end(), [&backend_name](const auto& i) { return backend_name == i->GetName(); }); @@ -80,7 +80,7 @@ private: backend_thread = std::thread([&] { Entry entry; auto write_logs = [&](Entry& e) { - std::lock_guard<std::mutex> lock(writing_mutex); + std::lock_guard lock{writing_mutex}; for (const auto& backend : backends) { backend->Write(e); } diff --git a/src/common/lz4_compression.cpp b/src/common/lz4_compression.cpp new file mode 100644 index 000000000..ade6759bb --- /dev/null +++ b/src/common/lz4_compression.cpp @@ -0,0 +1,76 @@ +// Copyright 2019 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <algorithm> +#include <lz4hc.h> + +#include "common/assert.h" +#include "common/lz4_compression.h" + +namespace Common::Compression { + +std::vector<u8> CompressDataLZ4(const u8* source, std::size_t source_size) { + ASSERT_MSG(source_size <= LZ4_MAX_INPUT_SIZE, "Source size exceeds LZ4 maximum input size"); + + const auto source_size_int = static_cast<int>(source_size); + const int max_compressed_size = LZ4_compressBound(source_size_int); + std::vector<u8> compressed(max_compressed_size); + + const int compressed_size = LZ4_compress_default(reinterpret_cast<const char*>(source), + reinterpret_cast<char*>(compressed.data()), + source_size_int, max_compressed_size); + + if (compressed_size <= 0) { + // Compression failed + return {}; + } + + compressed.resize(compressed_size); + + return compressed; +} + +std::vector<u8> CompressDataLZ4HC(const u8* source, std::size_t source_size, + s32 compression_level) { + ASSERT_MSG(source_size <= LZ4_MAX_INPUT_SIZE, "Source size exceeds LZ4 maximum input size"); + + compression_level = std::clamp(compression_level, LZ4HC_CLEVEL_MIN, LZ4HC_CLEVEL_MAX); + + const auto source_size_int = static_cast<int>(source_size); + const int max_compressed_size = LZ4_compressBound(source_size_int); + std::vector<u8> compressed(max_compressed_size); + + const int compressed_size = LZ4_compress_HC( + reinterpret_cast<const char*>(source), reinterpret_cast<char*>(compressed.data()), + source_size_int, max_compressed_size, compression_level); + + if (compressed_size <= 0) { + // Compression failed + return {}; + } + + compressed.resize(compressed_size); + + return compressed; +} + +std::vector<u8> CompressDataLZ4HCMax(const u8* source, std::size_t source_size) { + return CompressDataLZ4HC(source, source_size, LZ4HC_CLEVEL_MAX); +} + +std::vector<u8> DecompressDataLZ4(const std::vector<u8>& compressed, + std::size_t uncompressed_size) { + std::vector<u8> uncompressed(uncompressed_size); + const int size_check = LZ4_decompress_safe(reinterpret_cast<const char*>(compressed.data()), + reinterpret_cast<char*>(uncompressed.data()), + static_cast<int>(compressed.size()), + static_cast<int>(uncompressed.size())); + if (static_cast<int>(uncompressed_size) != size_check) { + // Decompression failed + return {}; + } + return uncompressed; +} + +} // namespace Common::Compression diff --git a/src/common/lz4_compression.h b/src/common/lz4_compression.h new file mode 100644 index 000000000..fe2231a6c --- /dev/null +++ b/src/common/lz4_compression.h @@ -0,0 +1,55 @@ +// Copyright 2019 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <vector> + +#include "common/common_types.h" + +namespace Common::Compression { + +/** + * Compresses a source memory region with LZ4 and returns the compressed data in a vector. + * + * @param source the uncompressed source memory region. + * @param source_size the size in bytes of the uncompressed source memory region. + * + * @return the compressed data. + */ +std::vector<u8> CompressDataLZ4(const u8* source, std::size_t source_size); + +/** + * Utilizes the LZ4 subalgorithm LZ4HC with the specified compression level. Higher compression + * levels result in a smaller compressed size, but require more CPU time for compression. The + * compression level has almost no impact on decompression speed. Data compressed with LZ4HC can + * also be decompressed with the default LZ4 decompression. + * + * @param source the uncompressed source memory region. + * @param source_size the size in bytes of the uncompressed source memory region. + * @param compression_level the used compression level. Should be between 3 and 12. + * + * @return the compressed data. + */ +std::vector<u8> CompressDataLZ4HC(const u8* source, std::size_t source_size, s32 compression_level); + +/** + * Utilizes the LZ4 subalgorithm LZ4HC with the highest possible compression level. + * + * @param source the uncompressed source memory region. + * @param source_size the size in bytes of the uncompressed source memory region. + * + * @return the compressed data. + */ +std::vector<u8> CompressDataLZ4HCMax(const u8* source, std::size_t source_size); + +/** + * Decompresses a source memory region with LZ4 and returns the uncompressed data in a vector. + * + * @param compressed the compressed source memory region. + * @param uncompressed_size the size in bytes of the uncompressed data. + * + * @return the decompressed data. + */ +std::vector<u8> DecompressDataLZ4(const std::vector<u8>& compressed, std::size_t uncompressed_size); + +} // namespace Common::Compression
\ No newline at end of file diff --git a/src/common/multi_level_queue.h b/src/common/multi_level_queue.h new file mode 100644 index 000000000..9cb448f56 --- /dev/null +++ b/src/common/multi_level_queue.h @@ -0,0 +1,337 @@ +// Copyright 2019 TuxSH +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <array> +#include <iterator> +#include <list> +#include <utility> + +#include "common/bit_util.h" +#include "common/common_types.h" + +namespace Common { + +/** + * A MultiLevelQueue is a type of priority queue which has the following characteristics: + * - iteratable through each of its elements. + * - back can be obtained. + * - O(1) add, lookup (both front and back) + * - discrete priorities and a max of 64 priorities (limited domain) + * This type of priority queue is normaly used for managing threads within an scheduler + */ +template <typename T, std::size_t Depth> +class MultiLevelQueue { +public: + using value_type = T; + using reference = value_type&; + using const_reference = const value_type&; + using pointer = value_type*; + using const_pointer = const value_type*; + + using difference_type = typename std::pointer_traits<pointer>::difference_type; + using size_type = std::size_t; + + template <bool is_constant> + class iterator_impl { + public: + using iterator_category = std::bidirectional_iterator_tag; + using value_type = T; + using pointer = std::conditional_t<is_constant, T*, const T*>; + using reference = std::conditional_t<is_constant, const T&, T&>; + using difference_type = typename std::pointer_traits<pointer>::difference_type; + + friend bool operator==(const iterator_impl& lhs, const iterator_impl& rhs) { + if (lhs.IsEnd() && rhs.IsEnd()) + return true; + return std::tie(lhs.current_priority, lhs.it) == std::tie(rhs.current_priority, rhs.it); + } + + friend bool operator!=(const iterator_impl& lhs, const iterator_impl& rhs) { + return !operator==(lhs, rhs); + } + + reference operator*() const { + return *it; + } + + pointer operator->() const { + return it.operator->(); + } + + iterator_impl& operator++() { + if (IsEnd()) { + return *this; + } + + ++it; + + if (it == GetEndItForPrio()) { + u64 prios = mlq.used_priorities; + prios &= ~((1ULL << (current_priority + 1)) - 1); + if (prios == 0) { + current_priority = static_cast<u32>(mlq.depth()); + } else { + current_priority = CountTrailingZeroes64(prios); + it = GetBeginItForPrio(); + } + } + return *this; + } + + iterator_impl& operator--() { + if (IsEnd()) { + if (mlq.used_priorities != 0) { + current_priority = 63 - CountLeadingZeroes64(mlq.used_priorities); + it = GetEndItForPrio(); + --it; + } + } else if (it == GetBeginItForPrio()) { + u64 prios = mlq.used_priorities; + prios &= (1ULL << current_priority) - 1; + if (prios != 0) { + current_priority = CountTrailingZeroes64(prios); + it = GetEndItForPrio(); + --it; + } + } else { + --it; + } + return *this; + } + + iterator_impl operator++(int) { + const iterator_impl v{*this}; + ++(*this); + return v; + } + + iterator_impl operator--(int) { + const iterator_impl v{*this}; + --(*this); + return v; + } + + // allow implicit const->non-const + iterator_impl(const iterator_impl<false>& other) + : mlq(other.mlq), it(other.it), current_priority(other.current_priority) {} + + iterator_impl(const iterator_impl<true>& other) + : mlq(other.mlq), it(other.it), current_priority(other.current_priority) {} + + iterator_impl& operator=(const iterator_impl<false>& other) { + mlq = other.mlq; + it = other.it; + current_priority = other.current_priority; + return *this; + } + + friend class iterator_impl<true>; + iterator_impl() = default; + + private: + friend class MultiLevelQueue; + using container_ref = + std::conditional_t<is_constant, const MultiLevelQueue&, MultiLevelQueue&>; + using list_iterator = std::conditional_t<is_constant, typename std::list<T>::const_iterator, + typename std::list<T>::iterator>; + + explicit iterator_impl(container_ref mlq, list_iterator it, u32 current_priority) + : mlq(mlq), it(it), current_priority(current_priority) {} + explicit iterator_impl(container_ref mlq, u32 current_priority) + : mlq(mlq), it(), current_priority(current_priority) {} + + bool IsEnd() const { + return current_priority == mlq.depth(); + } + + list_iterator GetBeginItForPrio() const { + return mlq.levels[current_priority].begin(); + } + + list_iterator GetEndItForPrio() const { + return mlq.levels[current_priority].end(); + } + + container_ref mlq; + list_iterator it; + u32 current_priority; + }; + + using iterator = iterator_impl<false>; + using const_iterator = iterator_impl<true>; + + void add(const T& element, u32 priority, bool send_back = true) { + if (send_back) + levels[priority].push_back(element); + else + levels[priority].push_front(element); + used_priorities |= 1ULL << priority; + } + + void remove(const T& element, u32 priority) { + auto it = ListIterateTo(levels[priority], element); + if (it == levels[priority].end()) + return; + levels[priority].erase(it); + if (levels[priority].empty()) { + used_priorities &= ~(1ULL << priority); + } + } + + void adjust(const T& element, u32 old_priority, u32 new_priority, bool adjust_front = false) { + remove(element, old_priority); + add(element, new_priority, !adjust_front); + } + void adjust(const_iterator it, u32 old_priority, u32 new_priority, bool adjust_front = false) { + adjust(*it, old_priority, new_priority, adjust_front); + } + + void transfer_to_front(const T& element, u32 priority, MultiLevelQueue& other) { + ListSplice(other.levels[priority], other.levels[priority].begin(), levels[priority], + ListIterateTo(levels[priority], element)); + + other.used_priorities |= 1ULL << priority; + + if (levels[priority].empty()) { + used_priorities &= ~(1ULL << priority); + } + } + + void transfer_to_front(const_iterator it, u32 priority, MultiLevelQueue& other) { + transfer_to_front(*it, priority, other); + } + + void transfer_to_back(const T& element, u32 priority, MultiLevelQueue& other) { + ListSplice(other.levels[priority], other.levels[priority].end(), levels[priority], + ListIterateTo(levels[priority], element)); + + other.used_priorities |= 1ULL << priority; + + if (levels[priority].empty()) { + used_priorities &= ~(1ULL << priority); + } + } + + void transfer_to_back(const_iterator it, u32 priority, MultiLevelQueue& other) { + transfer_to_back(*it, priority, other); + } + + void yield(u32 priority, std::size_t n = 1) { + ListShiftForward(levels[priority], n); + } + + std::size_t depth() const { + return Depth; + } + + std::size_t size(u32 priority) const { + return levels[priority].size(); + } + + std::size_t size() const { + u64 priorities = used_priorities; + std::size_t size = 0; + while (priorities != 0) { + const u64 current_priority = CountTrailingZeroes64(priorities); + size += levels[current_priority].size(); + priorities &= ~(1ULL << current_priority); + } + return size; + } + + bool empty() const { + return used_priorities == 0; + } + + bool empty(u32 priority) const { + return (used_priorities & (1ULL << priority)) == 0; + } + + u32 highest_priority_set(u32 max_priority = 0) const { + const u64 priorities = + max_priority == 0 ? used_priorities : (used_priorities & ~((1ULL << max_priority) - 1)); + return priorities == 0 ? Depth : static_cast<u32>(CountTrailingZeroes64(priorities)); + } + + u32 lowest_priority_set(u32 min_priority = Depth - 1) const { + const u64 priorities = min_priority >= Depth - 1 + ? used_priorities + : (used_priorities & ((1ULL << (min_priority + 1)) - 1)); + return priorities == 0 ? Depth : 63 - CountLeadingZeroes64(priorities); + } + + const_iterator cbegin(u32 max_prio = 0) const { + const u32 priority = highest_priority_set(max_prio); + return priority == Depth ? cend() + : const_iterator{*this, levels[priority].cbegin(), priority}; + } + const_iterator begin(u32 max_prio = 0) const { + return cbegin(max_prio); + } + iterator begin(u32 max_prio = 0) { + const u32 priority = highest_priority_set(max_prio); + return priority == Depth ? end() : iterator{*this, levels[priority].begin(), priority}; + } + + const_iterator cend(u32 min_prio = Depth - 1) const { + return min_prio == Depth - 1 ? const_iterator{*this, Depth} : cbegin(min_prio + 1); + } + const_iterator end(u32 min_prio = Depth - 1) const { + return cend(min_prio); + } + iterator end(u32 min_prio = Depth - 1) { + return min_prio == Depth - 1 ? iterator{*this, Depth} : begin(min_prio + 1); + } + + T& front(u32 max_priority = 0) { + const u32 priority = highest_priority_set(max_priority); + return levels[priority == Depth ? 0 : priority].front(); + } + const T& front(u32 max_priority = 0) const { + const u32 priority = highest_priority_set(max_priority); + return levels[priority == Depth ? 0 : priority].front(); + } + + T back(u32 min_priority = Depth - 1) { + const u32 priority = lowest_priority_set(min_priority); // intended + return levels[priority == Depth ? 63 : priority].back(); + } + const T& back(u32 min_priority = Depth - 1) const { + const u32 priority = lowest_priority_set(min_priority); // intended + return levels[priority == Depth ? 63 : priority].back(); + } + +private: + using const_list_iterator = typename std::list<T>::const_iterator; + + static void ListShiftForward(std::list<T>& list, const std::size_t shift = 1) { + if (shift >= list.size()) { + return; + } + + const auto begin_range = list.begin(); + const auto end_range = std::next(begin_range, shift); + list.splice(list.end(), list, begin_range, end_range); + } + + static void ListSplice(std::list<T>& in_list, const_list_iterator position, + std::list<T>& out_list, const_list_iterator element) { + in_list.splice(position, out_list, element); + } + + static const_list_iterator ListIterateTo(const std::list<T>& list, const T& element) { + auto it = list.cbegin(); + while (it != list.cend() && *it != element) { + ++it; + } + return it; + } + + std::array<std::list<T>, Depth> levels; + u64 used_priorities = 0; +}; + +} // namespace Common diff --git a/src/common/thread.cpp b/src/common/thread.cpp index 5144c0d9f..fe7a420cc 100644 --- a/src/common/thread.cpp +++ b/src/common/thread.cpp @@ -27,18 +27,6 @@ namespace Common { #ifdef _MSC_VER -void SetThreadAffinity(std::thread::native_handle_type thread, u32 mask) { - SetThreadAffinityMask(thread, mask); -} - -void SetCurrentThreadAffinity(u32 mask) { - SetThreadAffinityMask(GetCurrentThread(), mask); -} - -void SwitchCurrentThread() { - SwitchToThread(); -} - // Sets the debugger-visible name of the current thread. // Uses undocumented (actually, it is now documented) trick. // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vsdebug/html/vxtsksettingthreadname.asp @@ -70,31 +58,6 @@ void SetCurrentThreadName(const char* name) { #else // !MSVC_VER, so must be POSIX threads -void SetThreadAffinity(std::thread::native_handle_type thread, u32 mask) { -#ifdef __APPLE__ - thread_policy_set(pthread_mach_thread_np(thread), THREAD_AFFINITY_POLICY, (integer_t*)&mask, 1); -#elif (defined __linux__ || defined __FreeBSD__) && !(defined ANDROID) - cpu_set_t cpu_set; - CPU_ZERO(&cpu_set); - - for (int i = 0; i != sizeof(mask) * 8; ++i) - if ((mask >> i) & 1) - CPU_SET(i, &cpu_set); - - pthread_setaffinity_np(thread, sizeof(cpu_set), &cpu_set); -#endif -} - -void SetCurrentThreadAffinity(u32 mask) { - SetThreadAffinity(pthread_self(), mask); -} - -#ifndef _WIN32 -void SwitchCurrentThread() { - usleep(1000 * 1); -} -#endif - // MinGW with the POSIX threading model does not support pthread_setname_np #if !defined(_WIN32) || defined(_MSC_VER) void SetCurrentThreadName(const char* name) { diff --git a/src/common/thread.h b/src/common/thread.h index 2cf74452d..0cfd98be6 100644 --- a/src/common/thread.h +++ b/src/common/thread.h @@ -9,14 +9,13 @@ #include <cstddef> #include <mutex> #include <thread> -#include "common/common_types.h" namespace Common { class Event { public: void Set() { - std::lock_guard<std::mutex> lk(mutex); + std::lock_guard lk{mutex}; if (!is_set) { is_set = true; condvar.notify_one(); @@ -24,14 +23,14 @@ public: } void Wait() { - std::unique_lock<std::mutex> lk(mutex); + std::unique_lock lk{mutex}; condvar.wait(lk, [&] { return is_set; }); is_set = false; } template <class Clock, class Duration> bool WaitUntil(const std::chrono::time_point<Clock, Duration>& time) { - std::unique_lock<std::mutex> lk(mutex); + std::unique_lock lk{mutex}; if (!condvar.wait_until(lk, time, [this] { return is_set; })) return false; is_set = false; @@ -39,7 +38,7 @@ public: } void Reset() { - std::unique_lock<std::mutex> lk(mutex); + std::unique_lock lk{mutex}; // no other action required, since wait loops on the predicate and any lingering signal will // get cleared on the first iteration is_set = false; @@ -57,7 +56,7 @@ public: /// Blocks until all "count" threads have called Sync() void Sync() { - std::unique_lock<std::mutex> lk(mutex); + std::unique_lock lk{mutex}; const std::size_t current_generation = generation; if (++waiting == count) { @@ -78,9 +77,6 @@ private: std::size_t generation = 0; // Incremented once each time the barrier is used }; -void SetThreadAffinity(std::thread::native_handle_type thread, u32 mask); -void SetCurrentThreadAffinity(u32 mask); -void SwitchCurrentThread(); // On Linux, this is equal to sleep 1ms void SetCurrentThreadName(const char* name); } // namespace Common diff --git a/src/common/threadsafe_queue.h b/src/common/threadsafe_queue.h index 821e8536a..e714ba5b3 100644 --- a/src/common/threadsafe_queue.h +++ b/src/common/threadsafe_queue.h @@ -78,7 +78,7 @@ public: T PopWait() { if (Empty()) { - std::unique_lock<std::mutex> lock(cv_mutex); + std::unique_lock lock{cv_mutex}; cv.wait(lock, [this]() { return !Empty(); }); } T t; @@ -137,7 +137,7 @@ public: template <typename Arg> void Push(Arg&& t) { - std::lock_guard<std::mutex> lock(write_lock); + std::lock_guard lock{write_lock}; spsc_queue.Push(t); } diff --git a/src/common/zstd_compression.cpp b/src/common/zstd_compression.cpp new file mode 100644 index 000000000..60a35c67c --- /dev/null +++ b/src/common/zstd_compression.cpp @@ -0,0 +1,53 @@ +// Copyright 2019 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <algorithm> +#include <zstd.h> + +#include "common/assert.h" +#include "common/zstd_compression.h" + +namespace Common::Compression { + +std::vector<u8> CompressDataZSTD(const u8* source, std::size_t source_size, s32 compression_level) { + compression_level = std::clamp(compression_level, 1, ZSTD_maxCLevel()); + + const std::size_t max_compressed_size = ZSTD_compressBound(source_size); + std::vector<u8> compressed(max_compressed_size); + + const std::size_t compressed_size = + ZSTD_compress(compressed.data(), compressed.size(), source, source_size, compression_level); + + if (ZSTD_isError(compressed_size)) { + // Compression failed + return {}; + } + + compressed.resize(compressed_size); + + return compressed; +} + +std::vector<u8> CompressDataZSTDDefault(const u8* source, std::size_t source_size) { + return CompressDataZSTD(source, source_size, ZSTD_CLEVEL_DEFAULT); +} + +std::vector<u8> DecompressDataZSTD(const std::vector<u8>& compressed) { + const std::size_t decompressed_size = + ZSTD_getDecompressedSize(compressed.data(), compressed.size()); + std::vector<u8> decompressed(decompressed_size); + + const std::size_t uncompressed_result_size = ZSTD_decompress( + decompressed.data(), decompressed.size(), compressed.data(), compressed.size()); + + if (decompressed_size != uncompressed_result_size || ZSTD_isError(uncompressed_result_size)) { + // Decompression failed + return {}; + } + return decompressed; +} + +} // namespace Common::Compression diff --git a/src/common/zstd_compression.h b/src/common/zstd_compression.h new file mode 100644 index 000000000..e0a64b035 --- /dev/null +++ b/src/common/zstd_compression.h @@ -0,0 +1,42 @@ +// Copyright 2019 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <vector> + +#include "common/common_types.h" + +namespace Common::Compression { + +/** + * Compresses a source memory region with Zstandard and returns the compressed data in a vector. + * + * @param source the uncompressed source memory region. + * @param source_size the size in bytes of the uncompressed source memory region. + * @param compression_level the used compression level. Should be between 1 and 22. + * + * @return the compressed data. + */ +std::vector<u8> CompressDataZSTD(const u8* source, std::size_t source_size, s32 compression_level); + +/** + * Compresses a source memory region with Zstandard with the default compression level and returns + * the compressed data in a vector. + * + * @param source the uncompressed source memory region. + * @param source_size the size in bytes of the uncompressed source memory region. + * + * @return the compressed data. + */ +std::vector<u8> CompressDataZSTDDefault(const u8* source, std::size_t source_size); + +/** + * Decompresses a source memory region with Zstandard and returns the uncompressed data in a vector. + * + * @param compressed the compressed source memory region. + * + * @return the decompressed data. + */ +std::vector<u8> DecompressDataZSTD(const std::vector<u8>& compressed); + +} // namespace Common::Compression
\ No newline at end of file |