aboutsummaryrefslogtreecommitdiff
path: root/src/common
diff options
context:
space:
mode:
Diffstat (limited to 'src/common')
-rw-r--r--src/common/CMakeLists.txt51
-rw-r--r--src/common/algorithm.h3
-rw-r--r--src/common/alignment.h69
-rw-r--r--src/common/assert.h7
-rw-r--r--src/common/atomic_ops.cpp75
-rw-r--r--src/common/atomic_ops.h17
-rw-r--r--src/common/bit_cast.h22
-rw-r--r--src/common/bit_field.h21
-rw-r--r--src/common/bit_util.h34
-rw-r--r--src/common/cityhash.h23
-rw-r--r--src/common/color.h38
-rw-r--r--src/common/common_funcs.h42
-rw-r--r--src/common/common_paths.h1
-rw-r--r--src/common/concepts.h34
-rw-r--r--src/common/detached_tasks.cpp3
-rw-r--r--src/common/dynamic_library.cpp2
-rw-r--r--src/common/dynamic_library.h13
-rw-r--r--src/common/fiber.cpp233
-rw-r--r--src/common/fiber.h78
-rw-r--r--src/common/file_util.cpp133
-rw-r--r--src/common/file_util.h98
-rw-r--r--src/common/hash.h25
-rw-r--r--src/common/hex_util.cpp34
-rw-r--r--src/common/hex_util.h37
-rw-r--r--src/common/logging/backend.cpp39
-rw-r--r--src/common/logging/backend.h16
-rw-r--r--src/common/logging/log.h1
-rw-r--r--src/common/lz4_compression.cpp16
-rw-r--r--src/common/lz4_compression.h24
-rw-r--r--src/common/math_util.h22
-rw-r--r--src/common/memory_detect.cpp73
-rw-r--r--src/common/memory_detect.h22
-rw-r--r--src/common/misc.cpp15
-rw-r--r--src/common/multi_level_queue.h37
-rw-r--r--src/common/page_table.cpp36
-rw-r--r--src/common/page_table.h53
-rw-r--r--src/common/param_package.h12
-rw-r--r--src/common/quaternion.h45
-rw-r--r--src/common/ring_buffer.h4
-rw-r--r--src/common/scope_exit.h9
-rw-r--r--src/common/spin_lock.cpp54
-rw-r--r--src/common/spin_lock.h34
-rw-r--r--src/common/stream.cpp47
-rw-r--r--src/common/stream.h56
-rw-r--r--src/common/string_util.cpp5
-rw-r--r--src/common/string_util.h46
-rw-r--r--src/common/swap.h46
-rw-r--r--src/common/telemetry.cpp5
-rw-r--r--src/common/telemetry.h16
-rw-r--r--src/common/thread.cpp64
-rw-r--r--src/common/thread.h22
-rw-r--r--src/common/thread_queue_list.h10
-rw-r--r--src/common/threadsafe_queue.h12
-rw-r--r--src/common/time_zone.cpp49
-rw-r--r--src/common/time_zone.h18
-rw-r--r--src/common/timer.cpp12
-rw-r--r--src/common/timer.h16
-rw-r--r--src/common/uint128.cpp26
-rw-r--r--src/common/uint128.h7
-rw-r--r--src/common/uuid.h17
-rw-r--r--src/common/vector_math.h279
-rw-r--r--src/common/virtual_buffer.cpp43
-rw-r--r--src/common/virtual_buffer.h82
-rw-r--r--src/common/wall_clock.cpp91
-rw-r--r--src/common/wall_clock.h55
-rw-r--r--src/common/web_result.h25
-rw-r--r--src/common/x64/cpu_detect.cpp38
-rw-r--r--src/common/x64/cpu_detect.h13
-rw-r--r--src/common/x64/native_clock.cpp103
-rw-r--r--src/common/x64/native_clock.h48
-rw-r--r--src/common/x64/xbyak_abi.h229
-rw-r--r--src/common/x64/xbyak_util.h47
-rw-r--r--src/common/zstd_compression.cpp1
-rw-r--r--src/common/zstd_compression.h17
74 files changed, 2445 insertions, 705 deletions
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index eeceaa6552..d20e6c3b54 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -32,6 +32,8 @@ add_custom_command(OUTPUT scm_rev.cpp
DEPENDS
# WARNING! It was too much work to try and make a common location for this list,
# so if you need to change it, please update CMakeModules/GenerateSCMRev.cmake as well
+ "${VIDEO_CORE}/renderer_opengl/gl_arb_decompiler.cpp"
+ "${VIDEO_CORE}/renderer_opengl/gl_arb_decompiler.h"
"${VIDEO_CORE}/renderer_opengl/gl_shader_cache.cpp"
"${VIDEO_CORE}/renderer_opengl/gl_shader_cache.h"
"${VIDEO_CORE}/renderer_opengl/gl_shader_decompiler.cpp"
@@ -96,8 +98,11 @@ add_library(common STATIC
algorithm.h
alignment.h
assert.h
+ atomic_ops.cpp
+ atomic_ops.h
detached_tasks.cpp
detached_tasks.h
+ bit_cast.h
bit_field.h
bit_util.h
cityhash.cpp
@@ -106,8 +111,11 @@ add_library(common STATIC
common_funcs.h
common_paths.h
common_types.h
+ concepts.h
dynamic_library.cpp
dynamic_library.h
+ fiber.cpp
+ fiber.h
file_util.cpp
file_util.h
hash.h
@@ -123,6 +131,8 @@ add_library(common STATIC
lz4_compression.cpp
lz4_compression.h
math_util.h
+ memory_detect.cpp
+ memory_detect.h
memory_hook.cpp
memory_hook.h
microprofile.cpp
@@ -139,6 +149,10 @@ add_library(common STATIC
scm_rev.cpp
scm_rev.h
scope_exit.h
+ spin_lock.cpp
+ spin_lock.h
+ stream.cpp
+ stream.h
string_util.cpp
string_util.h
swap.h
@@ -148,6 +162,8 @@ add_library(common STATIC
thread.h
thread_queue_list.h
threadsafe_queue.h
+ time_zone.cpp
+ time_zone.h
timer.cpp
timer.h
uint128.cpp
@@ -155,7 +171,10 @@ add_library(common STATIC
uuid.cpp
uuid.h
vector_math.h
- web_result.h
+ virtual_buffer.cpp
+ virtual_buffer.h
+ wall_clock.cpp
+ wall_clock.h
zstd_compression.cpp
zstd_compression.h
)
@@ -165,10 +184,36 @@ if(ARCHITECTURE_x86_64)
PRIVATE
x64/cpu_detect.cpp
x64/cpu_detect.h
+ x64/native_clock.cpp
+ x64/native_clock.h
+ x64/xbyak_abi.h
+ x64/xbyak_util.h
)
endif()
+if (MSVC)
+ target_compile_definitions(common PRIVATE
+ # The standard library doesn't provide any replacement for codecvt yet
+ # so we can disable this deprecation warning for the time being.
+ _SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING
+ )
+ target_compile_options(common PRIVATE
+ /W4
+ /WX
+ )
+else()
+ target_compile_options(common PRIVATE
+ -Werror
+ )
+endif()
+
create_target_directory_groups(common)
+find_package(Boost 1.71 COMPONENTS context headers REQUIRED)
-target_link_libraries(common PUBLIC Boost::boost fmt microprofile)
-target_link_libraries(common PRIVATE lz4_static libzstd_static)
+target_link_libraries(common PUBLIC ${Boost_LIBRARIES} fmt::fmt microprofile)
+target_link_libraries(common PRIVATE lz4::lz4 xbyak)
+if (MSVC)
+ target_link_libraries(common PRIVATE zstd::zstd)
+else()
+ target_link_libraries(common PRIVATE zstd)
+endif()
diff --git a/src/common/algorithm.h b/src/common/algorithm.h
index e21b1373ca..4804a3421c 100644
--- a/src/common/algorithm.h
+++ b/src/common/algorithm.h
@@ -15,7 +15,8 @@
namespace Common {
template <class ForwardIt, class T, class Compare = std::less<>>
-ForwardIt BinaryFind(ForwardIt first, ForwardIt last, const T& value, Compare comp = {}) {
+[[nodiscard]] ForwardIt BinaryFind(ForwardIt first, ForwardIt last, const T& value,
+ Compare comp = {}) {
// Note: BOTH type T and the type after ForwardIt is dereferenced
// must be implicitly convertible to BOTH Type1 and Type2, used in Compare.
// This is stricter than lower_bound requirement (see above)
diff --git a/src/common/alignment.h b/src/common/alignment.h
index cdd4833f81..5040043de9 100644
--- a/src/common/alignment.h
+++ b/src/common/alignment.h
@@ -3,41 +3,50 @@
#pragma once
#include <cstddef>
-#include <memory>
+#include <new>
#include <type_traits>
namespace Common {
template <typename T>
-constexpr T AlignUp(T value, std::size_t size) {
+[[nodiscard]] constexpr T AlignUp(T value, std::size_t size) {
static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
- return static_cast<T>(value + (size - value % size) % size);
+ auto mod{static_cast<T>(value % size)};
+ value -= mod;
+ return static_cast<T>(mod == T{0} ? value : value + size);
}
template <typename T>
-constexpr T AlignDown(T value, std::size_t size) {
+[[nodiscard]] constexpr T AlignDown(T value, std::size_t size) {
static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
return static_cast<T>(value - value % size);
}
template <typename T>
-constexpr T AlignBits(T value, std::size_t align) {
+[[nodiscard]] constexpr T AlignBits(T value, std::size_t align) {
static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
return static_cast<T>((value + ((1ULL << align) - 1)) >> align << align);
}
template <typename T>
-constexpr bool Is4KBAligned(T value) {
+[[nodiscard]] constexpr bool Is4KBAligned(T value) {
static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
return (value & 0xFFF) == 0;
}
template <typename T>
-constexpr bool IsWordAligned(T value) {
+[[nodiscard]] constexpr bool IsWordAligned(T value) {
static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
return (value & 0b11) == 0;
}
+template <typename T>
+[[nodiscard]] constexpr bool IsAligned(T value, std::size_t alignment) {
+ using U = typename std::make_unsigned<T>::type;
+ const U mask = static_cast<U>(alignment - 1);
+ return (value & mask) == 0;
+}
+
template <typename T, std::size_t Align = 16>
class AlignmentAllocator {
public:
@@ -45,66 +54,28 @@ public:
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
- using pointer = T*;
- using const_pointer = const T*;
-
- using reference = T&;
- using const_reference = const T&;
-
using propagate_on_container_copy_assignment = std::true_type;
using propagate_on_container_move_assignment = std::true_type;
using propagate_on_container_swap = std::true_type;
using is_always_equal = std::true_type;
-public:
constexpr AlignmentAllocator() noexcept = default;
template <typename T2>
constexpr AlignmentAllocator(const AlignmentAllocator<T2, Align>&) noexcept {}
- pointer address(reference r) noexcept {
- return std::addressof(r);
- }
-
- const_pointer address(const_reference r) const noexcept {
- return std::addressof(r);
- }
-
- pointer allocate(size_type n) {
- return static_cast<pointer>(::operator new (n, std::align_val_t{Align}));
+ [[nodiscard]] T* allocate(size_type n) {
+ return static_cast<T*>(::operator new (n * sizeof(T), std::align_val_t{Align}));
}
- void deallocate(pointer p, size_type) {
- ::operator delete (p, std::align_val_t{Align});
- }
-
- void construct(pointer p, const value_type& wert) {
- new (p) value_type(wert);
- }
-
- void destroy(pointer p) {
- p->~value_type();
- }
-
- size_type max_size() const noexcept {
- return size_type(-1) / sizeof(value_type);
+ void deallocate(T* p, size_type n) {
+ ::operator delete (p, n * sizeof(T), std::align_val_t{Align});
}
template <typename T2>
struct rebind {
using other = AlignmentAllocator<T2, Align>;
};
-
- bool operator!=(const AlignmentAllocator<T, Align>& other) const noexcept {
- return !(*this == other);
- }
-
- // Returns true if and only if storage allocated from *this
- // can be deallocated from other, and vice versa.
- // Always returns true for stateless allocators.
- bool operator==(const AlignmentAllocator<T, Align>& other) const noexcept {
- return true;
- }
};
} // namespace Common
diff --git a/src/common/assert.h b/src/common/assert.h
index 5b67c5c527..06d7b5612b 100644
--- a/src/common/assert.h
+++ b/src/common/assert.h
@@ -17,11 +17,12 @@
// enough for our purposes.
template <typename Fn>
#if defined(_MSC_VER)
-__declspec(noinline, noreturn)
+[[msvc::noinline, noreturn]]
#elif defined(__GNUC__)
- __attribute__((noinline, noreturn, cold))
+[[gnu::cold, gnu::noinline, noreturn]]
#endif
- static void assert_noinline_call(const Fn& fn) {
+static void
+assert_noinline_call(const Fn& fn) {
fn();
Crash();
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 0000000000..1612d0e67a
--- /dev/null
+++ b/src/common/atomic_ops.cpp
@@ -0,0 +1,75 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <cstring>
+
+#include "common/atomic_ops.h"
+
+#if _MSC_VER
+#include <intrin.h>
+#endif
+
+namespace Common {
+
+#if _MSC_VER
+
+bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected) {
+ const u8 result =
+ _InterlockedCompareExchange8(reinterpret_cast<volatile char*>(pointer), value, expected);
+ return result == expected;
+}
+
+bool AtomicCompareAndSwap(volatile u16* pointer, u16 value, u16 expected) {
+ const u16 result =
+ _InterlockedCompareExchange16(reinterpret_cast<volatile short*>(pointer), value, expected);
+ return result == expected;
+}
+
+bool AtomicCompareAndSwap(volatile u32* pointer, u32 value, u32 expected) {
+ const u32 result =
+ _InterlockedCompareExchange(reinterpret_cast<volatile long*>(pointer), value, expected);
+ return result == expected;
+}
+
+bool AtomicCompareAndSwap(volatile u64* pointer, u64 value, u64 expected) {
+ const u64 result = _InterlockedCompareExchange64(reinterpret_cast<volatile __int64*>(pointer),
+ value, expected);
+ return result == expected;
+}
+
+bool AtomicCompareAndSwap(volatile u64* pointer, u128 value, u128 expected) {
+ return _InterlockedCompareExchange128(reinterpret_cast<volatile __int64*>(pointer), value[1],
+ value[0],
+ reinterpret_cast<__int64*>(expected.data())) != 0;
+}
+
+#else
+
+bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected) {
+ return __sync_bool_compare_and_swap(pointer, expected, value);
+}
+
+bool AtomicCompareAndSwap(volatile u16* pointer, u16 value, u16 expected) {
+ return __sync_bool_compare_and_swap(pointer, expected, value);
+}
+
+bool AtomicCompareAndSwap(volatile u32* pointer, u32 value, u32 expected) {
+ return __sync_bool_compare_and_swap(pointer, expected, value);
+}
+
+bool AtomicCompareAndSwap(volatile u64* pointer, u64 value, u64 expected) {
+ return __sync_bool_compare_and_swap(pointer, expected, value);
+}
+
+bool AtomicCompareAndSwap(volatile u64* pointer, u128 value, u128 expected) {
+ unsigned __int128 value_a;
+ unsigned __int128 expected_a;
+ std::memcpy(&value_a, value.data(), sizeof(u128));
+ std::memcpy(&expected_a, expected.data(), sizeof(u128));
+ return __sync_bool_compare_and_swap((unsigned __int128*)pointer, expected_a, value_a);
+}
+
+#endif
+
+} // namespace Common
diff --git a/src/common/atomic_ops.h b/src/common/atomic_ops.h
new file mode 100644
index 0000000000..b468885893
--- /dev/null
+++ b/src/common/atomic_ops.h
@@ -0,0 +1,17 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "common/common_types.h"
+
+namespace Common {
+
+[[nodiscard]] bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected);
+[[nodiscard]] bool AtomicCompareAndSwap(volatile u16* pointer, u16 value, u16 expected);
+[[nodiscard]] bool AtomicCompareAndSwap(volatile u32* pointer, u32 value, u32 expected);
+[[nodiscard]] bool AtomicCompareAndSwap(volatile u64* pointer, u64 value, u64 expected);
+[[nodiscard]] bool AtomicCompareAndSwap(volatile u64* pointer, u128 value, u128 expected);
+
+} // namespace Common
diff --git a/src/common/bit_cast.h b/src/common/bit_cast.h
new file mode 100644
index 0000000000..a32a063d1e
--- /dev/null
+++ b/src/common/bit_cast.h
@@ -0,0 +1,22 @@
+// Copyright 2020 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <cstring>
+#include <type_traits>
+
+namespace Common {
+
+template <typename To, typename From>
+[[nodiscard]] std::enable_if_t<sizeof(To) == sizeof(From) && std::is_trivially_copyable_v<From> &&
+ std::is_trivially_copyable_v<To>,
+ To>
+BitCast(const From& src) noexcept {
+ To dst;
+ std::memcpy(&dst, &src, sizeof(To));
+ return dst;
+}
+
+} // namespace Common
diff --git a/src/common/bit_field.h b/src/common/bit_field.h
index fd2bbbd99f..0f0661172c 100644
--- a/src/common/bit_field.h
+++ b/src/common/bit_field.h
@@ -36,13 +36,6 @@
#include "common/common_funcs.h"
#include "common/swap.h"
-// Inlining
-#ifdef _WIN32
-#define FORCE_INLINE __forceinline
-#else
-#define FORCE_INLINE inline __attribute__((always_inline))
-#endif
-
/*
* Abstract bitfield class
*
@@ -142,8 +135,8 @@ public:
* containing several bitfields can be assembled by formatting each of their values and ORing
* the results together.
*/
- static constexpr FORCE_INLINE StorageType FormatValue(const T& value) {
- return ((StorageType)value << position) & mask;
+ [[nodiscard]] static constexpr StorageType FormatValue(const T& value) {
+ return (static_cast<StorageType>(value) << position) & mask;
}
/**
@@ -151,7 +144,7 @@ public:
* (such as Value() or operator T), but this can be used to extract a value from a bitfield
* union in a constexpr context.
*/
- static constexpr FORCE_INLINE T ExtractValue(const StorageType& storage) {
+ [[nodiscard]] static constexpr T ExtractValue(const StorageType& storage) {
if constexpr (std::numeric_limits<UnderlyingType>::is_signed) {
std::size_t shift = 8 * sizeof(T) - bits;
return static_cast<T>(static_cast<UnderlyingType>(storage << (shift - position)) >>
@@ -175,19 +168,19 @@ public:
constexpr BitField(BitField&&) noexcept = default;
constexpr BitField& operator=(BitField&&) noexcept = default;
- constexpr operator T() const {
+ [[nodiscard]] constexpr operator T() const {
return Value();
}
constexpr void Assign(const T& value) {
- storage = (static_cast<StorageType>(storage) & ~mask) | FormatValue(value);
+ storage = static_cast<StorageType>((storage & ~mask) | FormatValue(value));
}
- constexpr T Value() const {
+ [[nodiscard]] constexpr T Value() const {
return ExtractValue(storage);
}
- constexpr explicit operator bool() const {
+ [[nodiscard]] constexpr explicit operator bool() const {
return Value() != 0;
}
diff --git a/src/common/bit_util.h b/src/common/bit_util.h
index 6f7d5a9479..29f59a9a3e 100644
--- a/src/common/bit_util.h
+++ b/src/common/bit_util.h
@@ -17,12 +17,12 @@ namespace Common {
/// Gets the size of a specified type T in bits.
template <typename T>
-constexpr std::size_t BitSize() {
+[[nodiscard]] constexpr std::size_t BitSize() {
return sizeof(T) * CHAR_BIT;
}
#ifdef _MSC_VER
-inline u32 CountLeadingZeroes32(u32 value) {
+[[nodiscard]] inline u32 CountLeadingZeroes32(u32 value) {
unsigned long leading_zero = 0;
if (_BitScanReverse(&leading_zero, value) != 0) {
@@ -32,7 +32,7 @@ inline u32 CountLeadingZeroes32(u32 value) {
return 32;
}
-inline u32 CountLeadingZeroes64(u64 value) {
+[[nodiscard]] inline u32 CountLeadingZeroes64(u64 value) {
unsigned long leading_zero = 0;
if (_BitScanReverse64(&leading_zero, value) != 0) {
@@ -42,7 +42,7 @@ inline u32 CountLeadingZeroes64(u64 value) {
return 64;
}
#else
-inline u32 CountLeadingZeroes32(u32 value) {
+[[nodiscard]] inline u32 CountLeadingZeroes32(u32 value) {
if (value == 0) {
return 32;
}
@@ -50,7 +50,7 @@ inline u32 CountLeadingZeroes32(u32 value) {
return static_cast<u32>(__builtin_clz(value));
}
-inline u32 CountLeadingZeroes64(u64 value) {
+[[nodiscard]] inline u32 CountLeadingZeroes64(u64 value) {
if (value == 0) {
return 64;
}
@@ -60,7 +60,7 @@ inline u32 CountLeadingZeroes64(u64 value) {
#endif
#ifdef _MSC_VER
-inline u32 CountTrailingZeroes32(u32 value) {
+[[nodiscard]] inline u32 CountTrailingZeroes32(u32 value) {
unsigned long trailing_zero = 0;
if (_BitScanForward(&trailing_zero, value) != 0) {
@@ -70,7 +70,7 @@ inline u32 CountTrailingZeroes32(u32 value) {
return 32;
}
-inline u32 CountTrailingZeroes64(u64 value) {
+[[nodiscard]] inline u32 CountTrailingZeroes64(u64 value) {
unsigned long trailing_zero = 0;
if (_BitScanForward64(&trailing_zero, value) != 0) {
@@ -80,7 +80,7 @@ inline u32 CountTrailingZeroes64(u64 value) {
return 64;
}
#else
-inline u32 CountTrailingZeroes32(u32 value) {
+[[nodiscard]] inline u32 CountTrailingZeroes32(u32 value) {
if (value == 0) {
return 32;
}
@@ -88,7 +88,7 @@ inline u32 CountTrailingZeroes32(u32 value) {
return static_cast<u32>(__builtin_ctz(value));
}
-inline u32 CountTrailingZeroes64(u64 value) {
+[[nodiscard]] inline u32 CountTrailingZeroes64(u64 value) {
if (value == 0) {
return 64;
}
@@ -99,13 +99,13 @@ inline u32 CountTrailingZeroes64(u64 value) {
#ifdef _MSC_VER
-inline u32 MostSignificantBit32(const u32 value) {
+[[nodiscard]] inline u32 MostSignificantBit32(const u32 value) {
unsigned long result;
_BitScanReverse(&result, value);
return static_cast<u32>(result);
}
-inline u32 MostSignificantBit64(const u64 value) {
+[[nodiscard]] inline u32 MostSignificantBit64(const u64 value) {
unsigned long result;
_BitScanReverse64(&result, value);
return static_cast<u32>(result);
@@ -113,30 +113,30 @@ inline u32 MostSignificantBit64(const u64 value) {
#else
-inline u32 MostSignificantBit32(const u32 value) {
+[[nodiscard]] inline u32 MostSignificantBit32(const u32 value) {
return 31U - static_cast<u32>(__builtin_clz(value));
}
-inline u32 MostSignificantBit64(const u64 value) {
+[[nodiscard]] inline u32 MostSignificantBit64(const u64 value) {
return 63U - static_cast<u32>(__builtin_clzll(value));
}
#endif
-inline u32 Log2Floor32(const u32 value) {
+[[nodiscard]] inline u32 Log2Floor32(const u32 value) {
return MostSignificantBit32(value);
}
-inline u32 Log2Ceil32(const u32 value) {
+[[nodiscard]] inline u32 Log2Ceil32(const u32 value) {
const u32 log2_f = Log2Floor32(value);
return log2_f + ((value ^ (1U << log2_f)) != 0U);
}
-inline u32 Log2Floor64(const u64 value) {
+[[nodiscard]] inline u32 Log2Floor64(const u64 value) {
return MostSignificantBit64(value);
}
-inline u32 Log2Ceil64(const u64 value) {
+[[nodiscard]] inline u32 Log2Ceil64(const u64 value) {
const u64 log2_f = static_cast<u64>(Log2Floor64(value));
return static_cast<u32>(log2_f + ((value ^ (1ULL << log2_f)) != 0ULL));
}
diff --git a/src/common/cityhash.h b/src/common/cityhash.h
index 4b94f8e181..a00804e01e 100644
--- a/src/common/cityhash.h
+++ b/src/common/cityhash.h
@@ -61,42 +61,43 @@
#pragma once
+#include <cstddef>
+#include <cstdint>
#include <utility>
-#include <stdint.h>
-#include <stdlib.h> // for std::size_t.
namespace Common {
-typedef std::pair<uint64_t, uint64_t> uint128;
+using uint128 = std::pair<uint64_t, uint64_t>;
-inline uint64_t Uint128Low64(const uint128& x) {
+[[nodiscard]] inline uint64_t Uint128Low64(const uint128& x) {
return x.first;
}
-inline uint64_t Uint128High64(const uint128& x) {
+[[nodiscard]] inline uint64_t Uint128High64(const uint128& x) {
return x.second;
}
// Hash function for a byte array.
-uint64_t CityHash64(const char* buf, std::size_t len);
+[[nodiscard]] uint64_t CityHash64(const char* buf, std::size_t len);
// Hash function for a byte array. For convenience, a 64-bit seed is also
// hashed into the result.
-uint64_t CityHash64WithSeed(const char* buf, std::size_t len, uint64_t seed);
+[[nodiscard]] uint64_t CityHash64WithSeed(const char* buf, std::size_t len, uint64_t seed);
// Hash function for a byte array. For convenience, two seeds are also
// hashed into the result.
-uint64_t CityHash64WithSeeds(const char* buf, std::size_t len, uint64_t seed0, uint64_t seed1);
+[[nodiscard]] uint64_t CityHash64WithSeeds(const char* buf, std::size_t len, uint64_t seed0,
+ uint64_t seed1);
// Hash function for a byte array.
-uint128 CityHash128(const char* s, std::size_t len);
+[[nodiscard]] uint128 CityHash128(const char* s, std::size_t len);
// Hash function for a byte array. For convenience, a 128-bit seed is also
// hashed into the result.
-uint128 CityHash128WithSeed(const char* s, std::size_t len, uint128 seed);
+[[nodiscard]] uint128 CityHash128WithSeed(const char* s, std::size_t len, uint128 seed);
// Hash 128 input bits down to 64 bits of output.
// This is intended to be a reasonably good hash function.
-inline uint64_t Hash128to64(const uint128& x) {
+[[nodiscard]] inline uint64_t Hash128to64(const uint128& x) {
// Murmur-inspired hashing.
const uint64_t kMul = 0x9ddfea08eb382d69ULL;
uint64_t a = (Uint128Low64(x) ^ Uint128High64(x)) * kMul;
diff --git a/src/common/color.h b/src/common/color.h
index 3a2222077e..bbcac858e4 100644
--- a/src/common/color.h
+++ b/src/common/color.h
@@ -10,45 +10,45 @@
#include "common/swap.h"
#include "common/vector_math.h"
-namespace Color {
+namespace Common::Color {
/// Convert a 1-bit color component to 8 bit
-constexpr u8 Convert1To8(u8 value) {
+[[nodiscard]] constexpr u8 Convert1To8(u8 value) {
return value * 255;
}
/// Convert a 4-bit color component to 8 bit
-constexpr u8 Convert4To8(u8 value) {
+[[nodiscard]] constexpr u8 Convert4To8(u8 value) {
return (value << 4) | value;
}
/// Convert a 5-bit color component to 8 bit
-constexpr u8 Convert5To8(u8 value) {
+[[nodiscard]] constexpr u8 Convert5To8(u8 value) {
return (value << 3) | (value >> 2);
}
/// Convert a 6-bit color component to 8 bit
-constexpr u8 Convert6To8(u8 value) {
+[[nodiscard]] constexpr u8 Convert6To8(u8 value) {
return (value << 2) | (value >> 4);
}
/// Convert a 8-bit color component to 1 bit
-constexpr u8 Convert8To1(u8 value) {
+[[nodiscard]] constexpr u8 Convert8To1(u8 value) {
return value >> 7;
}
/// Convert a 8-bit color component to 4 bit
-constexpr u8 Convert8To4(u8 value) {
+[[nodiscard]] constexpr u8 Convert8To4(u8 value) {
return value >> 4;
}
/// Convert a 8-bit color component to 5 bit
-constexpr u8 Convert8To5(u8 value) {
+[[nodiscard]] constexpr u8 Convert8To5(u8 value) {
return value >> 3;
}
/// Convert a 8-bit color component to 6 bit
-constexpr u8 Convert8To6(u8 value) {
+[[nodiscard]] constexpr u8 Convert8To6(u8 value) {
return value >> 2;
}
@@ -57,7 +57,7 @@ constexpr u8 Convert8To6(u8 value) {
* @param bytes Pointer to encoded source color
* @return Result color decoded as Common::Vec4<u8>
*/
-inline Common::Vec4<u8> DecodeRGBA8(const u8* bytes) {
+[[nodiscard]] inline Common::Vec4<u8> DecodeRGBA8(const u8* bytes) {
return {bytes[3], bytes[2], bytes[1], bytes[0]};
}
@@ -66,7 +66,7 @@ inline Common::Vec4<u8> DecodeRGBA8(const u8* bytes) {
* @param bytes Pointer to encoded source color
* @return Result color decoded as Common::Vec4<u8>
*/
-inline Common::Vec4<u8> DecodeRGB8(const u8* bytes) {
+[[nodiscard]] inline Common::Vec4<u8> DecodeRGB8(const u8* bytes) {
return {bytes[2], bytes[1], bytes[0], 255};
}
@@ -75,7 +75,7 @@ inline Common::Vec4<u8> DecodeRGB8(const u8* bytes) {
* @param bytes Pointer to encoded source color
* @return Result color decoded as Common::Vec4<u8>
*/
-inline Common::Vec4<u8> DecodeRG8(const u8* bytes) {
+[[nodiscard]] inline Common::Vec4<u8> DecodeRG8(const u8* bytes) {
return {bytes[1], bytes[0], 0, 255};
}
@@ -84,7 +84,7 @@ inline Common::Vec4<u8> DecodeRG8(const u8* bytes) {
* @param bytes Pointer to encoded source color
* @return Result color decoded as Common::Vec4<u8>
*/
-inline Common::Vec4<u8> DecodeRGB565(const u8* bytes) {
+[[nodiscard]] inline Common::Vec4<u8> DecodeRGB565(const u8* bytes) {
u16_le pixel;
std::memcpy(&pixel, bytes, sizeof(pixel));
return {Convert5To8((pixel >> 11) & 0x1F), Convert6To8((pixel >> 5) & 0x3F),
@@ -96,7 +96,7 @@ inline Common::Vec4<u8> DecodeRGB565(const u8* bytes) {
* @param bytes Pointer to encoded source color
* @return Result color decoded as Common::Vec4<u8>
*/
-inline Common::Vec4<u8> DecodeRGB5A1(const u8* bytes) {
+[[nodiscard]] inline Common::Vec4<u8> DecodeRGB5A1(const u8* bytes) {
u16_le pixel;
std::memcpy(&pixel, bytes, sizeof(pixel));
return {Convert5To8((pixel >> 11) & 0x1F), Convert5To8((pixel >> 6) & 0x1F),
@@ -108,7 +108,7 @@ inline Common::Vec4<u8> DecodeRGB5A1(const u8* bytes) {
* @param bytes Pointer to encoded source color
* @return Result color decoded as Common::Vec4<u8>
*/
-inline Common::Vec4<u8> DecodeRGBA4(const u8* bytes) {
+[[nodiscard]] inline Common::Vec4<u8> DecodeRGBA4(const u8* bytes) {
u16_le pixel;
std::memcpy(&pixel, bytes, sizeof(pixel));
return {Convert4To8((pixel >> 12) & 0xF), Convert4To8((pixel >> 8) & 0xF),
@@ -120,7 +120,7 @@ inline Common::Vec4<u8> DecodeRGBA4(const u8* bytes) {
* @param bytes Pointer to encoded source value
* @return Depth value as an u32
*/
-inline u32 DecodeD16(const u8* bytes) {
+[[nodiscard]] inline u32 DecodeD16(const u8* bytes) {
u16_le data;
std::memcpy(&data, bytes, sizeof(data));
return data;
@@ -131,7 +131,7 @@ inline u32 DecodeD16(const u8* bytes) {
* @param bytes Pointer to encoded source value
* @return Depth value as an u32
*/
-inline u32 DecodeD24(const u8* bytes) {
+[[nodiscard]] inline u32 DecodeD24(const u8* bytes) {
return (bytes[2] << 16) | (bytes[1] << 8) | bytes[0];
}
@@ -140,7 +140,7 @@ inline u32 DecodeD24(const u8* bytes) {
* @param bytes Pointer to encoded source values
* @return Resulting values stored as a Common::Vec2
*/
-inline Common::Vec2<u32> DecodeD24S8(const u8* bytes) {
+[[nodiscard]] inline Common::Vec2<u32> DecodeD24S8(const u8* bytes) {
return {static_cast<u32>((bytes[2] << 16) | (bytes[1] << 8) | bytes[0]), bytes[3]};
}
@@ -268,4 +268,4 @@ inline void EncodeX24S8(u8 stencil, u8* bytes) {
bytes[3] = stencil;
}
-} // namespace Color
+} // namespace Common::Color
diff --git a/src/common/common_funcs.h b/src/common/common_funcs.h
index 052254678d..367b6bf6e7 100644
--- a/src/common/common_funcs.h
+++ b/src/common/common_funcs.h
@@ -53,11 +53,49 @@ __declspec(dllimport) void __stdcall DebugBreak(void);
// Call directly after the command or use the error num.
// This function might change the error code.
// Defined in Misc.cpp.
-std::string GetLastErrorMsg();
+[[nodiscard]] std::string GetLastErrorMsg();
+
+#define DECLARE_ENUM_FLAG_OPERATORS(type) \
+ [[nodiscard]] constexpr type operator|(type a, type b) noexcept { \
+ using T = std::underlying_type_t<type>; \
+ return static_cast<type>(static_cast<T>(a) | static_cast<T>(b)); \
+ } \
+ [[nodiscard]] constexpr type operator&(type a, type b) noexcept { \
+ using T = std::underlying_type_t<type>; \
+ return static_cast<type>(static_cast<T>(a) & static_cast<T>(b)); \
+ } \
+ [[nodiscard]] constexpr type operator^(type a, type b) noexcept { \
+ using T = std::underlying_type_t<type>; \
+ return static_cast<type>(static_cast<T>(a) ^ static_cast<T>(b)); \
+ } \
+ constexpr type& operator|=(type& a, type b) noexcept { \
+ a = a | b; \
+ return a; \
+ } \
+ constexpr type& operator&=(type& a, type b) noexcept { \
+ a = a & b; \
+ return a; \
+ } \
+ constexpr type& operator^=(type& a, type b) noexcept { \
+ a = a ^ b; \
+ return a; \
+ } \
+ [[nodiscard]] constexpr type operator~(type key) noexcept { \
+ using T = std::underlying_type_t<type>; \
+ return static_cast<type>(~static_cast<T>(key)); \
+ } \
+ [[nodiscard]] constexpr bool True(type key) noexcept { \
+ using T = std::underlying_type_t<type>; \
+ return static_cast<T>(key) != 0; \
+ } \
+ [[nodiscard]] constexpr bool False(type key) noexcept { \
+ using T = std::underlying_type_t<type>; \
+ return static_cast<T>(key) == 0; \
+ }
namespace Common {
-constexpr u32 MakeMagic(char a, char b, char c, char d) {
+[[nodiscard]] constexpr u32 MakeMagic(char a, char b, char c, char d) {
return u32(a) | u32(b) << 8 | u32(c) << 16 | u32(d) << 24;
}
diff --git a/src/common/common_paths.h b/src/common/common_paths.h
index 076752d3b6..3c593d5f6c 100644
--- a/src/common/common_paths.h
+++ b/src/common/common_paths.h
@@ -35,6 +35,7 @@
#define KEYS_DIR "keys"
#define LOAD_DIR "load"
#define DUMP_DIR "dump"
+#define SCREENSHOTS_DIR "screenshots"
#define SHADER_DIR "shader"
#define LOG_DIR "log"
diff --git a/src/common/concepts.h b/src/common/concepts.h
new file mode 100644
index 0000000000..5bef3ad676
--- /dev/null
+++ b/src/common/concepts.h
@@ -0,0 +1,34 @@
+// Copyright 2020 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <type_traits>
+
+namespace Common {
+
+// Check if type is like an STL container
+template <typename T>
+concept IsSTLContainer = requires(T t) {
+ typename T::value_type;
+ typename T::iterator;
+ typename T::const_iterator;
+ // TODO(ogniK): Replace below is std::same_as<void> when MSVC supports it.
+ t.begin();
+ t.end();
+ t.cbegin();
+ t.cend();
+ t.data();
+ t.size();
+};
+
+// TODO: Replace with std::derived_from when the <concepts> header
+// is available on all supported platforms.
+template <typename Derived, typename Base>
+concept DerivedFrom = requires {
+ std::is_base_of_v<Base, Derived>;
+ std::is_convertible_v<const volatile Derived*, const volatile Base*>;
+};
+
+} // namespace Common
diff --git a/src/common/detached_tasks.cpp b/src/common/detached_tasks.cpp
index f268d60216..f2b4939dfb 100644
--- a/src/common/detached_tasks.cpp
+++ b/src/common/detached_tasks.cpp
@@ -34,8 +34,7 @@ void DetachedTasks::AddTask(std::function<void()> task) {
std::unique_lock lock{instance->mutex};
--instance->count;
std::notify_all_at_thread_exit(instance->cv, std::move(lock));
- })
- .detach();
+ }).detach();
}
} // namespace Common
diff --git a/src/common/dynamic_library.cpp b/src/common/dynamic_library.cpp
index 7ab54e9e44..7f0a105217 100644
--- a/src/common/dynamic_library.cpp
+++ b/src/common/dynamic_library.cpp
@@ -21,7 +21,7 @@ namespace Common {
DynamicLibrary::DynamicLibrary() = default;
DynamicLibrary::DynamicLibrary(const char* filename) {
- Open(filename);
+ void(Open(filename));
}
DynamicLibrary::DynamicLibrary(DynamicLibrary&& rhs) noexcept
diff --git a/src/common/dynamic_library.h b/src/common/dynamic_library.h
index 2a06372fd8..3512da9402 100644
--- a/src/common/dynamic_library.h
+++ b/src/common/dynamic_library.h
@@ -33,7 +33,7 @@ public:
~DynamicLibrary();
/// Returns the specified library name with the platform-specific suffix added.
- static std::string GetUnprefixedFilename(const char* filename);
+ [[nodiscard]] static std::string GetUnprefixedFilename(const char* filename);
/// Returns the specified library name in platform-specific format.
/// Major/minor versions will not be included if set to -1.
@@ -41,28 +41,29 @@ public:
/// Windows: LIBNAME-MAJOR-MINOR.dll
/// Linux: libLIBNAME.so.MAJOR.MINOR
/// Mac: libLIBNAME.MAJOR.MINOR.dylib
- static std::string GetVersionedFilename(const char* libname, int major = -1, int minor = -1);
+ [[nodiscard]] static std::string GetVersionedFilename(const char* libname, int major = -1,
+ int minor = -1);
/// Returns true if a module is loaded, otherwise false.
- bool IsOpen() const {
+ [[nodiscard]] bool IsOpen() const {
return handle != nullptr;
}
/// Loads (or replaces) the handle with the specified library file name.
/// Returns true if the library was loaded and can be used.
- bool Open(const char* filename);
+ [[nodiscard]] bool Open(const char* filename);
/// Unloads the library, any function pointers from this library are no longer valid.
void Close();
/// Returns the address of the specified symbol (function or variable) as an untyped pointer.
/// If the specified symbol does not exist in this library, nullptr is returned.
- void* GetSymbolAddress(const char* name) const;
+ [[nodiscard]] void* GetSymbolAddress(const char* name) const;
/// Obtains the address of the specified symbol, automatically casting to the correct type.
/// Returns true if the symbol was found and assigned, otherwise false.
template <typename T>
- bool GetSymbol(const char* name, T* ptr) const {
+ [[nodiscard]] bool GetSymbol(const char* name, T* ptr) const {
*ptr = reinterpret_cast<T>(GetSymbolAddress(name));
return *ptr != nullptr;
}
diff --git a/src/common/fiber.cpp b/src/common/fiber.cpp
new file mode 100644
index 0000000000..3e3029cd10
--- /dev/null
+++ b/src/common/fiber.cpp
@@ -0,0 +1,233 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/assert.h"
+#include "common/fiber.h"
+#include "common/spin_lock.h"
+
+#if defined(_WIN32) || defined(WIN32)
+#include <windows.h>
+#else
+#include <boost/context/detail/fcontext.hpp>
+#endif
+
+namespace Common {
+
+constexpr std::size_t default_stack_size = 256 * 1024; // 256kb
+
+struct Fiber::FiberImpl {
+ SpinLock guard{};
+ std::function<void(void*)> entry_point;
+ std::function<void(void*)> rewind_point;
+ void* rewind_parameter{};
+ void* start_parameter{};
+ std::shared_ptr<Fiber> previous_fiber;
+ bool is_thread_fiber{};
+ bool released{};
+
+#if defined(_WIN32) || defined(WIN32)
+ LPVOID handle = nullptr;
+ LPVOID rewind_handle = nullptr;
+#else
+ alignas(64) std::array<u8, default_stack_size> stack;
+ alignas(64) std::array<u8, default_stack_size> rewind_stack;
+ u8* stack_limit;
+ u8* rewind_stack_limit;
+ boost::context::detail::fcontext_t context;
+ boost::context::detail::fcontext_t rewind_context;
+#endif
+};
+
+void Fiber::SetStartParameter(void* new_parameter) {
+ impl->start_parameter = new_parameter;
+}
+
+void Fiber::SetRewindPoint(std::function<void(void*)>&& rewind_func, void* rewind_param) {
+ impl->rewind_point = std::move(rewind_func);
+ impl->rewind_parameter = rewind_param;
+}
+
+#if defined(_WIN32) || defined(WIN32)
+
+void Fiber::Start() {
+ ASSERT(impl->previous_fiber != nullptr);
+ impl->previous_fiber->impl->guard.unlock();
+ impl->previous_fiber.reset();
+ impl->entry_point(impl->start_parameter);
+ UNREACHABLE();
+}
+
+void Fiber::OnRewind() {
+ ASSERT(impl->handle != nullptr);
+ DeleteFiber(impl->handle);
+ impl->handle = impl->rewind_handle;
+ impl->rewind_handle = nullptr;
+ impl->rewind_point(impl->rewind_parameter);
+ UNREACHABLE();
+}
+
+void Fiber::FiberStartFunc(void* fiber_parameter) {
+ auto* fiber = static_cast<Fiber*>(fiber_parameter);
+ fiber->Start();
+}
+
+void Fiber::RewindStartFunc(void* fiber_parameter) {
+ auto* fiber = static_cast<Fiber*>(fiber_parameter);
+ fiber->OnRewind();
+}
+
+Fiber::Fiber(std::function<void(void*)>&& entry_point_func, void* start_parameter)
+ : impl{std::make_unique<FiberImpl>()} {
+ impl->entry_point = std::move(entry_point_func);
+ impl->start_parameter = start_parameter;
+ impl->handle = CreateFiber(default_stack_size, &FiberStartFunc, this);
+}
+
+Fiber::Fiber() : impl{std::make_unique<FiberImpl>()} {}
+
+Fiber::~Fiber() {
+ if (impl->released) {
+ return;
+ }
+ // Make sure the Fiber is not being used
+ const bool locked = impl->guard.try_lock();
+ ASSERT_MSG(locked, "Destroying a fiber that's still running");
+ if (locked) {
+ impl->guard.unlock();
+ }
+ DeleteFiber(impl->handle);
+}
+
+void Fiber::Exit() {
+ ASSERT_MSG(impl->is_thread_fiber, "Exitting non main thread fiber");
+ if (!impl->is_thread_fiber) {
+ return;
+ }
+ ConvertFiberToThread();
+ impl->guard.unlock();
+ impl->released = true;
+}
+
+void Fiber::Rewind() {
+ ASSERT(impl->rewind_point);
+ ASSERT(impl->rewind_handle == nullptr);
+ impl->rewind_handle = CreateFiber(default_stack_size, &RewindStartFunc, this);
+ SwitchToFiber(impl->rewind_handle);
+}
+
+void Fiber::YieldTo(std::shared_ptr<Fiber> from, std::shared_ptr<Fiber> to) {
+ ASSERT_MSG(from != nullptr, "Yielding fiber is null!");
+ ASSERT_MSG(to != nullptr, "Next fiber is null!");
+ to->impl->guard.lock();
+ to->impl->previous_fiber = from;
+ SwitchToFiber(to->impl->handle);
+ ASSERT(from->impl->previous_fiber != nullptr);
+ from->impl->previous_fiber->impl->guard.unlock();
+ from->impl->previous_fiber.reset();
+}
+
+std::shared_ptr<Fiber> Fiber::ThreadToFiber() {
+ std::shared_ptr<Fiber> fiber = std::shared_ptr<Fiber>{new Fiber()};
+ fiber->impl->guard.lock();
+ fiber->impl->handle = ConvertThreadToFiber(nullptr);
+ fiber->impl->is_thread_fiber = true;
+ return fiber;
+}
+
+#else
+
+void Fiber::Start(boost::context::detail::transfer_t& transfer) {
+ ASSERT(impl->previous_fiber != nullptr);
+ impl->previous_fiber->impl->context = transfer.fctx;
+ impl->previous_fiber->impl->guard.unlock();
+ impl->previous_fiber.reset();
+ impl->entry_point(impl->start_parameter);
+ UNREACHABLE();
+}
+
+void Fiber::OnRewind([[maybe_unused]] boost::context::detail::transfer_t& transfer) {
+ ASSERT(impl->context != nullptr);
+ impl->context = impl->rewind_context;
+ impl->rewind_context = nullptr;
+ u8* tmp = impl->stack_limit;
+ impl->stack_limit = impl->rewind_stack_limit;
+ impl->rewind_stack_limit = tmp;
+ impl->rewind_point(impl->rewind_parameter);
+ UNREACHABLE();
+}
+
+void Fiber::FiberStartFunc(boost::context::detail::transfer_t transfer) {
+ auto* fiber = static_cast<Fiber*>(transfer.data);
+ fiber->Start(transfer);
+}
+
+void Fiber::RewindStartFunc(boost::context::detail::transfer_t transfer) {
+ auto* fiber = static_cast<Fiber*>(transfer.data);
+ fiber->OnRewind(transfer);
+}
+
+Fiber::Fiber(std::function<void(void*)>&& entry_point_func, void* start_parameter)
+ : impl{std::make_unique<FiberImpl>()} {
+ impl->entry_point = std::move(entry_point_func);
+ impl->start_parameter = start_parameter;
+ impl->stack_limit = impl->stack.data();
+ impl->rewind_stack_limit = impl->rewind_stack.data();
+ u8* stack_base = impl->stack_limit + default_stack_size;
+ impl->context =
+ boost::context::detail::make_fcontext(stack_base, impl->stack.size(), FiberStartFunc);
+}
+
+Fiber::Fiber() : impl{std::make_unique<FiberImpl>()} {}
+
+Fiber::~Fiber() {
+ if (impl->released) {
+ return;
+ }
+ // Make sure the Fiber is not being used
+ const bool locked = impl->guard.try_lock();
+ ASSERT_MSG(locked, "Destroying a fiber that's still running");
+ if (locked) {
+ impl->guard.unlock();
+ }
+}
+
+void Fiber::Exit() {
+ ASSERT_MSG(impl->is_thread_fiber, "Exitting non main thread fiber");
+ if (!impl->is_thread_fiber) {
+ return;
+ }
+ impl->guard.unlock();
+ impl->released = true;
+}
+
+void Fiber::Rewind() {
+ ASSERT(impl->rewind_point);
+ ASSERT(impl->rewind_context == nullptr);
+ u8* stack_base = impl->rewind_stack_limit + default_stack_size;
+ impl->rewind_context =
+ boost::context::detail::make_fcontext(stack_base, impl->stack.size(), RewindStartFunc);
+ boost::context::detail::jump_fcontext(impl->rewind_context, this);
+}
+
+void Fiber::YieldTo(std::shared_ptr<Fiber> from, std::shared_ptr<Fiber> to) {
+ ASSERT_MSG(from != nullptr, "Yielding fiber is null!");
+ ASSERT_MSG(to != nullptr, "Next fiber is null!");
+ to->impl->guard.lock();
+ to->impl->previous_fiber = from;
+ auto transfer = boost::context::detail::jump_fcontext(to->impl->context, to.get());
+ ASSERT(from->impl->previous_fiber != nullptr);
+ from->impl->previous_fiber->impl->context = transfer.fctx;
+ from->impl->previous_fiber->impl->guard.unlock();
+ from->impl->previous_fiber.reset();
+}
+
+std::shared_ptr<Fiber> Fiber::ThreadToFiber() {
+ std::shared_ptr<Fiber> fiber = std::shared_ptr<Fiber>{new Fiber()};
+ fiber->impl->guard.lock();
+ fiber->impl->is_thread_fiber = true;
+ return fiber;
+}
+
+#endif
+} // namespace Common
diff --git a/src/common/fiber.h b/src/common/fiber.h
new file mode 100644
index 0000000000..5323e85793
--- /dev/null
+++ b/src/common/fiber.h
@@ -0,0 +1,78 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <functional>
+#include <memory>
+
+#if !defined(_WIN32) && !defined(WIN32)
+namespace boost::context::detail {
+struct transfer_t;
+}
+#endif
+
+namespace Common {
+
+/**
+ * Fiber class
+ * a fiber is a userspace thread with it's own context. They can be used to
+ * implement coroutines, emulated threading systems and certain asynchronous
+ * patterns.
+ *
+ * This class implements fibers at a low level, thus allowing greater freedom
+ * to implement such patterns. This fiber class is 'threadsafe' only one fiber
+ * can be running at a time and threads will be locked while trying to yield to
+ * a running fiber until it yields. WARNING exchanging two running fibers between
+ * threads will cause a deadlock. In order to prevent a deadlock, each thread should
+ * have an intermediary fiber, you switch to the intermediary fiber of the current
+ * thread and then from it switch to the expected fiber. This way you can exchange
+ * 2 fibers within 2 different threads.
+ */
+class Fiber {
+public:
+ Fiber(std::function<void(void*)>&& entry_point_func, void* start_parameter);
+ ~Fiber();
+
+ Fiber(const Fiber&) = delete;
+ Fiber& operator=(const Fiber&) = delete;
+
+ Fiber(Fiber&&) = default;
+ Fiber& operator=(Fiber&&) = default;
+
+ /// Yields control from Fiber 'from' to Fiber 'to'
+ /// Fiber 'from' must be the currently running fiber.
+ static void YieldTo(std::shared_ptr<Fiber> from, std::shared_ptr<Fiber> to);
+ [[nodiscard]] static std::shared_ptr<Fiber> ThreadToFiber();
+
+ void SetRewindPoint(std::function<void(void*)>&& rewind_func, void* rewind_param);
+
+ void Rewind();
+
+ /// Only call from main thread's fiber
+ void Exit();
+
+ /// Changes the start parameter of the fiber. Has no effect if the fiber already started
+ void SetStartParameter(void* new_parameter);
+
+private:
+ Fiber();
+
+#if defined(_WIN32) || defined(WIN32)
+ void OnRewind();
+ void Start();
+ static void FiberStartFunc(void* fiber_parameter);
+ static void RewindStartFunc(void* fiber_parameter);
+#else
+ void OnRewind(boost::context::detail::transfer_t& transfer);
+ void Start(boost::context::detail::transfer_t& transfer);
+ static void FiberStartFunc(boost::context::detail::transfer_t transfer);
+ static void RewindStartFunc(boost::context::detail::transfer_t transfer);
+#endif
+
+ struct FiberImpl;
+ std::unique_ptr<FiberImpl> impl;
+};
+
+} // namespace Common
diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp
index 35eee0096c..18fbfa25bf 100644
--- a/src/common/file_util.cpp
+++ b/src/common/file_util.cpp
@@ -74,7 +74,7 @@
// This namespace has various generic functions related to files and paths.
// The code still needs a ton of cleanup.
// REMEMBER: strdup considered harmful!
-namespace FileUtil {
+namespace Common::FS {
// Remove any ending forward slashes from directory paths
// Modifies argument.
@@ -196,7 +196,7 @@ bool CreateFullPath(const std::string& fullPath) {
int panicCounter = 100;
LOG_TRACE(Common_Filesystem, "path {}", fullPath);
- if (FileUtil::Exists(fullPath)) {
+ if (Exists(fullPath)) {
LOG_DEBUG(Common_Filesystem, "path exists {}", fullPath);
return true;
}
@@ -212,7 +212,7 @@ bool CreateFullPath(const std::string& fullPath) {
// Include the '/' so the first call is CreateDir("/") rather than CreateDir("")
std::string const subPath(fullPath.substr(0, position + 1));
- if (!FileUtil::IsDirectory(subPath) && !FileUtil::CreateDir(subPath)) {
+ if (!IsDirectory(subPath) && !CreateDir(subPath)) {
LOG_ERROR(Common, "CreateFullPath: directory creation failed");
return false;
}
@@ -231,7 +231,7 @@ bool DeleteDir(const std::string& filename) {
LOG_TRACE(Common_Filesystem, "directory {}", filename);
// check if a directory
- if (!FileUtil::IsDirectory(filename)) {
+ if (!IsDirectory(filename)) {
LOG_ERROR(Common_Filesystem, "Not a directory {}", filename);
return false;
}
@@ -371,7 +371,7 @@ u64 GetSize(FILE* f) {
bool CreateEmptyFile(const std::string& filename) {
LOG_TRACE(Common_Filesystem, "{}", filename);
- if (!FileUtil::IOFile(filename, "wb").IsOpen()) {
+ if (!IOFile(filename, "wb").IsOpen()) {
LOG_ERROR(Common_Filesystem, "failed {}: {}", filename, GetLastErrorMsg());
return false;
}
@@ -472,13 +472,14 @@ u64 ScanDirectoryTree(const std::string& directory, FSTEntry& parent_entry,
}
bool DeleteDirRecursively(const std::string& directory, unsigned int recursion) {
- const auto callback = [recursion](u64* num_entries_out, const std::string& directory,
- const std::string& virtual_name) -> bool {
- std::string new_path = directory + DIR_SEP_CHR + virtual_name;
+ const auto callback = [recursion](u64*, const std::string& directory,
+ const std::string& virtual_name) {
+ const std::string new_path = directory + DIR_SEP_CHR + virtual_name;
if (IsDirectory(new_path)) {
- if (recursion == 0)
+ if (recursion == 0) {
return false;
+ }
return DeleteDirRecursively(new_path, recursion - 1);
}
return Delete(new_path);
@@ -488,29 +489,35 @@ bool DeleteDirRecursively(const std::string& directory, unsigned int recursion)
return false;
// Delete the outermost directory
- FileUtil::DeleteDir(directory);
+ DeleteDir(directory);
return true;
}
-void CopyDir(const std::string& source_path, const std::string& dest_path) {
+void CopyDir([[maybe_unused]] const std::string& source_path,
+ [[maybe_unused]] const std::string& dest_path) {
#ifndef _WIN32
- if (source_path == dest_path)
+ if (source_path == dest_path) {
return;
- if (!FileUtil::Exists(source_path))
+ }
+ if (!Exists(source_path)) {
return;
- if (!FileUtil::Exists(dest_path))
- FileUtil::CreateFullPath(dest_path);
+ }
+ if (!Exists(dest_path)) {
+ CreateFullPath(dest_path);
+ }
DIR* dirp = opendir(source_path.c_str());
- if (!dirp)
+ if (!dirp) {
return;
+ }
while (struct dirent* result = readdir(dirp)) {
const std::string virtualName(result->d_name);
// check for "." and ".."
if (((virtualName[0] == '.') && (virtualName[1] == '\0')) ||
- ((virtualName[0] == '.') && (virtualName[1] == '.') && (virtualName[2] == '\0')))
+ ((virtualName[0] == '.') && (virtualName[1] == '.') && (virtualName[2] == '\0'))) {
continue;
+ }
std::string source, dest;
source = source_path + virtualName;
@@ -518,11 +525,13 @@ void CopyDir(const std::string& source_path, const std::string& dest_path) {
if (IsDirectory(source)) {
source += '/';
dest += '/';
- if (!FileUtil::Exists(dest))
- FileUtil::CreateFullPath(dest);
+ if (!Exists(dest)) {
+ CreateFullPath(dest);
+ }
CopyDir(source, dest);
- } else if (!FileUtil::Exists(dest))
- FileUtil::Copy(source, dest);
+ } else if (!Exists(dest)) {
+ Copy(source, dest);
+ }
}
closedir(dirp);
#endif
@@ -538,7 +547,7 @@ std::optional<std::string> GetCurrentDir() {
if (!dir) {
#endif
LOG_ERROR(Common_Filesystem, "GetCurrentDirectory failed: {}", GetLastErrorMsg());
- return {};
+ return std::nullopt;
}
#ifdef _WIN32
std::string strDir = Common::UTF16ToUTF8(dir);
@@ -668,7 +677,7 @@ const std::string& GetUserPath(UserPath path, const std::string& new_path) {
if (user_path.empty()) {
#ifdef _WIN32
user_path = GetExeDirectory() + DIR_SEP USERDATA_DIR DIR_SEP;
- if (!FileUtil::IsDirectory(user_path)) {
+ if (!IsDirectory(user_path)) {
user_path = AppDataRoamingDirectory() + DIR_SEP EMU_DATA_DIR DIR_SEP;
} else {
LOG_INFO(Common_Filesystem, "Using the local user directory");
@@ -677,7 +686,7 @@ const std::string& GetUserPath(UserPath path, const std::string& new_path) {
paths.emplace(UserPath::ConfigDir, user_path + CONFIG_DIR DIR_SEP);
paths.emplace(UserPath::CacheDir, user_path + CACHE_DIR DIR_SEP);
#else
- if (FileUtil::Exists(ROOT_DIR DIR_SEP USERDATA_DIR)) {
+ if (Exists(ROOT_DIR DIR_SEP USERDATA_DIR)) {
user_path = ROOT_DIR DIR_SEP USERDATA_DIR DIR_SEP;
paths.emplace(UserPath::ConfigDir, user_path + CONFIG_DIR DIR_SEP);
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) {
paths.emplace(UserPath::NANDDir, user_path + NAND_DIR DIR_SEP);
paths.emplace(UserPath::LoadDir, user_path + LOAD_DIR DIR_SEP);
paths.emplace(UserPath::DumpDir, user_path + DUMP_DIR DIR_SEP);
+ paths.emplace(UserPath::ScreenshotsDir, user_path + SCREENSHOTS_DIR DIR_SEP);
paths.emplace(UserPath::ShaderDir, user_path + SHADER_DIR DIR_SEP);
paths.emplace(UserPath::SysDataDir, user_path + SYSDATA_DIR DIR_SEP);
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) {
}
if (!new_path.empty()) {
- if (!FileUtil::IsDirectory(new_path)) {
+ if (!IsDirectory(new_path)) {
LOG_ERROR(Common_Filesystem, "Invalid path specified {}", new_path);
return paths[path];
} else {
@@ -764,21 +774,23 @@ std::size_t ReadFileToString(bool text_file, const std::string& filename, std::s
void SplitFilename83(const std::string& filename, std::array<char, 9>& short_name,
std::array<char, 4>& extension) {
- const std::string forbidden_characters = ".\"/\\[]:;=, ";
+ static constexpr std::string_view forbidden_characters = ".\"/\\[]:;=, ";
// On a FAT32 partition, 8.3 names are stored as a 11 bytes array, filled with spaces.
short_name = {{' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '\0'}};
extension = {{' ', ' ', ' ', '\0'}};
- std::string::size_type point = filename.rfind('.');
- if (point == filename.size() - 1)
+ auto point = filename.rfind('.');
+ if (point == filename.size() - 1) {
point = filename.rfind('.', point);
+ }
// Get short name.
int j = 0;
for (char letter : filename.substr(0, point)) {
- if (forbidden_characters.find(letter, 0) != std::string::npos)
+ if (forbidden_characters.find(letter, 0) != std::string::npos) {
continue;
+ }
if (j == 8) {
// TODO(Link Mauve): also do that for filenames containing a space.
// 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
short_name[7] = '1';
break;
}
- short_name[j++] = toupper(letter);
+ short_name[j++] = static_cast<char>(std::toupper(letter));
}
// Get extension.
if (point != std::string::npos) {
j = 0;
- for (char letter : filename.substr(point + 1, 3))
- extension[j++] = toupper(letter);
+ for (char letter : filename.substr(point + 1, 3)) {
+ extension[j++] = static_cast<char>(std::toupper(letter));
+ }
}
}
@@ -888,16 +901,23 @@ std::string SanitizePath(std::string_view path_, DirectorySeparator directory_se
}
std::replace(path.begin(), path.end(), type1, type2);
- path.erase(std::unique(path.begin(), path.end(),
+
+ auto start = path.begin();
+#ifdef _WIN32
+ // allow network paths which start with a double backslash (e.g. \\server\share)
+ if (start != path.end())
+ ++start;
+#endif
+ path.erase(std::unique(start, path.end(),
[type2](char c1, char c2) { return c1 == type2 && c2 == type2; }),
path.end());
return std::string(RemoveTrailingSlash(path));
}
-IOFile::IOFile() {}
+IOFile::IOFile() = default;
IOFile::IOFile(const std::string& filename, const char openmode[], int flags) {
- Open(filename, openmode, flags);
+ void(Open(filename, openmode, flags));
}
IOFile::~IOFile() {
@@ -938,17 +958,18 @@ bool IOFile::Open(const std::string& filename, const char openmode[], int flags)
}
bool IOFile::Close() {
- if (!IsOpen() || 0 != std::fclose(m_file))
+ if (!IsOpen() || 0 != std::fclose(m_file)) {
return false;
+ }
m_file = nullptr;
return true;
}
u64 IOFile::GetSize() const {
- if (IsOpen())
- return FileUtil::GetSize(m_file);
-
+ if (IsOpen()) {
+ return FS::GetSize(m_file);
+ }
return 0;
}
@@ -957,9 +978,9 @@ bool IOFile::Seek(s64 off, int origin) const {
}
u64 IOFile::Tell() const {
- if (IsOpen())
+ if (IsOpen()) {
return ftello(m_file);
-
+ }
return std::numeric_limits<u64>::max();
}
@@ -967,6 +988,34 @@ bool IOFile::Flush() {
return IsOpen() && 0 == std::fflush(m_file);
}
+std::size_t IOFile::ReadImpl(void* data, std::size_t length, std::size_t data_size) const {
+ if (!IsOpen()) {
+ return std::numeric_limits<std::size_t>::max();
+ }
+
+ if (length == 0) {
+ return 0;
+ }
+
+ DEBUG_ASSERT(data != nullptr);
+
+ return std::fread(data, data_size, length, m_file);
+}
+
+std::size_t IOFile::WriteImpl(const void* data, std::size_t length, std::size_t data_size) {
+ if (!IsOpen()) {
+ return std::numeric_limits<std::size_t>::max();
+ }
+
+ if (length == 0) {
+ return 0;
+ }
+
+ DEBUG_ASSERT(data != nullptr);
+
+ return std::fwrite(data, data_size, length, m_file);
+}
+
bool IOFile::Resize(u64 size) {
return IsOpen() && 0 ==
#ifdef _WIN32
@@ -980,4 +1029,4 @@ bool IOFile::Resize(u64 size) {
;
}
-} // namespace FileUtil
+} // namespace Common::FS
diff --git a/src/common/file_util.h b/src/common/file_util.h
index cde7ddf2d1..840cde2a68 100644
--- a/src/common/file_util.h
+++ b/src/common/file_util.h
@@ -19,7 +19,7 @@
#include "common/string_util.h"
#endif
-namespace FileUtil {
+namespace Common::FS {
// User paths for GetUserPath
enum class UserPath {
@@ -32,6 +32,7 @@ enum class UserPath {
SDMCDir,
LoadDir,
DumpDir,
+ ScreenshotsDir,
ShaderDir,
SysDataDir,
UserDir,
@@ -47,19 +48,19 @@ struct FSTEntry {
};
// Returns true if file filename exists
-bool Exists(const std::string& filename);
+[[nodiscard]] bool Exists(const std::string& filename);
// Returns true if filename is a directory
-bool IsDirectory(const std::string& filename);
+[[nodiscard]] bool IsDirectory(const std::string& filename);
// Returns the size of filename (64bit)
-u64 GetSize(const std::string& filename);
+[[nodiscard]] u64 GetSize(const std::string& filename);
// Overloaded GetSize, accepts file descriptor
-u64 GetSize(const int fd);
+[[nodiscard]] u64 GetSize(int fd);
// Overloaded GetSize, accepts FILE*
-u64 GetSize(FILE* f);
+[[nodiscard]] u64 GetSize(FILE* f);
// Returns true if successful, or path already exists.
bool CreateDir(const std::string& filename);
@@ -119,7 +120,7 @@ u64 ScanDirectoryTree(const std::string& directory, FSTEntry& parent_entry,
bool DeleteDirRecursively(const std::string& directory, unsigned int recursion = 256);
// Returns the current directory
-std::optional<std::string> GetCurrentDir();
+[[nodiscard]] std::optional<std::string> GetCurrentDir();
// Create directory and copy contents (does not overwrite existing files)
void CopyDir(const std::string& source_path, const std::string& dest_path);
@@ -131,20 +132,20 @@ bool SetCurrentDir(const std::string& directory);
// directory. To be used in "multi-user" mode (that is, installed).
const std::string& GetUserPath(UserPath path, const std::string& new_path = "");
-std::string GetHactoolConfigurationPath();
+[[nodiscard]] std::string GetHactoolConfigurationPath();
-std::string GetNANDRegistrationDir(bool system = false);
+[[nodiscard]] std::string GetNANDRegistrationDir(bool system = false);
// Returns the path to where the sys file are
-std::string GetSysDirectory();
+[[nodiscard]] std::string GetSysDirectory();
#ifdef __APPLE__
-std::string GetBundleDirectory();
+[[nodiscard]] std::string GetBundleDirectory();
#endif
#ifdef _WIN32
-const std::string& GetExeDirectory();
-std::string AppDataRoamingDirectory();
+[[nodiscard]] const std::string& GetExeDirectory();
+[[nodiscard]] std::string AppDataRoamingDirectory();
#endif
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
// Splits the path on '/' or '\' and put the components into a vector
// i.e. "C:\Users\Yuzu\Documents\save.bin" becomes {"C:", "Users", "Yuzu", "Documents", "save.bin" }
-std::vector<std::string> SplitPathComponents(std::string_view filename);
+[[nodiscard]] std::vector<std::string> SplitPathComponents(std::string_view filename);
// Gets all of the text up to the last '/' or '\' in the path.
-std::string_view GetParentPath(std::string_view path);
+[[nodiscard]] std::string_view GetParentPath(std::string_view path);
// Gets all of the text after the first '/' or '\' in the path.
-std::string_view GetPathWithoutTop(std::string_view path);
+[[nodiscard]] std::string_view GetPathWithoutTop(std::string_view path);
// Gets the filename of the path
-std::string_view GetFilename(std::string_view path);
+[[nodiscard]] std::string_view GetFilename(std::string_view path);
// Gets the extension of the filename
-std::string_view GetExtensionFromFilename(std::string_view name);
+[[nodiscard]] std::string_view GetExtensionFromFilename(std::string_view name);
// Removes the final '/' or '\' if one exists
-std::string_view RemoveTrailingSlash(std::string_view path);
+[[nodiscard]] std::string_view RemoveTrailingSlash(std::string_view path);
// Creates a new vector containing indices [first, last) from the original.
template <typename T>
-std::vector<T> SliceVector(const std::vector<T>& vector, std::size_t first, std::size_t last) {
- if (first >= last)
+[[nodiscard]] std::vector<T> SliceVector(const std::vector<T>& vector, std::size_t first,
+ std::size_t last) {
+ if (first >= last) {
return {};
+ }
last = std::min<std::size_t>(last, vector.size());
return std::vector<T>(vector.begin() + first, vector.begin() + first + last);
}
-enum class DirectorySeparator { ForwardSlash, BackwardSlash, PlatformDefault };
+enum class DirectorySeparator {
+ ForwardSlash,
+ BackwardSlash,
+ PlatformDefault,
+};
// Removes trailing slash, makes all '\\' into '/', and removes duplicate '/'. Makes '/' into '\\'
// depending if directory_separator is BackwardSlash or PlatformDefault and running on windows
-std::string SanitizePath(std::string_view path,
- DirectorySeparator directory_separator = DirectorySeparator::ForwardSlash);
+[[nodiscard]] std::string SanitizePath(
+ std::string_view path,
+ DirectorySeparator directory_separator = DirectorySeparator::ForwardSlash);
+
+// To deal with Windows being dumb at Unicode
+template <typename T>
+void OpenFStream(T& fstream, const std::string& filename, std::ios_base::openmode openmode) {
+#ifdef _MSC_VER
+ fstream.open(Common::UTF8ToUTF16W(filename), openmode);
+#else
+ fstream.open(filename, openmode);
+#endif
+}
// simple wrapper for cstdlib file functions to
// hopefully will make error checking easier
@@ -222,22 +240,15 @@ public:
static_assert(std::is_trivially_copyable_v<T>,
"Given array does not consist of trivially copyable objects");
- if (!IsOpen()) {
- return std::numeric_limits<std::size_t>::max();
- }
-
- return std::fread(data, sizeof(T), length, m_file);
+ return ReadImpl(data, length, sizeof(T));
}
template <typename T>
std::size_t WriteArray(const T* data, std::size_t length) {
static_assert(std::is_trivially_copyable_v<T>,
"Given array does not consist of trivially copyable objects");
- if (!IsOpen()) {
- return std::numeric_limits<std::size_t>::max();
- }
- return std::fwrite(data, sizeof(T), length, m_file);
+ return WriteImpl(data, length, sizeof(T));
}
template <typename T>
@@ -262,13 +273,13 @@ public:
return WriteArray(str.data(), str.length());
}
- bool IsOpen() const {
+ [[nodiscard]] bool IsOpen() const {
return nullptr != m_file;
}
bool Seek(s64 off, int origin) const;
- u64 Tell() const;
- u64 GetSize() const;
+ [[nodiscard]] u64 Tell() const;
+ [[nodiscard]] u64 GetSize() const;
bool Resize(u64 size);
bool Flush();
@@ -278,17 +289,10 @@ public:
}
private:
+ std::size_t ReadImpl(void* data, std::size_t length, std::size_t data_size) const;
+ std::size_t WriteImpl(const void* data, std::size_t length, std::size_t data_size);
+
std::FILE* m_file = nullptr;
};
-} // namespace FileUtil
-
-// To deal with Windows being dumb at unicode:
-template <typename T>
-void OpenFStream(T& fstream, const std::string& filename, std::ios_base::openmode openmode) {
-#ifdef _MSC_VER
- fstream.open(Common::UTF8ToUTF16W(filename), openmode);
-#else
- fstream.open(filename, openmode);
-#endif
-}
+} // namespace Common::FS
diff --git a/src/common/hash.h b/src/common/hash.h
index b2538f3ea0..2989307029 100644
--- a/src/common/hash.h
+++ b/src/common/hash.h
@@ -5,36 +5,11 @@
#pragma once
#include <cstddef>
-#include <cstring>
#include <utility>
#include <boost/functional/hash.hpp>
-#include "common/cityhash.h"
-#include "common/common_types.h"
namespace Common {
-/**
- * Computes a 64-bit hash over the specified block of data
- * @param data Block of data to compute hash over
- * @param len Length of data (in bytes) to compute hash over
- * @returns 64-bit hash value that was computed over the data block
- */
-static inline u64 ComputeHash64(const void* data, std::size_t len) {
- return CityHash64(static_cast<const char*>(data), len);
-}
-
-/**
- * Computes a 64-bit hash of a struct. In addition to being trivially copyable, it is also critical
- * that either the struct includes no padding, or that any padding is initialized to a known value
- * by memsetting the struct to 0 before filling it in.
- */
-template <typename T>
-static inline u64 ComputeStructHash64(const T& data) {
- static_assert(std::is_trivially_copyable_v<T>,
- "Type passed to ComputeStructHash64 must be trivially copyable");
- return ComputeHash64(&data, sizeof(data));
-}
-
struct PairHash {
template <class T1, class T2>
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 c2f6cf0f69..74f52dd11c 100644
--- a/src/common/hex_util.cpp
+++ b/src/common/hex_util.cpp
@@ -3,21 +3,9 @@
// Refer to the license.txt file included.
#include "common/hex_util.h"
-#include "common/logging/log.h"
namespace Common {
-u8 ToHexNibble(char c1) {
- if (c1 >= 65 && c1 <= 70)
- return c1 - 55;
- if (c1 >= 97 && c1 <= 102)
- return c1 - 87;
- if (c1 >= 48 && c1 <= 57)
- return c1 - 48;
- LOG_ERROR(Common, "Invalid hex digit: 0x{:02X}", c1);
- return 0;
-}
-
std::vector<u8> HexStringToVector(std::string_view str, bool little_endian) {
std::vector<u8> out(str.size() / 2);
if (little_endian) {
@@ -30,26 +18,4 @@ std::vector<u8> HexStringToVector(std::string_view str, bool little_endian) {
return out;
}
-std::array<u8, 16> operator""_array16(const char* str, std::size_t len) {
- if (len != 32) {
- LOG_ERROR(Common,
- "Attempting to parse string to array that is not of correct size (expected=32, "
- "actual={}).",
- len);
- return {};
- }
- return HexStringToArray<16>(str);
-}
-
-std::array<u8, 32> operator""_array32(const char* str, std::size_t len) {
- if (len != 64) {
- LOG_ERROR(Common,
- "Attempting to parse string to array that is not of correct size (expected=64, "
- "actual={}).",
- len);
- return {};
- }
- return HexStringToArray<32>(str);
-}
-
} // namespace Common
diff --git a/src/common/hex_util.h b/src/common/hex_util.h
index bb4736f96f..a8d414fb8e 100644
--- a/src/common/hex_util.h
+++ b/src/common/hex_util.h
@@ -14,25 +14,37 @@
namespace Common {
-u8 ToHexNibble(char c1);
+[[nodiscard]] constexpr u8 ToHexNibble(char c) {
+ if (c >= 65 && c <= 70) {
+ return static_cast<u8>(c - 55);
+ }
+
+ if (c >= 97 && c <= 102) {
+ return static_cast<u8>(c - 87);
+ }
+
+ return static_cast<u8>(c - 48);
+}
-std::vector<u8> HexStringToVector(std::string_view str, bool little_endian);
+[[nodiscard]] std::vector<u8> HexStringToVector(std::string_view str, bool little_endian);
template <std::size_t Size, bool le = false>
-std::array<u8, Size> HexStringToArray(std::string_view str) {
+[[nodiscard]] constexpr std::array<u8, Size> HexStringToArray(std::string_view str) {
std::array<u8, Size> out{};
if constexpr (le) {
- for (std::size_t i = 2 * Size - 2; i <= 2 * Size; i -= 2)
- out[i / 2] = (ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]);
+ for (std::size_t i = 2 * Size - 2; i <= 2 * Size; i -= 2) {
+ out[i / 2] = static_cast<u8>((ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]));
+ }
} else {
- for (std::size_t i = 0; i < 2 * Size; i += 2)
- out[i / 2] = (ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]);
+ for (std::size_t i = 0; i < 2 * Size; i += 2) {
+ out[i / 2] = static_cast<u8>((ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]));
+ }
}
return out;
}
template <typename ContiguousContainer>
-std::string HexToString(const ContiguousContainer& data, bool upper = true) {
+[[nodiscard]] std::string HexToString(const ContiguousContainer& data, bool upper = true) {
static_assert(std::is_same_v<typename ContiguousContainer::value_type, u8>,
"Underlying type within the contiguous container must be u8.");
@@ -48,7 +60,12 @@ std::string HexToString(const ContiguousContainer& data, bool upper = true) {
return out;
}
-std::array<u8, 0x10> operator"" _array16(const char* str, std::size_t len);
-std::array<u8, 0x20> operator"" _array32(const char* str, std::size_t len);
+[[nodiscard]] constexpr std::array<u8, 16> AsArray(const char (&data)[17]) {
+ return HexStringToArray<16>(data);
+}
+
+[[nodiscard]] constexpr std::array<u8, 32> AsArray(const char (&data)[65]) {
+ return HexStringToArray<32>(data);
+}
} // namespace Common
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp
index 04bc3128f5..631f64d052 100644
--- a/src/common/logging/backend.cpp
+++ b/src/common/logging/backend.cpp
@@ -23,6 +23,7 @@
#include "common/logging/text_formatter.h"
#include "common/string_util.h"
#include "common/threadsafe_queue.h"
+#include "core/settings.h"
namespace Log {
@@ -113,19 +114,19 @@ private:
Entry CreateEntry(Class log_class, Level log_level, const char* filename, unsigned int line_nr,
const char* function, std::string message) const {
using std::chrono::duration_cast;
+ using std::chrono::microseconds;
using std::chrono::steady_clock;
- Entry entry;
- entry.timestamp =
- duration_cast<std::chrono::microseconds>(steady_clock::now() - time_origin);
- entry.log_class = log_class;
- entry.log_level = log_level;
- entry.filename = filename;
- entry.line_num = line_nr;
- entry.function = function;
- entry.message = std::move(message);
-
- return entry;
+ return {
+ .timestamp = duration_cast<microseconds>(steady_clock::now() - time_origin),
+ .log_class = log_class,
+ .log_level = log_level,
+ .filename = filename,
+ .line_num = line_nr,
+ .function = function,
+ .message = std::move(message),
+ .final_entry = false,
+ };
}
std::mutex writing_mutex;
@@ -152,10 +153,19 @@ FileBackend::FileBackend(const std::string& filename)
void FileBackend::Write(const Entry& entry) {
// prevent logs from going over the maximum size (in case its spamming and the user doesn't
// know)
- constexpr std::size_t MAX_BYTES_WRITTEN = 50 * 1024L * 1024L;
- if (!file.IsOpen() || bytes_written > MAX_BYTES_WRITTEN) {
+ constexpr std::size_t MAX_BYTES_WRITTEN = 100 * 1024 * 1024;
+ constexpr std::size_t MAX_BYTES_WRITTEN_EXTENDED = 1024 * 1024 * 1024;
+
+ if (!file.IsOpen()) {
return;
}
+
+ if (Settings::values.extended_logging && bytes_written > MAX_BYTES_WRITTEN_EXTENDED) {
+ return;
+ } else if (!Settings::values.extended_logging && bytes_written > MAX_BYTES_WRITTEN) {
+ return;
+ }
+
bytes_written += file.WriteString(FormatLogMessage(entry).append(1, '\n'));
if (entry.log_level >= Level::Error) {
file.Flush();
@@ -222,6 +232,7 @@ void DebuggerBackend::Write(const Entry& entry) {
SUB(Service, NPNS) \
SUB(Service, NS) \
SUB(Service, NVDRV) \
+ SUB(Service, OLSC) \
SUB(Service, PCIE) \
SUB(Service, PCTL) \
SUB(Service, PCV) \
@@ -274,7 +285,6 @@ const char* GetLogClassName(Class log_class) {
case Class::Count:
break;
}
- UNREACHABLE();
return "Invalid";
}
@@ -293,7 +303,6 @@ const char* GetLevelName(Level log_level) {
break;
}
#undef LVL
- UNREACHABLE();
return "Invalid";
}
diff --git a/src/common/logging/backend.h b/src/common/logging/backend.h
index fc338c70de..da1c2f185a 100644
--- a/src/common/logging/backend.h
+++ b/src/common/logging/backend.h
@@ -21,19 +21,13 @@ class Filter;
*/
struct Entry {
std::chrono::microseconds timestamp;
- Class log_class;
- Level log_level;
- const char* filename;
- unsigned int line_num;
+ Class log_class{};
+ Level log_level{};
+ const char* filename = nullptr;
+ unsigned int line_num = 0;
std::string function;
std::string message;
bool final_entry = false;
-
- Entry() = default;
- Entry(Entry&& o) = default;
-
- Entry& operator=(Entry&& o) = default;
- Entry& operator=(const Entry& o) = default;
};
/**
@@ -100,7 +94,7 @@ public:
void Write(const Entry& entry) override;
private:
- FileUtil::IOFile file;
+ Common::FS::IOFile file;
std::size_t bytes_written;
};
diff --git a/src/common/logging/log.h b/src/common/logging/log.h
index 13a4f1e30a..8358949182 100644
--- a/src/common/logging/log.h
+++ b/src/common/logging/log.h
@@ -95,6 +95,7 @@ enum class Class : ClassType {
Service_NPNS, ///< The NPNS service
Service_NS, ///< The NS services
Service_NVDRV, ///< The NVDRV (Nvidia driver) service
+ Service_OLSC, ///< The OLSC service
Service_PCIE, ///< The PCIe service
Service_PCTL, ///< The PCTL (Parental control) service
Service_PCV, ///< The PCV service
diff --git a/src/common/lz4_compression.cpp b/src/common/lz4_compression.cpp
index ade6759bb2..25700015ab 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) {
ASSERT_MSG(source_size <= LZ4_MAX_INPUT_SIZE, "Source size exceeds LZ4 maximum input size");
const auto source_size_int = static_cast<int>(source_size);
- const int max_compressed_size = LZ4_compressBound(source_size_int);
+ const auto max_compressed_size = static_cast<std::size_t>(LZ4_compressBound(source_size_int));
std::vector<u8> compressed(max_compressed_size);
- const int compressed_size = LZ4_compress_default(reinterpret_cast<const char*>(source),
- reinterpret_cast<char*>(compressed.data()),
- source_size_int, max_compressed_size);
+ const int compressed_size = LZ4_compress_default(
+ reinterpret_cast<const char*>(source), reinterpret_cast<char*>(compressed.data()),
+ source_size_int, static_cast<int>(max_compressed_size));
if (compressed_size <= 0) {
// Compression failed
return {};
}
- compressed.resize(compressed_size);
+ compressed.resize(static_cast<std::size_t>(compressed_size));
return compressed;
}
@@ -38,19 +38,19 @@ std::vector<u8> CompressDataLZ4HC(const u8* source, std::size_t source_size,
compression_level = std::clamp(compression_level, LZ4HC_CLEVEL_MIN, LZ4HC_CLEVEL_MAX);
const auto source_size_int = static_cast<int>(source_size);
- const int max_compressed_size = LZ4_compressBound(source_size_int);
+ const auto max_compressed_size = static_cast<std::size_t>(LZ4_compressBound(source_size_int));
std::vector<u8> compressed(max_compressed_size);
const int compressed_size = LZ4_compress_HC(
reinterpret_cast<const char*>(source), reinterpret_cast<char*>(compressed.data()),
- source_size_int, max_compressed_size, compression_level);
+ source_size_int, static_cast<int>(max_compressed_size), compression_level);
if (compressed_size <= 0) {
// Compression failed
return {};
}
- compressed.resize(compressed_size);
+ compressed.resize(static_cast<std::size_t>(compressed_size));
return compressed;
}
diff --git a/src/common/lz4_compression.h b/src/common/lz4_compression.h
index 4c16f6e030..87a4be1b0e 100644
--- a/src/common/lz4_compression.h
+++ b/src/common/lz4_compression.h
@@ -13,12 +13,12 @@ namespace Common::Compression {
/**
* Compresses a source memory region with LZ4 and returns the compressed data in a vector.
*
- * @param source the uncompressed source memory region.
- * @param source_size the size in bytes of the uncompressed source memory region.
+ * @param source The uncompressed source memory region.
+ * @param source_size The size of the uncompressed source memory region.
*
* @return the compressed data.
*/
-std::vector<u8> CompressDataLZ4(const u8* source, std::size_t source_size);
+[[nodiscard]] std::vector<u8> CompressDataLZ4(const u8* source, std::size_t source_size);
/**
* 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);
* compression level has almost no impact on decompression speed. Data compressed with LZ4HC can
* also be decompressed with the default LZ4 decompression.
*
- * @param source the uncompressed source memory region.
- * @param source_size the size in bytes of the uncompressed source memory region.
- * @param compression_level the used compression level. Should be between 3 and 12.
+ * @param source The uncompressed source memory region.
+ * @param source_size The size of the uncompressed source memory region.
+ * @param compression_level The used compression level. Should be between 3 and 12.
*
* @return the compressed data.
*/
-std::vector<u8> CompressDataLZ4HC(const u8* source, std::size_t source_size, s32 compression_level);
+[[nodiscard]] std::vector<u8> CompressDataLZ4HC(const u8* source, std::size_t source_size,
+ s32 compression_level);
/**
* Utilizes the LZ4 subalgorithm LZ4HC with the highest possible compression level.
*
- * @param source the uncompressed source memory region.
- * @param source_size the size in bytes of the uncompressed source memory region.
+ * @param source The uncompressed source memory region.
+ * @param source_size The size of the uncompressed source memory region
*
* @return the compressed data.
*/
-std::vector<u8> CompressDataLZ4HCMax(const u8* source, std::size_t source_size);
+[[nodiscard]] std::vector<u8> CompressDataLZ4HCMax(const u8* source, std::size_t source_size);
/**
* Decompresses a source memory region with LZ4 and returns the uncompressed data in a vector.
@@ -52,6 +53,7 @@ std::vector<u8> CompressDataLZ4HCMax(const u8* source, std::size_t source_size);
*
* @return the decompressed data.
*/
-std::vector<u8> DecompressDataLZ4(const std::vector<u8>& compressed, std::size_t uncompressed_size);
+[[nodiscard]] std::vector<u8> DecompressDataLZ4(const std::vector<u8>& compressed,
+ std::size_t uncompressed_size);
} // namespace Common::Compression \ No newline at end of file
diff --git a/src/common/math_util.h b/src/common/math_util.h
index 83ef0201f4..4c38d80405 100644
--- a/src/common/math_util.h
+++ b/src/common/math_util.h
@@ -9,7 +9,7 @@
namespace Common {
-constexpr float PI = 3.14159265f;
+constexpr float PI = 3.1415926535f;
template <class T>
struct Rectangle {
@@ -20,40 +20,40 @@ struct Rectangle {
constexpr Rectangle() = default;
- constexpr Rectangle(T left, T top, T right, T bottom)
- : left(left), top(top), right(right), bottom(bottom) {}
+ constexpr Rectangle(T left_, T top_, T right_, T bottom_)
+ : left(left_), top(top_), right(right_), bottom(bottom_) {}
- T GetWidth() const {
+ [[nodiscard]] T GetWidth() const {
if constexpr (std::is_floating_point_v<T>) {
return std::abs(right - left);
} else {
- return std::abs(static_cast<std::make_signed_t<T>>(right - left));
+ return static_cast<T>(std::abs(static_cast<std::make_signed_t<T>>(right - left)));
}
}
- T GetHeight() const {
+ [[nodiscard]] T GetHeight() const {
if constexpr (std::is_floating_point_v<T>) {
return std::abs(bottom - top);
} else {
- return std::abs(static_cast<std::make_signed_t<T>>(bottom - top));
+ return static_cast<T>(std::abs(static_cast<std::make_signed_t<T>>(bottom - top)));
}
}
- Rectangle<T> TranslateX(const T x) const {
+ [[nodiscard]] Rectangle<T> TranslateX(const T x) const {
return Rectangle{left + x, top, right + x, bottom};
}
- Rectangle<T> TranslateY(const T y) const {
+ [[nodiscard]] Rectangle<T> TranslateY(const T y) const {
return Rectangle{left, top + y, right, bottom + y};
}
- Rectangle<T> Scale(const float s) const {
+ [[nodiscard]] Rectangle<T> Scale(const float s) const {
return Rectangle{left, top, static_cast<T>(left + GetWidth() * s),
static_cast<T>(top + GetHeight() * s)};
}
};
template <typename T>
-Rectangle(T, T, T, T)->Rectangle<T>;
+Rectangle(T, T, T, T) -> Rectangle<T>;
} // namespace Common
diff --git a/src/common/memory_detect.cpp b/src/common/memory_detect.cpp
new file mode 100644
index 0000000000..8cff6ec379
--- /dev/null
+++ b/src/common/memory_detect.cpp
@@ -0,0 +1,73 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#ifdef _WIN32
+// clang-format off
+#include <windows.h>
+#include <sysinfoapi.h>
+// clang-format on
+#else
+#include <sys/types.h>
+#if defined(__APPLE__) || defined(__FreeBSD__)
+#include <sys/sysctl.h>
+#elif defined(__linux__)
+#include <sys/sysinfo.h>
+#else
+#include <unistd.h>
+#endif
+#endif
+
+#include "common/memory_detect.h"
+
+namespace Common {
+
+// Detects the RAM and Swapfile sizes
+static MemoryInfo Detect() {
+ MemoryInfo mem_info{};
+
+#ifdef _WIN32
+ MEMORYSTATUSEX memorystatus;
+ memorystatus.dwLength = sizeof(memorystatus);
+ GlobalMemoryStatusEx(&memorystatus);
+ mem_info.TotalPhysicalMemory = memorystatus.ullTotalPhys;
+ mem_info.TotalSwapMemory = memorystatus.ullTotalPageFile - mem_info.TotalPhysicalMemory;
+#elif defined(__APPLE__)
+ u64 ramsize;
+ struct xsw_usage vmusage;
+ std::size_t sizeof_ramsize = sizeof(ramsize);
+ std::size_t sizeof_vmusage = sizeof(vmusage);
+ // hw and vm are defined in sysctl.h
+ // https://github.com/apple/darwin-xnu/blob/master/bsd/sys/sysctl.h#L471
+ // sysctlbyname(const char *, void *, size_t *, void *, size_t);
+ sysctlbyname("hw.memsize", &ramsize, &sizeof_ramsize, nullptr, 0);
+ sysctlbyname("vm.swapusage", &vmusage, &sizeof_vmusage, nullptr, 0);
+ mem_info.TotalPhysicalMemory = ramsize;
+ mem_info.TotalSwapMemory = vmusage.xsu_total;
+#elif defined(__FreeBSD__)
+ u_long physmem, swap_total;
+ std::size_t sizeof_u_long = sizeof(u_long);
+ // sysctlbyname(const char *, void *, size_t *, const void *, size_t);
+ sysctlbyname("hw.physmem", &physmem, &sizeof_u_long, nullptr, 0);
+ sysctlbyname("vm.swap_total", &swap_total, &sizeof_u_long, nullptr, 0);
+ mem_info.TotalPhysicalMemory = physmem;
+ mem_info.TotalSwapMemory = swap_total;
+#elif defined(__linux__)
+ struct sysinfo meminfo;
+ sysinfo(&meminfo);
+ mem_info.TotalPhysicalMemory = meminfo.totalram;
+ mem_info.TotalSwapMemory = meminfo.totalswap;
+#else
+ mem_info.TotalPhysicalMemory = sysconf(_SC_PHYS_PAGES) * sysconf(_SC_PAGE_SIZE);
+ mem_info.TotalSwapMemory = 0;
+#endif
+
+ return mem_info;
+}
+
+const MemoryInfo& GetMemInfo() {
+ static MemoryInfo mem_info = Detect();
+ return mem_info;
+}
+
+} // 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 0000000000..0f73751c8f
--- /dev/null
+++ b/src/common/memory_detect.h
@@ -0,0 +1,22 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "common/common_types.h"
+
+namespace Common {
+
+struct MemoryInfo {
+ u64 TotalPhysicalMemory{};
+ u64 TotalSwapMemory{};
+};
+
+/**
+ * Gets the memory info of the host system
+ * @return Reference to a MemoryInfo struct with the physical and swap memory sizes in bytes
+ */
+[[nodiscard]] const MemoryInfo& GetMemInfo();
+
+} // namespace Common \ No newline at end of file
diff --git a/src/common/misc.cpp b/src/common/misc.cpp
index 68cb86cd13..1d5393597c 100644
--- a/src/common/misc.cpp
+++ b/src/common/misc.cpp
@@ -16,16 +16,23 @@
// Call directly after the command or use the error num.
// This function might change the error code.
std::string GetLastErrorMsg() {
- static const std::size_t buff_size = 255;
+ static constexpr std::size_t buff_size = 255;
char err_str[buff_size];
#ifdef _WIN32
FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, nullptr, GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), err_str, buff_size, nullptr);
+ return std::string(err_str, buff_size);
+#elif defined(__GLIBC__) && (_GNU_SOURCE || (_POSIX_C_SOURCE < 200112L && _XOPEN_SOURCE < 600))
+ // Thread safe (GNU-specific)
+ const char* str = strerror_r(errno, err_str, buff_size);
+ return std::string(str);
#else
// Thread safe (XSI-compliant)
- strerror_r(errno, err_str, buff_size);
+ const int success = strerror_r(errno, err_str, buff_size);
+ if (success != 0) {
+ return {};
+ }
+ return std::string(err_str);
#endif
-
- return std::string(err_str, buff_size);
}
diff --git a/src/common/multi_level_queue.h b/src/common/multi_level_queue.h
index 50acfdbf2f..4b305bf409 100644
--- a/src/common/multi_level_queue.h
+++ b/src/common/multi_level_queue.h
@@ -223,15 +223,15 @@ public:
ListShiftForward(levels[priority], n);
}
- std::size_t depth() const {
+ [[nodiscard]] std::size_t depth() const {
return Depth;
}
- std::size_t size(u32 priority) const {
+ [[nodiscard]] std::size_t size(u32 priority) const {
return levels[priority].size();
}
- std::size_t size() const {
+ [[nodiscard]] std::size_t size() const {
u64 priorities = used_priorities;
std::size_t size = 0;
while (priorities != 0) {
@@ -242,64 +242,64 @@ public:
return size;
}
- bool empty() const {
+ [[nodiscard]] bool empty() const {
return used_priorities == 0;
}
- bool empty(u32 priority) const {
+ [[nodiscard]] bool empty(u32 priority) const {
return (used_priorities & (1ULL << priority)) == 0;
}
- u32 highest_priority_set(u32 max_priority = 0) const {
+ [[nodiscard]] u32 highest_priority_set(u32 max_priority = 0) const {
const u64 priorities =
max_priority == 0 ? used_priorities : (used_priorities & ~((1ULL << max_priority) - 1));
return priorities == 0 ? Depth : static_cast<u32>(CountTrailingZeroes64(priorities));
}
- u32 lowest_priority_set(u32 min_priority = Depth - 1) const {
+ [[nodiscard]] u32 lowest_priority_set(u32 min_priority = Depth - 1) const {
const u64 priorities = min_priority >= Depth - 1
? used_priorities
: (used_priorities & ((1ULL << (min_priority + 1)) - 1));
return priorities == 0 ? Depth : 63 - CountLeadingZeroes64(priorities);
}
- const_iterator cbegin(u32 max_prio = 0) const {
+ [[nodiscard]] const_iterator cbegin(u32 max_prio = 0) const {
const u32 priority = highest_priority_set(max_prio);
return priority == Depth ? cend()
: const_iterator{*this, levels[priority].cbegin(), priority};
}
- const_iterator begin(u32 max_prio = 0) const {
+ [[nodiscard]] const_iterator begin(u32 max_prio = 0) const {
return cbegin(max_prio);
}
- iterator begin(u32 max_prio = 0) {
+ [[nodiscard]] iterator begin(u32 max_prio = 0) {
const u32 priority = highest_priority_set(max_prio);
return priority == Depth ? end() : iterator{*this, levels[priority].begin(), priority};
}
- const_iterator cend(u32 min_prio = Depth - 1) const {
+ [[nodiscard]] const_iterator cend(u32 min_prio = Depth - 1) const {
return min_prio == Depth - 1 ? const_iterator{*this, Depth} : cbegin(min_prio + 1);
}
- const_iterator end(u32 min_prio = Depth - 1) const {
+ [[nodiscard]] const_iterator end(u32 min_prio = Depth - 1) const {
return cend(min_prio);
}
- iterator end(u32 min_prio = Depth - 1) {
+ [[nodiscard]] iterator end(u32 min_prio = Depth - 1) {
return min_prio == Depth - 1 ? iterator{*this, Depth} : begin(min_prio + 1);
}
- T& front(u32 max_priority = 0) {
+ [[nodiscard]] T& front(u32 max_priority = 0) {
const u32 priority = highest_priority_set(max_priority);
return levels[priority == Depth ? 0 : priority].front();
}
- const T& front(u32 max_priority = 0) const {
+ [[nodiscard]] const T& front(u32 max_priority = 0) const {
const u32 priority = highest_priority_set(max_priority);
return levels[priority == Depth ? 0 : priority].front();
}
- T back(u32 min_priority = Depth - 1) {
+ [[nodiscard]] T& back(u32 min_priority = Depth - 1) {
const u32 priority = lowest_priority_set(min_priority); // intended
return levels[priority == Depth ? 63 : priority].back();
}
- const T& back(u32 min_priority = Depth - 1) const {
+ [[nodiscard]] const T& back(u32 min_priority = Depth - 1) const {
const u32 priority = lowest_priority_set(min_priority); // intended
return levels[priority == Depth ? 63 : priority].back();
}
@@ -329,7 +329,8 @@ private:
in_list.splice(position, out_list, element);
}
- static const_list_iterator ListIterateTo(const std::list<T>& list, const T& element) {
+ [[nodiscard]] static const_list_iterator ListIterateTo(const std::list<T>& list,
+ const T& element) {
auto it = list.cbegin();
while (it != list.cend() && *it != element) {
++it;
diff --git a/src/common/page_table.cpp b/src/common/page_table.cpp
index 566b57b622..bccea08945 100644
--- a/src/common/page_table.cpp
+++ b/src/common/page_table.cpp
@@ -6,36 +6,20 @@
namespace Common {
-PageTable::PageTable(std::size_t page_size_in_bits) : page_size_in_bits{page_size_in_bits} {}
+PageTable::PageTable() = default;
-PageTable::~PageTable() = default;
-
-void PageTable::Resize(std::size_t address_space_width_in_bits) {
- const std::size_t num_page_table_entries = 1ULL
- << (address_space_width_in_bits - page_size_in_bits);
+PageTable::~PageTable() noexcept = default;
+void PageTable::Resize(std::size_t address_space_width_in_bits, std::size_t page_size_in_bits,
+ bool has_attribute) {
+ const std::size_t num_page_table_entries{1ULL
+ << (address_space_width_in_bits - page_size_in_bits)};
pointers.resize(num_page_table_entries);
- attributes.resize(num_page_table_entries);
-
- // The default is a 39-bit address space, which causes an initial 1GB allocation size. If the
- // vector size is subsequently decreased (via resize), the vector might not automatically
- // actually reallocate/resize its underlying allocation, which wastes up to ~800 MB for
- // 36-bit titles. Call shrink_to_fit to reduce capacity to what's actually in use.
-
- pointers.shrink_to_fit();
- attributes.shrink_to_fit();
-}
-
-BackingPageTable::BackingPageTable(std::size_t page_size_in_bits) : PageTable{page_size_in_bits} {}
-
-BackingPageTable::~BackingPageTable() = default;
-
-void BackingPageTable::Resize(std::size_t address_space_width_in_bits) {
- PageTable::Resize(address_space_width_in_bits);
- const std::size_t num_page_table_entries = 1ULL
- << (address_space_width_in_bits - page_size_in_bits);
backing_addr.resize(num_page_table_entries);
- backing_addr.shrink_to_fit();
+
+ if (has_attribute) {
+ attributes.resize(num_page_table_entries);
+ }
}
} // namespace Common
diff --git a/src/common/page_table.h b/src/common/page_table.h
index dbc272ab7b..9754fabf91 100644
--- a/src/common/page_table.h
+++ b/src/common/page_table.h
@@ -4,10 +4,11 @@
#pragma once
-#include <vector>
-#include <boost/icl/interval_map.hpp>
+#include <tuple>
+
#include "common/common_types.h"
#include "common/memory_hook.h"
+#include "common/virtual_buffer.h"
namespace Common {
@@ -33,11 +34,11 @@ struct SpecialRegion {
MemoryHookPointer handler;
- bool operator<(const SpecialRegion& other) const {
+ [[nodiscard]] bool operator<(const SpecialRegion& other) const {
return std::tie(type, handler) < std::tie(other.type, other.handler);
}
- bool operator==(const SpecialRegion& other) const {
+ [[nodiscard]] bool operator==(const SpecialRegion& other) const {
return std::tie(type, handler) == std::tie(other.type, other.handler);
}
};
@@ -47,49 +48,35 @@ struct SpecialRegion {
* mimics the way a real CPU page table works.
*/
struct PageTable {
- explicit PageTable(std::size_t page_size_in_bits);
- ~PageTable();
+ PageTable();
+ ~PageTable() noexcept;
+
+ PageTable(const PageTable&) = delete;
+ PageTable& operator=(const PageTable&) = delete;
+
+ PageTable(PageTable&&) noexcept = default;
+ PageTable& operator=(PageTable&&) noexcept = default;
/**
* Resizes the page table to be able to accomodate enough pages within
* a given address space.
*
* @param address_space_width_in_bits The address size width in bits.
+ * @param page_size_in_bits The page size in bits.
+ * @param has_attribute Whether or not this page has any backing attributes.
*/
- void Resize(std::size_t address_space_width_in_bits);
+ void Resize(std::size_t address_space_width_in_bits, std::size_t page_size_in_bits,
+ bool has_attribute);
/**
* Vector of memory pointers backing each page. An entry can only be non-null if the
* corresponding entry in the `attributes` vector is of type `Memory`.
*/
- std::vector<u8*> pointers;
-
- /**
- * Contains MMIO handlers that back memory regions whose entries in the `attribute` vector is
- * of type `Special`.
- */
- boost::icl::interval_map<u64, std::set<SpecialRegion>> special_regions;
-
- /**
- * Vector of fine grained page attributes. If it is set to any value other than `Memory`, then
- * the corresponding entry in `pointers` MUST be set to null.
- */
- std::vector<PageType> attributes;
-
- const std::size_t page_size_in_bits{};
-};
-
-/**
- * A more advanced Page Table with the ability to save a backing address when using it
- * depends on another MMU.
- */
-struct BackingPageTable : PageTable {
- explicit BackingPageTable(std::size_t page_size_in_bits);
- ~BackingPageTable();
+ VirtualBuffer<u8*> pointers;
- void Resize(std::size_t address_space_width_in_bits);
+ VirtualBuffer<u64> backing_addr;
- std::vector<u64> backing_addr;
+ VirtualBuffer<PageType> attributes;
};
} // namespace Common
diff --git a/src/common/param_package.h b/src/common/param_package.h
index 6a0a9b6564..c13e454790 100644
--- a/src/common/param_package.h
+++ b/src/common/param_package.h
@@ -19,19 +19,19 @@ public:
explicit ParamPackage(const std::string& serialized);
ParamPackage(std::initializer_list<DataType::value_type> list);
ParamPackage(const ParamPackage& other) = default;
- ParamPackage(ParamPackage&& other) = default;
+ ParamPackage(ParamPackage&& other) noexcept = default;
ParamPackage& operator=(const ParamPackage& other) = default;
ParamPackage& operator=(ParamPackage&& other) = default;
- std::string Serialize() const;
- std::string Get(const std::string& key, const std::string& default_value) const;
- int Get(const std::string& key, int default_value) const;
- float Get(const std::string& key, float default_value) const;
+ [[nodiscard]] std::string Serialize() const;
+ [[nodiscard]] std::string Get(const std::string& key, const std::string& default_value) const;
+ [[nodiscard]] int Get(const std::string& key, int default_value) const;
+ [[nodiscard]] float Get(const std::string& key, float default_value) const;
void Set(const std::string& key, std::string value);
void Set(const std::string& key, int value);
void Set(const std::string& key, float value);
- bool Has(const std::string& key) const;
+ [[nodiscard]] bool Has(const std::string& key) const;
void Erase(const std::string& key);
void Clear();
diff --git a/src/common/quaternion.h b/src/common/quaternion.h
index 370198ae0f..4d0871eb46 100644
--- a/src/common/quaternion.h
+++ b/src/common/quaternion.h
@@ -14,35 +14,66 @@ public:
Vec3<T> xyz;
T w{};
- Quaternion<decltype(-T{})> Inverse() const {
+ [[nodiscard]] Quaternion<decltype(-T{})> Inverse() const {
return {-xyz, w};
}
- Quaternion<decltype(T{} + T{})> operator+(const Quaternion& other) const {
+ [[nodiscard]] Quaternion<decltype(T{} + T{})> operator+(const Quaternion& other) const {
return {xyz + other.xyz, w + other.w};
}
- Quaternion<decltype(T{} - T{})> operator-(const Quaternion& other) const {
+ [[nodiscard]] Quaternion<decltype(T{} - T{})> operator-(const Quaternion& other) const {
return {xyz - other.xyz, w - other.w};
}
- Quaternion<decltype(T{} * T{} - T{} * T{})> operator*(const Quaternion& other) const {
+ [[nodiscard]] Quaternion<decltype(T{} * T{} - T{} * T{})> operator*(
+ const Quaternion& other) const {
return {xyz * other.w + other.xyz * w + Cross(xyz, other.xyz),
w * other.w - Dot(xyz, other.xyz)};
}
- Quaternion<T> Normalized() const {
+ [[nodiscard]] Quaternion<T> Normalized() const {
T length = std::sqrt(xyz.Length2() + w * w);
return {xyz / length, w / length};
}
+
+ [[nodiscard]] std::array<decltype(-T{}), 16> ToMatrix() const {
+ const T x2 = xyz[0] * xyz[0];
+ const T y2 = xyz[1] * xyz[1];
+ const T z2 = xyz[2] * xyz[2];
+
+ const T xy = xyz[0] * xyz[1];
+ const T wz = w * xyz[2];
+ const T xz = xyz[0] * xyz[2];
+ const T wy = w * xyz[1];
+ const T yz = xyz[1] * xyz[2];
+ const T wx = w * xyz[0];
+
+ return {1.0f - 2.0f * (y2 + z2),
+ 2.0f * (xy + wz),
+ 2.0f * (xz - wy),
+ 0.0f,
+ 2.0f * (xy - wz),
+ 1.0f - 2.0f * (x2 + z2),
+ 2.0f * (yz + wx),
+ 0.0f,
+ 2.0f * (xz + wy),
+ 2.0f * (yz - wx),
+ 1.0f - 2.0f * (x2 + y2),
+ 0.0f,
+ 0.0f,
+ 0.0f,
+ 0.0f,
+ 1.0f};
+ }
};
template <typename T>
-auto QuaternionRotate(const Quaternion<T>& q, const Vec3<T>& v) {
+[[nodiscard]] auto QuaternionRotate(const Quaternion<T>& q, const Vec3<T>& v) {
return v + 2 * Cross(q.xyz, Cross(q.xyz, v) + v * q.w);
}
-inline Quaternion<float> MakeQuaternion(const Vec3<float>& axis, float angle) {
+[[nodiscard]] inline Quaternion<float> MakeQuaternion(const Vec3<float>& axis, float angle) {
return {axis * std::sin(angle / 2), std::cos(angle / 2)};
}
diff --git a/src/common/ring_buffer.h b/src/common/ring_buffer.h
index abe3b4dc2e..138fa01310 100644
--- a/src/common/ring_buffer.h
+++ b/src/common/ring_buffer.h
@@ -91,12 +91,12 @@ public:
}
/// @returns Number of slots used
- std::size_t Size() const {
+ [[nodiscard]] std::size_t Size() const {
return m_write_index.load() - m_read_index.load();
}
/// @returns Maximum size of ring buffer
- constexpr std::size_t Capacity() const {
+ [[nodiscard]] constexpr std::size_t Capacity() const {
return capacity;
}
diff --git a/src/common/scope_exit.h b/src/common/scope_exit.h
index 1176a72b19..68ef5f197e 100644
--- a/src/common/scope_exit.h
+++ b/src/common/scope_exit.h
@@ -12,10 +12,17 @@ template <typename Func>
struct ScopeExitHelper {
explicit ScopeExitHelper(Func&& func) : func(std::move(func)) {}
~ScopeExitHelper() {
- func();
+ if (active) {
+ func();
+ }
+ }
+
+ void Cancel() {
+ active = false;
}
Func func;
+ bool active{true};
};
template <typename Func>
diff --git a/src/common/spin_lock.cpp b/src/common/spin_lock.cpp
new file mode 100644
index 0000000000..c1524220f6
--- /dev/null
+++ b/src/common/spin_lock.cpp
@@ -0,0 +1,54 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/spin_lock.h"
+
+#if _MSC_VER
+#include <intrin.h>
+#if _M_AMD64
+#define __x86_64__ 1
+#endif
+#if _M_ARM64
+#define __aarch64__ 1
+#endif
+#else
+#if __x86_64__
+#include <xmmintrin.h>
+#endif
+#endif
+
+namespace {
+
+void ThreadPause() {
+#if __x86_64__
+ _mm_pause();
+#elif __aarch64__ && _MSC_VER
+ __yield();
+#elif __aarch64__
+ asm("yield");
+#endif
+}
+
+} // Anonymous namespace
+
+namespace Common {
+
+void SpinLock::lock() {
+ while (lck.test_and_set(std::memory_order_acquire)) {
+ ThreadPause();
+ }
+}
+
+void SpinLock::unlock() {
+ lck.clear(std::memory_order_release);
+}
+
+bool SpinLock::try_lock() {
+ if (lck.test_and_set(std::memory_order_acquire)) {
+ return false;
+ }
+ return true;
+}
+
+} // namespace Common
diff --git a/src/common/spin_lock.h b/src/common/spin_lock.h
new file mode 100644
index 0000000000..06ac2f5bbb
--- /dev/null
+++ b/src/common/spin_lock.h
@@ -0,0 +1,34 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <atomic>
+
+namespace Common {
+
+/**
+ * SpinLock class
+ * a lock similar to mutex that forces a thread to spin wait instead calling the
+ * supervisor. Should be used on short sequences of code.
+ */
+class SpinLock {
+public:
+ SpinLock() = default;
+
+ SpinLock(const SpinLock&) = delete;
+ SpinLock& operator=(const SpinLock&) = delete;
+
+ SpinLock(SpinLock&&) = delete;
+ SpinLock& operator=(SpinLock&&) = delete;
+
+ void lock();
+ void unlock();
+ [[nodiscard]] bool try_lock();
+
+private:
+ std::atomic_flag lck = ATOMIC_FLAG_INIT;
+};
+
+} // namespace Common
diff --git a/src/common/stream.cpp b/src/common/stream.cpp
new file mode 100644
index 0000000000..bf0496c265
--- /dev/null
+++ b/src/common/stream.cpp
@@ -0,0 +1,47 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <stdexcept>
+#include "common/common_types.h"
+#include "common/stream.h"
+
+namespace Common {
+
+Stream::Stream() = default;
+Stream::~Stream() = default;
+
+void Stream::Seek(s32 offset, SeekOrigin origin) {
+ if (origin == SeekOrigin::SetOrigin) {
+ if (offset < 0) {
+ position = 0;
+ } else if (position >= buffer.size()) {
+ position = buffer.size();
+ } else {
+ position = offset;
+ }
+ } else if (origin == SeekOrigin::FromCurrentPos) {
+ Seek(static_cast<s32>(position) + offset, SeekOrigin::SetOrigin);
+ } else if (origin == SeekOrigin::FromEnd) {
+ Seek(static_cast<s32>(buffer.size()) - offset, SeekOrigin::SetOrigin);
+ }
+}
+
+u8 Stream::ReadByte() {
+ if (position < buffer.size()) {
+ return buffer[position++];
+ } else {
+ throw std::out_of_range("Attempting to read a byte not within the buffer range");
+ }
+}
+
+void Stream::WriteByte(u8 byte) {
+ if (position == buffer.size()) {
+ buffer.push_back(byte);
+ position++;
+ } else {
+ buffer.insert(buffer.begin() + position, byte);
+ }
+}
+
+} // namespace Common
diff --git a/src/common/stream.h b/src/common/stream.h
new file mode 100644
index 0000000000..0e40692de1
--- /dev/null
+++ b/src/common/stream.h
@@ -0,0 +1,56 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <vector>
+#include "common/common_types.h"
+
+namespace Common {
+
+enum class SeekOrigin {
+ SetOrigin,
+ FromCurrentPos,
+ FromEnd,
+};
+
+class Stream {
+public:
+ /// Stream creates a bitstream and provides common functionality on the stream.
+ explicit Stream();
+ ~Stream();
+
+ Stream(const Stream&) = delete;
+ Stream& operator=(const Stream&) = delete;
+
+ Stream(Stream&&) = default;
+ Stream& operator=(Stream&&) = default;
+
+ /// Reposition bitstream "cursor" to the specified offset from origin
+ void Seek(s32 offset, SeekOrigin origin);
+
+ /// Reads next byte in the stream buffer and increments position
+ u8 ReadByte();
+
+ /// Writes byte at current position
+ void WriteByte(u8 byte);
+
+ [[nodiscard]] std::size_t GetPosition() const {
+ return position;
+ }
+
+ [[nodiscard]] std::vector<u8>& GetBuffer() {
+ return buffer;
+ }
+
+ [[nodiscard]] const std::vector<u8>& GetBuffer() const {
+ return buffer;
+ }
+
+private:
+ std::vector<u8> buffer;
+ std::size_t position{0};
+};
+
+} // namespace Common
diff --git a/src/common/string_util.cpp b/src/common/string_util.cpp
index 84883a1d30..4cba2aaa4e 100644
--- a/src/common/string_util.cpp
+++ b/src/common/string_util.cpp
@@ -8,6 +8,7 @@
#include <cstdlib>
#include <locale>
#include <sstream>
+
#include "common/common_paths.h"
#include "common/logging/log.h"
#include "common/string_util.h"
@@ -21,14 +22,14 @@ namespace Common {
/// Make a string lowercase
std::string ToLower(std::string str) {
std::transform(str.begin(), str.end(), str.begin(),
- [](unsigned char c) { return std::tolower(c); });
+ [](unsigned char c) { return static_cast<char>(std::tolower(c)); });
return str;
}
/// Make a string uppercase
std::string ToUpper(std::string str) {
std::transform(str.begin(), str.end(), str.begin(),
- [](unsigned char c) { return std::toupper(c); });
+ [](unsigned char c) { return static_cast<char>(std::toupper(c)); });
return str;
}
diff --git a/src/common/string_util.h b/src/common/string_util.h
index 583fd05e68..a32c07c062 100644
--- a/src/common/string_util.h
+++ b/src/common/string_util.h
@@ -12,19 +12,19 @@
namespace Common {
/// Make a string lowercase
-std::string ToLower(std::string str);
+[[nodiscard]] std::string ToLower(std::string str);
/// Make a string uppercase
-std::string ToUpper(std::string str);
+[[nodiscard]] std::string ToUpper(std::string str);
-std::string StringFromBuffer(const std::vector<u8>& data);
+[[nodiscard]] std::string StringFromBuffer(const std::vector<u8>& data);
-std::string StripSpaces(const std::string& s);
-std::string StripQuotes(const std::string& s);
+[[nodiscard]] std::string StripSpaces(const std::string& s);
+[[nodiscard]] std::string StripQuotes(const std::string& s);
-std::string StringFromBool(bool value);
+[[nodiscard]] std::string StringFromBool(bool value);
-std::string TabsToSpaces(int tab_size, std::string in);
+[[nodiscard]] std::string TabsToSpaces(int tab_size, std::string in);
void SplitString(const std::string& str, char delim, std::vector<std::string>& output);
@@ -34,14 +34,15 @@ bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _
void BuildCompleteFilename(std::string& _CompleteFilename, const std::string& _Path,
const std::string& _Filename);
-std::string ReplaceAll(std::string result, const std::string& src, const std::string& dest);
+[[nodiscard]] std::string ReplaceAll(std::string result, const std::string& src,
+ const std::string& dest);
-std::string UTF16ToUTF8(const std::u16string& input);
-std::u16string UTF8ToUTF16(const std::string& input);
+[[nodiscard]] std::string UTF16ToUTF8(const std::u16string& input);
+[[nodiscard]] std::u16string UTF8ToUTF16(const std::string& input);
#ifdef _WIN32
-std::string UTF16ToUTF8(const std::wstring& input);
-std::wstring UTF8ToUTF16W(const std::string& str);
+[[nodiscard]] std::string UTF16ToUTF8(const std::wstring& input);
+[[nodiscard]] std::wstring UTF8ToUTF16W(const std::string& str);
#endif
@@ -50,7 +51,7 @@ std::wstring UTF8ToUTF16W(const std::string& str);
* `other` for equality.
*/
template <typename InIt>
-bool ComparePartialString(InIt begin, InIt end, const char* other) {
+[[nodiscard]] bool ComparePartialString(InIt begin, InIt end, const char* other) {
for (; begin != end && *other != '\0'; ++begin, ++other) {
if (*begin != *other) {
return false;
@@ -64,26 +65,15 @@ bool ComparePartialString(InIt begin, InIt end, const char* other) {
* Creates a std::string from a fixed-size NUL-terminated char buffer. If the buffer isn't
* NUL-terminated then the string ends at max_len characters.
*/
-std::string StringFromFixedZeroTerminatedBuffer(const char* buffer, std::size_t max_len);
+[[nodiscard]] std::string StringFromFixedZeroTerminatedBuffer(const char* buffer,
+ std::size_t max_len);
/**
* Creates a UTF-16 std::u16string from a fixed-size NUL-terminated char buffer. If the buffer isn't
* null-terminated, then the string ends at the greatest multiple of two less then or equal to
* max_len_bytes.
*/
-std::u16string UTF16StringFromFixedZeroTerminatedBuffer(std::u16string_view buffer,
- std::size_t max_len);
-
-/**
- * Attempts to trim an arbitrary prefix from `path`, leaving only the part starting at `root`. It's
- * intended to be used to strip a system-specific build directory from the `__FILE__` macro,
- * leaving only the path relative to the sources root.
- *
- * @param path The input file path as a null-terminated string
- * @param root The name of the root source directory as a null-terminated string. Path up to and
- * including the last occurrence of this name will be stripped
- * @return A pointer to the same string passed as `path`, but starting at the trimmed portion
- */
-const char* TrimSourcePath(const char* path, const char* root = "src");
+[[nodiscard]] std::u16string UTF16StringFromFixedZeroTerminatedBuffer(std::u16string_view buffer,
+ std::size_t max_len);
} // namespace Common
diff --git a/src/common/swap.h b/src/common/swap.h
index 71932c2bbd..7665942a20 100644
--- a/src/common/swap.h
+++ b/src/common/swap.h
@@ -17,43 +17,14 @@
#pragma once
-#include <type_traits>
-
#if defined(_MSC_VER)
#include <cstdlib>
#endif
+#include <bit>
#include <cstring>
+#include <type_traits>
#include "common/common_types.h"
-// GCC
-#ifdef __GNUC__
-
-#if __BYTE_ORDER__ && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) && !defined(COMMON_LITTLE_ENDIAN)
-#define COMMON_LITTLE_ENDIAN 1
-#elif __BYTE_ORDER__ && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) && !defined(COMMON_BIG_ENDIAN)
-#define COMMON_BIG_ENDIAN 1
-#endif
-
-// LLVM/clang
-#elif defined(__clang__)
-
-#if __LITTLE_ENDIAN__ && !defined(COMMON_LITTLE_ENDIAN)
-#define COMMON_LITTLE_ENDIAN 1
-#elif __BIG_ENDIAN__ && !defined(COMMON_BIG_ENDIAN)
-#define COMMON_BIG_ENDIAN 1
-#endif
-
-// MSVC
-#elif defined(_MSC_VER) && !defined(COMMON_BIG_ENDIAN) && !defined(COMMON_LITTLE_ENDIAN)
-
-#define COMMON_LITTLE_ENDIAN 1
-#endif
-
-// Worst case, default to little endian.
-#if !COMMON_BIG_ENDIAN && !COMMON_LITTLE_ENDIAN
-#define COMMON_LITTLE_ENDIAN 1
-#endif
-
namespace Common {
#ifdef _MSC_VER
@@ -675,17 +646,8 @@ struct AddEndian<T, SwapTag> {
};
// Alias LETag/BETag as KeepTag/SwapTag depending on the system
-#if COMMON_LITTLE_ENDIAN
-
-using LETag = KeepTag;
-using BETag = SwapTag;
-
-#else
-
-using BETag = KeepTag;
-using LETag = SwapTag;
-
-#endif
+using LETag = std::conditional_t<std::endian::native == std::endian::little, KeepTag, SwapTag>;
+using BETag = std::conditional_t<std::endian::native == std::endian::big, KeepTag, SwapTag>;
// Aliases for LE types
using u16_le = AddEndian<u16, LETag>::type;
diff --git a/src/common/telemetry.cpp b/src/common/telemetry.cpp
index 200c6489aa..6241d08b3a 100644
--- a/src/common/telemetry.cpp
+++ b/src/common/telemetry.cpp
@@ -12,7 +12,7 @@
#include "common/x64/cpu_detect.h"
#endif
-namespace Telemetry {
+namespace Common::Telemetry {
void FieldCollection::Accept(VisitorInterface& visitor) const {
for (const auto& field : fields) {
@@ -60,6 +60,7 @@ void AppendCPUInfo(FieldCollection& fc) {
fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_AES", Common::GetCPUCaps().aes);
fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_AVX", Common::GetCPUCaps().avx);
fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_AVX2", Common::GetCPUCaps().avx2);
+ fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_AVX512", Common::GetCPUCaps().avx512);
fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_BMI1", Common::GetCPUCaps().bmi1);
fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_BMI2", Common::GetCPUCaps().bmi2);
fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_FMA", Common::GetCPUCaps().fma);
@@ -87,4 +88,4 @@ void AppendOSInfo(FieldCollection& fc) {
#endif
}
-} // namespace Telemetry
+} // namespace Common::Telemetry
diff --git a/src/common/telemetry.h b/src/common/telemetry.h
index 854a73fae7..a50c5d1dec 100644
--- a/src/common/telemetry.h
+++ b/src/common/telemetry.h
@@ -10,7 +10,7 @@
#include <string>
#include "common/common_types.h"
-namespace Telemetry {
+namespace Common::Telemetry {
/// Field type, used for grouping fields together in the final submitted telemetry log
enum class FieldType : u8 {
@@ -63,30 +63,30 @@ public:
void Accept(VisitorInterface& visitor) const override;
- const std::string& GetName() const override {
+ [[nodiscard]] const std::string& GetName() const override {
return name;
}
/**
* Returns the type of the field.
*/
- FieldType GetType() const {
+ [[nodiscard]] FieldType GetType() const {
return type;
}
/**
* Returns the value of the field.
*/
- const T& GetValue() const {
+ [[nodiscard]] const T& GetValue() const {
return value;
}
- bool operator==(const Field& other) const {
+ [[nodiscard]] bool operator==(const Field& other) const {
return (type == other.type) && (name == other.name) && (value == other.value);
}
- bool operator!=(const Field& other) const {
- return !(*this == other);
+ [[nodiscard]] bool operator!=(const Field& other) const {
+ return !operator==(other);
}
private:
@@ -196,4 +196,4 @@ void AppendCPUInfo(FieldCollection& fc);
/// such as platform name, etc.
void AppendOSInfo(FieldCollection& fc);
-} // namespace Telemetry
+} // namespace Common::Telemetry
diff --git a/src/common/thread.cpp b/src/common/thread.cpp
index 0cd2d10bff..d2c1ac60de 100644
--- a/src/common/thread.cpp
+++ b/src/common/thread.cpp
@@ -2,6 +2,8 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include "common/common_funcs.h"
+#include "common/logging/log.h"
#include "common/thread.h"
#ifdef __APPLE__
#include <mach/mach.h>
@@ -19,12 +21,60 @@
#include <unistd.h>
#endif
+#include <string>
+
#ifdef __FreeBSD__
#define cpu_set_t cpuset_t
#endif
namespace Common {
+#ifdef _WIN32
+
+void SetCurrentThreadPriority(ThreadPriority new_priority) {
+ auto handle = GetCurrentThread();
+ int windows_priority = 0;
+ switch (new_priority) {
+ case ThreadPriority::Low:
+ windows_priority = THREAD_PRIORITY_BELOW_NORMAL;
+ break;
+ case ThreadPriority::Normal:
+ windows_priority = THREAD_PRIORITY_NORMAL;
+ break;
+ case ThreadPriority::High:
+ windows_priority = THREAD_PRIORITY_ABOVE_NORMAL;
+ break;
+ case ThreadPriority::VeryHigh:
+ windows_priority = THREAD_PRIORITY_HIGHEST;
+ break;
+ default:
+ windows_priority = THREAD_PRIORITY_NORMAL;
+ break;
+ }
+ SetThreadPriority(handle, windows_priority);
+}
+
+#else
+
+void SetCurrentThreadPriority(ThreadPriority new_priority) {
+ pthread_t this_thread = pthread_self();
+
+ s32 max_prio = sched_get_priority_max(SCHED_OTHER);
+ s32 min_prio = sched_get_priority_min(SCHED_OTHER);
+ u32 level = static_cast<u32>(new_priority) + 1;
+
+ struct sched_param params;
+ if (max_prio > min_prio) {
+ params.sched_priority = min_prio + ((max_prio - min_prio) * level) / 4;
+ } else {
+ params.sched_priority = min_prio - ((min_prio - max_prio) * level) / 4;
+ }
+
+ pthread_setschedparam(this_thread, SCHED_OTHER, &params);
+}
+
+#endif
+
#ifdef _MSC_VER
// Sets the debugger-visible name of the current thread.
@@ -64,12 +114,26 @@ void SetCurrentThreadName(const char* name) {
pthread_set_name_np(pthread_self(), name);
#elif defined(__NetBSD__)
pthread_setname_np(pthread_self(), "%s", (void*)name);
+#elif defined(__linux__)
+ // Linux limits thread names to 15 characters and will outright reject any
+ // attempt to set a longer name with ERANGE.
+ std::string truncated(name, std::min(strlen(name), static_cast<size_t>(15)));
+ if (int e = pthread_setname_np(pthread_self(), truncated.c_str())) {
+ errno = e;
+ LOG_ERROR(Common, "Failed to set thread name to '{}': {}", truncated, GetLastErrorMsg());
+ }
#else
pthread_setname_np(pthread_self(), name);
#endif
}
#endif
+#if defined(_WIN32)
+void SetCurrentThreadName(const char* name) {
+ // Do Nothing on MingW
+}
+#endif
+
#endif
} // namespace Common
diff --git a/src/common/thread.h b/src/common/thread.h
index 2fc0716855..a8c17c71ab 100644
--- a/src/common/thread.h
+++ b/src/common/thread.h
@@ -4,11 +4,13 @@
#pragma once
+#include <atomic>
#include <chrono>
#include <condition_variable>
#include <cstddef>
#include <mutex>
#include <thread>
+#include "common/common_types.h"
namespace Common {
@@ -24,14 +26,13 @@ public:
void Wait() {
std::unique_lock lk{mutex};
- condvar.wait(lk, [&] { return is_set; });
+ condvar.wait(lk, [&] { return is_set.load(); });
is_set = false;
}
- template <class Duration>
- bool WaitFor(const std::chrono::duration<Duration>& time) {
+ bool WaitFor(const std::chrono::nanoseconds& time) {
std::unique_lock lk{mutex};
- if (!condvar.wait_for(lk, time, [this] { return is_set; }))
+ if (!condvar.wait_for(lk, time, [this] { return is_set.load(); }))
return false;
is_set = false;
return true;
@@ -40,7 +41,7 @@ public:
template <class Clock, class Duration>
bool WaitUntil(const std::chrono::time_point<Clock, Duration>& time) {
std::unique_lock lk{mutex};
- if (!condvar.wait_until(lk, time, [this] { return is_set; }))
+ if (!condvar.wait_until(lk, time, [this] { return is_set.load(); }))
return false;
is_set = false;
return true;
@@ -54,9 +55,9 @@ public:
}
private:
- bool is_set = false;
std::condition_variable condvar;
std::mutex mutex;
+ std::atomic_bool is_set{false};
};
class Barrier {
@@ -86,6 +87,15 @@ private:
std::size_t generation = 0; // Incremented once each time the barrier is used
};
+enum class ThreadPriority : u32 {
+ Low = 0,
+ Normal = 1,
+ High = 2,
+ VeryHigh = 3,
+};
+
+void SetCurrentThreadPriority(ThreadPriority new_priority);
+
void SetCurrentThreadName(const char* name);
} // namespace Common
diff --git a/src/common/thread_queue_list.h b/src/common/thread_queue_list.h
index 791f99a8c5..def9e5d8d1 100644
--- a/src/common/thread_queue_list.h
+++ b/src/common/thread_queue_list.h
@@ -18,14 +18,14 @@ struct ThreadQueueList {
using Priority = unsigned int;
// Number of priority levels. (Valid levels are [0..NUM_QUEUES).)
- static const Priority NUM_QUEUES = N;
+ static constexpr Priority NUM_QUEUES = N;
ThreadQueueList() {
first = nullptr;
}
// Only for debugging, returns priority level.
- Priority contains(const T& uid) const {
+ [[nodiscard]] Priority contains(const T& uid) const {
for (Priority i = 0; i < NUM_QUEUES; ++i) {
const Queue& cur = queues[i];
if (std::find(cur.data.cbegin(), cur.data.cend(), uid) != cur.data.cend()) {
@@ -36,7 +36,7 @@ struct ThreadQueueList {
return -1;
}
- T get_first() const {
+ [[nodiscard]] T get_first() const {
const Queue* cur = first;
while (cur != nullptr) {
if (!cur->data.empty()) {
@@ -49,7 +49,7 @@ struct ThreadQueueList {
}
template <typename UnaryPredicate>
- T get_first_filter(UnaryPredicate filter) const {
+ [[nodiscard]] T get_first_filter(UnaryPredicate filter) const {
const Queue* cur = first;
while (cur != nullptr) {
if (!cur->data.empty()) {
@@ -129,7 +129,7 @@ struct ThreadQueueList {
first = nullptr;
}
- bool empty(Priority priority) const {
+ [[nodiscard]] bool empty(Priority priority) const {
const Queue* cur = &queues[priority];
return cur->data.empty();
}
diff --git a/src/common/threadsafe_queue.h b/src/common/threadsafe_queue.h
index 8268bbd5c8..a4647314a7 100644
--- a/src/common/threadsafe_queue.h
+++ b/src/common/threadsafe_queue.h
@@ -25,15 +25,15 @@ public:
delete read_ptr;
}
- std::size_t Size() const {
+ [[nodiscard]] std::size_t Size() const {
return size.load();
}
- bool Empty() const {
+ [[nodiscard]] bool Empty() const {
return Size() == 0;
}
- T& Front() const {
+ [[nodiscard]] T& Front() const {
return read_ptr->current;
}
@@ -130,15 +130,15 @@ private:
template <typename T>
class MPSCQueue {
public:
- std::size_t Size() const {
+ [[nodiscard]] std::size_t Size() const {
return spsc_queue.Size();
}
- bool Empty() const {
+ [[nodiscard]] bool Empty() const {
return spsc_queue.Empty();
}
- T& Front() const {
+ [[nodiscard]] T& Front() const {
return spsc_queue.Front();
}
diff --git a/src/common/time_zone.cpp b/src/common/time_zone.cpp
new file mode 100644
index 0000000000..ce239eb63f
--- /dev/null
+++ b/src/common/time_zone.cpp
@@ -0,0 +1,49 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <chrono>
+#include <iomanip>
+#include <sstream>
+
+#include "common/logging/log.h"
+#include "common/time_zone.h"
+
+namespace Common::TimeZone {
+
+std::string GetDefaultTimeZone() {
+ return "GMT";
+}
+
+static std::string GetOsTimeZoneOffset() {
+ const std::time_t t{std::time(nullptr)};
+ const std::tm tm{*std::localtime(&t)};
+
+ std::stringstream ss;
+ ss << std::put_time(&tm, "%z"); // Get the current timezone offset, e.g. "-400", as a string
+
+ return ss.str();
+}
+
+static int ConvertOsTimeZoneOffsetToInt(const std::string& timezone) {
+ try {
+ return std::stoi(timezone);
+ } catch (const std::invalid_argument&) {
+ LOG_CRITICAL(Common, "invalid_argument with {}!", timezone);
+ return 0;
+ } catch (const std::out_of_range&) {
+ LOG_CRITICAL(Common, "out_of_range with {}!", timezone);
+ return 0;
+ }
+}
+
+std::chrono::seconds GetCurrentOffsetSeconds() {
+ const int offset{ConvertOsTimeZoneOffsetToInt(GetOsTimeZoneOffset())};
+
+ int seconds{(offset / 100) * 60 * 60}; // Convert hour component to seconds
+ seconds += (offset % 100) * 60; // Convert minute component to seconds
+
+ return std::chrono::seconds{seconds};
+}
+
+} // namespace Common::TimeZone
diff --git a/src/common/time_zone.h b/src/common/time_zone.h
new file mode 100644
index 0000000000..9f5939ca50
--- /dev/null
+++ b/src/common/time_zone.h
@@ -0,0 +1,18 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <chrono>
+#include <string>
+
+namespace Common::TimeZone {
+
+/// Gets the default timezone, i.e. "GMT"
+[[nodiscard]] std::string GetDefaultTimeZone();
+
+/// Gets the offset of the current timezone (from the default), in seconds
+[[nodiscard]] std::chrono::seconds GetCurrentOffsetSeconds();
+
+} // namespace Common::TimeZone
diff --git a/src/common/timer.cpp b/src/common/timer.cpp
index 2dc15e4343..d17dc2a503 100644
--- a/src/common/timer.cpp
+++ b/src/common/timer.cpp
@@ -142,20 +142,18 @@ std::string Timer::GetTimeFormatted() {
// ----------------
double Timer::GetDoubleTime() {
// Get continuous timestamp
- u64 TmpSeconds = static_cast<u64>(Common::Timer::GetTimeSinceJan1970().count());
- double ms = static_cast<u64>(GetTimeMs().count()) % 1000;
+ auto tmp_seconds = static_cast<u64>(GetTimeSinceJan1970().count());
+ const auto ms = static_cast<double>(static_cast<u64>(GetTimeMs().count()) % 1000);
// Remove a few years. We only really want enough seconds to make
// sure that we are detecting actual actions, perhaps 60 seconds is
// enough really, but I leave a year of seconds anyway, in case the
// user's clock is incorrect or something like that.
- TmpSeconds = TmpSeconds - (38 * 365 * 24 * 60 * 60);
+ tmp_seconds = tmp_seconds - (38 * 365 * 24 * 60 * 60);
// Make a smaller integer that fits in the double
- u32 Seconds = static_cast<u32>(TmpSeconds);
- double TmpTime = Seconds + ms;
-
- return TmpTime;
+ const auto seconds = static_cast<u32>(tmp_seconds);
+ return seconds + ms;
}
} // Namespace Common
diff --git a/src/common/timer.h b/src/common/timer.h
index 27b521baac..8894a143d1 100644
--- a/src/common/timer.h
+++ b/src/common/timer.h
@@ -19,18 +19,18 @@ public:
// The time difference is always returned in milliseconds, regardless of alternative internal
// representation
- std::chrono::milliseconds GetTimeDifference();
+ [[nodiscard]] std::chrono::milliseconds GetTimeDifference();
void AddTimeDifference();
- static std::chrono::seconds GetTimeSinceJan1970();
- static std::chrono::seconds GetLocalTimeSinceJan1970();
- static double GetDoubleTime();
+ [[nodiscard]] static std::chrono::seconds GetTimeSinceJan1970();
+ [[nodiscard]] static std::chrono::seconds GetLocalTimeSinceJan1970();
+ [[nodiscard]] static double GetDoubleTime();
- static std::string GetTimeFormatted();
- std::string GetTimeElapsedFormatted() const;
- std::chrono::milliseconds GetTimeElapsed();
+ [[nodiscard]] static std::string GetTimeFormatted();
+ [[nodiscard]] std::string GetTimeElapsedFormatted() const;
+ [[nodiscard]] std::chrono::milliseconds GetTimeElapsed();
- static std::chrono::milliseconds GetTimeMs();
+ [[nodiscard]] static std::chrono::milliseconds GetTimeMs();
private:
std::chrono::milliseconds m_LastTime;
diff --git a/src/common/uint128.cpp b/src/common/uint128.cpp
index 32bf56730f..16bf7c8283 100644
--- a/src/common/uint128.cpp
+++ b/src/common/uint128.cpp
@@ -6,12 +6,38 @@
#include <intrin.h>
#pragma intrinsic(_umul128)
+#pragma intrinsic(_udiv128)
#endif
#include <cstring>
#include "common/uint128.h"
namespace Common {
+#ifdef _MSC_VER
+
+u64 MultiplyAndDivide64(u64 a, u64 b, u64 d) {
+ u128 r{};
+ r[0] = _umul128(a, b, &r[1]);
+ u64 remainder;
+#if _MSC_VER < 1923
+ return udiv128(r[1], r[0], d, &remainder);
+#else
+ return _udiv128(r[1], r[0], d, &remainder);
+#endif
+}
+
+#else
+
+u64 MultiplyAndDivide64(u64 a, u64 b, u64 d) {
+ const u64 diva = a / d;
+ const u64 moda = a % d;
+ const u64 divb = b / d;
+ const u64 modb = b % d;
+ return diva * b + moda * divb + moda * modb / d;
+}
+
+#endif
+
u128 Multiply64Into128(u64 a, u64 b) {
u128 result;
#ifdef _MSC_VER
diff --git a/src/common/uint128.h b/src/common/uint128.h
index a3be2a2cba..969259ab6c 100644
--- a/src/common/uint128.h
+++ b/src/common/uint128.h
@@ -9,11 +9,14 @@
namespace Common {
+// This function multiplies 2 u64 values and divides it by a u64 value.
+[[nodiscard]] u64 MultiplyAndDivide64(u64 a, u64 b, u64 d);
+
// This function multiplies 2 u64 values and produces a u128 value;
-u128 Multiply64Into128(u64 a, u64 b);
+[[nodiscard]] u128 Multiply64Into128(u64 a, u64 b);
// This function divides a u128 by a u32 value and produces two u64 values:
// the result of division and the remainder
-std::pair<u64, u64> Divide128On32(u128 dividend, u32 divisor);
+[[nodiscard]] std::pair<u64, u64> Divide128On32(u128 dividend, u32 divisor);
} // namespace Common
diff --git a/src/common/uuid.h b/src/common/uuid.h
index f6ad064fb7..4ab9a25f0d 100644
--- a/src/common/uuid.h
+++ b/src/common/uuid.h
@@ -19,29 +19,34 @@ struct UUID {
constexpr explicit UUID(const u128& id) : uuid{id} {}
constexpr explicit UUID(const u64 lo, const u64 hi) : uuid{{lo, hi}} {}
- constexpr explicit operator bool() const {
+ [[nodiscard]] constexpr explicit operator bool() const {
return uuid[0] != INVALID_UUID[0] && uuid[1] != INVALID_UUID[1];
}
- constexpr bool operator==(const UUID& rhs) const {
+ [[nodiscard]] constexpr bool operator==(const UUID& rhs) const {
// TODO(DarkLordZach): Replace with uuid == rhs.uuid with C++20
return uuid[0] == rhs.uuid[0] && uuid[1] == rhs.uuid[1];
}
- constexpr bool operator!=(const UUID& rhs) const {
+ [[nodiscard]] constexpr bool operator!=(const UUID& rhs) const {
return !operator==(rhs);
}
// TODO(ogniK): Properly generate uuids based on RFC-4122
- static UUID Generate();
+ [[nodiscard]] static UUID Generate();
// Set the UUID to {0,0} to be considered an invalid user
constexpr void Invalidate() {
uuid = INVALID_UUID;
}
- std::string Format() const;
- std::string FormatSwitch() const;
+ // TODO(ogniK): Properly generate a Nintendo ID
+ [[nodiscard]] constexpr u64 GetNintendoID() const {
+ return uuid[0];
+ }
+
+ [[nodiscard]] std::string Format() const;
+ [[nodiscard]] std::string FormatSwitch() const;
};
static_assert(sizeof(UUID) == 16, "UUID is an invalid size!");
diff --git a/src/common/vector_math.h b/src/common/vector_math.h
index 4294853296..22dba3c2d2 100644
--- a/src/common/vector_math.h
+++ b/src/common/vector_math.h
@@ -52,15 +52,15 @@ public:
constexpr Vec2(const T& x_, const T& y_) : x(x_), y(y_) {}
template <typename T2>
- constexpr Vec2<T2> Cast() const {
+ [[nodiscard]] constexpr Vec2<T2> Cast() const {
return Vec2<T2>(static_cast<T2>(x), static_cast<T2>(y));
}
- static constexpr Vec2 AssignToAll(const T& f) {
+ [[nodiscard]] static constexpr Vec2 AssignToAll(const T& f) {
return Vec2{f, f};
}
- constexpr Vec2<decltype(T{} + T{})> operator+(const Vec2& other) const {
+ [[nodiscard]] constexpr Vec2<decltype(T{} + T{})> operator+(const Vec2& other) const {
return {x + other.x, y + other.y};
}
constexpr Vec2& operator+=(const Vec2& other) {
@@ -68,7 +68,7 @@ public:
y += other.y;
return *this;
}
- constexpr Vec2<decltype(T{} - T{})> operator-(const Vec2& other) const {
+ [[nodiscard]] constexpr Vec2<decltype(T{} - T{})> operator-(const Vec2& other) const {
return {x - other.x, y - other.y};
}
constexpr Vec2& operator-=(const Vec2& other) {
@@ -78,16 +78,22 @@ public:
}
template <typename U = T>
- constexpr Vec2<std::enable_if_t<std::is_signed_v<U>, U>> operator-() const {
+ [[nodiscard]] constexpr Vec2<std::enable_if_t<std::is_signed_v<U>, U>> operator-() const {
return {-x, -y};
}
- constexpr Vec2<decltype(T{} * T{})> operator*(const Vec2& other) const {
+ [[nodiscard]] constexpr Vec2<decltype(T{} * T{})> operator*(const Vec2& other) const {
return {x * other.x, y * other.y};
}
template <typename V>
- constexpr Vec2<decltype(T{} * V{})> operator*(const V& f) const {
- return {x * f, y * f};
+ [[nodiscard]] constexpr Vec2<decltype(T{} * V{})> operator*(const V& f) const {
+ using TV = decltype(T{} * V{});
+ using C = std::common_type_t<T, V>;
+
+ return {
+ static_cast<TV>(static_cast<C>(x) * static_cast<C>(f)),
+ static_cast<TV>(static_cast<C>(y) * static_cast<C>(f)),
+ };
}
template <typename V>
@@ -97,8 +103,14 @@ public:
}
template <typename V>
- constexpr Vec2<decltype(T{} / V{})> operator/(const V& f) const {
- return {x / f, y / f};
+ [[nodiscard]] constexpr Vec2<decltype(T{} / V{})> operator/(const V& f) const {
+ using TV = decltype(T{} / V{});
+ using C = std::common_type_t<T, V>;
+
+ return {
+ static_cast<TV>(static_cast<C>(x) / static_cast<C>(f)),
+ static_cast<TV>(static_cast<C>(y) / static_cast<C>(f)),
+ };
}
template <typename V>
@@ -107,18 +119,18 @@ public:
return *this;
}
- constexpr T Length2() const {
+ [[nodiscard]] constexpr T Length2() const {
return x * x + y * y;
}
// Only implemented for T=float
- float Length() const;
- float Normalize(); // returns the previous length, which is often useful
+ [[nodiscard]] float Length() const;
+ [[nodiscard]] float Normalize(); // returns the previous length, which is often useful
- constexpr T& operator[](std::size_t i) {
+ [[nodiscard]] constexpr T& operator[](std::size_t i) {
return *((&x) + i);
}
- constexpr const T& operator[](std::size_t i) const {
+ [[nodiscard]] constexpr const T& operator[](std::size_t i) const {
return *((&x) + i);
}
@@ -128,47 +140,50 @@ public:
}
// Common aliases: UV (texel coordinates), ST (texture coordinates)
- constexpr T& u() {
+ [[nodiscard]] constexpr T& u() {
return x;
}
- constexpr T& v() {
+ [[nodiscard]] constexpr T& v() {
return y;
}
- constexpr T& s() {
+ [[nodiscard]] constexpr T& s() {
return x;
}
- constexpr T& t() {
+ [[nodiscard]] constexpr T& t() {
return y;
}
- constexpr const T& u() const {
+ [[nodiscard]] constexpr const T& u() const {
return x;
}
- constexpr const T& v() const {
+ [[nodiscard]] constexpr const T& v() const {
return y;
}
- constexpr const T& s() const {
+ [[nodiscard]] constexpr const T& s() const {
return x;
}
- constexpr const T& t() const {
+ [[nodiscard]] constexpr const T& t() const {
return y;
}
// swizzlers - create a subvector of specific components
- constexpr Vec2 yx() const {
+ [[nodiscard]] constexpr Vec2 yx() const {
return Vec2(y, x);
}
- constexpr Vec2 vu() const {
+ [[nodiscard]] constexpr Vec2 vu() const {
return Vec2(y, x);
}
- constexpr Vec2 ts() const {
+ [[nodiscard]] constexpr Vec2 ts() const {
return Vec2(y, x);
}
};
template <typename T, typename V>
-constexpr Vec2<T> operator*(const V& f, const Vec2<T>& vec) {
- return Vec2<T>(f * vec.x, f * vec.y);
+[[nodiscard]] constexpr Vec2<T> operator*(const V& f, const Vec2<T>& vec) {
+ using C = std::common_type_t<T, V>;
+
+ return Vec2<T>(static_cast<T>(static_cast<C>(f) * static_cast<C>(vec.x)),
+ static_cast<T>(static_cast<C>(f) * static_cast<C>(vec.y)));
}
using Vec2f = Vec2<float>;
@@ -196,15 +211,15 @@ public:
constexpr Vec3(const T& x_, const T& y_, const T& z_) : x(x_), y(y_), z(z_) {}
template <typename T2>
- constexpr Vec3<T2> Cast() const {
+ [[nodiscard]] constexpr Vec3<T2> Cast() const {
return Vec3<T2>(static_cast<T2>(x), static_cast<T2>(y), static_cast<T2>(z));
}
- static constexpr Vec3 AssignToAll(const T& f) {
+ [[nodiscard]] static constexpr Vec3 AssignToAll(const T& f) {
return Vec3(f, f, f);
}
- constexpr Vec3<decltype(T{} + T{})> operator+(const Vec3& other) const {
+ [[nodiscard]] constexpr Vec3<decltype(T{} + T{})> operator+(const Vec3& other) const {
return {x + other.x, y + other.y, z + other.z};
}
@@ -215,7 +230,7 @@ public:
return *this;
}
- constexpr Vec3<decltype(T{} - T{})> operator-(const Vec3& other) const {
+ [[nodiscard]] constexpr Vec3<decltype(T{} - T{})> operator-(const Vec3& other) const {
return {x - other.x, y - other.y, z - other.z};
}
@@ -227,17 +242,24 @@ public:
}
template <typename U = T>
- constexpr Vec3<std::enable_if_t<std::is_signed_v<U>, U>> operator-() const {
+ [[nodiscard]] constexpr Vec3<std::enable_if_t<std::is_signed_v<U>, U>> operator-() const {
return {-x, -y, -z};
}
- constexpr Vec3<decltype(T{} * T{})> operator*(const Vec3& other) const {
+ [[nodiscard]] constexpr Vec3<decltype(T{} * T{})> operator*(const Vec3& other) const {
return {x * other.x, y * other.y, z * other.z};
}
template <typename V>
- constexpr Vec3<decltype(T{} * V{})> operator*(const V& f) const {
- return {x * f, y * f, z * f};
+ [[nodiscard]] constexpr Vec3<decltype(T{} * V{})> operator*(const V& f) const {
+ using TV = decltype(T{} * V{});
+ using C = std::common_type_t<T, V>;
+
+ return {
+ static_cast<TV>(static_cast<C>(x) * static_cast<C>(f)),
+ static_cast<TV>(static_cast<C>(y) * static_cast<C>(f)),
+ static_cast<TV>(static_cast<C>(z) * static_cast<C>(f)),
+ };
}
template <typename V>
@@ -246,8 +268,15 @@ public:
return *this;
}
template <typename V>
- constexpr Vec3<decltype(T{} / V{})> operator/(const V& f) const {
- return {x / f, y / f, z / f};
+ [[nodiscard]] constexpr Vec3<decltype(T{} / V{})> operator/(const V& f) const {
+ using TV = decltype(T{} / V{});
+ using C = std::common_type_t<T, V>;
+
+ return {
+ static_cast<TV>(static_cast<C>(x) / static_cast<C>(f)),
+ static_cast<TV>(static_cast<C>(y) / static_cast<C>(f)),
+ static_cast<TV>(static_cast<C>(z) / static_cast<C>(f)),
+ };
}
template <typename V>
@@ -256,20 +285,20 @@ public:
return *this;
}
- constexpr T Length2() const {
+ [[nodiscard]] constexpr T Length2() const {
return x * x + y * y + z * z;
}
// Only implemented for T=float
- float Length() const;
- Vec3 Normalized() const;
- float Normalize(); // returns the previous length, which is often useful
+ [[nodiscard]] float Length() const;
+ [[nodiscard]] Vec3 Normalized() const;
+ [[nodiscard]] float Normalize(); // returns the previous length, which is often useful
- constexpr T& operator[](std::size_t i) {
+ [[nodiscard]] constexpr T& operator[](std::size_t i) {
return *((&x) + i);
}
- constexpr const T& operator[](std::size_t i) const {
+ [[nodiscard]] constexpr const T& operator[](std::size_t i) const {
return *((&x) + i);
}
@@ -280,63 +309,63 @@ public:
}
// Common aliases: UVW (texel coordinates), RGB (colors), STQ (texture coordinates)
- constexpr T& u() {
+ [[nodiscard]] constexpr T& u() {
return x;
}
- constexpr T& v() {
+ [[nodiscard]] constexpr T& v() {
return y;
}
- constexpr T& w() {
+ [[nodiscard]] constexpr T& w() {
return z;
}
- constexpr T& r() {
+ [[nodiscard]] constexpr T& r() {
return x;
}
- constexpr T& g() {
+ [[nodiscard]] constexpr T& g() {
return y;
}
- constexpr T& b() {
+ [[nodiscard]] constexpr T& b() {
return z;
}
- constexpr T& s() {
+ [[nodiscard]] constexpr T& s() {
return x;
}
- constexpr T& t() {
+ [[nodiscard]] constexpr T& t() {
return y;
}
- constexpr T& q() {
+ [[nodiscard]] constexpr T& q() {
return z;
}
- constexpr const T& u() const {
+ [[nodiscard]] constexpr const T& u() const {
return x;
}
- constexpr const T& v() const {
+ [[nodiscard]] constexpr const T& v() const {
return y;
}
- constexpr const T& w() const {
+ [[nodiscard]] constexpr const T& w() const {
return z;
}
- constexpr const T& r() const {
+ [[nodiscard]] constexpr const T& r() const {
return x;
}
- constexpr const T& g() const {
+ [[nodiscard]] constexpr const T& g() const {
return y;
}
- constexpr const T& b() const {
+ [[nodiscard]] constexpr const T& b() const {
return z;
}
- constexpr const T& s() const {
+ [[nodiscard]] constexpr const T& s() const {
return x;
}
- constexpr const T& t() const {
+ [[nodiscard]] constexpr const T& t() const {
return y;
}
- constexpr const T& q() const {
+ [[nodiscard]] constexpr const T& q() const {
return z;
}
@@ -345,7 +374,7 @@ public:
// _DEFINE_SWIZZLER2 defines a single such function, DEFINE_SWIZZLER2 defines all of them for all
// component names (x<->r) and permutations (xy<->yx)
#define _DEFINE_SWIZZLER2(a, b, name) \
- constexpr Vec2<T> name() const { \
+ [[nodiscard]] constexpr Vec2<T> name() const { \
return Vec2<T>(a, b); \
}
#define DEFINE_SWIZZLER2(a, b, a2, b2, a3, b3, a4, b4) \
@@ -366,8 +395,12 @@ public:
};
template <typename T, typename V>
-constexpr Vec3<T> operator*(const V& f, const Vec3<T>& vec) {
- return Vec3<T>(f * vec.x, f * vec.y, f * vec.z);
+[[nodiscard]] constexpr Vec3<T> operator*(const V& f, const Vec3<T>& vec) {
+ using C = std::common_type_t<T, V>;
+
+ return Vec3<T>(static_cast<T>(static_cast<C>(f) * static_cast<C>(vec.x)),
+ static_cast<T>(static_cast<C>(f) * static_cast<C>(vec.y)),
+ static_cast<T>(static_cast<C>(f) * static_cast<C>(vec.z)));
}
template <>
@@ -402,16 +435,16 @@ public:
: x(x_), y(y_), z(z_), w(w_) {}
template <typename T2>
- constexpr Vec4<T2> Cast() const {
+ [[nodiscard]] constexpr Vec4<T2> Cast() const {
return Vec4<T2>(static_cast<T2>(x), static_cast<T2>(y), static_cast<T2>(z),
static_cast<T2>(w));
}
- static constexpr Vec4 AssignToAll(const T& f) {
+ [[nodiscard]] static constexpr Vec4 AssignToAll(const T& f) {
return Vec4(f, f, f, f);
}
- constexpr Vec4<decltype(T{} + T{})> operator+(const Vec4& other) const {
+ [[nodiscard]] constexpr Vec4<decltype(T{} + T{})> operator+(const Vec4& other) const {
return {x + other.x, y + other.y, z + other.z, w + other.w};
}
@@ -423,7 +456,7 @@ public:
return *this;
}
- constexpr Vec4<decltype(T{} - T{})> operator-(const Vec4& other) const {
+ [[nodiscard]] constexpr Vec4<decltype(T{} - T{})> operator-(const Vec4& other) const {
return {x - other.x, y - other.y, z - other.z, w - other.w};
}
@@ -436,17 +469,25 @@ public:
}
template <typename U = T>
- constexpr Vec4<std::enable_if_t<std::is_signed_v<U>, U>> operator-() const {
+ [[nodiscard]] constexpr Vec4<std::enable_if_t<std::is_signed_v<U>, U>> operator-() const {
return {-x, -y, -z, -w};
}
- constexpr Vec4<decltype(T{} * T{})> operator*(const Vec4& other) const {
+ [[nodiscard]] constexpr Vec4<decltype(T{} * T{})> operator*(const Vec4& other) const {
return {x * other.x, y * other.y, z * other.z, w * other.w};
}
template <typename V>
- constexpr Vec4<decltype(T{} * V{})> operator*(const V& f) const {
- return {x * f, y * f, z * f, w * f};
+ [[nodiscard]] constexpr Vec4<decltype(T{} * V{})> operator*(const V& f) const {
+ using TV = decltype(T{} * V{});
+ using C = std::common_type_t<T, V>;
+
+ return {
+ static_cast<TV>(static_cast<C>(x) * static_cast<C>(f)),
+ static_cast<TV>(static_cast<C>(y) * static_cast<C>(f)),
+ static_cast<TV>(static_cast<C>(z) * static_cast<C>(f)),
+ static_cast<TV>(static_cast<C>(w) * static_cast<C>(f)),
+ };
}
template <typename V>
@@ -456,8 +497,16 @@ public:
}
template <typename V>
- constexpr Vec4<decltype(T{} / V{})> operator/(const V& f) const {
- return {x / f, y / f, z / f, w / f};
+ [[nodiscard]] constexpr Vec4<decltype(T{} / V{})> operator/(const V& f) const {
+ using TV = decltype(T{} / V{});
+ using C = std::common_type_t<T, V>;
+
+ return {
+ static_cast<TV>(static_cast<C>(x) / static_cast<C>(f)),
+ static_cast<TV>(static_cast<C>(y) / static_cast<C>(f)),
+ static_cast<TV>(static_cast<C>(z) / static_cast<C>(f)),
+ static_cast<TV>(static_cast<C>(w) / static_cast<C>(f)),
+ };
}
template <typename V>
@@ -466,15 +515,15 @@ public:
return *this;
}
- constexpr T Length2() const {
+ [[nodiscard]] constexpr T Length2() const {
return x * x + y * y + z * z + w * w;
}
- constexpr T& operator[](std::size_t i) {
+ [[nodiscard]] constexpr T& operator[](std::size_t i) {
return *((&x) + i);
}
- constexpr const T& operator[](std::size_t i) const {
+ [[nodiscard]] constexpr const T& operator[](std::size_t i) const {
return *((&x) + i);
}
@@ -486,29 +535,29 @@ public:
}
// Common alias: RGBA (colors)
- constexpr T& r() {
+ [[nodiscard]] constexpr T& r() {
return x;
}
- constexpr T& g() {
+ [[nodiscard]] constexpr T& g() {
return y;
}
- constexpr T& b() {
+ [[nodiscard]] constexpr T& b() {
return z;
}
- constexpr T& a() {
+ [[nodiscard]] constexpr T& a() {
return w;
}
- constexpr const T& r() const {
+ [[nodiscard]] constexpr const T& r() const {
return x;
}
- constexpr const T& g() const {
+ [[nodiscard]] constexpr const T& g() const {
return y;
}
- constexpr const T& b() const {
+ [[nodiscard]] constexpr const T& b() const {
return z;
}
- constexpr const T& a() const {
+ [[nodiscard]] constexpr const T& a() const {
return w;
}
@@ -520,7 +569,7 @@ public:
// DEFINE_SWIZZLER2_COMP2 defines two component functions for all component names (x<->r) and
// permutations (xy<->yx)
#define _DEFINE_SWIZZLER2(a, b, name) \
- constexpr Vec2<T> name() const { \
+ [[nodiscard]] constexpr Vec2<T> name() const { \
return Vec2<T>(a, b); \
}
#define DEFINE_SWIZZLER2_COMP1(a, a2) \
@@ -547,7 +596,7 @@ public:
#undef _DEFINE_SWIZZLER2
#define _DEFINE_SWIZZLER3(a, b, c, name) \
- constexpr Vec3<T> name() const { \
+ [[nodiscard]] constexpr Vec3<T> name() const { \
return Vec3<T>(a, b, c); \
}
#define DEFINE_SWIZZLER3_COMP1(a, a2) \
@@ -581,8 +630,16 @@ public:
};
template <typename T, typename V>
-constexpr Vec4<decltype(V{} * T{})> operator*(const V& f, const Vec4<T>& vec) {
- return {f * vec.x, f * vec.y, f * vec.z, f * vec.w};
+[[nodiscard]] constexpr Vec4<decltype(V{} * T{})> operator*(const V& f, const Vec4<T>& vec) {
+ using TV = decltype(V{} * T{});
+ using C = std::common_type_t<T, V>;
+
+ return {
+ static_cast<TV>(static_cast<C>(f) * static_cast<C>(vec.x)),
+ static_cast<TV>(static_cast<C>(f) * static_cast<C>(vec.y)),
+ static_cast<TV>(static_cast<C>(f) * static_cast<C>(vec.z)),
+ static_cast<TV>(static_cast<C>(f) * static_cast<C>(vec.w)),
+ };
}
using Vec4f = Vec4<float>;
@@ -593,39 +650,41 @@ constexpr decltype(T{} * T{} + T{} * T{}) Dot(const Vec2<T>& a, const Vec2<T>& b
}
template <typename T>
-constexpr decltype(T{} * T{} + T{} * T{}) Dot(const Vec3<T>& a, const Vec3<T>& b) {
+[[nodiscard]] constexpr decltype(T{} * T{} + T{} * T{}) Dot(const Vec3<T>& a, const Vec3<T>& b) {
return a.x * b.x + a.y * b.y + a.z * b.z;
}
template <typename T>
-constexpr decltype(T{} * T{} + T{} * T{}) Dot(const Vec4<T>& a, const Vec4<T>& b) {
+[[nodiscard]] constexpr decltype(T{} * T{} + T{} * T{}) Dot(const Vec4<T>& a, const Vec4<T>& b) {
return a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w;
}
template <typename T>
-constexpr Vec3<decltype(T{} * T{} - T{} * T{})> Cross(const Vec3<T>& a, const Vec3<T>& b) {
+[[nodiscard]] constexpr Vec3<decltype(T{} * T{} - T{} * T{})> Cross(const Vec3<T>& a,
+ const Vec3<T>& b) {
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};
}
// linear interpolation via float: 0.0=begin, 1.0=end
template <typename X>
-constexpr decltype(X{} * float{} + X{} * float{}) Lerp(const X& begin, const X& end,
- const float t) {
+[[nodiscard]] constexpr decltype(X{} * float{} + X{} * float{}) Lerp(const X& begin, const X& end,
+ const float t) {
return begin * (1.f - t) + end * t;
}
// linear interpolation via int: 0=begin, base=end
template <typename X, int base>
-constexpr decltype((X{} * int{} + X{} * int{}) / base) LerpInt(const X& begin, const X& end,
- const int t) {
+[[nodiscard]] constexpr decltype((X{} * int{} + X{} * int{}) / base) LerpInt(const X& begin,
+ const X& end,
+ const int t) {
return (begin * (base - t) + end * t) / base;
}
// bilinear interpolation. s is for interpolating x00-x01 and x10-x11, and t is for the second
// interpolation.
template <typename X>
-constexpr auto BilinearInterp(const X& x00, const X& x01, const X& x10, const X& x11, const float s,
- const float t) {
+[[nodiscard]] constexpr auto BilinearInterp(const X& x00, const X& x01, const X& x10, const X& x11,
+ const float s, const float t) {
auto y0 = Lerp(x00, x01, s);
auto y1 = Lerp(x10, x11, s);
return Lerp(y0, y1, t);
@@ -633,42 +692,42 @@ constexpr auto BilinearInterp(const X& x00, const X& x01, const X& x10, const X&
// Utility vector factories
template <typename T>
-constexpr Vec2<T> MakeVec(const T& x, const T& y) {
+[[nodiscard]] constexpr Vec2<T> MakeVec(const T& x, const T& y) {
return Vec2<T>{x, y};
}
template <typename T>
-constexpr Vec3<T> MakeVec(const T& x, const T& y, const T& z) {
+[[nodiscard]] constexpr Vec3<T> MakeVec(const T& x, const T& y, const T& z) {
return Vec3<T>{x, y, z};
}
template <typename T>
-constexpr Vec4<T> MakeVec(const T& x, const T& y, const Vec2<T>& zw) {
+[[nodiscard]] constexpr Vec4<T> MakeVec(const T& x, const T& y, const Vec2<T>& zw) {
return MakeVec(x, y, zw[0], zw[1]);
}
template <typename T>
-constexpr Vec3<T> MakeVec(const Vec2<T>& xy, const T& z) {
+[[nodiscard]] constexpr Vec3<T> MakeVec(const Vec2<T>& xy, const T& z) {
return MakeVec(xy[0], xy[1], z);
}
template <typename T>
-constexpr Vec3<T> MakeVec(const T& x, const Vec2<T>& yz) {
+[[nodiscard]] constexpr Vec3<T> MakeVec(const T& x, const Vec2<T>& yz) {
return MakeVec(x, yz[0], yz[1]);
}
template <typename T>
-constexpr Vec4<T> MakeVec(const T& x, const T& y, const T& z, const T& w) {
+[[nodiscard]] constexpr Vec4<T> MakeVec(const T& x, const T& y, const T& z, const T& w) {
return Vec4<T>{x, y, z, w};
}
template <typename T>
-constexpr Vec4<T> MakeVec(const Vec2<T>& xy, const T& z, const T& w) {
+[[nodiscard]] constexpr Vec4<T> MakeVec(const Vec2<T>& xy, const T& z, const T& w) {
return MakeVec(xy[0], xy[1], z, w);
}
template <typename T>
-constexpr Vec4<T> MakeVec(const T& x, const Vec2<T>& yz, const T& w) {
+[[nodiscard]] constexpr Vec4<T> MakeVec(const T& x, const Vec2<T>& yz, const T& w) {
return MakeVec(x, yz[0], yz[1], w);
}
@@ -676,17 +735,17 @@ constexpr Vec4<T> MakeVec(const T& x, const Vec2<T>& yz, const T& w) {
// Even if someone wanted to use an odd object like Vec2<Vec2<T>>, the compiler would error
// out soon enough due to misuse of the returned structure.
template <typename T>
-constexpr Vec4<T> MakeVec(const Vec2<T>& xy, const Vec2<T>& zw) {
+[[nodiscard]] constexpr Vec4<T> MakeVec(const Vec2<T>& xy, const Vec2<T>& zw) {
return MakeVec(xy[0], xy[1], zw[0], zw[1]);
}
template <typename T>
-constexpr Vec4<T> MakeVec(const Vec3<T>& xyz, const T& w) {
+[[nodiscard]] constexpr Vec4<T> MakeVec(const Vec3<T>& xyz, const T& w) {
return MakeVec(xyz[0], xyz[1], xyz[2], w);
}
template <typename T>
-constexpr Vec4<T> MakeVec(const T& x, const Vec3<T>& yzw) {
+[[nodiscard]] constexpr Vec4<T> MakeVec(const T& x, const Vec3<T>& yzw) {
return MakeVec(x, yzw[0], yzw[1], yzw[2]);
}
diff --git a/src/common/virtual_buffer.cpp b/src/common/virtual_buffer.cpp
new file mode 100644
index 0000000000..e3ca292581
--- /dev/null
+++ b/src/common/virtual_buffer.cpp
@@ -0,0 +1,43 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#ifdef _WIN32
+#include <windows.h>
+#else
+#include <sys/mman.h>
+#endif
+
+#include "common/assert.h"
+#include "common/virtual_buffer.h"
+
+namespace Common {
+
+void* AllocateMemoryPages(std::size_t size) noexcept {
+#ifdef _WIN32
+ void* base{VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_READWRITE)};
+#else
+ void* base{mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0)};
+
+ if (base == MAP_FAILED) {
+ base = nullptr;
+ }
+#endif
+
+ ASSERT(base);
+
+ return base;
+}
+
+void FreeMemoryPages(void* base, [[maybe_unused]] std::size_t size) noexcept {
+ if (!base) {
+ return;
+ }
+#ifdef _WIN32
+ ASSERT(VirtualFree(base, 0, MEM_RELEASE));
+#else
+ ASSERT(munmap(base, size) == 0);
+#endif
+}
+
+} // namespace Common
diff --git a/src/common/virtual_buffer.h b/src/common/virtual_buffer.h
new file mode 100644
index 0000000000..91d4300360
--- /dev/null
+++ b/src/common/virtual_buffer.h
@@ -0,0 +1,82 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <type_traits>
+#include <utility>
+
+namespace Common {
+
+void* AllocateMemoryPages(std::size_t size) noexcept;
+void FreeMemoryPages(void* base, std::size_t size) noexcept;
+
+template <typename T>
+class VirtualBuffer final {
+public:
+ static_assert(
+ std::is_trivially_constructible_v<T>,
+ "T must be trivially constructible, as non-trivial constructors will not be executed "
+ "with the current allocator");
+
+ constexpr VirtualBuffer() = default;
+ explicit VirtualBuffer(std::size_t count) : alloc_size{count * sizeof(T)} {
+ base_ptr = reinterpret_cast<T*>(AllocateMemoryPages(alloc_size));
+ }
+
+ ~VirtualBuffer() noexcept {
+ FreeMemoryPages(base_ptr, alloc_size);
+ }
+
+ VirtualBuffer(const VirtualBuffer&) = delete;
+ VirtualBuffer& operator=(const VirtualBuffer&) = delete;
+
+ VirtualBuffer(VirtualBuffer&& other) noexcept
+ : alloc_size{std::exchange(other.alloc_size, 0)}, base_ptr{std::exchange(other.base_ptr),
+ nullptr} {}
+
+ VirtualBuffer& operator=(VirtualBuffer&& other) noexcept {
+ alloc_size = std::exchange(other.alloc_size, 0);
+ base_ptr = std::exchange(other.base_ptr, nullptr);
+ return *this;
+ }
+
+ void resize(std::size_t count) {
+ const auto new_size = count * sizeof(T);
+ if (new_size == alloc_size) {
+ return;
+ }
+
+ FreeMemoryPages(base_ptr, alloc_size);
+
+ alloc_size = new_size;
+ base_ptr = reinterpret_cast<T*>(AllocateMemoryPages(alloc_size));
+ }
+
+ [[nodiscard]] constexpr const T& operator[](std::size_t index) const {
+ return base_ptr[index];
+ }
+
+ [[nodiscard]] constexpr T& operator[](std::size_t index) {
+ return base_ptr[index];
+ }
+
+ [[nodiscard]] constexpr T* data() {
+ return base_ptr;
+ }
+
+ [[nodiscard]] constexpr const T* data() const {
+ return base_ptr;
+ }
+
+ [[nodiscard]] constexpr std::size_t size() const {
+ return alloc_size / sizeof(T);
+ }
+
+private:
+ std::size_t alloc_size{};
+ T* base_ptr{};
+};
+
+} // namespace Common
diff --git a/src/common/wall_clock.cpp b/src/common/wall_clock.cpp
new file mode 100644
index 0000000000..452a2837e6
--- /dev/null
+++ b/src/common/wall_clock.cpp
@@ -0,0 +1,91 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/uint128.h"
+#include "common/wall_clock.h"
+
+#ifdef ARCHITECTURE_x86_64
+#include "common/x64/cpu_detect.h"
+#include "common/x64/native_clock.h"
+#endif
+
+namespace Common {
+
+using base_timer = std::chrono::steady_clock;
+using base_time_point = std::chrono::time_point<base_timer>;
+
+class StandardWallClock final : public WallClock {
+public:
+ StandardWallClock(u64 emulated_cpu_frequency, u64 emulated_clock_frequency)
+ : WallClock(emulated_cpu_frequency, emulated_clock_frequency, false) {
+ start_time = base_timer::now();
+ }
+
+ std::chrono::nanoseconds GetTimeNS() override {
+ base_time_point current = base_timer::now();
+ auto elapsed = current - start_time;
+ return std::chrono::duration_cast<std::chrono::nanoseconds>(elapsed);
+ }
+
+ std::chrono::microseconds GetTimeUS() override {
+ base_time_point current = base_timer::now();
+ auto elapsed = current - start_time;
+ return std::chrono::duration_cast<std::chrono::microseconds>(elapsed);
+ }
+
+ std::chrono::milliseconds GetTimeMS() override {
+ base_time_point current = base_timer::now();
+ auto elapsed = current - start_time;
+ return std::chrono::duration_cast<std::chrono::milliseconds>(elapsed);
+ }
+
+ u64 GetClockCycles() override {
+ std::chrono::nanoseconds time_now = GetTimeNS();
+ const u128 temporary =
+ Common::Multiply64Into128(time_now.count(), emulated_clock_frequency);
+ return Common::Divide128On32(temporary, 1000000000).first;
+ }
+
+ u64 GetCPUCycles() override {
+ std::chrono::nanoseconds time_now = GetTimeNS();
+ const u128 temporary = Common::Multiply64Into128(time_now.count(), emulated_cpu_frequency);
+ return Common::Divide128On32(temporary, 1000000000).first;
+ }
+
+ void Pause([[maybe_unused]] bool is_paused) override {
+ // Do nothing in this clock type.
+ }
+
+private:
+ base_time_point start_time;
+};
+
+#ifdef ARCHITECTURE_x86_64
+
+std::unique_ptr<WallClock> CreateBestMatchingClock(u32 emulated_cpu_frequency,
+ u32 emulated_clock_frequency) {
+ const auto& caps = GetCPUCaps();
+ u64 rtsc_frequency = 0;
+ if (caps.invariant_tsc) {
+ rtsc_frequency = EstimateRDTSCFrequency();
+ }
+ if (rtsc_frequency == 0) {
+ return std::make_unique<StandardWallClock>(emulated_cpu_frequency,
+ emulated_clock_frequency);
+ } else {
+ return std::make_unique<X64::NativeClock>(emulated_cpu_frequency, emulated_clock_frequency,
+ rtsc_frequency);
+ }
+}
+
+#else
+
+std::unique_ptr<WallClock> CreateBestMatchingClock(u32 emulated_cpu_frequency,
+ u32 emulated_clock_frequency) {
+ return std::make_unique<StandardWallClock>(emulated_cpu_frequency, emulated_clock_frequency);
+}
+
+#endif
+
+} // namespace Common
diff --git a/src/common/wall_clock.h b/src/common/wall_clock.h
new file mode 100644
index 0000000000..bc7adfbf82
--- /dev/null
+++ b/src/common/wall_clock.h
@@ -0,0 +1,55 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <chrono>
+#include <memory>
+
+#include "common/common_types.h"
+
+namespace Common {
+
+class WallClock {
+public:
+ virtual ~WallClock() = default;
+
+ /// Returns current wall time in nanoseconds
+ [[nodiscard]] virtual std::chrono::nanoseconds GetTimeNS() = 0;
+
+ /// Returns current wall time in microseconds
+ [[nodiscard]] virtual std::chrono::microseconds GetTimeUS() = 0;
+
+ /// Returns current wall time in milliseconds
+ [[nodiscard]] virtual std::chrono::milliseconds GetTimeMS() = 0;
+
+ /// Returns current wall time in emulated clock cycles
+ [[nodiscard]] virtual u64 GetClockCycles() = 0;
+
+ /// Returns current wall time in emulated cpu cycles
+ [[nodiscard]] virtual u64 GetCPUCycles() = 0;
+
+ virtual void Pause(bool is_paused) = 0;
+
+ /// Tells if the wall clock, uses the host CPU's hardware clock
+ [[nodiscard]] bool IsNative() const {
+ return is_native;
+ }
+
+protected:
+ WallClock(u64 emulated_cpu_frequency, u64 emulated_clock_frequency, bool is_native)
+ : emulated_cpu_frequency{emulated_cpu_frequency},
+ emulated_clock_frequency{emulated_clock_frequency}, is_native{is_native} {}
+
+ u64 emulated_cpu_frequency;
+ u64 emulated_clock_frequency;
+
+private:
+ bool is_native;
+};
+
+[[nodiscard]] std::unique_ptr<WallClock> CreateBestMatchingClock(u32 emulated_cpu_frequency,
+ u32 emulated_clock_frequency);
+
+} // namespace Common
diff --git a/src/common/web_result.h b/src/common/web_result.h
deleted file mode 100644
index 8bfa2141d8..0000000000
--- a/src/common/web_result.h
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright 2018 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#pragma once
-
-#include <string>
-#include "common/common_types.h"
-
-namespace Common {
-struct WebResult {
- enum class Code : u32 {
- Success,
- InvalidURL,
- CredentialsMissing,
- LibError,
- HttpError,
- WrongContent,
- NoWebservice,
- };
- Code result_code;
- std::string result_string;
- std::string returned_data;
-};
-} // namespace Common
diff --git a/src/common/x64/cpu_detect.cpp b/src/common/x64/cpu_detect.cpp
index c9349a6b42..fccd2eee54 100644
--- a/src/common/x64/cpu_detect.cpp
+++ b/src/common/x64/cpu_detect.cpp
@@ -62,6 +62,17 @@ static CPUCaps Detect() {
std::memcpy(&caps.brand_string[0], &cpu_id[1], sizeof(int));
std::memcpy(&caps.brand_string[4], &cpu_id[3], sizeof(int));
std::memcpy(&caps.brand_string[8], &cpu_id[2], sizeof(int));
+ if (cpu_id[1] == 0x756e6547 && cpu_id[2] == 0x6c65746e && cpu_id[3] == 0x49656e69)
+ caps.manufacturer = Manufacturer::Intel;
+ else if (cpu_id[1] == 0x68747541 && cpu_id[2] == 0x444d4163 && cpu_id[3] == 0x69746e65)
+ caps.manufacturer = Manufacturer::AMD;
+ else if (cpu_id[1] == 0x6f677948 && cpu_id[2] == 0x656e6975 && cpu_id[3] == 0x6e65476e)
+ caps.manufacturer = Manufacturer::Hygon;
+ else
+ caps.manufacturer = Manufacturer::Unknown;
+
+ u32 family = {};
+ u32 model = {};
__cpuid(cpu_id, 0x80000000);
@@ -73,6 +84,14 @@ static CPUCaps Detect() {
// Detect family and other miscellaneous features
if (max_std_fn >= 1) {
__cpuid(cpu_id, 0x00000001);
+ family = (cpu_id[0] >> 8) & 0xf;
+ model = (cpu_id[0] >> 4) & 0xf;
+ if (family == 0xf) {
+ family += (cpu_id[0] >> 20) & 0xff;
+ }
+ if (family >= 6) {
+ model += ((cpu_id[0] >> 16) & 0xf) << 4;
+ }
if ((cpu_id[3] >> 25) & 1)
caps.sse = true;
@@ -110,6 +129,11 @@ static CPUCaps Detect() {
caps.bmi1 = true;
if ((cpu_id[1] >> 8) & 1)
caps.bmi2 = true;
+ // Checks for AVX512F, AVX512CD, AVX512VL, AVX512DQ, AVX512BW (Intel Skylake-X/SP)
+ if ((cpu_id[1] >> 16) & 1 && (cpu_id[1] >> 28) & 1 && (cpu_id[1] >> 31) & 1 &&
+ (cpu_id[1] >> 17) & 1 && (cpu_id[1] >> 30) & 1) {
+ caps.avx512 = caps.avx2;
+ }
}
}
@@ -130,6 +154,20 @@ static CPUCaps Detect() {
caps.fma4 = true;
}
+ if (max_ex_fn >= 0x80000007) {
+ __cpuid(cpu_id, 0x80000007);
+ if (cpu_id[3] & (1 << 8)) {
+ caps.invariant_tsc = true;
+ }
+ }
+
+ if (max_std_fn >= 0x16) {
+ __cpuid(cpu_id, 0x16);
+ caps.base_frequency = cpu_id[0];
+ caps.max_frequency = cpu_id[1];
+ caps.bus_frequency = cpu_id[2];
+ }
+
return caps;
}
diff --git a/src/common/x64/cpu_detect.h b/src/common/x64/cpu_detect.h
index 20f2ba234a..e3b63302ea 100644
--- a/src/common/x64/cpu_detect.h
+++ b/src/common/x64/cpu_detect.h
@@ -6,8 +6,16 @@
namespace Common {
+enum class Manufacturer : u32 {
+ Intel = 0,
+ AMD = 1,
+ Hygon = 2,
+ Unknown = 3,
+};
+
/// x86/x64 CPU capabilities that may be detected by this module
struct CPUCaps {
+ Manufacturer manufacturer;
char cpu_string[0x21];
char brand_string[0x41];
bool sse;
@@ -19,11 +27,16 @@ struct CPUCaps {
bool lzcnt;
bool avx;
bool avx2;
+ bool avx512;
bool bmi1;
bool bmi2;
bool fma;
bool fma4;
bool aes;
+ bool invariant_tsc;
+ u32 base_frequency;
+ u32 max_frequency;
+ u32 bus_frequency;
};
/**
diff --git a/src/common/x64/native_clock.cpp b/src/common/x64/native_clock.cpp
new file mode 100644
index 0000000000..424b39b1f1
--- /dev/null
+++ b/src/common/x64/native_clock.cpp
@@ -0,0 +1,103 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <chrono>
+#include <mutex>
+#include <thread>
+
+#ifdef _MSC_VER
+#include <intrin.h>
+#else
+#include <x86intrin.h>
+#endif
+
+#include "common/uint128.h"
+#include "common/x64/native_clock.h"
+
+namespace Common {
+
+u64 EstimateRDTSCFrequency() {
+ const auto milli_10 = std::chrono::milliseconds{10};
+ // get current time
+ _mm_mfence();
+ const u64 tscStart = __rdtsc();
+ const auto startTime = std::chrono::high_resolution_clock::now();
+ // wait roughly 3 seconds
+ while (true) {
+ auto milli = std::chrono::duration_cast<std::chrono::milliseconds>(
+ std::chrono::high_resolution_clock::now() - startTime);
+ if (milli.count() >= 3000)
+ break;
+ std::this_thread::sleep_for(milli_10);
+ }
+ const auto endTime = std::chrono::high_resolution_clock::now();
+ _mm_mfence();
+ const u64 tscEnd = __rdtsc();
+ // calculate difference
+ const u64 timer_diff =
+ std::chrono::duration_cast<std::chrono::nanoseconds>(endTime - startTime).count();
+ const u64 tsc_diff = tscEnd - tscStart;
+ const u64 tsc_freq = MultiplyAndDivide64(tsc_diff, 1000000000ULL, timer_diff);
+ return tsc_freq;
+}
+
+namespace X64 {
+NativeClock::NativeClock(u64 emulated_cpu_frequency, u64 emulated_clock_frequency,
+ u64 rtsc_frequency)
+ : WallClock(emulated_cpu_frequency, emulated_clock_frequency, true), rtsc_frequency{
+ rtsc_frequency} {
+ _mm_mfence();
+ last_measure = __rdtsc();
+ accumulated_ticks = 0U;
+}
+
+u64 NativeClock::GetRTSC() {
+ std::scoped_lock scope{rtsc_serialize};
+ _mm_mfence();
+ const u64 current_measure = __rdtsc();
+ u64 diff = current_measure - last_measure;
+ diff = diff & ~static_cast<u64>(static_cast<s64>(diff) >> 63); // max(diff, 0)
+ if (current_measure > last_measure) {
+ last_measure = current_measure;
+ }
+ accumulated_ticks += diff;
+ /// The clock cannot be more precise than the guest timer, remove the lower bits
+ return accumulated_ticks & inaccuracy_mask;
+}
+
+void NativeClock::Pause(bool is_paused) {
+ if (!is_paused) {
+ _mm_mfence();
+ last_measure = __rdtsc();
+ }
+}
+
+std::chrono::nanoseconds NativeClock::GetTimeNS() {
+ const u64 rtsc_value = GetRTSC();
+ return std::chrono::nanoseconds{MultiplyAndDivide64(rtsc_value, 1000000000, rtsc_frequency)};
+}
+
+std::chrono::microseconds NativeClock::GetTimeUS() {
+ const u64 rtsc_value = GetRTSC();
+ return std::chrono::microseconds{MultiplyAndDivide64(rtsc_value, 1000000, rtsc_frequency)};
+}
+
+std::chrono::milliseconds NativeClock::GetTimeMS() {
+ const u64 rtsc_value = GetRTSC();
+ return std::chrono::milliseconds{MultiplyAndDivide64(rtsc_value, 1000, rtsc_frequency)};
+}
+
+u64 NativeClock::GetClockCycles() {
+ const u64 rtsc_value = GetRTSC();
+ return MultiplyAndDivide64(rtsc_value, emulated_clock_frequency, rtsc_frequency);
+}
+
+u64 NativeClock::GetCPUCycles() {
+ const u64 rtsc_value = GetRTSC();
+ return MultiplyAndDivide64(rtsc_value, emulated_cpu_frequency, rtsc_frequency);
+}
+
+} // namespace X64
+
+} // namespace Common
diff --git a/src/common/x64/native_clock.h b/src/common/x64/native_clock.h
new file mode 100644
index 0000000000..97aab6ac96
--- /dev/null
+++ b/src/common/x64/native_clock.h
@@ -0,0 +1,48 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <optional>
+
+#include "common/spin_lock.h"
+#include "common/wall_clock.h"
+
+namespace Common {
+
+namespace X64 {
+class NativeClock final : public WallClock {
+public:
+ NativeClock(u64 emulated_cpu_frequency, u64 emulated_clock_frequency, u64 rtsc_frequency);
+
+ std::chrono::nanoseconds GetTimeNS() override;
+
+ std::chrono::microseconds GetTimeUS() override;
+
+ std::chrono::milliseconds GetTimeMS() override;
+
+ u64 GetClockCycles() override;
+
+ u64 GetCPUCycles() override;
+
+ void Pause(bool is_paused) override;
+
+private:
+ u64 GetRTSC();
+
+ /// value used to reduce the native clocks accuracy as some apss rely on
+ /// undefined behavior where the level of accuracy in the clock shouldn't
+ /// be higher.
+ static constexpr u64 inaccuracy_mask = ~(UINT64_C(0x400) - 1);
+
+ SpinLock rtsc_serialize{};
+ u64 last_measure{};
+ u64 accumulated_ticks{};
+ u64 rtsc_frequency;
+};
+} // namespace X64
+
+u64 EstimateRDTSCFrequency();
+
+} // namespace Common
diff --git a/src/common/x64/xbyak_abi.h b/src/common/x64/xbyak_abi.h
new file mode 100644
index 0000000000..26e4bfda52
--- /dev/null
+++ b/src/common/x64/xbyak_abi.h
@@ -0,0 +1,229 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <bitset>
+#include <initializer_list>
+#include <xbyak.h>
+#include "common/assert.h"
+
+namespace Common::X64 {
+
+constexpr std::size_t RegToIndex(const Xbyak::Reg& reg) {
+ using Kind = Xbyak::Reg::Kind;
+ ASSERT_MSG((reg.getKind() & (Kind::REG | Kind::XMM)) != 0,
+ "RegSet only support GPRs and XMM registers.");
+ ASSERT_MSG(reg.getIdx() < 16, "RegSet only supports XXM0-15.");
+ return reg.getIdx() + (reg.getKind() == Kind::REG ? 0 : 16);
+}
+
+constexpr Xbyak::Reg64 IndexToReg64(std::size_t reg_index) {
+ ASSERT(reg_index < 16);
+ return Xbyak::Reg64(static_cast<int>(reg_index));
+}
+
+constexpr Xbyak::Xmm IndexToXmm(std::size_t reg_index) {
+ ASSERT(reg_index >= 16 && reg_index < 32);
+ return Xbyak::Xmm(static_cast<int>(reg_index - 16));
+}
+
+constexpr Xbyak::Reg IndexToReg(std::size_t reg_index) {
+ if (reg_index < 16) {
+ return IndexToReg64(reg_index);
+ } else {
+ return IndexToXmm(reg_index);
+ }
+}
+
+inline std::bitset<32> BuildRegSet(std::initializer_list<Xbyak::Reg> regs) {
+ std::bitset<32> bits;
+ for (const Xbyak::Reg& reg : regs) {
+ bits[RegToIndex(reg)] = true;
+ }
+ return bits;
+}
+
+constexpr inline std::bitset<32> ABI_ALL_GPRS(0x0000FFFF);
+constexpr inline std::bitset<32> ABI_ALL_XMMS(0xFFFF0000);
+
+#ifdef _WIN32
+
+// Microsoft x64 ABI
+constexpr inline Xbyak::Reg ABI_RETURN = Xbyak::util::rax;
+constexpr inline Xbyak::Reg ABI_PARAM1 = Xbyak::util::rcx;
+constexpr inline Xbyak::Reg ABI_PARAM2 = Xbyak::util::rdx;
+constexpr inline Xbyak::Reg ABI_PARAM3 = Xbyak::util::r8;
+constexpr inline Xbyak::Reg ABI_PARAM4 = Xbyak::util::r9;
+
+const std::bitset<32> ABI_ALL_CALLER_SAVED = BuildRegSet({
+ // GPRs
+ Xbyak::util::rcx,
+ Xbyak::util::rdx,
+ Xbyak::util::r8,
+ Xbyak::util::r9,
+ Xbyak::util::r10,
+ Xbyak::util::r11,
+ // XMMs
+ Xbyak::util::xmm0,
+ Xbyak::util::xmm1,
+ Xbyak::util::xmm2,
+ Xbyak::util::xmm3,
+ Xbyak::util::xmm4,
+ Xbyak::util::xmm5,
+});
+
+const std::bitset<32> ABI_ALL_CALLEE_SAVED = BuildRegSet({
+ // GPRs
+ Xbyak::util::rbx,
+ Xbyak::util::rsi,
+ Xbyak::util::rdi,
+ Xbyak::util::rbp,
+ Xbyak::util::r12,
+ Xbyak::util::r13,
+ Xbyak::util::r14,
+ Xbyak::util::r15,
+ // XMMs
+ Xbyak::util::xmm6,
+ Xbyak::util::xmm7,
+ Xbyak::util::xmm8,
+ Xbyak::util::xmm9,
+ Xbyak::util::xmm10,
+ Xbyak::util::xmm11,
+ Xbyak::util::xmm12,
+ Xbyak::util::xmm13,
+ Xbyak::util::xmm14,
+ Xbyak::util::xmm15,
+});
+
+constexpr size_t ABI_SHADOW_SPACE = 0x20;
+
+#else
+
+// System V x86-64 ABI
+constexpr inline Xbyak::Reg ABI_RETURN = Xbyak::util::rax;
+constexpr inline Xbyak::Reg ABI_PARAM1 = Xbyak::util::rdi;
+constexpr inline Xbyak::Reg ABI_PARAM2 = Xbyak::util::rsi;
+constexpr inline Xbyak::Reg ABI_PARAM3 = Xbyak::util::rdx;
+constexpr inline Xbyak::Reg ABI_PARAM4 = Xbyak::util::rcx;
+
+const std::bitset<32> ABI_ALL_CALLER_SAVED = BuildRegSet({
+ // GPRs
+ Xbyak::util::rcx,
+ Xbyak::util::rdx,
+ Xbyak::util::rdi,
+ Xbyak::util::rsi,
+ Xbyak::util::r8,
+ Xbyak::util::r9,
+ Xbyak::util::r10,
+ Xbyak::util::r11,
+ // XMMs
+ Xbyak::util::xmm0,
+ Xbyak::util::xmm1,
+ Xbyak::util::xmm2,
+ Xbyak::util::xmm3,
+ Xbyak::util::xmm4,
+ Xbyak::util::xmm5,
+ Xbyak::util::xmm6,
+ Xbyak::util::xmm7,
+ Xbyak::util::xmm8,
+ Xbyak::util::xmm9,
+ Xbyak::util::xmm10,
+ Xbyak::util::xmm11,
+ Xbyak::util::xmm12,
+ Xbyak::util::xmm13,
+ Xbyak::util::xmm14,
+ Xbyak::util::xmm15,
+});
+
+const std::bitset<32> ABI_ALL_CALLEE_SAVED = BuildRegSet({
+ // GPRs
+ Xbyak::util::rbx,
+ Xbyak::util::rbp,
+ Xbyak::util::r12,
+ Xbyak::util::r13,
+ Xbyak::util::r14,
+ Xbyak::util::r15,
+});
+
+constexpr size_t ABI_SHADOW_SPACE = 0;
+
+#endif
+
+struct ABIFrameInfo {
+ s32 subtraction;
+ s32 xmm_offset;
+};
+
+inline ABIFrameInfo ABI_CalculateFrameSize(std::bitset<32> regs, size_t rsp_alignment,
+ size_t needed_frame_size) {
+ const auto count = (regs & ABI_ALL_GPRS).count();
+ rsp_alignment -= count * 8;
+ size_t subtraction = 0;
+ const auto xmm_count = (regs & ABI_ALL_XMMS).count();
+ if (xmm_count) {
+ // If we have any XMMs to save, we must align the stack here.
+ subtraction = rsp_alignment & 0xF;
+ }
+ subtraction += 0x10 * xmm_count;
+ size_t xmm_base_subtraction = subtraction;
+ subtraction += needed_frame_size;
+ subtraction += ABI_SHADOW_SPACE;
+ // Final alignment.
+ rsp_alignment -= subtraction;
+ subtraction += rsp_alignment & 0xF;
+
+ return ABIFrameInfo{static_cast<s32>(subtraction),
+ static_cast<s32>(subtraction - xmm_base_subtraction)};
+}
+
+inline size_t ABI_PushRegistersAndAdjustStack(Xbyak::CodeGenerator& code, std::bitset<32> regs,
+ size_t rsp_alignment, size_t needed_frame_size = 0) {
+ auto frame_info = ABI_CalculateFrameSize(regs, rsp_alignment, needed_frame_size);
+
+ for (std::size_t i = 0; i < regs.size(); ++i) {
+ if (regs[i] && ABI_ALL_GPRS[i]) {
+ code.push(IndexToReg64(i));
+ }
+ }
+
+ if (frame_info.subtraction != 0) {
+ code.sub(code.rsp, frame_info.subtraction);
+ }
+
+ for (std::size_t i = 0; i < regs.size(); ++i) {
+ if (regs[i] && ABI_ALL_XMMS[i]) {
+ code.movaps(code.xword[code.rsp + frame_info.xmm_offset], IndexToXmm(i));
+ frame_info.xmm_offset += 0x10;
+ }
+ }
+
+ return ABI_SHADOW_SPACE;
+}
+
+inline void ABI_PopRegistersAndAdjustStack(Xbyak::CodeGenerator& code, std::bitset<32> regs,
+ size_t rsp_alignment, size_t needed_frame_size = 0) {
+ auto frame_info = ABI_CalculateFrameSize(regs, rsp_alignment, needed_frame_size);
+
+ for (std::size_t i = 0; i < regs.size(); ++i) {
+ if (regs[i] && ABI_ALL_XMMS[i]) {
+ code.movaps(IndexToXmm(i), code.xword[code.rsp + frame_info.xmm_offset]);
+ frame_info.xmm_offset += 0x10;
+ }
+ }
+
+ if (frame_info.subtraction != 0) {
+ code.add(code.rsp, frame_info.subtraction);
+ }
+
+ // GPRs need to be popped in reverse order
+ for (std::size_t j = 0; j < regs.size(); ++j) {
+ const std::size_t i = regs.size() - j - 1;
+ if (regs[i] && ABI_ALL_GPRS[i]) {
+ code.pop(IndexToReg64(i));
+ }
+ }
+}
+
+} // namespace Common::X64
diff --git a/src/common/x64/xbyak_util.h b/src/common/x64/xbyak_util.h
new file mode 100644
index 0000000000..df17f8cbe1
--- /dev/null
+++ b/src/common/x64/xbyak_util.h
@@ -0,0 +1,47 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <type_traits>
+#include <xbyak.h>
+#include "common/x64/xbyak_abi.h"
+
+namespace Common::X64 {
+
+// Constants for use with cmpps/cmpss
+enum {
+ CMP_EQ = 0,
+ CMP_LT = 1,
+ CMP_LE = 2,
+ CMP_UNORD = 3,
+ CMP_NEQ = 4,
+ CMP_NLT = 5,
+ CMP_NLE = 6,
+ CMP_ORD = 7,
+};
+
+constexpr bool IsWithin2G(uintptr_t ref, uintptr_t target) {
+ const u64 distance = target - (ref + 5);
+ return !(distance >= 0x8000'0000ULL && distance <= ~0x8000'0000ULL);
+}
+
+inline bool IsWithin2G(const Xbyak::CodeGenerator& code, uintptr_t target) {
+ return IsWithin2G(reinterpret_cast<uintptr_t>(code.getCurr()), target);
+}
+
+template <typename T>
+inline void CallFarFunction(Xbyak::CodeGenerator& code, const T f) {
+ static_assert(std::is_pointer_v<T>, "Argument must be a (function) pointer.");
+ size_t addr = reinterpret_cast<size_t>(f);
+ if (IsWithin2G(code, addr)) {
+ code.call(f);
+ } else {
+ // ABI_RETURN is a safe temp register to use before a call
+ code.mov(ABI_RETURN, addr);
+ code.call(ABI_RETURN);
+ }
+}
+
+} // namespace Common::X64
diff --git a/src/common/zstd_compression.cpp b/src/common/zstd_compression.cpp
index 9785264925..5f45459da0 100644
--- a/src/common/zstd_compression.cpp
+++ b/src/common/zstd_compression.cpp
@@ -5,7 +5,6 @@
#include <algorithm>
#include <zstd.h>
-#include "common/assert.h"
#include "common/zstd_compression.h"
namespace Common::Compression {
diff --git a/src/common/zstd_compression.h b/src/common/zstd_compression.h
index e9de941c80..c26a30ab9d 100644
--- a/src/common/zstd_compression.h
+++ b/src/common/zstd_compression.h
@@ -13,24 +13,25 @@ namespace Common::Compression {
/**
* Compresses a source memory region with Zstandard and returns the compressed data in a vector.
*
- * @param source the uncompressed source memory region.
- * @param source_size the size in bytes of the uncompressed source memory region.
- * @param compression_level the used compression level. Should be between 1 and 22.
+ * @param source The uncompressed source memory region.
+ * @param source_size The size of the uncompressed source memory region.
+ * @param compression_level The used compression level. Should be between 1 and 22.
*
* @return the compressed data.
*/
-std::vector<u8> CompressDataZSTD(const u8* source, std::size_t source_size, s32 compression_level);
+[[nodiscard]] std::vector<u8> CompressDataZSTD(const u8* source, std::size_t source_size,
+ s32 compression_level);
/**
* Compresses a source memory region with Zstandard with the default compression level and returns
* the compressed data in a vector.
*
- * @param source the uncompressed source memory region.
- * @param source_size the size in bytes of the uncompressed source memory region.
+ * @param source The uncompressed source memory region.
+ * @param source_size The size of the uncompressed source memory region.
*
* @return the compressed data.
*/
-std::vector<u8> CompressDataZSTDDefault(const u8* source, std::size_t source_size);
+[[nodiscard]] std::vector<u8> CompressDataZSTDDefault(const u8* source, std::size_t source_size);
/**
* 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
*
* @return the decompressed data.
*/
-std::vector<u8> DecompressDataZSTD(const std::vector<u8>& compressed);
+[[nodiscard]] std::vector<u8> DecompressDataZSTD(const std::vector<u8>& compressed);
} // namespace Common::Compression \ No newline at end of file