diff options
Diffstat (limited to 'src/common')
| -rw-r--r-- | src/common/CMakeLists.txt | 11 | ||||
| -rw-r--r-- | src/common/bit_field.h | 28 | ||||
| -rw-r--r-- | src/common/bit_util.h | 39 | ||||
| -rw-r--r-- | src/common/color.h | 40 | ||||
| -rw-r--r-- | src/common/common_types.h | 7 | ||||
| -rw-r--r-- | src/common/detached_tasks.cpp | 8 | ||||
| -rw-r--r-- | src/common/logging/backend.cpp | 76 | ||||
| -rw-r--r-- | src/common/logging/backend.h | 5 | ||||
| -rw-r--r-- | src/common/logging/log.h | 1 | ||||
| -rw-r--r-- | src/common/lz4_compression.cpp | 78 | ||||
| -rw-r--r-- | src/common/lz4_compression.h | 55 | ||||
| -rw-r--r-- | src/common/math_util.h | 4 | ||||
| -rw-r--r-- | src/common/memory_hook.cpp | 11 | ||||
| -rw-r--r-- | src/common/memory_hook.h | 47 | ||||
| -rw-r--r-- | src/common/multi_level_queue.h | 337 | ||||
| -rw-r--r-- | src/common/page_table.cpp | 31 | ||||
| -rw-r--r-- | src/common/page_table.h | 84 | ||||
| -rw-r--r-- | src/common/quaternion.h | 10 | ||||
| -rw-r--r-- | src/common/swap.h | 180 | ||||
| -rw-r--r-- | src/common/thread.cpp | 37 | ||||
| -rw-r--r-- | src/common/thread.h | 14 | ||||
| -rw-r--r-- | src/common/thread_queue_list.h | 6 | ||||
| -rw-r--r-- | src/common/threadsafe_queue.h | 55 | ||||
| -rw-r--r-- | src/common/uint128.cpp | 45 | ||||
| -rw-r--r-- | src/common/uint128.h | 19 | ||||
| -rw-r--r-- | src/common/vector_math.h | 4 | 
26 files changed, 1032 insertions, 200 deletions
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index bdd885273..5639021d3 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -47,6 +47,7 @@ add_custom_command(OUTPUT scm_rev.cpp        "${VIDEO_CORE}/shader/decode/integer_set.cpp"        "${VIDEO_CORE}/shader/decode/integer_set_predicate.cpp"        "${VIDEO_CORE}/shader/decode/memory.cpp" +      "${VIDEO_CORE}/shader/decode/texture.cpp"        "${VIDEO_CORE}/shader/decode/other.cpp"        "${VIDEO_CORE}/shader/decode/predicate_set_predicate.cpp"        "${VIDEO_CORE}/shader/decode/predicate_set_register.cpp" @@ -90,11 +91,18 @@ 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      microprofile.cpp      microprofile.h      microprofileui.h      misc.cpp +    multi_level_queue.h +    page_table.cpp +    page_table.h      param_package.cpp      param_package.h      quaternion.h @@ -113,6 +121,8 @@ add_library(common STATIC      threadsafe_queue.h      timer.cpp      timer.h +    uint128.cpp +    uint128.h      vector_math.h      web_result.h  ) @@ -128,3 +138,4 @@ endif()  create_target_directory_groups(common)  target_link_libraries(common PUBLIC Boost::boost fmt microprofile) +target_link_libraries(common PRIVATE lz4_static) diff --git a/src/common/bit_field.h b/src/common/bit_field.h index 21e07925d..902e668e3 100644 --- a/src/common/bit_field.h +++ b/src/common/bit_field.h @@ -34,6 +34,7 @@  #include <limits>  #include <type_traits>  #include "common/common_funcs.h" +#include "common/swap.h"  /*   * Abstract bitfield class @@ -108,15 +109,9 @@   * symptoms.   */  #pragma pack(1) -template <std::size_t Position, std::size_t Bits, typename T> +template <std::size_t Position, std::size_t Bits, typename T, typename EndianTag = LETag>  struct BitField {  private: -    // We hide the copy assigment operator here, because the default copy -    // assignment would copy the full storage value, rather than just the bits -    // relevant to this particular bit field. -    // We don't delete it because we want BitField to be trivially copyable. -    constexpr BitField& operator=(const BitField&) = default; -      // UnderlyingType is T for non-enum types and the underlying type of T if      // T is an enumeration. Note that T is wrapped within an enable_if in the      // former case to workaround compile errors which arise when using @@ -127,6 +122,8 @@ private:      // We store the value as the unsigned type to avoid undefined behaviour on value shifting      using StorageType = std::make_unsigned_t<UnderlyingType>; +    using StorageTypeWithEndian = typename AddEndian<StorageType, EndianTag>::type; +  public:      /// Constants to allow limited introspection of fields if needed      static constexpr std::size_t position = Position; @@ -163,16 +160,20 @@ public:      BitField(T val) = delete;      BitField& operator=(T val) = delete; -    // Force default constructor to be created -    // so that we can use this within unions -    constexpr BitField() = default; +    constexpr BitField() noexcept = default; + +    constexpr BitField(const BitField&) noexcept = default; +    constexpr BitField& operator=(const BitField&) noexcept = default; + +    constexpr BitField(BitField&&) noexcept = default; +    constexpr BitField& operator=(BitField&&) noexcept = default;      constexpr FORCE_INLINE operator T() const {          return Value();      }      constexpr FORCE_INLINE void Assign(const T& value) { -        storage = (storage & ~mask) | FormatValue(value); +        storage = (static_cast<StorageType>(storage) & ~mask) | FormatValue(value);      }      constexpr T Value() const { @@ -184,7 +185,7 @@ public:      }  private: -    StorageType storage; +    StorageTypeWithEndian storage;      static_assert(bits + position <= 8 * sizeof(T), "Bitfield out of range"); @@ -195,3 +196,6 @@ private:      static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable in a BitField");  };  #pragma pack() + +template <std::size_t Position, std::size_t Bits, typename T> +using BitFieldBE = BitField<Position, Bits, T, BETag>; diff --git a/src/common/bit_util.h b/src/common/bit_util.h index 1eea17ba1..a4f9ed4aa 100644 --- a/src/common/bit_util.h +++ b/src/common/bit_util.h @@ -58,4 +58,43 @@ inline u64 CountLeadingZeroes64(u64 value) {      return __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 u64 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 __builtin_ctz(value); +} + +inline u64 CountTrailingZeroes64(u64 value) { +    if (value == 0) { +        return 64; +    } + +    return __builtin_ctzll(value); +} +#endif +  } // namespace Common diff --git a/src/common/color.h b/src/common/color.h index 0379040be..3a2222077 100644 --- a/src/common/color.h +++ b/src/common/color.h @@ -55,36 +55,36 @@ constexpr u8 Convert8To6(u8 value) {  /**   * Decode a color stored in RGBA8 format   * @param bytes Pointer to encoded source color - * @return Result color decoded as Math::Vec4<u8> + * @return Result color decoded as Common::Vec4<u8>   */ -inline Math::Vec4<u8> DecodeRGBA8(const u8* bytes) { +inline Common::Vec4<u8> DecodeRGBA8(const u8* bytes) {      return {bytes[3], bytes[2], bytes[1], bytes[0]};  }  /**   * Decode a color stored in RGB8 format   * @param bytes Pointer to encoded source color - * @return Result color decoded as Math::Vec4<u8> + * @return Result color decoded as Common::Vec4<u8>   */ -inline Math::Vec4<u8> DecodeRGB8(const u8* bytes) { +inline Common::Vec4<u8> DecodeRGB8(const u8* bytes) {      return {bytes[2], bytes[1], bytes[0], 255};  }  /**   * Decode a color stored in RG8 (aka HILO8) format   * @param bytes Pointer to encoded source color - * @return Result color decoded as Math::Vec4<u8> + * @return Result color decoded as Common::Vec4<u8>   */ -inline Math::Vec4<u8> DecodeRG8(const u8* bytes) { +inline Common::Vec4<u8> DecodeRG8(const u8* bytes) {      return {bytes[1], bytes[0], 0, 255};  }  /**   * Decode a color stored in RGB565 format   * @param bytes Pointer to encoded source color - * @return Result color decoded as Math::Vec4<u8> + * @return Result color decoded as Common::Vec4<u8>   */ -inline Math::Vec4<u8> DecodeRGB565(const u8* bytes) { +inline Common::Vec4<u8> DecodeRGB565(const u8* bytes) {      u16_le pixel;      std::memcpy(&pixel, bytes, sizeof(pixel));      return {Convert5To8((pixel >> 11) & 0x1F), Convert6To8((pixel >> 5) & 0x3F), @@ -94,9 +94,9 @@ inline Math::Vec4<u8> DecodeRGB565(const u8* bytes) {  /**   * Decode a color stored in RGB5A1 format   * @param bytes Pointer to encoded source color - * @return Result color decoded as Math::Vec4<u8> + * @return Result color decoded as Common::Vec4<u8>   */ -inline Math::Vec4<u8> DecodeRGB5A1(const u8* bytes) { +inline Common::Vec4<u8> DecodeRGB5A1(const u8* bytes) {      u16_le pixel;      std::memcpy(&pixel, bytes, sizeof(pixel));      return {Convert5To8((pixel >> 11) & 0x1F), Convert5To8((pixel >> 6) & 0x1F), @@ -106,9 +106,9 @@ inline Math::Vec4<u8> DecodeRGB5A1(const u8* bytes) {  /**   * Decode a color stored in RGBA4 format   * @param bytes Pointer to encoded source color - * @return Result color decoded as Math::Vec4<u8> + * @return Result color decoded as Common::Vec4<u8>   */ -inline Math::Vec4<u8> DecodeRGBA4(const u8* bytes) { +inline Common::Vec4<u8> DecodeRGBA4(const u8* bytes) {      u16_le pixel;      std::memcpy(&pixel, bytes, sizeof(pixel));      return {Convert4To8((pixel >> 12) & 0xF), Convert4To8((pixel >> 8) & 0xF), @@ -138,9 +138,9 @@ inline u32 DecodeD24(const u8* bytes) {  /**   * Decode a depth value and a stencil value stored in D24S8 format   * @param bytes Pointer to encoded source values - * @return Resulting values stored as a Math::Vec2 + * @return Resulting values stored as a Common::Vec2   */ -inline Math::Vec2<u32> DecodeD24S8(const u8* bytes) { +inline Common::Vec2<u32> DecodeD24S8(const u8* bytes) {      return {static_cast<u32>((bytes[2] << 16) | (bytes[1] << 8) | bytes[0]), bytes[3]};  } @@ -149,7 +149,7 @@ inline Math::Vec2<u32> DecodeD24S8(const u8* bytes) {   * @param color Source color to encode   * @param bytes Destination pointer to store encoded color   */ -inline void EncodeRGBA8(const Math::Vec4<u8>& color, u8* bytes) { +inline void EncodeRGBA8(const Common::Vec4<u8>& color, u8* bytes) {      bytes[3] = color.r();      bytes[2] = color.g();      bytes[1] = color.b(); @@ -161,7 +161,7 @@ inline void EncodeRGBA8(const Math::Vec4<u8>& color, u8* bytes) {   * @param color Source color to encode   * @param bytes Destination pointer to store encoded color   */ -inline void EncodeRGB8(const Math::Vec4<u8>& color, u8* bytes) { +inline void EncodeRGB8(const Common::Vec4<u8>& color, u8* bytes) {      bytes[2] = color.r();      bytes[1] = color.g();      bytes[0] = color.b(); @@ -172,7 +172,7 @@ inline void EncodeRGB8(const Math::Vec4<u8>& color, u8* bytes) {   * @param color Source color to encode   * @param bytes Destination pointer to store encoded color   */ -inline void EncodeRG8(const Math::Vec4<u8>& color, u8* bytes) { +inline void EncodeRG8(const Common::Vec4<u8>& color, u8* bytes) {      bytes[1] = color.r();      bytes[0] = color.g();  } @@ -181,7 +181,7 @@ inline void EncodeRG8(const Math::Vec4<u8>& color, u8* bytes) {   * @param color Source color to encode   * @param bytes Destination pointer to store encoded color   */ -inline void EncodeRGB565(const Math::Vec4<u8>& color, u8* bytes) { +inline void EncodeRGB565(const Common::Vec4<u8>& color, u8* bytes) {      const u16_le data =          (Convert8To5(color.r()) << 11) | (Convert8To6(color.g()) << 5) | Convert8To5(color.b()); @@ -193,7 +193,7 @@ inline void EncodeRGB565(const Math::Vec4<u8>& color, u8* bytes) {   * @param color Source color to encode   * @param bytes Destination pointer to store encoded color   */ -inline void EncodeRGB5A1(const Math::Vec4<u8>& color, u8* bytes) { +inline void EncodeRGB5A1(const Common::Vec4<u8>& color, u8* bytes) {      const u16_le data = (Convert8To5(color.r()) << 11) | (Convert8To5(color.g()) << 6) |                          (Convert8To5(color.b()) << 1) | Convert8To1(color.a()); @@ -205,7 +205,7 @@ inline void EncodeRGB5A1(const Math::Vec4<u8>& color, u8* bytes) {   * @param color Source color to encode   * @param bytes Destination pointer to store encoded color   */ -inline void EncodeRGBA4(const Math::Vec4<u8>& color, u8* bytes) { +inline void EncodeRGBA4(const Common::Vec4<u8>& color, u8* bytes) {      const u16 data = (Convert8To4(color.r()) << 12) | (Convert8To4(color.g()) << 8) |                       (Convert8To4(color.b()) << 4) | Convert8To4(color.a()); diff --git a/src/common/common_types.h b/src/common/common_types.h index 6b1766dca..4cec89fbd 100644 --- a/src/common/common_types.h +++ b/src/common/common_types.h @@ -40,10 +40,9 @@ using s64 = std::int64_t; ///< 64-bit signed int  using f32 = float;  ///< 32-bit floating point  using f64 = double; ///< 64-bit floating point -// TODO: It would be nice to eventually replace these with strong types that prevent accidental -// conversion between each other. -using VAddr = u64; ///< Represents a pointer in the userspace virtual address space. -using PAddr = u64; ///< Represents a pointer in the ARM11 physical address space. +using VAddr = u64;    ///< Represents a pointer in the userspace virtual address space. +using PAddr = u64;    ///< Represents a pointer in the ARM11 physical address space. +using GPUVAddr = u64; ///< Represents a pointer in the GPU virtual address space.  using u128 = std::array<std::uint64_t, 2>;  static_assert(sizeof(u128) == 16, "u128 must be 128 bits wide"); 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 12f6d0114..a03179520 100644 --- a/src/common/logging/backend.cpp +++ b/src/common/logging/backend.cpp @@ -39,19 +39,19 @@ public:      Impl(Impl const&) = delete;      const Impl& operator=(Impl const&) = delete; -    void PushEntry(Entry e) { -        std::lock_guard<std::mutex> lock(message_mutex); -        message_queue.Push(std::move(e)); -        message_cv.notify_one(); +    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> 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,21 +80,19 @@ 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);                  }              };              while (true) { -                { -                    std::unique_lock<std::mutex> lock(message_mutex); -                    message_cv.wait(lock, [&] { return !running || message_queue.Pop(entry); }); -                } -                if (!running) { +                entry = message_queue.PopWait(); +                if (entry.final_entry) {                      break;                  }                  write_logs(entry);              } +              // Drain the logging queue. Only writes out up to MAX_LOGS_TO_WRITE to prevent a case              // where a system is repeatedly spamming logs even on close.              const int MAX_LOGS_TO_WRITE = filter.IsDebug() ? INT_MAX : 100; @@ -106,18 +104,36 @@ private:      }      ~Impl() { -        running = false; -        message_cv.notify_one(); +        Entry entry; +        entry.final_entry = true; +        message_queue.Push(entry);          backend_thread.join();      } -    std::atomic_bool running{true}; -    std::mutex message_mutex, writing_mutex; -    std::condition_variable message_cv; +    Entry CreateEntry(Class log_class, Level log_level, const char* filename, unsigned int line_nr, +                      const char* function, std::string message) const { +        using std::chrono::duration_cast; +        using std::chrono::steady_clock; + +        Entry entry; +        entry.timestamp = +            duration_cast<std::chrono::microseconds>(steady_clock::now() - time_origin); +        entry.log_class = log_class; +        entry.log_level = log_level; +        entry.filename = Common::TrimSourcePath(filename); +        entry.line_num = line_nr; +        entry.function = function; +        entry.message = std::move(message); + +        return entry; +    } + +    std::mutex writing_mutex;      std::thread backend_thread;      std::vector<std::unique_ptr<Backend>> backends;      Common::MPSCQueue<Log::Entry> message_queue;      Filter filter; +    std::chrono::steady_clock::time_point time_origin{std::chrono::steady_clock::now()};  };  void ConsoleBackend::Write(const Entry& entry) { @@ -232,6 +248,7 @@ void DebuggerBackend::Write(const Entry& entry) {      CLS(Render)                                                                                    \      SUB(Render, Software)                                                                          \      SUB(Render, OpenGL)                                                                            \ +    SUB(Render, Vulkan)                                                                            \      CLS(Audio)                                                                                     \      SUB(Audio, DSP)                                                                                \      SUB(Audio, Sink)                                                                               \ @@ -275,25 +292,6 @@ const char* GetLevelName(Level log_level) {  #undef LVL  } -Entry CreateEntry(Class log_class, Level log_level, const char* filename, unsigned int line_nr, -                  const char* function, std::string message) { -    using std::chrono::duration_cast; -    using std::chrono::steady_clock; - -    static steady_clock::time_point time_origin = steady_clock::now(); - -    Entry entry; -    entry.timestamp = duration_cast<std::chrono::microseconds>(steady_clock::now() - time_origin); -    entry.log_class = log_class; -    entry.log_level = log_level; -    entry.filename = Common::TrimSourcePath(filename); -    entry.line_num = line_nr; -    entry.function = function; -    entry.message = std::move(message); - -    return entry; -} -  void SetGlobalFilter(const Filter& filter) {      Impl::Instance().SetGlobalFilter(filter);  } @@ -318,9 +316,7 @@ void FmtLogMessageImpl(Class log_class, Level log_level, const char* filename,      if (!filter.CheckMessage(log_class, log_level))          return; -    Entry entry = -        CreateEntry(log_class, log_level, filename, line_num, function, fmt::vformat(format, args)); - -    instance.PushEntry(std::move(entry)); +    instance.PushEntry(log_class, log_level, filename, line_num, function, +                       fmt::vformat(format, args));  }  } // namespace Log diff --git a/src/common/logging/backend.h b/src/common/logging/backend.h index 91bb0c309..fca0267a1 100644 --- a/src/common/logging/backend.h +++ b/src/common/logging/backend.h @@ -27,6 +27,7 @@ struct Entry {      unsigned int line_num;      std::string function;      std::string message; +    bool final_entry = false;      Entry() = default;      Entry(Entry&& o) = default; @@ -134,10 +135,6 @@ const char* GetLogClassName(Class log_class);   */  const char* GetLevelName(Level log_level); -/// Creates a log entry by formatting the given source location, and message. -Entry CreateEntry(Class log_class, Level log_level, const char* filename, unsigned int line_nr, -                  const char* function, std::string message); -  /**   * 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 diff --git a/src/common/logging/log.h b/src/common/logging/log.h index d4ec31ec3..8ed6d5050 100644 --- a/src/common/logging/log.h +++ b/src/common/logging/log.h @@ -112,6 +112,7 @@ enum class Class : ClassType {      Render,            ///< Emulator video output and hardware acceleration      Render_Software,   ///< Software renderer backend      Render_OpenGL,     ///< OpenGL backend +    Render_Vulkan,     ///< Vulkan backend      Audio,             ///< Audio emulation      Audio_DSP,         ///< The HLE implementation of the DSP      Audio_Sink,        ///< Emulator audio output backend diff --git a/src/common/lz4_compression.cpp b/src/common/lz4_compression.cpp new file mode 100644 index 000000000..dc9b4a916 --- /dev/null +++ b/src/common/lz4_compression.cpp @@ -0,0 +1,78 @@ +// Copyright 2019 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#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/math_util.h b/src/common/math_util.h index 94b4394c5..cff3d48c5 100644 --- a/src/common/math_util.h +++ b/src/common/math_util.h @@ -7,7 +7,7 @@  #include <cstdlib>  #include <type_traits> -namespace MathUtil { +namespace Common {  constexpr float PI = 3.14159265f; @@ -41,4 +41,4 @@ struct Rectangle {      }  }; -} // namespace MathUtil +} // namespace Common diff --git a/src/common/memory_hook.cpp b/src/common/memory_hook.cpp new file mode 100644 index 000000000..3986986d6 --- /dev/null +++ b/src/common/memory_hook.cpp @@ -0,0 +1,11 @@ +// Copyright 2018 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/memory_hook.h" + +namespace Common { + +MemoryHook::~MemoryHook() = default; + +} // namespace Common diff --git a/src/common/memory_hook.h b/src/common/memory_hook.h new file mode 100644 index 000000000..adaa4c2c5 --- /dev/null +++ b/src/common/memory_hook.h @@ -0,0 +1,47 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <memory> +#include <optional> + +#include "common/common_types.h" + +namespace Common { + +/** + * Memory hooks have two purposes: + * 1. To allow reads and writes to a region of memory to be intercepted. This is used to implement + *    texture forwarding and memory breakpoints for debugging. + * 2. To allow for the implementation of MMIO devices. + * + * A hook may be mapped to multiple regions of memory. + * + * If a std::nullopt or false is returned from a function, the read/write request is passed through + * to the underlying memory region. + */ +class MemoryHook { +public: +    virtual ~MemoryHook(); + +    virtual std::optional<bool> IsValidAddress(VAddr addr) = 0; + +    virtual std::optional<u8> Read8(VAddr addr) = 0; +    virtual std::optional<u16> Read16(VAddr addr) = 0; +    virtual std::optional<u32> Read32(VAddr addr) = 0; +    virtual std::optional<u64> Read64(VAddr addr) = 0; + +    virtual bool ReadBlock(VAddr src_addr, void* dest_buffer, std::size_t size) = 0; + +    virtual bool Write8(VAddr addr, u8 data) = 0; +    virtual bool Write16(VAddr addr, u16 data) = 0; +    virtual bool Write32(VAddr addr, u32 data) = 0; +    virtual bool Write64(VAddr addr, u64 data) = 0; + +    virtual bool WriteBlock(VAddr dest_addr, const void* src_buffer, std::size_t size) = 0; +}; + +using MemoryHookPointer = std::shared_ptr<MemoryHook>; +} // namespace Common diff --git a/src/common/multi_level_queue.h b/src/common/multi_level_queue.h new file mode 100644 index 000000000..2b61b91e0 --- /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 = 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/page_table.cpp b/src/common/page_table.cpp new file mode 100644 index 000000000..69b7abc54 --- /dev/null +++ b/src/common/page_table.cpp @@ -0,0 +1,31 @@ +// Copyright 2019 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/page_table.h" + +namespace Common { + +PageTable::PageTable(std::size_t page_size_in_bits) : page_size_in_bits{page_size_in_bits} {} + +PageTable::~PageTable() = default; + +void PageTable::Resize(std::size_t address_space_width_in_bits) { +    const std::size_t num_page_table_entries = 1ULL +                                               << (address_space_width_in_bits - page_size_in_bits); + +    pointers.resize(num_page_table_entries); +    attributes.resize(num_page_table_entries); +    backing_addr.resize(num_page_table_entries); + +    // The default is a 39-bit address space, which causes an initial 1GB allocation size. If the +    // vector size is subsequently decreased (via resize), the vector might not automatically +    // actually reallocate/resize its underlying allocation, which wastes up to ~800 MB for +    // 36-bit titles. Call shrink_to_fit to reduce capacity to what's actually in use. + +    pointers.shrink_to_fit(); +    attributes.shrink_to_fit(); +    backing_addr.shrink_to_fit(); +} + +} // namespace Common diff --git a/src/common/page_table.h b/src/common/page_table.h new file mode 100644 index 000000000..8b8ff0bb8 --- /dev/null +++ b/src/common/page_table.h @@ -0,0 +1,84 @@ +// Copyright 2019 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <vector> +#include <boost/icl/interval_map.hpp> +#include "common/common_types.h" +#include "common/memory_hook.h" + +namespace Common { + +enum class PageType : u8 { +    /// Page is unmapped and should cause an access error. +    Unmapped, +    /// Page is mapped to regular memory. This is the only type you can get pointers to. +    Memory, +    /// Page is mapped to regular memory, but also needs to check for rasterizer cache flushing and +    /// invalidation +    RasterizerCachedMemory, +    /// Page is mapped to a I/O region. Writing and reading to this page is handled by functions. +    Special, +    /// Page is allocated for use. +    Allocated, +}; + +struct SpecialRegion { +    enum class Type { +        DebugHook, +        IODevice, +    } type; + +    MemoryHookPointer handler; + +    bool operator<(const SpecialRegion& other) const { +        return std::tie(type, handler) < std::tie(other.type, other.handler); +    } + +    bool operator==(const SpecialRegion& other) const { +        return std::tie(type, handler) == std::tie(other.type, other.handler); +    } +}; + +/** + * A (reasonably) fast way of allowing switchable and remappable process address spaces. It loosely + * mimics the way a real CPU page table works. + */ +struct PageTable { +    explicit PageTable(std::size_t page_size_in_bits); +    ~PageTable(); + +    /** +     * Resizes the page table to be able to accomodate enough pages within +     * a given address space. +     * +     * @param address_space_width_in_bits The address size width in bits. +     */ +    void Resize(std::size_t address_space_width_in_bits); + +    /** +     * Vector of memory pointers backing each page. An entry can only be non-null if the +     * corresponding entry in the `attributes` vector is of type `Memory`. +     */ +    std::vector<u8*> pointers; + +    /** +     * Contains MMIO handlers that back memory regions whose entries in the `attribute` vector is +     * of type `Special`. +     */ +    boost::icl::interval_map<u64, std::set<SpecialRegion>> special_regions; + +    /** +     * Vector of fine grained page attributes. If it is set to any value other than `Memory`, then +     * the corresponding entry in `pointers` MUST be set to null. +     */ +    std::vector<PageType> attributes; + +    std::vector<u64> backing_addr; + +    const std::size_t page_size_in_bits{}; +}; + +} // namespace Common diff --git a/src/common/quaternion.h b/src/common/quaternion.h index c528c0b68..370198ae0 100644 --- a/src/common/quaternion.h +++ b/src/common/quaternion.h @@ -6,12 +6,12 @@  #include "common/vector_math.h" -namespace Math { +namespace Common {  template <typename T>  class Quaternion {  public: -    Math::Vec3<T> xyz; +    Vec3<T> xyz;      T w{};      Quaternion<decltype(-T{})> Inverse() const { @@ -38,12 +38,12 @@ public:  };  template <typename T> -auto QuaternionRotate(const Quaternion<T>& q, const Math::Vec3<T>& v) { +auto QuaternionRotate(const Quaternion<T>& q, const Vec3<T>& v) {      return v + 2 * Cross(q.xyz, Cross(q.xyz, v) + v * q.w);  } -inline Quaternion<float> MakeQuaternion(const Math::Vec3<float>& axis, float angle) { +inline Quaternion<float> MakeQuaternion(const Vec3<float>& axis, float angle) {      return {axis * std::sin(angle / 2), std::cos(angle / 2)};  } -} // namespace Math +} // namespace Common diff --git a/src/common/swap.h b/src/common/swap.h index 32af0b6ac..b3eab1324 100644 --- a/src/common/swap.h +++ b/src/common/swap.h @@ -17,6 +17,8 @@  #pragma once +#include <type_traits> +  #if defined(_MSC_VER)  #include <cstdlib>  #elif defined(__linux__) @@ -28,8 +30,8 @@  #include <cstring>  #include "common/common_types.h" -// GCC 4.6+ -#if __GNUC__ >= 5 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) +// GCC +#ifdef __GNUC__  #if __BYTE_ORDER__ && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) && !defined(COMMON_LITTLE_ENDIAN)  #define COMMON_LITTLE_ENDIAN 1 @@ -38,7 +40,7 @@  #endif  // LLVM/clang -#elif __clang__ +#elif defined(__clang__)  #if __LITTLE_ENDIAN__ && !defined(COMMON_LITTLE_ENDIAN)  #define COMMON_LITTLE_ENDIAN 1 @@ -170,7 +172,7 @@ struct swap_struct_t {      using swapped_t = swap_struct_t;  protected: -    T value = T(); +    T value;      static T swap(T v) {          return F::swap(v); @@ -605,52 +607,154 @@ struct swap_double_t {      }  }; -#if COMMON_LITTLE_ENDIAN -using u16_le = u16; -using u32_le = u32; -using u64_le = u64; +template <typename T> +struct swap_enum_t { +    static_assert(std::is_enum_v<T>); +    using base = std::underlying_type_t<T>; + +public: +    swap_enum_t() = default; +    swap_enum_t(const T& v) : value(swap(v)) {} + +    swap_enum_t& operator=(const T& v) { +        value = swap(v); +        return *this; +    } + +    operator T() const { +        return swap(value); +    } + +    explicit operator base() const { +        return static_cast<base>(swap(value)); +    } -using s16_le = s16; -using s32_le = s32; -using s64_le = s64; +protected: +    T value{}; +    // clang-format off +    using swap_t = std::conditional_t< +        std::is_same_v<base, u16>, swap_16_t<u16>, std::conditional_t< +        std::is_same_v<base, s16>, swap_16_t<s16>, std::conditional_t< +        std::is_same_v<base, u32>, swap_32_t<u32>, std::conditional_t< +        std::is_same_v<base, s32>, swap_32_t<s32>, std::conditional_t< +        std::is_same_v<base, u64>, swap_64_t<u64>, std::conditional_t< +        std::is_same_v<base, s64>, swap_64_t<s64>, void>>>>>>; +    // clang-format on +    static T swap(T x) { +        return static_cast<T>(swap_t::swap(static_cast<base>(x))); +    } +}; -using float_le = float; -using double_le = double; +struct SwapTag {}; // Use the different endianness from the system +struct KeepTag {}; // Use the same endianness as the system -using u64_be = swap_struct_t<u64, swap_64_t<u64>>; -using s64_be = swap_struct_t<s64, swap_64_t<s64>>; +template <typename T, typename Tag> +struct AddEndian; -using u32_be = swap_struct_t<u32, swap_32_t<u32>>; -using s32_be = swap_struct_t<s32, swap_32_t<s32>>; +// KeepTag specializations -using u16_be = swap_struct_t<u16, swap_16_t<u16>>; -using s16_be = swap_struct_t<s16, swap_16_t<s16>>; +template <typename T> +struct AddEndian<T, KeepTag> { +    using type = T; +}; -using float_be = swap_struct_t<float, swap_float_t<float>>; -using double_be = swap_struct_t<double, swap_double_t<double>>; -#else +// SwapTag specializations + +template <> +struct AddEndian<u8, SwapTag> { +    using type = u8; +}; + +template <> +struct AddEndian<u16, SwapTag> { +    using type = swap_struct_t<u16, swap_16_t<u16>>; +}; + +template <> +struct AddEndian<u32, SwapTag> { +    using type = swap_struct_t<u32, swap_32_t<u32>>; +}; -using u64_le = swap_struct_t<u64, swap_64_t<u64>>; -using s64_le = swap_struct_t<s64, swap_64_t<s64>>; +template <> +struct AddEndian<u64, SwapTag> { +    using type = swap_struct_t<u64, swap_64_t<u64>>; +}; + +template <> +struct AddEndian<s8, SwapTag> { +    using type = s8; +}; -using u32_le = swap_struct_t<u32, swap_32_t<u32>>; -using s32_le = swap_struct_t<s32, swap_32_t<s32>>; +template <> +struct AddEndian<s16, SwapTag> { +    using type = swap_struct_t<s16, swap_16_t<s16>>; +}; -using u16_le = swap_struct_t<u16, swap_16_t<u16>>; -using s16_le = swap_struct_t<s16, swap_16_t<s16>>; +template <> +struct AddEndian<s32, SwapTag> { +    using type = swap_struct_t<s32, swap_32_t<s32>>; +}; + +template <> +struct AddEndian<s64, SwapTag> { +    using type = swap_struct_t<s64, swap_64_t<s64>>; +}; + +template <> +struct AddEndian<float, SwapTag> { +    using type = swap_struct_t<float, swap_float_t<float>>; +}; + +template <> +struct AddEndian<double, SwapTag> { +    using type = swap_struct_t<double, swap_double_t<double>>; +}; + +template <typename T> +struct AddEndian<T, SwapTag> { +    static_assert(std::is_enum_v<T>); +    using type = swap_enum_t<T>; +}; -using float_le = swap_struct_t<float, swap_float_t<float>>; -using double_le = swap_struct_t<double, swap_double_t<double>>; +// Alias LETag/BETag as KeepTag/SwapTag depending on the system +#if COMMON_LITTLE_ENDIAN -using u16_be = u16; -using u32_be = u32; -using u64_be = u64; +using LETag = KeepTag; +using BETag = SwapTag; -using s16_be = s16; -using s32_be = s32; -using s64_be = s64; +#else -using float_be = float; -using double_be = double; +using BETag = KeepTag; +using LETag = SwapTag;  #endif + +// Aliases for LE types +using u16_le = AddEndian<u16, LETag>::type; +using u32_le = AddEndian<u32, LETag>::type; +using u64_le = AddEndian<u64, LETag>::type; + +using s16_le = AddEndian<s16, LETag>::type; +using s32_le = AddEndian<s32, LETag>::type; +using s64_le = AddEndian<s64, LETag>::type; + +template <typename T> +using enum_le = std::enable_if_t<std::is_enum_v<T>, typename AddEndian<T, LETag>::type>; + +using float_le = AddEndian<float, LETag>::type; +using double_le = AddEndian<double, LETag>::type; + +// Aliases for BE types +using u16_be = AddEndian<u16, BETag>::type; +using u32_be = AddEndian<u32, BETag>::type; +using u64_be = AddEndian<u64, BETag>::type; + +using s16_be = AddEndian<s16, BETag>::type; +using s32_be = AddEndian<s32, BETag>::type; +using s64_be = AddEndian<s64, BETag>::type; + +template <typename T> +using enum_be = std::enable_if_t<std::is_enum_v<T>, typename AddEndian<T, BETag>::type>; + +using float_be = AddEndian<float, BETag>::type; +using double_be = AddEndian<double, BETag>::type; 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/thread_queue_list.h b/src/common/thread_queue_list.h index e7594db68..791f99a8c 100644 --- a/src/common/thread_queue_list.h +++ b/src/common/thread_queue_list.h @@ -6,7 +6,6 @@  #include <array>  #include <deque> -#include <boost/range/algorithm_ext/erase.hpp>  namespace Common { @@ -111,8 +110,9 @@ struct ThreadQueueList {      }      void remove(Priority priority, const T& thread_id) { -        Queue* cur = &queues[priority]; -        boost::remove_erase(cur->data, thread_id); +        Queue* const cur = &queues[priority]; +        const auto iter = std::remove(cur->data.begin(), cur->data.end(), thread_id); +        cur->data.erase(iter, cur->data.end());      }      void rotate(Priority priority) { diff --git a/src/common/threadsafe_queue.h b/src/common/threadsafe_queue.h index edf13bc49..e714ba5b3 100644 --- a/src/common/threadsafe_queue.h +++ b/src/common/threadsafe_queue.h @@ -7,17 +7,17 @@  // a simple lockless thread-safe,  // single reader, single writer queue -#include <algorithm>  #include <atomic> +#include <condition_variable>  #include <cstddef>  #include <mutex> -#include "common/common_types.h" +#include <utility>  namespace Common { -template <typename T, bool NeedSize = true> +template <typename T>  class SPSCQueue {  public: -    SPSCQueue() : size(0) { +    SPSCQueue() {          write_ptr = read_ptr = new ElementPtr();      }      ~SPSCQueue() { @@ -25,13 +25,12 @@ public:          delete read_ptr;      } -    u32 Size() const { -        static_assert(NeedSize, "using Size() on FifoQueue without NeedSize"); +    std::size_t Size() const {          return size.load();      }      bool Empty() const { -        return !read_ptr->next.load(); +        return Size() == 0;      }      T& Front() const { @@ -47,13 +46,14 @@ public:          ElementPtr* new_ptr = new ElementPtr();          write_ptr->next.store(new_ptr, std::memory_order_release);          write_ptr = new_ptr; -        if (NeedSize) -            size++; +        cv.notify_one(); + +        ++size;      }      void Pop() { -        if (NeedSize) -            size--; +        --size; +          ElementPtr* tmpptr = read_ptr;          // advance the read pointer          read_ptr = tmpptr->next.load(); @@ -66,8 +66,7 @@ public:          if (Empty())              return false; -        if (NeedSize) -            size--; +        --size;          ElementPtr* tmpptr = read_ptr;          read_ptr = tmpptr->next.load(std::memory_order_acquire); @@ -77,6 +76,16 @@ public:          return true;      } +    T PopWait() { +        if (Empty()) { +            std::unique_lock lock{cv_mutex}; +            cv.wait(lock, [this]() { return !Empty(); }); +        } +        T t; +        Pop(t); +        return t; +    } +      // not thread-safe      void Clear() {          size.store(0); @@ -89,7 +98,7 @@ private:      // and a pointer to the next ElementPtr      class ElementPtr {      public: -        ElementPtr() : next(nullptr) {} +        ElementPtr() {}          ~ElementPtr() {              ElementPtr* next_ptr = next.load(); @@ -98,21 +107,23 @@ private:          }          T current; -        std::atomic<ElementPtr*> next; +        std::atomic<ElementPtr*> next{nullptr};      };      ElementPtr* write_ptr;      ElementPtr* read_ptr; -    std::atomic<u32> size; +    std::atomic_size_t size{0}; +    std::mutex cv_mutex; +    std::condition_variable cv;  };  // a simple thread-safe,  // single reader, multiple writer queue -template <typename T, bool NeedSize = true> +template <typename T>  class MPSCQueue {  public: -    u32 Size() const { +    std::size_t Size() const {          return spsc_queue.Size();      } @@ -126,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);      } @@ -138,13 +149,17 @@ public:          return spsc_queue.Pop(t);      } +    T PopWait() { +        return spsc_queue.PopWait(); +    } +      // not thread-safe      void Clear() {          spsc_queue.Clear();      }  private: -    SPSCQueue<T, NeedSize> spsc_queue; +    SPSCQueue<T> spsc_queue;      std::mutex write_lock;  };  } // namespace Common diff --git a/src/common/uint128.cpp b/src/common/uint128.cpp new file mode 100644 index 000000000..32bf56730 --- /dev/null +++ b/src/common/uint128.cpp @@ -0,0 +1,45 @@ +// Copyright 2019 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#ifdef _MSC_VER +#include <intrin.h> + +#pragma intrinsic(_umul128) +#endif +#include <cstring> +#include "common/uint128.h" + +namespace Common { + +u128 Multiply64Into128(u64 a, u64 b) { +    u128 result; +#ifdef _MSC_VER +    result[0] = _umul128(a, b, &result[1]); +#else +    unsigned __int128 tmp = a; +    tmp *= b; +    std::memcpy(&result, &tmp, sizeof(u128)); +#endif +    return result; +} + +std::pair<u64, u64> Divide128On32(u128 dividend, u32 divisor) { +    u64 remainder = dividend[0] % divisor; +    u64 accum = dividend[0] / divisor; +    if (dividend[1] == 0) +        return {accum, remainder}; +    // We ignore dividend[1] / divisor as that overflows +    const u64 first_segment = (dividend[1] % divisor) << 32; +    accum += (first_segment / divisor) << 32; +    const u64 second_segment = (first_segment % divisor) << 32; +    accum += (second_segment / divisor); +    remainder += second_segment % divisor; +    if (remainder >= divisor) { +        accum++; +        remainder -= divisor; +    } +    return {accum, remainder}; +} + +} // namespace Common diff --git a/src/common/uint128.h b/src/common/uint128.h new file mode 100644 index 000000000..a3be2a2cb --- /dev/null +++ b/src/common/uint128.h @@ -0,0 +1,19 @@ +// Copyright 2019 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <utility> +#include "common/common_types.h" + +namespace Common { + +// This function multiplies 2 u64 values and produces a u128 value; +u128 Multiply64Into128(u64 a, u64 b); + +// This function divides a u128 by a u32 value and produces two u64 values: +// the result of division and the remainder +std::pair<u64, u64> Divide128On32(u128 dividend, u32 divisor); + +} // namespace Common diff --git a/src/common/vector_math.h b/src/common/vector_math.h index 8feb49941..429485329 100644 --- a/src/common/vector_math.h +++ b/src/common/vector_math.h @@ -33,7 +33,7 @@  #include <cmath>  #include <type_traits> -namespace Math { +namespace Common {  template <typename T>  class Vec2; @@ -690,4 +690,4 @@ constexpr Vec4<T> MakeVec(const T& x, const Vec3<T>& yzw) {      return MakeVec(x, yzw[0], yzw[1], yzw[2]);  } -} // namespace Math +} // namespace Common  | 
