summaryrefslogtreecommitdiff
path: root/src/common
diff options
context:
space:
mode:
Diffstat (limited to 'src/common')
-rw-r--r--src/common/CMakeLists.txt34
-rw-r--r--src/common/address_space.h7
-rw-r--r--src/common/address_space.inc4
-rw-r--r--src/common/alignment.h18
-rw-r--r--src/common/announce_multiplayer_room.h2
-rw-r--r--src/common/atomic_helpers.h2
-rw-r--r--src/common/bit_cast.h20
-rw-r--r--src/common/bit_field.h5
-rw-r--r--src/common/bit_util.h6
-rw-r--r--src/common/bounded_threadsafe_queue.h319
-rw-r--r--src/common/concepts.h6
-rw-r--r--src/common/container_hash.h92
-rw-r--r--src/common/demangle.cpp6
-rw-r--r--src/common/div_ceil.h4
-rw-r--r--src/common/dynamic_library.cpp2
-rw-r--r--src/common/dynamic_library.h3
-rw-r--r--src/common/error.cpp3
-rw-r--r--src/common/expected.h60
-rw-r--r--src/common/fiber.cpp2
-rw-r--r--src/common/fixed_point.h2
-rw-r--r--src/common/fs/file.cpp38
-rw-r--r--src/common/fs/fs.cpp41
-rw-r--r--src/common/fs/fs_android.cpp98
-rw-r--r--src/common/fs/fs_android.h65
-rw-r--r--src/common/fs/fs_paths.h1
-rw-r--r--src/common/fs/fs_types.h2
-rw-r--r--src/common/fs/path_util.cpp27
-rw-r--r--src/common/fs/path_util.h9
-rw-r--r--src/common/host_memory.cpp38
-rw-r--r--src/common/input.h125
-rw-r--r--src/common/intrusive_list.h631
-rw-r--r--src/common/intrusive_red_black_tree.h28
-rw-r--r--src/common/logging/backend.cpp42
-rw-r--r--src/common/logging/filter.cpp2
-rw-r--r--src/common/logging/text_formatter.cpp35
-rw-r--r--src/common/logging/text_formatter.h2
-rw-r--r--src/common/logging/types.h202
-rw-r--r--src/common/make_unique_for_overwrite.h8
-rw-r--r--src/common/overflow.h22
-rw-r--r--src/common/polyfill_ranges.h8
-rw-r--r--src/common/polyfill_thread.h123
-rw-r--r--src/common/range_map.h6
-rw-r--r--src/common/ring_buffer.h3
-rw-r--r--src/common/scratch_buffer.h19
-rw-r--r--src/common/settings.cpp58
-rw-r--r--src/common/settings.h64
-rw-r--r--src/common/steady_clock.cpp80
-rw-r--r--src/common/steady_clock.h34
-rw-r--r--src/common/string_util.cpp16
-rw-r--r--src/common/string_util.h11
-rw-r--r--src/common/swap.h12
-rw-r--r--src/common/telemetry.cpp1
-rw-r--r--src/common/thread.h2
-rw-r--r--src/common/time_zone.cpp63
-rw-r--r--src/common/time_zone.h6
-rw-r--r--src/common/tree.h74
-rw-r--r--src/common/typed_address.h315
-rw-r--r--src/common/uuid.cpp2
-rw-r--r--src/common/vector_math.h30
-rw-r--r--src/common/wall_clock.cpp90
-rw-r--r--src/common/wall_clock.h88
-rw-r--r--src/common/windows/timer_resolution.cpp109
-rw-r--r--src/common/windows/timer_resolution.h38
-rw-r--r--src/common/x64/cpu_detect.cpp4
-rw-r--r--src/common/x64/cpu_detect.h1
-rw-r--r--src/common/x64/cpu_wait.cpp51
-rw-r--r--src/common/x64/cpu_wait.h10
-rw-r--r--src/common/x64/native_clock.cpp138
-rw-r--r--src/common/x64/native_clock.h56
-rw-r--r--src/common/x64/rdtsc.cpp39
-rw-r--r--src/common/x64/rdtsc.h37
-rw-r--r--src/common/zstd_compression.cpp2
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
150if (WIN32)
151 target_sources(common PRIVATE
152 windows/timer_resolution.cpp
153 windows/timer_resolution.h
154 )
155 target_link_libraries(common PRIVATE ntdll)
156endif()
157
158if(ANDROID)
159 target_sources(common
160 PRIVATE
161 fs/fs_android.cpp
162 fs/fs_android.h
163 )
164endif()
165
145if(ARCHITECTURE_x86_64) 166if(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
177create_target_directory_groups(common) 202create_target_directory_groups(common)
178 203
179target_link_libraries(common PUBLIC ${Boost_LIBRARIES} fmt::fmt microprofile Threads::Threads) 204target_link_libraries(common PUBLIC Boost::context Boost::headers fmt::fmt microprofile Threads::Threads)
180target_link_libraries(common PRIVATE lz4::lz4 zstd::zstd demangle) 205target_link_libraries(common PRIVATE lz4::lz4 zstd::zstd LLVM::Demangle)
206
207if (ANDROID)
208 # For ASharedMemory_create
209 target_link_libraries(common PRIVATE android)
210endif()
181 211
182if (YUZU_USE_PRECOMPILED_HEADERS) 212if (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
13namespace Common { 13namespace Common {
14template <typename VaType, size_t AddressSpaceBits> 14template <typename VaType, size_t AddressSpaceBits>
15concept AddressSpaceValid = std::is_unsigned_v<VaType> && sizeof(VaType) * 8 >= AddressSpaceBits; 15concept AddressSpaceValid = std::is_unsigned_v<VaType> && sizeof(VaType) * 8 >=
16AddressSpaceBits;
16 17
17struct EmptyStruct {}; 18struct EmptyStruct {};
18 19
@@ -21,7 +22,7 @@ struct EmptyStruct {};
21 */ 22 */
22template <typename VaType, VaType UnmappedVa, typename PaType, PaType UnmappedPa, 23template <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>
24requires AddressSpaceValid<VaType, AddressSpaceBits> 25 requires AddressSpaceValid<VaType, AddressSpaceBits>
25class FlatAddressSpaceMap { 26class FlatAddressSpaceMap {
26public: 27public:
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 */
111template <typename VaType, VaType UnmappedVa, size_t AddressSpaceBits> 112template <typename VaType, VaType UnmappedVa, size_t AddressSpaceBits>
112requires AddressSpaceValid<VaType, AddressSpaceBits> 113 requires AddressSpaceValid<VaType, AddressSpaceBits>
113class FlatAllocator 114class FlatAllocator
114 : public FlatAddressSpaceMap<VaType, UnmappedVa, bool, false, false, AddressSpaceBits> { 115 : public FlatAddressSpaceMap<VaType, UnmappedVa, bool, false, false, AddressSpaceBits> {
115private: 116private:
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 @@
10namespace Common { 10namespace Common {
11 11
12template <typename T> 12template <typename T>
13requires 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
20template <typename T> 20template <typename T>
21requires 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
26template <typename T> 26template <typename T>
27requires 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
32template <typename T> 32template <typename T>
33requires 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
38template <typename T> 38template <typename T>
39requires 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
44template <typename T> 44template <typename T>
45requires 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
52template <typename T, typename U> 52template <typename T, typename U>
53requires 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
9namespace Common { 12namespace Common {
10 13
11template <typename To, typename From> 14template <typename To, typename From>
12[[nodiscard]] std::enable_if_t<sizeof(To) == sizeof(From) && std::is_trivially_copyable_v<From> && 15constexpr 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);
15BitCast(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
189template <std::size_t Position, std::size_t Bits, typename T> 189template <std::size_t Position, std::size_t Bits, typename T>
190using BitFieldBE = BitField<Position, Bits, T, BETag>; 190using BitFieldBE = BitField<Position, Bits, T, BETag>;
191
192template <std::size_t Position, std::size_t Bits, typename T, typename EndianTag = LETag>
193inline 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
47template <typename T> 47template <typename T>
48requires 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
53template <typename T> 53template <typename T>
54requires 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
59template <size_t bit_index, typename T> 59template <size_t bit_index, typename T>
60requires 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
16namespace Common { 14namespace Common {
17 15
18#if defined(__cpp_lib_hardware_interference_size) 16namespace detail {
19constexpr size_t hardware_interference_size = std::hardware_destructive_interference_size; 17constexpr size_t DefaultCapacity = 0x1000;
20#else 18} // namespace detail
21constexpr size_t hardware_interference_size = 64; 19
22#endif 20template <typename T, size_t Capacity = detail::DefaultCapacity>
21class SPSCQueue {
22 static_assert((Capacity & (Capacity - 1)) == 0, "Capacity must be a power of two.");
23 23
24template <typename T, size_t capacity = 0x400>
25class MPSCQueue {
26public: 24public:
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
89private: 59private:
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
163template <typename T, size_t Capacity = detail::DefaultCapacity>
164class MPSCQueue {
165public:
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>, 198private:
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"); 203template <typename T, size_t Capacity = detail::DefaultCapacity>
204class MPMCQueue {
205public:
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
243private:
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.
17template <typename Derived, typename Base> 17template <typename Derived, typename Base>
18concept DerivedFrom = requires { 18concept 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.
24template <typename From, typename To> 24template <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
12namespace Common {
13
14namespace detail {
15
16template <typename T>
17 requires std::is_unsigned_v<T>
18inline 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
34template <size_t Bits>
35struct 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
43template <>
44struct 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
66template <typename T>
67inline 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
71template <typename It>
72inline 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
82template <typename T, size_t Size>
83std::size_t HashValue(const std::array<T, Size>& v) {
84 return HashRange(v.cbegin(), v.cend());
85}
86
87template <typename T, typename Allocator>
88std::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
7namespace llvm {
8char* itaniumDemangle(const char* mangled_name, char* buf, size_t* n, int* status);
9}
10
11namespace Common { 9namespace Common {
12 10
13std::string DemangleSymbol(const std::string& mangled) { 11std::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.
12template <typename N, typename D> 12template <typename N, typename D>
13requires 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
19template <typename N, typename D> 19template <typename N, typename D>
20requires 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
25DynamicLibrary::DynamicLibrary(void* handle_) : handle{handle_} {}
26
25DynamicLibrary::DynamicLibrary(DynamicLibrary&& rhs) noexcept 27DynamicLibrary::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 */
66template <typename T, typename E, bool = std::is_trivially_destructible_v<T>> 66template <typename T, typename E, bool = std::is_trivially_destructible_v<T>>
67requires std::is_trivially_destructible_v<E> 67 requires std::is_trivially_destructible_v<E>
68struct expected_storage_base { 68struct 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 */
113template <typename T, typename E> 113template <typename T, typename E>
114requires std::is_trivially_destructible_v<E> 114 requires std::is_trivially_destructible_v<E>
115struct expected_storage_base<T, E, true> { 115struct 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 */
253template <typename T, typename E, bool = std::is_trivially_copy_constructible_v<T>> 253template <typename T, typename E, bool = std::is_trivially_copy_constructible_v<T>>
254requires std::is_trivially_copy_constructible_v<E> 254 requires std::is_trivially_copy_constructible_v<E>
255struct expected_copy_base : expected_operations_base<T, E> { 255struct 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 */
263template <typename T, typename E> 263template <typename T, typename E>
264requires std::is_trivially_copy_constructible_v<E> 264 requires std::is_trivially_copy_constructible_v<E>
265struct expected_copy_base<T, E, false> : expected_operations_base<T, E> { 265struct 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 */
291template <typename T, typename E, bool = std::is_trivially_move_constructible_v<T>> 291template <typename T, typename E, bool = std::is_trivially_move_constructible_v<T>>
292requires std::is_trivially_move_constructible_v<E> 292 requires std::is_trivially_move_constructible_v<E>
293struct expected_move_base : expected_copy_base<T, E> { 293struct 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 */
301template <typename T, typename E> 301template <typename T, typename E>
302requires std::is_trivially_move_constructible_v<E> 302 requires std::is_trivially_move_constructible_v<E>
303struct expected_move_base<T, E, false> : expected_copy_base<T, E> { 303struct 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>>>
333requires 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>>
336struct expected_copy_assign_base : expected_move_base<T, E> { 336struct 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 */
344template <typename T, typename E> 344template <typename T, typename E>
345requires 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>>
348struct expected_copy_assign_base<T, E, false> : expected_move_base<T, E> { 348struct 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>>>
374requires 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>>
377struct expected_move_assign_base : expected_copy_assign_base<T, E> { 377struct 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 */
385template <typename T, typename E> 385template <typename T, typename E>
386requires 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>>
389struct expected_move_assign_base<T, E, false> : expected_copy_assign_base<T, E> { 389struct 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 */
413template <typename T, typename E, bool EnableCopy = std::is_copy_constructible_v<T>, 413template <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>>
415requires 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>>
416struct expected_delete_ctor_base { 416struct 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
424template <typename T, typename E> 424template <typename T, typename E>
425requires 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>>
426struct expected_delete_ctor_base<T, E, true, false> { 426struct 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
434template <typename T, typename E> 434template <typename T, typename E>
435requires 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>>
436struct expected_delete_ctor_base<T, E, false, true> { 436struct 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
444template <typename T, typename E> 444template <typename T, typename E>
445requires 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>>
446struct expected_delete_ctor_base<T, E, false, false> { 446struct 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>>>
463requires 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>>
465struct expected_delete_assign_base { 465struct 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
473template <typename T, typename E> 473template <typename T, typename E>
474requires 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>>
476struct expected_delete_assign_base<T, E, true, false> { 476struct 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
484template <typename T, typename E> 484template <typename T, typename E>
485requires 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>>
487struct expected_delete_assign_base<T, E, false, true> { 487struct 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
495template <typename T, typename E> 495template <typename T, typename E>
496requires 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>>
498struct expected_delete_assign_base<T, E, false, false> { 498struct 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
92void Fiber::Exit() { 92void 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;
22namespace detail { 22namespace 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
28template <size_t T> 28template <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
527bool Exists(const fs::path& path) { 530bool 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
531bool IsFile(const fs::path& path) { 542bool 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
535bool IsDir(const fs::path& path) { 554bool 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
539fs::path GetCurrentDir() { 566fs::path GetCurrentDir() {
@@ -578,6 +605,12 @@ fs::file_type GetEntryType(const fs::path& path) {
578} 605}
579 606
580u64 GetSize(const fs::path& path) { 607u64 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
6namespace Common::FS::Android {
7
8JNIEnv* 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
27void 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
44void 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
55bool 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
64int 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 }
94ANDROID_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
20namespace Common::FS::Android {
21
22static JavaVM* g_jvm = nullptr;
23static 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;
28ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR)
29ANDROID_STORAGE_FUNCTIONS(FS)
30#undef F
31#undef FS
32#undef FR
33
34enum class OpenMode {
35 Read,
36 Write,
37 ReadWrite,
38 WriteAppend,
39 WriteTruncate,
40 ReadWriteAppend,
41 ReadWriteTruncate,
42 Never
43};
44
45void RegisterCallbacks(JNIEnv* env, jclass clazz);
46
47void UnRegisterCallbacks();
48
49bool 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;
54ANDROID_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);
61ANDROID_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 */
69using DirEntryCallable = std::function<bool(const std::filesystem::path& path)>; 69using 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
83private: 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
131private:
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
224void SetAppDirectory(const std::string& app_directory) {
225 PathManagerImpl::GetInstance().Reinitialize(app_directory);
226}
227
213const fs::path& GetYuzuPath(YuzuPath yuzu_path) { 228const 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
351std::string SanitizePath(std::string_view path_, DirectorySeparator directory_separator) { 366std::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
13enum class YuzuPath { 13enum 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 */
190void 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 {
484public: 514public:
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
16namespace Common::Input { 16namespace 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
19enum class InputType { 19enum 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
56enum class CameraFormat { 58enum 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
66enum class VibrationError { 68enum 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
74enum class PollingError {
75 None,
76 NotSupported,
77 Unknown,
78};
79
80// Nfc reply from the controller 83// Nfc reply from the controller
81enum class NfcState { 84enum 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
94enum 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
101enum class VibrationAmplificationType { 97enum class VibrationAmplificationType {
102 Linear, 98 Linear,
@@ -107,7 +103,7 @@ enum class VibrationAmplificationType {
107struct AnalogProperties { 103struct 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 {
190struct BodyColorStatus { 190struct 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
213struct CameraStatus { 215struct 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
218struct NfcStatus { 220struct 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
228struct MifareData {
229 u8 command;
230 u8 sector;
231 std::array<u8, 0x6> key;
232 std::array<u8, 0x10> data;
233};
234
235struct 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 {
316public: 346public:
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
9namespace Common {
10
11// Forward declare implementation class for Node.
12namespace impl {
13
14class IntrusiveListImpl;
15
16}
17
18class IntrusiveListNode {
19 YUZU_NON_COPYABLE(IntrusiveListNode);
20
21private:
22 friend class impl::IntrusiveListImpl;
23
24 IntrusiveListNode* m_prev;
25 IntrusiveListNode* m_next;
26
27public:
28 constexpr IntrusiveListNode() : m_prev(this), m_next(this) {}
29
30 constexpr bool IsLinked() const {
31 return m_next != this;
32 }
33
34private:
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
96namespace impl {
97
98class IntrusiveListImpl {
99 YUZU_NON_COPYABLE(IntrusiveListImpl);
100
101private:
102 IntrusiveListNode m_root_node;
103
104public:
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
180public:
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
288private:
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
303template <class T, class Traits>
304class IntrusiveList {
305 YUZU_NON_COPYABLE(IntrusiveList);
306
307private:
308 impl::IntrusiveListImpl m_impl;
309
310public:
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
393private:
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
410public:
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
538template <auto T, class Derived = Common::impl::GetParentType<T>>
539class IntrusiveListMemberTraits;
540
541template <class Parent, IntrusiveListNode Parent::*Member, class Derived>
542class IntrusiveListMemberTraits<Member, Derived> {
543public:
544 using ListType = IntrusiveList<Derived, IntrusiveListMemberTraits>;
545
546private:
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
566template <auto T, class Derived = Common::impl::GetParentType<T>>
567class IntrusiveListMemberTraitsByNonConstexprOffsetOf;
568
569template <class Parent, IntrusiveListNode Parent::*Member, class Derived>
570class IntrusiveListMemberTraitsByNonConstexprOffsetOf<Member, Derived> {
571public:
572 using ListType = IntrusiveList<Derived, IntrusiveListMemberTraitsByNonConstexprOffsetOf>;
573
574private:
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
600template <class Derived>
601class IntrusiveListBaseNode : public IntrusiveListNode {};
602
603template <class Derived>
604class IntrusiveListBaseTraits {
605public:
606 using ListType = IntrusiveList<Derived, IntrusiveListBaseTraits>;
607
608private:
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
243template <typename T> 239template <typename T>
244concept HasRedBlackKeyType = requires { 240concept 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
248namespace impl { 246namespace impl {
249 247
250 template <typename T, typename Default> 248template <typename T, typename Default>
251 consteval auto* GetRedBlackKeyType() { 249consteval 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
33namespace Common::Log { 33namespace 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 */
162class LogcatBackend : public Backend {
163public:
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
158bool initialization_in_progress_suppress_logging = true; 178bool 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
214private: 234private:
@@ -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
114void 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);
15void PrintMessage(const Entry& entry); 15void 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.
17void PrintColoredMessage(const Entry& entry); 17void PrintColoredMessage(const Entry& entry);
18/// Formats and prints a log entry to the android logcat.
19void 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 */
31enum class Class : u8 { 31enum 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 @@
9namespace Common { 9namespace Common {
10 10
11template <class T> 11template <class T>
12requires(!std::is_array_v<T>) std::unique_ptr<T> make_unique_for_overwrite() { 12 requires(!std::is_array_v<T>)
13std::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
16template <class T> 17template <class T>
17requires 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>
19std::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
21template <class T, class... Args> 23template <class T, class... Args>
22requires std::is_bounded_array_v<T> 24 requires std::is_bounded_array_v<T>
23void make_unique_for_overwrite(Args&&...) = delete; 25void 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
9namespace Common {
10
11template <typename T>
12 requires(std::is_integral_v<T> && std::is_signed_v<T>)
13inline 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
19template <typename T> 19template <typename T>
20concept range = requires(T& t) { 20concept range = requires(T& t) {
21 begin(t); 21 begin(t);
22 end(t); 22 end(t);
23}; 23 };
24 24
25template <typename T> 25template <typename T>
26concept input_range = range<T>; 26concept 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
26template <typename Rep, typename Period>
27bool 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
37namespace std { 52namespace std {
38namespace polyfill { 53namespace polyfill {
39 54
40using stop_state_callbacks = list<function<void()>>; 55using stop_state_callback = size_t;
41 56
42class stop_state { 57class stop_state {
43public: 58public:
@@ -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
99private: 121private:
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
221private: 244private:
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
226template <typename Callback> 249template <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
344template <typename Rep, typename Period>
345bool 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
8namespace Common { 11namespace Common {
@@ -16,6 +19,12 @@ namespace Common {
16template <typename T> 19template <typename T>
17class ScratchBuffer { 20class ScratchBuffer {
18public: 21public:
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
89private: 108private:
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
11namespace Settings { 15namespace Settings {
12 16
@@ -14,18 +18,23 @@ Values values;
14static bool configuring_global = true; 18static bool configuring_global = true;
15 19
16std::string GetTimeZoneString() { 20std::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
31void LogSettings() { 40void 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
17namespace Settings { 17namespace Settings {
18 18
19enum class VSyncMode : u32 {
20 Immediate = 0,
21 Mailbox = 1,
22 FIFO = 2,
23 FIFORelaxed = 3,
24};
25
19enum class RendererBackend : u32 { 26enum 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
93enum class AstcRecompression : u32 {
94 Uncompressed = 0,
95 Bc1 = 1,
96 Bc3 = 2,
97};
98
86struct ResolutionScalingInfo { 99struct 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
12namespace Common {
13
14#ifdef _WIN32
15static s64 WindowsQueryPerformanceFrequency() {
16 LARGE_INTEGER frequency;
17 QueryPerformanceFrequency(&frequency);
18 return frequency.QuadPart;
19}
20
21static s64 WindowsQueryPerformanceCounter() {
22 LARGE_INTEGER counter;
23 QueryPerformanceCounter(&counter);
24 return counter.QuadPart;
25}
26
27static 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
40SteadyClock::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
68RealTimeClock::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
10namespace Common {
11
12struct 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
23struct 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
33std::string StringFromBuffer(const std::vector<u8>& data) { 33std::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
128std::string UTF16ToUTF8(const std::u16string& input) { 128std::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
133std::u16string UTF8ToUTF16(const std::string& input) { 133std::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
139static std::wstring CPToUTF16(u32 code_page, const std::string& input) { 139static 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
157std::string UTF16ToUTF8(const std::wstring& input) { 157std::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
175std::wstring UTF8ToUTF16W(const std::string& input) { 175std::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
419template <typename S, typename T, typename F> 419template <typename S, typename T, typename F>
420S operator+(const S& i, const swap_struct_t<T, F> v) { 420S 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
445template <typename S, typename T, typename F> 445template <typename S, typename T, typename F>
446S& operator+=(S& i, const swap_struct_t<T, F> v) { 446S& 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
469template <typename S, typename T, typename F> 469template <typename S, typename T, typename F>
470bool operator<(const S& p, const swap_struct_t<T, F> v) { 470bool 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
11namespace Common::TimeZone { 16namespace Common::TimeZone {
12 17
18// Time zone strings
19constexpr 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
28const std::array<const char*, 46>& GetTimeZoneStrings() {
29 return timezones;
30}
31
13std::string GetDefaultTimeZone() { 32std::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
27static int ConvertOsTimeZoneOffsetToInt(const std::string& timezone) { 43static 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
65const 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
73std::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
9namespace Common::TimeZone { 10namespace 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
104template <typename T> 104template <typename T>
105concept HasRBEntry = requires(T& t, const T& ct) { 105concept 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
110template <typename T> 110template <typename T>
111requires HasRBEntry<T> 111 requires HasRBEntry<T>
112class RBHead { 112class RBHead {
113private: 113private:
114 T* m_rbh_root = nullptr; 114 T* m_rbh_root = nullptr;
@@ -130,90 +130,90 @@ public:
130}; 130};
131 131
132template <typename T> 132template <typename T>
133requires 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}
137template <typename T> 137template <typename T>
138requires 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
143template <typename T> 143template <typename T>
144requires 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}
148template <typename T> 148template <typename T>
149requires 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
154template <typename T> 154template <typename T>
155requires 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}
159template <typename T> 159template <typename T>
160requires 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
165template <typename T> 165template <typename T>
166requires 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}
170template <typename T> 170template <typename T>
171requires 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
176template <typename T> 176template <typename T>
177requires HasRBEntry<T> 177 requires HasRBEntry<T>
178constexpr void RB_SET_LEFT(T* t, T* e) { 178constexpr void RB_SET_LEFT(T* t, T* e) {
179 RB_ENTRY(t).SetLeft(e); 179 RB_ENTRY(t).SetLeft(e);
180} 180}
181template <typename T> 181template <typename T>
182requires HasRBEntry<T> 182 requires HasRBEntry<T>
183constexpr void RB_SET_RIGHT(T* t, T* e) { 183constexpr void RB_SET_RIGHT(T* t, T* e) {
184 RB_ENTRY(t).SetRight(e); 184 RB_ENTRY(t).SetRight(e);
185} 185}
186template <typename T> 186template <typename T>
187requires HasRBEntry<T> 187 requires HasRBEntry<T>
188constexpr void RB_SET_PARENT(T* t, T* e) { 188constexpr void RB_SET_PARENT(T* t, T* e) {
189 RB_ENTRY(t).SetParent(e); 189 RB_ENTRY(t).SetParent(e);
190} 190}
191 191
192template <typename T> 192template <typename T>
193requires 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}
197template <typename T> 197template <typename T>
198requires 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
203template <typename T> 203template <typename T>
204requires 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
209template <typename T> 209template <typename T>
210requires HasRBEntry<T> 210 requires HasRBEntry<T>
211constexpr void RB_SET_COLOR(T* t, RBColor c) { 211constexpr void RB_SET_COLOR(T* t, RBColor c) {
212 RB_ENTRY(t).SetColor(c); 212 RB_ENTRY(t).SetColor(c);
213} 213}
214 214
215template <typename T> 215template <typename T>
216requires HasRBEntry<T> 216 requires HasRBEntry<T>
217constexpr void RB_SET(T* elm, T* parent) { 217constexpr 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
225template <typename T> 225template <typename T>
226requires HasRBEntry<T> 226 requires HasRBEntry<T>
227constexpr void RB_SET_BLACKRED(T* black, T* red) { 227constexpr 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
232template <typename T> 232template <typename T>
233requires HasRBEntry<T> 233 requires HasRBEntry<T>
234constexpr void RB_ROTATE_LEFT(RBHead<T>& head, T* elm, T*& tmp) { 234constexpr 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
254template <typename T> 254template <typename T>
255requires HasRBEntry<T> 255 requires HasRBEntry<T>
256constexpr void RB_ROTATE_RIGHT(RBHead<T>& head, T* elm, T*& tmp) { 256constexpr 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
276template <typename T> 276template <typename T>
277requires HasRBEntry<T> 277 requires HasRBEntry<T>
278constexpr void RB_REMOVE_COLOR(RBHead<T>& head, T* parent, T* elm) { 278constexpr 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
360template <typename T> 360template <typename T>
361requires HasRBEntry<T> 361 requires HasRBEntry<T>
362constexpr T* RB_REMOVE(RBHead<T>& head, T* elm) { 362constexpr 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
453template <typename T> 453template <typename T>
454requires HasRBEntry<T> 454 requires HasRBEntry<T>
455constexpr void RB_INSERT_COLOR(RBHead<T>& head, T* elm) { 455constexpr 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
501template <typename T, typename Compare> 501template <typename T, typename Compare>
502requires HasRBEntry<T> 502 requires HasRBEntry<T>
503constexpr T* RB_INSERT(RBHead<T>& head, T* elm, Compare cmp) { 503constexpr 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
536template <typename T, typename Compare> 536template <typename T, typename Compare>
537requires HasRBEntry<T> 537 requires HasRBEntry<T>
538constexpr T* RB_FIND(RBHead<T>& head, T* elm, Compare cmp) { 538constexpr 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
555template <typename T, typename Compare> 555template <typename T, typename Compare>
556requires HasRBEntry<T> 556 requires HasRBEntry<T>
557constexpr T* RB_NFIND(RBHead<T>& head, T* elm, Compare cmp) { 557constexpr 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
576template <typename T, typename U, typename Compare> 576template <typename T, typename U, typename Compare>
577requires HasRBEntry<T> 577 requires HasRBEntry<T>
578constexpr T* RB_FIND_KEY(RBHead<T>& head, const U& key, Compare cmp) { 578constexpr 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
595template <typename T, typename U, typename Compare> 595template <typename T, typename U, typename Compare>
596requires HasRBEntry<T> 596 requires HasRBEntry<T>
597constexpr T* RB_NFIND_KEY(RBHead<T>& head, const U& key, Compare cmp) { 597constexpr 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
616template <typename T, typename Compare> 616template <typename T, typename Compare>
617requires HasRBEntry<T> 617 requires HasRBEntry<T>
618constexpr T* RB_FIND_EXISTING(RBHead<T>& head, T* elm, Compare cmp) { 618constexpr 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
633template <typename T, typename U, typename Compare> 633template <typename T, typename U, typename Compare>
634requires HasRBEntry<T> 634 requires HasRBEntry<T>
635constexpr T* RB_FIND_EXISTING_KEY(RBHead<T>& head, const U& key, Compare cmp) { 635constexpr 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
650template <typename T> 650template <typename T>
651requires HasRBEntry<T> 651 requires HasRBEntry<T>
652constexpr T* RB_NEXT(T* elm) { 652constexpr 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
671template <typename T> 671template <typename T>
672requires HasRBEntry<T> 672 requires HasRBEntry<T>
673constexpr T* RB_PREV(T* elm) { 673constexpr 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
692template <typename T> 692template <typename T>
693requires HasRBEntry<T> 693 requires HasRBEntry<T>
694constexpr T* RB_MIN(RBHead<T>& head) { 694constexpr 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
706template <typename T> 706template <typename T>
707requires HasRBEntry<T> 707 requires HasRBEntry<T>
708constexpr T* RB_MAX(RBHead<T>& head) { 708constexpr 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
12namespace Common {
13
14template <bool Virtual, typename T>
15class TypedAddress {
16public:
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
131private:
132 uint64_t m_address{};
133};
134
135struct PhysicalAddressTag {};
136struct VirtualAddressTag {};
137struct ProcessAddressTag {};
138
139using PhysicalAddress = TypedAddress<false, PhysicalAddressTag>;
140using VirtualAddress = TypedAddress<true, VirtualAddressTag>;
141using ProcessAddress = TypedAddress<true, ProcessAddressTag>;
142
143// Define accessors.
144template <typename T>
145concept IsTypedAddress = std::same_as<T, PhysicalAddress> || std::same_as<T, VirtualAddress> ||
146 std::same_as<T, ProcessAddress>;
147
148template <typename T>
149constexpr 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.
161static_assert(sizeof(PhysicalAddress) == sizeof(uint64_t));
162static_assert(sizeof(VirtualAddress) == sizeof(uint64_t));
163static_assert(sizeof(ProcessAddress) == sizeof(uint64_t));
164
165static_assert(std::is_trivially_copyable_v<PhysicalAddress>);
166static_assert(std::is_trivially_copyable_v<VirtualAddress>);
167static_assert(std::is_trivially_copyable_v<ProcessAddress>);
168
169static_assert(std::is_trivially_copy_constructible_v<PhysicalAddress>);
170static_assert(std::is_trivially_copy_constructible_v<VirtualAddress>);
171static_assert(std::is_trivially_copy_constructible_v<ProcessAddress>);
172
173static_assert(std::is_trivially_move_constructible_v<PhysicalAddress>);
174static_assert(std::is_trivially_move_constructible_v<VirtualAddress>);
175static_assert(std::is_trivially_move_constructible_v<ProcessAddress>);
176
177static_assert(std::is_trivially_copy_assignable_v<PhysicalAddress>);
178static_assert(std::is_trivially_copy_assignable_v<VirtualAddress>);
179static_assert(std::is_trivially_copy_assignable_v<ProcessAddress>);
180
181static_assert(std::is_trivially_move_assignable_v<PhysicalAddress>);
182static_assert(std::is_trivially_move_assignable_v<VirtualAddress>);
183static_assert(std::is_trivially_move_assignable_v<ProcessAddress>);
184
185static_assert(std::is_trivially_destructible_v<PhysicalAddress>);
186static_assert(std::is_trivially_destructible_v<VirtualAddress>);
187static_assert(std::is_trivially_destructible_v<ProcessAddress>);
188
189static_assert(Null<uint64_t> == 0);
190static_assert(Null<PhysicalAddress> == Null<uint64_t>);
191static_assert(Null<VirtualAddress> == Null<uint64_t>);
192static_assert(Null<ProcessAddress> == Null<uint64_t>);
193
194// Constructor/assignment validations.
195static_assert([] {
196 const PhysicalAddress a(5);
197 PhysicalAddress b(a);
198 return b;
199}() == PhysicalAddress(5));
200static_assert([] {
201 const PhysicalAddress a(5);
202 PhysicalAddress b(10);
203 b = a;
204 return b;
205}() == PhysicalAddress(5));
206
207// Arithmetic validations.
208static_assert(PhysicalAddress(10) + 5 == PhysicalAddress(15));
209static_assert(PhysicalAddress(10) - 5 == PhysicalAddress(5));
210static_assert([] {
211 PhysicalAddress v(10);
212 v += 5;
213 return v;
214}() == PhysicalAddress(15));
215static_assert([] {
216 PhysicalAddress v(10);
217 v -= 5;
218 return v;
219}() == PhysicalAddress(5));
220static_assert(PhysicalAddress(10)++ == PhysicalAddress(10));
221static_assert(++PhysicalAddress(10) == PhysicalAddress(11));
222static_assert(PhysicalAddress(10)-- == PhysicalAddress(10));
223static_assert(--PhysicalAddress(10) == PhysicalAddress(9));
224
225// Logical validations.
226static_assert((PhysicalAddress(0b11111111) >> 1) == 0b01111111);
227static_assert((PhysicalAddress(0b10101010) >> 1) == 0b01010101);
228static_assert((PhysicalAddress(0b11111111) << 1) == 0b111111110);
229static_assert((PhysicalAddress(0b01010101) << 1) == 0b10101010);
230static_assert((PhysicalAddress(0b11111111) & 0b01010101) == 0b01010101);
231static_assert((PhysicalAddress(0b11111111) & 0b10101010) == 0b10101010);
232static_assert((PhysicalAddress(0b01010101) & 0b10101010) == 0b00000000);
233static_assert((PhysicalAddress(0b00000000) | 0b01010101) == 0b01010101);
234static_assert((PhysicalAddress(0b11111111) | 0b01010101) == 0b11111111);
235static_assert((PhysicalAddress(0b10101010) | 0b01010101) == 0b11111111);
236
237// Comparisons.
238static_assert(PhysicalAddress(0) == PhysicalAddress(0));
239static_assert(PhysicalAddress(0) != PhysicalAddress(1));
240static_assert(PhysicalAddress(0) < PhysicalAddress(1));
241static_assert(PhysicalAddress(0) <= PhysicalAddress(1));
242static_assert(PhysicalAddress(1) > PhysicalAddress(0));
243static_assert(PhysicalAddress(1) >= PhysicalAddress(0));
244
245static_assert(!(PhysicalAddress(0) == PhysicalAddress(1)));
246static_assert(!(PhysicalAddress(0) != PhysicalAddress(0)));
247static_assert(!(PhysicalAddress(1) < PhysicalAddress(0)));
248static_assert(!(PhysicalAddress(1) <= PhysicalAddress(0)));
249static_assert(!(PhysicalAddress(0) > PhysicalAddress(1)));
250static_assert(!(PhysicalAddress(0) >= PhysicalAddress(1)));
251
252} // namespace Common
253
254template <bool Virtual, typename T>
255constexpr inline uint64_t GetInteger(Common::TypedAddress<Virtual, T> address) {
256 return address.GetValue();
257}
258
259template <>
260struct 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
270template <>
271struct 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
281template <>
282struct 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
292namespace std {
293
294template <>
295struct hash<Common::PhysicalAddress> {
296 size_t operator()(const Common::PhysicalAddress& k) const noexcept {
297 return k.GetValue();
298 }
299};
300
301template <>
302struct hash<Common::ProcessAddress> {
303 size_t operator()(const Common::ProcessAddress& k) const noexcept {
304 return k.GetValue();
305 }
306};
307
308template <>
309struct 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
50std::array<u8, 0x10> ConstructFromFormattedString(std::string_view formatted_string) { 50std::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
643template <typename X> 651template <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
12namespace Common { 13namespace Common {
13 14
14using base_timer = std::chrono::steady_clock;
15using base_time_point = std::chrono::time_point<base_timer>;
16
17class StandardWallClock final : public WallClock { 15class StandardWallClock final : public WallClock {
18public: 16public:
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
59private: 51private:
60 base_time_point start_time; 52 SteadyClock::time_point start_time;
61}; 53};
62 54
55std::unique_ptr<WallClock> CreateOptimalClock() {
63#ifdef ARCHITECTURE_x86_64 56#ifdef ARCHITECTURE_x86_64
64
65std::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>();
89std::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 72std::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
13class WallClock { 14class WallClock {
14public: 15public:
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
43protected: 72protected:
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
51private: 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, 90std::unique_ptr<WallClock> CreateOptimalClock();
56 u64 emulated_clock_frequency); 91
92std::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
8extern "C" {
9// http://undocumented.ntinternals.net/index.html?page=UserMode%2FUndocumented%20Functions%2FTime%2FNtQueryTimerResolution.html
10NTSYSAPI 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
14NTSYSAPI 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
18NTSYSAPI 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
30namespace Common::Windows {
31
32namespace {
33
34using namespace std::chrono;
35
36constexpr nanoseconds ToNS(ULONG hundred_ns) {
37 return nanoseconds{hundred_ns * 100};
38}
39
40constexpr ULONG ToHundredNS(nanoseconds ns) {
41 return static_cast<ULONG>(ns.count()) / 100;
42}
43
44struct TimerResolution {
45 std::chrono::nanoseconds minimum;
46 std::chrono::nanoseconds maximum;
47 std::chrono::nanoseconds current;
48};
49
50TimerResolution 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
63void 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
77nanoseconds GetMinimumTimerResolution() {
78 return GetTimerResolution().minimum;
79}
80
81nanoseconds GetMaximumTimerResolution() {
82 return GetTimerResolution().maximum;
83}
84
85nanoseconds GetCurrentTimerResolution() {
86 return GetTimerResolution().current;
87}
88
89nanoseconds 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
97nanoseconds SetCurrentTimerResolutionToMaximum() {
98 SetHighQoS();
99 return SetCurrentTimerResolution(GetMaximumTimerResolution());
100}
101
102void 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
8namespace Common::Windows {
9
10/// Returns the minimum (least precise) supported timer resolution in nanoseconds.
11std::chrono::nanoseconds GetMinimumTimerResolution();
12
13/// Returns the maximum (most precise) supported timer resolution in nanoseconds.
14std::chrono::nanoseconds GetMaximumTimerResolution();
15
16/// Returns the current timer resolution in nanoseconds.
17std::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 */
26std::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 */
33std::chrono::nanoseconds SetCurrentTimerResolutionToMaximum();
34
35/// Sleep for one tick of the current timer resolution.
36void 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
14namespace 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
27static 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
41void 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
6namespace Common::X64 {
7
8void 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 8namespace Common::X64 {
13#include <intrin.h>
14#endif
15
16namespace 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
28static 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
42u64 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. 10NativeClock::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
63namespace X64 { 18std::chrono::nanoseconds NativeClock::GetTimeNS() const {
64NativeClock::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
77u64 NativeClock::GetRTSC() { 22std::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
95void NativeClock::Pause(bool is_paused) { 26std::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
109std::chrono::nanoseconds NativeClock::GetTimeNS() { 30u64 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
114std::chrono::microseconds NativeClock::GetTimeUS() { 34u64 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
119std::chrono::milliseconds NativeClock::GetTimeMS() { 38u64 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
124u64 NativeClock::GetClockCycles() { 42u64 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
129u64 NativeClock::GetCPUCycles() { 46bool 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
8namespace Common { 8namespace Common::X64 {
9 9
10namespace X64 {
11class NativeClock final : public WallClock { 10class NativeClock final : public WallClock {
12public: 11public:
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
28private: 30private:
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
53u64 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
10namespace Common::X64 {
11
12template <u64 Nearest>
13static u64 RoundToNearest(u64 value) {
14 const auto mod = value % Nearest;
15 return mod >= (Nearest / 2) ? (value - mod + Nearest) : (value - mod);
16}
17
18u64 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
12namespace 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
24static 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
35u64 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
34std::vector<u8> DecompressDataZSTD(std::span<const u8> compressed) { 34std::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(