summaryrefslogtreecommitdiff
path: root/src/common
diff options
context:
space:
mode:
Diffstat (limited to 'src/common')
-rw-r--r--src/common/CMakeLists.txt47
-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.h8
-rw-r--r--src/common/bit_field.h15
-rw-r--r--src/common/bounded_threadsafe_queue.h9
-rw-r--r--src/common/concepts.h8
-rw-r--r--src/common/fixed_point.h274
-rw-r--r--src/common/hash.h7
-rw-r--r--src/common/input.h30
-rw-r--r--src/common/logging/backend.cpp2
-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/settings.h4
-rw-r--r--src/common/thread.h4
17 files changed, 894 insertions, 211 deletions
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index 635fb85c8..c0555f840 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -14,34 +14,11 @@ if (DEFINED ENV{DISPLAYVERSION})
14 set(DISPLAY_VERSION $ENV{DISPLAYVERSION}) 14 set(DISPLAY_VERSION $ENV{DISPLAYVERSION})
15endif () 15endif ()
16 16
17# Pass the path to git to the GenerateSCMRev.cmake as well 17include(GenerateSCMRev)
18find_package(Git QUIET)
19
20add_custom_command(OUTPUT scm_rev.cpp
21 COMMAND ${CMAKE_COMMAND}
22 -DSRC_DIR=${CMAKE_SOURCE_DIR}
23 -DBUILD_REPOSITORY=${BUILD_REPOSITORY}
24 -DTITLE_BAR_FORMAT_IDLE=${TITLE_BAR_FORMAT_IDLE}
25 -DTITLE_BAR_FORMAT_RUNNING=${TITLE_BAR_FORMAT_RUNNING}
26 -DBUILD_TAG=${BUILD_TAG}
27 -DBUILD_ID=${DISPLAY_VERSION}
28 -DGIT_REF_SPEC=${GIT_REF_SPEC}
29 -DGIT_REV=${GIT_REV}
30 -DGIT_DESC=${GIT_DESC}
31 -DGIT_BRANCH=${GIT_BRANCH}
32 -DBUILD_FULLNAME=${BUILD_FULLNAME}
33 -DGIT_EXECUTABLE=${GIT_EXECUTABLE}
34 -P ${CMAKE_SOURCE_DIR}/CMakeModules/GenerateSCMRev.cmake
35 DEPENDS
36 # Check that the scm_rev files haven't changed
37 "${CMAKE_CURRENT_SOURCE_DIR}/scm_rev.cpp.in"
38 "${CMAKE_CURRENT_SOURCE_DIR}/scm_rev.h"
39 # technically we should regenerate if the git version changed, but its not worth the effort imo
40 "${CMAKE_SOURCE_DIR}/CMakeModules/GenerateSCMRev.cmake"
41 VERBATIM
42)
43 18
44add_library(common STATIC 19add_library(common STATIC
20 address_space.cpp
21 address_space.h
45 algorithm.h 22 algorithm.h
46 alignment.h 23 alignment.h
47 announce_multiplayer_room.h 24 announce_multiplayer_room.h
@@ -106,6 +83,8 @@ add_library(common STATIC
106 microprofile.cpp 83 microprofile.cpp
107 microprofile.h 84 microprofile.h
108 microprofileui.h 85 microprofileui.h
86 multi_level_page_table.cpp
87 multi_level_page_table.h
109 nvidia_flags.cpp 88 nvidia_flags.cpp
110 nvidia_flags.h 89 nvidia_flags.h
111 page_table.cpp 90 page_table.cpp
@@ -117,7 +96,7 @@ add_library(common STATIC
117 quaternion.h 96 quaternion.h
118 reader_writer_queue.h 97 reader_writer_queue.h
119 ring_buffer.h 98 ring_buffer.h
120 scm_rev.cpp 99 ${CMAKE_CURRENT_BINARY_DIR}/scm_rev.cpp
121 scm_rev.h 100 scm_rev.h
122 scope_exit.h 101 scope_exit.h
123 settings.cpp 102 settings.cpp
@@ -166,6 +145,7 @@ if(ARCHITECTURE_x86_64)
166 x64/xbyak_abi.h 145 x64/xbyak_abi.h
167 x64/xbyak_util.h 146 x64/xbyak_util.h
168 ) 147 )
148 target_link_libraries(common PRIVATE xbyak)
169endif() 149endif()
170 150
171if (MSVC) 151if (MSVC)
@@ -176,12 +156,13 @@ if (MSVC)
176 ) 156 )
177 target_compile_options(common PRIVATE 157 target_compile_options(common PRIVATE
178 /W4 158 /W4
179 /WX 159
160 /we4242 # 'identifier': conversion from 'type1' to 'type2', possible loss of data
161 /we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data
162 /we4800 # Implicit conversion from 'type' to bool. Possible information loss
180 ) 163 )
181else() 164else()
182 target_compile_options(common PRIVATE 165 target_compile_options(common PRIVATE
183 -Werror
184
185 $<$<CXX_COMPILER_ID:Clang>:-fsized-deallocation> 166 $<$<CXX_COMPILER_ID:Clang>:-fsized-deallocation>
186 ) 167 )
187endif() 168endif()
@@ -189,7 +170,11 @@ endif()
189create_target_directory_groups(common) 170create_target_directory_groups(common)
190 171
191target_link_libraries(common PUBLIC ${Boost_LIBRARIES} fmt::fmt microprofile Threads::Threads) 172target_link_libraries(common PUBLIC ${Boost_LIBRARIES} fmt::fmt microprofile Threads::Threads)
192target_link_libraries(common PRIVATE lz4::lz4 xbyak) 173if (TARGET lz4::lz4)
174 target_link_libraries(common PRIVATE lz4::lz4)
175else()
176 target_link_libraries(common PRIVATE LZ4::lz4_shared)
177endif()
193if (TARGET zstd::zstd) 178if (TARGET zstd::zstd)
194 target_link_libraries(common PRIVATE zstd::zstd) 179 target_link_libraries(common PRIVATE zstd::zstd)
195else() 180else()
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 9ddfd637b..c27c9241d 100644
--- a/src/common/algorithm.h
+++ b/src/common/algorithm.h
@@ -24,4 +24,12 @@ template <class ForwardIt, class T, class Compare = std::less<>>
24 return first != last && !comp(value, *first) ? first : last; 24 return first != last && !comp(value, *first) ? first : last;
25} 25}
26 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
27} // namespace Common 35} // namespace Common
diff --git a/src/common/bit_field.h b/src/common/bit_field.h
index 7e1df62b1..e4e58ea45 100644
--- a/src/common/bit_field.h
+++ b/src/common/bit_field.h
@@ -141,10 +141,6 @@ public:
141 constexpr BitField(BitField&&) noexcept = default; 141 constexpr BitField(BitField&&) noexcept = default;
142 constexpr BitField& operator=(BitField&&) noexcept = default; 142 constexpr BitField& operator=(BitField&&) noexcept = default;
143 143
144 [[nodiscard]] constexpr operator T() const {
145 return Value();
146 }
147
148 constexpr void Assign(const T& value) { 144 constexpr void Assign(const T& value) {
149#ifdef _MSC_VER 145#ifdef _MSC_VER
150 storage = static_cast<StorageType>((storage & ~mask) | FormatValue(value)); 146 storage = static_cast<StorageType>((storage & ~mask) | FormatValue(value));
@@ -162,6 +158,17 @@ public:
162 return ExtractValue(storage); 158 return ExtractValue(storage);
163 } 159 }
164 160
161 template <typename ConvertedToType>
162 [[nodiscard]] constexpr ConvertedToType As() const {
163 static_assert(!std::is_same_v<T, ConvertedToType>,
164 "Unnecessary cast. Use Value() instead.");
165 return static_cast<ConvertedToType>(Value());
166 }
167
168 [[nodiscard]] constexpr operator T() const {
169 return Value();
170 }
171
165 [[nodiscard]] constexpr explicit operator bool() const { 172 [[nodiscard]] constexpr explicit operator bool() const {
166 return Value() != 0; 173 return Value() != 0;
167 } 174 }
diff --git a/src/common/bounded_threadsafe_queue.h b/src/common/bounded_threadsafe_queue.h
index 7e465549b..21217801e 100644
--- a/src/common/bounded_threadsafe_queue.h
+++ b/src/common/bounded_threadsafe_queue.h
@@ -21,11 +21,6 @@ constexpr size_t hardware_interference_size = std::hardware_destructive_interfer
21constexpr size_t hardware_interference_size = 64; 21constexpr size_t hardware_interference_size = 64;
22#endif 22#endif
23 23
24#ifdef _MSC_VER
25#pragma warning(push)
26#pragma warning(disable : 4324)
27#endif
28
29template <typename T, size_t capacity = 0x400> 24template <typename T, size_t capacity = 0x400>
30class MPSCQueue { 25class MPSCQueue {
31public: 26public:
@@ -160,8 +155,4 @@ private:
160 static_assert(std::is_nothrow_destructible_v<T>, "T must be nothrow destructible"); 155 static_assert(std::is_nothrow_destructible_v<T>, "T must be nothrow destructible");
161}; 156};
162 157
163#ifdef _MSC_VER
164#pragma warning(pop)
165#endif
166
167} // namespace Common 158} // namespace Common
diff --git a/src/common/concepts.h b/src/common/concepts.h
index a97555f6a..e8ce30dfe 100644
--- a/src/common/concepts.h
+++ b/src/common/concepts.h
@@ -34,4 +34,12 @@ concept DerivedFrom = requires {
34template <typename From, typename To> 34template <typename From, typename To>
35concept ConvertibleTo = std::is_convertible_v<From, To>; 35concept ConvertibleTo = std::is_convertible_v<From, To>;
36 36
37// No equivalents in the stdlib
38
39template <typename T>
40concept IsArithmetic = std::is_arithmetic_v<T>;
41
42template <typename T>
43concept IsIntegral = std::is_integral_v<T>;
44
37} // namespace Common 45} // namespace Common
diff --git a/src/common/fixed_point.h b/src/common/fixed_point.h
index 4a0f72cc9..f899b0d54 100644
--- a/src/common/fixed_point.h
+++ b/src/common/fixed_point.h
@@ -4,14 +4,7 @@
4// From: https://github.com/eteran/cpp-utilities/blob/master/fixed/include/cpp-utilities/fixed.h 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 5// See also: http://stackoverflow.com/questions/79677/whats-the-best-way-to-do-fixed-point-math
6 6
7#ifndef FIXED_H_ 7#pragma once
8#define FIXED_H_
9
10#if __cplusplus >= 201402L
11#define CONSTEXPR14 constexpr
12#else
13#define CONSTEXPR14
14#endif
15 8
16#include <cstddef> // for size_t 9#include <cstddef> // for size_t
17#include <cstdint> 10#include <cstdint>
@@ -19,6 +12,8 @@
19#include <ostream> 12#include <ostream>
20#include <type_traits> 13#include <type_traits>
21 14
15#include <common/concepts.h>
16
22namespace Common { 17namespace Common {
23 18
24template <size_t I, size_t F> 19template <size_t I, size_t F>
@@ -57,8 +52,8 @@ struct type_from_size<64> {
57 static constexpr size_t size = 64; 52 static constexpr size_t size = 64;
58 53
59 using value_type = int64_t; 54 using value_type = int64_t;
60 using unsigned_type = std::make_unsigned<value_type>::type; 55 using unsigned_type = std::make_unsigned_t<value_type>;
61 using signed_type = std::make_signed<value_type>::type; 56 using signed_type = std::make_signed_t<value_type>;
62 using next_size = type_from_size<128>; 57 using next_size = type_from_size<128>;
63}; 58};
64 59
@@ -68,8 +63,8 @@ struct type_from_size<32> {
68 static constexpr size_t size = 32; 63 static constexpr size_t size = 32;
69 64
70 using value_type = int32_t; 65 using value_type = int32_t;
71 using unsigned_type = std::make_unsigned<value_type>::type; 66 using unsigned_type = std::make_unsigned_t<value_type>;
72 using signed_type = std::make_signed<value_type>::type; 67 using signed_type = std::make_signed_t<value_type>;
73 using next_size = type_from_size<64>; 68 using next_size = type_from_size<64>;
74}; 69};
75 70
@@ -79,8 +74,8 @@ struct type_from_size<16> {
79 static constexpr size_t size = 16; 74 static constexpr size_t size = 16;
80 75
81 using value_type = int16_t; 76 using value_type = int16_t;
82 using unsigned_type = std::make_unsigned<value_type>::type; 77 using unsigned_type = std::make_unsigned_t<value_type>;
83 using signed_type = std::make_signed<value_type>::type; 78 using signed_type = std::make_signed_t<value_type>;
84 using next_size = type_from_size<32>; 79 using next_size = type_from_size<32>;
85}; 80};
86 81
@@ -90,8 +85,8 @@ struct type_from_size<8> {
90 static constexpr size_t size = 8; 85 static constexpr size_t size = 8;
91 86
92 using value_type = int8_t; 87 using value_type = int8_t;
93 using unsigned_type = std::make_unsigned<value_type>::type; 88 using unsigned_type = std::make_unsigned_t<value_type>;
94 using signed_type = std::make_signed<value_type>::type; 89 using signed_type = std::make_signed_t<value_type>;
95 using next_size = type_from_size<16>; 90 using next_size = type_from_size<16>;
96}; 91};
97 92
@@ -106,9 +101,9 @@ constexpr B next_to_base(N rhs) {
106struct divide_by_zero : std::exception {}; 101struct divide_by_zero : std::exception {};
107 102
108template <size_t I, size_t F> 103template <size_t I, size_t F>
109CONSTEXPR14 FixedPoint<I, F> divide( 104constexpr FixedPoint<I, F> divide(
110 FixedPoint<I, F> numerator, FixedPoint<I, F> denominator, FixedPoint<I, F>& remainder, 105 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) { 106 std::enable_if_t<type_from_size<I + F>::next_size::is_specialized>* = nullptr) {
112 107
113 using next_type = typename FixedPoint<I, F>::next_type; 108 using next_type = typename FixedPoint<I, F>::next_type;
114 using base_type = typename FixedPoint<I, F>::base_type; 109 using base_type = typename FixedPoint<I, F>::base_type;
@@ -126,9 +121,9 @@ CONSTEXPR14 FixedPoint<I, F> divide(
126} 121}
127 122
128template <size_t I, size_t F> 123template <size_t I, size_t F>
129CONSTEXPR14 FixedPoint<I, F> divide( 124constexpr FixedPoint<I, F> divide(
130 FixedPoint<I, F> numerator, FixedPoint<I, F> denominator, FixedPoint<I, F>& remainder, 125 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) { 126 std::enable_if_t<!type_from_size<I + F>::next_size::is_specialized>* = nullptr) {
132 127
133 using unsigned_type = typename FixedPoint<I, F>::unsigned_type; 128 using unsigned_type = typename FixedPoint<I, F>::unsigned_type;
134 129
@@ -196,9 +191,9 @@ CONSTEXPR14 FixedPoint<I, F> divide(
196 191
197// this is the usual implementation of multiplication 192// this is the usual implementation of multiplication
198template <size_t I, size_t F> 193template <size_t I, size_t F>
199CONSTEXPR14 FixedPoint<I, F> multiply( 194constexpr FixedPoint<I, F> multiply(
200 FixedPoint<I, F> lhs, FixedPoint<I, F> rhs, 195 FixedPoint<I, F> lhs, FixedPoint<I, F> rhs,
201 typename std::enable_if<type_from_size<I + F>::next_size::is_specialized>::type* = nullptr) { 196 std::enable_if_t<type_from_size<I + F>::next_size::is_specialized>* = nullptr) {
202 197
203 using next_type = typename FixedPoint<I, F>::next_type; 198 using next_type = typename FixedPoint<I, F>::next_type;
204 using base_type = typename FixedPoint<I, F>::base_type; 199 using base_type = typename FixedPoint<I, F>::base_type;
@@ -215,9 +210,9 @@ CONSTEXPR14 FixedPoint<I, F> multiply(
215// it is slightly slower, but is more robust since it doesn't 210// it is slightly slower, but is more robust since it doesn't
216// require and upgraded type 211// require and upgraded type
217template <size_t I, size_t F> 212template <size_t I, size_t F>
218CONSTEXPR14 FixedPoint<I, F> multiply( 213constexpr FixedPoint<I, F> multiply(
219 FixedPoint<I, F> lhs, FixedPoint<I, F> rhs, 214 FixedPoint<I, F> lhs, FixedPoint<I, F> rhs,
220 typename std::enable_if<!type_from_size<I + F>::next_size::is_specialized>::type* = nullptr) { 215 std::enable_if_t<!type_from_size<I + F>::next_size::is_specialized>* = nullptr) {
221 216
222 using base_type = typename FixedPoint<I, F>::base_type; 217 using base_type = typename FixedPoint<I, F>::base_type;
223 218
@@ -272,19 +267,20 @@ public:
272 static constexpr base_type one = base_type(1) << fractional_bits; 267 static constexpr base_type one = base_type(1) << fractional_bits;
273 268
274public: // constructors 269public: // constructors
275 FixedPoint() = default; 270 constexpr FixedPoint() = default;
276 FixedPoint(const FixedPoint&) = default; 271
277 FixedPoint(FixedPoint&&) = default; 272 constexpr FixedPoint(const FixedPoint&) = default;
278 FixedPoint& operator=(const FixedPoint&) = default; 273 constexpr FixedPoint& operator=(const FixedPoint&) = default;
274
275 constexpr FixedPoint(FixedPoint&&) noexcept = default;
276 constexpr FixedPoint& operator=(FixedPoint&&) noexcept = default;
279 277
280 template <class Number> 278 template <IsArithmetic Number>
281 constexpr FixedPoint( 279 constexpr FixedPoint(Number n) : data_(static_cast<base_type>(n * one)) {}
282 Number n, typename std::enable_if<std::is_arithmetic<Number>::value>::type* = nullptr)
283 : data_(static_cast<base_type>(n * one)) {}
284 280
285public: // conversion 281public: // conversion
286 template <size_t I2, size_t F2> 282 template <size_t I2, size_t F2>
287 CONSTEXPR14 explicit FixedPoint(FixedPoint<I2, F2> other) { 283 constexpr explicit FixedPoint(FixedPoint<I2, F2> other) {
288 static_assert(I2 <= I && F2 <= F, "Scaling conversion can only upgrade types"); 284 static_assert(I2 <= I && F2 <= F, "Scaling conversion can only upgrade types");
289 using T = FixedPoint<I2, F2>; 285 using T = FixedPoint<I2, F2>;
290 286
@@ -308,36 +304,14 @@ public:
308 } 304 }
309 305
310public: // comparison operators 306public: // comparison operators
311 constexpr bool operator==(FixedPoint rhs) const { 307 friend constexpr auto operator<=>(FixedPoint lhs, FixedPoint rhs) = default;
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 308
335public: // unary operators 309public: // unary operators
336 constexpr bool operator!() const { 310 [[nodiscard]] constexpr bool operator!() const {
337 return !data_; 311 return !data_;
338 } 312 }
339 313
340 constexpr FixedPoint operator~() const { 314 [[nodiscard]] constexpr FixedPoint operator~() const {
341 // NOTE(eteran): this will often appear to "just negate" the value 315 // NOTE(eteran): this will often appear to "just negate" the value
342 // that is not an error, it is because -x == (~x+1) 316 // that is not an error, it is because -x == (~x+1)
343 // and that "+1" is adding an infinitesimally small fraction to the 317 // and that "+1" is adding an infinitesimally small fraction to the
@@ -345,89 +319,87 @@ public: // unary operators
345 return FixedPoint::from_base(~data_); 319 return FixedPoint::from_base(~data_);
346 } 320 }
347 321
348 constexpr FixedPoint operator-() const { 322 [[nodiscard]] constexpr FixedPoint operator-() const {
349 return FixedPoint::from_base(-data_); 323 return FixedPoint::from_base(-data_);
350 } 324 }
351 325
352 constexpr FixedPoint operator+() const { 326 [[nodiscard]] constexpr FixedPoint operator+() const {
353 return FixedPoint::from_base(+data_); 327 return FixedPoint::from_base(+data_);
354 } 328 }
355 329
356 CONSTEXPR14 FixedPoint& operator++() { 330 constexpr FixedPoint& operator++() {
357 data_ += one; 331 data_ += one;
358 return *this; 332 return *this;
359 } 333 }
360 334
361 CONSTEXPR14 FixedPoint& operator--() { 335 constexpr FixedPoint& operator--() {
362 data_ -= one; 336 data_ -= one;
363 return *this; 337 return *this;
364 } 338 }
365 339
366 CONSTEXPR14 FixedPoint operator++(int) { 340 constexpr FixedPoint operator++(int) {
367 FixedPoint tmp(*this); 341 FixedPoint tmp(*this);
368 data_ += one; 342 data_ += one;
369 return tmp; 343 return tmp;
370 } 344 }
371 345
372 CONSTEXPR14 FixedPoint operator--(int) { 346 constexpr FixedPoint operator--(int) {
373 FixedPoint tmp(*this); 347 FixedPoint tmp(*this);
374 data_ -= one; 348 data_ -= one;
375 return tmp; 349 return tmp;
376 } 350 }
377 351
378public: // basic math operators 352public: // basic math operators
379 CONSTEXPR14 FixedPoint& operator+=(FixedPoint n) { 353 constexpr FixedPoint& operator+=(FixedPoint n) {
380 data_ += n.data_; 354 data_ += n.data_;
381 return *this; 355 return *this;
382 } 356 }
383 357
384 CONSTEXPR14 FixedPoint& operator-=(FixedPoint n) { 358 constexpr FixedPoint& operator-=(FixedPoint n) {
385 data_ -= n.data_; 359 data_ -= n.data_;
386 return *this; 360 return *this;
387 } 361 }
388 362
389 CONSTEXPR14 FixedPoint& operator*=(FixedPoint n) { 363 constexpr FixedPoint& operator*=(FixedPoint n) {
390 return assign(detail::multiply(*this, n)); 364 return assign(detail::multiply(*this, n));
391 } 365 }
392 366
393 CONSTEXPR14 FixedPoint& operator/=(FixedPoint n) { 367 constexpr FixedPoint& operator/=(FixedPoint n) {
394 FixedPoint temp; 368 FixedPoint temp;
395 return assign(detail::divide(*this, n, temp)); 369 return assign(detail::divide(*this, n, temp));
396 } 370 }
397 371
398private: 372private:
399 CONSTEXPR14 FixedPoint& assign(FixedPoint rhs) { 373 constexpr FixedPoint& assign(FixedPoint rhs) {
400 data_ = rhs.data_; 374 data_ = rhs.data_;
401 return *this; 375 return *this;
402 } 376 }
403 377
404public: // binary math operators, effects underlying bit pattern since these 378public: // binary math operators, effects underlying bit pattern since these
405 // don't really typically make sense for non-integer values 379 // don't really typically make sense for non-integer values
406 CONSTEXPR14 FixedPoint& operator&=(FixedPoint n) { 380 constexpr FixedPoint& operator&=(FixedPoint n) {
407 data_ &= n.data_; 381 data_ &= n.data_;
408 return *this; 382 return *this;
409 } 383 }
410 384
411 CONSTEXPR14 FixedPoint& operator|=(FixedPoint n) { 385 constexpr FixedPoint& operator|=(FixedPoint n) {
412 data_ |= n.data_; 386 data_ |= n.data_;
413 return *this; 387 return *this;
414 } 388 }
415 389
416 CONSTEXPR14 FixedPoint& operator^=(FixedPoint n) { 390 constexpr FixedPoint& operator^=(FixedPoint n) {
417 data_ ^= n.data_; 391 data_ ^= n.data_;
418 return *this; 392 return *this;
419 } 393 }
420 394
421 template <class Integer, 395 template <IsIntegral Integer>
422 class = typename std::enable_if<std::is_integral<Integer>::value>::type> 396 constexpr FixedPoint& operator>>=(Integer n) {
423 CONSTEXPR14 FixedPoint& operator>>=(Integer n) {
424 data_ >>= n; 397 data_ >>= n;
425 return *this; 398 return *this;
426 } 399 }
427 400
428 template <class Integer, 401 template <IsIntegral Integer>
429 class = typename std::enable_if<std::is_integral<Integer>::value>::type> 402 constexpr FixedPoint& operator<<=(Integer n) {
430 CONSTEXPR14 FixedPoint& operator<<=(Integer n) {
431 data_ <<= n; 403 data_ <<= n;
432 return *this; 404 return *this;
433 } 405 }
@@ -437,42 +409,42 @@ public: // conversion to basic types
437 data_ += (data_ & fractional_mask) >> 1; 409 data_ += (data_ & fractional_mask) >> 1;
438 } 410 }
439 411
440 constexpr int to_int() { 412 [[nodiscard]] constexpr int to_int() {
441 round_up(); 413 round_up();
442 return static_cast<int>((data_ & integer_mask) >> fractional_bits); 414 return static_cast<int>((data_ & integer_mask) >> fractional_bits);
443 } 415 }
444 416
445 constexpr unsigned int to_uint() const { 417 [[nodiscard]] constexpr unsigned int to_uint() {
446 round_up(); 418 round_up();
447 return static_cast<unsigned int>((data_ & integer_mask) >> fractional_bits); 419 return static_cast<unsigned int>((data_ & integer_mask) >> fractional_bits);
448 } 420 }
449 421
450 constexpr int64_t to_long() { 422 [[nodiscard]] constexpr int64_t to_long() {
451 round_up(); 423 round_up();
452 return static_cast<int64_t>((data_ & integer_mask) >> fractional_bits); 424 return static_cast<int64_t>((data_ & integer_mask) >> fractional_bits);
453 } 425 }
454 426
455 constexpr int to_int_floor() const { 427 [[nodiscard]] constexpr int to_int_floor() const {
456 return static_cast<int>((data_ & integer_mask) >> fractional_bits); 428 return static_cast<int>((data_ & integer_mask) >> fractional_bits);
457 } 429 }
458 430
459 constexpr int64_t to_long_floor() { 431 [[nodiscard]] constexpr int64_t to_long_floor() const {
460 return static_cast<int64_t>((data_ & integer_mask) >> fractional_bits); 432 return static_cast<int64_t>((data_ & integer_mask) >> fractional_bits);
461 } 433 }
462 434
463 constexpr unsigned int to_uint_floor() const { 435 [[nodiscard]] constexpr unsigned int to_uint_floor() const {
464 return static_cast<unsigned int>((data_ & integer_mask) >> fractional_bits); 436 return static_cast<unsigned int>((data_ & integer_mask) >> fractional_bits);
465 } 437 }
466 438
467 constexpr float to_float() const { 439 [[nodiscard]] constexpr float to_float() const {
468 return static_cast<float>(data_) / FixedPoint::one; 440 return static_cast<float>(data_) / FixedPoint::one;
469 } 441 }
470 442
471 constexpr double to_double() const { 443 [[nodiscard]] constexpr double to_double() const {
472 return static_cast<double>(data_) / FixedPoint::one; 444 return static_cast<double>(data_) / FixedPoint::one;
473 } 445 }
474 446
475 constexpr base_type to_raw() const { 447 [[nodiscard]] constexpr base_type to_raw() const {
476 return data_; 448 return data_;
477 } 449 }
478 450
@@ -480,27 +452,27 @@ public: // conversion to basic types
480 data_ &= fractional_mask; 452 data_ &= fractional_mask;
481 } 453 }
482 454
483 constexpr base_type get_frac() const { 455 [[nodiscard]] constexpr base_type get_frac() const {
484 return data_ & fractional_mask; 456 return data_ & fractional_mask;
485 } 457 }
486 458
487public: 459public:
488 CONSTEXPR14 void swap(FixedPoint& rhs) { 460 constexpr void swap(FixedPoint& rhs) noexcept {
489 using std::swap; 461 using std::swap;
490 swap(data_, rhs.data_); 462 swap(data_, rhs.data_);
491 } 463 }
492 464
493public: 465public:
494 base_type data_; 466 base_type data_{};
495}; 467};
496 468
497// if we have the same fractional portion, but differing integer portions, we trivially upgrade the 469// if we have the same fractional portion, but differing integer portions, we trivially upgrade the
498// smaller type 470// smaller type
499template <size_t I1, size_t I2, size_t F> 471template <size_t I1, size_t I2, size_t F>
500CONSTEXPR14 typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>::type 472constexpr std::conditional_t<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>> operator+(
501operator+(FixedPoint<I1, F> lhs, FixedPoint<I2, F> rhs) { 473 FixedPoint<I1, F> lhs, FixedPoint<I2, F> rhs) {
502 474
503 using T = typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>::type; 475 using T = std::conditional_t<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>;
504 476
505 const T l = T::from_base(lhs.to_raw()); 477 const T l = T::from_base(lhs.to_raw());
506 const T r = T::from_base(rhs.to_raw()); 478 const T r = T::from_base(rhs.to_raw());
@@ -508,10 +480,10 @@ operator+(FixedPoint<I1, F> lhs, FixedPoint<I2, F> rhs) {
508} 480}
509 481
510template <size_t I1, size_t I2, size_t F> 482template <size_t I1, size_t I2, size_t F>
511CONSTEXPR14 typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>::type 483constexpr std::conditional_t<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>> operator-(
512operator-(FixedPoint<I1, F> lhs, FixedPoint<I2, F> rhs) { 484 FixedPoint<I1, F> lhs, FixedPoint<I2, F> rhs) {
513 485
514 using T = typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>::type; 486 using T = std::conditional_t<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>;
515 487
516 const T l = T::from_base(lhs.to_raw()); 488 const T l = T::from_base(lhs.to_raw());
517 const T r = T::from_base(rhs.to_raw()); 489 const T r = T::from_base(rhs.to_raw());
@@ -519,10 +491,10 @@ operator-(FixedPoint<I1, F> lhs, FixedPoint<I2, F> rhs) {
519} 491}
520 492
521template <size_t I1, size_t I2, size_t F> 493template <size_t I1, size_t I2, size_t F>
522CONSTEXPR14 typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>::type 494constexpr std::conditional_t<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>> operator*(
523operator*(FixedPoint<I1, F> lhs, FixedPoint<I2, F> rhs) { 495 FixedPoint<I1, F> lhs, FixedPoint<I2, F> rhs) {
524 496
525 using T = typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>::type; 497 using T = std::conditional_t<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>;
526 498
527 const T l = T::from_base(lhs.to_raw()); 499 const T l = T::from_base(lhs.to_raw());
528 const T r = T::from_base(rhs.to_raw()); 500 const T r = T::from_base(rhs.to_raw());
@@ -530,10 +502,10 @@ operator*(FixedPoint<I1, F> lhs, FixedPoint<I2, F> rhs) {
530} 502}
531 503
532template <size_t I1, size_t I2, size_t F> 504template <size_t I1, size_t I2, size_t F>
533CONSTEXPR14 typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>::type 505constexpr std::conditional_t<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>> operator/(
534operator/(FixedPoint<I1, F> lhs, FixedPoint<I2, F> rhs) { 506 FixedPoint<I1, F> lhs, FixedPoint<I2, F> rhs) {
535 507
536 using T = typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>::type; 508 using T = std::conditional_t<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>;
537 509
538 const T l = T::from_base(lhs.to_raw()); 510 const T l = T::from_base(lhs.to_raw());
539 const T r = T::from_base(rhs.to_raw()); 511 const T r = T::from_base(rhs.to_raw());
@@ -548,159 +520,133 @@ std::ostream& operator<<(std::ostream& os, FixedPoint<I, F> f) {
548 520
549// basic math operators 521// basic math operators
550template <size_t I, size_t F> 522template <size_t I, size_t F>
551CONSTEXPR14 FixedPoint<I, F> operator+(FixedPoint<I, F> lhs, FixedPoint<I, F> rhs) { 523constexpr FixedPoint<I, F> operator+(FixedPoint<I, F> lhs, FixedPoint<I, F> rhs) {
552 lhs += rhs; 524 lhs += rhs;
553 return lhs; 525 return lhs;
554} 526}
555template <size_t I, size_t F> 527template <size_t I, size_t F>
556CONSTEXPR14 FixedPoint<I, F> operator-(FixedPoint<I, F> lhs, FixedPoint<I, F> rhs) { 528constexpr FixedPoint<I, F> operator-(FixedPoint<I, F> lhs, FixedPoint<I, F> rhs) {
557 lhs -= rhs; 529 lhs -= rhs;
558 return lhs; 530 return lhs;
559} 531}
560template <size_t I, size_t F> 532template <size_t I, size_t F>
561CONSTEXPR14 FixedPoint<I, F> operator*(FixedPoint<I, F> lhs, FixedPoint<I, F> rhs) { 533constexpr FixedPoint<I, F> operator*(FixedPoint<I, F> lhs, FixedPoint<I, F> rhs) {
562 lhs *= rhs; 534 lhs *= rhs;
563 return lhs; 535 return lhs;
564} 536}
565template <size_t I, size_t F> 537template <size_t I, size_t F>
566CONSTEXPR14 FixedPoint<I, F> operator/(FixedPoint<I, F> lhs, FixedPoint<I, F> rhs) { 538constexpr FixedPoint<I, F> operator/(FixedPoint<I, F> lhs, FixedPoint<I, F> rhs) {
567 lhs /= rhs; 539 lhs /= rhs;
568 return lhs; 540 return lhs;
569} 541}
570 542
571template <size_t I, size_t F, class Number, 543template <size_t I, size_t F, IsArithmetic Number>
572 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type> 544constexpr FixedPoint<I, F> operator+(FixedPoint<I, F> lhs, Number rhs) {
573CONSTEXPR14 FixedPoint<I, F> operator+(FixedPoint<I, F> lhs, Number rhs) {
574 lhs += FixedPoint<I, F>(rhs); 545 lhs += FixedPoint<I, F>(rhs);
575 return lhs; 546 return lhs;
576} 547}
577template <size_t I, size_t F, class Number, 548template <size_t I, size_t F, IsArithmetic Number>
578 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type> 549constexpr FixedPoint<I, F> operator-(FixedPoint<I, F> lhs, Number rhs) {
579CONSTEXPR14 FixedPoint<I, F> operator-(FixedPoint<I, F> lhs, Number rhs) {
580 lhs -= FixedPoint<I, F>(rhs); 550 lhs -= FixedPoint<I, F>(rhs);
581 return lhs; 551 return lhs;
582} 552}
583template <size_t I, size_t F, class Number, 553template <size_t I, size_t F, IsArithmetic Number>
584 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type> 554constexpr FixedPoint<I, F> operator*(FixedPoint<I, F> lhs, Number rhs) {
585CONSTEXPR14 FixedPoint<I, F> operator*(FixedPoint<I, F> lhs, Number rhs) {
586 lhs *= FixedPoint<I, F>(rhs); 555 lhs *= FixedPoint<I, F>(rhs);
587 return lhs; 556 return lhs;
588} 557}
589template <size_t I, size_t F, class Number, 558template <size_t I, size_t F, IsArithmetic Number>
590 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type> 559constexpr FixedPoint<I, F> operator/(FixedPoint<I, F> lhs, Number rhs) {
591CONSTEXPR14 FixedPoint<I, F> operator/(FixedPoint<I, F> lhs, Number rhs) {
592 lhs /= FixedPoint<I, F>(rhs); 560 lhs /= FixedPoint<I, F>(rhs);
593 return lhs; 561 return lhs;
594} 562}
595 563
596template <size_t I, size_t F, class Number, 564template <size_t I, size_t F, IsArithmetic Number>
597 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type> 565constexpr FixedPoint<I, F> operator+(Number lhs, FixedPoint<I, F> rhs) {
598CONSTEXPR14 FixedPoint<I, F> operator+(Number lhs, FixedPoint<I, F> rhs) {
599 FixedPoint<I, F> tmp(lhs); 566 FixedPoint<I, F> tmp(lhs);
600 tmp += rhs; 567 tmp += rhs;
601 return tmp; 568 return tmp;
602} 569}
603template <size_t I, size_t F, class Number, 570template <size_t I, size_t F, IsArithmetic Number>
604 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type> 571constexpr FixedPoint<I, F> operator-(Number lhs, FixedPoint<I, F> rhs) {
605CONSTEXPR14 FixedPoint<I, F> operator-(Number lhs, FixedPoint<I, F> rhs) {
606 FixedPoint<I, F> tmp(lhs); 572 FixedPoint<I, F> tmp(lhs);
607 tmp -= rhs; 573 tmp -= rhs;
608 return tmp; 574 return tmp;
609} 575}
610template <size_t I, size_t F, class Number, 576template <size_t I, size_t F, IsArithmetic Number>
611 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type> 577constexpr FixedPoint<I, F> operator*(Number lhs, FixedPoint<I, F> rhs) {
612CONSTEXPR14 FixedPoint<I, F> operator*(Number lhs, FixedPoint<I, F> rhs) {
613 FixedPoint<I, F> tmp(lhs); 578 FixedPoint<I, F> tmp(lhs);
614 tmp *= rhs; 579 tmp *= rhs;
615 return tmp; 580 return tmp;
616} 581}
617template <size_t I, size_t F, class Number, 582template <size_t I, size_t F, IsArithmetic Number>
618 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type> 583constexpr FixedPoint<I, F> operator/(Number lhs, FixedPoint<I, F> rhs) {
619CONSTEXPR14 FixedPoint<I, F> operator/(Number lhs, FixedPoint<I, F> rhs) {
620 FixedPoint<I, F> tmp(lhs); 584 FixedPoint<I, F> tmp(lhs);
621 tmp /= rhs; 585 tmp /= rhs;
622 return tmp; 586 return tmp;
623} 587}
624 588
625// shift operators 589// shift operators
626template <size_t I, size_t F, class Integer, 590template <size_t I, size_t F, IsIntegral Integer>
627 class = typename std::enable_if<std::is_integral<Integer>::value>::type> 591constexpr FixedPoint<I, F> operator<<(FixedPoint<I, F> lhs, Integer rhs) {
628CONSTEXPR14 FixedPoint<I, F> operator<<(FixedPoint<I, F> lhs, Integer rhs) {
629 lhs <<= rhs; 592 lhs <<= rhs;
630 return lhs; 593 return lhs;
631} 594}
632template <size_t I, size_t F, class Integer, 595template <size_t I, size_t F, IsIntegral Integer>
633 class = typename std::enable_if<std::is_integral<Integer>::value>::type> 596constexpr FixedPoint<I, F> operator>>(FixedPoint<I, F> lhs, Integer rhs) {
634CONSTEXPR14 FixedPoint<I, F> operator>>(FixedPoint<I, F> lhs, Integer rhs) {
635 lhs >>= rhs; 597 lhs >>= rhs;
636 return lhs; 598 return lhs;
637} 599}
638 600
639// comparison operators 601// comparison operators
640template <size_t I, size_t F, class Number, 602template <size_t I, size_t F, IsArithmetic Number>
641 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
642constexpr bool operator>(FixedPoint<I, F> lhs, Number rhs) { 603constexpr bool operator>(FixedPoint<I, F> lhs, Number rhs) {
643 return lhs > FixedPoint<I, F>(rhs); 604 return lhs > FixedPoint<I, F>(rhs);
644} 605}
645template <size_t I, size_t F, class Number, 606template <size_t I, size_t F, IsArithmetic Number>
646 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
647constexpr bool operator<(FixedPoint<I, F> lhs, Number rhs) { 607constexpr bool operator<(FixedPoint<I, F> lhs, Number rhs) {
648 return lhs < FixedPoint<I, F>(rhs); 608 return lhs < FixedPoint<I, F>(rhs);
649} 609}
650template <size_t I, size_t F, class Number, 610template <size_t I, size_t F, IsArithmetic Number>
651 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
652constexpr bool operator>=(FixedPoint<I, F> lhs, Number rhs) { 611constexpr bool operator>=(FixedPoint<I, F> lhs, Number rhs) {
653 return lhs >= FixedPoint<I, F>(rhs); 612 return lhs >= FixedPoint<I, F>(rhs);
654} 613}
655template <size_t I, size_t F, class Number, 614template <size_t I, size_t F, IsArithmetic Number>
656 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
657constexpr bool operator<=(FixedPoint<I, F> lhs, Number rhs) { 615constexpr bool operator<=(FixedPoint<I, F> lhs, Number rhs) {
658 return lhs <= FixedPoint<I, F>(rhs); 616 return lhs <= FixedPoint<I, F>(rhs);
659} 617}
660template <size_t I, size_t F, class Number, 618template <size_t I, size_t F, IsArithmetic Number>
661 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
662constexpr bool operator==(FixedPoint<I, F> lhs, Number rhs) { 619constexpr bool operator==(FixedPoint<I, F> lhs, Number rhs) {
663 return lhs == FixedPoint<I, F>(rhs); 620 return lhs == FixedPoint<I, F>(rhs);
664} 621}
665template <size_t I, size_t F, class Number, 622template <size_t I, size_t F, IsArithmetic Number>
666 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
667constexpr bool operator!=(FixedPoint<I, F> lhs, Number rhs) { 623constexpr bool operator!=(FixedPoint<I, F> lhs, Number rhs) {
668 return lhs != FixedPoint<I, F>(rhs); 624 return lhs != FixedPoint<I, F>(rhs);
669} 625}
670 626
671template <size_t I, size_t F, class Number, 627template <size_t I, size_t F, IsArithmetic Number>
672 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
673constexpr bool operator>(Number lhs, FixedPoint<I, F> rhs) { 628constexpr bool operator>(Number lhs, FixedPoint<I, F> rhs) {
674 return FixedPoint<I, F>(lhs) > rhs; 629 return FixedPoint<I, F>(lhs) > rhs;
675} 630}
676template <size_t I, size_t F, class Number, 631template <size_t I, size_t F, IsArithmetic Number>
677 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
678constexpr bool operator<(Number lhs, FixedPoint<I, F> rhs) { 632constexpr bool operator<(Number lhs, FixedPoint<I, F> rhs) {
679 return FixedPoint<I, F>(lhs) < rhs; 633 return FixedPoint<I, F>(lhs) < rhs;
680} 634}
681template <size_t I, size_t F, class Number, 635template <size_t I, size_t F, IsArithmetic Number>
682 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
683constexpr bool operator>=(Number lhs, FixedPoint<I, F> rhs) { 636constexpr bool operator>=(Number lhs, FixedPoint<I, F> rhs) {
684 return FixedPoint<I, F>(lhs) >= rhs; 637 return FixedPoint<I, F>(lhs) >= rhs;
685} 638}
686template <size_t I, size_t F, class Number, 639template <size_t I, size_t F, IsArithmetic Number>
687 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
688constexpr bool operator<=(Number lhs, FixedPoint<I, F> rhs) { 640constexpr bool operator<=(Number lhs, FixedPoint<I, F> rhs) {
689 return FixedPoint<I, F>(lhs) <= rhs; 641 return FixedPoint<I, F>(lhs) <= rhs;
690} 642}
691template <size_t I, size_t F, class Number, 643template <size_t I, size_t F, IsArithmetic Number>
692 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
693constexpr bool operator==(Number lhs, FixedPoint<I, F> rhs) { 644constexpr bool operator==(Number lhs, FixedPoint<I, F> rhs) {
694 return FixedPoint<I, F>(lhs) == rhs; 645 return FixedPoint<I, F>(lhs) == rhs;
695} 646}
696template <size_t I, size_t F, class Number, 647template <size_t I, size_t F, IsArithmetic Number>
697 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
698constexpr bool operator!=(Number lhs, FixedPoint<I, F> rhs) { 648constexpr bool operator!=(Number lhs, FixedPoint<I, F> rhs) {
699 return FixedPoint<I, F>(lhs) != rhs; 649 return FixedPoint<I, F>(lhs) != rhs;
700} 650}
701 651
702} // namespace Common 652} // namespace Common
703
704#undef CONSTEXPR14
705
706#endif
diff --git a/src/common/hash.h b/src/common/hash.h
index b6f3e6d6f..e8fe78b07 100644
--- a/src/common/hash.h
+++ b/src/common/hash.h
@@ -18,4 +18,11 @@ struct PairHash {
18 } 18 }
19}; 19};
20 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
21} // namespace Common 28} // namespace Common
diff --git a/src/common/input.h b/src/common/input.h
index 825b0d650..b533f3844 100644
--- a/src/common/input.h
+++ b/src/common/input.h
@@ -76,6 +76,19 @@ enum class PollingError {
76 Unknown, 76 Unknown,
77}; 77};
78 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
79// Ir camera reply from the controller 92// Ir camera reply from the controller
80enum class CameraError { 93enum class CameraError {
81 None, 94 None,
@@ -202,6 +215,11 @@ struct CameraStatus {
202 std::vector<u8> data{}; 215 std::vector<u8> data{};
203}; 216};
204 217
218struct NfcStatus {
219 NfcState state{};
220 std::vector<u8> data{};
221};
222
205// 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
206enum class ButtonNames { 224enum class ButtonNames {
207 Undefined, 225 Undefined,
@@ -259,7 +277,9 @@ struct CallbackStatus {
259 BodyColorStatus color_status{}; 277 BodyColorStatus color_status{};
260 BatteryStatus battery_status{}; 278 BatteryStatus battery_status{};
261 VibrationStatus vibration_status{}; 279 VibrationStatus vibration_status{};
262 CameraStatus camera_status{}; 280 CameraFormat camera_status{CameraFormat::None};
281 NfcState nfc_status{NfcState::Unknown};
282 std::vector<u8> raw_data{};
263}; 283};
264 284
265// Triggered once every input change 285// Triggered once every input change
@@ -312,6 +332,14 @@ public:
312 virtual CameraError SetCameraFormat([[maybe_unused]] CameraFormat camera_format) { 332 virtual CameraError SetCameraFormat([[maybe_unused]] CameraFormat camera_format) {
313 return CameraError::NotSupported; 333 return CameraError::NotSupported;
314 } 334 }
335
336 virtual NfcState SupportsNfc() const {
337 return NfcState::NotSupported;
338 }
339
340 virtual NfcState WriteNfcData([[maybe_unused]] const std::vector<u8>& data) {
341 return NfcState::NotSupported;
342 }
315}; 343};
316 344
317/// An abstract class template for a factory that can create input devices. 345/// An abstract class template for a factory that can create input devices.
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp
index 8ce1c2fd1..15d92505e 100644
--- a/src/common/logging/backend.cpp
+++ b/src/common/logging/backend.cpp
@@ -219,7 +219,7 @@ private:
219 219
220 void StartBackendThread() { 220 void StartBackendThread() {
221 backend_thread = std::jthread([this](std::stop_token stop_token) { 221 backend_thread = std::jthread([this](std::stop_token stop_token) {
222 Common::SetCurrentThreadName("yuzu:Log"); 222 Common::SetCurrentThreadName("Logger");
223 Entry entry; 223 Entry entry;
224 const auto write_logs = [this, &entry]() { 224 const auto write_logs = [this, &entry]() {
225 ForEachBackend([&entry](Backend& backend) { backend.Write(entry); }); 225 ForEachBackend([&entry](Backend& backend) { backend.Write(entry); });
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/settings.h b/src/common/settings.h
index 13651de57..0eb98939c 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -431,7 +431,7 @@ struct Values {
431 FullscreenMode::Exclusive, 431 FullscreenMode::Exclusive,
432#endif 432#endif
433 FullscreenMode::Borderless, FullscreenMode::Exclusive, "fullscreen_mode"}; 433 FullscreenMode::Borderless, FullscreenMode::Exclusive, "fullscreen_mode"};
434 SwitchableSetting<int, true> aspect_ratio{0, 0, 3, "aspect_ratio"}; 434 SwitchableSetting<int, true> aspect_ratio{0, 0, 4, "aspect_ratio"};
435 SwitchableSetting<int, true> max_anisotropy{0, 0, 5, "max_anisotropy"}; 435 SwitchableSetting<int, true> max_anisotropy{0, 0, 5, "max_anisotropy"};
436 SwitchableSetting<bool> use_speed_limit{true, "use_speed_limit"}; 436 SwitchableSetting<bool> use_speed_limit{true, "use_speed_limit"};
437 SwitchableSetting<u16, true> speed_limit{100, 0, 9999, "speed_limit"}; 437 SwitchableSetting<u16, true> speed_limit{100, 0, 9999, "speed_limit"};
@@ -530,6 +530,8 @@ struct Values {
530 Setting<bool> use_debug_asserts{false, "use_debug_asserts"}; 530 Setting<bool> use_debug_asserts{false, "use_debug_asserts"};
531 Setting<bool> use_auto_stub{false, "use_auto_stub"}; 531 Setting<bool> use_auto_stub{false, "use_auto_stub"};
532 Setting<bool> enable_all_controllers{false, "enable_all_controllers"}; 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"};
533 535
534 // Miscellaneous 536 // Miscellaneous
535 Setting<std::string> log_filter{"*:Info", "log_filter"}; 537 Setting<std::string> log_filter{"*:Info", "log_filter"};
diff --git a/src/common/thread.h b/src/common/thread.h
index 1552f58e0..e17a7850f 100644
--- a/src/common/thread.h
+++ b/src/common/thread.h
@@ -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;