summaryrefslogtreecommitdiff
path: root/src/common
diff options
context:
space:
mode:
Diffstat (limited to 'src/common')
-rw-r--r--src/common/CMakeLists.txt51
-rw-r--r--src/common/algorithm.h3
-rw-r--r--src/common/alignment.h69
-rw-r--r--src/common/assert.h7
-rw-r--r--src/common/atomic_ops.cpp75
-rw-r--r--src/common/atomic_ops.h17
-rw-r--r--src/common/bit_cast.h22
-rw-r--r--src/common/bit_field.h21
-rw-r--r--src/common/bit_util.h34
-rw-r--r--src/common/cityhash.h23
-rw-r--r--src/common/color.h38
-rw-r--r--src/common/common_funcs.h42
-rw-r--r--src/common/common_paths.h1
-rw-r--r--src/common/concepts.h34
-rw-r--r--src/common/detached_tasks.cpp3
-rw-r--r--src/common/dynamic_library.cpp2
-rw-r--r--src/common/dynamic_library.h13
-rw-r--r--src/common/fiber.cpp233
-rw-r--r--src/common/fiber.h78
-rw-r--r--src/common/file_util.cpp133
-rw-r--r--src/common/file_util.h98
-rw-r--r--src/common/hash.h25
-rw-r--r--src/common/hex_util.cpp34
-rw-r--r--src/common/hex_util.h37
-rw-r--r--src/common/logging/backend.cpp39
-rw-r--r--src/common/logging/backend.h16
-rw-r--r--src/common/logging/log.h1
-rw-r--r--src/common/lz4_compression.cpp16
-rw-r--r--src/common/lz4_compression.h24
-rw-r--r--src/common/math_util.h22
-rw-r--r--src/common/memory_detect.cpp73
-rw-r--r--src/common/memory_detect.h22
-rw-r--r--src/common/misc.cpp15
-rw-r--r--src/common/multi_level_queue.h37
-rw-r--r--src/common/page_table.cpp36
-rw-r--r--src/common/page_table.h53
-rw-r--r--src/common/param_package.h12
-rw-r--r--src/common/quaternion.h45
-rw-r--r--src/common/ring_buffer.h4
-rw-r--r--src/common/scope_exit.h9
-rw-r--r--src/common/spin_lock.cpp54
-rw-r--r--src/common/spin_lock.h34
-rw-r--r--src/common/stream.cpp47
-rw-r--r--src/common/stream.h56
-rw-r--r--src/common/string_util.cpp5
-rw-r--r--src/common/string_util.h46
-rw-r--r--src/common/swap.h46
-rw-r--r--src/common/telemetry.cpp5
-rw-r--r--src/common/telemetry.h16
-rw-r--r--src/common/thread.cpp64
-rw-r--r--src/common/thread.h22
-rw-r--r--src/common/thread_queue_list.h10
-rw-r--r--src/common/threadsafe_queue.h12
-rw-r--r--src/common/time_zone.cpp49
-rw-r--r--src/common/time_zone.h18
-rw-r--r--src/common/timer.cpp12
-rw-r--r--src/common/timer.h16
-rw-r--r--src/common/uint128.cpp26
-rw-r--r--src/common/uint128.h7
-rw-r--r--src/common/uuid.h17
-rw-r--r--src/common/vector_math.h279
-rw-r--r--src/common/virtual_buffer.cpp43
-rw-r--r--src/common/virtual_buffer.h82
-rw-r--r--src/common/wall_clock.cpp91
-rw-r--r--src/common/wall_clock.h55
-rw-r--r--src/common/web_result.h25
-rw-r--r--src/common/x64/cpu_detect.cpp38
-rw-r--r--src/common/x64/cpu_detect.h13
-rw-r--r--src/common/x64/native_clock.cpp103
-rw-r--r--src/common/x64/native_clock.h48
-rw-r--r--src/common/x64/xbyak_abi.h229
-rw-r--r--src/common/x64/xbyak_util.h47
-rw-r--r--src/common/zstd_compression.cpp1
-rw-r--r--src/common/zstd_compression.h17
74 files changed, 2445 insertions, 705 deletions
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index eeceaa655..d20e6c3b5 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -32,6 +32,8 @@ add_custom_command(OUTPUT scm_rev.cpp
32 DEPENDS 32 DEPENDS
33 # WARNING! It was too much work to try and make a common location for this list, 33 # WARNING! It was too much work to try and make a common location for this list,
34 # so if you need to change it, please update CMakeModules/GenerateSCMRev.cmake as well 34 # so if you need to change it, please update CMakeModules/GenerateSCMRev.cmake as well
35 "${VIDEO_CORE}/renderer_opengl/gl_arb_decompiler.cpp"
36 "${VIDEO_CORE}/renderer_opengl/gl_arb_decompiler.h"
35 "${VIDEO_CORE}/renderer_opengl/gl_shader_cache.cpp" 37 "${VIDEO_CORE}/renderer_opengl/gl_shader_cache.cpp"
36 "${VIDEO_CORE}/renderer_opengl/gl_shader_cache.h" 38 "${VIDEO_CORE}/renderer_opengl/gl_shader_cache.h"
37 "${VIDEO_CORE}/renderer_opengl/gl_shader_decompiler.cpp" 39 "${VIDEO_CORE}/renderer_opengl/gl_shader_decompiler.cpp"
@@ -96,8 +98,11 @@ add_library(common STATIC
96 algorithm.h 98 algorithm.h
97 alignment.h 99 alignment.h
98 assert.h 100 assert.h
101 atomic_ops.cpp
102 atomic_ops.h
99 detached_tasks.cpp 103 detached_tasks.cpp
100 detached_tasks.h 104 detached_tasks.h
105 bit_cast.h
101 bit_field.h 106 bit_field.h
102 bit_util.h 107 bit_util.h
103 cityhash.cpp 108 cityhash.cpp
@@ -106,8 +111,11 @@ add_library(common STATIC
106 common_funcs.h 111 common_funcs.h
107 common_paths.h 112 common_paths.h
108 common_types.h 113 common_types.h
114 concepts.h
109 dynamic_library.cpp 115 dynamic_library.cpp
110 dynamic_library.h 116 dynamic_library.h
117 fiber.cpp
118 fiber.h
111 file_util.cpp 119 file_util.cpp
112 file_util.h 120 file_util.h
113 hash.h 121 hash.h
@@ -123,6 +131,8 @@ add_library(common STATIC
123 lz4_compression.cpp 131 lz4_compression.cpp
124 lz4_compression.h 132 lz4_compression.h
125 math_util.h 133 math_util.h
134 memory_detect.cpp
135 memory_detect.h
126 memory_hook.cpp 136 memory_hook.cpp
127 memory_hook.h 137 memory_hook.h
128 microprofile.cpp 138 microprofile.cpp
@@ -139,6 +149,10 @@ add_library(common STATIC
139 scm_rev.cpp 149 scm_rev.cpp
140 scm_rev.h 150 scm_rev.h
141 scope_exit.h 151 scope_exit.h
152 spin_lock.cpp
153 spin_lock.h
154 stream.cpp
155 stream.h
142 string_util.cpp 156 string_util.cpp
143 string_util.h 157 string_util.h
144 swap.h 158 swap.h
@@ -148,6 +162,8 @@ add_library(common STATIC
148 thread.h 162 thread.h
149 thread_queue_list.h 163 thread_queue_list.h
150 threadsafe_queue.h 164 threadsafe_queue.h
165 time_zone.cpp
166 time_zone.h
151 timer.cpp 167 timer.cpp
152 timer.h 168 timer.h
153 uint128.cpp 169 uint128.cpp
@@ -155,7 +171,10 @@ add_library(common STATIC
155 uuid.cpp 171 uuid.cpp
156 uuid.h 172 uuid.h
157 vector_math.h 173 vector_math.h
158 web_result.h 174 virtual_buffer.cpp
175 virtual_buffer.h
176 wall_clock.cpp
177 wall_clock.h
159 zstd_compression.cpp 178 zstd_compression.cpp
160 zstd_compression.h 179 zstd_compression.h
161) 180)
@@ -165,10 +184,36 @@ if(ARCHITECTURE_x86_64)
165 PRIVATE 184 PRIVATE
166 x64/cpu_detect.cpp 185 x64/cpu_detect.cpp
167 x64/cpu_detect.h 186 x64/cpu_detect.h
187 x64/native_clock.cpp
188 x64/native_clock.h
189 x64/xbyak_abi.h
190 x64/xbyak_util.h
168 ) 191 )
169endif() 192endif()
170 193
194if (MSVC)
195 target_compile_definitions(common PRIVATE
196 # The standard library doesn't provide any replacement for codecvt yet
197 # so we can disable this deprecation warning for the time being.
198 _SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING
199 )
200 target_compile_options(common PRIVATE
201 /W4
202 /WX
203 )
204else()
205 target_compile_options(common PRIVATE
206 -Werror
207 )
208endif()
209
171create_target_directory_groups(common) 210create_target_directory_groups(common)
211find_package(Boost 1.71 COMPONENTS context headers REQUIRED)
172 212
173target_link_libraries(common PUBLIC Boost::boost fmt microprofile) 213target_link_libraries(common PUBLIC ${Boost_LIBRARIES} fmt::fmt microprofile)
174target_link_libraries(common PRIVATE lz4_static libzstd_static) 214target_link_libraries(common PRIVATE lz4::lz4 xbyak)
215if (MSVC)
216 target_link_libraries(common PRIVATE zstd::zstd)
217else()
218 target_link_libraries(common PRIVATE zstd)
219endif()
diff --git a/src/common/algorithm.h b/src/common/algorithm.h
index e21b1373c..4804a3421 100644
--- a/src/common/algorithm.h
+++ b/src/common/algorithm.h
@@ -15,7 +15,8 @@
15namespace Common { 15namespace Common {
16 16
17template <class ForwardIt, class T, class Compare = std::less<>> 17template <class ForwardIt, class T, class Compare = std::less<>>
18ForwardIt BinaryFind(ForwardIt first, ForwardIt last, const T& value, Compare comp = {}) { 18[[nodiscard]] ForwardIt BinaryFind(ForwardIt first, ForwardIt last, const T& value,
19 Compare comp = {}) {
19 // Note: BOTH type T and the type after ForwardIt is dereferenced 20 // Note: BOTH type T and the type after ForwardIt is dereferenced
20 // must be implicitly convertible to BOTH Type1 and Type2, used in Compare. 21 // must be implicitly convertible to BOTH Type1 and Type2, used in Compare.
21 // This is stricter than lower_bound requirement (see above) 22 // This is stricter than lower_bound requirement (see above)
diff --git a/src/common/alignment.h b/src/common/alignment.h
index cdd4833f8..5040043de 100644
--- a/src/common/alignment.h
+++ b/src/common/alignment.h
@@ -3,41 +3,50 @@
3#pragma once 3#pragma once
4 4
5#include <cstddef> 5#include <cstddef>
6#include <memory> 6#include <new>
7#include <type_traits> 7#include <type_traits>
8 8
9namespace Common { 9namespace Common {
10 10
11template <typename T> 11template <typename T>
12constexpr T AlignUp(T value, std::size_t size) { 12[[nodiscard]] constexpr T AlignUp(T value, std::size_t size) {
13 static_assert(std::is_unsigned_v<T>, "T must be an unsigned value."); 13 static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
14 return static_cast<T>(value + (size - value % size) % size); 14 auto mod{static_cast<T>(value % size)};
15 value -= mod;
16 return static_cast<T>(mod == T{0} ? value : value + size);
15} 17}
16 18
17template <typename T> 19template <typename T>
18constexpr T AlignDown(T value, std::size_t size) { 20[[nodiscard]] constexpr T AlignDown(T value, std::size_t size) {
19 static_assert(std::is_unsigned_v<T>, "T must be an unsigned value."); 21 static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
20 return static_cast<T>(value - value % size); 22 return static_cast<T>(value - value % size);
21} 23}
22 24
23template <typename T> 25template <typename T>
24constexpr T AlignBits(T value, std::size_t align) { 26[[nodiscard]] constexpr T AlignBits(T value, std::size_t align) {
25 static_assert(std::is_unsigned_v<T>, "T must be an unsigned value."); 27 static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
26 return static_cast<T>((value + ((1ULL << align) - 1)) >> align << align); 28 return static_cast<T>((value + ((1ULL << align) - 1)) >> align << align);
27} 29}
28 30
29template <typename T> 31template <typename T>
30constexpr bool Is4KBAligned(T value) { 32[[nodiscard]] constexpr bool Is4KBAligned(T value) {
31 static_assert(std::is_unsigned_v<T>, "T must be an unsigned value."); 33 static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
32 return (value & 0xFFF) == 0; 34 return (value & 0xFFF) == 0;
33} 35}
34 36
35template <typename T> 37template <typename T>
36constexpr bool IsWordAligned(T value) { 38[[nodiscard]] constexpr bool IsWordAligned(T value) {
37 static_assert(std::is_unsigned_v<T>, "T must be an unsigned value."); 39 static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
38 return (value & 0b11) == 0; 40 return (value & 0b11) == 0;
39} 41}
40 42
43template <typename T>
44[[nodiscard]] constexpr bool IsAligned(T value, std::size_t alignment) {
45 using U = typename std::make_unsigned<T>::type;
46 const U mask = static_cast<U>(alignment - 1);
47 return (value & mask) == 0;
48}
49
41template <typename T, std::size_t Align = 16> 50template <typename T, std::size_t Align = 16>
42class AlignmentAllocator { 51class AlignmentAllocator {
43public: 52public:
@@ -45,66 +54,28 @@ public:
45 using size_type = std::size_t; 54 using size_type = std::size_t;
46 using difference_type = std::ptrdiff_t; 55 using difference_type = std::ptrdiff_t;
47 56
48 using pointer = T*;
49 using const_pointer = const T*;
50
51 using reference = T&;
52 using const_reference = const T&;
53
54 using propagate_on_container_copy_assignment = std::true_type; 57 using propagate_on_container_copy_assignment = std::true_type;
55 using propagate_on_container_move_assignment = std::true_type; 58 using propagate_on_container_move_assignment = std::true_type;
56 using propagate_on_container_swap = std::true_type; 59 using propagate_on_container_swap = std::true_type;
57 using is_always_equal = std::true_type; 60 using is_always_equal = std::true_type;
58 61
59public:
60 constexpr AlignmentAllocator() noexcept = default; 62 constexpr AlignmentAllocator() noexcept = default;
61 63
62 template <typename T2> 64 template <typename T2>
63 constexpr AlignmentAllocator(const AlignmentAllocator<T2, Align>&) noexcept {} 65 constexpr AlignmentAllocator(const AlignmentAllocator<T2, Align>&) noexcept {}
64 66
65 pointer address(reference r) noexcept { 67 [[nodiscard]] T* allocate(size_type n) {
66 return std::addressof(r); 68 return static_cast<T*>(::operator new (n * sizeof(T), std::align_val_t{Align}));
67 }
68
69 const_pointer address(const_reference r) const noexcept {
70 return std::addressof(r);
71 }
72
73 pointer allocate(size_type n) {
74 return static_cast<pointer>(::operator new (n, std::align_val_t{Align}));
75 } 69 }
76 70
77 void deallocate(pointer p, size_type) { 71 void deallocate(T* p, size_type n) {
78 ::operator delete (p, std::align_val_t{Align}); 72 ::operator delete (p, n * sizeof(T), std::align_val_t{Align});
79 }
80
81 void construct(pointer p, const value_type& wert) {
82 new (p) value_type(wert);
83 }
84
85 void destroy(pointer p) {
86 p->~value_type();
87 }
88
89 size_type max_size() const noexcept {
90 return size_type(-1) / sizeof(value_type);
91 } 73 }
92 74
93 template <typename T2> 75 template <typename T2>
94 struct rebind { 76 struct rebind {
95 using other = AlignmentAllocator<T2, Align>; 77 using other = AlignmentAllocator<T2, Align>;
96 }; 78 };
97
98 bool operator!=(const AlignmentAllocator<T, Align>& other) const noexcept {
99 return !(*this == other);
100 }
101
102 // Returns true if and only if storage allocated from *this
103 // can be deallocated from other, and vice versa.
104 // Always returns true for stateless allocators.
105 bool operator==(const AlignmentAllocator<T, Align>& other) const noexcept {
106 return true;
107 }
108}; 79};
109 80
110} // namespace Common 81} // namespace Common
diff --git a/src/common/assert.h b/src/common/assert.h
index 5b67c5c52..06d7b5612 100644
--- a/src/common/assert.h
+++ b/src/common/assert.h
@@ -17,11 +17,12 @@
17// enough for our purposes. 17// enough for our purposes.
18template <typename Fn> 18template <typename Fn>
19#if defined(_MSC_VER) 19#if defined(_MSC_VER)
20__declspec(noinline, noreturn) 20[[msvc::noinline, noreturn]]
21#elif defined(__GNUC__) 21#elif defined(__GNUC__)
22 __attribute__((noinline, noreturn, cold)) 22[[gnu::cold, gnu::noinline, noreturn]]
23#endif 23#endif
24 static void assert_noinline_call(const Fn& fn) { 24static void
25assert_noinline_call(const Fn& fn) {
25 fn(); 26 fn();
26 Crash(); 27 Crash();
27 exit(1); // Keeps GCC's mouth shut about this actually returning 28 exit(1); // Keeps GCC's mouth shut about this actually returning
diff --git a/src/common/atomic_ops.cpp b/src/common/atomic_ops.cpp
new file mode 100644
index 000000000..1612d0e67
--- /dev/null
+++ b/src/common/atomic_ops.cpp
@@ -0,0 +1,75 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <cstring>
6
7#include "common/atomic_ops.h"
8
9#if _MSC_VER
10#include <intrin.h>
11#endif
12
13namespace Common {
14
15#if _MSC_VER
16
17bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected) {
18 const u8 result =
19 _InterlockedCompareExchange8(reinterpret_cast<volatile char*>(pointer), value, expected);
20 return result == expected;
21}
22
23bool AtomicCompareAndSwap(volatile u16* pointer, u16 value, u16 expected) {
24 const u16 result =
25 _InterlockedCompareExchange16(reinterpret_cast<volatile short*>(pointer), value, expected);
26 return result == expected;
27}
28
29bool AtomicCompareAndSwap(volatile u32* pointer, u32 value, u32 expected) {
30 const u32 result =
31 _InterlockedCompareExchange(reinterpret_cast<volatile long*>(pointer), value, expected);
32 return result == expected;
33}
34
35bool AtomicCompareAndSwap(volatile u64* pointer, u64 value, u64 expected) {
36 const u64 result = _InterlockedCompareExchange64(reinterpret_cast<volatile __int64*>(pointer),
37 value, expected);
38 return result == expected;
39}
40
41bool AtomicCompareAndSwap(volatile u64* pointer, u128 value, u128 expected) {
42 return _InterlockedCompareExchange128(reinterpret_cast<volatile __int64*>(pointer), value[1],
43 value[0],
44 reinterpret_cast<__int64*>(expected.data())) != 0;
45}
46
47#else
48
49bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected) {
50 return __sync_bool_compare_and_swap(pointer, expected, value);
51}
52
53bool AtomicCompareAndSwap(volatile u16* pointer, u16 value, u16 expected) {
54 return __sync_bool_compare_and_swap(pointer, expected, value);
55}
56
57bool AtomicCompareAndSwap(volatile u32* pointer, u32 value, u32 expected) {
58 return __sync_bool_compare_and_swap(pointer, expected, value);
59}
60
61bool AtomicCompareAndSwap(volatile u64* pointer, u64 value, u64 expected) {
62 return __sync_bool_compare_and_swap(pointer, expected, value);
63}
64
65bool AtomicCompareAndSwap(volatile u64* pointer, u128 value, u128 expected) {
66 unsigned __int128 value_a;
67 unsigned __int128 expected_a;
68 std::memcpy(&value_a, value.data(), sizeof(u128));
69 std::memcpy(&expected_a, expected.data(), sizeof(u128));
70 return __sync_bool_compare_and_swap((unsigned __int128*)pointer, expected_a, value_a);
71}
72
73#endif
74
75} // namespace Common
diff --git a/src/common/atomic_ops.h b/src/common/atomic_ops.h
new file mode 100644
index 000000000..b46888589
--- /dev/null
+++ b/src/common/atomic_ops.h
@@ -0,0 +1,17 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/common_types.h"
8
9namespace Common {
10
11[[nodiscard]] bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected);
12[[nodiscard]] bool AtomicCompareAndSwap(volatile u16* pointer, u16 value, u16 expected);
13[[nodiscard]] bool AtomicCompareAndSwap(volatile u32* pointer, u32 value, u32 expected);
14[[nodiscard]] bool AtomicCompareAndSwap(volatile u64* pointer, u64 value, u64 expected);
15[[nodiscard]] bool AtomicCompareAndSwap(volatile u64* pointer, u128 value, u128 expected);
16
17} // namespace Common
diff --git a/src/common/bit_cast.h b/src/common/bit_cast.h
new file mode 100644
index 000000000..a32a063d1
--- /dev/null
+++ b/src/common/bit_cast.h
@@ -0,0 +1,22 @@
1// Copyright 2020 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <cstring>
8#include <type_traits>
9
10namespace Common {
11
12template <typename To, typename From>
13[[nodiscard]] std::enable_if_t<sizeof(To) == sizeof(From) && std::is_trivially_copyable_v<From> &&
14 std::is_trivially_copyable_v<To>,
15 To>
16BitCast(const From& src) noexcept {
17 To dst;
18 std::memcpy(&dst, &src, sizeof(To));
19 return dst;
20}
21
22} // namespace Common
diff --git a/src/common/bit_field.h b/src/common/bit_field.h
index fd2bbbd99..0f0661172 100644
--- a/src/common/bit_field.h
+++ b/src/common/bit_field.h
@@ -36,13 +36,6 @@
36#include "common/common_funcs.h" 36#include "common/common_funcs.h"
37#include "common/swap.h" 37#include "common/swap.h"
38 38
39// Inlining
40#ifdef _WIN32
41#define FORCE_INLINE __forceinline
42#else
43#define FORCE_INLINE inline __attribute__((always_inline))
44#endif
45
46/* 39/*
47 * Abstract bitfield class 40 * Abstract bitfield class
48 * 41 *
@@ -142,8 +135,8 @@ public:
142 * containing several bitfields can be assembled by formatting each of their values and ORing 135 * containing several bitfields can be assembled by formatting each of their values and ORing
143 * the results together. 136 * the results together.
144 */ 137 */
145 static constexpr FORCE_INLINE StorageType FormatValue(const T& value) { 138 [[nodiscard]] static constexpr StorageType FormatValue(const T& value) {
146 return ((StorageType)value << position) & mask; 139 return (static_cast<StorageType>(value) << position) & mask;
147 } 140 }
148 141
149 /** 142 /**
@@ -151,7 +144,7 @@ public:
151 * (such as Value() or operator T), but this can be used to extract a value from a bitfield 144 * (such as Value() or operator T), but this can be used to extract a value from a bitfield
152 * union in a constexpr context. 145 * union in a constexpr context.
153 */ 146 */
154 static constexpr FORCE_INLINE T ExtractValue(const StorageType& storage) { 147 [[nodiscard]] static constexpr T ExtractValue(const StorageType& storage) {
155 if constexpr (std::numeric_limits<UnderlyingType>::is_signed) { 148 if constexpr (std::numeric_limits<UnderlyingType>::is_signed) {
156 std::size_t shift = 8 * sizeof(T) - bits; 149 std::size_t shift = 8 * sizeof(T) - bits;
157 return static_cast<T>(static_cast<UnderlyingType>(storage << (shift - position)) >> 150 return static_cast<T>(static_cast<UnderlyingType>(storage << (shift - position)) >>
@@ -175,19 +168,19 @@ public:
175 constexpr BitField(BitField&&) noexcept = default; 168 constexpr BitField(BitField&&) noexcept = default;
176 constexpr BitField& operator=(BitField&&) noexcept = default; 169 constexpr BitField& operator=(BitField&&) noexcept = default;
177 170
178 constexpr operator T() const { 171 [[nodiscard]] constexpr operator T() const {
179 return Value(); 172 return Value();
180 } 173 }
181 174
182 constexpr void Assign(const T& value) { 175 constexpr void Assign(const T& value) {
183 storage = (static_cast<StorageType>(storage) & ~mask) | FormatValue(value); 176 storage = static_cast<StorageType>((storage & ~mask) | FormatValue(value));
184 } 177 }
185 178
186 constexpr T Value() const { 179 [[nodiscard]] constexpr T Value() const {
187 return ExtractValue(storage); 180 return ExtractValue(storage);
188 } 181 }
189 182
190 constexpr explicit operator bool() const { 183 [[nodiscard]] constexpr explicit operator bool() const {
191 return Value() != 0; 184 return Value() != 0;
192 } 185 }
193 186
diff --git a/src/common/bit_util.h b/src/common/bit_util.h
index 6f7d5a947..29f59a9a3 100644
--- a/src/common/bit_util.h
+++ b/src/common/bit_util.h
@@ -17,12 +17,12 @@ namespace Common {
17 17
18/// Gets the size of a specified type T in bits. 18/// Gets the size of a specified type T in bits.
19template <typename T> 19template <typename T>
20constexpr std::size_t BitSize() { 20[[nodiscard]] constexpr std::size_t BitSize() {
21 return sizeof(T) * CHAR_BIT; 21 return sizeof(T) * CHAR_BIT;
22} 22}
23 23
24#ifdef _MSC_VER 24#ifdef _MSC_VER
25inline u32 CountLeadingZeroes32(u32 value) { 25[[nodiscard]] inline u32 CountLeadingZeroes32(u32 value) {
26 unsigned long leading_zero = 0; 26 unsigned long leading_zero = 0;
27 27
28 if (_BitScanReverse(&leading_zero, value) != 0) { 28 if (_BitScanReverse(&leading_zero, value) != 0) {
@@ -32,7 +32,7 @@ inline u32 CountLeadingZeroes32(u32 value) {
32 return 32; 32 return 32;
33} 33}
34 34
35inline u32 CountLeadingZeroes64(u64 value) { 35[[nodiscard]] inline u32 CountLeadingZeroes64(u64 value) {
36 unsigned long leading_zero = 0; 36 unsigned long leading_zero = 0;
37 37
38 if (_BitScanReverse64(&leading_zero, value) != 0) { 38 if (_BitScanReverse64(&leading_zero, value) != 0) {
@@ -42,7 +42,7 @@ inline u32 CountLeadingZeroes64(u64 value) {
42 return 64; 42 return 64;
43} 43}
44#else 44#else
45inline u32 CountLeadingZeroes32(u32 value) { 45[[nodiscard]] inline u32 CountLeadingZeroes32(u32 value) {
46 if (value == 0) { 46 if (value == 0) {
47 return 32; 47 return 32;
48 } 48 }
@@ -50,7 +50,7 @@ inline u32 CountLeadingZeroes32(u32 value) {
50 return static_cast<u32>(__builtin_clz(value)); 50 return static_cast<u32>(__builtin_clz(value));
51} 51}
52 52
53inline u32 CountLeadingZeroes64(u64 value) { 53[[nodiscard]] inline u32 CountLeadingZeroes64(u64 value) {
54 if (value == 0) { 54 if (value == 0) {
55 return 64; 55 return 64;
56 } 56 }
@@ -60,7 +60,7 @@ inline u32 CountLeadingZeroes64(u64 value) {
60#endif 60#endif
61 61
62#ifdef _MSC_VER 62#ifdef _MSC_VER
63inline u32 CountTrailingZeroes32(u32 value) { 63[[nodiscard]] inline u32 CountTrailingZeroes32(u32 value) {
64 unsigned long trailing_zero = 0; 64 unsigned long trailing_zero = 0;
65 65
66 if (_BitScanForward(&trailing_zero, value) != 0) { 66 if (_BitScanForward(&trailing_zero, value) != 0) {
@@ -70,7 +70,7 @@ inline u32 CountTrailingZeroes32(u32 value) {
70 return 32; 70 return 32;
71} 71}
72 72
73inline u32 CountTrailingZeroes64(u64 value) { 73[[nodiscard]] inline u32 CountTrailingZeroes64(u64 value) {
74 unsigned long trailing_zero = 0; 74 unsigned long trailing_zero = 0;
75 75
76 if (_BitScanForward64(&trailing_zero, value) != 0) { 76 if (_BitScanForward64(&trailing_zero, value) != 0) {
@@ -80,7 +80,7 @@ inline u32 CountTrailingZeroes64(u64 value) {
80 return 64; 80 return 64;
81} 81}
82#else 82#else
83inline u32 CountTrailingZeroes32(u32 value) { 83[[nodiscard]] inline u32 CountTrailingZeroes32(u32 value) {
84 if (value == 0) { 84 if (value == 0) {
85 return 32; 85 return 32;
86 } 86 }
@@ -88,7 +88,7 @@ inline u32 CountTrailingZeroes32(u32 value) {
88 return static_cast<u32>(__builtin_ctz(value)); 88 return static_cast<u32>(__builtin_ctz(value));
89} 89}
90 90
91inline u32 CountTrailingZeroes64(u64 value) { 91[[nodiscard]] inline u32 CountTrailingZeroes64(u64 value) {
92 if (value == 0) { 92 if (value == 0) {
93 return 64; 93 return 64;
94 } 94 }
@@ -99,13 +99,13 @@ inline u32 CountTrailingZeroes64(u64 value) {
99 99
100#ifdef _MSC_VER 100#ifdef _MSC_VER
101 101
102inline u32 MostSignificantBit32(const u32 value) { 102[[nodiscard]] inline u32 MostSignificantBit32(const u32 value) {
103 unsigned long result; 103 unsigned long result;
104 _BitScanReverse(&result, value); 104 _BitScanReverse(&result, value);
105 return static_cast<u32>(result); 105 return static_cast<u32>(result);
106} 106}
107 107
108inline u32 MostSignificantBit64(const u64 value) { 108[[nodiscard]] inline u32 MostSignificantBit64(const u64 value) {
109 unsigned long result; 109 unsigned long result;
110 _BitScanReverse64(&result, value); 110 _BitScanReverse64(&result, value);
111 return static_cast<u32>(result); 111 return static_cast<u32>(result);
@@ -113,30 +113,30 @@ inline u32 MostSignificantBit64(const u64 value) {
113 113
114#else 114#else
115 115
116inline u32 MostSignificantBit32(const u32 value) { 116[[nodiscard]] inline u32 MostSignificantBit32(const u32 value) {
117 return 31U - static_cast<u32>(__builtin_clz(value)); 117 return 31U - static_cast<u32>(__builtin_clz(value));
118} 118}
119 119
120inline u32 MostSignificantBit64(const u64 value) { 120[[nodiscard]] inline u32 MostSignificantBit64(const u64 value) {
121 return 63U - static_cast<u32>(__builtin_clzll(value)); 121 return 63U - static_cast<u32>(__builtin_clzll(value));
122} 122}
123 123
124#endif 124#endif
125 125
126inline u32 Log2Floor32(const u32 value) { 126[[nodiscard]] inline u32 Log2Floor32(const u32 value) {
127 return MostSignificantBit32(value); 127 return MostSignificantBit32(value);
128} 128}
129 129
130inline u32 Log2Ceil32(const u32 value) { 130[[nodiscard]] inline u32 Log2Ceil32(const u32 value) {
131 const u32 log2_f = Log2Floor32(value); 131 const u32 log2_f = Log2Floor32(value);
132 return log2_f + ((value ^ (1U << log2_f)) != 0U); 132 return log2_f + ((value ^ (1U << log2_f)) != 0U);
133} 133}
134 134
135inline u32 Log2Floor64(const u64 value) { 135[[nodiscard]] inline u32 Log2Floor64(const u64 value) {
136 return MostSignificantBit64(value); 136 return MostSignificantBit64(value);
137} 137}
138 138
139inline u32 Log2Ceil64(const u64 value) { 139[[nodiscard]] inline u32 Log2Ceil64(const u64 value) {
140 const u64 log2_f = static_cast<u64>(Log2Floor64(value)); 140 const u64 log2_f = static_cast<u64>(Log2Floor64(value));
141 return static_cast<u32>(log2_f + ((value ^ (1ULL << log2_f)) != 0ULL)); 141 return static_cast<u32>(log2_f + ((value ^ (1ULL << log2_f)) != 0ULL));
142} 142}
diff --git a/src/common/cityhash.h b/src/common/cityhash.h
index 4b94f8e18..a00804e01 100644
--- a/src/common/cityhash.h
+++ b/src/common/cityhash.h
@@ -61,42 +61,43 @@
61 61
62#pragma once 62#pragma once
63 63
64#include <cstddef>
65#include <cstdint>
64#include <utility> 66#include <utility>
65#include <stdint.h>
66#include <stdlib.h> // for std::size_t.
67 67
68namespace Common { 68namespace Common {
69 69
70typedef std::pair<uint64_t, uint64_t> uint128; 70using uint128 = std::pair<uint64_t, uint64_t>;
71 71
72inline uint64_t Uint128Low64(const uint128& x) { 72[[nodiscard]] inline uint64_t Uint128Low64(const uint128& x) {
73 return x.first; 73 return x.first;
74} 74}
75inline uint64_t Uint128High64(const uint128& x) { 75[[nodiscard]] inline uint64_t Uint128High64(const uint128& x) {
76 return x.second; 76 return x.second;
77} 77}
78 78
79// Hash function for a byte array. 79// Hash function for a byte array.
80uint64_t CityHash64(const char* buf, std::size_t len); 80[[nodiscard]] uint64_t CityHash64(const char* buf, std::size_t len);
81 81
82// Hash function for a byte array. For convenience, a 64-bit seed is also 82// Hash function for a byte array. For convenience, a 64-bit seed is also
83// hashed into the result. 83// hashed into the result.
84uint64_t CityHash64WithSeed(const char* buf, std::size_t len, uint64_t seed); 84[[nodiscard]] uint64_t CityHash64WithSeed(const char* buf, std::size_t len, uint64_t seed);
85 85
86// Hash function for a byte array. For convenience, two seeds are also 86// Hash function for a byte array. For convenience, two seeds are also
87// hashed into the result. 87// hashed into the result.
88uint64_t CityHash64WithSeeds(const char* buf, std::size_t len, uint64_t seed0, uint64_t seed1); 88[[nodiscard]] uint64_t CityHash64WithSeeds(const char* buf, std::size_t len, uint64_t seed0,
89 uint64_t seed1);
89 90
90// Hash function for a byte array. 91// Hash function for a byte array.
91uint128 CityHash128(const char* s, std::size_t len); 92[[nodiscard]] uint128 CityHash128(const char* s, std::size_t len);
92 93
93// Hash function for a byte array. For convenience, a 128-bit seed is also 94// Hash function for a byte array. For convenience, a 128-bit seed is also
94// hashed into the result. 95// hashed into the result.
95uint128 CityHash128WithSeed(const char* s, std::size_t len, uint128 seed); 96[[nodiscard]] uint128 CityHash128WithSeed(const char* s, std::size_t len, uint128 seed);
96 97
97// Hash 128 input bits down to 64 bits of output. 98// Hash 128 input bits down to 64 bits of output.
98// This is intended to be a reasonably good hash function. 99// This is intended to be a reasonably good hash function.
99inline uint64_t Hash128to64(const uint128& x) { 100[[nodiscard]] inline uint64_t Hash128to64(const uint128& x) {
100 // Murmur-inspired hashing. 101 // Murmur-inspired hashing.
101 const uint64_t kMul = 0x9ddfea08eb382d69ULL; 102 const uint64_t kMul = 0x9ddfea08eb382d69ULL;
102 uint64_t a = (Uint128Low64(x) ^ Uint128High64(x)) * kMul; 103 uint64_t a = (Uint128Low64(x) ^ Uint128High64(x)) * kMul;
diff --git a/src/common/color.h b/src/common/color.h
index 3a2222077..bbcac858e 100644
--- a/src/common/color.h
+++ b/src/common/color.h
@@ -10,45 +10,45 @@
10#include "common/swap.h" 10#include "common/swap.h"
11#include "common/vector_math.h" 11#include "common/vector_math.h"
12 12
13namespace Color { 13namespace Common::Color {
14 14
15/// Convert a 1-bit color component to 8 bit 15/// Convert a 1-bit color component to 8 bit
16constexpr u8 Convert1To8(u8 value) { 16[[nodiscard]] constexpr u8 Convert1To8(u8 value) {
17 return value * 255; 17 return value * 255;
18} 18}
19 19
20/// Convert a 4-bit color component to 8 bit 20/// Convert a 4-bit color component to 8 bit
21constexpr u8 Convert4To8(u8 value) { 21[[nodiscard]] constexpr u8 Convert4To8(u8 value) {
22 return (value << 4) | value; 22 return (value << 4) | value;
23} 23}
24 24
25/// Convert a 5-bit color component to 8 bit 25/// Convert a 5-bit color component to 8 bit
26constexpr u8 Convert5To8(u8 value) { 26[[nodiscard]] constexpr u8 Convert5To8(u8 value) {
27 return (value << 3) | (value >> 2); 27 return (value << 3) | (value >> 2);
28} 28}
29 29
30/// Convert a 6-bit color component to 8 bit 30/// Convert a 6-bit color component to 8 bit
31constexpr u8 Convert6To8(u8 value) { 31[[nodiscard]] constexpr u8 Convert6To8(u8 value) {
32 return (value << 2) | (value >> 4); 32 return (value << 2) | (value >> 4);
33} 33}
34 34
35/// Convert a 8-bit color component to 1 bit 35/// Convert a 8-bit color component to 1 bit
36constexpr u8 Convert8To1(u8 value) { 36[[nodiscard]] constexpr u8 Convert8To1(u8 value) {
37 return value >> 7; 37 return value >> 7;
38} 38}
39 39
40/// Convert a 8-bit color component to 4 bit 40/// Convert a 8-bit color component to 4 bit
41constexpr u8 Convert8To4(u8 value) { 41[[nodiscard]] constexpr u8 Convert8To4(u8 value) {
42 return value >> 4; 42 return value >> 4;
43} 43}
44 44
45/// Convert a 8-bit color component to 5 bit 45/// Convert a 8-bit color component to 5 bit
46constexpr u8 Convert8To5(u8 value) { 46[[nodiscard]] constexpr u8 Convert8To5(u8 value) {
47 return value >> 3; 47 return value >> 3;
48} 48}
49 49
50/// Convert a 8-bit color component to 6 bit 50/// Convert a 8-bit color component to 6 bit
51constexpr u8 Convert8To6(u8 value) { 51[[nodiscard]] constexpr u8 Convert8To6(u8 value) {
52 return value >> 2; 52 return value >> 2;
53} 53}
54 54
@@ -57,7 +57,7 @@ constexpr u8 Convert8To6(u8 value) {
57 * @param bytes Pointer to encoded source color 57 * @param bytes Pointer to encoded source color
58 * @return Result color decoded as Common::Vec4<u8> 58 * @return Result color decoded as Common::Vec4<u8>
59 */ 59 */
60inline Common::Vec4<u8> DecodeRGBA8(const u8* bytes) { 60[[nodiscard]] inline Common::Vec4<u8> DecodeRGBA8(const u8* bytes) {
61 return {bytes[3], bytes[2], bytes[1], bytes[0]}; 61 return {bytes[3], bytes[2], bytes[1], bytes[0]};
62} 62}
63 63
@@ -66,7 +66,7 @@ inline Common::Vec4<u8> DecodeRGBA8(const u8* bytes) {
66 * @param bytes Pointer to encoded source color 66 * @param bytes Pointer to encoded source color
67 * @return Result color decoded as Common::Vec4<u8> 67 * @return Result color decoded as Common::Vec4<u8>
68 */ 68 */
69inline Common::Vec4<u8> DecodeRGB8(const u8* bytes) { 69[[nodiscard]] inline Common::Vec4<u8> DecodeRGB8(const u8* bytes) {
70 return {bytes[2], bytes[1], bytes[0], 255}; 70 return {bytes[2], bytes[1], bytes[0], 255};
71} 71}
72 72
@@ -75,7 +75,7 @@ inline Common::Vec4<u8> DecodeRGB8(const u8* bytes) {
75 * @param bytes Pointer to encoded source color 75 * @param bytes Pointer to encoded source color
76 * @return Result color decoded as Common::Vec4<u8> 76 * @return Result color decoded as Common::Vec4<u8>
77 */ 77 */
78inline Common::Vec4<u8> DecodeRG8(const u8* bytes) { 78[[nodiscard]] inline Common::Vec4<u8> DecodeRG8(const u8* bytes) {
79 return {bytes[1], bytes[0], 0, 255}; 79 return {bytes[1], bytes[0], 0, 255};
80} 80}
81 81
@@ -84,7 +84,7 @@ inline Common::Vec4<u8> DecodeRG8(const u8* bytes) {
84 * @param bytes Pointer to encoded source color 84 * @param bytes Pointer to encoded source color
85 * @return Result color decoded as Common::Vec4<u8> 85 * @return Result color decoded as Common::Vec4<u8>
86 */ 86 */
87inline Common::Vec4<u8> DecodeRGB565(const u8* bytes) { 87[[nodiscard]] inline Common::Vec4<u8> DecodeRGB565(const u8* bytes) {
88 u16_le pixel; 88 u16_le pixel;
89 std::memcpy(&pixel, bytes, sizeof(pixel)); 89 std::memcpy(&pixel, bytes, sizeof(pixel));
90 return {Convert5To8((pixel >> 11) & 0x1F), Convert6To8((pixel >> 5) & 0x3F), 90 return {Convert5To8((pixel >> 11) & 0x1F), Convert6To8((pixel >> 5) & 0x3F),
@@ -96,7 +96,7 @@ inline Common::Vec4<u8> DecodeRGB565(const u8* bytes) {
96 * @param bytes Pointer to encoded source color 96 * @param bytes Pointer to encoded source color
97 * @return Result color decoded as Common::Vec4<u8> 97 * @return Result color decoded as Common::Vec4<u8>
98 */ 98 */
99inline Common::Vec4<u8> DecodeRGB5A1(const u8* bytes) { 99[[nodiscard]] inline Common::Vec4<u8> DecodeRGB5A1(const u8* bytes) {
100 u16_le pixel; 100 u16_le pixel;
101 std::memcpy(&pixel, bytes, sizeof(pixel)); 101 std::memcpy(&pixel, bytes, sizeof(pixel));
102 return {Convert5To8((pixel >> 11) & 0x1F), Convert5To8((pixel >> 6) & 0x1F), 102 return {Convert5To8((pixel >> 11) & 0x1F), Convert5To8((pixel >> 6) & 0x1F),
@@ -108,7 +108,7 @@ inline Common::Vec4<u8> DecodeRGB5A1(const u8* bytes) {
108 * @param bytes Pointer to encoded source color 108 * @param bytes Pointer to encoded source color
109 * @return Result color decoded as Common::Vec4<u8> 109 * @return Result color decoded as Common::Vec4<u8>
110 */ 110 */
111inline Common::Vec4<u8> DecodeRGBA4(const u8* bytes) { 111[[nodiscard]] inline Common::Vec4<u8> DecodeRGBA4(const u8* bytes) {
112 u16_le pixel; 112 u16_le pixel;
113 std::memcpy(&pixel, bytes, sizeof(pixel)); 113 std::memcpy(&pixel, bytes, sizeof(pixel));
114 return {Convert4To8((pixel >> 12) & 0xF), Convert4To8((pixel >> 8) & 0xF), 114 return {Convert4To8((pixel >> 12) & 0xF), Convert4To8((pixel >> 8) & 0xF),
@@ -120,7 +120,7 @@ inline Common::Vec4<u8> DecodeRGBA4(const u8* bytes) {
120 * @param bytes Pointer to encoded source value 120 * @param bytes Pointer to encoded source value
121 * @return Depth value as an u32 121 * @return Depth value as an u32
122 */ 122 */
123inline u32 DecodeD16(const u8* bytes) { 123[[nodiscard]] inline u32 DecodeD16(const u8* bytes) {
124 u16_le data; 124 u16_le data;
125 std::memcpy(&data, bytes, sizeof(data)); 125 std::memcpy(&data, bytes, sizeof(data));
126 return data; 126 return data;
@@ -131,7 +131,7 @@ inline u32 DecodeD16(const u8* bytes) {
131 * @param bytes Pointer to encoded source value 131 * @param bytes Pointer to encoded source value
132 * @return Depth value as an u32 132 * @return Depth value as an u32
133 */ 133 */
134inline u32 DecodeD24(const u8* bytes) { 134[[nodiscard]] inline u32 DecodeD24(const u8* bytes) {
135 return (bytes[2] << 16) | (bytes[1] << 8) | bytes[0]; 135 return (bytes[2] << 16) | (bytes[1] << 8) | bytes[0];
136} 136}
137 137
@@ -140,7 +140,7 @@ inline u32 DecodeD24(const u8* bytes) {
140 * @param bytes Pointer to encoded source values 140 * @param bytes Pointer to encoded source values
141 * @return Resulting values stored as a Common::Vec2 141 * @return Resulting values stored as a Common::Vec2
142 */ 142 */
143inline Common::Vec2<u32> DecodeD24S8(const u8* bytes) { 143[[nodiscard]] inline Common::Vec2<u32> DecodeD24S8(const u8* bytes) {
144 return {static_cast<u32>((bytes[2] << 16) | (bytes[1] << 8) | bytes[0]), bytes[3]}; 144 return {static_cast<u32>((bytes[2] << 16) | (bytes[1] << 8) | bytes[0]), bytes[3]};
145} 145}
146 146
@@ -268,4 +268,4 @@ inline void EncodeX24S8(u8 stencil, u8* bytes) {
268 bytes[3] = stencil; 268 bytes[3] = stencil;
269} 269}
270 270
271} // namespace Color 271} // namespace Common::Color
diff --git a/src/common/common_funcs.h b/src/common/common_funcs.h
index 052254678..367b6bf6e 100644
--- a/src/common/common_funcs.h
+++ b/src/common/common_funcs.h
@@ -53,11 +53,49 @@ __declspec(dllimport) void __stdcall DebugBreak(void);
53// Call directly after the command or use the error num. 53// Call directly after the command or use the error num.
54// This function might change the error code. 54// This function might change the error code.
55// Defined in Misc.cpp. 55// Defined in Misc.cpp.
56std::string GetLastErrorMsg(); 56[[nodiscard]] std::string GetLastErrorMsg();
57
58#define DECLARE_ENUM_FLAG_OPERATORS(type) \
59 [[nodiscard]] constexpr type operator|(type a, type b) noexcept { \
60 using T = std::underlying_type_t<type>; \
61 return static_cast<type>(static_cast<T>(a) | static_cast<T>(b)); \
62 } \
63 [[nodiscard]] constexpr type operator&(type a, type b) noexcept { \
64 using T = std::underlying_type_t<type>; \
65 return static_cast<type>(static_cast<T>(a) & static_cast<T>(b)); \
66 } \
67 [[nodiscard]] constexpr type operator^(type a, type b) noexcept { \
68 using T = std::underlying_type_t<type>; \
69 return static_cast<type>(static_cast<T>(a) ^ static_cast<T>(b)); \
70 } \
71 constexpr type& operator|=(type& a, type b) noexcept { \
72 a = a | b; \
73 return a; \
74 } \
75 constexpr type& operator&=(type& a, type b) noexcept { \
76 a = a & b; \
77 return a; \
78 } \
79 constexpr type& operator^=(type& a, type b) noexcept { \
80 a = a ^ b; \
81 return a; \
82 } \
83 [[nodiscard]] constexpr type operator~(type key) noexcept { \
84 using T = std::underlying_type_t<type>; \
85 return static_cast<type>(~static_cast<T>(key)); \
86 } \
87 [[nodiscard]] constexpr bool True(type key) noexcept { \
88 using T = std::underlying_type_t<type>; \
89 return static_cast<T>(key) != 0; \
90 } \
91 [[nodiscard]] constexpr bool False(type key) noexcept { \
92 using T = std::underlying_type_t<type>; \
93 return static_cast<T>(key) == 0; \
94 }
57 95
58namespace Common { 96namespace Common {
59 97
60constexpr u32 MakeMagic(char a, char b, char c, char d) { 98[[nodiscard]] constexpr u32 MakeMagic(char a, char b, char c, char d) {
61 return u32(a) | u32(b) << 8 | u32(c) << 16 | u32(d) << 24; 99 return u32(a) | u32(b) << 8 | u32(c) << 16 | u32(d) << 24;
62} 100}
63 101
diff --git a/src/common/common_paths.h b/src/common/common_paths.h
index 076752d3b..3c593d5f6 100644
--- a/src/common/common_paths.h
+++ b/src/common/common_paths.h
@@ -35,6 +35,7 @@
35#define KEYS_DIR "keys" 35#define KEYS_DIR "keys"
36#define LOAD_DIR "load" 36#define LOAD_DIR "load"
37#define DUMP_DIR "dump" 37#define DUMP_DIR "dump"
38#define SCREENSHOTS_DIR "screenshots"
38#define SHADER_DIR "shader" 39#define SHADER_DIR "shader"
39#define LOG_DIR "log" 40#define LOG_DIR "log"
40 41
diff --git a/src/common/concepts.h b/src/common/concepts.h
new file mode 100644
index 000000000..5bef3ad67
--- /dev/null
+++ b/src/common/concepts.h
@@ -0,0 +1,34 @@
1// Copyright 2020 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <type_traits>
8
9namespace Common {
10
11// Check if type is like an STL container
12template <typename T>
13concept IsSTLContainer = requires(T t) {
14 typename T::value_type;
15 typename T::iterator;
16 typename T::const_iterator;
17 // TODO(ogniK): Replace below is std::same_as<void> when MSVC supports it.
18 t.begin();
19 t.end();
20 t.cbegin();
21 t.cend();
22 t.data();
23 t.size();
24};
25
26// TODO: Replace with std::derived_from when the <concepts> header
27// is available on all supported platforms.
28template <typename Derived, typename Base>
29concept DerivedFrom = requires {
30 std::is_base_of_v<Base, Derived>;
31 std::is_convertible_v<const volatile Derived*, const volatile Base*>;
32};
33
34} // namespace Common
diff --git a/src/common/detached_tasks.cpp b/src/common/detached_tasks.cpp
index f268d6021..f2b4939df 100644
--- a/src/common/detached_tasks.cpp
+++ b/src/common/detached_tasks.cpp
@@ -34,8 +34,7 @@ void DetachedTasks::AddTask(std::function<void()> task) {
34 std::unique_lock lock{instance->mutex}; 34 std::unique_lock lock{instance->mutex};
35 --instance->count; 35 --instance->count;
36 std::notify_all_at_thread_exit(instance->cv, std::move(lock)); 36 std::notify_all_at_thread_exit(instance->cv, std::move(lock));
37 }) 37 }).detach();
38 .detach();
39} 38}
40 39
41} // namespace Common 40} // namespace Common
diff --git a/src/common/dynamic_library.cpp b/src/common/dynamic_library.cpp
index 7ab54e9e4..7f0a10521 100644
--- a/src/common/dynamic_library.cpp
+++ b/src/common/dynamic_library.cpp
@@ -21,7 +21,7 @@ namespace Common {
21DynamicLibrary::DynamicLibrary() = default; 21DynamicLibrary::DynamicLibrary() = default;
22 22
23DynamicLibrary::DynamicLibrary(const char* filename) { 23DynamicLibrary::DynamicLibrary(const char* filename) {
24 Open(filename); 24 void(Open(filename));
25} 25}
26 26
27DynamicLibrary::DynamicLibrary(DynamicLibrary&& rhs) noexcept 27DynamicLibrary::DynamicLibrary(DynamicLibrary&& rhs) noexcept
diff --git a/src/common/dynamic_library.h b/src/common/dynamic_library.h
index 2a06372fd..3512da940 100644
--- a/src/common/dynamic_library.h
+++ b/src/common/dynamic_library.h
@@ -33,7 +33,7 @@ public:
33 ~DynamicLibrary(); 33 ~DynamicLibrary();
34 34
35 /// Returns the specified library name with the platform-specific suffix added. 35 /// Returns the specified library name with the platform-specific suffix added.
36 static std::string GetUnprefixedFilename(const char* filename); 36 [[nodiscard]] static std::string GetUnprefixedFilename(const char* filename);
37 37
38 /// Returns the specified library name in platform-specific format. 38 /// Returns the specified library name in platform-specific format.
39 /// Major/minor versions will not be included if set to -1. 39 /// Major/minor versions will not be included if set to -1.
@@ -41,28 +41,29 @@ public:
41 /// Windows: LIBNAME-MAJOR-MINOR.dll 41 /// Windows: LIBNAME-MAJOR-MINOR.dll
42 /// Linux: libLIBNAME.so.MAJOR.MINOR 42 /// Linux: libLIBNAME.so.MAJOR.MINOR
43 /// Mac: libLIBNAME.MAJOR.MINOR.dylib 43 /// Mac: libLIBNAME.MAJOR.MINOR.dylib
44 static std::string GetVersionedFilename(const char* libname, int major = -1, int minor = -1); 44 [[nodiscard]] static std::string GetVersionedFilename(const char* libname, int major = -1,
45 int minor = -1);
45 46
46 /// Returns true if a module is loaded, otherwise false. 47 /// Returns true if a module is loaded, otherwise false.
47 bool IsOpen() const { 48 [[nodiscard]] bool IsOpen() const {
48 return handle != nullptr; 49 return handle != nullptr;
49 } 50 }
50 51
51 /// Loads (or replaces) the handle with the specified library file name. 52 /// Loads (or replaces) the handle with the specified library file name.
52 /// Returns true if the library was loaded and can be used. 53 /// Returns true if the library was loaded and can be used.
53 bool Open(const char* filename); 54 [[nodiscard]] bool Open(const char* filename);
54 55
55 /// Unloads the library, any function pointers from this library are no longer valid. 56 /// Unloads the library, any function pointers from this library are no longer valid.
56 void Close(); 57 void Close();
57 58
58 /// Returns the address of the specified symbol (function or variable) as an untyped pointer. 59 /// Returns the address of the specified symbol (function or variable) as an untyped pointer.
59 /// If the specified symbol does not exist in this library, nullptr is returned. 60 /// If the specified symbol does not exist in this library, nullptr is returned.
60 void* GetSymbolAddress(const char* name) const; 61 [[nodiscard]] void* GetSymbolAddress(const char* name) const;
61 62
62 /// Obtains the address of the specified symbol, automatically casting to the correct type. 63 /// Obtains the address of the specified symbol, automatically casting to the correct type.
63 /// Returns true if the symbol was found and assigned, otherwise false. 64 /// Returns true if the symbol was found and assigned, otherwise false.
64 template <typename T> 65 template <typename T>
65 bool GetSymbol(const char* name, T* ptr) const { 66 [[nodiscard]] bool GetSymbol(const char* name, T* ptr) const {
66 *ptr = reinterpret_cast<T>(GetSymbolAddress(name)); 67 *ptr = reinterpret_cast<T>(GetSymbolAddress(name));
67 return *ptr != nullptr; 68 return *ptr != nullptr;
68 } 69 }
diff --git a/src/common/fiber.cpp b/src/common/fiber.cpp
new file mode 100644
index 000000000..3e3029cd1
--- /dev/null
+++ b/src/common/fiber.cpp
@@ -0,0 +1,233 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/assert.h"
6#include "common/fiber.h"
7#include "common/spin_lock.h"
8
9#if defined(_WIN32) || defined(WIN32)
10#include <windows.h>
11#else
12#include <boost/context/detail/fcontext.hpp>
13#endif
14
15namespace Common {
16
17constexpr std::size_t default_stack_size = 256 * 1024; // 256kb
18
19struct Fiber::FiberImpl {
20 SpinLock guard{};
21 std::function<void(void*)> entry_point;
22 std::function<void(void*)> rewind_point;
23 void* rewind_parameter{};
24 void* start_parameter{};
25 std::shared_ptr<Fiber> previous_fiber;
26 bool is_thread_fiber{};
27 bool released{};
28
29#if defined(_WIN32) || defined(WIN32)
30 LPVOID handle = nullptr;
31 LPVOID rewind_handle = nullptr;
32#else
33 alignas(64) std::array<u8, default_stack_size> stack;
34 alignas(64) std::array<u8, default_stack_size> rewind_stack;
35 u8* stack_limit;
36 u8* rewind_stack_limit;
37 boost::context::detail::fcontext_t context;
38 boost::context::detail::fcontext_t rewind_context;
39#endif
40};
41
42void Fiber::SetStartParameter(void* new_parameter) {
43 impl->start_parameter = new_parameter;
44}
45
46void Fiber::SetRewindPoint(std::function<void(void*)>&& rewind_func, void* rewind_param) {
47 impl->rewind_point = std::move(rewind_func);
48 impl->rewind_parameter = rewind_param;
49}
50
51#if defined(_WIN32) || defined(WIN32)
52
53void Fiber::Start() {
54 ASSERT(impl->previous_fiber != nullptr);
55 impl->previous_fiber->impl->guard.unlock();
56 impl->previous_fiber.reset();
57 impl->entry_point(impl->start_parameter);
58 UNREACHABLE();
59}
60
61void Fiber::OnRewind() {
62 ASSERT(impl->handle != nullptr);
63 DeleteFiber(impl->handle);
64 impl->handle = impl->rewind_handle;
65 impl->rewind_handle = nullptr;
66 impl->rewind_point(impl->rewind_parameter);
67 UNREACHABLE();
68}
69
70void Fiber::FiberStartFunc(void* fiber_parameter) {
71 auto* fiber = static_cast<Fiber*>(fiber_parameter);
72 fiber->Start();
73}
74
75void Fiber::RewindStartFunc(void* fiber_parameter) {
76 auto* fiber = static_cast<Fiber*>(fiber_parameter);
77 fiber->OnRewind();
78}
79
80Fiber::Fiber(std::function<void(void*)>&& entry_point_func, void* start_parameter)
81 : impl{std::make_unique<FiberImpl>()} {
82 impl->entry_point = std::move(entry_point_func);
83 impl->start_parameter = start_parameter;
84 impl->handle = CreateFiber(default_stack_size, &FiberStartFunc, this);
85}
86
87Fiber::Fiber() : impl{std::make_unique<FiberImpl>()} {}
88
89Fiber::~Fiber() {
90 if (impl->released) {
91 return;
92 }
93 // Make sure the Fiber is not being used
94 const bool locked = impl->guard.try_lock();
95 ASSERT_MSG(locked, "Destroying a fiber that's still running");
96 if (locked) {
97 impl->guard.unlock();
98 }
99 DeleteFiber(impl->handle);
100}
101
102void Fiber::Exit() {
103 ASSERT_MSG(impl->is_thread_fiber, "Exitting non main thread fiber");
104 if (!impl->is_thread_fiber) {
105 return;
106 }
107 ConvertFiberToThread();
108 impl->guard.unlock();
109 impl->released = true;
110}
111
112void Fiber::Rewind() {
113 ASSERT(impl->rewind_point);
114 ASSERT(impl->rewind_handle == nullptr);
115 impl->rewind_handle = CreateFiber(default_stack_size, &RewindStartFunc, this);
116 SwitchToFiber(impl->rewind_handle);
117}
118
119void Fiber::YieldTo(std::shared_ptr<Fiber> from, std::shared_ptr<Fiber> to) {
120 ASSERT_MSG(from != nullptr, "Yielding fiber is null!");
121 ASSERT_MSG(to != nullptr, "Next fiber is null!");
122 to->impl->guard.lock();
123 to->impl->previous_fiber = from;
124 SwitchToFiber(to->impl->handle);
125 ASSERT(from->impl->previous_fiber != nullptr);
126 from->impl->previous_fiber->impl->guard.unlock();
127 from->impl->previous_fiber.reset();
128}
129
130std::shared_ptr<Fiber> Fiber::ThreadToFiber() {
131 std::shared_ptr<Fiber> fiber = std::shared_ptr<Fiber>{new Fiber()};
132 fiber->impl->guard.lock();
133 fiber->impl->handle = ConvertThreadToFiber(nullptr);
134 fiber->impl->is_thread_fiber = true;
135 return fiber;
136}
137
138#else
139
140void Fiber::Start(boost::context::detail::transfer_t& transfer) {
141 ASSERT(impl->previous_fiber != nullptr);
142 impl->previous_fiber->impl->context = transfer.fctx;
143 impl->previous_fiber->impl->guard.unlock();
144 impl->previous_fiber.reset();
145 impl->entry_point(impl->start_parameter);
146 UNREACHABLE();
147}
148
149void Fiber::OnRewind([[maybe_unused]] boost::context::detail::transfer_t& transfer) {
150 ASSERT(impl->context != nullptr);
151 impl->context = impl->rewind_context;
152 impl->rewind_context = nullptr;
153 u8* tmp = impl->stack_limit;
154 impl->stack_limit = impl->rewind_stack_limit;
155 impl->rewind_stack_limit = tmp;
156 impl->rewind_point(impl->rewind_parameter);
157 UNREACHABLE();
158}
159
160void Fiber::FiberStartFunc(boost::context::detail::transfer_t transfer) {
161 auto* fiber = static_cast<Fiber*>(transfer.data);
162 fiber->Start(transfer);
163}
164
165void Fiber::RewindStartFunc(boost::context::detail::transfer_t transfer) {
166 auto* fiber = static_cast<Fiber*>(transfer.data);
167 fiber->OnRewind(transfer);
168}
169
170Fiber::Fiber(std::function<void(void*)>&& entry_point_func, void* start_parameter)
171 : impl{std::make_unique<FiberImpl>()} {
172 impl->entry_point = std::move(entry_point_func);
173 impl->start_parameter = start_parameter;
174 impl->stack_limit = impl->stack.data();
175 impl->rewind_stack_limit = impl->rewind_stack.data();
176 u8* stack_base = impl->stack_limit + default_stack_size;
177 impl->context =
178 boost::context::detail::make_fcontext(stack_base, impl->stack.size(), FiberStartFunc);
179}
180
181Fiber::Fiber() : impl{std::make_unique<FiberImpl>()} {}
182
183Fiber::~Fiber() {
184 if (impl->released) {
185 return;
186 }
187 // Make sure the Fiber is not being used
188 const bool locked = impl->guard.try_lock();
189 ASSERT_MSG(locked, "Destroying a fiber that's still running");
190 if (locked) {
191 impl->guard.unlock();
192 }
193}
194
195void Fiber::Exit() {
196 ASSERT_MSG(impl->is_thread_fiber, "Exitting non main thread fiber");
197 if (!impl->is_thread_fiber) {
198 return;
199 }
200 impl->guard.unlock();
201 impl->released = true;
202}
203
204void Fiber::Rewind() {
205 ASSERT(impl->rewind_point);
206 ASSERT(impl->rewind_context == nullptr);
207 u8* stack_base = impl->rewind_stack_limit + default_stack_size;
208 impl->rewind_context =
209 boost::context::detail::make_fcontext(stack_base, impl->stack.size(), RewindStartFunc);
210 boost::context::detail::jump_fcontext(impl->rewind_context, this);
211}
212
213void Fiber::YieldTo(std::shared_ptr<Fiber> from, std::shared_ptr<Fiber> to) {
214 ASSERT_MSG(from != nullptr, "Yielding fiber is null!");
215 ASSERT_MSG(to != nullptr, "Next fiber is null!");
216 to->impl->guard.lock();
217 to->impl->previous_fiber = from;
218 auto transfer = boost::context::detail::jump_fcontext(to->impl->context, to.get());
219 ASSERT(from->impl->previous_fiber != nullptr);
220 from->impl->previous_fiber->impl->context = transfer.fctx;
221 from->impl->previous_fiber->impl->guard.unlock();
222 from->impl->previous_fiber.reset();
223}
224
225std::shared_ptr<Fiber> Fiber::ThreadToFiber() {
226 std::shared_ptr<Fiber> fiber = std::shared_ptr<Fiber>{new Fiber()};
227 fiber->impl->guard.lock();
228 fiber->impl->is_thread_fiber = true;
229 return fiber;
230}
231
232#endif
233} // namespace Common
diff --git a/src/common/fiber.h b/src/common/fiber.h
new file mode 100644
index 000000000..5323e8579
--- /dev/null
+++ b/src/common/fiber.h
@@ -0,0 +1,78 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <functional>
8#include <memory>
9
10#if !defined(_WIN32) && !defined(WIN32)
11namespace boost::context::detail {
12struct transfer_t;
13}
14#endif
15
16namespace Common {
17
18/**
19 * Fiber class
20 * a fiber is a userspace thread with it's own context. They can be used to
21 * implement coroutines, emulated threading systems and certain asynchronous
22 * patterns.
23 *
24 * This class implements fibers at a low level, thus allowing greater freedom
25 * to implement such patterns. This fiber class is 'threadsafe' only one fiber
26 * can be running at a time and threads will be locked while trying to yield to
27 * a running fiber until it yields. WARNING exchanging two running fibers between
28 * threads will cause a deadlock. In order to prevent a deadlock, each thread should
29 * have an intermediary fiber, you switch to the intermediary fiber of the current
30 * thread and then from it switch to the expected fiber. This way you can exchange
31 * 2 fibers within 2 different threads.
32 */
33class Fiber {
34public:
35 Fiber(std::function<void(void*)>&& entry_point_func, void* start_parameter);
36 ~Fiber();
37
38 Fiber(const Fiber&) = delete;
39 Fiber& operator=(const Fiber&) = delete;
40
41 Fiber(Fiber&&) = default;
42 Fiber& operator=(Fiber&&) = default;
43
44 /// Yields control from Fiber 'from' to Fiber 'to'
45 /// Fiber 'from' must be the currently running fiber.
46 static void YieldTo(std::shared_ptr<Fiber> from, std::shared_ptr<Fiber> to);
47 [[nodiscard]] static std::shared_ptr<Fiber> ThreadToFiber();
48
49 void SetRewindPoint(std::function<void(void*)>&& rewind_func, void* rewind_param);
50
51 void Rewind();
52
53 /// Only call from main thread's fiber
54 void Exit();
55
56 /// Changes the start parameter of the fiber. Has no effect if the fiber already started
57 void SetStartParameter(void* new_parameter);
58
59private:
60 Fiber();
61
62#if defined(_WIN32) || defined(WIN32)
63 void OnRewind();
64 void Start();
65 static void FiberStartFunc(void* fiber_parameter);
66 static void RewindStartFunc(void* fiber_parameter);
67#else
68 void OnRewind(boost::context::detail::transfer_t& transfer);
69 void Start(boost::context::detail::transfer_t& transfer);
70 static void FiberStartFunc(boost::context::detail::transfer_t transfer);
71 static void RewindStartFunc(boost::context::detail::transfer_t transfer);
72#endif
73
74 struct FiberImpl;
75 std::unique_ptr<FiberImpl> impl;
76};
77
78} // namespace Common
diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp
index 35eee0096..18fbfa25b 100644
--- a/src/common/file_util.cpp
+++ b/src/common/file_util.cpp
@@ -74,7 +74,7 @@
74// This namespace has various generic functions related to files and paths. 74// This namespace has various generic functions related to files and paths.
75// The code still needs a ton of cleanup. 75// The code still needs a ton of cleanup.
76// REMEMBER: strdup considered harmful! 76// REMEMBER: strdup considered harmful!
77namespace FileUtil { 77namespace Common::FS {
78 78
79// Remove any ending forward slashes from directory paths 79// Remove any ending forward slashes from directory paths
80// Modifies argument. 80// Modifies argument.
@@ -196,7 +196,7 @@ bool CreateFullPath(const std::string& fullPath) {
196 int panicCounter = 100; 196 int panicCounter = 100;
197 LOG_TRACE(Common_Filesystem, "path {}", fullPath); 197 LOG_TRACE(Common_Filesystem, "path {}", fullPath);
198 198
199 if (FileUtil::Exists(fullPath)) { 199 if (Exists(fullPath)) {
200 LOG_DEBUG(Common_Filesystem, "path exists {}", fullPath); 200 LOG_DEBUG(Common_Filesystem, "path exists {}", fullPath);
201 return true; 201 return true;
202 } 202 }
@@ -212,7 +212,7 @@ bool CreateFullPath(const std::string& fullPath) {
212 212
213 // Include the '/' so the first call is CreateDir("/") rather than CreateDir("") 213 // Include the '/' so the first call is CreateDir("/") rather than CreateDir("")
214 std::string const subPath(fullPath.substr(0, position + 1)); 214 std::string const subPath(fullPath.substr(0, position + 1));
215 if (!FileUtil::IsDirectory(subPath) && !FileUtil::CreateDir(subPath)) { 215 if (!IsDirectory(subPath) && !CreateDir(subPath)) {
216 LOG_ERROR(Common, "CreateFullPath: directory creation failed"); 216 LOG_ERROR(Common, "CreateFullPath: directory creation failed");
217 return false; 217 return false;
218 } 218 }
@@ -231,7 +231,7 @@ bool DeleteDir(const std::string& filename) {
231 LOG_TRACE(Common_Filesystem, "directory {}", filename); 231 LOG_TRACE(Common_Filesystem, "directory {}", filename);
232 232
233 // check if a directory 233 // check if a directory
234 if (!FileUtil::IsDirectory(filename)) { 234 if (!IsDirectory(filename)) {
235 LOG_ERROR(Common_Filesystem, "Not a directory {}", filename); 235 LOG_ERROR(Common_Filesystem, "Not a directory {}", filename);
236 return false; 236 return false;
237 } 237 }
@@ -371,7 +371,7 @@ u64 GetSize(FILE* f) {
371bool CreateEmptyFile(const std::string& filename) { 371bool CreateEmptyFile(const std::string& filename) {
372 LOG_TRACE(Common_Filesystem, "{}", filename); 372 LOG_TRACE(Common_Filesystem, "{}", filename);
373 373
374 if (!FileUtil::IOFile(filename, "wb").IsOpen()) { 374 if (!IOFile(filename, "wb").IsOpen()) {
375 LOG_ERROR(Common_Filesystem, "failed {}: {}", filename, GetLastErrorMsg()); 375 LOG_ERROR(Common_Filesystem, "failed {}: {}", filename, GetLastErrorMsg());
376 return false; 376 return false;
377 } 377 }
@@ -472,13 +472,14 @@ u64 ScanDirectoryTree(const std::string& directory, FSTEntry& parent_entry,
472} 472}
473 473
474bool DeleteDirRecursively(const std::string& directory, unsigned int recursion) { 474bool DeleteDirRecursively(const std::string& directory, unsigned int recursion) {
475 const auto callback = [recursion](u64* num_entries_out, const std::string& directory, 475 const auto callback = [recursion](u64*, const std::string& directory,
476 const std::string& virtual_name) -> bool { 476 const std::string& virtual_name) {
477 std::string new_path = directory + DIR_SEP_CHR + virtual_name; 477 const std::string new_path = directory + DIR_SEP_CHR + virtual_name;
478 478
479 if (IsDirectory(new_path)) { 479 if (IsDirectory(new_path)) {
480 if (recursion == 0) 480 if (recursion == 0) {
481 return false; 481 return false;
482 }
482 return DeleteDirRecursively(new_path, recursion - 1); 483 return DeleteDirRecursively(new_path, recursion - 1);
483 } 484 }
484 return Delete(new_path); 485 return Delete(new_path);
@@ -488,29 +489,35 @@ bool DeleteDirRecursively(const std::string& directory, unsigned int recursion)
488 return false; 489 return false;
489 490
490 // Delete the outermost directory 491 // Delete the outermost directory
491 FileUtil::DeleteDir(directory); 492 DeleteDir(directory);
492 return true; 493 return true;
493} 494}
494 495
495void CopyDir(const std::string& source_path, const std::string& dest_path) { 496void CopyDir([[maybe_unused]] const std::string& source_path,
497 [[maybe_unused]] const std::string& dest_path) {
496#ifndef _WIN32 498#ifndef _WIN32
497 if (source_path == dest_path) 499 if (source_path == dest_path) {
498 return; 500 return;
499 if (!FileUtil::Exists(source_path)) 501 }
502 if (!Exists(source_path)) {
500 return; 503 return;
501 if (!FileUtil::Exists(dest_path)) 504 }
502 FileUtil::CreateFullPath(dest_path); 505 if (!Exists(dest_path)) {
506 CreateFullPath(dest_path);
507 }
503 508
504 DIR* dirp = opendir(source_path.c_str()); 509 DIR* dirp = opendir(source_path.c_str());
505 if (!dirp) 510 if (!dirp) {
506 return; 511 return;
512 }
507 513
508 while (struct dirent* result = readdir(dirp)) { 514 while (struct dirent* result = readdir(dirp)) {
509 const std::string virtualName(result->d_name); 515 const std::string virtualName(result->d_name);
510 // check for "." and ".." 516 // check for "." and ".."
511 if (((virtualName[0] == '.') && (virtualName[1] == '\0')) || 517 if (((virtualName[0] == '.') && (virtualName[1] == '\0')) ||
512 ((virtualName[0] == '.') && (virtualName[1] == '.') && (virtualName[2] == '\0'))) 518 ((virtualName[0] == '.') && (virtualName[1] == '.') && (virtualName[2] == '\0'))) {
513 continue; 519 continue;
520 }
514 521
515 std::string source, dest; 522 std::string source, dest;
516 source = source_path + virtualName; 523 source = source_path + virtualName;
@@ -518,11 +525,13 @@ void CopyDir(const std::string& source_path, const std::string& dest_path) {
518 if (IsDirectory(source)) { 525 if (IsDirectory(source)) {
519 source += '/'; 526 source += '/';
520 dest += '/'; 527 dest += '/';
521 if (!FileUtil::Exists(dest)) 528 if (!Exists(dest)) {
522 FileUtil::CreateFullPath(dest); 529 CreateFullPath(dest);
530 }
523 CopyDir(source, dest); 531 CopyDir(source, dest);
524 } else if (!FileUtil::Exists(dest)) 532 } else if (!Exists(dest)) {
525 FileUtil::Copy(source, dest); 533 Copy(source, dest);
534 }
526 } 535 }
527 closedir(dirp); 536 closedir(dirp);
528#endif 537#endif
@@ -538,7 +547,7 @@ std::optional<std::string> GetCurrentDir() {
538 if (!dir) { 547 if (!dir) {
539#endif 548#endif
540 LOG_ERROR(Common_Filesystem, "GetCurrentDirectory failed: {}", GetLastErrorMsg()); 549 LOG_ERROR(Common_Filesystem, "GetCurrentDirectory failed: {}", GetLastErrorMsg());
541 return {}; 550 return std::nullopt;
542 } 551 }
543#ifdef _WIN32 552#ifdef _WIN32
544 std::string strDir = Common::UTF16ToUTF8(dir); 553 std::string strDir = Common::UTF16ToUTF8(dir);
@@ -668,7 +677,7 @@ const std::string& GetUserPath(UserPath path, const std::string& new_path) {
668 if (user_path.empty()) { 677 if (user_path.empty()) {
669#ifdef _WIN32 678#ifdef _WIN32
670 user_path = GetExeDirectory() + DIR_SEP USERDATA_DIR DIR_SEP; 679 user_path = GetExeDirectory() + DIR_SEP USERDATA_DIR DIR_SEP;
671 if (!FileUtil::IsDirectory(user_path)) { 680 if (!IsDirectory(user_path)) {
672 user_path = AppDataRoamingDirectory() + DIR_SEP EMU_DATA_DIR DIR_SEP; 681 user_path = AppDataRoamingDirectory() + DIR_SEP EMU_DATA_DIR DIR_SEP;
673 } else { 682 } else {
674 LOG_INFO(Common_Filesystem, "Using the local user directory"); 683 LOG_INFO(Common_Filesystem, "Using the local user directory");
@@ -677,7 +686,7 @@ const std::string& GetUserPath(UserPath path, const std::string& new_path) {
677 paths.emplace(UserPath::ConfigDir, user_path + CONFIG_DIR DIR_SEP); 686 paths.emplace(UserPath::ConfigDir, user_path + CONFIG_DIR DIR_SEP);
678 paths.emplace(UserPath::CacheDir, user_path + CACHE_DIR DIR_SEP); 687 paths.emplace(UserPath::CacheDir, user_path + CACHE_DIR DIR_SEP);
679#else 688#else
680 if (FileUtil::Exists(ROOT_DIR DIR_SEP USERDATA_DIR)) { 689 if (Exists(ROOT_DIR DIR_SEP USERDATA_DIR)) {
681 user_path = ROOT_DIR DIR_SEP USERDATA_DIR DIR_SEP; 690 user_path = ROOT_DIR DIR_SEP USERDATA_DIR DIR_SEP;
682 paths.emplace(UserPath::ConfigDir, user_path + CONFIG_DIR DIR_SEP); 691 paths.emplace(UserPath::ConfigDir, user_path + CONFIG_DIR DIR_SEP);
683 paths.emplace(UserPath::CacheDir, user_path + CACHE_DIR DIR_SEP); 692 paths.emplace(UserPath::CacheDir, user_path + CACHE_DIR DIR_SEP);
@@ -695,6 +704,7 @@ const std::string& GetUserPath(UserPath path, const std::string& new_path) {
695 paths.emplace(UserPath::NANDDir, user_path + NAND_DIR DIR_SEP); 704 paths.emplace(UserPath::NANDDir, user_path + NAND_DIR DIR_SEP);
696 paths.emplace(UserPath::LoadDir, user_path + LOAD_DIR DIR_SEP); 705 paths.emplace(UserPath::LoadDir, user_path + LOAD_DIR DIR_SEP);
697 paths.emplace(UserPath::DumpDir, user_path + DUMP_DIR DIR_SEP); 706 paths.emplace(UserPath::DumpDir, user_path + DUMP_DIR DIR_SEP);
707 paths.emplace(UserPath::ScreenshotsDir, user_path + SCREENSHOTS_DIR DIR_SEP);
698 paths.emplace(UserPath::ShaderDir, user_path + SHADER_DIR DIR_SEP); 708 paths.emplace(UserPath::ShaderDir, user_path + SHADER_DIR DIR_SEP);
699 paths.emplace(UserPath::SysDataDir, user_path + SYSDATA_DIR DIR_SEP); 709 paths.emplace(UserPath::SysDataDir, user_path + SYSDATA_DIR DIR_SEP);
700 paths.emplace(UserPath::KeysDir, user_path + KEYS_DIR DIR_SEP); 710 paths.emplace(UserPath::KeysDir, user_path + KEYS_DIR DIR_SEP);
@@ -703,7 +713,7 @@ const std::string& GetUserPath(UserPath path, const std::string& new_path) {
703 } 713 }
704 714
705 if (!new_path.empty()) { 715 if (!new_path.empty()) {
706 if (!FileUtil::IsDirectory(new_path)) { 716 if (!IsDirectory(new_path)) {
707 LOG_ERROR(Common_Filesystem, "Invalid path specified {}", new_path); 717 LOG_ERROR(Common_Filesystem, "Invalid path specified {}", new_path);
708 return paths[path]; 718 return paths[path];
709 } else { 719 } else {
@@ -764,21 +774,23 @@ std::size_t ReadFileToString(bool text_file, const std::string& filename, std::s
764 774
765void SplitFilename83(const std::string& filename, std::array<char, 9>& short_name, 775void SplitFilename83(const std::string& filename, std::array<char, 9>& short_name,
766 std::array<char, 4>& extension) { 776 std::array<char, 4>& extension) {
767 const std::string forbidden_characters = ".\"/\\[]:;=, "; 777 static constexpr std::string_view forbidden_characters = ".\"/\\[]:;=, ";
768 778
769 // On a FAT32 partition, 8.3 names are stored as a 11 bytes array, filled with spaces. 779 // On a FAT32 partition, 8.3 names are stored as a 11 bytes array, filled with spaces.
770 short_name = {{' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '\0'}}; 780 short_name = {{' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '\0'}};
771 extension = {{' ', ' ', ' ', '\0'}}; 781 extension = {{' ', ' ', ' ', '\0'}};
772 782
773 std::string::size_type point = filename.rfind('.'); 783 auto point = filename.rfind('.');
774 if (point == filename.size() - 1) 784 if (point == filename.size() - 1) {
775 point = filename.rfind('.', point); 785 point = filename.rfind('.', point);
786 }
776 787
777 // Get short name. 788 // Get short name.
778 int j = 0; 789 int j = 0;
779 for (char letter : filename.substr(0, point)) { 790 for (char letter : filename.substr(0, point)) {
780 if (forbidden_characters.find(letter, 0) != std::string::npos) 791 if (forbidden_characters.find(letter, 0) != std::string::npos) {
781 continue; 792 continue;
793 }
782 if (j == 8) { 794 if (j == 8) {
783 // TODO(Link Mauve): also do that for filenames containing a space. 795 // TODO(Link Mauve): also do that for filenames containing a space.
784 // TODO(Link Mauve): handle multiple files having the same short name. 796 // TODO(Link Mauve): handle multiple files having the same short name.
@@ -786,14 +798,15 @@ void SplitFilename83(const std::string& filename, std::array<char, 9>& short_nam
786 short_name[7] = '1'; 798 short_name[7] = '1';
787 break; 799 break;
788 } 800 }
789 short_name[j++] = toupper(letter); 801 short_name[j++] = static_cast<char>(std::toupper(letter));
790 } 802 }
791 803
792 // Get extension. 804 // Get extension.
793 if (point != std::string::npos) { 805 if (point != std::string::npos) {
794 j = 0; 806 j = 0;
795 for (char letter : filename.substr(point + 1, 3)) 807 for (char letter : filename.substr(point + 1, 3)) {
796 extension[j++] = toupper(letter); 808 extension[j++] = static_cast<char>(std::toupper(letter));
809 }
797 } 810 }
798} 811}
799 812
@@ -888,16 +901,23 @@ std::string SanitizePath(std::string_view path_, DirectorySeparator directory_se
888 } 901 }
889 902
890 std::replace(path.begin(), path.end(), type1, type2); 903 std::replace(path.begin(), path.end(), type1, type2);
891 path.erase(std::unique(path.begin(), path.end(), 904
905 auto start = path.begin();
906#ifdef _WIN32
907 // allow network paths which start with a double backslash (e.g. \\server\share)
908 if (start != path.end())
909 ++start;
910#endif
911 path.erase(std::unique(start, path.end(),
892 [type2](char c1, char c2) { return c1 == type2 && c2 == type2; }), 912 [type2](char c1, char c2) { return c1 == type2 && c2 == type2; }),
893 path.end()); 913 path.end());
894 return std::string(RemoveTrailingSlash(path)); 914 return std::string(RemoveTrailingSlash(path));
895} 915}
896 916
897IOFile::IOFile() {} 917IOFile::IOFile() = default;
898 918
899IOFile::IOFile(const std::string& filename, const char openmode[], int flags) { 919IOFile::IOFile(const std::string& filename, const char openmode[], int flags) {
900 Open(filename, openmode, flags); 920 void(Open(filename, openmode, flags));
901} 921}
902 922
903IOFile::~IOFile() { 923IOFile::~IOFile() {
@@ -938,17 +958,18 @@ bool IOFile::Open(const std::string& filename, const char openmode[], int flags)
938} 958}
939 959
940bool IOFile::Close() { 960bool IOFile::Close() {
941 if (!IsOpen() || 0 != std::fclose(m_file)) 961 if (!IsOpen() || 0 != std::fclose(m_file)) {
942 return false; 962 return false;
963 }
943 964
944 m_file = nullptr; 965 m_file = nullptr;
945 return true; 966 return true;
946} 967}
947 968
948u64 IOFile::GetSize() const { 969u64 IOFile::GetSize() const {
949 if (IsOpen()) 970 if (IsOpen()) {
950 return FileUtil::GetSize(m_file); 971 return FS::GetSize(m_file);
951 972 }
952 return 0; 973 return 0;
953} 974}
954 975
@@ -957,9 +978,9 @@ bool IOFile::Seek(s64 off, int origin) const {
957} 978}
958 979
959u64 IOFile::Tell() const { 980u64 IOFile::Tell() const {
960 if (IsOpen()) 981 if (IsOpen()) {
961 return ftello(m_file); 982 return ftello(m_file);
962 983 }
963 return std::numeric_limits<u64>::max(); 984 return std::numeric_limits<u64>::max();
964} 985}
965 986
@@ -967,6 +988,34 @@ bool IOFile::Flush() {
967 return IsOpen() && 0 == std::fflush(m_file); 988 return IsOpen() && 0 == std::fflush(m_file);
968} 989}
969 990
991std::size_t IOFile::ReadImpl(void* data, std::size_t length, std::size_t data_size) const {
992 if (!IsOpen()) {
993 return std::numeric_limits<std::size_t>::max();
994 }
995
996 if (length == 0) {
997 return 0;
998 }
999
1000 DEBUG_ASSERT(data != nullptr);
1001
1002 return std::fread(data, data_size, length, m_file);
1003}
1004
1005std::size_t IOFile::WriteImpl(const void* data, std::size_t length, std::size_t data_size) {
1006 if (!IsOpen()) {
1007 return std::numeric_limits<std::size_t>::max();
1008 }
1009
1010 if (length == 0) {
1011 return 0;
1012 }
1013
1014 DEBUG_ASSERT(data != nullptr);
1015
1016 return std::fwrite(data, data_size, length, m_file);
1017}
1018
970bool IOFile::Resize(u64 size) { 1019bool IOFile::Resize(u64 size) {
971 return IsOpen() && 0 == 1020 return IsOpen() && 0 ==
972#ifdef _WIN32 1021#ifdef _WIN32
@@ -980,4 +1029,4 @@ bool IOFile::Resize(u64 size) {
980 ; 1029 ;
981} 1030}
982 1031
983} // namespace FileUtil 1032} // namespace Common::FS
diff --git a/src/common/file_util.h b/src/common/file_util.h
index cde7ddf2d..840cde2a6 100644
--- a/src/common/file_util.h
+++ b/src/common/file_util.h
@@ -19,7 +19,7 @@
19#include "common/string_util.h" 19#include "common/string_util.h"
20#endif 20#endif
21 21
22namespace FileUtil { 22namespace Common::FS {
23 23
24// User paths for GetUserPath 24// User paths for GetUserPath
25enum class UserPath { 25enum class UserPath {
@@ -32,6 +32,7 @@ enum class UserPath {
32 SDMCDir, 32 SDMCDir,
33 LoadDir, 33 LoadDir,
34 DumpDir, 34 DumpDir,
35 ScreenshotsDir,
35 ShaderDir, 36 ShaderDir,
36 SysDataDir, 37 SysDataDir,
37 UserDir, 38 UserDir,
@@ -47,19 +48,19 @@ struct FSTEntry {
47}; 48};
48 49
49// Returns true if file filename exists 50// Returns true if file filename exists
50bool Exists(const std::string& filename); 51[[nodiscard]] bool Exists(const std::string& filename);
51 52
52// Returns true if filename is a directory 53// Returns true if filename is a directory
53bool IsDirectory(const std::string& filename); 54[[nodiscard]] bool IsDirectory(const std::string& filename);
54 55
55// Returns the size of filename (64bit) 56// Returns the size of filename (64bit)
56u64 GetSize(const std::string& filename); 57[[nodiscard]] u64 GetSize(const std::string& filename);
57 58
58// Overloaded GetSize, accepts file descriptor 59// Overloaded GetSize, accepts file descriptor
59u64 GetSize(const int fd); 60[[nodiscard]] u64 GetSize(int fd);
60 61
61// Overloaded GetSize, accepts FILE* 62// Overloaded GetSize, accepts FILE*
62u64 GetSize(FILE* f); 63[[nodiscard]] u64 GetSize(FILE* f);
63 64
64// Returns true if successful, or path already exists. 65// Returns true if successful, or path already exists.
65bool CreateDir(const std::string& filename); 66bool CreateDir(const std::string& filename);
@@ -119,7 +120,7 @@ u64 ScanDirectoryTree(const std::string& directory, FSTEntry& parent_entry,
119bool DeleteDirRecursively(const std::string& directory, unsigned int recursion = 256); 120bool DeleteDirRecursively(const std::string& directory, unsigned int recursion = 256);
120 121
121// Returns the current directory 122// Returns the current directory
122std::optional<std::string> GetCurrentDir(); 123[[nodiscard]] std::optional<std::string> GetCurrentDir();
123 124
124// Create directory and copy contents (does not overwrite existing files) 125// Create directory and copy contents (does not overwrite existing files)
125void CopyDir(const std::string& source_path, const std::string& dest_path); 126void CopyDir(const std::string& source_path, const std::string& dest_path);
@@ -131,20 +132,20 @@ bool SetCurrentDir(const std::string& directory);
131// directory. To be used in "multi-user" mode (that is, installed). 132// directory. To be used in "multi-user" mode (that is, installed).
132const std::string& GetUserPath(UserPath path, const std::string& new_path = ""); 133const std::string& GetUserPath(UserPath path, const std::string& new_path = "");
133 134
134std::string GetHactoolConfigurationPath(); 135[[nodiscard]] std::string GetHactoolConfigurationPath();
135 136
136std::string GetNANDRegistrationDir(bool system = false); 137[[nodiscard]] std::string GetNANDRegistrationDir(bool system = false);
137 138
138// Returns the path to where the sys file are 139// Returns the path to where the sys file are
139std::string GetSysDirectory(); 140[[nodiscard]] std::string GetSysDirectory();
140 141
141#ifdef __APPLE__ 142#ifdef __APPLE__
142std::string GetBundleDirectory(); 143[[nodiscard]] std::string GetBundleDirectory();
143#endif 144#endif
144 145
145#ifdef _WIN32 146#ifdef _WIN32
146const std::string& GetExeDirectory(); 147[[nodiscard]] const std::string& GetExeDirectory();
147std::string AppDataRoamingDirectory(); 148[[nodiscard]] std::string AppDataRoamingDirectory();
148#endif 149#endif
149 150
150std::size_t WriteStringToFile(bool text_file, const std::string& filename, std::string_view str); 151std::size_t WriteStringToFile(bool text_file, const std::string& filename, std::string_view str);
@@ -163,38 +164,55 @@ void SplitFilename83(const std::string& filename, std::array<char, 9>& short_nam
163 164
164// Splits the path on '/' or '\' and put the components into a vector 165// Splits the path on '/' or '\' and put the components into a vector
165// i.e. "C:\Users\Yuzu\Documents\save.bin" becomes {"C:", "Users", "Yuzu", "Documents", "save.bin" } 166// i.e. "C:\Users\Yuzu\Documents\save.bin" becomes {"C:", "Users", "Yuzu", "Documents", "save.bin" }
166std::vector<std::string> SplitPathComponents(std::string_view filename); 167[[nodiscard]] std::vector<std::string> SplitPathComponents(std::string_view filename);
167 168
168// Gets all of the text up to the last '/' or '\' in the path. 169// Gets all of the text up to the last '/' or '\' in the path.
169std::string_view GetParentPath(std::string_view path); 170[[nodiscard]] std::string_view GetParentPath(std::string_view path);
170 171
171// Gets all of the text after the first '/' or '\' in the path. 172// Gets all of the text after the first '/' or '\' in the path.
172std::string_view GetPathWithoutTop(std::string_view path); 173[[nodiscard]] std::string_view GetPathWithoutTop(std::string_view path);
173 174
174// Gets the filename of the path 175// Gets the filename of the path
175std::string_view GetFilename(std::string_view path); 176[[nodiscard]] std::string_view GetFilename(std::string_view path);
176 177
177// Gets the extension of the filename 178// Gets the extension of the filename
178std::string_view GetExtensionFromFilename(std::string_view name); 179[[nodiscard]] std::string_view GetExtensionFromFilename(std::string_view name);
179 180
180// Removes the final '/' or '\' if one exists 181// Removes the final '/' or '\' if one exists
181std::string_view RemoveTrailingSlash(std::string_view path); 182[[nodiscard]] std::string_view RemoveTrailingSlash(std::string_view path);
182 183
183// Creates a new vector containing indices [first, last) from the original. 184// Creates a new vector containing indices [first, last) from the original.
184template <typename T> 185template <typename T>
185std::vector<T> SliceVector(const std::vector<T>& vector, std::size_t first, std::size_t last) { 186[[nodiscard]] std::vector<T> SliceVector(const std::vector<T>& vector, std::size_t first,
186 if (first >= last) 187 std::size_t last) {
188 if (first >= last) {
187 return {}; 189 return {};
190 }
188 last = std::min<std::size_t>(last, vector.size()); 191 last = std::min<std::size_t>(last, vector.size());
189 return std::vector<T>(vector.begin() + first, vector.begin() + first + last); 192 return std::vector<T>(vector.begin() + first, vector.begin() + first + last);
190} 193}
191 194
192enum class DirectorySeparator { ForwardSlash, BackwardSlash, PlatformDefault }; 195enum class DirectorySeparator {
196 ForwardSlash,
197 BackwardSlash,
198 PlatformDefault,
199};
193 200
194// Removes trailing slash, makes all '\\' into '/', and removes duplicate '/'. Makes '/' into '\\' 201// Removes trailing slash, makes all '\\' into '/', and removes duplicate '/'. Makes '/' into '\\'
195// depending if directory_separator is BackwardSlash or PlatformDefault and running on windows 202// depending if directory_separator is BackwardSlash or PlatformDefault and running on windows
196std::string SanitizePath(std::string_view path, 203[[nodiscard]] std::string SanitizePath(
197 DirectorySeparator directory_separator = DirectorySeparator::ForwardSlash); 204 std::string_view path,
205 DirectorySeparator directory_separator = DirectorySeparator::ForwardSlash);
206
207// To deal with Windows being dumb at Unicode
208template <typename T>
209void OpenFStream(T& fstream, const std::string& filename, std::ios_base::openmode openmode) {
210#ifdef _MSC_VER
211 fstream.open(Common::UTF8ToUTF16W(filename), openmode);
212#else
213 fstream.open(filename, openmode);
214#endif
215}
198 216
199// simple wrapper for cstdlib file functions to 217// simple wrapper for cstdlib file functions to
200// hopefully will make error checking easier 218// hopefully will make error checking easier
@@ -222,22 +240,15 @@ public:
222 static_assert(std::is_trivially_copyable_v<T>, 240 static_assert(std::is_trivially_copyable_v<T>,
223 "Given array does not consist of trivially copyable objects"); 241 "Given array does not consist of trivially copyable objects");
224 242
225 if (!IsOpen()) { 243 return ReadImpl(data, length, sizeof(T));
226 return std::numeric_limits<std::size_t>::max();
227 }
228
229 return std::fread(data, sizeof(T), length, m_file);
230 } 244 }
231 245
232 template <typename T> 246 template <typename T>
233 std::size_t WriteArray(const T* data, std::size_t length) { 247 std::size_t WriteArray(const T* data, std::size_t length) {
234 static_assert(std::is_trivially_copyable_v<T>, 248 static_assert(std::is_trivially_copyable_v<T>,
235 "Given array does not consist of trivially copyable objects"); 249 "Given array does not consist of trivially copyable objects");
236 if (!IsOpen()) {
237 return std::numeric_limits<std::size_t>::max();
238 }
239 250
240 return std::fwrite(data, sizeof(T), length, m_file); 251 return WriteImpl(data, length, sizeof(T));
241 } 252 }
242 253
243 template <typename T> 254 template <typename T>
@@ -262,13 +273,13 @@ public:
262 return WriteArray(str.data(), str.length()); 273 return WriteArray(str.data(), str.length());
263 } 274 }
264 275
265 bool IsOpen() const { 276 [[nodiscard]] bool IsOpen() const {
266 return nullptr != m_file; 277 return nullptr != m_file;
267 } 278 }
268 279
269 bool Seek(s64 off, int origin) const; 280 bool Seek(s64 off, int origin) const;
270 u64 Tell() const; 281 [[nodiscard]] u64 Tell() const;
271 u64 GetSize() const; 282 [[nodiscard]] u64 GetSize() const;
272 bool Resize(u64 size); 283 bool Resize(u64 size);
273 bool Flush(); 284 bool Flush();
274 285
@@ -278,17 +289,10 @@ public:
278 } 289 }
279 290
280private: 291private:
292 std::size_t ReadImpl(void* data, std::size_t length, std::size_t data_size) const;
293 std::size_t WriteImpl(const void* data, std::size_t length, std::size_t data_size);
294
281 std::FILE* m_file = nullptr; 295 std::FILE* m_file = nullptr;
282}; 296};
283 297
284} // namespace FileUtil 298} // namespace Common::FS
285
286// To deal with Windows being dumb at unicode:
287template <typename T>
288void OpenFStream(T& fstream, const std::string& filename, std::ios_base::openmode openmode) {
289#ifdef _MSC_VER
290 fstream.open(Common::UTF8ToUTF16W(filename), openmode);
291#else
292 fstream.open(filename, openmode);
293#endif
294}
diff --git a/src/common/hash.h b/src/common/hash.h
index b2538f3ea..298930702 100644
--- a/src/common/hash.h
+++ b/src/common/hash.h
@@ -5,36 +5,11 @@
5#pragma once 5#pragma once
6 6
7#include <cstddef> 7#include <cstddef>
8#include <cstring>
9#include <utility> 8#include <utility>
10#include <boost/functional/hash.hpp> 9#include <boost/functional/hash.hpp>
11#include "common/cityhash.h"
12#include "common/common_types.h"
13 10
14namespace Common { 11namespace Common {
15 12
16/**
17 * Computes a 64-bit hash over the specified block of data
18 * @param data Block of data to compute hash over
19 * @param len Length of data (in bytes) to compute hash over
20 * @returns 64-bit hash value that was computed over the data block
21 */
22static inline u64 ComputeHash64(const void* data, std::size_t len) {
23 return CityHash64(static_cast<const char*>(data), len);
24}
25
26/**
27 * Computes a 64-bit hash of a struct. In addition to being trivially copyable, it is also critical
28 * that either the struct includes no padding, or that any padding is initialized to a known value
29 * by memsetting the struct to 0 before filling it in.
30 */
31template <typename T>
32static inline u64 ComputeStructHash64(const T& data) {
33 static_assert(std::is_trivially_copyable_v<T>,
34 "Type passed to ComputeStructHash64 must be trivially copyable");
35 return ComputeHash64(&data, sizeof(data));
36}
37
38struct PairHash { 13struct PairHash {
39 template <class T1, class T2> 14 template <class T1, class T2>
40 std::size_t operator()(const std::pair<T1, T2>& pair) const noexcept { 15 std::size_t operator()(const std::pair<T1, T2>& pair) const noexcept {
diff --git a/src/common/hex_util.cpp b/src/common/hex_util.cpp
index c2f6cf0f6..74f52dd11 100644
--- a/src/common/hex_util.cpp
+++ b/src/common/hex_util.cpp
@@ -3,21 +3,9 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "common/hex_util.h" 5#include "common/hex_util.h"
6#include "common/logging/log.h"
7 6
8namespace Common { 7namespace Common {
9 8
10u8 ToHexNibble(char c1) {
11 if (c1 >= 65 && c1 <= 70)
12 return c1 - 55;
13 if (c1 >= 97 && c1 <= 102)
14 return c1 - 87;
15 if (c1 >= 48 && c1 <= 57)
16 return c1 - 48;
17 LOG_ERROR(Common, "Invalid hex digit: 0x{:02X}", c1);
18 return 0;
19}
20
21std::vector<u8> HexStringToVector(std::string_view str, bool little_endian) { 9std::vector<u8> HexStringToVector(std::string_view str, bool little_endian) {
22 std::vector<u8> out(str.size() / 2); 10 std::vector<u8> out(str.size() / 2);
23 if (little_endian) { 11 if (little_endian) {
@@ -30,26 +18,4 @@ std::vector<u8> HexStringToVector(std::string_view str, bool little_endian) {
30 return out; 18 return out;
31} 19}
32 20
33std::array<u8, 16> operator""_array16(const char* str, std::size_t len) {
34 if (len != 32) {
35 LOG_ERROR(Common,
36 "Attempting to parse string to array that is not of correct size (expected=32, "
37 "actual={}).",
38 len);
39 return {};
40 }
41 return HexStringToArray<16>(str);
42}
43
44std::array<u8, 32> operator""_array32(const char* str, std::size_t len) {
45 if (len != 64) {
46 LOG_ERROR(Common,
47 "Attempting to parse string to array that is not of correct size (expected=64, "
48 "actual={}).",
49 len);
50 return {};
51 }
52 return HexStringToArray<32>(str);
53}
54
55} // namespace Common 21} // namespace Common
diff --git a/src/common/hex_util.h b/src/common/hex_util.h
index bb4736f96..a8d414fb8 100644
--- a/src/common/hex_util.h
+++ b/src/common/hex_util.h
@@ -14,25 +14,37 @@
14 14
15namespace Common { 15namespace Common {
16 16
17u8 ToHexNibble(char c1); 17[[nodiscard]] constexpr u8 ToHexNibble(char c) {
18 if (c >= 65 && c <= 70) {
19 return static_cast<u8>(c - 55);
20 }
21
22 if (c >= 97 && c <= 102) {
23 return static_cast<u8>(c - 87);
24 }
25
26 return static_cast<u8>(c - 48);
27}
18 28
19std::vector<u8> HexStringToVector(std::string_view str, bool little_endian); 29[[nodiscard]] std::vector<u8> HexStringToVector(std::string_view str, bool little_endian);
20 30
21template <std::size_t Size, bool le = false> 31template <std::size_t Size, bool le = false>
22std::array<u8, Size> HexStringToArray(std::string_view str) { 32[[nodiscard]] constexpr std::array<u8, Size> HexStringToArray(std::string_view str) {
23 std::array<u8, Size> out{}; 33 std::array<u8, Size> out{};
24 if constexpr (le) { 34 if constexpr (le) {
25 for (std::size_t i = 2 * Size - 2; i <= 2 * Size; i -= 2) 35 for (std::size_t i = 2 * Size - 2; i <= 2 * Size; i -= 2) {
26 out[i / 2] = (ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]); 36 out[i / 2] = static_cast<u8>((ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]));
37 }
27 } else { 38 } else {
28 for (std::size_t i = 0; i < 2 * Size; i += 2) 39 for (std::size_t i = 0; i < 2 * Size; i += 2) {
29 out[i / 2] = (ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]); 40 out[i / 2] = static_cast<u8>((ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]));
41 }
30 } 42 }
31 return out; 43 return out;
32} 44}
33 45
34template <typename ContiguousContainer> 46template <typename ContiguousContainer>
35std::string HexToString(const ContiguousContainer& data, bool upper = true) { 47[[nodiscard]] std::string HexToString(const ContiguousContainer& data, bool upper = true) {
36 static_assert(std::is_same_v<typename ContiguousContainer::value_type, u8>, 48 static_assert(std::is_same_v<typename ContiguousContainer::value_type, u8>,
37 "Underlying type within the contiguous container must be u8."); 49 "Underlying type within the contiguous container must be u8.");
38 50
@@ -48,7 +60,12 @@ std::string HexToString(const ContiguousContainer& data, bool upper = true) {
48 return out; 60 return out;
49} 61}
50 62
51std::array<u8, 0x10> operator"" _array16(const char* str, std::size_t len); 63[[nodiscard]] constexpr std::array<u8, 16> AsArray(const char (&data)[17]) {
52std::array<u8, 0x20> operator"" _array32(const char* str, std::size_t len); 64 return HexStringToArray<16>(data);
65}
66
67[[nodiscard]] constexpr std::array<u8, 32> AsArray(const char (&data)[65]) {
68 return HexStringToArray<32>(data);
69}
53 70
54} // namespace Common 71} // namespace Common
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp
index 04bc3128f..631f64d05 100644
--- a/src/common/logging/backend.cpp
+++ b/src/common/logging/backend.cpp
@@ -23,6 +23,7 @@
23#include "common/logging/text_formatter.h" 23#include "common/logging/text_formatter.h"
24#include "common/string_util.h" 24#include "common/string_util.h"
25#include "common/threadsafe_queue.h" 25#include "common/threadsafe_queue.h"
26#include "core/settings.h"
26 27
27namespace Log { 28namespace Log {
28 29
@@ -113,19 +114,19 @@ private:
113 Entry CreateEntry(Class log_class, Level log_level, const char* filename, unsigned int line_nr, 114 Entry CreateEntry(Class log_class, Level log_level, const char* filename, unsigned int line_nr,
114 const char* function, std::string message) const { 115 const char* function, std::string message) const {
115 using std::chrono::duration_cast; 116 using std::chrono::duration_cast;
117 using std::chrono::microseconds;
116 using std::chrono::steady_clock; 118 using std::chrono::steady_clock;
117 119
118 Entry entry; 120 return {
119 entry.timestamp = 121 .timestamp = duration_cast<microseconds>(steady_clock::now() - time_origin),
120 duration_cast<std::chrono::microseconds>(steady_clock::now() - time_origin); 122 .log_class = log_class,
121 entry.log_class = log_class; 123 .log_level = log_level,
122 entry.log_level = log_level; 124 .filename = filename,
123 entry.filename = filename; 125 .line_num = line_nr,
124 entry.line_num = line_nr; 126 .function = function,
125 entry.function = function; 127 .message = std::move(message),
126 entry.message = std::move(message); 128 .final_entry = false,
127 129 };
128 return entry;
129 } 130 }
130 131
131 std::mutex writing_mutex; 132 std::mutex writing_mutex;
@@ -152,10 +153,19 @@ FileBackend::FileBackend(const std::string& filename)
152void FileBackend::Write(const Entry& entry) { 153void FileBackend::Write(const Entry& entry) {
153 // prevent logs from going over the maximum size (in case its spamming and the user doesn't 154 // prevent logs from going over the maximum size (in case its spamming and the user doesn't
154 // know) 155 // know)
155 constexpr std::size_t MAX_BYTES_WRITTEN = 50 * 1024L * 1024L; 156 constexpr std::size_t MAX_BYTES_WRITTEN = 100 * 1024 * 1024;
156 if (!file.IsOpen() || bytes_written > MAX_BYTES_WRITTEN) { 157 constexpr std::size_t MAX_BYTES_WRITTEN_EXTENDED = 1024 * 1024 * 1024;
158
159 if (!file.IsOpen()) {
157 return; 160 return;
158 } 161 }
162
163 if (Settings::values.extended_logging && bytes_written > MAX_BYTES_WRITTEN_EXTENDED) {
164 return;
165 } else if (!Settings::values.extended_logging && bytes_written > MAX_BYTES_WRITTEN) {
166 return;
167 }
168
159 bytes_written += file.WriteString(FormatLogMessage(entry).append(1, '\n')); 169 bytes_written += file.WriteString(FormatLogMessage(entry).append(1, '\n'));
160 if (entry.log_level >= Level::Error) { 170 if (entry.log_level >= Level::Error) {
161 file.Flush(); 171 file.Flush();
@@ -222,6 +232,7 @@ void DebuggerBackend::Write(const Entry& entry) {
222 SUB(Service, NPNS) \ 232 SUB(Service, NPNS) \
223 SUB(Service, NS) \ 233 SUB(Service, NS) \
224 SUB(Service, NVDRV) \ 234 SUB(Service, NVDRV) \
235 SUB(Service, OLSC) \
225 SUB(Service, PCIE) \ 236 SUB(Service, PCIE) \
226 SUB(Service, PCTL) \ 237 SUB(Service, PCTL) \
227 SUB(Service, PCV) \ 238 SUB(Service, PCV) \
@@ -274,7 +285,6 @@ const char* GetLogClassName(Class log_class) {
274 case Class::Count: 285 case Class::Count:
275 break; 286 break;
276 } 287 }
277 UNREACHABLE();
278 return "Invalid"; 288 return "Invalid";
279} 289}
280 290
@@ -293,7 +303,6 @@ const char* GetLevelName(Level log_level) {
293 break; 303 break;
294 } 304 }
295#undef LVL 305#undef LVL
296 UNREACHABLE();
297 return "Invalid"; 306 return "Invalid";
298} 307}
299 308
diff --git a/src/common/logging/backend.h b/src/common/logging/backend.h
index fc338c70d..da1c2f185 100644
--- a/src/common/logging/backend.h
+++ b/src/common/logging/backend.h
@@ -21,19 +21,13 @@ class Filter;
21 */ 21 */
22struct Entry { 22struct Entry {
23 std::chrono::microseconds timestamp; 23 std::chrono::microseconds timestamp;
24 Class log_class; 24 Class log_class{};
25 Level log_level; 25 Level log_level{};
26 const char* filename; 26 const char* filename = nullptr;
27 unsigned int line_num; 27 unsigned int line_num = 0;
28 std::string function; 28 std::string function;
29 std::string message; 29 std::string message;
30 bool final_entry = false; 30 bool final_entry = false;
31
32 Entry() = default;
33 Entry(Entry&& o) = default;
34
35 Entry& operator=(Entry&& o) = default;
36 Entry& operator=(const Entry& o) = default;
37}; 31};
38 32
39/** 33/**
@@ -100,7 +94,7 @@ public:
100 void Write(const Entry& entry) override; 94 void Write(const Entry& entry) override;
101 95
102private: 96private:
103 FileUtil::IOFile file; 97 Common::FS::IOFile file;
104 std::size_t bytes_written; 98 std::size_t bytes_written;
105}; 99};
106 100
diff --git a/src/common/logging/log.h b/src/common/logging/log.h
index 13a4f1e30..835894918 100644
--- a/src/common/logging/log.h
+++ b/src/common/logging/log.h
@@ -95,6 +95,7 @@ enum class Class : ClassType {
95 Service_NPNS, ///< The NPNS service 95 Service_NPNS, ///< The NPNS service
96 Service_NS, ///< The NS services 96 Service_NS, ///< The NS services
97 Service_NVDRV, ///< The NVDRV (Nvidia driver) service 97 Service_NVDRV, ///< The NVDRV (Nvidia driver) service
98 Service_OLSC, ///< The OLSC service
98 Service_PCIE, ///< The PCIe service 99 Service_PCIE, ///< The PCIe service
99 Service_PCTL, ///< The PCTL (Parental control) service 100 Service_PCTL, ///< The PCTL (Parental control) service
100 Service_PCV, ///< The PCV service 101 Service_PCV, ///< The PCV service
diff --git a/src/common/lz4_compression.cpp b/src/common/lz4_compression.cpp
index ade6759bb..25700015a 100644
--- a/src/common/lz4_compression.cpp
+++ b/src/common/lz4_compression.cpp
@@ -14,19 +14,19 @@ std::vector<u8> CompressDataLZ4(const u8* source, std::size_t source_size) {
14 ASSERT_MSG(source_size <= LZ4_MAX_INPUT_SIZE, "Source size exceeds LZ4 maximum input size"); 14 ASSERT_MSG(source_size <= LZ4_MAX_INPUT_SIZE, "Source size exceeds LZ4 maximum input size");
15 15
16 const auto source_size_int = static_cast<int>(source_size); 16 const auto source_size_int = static_cast<int>(source_size);
17 const int max_compressed_size = LZ4_compressBound(source_size_int); 17 const auto max_compressed_size = static_cast<std::size_t>(LZ4_compressBound(source_size_int));
18 std::vector<u8> compressed(max_compressed_size); 18 std::vector<u8> compressed(max_compressed_size);
19 19
20 const int compressed_size = LZ4_compress_default(reinterpret_cast<const char*>(source), 20 const int compressed_size = LZ4_compress_default(
21 reinterpret_cast<char*>(compressed.data()), 21 reinterpret_cast<const char*>(source), reinterpret_cast<char*>(compressed.data()),
22 source_size_int, max_compressed_size); 22 source_size_int, static_cast<int>(max_compressed_size));
23 23
24 if (compressed_size <= 0) { 24 if (compressed_size <= 0) {
25 // Compression failed 25 // Compression failed
26 return {}; 26 return {};
27 } 27 }
28 28
29 compressed.resize(compressed_size); 29 compressed.resize(static_cast<std::size_t>(compressed_size));
30 30
31 return compressed; 31 return compressed;
32} 32}
@@ -38,19 +38,19 @@ std::vector<u8> CompressDataLZ4HC(const u8* source, std::size_t source_size,
38 compression_level = std::clamp(compression_level, LZ4HC_CLEVEL_MIN, LZ4HC_CLEVEL_MAX); 38 compression_level = std::clamp(compression_level, LZ4HC_CLEVEL_MIN, LZ4HC_CLEVEL_MAX);
39 39
40 const auto source_size_int = static_cast<int>(source_size); 40 const auto source_size_int = static_cast<int>(source_size);
41 const int max_compressed_size = LZ4_compressBound(source_size_int); 41 const auto max_compressed_size = static_cast<std::size_t>(LZ4_compressBound(source_size_int));
42 std::vector<u8> compressed(max_compressed_size); 42 std::vector<u8> compressed(max_compressed_size);
43 43
44 const int compressed_size = LZ4_compress_HC( 44 const int compressed_size = LZ4_compress_HC(
45 reinterpret_cast<const char*>(source), reinterpret_cast<char*>(compressed.data()), 45 reinterpret_cast<const char*>(source), reinterpret_cast<char*>(compressed.data()),
46 source_size_int, max_compressed_size, compression_level); 46 source_size_int, static_cast<int>(max_compressed_size), compression_level);
47 47
48 if (compressed_size <= 0) { 48 if (compressed_size <= 0) {
49 // Compression failed 49 // Compression failed
50 return {}; 50 return {};
51 } 51 }
52 52
53 compressed.resize(compressed_size); 53 compressed.resize(static_cast<std::size_t>(compressed_size));
54 54
55 return compressed; 55 return compressed;
56} 56}
diff --git a/src/common/lz4_compression.h b/src/common/lz4_compression.h
index 4c16f6e03..87a4be1b0 100644
--- a/src/common/lz4_compression.h
+++ b/src/common/lz4_compression.h
@@ -13,12 +13,12 @@ namespace Common::Compression {
13/** 13/**
14 * Compresses a source memory region with LZ4 and returns the compressed data in a vector. 14 * Compresses a source memory region with LZ4 and returns the compressed data in a vector.
15 * 15 *
16 * @param source the uncompressed source memory region. 16 * @param source The uncompressed source memory region.
17 * @param source_size the size in bytes of the uncompressed source memory region. 17 * @param source_size The size of the uncompressed source memory region.
18 * 18 *
19 * @return the compressed data. 19 * @return the compressed data.
20 */ 20 */
21std::vector<u8> CompressDataLZ4(const u8* source, std::size_t source_size); 21[[nodiscard]] std::vector<u8> CompressDataLZ4(const u8* source, std::size_t source_size);
22 22
23/** 23/**
24 * Utilizes the LZ4 subalgorithm LZ4HC with the specified compression level. Higher compression 24 * Utilizes the LZ4 subalgorithm LZ4HC with the specified compression level. Higher compression
@@ -26,23 +26,24 @@ std::vector<u8> CompressDataLZ4(const u8* source, std::size_t source_size);
26 * compression level has almost no impact on decompression speed. Data compressed with LZ4HC can 26 * compression level has almost no impact on decompression speed. Data compressed with LZ4HC can
27 * also be decompressed with the default LZ4 decompression. 27 * also be decompressed with the default LZ4 decompression.
28 * 28 *
29 * @param source the uncompressed source memory region. 29 * @param source The uncompressed source memory region.
30 * @param source_size the size in bytes of the uncompressed source memory region. 30 * @param source_size The size of the uncompressed source memory region.
31 * @param compression_level the used compression level. Should be between 3 and 12. 31 * @param compression_level The used compression level. Should be between 3 and 12.
32 * 32 *
33 * @return the compressed data. 33 * @return the compressed data.
34 */ 34 */
35std::vector<u8> CompressDataLZ4HC(const u8* source, std::size_t source_size, s32 compression_level); 35[[nodiscard]] std::vector<u8> CompressDataLZ4HC(const u8* source, std::size_t source_size,
36 s32 compression_level);
36 37
37/** 38/**
38 * Utilizes the LZ4 subalgorithm LZ4HC with the highest possible compression level. 39 * Utilizes the LZ4 subalgorithm LZ4HC with the highest possible compression level.
39 * 40 *
40 * @param source the uncompressed source memory region. 41 * @param source The uncompressed source memory region.
41 * @param source_size the size in bytes of the uncompressed source memory region. 42 * @param source_size The size of the uncompressed source memory region
42 * 43 *
43 * @return the compressed data. 44 * @return the compressed data.
44 */ 45 */
45std::vector<u8> CompressDataLZ4HCMax(const u8* source, std::size_t source_size); 46[[nodiscard]] std::vector<u8> CompressDataLZ4HCMax(const u8* source, std::size_t source_size);
46 47
47/** 48/**
48 * Decompresses a source memory region with LZ4 and returns the uncompressed data in a vector. 49 * Decompresses a source memory region with LZ4 and returns the uncompressed data in a vector.
@@ -52,6 +53,7 @@ std::vector<u8> CompressDataLZ4HCMax(const u8* source, std::size_t source_size);
52 * 53 *
53 * @return the decompressed data. 54 * @return the decompressed data.
54 */ 55 */
55std::vector<u8> DecompressDataLZ4(const std::vector<u8>& compressed, std::size_t uncompressed_size); 56[[nodiscard]] std::vector<u8> DecompressDataLZ4(const std::vector<u8>& compressed,
57 std::size_t uncompressed_size);
56 58
57} // namespace Common::Compression \ No newline at end of file 59} // namespace Common::Compression \ No newline at end of file
diff --git a/src/common/math_util.h b/src/common/math_util.h
index 83ef0201f..4c38d8040 100644
--- a/src/common/math_util.h
+++ b/src/common/math_util.h
@@ -9,7 +9,7 @@
9 9
10namespace Common { 10namespace Common {
11 11
12constexpr float PI = 3.14159265f; 12constexpr float PI = 3.1415926535f;
13 13
14template <class T> 14template <class T>
15struct Rectangle { 15struct Rectangle {
@@ -20,40 +20,40 @@ struct Rectangle {
20 20
21 constexpr Rectangle() = default; 21 constexpr Rectangle() = default;
22 22
23 constexpr Rectangle(T left, T top, T right, T bottom) 23 constexpr Rectangle(T left_, T top_, T right_, T bottom_)
24 : left(left), top(top), right(right), bottom(bottom) {} 24 : left(left_), top(top_), right(right_), bottom(bottom_) {}
25 25
26 T GetWidth() const { 26 [[nodiscard]] T GetWidth() const {
27 if constexpr (std::is_floating_point_v<T>) { 27 if constexpr (std::is_floating_point_v<T>) {
28 return std::abs(right - left); 28 return std::abs(right - left);
29 } else { 29 } else {
30 return std::abs(static_cast<std::make_signed_t<T>>(right - left)); 30 return static_cast<T>(std::abs(static_cast<std::make_signed_t<T>>(right - left)));
31 } 31 }
32 } 32 }
33 33
34 T GetHeight() const { 34 [[nodiscard]] T GetHeight() const {
35 if constexpr (std::is_floating_point_v<T>) { 35 if constexpr (std::is_floating_point_v<T>) {
36 return std::abs(bottom - top); 36 return std::abs(bottom - top);
37 } else { 37 } else {
38 return std::abs(static_cast<std::make_signed_t<T>>(bottom - top)); 38 return static_cast<T>(std::abs(static_cast<std::make_signed_t<T>>(bottom - top)));
39 } 39 }
40 } 40 }
41 41
42 Rectangle<T> TranslateX(const T x) const { 42 [[nodiscard]] Rectangle<T> TranslateX(const T x) const {
43 return Rectangle{left + x, top, right + x, bottom}; 43 return Rectangle{left + x, top, right + x, bottom};
44 } 44 }
45 45
46 Rectangle<T> TranslateY(const T y) const { 46 [[nodiscard]] Rectangle<T> TranslateY(const T y) const {
47 return Rectangle{left, top + y, right, bottom + y}; 47 return Rectangle{left, top + y, right, bottom + y};
48 } 48 }
49 49
50 Rectangle<T> Scale(const float s) const { 50 [[nodiscard]] Rectangle<T> Scale(const float s) const {
51 return Rectangle{left, top, static_cast<T>(left + GetWidth() * s), 51 return Rectangle{left, top, static_cast<T>(left + GetWidth() * s),
52 static_cast<T>(top + GetHeight() * s)}; 52 static_cast<T>(top + GetHeight() * s)};
53 } 53 }
54}; 54};
55 55
56template <typename T> 56template <typename T>
57Rectangle(T, T, T, T)->Rectangle<T>; 57Rectangle(T, T, T, T) -> Rectangle<T>;
58 58
59} // namespace Common 59} // namespace Common
diff --git a/src/common/memory_detect.cpp b/src/common/memory_detect.cpp
new file mode 100644
index 000000000..8cff6ec37
--- /dev/null
+++ b/src/common/memory_detect.cpp
@@ -0,0 +1,73 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#ifdef _WIN32
6// clang-format off
7#include <windows.h>
8#include <sysinfoapi.h>
9// clang-format on
10#else
11#include <sys/types.h>
12#if defined(__APPLE__) || defined(__FreeBSD__)
13#include <sys/sysctl.h>
14#elif defined(__linux__)
15#include <sys/sysinfo.h>
16#else
17#include <unistd.h>
18#endif
19#endif
20
21#include "common/memory_detect.h"
22
23namespace Common {
24
25// Detects the RAM and Swapfile sizes
26static MemoryInfo Detect() {
27 MemoryInfo mem_info{};
28
29#ifdef _WIN32
30 MEMORYSTATUSEX memorystatus;
31 memorystatus.dwLength = sizeof(memorystatus);
32 GlobalMemoryStatusEx(&memorystatus);
33 mem_info.TotalPhysicalMemory = memorystatus.ullTotalPhys;
34 mem_info.TotalSwapMemory = memorystatus.ullTotalPageFile - mem_info.TotalPhysicalMemory;
35#elif defined(__APPLE__)
36 u64 ramsize;
37 struct xsw_usage vmusage;
38 std::size_t sizeof_ramsize = sizeof(ramsize);
39 std::size_t sizeof_vmusage = sizeof(vmusage);
40 // hw and vm are defined in sysctl.h
41 // https://github.com/apple/darwin-xnu/blob/master/bsd/sys/sysctl.h#L471
42 // sysctlbyname(const char *, void *, size_t *, void *, size_t);
43 sysctlbyname("hw.memsize", &ramsize, &sizeof_ramsize, nullptr, 0);
44 sysctlbyname("vm.swapusage", &vmusage, &sizeof_vmusage, nullptr, 0);
45 mem_info.TotalPhysicalMemory = ramsize;
46 mem_info.TotalSwapMemory = vmusage.xsu_total;
47#elif defined(__FreeBSD__)
48 u_long physmem, swap_total;
49 std::size_t sizeof_u_long = sizeof(u_long);
50 // sysctlbyname(const char *, void *, size_t *, const void *, size_t);
51 sysctlbyname("hw.physmem", &physmem, &sizeof_u_long, nullptr, 0);
52 sysctlbyname("vm.swap_total", &swap_total, &sizeof_u_long, nullptr, 0);
53 mem_info.TotalPhysicalMemory = physmem;
54 mem_info.TotalSwapMemory = swap_total;
55#elif defined(__linux__)
56 struct sysinfo meminfo;
57 sysinfo(&meminfo);
58 mem_info.TotalPhysicalMemory = meminfo.totalram;
59 mem_info.TotalSwapMemory = meminfo.totalswap;
60#else
61 mem_info.TotalPhysicalMemory = sysconf(_SC_PHYS_PAGES) * sysconf(_SC_PAGE_SIZE);
62 mem_info.TotalSwapMemory = 0;
63#endif
64
65 return mem_info;
66}
67
68const MemoryInfo& GetMemInfo() {
69 static MemoryInfo mem_info = Detect();
70 return mem_info;
71}
72
73} // namespace Common \ No newline at end of file
diff --git a/src/common/memory_detect.h b/src/common/memory_detect.h
new file mode 100644
index 000000000..0f73751c8
--- /dev/null
+++ b/src/common/memory_detect.h
@@ -0,0 +1,22 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/common_types.h"
8
9namespace Common {
10
11struct MemoryInfo {
12 u64 TotalPhysicalMemory{};
13 u64 TotalSwapMemory{};
14};
15
16/**
17 * Gets the memory info of the host system
18 * @return Reference to a MemoryInfo struct with the physical and swap memory sizes in bytes
19 */
20[[nodiscard]] const MemoryInfo& GetMemInfo();
21
22} // namespace Common \ No newline at end of file
diff --git a/src/common/misc.cpp b/src/common/misc.cpp
index 68cb86cd1..1d5393597 100644
--- a/src/common/misc.cpp
+++ b/src/common/misc.cpp
@@ -16,16 +16,23 @@
16// Call directly after the command or use the error num. 16// Call directly after the command or use the error num.
17// This function might change the error code. 17// This function might change the error code.
18std::string GetLastErrorMsg() { 18std::string GetLastErrorMsg() {
19 static const std::size_t buff_size = 255; 19 static constexpr std::size_t buff_size = 255;
20 char err_str[buff_size]; 20 char err_str[buff_size];
21 21
22#ifdef _WIN32 22#ifdef _WIN32
23 FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, nullptr, GetLastError(), 23 FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, nullptr, GetLastError(),
24 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), err_str, buff_size, nullptr); 24 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), err_str, buff_size, nullptr);
25 return std::string(err_str, buff_size);
26#elif defined(__GLIBC__) && (_GNU_SOURCE || (_POSIX_C_SOURCE < 200112L && _XOPEN_SOURCE < 600))
27 // Thread safe (GNU-specific)
28 const char* str = strerror_r(errno, err_str, buff_size);
29 return std::string(str);
25#else 30#else
26 // Thread safe (XSI-compliant) 31 // Thread safe (XSI-compliant)
27 strerror_r(errno, err_str, buff_size); 32 const int success = strerror_r(errno, err_str, buff_size);
33 if (success != 0) {
34 return {};
35 }
36 return std::string(err_str);
28#endif 37#endif
29
30 return std::string(err_str, buff_size);
31} 38}
diff --git a/src/common/multi_level_queue.h b/src/common/multi_level_queue.h
index 50acfdbf2..4b305bf40 100644
--- a/src/common/multi_level_queue.h
+++ b/src/common/multi_level_queue.h
@@ -223,15 +223,15 @@ public:
223 ListShiftForward(levels[priority], n); 223 ListShiftForward(levels[priority], n);
224 } 224 }
225 225
226 std::size_t depth() const { 226 [[nodiscard]] std::size_t depth() const {
227 return Depth; 227 return Depth;
228 } 228 }
229 229
230 std::size_t size(u32 priority) const { 230 [[nodiscard]] std::size_t size(u32 priority) const {
231 return levels[priority].size(); 231 return levels[priority].size();
232 } 232 }
233 233
234 std::size_t size() const { 234 [[nodiscard]] std::size_t size() const {
235 u64 priorities = used_priorities; 235 u64 priorities = used_priorities;
236 std::size_t size = 0; 236 std::size_t size = 0;
237 while (priorities != 0) { 237 while (priorities != 0) {
@@ -242,64 +242,64 @@ public:
242 return size; 242 return size;
243 } 243 }
244 244
245 bool empty() const { 245 [[nodiscard]] bool empty() const {
246 return used_priorities == 0; 246 return used_priorities == 0;
247 } 247 }
248 248
249 bool empty(u32 priority) const { 249 [[nodiscard]] bool empty(u32 priority) const {
250 return (used_priorities & (1ULL << priority)) == 0; 250 return (used_priorities & (1ULL << priority)) == 0;
251 } 251 }
252 252
253 u32 highest_priority_set(u32 max_priority = 0) const { 253 [[nodiscard]] u32 highest_priority_set(u32 max_priority = 0) const {
254 const u64 priorities = 254 const u64 priorities =
255 max_priority == 0 ? used_priorities : (used_priorities & ~((1ULL << max_priority) - 1)); 255 max_priority == 0 ? used_priorities : (used_priorities & ~((1ULL << max_priority) - 1));
256 return priorities == 0 ? Depth : static_cast<u32>(CountTrailingZeroes64(priorities)); 256 return priorities == 0 ? Depth : static_cast<u32>(CountTrailingZeroes64(priorities));
257 } 257 }
258 258
259 u32 lowest_priority_set(u32 min_priority = Depth - 1) const { 259 [[nodiscard]] u32 lowest_priority_set(u32 min_priority = Depth - 1) const {
260 const u64 priorities = min_priority >= Depth - 1 260 const u64 priorities = min_priority >= Depth - 1
261 ? used_priorities 261 ? used_priorities
262 : (used_priorities & ((1ULL << (min_priority + 1)) - 1)); 262 : (used_priorities & ((1ULL << (min_priority + 1)) - 1));
263 return priorities == 0 ? Depth : 63 - CountLeadingZeroes64(priorities); 263 return priorities == 0 ? Depth : 63 - CountLeadingZeroes64(priorities);
264 } 264 }
265 265
266 const_iterator cbegin(u32 max_prio = 0) const { 266 [[nodiscard]] const_iterator cbegin(u32 max_prio = 0) const {
267 const u32 priority = highest_priority_set(max_prio); 267 const u32 priority = highest_priority_set(max_prio);
268 return priority == Depth ? cend() 268 return priority == Depth ? cend()
269 : const_iterator{*this, levels[priority].cbegin(), priority}; 269 : const_iterator{*this, levels[priority].cbegin(), priority};
270 } 270 }
271 const_iterator begin(u32 max_prio = 0) const { 271 [[nodiscard]] const_iterator begin(u32 max_prio = 0) const {
272 return cbegin(max_prio); 272 return cbegin(max_prio);
273 } 273 }
274 iterator begin(u32 max_prio = 0) { 274 [[nodiscard]] iterator begin(u32 max_prio = 0) {
275 const u32 priority = highest_priority_set(max_prio); 275 const u32 priority = highest_priority_set(max_prio);
276 return priority == Depth ? end() : iterator{*this, levels[priority].begin(), priority}; 276 return priority == Depth ? end() : iterator{*this, levels[priority].begin(), priority};
277 } 277 }
278 278
279 const_iterator cend(u32 min_prio = Depth - 1) const { 279 [[nodiscard]] const_iterator cend(u32 min_prio = Depth - 1) const {
280 return min_prio == Depth - 1 ? const_iterator{*this, Depth} : cbegin(min_prio + 1); 280 return min_prio == Depth - 1 ? const_iterator{*this, Depth} : cbegin(min_prio + 1);
281 } 281 }
282 const_iterator end(u32 min_prio = Depth - 1) const { 282 [[nodiscard]] const_iterator end(u32 min_prio = Depth - 1) const {
283 return cend(min_prio); 283 return cend(min_prio);
284 } 284 }
285 iterator end(u32 min_prio = Depth - 1) { 285 [[nodiscard]] iterator end(u32 min_prio = Depth - 1) {
286 return min_prio == Depth - 1 ? iterator{*this, Depth} : begin(min_prio + 1); 286 return min_prio == Depth - 1 ? iterator{*this, Depth} : begin(min_prio + 1);
287 } 287 }
288 288
289 T& front(u32 max_priority = 0) { 289 [[nodiscard]] T& front(u32 max_priority = 0) {
290 const u32 priority = highest_priority_set(max_priority); 290 const u32 priority = highest_priority_set(max_priority);
291 return levels[priority == Depth ? 0 : priority].front(); 291 return levels[priority == Depth ? 0 : priority].front();
292 } 292 }
293 const T& front(u32 max_priority = 0) const { 293 [[nodiscard]] const T& front(u32 max_priority = 0) const {
294 const u32 priority = highest_priority_set(max_priority); 294 const u32 priority = highest_priority_set(max_priority);
295 return levels[priority == Depth ? 0 : priority].front(); 295 return levels[priority == Depth ? 0 : priority].front();
296 } 296 }
297 297
298 T back(u32 min_priority = Depth - 1) { 298 [[nodiscard]] T& back(u32 min_priority = Depth - 1) {
299 const u32 priority = lowest_priority_set(min_priority); // intended 299 const u32 priority = lowest_priority_set(min_priority); // intended
300 return levels[priority == Depth ? 63 : priority].back(); 300 return levels[priority == Depth ? 63 : priority].back();
301 } 301 }
302 const T& back(u32 min_priority = Depth - 1) const { 302 [[nodiscard]] const T& back(u32 min_priority = Depth - 1) const {
303 const u32 priority = lowest_priority_set(min_priority); // intended 303 const u32 priority = lowest_priority_set(min_priority); // intended
304 return levels[priority == Depth ? 63 : priority].back(); 304 return levels[priority == Depth ? 63 : priority].back();
305 } 305 }
@@ -329,7 +329,8 @@ private:
329 in_list.splice(position, out_list, element); 329 in_list.splice(position, out_list, element);
330 } 330 }
331 331
332 static const_list_iterator ListIterateTo(const std::list<T>& list, const T& element) { 332 [[nodiscard]] static const_list_iterator ListIterateTo(const std::list<T>& list,
333 const T& element) {
333 auto it = list.cbegin(); 334 auto it = list.cbegin();
334 while (it != list.cend() && *it != element) { 335 while (it != list.cend() && *it != element) {
335 ++it; 336 ++it;
diff --git a/src/common/page_table.cpp b/src/common/page_table.cpp
index 566b57b62..bccea0894 100644
--- a/src/common/page_table.cpp
+++ b/src/common/page_table.cpp
@@ -6,36 +6,20 @@
6 6
7namespace Common { 7namespace Common {
8 8
9PageTable::PageTable(std::size_t page_size_in_bits) : page_size_in_bits{page_size_in_bits} {} 9PageTable::PageTable() = default;
10 10
11PageTable::~PageTable() = default; 11PageTable::~PageTable() noexcept = default;
12
13void PageTable::Resize(std::size_t address_space_width_in_bits) {
14 const std::size_t num_page_table_entries = 1ULL
15 << (address_space_width_in_bits - page_size_in_bits);
16 12
13void PageTable::Resize(std::size_t address_space_width_in_bits, std::size_t page_size_in_bits,
14 bool has_attribute) {
15 const std::size_t num_page_table_entries{1ULL
16 << (address_space_width_in_bits - page_size_in_bits)};
17 pointers.resize(num_page_table_entries); 17 pointers.resize(num_page_table_entries);
18 attributes.resize(num_page_table_entries);
19
20 // The default is a 39-bit address space, which causes an initial 1GB allocation size. If the
21 // vector size is subsequently decreased (via resize), the vector might not automatically
22 // actually reallocate/resize its underlying allocation, which wastes up to ~800 MB for
23 // 36-bit titles. Call shrink_to_fit to reduce capacity to what's actually in use.
24
25 pointers.shrink_to_fit();
26 attributes.shrink_to_fit();
27}
28
29BackingPageTable::BackingPageTable(std::size_t page_size_in_bits) : PageTable{page_size_in_bits} {}
30
31BackingPageTable::~BackingPageTable() = default;
32
33void BackingPageTable::Resize(std::size_t address_space_width_in_bits) {
34 PageTable::Resize(address_space_width_in_bits);
35 const std::size_t num_page_table_entries = 1ULL
36 << (address_space_width_in_bits - page_size_in_bits);
37 backing_addr.resize(num_page_table_entries); 18 backing_addr.resize(num_page_table_entries);
38 backing_addr.shrink_to_fit(); 19
20 if (has_attribute) {
21 attributes.resize(num_page_table_entries);
22 }
39} 23}
40 24
41} // namespace Common 25} // namespace Common
diff --git a/src/common/page_table.h b/src/common/page_table.h
index dbc272ab7..9754fabf9 100644
--- a/src/common/page_table.h
+++ b/src/common/page_table.h
@@ -4,10 +4,11 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <vector> 7#include <tuple>
8#include <boost/icl/interval_map.hpp> 8
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "common/memory_hook.h" 10#include "common/memory_hook.h"
11#include "common/virtual_buffer.h"
11 12
12namespace Common { 13namespace Common {
13 14
@@ -33,11 +34,11 @@ struct SpecialRegion {
33 34
34 MemoryHookPointer handler; 35 MemoryHookPointer handler;
35 36
36 bool operator<(const SpecialRegion& other) const { 37 [[nodiscard]] bool operator<(const SpecialRegion& other) const {
37 return std::tie(type, handler) < std::tie(other.type, other.handler); 38 return std::tie(type, handler) < std::tie(other.type, other.handler);
38 } 39 }
39 40
40 bool operator==(const SpecialRegion& other) const { 41 [[nodiscard]] bool operator==(const SpecialRegion& other) const {
41 return std::tie(type, handler) == std::tie(other.type, other.handler); 42 return std::tie(type, handler) == std::tie(other.type, other.handler);
42 } 43 }
43}; 44};
@@ -47,49 +48,35 @@ struct SpecialRegion {
47 * mimics the way a real CPU page table works. 48 * mimics the way a real CPU page table works.
48 */ 49 */
49struct PageTable { 50struct PageTable {
50 explicit PageTable(std::size_t page_size_in_bits); 51 PageTable();
51 ~PageTable(); 52 ~PageTable() noexcept;
53
54 PageTable(const PageTable&) = delete;
55 PageTable& operator=(const PageTable&) = delete;
56
57 PageTable(PageTable&&) noexcept = default;
58 PageTable& operator=(PageTable&&) noexcept = default;
52 59
53 /** 60 /**
54 * Resizes the page table to be able to accomodate enough pages within 61 * Resizes the page table to be able to accomodate enough pages within
55 * a given address space. 62 * a given address space.
56 * 63 *
57 * @param address_space_width_in_bits The address size width in bits. 64 * @param address_space_width_in_bits The address size width in bits.
65 * @param page_size_in_bits The page size in bits.
66 * @param has_attribute Whether or not this page has any backing attributes.
58 */ 67 */
59 void Resize(std::size_t address_space_width_in_bits); 68 void Resize(std::size_t address_space_width_in_bits, std::size_t page_size_in_bits,
69 bool has_attribute);
60 70
61 /** 71 /**
62 * Vector of memory pointers backing each page. An entry can only be non-null if the 72 * Vector of memory pointers backing each page. An entry can only be non-null if the
63 * corresponding entry in the `attributes` vector is of type `Memory`. 73 * corresponding entry in the `attributes` vector is of type `Memory`.
64 */ 74 */
65 std::vector<u8*> pointers; 75 VirtualBuffer<u8*> pointers;
66
67 /**
68 * Contains MMIO handlers that back memory regions whose entries in the `attribute` vector is
69 * of type `Special`.
70 */
71 boost::icl::interval_map<u64, std::set<SpecialRegion>> special_regions;
72
73 /**
74 * Vector of fine grained page attributes. If it is set to any value other than `Memory`, then
75 * the corresponding entry in `pointers` MUST be set to null.
76 */
77 std::vector<PageType> attributes;
78
79 const std::size_t page_size_in_bits{};
80};
81
82/**
83 * A more advanced Page Table with the ability to save a backing address when using it
84 * depends on another MMU.
85 */
86struct BackingPageTable : PageTable {
87 explicit BackingPageTable(std::size_t page_size_in_bits);
88 ~BackingPageTable();
89 76
90 void Resize(std::size_t address_space_width_in_bits); 77 VirtualBuffer<u64> backing_addr;
91 78
92 std::vector<u64> backing_addr; 79 VirtualBuffer<PageType> attributes;
93}; 80};
94 81
95} // namespace Common 82} // namespace Common
diff --git a/src/common/param_package.h b/src/common/param_package.h
index 6a0a9b656..c13e45479 100644
--- a/src/common/param_package.h
+++ b/src/common/param_package.h
@@ -19,19 +19,19 @@ public:
19 explicit ParamPackage(const std::string& serialized); 19 explicit ParamPackage(const std::string& serialized);
20 ParamPackage(std::initializer_list<DataType::value_type> list); 20 ParamPackage(std::initializer_list<DataType::value_type> list);
21 ParamPackage(const ParamPackage& other) = default; 21 ParamPackage(const ParamPackage& other) = default;
22 ParamPackage(ParamPackage&& other) = default; 22 ParamPackage(ParamPackage&& other) noexcept = default;
23 23
24 ParamPackage& operator=(const ParamPackage& other) = default; 24 ParamPackage& operator=(const ParamPackage& other) = default;
25 ParamPackage& operator=(ParamPackage&& other) = default; 25 ParamPackage& operator=(ParamPackage&& other) = default;
26 26
27 std::string Serialize() const; 27 [[nodiscard]] std::string Serialize() const;
28 std::string Get(const std::string& key, const std::string& default_value) const; 28 [[nodiscard]] std::string Get(const std::string& key, const std::string& default_value) const;
29 int Get(const std::string& key, int default_value) const; 29 [[nodiscard]] int Get(const std::string& key, int default_value) const;
30 float Get(const std::string& key, float default_value) const; 30 [[nodiscard]] float Get(const std::string& key, float default_value) const;
31 void Set(const std::string& key, std::string value); 31 void Set(const std::string& key, std::string value);
32 void Set(const std::string& key, int value); 32 void Set(const std::string& key, int value);
33 void Set(const std::string& key, float value); 33 void Set(const std::string& key, float value);
34 bool Has(const std::string& key) const; 34 [[nodiscard]] bool Has(const std::string& key) const;
35 void Erase(const std::string& key); 35 void Erase(const std::string& key);
36 void Clear(); 36 void Clear();
37 37
diff --git a/src/common/quaternion.h b/src/common/quaternion.h
index 370198ae0..4d0871eb4 100644
--- a/src/common/quaternion.h
+++ b/src/common/quaternion.h
@@ -14,35 +14,66 @@ public:
14 Vec3<T> xyz; 14 Vec3<T> xyz;
15 T w{}; 15 T w{};
16 16
17 Quaternion<decltype(-T{})> Inverse() const { 17 [[nodiscard]] Quaternion<decltype(-T{})> Inverse() const {
18 return {-xyz, w}; 18 return {-xyz, w};
19 } 19 }
20 20
21 Quaternion<decltype(T{} + T{})> operator+(const Quaternion& other) const { 21 [[nodiscard]] Quaternion<decltype(T{} + T{})> operator+(const Quaternion& other) const {
22 return {xyz + other.xyz, w + other.w}; 22 return {xyz + other.xyz, w + other.w};
23 } 23 }
24 24
25 Quaternion<decltype(T{} - T{})> operator-(const Quaternion& other) const { 25 [[nodiscard]] Quaternion<decltype(T{} - T{})> operator-(const Quaternion& other) const {
26 return {xyz - other.xyz, w - other.w}; 26 return {xyz - other.xyz, w - other.w};
27 } 27 }
28 28
29 Quaternion<decltype(T{} * T{} - T{} * T{})> operator*(const Quaternion& other) const { 29 [[nodiscard]] Quaternion<decltype(T{} * T{} - T{} * T{})> operator*(
30 const Quaternion& other) const {
30 return {xyz * other.w + other.xyz * w + Cross(xyz, other.xyz), 31 return {xyz * other.w + other.xyz * w + Cross(xyz, other.xyz),
31 w * other.w - Dot(xyz, other.xyz)}; 32 w * other.w - Dot(xyz, other.xyz)};
32 } 33 }
33 34
34 Quaternion<T> Normalized() const { 35 [[nodiscard]] Quaternion<T> Normalized() const {
35 T length = std::sqrt(xyz.Length2() + w * w); 36 T length = std::sqrt(xyz.Length2() + w * w);
36 return {xyz / length, w / length}; 37 return {xyz / length, w / length};
37 } 38 }
39
40 [[nodiscard]] std::array<decltype(-T{}), 16> ToMatrix() const {
41 const T x2 = xyz[0] * xyz[0];
42 const T y2 = xyz[1] * xyz[1];
43 const T z2 = xyz[2] * xyz[2];
44
45 const T xy = xyz[0] * xyz[1];
46 const T wz = w * xyz[2];
47 const T xz = xyz[0] * xyz[2];
48 const T wy = w * xyz[1];
49 const T yz = xyz[1] * xyz[2];
50 const T wx = w * xyz[0];
51
52 return {1.0f - 2.0f * (y2 + z2),
53 2.0f * (xy + wz),
54 2.0f * (xz - wy),
55 0.0f,
56 2.0f * (xy - wz),
57 1.0f - 2.0f * (x2 + z2),
58 2.0f * (yz + wx),
59 0.0f,
60 2.0f * (xz + wy),
61 2.0f * (yz - wx),
62 1.0f - 2.0f * (x2 + y2),
63 0.0f,
64 0.0f,
65 0.0f,
66 0.0f,
67 1.0f};
68 }
38}; 69};
39 70
40template <typename T> 71template <typename T>
41auto QuaternionRotate(const Quaternion<T>& q, const Vec3<T>& v) { 72[[nodiscard]] auto QuaternionRotate(const Quaternion<T>& q, const Vec3<T>& v) {
42 return v + 2 * Cross(q.xyz, Cross(q.xyz, v) + v * q.w); 73 return v + 2 * Cross(q.xyz, Cross(q.xyz, v) + v * q.w);
43} 74}
44 75
45inline Quaternion<float> MakeQuaternion(const Vec3<float>& axis, float angle) { 76[[nodiscard]] inline Quaternion<float> MakeQuaternion(const Vec3<float>& axis, float angle) {
46 return {axis * std::sin(angle / 2), std::cos(angle / 2)}; 77 return {axis * std::sin(angle / 2), std::cos(angle / 2)};
47} 78}
48 79
diff --git a/src/common/ring_buffer.h b/src/common/ring_buffer.h
index abe3b4dc2..138fa0131 100644
--- a/src/common/ring_buffer.h
+++ b/src/common/ring_buffer.h
@@ -91,12 +91,12 @@ public:
91 } 91 }
92 92
93 /// @returns Number of slots used 93 /// @returns Number of slots used
94 std::size_t Size() const { 94 [[nodiscard]] std::size_t Size() const {
95 return m_write_index.load() - m_read_index.load(); 95 return m_write_index.load() - m_read_index.load();
96 } 96 }
97 97
98 /// @returns Maximum size of ring buffer 98 /// @returns Maximum size of ring buffer
99 constexpr std::size_t Capacity() const { 99 [[nodiscard]] constexpr std::size_t Capacity() const {
100 return capacity; 100 return capacity;
101 } 101 }
102 102
diff --git a/src/common/scope_exit.h b/src/common/scope_exit.h
index 1176a72b1..68ef5f197 100644
--- a/src/common/scope_exit.h
+++ b/src/common/scope_exit.h
@@ -12,10 +12,17 @@ template <typename Func>
12struct ScopeExitHelper { 12struct ScopeExitHelper {
13 explicit ScopeExitHelper(Func&& func) : func(std::move(func)) {} 13 explicit ScopeExitHelper(Func&& func) : func(std::move(func)) {}
14 ~ScopeExitHelper() { 14 ~ScopeExitHelper() {
15 func(); 15 if (active) {
16 func();
17 }
18 }
19
20 void Cancel() {
21 active = false;
16 } 22 }
17 23
18 Func func; 24 Func func;
25 bool active{true};
19}; 26};
20 27
21template <typename Func> 28template <typename Func>
diff --git a/src/common/spin_lock.cpp b/src/common/spin_lock.cpp
new file mode 100644
index 000000000..c1524220f
--- /dev/null
+++ b/src/common/spin_lock.cpp
@@ -0,0 +1,54 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/spin_lock.h"
6
7#if _MSC_VER
8#include <intrin.h>
9#if _M_AMD64
10#define __x86_64__ 1
11#endif
12#if _M_ARM64
13#define __aarch64__ 1
14#endif
15#else
16#if __x86_64__
17#include <xmmintrin.h>
18#endif
19#endif
20
21namespace {
22
23void ThreadPause() {
24#if __x86_64__
25 _mm_pause();
26#elif __aarch64__ && _MSC_VER
27 __yield();
28#elif __aarch64__
29 asm("yield");
30#endif
31}
32
33} // Anonymous namespace
34
35namespace Common {
36
37void SpinLock::lock() {
38 while (lck.test_and_set(std::memory_order_acquire)) {
39 ThreadPause();
40 }
41}
42
43void SpinLock::unlock() {
44 lck.clear(std::memory_order_release);
45}
46
47bool SpinLock::try_lock() {
48 if (lck.test_and_set(std::memory_order_acquire)) {
49 return false;
50 }
51 return true;
52}
53
54} // namespace Common
diff --git a/src/common/spin_lock.h b/src/common/spin_lock.h
new file mode 100644
index 000000000..06ac2f5bb
--- /dev/null
+++ b/src/common/spin_lock.h
@@ -0,0 +1,34 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <atomic>
8
9namespace Common {
10
11/**
12 * SpinLock class
13 * a lock similar to mutex that forces a thread to spin wait instead calling the
14 * supervisor. Should be used on short sequences of code.
15 */
16class SpinLock {
17public:
18 SpinLock() = default;
19
20 SpinLock(const SpinLock&) = delete;
21 SpinLock& operator=(const SpinLock&) = delete;
22
23 SpinLock(SpinLock&&) = delete;
24 SpinLock& operator=(SpinLock&&) = delete;
25
26 void lock();
27 void unlock();
28 [[nodiscard]] bool try_lock();
29
30private:
31 std::atomic_flag lck = ATOMIC_FLAG_INIT;
32};
33
34} // namespace Common
diff --git a/src/common/stream.cpp b/src/common/stream.cpp
new file mode 100644
index 000000000..bf0496c26
--- /dev/null
+++ b/src/common/stream.cpp
@@ -0,0 +1,47 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <stdexcept>
6#include "common/common_types.h"
7#include "common/stream.h"
8
9namespace Common {
10
11Stream::Stream() = default;
12Stream::~Stream() = default;
13
14void Stream::Seek(s32 offset, SeekOrigin origin) {
15 if (origin == SeekOrigin::SetOrigin) {
16 if (offset < 0) {
17 position = 0;
18 } else if (position >= buffer.size()) {
19 position = buffer.size();
20 } else {
21 position = offset;
22 }
23 } else if (origin == SeekOrigin::FromCurrentPos) {
24 Seek(static_cast<s32>(position) + offset, SeekOrigin::SetOrigin);
25 } else if (origin == SeekOrigin::FromEnd) {
26 Seek(static_cast<s32>(buffer.size()) - offset, SeekOrigin::SetOrigin);
27 }
28}
29
30u8 Stream::ReadByte() {
31 if (position < buffer.size()) {
32 return buffer[position++];
33 } else {
34 throw std::out_of_range("Attempting to read a byte not within the buffer range");
35 }
36}
37
38void Stream::WriteByte(u8 byte) {
39 if (position == buffer.size()) {
40 buffer.push_back(byte);
41 position++;
42 } else {
43 buffer.insert(buffer.begin() + position, byte);
44 }
45}
46
47} // namespace Common
diff --git a/src/common/stream.h b/src/common/stream.h
new file mode 100644
index 000000000..0e40692de
--- /dev/null
+++ b/src/common/stream.h
@@ -0,0 +1,56 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <vector>
8#include "common/common_types.h"
9
10namespace Common {
11
12enum class SeekOrigin {
13 SetOrigin,
14 FromCurrentPos,
15 FromEnd,
16};
17
18class Stream {
19public:
20 /// Stream creates a bitstream and provides common functionality on the stream.
21 explicit Stream();
22 ~Stream();
23
24 Stream(const Stream&) = delete;
25 Stream& operator=(const Stream&) = delete;
26
27 Stream(Stream&&) = default;
28 Stream& operator=(Stream&&) = default;
29
30 /// Reposition bitstream "cursor" to the specified offset from origin
31 void Seek(s32 offset, SeekOrigin origin);
32
33 /// Reads next byte in the stream buffer and increments position
34 u8 ReadByte();
35
36 /// Writes byte at current position
37 void WriteByte(u8 byte);
38
39 [[nodiscard]] std::size_t GetPosition() const {
40 return position;
41 }
42
43 [[nodiscard]] std::vector<u8>& GetBuffer() {
44 return buffer;
45 }
46
47 [[nodiscard]] const std::vector<u8>& GetBuffer() const {
48 return buffer;
49 }
50
51private:
52 std::vector<u8> buffer;
53 std::size_t position{0};
54};
55
56} // namespace Common
diff --git a/src/common/string_util.cpp b/src/common/string_util.cpp
index 84883a1d3..4cba2aaa4 100644
--- a/src/common/string_util.cpp
+++ b/src/common/string_util.cpp
@@ -8,6 +8,7 @@
8#include <cstdlib> 8#include <cstdlib>
9#include <locale> 9#include <locale>
10#include <sstream> 10#include <sstream>
11
11#include "common/common_paths.h" 12#include "common/common_paths.h"
12#include "common/logging/log.h" 13#include "common/logging/log.h"
13#include "common/string_util.h" 14#include "common/string_util.h"
@@ -21,14 +22,14 @@ namespace Common {
21/// Make a string lowercase 22/// Make a string lowercase
22std::string ToLower(std::string str) { 23std::string ToLower(std::string str) {
23 std::transform(str.begin(), str.end(), str.begin(), 24 std::transform(str.begin(), str.end(), str.begin(),
24 [](unsigned char c) { return std::tolower(c); }); 25 [](unsigned char c) { return static_cast<char>(std::tolower(c)); });
25 return str; 26 return str;
26} 27}
27 28
28/// Make a string uppercase 29/// Make a string uppercase
29std::string ToUpper(std::string str) { 30std::string ToUpper(std::string str) {
30 std::transform(str.begin(), str.end(), str.begin(), 31 std::transform(str.begin(), str.end(), str.begin(),
31 [](unsigned char c) { return std::toupper(c); }); 32 [](unsigned char c) { return static_cast<char>(std::toupper(c)); });
32 return str; 33 return str;
33} 34}
34 35
diff --git a/src/common/string_util.h b/src/common/string_util.h
index 583fd05e6..a32c07c06 100644
--- a/src/common/string_util.h
+++ b/src/common/string_util.h
@@ -12,19 +12,19 @@
12namespace Common { 12namespace Common {
13 13
14/// Make a string lowercase 14/// Make a string lowercase
15std::string ToLower(std::string str); 15[[nodiscard]] std::string ToLower(std::string str);
16 16
17/// Make a string uppercase 17/// Make a string uppercase
18std::string ToUpper(std::string str); 18[[nodiscard]] std::string ToUpper(std::string str);
19 19
20std::string StringFromBuffer(const std::vector<u8>& data); 20[[nodiscard]] std::string StringFromBuffer(const std::vector<u8>& data);
21 21
22std::string StripSpaces(const std::string& s); 22[[nodiscard]] std::string StripSpaces(const std::string& s);
23std::string StripQuotes(const std::string& s); 23[[nodiscard]] std::string StripQuotes(const std::string& s);
24 24
25std::string StringFromBool(bool value); 25[[nodiscard]] std::string StringFromBool(bool value);
26 26
27std::string TabsToSpaces(int tab_size, std::string in); 27[[nodiscard]] std::string TabsToSpaces(int tab_size, std::string in);
28 28
29void SplitString(const std::string& str, char delim, std::vector<std::string>& output); 29void SplitString(const std::string& str, char delim, std::vector<std::string>& output);
30 30
@@ -34,14 +34,15 @@ bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _
34 34
35void BuildCompleteFilename(std::string& _CompleteFilename, const std::string& _Path, 35void BuildCompleteFilename(std::string& _CompleteFilename, const std::string& _Path,
36 const std::string& _Filename); 36 const std::string& _Filename);
37std::string ReplaceAll(std::string result, const std::string& src, const std::string& dest); 37[[nodiscard]] std::string ReplaceAll(std::string result, const std::string& src,
38 const std::string& dest);
38 39
39std::string UTF16ToUTF8(const std::u16string& input); 40[[nodiscard]] std::string UTF16ToUTF8(const std::u16string& input);
40std::u16string UTF8ToUTF16(const std::string& input); 41[[nodiscard]] std::u16string UTF8ToUTF16(const std::string& input);
41 42
42#ifdef _WIN32 43#ifdef _WIN32
43std::string UTF16ToUTF8(const std::wstring& input); 44[[nodiscard]] std::string UTF16ToUTF8(const std::wstring& input);
44std::wstring UTF8ToUTF16W(const std::string& str); 45[[nodiscard]] std::wstring UTF8ToUTF16W(const std::string& str);
45 46
46#endif 47#endif
47 48
@@ -50,7 +51,7 @@ std::wstring UTF8ToUTF16W(const std::string& str);
50 * `other` for equality. 51 * `other` for equality.
51 */ 52 */
52template <typename InIt> 53template <typename InIt>
53bool ComparePartialString(InIt begin, InIt end, const char* other) { 54[[nodiscard]] bool ComparePartialString(InIt begin, InIt end, const char* other) {
54 for (; begin != end && *other != '\0'; ++begin, ++other) { 55 for (; begin != end && *other != '\0'; ++begin, ++other) {
55 if (*begin != *other) { 56 if (*begin != *other) {
56 return false; 57 return false;
@@ -64,26 +65,15 @@ bool ComparePartialString(InIt begin, InIt end, const char* other) {
64 * Creates a std::string from a fixed-size NUL-terminated char buffer. If the buffer isn't 65 * Creates a std::string from a fixed-size NUL-terminated char buffer. If the buffer isn't
65 * NUL-terminated then the string ends at max_len characters. 66 * NUL-terminated then the string ends at max_len characters.
66 */ 67 */
67std::string StringFromFixedZeroTerminatedBuffer(const char* buffer, std::size_t max_len); 68[[nodiscard]] std::string StringFromFixedZeroTerminatedBuffer(const char* buffer,
69 std::size_t max_len);
68 70
69/** 71/**
70 * Creates a UTF-16 std::u16string from a fixed-size NUL-terminated char buffer. If the buffer isn't 72 * Creates a UTF-16 std::u16string from a fixed-size NUL-terminated char buffer. If the buffer isn't
71 * null-terminated, then the string ends at the greatest multiple of two less then or equal to 73 * null-terminated, then the string ends at the greatest multiple of two less then or equal to
72 * max_len_bytes. 74 * max_len_bytes.
73 */ 75 */
74std::u16string UTF16StringFromFixedZeroTerminatedBuffer(std::u16string_view buffer, 76[[nodiscard]] std::u16string UTF16StringFromFixedZeroTerminatedBuffer(std::u16string_view buffer,
75 std::size_t max_len); 77 std::size_t max_len);
76
77/**
78 * Attempts to trim an arbitrary prefix from `path`, leaving only the part starting at `root`. It's
79 * intended to be used to strip a system-specific build directory from the `__FILE__` macro,
80 * leaving only the path relative to the sources root.
81 *
82 * @param path The input file path as a null-terminated string
83 * @param root The name of the root source directory as a null-terminated string. Path up to and
84 * including the last occurrence of this name will be stripped
85 * @return A pointer to the same string passed as `path`, but starting at the trimmed portion
86 */
87const char* TrimSourcePath(const char* path, const char* root = "src");
88 78
89} // namespace Common 79} // namespace Common
diff --git a/src/common/swap.h b/src/common/swap.h
index 71932c2bb..7665942a2 100644
--- a/src/common/swap.h
+++ b/src/common/swap.h
@@ -17,43 +17,14 @@
17 17
18#pragma once 18#pragma once
19 19
20#include <type_traits>
21
22#if defined(_MSC_VER) 20#if defined(_MSC_VER)
23#include <cstdlib> 21#include <cstdlib>
24#endif 22#endif
23#include <bit>
25#include <cstring> 24#include <cstring>
25#include <type_traits>
26#include "common/common_types.h" 26#include "common/common_types.h"
27 27
28// GCC
29#ifdef __GNUC__
30
31#if __BYTE_ORDER__ && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) && !defined(COMMON_LITTLE_ENDIAN)
32#define COMMON_LITTLE_ENDIAN 1
33#elif __BYTE_ORDER__ && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) && !defined(COMMON_BIG_ENDIAN)
34#define COMMON_BIG_ENDIAN 1
35#endif
36
37// LLVM/clang
38#elif defined(__clang__)
39
40#if __LITTLE_ENDIAN__ && !defined(COMMON_LITTLE_ENDIAN)
41#define COMMON_LITTLE_ENDIAN 1
42#elif __BIG_ENDIAN__ && !defined(COMMON_BIG_ENDIAN)
43#define COMMON_BIG_ENDIAN 1
44#endif
45
46// MSVC
47#elif defined(_MSC_VER) && !defined(COMMON_BIG_ENDIAN) && !defined(COMMON_LITTLE_ENDIAN)
48
49#define COMMON_LITTLE_ENDIAN 1
50#endif
51
52// Worst case, default to little endian.
53#if !COMMON_BIG_ENDIAN && !COMMON_LITTLE_ENDIAN
54#define COMMON_LITTLE_ENDIAN 1
55#endif
56
57namespace Common { 28namespace Common {
58 29
59#ifdef _MSC_VER 30#ifdef _MSC_VER
@@ -675,17 +646,8 @@ struct AddEndian<T, SwapTag> {
675}; 646};
676 647
677// Alias LETag/BETag as KeepTag/SwapTag depending on the system 648// Alias LETag/BETag as KeepTag/SwapTag depending on the system
678#if COMMON_LITTLE_ENDIAN 649using LETag = std::conditional_t<std::endian::native == std::endian::little, KeepTag, SwapTag>;
679 650using BETag = std::conditional_t<std::endian::native == std::endian::big, KeepTag, SwapTag>;
680using LETag = KeepTag;
681using BETag = SwapTag;
682
683#else
684
685using BETag = KeepTag;
686using LETag = SwapTag;
687
688#endif
689 651
690// Aliases for LE types 652// Aliases for LE types
691using u16_le = AddEndian<u16, LETag>::type; 653using u16_le = AddEndian<u16, LETag>::type;
diff --git a/src/common/telemetry.cpp b/src/common/telemetry.cpp
index 200c6489a..6241d08b3 100644
--- a/src/common/telemetry.cpp
+++ b/src/common/telemetry.cpp
@@ -12,7 +12,7 @@
12#include "common/x64/cpu_detect.h" 12#include "common/x64/cpu_detect.h"
13#endif 13#endif
14 14
15namespace Telemetry { 15namespace Common::Telemetry {
16 16
17void FieldCollection::Accept(VisitorInterface& visitor) const { 17void FieldCollection::Accept(VisitorInterface& visitor) const {
18 for (const auto& field : fields) { 18 for (const auto& field : fields) {
@@ -60,6 +60,7 @@ void AppendCPUInfo(FieldCollection& fc) {
60 fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_AES", Common::GetCPUCaps().aes); 60 fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_AES", Common::GetCPUCaps().aes);
61 fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_AVX", Common::GetCPUCaps().avx); 61 fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_AVX", Common::GetCPUCaps().avx);
62 fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_AVX2", Common::GetCPUCaps().avx2); 62 fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_AVX2", Common::GetCPUCaps().avx2);
63 fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_AVX512", Common::GetCPUCaps().avx512);
63 fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_BMI1", Common::GetCPUCaps().bmi1); 64 fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_BMI1", Common::GetCPUCaps().bmi1);
64 fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_BMI2", Common::GetCPUCaps().bmi2); 65 fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_BMI2", Common::GetCPUCaps().bmi2);
65 fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_FMA", Common::GetCPUCaps().fma); 66 fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_FMA", Common::GetCPUCaps().fma);
@@ -87,4 +88,4 @@ void AppendOSInfo(FieldCollection& fc) {
87#endif 88#endif
88} 89}
89 90
90} // namespace Telemetry 91} // namespace Common::Telemetry
diff --git a/src/common/telemetry.h b/src/common/telemetry.h
index 854a73fae..a50c5d1de 100644
--- a/src/common/telemetry.h
+++ b/src/common/telemetry.h
@@ -10,7 +10,7 @@
10#include <string> 10#include <string>
11#include "common/common_types.h" 11#include "common/common_types.h"
12 12
13namespace Telemetry { 13namespace Common::Telemetry {
14 14
15/// Field type, used for grouping fields together in the final submitted telemetry log 15/// Field type, used for grouping fields together in the final submitted telemetry log
16enum class FieldType : u8 { 16enum class FieldType : u8 {
@@ -63,30 +63,30 @@ public:
63 63
64 void Accept(VisitorInterface& visitor) const override; 64 void Accept(VisitorInterface& visitor) const override;
65 65
66 const std::string& GetName() const override { 66 [[nodiscard]] const std::string& GetName() const override {
67 return name; 67 return name;
68 } 68 }
69 69
70 /** 70 /**
71 * Returns the type of the field. 71 * Returns the type of the field.
72 */ 72 */
73 FieldType GetType() const { 73 [[nodiscard]] FieldType GetType() const {
74 return type; 74 return type;
75 } 75 }
76 76
77 /** 77 /**
78 * Returns the value of the field. 78 * Returns the value of the field.
79 */ 79 */
80 const T& GetValue() const { 80 [[nodiscard]] const T& GetValue() const {
81 return value; 81 return value;
82 } 82 }
83 83
84 bool operator==(const Field& other) const { 84 [[nodiscard]] bool operator==(const Field& other) const {
85 return (type == other.type) && (name == other.name) && (value == other.value); 85 return (type == other.type) && (name == other.name) && (value == other.value);
86 } 86 }
87 87
88 bool operator!=(const Field& other) const { 88 [[nodiscard]] bool operator!=(const Field& other) const {
89 return !(*this == other); 89 return !operator==(other);
90 } 90 }
91 91
92private: 92private:
@@ -196,4 +196,4 @@ void AppendCPUInfo(FieldCollection& fc);
196/// such as platform name, etc. 196/// such as platform name, etc.
197void AppendOSInfo(FieldCollection& fc); 197void AppendOSInfo(FieldCollection& fc);
198 198
199} // namespace Telemetry 199} // namespace Common::Telemetry
diff --git a/src/common/thread.cpp b/src/common/thread.cpp
index 0cd2d10bf..d2c1ac60d 100644
--- a/src/common/thread.cpp
+++ b/src/common/thread.cpp
@@ -2,6 +2,8 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "common/common_funcs.h"
6#include "common/logging/log.h"
5#include "common/thread.h" 7#include "common/thread.h"
6#ifdef __APPLE__ 8#ifdef __APPLE__
7#include <mach/mach.h> 9#include <mach/mach.h>
@@ -19,12 +21,60 @@
19#include <unistd.h> 21#include <unistd.h>
20#endif 22#endif
21 23
24#include <string>
25
22#ifdef __FreeBSD__ 26#ifdef __FreeBSD__
23#define cpu_set_t cpuset_t 27#define cpu_set_t cpuset_t
24#endif 28#endif
25 29
26namespace Common { 30namespace Common {
27 31
32#ifdef _WIN32
33
34void SetCurrentThreadPriority(ThreadPriority new_priority) {
35 auto handle = GetCurrentThread();
36 int windows_priority = 0;
37 switch (new_priority) {
38 case ThreadPriority::Low:
39 windows_priority = THREAD_PRIORITY_BELOW_NORMAL;
40 break;
41 case ThreadPriority::Normal:
42 windows_priority = THREAD_PRIORITY_NORMAL;
43 break;
44 case ThreadPriority::High:
45 windows_priority = THREAD_PRIORITY_ABOVE_NORMAL;
46 break;
47 case ThreadPriority::VeryHigh:
48 windows_priority = THREAD_PRIORITY_HIGHEST;
49 break;
50 default:
51 windows_priority = THREAD_PRIORITY_NORMAL;
52 break;
53 }
54 SetThreadPriority(handle, windows_priority);
55}
56
57#else
58
59void SetCurrentThreadPriority(ThreadPriority new_priority) {
60 pthread_t this_thread = pthread_self();
61
62 s32 max_prio = sched_get_priority_max(SCHED_OTHER);
63 s32 min_prio = sched_get_priority_min(SCHED_OTHER);
64 u32 level = static_cast<u32>(new_priority) + 1;
65
66 struct sched_param params;
67 if (max_prio > min_prio) {
68 params.sched_priority = min_prio + ((max_prio - min_prio) * level) / 4;
69 } else {
70 params.sched_priority = min_prio - ((min_prio - max_prio) * level) / 4;
71 }
72
73 pthread_setschedparam(this_thread, SCHED_OTHER, &params);
74}
75
76#endif
77
28#ifdef _MSC_VER 78#ifdef _MSC_VER
29 79
30// Sets the debugger-visible name of the current thread. 80// Sets the debugger-visible name of the current thread.
@@ -64,12 +114,26 @@ void SetCurrentThreadName(const char* name) {
64 pthread_set_name_np(pthread_self(), name); 114 pthread_set_name_np(pthread_self(), name);
65#elif defined(__NetBSD__) 115#elif defined(__NetBSD__)
66 pthread_setname_np(pthread_self(), "%s", (void*)name); 116 pthread_setname_np(pthread_self(), "%s", (void*)name);
117#elif defined(__linux__)
118 // Linux limits thread names to 15 characters and will outright reject any
119 // attempt to set a longer name with ERANGE.
120 std::string truncated(name, std::min(strlen(name), static_cast<size_t>(15)));
121 if (int e = pthread_setname_np(pthread_self(), truncated.c_str())) {
122 errno = e;
123 LOG_ERROR(Common, "Failed to set thread name to '{}': {}", truncated, GetLastErrorMsg());
124 }
67#else 125#else
68 pthread_setname_np(pthread_self(), name); 126 pthread_setname_np(pthread_self(), name);
69#endif 127#endif
70} 128}
71#endif 129#endif
72 130
131#if defined(_WIN32)
132void SetCurrentThreadName(const char* name) {
133 // Do Nothing on MingW
134}
135#endif
136
73#endif 137#endif
74 138
75} // namespace Common 139} // namespace Common
diff --git a/src/common/thread.h b/src/common/thread.h
index 2fc071685..a8c17c71a 100644
--- a/src/common/thread.h
+++ b/src/common/thread.h
@@ -4,11 +4,13 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <atomic>
7#include <chrono> 8#include <chrono>
8#include <condition_variable> 9#include <condition_variable>
9#include <cstddef> 10#include <cstddef>
10#include <mutex> 11#include <mutex>
11#include <thread> 12#include <thread>
13#include "common/common_types.h"
12 14
13namespace Common { 15namespace Common {
14 16
@@ -24,14 +26,13 @@ public:
24 26
25 void Wait() { 27 void Wait() {
26 std::unique_lock lk{mutex}; 28 std::unique_lock lk{mutex};
27 condvar.wait(lk, [&] { return is_set; }); 29 condvar.wait(lk, [&] { return is_set.load(); });
28 is_set = false; 30 is_set = false;
29 } 31 }
30 32
31 template <class Duration> 33 bool WaitFor(const std::chrono::nanoseconds& time) {
32 bool WaitFor(const std::chrono::duration<Duration>& time) {
33 std::unique_lock lk{mutex}; 34 std::unique_lock lk{mutex};
34 if (!condvar.wait_for(lk, time, [this] { return is_set; })) 35 if (!condvar.wait_for(lk, time, [this] { return is_set.load(); }))
35 return false; 36 return false;
36 is_set = false; 37 is_set = false;
37 return true; 38 return true;
@@ -40,7 +41,7 @@ public:
40 template <class Clock, class Duration> 41 template <class Clock, class Duration>
41 bool WaitUntil(const std::chrono::time_point<Clock, Duration>& time) { 42 bool WaitUntil(const std::chrono::time_point<Clock, Duration>& time) {
42 std::unique_lock lk{mutex}; 43 std::unique_lock lk{mutex};
43 if (!condvar.wait_until(lk, time, [this] { return is_set; })) 44 if (!condvar.wait_until(lk, time, [this] { return is_set.load(); }))
44 return false; 45 return false;
45 is_set = false; 46 is_set = false;
46 return true; 47 return true;
@@ -54,9 +55,9 @@ public:
54 } 55 }
55 56
56private: 57private:
57 bool is_set = false;
58 std::condition_variable condvar; 58 std::condition_variable condvar;
59 std::mutex mutex; 59 std::mutex mutex;
60 std::atomic_bool is_set{false};
60}; 61};
61 62
62class Barrier { 63class Barrier {
@@ -86,6 +87,15 @@ private:
86 std::size_t generation = 0; // Incremented once each time the barrier is used 87 std::size_t generation = 0; // Incremented once each time the barrier is used
87}; 88};
88 89
90enum class ThreadPriority : u32 {
91 Low = 0,
92 Normal = 1,
93 High = 2,
94 VeryHigh = 3,
95};
96
97void SetCurrentThreadPriority(ThreadPriority new_priority);
98
89void SetCurrentThreadName(const char* name); 99void SetCurrentThreadName(const char* name);
90 100
91} // namespace Common 101} // namespace Common
diff --git a/src/common/thread_queue_list.h b/src/common/thread_queue_list.h
index 791f99a8c..def9e5d8d 100644
--- a/src/common/thread_queue_list.h
+++ b/src/common/thread_queue_list.h
@@ -18,14 +18,14 @@ struct ThreadQueueList {
18 using Priority = unsigned int; 18 using Priority = unsigned int;
19 19
20 // Number of priority levels. (Valid levels are [0..NUM_QUEUES).) 20 // Number of priority levels. (Valid levels are [0..NUM_QUEUES).)
21 static const Priority NUM_QUEUES = N; 21 static constexpr Priority NUM_QUEUES = N;
22 22
23 ThreadQueueList() { 23 ThreadQueueList() {
24 first = nullptr; 24 first = nullptr;
25 } 25 }
26 26
27 // Only for debugging, returns priority level. 27 // Only for debugging, returns priority level.
28 Priority contains(const T& uid) const { 28 [[nodiscard]] Priority contains(const T& uid) const {
29 for (Priority i = 0; i < NUM_QUEUES; ++i) { 29 for (Priority i = 0; i < NUM_QUEUES; ++i) {
30 const Queue& cur = queues[i]; 30 const Queue& cur = queues[i];
31 if (std::find(cur.data.cbegin(), cur.data.cend(), uid) != cur.data.cend()) { 31 if (std::find(cur.data.cbegin(), cur.data.cend(), uid) != cur.data.cend()) {
@@ -36,7 +36,7 @@ struct ThreadQueueList {
36 return -1; 36 return -1;
37 } 37 }
38 38
39 T get_first() const { 39 [[nodiscard]] T get_first() const {
40 const Queue* cur = first; 40 const Queue* cur = first;
41 while (cur != nullptr) { 41 while (cur != nullptr) {
42 if (!cur->data.empty()) { 42 if (!cur->data.empty()) {
@@ -49,7 +49,7 @@ struct ThreadQueueList {
49 } 49 }
50 50
51 template <typename UnaryPredicate> 51 template <typename UnaryPredicate>
52 T get_first_filter(UnaryPredicate filter) const { 52 [[nodiscard]] T get_first_filter(UnaryPredicate filter) const {
53 const Queue* cur = first; 53 const Queue* cur = first;
54 while (cur != nullptr) { 54 while (cur != nullptr) {
55 if (!cur->data.empty()) { 55 if (!cur->data.empty()) {
@@ -129,7 +129,7 @@ struct ThreadQueueList {
129 first = nullptr; 129 first = nullptr;
130 } 130 }
131 131
132 bool empty(Priority priority) const { 132 [[nodiscard]] bool empty(Priority priority) const {
133 const Queue* cur = &queues[priority]; 133 const Queue* cur = &queues[priority];
134 return cur->data.empty(); 134 return cur->data.empty();
135 } 135 }
diff --git a/src/common/threadsafe_queue.h b/src/common/threadsafe_queue.h
index 8268bbd5c..a4647314a 100644
--- a/src/common/threadsafe_queue.h
+++ b/src/common/threadsafe_queue.h
@@ -25,15 +25,15 @@ public:
25 delete read_ptr; 25 delete read_ptr;
26 } 26 }
27 27
28 std::size_t Size() const { 28 [[nodiscard]] std::size_t Size() const {
29 return size.load(); 29 return size.load();
30 } 30 }
31 31
32 bool Empty() const { 32 [[nodiscard]] bool Empty() const {
33 return Size() == 0; 33 return Size() == 0;
34 } 34 }
35 35
36 T& Front() const { 36 [[nodiscard]] T& Front() const {
37 return read_ptr->current; 37 return read_ptr->current;
38 } 38 }
39 39
@@ -130,15 +130,15 @@ private:
130template <typename T> 130template <typename T>
131class MPSCQueue { 131class MPSCQueue {
132public: 132public:
133 std::size_t Size() const { 133 [[nodiscard]] std::size_t Size() const {
134 return spsc_queue.Size(); 134 return spsc_queue.Size();
135 } 135 }
136 136
137 bool Empty() const { 137 [[nodiscard]] bool Empty() const {
138 return spsc_queue.Empty(); 138 return spsc_queue.Empty();
139 } 139 }
140 140
141 T& Front() const { 141 [[nodiscard]] T& Front() const {
142 return spsc_queue.Front(); 142 return spsc_queue.Front();
143 } 143 }
144 144
diff --git a/src/common/time_zone.cpp b/src/common/time_zone.cpp
new file mode 100644
index 000000000..ce239eb63
--- /dev/null
+++ b/src/common/time_zone.cpp
@@ -0,0 +1,49 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <chrono>
6#include <iomanip>
7#include <sstream>
8
9#include "common/logging/log.h"
10#include "common/time_zone.h"
11
12namespace Common::TimeZone {
13
14std::string GetDefaultTimeZone() {
15 return "GMT";
16}
17
18static std::string GetOsTimeZoneOffset() {
19 const std::time_t t{std::time(nullptr)};
20 const std::tm tm{*std::localtime(&t)};
21
22 std::stringstream ss;
23 ss << std::put_time(&tm, "%z"); // Get the current timezone offset, e.g. "-400", as a string
24
25 return ss.str();
26}
27
28static int ConvertOsTimeZoneOffsetToInt(const std::string& timezone) {
29 try {
30 return std::stoi(timezone);
31 } catch (const std::invalid_argument&) {
32 LOG_CRITICAL(Common, "invalid_argument with {}!", timezone);
33 return 0;
34 } catch (const std::out_of_range&) {
35 LOG_CRITICAL(Common, "out_of_range with {}!", timezone);
36 return 0;
37 }
38}
39
40std::chrono::seconds GetCurrentOffsetSeconds() {
41 const int offset{ConvertOsTimeZoneOffsetToInt(GetOsTimeZoneOffset())};
42
43 int seconds{(offset / 100) * 60 * 60}; // Convert hour component to seconds
44 seconds += (offset % 100) * 60; // Convert minute component to seconds
45
46 return std::chrono::seconds{seconds};
47}
48
49} // namespace Common::TimeZone
diff --git a/src/common/time_zone.h b/src/common/time_zone.h
new file mode 100644
index 000000000..9f5939ca5
--- /dev/null
+++ b/src/common/time_zone.h
@@ -0,0 +1,18 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <chrono>
8#include <string>
9
10namespace Common::TimeZone {
11
12/// Gets the default timezone, i.e. "GMT"
13[[nodiscard]] std::string GetDefaultTimeZone();
14
15/// Gets the offset of the current timezone (from the default), in seconds
16[[nodiscard]] std::chrono::seconds GetCurrentOffsetSeconds();
17
18} // namespace Common::TimeZone
diff --git a/src/common/timer.cpp b/src/common/timer.cpp
index 2dc15e434..d17dc2a50 100644
--- a/src/common/timer.cpp
+++ b/src/common/timer.cpp
@@ -142,20 +142,18 @@ std::string Timer::GetTimeFormatted() {
142// ---------------- 142// ----------------
143double Timer::GetDoubleTime() { 143double Timer::GetDoubleTime() {
144 // Get continuous timestamp 144 // Get continuous timestamp
145 u64 TmpSeconds = static_cast<u64>(Common::Timer::GetTimeSinceJan1970().count()); 145 auto tmp_seconds = static_cast<u64>(GetTimeSinceJan1970().count());
146 double ms = static_cast<u64>(GetTimeMs().count()) % 1000; 146 const auto ms = static_cast<double>(static_cast<u64>(GetTimeMs().count()) % 1000);
147 147
148 // Remove a few years. We only really want enough seconds to make 148 // Remove a few years. We only really want enough seconds to make
149 // sure that we are detecting actual actions, perhaps 60 seconds is 149 // sure that we are detecting actual actions, perhaps 60 seconds is
150 // enough really, but I leave a year of seconds anyway, in case the 150 // enough really, but I leave a year of seconds anyway, in case the
151 // user's clock is incorrect or something like that. 151 // user's clock is incorrect or something like that.
152 TmpSeconds = TmpSeconds - (38 * 365 * 24 * 60 * 60); 152 tmp_seconds = tmp_seconds - (38 * 365 * 24 * 60 * 60);
153 153
154 // Make a smaller integer that fits in the double 154 // Make a smaller integer that fits in the double
155 u32 Seconds = static_cast<u32>(TmpSeconds); 155 const auto seconds = static_cast<u32>(tmp_seconds);
156 double TmpTime = Seconds + ms; 156 return seconds + ms;
157
158 return TmpTime;
159} 157}
160 158
161} // Namespace Common 159} // Namespace Common
diff --git a/src/common/timer.h b/src/common/timer.h
index 27b521baa..8894a143d 100644
--- a/src/common/timer.h
+++ b/src/common/timer.h
@@ -19,18 +19,18 @@ public:
19 19
20 // The time difference is always returned in milliseconds, regardless of alternative internal 20 // The time difference is always returned in milliseconds, regardless of alternative internal
21 // representation 21 // representation
22 std::chrono::milliseconds GetTimeDifference(); 22 [[nodiscard]] std::chrono::milliseconds GetTimeDifference();
23 void AddTimeDifference(); 23 void AddTimeDifference();
24 24
25 static std::chrono::seconds GetTimeSinceJan1970(); 25 [[nodiscard]] static std::chrono::seconds GetTimeSinceJan1970();
26 static std::chrono::seconds GetLocalTimeSinceJan1970(); 26 [[nodiscard]] static std::chrono::seconds GetLocalTimeSinceJan1970();
27 static double GetDoubleTime(); 27 [[nodiscard]] static double GetDoubleTime();
28 28
29 static std::string GetTimeFormatted(); 29 [[nodiscard]] static std::string GetTimeFormatted();
30 std::string GetTimeElapsedFormatted() const; 30 [[nodiscard]] std::string GetTimeElapsedFormatted() const;
31 std::chrono::milliseconds GetTimeElapsed(); 31 [[nodiscard]] std::chrono::milliseconds GetTimeElapsed();
32 32
33 static std::chrono::milliseconds GetTimeMs(); 33 [[nodiscard]] static std::chrono::milliseconds GetTimeMs();
34 34
35private: 35private:
36 std::chrono::milliseconds m_LastTime; 36 std::chrono::milliseconds m_LastTime;
diff --git a/src/common/uint128.cpp b/src/common/uint128.cpp
index 32bf56730..16bf7c828 100644
--- a/src/common/uint128.cpp
+++ b/src/common/uint128.cpp
@@ -6,12 +6,38 @@
6#include <intrin.h> 6#include <intrin.h>
7 7
8#pragma intrinsic(_umul128) 8#pragma intrinsic(_umul128)
9#pragma intrinsic(_udiv128)
9#endif 10#endif
10#include <cstring> 11#include <cstring>
11#include "common/uint128.h" 12#include "common/uint128.h"
12 13
13namespace Common { 14namespace Common {
14 15
16#ifdef _MSC_VER
17
18u64 MultiplyAndDivide64(u64 a, u64 b, u64 d) {
19 u128 r{};
20 r[0] = _umul128(a, b, &r[1]);
21 u64 remainder;
22#if _MSC_VER < 1923
23 return udiv128(r[1], r[0], d, &remainder);
24#else
25 return _udiv128(r[1], r[0], d, &remainder);
26#endif
27}
28
29#else
30
31u64 MultiplyAndDivide64(u64 a, u64 b, u64 d) {
32 const u64 diva = a / d;
33 const u64 moda = a % d;
34 const u64 divb = b / d;
35 const u64 modb = b % d;
36 return diva * b + moda * divb + moda * modb / d;
37}
38
39#endif
40
15u128 Multiply64Into128(u64 a, u64 b) { 41u128 Multiply64Into128(u64 a, u64 b) {
16 u128 result; 42 u128 result;
17#ifdef _MSC_VER 43#ifdef _MSC_VER
diff --git a/src/common/uint128.h b/src/common/uint128.h
index a3be2a2cb..969259ab6 100644
--- a/src/common/uint128.h
+++ b/src/common/uint128.h
@@ -9,11 +9,14 @@
9 9
10namespace Common { 10namespace Common {
11 11
12// This function multiplies 2 u64 values and divides it by a u64 value.
13[[nodiscard]] u64 MultiplyAndDivide64(u64 a, u64 b, u64 d);
14
12// This function multiplies 2 u64 values and produces a u128 value; 15// This function multiplies 2 u64 values and produces a u128 value;
13u128 Multiply64Into128(u64 a, u64 b); 16[[nodiscard]] u128 Multiply64Into128(u64 a, u64 b);
14 17
15// This function divides a u128 by a u32 value and produces two u64 values: 18// This function divides a u128 by a u32 value and produces two u64 values:
16// the result of division and the remainder 19// the result of division and the remainder
17std::pair<u64, u64> Divide128On32(u128 dividend, u32 divisor); 20[[nodiscard]] std::pair<u64, u64> Divide128On32(u128 dividend, u32 divisor);
18 21
19} // namespace Common 22} // namespace Common
diff --git a/src/common/uuid.h b/src/common/uuid.h
index f6ad064fb..4ab9a25f0 100644
--- a/src/common/uuid.h
+++ b/src/common/uuid.h
@@ -19,29 +19,34 @@ struct UUID {
19 constexpr explicit UUID(const u128& id) : uuid{id} {} 19 constexpr explicit UUID(const u128& id) : uuid{id} {}
20 constexpr explicit UUID(const u64 lo, const u64 hi) : uuid{{lo, hi}} {} 20 constexpr explicit UUID(const u64 lo, const u64 hi) : uuid{{lo, hi}} {}
21 21
22 constexpr explicit operator bool() const { 22 [[nodiscard]] constexpr explicit operator bool() const {
23 return uuid[0] != INVALID_UUID[0] && uuid[1] != INVALID_UUID[1]; 23 return uuid[0] != INVALID_UUID[0] && uuid[1] != INVALID_UUID[1];
24 } 24 }
25 25
26 constexpr bool operator==(const UUID& rhs) const { 26 [[nodiscard]] constexpr bool operator==(const UUID& rhs) const {
27 // TODO(DarkLordZach): Replace with uuid == rhs.uuid with C++20 27 // TODO(DarkLordZach): Replace with uuid == rhs.uuid with C++20
28 return uuid[0] == rhs.uuid[0] && uuid[1] == rhs.uuid[1]; 28 return uuid[0] == rhs.uuid[0] && uuid[1] == rhs.uuid[1];
29 } 29 }
30 30
31 constexpr bool operator!=(const UUID& rhs) const { 31 [[nodiscard]] constexpr bool operator!=(const UUID& rhs) const {
32 return !operator==(rhs); 32 return !operator==(rhs);
33 } 33 }
34 34
35 // TODO(ogniK): Properly generate uuids based on RFC-4122 35 // TODO(ogniK): Properly generate uuids based on RFC-4122
36 static UUID Generate(); 36 [[nodiscard]] static UUID Generate();
37 37
38 // Set the UUID to {0,0} to be considered an invalid user 38 // Set the UUID to {0,0} to be considered an invalid user
39 constexpr void Invalidate() { 39 constexpr void Invalidate() {
40 uuid = INVALID_UUID; 40 uuid = INVALID_UUID;
41 } 41 }
42 42
43 std::string Format() const; 43 // TODO(ogniK): Properly generate a Nintendo ID
44 std::string FormatSwitch() const; 44 [[nodiscard]] constexpr u64 GetNintendoID() const {
45 return uuid[0];
46 }
47
48 [[nodiscard]] std::string Format() const;
49 [[nodiscard]] std::string FormatSwitch() const;
45}; 50};
46static_assert(sizeof(UUID) == 16, "UUID is an invalid size!"); 51static_assert(sizeof(UUID) == 16, "UUID is an invalid size!");
47 52
diff --git a/src/common/vector_math.h b/src/common/vector_math.h
index 429485329..22dba3c2d 100644
--- a/src/common/vector_math.h
+++ b/src/common/vector_math.h
@@ -52,15 +52,15 @@ public:
52 constexpr Vec2(const T& x_, const T& y_) : x(x_), y(y_) {} 52 constexpr Vec2(const T& x_, const T& y_) : x(x_), y(y_) {}
53 53
54 template <typename T2> 54 template <typename T2>
55 constexpr Vec2<T2> Cast() const { 55 [[nodiscard]] constexpr Vec2<T2> Cast() const {
56 return Vec2<T2>(static_cast<T2>(x), static_cast<T2>(y)); 56 return Vec2<T2>(static_cast<T2>(x), static_cast<T2>(y));
57 } 57 }
58 58
59 static constexpr Vec2 AssignToAll(const T& f) { 59 [[nodiscard]] static constexpr Vec2 AssignToAll(const T& f) {
60 return Vec2{f, f}; 60 return Vec2{f, f};
61 } 61 }
62 62
63 constexpr Vec2<decltype(T{} + T{})> operator+(const Vec2& other) const { 63 [[nodiscard]] constexpr Vec2<decltype(T{} + T{})> operator+(const Vec2& other) const {
64 return {x + other.x, y + other.y}; 64 return {x + other.x, y + other.y};
65 } 65 }
66 constexpr Vec2& operator+=(const Vec2& other) { 66 constexpr Vec2& operator+=(const Vec2& other) {
@@ -68,7 +68,7 @@ public:
68 y += other.y; 68 y += other.y;
69 return *this; 69 return *this;
70 } 70 }
71 constexpr Vec2<decltype(T{} - T{})> operator-(const Vec2& other) const { 71 [[nodiscard]] constexpr Vec2<decltype(T{} - T{})> operator-(const Vec2& other) const {
72 return {x - other.x, y - other.y}; 72 return {x - other.x, y - other.y};
73 } 73 }
74 constexpr Vec2& operator-=(const Vec2& other) { 74 constexpr Vec2& operator-=(const Vec2& other) {
@@ -78,16 +78,22 @@ public:
78 } 78 }
79 79
80 template <typename U = T> 80 template <typename U = T>
81 constexpr Vec2<std::enable_if_t<std::is_signed_v<U>, U>> operator-() const { 81 [[nodiscard]] constexpr Vec2<std::enable_if_t<std::is_signed_v<U>, U>> operator-() const {
82 return {-x, -y}; 82 return {-x, -y};
83 } 83 }
84 constexpr Vec2<decltype(T{} * T{})> operator*(const Vec2& other) const { 84 [[nodiscard]] constexpr Vec2<decltype(T{} * T{})> operator*(const Vec2& other) const {
85 return {x * other.x, y * other.y}; 85 return {x * other.x, y * other.y};
86 } 86 }
87 87
88 template <typename V> 88 template <typename V>
89 constexpr Vec2<decltype(T{} * V{})> operator*(const V& f) const { 89 [[nodiscard]] constexpr Vec2<decltype(T{} * V{})> operator*(const V& f) const {
90 return {x * f, y * f}; 90 using TV = decltype(T{} * V{});
91 using C = std::common_type_t<T, V>;
92
93 return {
94 static_cast<TV>(static_cast<C>(x) * static_cast<C>(f)),
95 static_cast<TV>(static_cast<C>(y) * static_cast<C>(f)),
96 };
91 } 97 }
92 98
93 template <typename V> 99 template <typename V>
@@ -97,8 +103,14 @@ public:
97 } 103 }
98 104
99 template <typename V> 105 template <typename V>
100 constexpr Vec2<decltype(T{} / V{})> operator/(const V& f) const { 106 [[nodiscard]] constexpr Vec2<decltype(T{} / V{})> operator/(const V& f) const {
101 return {x / f, y / f}; 107 using TV = decltype(T{} / V{});
108 using C = std::common_type_t<T, V>;
109
110 return {
111 static_cast<TV>(static_cast<C>(x) / static_cast<C>(f)),
112 static_cast<TV>(static_cast<C>(y) / static_cast<C>(f)),
113 };
102 } 114 }
103 115
104 template <typename V> 116 template <typename V>
@@ -107,18 +119,18 @@ public:
107 return *this; 119 return *this;
108 } 120 }
109 121
110 constexpr T Length2() const { 122 [[nodiscard]] constexpr T Length2() const {
111 return x * x + y * y; 123 return x * x + y * y;
112 } 124 }
113 125
114 // Only implemented for T=float 126 // Only implemented for T=float
115 float Length() const; 127 [[nodiscard]] float Length() const;
116 float Normalize(); // returns the previous length, which is often useful 128 [[nodiscard]] float Normalize(); // returns the previous length, which is often useful
117 129
118 constexpr T& operator[](std::size_t i) { 130 [[nodiscard]] constexpr T& operator[](std::size_t i) {
119 return *((&x) + i); 131 return *((&x) + i);
120 } 132 }
121 constexpr const T& operator[](std::size_t i) const { 133 [[nodiscard]] constexpr const T& operator[](std::size_t i) const {
122 return *((&x) + i); 134 return *((&x) + i);
123 } 135 }
124 136
@@ -128,47 +140,50 @@ public:
128 } 140 }
129 141
130 // Common aliases: UV (texel coordinates), ST (texture coordinates) 142 // Common aliases: UV (texel coordinates), ST (texture coordinates)
131 constexpr T& u() { 143 [[nodiscard]] constexpr T& u() {
132 return x; 144 return x;
133 } 145 }
134 constexpr T& v() { 146 [[nodiscard]] constexpr T& v() {
135 return y; 147 return y;
136 } 148 }
137 constexpr T& s() { 149 [[nodiscard]] constexpr T& s() {
138 return x; 150 return x;
139 } 151 }
140 constexpr T& t() { 152 [[nodiscard]] constexpr T& t() {
141 return y; 153 return y;
142 } 154 }
143 155
144 constexpr const T& u() const { 156 [[nodiscard]] constexpr const T& u() const {
145 return x; 157 return x;
146 } 158 }
147 constexpr const T& v() const { 159 [[nodiscard]] constexpr const T& v() const {
148 return y; 160 return y;
149 } 161 }
150 constexpr const T& s() const { 162 [[nodiscard]] constexpr const T& s() const {
151 return x; 163 return x;
152 } 164 }
153 constexpr const T& t() const { 165 [[nodiscard]] constexpr const T& t() const {
154 return y; 166 return y;
155 } 167 }
156 168
157 // swizzlers - create a subvector of specific components 169 // swizzlers - create a subvector of specific components
158 constexpr Vec2 yx() const { 170 [[nodiscard]] constexpr Vec2 yx() const {
159 return Vec2(y, x); 171 return Vec2(y, x);
160 } 172 }
161 constexpr Vec2 vu() const { 173 [[nodiscard]] constexpr Vec2 vu() const {
162 return Vec2(y, x); 174 return Vec2(y, x);
163 } 175 }
164 constexpr Vec2 ts() const { 176 [[nodiscard]] constexpr Vec2 ts() const {
165 return Vec2(y, x); 177 return Vec2(y, x);
166 } 178 }
167}; 179};
168 180
169template <typename T, typename V> 181template <typename T, typename V>
170constexpr Vec2<T> operator*(const V& f, const Vec2<T>& vec) { 182[[nodiscard]] constexpr Vec2<T> operator*(const V& f, const Vec2<T>& vec) {
171 return Vec2<T>(f * vec.x, f * vec.y); 183 using C = std::common_type_t<T, V>;
184
185 return Vec2<T>(static_cast<T>(static_cast<C>(f) * static_cast<C>(vec.x)),
186 static_cast<T>(static_cast<C>(f) * static_cast<C>(vec.y)));
172} 187}
173 188
174using Vec2f = Vec2<float>; 189using Vec2f = Vec2<float>;
@@ -196,15 +211,15 @@ public:
196 constexpr Vec3(const T& x_, const T& y_, const T& z_) : x(x_), y(y_), z(z_) {} 211 constexpr Vec3(const T& x_, const T& y_, const T& z_) : x(x_), y(y_), z(z_) {}
197 212
198 template <typename T2> 213 template <typename T2>
199 constexpr Vec3<T2> Cast() const { 214 [[nodiscard]] constexpr Vec3<T2> Cast() const {
200 return Vec3<T2>(static_cast<T2>(x), static_cast<T2>(y), static_cast<T2>(z)); 215 return Vec3<T2>(static_cast<T2>(x), static_cast<T2>(y), static_cast<T2>(z));
201 } 216 }
202 217
203 static constexpr Vec3 AssignToAll(const T& f) { 218 [[nodiscard]] static constexpr Vec3 AssignToAll(const T& f) {
204 return Vec3(f, f, f); 219 return Vec3(f, f, f);
205 } 220 }
206 221
207 constexpr Vec3<decltype(T{} + T{})> operator+(const Vec3& other) const { 222 [[nodiscard]] constexpr Vec3<decltype(T{} + T{})> operator+(const Vec3& other) const {
208 return {x + other.x, y + other.y, z + other.z}; 223 return {x + other.x, y + other.y, z + other.z};
209 } 224 }
210 225
@@ -215,7 +230,7 @@ public:
215 return *this; 230 return *this;
216 } 231 }
217 232
218 constexpr Vec3<decltype(T{} - T{})> operator-(const Vec3& other) const { 233 [[nodiscard]] constexpr Vec3<decltype(T{} - T{})> operator-(const Vec3& other) const {
219 return {x - other.x, y - other.y, z - other.z}; 234 return {x - other.x, y - other.y, z - other.z};
220 } 235 }
221 236
@@ -227,17 +242,24 @@ public:
227 } 242 }
228 243
229 template <typename U = T> 244 template <typename U = T>
230 constexpr Vec3<std::enable_if_t<std::is_signed_v<U>, U>> operator-() const { 245 [[nodiscard]] constexpr Vec3<std::enable_if_t<std::is_signed_v<U>, U>> operator-() const {
231 return {-x, -y, -z}; 246 return {-x, -y, -z};
232 } 247 }
233 248
234 constexpr Vec3<decltype(T{} * T{})> operator*(const Vec3& other) const { 249 [[nodiscard]] constexpr Vec3<decltype(T{} * T{})> operator*(const Vec3& other) const {
235 return {x * other.x, y * other.y, z * other.z}; 250 return {x * other.x, y * other.y, z * other.z};
236 } 251 }
237 252
238 template <typename V> 253 template <typename V>
239 constexpr Vec3<decltype(T{} * V{})> operator*(const V& f) const { 254 [[nodiscard]] constexpr Vec3<decltype(T{} * V{})> operator*(const V& f) const {
240 return {x * f, y * f, z * f}; 255 using TV = decltype(T{} * V{});
256 using C = std::common_type_t<T, V>;
257
258 return {
259 static_cast<TV>(static_cast<C>(x) * static_cast<C>(f)),
260 static_cast<TV>(static_cast<C>(y) * static_cast<C>(f)),
261 static_cast<TV>(static_cast<C>(z) * static_cast<C>(f)),
262 };
241 } 263 }
242 264
243 template <typename V> 265 template <typename V>
@@ -246,8 +268,15 @@ public:
246 return *this; 268 return *this;
247 } 269 }
248 template <typename V> 270 template <typename V>
249 constexpr Vec3<decltype(T{} / V{})> operator/(const V& f) const { 271 [[nodiscard]] constexpr Vec3<decltype(T{} / V{})> operator/(const V& f) const {
250 return {x / f, y / f, z / f}; 272 using TV = decltype(T{} / V{});
273 using C = std::common_type_t<T, V>;
274
275 return {
276 static_cast<TV>(static_cast<C>(x) / static_cast<C>(f)),
277 static_cast<TV>(static_cast<C>(y) / static_cast<C>(f)),
278 static_cast<TV>(static_cast<C>(z) / static_cast<C>(f)),
279 };
251 } 280 }
252 281
253 template <typename V> 282 template <typename V>
@@ -256,20 +285,20 @@ public:
256 return *this; 285 return *this;
257 } 286 }
258 287
259 constexpr T Length2() const { 288 [[nodiscard]] constexpr T Length2() const {
260 return x * x + y * y + z * z; 289 return x * x + y * y + z * z;
261 } 290 }
262 291
263 // Only implemented for T=float 292 // Only implemented for T=float
264 float Length() const; 293 [[nodiscard]] float Length() const;
265 Vec3 Normalized() const; 294 [[nodiscard]] Vec3 Normalized() const;
266 float Normalize(); // returns the previous length, which is often useful 295 [[nodiscard]] float Normalize(); // returns the previous length, which is often useful
267 296
268 constexpr T& operator[](std::size_t i) { 297 [[nodiscard]] constexpr T& operator[](std::size_t i) {
269 return *((&x) + i); 298 return *((&x) + i);
270 } 299 }
271 300
272 constexpr const T& operator[](std::size_t i) const { 301 [[nodiscard]] constexpr const T& operator[](std::size_t i) const {
273 return *((&x) + i); 302 return *((&x) + i);
274 } 303 }
275 304
@@ -280,63 +309,63 @@ public:
280 } 309 }
281 310
282 // Common aliases: UVW (texel coordinates), RGB (colors), STQ (texture coordinates) 311 // Common aliases: UVW (texel coordinates), RGB (colors), STQ (texture coordinates)
283 constexpr T& u() { 312 [[nodiscard]] constexpr T& u() {
284 return x; 313 return x;
285 } 314 }
286 constexpr T& v() { 315 [[nodiscard]] constexpr T& v() {
287 return y; 316 return y;
288 } 317 }
289 constexpr T& w() { 318 [[nodiscard]] constexpr T& w() {
290 return z; 319 return z;
291 } 320 }
292 321
293 constexpr T& r() { 322 [[nodiscard]] constexpr T& r() {
294 return x; 323 return x;
295 } 324 }
296 constexpr T& g() { 325 [[nodiscard]] constexpr T& g() {
297 return y; 326 return y;
298 } 327 }
299 constexpr T& b() { 328 [[nodiscard]] constexpr T& b() {
300 return z; 329 return z;
301 } 330 }
302 331
303 constexpr T& s() { 332 [[nodiscard]] constexpr T& s() {
304 return x; 333 return x;
305 } 334 }
306 constexpr T& t() { 335 [[nodiscard]] constexpr T& t() {
307 return y; 336 return y;
308 } 337 }
309 constexpr T& q() { 338 [[nodiscard]] constexpr T& q() {
310 return z; 339 return z;
311 } 340 }
312 341
313 constexpr const T& u() const { 342 [[nodiscard]] constexpr const T& u() const {
314 return x; 343 return x;
315 } 344 }
316 constexpr const T& v() const { 345 [[nodiscard]] constexpr const T& v() const {
317 return y; 346 return y;
318 } 347 }
319 constexpr const T& w() const { 348 [[nodiscard]] constexpr const T& w() const {
320 return z; 349 return z;
321 } 350 }
322 351
323 constexpr const T& r() const { 352 [[nodiscard]] constexpr const T& r() const {
324 return x; 353 return x;
325 } 354 }
326 constexpr const T& g() const { 355 [[nodiscard]] constexpr const T& g() const {
327 return y; 356 return y;
328 } 357 }
329 constexpr const T& b() const { 358 [[nodiscard]] constexpr const T& b() const {
330 return z; 359 return z;
331 } 360 }
332 361
333 constexpr const T& s() const { 362 [[nodiscard]] constexpr const T& s() const {
334 return x; 363 return x;
335 } 364 }
336 constexpr const T& t() const { 365 [[nodiscard]] constexpr const T& t() const {
337 return y; 366 return y;
338 } 367 }
339 constexpr const T& q() const { 368 [[nodiscard]] constexpr const T& q() const {
340 return z; 369 return z;
341 } 370 }
342 371
@@ -345,7 +374,7 @@ public:
345// _DEFINE_SWIZZLER2 defines a single such function, DEFINE_SWIZZLER2 defines all of them for all 374// _DEFINE_SWIZZLER2 defines a single such function, DEFINE_SWIZZLER2 defines all of them for all
346// component names (x<->r) and permutations (xy<->yx) 375// component names (x<->r) and permutations (xy<->yx)
347#define _DEFINE_SWIZZLER2(a, b, name) \ 376#define _DEFINE_SWIZZLER2(a, b, name) \
348 constexpr Vec2<T> name() const { \ 377 [[nodiscard]] constexpr Vec2<T> name() const { \
349 return Vec2<T>(a, b); \ 378 return Vec2<T>(a, b); \
350 } 379 }
351#define DEFINE_SWIZZLER2(a, b, a2, b2, a3, b3, a4, b4) \ 380#define DEFINE_SWIZZLER2(a, b, a2, b2, a3, b3, a4, b4) \
@@ -366,8 +395,12 @@ public:
366}; 395};
367 396
368template <typename T, typename V> 397template <typename T, typename V>
369constexpr Vec3<T> operator*(const V& f, const Vec3<T>& vec) { 398[[nodiscard]] constexpr Vec3<T> operator*(const V& f, const Vec3<T>& vec) {
370 return Vec3<T>(f * vec.x, f * vec.y, f * vec.z); 399 using C = std::common_type_t<T, V>;
400
401 return Vec3<T>(static_cast<T>(static_cast<C>(f) * static_cast<C>(vec.x)),
402 static_cast<T>(static_cast<C>(f) * static_cast<C>(vec.y)),
403 static_cast<T>(static_cast<C>(f) * static_cast<C>(vec.z)));
371} 404}
372 405
373template <> 406template <>
@@ -402,16 +435,16 @@ public:
402 : x(x_), y(y_), z(z_), w(w_) {} 435 : x(x_), y(y_), z(z_), w(w_) {}
403 436
404 template <typename T2> 437 template <typename T2>
405 constexpr Vec4<T2> Cast() const { 438 [[nodiscard]] constexpr Vec4<T2> Cast() const {
406 return Vec4<T2>(static_cast<T2>(x), static_cast<T2>(y), static_cast<T2>(z), 439 return Vec4<T2>(static_cast<T2>(x), static_cast<T2>(y), static_cast<T2>(z),
407 static_cast<T2>(w)); 440 static_cast<T2>(w));
408 } 441 }
409 442
410 static constexpr Vec4 AssignToAll(const T& f) { 443 [[nodiscard]] static constexpr Vec4 AssignToAll(const T& f) {
411 return Vec4(f, f, f, f); 444 return Vec4(f, f, f, f);
412 } 445 }
413 446
414 constexpr Vec4<decltype(T{} + T{})> operator+(const Vec4& other) const { 447 [[nodiscard]] constexpr Vec4<decltype(T{} + T{})> operator+(const Vec4& other) const {
415 return {x + other.x, y + other.y, z + other.z, w + other.w}; 448 return {x + other.x, y + other.y, z + other.z, w + other.w};
416 } 449 }
417 450
@@ -423,7 +456,7 @@ public:
423 return *this; 456 return *this;
424 } 457 }
425 458
426 constexpr Vec4<decltype(T{} - T{})> operator-(const Vec4& other) const { 459 [[nodiscard]] constexpr Vec4<decltype(T{} - T{})> operator-(const Vec4& other) const {
427 return {x - other.x, y - other.y, z - other.z, w - other.w}; 460 return {x - other.x, y - other.y, z - other.z, w - other.w};
428 } 461 }
429 462
@@ -436,17 +469,25 @@ public:
436 } 469 }
437 470
438 template <typename U = T> 471 template <typename U = T>
439 constexpr Vec4<std::enable_if_t<std::is_signed_v<U>, U>> operator-() const { 472 [[nodiscard]] constexpr Vec4<std::enable_if_t<std::is_signed_v<U>, U>> operator-() const {
440 return {-x, -y, -z, -w}; 473 return {-x, -y, -z, -w};
441 } 474 }
442 475
443 constexpr Vec4<decltype(T{} * T{})> operator*(const Vec4& other) const { 476 [[nodiscard]] constexpr Vec4<decltype(T{} * T{})> operator*(const Vec4& other) const {
444 return {x * other.x, y * other.y, z * other.z, w * other.w}; 477 return {x * other.x, y * other.y, z * other.z, w * other.w};
445 } 478 }
446 479
447 template <typename V> 480 template <typename V>
448 constexpr Vec4<decltype(T{} * V{})> operator*(const V& f) const { 481 [[nodiscard]] constexpr Vec4<decltype(T{} * V{})> operator*(const V& f) const {
449 return {x * f, y * f, z * f, w * f}; 482 using TV = decltype(T{} * V{});
483 using C = std::common_type_t<T, V>;
484
485 return {
486 static_cast<TV>(static_cast<C>(x) * static_cast<C>(f)),
487 static_cast<TV>(static_cast<C>(y) * static_cast<C>(f)),
488 static_cast<TV>(static_cast<C>(z) * static_cast<C>(f)),
489 static_cast<TV>(static_cast<C>(w) * static_cast<C>(f)),
490 };
450 } 491 }
451 492
452 template <typename V> 493 template <typename V>
@@ -456,8 +497,16 @@ public:
456 } 497 }
457 498
458 template <typename V> 499 template <typename V>
459 constexpr Vec4<decltype(T{} / V{})> operator/(const V& f) const { 500 [[nodiscard]] constexpr Vec4<decltype(T{} / V{})> operator/(const V& f) const {
460 return {x / f, y / f, z / f, w / f}; 501 using TV = decltype(T{} / V{});
502 using C = std::common_type_t<T, V>;
503
504 return {
505 static_cast<TV>(static_cast<C>(x) / static_cast<C>(f)),
506 static_cast<TV>(static_cast<C>(y) / static_cast<C>(f)),
507 static_cast<TV>(static_cast<C>(z) / static_cast<C>(f)),
508 static_cast<TV>(static_cast<C>(w) / static_cast<C>(f)),
509 };
461 } 510 }
462 511
463 template <typename V> 512 template <typename V>
@@ -466,15 +515,15 @@ public:
466 return *this; 515 return *this;
467 } 516 }
468 517
469 constexpr T Length2() const { 518 [[nodiscard]] constexpr T Length2() const {
470 return x * x + y * y + z * z + w * w; 519 return x * x + y * y + z * z + w * w;
471 } 520 }
472 521
473 constexpr T& operator[](std::size_t i) { 522 [[nodiscard]] constexpr T& operator[](std::size_t i) {
474 return *((&x) + i); 523 return *((&x) + i);
475 } 524 }
476 525
477 constexpr const T& operator[](std::size_t i) const { 526 [[nodiscard]] constexpr const T& operator[](std::size_t i) const {
478 return *((&x) + i); 527 return *((&x) + i);
479 } 528 }
480 529
@@ -486,29 +535,29 @@ public:
486 } 535 }
487 536
488 // Common alias: RGBA (colors) 537 // Common alias: RGBA (colors)
489 constexpr T& r() { 538 [[nodiscard]] constexpr T& r() {
490 return x; 539 return x;
491 } 540 }
492 constexpr T& g() { 541 [[nodiscard]] constexpr T& g() {
493 return y; 542 return y;
494 } 543 }
495 constexpr T& b() { 544 [[nodiscard]] constexpr T& b() {
496 return z; 545 return z;
497 } 546 }
498 constexpr T& a() { 547 [[nodiscard]] constexpr T& a() {
499 return w; 548 return w;
500 } 549 }
501 550
502 constexpr const T& r() const { 551 [[nodiscard]] constexpr const T& r() const {
503 return x; 552 return x;
504 } 553 }
505 constexpr const T& g() const { 554 [[nodiscard]] constexpr const T& g() const {
506 return y; 555 return y;
507 } 556 }
508 constexpr const T& b() const { 557 [[nodiscard]] constexpr const T& b() const {
509 return z; 558 return z;
510 } 559 }
511 constexpr const T& a() const { 560 [[nodiscard]] constexpr const T& a() const {
512 return w; 561 return w;
513 } 562 }
514 563
@@ -520,7 +569,7 @@ public:
520// DEFINE_SWIZZLER2_COMP2 defines two component functions for all component names (x<->r) and 569// DEFINE_SWIZZLER2_COMP2 defines two component functions for all component names (x<->r) and
521// permutations (xy<->yx) 570// permutations (xy<->yx)
522#define _DEFINE_SWIZZLER2(a, b, name) \ 571#define _DEFINE_SWIZZLER2(a, b, name) \
523 constexpr Vec2<T> name() const { \ 572 [[nodiscard]] constexpr Vec2<T> name() const { \
524 return Vec2<T>(a, b); \ 573 return Vec2<T>(a, b); \
525 } 574 }
526#define DEFINE_SWIZZLER2_COMP1(a, a2) \ 575#define DEFINE_SWIZZLER2_COMP1(a, a2) \
@@ -547,7 +596,7 @@ public:
547#undef _DEFINE_SWIZZLER2 596#undef _DEFINE_SWIZZLER2
548 597
549#define _DEFINE_SWIZZLER3(a, b, c, name) \ 598#define _DEFINE_SWIZZLER3(a, b, c, name) \
550 constexpr Vec3<T> name() const { \ 599 [[nodiscard]] constexpr Vec3<T> name() const { \
551 return Vec3<T>(a, b, c); \ 600 return Vec3<T>(a, b, c); \
552 } 601 }
553#define DEFINE_SWIZZLER3_COMP1(a, a2) \ 602#define DEFINE_SWIZZLER3_COMP1(a, a2) \
@@ -581,8 +630,16 @@ public:
581}; 630};
582 631
583template <typename T, typename V> 632template <typename T, typename V>
584constexpr Vec4<decltype(V{} * T{})> operator*(const V& f, const Vec4<T>& vec) { 633[[nodiscard]] constexpr Vec4<decltype(V{} * T{})> operator*(const V& f, const Vec4<T>& vec) {
585 return {f * vec.x, f * vec.y, f * vec.z, f * vec.w}; 634 using TV = decltype(V{} * T{});
635 using C = std::common_type_t<T, V>;
636
637 return {
638 static_cast<TV>(static_cast<C>(f) * static_cast<C>(vec.x)),
639 static_cast<TV>(static_cast<C>(f) * static_cast<C>(vec.y)),
640 static_cast<TV>(static_cast<C>(f) * static_cast<C>(vec.z)),
641 static_cast<TV>(static_cast<C>(f) * static_cast<C>(vec.w)),
642 };
586} 643}
587 644
588using Vec4f = Vec4<float>; 645using Vec4f = Vec4<float>;
@@ -593,39 +650,41 @@ constexpr decltype(T{} * T{} + T{} * T{}) Dot(const Vec2<T>& a, const Vec2<T>& b
593} 650}
594 651
595template <typename T> 652template <typename T>
596constexpr decltype(T{} * T{} + T{} * T{}) Dot(const Vec3<T>& a, const Vec3<T>& b) { 653[[nodiscard]] constexpr decltype(T{} * T{} + T{} * T{}) Dot(const Vec3<T>& a, const Vec3<T>& b) {
597 return a.x * b.x + a.y * b.y + a.z * b.z; 654 return a.x * b.x + a.y * b.y + a.z * b.z;
598} 655}
599 656
600template <typename T> 657template <typename T>
601constexpr decltype(T{} * T{} + T{} * T{}) Dot(const Vec4<T>& a, const Vec4<T>& b) { 658[[nodiscard]] constexpr decltype(T{} * T{} + T{} * T{}) Dot(const Vec4<T>& a, const Vec4<T>& b) {
602 return a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w; 659 return a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w;
603} 660}
604 661
605template <typename T> 662template <typename T>
606constexpr Vec3<decltype(T{} * T{} - T{} * T{})> Cross(const Vec3<T>& a, const Vec3<T>& b) { 663[[nodiscard]] constexpr Vec3<decltype(T{} * T{} - T{} * T{})> Cross(const Vec3<T>& a,
664 const Vec3<T>& b) {
607 return {a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x}; 665 return {a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x};
608} 666}
609 667
610// linear interpolation via float: 0.0=begin, 1.0=end 668// linear interpolation via float: 0.0=begin, 1.0=end
611template <typename X> 669template <typename X>
612constexpr decltype(X{} * float{} + X{} * float{}) Lerp(const X& begin, const X& end, 670[[nodiscard]] constexpr decltype(X{} * float{} + X{} * float{}) Lerp(const X& begin, const X& end,
613 const float t) { 671 const float t) {
614 return begin * (1.f - t) + end * t; 672 return begin * (1.f - t) + end * t;
615} 673}
616 674
617// linear interpolation via int: 0=begin, base=end 675// linear interpolation via int: 0=begin, base=end
618template <typename X, int base> 676template <typename X, int base>
619constexpr decltype((X{} * int{} + X{} * int{}) / base) LerpInt(const X& begin, const X& end, 677[[nodiscard]] constexpr decltype((X{} * int{} + X{} * int{}) / base) LerpInt(const X& begin,
620 const int t) { 678 const X& end,
679 const int t) {
621 return (begin * (base - t) + end * t) / base; 680 return (begin * (base - t) + end * t) / base;
622} 681}
623 682
624// bilinear interpolation. s is for interpolating x00-x01 and x10-x11, and t is for the second 683// bilinear interpolation. s is for interpolating x00-x01 and x10-x11, and t is for the second
625// interpolation. 684// interpolation.
626template <typename X> 685template <typename X>
627constexpr auto BilinearInterp(const X& x00, const X& x01, const X& x10, const X& x11, const float s, 686[[nodiscard]] constexpr auto BilinearInterp(const X& x00, const X& x01, const X& x10, const X& x11,
628 const float t) { 687 const float s, const float t) {
629 auto y0 = Lerp(x00, x01, s); 688 auto y0 = Lerp(x00, x01, s);
630 auto y1 = Lerp(x10, x11, s); 689 auto y1 = Lerp(x10, x11, s);
631 return Lerp(y0, y1, t); 690 return Lerp(y0, y1, t);
@@ -633,42 +692,42 @@ constexpr auto BilinearInterp(const X& x00, const X& x01, const X& x10, const X&
633 692
634// Utility vector factories 693// Utility vector factories
635template <typename T> 694template <typename T>
636constexpr Vec2<T> MakeVec(const T& x, const T& y) { 695[[nodiscard]] constexpr Vec2<T> MakeVec(const T& x, const T& y) {
637 return Vec2<T>{x, y}; 696 return Vec2<T>{x, y};
638} 697}
639 698
640template <typename T> 699template <typename T>
641constexpr Vec3<T> MakeVec(const T& x, const T& y, const T& z) { 700[[nodiscard]] constexpr Vec3<T> MakeVec(const T& x, const T& y, const T& z) {
642 return Vec3<T>{x, y, z}; 701 return Vec3<T>{x, y, z};
643} 702}
644 703
645template <typename T> 704template <typename T>
646constexpr Vec4<T> MakeVec(const T& x, const T& y, const Vec2<T>& zw) { 705[[nodiscard]] constexpr Vec4<T> MakeVec(const T& x, const T& y, const Vec2<T>& zw) {
647 return MakeVec(x, y, zw[0], zw[1]); 706 return MakeVec(x, y, zw[0], zw[1]);
648} 707}
649 708
650template <typename T> 709template <typename T>
651constexpr Vec3<T> MakeVec(const Vec2<T>& xy, const T& z) { 710[[nodiscard]] constexpr Vec3<T> MakeVec(const Vec2<T>& xy, const T& z) {
652 return MakeVec(xy[0], xy[1], z); 711 return MakeVec(xy[0], xy[1], z);
653} 712}
654 713
655template <typename T> 714template <typename T>
656constexpr Vec3<T> MakeVec(const T& x, const Vec2<T>& yz) { 715[[nodiscard]] constexpr Vec3<T> MakeVec(const T& x, const Vec2<T>& yz) {
657 return MakeVec(x, yz[0], yz[1]); 716 return MakeVec(x, yz[0], yz[1]);
658} 717}
659 718
660template <typename T> 719template <typename T>
661constexpr Vec4<T> MakeVec(const T& x, const T& y, const T& z, const T& w) { 720[[nodiscard]] constexpr Vec4<T> MakeVec(const T& x, const T& y, const T& z, const T& w) {
662 return Vec4<T>{x, y, z, w}; 721 return Vec4<T>{x, y, z, w};
663} 722}
664 723
665template <typename T> 724template <typename T>
666constexpr Vec4<T> MakeVec(const Vec2<T>& xy, const T& z, const T& w) { 725[[nodiscard]] constexpr Vec4<T> MakeVec(const Vec2<T>& xy, const T& z, const T& w) {
667 return MakeVec(xy[0], xy[1], z, w); 726 return MakeVec(xy[0], xy[1], z, w);
668} 727}
669 728
670template <typename T> 729template <typename T>
671constexpr Vec4<T> MakeVec(const T& x, const Vec2<T>& yz, const T& w) { 730[[nodiscard]] constexpr Vec4<T> MakeVec(const T& x, const Vec2<T>& yz, const T& w) {
672 return MakeVec(x, yz[0], yz[1], w); 731 return MakeVec(x, yz[0], yz[1], w);
673} 732}
674 733
@@ -676,17 +735,17 @@ constexpr Vec4<T> MakeVec(const T& x, const Vec2<T>& yz, const T& w) {
676// Even if someone wanted to use an odd object like Vec2<Vec2<T>>, the compiler would error 735// Even if someone wanted to use an odd object like Vec2<Vec2<T>>, the compiler would error
677// out soon enough due to misuse of the returned structure. 736// out soon enough due to misuse of the returned structure.
678template <typename T> 737template <typename T>
679constexpr Vec4<T> MakeVec(const Vec2<T>& xy, const Vec2<T>& zw) { 738[[nodiscard]] constexpr Vec4<T> MakeVec(const Vec2<T>& xy, const Vec2<T>& zw) {
680 return MakeVec(xy[0], xy[1], zw[0], zw[1]); 739 return MakeVec(xy[0], xy[1], zw[0], zw[1]);
681} 740}
682 741
683template <typename T> 742template <typename T>
684constexpr Vec4<T> MakeVec(const Vec3<T>& xyz, const T& w) { 743[[nodiscard]] constexpr Vec4<T> MakeVec(const Vec3<T>& xyz, const T& w) {
685 return MakeVec(xyz[0], xyz[1], xyz[2], w); 744 return MakeVec(xyz[0], xyz[1], xyz[2], w);
686} 745}
687 746
688template <typename T> 747template <typename T>
689constexpr Vec4<T> MakeVec(const T& x, const Vec3<T>& yzw) { 748[[nodiscard]] constexpr Vec4<T> MakeVec(const T& x, const Vec3<T>& yzw) {
690 return MakeVec(x, yzw[0], yzw[1], yzw[2]); 749 return MakeVec(x, yzw[0], yzw[1], yzw[2]);
691} 750}
692 751
diff --git a/src/common/virtual_buffer.cpp b/src/common/virtual_buffer.cpp
new file mode 100644
index 000000000..e3ca29258
--- /dev/null
+++ b/src/common/virtual_buffer.cpp
@@ -0,0 +1,43 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#ifdef _WIN32
6#include <windows.h>
7#else
8#include <sys/mman.h>
9#endif
10
11#include "common/assert.h"
12#include "common/virtual_buffer.h"
13
14namespace Common {
15
16void* AllocateMemoryPages(std::size_t size) noexcept {
17#ifdef _WIN32
18 void* base{VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_READWRITE)};
19#else
20 void* base{mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0)};
21
22 if (base == MAP_FAILED) {
23 base = nullptr;
24 }
25#endif
26
27 ASSERT(base);
28
29 return base;
30}
31
32void FreeMemoryPages(void* base, [[maybe_unused]] std::size_t size) noexcept {
33 if (!base) {
34 return;
35 }
36#ifdef _WIN32
37 ASSERT(VirtualFree(base, 0, MEM_RELEASE));
38#else
39 ASSERT(munmap(base, size) == 0);
40#endif
41}
42
43} // namespace Common
diff --git a/src/common/virtual_buffer.h b/src/common/virtual_buffer.h
new file mode 100644
index 000000000..91d430036
--- /dev/null
+++ b/src/common/virtual_buffer.h
@@ -0,0 +1,82 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <type_traits>
8#include <utility>
9
10namespace Common {
11
12void* AllocateMemoryPages(std::size_t size) noexcept;
13void FreeMemoryPages(void* base, std::size_t size) noexcept;
14
15template <typename T>
16class VirtualBuffer final {
17public:
18 static_assert(
19 std::is_trivially_constructible_v<T>,
20 "T must be trivially constructible, as non-trivial constructors will not be executed "
21 "with the current allocator");
22
23 constexpr VirtualBuffer() = default;
24 explicit VirtualBuffer(std::size_t count) : alloc_size{count * sizeof(T)} {
25 base_ptr = reinterpret_cast<T*>(AllocateMemoryPages(alloc_size));
26 }
27
28 ~VirtualBuffer() noexcept {
29 FreeMemoryPages(base_ptr, alloc_size);
30 }
31
32 VirtualBuffer(const VirtualBuffer&) = delete;
33 VirtualBuffer& operator=(const VirtualBuffer&) = delete;
34
35 VirtualBuffer(VirtualBuffer&& other) noexcept
36 : alloc_size{std::exchange(other.alloc_size, 0)}, base_ptr{std::exchange(other.base_ptr),
37 nullptr} {}
38
39 VirtualBuffer& operator=(VirtualBuffer&& other) noexcept {
40 alloc_size = std::exchange(other.alloc_size, 0);
41 base_ptr = std::exchange(other.base_ptr, nullptr);
42 return *this;
43 }
44
45 void resize(std::size_t count) {
46 const auto new_size = count * sizeof(T);
47 if (new_size == alloc_size) {
48 return;
49 }
50
51 FreeMemoryPages(base_ptr, alloc_size);
52
53 alloc_size = new_size;
54 base_ptr = reinterpret_cast<T*>(AllocateMemoryPages(alloc_size));
55 }
56
57 [[nodiscard]] constexpr const T& operator[](std::size_t index) const {
58 return base_ptr[index];
59 }
60
61 [[nodiscard]] constexpr T& operator[](std::size_t index) {
62 return base_ptr[index];
63 }
64
65 [[nodiscard]] constexpr T* data() {
66 return base_ptr;
67 }
68
69 [[nodiscard]] constexpr const T* data() const {
70 return base_ptr;
71 }
72
73 [[nodiscard]] constexpr std::size_t size() const {
74 return alloc_size / sizeof(T);
75 }
76
77private:
78 std::size_t alloc_size{};
79 T* base_ptr{};
80};
81
82} // namespace Common
diff --git a/src/common/wall_clock.cpp b/src/common/wall_clock.cpp
new file mode 100644
index 000000000..452a2837e
--- /dev/null
+++ b/src/common/wall_clock.cpp
@@ -0,0 +1,91 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/uint128.h"
6#include "common/wall_clock.h"
7
8#ifdef ARCHITECTURE_x86_64
9#include "common/x64/cpu_detect.h"
10#include "common/x64/native_clock.h"
11#endif
12
13namespace Common {
14
15using base_timer = std::chrono::steady_clock;
16using base_time_point = std::chrono::time_point<base_timer>;
17
18class StandardWallClock final : public WallClock {
19public:
20 StandardWallClock(u64 emulated_cpu_frequency, u64 emulated_clock_frequency)
21 : WallClock(emulated_cpu_frequency, emulated_clock_frequency, false) {
22 start_time = base_timer::now();
23 }
24
25 std::chrono::nanoseconds GetTimeNS() override {
26 base_time_point current = base_timer::now();
27 auto elapsed = current - start_time;
28 return std::chrono::duration_cast<std::chrono::nanoseconds>(elapsed);
29 }
30
31 std::chrono::microseconds GetTimeUS() override {
32 base_time_point current = base_timer::now();
33 auto elapsed = current - start_time;
34 return std::chrono::duration_cast<std::chrono::microseconds>(elapsed);
35 }
36
37 std::chrono::milliseconds GetTimeMS() override {
38 base_time_point current = base_timer::now();
39 auto elapsed = current - start_time;
40 return std::chrono::duration_cast<std::chrono::milliseconds>(elapsed);
41 }
42
43 u64 GetClockCycles() override {
44 std::chrono::nanoseconds time_now = GetTimeNS();
45 const u128 temporary =
46 Common::Multiply64Into128(time_now.count(), emulated_clock_frequency);
47 return Common::Divide128On32(temporary, 1000000000).first;
48 }
49
50 u64 GetCPUCycles() override {
51 std::chrono::nanoseconds time_now = GetTimeNS();
52 const u128 temporary = Common::Multiply64Into128(time_now.count(), emulated_cpu_frequency);
53 return Common::Divide128On32(temporary, 1000000000).first;
54 }
55
56 void Pause([[maybe_unused]] bool is_paused) override {
57 // Do nothing in this clock type.
58 }
59
60private:
61 base_time_point start_time;
62};
63
64#ifdef ARCHITECTURE_x86_64
65
66std::unique_ptr<WallClock> CreateBestMatchingClock(u32 emulated_cpu_frequency,
67 u32 emulated_clock_frequency) {
68 const auto& caps = GetCPUCaps();
69 u64 rtsc_frequency = 0;
70 if (caps.invariant_tsc) {
71 rtsc_frequency = EstimateRDTSCFrequency();
72 }
73 if (rtsc_frequency == 0) {
74 return std::make_unique<StandardWallClock>(emulated_cpu_frequency,
75 emulated_clock_frequency);
76 } else {
77 return std::make_unique<X64::NativeClock>(emulated_cpu_frequency, emulated_clock_frequency,
78 rtsc_frequency);
79 }
80}
81
82#else
83
84std::unique_ptr<WallClock> CreateBestMatchingClock(u32 emulated_cpu_frequency,
85 u32 emulated_clock_frequency) {
86 return std::make_unique<StandardWallClock>(emulated_cpu_frequency, emulated_clock_frequency);
87}
88
89#endif
90
91} // namespace Common
diff --git a/src/common/wall_clock.h b/src/common/wall_clock.h
new file mode 100644
index 000000000..bc7adfbf8
--- /dev/null
+++ b/src/common/wall_clock.h
@@ -0,0 +1,55 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <chrono>
8#include <memory>
9
10#include "common/common_types.h"
11
12namespace Common {
13
14class WallClock {
15public:
16 virtual ~WallClock() = default;
17
18 /// Returns current wall time in nanoseconds
19 [[nodiscard]] virtual std::chrono::nanoseconds GetTimeNS() = 0;
20
21 /// Returns current wall time in microseconds
22 [[nodiscard]] virtual std::chrono::microseconds GetTimeUS() = 0;
23
24 /// Returns current wall time in milliseconds
25 [[nodiscard]] virtual std::chrono::milliseconds GetTimeMS() = 0;
26
27 /// Returns current wall time in emulated clock cycles
28 [[nodiscard]] virtual u64 GetClockCycles() = 0;
29
30 /// Returns current wall time in emulated cpu cycles
31 [[nodiscard]] virtual u64 GetCPUCycles() = 0;
32
33 virtual void Pause(bool is_paused) = 0;
34
35 /// Tells if the wall clock, uses the host CPU's hardware clock
36 [[nodiscard]] bool IsNative() const {
37 return is_native;
38 }
39
40protected:
41 WallClock(u64 emulated_cpu_frequency, u64 emulated_clock_frequency, bool is_native)
42 : emulated_cpu_frequency{emulated_cpu_frequency},
43 emulated_clock_frequency{emulated_clock_frequency}, is_native{is_native} {}
44
45 u64 emulated_cpu_frequency;
46 u64 emulated_clock_frequency;
47
48private:
49 bool is_native;
50};
51
52[[nodiscard]] std::unique_ptr<WallClock> CreateBestMatchingClock(u32 emulated_cpu_frequency,
53 u32 emulated_clock_frequency);
54
55} // namespace Common
diff --git a/src/common/web_result.h b/src/common/web_result.h
deleted file mode 100644
index 8bfa2141d..000000000
--- a/src/common/web_result.h
+++ /dev/null
@@ -1,25 +0,0 @@
1// Copyright 2018 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <string>
8#include "common/common_types.h"
9
10namespace Common {
11struct WebResult {
12 enum class Code : u32 {
13 Success,
14 InvalidURL,
15 CredentialsMissing,
16 LibError,
17 HttpError,
18 WrongContent,
19 NoWebservice,
20 };
21 Code result_code;
22 std::string result_string;
23 std::string returned_data;
24};
25} // namespace Common
diff --git a/src/common/x64/cpu_detect.cpp b/src/common/x64/cpu_detect.cpp
index c9349a6b4..fccd2eee5 100644
--- a/src/common/x64/cpu_detect.cpp
+++ b/src/common/x64/cpu_detect.cpp
@@ -62,6 +62,17 @@ static CPUCaps Detect() {
62 std::memcpy(&caps.brand_string[0], &cpu_id[1], sizeof(int)); 62 std::memcpy(&caps.brand_string[0], &cpu_id[1], sizeof(int));
63 std::memcpy(&caps.brand_string[4], &cpu_id[3], sizeof(int)); 63 std::memcpy(&caps.brand_string[4], &cpu_id[3], sizeof(int));
64 std::memcpy(&caps.brand_string[8], &cpu_id[2], sizeof(int)); 64 std::memcpy(&caps.brand_string[8], &cpu_id[2], sizeof(int));
65 if (cpu_id[1] == 0x756e6547 && cpu_id[2] == 0x6c65746e && cpu_id[3] == 0x49656e69)
66 caps.manufacturer = Manufacturer::Intel;
67 else if (cpu_id[1] == 0x68747541 && cpu_id[2] == 0x444d4163 && cpu_id[3] == 0x69746e65)
68 caps.manufacturer = Manufacturer::AMD;
69 else if (cpu_id[1] == 0x6f677948 && cpu_id[2] == 0x656e6975 && cpu_id[3] == 0x6e65476e)
70 caps.manufacturer = Manufacturer::Hygon;
71 else
72 caps.manufacturer = Manufacturer::Unknown;
73
74 u32 family = {};
75 u32 model = {};
65 76
66 __cpuid(cpu_id, 0x80000000); 77 __cpuid(cpu_id, 0x80000000);
67 78
@@ -73,6 +84,14 @@ static CPUCaps Detect() {
73 // Detect family and other miscellaneous features 84 // Detect family and other miscellaneous features
74 if (max_std_fn >= 1) { 85 if (max_std_fn >= 1) {
75 __cpuid(cpu_id, 0x00000001); 86 __cpuid(cpu_id, 0x00000001);
87 family = (cpu_id[0] >> 8) & 0xf;
88 model = (cpu_id[0] >> 4) & 0xf;
89 if (family == 0xf) {
90 family += (cpu_id[0] >> 20) & 0xff;
91 }
92 if (family >= 6) {
93 model += ((cpu_id[0] >> 16) & 0xf) << 4;
94 }
76 95
77 if ((cpu_id[3] >> 25) & 1) 96 if ((cpu_id[3] >> 25) & 1)
78 caps.sse = true; 97 caps.sse = true;
@@ -110,6 +129,11 @@ static CPUCaps Detect() {
110 caps.bmi1 = true; 129 caps.bmi1 = true;
111 if ((cpu_id[1] >> 8) & 1) 130 if ((cpu_id[1] >> 8) & 1)
112 caps.bmi2 = true; 131 caps.bmi2 = true;
132 // Checks for AVX512F, AVX512CD, AVX512VL, AVX512DQ, AVX512BW (Intel Skylake-X/SP)
133 if ((cpu_id[1] >> 16) & 1 && (cpu_id[1] >> 28) & 1 && (cpu_id[1] >> 31) & 1 &&
134 (cpu_id[1] >> 17) & 1 && (cpu_id[1] >> 30) & 1) {
135 caps.avx512 = caps.avx2;
136 }
113 } 137 }
114 } 138 }
115 139
@@ -130,6 +154,20 @@ static CPUCaps Detect() {
130 caps.fma4 = true; 154 caps.fma4 = true;
131 } 155 }
132 156
157 if (max_ex_fn >= 0x80000007) {
158 __cpuid(cpu_id, 0x80000007);
159 if (cpu_id[3] & (1 << 8)) {
160 caps.invariant_tsc = true;
161 }
162 }
163
164 if (max_std_fn >= 0x16) {
165 __cpuid(cpu_id, 0x16);
166 caps.base_frequency = cpu_id[0];
167 caps.max_frequency = cpu_id[1];
168 caps.bus_frequency = cpu_id[2];
169 }
170
133 return caps; 171 return caps;
134} 172}
135 173
diff --git a/src/common/x64/cpu_detect.h b/src/common/x64/cpu_detect.h
index 20f2ba234..e3b63302e 100644
--- a/src/common/x64/cpu_detect.h
+++ b/src/common/x64/cpu_detect.h
@@ -6,8 +6,16 @@
6 6
7namespace Common { 7namespace Common {
8 8
9enum class Manufacturer : u32 {
10 Intel = 0,
11 AMD = 1,
12 Hygon = 2,
13 Unknown = 3,
14};
15
9/// x86/x64 CPU capabilities that may be detected by this module 16/// x86/x64 CPU capabilities that may be detected by this module
10struct CPUCaps { 17struct CPUCaps {
18 Manufacturer manufacturer;
11 char cpu_string[0x21]; 19 char cpu_string[0x21];
12 char brand_string[0x41]; 20 char brand_string[0x41];
13 bool sse; 21 bool sse;
@@ -19,11 +27,16 @@ struct CPUCaps {
19 bool lzcnt; 27 bool lzcnt;
20 bool avx; 28 bool avx;
21 bool avx2; 29 bool avx2;
30 bool avx512;
22 bool bmi1; 31 bool bmi1;
23 bool bmi2; 32 bool bmi2;
24 bool fma; 33 bool fma;
25 bool fma4; 34 bool fma4;
26 bool aes; 35 bool aes;
36 bool invariant_tsc;
37 u32 base_frequency;
38 u32 max_frequency;
39 u32 bus_frequency;
27}; 40};
28 41
29/** 42/**
diff --git a/src/common/x64/native_clock.cpp b/src/common/x64/native_clock.cpp
new file mode 100644
index 000000000..424b39b1f
--- /dev/null
+++ b/src/common/x64/native_clock.cpp
@@ -0,0 +1,103 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <chrono>
6#include <mutex>
7#include <thread>
8
9#ifdef _MSC_VER
10#include <intrin.h>
11#else
12#include <x86intrin.h>
13#endif
14
15#include "common/uint128.h"
16#include "common/x64/native_clock.h"
17
18namespace Common {
19
20u64 EstimateRDTSCFrequency() {
21 const auto milli_10 = std::chrono::milliseconds{10};
22 // get current time
23 _mm_mfence();
24 const u64 tscStart = __rdtsc();
25 const auto startTime = std::chrono::high_resolution_clock::now();
26 // wait roughly 3 seconds
27 while (true) {
28 auto milli = std::chrono::duration_cast<std::chrono::milliseconds>(
29 std::chrono::high_resolution_clock::now() - startTime);
30 if (milli.count() >= 3000)
31 break;
32 std::this_thread::sleep_for(milli_10);
33 }
34 const auto endTime = std::chrono::high_resolution_clock::now();
35 _mm_mfence();
36 const u64 tscEnd = __rdtsc();
37 // calculate difference
38 const u64 timer_diff =
39 std::chrono::duration_cast<std::chrono::nanoseconds>(endTime - startTime).count();
40 const u64 tsc_diff = tscEnd - tscStart;
41 const u64 tsc_freq = MultiplyAndDivide64(tsc_diff, 1000000000ULL, timer_diff);
42 return tsc_freq;
43}
44
45namespace X64 {
46NativeClock::NativeClock(u64 emulated_cpu_frequency, u64 emulated_clock_frequency,
47 u64 rtsc_frequency)
48 : WallClock(emulated_cpu_frequency, emulated_clock_frequency, true), rtsc_frequency{
49 rtsc_frequency} {
50 _mm_mfence();
51 last_measure = __rdtsc();
52 accumulated_ticks = 0U;
53}
54
55u64 NativeClock::GetRTSC() {
56 std::scoped_lock scope{rtsc_serialize};
57 _mm_mfence();
58 const u64 current_measure = __rdtsc();
59 u64 diff = current_measure - last_measure;
60 diff = diff & ~static_cast<u64>(static_cast<s64>(diff) >> 63); // max(diff, 0)
61 if (current_measure > last_measure) {
62 last_measure = current_measure;
63 }
64 accumulated_ticks += diff;
65 /// The clock cannot be more precise than the guest timer, remove the lower bits
66 return accumulated_ticks & inaccuracy_mask;
67}
68
69void NativeClock::Pause(bool is_paused) {
70 if (!is_paused) {
71 _mm_mfence();
72 last_measure = __rdtsc();
73 }
74}
75
76std::chrono::nanoseconds NativeClock::GetTimeNS() {
77 const u64 rtsc_value = GetRTSC();
78 return std::chrono::nanoseconds{MultiplyAndDivide64(rtsc_value, 1000000000, rtsc_frequency)};
79}
80
81std::chrono::microseconds NativeClock::GetTimeUS() {
82 const u64 rtsc_value = GetRTSC();
83 return std::chrono::microseconds{MultiplyAndDivide64(rtsc_value, 1000000, rtsc_frequency)};
84}
85
86std::chrono::milliseconds NativeClock::GetTimeMS() {
87 const u64 rtsc_value = GetRTSC();
88 return std::chrono::milliseconds{MultiplyAndDivide64(rtsc_value, 1000, rtsc_frequency)};
89}
90
91u64 NativeClock::GetClockCycles() {
92 const u64 rtsc_value = GetRTSC();
93 return MultiplyAndDivide64(rtsc_value, emulated_clock_frequency, rtsc_frequency);
94}
95
96u64 NativeClock::GetCPUCycles() {
97 const u64 rtsc_value = GetRTSC();
98 return MultiplyAndDivide64(rtsc_value, emulated_cpu_frequency, rtsc_frequency);
99}
100
101} // namespace X64
102
103} // namespace Common
diff --git a/src/common/x64/native_clock.h b/src/common/x64/native_clock.h
new file mode 100644
index 000000000..97aab6ac9
--- /dev/null
+++ b/src/common/x64/native_clock.h
@@ -0,0 +1,48 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <optional>
8
9#include "common/spin_lock.h"
10#include "common/wall_clock.h"
11
12namespace Common {
13
14namespace X64 {
15class NativeClock final : public WallClock {
16public:
17 NativeClock(u64 emulated_cpu_frequency, u64 emulated_clock_frequency, u64 rtsc_frequency);
18
19 std::chrono::nanoseconds GetTimeNS() override;
20
21 std::chrono::microseconds GetTimeUS() override;
22
23 std::chrono::milliseconds GetTimeMS() override;
24
25 u64 GetClockCycles() override;
26
27 u64 GetCPUCycles() override;
28
29 void Pause(bool is_paused) override;
30
31private:
32 u64 GetRTSC();
33
34 /// value used to reduce the native clocks accuracy as some apss rely on
35 /// undefined behavior where the level of accuracy in the clock shouldn't
36 /// be higher.
37 static constexpr u64 inaccuracy_mask = ~(UINT64_C(0x400) - 1);
38
39 SpinLock rtsc_serialize{};
40 u64 last_measure{};
41 u64 accumulated_ticks{};
42 u64 rtsc_frequency;
43};
44} // namespace X64
45
46u64 EstimateRDTSCFrequency();
47
48} // namespace Common
diff --git a/src/common/x64/xbyak_abi.h b/src/common/x64/xbyak_abi.h
new file mode 100644
index 000000000..26e4bfda5
--- /dev/null
+++ b/src/common/x64/xbyak_abi.h
@@ -0,0 +1,229 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <bitset>
8#include <initializer_list>
9#include <xbyak.h>
10#include "common/assert.h"
11
12namespace Common::X64 {
13
14constexpr std::size_t RegToIndex(const Xbyak::Reg& reg) {
15 using Kind = Xbyak::Reg::Kind;
16 ASSERT_MSG((reg.getKind() & (Kind::REG | Kind::XMM)) != 0,
17 "RegSet only support GPRs and XMM registers.");
18 ASSERT_MSG(reg.getIdx() < 16, "RegSet only supports XXM0-15.");
19 return reg.getIdx() + (reg.getKind() == Kind::REG ? 0 : 16);
20}
21
22constexpr Xbyak::Reg64 IndexToReg64(std::size_t reg_index) {
23 ASSERT(reg_index < 16);
24 return Xbyak::Reg64(static_cast<int>(reg_index));
25}
26
27constexpr Xbyak::Xmm IndexToXmm(std::size_t reg_index) {
28 ASSERT(reg_index >= 16 && reg_index < 32);
29 return Xbyak::Xmm(static_cast<int>(reg_index - 16));
30}
31
32constexpr Xbyak::Reg IndexToReg(std::size_t reg_index) {
33 if (reg_index < 16) {
34 return IndexToReg64(reg_index);
35 } else {
36 return IndexToXmm(reg_index);
37 }
38}
39
40inline std::bitset<32> BuildRegSet(std::initializer_list<Xbyak::Reg> regs) {
41 std::bitset<32> bits;
42 for (const Xbyak::Reg& reg : regs) {
43 bits[RegToIndex(reg)] = true;
44 }
45 return bits;
46}
47
48constexpr inline std::bitset<32> ABI_ALL_GPRS(0x0000FFFF);
49constexpr inline std::bitset<32> ABI_ALL_XMMS(0xFFFF0000);
50
51#ifdef _WIN32
52
53// Microsoft x64 ABI
54constexpr inline Xbyak::Reg ABI_RETURN = Xbyak::util::rax;
55constexpr inline Xbyak::Reg ABI_PARAM1 = Xbyak::util::rcx;
56constexpr inline Xbyak::Reg ABI_PARAM2 = Xbyak::util::rdx;
57constexpr inline Xbyak::Reg ABI_PARAM3 = Xbyak::util::r8;
58constexpr inline Xbyak::Reg ABI_PARAM4 = Xbyak::util::r9;
59
60const std::bitset<32> ABI_ALL_CALLER_SAVED = BuildRegSet({
61 // GPRs
62 Xbyak::util::rcx,
63 Xbyak::util::rdx,
64 Xbyak::util::r8,
65 Xbyak::util::r9,
66 Xbyak::util::r10,
67 Xbyak::util::r11,
68 // XMMs
69 Xbyak::util::xmm0,
70 Xbyak::util::xmm1,
71 Xbyak::util::xmm2,
72 Xbyak::util::xmm3,
73 Xbyak::util::xmm4,
74 Xbyak::util::xmm5,
75});
76
77const std::bitset<32> ABI_ALL_CALLEE_SAVED = BuildRegSet({
78 // GPRs
79 Xbyak::util::rbx,
80 Xbyak::util::rsi,
81 Xbyak::util::rdi,
82 Xbyak::util::rbp,
83 Xbyak::util::r12,
84 Xbyak::util::r13,
85 Xbyak::util::r14,
86 Xbyak::util::r15,
87 // XMMs
88 Xbyak::util::xmm6,
89 Xbyak::util::xmm7,
90 Xbyak::util::xmm8,
91 Xbyak::util::xmm9,
92 Xbyak::util::xmm10,
93 Xbyak::util::xmm11,
94 Xbyak::util::xmm12,
95 Xbyak::util::xmm13,
96 Xbyak::util::xmm14,
97 Xbyak::util::xmm15,
98});
99
100constexpr size_t ABI_SHADOW_SPACE = 0x20;
101
102#else
103
104// System V x86-64 ABI
105constexpr inline Xbyak::Reg ABI_RETURN = Xbyak::util::rax;
106constexpr inline Xbyak::Reg ABI_PARAM1 = Xbyak::util::rdi;
107constexpr inline Xbyak::Reg ABI_PARAM2 = Xbyak::util::rsi;
108constexpr inline Xbyak::Reg ABI_PARAM3 = Xbyak::util::rdx;
109constexpr inline Xbyak::Reg ABI_PARAM4 = Xbyak::util::rcx;
110
111const std::bitset<32> ABI_ALL_CALLER_SAVED = BuildRegSet({
112 // GPRs
113 Xbyak::util::rcx,
114 Xbyak::util::rdx,
115 Xbyak::util::rdi,
116 Xbyak::util::rsi,
117 Xbyak::util::r8,
118 Xbyak::util::r9,
119 Xbyak::util::r10,
120 Xbyak::util::r11,
121 // XMMs
122 Xbyak::util::xmm0,
123 Xbyak::util::xmm1,
124 Xbyak::util::xmm2,
125 Xbyak::util::xmm3,
126 Xbyak::util::xmm4,
127 Xbyak::util::xmm5,
128 Xbyak::util::xmm6,
129 Xbyak::util::xmm7,
130 Xbyak::util::xmm8,
131 Xbyak::util::xmm9,
132 Xbyak::util::xmm10,
133 Xbyak::util::xmm11,
134 Xbyak::util::xmm12,
135 Xbyak::util::xmm13,
136 Xbyak::util::xmm14,
137 Xbyak::util::xmm15,
138});
139
140const std::bitset<32> ABI_ALL_CALLEE_SAVED = BuildRegSet({
141 // GPRs
142 Xbyak::util::rbx,
143 Xbyak::util::rbp,
144 Xbyak::util::r12,
145 Xbyak::util::r13,
146 Xbyak::util::r14,
147 Xbyak::util::r15,
148});
149
150constexpr size_t ABI_SHADOW_SPACE = 0;
151
152#endif
153
154struct ABIFrameInfo {
155 s32 subtraction;
156 s32 xmm_offset;
157};
158
159inline ABIFrameInfo ABI_CalculateFrameSize(std::bitset<32> regs, size_t rsp_alignment,
160 size_t needed_frame_size) {
161 const auto count = (regs & ABI_ALL_GPRS).count();
162 rsp_alignment -= count * 8;
163 size_t subtraction = 0;
164 const auto xmm_count = (regs & ABI_ALL_XMMS).count();
165 if (xmm_count) {
166 // If we have any XMMs to save, we must align the stack here.
167 subtraction = rsp_alignment & 0xF;
168 }
169 subtraction += 0x10 * xmm_count;
170 size_t xmm_base_subtraction = subtraction;
171 subtraction += needed_frame_size;
172 subtraction += ABI_SHADOW_SPACE;
173 // Final alignment.
174 rsp_alignment -= subtraction;
175 subtraction += rsp_alignment & 0xF;
176
177 return ABIFrameInfo{static_cast<s32>(subtraction),
178 static_cast<s32>(subtraction - xmm_base_subtraction)};
179}
180
181inline size_t ABI_PushRegistersAndAdjustStack(Xbyak::CodeGenerator& code, std::bitset<32> regs,
182 size_t rsp_alignment, size_t needed_frame_size = 0) {
183 auto frame_info = ABI_CalculateFrameSize(regs, rsp_alignment, needed_frame_size);
184
185 for (std::size_t i = 0; i < regs.size(); ++i) {
186 if (regs[i] && ABI_ALL_GPRS[i]) {
187 code.push(IndexToReg64(i));
188 }
189 }
190
191 if (frame_info.subtraction != 0) {
192 code.sub(code.rsp, frame_info.subtraction);
193 }
194
195 for (std::size_t i = 0; i < regs.size(); ++i) {
196 if (regs[i] && ABI_ALL_XMMS[i]) {
197 code.movaps(code.xword[code.rsp + frame_info.xmm_offset], IndexToXmm(i));
198 frame_info.xmm_offset += 0x10;
199 }
200 }
201
202 return ABI_SHADOW_SPACE;
203}
204
205inline void ABI_PopRegistersAndAdjustStack(Xbyak::CodeGenerator& code, std::bitset<32> regs,
206 size_t rsp_alignment, size_t needed_frame_size = 0) {
207 auto frame_info = ABI_CalculateFrameSize(regs, rsp_alignment, needed_frame_size);
208
209 for (std::size_t i = 0; i < regs.size(); ++i) {
210 if (regs[i] && ABI_ALL_XMMS[i]) {
211 code.movaps(IndexToXmm(i), code.xword[code.rsp + frame_info.xmm_offset]);
212 frame_info.xmm_offset += 0x10;
213 }
214 }
215
216 if (frame_info.subtraction != 0) {
217 code.add(code.rsp, frame_info.subtraction);
218 }
219
220 // GPRs need to be popped in reverse order
221 for (std::size_t j = 0; j < regs.size(); ++j) {
222 const std::size_t i = regs.size() - j - 1;
223 if (regs[i] && ABI_ALL_GPRS[i]) {
224 code.pop(IndexToReg64(i));
225 }
226 }
227}
228
229} // namespace Common::X64
diff --git a/src/common/x64/xbyak_util.h b/src/common/x64/xbyak_util.h
new file mode 100644
index 000000000..df17f8cbe
--- /dev/null
+++ b/src/common/x64/xbyak_util.h
@@ -0,0 +1,47 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <type_traits>
8#include <xbyak.h>
9#include "common/x64/xbyak_abi.h"
10
11namespace Common::X64 {
12
13// Constants for use with cmpps/cmpss
14enum {
15 CMP_EQ = 0,
16 CMP_LT = 1,
17 CMP_LE = 2,
18 CMP_UNORD = 3,
19 CMP_NEQ = 4,
20 CMP_NLT = 5,
21 CMP_NLE = 6,
22 CMP_ORD = 7,
23};
24
25constexpr bool IsWithin2G(uintptr_t ref, uintptr_t target) {
26 const u64 distance = target - (ref + 5);
27 return !(distance >= 0x8000'0000ULL && distance <= ~0x8000'0000ULL);
28}
29
30inline bool IsWithin2G(const Xbyak::CodeGenerator& code, uintptr_t target) {
31 return IsWithin2G(reinterpret_cast<uintptr_t>(code.getCurr()), target);
32}
33
34template <typename T>
35inline void CallFarFunction(Xbyak::CodeGenerator& code, const T f) {
36 static_assert(std::is_pointer_v<T>, "Argument must be a (function) pointer.");
37 size_t addr = reinterpret_cast<size_t>(f);
38 if (IsWithin2G(code, addr)) {
39 code.call(f);
40 } else {
41 // ABI_RETURN is a safe temp register to use before a call
42 code.mov(ABI_RETURN, addr);
43 code.call(ABI_RETURN);
44 }
45}
46
47} // namespace Common::X64
diff --git a/src/common/zstd_compression.cpp b/src/common/zstd_compression.cpp
index 978526492..5f45459da 100644
--- a/src/common/zstd_compression.cpp
+++ b/src/common/zstd_compression.cpp
@@ -5,7 +5,6 @@
5#include <algorithm> 5#include <algorithm>
6#include <zstd.h> 6#include <zstd.h>
7 7
8#include "common/assert.h"
9#include "common/zstd_compression.h" 8#include "common/zstd_compression.h"
10 9
11namespace Common::Compression { 10namespace Common::Compression {
diff --git a/src/common/zstd_compression.h b/src/common/zstd_compression.h
index e9de941c8..c26a30ab9 100644
--- a/src/common/zstd_compression.h
+++ b/src/common/zstd_compression.h
@@ -13,24 +13,25 @@ namespace Common::Compression {
13/** 13/**
14 * Compresses a source memory region with Zstandard and returns the compressed data in a vector. 14 * Compresses a source memory region with Zstandard and returns the compressed data in a vector.
15 * 15 *
16 * @param source the uncompressed source memory region. 16 * @param source The uncompressed source memory region.
17 * @param source_size the size in bytes of the uncompressed source memory region. 17 * @param source_size The size of the uncompressed source memory region.
18 * @param compression_level the used compression level. Should be between 1 and 22. 18 * @param compression_level The used compression level. Should be between 1 and 22.
19 * 19 *
20 * @return the compressed data. 20 * @return the compressed data.
21 */ 21 */
22std::vector<u8> CompressDataZSTD(const u8* source, std::size_t source_size, s32 compression_level); 22[[nodiscard]] std::vector<u8> CompressDataZSTD(const u8* source, std::size_t source_size,
23 s32 compression_level);
23 24
24/** 25/**
25 * Compresses a source memory region with Zstandard with the default compression level and returns 26 * Compresses a source memory region with Zstandard with the default compression level and returns
26 * the compressed data in a vector. 27 * the compressed data in a vector.
27 * 28 *
28 * @param source the uncompressed source memory region. 29 * @param source The uncompressed source memory region.
29 * @param source_size the size in bytes of the uncompressed source memory region. 30 * @param source_size The size of the uncompressed source memory region.
30 * 31 *
31 * @return the compressed data. 32 * @return the compressed data.
32 */ 33 */
33std::vector<u8> CompressDataZSTDDefault(const u8* source, std::size_t source_size); 34[[nodiscard]] std::vector<u8> CompressDataZSTDDefault(const u8* source, std::size_t source_size);
34 35
35/** 36/**
36 * Decompresses a source memory region with Zstandard and returns the uncompressed data in a vector. 37 * Decompresses a source memory region with Zstandard and returns the uncompressed data in a vector.
@@ -39,6 +40,6 @@ std::vector<u8> CompressDataZSTDDefault(const u8* source, std::size_t source_siz
39 * 40 *
40 * @return the decompressed data. 41 * @return the decompressed data.
41 */ 42 */
42std::vector<u8> DecompressDataZSTD(const std::vector<u8>& compressed); 43[[nodiscard]] std::vector<u8> DecompressDataZSTD(const std::vector<u8>& compressed);
43 44
44} // namespace Common::Compression \ No newline at end of file 45} // namespace Common::Compression \ No newline at end of file