diff options
Diffstat (limited to 'src/common')
127 files changed, 5496 insertions, 1503 deletions
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index adf70eb8b..a02696873 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt | |||
| @@ -1,3 +1,6 @@ | |||
| 1 | # SPDX-FileCopyrightText: 2018 yuzu Emulator Project | ||
| 2 | # SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 1 | if (DEFINED ENV{AZURECIREPO}) | 4 | if (DEFINED ENV{AZURECIREPO}) |
| 2 | set(BUILD_REPOSITORY $ENV{AZURECIREPO}) | 5 | set(BUILD_REPOSITORY $ENV{AZURECIREPO}) |
| 3 | endif() | 6 | endif() |
| @@ -11,38 +14,17 @@ if (DEFINED ENV{DISPLAYVERSION}) | |||
| 11 | set(DISPLAY_VERSION $ENV{DISPLAYVERSION}) | 14 | set(DISPLAY_VERSION $ENV{DISPLAYVERSION}) |
| 12 | endif () | 15 | endif () |
| 13 | 16 | ||
| 14 | # Pass the path to git to the GenerateSCMRev.cmake as well | 17 | include(GenerateSCMRev) |
| 15 | find_package(Git QUIET) | ||
| 16 | |||
| 17 | add_custom_command(OUTPUT scm_rev.cpp | ||
| 18 | COMMAND ${CMAKE_COMMAND} | ||
| 19 | -DSRC_DIR=${CMAKE_SOURCE_DIR} | ||
| 20 | -DBUILD_REPOSITORY=${BUILD_REPOSITORY} | ||
| 21 | -DTITLE_BAR_FORMAT_IDLE=${TITLE_BAR_FORMAT_IDLE} | ||
| 22 | -DTITLE_BAR_FORMAT_RUNNING=${TITLE_BAR_FORMAT_RUNNING} | ||
| 23 | -DBUILD_TAG=${BUILD_TAG} | ||
| 24 | -DBUILD_ID=${DISPLAY_VERSION} | ||
| 25 | -DGIT_REF_SPEC=${GIT_REF_SPEC} | ||
| 26 | -DGIT_REV=${GIT_REV} | ||
| 27 | -DGIT_DESC=${GIT_DESC} | ||
| 28 | -DGIT_BRANCH=${GIT_BRANCH} | ||
| 29 | -DBUILD_FULLNAME=${BUILD_FULLNAME} | ||
| 30 | -DGIT_EXECUTABLE=${GIT_EXECUTABLE} | ||
| 31 | -P ${CMAKE_SOURCE_DIR}/CMakeModules/GenerateSCMRev.cmake | ||
| 32 | DEPENDS | ||
| 33 | # Check that the scm_rev files haven't changed | ||
| 34 | "${CMAKE_CURRENT_SOURCE_DIR}/scm_rev.cpp.in" | ||
| 35 | "${CMAKE_CURRENT_SOURCE_DIR}/scm_rev.h" | ||
| 36 | # technically we should regenerate if the git version changed, but its not worth the effort imo | ||
| 37 | "${CMAKE_SOURCE_DIR}/CMakeModules/GenerateSCMRev.cmake" | ||
| 38 | VERBATIM | ||
| 39 | ) | ||
| 40 | 18 | ||
| 41 | add_library(common STATIC | 19 | add_library(common STATIC |
| 20 | address_space.cpp | ||
| 21 | address_space.h | ||
| 42 | algorithm.h | 22 | algorithm.h |
| 43 | alignment.h | 23 | alignment.h |
| 24 | announce_multiplayer_room.h | ||
| 44 | assert.cpp | 25 | assert.cpp |
| 45 | assert.h | 26 | assert.h |
| 27 | atomic_helpers.h | ||
| 46 | atomic_ops.h | 28 | atomic_ops.h |
| 47 | detached_tasks.cpp | 29 | detached_tasks.cpp |
| 48 | detached_tasks.h | 30 | detached_tasks.h |
| @@ -58,11 +40,13 @@ add_library(common STATIC | |||
| 58 | div_ceil.h | 40 | div_ceil.h |
| 59 | dynamic_library.cpp | 41 | dynamic_library.cpp |
| 60 | dynamic_library.h | 42 | dynamic_library.h |
| 43 | elf.h | ||
| 61 | error.cpp | 44 | error.cpp |
| 62 | error.h | 45 | error.h |
| 63 | expected.h | 46 | expected.h |
| 64 | fiber.cpp | 47 | fiber.cpp |
| 65 | fiber.h | 48 | fiber.h |
| 49 | fixed_point.h | ||
| 66 | fs/file.cpp | 50 | fs/file.cpp |
| 67 | fs/file.h | 51 | fs/file.h |
| 68 | fs/fs.cpp | 52 | fs/fs.cpp |
| @@ -99,6 +83,8 @@ add_library(common STATIC | |||
| 99 | microprofile.cpp | 83 | microprofile.cpp |
| 100 | microprofile.h | 84 | microprofile.h |
| 101 | microprofileui.h | 85 | microprofileui.h |
| 86 | multi_level_page_table.cpp | ||
| 87 | multi_level_page_table.h | ||
| 102 | nvidia_flags.cpp | 88 | nvidia_flags.cpp |
| 103 | nvidia_flags.h | 89 | nvidia_flags.h |
| 104 | page_table.cpp | 90 | page_table.cpp |
| @@ -108,14 +94,16 @@ add_library(common STATIC | |||
| 108 | parent_of_member.h | 94 | parent_of_member.h |
| 109 | point.h | 95 | point.h |
| 110 | quaternion.h | 96 | quaternion.h |
| 97 | reader_writer_queue.h | ||
| 111 | ring_buffer.h | 98 | ring_buffer.h |
| 112 | scm_rev.cpp | 99 | ${CMAKE_CURRENT_BINARY_DIR}/scm_rev.cpp |
| 113 | scm_rev.h | 100 | scm_rev.h |
| 114 | scope_exit.h | 101 | scope_exit.h |
| 115 | settings.cpp | 102 | settings.cpp |
| 116 | settings.h | 103 | settings.h |
| 117 | settings_input.cpp | 104 | settings_input.cpp |
| 118 | settings_input.h | 105 | settings_input.h |
| 106 | socket_types.h | ||
| 119 | spin_lock.cpp | 107 | spin_lock.cpp |
| 120 | spin_lock.h | 108 | spin_lock.h |
| 121 | stream.cpp | 109 | stream.cpp |
| @@ -157,6 +145,7 @@ if(ARCHITECTURE_x86_64) | |||
| 157 | x64/xbyak_abi.h | 145 | x64/xbyak_abi.h |
| 158 | x64/xbyak_util.h | 146 | x64/xbyak_util.h |
| 159 | ) | 147 | ) |
| 148 | target_link_libraries(common PRIVATE xbyak) | ||
| 160 | endif() | 149 | endif() |
| 161 | 150 | ||
| 162 | if (MSVC) | 151 | if (MSVC) |
| @@ -180,9 +169,10 @@ endif() | |||
| 180 | create_target_directory_groups(common) | 169 | create_target_directory_groups(common) |
| 181 | 170 | ||
| 182 | target_link_libraries(common PUBLIC ${Boost_LIBRARIES} fmt::fmt microprofile Threads::Threads) | 171 | target_link_libraries(common PUBLIC ${Boost_LIBRARIES} fmt::fmt microprofile Threads::Threads) |
| 183 | target_link_libraries(common PRIVATE lz4::lz4 xbyak) | 172 | target_link_libraries(common PRIVATE lz4::lz4) |
| 184 | if (MSVC) | 173 | if (TARGET zstd::zstd) |
| 185 | target_link_libraries(common PRIVATE zstd::zstd) | 174 | target_link_libraries(common PRIVATE zstd::zstd) |
| 186 | else() | 175 | else() |
| 187 | target_link_libraries(common PRIVATE zstd) | 176 | target_link_libraries(common PRIVATE |
| 177 | $<IF:$<TARGET_EXISTS:zstd::libzstd_shared>,zstd::libzstd_shared,zstd::libzstd_static>) | ||
| 188 | endif() | 178 | endif() |
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 4804a3421..c27c9241d 100644 --- a/src/common/algorithm.h +++ b/src/common/algorithm.h | |||
| @@ -1,6 +1,5 @@ | |||
| 1 | // Copyright 2019 yuzu emulator team | 1 | // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | // Refer to the license.txt file included. | ||
| 4 | 3 | ||
| 5 | #pragma once | 4 | #pragma once |
| 6 | 5 | ||
| @@ -25,4 +24,12 @@ template <class ForwardIt, class T, class Compare = std::less<>> | |||
| 25 | return first != last && !comp(value, *first) ? first : last; | 24 | return first != last && !comp(value, *first) ? first : last; |
| 26 | } | 25 | } |
| 27 | 26 | ||
| 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 | |||
| 28 | } // namespace Common | 35 | } // namespace Common |
diff --git a/src/common/alignment.h b/src/common/alignment.h index 8570c7d3c..7e897334b 100644 --- a/src/common/alignment.h +++ b/src/common/alignment.h | |||
| @@ -1,4 +1,5 @@ | |||
| 1 | // This file is under the public domain. | 1 | // SPDX-FileCopyrightText: 2014 Jannik Vogel <email@jannikvogel.de> |
| 2 | // SPDX-License-Identifier: CC0-1.0 | ||
| 2 | 3 | ||
| 3 | #pragma once | 4 | #pragma once |
| 4 | 5 | ||
diff --git a/src/common/announce_multiplayer_room.h b/src/common/announce_multiplayer_room.h new file mode 100644 index 000000000..4a3100fa4 --- /dev/null +++ b/src/common/announce_multiplayer_room.h | |||
| @@ -0,0 +1,140 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <array> | ||
| 7 | #include <functional> | ||
| 8 | #include <string> | ||
| 9 | #include <vector> | ||
| 10 | #include "common/common_types.h" | ||
| 11 | #include "common/socket_types.h" | ||
| 12 | #include "web_service/web_result.h" | ||
| 13 | |||
| 14 | namespace AnnounceMultiplayerRoom { | ||
| 15 | |||
| 16 | struct GameInfo { | ||
| 17 | std::string name{""}; | ||
| 18 | u64 id{0}; | ||
| 19 | std::string version{""}; | ||
| 20 | }; | ||
| 21 | |||
| 22 | struct Member { | ||
| 23 | std::string username; | ||
| 24 | std::string nickname; | ||
| 25 | std::string display_name; | ||
| 26 | std::string avatar_url; | ||
| 27 | Network::IPv4Address fake_ip; | ||
| 28 | GameInfo game; | ||
| 29 | }; | ||
| 30 | |||
| 31 | struct RoomInformation { | ||
| 32 | std::string name; ///< Name of the server | ||
| 33 | std::string description; ///< Server description | ||
| 34 | u32 member_slots; ///< Maximum number of members in this room | ||
| 35 | u16 port; ///< The port of this room | ||
| 36 | GameInfo preferred_game; ///< Game to advertise that you want to play | ||
| 37 | std::string host_username; ///< Forum username of the host | ||
| 38 | bool enable_yuzu_mods; ///< Allow yuzu Moderators to moderate on this room | ||
| 39 | }; | ||
| 40 | |||
| 41 | struct Room { | ||
| 42 | RoomInformation information; | ||
| 43 | |||
| 44 | std::string id; | ||
| 45 | std::string verify_uid; ///< UID used for verification | ||
| 46 | std::string ip; | ||
| 47 | u32 net_version; | ||
| 48 | bool has_password; | ||
| 49 | |||
| 50 | std::vector<Member> members; | ||
| 51 | }; | ||
| 52 | using RoomList = std::vector<Room>; | ||
| 53 | |||
| 54 | /** | ||
| 55 | * A AnnounceMultiplayerRoom interface class. A backend to submit/get to/from a web service should | ||
| 56 | * implement this interface. | ||
| 57 | */ | ||
| 58 | class Backend { | ||
| 59 | public: | ||
| 60 | virtual ~Backend() = default; | ||
| 61 | |||
| 62 | /** | ||
| 63 | * Sets the Information that gets used for the announce | ||
| 64 | * @param uid The Id of the room | ||
| 65 | * @param name The name of the room | ||
| 66 | * @param description The room description | ||
| 67 | * @param port The port of the room | ||
| 68 | * @param net_version The version of the libNetwork that gets used | ||
| 69 | * @param has_password True if the room is passowrd protected | ||
| 70 | * @param preferred_game The preferred game of the room | ||
| 71 | * @param preferred_game_id The title id of the preferred game | ||
| 72 | */ | ||
| 73 | virtual void SetRoomInformation(const std::string& name, const std::string& description, | ||
| 74 | const u16 port, const u32 max_player, const u32 net_version, | ||
| 75 | const bool has_password, const GameInfo& preferred_game) = 0; | ||
| 76 | /** | ||
| 77 | * Adds a player information to the data that gets announced | ||
| 78 | * @param member The player to add | ||
| 79 | */ | ||
| 80 | virtual void AddPlayer(const Member& member) = 0; | ||
| 81 | |||
| 82 | /** | ||
| 83 | * Updates the data in the announce service. Re-register the room when required. | ||
| 84 | * @result The result of the update attempt | ||
| 85 | */ | ||
| 86 | virtual WebService::WebResult Update() = 0; | ||
| 87 | |||
| 88 | /** | ||
| 89 | * Registers the data in the announce service | ||
| 90 | * @result The result of the register attempt. When the result code is Success, A global Guid of | ||
| 91 | * the room which may be used for verification will be in the result's returned_data. | ||
| 92 | */ | ||
| 93 | virtual WebService::WebResult Register() = 0; | ||
| 94 | |||
| 95 | /** | ||
| 96 | * Empties the stored players | ||
| 97 | */ | ||
| 98 | virtual void ClearPlayers() = 0; | ||
| 99 | |||
| 100 | /** | ||
| 101 | * Get the room information from the announce service | ||
| 102 | * @result A list of all rooms the announce service has | ||
| 103 | */ | ||
| 104 | virtual RoomList GetRoomList() = 0; | ||
| 105 | |||
| 106 | /** | ||
| 107 | * Sends a delete message to the announce service | ||
| 108 | */ | ||
| 109 | virtual void Delete() = 0; | ||
| 110 | }; | ||
| 111 | |||
| 112 | /** | ||
| 113 | * Empty implementation of AnnounceMultiplayerRoom interface that drops all data. Used when a | ||
| 114 | * functional backend implementation is not available. | ||
| 115 | */ | ||
| 116 | class NullBackend : public Backend { | ||
| 117 | public: | ||
| 118 | ~NullBackend() = default; | ||
| 119 | void SetRoomInformation(const std::string& /*name*/, const std::string& /*description*/, | ||
| 120 | const u16 /*port*/, const u32 /*max_player*/, const u32 /*net_version*/, | ||
| 121 | const bool /*has_password*/, | ||
| 122 | const GameInfo& /*preferred_game*/) override {} | ||
| 123 | void AddPlayer(const Member& /*member*/) override {} | ||
| 124 | WebService::WebResult Update() override { | ||
| 125 | return WebService::WebResult{WebService::WebResult::Code::NoWebservice, | ||
| 126 | "WebService is missing", ""}; | ||
| 127 | } | ||
| 128 | WebService::WebResult Register() override { | ||
| 129 | return WebService::WebResult{WebService::WebResult::Code::NoWebservice, | ||
| 130 | "WebService is missing", ""}; | ||
| 131 | } | ||
| 132 | void ClearPlayers() override {} | ||
| 133 | RoomList GetRoomList() override { | ||
| 134 | return RoomList{}; | ||
| 135 | } | ||
| 136 | |||
| 137 | void Delete() override {} | ||
| 138 | }; | ||
| 139 | |||
| 140 | } // namespace AnnounceMultiplayerRoom | ||
diff --git a/src/common/assert.cpp b/src/common/assert.cpp index 72f1121aa..6026b7dc2 100644 --- a/src/common/assert.cpp +++ b/src/common/assert.cpp | |||
| @@ -1,14 +1,18 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | // Refer to the license.txt file included. | ||
| 4 | 3 | ||
| 5 | #include "common/assert.h" | 4 | #include "common/assert.h" |
| 6 | #include "common/common_funcs.h" | 5 | #include "common/common_funcs.h" |
| 7 | 6 | ||
| 8 | #include "common/settings.h" | 7 | #include "common/settings.h" |
| 9 | 8 | ||
| 10 | void assert_handle_failure() { | 9 | void assert_fail_impl() { |
| 11 | if (Settings::values.use_debug_asserts) { | 10 | if (Settings::values.use_debug_asserts) { |
| 12 | Crash(); | 11 | Crash(); |
| 13 | } | 12 | } |
| 14 | } | 13 | } |
| 14 | |||
| 15 | [[noreturn]] void unreachable_impl() { | ||
| 16 | Crash(); | ||
| 17 | throw std::runtime_error("Unreachable code"); | ||
| 18 | } | ||
diff --git a/src/common/assert.h b/src/common/assert.h index 33060d865..8c927fcc0 100644 --- a/src/common/assert.h +++ b/src/common/assert.h | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project | 1 | // SPDX-FileCopyrightText: 2013 Dolphin Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-FileCopyrightText: 2014 Citra Emulator Project |
| 3 | // Refer to the license.txt file included. | 3 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| @@ -9,44 +9,43 @@ | |||
| 9 | // Sometimes we want to try to continue even after hitting an assert. | 9 | // Sometimes we want to try to continue even after hitting an assert. |
| 10 | // However touching this file yields a global recompilation as this header is included almost | 10 | // However touching this file yields a global recompilation as this header is included almost |
| 11 | // everywhere. So let's just move the handling of the failed assert to a single cpp file. | 11 | // everywhere. So let's just move the handling of the failed assert to a single cpp file. |
| 12 | void assert_handle_failure(); | ||
| 13 | 12 | ||
| 14 | // For asserts we'd like to keep all the junk executed when an assert happens away from the | 13 | void assert_fail_impl(); |
| 15 | // important code in the function. One way of doing this is to put all the relevant code inside a | 14 | [[noreturn]] void unreachable_impl(); |
| 16 | // lambda and force the compiler to not inline it. Unfortunately, MSVC seems to have no syntax to | 15 | |
| 17 | // specify __declspec on lambda functions, so what we do instead is define a noinline wrapper | 16 | #ifdef _MSC_VER |
| 18 | // template that calls the lambda. This seems to generate an extra instruction at the call-site | 17 | #define YUZU_NO_INLINE __declspec(noinline) |
| 19 | // compared to the ideal implementation (which wouldn't support ASSERT_MSG parameters), but is good | 18 | #else |
| 20 | // enough for our purposes. | 19 | #define YUZU_NO_INLINE __attribute__((noinline)) |
| 21 | template <typename Fn> | ||
| 22 | #if defined(_MSC_VER) | ||
| 23 | [[msvc::noinline]] | ||
| 24 | #elif defined(__GNUC__) | ||
| 25 | [[gnu::cold, gnu::noinline]] | ||
| 26 | #endif | 20 | #endif |
| 27 | static void | ||
| 28 | assert_noinline_call(const Fn& fn) { | ||
| 29 | fn(); | ||
| 30 | assert_handle_failure(); | ||
| 31 | } | ||
| 32 | 21 | ||
| 33 | #define ASSERT(_a_) \ | 22 | #define ASSERT(_a_) \ |
| 34 | do \ | 23 | ([&]() YUZU_NO_INLINE { \ |
| 35 | if (!(_a_)) { \ | 24 | if (!(_a_)) [[unlikely]] { \ |
| 36 | assert_noinline_call([] { LOG_CRITICAL(Debug, "Assertion Failed!"); }); \ | 25 | LOG_CRITICAL(Debug, "Assertion Failed!"); \ |
| 26 | assert_fail_impl(); \ | ||
| 37 | } \ | 27 | } \ |
| 38 | while (0) | 28 | }()) |
| 39 | 29 | ||
| 40 | #define ASSERT_MSG(_a_, ...) \ | 30 | #define ASSERT_MSG(_a_, ...) \ |
| 41 | do \ | 31 | ([&]() YUZU_NO_INLINE { \ |
| 42 | if (!(_a_)) { \ | 32 | if (!(_a_)) [[unlikely]] { \ |
| 43 | assert_noinline_call([&] { LOG_CRITICAL(Debug, "Assertion Failed!\n" __VA_ARGS__); }); \ | 33 | LOG_CRITICAL(Debug, "Assertion Failed!\n" __VA_ARGS__); \ |
| 34 | assert_fail_impl(); \ | ||
| 44 | } \ | 35 | } \ |
| 45 | while (0) | 36 | }()) |
| 37 | |||
| 38 | #define UNREACHABLE() \ | ||
| 39 | do { \ | ||
| 40 | LOG_CRITICAL(Debug, "Unreachable code!"); \ | ||
| 41 | unreachable_impl(); \ | ||
| 42 | } while (0) | ||
| 46 | 43 | ||
| 47 | #define UNREACHABLE() assert_noinline_call([] { LOG_CRITICAL(Debug, "Unreachable code!"); }) | ||
| 48 | #define UNREACHABLE_MSG(...) \ | 44 | #define UNREACHABLE_MSG(...) \ |
| 49 | assert_noinline_call([&] { LOG_CRITICAL(Debug, "Unreachable code!\n" __VA_ARGS__); }) | 45 | do { \ |
| 46 | LOG_CRITICAL(Debug, "Unreachable code!\n" __VA_ARGS__); \ | ||
| 47 | unreachable_impl(); \ | ||
| 48 | } while (0) | ||
| 50 | 49 | ||
| 51 | #ifdef _DEBUG | 50 | #ifdef _DEBUG |
| 52 | #define DEBUG_ASSERT(_a_) ASSERT(_a_) | 51 | #define DEBUG_ASSERT(_a_) ASSERT(_a_) |
diff --git a/src/common/atomic_helpers.h b/src/common/atomic_helpers.h new file mode 100644 index 000000000..bef5015c1 --- /dev/null +++ b/src/common/atomic_helpers.h | |||
| @@ -0,0 +1,775 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2013-2016 Cameron Desrochers | ||
| 2 | // SPDX-FileCopyrightText: 2015 Jeff Preshing | ||
| 3 | // SPDX-License-Identifier: BSD-2-Clause AND Zlib | ||
| 4 | |||
| 5 | // Distributed under the simplified BSD license (see the license file that | ||
| 6 | // should have come with this header). | ||
| 7 | // Uses Jeff Preshing's semaphore implementation (under the terms of its | ||
| 8 | // separate zlib license, embedded below). | ||
| 9 | |||
| 10 | #pragma once | ||
| 11 | |||
| 12 | // Provides portable (VC++2010+, Intel ICC 13, GCC 4.7+, and anything C++11 compliant) | ||
| 13 | // implementation of low-level memory barriers, plus a few semi-portable utility macros (for | ||
| 14 | // inlining and alignment). Also has a basic atomic type (limited to hardware-supported atomics with | ||
| 15 | // no memory ordering guarantees). Uses the AE_* prefix for macros (historical reasons), and the | ||
| 16 | // "moodycamel" namespace for symbols. | ||
| 17 | |||
| 18 | #include <cassert> | ||
| 19 | #include <cerrno> | ||
| 20 | #include <cstdint> | ||
| 21 | #include <ctime> | ||
| 22 | #include <type_traits> | ||
| 23 | |||
| 24 | // Platform detection | ||
| 25 | #if defined(__INTEL_COMPILER) | ||
| 26 | #define AE_ICC | ||
| 27 | #elif defined(_MSC_VER) | ||
| 28 | #define AE_VCPP | ||
| 29 | #elif defined(__GNUC__) | ||
| 30 | #define AE_GCC | ||
| 31 | #endif | ||
| 32 | |||
| 33 | #if defined(_M_IA64) || defined(__ia64__) | ||
| 34 | #define AE_ARCH_IA64 | ||
| 35 | #elif defined(_WIN64) || defined(__amd64__) || defined(_M_X64) || defined(__x86_64__) | ||
| 36 | #define AE_ARCH_X64 | ||
| 37 | #elif defined(_M_IX86) || defined(__i386__) | ||
| 38 | #define AE_ARCH_X86 | ||
| 39 | #elif defined(_M_PPC) || defined(__powerpc__) | ||
| 40 | #define AE_ARCH_PPC | ||
| 41 | #else | ||
| 42 | #define AE_ARCH_UNKNOWN | ||
| 43 | #endif | ||
| 44 | |||
| 45 | // AE_UNUSED | ||
| 46 | #define AE_UNUSED(x) ((void)x) | ||
| 47 | |||
| 48 | // AE_NO_TSAN/AE_TSAN_ANNOTATE_* | ||
| 49 | #if defined(__has_feature) | ||
| 50 | #if __has_feature(thread_sanitizer) | ||
| 51 | #if __cplusplus >= 201703L // inline variables require C++17 | ||
| 52 | namespace Common { | ||
| 53 | inline int ae_tsan_global; | ||
| 54 | } | ||
| 55 | #define AE_TSAN_ANNOTATE_RELEASE() \ | ||
| 56 | AnnotateHappensBefore(__FILE__, __LINE__, (void*)(&::moodycamel::ae_tsan_global)) | ||
| 57 | #define AE_TSAN_ANNOTATE_ACQUIRE() \ | ||
| 58 | AnnotateHappensAfter(__FILE__, __LINE__, (void*)(&::moodycamel::ae_tsan_global)) | ||
| 59 | extern "C" void AnnotateHappensBefore(const char*, int, void*); | ||
| 60 | extern "C" void AnnotateHappensAfter(const char*, int, void*); | ||
| 61 | #else // when we can't work with tsan, attempt to disable its warnings | ||
| 62 | #define AE_NO_TSAN __attribute__((no_sanitize("thread"))) | ||
| 63 | #endif | ||
| 64 | #endif | ||
| 65 | #endif | ||
| 66 | #ifndef AE_NO_TSAN | ||
| 67 | #define AE_NO_TSAN | ||
| 68 | #endif | ||
| 69 | #ifndef AE_TSAN_ANNOTATE_RELEASE | ||
| 70 | #define AE_TSAN_ANNOTATE_RELEASE() | ||
| 71 | #define AE_TSAN_ANNOTATE_ACQUIRE() | ||
| 72 | #endif | ||
| 73 | |||
| 74 | // AE_FORCEINLINE | ||
| 75 | #if defined(AE_VCPP) || defined(AE_ICC) | ||
| 76 | #define AE_FORCEINLINE __forceinline | ||
| 77 | #elif defined(AE_GCC) | ||
| 78 | //#define AE_FORCEINLINE __attribute__((always_inline)) | ||
| 79 | #define AE_FORCEINLINE inline | ||
| 80 | #else | ||
| 81 | #define AE_FORCEINLINE inline | ||
| 82 | #endif | ||
| 83 | |||
| 84 | // AE_ALIGN | ||
| 85 | #if defined(AE_VCPP) || defined(AE_ICC) | ||
| 86 | #define AE_ALIGN(x) __declspec(align(x)) | ||
| 87 | #elif defined(AE_GCC) | ||
| 88 | #define AE_ALIGN(x) __attribute__((aligned(x))) | ||
| 89 | #else | ||
| 90 | // Assume GCC compliant syntax... | ||
| 91 | #define AE_ALIGN(x) __attribute__((aligned(x))) | ||
| 92 | #endif | ||
| 93 | |||
| 94 | // Portable atomic fences implemented below: | ||
| 95 | |||
| 96 | namespace Common { | ||
| 97 | |||
| 98 | enum memory_order { | ||
| 99 | memory_order_relaxed, | ||
| 100 | memory_order_acquire, | ||
| 101 | memory_order_release, | ||
| 102 | memory_order_acq_rel, | ||
| 103 | memory_order_seq_cst, | ||
| 104 | |||
| 105 | // memory_order_sync: Forces a full sync: | ||
| 106 | // #LoadLoad, #LoadStore, #StoreStore, and most significantly, #StoreLoad | ||
| 107 | memory_order_sync = memory_order_seq_cst | ||
| 108 | }; | ||
| 109 | |||
| 110 | } // namespace Common | ||
| 111 | |||
| 112 | #if (defined(AE_VCPP) && (_MSC_VER < 1700 || defined(__cplusplus_cli))) || \ | ||
| 113 | (defined(AE_ICC) && __INTEL_COMPILER < 1600) | ||
| 114 | // VS2010 and ICC13 don't support std::atomic_*_fence, implement our own fences | ||
| 115 | |||
| 116 | #include <intrin.h> | ||
| 117 | |||
| 118 | #if defined(AE_ARCH_X64) || defined(AE_ARCH_X86) | ||
| 119 | #define AeFullSync _mm_mfence | ||
| 120 | #define AeLiteSync _mm_mfence | ||
| 121 | #elif defined(AE_ARCH_IA64) | ||
| 122 | #define AeFullSync __mf | ||
| 123 | #define AeLiteSync __mf | ||
| 124 | #elif defined(AE_ARCH_PPC) | ||
| 125 | #include <ppcintrinsics.h> | ||
| 126 | #define AeFullSync __sync | ||
| 127 | #define AeLiteSync __lwsync | ||
| 128 | #endif | ||
| 129 | |||
| 130 | #ifdef AE_VCPP | ||
| 131 | #pragma warning(push) | ||
| 132 | #pragma warning(disable : 4365) // Disable erroneous 'conversion from long to unsigned int, | ||
| 133 | // signed/unsigned mismatch' error when using `assert` | ||
| 134 | #ifdef __cplusplus_cli | ||
| 135 | #pragma managed(push, off) | ||
| 136 | #endif | ||
| 137 | #endif | ||
| 138 | |||
| 139 | namespace Common { | ||
| 140 | |||
| 141 | AE_FORCEINLINE void compiler_fence(memory_order order) AE_NO_TSAN { | ||
| 142 | switch (order) { | ||
| 143 | case memory_order_relaxed: | ||
| 144 | break; | ||
| 145 | case memory_order_acquire: | ||
| 146 | _ReadBarrier(); | ||
| 147 | break; | ||
| 148 | case memory_order_release: | ||
| 149 | _WriteBarrier(); | ||
| 150 | break; | ||
| 151 | case memory_order_acq_rel: | ||
| 152 | _ReadWriteBarrier(); | ||
| 153 | break; | ||
| 154 | case memory_order_seq_cst: | ||
| 155 | _ReadWriteBarrier(); | ||
| 156 | break; | ||
| 157 | default: | ||
| 158 | assert(false); | ||
| 159 | } | ||
| 160 | } | ||
| 161 | |||
| 162 | // x86/x64 have a strong memory model -- all loads and stores have | ||
| 163 | // acquire and release semantics automatically (so only need compiler | ||
| 164 | // barriers for those). | ||
| 165 | #if defined(AE_ARCH_X86) || defined(AE_ARCH_X64) | ||
| 166 | AE_FORCEINLINE void fence(memory_order order) AE_NO_TSAN { | ||
| 167 | switch (order) { | ||
| 168 | case memory_order_relaxed: | ||
| 169 | break; | ||
| 170 | case memory_order_acquire: | ||
| 171 | _ReadBarrier(); | ||
| 172 | break; | ||
| 173 | case memory_order_release: | ||
| 174 | _WriteBarrier(); | ||
| 175 | break; | ||
| 176 | case memory_order_acq_rel: | ||
| 177 | _ReadWriteBarrier(); | ||
| 178 | break; | ||
| 179 | case memory_order_seq_cst: | ||
| 180 | _ReadWriteBarrier(); | ||
| 181 | AeFullSync(); | ||
| 182 | _ReadWriteBarrier(); | ||
| 183 | break; | ||
| 184 | default: | ||
| 185 | assert(false); | ||
| 186 | } | ||
| 187 | } | ||
| 188 | #else | ||
| 189 | AE_FORCEINLINE void fence(memory_order order) AE_NO_TSAN { | ||
| 190 | // Non-specialized arch, use heavier memory barriers everywhere just in case :-( | ||
| 191 | switch (order) { | ||
| 192 | case memory_order_relaxed: | ||
| 193 | break; | ||
| 194 | case memory_order_acquire: | ||
| 195 | _ReadBarrier(); | ||
| 196 | AeLiteSync(); | ||
| 197 | _ReadBarrier(); | ||
| 198 | break; | ||
| 199 | case memory_order_release: | ||
| 200 | _WriteBarrier(); | ||
| 201 | AeLiteSync(); | ||
| 202 | _WriteBarrier(); | ||
| 203 | break; | ||
| 204 | case memory_order_acq_rel: | ||
| 205 | _ReadWriteBarrier(); | ||
| 206 | AeLiteSync(); | ||
| 207 | _ReadWriteBarrier(); | ||
| 208 | break; | ||
| 209 | case memory_order_seq_cst: | ||
| 210 | _ReadWriteBarrier(); | ||
| 211 | AeFullSync(); | ||
| 212 | _ReadWriteBarrier(); | ||
| 213 | break; | ||
| 214 | default: | ||
| 215 | assert(false); | ||
| 216 | } | ||
| 217 | } | ||
| 218 | #endif | ||
| 219 | } // namespace Common | ||
| 220 | #else | ||
| 221 | // Use standard library of atomics | ||
| 222 | #include <atomic> | ||
| 223 | |||
| 224 | namespace Common { | ||
| 225 | |||
| 226 | AE_FORCEINLINE void compiler_fence(memory_order order) AE_NO_TSAN { | ||
| 227 | switch (order) { | ||
| 228 | case memory_order_relaxed: | ||
| 229 | break; | ||
| 230 | case memory_order_acquire: | ||
| 231 | std::atomic_signal_fence(std::memory_order_acquire); | ||
| 232 | break; | ||
| 233 | case memory_order_release: | ||
| 234 | std::atomic_signal_fence(std::memory_order_release); | ||
| 235 | break; | ||
| 236 | case memory_order_acq_rel: | ||
| 237 | std::atomic_signal_fence(std::memory_order_acq_rel); | ||
| 238 | break; | ||
| 239 | case memory_order_seq_cst: | ||
| 240 | std::atomic_signal_fence(std::memory_order_seq_cst); | ||
| 241 | break; | ||
| 242 | default: | ||
| 243 | assert(false); | ||
| 244 | } | ||
| 245 | } | ||
| 246 | |||
| 247 | AE_FORCEINLINE void fence(memory_order order) AE_NO_TSAN { | ||
| 248 | switch (order) { | ||
| 249 | case memory_order_relaxed: | ||
| 250 | break; | ||
| 251 | case memory_order_acquire: | ||
| 252 | AE_TSAN_ANNOTATE_ACQUIRE(); | ||
| 253 | std::atomic_thread_fence(std::memory_order_acquire); | ||
| 254 | break; | ||
| 255 | case memory_order_release: | ||
| 256 | AE_TSAN_ANNOTATE_RELEASE(); | ||
| 257 | std::atomic_thread_fence(std::memory_order_release); | ||
| 258 | break; | ||
| 259 | case memory_order_acq_rel: | ||
| 260 | AE_TSAN_ANNOTATE_ACQUIRE(); | ||
| 261 | AE_TSAN_ANNOTATE_RELEASE(); | ||
| 262 | std::atomic_thread_fence(std::memory_order_acq_rel); | ||
| 263 | break; | ||
| 264 | case memory_order_seq_cst: | ||
| 265 | AE_TSAN_ANNOTATE_ACQUIRE(); | ||
| 266 | AE_TSAN_ANNOTATE_RELEASE(); | ||
| 267 | std::atomic_thread_fence(std::memory_order_seq_cst); | ||
| 268 | break; | ||
| 269 | default: | ||
| 270 | assert(false); | ||
| 271 | } | ||
| 272 | } | ||
| 273 | |||
| 274 | } // namespace Common | ||
| 275 | |||
| 276 | #endif | ||
| 277 | |||
| 278 | #if !defined(AE_VCPP) || (_MSC_VER >= 1700 && !defined(__cplusplus_cli)) | ||
| 279 | #define AE_USE_STD_ATOMIC_FOR_WEAK_ATOMIC | ||
| 280 | #endif | ||
| 281 | |||
| 282 | #ifdef AE_USE_STD_ATOMIC_FOR_WEAK_ATOMIC | ||
| 283 | #include <atomic> | ||
| 284 | #endif | ||
| 285 | #include <utility> | ||
| 286 | |||
| 287 | // WARNING: *NOT* A REPLACEMENT FOR std::atomic. READ CAREFULLY: | ||
| 288 | // Provides basic support for atomic variables -- no memory ordering guarantees are provided. | ||
| 289 | // The guarantee of atomicity is only made for types that already have atomic load and store | ||
| 290 | // guarantees at the hardware level -- on most platforms this generally means aligned pointers and | ||
| 291 | // integers (only). | ||
| 292 | namespace Common { | ||
| 293 | template <typename T> | ||
| 294 | class weak_atomic { | ||
| 295 | public: | ||
| 296 | AE_NO_TSAN weak_atomic() : value() {} | ||
| 297 | #ifdef AE_VCPP | ||
| 298 | #pragma warning(push) | ||
| 299 | #pragma warning(disable : 4100) // Get rid of (erroneous) 'unreferenced formal parameter' warning | ||
| 300 | #endif | ||
| 301 | template <typename U> | ||
| 302 | AE_NO_TSAN weak_atomic(U&& x) : value(std::forward<U>(x)) {} | ||
| 303 | #ifdef __cplusplus_cli | ||
| 304 | // Work around bug with universal reference/nullptr combination that only appears when /clr is | ||
| 305 | // on | ||
| 306 | AE_NO_TSAN weak_atomic(nullptr_t) : value(nullptr) {} | ||
| 307 | #endif | ||
| 308 | AE_NO_TSAN weak_atomic(weak_atomic const& other) : value(other.load()) {} | ||
| 309 | AE_NO_TSAN weak_atomic(weak_atomic&& other) : value(std::move(other.load())) {} | ||
| 310 | #ifdef AE_VCPP | ||
| 311 | #pragma warning(pop) | ||
| 312 | #endif | ||
| 313 | |||
| 314 | AE_FORCEINLINE operator T() const AE_NO_TSAN { | ||
| 315 | return load(); | ||
| 316 | } | ||
| 317 | |||
| 318 | #ifndef AE_USE_STD_ATOMIC_FOR_WEAK_ATOMIC | ||
| 319 | template <typename U> | ||
| 320 | AE_FORCEINLINE weak_atomic const& operator=(U&& x) AE_NO_TSAN { | ||
| 321 | value = std::forward<U>(x); | ||
| 322 | return *this; | ||
| 323 | } | ||
| 324 | AE_FORCEINLINE weak_atomic const& operator=(weak_atomic const& other) AE_NO_TSAN { | ||
| 325 | value = other.value; | ||
| 326 | return *this; | ||
| 327 | } | ||
| 328 | |||
| 329 | AE_FORCEINLINE T load() const AE_NO_TSAN { | ||
| 330 | return value; | ||
| 331 | } | ||
| 332 | |||
| 333 | AE_FORCEINLINE T fetch_add_acquire(T increment) AE_NO_TSAN { | ||
| 334 | #if defined(AE_ARCH_X64) || defined(AE_ARCH_X86) | ||
| 335 | if (sizeof(T) == 4) | ||
| 336 | return _InterlockedExchangeAdd((long volatile*)&value, (long)increment); | ||
| 337 | #if defined(_M_AMD64) | ||
| 338 | else if (sizeof(T) == 8) | ||
| 339 | return _InterlockedExchangeAdd64((long long volatile*)&value, (long long)increment); | ||
| 340 | #endif | ||
| 341 | #else | ||
| 342 | #error Unsupported platform | ||
| 343 | #endif | ||
| 344 | assert(false && "T must be either a 32 or 64 bit type"); | ||
| 345 | return value; | ||
| 346 | } | ||
| 347 | |||
| 348 | AE_FORCEINLINE T fetch_add_release(T increment) AE_NO_TSAN { | ||
| 349 | #if defined(AE_ARCH_X64) || defined(AE_ARCH_X86) | ||
| 350 | if (sizeof(T) == 4) | ||
| 351 | return _InterlockedExchangeAdd((long volatile*)&value, (long)increment); | ||
| 352 | #if defined(_M_AMD64) | ||
| 353 | else if (sizeof(T) == 8) | ||
| 354 | return _InterlockedExchangeAdd64((long long volatile*)&value, (long long)increment); | ||
| 355 | #endif | ||
| 356 | #else | ||
| 357 | #error Unsupported platform | ||
| 358 | #endif | ||
| 359 | assert(false && "T must be either a 32 or 64 bit type"); | ||
| 360 | return value; | ||
| 361 | } | ||
| 362 | #else | ||
| 363 | template <typename U> | ||
| 364 | AE_FORCEINLINE weak_atomic const& operator=(U&& x) AE_NO_TSAN { | ||
| 365 | value.store(std::forward<U>(x), std::memory_order_relaxed); | ||
| 366 | return *this; | ||
| 367 | } | ||
| 368 | |||
| 369 | AE_FORCEINLINE weak_atomic const& operator=(weak_atomic const& other) AE_NO_TSAN { | ||
| 370 | value.store(other.value.load(std::memory_order_relaxed), std::memory_order_relaxed); | ||
| 371 | return *this; | ||
| 372 | } | ||
| 373 | |||
| 374 | AE_FORCEINLINE T load() const AE_NO_TSAN { | ||
| 375 | return value.load(std::memory_order_relaxed); | ||
| 376 | } | ||
| 377 | |||
| 378 | AE_FORCEINLINE T fetch_add_acquire(T increment) AE_NO_TSAN { | ||
| 379 | return value.fetch_add(increment, std::memory_order_acquire); | ||
| 380 | } | ||
| 381 | |||
| 382 | AE_FORCEINLINE T fetch_add_release(T increment) AE_NO_TSAN { | ||
| 383 | return value.fetch_add(increment, std::memory_order_release); | ||
| 384 | } | ||
| 385 | #endif | ||
| 386 | |||
| 387 | private: | ||
| 388 | #ifndef AE_USE_STD_ATOMIC_FOR_WEAK_ATOMIC | ||
| 389 | // No std::atomic support, but still need to circumvent compiler optimizations. | ||
| 390 | // `volatile` will make memory access slow, but is guaranteed to be reliable. | ||
| 391 | volatile T value; | ||
| 392 | #else | ||
| 393 | std::atomic<T> value; | ||
| 394 | #endif | ||
| 395 | }; | ||
| 396 | |||
| 397 | } // namespace Common | ||
| 398 | |||
| 399 | // Portable single-producer, single-consumer semaphore below: | ||
| 400 | |||
| 401 | #if defined(_WIN32) | ||
| 402 | // Avoid including windows.h in a header; we only need a handful of | ||
| 403 | // items, so we'll redeclare them here (this is relatively safe since | ||
| 404 | // the API generally has to remain stable between Windows versions). | ||
| 405 | // I know this is an ugly hack but it still beats polluting the global | ||
| 406 | // namespace with thousands of generic names or adding a .cpp for nothing. | ||
| 407 | extern "C" { | ||
| 408 | struct _SECURITY_ATTRIBUTES; | ||
| 409 | __declspec(dllimport) void* __stdcall CreateSemaphoreW(_SECURITY_ATTRIBUTES* lpSemaphoreAttributes, | ||
| 410 | long lInitialCount, long lMaximumCount, | ||
| 411 | const wchar_t* lpName); | ||
| 412 | __declspec(dllimport) int __stdcall CloseHandle(void* hObject); | ||
| 413 | __declspec(dllimport) unsigned long __stdcall WaitForSingleObject(void* hHandle, | ||
| 414 | unsigned long dwMilliseconds); | ||
| 415 | __declspec(dllimport) int __stdcall ReleaseSemaphore(void* hSemaphore, long lReleaseCount, | ||
| 416 | long* lpPreviousCount); | ||
| 417 | } | ||
| 418 | #elif defined(__MACH__) | ||
| 419 | #include <mach/mach.h> | ||
| 420 | #elif defined(__unix__) | ||
| 421 | #include <semaphore.h> | ||
| 422 | #elif defined(FREERTOS) | ||
| 423 | #include <FreeRTOS.h> | ||
| 424 | #include <semphr.h> | ||
| 425 | #include <task.h> | ||
| 426 | #endif | ||
| 427 | |||
| 428 | namespace Common { | ||
| 429 | // Code in the spsc_sema namespace below is an adaptation of Jeff Preshing's | ||
| 430 | // portable + lightweight semaphore implementations, originally from | ||
| 431 | // https://github.com/preshing/cpp11-on-multicore/blob/master/common/sema.h | ||
| 432 | // LICENSE: | ||
| 433 | // Copyright (c) 2015 Jeff Preshing | ||
| 434 | // | ||
| 435 | // This software is provided 'as-is', without any express or implied | ||
| 436 | // warranty. In no event will the authors be held liable for any damages | ||
| 437 | // arising from the use of this software. | ||
| 438 | // | ||
| 439 | // Permission is granted to anyone to use this software for any purpose, | ||
| 440 | // including commercial applications, and to alter it and redistribute it | ||
| 441 | // freely, subject to the following restrictions: | ||
| 442 | // | ||
| 443 | // 1. The origin of this software must not be misrepresented; you must not | ||
| 444 | // claim that you wrote the original software. If you use this software | ||
| 445 | // in a product, an acknowledgement in the product documentation would be | ||
| 446 | // appreciated but is not required. | ||
| 447 | // 2. Altered source versions must be plainly marked as such, and must not be | ||
| 448 | // misrepresented as being the original software. | ||
| 449 | // 3. This notice may not be removed or altered from any source distribution. | ||
| 450 | namespace spsc_sema { | ||
| 451 | #if defined(_WIN32) | ||
| 452 | class Semaphore { | ||
| 453 | private: | ||
| 454 | void* m_hSema; | ||
| 455 | |||
| 456 | Semaphore(const Semaphore& other); | ||
| 457 | Semaphore& operator=(const Semaphore& other); | ||
| 458 | |||
| 459 | public: | ||
| 460 | AE_NO_TSAN Semaphore(int initialCount = 0) : m_hSema() { | ||
| 461 | assert(initialCount >= 0); | ||
| 462 | const long maxLong = 0x7fffffff; | ||
| 463 | m_hSema = CreateSemaphoreW(nullptr, initialCount, maxLong, nullptr); | ||
| 464 | assert(m_hSema); | ||
| 465 | } | ||
| 466 | |||
| 467 | AE_NO_TSAN ~Semaphore() { | ||
| 468 | CloseHandle(m_hSema); | ||
| 469 | } | ||
| 470 | |||
| 471 | bool wait() AE_NO_TSAN { | ||
| 472 | const unsigned long infinite = 0xffffffff; | ||
| 473 | return WaitForSingleObject(m_hSema, infinite) == 0; | ||
| 474 | } | ||
| 475 | |||
| 476 | bool try_wait() AE_NO_TSAN { | ||
| 477 | return WaitForSingleObject(m_hSema, 0) == 0; | ||
| 478 | } | ||
| 479 | |||
| 480 | bool timed_wait(std::uint64_t usecs) AE_NO_TSAN { | ||
| 481 | return WaitForSingleObject(m_hSema, (unsigned long)(usecs / 1000)) == 0; | ||
| 482 | } | ||
| 483 | |||
| 484 | void signal(int count = 1) AE_NO_TSAN { | ||
| 485 | while (!ReleaseSemaphore(m_hSema, count, nullptr)) | ||
| 486 | ; | ||
| 487 | } | ||
| 488 | }; | ||
| 489 | #elif defined(__MACH__) | ||
| 490 | //--------------------------------------------------------- | ||
| 491 | // Semaphore (Apple iOS and OSX) | ||
| 492 | // Can't use POSIX semaphores due to | ||
| 493 | // http://lists.apple.com/archives/darwin-kernel/2009/Apr/msg00010.html | ||
| 494 | //--------------------------------------------------------- | ||
| 495 | class Semaphore { | ||
| 496 | private: | ||
| 497 | semaphore_t m_sema; | ||
| 498 | |||
| 499 | Semaphore(const Semaphore& other); | ||
| 500 | Semaphore& operator=(const Semaphore& other); | ||
| 501 | |||
| 502 | public: | ||
| 503 | AE_NO_TSAN Semaphore(int initialCount = 0) : m_sema() { | ||
| 504 | assert(initialCount >= 0); | ||
| 505 | kern_return_t rc = | ||
| 506 | semaphore_create(mach_task_self(), &m_sema, SYNC_POLICY_FIFO, initialCount); | ||
| 507 | assert(rc == KERN_SUCCESS); | ||
| 508 | AE_UNUSED(rc); | ||
| 509 | } | ||
| 510 | |||
| 511 | AE_NO_TSAN ~Semaphore() { | ||
| 512 | semaphore_destroy(mach_task_self(), m_sema); | ||
| 513 | } | ||
| 514 | |||
| 515 | bool wait() AE_NO_TSAN { | ||
| 516 | return semaphore_wait(m_sema) == KERN_SUCCESS; | ||
| 517 | } | ||
| 518 | |||
| 519 | bool try_wait() AE_NO_TSAN { | ||
| 520 | return timed_wait(0); | ||
| 521 | } | ||
| 522 | |||
| 523 | bool timed_wait(std::uint64_t timeout_usecs) AE_NO_TSAN { | ||
| 524 | mach_timespec_t ts; | ||
| 525 | ts.tv_sec = static_cast<unsigned int>(timeout_usecs / 1000000); | ||
| 526 | ts.tv_nsec = static_cast<int>((timeout_usecs % 1000000) * 1000); | ||
| 527 | |||
| 528 | // added in OSX 10.10: | ||
| 529 | // https://developer.apple.com/library/prerelease/mac/documentation/General/Reference/APIDiffsMacOSX10_10SeedDiff/modules/Darwin.html | ||
| 530 | kern_return_t rc = semaphore_timedwait(m_sema, ts); | ||
| 531 | return rc == KERN_SUCCESS; | ||
| 532 | } | ||
| 533 | |||
| 534 | void signal() AE_NO_TSAN { | ||
| 535 | while (semaphore_signal(m_sema) != KERN_SUCCESS) | ||
| 536 | ; | ||
| 537 | } | ||
| 538 | |||
| 539 | void signal(int count) AE_NO_TSAN { | ||
| 540 | while (count-- > 0) { | ||
| 541 | while (semaphore_signal(m_sema) != KERN_SUCCESS) | ||
| 542 | ; | ||
| 543 | } | ||
| 544 | } | ||
| 545 | }; | ||
| 546 | #elif defined(__unix__) | ||
| 547 | //--------------------------------------------------------- | ||
| 548 | // Semaphore (POSIX, Linux) | ||
| 549 | //--------------------------------------------------------- | ||
| 550 | class Semaphore { | ||
| 551 | private: | ||
| 552 | sem_t m_sema; | ||
| 553 | |||
| 554 | Semaphore(const Semaphore& other); | ||
| 555 | Semaphore& operator=(const Semaphore& other); | ||
| 556 | |||
| 557 | public: | ||
| 558 | AE_NO_TSAN Semaphore(int initialCount = 0) : m_sema() { | ||
| 559 | assert(initialCount >= 0); | ||
| 560 | int rc = sem_init(&m_sema, 0, static_cast<unsigned int>(initialCount)); | ||
| 561 | assert(rc == 0); | ||
| 562 | AE_UNUSED(rc); | ||
| 563 | } | ||
| 564 | |||
| 565 | AE_NO_TSAN ~Semaphore() { | ||
| 566 | sem_destroy(&m_sema); | ||
| 567 | } | ||
| 568 | |||
| 569 | bool wait() AE_NO_TSAN { | ||
| 570 | // http://stackoverflow.com/questions/2013181/gdb-causes-sem-wait-to-fail-with-eintr-error | ||
| 571 | int rc; | ||
| 572 | do { | ||
| 573 | rc = sem_wait(&m_sema); | ||
| 574 | } while (rc == -1 && errno == EINTR); | ||
| 575 | return rc == 0; | ||
| 576 | } | ||
| 577 | |||
| 578 | bool try_wait() AE_NO_TSAN { | ||
| 579 | int rc; | ||
| 580 | do { | ||
| 581 | rc = sem_trywait(&m_sema); | ||
| 582 | } while (rc == -1 && errno == EINTR); | ||
| 583 | return rc == 0; | ||
| 584 | } | ||
| 585 | |||
| 586 | bool timed_wait(std::uint64_t usecs) AE_NO_TSAN { | ||
| 587 | struct timespec ts; | ||
| 588 | const int usecs_in_1_sec = 1000000; | ||
| 589 | const int nsecs_in_1_sec = 1000000000; | ||
| 590 | clock_gettime(CLOCK_REALTIME, &ts); | ||
| 591 | ts.tv_sec += static_cast<time_t>(usecs / usecs_in_1_sec); | ||
| 592 | ts.tv_nsec += static_cast<long>(usecs % usecs_in_1_sec) * 1000; | ||
| 593 | // sem_timedwait bombs if you have more than 1e9 in tv_nsec | ||
| 594 | // so we have to clean things up before passing it in | ||
| 595 | if (ts.tv_nsec >= nsecs_in_1_sec) { | ||
| 596 | ts.tv_nsec -= nsecs_in_1_sec; | ||
| 597 | ++ts.tv_sec; | ||
| 598 | } | ||
| 599 | |||
| 600 | int rc; | ||
| 601 | do { | ||
| 602 | rc = sem_timedwait(&m_sema, &ts); | ||
| 603 | } while (rc == -1 && errno == EINTR); | ||
| 604 | return rc == 0; | ||
| 605 | } | ||
| 606 | |||
| 607 | void signal() AE_NO_TSAN { | ||
| 608 | while (sem_post(&m_sema) == -1) | ||
| 609 | ; | ||
| 610 | } | ||
| 611 | |||
| 612 | void signal(int count) AE_NO_TSAN { | ||
| 613 | while (count-- > 0) { | ||
| 614 | while (sem_post(&m_sema) == -1) | ||
| 615 | ; | ||
| 616 | } | ||
| 617 | } | ||
| 618 | }; | ||
| 619 | #elif defined(FREERTOS) | ||
| 620 | //--------------------------------------------------------- | ||
| 621 | // Semaphore (FreeRTOS) | ||
| 622 | //--------------------------------------------------------- | ||
| 623 | class Semaphore { | ||
| 624 | private: | ||
| 625 | SemaphoreHandle_t m_sema; | ||
| 626 | |||
| 627 | Semaphore(const Semaphore& other); | ||
| 628 | Semaphore& operator=(const Semaphore& other); | ||
| 629 | |||
| 630 | public: | ||
| 631 | AE_NO_TSAN Semaphore(int initialCount = 0) : m_sema() { | ||
| 632 | assert(initialCount >= 0); | ||
| 633 | m_sema = xSemaphoreCreateCounting(static_cast<UBaseType_t>(~0ull), | ||
| 634 | static_cast<UBaseType_t>(initialCount)); | ||
| 635 | assert(m_sema); | ||
| 636 | } | ||
| 637 | |||
| 638 | AE_NO_TSAN ~Semaphore() { | ||
| 639 | vSemaphoreDelete(m_sema); | ||
| 640 | } | ||
| 641 | |||
| 642 | bool wait() AE_NO_TSAN { | ||
| 643 | return xSemaphoreTake(m_sema, portMAX_DELAY) == pdTRUE; | ||
| 644 | } | ||
| 645 | |||
| 646 | bool try_wait() AE_NO_TSAN { | ||
| 647 | // Note: In an ISR context, if this causes a task to unblock, | ||
| 648 | // the caller won't know about it | ||
| 649 | if (xPortIsInsideInterrupt()) | ||
| 650 | return xSemaphoreTakeFromISR(m_sema, NULL) == pdTRUE; | ||
| 651 | return xSemaphoreTake(m_sema, 0) == pdTRUE; | ||
| 652 | } | ||
| 653 | |||
| 654 | bool timed_wait(std::uint64_t usecs) AE_NO_TSAN { | ||
| 655 | std::uint64_t msecs = usecs / 1000; | ||
| 656 | TickType_t ticks = static_cast<TickType_t>(msecs / portTICK_PERIOD_MS); | ||
| 657 | if (ticks == 0) | ||
| 658 | return try_wait(); | ||
| 659 | return xSemaphoreTake(m_sema, ticks) == pdTRUE; | ||
| 660 | } | ||
| 661 | |||
| 662 | void signal() AE_NO_TSAN { | ||
| 663 | // Note: In an ISR context, if this causes a task to unblock, | ||
| 664 | // the caller won't know about it | ||
| 665 | BaseType_t rc; | ||
| 666 | if (xPortIsInsideInterrupt()) | ||
| 667 | rc = xSemaphoreGiveFromISR(m_sema, NULL); | ||
| 668 | else | ||
| 669 | rc = xSemaphoreGive(m_sema); | ||
| 670 | assert(rc == pdTRUE); | ||
| 671 | AE_UNUSED(rc); | ||
| 672 | } | ||
| 673 | |||
| 674 | void signal(int count) AE_NO_TSAN { | ||
| 675 | while (count-- > 0) | ||
| 676 | signal(); | ||
| 677 | } | ||
| 678 | }; | ||
| 679 | #else | ||
| 680 | #error Unsupported platform! (No semaphore wrapper available) | ||
| 681 | #endif | ||
| 682 | |||
| 683 | //--------------------------------------------------------- | ||
| 684 | // LightweightSemaphore | ||
| 685 | //--------------------------------------------------------- | ||
| 686 | class LightweightSemaphore { | ||
| 687 | public: | ||
| 688 | typedef std::make_signed<std::size_t>::type ssize_t; | ||
| 689 | |||
| 690 | private: | ||
| 691 | weak_atomic<ssize_t> m_count; | ||
| 692 | Semaphore m_sema; | ||
| 693 | |||
| 694 | bool waitWithPartialSpinning(std::int64_t timeout_usecs = -1) AE_NO_TSAN { | ||
| 695 | ssize_t oldCount; | ||
| 696 | // Is there a better way to set the initial spin count? | ||
| 697 | // If we lower it to 1000, testBenaphore becomes 15x slower on my Core i7-5930K Windows PC, | ||
| 698 | // as threads start hitting the kernel semaphore. | ||
| 699 | int spin = 1024; | ||
| 700 | while (--spin >= 0) { | ||
| 701 | if (m_count.load() > 0) { | ||
| 702 | m_count.fetch_add_acquire(-1); | ||
| 703 | return true; | ||
| 704 | } | ||
| 705 | compiler_fence(memory_order_acquire); // Prevent the compiler from collapsing the loop. | ||
| 706 | } | ||
| 707 | oldCount = m_count.fetch_add_acquire(-1); | ||
| 708 | if (oldCount > 0) | ||
| 709 | return true; | ||
| 710 | if (timeout_usecs < 0) { | ||
| 711 | if (m_sema.wait()) | ||
| 712 | return true; | ||
| 713 | } | ||
| 714 | if (timeout_usecs > 0 && m_sema.timed_wait(static_cast<uint64_t>(timeout_usecs))) | ||
| 715 | return true; | ||
| 716 | // At this point, we've timed out waiting for the semaphore, but the | ||
| 717 | // count is still decremented indicating we may still be waiting on | ||
| 718 | // it. So we have to re-adjust the count, but only if the semaphore | ||
| 719 | // wasn't signaled enough times for us too since then. If it was, we | ||
| 720 | // need to release the semaphore too. | ||
| 721 | while (true) { | ||
| 722 | oldCount = m_count.fetch_add_release(1); | ||
| 723 | if (oldCount < 0) | ||
| 724 | return false; // successfully restored things to the way they were | ||
| 725 | // Oh, the producer thread just signaled the semaphore after all. Try again: | ||
| 726 | oldCount = m_count.fetch_add_acquire(-1); | ||
| 727 | if (oldCount > 0 && m_sema.try_wait()) | ||
| 728 | return true; | ||
| 729 | } | ||
| 730 | } | ||
| 731 | |||
| 732 | public: | ||
| 733 | AE_NO_TSAN LightweightSemaphore(ssize_t initialCount = 0) : m_count(initialCount), m_sema() { | ||
| 734 | assert(initialCount >= 0); | ||
| 735 | } | ||
| 736 | |||
| 737 | bool tryWait() AE_NO_TSAN { | ||
| 738 | if (m_count.load() > 0) { | ||
| 739 | m_count.fetch_add_acquire(-1); | ||
| 740 | return true; | ||
| 741 | } | ||
| 742 | return false; | ||
| 743 | } | ||
| 744 | |||
| 745 | bool wait() AE_NO_TSAN { | ||
| 746 | return tryWait() || waitWithPartialSpinning(); | ||
| 747 | } | ||
| 748 | |||
| 749 | bool wait(std::int64_t timeout_usecs) AE_NO_TSAN { | ||
| 750 | return tryWait() || waitWithPartialSpinning(timeout_usecs); | ||
| 751 | } | ||
| 752 | |||
| 753 | void signal(ssize_t count = 1) AE_NO_TSAN { | ||
| 754 | assert(count >= 0); | ||
| 755 | ssize_t oldCount = m_count.fetch_add_release(count); | ||
| 756 | assert(oldCount >= -1); | ||
| 757 | if (oldCount < 0) { | ||
| 758 | m_sema.signal(1); | ||
| 759 | } | ||
| 760 | } | ||
| 761 | |||
| 762 | std::size_t availableApprox() const AE_NO_TSAN { | ||
| 763 | ssize_t count = m_count.load(); | ||
| 764 | return count > 0 ? static_cast<std::size_t>(count) : 0; | ||
| 765 | } | ||
| 766 | }; | ||
| 767 | } // namespace spsc_sema | ||
| 768 | } // namespace Common | ||
| 769 | |||
| 770 | #if defined(AE_VCPP) && (_MSC_VER < 1700 || defined(__cplusplus_cli)) | ||
| 771 | #pragma warning(pop) | ||
| 772 | #ifdef __cplusplus_cli | ||
| 773 | #pragma managed(pop) | ||
| 774 | #endif | ||
| 775 | #endif | ||
diff --git a/src/common/atomic_ops.h b/src/common/atomic_ops.h index 2b1f515e8..c18bb33c4 100644 --- a/src/common/atomic_ops.h +++ b/src/common/atomic_ops.h | |||
| @@ -1,16 +1,14 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | // Refer to the license.txt file included. | ||
| 4 | 3 | ||
| 5 | #pragma once | 4 | #pragma once |
| 6 | 5 | ||
| 7 | #include <cstring> | ||
| 8 | #include <memory> | ||
| 9 | |||
| 10 | #include "common/common_types.h" | 6 | #include "common/common_types.h" |
| 11 | 7 | ||
| 12 | #if _MSC_VER | 8 | #if _MSC_VER |
| 13 | #include <intrin.h> | 9 | #include <intrin.h> |
| 10 | #else | ||
| 11 | #include <cstring> | ||
| 14 | #endif | 12 | #endif |
| 15 | 13 | ||
| 16 | namespace Common { | 14 | namespace Common { |
| @@ -47,6 +45,50 @@ namespace Common { | |||
| 47 | reinterpret_cast<__int64*>(expected.data())) != 0; | 45 | reinterpret_cast<__int64*>(expected.data())) != 0; |
| 48 | } | 46 | } |
| 49 | 47 | ||
| 48 | [[nodiscard]] inline bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected, | ||
| 49 | u8& actual) { | ||
| 50 | actual = | ||
| 51 | _InterlockedCompareExchange8(reinterpret_cast<volatile char*>(pointer), value, expected); | ||
| 52 | return actual == expected; | ||
| 53 | } | ||
| 54 | |||
| 55 | [[nodiscard]] inline bool AtomicCompareAndSwap(volatile u16* pointer, u16 value, u16 expected, | ||
| 56 | u16& actual) { | ||
| 57 | actual = | ||
| 58 | _InterlockedCompareExchange16(reinterpret_cast<volatile short*>(pointer), value, expected); | ||
| 59 | return actual == expected; | ||
| 60 | } | ||
| 61 | |||
| 62 | [[nodiscard]] inline bool AtomicCompareAndSwap(volatile u32* pointer, u32 value, u32 expected, | ||
| 63 | u32& actual) { | ||
| 64 | actual = | ||
| 65 | _InterlockedCompareExchange(reinterpret_cast<volatile long*>(pointer), value, expected); | ||
| 66 | return actual == expected; | ||
| 67 | } | ||
| 68 | |||
| 69 | [[nodiscard]] inline bool AtomicCompareAndSwap(volatile u64* pointer, u64 value, u64 expected, | ||
| 70 | u64& actual) { | ||
| 71 | actual = _InterlockedCompareExchange64(reinterpret_cast<volatile __int64*>(pointer), value, | ||
| 72 | expected); | ||
| 73 | return actual == expected; | ||
| 74 | } | ||
| 75 | |||
| 76 | [[nodiscard]] inline bool AtomicCompareAndSwap(volatile u64* pointer, u128 value, u128 expected, | ||
| 77 | u128& actual) { | ||
| 78 | const bool result = | ||
| 79 | _InterlockedCompareExchange128(reinterpret_cast<volatile __int64*>(pointer), value[1], | ||
| 80 | value[0], reinterpret_cast<__int64*>(expected.data())) != 0; | ||
| 81 | actual = expected; | ||
| 82 | return result; | ||
| 83 | } | ||
| 84 | |||
| 85 | [[nodiscard]] inline u128 AtomicLoad128(volatile u64* pointer) { | ||
| 86 | u128 result{}; | ||
| 87 | _InterlockedCompareExchange128(reinterpret_cast<volatile __int64*>(pointer), result[1], | ||
| 88 | result[0], reinterpret_cast<__int64*>(result.data())); | ||
| 89 | return result; | ||
| 90 | } | ||
| 91 | |||
| 50 | #else | 92 | #else |
| 51 | 93 | ||
| 52 | [[nodiscard]] inline bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected) { | 94 | [[nodiscard]] inline bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected) { |
| @@ -73,6 +115,52 @@ namespace Common { | |||
| 73 | return __sync_bool_compare_and_swap((unsigned __int128*)pointer, expected_a, value_a); | 115 | return __sync_bool_compare_and_swap((unsigned __int128*)pointer, expected_a, value_a); |
| 74 | } | 116 | } |
| 75 | 117 | ||
| 118 | [[nodiscard]] inline bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected, | ||
| 119 | u8& actual) { | ||
| 120 | actual = __sync_val_compare_and_swap(pointer, expected, value); | ||
| 121 | return actual == expected; | ||
| 122 | } | ||
| 123 | |||
| 124 | [[nodiscard]] inline bool AtomicCompareAndSwap(volatile u16* pointer, u16 value, u16 expected, | ||
| 125 | u16& actual) { | ||
| 126 | actual = __sync_val_compare_and_swap(pointer, expected, value); | ||
| 127 | return actual == expected; | ||
| 128 | } | ||
| 129 | |||
| 130 | [[nodiscard]] inline bool AtomicCompareAndSwap(volatile u32* pointer, u32 value, u32 expected, | ||
| 131 | u32& actual) { | ||
| 132 | actual = __sync_val_compare_and_swap(pointer, expected, value); | ||
| 133 | return actual == expected; | ||
| 134 | } | ||
| 135 | |||
| 136 | [[nodiscard]] inline bool AtomicCompareAndSwap(volatile u64* pointer, u64 value, u64 expected, | ||
| 137 | u64& actual) { | ||
| 138 | actual = __sync_val_compare_and_swap(pointer, expected, value); | ||
| 139 | return actual == expected; | ||
| 140 | } | ||
| 141 | |||
| 142 | [[nodiscard]] inline bool AtomicCompareAndSwap(volatile u64* pointer, u128 value, u128 expected, | ||
| 143 | u128& actual) { | ||
| 144 | unsigned __int128 value_a; | ||
| 145 | unsigned __int128 expected_a; | ||
| 146 | unsigned __int128 actual_a; | ||
| 147 | std::memcpy(&value_a, value.data(), sizeof(u128)); | ||
| 148 | std::memcpy(&expected_a, expected.data(), sizeof(u128)); | ||
| 149 | actual_a = __sync_val_compare_and_swap((unsigned __int128*)pointer, expected_a, value_a); | ||
| 150 | std::memcpy(actual.data(), &actual_a, sizeof(u128)); | ||
| 151 | return actual_a == expected_a; | ||
| 152 | } | ||
| 153 | |||
| 154 | [[nodiscard]] inline u128 AtomicLoad128(volatile u64* pointer) { | ||
| 155 | unsigned __int128 zeros_a = 0; | ||
| 156 | unsigned __int128 result_a = | ||
| 157 | __sync_val_compare_and_swap((unsigned __int128*)pointer, zeros_a, zeros_a); | ||
| 158 | |||
| 159 | u128 result; | ||
| 160 | std::memcpy(result.data(), &result_a, sizeof(u128)); | ||
| 161 | return result; | ||
| 162 | } | ||
| 163 | |||
| 76 | #endif | 164 | #endif |
| 77 | 165 | ||
| 78 | } // namespace Common | 166 | } // namespace Common |
diff --git a/src/common/bit_cast.h b/src/common/bit_cast.h index a32a063d1..535148b4d 100644 --- a/src/common/bit_cast.h +++ b/src/common/bit_cast.h | |||
| @@ -1,6 +1,5 @@ | |||
| 1 | // Copyright 2020 yuzu emulator team | 1 | // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | // Refer to the license.txt file included. | ||
| 4 | 3 | ||
| 5 | #pragma once | 4 | #pragma once |
| 6 | 5 | ||
diff --git a/src/common/bit_field.h b/src/common/bit_field.h index 0f0661172..7e1df62b1 100644 --- a/src/common/bit_field.h +++ b/src/common/bit_field.h | |||
| @@ -1,39 +1,12 @@ | |||
| 1 | // Licensed under GPLv2 or any later version | 1 | // SPDX-FileCopyrightText: 2014 Tony Wasserka |
| 2 | // Refer to the license.txt file included. | 2 | // SPDX-FileCopyrightText: 2014 Dolphin Emulator Project |
| 3 | 3 | // SPDX-License-Identifier: BSD-3-Clause AND GPL-2.0-or-later | |
| 4 | // Copyright 2014 Tony Wasserka | ||
| 5 | // All rights reserved. | ||
| 6 | // | ||
| 7 | // Redistribution and use in source and binary forms, with or without | ||
| 8 | // modification, are permitted provided that the following conditions are met: | ||
| 9 | // | ||
| 10 | // * Redistributions of source code must retain the above copyright | ||
| 11 | // notice, this list of conditions and the following disclaimer. | ||
| 12 | // * Redistributions in binary form must reproduce the above copyright | ||
| 13 | // notice, this list of conditions and the following disclaimer in the | ||
| 14 | // documentation and/or other materials provided with the distribution. | ||
| 15 | // * Neither the name of the owner nor the names of its contributors may | ||
| 16 | // be used to endorse or promote products derived from this software | ||
| 17 | // without specific prior written permission. | ||
| 18 | // | ||
| 19 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
| 20 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
| 21 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||
| 22 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
| 23 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| 24 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||
| 25 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
| 26 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
| 27 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
| 28 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
| 29 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| 30 | 4 | ||
| 31 | #pragma once | 5 | #pragma once |
| 32 | 6 | ||
| 33 | #include <cstddef> | 7 | #include <cstddef> |
| 34 | #include <limits> | 8 | #include <limits> |
| 35 | #include <type_traits> | 9 | #include <type_traits> |
| 36 | #include "common/common_funcs.h" | ||
| 37 | #include "common/swap.h" | 10 | #include "common/swap.h" |
| 38 | 11 | ||
| 39 | /* | 12 | /* |
| @@ -173,7 +146,16 @@ public: | |||
| 173 | } | 146 | } |
| 174 | 147 | ||
| 175 | constexpr void Assign(const T& value) { | 148 | constexpr void Assign(const T& value) { |
| 149 | #ifdef _MSC_VER | ||
| 176 | storage = static_cast<StorageType>((storage & ~mask) | FormatValue(value)); | 150 | storage = static_cast<StorageType>((storage & ~mask) | FormatValue(value)); |
| 151 | #else | ||
| 152 | // Explicitly reload with memcpy to avoid compiler aliasing quirks | ||
| 153 | // regarding optimization: GCC/Clang clobber chained stores to | ||
| 154 | // different bitfields in the same struct with the last value. | ||
| 155 | StorageTypeWithEndian storage_; | ||
| 156 | std::memcpy(&storage_, &storage, sizeof(storage_)); | ||
| 157 | storage = static_cast<StorageType>((storage_ & ~mask) | FormatValue(value)); | ||
| 158 | #endif | ||
| 177 | } | 159 | } |
| 178 | 160 | ||
| 179 | [[nodiscard]] constexpr T Value() const { | 161 | [[nodiscard]] constexpr T Value() const { |
diff --git a/src/common/bit_set.h b/src/common/bit_set.h index 9235ad412..74754504b 100644 --- a/src/common/bit_set.h +++ b/src/common/bit_set.h | |||
| @@ -1,18 +1,5 @@ | |||
| 1 | /* | 1 | // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project |
| 2 | * Copyright (c) 2018-2020 Atmosphère-NX | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify it | ||
| 5 | * under the terms and conditions of the GNU General Public License, | ||
| 6 | * version 2, as published by the Free Software Foundation. | ||
| 7 | * | ||
| 8 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
| 9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 11 | * more details. | ||
| 12 | * | ||
| 13 | * You should have received a copy of the GNU General Public License | ||
| 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 15 | */ | ||
| 16 | 3 | ||
| 17 | #pragma once | 4 | #pragma once |
| 18 | 5 | ||
diff --git a/src/common/bit_util.h b/src/common/bit_util.h index f50d3308a..e4e6287f3 100644 --- a/src/common/bit_util.h +++ b/src/common/bit_util.h | |||
| @@ -1,6 +1,5 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | 1 | // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | // Refer to the license.txt file included. | ||
| 4 | 3 | ||
| 5 | #pragma once | 4 | #pragma once |
| 6 | 5 | ||
| @@ -57,4 +56,11 @@ requires std::is_integral_v<T> | |||
| 57 | return static_cast<T>(1ULL << ((8U * sizeof(T)) - std::countl_zero(value - 1U))); | 56 | return static_cast<T>(1ULL << ((8U * sizeof(T)) - std::countl_zero(value - 1U))); |
| 58 | } | 57 | } |
| 59 | 58 | ||
| 59 | template <size_t bit_index, typename T> | ||
| 60 | requires std::is_integral_v<T> | ||
| 61 | [[nodiscard]] constexpr bool Bit(const T value) { | ||
| 62 | static_assert(bit_index < BitSize<T>(), "bit_index must be smaller than size of T"); | ||
| 63 | return ((value >> bit_index) & T(1)) == T(1); | ||
| 64 | } | ||
| 65 | |||
| 60 | } // namespace Common | 66 | } // namespace Common |
diff --git a/src/common/bounded_threadsafe_queue.h b/src/common/bounded_threadsafe_queue.h new file mode 100644 index 000000000..7e465549b --- /dev/null +++ b/src/common/bounded_threadsafe_queue.h | |||
| @@ -0,0 +1,167 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright (c) 2020 Erik Rigtorp <erik@rigtorp.se> | ||
| 2 | // SPDX-License-Identifier: MIT | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <atomic> | ||
| 7 | #include <bit> | ||
| 8 | #include <condition_variable> | ||
| 9 | #include <memory> | ||
| 10 | #include <mutex> | ||
| 11 | #include <new> | ||
| 12 | #include <stop_token> | ||
| 13 | #include <type_traits> | ||
| 14 | #include <utility> | ||
| 15 | |||
| 16 | namespace Common { | ||
| 17 | |||
| 18 | #if defined(__cpp_lib_hardware_interference_size) | ||
| 19 | constexpr size_t hardware_interference_size = std::hardware_destructive_interference_size; | ||
| 20 | #else | ||
| 21 | constexpr size_t hardware_interference_size = 64; | ||
| 22 | #endif | ||
| 23 | |||
| 24 | #ifdef _MSC_VER | ||
| 25 | #pragma warning(push) | ||
| 26 | #pragma warning(disable : 4324) | ||
| 27 | #endif | ||
| 28 | |||
| 29 | template <typename T, size_t capacity = 0x400> | ||
| 30 | class MPSCQueue { | ||
| 31 | public: | ||
| 32 | explicit MPSCQueue() : allocator{std::allocator<Slot<T>>()} { | ||
| 33 | // Allocate one extra slot to prevent false sharing on the last slot | ||
| 34 | slots = allocator.allocate(capacity + 1); | ||
| 35 | // Allocators are not required to honor alignment for over-aligned types | ||
| 36 | // (see http://eel.is/c++draft/allocator.requirements#10) so we verify | ||
| 37 | // alignment here | ||
| 38 | if (reinterpret_cast<uintptr_t>(slots) % alignof(Slot<T>) != 0) { | ||
| 39 | allocator.deallocate(slots, capacity + 1); | ||
| 40 | throw std::bad_alloc(); | ||
| 41 | } | ||
| 42 | for (size_t i = 0; i < capacity; ++i) { | ||
| 43 | std::construct_at(&slots[i]); | ||
| 44 | } | ||
| 45 | static_assert(std::has_single_bit(capacity), "capacity must be an integer power of 2"); | ||
| 46 | static_assert(alignof(Slot<T>) == hardware_interference_size, | ||
| 47 | "Slot must be aligned to cache line boundary to prevent false sharing"); | ||
| 48 | static_assert(sizeof(Slot<T>) % hardware_interference_size == 0, | ||
| 49 | "Slot size must be a multiple of cache line size to prevent " | ||
| 50 | "false sharing between adjacent slots"); | ||
| 51 | static_assert(sizeof(MPSCQueue) % hardware_interference_size == 0, | ||
| 52 | "Queue size must be a multiple of cache line size to " | ||
| 53 | "prevent false sharing between adjacent queues"); | ||
| 54 | } | ||
| 55 | |||
| 56 | ~MPSCQueue() noexcept { | ||
| 57 | for (size_t i = 0; i < capacity; ++i) { | ||
| 58 | std::destroy_at(&slots[i]); | ||
| 59 | } | ||
| 60 | allocator.deallocate(slots, capacity + 1); | ||
| 61 | } | ||
| 62 | |||
| 63 | // The queue must be both non-copyable and non-movable | ||
| 64 | MPSCQueue(const MPSCQueue&) = delete; | ||
| 65 | MPSCQueue& operator=(const MPSCQueue&) = delete; | ||
| 66 | |||
| 67 | MPSCQueue(MPSCQueue&&) = delete; | ||
| 68 | MPSCQueue& operator=(MPSCQueue&&) = delete; | ||
| 69 | |||
| 70 | void Push(const T& v) noexcept { | ||
| 71 | static_assert(std::is_nothrow_copy_constructible_v<T>, | ||
| 72 | "T must be nothrow copy constructible"); | ||
| 73 | emplace(v); | ||
| 74 | } | ||
| 75 | |||
| 76 | template <typename P, typename = std::enable_if_t<std::is_nothrow_constructible_v<T, P&&>>> | ||
| 77 | void Push(P&& v) noexcept { | ||
| 78 | emplace(std::forward<P>(v)); | ||
| 79 | } | ||
| 80 | |||
| 81 | void Pop(T& v, std::stop_token stop) noexcept { | ||
| 82 | auto const tail = tail_.fetch_add(1); | ||
| 83 | auto& slot = slots[idx(tail)]; | ||
| 84 | if (!slot.turn.test()) { | ||
| 85 | std::unique_lock lock{cv_mutex}; | ||
| 86 | cv.wait(lock, stop, [&slot] { return slot.turn.test(); }); | ||
| 87 | } | ||
| 88 | v = slot.move(); | ||
| 89 | slot.destroy(); | ||
| 90 | slot.turn.clear(); | ||
| 91 | slot.turn.notify_one(); | ||
| 92 | } | ||
| 93 | |||
| 94 | private: | ||
| 95 | template <typename U = T> | ||
| 96 | struct Slot { | ||
| 97 | ~Slot() noexcept { | ||
| 98 | if (turn.test()) { | ||
| 99 | destroy(); | ||
| 100 | } | ||
| 101 | } | ||
| 102 | |||
| 103 | template <typename... Args> | ||
| 104 | void construct(Args&&... args) noexcept { | ||
| 105 | static_assert(std::is_nothrow_constructible_v<U, Args&&...>, | ||
| 106 | "T must be nothrow constructible with Args&&..."); | ||
| 107 | std::construct_at(reinterpret_cast<U*>(&storage), std::forward<Args>(args)...); | ||
| 108 | } | ||
| 109 | |||
| 110 | void destroy() noexcept { | ||
| 111 | static_assert(std::is_nothrow_destructible_v<U>, "T must be nothrow destructible"); | ||
| 112 | std::destroy_at(reinterpret_cast<U*>(&storage)); | ||
| 113 | } | ||
| 114 | |||
| 115 | U&& move() noexcept { | ||
| 116 | return reinterpret_cast<U&&>(storage); | ||
| 117 | } | ||
| 118 | |||
| 119 | // Align to avoid false sharing between adjacent slots | ||
| 120 | alignas(hardware_interference_size) std::atomic_flag turn{}; | ||
| 121 | struct aligned_store { | ||
| 122 | struct type { | ||
| 123 | alignas(U) unsigned char data[sizeof(U)]; | ||
| 124 | }; | ||
| 125 | }; | ||
| 126 | typename aligned_store::type storage; | ||
| 127 | }; | ||
| 128 | |||
| 129 | template <typename... Args> | ||
| 130 | void emplace(Args&&... args) noexcept { | ||
| 131 | static_assert(std::is_nothrow_constructible_v<T, Args&&...>, | ||
| 132 | "T must be nothrow constructible with Args&&..."); | ||
| 133 | auto const head = head_.fetch_add(1); | ||
| 134 | auto& slot = slots[idx(head)]; | ||
| 135 | slot.turn.wait(true); | ||
| 136 | slot.construct(std::forward<Args>(args)...); | ||
| 137 | slot.turn.test_and_set(); | ||
| 138 | cv.notify_one(); | ||
| 139 | } | ||
| 140 | |||
| 141 | constexpr size_t idx(size_t i) const noexcept { | ||
| 142 | return i & mask; | ||
| 143 | } | ||
| 144 | |||
| 145 | static constexpr size_t mask = capacity - 1; | ||
| 146 | |||
| 147 | // Align to avoid false sharing between head_ and tail_ | ||
| 148 | alignas(hardware_interference_size) std::atomic<size_t> head_{0}; | ||
| 149 | alignas(hardware_interference_size) std::atomic<size_t> tail_{0}; | ||
| 150 | |||
| 151 | std::mutex cv_mutex; | ||
| 152 | std::condition_variable_any cv; | ||
| 153 | |||
| 154 | Slot<T>* slots; | ||
| 155 | [[no_unique_address]] std::allocator<Slot<T>> allocator; | ||
| 156 | |||
| 157 | static_assert(std::is_nothrow_copy_assignable_v<T> || std::is_nothrow_move_assignable_v<T>, | ||
| 158 | "T must be nothrow copy or move assignable"); | ||
| 159 | |||
| 160 | static_assert(std::is_nothrow_destructible_v<T>, "T must be nothrow destructible"); | ||
| 161 | }; | ||
| 162 | |||
| 163 | #ifdef _MSC_VER | ||
| 164 | #pragma warning(pop) | ||
| 165 | #endif | ||
| 166 | |||
| 167 | } // namespace Common | ||
diff --git a/src/common/cityhash.cpp b/src/common/cityhash.cpp index 66218fc21..d50ac9e6c 100644 --- a/src/common/cityhash.cpp +++ b/src/common/cityhash.cpp | |||
| @@ -1,23 +1,8 @@ | |||
| 1 | // Copyright (c) 2011 Google, Inc. | 1 | // SPDX-FileCopyrightText: 2011 Google, Inc. |
| 2 | // | 2 | // SPDX-FileContributor: Geoff Pike |
| 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy | 3 | // SPDX-FileContributor: Jyrki Alakuijala |
| 4 | // of this software and associated documentation files (the "Software"), to deal | 4 | // SPDX-License-Identifier: MIT |
| 5 | // in the Software without restriction, including without limitation the rights | 5 | |
| 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
| 7 | // copies of the Software, and to permit persons to whom the Software is | ||
| 8 | // furnished to do so, subject to the following conditions: | ||
| 9 | // | ||
| 10 | // The above copyright notice and this permission notice shall be included in | ||
| 11 | // all copies or substantial portions of the Software. | ||
| 12 | // | ||
| 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
| 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
| 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
| 19 | // THE SOFTWARE. | ||
| 20 | // | ||
| 21 | // CityHash, by Geoff Pike and Jyrki Alakuijala | 6 | // CityHash, by Geoff Pike and Jyrki Alakuijala |
| 22 | // | 7 | // |
| 23 | // This file provides CityHash64() and related functions. | 8 | // This file provides CityHash64() and related functions. |
diff --git a/src/common/cityhash.h b/src/common/cityhash.h index d74fc7639..627ba81cd 100644 --- a/src/common/cityhash.h +++ b/src/common/cityhash.h | |||
| @@ -1,23 +1,8 @@ | |||
| 1 | // Copyright (c) 2011 Google, Inc. | 1 | // SPDX-FileCopyrightText: 2011 Google, Inc. |
| 2 | // | 2 | // SPDX-FileContributor: Geoff Pike |
| 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy | 3 | // SPDX-FileContributor: Jyrki Alakuijala |
| 4 | // of this software and associated documentation files (the "Software"), to deal | 4 | // SPDX-License-Identifier: MIT |
| 5 | // in the Software without restriction, including without limitation the rights | 5 | |
| 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
| 7 | // copies of the Software, and to permit persons to whom the Software is | ||
| 8 | // furnished to do so, subject to the following conditions: | ||
| 9 | // | ||
| 10 | // The above copyright notice and this permission notice shall be included in | ||
| 11 | // all copies or substantial portions of the Software. | ||
| 12 | // | ||
| 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
| 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
| 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
| 19 | // THE SOFTWARE. | ||
| 20 | // | ||
| 21 | // CityHash, by Geoff Pike and Jyrki Alakuijala | 6 | // CityHash, by Geoff Pike and Jyrki Alakuijala |
| 22 | // | 7 | // |
| 23 | // http://code.google.com/p/cityhash/ | 8 | // http://code.google.com/p/cityhash/ |
diff --git a/src/common/common_funcs.h b/src/common/common_funcs.h index 4c1e29de6..e1e2a90fc 100644 --- a/src/common/common_funcs.h +++ b/src/common/common_funcs.h | |||
| @@ -1,6 +1,5 @@ | |||
| 1 | // Copyright 2019 yuzu emulator team | 1 | // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | // Refer to the license.txt file included. | ||
| 4 | 3 | ||
| 5 | #pragma once | 4 | #pragma once |
| 6 | 5 | ||
| @@ -19,14 +18,16 @@ | |||
| 19 | /// Helper macros to insert unused bytes or words to properly align structs. These values will be | 18 | /// Helper macros to insert unused bytes or words to properly align structs. These values will be |
| 20 | /// zero-initialized. | 19 | /// zero-initialized. |
| 21 | #define INSERT_PADDING_BYTES(num_bytes) \ | 20 | #define INSERT_PADDING_BYTES(num_bytes) \ |
| 22 | std::array<u8, num_bytes> CONCAT2(pad, __LINE__) {} | 21 | [[maybe_unused]] std::array<u8, num_bytes> CONCAT2(pad, __LINE__) {} |
| 23 | #define INSERT_PADDING_WORDS(num_words) \ | 22 | #define INSERT_PADDING_WORDS(num_words) \ |
| 24 | std::array<u32, num_words> CONCAT2(pad, __LINE__) {} | 23 | [[maybe_unused]] std::array<u32, num_words> CONCAT2(pad, __LINE__) {} |
| 25 | 24 | ||
| 26 | /// These are similar to the INSERT_PADDING_* macros but do not zero-initialize the contents. | 25 | /// These are similar to the INSERT_PADDING_* macros but do not zero-initialize the contents. |
| 27 | /// This keeps the structure trivial to construct. | 26 | /// This keeps the structure trivial to construct. |
| 28 | #define INSERT_PADDING_BYTES_NOINIT(num_bytes) std::array<u8, num_bytes> CONCAT2(pad, __LINE__) | 27 | #define INSERT_PADDING_BYTES_NOINIT(num_bytes) \ |
| 29 | #define INSERT_PADDING_WORDS_NOINIT(num_words) std::array<u32, num_words> CONCAT2(pad, __LINE__) | 28 | [[maybe_unused]] std::array<u8, num_bytes> CONCAT2(pad, __LINE__) |
| 29 | #define INSERT_PADDING_WORDS_NOINIT(num_words) \ | ||
| 30 | [[maybe_unused]] std::array<u32, num_words> CONCAT2(pad, __LINE__) | ||
| 30 | 31 | ||
| 31 | #ifndef _MSC_VER | 32 | #ifndef _MSC_VER |
| 32 | 33 | ||
diff --git a/src/common/common_types.h b/src/common/common_types.h index 99bffc460..0fc225aff 100644 --- a/src/common/common_types.h +++ b/src/common/common_types.h | |||
| @@ -1,3 +1,7 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2012 Gekko Emulator | ||
| 2 | // SPDX-FileContributor: ShizZy <shizzy247@gmail.com> | ||
| 3 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 4 | |||
| 1 | /** | 5 | /** |
| 2 | * Copyright (C) 2005-2012 Gekko Emulator | 6 | * Copyright (C) 2005-2012 Gekko Emulator |
| 3 | * | 7 | * |
diff --git a/src/common/concepts.h b/src/common/concepts.h index aa08065a7..a97555f6a 100644 --- a/src/common/concepts.h +++ b/src/common/concepts.h | |||
| @@ -1,6 +1,5 @@ | |||
| 1 | // Copyright 2020 yuzu emulator team | 1 | // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | // Refer to the license.txt file included. | ||
| 4 | 3 | ||
| 5 | #pragma once | 4 | #pragma once |
| 6 | 5 | ||
diff --git a/src/common/detached_tasks.cpp b/src/common/detached_tasks.cpp index c1362631e..da64848da 100644 --- a/src/common/detached_tasks.cpp +++ b/src/common/detached_tasks.cpp | |||
| @@ -1,6 +1,5 @@ | |||
| 1 | // Copyright 2018 Citra Emulator Project | 1 | // SPDX-FileCopyrightText: 2018 Citra Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | // Refer to the license.txt file included. | ||
| 4 | 3 | ||
| 5 | #include <thread> | 4 | #include <thread> |
| 6 | #include "common/assert.h" | 5 | #include "common/assert.h" |
| @@ -33,9 +32,9 @@ void DetachedTasks::AddTask(std::function<void()> task) { | |||
| 33 | ++instance->count; | 32 | ++instance->count; |
| 34 | std::thread([task{std::move(task)}]() { | 33 | std::thread([task{std::move(task)}]() { |
| 35 | task(); | 34 | task(); |
| 36 | std::unique_lock lock{instance->mutex}; | 35 | std::unique_lock thread_lock{instance->mutex}; |
| 37 | --instance->count; | 36 | --instance->count; |
| 38 | std::notify_all_at_thread_exit(instance->cv, std::move(lock)); | 37 | std::notify_all_at_thread_exit(instance->cv, std::move(thread_lock)); |
| 39 | }).detach(); | 38 | }).detach(); |
| 40 | } | 39 | } |
| 41 | 40 | ||
diff --git a/src/common/detached_tasks.h b/src/common/detached_tasks.h index 5dd8fc27b..416a2d7f3 100644 --- a/src/common/detached_tasks.h +++ b/src/common/detached_tasks.h | |||
| @@ -1,6 +1,5 @@ | |||
| 1 | // Copyright 2018 Citra Emulator Project | 1 | // SPDX-FileCopyrightText: 2018 Citra Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | // Refer to the license.txt file included. | ||
| 4 | 3 | ||
| 5 | #pragma once | 4 | #pragma once |
| 6 | 5 | ||
diff --git a/src/common/div_ceil.h b/src/common/div_ceil.h index e1db35464..eebc279c2 100644 --- a/src/common/div_ceil.h +++ b/src/common/div_ceil.h | |||
| @@ -1,6 +1,5 @@ | |||
| 1 | // Copyright 2020 yuzu emulator team | 1 | // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | // Refer to the license.txt file included. | ||
| 4 | 3 | ||
| 5 | #pragma once | 4 | #pragma once |
| 6 | 5 | ||
diff --git a/src/common/dynamic_library.cpp b/src/common/dynamic_library.cpp index 7f0a10521..054277a2b 100644 --- a/src/common/dynamic_library.cpp +++ b/src/common/dynamic_library.cpp | |||
| @@ -1,8 +1,6 @@ | |||
| 1 | // Copyright 2019 Dolphin Emulator Project | 1 | // SPDX-FileCopyrightText: 2019 Dolphin Emulator Project |
| 2 | // Licensed under GPLv2+ | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | // Refer to the license.txt file included. | ||
| 4 | 3 | ||
| 5 | #include <cstring> | ||
| 6 | #include <string> | 4 | #include <string> |
| 7 | #include <utility> | 5 | #include <utility> |
| 8 | 6 | ||
diff --git a/src/common/dynamic_library.h b/src/common/dynamic_library.h index 3512da940..f42bdf441 100644 --- a/src/common/dynamic_library.h +++ b/src/common/dynamic_library.h | |||
| @@ -1,6 +1,5 @@ | |||
| 1 | // Copyright 2019 Dolphin Emulator Project | 1 | // SPDX-FileCopyrightText: 2019 Dolphin Emulator Project |
| 2 | // Licensed under GPLv2+ | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | // Refer to the license.txt file included. | ||
| 4 | 3 | ||
| 5 | #pragma once | 4 | #pragma once |
| 6 | 5 | ||
diff --git a/src/common/elf.h b/src/common/elf.h new file mode 100644 index 000000000..14a5e9597 --- /dev/null +++ b/src/common/elf.h | |||
| @@ -0,0 +1,333 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <array> | ||
| 7 | #include <cstddef> | ||
| 8 | |||
| 9 | #include "common_types.h" | ||
| 10 | |||
| 11 | namespace Common { | ||
| 12 | namespace ELF { | ||
| 13 | |||
| 14 | /* Type for a 16-bit quantity. */ | ||
| 15 | using Elf32_Half = u16; | ||
| 16 | using Elf64_Half = u16; | ||
| 17 | |||
| 18 | /* Types for signed and unsigned 32-bit quantities. */ | ||
| 19 | using Elf32_Word = u32; | ||
| 20 | using Elf32_Sword = s32; | ||
| 21 | using Elf64_Word = u32; | ||
| 22 | using Elf64_Sword = s32; | ||
| 23 | |||
| 24 | /* Types for signed and unsigned 64-bit quantities. */ | ||
| 25 | using Elf32_Xword = u64; | ||
| 26 | using Elf32_Sxword = s64; | ||
| 27 | using Elf64_Xword = u64; | ||
| 28 | using Elf64_Sxword = s64; | ||
| 29 | |||
| 30 | /* Type of addresses. */ | ||
| 31 | using Elf32_Addr = u32; | ||
| 32 | using Elf64_Addr = u64; | ||
| 33 | |||
| 34 | /* Type of file offsets. */ | ||
| 35 | using Elf32_Off = u32; | ||
| 36 | using Elf64_Off = u64; | ||
| 37 | |||
| 38 | /* Type for section indices, which are 16-bit quantities. */ | ||
| 39 | using Elf32_Section = u16; | ||
| 40 | using Elf64_Section = u16; | ||
| 41 | |||
| 42 | /* Type for version symbol information. */ | ||
| 43 | using Elf32_Versym = Elf32_Half; | ||
| 44 | using Elf64_Versym = Elf64_Half; | ||
| 45 | |||
| 46 | constexpr size_t ElfIdentSize = 16; | ||
| 47 | |||
| 48 | /* The ELF file header. This appears at the start of every ELF file. */ | ||
| 49 | |||
| 50 | struct Elf32_Ehdr { | ||
| 51 | std::array<u8, ElfIdentSize> e_ident; /* Magic number and other info */ | ||
| 52 | Elf32_Half e_type; /* Object file type */ | ||
| 53 | Elf32_Half e_machine; /* Architecture */ | ||
| 54 | Elf32_Word e_version; /* Object file version */ | ||
| 55 | Elf32_Addr e_entry; /* Entry point virtual address */ | ||
| 56 | Elf32_Off e_phoff; /* Program header table file offset */ | ||
| 57 | Elf32_Off e_shoff; /* Section header table file offset */ | ||
| 58 | Elf32_Word e_flags; /* Processor-specific flags */ | ||
| 59 | Elf32_Half e_ehsize; /* ELF header size in bytes */ | ||
| 60 | Elf32_Half e_phentsize; /* Program header table entry size */ | ||
| 61 | Elf32_Half e_phnum; /* Program header table entry count */ | ||
| 62 | Elf32_Half e_shentsize; /* Section header table entry size */ | ||
| 63 | Elf32_Half e_shnum; /* Section header table entry count */ | ||
| 64 | Elf32_Half e_shstrndx; /* Section header string table index */ | ||
| 65 | }; | ||
| 66 | |||
| 67 | struct Elf64_Ehdr { | ||
| 68 | std::array<u8, ElfIdentSize> e_ident; /* Magic number and other info */ | ||
| 69 | Elf64_Half e_type; /* Object file type */ | ||
| 70 | Elf64_Half e_machine; /* Architecture */ | ||
| 71 | Elf64_Word e_version; /* Object file version */ | ||
| 72 | Elf64_Addr e_entry; /* Entry point virtual address */ | ||
| 73 | Elf64_Off e_phoff; /* Program header table file offset */ | ||
| 74 | Elf64_Off e_shoff; /* Section header table file offset */ | ||
| 75 | Elf64_Word e_flags; /* Processor-specific flags */ | ||
| 76 | Elf64_Half e_ehsize; /* ELF header size in bytes */ | ||
| 77 | Elf64_Half e_phentsize; /* Program header table entry size */ | ||
| 78 | Elf64_Half e_phnum; /* Program header table entry count */ | ||
| 79 | Elf64_Half e_shentsize; /* Section header table entry size */ | ||
| 80 | Elf64_Half e_shnum; /* Section header table entry count */ | ||
| 81 | Elf64_Half e_shstrndx; /* Section header string table index */ | ||
| 82 | }; | ||
| 83 | |||
| 84 | constexpr u8 ElfClass32 = 1; /* 32-bit objects */ | ||
| 85 | constexpr u8 ElfClass64 = 2; /* 64-bit objects */ | ||
| 86 | constexpr u8 ElfData2Lsb = 1; /* 2's complement, little endian */ | ||
| 87 | constexpr u8 ElfVersionCurrent = 1; /* EV_CURRENT */ | ||
| 88 | constexpr u8 ElfOsAbiNone = 0; /* System V ABI */ | ||
| 89 | |||
| 90 | constexpr u16 ElfTypeNone = 0; /* No file type */ | ||
| 91 | constexpr u16 ElfTypeRel = 0; /* Relocatable file */ | ||
| 92 | constexpr u16 ElfTypeExec = 0; /* Executable file */ | ||
| 93 | constexpr u16 ElfTypeDyn = 0; /* Shared object file */ | ||
| 94 | |||
| 95 | constexpr u16 ElfMachineArm = 40; /* ARM */ | ||
| 96 | constexpr u16 ElfMachineAArch64 = 183; /* ARM AARCH64 */ | ||
| 97 | |||
| 98 | constexpr std::array<u8, ElfIdentSize> Elf32Ident{ | ||
| 99 | 0x7f, 'E', 'L', 'F', ElfClass32, ElfData2Lsb, ElfVersionCurrent, ElfOsAbiNone}; | ||
| 100 | |||
| 101 | constexpr std::array<u8, ElfIdentSize> Elf64Ident{ | ||
| 102 | 0x7f, 'E', 'L', 'F', ElfClass64, ElfData2Lsb, ElfVersionCurrent, ElfOsAbiNone}; | ||
| 103 | |||
| 104 | /* Section header. */ | ||
| 105 | |||
| 106 | struct Elf32_Shdr { | ||
| 107 | Elf32_Word sh_name; /* Section name (string tbl index) */ | ||
| 108 | Elf32_Word sh_type; /* Section type */ | ||
| 109 | Elf32_Word sh_flags; /* Section flags */ | ||
| 110 | Elf32_Addr sh_addr; /* Section virtual addr at execution */ | ||
| 111 | Elf32_Off sh_offset; /* Section file offset */ | ||
| 112 | Elf32_Word sh_size; /* Section size in bytes */ | ||
| 113 | Elf32_Word sh_link; /* Link to another section */ | ||
| 114 | Elf32_Word sh_info; /* Additional section information */ | ||
| 115 | Elf32_Word sh_addralign; /* Section alignment */ | ||
| 116 | Elf32_Word sh_entsize; /* Entry size if section holds table */ | ||
| 117 | }; | ||
| 118 | |||
| 119 | struct Elf64_Shdr { | ||
| 120 | Elf64_Word sh_name; /* Section name (string tbl index) */ | ||
| 121 | Elf64_Word sh_type; /* Section type */ | ||
| 122 | Elf64_Xword sh_flags; /* Section flags */ | ||
| 123 | Elf64_Addr sh_addr; /* Section virtual addr at execution */ | ||
| 124 | Elf64_Off sh_offset; /* Section file offset */ | ||
| 125 | Elf64_Xword sh_size; /* Section size in bytes */ | ||
| 126 | Elf64_Word sh_link; /* Link to another section */ | ||
| 127 | Elf64_Word sh_info; /* Additional section information */ | ||
| 128 | Elf64_Xword sh_addralign; /* Section alignment */ | ||
| 129 | Elf64_Xword sh_entsize; /* Entry size if section holds table */ | ||
| 130 | }; | ||
| 131 | |||
| 132 | constexpr u32 ElfShnUndef = 0; /* Undefined section */ | ||
| 133 | |||
| 134 | constexpr u32 ElfShtNull = 0; /* Section header table entry unused */ | ||
| 135 | constexpr u32 ElfShtProgBits = 1; /* Program data */ | ||
| 136 | constexpr u32 ElfShtSymtab = 2; /* Symbol table */ | ||
| 137 | constexpr u32 ElfShtStrtab = 3; /* String table */ | ||
| 138 | constexpr u32 ElfShtRela = 4; /* Relocation entries with addends */ | ||
| 139 | constexpr u32 ElfShtDynamic = 6; /* Dynamic linking information */ | ||
| 140 | constexpr u32 ElfShtNobits = 7; /* Program space with no data (bss) */ | ||
| 141 | constexpr u32 ElfShtRel = 9; /* Relocation entries, no addends */ | ||
| 142 | constexpr u32 ElfShtDynsym = 11; /* Dynamic linker symbol table */ | ||
| 143 | |||
| 144 | /* Symbol table entry. */ | ||
| 145 | |||
| 146 | struct Elf32_Sym { | ||
| 147 | Elf32_Word st_name; /* Symbol name (string tbl index) */ | ||
| 148 | Elf32_Addr st_value; /* Symbol value */ | ||
| 149 | Elf32_Word st_size; /* Symbol size */ | ||
| 150 | u8 st_info; /* Symbol type and binding */ | ||
| 151 | u8 st_other; /* Symbol visibility */ | ||
| 152 | Elf32_Section st_shndx; /* Section index */ | ||
| 153 | }; | ||
| 154 | |||
| 155 | struct Elf64_Sym { | ||
| 156 | Elf64_Word st_name; /* Symbol name (string tbl index) */ | ||
| 157 | u8 st_info; /* Symbol type and binding */ | ||
| 158 | u8 st_other; /* Symbol visibility */ | ||
| 159 | Elf64_Section st_shndx; /* Section index */ | ||
| 160 | Elf64_Addr st_value; /* Symbol value */ | ||
| 161 | Elf64_Xword st_size; /* Symbol size */ | ||
| 162 | }; | ||
| 163 | |||
| 164 | /* How to extract and insert information held in the st_info field. */ | ||
| 165 | |||
| 166 | static inline u8 ElfStBind(u8 st_info) { | ||
| 167 | return st_info >> 4; | ||
| 168 | } | ||
| 169 | static inline u8 ElfStType(u8 st_info) { | ||
| 170 | return st_info & 0xf; | ||
| 171 | } | ||
| 172 | static inline u8 ElfStInfo(u8 st_bind, u8 st_type) { | ||
| 173 | return static_cast<u8>((st_bind << 4) + (st_type & 0xf)); | ||
| 174 | } | ||
| 175 | |||
| 176 | constexpr u8 ElfBindLocal = 0; /* Local symbol */ | ||
| 177 | constexpr u8 ElfBindGlobal = 1; /* Global symbol */ | ||
| 178 | constexpr u8 ElfBindWeak = 2; /* Weak symbol */ | ||
| 179 | |||
| 180 | constexpr u8 ElfTypeUnspec = 0; /* Symbol type is unspecified */ | ||
| 181 | constexpr u8 ElfTypeObject = 1; /* Symbol is a data object */ | ||
| 182 | constexpr u8 ElfTypeFunc = 2; /* Symbol is a code object */ | ||
| 183 | |||
| 184 | static inline u8 ElfStVisibility(u8 st_other) { | ||
| 185 | return static_cast<u8>(st_other & 0x3); | ||
| 186 | } | ||
| 187 | |||
| 188 | constexpr u8 ElfVisibilityDefault = 0; /* Default symbol visibility rules */ | ||
| 189 | constexpr u8 ElfVisibilityInternal = 1; /* Processor specific hidden class */ | ||
| 190 | constexpr u8 ElfVisibilityHidden = 2; /* Sym unavailable in other modules */ | ||
| 191 | constexpr u8 ElfVisibilityProtected = 3; /* Not preemptible, not exported */ | ||
| 192 | |||
| 193 | /* Relocation table entry without addend (in section of type ShtRel). */ | ||
| 194 | |||
| 195 | struct Elf32_Rel { | ||
| 196 | Elf32_Addr r_offset; /* Address */ | ||
| 197 | Elf32_Word r_info; /* Relocation type and symbol index */ | ||
| 198 | }; | ||
| 199 | |||
| 200 | /* Relocation table entry with addend (in section of type ShtRela). */ | ||
| 201 | |||
| 202 | struct Elf32_Rela { | ||
| 203 | Elf32_Addr r_offset; /* Address */ | ||
| 204 | Elf32_Word r_info; /* Relocation type and symbol index */ | ||
| 205 | Elf32_Sword r_addend; /* Addend */ | ||
| 206 | }; | ||
| 207 | |||
| 208 | struct Elf64_Rela { | ||
| 209 | Elf64_Addr r_offset; /* Address */ | ||
| 210 | Elf64_Xword r_info; /* Relocation type and symbol index */ | ||
| 211 | Elf64_Sxword r_addend; /* Addend */ | ||
| 212 | }; | ||
| 213 | |||
| 214 | /* How to extract and insert information held in the r_info field. */ | ||
| 215 | |||
| 216 | static inline u32 Elf32RelSymIndex(Elf32_Word r_info) { | ||
| 217 | return r_info >> 8; | ||
| 218 | } | ||
| 219 | static inline u8 Elf32RelType(Elf32_Word r_info) { | ||
| 220 | return static_cast<u8>(r_info & 0xff); | ||
| 221 | } | ||
| 222 | static inline Elf32_Word Elf32RelInfo(u32 sym_index, u8 type) { | ||
| 223 | return (sym_index << 8) + type; | ||
| 224 | } | ||
| 225 | static inline u32 Elf64RelSymIndex(Elf64_Xword r_info) { | ||
| 226 | return static_cast<u32>(r_info >> 32); | ||
| 227 | } | ||
| 228 | static inline u32 Elf64RelType(Elf64_Xword r_info) { | ||
| 229 | return r_info & 0xffffffff; | ||
| 230 | } | ||
| 231 | static inline Elf64_Xword Elf64RelInfo(u32 sym_index, u32 type) { | ||
| 232 | return (static_cast<Elf64_Xword>(sym_index) << 32) + type; | ||
| 233 | } | ||
| 234 | |||
| 235 | constexpr u32 ElfArmCopy = 20; /* Copy symbol at runtime */ | ||
| 236 | constexpr u32 ElfArmGlobDat = 21; /* Create GOT entry */ | ||
| 237 | constexpr u32 ElfArmJumpSlot = 22; /* Create PLT entry */ | ||
| 238 | constexpr u32 ElfArmRelative = 23; /* Adjust by program base */ | ||
| 239 | |||
| 240 | constexpr u32 ElfAArch64Copy = 1024; /* Copy symbol at runtime */ | ||
| 241 | constexpr u32 ElfAArch64GlobDat = 1025; /* Create GOT entry */ | ||
| 242 | constexpr u32 ElfAArch64JumpSlot = 1026; /* Create PLT entry */ | ||
| 243 | constexpr u32 ElfAArch64Relative = 1027; /* Adjust by program base */ | ||
| 244 | |||
| 245 | /* Program segment header. */ | ||
| 246 | |||
| 247 | struct Elf32_Phdr { | ||
| 248 | Elf32_Word p_type; /* Segment type */ | ||
| 249 | Elf32_Off p_offset; /* Segment file offset */ | ||
| 250 | Elf32_Addr p_vaddr; /* Segment virtual address */ | ||
| 251 | Elf32_Addr p_paddr; /* Segment physical address */ | ||
| 252 | Elf32_Word p_filesz; /* Segment size in file */ | ||
| 253 | Elf32_Word p_memsz; /* Segment size in memory */ | ||
| 254 | Elf32_Word p_flags; /* Segment flags */ | ||
| 255 | Elf32_Word p_align; /* Segment alignment */ | ||
| 256 | }; | ||
| 257 | |||
| 258 | struct Elf64_Phdr { | ||
| 259 | Elf64_Word p_type; /* Segment type */ | ||
| 260 | Elf64_Word p_flags; /* Segment flags */ | ||
| 261 | Elf64_Off p_offset; /* Segment file offset */ | ||
| 262 | Elf64_Addr p_vaddr; /* Segment virtual address */ | ||
| 263 | Elf64_Addr p_paddr; /* Segment physical address */ | ||
| 264 | Elf64_Xword p_filesz; /* Segment size in file */ | ||
| 265 | Elf64_Xword p_memsz; /* Segment size in memory */ | ||
| 266 | Elf64_Xword p_align; /* Segment alignment */ | ||
| 267 | }; | ||
| 268 | |||
| 269 | /* Legal values for p_type (segment type). */ | ||
| 270 | |||
| 271 | constexpr u32 ElfPtNull = 0; /* Program header table entry unused */ | ||
| 272 | constexpr u32 ElfPtLoad = 1; /* Loadable program segment */ | ||
| 273 | constexpr u32 ElfPtDynamic = 2; /* Dynamic linking information */ | ||
| 274 | constexpr u32 ElfPtInterp = 3; /* Program interpreter */ | ||
| 275 | constexpr u32 ElfPtNote = 4; /* Auxiliary information */ | ||
| 276 | constexpr u32 ElfPtPhdr = 6; /* Entry for header table itself */ | ||
| 277 | constexpr u32 ElfPtTls = 7; /* Thread-local storage segment */ | ||
| 278 | |||
| 279 | /* Legal values for p_flags (segment flags). */ | ||
| 280 | |||
| 281 | constexpr u32 ElfPfExec = 0; /* Segment is executable */ | ||
| 282 | constexpr u32 ElfPfWrite = 1; /* Segment is writable */ | ||
| 283 | constexpr u32 ElfPfRead = 2; /* Segment is readable */ | ||
| 284 | |||
| 285 | /* Dynamic section entry. */ | ||
| 286 | |||
| 287 | struct Elf32_Dyn { | ||
| 288 | Elf32_Sword d_tag; /* Dynamic entry type */ | ||
| 289 | union { | ||
| 290 | Elf32_Word d_val; /* Integer value */ | ||
| 291 | Elf32_Addr d_ptr; /* Address value */ | ||
| 292 | } d_un; | ||
| 293 | }; | ||
| 294 | |||
| 295 | struct Elf64_Dyn { | ||
| 296 | Elf64_Sxword d_tag; /* Dynamic entry type */ | ||
| 297 | union { | ||
| 298 | Elf64_Xword d_val; /* Integer value */ | ||
| 299 | Elf64_Addr d_ptr; /* Address value */ | ||
| 300 | } d_un; | ||
| 301 | }; | ||
| 302 | |||
| 303 | /* Legal values for d_tag (dynamic entry type). */ | ||
| 304 | |||
| 305 | constexpr u32 ElfDtNull = 0; /* Marks end of dynamic section */ | ||
| 306 | constexpr u32 ElfDtNeeded = 1; /* Name of needed library */ | ||
| 307 | constexpr u32 ElfDtPltRelSz = 2; /* Size in bytes of PLT relocs */ | ||
| 308 | constexpr u32 ElfDtPltGot = 3; /* Processor defined value */ | ||
| 309 | constexpr u32 ElfDtHash = 4; /* Address of symbol hash table */ | ||
| 310 | constexpr u32 ElfDtStrtab = 5; /* Address of string table */ | ||
| 311 | constexpr u32 ElfDtSymtab = 6; /* Address of symbol table */ | ||
| 312 | constexpr u32 ElfDtRela = 7; /* Address of Rela relocs */ | ||
| 313 | constexpr u32 ElfDtRelasz = 8; /* Total size of Rela relocs */ | ||
| 314 | constexpr u32 ElfDtRelaent = 9; /* Size of one Rela reloc */ | ||
| 315 | constexpr u32 ElfDtStrsz = 10; /* Size of string table */ | ||
| 316 | constexpr u32 ElfDtSyment = 11; /* Size of one symbol table entry */ | ||
| 317 | constexpr u32 ElfDtInit = 12; /* Address of init function */ | ||
| 318 | constexpr u32 ElfDtFini = 13; /* Address of termination function */ | ||
| 319 | constexpr u32 ElfDtRel = 17; /* Address of Rel relocs */ | ||
| 320 | constexpr u32 ElfDtRelsz = 18; /* Total size of Rel relocs */ | ||
| 321 | constexpr u32 ElfDtRelent = 19; /* Size of one Rel reloc */ | ||
| 322 | constexpr u32 ElfDtPltRel = 20; /* Type of reloc in PLT */ | ||
| 323 | constexpr u32 ElfDtTextRel = 22; /* Reloc might modify .text */ | ||
| 324 | constexpr u32 ElfDtJmpRel = 23; /* Address of PLT relocs */ | ||
| 325 | constexpr u32 ElfDtBindNow = 24; /* Process relocations of object */ | ||
| 326 | constexpr u32 ElfDtInitArray = 25; /* Array with addresses of init fct */ | ||
| 327 | constexpr u32 ElfDtFiniArray = 26; /* Array with addresses of fini fct */ | ||
| 328 | constexpr u32 ElfDtInitArraySz = 27; /* Size in bytes of DT_INIT_ARRAY */ | ||
| 329 | constexpr u32 ElfDtFiniArraySz = 28; /* Size in bytes of DT_FINI_ARRAY */ | ||
| 330 | constexpr u32 ElfDtSymtabShndx = 34; /* Address of SYMTAB_SHNDX section */ | ||
| 331 | |||
| 332 | } // namespace ELF | ||
| 333 | } // namespace Common | ||
diff --git a/src/common/error.cpp b/src/common/error.cpp index d4455e310..ddb03bd45 100644 --- a/src/common/error.cpp +++ b/src/common/error.cpp | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project | 1 | // SPDX-FileCopyrightText: 2013 Dolphin Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-FileCopyrightText: 2014 Citra Emulator Project |
| 3 | // Refer to the license.txt file included. | 3 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 4 | 4 | ||
| 5 | #include <cstddef> | 5 | #include <cstddef> |
| 6 | #ifdef _WIN32 | 6 | #ifdef _WIN32 |
diff --git a/src/common/error.h b/src/common/error.h index e084d4b0f..62a3bd835 100644 --- a/src/common/error.h +++ b/src/common/error.h | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project | 1 | // SPDX-FileCopyrightText: 2013 Dolphin Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-FileCopyrightText: 2014 Citra Emulator Project |
| 3 | // Refer to the license.txt file included. | 3 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
diff --git a/src/common/expected.h b/src/common/expected.h index c8d8579c1..6e6c86ee7 100644 --- a/src/common/expected.h +++ b/src/common/expected.h | |||
| @@ -1,6 +1,5 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | // Refer to the license.txt file included. | ||
| 4 | 3 | ||
| 5 | // This is based on the proposed implementation of std::expected (P0323) | 4 | // This is based on the proposed implementation of std::expected (P0323) |
| 6 | // https://github.com/TartanLlama/expected/blob/master/include/tl/expected.hpp | 5 | // https://github.com/TartanLlama/expected/blob/master/include/tl/expected.hpp |
diff --git a/src/common/fiber.cpp b/src/common/fiber.cpp index 81b212e4b..bc92b360b 100644 --- a/src/common/fiber.cpp +++ b/src/common/fiber.cpp | |||
| @@ -1,10 +1,10 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | // Refer to the license.txt file included. | 3 | |
| 4 | #include <mutex> | ||
| 4 | 5 | ||
| 5 | #include "common/assert.h" | 6 | #include "common/assert.h" |
| 6 | #include "common/fiber.h" | 7 | #include "common/fiber.h" |
| 7 | #include "common/spin_lock.h" | ||
| 8 | #include "common/virtual_buffer.h" | 8 | #include "common/virtual_buffer.h" |
| 9 | 9 | ||
| 10 | #include <boost/context/detail/fcontext.hpp> | 10 | #include <boost/context/detail/fcontext.hpp> |
| @@ -19,11 +19,9 @@ struct Fiber::FiberImpl { | |||
| 19 | VirtualBuffer<u8> stack; | 19 | VirtualBuffer<u8> stack; |
| 20 | VirtualBuffer<u8> rewind_stack; | 20 | VirtualBuffer<u8> rewind_stack; |
| 21 | 21 | ||
| 22 | SpinLock guard{}; | 22 | std::mutex guard; |
| 23 | std::function<void(void*)> entry_point; | 23 | std::function<void()> entry_point; |
| 24 | std::function<void(void*)> rewind_point; | 24 | std::function<void()> rewind_point; |
| 25 | void* rewind_parameter{}; | ||
| 26 | void* start_parameter{}; | ||
| 27 | std::shared_ptr<Fiber> previous_fiber; | 25 | std::shared_ptr<Fiber> previous_fiber; |
| 28 | bool is_thread_fiber{}; | 26 | bool is_thread_fiber{}; |
| 29 | bool released{}; | 27 | bool released{}; |
| @@ -34,13 +32,8 @@ struct Fiber::FiberImpl { | |||
| 34 | boost::context::detail::fcontext_t rewind_context{}; | 32 | boost::context::detail::fcontext_t rewind_context{}; |
| 35 | }; | 33 | }; |
| 36 | 34 | ||
| 37 | void Fiber::SetStartParameter(void* new_parameter) { | 35 | void Fiber::SetRewindPoint(std::function<void()>&& rewind_func) { |
| 38 | impl->start_parameter = new_parameter; | ||
| 39 | } | ||
| 40 | |||
| 41 | void Fiber::SetRewindPoint(std::function<void(void*)>&& rewind_func, void* rewind_param) { | ||
| 42 | impl->rewind_point = std::move(rewind_func); | 36 | impl->rewind_point = std::move(rewind_func); |
| 43 | impl->rewind_parameter = rewind_param; | ||
| 44 | } | 37 | } |
| 45 | 38 | ||
| 46 | void Fiber::Start(boost::context::detail::transfer_t& transfer) { | 39 | void Fiber::Start(boost::context::detail::transfer_t& transfer) { |
| @@ -48,7 +41,7 @@ void Fiber::Start(boost::context::detail::transfer_t& transfer) { | |||
| 48 | impl->previous_fiber->impl->context = transfer.fctx; | 41 | impl->previous_fiber->impl->context = transfer.fctx; |
| 49 | impl->previous_fiber->impl->guard.unlock(); | 42 | impl->previous_fiber->impl->guard.unlock(); |
| 50 | impl->previous_fiber.reset(); | 43 | impl->previous_fiber.reset(); |
| 51 | impl->entry_point(impl->start_parameter); | 44 | impl->entry_point(); |
| 52 | UNREACHABLE(); | 45 | UNREACHABLE(); |
| 53 | } | 46 | } |
| 54 | 47 | ||
| @@ -59,7 +52,7 @@ void Fiber::OnRewind([[maybe_unused]] boost::context::detail::transfer_t& transf | |||
| 59 | u8* tmp = impl->stack_limit; | 52 | u8* tmp = impl->stack_limit; |
| 60 | impl->stack_limit = impl->rewind_stack_limit; | 53 | impl->stack_limit = impl->rewind_stack_limit; |
| 61 | impl->rewind_stack_limit = tmp; | 54 | impl->rewind_stack_limit = tmp; |
| 62 | impl->rewind_point(impl->rewind_parameter); | 55 | impl->rewind_point(); |
| 63 | UNREACHABLE(); | 56 | UNREACHABLE(); |
| 64 | } | 57 | } |
| 65 | 58 | ||
| @@ -73,10 +66,8 @@ void Fiber::RewindStartFunc(boost::context::detail::transfer_t transfer) { | |||
| 73 | fiber->OnRewind(transfer); | 66 | fiber->OnRewind(transfer); |
| 74 | } | 67 | } |
| 75 | 68 | ||
| 76 | Fiber::Fiber(std::function<void(void*)>&& entry_point_func, void* start_parameter) | 69 | Fiber::Fiber(std::function<void()>&& entry_point_func) : impl{std::make_unique<FiberImpl>()} { |
| 77 | : impl{std::make_unique<FiberImpl>()} { | ||
| 78 | impl->entry_point = std::move(entry_point_func); | 70 | impl->entry_point = std::move(entry_point_func); |
| 79 | impl->start_parameter = start_parameter; | ||
| 80 | impl->stack_limit = impl->stack.data(); | 71 | impl->stack_limit = impl->stack.data(); |
| 81 | impl->rewind_stack_limit = impl->rewind_stack.data(); | 72 | impl->rewind_stack_limit = impl->rewind_stack.data(); |
| 82 | u8* stack_base = impl->stack_limit + default_stack_size; | 73 | u8* stack_base = impl->stack_limit + default_stack_size; |
diff --git a/src/common/fiber.h b/src/common/fiber.h index f2a8ff29a..f24d333a3 100644 --- a/src/common/fiber.h +++ b/src/common/fiber.h | |||
| @@ -1,6 +1,5 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | // Refer to the license.txt file included. | ||
| 4 | 3 | ||
| 5 | #pragma once | 4 | #pragma once |
| 6 | 5 | ||
| @@ -30,7 +29,7 @@ namespace Common { | |||
| 30 | */ | 29 | */ |
| 31 | class Fiber { | 30 | class Fiber { |
| 32 | public: | 31 | public: |
| 33 | Fiber(std::function<void(void*)>&& entry_point_func, void* start_parameter); | 32 | Fiber(std::function<void()>&& entry_point_func); |
| 34 | ~Fiber(); | 33 | ~Fiber(); |
| 35 | 34 | ||
| 36 | Fiber(const Fiber&) = delete; | 35 | Fiber(const Fiber&) = delete; |
| @@ -44,16 +43,13 @@ public: | |||
| 44 | static void YieldTo(std::weak_ptr<Fiber> weak_from, Fiber& to); | 43 | static void YieldTo(std::weak_ptr<Fiber> weak_from, Fiber& to); |
| 45 | [[nodiscard]] static std::shared_ptr<Fiber> ThreadToFiber(); | 44 | [[nodiscard]] static std::shared_ptr<Fiber> ThreadToFiber(); |
| 46 | 45 | ||
| 47 | void SetRewindPoint(std::function<void(void*)>&& rewind_func, void* rewind_param); | 46 | void SetRewindPoint(std::function<void()>&& rewind_func); |
| 48 | 47 | ||
| 49 | void Rewind(); | 48 | void Rewind(); |
| 50 | 49 | ||
| 51 | /// Only call from main thread's fiber | 50 | /// Only call from main thread's fiber |
| 52 | void Exit(); | 51 | void Exit(); |
| 53 | 52 | ||
| 54 | /// Changes the start parameter of the fiber. Has no effect if the fiber already started | ||
| 55 | void SetStartParameter(void* new_parameter); | ||
| 56 | |||
| 57 | private: | 53 | private: |
| 58 | Fiber(); | 54 | Fiber(); |
| 59 | 55 | ||
diff --git a/src/common/fixed_point.h b/src/common/fixed_point.h new file mode 100644 index 000000000..4a0f72cc9 --- /dev/null +++ b/src/common/fixed_point.h | |||
| @@ -0,0 +1,706 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2015 Evan Teran | ||
| 2 | // SPDX-License-Identifier: MIT | ||
| 3 | |||
| 4 | // From: https://github.com/eteran/cpp-utilities/blob/master/fixed/include/cpp-utilities/fixed.h | ||
| 5 | // See also: http://stackoverflow.com/questions/79677/whats-the-best-way-to-do-fixed-point-math | ||
| 6 | |||
| 7 | #ifndef FIXED_H_ | ||
| 8 | #define FIXED_H_ | ||
| 9 | |||
| 10 | #if __cplusplus >= 201402L | ||
| 11 | #define CONSTEXPR14 constexpr | ||
| 12 | #else | ||
| 13 | #define CONSTEXPR14 | ||
| 14 | #endif | ||
| 15 | |||
| 16 | #include <cstddef> // for size_t | ||
| 17 | #include <cstdint> | ||
| 18 | #include <exception> | ||
| 19 | #include <ostream> | ||
| 20 | #include <type_traits> | ||
| 21 | |||
| 22 | namespace Common { | ||
| 23 | |||
| 24 | template <size_t I, size_t F> | ||
| 25 | class FixedPoint; | ||
| 26 | |||
| 27 | namespace detail { | ||
| 28 | |||
| 29 | // helper templates to make magic with types :) | ||
| 30 | // these allow us to determine resonable types from | ||
| 31 | // a desired size, they also let us infer the next largest type | ||
| 32 | // from a type which is nice for the division op | ||
| 33 | template <size_t T> | ||
| 34 | struct type_from_size { | ||
| 35 | using value_type = void; | ||
| 36 | using unsigned_type = void; | ||
| 37 | using signed_type = void; | ||
| 38 | static constexpr bool is_specialized = false; | ||
| 39 | }; | ||
| 40 | |||
| 41 | #if defined(__GNUC__) && defined(__x86_64__) && !defined(__STRICT_ANSI__) | ||
| 42 | template <> | ||
| 43 | struct type_from_size<128> { | ||
| 44 | static constexpr bool is_specialized = true; | ||
| 45 | static constexpr size_t size = 128; | ||
| 46 | |||
| 47 | using value_type = __int128; | ||
| 48 | using unsigned_type = unsigned __int128; | ||
| 49 | using signed_type = __int128; | ||
| 50 | using next_size = type_from_size<256>; | ||
| 51 | }; | ||
| 52 | #endif | ||
| 53 | |||
| 54 | template <> | ||
| 55 | struct type_from_size<64> { | ||
| 56 | static constexpr bool is_specialized = true; | ||
| 57 | static constexpr size_t size = 64; | ||
| 58 | |||
| 59 | using value_type = int64_t; | ||
| 60 | using unsigned_type = std::make_unsigned<value_type>::type; | ||
| 61 | using signed_type = std::make_signed<value_type>::type; | ||
| 62 | using next_size = type_from_size<128>; | ||
| 63 | }; | ||
| 64 | |||
| 65 | template <> | ||
| 66 | struct type_from_size<32> { | ||
| 67 | static constexpr bool is_specialized = true; | ||
| 68 | static constexpr size_t size = 32; | ||
| 69 | |||
| 70 | using value_type = int32_t; | ||
| 71 | using unsigned_type = std::make_unsigned<value_type>::type; | ||
| 72 | using signed_type = std::make_signed<value_type>::type; | ||
| 73 | using next_size = type_from_size<64>; | ||
| 74 | }; | ||
| 75 | |||
| 76 | template <> | ||
| 77 | struct type_from_size<16> { | ||
| 78 | static constexpr bool is_specialized = true; | ||
| 79 | static constexpr size_t size = 16; | ||
| 80 | |||
| 81 | using value_type = int16_t; | ||
| 82 | using unsigned_type = std::make_unsigned<value_type>::type; | ||
| 83 | using signed_type = std::make_signed<value_type>::type; | ||
| 84 | using next_size = type_from_size<32>; | ||
| 85 | }; | ||
| 86 | |||
| 87 | template <> | ||
| 88 | struct type_from_size<8> { | ||
| 89 | static constexpr bool is_specialized = true; | ||
| 90 | static constexpr size_t size = 8; | ||
| 91 | |||
| 92 | using value_type = int8_t; | ||
| 93 | using unsigned_type = std::make_unsigned<value_type>::type; | ||
| 94 | using signed_type = std::make_signed<value_type>::type; | ||
| 95 | using next_size = type_from_size<16>; | ||
| 96 | }; | ||
| 97 | |||
| 98 | // this is to assist in adding support for non-native base | ||
| 99 | // types (for adding big-int support), this should be fine | ||
| 100 | // unless your bit-int class doesn't nicely support casting | ||
| 101 | template <class B, class N> | ||
| 102 | constexpr B next_to_base(N rhs) { | ||
| 103 | return static_cast<B>(rhs); | ||
| 104 | } | ||
| 105 | |||
| 106 | struct divide_by_zero : std::exception {}; | ||
| 107 | |||
| 108 | template <size_t I, size_t F> | ||
| 109 | CONSTEXPR14 FixedPoint<I, F> divide( | ||
| 110 | FixedPoint<I, F> numerator, FixedPoint<I, F> denominator, FixedPoint<I, F>& remainder, | ||
| 111 | typename std::enable_if<type_from_size<I + F>::next_size::is_specialized>::type* = nullptr) { | ||
| 112 | |||
| 113 | using next_type = typename FixedPoint<I, F>::next_type; | ||
| 114 | using base_type = typename FixedPoint<I, F>::base_type; | ||
| 115 | constexpr size_t fractional_bits = FixedPoint<I, F>::fractional_bits; | ||
| 116 | |||
| 117 | next_type t(numerator.to_raw()); | ||
| 118 | t <<= fractional_bits; | ||
| 119 | |||
| 120 | FixedPoint<I, F> quotient; | ||
| 121 | |||
| 122 | quotient = FixedPoint<I, F>::from_base(next_to_base<base_type>(t / denominator.to_raw())); | ||
| 123 | remainder = FixedPoint<I, F>::from_base(next_to_base<base_type>(t % denominator.to_raw())); | ||
| 124 | |||
| 125 | return quotient; | ||
| 126 | } | ||
| 127 | |||
| 128 | template <size_t I, size_t F> | ||
| 129 | CONSTEXPR14 FixedPoint<I, F> divide( | ||
| 130 | FixedPoint<I, F> numerator, FixedPoint<I, F> denominator, FixedPoint<I, F>& remainder, | ||
| 131 | typename std::enable_if<!type_from_size<I + F>::next_size::is_specialized>::type* = nullptr) { | ||
| 132 | |||
| 133 | using unsigned_type = typename FixedPoint<I, F>::unsigned_type; | ||
| 134 | |||
| 135 | constexpr int bits = FixedPoint<I, F>::total_bits; | ||
| 136 | |||
| 137 | if (denominator == 0) { | ||
| 138 | throw divide_by_zero(); | ||
| 139 | } else { | ||
| 140 | |||
| 141 | int sign = 0; | ||
| 142 | |||
| 143 | FixedPoint<I, F> quotient; | ||
| 144 | |||
| 145 | if (numerator < 0) { | ||
| 146 | sign ^= 1; | ||
| 147 | numerator = -numerator; | ||
| 148 | } | ||
| 149 | |||
| 150 | if (denominator < 0) { | ||
| 151 | sign ^= 1; | ||
| 152 | denominator = -denominator; | ||
| 153 | } | ||
| 154 | |||
| 155 | unsigned_type n = numerator.to_raw(); | ||
| 156 | unsigned_type d = denominator.to_raw(); | ||
| 157 | unsigned_type x = 1; | ||
| 158 | unsigned_type answer = 0; | ||
| 159 | |||
| 160 | // egyptian division algorithm | ||
| 161 | while ((n >= d) && (((d >> (bits - 1)) & 1) == 0)) { | ||
| 162 | x <<= 1; | ||
| 163 | d <<= 1; | ||
| 164 | } | ||
| 165 | |||
| 166 | while (x != 0) { | ||
| 167 | if (n >= d) { | ||
| 168 | n -= d; | ||
| 169 | answer += x; | ||
| 170 | } | ||
| 171 | |||
| 172 | x >>= 1; | ||
| 173 | d >>= 1; | ||
| 174 | } | ||
| 175 | |||
| 176 | unsigned_type l1 = n; | ||
| 177 | unsigned_type l2 = denominator.to_raw(); | ||
| 178 | |||
| 179 | // calculate the lower bits (needs to be unsigned) | ||
| 180 | while (l1 >> (bits - F) > 0) { | ||
| 181 | l1 >>= 1; | ||
| 182 | l2 >>= 1; | ||
| 183 | } | ||
| 184 | const unsigned_type lo = (l1 << F) / l2; | ||
| 185 | |||
| 186 | quotient = FixedPoint<I, F>::from_base((answer << F) | lo); | ||
| 187 | remainder = n; | ||
| 188 | |||
| 189 | if (sign) { | ||
| 190 | quotient = -quotient; | ||
| 191 | } | ||
| 192 | |||
| 193 | return quotient; | ||
| 194 | } | ||
| 195 | } | ||
| 196 | |||
| 197 | // this is the usual implementation of multiplication | ||
| 198 | template <size_t I, size_t F> | ||
| 199 | CONSTEXPR14 FixedPoint<I, F> multiply( | ||
| 200 | FixedPoint<I, F> lhs, FixedPoint<I, F> rhs, | ||
| 201 | typename std::enable_if<type_from_size<I + F>::next_size::is_specialized>::type* = nullptr) { | ||
| 202 | |||
| 203 | using next_type = typename FixedPoint<I, F>::next_type; | ||
| 204 | using base_type = typename FixedPoint<I, F>::base_type; | ||
| 205 | |||
| 206 | constexpr size_t fractional_bits = FixedPoint<I, F>::fractional_bits; | ||
| 207 | |||
| 208 | next_type t(static_cast<next_type>(lhs.to_raw()) * static_cast<next_type>(rhs.to_raw())); | ||
| 209 | t >>= fractional_bits; | ||
| 210 | |||
| 211 | return FixedPoint<I, F>::from_base(next_to_base<base_type>(t)); | ||
| 212 | } | ||
| 213 | |||
| 214 | // this is the fall back version we use when we don't have a next size | ||
| 215 | // it is slightly slower, but is more robust since it doesn't | ||
| 216 | // require and upgraded type | ||
| 217 | template <size_t I, size_t F> | ||
| 218 | CONSTEXPR14 FixedPoint<I, F> multiply( | ||
| 219 | FixedPoint<I, F> lhs, FixedPoint<I, F> rhs, | ||
| 220 | typename std::enable_if<!type_from_size<I + F>::next_size::is_specialized>::type* = nullptr) { | ||
| 221 | |||
| 222 | using base_type = typename FixedPoint<I, F>::base_type; | ||
| 223 | |||
| 224 | constexpr size_t fractional_bits = FixedPoint<I, F>::fractional_bits; | ||
| 225 | constexpr base_type integer_mask = FixedPoint<I, F>::integer_mask; | ||
| 226 | constexpr base_type fractional_mask = FixedPoint<I, F>::fractional_mask; | ||
| 227 | |||
| 228 | // more costly but doesn't need a larger type | ||
| 229 | const base_type a_hi = (lhs.to_raw() & integer_mask) >> fractional_bits; | ||
| 230 | const base_type b_hi = (rhs.to_raw() & integer_mask) >> fractional_bits; | ||
| 231 | const base_type a_lo = (lhs.to_raw() & fractional_mask); | ||
| 232 | const base_type b_lo = (rhs.to_raw() & fractional_mask); | ||
| 233 | |||
| 234 | const base_type x1 = a_hi * b_hi; | ||
| 235 | const base_type x2 = a_hi * b_lo; | ||
| 236 | const base_type x3 = a_lo * b_hi; | ||
| 237 | const base_type x4 = a_lo * b_lo; | ||
| 238 | |||
| 239 | return FixedPoint<I, F>::from_base((x1 << fractional_bits) + (x3 + x2) + | ||
| 240 | (x4 >> fractional_bits)); | ||
| 241 | } | ||
| 242 | } // namespace detail | ||
| 243 | |||
| 244 | template <size_t I, size_t F> | ||
| 245 | class FixedPoint { | ||
| 246 | static_assert(detail::type_from_size<I + F>::is_specialized, "invalid combination of sizes"); | ||
| 247 | |||
| 248 | public: | ||
| 249 | static constexpr size_t fractional_bits = F; | ||
| 250 | static constexpr size_t integer_bits = I; | ||
| 251 | static constexpr size_t total_bits = I + F; | ||
| 252 | |||
| 253 | using base_type_info = detail::type_from_size<total_bits>; | ||
| 254 | |||
| 255 | using base_type = typename base_type_info::value_type; | ||
| 256 | using next_type = typename base_type_info::next_size::value_type; | ||
| 257 | using unsigned_type = typename base_type_info::unsigned_type; | ||
| 258 | |||
| 259 | public: | ||
| 260 | #ifdef __GNUC__ | ||
| 261 | #pragma GCC diagnostic push | ||
| 262 | #pragma GCC diagnostic ignored "-Woverflow" | ||
| 263 | #endif | ||
| 264 | static constexpr base_type fractional_mask = | ||
| 265 | ~(static_cast<unsigned_type>(~base_type(0)) << fractional_bits); | ||
| 266 | static constexpr base_type integer_mask = ~fractional_mask; | ||
| 267 | #ifdef __GNUC__ | ||
| 268 | #pragma GCC diagnostic pop | ||
| 269 | #endif | ||
| 270 | |||
| 271 | public: | ||
| 272 | static constexpr base_type one = base_type(1) << fractional_bits; | ||
| 273 | |||
| 274 | public: // constructors | ||
| 275 | FixedPoint() = default; | ||
| 276 | FixedPoint(const FixedPoint&) = default; | ||
| 277 | FixedPoint(FixedPoint&&) = default; | ||
| 278 | FixedPoint& operator=(const FixedPoint&) = default; | ||
| 279 | |||
| 280 | template <class Number> | ||
| 281 | constexpr FixedPoint( | ||
| 282 | Number n, typename std::enable_if<std::is_arithmetic<Number>::value>::type* = nullptr) | ||
| 283 | : data_(static_cast<base_type>(n * one)) {} | ||
| 284 | |||
| 285 | public: // conversion | ||
| 286 | template <size_t I2, size_t F2> | ||
| 287 | CONSTEXPR14 explicit FixedPoint(FixedPoint<I2, F2> other) { | ||
| 288 | static_assert(I2 <= I && F2 <= F, "Scaling conversion can only upgrade types"); | ||
| 289 | using T = FixedPoint<I2, F2>; | ||
| 290 | |||
| 291 | const base_type fractional = (other.data_ & T::fractional_mask); | ||
| 292 | const base_type integer = (other.data_ & T::integer_mask) >> T::fractional_bits; | ||
| 293 | data_ = | ||
| 294 | (integer << fractional_bits) | (fractional << (fractional_bits - T::fractional_bits)); | ||
| 295 | } | ||
| 296 | |||
| 297 | private: | ||
| 298 | // this makes it simpler to create a FixedPoint point object from | ||
| 299 | // a native type without scaling | ||
| 300 | // use "FixedPoint::from_base" in order to perform this. | ||
| 301 | struct NoScale {}; | ||
| 302 | |||
| 303 | constexpr FixedPoint(base_type n, const NoScale&) : data_(n) {} | ||
| 304 | |||
| 305 | public: | ||
| 306 | static constexpr FixedPoint from_base(base_type n) { | ||
| 307 | return FixedPoint(n, NoScale()); | ||
| 308 | } | ||
| 309 | |||
| 310 | public: // comparison operators | ||
| 311 | constexpr bool operator==(FixedPoint rhs) const { | ||
| 312 | return data_ == rhs.data_; | ||
| 313 | } | ||
| 314 | |||
| 315 | constexpr bool operator!=(FixedPoint rhs) const { | ||
| 316 | return data_ != rhs.data_; | ||
| 317 | } | ||
| 318 | |||
| 319 | constexpr bool operator<(FixedPoint rhs) const { | ||
| 320 | return data_ < rhs.data_; | ||
| 321 | } | ||
| 322 | |||
| 323 | constexpr bool operator>(FixedPoint rhs) const { | ||
| 324 | return data_ > rhs.data_; | ||
| 325 | } | ||
| 326 | |||
| 327 | constexpr bool operator<=(FixedPoint rhs) const { | ||
| 328 | return data_ <= rhs.data_; | ||
| 329 | } | ||
| 330 | |||
| 331 | constexpr bool operator>=(FixedPoint rhs) const { | ||
| 332 | return data_ >= rhs.data_; | ||
| 333 | } | ||
| 334 | |||
| 335 | public: // unary operators | ||
| 336 | constexpr bool operator!() const { | ||
| 337 | return !data_; | ||
| 338 | } | ||
| 339 | |||
| 340 | constexpr FixedPoint operator~() const { | ||
| 341 | // NOTE(eteran): this will often appear to "just negate" the value | ||
| 342 | // that is not an error, it is because -x == (~x+1) | ||
| 343 | // and that "+1" is adding an infinitesimally small fraction to the | ||
| 344 | // complimented value | ||
| 345 | return FixedPoint::from_base(~data_); | ||
| 346 | } | ||
| 347 | |||
| 348 | constexpr FixedPoint operator-() const { | ||
| 349 | return FixedPoint::from_base(-data_); | ||
| 350 | } | ||
| 351 | |||
| 352 | constexpr FixedPoint operator+() const { | ||
| 353 | return FixedPoint::from_base(+data_); | ||
| 354 | } | ||
| 355 | |||
| 356 | CONSTEXPR14 FixedPoint& operator++() { | ||
| 357 | data_ += one; | ||
| 358 | return *this; | ||
| 359 | } | ||
| 360 | |||
| 361 | CONSTEXPR14 FixedPoint& operator--() { | ||
| 362 | data_ -= one; | ||
| 363 | return *this; | ||
| 364 | } | ||
| 365 | |||
| 366 | CONSTEXPR14 FixedPoint operator++(int) { | ||
| 367 | FixedPoint tmp(*this); | ||
| 368 | data_ += one; | ||
| 369 | return tmp; | ||
| 370 | } | ||
| 371 | |||
| 372 | CONSTEXPR14 FixedPoint operator--(int) { | ||
| 373 | FixedPoint tmp(*this); | ||
| 374 | data_ -= one; | ||
| 375 | return tmp; | ||
| 376 | } | ||
| 377 | |||
| 378 | public: // basic math operators | ||
| 379 | CONSTEXPR14 FixedPoint& operator+=(FixedPoint n) { | ||
| 380 | data_ += n.data_; | ||
| 381 | return *this; | ||
| 382 | } | ||
| 383 | |||
| 384 | CONSTEXPR14 FixedPoint& operator-=(FixedPoint n) { | ||
| 385 | data_ -= n.data_; | ||
| 386 | return *this; | ||
| 387 | } | ||
| 388 | |||
| 389 | CONSTEXPR14 FixedPoint& operator*=(FixedPoint n) { | ||
| 390 | return assign(detail::multiply(*this, n)); | ||
| 391 | } | ||
| 392 | |||
| 393 | CONSTEXPR14 FixedPoint& operator/=(FixedPoint n) { | ||
| 394 | FixedPoint temp; | ||
| 395 | return assign(detail::divide(*this, n, temp)); | ||
| 396 | } | ||
| 397 | |||
| 398 | private: | ||
| 399 | CONSTEXPR14 FixedPoint& assign(FixedPoint rhs) { | ||
| 400 | data_ = rhs.data_; | ||
| 401 | return *this; | ||
| 402 | } | ||
| 403 | |||
| 404 | public: // binary math operators, effects underlying bit pattern since these | ||
| 405 | // don't really typically make sense for non-integer values | ||
| 406 | CONSTEXPR14 FixedPoint& operator&=(FixedPoint n) { | ||
| 407 | data_ &= n.data_; | ||
| 408 | return *this; | ||
| 409 | } | ||
| 410 | |||
| 411 | CONSTEXPR14 FixedPoint& operator|=(FixedPoint n) { | ||
| 412 | data_ |= n.data_; | ||
| 413 | return *this; | ||
| 414 | } | ||
| 415 | |||
| 416 | CONSTEXPR14 FixedPoint& operator^=(FixedPoint n) { | ||
| 417 | data_ ^= n.data_; | ||
| 418 | return *this; | ||
| 419 | } | ||
| 420 | |||
| 421 | template <class Integer, | ||
| 422 | class = typename std::enable_if<std::is_integral<Integer>::value>::type> | ||
| 423 | CONSTEXPR14 FixedPoint& operator>>=(Integer n) { | ||
| 424 | data_ >>= n; | ||
| 425 | return *this; | ||
| 426 | } | ||
| 427 | |||
| 428 | template <class Integer, | ||
| 429 | class = typename std::enable_if<std::is_integral<Integer>::value>::type> | ||
| 430 | CONSTEXPR14 FixedPoint& operator<<=(Integer n) { | ||
| 431 | data_ <<= n; | ||
| 432 | return *this; | ||
| 433 | } | ||
| 434 | |||
| 435 | public: // conversion to basic types | ||
| 436 | constexpr void round_up() { | ||
| 437 | data_ += (data_ & fractional_mask) >> 1; | ||
| 438 | } | ||
| 439 | |||
| 440 | constexpr int to_int() { | ||
| 441 | round_up(); | ||
| 442 | return static_cast<int>((data_ & integer_mask) >> fractional_bits); | ||
| 443 | } | ||
| 444 | |||
| 445 | constexpr unsigned int to_uint() const { | ||
| 446 | round_up(); | ||
| 447 | return static_cast<unsigned int>((data_ & integer_mask) >> fractional_bits); | ||
| 448 | } | ||
| 449 | |||
| 450 | constexpr int64_t to_long() { | ||
| 451 | round_up(); | ||
| 452 | return static_cast<int64_t>((data_ & integer_mask) >> fractional_bits); | ||
| 453 | } | ||
| 454 | |||
| 455 | constexpr int to_int_floor() const { | ||
| 456 | return static_cast<int>((data_ & integer_mask) >> fractional_bits); | ||
| 457 | } | ||
| 458 | |||
| 459 | constexpr int64_t to_long_floor() { | ||
| 460 | return static_cast<int64_t>((data_ & integer_mask) >> fractional_bits); | ||
| 461 | } | ||
| 462 | |||
| 463 | constexpr unsigned int to_uint_floor() const { | ||
| 464 | return static_cast<unsigned int>((data_ & integer_mask) >> fractional_bits); | ||
| 465 | } | ||
| 466 | |||
| 467 | constexpr float to_float() const { | ||
| 468 | return static_cast<float>(data_) / FixedPoint::one; | ||
| 469 | } | ||
| 470 | |||
| 471 | constexpr double to_double() const { | ||
| 472 | return static_cast<double>(data_) / FixedPoint::one; | ||
| 473 | } | ||
| 474 | |||
| 475 | constexpr base_type to_raw() const { | ||
| 476 | return data_; | ||
| 477 | } | ||
| 478 | |||
| 479 | constexpr void clear_int() { | ||
| 480 | data_ &= fractional_mask; | ||
| 481 | } | ||
| 482 | |||
| 483 | constexpr base_type get_frac() const { | ||
| 484 | return data_ & fractional_mask; | ||
| 485 | } | ||
| 486 | |||
| 487 | public: | ||
| 488 | CONSTEXPR14 void swap(FixedPoint& rhs) { | ||
| 489 | using std::swap; | ||
| 490 | swap(data_, rhs.data_); | ||
| 491 | } | ||
| 492 | |||
| 493 | public: | ||
| 494 | base_type data_; | ||
| 495 | }; | ||
| 496 | |||
| 497 | // if we have the same fractional portion, but differing integer portions, we trivially upgrade the | ||
| 498 | // smaller type | ||
| 499 | template <size_t I1, size_t I2, size_t F> | ||
| 500 | CONSTEXPR14 typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>::type | ||
| 501 | operator+(FixedPoint<I1, F> lhs, FixedPoint<I2, F> rhs) { | ||
| 502 | |||
| 503 | using T = typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>::type; | ||
| 504 | |||
| 505 | const T l = T::from_base(lhs.to_raw()); | ||
| 506 | const T r = T::from_base(rhs.to_raw()); | ||
| 507 | return l + r; | ||
| 508 | } | ||
| 509 | |||
| 510 | template <size_t I1, size_t I2, size_t F> | ||
| 511 | CONSTEXPR14 typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>::type | ||
| 512 | operator-(FixedPoint<I1, F> lhs, FixedPoint<I2, F> rhs) { | ||
| 513 | |||
| 514 | using T = typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>::type; | ||
| 515 | |||
| 516 | const T l = T::from_base(lhs.to_raw()); | ||
| 517 | const T r = T::from_base(rhs.to_raw()); | ||
| 518 | return l - r; | ||
| 519 | } | ||
| 520 | |||
| 521 | template <size_t I1, size_t I2, size_t F> | ||
| 522 | CONSTEXPR14 typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>::type | ||
| 523 | operator*(FixedPoint<I1, F> lhs, FixedPoint<I2, F> rhs) { | ||
| 524 | |||
| 525 | using T = typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>::type; | ||
| 526 | |||
| 527 | const T l = T::from_base(lhs.to_raw()); | ||
| 528 | const T r = T::from_base(rhs.to_raw()); | ||
| 529 | return l * r; | ||
| 530 | } | ||
| 531 | |||
| 532 | template <size_t I1, size_t I2, size_t F> | ||
| 533 | CONSTEXPR14 typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>::type | ||
| 534 | operator/(FixedPoint<I1, F> lhs, FixedPoint<I2, F> rhs) { | ||
| 535 | |||
| 536 | using T = typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>::type; | ||
| 537 | |||
| 538 | const T l = T::from_base(lhs.to_raw()); | ||
| 539 | const T r = T::from_base(rhs.to_raw()); | ||
| 540 | return l / r; | ||
| 541 | } | ||
| 542 | |||
| 543 | template <size_t I, size_t F> | ||
| 544 | std::ostream& operator<<(std::ostream& os, FixedPoint<I, F> f) { | ||
| 545 | os << f.to_double(); | ||
| 546 | return os; | ||
| 547 | } | ||
| 548 | |||
| 549 | // basic math operators | ||
| 550 | template <size_t I, size_t F> | ||
| 551 | CONSTEXPR14 FixedPoint<I, F> operator+(FixedPoint<I, F> lhs, FixedPoint<I, F> rhs) { | ||
| 552 | lhs += rhs; | ||
| 553 | return lhs; | ||
| 554 | } | ||
| 555 | template <size_t I, size_t F> | ||
| 556 | CONSTEXPR14 FixedPoint<I, F> operator-(FixedPoint<I, F> lhs, FixedPoint<I, F> rhs) { | ||
| 557 | lhs -= rhs; | ||
| 558 | return lhs; | ||
| 559 | } | ||
| 560 | template <size_t I, size_t F> | ||
| 561 | CONSTEXPR14 FixedPoint<I, F> operator*(FixedPoint<I, F> lhs, FixedPoint<I, F> rhs) { | ||
| 562 | lhs *= rhs; | ||
| 563 | return lhs; | ||
| 564 | } | ||
| 565 | template <size_t I, size_t F> | ||
| 566 | CONSTEXPR14 FixedPoint<I, F> operator/(FixedPoint<I, F> lhs, FixedPoint<I, F> rhs) { | ||
| 567 | lhs /= rhs; | ||
| 568 | return lhs; | ||
| 569 | } | ||
| 570 | |||
| 571 | template <size_t I, size_t F, class Number, | ||
| 572 | class = typename std::enable_if<std::is_arithmetic<Number>::value>::type> | ||
| 573 | CONSTEXPR14 FixedPoint<I, F> operator+(FixedPoint<I, F> lhs, Number rhs) { | ||
| 574 | lhs += FixedPoint<I, F>(rhs); | ||
| 575 | return lhs; | ||
| 576 | } | ||
| 577 | template <size_t I, size_t F, class Number, | ||
| 578 | class = typename std::enable_if<std::is_arithmetic<Number>::value>::type> | ||
| 579 | CONSTEXPR14 FixedPoint<I, F> operator-(FixedPoint<I, F> lhs, Number rhs) { | ||
| 580 | lhs -= FixedPoint<I, F>(rhs); | ||
| 581 | return lhs; | ||
| 582 | } | ||
| 583 | template <size_t I, size_t F, class Number, | ||
| 584 | class = typename std::enable_if<std::is_arithmetic<Number>::value>::type> | ||
| 585 | CONSTEXPR14 FixedPoint<I, F> operator*(FixedPoint<I, F> lhs, Number rhs) { | ||
| 586 | lhs *= FixedPoint<I, F>(rhs); | ||
| 587 | return lhs; | ||
| 588 | } | ||
| 589 | template <size_t I, size_t F, class Number, | ||
| 590 | class = typename std::enable_if<std::is_arithmetic<Number>::value>::type> | ||
| 591 | CONSTEXPR14 FixedPoint<I, F> operator/(FixedPoint<I, F> lhs, Number rhs) { | ||
| 592 | lhs /= FixedPoint<I, F>(rhs); | ||
| 593 | return lhs; | ||
| 594 | } | ||
| 595 | |||
| 596 | template <size_t I, size_t F, class Number, | ||
| 597 | class = typename std::enable_if<std::is_arithmetic<Number>::value>::type> | ||
| 598 | CONSTEXPR14 FixedPoint<I, F> operator+(Number lhs, FixedPoint<I, F> rhs) { | ||
| 599 | FixedPoint<I, F> tmp(lhs); | ||
| 600 | tmp += rhs; | ||
| 601 | return tmp; | ||
| 602 | } | ||
| 603 | template <size_t I, size_t F, class Number, | ||
| 604 | class = typename std::enable_if<std::is_arithmetic<Number>::value>::type> | ||
| 605 | CONSTEXPR14 FixedPoint<I, F> operator-(Number lhs, FixedPoint<I, F> rhs) { | ||
| 606 | FixedPoint<I, F> tmp(lhs); | ||
| 607 | tmp -= rhs; | ||
| 608 | return tmp; | ||
| 609 | } | ||
| 610 | template <size_t I, size_t F, class Number, | ||
| 611 | class = typename std::enable_if<std::is_arithmetic<Number>::value>::type> | ||
| 612 | CONSTEXPR14 FixedPoint<I, F> operator*(Number lhs, FixedPoint<I, F> rhs) { | ||
| 613 | FixedPoint<I, F> tmp(lhs); | ||
| 614 | tmp *= rhs; | ||
| 615 | return tmp; | ||
| 616 | } | ||
| 617 | template <size_t I, size_t F, class Number, | ||
| 618 | class = typename std::enable_if<std::is_arithmetic<Number>::value>::type> | ||
| 619 | CONSTEXPR14 FixedPoint<I, F> operator/(Number lhs, FixedPoint<I, F> rhs) { | ||
| 620 | FixedPoint<I, F> tmp(lhs); | ||
| 621 | tmp /= rhs; | ||
| 622 | return tmp; | ||
| 623 | } | ||
| 624 | |||
| 625 | // shift operators | ||
| 626 | template <size_t I, size_t F, class Integer, | ||
| 627 | class = typename std::enable_if<std::is_integral<Integer>::value>::type> | ||
| 628 | CONSTEXPR14 FixedPoint<I, F> operator<<(FixedPoint<I, F> lhs, Integer rhs) { | ||
| 629 | lhs <<= rhs; | ||
| 630 | return lhs; | ||
| 631 | } | ||
| 632 | template <size_t I, size_t F, class Integer, | ||
| 633 | class = typename std::enable_if<std::is_integral<Integer>::value>::type> | ||
| 634 | CONSTEXPR14 FixedPoint<I, F> operator>>(FixedPoint<I, F> lhs, Integer rhs) { | ||
| 635 | lhs >>= rhs; | ||
| 636 | return lhs; | ||
| 637 | } | ||
| 638 | |||
| 639 | // comparison operators | ||
| 640 | template <size_t I, size_t F, class Number, | ||
| 641 | class = typename std::enable_if<std::is_arithmetic<Number>::value>::type> | ||
| 642 | constexpr bool operator>(FixedPoint<I, F> lhs, Number rhs) { | ||
| 643 | return lhs > FixedPoint<I, F>(rhs); | ||
| 644 | } | ||
| 645 | template <size_t I, size_t F, class Number, | ||
| 646 | class = typename std::enable_if<std::is_arithmetic<Number>::value>::type> | ||
| 647 | constexpr bool operator<(FixedPoint<I, F> lhs, Number rhs) { | ||
| 648 | return lhs < FixedPoint<I, F>(rhs); | ||
| 649 | } | ||
| 650 | template <size_t I, size_t F, class Number, | ||
| 651 | class = typename std::enable_if<std::is_arithmetic<Number>::value>::type> | ||
| 652 | constexpr bool operator>=(FixedPoint<I, F> lhs, Number rhs) { | ||
| 653 | return lhs >= FixedPoint<I, F>(rhs); | ||
| 654 | } | ||
| 655 | template <size_t I, size_t F, class Number, | ||
| 656 | class = typename std::enable_if<std::is_arithmetic<Number>::value>::type> | ||
| 657 | constexpr bool operator<=(FixedPoint<I, F> lhs, Number rhs) { | ||
| 658 | return lhs <= FixedPoint<I, F>(rhs); | ||
| 659 | } | ||
| 660 | template <size_t I, size_t F, class Number, | ||
| 661 | class = typename std::enable_if<std::is_arithmetic<Number>::value>::type> | ||
| 662 | constexpr bool operator==(FixedPoint<I, F> lhs, Number rhs) { | ||
| 663 | return lhs == FixedPoint<I, F>(rhs); | ||
| 664 | } | ||
| 665 | template <size_t I, size_t F, class Number, | ||
| 666 | class = typename std::enable_if<std::is_arithmetic<Number>::value>::type> | ||
| 667 | constexpr bool operator!=(FixedPoint<I, F> lhs, Number rhs) { | ||
| 668 | return lhs != FixedPoint<I, F>(rhs); | ||
| 669 | } | ||
| 670 | |||
| 671 | template <size_t I, size_t F, class Number, | ||
| 672 | class = typename std::enable_if<std::is_arithmetic<Number>::value>::type> | ||
| 673 | constexpr bool operator>(Number lhs, FixedPoint<I, F> rhs) { | ||
| 674 | return FixedPoint<I, F>(lhs) > rhs; | ||
| 675 | } | ||
| 676 | template <size_t I, size_t F, class Number, | ||
| 677 | class = typename std::enable_if<std::is_arithmetic<Number>::value>::type> | ||
| 678 | constexpr bool operator<(Number lhs, FixedPoint<I, F> rhs) { | ||
| 679 | return FixedPoint<I, F>(lhs) < rhs; | ||
| 680 | } | ||
| 681 | template <size_t I, size_t F, class Number, | ||
| 682 | class = typename std::enable_if<std::is_arithmetic<Number>::value>::type> | ||
| 683 | constexpr bool operator>=(Number lhs, FixedPoint<I, F> rhs) { | ||
| 684 | return FixedPoint<I, F>(lhs) >= rhs; | ||
| 685 | } | ||
| 686 | template <size_t I, size_t F, class Number, | ||
| 687 | class = typename std::enable_if<std::is_arithmetic<Number>::value>::type> | ||
| 688 | constexpr bool operator<=(Number lhs, FixedPoint<I, F> rhs) { | ||
| 689 | return FixedPoint<I, F>(lhs) <= rhs; | ||
| 690 | } | ||
| 691 | template <size_t I, size_t F, class Number, | ||
| 692 | class = typename std::enable_if<std::is_arithmetic<Number>::value>::type> | ||
| 693 | constexpr bool operator==(Number lhs, FixedPoint<I, F> rhs) { | ||
| 694 | return FixedPoint<I, F>(lhs) == rhs; | ||
| 695 | } | ||
| 696 | template <size_t I, size_t F, class Number, | ||
| 697 | class = typename std::enable_if<std::is_arithmetic<Number>::value>::type> | ||
| 698 | constexpr bool operator!=(Number lhs, FixedPoint<I, F> rhs) { | ||
| 699 | return FixedPoint<I, F>(lhs) != rhs; | ||
| 700 | } | ||
| 701 | |||
| 702 | } // namespace Common | ||
| 703 | |||
| 704 | #undef CONSTEXPR14 | ||
| 705 | |||
| 706 | #endif | ||
diff --git a/src/common/fs/file.cpp b/src/common/fs/file.cpp index 274f57659..fa8422c41 100644 --- a/src/common/fs/file.cpp +++ b/src/common/fs/file.cpp | |||
| @@ -1,10 +1,8 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | // Refer to the license.txt file included. | ||
| 4 | 3 | ||
| 5 | #include "common/fs/file.h" | 4 | #include "common/fs/file.h" |
| 6 | #include "common/fs/fs.h" | 5 | #include "common/fs/fs.h" |
| 7 | #include "common/fs/path_util.h" | ||
| 8 | #include "common/logging/log.h" | 6 | #include "common/logging/log.h" |
| 9 | 7 | ||
| 10 | #ifdef _WIN32 | 8 | #ifdef _WIN32 |
diff --git a/src/common/fs/file.h b/src/common/fs/file.h index a4f7944cd..69b53384c 100644 --- a/src/common/fs/file.h +++ b/src/common/fs/file.h | |||
| @@ -1,15 +1,12 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | // Refer to the license.txt file included. | ||
| 4 | 3 | ||
| 5 | #pragma once | 4 | #pragma once |
| 6 | 5 | ||
| 7 | #include <cstdio> | 6 | #include <cstdio> |
| 8 | #include <filesystem> | 7 | #include <filesystem> |
| 9 | #include <fstream> | ||
| 10 | #include <span> | 8 | #include <span> |
| 11 | #include <type_traits> | 9 | #include <type_traits> |
| 12 | #include <vector> | ||
| 13 | 10 | ||
| 14 | #include "common/concepts.h" | 11 | #include "common/concepts.h" |
| 15 | #include "common/fs/fs_types.h" | 12 | #include "common/fs/fs_types.h" |
diff --git a/src/common/fs/fs.cpp b/src/common/fs/fs.cpp index 9089cad67..e1716c62d 100644 --- a/src/common/fs/fs.cpp +++ b/src/common/fs/fs.cpp | |||
| @@ -1,6 +1,5 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | // Refer to the license.txt file included. | ||
| 4 | 3 | ||
| 5 | #include "common/fs/file.h" | 4 | #include "common/fs/file.h" |
| 6 | #include "common/fs/fs.h" | 5 | #include "common/fs/fs.h" |
diff --git a/src/common/fs/fs.h b/src/common/fs/fs.h index 183126de3..ce3eb309a 100644 --- a/src/common/fs/fs.h +++ b/src/common/fs/fs.h | |||
| @@ -1,6 +1,5 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | // Refer to the license.txt file included. | ||
| 4 | 3 | ||
| 5 | #pragma once | 4 | #pragma once |
| 6 | 5 | ||
diff --git a/src/common/fs/fs_paths.h b/src/common/fs/fs_paths.h index 5d447f108..c77c112f1 100644 --- a/src/common/fs/fs_paths.h +++ b/src/common/fs/fs_paths.h | |||
| @@ -1,6 +1,5 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | // Refer to the license.txt file included. | ||
| 4 | 3 | ||
| 5 | #pragma once | 4 | #pragma once |
| 6 | 5 | ||
diff --git a/src/common/fs/fs_types.h b/src/common/fs/fs_types.h index 089980aee..5a4090c19 100644 --- a/src/common/fs/fs_types.h +++ b/src/common/fs/fs_types.h | |||
| @@ -1,13 +1,11 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | // Refer to the license.txt file included. | ||
| 4 | 3 | ||
| 5 | #pragma once | 4 | #pragma once |
| 6 | 5 | ||
| 7 | #include <functional> | 6 | #include <functional> |
| 8 | 7 | ||
| 9 | #include "common/common_funcs.h" | 8 | #include "common/common_funcs.h" |
| 10 | #include "common/common_types.h" | ||
| 11 | 9 | ||
| 12 | namespace Common::FS { | 10 | namespace Common::FS { |
| 13 | 11 | ||
diff --git a/src/common/fs/fs_util.cpp b/src/common/fs/fs_util.cpp index 9f8671982..eb4ac1deb 100644 --- a/src/common/fs/fs_util.cpp +++ b/src/common/fs/fs_util.cpp | |||
| @@ -1,6 +1,5 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | // Refer to the license.txt file included. | ||
| 4 | 3 | ||
| 5 | #include <algorithm> | 4 | #include <algorithm> |
| 6 | 5 | ||
| @@ -16,6 +15,10 @@ std::u8string BufferToU8String(std::span<const u8> buffer) { | |||
| 16 | return std::u8string{buffer.begin(), std::ranges::find(buffer, u8{0})}; | 15 | return std::u8string{buffer.begin(), std::ranges::find(buffer, u8{0})}; |
| 17 | } | 16 | } |
| 18 | 17 | ||
| 18 | std::u8string_view BufferToU8StringView(std::span<const u8> buffer) { | ||
| 19 | return std::u8string_view{reinterpret_cast<const char8_t*>(buffer.data())}; | ||
| 20 | } | ||
| 21 | |||
| 19 | std::string ToUTF8String(std::u8string_view u8_string) { | 22 | std::string ToUTF8String(std::u8string_view u8_string) { |
| 20 | return std::string{u8_string.begin(), u8_string.end()}; | 23 | return std::string{u8_string.begin(), u8_string.end()}; |
| 21 | } | 24 | } |
| @@ -24,6 +27,10 @@ std::string BufferToUTF8String(std::span<const u8> buffer) { | |||
| 24 | return std::string{buffer.begin(), std::ranges::find(buffer, u8{0})}; | 27 | return std::string{buffer.begin(), std::ranges::find(buffer, u8{0})}; |
| 25 | } | 28 | } |
| 26 | 29 | ||
| 30 | std::string_view BufferToUTF8StringView(std::span<const u8> buffer) { | ||
| 31 | return std::string_view{reinterpret_cast<const char*>(buffer.data())}; | ||
| 32 | } | ||
| 33 | |||
| 27 | std::string PathToUTF8String(const std::filesystem::path& path) { | 34 | std::string PathToUTF8String(const std::filesystem::path& path) { |
| 28 | return ToUTF8String(path.u8string()); | 35 | return ToUTF8String(path.u8string()); |
| 29 | } | 36 | } |
diff --git a/src/common/fs/fs_util.h b/src/common/fs/fs_util.h index 1ec82eb35..2492a9f94 100644 --- a/src/common/fs/fs_util.h +++ b/src/common/fs/fs_util.h | |||
| @@ -1,6 +1,5 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | // Refer to the license.txt file included. | ||
| 4 | 3 | ||
| 5 | #pragma once | 4 | #pragma once |
| 6 | 5 | ||
| @@ -8,7 +7,6 @@ | |||
| 8 | #include <filesystem> | 7 | #include <filesystem> |
| 9 | #include <span> | 8 | #include <span> |
| 10 | #include <string> | 9 | #include <string> |
| 11 | #include <string_view> | ||
| 12 | 10 | ||
| 13 | #include "common/common_types.h" | 11 | #include "common/common_types.h" |
| 14 | 12 | ||
| @@ -38,6 +36,15 @@ concept IsChar = std::same_as<T, char>; | |||
| 38 | [[nodiscard]] std::u8string BufferToU8String(std::span<const u8> buffer); | 36 | [[nodiscard]] std::u8string BufferToU8String(std::span<const u8> buffer); |
| 39 | 37 | ||
| 40 | /** | 38 | /** |
| 39 | * Same as BufferToU8String, but returns a string view of the buffer. | ||
| 40 | * | ||
| 41 | * @param buffer Buffer of bytes | ||
| 42 | * | ||
| 43 | * @returns UTF-8 encoded std::u8string_view. | ||
| 44 | */ | ||
| 45 | [[nodiscard]] std::u8string_view BufferToU8StringView(std::span<const u8> buffer); | ||
| 46 | |||
| 47 | /** | ||
| 41 | * Converts a std::u8string or std::u8string_view to a UTF-8 encoded std::string. | 48 | * Converts a std::u8string or std::u8string_view to a UTF-8 encoded std::string. |
| 42 | * | 49 | * |
| 43 | * @param u8_string UTF-8 encoded u8string | 50 | * @param u8_string UTF-8 encoded u8string |
| @@ -58,6 +65,15 @@ concept IsChar = std::same_as<T, char>; | |||
| 58 | [[nodiscard]] std::string BufferToUTF8String(std::span<const u8> buffer); | 65 | [[nodiscard]] std::string BufferToUTF8String(std::span<const u8> buffer); |
| 59 | 66 | ||
| 60 | /** | 67 | /** |
| 68 | * Same as BufferToUTF8String, but returns a string view of the buffer. | ||
| 69 | * | ||
| 70 | * @param buffer Buffer of bytes | ||
| 71 | * | ||
| 72 | * @returns UTF-8 encoded std::string_view. | ||
| 73 | */ | ||
| 74 | [[nodiscard]] std::string_view BufferToUTF8StringView(std::span<const u8> buffer); | ||
| 75 | |||
| 76 | /** | ||
| 61 | * Converts a filesystem path to a UTF-8 encoded std::string. | 77 | * Converts a filesystem path to a UTF-8 encoded std::string. |
| 62 | * | 78 | * |
| 63 | * @param path Filesystem path | 79 | * @param path Filesystem path |
diff --git a/src/common/fs/path_util.cpp b/src/common/fs/path_util.cpp index 1bcb897b5..1074f2421 100644 --- a/src/common/fs/path_util.cpp +++ b/src/common/fs/path_util.cpp | |||
| @@ -1,6 +1,5 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | // Refer to the license.txt file included. | ||
| 4 | 3 | ||
| 5 | #include <algorithm> | 4 | #include <algorithm> |
| 6 | #include <unordered_map> | 5 | #include <unordered_map> |
| @@ -233,9 +232,7 @@ void SetYuzuPath(YuzuPath yuzu_path, const fs::path& new_path) { | |||
| 233 | fs::path GetExeDirectory() { | 232 | fs::path GetExeDirectory() { |
| 234 | wchar_t exe_path[MAX_PATH]; | 233 | wchar_t exe_path[MAX_PATH]; |
| 235 | 234 | ||
| 236 | GetModuleFileNameW(nullptr, exe_path, MAX_PATH); | 235 | if (GetModuleFileNameW(nullptr, exe_path, MAX_PATH) == 0) { |
| 237 | |||
| 238 | if (!exe_path) { | ||
| 239 | LOG_ERROR(Common_Filesystem, | 236 | LOG_ERROR(Common_Filesystem, |
| 240 | "Failed to get the path to the executable of the current process"); | 237 | "Failed to get the path to the executable of the current process"); |
| 241 | } | 238 | } |
diff --git a/src/common/fs/path_util.h b/src/common/fs/path_util.h index 0a9e3a145..13d713f1e 100644 --- a/src/common/fs/path_util.h +++ b/src/common/fs/path_util.h | |||
| @@ -1,6 +1,5 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | // Refer to the license.txt file included. | ||
| 4 | 3 | ||
| 5 | #pragma once | 4 | #pragma once |
| 6 | 5 | ||
diff --git a/src/common/hash.h b/src/common/hash.h index 298930702..e8fe78b07 100644 --- a/src/common/hash.h +++ b/src/common/hash.h | |||
| @@ -1,6 +1,5 @@ | |||
| 1 | // Copyright 2015 Citra Emulator Project | 1 | // SPDX-FileCopyrightText: 2015 Citra Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | // Refer to the license.txt file included. | ||
| 4 | 3 | ||
| 5 | #pragma once | 4 | #pragma once |
| 6 | 5 | ||
| @@ -19,4 +18,11 @@ struct PairHash { | |||
| 19 | } | 18 | } |
| 20 | }; | 19 | }; |
| 21 | 20 | ||
| 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 | |||
| 22 | } // namespace Common | 28 | } // namespace Common |
diff --git a/src/common/hex_util.cpp b/src/common/hex_util.cpp index 74f52dd11..07053295c 100644 --- a/src/common/hex_util.cpp +++ b/src/common/hex_util.cpp | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project | 1 | // SPDX-FileCopyrightText: 2013 Dolphin Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-FileCopyrightText: 2014 Citra Emulator Project |
| 3 | // Refer to the license.txt file included. | 3 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 4 | 4 | ||
| 5 | #include "common/hex_util.h" | 5 | #include "common/hex_util.h" |
| 6 | 6 | ||
diff --git a/src/common/hex_util.h b/src/common/hex_util.h index 5e9b6ef8b..a00904939 100644 --- a/src/common/hex_util.h +++ b/src/common/hex_util.h | |||
| @@ -1,13 +1,12 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project | 1 | // SPDX-FileCopyrightText: 2013 Dolphin Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-FileCopyrightText: 2014 Citra Emulator Project |
| 3 | // Refer to the license.txt file included. | 3 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <array> | 7 | #include <array> |
| 8 | #include <cstddef> | 8 | #include <cstddef> |
| 9 | #include <string> | 9 | #include <string> |
| 10 | #include <type_traits> | ||
| 11 | #include <vector> | 10 | #include <vector> |
| 12 | #include <fmt/format.h> | 11 | #include <fmt/format.h> |
| 13 | #include "common/common_types.h" | 12 | #include "common/common_types.h" |
diff --git a/src/common/host_memory.cpp b/src/common/host_memory.cpp index 28949fe5e..7f9659612 100644 --- a/src/common/host_memory.cpp +++ b/src/common/host_memory.cpp | |||
| @@ -1,6 +1,5 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | // Refer to the license.txt file included. | ||
| 4 | 3 | ||
| 5 | #ifdef _WIN32 | 4 | #ifdef _WIN32 |
| 6 | 5 | ||
| @@ -18,6 +17,7 @@ | |||
| 18 | #include <fcntl.h> | 17 | #include <fcntl.h> |
| 19 | #include <sys/mman.h> | 18 | #include <sys/mman.h> |
| 20 | #include <unistd.h> | 19 | #include <unistd.h> |
| 20 | #include "common/scope_exit.h" | ||
| 21 | 21 | ||
| 22 | #endif // ^^^ Linux ^^^ | 22 | #endif // ^^^ Linux ^^^ |
| 23 | 23 | ||
| @@ -27,7 +27,6 @@ | |||
| 27 | #include "common/assert.h" | 27 | #include "common/assert.h" |
| 28 | #include "common/host_memory.h" | 28 | #include "common/host_memory.h" |
| 29 | #include "common/logging/log.h" | 29 | #include "common/logging/log.h" |
| 30 | #include "common/scope_exit.h" | ||
| 31 | 30 | ||
| 32 | namespace Common { | 31 | namespace Common { |
| 33 | 32 | ||
| @@ -149,7 +148,7 @@ public: | |||
| 149 | } | 148 | } |
| 150 | 149 | ||
| 151 | void Unmap(size_t virtual_offset, size_t length) { | 150 | void Unmap(size_t virtual_offset, size_t length) { |
| 152 | std::lock_guard lock{placeholder_mutex}; | 151 | std::scoped_lock lock{placeholder_mutex}; |
| 153 | 152 | ||
| 154 | // Unmap until there are no more placeholders | 153 | // Unmap until there are no more placeholders |
| 155 | while (UnmapOnePlaceholder(virtual_offset, length)) { | 154 | while (UnmapOnePlaceholder(virtual_offset, length)) { |
| @@ -169,7 +168,7 @@ public: | |||
| 169 | } | 168 | } |
| 170 | const size_t virtual_end = virtual_offset + length; | 169 | const size_t virtual_end = virtual_offset + length; |
| 171 | 170 | ||
| 172 | std::lock_guard lock{placeholder_mutex}; | 171 | std::scoped_lock lock{placeholder_mutex}; |
| 173 | auto [it, end] = placeholders.equal_range({virtual_offset, virtual_end}); | 172 | auto [it, end] = placeholders.equal_range({virtual_offset, virtual_end}); |
| 174 | while (it != end) { | 173 | while (it != end) { |
| 175 | const size_t offset = std::max(it->lower(), virtual_offset); | 174 | const size_t offset = std::max(it->lower(), virtual_offset); |
| @@ -327,8 +326,8 @@ private: | |||
| 327 | bool IsNiechePlaceholder(size_t virtual_offset, size_t length) const { | 326 | bool IsNiechePlaceholder(size_t virtual_offset, size_t length) const { |
| 328 | const auto it = placeholders.upper_bound({virtual_offset, virtual_offset + length}); | 327 | const auto it = placeholders.upper_bound({virtual_offset, virtual_offset + length}); |
| 329 | if (it != placeholders.end() && it->lower() == virtual_offset + length) { | 328 | if (it != placeholders.end() && it->lower() == virtual_offset + length) { |
| 330 | const bool is_root = it == placeholders.begin() && virtual_offset == 0; | 329 | return it == placeholders.begin() ? virtual_offset == 0 |
| 331 | return is_root || std::prev(it)->upper() == virtual_offset; | 330 | : std::prev(it)->upper() == virtual_offset; |
| 332 | } | 331 | } |
| 333 | return false; | 332 | return false; |
| 334 | } | 333 | } |
diff --git a/src/common/host_memory.h b/src/common/host_memory.h index 9b8326d0f..447975ded 100644 --- a/src/common/host_memory.h +++ b/src/common/host_memory.h | |||
| @@ -1,6 +1,5 @@ | |||
| 1 | // Copyright 2019 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | // Refer to the license.txt file included. | ||
| 4 | 3 | ||
| 5 | #pragma once | 4 | #pragma once |
| 6 | 5 | ||
diff --git a/src/common/input.h b/src/common/input.h index 54fcb24b0..bfa0639f5 100644 --- a/src/common/input.h +++ b/src/common/input.h | |||
| @@ -1,6 +1,5 @@ | |||
| 1 | // Copyright 2017 Citra Emulator Project | 1 | // SPDX-FileCopyrightText: 2017 Citra Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | // Refer to the license.txt file included. | ||
| 4 | 3 | ||
| 5 | #pragma once | 4 | #pragma once |
| 6 | 5 | ||
| @@ -28,7 +27,7 @@ enum class InputType { | |||
| 28 | Color, | 27 | Color, |
| 29 | Vibration, | 28 | Vibration, |
| 30 | Nfc, | 29 | Nfc, |
| 31 | Ir, | 30 | IrSensor, |
| 32 | }; | 31 | }; |
| 33 | 32 | ||
| 34 | // Internal battery charge level | 33 | // Internal battery charge level |
| @@ -53,6 +52,15 @@ enum class PollingMode { | |||
| 53 | IR, | 52 | IR, |
| 54 | }; | 53 | }; |
| 55 | 54 | ||
| 55 | enum class CameraFormat { | ||
| 56 | Size320x240, | ||
| 57 | Size160x120, | ||
| 58 | Size80x60, | ||
| 59 | Size40x30, | ||
| 60 | Size20x15, | ||
| 61 | None, | ||
| 62 | }; | ||
| 63 | |||
| 56 | // Vibration reply from the controller | 64 | // Vibration reply from the controller |
| 57 | enum class VibrationError { | 65 | enum class VibrationError { |
| 58 | None, | 66 | None, |
| @@ -68,10 +76,31 @@ enum class PollingError { | |||
| 68 | Unknown, | 76 | Unknown, |
| 69 | }; | 77 | }; |
| 70 | 78 | ||
| 79 | // Nfc reply from the controller | ||
| 80 | enum class NfcState { | ||
| 81 | Success, | ||
| 82 | NewAmiibo, | ||
| 83 | WaitingForAmiibo, | ||
| 84 | AmiiboRemoved, | ||
| 85 | NotAnAmiibo, | ||
| 86 | NotSupported, | ||
| 87 | WrongDeviceState, | ||
| 88 | WriteFailed, | ||
| 89 | Unknown, | ||
| 90 | }; | ||
| 91 | |||
| 92 | // Ir camera reply from the controller | ||
| 93 | enum class CameraError { | ||
| 94 | None, | ||
| 95 | NotSupported, | ||
| 96 | Unknown, | ||
| 97 | }; | ||
| 98 | |||
| 71 | // Hint for amplification curve to be used | 99 | // Hint for amplification curve to be used |
| 72 | enum class VibrationAmplificationType { | 100 | enum class VibrationAmplificationType { |
| 73 | Linear, | 101 | Linear, |
| 74 | Exponential, | 102 | Exponential, |
| 103 | Test, | ||
| 75 | }; | 104 | }; |
| 76 | 105 | ||
| 77 | // Analog properties for calibration | 106 | // Analog properties for calibration |
| @@ -86,6 +115,8 @@ struct AnalogProperties { | |||
| 86 | float offset{}; | 115 | float offset{}; |
| 87 | // Invert direction of the sensor data | 116 | // Invert direction of the sensor data |
| 88 | bool inverted{}; | 117 | bool inverted{}; |
| 118 | // Press once to activate, press again to release | ||
| 119 | bool toggle{}; | ||
| 89 | }; | 120 | }; |
| 90 | 121 | ||
| 91 | // Single analog sensor data | 122 | // Single analog sensor data |
| @@ -99,8 +130,11 @@ struct AnalogStatus { | |||
| 99 | struct ButtonStatus { | 130 | struct ButtonStatus { |
| 100 | Common::UUID uuid{}; | 131 | Common::UUID uuid{}; |
| 101 | bool value{}; | 132 | bool value{}; |
| 133 | // Invert value of the button | ||
| 102 | bool inverted{}; | 134 | bool inverted{}; |
| 135 | // Press once to activate, press again to release | ||
| 103 | bool toggle{}; | 136 | bool toggle{}; |
| 137 | // Internal lock for the toggle status | ||
| 104 | bool locked{}; | 138 | bool locked{}; |
| 105 | }; | 139 | }; |
| 106 | 140 | ||
| @@ -175,6 +209,17 @@ struct LedStatus { | |||
| 175 | bool led_4{}; | 209 | bool led_4{}; |
| 176 | }; | 210 | }; |
| 177 | 211 | ||
| 212 | // Raw data fom camera | ||
| 213 | struct CameraStatus { | ||
| 214 | CameraFormat format{CameraFormat::None}; | ||
| 215 | std::vector<u8> data{}; | ||
| 216 | }; | ||
| 217 | |||
| 218 | struct NfcStatus { | ||
| 219 | NfcState state{}; | ||
| 220 | std::vector<u8> data{}; | ||
| 221 | }; | ||
| 222 | |||
| 178 | // List of buttons to be passed to Qt that can be translated | 223 | // List of buttons to be passed to Qt that can be translated |
| 179 | enum class ButtonNames { | 224 | enum class ButtonNames { |
| 180 | Undefined, | 225 | Undefined, |
| @@ -232,6 +277,8 @@ struct CallbackStatus { | |||
| 232 | BodyColorStatus color_status{}; | 277 | BodyColorStatus color_status{}; |
| 233 | BatteryStatus battery_status{}; | 278 | BatteryStatus battery_status{}; |
| 234 | VibrationStatus vibration_status{}; | 279 | VibrationStatus vibration_status{}; |
| 280 | CameraStatus camera_status{}; | ||
| 281 | NfcStatus nfc_status{}; | ||
| 235 | }; | 282 | }; |
| 236 | 283 | ||
| 237 | // Triggered once every input change | 284 | // Triggered once every input change |
| @@ -280,6 +327,18 @@ public: | |||
| 280 | virtual PollingError SetPollingMode([[maybe_unused]] PollingMode polling_mode) { | 327 | virtual PollingError SetPollingMode([[maybe_unused]] PollingMode polling_mode) { |
| 281 | return PollingError::NotSupported; | 328 | return PollingError::NotSupported; |
| 282 | } | 329 | } |
| 330 | |||
| 331 | virtual CameraError SetCameraFormat([[maybe_unused]] CameraFormat camera_format) { | ||
| 332 | return CameraError::NotSupported; | ||
| 333 | } | ||
| 334 | |||
| 335 | virtual NfcState SupportsNfc() const { | ||
| 336 | return NfcState::NotSupported; | ||
| 337 | } | ||
| 338 | |||
| 339 | virtual NfcState WriteNfcData([[maybe_unused]] const std::vector<u8>& data) { | ||
| 340 | return NfcState::NotSupported; | ||
| 341 | } | ||
| 283 | }; | 342 | }; |
| 284 | 343 | ||
| 285 | /// An abstract class template for a factory that can create input devices. | 344 | /// An abstract class template for a factory that can create input devices. |
diff --git a/src/common/intrusive_red_black_tree.h b/src/common/intrusive_red_black_tree.h index 3173cc449..93046615e 100644 --- a/src/common/intrusive_red_black_tree.h +++ b/src/common/intrusive_red_black_tree.h | |||
| @@ -1,9 +1,9 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | // Refer to the license.txt file included. | ||
| 4 | 3 | ||
| 5 | #pragma once | 4 | #pragma once |
| 6 | 5 | ||
| 6 | #include "common/common_funcs.h" | ||
| 7 | #include "common/parent_of_member.h" | 7 | #include "common/parent_of_member.h" |
| 8 | #include "common/tree.h" | 8 | #include "common/tree.h" |
| 9 | 9 | ||
| @@ -15,32 +15,33 @@ class IntrusiveRedBlackTreeImpl; | |||
| 15 | 15 | ||
| 16 | } | 16 | } |
| 17 | 17 | ||
| 18 | #pragma pack(push, 4) | ||
| 18 | struct IntrusiveRedBlackTreeNode { | 19 | struct IntrusiveRedBlackTreeNode { |
| 20 | YUZU_NON_COPYABLE(IntrusiveRedBlackTreeNode); | ||
| 21 | |||
| 19 | public: | 22 | public: |
| 20 | using EntryType = RBEntry<IntrusiveRedBlackTreeNode>; | 23 | using RBEntry = freebsd::RBEntry<IntrusiveRedBlackTreeNode>; |
| 21 | 24 | ||
| 22 | constexpr IntrusiveRedBlackTreeNode() = default; | 25 | private: |
| 26 | RBEntry m_entry; | ||
| 23 | 27 | ||
| 24 | void SetEntry(const EntryType& new_entry) { | 28 | public: |
| 25 | entry = new_entry; | 29 | explicit IntrusiveRedBlackTreeNode() = default; |
| 26 | } | ||
| 27 | 30 | ||
| 28 | [[nodiscard]] EntryType& GetEntry() { | 31 | [[nodiscard]] constexpr RBEntry& GetRBEntry() { |
| 29 | return entry; | 32 | return m_entry; |
| 30 | } | 33 | } |
| 31 | 34 | [[nodiscard]] constexpr const RBEntry& GetRBEntry() const { | |
| 32 | [[nodiscard]] const EntryType& GetEntry() const { | 35 | return m_entry; |
| 33 | return entry; | ||
| 34 | } | 36 | } |
| 35 | 37 | ||
| 36 | private: | 38 | constexpr void SetRBEntry(const RBEntry& entry) { |
| 37 | EntryType entry{}; | 39 | m_entry = entry; |
| 38 | 40 | } | |
| 39 | friend class impl::IntrusiveRedBlackTreeImpl; | ||
| 40 | |||
| 41 | template <class, class, class> | ||
| 42 | friend class IntrusiveRedBlackTree; | ||
| 43 | }; | 41 | }; |
| 42 | static_assert(sizeof(IntrusiveRedBlackTreeNode) == | ||
| 43 | 3 * sizeof(void*) + std::max<size_t>(sizeof(freebsd::RBColor), 4)); | ||
| 44 | #pragma pack(pop) | ||
| 44 | 45 | ||
| 45 | template <class T, class Traits, class Comparator> | 46 | template <class T, class Traits, class Comparator> |
| 46 | class IntrusiveRedBlackTree; | 47 | class IntrusiveRedBlackTree; |
| @@ -48,12 +49,17 @@ class IntrusiveRedBlackTree; | |||
| 48 | namespace impl { | 49 | namespace impl { |
| 49 | 50 | ||
| 50 | class IntrusiveRedBlackTreeImpl { | 51 | class IntrusiveRedBlackTreeImpl { |
| 52 | YUZU_NON_COPYABLE(IntrusiveRedBlackTreeImpl); | ||
| 53 | |||
| 51 | private: | 54 | private: |
| 52 | template <class, class, class> | 55 | template <class, class, class> |
| 53 | friend class ::Common::IntrusiveRedBlackTree; | 56 | friend class ::Common::IntrusiveRedBlackTree; |
| 54 | 57 | ||
| 55 | using RootType = RBHead<IntrusiveRedBlackTreeNode>; | 58 | private: |
| 56 | RootType root; | 59 | using RootType = freebsd::RBHead<IntrusiveRedBlackTreeNode>; |
| 60 | |||
| 61 | private: | ||
| 62 | RootType m_root; | ||
| 57 | 63 | ||
| 58 | public: | 64 | public: |
| 59 | template <bool Const> | 65 | template <bool Const> |
| @@ -81,149 +87,150 @@ public: | |||
| 81 | IntrusiveRedBlackTreeImpl::reference>; | 87 | IntrusiveRedBlackTreeImpl::reference>; |
| 82 | 88 | ||
| 83 | private: | 89 | private: |
| 84 | pointer node; | 90 | pointer m_node; |
| 85 | 91 | ||
| 86 | public: | 92 | public: |
| 87 | explicit Iterator(pointer n) : node(n) {} | 93 | constexpr explicit Iterator(pointer n) : m_node(n) {} |
| 88 | 94 | ||
| 89 | bool operator==(const Iterator& rhs) const { | 95 | constexpr bool operator==(const Iterator& rhs) const { |
| 90 | return this->node == rhs.node; | 96 | return m_node == rhs.m_node; |
| 91 | } | 97 | } |
| 92 | 98 | ||
| 93 | bool operator!=(const Iterator& rhs) const { | 99 | constexpr bool operator!=(const Iterator& rhs) const { |
| 94 | return !(*this == rhs); | 100 | return !(*this == rhs); |
| 95 | } | 101 | } |
| 96 | 102 | ||
| 97 | pointer operator->() const { | 103 | constexpr pointer operator->() const { |
| 98 | return this->node; | 104 | return m_node; |
| 99 | } | 105 | } |
| 100 | 106 | ||
| 101 | reference operator*() const { | 107 | constexpr reference operator*() const { |
| 102 | return *this->node; | 108 | return *m_node; |
| 103 | } | 109 | } |
| 104 | 110 | ||
| 105 | Iterator& operator++() { | 111 | constexpr Iterator& operator++() { |
| 106 | this->node = GetNext(this->node); | 112 | m_node = GetNext(m_node); |
| 107 | return *this; | 113 | return *this; |
| 108 | } | 114 | } |
| 109 | 115 | ||
| 110 | Iterator& operator--() { | 116 | constexpr Iterator& operator--() { |
| 111 | this->node = GetPrev(this->node); | 117 | m_node = GetPrev(m_node); |
| 112 | return *this; | 118 | return *this; |
| 113 | } | 119 | } |
| 114 | 120 | ||
| 115 | Iterator operator++(int) { | 121 | constexpr Iterator operator++(int) { |
| 116 | const Iterator it{*this}; | 122 | const Iterator it{*this}; |
| 117 | ++(*this); | 123 | ++(*this); |
| 118 | return it; | 124 | return it; |
| 119 | } | 125 | } |
| 120 | 126 | ||
| 121 | Iterator operator--(int) { | 127 | constexpr Iterator operator--(int) { |
| 122 | const Iterator it{*this}; | 128 | const Iterator it{*this}; |
| 123 | --(*this); | 129 | --(*this); |
| 124 | return it; | 130 | return it; |
| 125 | } | 131 | } |
| 126 | 132 | ||
| 127 | operator Iterator<true>() const { | 133 | constexpr operator Iterator<true>() const { |
| 128 | return Iterator<true>(this->node); | 134 | return Iterator<true>(m_node); |
| 129 | } | 135 | } |
| 130 | }; | 136 | }; |
| 131 | 137 | ||
| 132 | private: | 138 | private: |
| 133 | // Define accessors using RB_* functions. | 139 | constexpr bool EmptyImpl() const { |
| 134 | bool EmptyImpl() const { | 140 | return m_root.IsEmpty(); |
| 135 | return root.IsEmpty(); | ||
| 136 | } | 141 | } |
| 137 | 142 | ||
| 138 | IntrusiveRedBlackTreeNode* GetMinImpl() const { | 143 | constexpr IntrusiveRedBlackTreeNode* GetMinImpl() const { |
| 139 | return RB_MIN(const_cast<RootType*>(&root)); | 144 | return freebsd::RB_MIN(const_cast<RootType&>(m_root)); |
| 140 | } | 145 | } |
| 141 | 146 | ||
| 142 | IntrusiveRedBlackTreeNode* GetMaxImpl() const { | 147 | constexpr IntrusiveRedBlackTreeNode* GetMaxImpl() const { |
| 143 | return RB_MAX(const_cast<RootType*>(&root)); | 148 | return freebsd::RB_MAX(const_cast<RootType&>(m_root)); |
| 144 | } | 149 | } |
| 145 | 150 | ||
| 146 | IntrusiveRedBlackTreeNode* RemoveImpl(IntrusiveRedBlackTreeNode* node) { | 151 | constexpr IntrusiveRedBlackTreeNode* RemoveImpl(IntrusiveRedBlackTreeNode* node) { |
| 147 | return RB_REMOVE(&root, node); | 152 | return freebsd::RB_REMOVE(m_root, node); |
| 148 | } | 153 | } |
| 149 | 154 | ||
| 150 | public: | 155 | public: |
| 151 | static IntrusiveRedBlackTreeNode* GetNext(IntrusiveRedBlackTreeNode* node) { | 156 | static constexpr IntrusiveRedBlackTreeNode* GetNext(IntrusiveRedBlackTreeNode* node) { |
| 152 | return RB_NEXT(node); | 157 | return freebsd::RB_NEXT(node); |
| 153 | } | 158 | } |
| 154 | 159 | ||
| 155 | static IntrusiveRedBlackTreeNode* GetPrev(IntrusiveRedBlackTreeNode* node) { | 160 | static constexpr IntrusiveRedBlackTreeNode* GetPrev(IntrusiveRedBlackTreeNode* node) { |
| 156 | return RB_PREV(node); | 161 | return freebsd::RB_PREV(node); |
| 157 | } | 162 | } |
| 158 | 163 | ||
| 159 | static const IntrusiveRedBlackTreeNode* GetNext(const IntrusiveRedBlackTreeNode* node) { | 164 | static constexpr IntrusiveRedBlackTreeNode const* GetNext( |
| 165 | IntrusiveRedBlackTreeNode const* node) { | ||
| 160 | return static_cast<const IntrusiveRedBlackTreeNode*>( | 166 | return static_cast<const IntrusiveRedBlackTreeNode*>( |
| 161 | GetNext(const_cast<IntrusiveRedBlackTreeNode*>(node))); | 167 | GetNext(const_cast<IntrusiveRedBlackTreeNode*>(node))); |
| 162 | } | 168 | } |
| 163 | 169 | ||
| 164 | static const IntrusiveRedBlackTreeNode* GetPrev(const IntrusiveRedBlackTreeNode* node) { | 170 | static constexpr IntrusiveRedBlackTreeNode const* GetPrev( |
| 171 | IntrusiveRedBlackTreeNode const* node) { | ||
| 165 | return static_cast<const IntrusiveRedBlackTreeNode*>( | 172 | return static_cast<const IntrusiveRedBlackTreeNode*>( |
| 166 | GetPrev(const_cast<IntrusiveRedBlackTreeNode*>(node))); | 173 | GetPrev(const_cast<IntrusiveRedBlackTreeNode*>(node))); |
| 167 | } | 174 | } |
| 168 | 175 | ||
| 169 | public: | 176 | public: |
| 170 | constexpr IntrusiveRedBlackTreeImpl() {} | 177 | constexpr IntrusiveRedBlackTreeImpl() = default; |
| 171 | 178 | ||
| 172 | // Iterator accessors. | 179 | // Iterator accessors. |
| 173 | iterator begin() { | 180 | constexpr iterator begin() { |
| 174 | return iterator(this->GetMinImpl()); | 181 | return iterator(this->GetMinImpl()); |
| 175 | } | 182 | } |
| 176 | 183 | ||
| 177 | const_iterator begin() const { | 184 | constexpr const_iterator begin() const { |
| 178 | return const_iterator(this->GetMinImpl()); | 185 | return const_iterator(this->GetMinImpl()); |
| 179 | } | 186 | } |
| 180 | 187 | ||
| 181 | iterator end() { | 188 | constexpr iterator end() { |
| 182 | return iterator(static_cast<IntrusiveRedBlackTreeNode*>(nullptr)); | 189 | return iterator(static_cast<IntrusiveRedBlackTreeNode*>(nullptr)); |
| 183 | } | 190 | } |
| 184 | 191 | ||
| 185 | const_iterator end() const { | 192 | constexpr const_iterator end() const { |
| 186 | return const_iterator(static_cast<const IntrusiveRedBlackTreeNode*>(nullptr)); | 193 | return const_iterator(static_cast<const IntrusiveRedBlackTreeNode*>(nullptr)); |
| 187 | } | 194 | } |
| 188 | 195 | ||
| 189 | const_iterator cbegin() const { | 196 | constexpr const_iterator cbegin() const { |
| 190 | return this->begin(); | 197 | return this->begin(); |
| 191 | } | 198 | } |
| 192 | 199 | ||
| 193 | const_iterator cend() const { | 200 | constexpr const_iterator cend() const { |
| 194 | return this->end(); | 201 | return this->end(); |
| 195 | } | 202 | } |
| 196 | 203 | ||
| 197 | iterator iterator_to(reference ref) { | 204 | constexpr iterator iterator_to(reference ref) { |
| 198 | return iterator(&ref); | 205 | return iterator(std::addressof(ref)); |
| 199 | } | 206 | } |
| 200 | 207 | ||
| 201 | const_iterator iterator_to(const_reference ref) const { | 208 | constexpr const_iterator iterator_to(const_reference ref) const { |
| 202 | return const_iterator(&ref); | 209 | return const_iterator(std::addressof(ref)); |
| 203 | } | 210 | } |
| 204 | 211 | ||
| 205 | // Content management. | 212 | // Content management. |
| 206 | bool empty() const { | 213 | constexpr bool empty() const { |
| 207 | return this->EmptyImpl(); | 214 | return this->EmptyImpl(); |
| 208 | } | 215 | } |
| 209 | 216 | ||
| 210 | reference back() { | 217 | constexpr reference back() { |
| 211 | return *this->GetMaxImpl(); | 218 | return *this->GetMaxImpl(); |
| 212 | } | 219 | } |
| 213 | 220 | ||
| 214 | const_reference back() const { | 221 | constexpr const_reference back() const { |
| 215 | return *this->GetMaxImpl(); | 222 | return *this->GetMaxImpl(); |
| 216 | } | 223 | } |
| 217 | 224 | ||
| 218 | reference front() { | 225 | constexpr reference front() { |
| 219 | return *this->GetMinImpl(); | 226 | return *this->GetMinImpl(); |
| 220 | } | 227 | } |
| 221 | 228 | ||
| 222 | const_reference front() const { | 229 | constexpr const_reference front() const { |
| 223 | return *this->GetMinImpl(); | 230 | return *this->GetMinImpl(); |
| 224 | } | 231 | } |
| 225 | 232 | ||
| 226 | iterator erase(iterator it) { | 233 | constexpr iterator erase(iterator it) { |
| 227 | auto cur = std::addressof(*it); | 234 | auto cur = std::addressof(*it); |
| 228 | auto next = GetNext(cur); | 235 | auto next = GetNext(cur); |
| 229 | this->RemoveImpl(cur); | 236 | this->RemoveImpl(cur); |
| @@ -234,16 +241,16 @@ public: | |||
| 234 | } // namespace impl | 241 | } // namespace impl |
| 235 | 242 | ||
| 236 | template <typename T> | 243 | template <typename T> |
| 237 | concept HasLightCompareType = requires { | 244 | concept HasRedBlackKeyType = requires { |
| 238 | { std::is_same<typename T::LightCompareType, void>::value } -> std::convertible_to<bool>; | 245 | { std::is_same<typename T::RedBlackKeyType, void>::value } -> std::convertible_to<bool>; |
| 239 | }; | 246 | }; |
| 240 | 247 | ||
| 241 | namespace impl { | 248 | namespace impl { |
| 242 | 249 | ||
| 243 | template <typename T, typename Default> | 250 | template <typename T, typename Default> |
| 244 | consteval auto* GetLightCompareType() { | 251 | consteval auto* GetRedBlackKeyType() { |
| 245 | if constexpr (HasLightCompareType<T>) { | 252 | if constexpr (HasRedBlackKeyType<T>) { |
| 246 | return static_cast<typename T::LightCompareType*>(nullptr); | 253 | return static_cast<typename T::RedBlackKeyType*>(nullptr); |
| 247 | } else { | 254 | } else { |
| 248 | return static_cast<Default*>(nullptr); | 255 | return static_cast<Default*>(nullptr); |
| 249 | } | 256 | } |
| @@ -252,16 +259,17 @@ namespace impl { | |||
| 252 | } // namespace impl | 259 | } // namespace impl |
| 253 | 260 | ||
| 254 | template <typename T, typename Default> | 261 | template <typename T, typename Default> |
| 255 | using LightCompareType = std::remove_pointer_t<decltype(impl::GetLightCompareType<T, Default>())>; | 262 | using RedBlackKeyType = std::remove_pointer_t<decltype(impl::GetRedBlackKeyType<T, Default>())>; |
| 256 | 263 | ||
| 257 | template <class T, class Traits, class Comparator> | 264 | template <class T, class Traits, class Comparator> |
| 258 | class IntrusiveRedBlackTree { | 265 | class IntrusiveRedBlackTree { |
| 266 | YUZU_NON_COPYABLE(IntrusiveRedBlackTree); | ||
| 259 | 267 | ||
| 260 | public: | 268 | public: |
| 261 | using ImplType = impl::IntrusiveRedBlackTreeImpl; | 269 | using ImplType = impl::IntrusiveRedBlackTreeImpl; |
| 262 | 270 | ||
| 263 | private: | 271 | private: |
| 264 | ImplType impl{}; | 272 | ImplType m_impl; |
| 265 | 273 | ||
| 266 | public: | 274 | public: |
| 267 | template <bool Const> | 275 | template <bool Const> |
| @@ -277,9 +285,9 @@ public: | |||
| 277 | using iterator = Iterator<false>; | 285 | using iterator = Iterator<false>; |
| 278 | using const_iterator = Iterator<true>; | 286 | using const_iterator = Iterator<true>; |
| 279 | 287 | ||
| 280 | using light_value_type = LightCompareType<Comparator, value_type>; | 288 | using key_type = RedBlackKeyType<Comparator, value_type>; |
| 281 | using const_light_pointer = const light_value_type*; | 289 | using const_key_pointer = const key_type*; |
| 282 | using const_light_reference = const light_value_type&; | 290 | using const_key_reference = const key_type&; |
| 283 | 291 | ||
| 284 | template <bool Const> | 292 | template <bool Const> |
| 285 | class Iterator { | 293 | class Iterator { |
| @@ -298,183 +306,201 @@ public: | |||
| 298 | IntrusiveRedBlackTree::reference>; | 306 | IntrusiveRedBlackTree::reference>; |
| 299 | 307 | ||
| 300 | private: | 308 | private: |
| 301 | ImplIterator iterator; | 309 | ImplIterator m_impl; |
| 302 | 310 | ||
| 303 | private: | 311 | private: |
| 304 | explicit Iterator(ImplIterator it) : iterator(it) {} | 312 | constexpr explicit Iterator(ImplIterator it) : m_impl(it) {} |
| 305 | 313 | ||
| 306 | explicit Iterator(typename std::conditional<Const, ImplType::const_iterator, | 314 | constexpr explicit Iterator(typename ImplIterator::pointer p) : m_impl(p) {} |
| 307 | ImplType::iterator>::type::pointer ptr) | ||
| 308 | : iterator(ptr) {} | ||
| 309 | 315 | ||
| 310 | ImplIterator GetImplIterator() const { | 316 | constexpr ImplIterator GetImplIterator() const { |
| 311 | return this->iterator; | 317 | return m_impl; |
| 312 | } | 318 | } |
| 313 | 319 | ||
| 314 | public: | 320 | public: |
| 315 | bool operator==(const Iterator& rhs) const { | 321 | constexpr bool operator==(const Iterator& rhs) const { |
| 316 | return this->iterator == rhs.iterator; | 322 | return m_impl == rhs.m_impl; |
| 317 | } | 323 | } |
| 318 | 324 | ||
| 319 | bool operator!=(const Iterator& rhs) const { | 325 | constexpr bool operator!=(const Iterator& rhs) const { |
| 320 | return !(*this == rhs); | 326 | return !(*this == rhs); |
| 321 | } | 327 | } |
| 322 | 328 | ||
| 323 | pointer operator->() const { | 329 | constexpr pointer operator->() const { |
| 324 | return Traits::GetParent(std::addressof(*this->iterator)); | 330 | return Traits::GetParent(std::addressof(*m_impl)); |
| 325 | } | 331 | } |
| 326 | 332 | ||
| 327 | reference operator*() const { | 333 | constexpr reference operator*() const { |
| 328 | return *Traits::GetParent(std::addressof(*this->iterator)); | 334 | return *Traits::GetParent(std::addressof(*m_impl)); |
| 329 | } | 335 | } |
| 330 | 336 | ||
| 331 | Iterator& operator++() { | 337 | constexpr Iterator& operator++() { |
| 332 | ++this->iterator; | 338 | ++m_impl; |
| 333 | return *this; | 339 | return *this; |
| 334 | } | 340 | } |
| 335 | 341 | ||
| 336 | Iterator& operator--() { | 342 | constexpr Iterator& operator--() { |
| 337 | --this->iterator; | 343 | --m_impl; |
| 338 | return *this; | 344 | return *this; |
| 339 | } | 345 | } |
| 340 | 346 | ||
| 341 | Iterator operator++(int) { | 347 | constexpr Iterator operator++(int) { |
| 342 | const Iterator it{*this}; | 348 | const Iterator it{*this}; |
| 343 | ++this->iterator; | 349 | ++m_impl; |
| 344 | return it; | 350 | return it; |
| 345 | } | 351 | } |
| 346 | 352 | ||
| 347 | Iterator operator--(int) { | 353 | constexpr Iterator operator--(int) { |
| 348 | const Iterator it{*this}; | 354 | const Iterator it{*this}; |
| 349 | --this->iterator; | 355 | --m_impl; |
| 350 | return it; | 356 | return it; |
| 351 | } | 357 | } |
| 352 | 358 | ||
| 353 | operator Iterator<true>() const { | 359 | constexpr operator Iterator<true>() const { |
| 354 | return Iterator<true>(this->iterator); | 360 | return Iterator<true>(m_impl); |
| 355 | } | 361 | } |
| 356 | }; | 362 | }; |
| 357 | 363 | ||
| 358 | private: | 364 | private: |
| 359 | static int CompareImpl(const IntrusiveRedBlackTreeNode* lhs, | 365 | static constexpr int CompareImpl(const IntrusiveRedBlackTreeNode* lhs, |
| 360 | const IntrusiveRedBlackTreeNode* rhs) { | 366 | const IntrusiveRedBlackTreeNode* rhs) { |
| 361 | return Comparator::Compare(*Traits::GetParent(lhs), *Traits::GetParent(rhs)); | 367 | return Comparator::Compare(*Traits::GetParent(lhs), *Traits::GetParent(rhs)); |
| 362 | } | 368 | } |
| 363 | 369 | ||
| 364 | static int LightCompareImpl(const void* elm, const IntrusiveRedBlackTreeNode* rhs) { | 370 | static constexpr int CompareKeyImpl(const_key_reference key, |
| 365 | return Comparator::Compare(*static_cast<const_light_pointer>(elm), *Traits::GetParent(rhs)); | 371 | const IntrusiveRedBlackTreeNode* rhs) { |
| 372 | return Comparator::Compare(key, *Traits::GetParent(rhs)); | ||
| 366 | } | 373 | } |
| 367 | 374 | ||
| 368 | // Define accessors using RB_* functions. | 375 | // Define accessors using RB_* functions. |
| 369 | IntrusiveRedBlackTreeNode* InsertImpl(IntrusiveRedBlackTreeNode* node) { | 376 | constexpr IntrusiveRedBlackTreeNode* InsertImpl(IntrusiveRedBlackTreeNode* node) { |
| 370 | return RB_INSERT(&impl.root, node, CompareImpl); | 377 | return freebsd::RB_INSERT(m_impl.m_root, node, CompareImpl); |
| 371 | } | 378 | } |
| 372 | 379 | ||
| 373 | IntrusiveRedBlackTreeNode* FindImpl(const IntrusiveRedBlackTreeNode* node) const { | 380 | constexpr IntrusiveRedBlackTreeNode* FindImpl(IntrusiveRedBlackTreeNode const* node) const { |
| 374 | return RB_FIND(const_cast<ImplType::RootType*>(&impl.root), | 381 | return freebsd::RB_FIND(const_cast<ImplType::RootType&>(m_impl.m_root), |
| 375 | const_cast<IntrusiveRedBlackTreeNode*>(node), CompareImpl); | 382 | const_cast<IntrusiveRedBlackTreeNode*>(node), CompareImpl); |
| 376 | } | 383 | } |
| 377 | 384 | ||
| 378 | IntrusiveRedBlackTreeNode* NFindImpl(const IntrusiveRedBlackTreeNode* node) const { | 385 | constexpr IntrusiveRedBlackTreeNode* NFindImpl(IntrusiveRedBlackTreeNode const* node) const { |
| 379 | return RB_NFIND(const_cast<ImplType::RootType*>(&impl.root), | 386 | return freebsd::RB_NFIND(const_cast<ImplType::RootType&>(m_impl.m_root), |
| 380 | const_cast<IntrusiveRedBlackTreeNode*>(node), CompareImpl); | 387 | const_cast<IntrusiveRedBlackTreeNode*>(node), CompareImpl); |
| 381 | } | 388 | } |
| 382 | 389 | ||
| 383 | IntrusiveRedBlackTreeNode* FindLightImpl(const_light_pointer lelm) const { | 390 | constexpr IntrusiveRedBlackTreeNode* FindKeyImpl(const_key_reference key) const { |
| 384 | return RB_FIND_LIGHT(const_cast<ImplType::RootType*>(&impl.root), | 391 | return freebsd::RB_FIND_KEY(const_cast<ImplType::RootType&>(m_impl.m_root), key, |
| 385 | static_cast<const void*>(lelm), LightCompareImpl); | 392 | CompareKeyImpl); |
| 386 | } | 393 | } |
| 387 | 394 | ||
| 388 | IntrusiveRedBlackTreeNode* NFindLightImpl(const_light_pointer lelm) const { | 395 | constexpr IntrusiveRedBlackTreeNode* NFindKeyImpl(const_key_reference key) const { |
| 389 | return RB_NFIND_LIGHT(const_cast<ImplType::RootType*>(&impl.root), | 396 | return freebsd::RB_NFIND_KEY(const_cast<ImplType::RootType&>(m_impl.m_root), key, |
| 390 | static_cast<const void*>(lelm), LightCompareImpl); | 397 | CompareKeyImpl); |
| 398 | } | ||
| 399 | |||
| 400 | constexpr IntrusiveRedBlackTreeNode* FindExistingImpl( | ||
| 401 | IntrusiveRedBlackTreeNode const* node) const { | ||
| 402 | return freebsd::RB_FIND_EXISTING(const_cast<ImplType::RootType&>(m_impl.m_root), | ||
| 403 | const_cast<IntrusiveRedBlackTreeNode*>(node), CompareImpl); | ||
| 404 | } | ||
| 405 | |||
| 406 | constexpr IntrusiveRedBlackTreeNode* FindExistingKeyImpl(const_key_reference key) const { | ||
| 407 | return freebsd::RB_FIND_EXISTING_KEY(const_cast<ImplType::RootType&>(m_impl.m_root), key, | ||
| 408 | CompareKeyImpl); | ||
| 391 | } | 409 | } |
| 392 | 410 | ||
| 393 | public: | 411 | public: |
| 394 | constexpr IntrusiveRedBlackTree() = default; | 412 | constexpr IntrusiveRedBlackTree() = default; |
| 395 | 413 | ||
| 396 | // Iterator accessors. | 414 | // Iterator accessors. |
| 397 | iterator begin() { | 415 | constexpr iterator begin() { |
| 398 | return iterator(this->impl.begin()); | 416 | return iterator(m_impl.begin()); |
| 399 | } | 417 | } |
| 400 | 418 | ||
| 401 | const_iterator begin() const { | 419 | constexpr const_iterator begin() const { |
| 402 | return const_iterator(this->impl.begin()); | 420 | return const_iterator(m_impl.begin()); |
| 403 | } | 421 | } |
| 404 | 422 | ||
| 405 | iterator end() { | 423 | constexpr iterator end() { |
| 406 | return iterator(this->impl.end()); | 424 | return iterator(m_impl.end()); |
| 407 | } | 425 | } |
| 408 | 426 | ||
| 409 | const_iterator end() const { | 427 | constexpr const_iterator end() const { |
| 410 | return const_iterator(this->impl.end()); | 428 | return const_iterator(m_impl.end()); |
| 411 | } | 429 | } |
| 412 | 430 | ||
| 413 | const_iterator cbegin() const { | 431 | constexpr const_iterator cbegin() const { |
| 414 | return this->begin(); | 432 | return this->begin(); |
| 415 | } | 433 | } |
| 416 | 434 | ||
| 417 | const_iterator cend() const { | 435 | constexpr const_iterator cend() const { |
| 418 | return this->end(); | 436 | return this->end(); |
| 419 | } | 437 | } |
| 420 | 438 | ||
| 421 | iterator iterator_to(reference ref) { | 439 | constexpr iterator iterator_to(reference ref) { |
| 422 | return iterator(this->impl.iterator_to(*Traits::GetNode(std::addressof(ref)))); | 440 | return iterator(m_impl.iterator_to(*Traits::GetNode(std::addressof(ref)))); |
| 423 | } | 441 | } |
| 424 | 442 | ||
| 425 | const_iterator iterator_to(const_reference ref) const { | 443 | constexpr const_iterator iterator_to(const_reference ref) const { |
| 426 | return const_iterator(this->impl.iterator_to(*Traits::GetNode(std::addressof(ref)))); | 444 | return const_iterator(m_impl.iterator_to(*Traits::GetNode(std::addressof(ref)))); |
| 427 | } | 445 | } |
| 428 | 446 | ||
| 429 | // Content management. | 447 | // Content management. |
| 430 | bool empty() const { | 448 | constexpr bool empty() const { |
| 431 | return this->impl.empty(); | 449 | return m_impl.empty(); |
| 432 | } | 450 | } |
| 433 | 451 | ||
| 434 | reference back() { | 452 | constexpr reference back() { |
| 435 | return *Traits::GetParent(std::addressof(this->impl.back())); | 453 | return *Traits::GetParent(std::addressof(m_impl.back())); |
| 436 | } | 454 | } |
| 437 | 455 | ||
| 438 | const_reference back() const { | 456 | constexpr const_reference back() const { |
| 439 | return *Traits::GetParent(std::addressof(this->impl.back())); | 457 | return *Traits::GetParent(std::addressof(m_impl.back())); |
| 440 | } | 458 | } |
| 441 | 459 | ||
| 442 | reference front() { | 460 | constexpr reference front() { |
| 443 | return *Traits::GetParent(std::addressof(this->impl.front())); | 461 | return *Traits::GetParent(std::addressof(m_impl.front())); |
| 444 | } | 462 | } |
| 445 | 463 | ||
| 446 | const_reference front() const { | 464 | constexpr const_reference front() const { |
| 447 | return *Traits::GetParent(std::addressof(this->impl.front())); | 465 | return *Traits::GetParent(std::addressof(m_impl.front())); |
| 448 | } | 466 | } |
| 449 | 467 | ||
| 450 | iterator erase(iterator it) { | 468 | constexpr iterator erase(iterator it) { |
| 451 | return iterator(this->impl.erase(it.GetImplIterator())); | 469 | return iterator(m_impl.erase(it.GetImplIterator())); |
| 452 | } | 470 | } |
| 453 | 471 | ||
| 454 | iterator insert(reference ref) { | 472 | constexpr iterator insert(reference ref) { |
| 455 | ImplType::pointer node = Traits::GetNode(std::addressof(ref)); | 473 | ImplType::pointer node = Traits::GetNode(std::addressof(ref)); |
| 456 | this->InsertImpl(node); | 474 | this->InsertImpl(node); |
| 457 | return iterator(node); | 475 | return iterator(node); |
| 458 | } | 476 | } |
| 459 | 477 | ||
| 460 | iterator find(const_reference ref) const { | 478 | constexpr iterator find(const_reference ref) const { |
| 461 | return iterator(this->FindImpl(Traits::GetNode(std::addressof(ref)))); | 479 | return iterator(this->FindImpl(Traits::GetNode(std::addressof(ref)))); |
| 462 | } | 480 | } |
| 463 | 481 | ||
| 464 | iterator nfind(const_reference ref) const { | 482 | constexpr iterator nfind(const_reference ref) const { |
| 465 | return iterator(this->NFindImpl(Traits::GetNode(std::addressof(ref)))); | 483 | return iterator(this->NFindImpl(Traits::GetNode(std::addressof(ref)))); |
| 466 | } | 484 | } |
| 467 | 485 | ||
| 468 | iterator find_light(const_light_reference ref) const { | 486 | constexpr iterator find_key(const_key_reference ref) const { |
| 469 | return iterator(this->FindLightImpl(std::addressof(ref))); | 487 | return iterator(this->FindKeyImpl(ref)); |
| 488 | } | ||
| 489 | |||
| 490 | constexpr iterator nfind_key(const_key_reference ref) const { | ||
| 491 | return iterator(this->NFindKeyImpl(ref)); | ||
| 492 | } | ||
| 493 | |||
| 494 | constexpr iterator find_existing(const_reference ref) const { | ||
| 495 | return iterator(this->FindExistingImpl(Traits::GetNode(std::addressof(ref)))); | ||
| 470 | } | 496 | } |
| 471 | 497 | ||
| 472 | iterator nfind_light(const_light_reference ref) const { | 498 | constexpr iterator find_existing_key(const_key_reference ref) const { |
| 473 | return iterator(this->NFindLightImpl(std::addressof(ref))); | 499 | return iterator(this->FindExistingKeyImpl(ref)); |
| 474 | } | 500 | } |
| 475 | }; | 501 | }; |
| 476 | 502 | ||
| 477 | template <auto T, class Derived = impl::GetParentType<T>> | 503 | template <auto T, class Derived = Common::impl::GetParentType<T>> |
| 478 | class IntrusiveRedBlackTreeMemberTraits; | 504 | class IntrusiveRedBlackTreeMemberTraits; |
| 479 | 505 | ||
| 480 | template <class Parent, IntrusiveRedBlackTreeNode Parent::*Member, class Derived> | 506 | template <class Parent, IntrusiveRedBlackTreeNode Parent::*Member, class Derived> |
| @@ -498,19 +524,16 @@ private: | |||
| 498 | return std::addressof(parent->*Member); | 524 | return std::addressof(parent->*Member); |
| 499 | } | 525 | } |
| 500 | 526 | ||
| 501 | static constexpr Derived* GetParent(IntrusiveRedBlackTreeNode* node) { | 527 | static Derived* GetParent(IntrusiveRedBlackTreeNode* node) { |
| 502 | return GetParentPointer<Member, Derived>(node); | 528 | return Common::GetParentPointer<Member, Derived>(node); |
| 503 | } | 529 | } |
| 504 | 530 | ||
| 505 | static constexpr Derived const* GetParent(const IntrusiveRedBlackTreeNode* node) { | 531 | static Derived const* GetParent(IntrusiveRedBlackTreeNode const* node) { |
| 506 | return GetParentPointer<Member, Derived>(node); | 532 | return Common::GetParentPointer<Member, Derived>(node); |
| 507 | } | 533 | } |
| 508 | |||
| 509 | private: | ||
| 510 | static constexpr TypedStorage<Derived> DerivedStorage = {}; | ||
| 511 | }; | 534 | }; |
| 512 | 535 | ||
| 513 | template <auto T, class Derived = impl::GetParentType<T>> | 536 | template <auto T, class Derived = Common::impl::GetParentType<T>> |
| 514 | class IntrusiveRedBlackTreeMemberTraitsDeferredAssert; | 537 | class IntrusiveRedBlackTreeMemberTraitsDeferredAssert; |
| 515 | 538 | ||
| 516 | template <class Parent, IntrusiveRedBlackTreeNode Parent::*Member, class Derived> | 539 | template <class Parent, IntrusiveRedBlackTreeNode Parent::*Member, class Derived> |
| @@ -521,11 +544,6 @@ public: | |||
| 521 | IntrusiveRedBlackTree<Derived, IntrusiveRedBlackTreeMemberTraitsDeferredAssert, Comparator>; | 544 | IntrusiveRedBlackTree<Derived, IntrusiveRedBlackTreeMemberTraitsDeferredAssert, Comparator>; |
| 522 | using TreeTypeImpl = impl::IntrusiveRedBlackTreeImpl; | 545 | using TreeTypeImpl = impl::IntrusiveRedBlackTreeImpl; |
| 523 | 546 | ||
| 524 | static constexpr bool IsValid() { | ||
| 525 | TypedStorage<Derived> DerivedStorage = {}; | ||
| 526 | return GetParent(GetNode(GetPointer(DerivedStorage))) == GetPointer(DerivedStorage); | ||
| 527 | } | ||
| 528 | |||
| 529 | private: | 547 | private: |
| 530 | template <class, class, class> | 548 | template <class, class, class> |
| 531 | friend class IntrusiveRedBlackTree; | 549 | friend class IntrusiveRedBlackTree; |
| @@ -540,30 +558,36 @@ private: | |||
| 540 | return std::addressof(parent->*Member); | 558 | return std::addressof(parent->*Member); |
| 541 | } | 559 | } |
| 542 | 560 | ||
| 543 | static constexpr Derived* GetParent(IntrusiveRedBlackTreeNode* node) { | 561 | static Derived* GetParent(IntrusiveRedBlackTreeNode* node) { |
| 544 | return GetParentPointer<Member, Derived>(node); | 562 | return Common::GetParentPointer<Member, Derived>(node); |
| 545 | } | 563 | } |
| 546 | 564 | ||
| 547 | static constexpr Derived const* GetParent(const IntrusiveRedBlackTreeNode* node) { | 565 | static Derived const* GetParent(IntrusiveRedBlackTreeNode const* node) { |
| 548 | return GetParentPointer<Member, Derived>(node); | 566 | return Common::GetParentPointer<Member, Derived>(node); |
| 549 | } | 567 | } |
| 550 | }; | 568 | }; |
| 551 | 569 | ||
| 552 | template <class Derived> | 570 | template <class Derived> |
| 553 | class IntrusiveRedBlackTreeBaseNode : public IntrusiveRedBlackTreeNode { | 571 | class alignas(void*) IntrusiveRedBlackTreeBaseNode : public IntrusiveRedBlackTreeNode { |
| 554 | public: | 572 | public: |
| 573 | using IntrusiveRedBlackTreeNode::IntrusiveRedBlackTreeNode; | ||
| 574 | |||
| 555 | constexpr Derived* GetPrev() { | 575 | constexpr Derived* GetPrev() { |
| 556 | return static_cast<Derived*>(impl::IntrusiveRedBlackTreeImpl::GetPrev(this)); | 576 | return static_cast<Derived*>(static_cast<IntrusiveRedBlackTreeBaseNode*>( |
| 577 | impl::IntrusiveRedBlackTreeImpl::GetPrev(this))); | ||
| 557 | } | 578 | } |
| 558 | constexpr const Derived* GetPrev() const { | 579 | constexpr const Derived* GetPrev() const { |
| 559 | return static_cast<const Derived*>(impl::IntrusiveRedBlackTreeImpl::GetPrev(this)); | 580 | return static_cast<const Derived*>(static_cast<const IntrusiveRedBlackTreeBaseNode*>( |
| 581 | impl::IntrusiveRedBlackTreeImpl::GetPrev(this))); | ||
| 560 | } | 582 | } |
| 561 | 583 | ||
| 562 | constexpr Derived* GetNext() { | 584 | constexpr Derived* GetNext() { |
| 563 | return static_cast<Derived*>(impl::IntrusiveRedBlackTreeImpl::GetNext(this)); | 585 | return static_cast<Derived*>(static_cast<IntrusiveRedBlackTreeBaseNode*>( |
| 586 | impl::IntrusiveRedBlackTreeImpl::GetNext(this))); | ||
| 564 | } | 587 | } |
| 565 | constexpr const Derived* GetNext() const { | 588 | constexpr const Derived* GetNext() const { |
| 566 | return static_cast<const Derived*>(impl::IntrusiveRedBlackTreeImpl::GetNext(this)); | 589 | return static_cast<const Derived*>(static_cast<const IntrusiveRedBlackTreeBaseNode*>( |
| 590 | impl::IntrusiveRedBlackTreeImpl::GetNext(this))); | ||
| 567 | } | 591 | } |
| 568 | }; | 592 | }; |
| 569 | 593 | ||
| @@ -581,19 +605,22 @@ private: | |||
| 581 | friend class impl::IntrusiveRedBlackTreeImpl; | 605 | friend class impl::IntrusiveRedBlackTreeImpl; |
| 582 | 606 | ||
| 583 | static constexpr IntrusiveRedBlackTreeNode* GetNode(Derived* parent) { | 607 | static constexpr IntrusiveRedBlackTreeNode* GetNode(Derived* parent) { |
| 584 | return static_cast<IntrusiveRedBlackTreeNode*>(parent); | 608 | return static_cast<IntrusiveRedBlackTreeNode*>( |
| 609 | static_cast<IntrusiveRedBlackTreeBaseNode<Derived>*>(parent)); | ||
| 585 | } | 610 | } |
| 586 | 611 | ||
| 587 | static constexpr IntrusiveRedBlackTreeNode const* GetNode(Derived const* parent) { | 612 | static constexpr IntrusiveRedBlackTreeNode const* GetNode(Derived const* parent) { |
| 588 | return static_cast<const IntrusiveRedBlackTreeNode*>(parent); | 613 | return static_cast<const IntrusiveRedBlackTreeNode*>( |
| 614 | static_cast<const IntrusiveRedBlackTreeBaseNode<Derived>*>(parent)); | ||
| 589 | } | 615 | } |
| 590 | 616 | ||
| 591 | static constexpr Derived* GetParent(IntrusiveRedBlackTreeNode* node) { | 617 | static constexpr Derived* GetParent(IntrusiveRedBlackTreeNode* node) { |
| 592 | return static_cast<Derived*>(node); | 618 | return static_cast<Derived*>(static_cast<IntrusiveRedBlackTreeBaseNode<Derived>*>(node)); |
| 593 | } | 619 | } |
| 594 | 620 | ||
| 595 | static constexpr Derived const* GetParent(const IntrusiveRedBlackTreeNode* node) { | 621 | static constexpr Derived const* GetParent(IntrusiveRedBlackTreeNode const* node) { |
| 596 | return static_cast<const Derived*>(node); | 622 | return static_cast<const Derived*>( |
| 623 | static_cast<const IntrusiveRedBlackTreeBaseNode<Derived>*>(node)); | ||
| 597 | } | 624 | } |
| 598 | }; | 625 | }; |
| 599 | 626 | ||
diff --git a/src/common/literals.h b/src/common/literals.h index d55fed40b..0ad314afb 100644 --- a/src/common/literals.h +++ b/src/common/literals.h | |||
| @@ -1,6 +1,5 @@ | |||
| 1 | // Copyright 2021 yuzu emulator team | 1 | // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | // Refer to the license.txt file included. | ||
| 4 | 3 | ||
| 5 | #pragma once | 4 | #pragma once |
| 6 | 5 | ||
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp index c51c05b28..15d92505e 100644 --- a/src/common/logging/backend.cpp +++ b/src/common/logging/backend.cpp | |||
| @@ -1,14 +1,11 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // SPDX-FileCopyrightText: 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | // Refer to the license.txt file included. | ||
| 4 | 3 | ||
| 5 | #include <atomic> | 4 | #include <atomic> |
| 6 | #include <chrono> | 5 | #include <chrono> |
| 7 | #include <climits> | 6 | #include <climits> |
| 8 | #include <exception> | ||
| 9 | #include <stop_token> | 7 | #include <stop_token> |
| 10 | #include <thread> | 8 | #include <thread> |
| 11 | #include <vector> | ||
| 12 | 9 | ||
| 13 | #include <fmt/format.h> | 10 | #include <fmt/format.h> |
| 14 | 11 | ||
| @@ -218,19 +215,17 @@ private: | |||
| 218 | Impl(const std::filesystem::path& file_backend_filename, const Filter& filter_) | 215 | Impl(const std::filesystem::path& file_backend_filename, const Filter& filter_) |
| 219 | : filter{filter_}, file_backend{file_backend_filename} {} | 216 | : filter{filter_}, file_backend{file_backend_filename} {} |
| 220 | 217 | ||
| 221 | ~Impl() { | 218 | ~Impl() = default; |
| 222 | StopBackendThread(); | ||
| 223 | } | ||
| 224 | 219 | ||
| 225 | void StartBackendThread() { | 220 | void StartBackendThread() { |
| 226 | backend_thread = std::thread([this] { | 221 | backend_thread = std::jthread([this](std::stop_token stop_token) { |
| 227 | Common::SetCurrentThreadName("yuzu:Log"); | 222 | Common::SetCurrentThreadName("Logger"); |
| 228 | Entry entry; | 223 | Entry entry; |
| 229 | const auto write_logs = [this, &entry]() { | 224 | const auto write_logs = [this, &entry]() { |
| 230 | ForEachBackend([&entry](Backend& backend) { backend.Write(entry); }); | 225 | ForEachBackend([&entry](Backend& backend) { backend.Write(entry); }); |
| 231 | }; | 226 | }; |
| 232 | while (!stop.stop_requested()) { | 227 | while (!stop_token.stop_requested()) { |
| 233 | entry = message_queue.PopWait(stop.get_token()); | 228 | entry = message_queue.PopWait(stop_token); |
| 234 | if (entry.filename != nullptr) { | 229 | if (entry.filename != nullptr) { |
| 235 | write_logs(); | 230 | write_logs(); |
| 236 | } | 231 | } |
| @@ -244,11 +239,6 @@ private: | |||
| 244 | }); | 239 | }); |
| 245 | } | 240 | } |
| 246 | 241 | ||
| 247 | void StopBackendThread() { | ||
| 248 | stop.request_stop(); | ||
| 249 | backend_thread.join(); | ||
| 250 | } | ||
| 251 | |||
| 252 | Entry CreateEntry(Class log_class, Level log_level, const char* filename, unsigned int line_nr, | 242 | Entry CreateEntry(Class log_class, Level log_level, const char* filename, unsigned int line_nr, |
| 253 | const char* function, std::string&& message) const { | 243 | const char* function, std::string&& message) const { |
| 254 | using std::chrono::duration_cast; | 244 | using std::chrono::duration_cast; |
| @@ -283,10 +273,9 @@ private: | |||
| 283 | ColorConsoleBackend color_console_backend{}; | 273 | ColorConsoleBackend color_console_backend{}; |
| 284 | FileBackend file_backend; | 274 | FileBackend file_backend; |
| 285 | 275 | ||
| 286 | std::stop_source stop; | ||
| 287 | std::thread backend_thread; | ||
| 288 | MPSCQueue<Entry, true> message_queue{}; | 276 | MPSCQueue<Entry, true> message_queue{}; |
| 289 | std::chrono::steady_clock::time_point time_origin{std::chrono::steady_clock::now()}; | 277 | std::chrono::steady_clock::time_point time_origin{std::chrono::steady_clock::now()}; |
| 278 | std::jthread backend_thread; | ||
| 290 | }; | 279 | }; |
| 291 | } // namespace | 280 | } // namespace |
| 292 | 281 | ||
diff --git a/src/common/logging/backend.h b/src/common/logging/backend.h index bf785f402..12e5e2498 100644 --- a/src/common/logging/backend.h +++ b/src/common/logging/backend.h | |||
| @@ -1,10 +1,8 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // SPDX-FileCopyrightText: 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | // Refer to the license.txt file included. | ||
| 4 | 3 | ||
| 5 | #pragma once | 4 | #pragma once |
| 6 | 5 | ||
| 7 | #include <filesystem> | ||
| 8 | #include "common/logging/filter.h" | 6 | #include "common/logging/filter.h" |
| 9 | 7 | ||
| 10 | namespace Common::Log { | 8 | namespace Common::Log { |
diff --git a/src/common/logging/filter.cpp b/src/common/logging/filter.cpp index b898a652c..a959acb74 100644 --- a/src/common/logging/filter.cpp +++ b/src/common/logging/filter.cpp | |||
| @@ -1,6 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // SPDX-FileCopyrightText: 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | // Refer to the license.txt file included. | ||
| 4 | 3 | ||
| 5 | #include <algorithm> | 4 | #include <algorithm> |
| 6 | #include "common/logging/filter.h" | 5 | #include "common/logging/filter.h" |
| @@ -101,6 +100,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) { | |||
| 101 | SUB(Service, GRC) \ | 100 | SUB(Service, GRC) \ |
| 102 | SUB(Service, HID) \ | 101 | SUB(Service, HID) \ |
| 103 | SUB(Service, IRS) \ | 102 | SUB(Service, IRS) \ |
| 103 | SUB(Service, JIT) \ | ||
| 104 | SUB(Service, LBL) \ | 104 | SUB(Service, LBL) \ |
| 105 | SUB(Service, LDN) \ | 105 | SUB(Service, LDN) \ |
| 106 | SUB(Service, LDR) \ | 106 | SUB(Service, LDR) \ |
| @@ -108,6 +108,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) { | |||
| 108 | SUB(Service, Migration) \ | 108 | SUB(Service, Migration) \ |
| 109 | SUB(Service, Mii) \ | 109 | SUB(Service, Mii) \ |
| 110 | SUB(Service, MM) \ | 110 | SUB(Service, MM) \ |
| 111 | SUB(Service, MNPP) \ | ||
| 111 | SUB(Service, NCM) \ | 112 | SUB(Service, NCM) \ |
| 112 | SUB(Service, NFC) \ | 113 | SUB(Service, NFC) \ |
| 113 | SUB(Service, NFP) \ | 114 | SUB(Service, NFP) \ |
| @@ -118,6 +119,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) { | |||
| 118 | SUB(Service, NPNS) \ | 119 | SUB(Service, NPNS) \ |
| 119 | SUB(Service, NS) \ | 120 | SUB(Service, NS) \ |
| 120 | SUB(Service, NVDRV) \ | 121 | SUB(Service, NVDRV) \ |
| 122 | SUB(Service, NVFlinger) \ | ||
| 121 | SUB(Service, OLSC) \ | 123 | SUB(Service, OLSC) \ |
| 122 | SUB(Service, PCIE) \ | 124 | SUB(Service, PCIE) \ |
| 123 | SUB(Service, PCTL) \ | 125 | SUB(Service, PCTL) \ |
| @@ -125,7 +127,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) { | |||
| 125 | SUB(Service, PM) \ | 127 | SUB(Service, PM) \ |
| 126 | SUB(Service, PREPO) \ | 128 | SUB(Service, PREPO) \ |
| 127 | SUB(Service, PSC) \ | 129 | SUB(Service, PSC) \ |
| 128 | SUB(Service, PSM) \ | 130 | SUB(Service, PTM) \ |
| 129 | SUB(Service, SET) \ | 131 | SUB(Service, SET) \ |
| 130 | SUB(Service, SM) \ | 132 | SUB(Service, SM) \ |
| 131 | SUB(Service, SPL) \ | 133 | SUB(Service, SPL) \ |
diff --git a/src/common/logging/filter.h b/src/common/logging/filter.h index 1a3074e04..54d172cc0 100644 --- a/src/common/logging/filter.h +++ b/src/common/logging/filter.h | |||
| @@ -1,13 +1,11 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // SPDX-FileCopyrightText: 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | // Refer to the license.txt file included. | ||
| 4 | 3 | ||
| 5 | #pragma once | 4 | #pragma once |
| 6 | 5 | ||
| 7 | #include <array> | 6 | #include <array> |
| 8 | #include <chrono> | 7 | #include <chrono> |
| 9 | #include <cstddef> | 8 | #include <cstddef> |
| 10 | #include <string_view> | ||
| 11 | #include "common/logging/log.h" | 9 | #include "common/logging/log.h" |
| 12 | 10 | ||
| 13 | namespace Common::Log { | 11 | namespace Common::Log { |
diff --git a/src/common/logging/formatter.h b/src/common/logging/formatter.h index 552cde75a..88e55505d 100644 --- a/src/common/logging/formatter.h +++ b/src/common/logging/formatter.h | |||
| @@ -1,6 +1,5 @@ | |||
| 1 | // Copyright 2022 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | // Refer to the license.txt file included. | ||
| 4 | 3 | ||
| 5 | #pragma once | 4 | #pragma once |
| 6 | 5 | ||
diff --git a/src/common/logging/log.h b/src/common/logging/log.h index 0c80d01ee..c00c01a9e 100644 --- a/src/common/logging/log.h +++ b/src/common/logging/log.h | |||
| @@ -1,6 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // SPDX-FileCopyrightText: 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | // Refer to the license.txt file included. | ||
| 4 | 3 | ||
| 5 | #pragma once | 4 | #pragma once |
| 6 | 5 | ||
diff --git a/src/common/logging/log_entry.h b/src/common/logging/log_entry.h index b28570071..d8d7daf76 100644 --- a/src/common/logging/log_entry.h +++ b/src/common/logging/log_entry.h | |||
| @@ -1,6 +1,5 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | // Refer to the license.txt file included. | ||
| 4 | 3 | ||
| 5 | #pragma once | 4 | #pragma once |
| 6 | 5 | ||
diff --git a/src/common/logging/text_formatter.cpp b/src/common/logging/text_formatter.cpp index 10b2281db..09398ea64 100644 --- a/src/common/logging/text_formatter.cpp +++ b/src/common/logging/text_formatter.cpp | |||
| @@ -1,6 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // SPDX-FileCopyrightText: 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | // Refer to the license.txt file included. | ||
| 4 | 3 | ||
| 5 | #include <array> | 4 | #include <array> |
| 6 | #include <cstdio> | 5 | #include <cstdio> |
| @@ -10,12 +9,10 @@ | |||
| 10 | #endif | 9 | #endif |
| 11 | 10 | ||
| 12 | #include "common/assert.h" | 11 | #include "common/assert.h" |
| 13 | #include "common/common_funcs.h" | ||
| 14 | #include "common/logging/filter.h" | 12 | #include "common/logging/filter.h" |
| 15 | #include "common/logging/log.h" | 13 | #include "common/logging/log.h" |
| 16 | #include "common/logging/log_entry.h" | 14 | #include "common/logging/log_entry.h" |
| 17 | #include "common/logging/text_formatter.h" | 15 | #include "common/logging/text_formatter.h" |
| 18 | #include "common/string_util.h" | ||
| 19 | 16 | ||
| 20 | namespace Common::Log { | 17 | namespace Common::Log { |
| 21 | 18 | ||
diff --git a/src/common/logging/text_formatter.h b/src/common/logging/text_formatter.h index 171e74cfe..0d0ec4370 100644 --- a/src/common/logging/text_formatter.h +++ b/src/common/logging/text_formatter.h | |||
| @@ -1,10 +1,8 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // SPDX-FileCopyrightText: 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | // Refer to the license.txt file included. | ||
| 4 | 3 | ||
| 5 | #pragma once | 4 | #pragma once |
| 6 | 5 | ||
| 7 | #include <cstddef> | ||
| 8 | #include <string> | 6 | #include <string> |
| 9 | 7 | ||
| 10 | namespace Common::Log { | 8 | namespace Common::Log { |
diff --git a/src/common/logging/types.h b/src/common/logging/types.h index 9ed0c7ad6..595c15ada 100644 --- a/src/common/logging/types.h +++ b/src/common/logging/types.h | |||
| @@ -1,6 +1,5 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | // Refer to the license.txt file included. | ||
| 4 | 3 | ||
| 5 | #pragma once | 4 | #pragma once |
| 6 | 5 | ||
| @@ -69,6 +68,7 @@ enum class Class : u8 { | |||
| 69 | Service_GRC, ///< The game recording service | 68 | Service_GRC, ///< The game recording service |
| 70 | Service_HID, ///< The HID (Human interface device) service | 69 | Service_HID, ///< The HID (Human interface device) service |
| 71 | Service_IRS, ///< The IRS service | 70 | Service_IRS, ///< The IRS service |
| 71 | Service_JIT, ///< The JIT service | ||
| 72 | Service_LBL, ///< The LBL (LCD backlight) service | 72 | Service_LBL, ///< The LBL (LCD backlight) service |
| 73 | Service_LDN, ///< The LDN (Local domain network) service | 73 | Service_LDN, ///< The LDN (Local domain network) service |
| 74 | Service_LDR, ///< The loader service | 74 | Service_LDR, ///< The loader service |
| @@ -76,6 +76,7 @@ enum class Class : u8 { | |||
| 76 | Service_Migration, ///< The migration service | 76 | Service_Migration, ///< The migration service |
| 77 | Service_Mii, ///< The Mii service | 77 | Service_Mii, ///< The Mii service |
| 78 | Service_MM, ///< The MM (Multimedia) service | 78 | Service_MM, ///< The MM (Multimedia) service |
| 79 | Service_MNPP, ///< The MNPP service | ||
| 79 | Service_NCM, ///< The NCM service | 80 | Service_NCM, ///< The NCM service |
| 80 | Service_NFC, ///< The NFC (Near-field communication) service | 81 | Service_NFC, ///< The NFC (Near-field communication) service |
| 81 | Service_NFP, ///< The NFP service | 82 | Service_NFP, ///< The NFP service |
| @@ -86,6 +87,7 @@ enum class Class : u8 { | |||
| 86 | Service_NPNS, ///< The NPNS service | 87 | Service_NPNS, ///< The NPNS service |
| 87 | Service_NS, ///< The NS services | 88 | Service_NS, ///< The NS services |
| 88 | Service_NVDRV, ///< The NVDRV (Nvidia driver) service | 89 | Service_NVDRV, ///< The NVDRV (Nvidia driver) service |
| 90 | Service_NVFlinger, ///< The NVFlinger service | ||
| 89 | Service_OLSC, ///< The OLSC service | 91 | Service_OLSC, ///< The OLSC service |
| 90 | Service_PCIE, ///< The PCIe service | 92 | Service_PCIE, ///< The PCIe service |
| 91 | Service_PCTL, ///< The PCTL (Parental control) service | 93 | Service_PCTL, ///< The PCTL (Parental control) service |
| @@ -93,7 +95,7 @@ enum class Class : u8 { | |||
| 93 | Service_PM, ///< The PM service | 95 | Service_PM, ///< The PM service |
| 94 | Service_PREPO, ///< The PREPO (Play report) service | 96 | Service_PREPO, ///< The PREPO (Play report) service |
| 95 | Service_PSC, ///< The PSC service | 97 | Service_PSC, ///< The PSC service |
| 96 | Service_PSM, ///< The PSM service | 98 | Service_PTM, ///< The PTM service |
| 97 | Service_SET, ///< The SET (Settings) service | 99 | Service_SET, ///< The SET (Settings) service |
| 98 | Service_SM, ///< The SM (Service manager) service | 100 | Service_SM, ///< The SM (Service manager) service |
| 99 | Service_SPL, ///< The SPL service | 101 | Service_SPL, ///< The SPL service |
diff --git a/src/common/lru_cache.h b/src/common/lru_cache.h index 365488ba5..36cea5d27 100644 --- a/src/common/lru_cache.h +++ b/src/common/lru_cache.h | |||
| @@ -1,6 +1,5 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project |
| 2 | // Licensed under GPLv2+ or any later version | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | // Refer to the license.txt file included. | ||
| 4 | 3 | ||
| 5 | #pragma once | 4 | #pragma once |
| 6 | 5 | ||
diff --git a/src/common/lz4_compression.cpp b/src/common/lz4_compression.cpp index dbb40da7c..ffb32fecf 100644 --- a/src/common/lz4_compression.cpp +++ b/src/common/lz4_compression.cpp | |||
| @@ -1,6 +1,5 @@ | |||
| 1 | // Copyright 2019 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | // Refer to the license.txt file included. | ||
| 4 | 3 | ||
| 5 | #include <algorithm> | 4 | #include <algorithm> |
| 6 | #include <lz4hc.h> | 5 | #include <lz4hc.h> |
diff --git a/src/common/lz4_compression.h b/src/common/lz4_compression.h index 1b4717595..7fd53a960 100644 --- a/src/common/lz4_compression.h +++ b/src/common/lz4_compression.h | |||
| @@ -1,6 +1,5 @@ | |||
| 1 | // Copyright 2019 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | // Refer to the license.txt file included. | ||
| 4 | 3 | ||
| 5 | #pragma once | 4 | #pragma once |
| 6 | 5 | ||
diff --git a/src/common/math_util.h b/src/common/math_util.h index 510c4e56d..1f5928c15 100644 --- a/src/common/math_util.h +++ b/src/common/math_util.h | |||
| @@ -1,9 +1,10 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project | 1 | // SPDX-FileCopyrightText: 2013 Dolphin Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-FileCopyrightText: 2014 Citra Emulator Project |
| 3 | // Refer to the license.txt file included. | 3 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <algorithm> | ||
| 7 | #include <cstdlib> | 8 | #include <cstdlib> |
| 8 | #include <type_traits> | 9 | #include <type_traits> |
| 9 | 10 | ||
| @@ -20,10 +21,32 @@ struct Rectangle { | |||
| 20 | 21 | ||
| 21 | constexpr Rectangle() = default; | 22 | constexpr Rectangle() = default; |
| 22 | 23 | ||
| 24 | constexpr Rectangle(T width, T height) : right(width), bottom(height) {} | ||
| 25 | |||
| 23 | constexpr Rectangle(T left_, T top_, T right_, T bottom_) | 26 | constexpr Rectangle(T left_, T top_, T right_, T bottom_) |
| 24 | : left(left_), top(top_), right(right_), bottom(bottom_) {} | 27 | : left(left_), top(top_), right(right_), bottom(bottom_) {} |
| 25 | 28 | ||
| 26 | [[nodiscard]] T GetWidth() const { | 29 | [[nodiscard]] constexpr T Left() const { |
| 30 | return left; | ||
| 31 | } | ||
| 32 | |||
| 33 | [[nodiscard]] constexpr T Top() const { | ||
| 34 | return top; | ||
| 35 | } | ||
| 36 | |||
| 37 | [[nodiscard]] constexpr T Right() const { | ||
| 38 | return right; | ||
| 39 | } | ||
| 40 | |||
| 41 | [[nodiscard]] constexpr T Bottom() const { | ||
| 42 | return bottom; | ||
| 43 | } | ||
| 44 | |||
| 45 | [[nodiscard]] constexpr bool IsEmpty() const { | ||
| 46 | return (GetWidth() <= 0) || (GetHeight() <= 0); | ||
| 47 | } | ||
| 48 | |||
| 49 | [[nodiscard]] constexpr T GetWidth() const { | ||
| 27 | if constexpr (std::is_floating_point_v<T>) { | 50 | if constexpr (std::is_floating_point_v<T>) { |
| 28 | return std::abs(right - left); | 51 | return std::abs(right - left); |
| 29 | } else { | 52 | } else { |
| @@ -31,7 +54,7 @@ struct Rectangle { | |||
| 31 | } | 54 | } |
| 32 | } | 55 | } |
| 33 | 56 | ||
| 34 | [[nodiscard]] T GetHeight() const { | 57 | [[nodiscard]] constexpr T GetHeight() const { |
| 35 | if constexpr (std::is_floating_point_v<T>) { | 58 | if constexpr (std::is_floating_point_v<T>) { |
| 36 | return std::abs(bottom - top); | 59 | return std::abs(bottom - top); |
| 37 | } else { | 60 | } else { |
| @@ -39,18 +62,35 @@ struct Rectangle { | |||
| 39 | } | 62 | } |
| 40 | } | 63 | } |
| 41 | 64 | ||
| 42 | [[nodiscard]] Rectangle<T> TranslateX(const T x) const { | 65 | [[nodiscard]] constexpr Rectangle<T> TranslateX(const T x) const { |
| 43 | return Rectangle{left + x, top, right + x, bottom}; | 66 | return Rectangle{left + x, top, right + x, bottom}; |
| 44 | } | 67 | } |
| 45 | 68 | ||
| 46 | [[nodiscard]] Rectangle<T> TranslateY(const T y) const { | 69 | [[nodiscard]] constexpr Rectangle<T> TranslateY(const T y) const { |
| 47 | return Rectangle{left, top + y, right, bottom + y}; | 70 | return Rectangle{left, top + y, right, bottom + y}; |
| 48 | } | 71 | } |
| 49 | 72 | ||
| 50 | [[nodiscard]] Rectangle<T> Scale(const float s) const { | 73 | [[nodiscard]] constexpr Rectangle<T> Scale(const float s) const { |
| 51 | return Rectangle{left, top, static_cast<T>(static_cast<float>(left + GetWidth()) * s), | 74 | return Rectangle{left, top, static_cast<T>(static_cast<float>(left + GetWidth()) * s), |
| 52 | static_cast<T>(static_cast<float>(top + GetHeight()) * s)}; | 75 | static_cast<T>(static_cast<float>(top + GetHeight()) * s)}; |
| 53 | } | 76 | } |
| 77 | |||
| 78 | [[nodiscard]] constexpr bool operator==(const Rectangle<T>& rhs) const { | ||
| 79 | return (left == rhs.left) && (top == rhs.top) && (right == rhs.right) && | ||
| 80 | (bottom == rhs.bottom); | ||
| 81 | } | ||
| 82 | |||
| 83 | [[nodiscard]] constexpr bool operator!=(const Rectangle<T>& rhs) const { | ||
| 84 | return !operator==(rhs); | ||
| 85 | } | ||
| 86 | |||
| 87 | [[nodiscard]] constexpr bool Intersect(const Rectangle<T>& with, Rectangle<T>* result) const { | ||
| 88 | result->left = std::max(left, with.left); | ||
| 89 | result->top = std::max(top, with.top); | ||
| 90 | result->right = std::min(right, with.right); | ||
| 91 | result->bottom = std::min(bottom, with.bottom); | ||
| 92 | return !result->IsEmpty(); | ||
| 93 | } | ||
| 54 | }; | 94 | }; |
| 55 | 95 | ||
| 56 | template <typename T> | 96 | template <typename T> |
diff --git a/src/common/memory_detect.cpp b/src/common/memory_detect.cpp index 8cff6ec37..86a3abcc6 100644 --- a/src/common/memory_detect.cpp +++ b/src/common/memory_detect.cpp | |||
| @@ -1,6 +1,5 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | // Refer to the license.txt file included. | ||
| 4 | 3 | ||
| 5 | #ifdef _WIN32 | 4 | #ifdef _WIN32 |
| 6 | // clang-format off | 5 | // clang-format off |
| @@ -70,4 +69,4 @@ const MemoryInfo& GetMemInfo() { | |||
| 70 | return mem_info; | 69 | return mem_info; |
| 71 | } | 70 | } |
| 72 | 71 | ||
| 73 | } // namespace Common \ No newline at end of file | 72 | } // namespace Common |
diff --git a/src/common/memory_detect.h b/src/common/memory_detect.h index 0f73751c8..a345e6d28 100644 --- a/src/common/memory_detect.h +++ b/src/common/memory_detect.h | |||
| @@ -1,6 +1,5 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | // Refer to the license.txt file included. | ||
| 4 | 3 | ||
| 5 | #pragma once | 4 | #pragma once |
| 6 | 5 | ||
diff --git a/src/common/microprofile.cpp b/src/common/microprofile.cpp index ee25dd37f..e6657c82f 100644 --- a/src/common/microprofile.cpp +++ b/src/common/microprofile.cpp | |||
| @@ -1,6 +1,5 @@ | |||
| 1 | // Copyright 2015 Citra Emulator Project | 1 | // SPDX-FileCopyrightText: 2015 Citra Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | // Refer to the license.txt file included. | ||
| 4 | 3 | ||
| 5 | // Includes the MicroProfile implementation in this file for compilation | 4 | // Includes the MicroProfile implementation in this file for compilation |
| 6 | #define MICROPROFILE_IMPL 1 | 5 | #define MICROPROFILE_IMPL 1 |
diff --git a/src/common/microprofile.h b/src/common/microprofile.h index 54e7f3cc4..56ef0a2dc 100644 --- a/src/common/microprofile.h +++ b/src/common/microprofile.h | |||
| @@ -1,6 +1,5 @@ | |||
| 1 | // Copyright 2015 Citra Emulator Project | 1 | // SPDX-FileCopyrightText: 2015 Citra Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | // Refer to the license.txt file included. | ||
| 4 | 3 | ||
| 5 | #pragma once | 4 | #pragma once |
| 6 | 5 | ||
| @@ -23,12 +22,3 @@ typedef void* HANDLE; | |||
| 23 | #include <microprofile.h> | 22 | #include <microprofile.h> |
| 24 | 23 | ||
| 25 | #define MP_RGB(r, g, b) ((r) << 16 | (g) << 8 | (b) << 0) | 24 | #define MP_RGB(r, g, b) ((r) << 16 | (g) << 8 | (b) << 0) |
| 26 | |||
| 27 | // On OS X, some Mach header included by MicroProfile defines these as macros, conflicting with | ||
| 28 | // identifiers we use. | ||
| 29 | #ifdef PAGE_SIZE | ||
| 30 | #undef PAGE_SIZE | ||
| 31 | #endif | ||
| 32 | #ifdef PAGE_MASK | ||
| 33 | #undef PAGE_MASK | ||
| 34 | #endif | ||
diff --git a/src/common/microprofileui.h b/src/common/microprofileui.h index 41abe6b75..39ed18ffa 100644 --- a/src/common/microprofileui.h +++ b/src/common/microprofileui.h | |||
| @@ -1,6 +1,5 @@ | |||
| 1 | // Copyright 2015 Citra Emulator Project | 1 | // SPDX-FileCopyrightText: 2015 Citra Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | // Refer to the license.txt file included. | ||
| 4 | 3 | ||
| 5 | #pragma once | 4 | #pragma once |
| 6 | 5 | ||
diff --git a/src/common/multi_level_page_table.cpp b/src/common/multi_level_page_table.cpp new file mode 100644 index 000000000..46e362f3b --- /dev/null +++ b/src/common/multi_level_page_table.cpp | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2021 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "common/multi_level_page_table.inc" | ||
| 5 | |||
| 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/nvidia_flags.cpp b/src/common/nvidia_flags.cpp index d1afd1f1d..7ed7690ee 100644 --- a/src/common/nvidia_flags.cpp +++ b/src/common/nvidia_flags.cpp | |||
| @@ -1,12 +1,10 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | // Refer to the license.txt file included. | ||
| 4 | 3 | ||
| 5 | #include <cstdlib> | 4 | #include <cstdlib> |
| 6 | 5 | ||
| 7 | #include <fmt/format.h> | 6 | #include <fmt/format.h> |
| 8 | 7 | ||
| 9 | #include "common/fs/file.h" | ||
| 10 | #include "common/fs/fs.h" | 8 | #include "common/fs/fs.h" |
| 11 | #include "common/fs/path_util.h" | 9 | #include "common/fs/path_util.h" |
| 12 | #include "common/nvidia_flags.h" | 10 | #include "common/nvidia_flags.h" |
diff --git a/src/common/nvidia_flags.h b/src/common/nvidia_flags.h index 8930efcec..8c3b1bfb9 100644 --- a/src/common/nvidia_flags.h +++ b/src/common/nvidia_flags.h | |||
| @@ -1,6 +1,5 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | // Refer to the license.txt file included. | ||
| 4 | 3 | ||
| 5 | #pragma once | 4 | #pragma once |
| 6 | 5 | ||
diff --git a/src/common/page_table.cpp b/src/common/page_table.cpp index 9fffd816f..b744b68ce 100644 --- a/src/common/page_table.cpp +++ b/src/common/page_table.cpp | |||
| @@ -1,6 +1,5 @@ | |||
| 1 | // Copyright 2019 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | // Refer to the license.txt file included. | ||
| 4 | 3 | ||
| 5 | #include "common/page_table.h" | 4 | #include "common/page_table.h" |
| 6 | 5 | ||
| @@ -10,11 +9,65 @@ PageTable::PageTable() = default; | |||
| 10 | 9 | ||
| 11 | PageTable::~PageTable() noexcept = default; | 10 | PageTable::~PageTable() noexcept = default; |
| 12 | 11 | ||
| 13 | void PageTable::Resize(size_t address_space_width_in_bits, size_t page_size_in_bits) { | 12 | bool PageTable::BeginTraversal(TraversalEntry& out_entry, TraversalContext& out_context, |
| 14 | const size_t num_page_table_entries{1ULL << (address_space_width_in_bits - page_size_in_bits)}; | 13 | u64 address) const { |
| 14 | // Setup invalid defaults. | ||
| 15 | out_entry.phys_addr = 0; | ||
| 16 | out_entry.block_size = page_size; | ||
| 17 | out_context.next_page = 0; | ||
| 18 | |||
| 19 | // Validate that we can read the actual entry. | ||
| 20 | const auto page = address / page_size; | ||
| 21 | if (page >= backing_addr.size()) { | ||
| 22 | return false; | ||
| 23 | } | ||
| 24 | |||
| 25 | // Validate that the entry is mapped. | ||
| 26 | const auto phys_addr = backing_addr[page]; | ||
| 27 | if (phys_addr == 0) { | ||
| 28 | return false; | ||
| 29 | } | ||
| 30 | |||
| 31 | // Populate the results. | ||
| 32 | out_entry.phys_addr = phys_addr + address; | ||
| 33 | out_context.next_page = page + 1; | ||
| 34 | out_context.next_offset = address + page_size; | ||
| 35 | |||
| 36 | return true; | ||
| 37 | } | ||
| 38 | |||
| 39 | bool PageTable::ContinueTraversal(TraversalEntry& out_entry, TraversalContext& context) const { | ||
| 40 | // Setup invalid defaults. | ||
| 41 | out_entry.phys_addr = 0; | ||
| 42 | out_entry.block_size = page_size; | ||
| 43 | |||
| 44 | // Validate that we can read the actual entry. | ||
| 45 | const auto page = context.next_page; | ||
| 46 | if (page >= backing_addr.size()) { | ||
| 47 | return false; | ||
| 48 | } | ||
| 49 | |||
| 50 | // Validate that the entry is mapped. | ||
| 51 | const auto phys_addr = backing_addr[page]; | ||
| 52 | if (phys_addr == 0) { | ||
| 53 | return false; | ||
| 54 | } | ||
| 55 | |||
| 56 | // Populate the results. | ||
| 57 | out_entry.phys_addr = phys_addr + context.next_offset; | ||
| 58 | context.next_page = page + 1; | ||
| 59 | context.next_offset += page_size; | ||
| 60 | |||
| 61 | return true; | ||
| 62 | } | ||
| 63 | |||
| 64 | void PageTable::Resize(std::size_t address_space_width_in_bits, std::size_t page_size_in_bits) { | ||
| 65 | const std::size_t num_page_table_entries{1ULL | ||
| 66 | << (address_space_width_in_bits - page_size_in_bits)}; | ||
| 15 | pointers.resize(num_page_table_entries); | 67 | pointers.resize(num_page_table_entries); |
| 16 | backing_addr.resize(num_page_table_entries); | 68 | backing_addr.resize(num_page_table_entries); |
| 17 | current_address_space_width_in_bits = address_space_width_in_bits; | 69 | current_address_space_width_in_bits = address_space_width_in_bits; |
| 70 | page_size = 1ULL << page_size_in_bits; | ||
| 18 | } | 71 | } |
| 19 | 72 | ||
| 20 | } // namespace Common | 73 | } // namespace Common |
diff --git a/src/common/page_table.h b/src/common/page_table.h index 8267e8b4d..1ad3a9f8b 100644 --- a/src/common/page_table.h +++ b/src/common/page_table.h | |||
| @@ -1,11 +1,9 @@ | |||
| 1 | // Copyright 2019 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | // Refer to the license.txt file included. | ||
| 4 | 3 | ||
| 5 | #pragma once | 4 | #pragma once |
| 6 | 5 | ||
| 7 | #include <atomic> | 6 | #include <atomic> |
| 8 | #include <tuple> | ||
| 9 | 7 | ||
| 10 | #include "common/common_types.h" | 8 | #include "common/common_types.h" |
| 11 | #include "common/virtual_buffer.h" | 9 | #include "common/virtual_buffer.h" |
| @@ -17,6 +15,9 @@ enum class PageType : u8 { | |||
| 17 | Unmapped, | 15 | Unmapped, |
| 18 | /// Page is mapped to regular memory. This is the only type you can get pointers to. | 16 | /// Page is mapped to regular memory. This is the only type you can get pointers to. |
| 19 | Memory, | 17 | Memory, |
| 18 | /// Page is mapped to regular memory, but inaccessible from CPU fastmem and must use | ||
| 19 | /// the callbacks. | ||
| 20 | DebugMemory, | ||
| 20 | /// Page is mapped to regular memory, but also needs to check for rasterizer cache flushing and | 21 | /// Page is mapped to regular memory, but also needs to check for rasterizer cache flushing and |
| 21 | /// invalidation | 22 | /// invalidation |
| 22 | RasterizerCachedMemory, | 23 | RasterizerCachedMemory, |
| @@ -27,6 +28,16 @@ enum class PageType : u8 { | |||
| 27 | * mimics the way a real CPU page table works. | 28 | * mimics the way a real CPU page table works. |
| 28 | */ | 29 | */ |
| 29 | struct PageTable { | 30 | struct PageTable { |
| 31 | struct TraversalEntry { | ||
| 32 | u64 phys_addr{}; | ||
| 33 | std::size_t block_size{}; | ||
| 34 | }; | ||
| 35 | |||
| 36 | struct TraversalContext { | ||
| 37 | u64 next_page{}; | ||
| 38 | u64 next_offset{}; | ||
| 39 | }; | ||
| 40 | |||
| 30 | /// Number of bits reserved for attribute tagging. | 41 | /// Number of bits reserved for attribute tagging. |
| 31 | /// This can be at most the guaranteed alignment of the pointers in the page table. | 42 | /// This can be at most the guaranteed alignment of the pointers in the page table. |
| 32 | static constexpr int ATTRIBUTE_BITS = 2; | 43 | static constexpr int ATTRIBUTE_BITS = 2; |
| @@ -89,6 +100,10 @@ struct PageTable { | |||
| 89 | PageTable(PageTable&&) noexcept = default; | 100 | PageTable(PageTable&&) noexcept = default; |
| 90 | PageTable& operator=(PageTable&&) noexcept = default; | 101 | PageTable& operator=(PageTable&&) noexcept = default; |
| 91 | 102 | ||
| 103 | bool BeginTraversal(TraversalEntry& out_entry, TraversalContext& out_context, | ||
| 104 | u64 address) const; | ||
| 105 | bool ContinueTraversal(TraversalEntry& out_entry, TraversalContext& context) const; | ||
| 106 | |||
| 92 | /** | 107 | /** |
| 93 | * Resizes the page table to be able to accommodate enough pages within | 108 | * Resizes the page table to be able to accommodate enough pages within |
| 94 | * a given address space. | 109 | * a given address space. |
| @@ -96,9 +111,9 @@ struct PageTable { | |||
| 96 | * @param address_space_width_in_bits The address size width in bits. | 111 | * @param address_space_width_in_bits The address size width in bits. |
| 97 | * @param page_size_in_bits The page size in bits. | 112 | * @param page_size_in_bits The page size in bits. |
| 98 | */ | 113 | */ |
| 99 | void Resize(size_t address_space_width_in_bits, size_t page_size_in_bits); | 114 | void Resize(std::size_t address_space_width_in_bits, std::size_t page_size_in_bits); |
| 100 | 115 | ||
| 101 | size_t GetAddressSpaceBits() const { | 116 | std::size_t GetAddressSpaceBits() const { |
| 102 | return current_address_space_width_in_bits; | 117 | return current_address_space_width_in_bits; |
| 103 | } | 118 | } |
| 104 | 119 | ||
| @@ -110,9 +125,11 @@ struct PageTable { | |||
| 110 | 125 | ||
| 111 | VirtualBuffer<u64> backing_addr; | 126 | VirtualBuffer<u64> backing_addr; |
| 112 | 127 | ||
| 113 | size_t current_address_space_width_in_bits; | 128 | std::size_t current_address_space_width_in_bits{}; |
| 129 | |||
| 130 | u8* fastmem_arena{}; | ||
| 114 | 131 | ||
| 115 | u8* fastmem_arena; | 132 | std::size_t page_size{}; |
| 116 | }; | 133 | }; |
| 117 | 134 | ||
| 118 | } // namespace Common | 135 | } // namespace Common |
diff --git a/src/common/param_package.cpp b/src/common/param_package.cpp index bbf20f5eb..629babb81 100644 --- a/src/common/param_package.cpp +++ b/src/common/param_package.cpp | |||
| @@ -1,6 +1,5 @@ | |||
| 1 | // Copyright 2017 Citra Emulator Project | 1 | // SPDX-FileCopyrightText: 2017 Citra Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | // Refer to the license.txt file included. | ||
| 4 | 3 | ||
| 5 | #include <array> | 4 | #include <array> |
| 6 | #include <stdexcept> | 5 | #include <stdexcept> |
| @@ -76,7 +75,7 @@ std::string ParamPackage::Serialize() const { | |||
| 76 | std::string ParamPackage::Get(const std::string& key, const std::string& default_value) const { | 75 | std::string ParamPackage::Get(const std::string& key, const std::string& default_value) const { |
| 77 | auto pair = data.find(key); | 76 | auto pair = data.find(key); |
| 78 | if (pair == data.end()) { | 77 | if (pair == data.end()) { |
| 79 | LOG_DEBUG(Common, "key '{}' not found", key); | 78 | LOG_TRACE(Common, "key '{}' not found", key); |
| 80 | return default_value; | 79 | return default_value; |
| 81 | } | 80 | } |
| 82 | 81 | ||
| @@ -86,7 +85,7 @@ std::string ParamPackage::Get(const std::string& key, const std::string& default | |||
| 86 | int ParamPackage::Get(const std::string& key, int default_value) const { | 85 | int ParamPackage::Get(const std::string& key, int default_value) const { |
| 87 | auto pair = data.find(key); | 86 | auto pair = data.find(key); |
| 88 | if (pair == data.end()) { | 87 | if (pair == data.end()) { |
| 89 | LOG_DEBUG(Common, "key '{}' not found", key); | 88 | LOG_TRACE(Common, "key '{}' not found", key); |
| 90 | return default_value; | 89 | return default_value; |
| 91 | } | 90 | } |
| 92 | 91 | ||
| @@ -101,7 +100,7 @@ int ParamPackage::Get(const std::string& key, int default_value) const { | |||
| 101 | float ParamPackage::Get(const std::string& key, float default_value) const { | 100 | float ParamPackage::Get(const std::string& key, float default_value) const { |
| 102 | auto pair = data.find(key); | 101 | auto pair = data.find(key); |
| 103 | if (pair == data.end()) { | 102 | if (pair == data.end()) { |
| 104 | LOG_DEBUG(Common, "key {} not found", key); | 103 | LOG_TRACE(Common, "key {} not found", key); |
| 105 | return default_value; | 104 | return default_value; |
| 106 | } | 105 | } |
| 107 | 106 | ||
diff --git a/src/common/param_package.h b/src/common/param_package.h index c13e45479..d7c13cb1f 100644 --- a/src/common/param_package.h +++ b/src/common/param_package.h | |||
| @@ -1,6 +1,5 @@ | |||
| 1 | // Copyright 2017 Citra Emulator Project | 1 | // SPDX-FileCopyrightText: 2017 Citra Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | // Refer to the license.txt file included. | ||
| 4 | 3 | ||
| 5 | #pragma once | 4 | #pragma once |
| 6 | 5 | ||
diff --git a/src/common/parent_of_member.h b/src/common/parent_of_member.h index 58c70b0e7..8e03f17d8 100644 --- a/src/common/parent_of_member.h +++ b/src/common/parent_of_member.h | |||
| @@ -1,19 +1,17 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | // Refer to the license.txt file included. | ||
| 4 | 3 | ||
| 5 | #pragma once | 4 | #pragma once |
| 6 | 5 | ||
| 7 | #include <type_traits> | 6 | #include <type_traits> |
| 8 | 7 | ||
| 9 | #include "common/assert.h" | 8 | #include "common/assert.h" |
| 10 | #include "common/common_types.h" | ||
| 11 | 9 | ||
| 12 | namespace Common { | 10 | namespace Common { |
| 13 | namespace detail { | 11 | namespace detail { |
| 14 | template <typename T, size_t Size, size_t Align> | 12 | template <typename T, size_t Size, size_t Align> |
| 15 | struct TypedStorageImpl { | 13 | struct TypedStorageImpl { |
| 16 | std::aligned_storage_t<Size, Align> storage_; | 14 | alignas(Align) u8 storage_[Size]; |
| 17 | }; | 15 | }; |
| 18 | } // namespace detail | 16 | } // namespace detail |
| 19 | 17 | ||
diff --git a/src/common/point.h b/src/common/point.h index c0a52ad8d..6491856ea 100644 --- a/src/common/point.h +++ b/src/common/point.h | |||
| @@ -1,6 +1,5 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | // Refer to the license.txt file included. | ||
| 4 | 3 | ||
| 5 | #pragma once | 4 | #pragma once |
| 6 | 5 | ||
diff --git a/src/common/quaternion.h b/src/common/quaternion.h index 4d0871eb4..5bb5f2af0 100644 --- a/src/common/quaternion.h +++ b/src/common/quaternion.h | |||
| @@ -1,6 +1,5 @@ | |||
| 1 | // Copyright 2016 Citra Emulator Project | 1 | // SPDX-FileCopyrightText: 2016 Citra Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | // Refer to the license.txt file included. | ||
| 4 | 3 | ||
| 5 | #pragma once | 4 | #pragma once |
| 6 | 5 | ||
diff --git a/src/common/reader_writer_queue.h b/src/common/reader_writer_queue.h new file mode 100644 index 000000000..60c41a8cb --- /dev/null +++ b/src/common/reader_writer_queue.h | |||
| @@ -0,0 +1,940 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2013-2020 Cameron Desrochers | ||
| 2 | // SPDX-License-Identifier: BSD-2-Clause | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <cassert> | ||
| 7 | #include <cstdint> | ||
| 8 | #include <cstdlib> // For malloc/free/abort & size_t | ||
| 9 | #include <memory> | ||
| 10 | #include <new> | ||
| 11 | #include <stdexcept> | ||
| 12 | #include <type_traits> | ||
| 13 | #include <utility> | ||
| 14 | |||
| 15 | #include "common/atomic_helpers.h" | ||
| 16 | |||
| 17 | #if __cplusplus > 199711L || _MSC_VER >= 1700 // C++11 or VS2012 | ||
| 18 | #include <chrono> | ||
| 19 | #endif | ||
| 20 | |||
| 21 | // A lock-free queue for a single-consumer, single-producer architecture. | ||
| 22 | // The queue is also wait-free in the common path (except if more memory | ||
| 23 | // needs to be allocated, in which case malloc is called). | ||
| 24 | // Allocates memory sparingly, and only once if the original maximum size | ||
| 25 | // estimate is never exceeded. | ||
| 26 | // Tested on x86/x64 processors, but semantics should be correct for all | ||
| 27 | // architectures (given the right implementations in atomicops.h), provided | ||
| 28 | // that aligned integer and pointer accesses are naturally atomic. | ||
| 29 | // Note that there should only be one consumer thread and producer thread; | ||
| 30 | // Switching roles of the threads, or using multiple consecutive threads for | ||
| 31 | // one role, is not safe unless properly synchronized. | ||
| 32 | // Using the queue exclusively from one thread is fine, though a bit silly. | ||
| 33 | |||
| 34 | #ifndef MOODYCAMEL_CACHE_LINE_SIZE | ||
| 35 | #define MOODYCAMEL_CACHE_LINE_SIZE 64 | ||
| 36 | #endif | ||
| 37 | |||
| 38 | #ifndef MOODYCAMEL_EXCEPTIONS_ENABLED | ||
| 39 | #if (defined(_MSC_VER) && defined(_CPPUNWIND)) || (defined(__GNUC__) && defined(__EXCEPTIONS)) || \ | ||
| 40 | (!defined(_MSC_VER) && !defined(__GNUC__)) | ||
| 41 | #define MOODYCAMEL_EXCEPTIONS_ENABLED | ||
| 42 | #endif | ||
| 43 | #endif | ||
| 44 | |||
| 45 | #ifndef MOODYCAMEL_HAS_EMPLACE | ||
| 46 | #if !defined(_MSC_VER) || \ | ||
| 47 | _MSC_VER >= 1800 // variadic templates: either a non-MS compiler or VS >= 2013 | ||
| 48 | #define MOODYCAMEL_HAS_EMPLACE 1 | ||
| 49 | #endif | ||
| 50 | #endif | ||
| 51 | |||
| 52 | #ifndef MOODYCAMEL_MAYBE_ALIGN_TO_CACHELINE | ||
| 53 | #if defined(__APPLE__) && defined(__MACH__) && __cplusplus >= 201703L | ||
| 54 | // This is required to find out what deployment target we are using | ||
| 55 | #include <CoreFoundation/CoreFoundation.h> | ||
| 56 | #if !defined(MAC_OS_X_VERSION_MIN_REQUIRED) || \ | ||
| 57 | MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_14 | ||
| 58 | // C++17 new(size_t, align_val_t) is not backwards-compatible with older versions of macOS, so we | ||
| 59 | // can't support over-alignment in this case | ||
| 60 | #define MOODYCAMEL_MAYBE_ALIGN_TO_CACHELINE | ||
| 61 | #endif | ||
| 62 | #endif | ||
| 63 | #endif | ||
| 64 | |||
| 65 | #ifndef MOODYCAMEL_MAYBE_ALIGN_TO_CACHELINE | ||
| 66 | #define MOODYCAMEL_MAYBE_ALIGN_TO_CACHELINE AE_ALIGN(MOODYCAMEL_CACHE_LINE_SIZE) | ||
| 67 | #endif | ||
| 68 | |||
| 69 | #ifdef AE_VCPP | ||
| 70 | #pragma warning(push) | ||
| 71 | #pragma warning(disable : 4324) // structure was padded due to __declspec(align()) | ||
| 72 | #pragma warning(disable : 4820) // padding was added | ||
| 73 | #pragma warning(disable : 4127) // conditional expression is constant | ||
| 74 | #endif | ||
| 75 | |||
| 76 | namespace Common { | ||
| 77 | |||
| 78 | template <typename T, size_t MAX_BLOCK_SIZE = 512> | ||
| 79 | class MOODYCAMEL_MAYBE_ALIGN_TO_CACHELINE ReaderWriterQueue { | ||
| 80 | // Design: Based on a queue-of-queues. The low-level queues are just | ||
| 81 | // circular buffers with front and tail indices indicating where the | ||
| 82 | // next element to dequeue is and where the next element can be enqueued, | ||
| 83 | // respectively. Each low-level queue is called a "block". Each block | ||
| 84 | // wastes exactly one element's worth of space to keep the design simple | ||
| 85 | // (if front == tail then the queue is empty, and can't be full). | ||
| 86 | // The high-level queue is a circular linked list of blocks; again there | ||
| 87 | // is a front and tail, but this time they are pointers to the blocks. | ||
| 88 | // The front block is where the next element to be dequeued is, provided | ||
| 89 | // the block is not empty. The back block is where elements are to be | ||
| 90 | // enqueued, provided the block is not full. | ||
| 91 | // The producer thread owns all the tail indices/pointers. The consumer | ||
| 92 | // thread owns all the front indices/pointers. Both threads read each | ||
| 93 | // other's variables, but only the owning thread updates them. E.g. After | ||
| 94 | // the consumer reads the producer's tail, the tail may change before the | ||
| 95 | // consumer is done dequeuing an object, but the consumer knows the tail | ||
| 96 | // will never go backwards, only forwards. | ||
| 97 | // If there is no room to enqueue an object, an additional block (of | ||
| 98 | // equal size to the last block) is added. Blocks are never removed. | ||
| 99 | |||
| 100 | public: | ||
| 101 | typedef T value_type; | ||
| 102 | |||
| 103 | // Constructs a queue that can hold at least `size` elements without further | ||
| 104 | // allocations. If more than MAX_BLOCK_SIZE elements are requested, | ||
| 105 | // then several blocks of MAX_BLOCK_SIZE each are reserved (including | ||
| 106 | // at least one extra buffer block). | ||
| 107 | AE_NO_TSAN explicit ReaderWriterQueue(size_t size = 15) | ||
| 108 | #ifndef NDEBUG | ||
| 109 | : enqueuing(false), dequeuing(false) | ||
| 110 | #endif | ||
| 111 | { | ||
| 112 | assert(MAX_BLOCK_SIZE == ceilToPow2(MAX_BLOCK_SIZE) && | ||
| 113 | "MAX_BLOCK_SIZE must be a power of 2"); | ||
| 114 | assert(MAX_BLOCK_SIZE >= 2 && "MAX_BLOCK_SIZE must be at least 2"); | ||
| 115 | |||
| 116 | Block* firstBlock = nullptr; | ||
| 117 | |||
| 118 | largestBlockSize = | ||
| 119 | ceilToPow2(size + 1); // We need a spare slot to fit size elements in the block | ||
| 120 | if (largestBlockSize > MAX_BLOCK_SIZE * 2) { | ||
| 121 | // We need a spare block in case the producer is writing to a different block the | ||
| 122 | // consumer is reading from, and wants to enqueue the maximum number of elements. We | ||
| 123 | // also need a spare element in each block to avoid the ambiguity between front == tail | ||
| 124 | // meaning "empty" and "full". So the effective number of slots that are guaranteed to | ||
| 125 | // be usable at any time is the block size - 1 times the number of blocks - 1. Solving | ||
| 126 | // for size and applying a ceiling to the division gives us (after simplifying): | ||
| 127 | size_t initialBlockCount = (size + MAX_BLOCK_SIZE * 2 - 3) / (MAX_BLOCK_SIZE - 1); | ||
| 128 | largestBlockSize = MAX_BLOCK_SIZE; | ||
| 129 | Block* lastBlock = nullptr; | ||
| 130 | for (size_t i = 0; i != initialBlockCount; ++i) { | ||
| 131 | auto block = make_block(largestBlockSize); | ||
| 132 | if (block == nullptr) { | ||
| 133 | #ifdef MOODYCAMEL_EXCEPTIONS_ENABLED | ||
| 134 | throw std::bad_alloc(); | ||
| 135 | #else | ||
| 136 | abort(); | ||
| 137 | #endif | ||
| 138 | } | ||
| 139 | if (firstBlock == nullptr) { | ||
| 140 | firstBlock = block; | ||
| 141 | } else { | ||
| 142 | lastBlock->next = block; | ||
| 143 | } | ||
| 144 | lastBlock = block; | ||
| 145 | block->next = firstBlock; | ||
| 146 | } | ||
| 147 | } else { | ||
| 148 | firstBlock = make_block(largestBlockSize); | ||
| 149 | if (firstBlock == nullptr) { | ||
| 150 | #ifdef MOODYCAMEL_EXCEPTIONS_ENABLED | ||
| 151 | throw std::bad_alloc(); | ||
| 152 | #else | ||
| 153 | abort(); | ||
| 154 | #endif | ||
| 155 | } | ||
| 156 | firstBlock->next = firstBlock; | ||
| 157 | } | ||
| 158 | frontBlock = firstBlock; | ||
| 159 | tailBlock = firstBlock; | ||
| 160 | |||
| 161 | // Make sure the reader/writer threads will have the initialized memory setup above: | ||
| 162 | fence(memory_order_sync); | ||
| 163 | } | ||
| 164 | |||
| 165 | // Note: The queue should not be accessed concurrently while it's | ||
| 166 | // being moved. It's up to the user to synchronize this. | ||
| 167 | AE_NO_TSAN ReaderWriterQueue(ReaderWriterQueue&& other) | ||
| 168 | : frontBlock(other.frontBlock.load()), tailBlock(other.tailBlock.load()), | ||
| 169 | largestBlockSize(other.largestBlockSize) | ||
| 170 | #ifndef NDEBUG | ||
| 171 | , | ||
| 172 | enqueuing(false), dequeuing(false) | ||
| 173 | #endif | ||
| 174 | { | ||
| 175 | other.largestBlockSize = 32; | ||
| 176 | Block* b = other.make_block(other.largestBlockSize); | ||
| 177 | if (b == nullptr) { | ||
| 178 | #ifdef MOODYCAMEL_EXCEPTIONS_ENABLED | ||
| 179 | throw std::bad_alloc(); | ||
| 180 | #else | ||
| 181 | abort(); | ||
| 182 | #endif | ||
| 183 | } | ||
| 184 | b->next = b; | ||
| 185 | other.frontBlock = b; | ||
| 186 | other.tailBlock = b; | ||
| 187 | } | ||
| 188 | |||
| 189 | // Note: The queue should not be accessed concurrently while it's | ||
| 190 | // being moved. It's up to the user to synchronize this. | ||
| 191 | ReaderWriterQueue& operator=(ReaderWriterQueue&& other) AE_NO_TSAN { | ||
| 192 | Block* b = frontBlock.load(); | ||
| 193 | frontBlock = other.frontBlock.load(); | ||
| 194 | other.frontBlock = b; | ||
| 195 | b = tailBlock.load(); | ||
| 196 | tailBlock = other.tailBlock.load(); | ||
| 197 | other.tailBlock = b; | ||
| 198 | std::swap(largestBlockSize, other.largestBlockSize); | ||
| 199 | return *this; | ||
| 200 | } | ||
| 201 | |||
| 202 | // Note: The queue should not be accessed concurrently while it's | ||
| 203 | // being deleted. It's up to the user to synchronize this. | ||
| 204 | AE_NO_TSAN ~ReaderWriterQueue() { | ||
| 205 | // Make sure we get the latest version of all variables from other CPUs: | ||
| 206 | fence(memory_order_sync); | ||
| 207 | |||
| 208 | // Destroy any remaining objects in queue and free memory | ||
| 209 | Block* frontBlock_ = frontBlock; | ||
| 210 | Block* block = frontBlock_; | ||
| 211 | do { | ||
| 212 | Block* nextBlock = block->next; | ||
| 213 | size_t blockFront = block->front; | ||
| 214 | size_t blockTail = block->tail; | ||
| 215 | |||
| 216 | for (size_t i = blockFront; i != blockTail; i = (i + 1) & block->sizeMask) { | ||
| 217 | auto element = reinterpret_cast<T*>(block->data + i * sizeof(T)); | ||
| 218 | element->~T(); | ||
| 219 | (void)element; | ||
| 220 | } | ||
| 221 | |||
| 222 | auto rawBlock = block->rawThis; | ||
| 223 | block->~Block(); | ||
| 224 | std::free(rawBlock); | ||
| 225 | block = nextBlock; | ||
| 226 | } while (block != frontBlock_); | ||
| 227 | } | ||
| 228 | |||
| 229 | // Enqueues a copy of element if there is room in the queue. | ||
| 230 | // Returns true if the element was enqueued, false otherwise. | ||
| 231 | // Does not allocate memory. | ||
| 232 | AE_FORCEINLINE bool try_enqueue(T const& element) AE_NO_TSAN { | ||
| 233 | return inner_enqueue<CannotAlloc>(element); | ||
| 234 | } | ||
| 235 | |||
| 236 | // Enqueues a moved copy of element if there is room in the queue. | ||
| 237 | // Returns true if the element was enqueued, false otherwise. | ||
| 238 | // Does not allocate memory. | ||
| 239 | AE_FORCEINLINE bool try_enqueue(T&& element) AE_NO_TSAN { | ||
| 240 | return inner_enqueue<CannotAlloc>(std::forward<T>(element)); | ||
| 241 | } | ||
| 242 | |||
| 243 | #if MOODYCAMEL_HAS_EMPLACE | ||
| 244 | // Like try_enqueue() but with emplace semantics (i.e. construct-in-place). | ||
| 245 | template <typename... Args> | ||
| 246 | AE_FORCEINLINE bool try_emplace(Args&&... args) AE_NO_TSAN { | ||
| 247 | return inner_enqueue<CannotAlloc>(std::forward<Args>(args)...); | ||
| 248 | } | ||
| 249 | #endif | ||
| 250 | |||
| 251 | // Enqueues a copy of element on the queue. | ||
| 252 | // Allocates an additional block of memory if needed. | ||
| 253 | // Only fails (returns false) if memory allocation fails. | ||
| 254 | AE_FORCEINLINE bool enqueue(T const& element) AE_NO_TSAN { | ||
| 255 | return inner_enqueue<CanAlloc>(element); | ||
| 256 | } | ||
| 257 | |||
| 258 | // Enqueues a moved copy of element on the queue. | ||
| 259 | // Allocates an additional block of memory if needed. | ||
| 260 | // Only fails (returns false) if memory allocation fails. | ||
| 261 | AE_FORCEINLINE bool enqueue(T&& element) AE_NO_TSAN { | ||
| 262 | return inner_enqueue<CanAlloc>(std::forward<T>(element)); | ||
| 263 | } | ||
| 264 | |||
| 265 | #if MOODYCAMEL_HAS_EMPLACE | ||
| 266 | // Like enqueue() but with emplace semantics (i.e. construct-in-place). | ||
| 267 | template <typename... Args> | ||
| 268 | AE_FORCEINLINE bool emplace(Args&&... args) AE_NO_TSAN { | ||
| 269 | return inner_enqueue<CanAlloc>(std::forward<Args>(args)...); | ||
| 270 | } | ||
| 271 | #endif | ||
| 272 | |||
| 273 | // Attempts to dequeue an element; if the queue is empty, | ||
| 274 | // returns false instead. If the queue has at least one element, | ||
| 275 | // moves front to result using operator=, then returns true. | ||
| 276 | template <typename U> | ||
| 277 | bool try_dequeue(U& result) AE_NO_TSAN { | ||
| 278 | #ifndef NDEBUG | ||
| 279 | ReentrantGuard guard(this->dequeuing); | ||
| 280 | #endif | ||
| 281 | |||
| 282 | // High-level pseudocode: | ||
| 283 | // Remember where the tail block is | ||
| 284 | // If the front block has an element in it, dequeue it | ||
| 285 | // Else | ||
| 286 | // If front block was the tail block when we entered the function, return false | ||
| 287 | // Else advance to next block and dequeue the item there | ||
| 288 | |||
| 289 | // Note that we have to use the value of the tail block from before we check if the front | ||
| 290 | // block is full or not, in case the front block is empty and then, before we check if the | ||
| 291 | // tail block is at the front block or not, the producer fills up the front block *and | ||
| 292 | // moves on*, which would make us skip a filled block. Seems unlikely, but was consistently | ||
| 293 | // reproducible in practice. | ||
| 294 | // In order to avoid overhead in the common case, though, we do a double-checked pattern | ||
| 295 | // where we have the fast path if the front block is not empty, then read the tail block, | ||
| 296 | // then re-read the front block and check if it's not empty again, then check if the tail | ||
| 297 | // block has advanced. | ||
| 298 | |||
| 299 | Block* frontBlock_ = frontBlock.load(); | ||
| 300 | size_t blockTail = frontBlock_->localTail; | ||
| 301 | size_t blockFront = frontBlock_->front.load(); | ||
| 302 | |||
| 303 | if (blockFront != blockTail || | ||
| 304 | blockFront != (frontBlock_->localTail = frontBlock_->tail.load())) { | ||
| 305 | fence(memory_order_acquire); | ||
| 306 | |||
| 307 | non_empty_front_block: | ||
| 308 | // Front block not empty, dequeue from here | ||
| 309 | auto element = reinterpret_cast<T*>(frontBlock_->data + blockFront * sizeof(T)); | ||
| 310 | result = std::move(*element); | ||
| 311 | element->~T(); | ||
| 312 | |||
| 313 | blockFront = (blockFront + 1) & frontBlock_->sizeMask; | ||
| 314 | |||
| 315 | fence(memory_order_release); | ||
| 316 | frontBlock_->front = blockFront; | ||
| 317 | } else if (frontBlock_ != tailBlock.load()) { | ||
| 318 | fence(memory_order_acquire); | ||
| 319 | |||
| 320 | frontBlock_ = frontBlock.load(); | ||
| 321 | blockTail = frontBlock_->localTail = frontBlock_->tail.load(); | ||
| 322 | blockFront = frontBlock_->front.load(); | ||
| 323 | fence(memory_order_acquire); | ||
| 324 | |||
| 325 | if (blockFront != blockTail) { | ||
| 326 | // Oh look, the front block isn't empty after all | ||
| 327 | goto non_empty_front_block; | ||
| 328 | } | ||
| 329 | |||
| 330 | // Front block is empty but there's another block ahead, advance to it | ||
| 331 | Block* nextBlock = frontBlock_->next; | ||
| 332 | // Don't need an acquire fence here since next can only ever be set on the tailBlock, | ||
| 333 | // and we're not the tailBlock, and we did an acquire earlier after reading tailBlock | ||
| 334 | // which ensures next is up-to-date on this CPU in case we recently were at tailBlock. | ||
| 335 | |||
| 336 | size_t nextBlockFront = nextBlock->front.load(); | ||
| 337 | size_t nextBlockTail = nextBlock->localTail = nextBlock->tail.load(); | ||
| 338 | fence(memory_order_acquire); | ||
| 339 | |||
| 340 | // Since the tailBlock is only ever advanced after being written to, | ||
| 341 | // we know there's for sure an element to dequeue on it | ||
| 342 | assert(nextBlockFront != nextBlockTail); | ||
| 343 | AE_UNUSED(nextBlockTail); | ||
| 344 | |||
| 345 | // We're done with this block, let the producer use it if it needs | ||
| 346 | fence(memory_order_release); // Expose possibly pending changes to frontBlock->front | ||
| 347 | // from last dequeue | ||
| 348 | frontBlock = frontBlock_ = nextBlock; | ||
| 349 | |||
| 350 | compiler_fence(memory_order_release); // Not strictly needed | ||
| 351 | |||
| 352 | auto element = reinterpret_cast<T*>(frontBlock_->data + nextBlockFront * sizeof(T)); | ||
| 353 | |||
| 354 | result = std::move(*element); | ||
| 355 | element->~T(); | ||
| 356 | |||
| 357 | nextBlockFront = (nextBlockFront + 1) & frontBlock_->sizeMask; | ||
| 358 | |||
| 359 | fence(memory_order_release); | ||
| 360 | frontBlock_->front = nextBlockFront; | ||
| 361 | } else { | ||
| 362 | // No elements in current block and no other block to advance to | ||
| 363 | return false; | ||
| 364 | } | ||
| 365 | |||
| 366 | return true; | ||
| 367 | } | ||
| 368 | |||
| 369 | // Returns a pointer to the front element in the queue (the one that | ||
| 370 | // would be removed next by a call to `try_dequeue` or `pop`). If the | ||
| 371 | // queue appears empty at the time the method is called, nullptr is | ||
| 372 | // returned instead. | ||
| 373 | // Must be called only from the consumer thread. | ||
| 374 | T* peek() const AE_NO_TSAN { | ||
| 375 | #ifndef NDEBUG | ||
| 376 | ReentrantGuard guard(this->dequeuing); | ||
| 377 | #endif | ||
| 378 | // See try_dequeue() for reasoning | ||
| 379 | |||
| 380 | Block* frontBlock_ = frontBlock.load(); | ||
| 381 | size_t blockTail = frontBlock_->localTail; | ||
| 382 | size_t blockFront = frontBlock_->front.load(); | ||
| 383 | |||
| 384 | if (blockFront != blockTail || | ||
| 385 | blockFront != (frontBlock_->localTail = frontBlock_->tail.load())) { | ||
| 386 | fence(memory_order_acquire); | ||
| 387 | non_empty_front_block: | ||
| 388 | return reinterpret_cast<T*>(frontBlock_->data + blockFront * sizeof(T)); | ||
| 389 | } else if (frontBlock_ != tailBlock.load()) { | ||
| 390 | fence(memory_order_acquire); | ||
| 391 | frontBlock_ = frontBlock.load(); | ||
| 392 | blockTail = frontBlock_->localTail = frontBlock_->tail.load(); | ||
| 393 | blockFront = frontBlock_->front.load(); | ||
| 394 | fence(memory_order_acquire); | ||
| 395 | |||
| 396 | if (blockFront != blockTail) { | ||
| 397 | goto non_empty_front_block; | ||
| 398 | } | ||
| 399 | |||
| 400 | Block* nextBlock = frontBlock_->next; | ||
| 401 | |||
| 402 | size_t nextBlockFront = nextBlock->front.load(); | ||
| 403 | fence(memory_order_acquire); | ||
| 404 | |||
| 405 | assert(nextBlockFront != nextBlock->tail.load()); | ||
| 406 | return reinterpret_cast<T*>(nextBlock->data + nextBlockFront * sizeof(T)); | ||
| 407 | } | ||
| 408 | |||
| 409 | return nullptr; | ||
| 410 | } | ||
| 411 | |||
| 412 | // Removes the front element from the queue, if any, without returning it. | ||
| 413 | // Returns true on success, or false if the queue appeared empty at the time | ||
| 414 | // `pop` was called. | ||
| 415 | bool pop() AE_NO_TSAN { | ||
| 416 | #ifndef NDEBUG | ||
| 417 | ReentrantGuard guard(this->dequeuing); | ||
| 418 | #endif | ||
| 419 | // See try_dequeue() for reasoning | ||
| 420 | |||
| 421 | Block* frontBlock_ = frontBlock.load(); | ||
| 422 | size_t blockTail = frontBlock_->localTail; | ||
| 423 | size_t blockFront = frontBlock_->front.load(); | ||
| 424 | |||
| 425 | if (blockFront != blockTail || | ||
| 426 | blockFront != (frontBlock_->localTail = frontBlock_->tail.load())) { | ||
| 427 | fence(memory_order_acquire); | ||
| 428 | |||
| 429 | non_empty_front_block: | ||
| 430 | auto element = reinterpret_cast<T*>(frontBlock_->data + blockFront * sizeof(T)); | ||
| 431 | element->~T(); | ||
| 432 | |||
| 433 | blockFront = (blockFront + 1) & frontBlock_->sizeMask; | ||
| 434 | |||
| 435 | fence(memory_order_release); | ||
| 436 | frontBlock_->front = blockFront; | ||
| 437 | } else if (frontBlock_ != tailBlock.load()) { | ||
| 438 | fence(memory_order_acquire); | ||
| 439 | frontBlock_ = frontBlock.load(); | ||
| 440 | blockTail = frontBlock_->localTail = frontBlock_->tail.load(); | ||
| 441 | blockFront = frontBlock_->front.load(); | ||
| 442 | fence(memory_order_acquire); | ||
| 443 | |||
| 444 | if (blockFront != blockTail) { | ||
| 445 | goto non_empty_front_block; | ||
| 446 | } | ||
| 447 | |||
| 448 | // Front block is empty but there's another block ahead, advance to it | ||
| 449 | Block* nextBlock = frontBlock_->next; | ||
| 450 | |||
| 451 | size_t nextBlockFront = nextBlock->front.load(); | ||
| 452 | size_t nextBlockTail = nextBlock->localTail = nextBlock->tail.load(); | ||
| 453 | fence(memory_order_acquire); | ||
| 454 | |||
| 455 | assert(nextBlockFront != nextBlockTail); | ||
| 456 | AE_UNUSED(nextBlockTail); | ||
| 457 | |||
| 458 | fence(memory_order_release); | ||
| 459 | frontBlock = frontBlock_ = nextBlock; | ||
| 460 | |||
| 461 | compiler_fence(memory_order_release); | ||
| 462 | |||
| 463 | auto element = reinterpret_cast<T*>(frontBlock_->data + nextBlockFront * sizeof(T)); | ||
| 464 | element->~T(); | ||
| 465 | |||
| 466 | nextBlockFront = (nextBlockFront + 1) & frontBlock_->sizeMask; | ||
| 467 | |||
| 468 | fence(memory_order_release); | ||
| 469 | frontBlock_->front = nextBlockFront; | ||
| 470 | } else { | ||
| 471 | // No elements in current block and no other block to advance to | ||
| 472 | return false; | ||
| 473 | } | ||
| 474 | |||
| 475 | return true; | ||
| 476 | } | ||
| 477 | |||
| 478 | // Returns the approximate number of items currently in the queue. | ||
| 479 | // Safe to call from both the producer and consumer threads. | ||
| 480 | inline size_t size_approx() const AE_NO_TSAN { | ||
| 481 | size_t result = 0; | ||
| 482 | Block* frontBlock_ = frontBlock.load(); | ||
| 483 | Block* block = frontBlock_; | ||
| 484 | do { | ||
| 485 | fence(memory_order_acquire); | ||
| 486 | size_t blockFront = block->front.load(); | ||
| 487 | size_t blockTail = block->tail.load(); | ||
| 488 | result += (blockTail - blockFront) & block->sizeMask; | ||
| 489 | block = block->next.load(); | ||
| 490 | } while (block != frontBlock_); | ||
| 491 | return result; | ||
| 492 | } | ||
| 493 | |||
| 494 | // Returns the total number of items that could be enqueued without incurring | ||
| 495 | // an allocation when this queue is empty. | ||
| 496 | // Safe to call from both the producer and consumer threads. | ||
| 497 | // | ||
| 498 | // NOTE: The actual capacity during usage may be different depending on the consumer. | ||
| 499 | // If the consumer is removing elements concurrently, the producer cannot add to | ||
| 500 | // the block the consumer is removing from until it's completely empty, except in | ||
| 501 | // the case where the producer was writing to the same block the consumer was | ||
| 502 | // reading from the whole time. | ||
| 503 | inline size_t max_capacity() const { | ||
| 504 | size_t result = 0; | ||
| 505 | Block* frontBlock_ = frontBlock.load(); | ||
| 506 | Block* block = frontBlock_; | ||
| 507 | do { | ||
| 508 | fence(memory_order_acquire); | ||
| 509 | result += block->sizeMask; | ||
| 510 | block = block->next.load(); | ||
| 511 | } while (block != frontBlock_); | ||
| 512 | return result; | ||
| 513 | } | ||
| 514 | |||
| 515 | private: | ||
| 516 | enum AllocationMode { CanAlloc, CannotAlloc }; | ||
| 517 | |||
| 518 | #if MOODYCAMEL_HAS_EMPLACE | ||
| 519 | template <AllocationMode canAlloc, typename... Args> | ||
| 520 | bool inner_enqueue(Args&&... args) AE_NO_TSAN | ||
| 521 | #else | ||
| 522 | template <AllocationMode canAlloc, typename U> | ||
| 523 | bool inner_enqueue(U&& element) AE_NO_TSAN | ||
| 524 | #endif | ||
| 525 | { | ||
| 526 | #ifndef NDEBUG | ||
| 527 | ReentrantGuard guard(this->enqueuing); | ||
| 528 | #endif | ||
| 529 | |||
| 530 | // High-level pseudocode (assuming we're allowed to alloc a new block): | ||
| 531 | // If room in tail block, add to tail | ||
| 532 | // Else check next block | ||
| 533 | // If next block is not the head block, enqueue on next block | ||
| 534 | // Else create a new block and enqueue there | ||
| 535 | // Advance tail to the block we just enqueued to | ||
| 536 | |||
| 537 | Block* tailBlock_ = tailBlock.load(); | ||
| 538 | size_t blockFront = tailBlock_->localFront; | ||
| 539 | size_t blockTail = tailBlock_->tail.load(); | ||
| 540 | |||
| 541 | size_t nextBlockTail = (blockTail + 1) & tailBlock_->sizeMask; | ||
| 542 | if (nextBlockTail != blockFront || | ||
| 543 | nextBlockTail != (tailBlock_->localFront = tailBlock_->front.load())) { | ||
| 544 | fence(memory_order_acquire); | ||
| 545 | // This block has room for at least one more element | ||
| 546 | char* location = tailBlock_->data + blockTail * sizeof(T); | ||
| 547 | #if MOODYCAMEL_HAS_EMPLACE | ||
| 548 | new (location) T(std::forward<Args>(args)...); | ||
| 549 | #else | ||
| 550 | new (location) T(std::forward<U>(element)); | ||
| 551 | #endif | ||
| 552 | |||
| 553 | fence(memory_order_release); | ||
| 554 | tailBlock_->tail = nextBlockTail; | ||
| 555 | } else { | ||
| 556 | fence(memory_order_acquire); | ||
| 557 | if (tailBlock_->next.load() != frontBlock) { | ||
| 558 | // Note that the reason we can't advance to the frontBlock and start adding new | ||
| 559 | // entries there is because if we did, then dequeue would stay in that block, | ||
| 560 | // eventually reading the new values, instead of advancing to the next full block | ||
| 561 | // (whose values were enqueued first and so should be consumed first). | ||
| 562 | |||
| 563 | fence(memory_order_acquire); // Ensure we get latest writes if we got the latest | ||
| 564 | // frontBlock | ||
| 565 | |||
| 566 | // tailBlock is full, but there's a free block ahead, use it | ||
| 567 | Block* tailBlockNext = tailBlock_->next.load(); | ||
| 568 | size_t nextBlockFront = tailBlockNext->localFront = tailBlockNext->front.load(); | ||
| 569 | nextBlockTail = tailBlockNext->tail.load(); | ||
| 570 | fence(memory_order_acquire); | ||
| 571 | |||
| 572 | // This block must be empty since it's not the head block and we | ||
| 573 | // go through the blocks in a circle | ||
| 574 | assert(nextBlockFront == nextBlockTail); | ||
| 575 | tailBlockNext->localFront = nextBlockFront; | ||
| 576 | |||
| 577 | char* location = tailBlockNext->data + nextBlockTail * sizeof(T); | ||
| 578 | #if MOODYCAMEL_HAS_EMPLACE | ||
| 579 | new (location) T(std::forward<Args>(args)...); | ||
| 580 | #else | ||
| 581 | new (location) T(std::forward<U>(element)); | ||
| 582 | #endif | ||
| 583 | |||
| 584 | tailBlockNext->tail = (nextBlockTail + 1) & tailBlockNext->sizeMask; | ||
| 585 | |||
| 586 | fence(memory_order_release); | ||
| 587 | tailBlock = tailBlockNext; | ||
| 588 | } else if (canAlloc == CanAlloc) { | ||
| 589 | // tailBlock is full and there's no free block ahead; create a new block | ||
| 590 | auto newBlockSize = | ||
| 591 | largestBlockSize >= MAX_BLOCK_SIZE ? largestBlockSize : largestBlockSize * 2; | ||
| 592 | auto newBlock = make_block(newBlockSize); | ||
| 593 | if (newBlock == nullptr) { | ||
| 594 | // Could not allocate a block! | ||
| 595 | return false; | ||
| 596 | } | ||
| 597 | largestBlockSize = newBlockSize; | ||
| 598 | |||
| 599 | #if MOODYCAMEL_HAS_EMPLACE | ||
| 600 | new (newBlock->data) T(std::forward<Args>(args)...); | ||
| 601 | #else | ||
| 602 | new (newBlock->data) T(std::forward<U>(element)); | ||
| 603 | #endif | ||
| 604 | assert(newBlock->front == 0); | ||
| 605 | newBlock->tail = newBlock->localTail = 1; | ||
| 606 | |||
| 607 | newBlock->next = tailBlock_->next.load(); | ||
| 608 | tailBlock_->next = newBlock; | ||
| 609 | |||
| 610 | // Might be possible for the dequeue thread to see the new tailBlock->next | ||
| 611 | // *without* seeing the new tailBlock value, but this is OK since it can't | ||
| 612 | // advance to the next block until tailBlock is set anyway (because the only | ||
| 613 | // case where it could try to read the next is if it's already at the tailBlock, | ||
| 614 | // and it won't advance past tailBlock in any circumstance). | ||
| 615 | |||
| 616 | fence(memory_order_release); | ||
| 617 | tailBlock = newBlock; | ||
| 618 | } else if (canAlloc == CannotAlloc) { | ||
| 619 | // Would have had to allocate a new block to enqueue, but not allowed | ||
| 620 | return false; | ||
| 621 | } else { | ||
| 622 | assert(false && "Should be unreachable code"); | ||
| 623 | return false; | ||
| 624 | } | ||
| 625 | } | ||
| 626 | |||
| 627 | return true; | ||
| 628 | } | ||
| 629 | |||
| 630 | // Disable copying | ||
| 631 | ReaderWriterQueue(ReaderWriterQueue const&) {} | ||
| 632 | |||
| 633 | // Disable assignment | ||
| 634 | ReaderWriterQueue& operator=(ReaderWriterQueue const&) {} | ||
| 635 | |||
| 636 | AE_FORCEINLINE static size_t ceilToPow2(size_t x) { | ||
| 637 | // From http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 | ||
| 638 | --x; | ||
| 639 | x |= x >> 1; | ||
| 640 | x |= x >> 2; | ||
| 641 | x |= x >> 4; | ||
| 642 | for (size_t i = 1; i < sizeof(size_t); i <<= 1) { | ||
| 643 | x |= x >> (i << 3); | ||
| 644 | } | ||
| 645 | ++x; | ||
| 646 | return x; | ||
| 647 | } | ||
| 648 | |||
| 649 | template <typename U> | ||
| 650 | static AE_FORCEINLINE char* align_for(char* ptr) AE_NO_TSAN { | ||
| 651 | const std::size_t alignment = std::alignment_of<U>::value; | ||
| 652 | return ptr + (alignment - (reinterpret_cast<std::uintptr_t>(ptr) % alignment)) % alignment; | ||
| 653 | } | ||
| 654 | |||
| 655 | private: | ||
| 656 | #ifndef NDEBUG | ||
| 657 | struct ReentrantGuard { | ||
| 658 | AE_NO_TSAN ReentrantGuard(weak_atomic<bool>& _inSection) : inSection(_inSection) { | ||
| 659 | assert(!inSection && | ||
| 660 | "Concurrent (or re-entrant) enqueue or dequeue operation detected (only one " | ||
| 661 | "thread at a time may hold the producer or consumer role)"); | ||
| 662 | inSection = true; | ||
| 663 | } | ||
| 664 | |||
| 665 | AE_NO_TSAN ~ReentrantGuard() { | ||
| 666 | inSection = false; | ||
| 667 | } | ||
| 668 | |||
| 669 | private: | ||
| 670 | ReentrantGuard& operator=(ReentrantGuard const&); | ||
| 671 | |||
| 672 | private: | ||
| 673 | weak_atomic<bool>& inSection; | ||
| 674 | }; | ||
| 675 | #endif | ||
| 676 | |||
| 677 | struct Block { | ||
| 678 | // Avoid false-sharing by putting highly contended variables on their own cache lines | ||
| 679 | weak_atomic<size_t> front; // (Atomic) Elements are read from here | ||
| 680 | size_t localTail; // An uncontended shadow copy of tail, owned by the consumer | ||
| 681 | |||
| 682 | char cachelineFiller0[MOODYCAMEL_CACHE_LINE_SIZE - sizeof(weak_atomic<size_t>) - | ||
| 683 | sizeof(size_t)]; | ||
| 684 | weak_atomic<size_t> tail; // (Atomic) Elements are enqueued here | ||
| 685 | size_t localFront; | ||
| 686 | |||
| 687 | char cachelineFiller1[MOODYCAMEL_CACHE_LINE_SIZE - sizeof(weak_atomic<size_t>) - | ||
| 688 | sizeof(size_t)]; // next isn't very contended, but we don't want it on | ||
| 689 | // the same cache line as tail (which is) | ||
| 690 | weak_atomic<Block*> next; // (Atomic) | ||
| 691 | |||
| 692 | char* data; // Contents (on heap) are aligned to T's alignment | ||
| 693 | |||
| 694 | const size_t sizeMask; | ||
| 695 | |||
| 696 | // size must be a power of two (and greater than 0) | ||
| 697 | AE_NO_TSAN Block(size_t const& _size, char* _rawThis, char* _data) | ||
| 698 | : front(0UL), localTail(0), tail(0UL), localFront(0), next(nullptr), data(_data), | ||
| 699 | sizeMask(_size - 1), rawThis(_rawThis) {} | ||
| 700 | |||
| 701 | private: | ||
| 702 | // C4512 - Assignment operator could not be generated | ||
| 703 | Block& operator=(Block const&); | ||
| 704 | |||
| 705 | public: | ||
| 706 | char* rawThis; | ||
| 707 | }; | ||
| 708 | |||
| 709 | static Block* make_block(size_t capacity) AE_NO_TSAN { | ||
| 710 | // Allocate enough memory for the block itself, as well as all the elements it will contain | ||
| 711 | auto size = sizeof(Block) + std::alignment_of<Block>::value - 1; | ||
| 712 | size += sizeof(T) * capacity + std::alignment_of<T>::value - 1; | ||
| 713 | auto newBlockRaw = static_cast<char*>(std::malloc(size)); | ||
| 714 | if (newBlockRaw == nullptr) { | ||
| 715 | return nullptr; | ||
| 716 | } | ||
| 717 | |||
| 718 | auto newBlockAligned = align_for<Block>(newBlockRaw); | ||
| 719 | auto newBlockData = align_for<T>(newBlockAligned + sizeof(Block)); | ||
| 720 | return new (newBlockAligned) Block(capacity, newBlockRaw, newBlockData); | ||
| 721 | } | ||
| 722 | |||
| 723 | private: | ||
| 724 | weak_atomic<Block*> frontBlock; // (Atomic) Elements are dequeued from this block | ||
| 725 | |||
| 726 | char cachelineFiller[MOODYCAMEL_CACHE_LINE_SIZE - sizeof(weak_atomic<Block*>)]; | ||
| 727 | weak_atomic<Block*> tailBlock; // (Atomic) Elements are enqueued to this block | ||
| 728 | |||
| 729 | size_t largestBlockSize; | ||
| 730 | |||
| 731 | #ifndef NDEBUG | ||
| 732 | weak_atomic<bool> enqueuing; | ||
| 733 | mutable weak_atomic<bool> dequeuing; | ||
| 734 | #endif | ||
| 735 | }; | ||
| 736 | |||
| 737 | // Like ReaderWriterQueue, but also providees blocking operations | ||
| 738 | template <typename T, size_t MAX_BLOCK_SIZE = 512> | ||
| 739 | class BlockingReaderWriterQueue { | ||
| 740 | private: | ||
| 741 | typedef ::Common::ReaderWriterQueue<T, MAX_BLOCK_SIZE> ReaderWriterQueue; | ||
| 742 | |||
| 743 | public: | ||
| 744 | explicit BlockingReaderWriterQueue(size_t size = 15) AE_NO_TSAN | ||
| 745 | : inner(size), | ||
| 746 | sema(new spsc_sema::LightweightSemaphore()) {} | ||
| 747 | |||
| 748 | BlockingReaderWriterQueue(BlockingReaderWriterQueue&& other) AE_NO_TSAN | ||
| 749 | : inner(std::move(other.inner)), | ||
| 750 | sema(std::move(other.sema)) {} | ||
| 751 | |||
| 752 | BlockingReaderWriterQueue& operator=(BlockingReaderWriterQueue&& other) AE_NO_TSAN { | ||
| 753 | std::swap(sema, other.sema); | ||
| 754 | std::swap(inner, other.inner); | ||
| 755 | return *this; | ||
| 756 | } | ||
| 757 | |||
| 758 | // Enqueues a copy of element if there is room in the queue. | ||
| 759 | // Returns true if the element was enqueued, false otherwise. | ||
| 760 | // Does not allocate memory. | ||
| 761 | AE_FORCEINLINE bool try_enqueue(T const& element) AE_NO_TSAN { | ||
| 762 | if (inner.try_enqueue(element)) { | ||
| 763 | sema->signal(); | ||
| 764 | return true; | ||
| 765 | } | ||
| 766 | return false; | ||
| 767 | } | ||
| 768 | |||
| 769 | // Enqueues a moved copy of element if there is room in the queue. | ||
| 770 | // Returns true if the element was enqueued, false otherwise. | ||
| 771 | // Does not allocate memory. | ||
| 772 | AE_FORCEINLINE bool try_enqueue(T&& element) AE_NO_TSAN { | ||
| 773 | if (inner.try_enqueue(std::forward<T>(element))) { | ||
| 774 | sema->signal(); | ||
| 775 | return true; | ||
| 776 | } | ||
| 777 | return false; | ||
| 778 | } | ||
| 779 | |||
| 780 | #if MOODYCAMEL_HAS_EMPLACE | ||
| 781 | // Like try_enqueue() but with emplace semantics (i.e. construct-in-place). | ||
| 782 | template <typename... Args> | ||
| 783 | AE_FORCEINLINE bool try_emplace(Args&&... args) AE_NO_TSAN { | ||
| 784 | if (inner.try_emplace(std::forward<Args>(args)...)) { | ||
| 785 | sema->signal(); | ||
| 786 | return true; | ||
| 787 | } | ||
| 788 | return false; | ||
| 789 | } | ||
| 790 | #endif | ||
| 791 | |||
| 792 | // Enqueues a copy of element on the queue. | ||
| 793 | // Allocates an additional block of memory if needed. | ||
| 794 | // Only fails (returns false) if memory allocation fails. | ||
| 795 | AE_FORCEINLINE bool enqueue(T const& element) AE_NO_TSAN { | ||
| 796 | if (inner.enqueue(element)) { | ||
| 797 | sema->signal(); | ||
| 798 | return true; | ||
| 799 | } | ||
| 800 | return false; | ||
| 801 | } | ||
| 802 | |||
| 803 | // Enqueues a moved copy of element on the queue. | ||
| 804 | // Allocates an additional block of memory if needed. | ||
| 805 | // Only fails (returns false) if memory allocation fails. | ||
| 806 | AE_FORCEINLINE bool enqueue(T&& element) AE_NO_TSAN { | ||
| 807 | if (inner.enqueue(std::forward<T>(element))) { | ||
| 808 | sema->signal(); | ||
| 809 | return true; | ||
| 810 | } | ||
| 811 | return false; | ||
| 812 | } | ||
| 813 | |||
| 814 | #if MOODYCAMEL_HAS_EMPLACE | ||
| 815 | // Like enqueue() but with emplace semantics (i.e. construct-in-place). | ||
| 816 | template <typename... Args> | ||
| 817 | AE_FORCEINLINE bool emplace(Args&&... args) AE_NO_TSAN { | ||
| 818 | if (inner.emplace(std::forward<Args>(args)...)) { | ||
| 819 | sema->signal(); | ||
| 820 | return true; | ||
| 821 | } | ||
| 822 | return false; | ||
| 823 | } | ||
| 824 | #endif | ||
| 825 | |||
| 826 | // Attempts to dequeue an element; if the queue is empty, | ||
| 827 | // returns false instead. If the queue has at least one element, | ||
| 828 | // moves front to result using operator=, then returns true. | ||
| 829 | template <typename U> | ||
| 830 | bool try_dequeue(U& result) AE_NO_TSAN { | ||
| 831 | if (sema->tryWait()) { | ||
| 832 | bool success = inner.try_dequeue(result); | ||
| 833 | assert(success); | ||
| 834 | AE_UNUSED(success); | ||
| 835 | return true; | ||
| 836 | } | ||
| 837 | return false; | ||
| 838 | } | ||
| 839 | |||
| 840 | // Attempts to dequeue an element; if the queue is empty, | ||
| 841 | // waits until an element is available, then dequeues it. | ||
| 842 | template <typename U> | ||
| 843 | void wait_dequeue(U& result) AE_NO_TSAN { | ||
| 844 | while (!sema->wait()) | ||
| 845 | ; | ||
| 846 | bool success = inner.try_dequeue(result); | ||
| 847 | AE_UNUSED(result); | ||
| 848 | assert(success); | ||
| 849 | AE_UNUSED(success); | ||
| 850 | } | ||
| 851 | |||
| 852 | // Attempts to dequeue an element; if the queue is empty, | ||
| 853 | // waits until an element is available up to the specified timeout, | ||
| 854 | // then dequeues it and returns true, or returns false if the timeout | ||
| 855 | // expires before an element can be dequeued. | ||
| 856 | // Using a negative timeout indicates an indefinite timeout, | ||
| 857 | // and is thus functionally equivalent to calling wait_dequeue. | ||
| 858 | template <typename U> | ||
| 859 | bool wait_dequeue_timed(U& result, std::int64_t timeout_usecs) AE_NO_TSAN { | ||
| 860 | if (!sema->wait(timeout_usecs)) { | ||
| 861 | return false; | ||
| 862 | } | ||
| 863 | bool success = inner.try_dequeue(result); | ||
| 864 | AE_UNUSED(result); | ||
| 865 | assert(success); | ||
| 866 | AE_UNUSED(success); | ||
| 867 | return true; | ||
| 868 | } | ||
| 869 | |||
| 870 | #if __cplusplus > 199711L || _MSC_VER >= 1700 | ||
| 871 | // Attempts to dequeue an element; if the queue is empty, | ||
| 872 | // waits until an element is available up to the specified timeout, | ||
| 873 | // then dequeues it and returns true, or returns false if the timeout | ||
| 874 | // expires before an element can be dequeued. | ||
| 875 | // Using a negative timeout indicates an indefinite timeout, | ||
| 876 | // and is thus functionally equivalent to calling wait_dequeue. | ||
| 877 | template <typename U, typename Rep, typename Period> | ||
| 878 | inline bool wait_dequeue_timed(U& result, | ||
| 879 | std::chrono::duration<Rep, Period> const& timeout) AE_NO_TSAN { | ||
| 880 | return wait_dequeue_timed( | ||
| 881 | result, std::chrono::duration_cast<std::chrono::microseconds>(timeout).count()); | ||
| 882 | } | ||
| 883 | #endif | ||
| 884 | |||
| 885 | // Returns a pointer to the front element in the queue (the one that | ||
| 886 | // would be removed next by a call to `try_dequeue` or `pop`). If the | ||
| 887 | // queue appears empty at the time the method is called, nullptr is | ||
| 888 | // returned instead. | ||
| 889 | // Must be called only from the consumer thread. | ||
| 890 | AE_FORCEINLINE T* peek() const AE_NO_TSAN { | ||
| 891 | return inner.peek(); | ||
| 892 | } | ||
| 893 | |||
| 894 | // Removes the front element from the queue, if any, without returning it. | ||
| 895 | // Returns true on success, or false if the queue appeared empty at the time | ||
| 896 | // `pop` was called. | ||
| 897 | AE_FORCEINLINE bool pop() AE_NO_TSAN { | ||
| 898 | if (sema->tryWait()) { | ||
| 899 | bool result = inner.pop(); | ||
| 900 | assert(result); | ||
| 901 | AE_UNUSED(result); | ||
| 902 | return true; | ||
| 903 | } | ||
| 904 | return false; | ||
| 905 | } | ||
| 906 | |||
| 907 | // Returns the approximate number of items currently in the queue. | ||
| 908 | // Safe to call from both the producer and consumer threads. | ||
| 909 | AE_FORCEINLINE size_t size_approx() const AE_NO_TSAN { | ||
| 910 | return sema->availableApprox(); | ||
| 911 | } | ||
| 912 | |||
| 913 | // Returns the total number of items that could be enqueued without incurring | ||
| 914 | // an allocation when this queue is empty. | ||
| 915 | // Safe to call from both the producer and consumer threads. | ||
| 916 | // | ||
| 917 | // NOTE: The actual capacity during usage may be different depending on the consumer. | ||
| 918 | // If the consumer is removing elements concurrently, the producer cannot add to | ||
| 919 | // the block the consumer is removing from until it's completely empty, except in | ||
| 920 | // the case where the producer was writing to the same block the consumer was | ||
| 921 | // reading from the whole time. | ||
| 922 | AE_FORCEINLINE size_t max_capacity() const { | ||
| 923 | return inner.max_capacity(); | ||
| 924 | } | ||
| 925 | |||
| 926 | private: | ||
| 927 | // Disable copying & assignment | ||
| 928 | BlockingReaderWriterQueue(BlockingReaderWriterQueue const&) {} | ||
| 929 | BlockingReaderWriterQueue& operator=(BlockingReaderWriterQueue const&) {} | ||
| 930 | |||
| 931 | private: | ||
| 932 | ReaderWriterQueue inner; | ||
| 933 | std::unique_ptr<spsc_sema::LightweightSemaphore> sema; | ||
| 934 | }; | ||
| 935 | |||
| 936 | } // namespace Common | ||
| 937 | |||
| 938 | #ifdef AE_VCPP | ||
| 939 | #pragma warning(pop) | ||
| 940 | #endif | ||
diff --git a/src/common/ring_buffer.h b/src/common/ring_buffer.h index 4a8d09806..4c328ab44 100644 --- a/src/common/ring_buffer.h +++ b/src/common/ring_buffer.h | |||
| @@ -1,6 +1,5 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | 1 | // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | // Refer to the license.txt file included. | ||
| 4 | 3 | ||
| 5 | #pragma once | 4 | #pragma once |
| 6 | 5 | ||
| @@ -12,7 +11,6 @@ | |||
| 12 | #include <new> | 11 | #include <new> |
| 13 | #include <type_traits> | 12 | #include <type_traits> |
| 14 | #include <vector> | 13 | #include <vector> |
| 15 | #include "common/common_types.h" | ||
| 16 | 14 | ||
| 17 | namespace Common { | 15 | namespace Common { |
| 18 | 16 | ||
diff --git a/src/common/scm_rev.cpp.in b/src/common/scm_rev.cpp.in index cc88994c6..f0c124d69 100644 --- a/src/common/scm_rev.cpp.in +++ b/src/common/scm_rev.cpp.in | |||
| @@ -1,6 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // SPDX-FileCopyrightText: 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | // Refer to the license.txt file included. | ||
| 4 | 3 | ||
| 5 | #include "common/scm_rev.h" | 4 | #include "common/scm_rev.h" |
| 6 | 5 | ||
diff --git a/src/common/scm_rev.h b/src/common/scm_rev.h index 563015ec9..88404316a 100644 --- a/src/common/scm_rev.h +++ b/src/common/scm_rev.h | |||
| @@ -1,6 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // SPDX-FileCopyrightText: 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | // Refer to the license.txt file included. | ||
| 4 | 3 | ||
| 5 | #pragma once | 4 | #pragma once |
| 6 | 5 | ||
diff --git a/src/common/scope_exit.h b/src/common/scope_exit.h index 35dac3a8f..e9c789c88 100644 --- a/src/common/scope_exit.h +++ b/src/common/scope_exit.h | |||
| @@ -1,6 +1,5 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // SPDX-FileCopyrightText: 2014 Citra Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | // Refer to the license.txt file included. | ||
| 4 | 3 | ||
| 5 | #pragma once | 4 | #pragma once |
| 6 | 5 | ||
diff --git a/src/common/settings.cpp b/src/common/settings.cpp index 6964a8273..0a560ebb7 100644 --- a/src/common/settings.cpp +++ b/src/common/settings.cpp | |||
| @@ -1,6 +1,5 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | // Refer to the license.txt file included. | ||
| 4 | 3 | ||
| 5 | #include <string_view> | 4 | #include <string_view> |
| 6 | 5 | ||
| @@ -11,7 +10,7 @@ | |||
| 11 | 10 | ||
| 12 | namespace Settings { | 11 | namespace Settings { |
| 13 | 12 | ||
| 14 | Values values = {}; | 13 | Values values; |
| 15 | static bool configuring_global = true; | 14 | static bool configuring_global = true; |
| 16 | 15 | ||
| 17 | std::string GetTimeZoneString() { | 16 | std::string GetTimeZoneString() { |
| @@ -63,7 +62,8 @@ void LogSettings() { | |||
| 63 | log_setting("Renderer_UseAsynchronousShaders", values.use_asynchronous_shaders.GetValue()); | 62 | log_setting("Renderer_UseAsynchronousShaders", values.use_asynchronous_shaders.GetValue()); |
| 64 | log_setting("Renderer_AnisotropicFilteringLevel", values.max_anisotropy.GetValue()); | 63 | log_setting("Renderer_AnisotropicFilteringLevel", values.max_anisotropy.GetValue()); |
| 65 | log_setting("Audio_OutputEngine", values.sink_id.GetValue()); | 64 | log_setting("Audio_OutputEngine", values.sink_id.GetValue()); |
| 66 | log_setting("Audio_OutputDevice", values.audio_device_id.GetValue()); | 65 | log_setting("Audio_OutputDevice", values.audio_output_device_id.GetValue()); |
| 66 | log_setting("Audio_InputDevice", values.audio_input_device_id.GetValue()); | ||
| 67 | log_setting("DataStorage_UseVirtualSd", values.use_virtual_sd.GetValue()); | 67 | log_setting("DataStorage_UseVirtualSd", values.use_virtual_sd.GetValue()); |
| 68 | log_path("DataStorage_CacheDir", Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir)); | 68 | log_path("DataStorage_CacheDir", Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir)); |
| 69 | log_path("DataStorage_ConfigDir", Common::FS::GetYuzuPath(Common::FS::YuzuPath::ConfigDir)); | 69 | log_path("DataStorage_ConfigDir", Common::FS::GetYuzuPath(Common::FS::YuzuPath::ConfigDir)); |
| @@ -71,6 +71,7 @@ void LogSettings() { | |||
| 71 | log_path("DataStorage_NANDDir", Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir)); | 71 | log_path("DataStorage_NANDDir", Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir)); |
| 72 | log_path("DataStorage_SDMCDir", Common::FS::GetYuzuPath(Common::FS::YuzuPath::SDMCDir)); | 72 | log_path("DataStorage_SDMCDir", Common::FS::GetYuzuPath(Common::FS::YuzuPath::SDMCDir)); |
| 73 | log_setting("Debugging_ProgramArgs", values.program_args.GetValue()); | 73 | log_setting("Debugging_ProgramArgs", values.program_args.GetValue()); |
| 74 | log_setting("Debugging_GDBStub", values.use_gdbstub.GetValue()); | ||
| 74 | log_setting("Input_EnableMotion", values.motion_enabled.GetValue()); | 75 | log_setting("Input_EnableMotion", values.motion_enabled.GetValue()); |
| 75 | log_setting("Input_EnableVibration", values.vibration_enabled.GetValue()); | 76 | log_setting("Input_EnableVibration", values.vibration_enabled.GetValue()); |
| 76 | log_setting("Input_EnableRawInput", values.enable_raw_input.GetValue()); | 77 | log_setting("Input_EnableRawInput", values.enable_raw_input.GetValue()); |
| @@ -104,7 +105,7 @@ float Volume() { | |||
| 104 | if (values.audio_muted) { | 105 | if (values.audio_muted) { |
| 105 | return 0.0f; | 106 | return 0.0f; |
| 106 | } | 107 | } |
| 107 | return values.volume.GetValue() / 100.0f; | 108 | return values.volume.GetValue() / static_cast<f32>(values.volume.GetDefault()); |
| 108 | } | 109 | } |
| 109 | 110 | ||
| 110 | void UpdateRescalingInfo() { | 111 | void UpdateRescalingInfo() { |
| @@ -147,7 +148,7 @@ void UpdateRescalingInfo() { | |||
| 147 | info.down_shift = 0; | 148 | info.down_shift = 0; |
| 148 | break; | 149 | break; |
| 149 | default: | 150 | default: |
| 150 | UNREACHABLE(); | 151 | ASSERT(false); |
| 151 | info.up_scale = 1; | 152 | info.up_scale = 1; |
| 152 | info.down_shift = 0; | 153 | info.down_shift = 0; |
| 153 | } | 154 | } |
| @@ -167,6 +168,7 @@ void RestoreGlobalState(bool is_powered_on) { | |||
| 167 | 168 | ||
| 168 | // Core | 169 | // Core |
| 169 | values.use_multi_core.SetGlobal(true); | 170 | values.use_multi_core.SetGlobal(true); |
| 171 | values.use_extended_memory_layout.SetGlobal(true); | ||
| 170 | 172 | ||
| 171 | // CPU | 173 | // CPU |
| 172 | values.cpu_accuracy.SetGlobal(true); | 174 | values.cpu_accuracy.SetGlobal(true); |
| @@ -175,6 +177,7 @@ void RestoreGlobalState(bool is_powered_on) { | |||
| 175 | values.cpuopt_unsafe_ignore_standard_fpcr.SetGlobal(true); | 177 | values.cpuopt_unsafe_ignore_standard_fpcr.SetGlobal(true); |
| 176 | values.cpuopt_unsafe_inaccurate_nan.SetGlobal(true); | 178 | values.cpuopt_unsafe_inaccurate_nan.SetGlobal(true); |
| 177 | values.cpuopt_unsafe_fastmem_check.SetGlobal(true); | 179 | values.cpuopt_unsafe_fastmem_check.SetGlobal(true); |
| 180 | values.cpuopt_unsafe_ignore_global_monitor.SetGlobal(true); | ||
| 178 | 181 | ||
| 179 | // Renderer | 182 | // Renderer |
| 180 | values.renderer_backend.SetGlobal(true); | 183 | values.renderer_backend.SetGlobal(true); |
| @@ -183,7 +186,6 @@ void RestoreGlobalState(bool is_powered_on) { | |||
| 183 | values.max_anisotropy.SetGlobal(true); | 186 | values.max_anisotropy.SetGlobal(true); |
| 184 | values.use_speed_limit.SetGlobal(true); | 187 | values.use_speed_limit.SetGlobal(true); |
| 185 | values.speed_limit.SetGlobal(true); | 188 | values.speed_limit.SetGlobal(true); |
| 186 | values.fps_cap.SetGlobal(true); | ||
| 187 | values.use_disk_shader_cache.SetGlobal(true); | 189 | values.use_disk_shader_cache.SetGlobal(true); |
| 188 | values.gpu_accuracy.SetGlobal(true); | 190 | values.gpu_accuracy.SetGlobal(true); |
| 189 | values.use_asynchronous_gpu_emulation.SetGlobal(true); | 191 | values.use_asynchronous_gpu_emulation.SetGlobal(true); |
| @@ -193,6 +195,7 @@ void RestoreGlobalState(bool is_powered_on) { | |||
| 193 | values.shader_backend.SetGlobal(true); | 195 | values.shader_backend.SetGlobal(true); |
| 194 | values.use_asynchronous_shaders.SetGlobal(true); | 196 | values.use_asynchronous_shaders.SetGlobal(true); |
| 195 | values.use_fast_gpu_time.SetGlobal(true); | 197 | values.use_fast_gpu_time.SetGlobal(true); |
| 198 | values.use_pessimistic_flushes.SetGlobal(true); | ||
| 196 | values.bg_red.SetGlobal(true); | 199 | values.bg_red.SetGlobal(true); |
| 197 | values.bg_green.SetGlobal(true); | 200 | values.bg_green.SetGlobal(true); |
| 198 | values.bg_blue.SetGlobal(true); | 201 | values.bg_blue.SetGlobal(true); |
diff --git a/src/common/settings.h b/src/common/settings.h index 9bee6e10f..d2452c93b 100644 --- a/src/common/settings.h +++ b/src/common/settings.h | |||
| @@ -1,6 +1,5 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | // Refer to the license.txt file included. | ||
| 4 | 3 | ||
| 5 | #pragma once | 4 | #pragma once |
| 6 | 5 | ||
| @@ -38,6 +37,7 @@ enum class CPUAccuracy : u32 { | |||
| 38 | Auto = 0, | 37 | Auto = 0, |
| 39 | Accurate = 1, | 38 | Accurate = 1, |
| 40 | Unsafe = 2, | 39 | Unsafe = 2, |
| 40 | Paranoid = 3, | ||
| 41 | }; | 41 | }; |
| 42 | 42 | ||
| 43 | enum class FullscreenMode : u32 { | 43 | enum class FullscreenMode : u32 { |
| @@ -101,15 +101,15 @@ struct ResolutionScalingInfo { | |||
| 101 | } | 101 | } |
| 102 | }; | 102 | }; |
| 103 | 103 | ||
| 104 | /** The BasicSetting class is a simple resource manager. It defines a label and default value | 104 | /** The Setting class is a simple resource manager. It defines a label and default value alongside |
| 105 | * alongside the actual value of the setting for simpler and less-error prone use with frontend | 105 | * the actual value of the setting for simpler and less-error prone use with frontend |
| 106 | * configurations. Setting a default value and label is required, though subclasses may deviate from | 106 | * configurations. Specifying a default value and label is required. A minimum and maximum range can |
| 107 | * this requirement. | 107 | * be specified for sanitization. |
| 108 | */ | 108 | */ |
| 109 | template <typename Type> | 109 | template <typename Type, bool ranged = false> |
| 110 | class BasicSetting { | 110 | class Setting { |
| 111 | protected: | 111 | protected: |
| 112 | BasicSetting() = default; | 112 | Setting() = default; |
| 113 | 113 | ||
| 114 | /** | 114 | /** |
| 115 | * Only sets the setting to the given initializer, leaving the other members to their default | 115 | * Only sets the setting to the given initializer, leaving the other members to their default |
| @@ -117,7 +117,7 @@ protected: | |||
| 117 | * | 117 | * |
| 118 | * @param global_val Initial value of the setting | 118 | * @param global_val Initial value of the setting |
| 119 | */ | 119 | */ |
| 120 | explicit BasicSetting(const Type& global_val) : global{global_val} {} | 120 | explicit Setting(const Type& val) : value{val} {} |
| 121 | 121 | ||
| 122 | public: | 122 | public: |
| 123 | /** | 123 | /** |
| @@ -126,9 +126,22 @@ public: | |||
| 126 | * @param default_val Intial value of the setting, and default value of the setting | 126 | * @param default_val Intial value of the setting, and default value of the setting |
| 127 | * @param name Label for the setting | 127 | * @param name Label for the setting |
| 128 | */ | 128 | */ |
| 129 | explicit BasicSetting(const Type& default_val, const std::string& name) | 129 | explicit Setting(const Type& default_val, const std::string& name) requires(!ranged) |
| 130 | : default_value{default_val}, global{default_val}, label{name} {} | 130 | : value{default_val}, default_value{default_val}, label{name} {} |
| 131 | virtual ~BasicSetting() = default; | 131 | virtual ~Setting() = default; |
| 132 | |||
| 133 | /** | ||
| 134 | * Sets a default value, minimum value, maximum value, and label. | ||
| 135 | * | ||
| 136 | * @param default_val Intial value of the setting, and default value of the setting | ||
| 137 | * @param min_val Sets the minimum allowed value of the setting | ||
| 138 | * @param max_val Sets the maximum allowed value of the setting | ||
| 139 | * @param name Label for the setting | ||
| 140 | */ | ||
| 141 | explicit Setting(const Type& default_val, const Type& min_val, const Type& max_val, | ||
| 142 | const std::string& name) requires(ranged) | ||
| 143 | : value{default_val}, | ||
| 144 | default_value{default_val}, maximum{max_val}, minimum{min_val}, label{name} {} | ||
| 132 | 145 | ||
| 133 | /** | 146 | /** |
| 134 | * Returns a reference to the setting's value. | 147 | * Returns a reference to the setting's value. |
| @@ -136,17 +149,17 @@ public: | |||
| 136 | * @returns A reference to the setting | 149 | * @returns A reference to the setting |
| 137 | */ | 150 | */ |
| 138 | [[nodiscard]] virtual const Type& GetValue() const { | 151 | [[nodiscard]] virtual const Type& GetValue() const { |
| 139 | return global; | 152 | return value; |
| 140 | } | 153 | } |
| 141 | 154 | ||
| 142 | /** | 155 | /** |
| 143 | * Sets the setting to the given value. | 156 | * Sets the setting to the given value. |
| 144 | * | 157 | * |
| 145 | * @param value The desired value | 158 | * @param val The desired value |
| 146 | */ | 159 | */ |
| 147 | virtual void SetValue(const Type& value) { | 160 | virtual void SetValue(const Type& val) { |
| 148 | Type temp{value}; | 161 | Type temp{ranged ? std::clamp(val, minimum, maximum) : val}; |
| 149 | std::swap(global, temp); | 162 | std::swap(value, temp); |
| 150 | } | 163 | } |
| 151 | 164 | ||
| 152 | /** | 165 | /** |
| @@ -170,14 +183,14 @@ public: | |||
| 170 | /** | 183 | /** |
| 171 | * Assigns a value to the setting. | 184 | * Assigns a value to the setting. |
| 172 | * | 185 | * |
| 173 | * @param value The desired setting value | 186 | * @param val The desired setting value |
| 174 | * | 187 | * |
| 175 | * @returns A reference to the setting | 188 | * @returns A reference to the setting |
| 176 | */ | 189 | */ |
| 177 | virtual const Type& operator=(const Type& value) { | 190 | virtual const Type& operator=(const Type& val) { |
| 178 | Type temp{value}; | 191 | Type temp{ranged ? std::clamp(val, minimum, maximum) : val}; |
| 179 | std::swap(global, temp); | 192 | std::swap(value, temp); |
| 180 | return global; | 193 | return value; |
| 181 | } | 194 | } |
| 182 | 195 | ||
| 183 | /** | 196 | /** |
| @@ -186,72 +199,27 @@ public: | |||
| 186 | * @returns A reference to the setting | 199 | * @returns A reference to the setting |
| 187 | */ | 200 | */ |
| 188 | explicit virtual operator const Type&() const { | 201 | explicit virtual operator const Type&() const { |
| 189 | return global; | 202 | return value; |
| 190 | } | 203 | } |
| 191 | 204 | ||
| 192 | protected: | 205 | protected: |
| 206 | Type value{}; ///< The setting | ||
| 193 | const Type default_value{}; ///< The default value | 207 | const Type default_value{}; ///< The default value |
| 194 | Type global{}; ///< The setting | 208 | const Type maximum{}; ///< Maximum allowed value of the setting |
| 209 | const Type minimum{}; ///< Minimum allowed value of the setting | ||
| 195 | const std::string label{}; ///< The setting's label | 210 | const std::string label{}; ///< The setting's label |
| 196 | }; | 211 | }; |
| 197 | 212 | ||
| 198 | /** | 213 | /** |
| 199 | * BasicRangedSetting class is intended for use with quantifiable settings that need a more | 214 | * The SwitchableSetting class is a slightly more complex version of the Setting class. This adds a |
| 200 | * restrictive range than implicitly defined by its type. Implements a minimum and maximum that is | ||
| 201 | * simply used to sanitize SetValue and the assignment overload. | ||
| 202 | */ | ||
| 203 | template <typename Type> | ||
| 204 | class BasicRangedSetting : virtual public BasicSetting<Type> { | ||
| 205 | public: | ||
| 206 | /** | ||
| 207 | * Sets a default value, minimum value, maximum value, and label. | ||
| 208 | * | ||
| 209 | * @param default_val Intial value of the setting, and default value of the setting | ||
| 210 | * @param min_val Sets the minimum allowed value of the setting | ||
| 211 | * @param max_val Sets the maximum allowed value of the setting | ||
| 212 | * @param name Label for the setting | ||
| 213 | */ | ||
| 214 | explicit BasicRangedSetting(const Type& default_val, const Type& min_val, const Type& max_val, | ||
| 215 | const std::string& name) | ||
| 216 | : BasicSetting<Type>{default_val, name}, minimum{min_val}, maximum{max_val} {} | ||
| 217 | virtual ~BasicRangedSetting() = default; | ||
| 218 | |||
| 219 | /** | ||
| 220 | * Like BasicSetting's SetValue, except value is clamped to the range of the setting. | ||
| 221 | * | ||
| 222 | * @param value The desired value | ||
| 223 | */ | ||
| 224 | void SetValue(const Type& value) override { | ||
| 225 | this->global = std::clamp(value, minimum, maximum); | ||
| 226 | } | ||
| 227 | |||
| 228 | /** | ||
| 229 | * Like BasicSetting's assignment overload, except value is clamped to the range of the setting. | ||
| 230 | * | ||
| 231 | * @param value The desired value | ||
| 232 | * @returns A reference to the setting's value | ||
| 233 | */ | ||
| 234 | const Type& operator=(const Type& value) override { | ||
| 235 | this->global = std::clamp(value, minimum, maximum); | ||
| 236 | return this->global; | ||
| 237 | } | ||
| 238 | |||
| 239 | const Type minimum; ///< Minimum allowed value of the setting | ||
| 240 | const Type maximum; ///< Maximum allowed value of the setting | ||
| 241 | }; | ||
| 242 | |||
| 243 | /** | ||
| 244 | * The Setting class is a slightly more complex version of the BasicSetting class. This adds a | ||
| 245 | * custom setting to switch to when a guest application specifically requires it. The effect is that | 215 | * custom setting to switch to when a guest application specifically requires it. The effect is that |
| 246 | * other components of the emulator can access the setting's intended value without any need for the | 216 | * other components of the emulator can access the setting's intended value without any need for the |
| 247 | * component to ask whether the custom or global setting is needed at the moment. | 217 | * component to ask whether the custom or global setting is needed at the moment. |
| 248 | * | 218 | * |
| 249 | * By default, the global setting is used. | 219 | * By default, the global setting is used. |
| 250 | * | ||
| 251 | * Like the BasicSetting, this requires setting a default value and label to use. | ||
| 252 | */ | 220 | */ |
| 253 | template <typename Type> | 221 | template <typename Type, bool ranged = false> |
| 254 | class Setting : virtual public BasicSetting<Type> { | 222 | class SwitchableSetting : virtual public Setting<Type, ranged> { |
| 255 | public: | 223 | public: |
| 256 | /** | 224 | /** |
| 257 | * Sets a default value, label, and setting value. | 225 | * Sets a default value, label, and setting value. |
| @@ -259,9 +227,21 @@ public: | |||
| 259 | * @param default_val Intial value of the setting, and default value of the setting | 227 | * @param default_val Intial value of the setting, and default value of the setting |
| 260 | * @param name Label for the setting | 228 | * @param name Label for the setting |
| 261 | */ | 229 | */ |
| 262 | explicit Setting(const Type& default_val, const std::string& name) | 230 | explicit SwitchableSetting(const Type& default_val, const std::string& name) requires(!ranged) |
| 263 | : BasicSetting<Type>(default_val, name) {} | 231 | : Setting<Type>{default_val, name} {} |
| 264 | virtual ~Setting() = default; | 232 | virtual ~SwitchableSetting() = default; |
| 233 | |||
| 234 | /** | ||
| 235 | * Sets a default value, minimum value, maximum value, and label. | ||
| 236 | * | ||
| 237 | * @param default_val Intial value of the setting, and default value of the setting | ||
| 238 | * @param min_val Sets the minimum allowed value of the setting | ||
| 239 | * @param max_val Sets the maximum allowed value of the setting | ||
| 240 | * @param name Label for the setting | ||
| 241 | */ | ||
| 242 | explicit SwitchableSetting(const Type& default_val, const Type& min_val, const Type& max_val, | ||
| 243 | const std::string& name) requires(ranged) | ||
| 244 | : Setting<Type, true>{default_val, min_val, max_val, name} {} | ||
| 265 | 245 | ||
| 266 | /** | 246 | /** |
| 267 | * Tells this setting to represent either the global or custom setting when other member | 247 | * Tells this setting to represent either the global or custom setting when other member |
| @@ -292,13 +272,13 @@ public: | |||
| 292 | */ | 272 | */ |
| 293 | [[nodiscard]] virtual const Type& GetValue() const override { | 273 | [[nodiscard]] virtual const Type& GetValue() const override { |
| 294 | if (use_global) { | 274 | if (use_global) { |
| 295 | return this->global; | 275 | return this->value; |
| 296 | } | 276 | } |
| 297 | return custom; | 277 | return custom; |
| 298 | } | 278 | } |
| 299 | [[nodiscard]] virtual const Type& GetValue(bool need_global) const { | 279 | [[nodiscard]] virtual const Type& GetValue(bool need_global) const { |
| 300 | if (use_global || need_global) { | 280 | if (use_global || need_global) { |
| 301 | return this->global; | 281 | return this->value; |
| 302 | } | 282 | } |
| 303 | return custom; | 283 | return custom; |
| 304 | } | 284 | } |
| @@ -306,12 +286,12 @@ public: | |||
| 306 | /** | 286 | /** |
| 307 | * Sets the current setting value depending on the global state. | 287 | * Sets the current setting value depending on the global state. |
| 308 | * | 288 | * |
| 309 | * @param value The new value | 289 | * @param val The new value |
| 310 | */ | 290 | */ |
| 311 | void SetValue(const Type& value) override { | 291 | void SetValue(const Type& val) override { |
| 312 | Type temp{value}; | 292 | Type temp{ranged ? std::clamp(val, this->minimum, this->maximum) : val}; |
| 313 | if (use_global) { | 293 | if (use_global) { |
| 314 | std::swap(this->global, temp); | 294 | std::swap(this->value, temp); |
| 315 | } else { | 295 | } else { |
| 316 | std::swap(custom, temp); | 296 | std::swap(custom, temp); |
| 317 | } | 297 | } |
| @@ -320,15 +300,15 @@ public: | |||
| 320 | /** | 300 | /** |
| 321 | * Assigns the current setting value depending on the global state. | 301 | * Assigns the current setting value depending on the global state. |
| 322 | * | 302 | * |
| 323 | * @param value The new value | 303 | * @param val The new value |
| 324 | * | 304 | * |
| 325 | * @returns A reference to the current setting value | 305 | * @returns A reference to the current setting value |
| 326 | */ | 306 | */ |
| 327 | const Type& operator=(const Type& value) override { | 307 | const Type& operator=(const Type& val) override { |
| 328 | Type temp{value}; | 308 | Type temp{ranged ? std::clamp(val, this->minimum, this->maximum) : val}; |
| 329 | if (use_global) { | 309 | if (use_global) { |
| 330 | std::swap(this->global, temp); | 310 | std::swap(this->value, temp); |
| 331 | return this->global; | 311 | return this->value; |
| 332 | } | 312 | } |
| 333 | std::swap(custom, temp); | 313 | std::swap(custom, temp); |
| 334 | return custom; | 314 | return custom; |
| @@ -341,7 +321,7 @@ public: | |||
| 341 | */ | 321 | */ |
| 342 | virtual explicit operator const Type&() const override { | 322 | virtual explicit operator const Type&() const override { |
| 343 | if (use_global) { | 323 | if (use_global) { |
| 344 | return this->global; | 324 | return this->value; |
| 345 | } | 325 | } |
| 346 | return custom; | 326 | return custom; |
| 347 | } | 327 | } |
| @@ -352,75 +332,6 @@ protected: | |||
| 352 | }; | 332 | }; |
| 353 | 333 | ||
| 354 | /** | 334 | /** |
| 355 | * RangedSetting is a Setting that implements a maximum and minimum value for its setting. Intended | ||
| 356 | * for use with quantifiable settings. | ||
| 357 | */ | ||
| 358 | template <typename Type> | ||
| 359 | class RangedSetting final : public BasicRangedSetting<Type>, public Setting<Type> { | ||
| 360 | public: | ||
| 361 | /** | ||
| 362 | * Sets a default value, minimum value, maximum value, and label. | ||
| 363 | * | ||
| 364 | * @param default_val Intial value of the setting, and default value of the setting | ||
| 365 | * @param min_val Sets the minimum allowed value of the setting | ||
| 366 | * @param max_val Sets the maximum allowed value of the setting | ||
| 367 | * @param name Label for the setting | ||
| 368 | */ | ||
| 369 | explicit RangedSetting(const Type& default_val, const Type& min_val, const Type& max_val, | ||
| 370 | const std::string& name) | ||
| 371 | : BasicSetting<Type>{default_val, name}, | ||
| 372 | BasicRangedSetting<Type>{default_val, min_val, max_val, name}, Setting<Type>{default_val, | ||
| 373 | name} {} | ||
| 374 | virtual ~RangedSetting() = default; | ||
| 375 | |||
| 376 | // The following are needed to avoid a MSVC bug | ||
| 377 | // (source: https://stackoverflow.com/questions/469508) | ||
| 378 | [[nodiscard]] const Type& GetValue() const override { | ||
| 379 | return Setting<Type>::GetValue(); | ||
| 380 | } | ||
| 381 | [[nodiscard]] const Type& GetValue(bool need_global) const override { | ||
| 382 | return Setting<Type>::GetValue(need_global); | ||
| 383 | } | ||
| 384 | explicit operator const Type&() const override { | ||
| 385 | if (this->use_global) { | ||
| 386 | return this->global; | ||
| 387 | } | ||
| 388 | return this->custom; | ||
| 389 | } | ||
| 390 | |||
| 391 | /** | ||
| 392 | * Like BasicSetting's SetValue, except value is clamped to the range of the setting. Sets the | ||
| 393 | * appropriate value depending on the global state. | ||
| 394 | * | ||
| 395 | * @param value The desired value | ||
| 396 | */ | ||
| 397 | void SetValue(const Type& value) override { | ||
| 398 | const Type temp = std::clamp(value, this->minimum, this->maximum); | ||
| 399 | if (this->use_global) { | ||
| 400 | this->global = temp; | ||
| 401 | } | ||
| 402 | this->custom = temp; | ||
| 403 | } | ||
| 404 | |||
| 405 | /** | ||
| 406 | * Like BasicSetting's assignment overload, except value is clamped to the range of the setting. | ||
| 407 | * Uses the appropriate value depending on the global state. | ||
| 408 | * | ||
| 409 | * @param value The desired value | ||
| 410 | * @returns A reference to the setting's value | ||
| 411 | */ | ||
| 412 | const Type& operator=(const Type& value) override { | ||
| 413 | const Type temp = std::clamp(value, this->minimum, this->maximum); | ||
| 414 | if (this->use_global) { | ||
| 415 | this->global = temp; | ||
| 416 | return this->global; | ||
| 417 | } | ||
| 418 | this->custom = temp; | ||
| 419 | return this->custom; | ||
| 420 | } | ||
| 421 | }; | ||
| 422 | |||
| 423 | /** | ||
| 424 | * The InputSetting class allows for getting a reference to either the global or custom members. | 335 | * The InputSetting class allows for getting a reference to either the global or custom members. |
| 425 | * This is required as we cannot easily modify the values of user-defined types within containers | 336 | * This is required as we cannot easily modify the values of user-defined types within containers |
| 426 | * using the SetValue() member function found in the Setting class. The primary purpose of this | 337 | * using the SetValue() member function found in the Setting class. The primary purpose of this |
| @@ -431,7 +342,7 @@ template <typename Type> | |||
| 431 | class InputSetting final { | 342 | class InputSetting final { |
| 432 | public: | 343 | public: |
| 433 | InputSetting() = default; | 344 | InputSetting() = default; |
| 434 | explicit InputSetting(Type val) : BasicSetting<Type>(val) {} | 345 | explicit InputSetting(Type val) : Setting<Type>(val) {} |
| 435 | ~InputSetting() = default; | 346 | ~InputSetting() = default; |
| 436 | void SetGlobal(bool to_global) { | 347 | void SetGlobal(bool to_global) { |
| 437 | use_global = to_global; | 348 | use_global = to_global; |
| @@ -459,167 +370,181 @@ struct TouchFromButtonMap { | |||
| 459 | 370 | ||
| 460 | struct Values { | 371 | struct Values { |
| 461 | // Audio | 372 | // Audio |
| 462 | BasicSetting<std::string> audio_device_id{"auto", "output_device"}; | 373 | Setting<std::string> sink_id{"auto", "output_engine"}; |
| 463 | BasicSetting<std::string> sink_id{"auto", "output_engine"}; | 374 | Setting<std::string> audio_output_device_id{"auto", "output_device"}; |
| 464 | BasicSetting<bool> audio_muted{false, "audio_muted"}; | 375 | Setting<std::string> audio_input_device_id{"auto", "input_device"}; |
| 465 | RangedSetting<u8> volume{100, 0, 100, "volume"}; | 376 | Setting<bool> audio_muted{false, "audio_muted"}; |
| 377 | SwitchableSetting<u8, true> volume{100, 0, 200, "volume"}; | ||
| 378 | Setting<bool> dump_audio_commands{false, "dump_audio_commands"}; | ||
| 466 | 379 | ||
| 467 | // Core | 380 | // Core |
| 468 | Setting<bool> use_multi_core{true, "use_multi_core"}; | 381 | SwitchableSetting<bool> use_multi_core{true, "use_multi_core"}; |
| 382 | SwitchableSetting<bool> use_extended_memory_layout{false, "use_extended_memory_layout"}; | ||
| 469 | 383 | ||
| 470 | // Cpu | 384 | // Cpu |
| 471 | RangedSetting<CPUAccuracy> cpu_accuracy{CPUAccuracy::Auto, CPUAccuracy::Auto, | 385 | SwitchableSetting<CPUAccuracy, true> cpu_accuracy{CPUAccuracy::Auto, CPUAccuracy::Auto, |
| 472 | CPUAccuracy::Unsafe, "cpu_accuracy"}; | 386 | CPUAccuracy::Paranoid, "cpu_accuracy"}; |
| 473 | // TODO: remove cpu_accuracy_first_time, migration setting added 8 July 2021 | 387 | // TODO: remove cpu_accuracy_first_time, migration setting added 8 July 2021 |
| 474 | BasicSetting<bool> cpu_accuracy_first_time{true, "cpu_accuracy_first_time"}; | 388 | Setting<bool> cpu_accuracy_first_time{true, "cpu_accuracy_first_time"}; |
| 475 | BasicSetting<bool> cpu_debug_mode{false, "cpu_debug_mode"}; | 389 | Setting<bool> cpu_debug_mode{false, "cpu_debug_mode"}; |
| 476 | 390 | ||
| 477 | BasicSetting<bool> cpuopt_page_tables{true, "cpuopt_page_tables"}; | 391 | Setting<bool> cpuopt_page_tables{true, "cpuopt_page_tables"}; |
| 478 | BasicSetting<bool> cpuopt_block_linking{true, "cpuopt_block_linking"}; | 392 | Setting<bool> cpuopt_block_linking{true, "cpuopt_block_linking"}; |
| 479 | BasicSetting<bool> cpuopt_return_stack_buffer{true, "cpuopt_return_stack_buffer"}; | 393 | Setting<bool> cpuopt_return_stack_buffer{true, "cpuopt_return_stack_buffer"}; |
| 480 | BasicSetting<bool> cpuopt_fast_dispatcher{true, "cpuopt_fast_dispatcher"}; | 394 | Setting<bool> cpuopt_fast_dispatcher{true, "cpuopt_fast_dispatcher"}; |
| 481 | BasicSetting<bool> cpuopt_context_elimination{true, "cpuopt_context_elimination"}; | 395 | Setting<bool> cpuopt_context_elimination{true, "cpuopt_context_elimination"}; |
| 482 | BasicSetting<bool> cpuopt_const_prop{true, "cpuopt_const_prop"}; | 396 | Setting<bool> cpuopt_const_prop{true, "cpuopt_const_prop"}; |
| 483 | BasicSetting<bool> cpuopt_misc_ir{true, "cpuopt_misc_ir"}; | 397 | Setting<bool> cpuopt_misc_ir{true, "cpuopt_misc_ir"}; |
| 484 | BasicSetting<bool> cpuopt_reduce_misalign_checks{true, "cpuopt_reduce_misalign_checks"}; | 398 | Setting<bool> cpuopt_reduce_misalign_checks{true, "cpuopt_reduce_misalign_checks"}; |
| 485 | BasicSetting<bool> cpuopt_fastmem{true, "cpuopt_fastmem"}; | 399 | Setting<bool> cpuopt_fastmem{true, "cpuopt_fastmem"}; |
| 486 | 400 | Setting<bool> cpuopt_fastmem_exclusives{true, "cpuopt_fastmem_exclusives"}; | |
| 487 | Setting<bool> cpuopt_unsafe_unfuse_fma{true, "cpuopt_unsafe_unfuse_fma"}; | 401 | Setting<bool> cpuopt_recompile_exclusives{true, "cpuopt_recompile_exclusives"}; |
| 488 | Setting<bool> cpuopt_unsafe_reduce_fp_error{true, "cpuopt_unsafe_reduce_fp_error"}; | 402 | |
| 489 | Setting<bool> cpuopt_unsafe_ignore_standard_fpcr{true, "cpuopt_unsafe_ignore_standard_fpcr"}; | 403 | SwitchableSetting<bool> cpuopt_unsafe_unfuse_fma{true, "cpuopt_unsafe_unfuse_fma"}; |
| 490 | Setting<bool> cpuopt_unsafe_inaccurate_nan{true, "cpuopt_unsafe_inaccurate_nan"}; | 404 | SwitchableSetting<bool> cpuopt_unsafe_reduce_fp_error{true, "cpuopt_unsafe_reduce_fp_error"}; |
| 491 | Setting<bool> cpuopt_unsafe_fastmem_check{true, "cpuopt_unsafe_fastmem_check"}; | 405 | SwitchableSetting<bool> cpuopt_unsafe_ignore_standard_fpcr{ |
| 406 | true, "cpuopt_unsafe_ignore_standard_fpcr"}; | ||
| 407 | SwitchableSetting<bool> cpuopt_unsafe_inaccurate_nan{true, "cpuopt_unsafe_inaccurate_nan"}; | ||
| 408 | SwitchableSetting<bool> cpuopt_unsafe_fastmem_check{true, "cpuopt_unsafe_fastmem_check"}; | ||
| 409 | SwitchableSetting<bool> cpuopt_unsafe_ignore_global_monitor{ | ||
| 410 | true, "cpuopt_unsafe_ignore_global_monitor"}; | ||
| 492 | 411 | ||
| 493 | // Renderer | 412 | // Renderer |
| 494 | RangedSetting<RendererBackend> renderer_backend{ | 413 | SwitchableSetting<RendererBackend, true> renderer_backend{ |
| 495 | RendererBackend::OpenGL, RendererBackend::OpenGL, RendererBackend::Vulkan, "backend"}; | 414 | RendererBackend::Vulkan, RendererBackend::OpenGL, RendererBackend::Vulkan, "backend"}; |
| 496 | BasicSetting<bool> renderer_debug{false, "debug"}; | 415 | Setting<bool> renderer_debug{false, "debug"}; |
| 497 | BasicSetting<bool> renderer_shader_feedback{false, "shader_feedback"}; | 416 | Setting<bool> renderer_shader_feedback{false, "shader_feedback"}; |
| 498 | BasicSetting<bool> enable_nsight_aftermath{false, "nsight_aftermath"}; | 417 | Setting<bool> enable_nsight_aftermath{false, "nsight_aftermath"}; |
| 499 | BasicSetting<bool> disable_shader_loop_safety_checks{false, | 418 | Setting<bool> disable_shader_loop_safety_checks{false, "disable_shader_loop_safety_checks"}; |
| 500 | "disable_shader_loop_safety_checks"}; | 419 | SwitchableSetting<int> vulkan_device{0, "vulkan_device"}; |
| 501 | Setting<int> vulkan_device{0, "vulkan_device"}; | ||
| 502 | 420 | ||
| 503 | ResolutionScalingInfo resolution_info{}; | 421 | ResolutionScalingInfo resolution_info{}; |
| 504 | Setting<ResolutionSetup> resolution_setup{ResolutionSetup::Res1X, "resolution_setup"}; | 422 | SwitchableSetting<ResolutionSetup> resolution_setup{ResolutionSetup::Res1X, "resolution_setup"}; |
| 505 | Setting<ScalingFilter> scaling_filter{ScalingFilter::Bilinear, "scaling_filter"}; | 423 | SwitchableSetting<ScalingFilter> scaling_filter{ScalingFilter::Bilinear, "scaling_filter"}; |
| 506 | Setting<AntiAliasing> anti_aliasing{AntiAliasing::None, "anti_aliasing"}; | 424 | SwitchableSetting<AntiAliasing> anti_aliasing{AntiAliasing::None, "anti_aliasing"}; |
| 507 | // *nix platforms may have issues with the borderless windowed fullscreen mode. | 425 | // *nix platforms may have issues with the borderless windowed fullscreen mode. |
| 508 | // Default to exclusive fullscreen on these platforms for now. | 426 | // Default to exclusive fullscreen on these platforms for now. |
| 509 | RangedSetting<FullscreenMode> fullscreen_mode{ | 427 | SwitchableSetting<FullscreenMode, true> fullscreen_mode{ |
| 510 | #ifdef _WIN32 | 428 | #ifdef _WIN32 |
| 511 | FullscreenMode::Borderless, | 429 | FullscreenMode::Borderless, |
| 512 | #else | 430 | #else |
| 513 | FullscreenMode::Exclusive, | 431 | FullscreenMode::Exclusive, |
| 514 | #endif | 432 | #endif |
| 515 | FullscreenMode::Borderless, FullscreenMode::Exclusive, "fullscreen_mode"}; | 433 | FullscreenMode::Borderless, FullscreenMode::Exclusive, "fullscreen_mode"}; |
| 516 | RangedSetting<int> aspect_ratio{0, 0, 3, "aspect_ratio"}; | 434 | SwitchableSetting<int, true> aspect_ratio{0, 0, 3, "aspect_ratio"}; |
| 517 | RangedSetting<int> max_anisotropy{0, 0, 5, "max_anisotropy"}; | 435 | SwitchableSetting<int, true> max_anisotropy{0, 0, 5, "max_anisotropy"}; |
| 518 | Setting<bool> use_speed_limit{true, "use_speed_limit"}; | 436 | SwitchableSetting<bool> use_speed_limit{true, "use_speed_limit"}; |
| 519 | RangedSetting<u16> speed_limit{100, 0, 9999, "speed_limit"}; | 437 | SwitchableSetting<u16, true> speed_limit{100, 0, 9999, "speed_limit"}; |
| 520 | Setting<bool> use_disk_shader_cache{true, "use_disk_shader_cache"}; | 438 | SwitchableSetting<bool> use_disk_shader_cache{true, "use_disk_shader_cache"}; |
| 521 | RangedSetting<GPUAccuracy> gpu_accuracy{GPUAccuracy::High, GPUAccuracy::Normal, | 439 | SwitchableSetting<GPUAccuracy, true> gpu_accuracy{GPUAccuracy::High, GPUAccuracy::Normal, |
| 522 | GPUAccuracy::Extreme, "gpu_accuracy"}; | 440 | GPUAccuracy::Extreme, "gpu_accuracy"}; |
| 523 | Setting<bool> use_asynchronous_gpu_emulation{true, "use_asynchronous_gpu_emulation"}; | 441 | SwitchableSetting<bool> use_asynchronous_gpu_emulation{true, "use_asynchronous_gpu_emulation"}; |
| 524 | Setting<NvdecEmulation> nvdec_emulation{NvdecEmulation::GPU, "nvdec_emulation"}; | 442 | SwitchableSetting<NvdecEmulation> nvdec_emulation{NvdecEmulation::GPU, "nvdec_emulation"}; |
| 525 | Setting<bool> accelerate_astc{true, "accelerate_astc"}; | 443 | SwitchableSetting<bool> accelerate_astc{true, "accelerate_astc"}; |
| 526 | Setting<bool> use_vsync{true, "use_vsync"}; | 444 | SwitchableSetting<bool> use_vsync{true, "use_vsync"}; |
| 527 | RangedSetting<u16> fps_cap{1000, 1, 1000, "fps_cap"}; | 445 | SwitchableSetting<ShaderBackend, true> shader_backend{ShaderBackend::GLASM, ShaderBackend::GLSL, |
| 528 | BasicSetting<bool> disable_fps_limit{false, "disable_fps_limit"}; | 446 | ShaderBackend::SPIRV, "shader_backend"}; |
| 529 | RangedSetting<ShaderBackend> shader_backend{ShaderBackend::GLASM, ShaderBackend::GLSL, | 447 | SwitchableSetting<bool> use_asynchronous_shaders{false, "use_asynchronous_shaders"}; |
| 530 | ShaderBackend::SPIRV, "shader_backend"}; | 448 | SwitchableSetting<bool> use_fast_gpu_time{true, "use_fast_gpu_time"}; |
| 531 | Setting<bool> use_asynchronous_shaders{false, "use_asynchronous_shaders"}; | 449 | SwitchableSetting<bool> use_pessimistic_flushes{false, "use_pessimistic_flushes"}; |
| 532 | Setting<bool> use_fast_gpu_time{true, "use_fast_gpu_time"}; | 450 | |
| 533 | 451 | SwitchableSetting<u8> bg_red{0, "bg_red"}; | |
| 534 | Setting<u8> bg_red{0, "bg_red"}; | 452 | SwitchableSetting<u8> bg_green{0, "bg_green"}; |
| 535 | Setting<u8> bg_green{0, "bg_green"}; | 453 | SwitchableSetting<u8> bg_blue{0, "bg_blue"}; |
| 536 | Setting<u8> bg_blue{0, "bg_blue"}; | ||
| 537 | 454 | ||
| 538 | // System | 455 | // System |
| 539 | Setting<std::optional<u32>> rng_seed{std::optional<u32>(), "rng_seed"}; | 456 | SwitchableSetting<std::optional<u32>> rng_seed{std::optional<u32>(), "rng_seed"}; |
| 540 | // Measured in seconds since epoch | 457 | // Measured in seconds since epoch |
| 541 | std::optional<s64> custom_rtc; | 458 | std::optional<s64> custom_rtc; |
| 542 | // Set on game boot, reset on stop. Seconds difference between current time and `custom_rtc` | 459 | // Set on game boot, reset on stop. Seconds difference between current time and `custom_rtc` |
| 543 | s64 custom_rtc_differential; | 460 | s64 custom_rtc_differential; |
| 544 | 461 | ||
| 545 | BasicSetting<s32> current_user{0, "current_user"}; | 462 | Setting<s32> current_user{0, "current_user"}; |
| 546 | RangedSetting<s32> language_index{1, 0, 17, "language_index"}; | 463 | SwitchableSetting<s32, true> language_index{1, 0, 17, "language_index"}; |
| 547 | RangedSetting<s32> region_index{1, 0, 6, "region_index"}; | 464 | SwitchableSetting<s32, true> region_index{1, 0, 6, "region_index"}; |
| 548 | RangedSetting<s32> time_zone_index{0, 0, 45, "time_zone_index"}; | 465 | SwitchableSetting<s32, true> time_zone_index{0, 0, 45, "time_zone_index"}; |
| 549 | RangedSetting<s32> sound_index{1, 0, 2, "sound_index"}; | 466 | SwitchableSetting<s32, true> sound_index{1, 0, 2, "sound_index"}; |
| 550 | 467 | ||
| 551 | // Controls | 468 | // Controls |
| 552 | InputSetting<std::array<PlayerInput, 10>> players; | 469 | InputSetting<std::array<PlayerInput, 10>> players; |
| 553 | 470 | ||
| 554 | Setting<bool> use_docked_mode{true, "use_docked_mode"}; | 471 | SwitchableSetting<bool> use_docked_mode{true, "use_docked_mode"}; |
| 555 | 472 | ||
| 556 | BasicSetting<bool> enable_raw_input{false, "enable_raw_input"}; | 473 | Setting<bool> enable_raw_input{false, "enable_raw_input"}; |
| 557 | BasicSetting<bool> controller_navigation{true, "controller_navigation"}; | 474 | Setting<bool> controller_navigation{true, "controller_navigation"}; |
| 558 | 475 | ||
| 559 | Setting<bool> vibration_enabled{true, "vibration_enabled"}; | 476 | SwitchableSetting<bool> vibration_enabled{true, "vibration_enabled"}; |
| 560 | Setting<bool> enable_accurate_vibrations{false, "enable_accurate_vibrations"}; | 477 | SwitchableSetting<bool> enable_accurate_vibrations{false, "enable_accurate_vibrations"}; |
| 561 | 478 | ||
| 562 | Setting<bool> motion_enabled{true, "motion_enabled"}; | 479 | SwitchableSetting<bool> motion_enabled{true, "motion_enabled"}; |
| 563 | BasicSetting<std::string> udp_input_servers{"127.0.0.1:26760", "udp_input_servers"}; | 480 | Setting<std::string> udp_input_servers{"127.0.0.1:26760", "udp_input_servers"}; |
| 564 | BasicSetting<bool> enable_udp_controller{false, "enable_udp_controller"}; | 481 | Setting<bool> enable_udp_controller{false, "enable_udp_controller"}; |
| 565 | 482 | ||
| 566 | BasicSetting<bool> pause_tas_on_load{true, "pause_tas_on_load"}; | 483 | Setting<bool> pause_tas_on_load{true, "pause_tas_on_load"}; |
| 567 | BasicSetting<bool> tas_enable{false, "tas_enable"}; | 484 | Setting<bool> tas_enable{false, "tas_enable"}; |
| 568 | BasicSetting<bool> tas_loop{false, "tas_loop"}; | 485 | Setting<bool> tas_loop{false, "tas_loop"}; |
| 569 | 486 | ||
| 570 | BasicSetting<bool> mouse_panning{false, "mouse_panning"}; | 487 | Setting<bool> mouse_panning{false, "mouse_panning"}; |
| 571 | BasicRangedSetting<u8> mouse_panning_sensitivity{10, 1, 100, "mouse_panning_sensitivity"}; | 488 | Setting<u8, true> mouse_panning_sensitivity{10, 1, 100, "mouse_panning_sensitivity"}; |
| 572 | BasicSetting<bool> mouse_enabled{false, "mouse_enabled"}; | 489 | Setting<bool> mouse_enabled{false, "mouse_enabled"}; |
| 573 | 490 | ||
| 574 | BasicSetting<bool> emulate_analog_keyboard{false, "emulate_analog_keyboard"}; | 491 | Setting<bool> emulate_analog_keyboard{false, "emulate_analog_keyboard"}; |
| 575 | BasicSetting<bool> keyboard_enabled{false, "keyboard_enabled"}; | 492 | Setting<bool> keyboard_enabled{false, "keyboard_enabled"}; |
| 576 | 493 | ||
| 577 | BasicSetting<bool> debug_pad_enabled{false, "debug_pad_enabled"}; | 494 | Setting<bool> debug_pad_enabled{false, "debug_pad_enabled"}; |
| 578 | ButtonsRaw debug_pad_buttons; | 495 | ButtonsRaw debug_pad_buttons; |
| 579 | AnalogsRaw debug_pad_analogs; | 496 | AnalogsRaw debug_pad_analogs; |
| 580 | 497 | ||
| 581 | TouchscreenInput touchscreen; | 498 | TouchscreenInput touchscreen; |
| 582 | 499 | ||
| 583 | BasicSetting<std::string> touch_device{"min_x:100,min_y:50,max_x:1800,max_y:850", | 500 | Setting<std::string> touch_device{"min_x:100,min_y:50,max_x:1800,max_y:850", "touch_device"}; |
| 584 | "touch_device"}; | 501 | Setting<int> touch_from_button_map_index{0, "touch_from_button_map"}; |
| 585 | BasicSetting<int> touch_from_button_map_index{0, "touch_from_button_map"}; | ||
| 586 | std::vector<TouchFromButtonMap> touch_from_button_maps; | 502 | std::vector<TouchFromButtonMap> touch_from_button_maps; |
| 587 | 503 | ||
| 504 | Setting<bool> enable_ring_controller{true, "enable_ring_controller"}; | ||
| 505 | RingconRaw ringcon_analogs; | ||
| 506 | |||
| 507 | Setting<bool> enable_ir_sensor{false, "enable_ir_sensor"}; | ||
| 508 | Setting<std::string> ir_sensor_device{"auto", "ir_sensor_device"}; | ||
| 509 | |||
| 588 | // Data Storage | 510 | // Data Storage |
| 589 | BasicSetting<bool> use_virtual_sd{true, "use_virtual_sd"}; | 511 | Setting<bool> use_virtual_sd{true, "use_virtual_sd"}; |
| 590 | BasicSetting<bool> gamecard_inserted{false, "gamecard_inserted"}; | 512 | Setting<bool> gamecard_inserted{false, "gamecard_inserted"}; |
| 591 | BasicSetting<bool> gamecard_current_game{false, "gamecard_current_game"}; | 513 | Setting<bool> gamecard_current_game{false, "gamecard_current_game"}; |
| 592 | BasicSetting<std::string> gamecard_path{std::string(), "gamecard_path"}; | 514 | Setting<std::string> gamecard_path{std::string(), "gamecard_path"}; |
| 593 | 515 | ||
| 594 | // Debugging | 516 | // Debugging |
| 595 | bool record_frame_times; | 517 | bool record_frame_times; |
| 596 | BasicSetting<bool> use_gdbstub{false, "use_gdbstub"}; | 518 | Setting<bool> use_gdbstub{false, "use_gdbstub"}; |
| 597 | BasicSetting<u16> gdbstub_port{0, "gdbstub_port"}; | 519 | Setting<u16> gdbstub_port{6543, "gdbstub_port"}; |
| 598 | BasicSetting<std::string> program_args{std::string(), "program_args"}; | 520 | Setting<std::string> program_args{std::string(), "program_args"}; |
| 599 | BasicSetting<bool> dump_exefs{false, "dump_exefs"}; | 521 | Setting<bool> dump_exefs{false, "dump_exefs"}; |
| 600 | BasicSetting<bool> dump_nso{false, "dump_nso"}; | 522 | Setting<bool> dump_nso{false, "dump_nso"}; |
| 601 | BasicSetting<bool> dump_shaders{false, "dump_shaders"}; | 523 | Setting<bool> dump_shaders{false, "dump_shaders"}; |
| 602 | BasicSetting<bool> enable_fs_access_log{false, "enable_fs_access_log"}; | 524 | Setting<bool> dump_macros{false, "dump_macros"}; |
| 603 | BasicSetting<bool> reporting_services{false, "reporting_services"}; | 525 | Setting<bool> enable_fs_access_log{false, "enable_fs_access_log"}; |
| 604 | BasicSetting<bool> quest_flag{false, "quest_flag"}; | 526 | Setting<bool> reporting_services{false, "reporting_services"}; |
| 605 | BasicSetting<bool> disable_macro_jit{false, "disable_macro_jit"}; | 527 | Setting<bool> quest_flag{false, "quest_flag"}; |
| 606 | BasicSetting<bool> extended_logging{false, "extended_logging"}; | 528 | Setting<bool> disable_macro_jit{false, "disable_macro_jit"}; |
| 607 | BasicSetting<bool> use_debug_asserts{false, "use_debug_asserts"}; | 529 | Setting<bool> extended_logging{false, "extended_logging"}; |
| 608 | BasicSetting<bool> use_auto_stub{false, "use_auto_stub"}; | 530 | Setting<bool> use_debug_asserts{false, "use_debug_asserts"}; |
| 609 | BasicSetting<bool> enable_all_controllers{false, "enable_all_controllers"}; | 531 | Setting<bool> use_auto_stub{false, "use_auto_stub"}; |
| 532 | Setting<bool> enable_all_controllers{false, "enable_all_controllers"}; | ||
| 533 | Setting<bool> create_crash_dumps{false, "create_crash_dumps"}; | ||
| 534 | Setting<bool> perform_vulkan_check{true, "perform_vulkan_check"}; | ||
| 610 | 535 | ||
| 611 | // Miscellaneous | 536 | // Miscellaneous |
| 612 | BasicSetting<std::string> log_filter{"*:Info", "log_filter"}; | 537 | Setting<std::string> log_filter{"*:Info", "log_filter"}; |
| 613 | BasicSetting<bool> use_dev_keys{false, "use_dev_keys"}; | 538 | Setting<bool> use_dev_keys{false, "use_dev_keys"}; |
| 614 | 539 | ||
| 615 | // Network | 540 | // Network |
| 616 | BasicSetting<std::string> network_interface{std::string(), "network_interface"}; | 541 | Setting<std::string> network_interface{std::string(), "network_interface"}; |
| 617 | 542 | ||
| 618 | // WebService | 543 | // WebService |
| 619 | BasicSetting<bool> enable_telemetry{true, "enable_telemetry"}; | 544 | Setting<bool> enable_telemetry{true, "enable_telemetry"}; |
| 620 | BasicSetting<std::string> web_api_url{"https://api.yuzu-emu.org", "web_api_url"}; | 545 | Setting<std::string> web_api_url{"https://api.yuzu-emu.org", "web_api_url"}; |
| 621 | BasicSetting<std::string> yuzu_username{std::string(), "yuzu_username"}; | 546 | Setting<std::string> yuzu_username{std::string(), "yuzu_username"}; |
| 622 | BasicSetting<std::string> yuzu_token{std::string(), "yuzu_token"}; | 547 | Setting<std::string> yuzu_token{std::string(), "yuzu_token"}; |
| 623 | 548 | ||
| 624 | // Add-Ons | 549 | // Add-Ons |
| 625 | std::map<u64, std::vector<std::string>> disabled_addons; | 550 | std::map<u64, std::vector<std::string>> disabled_addons; |
diff --git a/src/common/settings_input.cpp b/src/common/settings_input.cpp index bea2b837b..0a6eea3cf 100644 --- a/src/common/settings_input.cpp +++ b/src/common/settings_input.cpp | |||
| @@ -1,6 +1,5 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | // Refer to the license.txt file included. | ||
| 4 | 3 | ||
| 5 | #include "common/settings_input.h" | 4 | #include "common/settings_input.h" |
| 6 | 5 | ||
diff --git a/src/common/settings_input.h b/src/common/settings_input.h index 4ff37e186..485e4ad22 100644 --- a/src/common/settings_input.h +++ b/src/common/settings_input.h | |||
| @@ -1,6 +1,5 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | // Refer to the license.txt file included. | ||
| 4 | 3 | ||
| 5 | #pragma once | 4 | #pragma once |
| 6 | 5 | ||
| @@ -357,6 +356,7 @@ constexpr int NUM_KEYBOARD_MODS_HID = NumKeyboardMods; | |||
| 357 | using AnalogsRaw = std::array<std::string, NativeAnalog::NumAnalogs>; | 356 | using AnalogsRaw = std::array<std::string, NativeAnalog::NumAnalogs>; |
| 358 | using ButtonsRaw = std::array<std::string, NativeButton::NumButtons>; | 357 | using ButtonsRaw = std::array<std::string, NativeButton::NumButtons>; |
| 359 | using MotionsRaw = std::array<std::string, NativeMotion::NumMotions>; | 358 | using MotionsRaw = std::array<std::string, NativeMotion::NumMotions>; |
| 359 | using RingconRaw = std::string; | ||
| 360 | 360 | ||
| 361 | constexpr u32 JOYCON_BODY_NEON_RED = 0xFF3C28; | 361 | constexpr u32 JOYCON_BODY_NEON_RED = 0xFF3C28; |
| 362 | constexpr u32 JOYCON_BUTTONS_NEON_RED = 0x1E0A0A; | 362 | constexpr u32 JOYCON_BUTTONS_NEON_RED = 0x1E0A0A; |
diff --git a/src/common/socket_types.h b/src/common/socket_types.h new file mode 100644 index 000000000..0a801a443 --- /dev/null +++ b/src/common/socket_types.h | |||
| @@ -0,0 +1,51 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "common/common_types.h" | ||
| 7 | |||
| 8 | namespace Network { | ||
| 9 | |||
| 10 | /// Address families | ||
| 11 | enum class Domain : u8 { | ||
| 12 | INET, ///< Address family for IPv4 | ||
| 13 | }; | ||
| 14 | |||
| 15 | /// Socket types | ||
| 16 | enum class Type { | ||
| 17 | STREAM, | ||
| 18 | DGRAM, | ||
| 19 | RAW, | ||
| 20 | SEQPACKET, | ||
| 21 | }; | ||
| 22 | |||
| 23 | /// Protocol values for sockets | ||
| 24 | enum class Protocol : u8 { | ||
| 25 | ICMP, | ||
| 26 | TCP, | ||
| 27 | UDP, | ||
| 28 | }; | ||
| 29 | |||
| 30 | /// Shutdown mode | ||
| 31 | enum class ShutdownHow { | ||
| 32 | RD, | ||
| 33 | WR, | ||
| 34 | RDWR, | ||
| 35 | }; | ||
| 36 | |||
| 37 | /// Array of IPv4 address | ||
| 38 | using IPv4Address = std::array<u8, 4>; | ||
| 39 | |||
| 40 | /// Cross-platform sockaddr structure | ||
| 41 | struct SockAddrIn { | ||
| 42 | Domain family; | ||
| 43 | IPv4Address ip; | ||
| 44 | u16 portno; | ||
| 45 | }; | ||
| 46 | |||
| 47 | constexpr u32 FLAG_MSG_PEEK = 0x2; | ||
| 48 | constexpr u32 FLAG_MSG_DONTWAIT = 0x80; | ||
| 49 | constexpr u32 FLAG_O_NONBLOCK = 0x800; | ||
| 50 | |||
| 51 | } // namespace Network | ||
diff --git a/src/common/spin_lock.cpp b/src/common/spin_lock.cpp index c1524220f..b2ef4ea1d 100644 --- a/src/common/spin_lock.cpp +++ b/src/common/spin_lock.cpp | |||
| @@ -1,6 +1,5 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | // Refer to the license.txt file included. | ||
| 4 | 3 | ||
| 5 | #include "common/spin_lock.h" | 4 | #include "common/spin_lock.h" |
| 6 | 5 | ||
diff --git a/src/common/spin_lock.h b/src/common/spin_lock.h index 06ac2f5bb..a83274851 100644 --- a/src/common/spin_lock.h +++ b/src/common/spin_lock.h | |||
| @@ -1,6 +1,5 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | // Refer to the license.txt file included. | ||
| 4 | 3 | ||
| 5 | #pragma once | 4 | #pragma once |
| 6 | 5 | ||
diff --git a/src/common/stream.cpp b/src/common/stream.cpp index bf0496c26..80ddd68c8 100644 --- a/src/common/stream.cpp +++ b/src/common/stream.cpp | |||
| @@ -1,6 +1,5 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | // Refer to the license.txt file included. | ||
| 4 | 3 | ||
| 5 | #include <stdexcept> | 4 | #include <stdexcept> |
| 6 | #include "common/common_types.h" | 5 | #include "common/common_types.h" |
diff --git a/src/common/stream.h b/src/common/stream.h index 0e40692de..5bb26e883 100644 --- a/src/common/stream.h +++ b/src/common/stream.h | |||
| @@ -1,6 +1,5 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | // Refer to the license.txt file included. | ||
| 4 | 3 | ||
| 5 | #pragma once | 4 | #pragma once |
| 6 | 5 | ||
diff --git a/src/common/string_util.cpp b/src/common/string_util.cpp index 662171138..7a495bc79 100644 --- a/src/common/string_util.cpp +++ b/src/common/string_util.cpp | |||
| @@ -1,15 +1,13 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project | 1 | // SPDX-FileCopyrightText: 2013 Dolphin Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-FileCopyrightText: 2014 Citra Emulator Project |
| 3 | // Refer to the license.txt file included. | 3 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 4 | 4 | ||
| 5 | #include <algorithm> | 5 | #include <algorithm> |
| 6 | #include <cctype> | 6 | #include <cctype> |
| 7 | #include <codecvt> | 7 | #include <codecvt> |
| 8 | #include <cstdlib> | ||
| 9 | #include <locale> | 8 | #include <locale> |
| 10 | #include <sstream> | 9 | #include <sstream> |
| 11 | 10 | ||
| 12 | #include "common/logging/log.h" | ||
| 13 | #include "common/string_util.h" | 11 | #include "common/string_util.h" |
| 14 | 12 | ||
| 15 | #ifdef _WIN32 | 13 | #ifdef _WIN32 |
| @@ -180,6 +178,10 @@ std::wstring UTF8ToUTF16W(const std::string& input) { | |||
| 180 | 178 | ||
| 181 | #endif | 179 | #endif |
| 182 | 180 | ||
| 181 | std::u16string U16StringFromBuffer(const u16* input, std::size_t length) { | ||
| 182 | return std::u16string(reinterpret_cast<const char16_t*>(input), length); | ||
| 183 | } | ||
| 184 | |||
| 183 | std::string StringFromFixedZeroTerminatedBuffer(std::string_view buffer, std::size_t max_len) { | 185 | std::string StringFromFixedZeroTerminatedBuffer(std::string_view buffer, std::size_t max_len) { |
| 184 | std::size_t len = 0; | 186 | std::size_t len = 0; |
| 185 | while (len < buffer.length() && len < max_len && buffer[len] != '\0') { | 187 | while (len < buffer.length() && len < max_len && buffer[len] != '\0') { |
diff --git a/src/common/string_util.h b/src/common/string_util.h index f0dd632ee..ce18a33cf 100644 --- a/src/common/string_util.h +++ b/src/common/string_util.h | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project | 1 | // SPDX-FileCopyrightText: 2013 Dolphin Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-FileCopyrightText: 2014 Citra Emulator Project |
| 3 | // Refer to the license.txt file included. | 3 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| @@ -44,6 +44,8 @@ bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _ | |||
| 44 | 44 | ||
| 45 | #endif | 45 | #endif |
| 46 | 46 | ||
| 47 | [[nodiscard]] std::u16string U16StringFromBuffer(const u16* input, std::size_t length); | ||
| 48 | |||
| 47 | /** | 49 | /** |
| 48 | * Compares the string defined by the range [`begin`, `end`) to the null-terminated C-string | 50 | * Compares the string defined by the range [`begin`, `end`) to the null-terminated C-string |
| 49 | * `other` for equality. | 51 | * `other` for equality. |
diff --git a/src/common/swap.h b/src/common/swap.h index a80e191dc..037b82781 100644 --- a/src/common/swap.h +++ b/src/common/swap.h | |||
| @@ -1,16 +1,6 @@ | |||
| 1 | // Copyright (c) 2012- PPSSPP Project / Dolphin Project. | 1 | // SPDX-FileCopyrightText: 2012 PPSSPP Project |
| 2 | 2 | // SPDX-FileCopyrightText: 2012 Dolphin Emulator Project | |
| 3 | // This program is free software: you can redistribute it and/or modify | 3 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 4 | // it under the terms of the GNU General Public License as published by | ||
| 5 | // the Free Software Foundation, version 2.0 or later versions. | ||
| 6 | |||
| 7 | // This program is distributed in the hope that it will be useful, | ||
| 8 | // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 9 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 10 | // GNU General Public License 2.0 for more details. | ||
| 11 | |||
| 12 | // A copy of the GPL 2.0 should have been included with the program. | ||
| 13 | // If not, see http://www.gnu.org/licenses/ | ||
| 14 | 4 | ||
| 15 | // Official git repository and contact information can be found at | 5 | // Official git repository and contact information can be found at |
| 16 | // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. | 6 | // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. |
diff --git a/src/common/telemetry.cpp b/src/common/telemetry.cpp index 6241d08b3..d26394359 100644 --- a/src/common/telemetry.cpp +++ b/src/common/telemetry.cpp | |||
| @@ -1,10 +1,8 @@ | |||
| 1 | // Copyright 2017 Citra Emulator Project | 1 | // SPDX-FileCopyrightText: 2017 Citra Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | // Refer to the license.txt file included. | ||
| 4 | 3 | ||
| 5 | #include <algorithm> | 4 | #include <algorithm> |
| 6 | #include <cstring> | 5 | #include <cstring> |
| 7 | #include "common/assert.h" | ||
| 8 | #include "common/scm_rev.h" | 6 | #include "common/scm_rev.h" |
| 9 | #include "common/telemetry.h" | 7 | #include "common/telemetry.h" |
| 10 | 8 | ||
| @@ -55,22 +53,50 @@ void AppendBuildInfo(FieldCollection& fc) { | |||
| 55 | 53 | ||
| 56 | void AppendCPUInfo(FieldCollection& fc) { | 54 | void AppendCPUInfo(FieldCollection& fc) { |
| 57 | #ifdef ARCHITECTURE_x86_64 | 55 | #ifdef ARCHITECTURE_x86_64 |
| 58 | fc.AddField(FieldType::UserSystem, "CPU_Model", Common::GetCPUCaps().cpu_string); | 56 | |
| 59 | fc.AddField(FieldType::UserSystem, "CPU_BrandString", Common::GetCPUCaps().brand_string); | 57 | const auto& caps = Common::GetCPUCaps(); |
| 60 | fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_AES", Common::GetCPUCaps().aes); | 58 | const auto add_field = [&fc](std::string_view field_name, const auto& field_value) { |
| 61 | fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_AVX", Common::GetCPUCaps().avx); | 59 | fc.AddField(FieldType::UserSystem, field_name, field_value); |
| 62 | fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_AVX2", Common::GetCPUCaps().avx2); | 60 | }; |
| 63 | fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_AVX512", Common::GetCPUCaps().avx512); | 61 | add_field("CPU_Model", caps.cpu_string); |
| 64 | fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_BMI1", Common::GetCPUCaps().bmi1); | 62 | add_field("CPU_BrandString", caps.brand_string); |
| 65 | fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_BMI2", Common::GetCPUCaps().bmi2); | 63 | |
| 66 | fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_FMA", Common::GetCPUCaps().fma); | 64 | add_field("CPU_Extension_x64_SSE", caps.sse); |
| 67 | fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_FMA4", Common::GetCPUCaps().fma4); | 65 | add_field("CPU_Extension_x64_SSE2", caps.sse2); |
| 68 | fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_SSE", Common::GetCPUCaps().sse); | 66 | add_field("CPU_Extension_x64_SSE3", caps.sse3); |
| 69 | fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_SSE2", Common::GetCPUCaps().sse2); | 67 | add_field("CPU_Extension_x64_SSSE3", caps.ssse3); |
| 70 | fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_SSE3", Common::GetCPUCaps().sse3); | 68 | add_field("CPU_Extension_x64_SSE41", caps.sse4_1); |
| 71 | fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_SSSE3", Common::GetCPUCaps().ssse3); | 69 | add_field("CPU_Extension_x64_SSE42", caps.sse4_2); |
| 72 | fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_SSE41", Common::GetCPUCaps().sse4_1); | 70 | |
| 73 | fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_SSE42", Common::GetCPUCaps().sse4_2); | 71 | add_field("CPU_Extension_x64_AVX", caps.avx); |
| 72 | add_field("CPU_Extension_x64_AVX_VNNI", caps.avx_vnni); | ||
| 73 | add_field("CPU_Extension_x64_AVX2", caps.avx2); | ||
| 74 | |||
| 75 | // Skylake-X/SP level AVX512, for compatibility with the previous telemetry field | ||
| 76 | add_field("CPU_Extension_x64_AVX512", | ||
| 77 | caps.avx512f && caps.avx512cd && caps.avx512vl && caps.avx512dq && caps.avx512bw); | ||
| 78 | |||
| 79 | add_field("CPU_Extension_x64_AVX512F", caps.avx512f); | ||
| 80 | add_field("CPU_Extension_x64_AVX512CD", caps.avx512cd); | ||
| 81 | add_field("CPU_Extension_x64_AVX512VL", caps.avx512vl); | ||
| 82 | add_field("CPU_Extension_x64_AVX512DQ", caps.avx512dq); | ||
| 83 | add_field("CPU_Extension_x64_AVX512BW", caps.avx512bw); | ||
| 84 | add_field("CPU_Extension_x64_AVX512BITALG", caps.avx512bitalg); | ||
| 85 | add_field("CPU_Extension_x64_AVX512VBMI", caps.avx512vbmi); | ||
| 86 | |||
| 87 | add_field("CPU_Extension_x64_AES", caps.aes); | ||
| 88 | add_field("CPU_Extension_x64_BMI1", caps.bmi1); | ||
| 89 | add_field("CPU_Extension_x64_BMI2", caps.bmi2); | ||
| 90 | add_field("CPU_Extension_x64_F16C", caps.f16c); | ||
| 91 | add_field("CPU_Extension_x64_FMA", caps.fma); | ||
| 92 | add_field("CPU_Extension_x64_FMA4", caps.fma4); | ||
| 93 | add_field("CPU_Extension_x64_GFNI", caps.gfni); | ||
| 94 | add_field("CPU_Extension_x64_INVARIANT_TSC", caps.invariant_tsc); | ||
| 95 | add_field("CPU_Extension_x64_LZCNT", caps.lzcnt); | ||
| 96 | add_field("CPU_Extension_x64_MOVBE", caps.movbe); | ||
| 97 | add_field("CPU_Extension_x64_PCLMULQDQ", caps.pclmulqdq); | ||
| 98 | add_field("CPU_Extension_x64_POPCNT", caps.popcnt); | ||
| 99 | add_field("CPU_Extension_x64_SHA", caps.sha); | ||
| 74 | #else | 100 | #else |
| 75 | fc.AddField(FieldType::UserSystem, "CPU_Model", "Other"); | 101 | fc.AddField(FieldType::UserSystem, "CPU_Model", "Other"); |
| 76 | #endif | 102 | #endif |
diff --git a/src/common/telemetry.h b/src/common/telemetry.h index d38aeac99..ba633d5a5 100644 --- a/src/common/telemetry.h +++ b/src/common/telemetry.h | |||
| @@ -1,6 +1,5 @@ | |||
| 1 | // Copyright 2017 Citra Emulator Project | 1 | // SPDX-FileCopyrightText: 2017 Citra Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | // Refer to the license.txt file included. | ||
| 4 | 3 | ||
| 5 | #pragma once | 4 | #pragma once |
| 6 | 5 | ||
| @@ -55,8 +54,8 @@ class Field : public FieldInterface { | |||
| 55 | public: | 54 | public: |
| 56 | YUZU_NON_COPYABLE(Field); | 55 | YUZU_NON_COPYABLE(Field); |
| 57 | 56 | ||
| 58 | Field(FieldType type_, std::string name_, T value_) | 57 | Field(FieldType type_, std::string_view name_, T value_) |
| 59 | : name(std::move(name_)), type(type_), value(std::move(value_)) {} | 58 | : name(name_), type(type_), value(std::move(value_)) {} |
| 60 | 59 | ||
| 61 | ~Field() override = default; | 60 | ~Field() override = default; |
| 62 | 61 | ||
| @@ -123,7 +122,7 @@ public: | |||
| 123 | * @param value Value for the field to add. | 122 | * @param value Value for the field to add. |
| 124 | */ | 123 | */ |
| 125 | template <typename T> | 124 | template <typename T> |
| 126 | void AddField(FieldType type, const char* name, T value) { | 125 | void AddField(FieldType type, std::string_view name, T value) { |
| 127 | return AddField(std::make_unique<Field<T>>(type, name, std::move(value))); | 126 | return AddField(std::make_unique<Field<T>>(type, name, std::move(value))); |
| 128 | } | 127 | } |
| 129 | 128 | ||
| @@ -171,6 +170,9 @@ struct VisitorInterface { | |||
| 171 | struct NullVisitor final : public VisitorInterface { | 170 | struct NullVisitor final : public VisitorInterface { |
| 172 | YUZU_NON_COPYABLE(NullVisitor); | 171 | YUZU_NON_COPYABLE(NullVisitor); |
| 173 | 172 | ||
| 173 | NullVisitor() = default; | ||
| 174 | ~NullVisitor() override = default; | ||
| 175 | |||
| 174 | void Visit(const Field<bool>& /*field*/) override {} | 176 | void Visit(const Field<bool>& /*field*/) override {} |
| 175 | void Visit(const Field<double>& /*field*/) override {} | 177 | void Visit(const Field<double>& /*field*/) override {} |
| 176 | void Visit(const Field<float>& /*field*/) override {} | 178 | void Visit(const Field<float>& /*field*/) override {} |
diff --git a/src/common/thread.cpp b/src/common/thread.cpp index 946a1114d..919e33af9 100644 --- a/src/common/thread.cpp +++ b/src/common/thread.cpp | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project | 1 | // SPDX-FileCopyrightText: 2013 Dolphin Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-FileCopyrightText: 2014 Citra Emulator Project |
| 3 | // Refer to the license.txt file included. | 3 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 4 | 4 | ||
| 5 | #include <string> | 5 | #include <string> |
| 6 | 6 | ||
| @@ -47,6 +47,9 @@ void SetCurrentThreadPriority(ThreadPriority new_priority) { | |||
| 47 | case ThreadPriority::VeryHigh: | 47 | case ThreadPriority::VeryHigh: |
| 48 | windows_priority = THREAD_PRIORITY_HIGHEST; | 48 | windows_priority = THREAD_PRIORITY_HIGHEST; |
| 49 | break; | 49 | break; |
| 50 | case ThreadPriority::Critical: | ||
| 51 | windows_priority = THREAD_PRIORITY_TIME_CRITICAL; | ||
| 52 | break; | ||
| 50 | default: | 53 | default: |
| 51 | windows_priority = THREAD_PRIORITY_NORMAL; | 54 | windows_priority = THREAD_PRIORITY_NORMAL; |
| 52 | break; | 55 | break; |
| @@ -59,9 +62,10 @@ void SetCurrentThreadPriority(ThreadPriority new_priority) { | |||
| 59 | void SetCurrentThreadPriority(ThreadPriority new_priority) { | 62 | void SetCurrentThreadPriority(ThreadPriority new_priority) { |
| 60 | pthread_t this_thread = pthread_self(); | 63 | pthread_t this_thread = pthread_self(); |
| 61 | 64 | ||
| 62 | s32 max_prio = sched_get_priority_max(SCHED_OTHER); | 65 | const auto scheduling_type = SCHED_OTHER; |
| 63 | s32 min_prio = sched_get_priority_min(SCHED_OTHER); | 66 | s32 max_prio = sched_get_priority_max(scheduling_type); |
| 64 | u32 level = static_cast<u32>(new_priority) + 1; | 67 | s32 min_prio = sched_get_priority_min(scheduling_type); |
| 68 | u32 level = std::max(static_cast<u32>(new_priority) + 1, 4U); | ||
| 65 | 69 | ||
| 66 | struct sched_param params; | 70 | struct sched_param params; |
| 67 | if (max_prio > min_prio) { | 71 | if (max_prio > min_prio) { |
| @@ -70,7 +74,7 @@ void SetCurrentThreadPriority(ThreadPriority new_priority) { | |||
| 70 | params.sched_priority = min_prio - ((min_prio - max_prio) * level) / 4; | 74 | params.sched_priority = min_prio - ((min_prio - max_prio) * level) / 4; |
| 71 | } | 75 | } |
| 72 | 76 | ||
| 73 | pthread_setschedparam(this_thread, SCHED_OTHER, ¶ms); | 77 | pthread_setschedparam(this_thread, scheduling_type, ¶ms); |
| 74 | } | 78 | } |
| 75 | 79 | ||
| 76 | #endif | 80 | #endif |
diff --git a/src/common/thread.h b/src/common/thread.h index a8c17c71a..e17a7850f 100644 --- a/src/common/thread.h +++ b/src/common/thread.h | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project | 1 | // SPDX-FileCopyrightText: 2013 Dolphin Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-FileCopyrightText: 2014 Citra Emulator Project |
| 3 | // Refer to the license.txt file included. | 3 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| @@ -17,7 +17,7 @@ namespace Common { | |||
| 17 | class Event { | 17 | class Event { |
| 18 | public: | 18 | public: |
| 19 | void Set() { | 19 | void Set() { |
| 20 | std::lock_guard lk{mutex}; | 20 | std::scoped_lock lk{mutex}; |
| 21 | if (!is_set) { | 21 | if (!is_set) { |
| 22 | is_set = true; | 22 | is_set = true; |
| 23 | condvar.notify_one(); | 23 | condvar.notify_one(); |
| @@ -54,6 +54,10 @@ public: | |||
| 54 | is_set = false; | 54 | is_set = false; |
| 55 | } | 55 | } |
| 56 | 56 | ||
| 57 | [[nodiscard]] bool IsSet() { | ||
| 58 | return is_set; | ||
| 59 | } | ||
| 60 | |||
| 57 | private: | 61 | private: |
| 58 | std::condition_variable condvar; | 62 | std::condition_variable condvar; |
| 59 | std::mutex mutex; | 63 | std::mutex mutex; |
| @@ -92,6 +96,7 @@ enum class ThreadPriority : u32 { | |||
| 92 | Normal = 1, | 96 | Normal = 1, |
| 93 | High = 2, | 97 | High = 2, |
| 94 | VeryHigh = 3, | 98 | VeryHigh = 3, |
| 99 | Critical = 4, | ||
| 95 | }; | 100 | }; |
| 96 | 101 | ||
| 97 | void SetCurrentThreadPriority(ThreadPriority new_priority); | 102 | void SetCurrentThreadPriority(ThreadPriority new_priority); |
diff --git a/src/common/thread_queue_list.h b/src/common/thread_queue_list.h index def9e5d8d..ce48cec92 100644 --- a/src/common/thread_queue_list.h +++ b/src/common/thread_queue_list.h | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project / PPSSPP Project | 1 | // SPDX-FileCopyrightText: 2012 PPSSPP Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-FileCopyrightText: 2014 Dolphin Emulator Project |
| 3 | // Refer to the license.txt file included. | 3 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
diff --git a/src/common/thread_worker.h b/src/common/thread_worker.h index cd0017726..62c60f724 100644 --- a/src/common/thread_worker.h +++ b/src/common/thread_worker.h | |||
| @@ -1,6 +1,5 @@ | |||
| 1 | // Copyright 2020 yuzu emulator team | 1 | // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | // Refer to the license.txt file included. | ||
| 4 | 3 | ||
| 5 | #pragma once | 4 | #pragma once |
| 6 | 5 | ||
diff --git a/src/common/threadsafe_queue.h b/src/common/threadsafe_queue.h index 2c8c2b90e..053798e79 100644 --- a/src/common/threadsafe_queue.h +++ b/src/common/threadsafe_queue.h | |||
| @@ -1,6 +1,5 @@ | |||
| 1 | // Copyright 2010 Dolphin Emulator Project | 1 | // SPDX-FileCopyrightText: 2010 Dolphin Emulator Project |
| 2 | // Licensed under GPLv2+ | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | // Refer to the license.txt file included. | ||
| 4 | 3 | ||
| 5 | #pragma once | 4 | #pragma once |
| 6 | 5 | ||
| @@ -40,7 +39,7 @@ public: | |||
| 40 | template <typename Arg> | 39 | template <typename Arg> |
| 41 | void Push(Arg&& t) { | 40 | void Push(Arg&& t) { |
| 42 | // create the element, add it to the queue | 41 | // create the element, add it to the queue |
| 43 | write_ptr->current = std::forward<Arg>(t); | 42 | write_ptr->current = std::move(t); |
| 44 | // set the next pointer to a new element ptr | 43 | // set the next pointer to a new element ptr |
| 45 | // then advance the write pointer | 44 | // then advance the write pointer |
| 46 | ElementPtr* new_ptr = new ElementPtr(); | 45 | ElementPtr* new_ptr = new ElementPtr(); |
| @@ -52,7 +51,7 @@ public: | |||
| 52 | // line before cv.wait | 51 | // line before cv.wait |
| 53 | // TODO(bunnei): This can be replaced with C++20 waitable atomics when properly supported. | 52 | // TODO(bunnei): This can be replaced with C++20 waitable atomics when properly supported. |
| 54 | // See discussion on https://github.com/yuzu-emu/yuzu/pull/3173 for details. | 53 | // See discussion on https://github.com/yuzu-emu/yuzu/pull/3173 for details. |
| 55 | std::lock_guard lock{cv_mutex}; | 54 | std::scoped_lock lock{cv_mutex}; |
| 56 | cv.notify_one(); | 55 | cv.notify_one(); |
| 57 | } | 56 | } |
| 58 | 57 | ||
| @@ -159,7 +158,7 @@ public: | |||
| 159 | 158 | ||
| 160 | template <typename Arg> | 159 | template <typename Arg> |
| 161 | void Push(Arg&& t) { | 160 | void Push(Arg&& t) { |
| 162 | std::lock_guard lock{write_lock}; | 161 | std::scoped_lock lock{write_lock}; |
| 163 | spsc_queue.Push(t); | 162 | spsc_queue.Push(t); |
| 164 | } | 163 | } |
| 165 | 164 | ||
diff --git a/src/common/time_zone.cpp b/src/common/time_zone.cpp index ce239eb63..126836b01 100644 --- a/src/common/time_zone.cpp +++ b/src/common/time_zone.cpp | |||
| @@ -1,6 +1,5 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | // Refer to the license.txt file included. | ||
| 4 | 3 | ||
| 5 | #include <chrono> | 4 | #include <chrono> |
| 6 | #include <iomanip> | 5 | #include <iomanip> |
diff --git a/src/common/time_zone.h b/src/common/time_zone.h index 9f5939ca5..99cae6ef2 100644 --- a/src/common/time_zone.h +++ b/src/common/time_zone.h | |||
| @@ -1,6 +1,5 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | // Refer to the license.txt file included. | ||
| 4 | 3 | ||
| 5 | #pragma once | 4 | #pragma once |
| 6 | 5 | ||
diff --git a/src/common/tiny_mt.h b/src/common/tiny_mt.h index 19ae5b7d6..5d5ebf158 100644 --- a/src/common/tiny_mt.h +++ b/src/common/tiny_mt.h | |||
| @@ -1,6 +1,5 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | // Refer to the license.txt file included. | ||
| 4 | 3 | ||
| 5 | #pragma once | 4 | #pragma once |
| 6 | 5 | ||
diff --git a/src/common/tree.h b/src/common/tree.h index 18faa4a48..f77859209 100644 --- a/src/common/tree.h +++ b/src/common/tree.h | |||
| @@ -1,32 +1,10 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2002 Niels Provos <provos@citi.umich.edu> | ||
| 2 | // SPDX-License-Identifier: BSD-2-Clause | ||
| 3 | |||
| 1 | /* $NetBSD: tree.h,v 1.8 2004/03/28 19:38:30 provos Exp $ */ | 4 | /* $NetBSD: tree.h,v 1.8 2004/03/28 19:38:30 provos Exp $ */ |
| 2 | /* $OpenBSD: tree.h,v 1.7 2002/10/17 21:51:54 art Exp $ */ | 5 | /* $OpenBSD: tree.h,v 1.7 2002/10/17 21:51:54 art Exp $ */ |
| 3 | /* $FreeBSD$ */ | 6 | /* $FreeBSD$ */ |
| 4 | 7 | ||
| 5 | /*- | ||
| 6 | * Copyright 2002 Niels Provos <provos@citi.umich.edu> | ||
| 7 | * All rights reserved. | ||
| 8 | * | ||
| 9 | * Redistribution and use in source and binary forms, with or without | ||
| 10 | * modification, are permitted provided that the following conditions | ||
| 11 | * are met: | ||
| 12 | * 1. Redistributions of source code must retain the above copyright | ||
| 13 | * notice, this list of conditions and the following disclaimer. | ||
| 14 | * 2. Redistributions in binary form must reproduce the above copyright | ||
| 15 | * notice, this list of conditions and the following disclaimer in the | ||
| 16 | * documentation and/or other materials provided with the distribution. | ||
| 17 | * | ||
| 18 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | ||
| 19 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | ||
| 20 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | ||
| 21 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | ||
| 22 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | ||
| 23 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
| 24 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
| 25 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
| 26 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | ||
| 27 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| 28 | */ | ||
| 29 | |||
| 30 | #pragma once | 8 | #pragma once |
| 31 | 9 | ||
| 32 | /* | 10 | /* |
| @@ -43,294 +21,265 @@ | |||
| 43 | * The maximum height of a red-black tree is 2lg (n+1). | 21 | * The maximum height of a red-black tree is 2lg (n+1). |
| 44 | */ | 22 | */ |
| 45 | 23 | ||
| 46 | #include "common/assert.h" | 24 | namespace Common::freebsd { |
| 25 | |||
| 26 | enum class RBColor { | ||
| 27 | RB_BLACK = 0, | ||
| 28 | RB_RED = 1, | ||
| 29 | }; | ||
| 47 | 30 | ||
| 48 | namespace Common { | 31 | #pragma pack(push, 4) |
| 49 | template <typename T> | 32 | template <typename T> |
| 50 | class RBHead { | 33 | class RBEntry { |
| 51 | public: | 34 | public: |
| 52 | [[nodiscard]] T* Root() { | 35 | constexpr RBEntry() = default; |
| 53 | return rbh_root; | ||
| 54 | } | ||
| 55 | 36 | ||
| 56 | [[nodiscard]] const T* Root() const { | 37 | [[nodiscard]] constexpr T* Left() { |
| 57 | return rbh_root; | 38 | return m_rbe_left; |
| 58 | } | 39 | } |
| 59 | 40 | [[nodiscard]] constexpr const T* Left() const { | |
| 60 | void SetRoot(T* root) { | 41 | return m_rbe_left; |
| 61 | rbh_root = root; | ||
| 62 | } | 42 | } |
| 63 | 43 | ||
| 64 | [[nodiscard]] bool IsEmpty() const { | 44 | constexpr void SetLeft(T* e) { |
| 65 | return Root() == nullptr; | 45 | m_rbe_left = e; |
| 66 | } | 46 | } |
| 67 | 47 | ||
| 68 | private: | 48 | [[nodiscard]] constexpr T* Right() { |
| 69 | T* rbh_root = nullptr; | 49 | return m_rbe_right; |
| 70 | }; | ||
| 71 | |||
| 72 | enum class EntryColor { | ||
| 73 | Black, | ||
| 74 | Red, | ||
| 75 | }; | ||
| 76 | |||
| 77 | template <typename T> | ||
| 78 | class RBEntry { | ||
| 79 | public: | ||
| 80 | [[nodiscard]] T* Left() { | ||
| 81 | return rbe_left; | ||
| 82 | } | 50 | } |
| 83 | 51 | [[nodiscard]] constexpr const T* Right() const { | |
| 84 | [[nodiscard]] const T* Left() const { | 52 | return m_rbe_right; |
| 85 | return rbe_left; | ||
| 86 | } | 53 | } |
| 87 | 54 | ||
| 88 | void SetLeft(T* left) { | 55 | constexpr void SetRight(T* e) { |
| 89 | rbe_left = left; | 56 | m_rbe_right = e; |
| 90 | } | 57 | } |
| 91 | 58 | ||
| 92 | [[nodiscard]] T* Right() { | 59 | [[nodiscard]] constexpr T* Parent() { |
| 93 | return rbe_right; | 60 | return m_rbe_parent; |
| 94 | } | 61 | } |
| 95 | 62 | [[nodiscard]] constexpr const T* Parent() const { | |
| 96 | [[nodiscard]] const T* Right() const { | 63 | return m_rbe_parent; |
| 97 | return rbe_right; | ||
| 98 | } | 64 | } |
| 99 | 65 | ||
| 100 | void SetRight(T* right) { | 66 | constexpr void SetParent(T* e) { |
| 101 | rbe_right = right; | 67 | m_rbe_parent = e; |
| 102 | } | 68 | } |
| 103 | 69 | ||
| 104 | [[nodiscard]] T* Parent() { | 70 | [[nodiscard]] constexpr bool IsBlack() const { |
| 105 | return rbe_parent; | 71 | return m_rbe_color == RBColor::RB_BLACK; |
| 106 | } | 72 | } |
| 107 | 73 | [[nodiscard]] constexpr bool IsRed() const { | |
| 108 | [[nodiscard]] const T* Parent() const { | 74 | return m_rbe_color == RBColor::RB_RED; |
| 109 | return rbe_parent; | ||
| 110 | } | 75 | } |
| 111 | 76 | [[nodiscard]] constexpr RBColor Color() const { | |
| 112 | void SetParent(T* parent) { | 77 | return m_rbe_color; |
| 113 | rbe_parent = parent; | ||
| 114 | } | 78 | } |
| 115 | 79 | ||
| 116 | [[nodiscard]] bool IsBlack() const { | 80 | constexpr void SetColor(RBColor c) { |
| 117 | return rbe_color == EntryColor::Black; | 81 | m_rbe_color = c; |
| 118 | } | 82 | } |
| 119 | 83 | ||
| 120 | [[nodiscard]] bool IsRed() const { | 84 | private: |
| 121 | return rbe_color == EntryColor::Red; | 85 | T* m_rbe_left{}; |
| 122 | } | 86 | T* m_rbe_right{}; |
| 87 | T* m_rbe_parent{}; | ||
| 88 | RBColor m_rbe_color{RBColor::RB_BLACK}; | ||
| 89 | }; | ||
| 90 | #pragma pack(pop) | ||
| 123 | 91 | ||
| 124 | [[nodiscard]] EntryColor Color() const { | 92 | template <typename T> |
| 125 | return rbe_color; | 93 | struct CheckRBEntry { |
| 126 | } | 94 | static constexpr bool value = false; |
| 95 | }; | ||
| 96 | template <typename T> | ||
| 97 | struct CheckRBEntry<RBEntry<T>> { | ||
| 98 | static constexpr bool value = true; | ||
| 99 | }; | ||
| 127 | 100 | ||
| 128 | void SetColor(EntryColor color) { | 101 | template <typename T> |
| 129 | rbe_color = color; | 102 | concept IsRBEntry = CheckRBEntry<T>::value; |
| 130 | } | 103 | |
| 104 | template <typename T> | ||
| 105 | concept HasRBEntry = requires(T& t, const T& ct) { | ||
| 106 | { t.GetRBEntry() } -> std::same_as<RBEntry<T>&>; | ||
| 107 | { ct.GetRBEntry() } -> std::same_as<const RBEntry<T>&>; | ||
| 108 | }; | ||
| 131 | 109 | ||
| 110 | template <typename T> | ||
| 111 | requires HasRBEntry<T> | ||
| 112 | class RBHead { | ||
| 132 | private: | 113 | private: |
| 133 | T* rbe_left = nullptr; | 114 | T* m_rbh_root = nullptr; |
| 134 | T* rbe_right = nullptr; | 115 | |
| 135 | T* rbe_parent = nullptr; | 116 | public: |
| 136 | EntryColor rbe_color{}; | 117 | [[nodiscard]] constexpr T* Root() { |
| 118 | return m_rbh_root; | ||
| 119 | } | ||
| 120 | [[nodiscard]] constexpr const T* Root() const { | ||
| 121 | return m_rbh_root; | ||
| 122 | } | ||
| 123 | constexpr void SetRoot(T* root) { | ||
| 124 | m_rbh_root = root; | ||
| 125 | } | ||
| 126 | |||
| 127 | [[nodiscard]] constexpr bool IsEmpty() const { | ||
| 128 | return this->Root() == nullptr; | ||
| 129 | } | ||
| 137 | }; | 130 | }; |
| 138 | 131 | ||
| 139 | template <typename Node> | 132 | template <typename T> |
| 140 | [[nodiscard]] RBEntry<Node>& RB_ENTRY(Node* node) { | 133 | requires HasRBEntry<T> |
| 141 | return node->GetEntry(); | 134 | [[nodiscard]] constexpr RBEntry<T>& RB_ENTRY(T* t) { |
| 135 | return t->GetRBEntry(); | ||
| 142 | } | 136 | } |
| 143 | 137 | template <typename T> | |
| 144 | template <typename Node> | 138 | requires HasRBEntry<T> |
| 145 | [[nodiscard]] const RBEntry<Node>& RB_ENTRY(const Node* node) { | 139 | [[nodiscard]] constexpr const RBEntry<T>& RB_ENTRY(const T* t) { |
| 146 | return node->GetEntry(); | 140 | return t->GetRBEntry(); |
| 147 | } | 141 | } |
| 148 | 142 | ||
| 149 | template <typename Node> | 143 | template <typename T> |
| 150 | [[nodiscard]] Node* RB_PARENT(Node* node) { | 144 | requires HasRBEntry<T> |
| 151 | return RB_ENTRY(node).Parent(); | 145 | [[nodiscard]] constexpr T* RB_LEFT(T* t) { |
| 146 | return RB_ENTRY(t).Left(); | ||
| 152 | } | 147 | } |
| 153 | 148 | template <typename T> | |
| 154 | template <typename Node> | 149 | requires HasRBEntry<T> |
| 155 | [[nodiscard]] const Node* RB_PARENT(const Node* node) { | 150 | [[nodiscard]] constexpr const T* RB_LEFT(const T* t) { |
| 156 | return RB_ENTRY(node).Parent(); | 151 | return RB_ENTRY(t).Left(); |
| 157 | } | 152 | } |
| 158 | 153 | ||
| 159 | template <typename Node> | 154 | template <typename T> |
| 160 | void RB_SET_PARENT(Node* node, Node* parent) { | 155 | requires HasRBEntry<T> |
| 161 | return RB_ENTRY(node).SetParent(parent); | 156 | [[nodiscard]] constexpr T* RB_RIGHT(T* t) { |
| 157 | return RB_ENTRY(t).Right(); | ||
| 162 | } | 158 | } |
| 163 | 159 | template <typename T> | |
| 164 | template <typename Node> | 160 | requires HasRBEntry<T> |
| 165 | [[nodiscard]] Node* RB_LEFT(Node* node) { | 161 | [[nodiscard]] constexpr const T* RB_RIGHT(const T* t) { |
| 166 | return RB_ENTRY(node).Left(); | 162 | return RB_ENTRY(t).Right(); |
| 167 | } | 163 | } |
| 168 | 164 | ||
| 169 | template <typename Node> | 165 | template <typename T> |
| 170 | [[nodiscard]] const Node* RB_LEFT(const Node* node) { | 166 | requires HasRBEntry<T> |
| 171 | return RB_ENTRY(node).Left(); | 167 | [[nodiscard]] constexpr T* RB_PARENT(T* t) { |
| 168 | return RB_ENTRY(t).Parent(); | ||
| 172 | } | 169 | } |
| 173 | 170 | template <typename T> | |
| 174 | template <typename Node> | 171 | requires HasRBEntry<T> |
| 175 | void RB_SET_LEFT(Node* node, Node* left) { | 172 | [[nodiscard]] constexpr const T* RB_PARENT(const T* t) { |
| 176 | return RB_ENTRY(node).SetLeft(left); | 173 | return RB_ENTRY(t).Parent(); |
| 177 | } | 174 | } |
| 178 | 175 | ||
| 179 | template <typename Node> | 176 | template <typename T> |
| 180 | [[nodiscard]] Node* RB_RIGHT(Node* node) { | 177 | requires HasRBEntry<T> |
| 181 | return RB_ENTRY(node).Right(); | 178 | constexpr void RB_SET_LEFT(T* t, T* e) { |
| 179 | RB_ENTRY(t).SetLeft(e); | ||
| 182 | } | 180 | } |
| 183 | 181 | template <typename T> | |
| 184 | template <typename Node> | 182 | requires HasRBEntry<T> |
| 185 | [[nodiscard]] const Node* RB_RIGHT(const Node* node) { | 183 | constexpr void RB_SET_RIGHT(T* t, T* e) { |
| 186 | return RB_ENTRY(node).Right(); | 184 | RB_ENTRY(t).SetRight(e); |
| 187 | } | 185 | } |
| 188 | 186 | template <typename T> | |
| 189 | template <typename Node> | 187 | requires HasRBEntry<T> |
| 190 | void RB_SET_RIGHT(Node* node, Node* right) { | 188 | constexpr void RB_SET_PARENT(T* t, T* e) { |
| 191 | return RB_ENTRY(node).SetRight(right); | 189 | RB_ENTRY(t).SetParent(e); |
| 192 | } | 190 | } |
| 193 | 191 | ||
| 194 | template <typename Node> | 192 | template <typename T> |
| 195 | [[nodiscard]] bool RB_IS_BLACK(const Node* node) { | 193 | requires HasRBEntry<T> |
| 196 | return RB_ENTRY(node).IsBlack(); | 194 | [[nodiscard]] constexpr bool RB_IS_BLACK(const T* t) { |
| 195 | return RB_ENTRY(t).IsBlack(); | ||
| 197 | } | 196 | } |
| 198 | 197 | template <typename T> | |
| 199 | template <typename Node> | 198 | requires HasRBEntry<T> |
| 200 | [[nodiscard]] bool RB_IS_RED(const Node* node) { | 199 | [[nodiscard]] constexpr bool RB_IS_RED(const T* t) { |
| 201 | return RB_ENTRY(node).IsRed(); | 200 | return RB_ENTRY(t).IsRed(); |
| 202 | } | 201 | } |
| 203 | 202 | ||
| 204 | template <typename Node> | 203 | template <typename T> |
| 205 | [[nodiscard]] EntryColor RB_COLOR(const Node* node) { | 204 | requires HasRBEntry<T> |
| 206 | return RB_ENTRY(node).Color(); | 205 | [[nodiscard]] constexpr RBColor RB_COLOR(const T* t) { |
| 206 | return RB_ENTRY(t).Color(); | ||
| 207 | } | 207 | } |
| 208 | 208 | ||
| 209 | template <typename Node> | 209 | template <typename T> |
| 210 | void RB_SET_COLOR(Node* node, EntryColor color) { | 210 | requires HasRBEntry<T> |
| 211 | return RB_ENTRY(node).SetColor(color); | 211 | constexpr void RB_SET_COLOR(T* t, RBColor c) { |
| 212 | RB_ENTRY(t).SetColor(c); | ||
| 212 | } | 213 | } |
| 213 | 214 | ||
| 214 | template <typename Node> | 215 | template <typename T> |
| 215 | void RB_SET(Node* node, Node* parent) { | 216 | requires HasRBEntry<T> |
| 216 | auto& entry = RB_ENTRY(node); | 217 | constexpr void RB_SET(T* elm, T* parent) { |
| 217 | entry.SetParent(parent); | 218 | auto& rb_entry = RB_ENTRY(elm); |
| 218 | entry.SetLeft(nullptr); | 219 | rb_entry.SetParent(parent); |
| 219 | entry.SetRight(nullptr); | 220 | rb_entry.SetLeft(nullptr); |
| 220 | entry.SetColor(EntryColor::Red); | 221 | rb_entry.SetRight(nullptr); |
| 222 | rb_entry.SetColor(RBColor::RB_RED); | ||
| 221 | } | 223 | } |
| 222 | 224 | ||
| 223 | template <typename Node> | 225 | template <typename T> |
| 224 | void RB_SET_BLACKRED(Node* black, Node* red) { | 226 | requires HasRBEntry<T> |
| 225 | RB_SET_COLOR(black, EntryColor::Black); | 227 | constexpr void RB_SET_BLACKRED(T* black, T* red) { |
| 226 | RB_SET_COLOR(red, EntryColor::Red); | 228 | RB_SET_COLOR(black, RBColor::RB_BLACK); |
| 229 | RB_SET_COLOR(red, RBColor::RB_RED); | ||
| 227 | } | 230 | } |
| 228 | 231 | ||
| 229 | template <typename Node> | 232 | template <typename T> |
| 230 | void RB_ROTATE_LEFT(RBHead<Node>* head, Node* elm, Node*& tmp) { | 233 | requires HasRBEntry<T> |
| 234 | constexpr void RB_ROTATE_LEFT(RBHead<T>& head, T* elm, T*& tmp) { | ||
| 231 | tmp = RB_RIGHT(elm); | 235 | tmp = RB_RIGHT(elm); |
| 232 | RB_SET_RIGHT(elm, RB_LEFT(tmp)); | 236 | if (RB_SET_RIGHT(elm, RB_LEFT(tmp)); RB_RIGHT(elm) != nullptr) { |
| 233 | if (RB_RIGHT(elm) != nullptr) { | ||
| 234 | RB_SET_PARENT(RB_LEFT(tmp), elm); | 237 | RB_SET_PARENT(RB_LEFT(tmp), elm); |
| 235 | } | 238 | } |
| 236 | 239 | ||
| 237 | RB_SET_PARENT(tmp, RB_PARENT(elm)); | 240 | if (RB_SET_PARENT(tmp, RB_PARENT(elm)); RB_PARENT(tmp) != nullptr) { |
| 238 | if (RB_PARENT(tmp) != nullptr) { | ||
| 239 | if (elm == RB_LEFT(RB_PARENT(elm))) { | 241 | if (elm == RB_LEFT(RB_PARENT(elm))) { |
| 240 | RB_SET_LEFT(RB_PARENT(elm), tmp); | 242 | RB_SET_LEFT(RB_PARENT(elm), tmp); |
| 241 | } else { | 243 | } else { |
| 242 | RB_SET_RIGHT(RB_PARENT(elm), tmp); | 244 | RB_SET_RIGHT(RB_PARENT(elm), tmp); |
| 243 | } | 245 | } |
| 244 | } else { | 246 | } else { |
| 245 | head->SetRoot(tmp); | 247 | head.SetRoot(tmp); |
| 246 | } | 248 | } |
| 247 | 249 | ||
| 248 | RB_SET_LEFT(tmp, elm); | 250 | RB_SET_LEFT(tmp, elm); |
| 249 | RB_SET_PARENT(elm, tmp); | 251 | RB_SET_PARENT(elm, tmp); |
| 250 | } | 252 | } |
| 251 | 253 | ||
| 252 | template <typename Node> | 254 | template <typename T> |
| 253 | void RB_ROTATE_RIGHT(RBHead<Node>* head, Node* elm, Node*& tmp) { | 255 | requires HasRBEntry<T> |
| 256 | constexpr void RB_ROTATE_RIGHT(RBHead<T>& head, T* elm, T*& tmp) { | ||
| 254 | tmp = RB_LEFT(elm); | 257 | tmp = RB_LEFT(elm); |
| 255 | RB_SET_LEFT(elm, RB_RIGHT(tmp)); | 258 | if (RB_SET_LEFT(elm, RB_RIGHT(tmp)); RB_LEFT(elm) != nullptr) { |
| 256 | if (RB_LEFT(elm) != nullptr) { | ||
| 257 | RB_SET_PARENT(RB_RIGHT(tmp), elm); | 259 | RB_SET_PARENT(RB_RIGHT(tmp), elm); |
| 258 | } | 260 | } |
| 259 | 261 | ||
| 260 | RB_SET_PARENT(tmp, RB_PARENT(elm)); | 262 | if (RB_SET_PARENT(tmp, RB_PARENT(elm)); RB_PARENT(tmp) != nullptr) { |
| 261 | if (RB_PARENT(tmp) != nullptr) { | ||
| 262 | if (elm == RB_LEFT(RB_PARENT(elm))) { | 263 | if (elm == RB_LEFT(RB_PARENT(elm))) { |
| 263 | RB_SET_LEFT(RB_PARENT(elm), tmp); | 264 | RB_SET_LEFT(RB_PARENT(elm), tmp); |
| 264 | } else { | 265 | } else { |
| 265 | RB_SET_RIGHT(RB_PARENT(elm), tmp); | 266 | RB_SET_RIGHT(RB_PARENT(elm), tmp); |
| 266 | } | 267 | } |
| 267 | } else { | 268 | } else { |
| 268 | head->SetRoot(tmp); | 269 | head.SetRoot(tmp); |
| 269 | } | 270 | } |
| 270 | 271 | ||
| 271 | RB_SET_RIGHT(tmp, elm); | 272 | RB_SET_RIGHT(tmp, elm); |
| 272 | RB_SET_PARENT(elm, tmp); | 273 | RB_SET_PARENT(elm, tmp); |
| 273 | } | 274 | } |
| 274 | 275 | ||
| 275 | template <typename Node> | 276 | template <typename T> |
| 276 | void RB_INSERT_COLOR(RBHead<Node>* head, Node* elm) { | 277 | requires HasRBEntry<T> |
| 277 | Node* parent = nullptr; | 278 | constexpr void RB_REMOVE_COLOR(RBHead<T>& head, T* parent, T* elm) { |
| 278 | Node* tmp = nullptr; | 279 | T* tmp; |
| 279 | 280 | while ((elm == nullptr || RB_IS_BLACK(elm)) && elm != head.Root()) { | |
| 280 | while ((parent = RB_PARENT(elm)) != nullptr && RB_IS_RED(parent)) { | ||
| 281 | Node* gparent = RB_PARENT(parent); | ||
| 282 | if (parent == RB_LEFT(gparent)) { | ||
| 283 | tmp = RB_RIGHT(gparent); | ||
| 284 | if (tmp && RB_IS_RED(tmp)) { | ||
| 285 | RB_SET_COLOR(tmp, EntryColor::Black); | ||
| 286 | RB_SET_BLACKRED(parent, gparent); | ||
| 287 | elm = gparent; | ||
| 288 | continue; | ||
| 289 | } | ||
| 290 | |||
| 291 | if (RB_RIGHT(parent) == elm) { | ||
| 292 | RB_ROTATE_LEFT(head, parent, tmp); | ||
| 293 | tmp = parent; | ||
| 294 | parent = elm; | ||
| 295 | elm = tmp; | ||
| 296 | } | ||
| 297 | |||
| 298 | RB_SET_BLACKRED(parent, gparent); | ||
| 299 | RB_ROTATE_RIGHT(head, gparent, tmp); | ||
| 300 | } else { | ||
| 301 | tmp = RB_LEFT(gparent); | ||
| 302 | if (tmp && RB_IS_RED(tmp)) { | ||
| 303 | RB_SET_COLOR(tmp, EntryColor::Black); | ||
| 304 | RB_SET_BLACKRED(parent, gparent); | ||
| 305 | elm = gparent; | ||
| 306 | continue; | ||
| 307 | } | ||
| 308 | |||
| 309 | if (RB_LEFT(parent) == elm) { | ||
| 310 | RB_ROTATE_RIGHT(head, parent, tmp); | ||
| 311 | tmp = parent; | ||
| 312 | parent = elm; | ||
| 313 | elm = tmp; | ||
| 314 | } | ||
| 315 | |||
| 316 | RB_SET_BLACKRED(parent, gparent); | ||
| 317 | RB_ROTATE_LEFT(head, gparent, tmp); | ||
| 318 | } | ||
| 319 | } | ||
| 320 | |||
| 321 | RB_SET_COLOR(head->Root(), EntryColor::Black); | ||
| 322 | } | ||
| 323 | |||
| 324 | template <typename Node> | ||
| 325 | void RB_REMOVE_COLOR(RBHead<Node>* head, Node* parent, Node* elm) { | ||
| 326 | Node* tmp; | ||
| 327 | while ((elm == nullptr || RB_IS_BLACK(elm)) && elm != head->Root() && parent != nullptr) { | ||
| 328 | if (RB_LEFT(parent) == elm) { | 281 | if (RB_LEFT(parent) == elm) { |
| 329 | tmp = RB_RIGHT(parent); | 282 | tmp = RB_RIGHT(parent); |
| 330 | if (!tmp) { | ||
| 331 | ASSERT_MSG(false, "tmp is invalid!"); | ||
| 332 | break; | ||
| 333 | } | ||
| 334 | if (RB_IS_RED(tmp)) { | 283 | if (RB_IS_RED(tmp)) { |
| 335 | RB_SET_BLACKRED(tmp, parent); | 284 | RB_SET_BLACKRED(tmp, parent); |
| 336 | RB_ROTATE_LEFT(head, parent, tmp); | 285 | RB_ROTATE_LEFT(head, parent, tmp); |
| @@ -339,29 +288,29 @@ void RB_REMOVE_COLOR(RBHead<Node>* head, Node* parent, Node* elm) { | |||
| 339 | 288 | ||
| 340 | if ((RB_LEFT(tmp) == nullptr || RB_IS_BLACK(RB_LEFT(tmp))) && | 289 | if ((RB_LEFT(tmp) == nullptr || RB_IS_BLACK(RB_LEFT(tmp))) && |
| 341 | (RB_RIGHT(tmp) == nullptr || RB_IS_BLACK(RB_RIGHT(tmp)))) { | 290 | (RB_RIGHT(tmp) == nullptr || RB_IS_BLACK(RB_RIGHT(tmp)))) { |
| 342 | RB_SET_COLOR(tmp, EntryColor::Red); | 291 | RB_SET_COLOR(tmp, RBColor::RB_RED); |
| 343 | elm = parent; | 292 | elm = parent; |
| 344 | parent = RB_PARENT(elm); | 293 | parent = RB_PARENT(elm); |
| 345 | } else { | 294 | } else { |
| 346 | if (RB_RIGHT(tmp) == nullptr || RB_IS_BLACK(RB_RIGHT(tmp))) { | 295 | if (RB_RIGHT(tmp) == nullptr || RB_IS_BLACK(RB_RIGHT(tmp))) { |
| 347 | Node* oleft; | 296 | T* oleft; |
| 348 | if ((oleft = RB_LEFT(tmp)) != nullptr) { | 297 | if ((oleft = RB_LEFT(tmp)) != nullptr) { |
| 349 | RB_SET_COLOR(oleft, EntryColor::Black); | 298 | RB_SET_COLOR(oleft, RBColor::RB_BLACK); |
| 350 | } | 299 | } |
| 351 | 300 | ||
| 352 | RB_SET_COLOR(tmp, EntryColor::Red); | 301 | RB_SET_COLOR(tmp, RBColor::RB_RED); |
| 353 | RB_ROTATE_RIGHT(head, tmp, oleft); | 302 | RB_ROTATE_RIGHT(head, tmp, oleft); |
| 354 | tmp = RB_RIGHT(parent); | 303 | tmp = RB_RIGHT(parent); |
| 355 | } | 304 | } |
| 356 | 305 | ||
| 357 | RB_SET_COLOR(tmp, RB_COLOR(parent)); | 306 | RB_SET_COLOR(tmp, RB_COLOR(parent)); |
| 358 | RB_SET_COLOR(parent, EntryColor::Black); | 307 | RB_SET_COLOR(parent, RBColor::RB_BLACK); |
| 359 | if (RB_RIGHT(tmp)) { | 308 | if (RB_RIGHT(tmp)) { |
| 360 | RB_SET_COLOR(RB_RIGHT(tmp), EntryColor::Black); | 309 | RB_SET_COLOR(RB_RIGHT(tmp), RBColor::RB_BLACK); |
| 361 | } | 310 | } |
| 362 | 311 | ||
| 363 | RB_ROTATE_LEFT(head, parent, tmp); | 312 | RB_ROTATE_LEFT(head, parent, tmp); |
| 364 | elm = head->Root(); | 313 | elm = head.Root(); |
| 365 | break; | 314 | break; |
| 366 | } | 315 | } |
| 367 | } else { | 316 | } else { |
| @@ -372,68 +321,56 @@ void RB_REMOVE_COLOR(RBHead<Node>* head, Node* parent, Node* elm) { | |||
| 372 | tmp = RB_LEFT(parent); | 321 | tmp = RB_LEFT(parent); |
| 373 | } | 322 | } |
| 374 | 323 | ||
| 375 | if (!tmp) { | ||
| 376 | ASSERT_MSG(false, "tmp is invalid!"); | ||
| 377 | break; | ||
| 378 | } | ||
| 379 | |||
| 380 | if ((RB_LEFT(tmp) == nullptr || RB_IS_BLACK(RB_LEFT(tmp))) && | 324 | if ((RB_LEFT(tmp) == nullptr || RB_IS_BLACK(RB_LEFT(tmp))) && |
| 381 | (RB_RIGHT(tmp) == nullptr || RB_IS_BLACK(RB_RIGHT(tmp)))) { | 325 | (RB_RIGHT(tmp) == nullptr || RB_IS_BLACK(RB_RIGHT(tmp)))) { |
| 382 | RB_SET_COLOR(tmp, EntryColor::Red); | 326 | RB_SET_COLOR(tmp, RBColor::RB_RED); |
| 383 | elm = parent; | 327 | elm = parent; |
| 384 | parent = RB_PARENT(elm); | 328 | parent = RB_PARENT(elm); |
| 385 | } else { | 329 | } else { |
| 386 | if (RB_LEFT(tmp) == nullptr || RB_IS_BLACK(RB_LEFT(tmp))) { | 330 | if (RB_LEFT(tmp) == nullptr || RB_IS_BLACK(RB_LEFT(tmp))) { |
| 387 | Node* oright; | 331 | T* oright; |
| 388 | if ((oright = RB_RIGHT(tmp)) != nullptr) { | 332 | if ((oright = RB_RIGHT(tmp)) != nullptr) { |
| 389 | RB_SET_COLOR(oright, EntryColor::Black); | 333 | RB_SET_COLOR(oright, RBColor::RB_BLACK); |
| 390 | } | 334 | } |
| 391 | 335 | ||
| 392 | RB_SET_COLOR(tmp, EntryColor::Red); | 336 | RB_SET_COLOR(tmp, RBColor::RB_RED); |
| 393 | RB_ROTATE_LEFT(head, tmp, oright); | 337 | RB_ROTATE_LEFT(head, tmp, oright); |
| 394 | tmp = RB_LEFT(parent); | 338 | tmp = RB_LEFT(parent); |
| 395 | } | 339 | } |
| 396 | 340 | ||
| 397 | RB_SET_COLOR(tmp, RB_COLOR(parent)); | 341 | RB_SET_COLOR(tmp, RB_COLOR(parent)); |
| 398 | RB_SET_COLOR(parent, EntryColor::Black); | 342 | RB_SET_COLOR(parent, RBColor::RB_BLACK); |
| 399 | 343 | ||
| 400 | if (RB_LEFT(tmp)) { | 344 | if (RB_LEFT(tmp)) { |
| 401 | RB_SET_COLOR(RB_LEFT(tmp), EntryColor::Black); | 345 | RB_SET_COLOR(RB_LEFT(tmp), RBColor::RB_BLACK); |
| 402 | } | 346 | } |
| 403 | 347 | ||
| 404 | RB_ROTATE_RIGHT(head, parent, tmp); | 348 | RB_ROTATE_RIGHT(head, parent, tmp); |
| 405 | elm = head->Root(); | 349 | elm = head.Root(); |
| 406 | break; | 350 | break; |
| 407 | } | 351 | } |
| 408 | } | 352 | } |
| 409 | } | 353 | } |
| 410 | 354 | ||
| 411 | if (elm) { | 355 | if (elm) { |
| 412 | RB_SET_COLOR(elm, EntryColor::Black); | 356 | RB_SET_COLOR(elm, RBColor::RB_BLACK); |
| 413 | } | 357 | } |
| 414 | } | 358 | } |
| 415 | 359 | ||
| 416 | template <typename Node> | 360 | template <typename T> |
| 417 | Node* RB_REMOVE(RBHead<Node>* head, Node* elm) { | 361 | requires HasRBEntry<T> |
| 418 | Node* child = nullptr; | 362 | constexpr T* RB_REMOVE(RBHead<T>& head, T* elm) { |
| 419 | Node* parent = nullptr; | 363 | T* child = nullptr; |
| 420 | Node* old = elm; | 364 | T* parent = nullptr; |
| 421 | EntryColor color{}; | 365 | T* old = elm; |
| 422 | 366 | RBColor color = RBColor::RB_BLACK; | |
| 423 | const auto finalize = [&] { | ||
| 424 | if (color == EntryColor::Black) { | ||
| 425 | RB_REMOVE_COLOR(head, parent, child); | ||
| 426 | } | ||
| 427 | |||
| 428 | return old; | ||
| 429 | }; | ||
| 430 | 367 | ||
| 431 | if (RB_LEFT(elm) == nullptr) { | 368 | if (RB_LEFT(elm) == nullptr) { |
| 432 | child = RB_RIGHT(elm); | 369 | child = RB_RIGHT(elm); |
| 433 | } else if (RB_RIGHT(elm) == nullptr) { | 370 | } else if (RB_RIGHT(elm) == nullptr) { |
| 434 | child = RB_LEFT(elm); | 371 | child = RB_LEFT(elm); |
| 435 | } else { | 372 | } else { |
| 436 | Node* left; | 373 | T* left; |
| 437 | elm = RB_RIGHT(elm); | 374 | elm = RB_RIGHT(elm); |
| 438 | while ((left = RB_LEFT(elm)) != nullptr) { | 375 | while ((left = RB_LEFT(elm)) != nullptr) { |
| 439 | elm = left; | 376 | elm = left; |
| @@ -446,6 +383,7 @@ Node* RB_REMOVE(RBHead<Node>* head, Node* elm) { | |||
| 446 | if (child) { | 383 | if (child) { |
| 447 | RB_SET_PARENT(child, parent); | 384 | RB_SET_PARENT(child, parent); |
| 448 | } | 385 | } |
| 386 | |||
| 449 | if (parent) { | 387 | if (parent) { |
| 450 | if (RB_LEFT(parent) == elm) { | 388 | if (RB_LEFT(parent) == elm) { |
| 451 | RB_SET_LEFT(parent, child); | 389 | RB_SET_LEFT(parent, child); |
| @@ -453,14 +391,14 @@ Node* RB_REMOVE(RBHead<Node>* head, Node* elm) { | |||
| 453 | RB_SET_RIGHT(parent, child); | 391 | RB_SET_RIGHT(parent, child); |
| 454 | } | 392 | } |
| 455 | } else { | 393 | } else { |
| 456 | head->SetRoot(child); | 394 | head.SetRoot(child); |
| 457 | } | 395 | } |
| 458 | 396 | ||
| 459 | if (RB_PARENT(elm) == old) { | 397 | if (RB_PARENT(elm) == old) { |
| 460 | parent = elm; | 398 | parent = elm; |
| 461 | } | 399 | } |
| 462 | 400 | ||
| 463 | elm->SetEntry(old->GetEntry()); | 401 | elm->SetRBEntry(old->GetRBEntry()); |
| 464 | 402 | ||
| 465 | if (RB_PARENT(old)) { | 403 | if (RB_PARENT(old)) { |
| 466 | if (RB_LEFT(RB_PARENT(old)) == old) { | 404 | if (RB_LEFT(RB_PARENT(old)) == old) { |
| @@ -469,17 +407,24 @@ Node* RB_REMOVE(RBHead<Node>* head, Node* elm) { | |||
| 469 | RB_SET_RIGHT(RB_PARENT(old), elm); | 407 | RB_SET_RIGHT(RB_PARENT(old), elm); |
| 470 | } | 408 | } |
| 471 | } else { | 409 | } else { |
| 472 | head->SetRoot(elm); | 410 | head.SetRoot(elm); |
| 473 | } | 411 | } |
| 412 | |||
| 474 | RB_SET_PARENT(RB_LEFT(old), elm); | 413 | RB_SET_PARENT(RB_LEFT(old), elm); |
| 414 | |||
| 475 | if (RB_RIGHT(old)) { | 415 | if (RB_RIGHT(old)) { |
| 476 | RB_SET_PARENT(RB_RIGHT(old), elm); | 416 | RB_SET_PARENT(RB_RIGHT(old), elm); |
| 477 | } | 417 | } |
| 418 | |||
| 478 | if (parent) { | 419 | if (parent) { |
| 479 | left = parent; | 420 | left = parent; |
| 480 | } | 421 | } |
| 481 | 422 | ||
| 482 | return finalize(); | 423 | if (color == RBColor::RB_BLACK) { |
| 424 | RB_REMOVE_COLOR(head, parent, child); | ||
| 425 | } | ||
| 426 | |||
| 427 | return old; | ||
| 483 | } | 428 | } |
| 484 | 429 | ||
| 485 | parent = RB_PARENT(elm); | 430 | parent = RB_PARENT(elm); |
| @@ -495,17 +440,69 @@ Node* RB_REMOVE(RBHead<Node>* head, Node* elm) { | |||
| 495 | RB_SET_RIGHT(parent, child); | 440 | RB_SET_RIGHT(parent, child); |
| 496 | } | 441 | } |
| 497 | } else { | 442 | } else { |
| 498 | head->SetRoot(child); | 443 | head.SetRoot(child); |
| 444 | } | ||
| 445 | |||
| 446 | if (color == RBColor::RB_BLACK) { | ||
| 447 | RB_REMOVE_COLOR(head, parent, child); | ||
| 448 | } | ||
| 449 | |||
| 450 | return old; | ||
| 451 | } | ||
| 452 | |||
| 453 | template <typename T> | ||
| 454 | requires HasRBEntry<T> | ||
| 455 | constexpr void RB_INSERT_COLOR(RBHead<T>& head, T* elm) { | ||
| 456 | T *parent = nullptr, *tmp = nullptr; | ||
| 457 | while ((parent = RB_PARENT(elm)) != nullptr && RB_IS_RED(parent)) { | ||
| 458 | T* gparent = RB_PARENT(parent); | ||
| 459 | if (parent == RB_LEFT(gparent)) { | ||
| 460 | tmp = RB_RIGHT(gparent); | ||
| 461 | if (tmp && RB_IS_RED(tmp)) { | ||
| 462 | RB_SET_COLOR(tmp, RBColor::RB_BLACK); | ||
| 463 | RB_SET_BLACKRED(parent, gparent); | ||
| 464 | elm = gparent; | ||
| 465 | continue; | ||
| 466 | } | ||
| 467 | |||
| 468 | if (RB_RIGHT(parent) == elm) { | ||
| 469 | RB_ROTATE_LEFT(head, parent, tmp); | ||
| 470 | tmp = parent; | ||
| 471 | parent = elm; | ||
| 472 | elm = tmp; | ||
| 473 | } | ||
| 474 | |||
| 475 | RB_SET_BLACKRED(parent, gparent); | ||
| 476 | RB_ROTATE_RIGHT(head, gparent, tmp); | ||
| 477 | } else { | ||
| 478 | tmp = RB_LEFT(gparent); | ||
| 479 | if (tmp && RB_IS_RED(tmp)) { | ||
| 480 | RB_SET_COLOR(tmp, RBColor::RB_BLACK); | ||
| 481 | RB_SET_BLACKRED(parent, gparent); | ||
| 482 | elm = gparent; | ||
| 483 | continue; | ||
| 484 | } | ||
| 485 | |||
| 486 | if (RB_LEFT(parent) == elm) { | ||
| 487 | RB_ROTATE_RIGHT(head, parent, tmp); | ||
| 488 | tmp = parent; | ||
| 489 | parent = elm; | ||
| 490 | elm = tmp; | ||
| 491 | } | ||
| 492 | |||
| 493 | RB_SET_BLACKRED(parent, gparent); | ||
| 494 | RB_ROTATE_LEFT(head, gparent, tmp); | ||
| 495 | } | ||
| 499 | } | 496 | } |
| 500 | 497 | ||
| 501 | return finalize(); | 498 | RB_SET_COLOR(head.Root(), RBColor::RB_BLACK); |
| 502 | } | 499 | } |
| 503 | 500 | ||
| 504 | // Inserts a node into the RB tree | 501 | template <typename T, typename Compare> |
| 505 | template <typename Node, typename CompareFunction> | 502 | requires HasRBEntry<T> |
| 506 | Node* RB_INSERT(RBHead<Node>* head, Node* elm, CompareFunction cmp) { | 503 | constexpr T* RB_INSERT(RBHead<T>& head, T* elm, Compare cmp) { |
| 507 | Node* parent = nullptr; | 504 | T* parent = nullptr; |
| 508 | Node* tmp = head->Root(); | 505 | T* tmp = head.Root(); |
| 509 | int comp = 0; | 506 | int comp = 0; |
| 510 | 507 | ||
| 511 | while (tmp) { | 508 | while (tmp) { |
| @@ -529,17 +526,17 @@ Node* RB_INSERT(RBHead<Node>* head, Node* elm, CompareFunction cmp) { | |||
| 529 | RB_SET_RIGHT(parent, elm); | 526 | RB_SET_RIGHT(parent, elm); |
| 530 | } | 527 | } |
| 531 | } else { | 528 | } else { |
| 532 | head->SetRoot(elm); | 529 | head.SetRoot(elm); |
| 533 | } | 530 | } |
| 534 | 531 | ||
| 535 | RB_INSERT_COLOR(head, elm); | 532 | RB_INSERT_COLOR(head, elm); |
| 536 | return nullptr; | 533 | return nullptr; |
| 537 | } | 534 | } |
| 538 | 535 | ||
| 539 | // Finds the node with the same key as elm | 536 | template <typename T, typename Compare> |
| 540 | template <typename Node, typename CompareFunction> | 537 | requires HasRBEntry<T> |
| 541 | Node* RB_FIND(RBHead<Node>* head, Node* elm, CompareFunction cmp) { | 538 | constexpr T* RB_FIND(RBHead<T>& head, T* elm, Compare cmp) { |
| 542 | Node* tmp = head->Root(); | 539 | T* tmp = head.Root(); |
| 543 | 540 | ||
| 544 | while (tmp) { | 541 | while (tmp) { |
| 545 | const int comp = cmp(elm, tmp); | 542 | const int comp = cmp(elm, tmp); |
| @@ -555,11 +552,11 @@ Node* RB_FIND(RBHead<Node>* head, Node* elm, CompareFunction cmp) { | |||
| 555 | return nullptr; | 552 | return nullptr; |
| 556 | } | 553 | } |
| 557 | 554 | ||
| 558 | // Finds the first node greater than or equal to the search key | 555 | template <typename T, typename Compare> |
| 559 | template <typename Node, typename CompareFunction> | 556 | requires HasRBEntry<T> |
| 560 | Node* RB_NFIND(RBHead<Node>* head, Node* elm, CompareFunction cmp) { | 557 | constexpr T* RB_NFIND(RBHead<T>& head, T* elm, Compare cmp) { |
| 561 | Node* tmp = head->Root(); | 558 | T* tmp = head.Root(); |
| 562 | Node* res = nullptr; | 559 | T* res = nullptr; |
| 563 | 560 | ||
| 564 | while (tmp) { | 561 | while (tmp) { |
| 565 | const int comp = cmp(elm, tmp); | 562 | const int comp = cmp(elm, tmp); |
| @@ -576,13 +573,13 @@ Node* RB_NFIND(RBHead<Node>* head, Node* elm, CompareFunction cmp) { | |||
| 576 | return res; | 573 | return res; |
| 577 | } | 574 | } |
| 578 | 575 | ||
| 579 | // Finds the node with the same key as lelm | 576 | template <typename T, typename U, typename Compare> |
| 580 | template <typename Node, typename CompareFunction> | 577 | requires HasRBEntry<T> |
| 581 | Node* RB_FIND_LIGHT(RBHead<Node>* head, const void* lelm, CompareFunction lcmp) { | 578 | constexpr T* RB_FIND_KEY(RBHead<T>& head, const U& key, Compare cmp) { |
| 582 | Node* tmp = head->Root(); | 579 | T* tmp = head.Root(); |
| 583 | 580 | ||
| 584 | while (tmp) { | 581 | while (tmp) { |
| 585 | const int comp = lcmp(lelm, tmp); | 582 | const int comp = cmp(key, tmp); |
| 586 | if (comp < 0) { | 583 | if (comp < 0) { |
| 587 | tmp = RB_LEFT(tmp); | 584 | tmp = RB_LEFT(tmp); |
| 588 | } else if (comp > 0) { | 585 | } else if (comp > 0) { |
| @@ -595,14 +592,14 @@ Node* RB_FIND_LIGHT(RBHead<Node>* head, const void* lelm, CompareFunction lcmp) | |||
| 595 | return nullptr; | 592 | return nullptr; |
| 596 | } | 593 | } |
| 597 | 594 | ||
| 598 | // Finds the first node greater than or equal to the search key | 595 | template <typename T, typename U, typename Compare> |
| 599 | template <typename Node, typename CompareFunction> | 596 | requires HasRBEntry<T> |
| 600 | Node* RB_NFIND_LIGHT(RBHead<Node>* head, const void* lelm, CompareFunction lcmp) { | 597 | constexpr T* RB_NFIND_KEY(RBHead<T>& head, const U& key, Compare cmp) { |
| 601 | Node* tmp = head->Root(); | 598 | T* tmp = head.Root(); |
| 602 | Node* res = nullptr; | 599 | T* res = nullptr; |
| 603 | 600 | ||
| 604 | while (tmp) { | 601 | while (tmp) { |
| 605 | const int comp = lcmp(lelm, tmp); | 602 | const int comp = cmp(key, tmp); |
| 606 | if (comp < 0) { | 603 | if (comp < 0) { |
| 607 | res = tmp; | 604 | res = tmp; |
| 608 | tmp = RB_LEFT(tmp); | 605 | tmp = RB_LEFT(tmp); |
| @@ -616,8 +613,43 @@ Node* RB_NFIND_LIGHT(RBHead<Node>* head, const void* lelm, CompareFunction lcmp) | |||
| 616 | return res; | 613 | return res; |
| 617 | } | 614 | } |
| 618 | 615 | ||
| 619 | template <typename Node> | 616 | template <typename T, typename Compare> |
| 620 | Node* RB_NEXT(Node* elm) { | 617 | requires HasRBEntry<T> |
| 618 | constexpr T* RB_FIND_EXISTING(RBHead<T>& head, T* elm, Compare cmp) { | ||
| 619 | T* tmp = head.Root(); | ||
| 620 | |||
| 621 | while (true) { | ||
| 622 | const int comp = cmp(elm, tmp); | ||
| 623 | if (comp < 0) { | ||
| 624 | tmp = RB_LEFT(tmp); | ||
| 625 | } else if (comp > 0) { | ||
| 626 | tmp = RB_RIGHT(tmp); | ||
| 627 | } else { | ||
| 628 | return tmp; | ||
| 629 | } | ||
| 630 | } | ||
| 631 | } | ||
| 632 | |||
| 633 | template <typename T, typename U, typename Compare> | ||
| 634 | requires HasRBEntry<T> | ||
| 635 | constexpr T* RB_FIND_EXISTING_KEY(RBHead<T>& head, const U& key, Compare cmp) { | ||
| 636 | T* tmp = head.Root(); | ||
| 637 | |||
| 638 | while (true) { | ||
| 639 | const int comp = cmp(key, tmp); | ||
| 640 | if (comp < 0) { | ||
| 641 | tmp = RB_LEFT(tmp); | ||
| 642 | } else if (comp > 0) { | ||
| 643 | tmp = RB_RIGHT(tmp); | ||
| 644 | } else { | ||
| 645 | return tmp; | ||
| 646 | } | ||
| 647 | } | ||
| 648 | } | ||
| 649 | |||
| 650 | template <typename T> | ||
| 651 | requires HasRBEntry<T> | ||
| 652 | constexpr T* RB_NEXT(T* elm) { | ||
| 621 | if (RB_RIGHT(elm)) { | 653 | if (RB_RIGHT(elm)) { |
| 622 | elm = RB_RIGHT(elm); | 654 | elm = RB_RIGHT(elm); |
| 623 | while (RB_LEFT(elm)) { | 655 | while (RB_LEFT(elm)) { |
| @@ -636,8 +668,9 @@ Node* RB_NEXT(Node* elm) { | |||
| 636 | return elm; | 668 | return elm; |
| 637 | } | 669 | } |
| 638 | 670 | ||
| 639 | template <typename Node> | 671 | template <typename T> |
| 640 | Node* RB_PREV(Node* elm) { | 672 | requires HasRBEntry<T> |
| 673 | constexpr T* RB_PREV(T* elm) { | ||
| 641 | if (RB_LEFT(elm)) { | 674 | if (RB_LEFT(elm)) { |
| 642 | elm = RB_LEFT(elm); | 675 | elm = RB_LEFT(elm); |
| 643 | while (RB_RIGHT(elm)) { | 676 | while (RB_RIGHT(elm)) { |
| @@ -656,30 +689,32 @@ Node* RB_PREV(Node* elm) { | |||
| 656 | return elm; | 689 | return elm; |
| 657 | } | 690 | } |
| 658 | 691 | ||
| 659 | template <typename Node> | 692 | template <typename T> |
| 660 | Node* RB_MINMAX(RBHead<Node>* head, bool is_min) { | 693 | requires HasRBEntry<T> |
| 661 | Node* tmp = head->Root(); | 694 | constexpr T* RB_MIN(RBHead<T>& head) { |
| 662 | Node* parent = nullptr; | 695 | T* tmp = head.Root(); |
| 696 | T* parent = nullptr; | ||
| 663 | 697 | ||
| 664 | while (tmp) { | 698 | while (tmp) { |
| 665 | parent = tmp; | 699 | parent = tmp; |
| 666 | if (is_min) { | 700 | tmp = RB_LEFT(tmp); |
| 667 | tmp = RB_LEFT(tmp); | ||
| 668 | } else { | ||
| 669 | tmp = RB_RIGHT(tmp); | ||
| 670 | } | ||
| 671 | } | 701 | } |
| 672 | 702 | ||
| 673 | return parent; | 703 | return parent; |
| 674 | } | 704 | } |
| 675 | 705 | ||
| 676 | template <typename Node> | 706 | template <typename T> |
| 677 | Node* RB_MIN(RBHead<Node>* head) { | 707 | requires HasRBEntry<T> |
| 678 | return RB_MINMAX(head, true); | 708 | constexpr T* RB_MAX(RBHead<T>& head) { |
| 679 | } | 709 | T* tmp = head.Root(); |
| 710 | T* parent = nullptr; | ||
| 680 | 711 | ||
| 681 | template <typename Node> | 712 | while (tmp) { |
| 682 | Node* RB_MAX(RBHead<Node>* head) { | 713 | parent = tmp; |
| 683 | return RB_MINMAX(head, false); | 714 | tmp = RB_RIGHT(tmp); |
| 715 | } | ||
| 716 | |||
| 717 | return parent; | ||
| 684 | } | 718 | } |
| 685 | } // namespace Common | 719 | |
| 720 | } // namespace Common::freebsd | ||
diff --git a/src/common/uint128.h b/src/common/uint128.h index 4780b2f9d..f450a6db9 100644 --- a/src/common/uint128.h +++ b/src/common/uint128.h | |||
| @@ -1,10 +1,8 @@ | |||
| 1 | // Copyright 2019 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | // Refer to the license.txt file included. | ||
| 4 | 3 | ||
| 5 | #pragma once | 4 | #pragma once |
| 6 | 5 | ||
| 7 | #include <cstring> | ||
| 8 | #include <utility> | 6 | #include <utility> |
| 9 | 7 | ||
| 10 | #ifdef _MSC_VER | 8 | #ifdef _MSC_VER |
| @@ -13,7 +11,7 @@ | |||
| 13 | #pragma intrinsic(_umul128) | 11 | #pragma intrinsic(_umul128) |
| 14 | #pragma intrinsic(_udiv128) | 12 | #pragma intrinsic(_udiv128) |
| 15 | #else | 13 | #else |
| 16 | #include <x86intrin.h> | 14 | #include <cstring> |
| 17 | #endif | 15 | #endif |
| 18 | 16 | ||
| 19 | #include "common/common_types.h" | 17 | #include "common/common_types.h" |
diff --git a/src/common/unique_function.h b/src/common/unique_function.h index ca0559071..c15d88349 100644 --- a/src/common/unique_function.h +++ b/src/common/unique_function.h | |||
| @@ -1,6 +1,5 @@ | |||
| 1 | // Copyright 2021 yuzu emulator team | 1 | // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | // Refer to the license.txt file included. | ||
| 4 | 3 | ||
| 5 | #pragma once | 4 | #pragma once |
| 6 | 5 | ||
diff --git a/src/common/uuid.cpp b/src/common/uuid.cpp index 2b6a530e3..89e1ed225 100644 --- a/src/common/uuid.cpp +++ b/src/common/uuid.cpp | |||
| @@ -1,6 +1,5 @@ | |||
| 1 | // Copyright 2022 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | // Refer to the license.txt file included. | ||
| 4 | 3 | ||
| 5 | #include <bit> | 4 | #include <bit> |
| 6 | #include <optional> | 5 | #include <optional> |
diff --git a/src/common/uuid.h b/src/common/uuid.h index fe31e64e6..7172ca165 100644 --- a/src/common/uuid.h +++ b/src/common/uuid.h | |||
| @@ -1,13 +1,11 @@ | |||
| 1 | // Copyright 2022 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | // Refer to the license.txt file included. | ||
| 4 | 3 | ||
| 5 | #pragma once | 4 | #pragma once |
| 6 | 5 | ||
| 7 | #include <array> | 6 | #include <array> |
| 8 | #include <functional> | 7 | #include <functional> |
| 9 | #include <string> | 8 | #include <string> |
| 10 | #include <string_view> | ||
| 11 | 9 | ||
| 12 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 13 | 11 | ||
diff --git a/src/common/vector_math.h b/src/common/vector_math.h index ba7c363c1..e62eeea2e 100644 --- a/src/common/vector_math.h +++ b/src/common/vector_math.h | |||
| @@ -1,32 +1,6 @@ | |||
| 1 | // Licensed under GPLv2 or any later version | 1 | // SPDX-FileCopyrightText: 2014 Tony Wasserka |
| 2 | // Refer to the license.txt file included. | 2 | // SPDX-FileCopyrightText: 2014 Dolphin Emulator Project |
| 3 | 3 | // SPDX-License-Identifier: BSD-3-Clause AND GPL-2.0-or-later | |
| 4 | // Copyright 2014 Tony Wasserka | ||
| 5 | // All rights reserved. | ||
| 6 | // | ||
| 7 | // Redistribution and use in source and binary forms, with or without | ||
| 8 | // modification, are permitted provided that the following conditions are met: | ||
| 9 | // | ||
| 10 | // * Redistributions of source code must retain the above copyright | ||
| 11 | // notice, this list of conditions and the following disclaimer. | ||
| 12 | // * Redistributions in binary form must reproduce the above copyright | ||
| 13 | // notice, this list of conditions and the following disclaimer in the | ||
| 14 | // documentation and/or other materials provided with the distribution. | ||
| 15 | // * Neither the name of the owner nor the names of its contributors may | ||
| 16 | // be used to endorse or promote products derived from this software | ||
| 17 | // without specific prior written permission. | ||
| 18 | // | ||
| 19 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
| 20 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
| 21 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||
| 22 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
| 23 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| 24 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||
| 25 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
| 26 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
| 27 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
| 28 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
| 29 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| 30 | 4 | ||
| 31 | #pragma once | 5 | #pragma once |
| 32 | 6 | ||
diff --git a/src/common/virtual_buffer.cpp b/src/common/virtual_buffer.cpp index e3ca29258..dea6de99f 100644 --- a/src/common/virtual_buffer.cpp +++ b/src/common/virtual_buffer.cpp | |||
| @@ -1,6 +1,5 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | // Refer to the license.txt file included. | ||
| 4 | 3 | ||
| 5 | #ifdef _WIN32 | 4 | #ifdef _WIN32 |
| 6 | #include <windows.h> | 5 | #include <windows.h> |
diff --git a/src/common/virtual_buffer.h b/src/common/virtual_buffer.h index fb1a6f81f..4f6e3e6e5 100644 --- a/src/common/virtual_buffer.h +++ b/src/common/virtual_buffer.h | |||
| @@ -1,10 +1,8 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | // Refer to the license.txt file included. | ||
| 4 | 3 | ||
| 5 | #pragma once | 4 | #pragma once |
| 6 | 5 | ||
| 7 | #include <type_traits> | ||
| 8 | #include <utility> | 6 | #include <utility> |
| 9 | 7 | ||
| 10 | namespace Common { | 8 | namespace Common { |
diff --git a/src/common/wall_clock.cpp b/src/common/wall_clock.cpp index 9acf7551e..ae07f2811 100644 --- a/src/common/wall_clock.cpp +++ b/src/common/wall_clock.cpp | |||
| @@ -1,8 +1,5 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <cstdint> | ||
| 6 | 3 | ||
| 7 | #include "common/uint128.h" | 4 | #include "common/uint128.h" |
| 8 | #include "common/wall_clock.h" | 5 | #include "common/wall_clock.h" |
| @@ -70,7 +67,7 @@ std::unique_ptr<WallClock> CreateBestMatchingClock(u64 emulated_cpu_frequency, | |||
| 70 | const auto& caps = GetCPUCaps(); | 67 | const auto& caps = GetCPUCaps(); |
| 71 | u64 rtsc_frequency = 0; | 68 | u64 rtsc_frequency = 0; |
| 72 | if (caps.invariant_tsc) { | 69 | if (caps.invariant_tsc) { |
| 73 | rtsc_frequency = EstimateRDTSCFrequency(); | 70 | rtsc_frequency = caps.tsc_frequency ? caps.tsc_frequency : EstimateRDTSCFrequency(); |
| 74 | } | 71 | } |
| 75 | 72 | ||
| 76 | // Fallback to StandardWallClock if the hardware TSC does not have the precision greater than: | 73 | // Fallback to StandardWallClock if the hardware TSC does not have the precision greater than: |
diff --git a/src/common/wall_clock.h b/src/common/wall_clock.h index 874448c27..828a523a8 100644 --- a/src/common/wall_clock.h +++ b/src/common/wall_clock.h | |||
| @@ -1,6 +1,5 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | // Refer to the license.txt file included. | ||
| 4 | 3 | ||
| 5 | #pragma once | 4 | #pragma once |
| 6 | 5 | ||
diff --git a/src/common/x64/cpu_detect.cpp b/src/common/x64/cpu_detect.cpp index fbeacc7e2..1a27532d4 100644 --- a/src/common/x64/cpu_detect.cpp +++ b/src/common/x64/cpu_detect.cpp | |||
| @@ -1,8 +1,12 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project / 2015 Citra Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-FileCopyrightText: Copyright 2013 Dolphin Emulator Project / 2015 Citra Emulator Project |
| 3 | // Refer to the license.txt file included. | 3 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 4 | 4 | ||
| 5 | #include <array> | ||
| 5 | #include <cstring> | 6 | #include <cstring> |
| 7 | #include <iterator> | ||
| 8 | #include <string_view> | ||
| 9 | #include "common/bit_util.h" | ||
| 6 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 7 | #include "common/x64/cpu_detect.h" | 11 | #include "common/x64/cpu_detect.h" |
| 8 | 12 | ||
| @@ -17,7 +21,7 @@ | |||
| 17 | // clang-format on | 21 | // clang-format on |
| 18 | #endif | 22 | #endif |
| 19 | 23 | ||
| 20 | static inline void __cpuidex(int info[4], int function_id, int subfunction_id) { | 24 | static inline void __cpuidex(int info[4], u32 function_id, u32 subfunction_id) { |
| 21 | #if defined(__DragonFly__) || defined(__FreeBSD__) | 25 | #if defined(__DragonFly__) || defined(__FreeBSD__) |
| 22 | // Despite the name, this is just do_cpuid() with ECX as second input. | 26 | // Despite the name, this is just do_cpuid() with ECX as second input. |
| 23 | cpuid_count((u_int)function_id, (u_int)subfunction_id, (u_int*)info); | 27 | cpuid_count((u_int)function_id, (u_int)subfunction_id, (u_int*)info); |
| @@ -30,7 +34,7 @@ static inline void __cpuidex(int info[4], int function_id, int subfunction_id) { | |||
| 30 | #endif | 34 | #endif |
| 31 | } | 35 | } |
| 32 | 36 | ||
| 33 | static inline void __cpuid(int info[4], int function_id) { | 37 | static inline void __cpuid(int info[4], u32 function_id) { |
| 34 | return __cpuidex(info, function_id, 0); | 38 | return __cpuidex(info, function_id, 0); |
| 35 | } | 39 | } |
| 36 | 40 | ||
| @@ -45,6 +49,17 @@ static inline u64 _xgetbv(u32 index) { | |||
| 45 | 49 | ||
| 46 | namespace Common { | 50 | namespace Common { |
| 47 | 51 | ||
| 52 | CPUCaps::Manufacturer CPUCaps::ParseManufacturer(std::string_view brand_string) { | ||
| 53 | if (brand_string == "GenuineIntel") { | ||
| 54 | return Manufacturer::Intel; | ||
| 55 | } else if (brand_string == "AuthenticAMD") { | ||
| 56 | return Manufacturer::AMD; | ||
| 57 | } else if (brand_string == "HygonGenuine") { | ||
| 58 | return Manufacturer::Hygon; | ||
| 59 | } | ||
| 60 | return Manufacturer::Unknown; | ||
| 61 | } | ||
| 62 | |||
| 48 | // Detects the various CPU features | 63 | // Detects the various CPU features |
| 49 | static CPUCaps Detect() { | 64 | static CPUCaps Detect() { |
| 50 | CPUCaps caps = {}; | 65 | CPUCaps caps = {}; |
| @@ -53,75 +68,74 @@ static CPUCaps Detect() { | |||
| 53 | // yuzu at all anyway | 68 | // yuzu at all anyway |
| 54 | 69 | ||
| 55 | int cpu_id[4]; | 70 | int cpu_id[4]; |
| 56 | memset(caps.brand_string, 0, sizeof(caps.brand_string)); | ||
| 57 | 71 | ||
| 58 | // Detect CPU's CPUID capabilities and grab CPU string | 72 | // Detect CPU's CPUID capabilities and grab manufacturer string |
| 59 | __cpuid(cpu_id, 0x00000000); | 73 | __cpuid(cpu_id, 0x00000000); |
| 60 | u32 max_std_fn = cpu_id[0]; // EAX | 74 | const u32 max_std_fn = cpu_id[0]; // EAX |
| 61 | |||
| 62 | std::memcpy(&caps.brand_string[0], &cpu_id[1], sizeof(int)); | ||
| 63 | std::memcpy(&caps.brand_string[4], &cpu_id[3], sizeof(int)); | ||
| 64 | std::memcpy(&caps.brand_string[8], &cpu_id[2], sizeof(int)); | ||
| 65 | if (cpu_id[1] == 0x756e6547 && cpu_id[2] == 0x6c65746e && cpu_id[3] == 0x49656e69) | ||
| 66 | caps.manufacturer = Manufacturer::Intel; | ||
| 67 | else if (cpu_id[1] == 0x68747541 && cpu_id[2] == 0x444d4163 && cpu_id[3] == 0x69746e65) | ||
| 68 | caps.manufacturer = Manufacturer::AMD; | ||
| 69 | else if (cpu_id[1] == 0x6f677948 && cpu_id[2] == 0x656e6975 && cpu_id[3] == 0x6e65476e) | ||
| 70 | caps.manufacturer = Manufacturer::Hygon; | ||
| 71 | else | ||
| 72 | caps.manufacturer = Manufacturer::Unknown; | ||
| 73 | 75 | ||
| 74 | __cpuid(cpu_id, 0x80000000); | 76 | std::memset(caps.brand_string, 0, std::size(caps.brand_string)); |
| 77 | std::memcpy(&caps.brand_string[0], &cpu_id[1], sizeof(u32)); | ||
| 78 | std::memcpy(&caps.brand_string[4], &cpu_id[3], sizeof(u32)); | ||
| 79 | std::memcpy(&caps.brand_string[8], &cpu_id[2], sizeof(u32)); | ||
| 80 | |||
| 81 | caps.manufacturer = CPUCaps::ParseManufacturer(caps.brand_string); | ||
| 82 | |||
| 83 | // Set reasonable default cpu string even if brand string not available | ||
| 84 | std::strncpy(caps.cpu_string, caps.brand_string, std::size(caps.brand_string)); | ||
| 75 | 85 | ||
| 76 | u32 max_ex_fn = cpu_id[0]; | 86 | __cpuid(cpu_id, 0x80000000); |
| 77 | 87 | ||
| 78 | // Set reasonable default brand string even if brand string not available | 88 | const u32 max_ex_fn = cpu_id[0]; |
| 79 | strcpy(caps.cpu_string, caps.brand_string); | ||
| 80 | 89 | ||
| 81 | // Detect family and other miscellaneous features | 90 | // Detect family and other miscellaneous features |
| 82 | if (max_std_fn >= 1) { | 91 | if (max_std_fn >= 1) { |
| 83 | __cpuid(cpu_id, 0x00000001); | 92 | __cpuid(cpu_id, 0x00000001); |
| 84 | if ((cpu_id[3] >> 25) & 1) | 93 | caps.sse = Common::Bit<25>(cpu_id[3]); |
| 85 | caps.sse = true; | 94 | caps.sse2 = Common::Bit<26>(cpu_id[3]); |
| 86 | if ((cpu_id[3] >> 26) & 1) | 95 | caps.sse3 = Common::Bit<0>(cpu_id[2]); |
| 87 | caps.sse2 = true; | 96 | caps.pclmulqdq = Common::Bit<1>(cpu_id[2]); |
| 88 | if ((cpu_id[2]) & 1) | 97 | caps.ssse3 = Common::Bit<9>(cpu_id[2]); |
| 89 | caps.sse3 = true; | 98 | caps.sse4_1 = Common::Bit<19>(cpu_id[2]); |
| 90 | if ((cpu_id[2] >> 9) & 1) | 99 | caps.sse4_2 = Common::Bit<20>(cpu_id[2]); |
| 91 | caps.ssse3 = true; | 100 | caps.movbe = Common::Bit<22>(cpu_id[2]); |
| 92 | if ((cpu_id[2] >> 19) & 1) | 101 | caps.popcnt = Common::Bit<23>(cpu_id[2]); |
| 93 | caps.sse4_1 = true; | 102 | caps.aes = Common::Bit<25>(cpu_id[2]); |
| 94 | if ((cpu_id[2] >> 20) & 1) | 103 | caps.f16c = Common::Bit<29>(cpu_id[2]); |
| 95 | caps.sse4_2 = true; | ||
| 96 | if ((cpu_id[2] >> 25) & 1) | ||
| 97 | caps.aes = true; | ||
| 98 | 104 | ||
| 99 | // AVX support requires 3 separate checks: | 105 | // AVX support requires 3 separate checks: |
| 100 | // - Is the AVX bit set in CPUID? | 106 | // - Is the AVX bit set in CPUID? |
| 101 | // - Is the XSAVE bit set in CPUID? | 107 | // - Is the XSAVE bit set in CPUID? |
| 102 | // - XGETBV result has the XCR bit set. | 108 | // - XGETBV result has the XCR bit set. |
| 103 | if (((cpu_id[2] >> 28) & 1) && ((cpu_id[2] >> 27) & 1)) { | 109 | if (Common::Bit<28>(cpu_id[2]) && Common::Bit<27>(cpu_id[2])) { |
| 104 | if ((_xgetbv(_XCR_XFEATURE_ENABLED_MASK) & 0x6) == 0x6) { | 110 | if ((_xgetbv(_XCR_XFEATURE_ENABLED_MASK) & 0x6) == 0x6) { |
| 105 | caps.avx = true; | 111 | caps.avx = true; |
| 106 | if ((cpu_id[2] >> 12) & 1) | 112 | if (Common::Bit<12>(cpu_id[2])) |
| 107 | caps.fma = true; | 113 | caps.fma = true; |
| 108 | } | 114 | } |
| 109 | } | 115 | } |
| 110 | 116 | ||
| 111 | if (max_std_fn >= 7) { | 117 | if (max_std_fn >= 7) { |
| 112 | __cpuidex(cpu_id, 0x00000007, 0x00000000); | 118 | __cpuidex(cpu_id, 0x00000007, 0x00000000); |
| 113 | // Can't enable AVX2 unless the XSAVE/XGETBV checks above passed | 119 | // Can't enable AVX{2,512} unless the XSAVE/XGETBV checks above passed |
| 114 | if ((cpu_id[1] >> 5) & 1) | 120 | if (caps.avx) { |
| 115 | caps.avx2 = caps.avx; | 121 | caps.avx2 = Common::Bit<5>(cpu_id[1]); |
| 116 | if ((cpu_id[1] >> 3) & 1) | 122 | caps.avx512f = Common::Bit<16>(cpu_id[1]); |
| 117 | caps.bmi1 = true; | 123 | caps.avx512dq = Common::Bit<17>(cpu_id[1]); |
| 118 | if ((cpu_id[1] >> 8) & 1) | 124 | caps.avx512cd = Common::Bit<28>(cpu_id[1]); |
| 119 | caps.bmi2 = true; | 125 | caps.avx512bw = Common::Bit<30>(cpu_id[1]); |
| 120 | // Checks for AVX512F, AVX512CD, AVX512VL, AVX512DQ, AVX512BW (Intel Skylake-X/SP) | 126 | caps.avx512vl = Common::Bit<31>(cpu_id[1]); |
| 121 | if ((cpu_id[1] >> 16) & 1 && (cpu_id[1] >> 28) & 1 && (cpu_id[1] >> 31) & 1 && | 127 | caps.avx512vbmi = Common::Bit<1>(cpu_id[2]); |
| 122 | (cpu_id[1] >> 17) & 1 && (cpu_id[1] >> 30) & 1) { | 128 | caps.avx512bitalg = Common::Bit<12>(cpu_id[2]); |
| 123 | caps.avx512 = caps.avx2; | ||
| 124 | } | 129 | } |
| 130 | |||
| 131 | caps.bmi1 = Common::Bit<3>(cpu_id[1]); | ||
| 132 | caps.bmi2 = Common::Bit<8>(cpu_id[1]); | ||
| 133 | caps.sha = Common::Bit<29>(cpu_id[1]); | ||
| 134 | |||
| 135 | caps.gfni = Common::Bit<8>(cpu_id[2]); | ||
| 136 | |||
| 137 | __cpuidex(cpu_id, 0x00000007, 0x00000001); | ||
| 138 | caps.avx_vnni = caps.avx && Common::Bit<4>(cpu_id[0]); | ||
| 125 | } | 139 | } |
| 126 | } | 140 | } |
| 127 | 141 | ||
| @@ -138,14 +152,28 @@ static CPUCaps Detect() { | |||
| 138 | if (max_ex_fn >= 0x80000001) { | 152 | if (max_ex_fn >= 0x80000001) { |
| 139 | // Check for more features | 153 | // Check for more features |
| 140 | __cpuid(cpu_id, 0x80000001); | 154 | __cpuid(cpu_id, 0x80000001); |
| 141 | if ((cpu_id[2] >> 16) & 1) | 155 | caps.lzcnt = Common::Bit<5>(cpu_id[2]); |
| 142 | caps.fma4 = true; | 156 | caps.fma4 = Common::Bit<16>(cpu_id[2]); |
| 143 | } | 157 | } |
| 144 | 158 | ||
| 145 | if (max_ex_fn >= 0x80000007) { | 159 | if (max_ex_fn >= 0x80000007) { |
| 146 | __cpuid(cpu_id, 0x80000007); | 160 | __cpuid(cpu_id, 0x80000007); |
| 147 | if (cpu_id[3] & (1 << 8)) { | 161 | caps.invariant_tsc = Common::Bit<8>(cpu_id[3]); |
| 148 | caps.invariant_tsc = true; | 162 | } |
| 163 | |||
| 164 | if (max_std_fn >= 0x15) { | ||
| 165 | __cpuid(cpu_id, 0x15); | ||
| 166 | caps.tsc_crystal_ratio_denominator = cpu_id[0]; | ||
| 167 | caps.tsc_crystal_ratio_numerator = cpu_id[1]; | ||
| 168 | caps.crystal_frequency = cpu_id[2]; | ||
| 169 | // Some CPU models might not return a crystal frequency. | ||
| 170 | // The CPU model can be detected to use the values from turbostat | ||
| 171 | // https://github.com/torvalds/linux/blob/master/tools/power/x86/turbostat/turbostat.c#L5569 | ||
| 172 | // but it's easier to just estimate the TSC tick rate for these cases. | ||
| 173 | if (caps.tsc_crystal_ratio_denominator) { | ||
| 174 | caps.tsc_frequency = static_cast<u64>(caps.crystal_frequency) * | ||
| 175 | caps.tsc_crystal_ratio_numerator / | ||
| 176 | caps.tsc_crystal_ratio_denominator; | ||
| 149 | } | 177 | } |
| 150 | } | 178 | } |
| 151 | 179 | ||
diff --git a/src/common/x64/cpu_detect.h b/src/common/x64/cpu_detect.h index e3b63302e..6830f3795 100644 --- a/src/common/x64/cpu_detect.h +++ b/src/common/x64/cpu_detect.h | |||
| @@ -1,42 +1,71 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project / 2015 Citra Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-FileCopyrightText: Copyright 2013 Dolphin Emulator Project / 2015 Citra Emulator Project |
| 3 | // Refer to the license.txt file included. | 3 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | namespace Common { | 7 | #include <string_view> |
| 8 | #include "common/common_types.h" | ||
| 8 | 9 | ||
| 9 | enum class Manufacturer : u32 { | 10 | namespace Common { |
| 10 | Intel = 0, | ||
| 11 | AMD = 1, | ||
| 12 | Hygon = 2, | ||
| 13 | Unknown = 3, | ||
| 14 | }; | ||
| 15 | 11 | ||
| 16 | /// x86/x64 CPU capabilities that may be detected by this module | 12 | /// x86/x64 CPU capabilities that may be detected by this module |
| 17 | struct CPUCaps { | 13 | struct CPUCaps { |
| 14 | |||
| 15 | enum class Manufacturer : u8 { | ||
| 16 | Unknown = 0, | ||
| 17 | Intel = 1, | ||
| 18 | AMD = 2, | ||
| 19 | Hygon = 3, | ||
| 20 | }; | ||
| 21 | |||
| 22 | static Manufacturer ParseManufacturer(std::string_view brand_string); | ||
| 23 | |||
| 18 | Manufacturer manufacturer; | 24 | Manufacturer manufacturer; |
| 19 | char cpu_string[0x21]; | 25 | char brand_string[13]; |
| 20 | char brand_string[0x41]; | 26 | |
| 21 | bool sse; | 27 | char cpu_string[48]; |
| 22 | bool sse2; | 28 | |
| 23 | bool sse3; | ||
| 24 | bool ssse3; | ||
| 25 | bool sse4_1; | ||
| 26 | bool sse4_2; | ||
| 27 | bool lzcnt; | ||
| 28 | bool avx; | ||
| 29 | bool avx2; | ||
| 30 | bool avx512; | ||
| 31 | bool bmi1; | ||
| 32 | bool bmi2; | ||
| 33 | bool fma; | ||
| 34 | bool fma4; | ||
| 35 | bool aes; | ||
| 36 | bool invariant_tsc; | ||
| 37 | u32 base_frequency; | 29 | u32 base_frequency; |
| 38 | u32 max_frequency; | 30 | u32 max_frequency; |
| 39 | u32 bus_frequency; | 31 | u32 bus_frequency; |
| 32 | |||
| 33 | u32 tsc_crystal_ratio_denominator; | ||
| 34 | u32 tsc_crystal_ratio_numerator; | ||
| 35 | u32 crystal_frequency; | ||
| 36 | u64 tsc_frequency; // Derived from the above three values | ||
| 37 | |||
| 38 | bool sse : 1; | ||
| 39 | bool sse2 : 1; | ||
| 40 | bool sse3 : 1; | ||
| 41 | bool ssse3 : 1; | ||
| 42 | bool sse4_1 : 1; | ||
| 43 | bool sse4_2 : 1; | ||
| 44 | |||
| 45 | bool avx : 1; | ||
| 46 | bool avx_vnni : 1; | ||
| 47 | bool avx2 : 1; | ||
| 48 | bool avx512f : 1; | ||
| 49 | bool avx512dq : 1; | ||
| 50 | bool avx512cd : 1; | ||
| 51 | bool avx512bw : 1; | ||
| 52 | bool avx512vl : 1; | ||
| 53 | bool avx512vbmi : 1; | ||
| 54 | bool avx512bitalg : 1; | ||
| 55 | |||
| 56 | bool aes : 1; | ||
| 57 | bool bmi1 : 1; | ||
| 58 | bool bmi2 : 1; | ||
| 59 | bool f16c : 1; | ||
| 60 | bool fma : 1; | ||
| 61 | bool fma4 : 1; | ||
| 62 | bool gfni : 1; | ||
| 63 | bool invariant_tsc : 1; | ||
| 64 | bool lzcnt : 1; | ||
| 65 | bool movbe : 1; | ||
| 66 | bool pclmulqdq : 1; | ||
| 67 | bool popcnt : 1; | ||
| 68 | bool sha : 1; | ||
| 40 | }; | 69 | }; |
| 41 | 70 | ||
| 42 | /** | 71 | /** |
diff --git a/src/common/x64/native_clock.cpp b/src/common/x64/native_clock.cpp index 91b842829..8b08332ab 100644 --- a/src/common/x64/native_clock.cpp +++ b/src/common/x64/native_clock.cpp | |||
| @@ -1,36 +1,57 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | // Refer to the license.txt file included. | ||
| 4 | 3 | ||
| 5 | #include <array> | 4 | #include <array> |
| 6 | #include <chrono> | 5 | #include <chrono> |
| 7 | #include <limits> | ||
| 8 | #include <mutex> | ||
| 9 | #include <thread> | 6 | #include <thread> |
| 10 | 7 | ||
| 11 | #include "common/atomic_ops.h" | 8 | #include "common/atomic_ops.h" |
| 12 | #include "common/uint128.h" | 9 | #include "common/uint128.h" |
| 13 | #include "common/x64/native_clock.h" | 10 | #include "common/x64/native_clock.h" |
| 14 | 11 | ||
| 12 | #ifdef _MSC_VER | ||
| 13 | #include <intrin.h> | ||
| 14 | #endif | ||
| 15 | |||
| 15 | namespace Common { | 16 | namespace Common { |
| 16 | 17 | ||
| 18 | #ifdef _MSC_VER | ||
| 19 | __forceinline static u64 FencedRDTSC() { | ||
| 20 | _mm_lfence(); | ||
| 21 | _ReadWriteBarrier(); | ||
| 22 | const u64 result = __rdtsc(); | ||
| 23 | _mm_lfence(); | ||
| 24 | _ReadWriteBarrier(); | ||
| 25 | return result; | ||
| 26 | } | ||
| 27 | #else | ||
| 28 | static u64 FencedRDTSC() { | ||
| 29 | u64 result; | ||
| 30 | asm volatile("lfence\n\t" | ||
| 31 | "rdtsc\n\t" | ||
| 32 | "shl $32, %%rdx\n\t" | ||
| 33 | "or %%rdx, %0\n\t" | ||
| 34 | "lfence" | ||
| 35 | : "=a"(result) | ||
| 36 | : | ||
| 37 | : "rdx", "memory", "cc"); | ||
| 38 | return result; | ||
| 39 | } | ||
| 40 | #endif | ||
| 41 | |||
| 17 | u64 EstimateRDTSCFrequency() { | 42 | u64 EstimateRDTSCFrequency() { |
| 18 | // Discard the first result measuring the rdtsc. | 43 | // Discard the first result measuring the rdtsc. |
| 19 | _mm_mfence(); | 44 | FencedRDTSC(); |
| 20 | __rdtsc(); | ||
| 21 | std::this_thread::sleep_for(std::chrono::milliseconds{1}); | 45 | std::this_thread::sleep_for(std::chrono::milliseconds{1}); |
| 22 | _mm_mfence(); | 46 | FencedRDTSC(); |
| 23 | __rdtsc(); | ||
| 24 | 47 | ||
| 25 | // Get the current time. | 48 | // Get the current time. |
| 26 | const auto start_time = std::chrono::steady_clock::now(); | 49 | const auto start_time = std::chrono::steady_clock::now(); |
| 27 | _mm_mfence(); | 50 | const u64 tsc_start = FencedRDTSC(); |
| 28 | const u64 tsc_start = __rdtsc(); | ||
| 29 | // Wait for 200 milliseconds. | 51 | // Wait for 200 milliseconds. |
| 30 | std::this_thread::sleep_for(std::chrono::milliseconds{200}); | 52 | std::this_thread::sleep_for(std::chrono::milliseconds{200}); |
| 31 | const auto end_time = std::chrono::steady_clock::now(); | 53 | const auto end_time = std::chrono::steady_clock::now(); |
| 32 | _mm_mfence(); | 54 | const u64 tsc_end = FencedRDTSC(); |
| 33 | const u64 tsc_end = __rdtsc(); | ||
| 34 | // Calculate differences. | 55 | // Calculate differences. |
| 35 | const u64 timer_diff = static_cast<u64>( | 56 | const u64 timer_diff = static_cast<u64>( |
| 36 | std::chrono::duration_cast<std::chrono::nanoseconds>(end_time - start_time).count()); | 57 | std::chrono::duration_cast<std::chrono::nanoseconds>(end_time - start_time).count()); |
| @@ -44,8 +65,7 @@ NativeClock::NativeClock(u64 emulated_cpu_frequency_, u64 emulated_clock_frequen | |||
| 44 | u64 rtsc_frequency_) | 65 | u64 rtsc_frequency_) |
| 45 | : WallClock(emulated_cpu_frequency_, emulated_clock_frequency_, true), rtsc_frequency{ | 66 | : WallClock(emulated_cpu_frequency_, emulated_clock_frequency_, true), rtsc_frequency{ |
| 46 | rtsc_frequency_} { | 67 | rtsc_frequency_} { |
| 47 | _mm_mfence(); | 68 | time_point.inner.last_measure = FencedRDTSC(); |
| 48 | time_point.inner.last_measure = __rdtsc(); | ||
| 49 | time_point.inner.accumulated_ticks = 0U; | 69 | time_point.inner.accumulated_ticks = 0U; |
| 50 | ns_rtsc_factor = GetFixedPoint64Factor(NS_RATIO, rtsc_frequency); | 70 | ns_rtsc_factor = GetFixedPoint64Factor(NS_RATIO, rtsc_frequency); |
| 51 | us_rtsc_factor = GetFixedPoint64Factor(US_RATIO, rtsc_frequency); | 71 | us_rtsc_factor = GetFixedPoint64Factor(US_RATIO, rtsc_frequency); |
| @@ -57,10 +77,10 @@ NativeClock::NativeClock(u64 emulated_cpu_frequency_, u64 emulated_clock_frequen | |||
| 57 | u64 NativeClock::GetRTSC() { | 77 | u64 NativeClock::GetRTSC() { |
| 58 | TimePoint new_time_point{}; | 78 | TimePoint new_time_point{}; |
| 59 | TimePoint current_time_point{}; | 79 | TimePoint current_time_point{}; |
| 80 | |||
| 81 | current_time_point.pack = Common::AtomicLoad128(time_point.pack.data()); | ||
| 60 | do { | 82 | do { |
| 61 | current_time_point.pack = time_point.pack; | 83 | const u64 current_measure = FencedRDTSC(); |
| 62 | _mm_mfence(); | ||
| 63 | const u64 current_measure = __rdtsc(); | ||
| 64 | u64 diff = current_measure - current_time_point.inner.last_measure; | 84 | u64 diff = current_measure - current_time_point.inner.last_measure; |
| 65 | diff = diff & ~static_cast<u64>(static_cast<s64>(diff) >> 63); // max(diff, 0) | 85 | diff = diff & ~static_cast<u64>(static_cast<s64>(diff) >> 63); // max(diff, 0) |
| 66 | new_time_point.inner.last_measure = current_measure > current_time_point.inner.last_measure | 86 | new_time_point.inner.last_measure = current_measure > current_time_point.inner.last_measure |
| @@ -68,22 +88,21 @@ u64 NativeClock::GetRTSC() { | |||
| 68 | : current_time_point.inner.last_measure; | 88 | : current_time_point.inner.last_measure; |
| 69 | new_time_point.inner.accumulated_ticks = current_time_point.inner.accumulated_ticks + diff; | 89 | new_time_point.inner.accumulated_ticks = current_time_point.inner.accumulated_ticks + diff; |
| 70 | } while (!Common::AtomicCompareAndSwap(time_point.pack.data(), new_time_point.pack, | 90 | } while (!Common::AtomicCompareAndSwap(time_point.pack.data(), new_time_point.pack, |
| 71 | current_time_point.pack)); | 91 | current_time_point.pack, current_time_point.pack)); |
| 72 | /// The clock cannot be more precise than the guest timer, remove the lower bits | 92 | return new_time_point.inner.accumulated_ticks; |
| 73 | return new_time_point.inner.accumulated_ticks & inaccuracy_mask; | ||
| 74 | } | 93 | } |
| 75 | 94 | ||
| 76 | void NativeClock::Pause(bool is_paused) { | 95 | void NativeClock::Pause(bool is_paused) { |
| 77 | if (!is_paused) { | 96 | if (!is_paused) { |
| 78 | TimePoint current_time_point{}; | 97 | TimePoint current_time_point{}; |
| 79 | TimePoint new_time_point{}; | 98 | TimePoint new_time_point{}; |
| 99 | |||
| 100 | current_time_point.pack = Common::AtomicLoad128(time_point.pack.data()); | ||
| 80 | do { | 101 | do { |
| 81 | current_time_point.pack = time_point.pack; | ||
| 82 | new_time_point.pack = current_time_point.pack; | 102 | new_time_point.pack = current_time_point.pack; |
| 83 | _mm_mfence(); | 103 | new_time_point.inner.last_measure = FencedRDTSC(); |
| 84 | new_time_point.inner.last_measure = __rdtsc(); | ||
| 85 | } while (!Common::AtomicCompareAndSwap(time_point.pack.data(), new_time_point.pack, | 104 | } while (!Common::AtomicCompareAndSwap(time_point.pack.data(), new_time_point.pack, |
| 86 | current_time_point.pack)); | 105 | current_time_point.pack, current_time_point.pack)); |
| 87 | } | 106 | } |
| 88 | } | 107 | } |
| 89 | 108 | ||
diff --git a/src/common/x64/native_clock.h b/src/common/x64/native_clock.h index 7cbd400d2..38ae7a462 100644 --- a/src/common/x64/native_clock.h +++ b/src/common/x64/native_clock.h | |||
| @@ -1,11 +1,8 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | // Refer to the license.txt file included. | ||
| 4 | 3 | ||
| 5 | #pragma once | 4 | #pragma once |
| 6 | 5 | ||
| 7 | #include <optional> | ||
| 8 | |||
| 9 | #include "common/wall_clock.h" | 6 | #include "common/wall_clock.h" |
| 10 | 7 | ||
| 11 | namespace Common { | 8 | namespace Common { |
| @@ -40,12 +37,8 @@ private: | |||
| 40 | } inner; | 37 | } inner; |
| 41 | }; | 38 | }; |
| 42 | 39 | ||
| 43 | /// value used to reduce the native clocks accuracy as some apss rely on | ||
| 44 | /// undefined behavior where the level of accuracy in the clock shouldn't | ||
| 45 | /// be higher. | ||
| 46 | static constexpr u64 inaccuracy_mask = ~(UINT64_C(0x400) - 1); | ||
| 47 | |||
| 48 | TimePoint time_point; | 40 | TimePoint time_point; |
| 41 | |||
| 49 | // factors | 42 | // factors |
| 50 | u64 clock_rtsc_factor{}; | 43 | u64 clock_rtsc_factor{}; |
| 51 | u64 cpu_rtsc_factor{}; | 44 | u64 cpu_rtsc_factor{}; |
diff --git a/src/common/x64/xbyak_abi.h b/src/common/x64/xbyak_abi.h index 87b3d63a4..67e6e63c8 100644 --- a/src/common/x64/xbyak_abi.h +++ b/src/common/x64/xbyak_abi.h | |||
| @@ -1,6 +1,5 @@ | |||
| 1 | // Copyright 2016 Citra Emulator Project | 1 | // SPDX-FileCopyrightText: 2016 Citra Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | // Refer to the license.txt file included. | ||
| 4 | 3 | ||
| 5 | #pragma once | 4 | #pragma once |
| 6 | 5 | ||
diff --git a/src/common/x64/xbyak_util.h b/src/common/x64/xbyak_util.h index 44d2558f1..250e5cddb 100644 --- a/src/common/x64/xbyak_util.h +++ b/src/common/x64/xbyak_util.h | |||
| @@ -1,6 +1,5 @@ | |||
| 1 | // Copyright 2016 Citra Emulator Project | 1 | // SPDX-FileCopyrightText: 2016 Citra Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | // Refer to the license.txt file included. | ||
| 4 | 3 | ||
| 5 | #pragma once | 4 | #pragma once |
| 6 | 5 | ||
diff --git a/src/common/zstd_compression.cpp b/src/common/zstd_compression.cpp index 695b96a43..b71a41b78 100644 --- a/src/common/zstd_compression.cpp +++ b/src/common/zstd_compression.cpp | |||
| @@ -1,6 +1,5 @@ | |||
| 1 | // Copyright 2019 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | // Refer to the license.txt file included. | ||
| 4 | 3 | ||
| 5 | #include <algorithm> | 4 | #include <algorithm> |
| 6 | #include <zstd.h> | 5 | #include <zstd.h> |
diff --git a/src/common/zstd_compression.h b/src/common/zstd_compression.h index bbce14f4e..a5ab2d05b 100644 --- a/src/common/zstd_compression.h +++ b/src/common/zstd_compression.h | |||
| @@ -1,6 +1,5 @@ | |||
| 1 | // Copyright 2019 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | // Refer to the license.txt file included. | ||
| 4 | 3 | ||
| 5 | #pragma once | 4 | #pragma once |
| 6 | 5 | ||