diff options
Diffstat (limited to 'src/common')
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 | ) |
| 169 | endif() | 192 | endif() |
| 170 | 193 | ||
| 194 | if (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 | ) | ||
| 204 | else() | ||
| 205 | target_compile_options(common PRIVATE | ||
| 206 | -Werror | ||
| 207 | ) | ||
| 208 | endif() | ||
| 209 | |||
| 171 | create_target_directory_groups(common) | 210 | create_target_directory_groups(common) |
| 211 | find_package(Boost 1.71 COMPONENTS context headers REQUIRED) | ||
| 172 | 212 | ||
| 173 | target_link_libraries(common PUBLIC Boost::boost fmt microprofile) | 213 | target_link_libraries(common PUBLIC ${Boost_LIBRARIES} fmt::fmt microprofile) |
| 174 | target_link_libraries(common PRIVATE lz4_static libzstd_static) | 214 | target_link_libraries(common PRIVATE lz4::lz4 xbyak) |
| 215 | if (MSVC) | ||
| 216 | target_link_libraries(common PRIVATE zstd::zstd) | ||
| 217 | else() | ||
| 218 | target_link_libraries(common PRIVATE zstd) | ||
| 219 | endif() | ||
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 @@ | |||
| 15 | namespace Common { | 15 | namespace Common { |
| 16 | 16 | ||
| 17 | template <class ForwardIt, class T, class Compare = std::less<>> | 17 | template <class ForwardIt, class T, class Compare = std::less<>> |
| 18 | ForwardIt 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 | ||
| 9 | namespace Common { | 9 | namespace Common { |
| 10 | 10 | ||
| 11 | template <typename T> | 11 | template <typename T> |
| 12 | constexpr 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 | ||
| 17 | template <typename T> | 19 | template <typename T> |
| 18 | constexpr 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 | ||
| 23 | template <typename T> | 25 | template <typename T> |
| 24 | constexpr 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 | ||
| 29 | template <typename T> | 31 | template <typename T> |
| 30 | constexpr 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 | ||
| 35 | template <typename T> | 37 | template <typename T> |
| 36 | constexpr 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 | ||
| 43 | template <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 | |||
| 41 | template <typename T, std::size_t Align = 16> | 50 | template <typename T, std::size_t Align = 16> |
| 42 | class AlignmentAllocator { | 51 | class AlignmentAllocator { |
| 43 | public: | 52 | public: |
| @@ -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 | ||
| 59 | public: | ||
| 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. |
| 18 | template <typename Fn> | 18 | template <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) { | 24 | static void |
| 25 | assert_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 | |||
| 13 | namespace Common { | ||
| 14 | |||
| 15 | #if _MSC_VER | ||
| 16 | |||
| 17 | bool 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 | |||
| 23 | bool 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 | |||
| 29 | bool 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 | |||
| 35 | bool 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 | |||
| 41 | bool 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 | |||
| 49 | bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected) { | ||
| 50 | return __sync_bool_compare_and_swap(pointer, expected, value); | ||
| 51 | } | ||
| 52 | |||
| 53 | bool AtomicCompareAndSwap(volatile u16* pointer, u16 value, u16 expected) { | ||
| 54 | return __sync_bool_compare_and_swap(pointer, expected, value); | ||
| 55 | } | ||
| 56 | |||
| 57 | bool AtomicCompareAndSwap(volatile u32* pointer, u32 value, u32 expected) { | ||
| 58 | return __sync_bool_compare_and_swap(pointer, expected, value); | ||
| 59 | } | ||
| 60 | |||
| 61 | bool AtomicCompareAndSwap(volatile u64* pointer, u64 value, u64 expected) { | ||
| 62 | return __sync_bool_compare_and_swap(pointer, expected, value); | ||
| 63 | } | ||
| 64 | |||
| 65 | bool 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 | |||
| 9 | namespace 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 | |||
| 10 | namespace Common { | ||
| 11 | |||
| 12 | template <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> | ||
| 16 | BitCast(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. |
| 19 | template <typename T> | 19 | template <typename T> |
| 20 | constexpr 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 |
| 25 | inline 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 | ||
| 35 | inline 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 |
| 45 | inline 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 | ||
| 53 | inline 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 |
| 63 | inline 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 | ||
| 73 | inline 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 |
| 83 | inline 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 | ||
| 91 | inline 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 | ||
| 102 | inline 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 | ||
| 108 | inline 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 | ||
| 116 | inline 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 | ||
| 120 | inline 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 | ||
| 126 | inline 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 | ||
| 130 | inline 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 | ||
| 135 | inline 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 | ||
| 139 | inline 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 | ||
| 68 | namespace Common { | 68 | namespace Common { |
| 69 | 69 | ||
| 70 | typedef std::pair<uint64_t, uint64_t> uint128; | 70 | using uint128 = std::pair<uint64_t, uint64_t>; |
| 71 | 71 | ||
| 72 | inline 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 | } |
| 75 | inline 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. |
| 80 | uint64_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. |
| 84 | uint64_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. |
| 88 | uint64_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. |
| 91 | uint128 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. |
| 95 | uint128 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. |
| 99 | inline 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 | ||
| 13 | namespace Color { | 13 | namespace Common::Color { |
| 14 | 14 | ||
| 15 | /// Convert a 1-bit color component to 8 bit | 15 | /// Convert a 1-bit color component to 8 bit |
| 16 | constexpr 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 |
| 21 | constexpr 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 |
| 26 | constexpr 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 |
| 31 | constexpr 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 |
| 36 | constexpr 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 |
| 41 | constexpr 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 |
| 46 | constexpr 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 |
| 51 | constexpr 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 | */ |
| 60 | inline 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 | */ |
| 69 | inline 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 | */ |
| 78 | inline 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 | */ |
| 87 | inline 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 | */ |
| 99 | inline 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 | */ |
| 111 | inline 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 | */ |
| 123 | inline 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 | */ |
| 134 | inline 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 | */ |
| 143 | inline 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. |
| 56 | std::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 | ||
| 58 | namespace Common { | 96 | namespace Common { |
| 59 | 97 | ||
| 60 | constexpr 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 | |||
| 9 | namespace Common { | ||
| 10 | |||
| 11 | // Check if type is like an STL container | ||
| 12 | template <typename T> | ||
| 13 | concept 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. | ||
| 28 | template <typename Derived, typename Base> | ||
| 29 | concept 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 { | |||
| 21 | DynamicLibrary::DynamicLibrary() = default; | 21 | DynamicLibrary::DynamicLibrary() = default; |
| 22 | 22 | ||
| 23 | DynamicLibrary::DynamicLibrary(const char* filename) { | 23 | DynamicLibrary::DynamicLibrary(const char* filename) { |
| 24 | Open(filename); | 24 | void(Open(filename)); |
| 25 | } | 25 | } |
| 26 | 26 | ||
| 27 | DynamicLibrary::DynamicLibrary(DynamicLibrary&& rhs) noexcept | 27 | DynamicLibrary::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 | |||
| 15 | namespace Common { | ||
| 16 | |||
| 17 | constexpr std::size_t default_stack_size = 256 * 1024; // 256kb | ||
| 18 | |||
| 19 | struct 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 | |||
| 42 | void Fiber::SetStartParameter(void* new_parameter) { | ||
| 43 | impl->start_parameter = new_parameter; | ||
| 44 | } | ||
| 45 | |||
| 46 | void 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 | |||
| 53 | void 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 | |||
| 61 | void 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 | |||
| 70 | void Fiber::FiberStartFunc(void* fiber_parameter) { | ||
| 71 | auto* fiber = static_cast<Fiber*>(fiber_parameter); | ||
| 72 | fiber->Start(); | ||
| 73 | } | ||
| 74 | |||
| 75 | void Fiber::RewindStartFunc(void* fiber_parameter) { | ||
| 76 | auto* fiber = static_cast<Fiber*>(fiber_parameter); | ||
| 77 | fiber->OnRewind(); | ||
| 78 | } | ||
| 79 | |||
| 80 | Fiber::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 | |||
| 87 | Fiber::Fiber() : impl{std::make_unique<FiberImpl>()} {} | ||
| 88 | |||
| 89 | Fiber::~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 | |||
| 102 | void 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 | |||
| 112 | void 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 | |||
| 119 | void 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 | |||
| 130 | std::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 | |||
| 140 | void 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 | |||
| 149 | void 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 | |||
| 160 | void Fiber::FiberStartFunc(boost::context::detail::transfer_t transfer) { | ||
| 161 | auto* fiber = static_cast<Fiber*>(transfer.data); | ||
| 162 | fiber->Start(transfer); | ||
| 163 | } | ||
| 164 | |||
| 165 | void Fiber::RewindStartFunc(boost::context::detail::transfer_t transfer) { | ||
| 166 | auto* fiber = static_cast<Fiber*>(transfer.data); | ||
| 167 | fiber->OnRewind(transfer); | ||
| 168 | } | ||
| 169 | |||
| 170 | Fiber::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 | |||
| 181 | Fiber::Fiber() : impl{std::make_unique<FiberImpl>()} {} | ||
| 182 | |||
| 183 | Fiber::~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 | |||
| 195 | void 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 | |||
| 204 | void 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 | |||
| 213 | void 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 | |||
| 225 | std::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) | ||
| 11 | namespace boost::context::detail { | ||
| 12 | struct transfer_t; | ||
| 13 | } | ||
| 14 | #endif | ||
| 15 | |||
| 16 | namespace 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 | */ | ||
| 33 | class Fiber { | ||
| 34 | public: | ||
| 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 | |||
| 59 | private: | ||
| 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! |
| 77 | namespace FileUtil { | 77 | namespace 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) { | |||
| 371 | bool CreateEmptyFile(const std::string& filename) { | 371 | bool 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 | ||
| 474 | bool DeleteDirRecursively(const std::string& directory, unsigned int recursion) { | 474 | bool 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 | ||
| 495 | void CopyDir(const std::string& source_path, const std::string& dest_path) { | 496 | void 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 | ||
| 765 | void SplitFilename83(const std::string& filename, std::array<char, 9>& short_name, | 775 | void 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 | ||
| 897 | IOFile::IOFile() {} | 917 | IOFile::IOFile() = default; |
| 898 | 918 | ||
| 899 | IOFile::IOFile(const std::string& filename, const char openmode[], int flags) { | 919 | IOFile::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 | ||
| 903 | IOFile::~IOFile() { | 923 | IOFile::~IOFile() { |
| @@ -938,17 +958,18 @@ bool IOFile::Open(const std::string& filename, const char openmode[], int flags) | |||
| 938 | } | 958 | } |
| 939 | 959 | ||
| 940 | bool IOFile::Close() { | 960 | bool 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 | ||
| 948 | u64 IOFile::GetSize() const { | 969 | u64 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 | ||
| 959 | u64 IOFile::Tell() const { | 980 | u64 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 | ||
| 991 | std::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 | |||
| 1005 | std::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 | |||
| 970 | bool IOFile::Resize(u64 size) { | 1019 | bool 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 | ||
| 22 | namespace FileUtil { | 22 | namespace Common::FS { |
| 23 | 23 | ||
| 24 | // User paths for GetUserPath | 24 | // User paths for GetUserPath |
| 25 | enum class UserPath { | 25 | enum 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 |
| 50 | bool 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 |
| 53 | bool 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) |
| 56 | u64 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 |
| 59 | u64 GetSize(const int fd); | 60 | [[nodiscard]] u64 GetSize(int fd); |
| 60 | 61 | ||
| 61 | // Overloaded GetSize, accepts FILE* | 62 | // Overloaded GetSize, accepts FILE* |
| 62 | u64 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. |
| 65 | bool CreateDir(const std::string& filename); | 66 | bool CreateDir(const std::string& filename); |
| @@ -119,7 +120,7 @@ u64 ScanDirectoryTree(const std::string& directory, FSTEntry& parent_entry, | |||
| 119 | bool DeleteDirRecursively(const std::string& directory, unsigned int recursion = 256); | 120 | bool DeleteDirRecursively(const std::string& directory, unsigned int recursion = 256); |
| 120 | 121 | ||
| 121 | // Returns the current directory | 122 | // Returns the current directory |
| 122 | std::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) |
| 125 | void CopyDir(const std::string& source_path, const std::string& dest_path); | 126 | void 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). |
| 132 | const std::string& GetUserPath(UserPath path, const std::string& new_path = ""); | 133 | const std::string& GetUserPath(UserPath path, const std::string& new_path = ""); |
| 133 | 134 | ||
| 134 | std::string GetHactoolConfigurationPath(); | 135 | [[nodiscard]] std::string GetHactoolConfigurationPath(); |
| 135 | 136 | ||
| 136 | std::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 |
| 139 | std::string GetSysDirectory(); | 140 | [[nodiscard]] std::string GetSysDirectory(); |
| 140 | 141 | ||
| 141 | #ifdef __APPLE__ | 142 | #ifdef __APPLE__ |
| 142 | std::string GetBundleDirectory(); | 143 | [[nodiscard]] std::string GetBundleDirectory(); |
| 143 | #endif | 144 | #endif |
| 144 | 145 | ||
| 145 | #ifdef _WIN32 | 146 | #ifdef _WIN32 |
| 146 | const std::string& GetExeDirectory(); | 147 | [[nodiscard]] const std::string& GetExeDirectory(); |
| 147 | std::string AppDataRoamingDirectory(); | 148 | [[nodiscard]] std::string AppDataRoamingDirectory(); |
| 148 | #endif | 149 | #endif |
| 149 | 150 | ||
| 150 | std::size_t WriteStringToFile(bool text_file, const std::string& filename, std::string_view str); | 151 | std::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" } |
| 166 | std::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. |
| 169 | std::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. |
| 172 | std::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 |
| 175 | std::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 |
| 178 | std::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 |
| 181 | std::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. |
| 184 | template <typename T> | 185 | template <typename T> |
| 185 | std::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 | ||
| 192 | enum class DirectorySeparator { ForwardSlash, BackwardSlash, PlatformDefault }; | 195 | enum 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 |
| 196 | std::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 | ||
| 208 | template <typename T> | ||
| 209 | void 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 | ||
| 280 | private: | 291 | private: |
| 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: | ||
| 287 | template <typename T> | ||
| 288 | void 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 | ||
| 14 | namespace Common { | 11 | namespace 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 | */ | ||
| 22 | static 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 | */ | ||
| 31 | template <typename T> | ||
| 32 | static 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 | |||
| 38 | struct PairHash { | 13 | struct 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 | ||
| 8 | namespace Common { | 7 | namespace Common { |
| 9 | 8 | ||
| 10 | u8 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 | |||
| 21 | std::vector<u8> HexStringToVector(std::string_view str, bool little_endian) { | 9 | std::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 | ||
| 33 | std::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 | |||
| 44 | std::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 | ||
| 15 | namespace Common { | 15 | namespace Common { |
| 16 | 16 | ||
| 17 | u8 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 | ||
| 19 | std::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 | ||
| 21 | template <std::size_t Size, bool le = false> | 31 | template <std::size_t Size, bool le = false> |
| 22 | std::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 | ||
| 34 | template <typename ContiguousContainer> | 46 | template <typename ContiguousContainer> |
| 35 | std::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 | ||
| 51 | std::array<u8, 0x10> operator"" _array16(const char* str, std::size_t len); | 63 | [[nodiscard]] constexpr std::array<u8, 16> AsArray(const char (&data)[17]) { |
| 52 | std::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 | ||
| 27 | namespace Log { | 28 | namespace 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) | |||
| 152 | void FileBackend::Write(const Entry& entry) { | 153 | void 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 | */ |
| 22 | struct Entry { | 22 | struct 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 | ||
| 102 | private: | 96 | private: |
| 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 | */ |
| 21 | std::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 | */ |
| 35 | std::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 | */ |
| 45 | std::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 | */ |
| 55 | std::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 | ||
| 10 | namespace Common { | 10 | namespace Common { |
| 11 | 11 | ||
| 12 | constexpr float PI = 3.14159265f; | 12 | constexpr float PI = 3.1415926535f; |
| 13 | 13 | ||
| 14 | template <class T> | 14 | template <class T> |
| 15 | struct Rectangle { | 15 | struct 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 | ||
| 56 | template <typename T> | 56 | template <typename T> |
| 57 | Rectangle(T, T, T, T)->Rectangle<T>; | 57 | Rectangle(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 | |||
| 23 | namespace Common { | ||
| 24 | |||
| 25 | // Detects the RAM and Swapfile sizes | ||
| 26 | static 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 | |||
| 68 | const 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 | |||
| 9 | namespace Common { | ||
| 10 | |||
| 11 | struct 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. |
| 18 | std::string GetLastErrorMsg() { | 18 | std::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 | ||
| 7 | namespace Common { | 7 | namespace Common { |
| 8 | 8 | ||
| 9 | PageTable::PageTable(std::size_t page_size_in_bits) : page_size_in_bits{page_size_in_bits} {} | 9 | PageTable::PageTable() = default; |
| 10 | 10 | ||
| 11 | PageTable::~PageTable() = default; | 11 | PageTable::~PageTable() noexcept = default; |
| 12 | |||
| 13 | void 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 | ||
| 13 | void 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 | |||
| 29 | BackingPageTable::BackingPageTable(std::size_t page_size_in_bits) : PageTable{page_size_in_bits} {} | ||
| 30 | |||
| 31 | BackingPageTable::~BackingPageTable() = default; | ||
| 32 | |||
| 33 | void 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 | ||
| 12 | namespace Common { | 13 | namespace 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 | */ |
| 49 | struct PageTable { | 50 | struct 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 | */ | ||
| 86 | struct 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 | ||
| 40 | template <typename T> | 71 | template <typename T> |
| 41 | auto 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 | ||
| 45 | inline 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> | |||
| 12 | struct ScopeExitHelper { | 12 | struct 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 | ||
| 21 | template <typename Func> | 28 | template <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 | |||
| 21 | namespace { | ||
| 22 | |||
| 23 | void 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 | |||
| 35 | namespace Common { | ||
| 36 | |||
| 37 | void SpinLock::lock() { | ||
| 38 | while (lck.test_and_set(std::memory_order_acquire)) { | ||
| 39 | ThreadPause(); | ||
| 40 | } | ||
| 41 | } | ||
| 42 | |||
| 43 | void SpinLock::unlock() { | ||
| 44 | lck.clear(std::memory_order_release); | ||
| 45 | } | ||
| 46 | |||
| 47 | bool 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 | |||
| 9 | namespace 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 | */ | ||
| 16 | class SpinLock { | ||
| 17 | public: | ||
| 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 | |||
| 30 | private: | ||
| 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 | |||
| 9 | namespace Common { | ||
| 10 | |||
| 11 | Stream::Stream() = default; | ||
| 12 | Stream::~Stream() = default; | ||
| 13 | |||
| 14 | void 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 | |||
| 30 | u8 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 | |||
| 38 | void 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 | |||
| 10 | namespace Common { | ||
| 11 | |||
| 12 | enum class SeekOrigin { | ||
| 13 | SetOrigin, | ||
| 14 | FromCurrentPos, | ||
| 15 | FromEnd, | ||
| 16 | }; | ||
| 17 | |||
| 18 | class Stream { | ||
| 19 | public: | ||
| 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 | |||
| 51 | private: | ||
| 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 |
| 22 | std::string ToLower(std::string str) { | 23 | std::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 |
| 29 | std::string ToUpper(std::string str) { | 30 | std::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 @@ | |||
| 12 | namespace Common { | 12 | namespace Common { |
| 13 | 13 | ||
| 14 | /// Make a string lowercase | 14 | /// Make a string lowercase |
| 15 | std::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 |
| 18 | std::string ToUpper(std::string str); | 18 | [[nodiscard]] std::string ToUpper(std::string str); |
| 19 | 19 | ||
| 20 | std::string StringFromBuffer(const std::vector<u8>& data); | 20 | [[nodiscard]] std::string StringFromBuffer(const std::vector<u8>& data); |
| 21 | 21 | ||
| 22 | std::string StripSpaces(const std::string& s); | 22 | [[nodiscard]] std::string StripSpaces(const std::string& s); |
| 23 | std::string StripQuotes(const std::string& s); | 23 | [[nodiscard]] std::string StripQuotes(const std::string& s); |
| 24 | 24 | ||
| 25 | std::string StringFromBool(bool value); | 25 | [[nodiscard]] std::string StringFromBool(bool value); |
| 26 | 26 | ||
| 27 | std::string TabsToSpaces(int tab_size, std::string in); | 27 | [[nodiscard]] std::string TabsToSpaces(int tab_size, std::string in); |
| 28 | 28 | ||
| 29 | void SplitString(const std::string& str, char delim, std::vector<std::string>& output); | 29 | void 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 | ||
| 35 | void BuildCompleteFilename(std::string& _CompleteFilename, const std::string& _Path, | 35 | void BuildCompleteFilename(std::string& _CompleteFilename, const std::string& _Path, |
| 36 | const std::string& _Filename); | 36 | const std::string& _Filename); |
| 37 | std::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 | ||
| 39 | std::string UTF16ToUTF8(const std::u16string& input); | 40 | [[nodiscard]] std::string UTF16ToUTF8(const std::u16string& input); |
| 40 | std::u16string UTF8ToUTF16(const std::string& input); | 41 | [[nodiscard]] std::u16string UTF8ToUTF16(const std::string& input); |
| 41 | 42 | ||
| 42 | #ifdef _WIN32 | 43 | #ifdef _WIN32 |
| 43 | std::string UTF16ToUTF8(const std::wstring& input); | 44 | [[nodiscard]] std::string UTF16ToUTF8(const std::wstring& input); |
| 44 | std::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 | */ |
| 52 | template <typename InIt> | 53 | template <typename InIt> |
| 53 | bool 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 | */ |
| 67 | std::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 | */ |
| 74 | std::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 | */ | ||
| 87 | const 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 | |||
| 57 | namespace Common { | 28 | namespace 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 | 649 | using LETag = std::conditional_t<std::endian::native == std::endian::little, KeepTag, SwapTag>; |
| 679 | 650 | using BETag = std::conditional_t<std::endian::native == std::endian::big, KeepTag, SwapTag>; | |
| 680 | using LETag = KeepTag; | ||
| 681 | using BETag = SwapTag; | ||
| 682 | |||
| 683 | #else | ||
| 684 | |||
| 685 | using BETag = KeepTag; | ||
| 686 | using LETag = SwapTag; | ||
| 687 | |||
| 688 | #endif | ||
| 689 | 651 | ||
| 690 | // Aliases for LE types | 652 | // Aliases for LE types |
| 691 | using u16_le = AddEndian<u16, LETag>::type; | 653 | using 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 | ||
| 15 | namespace Telemetry { | 15 | namespace Common::Telemetry { |
| 16 | 16 | ||
| 17 | void FieldCollection::Accept(VisitorInterface& visitor) const { | 17 | void 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 | ||
| 13 | namespace Telemetry { | 13 | namespace 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 |
| 16 | enum class FieldType : u8 { | 16 | enum 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 | ||
| 92 | private: | 92 | private: |
| @@ -196,4 +196,4 @@ void AppendCPUInfo(FieldCollection& fc); | |||
| 196 | /// such as platform name, etc. | 196 | /// such as platform name, etc. |
| 197 | void AppendOSInfo(FieldCollection& fc); | 197 | void 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 | ||
| 26 | namespace Common { | 30 | namespace Common { |
| 27 | 31 | ||
| 32 | #ifdef _WIN32 | ||
| 33 | |||
| 34 | void 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 | |||
| 59 | void 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, ¶ms); | ||
| 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) | ||
| 132 | void 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 | ||
| 13 | namespace Common { | 15 | namespace 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 | ||
| 56 | private: | 57 | private: |
| 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 | ||
| 62 | class Barrier { | 63 | class 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 | ||
| 90 | enum class ThreadPriority : u32 { | ||
| 91 | Low = 0, | ||
| 92 | Normal = 1, | ||
| 93 | High = 2, | ||
| 94 | VeryHigh = 3, | ||
| 95 | }; | ||
| 96 | |||
| 97 | void SetCurrentThreadPriority(ThreadPriority new_priority); | ||
| 98 | |||
| 89 | void SetCurrentThreadName(const char* name); | 99 | void 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: | |||
| 130 | template <typename T> | 130 | template <typename T> |
| 131 | class MPSCQueue { | 131 | class MPSCQueue { |
| 132 | public: | 132 | public: |
| 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 | |||
| 12 | namespace Common::TimeZone { | ||
| 13 | |||
| 14 | std::string GetDefaultTimeZone() { | ||
| 15 | return "GMT"; | ||
| 16 | } | ||
| 17 | |||
| 18 | static 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 | |||
| 28 | static 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 | |||
| 40 | std::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 | |||
| 10 | namespace 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 | // ---------------- |
| 143 | double Timer::GetDoubleTime() { | 143 | double 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 | ||
| 35 | private: | 35 | private: |
| 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 | ||
| 13 | namespace Common { | 14 | namespace Common { |
| 14 | 15 | ||
| 16 | #ifdef _MSC_VER | ||
| 17 | |||
| 18 | u64 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 | |||
| 31 | u64 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 | |||
| 15 | u128 Multiply64Into128(u64 a, u64 b) { | 41 | u128 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 | ||
| 10 | namespace Common { | 10 | namespace 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; |
| 13 | u128 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 |
| 17 | std::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 | }; |
| 46 | static_assert(sizeof(UUID) == 16, "UUID is an invalid size!"); | 51 | static_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 | ||
| 169 | template <typename T, typename V> | 181 | template <typename T, typename V> |
| 170 | constexpr 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 | ||
| 174 | using Vec2f = Vec2<float>; | 189 | using 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 | ||
| 368 | template <typename T, typename V> | 397 | template <typename T, typename V> |
| 369 | constexpr 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 | ||
| 373 | template <> | 406 | template <> |
| @@ -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 | ||
| 583 | template <typename T, typename V> | 632 | template <typename T, typename V> |
| 584 | constexpr 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 | ||
| 588 | using Vec4f = Vec4<float>; | 645 | using 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 | ||
| 595 | template <typename T> | 652 | template <typename T> |
| 596 | constexpr 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 | ||
| 600 | template <typename T> | 657 | template <typename T> |
| 601 | constexpr 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 | ||
| 605 | template <typename T> | 662 | template <typename T> |
| 606 | constexpr 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 |
| 611 | template <typename X> | 669 | template <typename X> |
| 612 | constexpr 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 |
| 618 | template <typename X, int base> | 676 | template <typename X, int base> |
| 619 | constexpr 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. |
| 626 | template <typename X> | 685 | template <typename X> |
| 627 | constexpr 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 |
| 635 | template <typename T> | 694 | template <typename T> |
| 636 | constexpr 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 | ||
| 640 | template <typename T> | 699 | template <typename T> |
| 641 | constexpr 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 | ||
| 645 | template <typename T> | 704 | template <typename T> |
| 646 | constexpr 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 | ||
| 650 | template <typename T> | 709 | template <typename T> |
| 651 | constexpr 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 | ||
| 655 | template <typename T> | 714 | template <typename T> |
| 656 | constexpr 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 | ||
| 660 | template <typename T> | 719 | template <typename T> |
| 661 | constexpr 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 | ||
| 665 | template <typename T> | 724 | template <typename T> |
| 666 | constexpr 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 | ||
| 670 | template <typename T> | 729 | template <typename T> |
| 671 | constexpr 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. |
| 678 | template <typename T> | 737 | template <typename T> |
| 679 | constexpr 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 | ||
| 683 | template <typename T> | 742 | template <typename T> |
| 684 | constexpr 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 | ||
| 688 | template <typename T> | 747 | template <typename T> |
| 689 | constexpr 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 | |||
| 14 | namespace Common { | ||
| 15 | |||
| 16 | void* 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 | |||
| 32 | void 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 | |||
| 10 | namespace Common { | ||
| 11 | |||
| 12 | void* AllocateMemoryPages(std::size_t size) noexcept; | ||
| 13 | void FreeMemoryPages(void* base, std::size_t size) noexcept; | ||
| 14 | |||
| 15 | template <typename T> | ||
| 16 | class VirtualBuffer final { | ||
| 17 | public: | ||
| 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 | |||
| 77 | private: | ||
| 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 | |||
| 13 | namespace Common { | ||
| 14 | |||
| 15 | using base_timer = std::chrono::steady_clock; | ||
| 16 | using base_time_point = std::chrono::time_point<base_timer>; | ||
| 17 | |||
| 18 | class StandardWallClock final : public WallClock { | ||
| 19 | public: | ||
| 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 | |||
| 60 | private: | ||
| 61 | base_time_point start_time; | ||
| 62 | }; | ||
| 63 | |||
| 64 | #ifdef ARCHITECTURE_x86_64 | ||
| 65 | |||
| 66 | std::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 | |||
| 84 | std::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 | |||
| 12 | namespace Common { | ||
| 13 | |||
| 14 | class WallClock { | ||
| 15 | public: | ||
| 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 | |||
| 40 | protected: | ||
| 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 | |||
| 48 | private: | ||
| 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 | |||
| 10 | namespace Common { | ||
| 11 | struct 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 | ||
| 7 | namespace Common { | 7 | namespace Common { |
| 8 | 8 | ||
| 9 | enum 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 |
| 10 | struct CPUCaps { | 17 | struct 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 | |||
| 18 | namespace Common { | ||
| 19 | |||
| 20 | u64 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 | |||
| 45 | namespace X64 { | ||
| 46 | NativeClock::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 | |||
| 55 | u64 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 | |||
| 69 | void NativeClock::Pause(bool is_paused) { | ||
| 70 | if (!is_paused) { | ||
| 71 | _mm_mfence(); | ||
| 72 | last_measure = __rdtsc(); | ||
| 73 | } | ||
| 74 | } | ||
| 75 | |||
| 76 | std::chrono::nanoseconds NativeClock::GetTimeNS() { | ||
| 77 | const u64 rtsc_value = GetRTSC(); | ||
| 78 | return std::chrono::nanoseconds{MultiplyAndDivide64(rtsc_value, 1000000000, rtsc_frequency)}; | ||
| 79 | } | ||
| 80 | |||
| 81 | std::chrono::microseconds NativeClock::GetTimeUS() { | ||
| 82 | const u64 rtsc_value = GetRTSC(); | ||
| 83 | return std::chrono::microseconds{MultiplyAndDivide64(rtsc_value, 1000000, rtsc_frequency)}; | ||
| 84 | } | ||
| 85 | |||
| 86 | std::chrono::milliseconds NativeClock::GetTimeMS() { | ||
| 87 | const u64 rtsc_value = GetRTSC(); | ||
| 88 | return std::chrono::milliseconds{MultiplyAndDivide64(rtsc_value, 1000, rtsc_frequency)}; | ||
| 89 | } | ||
| 90 | |||
| 91 | u64 NativeClock::GetClockCycles() { | ||
| 92 | const u64 rtsc_value = GetRTSC(); | ||
| 93 | return MultiplyAndDivide64(rtsc_value, emulated_clock_frequency, rtsc_frequency); | ||
| 94 | } | ||
| 95 | |||
| 96 | u64 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 | |||
| 12 | namespace Common { | ||
| 13 | |||
| 14 | namespace X64 { | ||
| 15 | class NativeClock final : public WallClock { | ||
| 16 | public: | ||
| 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 | |||
| 31 | private: | ||
| 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 | |||
| 46 | u64 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 | |||
| 12 | namespace Common::X64 { | ||
| 13 | |||
| 14 | constexpr 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 | |||
| 22 | constexpr Xbyak::Reg64 IndexToReg64(std::size_t reg_index) { | ||
| 23 | ASSERT(reg_index < 16); | ||
| 24 | return Xbyak::Reg64(static_cast<int>(reg_index)); | ||
| 25 | } | ||
| 26 | |||
| 27 | constexpr 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 | |||
| 32 | constexpr 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 | |||
| 40 | inline 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 | |||
| 48 | constexpr inline std::bitset<32> ABI_ALL_GPRS(0x0000FFFF); | ||
| 49 | constexpr inline std::bitset<32> ABI_ALL_XMMS(0xFFFF0000); | ||
| 50 | |||
| 51 | #ifdef _WIN32 | ||
| 52 | |||
| 53 | // Microsoft x64 ABI | ||
| 54 | constexpr inline Xbyak::Reg ABI_RETURN = Xbyak::util::rax; | ||
| 55 | constexpr inline Xbyak::Reg ABI_PARAM1 = Xbyak::util::rcx; | ||
| 56 | constexpr inline Xbyak::Reg ABI_PARAM2 = Xbyak::util::rdx; | ||
| 57 | constexpr inline Xbyak::Reg ABI_PARAM3 = Xbyak::util::r8; | ||
| 58 | constexpr inline Xbyak::Reg ABI_PARAM4 = Xbyak::util::r9; | ||
| 59 | |||
| 60 | const 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 | |||
| 77 | const 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 | |||
| 100 | constexpr size_t ABI_SHADOW_SPACE = 0x20; | ||
| 101 | |||
| 102 | #else | ||
| 103 | |||
| 104 | // System V x86-64 ABI | ||
| 105 | constexpr inline Xbyak::Reg ABI_RETURN = Xbyak::util::rax; | ||
| 106 | constexpr inline Xbyak::Reg ABI_PARAM1 = Xbyak::util::rdi; | ||
| 107 | constexpr inline Xbyak::Reg ABI_PARAM2 = Xbyak::util::rsi; | ||
| 108 | constexpr inline Xbyak::Reg ABI_PARAM3 = Xbyak::util::rdx; | ||
| 109 | constexpr inline Xbyak::Reg ABI_PARAM4 = Xbyak::util::rcx; | ||
| 110 | |||
| 111 | const 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 | |||
| 140 | const 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 | |||
| 150 | constexpr size_t ABI_SHADOW_SPACE = 0; | ||
| 151 | |||
| 152 | #endif | ||
| 153 | |||
| 154 | struct ABIFrameInfo { | ||
| 155 | s32 subtraction; | ||
| 156 | s32 xmm_offset; | ||
| 157 | }; | ||
| 158 | |||
| 159 | inline 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 | |||
| 181 | inline 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 | |||
| 205 | inline 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 | |||
| 11 | namespace Common::X64 { | ||
| 12 | |||
| 13 | // Constants for use with cmpps/cmpss | ||
| 14 | enum { | ||
| 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 | |||
| 25 | constexpr 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 | |||
| 30 | inline bool IsWithin2G(const Xbyak::CodeGenerator& code, uintptr_t target) { | ||
| 31 | return IsWithin2G(reinterpret_cast<uintptr_t>(code.getCurr()), target); | ||
| 32 | } | ||
| 33 | |||
| 34 | template <typename T> | ||
| 35 | inline 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 | ||
| 11 | namespace Common::Compression { | 10 | namespace 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 | */ |
| 22 | std::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 | */ |
| 33 | std::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 | */ |
| 42 | std::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 |