summaryrefslogtreecommitdiff
path: root/src/common
diff options
context:
space:
mode:
Diffstat (limited to 'src/common')
-rw-r--r--src/common/CMakeLists.txt50
-rw-r--r--src/common/address_space.cpp10
-rw-r--r--src/common/address_space.h150
-rw-r--r--src/common/address_space.inc366
-rw-r--r--src/common/algorithm.h13
-rw-r--r--src/common/alignment.h3
-rw-r--r--src/common/announce_multiplayer_room.h140
-rw-r--r--src/common/assert.cpp12
-rw-r--r--src/common/assert.h61
-rw-r--r--src/common/atomic_helpers.h775
-rw-r--r--src/common/atomic_ops.h100
-rw-r--r--src/common/bit_cast.h5
-rw-r--r--src/common/bit_field.h42
-rw-r--r--src/common/bit_set.h17
-rw-r--r--src/common/bit_util.h12
-rw-r--r--src/common/bounded_threadsafe_queue.h167
-rw-r--r--src/common/cityhash.cpp25
-rw-r--r--src/common/cityhash.h25
-rw-r--r--src/common/common_funcs.h15
-rw-r--r--src/common/common_types.h4
-rw-r--r--src/common/concepts.h5
-rw-r--r--src/common/detached_tasks.cpp9
-rw-r--r--src/common/detached_tasks.h5
-rw-r--r--src/common/div_ceil.h5
-rw-r--r--src/common/dynamic_library.cpp6
-rw-r--r--src/common/dynamic_library.h5
-rw-r--r--src/common/elf.h333
-rw-r--r--src/common/error.cpp6
-rw-r--r--src/common/error.h6
-rw-r--r--src/common/expected.h5
-rw-r--r--src/common/fiber.cpp31
-rw-r--r--src/common/fiber.h12
-rw-r--r--src/common/fixed_point.h706
-rw-r--r--src/common/fs/file.cpp6
-rw-r--r--src/common/fs/file.h7
-rw-r--r--src/common/fs/fs.cpp5
-rw-r--r--src/common/fs/fs.h5
-rw-r--r--src/common/fs/fs_paths.h5
-rw-r--r--src/common/fs/fs_types.h6
-rw-r--r--src/common/fs/fs_util.cpp13
-rw-r--r--src/common/fs/fs_util.h24
-rw-r--r--src/common/fs/path_util.cpp9
-rw-r--r--src/common/fs/path_util.h5
-rw-r--r--src/common/hash.h12
-rw-r--r--src/common/hex_util.cpp6
-rw-r--r--src/common/hex_util.h7
-rw-r--r--src/common/host_memory.cpp15
-rw-r--r--src/common/host_memory.h5
-rw-r--r--src/common/input.h67
-rw-r--r--src/common/intrusive_red_black_tree.h395
-rw-r--r--src/common/literals.h5
-rw-r--r--src/common/logging/backend.cpp27
-rw-r--r--src/common/logging/backend.h6
-rw-r--r--src/common/logging/filter.cpp10
-rw-r--r--src/common/logging/filter.h6
-rw-r--r--src/common/logging/formatter.h5
-rw-r--r--src/common/logging/log.h5
-rw-r--r--src/common/logging/log_entry.h5
-rw-r--r--src/common/logging/text_formatter.cpp7
-rw-r--r--src/common/logging/text_formatter.h6
-rw-r--r--src/common/logging/types.h10
-rw-r--r--src/common/lru_cache.h5
-rw-r--r--src/common/lz4_compression.cpp5
-rw-r--r--src/common/lz4_compression.h5
-rw-r--r--src/common/math_util.h56
-rw-r--r--src/common/memory_detect.cpp7
-rw-r--r--src/common/memory_detect.h5
-rw-r--r--src/common/microprofile.cpp5
-rw-r--r--src/common/microprofile.h14
-rw-r--r--src/common/microprofileui.h5
-rw-r--r--src/common/multi_level_page_table.cpp9
-rw-r--r--src/common/multi_level_page_table.h78
-rw-r--r--src/common/multi_level_page_table.inc84
-rw-r--r--src/common/nvidia_flags.cpp6
-rw-r--r--src/common/nvidia_flags.h5
-rw-r--r--src/common/page_table.cpp63
-rw-r--r--src/common/page_table.h33
-rw-r--r--src/common/param_package.cpp11
-rw-r--r--src/common/param_package.h5
-rw-r--r--src/common/parent_of_member.h8
-rw-r--r--src/common/point.h5
-rw-r--r--src/common/quaternion.h5
-rw-r--r--src/common/reader_writer_queue.h940
-rw-r--r--src/common/ring_buffer.h6
-rw-r--r--src/common/scm_rev.cpp.in5
-rw-r--r--src/common/scm_rev.h5
-rw-r--r--src/common/scope_exit.h5
-rw-r--r--src/common/settings.cpp19
-rw-r--r--src/common/settings.h465
-rw-r--r--src/common/settings_input.cpp5
-rw-r--r--src/common/settings_input.h6
-rw-r--r--src/common/socket_types.h51
-rw-r--r--src/common/spin_lock.cpp5
-rw-r--r--src/common/spin_lock.h5
-rw-r--r--src/common/stream.cpp5
-rw-r--r--src/common/stream.h5
-rw-r--r--src/common/string_util.cpp12
-rw-r--r--src/common/string_util.h8
-rw-r--r--src/common/swap.h16
-rw-r--r--src/common/telemetry.cpp66
-rw-r--r--src/common/telemetry.h14
-rw-r--r--src/common/thread.cpp18
-rw-r--r--src/common/thread.h13
-rw-r--r--src/common/thread_queue_list.h6
-rw-r--r--src/common/thread_worker.h5
-rw-r--r--src/common/threadsafe_queue.h11
-rw-r--r--src/common/time_zone.cpp5
-rw-r--r--src/common/time_zone.h5
-rw-r--r--src/common/tiny_mt.h5
-rw-r--r--src/common/tree.h653
-rw-r--r--src/common/uint128.h8
-rw-r--r--src/common/unique_function.h5
-rw-r--r--src/common/uuid.cpp5
-rw-r--r--src/common/uuid.h6
-rw-r--r--src/common/vector_math.h32
-rw-r--r--src/common/virtual_buffer.cpp5
-rw-r--r--src/common/virtual_buffer.h6
-rw-r--r--src/common/wall_clock.cpp9
-rw-r--r--src/common/wall_clock.h5
-rw-r--r--src/common/x64/cpu_detect.cpp138
-rw-r--r--src/common/x64/cpu_detect.h85
-rw-r--r--src/common/x64/native_clock.cpp69
-rw-r--r--src/common/x64/native_clock.h13
-rw-r--r--src/common/x64/xbyak_abi.h5
-rw-r--r--src/common/x64/xbyak_util.h5
-rw-r--r--src/common/zstd_compression.cpp5
-rw-r--r--src/common/zstd_compression.h5
127 files changed, 5496 insertions, 1503 deletions
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index adf70eb8b..a02696873 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -1,3 +1,6 @@
1# SPDX-FileCopyrightText: 2018 yuzu Emulator Project
2# SPDX-License-Identifier: GPL-2.0-or-later
3
1if (DEFINED ENV{AZURECIREPO}) 4if (DEFINED ENV{AZURECIREPO})
2 set(BUILD_REPOSITORY $ENV{AZURECIREPO}) 5 set(BUILD_REPOSITORY $ENV{AZURECIREPO})
3endif() 6endif()
@@ -11,38 +14,17 @@ if (DEFINED ENV{DISPLAYVERSION})
11 set(DISPLAY_VERSION $ENV{DISPLAYVERSION}) 14 set(DISPLAY_VERSION $ENV{DISPLAYVERSION})
12endif () 15endif ()
13 16
14# Pass the path to git to the GenerateSCMRev.cmake as well 17include(GenerateSCMRev)
15find_package(Git QUIET)
16
17add_custom_command(OUTPUT scm_rev.cpp
18 COMMAND ${CMAKE_COMMAND}
19 -DSRC_DIR=${CMAKE_SOURCE_DIR}
20 -DBUILD_REPOSITORY=${BUILD_REPOSITORY}
21 -DTITLE_BAR_FORMAT_IDLE=${TITLE_BAR_FORMAT_IDLE}
22 -DTITLE_BAR_FORMAT_RUNNING=${TITLE_BAR_FORMAT_RUNNING}
23 -DBUILD_TAG=${BUILD_TAG}
24 -DBUILD_ID=${DISPLAY_VERSION}
25 -DGIT_REF_SPEC=${GIT_REF_SPEC}
26 -DGIT_REV=${GIT_REV}
27 -DGIT_DESC=${GIT_DESC}
28 -DGIT_BRANCH=${GIT_BRANCH}
29 -DBUILD_FULLNAME=${BUILD_FULLNAME}
30 -DGIT_EXECUTABLE=${GIT_EXECUTABLE}
31 -P ${CMAKE_SOURCE_DIR}/CMakeModules/GenerateSCMRev.cmake
32 DEPENDS
33 # Check that the scm_rev files haven't changed
34 "${CMAKE_CURRENT_SOURCE_DIR}/scm_rev.cpp.in"
35 "${CMAKE_CURRENT_SOURCE_DIR}/scm_rev.h"
36 # technically we should regenerate if the git version changed, but its not worth the effort imo
37 "${CMAKE_SOURCE_DIR}/CMakeModules/GenerateSCMRev.cmake"
38 VERBATIM
39)
40 18
41add_library(common STATIC 19add_library(common STATIC
20 address_space.cpp
21 address_space.h
42 algorithm.h 22 algorithm.h
43 alignment.h 23 alignment.h
24 announce_multiplayer_room.h
44 assert.cpp 25 assert.cpp
45 assert.h 26 assert.h
27 atomic_helpers.h
46 atomic_ops.h 28 atomic_ops.h
47 detached_tasks.cpp 29 detached_tasks.cpp
48 detached_tasks.h 30 detached_tasks.h
@@ -58,11 +40,13 @@ add_library(common STATIC
58 div_ceil.h 40 div_ceil.h
59 dynamic_library.cpp 41 dynamic_library.cpp
60 dynamic_library.h 42 dynamic_library.h
43 elf.h
61 error.cpp 44 error.cpp
62 error.h 45 error.h
63 expected.h 46 expected.h
64 fiber.cpp 47 fiber.cpp
65 fiber.h 48 fiber.h
49 fixed_point.h
66 fs/file.cpp 50 fs/file.cpp
67 fs/file.h 51 fs/file.h
68 fs/fs.cpp 52 fs/fs.cpp
@@ -99,6 +83,8 @@ add_library(common STATIC
99 microprofile.cpp 83 microprofile.cpp
100 microprofile.h 84 microprofile.h
101 microprofileui.h 85 microprofileui.h
86 multi_level_page_table.cpp
87 multi_level_page_table.h
102 nvidia_flags.cpp 88 nvidia_flags.cpp
103 nvidia_flags.h 89 nvidia_flags.h
104 page_table.cpp 90 page_table.cpp
@@ -108,14 +94,16 @@ add_library(common STATIC
108 parent_of_member.h 94 parent_of_member.h
109 point.h 95 point.h
110 quaternion.h 96 quaternion.h
97 reader_writer_queue.h
111 ring_buffer.h 98 ring_buffer.h
112 scm_rev.cpp 99 ${CMAKE_CURRENT_BINARY_DIR}/scm_rev.cpp
113 scm_rev.h 100 scm_rev.h
114 scope_exit.h 101 scope_exit.h
115 settings.cpp 102 settings.cpp
116 settings.h 103 settings.h
117 settings_input.cpp 104 settings_input.cpp
118 settings_input.h 105 settings_input.h
106 socket_types.h
119 spin_lock.cpp 107 spin_lock.cpp
120 spin_lock.h 108 spin_lock.h
121 stream.cpp 109 stream.cpp
@@ -157,6 +145,7 @@ if(ARCHITECTURE_x86_64)
157 x64/xbyak_abi.h 145 x64/xbyak_abi.h
158 x64/xbyak_util.h 146 x64/xbyak_util.h
159 ) 147 )
148 target_link_libraries(common PRIVATE xbyak)
160endif() 149endif()
161 150
162if (MSVC) 151if (MSVC)
@@ -180,9 +169,10 @@ endif()
180create_target_directory_groups(common) 169create_target_directory_groups(common)
181 170
182target_link_libraries(common PUBLIC ${Boost_LIBRARIES} fmt::fmt microprofile Threads::Threads) 171target_link_libraries(common PUBLIC ${Boost_LIBRARIES} fmt::fmt microprofile Threads::Threads)
183target_link_libraries(common PRIVATE lz4::lz4 xbyak) 172target_link_libraries(common PRIVATE lz4::lz4)
184if (MSVC) 173if (TARGET zstd::zstd)
185 target_link_libraries(common PRIVATE zstd::zstd) 174 target_link_libraries(common PRIVATE zstd::zstd)
186else() 175else()
187 target_link_libraries(common PRIVATE zstd) 176 target_link_libraries(common PRIVATE
177 $<IF:$<TARGET_EXISTS:zstd::libzstd_shared>,zstd::libzstd_shared,zstd::libzstd_static>)
188endif() 178endif()
diff --git a/src/common/address_space.cpp b/src/common/address_space.cpp
new file mode 100644
index 000000000..866e78dbe
--- /dev/null
+++ b/src/common/address_space.cpp
@@ -0,0 +1,10 @@
1// SPDX-FileCopyrightText: 2021 Skyline Team and Contributors
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include "common/address_space.inc"
5
6namespace Common {
7
8template class Common::FlatAllocator<u32, 0, 32>;
9
10}
diff --git a/src/common/address_space.h b/src/common/address_space.h
new file mode 100644
index 000000000..9222b2fdc
--- /dev/null
+++ b/src/common/address_space.h
@@ -0,0 +1,150 @@
1// SPDX-FileCopyrightText: 2021 Skyline Team and Contributors
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include <concepts>
7#include <functional>
8#include <mutex>
9#include <vector>
10
11#include "common/common_types.h"
12
13namespace Common {
14template <typename VaType, size_t AddressSpaceBits>
15concept AddressSpaceValid = std::is_unsigned_v<VaType> && sizeof(VaType) * 8 >= AddressSpaceBits;
16
17struct EmptyStruct {};
18
19/**
20 * @brief FlatAddressSpaceMap provides a generic VA->PA mapping implementation using a sorted vector
21 */
22template <typename VaType, VaType UnmappedVa, typename PaType, PaType UnmappedPa,
23 bool PaContigSplit, size_t AddressSpaceBits, typename ExtraBlockInfo = EmptyStruct>
24requires AddressSpaceValid<VaType, AddressSpaceBits>
25class FlatAddressSpaceMap {
26public:
27 /// The maximum VA that this AS can technically reach
28 static constexpr VaType VaMaximum{(1ULL << (AddressSpaceBits - 1)) +
29 ((1ULL << (AddressSpaceBits - 1)) - 1)};
30
31 explicit FlatAddressSpaceMap(VaType va_limit,
32 std::function<void(VaType, VaType)> unmap_callback = {});
33
34 FlatAddressSpaceMap() = default;
35
36 void Map(VaType virt, PaType phys, VaType size, ExtraBlockInfo extra_info = {}) {
37 std::scoped_lock lock(block_mutex);
38 MapLocked(virt, phys, size, extra_info);
39 }
40
41 void Unmap(VaType virt, VaType size) {
42 std::scoped_lock lock(block_mutex);
43 UnmapLocked(virt, size);
44 }
45
46 VaType GetVALimit() const {
47 return va_limit;
48 }
49
50protected:
51 /**
52 * @brief Represents a block of memory in the AS, the physical mapping is contiguous until
53 * another block with a different phys address is hit
54 */
55 struct Block {
56 /// VA of the block
57 VaType virt{UnmappedVa};
58 /// PA of the block, will increase 1-1 with VA until a new block is encountered
59 PaType phys{UnmappedPa};
60 [[no_unique_address]] ExtraBlockInfo extra_info;
61
62 Block() = default;
63
64 Block(VaType virt_, PaType phys_, ExtraBlockInfo extra_info_)
65 : virt(virt_), phys(phys_), extra_info(extra_info_) {}
66
67 bool Valid() const {
68 return virt != UnmappedVa;
69 }
70
71 bool Mapped() const {
72 return phys != UnmappedPa;
73 }
74
75 bool Unmapped() const {
76 return phys == UnmappedPa;
77 }
78
79 bool operator<(const VaType& p_virt) const {
80 return virt < p_virt;
81 }
82 };
83
84 /**
85 * @brief Maps a PA range into the given AS region
86 * @note block_mutex MUST be locked when calling this
87 */
88 void MapLocked(VaType virt, PaType phys, VaType size, ExtraBlockInfo extra_info);
89
90 /**
91 * @brief Unmaps the given range and merges it with other unmapped regions
92 * @note block_mutex MUST be locked when calling this
93 */
94 void UnmapLocked(VaType virt, VaType size);
95
96 std::mutex block_mutex;
97 std::vector<Block> blocks{Block{}};
98
99 /// a soft limit on the maximum VA of the AS
100 VaType va_limit{VaMaximum};
101
102private:
103 /// Callback called when the mappings in an region have changed
104 std::function<void(VaType, VaType)> unmap_callback{};
105};
106
107/**
108 * @brief FlatMemoryManager specialises FlatAddressSpaceMap to work as an allocator, with an
109 * initial, fast linear pass and a subsequent slower pass that iterates until it finds a free block
110 */
111template <typename VaType, VaType UnmappedVa, size_t AddressSpaceBits>
112requires AddressSpaceValid<VaType, AddressSpaceBits>
113class FlatAllocator
114 : public FlatAddressSpaceMap<VaType, UnmappedVa, bool, false, false, AddressSpaceBits> {
115private:
116 using Base = FlatAddressSpaceMap<VaType, UnmappedVa, bool, false, false, AddressSpaceBits>;
117
118public:
119 explicit FlatAllocator(VaType virt_start, VaType va_limit = Base::VaMaximum);
120
121 /**
122 * @brief Allocates a region in the AS of the given size and returns its address
123 */
124 VaType Allocate(VaType size);
125
126 /**
127 * @brief Marks the given region in the AS as allocated
128 */
129 void AllocateFixed(VaType virt, VaType size);
130
131 /**
132 * @brief Frees an AS region so it can be used again
133 */
134 void Free(VaType virt, VaType size);
135
136 VaType GetVAStart() const {
137 return virt_start;
138 }
139
140private:
141 /// The base VA of the allocator, no allocations will be below this
142 VaType virt_start;
143
144 /**
145 * The end address for the initial linear allocation pass
146 * Once this reaches the AS limit the slower allocation path will be used
147 */
148 VaType current_linear_alloc_end;
149};
150} // namespace Common
diff --git a/src/common/address_space.inc b/src/common/address_space.inc
new file mode 100644
index 000000000..2195dabd5
--- /dev/null
+++ b/src/common/address_space.inc
@@ -0,0 +1,366 @@
1// SPDX-FileCopyrightText: 2021 Skyline Team and Contributors
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include "common/address_space.h"
5#include "common/assert.h"
6
7#define MAP_MEMBER(returnType) \
8 template <typename VaType, VaType UnmappedVa, typename PaType, PaType UnmappedPa, \
9 bool PaContigSplit, size_t AddressSpaceBits, typename ExtraBlockInfo> \
10 requires AddressSpaceValid<VaType, AddressSpaceBits> returnType FlatAddressSpaceMap< \
11 VaType, UnmappedVa, PaType, UnmappedPa, PaContigSplit, AddressSpaceBits, ExtraBlockInfo>
12#define MAP_MEMBER_CONST() \
13 template <typename VaType, VaType UnmappedVa, typename PaType, PaType UnmappedPa, \
14 bool PaContigSplit, size_t AddressSpaceBits, typename ExtraBlockInfo> \
15 requires AddressSpaceValid<VaType, AddressSpaceBits> FlatAddressSpaceMap< \
16 VaType, UnmappedVa, PaType, UnmappedPa, PaContigSplit, AddressSpaceBits, ExtraBlockInfo>
17
18#define MM_MEMBER(returnType) \
19 template <typename VaType, VaType UnmappedVa, size_t AddressSpaceBits> \
20 requires AddressSpaceValid<VaType, AddressSpaceBits> returnType \
21 FlatMemoryManager<VaType, UnmappedVa, AddressSpaceBits>
22
23#define ALLOC_MEMBER(returnType) \
24 template <typename VaType, VaType UnmappedVa, size_t AddressSpaceBits> \
25 requires AddressSpaceValid<VaType, AddressSpaceBits> returnType \
26 FlatAllocator<VaType, UnmappedVa, AddressSpaceBits>
27#define ALLOC_MEMBER_CONST() \
28 template <typename VaType, VaType UnmappedVa, size_t AddressSpaceBits> \
29 requires AddressSpaceValid<VaType, AddressSpaceBits> \
30 FlatAllocator<VaType, UnmappedVa, AddressSpaceBits>
31
32namespace Common {
33MAP_MEMBER_CONST()::FlatAddressSpaceMap(VaType va_limit_,
34 std::function<void(VaType, VaType)> unmap_callback_)
35 : va_limit{va_limit_}, unmap_callback{std::move(unmap_callback_)} {
36 if (va_limit > VaMaximum) {
37 ASSERT_MSG(false, "Invalid VA limit!");
38 }
39}
40
41MAP_MEMBER(void)::MapLocked(VaType virt, PaType phys, VaType size, ExtraBlockInfo extra_info) {
42 VaType virt_end{virt + size};
43
44 if (virt_end > va_limit) {
45 ASSERT_MSG(false,
46 "Trying to map a block past the VA limit: virt_end: 0x{:X}, va_limit: 0x{:X}",
47 virt_end, va_limit);
48 }
49
50 auto block_end_successor{std::lower_bound(blocks.begin(), blocks.end(), virt_end)};
51 if (block_end_successor == blocks.begin()) {
52 ASSERT_MSG(false, "Trying to map a block before the VA start: virt_end: 0x{:X}", virt_end);
53 }
54
55 auto block_end_predecessor{std::prev(block_end_successor)};
56
57 if (block_end_successor != blocks.end()) {
58 // We have blocks in front of us, if one is directly in front then we don't have to add a
59 // tail
60 if (block_end_successor->virt != virt_end) {
61 PaType tailPhys{[&]() -> PaType {
62 if constexpr (!PaContigSplit) {
63 // Always propagate unmapped regions rather than calculating offset
64 return block_end_predecessor->phys;
65 } else {
66 if (block_end_predecessor->Unmapped()) {
67 // Always propagate unmapped regions rather than calculating offset
68 return block_end_predecessor->phys;
69 } else {
70 return block_end_predecessor->phys + virt_end - block_end_predecessor->virt;
71 }
72 }
73 }()};
74
75 if (block_end_predecessor->virt >= virt) {
76 // If this block's start would be overlapped by the map then reuse it as a tail
77 // block
78 block_end_predecessor->virt = virt_end;
79 block_end_predecessor->phys = tailPhys;
80 block_end_predecessor->extra_info = block_end_predecessor->extra_info;
81
82 // No longer predecessor anymore
83 block_end_successor = block_end_predecessor--;
84 } else {
85 // Else insert a new one and we're done
86 blocks.insert(block_end_successor,
87 {Block(virt, phys, extra_info),
88 Block(virt_end, tailPhys, block_end_predecessor->extra_info)});
89 if (unmap_callback) {
90 unmap_callback(virt, size);
91 }
92
93 return;
94 }
95 }
96 } else {
97 // block_end_predecessor will always be unmapped as blocks has to be terminated by an
98 // unmapped chunk
99 if (block_end_predecessor != blocks.begin() && block_end_predecessor->virt >= virt) {
100 // Move the unmapped block start backwards
101 block_end_predecessor->virt = virt_end;
102
103 // No longer predecessor anymore
104 block_end_successor = block_end_predecessor--;
105 } else {
106 // Else insert a new one and we're done
107 blocks.insert(block_end_successor,
108 {Block(virt, phys, extra_info), Block(virt_end, UnmappedPa, {})});
109 if (unmap_callback) {
110 unmap_callback(virt, size);
111 }
112
113 return;
114 }
115 }
116
117 auto block_start_successor{block_end_successor};
118
119 // Walk the block vector to find the start successor as this is more efficient than another
120 // binary search in most scenarios
121 while (std::prev(block_start_successor)->virt >= virt) {
122 block_start_successor--;
123 }
124
125 // Check that the start successor is either the end block or something in between
126 if (block_start_successor->virt > virt_end) {
127 ASSERT_MSG(false, "Unsorted block in AS map: virt: 0x{:X}", block_start_successor->virt);
128 } else if (block_start_successor->virt == virt_end) {
129 // We need to create a new block as there are none spare that we would overwrite
130 blocks.insert(block_start_successor, Block(virt, phys, extra_info));
131 } else {
132 // Erase overwritten blocks
133 if (auto eraseStart{std::next(block_start_successor)}; eraseStart != block_end_successor) {
134 blocks.erase(eraseStart, block_end_successor);
135 }
136
137 // Reuse a block that would otherwise be overwritten as a start block
138 block_start_successor->virt = virt;
139 block_start_successor->phys = phys;
140 block_start_successor->extra_info = extra_info;
141 }
142
143 if (unmap_callback) {
144 unmap_callback(virt, size);
145 }
146}
147
148MAP_MEMBER(void)::UnmapLocked(VaType virt, VaType size) {
149 VaType virt_end{virt + size};
150
151 if (virt_end > va_limit) {
152 ASSERT_MSG(false,
153 "Trying to map a block past the VA limit: virt_end: 0x{:X}, va_limit: 0x{:X}",
154 virt_end, va_limit);
155 }
156
157 auto block_end_successor{std::lower_bound(blocks.begin(), blocks.end(), virt_end)};
158 if (block_end_successor == blocks.begin()) {
159 ASSERT_MSG(false, "Trying to unmap a block before the VA start: virt_end: 0x{:X}",
160 virt_end);
161 }
162
163 auto block_end_predecessor{std::prev(block_end_successor)};
164
165 auto walk_back_to_predecessor{[&](auto iter) {
166 while (iter->virt >= virt) {
167 iter--;
168 }
169
170 return iter;
171 }};
172
173 auto erase_blocks_with_end_unmapped{[&](auto unmappedEnd) {
174 auto block_start_predecessor{walk_back_to_predecessor(unmappedEnd)};
175 auto block_start_successor{std::next(block_start_predecessor)};
176
177 auto eraseEnd{[&]() {
178 if (block_start_predecessor->Unmapped()) {
179 // If the start predecessor is unmapped then we can erase everything in our region
180 // and be done
181 return std::next(unmappedEnd);
182 } else {
183 // Else reuse the end predecessor as the start of our unmapped region then erase all
184 // up to it
185 unmappedEnd->virt = virt;
186 return unmappedEnd;
187 }
188 }()};
189
190 // We can't have two unmapped regions after each other
191 if (eraseEnd != blocks.end() &&
192 (eraseEnd == block_start_successor ||
193 (block_start_predecessor->Unmapped() && eraseEnd->Unmapped()))) {
194 ASSERT_MSG(false, "Multiple contiguous unmapped regions are unsupported!");
195 }
196
197 blocks.erase(block_start_successor, eraseEnd);
198 }};
199
200 // We can avoid any splitting logic if these are the case
201 if (block_end_predecessor->Unmapped()) {
202 if (block_end_predecessor->virt > virt) {
203 erase_blocks_with_end_unmapped(block_end_predecessor);
204 }
205
206 if (unmap_callback) {
207 unmap_callback(virt, size);
208 }
209
210 return; // The region is unmapped, bail out early
211 } else if (block_end_successor->virt == virt_end && block_end_successor->Unmapped()) {
212 erase_blocks_with_end_unmapped(block_end_successor);
213
214 if (unmap_callback) {
215 unmap_callback(virt, size);
216 }
217
218 return; // The region is unmapped here and doesn't need splitting, bail out early
219 } else if (block_end_successor == blocks.end()) {
220 // This should never happen as the end should always follow an unmapped block
221 ASSERT_MSG(false, "Unexpected Memory Manager state!");
222 } else if (block_end_successor->virt != virt_end) {
223 // If one block is directly in front then we don't have to add a tail
224
225 // The previous block is mapped so we will need to add a tail with an offset
226 PaType tailPhys{[&]() {
227 if constexpr (PaContigSplit) {
228 return block_end_predecessor->phys + virt_end - block_end_predecessor->virt;
229 } else {
230 return block_end_predecessor->phys;
231 }
232 }()};
233
234 if (block_end_predecessor->virt >= virt) {
235 // If this block's start would be overlapped by the unmap then reuse it as a tail block
236 block_end_predecessor->virt = virt_end;
237 block_end_predecessor->phys = tailPhys;
238
239 // No longer predecessor anymore
240 block_end_successor = block_end_predecessor--;
241 } else {
242 blocks.insert(block_end_successor,
243 {Block(virt, UnmappedPa, {}),
244 Block(virt_end, tailPhys, block_end_predecessor->extra_info)});
245 if (unmap_callback) {
246 unmap_callback(virt, size);
247 }
248
249 // The previous block is mapped and ends before
250 return;
251 }
252 }
253
254 // Walk the block vector to find the start predecessor as this is more efficient than another
255 // binary search in most scenarios
256 auto block_start_predecessor{walk_back_to_predecessor(block_end_successor)};
257 auto block_start_successor{std::next(block_start_predecessor)};
258
259 if (block_start_successor->virt > virt_end) {
260 ASSERT_MSG(false, "Unsorted block in AS map: virt: 0x{:X}", block_start_successor->virt);
261 } else if (block_start_successor->virt == virt_end) {
262 // There are no blocks between the start and the end that would let us skip inserting a new
263 // one for head
264
265 // The previous block is may be unmapped, if so we don't need to insert any unmaps after it
266 if (block_start_predecessor->Mapped()) {
267 blocks.insert(block_start_successor, Block(virt, UnmappedPa, {}));
268 }
269 } else if (block_start_predecessor->Unmapped()) {
270 // If the previous block is unmapped
271 blocks.erase(block_start_successor, block_end_predecessor);
272 } else {
273 // Erase overwritten blocks, skipping the first one as we have written the unmapped start
274 // block there
275 if (auto eraseStart{std::next(block_start_successor)}; eraseStart != block_end_successor) {
276 blocks.erase(eraseStart, block_end_successor);
277 }
278
279 // Add in the unmapped block header
280 block_start_successor->virt = virt;
281 block_start_successor->phys = UnmappedPa;
282 }
283
284 if (unmap_callback)
285 unmap_callback(virt, size);
286}
287
288ALLOC_MEMBER_CONST()::FlatAllocator(VaType virt_start_, VaType va_limit_)
289 : Base{va_limit_}, virt_start{virt_start_}, current_linear_alloc_end{virt_start_} {}
290
291ALLOC_MEMBER(VaType)::Allocate(VaType size) {
292 std::scoped_lock lock(this->block_mutex);
293
294 VaType alloc_start{UnmappedVa};
295 VaType alloc_end{current_linear_alloc_end + size};
296
297 // Avoid searching backwards in the address space if possible
298 if (alloc_end >= current_linear_alloc_end && alloc_end <= this->va_limit) {
299 auto alloc_end_successor{
300 std::lower_bound(this->blocks.begin(), this->blocks.end(), alloc_end)};
301 if (alloc_end_successor == this->blocks.begin()) {
302 ASSERT_MSG(false, "First block in AS map is invalid!");
303 }
304
305 auto alloc_end_predecessor{std::prev(alloc_end_successor)};
306 if (alloc_end_predecessor->virt <= current_linear_alloc_end) {
307 alloc_start = current_linear_alloc_end;
308 } else {
309 // Skip over fixed any mappings in front of us
310 while (alloc_end_successor != this->blocks.end()) {
311 if (alloc_end_successor->virt - alloc_end_predecessor->virt < size ||
312 alloc_end_predecessor->Mapped()) {
313 alloc_start = alloc_end_predecessor->virt;
314 break;
315 }
316
317 alloc_end_predecessor = alloc_end_successor++;
318
319 // Use the VA limit to calculate if we can fit in the final block since it has no
320 // successor
321 if (alloc_end_successor == this->blocks.end()) {
322 alloc_end = alloc_end_predecessor->virt + size;
323
324 if (alloc_end >= alloc_end_predecessor->virt && alloc_end <= this->va_limit) {
325 alloc_start = alloc_end_predecessor->virt;
326 }
327 }
328 }
329 }
330 }
331
332 if (alloc_start != UnmappedVa) {
333 current_linear_alloc_end = alloc_start + size;
334 } else { // If linear allocation overflows the AS then find a gap
335 if (this->blocks.size() <= 2) {
336 ASSERT_MSG(false, "Unexpected allocator state!");
337 }
338
339 auto search_predecessor{this->blocks.begin()};
340 auto search_successor{std::next(search_predecessor)};
341
342 while (search_successor != this->blocks.end() &&
343 (search_successor->virt - search_predecessor->virt < size ||
344 search_predecessor->Mapped())) {
345 search_predecessor = search_successor++;
346 }
347
348 if (search_successor != this->blocks.end()) {
349 alloc_start = search_predecessor->virt;
350 } else {
351 return {}; // AS is full
352 }
353 }
354
355 this->MapLocked(alloc_start, true, size, {});
356 return alloc_start;
357}
358
359ALLOC_MEMBER(void)::AllocateFixed(VaType virt, VaType size) {
360 this->Map(virt, true, size);
361}
362
363ALLOC_MEMBER(void)::Free(VaType virt, VaType size) {
364 this->Unmap(virt, size);
365}
366} // namespace Common
diff --git a/src/common/algorithm.h b/src/common/algorithm.h
index 4804a3421..c27c9241d 100644
--- a/src/common/algorithm.h
+++ b/src/common/algorithm.h
@@ -1,6 +1,5 @@
1// Copyright 2019 yuzu emulator team 1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#pragma once 4#pragma once
6 5
@@ -25,4 +24,12 @@ template <class ForwardIt, class T, class Compare = std::less<>>
25 return first != last && !comp(value, *first) ? first : last; 24 return first != last && !comp(value, *first) ? first : last;
26} 25}
27 26
27template <typename T, typename Func, typename... Args>
28T FoldRight(T initial_value, Func&& func, Args&&... args) {
29 T value{initial_value};
30 const auto high_func = [&value, &func]<typename U>(U x) { value = func(value, x); };
31 (std::invoke(high_func, std::forward<Args>(args)), ...);
32 return value;
33}
34
28} // namespace Common 35} // namespace Common
diff --git a/src/common/alignment.h b/src/common/alignment.h
index 8570c7d3c..7e897334b 100644
--- a/src/common/alignment.h
+++ b/src/common/alignment.h
@@ -1,4 +1,5 @@
1// This file is under the public domain. 1// SPDX-FileCopyrightText: 2014 Jannik Vogel <email@jannikvogel.de>
2// SPDX-License-Identifier: CC0-1.0
2 3
3#pragma once 4#pragma once
4 5
diff --git a/src/common/announce_multiplayer_room.h b/src/common/announce_multiplayer_room.h
new file mode 100644
index 000000000..4a3100fa4
--- /dev/null
+++ b/src/common/announce_multiplayer_room.h
@@ -0,0 +1,140 @@
1// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <array>
7#include <functional>
8#include <string>
9#include <vector>
10#include "common/common_types.h"
11#include "common/socket_types.h"
12#include "web_service/web_result.h"
13
14namespace AnnounceMultiplayerRoom {
15
16struct GameInfo {
17 std::string name{""};
18 u64 id{0};
19 std::string version{""};
20};
21
22struct Member {
23 std::string username;
24 std::string nickname;
25 std::string display_name;
26 std::string avatar_url;
27 Network::IPv4Address fake_ip;
28 GameInfo game;
29};
30
31struct RoomInformation {
32 std::string name; ///< Name of the server
33 std::string description; ///< Server description
34 u32 member_slots; ///< Maximum number of members in this room
35 u16 port; ///< The port of this room
36 GameInfo preferred_game; ///< Game to advertise that you want to play
37 std::string host_username; ///< Forum username of the host
38 bool enable_yuzu_mods; ///< Allow yuzu Moderators to moderate on this room
39};
40
41struct Room {
42 RoomInformation information;
43
44 std::string id;
45 std::string verify_uid; ///< UID used for verification
46 std::string ip;
47 u32 net_version;
48 bool has_password;
49
50 std::vector<Member> members;
51};
52using RoomList = std::vector<Room>;
53
54/**
55 * A AnnounceMultiplayerRoom interface class. A backend to submit/get to/from a web service should
56 * implement this interface.
57 */
58class Backend {
59public:
60 virtual ~Backend() = default;
61
62 /**
63 * Sets the Information that gets used for the announce
64 * @param uid The Id of the room
65 * @param name The name of the room
66 * @param description The room description
67 * @param port The port of the room
68 * @param net_version The version of the libNetwork that gets used
69 * @param has_password True if the room is passowrd protected
70 * @param preferred_game The preferred game of the room
71 * @param preferred_game_id The title id of the preferred game
72 */
73 virtual void SetRoomInformation(const std::string& name, const std::string& description,
74 const u16 port, const u32 max_player, const u32 net_version,
75 const bool has_password, const GameInfo& preferred_game) = 0;
76 /**
77 * Adds a player information to the data that gets announced
78 * @param member The player to add
79 */
80 virtual void AddPlayer(const Member& member) = 0;
81
82 /**
83 * Updates the data in the announce service. Re-register the room when required.
84 * @result The result of the update attempt
85 */
86 virtual WebService::WebResult Update() = 0;
87
88 /**
89 * Registers the data in the announce service
90 * @result The result of the register attempt. When the result code is Success, A global Guid of
91 * the room which may be used for verification will be in the result's returned_data.
92 */
93 virtual WebService::WebResult Register() = 0;
94
95 /**
96 * Empties the stored players
97 */
98 virtual void ClearPlayers() = 0;
99
100 /**
101 * Get the room information from the announce service
102 * @result A list of all rooms the announce service has
103 */
104 virtual RoomList GetRoomList() = 0;
105
106 /**
107 * Sends a delete message to the announce service
108 */
109 virtual void Delete() = 0;
110};
111
112/**
113 * Empty implementation of AnnounceMultiplayerRoom interface that drops all data. Used when a
114 * functional backend implementation is not available.
115 */
116class NullBackend : public Backend {
117public:
118 ~NullBackend() = default;
119 void SetRoomInformation(const std::string& /*name*/, const std::string& /*description*/,
120 const u16 /*port*/, const u32 /*max_player*/, const u32 /*net_version*/,
121 const bool /*has_password*/,
122 const GameInfo& /*preferred_game*/) override {}
123 void AddPlayer(const Member& /*member*/) override {}
124 WebService::WebResult Update() override {
125 return WebService::WebResult{WebService::WebResult::Code::NoWebservice,
126 "WebService is missing", ""};
127 }
128 WebService::WebResult Register() override {
129 return WebService::WebResult{WebService::WebResult::Code::NoWebservice,
130 "WebService is missing", ""};
131 }
132 void ClearPlayers() override {}
133 RoomList GetRoomList() override {
134 return RoomList{};
135 }
136
137 void Delete() override {}
138};
139
140} // namespace AnnounceMultiplayerRoom
diff --git a/src/common/assert.cpp b/src/common/assert.cpp
index 72f1121aa..6026b7dc2 100644
--- a/src/common/assert.cpp
+++ b/src/common/assert.cpp
@@ -1,14 +1,18 @@
1// Copyright 2021 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#include "common/assert.h" 4#include "common/assert.h"
6#include "common/common_funcs.h" 5#include "common/common_funcs.h"
7 6
8#include "common/settings.h" 7#include "common/settings.h"
9 8
10void assert_handle_failure() { 9void assert_fail_impl() {
11 if (Settings::values.use_debug_asserts) { 10 if (Settings::values.use_debug_asserts) {
12 Crash(); 11 Crash();
13 } 12 }
14} 13}
14
15[[noreturn]] void unreachable_impl() {
16 Crash();
17 throw std::runtime_error("Unreachable code");
18}
diff --git a/src/common/assert.h b/src/common/assert.h
index 33060d865..8c927fcc0 100644
--- a/src/common/assert.h
+++ b/src/common/assert.h
@@ -1,6 +1,6 @@
1// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project 1// SPDX-FileCopyrightText: 2013 Dolphin Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-FileCopyrightText: 2014 Citra Emulator Project
3// Refer to the license.txt file included. 3// SPDX-License-Identifier: GPL-2.0-or-later
4 4
5#pragma once 5#pragma once
6 6
@@ -9,44 +9,43 @@
9// Sometimes we want to try to continue even after hitting an assert. 9// Sometimes we want to try to continue even after hitting an assert.
10// However touching this file yields a global recompilation as this header is included almost 10// However touching this file yields a global recompilation as this header is included almost
11// everywhere. So let's just move the handling of the failed assert to a single cpp file. 11// everywhere. So let's just move the handling of the failed assert to a single cpp file.
12void assert_handle_failure();
13 12
14// For asserts we'd like to keep all the junk executed when an assert happens away from the 13void assert_fail_impl();
15// important code in the function. One way of doing this is to put all the relevant code inside a 14[[noreturn]] void unreachable_impl();
16// lambda and force the compiler to not inline it. Unfortunately, MSVC seems to have no syntax to 15
17// specify __declspec on lambda functions, so what we do instead is define a noinline wrapper 16#ifdef _MSC_VER
18// template that calls the lambda. This seems to generate an extra instruction at the call-site 17#define YUZU_NO_INLINE __declspec(noinline)
19// compared to the ideal implementation (which wouldn't support ASSERT_MSG parameters), but is good 18#else
20// enough for our purposes. 19#define YUZU_NO_INLINE __attribute__((noinline))
21template <typename Fn>
22#if defined(_MSC_VER)
23[[msvc::noinline]]
24#elif defined(__GNUC__)
25[[gnu::cold, gnu::noinline]]
26#endif 20#endif
27static void
28assert_noinline_call(const Fn& fn) {
29 fn();
30 assert_handle_failure();
31}
32 21
33#define ASSERT(_a_) \ 22#define ASSERT(_a_) \
34 do \ 23 ([&]() YUZU_NO_INLINE { \
35 if (!(_a_)) { \ 24 if (!(_a_)) [[unlikely]] { \
36 assert_noinline_call([] { LOG_CRITICAL(Debug, "Assertion Failed!"); }); \ 25 LOG_CRITICAL(Debug, "Assertion Failed!"); \
26 assert_fail_impl(); \
37 } \ 27 } \
38 while (0) 28 }())
39 29
40#define ASSERT_MSG(_a_, ...) \ 30#define ASSERT_MSG(_a_, ...) \
41 do \ 31 ([&]() YUZU_NO_INLINE { \
42 if (!(_a_)) { \ 32 if (!(_a_)) [[unlikely]] { \
43 assert_noinline_call([&] { LOG_CRITICAL(Debug, "Assertion Failed!\n" __VA_ARGS__); }); \ 33 LOG_CRITICAL(Debug, "Assertion Failed!\n" __VA_ARGS__); \
34 assert_fail_impl(); \
44 } \ 35 } \
45 while (0) 36 }())
37
38#define UNREACHABLE() \
39 do { \
40 LOG_CRITICAL(Debug, "Unreachable code!"); \
41 unreachable_impl(); \
42 } while (0)
46 43
47#define UNREACHABLE() assert_noinline_call([] { LOG_CRITICAL(Debug, "Unreachable code!"); })
48#define UNREACHABLE_MSG(...) \ 44#define UNREACHABLE_MSG(...) \
49 assert_noinline_call([&] { LOG_CRITICAL(Debug, "Unreachable code!\n" __VA_ARGS__); }) 45 do { \
46 LOG_CRITICAL(Debug, "Unreachable code!\n" __VA_ARGS__); \
47 unreachable_impl(); \
48 } while (0)
50 49
51#ifdef _DEBUG 50#ifdef _DEBUG
52#define DEBUG_ASSERT(_a_) ASSERT(_a_) 51#define DEBUG_ASSERT(_a_) ASSERT(_a_)
diff --git a/src/common/atomic_helpers.h b/src/common/atomic_helpers.h
new file mode 100644
index 000000000..bef5015c1
--- /dev/null
+++ b/src/common/atomic_helpers.h
@@ -0,0 +1,775 @@
1// SPDX-FileCopyrightText: 2013-2016 Cameron Desrochers
2// SPDX-FileCopyrightText: 2015 Jeff Preshing
3// SPDX-License-Identifier: BSD-2-Clause AND Zlib
4
5// Distributed under the simplified BSD license (see the license file that
6// should have come with this header).
7// Uses Jeff Preshing's semaphore implementation (under the terms of its
8// separate zlib license, embedded below).
9
10#pragma once
11
12// Provides portable (VC++2010+, Intel ICC 13, GCC 4.7+, and anything C++11 compliant)
13// implementation of low-level memory barriers, plus a few semi-portable utility macros (for
14// inlining and alignment). Also has a basic atomic type (limited to hardware-supported atomics with
15// no memory ordering guarantees). Uses the AE_* prefix for macros (historical reasons), and the
16// "moodycamel" namespace for symbols.
17
18#include <cassert>
19#include <cerrno>
20#include <cstdint>
21#include <ctime>
22#include <type_traits>
23
24// Platform detection
25#if defined(__INTEL_COMPILER)
26#define AE_ICC
27#elif defined(_MSC_VER)
28#define AE_VCPP
29#elif defined(__GNUC__)
30#define AE_GCC
31#endif
32
33#if defined(_M_IA64) || defined(__ia64__)
34#define AE_ARCH_IA64
35#elif defined(_WIN64) || defined(__amd64__) || defined(_M_X64) || defined(__x86_64__)
36#define AE_ARCH_X64
37#elif defined(_M_IX86) || defined(__i386__)
38#define AE_ARCH_X86
39#elif defined(_M_PPC) || defined(__powerpc__)
40#define AE_ARCH_PPC
41#else
42#define AE_ARCH_UNKNOWN
43#endif
44
45// AE_UNUSED
46#define AE_UNUSED(x) ((void)x)
47
48// AE_NO_TSAN/AE_TSAN_ANNOTATE_*
49#if defined(__has_feature)
50#if __has_feature(thread_sanitizer)
51#if __cplusplus >= 201703L // inline variables require C++17
52namespace Common {
53inline int ae_tsan_global;
54}
55#define AE_TSAN_ANNOTATE_RELEASE() \
56 AnnotateHappensBefore(__FILE__, __LINE__, (void*)(&::moodycamel::ae_tsan_global))
57#define AE_TSAN_ANNOTATE_ACQUIRE() \
58 AnnotateHappensAfter(__FILE__, __LINE__, (void*)(&::moodycamel::ae_tsan_global))
59extern "C" void AnnotateHappensBefore(const char*, int, void*);
60extern "C" void AnnotateHappensAfter(const char*, int, void*);
61#else // when we can't work with tsan, attempt to disable its warnings
62#define AE_NO_TSAN __attribute__((no_sanitize("thread")))
63#endif
64#endif
65#endif
66#ifndef AE_NO_TSAN
67#define AE_NO_TSAN
68#endif
69#ifndef AE_TSAN_ANNOTATE_RELEASE
70#define AE_TSAN_ANNOTATE_RELEASE()
71#define AE_TSAN_ANNOTATE_ACQUIRE()
72#endif
73
74// AE_FORCEINLINE
75#if defined(AE_VCPP) || defined(AE_ICC)
76#define AE_FORCEINLINE __forceinline
77#elif defined(AE_GCC)
78//#define AE_FORCEINLINE __attribute__((always_inline))
79#define AE_FORCEINLINE inline
80#else
81#define AE_FORCEINLINE inline
82#endif
83
84// AE_ALIGN
85#if defined(AE_VCPP) || defined(AE_ICC)
86#define AE_ALIGN(x) __declspec(align(x))
87#elif defined(AE_GCC)
88#define AE_ALIGN(x) __attribute__((aligned(x)))
89#else
90// Assume GCC compliant syntax...
91#define AE_ALIGN(x) __attribute__((aligned(x)))
92#endif
93
94// Portable atomic fences implemented below:
95
96namespace Common {
97
98enum memory_order {
99 memory_order_relaxed,
100 memory_order_acquire,
101 memory_order_release,
102 memory_order_acq_rel,
103 memory_order_seq_cst,
104
105 // memory_order_sync: Forces a full sync:
106 // #LoadLoad, #LoadStore, #StoreStore, and most significantly, #StoreLoad
107 memory_order_sync = memory_order_seq_cst
108};
109
110} // namespace Common
111
112#if (defined(AE_VCPP) && (_MSC_VER < 1700 || defined(__cplusplus_cli))) || \
113 (defined(AE_ICC) && __INTEL_COMPILER < 1600)
114// VS2010 and ICC13 don't support std::atomic_*_fence, implement our own fences
115
116#include <intrin.h>
117
118#if defined(AE_ARCH_X64) || defined(AE_ARCH_X86)
119#define AeFullSync _mm_mfence
120#define AeLiteSync _mm_mfence
121#elif defined(AE_ARCH_IA64)
122#define AeFullSync __mf
123#define AeLiteSync __mf
124#elif defined(AE_ARCH_PPC)
125#include <ppcintrinsics.h>
126#define AeFullSync __sync
127#define AeLiteSync __lwsync
128#endif
129
130#ifdef AE_VCPP
131#pragma warning(push)
132#pragma warning(disable : 4365) // Disable erroneous 'conversion from long to unsigned int,
133 // signed/unsigned mismatch' error when using `assert`
134#ifdef __cplusplus_cli
135#pragma managed(push, off)
136#endif
137#endif
138
139namespace Common {
140
141AE_FORCEINLINE void compiler_fence(memory_order order) AE_NO_TSAN {
142 switch (order) {
143 case memory_order_relaxed:
144 break;
145 case memory_order_acquire:
146 _ReadBarrier();
147 break;
148 case memory_order_release:
149 _WriteBarrier();
150 break;
151 case memory_order_acq_rel:
152 _ReadWriteBarrier();
153 break;
154 case memory_order_seq_cst:
155 _ReadWriteBarrier();
156 break;
157 default:
158 assert(false);
159 }
160}
161
162// x86/x64 have a strong memory model -- all loads and stores have
163// acquire and release semantics automatically (so only need compiler
164// barriers for those).
165#if defined(AE_ARCH_X86) || defined(AE_ARCH_X64)
166AE_FORCEINLINE void fence(memory_order order) AE_NO_TSAN {
167 switch (order) {
168 case memory_order_relaxed:
169 break;
170 case memory_order_acquire:
171 _ReadBarrier();
172 break;
173 case memory_order_release:
174 _WriteBarrier();
175 break;
176 case memory_order_acq_rel:
177 _ReadWriteBarrier();
178 break;
179 case memory_order_seq_cst:
180 _ReadWriteBarrier();
181 AeFullSync();
182 _ReadWriteBarrier();
183 break;
184 default:
185 assert(false);
186 }
187}
188#else
189AE_FORCEINLINE void fence(memory_order order) AE_NO_TSAN {
190 // Non-specialized arch, use heavier memory barriers everywhere just in case :-(
191 switch (order) {
192 case memory_order_relaxed:
193 break;
194 case memory_order_acquire:
195 _ReadBarrier();
196 AeLiteSync();
197 _ReadBarrier();
198 break;
199 case memory_order_release:
200 _WriteBarrier();
201 AeLiteSync();
202 _WriteBarrier();
203 break;
204 case memory_order_acq_rel:
205 _ReadWriteBarrier();
206 AeLiteSync();
207 _ReadWriteBarrier();
208 break;
209 case memory_order_seq_cst:
210 _ReadWriteBarrier();
211 AeFullSync();
212 _ReadWriteBarrier();
213 break;
214 default:
215 assert(false);
216 }
217}
218#endif
219} // namespace Common
220#else
221// Use standard library of atomics
222#include <atomic>
223
224namespace Common {
225
226AE_FORCEINLINE void compiler_fence(memory_order order) AE_NO_TSAN {
227 switch (order) {
228 case memory_order_relaxed:
229 break;
230 case memory_order_acquire:
231 std::atomic_signal_fence(std::memory_order_acquire);
232 break;
233 case memory_order_release:
234 std::atomic_signal_fence(std::memory_order_release);
235 break;
236 case memory_order_acq_rel:
237 std::atomic_signal_fence(std::memory_order_acq_rel);
238 break;
239 case memory_order_seq_cst:
240 std::atomic_signal_fence(std::memory_order_seq_cst);
241 break;
242 default:
243 assert(false);
244 }
245}
246
247AE_FORCEINLINE void fence(memory_order order) AE_NO_TSAN {
248 switch (order) {
249 case memory_order_relaxed:
250 break;
251 case memory_order_acquire:
252 AE_TSAN_ANNOTATE_ACQUIRE();
253 std::atomic_thread_fence(std::memory_order_acquire);
254 break;
255 case memory_order_release:
256 AE_TSAN_ANNOTATE_RELEASE();
257 std::atomic_thread_fence(std::memory_order_release);
258 break;
259 case memory_order_acq_rel:
260 AE_TSAN_ANNOTATE_ACQUIRE();
261 AE_TSAN_ANNOTATE_RELEASE();
262 std::atomic_thread_fence(std::memory_order_acq_rel);
263 break;
264 case memory_order_seq_cst:
265 AE_TSAN_ANNOTATE_ACQUIRE();
266 AE_TSAN_ANNOTATE_RELEASE();
267 std::atomic_thread_fence(std::memory_order_seq_cst);
268 break;
269 default:
270 assert(false);
271 }
272}
273
274} // namespace Common
275
276#endif
277
278#if !defined(AE_VCPP) || (_MSC_VER >= 1700 && !defined(__cplusplus_cli))
279#define AE_USE_STD_ATOMIC_FOR_WEAK_ATOMIC
280#endif
281
282#ifdef AE_USE_STD_ATOMIC_FOR_WEAK_ATOMIC
283#include <atomic>
284#endif
285#include <utility>
286
287// WARNING: *NOT* A REPLACEMENT FOR std::atomic. READ CAREFULLY:
288// Provides basic support for atomic variables -- no memory ordering guarantees are provided.
289// The guarantee of atomicity is only made for types that already have atomic load and store
290// guarantees at the hardware level -- on most platforms this generally means aligned pointers and
291// integers (only).
292namespace Common {
293template <typename T>
294class weak_atomic {
295public:
296 AE_NO_TSAN weak_atomic() : value() {}
297#ifdef AE_VCPP
298#pragma warning(push)
299#pragma warning(disable : 4100) // Get rid of (erroneous) 'unreferenced formal parameter' warning
300#endif
301 template <typename U>
302 AE_NO_TSAN weak_atomic(U&& x) : value(std::forward<U>(x)) {}
303#ifdef __cplusplus_cli
304 // Work around bug with universal reference/nullptr combination that only appears when /clr is
305 // on
306 AE_NO_TSAN weak_atomic(nullptr_t) : value(nullptr) {}
307#endif
308 AE_NO_TSAN weak_atomic(weak_atomic const& other) : value(other.load()) {}
309 AE_NO_TSAN weak_atomic(weak_atomic&& other) : value(std::move(other.load())) {}
310#ifdef AE_VCPP
311#pragma warning(pop)
312#endif
313
314 AE_FORCEINLINE operator T() const AE_NO_TSAN {
315 return load();
316 }
317
318#ifndef AE_USE_STD_ATOMIC_FOR_WEAK_ATOMIC
319 template <typename U>
320 AE_FORCEINLINE weak_atomic const& operator=(U&& x) AE_NO_TSAN {
321 value = std::forward<U>(x);
322 return *this;
323 }
324 AE_FORCEINLINE weak_atomic const& operator=(weak_atomic const& other) AE_NO_TSAN {
325 value = other.value;
326 return *this;
327 }
328
329 AE_FORCEINLINE T load() const AE_NO_TSAN {
330 return value;
331 }
332
333 AE_FORCEINLINE T fetch_add_acquire(T increment) AE_NO_TSAN {
334#if defined(AE_ARCH_X64) || defined(AE_ARCH_X86)
335 if (sizeof(T) == 4)
336 return _InterlockedExchangeAdd((long volatile*)&value, (long)increment);
337#if defined(_M_AMD64)
338 else if (sizeof(T) == 8)
339 return _InterlockedExchangeAdd64((long long volatile*)&value, (long long)increment);
340#endif
341#else
342#error Unsupported platform
343#endif
344 assert(false && "T must be either a 32 or 64 bit type");
345 return value;
346 }
347
348 AE_FORCEINLINE T fetch_add_release(T increment) AE_NO_TSAN {
349#if defined(AE_ARCH_X64) || defined(AE_ARCH_X86)
350 if (sizeof(T) == 4)
351 return _InterlockedExchangeAdd((long volatile*)&value, (long)increment);
352#if defined(_M_AMD64)
353 else if (sizeof(T) == 8)
354 return _InterlockedExchangeAdd64((long long volatile*)&value, (long long)increment);
355#endif
356#else
357#error Unsupported platform
358#endif
359 assert(false && "T must be either a 32 or 64 bit type");
360 return value;
361 }
362#else
363 template <typename U>
364 AE_FORCEINLINE weak_atomic const& operator=(U&& x) AE_NO_TSAN {
365 value.store(std::forward<U>(x), std::memory_order_relaxed);
366 return *this;
367 }
368
369 AE_FORCEINLINE weak_atomic const& operator=(weak_atomic const& other) AE_NO_TSAN {
370 value.store(other.value.load(std::memory_order_relaxed), std::memory_order_relaxed);
371 return *this;
372 }
373
374 AE_FORCEINLINE T load() const AE_NO_TSAN {
375 return value.load(std::memory_order_relaxed);
376 }
377
378 AE_FORCEINLINE T fetch_add_acquire(T increment) AE_NO_TSAN {
379 return value.fetch_add(increment, std::memory_order_acquire);
380 }
381
382 AE_FORCEINLINE T fetch_add_release(T increment) AE_NO_TSAN {
383 return value.fetch_add(increment, std::memory_order_release);
384 }
385#endif
386
387private:
388#ifndef AE_USE_STD_ATOMIC_FOR_WEAK_ATOMIC
389 // No std::atomic support, but still need to circumvent compiler optimizations.
390 // `volatile` will make memory access slow, but is guaranteed to be reliable.
391 volatile T value;
392#else
393 std::atomic<T> value;
394#endif
395};
396
397} // namespace Common
398
399// Portable single-producer, single-consumer semaphore below:
400
401#if defined(_WIN32)
402// Avoid including windows.h in a header; we only need a handful of
403// items, so we'll redeclare them here (this is relatively safe since
404// the API generally has to remain stable between Windows versions).
405// I know this is an ugly hack but it still beats polluting the global
406// namespace with thousands of generic names or adding a .cpp for nothing.
407extern "C" {
408struct _SECURITY_ATTRIBUTES;
409__declspec(dllimport) void* __stdcall CreateSemaphoreW(_SECURITY_ATTRIBUTES* lpSemaphoreAttributes,
410 long lInitialCount, long lMaximumCount,
411 const wchar_t* lpName);
412__declspec(dllimport) int __stdcall CloseHandle(void* hObject);
413__declspec(dllimport) unsigned long __stdcall WaitForSingleObject(void* hHandle,
414 unsigned long dwMilliseconds);
415__declspec(dllimport) int __stdcall ReleaseSemaphore(void* hSemaphore, long lReleaseCount,
416 long* lpPreviousCount);
417}
418#elif defined(__MACH__)
419#include <mach/mach.h>
420#elif defined(__unix__)
421#include <semaphore.h>
422#elif defined(FREERTOS)
423#include <FreeRTOS.h>
424#include <semphr.h>
425#include <task.h>
426#endif
427
428namespace Common {
429// Code in the spsc_sema namespace below is an adaptation of Jeff Preshing's
430// portable + lightweight semaphore implementations, originally from
431// https://github.com/preshing/cpp11-on-multicore/blob/master/common/sema.h
432// LICENSE:
433// Copyright (c) 2015 Jeff Preshing
434//
435// This software is provided 'as-is', without any express or implied
436// warranty. In no event will the authors be held liable for any damages
437// arising from the use of this software.
438//
439// Permission is granted to anyone to use this software for any purpose,
440// including commercial applications, and to alter it and redistribute it
441// freely, subject to the following restrictions:
442//
443// 1. The origin of this software must not be misrepresented; you must not
444// claim that you wrote the original software. If you use this software
445// in a product, an acknowledgement in the product documentation would be
446// appreciated but is not required.
447// 2. Altered source versions must be plainly marked as such, and must not be
448// misrepresented as being the original software.
449// 3. This notice may not be removed or altered from any source distribution.
450namespace spsc_sema {
451#if defined(_WIN32)
452class Semaphore {
453private:
454 void* m_hSema;
455
456 Semaphore(const Semaphore& other);
457 Semaphore& operator=(const Semaphore& other);
458
459public:
460 AE_NO_TSAN Semaphore(int initialCount = 0) : m_hSema() {
461 assert(initialCount >= 0);
462 const long maxLong = 0x7fffffff;
463 m_hSema = CreateSemaphoreW(nullptr, initialCount, maxLong, nullptr);
464 assert(m_hSema);
465 }
466
467 AE_NO_TSAN ~Semaphore() {
468 CloseHandle(m_hSema);
469 }
470
471 bool wait() AE_NO_TSAN {
472 const unsigned long infinite = 0xffffffff;
473 return WaitForSingleObject(m_hSema, infinite) == 0;
474 }
475
476 bool try_wait() AE_NO_TSAN {
477 return WaitForSingleObject(m_hSema, 0) == 0;
478 }
479
480 bool timed_wait(std::uint64_t usecs) AE_NO_TSAN {
481 return WaitForSingleObject(m_hSema, (unsigned long)(usecs / 1000)) == 0;
482 }
483
484 void signal(int count = 1) AE_NO_TSAN {
485 while (!ReleaseSemaphore(m_hSema, count, nullptr))
486 ;
487 }
488};
489#elif defined(__MACH__)
490//---------------------------------------------------------
491// Semaphore (Apple iOS and OSX)
492// Can't use POSIX semaphores due to
493// http://lists.apple.com/archives/darwin-kernel/2009/Apr/msg00010.html
494//---------------------------------------------------------
495class Semaphore {
496private:
497 semaphore_t m_sema;
498
499 Semaphore(const Semaphore& other);
500 Semaphore& operator=(const Semaphore& other);
501
502public:
503 AE_NO_TSAN Semaphore(int initialCount = 0) : m_sema() {
504 assert(initialCount >= 0);
505 kern_return_t rc =
506 semaphore_create(mach_task_self(), &m_sema, SYNC_POLICY_FIFO, initialCount);
507 assert(rc == KERN_SUCCESS);
508 AE_UNUSED(rc);
509 }
510
511 AE_NO_TSAN ~Semaphore() {
512 semaphore_destroy(mach_task_self(), m_sema);
513 }
514
515 bool wait() AE_NO_TSAN {
516 return semaphore_wait(m_sema) == KERN_SUCCESS;
517 }
518
519 bool try_wait() AE_NO_TSAN {
520 return timed_wait(0);
521 }
522
523 bool timed_wait(std::uint64_t timeout_usecs) AE_NO_TSAN {
524 mach_timespec_t ts;
525 ts.tv_sec = static_cast<unsigned int>(timeout_usecs / 1000000);
526 ts.tv_nsec = static_cast<int>((timeout_usecs % 1000000) * 1000);
527
528 // added in OSX 10.10:
529 // https://developer.apple.com/library/prerelease/mac/documentation/General/Reference/APIDiffsMacOSX10_10SeedDiff/modules/Darwin.html
530 kern_return_t rc = semaphore_timedwait(m_sema, ts);
531 return rc == KERN_SUCCESS;
532 }
533
534 void signal() AE_NO_TSAN {
535 while (semaphore_signal(m_sema) != KERN_SUCCESS)
536 ;
537 }
538
539 void signal(int count) AE_NO_TSAN {
540 while (count-- > 0) {
541 while (semaphore_signal(m_sema) != KERN_SUCCESS)
542 ;
543 }
544 }
545};
546#elif defined(__unix__)
547//---------------------------------------------------------
548// Semaphore (POSIX, Linux)
549//---------------------------------------------------------
550class Semaphore {
551private:
552 sem_t m_sema;
553
554 Semaphore(const Semaphore& other);
555 Semaphore& operator=(const Semaphore& other);
556
557public:
558 AE_NO_TSAN Semaphore(int initialCount = 0) : m_sema() {
559 assert(initialCount >= 0);
560 int rc = sem_init(&m_sema, 0, static_cast<unsigned int>(initialCount));
561 assert(rc == 0);
562 AE_UNUSED(rc);
563 }
564
565 AE_NO_TSAN ~Semaphore() {
566 sem_destroy(&m_sema);
567 }
568
569 bool wait() AE_NO_TSAN {
570 // http://stackoverflow.com/questions/2013181/gdb-causes-sem-wait-to-fail-with-eintr-error
571 int rc;
572 do {
573 rc = sem_wait(&m_sema);
574 } while (rc == -1 && errno == EINTR);
575 return rc == 0;
576 }
577
578 bool try_wait() AE_NO_TSAN {
579 int rc;
580 do {
581 rc = sem_trywait(&m_sema);
582 } while (rc == -1 && errno == EINTR);
583 return rc == 0;
584 }
585
586 bool timed_wait(std::uint64_t usecs) AE_NO_TSAN {
587 struct timespec ts;
588 const int usecs_in_1_sec = 1000000;
589 const int nsecs_in_1_sec = 1000000000;
590 clock_gettime(CLOCK_REALTIME, &ts);
591 ts.tv_sec += static_cast<time_t>(usecs / usecs_in_1_sec);
592 ts.tv_nsec += static_cast<long>(usecs % usecs_in_1_sec) * 1000;
593 // sem_timedwait bombs if you have more than 1e9 in tv_nsec
594 // so we have to clean things up before passing it in
595 if (ts.tv_nsec >= nsecs_in_1_sec) {
596 ts.tv_nsec -= nsecs_in_1_sec;
597 ++ts.tv_sec;
598 }
599
600 int rc;
601 do {
602 rc = sem_timedwait(&m_sema, &ts);
603 } while (rc == -1 && errno == EINTR);
604 return rc == 0;
605 }
606
607 void signal() AE_NO_TSAN {
608 while (sem_post(&m_sema) == -1)
609 ;
610 }
611
612 void signal(int count) AE_NO_TSAN {
613 while (count-- > 0) {
614 while (sem_post(&m_sema) == -1)
615 ;
616 }
617 }
618};
619#elif defined(FREERTOS)
620//---------------------------------------------------------
621// Semaphore (FreeRTOS)
622//---------------------------------------------------------
623class Semaphore {
624private:
625 SemaphoreHandle_t m_sema;
626
627 Semaphore(const Semaphore& other);
628 Semaphore& operator=(const Semaphore& other);
629
630public:
631 AE_NO_TSAN Semaphore(int initialCount = 0) : m_sema() {
632 assert(initialCount >= 0);
633 m_sema = xSemaphoreCreateCounting(static_cast<UBaseType_t>(~0ull),
634 static_cast<UBaseType_t>(initialCount));
635 assert(m_sema);
636 }
637
638 AE_NO_TSAN ~Semaphore() {
639 vSemaphoreDelete(m_sema);
640 }
641
642 bool wait() AE_NO_TSAN {
643 return xSemaphoreTake(m_sema, portMAX_DELAY) == pdTRUE;
644 }
645
646 bool try_wait() AE_NO_TSAN {
647 // Note: In an ISR context, if this causes a task to unblock,
648 // the caller won't know about it
649 if (xPortIsInsideInterrupt())
650 return xSemaphoreTakeFromISR(m_sema, NULL) == pdTRUE;
651 return xSemaphoreTake(m_sema, 0) == pdTRUE;
652 }
653
654 bool timed_wait(std::uint64_t usecs) AE_NO_TSAN {
655 std::uint64_t msecs = usecs / 1000;
656 TickType_t ticks = static_cast<TickType_t>(msecs / portTICK_PERIOD_MS);
657 if (ticks == 0)
658 return try_wait();
659 return xSemaphoreTake(m_sema, ticks) == pdTRUE;
660 }
661
662 void signal() AE_NO_TSAN {
663 // Note: In an ISR context, if this causes a task to unblock,
664 // the caller won't know about it
665 BaseType_t rc;
666 if (xPortIsInsideInterrupt())
667 rc = xSemaphoreGiveFromISR(m_sema, NULL);
668 else
669 rc = xSemaphoreGive(m_sema);
670 assert(rc == pdTRUE);
671 AE_UNUSED(rc);
672 }
673
674 void signal(int count) AE_NO_TSAN {
675 while (count-- > 0)
676 signal();
677 }
678};
679#else
680#error Unsupported platform! (No semaphore wrapper available)
681#endif
682
683//---------------------------------------------------------
684// LightweightSemaphore
685//---------------------------------------------------------
686class LightweightSemaphore {
687public:
688 typedef std::make_signed<std::size_t>::type ssize_t;
689
690private:
691 weak_atomic<ssize_t> m_count;
692 Semaphore m_sema;
693
694 bool waitWithPartialSpinning(std::int64_t timeout_usecs = -1) AE_NO_TSAN {
695 ssize_t oldCount;
696 // Is there a better way to set the initial spin count?
697 // If we lower it to 1000, testBenaphore becomes 15x slower on my Core i7-5930K Windows PC,
698 // as threads start hitting the kernel semaphore.
699 int spin = 1024;
700 while (--spin >= 0) {
701 if (m_count.load() > 0) {
702 m_count.fetch_add_acquire(-1);
703 return true;
704 }
705 compiler_fence(memory_order_acquire); // Prevent the compiler from collapsing the loop.
706 }
707 oldCount = m_count.fetch_add_acquire(-1);
708 if (oldCount > 0)
709 return true;
710 if (timeout_usecs < 0) {
711 if (m_sema.wait())
712 return true;
713 }
714 if (timeout_usecs > 0 && m_sema.timed_wait(static_cast<uint64_t>(timeout_usecs)))
715 return true;
716 // At this point, we've timed out waiting for the semaphore, but the
717 // count is still decremented indicating we may still be waiting on
718 // it. So we have to re-adjust the count, but only if the semaphore
719 // wasn't signaled enough times for us too since then. If it was, we
720 // need to release the semaphore too.
721 while (true) {
722 oldCount = m_count.fetch_add_release(1);
723 if (oldCount < 0)
724 return false; // successfully restored things to the way they were
725 // Oh, the producer thread just signaled the semaphore after all. Try again:
726 oldCount = m_count.fetch_add_acquire(-1);
727 if (oldCount > 0 && m_sema.try_wait())
728 return true;
729 }
730 }
731
732public:
733 AE_NO_TSAN LightweightSemaphore(ssize_t initialCount = 0) : m_count(initialCount), m_sema() {
734 assert(initialCount >= 0);
735 }
736
737 bool tryWait() AE_NO_TSAN {
738 if (m_count.load() > 0) {
739 m_count.fetch_add_acquire(-1);
740 return true;
741 }
742 return false;
743 }
744
745 bool wait() AE_NO_TSAN {
746 return tryWait() || waitWithPartialSpinning();
747 }
748
749 bool wait(std::int64_t timeout_usecs) AE_NO_TSAN {
750 return tryWait() || waitWithPartialSpinning(timeout_usecs);
751 }
752
753 void signal(ssize_t count = 1) AE_NO_TSAN {
754 assert(count >= 0);
755 ssize_t oldCount = m_count.fetch_add_release(count);
756 assert(oldCount >= -1);
757 if (oldCount < 0) {
758 m_sema.signal(1);
759 }
760 }
761
762 std::size_t availableApprox() const AE_NO_TSAN {
763 ssize_t count = m_count.load();
764 return count > 0 ? static_cast<std::size_t>(count) : 0;
765 }
766};
767} // namespace spsc_sema
768} // namespace Common
769
770#if defined(AE_VCPP) && (_MSC_VER < 1700 || defined(__cplusplus_cli))
771#pragma warning(pop)
772#ifdef __cplusplus_cli
773#pragma managed(pop)
774#endif
775#endif
diff --git a/src/common/atomic_ops.h b/src/common/atomic_ops.h
index 2b1f515e8..c18bb33c4 100644
--- a/src/common/atomic_ops.h
+++ b/src/common/atomic_ops.h
@@ -1,16 +1,14 @@
1// Copyright 2020 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#pragma once 4#pragma once
6 5
7#include <cstring>
8#include <memory>
9
10#include "common/common_types.h" 6#include "common/common_types.h"
11 7
12#if _MSC_VER 8#if _MSC_VER
13#include <intrin.h> 9#include <intrin.h>
10#else
11#include <cstring>
14#endif 12#endif
15 13
16namespace Common { 14namespace Common {
@@ -47,6 +45,50 @@ namespace Common {
47 reinterpret_cast<__int64*>(expected.data())) != 0; 45 reinterpret_cast<__int64*>(expected.data())) != 0;
48} 46}
49 47
48[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected,
49 u8& actual) {
50 actual =
51 _InterlockedCompareExchange8(reinterpret_cast<volatile char*>(pointer), value, expected);
52 return actual == expected;
53}
54
55[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u16* pointer, u16 value, u16 expected,
56 u16& actual) {
57 actual =
58 _InterlockedCompareExchange16(reinterpret_cast<volatile short*>(pointer), value, expected);
59 return actual == expected;
60}
61
62[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u32* pointer, u32 value, u32 expected,
63 u32& actual) {
64 actual =
65 _InterlockedCompareExchange(reinterpret_cast<volatile long*>(pointer), value, expected);
66 return actual == expected;
67}
68
69[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u64* pointer, u64 value, u64 expected,
70 u64& actual) {
71 actual = _InterlockedCompareExchange64(reinterpret_cast<volatile __int64*>(pointer), value,
72 expected);
73 return actual == expected;
74}
75
76[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u64* pointer, u128 value, u128 expected,
77 u128& actual) {
78 const bool result =
79 _InterlockedCompareExchange128(reinterpret_cast<volatile __int64*>(pointer), value[1],
80 value[0], reinterpret_cast<__int64*>(expected.data())) != 0;
81 actual = expected;
82 return result;
83}
84
85[[nodiscard]] inline u128 AtomicLoad128(volatile u64* pointer) {
86 u128 result{};
87 _InterlockedCompareExchange128(reinterpret_cast<volatile __int64*>(pointer), result[1],
88 result[0], reinterpret_cast<__int64*>(result.data()));
89 return result;
90}
91
50#else 92#else
51 93
52[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected) { 94[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected) {
@@ -73,6 +115,52 @@ namespace Common {
73 return __sync_bool_compare_and_swap((unsigned __int128*)pointer, expected_a, value_a); 115 return __sync_bool_compare_and_swap((unsigned __int128*)pointer, expected_a, value_a);
74} 116}
75 117
118[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected,
119 u8& actual) {
120 actual = __sync_val_compare_and_swap(pointer, expected, value);
121 return actual == expected;
122}
123
124[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u16* pointer, u16 value, u16 expected,
125 u16& actual) {
126 actual = __sync_val_compare_and_swap(pointer, expected, value);
127 return actual == expected;
128}
129
130[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u32* pointer, u32 value, u32 expected,
131 u32& actual) {
132 actual = __sync_val_compare_and_swap(pointer, expected, value);
133 return actual == expected;
134}
135
136[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u64* pointer, u64 value, u64 expected,
137 u64& actual) {
138 actual = __sync_val_compare_and_swap(pointer, expected, value);
139 return actual == expected;
140}
141
142[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u64* pointer, u128 value, u128 expected,
143 u128& actual) {
144 unsigned __int128 value_a;
145 unsigned __int128 expected_a;
146 unsigned __int128 actual_a;
147 std::memcpy(&value_a, value.data(), sizeof(u128));
148 std::memcpy(&expected_a, expected.data(), sizeof(u128));
149 actual_a = __sync_val_compare_and_swap((unsigned __int128*)pointer, expected_a, value_a);
150 std::memcpy(actual.data(), &actual_a, sizeof(u128));
151 return actual_a == expected_a;
152}
153
154[[nodiscard]] inline u128 AtomicLoad128(volatile u64* pointer) {
155 unsigned __int128 zeros_a = 0;
156 unsigned __int128 result_a =
157 __sync_val_compare_and_swap((unsigned __int128*)pointer, zeros_a, zeros_a);
158
159 u128 result;
160 std::memcpy(result.data(), &result_a, sizeof(u128));
161 return result;
162}
163
76#endif 164#endif
77 165
78} // namespace Common 166} // namespace Common
diff --git a/src/common/bit_cast.h b/src/common/bit_cast.h
index a32a063d1..535148b4d 100644
--- a/src/common/bit_cast.h
+++ b/src/common/bit_cast.h
@@ -1,6 +1,5 @@
1// Copyright 2020 yuzu emulator team 1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#pragma once 4#pragma once
6 5
diff --git a/src/common/bit_field.h b/src/common/bit_field.h
index 0f0661172..7e1df62b1 100644
--- a/src/common/bit_field.h
+++ b/src/common/bit_field.h
@@ -1,39 +1,12 @@
1// Licensed under GPLv2 or any later version 1// SPDX-FileCopyrightText: 2014 Tony Wasserka
2// Refer to the license.txt file included. 2// SPDX-FileCopyrightText: 2014 Dolphin Emulator Project
3 3// SPDX-License-Identifier: BSD-3-Clause AND GPL-2.0-or-later
4// Copyright 2014 Tony Wasserka
5// All rights reserved.
6//
7// Redistribution and use in source and binary forms, with or without
8// modification, are permitted provided that the following conditions are met:
9//
10// * Redistributions of source code must retain the above copyright
11// notice, this list of conditions and the following disclaimer.
12// * Redistributions in binary form must reproduce the above copyright
13// notice, this list of conditions and the following disclaimer in the
14// documentation and/or other materials provided with the distribution.
15// * Neither the name of the owner nor the names of its contributors may
16// be used to endorse or promote products derived from this software
17// without specific prior written permission.
18//
19// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 4
31#pragma once 5#pragma once
32 6
33#include <cstddef> 7#include <cstddef>
34#include <limits> 8#include <limits>
35#include <type_traits> 9#include <type_traits>
36#include "common/common_funcs.h"
37#include "common/swap.h" 10#include "common/swap.h"
38 11
39/* 12/*
@@ -173,7 +146,16 @@ public:
173 } 146 }
174 147
175 constexpr void Assign(const T& value) { 148 constexpr void Assign(const T& value) {
149#ifdef _MSC_VER
176 storage = static_cast<StorageType>((storage & ~mask) | FormatValue(value)); 150 storage = static_cast<StorageType>((storage & ~mask) | FormatValue(value));
151#else
152 // Explicitly reload with memcpy to avoid compiler aliasing quirks
153 // regarding optimization: GCC/Clang clobber chained stores to
154 // different bitfields in the same struct with the last value.
155 StorageTypeWithEndian storage_;
156 std::memcpy(&storage_, &storage, sizeof(storage_));
157 storage = static_cast<StorageType>((storage_ & ~mask) | FormatValue(value));
158#endif
177 } 159 }
178 160
179 [[nodiscard]] constexpr T Value() const { 161 [[nodiscard]] constexpr T Value() const {
diff --git a/src/common/bit_set.h b/src/common/bit_set.h
index 9235ad412..74754504b 100644
--- a/src/common/bit_set.h
+++ b/src/common/bit_set.h
@@ -1,18 +1,5 @@
1/* 1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
2 * Copyright (c) 2018-2020 Atmosphère-NX 2// SPDX-License-Identifier: GPL-2.0-or-later
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms and conditions of the GNU General Public License,
6 * version 2, as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
11 * more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16 3
17#pragma once 4#pragma once
18 5
diff --git a/src/common/bit_util.h b/src/common/bit_util.h
index f50d3308a..e4e6287f3 100644
--- a/src/common/bit_util.h
+++ b/src/common/bit_util.h
@@ -1,6 +1,5 @@
1// Copyright 2018 yuzu emulator team 1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#pragma once 4#pragma once
6 5
@@ -57,4 +56,11 @@ requires std::is_integral_v<T>
57 return static_cast<T>(1ULL << ((8U * sizeof(T)) - std::countl_zero(value - 1U))); 56 return static_cast<T>(1ULL << ((8U * sizeof(T)) - std::countl_zero(value - 1U)));
58} 57}
59 58
59template <size_t bit_index, typename T>
60requires std::is_integral_v<T>
61[[nodiscard]] constexpr bool Bit(const T value) {
62 static_assert(bit_index < BitSize<T>(), "bit_index must be smaller than size of T");
63 return ((value >> bit_index) & T(1)) == T(1);
64}
65
60} // namespace Common 66} // namespace Common
diff --git a/src/common/bounded_threadsafe_queue.h b/src/common/bounded_threadsafe_queue.h
new file mode 100644
index 000000000..7e465549b
--- /dev/null
+++ b/src/common/bounded_threadsafe_queue.h
@@ -0,0 +1,167 @@
1// SPDX-FileCopyrightText: Copyright (c) 2020 Erik Rigtorp <erik@rigtorp.se>
2// SPDX-License-Identifier: MIT
3
4#pragma once
5
6#include <atomic>
7#include <bit>
8#include <condition_variable>
9#include <memory>
10#include <mutex>
11#include <new>
12#include <stop_token>
13#include <type_traits>
14#include <utility>
15
16namespace Common {
17
18#if defined(__cpp_lib_hardware_interference_size)
19constexpr size_t hardware_interference_size = std::hardware_destructive_interference_size;
20#else
21constexpr size_t hardware_interference_size = 64;
22#endif
23
24#ifdef _MSC_VER
25#pragma warning(push)
26#pragma warning(disable : 4324)
27#endif
28
29template <typename T, size_t capacity = 0x400>
30class MPSCQueue {
31public:
32 explicit MPSCQueue() : allocator{std::allocator<Slot<T>>()} {
33 // Allocate one extra slot to prevent false sharing on the last slot
34 slots = allocator.allocate(capacity + 1);
35 // Allocators are not required to honor alignment for over-aligned types
36 // (see http://eel.is/c++draft/allocator.requirements#10) so we verify
37 // alignment here
38 if (reinterpret_cast<uintptr_t>(slots) % alignof(Slot<T>) != 0) {
39 allocator.deallocate(slots, capacity + 1);
40 throw std::bad_alloc();
41 }
42 for (size_t i = 0; i < capacity; ++i) {
43 std::construct_at(&slots[i]);
44 }
45 static_assert(std::has_single_bit(capacity), "capacity must be an integer power of 2");
46 static_assert(alignof(Slot<T>) == hardware_interference_size,
47 "Slot must be aligned to cache line boundary to prevent false sharing");
48 static_assert(sizeof(Slot<T>) % hardware_interference_size == 0,
49 "Slot size must be a multiple of cache line size to prevent "
50 "false sharing between adjacent slots");
51 static_assert(sizeof(MPSCQueue) % hardware_interference_size == 0,
52 "Queue size must be a multiple of cache line size to "
53 "prevent false sharing between adjacent queues");
54 }
55
56 ~MPSCQueue() noexcept {
57 for (size_t i = 0; i < capacity; ++i) {
58 std::destroy_at(&slots[i]);
59 }
60 allocator.deallocate(slots, capacity + 1);
61 }
62
63 // The queue must be both non-copyable and non-movable
64 MPSCQueue(const MPSCQueue&) = delete;
65 MPSCQueue& operator=(const MPSCQueue&) = delete;
66
67 MPSCQueue(MPSCQueue&&) = delete;
68 MPSCQueue& operator=(MPSCQueue&&) = delete;
69
70 void Push(const T& v) noexcept {
71 static_assert(std::is_nothrow_copy_constructible_v<T>,
72 "T must be nothrow copy constructible");
73 emplace(v);
74 }
75
76 template <typename P, typename = std::enable_if_t<std::is_nothrow_constructible_v<T, P&&>>>
77 void Push(P&& v) noexcept {
78 emplace(std::forward<P>(v));
79 }
80
81 void Pop(T& v, std::stop_token stop) noexcept {
82 auto const tail = tail_.fetch_add(1);
83 auto& slot = slots[idx(tail)];
84 if (!slot.turn.test()) {
85 std::unique_lock lock{cv_mutex};
86 cv.wait(lock, stop, [&slot] { return slot.turn.test(); });
87 }
88 v = slot.move();
89 slot.destroy();
90 slot.turn.clear();
91 slot.turn.notify_one();
92 }
93
94private:
95 template <typename U = T>
96 struct Slot {
97 ~Slot() noexcept {
98 if (turn.test()) {
99 destroy();
100 }
101 }
102
103 template <typename... Args>
104 void construct(Args&&... args) noexcept {
105 static_assert(std::is_nothrow_constructible_v<U, Args&&...>,
106 "T must be nothrow constructible with Args&&...");
107 std::construct_at(reinterpret_cast<U*>(&storage), std::forward<Args>(args)...);
108 }
109
110 void destroy() noexcept {
111 static_assert(std::is_nothrow_destructible_v<U>, "T must be nothrow destructible");
112 std::destroy_at(reinterpret_cast<U*>(&storage));
113 }
114
115 U&& move() noexcept {
116 return reinterpret_cast<U&&>(storage);
117 }
118
119 // Align to avoid false sharing between adjacent slots
120 alignas(hardware_interference_size) std::atomic_flag turn{};
121 struct aligned_store {
122 struct type {
123 alignas(U) unsigned char data[sizeof(U)];
124 };
125 };
126 typename aligned_store::type storage;
127 };
128
129 template <typename... Args>
130 void emplace(Args&&... args) noexcept {
131 static_assert(std::is_nothrow_constructible_v<T, Args&&...>,
132 "T must be nothrow constructible with Args&&...");
133 auto const head = head_.fetch_add(1);
134 auto& slot = slots[idx(head)];
135 slot.turn.wait(true);
136 slot.construct(std::forward<Args>(args)...);
137 slot.turn.test_and_set();
138 cv.notify_one();
139 }
140
141 constexpr size_t idx(size_t i) const noexcept {
142 return i & mask;
143 }
144
145 static constexpr size_t mask = capacity - 1;
146
147 // Align to avoid false sharing between head_ and tail_
148 alignas(hardware_interference_size) std::atomic<size_t> head_{0};
149 alignas(hardware_interference_size) std::atomic<size_t> tail_{0};
150
151 std::mutex cv_mutex;
152 std::condition_variable_any cv;
153
154 Slot<T>* slots;
155 [[no_unique_address]] std::allocator<Slot<T>> allocator;
156
157 static_assert(std::is_nothrow_copy_assignable_v<T> || std::is_nothrow_move_assignable_v<T>,
158 "T must be nothrow copy or move assignable");
159
160 static_assert(std::is_nothrow_destructible_v<T>, "T must be nothrow destructible");
161};
162
163#ifdef _MSC_VER
164#pragma warning(pop)
165#endif
166
167} // namespace Common
diff --git a/src/common/cityhash.cpp b/src/common/cityhash.cpp
index 66218fc21..d50ac9e6c 100644
--- a/src/common/cityhash.cpp
+++ b/src/common/cityhash.cpp
@@ -1,23 +1,8 @@
1// Copyright (c) 2011 Google, Inc. 1// SPDX-FileCopyrightText: 2011 Google, Inc.
2// 2// SPDX-FileContributor: Geoff Pike
3// Permission is hereby granted, free of charge, to any person obtaining a copy 3// SPDX-FileContributor: Jyrki Alakuijala
4// of this software and associated documentation files (the "Software"), to deal 4// SPDX-License-Identifier: MIT
5// in the Software without restriction, including without limitation the rights 5
6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7// copies of the Software, and to permit persons to whom the Software is
8// furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in
11// all copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19// THE SOFTWARE.
20//
21// CityHash, by Geoff Pike and Jyrki Alakuijala 6// CityHash, by Geoff Pike and Jyrki Alakuijala
22// 7//
23// This file provides CityHash64() and related functions. 8// This file provides CityHash64() and related functions.
diff --git a/src/common/cityhash.h b/src/common/cityhash.h
index d74fc7639..627ba81cd 100644
--- a/src/common/cityhash.h
+++ b/src/common/cityhash.h
@@ -1,23 +1,8 @@
1// Copyright (c) 2011 Google, Inc. 1// SPDX-FileCopyrightText: 2011 Google, Inc.
2// 2// SPDX-FileContributor: Geoff Pike
3// Permission is hereby granted, free of charge, to any person obtaining a copy 3// SPDX-FileContributor: Jyrki Alakuijala
4// of this software and associated documentation files (the "Software"), to deal 4// SPDX-License-Identifier: MIT
5// in the Software without restriction, including without limitation the rights 5
6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7// copies of the Software, and to permit persons to whom the Software is
8// furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in
11// all copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19// THE SOFTWARE.
20//
21// CityHash, by Geoff Pike and Jyrki Alakuijala 6// CityHash, by Geoff Pike and Jyrki Alakuijala
22// 7//
23// http://code.google.com/p/cityhash/ 8// http://code.google.com/p/cityhash/
diff --git a/src/common/common_funcs.h b/src/common/common_funcs.h
index 4c1e29de6..e1e2a90fc 100644
--- a/src/common/common_funcs.h
+++ b/src/common/common_funcs.h
@@ -1,6 +1,5 @@
1// Copyright 2019 yuzu emulator team 1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#pragma once 4#pragma once
6 5
@@ -19,14 +18,16 @@
19/// Helper macros to insert unused bytes or words to properly align structs. These values will be 18/// Helper macros to insert unused bytes or words to properly align structs. These values will be
20/// zero-initialized. 19/// zero-initialized.
21#define INSERT_PADDING_BYTES(num_bytes) \ 20#define INSERT_PADDING_BYTES(num_bytes) \
22 std::array<u8, num_bytes> CONCAT2(pad, __LINE__) {} 21 [[maybe_unused]] std::array<u8, num_bytes> CONCAT2(pad, __LINE__) {}
23#define INSERT_PADDING_WORDS(num_words) \ 22#define INSERT_PADDING_WORDS(num_words) \
24 std::array<u32, num_words> CONCAT2(pad, __LINE__) {} 23 [[maybe_unused]] std::array<u32, num_words> CONCAT2(pad, __LINE__) {}
25 24
26/// These are similar to the INSERT_PADDING_* macros but do not zero-initialize the contents. 25/// These are similar to the INSERT_PADDING_* macros but do not zero-initialize the contents.
27/// This keeps the structure trivial to construct. 26/// This keeps the structure trivial to construct.
28#define INSERT_PADDING_BYTES_NOINIT(num_bytes) std::array<u8, num_bytes> CONCAT2(pad, __LINE__) 27#define INSERT_PADDING_BYTES_NOINIT(num_bytes) \
29#define INSERT_PADDING_WORDS_NOINIT(num_words) std::array<u32, num_words> CONCAT2(pad, __LINE__) 28 [[maybe_unused]] std::array<u8, num_bytes> CONCAT2(pad, __LINE__)
29#define INSERT_PADDING_WORDS_NOINIT(num_words) \
30 [[maybe_unused]] std::array<u32, num_words> CONCAT2(pad, __LINE__)
30 31
31#ifndef _MSC_VER 32#ifndef _MSC_VER
32 33
diff --git a/src/common/common_types.h b/src/common/common_types.h
index 99bffc460..0fc225aff 100644
--- a/src/common/common_types.h
+++ b/src/common/common_types.h
@@ -1,3 +1,7 @@
1// SPDX-FileCopyrightText: 2012 Gekko Emulator
2// SPDX-FileContributor: ShizZy <shizzy247@gmail.com>
3// SPDX-License-Identifier: GPL-2.0-or-later
4
1/** 5/**
2 * Copyright (C) 2005-2012 Gekko Emulator 6 * Copyright (C) 2005-2012 Gekko Emulator
3 * 7 *
diff --git a/src/common/concepts.h b/src/common/concepts.h
index aa08065a7..a97555f6a 100644
--- a/src/common/concepts.h
+++ b/src/common/concepts.h
@@ -1,6 +1,5 @@
1// Copyright 2020 yuzu emulator team 1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#pragma once 4#pragma once
6 5
diff --git a/src/common/detached_tasks.cpp b/src/common/detached_tasks.cpp
index c1362631e..da64848da 100644
--- a/src/common/detached_tasks.cpp
+++ b/src/common/detached_tasks.cpp
@@ -1,6 +1,5 @@
1// Copyright 2018 Citra Emulator Project 1// SPDX-FileCopyrightText: 2018 Citra Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#include <thread> 4#include <thread>
6#include "common/assert.h" 5#include "common/assert.h"
@@ -33,9 +32,9 @@ void DetachedTasks::AddTask(std::function<void()> task) {
33 ++instance->count; 32 ++instance->count;
34 std::thread([task{std::move(task)}]() { 33 std::thread([task{std::move(task)}]() {
35 task(); 34 task();
36 std::unique_lock lock{instance->mutex}; 35 std::unique_lock thread_lock{instance->mutex};
37 --instance->count; 36 --instance->count;
38 std::notify_all_at_thread_exit(instance->cv, std::move(lock)); 37 std::notify_all_at_thread_exit(instance->cv, std::move(thread_lock));
39 }).detach(); 38 }).detach();
40} 39}
41 40
diff --git a/src/common/detached_tasks.h b/src/common/detached_tasks.h
index 5dd8fc27b..416a2d7f3 100644
--- a/src/common/detached_tasks.h
+++ b/src/common/detached_tasks.h
@@ -1,6 +1,5 @@
1// Copyright 2018 Citra Emulator Project 1// SPDX-FileCopyrightText: 2018 Citra Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#pragma once 4#pragma once
6 5
diff --git a/src/common/div_ceil.h b/src/common/div_ceil.h
index e1db35464..eebc279c2 100644
--- a/src/common/div_ceil.h
+++ b/src/common/div_ceil.h
@@ -1,6 +1,5 @@
1// Copyright 2020 yuzu emulator team 1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#pragma once 4#pragma once
6 5
diff --git a/src/common/dynamic_library.cpp b/src/common/dynamic_library.cpp
index 7f0a10521..054277a2b 100644
--- a/src/common/dynamic_library.cpp
+++ b/src/common/dynamic_library.cpp
@@ -1,8 +1,6 @@
1// Copyright 2019 Dolphin Emulator Project 1// SPDX-FileCopyrightText: 2019 Dolphin Emulator Project
2// Licensed under GPLv2+ 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#include <cstring>
6#include <string> 4#include <string>
7#include <utility> 5#include <utility>
8 6
diff --git a/src/common/dynamic_library.h b/src/common/dynamic_library.h
index 3512da940..f42bdf441 100644
--- a/src/common/dynamic_library.h
+++ b/src/common/dynamic_library.h
@@ -1,6 +1,5 @@
1// Copyright 2019 Dolphin Emulator Project 1// SPDX-FileCopyrightText: 2019 Dolphin Emulator Project
2// Licensed under GPLv2+ 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#pragma once 4#pragma once
6 5
diff --git a/src/common/elf.h b/src/common/elf.h
new file mode 100644
index 000000000..14a5e9597
--- /dev/null
+++ b/src/common/elf.h
@@ -0,0 +1,333 @@
1// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <array>
7#include <cstddef>
8
9#include "common_types.h"
10
11namespace Common {
12namespace ELF {
13
14/* Type for a 16-bit quantity. */
15using Elf32_Half = u16;
16using Elf64_Half = u16;
17
18/* Types for signed and unsigned 32-bit quantities. */
19using Elf32_Word = u32;
20using Elf32_Sword = s32;
21using Elf64_Word = u32;
22using Elf64_Sword = s32;
23
24/* Types for signed and unsigned 64-bit quantities. */
25using Elf32_Xword = u64;
26using Elf32_Sxword = s64;
27using Elf64_Xword = u64;
28using Elf64_Sxword = s64;
29
30/* Type of addresses. */
31using Elf32_Addr = u32;
32using Elf64_Addr = u64;
33
34/* Type of file offsets. */
35using Elf32_Off = u32;
36using Elf64_Off = u64;
37
38/* Type for section indices, which are 16-bit quantities. */
39using Elf32_Section = u16;
40using Elf64_Section = u16;
41
42/* Type for version symbol information. */
43using Elf32_Versym = Elf32_Half;
44using Elf64_Versym = Elf64_Half;
45
46constexpr size_t ElfIdentSize = 16;
47
48/* The ELF file header. This appears at the start of every ELF file. */
49
50struct Elf32_Ehdr {
51 std::array<u8, ElfIdentSize> e_ident; /* Magic number and other info */
52 Elf32_Half e_type; /* Object file type */
53 Elf32_Half e_machine; /* Architecture */
54 Elf32_Word e_version; /* Object file version */
55 Elf32_Addr e_entry; /* Entry point virtual address */
56 Elf32_Off e_phoff; /* Program header table file offset */
57 Elf32_Off e_shoff; /* Section header table file offset */
58 Elf32_Word e_flags; /* Processor-specific flags */
59 Elf32_Half e_ehsize; /* ELF header size in bytes */
60 Elf32_Half e_phentsize; /* Program header table entry size */
61 Elf32_Half e_phnum; /* Program header table entry count */
62 Elf32_Half e_shentsize; /* Section header table entry size */
63 Elf32_Half e_shnum; /* Section header table entry count */
64 Elf32_Half e_shstrndx; /* Section header string table index */
65};
66
67struct Elf64_Ehdr {
68 std::array<u8, ElfIdentSize> e_ident; /* Magic number and other info */
69 Elf64_Half e_type; /* Object file type */
70 Elf64_Half e_machine; /* Architecture */
71 Elf64_Word e_version; /* Object file version */
72 Elf64_Addr e_entry; /* Entry point virtual address */
73 Elf64_Off e_phoff; /* Program header table file offset */
74 Elf64_Off e_shoff; /* Section header table file offset */
75 Elf64_Word e_flags; /* Processor-specific flags */
76 Elf64_Half e_ehsize; /* ELF header size in bytes */
77 Elf64_Half e_phentsize; /* Program header table entry size */
78 Elf64_Half e_phnum; /* Program header table entry count */
79 Elf64_Half e_shentsize; /* Section header table entry size */
80 Elf64_Half e_shnum; /* Section header table entry count */
81 Elf64_Half e_shstrndx; /* Section header string table index */
82};
83
84constexpr u8 ElfClass32 = 1; /* 32-bit objects */
85constexpr u8 ElfClass64 = 2; /* 64-bit objects */
86constexpr u8 ElfData2Lsb = 1; /* 2's complement, little endian */
87constexpr u8 ElfVersionCurrent = 1; /* EV_CURRENT */
88constexpr u8 ElfOsAbiNone = 0; /* System V ABI */
89
90constexpr u16 ElfTypeNone = 0; /* No file type */
91constexpr u16 ElfTypeRel = 0; /* Relocatable file */
92constexpr u16 ElfTypeExec = 0; /* Executable file */
93constexpr u16 ElfTypeDyn = 0; /* Shared object file */
94
95constexpr u16 ElfMachineArm = 40; /* ARM */
96constexpr u16 ElfMachineAArch64 = 183; /* ARM AARCH64 */
97
98constexpr std::array<u8, ElfIdentSize> Elf32Ident{
99 0x7f, 'E', 'L', 'F', ElfClass32, ElfData2Lsb, ElfVersionCurrent, ElfOsAbiNone};
100
101constexpr std::array<u8, ElfIdentSize> Elf64Ident{
102 0x7f, 'E', 'L', 'F', ElfClass64, ElfData2Lsb, ElfVersionCurrent, ElfOsAbiNone};
103
104/* Section header. */
105
106struct Elf32_Shdr {
107 Elf32_Word sh_name; /* Section name (string tbl index) */
108 Elf32_Word sh_type; /* Section type */
109 Elf32_Word sh_flags; /* Section flags */
110 Elf32_Addr sh_addr; /* Section virtual addr at execution */
111 Elf32_Off sh_offset; /* Section file offset */
112 Elf32_Word sh_size; /* Section size in bytes */
113 Elf32_Word sh_link; /* Link to another section */
114 Elf32_Word sh_info; /* Additional section information */
115 Elf32_Word sh_addralign; /* Section alignment */
116 Elf32_Word sh_entsize; /* Entry size if section holds table */
117};
118
119struct Elf64_Shdr {
120 Elf64_Word sh_name; /* Section name (string tbl index) */
121 Elf64_Word sh_type; /* Section type */
122 Elf64_Xword sh_flags; /* Section flags */
123 Elf64_Addr sh_addr; /* Section virtual addr at execution */
124 Elf64_Off sh_offset; /* Section file offset */
125 Elf64_Xword sh_size; /* Section size in bytes */
126 Elf64_Word sh_link; /* Link to another section */
127 Elf64_Word sh_info; /* Additional section information */
128 Elf64_Xword sh_addralign; /* Section alignment */
129 Elf64_Xword sh_entsize; /* Entry size if section holds table */
130};
131
132constexpr u32 ElfShnUndef = 0; /* Undefined section */
133
134constexpr u32 ElfShtNull = 0; /* Section header table entry unused */
135constexpr u32 ElfShtProgBits = 1; /* Program data */
136constexpr u32 ElfShtSymtab = 2; /* Symbol table */
137constexpr u32 ElfShtStrtab = 3; /* String table */
138constexpr u32 ElfShtRela = 4; /* Relocation entries with addends */
139constexpr u32 ElfShtDynamic = 6; /* Dynamic linking information */
140constexpr u32 ElfShtNobits = 7; /* Program space with no data (bss) */
141constexpr u32 ElfShtRel = 9; /* Relocation entries, no addends */
142constexpr u32 ElfShtDynsym = 11; /* Dynamic linker symbol table */
143
144/* Symbol table entry. */
145
146struct Elf32_Sym {
147 Elf32_Word st_name; /* Symbol name (string tbl index) */
148 Elf32_Addr st_value; /* Symbol value */
149 Elf32_Word st_size; /* Symbol size */
150 u8 st_info; /* Symbol type and binding */
151 u8 st_other; /* Symbol visibility */
152 Elf32_Section st_shndx; /* Section index */
153};
154
155struct Elf64_Sym {
156 Elf64_Word st_name; /* Symbol name (string tbl index) */
157 u8 st_info; /* Symbol type and binding */
158 u8 st_other; /* Symbol visibility */
159 Elf64_Section st_shndx; /* Section index */
160 Elf64_Addr st_value; /* Symbol value */
161 Elf64_Xword st_size; /* Symbol size */
162};
163
164/* How to extract and insert information held in the st_info field. */
165
166static inline u8 ElfStBind(u8 st_info) {
167 return st_info >> 4;
168}
169static inline u8 ElfStType(u8 st_info) {
170 return st_info & 0xf;
171}
172static inline u8 ElfStInfo(u8 st_bind, u8 st_type) {
173 return static_cast<u8>((st_bind << 4) + (st_type & 0xf));
174}
175
176constexpr u8 ElfBindLocal = 0; /* Local symbol */
177constexpr u8 ElfBindGlobal = 1; /* Global symbol */
178constexpr u8 ElfBindWeak = 2; /* Weak symbol */
179
180constexpr u8 ElfTypeUnspec = 0; /* Symbol type is unspecified */
181constexpr u8 ElfTypeObject = 1; /* Symbol is a data object */
182constexpr u8 ElfTypeFunc = 2; /* Symbol is a code object */
183
184static inline u8 ElfStVisibility(u8 st_other) {
185 return static_cast<u8>(st_other & 0x3);
186}
187
188constexpr u8 ElfVisibilityDefault = 0; /* Default symbol visibility rules */
189constexpr u8 ElfVisibilityInternal = 1; /* Processor specific hidden class */
190constexpr u8 ElfVisibilityHidden = 2; /* Sym unavailable in other modules */
191constexpr u8 ElfVisibilityProtected = 3; /* Not preemptible, not exported */
192
193/* Relocation table entry without addend (in section of type ShtRel). */
194
195struct Elf32_Rel {
196 Elf32_Addr r_offset; /* Address */
197 Elf32_Word r_info; /* Relocation type and symbol index */
198};
199
200/* Relocation table entry with addend (in section of type ShtRela). */
201
202struct Elf32_Rela {
203 Elf32_Addr r_offset; /* Address */
204 Elf32_Word r_info; /* Relocation type and symbol index */
205 Elf32_Sword r_addend; /* Addend */
206};
207
208struct Elf64_Rela {
209 Elf64_Addr r_offset; /* Address */
210 Elf64_Xword r_info; /* Relocation type and symbol index */
211 Elf64_Sxword r_addend; /* Addend */
212};
213
214/* How to extract and insert information held in the r_info field. */
215
216static inline u32 Elf32RelSymIndex(Elf32_Word r_info) {
217 return r_info >> 8;
218}
219static inline u8 Elf32RelType(Elf32_Word r_info) {
220 return static_cast<u8>(r_info & 0xff);
221}
222static inline Elf32_Word Elf32RelInfo(u32 sym_index, u8 type) {
223 return (sym_index << 8) + type;
224}
225static inline u32 Elf64RelSymIndex(Elf64_Xword r_info) {
226 return static_cast<u32>(r_info >> 32);
227}
228static inline u32 Elf64RelType(Elf64_Xword r_info) {
229 return r_info & 0xffffffff;
230}
231static inline Elf64_Xword Elf64RelInfo(u32 sym_index, u32 type) {
232 return (static_cast<Elf64_Xword>(sym_index) << 32) + type;
233}
234
235constexpr u32 ElfArmCopy = 20; /* Copy symbol at runtime */
236constexpr u32 ElfArmGlobDat = 21; /* Create GOT entry */
237constexpr u32 ElfArmJumpSlot = 22; /* Create PLT entry */
238constexpr u32 ElfArmRelative = 23; /* Adjust by program base */
239
240constexpr u32 ElfAArch64Copy = 1024; /* Copy symbol at runtime */
241constexpr u32 ElfAArch64GlobDat = 1025; /* Create GOT entry */
242constexpr u32 ElfAArch64JumpSlot = 1026; /* Create PLT entry */
243constexpr u32 ElfAArch64Relative = 1027; /* Adjust by program base */
244
245/* Program segment header. */
246
247struct Elf32_Phdr {
248 Elf32_Word p_type; /* Segment type */
249 Elf32_Off p_offset; /* Segment file offset */
250 Elf32_Addr p_vaddr; /* Segment virtual address */
251 Elf32_Addr p_paddr; /* Segment physical address */
252 Elf32_Word p_filesz; /* Segment size in file */
253 Elf32_Word p_memsz; /* Segment size in memory */
254 Elf32_Word p_flags; /* Segment flags */
255 Elf32_Word p_align; /* Segment alignment */
256};
257
258struct Elf64_Phdr {
259 Elf64_Word p_type; /* Segment type */
260 Elf64_Word p_flags; /* Segment flags */
261 Elf64_Off p_offset; /* Segment file offset */
262 Elf64_Addr p_vaddr; /* Segment virtual address */
263 Elf64_Addr p_paddr; /* Segment physical address */
264 Elf64_Xword p_filesz; /* Segment size in file */
265 Elf64_Xword p_memsz; /* Segment size in memory */
266 Elf64_Xword p_align; /* Segment alignment */
267};
268
269/* Legal values for p_type (segment type). */
270
271constexpr u32 ElfPtNull = 0; /* Program header table entry unused */
272constexpr u32 ElfPtLoad = 1; /* Loadable program segment */
273constexpr u32 ElfPtDynamic = 2; /* Dynamic linking information */
274constexpr u32 ElfPtInterp = 3; /* Program interpreter */
275constexpr u32 ElfPtNote = 4; /* Auxiliary information */
276constexpr u32 ElfPtPhdr = 6; /* Entry for header table itself */
277constexpr u32 ElfPtTls = 7; /* Thread-local storage segment */
278
279/* Legal values for p_flags (segment flags). */
280
281constexpr u32 ElfPfExec = 0; /* Segment is executable */
282constexpr u32 ElfPfWrite = 1; /* Segment is writable */
283constexpr u32 ElfPfRead = 2; /* Segment is readable */
284
285/* Dynamic section entry. */
286
287struct Elf32_Dyn {
288 Elf32_Sword d_tag; /* Dynamic entry type */
289 union {
290 Elf32_Word d_val; /* Integer value */
291 Elf32_Addr d_ptr; /* Address value */
292 } d_un;
293};
294
295struct Elf64_Dyn {
296 Elf64_Sxword d_tag; /* Dynamic entry type */
297 union {
298 Elf64_Xword d_val; /* Integer value */
299 Elf64_Addr d_ptr; /* Address value */
300 } d_un;
301};
302
303/* Legal values for d_tag (dynamic entry type). */
304
305constexpr u32 ElfDtNull = 0; /* Marks end of dynamic section */
306constexpr u32 ElfDtNeeded = 1; /* Name of needed library */
307constexpr u32 ElfDtPltRelSz = 2; /* Size in bytes of PLT relocs */
308constexpr u32 ElfDtPltGot = 3; /* Processor defined value */
309constexpr u32 ElfDtHash = 4; /* Address of symbol hash table */
310constexpr u32 ElfDtStrtab = 5; /* Address of string table */
311constexpr u32 ElfDtSymtab = 6; /* Address of symbol table */
312constexpr u32 ElfDtRela = 7; /* Address of Rela relocs */
313constexpr u32 ElfDtRelasz = 8; /* Total size of Rela relocs */
314constexpr u32 ElfDtRelaent = 9; /* Size of one Rela reloc */
315constexpr u32 ElfDtStrsz = 10; /* Size of string table */
316constexpr u32 ElfDtSyment = 11; /* Size of one symbol table entry */
317constexpr u32 ElfDtInit = 12; /* Address of init function */
318constexpr u32 ElfDtFini = 13; /* Address of termination function */
319constexpr u32 ElfDtRel = 17; /* Address of Rel relocs */
320constexpr u32 ElfDtRelsz = 18; /* Total size of Rel relocs */
321constexpr u32 ElfDtRelent = 19; /* Size of one Rel reloc */
322constexpr u32 ElfDtPltRel = 20; /* Type of reloc in PLT */
323constexpr u32 ElfDtTextRel = 22; /* Reloc might modify .text */
324constexpr u32 ElfDtJmpRel = 23; /* Address of PLT relocs */
325constexpr u32 ElfDtBindNow = 24; /* Process relocations of object */
326constexpr u32 ElfDtInitArray = 25; /* Array with addresses of init fct */
327constexpr u32 ElfDtFiniArray = 26; /* Array with addresses of fini fct */
328constexpr u32 ElfDtInitArraySz = 27; /* Size in bytes of DT_INIT_ARRAY */
329constexpr u32 ElfDtFiniArraySz = 28; /* Size in bytes of DT_FINI_ARRAY */
330constexpr u32 ElfDtSymtabShndx = 34; /* Address of SYMTAB_SHNDX section */
331
332} // namespace ELF
333} // namespace Common
diff --git a/src/common/error.cpp b/src/common/error.cpp
index d4455e310..ddb03bd45 100644
--- a/src/common/error.cpp
+++ b/src/common/error.cpp
@@ -1,6 +1,6 @@
1// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project 1// SPDX-FileCopyrightText: 2013 Dolphin Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-FileCopyrightText: 2014 Citra Emulator Project
3// Refer to the license.txt file included. 3// SPDX-License-Identifier: GPL-2.0-or-later
4 4
5#include <cstddef> 5#include <cstddef>
6#ifdef _WIN32 6#ifdef _WIN32
diff --git a/src/common/error.h b/src/common/error.h
index e084d4b0f..62a3bd835 100644
--- a/src/common/error.h
+++ b/src/common/error.h
@@ -1,6 +1,6 @@
1// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project 1// SPDX-FileCopyrightText: 2013 Dolphin Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-FileCopyrightText: 2014 Citra Emulator Project
3// Refer to the license.txt file included. 3// SPDX-License-Identifier: GPL-2.0-or-later
4 4
5#pragma once 5#pragma once
6 6
diff --git a/src/common/expected.h b/src/common/expected.h
index c8d8579c1..6e6c86ee7 100644
--- a/src/common/expected.h
+++ b/src/common/expected.h
@@ -1,6 +1,5 @@
1// Copyright 2021 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5// This is based on the proposed implementation of std::expected (P0323) 4// This is based on the proposed implementation of std::expected (P0323)
6// https://github.com/TartanLlama/expected/blob/master/include/tl/expected.hpp 5// https://github.com/TartanLlama/expected/blob/master/include/tl/expected.hpp
diff --git a/src/common/fiber.cpp b/src/common/fiber.cpp
index 81b212e4b..bc92b360b 100644
--- a/src/common/fiber.cpp
+++ b/src/common/fiber.cpp
@@ -1,10 +1,10 @@
1// Copyright 2020 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included. 3
4#include <mutex>
4 5
5#include "common/assert.h" 6#include "common/assert.h"
6#include "common/fiber.h" 7#include "common/fiber.h"
7#include "common/spin_lock.h"
8#include "common/virtual_buffer.h" 8#include "common/virtual_buffer.h"
9 9
10#include <boost/context/detail/fcontext.hpp> 10#include <boost/context/detail/fcontext.hpp>
@@ -19,11 +19,9 @@ struct Fiber::FiberImpl {
19 VirtualBuffer<u8> stack; 19 VirtualBuffer<u8> stack;
20 VirtualBuffer<u8> rewind_stack; 20 VirtualBuffer<u8> rewind_stack;
21 21
22 SpinLock guard{}; 22 std::mutex guard;
23 std::function<void(void*)> entry_point; 23 std::function<void()> entry_point;
24 std::function<void(void*)> rewind_point; 24 std::function<void()> rewind_point;
25 void* rewind_parameter{};
26 void* start_parameter{};
27 std::shared_ptr<Fiber> previous_fiber; 25 std::shared_ptr<Fiber> previous_fiber;
28 bool is_thread_fiber{}; 26 bool is_thread_fiber{};
29 bool released{}; 27 bool released{};
@@ -34,13 +32,8 @@ struct Fiber::FiberImpl {
34 boost::context::detail::fcontext_t rewind_context{}; 32 boost::context::detail::fcontext_t rewind_context{};
35}; 33};
36 34
37void Fiber::SetStartParameter(void* new_parameter) { 35void Fiber::SetRewindPoint(std::function<void()>&& rewind_func) {
38 impl->start_parameter = new_parameter;
39}
40
41void Fiber::SetRewindPoint(std::function<void(void*)>&& rewind_func, void* rewind_param) {
42 impl->rewind_point = std::move(rewind_func); 36 impl->rewind_point = std::move(rewind_func);
43 impl->rewind_parameter = rewind_param;
44} 37}
45 38
46void Fiber::Start(boost::context::detail::transfer_t& transfer) { 39void Fiber::Start(boost::context::detail::transfer_t& transfer) {
@@ -48,7 +41,7 @@ void Fiber::Start(boost::context::detail::transfer_t& transfer) {
48 impl->previous_fiber->impl->context = transfer.fctx; 41 impl->previous_fiber->impl->context = transfer.fctx;
49 impl->previous_fiber->impl->guard.unlock(); 42 impl->previous_fiber->impl->guard.unlock();
50 impl->previous_fiber.reset(); 43 impl->previous_fiber.reset();
51 impl->entry_point(impl->start_parameter); 44 impl->entry_point();
52 UNREACHABLE(); 45 UNREACHABLE();
53} 46}
54 47
@@ -59,7 +52,7 @@ void Fiber::OnRewind([[maybe_unused]] boost::context::detail::transfer_t& transf
59 u8* tmp = impl->stack_limit; 52 u8* tmp = impl->stack_limit;
60 impl->stack_limit = impl->rewind_stack_limit; 53 impl->stack_limit = impl->rewind_stack_limit;
61 impl->rewind_stack_limit = tmp; 54 impl->rewind_stack_limit = tmp;
62 impl->rewind_point(impl->rewind_parameter); 55 impl->rewind_point();
63 UNREACHABLE(); 56 UNREACHABLE();
64} 57}
65 58
@@ -73,10 +66,8 @@ void Fiber::RewindStartFunc(boost::context::detail::transfer_t transfer) {
73 fiber->OnRewind(transfer); 66 fiber->OnRewind(transfer);
74} 67}
75 68
76Fiber::Fiber(std::function<void(void*)>&& entry_point_func, void* start_parameter) 69Fiber::Fiber(std::function<void()>&& entry_point_func) : impl{std::make_unique<FiberImpl>()} {
77 : impl{std::make_unique<FiberImpl>()} {
78 impl->entry_point = std::move(entry_point_func); 70 impl->entry_point = std::move(entry_point_func);
79 impl->start_parameter = start_parameter;
80 impl->stack_limit = impl->stack.data(); 71 impl->stack_limit = impl->stack.data();
81 impl->rewind_stack_limit = impl->rewind_stack.data(); 72 impl->rewind_stack_limit = impl->rewind_stack.data();
82 u8* stack_base = impl->stack_limit + default_stack_size; 73 u8* stack_base = impl->stack_limit + default_stack_size;
diff --git a/src/common/fiber.h b/src/common/fiber.h
index f2a8ff29a..f24d333a3 100644
--- a/src/common/fiber.h
+++ b/src/common/fiber.h
@@ -1,6 +1,5 @@
1// Copyright 2020 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#pragma once 4#pragma once
6 5
@@ -30,7 +29,7 @@ namespace Common {
30 */ 29 */
31class Fiber { 30class Fiber {
32public: 31public:
33 Fiber(std::function<void(void*)>&& entry_point_func, void* start_parameter); 32 Fiber(std::function<void()>&& entry_point_func);
34 ~Fiber(); 33 ~Fiber();
35 34
36 Fiber(const Fiber&) = delete; 35 Fiber(const Fiber&) = delete;
@@ -44,16 +43,13 @@ public:
44 static void YieldTo(std::weak_ptr<Fiber> weak_from, Fiber& to); 43 static void YieldTo(std::weak_ptr<Fiber> weak_from, Fiber& to);
45 [[nodiscard]] static std::shared_ptr<Fiber> ThreadToFiber(); 44 [[nodiscard]] static std::shared_ptr<Fiber> ThreadToFiber();
46 45
47 void SetRewindPoint(std::function<void(void*)>&& rewind_func, void* rewind_param); 46 void SetRewindPoint(std::function<void()>&& rewind_func);
48 47
49 void Rewind(); 48 void Rewind();
50 49
51 /// Only call from main thread's fiber 50 /// Only call from main thread's fiber
52 void Exit(); 51 void Exit();
53 52
54 /// Changes the start parameter of the fiber. Has no effect if the fiber already started
55 void SetStartParameter(void* new_parameter);
56
57private: 53private:
58 Fiber(); 54 Fiber();
59 55
diff --git a/src/common/fixed_point.h b/src/common/fixed_point.h
new file mode 100644
index 000000000..4a0f72cc9
--- /dev/null
+++ b/src/common/fixed_point.h
@@ -0,0 +1,706 @@
1// SPDX-FileCopyrightText: 2015 Evan Teran
2// SPDX-License-Identifier: MIT
3
4// From: https://github.com/eteran/cpp-utilities/blob/master/fixed/include/cpp-utilities/fixed.h
5// See also: http://stackoverflow.com/questions/79677/whats-the-best-way-to-do-fixed-point-math
6
7#ifndef FIXED_H_
8#define FIXED_H_
9
10#if __cplusplus >= 201402L
11#define CONSTEXPR14 constexpr
12#else
13#define CONSTEXPR14
14#endif
15
16#include <cstddef> // for size_t
17#include <cstdint>
18#include <exception>
19#include <ostream>
20#include <type_traits>
21
22namespace Common {
23
24template <size_t I, size_t F>
25class FixedPoint;
26
27namespace detail {
28
29// helper templates to make magic with types :)
30// these allow us to determine resonable types from
31// a desired size, they also let us infer the next largest type
32// from a type which is nice for the division op
33template <size_t T>
34struct type_from_size {
35 using value_type = void;
36 using unsigned_type = void;
37 using signed_type = void;
38 static constexpr bool is_specialized = false;
39};
40
41#if defined(__GNUC__) && defined(__x86_64__) && !defined(__STRICT_ANSI__)
42template <>
43struct type_from_size<128> {
44 static constexpr bool is_specialized = true;
45 static constexpr size_t size = 128;
46
47 using value_type = __int128;
48 using unsigned_type = unsigned __int128;
49 using signed_type = __int128;
50 using next_size = type_from_size<256>;
51};
52#endif
53
54template <>
55struct type_from_size<64> {
56 static constexpr bool is_specialized = true;
57 static constexpr size_t size = 64;
58
59 using value_type = int64_t;
60 using unsigned_type = std::make_unsigned<value_type>::type;
61 using signed_type = std::make_signed<value_type>::type;
62 using next_size = type_from_size<128>;
63};
64
65template <>
66struct type_from_size<32> {
67 static constexpr bool is_specialized = true;
68 static constexpr size_t size = 32;
69
70 using value_type = int32_t;
71 using unsigned_type = std::make_unsigned<value_type>::type;
72 using signed_type = std::make_signed<value_type>::type;
73 using next_size = type_from_size<64>;
74};
75
76template <>
77struct type_from_size<16> {
78 static constexpr bool is_specialized = true;
79 static constexpr size_t size = 16;
80
81 using value_type = int16_t;
82 using unsigned_type = std::make_unsigned<value_type>::type;
83 using signed_type = std::make_signed<value_type>::type;
84 using next_size = type_from_size<32>;
85};
86
87template <>
88struct type_from_size<8> {
89 static constexpr bool is_specialized = true;
90 static constexpr size_t size = 8;
91
92 using value_type = int8_t;
93 using unsigned_type = std::make_unsigned<value_type>::type;
94 using signed_type = std::make_signed<value_type>::type;
95 using next_size = type_from_size<16>;
96};
97
98// this is to assist in adding support for non-native base
99// types (for adding big-int support), this should be fine
100// unless your bit-int class doesn't nicely support casting
101template <class B, class N>
102constexpr B next_to_base(N rhs) {
103 return static_cast<B>(rhs);
104}
105
106struct divide_by_zero : std::exception {};
107
108template <size_t I, size_t F>
109CONSTEXPR14 FixedPoint<I, F> divide(
110 FixedPoint<I, F> numerator, FixedPoint<I, F> denominator, FixedPoint<I, F>& remainder,
111 typename std::enable_if<type_from_size<I + F>::next_size::is_specialized>::type* = nullptr) {
112
113 using next_type = typename FixedPoint<I, F>::next_type;
114 using base_type = typename FixedPoint<I, F>::base_type;
115 constexpr size_t fractional_bits = FixedPoint<I, F>::fractional_bits;
116
117 next_type t(numerator.to_raw());
118 t <<= fractional_bits;
119
120 FixedPoint<I, F> quotient;
121
122 quotient = FixedPoint<I, F>::from_base(next_to_base<base_type>(t / denominator.to_raw()));
123 remainder = FixedPoint<I, F>::from_base(next_to_base<base_type>(t % denominator.to_raw()));
124
125 return quotient;
126}
127
128template <size_t I, size_t F>
129CONSTEXPR14 FixedPoint<I, F> divide(
130 FixedPoint<I, F> numerator, FixedPoint<I, F> denominator, FixedPoint<I, F>& remainder,
131 typename std::enable_if<!type_from_size<I + F>::next_size::is_specialized>::type* = nullptr) {
132
133 using unsigned_type = typename FixedPoint<I, F>::unsigned_type;
134
135 constexpr int bits = FixedPoint<I, F>::total_bits;
136
137 if (denominator == 0) {
138 throw divide_by_zero();
139 } else {
140
141 int sign = 0;
142
143 FixedPoint<I, F> quotient;
144
145 if (numerator < 0) {
146 sign ^= 1;
147 numerator = -numerator;
148 }
149
150 if (denominator < 0) {
151 sign ^= 1;
152 denominator = -denominator;
153 }
154
155 unsigned_type n = numerator.to_raw();
156 unsigned_type d = denominator.to_raw();
157 unsigned_type x = 1;
158 unsigned_type answer = 0;
159
160 // egyptian division algorithm
161 while ((n >= d) && (((d >> (bits - 1)) & 1) == 0)) {
162 x <<= 1;
163 d <<= 1;
164 }
165
166 while (x != 0) {
167 if (n >= d) {
168 n -= d;
169 answer += x;
170 }
171
172 x >>= 1;
173 d >>= 1;
174 }
175
176 unsigned_type l1 = n;
177 unsigned_type l2 = denominator.to_raw();
178
179 // calculate the lower bits (needs to be unsigned)
180 while (l1 >> (bits - F) > 0) {
181 l1 >>= 1;
182 l2 >>= 1;
183 }
184 const unsigned_type lo = (l1 << F) / l2;
185
186 quotient = FixedPoint<I, F>::from_base((answer << F) | lo);
187 remainder = n;
188
189 if (sign) {
190 quotient = -quotient;
191 }
192
193 return quotient;
194 }
195}
196
197// this is the usual implementation of multiplication
198template <size_t I, size_t F>
199CONSTEXPR14 FixedPoint<I, F> multiply(
200 FixedPoint<I, F> lhs, FixedPoint<I, F> rhs,
201 typename std::enable_if<type_from_size<I + F>::next_size::is_specialized>::type* = nullptr) {
202
203 using next_type = typename FixedPoint<I, F>::next_type;
204 using base_type = typename FixedPoint<I, F>::base_type;
205
206 constexpr size_t fractional_bits = FixedPoint<I, F>::fractional_bits;
207
208 next_type t(static_cast<next_type>(lhs.to_raw()) * static_cast<next_type>(rhs.to_raw()));
209 t >>= fractional_bits;
210
211 return FixedPoint<I, F>::from_base(next_to_base<base_type>(t));
212}
213
214// this is the fall back version we use when we don't have a next size
215// it is slightly slower, but is more robust since it doesn't
216// require and upgraded type
217template <size_t I, size_t F>
218CONSTEXPR14 FixedPoint<I, F> multiply(
219 FixedPoint<I, F> lhs, FixedPoint<I, F> rhs,
220 typename std::enable_if<!type_from_size<I + F>::next_size::is_specialized>::type* = nullptr) {
221
222 using base_type = typename FixedPoint<I, F>::base_type;
223
224 constexpr size_t fractional_bits = FixedPoint<I, F>::fractional_bits;
225 constexpr base_type integer_mask = FixedPoint<I, F>::integer_mask;
226 constexpr base_type fractional_mask = FixedPoint<I, F>::fractional_mask;
227
228 // more costly but doesn't need a larger type
229 const base_type a_hi = (lhs.to_raw() & integer_mask) >> fractional_bits;
230 const base_type b_hi = (rhs.to_raw() & integer_mask) >> fractional_bits;
231 const base_type a_lo = (lhs.to_raw() & fractional_mask);
232 const base_type b_lo = (rhs.to_raw() & fractional_mask);
233
234 const base_type x1 = a_hi * b_hi;
235 const base_type x2 = a_hi * b_lo;
236 const base_type x3 = a_lo * b_hi;
237 const base_type x4 = a_lo * b_lo;
238
239 return FixedPoint<I, F>::from_base((x1 << fractional_bits) + (x3 + x2) +
240 (x4 >> fractional_bits));
241}
242} // namespace detail
243
244template <size_t I, size_t F>
245class FixedPoint {
246 static_assert(detail::type_from_size<I + F>::is_specialized, "invalid combination of sizes");
247
248public:
249 static constexpr size_t fractional_bits = F;
250 static constexpr size_t integer_bits = I;
251 static constexpr size_t total_bits = I + F;
252
253 using base_type_info = detail::type_from_size<total_bits>;
254
255 using base_type = typename base_type_info::value_type;
256 using next_type = typename base_type_info::next_size::value_type;
257 using unsigned_type = typename base_type_info::unsigned_type;
258
259public:
260#ifdef __GNUC__
261#pragma GCC diagnostic push
262#pragma GCC diagnostic ignored "-Woverflow"
263#endif
264 static constexpr base_type fractional_mask =
265 ~(static_cast<unsigned_type>(~base_type(0)) << fractional_bits);
266 static constexpr base_type integer_mask = ~fractional_mask;
267#ifdef __GNUC__
268#pragma GCC diagnostic pop
269#endif
270
271public:
272 static constexpr base_type one = base_type(1) << fractional_bits;
273
274public: // constructors
275 FixedPoint() = default;
276 FixedPoint(const FixedPoint&) = default;
277 FixedPoint(FixedPoint&&) = default;
278 FixedPoint& operator=(const FixedPoint&) = default;
279
280 template <class Number>
281 constexpr FixedPoint(
282 Number n, typename std::enable_if<std::is_arithmetic<Number>::value>::type* = nullptr)
283 : data_(static_cast<base_type>(n * one)) {}
284
285public: // conversion
286 template <size_t I2, size_t F2>
287 CONSTEXPR14 explicit FixedPoint(FixedPoint<I2, F2> other) {
288 static_assert(I2 <= I && F2 <= F, "Scaling conversion can only upgrade types");
289 using T = FixedPoint<I2, F2>;
290
291 const base_type fractional = (other.data_ & T::fractional_mask);
292 const base_type integer = (other.data_ & T::integer_mask) >> T::fractional_bits;
293 data_ =
294 (integer << fractional_bits) | (fractional << (fractional_bits - T::fractional_bits));
295 }
296
297private:
298 // this makes it simpler to create a FixedPoint point object from
299 // a native type without scaling
300 // use "FixedPoint::from_base" in order to perform this.
301 struct NoScale {};
302
303 constexpr FixedPoint(base_type n, const NoScale&) : data_(n) {}
304
305public:
306 static constexpr FixedPoint from_base(base_type n) {
307 return FixedPoint(n, NoScale());
308 }
309
310public: // comparison operators
311 constexpr bool operator==(FixedPoint rhs) const {
312 return data_ == rhs.data_;
313 }
314
315 constexpr bool operator!=(FixedPoint rhs) const {
316 return data_ != rhs.data_;
317 }
318
319 constexpr bool operator<(FixedPoint rhs) const {
320 return data_ < rhs.data_;
321 }
322
323 constexpr bool operator>(FixedPoint rhs) const {
324 return data_ > rhs.data_;
325 }
326
327 constexpr bool operator<=(FixedPoint rhs) const {
328 return data_ <= rhs.data_;
329 }
330
331 constexpr bool operator>=(FixedPoint rhs) const {
332 return data_ >= rhs.data_;
333 }
334
335public: // unary operators
336 constexpr bool operator!() const {
337 return !data_;
338 }
339
340 constexpr FixedPoint operator~() const {
341 // NOTE(eteran): this will often appear to "just negate" the value
342 // that is not an error, it is because -x == (~x+1)
343 // and that "+1" is adding an infinitesimally small fraction to the
344 // complimented value
345 return FixedPoint::from_base(~data_);
346 }
347
348 constexpr FixedPoint operator-() const {
349 return FixedPoint::from_base(-data_);
350 }
351
352 constexpr FixedPoint operator+() const {
353 return FixedPoint::from_base(+data_);
354 }
355
356 CONSTEXPR14 FixedPoint& operator++() {
357 data_ += one;
358 return *this;
359 }
360
361 CONSTEXPR14 FixedPoint& operator--() {
362 data_ -= one;
363 return *this;
364 }
365
366 CONSTEXPR14 FixedPoint operator++(int) {
367 FixedPoint tmp(*this);
368 data_ += one;
369 return tmp;
370 }
371
372 CONSTEXPR14 FixedPoint operator--(int) {
373 FixedPoint tmp(*this);
374 data_ -= one;
375 return tmp;
376 }
377
378public: // basic math operators
379 CONSTEXPR14 FixedPoint& operator+=(FixedPoint n) {
380 data_ += n.data_;
381 return *this;
382 }
383
384 CONSTEXPR14 FixedPoint& operator-=(FixedPoint n) {
385 data_ -= n.data_;
386 return *this;
387 }
388
389 CONSTEXPR14 FixedPoint& operator*=(FixedPoint n) {
390 return assign(detail::multiply(*this, n));
391 }
392
393 CONSTEXPR14 FixedPoint& operator/=(FixedPoint n) {
394 FixedPoint temp;
395 return assign(detail::divide(*this, n, temp));
396 }
397
398private:
399 CONSTEXPR14 FixedPoint& assign(FixedPoint rhs) {
400 data_ = rhs.data_;
401 return *this;
402 }
403
404public: // binary math operators, effects underlying bit pattern since these
405 // don't really typically make sense for non-integer values
406 CONSTEXPR14 FixedPoint& operator&=(FixedPoint n) {
407 data_ &= n.data_;
408 return *this;
409 }
410
411 CONSTEXPR14 FixedPoint& operator|=(FixedPoint n) {
412 data_ |= n.data_;
413 return *this;
414 }
415
416 CONSTEXPR14 FixedPoint& operator^=(FixedPoint n) {
417 data_ ^= n.data_;
418 return *this;
419 }
420
421 template <class Integer,
422 class = typename std::enable_if<std::is_integral<Integer>::value>::type>
423 CONSTEXPR14 FixedPoint& operator>>=(Integer n) {
424 data_ >>= n;
425 return *this;
426 }
427
428 template <class Integer,
429 class = typename std::enable_if<std::is_integral<Integer>::value>::type>
430 CONSTEXPR14 FixedPoint& operator<<=(Integer n) {
431 data_ <<= n;
432 return *this;
433 }
434
435public: // conversion to basic types
436 constexpr void round_up() {
437 data_ += (data_ & fractional_mask) >> 1;
438 }
439
440 constexpr int to_int() {
441 round_up();
442 return static_cast<int>((data_ & integer_mask) >> fractional_bits);
443 }
444
445 constexpr unsigned int to_uint() const {
446 round_up();
447 return static_cast<unsigned int>((data_ & integer_mask) >> fractional_bits);
448 }
449
450 constexpr int64_t to_long() {
451 round_up();
452 return static_cast<int64_t>((data_ & integer_mask) >> fractional_bits);
453 }
454
455 constexpr int to_int_floor() const {
456 return static_cast<int>((data_ & integer_mask) >> fractional_bits);
457 }
458
459 constexpr int64_t to_long_floor() {
460 return static_cast<int64_t>((data_ & integer_mask) >> fractional_bits);
461 }
462
463 constexpr unsigned int to_uint_floor() const {
464 return static_cast<unsigned int>((data_ & integer_mask) >> fractional_bits);
465 }
466
467 constexpr float to_float() const {
468 return static_cast<float>(data_) / FixedPoint::one;
469 }
470
471 constexpr double to_double() const {
472 return static_cast<double>(data_) / FixedPoint::one;
473 }
474
475 constexpr base_type to_raw() const {
476 return data_;
477 }
478
479 constexpr void clear_int() {
480 data_ &= fractional_mask;
481 }
482
483 constexpr base_type get_frac() const {
484 return data_ & fractional_mask;
485 }
486
487public:
488 CONSTEXPR14 void swap(FixedPoint& rhs) {
489 using std::swap;
490 swap(data_, rhs.data_);
491 }
492
493public:
494 base_type data_;
495};
496
497// if we have the same fractional portion, but differing integer portions, we trivially upgrade the
498// smaller type
499template <size_t I1, size_t I2, size_t F>
500CONSTEXPR14 typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>::type
501operator+(FixedPoint<I1, F> lhs, FixedPoint<I2, F> rhs) {
502
503 using T = typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>::type;
504
505 const T l = T::from_base(lhs.to_raw());
506 const T r = T::from_base(rhs.to_raw());
507 return l + r;
508}
509
510template <size_t I1, size_t I2, size_t F>
511CONSTEXPR14 typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>::type
512operator-(FixedPoint<I1, F> lhs, FixedPoint<I2, F> rhs) {
513
514 using T = typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>::type;
515
516 const T l = T::from_base(lhs.to_raw());
517 const T r = T::from_base(rhs.to_raw());
518 return l - r;
519}
520
521template <size_t I1, size_t I2, size_t F>
522CONSTEXPR14 typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>::type
523operator*(FixedPoint<I1, F> lhs, FixedPoint<I2, F> rhs) {
524
525 using T = typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>::type;
526
527 const T l = T::from_base(lhs.to_raw());
528 const T r = T::from_base(rhs.to_raw());
529 return l * r;
530}
531
532template <size_t I1, size_t I2, size_t F>
533CONSTEXPR14 typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>::type
534operator/(FixedPoint<I1, F> lhs, FixedPoint<I2, F> rhs) {
535
536 using T = typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>::type;
537
538 const T l = T::from_base(lhs.to_raw());
539 const T r = T::from_base(rhs.to_raw());
540 return l / r;
541}
542
543template <size_t I, size_t F>
544std::ostream& operator<<(std::ostream& os, FixedPoint<I, F> f) {
545 os << f.to_double();
546 return os;
547}
548
549// basic math operators
550template <size_t I, size_t F>
551CONSTEXPR14 FixedPoint<I, F> operator+(FixedPoint<I, F> lhs, FixedPoint<I, F> rhs) {
552 lhs += rhs;
553 return lhs;
554}
555template <size_t I, size_t F>
556CONSTEXPR14 FixedPoint<I, F> operator-(FixedPoint<I, F> lhs, FixedPoint<I, F> rhs) {
557 lhs -= rhs;
558 return lhs;
559}
560template <size_t I, size_t F>
561CONSTEXPR14 FixedPoint<I, F> operator*(FixedPoint<I, F> lhs, FixedPoint<I, F> rhs) {
562 lhs *= rhs;
563 return lhs;
564}
565template <size_t I, size_t F>
566CONSTEXPR14 FixedPoint<I, F> operator/(FixedPoint<I, F> lhs, FixedPoint<I, F> rhs) {
567 lhs /= rhs;
568 return lhs;
569}
570
571template <size_t I, size_t F, class Number,
572 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
573CONSTEXPR14 FixedPoint<I, F> operator+(FixedPoint<I, F> lhs, Number rhs) {
574 lhs += FixedPoint<I, F>(rhs);
575 return lhs;
576}
577template <size_t I, size_t F, class Number,
578 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
579CONSTEXPR14 FixedPoint<I, F> operator-(FixedPoint<I, F> lhs, Number rhs) {
580 lhs -= FixedPoint<I, F>(rhs);
581 return lhs;
582}
583template <size_t I, size_t F, class Number,
584 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
585CONSTEXPR14 FixedPoint<I, F> operator*(FixedPoint<I, F> lhs, Number rhs) {
586 lhs *= FixedPoint<I, F>(rhs);
587 return lhs;
588}
589template <size_t I, size_t F, class Number,
590 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
591CONSTEXPR14 FixedPoint<I, F> operator/(FixedPoint<I, F> lhs, Number rhs) {
592 lhs /= FixedPoint<I, F>(rhs);
593 return lhs;
594}
595
596template <size_t I, size_t F, class Number,
597 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
598CONSTEXPR14 FixedPoint<I, F> operator+(Number lhs, FixedPoint<I, F> rhs) {
599 FixedPoint<I, F> tmp(lhs);
600 tmp += rhs;
601 return tmp;
602}
603template <size_t I, size_t F, class Number,
604 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
605CONSTEXPR14 FixedPoint<I, F> operator-(Number lhs, FixedPoint<I, F> rhs) {
606 FixedPoint<I, F> tmp(lhs);
607 tmp -= rhs;
608 return tmp;
609}
610template <size_t I, size_t F, class Number,
611 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
612CONSTEXPR14 FixedPoint<I, F> operator*(Number lhs, FixedPoint<I, F> rhs) {
613 FixedPoint<I, F> tmp(lhs);
614 tmp *= rhs;
615 return tmp;
616}
617template <size_t I, size_t F, class Number,
618 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
619CONSTEXPR14 FixedPoint<I, F> operator/(Number lhs, FixedPoint<I, F> rhs) {
620 FixedPoint<I, F> tmp(lhs);
621 tmp /= rhs;
622 return tmp;
623}
624
625// shift operators
626template <size_t I, size_t F, class Integer,
627 class = typename std::enable_if<std::is_integral<Integer>::value>::type>
628CONSTEXPR14 FixedPoint<I, F> operator<<(FixedPoint<I, F> lhs, Integer rhs) {
629 lhs <<= rhs;
630 return lhs;
631}
632template <size_t I, size_t F, class Integer,
633 class = typename std::enable_if<std::is_integral<Integer>::value>::type>
634CONSTEXPR14 FixedPoint<I, F> operator>>(FixedPoint<I, F> lhs, Integer rhs) {
635 lhs >>= rhs;
636 return lhs;
637}
638
639// comparison operators
640template <size_t I, size_t F, class Number,
641 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
642constexpr bool operator>(FixedPoint<I, F> lhs, Number rhs) {
643 return lhs > FixedPoint<I, F>(rhs);
644}
645template <size_t I, size_t F, class Number,
646 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
647constexpr bool operator<(FixedPoint<I, F> lhs, Number rhs) {
648 return lhs < FixedPoint<I, F>(rhs);
649}
650template <size_t I, size_t F, class Number,
651 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
652constexpr bool operator>=(FixedPoint<I, F> lhs, Number rhs) {
653 return lhs >= FixedPoint<I, F>(rhs);
654}
655template <size_t I, size_t F, class Number,
656 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
657constexpr bool operator<=(FixedPoint<I, F> lhs, Number rhs) {
658 return lhs <= FixedPoint<I, F>(rhs);
659}
660template <size_t I, size_t F, class Number,
661 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
662constexpr bool operator==(FixedPoint<I, F> lhs, Number rhs) {
663 return lhs == FixedPoint<I, F>(rhs);
664}
665template <size_t I, size_t F, class Number,
666 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
667constexpr bool operator!=(FixedPoint<I, F> lhs, Number rhs) {
668 return lhs != FixedPoint<I, F>(rhs);
669}
670
671template <size_t I, size_t F, class Number,
672 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
673constexpr bool operator>(Number lhs, FixedPoint<I, F> rhs) {
674 return FixedPoint<I, F>(lhs) > rhs;
675}
676template <size_t I, size_t F, class Number,
677 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
678constexpr bool operator<(Number lhs, FixedPoint<I, F> rhs) {
679 return FixedPoint<I, F>(lhs) < rhs;
680}
681template <size_t I, size_t F, class Number,
682 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
683constexpr bool operator>=(Number lhs, FixedPoint<I, F> rhs) {
684 return FixedPoint<I, F>(lhs) >= rhs;
685}
686template <size_t I, size_t F, class Number,
687 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
688constexpr bool operator<=(Number lhs, FixedPoint<I, F> rhs) {
689 return FixedPoint<I, F>(lhs) <= rhs;
690}
691template <size_t I, size_t F, class Number,
692 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
693constexpr bool operator==(Number lhs, FixedPoint<I, F> rhs) {
694 return FixedPoint<I, F>(lhs) == rhs;
695}
696template <size_t I, size_t F, class Number,
697 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
698constexpr bool operator!=(Number lhs, FixedPoint<I, F> rhs) {
699 return FixedPoint<I, F>(lhs) != rhs;
700}
701
702} // namespace Common
703
704#undef CONSTEXPR14
705
706#endif
diff --git a/src/common/fs/file.cpp b/src/common/fs/file.cpp
index 274f57659..fa8422c41 100644
--- a/src/common/fs/file.cpp
+++ b/src/common/fs/file.cpp
@@ -1,10 +1,8 @@
1// Copyright 2021 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#include "common/fs/file.h" 4#include "common/fs/file.h"
6#include "common/fs/fs.h" 5#include "common/fs/fs.h"
7#include "common/fs/path_util.h"
8#include "common/logging/log.h" 6#include "common/logging/log.h"
9 7
10#ifdef _WIN32 8#ifdef _WIN32
diff --git a/src/common/fs/file.h b/src/common/fs/file.h
index a4f7944cd..69b53384c 100644
--- a/src/common/fs/file.h
+++ b/src/common/fs/file.h
@@ -1,15 +1,12 @@
1// Copyright 2021 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#pragma once 4#pragma once
6 5
7#include <cstdio> 6#include <cstdio>
8#include <filesystem> 7#include <filesystem>
9#include <fstream>
10#include <span> 8#include <span>
11#include <type_traits> 9#include <type_traits>
12#include <vector>
13 10
14#include "common/concepts.h" 11#include "common/concepts.h"
15#include "common/fs/fs_types.h" 12#include "common/fs/fs_types.h"
diff --git a/src/common/fs/fs.cpp b/src/common/fs/fs.cpp
index 9089cad67..e1716c62d 100644
--- a/src/common/fs/fs.cpp
+++ b/src/common/fs/fs.cpp
@@ -1,6 +1,5 @@
1// Copyright 2021 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#include "common/fs/file.h" 4#include "common/fs/file.h"
6#include "common/fs/fs.h" 5#include "common/fs/fs.h"
diff --git a/src/common/fs/fs.h b/src/common/fs/fs.h
index 183126de3..ce3eb309a 100644
--- a/src/common/fs/fs.h
+++ b/src/common/fs/fs.h
@@ -1,6 +1,5 @@
1// Copyright 2021 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#pragma once 4#pragma once
6 5
diff --git a/src/common/fs/fs_paths.h b/src/common/fs/fs_paths.h
index 5d447f108..c77c112f1 100644
--- a/src/common/fs/fs_paths.h
+++ b/src/common/fs/fs_paths.h
@@ -1,6 +1,5 @@
1// Copyright 2021 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#pragma once 4#pragma once
6 5
diff --git a/src/common/fs/fs_types.h b/src/common/fs/fs_types.h
index 089980aee..5a4090c19 100644
--- a/src/common/fs/fs_types.h
+++ b/src/common/fs/fs_types.h
@@ -1,13 +1,11 @@
1// Copyright 2021 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#pragma once 4#pragma once
6 5
7#include <functional> 6#include <functional>
8 7
9#include "common/common_funcs.h" 8#include "common/common_funcs.h"
10#include "common/common_types.h"
11 9
12namespace Common::FS { 10namespace Common::FS {
13 11
diff --git a/src/common/fs/fs_util.cpp b/src/common/fs/fs_util.cpp
index 9f8671982..eb4ac1deb 100644
--- a/src/common/fs/fs_util.cpp
+++ b/src/common/fs/fs_util.cpp
@@ -1,6 +1,5 @@
1// Copyright 2021 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#include <algorithm> 4#include <algorithm>
6 5
@@ -16,6 +15,10 @@ std::u8string BufferToU8String(std::span<const u8> buffer) {
16 return std::u8string{buffer.begin(), std::ranges::find(buffer, u8{0})}; 15 return std::u8string{buffer.begin(), std::ranges::find(buffer, u8{0})};
17} 16}
18 17
18std::u8string_view BufferToU8StringView(std::span<const u8> buffer) {
19 return std::u8string_view{reinterpret_cast<const char8_t*>(buffer.data())};
20}
21
19std::string ToUTF8String(std::u8string_view u8_string) { 22std::string ToUTF8String(std::u8string_view u8_string) {
20 return std::string{u8_string.begin(), u8_string.end()}; 23 return std::string{u8_string.begin(), u8_string.end()};
21} 24}
@@ -24,6 +27,10 @@ std::string BufferToUTF8String(std::span<const u8> buffer) {
24 return std::string{buffer.begin(), std::ranges::find(buffer, u8{0})}; 27 return std::string{buffer.begin(), std::ranges::find(buffer, u8{0})};
25} 28}
26 29
30std::string_view BufferToUTF8StringView(std::span<const u8> buffer) {
31 return std::string_view{reinterpret_cast<const char*>(buffer.data())};
32}
33
27std::string PathToUTF8String(const std::filesystem::path& path) { 34std::string PathToUTF8String(const std::filesystem::path& path) {
28 return ToUTF8String(path.u8string()); 35 return ToUTF8String(path.u8string());
29} 36}
diff --git a/src/common/fs/fs_util.h b/src/common/fs/fs_util.h
index 1ec82eb35..2492a9f94 100644
--- a/src/common/fs/fs_util.h
+++ b/src/common/fs/fs_util.h
@@ -1,6 +1,5 @@
1// Copyright 2021 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#pragma once 4#pragma once
6 5
@@ -8,7 +7,6 @@
8#include <filesystem> 7#include <filesystem>
9#include <span> 8#include <span>
10#include <string> 9#include <string>
11#include <string_view>
12 10
13#include "common/common_types.h" 11#include "common/common_types.h"
14 12
@@ -38,6 +36,15 @@ concept IsChar = std::same_as<T, char>;
38[[nodiscard]] std::u8string BufferToU8String(std::span<const u8> buffer); 36[[nodiscard]] std::u8string BufferToU8String(std::span<const u8> buffer);
39 37
40/** 38/**
39 * Same as BufferToU8String, but returns a string view of the buffer.
40 *
41 * @param buffer Buffer of bytes
42 *
43 * @returns UTF-8 encoded std::u8string_view.
44 */
45[[nodiscard]] std::u8string_view BufferToU8StringView(std::span<const u8> buffer);
46
47/**
41 * Converts a std::u8string or std::u8string_view to a UTF-8 encoded std::string. 48 * Converts a std::u8string or std::u8string_view to a UTF-8 encoded std::string.
42 * 49 *
43 * @param u8_string UTF-8 encoded u8string 50 * @param u8_string UTF-8 encoded u8string
@@ -58,6 +65,15 @@ concept IsChar = std::same_as<T, char>;
58[[nodiscard]] std::string BufferToUTF8String(std::span<const u8> buffer); 65[[nodiscard]] std::string BufferToUTF8String(std::span<const u8> buffer);
59 66
60/** 67/**
68 * Same as BufferToUTF8String, but returns a string view of the buffer.
69 *
70 * @param buffer Buffer of bytes
71 *
72 * @returns UTF-8 encoded std::string_view.
73 */
74[[nodiscard]] std::string_view BufferToUTF8StringView(std::span<const u8> buffer);
75
76/**
61 * Converts a filesystem path to a UTF-8 encoded std::string. 77 * Converts a filesystem path to a UTF-8 encoded std::string.
62 * 78 *
63 * @param path Filesystem path 79 * @param path Filesystem path
diff --git a/src/common/fs/path_util.cpp b/src/common/fs/path_util.cpp
index 1bcb897b5..1074f2421 100644
--- a/src/common/fs/path_util.cpp
+++ b/src/common/fs/path_util.cpp
@@ -1,6 +1,5 @@
1// Copyright 2021 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#include <algorithm> 4#include <algorithm>
6#include <unordered_map> 5#include <unordered_map>
@@ -233,9 +232,7 @@ void SetYuzuPath(YuzuPath yuzu_path, const fs::path& new_path) {
233fs::path GetExeDirectory() { 232fs::path GetExeDirectory() {
234 wchar_t exe_path[MAX_PATH]; 233 wchar_t exe_path[MAX_PATH];
235 234
236 GetModuleFileNameW(nullptr, exe_path, MAX_PATH); 235 if (GetModuleFileNameW(nullptr, exe_path, MAX_PATH) == 0) {
237
238 if (!exe_path) {
239 LOG_ERROR(Common_Filesystem, 236 LOG_ERROR(Common_Filesystem,
240 "Failed to get the path to the executable of the current process"); 237 "Failed to get the path to the executable of the current process");
241 } 238 }
diff --git a/src/common/fs/path_util.h b/src/common/fs/path_util.h
index 0a9e3a145..13d713f1e 100644
--- a/src/common/fs/path_util.h
+++ b/src/common/fs/path_util.h
@@ -1,6 +1,5 @@
1// Copyright 2021 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#pragma once 4#pragma once
6 5
diff --git a/src/common/hash.h b/src/common/hash.h
index 298930702..e8fe78b07 100644
--- a/src/common/hash.h
+++ b/src/common/hash.h
@@ -1,6 +1,5 @@
1// Copyright 2015 Citra Emulator Project 1// SPDX-FileCopyrightText: 2015 Citra Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#pragma once 4#pragma once
6 5
@@ -19,4 +18,11 @@ struct PairHash {
19 } 18 }
20}; 19};
21 20
21template <typename T>
22struct IdentityHash {
23 [[nodiscard]] size_t operator()(T value) const noexcept {
24 return static_cast<size_t>(value);
25 }
26};
27
22} // namespace Common 28} // namespace Common
diff --git a/src/common/hex_util.cpp b/src/common/hex_util.cpp
index 74f52dd11..07053295c 100644
--- a/src/common/hex_util.cpp
+++ b/src/common/hex_util.cpp
@@ -1,6 +1,6 @@
1// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project 1// SPDX-FileCopyrightText: 2013 Dolphin Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-FileCopyrightText: 2014 Citra Emulator Project
3// Refer to the license.txt file included. 3// SPDX-License-Identifier: GPL-2.0-or-later
4 4
5#include "common/hex_util.h" 5#include "common/hex_util.h"
6 6
diff --git a/src/common/hex_util.h b/src/common/hex_util.h
index 5e9b6ef8b..a00904939 100644
--- a/src/common/hex_util.h
+++ b/src/common/hex_util.h
@@ -1,13 +1,12 @@
1// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project 1// SPDX-FileCopyrightText: 2013 Dolphin Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-FileCopyrightText: 2014 Citra Emulator Project
3// Refer to the license.txt file included. 3// SPDX-License-Identifier: GPL-2.0-or-later
4 4
5#pragma once 5#pragma once
6 6
7#include <array> 7#include <array>
8#include <cstddef> 8#include <cstddef>
9#include <string> 9#include <string>
10#include <type_traits>
11#include <vector> 10#include <vector>
12#include <fmt/format.h> 11#include <fmt/format.h>
13#include "common/common_types.h" 12#include "common/common_types.h"
diff --git a/src/common/host_memory.cpp b/src/common/host_memory.cpp
index 28949fe5e..7f9659612 100644
--- a/src/common/host_memory.cpp
+++ b/src/common/host_memory.cpp
@@ -1,6 +1,5 @@
1// Copyright 2021 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#ifdef _WIN32 4#ifdef _WIN32
6 5
@@ -18,6 +17,7 @@
18#include <fcntl.h> 17#include <fcntl.h>
19#include <sys/mman.h> 18#include <sys/mman.h>
20#include <unistd.h> 19#include <unistd.h>
20#include "common/scope_exit.h"
21 21
22#endif // ^^^ Linux ^^^ 22#endif // ^^^ Linux ^^^
23 23
@@ -27,7 +27,6 @@
27#include "common/assert.h" 27#include "common/assert.h"
28#include "common/host_memory.h" 28#include "common/host_memory.h"
29#include "common/logging/log.h" 29#include "common/logging/log.h"
30#include "common/scope_exit.h"
31 30
32namespace Common { 31namespace Common {
33 32
@@ -149,7 +148,7 @@ public:
149 } 148 }
150 149
151 void Unmap(size_t virtual_offset, size_t length) { 150 void Unmap(size_t virtual_offset, size_t length) {
152 std::lock_guard lock{placeholder_mutex}; 151 std::scoped_lock lock{placeholder_mutex};
153 152
154 // Unmap until there are no more placeholders 153 // Unmap until there are no more placeholders
155 while (UnmapOnePlaceholder(virtual_offset, length)) { 154 while (UnmapOnePlaceholder(virtual_offset, length)) {
@@ -169,7 +168,7 @@ public:
169 } 168 }
170 const size_t virtual_end = virtual_offset + length; 169 const size_t virtual_end = virtual_offset + length;
171 170
172 std::lock_guard lock{placeholder_mutex}; 171 std::scoped_lock lock{placeholder_mutex};
173 auto [it, end] = placeholders.equal_range({virtual_offset, virtual_end}); 172 auto [it, end] = placeholders.equal_range({virtual_offset, virtual_end});
174 while (it != end) { 173 while (it != end) {
175 const size_t offset = std::max(it->lower(), virtual_offset); 174 const size_t offset = std::max(it->lower(), virtual_offset);
@@ -327,8 +326,8 @@ private:
327 bool IsNiechePlaceholder(size_t virtual_offset, size_t length) const { 326 bool IsNiechePlaceholder(size_t virtual_offset, size_t length) const {
328 const auto it = placeholders.upper_bound({virtual_offset, virtual_offset + length}); 327 const auto it = placeholders.upper_bound({virtual_offset, virtual_offset + length});
329 if (it != placeholders.end() && it->lower() == virtual_offset + length) { 328 if (it != placeholders.end() && it->lower() == virtual_offset + length) {
330 const bool is_root = it == placeholders.begin() && virtual_offset == 0; 329 return it == placeholders.begin() ? virtual_offset == 0
331 return is_root || std::prev(it)->upper() == virtual_offset; 330 : std::prev(it)->upper() == virtual_offset;
332 } 331 }
333 return false; 332 return false;
334 } 333 }
diff --git a/src/common/host_memory.h b/src/common/host_memory.h
index 9b8326d0f..447975ded 100644
--- a/src/common/host_memory.h
+++ b/src/common/host_memory.h
@@ -1,6 +1,5 @@
1// Copyright 2019 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#pragma once 4#pragma once
6 5
diff --git a/src/common/input.h b/src/common/input.h
index 54fcb24b0..bfa0639f5 100644
--- a/src/common/input.h
+++ b/src/common/input.h
@@ -1,6 +1,5 @@
1// Copyright 2017 Citra Emulator Project 1// SPDX-FileCopyrightText: 2017 Citra Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#pragma once 4#pragma once
6 5
@@ -28,7 +27,7 @@ enum class InputType {
28 Color, 27 Color,
29 Vibration, 28 Vibration,
30 Nfc, 29 Nfc,
31 Ir, 30 IrSensor,
32}; 31};
33 32
34// Internal battery charge level 33// Internal battery charge level
@@ -53,6 +52,15 @@ enum class PollingMode {
53 IR, 52 IR,
54}; 53};
55 54
55enum class CameraFormat {
56 Size320x240,
57 Size160x120,
58 Size80x60,
59 Size40x30,
60 Size20x15,
61 None,
62};
63
56// Vibration reply from the controller 64// Vibration reply from the controller
57enum class VibrationError { 65enum class VibrationError {
58 None, 66 None,
@@ -68,10 +76,31 @@ enum class PollingError {
68 Unknown, 76 Unknown,
69}; 77};
70 78
79// Nfc reply from the controller
80enum class NfcState {
81 Success,
82 NewAmiibo,
83 WaitingForAmiibo,
84 AmiiboRemoved,
85 NotAnAmiibo,
86 NotSupported,
87 WrongDeviceState,
88 WriteFailed,
89 Unknown,
90};
91
92// Ir camera reply from the controller
93enum class CameraError {
94 None,
95 NotSupported,
96 Unknown,
97};
98
71// Hint for amplification curve to be used 99// Hint for amplification curve to be used
72enum class VibrationAmplificationType { 100enum class VibrationAmplificationType {
73 Linear, 101 Linear,
74 Exponential, 102 Exponential,
103 Test,
75}; 104};
76 105
77// Analog properties for calibration 106// Analog properties for calibration
@@ -86,6 +115,8 @@ struct AnalogProperties {
86 float offset{}; 115 float offset{};
87 // Invert direction of the sensor data 116 // Invert direction of the sensor data
88 bool inverted{}; 117 bool inverted{};
118 // Press once to activate, press again to release
119 bool toggle{};
89}; 120};
90 121
91// Single analog sensor data 122// Single analog sensor data
@@ -99,8 +130,11 @@ struct AnalogStatus {
99struct ButtonStatus { 130struct ButtonStatus {
100 Common::UUID uuid{}; 131 Common::UUID uuid{};
101 bool value{}; 132 bool value{};
133 // Invert value of the button
102 bool inverted{}; 134 bool inverted{};
135 // Press once to activate, press again to release
103 bool toggle{}; 136 bool toggle{};
137 // Internal lock for the toggle status
104 bool locked{}; 138 bool locked{};
105}; 139};
106 140
@@ -175,6 +209,17 @@ struct LedStatus {
175 bool led_4{}; 209 bool led_4{};
176}; 210};
177 211
212// Raw data fom camera
213struct CameraStatus {
214 CameraFormat format{CameraFormat::None};
215 std::vector<u8> data{};
216};
217
218struct NfcStatus {
219 NfcState state{};
220 std::vector<u8> data{};
221};
222
178// List of buttons to be passed to Qt that can be translated 223// List of buttons to be passed to Qt that can be translated
179enum class ButtonNames { 224enum class ButtonNames {
180 Undefined, 225 Undefined,
@@ -232,6 +277,8 @@ struct CallbackStatus {
232 BodyColorStatus color_status{}; 277 BodyColorStatus color_status{};
233 BatteryStatus battery_status{}; 278 BatteryStatus battery_status{};
234 VibrationStatus vibration_status{}; 279 VibrationStatus vibration_status{};
280 CameraStatus camera_status{};
281 NfcStatus nfc_status{};
235}; 282};
236 283
237// Triggered once every input change 284// Triggered once every input change
@@ -280,6 +327,18 @@ public:
280 virtual PollingError SetPollingMode([[maybe_unused]] PollingMode polling_mode) { 327 virtual PollingError SetPollingMode([[maybe_unused]] PollingMode polling_mode) {
281 return PollingError::NotSupported; 328 return PollingError::NotSupported;
282 } 329 }
330
331 virtual CameraError SetCameraFormat([[maybe_unused]] CameraFormat camera_format) {
332 return CameraError::NotSupported;
333 }
334
335 virtual NfcState SupportsNfc() const {
336 return NfcState::NotSupported;
337 }
338
339 virtual NfcState WriteNfcData([[maybe_unused]] const std::vector<u8>& data) {
340 return NfcState::NotSupported;
341 }
283}; 342};
284 343
285/// An abstract class template for a factory that can create input devices. 344/// An abstract class template for a factory that can create input devices.
diff --git a/src/common/intrusive_red_black_tree.h b/src/common/intrusive_red_black_tree.h
index 3173cc449..93046615e 100644
--- a/src/common/intrusive_red_black_tree.h
+++ b/src/common/intrusive_red_black_tree.h
@@ -1,9 +1,9 @@
1// Copyright 2021 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#pragma once 4#pragma once
6 5
6#include "common/common_funcs.h"
7#include "common/parent_of_member.h" 7#include "common/parent_of_member.h"
8#include "common/tree.h" 8#include "common/tree.h"
9 9
@@ -15,32 +15,33 @@ class IntrusiveRedBlackTreeImpl;
15 15
16} 16}
17 17
18#pragma pack(push, 4)
18struct IntrusiveRedBlackTreeNode { 19struct IntrusiveRedBlackTreeNode {
20 YUZU_NON_COPYABLE(IntrusiveRedBlackTreeNode);
21
19public: 22public:
20 using EntryType = RBEntry<IntrusiveRedBlackTreeNode>; 23 using RBEntry = freebsd::RBEntry<IntrusiveRedBlackTreeNode>;
21 24
22 constexpr IntrusiveRedBlackTreeNode() = default; 25private:
26 RBEntry m_entry;
23 27
24 void SetEntry(const EntryType& new_entry) { 28public:
25 entry = new_entry; 29 explicit IntrusiveRedBlackTreeNode() = default;
26 }
27 30
28 [[nodiscard]] EntryType& GetEntry() { 31 [[nodiscard]] constexpr RBEntry& GetRBEntry() {
29 return entry; 32 return m_entry;
30 } 33 }
31 34 [[nodiscard]] constexpr const RBEntry& GetRBEntry() const {
32 [[nodiscard]] const EntryType& GetEntry() const { 35 return m_entry;
33 return entry;
34 } 36 }
35 37
36private: 38 constexpr void SetRBEntry(const RBEntry& entry) {
37 EntryType entry{}; 39 m_entry = entry;
38 40 }
39 friend class impl::IntrusiveRedBlackTreeImpl;
40
41 template <class, class, class>
42 friend class IntrusiveRedBlackTree;
43}; 41};
42static_assert(sizeof(IntrusiveRedBlackTreeNode) ==
43 3 * sizeof(void*) + std::max<size_t>(sizeof(freebsd::RBColor), 4));
44#pragma pack(pop)
44 45
45template <class T, class Traits, class Comparator> 46template <class T, class Traits, class Comparator>
46class IntrusiveRedBlackTree; 47class IntrusiveRedBlackTree;
@@ -48,12 +49,17 @@ class IntrusiveRedBlackTree;
48namespace impl { 49namespace impl {
49 50
50class IntrusiveRedBlackTreeImpl { 51class IntrusiveRedBlackTreeImpl {
52 YUZU_NON_COPYABLE(IntrusiveRedBlackTreeImpl);
53
51private: 54private:
52 template <class, class, class> 55 template <class, class, class>
53 friend class ::Common::IntrusiveRedBlackTree; 56 friend class ::Common::IntrusiveRedBlackTree;
54 57
55 using RootType = RBHead<IntrusiveRedBlackTreeNode>; 58private:
56 RootType root; 59 using RootType = freebsd::RBHead<IntrusiveRedBlackTreeNode>;
60
61private:
62 RootType m_root;
57 63
58public: 64public:
59 template <bool Const> 65 template <bool Const>
@@ -81,149 +87,150 @@ public:
81 IntrusiveRedBlackTreeImpl::reference>; 87 IntrusiveRedBlackTreeImpl::reference>;
82 88
83 private: 89 private:
84 pointer node; 90 pointer m_node;
85 91
86 public: 92 public:
87 explicit Iterator(pointer n) : node(n) {} 93 constexpr explicit Iterator(pointer n) : m_node(n) {}
88 94
89 bool operator==(const Iterator& rhs) const { 95 constexpr bool operator==(const Iterator& rhs) const {
90 return this->node == rhs.node; 96 return m_node == rhs.m_node;
91 } 97 }
92 98
93 bool operator!=(const Iterator& rhs) const { 99 constexpr bool operator!=(const Iterator& rhs) const {
94 return !(*this == rhs); 100 return !(*this == rhs);
95 } 101 }
96 102
97 pointer operator->() const { 103 constexpr pointer operator->() const {
98 return this->node; 104 return m_node;
99 } 105 }
100 106
101 reference operator*() const { 107 constexpr reference operator*() const {
102 return *this->node; 108 return *m_node;
103 } 109 }
104 110
105 Iterator& operator++() { 111 constexpr Iterator& operator++() {
106 this->node = GetNext(this->node); 112 m_node = GetNext(m_node);
107 return *this; 113 return *this;
108 } 114 }
109 115
110 Iterator& operator--() { 116 constexpr Iterator& operator--() {
111 this->node = GetPrev(this->node); 117 m_node = GetPrev(m_node);
112 return *this; 118 return *this;
113 } 119 }
114 120
115 Iterator operator++(int) { 121 constexpr Iterator operator++(int) {
116 const Iterator it{*this}; 122 const Iterator it{*this};
117 ++(*this); 123 ++(*this);
118 return it; 124 return it;
119 } 125 }
120 126
121 Iterator operator--(int) { 127 constexpr Iterator operator--(int) {
122 const Iterator it{*this}; 128 const Iterator it{*this};
123 --(*this); 129 --(*this);
124 return it; 130 return it;
125 } 131 }
126 132
127 operator Iterator<true>() const { 133 constexpr operator Iterator<true>() const {
128 return Iterator<true>(this->node); 134 return Iterator<true>(m_node);
129 } 135 }
130 }; 136 };
131 137
132private: 138private:
133 // Define accessors using RB_* functions. 139 constexpr bool EmptyImpl() const {
134 bool EmptyImpl() const { 140 return m_root.IsEmpty();
135 return root.IsEmpty();
136 } 141 }
137 142
138 IntrusiveRedBlackTreeNode* GetMinImpl() const { 143 constexpr IntrusiveRedBlackTreeNode* GetMinImpl() const {
139 return RB_MIN(const_cast<RootType*>(&root)); 144 return freebsd::RB_MIN(const_cast<RootType&>(m_root));
140 } 145 }
141 146
142 IntrusiveRedBlackTreeNode* GetMaxImpl() const { 147 constexpr IntrusiveRedBlackTreeNode* GetMaxImpl() const {
143 return RB_MAX(const_cast<RootType*>(&root)); 148 return freebsd::RB_MAX(const_cast<RootType&>(m_root));
144 } 149 }
145 150
146 IntrusiveRedBlackTreeNode* RemoveImpl(IntrusiveRedBlackTreeNode* node) { 151 constexpr IntrusiveRedBlackTreeNode* RemoveImpl(IntrusiveRedBlackTreeNode* node) {
147 return RB_REMOVE(&root, node); 152 return freebsd::RB_REMOVE(m_root, node);
148 } 153 }
149 154
150public: 155public:
151 static IntrusiveRedBlackTreeNode* GetNext(IntrusiveRedBlackTreeNode* node) { 156 static constexpr IntrusiveRedBlackTreeNode* GetNext(IntrusiveRedBlackTreeNode* node) {
152 return RB_NEXT(node); 157 return freebsd::RB_NEXT(node);
153 } 158 }
154 159
155 static IntrusiveRedBlackTreeNode* GetPrev(IntrusiveRedBlackTreeNode* node) { 160 static constexpr IntrusiveRedBlackTreeNode* GetPrev(IntrusiveRedBlackTreeNode* node) {
156 return RB_PREV(node); 161 return freebsd::RB_PREV(node);
157 } 162 }
158 163
159 static const IntrusiveRedBlackTreeNode* GetNext(const IntrusiveRedBlackTreeNode* node) { 164 static constexpr IntrusiveRedBlackTreeNode const* GetNext(
165 IntrusiveRedBlackTreeNode const* node) {
160 return static_cast<const IntrusiveRedBlackTreeNode*>( 166 return static_cast<const IntrusiveRedBlackTreeNode*>(
161 GetNext(const_cast<IntrusiveRedBlackTreeNode*>(node))); 167 GetNext(const_cast<IntrusiveRedBlackTreeNode*>(node)));
162 } 168 }
163 169
164 static const IntrusiveRedBlackTreeNode* GetPrev(const IntrusiveRedBlackTreeNode* node) { 170 static constexpr IntrusiveRedBlackTreeNode const* GetPrev(
171 IntrusiveRedBlackTreeNode const* node) {
165 return static_cast<const IntrusiveRedBlackTreeNode*>( 172 return static_cast<const IntrusiveRedBlackTreeNode*>(
166 GetPrev(const_cast<IntrusiveRedBlackTreeNode*>(node))); 173 GetPrev(const_cast<IntrusiveRedBlackTreeNode*>(node)));
167 } 174 }
168 175
169public: 176public:
170 constexpr IntrusiveRedBlackTreeImpl() {} 177 constexpr IntrusiveRedBlackTreeImpl() = default;
171 178
172 // Iterator accessors. 179 // Iterator accessors.
173 iterator begin() { 180 constexpr iterator begin() {
174 return iterator(this->GetMinImpl()); 181 return iterator(this->GetMinImpl());
175 } 182 }
176 183
177 const_iterator begin() const { 184 constexpr const_iterator begin() const {
178 return const_iterator(this->GetMinImpl()); 185 return const_iterator(this->GetMinImpl());
179 } 186 }
180 187
181 iterator end() { 188 constexpr iterator end() {
182 return iterator(static_cast<IntrusiveRedBlackTreeNode*>(nullptr)); 189 return iterator(static_cast<IntrusiveRedBlackTreeNode*>(nullptr));
183 } 190 }
184 191
185 const_iterator end() const { 192 constexpr const_iterator end() const {
186 return const_iterator(static_cast<const IntrusiveRedBlackTreeNode*>(nullptr)); 193 return const_iterator(static_cast<const IntrusiveRedBlackTreeNode*>(nullptr));
187 } 194 }
188 195
189 const_iterator cbegin() const { 196 constexpr const_iterator cbegin() const {
190 return this->begin(); 197 return this->begin();
191 } 198 }
192 199
193 const_iterator cend() const { 200 constexpr const_iterator cend() const {
194 return this->end(); 201 return this->end();
195 } 202 }
196 203
197 iterator iterator_to(reference ref) { 204 constexpr iterator iterator_to(reference ref) {
198 return iterator(&ref); 205 return iterator(std::addressof(ref));
199 } 206 }
200 207
201 const_iterator iterator_to(const_reference ref) const { 208 constexpr const_iterator iterator_to(const_reference ref) const {
202 return const_iterator(&ref); 209 return const_iterator(std::addressof(ref));
203 } 210 }
204 211
205 // Content management. 212 // Content management.
206 bool empty() const { 213 constexpr bool empty() const {
207 return this->EmptyImpl(); 214 return this->EmptyImpl();
208 } 215 }
209 216
210 reference back() { 217 constexpr reference back() {
211 return *this->GetMaxImpl(); 218 return *this->GetMaxImpl();
212 } 219 }
213 220
214 const_reference back() const { 221 constexpr const_reference back() const {
215 return *this->GetMaxImpl(); 222 return *this->GetMaxImpl();
216 } 223 }
217 224
218 reference front() { 225 constexpr reference front() {
219 return *this->GetMinImpl(); 226 return *this->GetMinImpl();
220 } 227 }
221 228
222 const_reference front() const { 229 constexpr const_reference front() const {
223 return *this->GetMinImpl(); 230 return *this->GetMinImpl();
224 } 231 }
225 232
226 iterator erase(iterator it) { 233 constexpr iterator erase(iterator it) {
227 auto cur = std::addressof(*it); 234 auto cur = std::addressof(*it);
228 auto next = GetNext(cur); 235 auto next = GetNext(cur);
229 this->RemoveImpl(cur); 236 this->RemoveImpl(cur);
@@ -234,16 +241,16 @@ public:
234} // namespace impl 241} // namespace impl
235 242
236template <typename T> 243template <typename T>
237concept HasLightCompareType = requires { 244concept HasRedBlackKeyType = requires {
238 { std::is_same<typename T::LightCompareType, void>::value } -> std::convertible_to<bool>; 245 { std::is_same<typename T::RedBlackKeyType, void>::value } -> std::convertible_to<bool>;
239}; 246};
240 247
241namespace impl { 248namespace impl {
242 249
243 template <typename T, typename Default> 250 template <typename T, typename Default>
244 consteval auto* GetLightCompareType() { 251 consteval auto* GetRedBlackKeyType() {
245 if constexpr (HasLightCompareType<T>) { 252 if constexpr (HasRedBlackKeyType<T>) {
246 return static_cast<typename T::LightCompareType*>(nullptr); 253 return static_cast<typename T::RedBlackKeyType*>(nullptr);
247 } else { 254 } else {
248 return static_cast<Default*>(nullptr); 255 return static_cast<Default*>(nullptr);
249 } 256 }
@@ -252,16 +259,17 @@ namespace impl {
252} // namespace impl 259} // namespace impl
253 260
254template <typename T, typename Default> 261template <typename T, typename Default>
255using LightCompareType = std::remove_pointer_t<decltype(impl::GetLightCompareType<T, Default>())>; 262using RedBlackKeyType = std::remove_pointer_t<decltype(impl::GetRedBlackKeyType<T, Default>())>;
256 263
257template <class T, class Traits, class Comparator> 264template <class T, class Traits, class Comparator>
258class IntrusiveRedBlackTree { 265class IntrusiveRedBlackTree {
266 YUZU_NON_COPYABLE(IntrusiveRedBlackTree);
259 267
260public: 268public:
261 using ImplType = impl::IntrusiveRedBlackTreeImpl; 269 using ImplType = impl::IntrusiveRedBlackTreeImpl;
262 270
263private: 271private:
264 ImplType impl{}; 272 ImplType m_impl;
265 273
266public: 274public:
267 template <bool Const> 275 template <bool Const>
@@ -277,9 +285,9 @@ public:
277 using iterator = Iterator<false>; 285 using iterator = Iterator<false>;
278 using const_iterator = Iterator<true>; 286 using const_iterator = Iterator<true>;
279 287
280 using light_value_type = LightCompareType<Comparator, value_type>; 288 using key_type = RedBlackKeyType<Comparator, value_type>;
281 using const_light_pointer = const light_value_type*; 289 using const_key_pointer = const key_type*;
282 using const_light_reference = const light_value_type&; 290 using const_key_reference = const key_type&;
283 291
284 template <bool Const> 292 template <bool Const>
285 class Iterator { 293 class Iterator {
@@ -298,183 +306,201 @@ public:
298 IntrusiveRedBlackTree::reference>; 306 IntrusiveRedBlackTree::reference>;
299 307
300 private: 308 private:
301 ImplIterator iterator; 309 ImplIterator m_impl;
302 310
303 private: 311 private:
304 explicit Iterator(ImplIterator it) : iterator(it) {} 312 constexpr explicit Iterator(ImplIterator it) : m_impl(it) {}
305 313
306 explicit Iterator(typename std::conditional<Const, ImplType::const_iterator, 314 constexpr explicit Iterator(typename ImplIterator::pointer p) : m_impl(p) {}
307 ImplType::iterator>::type::pointer ptr)
308 : iterator(ptr) {}
309 315
310 ImplIterator GetImplIterator() const { 316 constexpr ImplIterator GetImplIterator() const {
311 return this->iterator; 317 return m_impl;
312 } 318 }
313 319
314 public: 320 public:
315 bool operator==(const Iterator& rhs) const { 321 constexpr bool operator==(const Iterator& rhs) const {
316 return this->iterator == rhs.iterator; 322 return m_impl == rhs.m_impl;
317 } 323 }
318 324
319 bool operator!=(const Iterator& rhs) const { 325 constexpr bool operator!=(const Iterator& rhs) const {
320 return !(*this == rhs); 326 return !(*this == rhs);
321 } 327 }
322 328
323 pointer operator->() const { 329 constexpr pointer operator->() const {
324 return Traits::GetParent(std::addressof(*this->iterator)); 330 return Traits::GetParent(std::addressof(*m_impl));
325 } 331 }
326 332
327 reference operator*() const { 333 constexpr reference operator*() const {
328 return *Traits::GetParent(std::addressof(*this->iterator)); 334 return *Traits::GetParent(std::addressof(*m_impl));
329 } 335 }
330 336
331 Iterator& operator++() { 337 constexpr Iterator& operator++() {
332 ++this->iterator; 338 ++m_impl;
333 return *this; 339 return *this;
334 } 340 }
335 341
336 Iterator& operator--() { 342 constexpr Iterator& operator--() {
337 --this->iterator; 343 --m_impl;
338 return *this; 344 return *this;
339 } 345 }
340 346
341 Iterator operator++(int) { 347 constexpr Iterator operator++(int) {
342 const Iterator it{*this}; 348 const Iterator it{*this};
343 ++this->iterator; 349 ++m_impl;
344 return it; 350 return it;
345 } 351 }
346 352
347 Iterator operator--(int) { 353 constexpr Iterator operator--(int) {
348 const Iterator it{*this}; 354 const Iterator it{*this};
349 --this->iterator; 355 --m_impl;
350 return it; 356 return it;
351 } 357 }
352 358
353 operator Iterator<true>() const { 359 constexpr operator Iterator<true>() const {
354 return Iterator<true>(this->iterator); 360 return Iterator<true>(m_impl);
355 } 361 }
356 }; 362 };
357 363
358private: 364private:
359 static int CompareImpl(const IntrusiveRedBlackTreeNode* lhs, 365 static constexpr int CompareImpl(const IntrusiveRedBlackTreeNode* lhs,
360 const IntrusiveRedBlackTreeNode* rhs) { 366 const IntrusiveRedBlackTreeNode* rhs) {
361 return Comparator::Compare(*Traits::GetParent(lhs), *Traits::GetParent(rhs)); 367 return Comparator::Compare(*Traits::GetParent(lhs), *Traits::GetParent(rhs));
362 } 368 }
363 369
364 static int LightCompareImpl(const void* elm, const IntrusiveRedBlackTreeNode* rhs) { 370 static constexpr int CompareKeyImpl(const_key_reference key,
365 return Comparator::Compare(*static_cast<const_light_pointer>(elm), *Traits::GetParent(rhs)); 371 const IntrusiveRedBlackTreeNode* rhs) {
372 return Comparator::Compare(key, *Traits::GetParent(rhs));
366 } 373 }
367 374
368 // Define accessors using RB_* functions. 375 // Define accessors using RB_* functions.
369 IntrusiveRedBlackTreeNode* InsertImpl(IntrusiveRedBlackTreeNode* node) { 376 constexpr IntrusiveRedBlackTreeNode* InsertImpl(IntrusiveRedBlackTreeNode* node) {
370 return RB_INSERT(&impl.root, node, CompareImpl); 377 return freebsd::RB_INSERT(m_impl.m_root, node, CompareImpl);
371 } 378 }
372 379
373 IntrusiveRedBlackTreeNode* FindImpl(const IntrusiveRedBlackTreeNode* node) const { 380 constexpr IntrusiveRedBlackTreeNode* FindImpl(IntrusiveRedBlackTreeNode const* node) const {
374 return RB_FIND(const_cast<ImplType::RootType*>(&impl.root), 381 return freebsd::RB_FIND(const_cast<ImplType::RootType&>(m_impl.m_root),
375 const_cast<IntrusiveRedBlackTreeNode*>(node), CompareImpl); 382 const_cast<IntrusiveRedBlackTreeNode*>(node), CompareImpl);
376 } 383 }
377 384
378 IntrusiveRedBlackTreeNode* NFindImpl(const IntrusiveRedBlackTreeNode* node) const { 385 constexpr IntrusiveRedBlackTreeNode* NFindImpl(IntrusiveRedBlackTreeNode const* node) const {
379 return RB_NFIND(const_cast<ImplType::RootType*>(&impl.root), 386 return freebsd::RB_NFIND(const_cast<ImplType::RootType&>(m_impl.m_root),
380 const_cast<IntrusiveRedBlackTreeNode*>(node), CompareImpl); 387 const_cast<IntrusiveRedBlackTreeNode*>(node), CompareImpl);
381 } 388 }
382 389
383 IntrusiveRedBlackTreeNode* FindLightImpl(const_light_pointer lelm) const { 390 constexpr IntrusiveRedBlackTreeNode* FindKeyImpl(const_key_reference key) const {
384 return RB_FIND_LIGHT(const_cast<ImplType::RootType*>(&impl.root), 391 return freebsd::RB_FIND_KEY(const_cast<ImplType::RootType&>(m_impl.m_root), key,
385 static_cast<const void*>(lelm), LightCompareImpl); 392 CompareKeyImpl);
386 } 393 }
387 394
388 IntrusiveRedBlackTreeNode* NFindLightImpl(const_light_pointer lelm) const { 395 constexpr IntrusiveRedBlackTreeNode* NFindKeyImpl(const_key_reference key) const {
389 return RB_NFIND_LIGHT(const_cast<ImplType::RootType*>(&impl.root), 396 return freebsd::RB_NFIND_KEY(const_cast<ImplType::RootType&>(m_impl.m_root), key,
390 static_cast<const void*>(lelm), LightCompareImpl); 397 CompareKeyImpl);
398 }
399
400 constexpr IntrusiveRedBlackTreeNode* FindExistingImpl(
401 IntrusiveRedBlackTreeNode const* node) const {
402 return freebsd::RB_FIND_EXISTING(const_cast<ImplType::RootType&>(m_impl.m_root),
403 const_cast<IntrusiveRedBlackTreeNode*>(node), CompareImpl);
404 }
405
406 constexpr IntrusiveRedBlackTreeNode* FindExistingKeyImpl(const_key_reference key) const {
407 return freebsd::RB_FIND_EXISTING_KEY(const_cast<ImplType::RootType&>(m_impl.m_root), key,
408 CompareKeyImpl);
391 } 409 }
392 410
393public: 411public:
394 constexpr IntrusiveRedBlackTree() = default; 412 constexpr IntrusiveRedBlackTree() = default;
395 413
396 // Iterator accessors. 414 // Iterator accessors.
397 iterator begin() { 415 constexpr iterator begin() {
398 return iterator(this->impl.begin()); 416 return iterator(m_impl.begin());
399 } 417 }
400 418
401 const_iterator begin() const { 419 constexpr const_iterator begin() const {
402 return const_iterator(this->impl.begin()); 420 return const_iterator(m_impl.begin());
403 } 421 }
404 422
405 iterator end() { 423 constexpr iterator end() {
406 return iterator(this->impl.end()); 424 return iterator(m_impl.end());
407 } 425 }
408 426
409 const_iterator end() const { 427 constexpr const_iterator end() const {
410 return const_iterator(this->impl.end()); 428 return const_iterator(m_impl.end());
411 } 429 }
412 430
413 const_iterator cbegin() const { 431 constexpr const_iterator cbegin() const {
414 return this->begin(); 432 return this->begin();
415 } 433 }
416 434
417 const_iterator cend() const { 435 constexpr const_iterator cend() const {
418 return this->end(); 436 return this->end();
419 } 437 }
420 438
421 iterator iterator_to(reference ref) { 439 constexpr iterator iterator_to(reference ref) {
422 return iterator(this->impl.iterator_to(*Traits::GetNode(std::addressof(ref)))); 440 return iterator(m_impl.iterator_to(*Traits::GetNode(std::addressof(ref))));
423 } 441 }
424 442
425 const_iterator iterator_to(const_reference ref) const { 443 constexpr const_iterator iterator_to(const_reference ref) const {
426 return const_iterator(this->impl.iterator_to(*Traits::GetNode(std::addressof(ref)))); 444 return const_iterator(m_impl.iterator_to(*Traits::GetNode(std::addressof(ref))));
427 } 445 }
428 446
429 // Content management. 447 // Content management.
430 bool empty() const { 448 constexpr bool empty() const {
431 return this->impl.empty(); 449 return m_impl.empty();
432 } 450 }
433 451
434 reference back() { 452 constexpr reference back() {
435 return *Traits::GetParent(std::addressof(this->impl.back())); 453 return *Traits::GetParent(std::addressof(m_impl.back()));
436 } 454 }
437 455
438 const_reference back() const { 456 constexpr const_reference back() const {
439 return *Traits::GetParent(std::addressof(this->impl.back())); 457 return *Traits::GetParent(std::addressof(m_impl.back()));
440 } 458 }
441 459
442 reference front() { 460 constexpr reference front() {
443 return *Traits::GetParent(std::addressof(this->impl.front())); 461 return *Traits::GetParent(std::addressof(m_impl.front()));
444 } 462 }
445 463
446 const_reference front() const { 464 constexpr const_reference front() const {
447 return *Traits::GetParent(std::addressof(this->impl.front())); 465 return *Traits::GetParent(std::addressof(m_impl.front()));
448 } 466 }
449 467
450 iterator erase(iterator it) { 468 constexpr iterator erase(iterator it) {
451 return iterator(this->impl.erase(it.GetImplIterator())); 469 return iterator(m_impl.erase(it.GetImplIterator()));
452 } 470 }
453 471
454 iterator insert(reference ref) { 472 constexpr iterator insert(reference ref) {
455 ImplType::pointer node = Traits::GetNode(std::addressof(ref)); 473 ImplType::pointer node = Traits::GetNode(std::addressof(ref));
456 this->InsertImpl(node); 474 this->InsertImpl(node);
457 return iterator(node); 475 return iterator(node);
458 } 476 }
459 477
460 iterator find(const_reference ref) const { 478 constexpr iterator find(const_reference ref) const {
461 return iterator(this->FindImpl(Traits::GetNode(std::addressof(ref)))); 479 return iterator(this->FindImpl(Traits::GetNode(std::addressof(ref))));
462 } 480 }
463 481
464 iterator nfind(const_reference ref) const { 482 constexpr iterator nfind(const_reference ref) const {
465 return iterator(this->NFindImpl(Traits::GetNode(std::addressof(ref)))); 483 return iterator(this->NFindImpl(Traits::GetNode(std::addressof(ref))));
466 } 484 }
467 485
468 iterator find_light(const_light_reference ref) const { 486 constexpr iterator find_key(const_key_reference ref) const {
469 return iterator(this->FindLightImpl(std::addressof(ref))); 487 return iterator(this->FindKeyImpl(ref));
488 }
489
490 constexpr iterator nfind_key(const_key_reference ref) const {
491 return iterator(this->NFindKeyImpl(ref));
492 }
493
494 constexpr iterator find_existing(const_reference ref) const {
495 return iterator(this->FindExistingImpl(Traits::GetNode(std::addressof(ref))));
470 } 496 }
471 497
472 iterator nfind_light(const_light_reference ref) const { 498 constexpr iterator find_existing_key(const_key_reference ref) const {
473 return iterator(this->NFindLightImpl(std::addressof(ref))); 499 return iterator(this->FindExistingKeyImpl(ref));
474 } 500 }
475}; 501};
476 502
477template <auto T, class Derived = impl::GetParentType<T>> 503template <auto T, class Derived = Common::impl::GetParentType<T>>
478class IntrusiveRedBlackTreeMemberTraits; 504class IntrusiveRedBlackTreeMemberTraits;
479 505
480template <class Parent, IntrusiveRedBlackTreeNode Parent::*Member, class Derived> 506template <class Parent, IntrusiveRedBlackTreeNode Parent::*Member, class Derived>
@@ -498,19 +524,16 @@ private:
498 return std::addressof(parent->*Member); 524 return std::addressof(parent->*Member);
499 } 525 }
500 526
501 static constexpr Derived* GetParent(IntrusiveRedBlackTreeNode* node) { 527 static Derived* GetParent(IntrusiveRedBlackTreeNode* node) {
502 return GetParentPointer<Member, Derived>(node); 528 return Common::GetParentPointer<Member, Derived>(node);
503 } 529 }
504 530
505 static constexpr Derived const* GetParent(const IntrusiveRedBlackTreeNode* node) { 531 static Derived const* GetParent(IntrusiveRedBlackTreeNode const* node) {
506 return GetParentPointer<Member, Derived>(node); 532 return Common::GetParentPointer<Member, Derived>(node);
507 } 533 }
508
509private:
510 static constexpr TypedStorage<Derived> DerivedStorage = {};
511}; 534};
512 535
513template <auto T, class Derived = impl::GetParentType<T>> 536template <auto T, class Derived = Common::impl::GetParentType<T>>
514class IntrusiveRedBlackTreeMemberTraitsDeferredAssert; 537class IntrusiveRedBlackTreeMemberTraitsDeferredAssert;
515 538
516template <class Parent, IntrusiveRedBlackTreeNode Parent::*Member, class Derived> 539template <class Parent, IntrusiveRedBlackTreeNode Parent::*Member, class Derived>
@@ -521,11 +544,6 @@ public:
521 IntrusiveRedBlackTree<Derived, IntrusiveRedBlackTreeMemberTraitsDeferredAssert, Comparator>; 544 IntrusiveRedBlackTree<Derived, IntrusiveRedBlackTreeMemberTraitsDeferredAssert, Comparator>;
522 using TreeTypeImpl = impl::IntrusiveRedBlackTreeImpl; 545 using TreeTypeImpl = impl::IntrusiveRedBlackTreeImpl;
523 546
524 static constexpr bool IsValid() {
525 TypedStorage<Derived> DerivedStorage = {};
526 return GetParent(GetNode(GetPointer(DerivedStorage))) == GetPointer(DerivedStorage);
527 }
528
529private: 547private:
530 template <class, class, class> 548 template <class, class, class>
531 friend class IntrusiveRedBlackTree; 549 friend class IntrusiveRedBlackTree;
@@ -540,30 +558,36 @@ private:
540 return std::addressof(parent->*Member); 558 return std::addressof(parent->*Member);
541 } 559 }
542 560
543 static constexpr Derived* GetParent(IntrusiveRedBlackTreeNode* node) { 561 static Derived* GetParent(IntrusiveRedBlackTreeNode* node) {
544 return GetParentPointer<Member, Derived>(node); 562 return Common::GetParentPointer<Member, Derived>(node);
545 } 563 }
546 564
547 static constexpr Derived const* GetParent(const IntrusiveRedBlackTreeNode* node) { 565 static Derived const* GetParent(IntrusiveRedBlackTreeNode const* node) {
548 return GetParentPointer<Member, Derived>(node); 566 return Common::GetParentPointer<Member, Derived>(node);
549 } 567 }
550}; 568};
551 569
552template <class Derived> 570template <class Derived>
553class IntrusiveRedBlackTreeBaseNode : public IntrusiveRedBlackTreeNode { 571class alignas(void*) IntrusiveRedBlackTreeBaseNode : public IntrusiveRedBlackTreeNode {
554public: 572public:
573 using IntrusiveRedBlackTreeNode::IntrusiveRedBlackTreeNode;
574
555 constexpr Derived* GetPrev() { 575 constexpr Derived* GetPrev() {
556 return static_cast<Derived*>(impl::IntrusiveRedBlackTreeImpl::GetPrev(this)); 576 return static_cast<Derived*>(static_cast<IntrusiveRedBlackTreeBaseNode*>(
577 impl::IntrusiveRedBlackTreeImpl::GetPrev(this)));
557 } 578 }
558 constexpr const Derived* GetPrev() const { 579 constexpr const Derived* GetPrev() const {
559 return static_cast<const Derived*>(impl::IntrusiveRedBlackTreeImpl::GetPrev(this)); 580 return static_cast<const Derived*>(static_cast<const IntrusiveRedBlackTreeBaseNode*>(
581 impl::IntrusiveRedBlackTreeImpl::GetPrev(this)));
560 } 582 }
561 583
562 constexpr Derived* GetNext() { 584 constexpr Derived* GetNext() {
563 return static_cast<Derived*>(impl::IntrusiveRedBlackTreeImpl::GetNext(this)); 585 return static_cast<Derived*>(static_cast<IntrusiveRedBlackTreeBaseNode*>(
586 impl::IntrusiveRedBlackTreeImpl::GetNext(this)));
564 } 587 }
565 constexpr const Derived* GetNext() const { 588 constexpr const Derived* GetNext() const {
566 return static_cast<const Derived*>(impl::IntrusiveRedBlackTreeImpl::GetNext(this)); 589 return static_cast<const Derived*>(static_cast<const IntrusiveRedBlackTreeBaseNode*>(
590 impl::IntrusiveRedBlackTreeImpl::GetNext(this)));
567 } 591 }
568}; 592};
569 593
@@ -581,19 +605,22 @@ private:
581 friend class impl::IntrusiveRedBlackTreeImpl; 605 friend class impl::IntrusiveRedBlackTreeImpl;
582 606
583 static constexpr IntrusiveRedBlackTreeNode* GetNode(Derived* parent) { 607 static constexpr IntrusiveRedBlackTreeNode* GetNode(Derived* parent) {
584 return static_cast<IntrusiveRedBlackTreeNode*>(parent); 608 return static_cast<IntrusiveRedBlackTreeNode*>(
609 static_cast<IntrusiveRedBlackTreeBaseNode<Derived>*>(parent));
585 } 610 }
586 611
587 static constexpr IntrusiveRedBlackTreeNode const* GetNode(Derived const* parent) { 612 static constexpr IntrusiveRedBlackTreeNode const* GetNode(Derived const* parent) {
588 return static_cast<const IntrusiveRedBlackTreeNode*>(parent); 613 return static_cast<const IntrusiveRedBlackTreeNode*>(
614 static_cast<const IntrusiveRedBlackTreeBaseNode<Derived>*>(parent));
589 } 615 }
590 616
591 static constexpr Derived* GetParent(IntrusiveRedBlackTreeNode* node) { 617 static constexpr Derived* GetParent(IntrusiveRedBlackTreeNode* node) {
592 return static_cast<Derived*>(node); 618 return static_cast<Derived*>(static_cast<IntrusiveRedBlackTreeBaseNode<Derived>*>(node));
593 } 619 }
594 620
595 static constexpr Derived const* GetParent(const IntrusiveRedBlackTreeNode* node) { 621 static constexpr Derived const* GetParent(IntrusiveRedBlackTreeNode const* node) {
596 return static_cast<const Derived*>(node); 622 return static_cast<const Derived*>(
623 static_cast<const IntrusiveRedBlackTreeBaseNode<Derived>*>(node));
597 } 624 }
598}; 625};
599 626
diff --git a/src/common/literals.h b/src/common/literals.h
index d55fed40b..0ad314afb 100644
--- a/src/common/literals.h
+++ b/src/common/literals.h
@@ -1,6 +1,5 @@
1// Copyright 2021 yuzu emulator team 1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#pragma once 4#pragma once
6 5
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp
index c51c05b28..15d92505e 100644
--- a/src/common/logging/backend.cpp
+++ b/src/common/logging/backend.cpp
@@ -1,14 +1,11 @@
1// Copyright 2014 Citra Emulator Project 1// SPDX-FileCopyrightText: 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#include <atomic> 4#include <atomic>
6#include <chrono> 5#include <chrono>
7#include <climits> 6#include <climits>
8#include <exception>
9#include <stop_token> 7#include <stop_token>
10#include <thread> 8#include <thread>
11#include <vector>
12 9
13#include <fmt/format.h> 10#include <fmt/format.h>
14 11
@@ -218,19 +215,17 @@ private:
218 Impl(const std::filesystem::path& file_backend_filename, const Filter& filter_) 215 Impl(const std::filesystem::path& file_backend_filename, const Filter& filter_)
219 : filter{filter_}, file_backend{file_backend_filename} {} 216 : filter{filter_}, file_backend{file_backend_filename} {}
220 217
221 ~Impl() { 218 ~Impl() = default;
222 StopBackendThread();
223 }
224 219
225 void StartBackendThread() { 220 void StartBackendThread() {
226 backend_thread = std::thread([this] { 221 backend_thread = std::jthread([this](std::stop_token stop_token) {
227 Common::SetCurrentThreadName("yuzu:Log"); 222 Common::SetCurrentThreadName("Logger");
228 Entry entry; 223 Entry entry;
229 const auto write_logs = [this, &entry]() { 224 const auto write_logs = [this, &entry]() {
230 ForEachBackend([&entry](Backend& backend) { backend.Write(entry); }); 225 ForEachBackend([&entry](Backend& backend) { backend.Write(entry); });
231 }; 226 };
232 while (!stop.stop_requested()) { 227 while (!stop_token.stop_requested()) {
233 entry = message_queue.PopWait(stop.get_token()); 228 entry = message_queue.PopWait(stop_token);
234 if (entry.filename != nullptr) { 229 if (entry.filename != nullptr) {
235 write_logs(); 230 write_logs();
236 } 231 }
@@ -244,11 +239,6 @@ private:
244 }); 239 });
245 } 240 }
246 241
247 void StopBackendThread() {
248 stop.request_stop();
249 backend_thread.join();
250 }
251
252 Entry CreateEntry(Class log_class, Level log_level, const char* filename, unsigned int line_nr, 242 Entry CreateEntry(Class log_class, Level log_level, const char* filename, unsigned int line_nr,
253 const char* function, std::string&& message) const { 243 const char* function, std::string&& message) const {
254 using std::chrono::duration_cast; 244 using std::chrono::duration_cast;
@@ -283,10 +273,9 @@ private:
283 ColorConsoleBackend color_console_backend{}; 273 ColorConsoleBackend color_console_backend{};
284 FileBackend file_backend; 274 FileBackend file_backend;
285 275
286 std::stop_source stop;
287 std::thread backend_thread;
288 MPSCQueue<Entry, true> message_queue{}; 276 MPSCQueue<Entry, true> message_queue{};
289 std::chrono::steady_clock::time_point time_origin{std::chrono::steady_clock::now()}; 277 std::chrono::steady_clock::time_point time_origin{std::chrono::steady_clock::now()};
278 std::jthread backend_thread;
290}; 279};
291} // namespace 280} // namespace
292 281
diff --git a/src/common/logging/backend.h b/src/common/logging/backend.h
index bf785f402..12e5e2498 100644
--- a/src/common/logging/backend.h
+++ b/src/common/logging/backend.h
@@ -1,10 +1,8 @@
1// Copyright 2014 Citra Emulator Project 1// SPDX-FileCopyrightText: 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#pragma once 4#pragma once
6 5
7#include <filesystem>
8#include "common/logging/filter.h" 6#include "common/logging/filter.h"
9 7
10namespace Common::Log { 8namespace Common::Log {
diff --git a/src/common/logging/filter.cpp b/src/common/logging/filter.cpp
index b898a652c..a959acb74 100644
--- a/src/common/logging/filter.cpp
+++ b/src/common/logging/filter.cpp
@@ -1,6 +1,5 @@
1// Copyright 2014 Citra Emulator Project 1// SPDX-FileCopyrightText: 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#include <algorithm> 4#include <algorithm>
6#include "common/logging/filter.h" 5#include "common/logging/filter.h"
@@ -101,6 +100,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
101 SUB(Service, GRC) \ 100 SUB(Service, GRC) \
102 SUB(Service, HID) \ 101 SUB(Service, HID) \
103 SUB(Service, IRS) \ 102 SUB(Service, IRS) \
103 SUB(Service, JIT) \
104 SUB(Service, LBL) \ 104 SUB(Service, LBL) \
105 SUB(Service, LDN) \ 105 SUB(Service, LDN) \
106 SUB(Service, LDR) \ 106 SUB(Service, LDR) \
@@ -108,6 +108,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
108 SUB(Service, Migration) \ 108 SUB(Service, Migration) \
109 SUB(Service, Mii) \ 109 SUB(Service, Mii) \
110 SUB(Service, MM) \ 110 SUB(Service, MM) \
111 SUB(Service, MNPP) \
111 SUB(Service, NCM) \ 112 SUB(Service, NCM) \
112 SUB(Service, NFC) \ 113 SUB(Service, NFC) \
113 SUB(Service, NFP) \ 114 SUB(Service, NFP) \
@@ -118,6 +119,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
118 SUB(Service, NPNS) \ 119 SUB(Service, NPNS) \
119 SUB(Service, NS) \ 120 SUB(Service, NS) \
120 SUB(Service, NVDRV) \ 121 SUB(Service, NVDRV) \
122 SUB(Service, NVFlinger) \
121 SUB(Service, OLSC) \ 123 SUB(Service, OLSC) \
122 SUB(Service, PCIE) \ 124 SUB(Service, PCIE) \
123 SUB(Service, PCTL) \ 125 SUB(Service, PCTL) \
@@ -125,7 +127,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
125 SUB(Service, PM) \ 127 SUB(Service, PM) \
126 SUB(Service, PREPO) \ 128 SUB(Service, PREPO) \
127 SUB(Service, PSC) \ 129 SUB(Service, PSC) \
128 SUB(Service, PSM) \ 130 SUB(Service, PTM) \
129 SUB(Service, SET) \ 131 SUB(Service, SET) \
130 SUB(Service, SM) \ 132 SUB(Service, SM) \
131 SUB(Service, SPL) \ 133 SUB(Service, SPL) \
diff --git a/src/common/logging/filter.h b/src/common/logging/filter.h
index 1a3074e04..54d172cc0 100644
--- a/src/common/logging/filter.h
+++ b/src/common/logging/filter.h
@@ -1,13 +1,11 @@
1// Copyright 2014 Citra Emulator Project 1// SPDX-FileCopyrightText: 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#pragma once 4#pragma once
6 5
7#include <array> 6#include <array>
8#include <chrono> 7#include <chrono>
9#include <cstddef> 8#include <cstddef>
10#include <string_view>
11#include "common/logging/log.h" 9#include "common/logging/log.h"
12 10
13namespace Common::Log { 11namespace Common::Log {
diff --git a/src/common/logging/formatter.h b/src/common/logging/formatter.h
index 552cde75a..88e55505d 100644
--- a/src/common/logging/formatter.h
+++ b/src/common/logging/formatter.h
@@ -1,6 +1,5 @@
1// Copyright 2022 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#pragma once 4#pragma once
6 5
diff --git a/src/common/logging/log.h b/src/common/logging/log.h
index 0c80d01ee..c00c01a9e 100644
--- a/src/common/logging/log.h
+++ b/src/common/logging/log.h
@@ -1,6 +1,5 @@
1// Copyright 2014 Citra Emulator Project 1// SPDX-FileCopyrightText: 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#pragma once 4#pragma once
6 5
diff --git a/src/common/logging/log_entry.h b/src/common/logging/log_entry.h
index b28570071..d8d7daf76 100644
--- a/src/common/logging/log_entry.h
+++ b/src/common/logging/log_entry.h
@@ -1,6 +1,5 @@
1// Copyright 2021 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#pragma once 4#pragma once
6 5
diff --git a/src/common/logging/text_formatter.cpp b/src/common/logging/text_formatter.cpp
index 10b2281db..09398ea64 100644
--- a/src/common/logging/text_formatter.cpp
+++ b/src/common/logging/text_formatter.cpp
@@ -1,6 +1,5 @@
1// Copyright 2014 Citra Emulator Project 1// SPDX-FileCopyrightText: 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#include <array> 4#include <array>
6#include <cstdio> 5#include <cstdio>
@@ -10,12 +9,10 @@
10#endif 9#endif
11 10
12#include "common/assert.h" 11#include "common/assert.h"
13#include "common/common_funcs.h"
14#include "common/logging/filter.h" 12#include "common/logging/filter.h"
15#include "common/logging/log.h" 13#include "common/logging/log.h"
16#include "common/logging/log_entry.h" 14#include "common/logging/log_entry.h"
17#include "common/logging/text_formatter.h" 15#include "common/logging/text_formatter.h"
18#include "common/string_util.h"
19 16
20namespace Common::Log { 17namespace Common::Log {
21 18
diff --git a/src/common/logging/text_formatter.h b/src/common/logging/text_formatter.h
index 171e74cfe..0d0ec4370 100644
--- a/src/common/logging/text_formatter.h
+++ b/src/common/logging/text_formatter.h
@@ -1,10 +1,8 @@
1// Copyright 2014 Citra Emulator Project 1// SPDX-FileCopyrightText: 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#pragma once 4#pragma once
6 5
7#include <cstddef>
8#include <string> 6#include <string>
9 7
10namespace Common::Log { 8namespace Common::Log {
diff --git a/src/common/logging/types.h b/src/common/logging/types.h
index 9ed0c7ad6..595c15ada 100644
--- a/src/common/logging/types.h
+++ b/src/common/logging/types.h
@@ -1,6 +1,5 @@
1// Copyright 2021 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#pragma once 4#pragma once
6 5
@@ -69,6 +68,7 @@ enum class Class : u8 {
69 Service_GRC, ///< The game recording service 68 Service_GRC, ///< The game recording service
70 Service_HID, ///< The HID (Human interface device) service 69 Service_HID, ///< The HID (Human interface device) service
71 Service_IRS, ///< The IRS service 70 Service_IRS, ///< The IRS service
71 Service_JIT, ///< The JIT service
72 Service_LBL, ///< The LBL (LCD backlight) service 72 Service_LBL, ///< The LBL (LCD backlight) service
73 Service_LDN, ///< The LDN (Local domain network) service 73 Service_LDN, ///< The LDN (Local domain network) service
74 Service_LDR, ///< The loader service 74 Service_LDR, ///< The loader service
@@ -76,6 +76,7 @@ enum class Class : u8 {
76 Service_Migration, ///< The migration service 76 Service_Migration, ///< The migration service
77 Service_Mii, ///< The Mii service 77 Service_Mii, ///< The Mii service
78 Service_MM, ///< The MM (Multimedia) service 78 Service_MM, ///< The MM (Multimedia) service
79 Service_MNPP, ///< The MNPP service
79 Service_NCM, ///< The NCM service 80 Service_NCM, ///< The NCM service
80 Service_NFC, ///< The NFC (Near-field communication) service 81 Service_NFC, ///< The NFC (Near-field communication) service
81 Service_NFP, ///< The NFP service 82 Service_NFP, ///< The NFP service
@@ -86,6 +87,7 @@ enum class Class : u8 {
86 Service_NPNS, ///< The NPNS service 87 Service_NPNS, ///< The NPNS service
87 Service_NS, ///< The NS services 88 Service_NS, ///< The NS services
88 Service_NVDRV, ///< The NVDRV (Nvidia driver) service 89 Service_NVDRV, ///< The NVDRV (Nvidia driver) service
90 Service_NVFlinger, ///< The NVFlinger service
89 Service_OLSC, ///< The OLSC service 91 Service_OLSC, ///< The OLSC service
90 Service_PCIE, ///< The PCIe service 92 Service_PCIE, ///< The PCIe service
91 Service_PCTL, ///< The PCTL (Parental control) service 93 Service_PCTL, ///< The PCTL (Parental control) service
@@ -93,7 +95,7 @@ enum class Class : u8 {
93 Service_PM, ///< The PM service 95 Service_PM, ///< The PM service
94 Service_PREPO, ///< The PREPO (Play report) service 96 Service_PREPO, ///< The PREPO (Play report) service
95 Service_PSC, ///< The PSC service 97 Service_PSC, ///< The PSC service
96 Service_PSM, ///< The PSM service 98 Service_PTM, ///< The PTM service
97 Service_SET, ///< The SET (Settings) service 99 Service_SET, ///< The SET (Settings) service
98 Service_SM, ///< The SM (Service manager) service 100 Service_SM, ///< The SM (Service manager) service
99 Service_SPL, ///< The SPL service 101 Service_SPL, ///< The SPL service
diff --git a/src/common/lru_cache.h b/src/common/lru_cache.h
index 365488ba5..36cea5d27 100644
--- a/src/common/lru_cache.h
+++ b/src/common/lru_cache.h
@@ -1,6 +1,5 @@
1// Copyright 2021 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2+ or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#pragma once 4#pragma once
6 5
diff --git a/src/common/lz4_compression.cpp b/src/common/lz4_compression.cpp
index dbb40da7c..ffb32fecf 100644
--- a/src/common/lz4_compression.cpp
+++ b/src/common/lz4_compression.cpp
@@ -1,6 +1,5 @@
1// Copyright 2019 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#include <algorithm> 4#include <algorithm>
6#include <lz4hc.h> 5#include <lz4hc.h>
diff --git a/src/common/lz4_compression.h b/src/common/lz4_compression.h
index 1b4717595..7fd53a960 100644
--- a/src/common/lz4_compression.h
+++ b/src/common/lz4_compression.h
@@ -1,6 +1,5 @@
1// Copyright 2019 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#pragma once 4#pragma once
6 5
diff --git a/src/common/math_util.h b/src/common/math_util.h
index 510c4e56d..1f5928c15 100644
--- a/src/common/math_util.h
+++ b/src/common/math_util.h
@@ -1,9 +1,10 @@
1// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project 1// SPDX-FileCopyrightText: 2013 Dolphin Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-FileCopyrightText: 2014 Citra Emulator Project
3// Refer to the license.txt file included. 3// SPDX-License-Identifier: GPL-2.0-or-later
4 4
5#pragma once 5#pragma once
6 6
7#include <algorithm>
7#include <cstdlib> 8#include <cstdlib>
8#include <type_traits> 9#include <type_traits>
9 10
@@ -20,10 +21,32 @@ struct Rectangle {
20 21
21 constexpr Rectangle() = default; 22 constexpr Rectangle() = default;
22 23
24 constexpr Rectangle(T width, T height) : right(width), bottom(height) {}
25
23 constexpr Rectangle(T left_, T top_, T right_, T bottom_) 26 constexpr Rectangle(T left_, T top_, T right_, T bottom_)
24 : left(left_), top(top_), right(right_), bottom(bottom_) {} 27 : left(left_), top(top_), right(right_), bottom(bottom_) {}
25 28
26 [[nodiscard]] T GetWidth() const { 29 [[nodiscard]] constexpr T Left() const {
30 return left;
31 }
32
33 [[nodiscard]] constexpr T Top() const {
34 return top;
35 }
36
37 [[nodiscard]] constexpr T Right() const {
38 return right;
39 }
40
41 [[nodiscard]] constexpr T Bottom() const {
42 return bottom;
43 }
44
45 [[nodiscard]] constexpr bool IsEmpty() const {
46 return (GetWidth() <= 0) || (GetHeight() <= 0);
47 }
48
49 [[nodiscard]] constexpr T GetWidth() const {
27 if constexpr (std::is_floating_point_v<T>) { 50 if constexpr (std::is_floating_point_v<T>) {
28 return std::abs(right - left); 51 return std::abs(right - left);
29 } else { 52 } else {
@@ -31,7 +54,7 @@ struct Rectangle {
31 } 54 }
32 } 55 }
33 56
34 [[nodiscard]] T GetHeight() const { 57 [[nodiscard]] constexpr T GetHeight() const {
35 if constexpr (std::is_floating_point_v<T>) { 58 if constexpr (std::is_floating_point_v<T>) {
36 return std::abs(bottom - top); 59 return std::abs(bottom - top);
37 } else { 60 } else {
@@ -39,18 +62,35 @@ struct Rectangle {
39 } 62 }
40 } 63 }
41 64
42 [[nodiscard]] Rectangle<T> TranslateX(const T x) const { 65 [[nodiscard]] constexpr Rectangle<T> TranslateX(const T x) const {
43 return Rectangle{left + x, top, right + x, bottom}; 66 return Rectangle{left + x, top, right + x, bottom};
44 } 67 }
45 68
46 [[nodiscard]] Rectangle<T> TranslateY(const T y) const { 69 [[nodiscard]] constexpr Rectangle<T> TranslateY(const T y) const {
47 return Rectangle{left, top + y, right, bottom + y}; 70 return Rectangle{left, top + y, right, bottom + y};
48 } 71 }
49 72
50 [[nodiscard]] Rectangle<T> Scale(const float s) const { 73 [[nodiscard]] constexpr Rectangle<T> Scale(const float s) const {
51 return Rectangle{left, top, static_cast<T>(static_cast<float>(left + GetWidth()) * s), 74 return Rectangle{left, top, static_cast<T>(static_cast<float>(left + GetWidth()) * s),
52 static_cast<T>(static_cast<float>(top + GetHeight()) * s)}; 75 static_cast<T>(static_cast<float>(top + GetHeight()) * s)};
53 } 76 }
77
78 [[nodiscard]] constexpr bool operator==(const Rectangle<T>& rhs) const {
79 return (left == rhs.left) && (top == rhs.top) && (right == rhs.right) &&
80 (bottom == rhs.bottom);
81 }
82
83 [[nodiscard]] constexpr bool operator!=(const Rectangle<T>& rhs) const {
84 return !operator==(rhs);
85 }
86
87 [[nodiscard]] constexpr bool Intersect(const Rectangle<T>& with, Rectangle<T>* result) const {
88 result->left = std::max(left, with.left);
89 result->top = std::max(top, with.top);
90 result->right = std::min(right, with.right);
91 result->bottom = std::min(bottom, with.bottom);
92 return !result->IsEmpty();
93 }
54}; 94};
55 95
56template <typename T> 96template <typename T>
diff --git a/src/common/memory_detect.cpp b/src/common/memory_detect.cpp
index 8cff6ec37..86a3abcc6 100644
--- a/src/common/memory_detect.cpp
+++ b/src/common/memory_detect.cpp
@@ -1,6 +1,5 @@
1// Copyright 2020 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#ifdef _WIN32 4#ifdef _WIN32
6// clang-format off 5// clang-format off
@@ -70,4 +69,4 @@ const MemoryInfo& GetMemInfo() {
70 return mem_info; 69 return mem_info;
71} 70}
72 71
73} // namespace Common \ No newline at end of file 72} // namespace Common
diff --git a/src/common/memory_detect.h b/src/common/memory_detect.h
index 0f73751c8..a345e6d28 100644
--- a/src/common/memory_detect.h
+++ b/src/common/memory_detect.h
@@ -1,6 +1,5 @@
1// Copyright 2020 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#pragma once 4#pragma once
6 5
diff --git a/src/common/microprofile.cpp b/src/common/microprofile.cpp
index ee25dd37f..e6657c82f 100644
--- a/src/common/microprofile.cpp
+++ b/src/common/microprofile.cpp
@@ -1,6 +1,5 @@
1// Copyright 2015 Citra Emulator Project 1// SPDX-FileCopyrightText: 2015 Citra Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5// Includes the MicroProfile implementation in this file for compilation 4// Includes the MicroProfile implementation in this file for compilation
6#define MICROPROFILE_IMPL 1 5#define MICROPROFILE_IMPL 1
diff --git a/src/common/microprofile.h b/src/common/microprofile.h
index 54e7f3cc4..56ef0a2dc 100644
--- a/src/common/microprofile.h
+++ b/src/common/microprofile.h
@@ -1,6 +1,5 @@
1// Copyright 2015 Citra Emulator Project 1// SPDX-FileCopyrightText: 2015 Citra Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#pragma once 4#pragma once
6 5
@@ -23,12 +22,3 @@ typedef void* HANDLE;
23#include <microprofile.h> 22#include <microprofile.h>
24 23
25#define MP_RGB(r, g, b) ((r) << 16 | (g) << 8 | (b) << 0) 24#define MP_RGB(r, g, b) ((r) << 16 | (g) << 8 | (b) << 0)
26
27// On OS X, some Mach header included by MicroProfile defines these as macros, conflicting with
28// identifiers we use.
29#ifdef PAGE_SIZE
30#undef PAGE_SIZE
31#endif
32#ifdef PAGE_MASK
33#undef PAGE_MASK
34#endif
diff --git a/src/common/microprofileui.h b/src/common/microprofileui.h
index 41abe6b75..39ed18ffa 100644
--- a/src/common/microprofileui.h
+++ b/src/common/microprofileui.h
@@ -1,6 +1,5 @@
1// Copyright 2015 Citra Emulator Project 1// SPDX-FileCopyrightText: 2015 Citra Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#pragma once 4#pragma once
6 5
diff --git a/src/common/multi_level_page_table.cpp b/src/common/multi_level_page_table.cpp
new file mode 100644
index 000000000..46e362f3b
--- /dev/null
+++ b/src/common/multi_level_page_table.cpp
@@ -0,0 +1,9 @@
1// SPDX-FileCopyrightText: 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/multi_level_page_table.inc"
5
6namespace Common {
7template class Common::MultiLevelPageTable<u64>;
8template class Common::MultiLevelPageTable<u32>;
9} // namespace Common
diff --git a/src/common/multi_level_page_table.h b/src/common/multi_level_page_table.h
new file mode 100644
index 000000000..31f6676a0
--- /dev/null
+++ b/src/common/multi_level_page_table.h
@@ -0,0 +1,78 @@
1// SPDX-FileCopyrightText: 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <type_traits>
7#include <utility>
8#include <vector>
9
10#include "common/common_types.h"
11
12namespace Common {
13
14template <typename BaseAddr>
15class MultiLevelPageTable final {
16public:
17 constexpr MultiLevelPageTable() = default;
18 explicit MultiLevelPageTable(std::size_t address_space_bits, std::size_t first_level_bits,
19 std::size_t page_bits);
20
21 ~MultiLevelPageTable() noexcept;
22
23 MultiLevelPageTable(const MultiLevelPageTable&) = delete;
24 MultiLevelPageTable& operator=(const MultiLevelPageTable&) = delete;
25
26 MultiLevelPageTable(MultiLevelPageTable&& other) noexcept
27 : address_space_bits{std::exchange(other.address_space_bits, 0)},
28 first_level_bits{std::exchange(other.first_level_bits, 0)}, page_bits{std::exchange(
29 other.page_bits, 0)},
30 first_level_shift{std::exchange(other.first_level_shift, 0)},
31 first_level_chunk_size{std::exchange(other.first_level_chunk_size, 0)},
32 first_level_map{std::move(other.first_level_map)}, base_ptr{std::exchange(other.base_ptr,
33 nullptr)} {}
34
35 MultiLevelPageTable& operator=(MultiLevelPageTable&& other) noexcept {
36 address_space_bits = std::exchange(other.address_space_bits, 0);
37 first_level_bits = std::exchange(other.first_level_bits, 0);
38 page_bits = std::exchange(other.page_bits, 0);
39 first_level_shift = std::exchange(other.first_level_shift, 0);
40 first_level_chunk_size = std::exchange(other.first_level_chunk_size, 0);
41 alloc_size = std::exchange(other.alloc_size, 0);
42 first_level_map = std::move(other.first_level_map);
43 base_ptr = std::exchange(other.base_ptr, nullptr);
44 return *this;
45 }
46
47 void ReserveRange(u64 start, std::size_t size);
48
49 [[nodiscard]] const BaseAddr& operator[](std::size_t index) const {
50 return base_ptr[index];
51 }
52
53 [[nodiscard]] BaseAddr& operator[](std::size_t index) {
54 return base_ptr[index];
55 }
56
57 [[nodiscard]] BaseAddr* data() {
58 return base_ptr;
59 }
60
61 [[nodiscard]] const BaseAddr* data() const {
62 return base_ptr;
63 }
64
65private:
66 void AllocateLevel(u64 level);
67
68 std::size_t address_space_bits{};
69 std::size_t first_level_bits{};
70 std::size_t page_bits{};
71 std::size_t first_level_shift{};
72 std::size_t first_level_chunk_size{};
73 std::size_t alloc_size{};
74 std::vector<void*> first_level_map{};
75 BaseAddr* base_ptr{};
76};
77
78} // namespace Common
diff --git a/src/common/multi_level_page_table.inc b/src/common/multi_level_page_table.inc
new file mode 100644
index 000000000..8ac506fa0
--- /dev/null
+++ b/src/common/multi_level_page_table.inc
@@ -0,0 +1,84 @@
1// SPDX-FileCopyrightText: 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#ifdef _WIN32
5#include <windows.h>
6#else
7#include <sys/mman.h>
8#endif
9
10#include "common/assert.h"
11#include "common/multi_level_page_table.h"
12
13namespace Common {
14
15template <typename BaseAddr>
16MultiLevelPageTable<BaseAddr>::MultiLevelPageTable(std::size_t address_space_bits_,
17 std::size_t first_level_bits_,
18 std::size_t page_bits_)
19 : address_space_bits{address_space_bits_},
20 first_level_bits{first_level_bits_}, page_bits{page_bits_} {
21 if (page_bits == 0) {
22 return;
23 }
24 first_level_shift = address_space_bits - first_level_bits;
25 first_level_chunk_size = (1ULL << (first_level_shift - page_bits)) * sizeof(BaseAddr);
26 alloc_size = (1ULL << (address_space_bits - page_bits)) * sizeof(BaseAddr);
27 std::size_t first_level_size = 1ULL << first_level_bits;
28 first_level_map.resize(first_level_size, nullptr);
29#ifdef _WIN32
30 void* base{VirtualAlloc(nullptr, alloc_size, MEM_RESERVE, PAGE_READWRITE)};
31#else
32 void* base{mmap(nullptr, alloc_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)};
33
34 if (base == MAP_FAILED) {
35 base = nullptr;
36 }
37#endif
38
39 ASSERT(base);
40 base_ptr = reinterpret_cast<BaseAddr*>(base);
41}
42
43template <typename BaseAddr>
44MultiLevelPageTable<BaseAddr>::~MultiLevelPageTable() noexcept {
45 if (!base_ptr) {
46 return;
47 }
48#ifdef _WIN32
49 ASSERT(VirtualFree(base_ptr, 0, MEM_RELEASE));
50#else
51 ASSERT(munmap(base_ptr, alloc_size) == 0);
52#endif
53}
54
55template <typename BaseAddr>
56void MultiLevelPageTable<BaseAddr>::ReserveRange(u64 start, std::size_t size) {
57 const u64 new_start = start >> first_level_shift;
58 const u64 new_end = (start + size) >> first_level_shift;
59 for (u64 i = new_start; i <= new_end; i++) {
60 if (!first_level_map[i]) {
61 AllocateLevel(i);
62 }
63 }
64}
65
66template <typename BaseAddr>
67void MultiLevelPageTable<BaseAddr>::AllocateLevel(u64 level) {
68 void* ptr = reinterpret_cast<char *>(base_ptr) + level * first_level_chunk_size;
69#ifdef _WIN32
70 void* base{VirtualAlloc(ptr, first_level_chunk_size, MEM_COMMIT, PAGE_READWRITE)};
71#else
72 void* base{mmap(ptr, first_level_chunk_size, PROT_READ | PROT_WRITE,
73 MAP_ANONYMOUS | MAP_PRIVATE, -1, 0)};
74
75 if (base == MAP_FAILED) {
76 base = nullptr;
77 }
78#endif
79 ASSERT(base);
80
81 first_level_map[level] = base;
82}
83
84} // namespace Common
diff --git a/src/common/nvidia_flags.cpp b/src/common/nvidia_flags.cpp
index d1afd1f1d..7ed7690ee 100644
--- a/src/common/nvidia_flags.cpp
+++ b/src/common/nvidia_flags.cpp
@@ -1,12 +1,10 @@
1// Copyright 2021 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#include <cstdlib> 4#include <cstdlib>
6 5
7#include <fmt/format.h> 6#include <fmt/format.h>
8 7
9#include "common/fs/file.h"
10#include "common/fs/fs.h" 8#include "common/fs/fs.h"
11#include "common/fs/path_util.h" 9#include "common/fs/path_util.h"
12#include "common/nvidia_flags.h" 10#include "common/nvidia_flags.h"
diff --git a/src/common/nvidia_flags.h b/src/common/nvidia_flags.h
index 8930efcec..8c3b1bfb9 100644
--- a/src/common/nvidia_flags.h
+++ b/src/common/nvidia_flags.h
@@ -1,6 +1,5 @@
1// Copyright 2021 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#pragma once 4#pragma once
6 5
diff --git a/src/common/page_table.cpp b/src/common/page_table.cpp
index 9fffd816f..b744b68ce 100644
--- a/src/common/page_table.cpp
+++ b/src/common/page_table.cpp
@@ -1,6 +1,5 @@
1// Copyright 2019 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#include "common/page_table.h" 4#include "common/page_table.h"
6 5
@@ -10,11 +9,65 @@ PageTable::PageTable() = default;
10 9
11PageTable::~PageTable() noexcept = default; 10PageTable::~PageTable() noexcept = default;
12 11
13void PageTable::Resize(size_t address_space_width_in_bits, size_t page_size_in_bits) { 12bool PageTable::BeginTraversal(TraversalEntry& out_entry, TraversalContext& out_context,
14 const size_t num_page_table_entries{1ULL << (address_space_width_in_bits - page_size_in_bits)}; 13 u64 address) const {
14 // Setup invalid defaults.
15 out_entry.phys_addr = 0;
16 out_entry.block_size = page_size;
17 out_context.next_page = 0;
18
19 // Validate that we can read the actual entry.
20 const auto page = address / page_size;
21 if (page >= backing_addr.size()) {
22 return false;
23 }
24
25 // Validate that the entry is mapped.
26 const auto phys_addr = backing_addr[page];
27 if (phys_addr == 0) {
28 return false;
29 }
30
31 // Populate the results.
32 out_entry.phys_addr = phys_addr + address;
33 out_context.next_page = page + 1;
34 out_context.next_offset = address + page_size;
35
36 return true;
37}
38
39bool PageTable::ContinueTraversal(TraversalEntry& out_entry, TraversalContext& context) const {
40 // Setup invalid defaults.
41 out_entry.phys_addr = 0;
42 out_entry.block_size = page_size;
43
44 // Validate that we can read the actual entry.
45 const auto page = context.next_page;
46 if (page >= backing_addr.size()) {
47 return false;
48 }
49
50 // Validate that the entry is mapped.
51 const auto phys_addr = backing_addr[page];
52 if (phys_addr == 0) {
53 return false;
54 }
55
56 // Populate the results.
57 out_entry.phys_addr = phys_addr + context.next_offset;
58 context.next_page = page + 1;
59 context.next_offset += page_size;
60
61 return true;
62}
63
64void PageTable::Resize(std::size_t address_space_width_in_bits, std::size_t page_size_in_bits) {
65 const std::size_t num_page_table_entries{1ULL
66 << (address_space_width_in_bits - page_size_in_bits)};
15 pointers.resize(num_page_table_entries); 67 pointers.resize(num_page_table_entries);
16 backing_addr.resize(num_page_table_entries); 68 backing_addr.resize(num_page_table_entries);
17 current_address_space_width_in_bits = address_space_width_in_bits; 69 current_address_space_width_in_bits = address_space_width_in_bits;
70 page_size = 1ULL << page_size_in_bits;
18} 71}
19 72
20} // namespace Common 73} // namespace Common
diff --git a/src/common/page_table.h b/src/common/page_table.h
index 8267e8b4d..1ad3a9f8b 100644
--- a/src/common/page_table.h
+++ b/src/common/page_table.h
@@ -1,11 +1,9 @@
1// Copyright 2019 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#pragma once 4#pragma once
6 5
7#include <atomic> 6#include <atomic>
8#include <tuple>
9 7
10#include "common/common_types.h" 8#include "common/common_types.h"
11#include "common/virtual_buffer.h" 9#include "common/virtual_buffer.h"
@@ -17,6 +15,9 @@ enum class PageType : u8 {
17 Unmapped, 15 Unmapped,
18 /// Page is mapped to regular memory. This is the only type you can get pointers to. 16 /// Page is mapped to regular memory. This is the only type you can get pointers to.
19 Memory, 17 Memory,
18 /// Page is mapped to regular memory, but inaccessible from CPU fastmem and must use
19 /// the callbacks.
20 DebugMemory,
20 /// Page is mapped to regular memory, but also needs to check for rasterizer cache flushing and 21 /// Page is mapped to regular memory, but also needs to check for rasterizer cache flushing and
21 /// invalidation 22 /// invalidation
22 RasterizerCachedMemory, 23 RasterizerCachedMemory,
@@ -27,6 +28,16 @@ enum class PageType : u8 {
27 * mimics the way a real CPU page table works. 28 * mimics the way a real CPU page table works.
28 */ 29 */
29struct PageTable { 30struct PageTable {
31 struct TraversalEntry {
32 u64 phys_addr{};
33 std::size_t block_size{};
34 };
35
36 struct TraversalContext {
37 u64 next_page{};
38 u64 next_offset{};
39 };
40
30 /// Number of bits reserved for attribute tagging. 41 /// Number of bits reserved for attribute tagging.
31 /// This can be at most the guaranteed alignment of the pointers in the page table. 42 /// This can be at most the guaranteed alignment of the pointers in the page table.
32 static constexpr int ATTRIBUTE_BITS = 2; 43 static constexpr int ATTRIBUTE_BITS = 2;
@@ -89,6 +100,10 @@ struct PageTable {
89 PageTable(PageTable&&) noexcept = default; 100 PageTable(PageTable&&) noexcept = default;
90 PageTable& operator=(PageTable&&) noexcept = default; 101 PageTable& operator=(PageTable&&) noexcept = default;
91 102
103 bool BeginTraversal(TraversalEntry& out_entry, TraversalContext& out_context,
104 u64 address) const;
105 bool ContinueTraversal(TraversalEntry& out_entry, TraversalContext& context) const;
106
92 /** 107 /**
93 * Resizes the page table to be able to accommodate enough pages within 108 * Resizes the page table to be able to accommodate enough pages within
94 * a given address space. 109 * a given address space.
@@ -96,9 +111,9 @@ struct PageTable {
96 * @param address_space_width_in_bits The address size width in bits. 111 * @param address_space_width_in_bits The address size width in bits.
97 * @param page_size_in_bits The page size in bits. 112 * @param page_size_in_bits The page size in bits.
98 */ 113 */
99 void Resize(size_t address_space_width_in_bits, size_t page_size_in_bits); 114 void Resize(std::size_t address_space_width_in_bits, std::size_t page_size_in_bits);
100 115
101 size_t GetAddressSpaceBits() const { 116 std::size_t GetAddressSpaceBits() const {
102 return current_address_space_width_in_bits; 117 return current_address_space_width_in_bits;
103 } 118 }
104 119
@@ -110,9 +125,11 @@ struct PageTable {
110 125
111 VirtualBuffer<u64> backing_addr; 126 VirtualBuffer<u64> backing_addr;
112 127
113 size_t current_address_space_width_in_bits; 128 std::size_t current_address_space_width_in_bits{};
129
130 u8* fastmem_arena{};
114 131
115 u8* fastmem_arena; 132 std::size_t page_size{};
116}; 133};
117 134
118} // namespace Common 135} // namespace Common
diff --git a/src/common/param_package.cpp b/src/common/param_package.cpp
index bbf20f5eb..629babb81 100644
--- a/src/common/param_package.cpp
+++ b/src/common/param_package.cpp
@@ -1,6 +1,5 @@
1// Copyright 2017 Citra Emulator Project 1// SPDX-FileCopyrightText: 2017 Citra Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#include <array> 4#include <array>
6#include <stdexcept> 5#include <stdexcept>
@@ -76,7 +75,7 @@ std::string ParamPackage::Serialize() const {
76std::string ParamPackage::Get(const std::string& key, const std::string& default_value) const { 75std::string ParamPackage::Get(const std::string& key, const std::string& default_value) const {
77 auto pair = data.find(key); 76 auto pair = data.find(key);
78 if (pair == data.end()) { 77 if (pair == data.end()) {
79 LOG_DEBUG(Common, "key '{}' not found", key); 78 LOG_TRACE(Common, "key '{}' not found", key);
80 return default_value; 79 return default_value;
81 } 80 }
82 81
@@ -86,7 +85,7 @@ std::string ParamPackage::Get(const std::string& key, const std::string& default
86int ParamPackage::Get(const std::string& key, int default_value) const { 85int ParamPackage::Get(const std::string& key, int default_value) const {
87 auto pair = data.find(key); 86 auto pair = data.find(key);
88 if (pair == data.end()) { 87 if (pair == data.end()) {
89 LOG_DEBUG(Common, "key '{}' not found", key); 88 LOG_TRACE(Common, "key '{}' not found", key);
90 return default_value; 89 return default_value;
91 } 90 }
92 91
@@ -101,7 +100,7 @@ int ParamPackage::Get(const std::string& key, int default_value) const {
101float ParamPackage::Get(const std::string& key, float default_value) const { 100float ParamPackage::Get(const std::string& key, float default_value) const {
102 auto pair = data.find(key); 101 auto pair = data.find(key);
103 if (pair == data.end()) { 102 if (pair == data.end()) {
104 LOG_DEBUG(Common, "key {} not found", key); 103 LOG_TRACE(Common, "key {} not found", key);
105 return default_value; 104 return default_value;
106 } 105 }
107 106
diff --git a/src/common/param_package.h b/src/common/param_package.h
index c13e45479..d7c13cb1f 100644
--- a/src/common/param_package.h
+++ b/src/common/param_package.h
@@ -1,6 +1,5 @@
1// Copyright 2017 Citra Emulator Project 1// SPDX-FileCopyrightText: 2017 Citra Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#pragma once 4#pragma once
6 5
diff --git a/src/common/parent_of_member.h b/src/common/parent_of_member.h
index 58c70b0e7..8e03f17d8 100644
--- a/src/common/parent_of_member.h
+++ b/src/common/parent_of_member.h
@@ -1,19 +1,17 @@
1// Copyright 2021 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#pragma once 4#pragma once
6 5
7#include <type_traits> 6#include <type_traits>
8 7
9#include "common/assert.h" 8#include "common/assert.h"
10#include "common/common_types.h"
11 9
12namespace Common { 10namespace Common {
13namespace detail { 11namespace detail {
14template <typename T, size_t Size, size_t Align> 12template <typename T, size_t Size, size_t Align>
15struct TypedStorageImpl { 13struct TypedStorageImpl {
16 std::aligned_storage_t<Size, Align> storage_; 14 alignas(Align) u8 storage_[Size];
17}; 15};
18} // namespace detail 16} // namespace detail
19 17
diff --git a/src/common/point.h b/src/common/point.h
index c0a52ad8d..6491856ea 100644
--- a/src/common/point.h
+++ b/src/common/point.h
@@ -1,6 +1,5 @@
1// Copyright 2021 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#pragma once 4#pragma once
6 5
diff --git a/src/common/quaternion.h b/src/common/quaternion.h
index 4d0871eb4..5bb5f2af0 100644
--- a/src/common/quaternion.h
+++ b/src/common/quaternion.h
@@ -1,6 +1,5 @@
1// Copyright 2016 Citra Emulator Project 1// SPDX-FileCopyrightText: 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#pragma once 4#pragma once
6 5
diff --git a/src/common/reader_writer_queue.h b/src/common/reader_writer_queue.h
new file mode 100644
index 000000000..60c41a8cb
--- /dev/null
+++ b/src/common/reader_writer_queue.h
@@ -0,0 +1,940 @@
1// SPDX-FileCopyrightText: 2013-2020 Cameron Desrochers
2// SPDX-License-Identifier: BSD-2-Clause
3
4#pragma once
5
6#include <cassert>
7#include <cstdint>
8#include <cstdlib> // For malloc/free/abort & size_t
9#include <memory>
10#include <new>
11#include <stdexcept>
12#include <type_traits>
13#include <utility>
14
15#include "common/atomic_helpers.h"
16
17#if __cplusplus > 199711L || _MSC_VER >= 1700 // C++11 or VS2012
18#include <chrono>
19#endif
20
21// A lock-free queue for a single-consumer, single-producer architecture.
22// The queue is also wait-free in the common path (except if more memory
23// needs to be allocated, in which case malloc is called).
24// Allocates memory sparingly, and only once if the original maximum size
25// estimate is never exceeded.
26// Tested on x86/x64 processors, but semantics should be correct for all
27// architectures (given the right implementations in atomicops.h), provided
28// that aligned integer and pointer accesses are naturally atomic.
29// Note that there should only be one consumer thread and producer thread;
30// Switching roles of the threads, or using multiple consecutive threads for
31// one role, is not safe unless properly synchronized.
32// Using the queue exclusively from one thread is fine, though a bit silly.
33
34#ifndef MOODYCAMEL_CACHE_LINE_SIZE
35#define MOODYCAMEL_CACHE_LINE_SIZE 64
36#endif
37
38#ifndef MOODYCAMEL_EXCEPTIONS_ENABLED
39#if (defined(_MSC_VER) && defined(_CPPUNWIND)) || (defined(__GNUC__) && defined(__EXCEPTIONS)) || \
40 (!defined(_MSC_VER) && !defined(__GNUC__))
41#define MOODYCAMEL_EXCEPTIONS_ENABLED
42#endif
43#endif
44
45#ifndef MOODYCAMEL_HAS_EMPLACE
46#if !defined(_MSC_VER) || \
47 _MSC_VER >= 1800 // variadic templates: either a non-MS compiler or VS >= 2013
48#define MOODYCAMEL_HAS_EMPLACE 1
49#endif
50#endif
51
52#ifndef MOODYCAMEL_MAYBE_ALIGN_TO_CACHELINE
53#if defined(__APPLE__) && defined(__MACH__) && __cplusplus >= 201703L
54// This is required to find out what deployment target we are using
55#include <CoreFoundation/CoreFoundation.h>
56#if !defined(MAC_OS_X_VERSION_MIN_REQUIRED) || \
57 MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_14
58// C++17 new(size_t, align_val_t) is not backwards-compatible with older versions of macOS, so we
59// can't support over-alignment in this case
60#define MOODYCAMEL_MAYBE_ALIGN_TO_CACHELINE
61#endif
62#endif
63#endif
64
65#ifndef MOODYCAMEL_MAYBE_ALIGN_TO_CACHELINE
66#define MOODYCAMEL_MAYBE_ALIGN_TO_CACHELINE AE_ALIGN(MOODYCAMEL_CACHE_LINE_SIZE)
67#endif
68
69#ifdef AE_VCPP
70#pragma warning(push)
71#pragma warning(disable : 4324) // structure was padded due to __declspec(align())
72#pragma warning(disable : 4820) // padding was added
73#pragma warning(disable : 4127) // conditional expression is constant
74#endif
75
76namespace Common {
77
78template <typename T, size_t MAX_BLOCK_SIZE = 512>
79class MOODYCAMEL_MAYBE_ALIGN_TO_CACHELINE ReaderWriterQueue {
80 // Design: Based on a queue-of-queues. The low-level queues are just
81 // circular buffers with front and tail indices indicating where the
82 // next element to dequeue is and where the next element can be enqueued,
83 // respectively. Each low-level queue is called a "block". Each block
84 // wastes exactly one element's worth of space to keep the design simple
85 // (if front == tail then the queue is empty, and can't be full).
86 // The high-level queue is a circular linked list of blocks; again there
87 // is a front and tail, but this time they are pointers to the blocks.
88 // The front block is where the next element to be dequeued is, provided
89 // the block is not empty. The back block is where elements are to be
90 // enqueued, provided the block is not full.
91 // The producer thread owns all the tail indices/pointers. The consumer
92 // thread owns all the front indices/pointers. Both threads read each
93 // other's variables, but only the owning thread updates them. E.g. After
94 // the consumer reads the producer's tail, the tail may change before the
95 // consumer is done dequeuing an object, but the consumer knows the tail
96 // will never go backwards, only forwards.
97 // If there is no room to enqueue an object, an additional block (of
98 // equal size to the last block) is added. Blocks are never removed.
99
100public:
101 typedef T value_type;
102
103 // Constructs a queue that can hold at least `size` elements without further
104 // allocations. If more than MAX_BLOCK_SIZE elements are requested,
105 // then several blocks of MAX_BLOCK_SIZE each are reserved (including
106 // at least one extra buffer block).
107 AE_NO_TSAN explicit ReaderWriterQueue(size_t size = 15)
108#ifndef NDEBUG
109 : enqueuing(false), dequeuing(false)
110#endif
111 {
112 assert(MAX_BLOCK_SIZE == ceilToPow2(MAX_BLOCK_SIZE) &&
113 "MAX_BLOCK_SIZE must be a power of 2");
114 assert(MAX_BLOCK_SIZE >= 2 && "MAX_BLOCK_SIZE must be at least 2");
115
116 Block* firstBlock = nullptr;
117
118 largestBlockSize =
119 ceilToPow2(size + 1); // We need a spare slot to fit size elements in the block
120 if (largestBlockSize > MAX_BLOCK_SIZE * 2) {
121 // We need a spare block in case the producer is writing to a different block the
122 // consumer is reading from, and wants to enqueue the maximum number of elements. We
123 // also need a spare element in each block to avoid the ambiguity between front == tail
124 // meaning "empty" and "full". So the effective number of slots that are guaranteed to
125 // be usable at any time is the block size - 1 times the number of blocks - 1. Solving
126 // for size and applying a ceiling to the division gives us (after simplifying):
127 size_t initialBlockCount = (size + MAX_BLOCK_SIZE * 2 - 3) / (MAX_BLOCK_SIZE - 1);
128 largestBlockSize = MAX_BLOCK_SIZE;
129 Block* lastBlock = nullptr;
130 for (size_t i = 0; i != initialBlockCount; ++i) {
131 auto block = make_block(largestBlockSize);
132 if (block == nullptr) {
133#ifdef MOODYCAMEL_EXCEPTIONS_ENABLED
134 throw std::bad_alloc();
135#else
136 abort();
137#endif
138 }
139 if (firstBlock == nullptr) {
140 firstBlock = block;
141 } else {
142 lastBlock->next = block;
143 }
144 lastBlock = block;
145 block->next = firstBlock;
146 }
147 } else {
148 firstBlock = make_block(largestBlockSize);
149 if (firstBlock == nullptr) {
150#ifdef MOODYCAMEL_EXCEPTIONS_ENABLED
151 throw std::bad_alloc();
152#else
153 abort();
154#endif
155 }
156 firstBlock->next = firstBlock;
157 }
158 frontBlock = firstBlock;
159 tailBlock = firstBlock;
160
161 // Make sure the reader/writer threads will have the initialized memory setup above:
162 fence(memory_order_sync);
163 }
164
165 // Note: The queue should not be accessed concurrently while it's
166 // being moved. It's up to the user to synchronize this.
167 AE_NO_TSAN ReaderWriterQueue(ReaderWriterQueue&& other)
168 : frontBlock(other.frontBlock.load()), tailBlock(other.tailBlock.load()),
169 largestBlockSize(other.largestBlockSize)
170#ifndef NDEBUG
171 ,
172 enqueuing(false), dequeuing(false)
173#endif
174 {
175 other.largestBlockSize = 32;
176 Block* b = other.make_block(other.largestBlockSize);
177 if (b == nullptr) {
178#ifdef MOODYCAMEL_EXCEPTIONS_ENABLED
179 throw std::bad_alloc();
180#else
181 abort();
182#endif
183 }
184 b->next = b;
185 other.frontBlock = b;
186 other.tailBlock = b;
187 }
188
189 // Note: The queue should not be accessed concurrently while it's
190 // being moved. It's up to the user to synchronize this.
191 ReaderWriterQueue& operator=(ReaderWriterQueue&& other) AE_NO_TSAN {
192 Block* b = frontBlock.load();
193 frontBlock = other.frontBlock.load();
194 other.frontBlock = b;
195 b = tailBlock.load();
196 tailBlock = other.tailBlock.load();
197 other.tailBlock = b;
198 std::swap(largestBlockSize, other.largestBlockSize);
199 return *this;
200 }
201
202 // Note: The queue should not be accessed concurrently while it's
203 // being deleted. It's up to the user to synchronize this.
204 AE_NO_TSAN ~ReaderWriterQueue() {
205 // Make sure we get the latest version of all variables from other CPUs:
206 fence(memory_order_sync);
207
208 // Destroy any remaining objects in queue and free memory
209 Block* frontBlock_ = frontBlock;
210 Block* block = frontBlock_;
211 do {
212 Block* nextBlock = block->next;
213 size_t blockFront = block->front;
214 size_t blockTail = block->tail;
215
216 for (size_t i = blockFront; i != blockTail; i = (i + 1) & block->sizeMask) {
217 auto element = reinterpret_cast<T*>(block->data + i * sizeof(T));
218 element->~T();
219 (void)element;
220 }
221
222 auto rawBlock = block->rawThis;
223 block->~Block();
224 std::free(rawBlock);
225 block = nextBlock;
226 } while (block != frontBlock_);
227 }
228
229 // Enqueues a copy of element if there is room in the queue.
230 // Returns true if the element was enqueued, false otherwise.
231 // Does not allocate memory.
232 AE_FORCEINLINE bool try_enqueue(T const& element) AE_NO_TSAN {
233 return inner_enqueue<CannotAlloc>(element);
234 }
235
236 // Enqueues a moved copy of element if there is room in the queue.
237 // Returns true if the element was enqueued, false otherwise.
238 // Does not allocate memory.
239 AE_FORCEINLINE bool try_enqueue(T&& element) AE_NO_TSAN {
240 return inner_enqueue<CannotAlloc>(std::forward<T>(element));
241 }
242
243#if MOODYCAMEL_HAS_EMPLACE
244 // Like try_enqueue() but with emplace semantics (i.e. construct-in-place).
245 template <typename... Args>
246 AE_FORCEINLINE bool try_emplace(Args&&... args) AE_NO_TSAN {
247 return inner_enqueue<CannotAlloc>(std::forward<Args>(args)...);
248 }
249#endif
250
251 // Enqueues a copy of element on the queue.
252 // Allocates an additional block of memory if needed.
253 // Only fails (returns false) if memory allocation fails.
254 AE_FORCEINLINE bool enqueue(T const& element) AE_NO_TSAN {
255 return inner_enqueue<CanAlloc>(element);
256 }
257
258 // Enqueues a moved copy of element on the queue.
259 // Allocates an additional block of memory if needed.
260 // Only fails (returns false) if memory allocation fails.
261 AE_FORCEINLINE bool enqueue(T&& element) AE_NO_TSAN {
262 return inner_enqueue<CanAlloc>(std::forward<T>(element));
263 }
264
265#if MOODYCAMEL_HAS_EMPLACE
266 // Like enqueue() but with emplace semantics (i.e. construct-in-place).
267 template <typename... Args>
268 AE_FORCEINLINE bool emplace(Args&&... args) AE_NO_TSAN {
269 return inner_enqueue<CanAlloc>(std::forward<Args>(args)...);
270 }
271#endif
272
273 // Attempts to dequeue an element; if the queue is empty,
274 // returns false instead. If the queue has at least one element,
275 // moves front to result using operator=, then returns true.
276 template <typename U>
277 bool try_dequeue(U& result) AE_NO_TSAN {
278#ifndef NDEBUG
279 ReentrantGuard guard(this->dequeuing);
280#endif
281
282 // High-level pseudocode:
283 // Remember where the tail block is
284 // If the front block has an element in it, dequeue it
285 // Else
286 // If front block was the tail block when we entered the function, return false
287 // Else advance to next block and dequeue the item there
288
289 // Note that we have to use the value of the tail block from before we check if the front
290 // block is full or not, in case the front block is empty and then, before we check if the
291 // tail block is at the front block or not, the producer fills up the front block *and
292 // moves on*, which would make us skip a filled block. Seems unlikely, but was consistently
293 // reproducible in practice.
294 // In order to avoid overhead in the common case, though, we do a double-checked pattern
295 // where we have the fast path if the front block is not empty, then read the tail block,
296 // then re-read the front block and check if it's not empty again, then check if the tail
297 // block has advanced.
298
299 Block* frontBlock_ = frontBlock.load();
300 size_t blockTail = frontBlock_->localTail;
301 size_t blockFront = frontBlock_->front.load();
302
303 if (blockFront != blockTail ||
304 blockFront != (frontBlock_->localTail = frontBlock_->tail.load())) {
305 fence(memory_order_acquire);
306
307 non_empty_front_block:
308 // Front block not empty, dequeue from here
309 auto element = reinterpret_cast<T*>(frontBlock_->data + blockFront * sizeof(T));
310 result = std::move(*element);
311 element->~T();
312
313 blockFront = (blockFront + 1) & frontBlock_->sizeMask;
314
315 fence(memory_order_release);
316 frontBlock_->front = blockFront;
317 } else if (frontBlock_ != tailBlock.load()) {
318 fence(memory_order_acquire);
319
320 frontBlock_ = frontBlock.load();
321 blockTail = frontBlock_->localTail = frontBlock_->tail.load();
322 blockFront = frontBlock_->front.load();
323 fence(memory_order_acquire);
324
325 if (blockFront != blockTail) {
326 // Oh look, the front block isn't empty after all
327 goto non_empty_front_block;
328 }
329
330 // Front block is empty but there's another block ahead, advance to it
331 Block* nextBlock = frontBlock_->next;
332 // Don't need an acquire fence here since next can only ever be set on the tailBlock,
333 // and we're not the tailBlock, and we did an acquire earlier after reading tailBlock
334 // which ensures next is up-to-date on this CPU in case we recently were at tailBlock.
335
336 size_t nextBlockFront = nextBlock->front.load();
337 size_t nextBlockTail = nextBlock->localTail = nextBlock->tail.load();
338 fence(memory_order_acquire);
339
340 // Since the tailBlock is only ever advanced after being written to,
341 // we know there's for sure an element to dequeue on it
342 assert(nextBlockFront != nextBlockTail);
343 AE_UNUSED(nextBlockTail);
344
345 // We're done with this block, let the producer use it if it needs
346 fence(memory_order_release); // Expose possibly pending changes to frontBlock->front
347 // from last dequeue
348 frontBlock = frontBlock_ = nextBlock;
349
350 compiler_fence(memory_order_release); // Not strictly needed
351
352 auto element = reinterpret_cast<T*>(frontBlock_->data + nextBlockFront * sizeof(T));
353
354 result = std::move(*element);
355 element->~T();
356
357 nextBlockFront = (nextBlockFront + 1) & frontBlock_->sizeMask;
358
359 fence(memory_order_release);
360 frontBlock_->front = nextBlockFront;
361 } else {
362 // No elements in current block and no other block to advance to
363 return false;
364 }
365
366 return true;
367 }
368
369 // Returns a pointer to the front element in the queue (the one that
370 // would be removed next by a call to `try_dequeue` or `pop`). If the
371 // queue appears empty at the time the method is called, nullptr is
372 // returned instead.
373 // Must be called only from the consumer thread.
374 T* peek() const AE_NO_TSAN {
375#ifndef NDEBUG
376 ReentrantGuard guard(this->dequeuing);
377#endif
378 // See try_dequeue() for reasoning
379
380 Block* frontBlock_ = frontBlock.load();
381 size_t blockTail = frontBlock_->localTail;
382 size_t blockFront = frontBlock_->front.load();
383
384 if (blockFront != blockTail ||
385 blockFront != (frontBlock_->localTail = frontBlock_->tail.load())) {
386 fence(memory_order_acquire);
387 non_empty_front_block:
388 return reinterpret_cast<T*>(frontBlock_->data + blockFront * sizeof(T));
389 } else if (frontBlock_ != tailBlock.load()) {
390 fence(memory_order_acquire);
391 frontBlock_ = frontBlock.load();
392 blockTail = frontBlock_->localTail = frontBlock_->tail.load();
393 blockFront = frontBlock_->front.load();
394 fence(memory_order_acquire);
395
396 if (blockFront != blockTail) {
397 goto non_empty_front_block;
398 }
399
400 Block* nextBlock = frontBlock_->next;
401
402 size_t nextBlockFront = nextBlock->front.load();
403 fence(memory_order_acquire);
404
405 assert(nextBlockFront != nextBlock->tail.load());
406 return reinterpret_cast<T*>(nextBlock->data + nextBlockFront * sizeof(T));
407 }
408
409 return nullptr;
410 }
411
412 // Removes the front element from the queue, if any, without returning it.
413 // Returns true on success, or false if the queue appeared empty at the time
414 // `pop` was called.
415 bool pop() AE_NO_TSAN {
416#ifndef NDEBUG
417 ReentrantGuard guard(this->dequeuing);
418#endif
419 // See try_dequeue() for reasoning
420
421 Block* frontBlock_ = frontBlock.load();
422 size_t blockTail = frontBlock_->localTail;
423 size_t blockFront = frontBlock_->front.load();
424
425 if (blockFront != blockTail ||
426 blockFront != (frontBlock_->localTail = frontBlock_->tail.load())) {
427 fence(memory_order_acquire);
428
429 non_empty_front_block:
430 auto element = reinterpret_cast<T*>(frontBlock_->data + blockFront * sizeof(T));
431 element->~T();
432
433 blockFront = (blockFront + 1) & frontBlock_->sizeMask;
434
435 fence(memory_order_release);
436 frontBlock_->front = blockFront;
437 } else if (frontBlock_ != tailBlock.load()) {
438 fence(memory_order_acquire);
439 frontBlock_ = frontBlock.load();
440 blockTail = frontBlock_->localTail = frontBlock_->tail.load();
441 blockFront = frontBlock_->front.load();
442 fence(memory_order_acquire);
443
444 if (blockFront != blockTail) {
445 goto non_empty_front_block;
446 }
447
448 // Front block is empty but there's another block ahead, advance to it
449 Block* nextBlock = frontBlock_->next;
450
451 size_t nextBlockFront = nextBlock->front.load();
452 size_t nextBlockTail = nextBlock->localTail = nextBlock->tail.load();
453 fence(memory_order_acquire);
454
455 assert(nextBlockFront != nextBlockTail);
456 AE_UNUSED(nextBlockTail);
457
458 fence(memory_order_release);
459 frontBlock = frontBlock_ = nextBlock;
460
461 compiler_fence(memory_order_release);
462
463 auto element = reinterpret_cast<T*>(frontBlock_->data + nextBlockFront * sizeof(T));
464 element->~T();
465
466 nextBlockFront = (nextBlockFront + 1) & frontBlock_->sizeMask;
467
468 fence(memory_order_release);
469 frontBlock_->front = nextBlockFront;
470 } else {
471 // No elements in current block and no other block to advance to
472 return false;
473 }
474
475 return true;
476 }
477
478 // Returns the approximate number of items currently in the queue.
479 // Safe to call from both the producer and consumer threads.
480 inline size_t size_approx() const AE_NO_TSAN {
481 size_t result = 0;
482 Block* frontBlock_ = frontBlock.load();
483 Block* block = frontBlock_;
484 do {
485 fence(memory_order_acquire);
486 size_t blockFront = block->front.load();
487 size_t blockTail = block->tail.load();
488 result += (blockTail - blockFront) & block->sizeMask;
489 block = block->next.load();
490 } while (block != frontBlock_);
491 return result;
492 }
493
494 // Returns the total number of items that could be enqueued without incurring
495 // an allocation when this queue is empty.
496 // Safe to call from both the producer and consumer threads.
497 //
498 // NOTE: The actual capacity during usage may be different depending on the consumer.
499 // If the consumer is removing elements concurrently, the producer cannot add to
500 // the block the consumer is removing from until it's completely empty, except in
501 // the case where the producer was writing to the same block the consumer was
502 // reading from the whole time.
503 inline size_t max_capacity() const {
504 size_t result = 0;
505 Block* frontBlock_ = frontBlock.load();
506 Block* block = frontBlock_;
507 do {
508 fence(memory_order_acquire);
509 result += block->sizeMask;
510 block = block->next.load();
511 } while (block != frontBlock_);
512 return result;
513 }
514
515private:
516 enum AllocationMode { CanAlloc, CannotAlloc };
517
518#if MOODYCAMEL_HAS_EMPLACE
519 template <AllocationMode canAlloc, typename... Args>
520 bool inner_enqueue(Args&&... args) AE_NO_TSAN
521#else
522 template <AllocationMode canAlloc, typename U>
523 bool inner_enqueue(U&& element) AE_NO_TSAN
524#endif
525 {
526#ifndef NDEBUG
527 ReentrantGuard guard(this->enqueuing);
528#endif
529
530 // High-level pseudocode (assuming we're allowed to alloc a new block):
531 // If room in tail block, add to tail
532 // Else check next block
533 // If next block is not the head block, enqueue on next block
534 // Else create a new block and enqueue there
535 // Advance tail to the block we just enqueued to
536
537 Block* tailBlock_ = tailBlock.load();
538 size_t blockFront = tailBlock_->localFront;
539 size_t blockTail = tailBlock_->tail.load();
540
541 size_t nextBlockTail = (blockTail + 1) & tailBlock_->sizeMask;
542 if (nextBlockTail != blockFront ||
543 nextBlockTail != (tailBlock_->localFront = tailBlock_->front.load())) {
544 fence(memory_order_acquire);
545 // This block has room for at least one more element
546 char* location = tailBlock_->data + blockTail * sizeof(T);
547#if MOODYCAMEL_HAS_EMPLACE
548 new (location) T(std::forward<Args>(args)...);
549#else
550 new (location) T(std::forward<U>(element));
551#endif
552
553 fence(memory_order_release);
554 tailBlock_->tail = nextBlockTail;
555 } else {
556 fence(memory_order_acquire);
557 if (tailBlock_->next.load() != frontBlock) {
558 // Note that the reason we can't advance to the frontBlock and start adding new
559 // entries there is because if we did, then dequeue would stay in that block,
560 // eventually reading the new values, instead of advancing to the next full block
561 // (whose values were enqueued first and so should be consumed first).
562
563 fence(memory_order_acquire); // Ensure we get latest writes if we got the latest
564 // frontBlock
565
566 // tailBlock is full, but there's a free block ahead, use it
567 Block* tailBlockNext = tailBlock_->next.load();
568 size_t nextBlockFront = tailBlockNext->localFront = tailBlockNext->front.load();
569 nextBlockTail = tailBlockNext->tail.load();
570 fence(memory_order_acquire);
571
572 // This block must be empty since it's not the head block and we
573 // go through the blocks in a circle
574 assert(nextBlockFront == nextBlockTail);
575 tailBlockNext->localFront = nextBlockFront;
576
577 char* location = tailBlockNext->data + nextBlockTail * sizeof(T);
578#if MOODYCAMEL_HAS_EMPLACE
579 new (location) T(std::forward<Args>(args)...);
580#else
581 new (location) T(std::forward<U>(element));
582#endif
583
584 tailBlockNext->tail = (nextBlockTail + 1) & tailBlockNext->sizeMask;
585
586 fence(memory_order_release);
587 tailBlock = tailBlockNext;
588 } else if (canAlloc == CanAlloc) {
589 // tailBlock is full and there's no free block ahead; create a new block
590 auto newBlockSize =
591 largestBlockSize >= MAX_BLOCK_SIZE ? largestBlockSize : largestBlockSize * 2;
592 auto newBlock = make_block(newBlockSize);
593 if (newBlock == nullptr) {
594 // Could not allocate a block!
595 return false;
596 }
597 largestBlockSize = newBlockSize;
598
599#if MOODYCAMEL_HAS_EMPLACE
600 new (newBlock->data) T(std::forward<Args>(args)...);
601#else
602 new (newBlock->data) T(std::forward<U>(element));
603#endif
604 assert(newBlock->front == 0);
605 newBlock->tail = newBlock->localTail = 1;
606
607 newBlock->next = tailBlock_->next.load();
608 tailBlock_->next = newBlock;
609
610 // Might be possible for the dequeue thread to see the new tailBlock->next
611 // *without* seeing the new tailBlock value, but this is OK since it can't
612 // advance to the next block until tailBlock is set anyway (because the only
613 // case where it could try to read the next is if it's already at the tailBlock,
614 // and it won't advance past tailBlock in any circumstance).
615
616 fence(memory_order_release);
617 tailBlock = newBlock;
618 } else if (canAlloc == CannotAlloc) {
619 // Would have had to allocate a new block to enqueue, but not allowed
620 return false;
621 } else {
622 assert(false && "Should be unreachable code");
623 return false;
624 }
625 }
626
627 return true;
628 }
629
630 // Disable copying
631 ReaderWriterQueue(ReaderWriterQueue const&) {}
632
633 // Disable assignment
634 ReaderWriterQueue& operator=(ReaderWriterQueue const&) {}
635
636 AE_FORCEINLINE static size_t ceilToPow2(size_t x) {
637 // From http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2
638 --x;
639 x |= x >> 1;
640 x |= x >> 2;
641 x |= x >> 4;
642 for (size_t i = 1; i < sizeof(size_t); i <<= 1) {
643 x |= x >> (i << 3);
644 }
645 ++x;
646 return x;
647 }
648
649 template <typename U>
650 static AE_FORCEINLINE char* align_for(char* ptr) AE_NO_TSAN {
651 const std::size_t alignment = std::alignment_of<U>::value;
652 return ptr + (alignment - (reinterpret_cast<std::uintptr_t>(ptr) % alignment)) % alignment;
653 }
654
655private:
656#ifndef NDEBUG
657 struct ReentrantGuard {
658 AE_NO_TSAN ReentrantGuard(weak_atomic<bool>& _inSection) : inSection(_inSection) {
659 assert(!inSection &&
660 "Concurrent (or re-entrant) enqueue or dequeue operation detected (only one "
661 "thread at a time may hold the producer or consumer role)");
662 inSection = true;
663 }
664
665 AE_NO_TSAN ~ReentrantGuard() {
666 inSection = false;
667 }
668
669 private:
670 ReentrantGuard& operator=(ReentrantGuard const&);
671
672 private:
673 weak_atomic<bool>& inSection;
674 };
675#endif
676
677 struct Block {
678 // Avoid false-sharing by putting highly contended variables on their own cache lines
679 weak_atomic<size_t> front; // (Atomic) Elements are read from here
680 size_t localTail; // An uncontended shadow copy of tail, owned by the consumer
681
682 char cachelineFiller0[MOODYCAMEL_CACHE_LINE_SIZE - sizeof(weak_atomic<size_t>) -
683 sizeof(size_t)];
684 weak_atomic<size_t> tail; // (Atomic) Elements are enqueued here
685 size_t localFront;
686
687 char cachelineFiller1[MOODYCAMEL_CACHE_LINE_SIZE - sizeof(weak_atomic<size_t>) -
688 sizeof(size_t)]; // next isn't very contended, but we don't want it on
689 // the same cache line as tail (which is)
690 weak_atomic<Block*> next; // (Atomic)
691
692 char* data; // Contents (on heap) are aligned to T's alignment
693
694 const size_t sizeMask;
695
696 // size must be a power of two (and greater than 0)
697 AE_NO_TSAN Block(size_t const& _size, char* _rawThis, char* _data)
698 : front(0UL), localTail(0), tail(0UL), localFront(0), next(nullptr), data(_data),
699 sizeMask(_size - 1), rawThis(_rawThis) {}
700
701 private:
702 // C4512 - Assignment operator could not be generated
703 Block& operator=(Block const&);
704
705 public:
706 char* rawThis;
707 };
708
709 static Block* make_block(size_t capacity) AE_NO_TSAN {
710 // Allocate enough memory for the block itself, as well as all the elements it will contain
711 auto size = sizeof(Block) + std::alignment_of<Block>::value - 1;
712 size += sizeof(T) * capacity + std::alignment_of<T>::value - 1;
713 auto newBlockRaw = static_cast<char*>(std::malloc(size));
714 if (newBlockRaw == nullptr) {
715 return nullptr;
716 }
717
718 auto newBlockAligned = align_for<Block>(newBlockRaw);
719 auto newBlockData = align_for<T>(newBlockAligned + sizeof(Block));
720 return new (newBlockAligned) Block(capacity, newBlockRaw, newBlockData);
721 }
722
723private:
724 weak_atomic<Block*> frontBlock; // (Atomic) Elements are dequeued from this block
725
726 char cachelineFiller[MOODYCAMEL_CACHE_LINE_SIZE - sizeof(weak_atomic<Block*>)];
727 weak_atomic<Block*> tailBlock; // (Atomic) Elements are enqueued to this block
728
729 size_t largestBlockSize;
730
731#ifndef NDEBUG
732 weak_atomic<bool> enqueuing;
733 mutable weak_atomic<bool> dequeuing;
734#endif
735};
736
737// Like ReaderWriterQueue, but also providees blocking operations
738template <typename T, size_t MAX_BLOCK_SIZE = 512>
739class BlockingReaderWriterQueue {
740private:
741 typedef ::Common::ReaderWriterQueue<T, MAX_BLOCK_SIZE> ReaderWriterQueue;
742
743public:
744 explicit BlockingReaderWriterQueue(size_t size = 15) AE_NO_TSAN
745 : inner(size),
746 sema(new spsc_sema::LightweightSemaphore()) {}
747
748 BlockingReaderWriterQueue(BlockingReaderWriterQueue&& other) AE_NO_TSAN
749 : inner(std::move(other.inner)),
750 sema(std::move(other.sema)) {}
751
752 BlockingReaderWriterQueue& operator=(BlockingReaderWriterQueue&& other) AE_NO_TSAN {
753 std::swap(sema, other.sema);
754 std::swap(inner, other.inner);
755 return *this;
756 }
757
758 // Enqueues a copy of element if there is room in the queue.
759 // Returns true if the element was enqueued, false otherwise.
760 // Does not allocate memory.
761 AE_FORCEINLINE bool try_enqueue(T const& element) AE_NO_TSAN {
762 if (inner.try_enqueue(element)) {
763 sema->signal();
764 return true;
765 }
766 return false;
767 }
768
769 // Enqueues a moved copy of element if there is room in the queue.
770 // Returns true if the element was enqueued, false otherwise.
771 // Does not allocate memory.
772 AE_FORCEINLINE bool try_enqueue(T&& element) AE_NO_TSAN {
773 if (inner.try_enqueue(std::forward<T>(element))) {
774 sema->signal();
775 return true;
776 }
777 return false;
778 }
779
780#if MOODYCAMEL_HAS_EMPLACE
781 // Like try_enqueue() but with emplace semantics (i.e. construct-in-place).
782 template <typename... Args>
783 AE_FORCEINLINE bool try_emplace(Args&&... args) AE_NO_TSAN {
784 if (inner.try_emplace(std::forward<Args>(args)...)) {
785 sema->signal();
786 return true;
787 }
788 return false;
789 }
790#endif
791
792 // Enqueues a copy of element on the queue.
793 // Allocates an additional block of memory if needed.
794 // Only fails (returns false) if memory allocation fails.
795 AE_FORCEINLINE bool enqueue(T const& element) AE_NO_TSAN {
796 if (inner.enqueue(element)) {
797 sema->signal();
798 return true;
799 }
800 return false;
801 }
802
803 // Enqueues a moved copy of element on the queue.
804 // Allocates an additional block of memory if needed.
805 // Only fails (returns false) if memory allocation fails.
806 AE_FORCEINLINE bool enqueue(T&& element) AE_NO_TSAN {
807 if (inner.enqueue(std::forward<T>(element))) {
808 sema->signal();
809 return true;
810 }
811 return false;
812 }
813
814#if MOODYCAMEL_HAS_EMPLACE
815 // Like enqueue() but with emplace semantics (i.e. construct-in-place).
816 template <typename... Args>
817 AE_FORCEINLINE bool emplace(Args&&... args) AE_NO_TSAN {
818 if (inner.emplace(std::forward<Args>(args)...)) {
819 sema->signal();
820 return true;
821 }
822 return false;
823 }
824#endif
825
826 // Attempts to dequeue an element; if the queue is empty,
827 // returns false instead. If the queue has at least one element,
828 // moves front to result using operator=, then returns true.
829 template <typename U>
830 bool try_dequeue(U& result) AE_NO_TSAN {
831 if (sema->tryWait()) {
832 bool success = inner.try_dequeue(result);
833 assert(success);
834 AE_UNUSED(success);
835 return true;
836 }
837 return false;
838 }
839
840 // Attempts to dequeue an element; if the queue is empty,
841 // waits until an element is available, then dequeues it.
842 template <typename U>
843 void wait_dequeue(U& result) AE_NO_TSAN {
844 while (!sema->wait())
845 ;
846 bool success = inner.try_dequeue(result);
847 AE_UNUSED(result);
848 assert(success);
849 AE_UNUSED(success);
850 }
851
852 // Attempts to dequeue an element; if the queue is empty,
853 // waits until an element is available up to the specified timeout,
854 // then dequeues it and returns true, or returns false if the timeout
855 // expires before an element can be dequeued.
856 // Using a negative timeout indicates an indefinite timeout,
857 // and is thus functionally equivalent to calling wait_dequeue.
858 template <typename U>
859 bool wait_dequeue_timed(U& result, std::int64_t timeout_usecs) AE_NO_TSAN {
860 if (!sema->wait(timeout_usecs)) {
861 return false;
862 }
863 bool success = inner.try_dequeue(result);
864 AE_UNUSED(result);
865 assert(success);
866 AE_UNUSED(success);
867 return true;
868 }
869
870#if __cplusplus > 199711L || _MSC_VER >= 1700
871 // Attempts to dequeue an element; if the queue is empty,
872 // waits until an element is available up to the specified timeout,
873 // then dequeues it and returns true, or returns false if the timeout
874 // expires before an element can be dequeued.
875 // Using a negative timeout indicates an indefinite timeout,
876 // and is thus functionally equivalent to calling wait_dequeue.
877 template <typename U, typename Rep, typename Period>
878 inline bool wait_dequeue_timed(U& result,
879 std::chrono::duration<Rep, Period> const& timeout) AE_NO_TSAN {
880 return wait_dequeue_timed(
881 result, std::chrono::duration_cast<std::chrono::microseconds>(timeout).count());
882 }
883#endif
884
885 // Returns a pointer to the front element in the queue (the one that
886 // would be removed next by a call to `try_dequeue` or `pop`). If the
887 // queue appears empty at the time the method is called, nullptr is
888 // returned instead.
889 // Must be called only from the consumer thread.
890 AE_FORCEINLINE T* peek() const AE_NO_TSAN {
891 return inner.peek();
892 }
893
894 // Removes the front element from the queue, if any, without returning it.
895 // Returns true on success, or false if the queue appeared empty at the time
896 // `pop` was called.
897 AE_FORCEINLINE bool pop() AE_NO_TSAN {
898 if (sema->tryWait()) {
899 bool result = inner.pop();
900 assert(result);
901 AE_UNUSED(result);
902 return true;
903 }
904 return false;
905 }
906
907 // Returns the approximate number of items currently in the queue.
908 // Safe to call from both the producer and consumer threads.
909 AE_FORCEINLINE size_t size_approx() const AE_NO_TSAN {
910 return sema->availableApprox();
911 }
912
913 // Returns the total number of items that could be enqueued without incurring
914 // an allocation when this queue is empty.
915 // Safe to call from both the producer and consumer threads.
916 //
917 // NOTE: The actual capacity during usage may be different depending on the consumer.
918 // If the consumer is removing elements concurrently, the producer cannot add to
919 // the block the consumer is removing from until it's completely empty, except in
920 // the case where the producer was writing to the same block the consumer was
921 // reading from the whole time.
922 AE_FORCEINLINE size_t max_capacity() const {
923 return inner.max_capacity();
924 }
925
926private:
927 // Disable copying & assignment
928 BlockingReaderWriterQueue(BlockingReaderWriterQueue const&) {}
929 BlockingReaderWriterQueue& operator=(BlockingReaderWriterQueue const&) {}
930
931private:
932 ReaderWriterQueue inner;
933 std::unique_ptr<spsc_sema::LightweightSemaphore> sema;
934};
935
936} // namespace Common
937
938#ifdef AE_VCPP
939#pragma warning(pop)
940#endif
diff --git a/src/common/ring_buffer.h b/src/common/ring_buffer.h
index 4a8d09806..4c328ab44 100644
--- a/src/common/ring_buffer.h
+++ b/src/common/ring_buffer.h
@@ -1,6 +1,5 @@
1// Copyright 2018 yuzu emulator team 1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#pragma once 4#pragma once
6 5
@@ -12,7 +11,6 @@
12#include <new> 11#include <new>
13#include <type_traits> 12#include <type_traits>
14#include <vector> 13#include <vector>
15#include "common/common_types.h"
16 14
17namespace Common { 15namespace Common {
18 16
diff --git a/src/common/scm_rev.cpp.in b/src/common/scm_rev.cpp.in
index cc88994c6..f0c124d69 100644
--- a/src/common/scm_rev.cpp.in
+++ b/src/common/scm_rev.cpp.in
@@ -1,6 +1,5 @@
1// Copyright 2014 Citra Emulator Project 1// SPDX-FileCopyrightText: 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#include "common/scm_rev.h" 4#include "common/scm_rev.h"
6 5
diff --git a/src/common/scm_rev.h b/src/common/scm_rev.h
index 563015ec9..88404316a 100644
--- a/src/common/scm_rev.h
+++ b/src/common/scm_rev.h
@@ -1,6 +1,5 @@
1// Copyright 2014 Citra Emulator Project 1// SPDX-FileCopyrightText: 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#pragma once 4#pragma once
6 5
diff --git a/src/common/scope_exit.h b/src/common/scope_exit.h
index 35dac3a8f..e9c789c88 100644
--- a/src/common/scope_exit.h
+++ b/src/common/scope_exit.h
@@ -1,6 +1,5 @@
1// Copyright 2014 Citra Emulator Project 1// SPDX-FileCopyrightText: 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#pragma once 4#pragma once
6 5
diff --git a/src/common/settings.cpp b/src/common/settings.cpp
index 6964a8273..0a560ebb7 100644
--- a/src/common/settings.cpp
+++ b/src/common/settings.cpp
@@ -1,6 +1,5 @@
1// Copyright 2021 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#include <string_view> 4#include <string_view>
6 5
@@ -11,7 +10,7 @@
11 10
12namespace Settings { 11namespace Settings {
13 12
14Values values = {}; 13Values values;
15static bool configuring_global = true; 14static bool configuring_global = true;
16 15
17std::string GetTimeZoneString() { 16std::string GetTimeZoneString() {
@@ -63,7 +62,8 @@ void LogSettings() {
63 log_setting("Renderer_UseAsynchronousShaders", values.use_asynchronous_shaders.GetValue()); 62 log_setting("Renderer_UseAsynchronousShaders", values.use_asynchronous_shaders.GetValue());
64 log_setting("Renderer_AnisotropicFilteringLevel", values.max_anisotropy.GetValue()); 63 log_setting("Renderer_AnisotropicFilteringLevel", values.max_anisotropy.GetValue());
65 log_setting("Audio_OutputEngine", values.sink_id.GetValue()); 64 log_setting("Audio_OutputEngine", values.sink_id.GetValue());
66 log_setting("Audio_OutputDevice", values.audio_device_id.GetValue()); 65 log_setting("Audio_OutputDevice", values.audio_output_device_id.GetValue());
66 log_setting("Audio_InputDevice", values.audio_input_device_id.GetValue());
67 log_setting("DataStorage_UseVirtualSd", values.use_virtual_sd.GetValue()); 67 log_setting("DataStorage_UseVirtualSd", values.use_virtual_sd.GetValue());
68 log_path("DataStorage_CacheDir", Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir)); 68 log_path("DataStorage_CacheDir", Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir));
69 log_path("DataStorage_ConfigDir", Common::FS::GetYuzuPath(Common::FS::YuzuPath::ConfigDir)); 69 log_path("DataStorage_ConfigDir", Common::FS::GetYuzuPath(Common::FS::YuzuPath::ConfigDir));
@@ -71,6 +71,7 @@ void LogSettings() {
71 log_path("DataStorage_NANDDir", Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir)); 71 log_path("DataStorage_NANDDir", Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir));
72 log_path("DataStorage_SDMCDir", Common::FS::GetYuzuPath(Common::FS::YuzuPath::SDMCDir)); 72 log_path("DataStorage_SDMCDir", Common::FS::GetYuzuPath(Common::FS::YuzuPath::SDMCDir));
73 log_setting("Debugging_ProgramArgs", values.program_args.GetValue()); 73 log_setting("Debugging_ProgramArgs", values.program_args.GetValue());
74 log_setting("Debugging_GDBStub", values.use_gdbstub.GetValue());
74 log_setting("Input_EnableMotion", values.motion_enabled.GetValue()); 75 log_setting("Input_EnableMotion", values.motion_enabled.GetValue());
75 log_setting("Input_EnableVibration", values.vibration_enabled.GetValue()); 76 log_setting("Input_EnableVibration", values.vibration_enabled.GetValue());
76 log_setting("Input_EnableRawInput", values.enable_raw_input.GetValue()); 77 log_setting("Input_EnableRawInput", values.enable_raw_input.GetValue());
@@ -104,7 +105,7 @@ float Volume() {
104 if (values.audio_muted) { 105 if (values.audio_muted) {
105 return 0.0f; 106 return 0.0f;
106 } 107 }
107 return values.volume.GetValue() / 100.0f; 108 return values.volume.GetValue() / static_cast<f32>(values.volume.GetDefault());
108} 109}
109 110
110void UpdateRescalingInfo() { 111void UpdateRescalingInfo() {
@@ -147,7 +148,7 @@ void UpdateRescalingInfo() {
147 info.down_shift = 0; 148 info.down_shift = 0;
148 break; 149 break;
149 default: 150 default:
150 UNREACHABLE(); 151 ASSERT(false);
151 info.up_scale = 1; 152 info.up_scale = 1;
152 info.down_shift = 0; 153 info.down_shift = 0;
153 } 154 }
@@ -167,6 +168,7 @@ void RestoreGlobalState(bool is_powered_on) {
167 168
168 // Core 169 // Core
169 values.use_multi_core.SetGlobal(true); 170 values.use_multi_core.SetGlobal(true);
171 values.use_extended_memory_layout.SetGlobal(true);
170 172
171 // CPU 173 // CPU
172 values.cpu_accuracy.SetGlobal(true); 174 values.cpu_accuracy.SetGlobal(true);
@@ -175,6 +177,7 @@ void RestoreGlobalState(bool is_powered_on) {
175 values.cpuopt_unsafe_ignore_standard_fpcr.SetGlobal(true); 177 values.cpuopt_unsafe_ignore_standard_fpcr.SetGlobal(true);
176 values.cpuopt_unsafe_inaccurate_nan.SetGlobal(true); 178 values.cpuopt_unsafe_inaccurate_nan.SetGlobal(true);
177 values.cpuopt_unsafe_fastmem_check.SetGlobal(true); 179 values.cpuopt_unsafe_fastmem_check.SetGlobal(true);
180 values.cpuopt_unsafe_ignore_global_monitor.SetGlobal(true);
178 181
179 // Renderer 182 // Renderer
180 values.renderer_backend.SetGlobal(true); 183 values.renderer_backend.SetGlobal(true);
@@ -183,7 +186,6 @@ void RestoreGlobalState(bool is_powered_on) {
183 values.max_anisotropy.SetGlobal(true); 186 values.max_anisotropy.SetGlobal(true);
184 values.use_speed_limit.SetGlobal(true); 187 values.use_speed_limit.SetGlobal(true);
185 values.speed_limit.SetGlobal(true); 188 values.speed_limit.SetGlobal(true);
186 values.fps_cap.SetGlobal(true);
187 values.use_disk_shader_cache.SetGlobal(true); 189 values.use_disk_shader_cache.SetGlobal(true);
188 values.gpu_accuracy.SetGlobal(true); 190 values.gpu_accuracy.SetGlobal(true);
189 values.use_asynchronous_gpu_emulation.SetGlobal(true); 191 values.use_asynchronous_gpu_emulation.SetGlobal(true);
@@ -193,6 +195,7 @@ void RestoreGlobalState(bool is_powered_on) {
193 values.shader_backend.SetGlobal(true); 195 values.shader_backend.SetGlobal(true);
194 values.use_asynchronous_shaders.SetGlobal(true); 196 values.use_asynchronous_shaders.SetGlobal(true);
195 values.use_fast_gpu_time.SetGlobal(true); 197 values.use_fast_gpu_time.SetGlobal(true);
198 values.use_pessimistic_flushes.SetGlobal(true);
196 values.bg_red.SetGlobal(true); 199 values.bg_red.SetGlobal(true);
197 values.bg_green.SetGlobal(true); 200 values.bg_green.SetGlobal(true);
198 values.bg_blue.SetGlobal(true); 201 values.bg_blue.SetGlobal(true);
diff --git a/src/common/settings.h b/src/common/settings.h
index 9bee6e10f..d2452c93b 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -1,6 +1,5 @@
1// Copyright 2021 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#pragma once 4#pragma once
6 5
@@ -38,6 +37,7 @@ enum class CPUAccuracy : u32 {
38 Auto = 0, 37 Auto = 0,
39 Accurate = 1, 38 Accurate = 1,
40 Unsafe = 2, 39 Unsafe = 2,
40 Paranoid = 3,
41}; 41};
42 42
43enum class FullscreenMode : u32 { 43enum class FullscreenMode : u32 {
@@ -101,15 +101,15 @@ struct ResolutionScalingInfo {
101 } 101 }
102}; 102};
103 103
104/** The BasicSetting class is a simple resource manager. It defines a label and default value 104/** The Setting class is a simple resource manager. It defines a label and default value alongside
105 * alongside the actual value of the setting for simpler and less-error prone use with frontend 105 * the actual value of the setting for simpler and less-error prone use with frontend
106 * configurations. Setting a default value and label is required, though subclasses may deviate from 106 * configurations. Specifying a default value and label is required. A minimum and maximum range can
107 * this requirement. 107 * be specified for sanitization.
108 */ 108 */
109template <typename Type> 109template <typename Type, bool ranged = false>
110class BasicSetting { 110class Setting {
111protected: 111protected:
112 BasicSetting() = default; 112 Setting() = default;
113 113
114 /** 114 /**
115 * Only sets the setting to the given initializer, leaving the other members to their default 115 * Only sets the setting to the given initializer, leaving the other members to their default
@@ -117,7 +117,7 @@ protected:
117 * 117 *
118 * @param global_val Initial value of the setting 118 * @param global_val Initial value of the setting
119 */ 119 */
120 explicit BasicSetting(const Type& global_val) : global{global_val} {} 120 explicit Setting(const Type& val) : value{val} {}
121 121
122public: 122public:
123 /** 123 /**
@@ -126,9 +126,22 @@ public:
126 * @param default_val Intial value of the setting, and default value of the setting 126 * @param default_val Intial value of the setting, and default value of the setting
127 * @param name Label for the setting 127 * @param name Label for the setting
128 */ 128 */
129 explicit BasicSetting(const Type& default_val, const std::string& name) 129 explicit Setting(const Type& default_val, const std::string& name) requires(!ranged)
130 : default_value{default_val}, global{default_val}, label{name} {} 130 : value{default_val}, default_value{default_val}, label{name} {}
131 virtual ~BasicSetting() = default; 131 virtual ~Setting() = default;
132
133 /**
134 * Sets a default value, minimum value, maximum value, and label.
135 *
136 * @param default_val Intial value of the setting, and default value of the setting
137 * @param min_val Sets the minimum allowed value of the setting
138 * @param max_val Sets the maximum allowed value of the setting
139 * @param name Label for the setting
140 */
141 explicit Setting(const Type& default_val, const Type& min_val, const Type& max_val,
142 const std::string& name) requires(ranged)
143 : value{default_val},
144 default_value{default_val}, maximum{max_val}, minimum{min_val}, label{name} {}
132 145
133 /** 146 /**
134 * Returns a reference to the setting's value. 147 * Returns a reference to the setting's value.
@@ -136,17 +149,17 @@ public:
136 * @returns A reference to the setting 149 * @returns A reference to the setting
137 */ 150 */
138 [[nodiscard]] virtual const Type& GetValue() const { 151 [[nodiscard]] virtual const Type& GetValue() const {
139 return global; 152 return value;
140 } 153 }
141 154
142 /** 155 /**
143 * Sets the setting to the given value. 156 * Sets the setting to the given value.
144 * 157 *
145 * @param value The desired value 158 * @param val The desired value
146 */ 159 */
147 virtual void SetValue(const Type& value) { 160 virtual void SetValue(const Type& val) {
148 Type temp{value}; 161 Type temp{ranged ? std::clamp(val, minimum, maximum) : val};
149 std::swap(global, temp); 162 std::swap(value, temp);
150 } 163 }
151 164
152 /** 165 /**
@@ -170,14 +183,14 @@ public:
170 /** 183 /**
171 * Assigns a value to the setting. 184 * Assigns a value to the setting.
172 * 185 *
173 * @param value The desired setting value 186 * @param val The desired setting value
174 * 187 *
175 * @returns A reference to the setting 188 * @returns A reference to the setting
176 */ 189 */
177 virtual const Type& operator=(const Type& value) { 190 virtual const Type& operator=(const Type& val) {
178 Type temp{value}; 191 Type temp{ranged ? std::clamp(val, minimum, maximum) : val};
179 std::swap(global, temp); 192 std::swap(value, temp);
180 return global; 193 return value;
181 } 194 }
182 195
183 /** 196 /**
@@ -186,72 +199,27 @@ public:
186 * @returns A reference to the setting 199 * @returns A reference to the setting
187 */ 200 */
188 explicit virtual operator const Type&() const { 201 explicit virtual operator const Type&() const {
189 return global; 202 return value;
190 } 203 }
191 204
192protected: 205protected:
206 Type value{}; ///< The setting
193 const Type default_value{}; ///< The default value 207 const Type default_value{}; ///< The default value
194 Type global{}; ///< The setting 208 const Type maximum{}; ///< Maximum allowed value of the setting
209 const Type minimum{}; ///< Minimum allowed value of the setting
195 const std::string label{}; ///< The setting's label 210 const std::string label{}; ///< The setting's label
196}; 211};
197 212
198/** 213/**
199 * BasicRangedSetting class is intended for use with quantifiable settings that need a more 214 * The SwitchableSetting class is a slightly more complex version of the Setting class. This adds a
200 * restrictive range than implicitly defined by its type. Implements a minimum and maximum that is
201 * simply used to sanitize SetValue and the assignment overload.
202 */
203template <typename Type>
204class BasicRangedSetting : virtual public BasicSetting<Type> {
205public:
206 /**
207 * Sets a default value, minimum value, maximum value, and label.
208 *
209 * @param default_val Intial value of the setting, and default value of the setting
210 * @param min_val Sets the minimum allowed value of the setting
211 * @param max_val Sets the maximum allowed value of the setting
212 * @param name Label for the setting
213 */
214 explicit BasicRangedSetting(const Type& default_val, const Type& min_val, const Type& max_val,
215 const std::string& name)
216 : BasicSetting<Type>{default_val, name}, minimum{min_val}, maximum{max_val} {}
217 virtual ~BasicRangedSetting() = default;
218
219 /**
220 * Like BasicSetting's SetValue, except value is clamped to the range of the setting.
221 *
222 * @param value The desired value
223 */
224 void SetValue(const Type& value) override {
225 this->global = std::clamp(value, minimum, maximum);
226 }
227
228 /**
229 * Like BasicSetting's assignment overload, except value is clamped to the range of the setting.
230 *
231 * @param value The desired value
232 * @returns A reference to the setting's value
233 */
234 const Type& operator=(const Type& value) override {
235 this->global = std::clamp(value, minimum, maximum);
236 return this->global;
237 }
238
239 const Type minimum; ///< Minimum allowed value of the setting
240 const Type maximum; ///< Maximum allowed value of the setting
241};
242
243/**
244 * The Setting class is a slightly more complex version of the BasicSetting class. This adds a
245 * custom setting to switch to when a guest application specifically requires it. The effect is that 215 * custom setting to switch to when a guest application specifically requires it. The effect is that
246 * other components of the emulator can access the setting's intended value without any need for the 216 * other components of the emulator can access the setting's intended value without any need for the
247 * component to ask whether the custom or global setting is needed at the moment. 217 * component to ask whether the custom or global setting is needed at the moment.
248 * 218 *
249 * By default, the global setting is used. 219 * By default, the global setting is used.
250 *
251 * Like the BasicSetting, this requires setting a default value and label to use.
252 */ 220 */
253template <typename Type> 221template <typename Type, bool ranged = false>
254class Setting : virtual public BasicSetting<Type> { 222class SwitchableSetting : virtual public Setting<Type, ranged> {
255public: 223public:
256 /** 224 /**
257 * Sets a default value, label, and setting value. 225 * Sets a default value, label, and setting value.
@@ -259,9 +227,21 @@ public:
259 * @param default_val Intial value of the setting, and default value of the setting 227 * @param default_val Intial value of the setting, and default value of the setting
260 * @param name Label for the setting 228 * @param name Label for the setting
261 */ 229 */
262 explicit Setting(const Type& default_val, const std::string& name) 230 explicit SwitchableSetting(const Type& default_val, const std::string& name) requires(!ranged)
263 : BasicSetting<Type>(default_val, name) {} 231 : Setting<Type>{default_val, name} {}
264 virtual ~Setting() = default; 232 virtual ~SwitchableSetting() = default;
233
234 /**
235 * Sets a default value, minimum value, maximum value, and label.
236 *
237 * @param default_val Intial value of the setting, and default value of the setting
238 * @param min_val Sets the minimum allowed value of the setting
239 * @param max_val Sets the maximum allowed value of the setting
240 * @param name Label for the setting
241 */
242 explicit SwitchableSetting(const Type& default_val, const Type& min_val, const Type& max_val,
243 const std::string& name) requires(ranged)
244 : Setting<Type, true>{default_val, min_val, max_val, name} {}
265 245
266 /** 246 /**
267 * Tells this setting to represent either the global or custom setting when other member 247 * Tells this setting to represent either the global or custom setting when other member
@@ -292,13 +272,13 @@ public:
292 */ 272 */
293 [[nodiscard]] virtual const Type& GetValue() const override { 273 [[nodiscard]] virtual const Type& GetValue() const override {
294 if (use_global) { 274 if (use_global) {
295 return this->global; 275 return this->value;
296 } 276 }
297 return custom; 277 return custom;
298 } 278 }
299 [[nodiscard]] virtual const Type& GetValue(bool need_global) const { 279 [[nodiscard]] virtual const Type& GetValue(bool need_global) const {
300 if (use_global || need_global) { 280 if (use_global || need_global) {
301 return this->global; 281 return this->value;
302 } 282 }
303 return custom; 283 return custom;
304 } 284 }
@@ -306,12 +286,12 @@ public:
306 /** 286 /**
307 * Sets the current setting value depending on the global state. 287 * Sets the current setting value depending on the global state.
308 * 288 *
309 * @param value The new value 289 * @param val The new value
310 */ 290 */
311 void SetValue(const Type& value) override { 291 void SetValue(const Type& val) override {
312 Type temp{value}; 292 Type temp{ranged ? std::clamp(val, this->minimum, this->maximum) : val};
313 if (use_global) { 293 if (use_global) {
314 std::swap(this->global, temp); 294 std::swap(this->value, temp);
315 } else { 295 } else {
316 std::swap(custom, temp); 296 std::swap(custom, temp);
317 } 297 }
@@ -320,15 +300,15 @@ public:
320 /** 300 /**
321 * Assigns the current setting value depending on the global state. 301 * Assigns the current setting value depending on the global state.
322 * 302 *
323 * @param value The new value 303 * @param val The new value
324 * 304 *
325 * @returns A reference to the current setting value 305 * @returns A reference to the current setting value
326 */ 306 */
327 const Type& operator=(const Type& value) override { 307 const Type& operator=(const Type& val) override {
328 Type temp{value}; 308 Type temp{ranged ? std::clamp(val, this->minimum, this->maximum) : val};
329 if (use_global) { 309 if (use_global) {
330 std::swap(this->global, temp); 310 std::swap(this->value, temp);
331 return this->global; 311 return this->value;
332 } 312 }
333 std::swap(custom, temp); 313 std::swap(custom, temp);
334 return custom; 314 return custom;
@@ -341,7 +321,7 @@ public:
341 */ 321 */
342 virtual explicit operator const Type&() const override { 322 virtual explicit operator const Type&() const override {
343 if (use_global) { 323 if (use_global) {
344 return this->global; 324 return this->value;
345 } 325 }
346 return custom; 326 return custom;
347 } 327 }
@@ -352,75 +332,6 @@ protected:
352}; 332};
353 333
354/** 334/**
355 * RangedSetting is a Setting that implements a maximum and minimum value for its setting. Intended
356 * for use with quantifiable settings.
357 */
358template <typename Type>
359class RangedSetting final : public BasicRangedSetting<Type>, public Setting<Type> {
360public:
361 /**
362 * Sets a default value, minimum value, maximum value, and label.
363 *
364 * @param default_val Intial value of the setting, and default value of the setting
365 * @param min_val Sets the minimum allowed value of the setting
366 * @param max_val Sets the maximum allowed value of the setting
367 * @param name Label for the setting
368 */
369 explicit RangedSetting(const Type& default_val, const Type& min_val, const Type& max_val,
370 const std::string& name)
371 : BasicSetting<Type>{default_val, name},
372 BasicRangedSetting<Type>{default_val, min_val, max_val, name}, Setting<Type>{default_val,
373 name} {}
374 virtual ~RangedSetting() = default;
375
376 // The following are needed to avoid a MSVC bug
377 // (source: https://stackoverflow.com/questions/469508)
378 [[nodiscard]] const Type& GetValue() const override {
379 return Setting<Type>::GetValue();
380 }
381 [[nodiscard]] const Type& GetValue(bool need_global) const override {
382 return Setting<Type>::GetValue(need_global);
383 }
384 explicit operator const Type&() const override {
385 if (this->use_global) {
386 return this->global;
387 }
388 return this->custom;
389 }
390
391 /**
392 * Like BasicSetting's SetValue, except value is clamped to the range of the setting. Sets the
393 * appropriate value depending on the global state.
394 *
395 * @param value The desired value
396 */
397 void SetValue(const Type& value) override {
398 const Type temp = std::clamp(value, this->minimum, this->maximum);
399 if (this->use_global) {
400 this->global = temp;
401 }
402 this->custom = temp;
403 }
404
405 /**
406 * Like BasicSetting's assignment overload, except value is clamped to the range of the setting.
407 * Uses the appropriate value depending on the global state.
408 *
409 * @param value The desired value
410 * @returns A reference to the setting's value
411 */
412 const Type& operator=(const Type& value) override {
413 const Type temp = std::clamp(value, this->minimum, this->maximum);
414 if (this->use_global) {
415 this->global = temp;
416 return this->global;
417 }
418 this->custom = temp;
419 return this->custom;
420 }
421};
422
423/**
424 * The InputSetting class allows for getting a reference to either the global or custom members. 335 * The InputSetting class allows for getting a reference to either the global or custom members.
425 * This is required as we cannot easily modify the values of user-defined types within containers 336 * This is required as we cannot easily modify the values of user-defined types within containers
426 * using the SetValue() member function found in the Setting class. The primary purpose of this 337 * using the SetValue() member function found in the Setting class. The primary purpose of this
@@ -431,7 +342,7 @@ template <typename Type>
431class InputSetting final { 342class InputSetting final {
432public: 343public:
433 InputSetting() = default; 344 InputSetting() = default;
434 explicit InputSetting(Type val) : BasicSetting<Type>(val) {} 345 explicit InputSetting(Type val) : Setting<Type>(val) {}
435 ~InputSetting() = default; 346 ~InputSetting() = default;
436 void SetGlobal(bool to_global) { 347 void SetGlobal(bool to_global) {
437 use_global = to_global; 348 use_global = to_global;
@@ -459,167 +370,181 @@ struct TouchFromButtonMap {
459 370
460struct Values { 371struct Values {
461 // Audio 372 // Audio
462 BasicSetting<std::string> audio_device_id{"auto", "output_device"}; 373 Setting<std::string> sink_id{"auto", "output_engine"};
463 BasicSetting<std::string> sink_id{"auto", "output_engine"}; 374 Setting<std::string> audio_output_device_id{"auto", "output_device"};
464 BasicSetting<bool> audio_muted{false, "audio_muted"}; 375 Setting<std::string> audio_input_device_id{"auto", "input_device"};
465 RangedSetting<u8> volume{100, 0, 100, "volume"}; 376 Setting<bool> audio_muted{false, "audio_muted"};
377 SwitchableSetting<u8, true> volume{100, 0, 200, "volume"};
378 Setting<bool> dump_audio_commands{false, "dump_audio_commands"};
466 379
467 // Core 380 // Core
468 Setting<bool> use_multi_core{true, "use_multi_core"}; 381 SwitchableSetting<bool> use_multi_core{true, "use_multi_core"};
382 SwitchableSetting<bool> use_extended_memory_layout{false, "use_extended_memory_layout"};
469 383
470 // Cpu 384 // Cpu
471 RangedSetting<CPUAccuracy> cpu_accuracy{CPUAccuracy::Auto, CPUAccuracy::Auto, 385 SwitchableSetting<CPUAccuracy, true> cpu_accuracy{CPUAccuracy::Auto, CPUAccuracy::Auto,
472 CPUAccuracy::Unsafe, "cpu_accuracy"}; 386 CPUAccuracy::Paranoid, "cpu_accuracy"};
473 // TODO: remove cpu_accuracy_first_time, migration setting added 8 July 2021 387 // TODO: remove cpu_accuracy_first_time, migration setting added 8 July 2021
474 BasicSetting<bool> cpu_accuracy_first_time{true, "cpu_accuracy_first_time"}; 388 Setting<bool> cpu_accuracy_first_time{true, "cpu_accuracy_first_time"};
475 BasicSetting<bool> cpu_debug_mode{false, "cpu_debug_mode"}; 389 Setting<bool> cpu_debug_mode{false, "cpu_debug_mode"};
476 390
477 BasicSetting<bool> cpuopt_page_tables{true, "cpuopt_page_tables"}; 391 Setting<bool> cpuopt_page_tables{true, "cpuopt_page_tables"};
478 BasicSetting<bool> cpuopt_block_linking{true, "cpuopt_block_linking"}; 392 Setting<bool> cpuopt_block_linking{true, "cpuopt_block_linking"};
479 BasicSetting<bool> cpuopt_return_stack_buffer{true, "cpuopt_return_stack_buffer"}; 393 Setting<bool> cpuopt_return_stack_buffer{true, "cpuopt_return_stack_buffer"};
480 BasicSetting<bool> cpuopt_fast_dispatcher{true, "cpuopt_fast_dispatcher"}; 394 Setting<bool> cpuopt_fast_dispatcher{true, "cpuopt_fast_dispatcher"};
481 BasicSetting<bool> cpuopt_context_elimination{true, "cpuopt_context_elimination"}; 395 Setting<bool> cpuopt_context_elimination{true, "cpuopt_context_elimination"};
482 BasicSetting<bool> cpuopt_const_prop{true, "cpuopt_const_prop"}; 396 Setting<bool> cpuopt_const_prop{true, "cpuopt_const_prop"};
483 BasicSetting<bool> cpuopt_misc_ir{true, "cpuopt_misc_ir"}; 397 Setting<bool> cpuopt_misc_ir{true, "cpuopt_misc_ir"};
484 BasicSetting<bool> cpuopt_reduce_misalign_checks{true, "cpuopt_reduce_misalign_checks"}; 398 Setting<bool> cpuopt_reduce_misalign_checks{true, "cpuopt_reduce_misalign_checks"};
485 BasicSetting<bool> cpuopt_fastmem{true, "cpuopt_fastmem"}; 399 Setting<bool> cpuopt_fastmem{true, "cpuopt_fastmem"};
486 400 Setting<bool> cpuopt_fastmem_exclusives{true, "cpuopt_fastmem_exclusives"};
487 Setting<bool> cpuopt_unsafe_unfuse_fma{true, "cpuopt_unsafe_unfuse_fma"}; 401 Setting<bool> cpuopt_recompile_exclusives{true, "cpuopt_recompile_exclusives"};
488 Setting<bool> cpuopt_unsafe_reduce_fp_error{true, "cpuopt_unsafe_reduce_fp_error"}; 402
489 Setting<bool> cpuopt_unsafe_ignore_standard_fpcr{true, "cpuopt_unsafe_ignore_standard_fpcr"}; 403 SwitchableSetting<bool> cpuopt_unsafe_unfuse_fma{true, "cpuopt_unsafe_unfuse_fma"};
490 Setting<bool> cpuopt_unsafe_inaccurate_nan{true, "cpuopt_unsafe_inaccurate_nan"}; 404 SwitchableSetting<bool> cpuopt_unsafe_reduce_fp_error{true, "cpuopt_unsafe_reduce_fp_error"};
491 Setting<bool> cpuopt_unsafe_fastmem_check{true, "cpuopt_unsafe_fastmem_check"}; 405 SwitchableSetting<bool> cpuopt_unsafe_ignore_standard_fpcr{
406 true, "cpuopt_unsafe_ignore_standard_fpcr"};
407 SwitchableSetting<bool> cpuopt_unsafe_inaccurate_nan{true, "cpuopt_unsafe_inaccurate_nan"};
408 SwitchableSetting<bool> cpuopt_unsafe_fastmem_check{true, "cpuopt_unsafe_fastmem_check"};
409 SwitchableSetting<bool> cpuopt_unsafe_ignore_global_monitor{
410 true, "cpuopt_unsafe_ignore_global_monitor"};
492 411
493 // Renderer 412 // Renderer
494 RangedSetting<RendererBackend> renderer_backend{ 413 SwitchableSetting<RendererBackend, true> renderer_backend{
495 RendererBackend::OpenGL, RendererBackend::OpenGL, RendererBackend::Vulkan, "backend"}; 414 RendererBackend::Vulkan, RendererBackend::OpenGL, RendererBackend::Vulkan, "backend"};
496 BasicSetting<bool> renderer_debug{false, "debug"}; 415 Setting<bool> renderer_debug{false, "debug"};
497 BasicSetting<bool> renderer_shader_feedback{false, "shader_feedback"}; 416 Setting<bool> renderer_shader_feedback{false, "shader_feedback"};
498 BasicSetting<bool> enable_nsight_aftermath{false, "nsight_aftermath"}; 417 Setting<bool> enable_nsight_aftermath{false, "nsight_aftermath"};
499 BasicSetting<bool> disable_shader_loop_safety_checks{false, 418 Setting<bool> disable_shader_loop_safety_checks{false, "disable_shader_loop_safety_checks"};
500 "disable_shader_loop_safety_checks"}; 419 SwitchableSetting<int> vulkan_device{0, "vulkan_device"};
501 Setting<int> vulkan_device{0, "vulkan_device"};
502 420
503 ResolutionScalingInfo resolution_info{}; 421 ResolutionScalingInfo resolution_info{};
504 Setting<ResolutionSetup> resolution_setup{ResolutionSetup::Res1X, "resolution_setup"}; 422 SwitchableSetting<ResolutionSetup> resolution_setup{ResolutionSetup::Res1X, "resolution_setup"};
505 Setting<ScalingFilter> scaling_filter{ScalingFilter::Bilinear, "scaling_filter"}; 423 SwitchableSetting<ScalingFilter> scaling_filter{ScalingFilter::Bilinear, "scaling_filter"};
506 Setting<AntiAliasing> anti_aliasing{AntiAliasing::None, "anti_aliasing"}; 424 SwitchableSetting<AntiAliasing> anti_aliasing{AntiAliasing::None, "anti_aliasing"};
507 // *nix platforms may have issues with the borderless windowed fullscreen mode. 425 // *nix platforms may have issues with the borderless windowed fullscreen mode.
508 // Default to exclusive fullscreen on these platforms for now. 426 // Default to exclusive fullscreen on these platforms for now.
509 RangedSetting<FullscreenMode> fullscreen_mode{ 427 SwitchableSetting<FullscreenMode, true> fullscreen_mode{
510#ifdef _WIN32 428#ifdef _WIN32
511 FullscreenMode::Borderless, 429 FullscreenMode::Borderless,
512#else 430#else
513 FullscreenMode::Exclusive, 431 FullscreenMode::Exclusive,
514#endif 432#endif
515 FullscreenMode::Borderless, FullscreenMode::Exclusive, "fullscreen_mode"}; 433 FullscreenMode::Borderless, FullscreenMode::Exclusive, "fullscreen_mode"};
516 RangedSetting<int> aspect_ratio{0, 0, 3, "aspect_ratio"}; 434 SwitchableSetting<int, true> aspect_ratio{0, 0, 3, "aspect_ratio"};
517 RangedSetting<int> max_anisotropy{0, 0, 5, "max_anisotropy"}; 435 SwitchableSetting<int, true> max_anisotropy{0, 0, 5, "max_anisotropy"};
518 Setting<bool> use_speed_limit{true, "use_speed_limit"}; 436 SwitchableSetting<bool> use_speed_limit{true, "use_speed_limit"};
519 RangedSetting<u16> speed_limit{100, 0, 9999, "speed_limit"}; 437 SwitchableSetting<u16, true> speed_limit{100, 0, 9999, "speed_limit"};
520 Setting<bool> use_disk_shader_cache{true, "use_disk_shader_cache"}; 438 SwitchableSetting<bool> use_disk_shader_cache{true, "use_disk_shader_cache"};
521 RangedSetting<GPUAccuracy> gpu_accuracy{GPUAccuracy::High, GPUAccuracy::Normal, 439 SwitchableSetting<GPUAccuracy, true> gpu_accuracy{GPUAccuracy::High, GPUAccuracy::Normal,
522 GPUAccuracy::Extreme, "gpu_accuracy"}; 440 GPUAccuracy::Extreme, "gpu_accuracy"};
523 Setting<bool> use_asynchronous_gpu_emulation{true, "use_asynchronous_gpu_emulation"}; 441 SwitchableSetting<bool> use_asynchronous_gpu_emulation{true, "use_asynchronous_gpu_emulation"};
524 Setting<NvdecEmulation> nvdec_emulation{NvdecEmulation::GPU, "nvdec_emulation"}; 442 SwitchableSetting<NvdecEmulation> nvdec_emulation{NvdecEmulation::GPU, "nvdec_emulation"};
525 Setting<bool> accelerate_astc{true, "accelerate_astc"}; 443 SwitchableSetting<bool> accelerate_astc{true, "accelerate_astc"};
526 Setting<bool> use_vsync{true, "use_vsync"}; 444 SwitchableSetting<bool> use_vsync{true, "use_vsync"};
527 RangedSetting<u16> fps_cap{1000, 1, 1000, "fps_cap"}; 445 SwitchableSetting<ShaderBackend, true> shader_backend{ShaderBackend::GLASM, ShaderBackend::GLSL,
528 BasicSetting<bool> disable_fps_limit{false, "disable_fps_limit"}; 446 ShaderBackend::SPIRV, "shader_backend"};
529 RangedSetting<ShaderBackend> shader_backend{ShaderBackend::GLASM, ShaderBackend::GLSL, 447 SwitchableSetting<bool> use_asynchronous_shaders{false, "use_asynchronous_shaders"};
530 ShaderBackend::SPIRV, "shader_backend"}; 448 SwitchableSetting<bool> use_fast_gpu_time{true, "use_fast_gpu_time"};
531 Setting<bool> use_asynchronous_shaders{false, "use_asynchronous_shaders"}; 449 SwitchableSetting<bool> use_pessimistic_flushes{false, "use_pessimistic_flushes"};
532 Setting<bool> use_fast_gpu_time{true, "use_fast_gpu_time"}; 450
533 451 SwitchableSetting<u8> bg_red{0, "bg_red"};
534 Setting<u8> bg_red{0, "bg_red"}; 452 SwitchableSetting<u8> bg_green{0, "bg_green"};
535 Setting<u8> bg_green{0, "bg_green"}; 453 SwitchableSetting<u8> bg_blue{0, "bg_blue"};
536 Setting<u8> bg_blue{0, "bg_blue"};
537 454
538 // System 455 // System
539 Setting<std::optional<u32>> rng_seed{std::optional<u32>(), "rng_seed"}; 456 SwitchableSetting<std::optional<u32>> rng_seed{std::optional<u32>(), "rng_seed"};
540 // Measured in seconds since epoch 457 // Measured in seconds since epoch
541 std::optional<s64> custom_rtc; 458 std::optional<s64> custom_rtc;
542 // Set on game boot, reset on stop. Seconds difference between current time and `custom_rtc` 459 // Set on game boot, reset on stop. Seconds difference between current time and `custom_rtc`
543 s64 custom_rtc_differential; 460 s64 custom_rtc_differential;
544 461
545 BasicSetting<s32> current_user{0, "current_user"}; 462 Setting<s32> current_user{0, "current_user"};
546 RangedSetting<s32> language_index{1, 0, 17, "language_index"}; 463 SwitchableSetting<s32, true> language_index{1, 0, 17, "language_index"};
547 RangedSetting<s32> region_index{1, 0, 6, "region_index"}; 464 SwitchableSetting<s32, true> region_index{1, 0, 6, "region_index"};
548 RangedSetting<s32> time_zone_index{0, 0, 45, "time_zone_index"}; 465 SwitchableSetting<s32, true> time_zone_index{0, 0, 45, "time_zone_index"};
549 RangedSetting<s32> sound_index{1, 0, 2, "sound_index"}; 466 SwitchableSetting<s32, true> sound_index{1, 0, 2, "sound_index"};
550 467
551 // Controls 468 // Controls
552 InputSetting<std::array<PlayerInput, 10>> players; 469 InputSetting<std::array<PlayerInput, 10>> players;
553 470
554 Setting<bool> use_docked_mode{true, "use_docked_mode"}; 471 SwitchableSetting<bool> use_docked_mode{true, "use_docked_mode"};
555 472
556 BasicSetting<bool> enable_raw_input{false, "enable_raw_input"}; 473 Setting<bool> enable_raw_input{false, "enable_raw_input"};
557 BasicSetting<bool> controller_navigation{true, "controller_navigation"}; 474 Setting<bool> controller_navigation{true, "controller_navigation"};
558 475
559 Setting<bool> vibration_enabled{true, "vibration_enabled"}; 476 SwitchableSetting<bool> vibration_enabled{true, "vibration_enabled"};
560 Setting<bool> enable_accurate_vibrations{false, "enable_accurate_vibrations"}; 477 SwitchableSetting<bool> enable_accurate_vibrations{false, "enable_accurate_vibrations"};
561 478
562 Setting<bool> motion_enabled{true, "motion_enabled"}; 479 SwitchableSetting<bool> motion_enabled{true, "motion_enabled"};
563 BasicSetting<std::string> udp_input_servers{"127.0.0.1:26760", "udp_input_servers"}; 480 Setting<std::string> udp_input_servers{"127.0.0.1:26760", "udp_input_servers"};
564 BasicSetting<bool> enable_udp_controller{false, "enable_udp_controller"}; 481 Setting<bool> enable_udp_controller{false, "enable_udp_controller"};
565 482
566 BasicSetting<bool> pause_tas_on_load{true, "pause_tas_on_load"}; 483 Setting<bool> pause_tas_on_load{true, "pause_tas_on_load"};
567 BasicSetting<bool> tas_enable{false, "tas_enable"}; 484 Setting<bool> tas_enable{false, "tas_enable"};
568 BasicSetting<bool> tas_loop{false, "tas_loop"}; 485 Setting<bool> tas_loop{false, "tas_loop"};
569 486
570 BasicSetting<bool> mouse_panning{false, "mouse_panning"}; 487 Setting<bool> mouse_panning{false, "mouse_panning"};
571 BasicRangedSetting<u8> mouse_panning_sensitivity{10, 1, 100, "mouse_panning_sensitivity"}; 488 Setting<u8, true> mouse_panning_sensitivity{10, 1, 100, "mouse_panning_sensitivity"};
572 BasicSetting<bool> mouse_enabled{false, "mouse_enabled"}; 489 Setting<bool> mouse_enabled{false, "mouse_enabled"};
573 490
574 BasicSetting<bool> emulate_analog_keyboard{false, "emulate_analog_keyboard"}; 491 Setting<bool> emulate_analog_keyboard{false, "emulate_analog_keyboard"};
575 BasicSetting<bool> keyboard_enabled{false, "keyboard_enabled"}; 492 Setting<bool> keyboard_enabled{false, "keyboard_enabled"};
576 493
577 BasicSetting<bool> debug_pad_enabled{false, "debug_pad_enabled"}; 494 Setting<bool> debug_pad_enabled{false, "debug_pad_enabled"};
578 ButtonsRaw debug_pad_buttons; 495 ButtonsRaw debug_pad_buttons;
579 AnalogsRaw debug_pad_analogs; 496 AnalogsRaw debug_pad_analogs;
580 497
581 TouchscreenInput touchscreen; 498 TouchscreenInput touchscreen;
582 499
583 BasicSetting<std::string> touch_device{"min_x:100,min_y:50,max_x:1800,max_y:850", 500 Setting<std::string> touch_device{"min_x:100,min_y:50,max_x:1800,max_y:850", "touch_device"};
584 "touch_device"}; 501 Setting<int> touch_from_button_map_index{0, "touch_from_button_map"};
585 BasicSetting<int> touch_from_button_map_index{0, "touch_from_button_map"};
586 std::vector<TouchFromButtonMap> touch_from_button_maps; 502 std::vector<TouchFromButtonMap> touch_from_button_maps;
587 503
504 Setting<bool> enable_ring_controller{true, "enable_ring_controller"};
505 RingconRaw ringcon_analogs;
506
507 Setting<bool> enable_ir_sensor{false, "enable_ir_sensor"};
508 Setting<std::string> ir_sensor_device{"auto", "ir_sensor_device"};
509
588 // Data Storage 510 // Data Storage
589 BasicSetting<bool> use_virtual_sd{true, "use_virtual_sd"}; 511 Setting<bool> use_virtual_sd{true, "use_virtual_sd"};
590 BasicSetting<bool> gamecard_inserted{false, "gamecard_inserted"}; 512 Setting<bool> gamecard_inserted{false, "gamecard_inserted"};
591 BasicSetting<bool> gamecard_current_game{false, "gamecard_current_game"}; 513 Setting<bool> gamecard_current_game{false, "gamecard_current_game"};
592 BasicSetting<std::string> gamecard_path{std::string(), "gamecard_path"}; 514 Setting<std::string> gamecard_path{std::string(), "gamecard_path"};
593 515
594 // Debugging 516 // Debugging
595 bool record_frame_times; 517 bool record_frame_times;
596 BasicSetting<bool> use_gdbstub{false, "use_gdbstub"}; 518 Setting<bool> use_gdbstub{false, "use_gdbstub"};
597 BasicSetting<u16> gdbstub_port{0, "gdbstub_port"}; 519 Setting<u16> gdbstub_port{6543, "gdbstub_port"};
598 BasicSetting<std::string> program_args{std::string(), "program_args"}; 520 Setting<std::string> program_args{std::string(), "program_args"};
599 BasicSetting<bool> dump_exefs{false, "dump_exefs"}; 521 Setting<bool> dump_exefs{false, "dump_exefs"};
600 BasicSetting<bool> dump_nso{false, "dump_nso"}; 522 Setting<bool> dump_nso{false, "dump_nso"};
601 BasicSetting<bool> dump_shaders{false, "dump_shaders"}; 523 Setting<bool> dump_shaders{false, "dump_shaders"};
602 BasicSetting<bool> enable_fs_access_log{false, "enable_fs_access_log"}; 524 Setting<bool> dump_macros{false, "dump_macros"};
603 BasicSetting<bool> reporting_services{false, "reporting_services"}; 525 Setting<bool> enable_fs_access_log{false, "enable_fs_access_log"};
604 BasicSetting<bool> quest_flag{false, "quest_flag"}; 526 Setting<bool> reporting_services{false, "reporting_services"};
605 BasicSetting<bool> disable_macro_jit{false, "disable_macro_jit"}; 527 Setting<bool> quest_flag{false, "quest_flag"};
606 BasicSetting<bool> extended_logging{false, "extended_logging"}; 528 Setting<bool> disable_macro_jit{false, "disable_macro_jit"};
607 BasicSetting<bool> use_debug_asserts{false, "use_debug_asserts"}; 529 Setting<bool> extended_logging{false, "extended_logging"};
608 BasicSetting<bool> use_auto_stub{false, "use_auto_stub"}; 530 Setting<bool> use_debug_asserts{false, "use_debug_asserts"};
609 BasicSetting<bool> enable_all_controllers{false, "enable_all_controllers"}; 531 Setting<bool> use_auto_stub{false, "use_auto_stub"};
532 Setting<bool> enable_all_controllers{false, "enable_all_controllers"};
533 Setting<bool> create_crash_dumps{false, "create_crash_dumps"};
534 Setting<bool> perform_vulkan_check{true, "perform_vulkan_check"};
610 535
611 // Miscellaneous 536 // Miscellaneous
612 BasicSetting<std::string> log_filter{"*:Info", "log_filter"}; 537 Setting<std::string> log_filter{"*:Info", "log_filter"};
613 BasicSetting<bool> use_dev_keys{false, "use_dev_keys"}; 538 Setting<bool> use_dev_keys{false, "use_dev_keys"};
614 539
615 // Network 540 // Network
616 BasicSetting<std::string> network_interface{std::string(), "network_interface"}; 541 Setting<std::string> network_interface{std::string(), "network_interface"};
617 542
618 // WebService 543 // WebService
619 BasicSetting<bool> enable_telemetry{true, "enable_telemetry"}; 544 Setting<bool> enable_telemetry{true, "enable_telemetry"};
620 BasicSetting<std::string> web_api_url{"https://api.yuzu-emu.org", "web_api_url"}; 545 Setting<std::string> web_api_url{"https://api.yuzu-emu.org", "web_api_url"};
621 BasicSetting<std::string> yuzu_username{std::string(), "yuzu_username"}; 546 Setting<std::string> yuzu_username{std::string(), "yuzu_username"};
622 BasicSetting<std::string> yuzu_token{std::string(), "yuzu_token"}; 547 Setting<std::string> yuzu_token{std::string(), "yuzu_token"};
623 548
624 // Add-Ons 549 // Add-Ons
625 std::map<u64, std::vector<std::string>> disabled_addons; 550 std::map<u64, std::vector<std::string>> disabled_addons;
diff --git a/src/common/settings_input.cpp b/src/common/settings_input.cpp
index bea2b837b..0a6eea3cf 100644
--- a/src/common/settings_input.cpp
+++ b/src/common/settings_input.cpp
@@ -1,6 +1,5 @@
1// Copyright 2020 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#include "common/settings_input.h" 4#include "common/settings_input.h"
6 5
diff --git a/src/common/settings_input.h b/src/common/settings_input.h
index 4ff37e186..485e4ad22 100644
--- a/src/common/settings_input.h
+++ b/src/common/settings_input.h
@@ -1,6 +1,5 @@
1// Copyright 2020 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#pragma once 4#pragma once
6 5
@@ -357,6 +356,7 @@ constexpr int NUM_KEYBOARD_MODS_HID = NumKeyboardMods;
357using AnalogsRaw = std::array<std::string, NativeAnalog::NumAnalogs>; 356using AnalogsRaw = std::array<std::string, NativeAnalog::NumAnalogs>;
358using ButtonsRaw = std::array<std::string, NativeButton::NumButtons>; 357using ButtonsRaw = std::array<std::string, NativeButton::NumButtons>;
359using MotionsRaw = std::array<std::string, NativeMotion::NumMotions>; 358using MotionsRaw = std::array<std::string, NativeMotion::NumMotions>;
359using RingconRaw = std::string;
360 360
361constexpr u32 JOYCON_BODY_NEON_RED = 0xFF3C28; 361constexpr u32 JOYCON_BODY_NEON_RED = 0xFF3C28;
362constexpr u32 JOYCON_BUTTONS_NEON_RED = 0x1E0A0A; 362constexpr u32 JOYCON_BUTTONS_NEON_RED = 0x1E0A0A;
diff --git a/src/common/socket_types.h b/src/common/socket_types.h
new file mode 100644
index 000000000..0a801a443
--- /dev/null
+++ b/src/common/socket_types.h
@@ -0,0 +1,51 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "common/common_types.h"
7
8namespace Network {
9
10/// Address families
11enum class Domain : u8 {
12 INET, ///< Address family for IPv4
13};
14
15/// Socket types
16enum class Type {
17 STREAM,
18 DGRAM,
19 RAW,
20 SEQPACKET,
21};
22
23/// Protocol values for sockets
24enum class Protocol : u8 {
25 ICMP,
26 TCP,
27 UDP,
28};
29
30/// Shutdown mode
31enum class ShutdownHow {
32 RD,
33 WR,
34 RDWR,
35};
36
37/// Array of IPv4 address
38using IPv4Address = std::array<u8, 4>;
39
40/// Cross-platform sockaddr structure
41struct SockAddrIn {
42 Domain family;
43 IPv4Address ip;
44 u16 portno;
45};
46
47constexpr u32 FLAG_MSG_PEEK = 0x2;
48constexpr u32 FLAG_MSG_DONTWAIT = 0x80;
49constexpr u32 FLAG_O_NONBLOCK = 0x800;
50
51} // namespace Network
diff --git a/src/common/spin_lock.cpp b/src/common/spin_lock.cpp
index c1524220f..b2ef4ea1d 100644
--- a/src/common/spin_lock.cpp
+++ b/src/common/spin_lock.cpp
@@ -1,6 +1,5 @@
1// Copyright 2020 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#include "common/spin_lock.h" 4#include "common/spin_lock.h"
6 5
diff --git a/src/common/spin_lock.h b/src/common/spin_lock.h
index 06ac2f5bb..a83274851 100644
--- a/src/common/spin_lock.h
+++ b/src/common/spin_lock.h
@@ -1,6 +1,5 @@
1// Copyright 2020 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#pragma once 4#pragma once
6 5
diff --git a/src/common/stream.cpp b/src/common/stream.cpp
index bf0496c26..80ddd68c8 100644
--- a/src/common/stream.cpp
+++ b/src/common/stream.cpp
@@ -1,6 +1,5 @@
1// Copyright 2020 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#include <stdexcept> 4#include <stdexcept>
6#include "common/common_types.h" 5#include "common/common_types.h"
diff --git a/src/common/stream.h b/src/common/stream.h
index 0e40692de..5bb26e883 100644
--- a/src/common/stream.h
+++ b/src/common/stream.h
@@ -1,6 +1,5 @@
1// Copyright 2020 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#pragma once 4#pragma once
6 5
diff --git a/src/common/string_util.cpp b/src/common/string_util.cpp
index 662171138..7a495bc79 100644
--- a/src/common/string_util.cpp
+++ b/src/common/string_util.cpp
@@ -1,15 +1,13 @@
1// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project 1// SPDX-FileCopyrightText: 2013 Dolphin Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-FileCopyrightText: 2014 Citra Emulator Project
3// Refer to the license.txt file included. 3// SPDX-License-Identifier: GPL-2.0-or-later
4 4
5#include <algorithm> 5#include <algorithm>
6#include <cctype> 6#include <cctype>
7#include <codecvt> 7#include <codecvt>
8#include <cstdlib>
9#include <locale> 8#include <locale>
10#include <sstream> 9#include <sstream>
11 10
12#include "common/logging/log.h"
13#include "common/string_util.h" 11#include "common/string_util.h"
14 12
15#ifdef _WIN32 13#ifdef _WIN32
@@ -180,6 +178,10 @@ std::wstring UTF8ToUTF16W(const std::string& input) {
180 178
181#endif 179#endif
182 180
181std::u16string U16StringFromBuffer(const u16* input, std::size_t length) {
182 return std::u16string(reinterpret_cast<const char16_t*>(input), length);
183}
184
183std::string StringFromFixedZeroTerminatedBuffer(std::string_view buffer, std::size_t max_len) { 185std::string StringFromFixedZeroTerminatedBuffer(std::string_view buffer, std::size_t max_len) {
184 std::size_t len = 0; 186 std::size_t len = 0;
185 while (len < buffer.length() && len < max_len && buffer[len] != '\0') { 187 while (len < buffer.length() && len < max_len && buffer[len] != '\0') {
diff --git a/src/common/string_util.h b/src/common/string_util.h
index f0dd632ee..ce18a33cf 100644
--- a/src/common/string_util.h
+++ b/src/common/string_util.h
@@ -1,6 +1,6 @@
1// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project 1// SPDX-FileCopyrightText: 2013 Dolphin Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-FileCopyrightText: 2014 Citra Emulator Project
3// Refer to the license.txt file included. 3// SPDX-License-Identifier: GPL-2.0-or-later
4 4
5#pragma once 5#pragma once
6 6
@@ -44,6 +44,8 @@ bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _
44 44
45#endif 45#endif
46 46
47[[nodiscard]] std::u16string U16StringFromBuffer(const u16* input, std::size_t length);
48
47/** 49/**
48 * Compares the string defined by the range [`begin`, `end`) to the null-terminated C-string 50 * Compares the string defined by the range [`begin`, `end`) to the null-terminated C-string
49 * `other` for equality. 51 * `other` for equality.
diff --git a/src/common/swap.h b/src/common/swap.h
index a80e191dc..037b82781 100644
--- a/src/common/swap.h
+++ b/src/common/swap.h
@@ -1,16 +1,6 @@
1// Copyright (c) 2012- PPSSPP Project / Dolphin Project. 1// SPDX-FileCopyrightText: 2012 PPSSPP Project
2 2// SPDX-FileCopyrightText: 2012 Dolphin Emulator Project
3// This program is free software: you can redistribute it and/or modify 3// SPDX-License-Identifier: GPL-2.0-or-later
4// it under the terms of the GNU General Public License as published by
5// the Free Software Foundation, version 2.0 or later versions.
6
7// This program is distributed in the hope that it will be useful,
8// but WITHOUT ANY WARRANTY; without even the implied warranty of
9// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10// GNU General Public License 2.0 for more details.
11
12// A copy of the GPL 2.0 should have been included with the program.
13// If not, see http://www.gnu.org/licenses/
14 4
15// Official git repository and contact information can be found at 5// Official git repository and contact information can be found at
16// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. 6// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
diff --git a/src/common/telemetry.cpp b/src/common/telemetry.cpp
index 6241d08b3..d26394359 100644
--- a/src/common/telemetry.cpp
+++ b/src/common/telemetry.cpp
@@ -1,10 +1,8 @@
1// Copyright 2017 Citra Emulator Project 1// SPDX-FileCopyrightText: 2017 Citra Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#include <algorithm> 4#include <algorithm>
6#include <cstring> 5#include <cstring>
7#include "common/assert.h"
8#include "common/scm_rev.h" 6#include "common/scm_rev.h"
9#include "common/telemetry.h" 7#include "common/telemetry.h"
10 8
@@ -55,22 +53,50 @@ void AppendBuildInfo(FieldCollection& fc) {
55 53
56void AppendCPUInfo(FieldCollection& fc) { 54void AppendCPUInfo(FieldCollection& fc) {
57#ifdef ARCHITECTURE_x86_64 55#ifdef ARCHITECTURE_x86_64
58 fc.AddField(FieldType::UserSystem, "CPU_Model", Common::GetCPUCaps().cpu_string); 56
59 fc.AddField(FieldType::UserSystem, "CPU_BrandString", Common::GetCPUCaps().brand_string); 57 const auto& caps = Common::GetCPUCaps();
60 fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_AES", Common::GetCPUCaps().aes); 58 const auto add_field = [&fc](std::string_view field_name, const auto& field_value) {
61 fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_AVX", Common::GetCPUCaps().avx); 59 fc.AddField(FieldType::UserSystem, field_name, field_value);
62 fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_AVX2", Common::GetCPUCaps().avx2); 60 };
63 fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_AVX512", Common::GetCPUCaps().avx512); 61 add_field("CPU_Model", caps.cpu_string);
64 fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_BMI1", Common::GetCPUCaps().bmi1); 62 add_field("CPU_BrandString", caps.brand_string);
65 fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_BMI2", Common::GetCPUCaps().bmi2); 63
66 fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_FMA", Common::GetCPUCaps().fma); 64 add_field("CPU_Extension_x64_SSE", caps.sse);
67 fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_FMA4", Common::GetCPUCaps().fma4); 65 add_field("CPU_Extension_x64_SSE2", caps.sse2);
68 fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_SSE", Common::GetCPUCaps().sse); 66 add_field("CPU_Extension_x64_SSE3", caps.sse3);
69 fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_SSE2", Common::GetCPUCaps().sse2); 67 add_field("CPU_Extension_x64_SSSE3", caps.ssse3);
70 fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_SSE3", Common::GetCPUCaps().sse3); 68 add_field("CPU_Extension_x64_SSE41", caps.sse4_1);
71 fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_SSSE3", Common::GetCPUCaps().ssse3); 69 add_field("CPU_Extension_x64_SSE42", caps.sse4_2);
72 fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_SSE41", Common::GetCPUCaps().sse4_1); 70
73 fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_SSE42", Common::GetCPUCaps().sse4_2); 71 add_field("CPU_Extension_x64_AVX", caps.avx);
72 add_field("CPU_Extension_x64_AVX_VNNI", caps.avx_vnni);
73 add_field("CPU_Extension_x64_AVX2", caps.avx2);
74
75 // Skylake-X/SP level AVX512, for compatibility with the previous telemetry field
76 add_field("CPU_Extension_x64_AVX512",
77 caps.avx512f && caps.avx512cd && caps.avx512vl && caps.avx512dq && caps.avx512bw);
78
79 add_field("CPU_Extension_x64_AVX512F", caps.avx512f);
80 add_field("CPU_Extension_x64_AVX512CD", caps.avx512cd);
81 add_field("CPU_Extension_x64_AVX512VL", caps.avx512vl);
82 add_field("CPU_Extension_x64_AVX512DQ", caps.avx512dq);
83 add_field("CPU_Extension_x64_AVX512BW", caps.avx512bw);
84 add_field("CPU_Extension_x64_AVX512BITALG", caps.avx512bitalg);
85 add_field("CPU_Extension_x64_AVX512VBMI", caps.avx512vbmi);
86
87 add_field("CPU_Extension_x64_AES", caps.aes);
88 add_field("CPU_Extension_x64_BMI1", caps.bmi1);
89 add_field("CPU_Extension_x64_BMI2", caps.bmi2);
90 add_field("CPU_Extension_x64_F16C", caps.f16c);
91 add_field("CPU_Extension_x64_FMA", caps.fma);
92 add_field("CPU_Extension_x64_FMA4", caps.fma4);
93 add_field("CPU_Extension_x64_GFNI", caps.gfni);
94 add_field("CPU_Extension_x64_INVARIANT_TSC", caps.invariant_tsc);
95 add_field("CPU_Extension_x64_LZCNT", caps.lzcnt);
96 add_field("CPU_Extension_x64_MOVBE", caps.movbe);
97 add_field("CPU_Extension_x64_PCLMULQDQ", caps.pclmulqdq);
98 add_field("CPU_Extension_x64_POPCNT", caps.popcnt);
99 add_field("CPU_Extension_x64_SHA", caps.sha);
74#else 100#else
75 fc.AddField(FieldType::UserSystem, "CPU_Model", "Other"); 101 fc.AddField(FieldType::UserSystem, "CPU_Model", "Other");
76#endif 102#endif
diff --git a/src/common/telemetry.h b/src/common/telemetry.h
index d38aeac99..ba633d5a5 100644
--- a/src/common/telemetry.h
+++ b/src/common/telemetry.h
@@ -1,6 +1,5 @@
1// Copyright 2017 Citra Emulator Project 1// SPDX-FileCopyrightText: 2017 Citra Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#pragma once 4#pragma once
6 5
@@ -55,8 +54,8 @@ class Field : public FieldInterface {
55public: 54public:
56 YUZU_NON_COPYABLE(Field); 55 YUZU_NON_COPYABLE(Field);
57 56
58 Field(FieldType type_, std::string name_, T value_) 57 Field(FieldType type_, std::string_view name_, T value_)
59 : name(std::move(name_)), type(type_), value(std::move(value_)) {} 58 : name(name_), type(type_), value(std::move(value_)) {}
60 59
61 ~Field() override = default; 60 ~Field() override = default;
62 61
@@ -123,7 +122,7 @@ public:
123 * @param value Value for the field to add. 122 * @param value Value for the field to add.
124 */ 123 */
125 template <typename T> 124 template <typename T>
126 void AddField(FieldType type, const char* name, T value) { 125 void AddField(FieldType type, std::string_view name, T value) {
127 return AddField(std::make_unique<Field<T>>(type, name, std::move(value))); 126 return AddField(std::make_unique<Field<T>>(type, name, std::move(value)));
128 } 127 }
129 128
@@ -171,6 +170,9 @@ struct VisitorInterface {
171struct NullVisitor final : public VisitorInterface { 170struct NullVisitor final : public VisitorInterface {
172 YUZU_NON_COPYABLE(NullVisitor); 171 YUZU_NON_COPYABLE(NullVisitor);
173 172
173 NullVisitor() = default;
174 ~NullVisitor() override = default;
175
174 void Visit(const Field<bool>& /*field*/) override {} 176 void Visit(const Field<bool>& /*field*/) override {}
175 void Visit(const Field<double>& /*field*/) override {} 177 void Visit(const Field<double>& /*field*/) override {}
176 void Visit(const Field<float>& /*field*/) override {} 178 void Visit(const Field<float>& /*field*/) override {}
diff --git a/src/common/thread.cpp b/src/common/thread.cpp
index 946a1114d..919e33af9 100644
--- a/src/common/thread.cpp
+++ b/src/common/thread.cpp
@@ -1,6 +1,6 @@
1// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project 1// SPDX-FileCopyrightText: 2013 Dolphin Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-FileCopyrightText: 2014 Citra Emulator Project
3// Refer to the license.txt file included. 3// SPDX-License-Identifier: GPL-2.0-or-later
4 4
5#include <string> 5#include <string>
6 6
@@ -47,6 +47,9 @@ void SetCurrentThreadPriority(ThreadPriority new_priority) {
47 case ThreadPriority::VeryHigh: 47 case ThreadPriority::VeryHigh:
48 windows_priority = THREAD_PRIORITY_HIGHEST; 48 windows_priority = THREAD_PRIORITY_HIGHEST;
49 break; 49 break;
50 case ThreadPriority::Critical:
51 windows_priority = THREAD_PRIORITY_TIME_CRITICAL;
52 break;
50 default: 53 default:
51 windows_priority = THREAD_PRIORITY_NORMAL; 54 windows_priority = THREAD_PRIORITY_NORMAL;
52 break; 55 break;
@@ -59,9 +62,10 @@ void SetCurrentThreadPriority(ThreadPriority new_priority) {
59void SetCurrentThreadPriority(ThreadPriority new_priority) { 62void SetCurrentThreadPriority(ThreadPriority new_priority) {
60 pthread_t this_thread = pthread_self(); 63 pthread_t this_thread = pthread_self();
61 64
62 s32 max_prio = sched_get_priority_max(SCHED_OTHER); 65 const auto scheduling_type = SCHED_OTHER;
63 s32 min_prio = sched_get_priority_min(SCHED_OTHER); 66 s32 max_prio = sched_get_priority_max(scheduling_type);
64 u32 level = static_cast<u32>(new_priority) + 1; 67 s32 min_prio = sched_get_priority_min(scheduling_type);
68 u32 level = std::max(static_cast<u32>(new_priority) + 1, 4U);
65 69
66 struct sched_param params; 70 struct sched_param params;
67 if (max_prio > min_prio) { 71 if (max_prio > min_prio) {
@@ -70,7 +74,7 @@ void SetCurrentThreadPriority(ThreadPriority new_priority) {
70 params.sched_priority = min_prio - ((min_prio - max_prio) * level) / 4; 74 params.sched_priority = min_prio - ((min_prio - max_prio) * level) / 4;
71 } 75 }
72 76
73 pthread_setschedparam(this_thread, SCHED_OTHER, &params); 77 pthread_setschedparam(this_thread, scheduling_type, &params);
74} 78}
75 79
76#endif 80#endif
diff --git a/src/common/thread.h b/src/common/thread.h
index a8c17c71a..e17a7850f 100644
--- a/src/common/thread.h
+++ b/src/common/thread.h
@@ -1,6 +1,6 @@
1// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project 1// SPDX-FileCopyrightText: 2013 Dolphin Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-FileCopyrightText: 2014 Citra Emulator Project
3// Refer to the license.txt file included. 3// SPDX-License-Identifier: GPL-2.0-or-later
4 4
5#pragma once 5#pragma once
6 6
@@ -17,7 +17,7 @@ namespace Common {
17class Event { 17class Event {
18public: 18public:
19 void Set() { 19 void Set() {
20 std::lock_guard lk{mutex}; 20 std::scoped_lock lk{mutex};
21 if (!is_set) { 21 if (!is_set) {
22 is_set = true; 22 is_set = true;
23 condvar.notify_one(); 23 condvar.notify_one();
@@ -54,6 +54,10 @@ public:
54 is_set = false; 54 is_set = false;
55 } 55 }
56 56
57 [[nodiscard]] bool IsSet() {
58 return is_set;
59 }
60
57private: 61private:
58 std::condition_variable condvar; 62 std::condition_variable condvar;
59 std::mutex mutex; 63 std::mutex mutex;
@@ -92,6 +96,7 @@ enum class ThreadPriority : u32 {
92 Normal = 1, 96 Normal = 1,
93 High = 2, 97 High = 2,
94 VeryHigh = 3, 98 VeryHigh = 3,
99 Critical = 4,
95}; 100};
96 101
97void SetCurrentThreadPriority(ThreadPriority new_priority); 102void SetCurrentThreadPriority(ThreadPriority new_priority);
diff --git a/src/common/thread_queue_list.h b/src/common/thread_queue_list.h
index def9e5d8d..ce48cec92 100644
--- a/src/common/thread_queue_list.h
+++ b/src/common/thread_queue_list.h
@@ -1,6 +1,6 @@
1// Copyright 2014 Citra Emulator Project / PPSSPP Project 1// SPDX-FileCopyrightText: 2012 PPSSPP Project
2// Licensed under GPLv2 or any later version 2// SPDX-FileCopyrightText: 2014 Dolphin Emulator Project
3// Refer to the license.txt file included. 3// SPDX-License-Identifier: GPL-2.0-or-later
4 4
5#pragma once 5#pragma once
6 6
diff --git a/src/common/thread_worker.h b/src/common/thread_worker.h
index cd0017726..62c60f724 100644
--- a/src/common/thread_worker.h
+++ b/src/common/thread_worker.h
@@ -1,6 +1,5 @@
1// Copyright 2020 yuzu emulator team 1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#pragma once 4#pragma once
6 5
diff --git a/src/common/threadsafe_queue.h b/src/common/threadsafe_queue.h
index 2c8c2b90e..053798e79 100644
--- a/src/common/threadsafe_queue.h
+++ b/src/common/threadsafe_queue.h
@@ -1,6 +1,5 @@
1// Copyright 2010 Dolphin Emulator Project 1// SPDX-FileCopyrightText: 2010 Dolphin Emulator Project
2// Licensed under GPLv2+ 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#pragma once 4#pragma once
6 5
@@ -40,7 +39,7 @@ public:
40 template <typename Arg> 39 template <typename Arg>
41 void Push(Arg&& t) { 40 void Push(Arg&& t) {
42 // create the element, add it to the queue 41 // create the element, add it to the queue
43 write_ptr->current = std::forward<Arg>(t); 42 write_ptr->current = std::move(t);
44 // set the next pointer to a new element ptr 43 // set the next pointer to a new element ptr
45 // then advance the write pointer 44 // then advance the write pointer
46 ElementPtr* new_ptr = new ElementPtr(); 45 ElementPtr* new_ptr = new ElementPtr();
@@ -52,7 +51,7 @@ public:
52 // line before cv.wait 51 // line before cv.wait
53 // TODO(bunnei): This can be replaced with C++20 waitable atomics when properly supported. 52 // TODO(bunnei): This can be replaced with C++20 waitable atomics when properly supported.
54 // See discussion on https://github.com/yuzu-emu/yuzu/pull/3173 for details. 53 // See discussion on https://github.com/yuzu-emu/yuzu/pull/3173 for details.
55 std::lock_guard lock{cv_mutex}; 54 std::scoped_lock lock{cv_mutex};
56 cv.notify_one(); 55 cv.notify_one();
57 } 56 }
58 57
@@ -159,7 +158,7 @@ public:
159 158
160 template <typename Arg> 159 template <typename Arg>
161 void Push(Arg&& t) { 160 void Push(Arg&& t) {
162 std::lock_guard lock{write_lock}; 161 std::scoped_lock lock{write_lock};
163 spsc_queue.Push(t); 162 spsc_queue.Push(t);
164 } 163 }
165 164
diff --git a/src/common/time_zone.cpp b/src/common/time_zone.cpp
index ce239eb63..126836b01 100644
--- a/src/common/time_zone.cpp
+++ b/src/common/time_zone.cpp
@@ -1,6 +1,5 @@
1// Copyright 2020 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#include <chrono> 4#include <chrono>
6#include <iomanip> 5#include <iomanip>
diff --git a/src/common/time_zone.h b/src/common/time_zone.h
index 9f5939ca5..99cae6ef2 100644
--- a/src/common/time_zone.h
+++ b/src/common/time_zone.h
@@ -1,6 +1,5 @@
1// Copyright 2020 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#pragma once 4#pragma once
6 5
diff --git a/src/common/tiny_mt.h b/src/common/tiny_mt.h
index 19ae5b7d6..5d5ebf158 100644
--- a/src/common/tiny_mt.h
+++ b/src/common/tiny_mt.h
@@ -1,6 +1,5 @@
1// Copyright 2021 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#pragma once 4#pragma once
6 5
diff --git a/src/common/tree.h b/src/common/tree.h
index 18faa4a48..f77859209 100644
--- a/src/common/tree.h
+++ b/src/common/tree.h
@@ -1,32 +1,10 @@
1// SPDX-FileCopyrightText: 2002 Niels Provos <provos@citi.umich.edu>
2// SPDX-License-Identifier: BSD-2-Clause
3
1/* $NetBSD: tree.h,v 1.8 2004/03/28 19:38:30 provos Exp $ */ 4/* $NetBSD: tree.h,v 1.8 2004/03/28 19:38:30 provos Exp $ */
2/* $OpenBSD: tree.h,v 1.7 2002/10/17 21:51:54 art Exp $ */ 5/* $OpenBSD: tree.h,v 1.7 2002/10/17 21:51:54 art Exp $ */
3/* $FreeBSD$ */ 6/* $FreeBSD$ */
4 7
5/*-
6 * Copyright 2002 Niels Provos <provos@citi.umich.edu>
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30#pragma once 8#pragma once
31 9
32/* 10/*
@@ -43,294 +21,265 @@
43 * The maximum height of a red-black tree is 2lg (n+1). 21 * The maximum height of a red-black tree is 2lg (n+1).
44 */ 22 */
45 23
46#include "common/assert.h" 24namespace Common::freebsd {
25
26enum class RBColor {
27 RB_BLACK = 0,
28 RB_RED = 1,
29};
47 30
48namespace Common { 31#pragma pack(push, 4)
49template <typename T> 32template <typename T>
50class RBHead { 33class RBEntry {
51public: 34public:
52 [[nodiscard]] T* Root() { 35 constexpr RBEntry() = default;
53 return rbh_root;
54 }
55 36
56 [[nodiscard]] const T* Root() const { 37 [[nodiscard]] constexpr T* Left() {
57 return rbh_root; 38 return m_rbe_left;
58 } 39 }
59 40 [[nodiscard]] constexpr const T* Left() const {
60 void SetRoot(T* root) { 41 return m_rbe_left;
61 rbh_root = root;
62 } 42 }
63 43
64 [[nodiscard]] bool IsEmpty() const { 44 constexpr void SetLeft(T* e) {
65 return Root() == nullptr; 45 m_rbe_left = e;
66 } 46 }
67 47
68private: 48 [[nodiscard]] constexpr T* Right() {
69 T* rbh_root = nullptr; 49 return m_rbe_right;
70};
71
72enum class EntryColor {
73 Black,
74 Red,
75};
76
77template <typename T>
78class RBEntry {
79public:
80 [[nodiscard]] T* Left() {
81 return rbe_left;
82 } 50 }
83 51 [[nodiscard]] constexpr const T* Right() const {
84 [[nodiscard]] const T* Left() const { 52 return m_rbe_right;
85 return rbe_left;
86 } 53 }
87 54
88 void SetLeft(T* left) { 55 constexpr void SetRight(T* e) {
89 rbe_left = left; 56 m_rbe_right = e;
90 } 57 }
91 58
92 [[nodiscard]] T* Right() { 59 [[nodiscard]] constexpr T* Parent() {
93 return rbe_right; 60 return m_rbe_parent;
94 } 61 }
95 62 [[nodiscard]] constexpr const T* Parent() const {
96 [[nodiscard]] const T* Right() const { 63 return m_rbe_parent;
97 return rbe_right;
98 } 64 }
99 65
100 void SetRight(T* right) { 66 constexpr void SetParent(T* e) {
101 rbe_right = right; 67 m_rbe_parent = e;
102 } 68 }
103 69
104 [[nodiscard]] T* Parent() { 70 [[nodiscard]] constexpr bool IsBlack() const {
105 return rbe_parent; 71 return m_rbe_color == RBColor::RB_BLACK;
106 } 72 }
107 73 [[nodiscard]] constexpr bool IsRed() const {
108 [[nodiscard]] const T* Parent() const { 74 return m_rbe_color == RBColor::RB_RED;
109 return rbe_parent;
110 } 75 }
111 76 [[nodiscard]] constexpr RBColor Color() const {
112 void SetParent(T* parent) { 77 return m_rbe_color;
113 rbe_parent = parent;
114 } 78 }
115 79
116 [[nodiscard]] bool IsBlack() const { 80 constexpr void SetColor(RBColor c) {
117 return rbe_color == EntryColor::Black; 81 m_rbe_color = c;
118 } 82 }
119 83
120 [[nodiscard]] bool IsRed() const { 84private:
121 return rbe_color == EntryColor::Red; 85 T* m_rbe_left{};
122 } 86 T* m_rbe_right{};
87 T* m_rbe_parent{};
88 RBColor m_rbe_color{RBColor::RB_BLACK};
89};
90#pragma pack(pop)
123 91
124 [[nodiscard]] EntryColor Color() const { 92template <typename T>
125 return rbe_color; 93struct CheckRBEntry {
126 } 94 static constexpr bool value = false;
95};
96template <typename T>
97struct CheckRBEntry<RBEntry<T>> {
98 static constexpr bool value = true;
99};
127 100
128 void SetColor(EntryColor color) { 101template <typename T>
129 rbe_color = color; 102concept IsRBEntry = CheckRBEntry<T>::value;
130 } 103
104template <typename T>
105concept HasRBEntry = requires(T& t, const T& ct) {
106 { t.GetRBEntry() } -> std::same_as<RBEntry<T>&>;
107 { ct.GetRBEntry() } -> std::same_as<const RBEntry<T>&>;
108};
131 109
110template <typename T>
111requires HasRBEntry<T>
112class RBHead {
132private: 113private:
133 T* rbe_left = nullptr; 114 T* m_rbh_root = nullptr;
134 T* rbe_right = nullptr; 115
135 T* rbe_parent = nullptr; 116public:
136 EntryColor rbe_color{}; 117 [[nodiscard]] constexpr T* Root() {
118 return m_rbh_root;
119 }
120 [[nodiscard]] constexpr const T* Root() const {
121 return m_rbh_root;
122 }
123 constexpr void SetRoot(T* root) {
124 m_rbh_root = root;
125 }
126
127 [[nodiscard]] constexpr bool IsEmpty() const {
128 return this->Root() == nullptr;
129 }
137}; 130};
138 131
139template <typename Node> 132template <typename T>
140[[nodiscard]] RBEntry<Node>& RB_ENTRY(Node* node) { 133requires HasRBEntry<T>
141 return node->GetEntry(); 134[[nodiscard]] constexpr RBEntry<T>& RB_ENTRY(T* t) {
135 return t->GetRBEntry();
142} 136}
143 137template <typename T>
144template <typename Node> 138requires HasRBEntry<T>
145[[nodiscard]] const RBEntry<Node>& RB_ENTRY(const Node* node) { 139[[nodiscard]] constexpr const RBEntry<T>& RB_ENTRY(const T* t) {
146 return node->GetEntry(); 140 return t->GetRBEntry();
147} 141}
148 142
149template <typename Node> 143template <typename T>
150[[nodiscard]] Node* RB_PARENT(Node* node) { 144requires HasRBEntry<T>
151 return RB_ENTRY(node).Parent(); 145[[nodiscard]] constexpr T* RB_LEFT(T* t) {
146 return RB_ENTRY(t).Left();
152} 147}
153 148template <typename T>
154template <typename Node> 149requires HasRBEntry<T>
155[[nodiscard]] const Node* RB_PARENT(const Node* node) { 150[[nodiscard]] constexpr const T* RB_LEFT(const T* t) {
156 return RB_ENTRY(node).Parent(); 151 return RB_ENTRY(t).Left();
157} 152}
158 153
159template <typename Node> 154template <typename T>
160void RB_SET_PARENT(Node* node, Node* parent) { 155requires HasRBEntry<T>
161 return RB_ENTRY(node).SetParent(parent); 156[[nodiscard]] constexpr T* RB_RIGHT(T* t) {
157 return RB_ENTRY(t).Right();
162} 158}
163 159template <typename T>
164template <typename Node> 160requires HasRBEntry<T>
165[[nodiscard]] Node* RB_LEFT(Node* node) { 161[[nodiscard]] constexpr const T* RB_RIGHT(const T* t) {
166 return RB_ENTRY(node).Left(); 162 return RB_ENTRY(t).Right();
167} 163}
168 164
169template <typename Node> 165template <typename T>
170[[nodiscard]] const Node* RB_LEFT(const Node* node) { 166requires HasRBEntry<T>
171 return RB_ENTRY(node).Left(); 167[[nodiscard]] constexpr T* RB_PARENT(T* t) {
168 return RB_ENTRY(t).Parent();
172} 169}
173 170template <typename T>
174template <typename Node> 171requires HasRBEntry<T>
175void RB_SET_LEFT(Node* node, Node* left) { 172[[nodiscard]] constexpr const T* RB_PARENT(const T* t) {
176 return RB_ENTRY(node).SetLeft(left); 173 return RB_ENTRY(t).Parent();
177} 174}
178 175
179template <typename Node> 176template <typename T>
180[[nodiscard]] Node* RB_RIGHT(Node* node) { 177requires HasRBEntry<T>
181 return RB_ENTRY(node).Right(); 178constexpr void RB_SET_LEFT(T* t, T* e) {
179 RB_ENTRY(t).SetLeft(e);
182} 180}
183 181template <typename T>
184template <typename Node> 182requires HasRBEntry<T>
185[[nodiscard]] const Node* RB_RIGHT(const Node* node) { 183constexpr void RB_SET_RIGHT(T* t, T* e) {
186 return RB_ENTRY(node).Right(); 184 RB_ENTRY(t).SetRight(e);
187} 185}
188 186template <typename T>
189template <typename Node> 187requires HasRBEntry<T>
190void RB_SET_RIGHT(Node* node, Node* right) { 188constexpr void RB_SET_PARENT(T* t, T* e) {
191 return RB_ENTRY(node).SetRight(right); 189 RB_ENTRY(t).SetParent(e);
192} 190}
193 191
194template <typename Node> 192template <typename T>
195[[nodiscard]] bool RB_IS_BLACK(const Node* node) { 193requires HasRBEntry<T>
196 return RB_ENTRY(node).IsBlack(); 194[[nodiscard]] constexpr bool RB_IS_BLACK(const T* t) {
195 return RB_ENTRY(t).IsBlack();
197} 196}
198 197template <typename T>
199template <typename Node> 198requires HasRBEntry<T>
200[[nodiscard]] bool RB_IS_RED(const Node* node) { 199[[nodiscard]] constexpr bool RB_IS_RED(const T* t) {
201 return RB_ENTRY(node).IsRed(); 200 return RB_ENTRY(t).IsRed();
202} 201}
203 202
204template <typename Node> 203template <typename T>
205[[nodiscard]] EntryColor RB_COLOR(const Node* node) { 204requires HasRBEntry<T>
206 return RB_ENTRY(node).Color(); 205[[nodiscard]] constexpr RBColor RB_COLOR(const T* t) {
206 return RB_ENTRY(t).Color();
207} 207}
208 208
209template <typename Node> 209template <typename T>
210void RB_SET_COLOR(Node* node, EntryColor color) { 210requires HasRBEntry<T>
211 return RB_ENTRY(node).SetColor(color); 211constexpr void RB_SET_COLOR(T* t, RBColor c) {
212 RB_ENTRY(t).SetColor(c);
212} 213}
213 214
214template <typename Node> 215template <typename T>
215void RB_SET(Node* node, Node* parent) { 216requires HasRBEntry<T>
216 auto& entry = RB_ENTRY(node); 217constexpr void RB_SET(T* elm, T* parent) {
217 entry.SetParent(parent); 218 auto& rb_entry = RB_ENTRY(elm);
218 entry.SetLeft(nullptr); 219 rb_entry.SetParent(parent);
219 entry.SetRight(nullptr); 220 rb_entry.SetLeft(nullptr);
220 entry.SetColor(EntryColor::Red); 221 rb_entry.SetRight(nullptr);
222 rb_entry.SetColor(RBColor::RB_RED);
221} 223}
222 224
223template <typename Node> 225template <typename T>
224void RB_SET_BLACKRED(Node* black, Node* red) { 226requires HasRBEntry<T>
225 RB_SET_COLOR(black, EntryColor::Black); 227constexpr void RB_SET_BLACKRED(T* black, T* red) {
226 RB_SET_COLOR(red, EntryColor::Red); 228 RB_SET_COLOR(black, RBColor::RB_BLACK);
229 RB_SET_COLOR(red, RBColor::RB_RED);
227} 230}
228 231
229template <typename Node> 232template <typename T>
230void RB_ROTATE_LEFT(RBHead<Node>* head, Node* elm, Node*& tmp) { 233requires HasRBEntry<T>
234constexpr void RB_ROTATE_LEFT(RBHead<T>& head, T* elm, T*& tmp) {
231 tmp = RB_RIGHT(elm); 235 tmp = RB_RIGHT(elm);
232 RB_SET_RIGHT(elm, RB_LEFT(tmp)); 236 if (RB_SET_RIGHT(elm, RB_LEFT(tmp)); RB_RIGHT(elm) != nullptr) {
233 if (RB_RIGHT(elm) != nullptr) {
234 RB_SET_PARENT(RB_LEFT(tmp), elm); 237 RB_SET_PARENT(RB_LEFT(tmp), elm);
235 } 238 }
236 239
237 RB_SET_PARENT(tmp, RB_PARENT(elm)); 240 if (RB_SET_PARENT(tmp, RB_PARENT(elm)); RB_PARENT(tmp) != nullptr) {
238 if (RB_PARENT(tmp) != nullptr) {
239 if (elm == RB_LEFT(RB_PARENT(elm))) { 241 if (elm == RB_LEFT(RB_PARENT(elm))) {
240 RB_SET_LEFT(RB_PARENT(elm), tmp); 242 RB_SET_LEFT(RB_PARENT(elm), tmp);
241 } else { 243 } else {
242 RB_SET_RIGHT(RB_PARENT(elm), tmp); 244 RB_SET_RIGHT(RB_PARENT(elm), tmp);
243 } 245 }
244 } else { 246 } else {
245 head->SetRoot(tmp); 247 head.SetRoot(tmp);
246 } 248 }
247 249
248 RB_SET_LEFT(tmp, elm); 250 RB_SET_LEFT(tmp, elm);
249 RB_SET_PARENT(elm, tmp); 251 RB_SET_PARENT(elm, tmp);
250} 252}
251 253
252template <typename Node> 254template <typename T>
253void RB_ROTATE_RIGHT(RBHead<Node>* head, Node* elm, Node*& tmp) { 255requires HasRBEntry<T>
256constexpr void RB_ROTATE_RIGHT(RBHead<T>& head, T* elm, T*& tmp) {
254 tmp = RB_LEFT(elm); 257 tmp = RB_LEFT(elm);
255 RB_SET_LEFT(elm, RB_RIGHT(tmp)); 258 if (RB_SET_LEFT(elm, RB_RIGHT(tmp)); RB_LEFT(elm) != nullptr) {
256 if (RB_LEFT(elm) != nullptr) {
257 RB_SET_PARENT(RB_RIGHT(tmp), elm); 259 RB_SET_PARENT(RB_RIGHT(tmp), elm);
258 } 260 }
259 261
260 RB_SET_PARENT(tmp, RB_PARENT(elm)); 262 if (RB_SET_PARENT(tmp, RB_PARENT(elm)); RB_PARENT(tmp) != nullptr) {
261 if (RB_PARENT(tmp) != nullptr) {
262 if (elm == RB_LEFT(RB_PARENT(elm))) { 263 if (elm == RB_LEFT(RB_PARENT(elm))) {
263 RB_SET_LEFT(RB_PARENT(elm), tmp); 264 RB_SET_LEFT(RB_PARENT(elm), tmp);
264 } else { 265 } else {
265 RB_SET_RIGHT(RB_PARENT(elm), tmp); 266 RB_SET_RIGHT(RB_PARENT(elm), tmp);
266 } 267 }
267 } else { 268 } else {
268 head->SetRoot(tmp); 269 head.SetRoot(tmp);
269 } 270 }
270 271
271 RB_SET_RIGHT(tmp, elm); 272 RB_SET_RIGHT(tmp, elm);
272 RB_SET_PARENT(elm, tmp); 273 RB_SET_PARENT(elm, tmp);
273} 274}
274 275
275template <typename Node> 276template <typename T>
276void RB_INSERT_COLOR(RBHead<Node>* head, Node* elm) { 277requires HasRBEntry<T>
277 Node* parent = nullptr; 278constexpr void RB_REMOVE_COLOR(RBHead<T>& head, T* parent, T* elm) {
278 Node* tmp = nullptr; 279 T* tmp;
279 280 while ((elm == nullptr || RB_IS_BLACK(elm)) && elm != head.Root()) {
280 while ((parent = RB_PARENT(elm)) != nullptr && RB_IS_RED(parent)) {
281 Node* gparent = RB_PARENT(parent);
282 if (parent == RB_LEFT(gparent)) {
283 tmp = RB_RIGHT(gparent);
284 if (tmp && RB_IS_RED(tmp)) {
285 RB_SET_COLOR(tmp, EntryColor::Black);
286 RB_SET_BLACKRED(parent, gparent);
287 elm = gparent;
288 continue;
289 }
290
291 if (RB_RIGHT(parent) == elm) {
292 RB_ROTATE_LEFT(head, parent, tmp);
293 tmp = parent;
294 parent = elm;
295 elm = tmp;
296 }
297
298 RB_SET_BLACKRED(parent, gparent);
299 RB_ROTATE_RIGHT(head, gparent, tmp);
300 } else {
301 tmp = RB_LEFT(gparent);
302 if (tmp && RB_IS_RED(tmp)) {
303 RB_SET_COLOR(tmp, EntryColor::Black);
304 RB_SET_BLACKRED(parent, gparent);
305 elm = gparent;
306 continue;
307 }
308
309 if (RB_LEFT(parent) == elm) {
310 RB_ROTATE_RIGHT(head, parent, tmp);
311 tmp = parent;
312 parent = elm;
313 elm = tmp;
314 }
315
316 RB_SET_BLACKRED(parent, gparent);
317 RB_ROTATE_LEFT(head, gparent, tmp);
318 }
319 }
320
321 RB_SET_COLOR(head->Root(), EntryColor::Black);
322}
323
324template <typename Node>
325void RB_REMOVE_COLOR(RBHead<Node>* head, Node* parent, Node* elm) {
326 Node* tmp;
327 while ((elm == nullptr || RB_IS_BLACK(elm)) && elm != head->Root() && parent != nullptr) {
328 if (RB_LEFT(parent) == elm) { 281 if (RB_LEFT(parent) == elm) {
329 tmp = RB_RIGHT(parent); 282 tmp = RB_RIGHT(parent);
330 if (!tmp) {
331 ASSERT_MSG(false, "tmp is invalid!");
332 break;
333 }
334 if (RB_IS_RED(tmp)) { 283 if (RB_IS_RED(tmp)) {
335 RB_SET_BLACKRED(tmp, parent); 284 RB_SET_BLACKRED(tmp, parent);
336 RB_ROTATE_LEFT(head, parent, tmp); 285 RB_ROTATE_LEFT(head, parent, tmp);
@@ -339,29 +288,29 @@ void RB_REMOVE_COLOR(RBHead<Node>* head, Node* parent, Node* elm) {
339 288
340 if ((RB_LEFT(tmp) == nullptr || RB_IS_BLACK(RB_LEFT(tmp))) && 289 if ((RB_LEFT(tmp) == nullptr || RB_IS_BLACK(RB_LEFT(tmp))) &&
341 (RB_RIGHT(tmp) == nullptr || RB_IS_BLACK(RB_RIGHT(tmp)))) { 290 (RB_RIGHT(tmp) == nullptr || RB_IS_BLACK(RB_RIGHT(tmp)))) {
342 RB_SET_COLOR(tmp, EntryColor::Red); 291 RB_SET_COLOR(tmp, RBColor::RB_RED);
343 elm = parent; 292 elm = parent;
344 parent = RB_PARENT(elm); 293 parent = RB_PARENT(elm);
345 } else { 294 } else {
346 if (RB_RIGHT(tmp) == nullptr || RB_IS_BLACK(RB_RIGHT(tmp))) { 295 if (RB_RIGHT(tmp) == nullptr || RB_IS_BLACK(RB_RIGHT(tmp))) {
347 Node* oleft; 296 T* oleft;
348 if ((oleft = RB_LEFT(tmp)) != nullptr) { 297 if ((oleft = RB_LEFT(tmp)) != nullptr) {
349 RB_SET_COLOR(oleft, EntryColor::Black); 298 RB_SET_COLOR(oleft, RBColor::RB_BLACK);
350 } 299 }
351 300
352 RB_SET_COLOR(tmp, EntryColor::Red); 301 RB_SET_COLOR(tmp, RBColor::RB_RED);
353 RB_ROTATE_RIGHT(head, tmp, oleft); 302 RB_ROTATE_RIGHT(head, tmp, oleft);
354 tmp = RB_RIGHT(parent); 303 tmp = RB_RIGHT(parent);
355 } 304 }
356 305
357 RB_SET_COLOR(tmp, RB_COLOR(parent)); 306 RB_SET_COLOR(tmp, RB_COLOR(parent));
358 RB_SET_COLOR(parent, EntryColor::Black); 307 RB_SET_COLOR(parent, RBColor::RB_BLACK);
359 if (RB_RIGHT(tmp)) { 308 if (RB_RIGHT(tmp)) {
360 RB_SET_COLOR(RB_RIGHT(tmp), EntryColor::Black); 309 RB_SET_COLOR(RB_RIGHT(tmp), RBColor::RB_BLACK);
361 } 310 }
362 311
363 RB_ROTATE_LEFT(head, parent, tmp); 312 RB_ROTATE_LEFT(head, parent, tmp);
364 elm = head->Root(); 313 elm = head.Root();
365 break; 314 break;
366 } 315 }
367 } else { 316 } else {
@@ -372,68 +321,56 @@ void RB_REMOVE_COLOR(RBHead<Node>* head, Node* parent, Node* elm) {
372 tmp = RB_LEFT(parent); 321 tmp = RB_LEFT(parent);
373 } 322 }
374 323
375 if (!tmp) {
376 ASSERT_MSG(false, "tmp is invalid!");
377 break;
378 }
379
380 if ((RB_LEFT(tmp) == nullptr || RB_IS_BLACK(RB_LEFT(tmp))) && 324 if ((RB_LEFT(tmp) == nullptr || RB_IS_BLACK(RB_LEFT(tmp))) &&
381 (RB_RIGHT(tmp) == nullptr || RB_IS_BLACK(RB_RIGHT(tmp)))) { 325 (RB_RIGHT(tmp) == nullptr || RB_IS_BLACK(RB_RIGHT(tmp)))) {
382 RB_SET_COLOR(tmp, EntryColor::Red); 326 RB_SET_COLOR(tmp, RBColor::RB_RED);
383 elm = parent; 327 elm = parent;
384 parent = RB_PARENT(elm); 328 parent = RB_PARENT(elm);
385 } else { 329 } else {
386 if (RB_LEFT(tmp) == nullptr || RB_IS_BLACK(RB_LEFT(tmp))) { 330 if (RB_LEFT(tmp) == nullptr || RB_IS_BLACK(RB_LEFT(tmp))) {
387 Node* oright; 331 T* oright;
388 if ((oright = RB_RIGHT(tmp)) != nullptr) { 332 if ((oright = RB_RIGHT(tmp)) != nullptr) {
389 RB_SET_COLOR(oright, EntryColor::Black); 333 RB_SET_COLOR(oright, RBColor::RB_BLACK);
390 } 334 }
391 335
392 RB_SET_COLOR(tmp, EntryColor::Red); 336 RB_SET_COLOR(tmp, RBColor::RB_RED);
393 RB_ROTATE_LEFT(head, tmp, oright); 337 RB_ROTATE_LEFT(head, tmp, oright);
394 tmp = RB_LEFT(parent); 338 tmp = RB_LEFT(parent);
395 } 339 }
396 340
397 RB_SET_COLOR(tmp, RB_COLOR(parent)); 341 RB_SET_COLOR(tmp, RB_COLOR(parent));
398 RB_SET_COLOR(parent, EntryColor::Black); 342 RB_SET_COLOR(parent, RBColor::RB_BLACK);
399 343
400 if (RB_LEFT(tmp)) { 344 if (RB_LEFT(tmp)) {
401 RB_SET_COLOR(RB_LEFT(tmp), EntryColor::Black); 345 RB_SET_COLOR(RB_LEFT(tmp), RBColor::RB_BLACK);
402 } 346 }
403 347
404 RB_ROTATE_RIGHT(head, parent, tmp); 348 RB_ROTATE_RIGHT(head, parent, tmp);
405 elm = head->Root(); 349 elm = head.Root();
406 break; 350 break;
407 } 351 }
408 } 352 }
409 } 353 }
410 354
411 if (elm) { 355 if (elm) {
412 RB_SET_COLOR(elm, EntryColor::Black); 356 RB_SET_COLOR(elm, RBColor::RB_BLACK);
413 } 357 }
414} 358}
415 359
416template <typename Node> 360template <typename T>
417Node* RB_REMOVE(RBHead<Node>* head, Node* elm) { 361requires HasRBEntry<T>
418 Node* child = nullptr; 362constexpr T* RB_REMOVE(RBHead<T>& head, T* elm) {
419 Node* parent = nullptr; 363 T* child = nullptr;
420 Node* old = elm; 364 T* parent = nullptr;
421 EntryColor color{}; 365 T* old = elm;
422 366 RBColor color = RBColor::RB_BLACK;
423 const auto finalize = [&] {
424 if (color == EntryColor::Black) {
425 RB_REMOVE_COLOR(head, parent, child);
426 }
427
428 return old;
429 };
430 367
431 if (RB_LEFT(elm) == nullptr) { 368 if (RB_LEFT(elm) == nullptr) {
432 child = RB_RIGHT(elm); 369 child = RB_RIGHT(elm);
433 } else if (RB_RIGHT(elm) == nullptr) { 370 } else if (RB_RIGHT(elm) == nullptr) {
434 child = RB_LEFT(elm); 371 child = RB_LEFT(elm);
435 } else { 372 } else {
436 Node* left; 373 T* left;
437 elm = RB_RIGHT(elm); 374 elm = RB_RIGHT(elm);
438 while ((left = RB_LEFT(elm)) != nullptr) { 375 while ((left = RB_LEFT(elm)) != nullptr) {
439 elm = left; 376 elm = left;
@@ -446,6 +383,7 @@ Node* RB_REMOVE(RBHead<Node>* head, Node* elm) {
446 if (child) { 383 if (child) {
447 RB_SET_PARENT(child, parent); 384 RB_SET_PARENT(child, parent);
448 } 385 }
386
449 if (parent) { 387 if (parent) {
450 if (RB_LEFT(parent) == elm) { 388 if (RB_LEFT(parent) == elm) {
451 RB_SET_LEFT(parent, child); 389 RB_SET_LEFT(parent, child);
@@ -453,14 +391,14 @@ Node* RB_REMOVE(RBHead<Node>* head, Node* elm) {
453 RB_SET_RIGHT(parent, child); 391 RB_SET_RIGHT(parent, child);
454 } 392 }
455 } else { 393 } else {
456 head->SetRoot(child); 394 head.SetRoot(child);
457 } 395 }
458 396
459 if (RB_PARENT(elm) == old) { 397 if (RB_PARENT(elm) == old) {
460 parent = elm; 398 parent = elm;
461 } 399 }
462 400
463 elm->SetEntry(old->GetEntry()); 401 elm->SetRBEntry(old->GetRBEntry());
464 402
465 if (RB_PARENT(old)) { 403 if (RB_PARENT(old)) {
466 if (RB_LEFT(RB_PARENT(old)) == old) { 404 if (RB_LEFT(RB_PARENT(old)) == old) {
@@ -469,17 +407,24 @@ Node* RB_REMOVE(RBHead<Node>* head, Node* elm) {
469 RB_SET_RIGHT(RB_PARENT(old), elm); 407 RB_SET_RIGHT(RB_PARENT(old), elm);
470 } 408 }
471 } else { 409 } else {
472 head->SetRoot(elm); 410 head.SetRoot(elm);
473 } 411 }
412
474 RB_SET_PARENT(RB_LEFT(old), elm); 413 RB_SET_PARENT(RB_LEFT(old), elm);
414
475 if (RB_RIGHT(old)) { 415 if (RB_RIGHT(old)) {
476 RB_SET_PARENT(RB_RIGHT(old), elm); 416 RB_SET_PARENT(RB_RIGHT(old), elm);
477 } 417 }
418
478 if (parent) { 419 if (parent) {
479 left = parent; 420 left = parent;
480 } 421 }
481 422
482 return finalize(); 423 if (color == RBColor::RB_BLACK) {
424 RB_REMOVE_COLOR(head, parent, child);
425 }
426
427 return old;
483 } 428 }
484 429
485 parent = RB_PARENT(elm); 430 parent = RB_PARENT(elm);
@@ -495,17 +440,69 @@ Node* RB_REMOVE(RBHead<Node>* head, Node* elm) {
495 RB_SET_RIGHT(parent, child); 440 RB_SET_RIGHT(parent, child);
496 } 441 }
497 } else { 442 } else {
498 head->SetRoot(child); 443 head.SetRoot(child);
444 }
445
446 if (color == RBColor::RB_BLACK) {
447 RB_REMOVE_COLOR(head, parent, child);
448 }
449
450 return old;
451}
452
453template <typename T>
454requires HasRBEntry<T>
455constexpr void RB_INSERT_COLOR(RBHead<T>& head, T* elm) {
456 T *parent = nullptr, *tmp = nullptr;
457 while ((parent = RB_PARENT(elm)) != nullptr && RB_IS_RED(parent)) {
458 T* gparent = RB_PARENT(parent);
459 if (parent == RB_LEFT(gparent)) {
460 tmp = RB_RIGHT(gparent);
461 if (tmp && RB_IS_RED(tmp)) {
462 RB_SET_COLOR(tmp, RBColor::RB_BLACK);
463 RB_SET_BLACKRED(parent, gparent);
464 elm = gparent;
465 continue;
466 }
467
468 if (RB_RIGHT(parent) == elm) {
469 RB_ROTATE_LEFT(head, parent, tmp);
470 tmp = parent;
471 parent = elm;
472 elm = tmp;
473 }
474
475 RB_SET_BLACKRED(parent, gparent);
476 RB_ROTATE_RIGHT(head, gparent, tmp);
477 } else {
478 tmp = RB_LEFT(gparent);
479 if (tmp && RB_IS_RED(tmp)) {
480 RB_SET_COLOR(tmp, RBColor::RB_BLACK);
481 RB_SET_BLACKRED(parent, gparent);
482 elm = gparent;
483 continue;
484 }
485
486 if (RB_LEFT(parent) == elm) {
487 RB_ROTATE_RIGHT(head, parent, tmp);
488 tmp = parent;
489 parent = elm;
490 elm = tmp;
491 }
492
493 RB_SET_BLACKRED(parent, gparent);
494 RB_ROTATE_LEFT(head, gparent, tmp);
495 }
499 } 496 }
500 497
501 return finalize(); 498 RB_SET_COLOR(head.Root(), RBColor::RB_BLACK);
502} 499}
503 500
504// Inserts a node into the RB tree 501template <typename T, typename Compare>
505template <typename Node, typename CompareFunction> 502requires HasRBEntry<T>
506Node* RB_INSERT(RBHead<Node>* head, Node* elm, CompareFunction cmp) { 503constexpr T* RB_INSERT(RBHead<T>& head, T* elm, Compare cmp) {
507 Node* parent = nullptr; 504 T* parent = nullptr;
508 Node* tmp = head->Root(); 505 T* tmp = head.Root();
509 int comp = 0; 506 int comp = 0;
510 507
511 while (tmp) { 508 while (tmp) {
@@ -529,17 +526,17 @@ Node* RB_INSERT(RBHead<Node>* head, Node* elm, CompareFunction cmp) {
529 RB_SET_RIGHT(parent, elm); 526 RB_SET_RIGHT(parent, elm);
530 } 527 }
531 } else { 528 } else {
532 head->SetRoot(elm); 529 head.SetRoot(elm);
533 } 530 }
534 531
535 RB_INSERT_COLOR(head, elm); 532 RB_INSERT_COLOR(head, elm);
536 return nullptr; 533 return nullptr;
537} 534}
538 535
539// Finds the node with the same key as elm 536template <typename T, typename Compare>
540template <typename Node, typename CompareFunction> 537requires HasRBEntry<T>
541Node* RB_FIND(RBHead<Node>* head, Node* elm, CompareFunction cmp) { 538constexpr T* RB_FIND(RBHead<T>& head, T* elm, Compare cmp) {
542 Node* tmp = head->Root(); 539 T* tmp = head.Root();
543 540
544 while (tmp) { 541 while (tmp) {
545 const int comp = cmp(elm, tmp); 542 const int comp = cmp(elm, tmp);
@@ -555,11 +552,11 @@ Node* RB_FIND(RBHead<Node>* head, Node* elm, CompareFunction cmp) {
555 return nullptr; 552 return nullptr;
556} 553}
557 554
558// Finds the first node greater than or equal to the search key 555template <typename T, typename Compare>
559template <typename Node, typename CompareFunction> 556requires HasRBEntry<T>
560Node* RB_NFIND(RBHead<Node>* head, Node* elm, CompareFunction cmp) { 557constexpr T* RB_NFIND(RBHead<T>& head, T* elm, Compare cmp) {
561 Node* tmp = head->Root(); 558 T* tmp = head.Root();
562 Node* res = nullptr; 559 T* res = nullptr;
563 560
564 while (tmp) { 561 while (tmp) {
565 const int comp = cmp(elm, tmp); 562 const int comp = cmp(elm, tmp);
@@ -576,13 +573,13 @@ Node* RB_NFIND(RBHead<Node>* head, Node* elm, CompareFunction cmp) {
576 return res; 573 return res;
577} 574}
578 575
579// Finds the node with the same key as lelm 576template <typename T, typename U, typename Compare>
580template <typename Node, typename CompareFunction> 577requires HasRBEntry<T>
581Node* RB_FIND_LIGHT(RBHead<Node>* head, const void* lelm, CompareFunction lcmp) { 578constexpr T* RB_FIND_KEY(RBHead<T>& head, const U& key, Compare cmp) {
582 Node* tmp = head->Root(); 579 T* tmp = head.Root();
583 580
584 while (tmp) { 581 while (tmp) {
585 const int comp = lcmp(lelm, tmp); 582 const int comp = cmp(key, tmp);
586 if (comp < 0) { 583 if (comp < 0) {
587 tmp = RB_LEFT(tmp); 584 tmp = RB_LEFT(tmp);
588 } else if (comp > 0) { 585 } else if (comp > 0) {
@@ -595,14 +592,14 @@ Node* RB_FIND_LIGHT(RBHead<Node>* head, const void* lelm, CompareFunction lcmp)
595 return nullptr; 592 return nullptr;
596} 593}
597 594
598// Finds the first node greater than or equal to the search key 595template <typename T, typename U, typename Compare>
599template <typename Node, typename CompareFunction> 596requires HasRBEntry<T>
600Node* RB_NFIND_LIGHT(RBHead<Node>* head, const void* lelm, CompareFunction lcmp) { 597constexpr T* RB_NFIND_KEY(RBHead<T>& head, const U& key, Compare cmp) {
601 Node* tmp = head->Root(); 598 T* tmp = head.Root();
602 Node* res = nullptr; 599 T* res = nullptr;
603 600
604 while (tmp) { 601 while (tmp) {
605 const int comp = lcmp(lelm, tmp); 602 const int comp = cmp(key, tmp);
606 if (comp < 0) { 603 if (comp < 0) {
607 res = tmp; 604 res = tmp;
608 tmp = RB_LEFT(tmp); 605 tmp = RB_LEFT(tmp);
@@ -616,8 +613,43 @@ Node* RB_NFIND_LIGHT(RBHead<Node>* head, const void* lelm, CompareFunction lcmp)
616 return res; 613 return res;
617} 614}
618 615
619template <typename Node> 616template <typename T, typename Compare>
620Node* RB_NEXT(Node* elm) { 617requires HasRBEntry<T>
618constexpr T* RB_FIND_EXISTING(RBHead<T>& head, T* elm, Compare cmp) {
619 T* tmp = head.Root();
620
621 while (true) {
622 const int comp = cmp(elm, tmp);
623 if (comp < 0) {
624 tmp = RB_LEFT(tmp);
625 } else if (comp > 0) {
626 tmp = RB_RIGHT(tmp);
627 } else {
628 return tmp;
629 }
630 }
631}
632
633template <typename T, typename U, typename Compare>
634requires HasRBEntry<T>
635constexpr T* RB_FIND_EXISTING_KEY(RBHead<T>& head, const U& key, Compare cmp) {
636 T* tmp = head.Root();
637
638 while (true) {
639 const int comp = cmp(key, tmp);
640 if (comp < 0) {
641 tmp = RB_LEFT(tmp);
642 } else if (comp > 0) {
643 tmp = RB_RIGHT(tmp);
644 } else {
645 return tmp;
646 }
647 }
648}
649
650template <typename T>
651requires HasRBEntry<T>
652constexpr T* RB_NEXT(T* elm) {
621 if (RB_RIGHT(elm)) { 653 if (RB_RIGHT(elm)) {
622 elm = RB_RIGHT(elm); 654 elm = RB_RIGHT(elm);
623 while (RB_LEFT(elm)) { 655 while (RB_LEFT(elm)) {
@@ -636,8 +668,9 @@ Node* RB_NEXT(Node* elm) {
636 return elm; 668 return elm;
637} 669}
638 670
639template <typename Node> 671template <typename T>
640Node* RB_PREV(Node* elm) { 672requires HasRBEntry<T>
673constexpr T* RB_PREV(T* elm) {
641 if (RB_LEFT(elm)) { 674 if (RB_LEFT(elm)) {
642 elm = RB_LEFT(elm); 675 elm = RB_LEFT(elm);
643 while (RB_RIGHT(elm)) { 676 while (RB_RIGHT(elm)) {
@@ -656,30 +689,32 @@ Node* RB_PREV(Node* elm) {
656 return elm; 689 return elm;
657} 690}
658 691
659template <typename Node> 692template <typename T>
660Node* RB_MINMAX(RBHead<Node>* head, bool is_min) { 693requires HasRBEntry<T>
661 Node* tmp = head->Root(); 694constexpr T* RB_MIN(RBHead<T>& head) {
662 Node* parent = nullptr; 695 T* tmp = head.Root();
696 T* parent = nullptr;
663 697
664 while (tmp) { 698 while (tmp) {
665 parent = tmp; 699 parent = tmp;
666 if (is_min) { 700 tmp = RB_LEFT(tmp);
667 tmp = RB_LEFT(tmp);
668 } else {
669 tmp = RB_RIGHT(tmp);
670 }
671 } 701 }
672 702
673 return parent; 703 return parent;
674} 704}
675 705
676template <typename Node> 706template <typename T>
677Node* RB_MIN(RBHead<Node>* head) { 707requires HasRBEntry<T>
678 return RB_MINMAX(head, true); 708constexpr T* RB_MAX(RBHead<T>& head) {
679} 709 T* tmp = head.Root();
710 T* parent = nullptr;
680 711
681template <typename Node> 712 while (tmp) {
682Node* RB_MAX(RBHead<Node>* head) { 713 parent = tmp;
683 return RB_MINMAX(head, false); 714 tmp = RB_RIGHT(tmp);
715 }
716
717 return parent;
684} 718}
685} // namespace Common 719
720} // namespace Common::freebsd
diff --git a/src/common/uint128.h b/src/common/uint128.h
index 4780b2f9d..f450a6db9 100644
--- a/src/common/uint128.h
+++ b/src/common/uint128.h
@@ -1,10 +1,8 @@
1// Copyright 2019 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#pragma once 4#pragma once
6 5
7#include <cstring>
8#include <utility> 6#include <utility>
9 7
10#ifdef _MSC_VER 8#ifdef _MSC_VER
@@ -13,7 +11,7 @@
13#pragma intrinsic(_umul128) 11#pragma intrinsic(_umul128)
14#pragma intrinsic(_udiv128) 12#pragma intrinsic(_udiv128)
15#else 13#else
16#include <x86intrin.h> 14#include <cstring>
17#endif 15#endif
18 16
19#include "common/common_types.h" 17#include "common/common_types.h"
diff --git a/src/common/unique_function.h b/src/common/unique_function.h
index ca0559071..c15d88349 100644
--- a/src/common/unique_function.h
+++ b/src/common/unique_function.h
@@ -1,6 +1,5 @@
1// Copyright 2021 yuzu emulator team 1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#pragma once 4#pragma once
6 5
diff --git a/src/common/uuid.cpp b/src/common/uuid.cpp
index 2b6a530e3..89e1ed225 100644
--- a/src/common/uuid.cpp
+++ b/src/common/uuid.cpp
@@ -1,6 +1,5 @@
1// Copyright 2022 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#include <bit> 4#include <bit>
6#include <optional> 5#include <optional>
diff --git a/src/common/uuid.h b/src/common/uuid.h
index fe31e64e6..7172ca165 100644
--- a/src/common/uuid.h
+++ b/src/common/uuid.h
@@ -1,13 +1,11 @@
1// Copyright 2022 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#pragma once 4#pragma once
6 5
7#include <array> 6#include <array>
8#include <functional> 7#include <functional>
9#include <string> 8#include <string>
10#include <string_view>
11 9
12#include "common/common_types.h" 10#include "common/common_types.h"
13 11
diff --git a/src/common/vector_math.h b/src/common/vector_math.h
index ba7c363c1..e62eeea2e 100644
--- a/src/common/vector_math.h
+++ b/src/common/vector_math.h
@@ -1,32 +1,6 @@
1// Licensed under GPLv2 or any later version 1// SPDX-FileCopyrightText: 2014 Tony Wasserka
2// Refer to the license.txt file included. 2// SPDX-FileCopyrightText: 2014 Dolphin Emulator Project
3 3// SPDX-License-Identifier: BSD-3-Clause AND GPL-2.0-or-later
4// Copyright 2014 Tony Wasserka
5// All rights reserved.
6//
7// Redistribution and use in source and binary forms, with or without
8// modification, are permitted provided that the following conditions are met:
9//
10// * Redistributions of source code must retain the above copyright
11// notice, this list of conditions and the following disclaimer.
12// * Redistributions in binary form must reproduce the above copyright
13// notice, this list of conditions and the following disclaimer in the
14// documentation and/or other materials provided with the distribution.
15// * Neither the name of the owner nor the names of its contributors may
16// be used to endorse or promote products derived from this software
17// without specific prior written permission.
18//
19// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 4
31#pragma once 5#pragma once
32 6
diff --git a/src/common/virtual_buffer.cpp b/src/common/virtual_buffer.cpp
index e3ca29258..dea6de99f 100644
--- a/src/common/virtual_buffer.cpp
+++ b/src/common/virtual_buffer.cpp
@@ -1,6 +1,5 @@
1// Copyright 2020 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#ifdef _WIN32 4#ifdef _WIN32
6#include <windows.h> 5#include <windows.h>
diff --git a/src/common/virtual_buffer.h b/src/common/virtual_buffer.h
index fb1a6f81f..4f6e3e6e5 100644
--- a/src/common/virtual_buffer.h
+++ b/src/common/virtual_buffer.h
@@ -1,10 +1,8 @@
1// Copyright 2020 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#pragma once 4#pragma once
6 5
7#include <type_traits>
8#include <utility> 6#include <utility>
9 7
10namespace Common { 8namespace Common {
diff --git a/src/common/wall_clock.cpp b/src/common/wall_clock.cpp
index 9acf7551e..ae07f2811 100644
--- a/src/common/wall_clock.cpp
+++ b/src/common/wall_clock.cpp
@@ -1,8 +1,5 @@
1// Copyright 2020 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4
5#include <cstdint>
6 3
7#include "common/uint128.h" 4#include "common/uint128.h"
8#include "common/wall_clock.h" 5#include "common/wall_clock.h"
@@ -70,7 +67,7 @@ std::unique_ptr<WallClock> CreateBestMatchingClock(u64 emulated_cpu_frequency,
70 const auto& caps = GetCPUCaps(); 67 const auto& caps = GetCPUCaps();
71 u64 rtsc_frequency = 0; 68 u64 rtsc_frequency = 0;
72 if (caps.invariant_tsc) { 69 if (caps.invariant_tsc) {
73 rtsc_frequency = EstimateRDTSCFrequency(); 70 rtsc_frequency = caps.tsc_frequency ? caps.tsc_frequency : EstimateRDTSCFrequency();
74 } 71 }
75 72
76 // Fallback to StandardWallClock if the hardware TSC does not have the precision greater than: 73 // Fallback to StandardWallClock if the hardware TSC does not have the precision greater than:
diff --git a/src/common/wall_clock.h b/src/common/wall_clock.h
index 874448c27..828a523a8 100644
--- a/src/common/wall_clock.h
+++ b/src/common/wall_clock.h
@@ -1,6 +1,5 @@
1// Copyright 2020 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#pragma once 4#pragma once
6 5
diff --git a/src/common/x64/cpu_detect.cpp b/src/common/x64/cpu_detect.cpp
index fbeacc7e2..1a27532d4 100644
--- a/src/common/x64/cpu_detect.cpp
+++ b/src/common/x64/cpu_detect.cpp
@@ -1,8 +1,12 @@
1// Copyright 2013 Dolphin Emulator Project / 2015 Citra Emulator Project 1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-FileCopyrightText: Copyright 2013 Dolphin Emulator Project / 2015 Citra Emulator Project
3// Refer to the license.txt file included. 3// SPDX-License-Identifier: GPL-2.0-or-later
4 4
5#include <array>
5#include <cstring> 6#include <cstring>
7#include <iterator>
8#include <string_view>
9#include "common/bit_util.h"
6#include "common/common_types.h" 10#include "common/common_types.h"
7#include "common/x64/cpu_detect.h" 11#include "common/x64/cpu_detect.h"
8 12
@@ -17,7 +21,7 @@
17// clang-format on 21// clang-format on
18#endif 22#endif
19 23
20static inline void __cpuidex(int info[4], int function_id, int subfunction_id) { 24static inline void __cpuidex(int info[4], u32 function_id, u32 subfunction_id) {
21#if defined(__DragonFly__) || defined(__FreeBSD__) 25#if defined(__DragonFly__) || defined(__FreeBSD__)
22 // Despite the name, this is just do_cpuid() with ECX as second input. 26 // Despite the name, this is just do_cpuid() with ECX as second input.
23 cpuid_count((u_int)function_id, (u_int)subfunction_id, (u_int*)info); 27 cpuid_count((u_int)function_id, (u_int)subfunction_id, (u_int*)info);
@@ -30,7 +34,7 @@ static inline void __cpuidex(int info[4], int function_id, int subfunction_id) {
30#endif 34#endif
31} 35}
32 36
33static inline void __cpuid(int info[4], int function_id) { 37static inline void __cpuid(int info[4], u32 function_id) {
34 return __cpuidex(info, function_id, 0); 38 return __cpuidex(info, function_id, 0);
35} 39}
36 40
@@ -45,6 +49,17 @@ static inline u64 _xgetbv(u32 index) {
45 49
46namespace Common { 50namespace Common {
47 51
52CPUCaps::Manufacturer CPUCaps::ParseManufacturer(std::string_view brand_string) {
53 if (brand_string == "GenuineIntel") {
54 return Manufacturer::Intel;
55 } else if (brand_string == "AuthenticAMD") {
56 return Manufacturer::AMD;
57 } else if (brand_string == "HygonGenuine") {
58 return Manufacturer::Hygon;
59 }
60 return Manufacturer::Unknown;
61}
62
48// Detects the various CPU features 63// Detects the various CPU features
49static CPUCaps Detect() { 64static CPUCaps Detect() {
50 CPUCaps caps = {}; 65 CPUCaps caps = {};
@@ -53,75 +68,74 @@ static CPUCaps Detect() {
53 // yuzu at all anyway 68 // yuzu at all anyway
54 69
55 int cpu_id[4]; 70 int cpu_id[4];
56 memset(caps.brand_string, 0, sizeof(caps.brand_string));
57 71
58 // Detect CPU's CPUID capabilities and grab CPU string 72 // Detect CPU's CPUID capabilities and grab manufacturer string
59 __cpuid(cpu_id, 0x00000000); 73 __cpuid(cpu_id, 0x00000000);
60 u32 max_std_fn = cpu_id[0]; // EAX 74 const u32 max_std_fn = cpu_id[0]; // EAX
61
62 std::memcpy(&caps.brand_string[0], &cpu_id[1], sizeof(int));
63 std::memcpy(&caps.brand_string[4], &cpu_id[3], sizeof(int));
64 std::memcpy(&caps.brand_string[8], &cpu_id[2], sizeof(int));
65 if (cpu_id[1] == 0x756e6547 && cpu_id[2] == 0x6c65746e && cpu_id[3] == 0x49656e69)
66 caps.manufacturer = Manufacturer::Intel;
67 else if (cpu_id[1] == 0x68747541 && cpu_id[2] == 0x444d4163 && cpu_id[3] == 0x69746e65)
68 caps.manufacturer = Manufacturer::AMD;
69 else if (cpu_id[1] == 0x6f677948 && cpu_id[2] == 0x656e6975 && cpu_id[3] == 0x6e65476e)
70 caps.manufacturer = Manufacturer::Hygon;
71 else
72 caps.manufacturer = Manufacturer::Unknown;
73 75
74 __cpuid(cpu_id, 0x80000000); 76 std::memset(caps.brand_string, 0, std::size(caps.brand_string));
77 std::memcpy(&caps.brand_string[0], &cpu_id[1], sizeof(u32));
78 std::memcpy(&caps.brand_string[4], &cpu_id[3], sizeof(u32));
79 std::memcpy(&caps.brand_string[8], &cpu_id[2], sizeof(u32));
80
81 caps.manufacturer = CPUCaps::ParseManufacturer(caps.brand_string);
82
83 // Set reasonable default cpu string even if brand string not available
84 std::strncpy(caps.cpu_string, caps.brand_string, std::size(caps.brand_string));
75 85
76 u32 max_ex_fn = cpu_id[0]; 86 __cpuid(cpu_id, 0x80000000);
77 87
78 // Set reasonable default brand string even if brand string not available 88 const u32 max_ex_fn = cpu_id[0];
79 strcpy(caps.cpu_string, caps.brand_string);
80 89
81 // Detect family and other miscellaneous features 90 // Detect family and other miscellaneous features
82 if (max_std_fn >= 1) { 91 if (max_std_fn >= 1) {
83 __cpuid(cpu_id, 0x00000001); 92 __cpuid(cpu_id, 0x00000001);
84 if ((cpu_id[3] >> 25) & 1) 93 caps.sse = Common::Bit<25>(cpu_id[3]);
85 caps.sse = true; 94 caps.sse2 = Common::Bit<26>(cpu_id[3]);
86 if ((cpu_id[3] >> 26) & 1) 95 caps.sse3 = Common::Bit<0>(cpu_id[2]);
87 caps.sse2 = true; 96 caps.pclmulqdq = Common::Bit<1>(cpu_id[2]);
88 if ((cpu_id[2]) & 1) 97 caps.ssse3 = Common::Bit<9>(cpu_id[2]);
89 caps.sse3 = true; 98 caps.sse4_1 = Common::Bit<19>(cpu_id[2]);
90 if ((cpu_id[2] >> 9) & 1) 99 caps.sse4_2 = Common::Bit<20>(cpu_id[2]);
91 caps.ssse3 = true; 100 caps.movbe = Common::Bit<22>(cpu_id[2]);
92 if ((cpu_id[2] >> 19) & 1) 101 caps.popcnt = Common::Bit<23>(cpu_id[2]);
93 caps.sse4_1 = true; 102 caps.aes = Common::Bit<25>(cpu_id[2]);
94 if ((cpu_id[2] >> 20) & 1) 103 caps.f16c = Common::Bit<29>(cpu_id[2]);
95 caps.sse4_2 = true;
96 if ((cpu_id[2] >> 25) & 1)
97 caps.aes = true;
98 104
99 // AVX support requires 3 separate checks: 105 // AVX support requires 3 separate checks:
100 // - Is the AVX bit set in CPUID? 106 // - Is the AVX bit set in CPUID?
101 // - Is the XSAVE bit set in CPUID? 107 // - Is the XSAVE bit set in CPUID?
102 // - XGETBV result has the XCR bit set. 108 // - XGETBV result has the XCR bit set.
103 if (((cpu_id[2] >> 28) & 1) && ((cpu_id[2] >> 27) & 1)) { 109 if (Common::Bit<28>(cpu_id[2]) && Common::Bit<27>(cpu_id[2])) {
104 if ((_xgetbv(_XCR_XFEATURE_ENABLED_MASK) & 0x6) == 0x6) { 110 if ((_xgetbv(_XCR_XFEATURE_ENABLED_MASK) & 0x6) == 0x6) {
105 caps.avx = true; 111 caps.avx = true;
106 if ((cpu_id[2] >> 12) & 1) 112 if (Common::Bit<12>(cpu_id[2]))
107 caps.fma = true; 113 caps.fma = true;
108 } 114 }
109 } 115 }
110 116
111 if (max_std_fn >= 7) { 117 if (max_std_fn >= 7) {
112 __cpuidex(cpu_id, 0x00000007, 0x00000000); 118 __cpuidex(cpu_id, 0x00000007, 0x00000000);
113 // Can't enable AVX2 unless the XSAVE/XGETBV checks above passed 119 // Can't enable AVX{2,512} unless the XSAVE/XGETBV checks above passed
114 if ((cpu_id[1] >> 5) & 1) 120 if (caps.avx) {
115 caps.avx2 = caps.avx; 121 caps.avx2 = Common::Bit<5>(cpu_id[1]);
116 if ((cpu_id[1] >> 3) & 1) 122 caps.avx512f = Common::Bit<16>(cpu_id[1]);
117 caps.bmi1 = true; 123 caps.avx512dq = Common::Bit<17>(cpu_id[1]);
118 if ((cpu_id[1] >> 8) & 1) 124 caps.avx512cd = Common::Bit<28>(cpu_id[1]);
119 caps.bmi2 = true; 125 caps.avx512bw = Common::Bit<30>(cpu_id[1]);
120 // Checks for AVX512F, AVX512CD, AVX512VL, AVX512DQ, AVX512BW (Intel Skylake-X/SP) 126 caps.avx512vl = Common::Bit<31>(cpu_id[1]);
121 if ((cpu_id[1] >> 16) & 1 && (cpu_id[1] >> 28) & 1 && (cpu_id[1] >> 31) & 1 && 127 caps.avx512vbmi = Common::Bit<1>(cpu_id[2]);
122 (cpu_id[1] >> 17) & 1 && (cpu_id[1] >> 30) & 1) { 128 caps.avx512bitalg = Common::Bit<12>(cpu_id[2]);
123 caps.avx512 = caps.avx2;
124 } 129 }
130
131 caps.bmi1 = Common::Bit<3>(cpu_id[1]);
132 caps.bmi2 = Common::Bit<8>(cpu_id[1]);
133 caps.sha = Common::Bit<29>(cpu_id[1]);
134
135 caps.gfni = Common::Bit<8>(cpu_id[2]);
136
137 __cpuidex(cpu_id, 0x00000007, 0x00000001);
138 caps.avx_vnni = caps.avx && Common::Bit<4>(cpu_id[0]);
125 } 139 }
126 } 140 }
127 141
@@ -138,14 +152,28 @@ static CPUCaps Detect() {
138 if (max_ex_fn >= 0x80000001) { 152 if (max_ex_fn >= 0x80000001) {
139 // Check for more features 153 // Check for more features
140 __cpuid(cpu_id, 0x80000001); 154 __cpuid(cpu_id, 0x80000001);
141 if ((cpu_id[2] >> 16) & 1) 155 caps.lzcnt = Common::Bit<5>(cpu_id[2]);
142 caps.fma4 = true; 156 caps.fma4 = Common::Bit<16>(cpu_id[2]);
143 } 157 }
144 158
145 if (max_ex_fn >= 0x80000007) { 159 if (max_ex_fn >= 0x80000007) {
146 __cpuid(cpu_id, 0x80000007); 160 __cpuid(cpu_id, 0x80000007);
147 if (cpu_id[3] & (1 << 8)) { 161 caps.invariant_tsc = Common::Bit<8>(cpu_id[3]);
148 caps.invariant_tsc = true; 162 }
163
164 if (max_std_fn >= 0x15) {
165 __cpuid(cpu_id, 0x15);
166 caps.tsc_crystal_ratio_denominator = cpu_id[0];
167 caps.tsc_crystal_ratio_numerator = cpu_id[1];
168 caps.crystal_frequency = cpu_id[2];
169 // Some CPU models might not return a crystal frequency.
170 // The CPU model can be detected to use the values from turbostat
171 // https://github.com/torvalds/linux/blob/master/tools/power/x86/turbostat/turbostat.c#L5569
172 // but it's easier to just estimate the TSC tick rate for these cases.
173 if (caps.tsc_crystal_ratio_denominator) {
174 caps.tsc_frequency = static_cast<u64>(caps.crystal_frequency) *
175 caps.tsc_crystal_ratio_numerator /
176 caps.tsc_crystal_ratio_denominator;
149 } 177 }
150 } 178 }
151 179
diff --git a/src/common/x64/cpu_detect.h b/src/common/x64/cpu_detect.h
index e3b63302e..6830f3795 100644
--- a/src/common/x64/cpu_detect.h
+++ b/src/common/x64/cpu_detect.h
@@ -1,42 +1,71 @@
1// Copyright 2013 Dolphin Emulator Project / 2015 Citra Emulator Project 1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-FileCopyrightText: Copyright 2013 Dolphin Emulator Project / 2015 Citra Emulator Project
3// Refer to the license.txt file included. 3// SPDX-License-Identifier: GPL-2.0-or-later
4 4
5#pragma once 5#pragma once
6 6
7namespace Common { 7#include <string_view>
8#include "common/common_types.h"
8 9
9enum class Manufacturer : u32 { 10namespace Common {
10 Intel = 0,
11 AMD = 1,
12 Hygon = 2,
13 Unknown = 3,
14};
15 11
16/// x86/x64 CPU capabilities that may be detected by this module 12/// x86/x64 CPU capabilities that may be detected by this module
17struct CPUCaps { 13struct CPUCaps {
14
15 enum class Manufacturer : u8 {
16 Unknown = 0,
17 Intel = 1,
18 AMD = 2,
19 Hygon = 3,
20 };
21
22 static Manufacturer ParseManufacturer(std::string_view brand_string);
23
18 Manufacturer manufacturer; 24 Manufacturer manufacturer;
19 char cpu_string[0x21]; 25 char brand_string[13];
20 char brand_string[0x41]; 26
21 bool sse; 27 char cpu_string[48];
22 bool sse2; 28
23 bool sse3;
24 bool ssse3;
25 bool sse4_1;
26 bool sse4_2;
27 bool lzcnt;
28 bool avx;
29 bool avx2;
30 bool avx512;
31 bool bmi1;
32 bool bmi2;
33 bool fma;
34 bool fma4;
35 bool aes;
36 bool invariant_tsc;
37 u32 base_frequency; 29 u32 base_frequency;
38 u32 max_frequency; 30 u32 max_frequency;
39 u32 bus_frequency; 31 u32 bus_frequency;
32
33 u32 tsc_crystal_ratio_denominator;
34 u32 tsc_crystal_ratio_numerator;
35 u32 crystal_frequency;
36 u64 tsc_frequency; // Derived from the above three values
37
38 bool sse : 1;
39 bool sse2 : 1;
40 bool sse3 : 1;
41 bool ssse3 : 1;
42 bool sse4_1 : 1;
43 bool sse4_2 : 1;
44
45 bool avx : 1;
46 bool avx_vnni : 1;
47 bool avx2 : 1;
48 bool avx512f : 1;
49 bool avx512dq : 1;
50 bool avx512cd : 1;
51 bool avx512bw : 1;
52 bool avx512vl : 1;
53 bool avx512vbmi : 1;
54 bool avx512bitalg : 1;
55
56 bool aes : 1;
57 bool bmi1 : 1;
58 bool bmi2 : 1;
59 bool f16c : 1;
60 bool fma : 1;
61 bool fma4 : 1;
62 bool gfni : 1;
63 bool invariant_tsc : 1;
64 bool lzcnt : 1;
65 bool movbe : 1;
66 bool pclmulqdq : 1;
67 bool popcnt : 1;
68 bool sha : 1;
40}; 69};
41 70
42/** 71/**
diff --git a/src/common/x64/native_clock.cpp b/src/common/x64/native_clock.cpp
index 91b842829..8b08332ab 100644
--- a/src/common/x64/native_clock.cpp
+++ b/src/common/x64/native_clock.cpp
@@ -1,36 +1,57 @@
1// Copyright 2020 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#include <array> 4#include <array>
6#include <chrono> 5#include <chrono>
7#include <limits>
8#include <mutex>
9#include <thread> 6#include <thread>
10 7
11#include "common/atomic_ops.h" 8#include "common/atomic_ops.h"
12#include "common/uint128.h" 9#include "common/uint128.h"
13#include "common/x64/native_clock.h" 10#include "common/x64/native_clock.h"
14 11
12#ifdef _MSC_VER
13#include <intrin.h>
14#endif
15
15namespace Common { 16namespace Common {
16 17
18#ifdef _MSC_VER
19__forceinline static u64 FencedRDTSC() {
20 _mm_lfence();
21 _ReadWriteBarrier();
22 const u64 result = __rdtsc();
23 _mm_lfence();
24 _ReadWriteBarrier();
25 return result;
26}
27#else
28static u64 FencedRDTSC() {
29 u64 result;
30 asm volatile("lfence\n\t"
31 "rdtsc\n\t"
32 "shl $32, %%rdx\n\t"
33 "or %%rdx, %0\n\t"
34 "lfence"
35 : "=a"(result)
36 :
37 : "rdx", "memory", "cc");
38 return result;
39}
40#endif
41
17u64 EstimateRDTSCFrequency() { 42u64 EstimateRDTSCFrequency() {
18 // Discard the first result measuring the rdtsc. 43 // Discard the first result measuring the rdtsc.
19 _mm_mfence(); 44 FencedRDTSC();
20 __rdtsc();
21 std::this_thread::sleep_for(std::chrono::milliseconds{1}); 45 std::this_thread::sleep_for(std::chrono::milliseconds{1});
22 _mm_mfence(); 46 FencedRDTSC();
23 __rdtsc();
24 47
25 // Get the current time. 48 // Get the current time.
26 const auto start_time = std::chrono::steady_clock::now(); 49 const auto start_time = std::chrono::steady_clock::now();
27 _mm_mfence(); 50 const u64 tsc_start = FencedRDTSC();
28 const u64 tsc_start = __rdtsc();
29 // Wait for 200 milliseconds. 51 // Wait for 200 milliseconds.
30 std::this_thread::sleep_for(std::chrono::milliseconds{200}); 52 std::this_thread::sleep_for(std::chrono::milliseconds{200});
31 const auto end_time = std::chrono::steady_clock::now(); 53 const auto end_time = std::chrono::steady_clock::now();
32 _mm_mfence(); 54 const u64 tsc_end = FencedRDTSC();
33 const u64 tsc_end = __rdtsc();
34 // Calculate differences. 55 // Calculate differences.
35 const u64 timer_diff = static_cast<u64>( 56 const u64 timer_diff = static_cast<u64>(
36 std::chrono::duration_cast<std::chrono::nanoseconds>(end_time - start_time).count()); 57 std::chrono::duration_cast<std::chrono::nanoseconds>(end_time - start_time).count());
@@ -44,8 +65,7 @@ NativeClock::NativeClock(u64 emulated_cpu_frequency_, u64 emulated_clock_frequen
44 u64 rtsc_frequency_) 65 u64 rtsc_frequency_)
45 : WallClock(emulated_cpu_frequency_, emulated_clock_frequency_, true), rtsc_frequency{ 66 : WallClock(emulated_cpu_frequency_, emulated_clock_frequency_, true), rtsc_frequency{
46 rtsc_frequency_} { 67 rtsc_frequency_} {
47 _mm_mfence(); 68 time_point.inner.last_measure = FencedRDTSC();
48 time_point.inner.last_measure = __rdtsc();
49 time_point.inner.accumulated_ticks = 0U; 69 time_point.inner.accumulated_ticks = 0U;
50 ns_rtsc_factor = GetFixedPoint64Factor(NS_RATIO, rtsc_frequency); 70 ns_rtsc_factor = GetFixedPoint64Factor(NS_RATIO, rtsc_frequency);
51 us_rtsc_factor = GetFixedPoint64Factor(US_RATIO, rtsc_frequency); 71 us_rtsc_factor = GetFixedPoint64Factor(US_RATIO, rtsc_frequency);
@@ -57,10 +77,10 @@ NativeClock::NativeClock(u64 emulated_cpu_frequency_, u64 emulated_clock_frequen
57u64 NativeClock::GetRTSC() { 77u64 NativeClock::GetRTSC() {
58 TimePoint new_time_point{}; 78 TimePoint new_time_point{};
59 TimePoint current_time_point{}; 79 TimePoint current_time_point{};
80
81 current_time_point.pack = Common::AtomicLoad128(time_point.pack.data());
60 do { 82 do {
61 current_time_point.pack = time_point.pack; 83 const u64 current_measure = FencedRDTSC();
62 _mm_mfence();
63 const u64 current_measure = __rdtsc();
64 u64 diff = current_measure - current_time_point.inner.last_measure; 84 u64 diff = current_measure - current_time_point.inner.last_measure;
65 diff = diff & ~static_cast<u64>(static_cast<s64>(diff) >> 63); // max(diff, 0) 85 diff = diff & ~static_cast<u64>(static_cast<s64>(diff) >> 63); // max(diff, 0)
66 new_time_point.inner.last_measure = current_measure > current_time_point.inner.last_measure 86 new_time_point.inner.last_measure = current_measure > current_time_point.inner.last_measure
@@ -68,22 +88,21 @@ u64 NativeClock::GetRTSC() {
68 : current_time_point.inner.last_measure; 88 : current_time_point.inner.last_measure;
69 new_time_point.inner.accumulated_ticks = current_time_point.inner.accumulated_ticks + diff; 89 new_time_point.inner.accumulated_ticks = current_time_point.inner.accumulated_ticks + diff;
70 } while (!Common::AtomicCompareAndSwap(time_point.pack.data(), new_time_point.pack, 90 } while (!Common::AtomicCompareAndSwap(time_point.pack.data(), new_time_point.pack,
71 current_time_point.pack)); 91 current_time_point.pack, current_time_point.pack));
72 /// The clock cannot be more precise than the guest timer, remove the lower bits 92 return new_time_point.inner.accumulated_ticks;
73 return new_time_point.inner.accumulated_ticks & inaccuracy_mask;
74} 93}
75 94
76void NativeClock::Pause(bool is_paused) { 95void NativeClock::Pause(bool is_paused) {
77 if (!is_paused) { 96 if (!is_paused) {
78 TimePoint current_time_point{}; 97 TimePoint current_time_point{};
79 TimePoint new_time_point{}; 98 TimePoint new_time_point{};
99
100 current_time_point.pack = Common::AtomicLoad128(time_point.pack.data());
80 do { 101 do {
81 current_time_point.pack = time_point.pack;
82 new_time_point.pack = current_time_point.pack; 102 new_time_point.pack = current_time_point.pack;
83 _mm_mfence(); 103 new_time_point.inner.last_measure = FencedRDTSC();
84 new_time_point.inner.last_measure = __rdtsc();
85 } while (!Common::AtomicCompareAndSwap(time_point.pack.data(), new_time_point.pack, 104 } while (!Common::AtomicCompareAndSwap(time_point.pack.data(), new_time_point.pack,
86 current_time_point.pack)); 105 current_time_point.pack, current_time_point.pack));
87 } 106 }
88} 107}
89 108
diff --git a/src/common/x64/native_clock.h b/src/common/x64/native_clock.h
index 7cbd400d2..38ae7a462 100644
--- a/src/common/x64/native_clock.h
+++ b/src/common/x64/native_clock.h
@@ -1,11 +1,8 @@
1// Copyright 2020 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#pragma once 4#pragma once
6 5
7#include <optional>
8
9#include "common/wall_clock.h" 6#include "common/wall_clock.h"
10 7
11namespace Common { 8namespace Common {
@@ -40,12 +37,8 @@ private:
40 } inner; 37 } inner;
41 }; 38 };
42 39
43 /// value used to reduce the native clocks accuracy as some apss rely on
44 /// undefined behavior where the level of accuracy in the clock shouldn't
45 /// be higher.
46 static constexpr u64 inaccuracy_mask = ~(UINT64_C(0x400) - 1);
47
48 TimePoint time_point; 40 TimePoint time_point;
41
49 // factors 42 // factors
50 u64 clock_rtsc_factor{}; 43 u64 clock_rtsc_factor{};
51 u64 cpu_rtsc_factor{}; 44 u64 cpu_rtsc_factor{};
diff --git a/src/common/x64/xbyak_abi.h b/src/common/x64/xbyak_abi.h
index 87b3d63a4..67e6e63c8 100644
--- a/src/common/x64/xbyak_abi.h
+++ b/src/common/x64/xbyak_abi.h
@@ -1,6 +1,5 @@
1// Copyright 2016 Citra Emulator Project 1// SPDX-FileCopyrightText: 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#pragma once 4#pragma once
6 5
diff --git a/src/common/x64/xbyak_util.h b/src/common/x64/xbyak_util.h
index 44d2558f1..250e5cddb 100644
--- a/src/common/x64/xbyak_util.h
+++ b/src/common/x64/xbyak_util.h
@@ -1,6 +1,5 @@
1// Copyright 2016 Citra Emulator Project 1// SPDX-FileCopyrightText: 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#pragma once 4#pragma once
6 5
diff --git a/src/common/zstd_compression.cpp b/src/common/zstd_compression.cpp
index 695b96a43..b71a41b78 100644
--- a/src/common/zstd_compression.cpp
+++ b/src/common/zstd_compression.cpp
@@ -1,6 +1,5 @@
1// Copyright 2019 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#include <algorithm> 4#include <algorithm>
6#include <zstd.h> 5#include <zstd.h>
diff --git a/src/common/zstd_compression.h b/src/common/zstd_compression.h
index bbce14f4e..a5ab2d05b 100644
--- a/src/common/zstd_compression.h
+++ b/src/common/zstd_compression.h
@@ -1,6 +1,5 @@
1// Copyright 2019 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#pragma once 4#pragma once
6 5