diff options
Diffstat (limited to 'src/common')
72 files changed, 2862 insertions, 741 deletions
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index bd6ac6716..3adf13a3f 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt | |||
| @@ -38,6 +38,7 @@ add_library(common STATIC | |||
| 38 | common_precompiled_headers.h | 38 | common_precompiled_headers.h |
| 39 | common_types.h | 39 | common_types.h |
| 40 | concepts.h | 40 | concepts.h |
| 41 | container_hash.h | ||
| 41 | demangle.cpp | 42 | demangle.cpp |
| 42 | demangle.h | 43 | demangle.h |
| 43 | div_ceil.h | 44 | div_ceil.h |
| @@ -91,6 +92,7 @@ add_library(common STATIC | |||
| 91 | multi_level_page_table.h | 92 | multi_level_page_table.h |
| 92 | nvidia_flags.cpp | 93 | nvidia_flags.cpp |
| 93 | nvidia_flags.h | 94 | nvidia_flags.h |
| 95 | overflow.h | ||
| 94 | page_table.cpp | 96 | page_table.cpp |
| 95 | page_table.h | 97 | page_table.h |
| 96 | param_package.cpp | 98 | param_package.cpp |
| @@ -113,6 +115,8 @@ add_library(common STATIC | |||
| 113 | socket_types.h | 115 | socket_types.h |
| 114 | spin_lock.cpp | 116 | spin_lock.cpp |
| 115 | spin_lock.h | 117 | spin_lock.h |
| 118 | steady_clock.cpp | ||
| 119 | steady_clock.h | ||
| 116 | stream.cpp | 120 | stream.cpp |
| 117 | stream.h | 121 | stream.h |
| 118 | string_util.cpp | 122 | string_util.cpp |
| @@ -129,6 +133,7 @@ add_library(common STATIC | |||
| 129 | time_zone.h | 133 | time_zone.h |
| 130 | tiny_mt.h | 134 | tiny_mt.h |
| 131 | tree.h | 135 | tree.h |
| 136 | typed_address.h | ||
| 132 | uint128.h | 137 | uint128.h |
| 133 | unique_function.h | 138 | unique_function.h |
| 134 | uuid.cpp | 139 | uuid.cpp |
| @@ -142,13 +147,33 @@ add_library(common STATIC | |||
| 142 | zstd_compression.h | 147 | zstd_compression.h |
| 143 | ) | 148 | ) |
| 144 | 149 | ||
| 150 | if (WIN32) | ||
| 151 | target_sources(common PRIVATE | ||
| 152 | windows/timer_resolution.cpp | ||
| 153 | windows/timer_resolution.h | ||
| 154 | ) | ||
| 155 | target_link_libraries(common PRIVATE ntdll) | ||
| 156 | endif() | ||
| 157 | |||
| 158 | if(ANDROID) | ||
| 159 | target_sources(common | ||
| 160 | PRIVATE | ||
| 161 | fs/fs_android.cpp | ||
| 162 | fs/fs_android.h | ||
| 163 | ) | ||
| 164 | endif() | ||
| 165 | |||
| 145 | if(ARCHITECTURE_x86_64) | 166 | if(ARCHITECTURE_x86_64) |
| 146 | target_sources(common | 167 | target_sources(common |
| 147 | PRIVATE | 168 | PRIVATE |
| 148 | x64/cpu_detect.cpp | 169 | x64/cpu_detect.cpp |
| 149 | x64/cpu_detect.h | 170 | x64/cpu_detect.h |
| 171 | x64/cpu_wait.cpp | ||
| 172 | x64/cpu_wait.h | ||
| 150 | x64/native_clock.cpp | 173 | x64/native_clock.cpp |
| 151 | x64/native_clock.h | 174 | x64/native_clock.h |
| 175 | x64/rdtsc.cpp | ||
| 176 | x64/rdtsc.h | ||
| 152 | x64/xbyak_abi.h | 177 | x64/xbyak_abi.h |
| 153 | x64/xbyak_util.h | 178 | x64/xbyak_util.h |
| 154 | ) | 179 | ) |
| @@ -176,8 +201,13 @@ endif() | |||
| 176 | 201 | ||
| 177 | create_target_directory_groups(common) | 202 | create_target_directory_groups(common) |
| 178 | 203 | ||
| 179 | target_link_libraries(common PUBLIC ${Boost_LIBRARIES} fmt::fmt microprofile Threads::Threads) | 204 | target_link_libraries(common PUBLIC Boost::context Boost::headers fmt::fmt microprofile Threads::Threads) |
| 180 | target_link_libraries(common PRIVATE lz4::lz4 zstd::zstd demangle) | 205 | target_link_libraries(common PRIVATE lz4::lz4 zstd::zstd LLVM::Demangle) |
| 206 | |||
| 207 | if (ANDROID) | ||
| 208 | # For ASharedMemory_create | ||
| 209 | target_link_libraries(common PRIVATE android) | ||
| 210 | endif() | ||
| 181 | 211 | ||
| 182 | if (YUZU_USE_PRECOMPILED_HEADERS) | 212 | if (YUZU_USE_PRECOMPILED_HEADERS) |
| 183 | target_precompile_headers(common PRIVATE precompiled_headers.h) | 213 | target_precompile_headers(common PRIVATE precompiled_headers.h) |
diff --git a/src/common/address_space.h b/src/common/address_space.h index 9222b2fdc..8683c23c3 100644 --- a/src/common/address_space.h +++ b/src/common/address_space.h | |||
| @@ -12,7 +12,8 @@ | |||
| 12 | 12 | ||
| 13 | namespace Common { | 13 | namespace Common { |
| 14 | template <typename VaType, size_t AddressSpaceBits> | 14 | template <typename VaType, size_t AddressSpaceBits> |
| 15 | concept AddressSpaceValid = std::is_unsigned_v<VaType> && sizeof(VaType) * 8 >= AddressSpaceBits; | 15 | concept AddressSpaceValid = std::is_unsigned_v<VaType> && sizeof(VaType) * 8 >= |
| 16 | AddressSpaceBits; | ||
| 16 | 17 | ||
| 17 | struct EmptyStruct {}; | 18 | struct EmptyStruct {}; |
| 18 | 19 | ||
| @@ -21,7 +22,7 @@ struct EmptyStruct {}; | |||
| 21 | */ | 22 | */ |
| 22 | template <typename VaType, VaType UnmappedVa, typename PaType, PaType UnmappedPa, | 23 | template <typename VaType, VaType UnmappedVa, typename PaType, PaType UnmappedPa, |
| 23 | bool PaContigSplit, size_t AddressSpaceBits, typename ExtraBlockInfo = EmptyStruct> | 24 | bool PaContigSplit, size_t AddressSpaceBits, typename ExtraBlockInfo = EmptyStruct> |
| 24 | requires AddressSpaceValid<VaType, AddressSpaceBits> | 25 | requires AddressSpaceValid<VaType, AddressSpaceBits> |
| 25 | class FlatAddressSpaceMap { | 26 | class FlatAddressSpaceMap { |
| 26 | public: | 27 | public: |
| 27 | /// The maximum VA that this AS can technically reach | 28 | /// The maximum VA that this AS can technically reach |
| @@ -109,7 +110,7 @@ private: | |||
| 109 | * initial, fast linear pass and a subsequent slower pass that iterates until it finds a free block | 110 | * initial, fast linear pass and a subsequent slower pass that iterates until it finds a free block |
| 110 | */ | 111 | */ |
| 111 | template <typename VaType, VaType UnmappedVa, size_t AddressSpaceBits> | 112 | template <typename VaType, VaType UnmappedVa, size_t AddressSpaceBits> |
| 112 | requires AddressSpaceValid<VaType, AddressSpaceBits> | 113 | requires AddressSpaceValid<VaType, AddressSpaceBits> |
| 113 | class FlatAllocator | 114 | class FlatAllocator |
| 114 | : public FlatAddressSpaceMap<VaType, UnmappedVa, bool, false, false, AddressSpaceBits> { | 115 | : public FlatAddressSpaceMap<VaType, UnmappedVa, bool, false, false, AddressSpaceBits> { |
| 115 | private: | 116 | private: |
diff --git a/src/common/address_space.inc b/src/common/address_space.inc index 2195dabd5..1ee82df53 100644 --- a/src/common/address_space.inc +++ b/src/common/address_space.inc | |||
| @@ -72,7 +72,7 @@ MAP_MEMBER(void)::MapLocked(VaType virt, PaType phys, VaType size, ExtraBlockInf | |||
| 72 | } | 72 | } |
| 73 | }()}; | 73 | }()}; |
| 74 | 74 | ||
| 75 | if (block_end_predecessor->virt >= virt) { | 75 | if (block_end_predecessor != blocks.begin() && block_end_predecessor->virt >= virt) { |
| 76 | // If this block's start would be overlapped by the map then reuse it as a tail | 76 | // If this block's start would be overlapped by the map then reuse it as a tail |
| 77 | // block | 77 | // block |
| 78 | block_end_predecessor->virt = virt_end; | 78 | block_end_predecessor->virt = virt_end; |
| @@ -336,7 +336,7 @@ ALLOC_MEMBER(VaType)::Allocate(VaType size) { | |||
| 336 | ASSERT_MSG(false, "Unexpected allocator state!"); | 336 | ASSERT_MSG(false, "Unexpected allocator state!"); |
| 337 | } | 337 | } |
| 338 | 338 | ||
| 339 | auto search_predecessor{this->blocks.begin()}; | 339 | auto search_predecessor{std::next(this->blocks.begin())}; |
| 340 | auto search_successor{std::next(search_predecessor)}; | 340 | auto search_successor{std::next(search_predecessor)}; |
| 341 | 341 | ||
| 342 | while (search_successor != this->blocks.end() && | 342 | while (search_successor != this->blocks.end() && |
diff --git a/src/common/alignment.h b/src/common/alignment.h index 7e897334b..fa715d497 100644 --- a/src/common/alignment.h +++ b/src/common/alignment.h | |||
| @@ -10,7 +10,7 @@ | |||
| 10 | namespace Common { | 10 | namespace Common { |
| 11 | 11 | ||
| 12 | template <typename T> | 12 | template <typename T> |
| 13 | requires std::is_unsigned_v<T> | 13 | requires std::is_unsigned_v<T> |
| 14 | [[nodiscard]] constexpr T AlignUp(T value, size_t size) { | 14 | [[nodiscard]] constexpr T AlignUp(T value, size_t size) { |
| 15 | auto mod{static_cast<T>(value % size)}; | 15 | auto mod{static_cast<T>(value % size)}; |
| 16 | value -= mod; | 16 | value -= mod; |
| @@ -18,31 +18,31 @@ requires std::is_unsigned_v<T> | |||
| 18 | } | 18 | } |
| 19 | 19 | ||
| 20 | template <typename T> | 20 | template <typename T> |
| 21 | requires std::is_unsigned_v<T> | 21 | requires std::is_unsigned_v<T> |
| 22 | [[nodiscard]] constexpr T AlignUpLog2(T value, size_t align_log2) { | 22 | [[nodiscard]] constexpr T AlignUpLog2(T value, size_t align_log2) { |
| 23 | return static_cast<T>((value + ((1ULL << align_log2) - 1)) >> align_log2 << align_log2); | 23 | return static_cast<T>((value + ((1ULL << align_log2) - 1)) >> align_log2 << align_log2); |
| 24 | } | 24 | } |
| 25 | 25 | ||
| 26 | template <typename T> | 26 | template <typename T> |
| 27 | requires std::is_unsigned_v<T> | 27 | requires std::is_unsigned_v<T> |
| 28 | [[nodiscard]] constexpr T AlignDown(T value, size_t size) { | 28 | [[nodiscard]] constexpr T AlignDown(T value, size_t size) { |
| 29 | return static_cast<T>(value - value % size); | 29 | return static_cast<T>(value - value % size); |
| 30 | } | 30 | } |
| 31 | 31 | ||
| 32 | template <typename T> | 32 | template <typename T> |
| 33 | requires std::is_unsigned_v<T> | 33 | requires std::is_unsigned_v<T> |
| 34 | [[nodiscard]] constexpr bool Is4KBAligned(T value) { | 34 | [[nodiscard]] constexpr bool Is4KBAligned(T value) { |
| 35 | return (value & 0xFFF) == 0; | 35 | return (value & 0xFFF) == 0; |
| 36 | } | 36 | } |
| 37 | 37 | ||
| 38 | template <typename T> | 38 | template <typename T> |
| 39 | requires std::is_unsigned_v<T> | 39 | requires std::is_unsigned_v<T> |
| 40 | [[nodiscard]] constexpr bool IsWordAligned(T value) { | 40 | [[nodiscard]] constexpr bool IsWordAligned(T value) { |
| 41 | return (value & 0b11) == 0; | 41 | return (value & 0b11) == 0; |
| 42 | } | 42 | } |
| 43 | 43 | ||
| 44 | template <typename T> | 44 | template <typename T> |
| 45 | requires std::is_integral_v<T> | 45 | requires std::is_integral_v<T> |
| 46 | [[nodiscard]] constexpr bool IsAligned(T value, size_t alignment) { | 46 | [[nodiscard]] constexpr bool IsAligned(T value, size_t alignment) { |
| 47 | using U = typename std::make_unsigned_t<T>; | 47 | using U = typename std::make_unsigned_t<T>; |
| 48 | const U mask = static_cast<U>(alignment - 1); | 48 | const U mask = static_cast<U>(alignment - 1); |
| @@ -50,7 +50,7 @@ requires std::is_integral_v<T> | |||
| 50 | } | 50 | } |
| 51 | 51 | ||
| 52 | template <typename T, typename U> | 52 | template <typename T, typename U> |
| 53 | requires std::is_integral_v<T> | 53 | requires std::is_integral_v<T> |
| 54 | [[nodiscard]] constexpr T DivideUp(T x, U y) { | 54 | [[nodiscard]] constexpr T DivideUp(T x, U y) { |
| 55 | return (x + (y - 1)) / y; | 55 | return (x + (y - 1)) / y; |
| 56 | } | 56 | } |
| @@ -73,11 +73,11 @@ public: | |||
| 73 | constexpr AlignmentAllocator(const AlignmentAllocator<T2, Align>&) noexcept {} | 73 | constexpr AlignmentAllocator(const AlignmentAllocator<T2, Align>&) noexcept {} |
| 74 | 74 | ||
| 75 | [[nodiscard]] T* allocate(size_type n) { | 75 | [[nodiscard]] T* allocate(size_type n) { |
| 76 | return static_cast<T*>(::operator new (n * sizeof(T), std::align_val_t{Align})); | 76 | return static_cast<T*>(::operator new(n * sizeof(T), std::align_val_t{Align})); |
| 77 | } | 77 | } |
| 78 | 78 | ||
| 79 | void deallocate(T* p, size_type n) { | 79 | void deallocate(T* p, size_type n) { |
| 80 | ::operator delete (p, n * sizeof(T), std::align_val_t{Align}); | 80 | ::operator delete(p, n * sizeof(T), std::align_val_t{Align}); |
| 81 | } | 81 | } |
| 82 | 82 | ||
| 83 | template <typename T2> | 83 | template <typename T2> |
diff --git a/src/common/announce_multiplayer_room.h b/src/common/announce_multiplayer_room.h index 4a3100fa4..f32060196 100644 --- a/src/common/announce_multiplayer_room.h +++ b/src/common/announce_multiplayer_room.h | |||
| @@ -66,7 +66,7 @@ public: | |||
| 66 | * @param description The room description | 66 | * @param description The room description |
| 67 | * @param port The port of the room | 67 | * @param port The port of the room |
| 68 | * @param net_version The version of the libNetwork that gets used | 68 | * @param net_version The version of the libNetwork that gets used |
| 69 | * @param has_password True if the room is passowrd protected | 69 | * @param has_password True if the room is password protected |
| 70 | * @param preferred_game The preferred game of the room | 70 | * @param preferred_game The preferred game of the room |
| 71 | * @param preferred_game_id The title id of the preferred game | 71 | * @param preferred_game_id The title id of the preferred game |
| 72 | */ | 72 | */ |
diff --git a/src/common/atomic_helpers.h b/src/common/atomic_helpers.h index aef3b66a4..d997f10ba 100644 --- a/src/common/atomic_helpers.h +++ b/src/common/atomic_helpers.h | |||
| @@ -75,7 +75,7 @@ extern "C" void AnnotateHappensAfter(const char*, int, void*); | |||
| 75 | #if defined(AE_VCPP) || defined(AE_ICC) | 75 | #if defined(AE_VCPP) || defined(AE_ICC) |
| 76 | #define AE_FORCEINLINE __forceinline | 76 | #define AE_FORCEINLINE __forceinline |
| 77 | #elif defined(AE_GCC) | 77 | #elif defined(AE_GCC) |
| 78 | //#define AE_FORCEINLINE __attribute__((always_inline)) | 78 | // #define AE_FORCEINLINE __attribute__((always_inline)) |
| 79 | #define AE_FORCEINLINE inline | 79 | #define AE_FORCEINLINE inline |
| 80 | #else | 80 | #else |
| 81 | #define AE_FORCEINLINE inline | 81 | #define AE_FORCEINLINE inline |
diff --git a/src/common/bit_cast.h b/src/common/bit_cast.h index 535148b4d..c6110c542 100644 --- a/src/common/bit_cast.h +++ b/src/common/bit_cast.h | |||
| @@ -3,19 +3,21 @@ | |||
| 3 | 3 | ||
| 4 | #pragma once | 4 | #pragma once |
| 5 | 5 | ||
| 6 | #include <cstring> | 6 | #include <version> |
| 7 | #include <type_traits> | 7 | |
| 8 | #ifdef __cpp_lib_bit_cast | ||
| 9 | #include <bit> | ||
| 10 | #endif | ||
| 8 | 11 | ||
| 9 | namespace Common { | 12 | namespace Common { |
| 10 | 13 | ||
| 11 | template <typename To, typename From> | 14 | template <typename To, typename From> |
| 12 | [[nodiscard]] std::enable_if_t<sizeof(To) == sizeof(From) && std::is_trivially_copyable_v<From> && | 15 | constexpr inline To BitCast(const From& from) { |
| 13 | std::is_trivially_copyable_v<To>, | 16 | #ifdef __cpp_lib_bit_cast |
| 14 | To> | 17 | return std::bit_cast<To>(from); |
| 15 | BitCast(const From& src) noexcept { | 18 | #else |
| 16 | To dst; | 19 | return __builtin_bit_cast(To, from); |
| 17 | std::memcpy(&dst, &src, sizeof(To)); | 20 | #endif |
| 18 | return dst; | ||
| 19 | } | 21 | } |
| 20 | 22 | ||
| 21 | } // namespace Common | 23 | } // namespace Common |
diff --git a/src/common/bit_field.h b/src/common/bit_field.h index e4e58ea45..0168ff9cb 100644 --- a/src/common/bit_field.h +++ b/src/common/bit_field.h | |||
| @@ -188,3 +188,8 @@ private: | |||
| 188 | 188 | ||
| 189 | template <std::size_t Position, std::size_t Bits, typename T> | 189 | template <std::size_t Position, std::size_t Bits, typename T> |
| 190 | using BitFieldBE = BitField<Position, Bits, T, BETag>; | 190 | using BitFieldBE = BitField<Position, Bits, T, BETag>; |
| 191 | |||
| 192 | template <std::size_t Position, std::size_t Bits, typename T, typename EndianTag = LETag> | ||
| 193 | inline auto format_as(BitField<Position, Bits, T, EndianTag> bitfield) { | ||
| 194 | return bitfield.Value(); | ||
| 195 | } | ||
diff --git a/src/common/bit_util.h b/src/common/bit_util.h index e4e6287f3..13368b439 100644 --- a/src/common/bit_util.h +++ b/src/common/bit_util.h | |||
| @@ -45,19 +45,19 @@ template <typename T> | |||
| 45 | } | 45 | } |
| 46 | 46 | ||
| 47 | template <typename T> | 47 | template <typename T> |
| 48 | requires std::is_unsigned_v<T> | 48 | requires std::is_unsigned_v<T> |
| 49 | [[nodiscard]] constexpr bool IsPow2(T value) { | 49 | [[nodiscard]] constexpr bool IsPow2(T value) { |
| 50 | return std::has_single_bit(value); | 50 | return std::has_single_bit(value); |
| 51 | } | 51 | } |
| 52 | 52 | ||
| 53 | template <typename T> | 53 | template <typename T> |
| 54 | requires std::is_integral_v<T> | 54 | requires std::is_integral_v<T> |
| 55 | [[nodiscard]] T NextPow2(T value) { | 55 | [[nodiscard]] T NextPow2(T value) { |
| 56 | return static_cast<T>(1ULL << ((8U * sizeof(T)) - std::countl_zero(value - 1U))); | 56 | return static_cast<T>(1ULL << ((8U * sizeof(T)) - std::countl_zero(value - 1U))); |
| 57 | } | 57 | } |
| 58 | 58 | ||
| 59 | template <size_t bit_index, typename T> | 59 | template <size_t bit_index, typename T> |
| 60 | requires std::is_integral_v<T> | 60 | requires std::is_integral_v<T> |
| 61 | [[nodiscard]] constexpr bool Bit(const T value) { | 61 | [[nodiscard]] constexpr bool Bit(const T value) { |
| 62 | static_assert(bit_index < BitSize<T>(), "bit_index must be smaller than size of T"); | 62 | static_assert(bit_index < BitSize<T>(), "bit_index must be smaller than size of T"); |
| 63 | return ((value >> bit_index) & T(1)) == T(1); | 63 | return ((value >> bit_index) & T(1)) == T(1); |
diff --git a/src/common/bounded_threadsafe_queue.h b/src/common/bounded_threadsafe_queue.h index 21217801e..bd87aa09b 100644 --- a/src/common/bounded_threadsafe_queue.h +++ b/src/common/bounded_threadsafe_queue.h | |||
| @@ -1,158 +1,249 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright (c) 2020 Erik Rigtorp <erik@rigtorp.se> | 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: MIT | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #pragma once | 4 | #pragma once |
| 5 | 5 | ||
| 6 | #include <atomic> | 6 | #include <atomic> |
| 7 | #include <bit> | ||
| 8 | #include <condition_variable> | 7 | #include <condition_variable> |
| 9 | #include <memory> | 8 | #include <cstddef> |
| 10 | #include <mutex> | 9 | #include <mutex> |
| 11 | #include <new> | 10 | #include <new> |
| 12 | #include <stop_token> | 11 | |
| 13 | #include <type_traits> | 12 | #include "common/polyfill_thread.h" |
| 14 | #include <utility> | ||
| 15 | 13 | ||
| 16 | namespace Common { | 14 | namespace Common { |
| 17 | 15 | ||
| 18 | #if defined(__cpp_lib_hardware_interference_size) | 16 | namespace detail { |
| 19 | constexpr size_t hardware_interference_size = std::hardware_destructive_interference_size; | 17 | constexpr size_t DefaultCapacity = 0x1000; |
| 20 | #else | 18 | } // namespace detail |
| 21 | constexpr size_t hardware_interference_size = 64; | 19 | |
| 22 | #endif | 20 | template <typename T, size_t Capacity = detail::DefaultCapacity> |
| 21 | class SPSCQueue { | ||
| 22 | static_assert((Capacity & (Capacity - 1)) == 0, "Capacity must be a power of two."); | ||
| 23 | 23 | ||
| 24 | template <typename T, size_t capacity = 0x400> | ||
| 25 | class MPSCQueue { | ||
| 26 | public: | 24 | public: |
| 27 | explicit MPSCQueue() : allocator{std::allocator<Slot<T>>()} { | 25 | template <typename... Args> |
| 28 | // Allocate one extra slot to prevent false sharing on the last slot | 26 | bool TryEmplace(Args&&... args) { |
| 29 | slots = allocator.allocate(capacity + 1); | 27 | return Emplace<PushMode::Try>(std::forward<Args>(args)...); |
| 30 | // Allocators are not required to honor alignment for over-aligned types | ||
| 31 | // (see http://eel.is/c++draft/allocator.requirements#10) so we verify | ||
| 32 | // alignment here | ||
| 33 | if (reinterpret_cast<uintptr_t>(slots) % alignof(Slot<T>) != 0) { | ||
| 34 | allocator.deallocate(slots, capacity + 1); | ||
| 35 | throw std::bad_alloc(); | ||
| 36 | } | ||
| 37 | for (size_t i = 0; i < capacity; ++i) { | ||
| 38 | std::construct_at(&slots[i]); | ||
| 39 | } | ||
| 40 | static_assert(std::has_single_bit(capacity), "capacity must be an integer power of 2"); | ||
| 41 | static_assert(alignof(Slot<T>) == hardware_interference_size, | ||
| 42 | "Slot must be aligned to cache line boundary to prevent false sharing"); | ||
| 43 | static_assert(sizeof(Slot<T>) % hardware_interference_size == 0, | ||
| 44 | "Slot size must be a multiple of cache line size to prevent " | ||
| 45 | "false sharing between adjacent slots"); | ||
| 46 | static_assert(sizeof(MPSCQueue) % hardware_interference_size == 0, | ||
| 47 | "Queue size must be a multiple of cache line size to " | ||
| 48 | "prevent false sharing between adjacent queues"); | ||
| 49 | } | ||
| 50 | |||
| 51 | ~MPSCQueue() noexcept { | ||
| 52 | for (size_t i = 0; i < capacity; ++i) { | ||
| 53 | std::destroy_at(&slots[i]); | ||
| 54 | } | ||
| 55 | allocator.deallocate(slots, capacity + 1); | ||
| 56 | } | 28 | } |
| 57 | 29 | ||
| 58 | // The queue must be both non-copyable and non-movable | 30 | template <typename... Args> |
| 59 | MPSCQueue(const MPSCQueue&) = delete; | 31 | void EmplaceWait(Args&&... args) { |
| 60 | MPSCQueue& operator=(const MPSCQueue&) = delete; | 32 | Emplace<PushMode::Wait>(std::forward<Args>(args)...); |
| 33 | } | ||
| 61 | 34 | ||
| 62 | MPSCQueue(MPSCQueue&&) = delete; | 35 | bool TryPop(T& t) { |
| 63 | MPSCQueue& operator=(MPSCQueue&&) = delete; | 36 | return Pop<PopMode::Try>(t); |
| 37 | } | ||
| 64 | 38 | ||
| 65 | void Push(const T& v) noexcept { | 39 | void PopWait(T& t) { |
| 66 | static_assert(std::is_nothrow_copy_constructible_v<T>, | 40 | Pop<PopMode::Wait>(t); |
| 67 | "T must be nothrow copy constructible"); | ||
| 68 | emplace(v); | ||
| 69 | } | 41 | } |
| 70 | 42 | ||
| 71 | template <typename P, typename = std::enable_if_t<std::is_nothrow_constructible_v<T, P&&>>> | 43 | void PopWait(T& t, std::stop_token stop_token) { |
| 72 | void Push(P&& v) noexcept { | 44 | Pop<PopMode::WaitWithStopToken>(t, stop_token); |
| 73 | emplace(std::forward<P>(v)); | ||
| 74 | } | 45 | } |
| 75 | 46 | ||
| 76 | void Pop(T& v, std::stop_token stop) noexcept { | 47 | T PopWait() { |
| 77 | auto const tail = tail_.fetch_add(1); | 48 | T t; |
| 78 | auto& slot = slots[idx(tail)]; | 49 | Pop<PopMode::Wait>(t); |
| 79 | if (!slot.turn.test()) { | 50 | return t; |
| 80 | std::unique_lock lock{cv_mutex}; | 51 | } |
| 81 | cv.wait(lock, stop, [&slot] { return slot.turn.test(); }); | 52 | |
| 82 | } | 53 | T PopWait(std::stop_token stop_token) { |
| 83 | v = slot.move(); | 54 | T t; |
| 84 | slot.destroy(); | 55 | Pop<PopMode::WaitWithStopToken>(t, stop_token); |
| 85 | slot.turn.clear(); | 56 | return t; |
| 86 | slot.turn.notify_one(); | ||
| 87 | } | 57 | } |
| 88 | 58 | ||
| 89 | private: | 59 | private: |
| 90 | template <typename U = T> | 60 | enum class PushMode { |
| 91 | struct Slot { | 61 | Try, |
| 92 | ~Slot() noexcept { | 62 | Wait, |
| 93 | if (turn.test()) { | 63 | Count, |
| 94 | destroy(); | 64 | }; |
| 65 | |||
| 66 | enum class PopMode { | ||
| 67 | Try, | ||
| 68 | Wait, | ||
| 69 | WaitWithStopToken, | ||
| 70 | Count, | ||
| 71 | }; | ||
| 72 | |||
| 73 | template <PushMode Mode, typename... Args> | ||
| 74 | bool Emplace(Args&&... args) { | ||
| 75 | const size_t write_index = m_write_index.load(std::memory_order::relaxed); | ||
| 76 | |||
| 77 | if constexpr (Mode == PushMode::Try) { | ||
| 78 | // Check if we have free slots to write to. | ||
| 79 | if ((write_index - m_read_index.load(std::memory_order::acquire)) == Capacity) { | ||
| 80 | return false; | ||
| 95 | } | 81 | } |
| 82 | } else if constexpr (Mode == PushMode::Wait) { | ||
| 83 | // Wait until we have free slots to write to. | ||
| 84 | std::unique_lock lock{producer_cv_mutex}; | ||
| 85 | producer_cv.wait(lock, [this, write_index] { | ||
| 86 | return (write_index - m_read_index.load(std::memory_order::acquire)) < Capacity; | ||
| 87 | }); | ||
| 88 | } else { | ||
| 89 | static_assert(Mode < PushMode::Count, "Invalid PushMode."); | ||
| 96 | } | 90 | } |
| 97 | 91 | ||
| 98 | template <typename... Args> | 92 | // Determine the position to write to. |
| 99 | void construct(Args&&... args) noexcept { | 93 | const size_t pos = write_index % Capacity; |
| 100 | static_assert(std::is_nothrow_constructible_v<U, Args&&...>, | ||
| 101 | "T must be nothrow constructible with Args&&..."); | ||
| 102 | std::construct_at(reinterpret_cast<U*>(&storage), std::forward<Args>(args)...); | ||
| 103 | } | ||
| 104 | 94 | ||
| 105 | void destroy() noexcept { | 95 | // Emplace into the queue. |
| 106 | static_assert(std::is_nothrow_destructible_v<U>, "T must be nothrow destructible"); | 96 | std::construct_at(std::addressof(m_data[pos]), std::forward<Args>(args)...); |
| 107 | std::destroy_at(reinterpret_cast<U*>(&storage)); | 97 | |
| 108 | } | 98 | // Increment the write index. |
| 99 | ++m_write_index; | ||
| 100 | |||
| 101 | // Notify the consumer that we have pushed into the queue. | ||
| 102 | std::scoped_lock lock{consumer_cv_mutex}; | ||
| 103 | consumer_cv.notify_one(); | ||
| 104 | |||
| 105 | return true; | ||
| 106 | } | ||
| 107 | |||
| 108 | template <PopMode Mode> | ||
| 109 | bool Pop(T& t, [[maybe_unused]] std::stop_token stop_token = {}) { | ||
| 110 | const size_t read_index = m_read_index.load(std::memory_order::relaxed); | ||
| 109 | 111 | ||
| 110 | U&& move() noexcept { | 112 | if constexpr (Mode == PopMode::Try) { |
| 111 | return reinterpret_cast<U&&>(storage); | 113 | // Check if the queue is empty. |
| 114 | if (read_index == m_write_index.load(std::memory_order::acquire)) { | ||
| 115 | return false; | ||
| 116 | } | ||
| 117 | } else if constexpr (Mode == PopMode::Wait) { | ||
| 118 | // Wait until the queue is not empty. | ||
| 119 | std::unique_lock lock{consumer_cv_mutex}; | ||
| 120 | consumer_cv.wait(lock, [this, read_index] { | ||
| 121 | return read_index != m_write_index.load(std::memory_order::acquire); | ||
| 122 | }); | ||
| 123 | } else if constexpr (Mode == PopMode::WaitWithStopToken) { | ||
| 124 | // Wait until the queue is not empty. | ||
| 125 | std::unique_lock lock{consumer_cv_mutex}; | ||
| 126 | Common::CondvarWait(consumer_cv, lock, stop_token, [this, read_index] { | ||
| 127 | return read_index != m_write_index.load(std::memory_order::acquire); | ||
| 128 | }); | ||
| 129 | if (stop_token.stop_requested()) { | ||
| 130 | return false; | ||
| 131 | } | ||
| 132 | } else { | ||
| 133 | static_assert(Mode < PopMode::Count, "Invalid PopMode."); | ||
| 112 | } | 134 | } |
| 113 | 135 | ||
| 114 | // Align to avoid false sharing between adjacent slots | 136 | // Determine the position to read from. |
| 115 | alignas(hardware_interference_size) std::atomic_flag turn{}; | 137 | const size_t pos = read_index % Capacity; |
| 116 | struct aligned_store { | 138 | |
| 117 | struct type { | 139 | // Pop the data off the queue, moving it. |
| 118 | alignas(U) unsigned char data[sizeof(U)]; | 140 | t = std::move(m_data[pos]); |
| 119 | }; | 141 | |
| 120 | }; | 142 | // Increment the read index. |
| 121 | typename aligned_store::type storage; | 143 | ++m_read_index; |
| 122 | }; | 144 | |
| 145 | // Notify the producer that we have popped off the queue. | ||
| 146 | std::scoped_lock lock{producer_cv_mutex}; | ||
| 147 | producer_cv.notify_one(); | ||
| 148 | |||
| 149 | return true; | ||
| 150 | } | ||
| 123 | 151 | ||
| 152 | alignas(128) std::atomic_size_t m_read_index{0}; | ||
| 153 | alignas(128) std::atomic_size_t m_write_index{0}; | ||
| 154 | |||
| 155 | std::array<T, Capacity> m_data; | ||
| 156 | |||
| 157 | std::condition_variable_any producer_cv; | ||
| 158 | std::mutex producer_cv_mutex; | ||
| 159 | std::condition_variable_any consumer_cv; | ||
| 160 | std::mutex consumer_cv_mutex; | ||
| 161 | }; | ||
| 162 | |||
| 163 | template <typename T, size_t Capacity = detail::DefaultCapacity> | ||
| 164 | class MPSCQueue { | ||
| 165 | public: | ||
| 124 | template <typename... Args> | 166 | template <typename... Args> |
| 125 | void emplace(Args&&... args) noexcept { | 167 | bool TryEmplace(Args&&... args) { |
| 126 | static_assert(std::is_nothrow_constructible_v<T, Args&&...>, | 168 | std::scoped_lock lock{write_mutex}; |
| 127 | "T must be nothrow constructible with Args&&..."); | 169 | return spsc_queue.TryEmplace(std::forward<Args>(args)...); |
| 128 | auto const head = head_.fetch_add(1); | ||
| 129 | auto& slot = slots[idx(head)]; | ||
| 130 | slot.turn.wait(true); | ||
| 131 | slot.construct(std::forward<Args>(args)...); | ||
| 132 | slot.turn.test_and_set(); | ||
| 133 | cv.notify_one(); | ||
| 134 | } | 170 | } |
| 135 | 171 | ||
| 136 | constexpr size_t idx(size_t i) const noexcept { | 172 | template <typename... Args> |
| 137 | return i & mask; | 173 | void EmplaceWait(Args&&... args) { |
| 174 | std::scoped_lock lock{write_mutex}; | ||
| 175 | spsc_queue.EmplaceWait(std::forward<Args>(args)...); | ||
| 138 | } | 176 | } |
| 139 | 177 | ||
| 140 | static constexpr size_t mask = capacity - 1; | 178 | bool TryPop(T& t) { |
| 179 | return spsc_queue.TryPop(t); | ||
| 180 | } | ||
| 141 | 181 | ||
| 142 | // Align to avoid false sharing between head_ and tail_ | 182 | void PopWait(T& t) { |
| 143 | alignas(hardware_interference_size) std::atomic<size_t> head_{0}; | 183 | spsc_queue.PopWait(t); |
| 144 | alignas(hardware_interference_size) std::atomic<size_t> tail_{0}; | 184 | } |
| 145 | 185 | ||
| 146 | std::mutex cv_mutex; | 186 | void PopWait(T& t, std::stop_token stop_token) { |
| 147 | std::condition_variable_any cv; | 187 | spsc_queue.PopWait(t, stop_token); |
| 188 | } | ||
| 189 | |||
| 190 | T PopWait() { | ||
| 191 | return spsc_queue.PopWait(); | ||
| 192 | } | ||
| 148 | 193 | ||
| 149 | Slot<T>* slots; | 194 | T PopWait(std::stop_token stop_token) { |
| 150 | [[no_unique_address]] std::allocator<Slot<T>> allocator; | 195 | return spsc_queue.PopWait(stop_token); |
| 196 | } | ||
| 151 | 197 | ||
| 152 | static_assert(std::is_nothrow_copy_assignable_v<T> || std::is_nothrow_move_assignable_v<T>, | 198 | private: |
| 153 | "T must be nothrow copy or move assignable"); | 199 | SPSCQueue<T, Capacity> spsc_queue; |
| 200 | std::mutex write_mutex; | ||
| 201 | }; | ||
| 154 | 202 | ||
| 155 | static_assert(std::is_nothrow_destructible_v<T>, "T must be nothrow destructible"); | 203 | template <typename T, size_t Capacity = detail::DefaultCapacity> |
| 204 | class MPMCQueue { | ||
| 205 | public: | ||
| 206 | template <typename... Args> | ||
| 207 | bool TryEmplace(Args&&... args) { | ||
| 208 | std::scoped_lock lock{write_mutex}; | ||
| 209 | return spsc_queue.TryEmplace(std::forward<Args>(args)...); | ||
| 210 | } | ||
| 211 | |||
| 212 | template <typename... Args> | ||
| 213 | void EmplaceWait(Args&&... args) { | ||
| 214 | std::scoped_lock lock{write_mutex}; | ||
| 215 | spsc_queue.EmplaceWait(std::forward<Args>(args)...); | ||
| 216 | } | ||
| 217 | |||
| 218 | bool TryPop(T& t) { | ||
| 219 | std::scoped_lock lock{read_mutex}; | ||
| 220 | return spsc_queue.TryPop(t); | ||
| 221 | } | ||
| 222 | |||
| 223 | void PopWait(T& t) { | ||
| 224 | std::scoped_lock lock{read_mutex}; | ||
| 225 | spsc_queue.PopWait(t); | ||
| 226 | } | ||
| 227 | |||
| 228 | void PopWait(T& t, std::stop_token stop_token) { | ||
| 229 | std::scoped_lock lock{read_mutex}; | ||
| 230 | spsc_queue.PopWait(t, stop_token); | ||
| 231 | } | ||
| 232 | |||
| 233 | T PopWait() { | ||
| 234 | std::scoped_lock lock{read_mutex}; | ||
| 235 | return spsc_queue.PopWait(); | ||
| 236 | } | ||
| 237 | |||
| 238 | T PopWait(std::stop_token stop_token) { | ||
| 239 | std::scoped_lock lock{read_mutex}; | ||
| 240 | return spsc_queue.PopWait(stop_token); | ||
| 241 | } | ||
| 242 | |||
| 243 | private: | ||
| 244 | SPSCQueue<T, Capacity> spsc_queue; | ||
| 245 | std::mutex write_mutex; | ||
| 246 | std::mutex read_mutex; | ||
| 156 | }; | 247 | }; |
| 157 | 248 | ||
| 158 | } // namespace Common | 249 | } // namespace Common |
diff --git a/src/common/concepts.h b/src/common/concepts.h index a9acff3e7..61df1d32a 100644 --- a/src/common/concepts.h +++ b/src/common/concepts.h | |||
| @@ -16,9 +16,9 @@ concept IsContiguousContainer = std::contiguous_iterator<typename T::iterator>; | |||
| 16 | // is available on all supported platforms. | 16 | // is available on all supported platforms. |
| 17 | template <typename Derived, typename Base> | 17 | template <typename Derived, typename Base> |
| 18 | concept DerivedFrom = requires { | 18 | concept DerivedFrom = requires { |
| 19 | std::is_base_of_v<Base, Derived>; | 19 | std::is_base_of_v<Base, Derived>; |
| 20 | std::is_convertible_v<const volatile Derived*, const volatile Base*>; | 20 | std::is_convertible_v<const volatile Derived*, const volatile Base*>; |
| 21 | }; | 21 | }; |
| 22 | 22 | ||
| 23 | // TODO: Replace with std::convertible_to when libc++ implements it. | 23 | // TODO: Replace with std::convertible_to when libc++ implements it. |
| 24 | template <typename From, typename To> | 24 | template <typename From, typename To> |
diff --git a/src/common/container_hash.h b/src/common/container_hash.h new file mode 100644 index 000000000..a5e357745 --- /dev/null +++ b/src/common/container_hash.h | |||
| @@ -0,0 +1,92 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2005-2014 Daniel James | ||
| 2 | // SPDX-FileCopyrightText: 2016 Austin Appleby | ||
| 3 | // SPDX-License-Identifier: BSL-1.0 | ||
| 4 | |||
| 5 | #include <array> | ||
| 6 | #include <climits> | ||
| 7 | #include <cstdint> | ||
| 8 | #include <limits> | ||
| 9 | #include <type_traits> | ||
| 10 | #include <vector> | ||
| 11 | |||
| 12 | namespace Common { | ||
| 13 | |||
| 14 | namespace detail { | ||
| 15 | |||
| 16 | template <typename T> | ||
| 17 | requires std::is_unsigned_v<T> | ||
| 18 | inline std::size_t HashValue(T val) { | ||
| 19 | const unsigned int size_t_bits = std::numeric_limits<std::size_t>::digits; | ||
| 20 | const unsigned int length = | ||
| 21 | (std::numeric_limits<T>::digits - 1) / static_cast<unsigned int>(size_t_bits); | ||
| 22 | |||
| 23 | std::size_t seed = 0; | ||
| 24 | |||
| 25 | for (unsigned int i = length * size_t_bits; i > 0; i -= size_t_bits) { | ||
| 26 | seed ^= static_cast<size_t>(val >> i) + (seed << 6) + (seed >> 2); | ||
| 27 | } | ||
| 28 | |||
| 29 | seed ^= static_cast<size_t>(val) + (seed << 6) + (seed >> 2); | ||
| 30 | |||
| 31 | return seed; | ||
| 32 | } | ||
| 33 | |||
| 34 | template <size_t Bits> | ||
| 35 | struct HashCombineImpl { | ||
| 36 | template <typename T> | ||
| 37 | static inline T fn(T seed, T value) { | ||
| 38 | seed ^= value + 0x9e3779b9 + (seed << 6) + (seed >> 2); | ||
| 39 | return seed; | ||
| 40 | } | ||
| 41 | }; | ||
| 42 | |||
| 43 | template <> | ||
| 44 | struct HashCombineImpl<64> { | ||
| 45 | static inline std::uint64_t fn(std::uint64_t h, std::uint64_t k) { | ||
| 46 | const std::uint64_t m = (std::uint64_t(0xc6a4a793) << 32) + 0x5bd1e995; | ||
| 47 | const int r = 47; | ||
| 48 | |||
| 49 | k *= m; | ||
| 50 | k ^= k >> r; | ||
| 51 | k *= m; | ||
| 52 | |||
| 53 | h ^= k; | ||
| 54 | h *= m; | ||
| 55 | |||
| 56 | // Completely arbitrary number, to prevent 0's | ||
| 57 | // from hashing to 0. | ||
| 58 | h += 0xe6546b64; | ||
| 59 | |||
| 60 | return h; | ||
| 61 | } | ||
| 62 | }; | ||
| 63 | |||
| 64 | } // namespace detail | ||
| 65 | |||
| 66 | template <typename T> | ||
| 67 | inline void HashCombine(std::size_t& seed, const T& v) { | ||
| 68 | seed = detail::HashCombineImpl<sizeof(std::size_t) * CHAR_BIT>::fn(seed, detail::HashValue(v)); | ||
| 69 | } | ||
| 70 | |||
| 71 | template <typename It> | ||
| 72 | inline std::size_t HashRange(It first, It last) { | ||
| 73 | std::size_t seed = 0; | ||
| 74 | |||
| 75 | for (; first != last; ++first) { | ||
| 76 | HashCombine<typename std::iterator_traits<It>::value_type>(seed, *first); | ||
| 77 | } | ||
| 78 | |||
| 79 | return seed; | ||
| 80 | } | ||
| 81 | |||
| 82 | template <typename T, size_t Size> | ||
| 83 | std::size_t HashValue(const std::array<T, Size>& v) { | ||
| 84 | return HashRange(v.cbegin(), v.cend()); | ||
| 85 | } | ||
| 86 | |||
| 87 | template <typename T, typename Allocator> | ||
| 88 | std::size_t HashValue(const std::vector<T, Allocator>& v) { | ||
| 89 | return HashRange(v.cbegin(), v.cend()); | ||
| 90 | } | ||
| 91 | |||
| 92 | } // namespace Common | ||
diff --git a/src/common/demangle.cpp b/src/common/demangle.cpp index f4246f666..3310faf86 100644 --- a/src/common/demangle.cpp +++ b/src/common/demangle.cpp | |||
| @@ -1,13 +1,11 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #include <llvm/Demangle/Demangle.h> | ||
| 5 | |||
| 4 | #include "common/demangle.h" | 6 | #include "common/demangle.h" |
| 5 | #include "common/scope_exit.h" | 7 | #include "common/scope_exit.h" |
| 6 | 8 | ||
| 7 | namespace llvm { | ||
| 8 | char* itaniumDemangle(const char* mangled_name, char* buf, size_t* n, int* status); | ||
| 9 | } | ||
| 10 | |||
| 11 | namespace Common { | 9 | namespace Common { |
| 12 | 10 | ||
| 13 | std::string DemangleSymbol(const std::string& mangled) { | 11 | std::string DemangleSymbol(const std::string& mangled) { |
diff --git a/src/common/div_ceil.h b/src/common/div_ceil.h index eebc279c2..c12477d42 100644 --- a/src/common/div_ceil.h +++ b/src/common/div_ceil.h | |||
| @@ -10,14 +10,14 @@ namespace Common { | |||
| 10 | 10 | ||
| 11 | /// Ceiled integer division. | 11 | /// Ceiled integer division. |
| 12 | template <typename N, typename D> | 12 | template <typename N, typename D> |
| 13 | requires std::is_integral_v<N> && std::is_unsigned_v<D> | 13 | requires std::is_integral_v<N> && std::is_unsigned_v<D> |
| 14 | [[nodiscard]] constexpr N DivCeil(N number, D divisor) { | 14 | [[nodiscard]] constexpr N DivCeil(N number, D divisor) { |
| 15 | return static_cast<N>((static_cast<D>(number) + divisor - 1) / divisor); | 15 | return static_cast<N>((static_cast<D>(number) + divisor - 1) / divisor); |
| 16 | } | 16 | } |
| 17 | 17 | ||
| 18 | /// Ceiled integer division with logarithmic divisor in base 2 | 18 | /// Ceiled integer division with logarithmic divisor in base 2 |
| 19 | template <typename N, typename D> | 19 | template <typename N, typename D> |
| 20 | requires std::is_integral_v<N> && std::is_unsigned_v<D> | 20 | requires std::is_integral_v<N> && std::is_unsigned_v<D> |
| 21 | [[nodiscard]] constexpr N DivCeilLog2(N value, D alignment_log2) { | 21 | [[nodiscard]] constexpr N DivCeilLog2(N value, D alignment_log2) { |
| 22 | return static_cast<N>((static_cast<D>(value) + (D(1) << alignment_log2) - 1) >> alignment_log2); | 22 | return static_cast<N>((static_cast<D>(value) + (D(1) << alignment_log2) - 1) >> alignment_log2); |
| 23 | } | 23 | } |
diff --git a/src/common/dynamic_library.cpp b/src/common/dynamic_library.cpp index 054277a2b..4fabe7e52 100644 --- a/src/common/dynamic_library.cpp +++ b/src/common/dynamic_library.cpp | |||
| @@ -22,6 +22,8 @@ DynamicLibrary::DynamicLibrary(const char* filename) { | |||
| 22 | void(Open(filename)); | 22 | void(Open(filename)); |
| 23 | } | 23 | } |
| 24 | 24 | ||
| 25 | DynamicLibrary::DynamicLibrary(void* handle_) : handle{handle_} {} | ||
| 26 | |||
| 25 | DynamicLibrary::DynamicLibrary(DynamicLibrary&& rhs) noexcept | 27 | DynamicLibrary::DynamicLibrary(DynamicLibrary&& rhs) noexcept |
| 26 | : handle{std::exchange(rhs.handle, nullptr)} {} | 28 | : handle{std::exchange(rhs.handle, nullptr)} {} |
| 27 | 29 | ||
diff --git a/src/common/dynamic_library.h b/src/common/dynamic_library.h index f42bdf441..662d454d4 100644 --- a/src/common/dynamic_library.h +++ b/src/common/dynamic_library.h | |||
| @@ -20,6 +20,9 @@ public: | |||
| 20 | /// Automatically loads the specified library. Call IsOpen() to check validity before use. | 20 | /// Automatically loads the specified library. Call IsOpen() to check validity before use. |
| 21 | explicit DynamicLibrary(const char* filename); | 21 | explicit DynamicLibrary(const char* filename); |
| 22 | 22 | ||
| 23 | /// Initializes the dynamic library with an already opened handle. | ||
| 24 | explicit DynamicLibrary(void* handle_); | ||
| 25 | |||
| 23 | /// Moves the library. | 26 | /// Moves the library. |
| 24 | DynamicLibrary(DynamicLibrary&&) noexcept; | 27 | DynamicLibrary(DynamicLibrary&&) noexcept; |
| 25 | DynamicLibrary& operator=(DynamicLibrary&&) noexcept; | 28 | DynamicLibrary& operator=(DynamicLibrary&&) noexcept; |
diff --git a/src/common/error.cpp b/src/common/error.cpp index ddb03bd45..1b2009db7 100644 --- a/src/common/error.cpp +++ b/src/common/error.cpp | |||
| @@ -30,7 +30,8 @@ std::string NativeErrorToString(int e) { | |||
| 30 | return ret; | 30 | return ret; |
| 31 | #else | 31 | #else |
| 32 | char err_str[255]; | 32 | char err_str[255]; |
| 33 | #if defined(__GLIBC__) && (_GNU_SOURCE || (_POSIX_C_SOURCE < 200112L && _XOPEN_SOURCE < 600)) | 33 | #if defined(ANDROID) || \ |
| 34 | (defined(__GLIBC__) && (_GNU_SOURCE || (_POSIX_C_SOURCE < 200112L && _XOPEN_SOURCE < 600))) | ||
| 34 | // Thread safe (GNU-specific) | 35 | // Thread safe (GNU-specific) |
| 35 | const char* str = strerror_r(e, err_str, sizeof(err_str)); | 36 | const char* str = strerror_r(e, err_str, sizeof(err_str)); |
| 36 | return std::string(str); | 37 | return std::string(str); |
diff --git a/src/common/expected.h b/src/common/expected.h index 6e6c86ee7..5fccfbcbd 100644 --- a/src/common/expected.h +++ b/src/common/expected.h | |||
| @@ -64,7 +64,7 @@ struct no_init_t { | |||
| 64 | * Additionally, this requires E to be trivially destructible | 64 | * Additionally, this requires E to be trivially destructible |
| 65 | */ | 65 | */ |
| 66 | template <typename T, typename E, bool = std::is_trivially_destructible_v<T>> | 66 | template <typename T, typename E, bool = std::is_trivially_destructible_v<T>> |
| 67 | requires std::is_trivially_destructible_v<E> | 67 | requires std::is_trivially_destructible_v<E> |
| 68 | struct expected_storage_base { | 68 | struct expected_storage_base { |
| 69 | constexpr expected_storage_base() : m_val{T{}}, m_has_val{true} {} | 69 | constexpr expected_storage_base() : m_val{T{}}, m_has_val{true} {} |
| 70 | 70 | ||
| @@ -111,7 +111,7 @@ struct expected_storage_base { | |||
| 111 | * Additionally, this requires E to be trivially destructible | 111 | * Additionally, this requires E to be trivially destructible |
| 112 | */ | 112 | */ |
| 113 | template <typename T, typename E> | 113 | template <typename T, typename E> |
| 114 | requires std::is_trivially_destructible_v<E> | 114 | requires std::is_trivially_destructible_v<E> |
| 115 | struct expected_storage_base<T, E, true> { | 115 | struct expected_storage_base<T, E, true> { |
| 116 | constexpr expected_storage_base() : m_val{T{}}, m_has_val{true} {} | 116 | constexpr expected_storage_base() : m_val{T{}}, m_has_val{true} {} |
| 117 | 117 | ||
| @@ -251,7 +251,7 @@ struct expected_operations_base : expected_storage_base<T, E> { | |||
| 251 | * Additionally, this requires E to be trivially copy constructible | 251 | * Additionally, this requires E to be trivially copy constructible |
| 252 | */ | 252 | */ |
| 253 | template <typename T, typename E, bool = std::is_trivially_copy_constructible_v<T>> | 253 | template <typename T, typename E, bool = std::is_trivially_copy_constructible_v<T>> |
| 254 | requires std::is_trivially_copy_constructible_v<E> | 254 | requires std::is_trivially_copy_constructible_v<E> |
| 255 | struct expected_copy_base : expected_operations_base<T, E> { | 255 | struct expected_copy_base : expected_operations_base<T, E> { |
| 256 | using expected_operations_base<T, E>::expected_operations_base; | 256 | using expected_operations_base<T, E>::expected_operations_base; |
| 257 | }; | 257 | }; |
| @@ -261,7 +261,7 @@ struct expected_copy_base : expected_operations_base<T, E> { | |||
| 261 | * Additionally, this requires E to be trivially copy constructible | 261 | * Additionally, this requires E to be trivially copy constructible |
| 262 | */ | 262 | */ |
| 263 | template <typename T, typename E> | 263 | template <typename T, typename E> |
| 264 | requires std::is_trivially_copy_constructible_v<E> | 264 | requires std::is_trivially_copy_constructible_v<E> |
| 265 | struct expected_copy_base<T, E, false> : expected_operations_base<T, E> { | 265 | struct expected_copy_base<T, E, false> : expected_operations_base<T, E> { |
| 266 | using expected_operations_base<T, E>::expected_operations_base; | 266 | using expected_operations_base<T, E>::expected_operations_base; |
| 267 | 267 | ||
| @@ -289,7 +289,7 @@ struct expected_copy_base<T, E, false> : expected_operations_base<T, E> { | |||
| 289 | * Additionally, this requires E to be trivially move constructible | 289 | * Additionally, this requires E to be trivially move constructible |
| 290 | */ | 290 | */ |
| 291 | template <typename T, typename E, bool = std::is_trivially_move_constructible_v<T>> | 291 | template <typename T, typename E, bool = std::is_trivially_move_constructible_v<T>> |
| 292 | requires std::is_trivially_move_constructible_v<E> | 292 | requires std::is_trivially_move_constructible_v<E> |
| 293 | struct expected_move_base : expected_copy_base<T, E> { | 293 | struct expected_move_base : expected_copy_base<T, E> { |
| 294 | using expected_copy_base<T, E>::expected_copy_base; | 294 | using expected_copy_base<T, E>::expected_copy_base; |
| 295 | }; | 295 | }; |
| @@ -299,7 +299,7 @@ struct expected_move_base : expected_copy_base<T, E> { | |||
| 299 | * Additionally, this requires E to be trivially move constructible | 299 | * Additionally, this requires E to be trivially move constructible |
| 300 | */ | 300 | */ |
| 301 | template <typename T, typename E> | 301 | template <typename T, typename E> |
| 302 | requires std::is_trivially_move_constructible_v<E> | 302 | requires std::is_trivially_move_constructible_v<E> |
| 303 | struct expected_move_base<T, E, false> : expected_copy_base<T, E> { | 303 | struct expected_move_base<T, E, false> : expected_copy_base<T, E> { |
| 304 | using expected_copy_base<T, E>::expected_copy_base; | 304 | using expected_copy_base<T, E>::expected_copy_base; |
| 305 | 305 | ||
| @@ -330,9 +330,9 @@ template <typename T, typename E, | |||
| 330 | bool = std::conjunction_v<std::is_trivially_copy_assignable<T>, | 330 | bool = std::conjunction_v<std::is_trivially_copy_assignable<T>, |
| 331 | std::is_trivially_copy_constructible<T>, | 331 | std::is_trivially_copy_constructible<T>, |
| 332 | std::is_trivially_destructible<T>>> | 332 | std::is_trivially_destructible<T>>> |
| 333 | requires std::conjunction_v<std::is_trivially_copy_assignable<E>, | 333 | requires std::conjunction_v<std::is_trivially_copy_assignable<E>, |
| 334 | std::is_trivially_copy_constructible<E>, | 334 | std::is_trivially_copy_constructible<E>, |
| 335 | std::is_trivially_destructible<E>> | 335 | std::is_trivially_destructible<E>> |
| 336 | struct expected_copy_assign_base : expected_move_base<T, E> { | 336 | struct expected_copy_assign_base : expected_move_base<T, E> { |
| 337 | using expected_move_base<T, E>::expected_move_base; | 337 | using expected_move_base<T, E>::expected_move_base; |
| 338 | }; | 338 | }; |
| @@ -342,9 +342,9 @@ struct expected_copy_assign_base : expected_move_base<T, E> { | |||
| 342 | * Additionally, this requires E to be trivially copy assignable | 342 | * Additionally, this requires E to be trivially copy assignable |
| 343 | */ | 343 | */ |
| 344 | template <typename T, typename E> | 344 | template <typename T, typename E> |
| 345 | requires std::conjunction_v<std::is_trivially_copy_assignable<E>, | 345 | requires std::conjunction_v<std::is_trivially_copy_assignable<E>, |
| 346 | std::is_trivially_copy_constructible<E>, | 346 | std::is_trivially_copy_constructible<E>, |
| 347 | std::is_trivially_destructible<E>> | 347 | std::is_trivially_destructible<E>> |
| 348 | struct expected_copy_assign_base<T, E, false> : expected_move_base<T, E> { | 348 | struct expected_copy_assign_base<T, E, false> : expected_move_base<T, E> { |
| 349 | using expected_move_base<T, E>::expected_move_base; | 349 | using expected_move_base<T, E>::expected_move_base; |
| 350 | 350 | ||
| @@ -371,9 +371,9 @@ template <typename T, typename E, | |||
| 371 | bool = std::conjunction_v<std::is_trivially_move_assignable<T>, | 371 | bool = std::conjunction_v<std::is_trivially_move_assignable<T>, |
| 372 | std::is_trivially_move_constructible<T>, | 372 | std::is_trivially_move_constructible<T>, |
| 373 | std::is_trivially_destructible<T>>> | 373 | std::is_trivially_destructible<T>>> |
| 374 | requires std::conjunction_v<std::is_trivially_move_assignable<E>, | 374 | requires std::conjunction_v<std::is_trivially_move_assignable<E>, |
| 375 | std::is_trivially_move_constructible<E>, | 375 | std::is_trivially_move_constructible<E>, |
| 376 | std::is_trivially_destructible<E>> | 376 | std::is_trivially_destructible<E>> |
| 377 | struct expected_move_assign_base : expected_copy_assign_base<T, E> { | 377 | struct expected_move_assign_base : expected_copy_assign_base<T, E> { |
| 378 | using expected_copy_assign_base<T, E>::expected_copy_assign_base; | 378 | using expected_copy_assign_base<T, E>::expected_copy_assign_base; |
| 379 | }; | 379 | }; |
| @@ -383,9 +383,9 @@ struct expected_move_assign_base : expected_copy_assign_base<T, E> { | |||
| 383 | * Additionally, this requires E to be trivially move assignable | 383 | * Additionally, this requires E to be trivially move assignable |
| 384 | */ | 384 | */ |
| 385 | template <typename T, typename E> | 385 | template <typename T, typename E> |
| 386 | requires std::conjunction_v<std::is_trivially_move_assignable<E>, | 386 | requires std::conjunction_v<std::is_trivially_move_assignable<E>, |
| 387 | std::is_trivially_move_constructible<E>, | 387 | std::is_trivially_move_constructible<E>, |
| 388 | std::is_trivially_destructible<E>> | 388 | std::is_trivially_destructible<E>> |
| 389 | struct expected_move_assign_base<T, E, false> : expected_copy_assign_base<T, E> { | 389 | struct expected_move_assign_base<T, E, false> : expected_copy_assign_base<T, E> { |
| 390 | using expected_copy_assign_base<T, E>::expected_copy_assign_base; | 390 | using expected_copy_assign_base<T, E>::expected_copy_assign_base; |
| 391 | 391 | ||
| @@ -412,7 +412,7 @@ struct expected_move_assign_base<T, E, false> : expected_copy_assign_base<T, E> | |||
| 412 | */ | 412 | */ |
| 413 | template <typename T, typename E, bool EnableCopy = std::is_copy_constructible_v<T>, | 413 | template <typename T, typename E, bool EnableCopy = std::is_copy_constructible_v<T>, |
| 414 | bool EnableMove = std::is_move_constructible_v<T>> | 414 | bool EnableMove = std::is_move_constructible_v<T>> |
| 415 | requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>> | 415 | requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>> |
| 416 | struct expected_delete_ctor_base { | 416 | struct expected_delete_ctor_base { |
| 417 | expected_delete_ctor_base() = default; | 417 | expected_delete_ctor_base() = default; |
| 418 | expected_delete_ctor_base(const expected_delete_ctor_base&) = default; | 418 | expected_delete_ctor_base(const expected_delete_ctor_base&) = default; |
| @@ -422,7 +422,7 @@ struct expected_delete_ctor_base { | |||
| 422 | }; | 422 | }; |
| 423 | 423 | ||
| 424 | template <typename T, typename E> | 424 | template <typename T, typename E> |
| 425 | requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>> | 425 | requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>> |
| 426 | struct expected_delete_ctor_base<T, E, true, false> { | 426 | struct expected_delete_ctor_base<T, E, true, false> { |
| 427 | expected_delete_ctor_base() = default; | 427 | expected_delete_ctor_base() = default; |
| 428 | expected_delete_ctor_base(const expected_delete_ctor_base&) = default; | 428 | expected_delete_ctor_base(const expected_delete_ctor_base&) = default; |
| @@ -432,7 +432,7 @@ struct expected_delete_ctor_base<T, E, true, false> { | |||
| 432 | }; | 432 | }; |
| 433 | 433 | ||
| 434 | template <typename T, typename E> | 434 | template <typename T, typename E> |
| 435 | requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>> | 435 | requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>> |
| 436 | struct expected_delete_ctor_base<T, E, false, true> { | 436 | struct expected_delete_ctor_base<T, E, false, true> { |
| 437 | expected_delete_ctor_base() = default; | 437 | expected_delete_ctor_base() = default; |
| 438 | expected_delete_ctor_base(const expected_delete_ctor_base&) = delete; | 438 | expected_delete_ctor_base(const expected_delete_ctor_base&) = delete; |
| @@ -442,7 +442,7 @@ struct expected_delete_ctor_base<T, E, false, true> { | |||
| 442 | }; | 442 | }; |
| 443 | 443 | ||
| 444 | template <typename T, typename E> | 444 | template <typename T, typename E> |
| 445 | requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>> | 445 | requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>> |
| 446 | struct expected_delete_ctor_base<T, E, false, false> { | 446 | struct expected_delete_ctor_base<T, E, false, false> { |
| 447 | expected_delete_ctor_base() = default; | 447 | expected_delete_ctor_base() = default; |
| 448 | expected_delete_ctor_base(const expected_delete_ctor_base&) = delete; | 448 | expected_delete_ctor_base(const expected_delete_ctor_base&) = delete; |
| @@ -460,8 +460,8 @@ template < | |||
| 460 | typename T, typename E, | 460 | typename T, typename E, |
| 461 | bool EnableCopy = std::conjunction_v<std::is_copy_constructible<T>, std::is_copy_assignable<T>>, | 461 | bool EnableCopy = std::conjunction_v<std::is_copy_constructible<T>, std::is_copy_assignable<T>>, |
| 462 | bool EnableMove = std::conjunction_v<std::is_move_constructible<T>, std::is_move_assignable<T>>> | 462 | bool EnableMove = std::conjunction_v<std::is_move_constructible<T>, std::is_move_assignable<T>>> |
| 463 | requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>, | 463 | requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>, |
| 464 | std::is_copy_assignable<E>, std::is_move_assignable<E>> | 464 | std::is_copy_assignable<E>, std::is_move_assignable<E>> |
| 465 | struct expected_delete_assign_base { | 465 | struct expected_delete_assign_base { |
| 466 | expected_delete_assign_base() = default; | 466 | expected_delete_assign_base() = default; |
| 467 | expected_delete_assign_base(const expected_delete_assign_base&) = default; | 467 | expected_delete_assign_base(const expected_delete_assign_base&) = default; |
| @@ -471,8 +471,8 @@ struct expected_delete_assign_base { | |||
| 471 | }; | 471 | }; |
| 472 | 472 | ||
| 473 | template <typename T, typename E> | 473 | template <typename T, typename E> |
| 474 | requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>, | 474 | requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>, |
| 475 | std::is_copy_assignable<E>, std::is_move_assignable<E>> | 475 | std::is_copy_assignable<E>, std::is_move_assignable<E>> |
| 476 | struct expected_delete_assign_base<T, E, true, false> { | 476 | struct expected_delete_assign_base<T, E, true, false> { |
| 477 | expected_delete_assign_base() = default; | 477 | expected_delete_assign_base() = default; |
| 478 | expected_delete_assign_base(const expected_delete_assign_base&) = default; | 478 | expected_delete_assign_base(const expected_delete_assign_base&) = default; |
| @@ -482,8 +482,8 @@ struct expected_delete_assign_base<T, E, true, false> { | |||
| 482 | }; | 482 | }; |
| 483 | 483 | ||
| 484 | template <typename T, typename E> | 484 | template <typename T, typename E> |
| 485 | requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>, | 485 | requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>, |
| 486 | std::is_copy_assignable<E>, std::is_move_assignable<E>> | 486 | std::is_copy_assignable<E>, std::is_move_assignable<E>> |
| 487 | struct expected_delete_assign_base<T, E, false, true> { | 487 | struct expected_delete_assign_base<T, E, false, true> { |
| 488 | expected_delete_assign_base() = default; | 488 | expected_delete_assign_base() = default; |
| 489 | expected_delete_assign_base(const expected_delete_assign_base&) = default; | 489 | expected_delete_assign_base(const expected_delete_assign_base&) = default; |
| @@ -493,8 +493,8 @@ struct expected_delete_assign_base<T, E, false, true> { | |||
| 493 | }; | 493 | }; |
| 494 | 494 | ||
| 495 | template <typename T, typename E> | 495 | template <typename T, typename E> |
| 496 | requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>, | 496 | requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>, |
| 497 | std::is_copy_assignable<E>, std::is_move_assignable<E>> | 497 | std::is_copy_assignable<E>, std::is_move_assignable<E>> |
| 498 | struct expected_delete_assign_base<T, E, false, false> { | 498 | struct expected_delete_assign_base<T, E, false, false> { |
| 499 | expected_delete_assign_base() = default; | 499 | expected_delete_assign_base() = default; |
| 500 | expected_delete_assign_base(const expected_delete_assign_base&) = default; | 500 | expected_delete_assign_base(const expected_delete_assign_base&) = default; |
diff --git a/src/common/fiber.cpp b/src/common/fiber.cpp index bc92b360b..c991b7cf1 100644 --- a/src/common/fiber.cpp +++ b/src/common/fiber.cpp | |||
| @@ -90,7 +90,7 @@ Fiber::~Fiber() { | |||
| 90 | } | 90 | } |
| 91 | 91 | ||
| 92 | void Fiber::Exit() { | 92 | void Fiber::Exit() { |
| 93 | ASSERT_MSG(impl->is_thread_fiber, "Exitting non main thread fiber"); | 93 | ASSERT_MSG(impl->is_thread_fiber, "Exiting non main thread fiber"); |
| 94 | if (!impl->is_thread_fiber) { | 94 | if (!impl->is_thread_fiber) { |
| 95 | return; | 95 | return; |
| 96 | } | 96 | } |
diff --git a/src/common/fixed_point.h b/src/common/fixed_point.h index f899b0d54..b0f3ae2cc 100644 --- a/src/common/fixed_point.h +++ b/src/common/fixed_point.h | |||
| @@ -22,7 +22,7 @@ class FixedPoint; | |||
| 22 | namespace detail { | 22 | namespace detail { |
| 23 | 23 | ||
| 24 | // helper templates to make magic with types :) | 24 | // helper templates to make magic with types :) |
| 25 | // these allow us to determine resonable types from | 25 | // these allow us to determine reasonable types from |
| 26 | // a desired size, they also let us infer the next largest type | 26 | // a desired size, they also let us infer the next largest type |
| 27 | // from a type which is nice for the division op | 27 | // from a type which is nice for the division op |
| 28 | template <size_t T> | 28 | template <size_t T> |
diff --git a/src/common/fs/file.cpp b/src/common/fs/file.cpp index 656b03cc5..b0b25eb43 100644 --- a/src/common/fs/file.cpp +++ b/src/common/fs/file.cpp | |||
| @@ -5,6 +5,9 @@ | |||
| 5 | 5 | ||
| 6 | #include "common/fs/file.h" | 6 | #include "common/fs/file.h" |
| 7 | #include "common/fs/fs.h" | 7 | #include "common/fs/fs.h" |
| 8 | #ifdef ANDROID | ||
| 9 | #include "common/fs/fs_android.h" | ||
| 10 | #endif | ||
| 8 | #include "common/logging/log.h" | 11 | #include "common/logging/log.h" |
| 9 | 12 | ||
| 10 | #ifdef _WIN32 | 13 | #ifdef _WIN32 |
| @@ -252,6 +255,23 @@ void IOFile::Open(const fs::path& path, FileAccessMode mode, FileType type, File | |||
| 252 | } else { | 255 | } else { |
| 253 | _wfopen_s(&file, path.c_str(), AccessModeToWStr(mode, type)); | 256 | _wfopen_s(&file, path.c_str(), AccessModeToWStr(mode, type)); |
| 254 | } | 257 | } |
| 258 | #elif ANDROID | ||
| 259 | if (Android::IsContentUri(path)) { | ||
| 260 | ASSERT_MSG(mode == FileAccessMode::Read, "Content URI file access is for read-only!"); | ||
| 261 | const auto fd = Android::OpenContentUri(path, Android::OpenMode::Read); | ||
| 262 | if (fd != -1) { | ||
| 263 | file = fdopen(fd, "r"); | ||
| 264 | const auto error_num = errno; | ||
| 265 | if (error_num != 0 && file == nullptr) { | ||
| 266 | LOG_ERROR(Common_Filesystem, "Error opening file: {}, error: {}", path.c_str(), | ||
| 267 | strerror(error_num)); | ||
| 268 | } | ||
| 269 | } else { | ||
| 270 | LOG_ERROR(Common_Filesystem, "Error opening file: {}", path.c_str()); | ||
| 271 | } | ||
| 272 | } else { | ||
| 273 | file = std::fopen(path.c_str(), AccessModeToStr(mode, type)); | ||
| 274 | } | ||
| 255 | #else | 275 | #else |
| 256 | file = std::fopen(path.c_str(), AccessModeToStr(mode, type)); | 276 | file = std::fopen(path.c_str(), AccessModeToStr(mode, type)); |
| 257 | #endif | 277 | #endif |
| @@ -372,6 +392,23 @@ u64 IOFile::GetSize() const { | |||
| 372 | // Flush any unwritten buffered data into the file prior to retrieving the file size. | 392 | // Flush any unwritten buffered data into the file prior to retrieving the file size. |
| 373 | std::fflush(file); | 393 | std::fflush(file); |
| 374 | 394 | ||
| 395 | #if ANDROID | ||
| 396 | u64 file_size = 0; | ||
| 397 | if (Android::IsContentUri(file_path)) { | ||
| 398 | file_size = Android::GetSize(file_path); | ||
| 399 | } else { | ||
| 400 | std::error_code ec; | ||
| 401 | |||
| 402 | file_size = fs::file_size(file_path, ec); | ||
| 403 | |||
| 404 | if (ec) { | ||
| 405 | LOG_ERROR(Common_Filesystem, | ||
| 406 | "Failed to retrieve the file size of path={}, ec_message={}", | ||
| 407 | PathToUTF8String(file_path), ec.message()); | ||
| 408 | return 0; | ||
| 409 | } | ||
| 410 | } | ||
| 411 | #else | ||
| 375 | std::error_code ec; | 412 | std::error_code ec; |
| 376 | 413 | ||
| 377 | const auto file_size = fs::file_size(file_path, ec); | 414 | const auto file_size = fs::file_size(file_path, ec); |
| @@ -381,6 +418,7 @@ u64 IOFile::GetSize() const { | |||
| 381 | PathToUTF8String(file_path), ec.message()); | 418 | PathToUTF8String(file_path), ec.message()); |
| 382 | return 0; | 419 | return 0; |
| 383 | } | 420 | } |
| 421 | #endif | ||
| 384 | 422 | ||
| 385 | return file_size; | 423 | return file_size; |
| 386 | } | 424 | } |
diff --git a/src/common/fs/fs.cpp b/src/common/fs/fs.cpp index e1716c62d..36e67c145 100644 --- a/src/common/fs/fs.cpp +++ b/src/common/fs/fs.cpp | |||
| @@ -3,6 +3,9 @@ | |||
| 3 | 3 | ||
| 4 | #include "common/fs/file.h" | 4 | #include "common/fs/file.h" |
| 5 | #include "common/fs/fs.h" | 5 | #include "common/fs/fs.h" |
| 6 | #ifdef ANDROID | ||
| 7 | #include "common/fs/fs_android.h" | ||
| 8 | #endif | ||
| 6 | #include "common/fs/path_util.h" | 9 | #include "common/fs/path_util.h" |
| 7 | #include "common/logging/log.h" | 10 | #include "common/logging/log.h" |
| 8 | 11 | ||
| @@ -433,7 +436,7 @@ void IterateDirEntries(const std::filesystem::path& path, const DirEntryCallable | |||
| 433 | 436 | ||
| 434 | if (True(filter & DirEntryFilter::File) && | 437 | if (True(filter & DirEntryFilter::File) && |
| 435 | entry.status().type() == fs::file_type::regular) { | 438 | entry.status().type() == fs::file_type::regular) { |
| 436 | if (!callback(entry.path())) { | 439 | if (!callback(entry)) { |
| 437 | callback_error = true; | 440 | callback_error = true; |
| 438 | break; | 441 | break; |
| 439 | } | 442 | } |
| @@ -441,7 +444,7 @@ void IterateDirEntries(const std::filesystem::path& path, const DirEntryCallable | |||
| 441 | 444 | ||
| 442 | if (True(filter & DirEntryFilter::Directory) && | 445 | if (True(filter & DirEntryFilter::Directory) && |
| 443 | entry.status().type() == fs::file_type::directory) { | 446 | entry.status().type() == fs::file_type::directory) { |
| 444 | if (!callback(entry.path())) { | 447 | if (!callback(entry)) { |
| 445 | callback_error = true; | 448 | callback_error = true; |
| 446 | break; | 449 | break; |
| 447 | } | 450 | } |
| @@ -490,7 +493,7 @@ void IterateDirEntriesRecursively(const std::filesystem::path& path, | |||
| 490 | 493 | ||
| 491 | if (True(filter & DirEntryFilter::File) && | 494 | if (True(filter & DirEntryFilter::File) && |
| 492 | entry.status().type() == fs::file_type::regular) { | 495 | entry.status().type() == fs::file_type::regular) { |
| 493 | if (!callback(entry.path())) { | 496 | if (!callback(entry)) { |
| 494 | callback_error = true; | 497 | callback_error = true; |
| 495 | break; | 498 | break; |
| 496 | } | 499 | } |
| @@ -498,7 +501,7 @@ void IterateDirEntriesRecursively(const std::filesystem::path& path, | |||
| 498 | 501 | ||
| 499 | if (True(filter & DirEntryFilter::Directory) && | 502 | if (True(filter & DirEntryFilter::Directory) && |
| 500 | entry.status().type() == fs::file_type::directory) { | 503 | entry.status().type() == fs::file_type::directory) { |
| 501 | if (!callback(entry.path())) { | 504 | if (!callback(entry)) { |
| 502 | callback_error = true; | 505 | callback_error = true; |
| 503 | break; | 506 | break; |
| 504 | } | 507 | } |
| @@ -525,15 +528,39 @@ void IterateDirEntriesRecursively(const std::filesystem::path& path, | |||
| 525 | // Generic Filesystem Operations | 528 | // Generic Filesystem Operations |
| 526 | 529 | ||
| 527 | bool Exists(const fs::path& path) { | 530 | bool Exists(const fs::path& path) { |
| 531 | #ifdef ANDROID | ||
| 532 | if (Android::IsContentUri(path)) { | ||
| 533 | return Android::Exists(path); | ||
| 534 | } else { | ||
| 535 | return fs::exists(path); | ||
| 536 | } | ||
| 537 | #else | ||
| 528 | return fs::exists(path); | 538 | return fs::exists(path); |
| 539 | #endif | ||
| 529 | } | 540 | } |
| 530 | 541 | ||
| 531 | bool IsFile(const fs::path& path) { | 542 | bool IsFile(const fs::path& path) { |
| 543 | #ifdef ANDROID | ||
| 544 | if (Android::IsContentUri(path)) { | ||
| 545 | return !Android::IsDirectory(path); | ||
| 546 | } else { | ||
| 547 | return fs::is_regular_file(path); | ||
| 548 | } | ||
| 549 | #else | ||
| 532 | return fs::is_regular_file(path); | 550 | return fs::is_regular_file(path); |
| 551 | #endif | ||
| 533 | } | 552 | } |
| 534 | 553 | ||
| 535 | bool IsDir(const fs::path& path) { | 554 | bool IsDir(const fs::path& path) { |
| 555 | #ifdef ANDROID | ||
| 556 | if (Android::IsContentUri(path)) { | ||
| 557 | return Android::IsDirectory(path); | ||
| 558 | } else { | ||
| 559 | return fs::is_directory(path); | ||
| 560 | } | ||
| 561 | #else | ||
| 536 | return fs::is_directory(path); | 562 | return fs::is_directory(path); |
| 563 | #endif | ||
| 537 | } | 564 | } |
| 538 | 565 | ||
| 539 | fs::path GetCurrentDir() { | 566 | fs::path GetCurrentDir() { |
| @@ -578,6 +605,12 @@ fs::file_type GetEntryType(const fs::path& path) { | |||
| 578 | } | 605 | } |
| 579 | 606 | ||
| 580 | u64 GetSize(const fs::path& path) { | 607 | u64 GetSize(const fs::path& path) { |
| 608 | #ifdef ANDROID | ||
| 609 | if (Android::IsContentUri(path)) { | ||
| 610 | return Android::GetSize(path); | ||
| 611 | } | ||
| 612 | #endif | ||
| 613 | |||
| 581 | std::error_code ec; | 614 | std::error_code ec; |
| 582 | 615 | ||
| 583 | const auto file_size = fs::file_size(path, ec); | 616 | const auto file_size = fs::file_size(path, ec); |
diff --git a/src/common/fs/fs_android.cpp b/src/common/fs/fs_android.cpp new file mode 100644 index 000000000..298a79bac --- /dev/null +++ b/src/common/fs/fs_android.cpp | |||
| @@ -0,0 +1,98 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "common/fs/fs_android.h" | ||
| 5 | |||
| 6 | namespace Common::FS::Android { | ||
| 7 | |||
| 8 | JNIEnv* GetEnvForThread() { | ||
| 9 | thread_local static struct OwnedEnv { | ||
| 10 | OwnedEnv() { | ||
| 11 | status = g_jvm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6); | ||
| 12 | if (status == JNI_EDETACHED) | ||
| 13 | g_jvm->AttachCurrentThread(&env, nullptr); | ||
| 14 | } | ||
| 15 | |||
| 16 | ~OwnedEnv() { | ||
| 17 | if (status == JNI_EDETACHED) | ||
| 18 | g_jvm->DetachCurrentThread(); | ||
| 19 | } | ||
| 20 | |||
| 21 | int status; | ||
| 22 | JNIEnv* env = nullptr; | ||
| 23 | } owned; | ||
| 24 | return owned.env; | ||
| 25 | } | ||
| 26 | |||
| 27 | void RegisterCallbacks(JNIEnv* env, jclass clazz) { | ||
| 28 | env->GetJavaVM(&g_jvm); | ||
| 29 | native_library = clazz; | ||
| 30 | |||
| 31 | #define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature) \ | ||
| 32 | F(JMethodID, JMethodName, Signature) | ||
| 33 | #define FS(FunctionName, ReturnValue, Parameters, JMethodID, JMethodName, Signature) \ | ||
| 34 | F(JMethodID, JMethodName, Signature) | ||
| 35 | #define F(JMethodID, JMethodName, Signature) \ | ||
| 36 | JMethodID = env->GetStaticMethodID(native_library, JMethodName, Signature); | ||
| 37 | ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR) | ||
| 38 | ANDROID_STORAGE_FUNCTIONS(FS) | ||
| 39 | #undef F | ||
| 40 | #undef FS | ||
| 41 | #undef FR | ||
| 42 | } | ||
| 43 | |||
| 44 | void UnRegisterCallbacks() { | ||
| 45 | #define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature) F(JMethodID) | ||
| 46 | #define FS(FunctionName, ReturnValue, Parameters, JMethodID, JMethodName, Signature) F(JMethodID) | ||
| 47 | #define F(JMethodID) JMethodID = nullptr; | ||
| 48 | ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR) | ||
| 49 | ANDROID_STORAGE_FUNCTIONS(FS) | ||
| 50 | #undef F | ||
| 51 | #undef FS | ||
| 52 | #undef FR | ||
| 53 | } | ||
| 54 | |||
| 55 | bool IsContentUri(const std::string& path) { | ||
| 56 | constexpr std::string_view prefix = "content://"; | ||
| 57 | if (path.size() < prefix.size()) [[unlikely]] { | ||
| 58 | return false; | ||
| 59 | } | ||
| 60 | |||
| 61 | return path.find(prefix) == 0; | ||
| 62 | } | ||
| 63 | |||
| 64 | int OpenContentUri(const std::string& filepath, OpenMode openmode) { | ||
| 65 | if (open_content_uri == nullptr) | ||
| 66 | return -1; | ||
| 67 | |||
| 68 | const char* mode = ""; | ||
| 69 | switch (openmode) { | ||
| 70 | case OpenMode::Read: | ||
| 71 | mode = "r"; | ||
| 72 | break; | ||
| 73 | default: | ||
| 74 | UNIMPLEMENTED(); | ||
| 75 | return -1; | ||
| 76 | } | ||
| 77 | auto env = GetEnvForThread(); | ||
| 78 | jstring j_filepath = env->NewStringUTF(filepath.c_str()); | ||
| 79 | jstring j_mode = env->NewStringUTF(mode); | ||
| 80 | return env->CallStaticIntMethod(native_library, open_content_uri, j_filepath, j_mode); | ||
| 81 | } | ||
| 82 | |||
| 83 | #define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature) \ | ||
| 84 | F(FunctionName, ReturnValue, JMethodID, Caller) | ||
| 85 | #define F(FunctionName, ReturnValue, JMethodID, Caller) \ | ||
| 86 | ReturnValue FunctionName(const std::string& filepath) { \ | ||
| 87 | if (JMethodID == nullptr) { \ | ||
| 88 | return 0; \ | ||
| 89 | } \ | ||
| 90 | auto env = GetEnvForThread(); \ | ||
| 91 | jstring j_filepath = env->NewStringUTF(filepath.c_str()); \ | ||
| 92 | return env->Caller(native_library, JMethodID, j_filepath); \ | ||
| 93 | } | ||
| 94 | ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR) | ||
| 95 | #undef F | ||
| 96 | #undef FR | ||
| 97 | |||
| 98 | } // namespace Common::FS::Android | ||
diff --git a/src/common/fs/fs_android.h b/src/common/fs/fs_android.h new file mode 100644 index 000000000..b441c2a12 --- /dev/null +++ b/src/common/fs/fs_android.h | |||
| @@ -0,0 +1,65 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <string> | ||
| 7 | #include <vector> | ||
| 8 | #include <jni.h> | ||
| 9 | |||
| 10 | #define ANDROID_STORAGE_FUNCTIONS(V) \ | ||
| 11 | V(OpenContentUri, int, (const std::string& filepath, OpenMode openmode), open_content_uri, \ | ||
| 12 | "openContentUri", "(Ljava/lang/String;Ljava/lang/String;)I") | ||
| 13 | |||
| 14 | #define ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(V) \ | ||
| 15 | V(GetSize, std::uint64_t, get_size, CallStaticLongMethod, "getSize", "(Ljava/lang/String;)J") \ | ||
| 16 | V(IsDirectory, bool, is_directory, CallStaticBooleanMethod, "isDirectory", \ | ||
| 17 | "(Ljava/lang/String;)Z") \ | ||
| 18 | V(Exists, bool, file_exists, CallStaticBooleanMethod, "exists", "(Ljava/lang/String;)Z") | ||
| 19 | |||
| 20 | namespace Common::FS::Android { | ||
| 21 | |||
| 22 | static JavaVM* g_jvm = nullptr; | ||
| 23 | static jclass native_library = nullptr; | ||
| 24 | |||
| 25 | #define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature) F(JMethodID) | ||
| 26 | #define FS(FunctionName, ReturnValue, Parameters, JMethodID, JMethodName, Signature) F(JMethodID) | ||
| 27 | #define F(JMethodID) static jmethodID JMethodID = nullptr; | ||
| 28 | ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR) | ||
| 29 | ANDROID_STORAGE_FUNCTIONS(FS) | ||
| 30 | #undef F | ||
| 31 | #undef FS | ||
| 32 | #undef FR | ||
| 33 | |||
| 34 | enum class OpenMode { | ||
| 35 | Read, | ||
| 36 | Write, | ||
| 37 | ReadWrite, | ||
| 38 | WriteAppend, | ||
| 39 | WriteTruncate, | ||
| 40 | ReadWriteAppend, | ||
| 41 | ReadWriteTruncate, | ||
| 42 | Never | ||
| 43 | }; | ||
| 44 | |||
| 45 | void RegisterCallbacks(JNIEnv* env, jclass clazz); | ||
| 46 | |||
| 47 | void UnRegisterCallbacks(); | ||
| 48 | |||
| 49 | bool IsContentUri(const std::string& path); | ||
| 50 | |||
| 51 | #define FS(FunctionName, ReturnValue, Parameters, JMethodID, JMethodName, Signature) \ | ||
| 52 | F(FunctionName, Parameters, ReturnValue) | ||
| 53 | #define F(FunctionName, Parameters, ReturnValue) ReturnValue FunctionName Parameters; | ||
| 54 | ANDROID_STORAGE_FUNCTIONS(FS) | ||
| 55 | #undef F | ||
| 56 | #undef FS | ||
| 57 | |||
| 58 | #define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature) \ | ||
| 59 | F(FunctionName, ReturnValue) | ||
| 60 | #define F(FunctionName, ReturnValue) ReturnValue FunctionName(const std::string& filepath); | ||
| 61 | ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR) | ||
| 62 | #undef F | ||
| 63 | #undef FR | ||
| 64 | |||
| 65 | } // namespace Common::FS::Android | ||
diff --git a/src/common/fs/fs_paths.h b/src/common/fs/fs_paths.h index c77c112f1..61bac9eba 100644 --- a/src/common/fs/fs_paths.h +++ b/src/common/fs/fs_paths.h | |||
| @@ -10,6 +10,7 @@ | |||
| 10 | 10 | ||
| 11 | // Sub-directories contained within a yuzu data directory | 11 | // Sub-directories contained within a yuzu data directory |
| 12 | 12 | ||
| 13 | #define AMIIBO_DIR "amiibo" | ||
| 13 | #define CACHE_DIR "cache" | 14 | #define CACHE_DIR "cache" |
| 14 | #define CONFIG_DIR "config" | 15 | #define CONFIG_DIR "config" |
| 15 | #define DUMP_DIR "dump" | 16 | #define DUMP_DIR "dump" |
diff --git a/src/common/fs/fs_types.h b/src/common/fs/fs_types.h index 5a4090c19..900f85d24 100644 --- a/src/common/fs/fs_types.h +++ b/src/common/fs/fs_types.h | |||
| @@ -66,6 +66,6 @@ DECLARE_ENUM_FLAG_OPERATORS(DirEntryFilter); | |||
| 66 | * @returns A boolean value. | 66 | * @returns A boolean value. |
| 67 | * Return true to indicate whether the callback is successful, false otherwise. | 67 | * Return true to indicate whether the callback is successful, false otherwise. |
| 68 | */ | 68 | */ |
| 69 | using DirEntryCallable = std::function<bool(const std::filesystem::path& path)>; | 69 | using DirEntryCallable = std::function<bool(const std::filesystem::directory_entry& entry)>; |
| 70 | 70 | ||
| 71 | } // namespace Common::FS | 71 | } // namespace Common::FS |
diff --git a/src/common/fs/path_util.cpp b/src/common/fs/path_util.cpp index defa3e918..d71cfacc6 100644 --- a/src/common/fs/path_util.cpp +++ b/src/common/fs/path_util.cpp | |||
| @@ -6,6 +6,9 @@ | |||
| 6 | #include <unordered_map> | 6 | #include <unordered_map> |
| 7 | 7 | ||
| 8 | #include "common/fs/fs.h" | 8 | #include "common/fs/fs.h" |
| 9 | #ifdef ANDROID | ||
| 10 | #include "common/fs/fs_android.h" | ||
| 11 | #endif | ||
| 9 | #include "common/fs/fs_paths.h" | 12 | #include "common/fs/fs_paths.h" |
| 10 | #include "common/fs/path_util.h" | 13 | #include "common/fs/path_util.h" |
| 11 | #include "common/logging/log.h" | 14 | #include "common/logging/log.h" |
| @@ -80,9 +83,7 @@ public: | |||
| 80 | yuzu_paths.insert_or_assign(yuzu_path, new_path); | 83 | yuzu_paths.insert_or_assign(yuzu_path, new_path); |
| 81 | } | 84 | } |
| 82 | 85 | ||
| 83 | private: | 86 | void Reinitialize(fs::path yuzu_path = {}) { |
| 84 | PathManagerImpl() { | ||
| 85 | fs::path yuzu_path; | ||
| 86 | fs::path yuzu_path_cache; | 87 | fs::path yuzu_path_cache; |
| 87 | fs::path yuzu_path_config; | 88 | fs::path yuzu_path_config; |
| 88 | 89 | ||
| @@ -95,6 +96,10 @@ private: | |||
| 95 | 96 | ||
| 96 | yuzu_path_cache = yuzu_path / CACHE_DIR; | 97 | yuzu_path_cache = yuzu_path / CACHE_DIR; |
| 97 | yuzu_path_config = yuzu_path / CONFIG_DIR; | 98 | yuzu_path_config = yuzu_path / CONFIG_DIR; |
| 99 | #elif ANDROID | ||
| 100 | ASSERT(!yuzu_path.empty()); | ||
| 101 | yuzu_path_cache = yuzu_path / CACHE_DIR; | ||
| 102 | yuzu_path_config = yuzu_path / CONFIG_DIR; | ||
| 98 | #else | 103 | #else |
| 99 | yuzu_path = GetCurrentDir() / PORTABLE_DIR; | 104 | yuzu_path = GetCurrentDir() / PORTABLE_DIR; |
| 100 | 105 | ||
| @@ -109,6 +114,7 @@ private: | |||
| 109 | #endif | 114 | #endif |
| 110 | 115 | ||
| 111 | GenerateYuzuPath(YuzuPath::YuzuDir, yuzu_path); | 116 | GenerateYuzuPath(YuzuPath::YuzuDir, yuzu_path); |
| 117 | GenerateYuzuPath(YuzuPath::AmiiboDir, yuzu_path / AMIIBO_DIR); | ||
| 112 | GenerateYuzuPath(YuzuPath::CacheDir, yuzu_path_cache); | 118 | GenerateYuzuPath(YuzuPath::CacheDir, yuzu_path_cache); |
| 113 | GenerateYuzuPath(YuzuPath::ConfigDir, yuzu_path_config); | 119 | GenerateYuzuPath(YuzuPath::ConfigDir, yuzu_path_config); |
| 114 | GenerateYuzuPath(YuzuPath::DumpDir, yuzu_path / DUMP_DIR); | 120 | GenerateYuzuPath(YuzuPath::DumpDir, yuzu_path / DUMP_DIR); |
| @@ -122,6 +128,11 @@ private: | |||
| 122 | GenerateYuzuPath(YuzuPath::TASDir, yuzu_path / TAS_DIR); | 128 | GenerateYuzuPath(YuzuPath::TASDir, yuzu_path / TAS_DIR); |
| 123 | } | 129 | } |
| 124 | 130 | ||
| 131 | private: | ||
| 132 | PathManagerImpl() { | ||
| 133 | Reinitialize(); | ||
| 134 | } | ||
| 135 | |||
| 125 | ~PathManagerImpl() = default; | 136 | ~PathManagerImpl() = default; |
| 126 | 137 | ||
| 127 | void GenerateYuzuPath(YuzuPath yuzu_path, const fs::path& new_path) { | 138 | void GenerateYuzuPath(YuzuPath yuzu_path, const fs::path& new_path) { |
| @@ -210,6 +221,10 @@ fs::path RemoveTrailingSeparators(const fs::path& path) { | |||
| 210 | return fs::path{string_path}; | 221 | return fs::path{string_path}; |
| 211 | } | 222 | } |
| 212 | 223 | ||
| 224 | void SetAppDirectory(const std::string& app_directory) { | ||
| 225 | PathManagerImpl::GetInstance().Reinitialize(app_directory); | ||
| 226 | } | ||
| 227 | |||
| 213 | const fs::path& GetYuzuPath(YuzuPath yuzu_path) { | 228 | const fs::path& GetYuzuPath(YuzuPath yuzu_path) { |
| 214 | return PathManagerImpl::GetInstance().GetYuzuPathImpl(yuzu_path); | 229 | return PathManagerImpl::GetInstance().GetYuzuPathImpl(yuzu_path); |
| 215 | } | 230 | } |
| @@ -350,6 +365,12 @@ std::vector<std::string> SplitPathComponents(std::string_view filename) { | |||
| 350 | 365 | ||
| 351 | std::string SanitizePath(std::string_view path_, DirectorySeparator directory_separator) { | 366 | std::string SanitizePath(std::string_view path_, DirectorySeparator directory_separator) { |
| 352 | std::string path(path_); | 367 | std::string path(path_); |
| 368 | #ifdef ANDROID | ||
| 369 | if (Android::IsContentUri(path)) { | ||
| 370 | return path; | ||
| 371 | } | ||
| 372 | #endif // ANDROID | ||
| 373 | |||
| 353 | char type1 = directory_separator == DirectorySeparator::BackwardSlash ? '/' : '\\'; | 374 | char type1 = directory_separator == DirectorySeparator::BackwardSlash ? '/' : '\\'; |
| 354 | char type2 = directory_separator == DirectorySeparator::BackwardSlash ? '\\' : '/'; | 375 | char type2 = directory_separator == DirectorySeparator::BackwardSlash ? '\\' : '/'; |
| 355 | 376 | ||
diff --git a/src/common/fs/path_util.h b/src/common/fs/path_util.h index 13d713f1e..ba28964d0 100644 --- a/src/common/fs/path_util.h +++ b/src/common/fs/path_util.h | |||
| @@ -12,6 +12,7 @@ namespace Common::FS { | |||
| 12 | 12 | ||
| 13 | enum class YuzuPath { | 13 | enum class YuzuPath { |
| 14 | YuzuDir, // Where yuzu stores its data. | 14 | YuzuDir, // Where yuzu stores its data. |
| 15 | AmiiboDir, // Where Amiibo backups are stored. | ||
| 15 | CacheDir, // Where cached filesystem data is stored. | 16 | CacheDir, // Where cached filesystem data is stored. |
| 16 | ConfigDir, // Where config files are stored. | 17 | ConfigDir, // Where config files are stored. |
| 17 | DumpDir, // Where dumped data is stored. | 18 | DumpDir, // Where dumped data is stored. |
| @@ -181,6 +182,14 @@ template <typename Path> | |||
| 181 | #endif | 182 | #endif |
| 182 | 183 | ||
| 183 | /** | 184 | /** |
| 185 | * Sets the directory used for application storage. Used on Android where we do not know internal | ||
| 186 | * storage until informed by the frontend. | ||
| 187 | * | ||
| 188 | * @param app_directory Directory to use for application storage. | ||
| 189 | */ | ||
| 190 | void SetAppDirectory(const std::string& app_directory); | ||
| 191 | |||
| 192 | /** | ||
| 184 | * Gets the filesystem path associated with the YuzuPath enum. | 193 | * Gets the filesystem path associated with the YuzuPath enum. |
| 185 | * | 194 | * |
| 186 | * @param yuzu_path YuzuPath enum | 195 | * @param yuzu_path YuzuPath enum |
diff --git a/src/common/host_memory.cpp b/src/common/host_memory.cpp index 611c7d1a3..ba22595e0 100644 --- a/src/common/host_memory.cpp +++ b/src/common/host_memory.cpp | |||
| @@ -11,9 +11,14 @@ | |||
| 11 | 11 | ||
| 12 | #elif defined(__linux__) || defined(__FreeBSD__) // ^^^ Windows ^^^ vvv Linux vvv | 12 | #elif defined(__linux__) || defined(__FreeBSD__) // ^^^ Windows ^^^ vvv Linux vvv |
| 13 | 13 | ||
| 14 | #ifdef ANDROID | ||
| 15 | #include <android/sharedmem.h> | ||
| 16 | #endif | ||
| 17 | |||
| 14 | #ifndef _GNU_SOURCE | 18 | #ifndef _GNU_SOURCE |
| 15 | #define _GNU_SOURCE | 19 | #define _GNU_SOURCE |
| 16 | #endif | 20 | #endif |
| 21 | #include <boost/icl/interval_set.hpp> | ||
| 17 | #include <fcntl.h> | 22 | #include <fcntl.h> |
| 18 | #include <sys/mman.h> | 23 | #include <sys/mman.h> |
| 19 | #include <unistd.h> | 24 | #include <unistd.h> |
| @@ -322,7 +327,7 @@ private: | |||
| 322 | } | 327 | } |
| 323 | 328 | ||
| 324 | /// Return true when a given memory region is a "nieche" and the placeholders don't have to be | 329 | /// Return true when a given memory region is a "nieche" and the placeholders don't have to be |
| 325 | /// splitted. | 330 | /// split. |
| 326 | bool IsNiechePlaceholder(size_t virtual_offset, size_t length) const { | 331 | bool IsNiechePlaceholder(size_t virtual_offset, size_t length) const { |
| 327 | const auto it = placeholders.upper_bound({virtual_offset, virtual_offset + length}); | 332 | const auto it = placeholders.upper_bound({virtual_offset, virtual_offset + length}); |
| 328 | if (it != placeholders.end() && it->lower() == virtual_offset + length) { | 333 | if (it != placeholders.end() && it->lower() == virtual_offset + length) { |
| @@ -366,17 +371,20 @@ public: | |||
| 366 | } | 371 | } |
| 367 | 372 | ||
| 368 | // Backing memory initialization | 373 | // Backing memory initialization |
| 369 | #if defined(__FreeBSD__) && __FreeBSD__ < 13 | 374 | #ifdef ANDROID |
| 375 | fd = ASharedMemory_create("HostMemory", backing_size); | ||
| 376 | #elif defined(__FreeBSD__) && __FreeBSD__ < 13 | ||
| 370 | // XXX Drop after FreeBSD 12.* reaches EOL on 2024-06-30 | 377 | // XXX Drop after FreeBSD 12.* reaches EOL on 2024-06-30 |
| 371 | fd = shm_open(SHM_ANON, O_RDWR, 0600); | 378 | fd = shm_open(SHM_ANON, O_RDWR, 0600); |
| 372 | #else | 379 | #else |
| 373 | fd = memfd_create("HostMemory", 0); | 380 | fd = memfd_create("HostMemory", 0); |
| 374 | #endif | 381 | #endif |
| 375 | if (fd == -1) { | 382 | if (fd < 0) { |
| 376 | LOG_CRITICAL(HW_Memory, "memfd_create failed: {}", strerror(errno)); | 383 | LOG_CRITICAL(HW_Memory, "memfd_create failed: {}", strerror(errno)); |
| 377 | throw std::bad_alloc{}; | 384 | throw std::bad_alloc{}; |
| 378 | } | 385 | } |
| 379 | 386 | ||
| 387 | #ifndef ANDROID | ||
| 380 | // Defined to extend the file with zeros | 388 | // Defined to extend the file with zeros |
| 381 | int ret = ftruncate(fd, backing_size); | 389 | int ret = ftruncate(fd, backing_size); |
| 382 | if (ret != 0) { | 390 | if (ret != 0) { |
| @@ -384,6 +392,7 @@ public: | |||
| 384 | strerror(errno)); | 392 | strerror(errno)); |
| 385 | throw std::bad_alloc{}; | 393 | throw std::bad_alloc{}; |
| 386 | } | 394 | } |
| 395 | #endif | ||
| 387 | 396 | ||
| 388 | backing_base = static_cast<u8*>( | 397 | backing_base = static_cast<u8*>( |
| 389 | mmap(nullptr, backing_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)); | 398 | mmap(nullptr, backing_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)); |
| @@ -415,6 +424,7 @@ public: | |||
| 415 | madvise(virtual_base, virtual_size, MADV_HUGEPAGE); | 424 | madvise(virtual_base, virtual_size, MADV_HUGEPAGE); |
| 416 | #endif | 425 | #endif |
| 417 | 426 | ||
| 427 | placeholders.add({0, virtual_size}); | ||
| 418 | good = true; | 428 | good = true; |
| 419 | } | 429 | } |
| 420 | 430 | ||
| @@ -423,6 +433,10 @@ public: | |||
| 423 | } | 433 | } |
| 424 | 434 | ||
| 425 | void Map(size_t virtual_offset, size_t host_offset, size_t length) { | 435 | void Map(size_t virtual_offset, size_t host_offset, size_t length) { |
| 436 | { | ||
| 437 | std::scoped_lock lock{placeholder_mutex}; | ||
| 438 | placeholders.subtract({virtual_offset, virtual_offset + length}); | ||
| 439 | } | ||
| 426 | 440 | ||
| 427 | void* ret = mmap(virtual_base + virtual_offset, length, PROT_READ | PROT_WRITE, | 441 | void* ret = mmap(virtual_base + virtual_offset, length, PROT_READ | PROT_WRITE, |
| 428 | MAP_SHARED | MAP_FIXED, fd, host_offset); | 442 | MAP_SHARED | MAP_FIXED, fd, host_offset); |
| @@ -433,6 +447,19 @@ public: | |||
| 433 | // The method name is wrong. We're still talking about the virtual range. | 447 | // The method name is wrong. We're still talking about the virtual range. |
| 434 | // We don't want to unmap, we want to reserve this memory. | 448 | // We don't want to unmap, we want to reserve this memory. |
| 435 | 449 | ||
| 450 | { | ||
| 451 | std::scoped_lock lock{placeholder_mutex}; | ||
| 452 | auto it = placeholders.find({virtual_offset - 1, virtual_offset + length + 1}); | ||
| 453 | |||
| 454 | if (it != placeholders.end()) { | ||
| 455 | size_t prev_upper = virtual_offset + length; | ||
| 456 | virtual_offset = std::min(virtual_offset, it->lower()); | ||
| 457 | length = std::max(it->upper(), prev_upper) - virtual_offset; | ||
| 458 | } | ||
| 459 | |||
| 460 | placeholders.add({virtual_offset, virtual_offset + length}); | ||
| 461 | } | ||
| 462 | |||
| 436 | void* ret = mmap(virtual_base + virtual_offset, length, PROT_NONE, | 463 | void* ret = mmap(virtual_base + virtual_offset, length, PROT_NONE, |
| 437 | MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0); | 464 | MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0); |
| 438 | ASSERT_MSG(ret != MAP_FAILED, "mmap failed: {}", strerror(errno)); | 465 | ASSERT_MSG(ret != MAP_FAILED, "mmap failed: {}", strerror(errno)); |
| @@ -476,6 +503,9 @@ private: | |||
| 476 | } | 503 | } |
| 477 | 504 | ||
| 478 | int fd{-1}; // memfd file descriptor, -1 is the error value of memfd_create | 505 | int fd{-1}; // memfd file descriptor, -1 is the error value of memfd_create |
| 506 | |||
| 507 | boost::icl::interval_set<size_t> placeholders; ///< Mapped placeholders | ||
| 508 | std::mutex placeholder_mutex; ///< Mutex for placeholders | ||
| 479 | }; | 509 | }; |
| 480 | 510 | ||
| 481 | #else // ^^^ Linux ^^^ vvv Generic vvv | 511 | #else // ^^^ Linux ^^^ vvv Generic vvv |
| @@ -484,7 +514,7 @@ class HostMemory::Impl { | |||
| 484 | public: | 514 | public: |
| 485 | explicit Impl(size_t /*backing_size */, size_t /* virtual_size */) { | 515 | explicit Impl(size_t /*backing_size */, size_t /* virtual_size */) { |
| 486 | // This is just a place holder. | 516 | // This is just a place holder. |
| 487 | // Please implement fastmem in a propper way on your platform. | 517 | // Please implement fastmem in a proper way on your platform. |
| 488 | throw std::bad_alloc{}; | 518 | throw std::bad_alloc{}; |
| 489 | } | 519 | } |
| 490 | 520 | ||
diff --git a/src/common/input.h b/src/common/input.h index d27b1d772..ea30770ae 100644 --- a/src/common/input.h +++ b/src/common/input.h | |||
| @@ -15,7 +15,7 @@ | |||
| 15 | 15 | ||
| 16 | namespace Common::Input { | 16 | namespace Common::Input { |
| 17 | 17 | ||
| 18 | // Type of data that is expected to recieve or send | 18 | // Type of data that is expected to receive or send |
| 19 | enum class InputType { | 19 | enum class InputType { |
| 20 | None, | 20 | None, |
| 21 | Battery, | 21 | Battery, |
| @@ -46,11 +46,13 @@ enum class PollingMode { | |||
| 46 | // Constant polling of buttons, analogs and motion data | 46 | // Constant polling of buttons, analogs and motion data |
| 47 | Active, | 47 | Active, |
| 48 | // Only update on button change, digital analogs | 48 | // Only update on button change, digital analogs |
| 49 | Pasive, | 49 | Passive, |
| 50 | // Enable near field communication polling | 50 | // Enable near field communication polling |
| 51 | NFC, | 51 | NFC, |
| 52 | // Enable infrared camera polling | 52 | // Enable infrared camera polling |
| 53 | IR, | 53 | IR, |
| 54 | // Enable ring controller polling | ||
| 55 | Ring, | ||
| 54 | }; | 56 | }; |
| 55 | 57 | ||
| 56 | enum class CameraFormat { | 58 | enum class CameraFormat { |
| @@ -62,41 +64,35 @@ enum class CameraFormat { | |||
| 62 | None, | 64 | None, |
| 63 | }; | 65 | }; |
| 64 | 66 | ||
| 65 | // Vibration reply from the controller | 67 | // Different results that can happen from a device request |
| 66 | enum class VibrationError { | 68 | enum class DriverResult { |
| 67 | None, | 69 | Success, |
| 70 | WrongReply, | ||
| 71 | Timeout, | ||
| 72 | UnsupportedControllerType, | ||
| 73 | HandleInUse, | ||
| 74 | ErrorReadingData, | ||
| 75 | ErrorWritingData, | ||
| 76 | NoDeviceDetected, | ||
| 77 | InvalidHandle, | ||
| 68 | NotSupported, | 78 | NotSupported, |
| 69 | Disabled, | 79 | Disabled, |
| 70 | Unknown, | 80 | Unknown, |
| 71 | }; | 81 | }; |
| 72 | 82 | ||
| 73 | // Polling mode reply from the controller | ||
| 74 | enum class PollingError { | ||
| 75 | None, | ||
| 76 | NotSupported, | ||
| 77 | Unknown, | ||
| 78 | }; | ||
| 79 | |||
| 80 | // Nfc reply from the controller | 83 | // Nfc reply from the controller |
| 81 | enum class NfcState { | 84 | enum class NfcState { |
| 82 | Success, | 85 | Success, |
| 83 | NewAmiibo, | 86 | NewAmiibo, |
| 84 | WaitingForAmiibo, | 87 | WaitingForAmiibo, |
| 85 | AmiiboRemoved, | 88 | AmiiboRemoved, |
| 86 | NotAnAmiibo, | 89 | InvalidTagType, |
| 87 | NotSupported, | 90 | NotSupported, |
| 88 | WrongDeviceState, | 91 | WrongDeviceState, |
| 89 | WriteFailed, | 92 | WriteFailed, |
| 90 | Unknown, | 93 | Unknown, |
| 91 | }; | 94 | }; |
| 92 | 95 | ||
| 93 | // Ir camera reply from the controller | ||
| 94 | enum class CameraError { | ||
| 95 | None, | ||
| 96 | NotSupported, | ||
| 97 | Unknown, | ||
| 98 | }; | ||
| 99 | |||
| 100 | // Hint for amplification curve to be used | 96 | // Hint for amplification curve to be used |
| 101 | enum class VibrationAmplificationType { | 97 | enum class VibrationAmplificationType { |
| 102 | Linear, | 98 | Linear, |
| @@ -107,7 +103,7 @@ enum class VibrationAmplificationType { | |||
| 107 | struct AnalogProperties { | 103 | struct AnalogProperties { |
| 108 | // Anything below this value will be detected as zero | 104 | // Anything below this value will be detected as zero |
| 109 | float deadzone{}; | 105 | float deadzone{}; |
| 110 | // Anyting above this values will be detected as one | 106 | // Anything above this values will be detected as one |
| 111 | float range{1.0f}; | 107 | float range{1.0f}; |
| 112 | // Minimum value to be detected as active | 108 | // Minimum value to be detected as active |
| 113 | float threshold{0.5f}; | 109 | float threshold{0.5f}; |
| @@ -115,6 +111,8 @@ struct AnalogProperties { | |||
| 115 | float offset{}; | 111 | float offset{}; |
| 116 | // Invert direction of the sensor data | 112 | // Invert direction of the sensor data |
| 117 | bool inverted{}; | 113 | bool inverted{}; |
| 114 | // Invert the state if it's converted to a button | ||
| 115 | bool inverted_button{}; | ||
| 118 | // Press once to activate, press again to release | 116 | // Press once to activate, press again to release |
| 119 | bool toggle{}; | 117 | bool toggle{}; |
| 120 | }; | 118 | }; |
| @@ -134,6 +132,8 @@ struct ButtonStatus { | |||
| 134 | bool inverted{}; | 132 | bool inverted{}; |
| 135 | // Press once to activate, press again to release | 133 | // Press once to activate, press again to release |
| 136 | bool toggle{}; | 134 | bool toggle{}; |
| 135 | // Spams the button when active | ||
| 136 | bool turbo{}; | ||
| 137 | // Internal lock for the toggle status | 137 | // Internal lock for the toggle status |
| 138 | bool locked{}; | 138 | bool locked{}; |
| 139 | }; | 139 | }; |
| @@ -190,6 +190,8 @@ struct TouchStatus { | |||
| 190 | struct BodyColorStatus { | 190 | struct BodyColorStatus { |
| 191 | u32 body{}; | 191 | u32 body{}; |
| 192 | u32 buttons{}; | 192 | u32 buttons{}; |
| 193 | u32 left_grip{}; | ||
| 194 | u32 right_grip{}; | ||
| 193 | }; | 195 | }; |
| 194 | 196 | ||
| 195 | // HD rumble data | 197 | // HD rumble data |
| @@ -209,15 +211,29 @@ struct LedStatus { | |||
| 209 | bool led_4{}; | 211 | bool led_4{}; |
| 210 | }; | 212 | }; |
| 211 | 213 | ||
| 212 | // Raw data fom camera | 214 | // Raw data from camera |
| 213 | struct CameraStatus { | 215 | struct CameraStatus { |
| 214 | CameraFormat format{CameraFormat::None}; | 216 | CameraFormat format{CameraFormat::None}; |
| 215 | std::vector<u8> data{}; | 217 | std::vector<u8> data{}; |
| 216 | }; | 218 | }; |
| 217 | 219 | ||
| 218 | struct NfcStatus { | 220 | struct NfcStatus { |
| 219 | NfcState state{}; | 221 | NfcState state{NfcState::Unknown}; |
| 220 | std::vector<u8> data{}; | 222 | u8 uuid_length; |
| 223 | u8 protocol; | ||
| 224 | u8 tag_type; | ||
| 225 | std::array<u8, 10> uuid; | ||
| 226 | }; | ||
| 227 | |||
| 228 | struct MifareData { | ||
| 229 | u8 command; | ||
| 230 | u8 sector; | ||
| 231 | std::array<u8, 0x6> key; | ||
| 232 | std::array<u8, 0x10> data; | ||
| 233 | }; | ||
| 234 | |||
| 235 | struct MifareRequest { | ||
| 236 | std::array<MifareData, 0x10> data; | ||
| 221 | }; | 237 | }; |
| 222 | 238 | ||
| 223 | // List of buttons to be passed to Qt that can be translated | 239 | // List of buttons to be passed to Qt that can be translated |
| @@ -228,17 +244,31 @@ enum class ButtonNames { | |||
| 228 | Engine, | 244 | Engine, |
| 229 | // This will display the button by value instead of the button name | 245 | // This will display the button by value instead of the button name |
| 230 | Value, | 246 | Value, |
| 247 | |||
| 248 | // Joycon button names | ||
| 231 | ButtonLeft, | 249 | ButtonLeft, |
| 232 | ButtonRight, | 250 | ButtonRight, |
| 233 | ButtonDown, | 251 | ButtonDown, |
| 234 | ButtonUp, | 252 | ButtonUp, |
| 235 | TriggerZ, | ||
| 236 | TriggerR, | ||
| 237 | TriggerL, | ||
| 238 | ButtonA, | 253 | ButtonA, |
| 239 | ButtonB, | 254 | ButtonB, |
| 240 | ButtonX, | 255 | ButtonX, |
| 241 | ButtonY, | 256 | ButtonY, |
| 257 | ButtonPlus, | ||
| 258 | ButtonMinus, | ||
| 259 | ButtonHome, | ||
| 260 | ButtonCapture, | ||
| 261 | ButtonStickL, | ||
| 262 | ButtonStickR, | ||
| 263 | TriggerL, | ||
| 264 | TriggerZL, | ||
| 265 | TriggerSL, | ||
| 266 | TriggerR, | ||
| 267 | TriggerZR, | ||
| 268 | TriggerSR, | ||
| 269 | |||
| 270 | // GC button names | ||
| 271 | TriggerZ, | ||
| 242 | ButtonStart, | 272 | ButtonStart, |
| 243 | 273 | ||
| 244 | // DS4 button names | 274 | // DS4 button names |
| @@ -278,7 +308,7 @@ struct CallbackStatus { | |||
| 278 | BatteryStatus battery_status{}; | 308 | BatteryStatus battery_status{}; |
| 279 | VibrationStatus vibration_status{}; | 309 | VibrationStatus vibration_status{}; |
| 280 | CameraFormat camera_status{CameraFormat::None}; | 310 | CameraFormat camera_status{CameraFormat::None}; |
| 281 | NfcState nfc_status{NfcState::Unknown}; | 311 | NfcStatus nfc_status{}; |
| 282 | std::vector<u8> raw_data{}; | 312 | std::vector<u8> raw_data{}; |
| 283 | }; | 313 | }; |
| 284 | 314 | ||
| @@ -316,31 +346,54 @@ class OutputDevice { | |||
| 316 | public: | 346 | public: |
| 317 | virtual ~OutputDevice() = default; | 347 | virtual ~OutputDevice() = default; |
| 318 | 348 | ||
| 319 | virtual void SetLED([[maybe_unused]] const LedStatus& led_status) {} | 349 | virtual DriverResult SetLED([[maybe_unused]] const LedStatus& led_status) { |
| 350 | return DriverResult::NotSupported; | ||
| 351 | } | ||
| 320 | 352 | ||
| 321 | virtual VibrationError SetVibration([[maybe_unused]] const VibrationStatus& vibration_status) { | 353 | virtual DriverResult SetVibration([[maybe_unused]] const VibrationStatus& vibration_status) { |
| 322 | return VibrationError::NotSupported; | 354 | return DriverResult::NotSupported; |
| 323 | } | 355 | } |
| 324 | 356 | ||
| 325 | virtual bool IsVibrationEnabled() { | 357 | virtual bool IsVibrationEnabled() { |
| 326 | return false; | 358 | return false; |
| 327 | } | 359 | } |
| 328 | 360 | ||
| 329 | virtual PollingError SetPollingMode([[maybe_unused]] PollingMode polling_mode) { | 361 | virtual DriverResult SetPollingMode([[maybe_unused]] PollingMode polling_mode) { |
| 330 | return PollingError::NotSupported; | 362 | return DriverResult::NotSupported; |
| 331 | } | 363 | } |
| 332 | 364 | ||
| 333 | virtual CameraError SetCameraFormat([[maybe_unused]] CameraFormat camera_format) { | 365 | virtual DriverResult SetCameraFormat([[maybe_unused]] CameraFormat camera_format) { |
| 334 | return CameraError::NotSupported; | 366 | return DriverResult::NotSupported; |
| 335 | } | 367 | } |
| 336 | 368 | ||
| 337 | virtual NfcState SupportsNfc() const { | 369 | virtual NfcState SupportsNfc() const { |
| 338 | return NfcState::NotSupported; | 370 | return NfcState::NotSupported; |
| 339 | } | 371 | } |
| 340 | 372 | ||
| 373 | virtual NfcState StartNfcPolling() { | ||
| 374 | return NfcState::NotSupported; | ||
| 375 | } | ||
| 376 | |||
| 377 | virtual NfcState StopNfcPolling() { | ||
| 378 | return NfcState::NotSupported; | ||
| 379 | } | ||
| 380 | |||
| 381 | virtual NfcState ReadAmiiboData([[maybe_unused]] std::vector<u8>& out_data) { | ||
| 382 | return NfcState::NotSupported; | ||
| 383 | } | ||
| 384 | |||
| 341 | virtual NfcState WriteNfcData([[maybe_unused]] const std::vector<u8>& data) { | 385 | virtual NfcState WriteNfcData([[maybe_unused]] const std::vector<u8>& data) { |
| 342 | return NfcState::NotSupported; | 386 | return NfcState::NotSupported; |
| 343 | } | 387 | } |
| 388 | |||
| 389 | virtual NfcState ReadMifareData([[maybe_unused]] const MifareRequest& request, | ||
| 390 | [[maybe_unused]] MifareRequest& out_data) { | ||
| 391 | return NfcState::NotSupported; | ||
| 392 | } | ||
| 393 | |||
| 394 | virtual NfcState WriteMifareData([[maybe_unused]] const MifareRequest& request) { | ||
| 395 | return NfcState::NotSupported; | ||
| 396 | } | ||
| 344 | }; | 397 | }; |
| 345 | 398 | ||
| 346 | /// An abstract class template for a factory that can create input devices. | 399 | /// An abstract class template for a factory that can create input devices. |
| @@ -412,7 +465,7 @@ inline void UnregisterOutputFactory(const std::string& name) { | |||
| 412 | } | 465 | } |
| 413 | 466 | ||
| 414 | /** | 467 | /** |
| 415 | * Create an input device from given paramters. | 468 | * Create an input device from given parameters. |
| 416 | * @tparam InputDeviceType the type of input devices to create | 469 | * @tparam InputDeviceType the type of input devices to create |
| 417 | * @param params a serialized ParamPackage string that contains all parameters for creating the | 470 | * @param params a serialized ParamPackage string that contains all parameters for creating the |
| 418 | * device | 471 | * device |
diff --git a/src/common/intrusive_list.h b/src/common/intrusive_list.h new file mode 100644 index 000000000..d330dc1c2 --- /dev/null +++ b/src/common/intrusive_list.h | |||
| @@ -0,0 +1,631 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "common/common_funcs.h" | ||
| 7 | #include "common/parent_of_member.h" | ||
| 8 | |||
| 9 | namespace Common { | ||
| 10 | |||
| 11 | // Forward declare implementation class for Node. | ||
| 12 | namespace impl { | ||
| 13 | |||
| 14 | class IntrusiveListImpl; | ||
| 15 | |||
| 16 | } | ||
| 17 | |||
| 18 | class IntrusiveListNode { | ||
| 19 | YUZU_NON_COPYABLE(IntrusiveListNode); | ||
| 20 | |||
| 21 | private: | ||
| 22 | friend class impl::IntrusiveListImpl; | ||
| 23 | |||
| 24 | IntrusiveListNode* m_prev; | ||
| 25 | IntrusiveListNode* m_next; | ||
| 26 | |||
| 27 | public: | ||
| 28 | constexpr IntrusiveListNode() : m_prev(this), m_next(this) {} | ||
| 29 | |||
| 30 | constexpr bool IsLinked() const { | ||
| 31 | return m_next != this; | ||
| 32 | } | ||
| 33 | |||
| 34 | private: | ||
| 35 | constexpr void LinkPrev(IntrusiveListNode* node) { | ||
| 36 | // We can't link an already linked node. | ||
| 37 | ASSERT(!node->IsLinked()); | ||
| 38 | this->SplicePrev(node, node); | ||
| 39 | } | ||
| 40 | |||
| 41 | constexpr void SplicePrev(IntrusiveListNode* first, IntrusiveListNode* last) { | ||
| 42 | // Splice a range into the list. | ||
| 43 | auto last_prev = last->m_prev; | ||
| 44 | first->m_prev = m_prev; | ||
| 45 | last_prev->m_next = this; | ||
| 46 | m_prev->m_next = first; | ||
| 47 | m_prev = last_prev; | ||
| 48 | } | ||
| 49 | |||
| 50 | constexpr void LinkNext(IntrusiveListNode* node) { | ||
| 51 | // We can't link an already linked node. | ||
| 52 | ASSERT(!node->IsLinked()); | ||
| 53 | return this->SpliceNext(node, node); | ||
| 54 | } | ||
| 55 | |||
| 56 | constexpr void SpliceNext(IntrusiveListNode* first, IntrusiveListNode* last) { | ||
| 57 | // Splice a range into the list. | ||
| 58 | auto last_prev = last->m_prev; | ||
| 59 | first->m_prev = this; | ||
| 60 | last_prev->m_next = m_next; | ||
| 61 | m_next->m_prev = last_prev; | ||
| 62 | m_next = first; | ||
| 63 | } | ||
| 64 | |||
| 65 | constexpr void Unlink() { | ||
| 66 | this->Unlink(m_next); | ||
| 67 | } | ||
| 68 | |||
| 69 | constexpr void Unlink(IntrusiveListNode* last) { | ||
| 70 | // Unlink a node from a next node. | ||
| 71 | auto last_prev = last->m_prev; | ||
| 72 | m_prev->m_next = last; | ||
| 73 | last->m_prev = m_prev; | ||
| 74 | last_prev->m_next = this; | ||
| 75 | m_prev = last_prev; | ||
| 76 | } | ||
| 77 | |||
| 78 | constexpr IntrusiveListNode* GetPrev() { | ||
| 79 | return m_prev; | ||
| 80 | } | ||
| 81 | |||
| 82 | constexpr const IntrusiveListNode* GetPrev() const { | ||
| 83 | return m_prev; | ||
| 84 | } | ||
| 85 | |||
| 86 | constexpr IntrusiveListNode* GetNext() { | ||
| 87 | return m_next; | ||
| 88 | } | ||
| 89 | |||
| 90 | constexpr const IntrusiveListNode* GetNext() const { | ||
| 91 | return m_next; | ||
| 92 | } | ||
| 93 | }; | ||
| 94 | // DEPRECATED: static_assert(std::is_literal_type<IntrusiveListNode>::value); | ||
| 95 | |||
| 96 | namespace impl { | ||
| 97 | |||
| 98 | class IntrusiveListImpl { | ||
| 99 | YUZU_NON_COPYABLE(IntrusiveListImpl); | ||
| 100 | |||
| 101 | private: | ||
| 102 | IntrusiveListNode m_root_node; | ||
| 103 | |||
| 104 | public: | ||
| 105 | template <bool Const> | ||
| 106 | class Iterator; | ||
| 107 | |||
| 108 | using value_type = IntrusiveListNode; | ||
| 109 | using size_type = size_t; | ||
| 110 | using difference_type = ptrdiff_t; | ||
| 111 | using pointer = value_type*; | ||
| 112 | using const_pointer = const value_type*; | ||
| 113 | using reference = value_type&; | ||
| 114 | using const_reference = const value_type&; | ||
| 115 | using iterator = Iterator<false>; | ||
| 116 | using const_iterator = Iterator<true>; | ||
| 117 | using reverse_iterator = std::reverse_iterator<iterator>; | ||
| 118 | using const_reverse_iterator = std::reverse_iterator<const_iterator>; | ||
| 119 | |||
| 120 | template <bool Const> | ||
| 121 | class Iterator { | ||
| 122 | public: | ||
| 123 | using iterator_category = std::bidirectional_iterator_tag; | ||
| 124 | using value_type = typename IntrusiveListImpl::value_type; | ||
| 125 | using difference_type = typename IntrusiveListImpl::difference_type; | ||
| 126 | using pointer = | ||
| 127 | std::conditional_t<Const, IntrusiveListImpl::const_pointer, IntrusiveListImpl::pointer>; | ||
| 128 | using reference = std::conditional_t<Const, IntrusiveListImpl::const_reference, | ||
| 129 | IntrusiveListImpl::reference>; | ||
| 130 | |||
| 131 | private: | ||
| 132 | pointer m_node; | ||
| 133 | |||
| 134 | public: | ||
| 135 | constexpr explicit Iterator(pointer n) : m_node(n) {} | ||
| 136 | |||
| 137 | constexpr bool operator==(const Iterator& rhs) const { | ||
| 138 | return m_node == rhs.m_node; | ||
| 139 | } | ||
| 140 | |||
| 141 | constexpr pointer operator->() const { | ||
| 142 | return m_node; | ||
| 143 | } | ||
| 144 | |||
| 145 | constexpr reference operator*() const { | ||
| 146 | return *m_node; | ||
| 147 | } | ||
| 148 | |||
| 149 | constexpr Iterator& operator++() { | ||
| 150 | m_node = m_node->m_next; | ||
| 151 | return *this; | ||
| 152 | } | ||
| 153 | |||
| 154 | constexpr Iterator& operator--() { | ||
| 155 | m_node = m_node->m_prev; | ||
| 156 | return *this; | ||
| 157 | } | ||
| 158 | |||
| 159 | constexpr Iterator operator++(int) { | ||
| 160 | const Iterator it{*this}; | ||
| 161 | ++(*this); | ||
| 162 | return it; | ||
| 163 | } | ||
| 164 | |||
| 165 | constexpr Iterator operator--(int) { | ||
| 166 | const Iterator it{*this}; | ||
| 167 | --(*this); | ||
| 168 | return it; | ||
| 169 | } | ||
| 170 | |||
| 171 | constexpr operator Iterator<true>() const { | ||
| 172 | return Iterator<true>(m_node); | ||
| 173 | } | ||
| 174 | |||
| 175 | constexpr Iterator<false> GetNonConstIterator() const { | ||
| 176 | return Iterator<false>(const_cast<IntrusiveListImpl::pointer>(m_node)); | ||
| 177 | } | ||
| 178 | }; | ||
| 179 | |||
| 180 | public: | ||
| 181 | constexpr IntrusiveListImpl() : m_root_node() {} | ||
| 182 | |||
| 183 | // Iterator accessors. | ||
| 184 | constexpr iterator begin() { | ||
| 185 | return iterator(m_root_node.GetNext()); | ||
| 186 | } | ||
| 187 | |||
| 188 | constexpr const_iterator begin() const { | ||
| 189 | return const_iterator(m_root_node.GetNext()); | ||
| 190 | } | ||
| 191 | |||
| 192 | constexpr iterator end() { | ||
| 193 | return iterator(std::addressof(m_root_node)); | ||
| 194 | } | ||
| 195 | |||
| 196 | constexpr const_iterator end() const { | ||
| 197 | return const_iterator(std::addressof(m_root_node)); | ||
| 198 | } | ||
| 199 | |||
| 200 | constexpr iterator iterator_to(reference v) { | ||
| 201 | // Only allow iterator_to for values in lists. | ||
| 202 | ASSERT(v.IsLinked()); | ||
| 203 | return iterator(std::addressof(v)); | ||
| 204 | } | ||
| 205 | |||
| 206 | constexpr const_iterator iterator_to(const_reference v) const { | ||
| 207 | // Only allow iterator_to for values in lists. | ||
| 208 | ASSERT(v.IsLinked()); | ||
| 209 | return const_iterator(std::addressof(v)); | ||
| 210 | } | ||
| 211 | |||
| 212 | // Content management. | ||
| 213 | constexpr bool empty() const { | ||
| 214 | return !m_root_node.IsLinked(); | ||
| 215 | } | ||
| 216 | |||
| 217 | constexpr size_type size() const { | ||
| 218 | return static_cast<size_type>(std::distance(this->begin(), this->end())); | ||
| 219 | } | ||
| 220 | |||
| 221 | constexpr reference back() { | ||
| 222 | return *m_root_node.GetPrev(); | ||
| 223 | } | ||
| 224 | |||
| 225 | constexpr const_reference back() const { | ||
| 226 | return *m_root_node.GetPrev(); | ||
| 227 | } | ||
| 228 | |||
| 229 | constexpr reference front() { | ||
| 230 | return *m_root_node.GetNext(); | ||
| 231 | } | ||
| 232 | |||
| 233 | constexpr const_reference front() const { | ||
| 234 | return *m_root_node.GetNext(); | ||
| 235 | } | ||
| 236 | |||
| 237 | constexpr void push_back(reference node) { | ||
| 238 | m_root_node.LinkPrev(std::addressof(node)); | ||
| 239 | } | ||
| 240 | |||
| 241 | constexpr void push_front(reference node) { | ||
| 242 | m_root_node.LinkNext(std::addressof(node)); | ||
| 243 | } | ||
| 244 | |||
| 245 | constexpr void pop_back() { | ||
| 246 | m_root_node.GetPrev()->Unlink(); | ||
| 247 | } | ||
| 248 | |||
| 249 | constexpr void pop_front() { | ||
| 250 | m_root_node.GetNext()->Unlink(); | ||
| 251 | } | ||
| 252 | |||
| 253 | constexpr iterator insert(const_iterator pos, reference node) { | ||
| 254 | pos.GetNonConstIterator()->LinkPrev(std::addressof(node)); | ||
| 255 | return iterator(std::addressof(node)); | ||
| 256 | } | ||
| 257 | |||
| 258 | constexpr void splice(const_iterator pos, IntrusiveListImpl& o) { | ||
| 259 | splice_impl(pos, o.begin(), o.end()); | ||
| 260 | } | ||
| 261 | |||
| 262 | constexpr void splice(const_iterator pos, IntrusiveListImpl& o, const_iterator first) { | ||
| 263 | const_iterator last(first); | ||
| 264 | std::advance(last, 1); | ||
| 265 | splice_impl(pos, first, last); | ||
| 266 | } | ||
| 267 | |||
| 268 | constexpr void splice(const_iterator pos, IntrusiveListImpl& o, const_iterator first, | ||
| 269 | const_iterator last) { | ||
| 270 | splice_impl(pos, first, last); | ||
| 271 | } | ||
| 272 | |||
| 273 | constexpr iterator erase(const_iterator pos) { | ||
| 274 | if (pos == this->end()) { | ||
| 275 | return this->end(); | ||
| 276 | } | ||
| 277 | iterator it(pos.GetNonConstIterator()); | ||
| 278 | (it++)->Unlink(); | ||
| 279 | return it; | ||
| 280 | } | ||
| 281 | |||
| 282 | constexpr void clear() { | ||
| 283 | while (!this->empty()) { | ||
| 284 | this->pop_front(); | ||
| 285 | } | ||
| 286 | } | ||
| 287 | |||
| 288 | private: | ||
| 289 | constexpr void splice_impl(const_iterator _pos, const_iterator _first, const_iterator _last) { | ||
| 290 | if (_first == _last) { | ||
| 291 | return; | ||
| 292 | } | ||
| 293 | iterator pos(_pos.GetNonConstIterator()); | ||
| 294 | iterator first(_first.GetNonConstIterator()); | ||
| 295 | iterator last(_last.GetNonConstIterator()); | ||
| 296 | first->Unlink(std::addressof(*last)); | ||
| 297 | pos->SplicePrev(std::addressof(*first), std::addressof(*first)); | ||
| 298 | } | ||
| 299 | }; | ||
| 300 | |||
| 301 | } // namespace impl | ||
| 302 | |||
| 303 | template <class T, class Traits> | ||
| 304 | class IntrusiveList { | ||
| 305 | YUZU_NON_COPYABLE(IntrusiveList); | ||
| 306 | |||
| 307 | private: | ||
| 308 | impl::IntrusiveListImpl m_impl; | ||
| 309 | |||
| 310 | public: | ||
| 311 | template <bool Const> | ||
| 312 | class Iterator; | ||
| 313 | |||
| 314 | using value_type = T; | ||
| 315 | using size_type = size_t; | ||
| 316 | using difference_type = ptrdiff_t; | ||
| 317 | using pointer = value_type*; | ||
| 318 | using const_pointer = const value_type*; | ||
| 319 | using reference = value_type&; | ||
| 320 | using const_reference = const value_type&; | ||
| 321 | using iterator = Iterator<false>; | ||
| 322 | using const_iterator = Iterator<true>; | ||
| 323 | using reverse_iterator = std::reverse_iterator<iterator>; | ||
| 324 | using const_reverse_iterator = std::reverse_iterator<const_iterator>; | ||
| 325 | |||
| 326 | template <bool Const> | ||
| 327 | class Iterator { | ||
| 328 | public: | ||
| 329 | friend class Common::IntrusiveList<T, Traits>; | ||
| 330 | |||
| 331 | using ImplIterator = | ||
| 332 | std::conditional_t<Const, Common::impl::IntrusiveListImpl::const_iterator, | ||
| 333 | Common::impl::IntrusiveListImpl::iterator>; | ||
| 334 | |||
| 335 | using iterator_category = std::bidirectional_iterator_tag; | ||
| 336 | using value_type = typename IntrusiveList::value_type; | ||
| 337 | using difference_type = typename IntrusiveList::difference_type; | ||
| 338 | using pointer = | ||
| 339 | std::conditional_t<Const, IntrusiveList::const_pointer, IntrusiveList::pointer>; | ||
| 340 | using reference = | ||
| 341 | std::conditional_t<Const, IntrusiveList::const_reference, IntrusiveList::reference>; | ||
| 342 | |||
| 343 | private: | ||
| 344 | ImplIterator m_iterator; | ||
| 345 | |||
| 346 | private: | ||
| 347 | constexpr explicit Iterator(ImplIterator it) : m_iterator(it) {} | ||
| 348 | |||
| 349 | constexpr ImplIterator GetImplIterator() const { | ||
| 350 | return m_iterator; | ||
| 351 | } | ||
| 352 | |||
| 353 | public: | ||
| 354 | constexpr bool operator==(const Iterator& rhs) const { | ||
| 355 | return m_iterator == rhs.m_iterator; | ||
| 356 | } | ||
| 357 | |||
| 358 | constexpr pointer operator->() const { | ||
| 359 | return std::addressof(Traits::GetParent(*m_iterator)); | ||
| 360 | } | ||
| 361 | |||
| 362 | constexpr reference operator*() const { | ||
| 363 | return Traits::GetParent(*m_iterator); | ||
| 364 | } | ||
| 365 | |||
| 366 | constexpr Iterator& operator++() { | ||
| 367 | ++m_iterator; | ||
| 368 | return *this; | ||
| 369 | } | ||
| 370 | |||
| 371 | constexpr Iterator& operator--() { | ||
| 372 | --m_iterator; | ||
| 373 | return *this; | ||
| 374 | } | ||
| 375 | |||
| 376 | constexpr Iterator operator++(int) { | ||
| 377 | const Iterator it{*this}; | ||
| 378 | ++m_iterator; | ||
| 379 | return it; | ||
| 380 | } | ||
| 381 | |||
| 382 | constexpr Iterator operator--(int) { | ||
| 383 | const Iterator it{*this}; | ||
| 384 | --m_iterator; | ||
| 385 | return it; | ||
| 386 | } | ||
| 387 | |||
| 388 | constexpr operator Iterator<true>() const { | ||
| 389 | return Iterator<true>(m_iterator); | ||
| 390 | } | ||
| 391 | }; | ||
| 392 | |||
| 393 | private: | ||
| 394 | static constexpr IntrusiveListNode& GetNode(reference ref) { | ||
| 395 | return Traits::GetNode(ref); | ||
| 396 | } | ||
| 397 | |||
| 398 | static constexpr IntrusiveListNode const& GetNode(const_reference ref) { | ||
| 399 | return Traits::GetNode(ref); | ||
| 400 | } | ||
| 401 | |||
| 402 | static constexpr reference GetParent(IntrusiveListNode& node) { | ||
| 403 | return Traits::GetParent(node); | ||
| 404 | } | ||
| 405 | |||
| 406 | static constexpr const_reference GetParent(IntrusiveListNode const& node) { | ||
| 407 | return Traits::GetParent(node); | ||
| 408 | } | ||
| 409 | |||
| 410 | public: | ||
| 411 | constexpr IntrusiveList() : m_impl() {} | ||
| 412 | |||
| 413 | // Iterator accessors. | ||
| 414 | constexpr iterator begin() { | ||
| 415 | return iterator(m_impl.begin()); | ||
| 416 | } | ||
| 417 | |||
| 418 | constexpr const_iterator begin() const { | ||
| 419 | return const_iterator(m_impl.begin()); | ||
| 420 | } | ||
| 421 | |||
| 422 | constexpr iterator end() { | ||
| 423 | return iterator(m_impl.end()); | ||
| 424 | } | ||
| 425 | |||
| 426 | constexpr const_iterator end() const { | ||
| 427 | return const_iterator(m_impl.end()); | ||
| 428 | } | ||
| 429 | |||
| 430 | constexpr const_iterator cbegin() const { | ||
| 431 | return this->begin(); | ||
| 432 | } | ||
| 433 | |||
| 434 | constexpr const_iterator cend() const { | ||
| 435 | return this->end(); | ||
| 436 | } | ||
| 437 | |||
| 438 | constexpr reverse_iterator rbegin() { | ||
| 439 | return reverse_iterator(this->end()); | ||
| 440 | } | ||
| 441 | |||
| 442 | constexpr const_reverse_iterator rbegin() const { | ||
| 443 | return const_reverse_iterator(this->end()); | ||
| 444 | } | ||
| 445 | |||
| 446 | constexpr reverse_iterator rend() { | ||
| 447 | return reverse_iterator(this->begin()); | ||
| 448 | } | ||
| 449 | |||
| 450 | constexpr const_reverse_iterator rend() const { | ||
| 451 | return const_reverse_iterator(this->begin()); | ||
| 452 | } | ||
| 453 | |||
| 454 | constexpr const_reverse_iterator crbegin() const { | ||
| 455 | return this->rbegin(); | ||
| 456 | } | ||
| 457 | |||
| 458 | constexpr const_reverse_iterator crend() const { | ||
| 459 | return this->rend(); | ||
| 460 | } | ||
| 461 | |||
| 462 | constexpr iterator iterator_to(reference v) { | ||
| 463 | return iterator(m_impl.iterator_to(GetNode(v))); | ||
| 464 | } | ||
| 465 | |||
| 466 | constexpr const_iterator iterator_to(const_reference v) const { | ||
| 467 | return const_iterator(m_impl.iterator_to(GetNode(v))); | ||
| 468 | } | ||
| 469 | |||
| 470 | // Content management. | ||
| 471 | constexpr bool empty() const { | ||
| 472 | return m_impl.empty(); | ||
| 473 | } | ||
| 474 | |||
| 475 | constexpr size_type size() const { | ||
| 476 | return m_impl.size(); | ||
| 477 | } | ||
| 478 | |||
| 479 | constexpr reference back() { | ||
| 480 | return GetParent(m_impl.back()); | ||
| 481 | } | ||
| 482 | |||
| 483 | constexpr const_reference back() const { | ||
| 484 | return GetParent(m_impl.back()); | ||
| 485 | } | ||
| 486 | |||
| 487 | constexpr reference front() { | ||
| 488 | return GetParent(m_impl.front()); | ||
| 489 | } | ||
| 490 | |||
| 491 | constexpr const_reference front() const { | ||
| 492 | return GetParent(m_impl.front()); | ||
| 493 | } | ||
| 494 | |||
| 495 | constexpr void push_back(reference ref) { | ||
| 496 | m_impl.push_back(GetNode(ref)); | ||
| 497 | } | ||
| 498 | |||
| 499 | constexpr void push_front(reference ref) { | ||
| 500 | m_impl.push_front(GetNode(ref)); | ||
| 501 | } | ||
| 502 | |||
| 503 | constexpr void pop_back() { | ||
| 504 | m_impl.pop_back(); | ||
| 505 | } | ||
| 506 | |||
| 507 | constexpr void pop_front() { | ||
| 508 | m_impl.pop_front(); | ||
| 509 | } | ||
| 510 | |||
| 511 | constexpr iterator insert(const_iterator pos, reference ref) { | ||
| 512 | return iterator(m_impl.insert(pos.GetImplIterator(), GetNode(ref))); | ||
| 513 | } | ||
| 514 | |||
| 515 | constexpr void splice(const_iterator pos, IntrusiveList& o) { | ||
| 516 | m_impl.splice(pos.GetImplIterator(), o.m_impl); | ||
| 517 | } | ||
| 518 | |||
| 519 | constexpr void splice(const_iterator pos, IntrusiveList& o, const_iterator first) { | ||
| 520 | m_impl.splice(pos.GetImplIterator(), o.m_impl, first.GetImplIterator()); | ||
| 521 | } | ||
| 522 | |||
| 523 | constexpr void splice(const_iterator pos, IntrusiveList& o, const_iterator first, | ||
| 524 | const_iterator last) { | ||
| 525 | m_impl.splice(pos.GetImplIterator(), o.m_impl, first.GetImplIterator(), | ||
| 526 | last.GetImplIterator()); | ||
| 527 | } | ||
| 528 | |||
| 529 | constexpr iterator erase(const_iterator pos) { | ||
| 530 | return iterator(m_impl.erase(pos.GetImplIterator())); | ||
| 531 | } | ||
| 532 | |||
| 533 | constexpr void clear() { | ||
| 534 | m_impl.clear(); | ||
| 535 | } | ||
| 536 | }; | ||
| 537 | |||
| 538 | template <auto T, class Derived = Common::impl::GetParentType<T>> | ||
| 539 | class IntrusiveListMemberTraits; | ||
| 540 | |||
| 541 | template <class Parent, IntrusiveListNode Parent::*Member, class Derived> | ||
| 542 | class IntrusiveListMemberTraits<Member, Derived> { | ||
| 543 | public: | ||
| 544 | using ListType = IntrusiveList<Derived, IntrusiveListMemberTraits>; | ||
| 545 | |||
| 546 | private: | ||
| 547 | friend class IntrusiveList<Derived, IntrusiveListMemberTraits>; | ||
| 548 | |||
| 549 | static constexpr IntrusiveListNode& GetNode(Derived& parent) { | ||
| 550 | return parent.*Member; | ||
| 551 | } | ||
| 552 | |||
| 553 | static constexpr IntrusiveListNode const& GetNode(Derived const& parent) { | ||
| 554 | return parent.*Member; | ||
| 555 | } | ||
| 556 | |||
| 557 | static Derived& GetParent(IntrusiveListNode& node) { | ||
| 558 | return Common::GetParentReference<Member, Derived>(std::addressof(node)); | ||
| 559 | } | ||
| 560 | |||
| 561 | static Derived const& GetParent(IntrusiveListNode const& node) { | ||
| 562 | return Common::GetParentReference<Member, Derived>(std::addressof(node)); | ||
| 563 | } | ||
| 564 | }; | ||
| 565 | |||
| 566 | template <auto T, class Derived = Common::impl::GetParentType<T>> | ||
| 567 | class IntrusiveListMemberTraitsByNonConstexprOffsetOf; | ||
| 568 | |||
| 569 | template <class Parent, IntrusiveListNode Parent::*Member, class Derived> | ||
| 570 | class IntrusiveListMemberTraitsByNonConstexprOffsetOf<Member, Derived> { | ||
| 571 | public: | ||
| 572 | using ListType = IntrusiveList<Derived, IntrusiveListMemberTraitsByNonConstexprOffsetOf>; | ||
| 573 | |||
| 574 | private: | ||
| 575 | friend class IntrusiveList<Derived, IntrusiveListMemberTraitsByNonConstexprOffsetOf>; | ||
| 576 | |||
| 577 | static constexpr IntrusiveListNode& GetNode(Derived& parent) { | ||
| 578 | return parent.*Member; | ||
| 579 | } | ||
| 580 | |||
| 581 | static constexpr IntrusiveListNode const& GetNode(Derived const& parent) { | ||
| 582 | return parent.*Member; | ||
| 583 | } | ||
| 584 | |||
| 585 | static Derived& GetParent(IntrusiveListNode& node) { | ||
| 586 | return *reinterpret_cast<Derived*>(reinterpret_cast<char*>(std::addressof(node)) - | ||
| 587 | GetOffset()); | ||
| 588 | } | ||
| 589 | |||
| 590 | static Derived const& GetParent(IntrusiveListNode const& node) { | ||
| 591 | return *reinterpret_cast<const Derived*>( | ||
| 592 | reinterpret_cast<const char*>(std::addressof(node)) - GetOffset()); | ||
| 593 | } | ||
| 594 | |||
| 595 | static uintptr_t GetOffset() { | ||
| 596 | return reinterpret_cast<uintptr_t>(std::addressof(reinterpret_cast<Derived*>(0)->*Member)); | ||
| 597 | } | ||
| 598 | }; | ||
| 599 | |||
| 600 | template <class Derived> | ||
| 601 | class IntrusiveListBaseNode : public IntrusiveListNode {}; | ||
| 602 | |||
| 603 | template <class Derived> | ||
| 604 | class IntrusiveListBaseTraits { | ||
| 605 | public: | ||
| 606 | using ListType = IntrusiveList<Derived, IntrusiveListBaseTraits>; | ||
| 607 | |||
| 608 | private: | ||
| 609 | friend class IntrusiveList<Derived, IntrusiveListBaseTraits>; | ||
| 610 | |||
| 611 | static constexpr IntrusiveListNode& GetNode(Derived& parent) { | ||
| 612 | return static_cast<IntrusiveListNode&>( | ||
| 613 | static_cast<IntrusiveListBaseNode<Derived>&>(parent)); | ||
| 614 | } | ||
| 615 | |||
| 616 | static constexpr IntrusiveListNode const& GetNode(Derived const& parent) { | ||
| 617 | return static_cast<const IntrusiveListNode&>( | ||
| 618 | static_cast<const IntrusiveListBaseNode<Derived>&>(parent)); | ||
| 619 | } | ||
| 620 | |||
| 621 | static constexpr Derived& GetParent(IntrusiveListNode& node) { | ||
| 622 | return static_cast<Derived&>(static_cast<IntrusiveListBaseNode<Derived>&>(node)); | ||
| 623 | } | ||
| 624 | |||
| 625 | static constexpr Derived const& GetParent(IntrusiveListNode const& node) { | ||
| 626 | return static_cast<const Derived&>( | ||
| 627 | static_cast<const IntrusiveListBaseNode<Derived>&>(node)); | ||
| 628 | } | ||
| 629 | }; | ||
| 630 | |||
| 631 | } // namespace Common | ||
diff --git a/src/common/intrusive_red_black_tree.h b/src/common/intrusive_red_black_tree.h index 93046615e..bc2940fa0 100644 --- a/src/common/intrusive_red_black_tree.h +++ b/src/common/intrusive_red_black_tree.h | |||
| @@ -96,10 +96,6 @@ public: | |||
| 96 | return m_node == rhs.m_node; | 96 | return m_node == rhs.m_node; |
| 97 | } | 97 | } |
| 98 | 98 | ||
| 99 | constexpr bool operator!=(const Iterator& rhs) const { | ||
| 100 | return !(*this == rhs); | ||
| 101 | } | ||
| 102 | |||
| 103 | constexpr pointer operator->() const { | 99 | constexpr pointer operator->() const { |
| 104 | return m_node; | 100 | return m_node; |
| 105 | } | 101 | } |
| @@ -242,19 +238,21 @@ public: | |||
| 242 | 238 | ||
| 243 | template <typename T> | 239 | template <typename T> |
| 244 | concept HasRedBlackKeyType = requires { | 240 | concept HasRedBlackKeyType = requires { |
| 245 | { std::is_same<typename T::RedBlackKeyType, void>::value } -> std::convertible_to<bool>; | 241 | { |
| 246 | }; | 242 | std::is_same<typename T::RedBlackKeyType, void>::value |
| 243 | } -> std::convertible_to<bool>; | ||
| 244 | }; | ||
| 247 | 245 | ||
| 248 | namespace impl { | 246 | namespace impl { |
| 249 | 247 | ||
| 250 | template <typename T, typename Default> | 248 | template <typename T, typename Default> |
| 251 | consteval auto* GetRedBlackKeyType() { | 249 | consteval auto* GetRedBlackKeyType() { |
| 252 | if constexpr (HasRedBlackKeyType<T>) { | 250 | if constexpr (HasRedBlackKeyType<T>) { |
| 253 | return static_cast<typename T::RedBlackKeyType*>(nullptr); | 251 | return static_cast<typename T::RedBlackKeyType*>(nullptr); |
| 254 | } else { | 252 | } else { |
| 255 | return static_cast<Default*>(nullptr); | 253 | return static_cast<Default*>(nullptr); |
| 256 | } | ||
| 257 | } | 254 | } |
| 255 | } | ||
| 258 | 256 | ||
| 259 | } // namespace impl | 257 | } // namespace impl |
| 260 | 258 | ||
| @@ -322,10 +320,6 @@ public: | |||
| 322 | return m_impl == rhs.m_impl; | 320 | return m_impl == rhs.m_impl; |
| 323 | } | 321 | } |
| 324 | 322 | ||
| 325 | constexpr bool operator!=(const Iterator& rhs) const { | ||
| 326 | return !(*this == rhs); | ||
| 327 | } | ||
| 328 | |||
| 329 | constexpr pointer operator->() const { | 323 | constexpr pointer operator->() const { |
| 330 | return Traits::GetParent(std::addressof(*m_impl)); | 324 | return Traits::GetParent(std::addressof(*m_impl)); |
| 331 | } | 325 | } |
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp index 2a3bded40..6e8e8eb36 100644 --- a/src/common/logging/backend.cpp +++ b/src/common/logging/backend.cpp | |||
| @@ -28,7 +28,7 @@ | |||
| 28 | #ifdef _WIN32 | 28 | #ifdef _WIN32 |
| 29 | #include "common/string_util.h" | 29 | #include "common/string_util.h" |
| 30 | #endif | 30 | #endif |
| 31 | #include "common/threadsafe_queue.h" | 31 | #include "common/bounded_threadsafe_queue.h" |
| 32 | 32 | ||
| 33 | namespace Common::Log { | 33 | namespace Common::Log { |
| 34 | 34 | ||
| @@ -155,6 +155,26 @@ public: | |||
| 155 | void EnableForStacktrace() override {} | 155 | void EnableForStacktrace() override {} |
| 156 | }; | 156 | }; |
| 157 | 157 | ||
| 158 | #ifdef ANDROID | ||
| 159 | /** | ||
| 160 | * Backend that writes to the Android logcat | ||
| 161 | */ | ||
| 162 | class LogcatBackend : public Backend { | ||
| 163 | public: | ||
| 164 | explicit LogcatBackend() = default; | ||
| 165 | |||
| 166 | ~LogcatBackend() override = default; | ||
| 167 | |||
| 168 | void Write(const Entry& entry) override { | ||
| 169 | PrintMessageToLogcat(entry); | ||
| 170 | } | ||
| 171 | |||
| 172 | void Flush() override {} | ||
| 173 | |||
| 174 | void EnableForStacktrace() override {} | ||
| 175 | }; | ||
| 176 | #endif | ||
| 177 | |||
| 158 | bool initialization_in_progress_suppress_logging = true; | 178 | bool initialization_in_progress_suppress_logging = true; |
| 159 | 179 | ||
| 160 | /** | 180 | /** |
| @@ -204,11 +224,11 @@ public: | |||
| 204 | 224 | ||
| 205 | void PushEntry(Class log_class, Level log_level, const char* filename, unsigned int line_num, | 225 | void PushEntry(Class log_class, Level log_level, const char* filename, unsigned int line_num, |
| 206 | const char* function, std::string&& message) { | 226 | const char* function, std::string&& message) { |
| 207 | if (!filter.CheckMessage(log_class, log_level)) | 227 | if (!filter.CheckMessage(log_class, log_level)) { |
| 208 | return; | 228 | return; |
| 209 | const Entry& entry = | 229 | } |
| 210 | CreateEntry(log_class, log_level, filename, line_num, function, std::move(message)); | 230 | message_queue.EmplaceWait( |
| 211 | message_queue.Push(entry); | 231 | CreateEntry(log_class, log_level, filename, line_num, function, std::move(message))); |
| 212 | } | 232 | } |
| 213 | 233 | ||
| 214 | private: | 234 | private: |
| @@ -225,7 +245,7 @@ private: | |||
| 225 | ForEachBackend([&entry](Backend& backend) { backend.Write(entry); }); | 245 | ForEachBackend([&entry](Backend& backend) { backend.Write(entry); }); |
| 226 | }; | 246 | }; |
| 227 | while (!stop_token.stop_requested()) { | 247 | while (!stop_token.stop_requested()) { |
| 228 | entry = message_queue.PopWait(stop_token); | 248 | message_queue.PopWait(entry, stop_token); |
| 229 | if (entry.filename != nullptr) { | 249 | if (entry.filename != nullptr) { |
| 230 | write_logs(); | 250 | write_logs(); |
| 231 | } | 251 | } |
| @@ -233,7 +253,7 @@ private: | |||
| 233 | // Drain the logging queue. Only writes out up to MAX_LOGS_TO_WRITE to prevent a | 253 | // Drain the logging queue. Only writes out up to MAX_LOGS_TO_WRITE to prevent a |
| 234 | // case where a system is repeatedly spamming logs even on close. | 254 | // case where a system is repeatedly spamming logs even on close. |
| 235 | int max_logs_to_write = filter.IsDebug() ? INT_MAX : 100; | 255 | int max_logs_to_write = filter.IsDebug() ? INT_MAX : 100; |
| 236 | while (max_logs_to_write-- && message_queue.Pop(entry)) { | 256 | while (max_logs_to_write-- && message_queue.TryPop(entry)) { |
| 237 | write_logs(); | 257 | write_logs(); |
| 238 | } | 258 | } |
| 239 | }); | 259 | }); |
| @@ -260,6 +280,9 @@ private: | |||
| 260 | lambda(static_cast<Backend&>(debugger_backend)); | 280 | lambda(static_cast<Backend&>(debugger_backend)); |
| 261 | lambda(static_cast<Backend&>(color_console_backend)); | 281 | lambda(static_cast<Backend&>(color_console_backend)); |
| 262 | lambda(static_cast<Backend&>(file_backend)); | 282 | lambda(static_cast<Backend&>(file_backend)); |
| 283 | #ifdef ANDROID | ||
| 284 | lambda(static_cast<Backend&>(lc_backend)); | ||
| 285 | #endif | ||
| 263 | } | 286 | } |
| 264 | 287 | ||
| 265 | static void Deleter(Impl* ptr) { | 288 | static void Deleter(Impl* ptr) { |
| @@ -272,8 +295,11 @@ private: | |||
| 272 | DebuggerBackend debugger_backend{}; | 295 | DebuggerBackend debugger_backend{}; |
| 273 | ColorConsoleBackend color_console_backend{}; | 296 | ColorConsoleBackend color_console_backend{}; |
| 274 | FileBackend file_backend; | 297 | FileBackend file_backend; |
| 298 | #ifdef ANDROID | ||
| 299 | LogcatBackend lc_backend{}; | ||
| 300 | #endif | ||
| 275 | 301 | ||
| 276 | MPSCQueue<Entry, true> message_queue{}; | 302 | MPSCQueue<Entry> message_queue{}; |
| 277 | std::chrono::steady_clock::time_point time_origin{std::chrono::steady_clock::now()}; | 303 | std::chrono::steady_clock::time_point time_origin{std::chrono::steady_clock::now()}; |
| 278 | std::jthread backend_thread; | 304 | std::jthread backend_thread; |
| 279 | }; | 305 | }; |
diff --git a/src/common/logging/filter.cpp b/src/common/logging/filter.cpp index a959acb74..c95909561 100644 --- a/src/common/logging/filter.cpp +++ b/src/common/logging/filter.cpp | |||
| @@ -119,7 +119,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) { | |||
| 119 | SUB(Service, NPNS) \ | 119 | SUB(Service, NPNS) \ |
| 120 | SUB(Service, NS) \ | 120 | SUB(Service, NS) \ |
| 121 | SUB(Service, NVDRV) \ | 121 | SUB(Service, NVDRV) \ |
| 122 | SUB(Service, NVFlinger) \ | 122 | SUB(Service, Nvnflinger) \ |
| 123 | SUB(Service, OLSC) \ | 123 | SUB(Service, OLSC) \ |
| 124 | SUB(Service, PCIE) \ | 124 | SUB(Service, PCIE) \ |
| 125 | SUB(Service, PCTL) \ | 125 | SUB(Service, PCTL) \ |
diff --git a/src/common/logging/text_formatter.cpp b/src/common/logging/text_formatter.cpp index 09398ea64..2c453177b 100644 --- a/src/common/logging/text_formatter.cpp +++ b/src/common/logging/text_formatter.cpp | |||
| @@ -8,6 +8,10 @@ | |||
| 8 | #include <windows.h> | 8 | #include <windows.h> |
| 9 | #endif | 9 | #endif |
| 10 | 10 | ||
| 11 | #ifdef ANDROID | ||
| 12 | #include <android/log.h> | ||
| 13 | #endif | ||
| 14 | |||
| 11 | #include "common/assert.h" | 15 | #include "common/assert.h" |
| 12 | #include "common/logging/filter.h" | 16 | #include "common/logging/filter.h" |
| 13 | #include "common/logging/log.h" | 17 | #include "common/logging/log.h" |
| @@ -106,4 +110,35 @@ void PrintColoredMessage(const Entry& entry) { | |||
| 106 | #undef ESC | 110 | #undef ESC |
| 107 | #endif | 111 | #endif |
| 108 | } | 112 | } |
| 113 | |||
| 114 | void PrintMessageToLogcat(const Entry& entry) { | ||
| 115 | #ifdef ANDROID | ||
| 116 | const auto str = FormatLogMessage(entry); | ||
| 117 | |||
| 118 | android_LogPriority android_log_priority; | ||
| 119 | switch (entry.log_level) { | ||
| 120 | case Level::Trace: | ||
| 121 | android_log_priority = ANDROID_LOG_VERBOSE; | ||
| 122 | break; | ||
| 123 | case Level::Debug: | ||
| 124 | android_log_priority = ANDROID_LOG_DEBUG; | ||
| 125 | break; | ||
| 126 | case Level::Info: | ||
| 127 | android_log_priority = ANDROID_LOG_INFO; | ||
| 128 | break; | ||
| 129 | case Level::Warning: | ||
| 130 | android_log_priority = ANDROID_LOG_WARN; | ||
| 131 | break; | ||
| 132 | case Level::Error: | ||
| 133 | android_log_priority = ANDROID_LOG_ERROR; | ||
| 134 | break; | ||
| 135 | case Level::Critical: | ||
| 136 | android_log_priority = ANDROID_LOG_FATAL; | ||
| 137 | break; | ||
| 138 | case Level::Count: | ||
| 139 | UNREACHABLE(); | ||
| 140 | } | ||
| 141 | __android_log_print(android_log_priority, "YuzuNative", "%s", str.c_str()); | ||
| 142 | #endif | ||
| 143 | } | ||
| 109 | } // namespace Common::Log | 144 | } // namespace Common::Log |
diff --git a/src/common/logging/text_formatter.h b/src/common/logging/text_formatter.h index 0d0ec4370..68417420b 100644 --- a/src/common/logging/text_formatter.h +++ b/src/common/logging/text_formatter.h | |||
| @@ -15,4 +15,6 @@ std::string FormatLogMessage(const Entry& entry); | |||
| 15 | void PrintMessage(const Entry& entry); | 15 | void PrintMessage(const Entry& entry); |
| 16 | /// Prints the same message as `PrintMessage`, but colored according to the severity level. | 16 | /// Prints the same message as `PrintMessage`, but colored according to the severity level. |
| 17 | void PrintColoredMessage(const Entry& entry); | 17 | void PrintColoredMessage(const Entry& entry); |
| 18 | /// Formats and prints a log entry to the android logcat. | ||
| 19 | void PrintMessageToLogcat(const Entry& entry); | ||
| 18 | } // namespace Common::Log | 20 | } // namespace Common::Log |
diff --git a/src/common/logging/types.h b/src/common/logging/types.h index 595c15ada..8356e3183 100644 --- a/src/common/logging/types.h +++ b/src/common/logging/types.h | |||
| @@ -29,107 +29,107 @@ enum class Level : u8 { | |||
| 29 | * filter.cpp. | 29 | * filter.cpp. |
| 30 | */ | 30 | */ |
| 31 | enum class Class : u8 { | 31 | enum class Class : u8 { |
| 32 | Log, ///< Messages about the log system itself | 32 | Log, ///< Messages about the log system itself |
| 33 | Common, ///< Library routines | 33 | Common, ///< Library routines |
| 34 | Common_Filesystem, ///< Filesystem interface library | 34 | Common_Filesystem, ///< Filesystem interface library |
| 35 | Common_Memory, ///< Memory mapping and management functions | 35 | Common_Memory, ///< Memory mapping and management functions |
| 36 | Core, ///< LLE emulation core | 36 | Core, ///< LLE emulation core |
| 37 | Core_ARM, ///< ARM CPU core | 37 | Core_ARM, ///< ARM CPU core |
| 38 | Core_Timing, ///< CoreTiming functions | 38 | Core_Timing, ///< CoreTiming functions |
| 39 | Config, ///< Emulator configuration (including commandline) | 39 | Config, ///< Emulator configuration (including commandline) |
| 40 | Debug, ///< Debugging tools | 40 | Debug, ///< Debugging tools |
| 41 | Debug_Emulated, ///< Debug messages from the emulated programs | 41 | Debug_Emulated, ///< Debug messages from the emulated programs |
| 42 | Debug_GPU, ///< GPU debugging tools | 42 | Debug_GPU, ///< GPU debugging tools |
| 43 | Debug_Breakpoint, ///< Logging breakpoints and watchpoints | 43 | Debug_Breakpoint, ///< Logging breakpoints and watchpoints |
| 44 | Debug_GDBStub, ///< GDB Stub | 44 | Debug_GDBStub, ///< GDB Stub |
| 45 | Kernel, ///< The HLE implementation of the CTR kernel | 45 | Kernel, ///< The HLE implementation of the CTR kernel |
| 46 | Kernel_SVC, ///< Kernel system calls | 46 | Kernel_SVC, ///< Kernel system calls |
| 47 | Service, ///< HLE implementation of system services. Each major service | 47 | Service, ///< HLE implementation of system services. Each major service |
| 48 | ///< should have its own subclass. | 48 | ///< should have its own subclass. |
| 49 | Service_ACC, ///< The ACC (Accounts) service | 49 | Service_ACC, ///< The ACC (Accounts) service |
| 50 | Service_AM, ///< The AM (Applet manager) service | 50 | Service_AM, ///< The AM (Applet manager) service |
| 51 | Service_AOC, ///< The AOC (AddOn Content) service | 51 | Service_AOC, ///< The AOC (AddOn Content) service |
| 52 | Service_APM, ///< The APM (Performance) service | 52 | Service_APM, ///< The APM (Performance) service |
| 53 | Service_ARP, ///< The ARP service | 53 | Service_ARP, ///< The ARP service |
| 54 | Service_Audio, ///< The Audio (Audio control) service | 54 | Service_Audio, ///< The Audio (Audio control) service |
| 55 | Service_BCAT, ///< The BCAT service | 55 | Service_BCAT, ///< The BCAT service |
| 56 | Service_BGTC, ///< The BGTC (Background Task Controller) service | 56 | Service_BGTC, ///< The BGTC (Background Task Controller) service |
| 57 | Service_BPC, ///< The BPC service | 57 | Service_BPC, ///< The BPC service |
| 58 | Service_BTDRV, ///< The Bluetooth driver service | 58 | Service_BTDRV, ///< The Bluetooth driver service |
| 59 | Service_BTM, ///< The BTM service | 59 | Service_BTM, ///< The BTM service |
| 60 | Service_Capture, ///< The capture service | 60 | Service_Capture, ///< The capture service |
| 61 | Service_ERPT, ///< The error reporting service | 61 | Service_ERPT, ///< The error reporting service |
| 62 | Service_ETicket, ///< The ETicket service | 62 | Service_ETicket, ///< The ETicket service |
| 63 | Service_EUPLD, ///< The error upload service | 63 | Service_EUPLD, ///< The error upload service |
| 64 | Service_Fatal, ///< The Fatal service | 64 | Service_Fatal, ///< The Fatal service |
| 65 | Service_FGM, ///< The FGM service | 65 | Service_FGM, ///< The FGM service |
| 66 | Service_Friend, ///< The friend service | 66 | Service_Friend, ///< The friend service |
| 67 | Service_FS, ///< The FS (Filesystem) service | 67 | Service_FS, ///< The FS (Filesystem) service |
| 68 | Service_GRC, ///< The game recording service | 68 | Service_GRC, ///< The game recording service |
| 69 | Service_HID, ///< The HID (Human interface device) service | 69 | Service_HID, ///< The HID (Human interface device) service |
| 70 | Service_IRS, ///< The IRS service | 70 | Service_IRS, ///< The IRS service |
| 71 | Service_JIT, ///< The JIT service | 71 | Service_JIT, ///< The JIT service |
| 72 | Service_LBL, ///< The LBL (LCD backlight) service | 72 | Service_LBL, ///< The LBL (LCD backlight) service |
| 73 | Service_LDN, ///< The LDN (Local domain network) service | 73 | Service_LDN, ///< The LDN (Local domain network) service |
| 74 | Service_LDR, ///< The loader service | 74 | Service_LDR, ///< The loader service |
| 75 | Service_LM, ///< The LM (Logger) service | 75 | Service_LM, ///< The LM (Logger) service |
| 76 | Service_Migration, ///< The migration service | 76 | Service_Migration, ///< The migration service |
| 77 | Service_Mii, ///< The Mii service | 77 | Service_Mii, ///< The Mii service |
| 78 | Service_MM, ///< The MM (Multimedia) service | 78 | Service_MM, ///< The MM (Multimedia) service |
| 79 | Service_MNPP, ///< The MNPP service | 79 | Service_MNPP, ///< The MNPP service |
| 80 | Service_NCM, ///< The NCM service | 80 | Service_NCM, ///< The NCM service |
| 81 | Service_NFC, ///< The NFC (Near-field communication) service | 81 | Service_NFC, ///< The NFC (Near-field communication) service |
| 82 | Service_NFP, ///< The NFP service | 82 | Service_NFP, ///< The NFP service |
| 83 | Service_NGCT, ///< The NGCT (No Good Content for Terra) service | 83 | Service_NGCT, ///< The NGCT (No Good Content for Terra) service |
| 84 | Service_NIFM, ///< The NIFM (Network interface) service | 84 | Service_NIFM, ///< The NIFM (Network interface) service |
| 85 | Service_NIM, ///< The NIM service | 85 | Service_NIM, ///< The NIM service |
| 86 | Service_NOTIF, ///< The NOTIF (Notification) service | 86 | Service_NOTIF, ///< The NOTIF (Notification) service |
| 87 | Service_NPNS, ///< The NPNS service | 87 | Service_NPNS, ///< The NPNS service |
| 88 | Service_NS, ///< The NS services | 88 | Service_NS, ///< The NS services |
| 89 | Service_NVDRV, ///< The NVDRV (Nvidia driver) service | 89 | Service_NVDRV, ///< The NVDRV (Nvidia driver) service |
| 90 | Service_NVFlinger, ///< The NVFlinger service | 90 | Service_Nvnflinger, ///< The Nvnflinger service |
| 91 | Service_OLSC, ///< The OLSC service | 91 | Service_OLSC, ///< The OLSC service |
| 92 | Service_PCIE, ///< The PCIe service | 92 | Service_PCIE, ///< The PCIe service |
| 93 | Service_PCTL, ///< The PCTL (Parental control) service | 93 | Service_PCTL, ///< The PCTL (Parental control) service |
| 94 | Service_PCV, ///< The PCV service | 94 | Service_PCV, ///< The PCV service |
| 95 | Service_PM, ///< The PM service | 95 | Service_PM, ///< The PM service |
| 96 | Service_PREPO, ///< The PREPO (Play report) service | 96 | Service_PREPO, ///< The PREPO (Play report) service |
| 97 | Service_PSC, ///< The PSC service | 97 | Service_PSC, ///< The PSC service |
| 98 | Service_PTM, ///< The PTM service | 98 | Service_PTM, ///< The PTM service |
| 99 | Service_SET, ///< The SET (Settings) service | 99 | Service_SET, ///< The SET (Settings) service |
| 100 | Service_SM, ///< The SM (Service manager) service | 100 | Service_SM, ///< The SM (Service manager) service |
| 101 | Service_SPL, ///< The SPL service | 101 | Service_SPL, ///< The SPL service |
| 102 | Service_SSL, ///< The SSL service | 102 | Service_SSL, ///< The SSL service |
| 103 | Service_TCAP, ///< The TCAP service. | 103 | Service_TCAP, ///< The TCAP service. |
| 104 | Service_Time, ///< The time service | 104 | Service_Time, ///< The time service |
| 105 | Service_USB, ///< The USB (Universal Serial Bus) service | 105 | Service_USB, ///< The USB (Universal Serial Bus) service |
| 106 | Service_VI, ///< The VI (Video interface) service | 106 | Service_VI, ///< The VI (Video interface) service |
| 107 | Service_WLAN, ///< The WLAN (Wireless local area network) service | 107 | Service_WLAN, ///< The WLAN (Wireless local area network) service |
| 108 | HW, ///< Low-level hardware emulation | 108 | HW, ///< Low-level hardware emulation |
| 109 | HW_Memory, ///< Memory-map and address translation | 109 | HW_Memory, ///< Memory-map and address translation |
| 110 | HW_LCD, ///< LCD register emulation | 110 | HW_LCD, ///< LCD register emulation |
| 111 | HW_GPU, ///< GPU control emulation | 111 | HW_GPU, ///< GPU control emulation |
| 112 | HW_AES, ///< AES engine emulation | 112 | HW_AES, ///< AES engine emulation |
| 113 | IPC, ///< IPC interface | 113 | IPC, ///< IPC interface |
| 114 | Frontend, ///< Emulator UI | 114 | Frontend, ///< Emulator UI |
| 115 | Render, ///< Emulator video output and hardware acceleration | 115 | Render, ///< Emulator video output and hardware acceleration |
| 116 | Render_Software, ///< Software renderer backend | 116 | Render_Software, ///< Software renderer backend |
| 117 | Render_OpenGL, ///< OpenGL backend | 117 | Render_OpenGL, ///< OpenGL backend |
| 118 | Render_Vulkan, ///< Vulkan backend | 118 | Render_Vulkan, ///< Vulkan backend |
| 119 | Shader, ///< Shader recompiler | 119 | Shader, ///< Shader recompiler |
| 120 | Shader_SPIRV, ///< Shader SPIR-V code generation | 120 | Shader_SPIRV, ///< Shader SPIR-V code generation |
| 121 | Shader_GLASM, ///< Shader GLASM code generation | 121 | Shader_GLASM, ///< Shader GLASM code generation |
| 122 | Shader_GLSL, ///< Shader GLSL code generation | 122 | Shader_GLSL, ///< Shader GLSL code generation |
| 123 | Audio, ///< Audio emulation | 123 | Audio, ///< Audio emulation |
| 124 | Audio_DSP, ///< The HLE implementation of the DSP | 124 | Audio_DSP, ///< The HLE implementation of the DSP |
| 125 | Audio_Sink, ///< Emulator audio output backend | 125 | Audio_Sink, ///< Emulator audio output backend |
| 126 | Loader, ///< ROM loader | 126 | Loader, ///< ROM loader |
| 127 | CheatEngine, ///< Memory manipulation and engine VM functions | 127 | CheatEngine, ///< Memory manipulation and engine VM functions |
| 128 | Crypto, ///< Cryptographic engine/functions | 128 | Crypto, ///< Cryptographic engine/functions |
| 129 | Input, ///< Input emulation | 129 | Input, ///< Input emulation |
| 130 | Network, ///< Network emulation | 130 | Network, ///< Network emulation |
| 131 | WebService, ///< Interface to yuzu Web Services | 131 | WebService, ///< Interface to yuzu Web Services |
| 132 | Count ///< Total number of logging classes | 132 | Count ///< Total number of logging classes |
| 133 | }; | 133 | }; |
| 134 | 134 | ||
| 135 | } // namespace Common::Log | 135 | } // namespace Common::Log |
diff --git a/src/common/make_unique_for_overwrite.h b/src/common/make_unique_for_overwrite.h index c7413cf51..17f81bba4 100644 --- a/src/common/make_unique_for_overwrite.h +++ b/src/common/make_unique_for_overwrite.h | |||
| @@ -9,17 +9,19 @@ | |||
| 9 | namespace Common { | 9 | namespace Common { |
| 10 | 10 | ||
| 11 | template <class T> | 11 | template <class T> |
| 12 | requires(!std::is_array_v<T>) std::unique_ptr<T> make_unique_for_overwrite() { | 12 | requires(!std::is_array_v<T>) |
| 13 | std::unique_ptr<T> make_unique_for_overwrite() { | ||
| 13 | return std::unique_ptr<T>(new T); | 14 | return std::unique_ptr<T>(new T); |
| 14 | } | 15 | } |
| 15 | 16 | ||
| 16 | template <class T> | 17 | template <class T> |
| 17 | requires std::is_unbounded_array_v<T> std::unique_ptr<T> make_unique_for_overwrite(std::size_t n) { | 18 | requires std::is_unbounded_array_v<T> |
| 19 | std::unique_ptr<T> make_unique_for_overwrite(std::size_t n) { | ||
| 18 | return std::unique_ptr<T>(new std::remove_extent_t<T>[n]); | 20 | return std::unique_ptr<T>(new std::remove_extent_t<T>[n]); |
| 19 | } | 21 | } |
| 20 | 22 | ||
| 21 | template <class T, class... Args> | 23 | template <class T, class... Args> |
| 22 | requires std::is_bounded_array_v<T> | 24 | requires std::is_bounded_array_v<T> |
| 23 | void make_unique_for_overwrite(Args&&...) = delete; | 25 | void make_unique_for_overwrite(Args&&...) = delete; |
| 24 | 26 | ||
| 25 | } // namespace Common | 27 | } // namespace Common |
diff --git a/src/common/overflow.h b/src/common/overflow.h new file mode 100644 index 000000000..44d8e7e73 --- /dev/null +++ b/src/common/overflow.h | |||
| @@ -0,0 +1,22 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <type_traits> | ||
| 7 | #include "bit_cast.h" | ||
| 8 | |||
| 9 | namespace Common { | ||
| 10 | |||
| 11 | template <typename T> | ||
| 12 | requires(std::is_integral_v<T> && std::is_signed_v<T>) | ||
| 13 | inline T WrappingAdd(T lhs, T rhs) { | ||
| 14 | using U = std::make_unsigned_t<T>; | ||
| 15 | |||
| 16 | U lhs_u = BitCast<U>(lhs); | ||
| 17 | U rhs_u = BitCast<U>(rhs); | ||
| 18 | |||
| 19 | return BitCast<T>(lhs_u + rhs_u); | ||
| 20 | } | ||
| 21 | |||
| 22 | } // namespace Common | ||
diff --git a/src/common/polyfill_ranges.h b/src/common/polyfill_ranges.h index ca44bfaef..512dbcbcb 100644 --- a/src/common/polyfill_ranges.h +++ b/src/common/polyfill_ranges.h | |||
| @@ -18,9 +18,9 @@ namespace ranges { | |||
| 18 | 18 | ||
| 19 | template <typename T> | 19 | template <typename T> |
| 20 | concept range = requires(T& t) { | 20 | concept range = requires(T& t) { |
| 21 | begin(t); | 21 | begin(t); |
| 22 | end(t); | 22 | end(t); |
| 23 | }; | 23 | }; |
| 24 | 24 | ||
| 25 | template <typename T> | 25 | template <typename T> |
| 26 | concept input_range = range<T>; | 26 | concept input_range = range<T>; |
| @@ -421,7 +421,7 @@ struct generate_fn { | |||
| 421 | } | 421 | } |
| 422 | 422 | ||
| 423 | template <typename R, std::copy_constructible F> | 423 | template <typename R, std::copy_constructible F> |
| 424 | requires std::invocable<F&> && ranges::output_range<R> | 424 | requires std::invocable<F&> && ranges::output_range<R> |
| 425 | constexpr ranges::iterator_t<R> operator()(R&& r, F gen) const { | 425 | constexpr ranges::iterator_t<R> operator()(R&& r, F gen) const { |
| 426 | return operator()(ranges::begin(r), ranges::end(r), std::move(gen)); | 426 | return operator()(ranges::begin(r), ranges::end(r), std::move(gen)); |
| 427 | } | 427 | } |
diff --git a/src/common/polyfill_thread.h b/src/common/polyfill_thread.h index 5a8d1ce08..b5ef055db 100644 --- a/src/common/polyfill_thread.h +++ b/src/common/polyfill_thread.h | |||
| @@ -11,6 +11,8 @@ | |||
| 11 | 11 | ||
| 12 | #ifdef __cpp_lib_jthread | 12 | #ifdef __cpp_lib_jthread |
| 13 | 13 | ||
| 14 | #include <chrono> | ||
| 15 | #include <condition_variable> | ||
| 14 | #include <stop_token> | 16 | #include <stop_token> |
| 15 | #include <thread> | 17 | #include <thread> |
| 16 | 18 | ||
| @@ -21,23 +23,36 @@ void CondvarWait(Condvar& cv, Lock& lock, std::stop_token token, Pred&& pred) { | |||
| 21 | cv.wait(lock, token, std::move(pred)); | 23 | cv.wait(lock, token, std::move(pred)); |
| 22 | } | 24 | } |
| 23 | 25 | ||
| 26 | template <typename Rep, typename Period> | ||
| 27 | bool StoppableTimedWait(std::stop_token token, const std::chrono::duration<Rep, Period>& rel_time) { | ||
| 28 | std::condition_variable_any cv; | ||
| 29 | std::mutex m; | ||
| 30 | |||
| 31 | // Perform the timed wait. | ||
| 32 | std::unique_lock lk{m}; | ||
| 33 | return !cv.wait_for(lk, token, rel_time, [&] { return token.stop_requested(); }); | ||
| 34 | } | ||
| 35 | |||
| 24 | } // namespace Common | 36 | } // namespace Common |
| 25 | 37 | ||
| 26 | #else | 38 | #else |
| 27 | 39 | ||
| 28 | #include <atomic> | 40 | #include <atomic> |
| 41 | #include <chrono> | ||
| 42 | #include <condition_variable> | ||
| 29 | #include <functional> | 43 | #include <functional> |
| 30 | #include <list> | 44 | #include <map> |
| 31 | #include <memory> | 45 | #include <memory> |
| 32 | #include <mutex> | 46 | #include <mutex> |
| 33 | #include <optional> | 47 | #include <optional> |
| 34 | #include <thread> | 48 | #include <thread> |
| 35 | #include <type_traits> | 49 | #include <type_traits> |
| 50 | #include <utility> | ||
| 36 | 51 | ||
| 37 | namespace std { | 52 | namespace std { |
| 38 | namespace polyfill { | 53 | namespace polyfill { |
| 39 | 54 | ||
| 40 | using stop_state_callbacks = list<function<void()>>; | 55 | using stop_state_callback = size_t; |
| 41 | 56 | ||
| 42 | class stop_state { | 57 | class stop_state { |
| 43 | public: | 58 | public: |
| @@ -45,61 +60,69 @@ public: | |||
| 45 | ~stop_state() = default; | 60 | ~stop_state() = default; |
| 46 | 61 | ||
| 47 | bool request_stop() { | 62 | bool request_stop() { |
| 48 | stop_state_callbacks callbacks; | 63 | unique_lock lk{m_lock}; |
| 49 | 64 | ||
| 50 | { | 65 | if (m_stop_requested) { |
| 51 | scoped_lock lk{m_lock}; | 66 | // Already set, nothing to do. |
| 67 | return false; | ||
| 68 | } | ||
| 52 | 69 | ||
| 53 | if (m_stop_requested.load()) { | 70 | // Mark stop requested. |
| 54 | // Already set, nothing to do | 71 | m_stop_requested = true; |
| 55 | return false; | ||
| 56 | } | ||
| 57 | 72 | ||
| 58 | // Set as requested | 73 | while (!m_callbacks.empty()) { |
| 59 | m_stop_requested = true; | 74 | // Get an iterator to the first element. |
| 75 | const auto it = m_callbacks.begin(); | ||
| 60 | 76 | ||
| 61 | // Copy callback list | 77 | // Move the callback function out of the map. |
| 62 | callbacks = m_callbacks; | 78 | function<void()> f; |
| 63 | } | 79 | swap(it->second, f); |
| 80 | |||
| 81 | // Erase the now-empty map element. | ||
| 82 | m_callbacks.erase(it); | ||
| 64 | 83 | ||
| 65 | for (auto callback : callbacks) { | 84 | // Run the callback. |
| 66 | callback(); | 85 | if (f) { |
| 86 | f(); | ||
| 87 | } | ||
| 67 | } | 88 | } |
| 68 | 89 | ||
| 69 | return true; | 90 | return true; |
| 70 | } | 91 | } |
| 71 | 92 | ||
| 72 | bool stop_requested() const { | 93 | bool stop_requested() const { |
| 73 | return m_stop_requested.load(); | 94 | unique_lock lk{m_lock}; |
| 95 | return m_stop_requested; | ||
| 74 | } | 96 | } |
| 75 | 97 | ||
| 76 | stop_state_callbacks::const_iterator insert_callback(function<void()> f) { | 98 | stop_state_callback insert_callback(function<void()> f) { |
| 77 | stop_state_callbacks::const_iterator ret{}; | 99 | unique_lock lk{m_lock}; |
| 78 | bool should_run{}; | ||
| 79 | |||
| 80 | { | ||
| 81 | scoped_lock lk{m_lock}; | ||
| 82 | should_run = m_stop_requested.load(); | ||
| 83 | m_callbacks.push_front(f); | ||
| 84 | ret = m_callbacks.begin(); | ||
| 85 | } | ||
| 86 | 100 | ||
| 87 | if (should_run) { | 101 | if (m_stop_requested) { |
| 88 | f(); | 102 | // Stop already requested. Don't insert anything, |
| 103 | // just run the callback synchronously. | ||
| 104 | if (f) { | ||
| 105 | f(); | ||
| 106 | } | ||
| 107 | return 0; | ||
| 89 | } | 108 | } |
| 90 | 109 | ||
| 110 | // Insert the callback. | ||
| 111 | stop_state_callback ret = ++m_next_callback; | ||
| 112 | m_callbacks.emplace(ret, move(f)); | ||
| 91 | return ret; | 113 | return ret; |
| 92 | } | 114 | } |
| 93 | 115 | ||
| 94 | void remove_callback(stop_state_callbacks::const_iterator it) { | 116 | void remove_callback(stop_state_callback cb) { |
| 95 | scoped_lock lk{m_lock}; | 117 | unique_lock lk{m_lock}; |
| 96 | m_callbacks.erase(it); | 118 | m_callbacks.erase(cb); |
| 97 | } | 119 | } |
| 98 | 120 | ||
| 99 | private: | 121 | private: |
| 100 | mutex m_lock; | 122 | mutable recursive_mutex m_lock; |
| 101 | atomic<bool> m_stop_requested; | 123 | map<stop_state_callback, function<void()>> m_callbacks; |
| 102 | stop_state_callbacks m_callbacks; | 124 | stop_state_callback m_next_callback{0}; |
| 125 | bool m_stop_requested{false}; | ||
| 103 | }; | 126 | }; |
| 104 | 127 | ||
| 105 | } // namespace polyfill | 128 | } // namespace polyfill |
| @@ -190,7 +213,7 @@ public: | |||
| 190 | using callback_type = Callback; | 213 | using callback_type = Callback; |
| 191 | 214 | ||
| 192 | template <typename C> | 215 | template <typename C> |
| 193 | requires constructible_from<Callback, C> | 216 | requires constructible_from<Callback, C> |
| 194 | explicit stop_callback(const stop_token& st, | 217 | explicit stop_callback(const stop_token& st, |
| 195 | C&& cb) noexcept(is_nothrow_constructible_v<Callback, C>) | 218 | C&& cb) noexcept(is_nothrow_constructible_v<Callback, C>) |
| 196 | : m_stop_state(st.m_stop_state) { | 219 | : m_stop_state(st.m_stop_state) { |
| @@ -199,7 +222,7 @@ public: | |||
| 199 | } | 222 | } |
| 200 | } | 223 | } |
| 201 | template <typename C> | 224 | template <typename C> |
| 202 | requires constructible_from<Callback, C> | 225 | requires constructible_from<Callback, C> |
| 203 | explicit stop_callback(stop_token&& st, | 226 | explicit stop_callback(stop_token&& st, |
| 204 | C&& cb) noexcept(is_nothrow_constructible_v<Callback, C>) | 227 | C&& cb) noexcept(is_nothrow_constructible_v<Callback, C>) |
| 205 | : m_stop_state(move(st.m_stop_state)) { | 228 | : m_stop_state(move(st.m_stop_state)) { |
| @@ -209,7 +232,7 @@ public: | |||
| 209 | } | 232 | } |
| 210 | ~stop_callback() { | 233 | ~stop_callback() { |
| 211 | if (m_stop_state && m_callback) { | 234 | if (m_stop_state && m_callback) { |
| 212 | m_stop_state->remove_callback(*m_callback); | 235 | m_stop_state->remove_callback(m_callback); |
| 213 | } | 236 | } |
| 214 | } | 237 | } |
| 215 | 238 | ||
| @@ -220,7 +243,7 @@ public: | |||
| 220 | 243 | ||
| 221 | private: | 244 | private: |
| 222 | shared_ptr<polyfill::stop_state> m_stop_state; | 245 | shared_ptr<polyfill::stop_state> m_stop_state; |
| 223 | optional<polyfill::stop_state_callbacks::const_iterator> m_callback; | 246 | polyfill::stop_state_callback m_callback; |
| 224 | }; | 247 | }; |
| 225 | 248 | ||
| 226 | template <typename Callback> | 249 | template <typename Callback> |
| @@ -318,6 +341,28 @@ void CondvarWait(Condvar& cv, Lock& lock, std::stop_token token, Pred pred) { | |||
| 318 | cv.wait(lock, [&] { return pred() || token.stop_requested(); }); | 341 | cv.wait(lock, [&] { return pred() || token.stop_requested(); }); |
| 319 | } | 342 | } |
| 320 | 343 | ||
| 344 | template <typename Rep, typename Period> | ||
| 345 | bool StoppableTimedWait(std::stop_token token, const std::chrono::duration<Rep, Period>& rel_time) { | ||
| 346 | if (token.stop_requested()) { | ||
| 347 | return false; | ||
| 348 | } | ||
| 349 | |||
| 350 | bool stop_requested = false; | ||
| 351 | std::condition_variable cv; | ||
| 352 | std::mutex m; | ||
| 353 | |||
| 354 | std::stop_callback cb(token, [&] { | ||
| 355 | // Wake up the waiting thread. | ||
| 356 | std::unique_lock lk{m}; | ||
| 357 | stop_requested = true; | ||
| 358 | cv.notify_one(); | ||
| 359 | }); | ||
| 360 | |||
| 361 | // Perform the timed wait. | ||
| 362 | std::unique_lock lk{m}; | ||
| 363 | return !cv.wait_for(lk, rel_time, [&] { return stop_requested; }); | ||
| 364 | } | ||
| 365 | |||
| 321 | } // namespace Common | 366 | } // namespace Common |
| 322 | 367 | ||
| 323 | #endif | 368 | #endif |
diff --git a/src/common/range_map.h b/src/common/range_map.h index 79c7ef547..ab73993e3 100644 --- a/src/common/range_map.h +++ b/src/common/range_map.h | |||
| @@ -38,12 +38,12 @@ public: | |||
| 38 | Map(address, address_end, null_value); | 38 | Map(address, address_end, null_value); |
| 39 | } | 39 | } |
| 40 | 40 | ||
| 41 | [[nodiscard]] size_t GetContinousSizeFrom(KeyTBase address) const { | 41 | [[nodiscard]] size_t GetContinuousSizeFrom(KeyTBase address) const { |
| 42 | const KeyT new_address = static_cast<KeyT>(address); | 42 | const KeyT new_address = static_cast<KeyT>(address); |
| 43 | if (new_address < 0) { | 43 | if (new_address < 0) { |
| 44 | return 0; | 44 | return 0; |
| 45 | } | 45 | } |
| 46 | return ContinousSizeInternal(new_address); | 46 | return ContinuousSizeInternal(new_address); |
| 47 | } | 47 | } |
| 48 | 48 | ||
| 49 | [[nodiscard]] ValueT GetValueAt(KeyT address) const { | 49 | [[nodiscard]] ValueT GetValueAt(KeyT address) const { |
| @@ -59,7 +59,7 @@ private: | |||
| 59 | using IteratorType = typename MapType::iterator; | 59 | using IteratorType = typename MapType::iterator; |
| 60 | using ConstIteratorType = typename MapType::const_iterator; | 60 | using ConstIteratorType = typename MapType::const_iterator; |
| 61 | 61 | ||
| 62 | size_t ContinousSizeInternal(KeyT address) const { | 62 | size_t ContinuousSizeInternal(KeyT address) const { |
| 63 | const auto it = GetFirstElementBeforeOrOn(address); | 63 | const auto it = GetFirstElementBeforeOrOn(address); |
| 64 | if (it == container.end() || it->second == null_value) { | 64 | if (it == container.end() || it->second == null_value) { |
| 65 | return 0; | 65 | return 0; |
diff --git a/src/common/ring_buffer.h b/src/common/ring_buffer.h index 4c328ab44..416680d44 100644 --- a/src/common/ring_buffer.h +++ b/src/common/ring_buffer.h | |||
| @@ -9,6 +9,7 @@ | |||
| 9 | #include <cstddef> | 9 | #include <cstddef> |
| 10 | #include <cstring> | 10 | #include <cstring> |
| 11 | #include <new> | 11 | #include <new> |
| 12 | #include <span> | ||
| 12 | #include <type_traits> | 13 | #include <type_traits> |
| 13 | #include <vector> | 14 | #include <vector> |
| 14 | 15 | ||
| @@ -53,7 +54,7 @@ public: | |||
| 53 | return push_count; | 54 | return push_count; |
| 54 | } | 55 | } |
| 55 | 56 | ||
| 56 | std::size_t Push(const std::vector<T>& input) { | 57 | std::size_t Push(const std::span<T> input) { |
| 57 | return Push(input.data(), input.size()); | 58 | return Push(input.data(), input.size()); |
| 58 | } | 59 | } |
| 59 | 60 | ||
diff --git a/src/common/scratch_buffer.h b/src/common/scratch_buffer.h index 1245a5086..6fe907953 100644 --- a/src/common/scratch_buffer.h +++ b/src/common/scratch_buffer.h | |||
| @@ -3,6 +3,9 @@ | |||
| 3 | 3 | ||
| 4 | #pragma once | 4 | #pragma once |
| 5 | 5 | ||
| 6 | #include <iterator> | ||
| 7 | |||
| 8 | #include "common/concepts.h" | ||
| 6 | #include "common/make_unique_for_overwrite.h" | 9 | #include "common/make_unique_for_overwrite.h" |
| 7 | 10 | ||
| 8 | namespace Common { | 11 | namespace Common { |
| @@ -16,6 +19,12 @@ namespace Common { | |||
| 16 | template <typename T> | 19 | template <typename T> |
| 17 | class ScratchBuffer { | 20 | class ScratchBuffer { |
| 18 | public: | 21 | public: |
| 22 | using iterator = T*; | ||
| 23 | using const_iterator = const T*; | ||
| 24 | using value_type = T; | ||
| 25 | using element_type = T; | ||
| 26 | using iterator_category = std::contiguous_iterator_tag; | ||
| 27 | |||
| 19 | ScratchBuffer() = default; | 28 | ScratchBuffer() = default; |
| 20 | 29 | ||
| 21 | explicit ScratchBuffer(size_t initial_capacity) | 30 | explicit ScratchBuffer(size_t initial_capacity) |
| @@ -23,6 +32,10 @@ public: | |||
| 23 | buffer{Common::make_unique_for_overwrite<T[]>(initial_capacity)} {} | 32 | buffer{Common::make_unique_for_overwrite<T[]>(initial_capacity)} {} |
| 24 | 33 | ||
| 25 | ~ScratchBuffer() = default; | 34 | ~ScratchBuffer() = default; |
| 35 | ScratchBuffer(const ScratchBuffer&) = delete; | ||
| 36 | ScratchBuffer& operator=(const ScratchBuffer&) = delete; | ||
| 37 | ScratchBuffer(ScratchBuffer&&) = default; | ||
| 38 | ScratchBuffer& operator=(ScratchBuffer&&) = default; | ||
| 26 | 39 | ||
| 27 | /// This will only grow the buffer's capacity if size is greater than the current capacity. | 40 | /// This will only grow the buffer's capacity if size is greater than the current capacity. |
| 28 | /// The previously held data will remain intact. | 41 | /// The previously held data will remain intact. |
| @@ -86,6 +99,12 @@ public: | |||
| 86 | return buffer_capacity; | 99 | return buffer_capacity; |
| 87 | } | 100 | } |
| 88 | 101 | ||
| 102 | void swap(ScratchBuffer& other) noexcept { | ||
| 103 | std::swap(last_requested_size, other.last_requested_size); | ||
| 104 | std::swap(buffer_capacity, other.buffer_capacity); | ||
| 105 | std::swap(buffer, other.buffer); | ||
| 106 | } | ||
| 107 | |||
| 89 | private: | 108 | private: |
| 90 | size_t last_requested_size{}; | 109 | size_t last_requested_size{}; |
| 91 | size_t buffer_capacity{}; | 110 | size_t buffer_capacity{}; |
diff --git a/src/common/settings.cpp b/src/common/settings.cpp index b1a2aa8b2..66dffc9bf 100644 --- a/src/common/settings.cpp +++ b/src/common/settings.cpp | |||
| @@ -1,12 +1,16 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #if __cpp_lib_chrono >= 201907L | ||
| 5 | #include <chrono> | ||
| 6 | #endif | ||
| 4 | #include <string_view> | 7 | #include <string_view> |
| 5 | 8 | ||
| 6 | #include "common/assert.h" | 9 | #include "common/assert.h" |
| 7 | #include "common/fs/path_util.h" | 10 | #include "common/fs/path_util.h" |
| 8 | #include "common/logging/log.h" | 11 | #include "common/logging/log.h" |
| 9 | #include "common/settings.h" | 12 | #include "common/settings.h" |
| 13 | #include "common/time_zone.h" | ||
| 10 | 14 | ||
| 11 | namespace Settings { | 15 | namespace Settings { |
| 12 | 16 | ||
| @@ -14,18 +18,23 @@ Values values; | |||
| 14 | static bool configuring_global = true; | 18 | static bool configuring_global = true; |
| 15 | 19 | ||
| 16 | std::string GetTimeZoneString() { | 20 | std::string GetTimeZoneString() { |
| 17 | static constexpr std::array timezones{ | ||
| 18 | "auto", "default", "CET", "CST6CDT", "Cuba", "EET", "Egypt", "Eire", | ||
| 19 | "EST", "EST5EDT", "GB", "GB-Eire", "GMT", "GMT+0", "GMT-0", "GMT0", | ||
| 20 | "Greenwich", "Hongkong", "HST", "Iceland", "Iran", "Israel", "Jamaica", "Japan", | ||
| 21 | "Kwajalein", "Libya", "MET", "MST", "MST7MDT", "Navajo", "NZ", "NZ-CHAT", | ||
| 22 | "Poland", "Portugal", "PRC", "PST8PDT", "ROC", "ROK", "Singapore", "Turkey", | ||
| 23 | "UCT", "Universal", "UTC", "W-SU", "WET", "Zulu", | ||
| 24 | }; | ||
| 25 | |||
| 26 | const auto time_zone_index = static_cast<std::size_t>(values.time_zone_index.GetValue()); | 21 | const auto time_zone_index = static_cast<std::size_t>(values.time_zone_index.GetValue()); |
| 27 | ASSERT(time_zone_index < timezones.size()); | 22 | ASSERT(time_zone_index < Common::TimeZone::GetTimeZoneStrings().size()); |
| 28 | return timezones[time_zone_index]; | 23 | |
| 24 | std::string location_name; | ||
| 25 | if (time_zone_index == 0) { // Auto | ||
| 26 | #if __cpp_lib_chrono >= 201907L | ||
| 27 | const struct std::chrono::tzdb& time_zone_data = std::chrono::get_tzdb(); | ||
| 28 | const std::chrono::time_zone* current_zone = time_zone_data.current_zone(); | ||
| 29 | std::string_view current_zone_name = current_zone->name(); | ||
| 30 | location_name = current_zone_name; | ||
| 31 | #else | ||
| 32 | location_name = Common::TimeZone::FindSystemTimeZone(); | ||
| 33 | #endif | ||
| 34 | } else { | ||
| 35 | location_name = Common::TimeZone::GetTimeZoneStrings()[time_zone_index]; | ||
| 36 | } | ||
| 37 | return location_name; | ||
| 29 | } | 38 | } |
| 30 | 39 | ||
| 31 | void LogSettings() { | 40 | void LogSettings() { |
| @@ -45,6 +54,7 @@ void LogSettings() { | |||
| 45 | log_setting("System_LanguageIndex", values.language_index.GetValue()); | 54 | log_setting("System_LanguageIndex", values.language_index.GetValue()); |
| 46 | log_setting("System_RegionIndex", values.region_index.GetValue()); | 55 | log_setting("System_RegionIndex", values.region_index.GetValue()); |
| 47 | log_setting("System_TimeZoneIndex", values.time_zone_index.GetValue()); | 56 | log_setting("System_TimeZoneIndex", values.time_zone_index.GetValue()); |
| 57 | log_setting("System_UnsafeMemoryLayout", values.use_unsafe_extended_memory_layout.GetValue()); | ||
| 48 | log_setting("Core_UseMultiCore", values.use_multi_core.GetValue()); | 58 | log_setting("Core_UseMultiCore", values.use_multi_core.GetValue()); |
| 49 | log_setting("CPU_Accuracy", values.cpu_accuracy.GetValue()); | 59 | log_setting("CPU_Accuracy", values.cpu_accuracy.GetValue()); |
| 50 | log_setting("Renderer_UseResolutionScaling", values.resolution_setup.GetValue()); | 60 | log_setting("Renderer_UseResolutionScaling", values.resolution_setup.GetValue()); |
| @@ -59,7 +69,10 @@ void LogSettings() { | |||
| 59 | values.use_asynchronous_gpu_emulation.GetValue()); | 69 | values.use_asynchronous_gpu_emulation.GetValue()); |
| 60 | log_setting("Renderer_NvdecEmulation", values.nvdec_emulation.GetValue()); | 70 | log_setting("Renderer_NvdecEmulation", values.nvdec_emulation.GetValue()); |
| 61 | log_setting("Renderer_AccelerateASTC", values.accelerate_astc.GetValue()); | 71 | log_setting("Renderer_AccelerateASTC", values.accelerate_astc.GetValue()); |
| 62 | log_setting("Renderer_UseVsync", values.use_vsync.GetValue()); | 72 | log_setting("Renderer_AsyncASTC", values.async_astc.GetValue()); |
| 73 | log_setting("Renderer_AstcRecompression", values.astc_recompression.GetValue()); | ||
| 74 | log_setting("Renderer_UseVsync", values.vsync_mode.GetValue()); | ||
| 75 | log_setting("Renderer_UseReactiveFlushing", values.use_reactive_flushing.GetValue()); | ||
| 63 | log_setting("Renderer_ShaderBackend", values.shader_backend.GetValue()); | 76 | log_setting("Renderer_ShaderBackend", values.shader_backend.GetValue()); |
| 64 | log_setting("Renderer_UseAsynchronousShaders", values.use_asynchronous_shaders.GetValue()); | 77 | log_setting("Renderer_UseAsynchronousShaders", values.use_asynchronous_shaders.GetValue()); |
| 65 | log_setting("Renderer_AnisotropicFilteringLevel", values.max_anisotropy.GetValue()); | 78 | log_setting("Renderer_AnisotropicFilteringLevel", values.max_anisotropy.GetValue()); |
| @@ -76,6 +89,13 @@ void LogSettings() { | |||
| 76 | log_setting("Debugging_GDBStub", values.use_gdbstub.GetValue()); | 89 | log_setting("Debugging_GDBStub", values.use_gdbstub.GetValue()); |
| 77 | log_setting("Input_EnableMotion", values.motion_enabled.GetValue()); | 90 | log_setting("Input_EnableMotion", values.motion_enabled.GetValue()); |
| 78 | log_setting("Input_EnableVibration", values.vibration_enabled.GetValue()); | 91 | log_setting("Input_EnableVibration", values.vibration_enabled.GetValue()); |
| 92 | log_setting("Input_EnableTouch", values.touchscreen.enabled); | ||
| 93 | log_setting("Input_EnableMouse", values.mouse_enabled.GetValue()); | ||
| 94 | log_setting("Input_EnableKeyboard", values.keyboard_enabled.GetValue()); | ||
| 95 | log_setting("Input_EnableRingController", values.enable_ring_controller.GetValue()); | ||
| 96 | log_setting("Input_EnableIrSensor", values.enable_ir_sensor.GetValue()); | ||
| 97 | log_setting("Input_EnableCustomJoycon", values.enable_joycon_driver.GetValue()); | ||
| 98 | log_setting("Input_EnableCustomProController", values.enable_procon_driver.GetValue()); | ||
| 79 | log_setting("Input_EnableRawInput", values.enable_raw_input.GetValue()); | 99 | log_setting("Input_EnableRawInput", values.enable_raw_input.GetValue()); |
| 80 | } | 100 | } |
| 81 | 101 | ||
| @@ -183,7 +203,7 @@ void RestoreGlobalState(bool is_powered_on) { | |||
| 183 | 203 | ||
| 184 | // Core | 204 | // Core |
| 185 | values.use_multi_core.SetGlobal(true); | 205 | values.use_multi_core.SetGlobal(true); |
| 186 | values.use_extended_memory_layout.SetGlobal(true); | 206 | values.use_unsafe_extended_memory_layout.SetGlobal(true); |
| 187 | 207 | ||
| 188 | // CPU | 208 | // CPU |
| 189 | values.cpu_accuracy.SetGlobal(true); | 209 | values.cpu_accuracy.SetGlobal(true); |
| @@ -197,9 +217,14 @@ void RestoreGlobalState(bool is_powered_on) { | |||
| 197 | // Renderer | 217 | // Renderer |
| 198 | values.fsr_sharpening_slider.SetGlobal(true); | 218 | values.fsr_sharpening_slider.SetGlobal(true); |
| 199 | values.renderer_backend.SetGlobal(true); | 219 | values.renderer_backend.SetGlobal(true); |
| 220 | values.async_presentation.SetGlobal(true); | ||
| 200 | values.renderer_force_max_clock.SetGlobal(true); | 221 | values.renderer_force_max_clock.SetGlobal(true); |
| 201 | values.vulkan_device.SetGlobal(true); | 222 | values.vulkan_device.SetGlobal(true); |
| 223 | values.fullscreen_mode.SetGlobal(true); | ||
| 202 | values.aspect_ratio.SetGlobal(true); | 224 | values.aspect_ratio.SetGlobal(true); |
| 225 | values.resolution_setup.SetGlobal(true); | ||
| 226 | values.scaling_filter.SetGlobal(true); | ||
| 227 | values.anti_aliasing.SetGlobal(true); | ||
| 203 | values.max_anisotropy.SetGlobal(true); | 228 | values.max_anisotropy.SetGlobal(true); |
| 204 | values.use_speed_limit.SetGlobal(true); | 229 | values.use_speed_limit.SetGlobal(true); |
| 205 | values.speed_limit.SetGlobal(true); | 230 | values.speed_limit.SetGlobal(true); |
| @@ -208,15 +233,18 @@ void RestoreGlobalState(bool is_powered_on) { | |||
| 208 | values.use_asynchronous_gpu_emulation.SetGlobal(true); | 233 | values.use_asynchronous_gpu_emulation.SetGlobal(true); |
| 209 | values.nvdec_emulation.SetGlobal(true); | 234 | values.nvdec_emulation.SetGlobal(true); |
| 210 | values.accelerate_astc.SetGlobal(true); | 235 | values.accelerate_astc.SetGlobal(true); |
| 211 | values.use_vsync.SetGlobal(true); | 236 | values.async_astc.SetGlobal(true); |
| 237 | values.astc_recompression.SetGlobal(true); | ||
| 238 | values.use_reactive_flushing.SetGlobal(true); | ||
| 212 | values.shader_backend.SetGlobal(true); | 239 | values.shader_backend.SetGlobal(true); |
| 213 | values.use_asynchronous_shaders.SetGlobal(true); | 240 | values.use_asynchronous_shaders.SetGlobal(true); |
| 214 | values.use_fast_gpu_time.SetGlobal(true); | 241 | values.use_fast_gpu_time.SetGlobal(true); |
| 215 | values.use_pessimistic_flushes.SetGlobal(true); | ||
| 216 | values.use_vulkan_driver_pipeline_cache.SetGlobal(true); | 242 | values.use_vulkan_driver_pipeline_cache.SetGlobal(true); |
| 217 | values.bg_red.SetGlobal(true); | 243 | values.bg_red.SetGlobal(true); |
| 218 | values.bg_green.SetGlobal(true); | 244 | values.bg_green.SetGlobal(true); |
| 219 | values.bg_blue.SetGlobal(true); | 245 | values.bg_blue.SetGlobal(true); |
| 246 | values.enable_compute_pipelines.SetGlobal(true); | ||
| 247 | values.use_video_framerate.SetGlobal(true); | ||
| 220 | 248 | ||
| 221 | // System | 249 | // System |
| 222 | values.language_index.SetGlobal(true); | 250 | values.language_index.SetGlobal(true); |
diff --git a/src/common/settings.h b/src/common/settings.h index 80b2eeabc..ae5ed93d8 100644 --- a/src/common/settings.h +++ b/src/common/settings.h | |||
| @@ -16,6 +16,13 @@ | |||
| 16 | 16 | ||
| 17 | namespace Settings { | 17 | namespace Settings { |
| 18 | 18 | ||
| 19 | enum class VSyncMode : u32 { | ||
| 20 | Immediate = 0, | ||
| 21 | Mailbox = 1, | ||
| 22 | FIFO = 2, | ||
| 23 | FIFORelaxed = 3, | ||
| 24 | }; | ||
| 25 | |||
| 19 | enum class RendererBackend : u32 { | 26 | enum class RendererBackend : u32 { |
| 20 | OpenGL = 0, | 27 | OpenGL = 0, |
| 21 | Vulkan = 1, | 28 | Vulkan = 1, |
| @@ -83,6 +90,12 @@ enum class AntiAliasing : u32 { | |||
| 83 | LastAA = Smaa, | 90 | LastAA = Smaa, |
| 84 | }; | 91 | }; |
| 85 | 92 | ||
| 93 | enum class AstcRecompression : u32 { | ||
| 94 | Uncompressed = 0, | ||
| 95 | Bc1 = 1, | ||
| 96 | Bc3 = 2, | ||
| 97 | }; | ||
| 98 | |||
| 86 | struct ResolutionScalingInfo { | 99 | struct ResolutionScalingInfo { |
| 87 | u32 up_scale{1}; | 100 | u32 up_scale{1}; |
| 88 | u32 down_shift{0}; | 101 | u32 down_shift{0}; |
| @@ -128,23 +141,25 @@ public: | |||
| 128 | /** | 141 | /** |
| 129 | * Sets a default value, label, and setting value. | 142 | * Sets a default value, label, and setting value. |
| 130 | * | 143 | * |
| 131 | * @param default_val Intial value of the setting, and default value of the setting | 144 | * @param default_val Initial value of the setting, and default value of the setting |
| 132 | * @param name Label for the setting | 145 | * @param name Label for the setting |
| 133 | */ | 146 | */ |
| 134 | explicit Setting(const Type& default_val, const std::string& name) requires(!ranged) | 147 | explicit Setting(const Type& default_val, const std::string& name) |
| 148 | requires(!ranged) | ||
| 135 | : value{default_val}, default_value{default_val}, label{name} {} | 149 | : value{default_val}, default_value{default_val}, label{name} {} |
| 136 | virtual ~Setting() = default; | 150 | virtual ~Setting() = default; |
| 137 | 151 | ||
| 138 | /** | 152 | /** |
| 139 | * Sets a default value, minimum value, maximum value, and label. | 153 | * Sets a default value, minimum value, maximum value, and label. |
| 140 | * | 154 | * |
| 141 | * @param default_val Intial value of the setting, and default value of the setting | 155 | * @param default_val Initial value of the setting, and default value of the setting |
| 142 | * @param min_val Sets the minimum allowed value of the setting | 156 | * @param min_val Sets the minimum allowed value of the setting |
| 143 | * @param max_val Sets the maximum allowed value of the setting | 157 | * @param max_val Sets the maximum allowed value of the setting |
| 144 | * @param name Label for the setting | 158 | * @param name Label for the setting |
| 145 | */ | 159 | */ |
| 146 | explicit Setting(const Type& default_val, const Type& min_val, const Type& max_val, | 160 | explicit Setting(const Type& default_val, const Type& min_val, const Type& max_val, |
| 147 | const std::string& name) requires(ranged) | 161 | const std::string& name) |
| 162 | requires(ranged) | ||
| 148 | : value{default_val}, | 163 | : value{default_val}, |
| 149 | default_value{default_val}, maximum{max_val}, minimum{min_val}, label{name} {} | 164 | default_value{default_val}, maximum{max_val}, minimum{min_val}, label{name} {} |
| 150 | 165 | ||
| @@ -229,23 +244,25 @@ public: | |||
| 229 | /** | 244 | /** |
| 230 | * Sets a default value, label, and setting value. | 245 | * Sets a default value, label, and setting value. |
| 231 | * | 246 | * |
| 232 | * @param default_val Intial value of the setting, and default value of the setting | 247 | * @param default_val Initial value of the setting, and default value of the setting |
| 233 | * @param name Label for the setting | 248 | * @param name Label for the setting |
| 234 | */ | 249 | */ |
| 235 | explicit SwitchableSetting(const Type& default_val, const std::string& name) requires(!ranged) | 250 | explicit SwitchableSetting(const Type& default_val, const std::string& name) |
| 251 | requires(!ranged) | ||
| 236 | : Setting<Type>{default_val, name} {} | 252 | : Setting<Type>{default_val, name} {} |
| 237 | virtual ~SwitchableSetting() = default; | 253 | virtual ~SwitchableSetting() = default; |
| 238 | 254 | ||
| 239 | /** | 255 | /** |
| 240 | * Sets a default value, minimum value, maximum value, and label. | 256 | * Sets a default value, minimum value, maximum value, and label. |
| 241 | * | 257 | * |
| 242 | * @param default_val Intial value of the setting, and default value of the setting | 258 | * @param default_val Initial value of the setting, and default value of the setting |
| 243 | * @param min_val Sets the minimum allowed value of the setting | 259 | * @param min_val Sets the minimum allowed value of the setting |
| 244 | * @param max_val Sets the maximum allowed value of the setting | 260 | * @param max_val Sets the maximum allowed value of the setting |
| 245 | * @param name Label for the setting | 261 | * @param name Label for the setting |
| 246 | */ | 262 | */ |
| 247 | explicit SwitchableSetting(const Type& default_val, const Type& min_val, const Type& max_val, | 263 | explicit SwitchableSetting(const Type& default_val, const Type& min_val, const Type& max_val, |
| 248 | const std::string& name) requires(ranged) | 264 | const std::string& name) |
| 265 | requires(ranged) | ||
| 249 | : Setting<Type, true>{default_val, min_val, max_val, name} {} | 266 | : Setting<Type, true>{default_val, min_val, max_val, name} {} |
| 250 | 267 | ||
| 251 | /** | 268 | /** |
| @@ -384,7 +401,8 @@ struct Values { | |||
| 384 | 401 | ||
| 385 | // Core | 402 | // Core |
| 386 | SwitchableSetting<bool> use_multi_core{true, "use_multi_core"}; | 403 | SwitchableSetting<bool> use_multi_core{true, "use_multi_core"}; |
| 387 | SwitchableSetting<bool> use_extended_memory_layout{false, "use_extended_memory_layout"}; | 404 | SwitchableSetting<bool> use_unsafe_extended_memory_layout{false, |
| 405 | "use_unsafe_extended_memory_layout"}; | ||
| 388 | 406 | ||
| 389 | // Cpu | 407 | // Cpu |
| 390 | SwitchableSetting<CPUAccuracy, true> cpu_accuracy{CPUAccuracy::Auto, CPUAccuracy::Auto, | 408 | SwitchableSetting<CPUAccuracy, true> cpu_accuracy{CPUAccuracy::Auto, CPUAccuracy::Auto, |
| @@ -418,6 +436,7 @@ struct Values { | |||
| 418 | // Renderer | 436 | // Renderer |
| 419 | SwitchableSetting<RendererBackend, true> renderer_backend{ | 437 | SwitchableSetting<RendererBackend, true> renderer_backend{ |
| 420 | RendererBackend::Vulkan, RendererBackend::OpenGL, RendererBackend::Null, "backend"}; | 438 | RendererBackend::Vulkan, RendererBackend::OpenGL, RendererBackend::Null, "backend"}; |
| 439 | SwitchableSetting<bool> async_presentation{false, "async_presentation"}; | ||
| 421 | SwitchableSetting<bool> renderer_force_max_clock{false, "force_max_clock"}; | 440 | SwitchableSetting<bool> renderer_force_max_clock{false, "force_max_clock"}; |
| 422 | Setting<bool> renderer_debug{false, "debug"}; | 441 | Setting<bool> renderer_debug{false, "debug"}; |
| 423 | Setting<bool> renderer_shader_feedback{false, "shader_feedback"}; | 442 | Setting<bool> renderer_shader_feedback{false, "shader_feedback"}; |
| @@ -449,14 +468,22 @@ struct Values { | |||
| 449 | SwitchableSetting<bool> use_asynchronous_gpu_emulation{true, "use_asynchronous_gpu_emulation"}; | 468 | SwitchableSetting<bool> use_asynchronous_gpu_emulation{true, "use_asynchronous_gpu_emulation"}; |
| 450 | SwitchableSetting<NvdecEmulation> nvdec_emulation{NvdecEmulation::GPU, "nvdec_emulation"}; | 469 | SwitchableSetting<NvdecEmulation> nvdec_emulation{NvdecEmulation::GPU, "nvdec_emulation"}; |
| 451 | SwitchableSetting<bool> accelerate_astc{true, "accelerate_astc"}; | 470 | SwitchableSetting<bool> accelerate_astc{true, "accelerate_astc"}; |
| 452 | SwitchableSetting<bool> use_vsync{true, "use_vsync"}; | 471 | SwitchableSetting<bool> async_astc{false, "async_astc"}; |
| 472 | Setting<VSyncMode, true> vsync_mode{VSyncMode::FIFO, VSyncMode::Immediate, | ||
| 473 | VSyncMode::FIFORelaxed, "use_vsync"}; | ||
| 474 | SwitchableSetting<bool> use_reactive_flushing{true, "use_reactive_flushing"}; | ||
| 453 | SwitchableSetting<ShaderBackend, true> shader_backend{ShaderBackend::GLSL, ShaderBackend::GLSL, | 475 | SwitchableSetting<ShaderBackend, true> shader_backend{ShaderBackend::GLSL, ShaderBackend::GLSL, |
| 454 | ShaderBackend::SPIRV, "shader_backend"}; | 476 | ShaderBackend::SPIRV, "shader_backend"}; |
| 455 | SwitchableSetting<bool> use_asynchronous_shaders{false, "use_asynchronous_shaders"}; | 477 | SwitchableSetting<bool> use_asynchronous_shaders{false, "use_asynchronous_shaders"}; |
| 456 | SwitchableSetting<bool> use_fast_gpu_time{true, "use_fast_gpu_time"}; | 478 | SwitchableSetting<bool> use_fast_gpu_time{true, "use_fast_gpu_time"}; |
| 457 | SwitchableSetting<bool> use_pessimistic_flushes{false, "use_pessimistic_flushes"}; | ||
| 458 | SwitchableSetting<bool> use_vulkan_driver_pipeline_cache{true, | 479 | SwitchableSetting<bool> use_vulkan_driver_pipeline_cache{true, |
| 459 | "use_vulkan_driver_pipeline_cache"}; | 480 | "use_vulkan_driver_pipeline_cache"}; |
| 481 | SwitchableSetting<bool> enable_compute_pipelines{false, "enable_compute_pipelines"}; | ||
| 482 | SwitchableSetting<AstcRecompression, true> astc_recompression{ | ||
| 483 | AstcRecompression::Uncompressed, AstcRecompression::Uncompressed, AstcRecompression::Bc3, | ||
| 484 | "astc_recompression"}; | ||
| 485 | SwitchableSetting<bool> use_video_framerate{false, "use_video_framerate"}; | ||
| 486 | SwitchableSetting<bool> barrier_feedback_loops{true, "barrier_feedback_loops"}; | ||
| 460 | 487 | ||
| 461 | SwitchableSetting<u8> bg_red{0, "bg_red"}; | 488 | SwitchableSetting<u8> bg_red{0, "bg_red"}; |
| 462 | SwitchableSetting<u8> bg_green{0, "bg_green"}; | 489 | SwitchableSetting<u8> bg_green{0, "bg_green"}; |
| @@ -483,6 +510,8 @@ struct Values { | |||
| 483 | 510 | ||
| 484 | Setting<bool> enable_raw_input{false, "enable_raw_input"}; | 511 | Setting<bool> enable_raw_input{false, "enable_raw_input"}; |
| 485 | Setting<bool> controller_navigation{true, "controller_navigation"}; | 512 | Setting<bool> controller_navigation{true, "controller_navigation"}; |
| 513 | Setting<bool> enable_joycon_driver{true, "enable_joycon_driver"}; | ||
| 514 | Setting<bool> enable_procon_driver{false, "enable_procon_driver"}; | ||
| 486 | 515 | ||
| 487 | SwitchableSetting<bool> vibration_enabled{true, "vibration_enabled"}; | 516 | SwitchableSetting<bool> vibration_enabled{true, "vibration_enabled"}; |
| 488 | SwitchableSetting<bool> enable_accurate_vibrations{false, "enable_accurate_vibrations"}; | 517 | SwitchableSetting<bool> enable_accurate_vibrations{false, "enable_accurate_vibrations"}; |
| @@ -496,9 +525,16 @@ struct Values { | |||
| 496 | Setting<bool> tas_loop{false, "tas_loop"}; | 525 | Setting<bool> tas_loop{false, "tas_loop"}; |
| 497 | 526 | ||
| 498 | Setting<bool> mouse_panning{false, "mouse_panning"}; | 527 | Setting<bool> mouse_panning{false, "mouse_panning"}; |
| 499 | Setting<u8, true> mouse_panning_sensitivity{10, 1, 100, "mouse_panning_sensitivity"}; | 528 | Setting<u8, true> mouse_panning_x_sensitivity{50, 1, 100, "mouse_panning_x_sensitivity"}; |
| 500 | Setting<bool> mouse_enabled{false, "mouse_enabled"}; | 529 | Setting<u8, true> mouse_panning_y_sensitivity{50, 1, 100, "mouse_panning_y_sensitivity"}; |
| 530 | Setting<u8, true> mouse_panning_deadzone_x_counterweight{ | ||
| 531 | 0, 0, 100, "mouse_panning_deadzone_x_counterweight"}; | ||
| 532 | Setting<u8, true> mouse_panning_deadzone_y_counterweight{ | ||
| 533 | 0, 0, 100, "mouse_panning_deadzone_y_counterweight"}; | ||
| 534 | Setting<u8, true> mouse_panning_decay_strength{22, 0, 100, "mouse_panning_decay_strength"}; | ||
| 535 | Setting<u8, true> mouse_panning_min_decay{5, 0, 100, "mouse_panning_min_decay"}; | ||
| 501 | 536 | ||
| 537 | Setting<bool> mouse_enabled{false, "mouse_enabled"}; | ||
| 502 | Setting<bool> emulate_analog_keyboard{false, "emulate_analog_keyboard"}; | 538 | Setting<bool> emulate_analog_keyboard{false, "emulate_analog_keyboard"}; |
| 503 | Setting<bool> keyboard_enabled{false, "keyboard_enabled"}; | 539 | Setting<bool> keyboard_enabled{false, "keyboard_enabled"}; |
| 504 | 540 | ||
| @@ -518,6 +554,8 @@ struct Values { | |||
| 518 | Setting<bool> enable_ir_sensor{false, "enable_ir_sensor"}; | 554 | Setting<bool> enable_ir_sensor{false, "enable_ir_sensor"}; |
| 519 | Setting<std::string> ir_sensor_device{"auto", "ir_sensor_device"}; | 555 | Setting<std::string> ir_sensor_device{"auto", "ir_sensor_device"}; |
| 520 | 556 | ||
| 557 | Setting<bool> random_amiibo_id{false, "random_amiibo_id"}; | ||
| 558 | |||
| 521 | // Data Storage | 559 | // Data Storage |
| 522 | Setting<bool> use_virtual_sd{true, "use_virtual_sd"}; | 560 | Setting<bool> use_virtual_sd{true, "use_virtual_sd"}; |
| 523 | Setting<bool> gamecard_inserted{false, "gamecard_inserted"}; | 561 | Setting<bool> gamecard_inserted{false, "gamecard_inserted"}; |
diff --git a/src/common/steady_clock.cpp b/src/common/steady_clock.cpp new file mode 100644 index 000000000..9415eed29 --- /dev/null +++ b/src/common/steady_clock.cpp | |||
| @@ -0,0 +1,80 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #if defined(_WIN32) | ||
| 5 | #include <windows.h> | ||
| 6 | #else | ||
| 7 | #include <time.h> | ||
| 8 | #endif | ||
| 9 | |||
| 10 | #include "common/steady_clock.h" | ||
| 11 | |||
| 12 | namespace Common { | ||
| 13 | |||
| 14 | #ifdef _WIN32 | ||
| 15 | static s64 WindowsQueryPerformanceFrequency() { | ||
| 16 | LARGE_INTEGER frequency; | ||
| 17 | QueryPerformanceFrequency(&frequency); | ||
| 18 | return frequency.QuadPart; | ||
| 19 | } | ||
| 20 | |||
| 21 | static s64 WindowsQueryPerformanceCounter() { | ||
| 22 | LARGE_INTEGER counter; | ||
| 23 | QueryPerformanceCounter(&counter); | ||
| 24 | return counter.QuadPart; | ||
| 25 | } | ||
| 26 | |||
| 27 | static s64 GetSystemTimeNS() { | ||
| 28 | // GetSystemTimePreciseAsFileTime returns the file time in 100ns units. | ||
| 29 | static constexpr s64 Multiplier = 100; | ||
| 30 | // Convert Windows epoch to Unix epoch. | ||
| 31 | static constexpr s64 WindowsEpochToUnixEpoch = 0x19DB1DED53E8000LL; | ||
| 32 | |||
| 33 | FILETIME filetime; | ||
| 34 | GetSystemTimePreciseAsFileTime(&filetime); | ||
| 35 | return Multiplier * ((static_cast<s64>(filetime.dwHighDateTime) << 32) + | ||
| 36 | static_cast<s64>(filetime.dwLowDateTime) - WindowsEpochToUnixEpoch); | ||
| 37 | } | ||
| 38 | #endif | ||
| 39 | |||
| 40 | SteadyClock::time_point SteadyClock::Now() noexcept { | ||
| 41 | #if defined(_WIN32) | ||
| 42 | static const auto freq = WindowsQueryPerformanceFrequency(); | ||
| 43 | const auto counter = WindowsQueryPerformanceCounter(); | ||
| 44 | |||
| 45 | // 10 MHz is a very common QPC frequency on modern PCs. | ||
| 46 | // Optimizing for this specific frequency can double the performance of | ||
| 47 | // this function by avoiding the expensive frequency conversion path. | ||
| 48 | static constexpr s64 TenMHz = 10'000'000; | ||
| 49 | |||
| 50 | if (freq == TenMHz) [[likely]] { | ||
| 51 | static_assert(period::den % TenMHz == 0); | ||
| 52 | static constexpr s64 Multiplier = period::den / TenMHz; | ||
| 53 | return time_point{duration{counter * Multiplier}}; | ||
| 54 | } | ||
| 55 | |||
| 56 | const auto whole = (counter / freq) * period::den; | ||
| 57 | const auto part = (counter % freq) * period::den / freq; | ||
| 58 | return time_point{duration{whole + part}}; | ||
| 59 | #elif defined(__APPLE__) | ||
| 60 | return time_point{duration{clock_gettime_nsec_np(CLOCK_MONOTONIC_RAW)}}; | ||
| 61 | #else | ||
| 62 | timespec ts; | ||
| 63 | clock_gettime(CLOCK_MONOTONIC, &ts); | ||
| 64 | return time_point{std::chrono::seconds{ts.tv_sec} + std::chrono::nanoseconds{ts.tv_nsec}}; | ||
| 65 | #endif | ||
| 66 | } | ||
| 67 | |||
| 68 | RealTimeClock::time_point RealTimeClock::Now() noexcept { | ||
| 69 | #if defined(_WIN32) | ||
| 70 | return time_point{duration{GetSystemTimeNS()}}; | ||
| 71 | #elif defined(__APPLE__) | ||
| 72 | return time_point{duration{clock_gettime_nsec_np(CLOCK_REALTIME)}}; | ||
| 73 | #else | ||
| 74 | timespec ts; | ||
| 75 | clock_gettime(CLOCK_REALTIME, &ts); | ||
| 76 | return time_point{std::chrono::seconds{ts.tv_sec} + std::chrono::nanoseconds{ts.tv_nsec}}; | ||
| 77 | #endif | ||
| 78 | } | ||
| 79 | |||
| 80 | }; // namespace Common | ||
diff --git a/src/common/steady_clock.h b/src/common/steady_clock.h new file mode 100644 index 000000000..dbd0e2513 --- /dev/null +++ b/src/common/steady_clock.h | |||
| @@ -0,0 +1,34 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <chrono> | ||
| 7 | |||
| 8 | #include "common/common_types.h" | ||
| 9 | |||
| 10 | namespace Common { | ||
| 11 | |||
| 12 | struct SteadyClock { | ||
| 13 | using rep = s64; | ||
| 14 | using period = std::nano; | ||
| 15 | using duration = std::chrono::nanoseconds; | ||
| 16 | using time_point = std::chrono::time_point<SteadyClock>; | ||
| 17 | |||
| 18 | static constexpr bool is_steady = true; | ||
| 19 | |||
| 20 | [[nodiscard]] static time_point Now() noexcept; | ||
| 21 | }; | ||
| 22 | |||
| 23 | struct RealTimeClock { | ||
| 24 | using rep = s64; | ||
| 25 | using period = std::nano; | ||
| 26 | using duration = std::chrono::nanoseconds; | ||
| 27 | using time_point = std::chrono::time_point<RealTimeClock>; | ||
| 28 | |||
| 29 | static constexpr bool is_steady = false; | ||
| 30 | |||
| 31 | [[nodiscard]] static time_point Now() noexcept; | ||
| 32 | }; | ||
| 33 | |||
| 34 | } // namespace Common | ||
diff --git a/src/common/string_util.cpp b/src/common/string_util.cpp index b26db4796..feab1653d 100644 --- a/src/common/string_util.cpp +++ b/src/common/string_util.cpp | |||
| @@ -30,7 +30,7 @@ std::string ToUpper(std::string str) { | |||
| 30 | return str; | 30 | return str; |
| 31 | } | 31 | } |
| 32 | 32 | ||
| 33 | std::string StringFromBuffer(const std::vector<u8>& data) { | 33 | std::string StringFromBuffer(std::span<const u8> data) { |
| 34 | return std::string(data.begin(), std::find(data.begin(), data.end(), '\0')); | 34 | return std::string(data.begin(), std::find(data.begin(), data.end(), '\0')); |
| 35 | } | 35 | } |
| 36 | 36 | ||
| @@ -125,18 +125,18 @@ std::string ReplaceAll(std::string result, const std::string& src, const std::st | |||
| 125 | return result; | 125 | return result; |
| 126 | } | 126 | } |
| 127 | 127 | ||
| 128 | std::string UTF16ToUTF8(const std::u16string& input) { | 128 | std::string UTF16ToUTF8(std::u16string_view input) { |
| 129 | std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> convert; | 129 | std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> convert; |
| 130 | return convert.to_bytes(input); | 130 | return convert.to_bytes(input.data(), input.data() + input.size()); |
| 131 | } | 131 | } |
| 132 | 132 | ||
| 133 | std::u16string UTF8ToUTF16(const std::string& input) { | 133 | std::u16string UTF8ToUTF16(std::string_view input) { |
| 134 | std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> convert; | 134 | std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> convert; |
| 135 | return convert.from_bytes(input); | 135 | return convert.from_bytes(input.data(), input.data() + input.size()); |
| 136 | } | 136 | } |
| 137 | 137 | ||
| 138 | #ifdef _WIN32 | 138 | #ifdef _WIN32 |
| 139 | static std::wstring CPToUTF16(u32 code_page, const std::string& input) { | 139 | static std::wstring CPToUTF16(u32 code_page, std::string_view input) { |
| 140 | const auto size = | 140 | const auto size = |
| 141 | MultiByteToWideChar(code_page, 0, input.data(), static_cast<int>(input.size()), nullptr, 0); | 141 | MultiByteToWideChar(code_page, 0, input.data(), static_cast<int>(input.size()), nullptr, 0); |
| 142 | 142 | ||
| @@ -154,7 +154,7 @@ static std::wstring CPToUTF16(u32 code_page, const std::string& input) { | |||
| 154 | return output; | 154 | return output; |
| 155 | } | 155 | } |
| 156 | 156 | ||
| 157 | std::string UTF16ToUTF8(const std::wstring& input) { | 157 | std::string UTF16ToUTF8(std::wstring_view input) { |
| 158 | const auto size = WideCharToMultiByte(CP_UTF8, 0, input.data(), static_cast<int>(input.size()), | 158 | const auto size = WideCharToMultiByte(CP_UTF8, 0, input.data(), static_cast<int>(input.size()), |
| 159 | nullptr, 0, nullptr, nullptr); | 159 | nullptr, 0, nullptr, nullptr); |
| 160 | if (size == 0) { | 160 | if (size == 0) { |
| @@ -172,7 +172,7 @@ std::string UTF16ToUTF8(const std::wstring& input) { | |||
| 172 | return output; | 172 | return output; |
| 173 | } | 173 | } |
| 174 | 174 | ||
| 175 | std::wstring UTF8ToUTF16W(const std::string& input) { | 175 | std::wstring UTF8ToUTF16W(std::string_view input) { |
| 176 | return CPToUTF16(CP_UTF8, input); | 176 | return CPToUTF16(CP_UTF8, input); |
| 177 | } | 177 | } |
| 178 | 178 | ||
diff --git a/src/common/string_util.h b/src/common/string_util.h index ce18a33cf..c351f1a0c 100644 --- a/src/common/string_util.h +++ b/src/common/string_util.h | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <cstddef> | 7 | #include <cstddef> |
| 8 | #include <span> | ||
| 8 | #include <string> | 9 | #include <string> |
| 9 | #include <vector> | 10 | #include <vector> |
| 10 | #include "common/common_types.h" | 11 | #include "common/common_types.h" |
| @@ -17,7 +18,7 @@ namespace Common { | |||
| 17 | /// Make a string uppercase | 18 | /// Make a string uppercase |
| 18 | [[nodiscard]] std::string ToUpper(std::string str); | 19 | [[nodiscard]] std::string ToUpper(std::string str); |
| 19 | 20 | ||
| 20 | [[nodiscard]] std::string StringFromBuffer(const std::vector<u8>& data); | 21 | [[nodiscard]] std::string StringFromBuffer(std::span<const u8> data); |
| 21 | 22 | ||
| 22 | [[nodiscard]] std::string StripSpaces(const std::string& s); | 23 | [[nodiscard]] std::string StripSpaces(const std::string& s); |
| 23 | [[nodiscard]] std::string StripQuotes(const std::string& s); | 24 | [[nodiscard]] std::string StripQuotes(const std::string& s); |
| @@ -35,12 +36,12 @@ bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _ | |||
| 35 | [[nodiscard]] std::string ReplaceAll(std::string result, const std::string& src, | 36 | [[nodiscard]] std::string ReplaceAll(std::string result, const std::string& src, |
| 36 | const std::string& dest); | 37 | const std::string& dest); |
| 37 | 38 | ||
| 38 | [[nodiscard]] std::string UTF16ToUTF8(const std::u16string& input); | 39 | [[nodiscard]] std::string UTF16ToUTF8(std::u16string_view input); |
| 39 | [[nodiscard]] std::u16string UTF8ToUTF16(const std::string& input); | 40 | [[nodiscard]] std::u16string UTF8ToUTF16(std::string_view input); |
| 40 | 41 | ||
| 41 | #ifdef _WIN32 | 42 | #ifdef _WIN32 |
| 42 | [[nodiscard]] std::string UTF16ToUTF8(const std::wstring& input); | 43 | [[nodiscard]] std::string UTF16ToUTF8(std::wstring_view input); |
| 43 | [[nodiscard]] std::wstring UTF8ToUTF16W(const std::string& str); | 44 | [[nodiscard]] std::wstring UTF8ToUTF16W(std::string_view str); |
| 44 | 45 | ||
| 45 | #endif | 46 | #endif |
| 46 | 47 | ||
diff --git a/src/common/swap.h b/src/common/swap.h index 037b82781..085baaf9a 100644 --- a/src/common/swap.h +++ b/src/common/swap.h | |||
| @@ -229,7 +229,7 @@ public: | |||
| 229 | value = swap(swap() - 1); | 229 | value = swap(swap() - 1); |
| 230 | return old; | 230 | return old; |
| 231 | } | 231 | } |
| 232 | // Comparaison | 232 | // Comparison |
| 233 | // v == i | 233 | // v == i |
| 234 | bool operator==(const swapped_t& i) const { | 234 | bool operator==(const swapped_t& i) const { |
| 235 | return swap() == i.swap(); | 235 | return swap() == i.swap(); |
| @@ -368,7 +368,7 @@ public: | |||
| 368 | // Member | 368 | // Member |
| 369 | /** todo **/ | 369 | /** todo **/ |
| 370 | 370 | ||
| 371 | // Arithmetics | 371 | // Arithmetic |
| 372 | template <typename S, typename T2, typename F2> | 372 | template <typename S, typename T2, typename F2> |
| 373 | friend S operator+(const S& p, const swapped_t v); | 373 | friend S operator+(const S& p, const swapped_t v); |
| 374 | 374 | ||
| @@ -384,7 +384,7 @@ public: | |||
| 384 | template <typename S, typename T2, typename F2> | 384 | template <typename S, typename T2, typename F2> |
| 385 | friend S operator%(const S& p, const swapped_t v); | 385 | friend S operator%(const S& p, const swapped_t v); |
| 386 | 386 | ||
| 387 | // Arithmetics + assignments | 387 | // Arithmetic + assignments |
| 388 | template <typename S, typename T2, typename F2> | 388 | template <typename S, typename T2, typename F2> |
| 389 | friend S operator+=(const S& p, const swapped_t v); | 389 | friend S operator+=(const S& p, const swapped_t v); |
| 390 | 390 | ||
| @@ -415,7 +415,7 @@ public: | |||
| 415 | friend bool operator==(const S& p, const swapped_t v); | 415 | friend bool operator==(const S& p, const swapped_t v); |
| 416 | }; | 416 | }; |
| 417 | 417 | ||
| 418 | // Arithmetics | 418 | // Arithmetic |
| 419 | template <typename S, typename T, typename F> | 419 | template <typename S, typename T, typename F> |
| 420 | S operator+(const S& i, const swap_struct_t<T, F> v) { | 420 | S operator+(const S& i, const swap_struct_t<T, F> v) { |
| 421 | return i + v.swap(); | 421 | return i + v.swap(); |
| @@ -441,7 +441,7 @@ S operator%(const S& i, const swap_struct_t<T, F> v) { | |||
| 441 | return i % v.swap(); | 441 | return i % v.swap(); |
| 442 | } | 442 | } |
| 443 | 443 | ||
| 444 | // Arithmetics + assignments | 444 | // Arithmetic + assignments |
| 445 | template <typename S, typename T, typename F> | 445 | template <typename S, typename T, typename F> |
| 446 | S& operator+=(S& i, const swap_struct_t<T, F> v) { | 446 | S& operator+=(S& i, const swap_struct_t<T, F> v) { |
| 447 | i += v.swap(); | 447 | i += v.swap(); |
| @@ -465,7 +465,7 @@ S operator&(const swap_struct_t<T, F> v, const S& i) { | |||
| 465 | return static_cast<S>(v.swap() & i); | 465 | return static_cast<S>(v.swap() & i); |
| 466 | } | 466 | } |
| 467 | 467 | ||
| 468 | // Comparaison | 468 | // Comparison |
| 469 | template <typename S, typename T, typename F> | 469 | template <typename S, typename T, typename F> |
| 470 | bool operator<(const S& p, const swap_struct_t<T, F> v) { | 470 | bool operator<(const S& p, const swap_struct_t<T, F> v) { |
| 471 | return p < v.swap(); | 471 | return p < v.swap(); |
diff --git a/src/common/telemetry.cpp b/src/common/telemetry.cpp index d26394359..91352912d 100644 --- a/src/common/telemetry.cpp +++ b/src/common/telemetry.cpp | |||
| @@ -97,6 +97,7 @@ void AppendCPUInfo(FieldCollection& fc) { | |||
| 97 | add_field("CPU_Extension_x64_PCLMULQDQ", caps.pclmulqdq); | 97 | add_field("CPU_Extension_x64_PCLMULQDQ", caps.pclmulqdq); |
| 98 | add_field("CPU_Extension_x64_POPCNT", caps.popcnt); | 98 | add_field("CPU_Extension_x64_POPCNT", caps.popcnt); |
| 99 | add_field("CPU_Extension_x64_SHA", caps.sha); | 99 | add_field("CPU_Extension_x64_SHA", caps.sha); |
| 100 | add_field("CPU_Extension_x64_WAITPKG", caps.waitpkg); | ||
| 100 | #else | 101 | #else |
| 101 | fc.AddField(FieldType::UserSystem, "CPU_Model", "Other"); | 102 | fc.AddField(FieldType::UserSystem, "CPU_Model", "Other"); |
| 102 | #endif | 103 | #endif |
diff --git a/src/common/thread.h b/src/common/thread.h index 8ae169b4e..c6976fb6c 100644 --- a/src/common/thread.h +++ b/src/common/thread.h | |||
| @@ -55,7 +55,7 @@ public: | |||
| 55 | is_set = false; | 55 | is_set = false; |
| 56 | } | 56 | } |
| 57 | 57 | ||
| 58 | [[nodiscard]] bool IsSet() { | 58 | [[nodiscard]] bool IsSet() const { |
| 59 | return is_set; | 59 | return is_set; |
| 60 | } | 60 | } |
| 61 | 61 | ||
diff --git a/src/common/time_zone.cpp b/src/common/time_zone.cpp index 126836b01..d8d7896c6 100644 --- a/src/common/time_zone.cpp +++ b/src/common/time_zone.cpp | |||
| @@ -2,14 +2,33 @@ | |||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #include <chrono> | 4 | #include <chrono> |
| 5 | #include <exception> | ||
| 5 | #include <iomanip> | 6 | #include <iomanip> |
| 6 | #include <sstream> | 7 | #include <sstream> |
| 8 | #include <stdexcept> | ||
| 9 | #include <fmt/chrono.h> | ||
| 10 | #include <fmt/core.h> | ||
| 7 | 11 | ||
| 8 | #include "common/logging/log.h" | 12 | #include "common/logging/log.h" |
| 13 | #include "common/settings.h" | ||
| 9 | #include "common/time_zone.h" | 14 | #include "common/time_zone.h" |
| 10 | 15 | ||
| 11 | namespace Common::TimeZone { | 16 | namespace Common::TimeZone { |
| 12 | 17 | ||
| 18 | // Time zone strings | ||
| 19 | constexpr std::array timezones{ | ||
| 20 | "GMT", "GMT", "CET", "CST6CDT", "Cuba", "EET", "Egypt", "Eire", | ||
| 21 | "EST", "EST5EDT", "GB", "GB-Eire", "GMT", "GMT+0", "GMT-0", "GMT0", | ||
| 22 | "Greenwich", "Hongkong", "HST", "Iceland", "Iran", "Israel", "Jamaica", "Japan", | ||
| 23 | "Kwajalein", "Libya", "MET", "MST", "MST7MDT", "Navajo", "NZ", "NZ-CHAT", | ||
| 24 | "Poland", "Portugal", "PRC", "PST8PDT", "ROC", "ROK", "Singapore", "Turkey", | ||
| 25 | "UCT", "Universal", "UTC", "W-SU", "WET", "Zulu", | ||
| 26 | }; | ||
| 27 | |||
| 28 | const std::array<const char*, 46>& GetTimeZoneStrings() { | ||
| 29 | return timezones; | ||
| 30 | } | ||
| 31 | |||
| 13 | std::string GetDefaultTimeZone() { | 32 | std::string GetDefaultTimeZone() { |
| 14 | return "GMT"; | 33 | return "GMT"; |
| 15 | } | 34 | } |
| @@ -18,10 +37,7 @@ static std::string GetOsTimeZoneOffset() { | |||
| 18 | const std::time_t t{std::time(nullptr)}; | 37 | const std::time_t t{std::time(nullptr)}; |
| 19 | const std::tm tm{*std::localtime(&t)}; | 38 | const std::tm tm{*std::localtime(&t)}; |
| 20 | 39 | ||
| 21 | std::stringstream ss; | 40 | return fmt::format("{:%z}", tm); |
| 22 | ss << std::put_time(&tm, "%z"); // Get the current timezone offset, e.g. "-400", as a string | ||
| 23 | |||
| 24 | return ss.str(); | ||
| 25 | } | 41 | } |
| 26 | 42 | ||
| 27 | static int ConvertOsTimeZoneOffsetToInt(const std::string& timezone) { | 43 | static int ConvertOsTimeZoneOffsetToInt(const std::string& timezone) { |
| @@ -45,4 +61,43 @@ std::chrono::seconds GetCurrentOffsetSeconds() { | |||
| 45 | return std::chrono::seconds{seconds}; | 61 | return std::chrono::seconds{seconds}; |
| 46 | } | 62 | } |
| 47 | 63 | ||
| 64 | // Key is [Hours * 100 + Minutes], multiplied by 100 if DST | ||
| 65 | const static std::map<s64, const char*> off_timezones = { | ||
| 66 | {530, "Asia/Calcutta"}, {930, "Australia/Darwin"}, {845, "Australia/Eucla"}, | ||
| 67 | {103000, "Australia/Adelaide"}, {1030, "Australia/Lord_Howe"}, {630, "Indian/Cocos"}, | ||
| 68 | {1245, "Pacific/Chatham"}, {134500, "Pacific/Chatham"}, {-330, "Canada/Newfoundland"}, | ||
| 69 | {-23000, "Canada/Newfoundland"}, {430, "Asia/Kabul"}, {330, "Asia/Tehran"}, | ||
| 70 | {43000, "Asia/Tehran"}, {545, "Asia/Kathmandu"}, {-930, "Asia/Marquesas"}, | ||
| 71 | }; | ||
| 72 | |||
| 73 | std::string FindSystemTimeZone() { | ||
| 74 | #if defined(MINGW) | ||
| 75 | // MinGW has broken strftime -- https://sourceforge.net/p/mingw-w64/bugs/793/ | ||
| 76 | // e.g. fmt::format("{:%z}") -- returns "Eastern Daylight Time" when it should be "-0400" | ||
| 77 | return timezones[0]; | ||
| 78 | #else | ||
| 79 | const s64 seconds = static_cast<s64>(GetCurrentOffsetSeconds().count()); | ||
| 80 | |||
| 81 | const s64 minutes = seconds / 60; | ||
| 82 | const s64 hours = minutes / 60; | ||
| 83 | |||
| 84 | const s64 minutes_off = minutes - hours * 60; | ||
| 85 | |||
| 86 | if (minutes_off != 0) { | ||
| 87 | const auto the_time = std::time(nullptr); | ||
| 88 | const struct std::tm& local = *std::localtime(&the_time); | ||
| 89 | const bool is_dst = local.tm_isdst != 0; | ||
| 90 | |||
| 91 | const s64 tz_index = (hours * 100 + minutes_off) * (is_dst ? 100 : 1); | ||
| 92 | |||
| 93 | try { | ||
| 94 | return off_timezones.at(tz_index); | ||
| 95 | } catch (std::out_of_range&) { | ||
| 96 | LOG_ERROR(Common, "Time zone {} not handled, defaulting to hour offset.", tz_index); | ||
| 97 | } | ||
| 98 | } | ||
| 99 | return fmt::format("Etc/GMT{:s}{:d}", hours > 0 ? "-" : "+", std::abs(hours)); | ||
| 100 | #endif | ||
| 101 | } | ||
| 102 | |||
| 48 | } // namespace Common::TimeZone | 103 | } // namespace Common::TimeZone |
diff --git a/src/common/time_zone.h b/src/common/time_zone.h index 99cae6ef2..f574d5c04 100644 --- a/src/common/time_zone.h +++ b/src/common/time_zone.h | |||
| @@ -3,15 +3,21 @@ | |||
| 3 | 3 | ||
| 4 | #pragma once | 4 | #pragma once |
| 5 | 5 | ||
| 6 | #include <array> | ||
| 6 | #include <chrono> | 7 | #include <chrono> |
| 7 | #include <string> | 8 | #include <string> |
| 8 | 9 | ||
| 9 | namespace Common::TimeZone { | 10 | namespace Common::TimeZone { |
| 10 | 11 | ||
| 12 | [[nodiscard]] const std::array<const char*, 46>& GetTimeZoneStrings(); | ||
| 13 | |||
| 11 | /// Gets the default timezone, i.e. "GMT" | 14 | /// Gets the default timezone, i.e. "GMT" |
| 12 | [[nodiscard]] std::string GetDefaultTimeZone(); | 15 | [[nodiscard]] std::string GetDefaultTimeZone(); |
| 13 | 16 | ||
| 14 | /// Gets the offset of the current timezone (from the default), in seconds | 17 | /// Gets the offset of the current timezone (from the default), in seconds |
| 15 | [[nodiscard]] std::chrono::seconds GetCurrentOffsetSeconds(); | 18 | [[nodiscard]] std::chrono::seconds GetCurrentOffsetSeconds(); |
| 16 | 19 | ||
| 20 | /// Searches time zone offsets for the closest offset to the system time zone | ||
| 21 | [[nodiscard]] std::string FindSystemTimeZone(); | ||
| 22 | |||
| 17 | } // namespace Common::TimeZone | 23 | } // namespace Common::TimeZone |
diff --git a/src/common/tree.h b/src/common/tree.h index f77859209..f4fc43de3 100644 --- a/src/common/tree.h +++ b/src/common/tree.h | |||
| @@ -103,12 +103,12 @@ concept IsRBEntry = CheckRBEntry<T>::value; | |||
| 103 | 103 | ||
| 104 | template <typename T> | 104 | template <typename T> |
| 105 | concept HasRBEntry = requires(T& t, const T& ct) { | 105 | concept HasRBEntry = requires(T& t, const T& ct) { |
| 106 | { t.GetRBEntry() } -> std::same_as<RBEntry<T>&>; | 106 | { t.GetRBEntry() } -> std::same_as<RBEntry<T>&>; |
| 107 | { ct.GetRBEntry() } -> std::same_as<const RBEntry<T>&>; | 107 | { ct.GetRBEntry() } -> std::same_as<const RBEntry<T>&>; |
| 108 | }; | 108 | }; |
| 109 | 109 | ||
| 110 | template <typename T> | 110 | template <typename T> |
| 111 | requires HasRBEntry<T> | 111 | requires HasRBEntry<T> |
| 112 | class RBHead { | 112 | class RBHead { |
| 113 | private: | 113 | private: |
| 114 | T* m_rbh_root = nullptr; | 114 | T* m_rbh_root = nullptr; |
| @@ -130,90 +130,90 @@ public: | |||
| 130 | }; | 130 | }; |
| 131 | 131 | ||
| 132 | template <typename T> | 132 | template <typename T> |
| 133 | requires HasRBEntry<T> | 133 | requires HasRBEntry<T> |
| 134 | [[nodiscard]] constexpr RBEntry<T>& RB_ENTRY(T* t) { | 134 | [[nodiscard]] constexpr RBEntry<T>& RB_ENTRY(T* t) { |
| 135 | return t->GetRBEntry(); | 135 | return t->GetRBEntry(); |
| 136 | } | 136 | } |
| 137 | template <typename T> | 137 | template <typename T> |
| 138 | requires HasRBEntry<T> | 138 | requires HasRBEntry<T> |
| 139 | [[nodiscard]] constexpr const RBEntry<T>& RB_ENTRY(const T* t) { | 139 | [[nodiscard]] constexpr const RBEntry<T>& RB_ENTRY(const T* t) { |
| 140 | return t->GetRBEntry(); | 140 | return t->GetRBEntry(); |
| 141 | } | 141 | } |
| 142 | 142 | ||
| 143 | template <typename T> | 143 | template <typename T> |
| 144 | requires HasRBEntry<T> | 144 | requires HasRBEntry<T> |
| 145 | [[nodiscard]] constexpr T* RB_LEFT(T* t) { | 145 | [[nodiscard]] constexpr T* RB_LEFT(T* t) { |
| 146 | return RB_ENTRY(t).Left(); | 146 | return RB_ENTRY(t).Left(); |
| 147 | } | 147 | } |
| 148 | template <typename T> | 148 | template <typename T> |
| 149 | requires HasRBEntry<T> | 149 | requires HasRBEntry<T> |
| 150 | [[nodiscard]] constexpr const T* RB_LEFT(const T* t) { | 150 | [[nodiscard]] constexpr const T* RB_LEFT(const T* t) { |
| 151 | return RB_ENTRY(t).Left(); | 151 | return RB_ENTRY(t).Left(); |
| 152 | } | 152 | } |
| 153 | 153 | ||
| 154 | template <typename T> | 154 | template <typename T> |
| 155 | requires HasRBEntry<T> | 155 | requires HasRBEntry<T> |
| 156 | [[nodiscard]] constexpr T* RB_RIGHT(T* t) { | 156 | [[nodiscard]] constexpr T* RB_RIGHT(T* t) { |
| 157 | return RB_ENTRY(t).Right(); | 157 | return RB_ENTRY(t).Right(); |
| 158 | } | 158 | } |
| 159 | template <typename T> | 159 | template <typename T> |
| 160 | requires HasRBEntry<T> | 160 | requires HasRBEntry<T> |
| 161 | [[nodiscard]] constexpr const T* RB_RIGHT(const T* t) { | 161 | [[nodiscard]] constexpr const T* RB_RIGHT(const T* t) { |
| 162 | return RB_ENTRY(t).Right(); | 162 | return RB_ENTRY(t).Right(); |
| 163 | } | 163 | } |
| 164 | 164 | ||
| 165 | template <typename T> | 165 | template <typename T> |
| 166 | requires HasRBEntry<T> | 166 | requires HasRBEntry<T> |
| 167 | [[nodiscard]] constexpr T* RB_PARENT(T* t) { | 167 | [[nodiscard]] constexpr T* RB_PARENT(T* t) { |
| 168 | return RB_ENTRY(t).Parent(); | 168 | return RB_ENTRY(t).Parent(); |
| 169 | } | 169 | } |
| 170 | template <typename T> | 170 | template <typename T> |
| 171 | requires HasRBEntry<T> | 171 | requires HasRBEntry<T> |
| 172 | [[nodiscard]] constexpr const T* RB_PARENT(const T* t) { | 172 | [[nodiscard]] constexpr const T* RB_PARENT(const T* t) { |
| 173 | return RB_ENTRY(t).Parent(); | 173 | return RB_ENTRY(t).Parent(); |
| 174 | } | 174 | } |
| 175 | 175 | ||
| 176 | template <typename T> | 176 | template <typename T> |
| 177 | requires HasRBEntry<T> | 177 | requires HasRBEntry<T> |
| 178 | constexpr void RB_SET_LEFT(T* t, T* e) { | 178 | constexpr void RB_SET_LEFT(T* t, T* e) { |
| 179 | RB_ENTRY(t).SetLeft(e); | 179 | RB_ENTRY(t).SetLeft(e); |
| 180 | } | 180 | } |
| 181 | template <typename T> | 181 | template <typename T> |
| 182 | requires HasRBEntry<T> | 182 | requires HasRBEntry<T> |
| 183 | constexpr void RB_SET_RIGHT(T* t, T* e) { | 183 | constexpr void RB_SET_RIGHT(T* t, T* e) { |
| 184 | RB_ENTRY(t).SetRight(e); | 184 | RB_ENTRY(t).SetRight(e); |
| 185 | } | 185 | } |
| 186 | template <typename T> | 186 | template <typename T> |
| 187 | requires HasRBEntry<T> | 187 | requires HasRBEntry<T> |
| 188 | constexpr void RB_SET_PARENT(T* t, T* e) { | 188 | constexpr void RB_SET_PARENT(T* t, T* e) { |
| 189 | RB_ENTRY(t).SetParent(e); | 189 | RB_ENTRY(t).SetParent(e); |
| 190 | } | 190 | } |
| 191 | 191 | ||
| 192 | template <typename T> | 192 | template <typename T> |
| 193 | requires HasRBEntry<T> | 193 | requires HasRBEntry<T> |
| 194 | [[nodiscard]] constexpr bool RB_IS_BLACK(const T* t) { | 194 | [[nodiscard]] constexpr bool RB_IS_BLACK(const T* t) { |
| 195 | return RB_ENTRY(t).IsBlack(); | 195 | return RB_ENTRY(t).IsBlack(); |
| 196 | } | 196 | } |
| 197 | template <typename T> | 197 | template <typename T> |
| 198 | requires HasRBEntry<T> | 198 | requires HasRBEntry<T> |
| 199 | [[nodiscard]] constexpr bool RB_IS_RED(const T* t) { | 199 | [[nodiscard]] constexpr bool RB_IS_RED(const T* t) { |
| 200 | return RB_ENTRY(t).IsRed(); | 200 | return RB_ENTRY(t).IsRed(); |
| 201 | } | 201 | } |
| 202 | 202 | ||
| 203 | template <typename T> | 203 | template <typename T> |
| 204 | requires HasRBEntry<T> | 204 | requires HasRBEntry<T> |
| 205 | [[nodiscard]] constexpr RBColor RB_COLOR(const T* t) { | 205 | [[nodiscard]] constexpr RBColor RB_COLOR(const T* t) { |
| 206 | return RB_ENTRY(t).Color(); | 206 | return RB_ENTRY(t).Color(); |
| 207 | } | 207 | } |
| 208 | 208 | ||
| 209 | template <typename T> | 209 | template <typename T> |
| 210 | requires HasRBEntry<T> | 210 | requires HasRBEntry<T> |
| 211 | constexpr void RB_SET_COLOR(T* t, RBColor c) { | 211 | constexpr void RB_SET_COLOR(T* t, RBColor c) { |
| 212 | RB_ENTRY(t).SetColor(c); | 212 | RB_ENTRY(t).SetColor(c); |
| 213 | } | 213 | } |
| 214 | 214 | ||
| 215 | template <typename T> | 215 | template <typename T> |
| 216 | requires HasRBEntry<T> | 216 | requires HasRBEntry<T> |
| 217 | constexpr void RB_SET(T* elm, T* parent) { | 217 | constexpr void RB_SET(T* elm, T* parent) { |
| 218 | auto& rb_entry = RB_ENTRY(elm); | 218 | auto& rb_entry = RB_ENTRY(elm); |
| 219 | rb_entry.SetParent(parent); | 219 | rb_entry.SetParent(parent); |
| @@ -223,14 +223,14 @@ constexpr void RB_SET(T* elm, T* parent) { | |||
| 223 | } | 223 | } |
| 224 | 224 | ||
| 225 | template <typename T> | 225 | template <typename T> |
| 226 | requires HasRBEntry<T> | 226 | requires HasRBEntry<T> |
| 227 | constexpr void RB_SET_BLACKRED(T* black, T* red) { | 227 | constexpr void RB_SET_BLACKRED(T* black, T* red) { |
| 228 | RB_SET_COLOR(black, RBColor::RB_BLACK); | 228 | RB_SET_COLOR(black, RBColor::RB_BLACK); |
| 229 | RB_SET_COLOR(red, RBColor::RB_RED); | 229 | RB_SET_COLOR(red, RBColor::RB_RED); |
| 230 | } | 230 | } |
| 231 | 231 | ||
| 232 | template <typename T> | 232 | template <typename T> |
| 233 | requires HasRBEntry<T> | 233 | requires HasRBEntry<T> |
| 234 | constexpr void RB_ROTATE_LEFT(RBHead<T>& head, T* elm, T*& tmp) { | 234 | constexpr void RB_ROTATE_LEFT(RBHead<T>& head, T* elm, T*& tmp) { |
| 235 | tmp = RB_RIGHT(elm); | 235 | tmp = RB_RIGHT(elm); |
| 236 | if (RB_SET_RIGHT(elm, RB_LEFT(tmp)); RB_RIGHT(elm) != nullptr) { | 236 | if (RB_SET_RIGHT(elm, RB_LEFT(tmp)); RB_RIGHT(elm) != nullptr) { |
| @@ -252,7 +252,7 @@ constexpr void RB_ROTATE_LEFT(RBHead<T>& head, T* elm, T*& tmp) { | |||
| 252 | } | 252 | } |
| 253 | 253 | ||
| 254 | template <typename T> | 254 | template <typename T> |
| 255 | requires HasRBEntry<T> | 255 | requires HasRBEntry<T> |
| 256 | constexpr void RB_ROTATE_RIGHT(RBHead<T>& head, T* elm, T*& tmp) { | 256 | constexpr void RB_ROTATE_RIGHT(RBHead<T>& head, T* elm, T*& tmp) { |
| 257 | tmp = RB_LEFT(elm); | 257 | tmp = RB_LEFT(elm); |
| 258 | if (RB_SET_LEFT(elm, RB_RIGHT(tmp)); RB_LEFT(elm) != nullptr) { | 258 | if (RB_SET_LEFT(elm, RB_RIGHT(tmp)); RB_LEFT(elm) != nullptr) { |
| @@ -274,7 +274,7 @@ constexpr void RB_ROTATE_RIGHT(RBHead<T>& head, T* elm, T*& tmp) { | |||
| 274 | } | 274 | } |
| 275 | 275 | ||
| 276 | template <typename T> | 276 | template <typename T> |
| 277 | requires HasRBEntry<T> | 277 | requires HasRBEntry<T> |
| 278 | constexpr void RB_REMOVE_COLOR(RBHead<T>& head, T* parent, T* elm) { | 278 | constexpr void RB_REMOVE_COLOR(RBHead<T>& head, T* parent, T* elm) { |
| 279 | T* tmp; | 279 | T* tmp; |
| 280 | while ((elm == nullptr || RB_IS_BLACK(elm)) && elm != head.Root()) { | 280 | while ((elm == nullptr || RB_IS_BLACK(elm)) && elm != head.Root()) { |
| @@ -358,7 +358,7 @@ constexpr void RB_REMOVE_COLOR(RBHead<T>& head, T* parent, T* elm) { | |||
| 358 | } | 358 | } |
| 359 | 359 | ||
| 360 | template <typename T> | 360 | template <typename T> |
| 361 | requires HasRBEntry<T> | 361 | requires HasRBEntry<T> |
| 362 | constexpr T* RB_REMOVE(RBHead<T>& head, T* elm) { | 362 | constexpr T* RB_REMOVE(RBHead<T>& head, T* elm) { |
| 363 | T* child = nullptr; | 363 | T* child = nullptr; |
| 364 | T* parent = nullptr; | 364 | T* parent = nullptr; |
| @@ -451,7 +451,7 @@ constexpr T* RB_REMOVE(RBHead<T>& head, T* elm) { | |||
| 451 | } | 451 | } |
| 452 | 452 | ||
| 453 | template <typename T> | 453 | template <typename T> |
| 454 | requires HasRBEntry<T> | 454 | requires HasRBEntry<T> |
| 455 | constexpr void RB_INSERT_COLOR(RBHead<T>& head, T* elm) { | 455 | constexpr void RB_INSERT_COLOR(RBHead<T>& head, T* elm) { |
| 456 | T *parent = nullptr, *tmp = nullptr; | 456 | T *parent = nullptr, *tmp = nullptr; |
| 457 | while ((parent = RB_PARENT(elm)) != nullptr && RB_IS_RED(parent)) { | 457 | while ((parent = RB_PARENT(elm)) != nullptr && RB_IS_RED(parent)) { |
| @@ -499,7 +499,7 @@ constexpr void RB_INSERT_COLOR(RBHead<T>& head, T* elm) { | |||
| 499 | } | 499 | } |
| 500 | 500 | ||
| 501 | template <typename T, typename Compare> | 501 | template <typename T, typename Compare> |
| 502 | requires HasRBEntry<T> | 502 | requires HasRBEntry<T> |
| 503 | constexpr T* RB_INSERT(RBHead<T>& head, T* elm, Compare cmp) { | 503 | constexpr T* RB_INSERT(RBHead<T>& head, T* elm, Compare cmp) { |
| 504 | T* parent = nullptr; | 504 | T* parent = nullptr; |
| 505 | T* tmp = head.Root(); | 505 | T* tmp = head.Root(); |
| @@ -534,7 +534,7 @@ constexpr T* RB_INSERT(RBHead<T>& head, T* elm, Compare cmp) { | |||
| 534 | } | 534 | } |
| 535 | 535 | ||
| 536 | template <typename T, typename Compare> | 536 | template <typename T, typename Compare> |
| 537 | requires HasRBEntry<T> | 537 | requires HasRBEntry<T> |
| 538 | constexpr T* RB_FIND(RBHead<T>& head, T* elm, Compare cmp) { | 538 | constexpr T* RB_FIND(RBHead<T>& head, T* elm, Compare cmp) { |
| 539 | T* tmp = head.Root(); | 539 | T* tmp = head.Root(); |
| 540 | 540 | ||
| @@ -553,7 +553,7 @@ constexpr T* RB_FIND(RBHead<T>& head, T* elm, Compare cmp) { | |||
| 553 | } | 553 | } |
| 554 | 554 | ||
| 555 | template <typename T, typename Compare> | 555 | template <typename T, typename Compare> |
| 556 | requires HasRBEntry<T> | 556 | requires HasRBEntry<T> |
| 557 | constexpr T* RB_NFIND(RBHead<T>& head, T* elm, Compare cmp) { | 557 | constexpr T* RB_NFIND(RBHead<T>& head, T* elm, Compare cmp) { |
| 558 | T* tmp = head.Root(); | 558 | T* tmp = head.Root(); |
| 559 | T* res = nullptr; | 559 | T* res = nullptr; |
| @@ -574,7 +574,7 @@ constexpr T* RB_NFIND(RBHead<T>& head, T* elm, Compare cmp) { | |||
| 574 | } | 574 | } |
| 575 | 575 | ||
| 576 | template <typename T, typename U, typename Compare> | 576 | template <typename T, typename U, typename Compare> |
| 577 | requires HasRBEntry<T> | 577 | requires HasRBEntry<T> |
| 578 | constexpr T* RB_FIND_KEY(RBHead<T>& head, const U& key, Compare cmp) { | 578 | constexpr T* RB_FIND_KEY(RBHead<T>& head, const U& key, Compare cmp) { |
| 579 | T* tmp = head.Root(); | 579 | T* tmp = head.Root(); |
| 580 | 580 | ||
| @@ -593,7 +593,7 @@ constexpr T* RB_FIND_KEY(RBHead<T>& head, const U& key, Compare cmp) { | |||
| 593 | } | 593 | } |
| 594 | 594 | ||
| 595 | template <typename T, typename U, typename Compare> | 595 | template <typename T, typename U, typename Compare> |
| 596 | requires HasRBEntry<T> | 596 | requires HasRBEntry<T> |
| 597 | constexpr T* RB_NFIND_KEY(RBHead<T>& head, const U& key, Compare cmp) { | 597 | constexpr T* RB_NFIND_KEY(RBHead<T>& head, const U& key, Compare cmp) { |
| 598 | T* tmp = head.Root(); | 598 | T* tmp = head.Root(); |
| 599 | T* res = nullptr; | 599 | T* res = nullptr; |
| @@ -614,7 +614,7 @@ constexpr T* RB_NFIND_KEY(RBHead<T>& head, const U& key, Compare cmp) { | |||
| 614 | } | 614 | } |
| 615 | 615 | ||
| 616 | template <typename T, typename Compare> | 616 | template <typename T, typename Compare> |
| 617 | requires HasRBEntry<T> | 617 | requires HasRBEntry<T> |
| 618 | constexpr T* RB_FIND_EXISTING(RBHead<T>& head, T* elm, Compare cmp) { | 618 | constexpr T* RB_FIND_EXISTING(RBHead<T>& head, T* elm, Compare cmp) { |
| 619 | T* tmp = head.Root(); | 619 | T* tmp = head.Root(); |
| 620 | 620 | ||
| @@ -631,7 +631,7 @@ constexpr T* RB_FIND_EXISTING(RBHead<T>& head, T* elm, Compare cmp) { | |||
| 631 | } | 631 | } |
| 632 | 632 | ||
| 633 | template <typename T, typename U, typename Compare> | 633 | template <typename T, typename U, typename Compare> |
| 634 | requires HasRBEntry<T> | 634 | requires HasRBEntry<T> |
| 635 | constexpr T* RB_FIND_EXISTING_KEY(RBHead<T>& head, const U& key, Compare cmp) { | 635 | constexpr T* RB_FIND_EXISTING_KEY(RBHead<T>& head, const U& key, Compare cmp) { |
| 636 | T* tmp = head.Root(); | 636 | T* tmp = head.Root(); |
| 637 | 637 | ||
| @@ -648,7 +648,7 @@ constexpr T* RB_FIND_EXISTING_KEY(RBHead<T>& head, const U& key, Compare cmp) { | |||
| 648 | } | 648 | } |
| 649 | 649 | ||
| 650 | template <typename T> | 650 | template <typename T> |
| 651 | requires HasRBEntry<T> | 651 | requires HasRBEntry<T> |
| 652 | constexpr T* RB_NEXT(T* elm) { | 652 | constexpr T* RB_NEXT(T* elm) { |
| 653 | if (RB_RIGHT(elm)) { | 653 | if (RB_RIGHT(elm)) { |
| 654 | elm = RB_RIGHT(elm); | 654 | elm = RB_RIGHT(elm); |
| @@ -669,7 +669,7 @@ constexpr T* RB_NEXT(T* elm) { | |||
| 669 | } | 669 | } |
| 670 | 670 | ||
| 671 | template <typename T> | 671 | template <typename T> |
| 672 | requires HasRBEntry<T> | 672 | requires HasRBEntry<T> |
| 673 | constexpr T* RB_PREV(T* elm) { | 673 | constexpr T* RB_PREV(T* elm) { |
| 674 | if (RB_LEFT(elm)) { | 674 | if (RB_LEFT(elm)) { |
| 675 | elm = RB_LEFT(elm); | 675 | elm = RB_LEFT(elm); |
| @@ -690,7 +690,7 @@ constexpr T* RB_PREV(T* elm) { | |||
| 690 | } | 690 | } |
| 691 | 691 | ||
| 692 | template <typename T> | 692 | template <typename T> |
| 693 | requires HasRBEntry<T> | 693 | requires HasRBEntry<T> |
| 694 | constexpr T* RB_MIN(RBHead<T>& head) { | 694 | constexpr T* RB_MIN(RBHead<T>& head) { |
| 695 | T* tmp = head.Root(); | 695 | T* tmp = head.Root(); |
| 696 | T* parent = nullptr; | 696 | T* parent = nullptr; |
| @@ -704,7 +704,7 @@ constexpr T* RB_MIN(RBHead<T>& head) { | |||
| 704 | } | 704 | } |
| 705 | 705 | ||
| 706 | template <typename T> | 706 | template <typename T> |
| 707 | requires HasRBEntry<T> | 707 | requires HasRBEntry<T> |
| 708 | constexpr T* RB_MAX(RBHead<T>& head) { | 708 | constexpr T* RB_MAX(RBHead<T>& head) { |
| 709 | T* tmp = head.Root(); | 709 | T* tmp = head.Root(); |
| 710 | T* parent = nullptr; | 710 | T* parent = nullptr; |
diff --git a/src/common/typed_address.h b/src/common/typed_address.h new file mode 100644 index 000000000..64f4a07c2 --- /dev/null +++ b/src/common/typed_address.h | |||
| @@ -0,0 +1,315 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <compare> | ||
| 7 | #include <type_traits> | ||
| 8 | #include <fmt/format.h> | ||
| 9 | |||
| 10 | #include "common/common_types.h" | ||
| 11 | |||
| 12 | namespace Common { | ||
| 13 | |||
| 14 | template <bool Virtual, typename T> | ||
| 15 | class TypedAddress { | ||
| 16 | public: | ||
| 17 | // Constructors. | ||
| 18 | constexpr inline TypedAddress() : m_address(0) {} | ||
| 19 | constexpr inline TypedAddress(uint64_t a) : m_address(a) {} | ||
| 20 | |||
| 21 | template <typename U> | ||
| 22 | constexpr inline explicit TypedAddress(const U* ptr) | ||
| 23 | : m_address(reinterpret_cast<uint64_t>(ptr)) {} | ||
| 24 | |||
| 25 | // Copy constructor. | ||
| 26 | constexpr inline TypedAddress(const TypedAddress& rhs) = default; | ||
| 27 | |||
| 28 | // Assignment operator. | ||
| 29 | constexpr inline TypedAddress& operator=(const TypedAddress& rhs) = default; | ||
| 30 | |||
| 31 | // Arithmetic operators. | ||
| 32 | template <typename I> | ||
| 33 | constexpr inline TypedAddress operator+(I rhs) const { | ||
| 34 | static_assert(std::is_integral_v<I>); | ||
| 35 | return m_address + rhs; | ||
| 36 | } | ||
| 37 | |||
| 38 | constexpr inline TypedAddress operator+(TypedAddress rhs) const { | ||
| 39 | return m_address + rhs.m_address; | ||
| 40 | } | ||
| 41 | |||
| 42 | constexpr inline TypedAddress operator++() { | ||
| 43 | return ++m_address; | ||
| 44 | } | ||
| 45 | |||
| 46 | constexpr inline TypedAddress operator++(int) { | ||
| 47 | return m_address++; | ||
| 48 | } | ||
| 49 | |||
| 50 | template <typename I> | ||
| 51 | constexpr inline TypedAddress operator-(I rhs) const { | ||
| 52 | static_assert(std::is_integral_v<I>); | ||
| 53 | return m_address - rhs; | ||
| 54 | } | ||
| 55 | |||
| 56 | constexpr inline ptrdiff_t operator-(TypedAddress rhs) const { | ||
| 57 | return m_address - rhs.m_address; | ||
| 58 | } | ||
| 59 | |||
| 60 | constexpr inline TypedAddress operator--() { | ||
| 61 | return --m_address; | ||
| 62 | } | ||
| 63 | |||
| 64 | constexpr inline TypedAddress operator--(int) { | ||
| 65 | return m_address--; | ||
| 66 | } | ||
| 67 | |||
| 68 | template <typename I> | ||
| 69 | constexpr inline TypedAddress operator+=(I rhs) { | ||
| 70 | static_assert(std::is_integral_v<I>); | ||
| 71 | m_address += rhs; | ||
| 72 | return *this; | ||
| 73 | } | ||
| 74 | |||
| 75 | template <typename I> | ||
| 76 | constexpr inline TypedAddress operator-=(I rhs) { | ||
| 77 | static_assert(std::is_integral_v<I>); | ||
| 78 | m_address -= rhs; | ||
| 79 | return *this; | ||
| 80 | } | ||
| 81 | |||
| 82 | // Logical operators. | ||
| 83 | constexpr inline uint64_t operator&(uint64_t mask) const { | ||
| 84 | return m_address & mask; | ||
| 85 | } | ||
| 86 | |||
| 87 | constexpr inline uint64_t operator|(uint64_t mask) const { | ||
| 88 | return m_address | mask; | ||
| 89 | } | ||
| 90 | |||
| 91 | template <typename I> | ||
| 92 | constexpr inline TypedAddress operator|=(I rhs) { | ||
| 93 | static_assert(std::is_integral_v<I>); | ||
| 94 | m_address |= rhs; | ||
| 95 | return *this; | ||
| 96 | } | ||
| 97 | |||
| 98 | constexpr inline uint64_t operator<<(int shift) const { | ||
| 99 | return m_address << shift; | ||
| 100 | } | ||
| 101 | |||
| 102 | constexpr inline uint64_t operator>>(int shift) const { | ||
| 103 | return m_address >> shift; | ||
| 104 | } | ||
| 105 | |||
| 106 | template <typename U> | ||
| 107 | constexpr inline size_t operator/(U size) const { | ||
| 108 | return m_address / size; | ||
| 109 | } | ||
| 110 | |||
| 111 | constexpr explicit operator bool() const { | ||
| 112 | return m_address != 0; | ||
| 113 | } | ||
| 114 | |||
| 115 | // constexpr inline uint64_t operator%(U align) const { return m_address % align; } | ||
| 116 | |||
| 117 | // Comparison operators. | ||
| 118 | constexpr bool operator==(const TypedAddress&) const = default; | ||
| 119 | constexpr auto operator<=>(const TypedAddress&) const = default; | ||
| 120 | |||
| 121 | // For convenience, also define comparison operators versus uint64_t. | ||
| 122 | constexpr inline bool operator==(uint64_t rhs) const { | ||
| 123 | return m_address == rhs; | ||
| 124 | } | ||
| 125 | |||
| 126 | // Allow getting the address explicitly, for use in accessors. | ||
| 127 | constexpr inline uint64_t GetValue() const { | ||
| 128 | return m_address; | ||
| 129 | } | ||
| 130 | |||
| 131 | private: | ||
| 132 | uint64_t m_address{}; | ||
| 133 | }; | ||
| 134 | |||
| 135 | struct PhysicalAddressTag {}; | ||
| 136 | struct VirtualAddressTag {}; | ||
| 137 | struct ProcessAddressTag {}; | ||
| 138 | |||
| 139 | using PhysicalAddress = TypedAddress<false, PhysicalAddressTag>; | ||
| 140 | using VirtualAddress = TypedAddress<true, VirtualAddressTag>; | ||
| 141 | using ProcessAddress = TypedAddress<true, ProcessAddressTag>; | ||
| 142 | |||
| 143 | // Define accessors. | ||
| 144 | template <typename T> | ||
| 145 | concept IsTypedAddress = std::same_as<T, PhysicalAddress> || std::same_as<T, VirtualAddress> || | ||
| 146 | std::same_as<T, ProcessAddress>; | ||
| 147 | |||
| 148 | template <typename T> | ||
| 149 | constexpr inline T Null = [] { | ||
| 150 | if constexpr (std::is_same<T, uint64_t>::value) { | ||
| 151 | return 0; | ||
| 152 | } else { | ||
| 153 | static_assert(std::is_same<T, PhysicalAddress>::value || | ||
| 154 | std::is_same<T, VirtualAddress>::value || | ||
| 155 | std::is_same<T, ProcessAddress>::value); | ||
| 156 | return T(0); | ||
| 157 | } | ||
| 158 | }(); | ||
| 159 | |||
| 160 | // Basic type validations. | ||
| 161 | static_assert(sizeof(PhysicalAddress) == sizeof(uint64_t)); | ||
| 162 | static_assert(sizeof(VirtualAddress) == sizeof(uint64_t)); | ||
| 163 | static_assert(sizeof(ProcessAddress) == sizeof(uint64_t)); | ||
| 164 | |||
| 165 | static_assert(std::is_trivially_copyable_v<PhysicalAddress>); | ||
| 166 | static_assert(std::is_trivially_copyable_v<VirtualAddress>); | ||
| 167 | static_assert(std::is_trivially_copyable_v<ProcessAddress>); | ||
| 168 | |||
| 169 | static_assert(std::is_trivially_copy_constructible_v<PhysicalAddress>); | ||
| 170 | static_assert(std::is_trivially_copy_constructible_v<VirtualAddress>); | ||
| 171 | static_assert(std::is_trivially_copy_constructible_v<ProcessAddress>); | ||
| 172 | |||
| 173 | static_assert(std::is_trivially_move_constructible_v<PhysicalAddress>); | ||
| 174 | static_assert(std::is_trivially_move_constructible_v<VirtualAddress>); | ||
| 175 | static_assert(std::is_trivially_move_constructible_v<ProcessAddress>); | ||
| 176 | |||
| 177 | static_assert(std::is_trivially_copy_assignable_v<PhysicalAddress>); | ||
| 178 | static_assert(std::is_trivially_copy_assignable_v<VirtualAddress>); | ||
| 179 | static_assert(std::is_trivially_copy_assignable_v<ProcessAddress>); | ||
| 180 | |||
| 181 | static_assert(std::is_trivially_move_assignable_v<PhysicalAddress>); | ||
| 182 | static_assert(std::is_trivially_move_assignable_v<VirtualAddress>); | ||
| 183 | static_assert(std::is_trivially_move_assignable_v<ProcessAddress>); | ||
| 184 | |||
| 185 | static_assert(std::is_trivially_destructible_v<PhysicalAddress>); | ||
| 186 | static_assert(std::is_trivially_destructible_v<VirtualAddress>); | ||
| 187 | static_assert(std::is_trivially_destructible_v<ProcessAddress>); | ||
| 188 | |||
| 189 | static_assert(Null<uint64_t> == 0); | ||
| 190 | static_assert(Null<PhysicalAddress> == Null<uint64_t>); | ||
| 191 | static_assert(Null<VirtualAddress> == Null<uint64_t>); | ||
| 192 | static_assert(Null<ProcessAddress> == Null<uint64_t>); | ||
| 193 | |||
| 194 | // Constructor/assignment validations. | ||
| 195 | static_assert([] { | ||
| 196 | const PhysicalAddress a(5); | ||
| 197 | PhysicalAddress b(a); | ||
| 198 | return b; | ||
| 199 | }() == PhysicalAddress(5)); | ||
| 200 | static_assert([] { | ||
| 201 | const PhysicalAddress a(5); | ||
| 202 | PhysicalAddress b(10); | ||
| 203 | b = a; | ||
| 204 | return b; | ||
| 205 | }() == PhysicalAddress(5)); | ||
| 206 | |||
| 207 | // Arithmetic validations. | ||
| 208 | static_assert(PhysicalAddress(10) + 5 == PhysicalAddress(15)); | ||
| 209 | static_assert(PhysicalAddress(10) - 5 == PhysicalAddress(5)); | ||
| 210 | static_assert([] { | ||
| 211 | PhysicalAddress v(10); | ||
| 212 | v += 5; | ||
| 213 | return v; | ||
| 214 | }() == PhysicalAddress(15)); | ||
| 215 | static_assert([] { | ||
| 216 | PhysicalAddress v(10); | ||
| 217 | v -= 5; | ||
| 218 | return v; | ||
| 219 | }() == PhysicalAddress(5)); | ||
| 220 | static_assert(PhysicalAddress(10)++ == PhysicalAddress(10)); | ||
| 221 | static_assert(++PhysicalAddress(10) == PhysicalAddress(11)); | ||
| 222 | static_assert(PhysicalAddress(10)-- == PhysicalAddress(10)); | ||
| 223 | static_assert(--PhysicalAddress(10) == PhysicalAddress(9)); | ||
| 224 | |||
| 225 | // Logical validations. | ||
| 226 | static_assert((PhysicalAddress(0b11111111) >> 1) == 0b01111111); | ||
| 227 | static_assert((PhysicalAddress(0b10101010) >> 1) == 0b01010101); | ||
| 228 | static_assert((PhysicalAddress(0b11111111) << 1) == 0b111111110); | ||
| 229 | static_assert((PhysicalAddress(0b01010101) << 1) == 0b10101010); | ||
| 230 | static_assert((PhysicalAddress(0b11111111) & 0b01010101) == 0b01010101); | ||
| 231 | static_assert((PhysicalAddress(0b11111111) & 0b10101010) == 0b10101010); | ||
| 232 | static_assert((PhysicalAddress(0b01010101) & 0b10101010) == 0b00000000); | ||
| 233 | static_assert((PhysicalAddress(0b00000000) | 0b01010101) == 0b01010101); | ||
| 234 | static_assert((PhysicalAddress(0b11111111) | 0b01010101) == 0b11111111); | ||
| 235 | static_assert((PhysicalAddress(0b10101010) | 0b01010101) == 0b11111111); | ||
| 236 | |||
| 237 | // Comparisons. | ||
| 238 | static_assert(PhysicalAddress(0) == PhysicalAddress(0)); | ||
| 239 | static_assert(PhysicalAddress(0) != PhysicalAddress(1)); | ||
| 240 | static_assert(PhysicalAddress(0) < PhysicalAddress(1)); | ||
| 241 | static_assert(PhysicalAddress(0) <= PhysicalAddress(1)); | ||
| 242 | static_assert(PhysicalAddress(1) > PhysicalAddress(0)); | ||
| 243 | static_assert(PhysicalAddress(1) >= PhysicalAddress(0)); | ||
| 244 | |||
| 245 | static_assert(!(PhysicalAddress(0) == PhysicalAddress(1))); | ||
| 246 | static_assert(!(PhysicalAddress(0) != PhysicalAddress(0))); | ||
| 247 | static_assert(!(PhysicalAddress(1) < PhysicalAddress(0))); | ||
| 248 | static_assert(!(PhysicalAddress(1) <= PhysicalAddress(0))); | ||
| 249 | static_assert(!(PhysicalAddress(0) > PhysicalAddress(1))); | ||
| 250 | static_assert(!(PhysicalAddress(0) >= PhysicalAddress(1))); | ||
| 251 | |||
| 252 | } // namespace Common | ||
| 253 | |||
| 254 | template <bool Virtual, typename T> | ||
| 255 | constexpr inline uint64_t GetInteger(Common::TypedAddress<Virtual, T> address) { | ||
| 256 | return address.GetValue(); | ||
| 257 | } | ||
| 258 | |||
| 259 | template <> | ||
| 260 | struct fmt::formatter<Common::PhysicalAddress> { | ||
| 261 | constexpr auto parse(fmt::format_parse_context& ctx) { | ||
| 262 | return ctx.begin(); | ||
| 263 | } | ||
| 264 | template <typename FormatContext> | ||
| 265 | auto format(const Common::PhysicalAddress& addr, FormatContext& ctx) { | ||
| 266 | return fmt::format_to(ctx.out(), "{:#x}", static_cast<u64>(addr.GetValue())); | ||
| 267 | } | ||
| 268 | }; | ||
| 269 | |||
| 270 | template <> | ||
| 271 | struct fmt::formatter<Common::ProcessAddress> { | ||
| 272 | constexpr auto parse(fmt::format_parse_context& ctx) { | ||
| 273 | return ctx.begin(); | ||
| 274 | } | ||
| 275 | template <typename FormatContext> | ||
| 276 | auto format(const Common::ProcessAddress& addr, FormatContext& ctx) { | ||
| 277 | return fmt::format_to(ctx.out(), "{:#x}", static_cast<u64>(addr.GetValue())); | ||
| 278 | } | ||
| 279 | }; | ||
| 280 | |||
| 281 | template <> | ||
| 282 | struct fmt::formatter<Common::VirtualAddress> { | ||
| 283 | constexpr auto parse(fmt::format_parse_context& ctx) { | ||
| 284 | return ctx.begin(); | ||
| 285 | } | ||
| 286 | template <typename FormatContext> | ||
| 287 | auto format(const Common::VirtualAddress& addr, FormatContext& ctx) { | ||
| 288 | return fmt::format_to(ctx.out(), "{:#x}", static_cast<u64>(addr.GetValue())); | ||
| 289 | } | ||
| 290 | }; | ||
| 291 | |||
| 292 | namespace std { | ||
| 293 | |||
| 294 | template <> | ||
| 295 | struct hash<Common::PhysicalAddress> { | ||
| 296 | size_t operator()(const Common::PhysicalAddress& k) const noexcept { | ||
| 297 | return k.GetValue(); | ||
| 298 | } | ||
| 299 | }; | ||
| 300 | |||
| 301 | template <> | ||
| 302 | struct hash<Common::ProcessAddress> { | ||
| 303 | size_t operator()(const Common::ProcessAddress& k) const noexcept { | ||
| 304 | return k.GetValue(); | ||
| 305 | } | ||
| 306 | }; | ||
| 307 | |||
| 308 | template <> | ||
| 309 | struct hash<Common::VirtualAddress> { | ||
| 310 | size_t operator()(const Common::VirtualAddress& k) const noexcept { | ||
| 311 | return k.GetValue(); | ||
| 312 | } | ||
| 313 | }; | ||
| 314 | |||
| 315 | } // namespace std | ||
diff --git a/src/common/uuid.cpp b/src/common/uuid.cpp index 89e1ed225..035df7fe0 100644 --- a/src/common/uuid.cpp +++ b/src/common/uuid.cpp | |||
| @@ -48,7 +48,7 @@ std::array<u8, 0x10> ConstructFromRawString(std::string_view raw_string) { | |||
| 48 | } | 48 | } |
| 49 | 49 | ||
| 50 | std::array<u8, 0x10> ConstructFromFormattedString(std::string_view formatted_string) { | 50 | std::array<u8, 0x10> ConstructFromFormattedString(std::string_view formatted_string) { |
| 51 | std::array<u8, 0x10> uuid; | 51 | std::array<u8, 0x10> uuid{}; |
| 52 | 52 | ||
| 53 | size_t i = 0; | 53 | size_t i = 0; |
| 54 | 54 | ||
diff --git a/src/common/vector_math.h b/src/common/vector_math.h index e62eeea2e..b4885835d 100644 --- a/src/common/vector_math.h +++ b/src/common/vector_math.h | |||
| @@ -259,6 +259,20 @@ public: | |||
| 259 | return *this; | 259 | return *this; |
| 260 | } | 260 | } |
| 261 | 261 | ||
| 262 | void RotateFromOrigin(float roll, float pitch, float yaw) { | ||
| 263 | float temp = y; | ||
| 264 | y = std::cos(roll) * y - std::sin(roll) * z; | ||
| 265 | z = std::sin(roll) * temp + std::cos(roll) * z; | ||
| 266 | |||
| 267 | temp = x; | ||
| 268 | x = std::cos(pitch) * x + std::sin(pitch) * z; | ||
| 269 | z = -std::sin(pitch) * temp + std::cos(pitch) * z; | ||
| 270 | |||
| 271 | temp = x; | ||
| 272 | x = std::cos(yaw) * x - std::sin(yaw) * y; | ||
| 273 | y = std::sin(yaw) * temp + std::cos(yaw) * y; | ||
| 274 | } | ||
| 275 | |||
| 262 | [[nodiscard]] constexpr T Length2() const { | 276 | [[nodiscard]] constexpr T Length2() const { |
| 263 | return x * x + y * y + z * z; | 277 | return x * x + y * y + z * z; |
| 264 | } | 278 | } |
| @@ -348,9 +362,7 @@ public: | |||
| 348 | // _DEFINE_SWIZZLER2 defines a single such function, DEFINE_SWIZZLER2 defines all of them for all | 362 | // _DEFINE_SWIZZLER2 defines a single such function, DEFINE_SWIZZLER2 defines all of them for all |
| 349 | // component names (x<->r) and permutations (xy<->yx) | 363 | // component names (x<->r) and permutations (xy<->yx) |
| 350 | #define _DEFINE_SWIZZLER2(a, b, name) \ | 364 | #define _DEFINE_SWIZZLER2(a, b, name) \ |
| 351 | [[nodiscard]] constexpr Vec2<T> name() const { \ | 365 | [[nodiscard]] constexpr Vec2<T> name() const { return Vec2<T>(a, b); } |
| 352 | return Vec2<T>(a, b); \ | ||
| 353 | } | ||
| 354 | #define DEFINE_SWIZZLER2(a, b, a2, b2, a3, b3, a4, b4) \ | 366 | #define DEFINE_SWIZZLER2(a, b, a2, b2, a3, b3, a4, b4) \ |
| 355 | _DEFINE_SWIZZLER2(a, b, a##b); \ | 367 | _DEFINE_SWIZZLER2(a, b, a##b); \ |
| 356 | _DEFINE_SWIZZLER2(a, b, a2##b2); \ | 368 | _DEFINE_SWIZZLER2(a, b, a2##b2); \ |
| @@ -543,9 +555,7 @@ public: | |||
| 543 | // DEFINE_SWIZZLER2_COMP2 defines two component functions for all component names (x<->r) and | 555 | // DEFINE_SWIZZLER2_COMP2 defines two component functions for all component names (x<->r) and |
| 544 | // permutations (xy<->yx) | 556 | // permutations (xy<->yx) |
| 545 | #define _DEFINE_SWIZZLER2(a, b, name) \ | 557 | #define _DEFINE_SWIZZLER2(a, b, name) \ |
| 546 | [[nodiscard]] constexpr Vec2<T> name() const { \ | 558 | [[nodiscard]] constexpr Vec2<T> name() const { return Vec2<T>(a, b); } |
| 547 | return Vec2<T>(a, b); \ | ||
| 548 | } | ||
| 549 | #define DEFINE_SWIZZLER2_COMP1(a, a2) \ | 559 | #define DEFINE_SWIZZLER2_COMP1(a, a2) \ |
| 550 | _DEFINE_SWIZZLER2(a, a, a##a); \ | 560 | _DEFINE_SWIZZLER2(a, a, a##a); \ |
| 551 | _DEFINE_SWIZZLER2(a, a, a2##a2) | 561 | _DEFINE_SWIZZLER2(a, a, a2##a2) |
| @@ -570,9 +580,7 @@ public: | |||
| 570 | #undef _DEFINE_SWIZZLER2 | 580 | #undef _DEFINE_SWIZZLER2 |
| 571 | 581 | ||
| 572 | #define _DEFINE_SWIZZLER3(a, b, c, name) \ | 582 | #define _DEFINE_SWIZZLER3(a, b, c, name) \ |
| 573 | [[nodiscard]] constexpr Vec3<T> name() const { \ | 583 | [[nodiscard]] constexpr Vec3<T> name() const { return Vec3<T>(a, b, c); } |
| 574 | return Vec3<T>(a, b, c); \ | ||
| 575 | } | ||
| 576 | #define DEFINE_SWIZZLER3_COMP1(a, a2) \ | 584 | #define DEFINE_SWIZZLER3_COMP1(a, a2) \ |
| 577 | _DEFINE_SWIZZLER3(a, a, a, a##a##a); \ | 585 | _DEFINE_SWIZZLER3(a, a, a, a##a##a); \ |
| 578 | _DEFINE_SWIZZLER3(a, a, a, a2##a2##a2) | 586 | _DEFINE_SWIZZLER3(a, a, a, a2##a2##a2) |
| @@ -641,8 +649,8 @@ template <typename T> | |||
| 641 | 649 | ||
| 642 | // linear interpolation via float: 0.0=begin, 1.0=end | 650 | // linear interpolation via float: 0.0=begin, 1.0=end |
| 643 | template <typename X> | 651 | template <typename X> |
| 644 | [[nodiscard]] constexpr decltype(X{} * float{} + X{} * float{}) | 652 | [[nodiscard]] constexpr decltype(X{} * float{} + X{} * float{}) Lerp(const X& begin, const X& end, |
| 645 | Lerp(const X& begin, const X& end, const float t) { | 653 | const float t) { |
| 646 | return begin * (1.f - t) + end * t; | 654 | return begin * (1.f - t) + end * t; |
| 647 | } | 655 | } |
| 648 | 656 | ||
diff --git a/src/common/wall_clock.cpp b/src/common/wall_clock.cpp index ae07f2811..dc0dcbd68 100644 --- a/src/common/wall_clock.cpp +++ b/src/common/wall_clock.cpp | |||
| @@ -1,96 +1,76 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #include "common/uint128.h" | 4 | #include "common/steady_clock.h" |
| 5 | #include "common/wall_clock.h" | 5 | #include "common/wall_clock.h" |
| 6 | 6 | ||
| 7 | #ifdef ARCHITECTURE_x86_64 | 7 | #ifdef ARCHITECTURE_x86_64 |
| 8 | #include "common/x64/cpu_detect.h" | 8 | #include "common/x64/cpu_detect.h" |
| 9 | #include "common/x64/native_clock.h" | 9 | #include "common/x64/native_clock.h" |
| 10 | #include "common/x64/rdtsc.h" | ||
| 10 | #endif | 11 | #endif |
| 11 | 12 | ||
| 12 | namespace Common { | 13 | namespace Common { |
| 13 | 14 | ||
| 14 | using base_timer = std::chrono::steady_clock; | ||
| 15 | using base_time_point = std::chrono::time_point<base_timer>; | ||
| 16 | |||
| 17 | class StandardWallClock final : public WallClock { | 15 | class StandardWallClock final : public WallClock { |
| 18 | public: | 16 | public: |
| 19 | explicit StandardWallClock(u64 emulated_cpu_frequency_, u64 emulated_clock_frequency_) | 17 | explicit StandardWallClock() : start_time{SteadyClock::Now()} {} |
| 20 | : WallClock(emulated_cpu_frequency_, emulated_clock_frequency_, false) { | 18 | |
| 21 | start_time = base_timer::now(); | 19 | std::chrono::nanoseconds GetTimeNS() const override { |
| 20 | return SteadyClock::Now() - start_time; | ||
| 21 | } | ||
| 22 | |||
| 23 | std::chrono::microseconds GetTimeUS() const override { | ||
| 24 | return static_cast<std::chrono::microseconds>(GetHostTicksElapsed() / NsToUsRatio::den); | ||
| 22 | } | 25 | } |
| 23 | 26 | ||
| 24 | std::chrono::nanoseconds GetTimeNS() override { | 27 | std::chrono::milliseconds GetTimeMS() const override { |
| 25 | base_time_point current = base_timer::now(); | 28 | return static_cast<std::chrono::milliseconds>(GetHostTicksElapsed() / NsToMsRatio::den); |
| 26 | auto elapsed = current - start_time; | ||
| 27 | return std::chrono::duration_cast<std::chrono::nanoseconds>(elapsed); | ||
| 28 | } | 29 | } |
| 29 | 30 | ||
| 30 | std::chrono::microseconds GetTimeUS() override { | 31 | u64 GetCNTPCT() const override { |
| 31 | base_time_point current = base_timer::now(); | 32 | return GetHostTicksElapsed() * NsToCNTPCTRatio::num / NsToCNTPCTRatio::den; |
| 32 | auto elapsed = current - start_time; | ||
| 33 | return std::chrono::duration_cast<std::chrono::microseconds>(elapsed); | ||
| 34 | } | 33 | } |
| 35 | 34 | ||
| 36 | std::chrono::milliseconds GetTimeMS() override { | 35 | u64 GetGPUTick() const override { |
| 37 | base_time_point current = base_timer::now(); | 36 | return GetHostTicksElapsed() * NsToGPUTickRatio::num / NsToGPUTickRatio::den; |
| 38 | auto elapsed = current - start_time; | ||
| 39 | return std::chrono::duration_cast<std::chrono::milliseconds>(elapsed); | ||
| 40 | } | 37 | } |
| 41 | 38 | ||
| 42 | u64 GetClockCycles() override { | 39 | u64 GetHostTicksNow() const override { |
| 43 | std::chrono::nanoseconds time_now = GetTimeNS(); | 40 | return static_cast<u64>(SteadyClock::Now().time_since_epoch().count()); |
| 44 | const u128 temporary = | ||
| 45 | Common::Multiply64Into128(time_now.count(), emulated_clock_frequency); | ||
| 46 | return Common::Divide128On32(temporary, 1000000000).first; | ||
| 47 | } | 41 | } |
| 48 | 42 | ||
| 49 | u64 GetCPUCycles() override { | 43 | u64 GetHostTicksElapsed() const override { |
| 50 | std::chrono::nanoseconds time_now = GetTimeNS(); | 44 | return static_cast<u64>(GetTimeNS().count()); |
| 51 | const u128 temporary = Common::Multiply64Into128(time_now.count(), emulated_cpu_frequency); | ||
| 52 | return Common::Divide128On32(temporary, 1000000000).first; | ||
| 53 | } | 45 | } |
| 54 | 46 | ||
| 55 | void Pause([[maybe_unused]] bool is_paused) override { | 47 | bool IsNative() const override { |
| 56 | // Do nothing in this clock type. | 48 | return false; |
| 57 | } | 49 | } |
| 58 | 50 | ||
| 59 | private: | 51 | private: |
| 60 | base_time_point start_time; | 52 | SteadyClock::time_point start_time; |
| 61 | }; | 53 | }; |
| 62 | 54 | ||
| 55 | std::unique_ptr<WallClock> CreateOptimalClock() { | ||
| 63 | #ifdef ARCHITECTURE_x86_64 | 56 | #ifdef ARCHITECTURE_x86_64 |
| 64 | |||
| 65 | std::unique_ptr<WallClock> CreateBestMatchingClock(u64 emulated_cpu_frequency, | ||
| 66 | u64 emulated_clock_frequency) { | ||
| 67 | const auto& caps = GetCPUCaps(); | 57 | const auto& caps = GetCPUCaps(); |
| 68 | u64 rtsc_frequency = 0; | ||
| 69 | if (caps.invariant_tsc) { | ||
| 70 | rtsc_frequency = caps.tsc_frequency ? caps.tsc_frequency : EstimateRDTSCFrequency(); | ||
| 71 | } | ||
| 72 | 58 | ||
| 73 | // Fallback to StandardWallClock if the hardware TSC does not have the precision greater than: | 59 | if (caps.invariant_tsc && caps.tsc_frequency >= WallClock::GPUTickFreq) { |
| 74 | // - A nanosecond | 60 | return std::make_unique<X64::NativeClock>(caps.tsc_frequency); |
| 75 | // - The emulated CPU frequency | ||
| 76 | // - The emulated clock counter frequency (CNTFRQ) | ||
| 77 | if (rtsc_frequency <= WallClock::NS_RATIO || rtsc_frequency <= emulated_cpu_frequency || | ||
| 78 | rtsc_frequency <= emulated_clock_frequency) { | ||
| 79 | return std::make_unique<StandardWallClock>(emulated_cpu_frequency, | ||
| 80 | emulated_clock_frequency); | ||
| 81 | } else { | 61 | } else { |
| 82 | return std::make_unique<X64::NativeClock>(emulated_cpu_frequency, emulated_clock_frequency, | 62 | // Fallback to StandardWallClock if the hardware TSC |
| 83 | rtsc_frequency); | 63 | // - Is not invariant |
| 64 | // - Is not more precise than GPUTickFreq | ||
| 65 | return std::make_unique<StandardWallClock>(); | ||
| 84 | } | 66 | } |
| 85 | } | ||
| 86 | |||
| 87 | #else | 67 | #else |
| 88 | 68 | return std::make_unique<StandardWallClock>(); | |
| 89 | std::unique_ptr<WallClock> CreateBestMatchingClock(u64 emulated_cpu_frequency, | 69 | #endif |
| 90 | u64 emulated_clock_frequency) { | ||
| 91 | return std::make_unique<StandardWallClock>(emulated_cpu_frequency, emulated_clock_frequency); | ||
| 92 | } | 70 | } |
| 93 | 71 | ||
| 94 | #endif | 72 | std::unique_ptr<WallClock> CreateStandardWallClock() { |
| 73 | return std::make_unique<StandardWallClock>(); | ||
| 74 | } | ||
| 95 | 75 | ||
| 96 | } // namespace Common | 76 | } // namespace Common |
diff --git a/src/common/wall_clock.h b/src/common/wall_clock.h index 828a523a8..f45d3d8c5 100644 --- a/src/common/wall_clock.h +++ b/src/common/wall_clock.h | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | 5 | ||
| 6 | #include <chrono> | 6 | #include <chrono> |
| 7 | #include <memory> | 7 | #include <memory> |
| 8 | #include <ratio> | ||
| 8 | 9 | ||
| 9 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 10 | 11 | ||
| @@ -12,47 +13,82 @@ namespace Common { | |||
| 12 | 13 | ||
| 13 | class WallClock { | 14 | class WallClock { |
| 14 | public: | 15 | public: |
| 15 | static constexpr u64 NS_RATIO = 1'000'000'000; | 16 | static constexpr u64 CNTFRQ = 19'200'000; // CNTPCT_EL0 Frequency = 19.2 MHz |
| 16 | static constexpr u64 US_RATIO = 1'000'000; | 17 | static constexpr u64 GPUTickFreq = 614'400'000; // GM20B GPU Tick Frequency = 614.4 MHz |
| 17 | static constexpr u64 MS_RATIO = 1'000; | 18 | static constexpr u64 CPUTickFreq = 1'020'000'000; // T210/4 A57 CPU Tick Frequency = 1020.0 MHz |
| 18 | 19 | ||
| 19 | virtual ~WallClock() = default; | 20 | virtual ~WallClock() = default; |
| 20 | 21 | ||
| 21 | /// Returns current wall time in nanoseconds | 22 | /// @returns The time in nanoseconds since the construction of this clock. |
| 22 | [[nodiscard]] virtual std::chrono::nanoseconds GetTimeNS() = 0; | 23 | virtual std::chrono::nanoseconds GetTimeNS() const = 0; |
| 23 | 24 | ||
| 24 | /// Returns current wall time in microseconds | 25 | /// @returns The time in microseconds since the construction of this clock. |
| 25 | [[nodiscard]] virtual std::chrono::microseconds GetTimeUS() = 0; | 26 | virtual std::chrono::microseconds GetTimeUS() const = 0; |
| 26 | 27 | ||
| 27 | /// Returns current wall time in milliseconds | 28 | /// @returns The time in milliseconds since the construction of this clock. |
| 28 | [[nodiscard]] virtual std::chrono::milliseconds GetTimeMS() = 0; | 29 | virtual std::chrono::milliseconds GetTimeMS() const = 0; |
| 29 | 30 | ||
| 30 | /// Returns current wall time in emulated clock cycles | 31 | /// @returns The guest CNTPCT ticks since the construction of this clock. |
| 31 | [[nodiscard]] virtual u64 GetClockCycles() = 0; | 32 | virtual u64 GetCNTPCT() const = 0; |
| 32 | 33 | ||
| 33 | /// Returns current wall time in emulated cpu cycles | 34 | /// @returns The guest GPU ticks since the construction of this clock. |
| 34 | [[nodiscard]] virtual u64 GetCPUCycles() = 0; | 35 | virtual u64 GetGPUTick() const = 0; |
| 35 | 36 | ||
| 36 | virtual void Pause(bool is_paused) = 0; | 37 | /// @returns The raw host timer ticks since an indeterminate epoch. |
| 38 | virtual u64 GetHostTicksNow() const = 0; | ||
| 37 | 39 | ||
| 38 | /// Tells if the wall clock, uses the host CPU's hardware clock | 40 | /// @returns The raw host timer ticks since the construction of this clock. |
| 39 | [[nodiscard]] bool IsNative() const { | 41 | virtual u64 GetHostTicksElapsed() const = 0; |
| 40 | return is_native; | 42 | |
| 43 | /// @returns Whether the clock directly uses the host's hardware clock. | ||
| 44 | virtual bool IsNative() const = 0; | ||
| 45 | |||
| 46 | static inline u64 NSToCNTPCT(u64 ns) { | ||
| 47 | return ns * NsToCNTPCTRatio::num / NsToCNTPCTRatio::den; | ||
| 48 | } | ||
| 49 | |||
| 50 | static inline u64 NSToGPUTick(u64 ns) { | ||
| 51 | return ns * NsToGPUTickRatio::num / NsToGPUTickRatio::den; | ||
| 52 | } | ||
| 53 | |||
| 54 | // Cycle Timing | ||
| 55 | |||
| 56 | static inline u64 CPUTickToNS(u64 cpu_tick) { | ||
| 57 | return cpu_tick * CPUTickToNsRatio::num / CPUTickToNsRatio::den; | ||
| 58 | } | ||
| 59 | |||
| 60 | static inline u64 CPUTickToUS(u64 cpu_tick) { | ||
| 61 | return cpu_tick * CPUTickToUsRatio::num / CPUTickToUsRatio::den; | ||
| 62 | } | ||
| 63 | |||
| 64 | static inline u64 CPUTickToCNTPCT(u64 cpu_tick) { | ||
| 65 | return cpu_tick * CPUTickToCNTPCTRatio::num / CPUTickToCNTPCTRatio::den; | ||
| 66 | } | ||
| 67 | |||
| 68 | static inline u64 CPUTickToGPUTick(u64 cpu_tick) { | ||
| 69 | return cpu_tick * CPUTickToGPUTickRatio::num / CPUTickToGPUTickRatio::den; | ||
| 41 | } | 70 | } |
| 42 | 71 | ||
| 43 | protected: | 72 | protected: |
| 44 | explicit WallClock(u64 emulated_cpu_frequency_, u64 emulated_clock_frequency_, bool is_native_) | 73 | using NsRatio = std::nano; |
| 45 | : emulated_cpu_frequency{emulated_cpu_frequency_}, | 74 | using UsRatio = std::micro; |
| 46 | emulated_clock_frequency{emulated_clock_frequency_}, is_native{is_native_} {} | 75 | using MsRatio = std::milli; |
| 76 | |||
| 77 | using NsToUsRatio = std::ratio_divide<std::nano, std::micro>; | ||
| 78 | using NsToMsRatio = std::ratio_divide<std::nano, std::milli>; | ||
| 79 | using NsToCNTPCTRatio = std::ratio<CNTFRQ, std::nano::den>; | ||
| 80 | using NsToGPUTickRatio = std::ratio<GPUTickFreq, std::nano::den>; | ||
| 47 | 81 | ||
| 48 | u64 emulated_cpu_frequency; | 82 | // Cycle Timing |
| 49 | u64 emulated_clock_frequency; | ||
| 50 | 83 | ||
| 51 | private: | 84 | using CPUTickToNsRatio = std::ratio<std::nano::den, CPUTickFreq>; |
| 52 | bool is_native; | 85 | using CPUTickToUsRatio = std::ratio<std::micro::den, CPUTickFreq>; |
| 86 | using CPUTickToCNTPCTRatio = std::ratio<CNTFRQ, CPUTickFreq>; | ||
| 87 | using CPUTickToGPUTickRatio = std::ratio<GPUTickFreq, CPUTickFreq>; | ||
| 53 | }; | 88 | }; |
| 54 | 89 | ||
| 55 | [[nodiscard]] std::unique_ptr<WallClock> CreateBestMatchingClock(u64 emulated_cpu_frequency, | 90 | std::unique_ptr<WallClock> CreateOptimalClock(); |
| 56 | u64 emulated_clock_frequency); | 91 | |
| 92 | std::unique_ptr<WallClock> CreateStandardWallClock(); | ||
| 57 | 93 | ||
| 58 | } // namespace Common | 94 | } // namespace Common |
diff --git a/src/common/windows/timer_resolution.cpp b/src/common/windows/timer_resolution.cpp new file mode 100644 index 000000000..29c6e5c7e --- /dev/null +++ b/src/common/windows/timer_resolution.cpp | |||
| @@ -0,0 +1,109 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include <windows.h> | ||
| 5 | |||
| 6 | #include "common/windows/timer_resolution.h" | ||
| 7 | |||
| 8 | extern "C" { | ||
| 9 | // http://undocumented.ntinternals.net/index.html?page=UserMode%2FUndocumented%20Functions%2FTime%2FNtQueryTimerResolution.html | ||
| 10 | NTSYSAPI LONG NTAPI NtQueryTimerResolution(PULONG MinimumResolution, PULONG MaximumResolution, | ||
| 11 | PULONG CurrentResolution); | ||
| 12 | |||
| 13 | // http://undocumented.ntinternals.net/index.html?page=UserMode%2FUndocumented%20Functions%2FTime%2FNtSetTimerResolution.html | ||
| 14 | NTSYSAPI LONG NTAPI NtSetTimerResolution(ULONG DesiredResolution, BOOLEAN SetResolution, | ||
| 15 | PULONG CurrentResolution); | ||
| 16 | |||
| 17 | // http://undocumented.ntinternals.net/index.html?page=UserMode%2FUndocumented%20Functions%2FNT%20Objects%2FThread%2FNtDelayExecution.html | ||
| 18 | NTSYSAPI LONG NTAPI NtDelayExecution(BOOLEAN Alertable, PLARGE_INTEGER DelayInterval); | ||
| 19 | } | ||
| 20 | |||
| 21 | // Defines for compatibility with older Windows 10 SDKs. | ||
| 22 | |||
| 23 | #ifndef PROCESS_POWER_THROTTLING_EXECUTION_SPEED | ||
| 24 | #define PROCESS_POWER_THROTTLING_EXECUTION_SPEED 0x1 | ||
| 25 | #endif | ||
| 26 | #ifndef PROCESS_POWER_THROTTLING_IGNORE_TIMER_RESOLUTION | ||
| 27 | #define PROCESS_POWER_THROTTLING_IGNORE_TIMER_RESOLUTION 0x4 | ||
| 28 | #endif | ||
| 29 | |||
| 30 | namespace Common::Windows { | ||
| 31 | |||
| 32 | namespace { | ||
| 33 | |||
| 34 | using namespace std::chrono; | ||
| 35 | |||
| 36 | constexpr nanoseconds ToNS(ULONG hundred_ns) { | ||
| 37 | return nanoseconds{hundred_ns * 100}; | ||
| 38 | } | ||
| 39 | |||
| 40 | constexpr ULONG ToHundredNS(nanoseconds ns) { | ||
| 41 | return static_cast<ULONG>(ns.count()) / 100; | ||
| 42 | } | ||
| 43 | |||
| 44 | struct TimerResolution { | ||
| 45 | std::chrono::nanoseconds minimum; | ||
| 46 | std::chrono::nanoseconds maximum; | ||
| 47 | std::chrono::nanoseconds current; | ||
| 48 | }; | ||
| 49 | |||
| 50 | TimerResolution GetTimerResolution() { | ||
| 51 | ULONG MinimumTimerResolution; | ||
| 52 | ULONG MaximumTimerResolution; | ||
| 53 | ULONG CurrentTimerResolution; | ||
| 54 | NtQueryTimerResolution(&MinimumTimerResolution, &MaximumTimerResolution, | ||
| 55 | &CurrentTimerResolution); | ||
| 56 | return { | ||
| 57 | .minimum{ToNS(MinimumTimerResolution)}, | ||
| 58 | .maximum{ToNS(MaximumTimerResolution)}, | ||
| 59 | .current{ToNS(CurrentTimerResolution)}, | ||
| 60 | }; | ||
| 61 | } | ||
| 62 | |||
| 63 | void SetHighQoS() { | ||
| 64 | // https://learn.microsoft.com/en-us/windows/win32/procthread/quality-of-service | ||
| 65 | PROCESS_POWER_THROTTLING_STATE PowerThrottling{ | ||
| 66 | .Version{PROCESS_POWER_THROTTLING_CURRENT_VERSION}, | ||
| 67 | .ControlMask{PROCESS_POWER_THROTTLING_EXECUTION_SPEED | | ||
| 68 | PROCESS_POWER_THROTTLING_IGNORE_TIMER_RESOLUTION}, | ||
| 69 | .StateMask{}, | ||
| 70 | }; | ||
| 71 | SetProcessInformation(GetCurrentProcess(), ProcessPowerThrottling, &PowerThrottling, | ||
| 72 | sizeof(PROCESS_POWER_THROTTLING_STATE)); | ||
| 73 | } | ||
| 74 | |||
| 75 | } // Anonymous namespace | ||
| 76 | |||
| 77 | nanoseconds GetMinimumTimerResolution() { | ||
| 78 | return GetTimerResolution().minimum; | ||
| 79 | } | ||
| 80 | |||
| 81 | nanoseconds GetMaximumTimerResolution() { | ||
| 82 | return GetTimerResolution().maximum; | ||
| 83 | } | ||
| 84 | |||
| 85 | nanoseconds GetCurrentTimerResolution() { | ||
| 86 | return GetTimerResolution().current; | ||
| 87 | } | ||
| 88 | |||
| 89 | nanoseconds SetCurrentTimerResolution(nanoseconds timer_resolution) { | ||
| 90 | // Set the timer resolution, and return the current timer resolution. | ||
| 91 | const auto DesiredTimerResolution = ToHundredNS(timer_resolution); | ||
| 92 | ULONG CurrentTimerResolution; | ||
| 93 | NtSetTimerResolution(DesiredTimerResolution, TRUE, &CurrentTimerResolution); | ||
| 94 | return ToNS(CurrentTimerResolution); | ||
| 95 | } | ||
| 96 | |||
| 97 | nanoseconds SetCurrentTimerResolutionToMaximum() { | ||
| 98 | SetHighQoS(); | ||
| 99 | return SetCurrentTimerResolution(GetMaximumTimerResolution()); | ||
| 100 | } | ||
| 101 | |||
| 102 | void SleepForOneTick() { | ||
| 103 | LARGE_INTEGER DelayInterval{ | ||
| 104 | .QuadPart{-1}, | ||
| 105 | }; | ||
| 106 | NtDelayExecution(FALSE, &DelayInterval); | ||
| 107 | } | ||
| 108 | |||
| 109 | } // namespace Common::Windows | ||
diff --git a/src/common/windows/timer_resolution.h b/src/common/windows/timer_resolution.h new file mode 100644 index 000000000..e1e50a62d --- /dev/null +++ b/src/common/windows/timer_resolution.h | |||
| @@ -0,0 +1,38 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <chrono> | ||
| 7 | |||
| 8 | namespace Common::Windows { | ||
| 9 | |||
| 10 | /// Returns the minimum (least precise) supported timer resolution in nanoseconds. | ||
| 11 | std::chrono::nanoseconds GetMinimumTimerResolution(); | ||
| 12 | |||
| 13 | /// Returns the maximum (most precise) supported timer resolution in nanoseconds. | ||
| 14 | std::chrono::nanoseconds GetMaximumTimerResolution(); | ||
| 15 | |||
| 16 | /// Returns the current timer resolution in nanoseconds. | ||
| 17 | std::chrono::nanoseconds GetCurrentTimerResolution(); | ||
| 18 | |||
| 19 | /** | ||
| 20 | * Sets the current timer resolution. | ||
| 21 | * | ||
| 22 | * @param timer_resolution Timer resolution in nanoseconds. | ||
| 23 | * | ||
| 24 | * @returns The current timer resolution. | ||
| 25 | */ | ||
| 26 | std::chrono::nanoseconds SetCurrentTimerResolution(std::chrono::nanoseconds timer_resolution); | ||
| 27 | |||
| 28 | /** | ||
| 29 | * Sets the current timer resolution to the maximum supported timer resolution. | ||
| 30 | * | ||
| 31 | * @returns The current timer resolution. | ||
| 32 | */ | ||
| 33 | std::chrono::nanoseconds SetCurrentTimerResolutionToMaximum(); | ||
| 34 | |||
| 35 | /// Sleep for one tick of the current timer resolution. | ||
| 36 | void SleepForOneTick(); | ||
| 37 | |||
| 38 | } // namespace Common::Windows | ||
diff --git a/src/common/x64/cpu_detect.cpp b/src/common/x64/cpu_detect.cpp index e54383a4a..c998b1197 100644 --- a/src/common/x64/cpu_detect.cpp +++ b/src/common/x64/cpu_detect.cpp | |||
| @@ -14,6 +14,7 @@ | |||
| 14 | #include "common/common_types.h" | 14 | #include "common/common_types.h" |
| 15 | #include "common/logging/log.h" | 15 | #include "common/logging/log.h" |
| 16 | #include "common/x64/cpu_detect.h" | 16 | #include "common/x64/cpu_detect.h" |
| 17 | #include "common/x64/rdtsc.h" | ||
| 17 | 18 | ||
| 18 | #ifdef _WIN32 | 19 | #ifdef _WIN32 |
| 19 | #include <windows.h> | 20 | #include <windows.h> |
| @@ -144,6 +145,7 @@ static CPUCaps Detect() { | |||
| 144 | caps.bmi2 = Common::Bit<8>(cpu_id[1]); | 145 | caps.bmi2 = Common::Bit<8>(cpu_id[1]); |
| 145 | caps.sha = Common::Bit<29>(cpu_id[1]); | 146 | caps.sha = Common::Bit<29>(cpu_id[1]); |
| 146 | 147 | ||
| 148 | caps.waitpkg = Common::Bit<5>(cpu_id[2]); | ||
| 147 | caps.gfni = Common::Bit<8>(cpu_id[2]); | 149 | caps.gfni = Common::Bit<8>(cpu_id[2]); |
| 148 | 150 | ||
| 149 | __cpuidex(cpu_id, 0x00000007, 0x00000001); | 151 | __cpuidex(cpu_id, 0x00000007, 0x00000001); |
| @@ -186,6 +188,8 @@ static CPUCaps Detect() { | |||
| 186 | caps.tsc_frequency = static_cast<u64>(caps.crystal_frequency) * | 188 | caps.tsc_frequency = static_cast<u64>(caps.crystal_frequency) * |
| 187 | caps.tsc_crystal_ratio_numerator / | 189 | caps.tsc_crystal_ratio_numerator / |
| 188 | caps.tsc_crystal_ratio_denominator; | 190 | caps.tsc_crystal_ratio_denominator; |
| 191 | } else { | ||
| 192 | caps.tsc_frequency = X64::EstimateRDTSCFrequency(); | ||
| 189 | } | 193 | } |
| 190 | } | 194 | } |
| 191 | 195 | ||
diff --git a/src/common/x64/cpu_detect.h b/src/common/x64/cpu_detect.h index ca8db19d6..8253944d6 100644 --- a/src/common/x64/cpu_detect.h +++ b/src/common/x64/cpu_detect.h | |||
| @@ -67,6 +67,7 @@ struct CPUCaps { | |||
| 67 | bool pclmulqdq : 1; | 67 | bool pclmulqdq : 1; |
| 68 | bool popcnt : 1; | 68 | bool popcnt : 1; |
| 69 | bool sha : 1; | 69 | bool sha : 1; |
| 70 | bool waitpkg : 1; | ||
| 70 | }; | 71 | }; |
| 71 | 72 | ||
| 72 | /** | 73 | /** |
diff --git a/src/common/x64/cpu_wait.cpp b/src/common/x64/cpu_wait.cpp new file mode 100644 index 000000000..c53dd4945 --- /dev/null +++ b/src/common/x64/cpu_wait.cpp | |||
| @@ -0,0 +1,51 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include <thread> | ||
| 5 | |||
| 6 | #ifdef _MSC_VER | ||
| 7 | #include <intrin.h> | ||
| 8 | #endif | ||
| 9 | |||
| 10 | #include "common/x64/cpu_detect.h" | ||
| 11 | #include "common/x64/cpu_wait.h" | ||
| 12 | #include "common/x64/rdtsc.h" | ||
| 13 | |||
| 14 | namespace Common::X64 { | ||
| 15 | |||
| 16 | #ifdef _MSC_VER | ||
| 17 | __forceinline static void TPAUSE() { | ||
| 18 | // 100,000 cycles is a reasonable amount of time to wait to save on CPU resources. | ||
| 19 | // For reference: | ||
| 20 | // At 1 GHz, 100K cycles is 100us | ||
| 21 | // At 2 GHz, 100K cycles is 50us | ||
| 22 | // At 4 GHz, 100K cycles is 25us | ||
| 23 | static constexpr auto PauseCycles = 100'000; | ||
| 24 | _tpause(0, FencedRDTSC() + PauseCycles); | ||
| 25 | } | ||
| 26 | #else | ||
| 27 | static void TPAUSE() { | ||
| 28 | // 100,000 cycles is a reasonable amount of time to wait to save on CPU resources. | ||
| 29 | // For reference: | ||
| 30 | // At 1 GHz, 100K cycles is 100us | ||
| 31 | // At 2 GHz, 100K cycles is 50us | ||
| 32 | // At 4 GHz, 100K cycles is 25us | ||
| 33 | static constexpr auto PauseCycles = 100'000; | ||
| 34 | const auto tsc = FencedRDTSC() + PauseCycles; | ||
| 35 | const auto eax = static_cast<u32>(tsc & 0xFFFFFFFF); | ||
| 36 | const auto edx = static_cast<u32>(tsc >> 32); | ||
| 37 | asm volatile("tpause %0" : : "r"(0), "d"(edx), "a"(eax)); | ||
| 38 | } | ||
| 39 | #endif | ||
| 40 | |||
| 41 | void MicroSleep() { | ||
| 42 | static const bool has_waitpkg = GetCPUCaps().waitpkg; | ||
| 43 | |||
| 44 | if (has_waitpkg) { | ||
| 45 | TPAUSE(); | ||
| 46 | } else { | ||
| 47 | std::this_thread::yield(); | ||
| 48 | } | ||
| 49 | } | ||
| 50 | |||
| 51 | } // namespace Common::X64 | ||
diff --git a/src/common/x64/cpu_wait.h b/src/common/x64/cpu_wait.h new file mode 100644 index 000000000..99d3757a7 --- /dev/null +++ b/src/common/x64/cpu_wait.h | |||
| @@ -0,0 +1,10 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | namespace Common::X64 { | ||
| 7 | |||
| 8 | void MicroSleep(); | ||
| 9 | |||
| 10 | } // namespace Common::X64 | ||
diff --git a/src/common/x64/native_clock.cpp b/src/common/x64/native_clock.cpp index 8b08332ab..7d2a26bd9 100644 --- a/src/common/x64/native_clock.cpp +++ b/src/common/x64/native_clock.cpp | |||
| @@ -1,136 +1,50 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #include <array> | ||
| 5 | #include <chrono> | ||
| 6 | #include <thread> | ||
| 7 | |||
| 8 | #include "common/atomic_ops.h" | ||
| 9 | #include "common/uint128.h" | 4 | #include "common/uint128.h" |
| 10 | #include "common/x64/native_clock.h" | 5 | #include "common/x64/native_clock.h" |
| 6 | #include "common/x64/rdtsc.h" | ||
| 11 | 7 | ||
| 12 | #ifdef _MSC_VER | 8 | namespace Common::X64 { |
| 13 | #include <intrin.h> | ||
| 14 | #endif | ||
| 15 | |||
| 16 | namespace Common { | ||
| 17 | |||
| 18 | #ifdef _MSC_VER | ||
| 19 | __forceinline static u64 FencedRDTSC() { | ||
| 20 | _mm_lfence(); | ||
| 21 | _ReadWriteBarrier(); | ||
| 22 | const u64 result = __rdtsc(); | ||
| 23 | _mm_lfence(); | ||
| 24 | _ReadWriteBarrier(); | ||
| 25 | return result; | ||
| 26 | } | ||
| 27 | #else | ||
| 28 | static u64 FencedRDTSC() { | ||
| 29 | u64 result; | ||
| 30 | asm volatile("lfence\n\t" | ||
| 31 | "rdtsc\n\t" | ||
| 32 | "shl $32, %%rdx\n\t" | ||
| 33 | "or %%rdx, %0\n\t" | ||
| 34 | "lfence" | ||
| 35 | : "=a"(result) | ||
| 36 | : | ||
| 37 | : "rdx", "memory", "cc"); | ||
| 38 | return result; | ||
| 39 | } | ||
| 40 | #endif | ||
| 41 | |||
| 42 | u64 EstimateRDTSCFrequency() { | ||
| 43 | // Discard the first result measuring the rdtsc. | ||
| 44 | FencedRDTSC(); | ||
| 45 | std::this_thread::sleep_for(std::chrono::milliseconds{1}); | ||
| 46 | FencedRDTSC(); | ||
| 47 | 9 | ||
| 48 | // Get the current time. | 10 | NativeClock::NativeClock(u64 rdtsc_frequency_) |
| 49 | const auto start_time = std::chrono::steady_clock::now(); | 11 | : start_ticks{FencedRDTSC()}, rdtsc_frequency{rdtsc_frequency_}, |
| 50 | const u64 tsc_start = FencedRDTSC(); | 12 | ns_rdtsc_factor{GetFixedPoint64Factor(NsRatio::den, rdtsc_frequency)}, |
| 51 | // Wait for 200 milliseconds. | 13 | us_rdtsc_factor{GetFixedPoint64Factor(UsRatio::den, rdtsc_frequency)}, |
| 52 | std::this_thread::sleep_for(std::chrono::milliseconds{200}); | 14 | ms_rdtsc_factor{GetFixedPoint64Factor(MsRatio::den, rdtsc_frequency)}, |
| 53 | const auto end_time = std::chrono::steady_clock::now(); | 15 | cntpct_rdtsc_factor{GetFixedPoint64Factor(CNTFRQ, rdtsc_frequency)}, |
| 54 | const u64 tsc_end = FencedRDTSC(); | 16 | gputick_rdtsc_factor{GetFixedPoint64Factor(GPUTickFreq, rdtsc_frequency)} {} |
| 55 | // Calculate differences. | ||
| 56 | const u64 timer_diff = static_cast<u64>( | ||
| 57 | std::chrono::duration_cast<std::chrono::nanoseconds>(end_time - start_time).count()); | ||
| 58 | const u64 tsc_diff = tsc_end - tsc_start; | ||
| 59 | const u64 tsc_freq = MultiplyAndDivide64(tsc_diff, 1000000000ULL, timer_diff); | ||
| 60 | return tsc_freq; | ||
| 61 | } | ||
| 62 | 17 | ||
| 63 | namespace X64 { | 18 | std::chrono::nanoseconds NativeClock::GetTimeNS() const { |
| 64 | NativeClock::NativeClock(u64 emulated_cpu_frequency_, u64 emulated_clock_frequency_, | 19 | return std::chrono::nanoseconds{MultiplyHigh(GetHostTicksElapsed(), ns_rdtsc_factor)}; |
| 65 | u64 rtsc_frequency_) | ||
| 66 | : WallClock(emulated_cpu_frequency_, emulated_clock_frequency_, true), rtsc_frequency{ | ||
| 67 | rtsc_frequency_} { | ||
| 68 | time_point.inner.last_measure = FencedRDTSC(); | ||
| 69 | time_point.inner.accumulated_ticks = 0U; | ||
| 70 | ns_rtsc_factor = GetFixedPoint64Factor(NS_RATIO, rtsc_frequency); | ||
| 71 | us_rtsc_factor = GetFixedPoint64Factor(US_RATIO, rtsc_frequency); | ||
| 72 | ms_rtsc_factor = GetFixedPoint64Factor(MS_RATIO, rtsc_frequency); | ||
| 73 | clock_rtsc_factor = GetFixedPoint64Factor(emulated_clock_frequency, rtsc_frequency); | ||
| 74 | cpu_rtsc_factor = GetFixedPoint64Factor(emulated_cpu_frequency, rtsc_frequency); | ||
| 75 | } | 20 | } |
| 76 | 21 | ||
| 77 | u64 NativeClock::GetRTSC() { | 22 | std::chrono::microseconds NativeClock::GetTimeUS() const { |
| 78 | TimePoint new_time_point{}; | 23 | return std::chrono::microseconds{MultiplyHigh(GetHostTicksElapsed(), us_rdtsc_factor)}; |
| 79 | TimePoint current_time_point{}; | ||
| 80 | |||
| 81 | current_time_point.pack = Common::AtomicLoad128(time_point.pack.data()); | ||
| 82 | do { | ||
| 83 | const u64 current_measure = FencedRDTSC(); | ||
| 84 | u64 diff = current_measure - current_time_point.inner.last_measure; | ||
| 85 | diff = diff & ~static_cast<u64>(static_cast<s64>(diff) >> 63); // max(diff, 0) | ||
| 86 | new_time_point.inner.last_measure = current_measure > current_time_point.inner.last_measure | ||
| 87 | ? current_measure | ||
| 88 | : current_time_point.inner.last_measure; | ||
| 89 | new_time_point.inner.accumulated_ticks = current_time_point.inner.accumulated_ticks + diff; | ||
| 90 | } while (!Common::AtomicCompareAndSwap(time_point.pack.data(), new_time_point.pack, | ||
| 91 | current_time_point.pack, current_time_point.pack)); | ||
| 92 | return new_time_point.inner.accumulated_ticks; | ||
| 93 | } | 24 | } |
| 94 | 25 | ||
| 95 | void NativeClock::Pause(bool is_paused) { | 26 | std::chrono::milliseconds NativeClock::GetTimeMS() const { |
| 96 | if (!is_paused) { | 27 | return std::chrono::milliseconds{MultiplyHigh(GetHostTicksElapsed(), ms_rdtsc_factor)}; |
| 97 | TimePoint current_time_point{}; | ||
| 98 | TimePoint new_time_point{}; | ||
| 99 | |||
| 100 | current_time_point.pack = Common::AtomicLoad128(time_point.pack.data()); | ||
| 101 | do { | ||
| 102 | new_time_point.pack = current_time_point.pack; | ||
| 103 | new_time_point.inner.last_measure = FencedRDTSC(); | ||
| 104 | } while (!Common::AtomicCompareAndSwap(time_point.pack.data(), new_time_point.pack, | ||
| 105 | current_time_point.pack, current_time_point.pack)); | ||
| 106 | } | ||
| 107 | } | 28 | } |
| 108 | 29 | ||
| 109 | std::chrono::nanoseconds NativeClock::GetTimeNS() { | 30 | u64 NativeClock::GetCNTPCT() const { |
| 110 | const u64 rtsc_value = GetRTSC(); | 31 | return MultiplyHigh(GetHostTicksElapsed(), cntpct_rdtsc_factor); |
| 111 | return std::chrono::nanoseconds{MultiplyHigh(rtsc_value, ns_rtsc_factor)}; | ||
| 112 | } | 32 | } |
| 113 | 33 | ||
| 114 | std::chrono::microseconds NativeClock::GetTimeUS() { | 34 | u64 NativeClock::GetGPUTick() const { |
| 115 | const u64 rtsc_value = GetRTSC(); | 35 | return MultiplyHigh(GetHostTicksElapsed(), gputick_rdtsc_factor); |
| 116 | return std::chrono::microseconds{MultiplyHigh(rtsc_value, us_rtsc_factor)}; | ||
| 117 | } | 36 | } |
| 118 | 37 | ||
| 119 | std::chrono::milliseconds NativeClock::GetTimeMS() { | 38 | u64 NativeClock::GetHostTicksNow() const { |
| 120 | const u64 rtsc_value = GetRTSC(); | 39 | return FencedRDTSC(); |
| 121 | return std::chrono::milliseconds{MultiplyHigh(rtsc_value, ms_rtsc_factor)}; | ||
| 122 | } | 40 | } |
| 123 | 41 | ||
| 124 | u64 NativeClock::GetClockCycles() { | 42 | u64 NativeClock::GetHostTicksElapsed() const { |
| 125 | const u64 rtsc_value = GetRTSC(); | 43 | return FencedRDTSC() - start_ticks; |
| 126 | return MultiplyHigh(rtsc_value, clock_rtsc_factor); | ||
| 127 | } | 44 | } |
| 128 | 45 | ||
| 129 | u64 NativeClock::GetCPUCycles() { | 46 | bool NativeClock::IsNative() const { |
| 130 | const u64 rtsc_value = GetRTSC(); | 47 | return true; |
| 131 | return MultiplyHigh(rtsc_value, cpu_rtsc_factor); | ||
| 132 | } | 48 | } |
| 133 | 49 | ||
| 134 | } // namespace X64 | 50 | } // namespace Common::X64 |
| 135 | |||
| 136 | } // namespace Common | ||
diff --git a/src/common/x64/native_clock.h b/src/common/x64/native_clock.h index 38ae7a462..334415eff 100644 --- a/src/common/x64/native_clock.h +++ b/src/common/x64/native_clock.h | |||
| @@ -5,51 +5,37 @@ | |||
| 5 | 5 | ||
| 6 | #include "common/wall_clock.h" | 6 | #include "common/wall_clock.h" |
| 7 | 7 | ||
| 8 | namespace Common { | 8 | namespace Common::X64 { |
| 9 | 9 | ||
| 10 | namespace X64 { | ||
| 11 | class NativeClock final : public WallClock { | 10 | class NativeClock final : public WallClock { |
| 12 | public: | 11 | public: |
| 13 | explicit NativeClock(u64 emulated_cpu_frequency_, u64 emulated_clock_frequency_, | 12 | explicit NativeClock(u64 rdtsc_frequency_); |
| 14 | u64 rtsc_frequency_); | ||
| 15 | 13 | ||
| 16 | std::chrono::nanoseconds GetTimeNS() override; | 14 | std::chrono::nanoseconds GetTimeNS() const override; |
| 17 | 15 | ||
| 18 | std::chrono::microseconds GetTimeUS() override; | 16 | std::chrono::microseconds GetTimeUS() const override; |
| 19 | 17 | ||
| 20 | std::chrono::milliseconds GetTimeMS() override; | 18 | std::chrono::milliseconds GetTimeMS() const override; |
| 21 | 19 | ||
| 22 | u64 GetClockCycles() override; | 20 | u64 GetCNTPCT() const override; |
| 23 | 21 | ||
| 24 | u64 GetCPUCycles() override; | 22 | u64 GetGPUTick() const override; |
| 25 | 23 | ||
| 26 | void Pause(bool is_paused) override; | 24 | u64 GetHostTicksNow() const override; |
| 25 | |||
| 26 | u64 GetHostTicksElapsed() const override; | ||
| 27 | |||
| 28 | bool IsNative() const override; | ||
| 27 | 29 | ||
| 28 | private: | 30 | private: |
| 29 | u64 GetRTSC(); | 31 | u64 start_ticks; |
| 30 | 32 | u64 rdtsc_frequency; | |
| 31 | union alignas(16) TimePoint { | 33 | |
| 32 | TimePoint() : pack{} {} | 34 | u64 ns_rdtsc_factor; |
| 33 | u128 pack{}; | 35 | u64 us_rdtsc_factor; |
| 34 | struct Inner { | 36 | u64 ms_rdtsc_factor; |
| 35 | u64 last_measure{}; | 37 | u64 cntpct_rdtsc_factor; |
| 36 | u64 accumulated_ticks{}; | 38 | u64 gputick_rdtsc_factor; |
| 37 | } inner; | ||
| 38 | }; | ||
| 39 | |||
| 40 | TimePoint time_point; | ||
| 41 | |||
| 42 | // factors | ||
| 43 | u64 clock_rtsc_factor{}; | ||
| 44 | u64 cpu_rtsc_factor{}; | ||
| 45 | u64 ns_rtsc_factor{}; | ||
| 46 | u64 us_rtsc_factor{}; | ||
| 47 | u64 ms_rtsc_factor{}; | ||
| 48 | |||
| 49 | u64 rtsc_frequency; | ||
| 50 | }; | 39 | }; |
| 51 | } // namespace X64 | ||
| 52 | |||
| 53 | u64 EstimateRDTSCFrequency(); | ||
| 54 | 40 | ||
| 55 | } // namespace Common | 41 | } // namespace Common::X64 |
diff --git a/src/common/x64/rdtsc.cpp b/src/common/x64/rdtsc.cpp new file mode 100644 index 000000000..9273274a3 --- /dev/null +++ b/src/common/x64/rdtsc.cpp | |||
| @@ -0,0 +1,39 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include <thread> | ||
| 5 | |||
| 6 | #include "common/steady_clock.h" | ||
| 7 | #include "common/uint128.h" | ||
| 8 | #include "common/x64/rdtsc.h" | ||
| 9 | |||
| 10 | namespace Common::X64 { | ||
| 11 | |||
| 12 | template <u64 Nearest> | ||
| 13 | static u64 RoundToNearest(u64 value) { | ||
| 14 | const auto mod = value % Nearest; | ||
| 15 | return mod >= (Nearest / 2) ? (value - mod + Nearest) : (value - mod); | ||
| 16 | } | ||
| 17 | |||
| 18 | u64 EstimateRDTSCFrequency() { | ||
| 19 | // Discard the first result measuring the rdtsc. | ||
| 20 | FencedRDTSC(); | ||
| 21 | std::this_thread::sleep_for(std::chrono::milliseconds{1}); | ||
| 22 | FencedRDTSC(); | ||
| 23 | |||
| 24 | // Get the current time. | ||
| 25 | const auto start_time = RealTimeClock::Now(); | ||
| 26 | const u64 tsc_start = FencedRDTSC(); | ||
| 27 | // Wait for 100 milliseconds. | ||
| 28 | std::this_thread::sleep_for(std::chrono::milliseconds{100}); | ||
| 29 | const auto end_time = RealTimeClock::Now(); | ||
| 30 | const u64 tsc_end = FencedRDTSC(); | ||
| 31 | // Calculate differences. | ||
| 32 | const u64 timer_diff = static_cast<u64>( | ||
| 33 | std::chrono::duration_cast<std::chrono::nanoseconds>(end_time - start_time).count()); | ||
| 34 | const u64 tsc_diff = tsc_end - tsc_start; | ||
| 35 | const u64 tsc_freq = MultiplyAndDivide64(tsc_diff, 1000000000ULL, timer_diff); | ||
| 36 | return RoundToNearest<100'000>(tsc_freq); | ||
| 37 | } | ||
| 38 | |||
| 39 | } // namespace Common::X64 | ||
diff --git a/src/common/x64/rdtsc.h b/src/common/x64/rdtsc.h new file mode 100644 index 000000000..0ec4f52f9 --- /dev/null +++ b/src/common/x64/rdtsc.h | |||
| @@ -0,0 +1,37 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #ifdef _MSC_VER | ||
| 7 | #include <intrin.h> | ||
| 8 | #endif | ||
| 9 | |||
| 10 | #include "common/common_types.h" | ||
| 11 | |||
| 12 | namespace Common::X64 { | ||
| 13 | |||
| 14 | #ifdef _MSC_VER | ||
| 15 | __forceinline static u64 FencedRDTSC() { | ||
| 16 | _mm_lfence(); | ||
| 17 | _ReadWriteBarrier(); | ||
| 18 | const u64 result = __rdtsc(); | ||
| 19 | _mm_lfence(); | ||
| 20 | _ReadWriteBarrier(); | ||
| 21 | return result; | ||
| 22 | } | ||
| 23 | #else | ||
| 24 | static inline u64 FencedRDTSC() { | ||
| 25 | u64 eax; | ||
| 26 | u64 edx; | ||
| 27 | asm volatile("lfence\n\t" | ||
| 28 | "rdtsc\n\t" | ||
| 29 | "lfence\n\t" | ||
| 30 | : "=a"(eax), "=d"(edx)); | ||
| 31 | return (edx << 32) | eax; | ||
| 32 | } | ||
| 33 | #endif | ||
| 34 | |||
| 35 | u64 EstimateRDTSCFrequency(); | ||
| 36 | |||
| 37 | } // namespace Common::X64 | ||
diff --git a/src/common/zstd_compression.cpp b/src/common/zstd_compression.cpp index b71a41b78..cb6ec171b 100644 --- a/src/common/zstd_compression.cpp +++ b/src/common/zstd_compression.cpp | |||
| @@ -33,7 +33,7 @@ std::vector<u8> CompressDataZSTDDefault(const u8* source, std::size_t source_siz | |||
| 33 | 33 | ||
| 34 | std::vector<u8> DecompressDataZSTD(std::span<const u8> compressed) { | 34 | std::vector<u8> DecompressDataZSTD(std::span<const u8> compressed) { |
| 35 | const std::size_t decompressed_size = | 35 | const std::size_t decompressed_size = |
| 36 | ZSTD_getDecompressedSize(compressed.data(), compressed.size()); | 36 | ZSTD_getFrameContentSize(compressed.data(), compressed.size()); |
| 37 | std::vector<u8> decompressed(decompressed_size); | 37 | std::vector<u8> decompressed(decompressed_size); |
| 38 | 38 | ||
| 39 | const std::size_t uncompressed_result_size = ZSTD_decompress( | 39 | const std::size_t uncompressed_result_size = ZSTD_decompress( |