diff options
Diffstat (limited to 'src/common')
| -rw-r--r-- | src/common/CMakeLists.txt | 47 | ||||
| -rw-r--r-- | src/common/address_space.cpp | 10 | ||||
| -rw-r--r-- | src/common/address_space.h | 150 | ||||
| -rw-r--r-- | src/common/address_space.inc | 366 | ||||
| -rw-r--r-- | src/common/algorithm.h | 8 | ||||
| -rw-r--r-- | src/common/bit_field.h | 15 | ||||
| -rw-r--r-- | src/common/bounded_threadsafe_queue.h | 9 | ||||
| -rw-r--r-- | src/common/concepts.h | 8 | ||||
| -rw-r--r-- | src/common/fixed_point.h | 274 | ||||
| -rw-r--r-- | src/common/hash.h | 7 | ||||
| -rw-r--r-- | src/common/input.h | 30 | ||||
| -rw-r--r-- | src/common/logging/backend.cpp | 2 | ||||
| -rw-r--r-- | src/common/multi_level_page_table.cpp | 9 | ||||
| -rw-r--r-- | src/common/multi_level_page_table.h | 78 | ||||
| -rw-r--r-- | src/common/multi_level_page_table.inc | 84 | ||||
| -rw-r--r-- | src/common/settings.h | 4 | ||||
| -rw-r--r-- | src/common/thread.h | 4 |
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}) |
| 15 | endif () | 15 | endif () |
| 16 | 16 | ||
| 17 | # Pass the path to git to the GenerateSCMRev.cmake as well | 17 | include(GenerateSCMRev) |
| 18 | find_package(Git QUIET) | ||
| 19 | |||
| 20 | add_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 | ||
| 44 | add_library(common STATIC | 19 | add_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) | ||
| 169 | endif() | 149 | endif() |
| 170 | 150 | ||
| 171 | if (MSVC) | 151 | if (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 | ) |
| 181 | else() | 164 | else() |
| 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 | ) |
| 187 | endif() | 168 | endif() |
| @@ -189,7 +170,11 @@ endif() | |||
| 189 | create_target_directory_groups(common) | 170 | create_target_directory_groups(common) |
| 190 | 171 | ||
| 191 | target_link_libraries(common PUBLIC ${Boost_LIBRARIES} fmt::fmt microprofile Threads::Threads) | 172 | target_link_libraries(common PUBLIC ${Boost_LIBRARIES} fmt::fmt microprofile Threads::Threads) |
| 192 | target_link_libraries(common PRIVATE lz4::lz4 xbyak) | 173 | if (TARGET lz4::lz4) |
| 174 | target_link_libraries(common PRIVATE lz4::lz4) | ||
| 175 | else() | ||
| 176 | target_link_libraries(common PRIVATE LZ4::lz4_shared) | ||
| 177 | endif() | ||
| 193 | if (TARGET zstd::zstd) | 178 | if (TARGET zstd::zstd) |
| 194 | target_link_libraries(common PRIVATE zstd::zstd) | 179 | target_link_libraries(common PRIVATE zstd::zstd) |
| 195 | else() | 180 | else() |
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 | |||
| 6 | namespace Common { | ||
| 7 | |||
| 8 | template 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 | |||
| 13 | namespace Common { | ||
| 14 | template <typename VaType, size_t AddressSpaceBits> | ||
| 15 | concept AddressSpaceValid = std::is_unsigned_v<VaType> && sizeof(VaType) * 8 >= AddressSpaceBits; | ||
| 16 | |||
| 17 | struct EmptyStruct {}; | ||
| 18 | |||
| 19 | /** | ||
| 20 | * @brief FlatAddressSpaceMap provides a generic VA->PA mapping implementation using a sorted vector | ||
| 21 | */ | ||
| 22 | template <typename VaType, VaType UnmappedVa, typename PaType, PaType UnmappedPa, | ||
| 23 | bool PaContigSplit, size_t AddressSpaceBits, typename ExtraBlockInfo = EmptyStruct> | ||
| 24 | requires AddressSpaceValid<VaType, AddressSpaceBits> | ||
| 25 | class FlatAddressSpaceMap { | ||
| 26 | public: | ||
| 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 | |||
| 50 | protected: | ||
| 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 | |||
| 102 | private: | ||
| 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 | */ | ||
| 111 | template <typename VaType, VaType UnmappedVa, size_t AddressSpaceBits> | ||
| 112 | requires AddressSpaceValid<VaType, AddressSpaceBits> | ||
| 113 | class FlatAllocator | ||
| 114 | : public FlatAddressSpaceMap<VaType, UnmappedVa, bool, false, false, AddressSpaceBits> { | ||
| 115 | private: | ||
| 116 | using Base = FlatAddressSpaceMap<VaType, UnmappedVa, bool, false, false, AddressSpaceBits>; | ||
| 117 | |||
| 118 | public: | ||
| 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 | |||
| 140 | private: | ||
| 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 | |||
| 32 | namespace Common { | ||
| 33 | MAP_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 | |||
| 41 | MAP_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 | |||
| 148 | MAP_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 | |||
| 288 | ALLOC_MEMBER_CONST()::FlatAllocator(VaType virt_start_, VaType va_limit_) | ||
| 289 | : Base{va_limit_}, virt_start{virt_start_}, current_linear_alloc_end{virt_start_} {} | ||
| 290 | |||
| 291 | ALLOC_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 | |||
| 359 | ALLOC_MEMBER(void)::AllocateFixed(VaType virt, VaType size) { | ||
| 360 | this->Map(virt, true, size); | ||
| 361 | } | ||
| 362 | |||
| 363 | ALLOC_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 | ||
| 27 | template <typename T, typename Func, typename... Args> | ||
| 28 | T 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 | |||
| 21 | constexpr size_t hardware_interference_size = 64; | 21 | constexpr 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 | |||
| 29 | template <typename T, size_t capacity = 0x400> | 24 | template <typename T, size_t capacity = 0x400> |
| 30 | class MPSCQueue { | 25 | class MPSCQueue { |
| 31 | public: | 26 | public: |
| @@ -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 { | |||
| 34 | template <typename From, typename To> | 34 | template <typename From, typename To> |
| 35 | concept ConvertibleTo = std::is_convertible_v<From, To>; | 35 | concept ConvertibleTo = std::is_convertible_v<From, To>; |
| 36 | 36 | ||
| 37 | // No equivalents in the stdlib | ||
| 38 | |||
| 39 | template <typename T> | ||
| 40 | concept IsArithmetic = std::is_arithmetic_v<T>; | ||
| 41 | |||
| 42 | template <typename T> | ||
| 43 | concept 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 | |||
| 22 | namespace Common { | 17 | namespace Common { |
| 23 | 18 | ||
| 24 | template <size_t I, size_t F> | 19 | template <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) { | |||
| 106 | struct divide_by_zero : std::exception {}; | 101 | struct divide_by_zero : std::exception {}; |
| 107 | 102 | ||
| 108 | template <size_t I, size_t F> | 103 | template <size_t I, size_t F> |
| 109 | CONSTEXPR14 FixedPoint<I, F> divide( | 104 | constexpr 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 | ||
| 128 | template <size_t I, size_t F> | 123 | template <size_t I, size_t F> |
| 129 | CONSTEXPR14 FixedPoint<I, F> divide( | 124 | constexpr 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 |
| 198 | template <size_t I, size_t F> | 193 | template <size_t I, size_t F> |
| 199 | CONSTEXPR14 FixedPoint<I, F> multiply( | 194 | constexpr 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 |
| 217 | template <size_t I, size_t F> | 212 | template <size_t I, size_t F> |
| 218 | CONSTEXPR14 FixedPoint<I, F> multiply( | 213 | constexpr 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 | ||
| 274 | public: // constructors | 269 | public: // 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 | ||
| 285 | public: // conversion | 281 | public: // 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 | ||
| 310 | public: // comparison operators | 306 | public: // 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 | ||
| 335 | public: // unary operators | 309 | public: // 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 | ||
| 378 | public: // basic math operators | 352 | public: // 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 | ||
| 398 | private: | 372 | private: |
| 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 | ||
| 404 | public: // binary math operators, effects underlying bit pattern since these | 378 | public: // 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 | ||
| 487 | public: | 459 | public: |
| 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 | ||
| 493 | public: | 465 | public: |
| 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 |
| 499 | template <size_t I1, size_t I2, size_t F> | 471 | template <size_t I1, size_t I2, size_t F> |
| 500 | CONSTEXPR14 typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>::type | 472 | constexpr std::conditional_t<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>> operator+( |
| 501 | operator+(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 | ||
| 510 | template <size_t I1, size_t I2, size_t F> | 482 | template <size_t I1, size_t I2, size_t F> |
| 511 | CONSTEXPR14 typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>::type | 483 | constexpr std::conditional_t<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>> operator-( |
| 512 | operator-(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 | ||
| 521 | template <size_t I1, size_t I2, size_t F> | 493 | template <size_t I1, size_t I2, size_t F> |
| 522 | CONSTEXPR14 typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>::type | 494 | constexpr std::conditional_t<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>> operator*( |
| 523 | operator*(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 | ||
| 532 | template <size_t I1, size_t I2, size_t F> | 504 | template <size_t I1, size_t I2, size_t F> |
| 533 | CONSTEXPR14 typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>::type | 505 | constexpr std::conditional_t<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>> operator/( |
| 534 | operator/(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 |
| 550 | template <size_t I, size_t F> | 522 | template <size_t I, size_t F> |
| 551 | CONSTEXPR14 FixedPoint<I, F> operator+(FixedPoint<I, F> lhs, FixedPoint<I, F> rhs) { | 523 | constexpr 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 | } |
| 555 | template <size_t I, size_t F> | 527 | template <size_t I, size_t F> |
| 556 | CONSTEXPR14 FixedPoint<I, F> operator-(FixedPoint<I, F> lhs, FixedPoint<I, F> rhs) { | 528 | constexpr 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 | } |
| 560 | template <size_t I, size_t F> | 532 | template <size_t I, size_t F> |
| 561 | CONSTEXPR14 FixedPoint<I, F> operator*(FixedPoint<I, F> lhs, FixedPoint<I, F> rhs) { | 533 | constexpr 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 | } |
| 565 | template <size_t I, size_t F> | 537 | template <size_t I, size_t F> |
| 566 | CONSTEXPR14 FixedPoint<I, F> operator/(FixedPoint<I, F> lhs, FixedPoint<I, F> rhs) { | 538 | constexpr 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 | ||
| 571 | template <size_t I, size_t F, class Number, | 543 | template <size_t I, size_t F, IsArithmetic Number> |
| 572 | class = typename std::enable_if<std::is_arithmetic<Number>::value>::type> | 544 | constexpr FixedPoint<I, F> operator+(FixedPoint<I, F> lhs, Number rhs) { |
| 573 | CONSTEXPR14 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 | } |
| 577 | template <size_t I, size_t F, class Number, | 548 | template <size_t I, size_t F, IsArithmetic Number> |
| 578 | class = typename std::enable_if<std::is_arithmetic<Number>::value>::type> | 549 | constexpr FixedPoint<I, F> operator-(FixedPoint<I, F> lhs, Number rhs) { |
| 579 | CONSTEXPR14 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 | } |
| 583 | template <size_t I, size_t F, class Number, | 553 | template <size_t I, size_t F, IsArithmetic Number> |
| 584 | class = typename std::enable_if<std::is_arithmetic<Number>::value>::type> | 554 | constexpr FixedPoint<I, F> operator*(FixedPoint<I, F> lhs, Number rhs) { |
| 585 | CONSTEXPR14 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 | } |
| 589 | template <size_t I, size_t F, class Number, | 558 | template <size_t I, size_t F, IsArithmetic Number> |
| 590 | class = typename std::enable_if<std::is_arithmetic<Number>::value>::type> | 559 | constexpr FixedPoint<I, F> operator/(FixedPoint<I, F> lhs, Number rhs) { |
| 591 | CONSTEXPR14 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 | ||
| 596 | template <size_t I, size_t F, class Number, | 564 | template <size_t I, size_t F, IsArithmetic Number> |
| 597 | class = typename std::enable_if<std::is_arithmetic<Number>::value>::type> | 565 | constexpr FixedPoint<I, F> operator+(Number lhs, FixedPoint<I, F> rhs) { |
| 598 | CONSTEXPR14 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 | } |
| 603 | template <size_t I, size_t F, class Number, | 570 | template <size_t I, size_t F, IsArithmetic Number> |
| 604 | class = typename std::enable_if<std::is_arithmetic<Number>::value>::type> | 571 | constexpr FixedPoint<I, F> operator-(Number lhs, FixedPoint<I, F> rhs) { |
| 605 | CONSTEXPR14 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 | } |
| 610 | template <size_t I, size_t F, class Number, | 576 | template <size_t I, size_t F, IsArithmetic Number> |
| 611 | class = typename std::enable_if<std::is_arithmetic<Number>::value>::type> | 577 | constexpr FixedPoint<I, F> operator*(Number lhs, FixedPoint<I, F> rhs) { |
| 612 | CONSTEXPR14 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 | } |
| 617 | template <size_t I, size_t F, class Number, | 582 | template <size_t I, size_t F, IsArithmetic Number> |
| 618 | class = typename std::enable_if<std::is_arithmetic<Number>::value>::type> | 583 | constexpr FixedPoint<I, F> operator/(Number lhs, FixedPoint<I, F> rhs) { |
| 619 | CONSTEXPR14 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 |
| 626 | template <size_t I, size_t F, class Integer, | 590 | template <size_t I, size_t F, IsIntegral Integer> |
| 627 | class = typename std::enable_if<std::is_integral<Integer>::value>::type> | 591 | constexpr FixedPoint<I, F> operator<<(FixedPoint<I, F> lhs, Integer rhs) { |
| 628 | CONSTEXPR14 FixedPoint<I, F> operator<<(FixedPoint<I, F> lhs, Integer rhs) { | ||
| 629 | lhs <<= rhs; | 592 | lhs <<= rhs; |
| 630 | return lhs; | 593 | return lhs; |
| 631 | } | 594 | } |
| 632 | template <size_t I, size_t F, class Integer, | 595 | template <size_t I, size_t F, IsIntegral Integer> |
| 633 | class = typename std::enable_if<std::is_integral<Integer>::value>::type> | 596 | constexpr FixedPoint<I, F> operator>>(FixedPoint<I, F> lhs, Integer rhs) { |
| 634 | CONSTEXPR14 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 |
| 640 | template <size_t I, size_t F, class Number, | 602 | template <size_t I, size_t F, IsArithmetic Number> |
| 641 | class = typename std::enable_if<std::is_arithmetic<Number>::value>::type> | ||
| 642 | constexpr bool operator>(FixedPoint<I, F> lhs, Number rhs) { | 603 | constexpr bool operator>(FixedPoint<I, F> lhs, Number rhs) { |
| 643 | return lhs > FixedPoint<I, F>(rhs); | 604 | return lhs > FixedPoint<I, F>(rhs); |
| 644 | } | 605 | } |
| 645 | template <size_t I, size_t F, class Number, | 606 | template <size_t I, size_t F, IsArithmetic Number> |
| 646 | class = typename std::enable_if<std::is_arithmetic<Number>::value>::type> | ||
| 647 | constexpr bool operator<(FixedPoint<I, F> lhs, Number rhs) { | 607 | constexpr bool operator<(FixedPoint<I, F> lhs, Number rhs) { |
| 648 | return lhs < FixedPoint<I, F>(rhs); | 608 | return lhs < FixedPoint<I, F>(rhs); |
| 649 | } | 609 | } |
| 650 | template <size_t I, size_t F, class Number, | 610 | template <size_t I, size_t F, IsArithmetic Number> |
| 651 | class = typename std::enable_if<std::is_arithmetic<Number>::value>::type> | ||
| 652 | constexpr bool operator>=(FixedPoint<I, F> lhs, Number rhs) { | 611 | constexpr bool operator>=(FixedPoint<I, F> lhs, Number rhs) { |
| 653 | return lhs >= FixedPoint<I, F>(rhs); | 612 | return lhs >= FixedPoint<I, F>(rhs); |
| 654 | } | 613 | } |
| 655 | template <size_t I, size_t F, class Number, | 614 | template <size_t I, size_t F, IsArithmetic Number> |
| 656 | class = typename std::enable_if<std::is_arithmetic<Number>::value>::type> | ||
| 657 | constexpr bool operator<=(FixedPoint<I, F> lhs, Number rhs) { | 615 | constexpr bool operator<=(FixedPoint<I, F> lhs, Number rhs) { |
| 658 | return lhs <= FixedPoint<I, F>(rhs); | 616 | return lhs <= FixedPoint<I, F>(rhs); |
| 659 | } | 617 | } |
| 660 | template <size_t I, size_t F, class Number, | 618 | template <size_t I, size_t F, IsArithmetic Number> |
| 661 | class = typename std::enable_if<std::is_arithmetic<Number>::value>::type> | ||
| 662 | constexpr bool operator==(FixedPoint<I, F> lhs, Number rhs) { | 619 | constexpr bool operator==(FixedPoint<I, F> lhs, Number rhs) { |
| 663 | return lhs == FixedPoint<I, F>(rhs); | 620 | return lhs == FixedPoint<I, F>(rhs); |
| 664 | } | 621 | } |
| 665 | template <size_t I, size_t F, class Number, | 622 | template <size_t I, size_t F, IsArithmetic Number> |
| 666 | class = typename std::enable_if<std::is_arithmetic<Number>::value>::type> | ||
| 667 | constexpr bool operator!=(FixedPoint<I, F> lhs, Number rhs) { | 623 | constexpr 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 | ||
| 671 | template <size_t I, size_t F, class Number, | 627 | template <size_t I, size_t F, IsArithmetic Number> |
| 672 | class = typename std::enable_if<std::is_arithmetic<Number>::value>::type> | ||
| 673 | constexpr bool operator>(Number lhs, FixedPoint<I, F> rhs) { | 628 | constexpr bool operator>(Number lhs, FixedPoint<I, F> rhs) { |
| 674 | return FixedPoint<I, F>(lhs) > rhs; | 629 | return FixedPoint<I, F>(lhs) > rhs; |
| 675 | } | 630 | } |
| 676 | template <size_t I, size_t F, class Number, | 631 | template <size_t I, size_t F, IsArithmetic Number> |
| 677 | class = typename std::enable_if<std::is_arithmetic<Number>::value>::type> | ||
| 678 | constexpr bool operator<(Number lhs, FixedPoint<I, F> rhs) { | 632 | constexpr bool operator<(Number lhs, FixedPoint<I, F> rhs) { |
| 679 | return FixedPoint<I, F>(lhs) < rhs; | 633 | return FixedPoint<I, F>(lhs) < rhs; |
| 680 | } | 634 | } |
| 681 | template <size_t I, size_t F, class Number, | 635 | template <size_t I, size_t F, IsArithmetic Number> |
| 682 | class = typename std::enable_if<std::is_arithmetic<Number>::value>::type> | ||
| 683 | constexpr bool operator>=(Number lhs, FixedPoint<I, F> rhs) { | 636 | constexpr bool operator>=(Number lhs, FixedPoint<I, F> rhs) { |
| 684 | return FixedPoint<I, F>(lhs) >= rhs; | 637 | return FixedPoint<I, F>(lhs) >= rhs; |
| 685 | } | 638 | } |
| 686 | template <size_t I, size_t F, class Number, | 639 | template <size_t I, size_t F, IsArithmetic Number> |
| 687 | class = typename std::enable_if<std::is_arithmetic<Number>::value>::type> | ||
| 688 | constexpr bool operator<=(Number lhs, FixedPoint<I, F> rhs) { | 640 | constexpr bool operator<=(Number lhs, FixedPoint<I, F> rhs) { |
| 689 | return FixedPoint<I, F>(lhs) <= rhs; | 641 | return FixedPoint<I, F>(lhs) <= rhs; |
| 690 | } | 642 | } |
| 691 | template <size_t I, size_t F, class Number, | 643 | template <size_t I, size_t F, IsArithmetic Number> |
| 692 | class = typename std::enable_if<std::is_arithmetic<Number>::value>::type> | ||
| 693 | constexpr bool operator==(Number lhs, FixedPoint<I, F> rhs) { | 644 | constexpr bool operator==(Number lhs, FixedPoint<I, F> rhs) { |
| 694 | return FixedPoint<I, F>(lhs) == rhs; | 645 | return FixedPoint<I, F>(lhs) == rhs; |
| 695 | } | 646 | } |
| 696 | template <size_t I, size_t F, class Number, | 647 | template <size_t I, size_t F, IsArithmetic Number> |
| 697 | class = typename std::enable_if<std::is_arithmetic<Number>::value>::type> | ||
| 698 | constexpr bool operator!=(Number lhs, FixedPoint<I, F> rhs) { | 648 | constexpr 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 | ||
| 21 | template <typename T> | ||
| 22 | struct 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 | ||
| 80 | enum 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 |
| 80 | enum class CameraError { | 93 | enum 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 | ||
| 218 | struct 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 |
| 206 | enum class ButtonNames { | 224 | enum 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 | |||
| 6 | namespace Common { | ||
| 7 | template class Common::MultiLevelPageTable<u64>; | ||
| 8 | template 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 | |||
| 12 | namespace Common { | ||
| 13 | |||
| 14 | template <typename BaseAddr> | ||
| 15 | class MultiLevelPageTable final { | ||
| 16 | public: | ||
| 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 | |||
| 65 | private: | ||
| 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 | |||
| 13 | namespace Common { | ||
| 14 | |||
| 15 | template <typename BaseAddr> | ||
| 16 | MultiLevelPageTable<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 | |||
| 43 | template <typename BaseAddr> | ||
| 44 | MultiLevelPageTable<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 | |||
| 55 | template <typename BaseAddr> | ||
| 56 | void 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 | |||
| 66 | template <typename BaseAddr> | ||
| 67 | void 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 | |||
| 57 | private: | 61 | private: |
| 58 | std::condition_variable condvar; | 62 | std::condition_variable condvar; |
| 59 | std::mutex mutex; | 63 | std::mutex mutex; |