diff options
Diffstat (limited to 'src')
216 files changed, 7431 insertions, 2744 deletions
diff --git a/src/audio_core/common.h b/src/audio_core/common.h index 1ab537588..e6b95769f 100644 --- a/src/audio_core/common.h +++ b/src/audio_core/common.h | |||
| @@ -15,7 +15,9 @@ constexpr ResultCode ERR_INVALID_PARAMETERS{ErrorModule::Audio, 41}; | |||
| 15 | constexpr ResultCode ERR_SPLITTER_SORT_FAILED{ErrorModule::Audio, 43}; | 15 | constexpr ResultCode ERR_SPLITTER_SORT_FAILED{ErrorModule::Audio, 43}; |
| 16 | } // namespace Audren | 16 | } // namespace Audren |
| 17 | 17 | ||
| 18 | constexpr u32_le CURRENT_PROCESS_REVISION = Common::MakeMagic('R', 'E', 'V', '9'); | 18 | constexpr u8 BASE_REVISION = '0'; |
| 19 | constexpr u32_le CURRENT_PROCESS_REVISION = | ||
| 20 | Common::MakeMagic('R', 'E', 'V', static_cast<u8>(BASE_REVISION + 0xA)); | ||
| 19 | constexpr std::size_t MAX_MIX_BUFFERS = 24; | 21 | constexpr std::size_t MAX_MIX_BUFFERS = 24; |
| 20 | constexpr std::size_t MAX_BIQUAD_FILTERS = 2; | 22 | constexpr std::size_t MAX_BIQUAD_FILTERS = 2; |
| 21 | constexpr std::size_t MAX_CHANNEL_COUNT = 6; | 23 | constexpr std::size_t MAX_CHANNEL_COUNT = 6; |
diff --git a/src/common/bit_util.h b/src/common/bit_util.h index f50d3308a..f37538e06 100644 --- a/src/common/bit_util.h +++ b/src/common/bit_util.h | |||
| @@ -57,4 +57,11 @@ requires std::is_integral_v<T> | |||
| 57 | return static_cast<T>(1ULL << ((8U * sizeof(T)) - std::countl_zero(value - 1U))); | 57 | return static_cast<T>(1ULL << ((8U * sizeof(T)) - std::countl_zero(value - 1U))); |
| 58 | } | 58 | } |
| 59 | 59 | ||
| 60 | template <size_t bit_index, typename T> | ||
| 61 | requires std::is_integral_v<T> | ||
| 62 | [[nodiscard]] constexpr bool Bit(const T value) { | ||
| 63 | static_assert(bit_index < BitSize<T>(), "bit_index must be smaller than size of T"); | ||
| 64 | return ((value >> bit_index) & T(1)) == T(1); | ||
| 65 | } | ||
| 66 | |||
| 60 | } // namespace Common | 67 | } // namespace Common |
diff --git a/src/common/common_types.h b/src/common/common_types.h index 4cec89fbd..99bffc460 100644 --- a/src/common/common_types.h +++ b/src/common/common_types.h | |||
| @@ -46,13 +46,3 @@ using GPUVAddr = u64; ///< Represents a pointer in the GPU virtual address space | |||
| 46 | 46 | ||
| 47 | using u128 = std::array<std::uint64_t, 2>; | 47 | using u128 = std::array<std::uint64_t, 2>; |
| 48 | static_assert(sizeof(u128) == 16, "u128 must be 128 bits wide"); | 48 | static_assert(sizeof(u128) == 16, "u128 must be 128 bits wide"); |
| 49 | |||
| 50 | // An inheritable class to disallow the copy constructor and operator= functions | ||
| 51 | class NonCopyable { | ||
| 52 | protected: | ||
| 53 | constexpr NonCopyable() = default; | ||
| 54 | ~NonCopyable() = default; | ||
| 55 | |||
| 56 | NonCopyable(const NonCopyable&) = delete; | ||
| 57 | NonCopyable& operator=(const NonCopyable&) = delete; | ||
| 58 | }; | ||
diff --git a/src/common/fs/fs_util.cpp b/src/common/fs/fs_util.cpp index 9f8671982..0068112e6 100644 --- a/src/common/fs/fs_util.cpp +++ b/src/common/fs/fs_util.cpp | |||
| @@ -16,6 +16,10 @@ std::u8string BufferToU8String(std::span<const u8> buffer) { | |||
| 16 | return std::u8string{buffer.begin(), std::ranges::find(buffer, u8{0})}; | 16 | return std::u8string{buffer.begin(), std::ranges::find(buffer, u8{0})}; |
| 17 | } | 17 | } |
| 18 | 18 | ||
| 19 | std::u8string_view BufferToU8StringView(std::span<const u8> buffer) { | ||
| 20 | return std::u8string_view{reinterpret_cast<const char8_t*>(buffer.data())}; | ||
| 21 | } | ||
| 22 | |||
| 19 | std::string ToUTF8String(std::u8string_view u8_string) { | 23 | std::string ToUTF8String(std::u8string_view u8_string) { |
| 20 | return std::string{u8_string.begin(), u8_string.end()}; | 24 | return std::string{u8_string.begin(), u8_string.end()}; |
| 21 | } | 25 | } |
| @@ -24,6 +28,10 @@ std::string BufferToUTF8String(std::span<const u8> buffer) { | |||
| 24 | return std::string{buffer.begin(), std::ranges::find(buffer, u8{0})}; | 28 | return std::string{buffer.begin(), std::ranges::find(buffer, u8{0})}; |
| 25 | } | 29 | } |
| 26 | 30 | ||
| 31 | std::string_view BufferToUTF8StringView(std::span<const u8> buffer) { | ||
| 32 | return std::string_view{reinterpret_cast<const char*>(buffer.data())}; | ||
| 33 | } | ||
| 34 | |||
| 27 | std::string PathToUTF8String(const std::filesystem::path& path) { | 35 | std::string PathToUTF8String(const std::filesystem::path& path) { |
| 28 | return ToUTF8String(path.u8string()); | 36 | return ToUTF8String(path.u8string()); |
| 29 | } | 37 | } |
diff --git a/src/common/fs/fs_util.h b/src/common/fs/fs_util.h index 1ec82eb35..1620d38c9 100644 --- a/src/common/fs/fs_util.h +++ b/src/common/fs/fs_util.h | |||
| @@ -38,6 +38,15 @@ concept IsChar = std::same_as<T, char>; | |||
| 38 | [[nodiscard]] std::u8string BufferToU8String(std::span<const u8> buffer); | 38 | [[nodiscard]] std::u8string BufferToU8String(std::span<const u8> buffer); |
| 39 | 39 | ||
| 40 | /** | 40 | /** |
| 41 | * Same as BufferToU8String, but returns a string view of the buffer. | ||
| 42 | * | ||
| 43 | * @param buffer Buffer of bytes | ||
| 44 | * | ||
| 45 | * @returns UTF-8 encoded std::u8string_view. | ||
| 46 | */ | ||
| 47 | [[nodiscard]] std::u8string_view BufferToU8StringView(std::span<const u8> buffer); | ||
| 48 | |||
| 49 | /** | ||
| 41 | * Converts a std::u8string or std::u8string_view to a UTF-8 encoded std::string. | 50 | * Converts a std::u8string or std::u8string_view to a UTF-8 encoded std::string. |
| 42 | * | 51 | * |
| 43 | * @param u8_string UTF-8 encoded u8string | 52 | * @param u8_string UTF-8 encoded u8string |
| @@ -58,6 +67,15 @@ concept IsChar = std::same_as<T, char>; | |||
| 58 | [[nodiscard]] std::string BufferToUTF8String(std::span<const u8> buffer); | 67 | [[nodiscard]] std::string BufferToUTF8String(std::span<const u8> buffer); |
| 59 | 68 | ||
| 60 | /** | 69 | /** |
| 70 | * Same as BufferToUTF8String, but returns a string view of the buffer. | ||
| 71 | * | ||
| 72 | * @param buffer Buffer of bytes | ||
| 73 | * | ||
| 74 | * @returns UTF-8 encoded std::string_view. | ||
| 75 | */ | ||
| 76 | [[nodiscard]] std::string_view BufferToUTF8StringView(std::span<const u8> buffer); | ||
| 77 | |||
| 78 | /** | ||
| 61 | * Converts a filesystem path to a UTF-8 encoded std::string. | 79 | * Converts a filesystem path to a UTF-8 encoded std::string. |
| 62 | * | 80 | * |
| 63 | * @param path Filesystem path | 81 | * @param path Filesystem path |
diff --git a/src/common/host_memory.cpp b/src/common/host_memory.cpp index 28949fe5e..c465cfc14 100644 --- a/src/common/host_memory.cpp +++ b/src/common/host_memory.cpp | |||
| @@ -327,8 +327,8 @@ private: | |||
| 327 | bool IsNiechePlaceholder(size_t virtual_offset, size_t length) const { | 327 | bool IsNiechePlaceholder(size_t virtual_offset, size_t length) const { |
| 328 | const auto it = placeholders.upper_bound({virtual_offset, virtual_offset + length}); | 328 | const auto it = placeholders.upper_bound({virtual_offset, virtual_offset + length}); |
| 329 | if (it != placeholders.end() && it->lower() == virtual_offset + length) { | 329 | if (it != placeholders.end() && it->lower() == virtual_offset + length) { |
| 330 | const bool is_root = it == placeholders.begin() && virtual_offset == 0; | 330 | return it == placeholders.begin() ? virtual_offset == 0 |
| 331 | return is_root || std::prev(it)->upper() == virtual_offset; | 331 | : std::prev(it)->upper() == virtual_offset; |
| 332 | } | 332 | } |
| 333 | return false; | 333 | return false; |
| 334 | } | 334 | } |
diff --git a/src/common/intrusive_red_black_tree.h b/src/common/intrusive_red_black_tree.h index 3173cc449..b296b639e 100644 --- a/src/common/intrusive_red_black_tree.h +++ b/src/common/intrusive_red_black_tree.h | |||
| @@ -4,6 +4,8 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include "common/alignment.h" | ||
| 8 | #include "common/common_funcs.h" | ||
| 7 | #include "common/parent_of_member.h" | 9 | #include "common/parent_of_member.h" |
| 8 | #include "common/tree.h" | 10 | #include "common/tree.h" |
| 9 | 11 | ||
| @@ -15,32 +17,33 @@ class IntrusiveRedBlackTreeImpl; | |||
| 15 | 17 | ||
| 16 | } | 18 | } |
| 17 | 19 | ||
| 20 | #pragma pack(push, 4) | ||
| 18 | struct IntrusiveRedBlackTreeNode { | 21 | struct IntrusiveRedBlackTreeNode { |
| 22 | YUZU_NON_COPYABLE(IntrusiveRedBlackTreeNode); | ||
| 23 | |||
| 19 | public: | 24 | public: |
| 20 | using EntryType = RBEntry<IntrusiveRedBlackTreeNode>; | 25 | using RBEntry = freebsd::RBEntry<IntrusiveRedBlackTreeNode>; |
| 21 | 26 | ||
| 22 | constexpr IntrusiveRedBlackTreeNode() = default; | 27 | private: |
| 28 | RBEntry m_entry; | ||
| 23 | 29 | ||
| 24 | void SetEntry(const EntryType& new_entry) { | 30 | public: |
| 25 | entry = new_entry; | 31 | explicit IntrusiveRedBlackTreeNode() = default; |
| 26 | } | ||
| 27 | 32 | ||
| 28 | [[nodiscard]] EntryType& GetEntry() { | 33 | [[nodiscard]] constexpr RBEntry& GetRBEntry() { |
| 29 | return entry; | 34 | return m_entry; |
| 30 | } | 35 | } |
| 31 | 36 | [[nodiscard]] constexpr const RBEntry& GetRBEntry() const { | |
| 32 | [[nodiscard]] const EntryType& GetEntry() const { | 37 | return m_entry; |
| 33 | return entry; | ||
| 34 | } | 38 | } |
| 35 | 39 | ||
| 36 | private: | 40 | constexpr void SetRBEntry(const RBEntry& entry) { |
| 37 | EntryType entry{}; | 41 | m_entry = entry; |
| 38 | 42 | } | |
| 39 | friend class impl::IntrusiveRedBlackTreeImpl; | ||
| 40 | |||
| 41 | template <class, class, class> | ||
| 42 | friend class IntrusiveRedBlackTree; | ||
| 43 | }; | 43 | }; |
| 44 | static_assert(sizeof(IntrusiveRedBlackTreeNode) == | ||
| 45 | 3 * sizeof(void*) + std::max<size_t>(sizeof(freebsd::RBColor), 4)); | ||
| 46 | #pragma pack(pop) | ||
| 44 | 47 | ||
| 45 | template <class T, class Traits, class Comparator> | 48 | template <class T, class Traits, class Comparator> |
| 46 | class IntrusiveRedBlackTree; | 49 | class IntrusiveRedBlackTree; |
| @@ -48,12 +51,17 @@ class IntrusiveRedBlackTree; | |||
| 48 | namespace impl { | 51 | namespace impl { |
| 49 | 52 | ||
| 50 | class IntrusiveRedBlackTreeImpl { | 53 | class IntrusiveRedBlackTreeImpl { |
| 54 | YUZU_NON_COPYABLE(IntrusiveRedBlackTreeImpl); | ||
| 55 | |||
| 51 | private: | 56 | private: |
| 52 | template <class, class, class> | 57 | template <class, class, class> |
| 53 | friend class ::Common::IntrusiveRedBlackTree; | 58 | friend class ::Common::IntrusiveRedBlackTree; |
| 54 | 59 | ||
| 55 | using RootType = RBHead<IntrusiveRedBlackTreeNode>; | 60 | private: |
| 56 | RootType root; | 61 | using RootType = freebsd::RBHead<IntrusiveRedBlackTreeNode>; |
| 62 | |||
| 63 | private: | ||
| 64 | RootType m_root; | ||
| 57 | 65 | ||
| 58 | public: | 66 | public: |
| 59 | template <bool Const> | 67 | template <bool Const> |
| @@ -81,149 +89,150 @@ public: | |||
| 81 | IntrusiveRedBlackTreeImpl::reference>; | 89 | IntrusiveRedBlackTreeImpl::reference>; |
| 82 | 90 | ||
| 83 | private: | 91 | private: |
| 84 | pointer node; | 92 | pointer m_node; |
| 85 | 93 | ||
| 86 | public: | 94 | public: |
| 87 | explicit Iterator(pointer n) : node(n) {} | 95 | constexpr explicit Iterator(pointer n) : m_node(n) {} |
| 88 | 96 | ||
| 89 | bool operator==(const Iterator& rhs) const { | 97 | constexpr bool operator==(const Iterator& rhs) const { |
| 90 | return this->node == rhs.node; | 98 | return m_node == rhs.m_node; |
| 91 | } | 99 | } |
| 92 | 100 | ||
| 93 | bool operator!=(const Iterator& rhs) const { | 101 | constexpr bool operator!=(const Iterator& rhs) const { |
| 94 | return !(*this == rhs); | 102 | return !(*this == rhs); |
| 95 | } | 103 | } |
| 96 | 104 | ||
| 97 | pointer operator->() const { | 105 | constexpr pointer operator->() const { |
| 98 | return this->node; | 106 | return m_node; |
| 99 | } | 107 | } |
| 100 | 108 | ||
| 101 | reference operator*() const { | 109 | constexpr reference operator*() const { |
| 102 | return *this->node; | 110 | return *m_node; |
| 103 | } | 111 | } |
| 104 | 112 | ||
| 105 | Iterator& operator++() { | 113 | constexpr Iterator& operator++() { |
| 106 | this->node = GetNext(this->node); | 114 | m_node = GetNext(m_node); |
| 107 | return *this; | 115 | return *this; |
| 108 | } | 116 | } |
| 109 | 117 | ||
| 110 | Iterator& operator--() { | 118 | constexpr Iterator& operator--() { |
| 111 | this->node = GetPrev(this->node); | 119 | m_node = GetPrev(m_node); |
| 112 | return *this; | 120 | return *this; |
| 113 | } | 121 | } |
| 114 | 122 | ||
| 115 | Iterator operator++(int) { | 123 | constexpr Iterator operator++(int) { |
| 116 | const Iterator it{*this}; | 124 | const Iterator it{*this}; |
| 117 | ++(*this); | 125 | ++(*this); |
| 118 | return it; | 126 | return it; |
| 119 | } | 127 | } |
| 120 | 128 | ||
| 121 | Iterator operator--(int) { | 129 | constexpr Iterator operator--(int) { |
| 122 | const Iterator it{*this}; | 130 | const Iterator it{*this}; |
| 123 | --(*this); | 131 | --(*this); |
| 124 | return it; | 132 | return it; |
| 125 | } | 133 | } |
| 126 | 134 | ||
| 127 | operator Iterator<true>() const { | 135 | constexpr operator Iterator<true>() const { |
| 128 | return Iterator<true>(this->node); | 136 | return Iterator<true>(m_node); |
| 129 | } | 137 | } |
| 130 | }; | 138 | }; |
| 131 | 139 | ||
| 132 | private: | 140 | private: |
| 133 | // Define accessors using RB_* functions. | 141 | constexpr bool EmptyImpl() const { |
| 134 | bool EmptyImpl() const { | 142 | return m_root.IsEmpty(); |
| 135 | return root.IsEmpty(); | ||
| 136 | } | 143 | } |
| 137 | 144 | ||
| 138 | IntrusiveRedBlackTreeNode* GetMinImpl() const { | 145 | constexpr IntrusiveRedBlackTreeNode* GetMinImpl() const { |
| 139 | return RB_MIN(const_cast<RootType*>(&root)); | 146 | return freebsd::RB_MIN(const_cast<RootType&>(m_root)); |
| 140 | } | 147 | } |
| 141 | 148 | ||
| 142 | IntrusiveRedBlackTreeNode* GetMaxImpl() const { | 149 | constexpr IntrusiveRedBlackTreeNode* GetMaxImpl() const { |
| 143 | return RB_MAX(const_cast<RootType*>(&root)); | 150 | return freebsd::RB_MAX(const_cast<RootType&>(m_root)); |
| 144 | } | 151 | } |
| 145 | 152 | ||
| 146 | IntrusiveRedBlackTreeNode* RemoveImpl(IntrusiveRedBlackTreeNode* node) { | 153 | constexpr IntrusiveRedBlackTreeNode* RemoveImpl(IntrusiveRedBlackTreeNode* node) { |
| 147 | return RB_REMOVE(&root, node); | 154 | return freebsd::RB_REMOVE(m_root, node); |
| 148 | } | 155 | } |
| 149 | 156 | ||
| 150 | public: | 157 | public: |
| 151 | static IntrusiveRedBlackTreeNode* GetNext(IntrusiveRedBlackTreeNode* node) { | 158 | static constexpr IntrusiveRedBlackTreeNode* GetNext(IntrusiveRedBlackTreeNode* node) { |
| 152 | return RB_NEXT(node); | 159 | return freebsd::RB_NEXT(node); |
| 153 | } | 160 | } |
| 154 | 161 | ||
| 155 | static IntrusiveRedBlackTreeNode* GetPrev(IntrusiveRedBlackTreeNode* node) { | 162 | static constexpr IntrusiveRedBlackTreeNode* GetPrev(IntrusiveRedBlackTreeNode* node) { |
| 156 | return RB_PREV(node); | 163 | return freebsd::RB_PREV(node); |
| 157 | } | 164 | } |
| 158 | 165 | ||
| 159 | static const IntrusiveRedBlackTreeNode* GetNext(const IntrusiveRedBlackTreeNode* node) { | 166 | static constexpr IntrusiveRedBlackTreeNode const* GetNext( |
| 167 | IntrusiveRedBlackTreeNode const* node) { | ||
| 160 | return static_cast<const IntrusiveRedBlackTreeNode*>( | 168 | return static_cast<const IntrusiveRedBlackTreeNode*>( |
| 161 | GetNext(const_cast<IntrusiveRedBlackTreeNode*>(node))); | 169 | GetNext(const_cast<IntrusiveRedBlackTreeNode*>(node))); |
| 162 | } | 170 | } |
| 163 | 171 | ||
| 164 | static const IntrusiveRedBlackTreeNode* GetPrev(const IntrusiveRedBlackTreeNode* node) { | 172 | static constexpr IntrusiveRedBlackTreeNode const* GetPrev( |
| 173 | IntrusiveRedBlackTreeNode const* node) { | ||
| 165 | return static_cast<const IntrusiveRedBlackTreeNode*>( | 174 | return static_cast<const IntrusiveRedBlackTreeNode*>( |
| 166 | GetPrev(const_cast<IntrusiveRedBlackTreeNode*>(node))); | 175 | GetPrev(const_cast<IntrusiveRedBlackTreeNode*>(node))); |
| 167 | } | 176 | } |
| 168 | 177 | ||
| 169 | public: | 178 | public: |
| 170 | constexpr IntrusiveRedBlackTreeImpl() {} | 179 | constexpr IntrusiveRedBlackTreeImpl() = default; |
| 171 | 180 | ||
| 172 | // Iterator accessors. | 181 | // Iterator accessors. |
| 173 | iterator begin() { | 182 | constexpr iterator begin() { |
| 174 | return iterator(this->GetMinImpl()); | 183 | return iterator(this->GetMinImpl()); |
| 175 | } | 184 | } |
| 176 | 185 | ||
| 177 | const_iterator begin() const { | 186 | constexpr const_iterator begin() const { |
| 178 | return const_iterator(this->GetMinImpl()); | 187 | return const_iterator(this->GetMinImpl()); |
| 179 | } | 188 | } |
| 180 | 189 | ||
| 181 | iterator end() { | 190 | constexpr iterator end() { |
| 182 | return iterator(static_cast<IntrusiveRedBlackTreeNode*>(nullptr)); | 191 | return iterator(static_cast<IntrusiveRedBlackTreeNode*>(nullptr)); |
| 183 | } | 192 | } |
| 184 | 193 | ||
| 185 | const_iterator end() const { | 194 | constexpr const_iterator end() const { |
| 186 | return const_iterator(static_cast<const IntrusiveRedBlackTreeNode*>(nullptr)); | 195 | return const_iterator(static_cast<const IntrusiveRedBlackTreeNode*>(nullptr)); |
| 187 | } | 196 | } |
| 188 | 197 | ||
| 189 | const_iterator cbegin() const { | 198 | constexpr const_iterator cbegin() const { |
| 190 | return this->begin(); | 199 | return this->begin(); |
| 191 | } | 200 | } |
| 192 | 201 | ||
| 193 | const_iterator cend() const { | 202 | constexpr const_iterator cend() const { |
| 194 | return this->end(); | 203 | return this->end(); |
| 195 | } | 204 | } |
| 196 | 205 | ||
| 197 | iterator iterator_to(reference ref) { | 206 | constexpr iterator iterator_to(reference ref) { |
| 198 | return iterator(&ref); | 207 | return iterator(std::addressof(ref)); |
| 199 | } | 208 | } |
| 200 | 209 | ||
| 201 | const_iterator iterator_to(const_reference ref) const { | 210 | constexpr const_iterator iterator_to(const_reference ref) const { |
| 202 | return const_iterator(&ref); | 211 | return const_iterator(std::addressof(ref)); |
| 203 | } | 212 | } |
| 204 | 213 | ||
| 205 | // Content management. | 214 | // Content management. |
| 206 | bool empty() const { | 215 | constexpr bool empty() const { |
| 207 | return this->EmptyImpl(); | 216 | return this->EmptyImpl(); |
| 208 | } | 217 | } |
| 209 | 218 | ||
| 210 | reference back() { | 219 | constexpr reference back() { |
| 211 | return *this->GetMaxImpl(); | 220 | return *this->GetMaxImpl(); |
| 212 | } | 221 | } |
| 213 | 222 | ||
| 214 | const_reference back() const { | 223 | constexpr const_reference back() const { |
| 215 | return *this->GetMaxImpl(); | 224 | return *this->GetMaxImpl(); |
| 216 | } | 225 | } |
| 217 | 226 | ||
| 218 | reference front() { | 227 | constexpr reference front() { |
| 219 | return *this->GetMinImpl(); | 228 | return *this->GetMinImpl(); |
| 220 | } | 229 | } |
| 221 | 230 | ||
| 222 | const_reference front() const { | 231 | constexpr const_reference front() const { |
| 223 | return *this->GetMinImpl(); | 232 | return *this->GetMinImpl(); |
| 224 | } | 233 | } |
| 225 | 234 | ||
| 226 | iterator erase(iterator it) { | 235 | constexpr iterator erase(iterator it) { |
| 227 | auto cur = std::addressof(*it); | 236 | auto cur = std::addressof(*it); |
| 228 | auto next = GetNext(cur); | 237 | auto next = GetNext(cur); |
| 229 | this->RemoveImpl(cur); | 238 | this->RemoveImpl(cur); |
| @@ -234,16 +243,16 @@ public: | |||
| 234 | } // namespace impl | 243 | } // namespace impl |
| 235 | 244 | ||
| 236 | template <typename T> | 245 | template <typename T> |
| 237 | concept HasLightCompareType = requires { | 246 | concept HasRedBlackKeyType = requires { |
| 238 | { std::is_same<typename T::LightCompareType, void>::value } -> std::convertible_to<bool>; | 247 | { std::is_same<typename T::RedBlackKeyType, void>::value } -> std::convertible_to<bool>; |
| 239 | }; | 248 | }; |
| 240 | 249 | ||
| 241 | namespace impl { | 250 | namespace impl { |
| 242 | 251 | ||
| 243 | template <typename T, typename Default> | 252 | template <typename T, typename Default> |
| 244 | consteval auto* GetLightCompareType() { | 253 | consteval auto* GetRedBlackKeyType() { |
| 245 | if constexpr (HasLightCompareType<T>) { | 254 | if constexpr (HasRedBlackKeyType<T>) { |
| 246 | return static_cast<typename T::LightCompareType*>(nullptr); | 255 | return static_cast<typename T::RedBlackKeyType*>(nullptr); |
| 247 | } else { | 256 | } else { |
| 248 | return static_cast<Default*>(nullptr); | 257 | return static_cast<Default*>(nullptr); |
| 249 | } | 258 | } |
| @@ -252,16 +261,17 @@ namespace impl { | |||
| 252 | } // namespace impl | 261 | } // namespace impl |
| 253 | 262 | ||
| 254 | template <typename T, typename Default> | 263 | template <typename T, typename Default> |
| 255 | using LightCompareType = std::remove_pointer_t<decltype(impl::GetLightCompareType<T, Default>())>; | 264 | using RedBlackKeyType = std::remove_pointer_t<decltype(impl::GetRedBlackKeyType<T, Default>())>; |
| 256 | 265 | ||
| 257 | template <class T, class Traits, class Comparator> | 266 | template <class T, class Traits, class Comparator> |
| 258 | class IntrusiveRedBlackTree { | 267 | class IntrusiveRedBlackTree { |
| 268 | YUZU_NON_COPYABLE(IntrusiveRedBlackTree); | ||
| 259 | 269 | ||
| 260 | public: | 270 | public: |
| 261 | using ImplType = impl::IntrusiveRedBlackTreeImpl; | 271 | using ImplType = impl::IntrusiveRedBlackTreeImpl; |
| 262 | 272 | ||
| 263 | private: | 273 | private: |
| 264 | ImplType impl{}; | 274 | ImplType m_impl; |
| 265 | 275 | ||
| 266 | public: | 276 | public: |
| 267 | template <bool Const> | 277 | template <bool Const> |
| @@ -277,9 +287,9 @@ public: | |||
| 277 | using iterator = Iterator<false>; | 287 | using iterator = Iterator<false>; |
| 278 | using const_iterator = Iterator<true>; | 288 | using const_iterator = Iterator<true>; |
| 279 | 289 | ||
| 280 | using light_value_type = LightCompareType<Comparator, value_type>; | 290 | using key_type = RedBlackKeyType<Comparator, value_type>; |
| 281 | using const_light_pointer = const light_value_type*; | 291 | using const_key_pointer = const key_type*; |
| 282 | using const_light_reference = const light_value_type&; | 292 | using const_key_reference = const key_type&; |
| 283 | 293 | ||
| 284 | template <bool Const> | 294 | template <bool Const> |
| 285 | class Iterator { | 295 | class Iterator { |
| @@ -298,183 +308,201 @@ public: | |||
| 298 | IntrusiveRedBlackTree::reference>; | 308 | IntrusiveRedBlackTree::reference>; |
| 299 | 309 | ||
| 300 | private: | 310 | private: |
| 301 | ImplIterator iterator; | 311 | ImplIterator m_impl; |
| 302 | 312 | ||
| 303 | private: | 313 | private: |
| 304 | explicit Iterator(ImplIterator it) : iterator(it) {} | 314 | constexpr explicit Iterator(ImplIterator it) : m_impl(it) {} |
| 305 | 315 | ||
| 306 | explicit Iterator(typename std::conditional<Const, ImplType::const_iterator, | 316 | constexpr explicit Iterator(typename ImplIterator::pointer p) : m_impl(p) {} |
| 307 | ImplType::iterator>::type::pointer ptr) | ||
| 308 | : iterator(ptr) {} | ||
| 309 | 317 | ||
| 310 | ImplIterator GetImplIterator() const { | 318 | constexpr ImplIterator GetImplIterator() const { |
| 311 | return this->iterator; | 319 | return m_impl; |
| 312 | } | 320 | } |
| 313 | 321 | ||
| 314 | public: | 322 | public: |
| 315 | bool operator==(const Iterator& rhs) const { | 323 | constexpr bool operator==(const Iterator& rhs) const { |
| 316 | return this->iterator == rhs.iterator; | 324 | return m_impl == rhs.m_impl; |
| 317 | } | 325 | } |
| 318 | 326 | ||
| 319 | bool operator!=(const Iterator& rhs) const { | 327 | constexpr bool operator!=(const Iterator& rhs) const { |
| 320 | return !(*this == rhs); | 328 | return !(*this == rhs); |
| 321 | } | 329 | } |
| 322 | 330 | ||
| 323 | pointer operator->() const { | 331 | constexpr pointer operator->() const { |
| 324 | return Traits::GetParent(std::addressof(*this->iterator)); | 332 | return Traits::GetParent(std::addressof(*m_impl)); |
| 325 | } | 333 | } |
| 326 | 334 | ||
| 327 | reference operator*() const { | 335 | constexpr reference operator*() const { |
| 328 | return *Traits::GetParent(std::addressof(*this->iterator)); | 336 | return *Traits::GetParent(std::addressof(*m_impl)); |
| 329 | } | 337 | } |
| 330 | 338 | ||
| 331 | Iterator& operator++() { | 339 | constexpr Iterator& operator++() { |
| 332 | ++this->iterator; | 340 | ++m_impl; |
| 333 | return *this; | 341 | return *this; |
| 334 | } | 342 | } |
| 335 | 343 | ||
| 336 | Iterator& operator--() { | 344 | constexpr Iterator& operator--() { |
| 337 | --this->iterator; | 345 | --m_impl; |
| 338 | return *this; | 346 | return *this; |
| 339 | } | 347 | } |
| 340 | 348 | ||
| 341 | Iterator operator++(int) { | 349 | constexpr Iterator operator++(int) { |
| 342 | const Iterator it{*this}; | 350 | const Iterator it{*this}; |
| 343 | ++this->iterator; | 351 | ++m_impl; |
| 344 | return it; | 352 | return it; |
| 345 | } | 353 | } |
| 346 | 354 | ||
| 347 | Iterator operator--(int) { | 355 | constexpr Iterator operator--(int) { |
| 348 | const Iterator it{*this}; | 356 | const Iterator it{*this}; |
| 349 | --this->iterator; | 357 | --m_impl; |
| 350 | return it; | 358 | return it; |
| 351 | } | 359 | } |
| 352 | 360 | ||
| 353 | operator Iterator<true>() const { | 361 | constexpr operator Iterator<true>() const { |
| 354 | return Iterator<true>(this->iterator); | 362 | return Iterator<true>(m_impl); |
| 355 | } | 363 | } |
| 356 | }; | 364 | }; |
| 357 | 365 | ||
| 358 | private: | 366 | private: |
| 359 | static int CompareImpl(const IntrusiveRedBlackTreeNode* lhs, | 367 | static constexpr int CompareImpl(const IntrusiveRedBlackTreeNode* lhs, |
| 360 | const IntrusiveRedBlackTreeNode* rhs) { | 368 | const IntrusiveRedBlackTreeNode* rhs) { |
| 361 | return Comparator::Compare(*Traits::GetParent(lhs), *Traits::GetParent(rhs)); | 369 | return Comparator::Compare(*Traits::GetParent(lhs), *Traits::GetParent(rhs)); |
| 362 | } | 370 | } |
| 363 | 371 | ||
| 364 | static int LightCompareImpl(const void* elm, const IntrusiveRedBlackTreeNode* rhs) { | 372 | static constexpr int CompareKeyImpl(const_key_reference key, |
| 365 | return Comparator::Compare(*static_cast<const_light_pointer>(elm), *Traits::GetParent(rhs)); | 373 | const IntrusiveRedBlackTreeNode* rhs) { |
| 374 | return Comparator::Compare(key, *Traits::GetParent(rhs)); | ||
| 366 | } | 375 | } |
| 367 | 376 | ||
| 368 | // Define accessors using RB_* functions. | 377 | // Define accessors using RB_* functions. |
| 369 | IntrusiveRedBlackTreeNode* InsertImpl(IntrusiveRedBlackTreeNode* node) { | 378 | constexpr IntrusiveRedBlackTreeNode* InsertImpl(IntrusiveRedBlackTreeNode* node) { |
| 370 | return RB_INSERT(&impl.root, node, CompareImpl); | 379 | return freebsd::RB_INSERT(m_impl.m_root, node, CompareImpl); |
| 371 | } | 380 | } |
| 372 | 381 | ||
| 373 | IntrusiveRedBlackTreeNode* FindImpl(const IntrusiveRedBlackTreeNode* node) const { | 382 | constexpr IntrusiveRedBlackTreeNode* FindImpl(IntrusiveRedBlackTreeNode const* node) const { |
| 374 | return RB_FIND(const_cast<ImplType::RootType*>(&impl.root), | 383 | return freebsd::RB_FIND(const_cast<ImplType::RootType&>(m_impl.m_root), |
| 375 | const_cast<IntrusiveRedBlackTreeNode*>(node), CompareImpl); | 384 | const_cast<IntrusiveRedBlackTreeNode*>(node), CompareImpl); |
| 376 | } | 385 | } |
| 377 | 386 | ||
| 378 | IntrusiveRedBlackTreeNode* NFindImpl(const IntrusiveRedBlackTreeNode* node) const { | 387 | constexpr IntrusiveRedBlackTreeNode* NFindImpl(IntrusiveRedBlackTreeNode const* node) const { |
| 379 | return RB_NFIND(const_cast<ImplType::RootType*>(&impl.root), | 388 | return freebsd::RB_NFIND(const_cast<ImplType::RootType&>(m_impl.m_root), |
| 380 | const_cast<IntrusiveRedBlackTreeNode*>(node), CompareImpl); | 389 | const_cast<IntrusiveRedBlackTreeNode*>(node), CompareImpl); |
| 381 | } | 390 | } |
| 382 | 391 | ||
| 383 | IntrusiveRedBlackTreeNode* FindLightImpl(const_light_pointer lelm) const { | 392 | constexpr IntrusiveRedBlackTreeNode* FindKeyImpl(const_key_reference key) const { |
| 384 | return RB_FIND_LIGHT(const_cast<ImplType::RootType*>(&impl.root), | 393 | return freebsd::RB_FIND_KEY(const_cast<ImplType::RootType&>(m_impl.m_root), key, |
| 385 | static_cast<const void*>(lelm), LightCompareImpl); | 394 | CompareKeyImpl); |
| 386 | } | 395 | } |
| 387 | 396 | ||
| 388 | IntrusiveRedBlackTreeNode* NFindLightImpl(const_light_pointer lelm) const { | 397 | constexpr IntrusiveRedBlackTreeNode* NFindKeyImpl(const_key_reference key) const { |
| 389 | return RB_NFIND_LIGHT(const_cast<ImplType::RootType*>(&impl.root), | 398 | return freebsd::RB_NFIND_KEY(const_cast<ImplType::RootType&>(m_impl.m_root), key, |
| 390 | static_cast<const void*>(lelm), LightCompareImpl); | 399 | CompareKeyImpl); |
| 400 | } | ||
| 401 | |||
| 402 | constexpr IntrusiveRedBlackTreeNode* FindExistingImpl( | ||
| 403 | IntrusiveRedBlackTreeNode const* node) const { | ||
| 404 | return freebsd::RB_FIND_EXISTING(const_cast<ImplType::RootType&>(m_impl.m_root), | ||
| 405 | const_cast<IntrusiveRedBlackTreeNode*>(node), CompareImpl); | ||
| 406 | } | ||
| 407 | |||
| 408 | constexpr IntrusiveRedBlackTreeNode* FindExistingKeyImpl(const_key_reference key) const { | ||
| 409 | return freebsd::RB_FIND_EXISTING_KEY(const_cast<ImplType::RootType&>(m_impl.m_root), key, | ||
| 410 | CompareKeyImpl); | ||
| 391 | } | 411 | } |
| 392 | 412 | ||
| 393 | public: | 413 | public: |
| 394 | constexpr IntrusiveRedBlackTree() = default; | 414 | constexpr IntrusiveRedBlackTree() = default; |
| 395 | 415 | ||
| 396 | // Iterator accessors. | 416 | // Iterator accessors. |
| 397 | iterator begin() { | 417 | constexpr iterator begin() { |
| 398 | return iterator(this->impl.begin()); | 418 | return iterator(m_impl.begin()); |
| 399 | } | 419 | } |
| 400 | 420 | ||
| 401 | const_iterator begin() const { | 421 | constexpr const_iterator begin() const { |
| 402 | return const_iterator(this->impl.begin()); | 422 | return const_iterator(m_impl.begin()); |
| 403 | } | 423 | } |
| 404 | 424 | ||
| 405 | iterator end() { | 425 | constexpr iterator end() { |
| 406 | return iterator(this->impl.end()); | 426 | return iterator(m_impl.end()); |
| 407 | } | 427 | } |
| 408 | 428 | ||
| 409 | const_iterator end() const { | 429 | constexpr const_iterator end() const { |
| 410 | return const_iterator(this->impl.end()); | 430 | return const_iterator(m_impl.end()); |
| 411 | } | 431 | } |
| 412 | 432 | ||
| 413 | const_iterator cbegin() const { | 433 | constexpr const_iterator cbegin() const { |
| 414 | return this->begin(); | 434 | return this->begin(); |
| 415 | } | 435 | } |
| 416 | 436 | ||
| 417 | const_iterator cend() const { | 437 | constexpr const_iterator cend() const { |
| 418 | return this->end(); | 438 | return this->end(); |
| 419 | } | 439 | } |
| 420 | 440 | ||
| 421 | iterator iterator_to(reference ref) { | 441 | constexpr iterator iterator_to(reference ref) { |
| 422 | return iterator(this->impl.iterator_to(*Traits::GetNode(std::addressof(ref)))); | 442 | return iterator(m_impl.iterator_to(*Traits::GetNode(std::addressof(ref)))); |
| 423 | } | 443 | } |
| 424 | 444 | ||
| 425 | const_iterator iterator_to(const_reference ref) const { | 445 | constexpr const_iterator iterator_to(const_reference ref) const { |
| 426 | return const_iterator(this->impl.iterator_to(*Traits::GetNode(std::addressof(ref)))); | 446 | return const_iterator(m_impl.iterator_to(*Traits::GetNode(std::addressof(ref)))); |
| 427 | } | 447 | } |
| 428 | 448 | ||
| 429 | // Content management. | 449 | // Content management. |
| 430 | bool empty() const { | 450 | constexpr bool empty() const { |
| 431 | return this->impl.empty(); | 451 | return m_impl.empty(); |
| 432 | } | 452 | } |
| 433 | 453 | ||
| 434 | reference back() { | 454 | constexpr reference back() { |
| 435 | return *Traits::GetParent(std::addressof(this->impl.back())); | 455 | return *Traits::GetParent(std::addressof(m_impl.back())); |
| 436 | } | 456 | } |
| 437 | 457 | ||
| 438 | const_reference back() const { | 458 | constexpr const_reference back() const { |
| 439 | return *Traits::GetParent(std::addressof(this->impl.back())); | 459 | return *Traits::GetParent(std::addressof(m_impl.back())); |
| 440 | } | 460 | } |
| 441 | 461 | ||
| 442 | reference front() { | 462 | constexpr reference front() { |
| 443 | return *Traits::GetParent(std::addressof(this->impl.front())); | 463 | return *Traits::GetParent(std::addressof(m_impl.front())); |
| 444 | } | 464 | } |
| 445 | 465 | ||
| 446 | const_reference front() const { | 466 | constexpr const_reference front() const { |
| 447 | return *Traits::GetParent(std::addressof(this->impl.front())); | 467 | return *Traits::GetParent(std::addressof(m_impl.front())); |
| 448 | } | 468 | } |
| 449 | 469 | ||
| 450 | iterator erase(iterator it) { | 470 | constexpr iterator erase(iterator it) { |
| 451 | return iterator(this->impl.erase(it.GetImplIterator())); | 471 | return iterator(m_impl.erase(it.GetImplIterator())); |
| 452 | } | 472 | } |
| 453 | 473 | ||
| 454 | iterator insert(reference ref) { | 474 | constexpr iterator insert(reference ref) { |
| 455 | ImplType::pointer node = Traits::GetNode(std::addressof(ref)); | 475 | ImplType::pointer node = Traits::GetNode(std::addressof(ref)); |
| 456 | this->InsertImpl(node); | 476 | this->InsertImpl(node); |
| 457 | return iterator(node); | 477 | return iterator(node); |
| 458 | } | 478 | } |
| 459 | 479 | ||
| 460 | iterator find(const_reference ref) const { | 480 | constexpr iterator find(const_reference ref) const { |
| 461 | return iterator(this->FindImpl(Traits::GetNode(std::addressof(ref)))); | 481 | return iterator(this->FindImpl(Traits::GetNode(std::addressof(ref)))); |
| 462 | } | 482 | } |
| 463 | 483 | ||
| 464 | iterator nfind(const_reference ref) const { | 484 | constexpr iterator nfind(const_reference ref) const { |
| 465 | return iterator(this->NFindImpl(Traits::GetNode(std::addressof(ref)))); | 485 | return iterator(this->NFindImpl(Traits::GetNode(std::addressof(ref)))); |
| 466 | } | 486 | } |
| 467 | 487 | ||
| 468 | iterator find_light(const_light_reference ref) const { | 488 | constexpr iterator find_key(const_key_reference ref) const { |
| 469 | return iterator(this->FindLightImpl(std::addressof(ref))); | 489 | return iterator(this->FindKeyImpl(ref)); |
| 490 | } | ||
| 491 | |||
| 492 | constexpr iterator nfind_key(const_key_reference ref) const { | ||
| 493 | return iterator(this->NFindKeyImpl(ref)); | ||
| 494 | } | ||
| 495 | |||
| 496 | constexpr iterator find_existing(const_reference ref) const { | ||
| 497 | return iterator(this->FindExistingImpl(Traits::GetNode(std::addressof(ref)))); | ||
| 470 | } | 498 | } |
| 471 | 499 | ||
| 472 | iterator nfind_light(const_light_reference ref) const { | 500 | constexpr iterator find_existing_key(const_key_reference ref) const { |
| 473 | return iterator(this->NFindLightImpl(std::addressof(ref))); | 501 | return iterator(this->FindExistingKeyImpl(ref)); |
| 474 | } | 502 | } |
| 475 | }; | 503 | }; |
| 476 | 504 | ||
| 477 | template <auto T, class Derived = impl::GetParentType<T>> | 505 | template <auto T, class Derived = Common::impl::GetParentType<T>> |
| 478 | class IntrusiveRedBlackTreeMemberTraits; | 506 | class IntrusiveRedBlackTreeMemberTraits; |
| 479 | 507 | ||
| 480 | template <class Parent, IntrusiveRedBlackTreeNode Parent::*Member, class Derived> | 508 | template <class Parent, IntrusiveRedBlackTreeNode Parent::*Member, class Derived> |
| @@ -498,19 +526,16 @@ private: | |||
| 498 | return std::addressof(parent->*Member); | 526 | return std::addressof(parent->*Member); |
| 499 | } | 527 | } |
| 500 | 528 | ||
| 501 | static constexpr Derived* GetParent(IntrusiveRedBlackTreeNode* node) { | 529 | static Derived* GetParent(IntrusiveRedBlackTreeNode* node) { |
| 502 | return GetParentPointer<Member, Derived>(node); | 530 | return Common::GetParentPointer<Member, Derived>(node); |
| 503 | } | 531 | } |
| 504 | 532 | ||
| 505 | static constexpr Derived const* GetParent(const IntrusiveRedBlackTreeNode* node) { | 533 | static Derived const* GetParent(IntrusiveRedBlackTreeNode const* node) { |
| 506 | return GetParentPointer<Member, Derived>(node); | 534 | return Common::GetParentPointer<Member, Derived>(node); |
| 507 | } | 535 | } |
| 508 | |||
| 509 | private: | ||
| 510 | static constexpr TypedStorage<Derived> DerivedStorage = {}; | ||
| 511 | }; | 536 | }; |
| 512 | 537 | ||
| 513 | template <auto T, class Derived = impl::GetParentType<T>> | 538 | template <auto T, class Derived = Common::impl::GetParentType<T>> |
| 514 | class IntrusiveRedBlackTreeMemberTraitsDeferredAssert; | 539 | class IntrusiveRedBlackTreeMemberTraitsDeferredAssert; |
| 515 | 540 | ||
| 516 | template <class Parent, IntrusiveRedBlackTreeNode Parent::*Member, class Derived> | 541 | template <class Parent, IntrusiveRedBlackTreeNode Parent::*Member, class Derived> |
| @@ -521,11 +546,6 @@ public: | |||
| 521 | IntrusiveRedBlackTree<Derived, IntrusiveRedBlackTreeMemberTraitsDeferredAssert, Comparator>; | 546 | IntrusiveRedBlackTree<Derived, IntrusiveRedBlackTreeMemberTraitsDeferredAssert, Comparator>; |
| 522 | using TreeTypeImpl = impl::IntrusiveRedBlackTreeImpl; | 547 | using TreeTypeImpl = impl::IntrusiveRedBlackTreeImpl; |
| 523 | 548 | ||
| 524 | static constexpr bool IsValid() { | ||
| 525 | TypedStorage<Derived> DerivedStorage = {}; | ||
| 526 | return GetParent(GetNode(GetPointer(DerivedStorage))) == GetPointer(DerivedStorage); | ||
| 527 | } | ||
| 528 | |||
| 529 | private: | 549 | private: |
| 530 | template <class, class, class> | 550 | template <class, class, class> |
| 531 | friend class IntrusiveRedBlackTree; | 551 | friend class IntrusiveRedBlackTree; |
| @@ -540,30 +560,36 @@ private: | |||
| 540 | return std::addressof(parent->*Member); | 560 | return std::addressof(parent->*Member); |
| 541 | } | 561 | } |
| 542 | 562 | ||
| 543 | static constexpr Derived* GetParent(IntrusiveRedBlackTreeNode* node) { | 563 | static Derived* GetParent(IntrusiveRedBlackTreeNode* node) { |
| 544 | return GetParentPointer<Member, Derived>(node); | 564 | return Common::GetParentPointer<Member, Derived>(node); |
| 545 | } | 565 | } |
| 546 | 566 | ||
| 547 | static constexpr Derived const* GetParent(const IntrusiveRedBlackTreeNode* node) { | 567 | static Derived const* GetParent(IntrusiveRedBlackTreeNode const* node) { |
| 548 | return GetParentPointer<Member, Derived>(node); | 568 | return Common::GetParentPointer<Member, Derived>(node); |
| 549 | } | 569 | } |
| 550 | }; | 570 | }; |
| 551 | 571 | ||
| 552 | template <class Derived> | 572 | template <class Derived> |
| 553 | class IntrusiveRedBlackTreeBaseNode : public IntrusiveRedBlackTreeNode { | 573 | class alignas(void*) IntrusiveRedBlackTreeBaseNode : public IntrusiveRedBlackTreeNode { |
| 554 | public: | 574 | public: |
| 575 | using IntrusiveRedBlackTreeNode::IntrusiveRedBlackTreeNode; | ||
| 576 | |||
| 555 | constexpr Derived* GetPrev() { | 577 | constexpr Derived* GetPrev() { |
| 556 | return static_cast<Derived*>(impl::IntrusiveRedBlackTreeImpl::GetPrev(this)); | 578 | return static_cast<Derived*>(static_cast<IntrusiveRedBlackTreeBaseNode*>( |
| 579 | impl::IntrusiveRedBlackTreeImpl::GetPrev(this))); | ||
| 557 | } | 580 | } |
| 558 | constexpr const Derived* GetPrev() const { | 581 | constexpr const Derived* GetPrev() const { |
| 559 | return static_cast<const Derived*>(impl::IntrusiveRedBlackTreeImpl::GetPrev(this)); | 582 | return static_cast<const Derived*>(static_cast<const IntrusiveRedBlackTreeBaseNode*>( |
| 583 | impl::IntrusiveRedBlackTreeImpl::GetPrev(this))); | ||
| 560 | } | 584 | } |
| 561 | 585 | ||
| 562 | constexpr Derived* GetNext() { | 586 | constexpr Derived* GetNext() { |
| 563 | return static_cast<Derived*>(impl::IntrusiveRedBlackTreeImpl::GetNext(this)); | 587 | return static_cast<Derived*>(static_cast<IntrusiveRedBlackTreeBaseNode*>( |
| 588 | impl::IntrusiveRedBlackTreeImpl::GetNext(this))); | ||
| 564 | } | 589 | } |
| 565 | constexpr const Derived* GetNext() const { | 590 | constexpr const Derived* GetNext() const { |
| 566 | return static_cast<const Derived*>(impl::IntrusiveRedBlackTreeImpl::GetNext(this)); | 591 | return static_cast<const Derived*>(static_cast<const IntrusiveRedBlackTreeBaseNode*>( |
| 592 | impl::IntrusiveRedBlackTreeImpl::GetNext(this))); | ||
| 567 | } | 593 | } |
| 568 | }; | 594 | }; |
| 569 | 595 | ||
| @@ -581,19 +607,22 @@ private: | |||
| 581 | friend class impl::IntrusiveRedBlackTreeImpl; | 607 | friend class impl::IntrusiveRedBlackTreeImpl; |
| 582 | 608 | ||
| 583 | static constexpr IntrusiveRedBlackTreeNode* GetNode(Derived* parent) { | 609 | static constexpr IntrusiveRedBlackTreeNode* GetNode(Derived* parent) { |
| 584 | return static_cast<IntrusiveRedBlackTreeNode*>(parent); | 610 | return static_cast<IntrusiveRedBlackTreeNode*>( |
| 611 | static_cast<IntrusiveRedBlackTreeBaseNode<Derived>*>(parent)); | ||
| 585 | } | 612 | } |
| 586 | 613 | ||
| 587 | static constexpr IntrusiveRedBlackTreeNode const* GetNode(Derived const* parent) { | 614 | static constexpr IntrusiveRedBlackTreeNode const* GetNode(Derived const* parent) { |
| 588 | return static_cast<const IntrusiveRedBlackTreeNode*>(parent); | 615 | return static_cast<const IntrusiveRedBlackTreeNode*>( |
| 616 | static_cast<const IntrusiveRedBlackTreeBaseNode<Derived>*>(parent)); | ||
| 589 | } | 617 | } |
| 590 | 618 | ||
| 591 | static constexpr Derived* GetParent(IntrusiveRedBlackTreeNode* node) { | 619 | static constexpr Derived* GetParent(IntrusiveRedBlackTreeNode* node) { |
| 592 | return static_cast<Derived*>(node); | 620 | return static_cast<Derived*>(static_cast<IntrusiveRedBlackTreeBaseNode<Derived>*>(node)); |
| 593 | } | 621 | } |
| 594 | 622 | ||
| 595 | static constexpr Derived const* GetParent(const IntrusiveRedBlackTreeNode* node) { | 623 | static constexpr Derived const* GetParent(IntrusiveRedBlackTreeNode const* node) { |
| 596 | return static_cast<const Derived*>(node); | 624 | return static_cast<const Derived*>( |
| 625 | static_cast<const IntrusiveRedBlackTreeBaseNode<Derived>*>(node)); | ||
| 597 | } | 626 | } |
| 598 | }; | 627 | }; |
| 599 | 628 | ||
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp index c51c05b28..4a2462ec4 100644 --- a/src/common/logging/backend.cpp +++ b/src/common/logging/backend.cpp | |||
| @@ -218,19 +218,17 @@ private: | |||
| 218 | Impl(const std::filesystem::path& file_backend_filename, const Filter& filter_) | 218 | Impl(const std::filesystem::path& file_backend_filename, const Filter& filter_) |
| 219 | : filter{filter_}, file_backend{file_backend_filename} {} | 219 | : filter{filter_}, file_backend{file_backend_filename} {} |
| 220 | 220 | ||
| 221 | ~Impl() { | 221 | ~Impl() = default; |
| 222 | StopBackendThread(); | ||
| 223 | } | ||
| 224 | 222 | ||
| 225 | void StartBackendThread() { | 223 | void StartBackendThread() { |
| 226 | backend_thread = std::thread([this] { | 224 | backend_thread = std::jthread([this](std::stop_token stop_token) { |
| 227 | Common::SetCurrentThreadName("yuzu:Log"); | 225 | Common::SetCurrentThreadName("yuzu:Log"); |
| 228 | Entry entry; | 226 | Entry entry; |
| 229 | const auto write_logs = [this, &entry]() { | 227 | const auto write_logs = [this, &entry]() { |
| 230 | ForEachBackend([&entry](Backend& backend) { backend.Write(entry); }); | 228 | ForEachBackend([&entry](Backend& backend) { backend.Write(entry); }); |
| 231 | }; | 229 | }; |
| 232 | while (!stop.stop_requested()) { | 230 | while (!stop_token.stop_requested()) { |
| 233 | entry = message_queue.PopWait(stop.get_token()); | 231 | entry = message_queue.PopWait(stop_token); |
| 234 | if (entry.filename != nullptr) { | 232 | if (entry.filename != nullptr) { |
| 235 | write_logs(); | 233 | write_logs(); |
| 236 | } | 234 | } |
| @@ -244,11 +242,6 @@ private: | |||
| 244 | }); | 242 | }); |
| 245 | } | 243 | } |
| 246 | 244 | ||
| 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, | 245 | Entry CreateEntry(Class log_class, Level log_level, const char* filename, unsigned int line_nr, |
| 253 | const char* function, std::string&& message) const { | 246 | const char* function, std::string&& message) const { |
| 254 | using std::chrono::duration_cast; | 247 | using std::chrono::duration_cast; |
| @@ -283,10 +276,9 @@ private: | |||
| 283 | ColorConsoleBackend color_console_backend{}; | 276 | ColorConsoleBackend color_console_backend{}; |
| 284 | FileBackend file_backend; | 277 | FileBackend file_backend; |
| 285 | 278 | ||
| 286 | std::stop_source stop; | ||
| 287 | std::thread backend_thread; | ||
| 288 | MPSCQueue<Entry, true> message_queue{}; | 279 | MPSCQueue<Entry, true> message_queue{}; |
| 289 | std::chrono::steady_clock::time_point time_origin{std::chrono::steady_clock::now()}; | 280 | std::chrono::steady_clock::time_point time_origin{std::chrono::steady_clock::now()}; |
| 281 | std::jthread backend_thread; | ||
| 290 | }; | 282 | }; |
| 291 | } // namespace | 283 | } // namespace |
| 292 | 284 | ||
diff --git a/src/common/logging/filter.cpp b/src/common/logging/filter.cpp index b898a652c..4afc1369a 100644 --- a/src/common/logging/filter.cpp +++ b/src/common/logging/filter.cpp | |||
| @@ -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) \ |
diff --git a/src/common/logging/types.h b/src/common/logging/types.h index 9ed0c7ad6..2b6e4daa7 100644 --- a/src/common/logging/types.h +++ b/src/common/logging/types.h | |||
| @@ -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 |
diff --git a/src/common/page_table.cpp b/src/common/page_table.cpp index 9fffd816f..4817b09f9 100644 --- a/src/common/page_table.cpp +++ b/src/common/page_table.cpp | |||
| @@ -10,11 +10,65 @@ PageTable::PageTable() = default; | |||
| 10 | 10 | ||
| 11 | PageTable::~PageTable() noexcept = default; | 11 | PageTable::~PageTable() noexcept = default; |
| 12 | 12 | ||
| 13 | void PageTable::Resize(size_t address_space_width_in_bits, size_t page_size_in_bits) { | 13 | 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)}; | 14 | u64 address) const { |
| 15 | // Setup invalid defaults. | ||
| 16 | out_entry.phys_addr = 0; | ||
| 17 | out_entry.block_size = page_size; | ||
| 18 | out_context.next_page = 0; | ||
| 19 | |||
| 20 | // Validate that we can read the actual entry. | ||
| 21 | const auto page = address / page_size; | ||
| 22 | if (page >= backing_addr.size()) { | ||
| 23 | return false; | ||
| 24 | } | ||
| 25 | |||
| 26 | // Validate that the entry is mapped. | ||
| 27 | const auto phys_addr = backing_addr[page]; | ||
| 28 | if (phys_addr == 0) { | ||
| 29 | return false; | ||
| 30 | } | ||
| 31 | |||
| 32 | // Populate the results. | ||
| 33 | out_entry.phys_addr = phys_addr + address; | ||
| 34 | out_context.next_page = page + 1; | ||
| 35 | out_context.next_offset = address + page_size; | ||
| 36 | |||
| 37 | return true; | ||
| 38 | } | ||
| 39 | |||
| 40 | bool PageTable::ContinueTraversal(TraversalEntry& out_entry, TraversalContext& context) const { | ||
| 41 | // Setup invalid defaults. | ||
| 42 | out_entry.phys_addr = 0; | ||
| 43 | out_entry.block_size = page_size; | ||
| 44 | |||
| 45 | // Validate that we can read the actual entry. | ||
| 46 | const auto page = context.next_page; | ||
| 47 | if (page >= backing_addr.size()) { | ||
| 48 | return false; | ||
| 49 | } | ||
| 50 | |||
| 51 | // Validate that the entry is mapped. | ||
| 52 | const auto phys_addr = backing_addr[page]; | ||
| 53 | if (phys_addr == 0) { | ||
| 54 | return false; | ||
| 55 | } | ||
| 56 | |||
| 57 | // Populate the results. | ||
| 58 | out_entry.phys_addr = phys_addr + context.next_offset; | ||
| 59 | context.next_page = page + 1; | ||
| 60 | context.next_offset += page_size; | ||
| 61 | |||
| 62 | return true; | ||
| 63 | } | ||
| 64 | |||
| 65 | void PageTable::Resize(std::size_t address_space_width_in_bits, std::size_t page_size_in_bits) { | ||
| 66 | const std::size_t num_page_table_entries{1ULL | ||
| 67 | << (address_space_width_in_bits - page_size_in_bits)}; | ||
| 15 | pointers.resize(num_page_table_entries); | 68 | pointers.resize(num_page_table_entries); |
| 16 | backing_addr.resize(num_page_table_entries); | 69 | backing_addr.resize(num_page_table_entries); |
| 17 | current_address_space_width_in_bits = address_space_width_in_bits; | 70 | current_address_space_width_in_bits = address_space_width_in_bits; |
| 71 | page_size = 1ULL << page_size_in_bits; | ||
| 18 | } | 72 | } |
| 19 | 73 | ||
| 20 | } // namespace Common | 74 | } // namespace Common |
diff --git a/src/common/page_table.h b/src/common/page_table.h index 8267e8b4d..82d91e9f3 100644 --- a/src/common/page_table.h +++ b/src/common/page_table.h | |||
| @@ -27,6 +27,16 @@ enum class PageType : u8 { | |||
| 27 | * mimics the way a real CPU page table works. | 27 | * mimics the way a real CPU page table works. |
| 28 | */ | 28 | */ |
| 29 | struct PageTable { | 29 | struct PageTable { |
| 30 | struct TraversalEntry { | ||
| 31 | u64 phys_addr{}; | ||
| 32 | std::size_t block_size{}; | ||
| 33 | }; | ||
| 34 | |||
| 35 | struct TraversalContext { | ||
| 36 | u64 next_page{}; | ||
| 37 | u64 next_offset{}; | ||
| 38 | }; | ||
| 39 | |||
| 30 | /// Number of bits reserved for attribute tagging. | 40 | /// Number of bits reserved for attribute tagging. |
| 31 | /// This can be at most the guaranteed alignment of the pointers in the page table. | 41 | /// This can be at most the guaranteed alignment of the pointers in the page table. |
| 32 | static constexpr int ATTRIBUTE_BITS = 2; | 42 | static constexpr int ATTRIBUTE_BITS = 2; |
| @@ -89,6 +99,10 @@ struct PageTable { | |||
| 89 | PageTable(PageTable&&) noexcept = default; | 99 | PageTable(PageTable&&) noexcept = default; |
| 90 | PageTable& operator=(PageTable&&) noexcept = default; | 100 | PageTable& operator=(PageTable&&) noexcept = default; |
| 91 | 101 | ||
| 102 | bool BeginTraversal(TraversalEntry& out_entry, TraversalContext& out_context, | ||
| 103 | u64 address) const; | ||
| 104 | bool ContinueTraversal(TraversalEntry& out_entry, TraversalContext& context) const; | ||
| 105 | |||
| 92 | /** | 106 | /** |
| 93 | * Resizes the page table to be able to accommodate enough pages within | 107 | * Resizes the page table to be able to accommodate enough pages within |
| 94 | * a given address space. | 108 | * a given address space. |
| @@ -96,9 +110,9 @@ struct PageTable { | |||
| 96 | * @param address_space_width_in_bits The address size width in bits. | 110 | * @param address_space_width_in_bits The address size width in bits. |
| 97 | * @param page_size_in_bits The page size in bits. | 111 | * @param page_size_in_bits The page size in bits. |
| 98 | */ | 112 | */ |
| 99 | void Resize(size_t address_space_width_in_bits, size_t page_size_in_bits); | 113 | void Resize(std::size_t address_space_width_in_bits, std::size_t page_size_in_bits); |
| 100 | 114 | ||
| 101 | size_t GetAddressSpaceBits() const { | 115 | std::size_t GetAddressSpaceBits() const { |
| 102 | return current_address_space_width_in_bits; | 116 | return current_address_space_width_in_bits; |
| 103 | } | 117 | } |
| 104 | 118 | ||
| @@ -110,9 +124,11 @@ struct PageTable { | |||
| 110 | 124 | ||
| 111 | VirtualBuffer<u64> backing_addr; | 125 | VirtualBuffer<u64> backing_addr; |
| 112 | 126 | ||
| 113 | size_t current_address_space_width_in_bits; | 127 | std::size_t current_address_space_width_in_bits{}; |
| 128 | |||
| 129 | u8* fastmem_arena{}; | ||
| 114 | 130 | ||
| 115 | u8* fastmem_arena; | 131 | std::size_t page_size{}; |
| 116 | }; | 132 | }; |
| 117 | 133 | ||
| 118 | } // namespace Common | 134 | } // namespace Common |
diff --git a/src/common/settings.cpp b/src/common/settings.cpp index 6964a8273..877e0faa4 100644 --- a/src/common/settings.cpp +++ b/src/common/settings.cpp | |||
| @@ -167,6 +167,7 @@ void RestoreGlobalState(bool is_powered_on) { | |||
| 167 | 167 | ||
| 168 | // Core | 168 | // Core |
| 169 | values.use_multi_core.SetGlobal(true); | 169 | values.use_multi_core.SetGlobal(true); |
| 170 | values.use_extended_memory_layout.SetGlobal(true); | ||
| 170 | 171 | ||
| 171 | // CPU | 172 | // CPU |
| 172 | values.cpu_accuracy.SetGlobal(true); | 173 | values.cpu_accuracy.SetGlobal(true); |
| @@ -175,6 +176,7 @@ void RestoreGlobalState(bool is_powered_on) { | |||
| 175 | values.cpuopt_unsafe_ignore_standard_fpcr.SetGlobal(true); | 176 | values.cpuopt_unsafe_ignore_standard_fpcr.SetGlobal(true); |
| 176 | values.cpuopt_unsafe_inaccurate_nan.SetGlobal(true); | 177 | values.cpuopt_unsafe_inaccurate_nan.SetGlobal(true); |
| 177 | values.cpuopt_unsafe_fastmem_check.SetGlobal(true); | 178 | values.cpuopt_unsafe_fastmem_check.SetGlobal(true); |
| 179 | values.cpuopt_unsafe_ignore_global_monitor.SetGlobal(true); | ||
| 178 | 180 | ||
| 179 | // Renderer | 181 | // Renderer |
| 180 | values.renderer_backend.SetGlobal(true); | 182 | values.renderer_backend.SetGlobal(true); |
diff --git a/src/common/settings.h b/src/common/settings.h index 9bee6e10f..a37d83fb3 100644 --- a/src/common/settings.h +++ b/src/common/settings.h | |||
| @@ -466,6 +466,7 @@ struct Values { | |||
| 466 | 466 | ||
| 467 | // Core | 467 | // Core |
| 468 | Setting<bool> use_multi_core{true, "use_multi_core"}; | 468 | Setting<bool> use_multi_core{true, "use_multi_core"}; |
| 469 | Setting<bool> use_extended_memory_layout{false, "use_extended_memory_layout"}; | ||
| 469 | 470 | ||
| 470 | // Cpu | 471 | // Cpu |
| 471 | RangedSetting<CPUAccuracy> cpu_accuracy{CPUAccuracy::Auto, CPUAccuracy::Auto, | 472 | RangedSetting<CPUAccuracy> cpu_accuracy{CPUAccuracy::Auto, CPUAccuracy::Auto, |
| @@ -483,12 +484,15 @@ struct Values { | |||
| 483 | BasicSetting<bool> cpuopt_misc_ir{true, "cpuopt_misc_ir"}; | 484 | BasicSetting<bool> cpuopt_misc_ir{true, "cpuopt_misc_ir"}; |
| 484 | BasicSetting<bool> cpuopt_reduce_misalign_checks{true, "cpuopt_reduce_misalign_checks"}; | 485 | BasicSetting<bool> cpuopt_reduce_misalign_checks{true, "cpuopt_reduce_misalign_checks"}; |
| 485 | BasicSetting<bool> cpuopt_fastmem{true, "cpuopt_fastmem"}; | 486 | BasicSetting<bool> cpuopt_fastmem{true, "cpuopt_fastmem"}; |
| 487 | BasicSetting<bool> cpuopt_fastmem_exclusives{true, "cpuopt_fastmem_exclusives"}; | ||
| 488 | BasicSetting<bool> cpuopt_recompile_exclusives{true, "cpuopt_recompile_exclusives"}; | ||
| 486 | 489 | ||
| 487 | Setting<bool> cpuopt_unsafe_unfuse_fma{true, "cpuopt_unsafe_unfuse_fma"}; | 490 | Setting<bool> cpuopt_unsafe_unfuse_fma{true, "cpuopt_unsafe_unfuse_fma"}; |
| 488 | Setting<bool> cpuopt_unsafe_reduce_fp_error{true, "cpuopt_unsafe_reduce_fp_error"}; | 491 | Setting<bool> cpuopt_unsafe_reduce_fp_error{true, "cpuopt_unsafe_reduce_fp_error"}; |
| 489 | Setting<bool> cpuopt_unsafe_ignore_standard_fpcr{true, "cpuopt_unsafe_ignore_standard_fpcr"}; | 492 | Setting<bool> cpuopt_unsafe_ignore_standard_fpcr{true, "cpuopt_unsafe_ignore_standard_fpcr"}; |
| 490 | Setting<bool> cpuopt_unsafe_inaccurate_nan{true, "cpuopt_unsafe_inaccurate_nan"}; | 493 | Setting<bool> cpuopt_unsafe_inaccurate_nan{true, "cpuopt_unsafe_inaccurate_nan"}; |
| 491 | Setting<bool> cpuopt_unsafe_fastmem_check{true, "cpuopt_unsafe_fastmem_check"}; | 494 | Setting<bool> cpuopt_unsafe_fastmem_check{true, "cpuopt_unsafe_fastmem_check"}; |
| 495 | Setting<bool> cpuopt_unsafe_ignore_global_monitor{true, "cpuopt_unsafe_ignore_global_monitor"}; | ||
| 492 | 496 | ||
| 493 | // Renderer | 497 | // Renderer |
| 494 | RangedSetting<RendererBackend> renderer_backend{ | 498 | RangedSetting<RendererBackend> renderer_backend{ |
diff --git a/src/common/telemetry.cpp b/src/common/telemetry.cpp index 6241d08b3..98c82cd17 100644 --- a/src/common/telemetry.cpp +++ b/src/common/telemetry.cpp | |||
| @@ -55,22 +55,50 @@ void AppendBuildInfo(FieldCollection& fc) { | |||
| 55 | 55 | ||
| 56 | void AppendCPUInfo(FieldCollection& fc) { | 56 | void AppendCPUInfo(FieldCollection& fc) { |
| 57 | #ifdef ARCHITECTURE_x86_64 | 57 | #ifdef ARCHITECTURE_x86_64 |
| 58 | fc.AddField(FieldType::UserSystem, "CPU_Model", Common::GetCPUCaps().cpu_string); | 58 | |
| 59 | fc.AddField(FieldType::UserSystem, "CPU_BrandString", Common::GetCPUCaps().brand_string); | 59 | const auto& caps = Common::GetCPUCaps(); |
| 60 | fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_AES", Common::GetCPUCaps().aes); | 60 | 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); | 61 | fc.AddField(FieldType::UserSystem, field_name, field_value); |
| 62 | fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_AVX2", Common::GetCPUCaps().avx2); | 62 | }; |
| 63 | fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_AVX512", Common::GetCPUCaps().avx512); | 63 | add_field("CPU_Model", caps.cpu_string); |
| 64 | fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_BMI1", Common::GetCPUCaps().bmi1); | 64 | add_field("CPU_BrandString", caps.brand_string); |
| 65 | fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_BMI2", Common::GetCPUCaps().bmi2); | 65 | |
| 66 | fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_FMA", Common::GetCPUCaps().fma); | 66 | add_field("CPU_Extension_x64_SSE", caps.sse); |
| 67 | fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_FMA4", Common::GetCPUCaps().fma4); | 67 | add_field("CPU_Extension_x64_SSE2", caps.sse2); |
| 68 | fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_SSE", Common::GetCPUCaps().sse); | 68 | add_field("CPU_Extension_x64_SSE3", caps.sse3); |
| 69 | fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_SSE2", Common::GetCPUCaps().sse2); | 69 | add_field("CPU_Extension_x64_SSSE3", caps.ssse3); |
| 70 | fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_SSE3", Common::GetCPUCaps().sse3); | 70 | add_field("CPU_Extension_x64_SSE41", caps.sse4_1); |
| 71 | fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_SSSE3", Common::GetCPUCaps().ssse3); | 71 | add_field("CPU_Extension_x64_SSE42", caps.sse4_2); |
| 72 | fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_SSE41", Common::GetCPUCaps().sse4_1); | 72 | |
| 73 | fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_SSE42", Common::GetCPUCaps().sse4_2); | 73 | add_field("CPU_Extension_x64_AVX", caps.avx); |
| 74 | add_field("CPU_Extension_x64_AVX_VNNI", caps.avx_vnni); | ||
| 75 | add_field("CPU_Extension_x64_AVX2", caps.avx2); | ||
| 76 | |||
| 77 | // Skylake-X/SP level AVX512, for compatibility with the previous telemetry field | ||
| 78 | add_field("CPU_Extension_x64_AVX512", | ||
| 79 | caps.avx512f && caps.avx512cd && caps.avx512vl && caps.avx512dq && caps.avx512bw); | ||
| 80 | |||
| 81 | add_field("CPU_Extension_x64_AVX512F", caps.avx512f); | ||
| 82 | add_field("CPU_Extension_x64_AVX512CD", caps.avx512cd); | ||
| 83 | add_field("CPU_Extension_x64_AVX512VL", caps.avx512vl); | ||
| 84 | add_field("CPU_Extension_x64_AVX512DQ", caps.avx512dq); | ||
| 85 | add_field("CPU_Extension_x64_AVX512BW", caps.avx512bw); | ||
| 86 | add_field("CPU_Extension_x64_AVX512BITALG", caps.avx512bitalg); | ||
| 87 | add_field("CPU_Extension_x64_AVX512VBMI", caps.avx512vbmi); | ||
| 88 | |||
| 89 | add_field("CPU_Extension_x64_AES", caps.aes); | ||
| 90 | add_field("CPU_Extension_x64_BMI1", caps.bmi1); | ||
| 91 | add_field("CPU_Extension_x64_BMI2", caps.bmi2); | ||
| 92 | add_field("CPU_Extension_x64_F16C", caps.f16c); | ||
| 93 | add_field("CPU_Extension_x64_FMA", caps.fma); | ||
| 94 | add_field("CPU_Extension_x64_FMA4", caps.fma4); | ||
| 95 | add_field("CPU_Extension_x64_GFNI", caps.gfni); | ||
| 96 | add_field("CPU_Extension_x64_INVARIANT_TSC", caps.invariant_tsc); | ||
| 97 | add_field("CPU_Extension_x64_LZCNT", caps.lzcnt); | ||
| 98 | add_field("CPU_Extension_x64_MOVBE", caps.movbe); | ||
| 99 | add_field("CPU_Extension_x64_PCLMULQDQ", caps.pclmulqdq); | ||
| 100 | add_field("CPU_Extension_x64_POPCNT", caps.popcnt); | ||
| 101 | add_field("CPU_Extension_x64_SHA", caps.sha); | ||
| 74 | #else | 102 | #else |
| 75 | fc.AddField(FieldType::UserSystem, "CPU_Model", "Other"); | 103 | fc.AddField(FieldType::UserSystem, "CPU_Model", "Other"); |
| 76 | #endif | 104 | #endif |
diff --git a/src/common/telemetry.h b/src/common/telemetry.h index 49186e848..3524c857e 100644 --- a/src/common/telemetry.h +++ b/src/common/telemetry.h | |||
| @@ -8,6 +8,8 @@ | |||
| 8 | #include <map> | 8 | #include <map> |
| 9 | #include <memory> | 9 | #include <memory> |
| 10 | #include <string> | 10 | #include <string> |
| 11 | #include <string_view> | ||
| 12 | #include "common/common_funcs.h" | ||
| 11 | #include "common/common_types.h" | 13 | #include "common/common_types.h" |
| 12 | 14 | ||
| 13 | namespace Common::Telemetry { | 15 | namespace Common::Telemetry { |
| @@ -28,7 +30,7 @@ struct VisitorInterface; | |||
| 28 | /** | 30 | /** |
| 29 | * Interface class for telemetry data fields. | 31 | * Interface class for telemetry data fields. |
| 30 | */ | 32 | */ |
| 31 | class FieldInterface : NonCopyable { | 33 | class FieldInterface { |
| 32 | public: | 34 | public: |
| 33 | virtual ~FieldInterface() = default; | 35 | virtual ~FieldInterface() = default; |
| 34 | 36 | ||
| @@ -52,14 +54,15 @@ public: | |||
| 52 | template <typename T> | 54 | template <typename T> |
| 53 | class Field : public FieldInterface { | 55 | class Field : public FieldInterface { |
| 54 | public: | 56 | public: |
| 55 | Field(FieldType type_, std::string name_, T value_) | 57 | YUZU_NON_COPYABLE(Field); |
| 56 | : name(std::move(name_)), type(type_), value(std::move(value_)) {} | ||
| 57 | 58 | ||
| 58 | Field(const Field&) = default; | 59 | Field(FieldType type_, std::string_view name_, T value_) |
| 59 | Field& operator=(const Field&) = default; | 60 | : name(name_), type(type_), value(std::move(value_)) {} |
| 60 | 61 | ||
| 61 | Field(Field&&) = default; | 62 | ~Field() override = default; |
| 62 | Field& operator=(Field&& other) = default; | 63 | |
| 64 | Field(Field&&) noexcept = default; | ||
| 65 | Field& operator=(Field&& other) noexcept = default; | ||
| 63 | 66 | ||
| 64 | void Accept(VisitorInterface& visitor) const override; | 67 | void Accept(VisitorInterface& visitor) const override; |
| 65 | 68 | ||
| @@ -98,9 +101,15 @@ private: | |||
| 98 | /** | 101 | /** |
| 99 | * Collection of data fields that have been logged. | 102 | * Collection of data fields that have been logged. |
| 100 | */ | 103 | */ |
| 101 | class FieldCollection final : NonCopyable { | 104 | class FieldCollection final { |
| 102 | public: | 105 | public: |
| 106 | YUZU_NON_COPYABLE(FieldCollection); | ||
| 107 | |||
| 103 | FieldCollection() = default; | 108 | FieldCollection() = default; |
| 109 | ~FieldCollection() = default; | ||
| 110 | |||
| 111 | FieldCollection(FieldCollection&&) noexcept = default; | ||
| 112 | FieldCollection& operator=(FieldCollection&&) noexcept = default; | ||
| 104 | 113 | ||
| 105 | /** | 114 | /** |
| 106 | * Accept method for the visitor pattern, visits each field in the collection. | 115 | * Accept method for the visitor pattern, visits each field in the collection. |
| @@ -115,7 +124,7 @@ public: | |||
| 115 | * @param value Value for the field to add. | 124 | * @param value Value for the field to add. |
| 116 | */ | 125 | */ |
| 117 | template <typename T> | 126 | template <typename T> |
| 118 | void AddField(FieldType type, const char* name, T value) { | 127 | void AddField(FieldType type, std::string_view name, T value) { |
| 119 | return AddField(std::make_unique<Field<T>>(type, name, std::move(value))); | 128 | return AddField(std::make_unique<Field<T>>(type, name, std::move(value))); |
| 120 | } | 129 | } |
| 121 | 130 | ||
| @@ -133,7 +142,7 @@ private: | |||
| 133 | * Telemetry fields visitor interface class. A backend to log to a web service should implement | 142 | * Telemetry fields visitor interface class. A backend to log to a web service should implement |
| 134 | * this interface. | 143 | * this interface. |
| 135 | */ | 144 | */ |
| 136 | struct VisitorInterface : NonCopyable { | 145 | struct VisitorInterface { |
| 137 | virtual ~VisitorInterface() = default; | 146 | virtual ~VisitorInterface() = default; |
| 138 | 147 | ||
| 139 | virtual void Visit(const Field<bool>& field) = 0; | 148 | virtual void Visit(const Field<bool>& field) = 0; |
| @@ -160,8 +169,11 @@ struct VisitorInterface : NonCopyable { | |||
| 160 | * Empty implementation of VisitorInterface that drops all fields. Used when a functional | 169 | * Empty implementation of VisitorInterface that drops all fields. Used when a functional |
| 161 | * backend implementation is not available. | 170 | * backend implementation is not available. |
| 162 | */ | 171 | */ |
| 163 | struct NullVisitor : public VisitorInterface { | 172 | struct NullVisitor final : public VisitorInterface { |
| 164 | ~NullVisitor() = default; | 173 | YUZU_NON_COPYABLE(NullVisitor); |
| 174 | |||
| 175 | NullVisitor() = default; | ||
| 176 | ~NullVisitor() override = default; | ||
| 165 | 177 | ||
| 166 | void Visit(const Field<bool>& /*field*/) override {} | 178 | void Visit(const Field<bool>& /*field*/) override {} |
| 167 | void Visit(const Field<double>& /*field*/) override {} | 179 | void Visit(const Field<double>& /*field*/) override {} |
diff --git a/src/common/tree.h b/src/common/tree.h index 18faa4a48..28370e343 100644 --- a/src/common/tree.h +++ b/src/common/tree.h | |||
| @@ -43,294 +43,265 @@ | |||
| 43 | * The maximum height of a red-black tree is 2lg (n+1). | 43 | * The maximum height of a red-black tree is 2lg (n+1). |
| 44 | */ | 44 | */ |
| 45 | 45 | ||
| 46 | #include "common/assert.h" | 46 | namespace Common::freebsd { |
| 47 | 47 | ||
| 48 | namespace Common { | 48 | enum class RBColor { |
| 49 | RB_BLACK = 0, | ||
| 50 | RB_RED = 1, | ||
| 51 | }; | ||
| 52 | |||
| 53 | #pragma pack(push, 4) | ||
| 49 | template <typename T> | 54 | template <typename T> |
| 50 | class RBHead { | 55 | class RBEntry { |
| 51 | public: | 56 | public: |
| 52 | [[nodiscard]] T* Root() { | 57 | constexpr RBEntry() = default; |
| 53 | return rbh_root; | ||
| 54 | } | ||
| 55 | 58 | ||
| 56 | [[nodiscard]] const T* Root() const { | 59 | [[nodiscard]] constexpr T* Left() { |
| 57 | return rbh_root; | 60 | return m_rbe_left; |
| 58 | } | 61 | } |
| 59 | 62 | [[nodiscard]] constexpr const T* Left() const { | |
| 60 | void SetRoot(T* root) { | 63 | return m_rbe_left; |
| 61 | rbh_root = root; | ||
| 62 | } | 64 | } |
| 63 | 65 | ||
| 64 | [[nodiscard]] bool IsEmpty() const { | 66 | constexpr void SetLeft(T* e) { |
| 65 | return Root() == nullptr; | 67 | m_rbe_left = e; |
| 66 | } | 68 | } |
| 67 | 69 | ||
| 68 | private: | 70 | [[nodiscard]] constexpr T* Right() { |
| 69 | T* rbh_root = nullptr; | 71 | 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 | } | 72 | } |
| 83 | 73 | [[nodiscard]] constexpr const T* Right() const { | |
| 84 | [[nodiscard]] const T* Left() const { | 74 | return m_rbe_right; |
| 85 | return rbe_left; | ||
| 86 | } | 75 | } |
| 87 | 76 | ||
| 88 | void SetLeft(T* left) { | 77 | constexpr void SetRight(T* e) { |
| 89 | rbe_left = left; | 78 | m_rbe_right = e; |
| 90 | } | 79 | } |
| 91 | 80 | ||
| 92 | [[nodiscard]] T* Right() { | 81 | [[nodiscard]] constexpr T* Parent() { |
| 93 | return rbe_right; | 82 | return m_rbe_parent; |
| 94 | } | 83 | } |
| 95 | 84 | [[nodiscard]] constexpr const T* Parent() const { | |
| 96 | [[nodiscard]] const T* Right() const { | 85 | return m_rbe_parent; |
| 97 | return rbe_right; | ||
| 98 | } | 86 | } |
| 99 | 87 | ||
| 100 | void SetRight(T* right) { | 88 | constexpr void SetParent(T* e) { |
| 101 | rbe_right = right; | 89 | m_rbe_parent = e; |
| 102 | } | 90 | } |
| 103 | 91 | ||
| 104 | [[nodiscard]] T* Parent() { | 92 | [[nodiscard]] constexpr bool IsBlack() const { |
| 105 | return rbe_parent; | 93 | return m_rbe_color == RBColor::RB_BLACK; |
| 106 | } | 94 | } |
| 107 | 95 | [[nodiscard]] constexpr bool IsRed() const { | |
| 108 | [[nodiscard]] const T* Parent() const { | 96 | return m_rbe_color == RBColor::RB_RED; |
| 109 | return rbe_parent; | ||
| 110 | } | 97 | } |
| 111 | 98 | [[nodiscard]] constexpr RBColor Color() const { | |
| 112 | void SetParent(T* parent) { | 99 | return m_rbe_color; |
| 113 | rbe_parent = parent; | ||
| 114 | } | 100 | } |
| 115 | 101 | ||
| 116 | [[nodiscard]] bool IsBlack() const { | 102 | constexpr void SetColor(RBColor c) { |
| 117 | return rbe_color == EntryColor::Black; | 103 | m_rbe_color = c; |
| 118 | } | 104 | } |
| 119 | 105 | ||
| 120 | [[nodiscard]] bool IsRed() const { | 106 | private: |
| 121 | return rbe_color == EntryColor::Red; | 107 | T* m_rbe_left{}; |
| 122 | } | 108 | T* m_rbe_right{}; |
| 109 | T* m_rbe_parent{}; | ||
| 110 | RBColor m_rbe_color{RBColor::RB_BLACK}; | ||
| 111 | }; | ||
| 112 | #pragma pack(pop) | ||
| 123 | 113 | ||
| 124 | [[nodiscard]] EntryColor Color() const { | 114 | template <typename T> |
| 125 | return rbe_color; | 115 | struct CheckRBEntry { |
| 126 | } | 116 | static constexpr bool value = false; |
| 117 | }; | ||
| 118 | template <typename T> | ||
| 119 | struct CheckRBEntry<RBEntry<T>> { | ||
| 120 | static constexpr bool value = true; | ||
| 121 | }; | ||
| 127 | 122 | ||
| 128 | void SetColor(EntryColor color) { | 123 | template <typename T> |
| 129 | rbe_color = color; | 124 | concept IsRBEntry = CheckRBEntry<T>::value; |
| 130 | } | ||
| 131 | 125 | ||
| 126 | template <typename T> | ||
| 127 | concept HasRBEntry = requires(T& t, const T& ct) { | ||
| 128 | { t.GetRBEntry() } -> std::same_as<RBEntry<T>&>; | ||
| 129 | { ct.GetRBEntry() } -> std::same_as<const RBEntry<T>&>; | ||
| 130 | }; | ||
| 131 | |||
| 132 | template <typename T> | ||
| 133 | requires HasRBEntry<T> | ||
| 134 | class RBHead { | ||
| 132 | private: | 135 | private: |
| 133 | T* rbe_left = nullptr; | 136 | T* m_rbh_root = nullptr; |
| 134 | T* rbe_right = nullptr; | 137 | |
| 135 | T* rbe_parent = nullptr; | 138 | public: |
| 136 | EntryColor rbe_color{}; | 139 | [[nodiscard]] constexpr T* Root() { |
| 140 | return m_rbh_root; | ||
| 141 | } | ||
| 142 | [[nodiscard]] constexpr const T* Root() const { | ||
| 143 | return m_rbh_root; | ||
| 144 | } | ||
| 145 | constexpr void SetRoot(T* root) { | ||
| 146 | m_rbh_root = root; | ||
| 147 | } | ||
| 148 | |||
| 149 | [[nodiscard]] constexpr bool IsEmpty() const { | ||
| 150 | return this->Root() == nullptr; | ||
| 151 | } | ||
| 137 | }; | 152 | }; |
| 138 | 153 | ||
| 139 | template <typename Node> | 154 | template <typename T> |
| 140 | [[nodiscard]] RBEntry<Node>& RB_ENTRY(Node* node) { | 155 | requires HasRBEntry<T> |
| 141 | return node->GetEntry(); | 156 | [[nodiscard]] constexpr RBEntry<T>& RB_ENTRY(T* t) { |
| 157 | return t->GetRBEntry(); | ||
| 142 | } | 158 | } |
| 143 | 159 | template <typename T> | |
| 144 | template <typename Node> | 160 | requires HasRBEntry<T> |
| 145 | [[nodiscard]] const RBEntry<Node>& RB_ENTRY(const Node* node) { | 161 | [[nodiscard]] constexpr const RBEntry<T>& RB_ENTRY(const T* t) { |
| 146 | return node->GetEntry(); | 162 | return t->GetRBEntry(); |
| 147 | } | 163 | } |
| 148 | 164 | ||
| 149 | template <typename Node> | 165 | template <typename T> |
| 150 | [[nodiscard]] Node* RB_PARENT(Node* node) { | 166 | requires HasRBEntry<T> |
| 151 | return RB_ENTRY(node).Parent(); | 167 | [[nodiscard]] constexpr T* RB_LEFT(T* t) { |
| 168 | return RB_ENTRY(t).Left(); | ||
| 152 | } | 169 | } |
| 153 | 170 | template <typename T> | |
| 154 | template <typename Node> | 171 | requires HasRBEntry<T> |
| 155 | [[nodiscard]] const Node* RB_PARENT(const Node* node) { | 172 | [[nodiscard]] constexpr const T* RB_LEFT(const T* t) { |
| 156 | return RB_ENTRY(node).Parent(); | 173 | return RB_ENTRY(t).Left(); |
| 157 | } | 174 | } |
| 158 | 175 | ||
| 159 | template <typename Node> | 176 | template <typename T> |
| 160 | void RB_SET_PARENT(Node* node, Node* parent) { | 177 | requires HasRBEntry<T> |
| 161 | return RB_ENTRY(node).SetParent(parent); | 178 | [[nodiscard]] constexpr T* RB_RIGHT(T* t) { |
| 179 | return RB_ENTRY(t).Right(); | ||
| 162 | } | 180 | } |
| 163 | 181 | template <typename T> | |
| 164 | template <typename Node> | 182 | requires HasRBEntry<T> |
| 165 | [[nodiscard]] Node* RB_LEFT(Node* node) { | 183 | [[nodiscard]] constexpr const T* RB_RIGHT(const T* t) { |
| 166 | return RB_ENTRY(node).Left(); | 184 | return RB_ENTRY(t).Right(); |
| 167 | } | 185 | } |
| 168 | 186 | ||
| 169 | template <typename Node> | 187 | template <typename T> |
| 170 | [[nodiscard]] const Node* RB_LEFT(const Node* node) { | 188 | requires HasRBEntry<T> |
| 171 | return RB_ENTRY(node).Left(); | 189 | [[nodiscard]] constexpr T* RB_PARENT(T* t) { |
| 190 | return RB_ENTRY(t).Parent(); | ||
| 172 | } | 191 | } |
| 173 | 192 | template <typename T> | |
| 174 | template <typename Node> | 193 | requires HasRBEntry<T> |
| 175 | void RB_SET_LEFT(Node* node, Node* left) { | 194 | [[nodiscard]] constexpr const T* RB_PARENT(const T* t) { |
| 176 | return RB_ENTRY(node).SetLeft(left); | 195 | return RB_ENTRY(t).Parent(); |
| 177 | } | 196 | } |
| 178 | 197 | ||
| 179 | template <typename Node> | 198 | template <typename T> |
| 180 | [[nodiscard]] Node* RB_RIGHT(Node* node) { | 199 | requires HasRBEntry<T> |
| 181 | return RB_ENTRY(node).Right(); | 200 | constexpr void RB_SET_LEFT(T* t, T* e) { |
| 201 | RB_ENTRY(t).SetLeft(e); | ||
| 182 | } | 202 | } |
| 183 | 203 | template <typename T> | |
| 184 | template <typename Node> | 204 | requires HasRBEntry<T> |
| 185 | [[nodiscard]] const Node* RB_RIGHT(const Node* node) { | 205 | constexpr void RB_SET_RIGHT(T* t, T* e) { |
| 186 | return RB_ENTRY(node).Right(); | 206 | RB_ENTRY(t).SetRight(e); |
| 187 | } | 207 | } |
| 188 | 208 | template <typename T> | |
| 189 | template <typename Node> | 209 | requires HasRBEntry<T> |
| 190 | void RB_SET_RIGHT(Node* node, Node* right) { | 210 | constexpr void RB_SET_PARENT(T* t, T* e) { |
| 191 | return RB_ENTRY(node).SetRight(right); | 211 | RB_ENTRY(t).SetParent(e); |
| 192 | } | 212 | } |
| 193 | 213 | ||
| 194 | template <typename Node> | 214 | template <typename T> |
| 195 | [[nodiscard]] bool RB_IS_BLACK(const Node* node) { | 215 | requires HasRBEntry<T> |
| 196 | return RB_ENTRY(node).IsBlack(); | 216 | [[nodiscard]] constexpr bool RB_IS_BLACK(const T* t) { |
| 217 | return RB_ENTRY(t).IsBlack(); | ||
| 197 | } | 218 | } |
| 198 | 219 | template <typename T> | |
| 199 | template <typename Node> | 220 | requires HasRBEntry<T> |
| 200 | [[nodiscard]] bool RB_IS_RED(const Node* node) { | 221 | [[nodiscard]] constexpr bool RB_IS_RED(const T* t) { |
| 201 | return RB_ENTRY(node).IsRed(); | 222 | return RB_ENTRY(t).IsRed(); |
| 202 | } | 223 | } |
| 203 | 224 | ||
| 204 | template <typename Node> | 225 | template <typename T> |
| 205 | [[nodiscard]] EntryColor RB_COLOR(const Node* node) { | 226 | requires HasRBEntry<T> |
| 206 | return RB_ENTRY(node).Color(); | 227 | [[nodiscard]] constexpr RBColor RB_COLOR(const T* t) { |
| 228 | return RB_ENTRY(t).Color(); | ||
| 207 | } | 229 | } |
| 208 | 230 | ||
| 209 | template <typename Node> | 231 | template <typename T> |
| 210 | void RB_SET_COLOR(Node* node, EntryColor color) { | 232 | requires HasRBEntry<T> |
| 211 | return RB_ENTRY(node).SetColor(color); | 233 | constexpr void RB_SET_COLOR(T* t, RBColor c) { |
| 234 | RB_ENTRY(t).SetColor(c); | ||
| 212 | } | 235 | } |
| 213 | 236 | ||
| 214 | template <typename Node> | 237 | template <typename T> |
| 215 | void RB_SET(Node* node, Node* parent) { | 238 | requires HasRBEntry<T> |
| 216 | auto& entry = RB_ENTRY(node); | 239 | constexpr void RB_SET(T* elm, T* parent) { |
| 217 | entry.SetParent(parent); | 240 | auto& rb_entry = RB_ENTRY(elm); |
| 218 | entry.SetLeft(nullptr); | 241 | rb_entry.SetParent(parent); |
| 219 | entry.SetRight(nullptr); | 242 | rb_entry.SetLeft(nullptr); |
| 220 | entry.SetColor(EntryColor::Red); | 243 | rb_entry.SetRight(nullptr); |
| 244 | rb_entry.SetColor(RBColor::RB_RED); | ||
| 221 | } | 245 | } |
| 222 | 246 | ||
| 223 | template <typename Node> | 247 | template <typename T> |
| 224 | void RB_SET_BLACKRED(Node* black, Node* red) { | 248 | requires HasRBEntry<T> |
| 225 | RB_SET_COLOR(black, EntryColor::Black); | 249 | constexpr void RB_SET_BLACKRED(T* black, T* red) { |
| 226 | RB_SET_COLOR(red, EntryColor::Red); | 250 | RB_SET_COLOR(black, RBColor::RB_BLACK); |
| 251 | RB_SET_COLOR(red, RBColor::RB_RED); | ||
| 227 | } | 252 | } |
| 228 | 253 | ||
| 229 | template <typename Node> | 254 | template <typename T> |
| 230 | void RB_ROTATE_LEFT(RBHead<Node>* head, Node* elm, Node*& tmp) { | 255 | requires HasRBEntry<T> |
| 256 | constexpr void RB_ROTATE_LEFT(RBHead<T>& head, T* elm, T*& tmp) { | ||
| 231 | tmp = RB_RIGHT(elm); | 257 | tmp = RB_RIGHT(elm); |
| 232 | RB_SET_RIGHT(elm, RB_LEFT(tmp)); | 258 | 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); | 259 | RB_SET_PARENT(RB_LEFT(tmp), elm); |
| 235 | } | 260 | } |
| 236 | 261 | ||
| 237 | RB_SET_PARENT(tmp, RB_PARENT(elm)); | 262 | 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))) { | 263 | if (elm == RB_LEFT(RB_PARENT(elm))) { |
| 240 | RB_SET_LEFT(RB_PARENT(elm), tmp); | 264 | RB_SET_LEFT(RB_PARENT(elm), tmp); |
| 241 | } else { | 265 | } else { |
| 242 | RB_SET_RIGHT(RB_PARENT(elm), tmp); | 266 | RB_SET_RIGHT(RB_PARENT(elm), tmp); |
| 243 | } | 267 | } |
| 244 | } else { | 268 | } else { |
| 245 | head->SetRoot(tmp); | 269 | head.SetRoot(tmp); |
| 246 | } | 270 | } |
| 247 | 271 | ||
| 248 | RB_SET_LEFT(tmp, elm); | 272 | RB_SET_LEFT(tmp, elm); |
| 249 | RB_SET_PARENT(elm, tmp); | 273 | RB_SET_PARENT(elm, tmp); |
| 250 | } | 274 | } |
| 251 | 275 | ||
| 252 | template <typename Node> | 276 | template <typename T> |
| 253 | void RB_ROTATE_RIGHT(RBHead<Node>* head, Node* elm, Node*& tmp) { | 277 | requires HasRBEntry<T> |
| 278 | constexpr void RB_ROTATE_RIGHT(RBHead<T>& head, T* elm, T*& tmp) { | ||
| 254 | tmp = RB_LEFT(elm); | 279 | tmp = RB_LEFT(elm); |
| 255 | RB_SET_LEFT(elm, RB_RIGHT(tmp)); | 280 | 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); | 281 | RB_SET_PARENT(RB_RIGHT(tmp), elm); |
| 258 | } | 282 | } |
| 259 | 283 | ||
| 260 | RB_SET_PARENT(tmp, RB_PARENT(elm)); | 284 | 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))) { | 285 | if (elm == RB_LEFT(RB_PARENT(elm))) { |
| 263 | RB_SET_LEFT(RB_PARENT(elm), tmp); | 286 | RB_SET_LEFT(RB_PARENT(elm), tmp); |
| 264 | } else { | 287 | } else { |
| 265 | RB_SET_RIGHT(RB_PARENT(elm), tmp); | 288 | RB_SET_RIGHT(RB_PARENT(elm), tmp); |
| 266 | } | 289 | } |
| 267 | } else { | 290 | } else { |
| 268 | head->SetRoot(tmp); | 291 | head.SetRoot(tmp); |
| 269 | } | 292 | } |
| 270 | 293 | ||
| 271 | RB_SET_RIGHT(tmp, elm); | 294 | RB_SET_RIGHT(tmp, elm); |
| 272 | RB_SET_PARENT(elm, tmp); | 295 | RB_SET_PARENT(elm, tmp); |
| 273 | } | 296 | } |
| 274 | 297 | ||
| 275 | template <typename Node> | 298 | template <typename T> |
| 276 | void RB_INSERT_COLOR(RBHead<Node>* head, Node* elm) { | 299 | requires HasRBEntry<T> |
| 277 | Node* parent = nullptr; | 300 | constexpr void RB_REMOVE_COLOR(RBHead<T>& head, T* parent, T* elm) { |
| 278 | Node* tmp = nullptr; | 301 | T* tmp; |
| 279 | 302 | 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) { | 303 | if (RB_LEFT(parent) == elm) { |
| 329 | tmp = RB_RIGHT(parent); | 304 | tmp = RB_RIGHT(parent); |
| 330 | if (!tmp) { | ||
| 331 | ASSERT_MSG(false, "tmp is invalid!"); | ||
| 332 | break; | ||
| 333 | } | ||
| 334 | if (RB_IS_RED(tmp)) { | 305 | if (RB_IS_RED(tmp)) { |
| 335 | RB_SET_BLACKRED(tmp, parent); | 306 | RB_SET_BLACKRED(tmp, parent); |
| 336 | RB_ROTATE_LEFT(head, parent, tmp); | 307 | RB_ROTATE_LEFT(head, parent, tmp); |
| @@ -339,29 +310,29 @@ void RB_REMOVE_COLOR(RBHead<Node>* head, Node* parent, Node* elm) { | |||
| 339 | 310 | ||
| 340 | if ((RB_LEFT(tmp) == nullptr || RB_IS_BLACK(RB_LEFT(tmp))) && | 311 | if ((RB_LEFT(tmp) == nullptr || RB_IS_BLACK(RB_LEFT(tmp))) && |
| 341 | (RB_RIGHT(tmp) == nullptr || RB_IS_BLACK(RB_RIGHT(tmp)))) { | 312 | (RB_RIGHT(tmp) == nullptr || RB_IS_BLACK(RB_RIGHT(tmp)))) { |
| 342 | RB_SET_COLOR(tmp, EntryColor::Red); | 313 | RB_SET_COLOR(tmp, RBColor::RB_RED); |
| 343 | elm = parent; | 314 | elm = parent; |
| 344 | parent = RB_PARENT(elm); | 315 | parent = RB_PARENT(elm); |
| 345 | } else { | 316 | } else { |
| 346 | if (RB_RIGHT(tmp) == nullptr || RB_IS_BLACK(RB_RIGHT(tmp))) { | 317 | if (RB_RIGHT(tmp) == nullptr || RB_IS_BLACK(RB_RIGHT(tmp))) { |
| 347 | Node* oleft; | 318 | T* oleft; |
| 348 | if ((oleft = RB_LEFT(tmp)) != nullptr) { | 319 | if ((oleft = RB_LEFT(tmp)) != nullptr) { |
| 349 | RB_SET_COLOR(oleft, EntryColor::Black); | 320 | RB_SET_COLOR(oleft, RBColor::RB_BLACK); |
| 350 | } | 321 | } |
| 351 | 322 | ||
| 352 | RB_SET_COLOR(tmp, EntryColor::Red); | 323 | RB_SET_COLOR(tmp, RBColor::RB_RED); |
| 353 | RB_ROTATE_RIGHT(head, tmp, oleft); | 324 | RB_ROTATE_RIGHT(head, tmp, oleft); |
| 354 | tmp = RB_RIGHT(parent); | 325 | tmp = RB_RIGHT(parent); |
| 355 | } | 326 | } |
| 356 | 327 | ||
| 357 | RB_SET_COLOR(tmp, RB_COLOR(parent)); | 328 | RB_SET_COLOR(tmp, RB_COLOR(parent)); |
| 358 | RB_SET_COLOR(parent, EntryColor::Black); | 329 | RB_SET_COLOR(parent, RBColor::RB_BLACK); |
| 359 | if (RB_RIGHT(tmp)) { | 330 | if (RB_RIGHT(tmp)) { |
| 360 | RB_SET_COLOR(RB_RIGHT(tmp), EntryColor::Black); | 331 | RB_SET_COLOR(RB_RIGHT(tmp), RBColor::RB_BLACK); |
| 361 | } | 332 | } |
| 362 | 333 | ||
| 363 | RB_ROTATE_LEFT(head, parent, tmp); | 334 | RB_ROTATE_LEFT(head, parent, tmp); |
| 364 | elm = head->Root(); | 335 | elm = head.Root(); |
| 365 | break; | 336 | break; |
| 366 | } | 337 | } |
| 367 | } else { | 338 | } else { |
| @@ -372,68 +343,56 @@ void RB_REMOVE_COLOR(RBHead<Node>* head, Node* parent, Node* elm) { | |||
| 372 | tmp = RB_LEFT(parent); | 343 | tmp = RB_LEFT(parent); |
| 373 | } | 344 | } |
| 374 | 345 | ||
| 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))) && | 346 | if ((RB_LEFT(tmp) == nullptr || RB_IS_BLACK(RB_LEFT(tmp))) && |
| 381 | (RB_RIGHT(tmp) == nullptr || RB_IS_BLACK(RB_RIGHT(tmp)))) { | 347 | (RB_RIGHT(tmp) == nullptr || RB_IS_BLACK(RB_RIGHT(tmp)))) { |
| 382 | RB_SET_COLOR(tmp, EntryColor::Red); | 348 | RB_SET_COLOR(tmp, RBColor::RB_RED); |
| 383 | elm = parent; | 349 | elm = parent; |
| 384 | parent = RB_PARENT(elm); | 350 | parent = RB_PARENT(elm); |
| 385 | } else { | 351 | } else { |
| 386 | if (RB_LEFT(tmp) == nullptr || RB_IS_BLACK(RB_LEFT(tmp))) { | 352 | if (RB_LEFT(tmp) == nullptr || RB_IS_BLACK(RB_LEFT(tmp))) { |
| 387 | Node* oright; | 353 | T* oright; |
| 388 | if ((oright = RB_RIGHT(tmp)) != nullptr) { | 354 | if ((oright = RB_RIGHT(tmp)) != nullptr) { |
| 389 | RB_SET_COLOR(oright, EntryColor::Black); | 355 | RB_SET_COLOR(oright, RBColor::RB_BLACK); |
| 390 | } | 356 | } |
| 391 | 357 | ||
| 392 | RB_SET_COLOR(tmp, EntryColor::Red); | 358 | RB_SET_COLOR(tmp, RBColor::RB_RED); |
| 393 | RB_ROTATE_LEFT(head, tmp, oright); | 359 | RB_ROTATE_LEFT(head, tmp, oright); |
| 394 | tmp = RB_LEFT(parent); | 360 | tmp = RB_LEFT(parent); |
| 395 | } | 361 | } |
| 396 | 362 | ||
| 397 | RB_SET_COLOR(tmp, RB_COLOR(parent)); | 363 | RB_SET_COLOR(tmp, RB_COLOR(parent)); |
| 398 | RB_SET_COLOR(parent, EntryColor::Black); | 364 | RB_SET_COLOR(parent, RBColor::RB_BLACK); |
| 399 | 365 | ||
| 400 | if (RB_LEFT(tmp)) { | 366 | if (RB_LEFT(tmp)) { |
| 401 | RB_SET_COLOR(RB_LEFT(tmp), EntryColor::Black); | 367 | RB_SET_COLOR(RB_LEFT(tmp), RBColor::RB_BLACK); |
| 402 | } | 368 | } |
| 403 | 369 | ||
| 404 | RB_ROTATE_RIGHT(head, parent, tmp); | 370 | RB_ROTATE_RIGHT(head, parent, tmp); |
| 405 | elm = head->Root(); | 371 | elm = head.Root(); |
| 406 | break; | 372 | break; |
| 407 | } | 373 | } |
| 408 | } | 374 | } |
| 409 | } | 375 | } |
| 410 | 376 | ||
| 411 | if (elm) { | 377 | if (elm) { |
| 412 | RB_SET_COLOR(elm, EntryColor::Black); | 378 | RB_SET_COLOR(elm, RBColor::RB_BLACK); |
| 413 | } | 379 | } |
| 414 | } | 380 | } |
| 415 | 381 | ||
| 416 | template <typename Node> | 382 | template <typename T> |
| 417 | Node* RB_REMOVE(RBHead<Node>* head, Node* elm) { | 383 | requires HasRBEntry<T> |
| 418 | Node* child = nullptr; | 384 | constexpr T* RB_REMOVE(RBHead<T>& head, T* elm) { |
| 419 | Node* parent = nullptr; | 385 | T* child = nullptr; |
| 420 | Node* old = elm; | 386 | T* parent = nullptr; |
| 421 | EntryColor color{}; | 387 | T* old = elm; |
| 422 | 388 | 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 | 389 | ||
| 431 | if (RB_LEFT(elm) == nullptr) { | 390 | if (RB_LEFT(elm) == nullptr) { |
| 432 | child = RB_RIGHT(elm); | 391 | child = RB_RIGHT(elm); |
| 433 | } else if (RB_RIGHT(elm) == nullptr) { | 392 | } else if (RB_RIGHT(elm) == nullptr) { |
| 434 | child = RB_LEFT(elm); | 393 | child = RB_LEFT(elm); |
| 435 | } else { | 394 | } else { |
| 436 | Node* left; | 395 | T* left; |
| 437 | elm = RB_RIGHT(elm); | 396 | elm = RB_RIGHT(elm); |
| 438 | while ((left = RB_LEFT(elm)) != nullptr) { | 397 | while ((left = RB_LEFT(elm)) != nullptr) { |
| 439 | elm = left; | 398 | elm = left; |
| @@ -446,6 +405,7 @@ Node* RB_REMOVE(RBHead<Node>* head, Node* elm) { | |||
| 446 | if (child) { | 405 | if (child) { |
| 447 | RB_SET_PARENT(child, parent); | 406 | RB_SET_PARENT(child, parent); |
| 448 | } | 407 | } |
| 408 | |||
| 449 | if (parent) { | 409 | if (parent) { |
| 450 | if (RB_LEFT(parent) == elm) { | 410 | if (RB_LEFT(parent) == elm) { |
| 451 | RB_SET_LEFT(parent, child); | 411 | RB_SET_LEFT(parent, child); |
| @@ -453,14 +413,14 @@ Node* RB_REMOVE(RBHead<Node>* head, Node* elm) { | |||
| 453 | RB_SET_RIGHT(parent, child); | 413 | RB_SET_RIGHT(parent, child); |
| 454 | } | 414 | } |
| 455 | } else { | 415 | } else { |
| 456 | head->SetRoot(child); | 416 | head.SetRoot(child); |
| 457 | } | 417 | } |
| 458 | 418 | ||
| 459 | if (RB_PARENT(elm) == old) { | 419 | if (RB_PARENT(elm) == old) { |
| 460 | parent = elm; | 420 | parent = elm; |
| 461 | } | 421 | } |
| 462 | 422 | ||
| 463 | elm->SetEntry(old->GetEntry()); | 423 | elm->SetRBEntry(old->GetRBEntry()); |
| 464 | 424 | ||
| 465 | if (RB_PARENT(old)) { | 425 | if (RB_PARENT(old)) { |
| 466 | if (RB_LEFT(RB_PARENT(old)) == old) { | 426 | if (RB_LEFT(RB_PARENT(old)) == old) { |
| @@ -469,17 +429,24 @@ Node* RB_REMOVE(RBHead<Node>* head, Node* elm) { | |||
| 469 | RB_SET_RIGHT(RB_PARENT(old), elm); | 429 | RB_SET_RIGHT(RB_PARENT(old), elm); |
| 470 | } | 430 | } |
| 471 | } else { | 431 | } else { |
| 472 | head->SetRoot(elm); | 432 | head.SetRoot(elm); |
| 473 | } | 433 | } |
| 434 | |||
| 474 | RB_SET_PARENT(RB_LEFT(old), elm); | 435 | RB_SET_PARENT(RB_LEFT(old), elm); |
| 436 | |||
| 475 | if (RB_RIGHT(old)) { | 437 | if (RB_RIGHT(old)) { |
| 476 | RB_SET_PARENT(RB_RIGHT(old), elm); | 438 | RB_SET_PARENT(RB_RIGHT(old), elm); |
| 477 | } | 439 | } |
| 440 | |||
| 478 | if (parent) { | 441 | if (parent) { |
| 479 | left = parent; | 442 | left = parent; |
| 480 | } | 443 | } |
| 481 | 444 | ||
| 482 | return finalize(); | 445 | if (color == RBColor::RB_BLACK) { |
| 446 | RB_REMOVE_COLOR(head, parent, child); | ||
| 447 | } | ||
| 448 | |||
| 449 | return old; | ||
| 483 | } | 450 | } |
| 484 | 451 | ||
| 485 | parent = RB_PARENT(elm); | 452 | parent = RB_PARENT(elm); |
| @@ -495,17 +462,69 @@ Node* RB_REMOVE(RBHead<Node>* head, Node* elm) { | |||
| 495 | RB_SET_RIGHT(parent, child); | 462 | RB_SET_RIGHT(parent, child); |
| 496 | } | 463 | } |
| 497 | } else { | 464 | } else { |
| 498 | head->SetRoot(child); | 465 | head.SetRoot(child); |
| 466 | } | ||
| 467 | |||
| 468 | if (color == RBColor::RB_BLACK) { | ||
| 469 | RB_REMOVE_COLOR(head, parent, child); | ||
| 470 | } | ||
| 471 | |||
| 472 | return old; | ||
| 473 | } | ||
| 474 | |||
| 475 | template <typename T> | ||
| 476 | requires HasRBEntry<T> | ||
| 477 | constexpr void RB_INSERT_COLOR(RBHead<T>& head, T* elm) { | ||
| 478 | T *parent = nullptr, *tmp = nullptr; | ||
| 479 | while ((parent = RB_PARENT(elm)) != nullptr && RB_IS_RED(parent)) { | ||
| 480 | T* gparent = RB_PARENT(parent); | ||
| 481 | if (parent == RB_LEFT(gparent)) { | ||
| 482 | tmp = RB_RIGHT(gparent); | ||
| 483 | if (tmp && RB_IS_RED(tmp)) { | ||
| 484 | RB_SET_COLOR(tmp, RBColor::RB_BLACK); | ||
| 485 | RB_SET_BLACKRED(parent, gparent); | ||
| 486 | elm = gparent; | ||
| 487 | continue; | ||
| 488 | } | ||
| 489 | |||
| 490 | if (RB_RIGHT(parent) == elm) { | ||
| 491 | RB_ROTATE_LEFT(head, parent, tmp); | ||
| 492 | tmp = parent; | ||
| 493 | parent = elm; | ||
| 494 | elm = tmp; | ||
| 495 | } | ||
| 496 | |||
| 497 | RB_SET_BLACKRED(parent, gparent); | ||
| 498 | RB_ROTATE_RIGHT(head, gparent, tmp); | ||
| 499 | } else { | ||
| 500 | tmp = RB_LEFT(gparent); | ||
| 501 | if (tmp && RB_IS_RED(tmp)) { | ||
| 502 | RB_SET_COLOR(tmp, RBColor::RB_BLACK); | ||
| 503 | RB_SET_BLACKRED(parent, gparent); | ||
| 504 | elm = gparent; | ||
| 505 | continue; | ||
| 506 | } | ||
| 507 | |||
| 508 | if (RB_LEFT(parent) == elm) { | ||
| 509 | RB_ROTATE_RIGHT(head, parent, tmp); | ||
| 510 | tmp = parent; | ||
| 511 | parent = elm; | ||
| 512 | elm = tmp; | ||
| 513 | } | ||
| 514 | |||
| 515 | RB_SET_BLACKRED(parent, gparent); | ||
| 516 | RB_ROTATE_LEFT(head, gparent, tmp); | ||
| 517 | } | ||
| 499 | } | 518 | } |
| 500 | 519 | ||
| 501 | return finalize(); | 520 | RB_SET_COLOR(head.Root(), RBColor::RB_BLACK); |
| 502 | } | 521 | } |
| 503 | 522 | ||
| 504 | // Inserts a node into the RB tree | 523 | template <typename T, typename Compare> |
| 505 | template <typename Node, typename CompareFunction> | 524 | requires HasRBEntry<T> |
| 506 | Node* RB_INSERT(RBHead<Node>* head, Node* elm, CompareFunction cmp) { | 525 | constexpr T* RB_INSERT(RBHead<T>& head, T* elm, Compare cmp) { |
| 507 | Node* parent = nullptr; | 526 | T* parent = nullptr; |
| 508 | Node* tmp = head->Root(); | 527 | T* tmp = head.Root(); |
| 509 | int comp = 0; | 528 | int comp = 0; |
| 510 | 529 | ||
| 511 | while (tmp) { | 530 | while (tmp) { |
| @@ -529,17 +548,17 @@ Node* RB_INSERT(RBHead<Node>* head, Node* elm, CompareFunction cmp) { | |||
| 529 | RB_SET_RIGHT(parent, elm); | 548 | RB_SET_RIGHT(parent, elm); |
| 530 | } | 549 | } |
| 531 | } else { | 550 | } else { |
| 532 | head->SetRoot(elm); | 551 | head.SetRoot(elm); |
| 533 | } | 552 | } |
| 534 | 553 | ||
| 535 | RB_INSERT_COLOR(head, elm); | 554 | RB_INSERT_COLOR(head, elm); |
| 536 | return nullptr; | 555 | return nullptr; |
| 537 | } | 556 | } |
| 538 | 557 | ||
| 539 | // Finds the node with the same key as elm | 558 | template <typename T, typename Compare> |
| 540 | template <typename Node, typename CompareFunction> | 559 | requires HasRBEntry<T> |
| 541 | Node* RB_FIND(RBHead<Node>* head, Node* elm, CompareFunction cmp) { | 560 | constexpr T* RB_FIND(RBHead<T>& head, T* elm, Compare cmp) { |
| 542 | Node* tmp = head->Root(); | 561 | T* tmp = head.Root(); |
| 543 | 562 | ||
| 544 | while (tmp) { | 563 | while (tmp) { |
| 545 | const int comp = cmp(elm, tmp); | 564 | const int comp = cmp(elm, tmp); |
| @@ -555,11 +574,11 @@ Node* RB_FIND(RBHead<Node>* head, Node* elm, CompareFunction cmp) { | |||
| 555 | return nullptr; | 574 | return nullptr; |
| 556 | } | 575 | } |
| 557 | 576 | ||
| 558 | // Finds the first node greater than or equal to the search key | 577 | template <typename T, typename Compare> |
| 559 | template <typename Node, typename CompareFunction> | 578 | requires HasRBEntry<T> |
| 560 | Node* RB_NFIND(RBHead<Node>* head, Node* elm, CompareFunction cmp) { | 579 | constexpr T* RB_NFIND(RBHead<T>& head, T* elm, Compare cmp) { |
| 561 | Node* tmp = head->Root(); | 580 | T* tmp = head.Root(); |
| 562 | Node* res = nullptr; | 581 | T* res = nullptr; |
| 563 | 582 | ||
| 564 | while (tmp) { | 583 | while (tmp) { |
| 565 | const int comp = cmp(elm, tmp); | 584 | const int comp = cmp(elm, tmp); |
| @@ -576,13 +595,13 @@ Node* RB_NFIND(RBHead<Node>* head, Node* elm, CompareFunction cmp) { | |||
| 576 | return res; | 595 | return res; |
| 577 | } | 596 | } |
| 578 | 597 | ||
| 579 | // Finds the node with the same key as lelm | 598 | template <typename T, typename U, typename Compare> |
| 580 | template <typename Node, typename CompareFunction> | 599 | requires HasRBEntry<T> |
| 581 | Node* RB_FIND_LIGHT(RBHead<Node>* head, const void* lelm, CompareFunction lcmp) { | 600 | constexpr T* RB_FIND_KEY(RBHead<T>& head, const U& key, Compare cmp) { |
| 582 | Node* tmp = head->Root(); | 601 | T* tmp = head.Root(); |
| 583 | 602 | ||
| 584 | while (tmp) { | 603 | while (tmp) { |
| 585 | const int comp = lcmp(lelm, tmp); | 604 | const int comp = cmp(key, tmp); |
| 586 | if (comp < 0) { | 605 | if (comp < 0) { |
| 587 | tmp = RB_LEFT(tmp); | 606 | tmp = RB_LEFT(tmp); |
| 588 | } else if (comp > 0) { | 607 | } else if (comp > 0) { |
| @@ -595,14 +614,14 @@ Node* RB_FIND_LIGHT(RBHead<Node>* head, const void* lelm, CompareFunction lcmp) | |||
| 595 | return nullptr; | 614 | return nullptr; |
| 596 | } | 615 | } |
| 597 | 616 | ||
| 598 | // Finds the first node greater than or equal to the search key | 617 | template <typename T, typename U, typename Compare> |
| 599 | template <typename Node, typename CompareFunction> | 618 | requires HasRBEntry<T> |
| 600 | Node* RB_NFIND_LIGHT(RBHead<Node>* head, const void* lelm, CompareFunction lcmp) { | 619 | constexpr T* RB_NFIND_KEY(RBHead<T>& head, const U& key, Compare cmp) { |
| 601 | Node* tmp = head->Root(); | 620 | T* tmp = head.Root(); |
| 602 | Node* res = nullptr; | 621 | T* res = nullptr; |
| 603 | 622 | ||
| 604 | while (tmp) { | 623 | while (tmp) { |
| 605 | const int comp = lcmp(lelm, tmp); | 624 | const int comp = cmp(key, tmp); |
| 606 | if (comp < 0) { | 625 | if (comp < 0) { |
| 607 | res = tmp; | 626 | res = tmp; |
| 608 | tmp = RB_LEFT(tmp); | 627 | tmp = RB_LEFT(tmp); |
| @@ -616,8 +635,43 @@ Node* RB_NFIND_LIGHT(RBHead<Node>* head, const void* lelm, CompareFunction lcmp) | |||
| 616 | return res; | 635 | return res; |
| 617 | } | 636 | } |
| 618 | 637 | ||
| 619 | template <typename Node> | 638 | template <typename T, typename Compare> |
| 620 | Node* RB_NEXT(Node* elm) { | 639 | requires HasRBEntry<T> |
| 640 | constexpr T* RB_FIND_EXISTING(RBHead<T>& head, T* elm, Compare cmp) { | ||
| 641 | T* tmp = head.Root(); | ||
| 642 | |||
| 643 | while (true) { | ||
| 644 | const int comp = cmp(elm, tmp); | ||
| 645 | if (comp < 0) { | ||
| 646 | tmp = RB_LEFT(tmp); | ||
| 647 | } else if (comp > 0) { | ||
| 648 | tmp = RB_RIGHT(tmp); | ||
| 649 | } else { | ||
| 650 | return tmp; | ||
| 651 | } | ||
| 652 | } | ||
| 653 | } | ||
| 654 | |||
| 655 | template <typename T, typename U, typename Compare> | ||
| 656 | requires HasRBEntry<T> | ||
| 657 | constexpr T* RB_FIND_EXISTING_KEY(RBHead<T>& head, const U& key, Compare cmp) { | ||
| 658 | T* tmp = head.Root(); | ||
| 659 | |||
| 660 | while (true) { | ||
| 661 | const int comp = cmp(key, tmp); | ||
| 662 | if (comp < 0) { | ||
| 663 | tmp = RB_LEFT(tmp); | ||
| 664 | } else if (comp > 0) { | ||
| 665 | tmp = RB_RIGHT(tmp); | ||
| 666 | } else { | ||
| 667 | return tmp; | ||
| 668 | } | ||
| 669 | } | ||
| 670 | } | ||
| 671 | |||
| 672 | template <typename T> | ||
| 673 | requires HasRBEntry<T> | ||
| 674 | constexpr T* RB_NEXT(T* elm) { | ||
| 621 | if (RB_RIGHT(elm)) { | 675 | if (RB_RIGHT(elm)) { |
| 622 | elm = RB_RIGHT(elm); | 676 | elm = RB_RIGHT(elm); |
| 623 | while (RB_LEFT(elm)) { | 677 | while (RB_LEFT(elm)) { |
| @@ -636,8 +690,9 @@ Node* RB_NEXT(Node* elm) { | |||
| 636 | return elm; | 690 | return elm; |
| 637 | } | 691 | } |
| 638 | 692 | ||
| 639 | template <typename Node> | 693 | template <typename T> |
| 640 | Node* RB_PREV(Node* elm) { | 694 | requires HasRBEntry<T> |
| 695 | constexpr T* RB_PREV(T* elm) { | ||
| 641 | if (RB_LEFT(elm)) { | 696 | if (RB_LEFT(elm)) { |
| 642 | elm = RB_LEFT(elm); | 697 | elm = RB_LEFT(elm); |
| 643 | while (RB_RIGHT(elm)) { | 698 | while (RB_RIGHT(elm)) { |
| @@ -656,30 +711,32 @@ Node* RB_PREV(Node* elm) { | |||
| 656 | return elm; | 711 | return elm; |
| 657 | } | 712 | } |
| 658 | 713 | ||
| 659 | template <typename Node> | 714 | template <typename T> |
| 660 | Node* RB_MINMAX(RBHead<Node>* head, bool is_min) { | 715 | requires HasRBEntry<T> |
| 661 | Node* tmp = head->Root(); | 716 | constexpr T* RB_MIN(RBHead<T>& head) { |
| 662 | Node* parent = nullptr; | 717 | T* tmp = head.Root(); |
| 718 | T* parent = nullptr; | ||
| 663 | 719 | ||
| 664 | while (tmp) { | 720 | while (tmp) { |
| 665 | parent = tmp; | 721 | parent = tmp; |
| 666 | if (is_min) { | 722 | tmp = RB_LEFT(tmp); |
| 667 | tmp = RB_LEFT(tmp); | ||
| 668 | } else { | ||
| 669 | tmp = RB_RIGHT(tmp); | ||
| 670 | } | ||
| 671 | } | 723 | } |
| 672 | 724 | ||
| 673 | return parent; | 725 | return parent; |
| 674 | } | 726 | } |
| 675 | 727 | ||
| 676 | template <typename Node> | 728 | template <typename T> |
| 677 | Node* RB_MIN(RBHead<Node>* head) { | 729 | requires HasRBEntry<T> |
| 678 | return RB_MINMAX(head, true); | 730 | constexpr T* RB_MAX(RBHead<T>& head) { |
| 679 | } | 731 | T* tmp = head.Root(); |
| 732 | T* parent = nullptr; | ||
| 680 | 733 | ||
| 681 | template <typename Node> | 734 | while (tmp) { |
| 682 | Node* RB_MAX(RBHead<Node>* head) { | 735 | parent = tmp; |
| 683 | return RB_MINMAX(head, false); | 736 | tmp = RB_RIGHT(tmp); |
| 737 | } | ||
| 738 | |||
| 739 | return parent; | ||
| 684 | } | 740 | } |
| 685 | } // namespace Common | 741 | |
| 742 | } // namespace Common::freebsd | ||
diff --git a/src/common/uuid.cpp b/src/common/uuid.cpp index d7435a6e9..2b6a530e3 100644 --- a/src/common/uuid.cpp +++ b/src/common/uuid.cpp | |||
| @@ -1,23 +1,25 @@ | |||
| 1 | // Copyright 2018 yuzu Emulator Project | 1 | // Copyright 2022 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <bit> | ||
| 6 | #include <optional> | ||
| 5 | #include <random> | 7 | #include <random> |
| 6 | 8 | ||
| 7 | #include <fmt/format.h> | 9 | #include <fmt/format.h> |
| 8 | 10 | ||
| 9 | #include "common/assert.h" | 11 | #include "common/assert.h" |
| 12 | #include "common/tiny_mt.h" | ||
| 10 | #include "common/uuid.h" | 13 | #include "common/uuid.h" |
| 11 | 14 | ||
| 12 | namespace Common { | 15 | namespace Common { |
| 13 | 16 | ||
| 14 | namespace { | 17 | namespace { |
| 15 | 18 | ||
| 16 | bool IsHexDigit(char c) { | 19 | constexpr size_t RawStringSize = sizeof(UUID) * 2; |
| 17 | return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'); | 20 | constexpr size_t FormattedStringSize = RawStringSize + 4; |
| 18 | } | ||
| 19 | 21 | ||
| 20 | u8 HexCharToByte(char c) { | 22 | std::optional<u8> HexCharToByte(char c) { |
| 21 | if (c >= '0' && c <= '9') { | 23 | if (c >= '0' && c <= '9') { |
| 22 | return static_cast<u8>(c - '0'); | 24 | return static_cast<u8>(c - '0'); |
| 23 | } | 25 | } |
| @@ -28,60 +30,184 @@ u8 HexCharToByte(char c) { | |||
| 28 | return static_cast<u8>(c - 'A' + 10); | 30 | return static_cast<u8>(c - 'A' + 10); |
| 29 | } | 31 | } |
| 30 | ASSERT_MSG(false, "{} is not a hexadecimal digit!", c); | 32 | ASSERT_MSG(false, "{} is not a hexadecimal digit!", c); |
| 31 | return u8{0}; | 33 | return std::nullopt; |
| 32 | } | 34 | } |
| 33 | 35 | ||
| 34 | } // Anonymous namespace | 36 | std::array<u8, 0x10> ConstructFromRawString(std::string_view raw_string) { |
| 37 | std::array<u8, 0x10> uuid; | ||
| 38 | |||
| 39 | for (size_t i = 0; i < RawStringSize; i += 2) { | ||
| 40 | const auto upper = HexCharToByte(raw_string[i]); | ||
| 41 | const auto lower = HexCharToByte(raw_string[i + 1]); | ||
| 42 | if (!upper || !lower) { | ||
| 43 | return {}; | ||
| 44 | } | ||
| 45 | uuid[i / 2] = static_cast<u8>((*upper << 4) | *lower); | ||
| 46 | } | ||
| 47 | |||
| 48 | return uuid; | ||
| 49 | } | ||
| 35 | 50 | ||
| 36 | u128 HexStringToU128(std::string_view hex_string) { | 51 | std::array<u8, 0x10> ConstructFromFormattedString(std::string_view formatted_string) { |
| 37 | const size_t length = hex_string.length(); | 52 | std::array<u8, 0x10> uuid; |
| 38 | 53 | ||
| 39 | // Detect "0x" prefix. | 54 | size_t i = 0; |
| 40 | const bool has_0x_prefix = length > 2 && hex_string[0] == '0' && hex_string[1] == 'x'; | ||
| 41 | const size_t offset = has_0x_prefix ? 2 : 0; | ||
| 42 | 55 | ||
| 43 | // Check length. | 56 | // Process the first 8 characters. |
| 44 | if (length > 32 + offset) { | 57 | const auto* str = formatted_string.data(); |
| 45 | ASSERT_MSG(false, "hex_string has more than 32 hexadecimal characters!"); | 58 | |
| 46 | return INVALID_UUID; | 59 | for (; i < 4; ++i) { |
| 60 | const auto upper = HexCharToByte(*(str++)); | ||
| 61 | const auto lower = HexCharToByte(*(str++)); | ||
| 62 | if (!upper || !lower) { | ||
| 63 | return {}; | ||
| 64 | } | ||
| 65 | uuid[i] = static_cast<u8>((*upper << 4) | *lower); | ||
| 66 | } | ||
| 67 | |||
| 68 | // Process the next 4 characters. | ||
| 69 | ++str; | ||
| 70 | |||
| 71 | for (; i < 6; ++i) { | ||
| 72 | const auto upper = HexCharToByte(*(str++)); | ||
| 73 | const auto lower = HexCharToByte(*(str++)); | ||
| 74 | if (!upper || !lower) { | ||
| 75 | return {}; | ||
| 76 | } | ||
| 77 | uuid[i] = static_cast<u8>((*upper << 4) | *lower); | ||
| 47 | } | 78 | } |
| 48 | 79 | ||
| 49 | u64 lo = 0; | 80 | // Process the next 4 characters. |
| 50 | u64 hi = 0; | 81 | ++str; |
| 51 | for (size_t i = 0; i < length - offset; ++i) { | 82 | |
| 52 | const char c = hex_string[length - 1 - i]; | 83 | for (; i < 8; ++i) { |
| 53 | if (!IsHexDigit(c)) { | 84 | const auto upper = HexCharToByte(*(str++)); |
| 54 | ASSERT_MSG(false, "{} is not a hexadecimal digit!", c); | 85 | const auto lower = HexCharToByte(*(str++)); |
| 55 | return INVALID_UUID; | 86 | if (!upper || !lower) { |
| 87 | return {}; | ||
| 56 | } | 88 | } |
| 57 | if (i < 16) { | 89 | uuid[i] = static_cast<u8>((*upper << 4) | *lower); |
| 58 | lo |= u64{HexCharToByte(c)} << (i * 4); | 90 | } |
| 91 | |||
| 92 | // Process the next 4 characters. | ||
| 93 | ++str; | ||
| 94 | |||
| 95 | for (; i < 10; ++i) { | ||
| 96 | const auto upper = HexCharToByte(*(str++)); | ||
| 97 | const auto lower = HexCharToByte(*(str++)); | ||
| 98 | if (!upper || !lower) { | ||
| 99 | return {}; | ||
| 59 | } | 100 | } |
| 60 | if (i >= 16) { | 101 | uuid[i] = static_cast<u8>((*upper << 4) | *lower); |
| 61 | hi |= u64{HexCharToByte(c)} << ((i - 16) * 4); | 102 | } |
| 103 | |||
| 104 | // Process the last 12 characters. | ||
| 105 | ++str; | ||
| 106 | |||
| 107 | for (; i < 16; ++i) { | ||
| 108 | const auto upper = HexCharToByte(*(str++)); | ||
| 109 | const auto lower = HexCharToByte(*(str++)); | ||
| 110 | if (!upper || !lower) { | ||
| 111 | return {}; | ||
| 62 | } | 112 | } |
| 113 | uuid[i] = static_cast<u8>((*upper << 4) | *lower); | ||
| 114 | } | ||
| 115 | |||
| 116 | return uuid; | ||
| 117 | } | ||
| 118 | |||
| 119 | std::array<u8, 0x10> ConstructUUID(std::string_view uuid_string) { | ||
| 120 | const auto length = uuid_string.length(); | ||
| 121 | |||
| 122 | if (length == 0) { | ||
| 123 | return {}; | ||
| 124 | } | ||
| 125 | |||
| 126 | // Check if the input string contains 32 hexadecimal characters. | ||
| 127 | if (length == RawStringSize) { | ||
| 128 | return ConstructFromRawString(uuid_string); | ||
| 129 | } | ||
| 130 | |||
| 131 | // Check if the input string has the length of a RFC 4122 formatted UUID string. | ||
| 132 | if (length == FormattedStringSize) { | ||
| 133 | return ConstructFromFormattedString(uuid_string); | ||
| 63 | } | 134 | } |
| 64 | return u128{lo, hi}; | 135 | |
| 136 | ASSERT_MSG(false, "UUID string has an invalid length of {} characters!", length); | ||
| 137 | |||
| 138 | return {}; | ||
| 139 | } | ||
| 140 | |||
| 141 | } // Anonymous namespace | ||
| 142 | |||
| 143 | UUID::UUID(std::string_view uuid_string) : uuid{ConstructUUID(uuid_string)} {} | ||
| 144 | |||
| 145 | std::string UUID::RawString() const { | ||
| 146 | return fmt::format("{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}" | ||
| 147 | "{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}", | ||
| 148 | uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5], uuid[6], uuid[7], | ||
| 149 | uuid[8], uuid[9], uuid[10], uuid[11], uuid[12], uuid[13], uuid[14], | ||
| 150 | uuid[15]); | ||
| 151 | } | ||
| 152 | |||
| 153 | std::string UUID::FormattedString() const { | ||
| 154 | return fmt::format("{:02x}{:02x}{:02x}{:02x}" | ||
| 155 | "-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-" | ||
| 156 | "{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}", | ||
| 157 | uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5], uuid[6], uuid[7], | ||
| 158 | uuid[8], uuid[9], uuid[10], uuid[11], uuid[12], uuid[13], uuid[14], | ||
| 159 | uuid[15]); | ||
| 160 | } | ||
| 161 | |||
| 162 | size_t UUID::Hash() const noexcept { | ||
| 163 | u64 upper_hash; | ||
| 164 | u64 lower_hash; | ||
| 165 | |||
| 166 | std::memcpy(&upper_hash, uuid.data(), sizeof(u64)); | ||
| 167 | std::memcpy(&lower_hash, uuid.data() + sizeof(u64), sizeof(u64)); | ||
| 168 | |||
| 169 | return upper_hash ^ std::rotl(lower_hash, 1); | ||
| 65 | } | 170 | } |
| 66 | 171 | ||
| 67 | UUID UUID::Generate() { | 172 | u128 UUID::AsU128() const { |
| 173 | u128 uuid_old; | ||
| 174 | std::memcpy(&uuid_old, uuid.data(), sizeof(UUID)); | ||
| 175 | return uuid_old; | ||
| 176 | } | ||
| 177 | |||
| 178 | UUID UUID::MakeRandom() { | ||
| 68 | std::random_device device; | 179 | std::random_device device; |
| 69 | std::mt19937 gen(device()); | 180 | |
| 70 | std::uniform_int_distribution<u64> distribution(1, std::numeric_limits<u64>::max()); | 181 | return MakeRandomWithSeed(device()); |
| 71 | return UUID{distribution(gen), distribution(gen)}; | ||
| 72 | } | 182 | } |
| 73 | 183 | ||
| 74 | std::string UUID::Format() const { | 184 | UUID UUID::MakeRandomWithSeed(u32 seed) { |
| 75 | return fmt::format("{:016x}{:016x}", uuid[1], uuid[0]); | 185 | // Create and initialize our RNG. |
| 186 | TinyMT rng; | ||
| 187 | rng.Initialize(seed); | ||
| 188 | |||
| 189 | UUID uuid; | ||
| 190 | |||
| 191 | // Populate the UUID with random bytes. | ||
| 192 | rng.GenerateRandomBytes(uuid.uuid.data(), sizeof(UUID)); | ||
| 193 | |||
| 194 | return uuid; | ||
| 76 | } | 195 | } |
| 77 | 196 | ||
| 78 | std::string UUID::FormatSwitch() const { | 197 | UUID UUID::MakeRandomRFC4122V4() { |
| 79 | std::array<u8, 16> s{}; | 198 | auto uuid = MakeRandom(); |
| 80 | std::memcpy(s.data(), uuid.data(), sizeof(u128)); | 199 | |
| 81 | return fmt::format("{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{" | 200 | // According to Proposed Standard RFC 4122 Section 4.4, we must: |
| 82 | ":02x}{:02x}{:02x}{:02x}{:02x}", | 201 | |
| 83 | s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7], s[8], s[9], s[10], s[11], | 202 | // 1. Set the two most significant bits (bits 6 and 7) of the |
| 84 | s[12], s[13], s[14], s[15]); | 203 | // clock_seq_hi_and_reserved to zero and one, respectively. |
| 204 | uuid.uuid[8] = 0x80 | (uuid.uuid[8] & 0x3F); | ||
| 205 | |||
| 206 | // 2. Set the four most significant bits (bits 12 through 15) of the | ||
| 207 | // time_hi_and_version field to the 4-bit version number from Section 4.1.3. | ||
| 208 | uuid.uuid[6] = 0x40 | (uuid.uuid[6] & 0xF); | ||
| 209 | |||
| 210 | return uuid; | ||
| 85 | } | 211 | } |
| 86 | 212 | ||
| 87 | } // namespace Common | 213 | } // namespace Common |
diff --git a/src/common/uuid.h b/src/common/uuid.h index 8ea01f8da..fe31e64e6 100644 --- a/src/common/uuid.h +++ b/src/common/uuid.h | |||
| @@ -1,9 +1,11 @@ | |||
| 1 | // Copyright 2018 yuzu Emulator Project | 1 | // Copyright 2022 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <array> | ||
| 8 | #include <functional> | ||
| 7 | #include <string> | 9 | #include <string> |
| 8 | #include <string_view> | 10 | #include <string_view> |
| 9 | 11 | ||
| @@ -11,69 +13,119 @@ | |||
| 11 | 13 | ||
| 12 | namespace Common { | 14 | namespace Common { |
| 13 | 15 | ||
| 14 | constexpr u128 INVALID_UUID{{0, 0}}; | ||
| 15 | |||
| 16 | /** | ||
| 17 | * Converts a hex string to a 128-bit unsigned integer. | ||
| 18 | * | ||
| 19 | * The hex string can be formatted in lowercase or uppercase, with or without the "0x" prefix. | ||
| 20 | * | ||
| 21 | * This function will assert and return INVALID_UUID under the following conditions: | ||
| 22 | * - If the hex string is more than 32 characters long | ||
| 23 | * - If the hex string contains non-hexadecimal characters | ||
| 24 | * | ||
| 25 | * @param hex_string Hexadecimal string | ||
| 26 | * | ||
| 27 | * @returns A 128-bit unsigned integer if successfully converted, INVALID_UUID otherwise. | ||
| 28 | */ | ||
| 29 | [[nodiscard]] u128 HexStringToU128(std::string_view hex_string); | ||
| 30 | |||
| 31 | struct UUID { | 16 | struct UUID { |
| 32 | // UUIDs which are 0 are considered invalid! | 17 | std::array<u8, 0x10> uuid{}; |
| 33 | u128 uuid; | 18 | |
| 34 | UUID() = default; | 19 | /// Constructs an invalid UUID. |
| 35 | constexpr explicit UUID(const u128& id) : uuid{id} {} | 20 | constexpr UUID() = default; |
| 36 | constexpr explicit UUID(const u64 lo, const u64 hi) : uuid{{lo, hi}} {} | 21 | |
| 37 | explicit UUID(std::string_view hex_string) { | 22 | /// Constructs a UUID from a reference to a 128 bit array. |
| 38 | uuid = HexStringToU128(hex_string); | 23 | constexpr explicit UUID(const std::array<u8, 16>& uuid_) : uuid{uuid_} {} |
| 39 | } | 24 | |
| 40 | 25 | /** | |
| 41 | [[nodiscard]] constexpr explicit operator bool() const { | 26 | * Constructs a UUID from either: |
| 42 | return uuid != INVALID_UUID; | 27 | * 1. A 32 hexadecimal character string representing the bytes of the UUID |
| 43 | } | 28 | * 2. A RFC 4122 formatted UUID string, in the format xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx |
| 44 | 29 | * | |
| 45 | [[nodiscard]] constexpr bool operator==(const UUID& rhs) const { | 30 | * The input string may contain uppercase or lowercase characters, but they must: |
| 46 | return uuid == rhs.uuid; | 31 | * 1. Contain valid hexadecimal characters (0-9, a-f, A-F) |
| 47 | } | 32 | * 2. Not contain the "0x" hexadecimal prefix |
| 48 | 33 | * | |
| 49 | [[nodiscard]] constexpr bool operator!=(const UUID& rhs) const { | 34 | * Should the input string not meet the above requirements, |
| 50 | return !operator==(rhs); | 35 | * an assert will be triggered and an invalid UUID is set instead. |
| 51 | } | 36 | */ |
| 52 | 37 | explicit UUID(std::string_view uuid_string); | |
| 53 | // TODO(ogniK): Properly generate uuids based on RFC-4122 | 38 | |
| 54 | [[nodiscard]] static UUID Generate(); | 39 | ~UUID() = default; |
| 55 | 40 | ||
| 56 | // Set the UUID to {0,0} to be considered an invalid user | 41 | constexpr UUID(const UUID&) noexcept = default; |
| 57 | constexpr void Invalidate() { | 42 | constexpr UUID(UUID&&) noexcept = default; |
| 58 | uuid = INVALID_UUID; | 43 | |
| 44 | constexpr UUID& operator=(const UUID&) noexcept = default; | ||
| 45 | constexpr UUID& operator=(UUID&&) noexcept = default; | ||
| 46 | |||
| 47 | /** | ||
| 48 | * Returns whether the stored UUID is valid or not. | ||
| 49 | * | ||
| 50 | * @returns True if the stored UUID is valid, false otherwise. | ||
| 51 | */ | ||
| 52 | constexpr bool IsValid() const { | ||
| 53 | return uuid != std::array<u8, 0x10>{}; | ||
| 59 | } | 54 | } |
| 60 | 55 | ||
| 61 | [[nodiscard]] constexpr bool IsInvalid() const { | 56 | /** |
| 62 | return uuid == INVALID_UUID; | 57 | * Returns whether the stored UUID is invalid or not. |
| 63 | } | 58 | * |
| 64 | [[nodiscard]] constexpr bool IsValid() const { | 59 | * @returns True if the stored UUID is invalid, false otherwise. |
| 65 | return !IsInvalid(); | 60 | */ |
| 61 | constexpr bool IsInvalid() const { | ||
| 62 | return !IsValid(); | ||
| 66 | } | 63 | } |
| 67 | 64 | ||
| 68 | // TODO(ogniK): Properly generate a Nintendo ID | 65 | /** |
| 69 | [[nodiscard]] constexpr u64 GetNintendoID() const { | 66 | * Returns a 32 hexadecimal character string representing the bytes of the UUID. |
| 70 | return uuid[0]; | 67 | * |
| 68 | * @returns A 32 hexadecimal character string of the UUID. | ||
| 69 | */ | ||
| 70 | std::string RawString() const; | ||
| 71 | |||
| 72 | /** | ||
| 73 | * Returns a RFC 4122 formatted UUID string in the format xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx. | ||
| 74 | * | ||
| 75 | * @returns A RFC 4122 formatted UUID string. | ||
| 76 | */ | ||
| 77 | std::string FormattedString() const; | ||
| 78 | |||
| 79 | /** | ||
| 80 | * Returns a 64-bit hash of the UUID for use in hash table data structures. | ||
| 81 | * | ||
| 82 | * @returns A 64-bit hash of the UUID. | ||
| 83 | */ | ||
| 84 | size_t Hash() const noexcept; | ||
| 85 | |||
| 86 | /// DO NOT USE. Copies the contents of the UUID into a u128. | ||
| 87 | u128 AsU128() const; | ||
| 88 | |||
| 89 | /** | ||
| 90 | * Creates a default UUID "yuzu Default UID". | ||
| 91 | * | ||
| 92 | * @returns A UUID with its bytes set to the ASCII values of "yuzu Default UID". | ||
| 93 | */ | ||
| 94 | static constexpr UUID MakeDefault() { | ||
| 95 | return UUID{ | ||
| 96 | {'y', 'u', 'z', 'u', ' ', 'D', 'e', 'f', 'a', 'u', 'l', 't', ' ', 'U', 'I', 'D'}, | ||
| 97 | }; | ||
| 71 | } | 98 | } |
| 72 | 99 | ||
| 73 | [[nodiscard]] std::string Format() const; | 100 | /** |
| 74 | [[nodiscard]] std::string FormatSwitch() const; | 101 | * Creates a random UUID. |
| 102 | * | ||
| 103 | * @returns A random UUID. | ||
| 104 | */ | ||
| 105 | static UUID MakeRandom(); | ||
| 106 | |||
| 107 | /** | ||
| 108 | * Creates a random UUID with a seed. | ||
| 109 | * | ||
| 110 | * @param seed A seed to initialize the Mersenne-Twister RNG | ||
| 111 | * | ||
| 112 | * @returns A random UUID. | ||
| 113 | */ | ||
| 114 | static UUID MakeRandomWithSeed(u32 seed); | ||
| 115 | |||
| 116 | /** | ||
| 117 | * Creates a random UUID. The generated UUID is RFC 4122 Version 4 compliant. | ||
| 118 | * | ||
| 119 | * @returns A random UUID that is RFC 4122 Version 4 compliant. | ||
| 120 | */ | ||
| 121 | static UUID MakeRandomRFC4122V4(); | ||
| 122 | |||
| 123 | friend constexpr bool operator==(const UUID& lhs, const UUID& rhs) = default; | ||
| 75 | }; | 124 | }; |
| 76 | static_assert(sizeof(UUID) == 16, "UUID is an invalid size!"); | 125 | static_assert(sizeof(UUID) == 0x10, "UUID has incorrect size."); |
| 126 | |||
| 127 | /// An invalid UUID. This UUID has all its bytes set to 0. | ||
| 128 | constexpr UUID InvalidUUID = {}; | ||
| 77 | 129 | ||
| 78 | } // namespace Common | 130 | } // namespace Common |
| 79 | 131 | ||
| @@ -82,7 +134,7 @@ namespace std { | |||
| 82 | template <> | 134 | template <> |
| 83 | struct hash<Common::UUID> { | 135 | struct hash<Common::UUID> { |
| 84 | size_t operator()(const Common::UUID& uuid) const noexcept { | 136 | size_t operator()(const Common::UUID& uuid) const noexcept { |
| 85 | return uuid.uuid[1] ^ uuid.uuid[0]; | 137 | return uuid.Hash(); |
| 86 | } | 138 | } |
| 87 | }; | 139 | }; |
| 88 | 140 | ||
diff --git a/src/common/x64/cpu_detect.cpp b/src/common/x64/cpu_detect.cpp index fbeacc7e2..d81edb140 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 | // Copyright 2013 Dolphin Emulator Project / 2015 Citra Emulator Project / 2022 Yuzu Emulator |
| 2 | // Licensed under GPLv2 or any later version | 2 | // Project Licensed under GPLv2 or any later version Refer to the license.txt file included. |
| 3 | // Refer to the license.txt file included. | ||
| 4 | 3 | ||
| 4 | #include <array> | ||
| 5 | #include <cstring> | 5 | #include <cstring> |
| 6 | #include <iterator> | ||
| 7 | #include <span> | ||
| 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); | ||
| 75 | 82 | ||
| 76 | u32 max_ex_fn = cpu_id[0]; | 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)); | ||
| 77 | 85 | ||
| 78 | // Set reasonable default brand string even if brand string not available | 86 | __cpuid(cpu_id, 0x80000000); |
| 79 | strcpy(caps.cpu_string, caps.brand_string); | 87 | |
| 88 | const u32 max_ex_fn = cpu_id[0]; | ||
| 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,15 +152,13 @@ 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; | ||
| 149 | } | ||
| 150 | } | 162 | } |
| 151 | 163 | ||
| 152 | if (max_std_fn >= 0x16) { | 164 | if (max_std_fn >= 0x16) { |
diff --git a/src/common/x64/cpu_detect.h b/src/common/x64/cpu_detect.h index e3b63302e..40c48b132 100644 --- a/src/common/x64/cpu_detect.h +++ b/src/common/x64/cpu_detect.h | |||
| @@ -1,42 +1,65 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project / 2015 Citra Emulator Project | 1 | // Copyright 2013 Dolphin Emulator Project / 2015 Citra Emulator Project / 2022 Yuzu Emulator |
| 2 | // Licensed under GPLv2 or any later version | 2 | // Project Project Licensed under GPLv2 or any later version Refer to the license.txt file included. |
| 3 | // Refer to the license.txt file included. | ||
| 4 | 3 | ||
| 5 | #pragma once | 4 | #pragma once |
| 6 | 5 | ||
| 7 | namespace Common { | 6 | #include <string_view> |
| 7 | #include "common/common_types.h" | ||
| 8 | 8 | ||
| 9 | enum class Manufacturer : u32 { | 9 | namespace Common { |
| 10 | Intel = 0, | ||
| 11 | AMD = 1, | ||
| 12 | Hygon = 2, | ||
| 13 | Unknown = 3, | ||
| 14 | }; | ||
| 15 | 10 | ||
| 16 | /// x86/x64 CPU capabilities that may be detected by this module | 11 | /// x86/x64 CPU capabilities that may be detected by this module |
| 17 | struct CPUCaps { | 12 | struct CPUCaps { |
| 13 | |||
| 14 | enum class Manufacturer : u8 { | ||
| 15 | Unknown = 0, | ||
| 16 | Intel = 1, | ||
| 17 | AMD = 2, | ||
| 18 | Hygon = 3, | ||
| 19 | }; | ||
| 20 | |||
| 21 | static Manufacturer ParseManufacturer(std::string_view brand_string); | ||
| 22 | |||
| 18 | Manufacturer manufacturer; | 23 | Manufacturer manufacturer; |
| 19 | char cpu_string[0x21]; | 24 | char brand_string[13]; |
| 20 | char brand_string[0x41]; | 25 | |
| 21 | bool sse; | 26 | char cpu_string[48]; |
| 22 | bool sse2; | 27 | |
| 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; | 28 | u32 base_frequency; |
| 38 | u32 max_frequency; | 29 | u32 max_frequency; |
| 39 | u32 bus_frequency; | 30 | u32 bus_frequency; |
| 31 | |||
| 32 | bool sse : 1; | ||
| 33 | bool sse2 : 1; | ||
| 34 | bool sse3 : 1; | ||
| 35 | bool ssse3 : 1; | ||
| 36 | bool sse4_1 : 1; | ||
| 37 | bool sse4_2 : 1; | ||
| 38 | |||
| 39 | bool avx : 1; | ||
| 40 | bool avx_vnni : 1; | ||
| 41 | bool avx2 : 1; | ||
| 42 | bool avx512f : 1; | ||
| 43 | bool avx512dq : 1; | ||
| 44 | bool avx512cd : 1; | ||
| 45 | bool avx512bw : 1; | ||
| 46 | bool avx512vl : 1; | ||
| 47 | bool avx512vbmi : 1; | ||
| 48 | bool avx512bitalg : 1; | ||
| 49 | |||
| 50 | bool aes : 1; | ||
| 51 | bool bmi1 : 1; | ||
| 52 | bool bmi2 : 1; | ||
| 53 | bool f16c : 1; | ||
| 54 | bool fma : 1; | ||
| 55 | bool fma4 : 1; | ||
| 56 | bool gfni : 1; | ||
| 57 | bool invariant_tsc : 1; | ||
| 58 | bool lzcnt : 1; | ||
| 59 | bool movbe : 1; | ||
| 60 | bool pclmulqdq : 1; | ||
| 61 | bool popcnt : 1; | ||
| 62 | bool sha : 1; | ||
| 40 | }; | 63 | }; |
| 41 | 64 | ||
| 42 | /** | 65 | /** |
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 6e8d11919..1d4e92edb 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt | |||
| @@ -122,6 +122,8 @@ add_library(core STATIC | |||
| 122 | frontend/applets/error.h | 122 | frontend/applets/error.h |
| 123 | frontend/applets/general_frontend.cpp | 123 | frontend/applets/general_frontend.cpp |
| 124 | frontend/applets/general_frontend.h | 124 | frontend/applets/general_frontend.h |
| 125 | frontend/applets/mii.cpp | ||
| 126 | frontend/applets/mii.h | ||
| 125 | frontend/applets/profile_select.cpp | 127 | frontend/applets/profile_select.cpp |
| 126 | frontend/applets/profile_select.h | 128 | frontend/applets/profile_select.h |
| 127 | frontend/applets/software_keyboard.cpp | 129 | frontend/applets/software_keyboard.cpp |
| @@ -152,6 +154,7 @@ add_library(core STATIC | |||
| 152 | hle/api_version.h | 154 | hle/api_version.h |
| 153 | hle/ipc.h | 155 | hle/ipc.h |
| 154 | hle/ipc_helpers.h | 156 | hle/ipc_helpers.h |
| 157 | hle/kernel/board/nintendo/nx/k_memory_layout.h | ||
| 155 | hle/kernel/board/nintendo/nx/k_system_control.cpp | 158 | hle/kernel/board/nintendo/nx/k_system_control.cpp |
| 156 | hle/kernel/board/nintendo/nx/k_system_control.h | 159 | hle/kernel/board/nintendo/nx/k_system_control.h |
| 157 | hle/kernel/board/nintendo/nx/secure_monitor.h | 160 | hle/kernel/board/nintendo/nx/secure_monitor.h |
| @@ -164,6 +167,7 @@ add_library(core STATIC | |||
| 164 | hle/kernel/hle_ipc.h | 167 | hle/kernel/hle_ipc.h |
| 165 | hle/kernel/init/init_slab_setup.cpp | 168 | hle/kernel/init/init_slab_setup.cpp |
| 166 | hle/kernel/init/init_slab_setup.h | 169 | hle/kernel/init/init_slab_setup.h |
| 170 | hle/kernel/initial_process.h | ||
| 167 | hle/kernel/k_address_arbiter.cpp | 171 | hle/kernel/k_address_arbiter.cpp |
| 168 | hle/kernel/k_address_arbiter.h | 172 | hle/kernel/k_address_arbiter.h |
| 169 | hle/kernel/k_address_space_info.cpp | 173 | hle/kernel/k_address_space_info.cpp |
| @@ -205,6 +209,8 @@ add_library(core STATIC | |||
| 205 | hle/kernel/k_memory_region.h | 209 | hle/kernel/k_memory_region.h |
| 206 | hle/kernel/k_memory_region_type.h | 210 | hle/kernel/k_memory_region_type.h |
| 207 | hle/kernel/k_page_bitmap.h | 211 | hle/kernel/k_page_bitmap.h |
| 212 | hle/kernel/k_page_buffer.cpp | ||
| 213 | hle/kernel/k_page_buffer.h | ||
| 208 | hle/kernel/k_page_heap.cpp | 214 | hle/kernel/k_page_heap.cpp |
| 209 | hle/kernel/k_page_heap.h | 215 | hle/kernel/k_page_heap.h |
| 210 | hle/kernel/k_page_linked_list.h | 216 | hle/kernel/k_page_linked_list.h |
| @@ -242,6 +248,8 @@ add_library(core STATIC | |||
| 242 | hle/kernel/k_system_control.h | 248 | hle/kernel/k_system_control.h |
| 243 | hle/kernel/k_thread.cpp | 249 | hle/kernel/k_thread.cpp |
| 244 | hle/kernel/k_thread.h | 250 | hle/kernel/k_thread.h |
| 251 | hle/kernel/k_thread_local_page.cpp | ||
| 252 | hle/kernel/k_thread_local_page.h | ||
| 245 | hle/kernel/k_thread_queue.cpp | 253 | hle/kernel/k_thread_queue.cpp |
| 246 | hle/kernel/k_thread_queue.h | 254 | hle/kernel/k_thread_queue.h |
| 247 | hle/kernel/k_trace.h | 255 | hle/kernel/k_trace.h |
| @@ -298,6 +306,8 @@ add_library(core STATIC | |||
| 298 | hle/service/am/applets/applet_error.h | 306 | hle/service/am/applets/applet_error.h |
| 299 | hle/service/am/applets/applet_general_backend.cpp | 307 | hle/service/am/applets/applet_general_backend.cpp |
| 300 | hle/service/am/applets/applet_general_backend.h | 308 | hle/service/am/applets/applet_general_backend.h |
| 309 | hle/service/am/applets/applet_mii.cpp | ||
| 310 | hle/service/am/applets/applet_mii.h | ||
| 301 | hle/service/am/applets/applet_profile_select.cpp | 311 | hle/service/am/applets/applet_profile_select.cpp |
| 302 | hle/service/am/applets/applet_profile_select.h | 312 | hle/service/am/applets/applet_profile_select.h |
| 303 | hle/service/am/applets/applet_software_keyboard.cpp | 313 | hle/service/am/applets/applet_software_keyboard.cpp |
| @@ -467,6 +477,8 @@ add_library(core STATIC | |||
| 467 | hle/service/mii/types.h | 477 | hle/service/mii/types.h |
| 468 | hle/service/mm/mm_u.cpp | 478 | hle/service/mm/mm_u.cpp |
| 469 | hle/service/mm/mm_u.h | 479 | hle/service/mm/mm_u.h |
| 480 | hle/service/mnpp/mnpp_app.cpp | ||
| 481 | hle/service/mnpp/mnpp_app.h | ||
| 470 | hle/service/ncm/ncm.cpp | 482 | hle/service/ncm/ncm.cpp |
| 471 | hle/service/ncm/ncm.h | 483 | hle/service/ncm/ncm.h |
| 472 | hle/service/nfc/nfc.cpp | 484 | hle/service/nfc/nfc.cpp |
diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h index 689e3ceb5..c60322442 100644 --- a/src/core/arm/arm_interface.h +++ b/src/core/arm/arm_interface.h | |||
| @@ -6,6 +6,7 @@ | |||
| 6 | 6 | ||
| 7 | #include <array> | 7 | #include <array> |
| 8 | #include <vector> | 8 | #include <vector> |
| 9 | #include "common/common_funcs.h" | ||
| 9 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 10 | #include "core/hardware_properties.h" | 11 | #include "core/hardware_properties.h" |
| 11 | 12 | ||
| @@ -24,8 +25,11 @@ class CPUInterruptHandler; | |||
| 24 | using CPUInterrupts = std::array<CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES>; | 25 | using CPUInterrupts = std::array<CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES>; |
| 25 | 26 | ||
| 26 | /// Generic ARMv8 CPU interface | 27 | /// Generic ARMv8 CPU interface |
| 27 | class ARM_Interface : NonCopyable { | 28 | class ARM_Interface { |
| 28 | public: | 29 | public: |
| 30 | YUZU_NON_COPYABLE(ARM_Interface); | ||
| 31 | YUZU_NON_MOVEABLE(ARM_Interface); | ||
| 32 | |||
| 29 | explicit ARM_Interface(System& system_, CPUInterrupts& interrupt_handlers_, | 33 | explicit ARM_Interface(System& system_, CPUInterrupts& interrupt_handlers_, |
| 30 | bool uses_wall_clock_) | 34 | bool uses_wall_clock_) |
| 31 | : system{system_}, interrupt_handlers{interrupt_handlers_}, uses_wall_clock{ | 35 | : system{system_}, interrupt_handlers{interrupt_handlers_}, uses_wall_clock{ |
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp index b0d89c539..c1c843b8f 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp | |||
| @@ -137,6 +137,8 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable* | |||
| 137 | config.page_table_pointer_mask_bits = Common::PageTable::ATTRIBUTE_BITS; | 137 | config.page_table_pointer_mask_bits = Common::PageTable::ATTRIBUTE_BITS; |
| 138 | config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128; | 138 | config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128; |
| 139 | config.only_detect_misalignment_via_page_table_on_page_boundary = true; | 139 | config.only_detect_misalignment_via_page_table_on_page_boundary = true; |
| 140 | config.fastmem_exclusive_access = true; | ||
| 141 | config.recompile_on_exclusive_fastmem_failure = true; | ||
| 140 | 142 | ||
| 141 | // Multi-process state | 143 | // Multi-process state |
| 142 | config.processor_id = core_index; | 144 | config.processor_id = core_index; |
| @@ -146,8 +148,8 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable* | |||
| 146 | config.wall_clock_cntpct = uses_wall_clock; | 148 | config.wall_clock_cntpct = uses_wall_clock; |
| 147 | 149 | ||
| 148 | // Code cache size | 150 | // Code cache size |
| 149 | config.code_cache_size = 512_MiB; | 151 | config.code_cache_size = 128_MiB; |
| 150 | config.far_code_offset = 400_MiB; | 152 | config.far_code_offset = 100_MiB; |
| 151 | 153 | ||
| 152 | // Safe optimizations | 154 | // Safe optimizations |
| 153 | if (Settings::values.cpu_debug_mode) { | 155 | if (Settings::values.cpu_debug_mode) { |
| @@ -178,6 +180,12 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable* | |||
| 178 | if (!Settings::values.cpuopt_fastmem) { | 180 | if (!Settings::values.cpuopt_fastmem) { |
| 179 | config.fastmem_pointer = nullptr; | 181 | config.fastmem_pointer = nullptr; |
| 180 | } | 182 | } |
| 183 | if (!Settings::values.cpuopt_fastmem_exclusives) { | ||
| 184 | config.fastmem_exclusive_access = false; | ||
| 185 | } | ||
| 186 | if (!Settings::values.cpuopt_recompile_exclusives) { | ||
| 187 | config.recompile_on_exclusive_fastmem_failure = false; | ||
| 188 | } | ||
| 181 | } | 189 | } |
| 182 | 190 | ||
| 183 | // Unsafe optimizations | 191 | // Unsafe optimizations |
| @@ -195,6 +203,9 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable* | |||
| 195 | if (Settings::values.cpuopt_unsafe_inaccurate_nan) { | 203 | if (Settings::values.cpuopt_unsafe_inaccurate_nan) { |
| 196 | config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN; | 204 | config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN; |
| 197 | } | 205 | } |
| 206 | if (Settings::values.cpuopt_unsafe_ignore_global_monitor) { | ||
| 207 | config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor; | ||
| 208 | } | ||
| 198 | } | 209 | } |
| 199 | 210 | ||
| 200 | // Curated optimizations | 211 | // Curated optimizations |
| @@ -203,6 +214,7 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable* | |||
| 203 | config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA; | 214 | config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA; |
| 204 | config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreStandardFPCRValue; | 215 | config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreStandardFPCRValue; |
| 205 | config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN; | 216 | config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN; |
| 217 | config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor; | ||
| 206 | } | 218 | } |
| 207 | 219 | ||
| 208 | return std::make_unique<Dynarmic::A32::Jit>(config); | 220 | return std::make_unique<Dynarmic::A32::Jit>(config); |
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp index 56836bd05..aa74fce4d 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp | |||
| @@ -185,6 +185,9 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable* | |||
| 185 | config.fastmem_pointer = page_table->fastmem_arena; | 185 | config.fastmem_pointer = page_table->fastmem_arena; |
| 186 | config.fastmem_address_space_bits = address_space_bits; | 186 | config.fastmem_address_space_bits = address_space_bits; |
| 187 | config.silently_mirror_fastmem = false; | 187 | config.silently_mirror_fastmem = false; |
| 188 | |||
| 189 | config.fastmem_exclusive_access = true; | ||
| 190 | config.recompile_on_exclusive_fastmem_failure = true; | ||
| 188 | } | 191 | } |
| 189 | 192 | ||
| 190 | // Multi-process state | 193 | // Multi-process state |
| @@ -205,8 +208,8 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable* | |||
| 205 | config.wall_clock_cntpct = uses_wall_clock; | 208 | config.wall_clock_cntpct = uses_wall_clock; |
| 206 | 209 | ||
| 207 | // Code cache size | 210 | // Code cache size |
| 208 | config.code_cache_size = 512_MiB; | 211 | config.code_cache_size = 128_MiB; |
| 209 | config.far_code_offset = 400_MiB; | 212 | config.far_code_offset = 100_MiB; |
| 210 | 213 | ||
| 211 | // Safe optimizations | 214 | // Safe optimizations |
| 212 | if (Settings::values.cpu_debug_mode) { | 215 | if (Settings::values.cpu_debug_mode) { |
| @@ -237,6 +240,12 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable* | |||
| 237 | if (!Settings::values.cpuopt_fastmem) { | 240 | if (!Settings::values.cpuopt_fastmem) { |
| 238 | config.fastmem_pointer = nullptr; | 241 | config.fastmem_pointer = nullptr; |
| 239 | } | 242 | } |
| 243 | if (!Settings::values.cpuopt_fastmem_exclusives) { | ||
| 244 | config.fastmem_exclusive_access = false; | ||
| 245 | } | ||
| 246 | if (!Settings::values.cpuopt_recompile_exclusives) { | ||
| 247 | config.recompile_on_exclusive_fastmem_failure = false; | ||
| 248 | } | ||
| 240 | } | 249 | } |
| 241 | 250 | ||
| 242 | // Unsafe optimizations | 251 | // Unsafe optimizations |
| @@ -254,6 +263,9 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable* | |||
| 254 | if (Settings::values.cpuopt_unsafe_fastmem_check) { | 263 | if (Settings::values.cpuopt_unsafe_fastmem_check) { |
| 255 | config.fastmem_address_space_bits = 64; | 264 | config.fastmem_address_space_bits = 64; |
| 256 | } | 265 | } |
| 266 | if (Settings::values.cpuopt_unsafe_ignore_global_monitor) { | ||
| 267 | config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor; | ||
| 268 | } | ||
| 257 | } | 269 | } |
| 258 | 270 | ||
| 259 | // Curated optimizations | 271 | // Curated optimizations |
| @@ -262,6 +274,7 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable* | |||
| 262 | config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA; | 274 | config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA; |
| 263 | config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN; | 275 | config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN; |
| 264 | config.fastmem_address_space_bits = 64; | 276 | config.fastmem_address_space_bits = 64; |
| 277 | config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor; | ||
| 265 | } | 278 | } |
| 266 | 279 | ||
| 267 | return std::make_shared<Dynarmic::A64::Jit>(config); | 280 | return std::make_shared<Dynarmic::A64::Jit>(config); |
diff --git a/src/core/arm/dynarmic/arm_exclusive_monitor.cpp b/src/core/arm/dynarmic/arm_exclusive_monitor.cpp index 397d054a8..ea6b224e0 100644 --- a/src/core/arm/dynarmic/arm_exclusive_monitor.cpp +++ b/src/core/arm/dynarmic/arm_exclusive_monitor.cpp | |||
| @@ -37,8 +37,8 @@ u128 DynarmicExclusiveMonitor::ExclusiveRead128(std::size_t core_index, VAddr ad | |||
| 37 | }); | 37 | }); |
| 38 | } | 38 | } |
| 39 | 39 | ||
| 40 | void DynarmicExclusiveMonitor::ClearExclusive() { | 40 | void DynarmicExclusiveMonitor::ClearExclusive(std::size_t core_index) { |
| 41 | monitor.Clear(); | 41 | monitor.ClearProcessor(core_index); |
| 42 | } | 42 | } |
| 43 | 43 | ||
| 44 | bool DynarmicExclusiveMonitor::ExclusiveWrite8(std::size_t core_index, VAddr vaddr, u8 value) { | 44 | bool DynarmicExclusiveMonitor::ExclusiveWrite8(std::size_t core_index, VAddr vaddr, u8 value) { |
diff --git a/src/core/arm/dynarmic/arm_exclusive_monitor.h b/src/core/arm/dynarmic/arm_exclusive_monitor.h index 265c4ecef..5a15b43ef 100644 --- a/src/core/arm/dynarmic/arm_exclusive_monitor.h +++ b/src/core/arm/dynarmic/arm_exclusive_monitor.h | |||
| @@ -29,7 +29,7 @@ public: | |||
| 29 | u32 ExclusiveRead32(std::size_t core_index, VAddr addr) override; | 29 | u32 ExclusiveRead32(std::size_t core_index, VAddr addr) override; |
| 30 | u64 ExclusiveRead64(std::size_t core_index, VAddr addr) override; | 30 | u64 ExclusiveRead64(std::size_t core_index, VAddr addr) override; |
| 31 | u128 ExclusiveRead128(std::size_t core_index, VAddr addr) override; | 31 | u128 ExclusiveRead128(std::size_t core_index, VAddr addr) override; |
| 32 | void ClearExclusive() override; | 32 | void ClearExclusive(std::size_t core_index) override; |
| 33 | 33 | ||
| 34 | bool ExclusiveWrite8(std::size_t core_index, VAddr vaddr, u8 value) override; | 34 | bool ExclusiveWrite8(std::size_t core_index, VAddr vaddr, u8 value) override; |
| 35 | bool ExclusiveWrite16(std::size_t core_index, VAddr vaddr, u16 value) override; | 35 | bool ExclusiveWrite16(std::size_t core_index, VAddr vaddr, u16 value) override; |
diff --git a/src/core/arm/exclusive_monitor.h b/src/core/arm/exclusive_monitor.h index 62f6e6023..9914ca3da 100644 --- a/src/core/arm/exclusive_monitor.h +++ b/src/core/arm/exclusive_monitor.h | |||
| @@ -23,7 +23,7 @@ public: | |||
| 23 | virtual u32 ExclusiveRead32(std::size_t core_index, VAddr addr) = 0; | 23 | virtual u32 ExclusiveRead32(std::size_t core_index, VAddr addr) = 0; |
| 24 | virtual u64 ExclusiveRead64(std::size_t core_index, VAddr addr) = 0; | 24 | virtual u64 ExclusiveRead64(std::size_t core_index, VAddr addr) = 0; |
| 25 | virtual u128 ExclusiveRead128(std::size_t core_index, VAddr addr) = 0; | 25 | virtual u128 ExclusiveRead128(std::size_t core_index, VAddr addr) = 0; |
| 26 | virtual void ClearExclusive() = 0; | 26 | virtual void ClearExclusive(std::size_t core_index) = 0; |
| 27 | 27 | ||
| 28 | virtual bool ExclusiveWrite8(std::size_t core_index, VAddr vaddr, u8 value) = 0; | 28 | virtual bool ExclusiveWrite8(std::size_t core_index, VAddr vaddr, u8 value) = 0; |
| 29 | virtual bool ExclusiveWrite16(std::size_t core_index, VAddr vaddr, u16 value) = 0; | 29 | virtual bool ExclusiveWrite16(std::size_t core_index, VAddr vaddr, u16 value) = 0; |
diff --git a/src/core/core.cpp b/src/core/core.cpp index 3f9a7f44b..c60a784c3 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp | |||
| @@ -28,7 +28,9 @@ | |||
| 28 | #include "core/file_sys/vfs_real.h" | 28 | #include "core/file_sys/vfs_real.h" |
| 29 | #include "core/hardware_interrupt_manager.h" | 29 | #include "core/hardware_interrupt_manager.h" |
| 30 | #include "core/hid/hid_core.h" | 30 | #include "core/hid/hid_core.h" |
| 31 | #include "core/hle/kernel/k_memory_manager.h" | ||
| 31 | #include "core/hle/kernel/k_process.h" | 32 | #include "core/hle/kernel/k_process.h" |
| 33 | #include "core/hle/kernel/k_resource_limit.h" | ||
| 32 | #include "core/hle/kernel/k_scheduler.h" | 34 | #include "core/hle/kernel/k_scheduler.h" |
| 33 | #include "core/hle/kernel/kernel.h" | 35 | #include "core/hle/kernel/kernel.h" |
| 34 | #include "core/hle/kernel/physical_core.h" | 36 | #include "core/hle/kernel/physical_core.h" |
| @@ -252,9 +254,16 @@ struct System::Impl { | |||
| 252 | } | 254 | } |
| 253 | 255 | ||
| 254 | telemetry_session->AddInitialInfo(*app_loader, fs_controller, *content_provider); | 256 | telemetry_session->AddInitialInfo(*app_loader, fs_controller, *content_provider); |
| 257 | |||
| 258 | // Create a resource limit for the process. | ||
| 259 | const auto physical_memory_size = | ||
| 260 | kernel.MemoryManager().GetSize(Kernel::KMemoryManager::Pool::Application); | ||
| 261 | auto* resource_limit = Kernel::CreateResourceLimitForProcess(system, physical_memory_size); | ||
| 262 | |||
| 263 | // Create the process. | ||
| 255 | auto main_process = Kernel::KProcess::Create(system.Kernel()); | 264 | auto main_process = Kernel::KProcess::Create(system.Kernel()); |
| 256 | ASSERT(Kernel::KProcess::Initialize(main_process, system, "main", | 265 | ASSERT(Kernel::KProcess::Initialize(main_process, system, "main", |
| 257 | Kernel::KProcess::ProcessType::Userland) | 266 | Kernel::KProcess::ProcessType::Userland, resource_limit) |
| 258 | .IsSuccess()); | 267 | .IsSuccess()); |
| 259 | const auto [load_result, load_parameters] = app_loader->Load(*main_process, system); | 268 | const auto [load_result, load_parameters] = app_loader->Load(*main_process, system); |
| 260 | if (load_result != Loader::ResultStatus::Success) { | 269 | if (load_result != Loader::ResultStatus::Success) { |
| @@ -317,7 +326,9 @@ struct System::Impl { | |||
| 317 | is_powered_on = false; | 326 | is_powered_on = false; |
| 318 | exit_lock = false; | 327 | exit_lock = false; |
| 319 | 328 | ||
| 320 | gpu_core->NotifyShutdown(); | 329 | if (gpu_core != nullptr) { |
| 330 | gpu_core->NotifyShutdown(); | ||
| 331 | } | ||
| 321 | 332 | ||
| 322 | services.reset(); | 333 | services.reset(); |
| 323 | service_manager.reset(); | 334 | service_manager.reset(); |
diff --git a/src/core/device_memory.cpp b/src/core/device_memory.cpp index f19c0515f..e6bc63086 100644 --- a/src/core/device_memory.cpp +++ b/src/core/device_memory.cpp | |||
| @@ -3,10 +3,13 @@ | |||
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "core/device_memory.h" | 5 | #include "core/device_memory.h" |
| 6 | #include "hle/kernel/board/nintendo/nx/k_system_control.h" | ||
| 6 | 7 | ||
| 7 | namespace Core { | 8 | namespace Core { |
| 8 | 9 | ||
| 9 | DeviceMemory::DeviceMemory() : buffer{DramMemoryMap::Size, 1ULL << 39} {} | 10 | DeviceMemory::DeviceMemory() |
| 11 | : buffer{Kernel::Board::Nintendo::Nx::KSystemControl::Init::GetIntendedMemorySize(), | ||
| 12 | 1ULL << 39} {} | ||
| 10 | DeviceMemory::~DeviceMemory() = default; | 13 | DeviceMemory::~DeviceMemory() = default; |
| 11 | 14 | ||
| 12 | } // namespace Core | 15 | } // namespace Core |
diff --git a/src/core/device_memory.h b/src/core/device_memory.h index c4d17705f..daeb551ea 100644 --- a/src/core/device_memory.h +++ b/src/core/device_memory.h | |||
| @@ -12,12 +12,8 @@ namespace Core { | |||
| 12 | namespace DramMemoryMap { | 12 | namespace DramMemoryMap { |
| 13 | enum : u64 { | 13 | enum : u64 { |
| 14 | Base = 0x80000000ULL, | 14 | Base = 0x80000000ULL, |
| 15 | Size = 0x100000000ULL, | ||
| 16 | End = Base + Size, | ||
| 17 | KernelReserveBase = Base + 0x60000, | 15 | KernelReserveBase = Base + 0x60000, |
| 18 | SlabHeapBase = KernelReserveBase + 0x85000, | 16 | SlabHeapBase = KernelReserveBase + 0x85000, |
| 19 | SlapHeapSize = 0xa21000, | ||
| 20 | SlabHeapEnd = SlabHeapBase + SlapHeapSize, | ||
| 21 | }; | 17 | }; |
| 22 | }; // namespace DramMemoryMap | 18 | }; // namespace DramMemoryMap |
| 23 | 19 | ||
diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp index 7c0950bb0..f19ac4607 100644 --- a/src/core/file_sys/patch_manager.cpp +++ b/src/core/file_sys/patch_manager.cpp | |||
| @@ -128,15 +128,6 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const { | |||
| 128 | if (exefs == nullptr) | 128 | if (exefs == nullptr) |
| 129 | return exefs; | 129 | return exefs; |
| 130 | 130 | ||
| 131 | if (Settings::values.dump_exefs) { | ||
| 132 | LOG_INFO(Loader, "Dumping ExeFS for title_id={:016X}", title_id); | ||
| 133 | const auto dump_dir = fs_controller.GetModificationDumpRoot(title_id); | ||
| 134 | if (dump_dir != nullptr) { | ||
| 135 | const auto exefs_dir = GetOrCreateDirectoryRelative(dump_dir, "/exefs"); | ||
| 136 | VfsRawCopyD(exefs, exefs_dir); | ||
| 137 | } | ||
| 138 | } | ||
| 139 | |||
| 140 | const auto& disabled = Settings::values.disabled_addons[title_id]; | 131 | const auto& disabled = Settings::values.disabled_addons[title_id]; |
| 141 | const auto update_disabled = | 132 | const auto update_disabled = |
| 142 | std::find(disabled.cbegin(), disabled.cend(), "Update") != disabled.cend(); | 133 | std::find(disabled.cbegin(), disabled.cend(), "Update") != disabled.cend(); |
| @@ -179,6 +170,15 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const { | |||
| 179 | } | 170 | } |
| 180 | } | 171 | } |
| 181 | 172 | ||
| 173 | if (Settings::values.dump_exefs) { | ||
| 174 | LOG_INFO(Loader, "Dumping ExeFS for title_id={:016X}", title_id); | ||
| 175 | const auto dump_dir = fs_controller.GetModificationDumpRoot(title_id); | ||
| 176 | if (dump_dir != nullptr) { | ||
| 177 | const auto exefs_dir = GetOrCreateDirectoryRelative(dump_dir, "/exefs"); | ||
| 178 | VfsRawCopyD(exefs, exefs_dir); | ||
| 179 | } | ||
| 180 | } | ||
| 181 | |||
| 182 | return exefs; | 182 | return exefs; |
| 183 | } | 183 | } |
| 184 | 184 | ||
diff --git a/src/core/file_sys/vfs.h b/src/core/file_sys/vfs.h index 3e625fad6..1b9365853 100644 --- a/src/core/file_sys/vfs.h +++ b/src/core/file_sys/vfs.h | |||
| @@ -12,6 +12,7 @@ | |||
| 12 | #include <type_traits> | 12 | #include <type_traits> |
| 13 | #include <vector> | 13 | #include <vector> |
| 14 | 14 | ||
| 15 | #include "common/common_funcs.h" | ||
| 15 | #include "common/common_types.h" | 16 | #include "common/common_types.h" |
| 16 | #include "core/file_sys/vfs_types.h" | 17 | #include "core/file_sys/vfs_types.h" |
| 17 | 18 | ||
| @@ -29,8 +30,11 @@ enum class VfsEntryType { | |||
| 29 | // A class representing an abstract filesystem. A default implementation given the root VirtualDir | 30 | // A class representing an abstract filesystem. A default implementation given the root VirtualDir |
| 30 | // is provided for convenience, but if the Vfs implementation has any additional state or | 31 | // is provided for convenience, but if the Vfs implementation has any additional state or |
| 31 | // functionality, they will need to override. | 32 | // functionality, they will need to override. |
| 32 | class VfsFilesystem : NonCopyable { | 33 | class VfsFilesystem { |
| 33 | public: | 34 | public: |
| 35 | YUZU_NON_COPYABLE(VfsFilesystem); | ||
| 36 | YUZU_NON_MOVEABLE(VfsFilesystem); | ||
| 37 | |||
| 34 | explicit VfsFilesystem(VirtualDir root); | 38 | explicit VfsFilesystem(VirtualDir root); |
| 35 | virtual ~VfsFilesystem(); | 39 | virtual ~VfsFilesystem(); |
| 36 | 40 | ||
| @@ -77,8 +81,12 @@ protected: | |||
| 77 | }; | 81 | }; |
| 78 | 82 | ||
| 79 | // A class representing a file in an abstract filesystem. | 83 | // A class representing a file in an abstract filesystem. |
| 80 | class VfsFile : NonCopyable { | 84 | class VfsFile { |
| 81 | public: | 85 | public: |
| 86 | YUZU_NON_COPYABLE(VfsFile); | ||
| 87 | YUZU_NON_MOVEABLE(VfsFile); | ||
| 88 | |||
| 89 | VfsFile() = default; | ||
| 82 | virtual ~VfsFile(); | 90 | virtual ~VfsFile(); |
| 83 | 91 | ||
| 84 | // Retrieves the file name. | 92 | // Retrieves the file name. |
| @@ -176,8 +184,12 @@ public: | |||
| 176 | }; | 184 | }; |
| 177 | 185 | ||
| 178 | // A class representing a directory in an abstract filesystem. | 186 | // A class representing a directory in an abstract filesystem. |
| 179 | class VfsDirectory : NonCopyable { | 187 | class VfsDirectory { |
| 180 | public: | 188 | public: |
| 189 | YUZU_NON_COPYABLE(VfsDirectory); | ||
| 190 | YUZU_NON_MOVEABLE(VfsDirectory); | ||
| 191 | |||
| 192 | VfsDirectory() = default; | ||
| 181 | virtual ~VfsDirectory(); | 193 | virtual ~VfsDirectory(); |
| 182 | 194 | ||
| 183 | // Retrives the file located at path as if the current directory was root. Returns nullptr if | 195 | // Retrives the file located at path as if the current directory was root. Returns nullptr if |
diff --git a/src/core/frontend/applets/mii.cpp b/src/core/frontend/applets/mii.cpp new file mode 100644 index 000000000..1c05ff412 --- /dev/null +++ b/src/core/frontend/applets/mii.cpp | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | // Copyright 2022 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "common/logging/log.h" | ||
| 6 | #include "core/frontend/applets/mii.h" | ||
| 7 | |||
| 8 | namespace Core::Frontend { | ||
| 9 | |||
| 10 | MiiApplet::~MiiApplet() = default; | ||
| 11 | |||
| 12 | void DefaultMiiApplet::ShowMii( | ||
| 13 | const MiiParameters& parameters, | ||
| 14 | const std::function<void(const Core::Frontend::MiiParameters& parameters)> callback) const { | ||
| 15 | LOG_INFO(Service_HID, "(STUBBED) called"); | ||
| 16 | callback(parameters); | ||
| 17 | } | ||
| 18 | |||
| 19 | } // namespace Core::Frontend | ||
diff --git a/src/core/frontend/applets/mii.h b/src/core/frontend/applets/mii.h new file mode 100644 index 000000000..1fc40a9c6 --- /dev/null +++ b/src/core/frontend/applets/mii.h | |||
| @@ -0,0 +1,35 @@ | |||
| 1 | // Copyright 2022 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <functional> | ||
| 8 | |||
| 9 | #include "core/hle/result.h" | ||
| 10 | #include "core/hle/service/mii/mii_manager.h" | ||
| 11 | |||
| 12 | namespace Core::Frontend { | ||
| 13 | |||
| 14 | struct MiiParameters { | ||
| 15 | bool is_editable; | ||
| 16 | Service::Mii::MiiInfo mii_data{}; | ||
| 17 | }; | ||
| 18 | |||
| 19 | class MiiApplet { | ||
| 20 | public: | ||
| 21 | virtual ~MiiApplet(); | ||
| 22 | |||
| 23 | virtual void ShowMii(const MiiParameters& parameters, | ||
| 24 | const std::function<void(const Core::Frontend::MiiParameters& parameters)> | ||
| 25 | callback) const = 0; | ||
| 26 | }; | ||
| 27 | |||
| 28 | class DefaultMiiApplet final : public MiiApplet { | ||
| 29 | public: | ||
| 30 | void ShowMii(const MiiParameters& parameters, | ||
| 31 | const std::function<void(const Core::Frontend::MiiParameters& parameters)> | ||
| 32 | callback) const override; | ||
| 33 | }; | ||
| 34 | |||
| 35 | } // namespace Core::Frontend | ||
diff --git a/src/core/frontend/applets/profile_select.cpp b/src/core/frontend/applets/profile_select.cpp index 3e4f90be2..4c58c310f 100644 --- a/src/core/frontend/applets/profile_select.cpp +++ b/src/core/frontend/applets/profile_select.cpp | |||
| @@ -13,8 +13,7 @@ ProfileSelectApplet::~ProfileSelectApplet() = default; | |||
| 13 | void DefaultProfileSelectApplet::SelectProfile( | 13 | void DefaultProfileSelectApplet::SelectProfile( |
| 14 | std::function<void(std::optional<Common::UUID>)> callback) const { | 14 | std::function<void(std::optional<Common::UUID>)> callback) const { |
| 15 | Service::Account::ProfileManager manager; | 15 | Service::Account::ProfileManager manager; |
| 16 | callback(manager.GetUser(Settings::values.current_user.GetValue()) | 16 | callback(manager.GetUser(Settings::values.current_user.GetValue()).value_or(Common::UUID{})); |
| 17 | .value_or(Common::UUID{Common::INVALID_UUID})); | ||
| 18 | LOG_INFO(Service_ACC, "called, selecting current user instead of prompting..."); | 17 | LOG_INFO(Service_ACC, "called, selecting current user instead of prompting..."); |
| 19 | } | 18 | } |
| 20 | 19 | ||
diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h index e413a520a..b3bffecb2 100644 --- a/src/core/frontend/emu_window.h +++ b/src/core/frontend/emu_window.h | |||
| @@ -42,11 +42,20 @@ public: | |||
| 42 | context.MakeCurrent(); | 42 | context.MakeCurrent(); |
| 43 | } | 43 | } |
| 44 | ~Scoped() { | 44 | ~Scoped() { |
| 45 | context.DoneCurrent(); | 45 | if (active) { |
| 46 | context.DoneCurrent(); | ||
| 47 | } | ||
| 48 | } | ||
| 49 | |||
| 50 | /// In the event that context was destroyed before the Scoped is destroyed, this provides a | ||
| 51 | /// mechanism to prevent calling a destroyed object's method during the deconstructor | ||
| 52 | void Cancel() { | ||
| 53 | active = false; | ||
| 46 | } | 54 | } |
| 47 | 55 | ||
| 48 | private: | 56 | private: |
| 49 | GraphicsContext& context; | 57 | GraphicsContext& context; |
| 58 | bool active{true}; | ||
| 50 | }; | 59 | }; |
| 51 | 60 | ||
| 52 | /// Calls MakeCurrent on the context and calls DoneCurrent when the scope for the returned value | 61 | /// Calls MakeCurrent on the context and calls DoneCurrent when the scope for the returned value |
diff --git a/src/core/hid/emulated_console.h b/src/core/hid/emulated_console.h index 707419102..5eb170823 100644 --- a/src/core/hid/emulated_console.h +++ b/src/core/hid/emulated_console.h | |||
| @@ -10,6 +10,7 @@ | |||
| 10 | #include <mutex> | 10 | #include <mutex> |
| 11 | #include <unordered_map> | 11 | #include <unordered_map> |
| 12 | 12 | ||
| 13 | #include "common/common_funcs.h" | ||
| 13 | #include "common/common_types.h" | 14 | #include "common/common_types.h" |
| 14 | #include "common/input.h" | 15 | #include "common/input.h" |
| 15 | #include "common/param_package.h" | 16 | #include "common/param_package.h" |
diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp index a7cdf45e6..7e05666d6 100644 --- a/src/core/hid/emulated_controller.cpp +++ b/src/core/hid/emulated_controller.cpp | |||
| @@ -269,7 +269,8 @@ void EmulatedController::ReloadInput() { | |||
| 269 | } | 269 | } |
| 270 | 270 | ||
| 271 | // Use a common UUID for TAS | 271 | // Use a common UUID for TAS |
| 272 | const auto tas_uuid = Common::UUID{0x0, 0x7A5}; | 272 | static constexpr Common::UUID TAS_UUID = Common::UUID{ |
| 273 | {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0xA5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}; | ||
| 273 | 274 | ||
| 274 | // Register TAS devices. No need to force update | 275 | // Register TAS devices. No need to force update |
| 275 | for (std::size_t index = 0; index < tas_button_devices.size(); ++index) { | 276 | for (std::size_t index = 0; index < tas_button_devices.size(); ++index) { |
| @@ -278,8 +279,8 @@ void EmulatedController::ReloadInput() { | |||
| 278 | } | 279 | } |
| 279 | tas_button_devices[index]->SetCallback({ | 280 | tas_button_devices[index]->SetCallback({ |
| 280 | .on_change = | 281 | .on_change = |
| 281 | [this, index, tas_uuid](const Common::Input::CallbackStatus& callback) { | 282 | [this, index](const Common::Input::CallbackStatus& callback) { |
| 282 | SetButton(callback, index, tas_uuid); | 283 | SetButton(callback, index, TAS_UUID); |
| 283 | }, | 284 | }, |
| 284 | }); | 285 | }); |
| 285 | } | 286 | } |
| @@ -290,8 +291,8 @@ void EmulatedController::ReloadInput() { | |||
| 290 | } | 291 | } |
| 291 | tas_stick_devices[index]->SetCallback({ | 292 | tas_stick_devices[index]->SetCallback({ |
| 292 | .on_change = | 293 | .on_change = |
| 293 | [this, index, tas_uuid](const Common::Input::CallbackStatus& callback) { | 294 | [this, index](const Common::Input::CallbackStatus& callback) { |
| 294 | SetStick(callback, index, tas_uuid); | 295 | SetStick(callback, index, TAS_UUID); |
| 295 | }, | 296 | }, |
| 296 | }); | 297 | }); |
| 297 | } | 298 | } |
| @@ -884,6 +885,12 @@ bool EmulatedController::TestVibration(std::size_t device_index) { | |||
| 884 | return SetVibration(device_index, DEFAULT_VIBRATION_VALUE); | 885 | return SetVibration(device_index, DEFAULT_VIBRATION_VALUE); |
| 885 | } | 886 | } |
| 886 | 887 | ||
| 888 | bool EmulatedController::SetPollingMode(Common::Input::PollingMode polling_mode) { | ||
| 889 | LOG_INFO(Service_HID, "Set polling mode {}", polling_mode); | ||
| 890 | auto& output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; | ||
| 891 | return output_device->SetPollingMode(polling_mode) == Common::Input::PollingError::None; | ||
| 892 | } | ||
| 893 | |||
| 887 | void EmulatedController::SetLedPattern() { | 894 | void EmulatedController::SetLedPattern() { |
| 888 | for (auto& device : output_devices) { | 895 | for (auto& device : output_devices) { |
| 889 | if (!device) { | 896 | if (!device) { |
diff --git a/src/core/hid/emulated_controller.h b/src/core/hid/emulated_controller.h index a63a83cce..aa52f9572 100644 --- a/src/core/hid/emulated_controller.h +++ b/src/core/hid/emulated_controller.h | |||
| @@ -13,8 +13,6 @@ | |||
| 13 | #include "common/common_types.h" | 13 | #include "common/common_types.h" |
| 14 | #include "common/input.h" | 14 | #include "common/input.h" |
| 15 | #include "common/param_package.h" | 15 | #include "common/param_package.h" |
| 16 | #include "common/point.h" | ||
| 17 | #include "common/quaternion.h" | ||
| 18 | #include "common/settings.h" | 16 | #include "common/settings.h" |
| 19 | #include "common/vector_math.h" | 17 | #include "common/vector_math.h" |
| 20 | #include "core/hid/hid_types.h" | 18 | #include "core/hid/hid_types.h" |
| @@ -301,16 +299,23 @@ public: | |||
| 301 | 299 | ||
| 302 | /** | 300 | /** |
| 303 | * Sends a specific vibration to the output device | 301 | * Sends a specific vibration to the output device |
| 304 | * @return returns true if vibration had no errors | 302 | * @return true if vibration had no errors |
| 305 | */ | 303 | */ |
| 306 | bool SetVibration(std::size_t device_index, VibrationValue vibration); | 304 | bool SetVibration(std::size_t device_index, VibrationValue vibration); |
| 307 | 305 | ||
| 308 | /** | 306 | /** |
| 309 | * Sends a small vibration to the output device | 307 | * Sends a small vibration to the output device |
| 310 | * @return returns true if SetVibration was successfull | 308 | * @return true if SetVibration was successfull |
| 311 | */ | 309 | */ |
| 312 | bool TestVibration(std::size_t device_index); | 310 | bool TestVibration(std::size_t device_index); |
| 313 | 311 | ||
| 312 | /** | ||
| 313 | * Sets the desired data to be polled from a controller | ||
| 314 | * @param polling_mode type of input desired buttons, gyro, nfc, ir, etc. | ||
| 315 | * @return true if SetPollingMode was successfull | ||
| 316 | */ | ||
| 317 | bool SetPollingMode(Common::Input::PollingMode polling_mode); | ||
| 318 | |||
| 314 | /// Returns the led pattern corresponding to this emulated controller | 319 | /// Returns the led pattern corresponding to this emulated controller |
| 315 | LedPattern GetLedPattern() const; | 320 | LedPattern GetLedPattern() const; |
| 316 | 321 | ||
diff --git a/src/core/hid/hid_core.h b/src/core/hid/hid_core.h index 837f7de49..717f605e7 100644 --- a/src/core/hid/hid_core.h +++ b/src/core/hid/hid_core.h | |||
| @@ -6,6 +6,7 @@ | |||
| 6 | 6 | ||
| 7 | #include <memory> | 7 | #include <memory> |
| 8 | 8 | ||
| 9 | #include "common/common_funcs.h" | ||
| 9 | #include "core/hid/hid_types.h" | 10 | #include "core/hid/hid_types.h" |
| 10 | 11 | ||
| 11 | namespace Core::HID { | 12 | namespace Core::HID { |
diff --git a/src/core/hid/input_converter.cpp b/src/core/hid/input_converter.cpp index 860aab400..cd41607a7 100644 --- a/src/core/hid/input_converter.cpp +++ b/src/core/hid/input_converter.cpp | |||
| @@ -28,7 +28,7 @@ Common::Input::BatteryStatus TransformToBattery(const Common::Input::CallbackSta | |||
| 28 | if (value > 0.8f) { | 28 | if (value > 0.8f) { |
| 29 | battery = Common::Input::BatteryLevel::Full; | 29 | battery = Common::Input::BatteryLevel::Full; |
| 30 | } | 30 | } |
| 31 | if (value >= 1.0f) { | 31 | if (value >= 0.95f) { |
| 32 | battery = Common::Input::BatteryLevel::Charging; | 32 | battery = Common::Input::BatteryLevel::Charging; |
| 33 | } | 33 | } |
| 34 | break; | 34 | break; |
diff --git a/src/core/hle/ipc_helpers.h b/src/core/hle/ipc_helpers.h index cf204f570..3c4e45fcd 100644 --- a/src/core/hle/ipc_helpers.h +++ b/src/core/hle/ipc_helpers.h | |||
| @@ -385,7 +385,7 @@ public: | |||
| 385 | T PopRaw(); | 385 | T PopRaw(); |
| 386 | 386 | ||
| 387 | template <class T> | 387 | template <class T> |
| 388 | std::shared_ptr<T> PopIpcInterface() { | 388 | std::weak_ptr<T> PopIpcInterface() { |
| 389 | ASSERT(context->Session()->IsDomain()); | 389 | ASSERT(context->Session()->IsDomain()); |
| 390 | ASSERT(context->GetDomainMessageHeader().input_object_count > 0); | 390 | ASSERT(context->GetDomainMessageHeader().input_object_count > 0); |
| 391 | return context->GetDomainHandler<T>(Pop<u32>() - 1); | 391 | return context->GetDomainHandler<T>(Pop<u32>() - 1); |
| @@ -404,6 +404,11 @@ inline s32 RequestParser::Pop() { | |||
| 404 | return static_cast<s32>(Pop<u32>()); | 404 | return static_cast<s32>(Pop<u32>()); |
| 405 | } | 405 | } |
| 406 | 406 | ||
| 407 | // Ignore the -Wclass-memaccess warning on memcpy for non-trivially default constructible objects. | ||
| 408 | #if defined(__GNUC__) | ||
| 409 | #pragma GCC diagnostic push | ||
| 410 | #pragma GCC diagnostic ignored "-Wclass-memaccess" | ||
| 411 | #endif | ||
| 407 | template <typename T> | 412 | template <typename T> |
| 408 | void RequestParser::PopRaw(T& value) { | 413 | void RequestParser::PopRaw(T& value) { |
| 409 | static_assert(std::is_trivially_copyable_v<T>, | 414 | static_assert(std::is_trivially_copyable_v<T>, |
| @@ -411,6 +416,9 @@ void RequestParser::PopRaw(T& value) { | |||
| 411 | std::memcpy(&value, cmdbuf + index, sizeof(T)); | 416 | std::memcpy(&value, cmdbuf + index, sizeof(T)); |
| 412 | index += (sizeof(T) + 3) / 4; // round up to word length | 417 | index += (sizeof(T) + 3) / 4; // round up to word length |
| 413 | } | 418 | } |
| 419 | #if defined(__GNUC__) | ||
| 420 | #pragma GCC diagnostic pop | ||
| 421 | #endif | ||
| 414 | 422 | ||
| 415 | template <typename T> | 423 | template <typename T> |
| 416 | T RequestParser::PopRaw() { | 424 | T RequestParser::PopRaw() { |
diff --git a/src/core/hle/kernel/board/nintendo/nx/k_memory_layout.h b/src/core/hle/kernel/board/nintendo/nx/k_memory_layout.h new file mode 100644 index 000000000..01e225088 --- /dev/null +++ b/src/core/hle/kernel/board/nintendo/nx/k_memory_layout.h | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | // Copyright 2022 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "common/common_types.h" | ||
| 8 | |||
| 9 | namespace Kernel { | ||
| 10 | |||
| 11 | constexpr inline PAddr MainMemoryAddress = 0x80000000; | ||
| 12 | |||
| 13 | } // namespace Kernel | ||
diff --git a/src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp b/src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp index 6f335c251..8027bec00 100644 --- a/src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp +++ b/src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | #include <random> | 5 | #include <random> |
| 6 | 6 | ||
| 7 | #include "common/literals.h" | 7 | #include "common/literals.h" |
| 8 | #include "common/settings.h" | ||
| 8 | 9 | ||
| 9 | #include "core/hle/kernel/board/nintendo/nx/k_system_control.h" | 10 | #include "core/hle/kernel/board/nintendo/nx/k_system_control.h" |
| 10 | #include "core/hle/kernel/board/nintendo/nx/secure_monitor.h" | 11 | #include "core/hle/kernel/board/nintendo/nx/secure_monitor.h" |
| @@ -28,33 +29,20 @@ namespace { | |||
| 28 | 29 | ||
| 29 | using namespace Common::Literals; | 30 | using namespace Common::Literals; |
| 30 | 31 | ||
| 31 | u32 GetMemoryModeForInit() { | ||
| 32 | return 0x01; | ||
| 33 | } | ||
| 34 | |||
| 35 | u32 GetMemorySizeForInit() { | 32 | u32 GetMemorySizeForInit() { |
| 36 | return 0; | 33 | return Settings::values.use_extended_memory_layout ? Smc::MemorySize_6GB : Smc::MemorySize_4GB; |
| 37 | } | 34 | } |
| 38 | 35 | ||
| 39 | Smc::MemoryArrangement GetMemoryArrangeForInit() { | 36 | Smc::MemoryArrangement GetMemoryArrangeForInit() { |
| 40 | switch (GetMemoryModeForInit() & 0x3F) { | 37 | return Settings::values.use_extended_memory_layout ? Smc::MemoryArrangement_6GB |
| 41 | case 0x01: | 38 | : Smc::MemoryArrangement_4GB; |
| 42 | default: | ||
| 43 | return Smc::MemoryArrangement_4GB; | ||
| 44 | case 0x02: | ||
| 45 | return Smc::MemoryArrangement_4GBForAppletDev; | ||
| 46 | case 0x03: | ||
| 47 | return Smc::MemoryArrangement_4GBForSystemDev; | ||
| 48 | case 0x11: | ||
| 49 | return Smc::MemoryArrangement_6GB; | ||
| 50 | case 0x12: | ||
| 51 | return Smc::MemoryArrangement_6GBForAppletDev; | ||
| 52 | case 0x21: | ||
| 53 | return Smc::MemoryArrangement_8GB; | ||
| 54 | } | ||
| 55 | } | 39 | } |
| 56 | } // namespace | 40 | } // namespace |
| 57 | 41 | ||
| 42 | size_t KSystemControl::Init::GetRealMemorySize() { | ||
| 43 | return GetIntendedMemorySize(); | ||
| 44 | } | ||
| 45 | |||
| 58 | // Initialization. | 46 | // Initialization. |
| 59 | size_t KSystemControl::Init::GetIntendedMemorySize() { | 47 | size_t KSystemControl::Init::GetIntendedMemorySize() { |
| 60 | switch (GetMemorySizeForInit()) { | 48 | switch (GetMemorySizeForInit()) { |
| @@ -69,7 +57,13 @@ size_t KSystemControl::Init::GetIntendedMemorySize() { | |||
| 69 | } | 57 | } |
| 70 | 58 | ||
| 71 | PAddr KSystemControl::Init::GetKernelPhysicalBaseAddress(u64 base_address) { | 59 | PAddr KSystemControl::Init::GetKernelPhysicalBaseAddress(u64 base_address) { |
| 72 | return base_address; | 60 | const size_t real_dram_size = KSystemControl::Init::GetRealMemorySize(); |
| 61 | const size_t intended_dram_size = KSystemControl::Init::GetIntendedMemorySize(); | ||
| 62 | if (intended_dram_size * 2 < real_dram_size) { | ||
| 63 | return base_address; | ||
| 64 | } else { | ||
| 65 | return base_address + ((real_dram_size - intended_dram_size) / 2); | ||
| 66 | } | ||
| 73 | } | 67 | } |
| 74 | 68 | ||
| 75 | bool KSystemControl::Init::ShouldIncreaseThreadResourceLimit() { | 69 | bool KSystemControl::Init::ShouldIncreaseThreadResourceLimit() { |
diff --git a/src/core/hle/kernel/board/nintendo/nx/k_system_control.h b/src/core/hle/kernel/board/nintendo/nx/k_system_control.h index 52f230ced..df2a17f2a 100644 --- a/src/core/hle/kernel/board/nintendo/nx/k_system_control.h +++ b/src/core/hle/kernel/board/nintendo/nx/k_system_control.h | |||
| @@ -13,6 +13,7 @@ public: | |||
| 13 | class Init { | 13 | class Init { |
| 14 | public: | 14 | public: |
| 15 | // Initialization. | 15 | // Initialization. |
| 16 | static std::size_t GetRealMemorySize(); | ||
| 16 | static std::size_t GetIntendedMemorySize(); | 17 | static std::size_t GetIntendedMemorySize(); |
| 17 | static PAddr GetKernelPhysicalBaseAddress(u64 base_address); | 18 | static PAddr GetKernelPhysicalBaseAddress(u64 base_address); |
| 18 | static bool ShouldIncreaseThreadResourceLimit(); | 19 | static bool ShouldIncreaseThreadResourceLimit(); |
diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp index e19544c54..9f2175f82 100644 --- a/src/core/hle/kernel/hle_ipc.cpp +++ b/src/core/hle/kernel/hle_ipc.cpp | |||
| @@ -45,7 +45,7 @@ bool SessionRequestManager::HasSessionRequestHandler(const HLERequestContext& co | |||
| 45 | LOG_CRITICAL(IPC, "object_id {} is too big!", object_id); | 45 | LOG_CRITICAL(IPC, "object_id {} is too big!", object_id); |
| 46 | return false; | 46 | return false; |
| 47 | } | 47 | } |
| 48 | return DomainHandler(object_id - 1) != nullptr; | 48 | return DomainHandler(object_id - 1).lock() != nullptr; |
| 49 | } else { | 49 | } else { |
| 50 | return session_handler != nullptr; | 50 | return session_handler != nullptr; |
| 51 | } | 51 | } |
| @@ -53,9 +53,6 @@ bool SessionRequestManager::HasSessionRequestHandler(const HLERequestContext& co | |||
| 53 | 53 | ||
| 54 | void SessionRequestHandler::ClientConnected(KServerSession* session) { | 54 | void SessionRequestHandler::ClientConnected(KServerSession* session) { |
| 55 | session->ClientConnected(shared_from_this()); | 55 | session->ClientConnected(shared_from_this()); |
| 56 | |||
| 57 | // Ensure our server session is tracked globally. | ||
| 58 | kernel.RegisterServerSession(session); | ||
| 59 | } | 56 | } |
| 60 | 57 | ||
| 61 | void SessionRequestHandler::ClientDisconnected(KServerSession* session) { | 58 | void SessionRequestHandler::ClientDisconnected(KServerSession* session) { |
diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h index 754b41ff6..670cc741c 100644 --- a/src/core/hle/kernel/hle_ipc.h +++ b/src/core/hle/kernel/hle_ipc.h | |||
| @@ -94,6 +94,7 @@ protected: | |||
| 94 | std::weak_ptr<ServiceThread> service_thread; | 94 | std::weak_ptr<ServiceThread> service_thread; |
| 95 | }; | 95 | }; |
| 96 | 96 | ||
| 97 | using SessionRequestHandlerWeakPtr = std::weak_ptr<SessionRequestHandler>; | ||
| 97 | using SessionRequestHandlerPtr = std::shared_ptr<SessionRequestHandler>; | 98 | using SessionRequestHandlerPtr = std::shared_ptr<SessionRequestHandler>; |
| 98 | 99 | ||
| 99 | /** | 100 | /** |
| @@ -139,7 +140,7 @@ public: | |||
| 139 | } | 140 | } |
| 140 | } | 141 | } |
| 141 | 142 | ||
| 142 | SessionRequestHandlerPtr DomainHandler(std::size_t index) const { | 143 | SessionRequestHandlerWeakPtr DomainHandler(std::size_t index) const { |
| 143 | ASSERT_MSG(index < DomainHandlerCount(), "Unexpected handler index {}", index); | 144 | ASSERT_MSG(index < DomainHandlerCount(), "Unexpected handler index {}", index); |
| 144 | return domain_handlers.at(index); | 145 | return domain_handlers.at(index); |
| 145 | } | 146 | } |
| @@ -328,10 +329,10 @@ public: | |||
| 328 | 329 | ||
| 329 | template <typename T> | 330 | template <typename T> |
| 330 | std::shared_ptr<T> GetDomainHandler(std::size_t index) const { | 331 | std::shared_ptr<T> GetDomainHandler(std::size_t index) const { |
| 331 | return std::static_pointer_cast<T>(manager->DomainHandler(index)); | 332 | return std::static_pointer_cast<T>(manager.lock()->DomainHandler(index).lock()); |
| 332 | } | 333 | } |
| 333 | 334 | ||
| 334 | void SetSessionRequestManager(std::shared_ptr<SessionRequestManager> manager_) { | 335 | void SetSessionRequestManager(std::weak_ptr<SessionRequestManager> manager_) { |
| 335 | manager = std::move(manager_); | 336 | manager = std::move(manager_); |
| 336 | } | 337 | } |
| 337 | 338 | ||
| @@ -374,7 +375,7 @@ private: | |||
| 374 | u32 handles_offset{}; | 375 | u32 handles_offset{}; |
| 375 | u32 domain_offset{}; | 376 | u32 domain_offset{}; |
| 376 | 377 | ||
| 377 | std::shared_ptr<SessionRequestManager> manager; | 378 | std::weak_ptr<SessionRequestManager> manager; |
| 378 | 379 | ||
| 379 | KernelCore& kernel; | 380 | KernelCore& kernel; |
| 380 | Core::Memory::Memory& memory; | 381 | Core::Memory::Memory& memory; |
diff --git a/src/core/hle/kernel/init/init_slab_setup.cpp b/src/core/hle/kernel/init/init_slab_setup.cpp index 36fc0944a..b0f773ee0 100644 --- a/src/core/hle/kernel/init/init_slab_setup.cpp +++ b/src/core/hle/kernel/init/init_slab_setup.cpp | |||
| @@ -7,19 +7,23 @@ | |||
| 7 | #include "common/common_funcs.h" | 7 | #include "common/common_funcs.h" |
| 8 | #include "common/common_types.h" | 8 | #include "common/common_types.h" |
| 9 | #include "core/core.h" | 9 | #include "core/core.h" |
| 10 | #include "core/device_memory.h" | ||
| 10 | #include "core/hardware_properties.h" | 11 | #include "core/hardware_properties.h" |
| 11 | #include "core/hle/kernel/init/init_slab_setup.h" | 12 | #include "core/hle/kernel/init/init_slab_setup.h" |
| 12 | #include "core/hle/kernel/k_code_memory.h" | 13 | #include "core/hle/kernel/k_code_memory.h" |
| 13 | #include "core/hle/kernel/k_event.h" | 14 | #include "core/hle/kernel/k_event.h" |
| 14 | #include "core/hle/kernel/k_memory_layout.h" | 15 | #include "core/hle/kernel/k_memory_layout.h" |
| 15 | #include "core/hle/kernel/k_memory_manager.h" | 16 | #include "core/hle/kernel/k_memory_manager.h" |
| 17 | #include "core/hle/kernel/k_page_buffer.h" | ||
| 16 | #include "core/hle/kernel/k_port.h" | 18 | #include "core/hle/kernel/k_port.h" |
| 17 | #include "core/hle/kernel/k_process.h" | 19 | #include "core/hle/kernel/k_process.h" |
| 18 | #include "core/hle/kernel/k_resource_limit.h" | 20 | #include "core/hle/kernel/k_resource_limit.h" |
| 19 | #include "core/hle/kernel/k_session.h" | 21 | #include "core/hle/kernel/k_session.h" |
| 20 | #include "core/hle/kernel/k_shared_memory.h" | 22 | #include "core/hle/kernel/k_shared_memory.h" |
| 23 | #include "core/hle/kernel/k_shared_memory_info.h" | ||
| 21 | #include "core/hle/kernel/k_system_control.h" | 24 | #include "core/hle/kernel/k_system_control.h" |
| 22 | #include "core/hle/kernel/k_thread.h" | 25 | #include "core/hle/kernel/k_thread.h" |
| 26 | #include "core/hle/kernel/k_thread_local_page.h" | ||
| 23 | #include "core/hle/kernel/k_transfer_memory.h" | 27 | #include "core/hle/kernel/k_transfer_memory.h" |
| 24 | 28 | ||
| 25 | namespace Kernel::Init { | 29 | namespace Kernel::Init { |
| @@ -32,9 +36,13 @@ namespace Kernel::Init { | |||
| 32 | HANDLER(KEvent, (SLAB_COUNT(KEvent)), ##__VA_ARGS__) \ | 36 | HANDLER(KEvent, (SLAB_COUNT(KEvent)), ##__VA_ARGS__) \ |
| 33 | HANDLER(KPort, (SLAB_COUNT(KPort)), ##__VA_ARGS__) \ | 37 | HANDLER(KPort, (SLAB_COUNT(KPort)), ##__VA_ARGS__) \ |
| 34 | HANDLER(KSharedMemory, (SLAB_COUNT(KSharedMemory)), ##__VA_ARGS__) \ | 38 | HANDLER(KSharedMemory, (SLAB_COUNT(KSharedMemory)), ##__VA_ARGS__) \ |
| 39 | HANDLER(KSharedMemoryInfo, (SLAB_COUNT(KSharedMemory) * 8), ##__VA_ARGS__) \ | ||
| 35 | HANDLER(KTransferMemory, (SLAB_COUNT(KTransferMemory)), ##__VA_ARGS__) \ | 40 | HANDLER(KTransferMemory, (SLAB_COUNT(KTransferMemory)), ##__VA_ARGS__) \ |
| 36 | HANDLER(KCodeMemory, (SLAB_COUNT(KCodeMemory)), ##__VA_ARGS__) \ | 41 | HANDLER(KCodeMemory, (SLAB_COUNT(KCodeMemory)), ##__VA_ARGS__) \ |
| 37 | HANDLER(KSession, (SLAB_COUNT(KSession)), ##__VA_ARGS__) \ | 42 | HANDLER(KSession, (SLAB_COUNT(KSession)), ##__VA_ARGS__) \ |
| 43 | HANDLER(KThreadLocalPage, \ | ||
| 44 | (SLAB_COUNT(KProcess) + (SLAB_COUNT(KProcess) + SLAB_COUNT(KThread)) / 8), \ | ||
| 45 | ##__VA_ARGS__) \ | ||
| 38 | HANDLER(KResourceLimit, (SLAB_COUNT(KResourceLimit)), ##__VA_ARGS__) | 46 | HANDLER(KResourceLimit, (SLAB_COUNT(KResourceLimit)), ##__VA_ARGS__) |
| 39 | 47 | ||
| 40 | namespace { | 48 | namespace { |
| @@ -50,38 +58,46 @@ enum KSlabType : u32 { | |||
| 50 | // Constexpr counts. | 58 | // Constexpr counts. |
| 51 | constexpr size_t SlabCountKProcess = 80; | 59 | constexpr size_t SlabCountKProcess = 80; |
| 52 | constexpr size_t SlabCountKThread = 800; | 60 | constexpr size_t SlabCountKThread = 800; |
| 53 | constexpr size_t SlabCountKEvent = 700; | 61 | constexpr size_t SlabCountKEvent = 900; |
| 54 | constexpr size_t SlabCountKInterruptEvent = 100; | 62 | constexpr size_t SlabCountKInterruptEvent = 100; |
| 55 | constexpr size_t SlabCountKPort = 256 + 0x20; // Extra 0x20 ports over Nintendo for homebrew. | 63 | constexpr size_t SlabCountKPort = 384; |
| 56 | constexpr size_t SlabCountKSharedMemory = 80; | 64 | constexpr size_t SlabCountKSharedMemory = 80; |
| 57 | constexpr size_t SlabCountKTransferMemory = 200; | 65 | constexpr size_t SlabCountKTransferMemory = 200; |
| 58 | constexpr size_t SlabCountKCodeMemory = 10; | 66 | constexpr size_t SlabCountKCodeMemory = 10; |
| 59 | constexpr size_t SlabCountKDeviceAddressSpace = 300; | 67 | constexpr size_t SlabCountKDeviceAddressSpace = 300; |
| 60 | constexpr size_t SlabCountKSession = 933; | 68 | constexpr size_t SlabCountKSession = 1133; |
| 61 | constexpr size_t SlabCountKLightSession = 100; | 69 | constexpr size_t SlabCountKLightSession = 100; |
| 62 | constexpr size_t SlabCountKObjectName = 7; | 70 | constexpr size_t SlabCountKObjectName = 7; |
| 63 | constexpr size_t SlabCountKResourceLimit = 5; | 71 | constexpr size_t SlabCountKResourceLimit = 5; |
| 64 | constexpr size_t SlabCountKDebug = Core::Hardware::NUM_CPU_CORES; | 72 | constexpr size_t SlabCountKDebug = Core::Hardware::NUM_CPU_CORES; |
| 65 | constexpr size_t SlabCountKAlpha = 1; | 73 | constexpr size_t SlabCountKIoPool = 1; |
| 66 | constexpr size_t SlabCountKBeta = 6; | 74 | constexpr size_t SlabCountKIoRegion = 6; |
| 67 | 75 | ||
| 68 | constexpr size_t SlabCountExtraKThread = 160; | 76 | constexpr size_t SlabCountExtraKThread = 160; |
| 69 | 77 | ||
| 78 | /// Helper function to translate from the slab virtual address to the reserved location in physical | ||
| 79 | /// memory. | ||
| 80 | static PAddr TranslateSlabAddrToPhysical(KMemoryLayout& memory_layout, VAddr slab_addr) { | ||
| 81 | slab_addr -= memory_layout.GetSlabRegionAddress(); | ||
| 82 | return slab_addr + Core::DramMemoryMap::SlabHeapBase; | ||
| 83 | } | ||
| 84 | |||
| 70 | template <typename T> | 85 | template <typename T> |
| 71 | VAddr InitializeSlabHeap(Core::System& system, KMemoryLayout& memory_layout, VAddr address, | 86 | VAddr InitializeSlabHeap(Core::System& system, KMemoryLayout& memory_layout, VAddr address, |
| 72 | size_t num_objects) { | 87 | size_t num_objects) { |
| 73 | // TODO(bunnei): This is just a place holder. We should initialize the appropriate KSlabHeap for | ||
| 74 | // kernel object type T with the backing kernel memory pointer once we emulate kernel memory. | ||
| 75 | 88 | ||
| 76 | const size_t size = Common::AlignUp(sizeof(T) * num_objects, alignof(void*)); | 89 | const size_t size = Common::AlignUp(sizeof(T) * num_objects, alignof(void*)); |
| 77 | VAddr start = Common::AlignUp(address, alignof(T)); | 90 | VAddr start = Common::AlignUp(address, alignof(T)); |
| 78 | 91 | ||
| 79 | // This is intentionally empty. Once KSlabHeap is fully implemented, we can replace this with | 92 | // This should use the virtual memory address passed in, but currently, we do not setup the |
| 80 | // the pointer to emulated memory to pass along. Until then, KSlabHeap will just allocate/free | 93 | // kernel virtual memory layout. Instead, we simply map these at a region of physical memory |
| 81 | // host memory. | 94 | // that we reserve for the slab heaps. |
| 82 | void* backing_kernel_memory{}; | 95 | // TODO(bunnei): Fix this once we support the kernel virtual memory layout. |
| 83 | 96 | ||
| 84 | if (size > 0) { | 97 | if (size > 0) { |
| 98 | void* backing_kernel_memory{ | ||
| 99 | system.DeviceMemory().GetPointer(TranslateSlabAddrToPhysical(memory_layout, start))}; | ||
| 100 | |||
| 85 | const KMemoryRegion* region = memory_layout.FindVirtual(start + size - 1); | 101 | const KMemoryRegion* region = memory_layout.FindVirtual(start + size - 1); |
| 86 | ASSERT(region != nullptr); | 102 | ASSERT(region != nullptr); |
| 87 | ASSERT(region->IsDerivedFrom(KMemoryRegionType_KernelSlab)); | 103 | ASSERT(region->IsDerivedFrom(KMemoryRegionType_KernelSlab)); |
| @@ -91,6 +107,12 @@ VAddr InitializeSlabHeap(Core::System& system, KMemoryLayout& memory_layout, VAd | |||
| 91 | return start + size; | 107 | return start + size; |
| 92 | } | 108 | } |
| 93 | 109 | ||
| 110 | size_t CalculateSlabHeapGapSize() { | ||
| 111 | constexpr size_t KernelSlabHeapGapSize = 2_MiB - 296_KiB; | ||
| 112 | static_assert(KernelSlabHeapGapSize <= KernelSlabHeapGapsSizeMax); | ||
| 113 | return KernelSlabHeapGapSize; | ||
| 114 | } | ||
| 115 | |||
| 94 | } // namespace | 116 | } // namespace |
| 95 | 117 | ||
| 96 | KSlabResourceCounts KSlabResourceCounts::CreateDefault() { | 118 | KSlabResourceCounts KSlabResourceCounts::CreateDefault() { |
| @@ -109,8 +131,8 @@ KSlabResourceCounts KSlabResourceCounts::CreateDefault() { | |||
| 109 | .num_KObjectName = SlabCountKObjectName, | 131 | .num_KObjectName = SlabCountKObjectName, |
| 110 | .num_KResourceLimit = SlabCountKResourceLimit, | 132 | .num_KResourceLimit = SlabCountKResourceLimit, |
| 111 | .num_KDebug = SlabCountKDebug, | 133 | .num_KDebug = SlabCountKDebug, |
| 112 | .num_KAlpha = SlabCountKAlpha, | 134 | .num_KIoPool = SlabCountKIoPool, |
| 113 | .num_KBeta = SlabCountKBeta, | 135 | .num_KIoRegion = SlabCountKIoRegion, |
| 114 | }; | 136 | }; |
| 115 | } | 137 | } |
| 116 | 138 | ||
| @@ -136,11 +158,34 @@ size_t CalculateTotalSlabHeapSize(const KernelCore& kernel) { | |||
| 136 | #undef ADD_SLAB_SIZE | 158 | #undef ADD_SLAB_SIZE |
| 137 | 159 | ||
| 138 | // Add the reserved size. | 160 | // Add the reserved size. |
| 139 | size += KernelSlabHeapGapsSize; | 161 | size += CalculateSlabHeapGapSize(); |
| 140 | 162 | ||
| 141 | return size; | 163 | return size; |
| 142 | } | 164 | } |
| 143 | 165 | ||
| 166 | void InitializeKPageBufferSlabHeap(Core::System& system) { | ||
| 167 | auto& kernel = system.Kernel(); | ||
| 168 | |||
| 169 | const auto& counts = kernel.SlabResourceCounts(); | ||
| 170 | const size_t num_pages = | ||
| 171 | counts.num_KProcess + counts.num_KThread + (counts.num_KProcess + counts.num_KThread) / 8; | ||
| 172 | const size_t slab_size = num_pages * PageSize; | ||
| 173 | |||
| 174 | // Reserve memory from the system resource limit. | ||
| 175 | ASSERT(kernel.GetSystemResourceLimit()->Reserve(LimitableResource::PhysicalMemory, slab_size)); | ||
| 176 | |||
| 177 | // Allocate memory for the slab. | ||
| 178 | constexpr auto AllocateOption = KMemoryManager::EncodeOption( | ||
| 179 | KMemoryManager::Pool::System, KMemoryManager::Direction::FromFront); | ||
| 180 | const PAddr slab_address = | ||
| 181 | kernel.MemoryManager().AllocateAndOpenContinuous(num_pages, 1, AllocateOption); | ||
| 182 | ASSERT(slab_address != 0); | ||
| 183 | |||
| 184 | // Initialize the slabheap. | ||
| 185 | KPageBuffer::InitializeSlabHeap(kernel, system.DeviceMemory().GetPointer(slab_address), | ||
| 186 | slab_size); | ||
| 187 | } | ||
| 188 | |||
| 144 | void InitializeSlabHeaps(Core::System& system, KMemoryLayout& memory_layout) { | 189 | void InitializeSlabHeaps(Core::System& system, KMemoryLayout& memory_layout) { |
| 145 | auto& kernel = system.Kernel(); | 190 | auto& kernel = system.Kernel(); |
| 146 | 191 | ||
| @@ -160,13 +205,13 @@ void InitializeSlabHeaps(Core::System& system, KMemoryLayout& memory_layout) { | |||
| 160 | } | 205 | } |
| 161 | 206 | ||
| 162 | // Create an array to represent the gaps between the slabs. | 207 | // Create an array to represent the gaps between the slabs. |
| 163 | const size_t total_gap_size = KernelSlabHeapGapsSize; | 208 | const size_t total_gap_size = CalculateSlabHeapGapSize(); |
| 164 | std::array<size_t, slab_types.size()> slab_gaps; | 209 | std::array<size_t, slab_types.size()> slab_gaps; |
| 165 | for (size_t i = 0; i < slab_gaps.size(); i++) { | 210 | for (auto& slab_gap : slab_gaps) { |
| 166 | // Note: This is an off-by-one error from Nintendo's intention, because GenerateRandomRange | 211 | // Note: This is an off-by-one error from Nintendo's intention, because GenerateRandomRange |
| 167 | // is inclusive. However, Nintendo also has the off-by-one error, and it's "harmless", so we | 212 | // is inclusive. However, Nintendo also has the off-by-one error, and it's "harmless", so we |
| 168 | // will include it ourselves. | 213 | // will include it ourselves. |
| 169 | slab_gaps[i] = KSystemControl::GenerateRandomRange(0, total_gap_size); | 214 | slab_gap = KSystemControl::GenerateRandomRange(0, total_gap_size); |
| 170 | } | 215 | } |
| 171 | 216 | ||
| 172 | // Sort the array, so that we can treat differences between values as offsets to the starts of | 217 | // Sort the array, so that we can treat differences between values as offsets to the starts of |
| @@ -177,13 +222,21 @@ void InitializeSlabHeaps(Core::System& system, KMemoryLayout& memory_layout) { | |||
| 177 | } | 222 | } |
| 178 | } | 223 | } |
| 179 | 224 | ||
| 180 | for (size_t i = 0; i < slab_types.size(); i++) { | 225 | // Track the gaps, so that we can free them to the unused slab tree. |
| 226 | VAddr gap_start = address; | ||
| 227 | size_t gap_size = 0; | ||
| 228 | |||
| 229 | for (size_t i = 0; i < slab_gaps.size(); i++) { | ||
| 181 | // Add the random gap to the address. | 230 | // Add the random gap to the address. |
| 182 | address += (i == 0) ? slab_gaps[0] : slab_gaps[i] - slab_gaps[i - 1]; | 231 | const auto cur_gap = (i == 0) ? slab_gaps[0] : slab_gaps[i] - slab_gaps[i - 1]; |
| 232 | address += cur_gap; | ||
| 233 | gap_size += cur_gap; | ||
| 183 | 234 | ||
| 184 | #define INITIALIZE_SLAB_HEAP(NAME, COUNT, ...) \ | 235 | #define INITIALIZE_SLAB_HEAP(NAME, COUNT, ...) \ |
| 185 | case KSlabType_##NAME: \ | 236 | case KSlabType_##NAME: \ |
| 186 | address = InitializeSlabHeap<NAME>(system, memory_layout, address, COUNT); \ | 237 | if (COUNT > 0) { \ |
| 238 | address = InitializeSlabHeap<NAME>(system, memory_layout, address, COUNT); \ | ||
| 239 | } \ | ||
| 187 | break; | 240 | break; |
| 188 | 241 | ||
| 189 | // Initialize the slabheap. | 242 | // Initialize the slabheap. |
| @@ -192,7 +245,13 @@ void InitializeSlabHeaps(Core::System& system, KMemoryLayout& memory_layout) { | |||
| 192 | FOREACH_SLAB_TYPE(INITIALIZE_SLAB_HEAP) | 245 | FOREACH_SLAB_TYPE(INITIALIZE_SLAB_HEAP) |
| 193 | // If we somehow get an invalid type, abort. | 246 | // If we somehow get an invalid type, abort. |
| 194 | default: | 247 | default: |
| 195 | UNREACHABLE(); | 248 | UNREACHABLE_MSG("Unknown slab type: {}", slab_types[i]); |
| 249 | } | ||
| 250 | |||
| 251 | // If we've hit the end of a gap, free it. | ||
| 252 | if (gap_start + gap_size != address) { | ||
| 253 | gap_start = address; | ||
| 254 | gap_size = 0; | ||
| 196 | } | 255 | } |
| 197 | } | 256 | } |
| 198 | } | 257 | } |
diff --git a/src/core/hle/kernel/init/init_slab_setup.h b/src/core/hle/kernel/init/init_slab_setup.h index a8f7e0918..f54b67d02 100644 --- a/src/core/hle/kernel/init/init_slab_setup.h +++ b/src/core/hle/kernel/init/init_slab_setup.h | |||
| @@ -32,12 +32,13 @@ struct KSlabResourceCounts { | |||
| 32 | size_t num_KObjectName; | 32 | size_t num_KObjectName; |
| 33 | size_t num_KResourceLimit; | 33 | size_t num_KResourceLimit; |
| 34 | size_t num_KDebug; | 34 | size_t num_KDebug; |
| 35 | size_t num_KAlpha; | 35 | size_t num_KIoPool; |
| 36 | size_t num_KBeta; | 36 | size_t num_KIoRegion; |
| 37 | }; | 37 | }; |
| 38 | 38 | ||
| 39 | void InitializeSlabResourceCounts(KernelCore& kernel); | 39 | void InitializeSlabResourceCounts(KernelCore& kernel); |
| 40 | size_t CalculateTotalSlabHeapSize(const KernelCore& kernel); | 40 | size_t CalculateTotalSlabHeapSize(const KernelCore& kernel); |
| 41 | void InitializeKPageBufferSlabHeap(Core::System& system); | ||
| 41 | void InitializeSlabHeaps(Core::System& system, KMemoryLayout& memory_layout); | 42 | void InitializeSlabHeaps(Core::System& system, KMemoryLayout& memory_layout); |
| 42 | 43 | ||
| 43 | } // namespace Kernel::Init | 44 | } // namespace Kernel::Init |
diff --git a/src/core/hle/kernel/initial_process.h b/src/core/hle/kernel/initial_process.h new file mode 100644 index 000000000..25b27909c --- /dev/null +++ b/src/core/hle/kernel/initial_process.h | |||
| @@ -0,0 +1,23 @@ | |||
| 1 | // Copyright 2022 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "common/common_types.h" | ||
| 8 | #include "common/literals.h" | ||
| 9 | #include "core/hle/kernel/board/nintendo/nx/k_memory_layout.h" | ||
| 10 | #include "core/hle/kernel/board/nintendo/nx/k_system_control.h" | ||
| 11 | |||
| 12 | namespace Kernel { | ||
| 13 | |||
| 14 | using namespace Common::Literals; | ||
| 15 | |||
| 16 | constexpr std::size_t InitialProcessBinarySizeMax = 12_MiB; | ||
| 17 | |||
| 18 | static inline PAddr GetInitialProcessBinaryPhysicalAddress() { | ||
| 19 | return Kernel::Board::Nintendo::Nx::KSystemControl::Init::GetKernelPhysicalBaseAddress( | ||
| 20 | MainMemoryAddress); | ||
| 21 | } | ||
| 22 | |||
| 23 | } // namespace Kernel | ||
diff --git a/src/core/hle/kernel/k_address_arbiter.cpp b/src/core/hle/kernel/k_address_arbiter.cpp index 783c69858..8cdd0490f 100644 --- a/src/core/hle/kernel/k_address_arbiter.cpp +++ b/src/core/hle/kernel/k_address_arbiter.cpp | |||
| @@ -49,7 +49,7 @@ bool DecrementIfLessThan(Core::System& system, s32* out, VAddr address, s32 valu | |||
| 49 | } | 49 | } |
| 50 | } else { | 50 | } else { |
| 51 | // Otherwise, clear our exclusive hold and finish | 51 | // Otherwise, clear our exclusive hold and finish |
| 52 | monitor.ClearExclusive(); | 52 | monitor.ClearExclusive(current_core); |
| 53 | } | 53 | } |
| 54 | 54 | ||
| 55 | // We're done. | 55 | // We're done. |
| @@ -78,7 +78,7 @@ bool UpdateIfEqual(Core::System& system, s32* out, VAddr address, s32 value, s32 | |||
| 78 | } | 78 | } |
| 79 | } else { | 79 | } else { |
| 80 | // Otherwise, clear our exclusive hold and finish. | 80 | // Otherwise, clear our exclusive hold and finish. |
| 81 | monitor.ClearExclusive(); | 81 | monitor.ClearExclusive(current_core); |
| 82 | } | 82 | } |
| 83 | 83 | ||
| 84 | // We're done. | 84 | // We're done. |
| @@ -115,7 +115,7 @@ ResultCode KAddressArbiter::Signal(VAddr addr, s32 count) { | |||
| 115 | { | 115 | { |
| 116 | KScopedSchedulerLock sl(kernel); | 116 | KScopedSchedulerLock sl(kernel); |
| 117 | 117 | ||
| 118 | auto it = thread_tree.nfind_light({addr, -1}); | 118 | auto it = thread_tree.nfind_key({addr, -1}); |
| 119 | while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) && | 119 | while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) && |
| 120 | (it->GetAddressArbiterKey() == addr)) { | 120 | (it->GetAddressArbiterKey() == addr)) { |
| 121 | // End the thread's wait. | 121 | // End the thread's wait. |
| @@ -148,7 +148,7 @@ ResultCode KAddressArbiter::SignalAndIncrementIfEqual(VAddr addr, s32 value, s32 | |||
| 148 | return ResultInvalidState; | 148 | return ResultInvalidState; |
| 149 | } | 149 | } |
| 150 | 150 | ||
| 151 | auto it = thread_tree.nfind_light({addr, -1}); | 151 | auto it = thread_tree.nfind_key({addr, -1}); |
| 152 | while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) && | 152 | while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) && |
| 153 | (it->GetAddressArbiterKey() == addr)) { | 153 | (it->GetAddressArbiterKey() == addr)) { |
| 154 | // End the thread's wait. | 154 | // End the thread's wait. |
| @@ -171,7 +171,7 @@ ResultCode KAddressArbiter::SignalAndModifyByWaitingCountIfEqual(VAddr addr, s32 | |||
| 171 | { | 171 | { |
| 172 | [[maybe_unused]] const KScopedSchedulerLock sl(kernel); | 172 | [[maybe_unused]] const KScopedSchedulerLock sl(kernel); |
| 173 | 173 | ||
| 174 | auto it = thread_tree.nfind_light({addr, -1}); | 174 | auto it = thread_tree.nfind_key({addr, -1}); |
| 175 | // Determine the updated value. | 175 | // Determine the updated value. |
| 176 | s32 new_value{}; | 176 | s32 new_value{}; |
| 177 | if (count <= 0) { | 177 | if (count <= 0) { |
diff --git a/src/core/hle/kernel/k_auto_object.h b/src/core/hle/kernel/k_auto_object.h index 165b76747..05779f2d5 100644 --- a/src/core/hle/kernel/k_auto_object.h +++ b/src/core/hle/kernel/k_auto_object.h | |||
| @@ -20,8 +20,6 @@ class KernelCore; | |||
| 20 | class KProcess; | 20 | class KProcess; |
| 21 | 21 | ||
| 22 | #define KERNEL_AUTOOBJECT_TRAITS(CLASS, BASE_CLASS) \ | 22 | #define KERNEL_AUTOOBJECT_TRAITS(CLASS, BASE_CLASS) \ |
| 23 | YUZU_NON_COPYABLE(CLASS); \ | ||
| 24 | YUZU_NON_MOVEABLE(CLASS); \ | ||
| 25 | \ | 23 | \ |
| 26 | private: \ | 24 | private: \ |
| 27 | friend class ::Kernel::KClassTokenGenerator; \ | 25 | friend class ::Kernel::KClassTokenGenerator; \ |
| @@ -32,6 +30,9 @@ private: | |||
| 32 | } \ | 30 | } \ |
| 33 | \ | 31 | \ |
| 34 | public: \ | 32 | public: \ |
| 33 | YUZU_NON_COPYABLE(CLASS); \ | ||
| 34 | YUZU_NON_MOVEABLE(CLASS); \ | ||
| 35 | \ | ||
| 35 | using BaseClass = BASE_CLASS; \ | 36 | using BaseClass = BASE_CLASS; \ |
| 36 | static constexpr TypeObj GetStaticTypeObj() { \ | 37 | static constexpr TypeObj GetStaticTypeObj() { \ |
| 37 | constexpr ClassTokenType Token = ClassToken(); \ | 38 | constexpr ClassTokenType Token = ClassToken(); \ |
| @@ -224,9 +225,9 @@ private: | |||
| 224 | 225 | ||
| 225 | template <typename T> | 226 | template <typename T> |
| 226 | class KScopedAutoObject { | 227 | class KScopedAutoObject { |
| 228 | public: | ||
| 227 | YUZU_NON_COPYABLE(KScopedAutoObject); | 229 | YUZU_NON_COPYABLE(KScopedAutoObject); |
| 228 | 230 | ||
| 229 | public: | ||
| 230 | constexpr KScopedAutoObject() = default; | 231 | constexpr KScopedAutoObject() = default; |
| 231 | 232 | ||
| 232 | constexpr KScopedAutoObject(T* o) : m_obj(o) { | 233 | constexpr KScopedAutoObject(T* o) : m_obj(o) { |
diff --git a/src/core/hle/kernel/k_auto_object_container.h b/src/core/hle/kernel/k_auto_object_container.h index 4eadfe99d..697cc4289 100644 --- a/src/core/hle/kernel/k_auto_object_container.h +++ b/src/core/hle/kernel/k_auto_object_container.h | |||
| @@ -16,13 +16,12 @@ class KernelCore; | |||
| 16 | class KProcess; | 16 | class KProcess; |
| 17 | 17 | ||
| 18 | class KAutoObjectWithListContainer { | 18 | class KAutoObjectWithListContainer { |
| 19 | public: | ||
| 19 | YUZU_NON_COPYABLE(KAutoObjectWithListContainer); | 20 | YUZU_NON_COPYABLE(KAutoObjectWithListContainer); |
| 20 | YUZU_NON_MOVEABLE(KAutoObjectWithListContainer); | 21 | YUZU_NON_MOVEABLE(KAutoObjectWithListContainer); |
| 21 | 22 | ||
| 22 | public: | ||
| 23 | using ListType = boost::intrusive::rbtree<KAutoObjectWithList>; | 23 | using ListType = boost::intrusive::rbtree<KAutoObjectWithList>; |
| 24 | 24 | ||
| 25 | public: | ||
| 26 | class ListAccessor : public KScopedLightLock { | 25 | class ListAccessor : public KScopedLightLock { |
| 27 | public: | 26 | public: |
| 28 | explicit ListAccessor(KAutoObjectWithListContainer* container) | 27 | explicit ListAccessor(KAutoObjectWithListContainer* container) |
| @@ -48,7 +47,6 @@ public: | |||
| 48 | 47 | ||
| 49 | friend class ListAccessor; | 48 | friend class ListAccessor; |
| 50 | 49 | ||
| 51 | public: | ||
| 52 | KAutoObjectWithListContainer(KernelCore& kernel) : m_lock(kernel), m_object_list() {} | 50 | KAutoObjectWithListContainer(KernelCore& kernel) : m_lock(kernel), m_object_list() {} |
| 53 | 51 | ||
| 54 | void Initialize() {} | 52 | void Initialize() {} |
diff --git a/src/core/hle/kernel/k_code_memory.cpp b/src/core/hle/kernel/k_code_memory.cpp index d69f7ffb7..0b225e8e0 100644 --- a/src/core/hle/kernel/k_code_memory.cpp +++ b/src/core/hle/kernel/k_code_memory.cpp | |||
| @@ -2,6 +2,7 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "common/alignment.h" | ||
| 5 | #include "common/common_types.h" | 6 | #include "common/common_types.h" |
| 6 | #include "core/device_memory.h" | 7 | #include "core/device_memory.h" |
| 7 | #include "core/hle/kernel/k_auto_object.h" | 8 | #include "core/hle/kernel/k_auto_object.h" |
| @@ -28,8 +29,7 @@ ResultCode KCodeMemory::Initialize(Core::DeviceMemory& device_memory, VAddr addr | |||
| 28 | auto& page_table = m_owner->PageTable(); | 29 | auto& page_table = m_owner->PageTable(); |
| 29 | 30 | ||
| 30 | // Construct the page group. | 31 | // Construct the page group. |
| 31 | KMemoryInfo kBlockInfo = page_table.QueryInfo(addr); | 32 | m_page_group = KPageLinkedList(addr, Common::DivideUp(size, PageSize)); |
| 32 | m_page_group = KPageLinkedList(kBlockInfo.GetAddress(), kBlockInfo.GetNumPages()); | ||
| 33 | 33 | ||
| 34 | // Lock the memory. | 34 | // Lock the memory. |
| 35 | R_TRY(page_table.LockForCodeMemory(addr, size)) | 35 | R_TRY(page_table.LockForCodeMemory(addr, size)) |
| @@ -143,4 +143,4 @@ ResultCode KCodeMemory::UnmapFromOwner(VAddr address, size_t size) { | |||
| 143 | return ResultSuccess; | 143 | return ResultSuccess; |
| 144 | } | 144 | } |
| 145 | 145 | ||
| 146 | } // namespace Kernel \ No newline at end of file | 146 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/k_condition_variable.cpp b/src/core/hle/kernel/k_condition_variable.cpp index aadcc297a..8e2a9593c 100644 --- a/src/core/hle/kernel/k_condition_variable.cpp +++ b/src/core/hle/kernel/k_condition_variable.cpp | |||
| @@ -244,7 +244,7 @@ void KConditionVariable::Signal(u64 cv_key, s32 count) { | |||
| 244 | { | 244 | { |
| 245 | KScopedSchedulerLock sl(kernel); | 245 | KScopedSchedulerLock sl(kernel); |
| 246 | 246 | ||
| 247 | auto it = thread_tree.nfind_light({cv_key, -1}); | 247 | auto it = thread_tree.nfind_key({cv_key, -1}); |
| 248 | while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) && | 248 | while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) && |
| 249 | (it->GetConditionVariableKey() == cv_key)) { | 249 | (it->GetConditionVariableKey() == cv_key)) { |
| 250 | KThread* target_thread = std::addressof(*it); | 250 | KThread* target_thread = std::addressof(*it); |
diff --git a/src/core/hle/kernel/k_event.cpp b/src/core/hle/kernel/k_event.cpp index 0720efece..2e0e8de80 100644 --- a/src/core/hle/kernel/k_event.cpp +++ b/src/core/hle/kernel/k_event.cpp | |||
| @@ -14,7 +14,7 @@ KEvent::KEvent(KernelCore& kernel_) | |||
| 14 | 14 | ||
| 15 | KEvent::~KEvent() = default; | 15 | KEvent::~KEvent() = default; |
| 16 | 16 | ||
| 17 | void KEvent::Initialize(std::string&& name_) { | 17 | void KEvent::Initialize(std::string&& name_, KProcess* owner_) { |
| 18 | // Increment reference count. | 18 | // Increment reference count. |
| 19 | // Because reference count is one on creation, this will result | 19 | // Because reference count is one on creation, this will result |
| 20 | // in a reference count of two. Thus, when both readable and | 20 | // in a reference count of two. Thus, when both readable and |
| @@ -30,10 +30,8 @@ void KEvent::Initialize(std::string&& name_) { | |||
| 30 | writable_event.Initialize(this, name_ + ":Writable"); | 30 | writable_event.Initialize(this, name_ + ":Writable"); |
| 31 | 31 | ||
| 32 | // Set our owner process. | 32 | // Set our owner process. |
| 33 | owner = kernel.CurrentProcess(); | 33 | owner = owner_; |
| 34 | if (owner) { | 34 | owner->Open(); |
| 35 | owner->Open(); | ||
| 36 | } | ||
| 37 | 35 | ||
| 38 | // Mark initialized. | 36 | // Mark initialized. |
| 39 | name = std::move(name_); | 37 | name = std::move(name_); |
| @@ -47,10 +45,8 @@ void KEvent::Finalize() { | |||
| 47 | void KEvent::PostDestroy(uintptr_t arg) { | 45 | void KEvent::PostDestroy(uintptr_t arg) { |
| 48 | // Release the event count resource the owner process holds. | 46 | // Release the event count resource the owner process holds. |
| 49 | KProcess* owner = reinterpret_cast<KProcess*>(arg); | 47 | KProcess* owner = reinterpret_cast<KProcess*>(arg); |
| 50 | if (owner) { | 48 | owner->GetResourceLimit()->Release(LimitableResource::Events, 1); |
| 51 | owner->GetResourceLimit()->Release(LimitableResource::Events, 1); | 49 | owner->Close(); |
| 52 | owner->Close(); | ||
| 53 | } | ||
| 54 | } | 50 | } |
| 55 | 51 | ||
| 56 | } // namespace Kernel | 52 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/k_event.h b/src/core/hle/kernel/k_event.h index 3d3ec99e2..de9732ddf 100644 --- a/src/core/hle/kernel/k_event.h +++ b/src/core/hle/kernel/k_event.h | |||
| @@ -22,7 +22,7 @@ public: | |||
| 22 | explicit KEvent(KernelCore& kernel_); | 22 | explicit KEvent(KernelCore& kernel_); |
| 23 | ~KEvent() override; | 23 | ~KEvent() override; |
| 24 | 24 | ||
| 25 | void Initialize(std::string&& name); | 25 | void Initialize(std::string&& name, KProcess* owner_); |
| 26 | 26 | ||
| 27 | void Finalize() override; | 27 | void Finalize() override; |
| 28 | 28 | ||
diff --git a/src/core/hle/kernel/k_handle_table.cpp b/src/core/hle/kernel/k_handle_table.cpp index cf95f0852..db7512ee7 100644 --- a/src/core/hle/kernel/k_handle_table.cpp +++ b/src/core/hle/kernel/k_handle_table.cpp | |||
| @@ -63,7 +63,7 @@ bool KHandleTable::Remove(Handle handle) { | |||
| 63 | return true; | 63 | return true; |
| 64 | } | 64 | } |
| 65 | 65 | ||
| 66 | ResultCode KHandleTable::Add(Handle* out_handle, KAutoObject* obj, u16 type) { | 66 | ResultCode KHandleTable::Add(Handle* out_handle, KAutoObject* obj) { |
| 67 | KScopedDisableDispatch dd(kernel); | 67 | KScopedDisableDispatch dd(kernel); |
| 68 | KScopedSpinLock lk(m_lock); | 68 | KScopedSpinLock lk(m_lock); |
| 69 | 69 | ||
| @@ -75,7 +75,7 @@ ResultCode KHandleTable::Add(Handle* out_handle, KAutoObject* obj, u16 type) { | |||
| 75 | const auto linear_id = this->AllocateLinearId(); | 75 | const auto linear_id = this->AllocateLinearId(); |
| 76 | const auto index = this->AllocateEntry(); | 76 | const auto index = this->AllocateEntry(); |
| 77 | 77 | ||
| 78 | m_entry_infos[index].info = {.linear_id = linear_id, .type = type}; | 78 | m_entry_infos[index].linear_id = linear_id; |
| 79 | m_objects[index] = obj; | 79 | m_objects[index] = obj; |
| 80 | 80 | ||
| 81 | obj->Open(); | 81 | obj->Open(); |
| @@ -116,7 +116,7 @@ void KHandleTable::Unreserve(Handle handle) { | |||
| 116 | } | 116 | } |
| 117 | } | 117 | } |
| 118 | 118 | ||
| 119 | void KHandleTable::Register(Handle handle, KAutoObject* obj, u16 type) { | 119 | void KHandleTable::Register(Handle handle, KAutoObject* obj) { |
| 120 | KScopedDisableDispatch dd(kernel); | 120 | KScopedDisableDispatch dd(kernel); |
| 121 | KScopedSpinLock lk(m_lock); | 121 | KScopedSpinLock lk(m_lock); |
| 122 | 122 | ||
| @@ -132,7 +132,7 @@ void KHandleTable::Register(Handle handle, KAutoObject* obj, u16 type) { | |||
| 132 | // Set the entry. | 132 | // Set the entry. |
| 133 | ASSERT(m_objects[index] == nullptr); | 133 | ASSERT(m_objects[index] == nullptr); |
| 134 | 134 | ||
| 135 | m_entry_infos[index].info = {.linear_id = static_cast<u16>(linear_id), .type = type}; | 135 | m_entry_infos[index].linear_id = static_cast<u16>(linear_id); |
| 136 | m_objects[index] = obj; | 136 | m_objects[index] = obj; |
| 137 | 137 | ||
| 138 | obj->Open(); | 138 | obj->Open(); |
diff --git a/src/core/hle/kernel/k_handle_table.h b/src/core/hle/kernel/k_handle_table.h index 4b114ec2f..dd27689b6 100644 --- a/src/core/hle/kernel/k_handle_table.h +++ b/src/core/hle/kernel/k_handle_table.h | |||
| @@ -22,13 +22,12 @@ namespace Kernel { | |||
| 22 | class KernelCore; | 22 | class KernelCore; |
| 23 | 23 | ||
| 24 | class KHandleTable { | 24 | class KHandleTable { |
| 25 | public: | ||
| 25 | YUZU_NON_COPYABLE(KHandleTable); | 26 | YUZU_NON_COPYABLE(KHandleTable); |
| 26 | YUZU_NON_MOVEABLE(KHandleTable); | 27 | YUZU_NON_MOVEABLE(KHandleTable); |
| 27 | 28 | ||
| 28 | public: | ||
| 29 | static constexpr size_t MaxTableSize = 1024; | 29 | static constexpr size_t MaxTableSize = 1024; |
| 30 | 30 | ||
| 31 | public: | ||
| 32 | explicit KHandleTable(KernelCore& kernel_); | 31 | explicit KHandleTable(KernelCore& kernel_); |
| 33 | ~KHandleTable(); | 32 | ~KHandleTable(); |
| 34 | 33 | ||
| @@ -43,7 +42,7 @@ public: | |||
| 43 | m_free_head_index = -1; | 42 | m_free_head_index = -1; |
| 44 | 43 | ||
| 45 | // Free all entries. | 44 | // Free all entries. |
| 46 | for (s32 i = 0; i < static_cast<s32>(m_table_size); ++i) { | 45 | for (s16 i = 0; i < static_cast<s16>(m_table_size); ++i) { |
| 47 | m_objects[i] = nullptr; | 46 | m_objects[i] = nullptr; |
| 48 | m_entry_infos[i].next_free_index = i - 1; | 47 | m_entry_infos[i].next_free_index = i - 1; |
| 49 | m_free_head_index = i; | 48 | m_free_head_index = i; |
| @@ -105,17 +104,8 @@ public: | |||
| 105 | ResultCode Reserve(Handle* out_handle); | 104 | ResultCode Reserve(Handle* out_handle); |
| 106 | void Unreserve(Handle handle); | 105 | void Unreserve(Handle handle); |
| 107 | 106 | ||
| 108 | template <typename T> | 107 | ResultCode Add(Handle* out_handle, KAutoObject* obj); |
| 109 | ResultCode Add(Handle* out_handle, T* obj) { | 108 | void Register(Handle handle, KAutoObject* obj); |
| 110 | static_assert(std::is_base_of_v<KAutoObject, T>); | ||
| 111 | return this->Add(out_handle, obj, obj->GetTypeObj().GetClassToken()); | ||
| 112 | } | ||
| 113 | |||
| 114 | template <typename T> | ||
| 115 | void Register(Handle handle, T* obj) { | ||
| 116 | static_assert(std::is_base_of_v<KAutoObject, T>); | ||
| 117 | return this->Register(handle, obj, obj->GetTypeObj().GetClassToken()); | ||
| 118 | } | ||
| 119 | 109 | ||
| 120 | template <typename T> | 110 | template <typename T> |
| 121 | bool GetMultipleObjects(T** out, const Handle* handles, size_t num_handles) const { | 111 | bool GetMultipleObjects(T** out, const Handle* handles, size_t num_handles) const { |
| @@ -161,9 +151,6 @@ public: | |||
| 161 | } | 151 | } |
| 162 | 152 | ||
| 163 | private: | 153 | private: |
| 164 | ResultCode Add(Handle* out_handle, KAutoObject* obj, u16 type); | ||
| 165 | void Register(Handle handle, KAutoObject* obj, u16 type); | ||
| 166 | |||
| 167 | s32 AllocateEntry() { | 154 | s32 AllocateEntry() { |
| 168 | ASSERT(m_count < m_table_size); | 155 | ASSERT(m_count < m_table_size); |
| 169 | 156 | ||
| @@ -180,7 +167,7 @@ private: | |||
| 180 | ASSERT(m_count > 0); | 167 | ASSERT(m_count > 0); |
| 181 | 168 | ||
| 182 | m_objects[index] = nullptr; | 169 | m_objects[index] = nullptr; |
| 183 | m_entry_infos[index].next_free_index = m_free_head_index; | 170 | m_entry_infos[index].next_free_index = static_cast<s16>(m_free_head_index); |
| 184 | 171 | ||
| 185 | m_free_head_index = index; | 172 | m_free_head_index = index; |
| 186 | 173 | ||
| @@ -279,19 +266,13 @@ private: | |||
| 279 | } | 266 | } |
| 280 | 267 | ||
| 281 | union EntryInfo { | 268 | union EntryInfo { |
| 282 | struct { | 269 | u16 linear_id; |
| 283 | u16 linear_id; | 270 | s16 next_free_index; |
| 284 | u16 type; | ||
| 285 | } info; | ||
| 286 | s32 next_free_index; | ||
| 287 | 271 | ||
| 288 | constexpr u16 GetLinearId() const { | 272 | constexpr u16 GetLinearId() const { |
| 289 | return info.linear_id; | 273 | return linear_id; |
| 290 | } | ||
| 291 | constexpr u16 GetType() const { | ||
| 292 | return info.type; | ||
| 293 | } | 274 | } |
| 294 | constexpr s32 GetNextFreeIndex() const { | 275 | constexpr s16 GetNextFreeIndex() const { |
| 295 | return next_free_index; | 276 | return next_free_index; |
| 296 | } | 277 | } |
| 297 | }; | 278 | }; |
diff --git a/src/core/hle/kernel/k_memory_layout.h b/src/core/hle/kernel/k_memory_layout.h index 57ff538cc..0858827b6 100644 --- a/src/core/hle/kernel/k_memory_layout.h +++ b/src/core/hle/kernel/k_memory_layout.h | |||
| @@ -57,11 +57,11 @@ constexpr std::size_t KernelPageTableHeapSize = GetMaximumOverheadSize(MainMemor | |||
| 57 | constexpr std::size_t KernelInitialPageHeapSize = 128_KiB; | 57 | constexpr std::size_t KernelInitialPageHeapSize = 128_KiB; |
| 58 | 58 | ||
| 59 | constexpr std::size_t KernelSlabHeapDataSize = 5_MiB; | 59 | constexpr std::size_t KernelSlabHeapDataSize = 5_MiB; |
| 60 | constexpr std::size_t KernelSlabHeapGapsSize = 2_MiB - 64_KiB; | 60 | constexpr std::size_t KernelSlabHeapGapsSizeMax = 2_MiB - 64_KiB; |
| 61 | constexpr std::size_t KernelSlabHeapSize = KernelSlabHeapDataSize + KernelSlabHeapGapsSize; | 61 | constexpr std::size_t KernelSlabHeapSize = KernelSlabHeapDataSize + KernelSlabHeapGapsSizeMax; |
| 62 | 62 | ||
| 63 | // NOTE: This is calculated from KThread slab counts, assuming KThread size <= 0x860. | 63 | // NOTE: This is calculated from KThread slab counts, assuming KThread size <= 0x860. |
| 64 | constexpr std::size_t KernelSlabHeapAdditionalSize = 416_KiB; | 64 | constexpr std::size_t KernelSlabHeapAdditionalSize = 0x68000; |
| 65 | 65 | ||
| 66 | constexpr std::size_t KernelResourceSize = | 66 | constexpr std::size_t KernelResourceSize = |
| 67 | KernelPageTableHeapSize + KernelInitialPageHeapSize + KernelSlabHeapSize; | 67 | KernelPageTableHeapSize + KernelInitialPageHeapSize + KernelSlabHeapSize; |
| @@ -173,6 +173,10 @@ public: | |||
| 173 | return Dereference(FindVirtualLinear(address)); | 173 | return Dereference(FindVirtualLinear(address)); |
| 174 | } | 174 | } |
| 175 | 175 | ||
| 176 | const KMemoryRegion& GetPhysicalLinearRegion(PAddr address) const { | ||
| 177 | return Dereference(FindPhysicalLinear(address)); | ||
| 178 | } | ||
| 179 | |||
| 176 | const KMemoryRegion* GetPhysicalKernelTraceBufferRegion() const { | 180 | const KMemoryRegion* GetPhysicalKernelTraceBufferRegion() const { |
| 177 | return GetPhysicalMemoryRegionTree().FindFirstDerived(KMemoryRegionType_KernelTraceBuffer); | 181 | return GetPhysicalMemoryRegionTree().FindFirstDerived(KMemoryRegionType_KernelTraceBuffer); |
| 178 | } | 182 | } |
diff --git a/src/core/hle/kernel/k_memory_manager.cpp b/src/core/hle/kernel/k_memory_manager.cpp index 1b44541b1..a2f18f643 100644 --- a/src/core/hle/kernel/k_memory_manager.cpp +++ b/src/core/hle/kernel/k_memory_manager.cpp | |||
| @@ -10,189 +10,412 @@ | |||
| 10 | #include "common/scope_exit.h" | 10 | #include "common/scope_exit.h" |
| 11 | #include "core/core.h" | 11 | #include "core/core.h" |
| 12 | #include "core/device_memory.h" | 12 | #include "core/device_memory.h" |
| 13 | #include "core/hle/kernel/initial_process.h" | ||
| 13 | #include "core/hle/kernel/k_memory_manager.h" | 14 | #include "core/hle/kernel/k_memory_manager.h" |
| 14 | #include "core/hle/kernel/k_page_linked_list.h" | 15 | #include "core/hle/kernel/k_page_linked_list.h" |
| 16 | #include "core/hle/kernel/kernel.h" | ||
| 15 | #include "core/hle/kernel/svc_results.h" | 17 | #include "core/hle/kernel/svc_results.h" |
| 18 | #include "core/memory.h" | ||
| 16 | 19 | ||
| 17 | namespace Kernel { | 20 | namespace Kernel { |
| 18 | 21 | ||
| 19 | KMemoryManager::KMemoryManager(Core::System& system_) : system{system_} {} | 22 | namespace { |
| 23 | |||
| 24 | constexpr KMemoryManager::Pool GetPoolFromMemoryRegionType(u32 type) { | ||
| 25 | if ((type | KMemoryRegionType_DramApplicationPool) == type) { | ||
| 26 | return KMemoryManager::Pool::Application; | ||
| 27 | } else if ((type | KMemoryRegionType_DramAppletPool) == type) { | ||
| 28 | return KMemoryManager::Pool::Applet; | ||
| 29 | } else if ((type | KMemoryRegionType_DramSystemPool) == type) { | ||
| 30 | return KMemoryManager::Pool::System; | ||
| 31 | } else if ((type | KMemoryRegionType_DramSystemNonSecurePool) == type) { | ||
| 32 | return KMemoryManager::Pool::SystemNonSecure; | ||
| 33 | } else { | ||
| 34 | UNREACHABLE_MSG("InvalidMemoryRegionType for conversion to Pool"); | ||
| 35 | return {}; | ||
| 36 | } | ||
| 37 | } | ||
| 20 | 38 | ||
| 21 | std::size_t KMemoryManager::Impl::Initialize(Pool new_pool, u64 start_address, u64 end_address) { | 39 | } // namespace |
| 22 | const auto size{end_address - start_address}; | 40 | |
| 41 | KMemoryManager::KMemoryManager(Core::System& system_) | ||
| 42 | : system{system_}, pool_locks{ | ||
| 43 | KLightLock{system_.Kernel()}, | ||
| 44 | KLightLock{system_.Kernel()}, | ||
| 45 | KLightLock{system_.Kernel()}, | ||
| 46 | KLightLock{system_.Kernel()}, | ||
| 47 | } {} | ||
| 48 | |||
| 49 | void KMemoryManager::Initialize(VAddr management_region, size_t management_region_size) { | ||
| 50 | |||
| 51 | // Clear the management region to zero. | ||
| 52 | const VAddr management_region_end = management_region + management_region_size; | ||
| 53 | |||
| 54 | // Reset our manager count. | ||
| 55 | num_managers = 0; | ||
| 56 | |||
| 57 | // Traverse the virtual memory layout tree, initializing each manager as appropriate. | ||
| 58 | while (num_managers != MaxManagerCount) { | ||
| 59 | // Locate the region that should initialize the current manager. | ||
| 60 | PAddr region_address = 0; | ||
| 61 | size_t region_size = 0; | ||
| 62 | Pool region_pool = Pool::Count; | ||
| 63 | for (const auto& it : system.Kernel().MemoryLayout().GetPhysicalMemoryRegionTree()) { | ||
| 64 | // We only care about regions that we need to create managers for. | ||
| 65 | if (!it.IsDerivedFrom(KMemoryRegionType_DramUserPool)) { | ||
| 66 | continue; | ||
| 67 | } | ||
| 23 | 68 | ||
| 24 | // Calculate metadata sizes | 69 | // We want to initialize the managers in order. |
| 25 | const auto ref_count_size{(size / PageSize) * sizeof(u16)}; | 70 | if (it.GetAttributes() != num_managers) { |
| 26 | const auto optimize_map_size{(Common::AlignUp((size / PageSize), 64) / 64) * sizeof(u64)}; | 71 | continue; |
| 27 | const auto manager_size{Common::AlignUp(optimize_map_size + ref_count_size, PageSize)}; | 72 | } |
| 28 | const auto page_heap_size{KPageHeap::CalculateManagementOverheadSize(size)}; | ||
| 29 | const auto total_metadata_size{manager_size + page_heap_size}; | ||
| 30 | ASSERT(manager_size <= total_metadata_size); | ||
| 31 | ASSERT(Common::IsAligned(total_metadata_size, PageSize)); | ||
| 32 | 73 | ||
| 33 | // Setup region | 74 | const PAddr cur_start = it.GetAddress(); |
| 34 | pool = new_pool; | 75 | const PAddr cur_end = it.GetEndAddress(); |
| 76 | |||
| 77 | // Validate the region. | ||
| 78 | ASSERT(cur_end != 0); | ||
| 79 | ASSERT(cur_start != 0); | ||
| 80 | ASSERT(it.GetSize() > 0); | ||
| 81 | |||
| 82 | // Update the region's extents. | ||
| 83 | if (region_address == 0) { | ||
| 84 | region_address = cur_start; | ||
| 85 | region_size = it.GetSize(); | ||
| 86 | region_pool = GetPoolFromMemoryRegionType(it.GetType()); | ||
| 87 | } else { | ||
| 88 | ASSERT(cur_start == region_address + region_size); | ||
| 89 | |||
| 90 | // Update the size. | ||
| 91 | region_size = cur_end - region_address; | ||
| 92 | ASSERT(GetPoolFromMemoryRegionType(it.GetType()) == region_pool); | ||
| 93 | } | ||
| 94 | } | ||
| 95 | |||
| 96 | // If we didn't find a region, we're done. | ||
| 97 | if (region_size == 0) { | ||
| 98 | break; | ||
| 99 | } | ||
| 35 | 100 | ||
| 36 | // Initialize the manager's KPageHeap | 101 | // Initialize a new manager for the region. |
| 37 | heap.Initialize(start_address, size, page_heap_size); | 102 | Impl* manager = std::addressof(managers[num_managers++]); |
| 103 | ASSERT(num_managers <= managers.size()); | ||
| 104 | |||
| 105 | const size_t cur_size = manager->Initialize(region_address, region_size, management_region, | ||
| 106 | management_region_end, region_pool); | ||
| 107 | management_region += cur_size; | ||
| 108 | ASSERT(management_region <= management_region_end); | ||
| 109 | |||
| 110 | // Insert the manager into the pool list. | ||
| 111 | const auto region_pool_index = static_cast<u32>(region_pool); | ||
| 112 | if (pool_managers_tail[region_pool_index] == nullptr) { | ||
| 113 | pool_managers_head[region_pool_index] = manager; | ||
| 114 | } else { | ||
| 115 | pool_managers_tail[region_pool_index]->SetNext(manager); | ||
| 116 | manager->SetPrev(pool_managers_tail[region_pool_index]); | ||
| 117 | } | ||
| 118 | pool_managers_tail[region_pool_index] = manager; | ||
| 119 | } | ||
| 38 | 120 | ||
| 39 | // Free the memory to the heap | 121 | // Free each region to its corresponding heap. |
| 40 | heap.Free(start_address, size / PageSize); | 122 | size_t reserved_sizes[MaxManagerCount] = {}; |
| 123 | const PAddr ini_start = GetInitialProcessBinaryPhysicalAddress(); | ||
| 124 | const PAddr ini_end = ini_start + InitialProcessBinarySizeMax; | ||
| 125 | const PAddr ini_last = ini_end - 1; | ||
| 126 | for (const auto& it : system.Kernel().MemoryLayout().GetPhysicalMemoryRegionTree()) { | ||
| 127 | if (it.IsDerivedFrom(KMemoryRegionType_DramUserPool)) { | ||
| 128 | // Get the manager for the region. | ||
| 129 | auto index = it.GetAttributes(); | ||
| 130 | auto& manager = managers[index]; | ||
| 131 | |||
| 132 | const PAddr cur_start = it.GetAddress(); | ||
| 133 | const PAddr cur_last = it.GetLastAddress(); | ||
| 134 | const PAddr cur_end = it.GetEndAddress(); | ||
| 135 | |||
| 136 | if (cur_start <= ini_start && ini_last <= cur_last) { | ||
| 137 | // Free memory before the ini to the heap. | ||
| 138 | if (cur_start != ini_start) { | ||
| 139 | manager.Free(cur_start, (ini_start - cur_start) / PageSize); | ||
| 140 | } | ||
| 41 | 141 | ||
| 42 | // Update the heap's used size | 142 | // Open/reserve the ini memory. |
| 43 | heap.UpdateUsedSize(); | 143 | manager.OpenFirst(ini_start, InitialProcessBinarySizeMax / PageSize); |
| 144 | reserved_sizes[it.GetAttributes()] += InitialProcessBinarySizeMax; | ||
| 44 | 145 | ||
| 45 | return total_metadata_size; | 146 | // Free memory after the ini to the heap. |
| 46 | } | 147 | if (ini_last != cur_last) { |
| 148 | ASSERT(cur_end != 0); | ||
| 149 | manager.Free(ini_end, cur_end - ini_end); | ||
| 150 | } | ||
| 151 | } else { | ||
| 152 | // Ensure there's no partial overlap with the ini image. | ||
| 153 | if (cur_start <= ini_last) { | ||
| 154 | ASSERT(cur_last < ini_start); | ||
| 155 | } else { | ||
| 156 | // Otherwise, check the region for general validity. | ||
| 157 | ASSERT(cur_end != 0); | ||
| 158 | } | ||
| 47 | 159 | ||
| 48 | void KMemoryManager::InitializeManager(Pool pool, u64 start_address, u64 end_address) { | 160 | // Free the memory to the heap. |
| 49 | ASSERT(pool < Pool::Count); | 161 | manager.Free(cur_start, it.GetSize() / PageSize); |
| 50 | managers[static_cast<std::size_t>(pool)].Initialize(pool, start_address, end_address); | 162 | } |
| 163 | } | ||
| 164 | } | ||
| 165 | |||
| 166 | // Update the used size for all managers. | ||
| 167 | for (size_t i = 0; i < num_managers; ++i) { | ||
| 168 | managers[i].SetInitialUsedHeapSize(reserved_sizes[i]); | ||
| 169 | } | ||
| 51 | } | 170 | } |
| 52 | 171 | ||
| 53 | VAddr KMemoryManager::AllocateAndOpenContinuous(std::size_t num_pages, std::size_t align_pages, | 172 | PAddr KMemoryManager::AllocateAndOpenContinuous(size_t num_pages, size_t align_pages, u32 option) { |
| 54 | u32 option) { | 173 | // Early return if we're allocating no pages. |
| 55 | // Early return if we're allocating no pages | ||
| 56 | if (num_pages == 0) { | 174 | if (num_pages == 0) { |
| 57 | return {}; | 175 | return 0; |
| 58 | } | 176 | } |
| 59 | 177 | ||
| 60 | // Lock the pool that we're allocating from | 178 | // Lock the pool that we're allocating from. |
| 61 | const auto [pool, dir] = DecodeOption(option); | 179 | const auto [pool, dir] = DecodeOption(option); |
| 62 | const auto pool_index{static_cast<std::size_t>(pool)}; | 180 | KScopedLightLock lk(pool_locks[static_cast<std::size_t>(pool)]); |
| 63 | std::lock_guard lock{pool_locks[pool_index]}; | 181 | |
| 64 | 182 | // Choose a heap based on our page size request. | |
| 65 | // Choose a heap based on our page size request | 183 | const s32 heap_index = KPageHeap::GetAlignedBlockIndex(num_pages, align_pages); |
| 66 | const s32 heap_index{KPageHeap::GetAlignedBlockIndex(num_pages, align_pages)}; | 184 | |
| 67 | 185 | // Loop, trying to iterate from each block. | |
| 68 | // Loop, trying to iterate from each block | 186 | Impl* chosen_manager = nullptr; |
| 69 | // TODO (bunnei): Support multiple managers | 187 | PAddr allocated_block = 0; |
| 70 | Impl& chosen_manager{managers[pool_index]}; | 188 | for (chosen_manager = this->GetFirstManager(pool, dir); chosen_manager != nullptr; |
| 71 | VAddr allocated_block{chosen_manager.AllocateBlock(heap_index, false)}; | 189 | chosen_manager = this->GetNextManager(chosen_manager, dir)) { |
| 190 | allocated_block = chosen_manager->AllocateBlock(heap_index, true); | ||
| 191 | if (allocated_block != 0) { | ||
| 192 | break; | ||
| 193 | } | ||
| 194 | } | ||
| 72 | 195 | ||
| 73 | // If we failed to allocate, quit now | 196 | // If we failed to allocate, quit now. |
| 74 | if (!allocated_block) { | 197 | if (allocated_block == 0) { |
| 75 | return {}; | 198 | return 0; |
| 76 | } | 199 | } |
| 77 | 200 | ||
| 78 | // If we allocated more than we need, free some | 201 | // If we allocated more than we need, free some. |
| 79 | const auto allocated_pages{KPageHeap::GetBlockNumPages(heap_index)}; | 202 | const size_t allocated_pages = KPageHeap::GetBlockNumPages(heap_index); |
| 80 | if (allocated_pages > num_pages) { | 203 | if (allocated_pages > num_pages) { |
| 81 | chosen_manager.Free(allocated_block + num_pages * PageSize, allocated_pages - num_pages); | 204 | chosen_manager->Free(allocated_block + num_pages * PageSize, allocated_pages - num_pages); |
| 82 | } | 205 | } |
| 83 | 206 | ||
| 207 | // Open the first reference to the pages. | ||
| 208 | chosen_manager->OpenFirst(allocated_block, num_pages); | ||
| 209 | |||
| 84 | return allocated_block; | 210 | return allocated_block; |
| 85 | } | 211 | } |
| 86 | 212 | ||
| 87 | ResultCode KMemoryManager::Allocate(KPageLinkedList& page_list, std::size_t num_pages, Pool pool, | 213 | ResultCode KMemoryManager::AllocatePageGroupImpl(KPageLinkedList* out, size_t num_pages, Pool pool, |
| 88 | Direction dir, u32 heap_fill_value) { | 214 | Direction dir, bool random) { |
| 89 | ASSERT(page_list.GetNumPages() == 0); | 215 | // Choose a heap based on our page size request. |
| 216 | const s32 heap_index = KPageHeap::GetBlockIndex(num_pages); | ||
| 217 | R_UNLESS(0 <= heap_index, ResultOutOfMemory); | ||
| 218 | |||
| 219 | // Ensure that we don't leave anything un-freed. | ||
| 220 | auto group_guard = SCOPE_GUARD({ | ||
| 221 | for (const auto& it : out->Nodes()) { | ||
| 222 | auto& manager = this->GetManager(system.Kernel().MemoryLayout(), it.GetAddress()); | ||
| 223 | const size_t num_pages_to_free = | ||
| 224 | std::min(it.GetNumPages(), (manager.GetEndAddress() - it.GetAddress()) / PageSize); | ||
| 225 | manager.Free(it.GetAddress(), num_pages_to_free); | ||
| 226 | } | ||
| 227 | }); | ||
| 90 | 228 | ||
| 91 | // Early return if we're allocating no pages | 229 | // Keep allocating until we've allocated all our pages. |
| 92 | if (num_pages == 0) { | 230 | for (s32 index = heap_index; index >= 0 && num_pages > 0; index--) { |
| 93 | return ResultSuccess; | 231 | const size_t pages_per_alloc = KPageHeap::GetBlockNumPages(index); |
| 94 | } | 232 | for (Impl* cur_manager = this->GetFirstManager(pool, dir); cur_manager != nullptr; |
| 233 | cur_manager = this->GetNextManager(cur_manager, dir)) { | ||
| 234 | while (num_pages >= pages_per_alloc) { | ||
| 235 | // Allocate a block. | ||
| 236 | PAddr allocated_block = cur_manager->AllocateBlock(index, random); | ||
| 237 | if (allocated_block == 0) { | ||
| 238 | break; | ||
| 239 | } | ||
| 95 | 240 | ||
| 96 | // Lock the pool that we're allocating from | 241 | // Safely add it to our group. |
| 97 | const auto pool_index{static_cast<std::size_t>(pool)}; | 242 | { |
| 98 | std::lock_guard lock{pool_locks[pool_index]}; | 243 | auto block_guard = |
| 244 | SCOPE_GUARD({ cur_manager->Free(allocated_block, pages_per_alloc); }); | ||
| 245 | R_TRY(out->AddBlock(allocated_block, pages_per_alloc)); | ||
| 246 | block_guard.Cancel(); | ||
| 247 | } | ||
| 99 | 248 | ||
| 100 | // Choose a heap based on our page size request | 249 | num_pages -= pages_per_alloc; |
| 101 | const s32 heap_index{KPageHeap::GetBlockIndex(num_pages)}; | 250 | } |
| 102 | if (heap_index < 0) { | 251 | } |
| 103 | return ResultOutOfMemory; | ||
| 104 | } | 252 | } |
| 105 | 253 | ||
| 106 | // TODO (bunnei): Support multiple managers | 254 | // Only succeed if we allocated as many pages as we wanted. |
| 107 | Impl& chosen_manager{managers[pool_index]}; | 255 | R_UNLESS(num_pages == 0, ResultOutOfMemory); |
| 108 | 256 | ||
| 109 | // Ensure that we don't leave anything un-freed | 257 | // We succeeded! |
| 110 | auto group_guard = detail::ScopeExit([&] { | 258 | group_guard.Cancel(); |
| 111 | for (const auto& it : page_list.Nodes()) { | 259 | return ResultSuccess; |
| 112 | const auto min_num_pages{std::min<size_t>( | 260 | } |
| 113 | it.GetNumPages(), (chosen_manager.GetEndAddress() - it.GetAddress()) / PageSize)}; | ||
| 114 | chosen_manager.Free(it.GetAddress(), min_num_pages); | ||
| 115 | } | ||
| 116 | }); | ||
| 117 | 261 | ||
| 118 | // Keep allocating until we've allocated all our pages | 262 | ResultCode KMemoryManager::AllocateAndOpen(KPageLinkedList* out, size_t num_pages, u32 option) { |
| 119 | for (s32 index{heap_index}; index >= 0 && num_pages > 0; index--) { | 263 | ASSERT(out != nullptr); |
| 120 | const auto pages_per_alloc{KPageHeap::GetBlockNumPages(index)}; | 264 | ASSERT(out->GetNumPages() == 0); |
| 121 | 265 | ||
| 122 | while (num_pages >= pages_per_alloc) { | 266 | // Early return if we're allocating no pages. |
| 123 | // Allocate a block | 267 | R_SUCCEED_IF(num_pages == 0); |
| 124 | VAddr allocated_block{chosen_manager.AllocateBlock(index, false)}; | ||
| 125 | if (!allocated_block) { | ||
| 126 | break; | ||
| 127 | } | ||
| 128 | 268 | ||
| 129 | // Safely add it to our group | 269 | // Lock the pool that we're allocating from. |
| 130 | { | 270 | const auto [pool, dir] = DecodeOption(option); |
| 131 | auto block_guard = detail::ScopeExit( | 271 | KScopedLightLock lk(pool_locks[static_cast<size_t>(pool)]); |
| 132 | [&] { chosen_manager.Free(allocated_block, pages_per_alloc); }); | 272 | |
| 273 | // Allocate the page group. | ||
| 274 | R_TRY(this->AllocatePageGroupImpl(out, num_pages, pool, dir, false)); | ||
| 275 | |||
| 276 | // Open the first reference to the pages. | ||
| 277 | for (const auto& block : out->Nodes()) { | ||
| 278 | PAddr cur_address = block.GetAddress(); | ||
| 279 | size_t remaining_pages = block.GetNumPages(); | ||
| 280 | while (remaining_pages > 0) { | ||
| 281 | // Get the manager for the current address. | ||
| 282 | auto& manager = this->GetManager(system.Kernel().MemoryLayout(), cur_address); | ||
| 283 | |||
| 284 | // Process part or all of the block. | ||
| 285 | const size_t cur_pages = | ||
| 286 | std::min(remaining_pages, manager.GetPageOffsetToEnd(cur_address)); | ||
| 287 | manager.OpenFirst(cur_address, cur_pages); | ||
| 288 | |||
| 289 | // Advance. | ||
| 290 | cur_address += cur_pages * PageSize; | ||
| 291 | remaining_pages -= cur_pages; | ||
| 292 | } | ||
| 293 | } | ||
| 133 | 294 | ||
| 134 | if (const ResultCode result{page_list.AddBlock(allocated_block, pages_per_alloc)}; | 295 | return ResultSuccess; |
| 135 | result.IsError()) { | 296 | } |
| 136 | return result; | ||
| 137 | } | ||
| 138 | 297 | ||
| 139 | block_guard.Cancel(); | 298 | ResultCode KMemoryManager::AllocateAndOpenForProcess(KPageLinkedList* out, size_t num_pages, |
| 140 | } | 299 | u32 option, u64 process_id, u8 fill_pattern) { |
| 300 | ASSERT(out != nullptr); | ||
| 301 | ASSERT(out->GetNumPages() == 0); | ||
| 141 | 302 | ||
| 142 | num_pages -= pages_per_alloc; | 303 | // Decode the option. |
| 143 | } | 304 | const auto [pool, dir] = DecodeOption(option); |
| 144 | } | ||
| 145 | 305 | ||
| 146 | // Clear allocated memory. | 306 | // Allocate the memory. |
| 147 | for (const auto& it : page_list.Nodes()) { | 307 | { |
| 148 | std::memset(system.DeviceMemory().GetPointer(it.GetAddress()), heap_fill_value, | 308 | // Lock the pool that we're allocating from. |
| 149 | it.GetSize()); | 309 | KScopedLightLock lk(pool_locks[static_cast<size_t>(pool)]); |
| 310 | |||
| 311 | // Allocate the page group. | ||
| 312 | R_TRY(this->AllocatePageGroupImpl(out, num_pages, pool, dir, false)); | ||
| 313 | |||
| 314 | // Open the first reference to the pages. | ||
| 315 | for (const auto& block : out->Nodes()) { | ||
| 316 | PAddr cur_address = block.GetAddress(); | ||
| 317 | size_t remaining_pages = block.GetNumPages(); | ||
| 318 | while (remaining_pages > 0) { | ||
| 319 | // Get the manager for the current address. | ||
| 320 | auto& manager = this->GetManager(system.Kernel().MemoryLayout(), cur_address); | ||
| 321 | |||
| 322 | // Process part or all of the block. | ||
| 323 | const size_t cur_pages = | ||
| 324 | std::min(remaining_pages, manager.GetPageOffsetToEnd(cur_address)); | ||
| 325 | manager.OpenFirst(cur_address, cur_pages); | ||
| 326 | |||
| 327 | // Advance. | ||
| 328 | cur_address += cur_pages * PageSize; | ||
| 329 | remaining_pages -= cur_pages; | ||
| 330 | } | ||
| 331 | } | ||
| 150 | } | 332 | } |
| 151 | 333 | ||
| 152 | // Only succeed if we allocated as many pages as we wanted | 334 | // Set all the allocated memory. |
| 153 | if (num_pages) { | 335 | for (const auto& block : out->Nodes()) { |
| 154 | return ResultOutOfMemory; | 336 | std::memset(system.DeviceMemory().GetPointer(block.GetAddress()), fill_pattern, |
| 337 | block.GetSize()); | ||
| 155 | } | 338 | } |
| 156 | 339 | ||
| 157 | // We succeeded! | ||
| 158 | group_guard.Cancel(); | ||
| 159 | |||
| 160 | return ResultSuccess; | 340 | return ResultSuccess; |
| 161 | } | 341 | } |
| 162 | 342 | ||
| 163 | ResultCode KMemoryManager::Free(KPageLinkedList& page_list, std::size_t num_pages, Pool pool, | 343 | void KMemoryManager::Open(PAddr address, size_t num_pages) { |
| 164 | Direction dir, u32 heap_fill_value) { | 344 | // Repeatedly open references until we've done so for all pages. |
| 165 | // Early return if we're freeing no pages | 345 | while (num_pages) { |
| 166 | if (!num_pages) { | 346 | auto& manager = this->GetManager(system.Kernel().MemoryLayout(), address); |
| 167 | return ResultSuccess; | 347 | const size_t cur_pages = std::min(num_pages, manager.GetPageOffsetToEnd(address)); |
| 348 | |||
| 349 | { | ||
| 350 | KScopedLightLock lk(pool_locks[static_cast<size_t>(manager.GetPool())]); | ||
| 351 | manager.Open(address, cur_pages); | ||
| 352 | } | ||
| 353 | |||
| 354 | num_pages -= cur_pages; | ||
| 355 | address += cur_pages * PageSize; | ||
| 168 | } | 356 | } |
| 357 | } | ||
| 169 | 358 | ||
| 170 | // Lock the pool that we're freeing from | 359 | void KMemoryManager::Close(PAddr address, size_t num_pages) { |
| 171 | const auto pool_index{static_cast<std::size_t>(pool)}; | 360 | // Repeatedly close references until we've done so for all pages. |
| 172 | std::lock_guard lock{pool_locks[pool_index]}; | 361 | while (num_pages) { |
| 362 | auto& manager = this->GetManager(system.Kernel().MemoryLayout(), address); | ||
| 363 | const size_t cur_pages = std::min(num_pages, manager.GetPageOffsetToEnd(address)); | ||
| 173 | 364 | ||
| 174 | // TODO (bunnei): Support multiple managers | 365 | { |
| 175 | Impl& chosen_manager{managers[pool_index]}; | 366 | KScopedLightLock lk(pool_locks[static_cast<size_t>(manager.GetPool())]); |
| 367 | manager.Close(address, cur_pages); | ||
| 368 | } | ||
| 176 | 369 | ||
| 177 | // Free all of the pages | 370 | num_pages -= cur_pages; |
| 178 | for (const auto& it : page_list.Nodes()) { | 371 | address += cur_pages * PageSize; |
| 179 | const auto min_num_pages{std::min<size_t>( | ||
| 180 | it.GetNumPages(), (chosen_manager.GetEndAddress() - it.GetAddress()) / PageSize)}; | ||
| 181 | chosen_manager.Free(it.GetAddress(), min_num_pages); | ||
| 182 | } | 372 | } |
| 373 | } | ||
| 183 | 374 | ||
| 184 | return ResultSuccess; | 375 | void KMemoryManager::Close(const KPageLinkedList& pg) { |
| 376 | for (const auto& node : pg.Nodes()) { | ||
| 377 | Close(node.GetAddress(), node.GetNumPages()); | ||
| 378 | } | ||
| 379 | } | ||
| 380 | void KMemoryManager::Open(const KPageLinkedList& pg) { | ||
| 381 | for (const auto& node : pg.Nodes()) { | ||
| 382 | Open(node.GetAddress(), node.GetNumPages()); | ||
| 383 | } | ||
| 384 | } | ||
| 385 | |||
| 386 | size_t KMemoryManager::Impl::Initialize(PAddr address, size_t size, VAddr management, | ||
| 387 | VAddr management_end, Pool p) { | ||
| 388 | // Calculate management sizes. | ||
| 389 | const size_t ref_count_size = (size / PageSize) * sizeof(u16); | ||
| 390 | const size_t optimize_map_size = CalculateOptimizedProcessOverheadSize(size); | ||
| 391 | const size_t manager_size = Common::AlignUp(optimize_map_size + ref_count_size, PageSize); | ||
| 392 | const size_t page_heap_size = KPageHeap::CalculateManagementOverheadSize(size); | ||
| 393 | const size_t total_management_size = manager_size + page_heap_size; | ||
| 394 | ASSERT(manager_size <= total_management_size); | ||
| 395 | ASSERT(management + total_management_size <= management_end); | ||
| 396 | ASSERT(Common::IsAligned(total_management_size, PageSize)); | ||
| 397 | |||
| 398 | // Setup region. | ||
| 399 | pool = p; | ||
| 400 | management_region = management; | ||
| 401 | page_reference_counts.resize( | ||
| 402 | Kernel::Board::Nintendo::Nx::KSystemControl::Init::GetIntendedMemorySize() / PageSize); | ||
| 403 | ASSERT(Common::IsAligned(management_region, PageSize)); | ||
| 404 | |||
| 405 | // Initialize the manager's KPageHeap. | ||
| 406 | heap.Initialize(address, size, management + manager_size, page_heap_size); | ||
| 407 | |||
| 408 | return total_management_size; | ||
| 185 | } | 409 | } |
| 186 | 410 | ||
| 187 | std::size_t KMemoryManager::Impl::CalculateManagementOverheadSize(std::size_t region_size) { | 411 | size_t KMemoryManager::Impl::CalculateManagementOverheadSize(size_t region_size) { |
| 188 | const std::size_t ref_count_size = (region_size / PageSize) * sizeof(u16); | 412 | const size_t ref_count_size = (region_size / PageSize) * sizeof(u16); |
| 189 | const std::size_t optimize_map_size = | 413 | const size_t optimize_map_size = |
| 190 | (Common::AlignUp((region_size / PageSize), Common::BitSize<u64>()) / | 414 | (Common::AlignUp((region_size / PageSize), Common::BitSize<u64>()) / |
| 191 | Common::BitSize<u64>()) * | 415 | Common::BitSize<u64>()) * |
| 192 | sizeof(u64); | 416 | sizeof(u64); |
| 193 | const std::size_t manager_meta_size = | 417 | const size_t manager_meta_size = Common::AlignUp(optimize_map_size + ref_count_size, PageSize); |
| 194 | Common::AlignUp(optimize_map_size + ref_count_size, PageSize); | 418 | const size_t page_heap_size = KPageHeap::CalculateManagementOverheadSize(region_size); |
| 195 | const std::size_t page_heap_size = KPageHeap::CalculateManagementOverheadSize(region_size); | ||
| 196 | return manager_meta_size + page_heap_size; | 419 | return manager_meta_size + page_heap_size; |
| 197 | } | 420 | } |
| 198 | 421 | ||
diff --git a/src/core/hle/kernel/k_memory_manager.h b/src/core/hle/kernel/k_memory_manager.h index abd6c8ace..18775b262 100644 --- a/src/core/hle/kernel/k_memory_manager.h +++ b/src/core/hle/kernel/k_memory_manager.h | |||
| @@ -5,10 +5,12 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <array> | 7 | #include <array> |
| 8 | #include <mutex> | ||
| 9 | #include <tuple> | 8 | #include <tuple> |
| 10 | 9 | ||
| 10 | #include "common/common_funcs.h" | ||
| 11 | #include "common/common_types.h" | 11 | #include "common/common_types.h" |
| 12 | #include "core/hle/kernel/k_light_lock.h" | ||
| 13 | #include "core/hle/kernel/k_memory_layout.h" | ||
| 12 | #include "core/hle/kernel/k_page_heap.h" | 14 | #include "core/hle/kernel/k_page_heap.h" |
| 13 | #include "core/hle/result.h" | 15 | #include "core/hle/result.h" |
| 14 | 16 | ||
| @@ -20,8 +22,11 @@ namespace Kernel { | |||
| 20 | 22 | ||
| 21 | class KPageLinkedList; | 23 | class KPageLinkedList; |
| 22 | 24 | ||
| 23 | class KMemoryManager final : NonCopyable { | 25 | class KMemoryManager final { |
| 24 | public: | 26 | public: |
| 27 | YUZU_NON_COPYABLE(KMemoryManager); | ||
| 28 | YUZU_NON_MOVEABLE(KMemoryManager); | ||
| 29 | |||
| 25 | enum class Pool : u32 { | 30 | enum class Pool : u32 { |
| 26 | Application = 0, | 31 | Application = 0, |
| 27 | Applet = 1, | 32 | Applet = 1, |
| @@ -48,22 +53,33 @@ public: | |||
| 48 | 53 | ||
| 49 | explicit KMemoryManager(Core::System& system_); | 54 | explicit KMemoryManager(Core::System& system_); |
| 50 | 55 | ||
| 51 | constexpr std::size_t GetSize(Pool pool) const { | 56 | void Initialize(VAddr management_region, size_t management_region_size); |
| 52 | return managers[static_cast<std::size_t>(pool)].GetSize(); | 57 | |
| 58 | constexpr size_t GetSize(Pool pool) const { | ||
| 59 | constexpr Direction GetSizeDirection = Direction::FromFront; | ||
| 60 | size_t total = 0; | ||
| 61 | for (auto* manager = this->GetFirstManager(pool, GetSizeDirection); manager != nullptr; | ||
| 62 | manager = this->GetNextManager(manager, GetSizeDirection)) { | ||
| 63 | total += manager->GetSize(); | ||
| 64 | } | ||
| 65 | return total; | ||
| 53 | } | 66 | } |
| 54 | 67 | ||
| 55 | void InitializeManager(Pool pool, u64 start_address, u64 end_address); | 68 | PAddr AllocateAndOpenContinuous(size_t num_pages, size_t align_pages, u32 option); |
| 69 | ResultCode AllocateAndOpen(KPageLinkedList* out, size_t num_pages, u32 option); | ||
| 70 | ResultCode AllocateAndOpenForProcess(KPageLinkedList* out, size_t num_pages, u32 option, | ||
| 71 | u64 process_id, u8 fill_pattern); | ||
| 72 | |||
| 73 | static constexpr size_t MaxManagerCount = 10; | ||
| 56 | 74 | ||
| 57 | VAddr AllocateAndOpenContinuous(size_t num_pages, size_t align_pages, u32 option); | 75 | void Close(PAddr address, size_t num_pages); |
| 58 | ResultCode Allocate(KPageLinkedList& page_list, std::size_t num_pages, Pool pool, Direction dir, | 76 | void Close(const KPageLinkedList& pg); |
| 59 | u32 heap_fill_value = 0); | ||
| 60 | ResultCode Free(KPageLinkedList& page_list, std::size_t num_pages, Pool pool, Direction dir, | ||
| 61 | u32 heap_fill_value = 0); | ||
| 62 | 77 | ||
| 63 | static constexpr std::size_t MaxManagerCount = 10; | 78 | void Open(PAddr address, size_t num_pages); |
| 79 | void Open(const KPageLinkedList& pg); | ||
| 64 | 80 | ||
| 65 | public: | 81 | public: |
| 66 | static std::size_t CalculateManagementOverheadSize(std::size_t region_size) { | 82 | static size_t CalculateManagementOverheadSize(size_t region_size) { |
| 67 | return Impl::CalculateManagementOverheadSize(region_size); | 83 | return Impl::CalculateManagementOverheadSize(region_size); |
| 68 | } | 84 | } |
| 69 | 85 | ||
| @@ -88,38 +104,34 @@ public: | |||
| 88 | } | 104 | } |
| 89 | 105 | ||
| 90 | private: | 106 | private: |
| 91 | class Impl final : NonCopyable { | 107 | class Impl final { |
| 92 | private: | ||
| 93 | using RefCount = u16; | ||
| 94 | |||
| 95 | private: | ||
| 96 | KPageHeap heap; | ||
| 97 | Pool pool{}; | ||
| 98 | |||
| 99 | public: | 108 | public: |
| 100 | static std::size_t CalculateManagementOverheadSize(std::size_t region_size); | 109 | YUZU_NON_COPYABLE(Impl); |
| 101 | 110 | YUZU_NON_MOVEABLE(Impl); | |
| 102 | static constexpr std::size_t CalculateOptimizedProcessOverheadSize( | ||
| 103 | std::size_t region_size) { | ||
| 104 | return (Common::AlignUp((region_size / PageSize), Common::BitSize<u64>()) / | ||
| 105 | Common::BitSize<u64>()) * | ||
| 106 | sizeof(u64); | ||
| 107 | } | ||
| 108 | 111 | ||
| 109 | public: | ||
| 110 | Impl() = default; | 112 | Impl() = default; |
| 113 | ~Impl() = default; | ||
| 111 | 114 | ||
| 112 | std::size_t Initialize(Pool new_pool, u64 start_address, u64 end_address); | 115 | size_t Initialize(PAddr address, size_t size, VAddr management, VAddr management_end, |
| 116 | Pool p); | ||
| 113 | 117 | ||
| 114 | VAddr AllocateBlock(s32 index, bool random) { | 118 | VAddr AllocateBlock(s32 index, bool random) { |
| 115 | return heap.AllocateBlock(index, random); | 119 | return heap.AllocateBlock(index, random); |
| 116 | } | 120 | } |
| 117 | 121 | ||
| 118 | void Free(VAddr addr, std::size_t num_pages) { | 122 | void Free(VAddr addr, size_t num_pages) { |
| 119 | heap.Free(addr, num_pages); | 123 | heap.Free(addr, num_pages); |
| 120 | } | 124 | } |
| 121 | 125 | ||
| 122 | constexpr std::size_t GetSize() const { | 126 | void SetInitialUsedHeapSize(size_t reserved_size) { |
| 127 | heap.SetInitialUsedSize(reserved_size); | ||
| 128 | } | ||
| 129 | |||
| 130 | constexpr Pool GetPool() const { | ||
| 131 | return pool; | ||
| 132 | } | ||
| 133 | |||
| 134 | constexpr size_t GetSize() const { | ||
| 123 | return heap.GetSize(); | 135 | return heap.GetSize(); |
| 124 | } | 136 | } |
| 125 | 137 | ||
| @@ -130,12 +142,137 @@ private: | |||
| 130 | constexpr VAddr GetEndAddress() const { | 142 | constexpr VAddr GetEndAddress() const { |
| 131 | return heap.GetEndAddress(); | 143 | return heap.GetEndAddress(); |
| 132 | } | 144 | } |
| 145 | |||
| 146 | constexpr size_t GetPageOffset(PAddr address) const { | ||
| 147 | return heap.GetPageOffset(address); | ||
| 148 | } | ||
| 149 | |||
| 150 | constexpr size_t GetPageOffsetToEnd(PAddr address) const { | ||
| 151 | return heap.GetPageOffsetToEnd(address); | ||
| 152 | } | ||
| 153 | |||
| 154 | constexpr void SetNext(Impl* n) { | ||
| 155 | next = n; | ||
| 156 | } | ||
| 157 | |||
| 158 | constexpr void SetPrev(Impl* n) { | ||
| 159 | prev = n; | ||
| 160 | } | ||
| 161 | |||
| 162 | constexpr Impl* GetNext() const { | ||
| 163 | return next; | ||
| 164 | } | ||
| 165 | |||
| 166 | constexpr Impl* GetPrev() const { | ||
| 167 | return prev; | ||
| 168 | } | ||
| 169 | |||
| 170 | void OpenFirst(PAddr address, size_t num_pages) { | ||
| 171 | size_t index = this->GetPageOffset(address); | ||
| 172 | const size_t end = index + num_pages; | ||
| 173 | while (index < end) { | ||
| 174 | const RefCount ref_count = (++page_reference_counts[index]); | ||
| 175 | ASSERT(ref_count == 1); | ||
| 176 | |||
| 177 | index++; | ||
| 178 | } | ||
| 179 | } | ||
| 180 | |||
| 181 | void Open(PAddr address, size_t num_pages) { | ||
| 182 | size_t index = this->GetPageOffset(address); | ||
| 183 | const size_t end = index + num_pages; | ||
| 184 | while (index < end) { | ||
| 185 | const RefCount ref_count = (++page_reference_counts[index]); | ||
| 186 | ASSERT(ref_count > 1); | ||
| 187 | |||
| 188 | index++; | ||
| 189 | } | ||
| 190 | } | ||
| 191 | |||
| 192 | void Close(PAddr address, size_t num_pages) { | ||
| 193 | size_t index = this->GetPageOffset(address); | ||
| 194 | const size_t end = index + num_pages; | ||
| 195 | |||
| 196 | size_t free_start = 0; | ||
| 197 | size_t free_count = 0; | ||
| 198 | while (index < end) { | ||
| 199 | ASSERT(page_reference_counts[index] > 0); | ||
| 200 | const RefCount ref_count = (--page_reference_counts[index]); | ||
| 201 | |||
| 202 | // Keep track of how many zero refcounts we see in a row, to minimize calls to free. | ||
| 203 | if (ref_count == 0) { | ||
| 204 | if (free_count > 0) { | ||
| 205 | free_count++; | ||
| 206 | } else { | ||
| 207 | free_start = index; | ||
| 208 | free_count = 1; | ||
| 209 | } | ||
| 210 | } else { | ||
| 211 | if (free_count > 0) { | ||
| 212 | this->Free(heap.GetAddress() + free_start * PageSize, free_count); | ||
| 213 | free_count = 0; | ||
| 214 | } | ||
| 215 | } | ||
| 216 | |||
| 217 | index++; | ||
| 218 | } | ||
| 219 | |||
| 220 | if (free_count > 0) { | ||
| 221 | this->Free(heap.GetAddress() + free_start * PageSize, free_count); | ||
| 222 | } | ||
| 223 | } | ||
| 224 | |||
| 225 | static size_t CalculateManagementOverheadSize(size_t region_size); | ||
| 226 | |||
| 227 | static constexpr size_t CalculateOptimizedProcessOverheadSize(size_t region_size) { | ||
| 228 | return (Common::AlignUp((region_size / PageSize), Common::BitSize<u64>()) / | ||
| 229 | Common::BitSize<u64>()) * | ||
| 230 | sizeof(u64); | ||
| 231 | } | ||
| 232 | |||
| 233 | private: | ||
| 234 | using RefCount = u16; | ||
| 235 | |||
| 236 | KPageHeap heap; | ||
| 237 | std::vector<RefCount> page_reference_counts; | ||
| 238 | VAddr management_region{}; | ||
| 239 | Pool pool{}; | ||
| 240 | Impl* next{}; | ||
| 241 | Impl* prev{}; | ||
| 133 | }; | 242 | }; |
| 134 | 243 | ||
| 135 | private: | 244 | private: |
| 245 | Impl& GetManager(const KMemoryLayout& memory_layout, PAddr address) { | ||
| 246 | return managers[memory_layout.GetPhysicalLinearRegion(address).GetAttributes()]; | ||
| 247 | } | ||
| 248 | |||
| 249 | const Impl& GetManager(const KMemoryLayout& memory_layout, PAddr address) const { | ||
| 250 | return managers[memory_layout.GetPhysicalLinearRegion(address).GetAttributes()]; | ||
| 251 | } | ||
| 252 | |||
| 253 | constexpr Impl* GetFirstManager(Pool pool, Direction dir) const { | ||
| 254 | return dir == Direction::FromBack ? pool_managers_tail[static_cast<size_t>(pool)] | ||
| 255 | : pool_managers_head[static_cast<size_t>(pool)]; | ||
| 256 | } | ||
| 257 | |||
| 258 | constexpr Impl* GetNextManager(Impl* cur, Direction dir) const { | ||
| 259 | if (dir == Direction::FromBack) { | ||
| 260 | return cur->GetPrev(); | ||
| 261 | } else { | ||
| 262 | return cur->GetNext(); | ||
| 263 | } | ||
| 264 | } | ||
| 265 | |||
| 266 | ResultCode AllocatePageGroupImpl(KPageLinkedList* out, size_t num_pages, Pool pool, | ||
| 267 | Direction dir, bool random); | ||
| 268 | |||
| 269 | private: | ||
| 136 | Core::System& system; | 270 | Core::System& system; |
| 137 | std::array<std::mutex, static_cast<std::size_t>(Pool::Count)> pool_locks; | 271 | std::array<KLightLock, static_cast<size_t>(Pool::Count)> pool_locks; |
| 272 | std::array<Impl*, MaxManagerCount> pool_managers_head{}; | ||
| 273 | std::array<Impl*, MaxManagerCount> pool_managers_tail{}; | ||
| 138 | std::array<Impl, MaxManagerCount> managers; | 274 | std::array<Impl, MaxManagerCount> managers; |
| 275 | size_t num_managers{}; | ||
| 139 | }; | 276 | }; |
| 140 | 277 | ||
| 141 | } // namespace Kernel | 278 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/k_memory_region.h b/src/core/hle/kernel/k_memory_region.h index 90ab8fd62..e9bdf4e59 100644 --- a/src/core/hle/kernel/k_memory_region.h +++ b/src/core/hle/kernel/k_memory_region.h | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include "common/assert.h" | 7 | #include "common/assert.h" |
| 8 | #include "common/common_funcs.h" | ||
| 8 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 9 | #include "common/intrusive_red_black_tree.h" | 10 | #include "common/intrusive_red_black_tree.h" |
| 10 | #include "core/hle/kernel/k_memory_region_type.h" | 11 | #include "core/hle/kernel/k_memory_region_type.h" |
| @@ -13,11 +14,13 @@ namespace Kernel { | |||
| 13 | 14 | ||
| 14 | class KMemoryRegionAllocator; | 15 | class KMemoryRegionAllocator; |
| 15 | 16 | ||
| 16 | class KMemoryRegion final : public Common::IntrusiveRedBlackTreeBaseNode<KMemoryRegion>, | 17 | class KMemoryRegion final : public Common::IntrusiveRedBlackTreeBaseNode<KMemoryRegion> { |
| 17 | NonCopyable { | ||
| 18 | friend class KMemoryRegionTree; | 18 | friend class KMemoryRegionTree; |
| 19 | 19 | ||
| 20 | public: | 20 | public: |
| 21 | YUZU_NON_COPYABLE(KMemoryRegion); | ||
| 22 | YUZU_NON_MOVEABLE(KMemoryRegion); | ||
| 23 | |||
| 21 | constexpr KMemoryRegion() = default; | 24 | constexpr KMemoryRegion() = default; |
| 22 | constexpr KMemoryRegion(u64 address_, u64 last_address_) | 25 | constexpr KMemoryRegion(u64 address_, u64 last_address_) |
| 23 | : address{address_}, last_address{last_address_} {} | 26 | : address{address_}, last_address{last_address_} {} |
| @@ -29,6 +32,8 @@ public: | |||
| 29 | : KMemoryRegion(address_, last_address_, std::numeric_limits<u64>::max(), attributes_, | 32 | : KMemoryRegion(address_, last_address_, std::numeric_limits<u64>::max(), attributes_, |
| 30 | type_id_) {} | 33 | type_id_) {} |
| 31 | 34 | ||
| 35 | ~KMemoryRegion() = default; | ||
| 36 | |||
| 32 | static constexpr int Compare(const KMemoryRegion& lhs, const KMemoryRegion& rhs) { | 37 | static constexpr int Compare(const KMemoryRegion& lhs, const KMemoryRegion& rhs) { |
| 33 | if (lhs.GetAddress() < rhs.GetAddress()) { | 38 | if (lhs.GetAddress() < rhs.GetAddress()) { |
| 34 | return -1; | 39 | return -1; |
| @@ -39,16 +44,6 @@ public: | |||
| 39 | } | 44 | } |
| 40 | } | 45 | } |
| 41 | 46 | ||
| 42 | private: | ||
| 43 | constexpr void Reset(u64 a, u64 la, u64 p, u32 r, u32 t) { | ||
| 44 | address = a; | ||
| 45 | pair_address = p; | ||
| 46 | last_address = la; | ||
| 47 | attributes = r; | ||
| 48 | type_id = t; | ||
| 49 | } | ||
| 50 | |||
| 51 | public: | ||
| 52 | constexpr u64 GetAddress() const { | 47 | constexpr u64 GetAddress() const { |
| 53 | return address; | 48 | return address; |
| 54 | } | 49 | } |
| @@ -108,6 +103,14 @@ public: | |||
| 108 | } | 103 | } |
| 109 | 104 | ||
| 110 | private: | 105 | private: |
| 106 | constexpr void Reset(u64 a, u64 la, u64 p, u32 r, u32 t) { | ||
| 107 | address = a; | ||
| 108 | pair_address = p; | ||
| 109 | last_address = la; | ||
| 110 | attributes = r; | ||
| 111 | type_id = t; | ||
| 112 | } | ||
| 113 | |||
| 111 | u64 address{}; | 114 | u64 address{}; |
| 112 | u64 last_address{}; | 115 | u64 last_address{}; |
| 113 | u64 pair_address{}; | 116 | u64 pair_address{}; |
| @@ -115,8 +118,25 @@ private: | |||
| 115 | u32 type_id{}; | 118 | u32 type_id{}; |
| 116 | }; | 119 | }; |
| 117 | 120 | ||
| 118 | class KMemoryRegionTree final : NonCopyable { | 121 | class KMemoryRegionTree final { |
| 122 | private: | ||
| 123 | using TreeType = | ||
| 124 | Common::IntrusiveRedBlackTreeBaseTraits<KMemoryRegion>::TreeType<KMemoryRegion>; | ||
| 125 | |||
| 119 | public: | 126 | public: |
| 127 | YUZU_NON_COPYABLE(KMemoryRegionTree); | ||
| 128 | YUZU_NON_MOVEABLE(KMemoryRegionTree); | ||
| 129 | |||
| 130 | using value_type = TreeType::value_type; | ||
| 131 | using size_type = TreeType::size_type; | ||
| 132 | using difference_type = TreeType::difference_type; | ||
| 133 | using pointer = TreeType::pointer; | ||
| 134 | using const_pointer = TreeType::const_pointer; | ||
| 135 | using reference = TreeType::reference; | ||
| 136 | using const_reference = TreeType::const_reference; | ||
| 137 | using iterator = TreeType::iterator; | ||
| 138 | using const_iterator = TreeType::const_iterator; | ||
| 139 | |||
| 120 | struct DerivedRegionExtents { | 140 | struct DerivedRegionExtents { |
| 121 | const KMemoryRegion* first_region{}; | 141 | const KMemoryRegion* first_region{}; |
| 122 | const KMemoryRegion* last_region{}; | 142 | const KMemoryRegion* last_region{}; |
| @@ -140,29 +160,9 @@ public: | |||
| 140 | } | 160 | } |
| 141 | }; | 161 | }; |
| 142 | 162 | ||
| 143 | private: | ||
| 144 | using TreeType = | ||
| 145 | Common::IntrusiveRedBlackTreeBaseTraits<KMemoryRegion>::TreeType<KMemoryRegion>; | ||
| 146 | |||
| 147 | public: | ||
| 148 | using value_type = TreeType::value_type; | ||
| 149 | using size_type = TreeType::size_type; | ||
| 150 | using difference_type = TreeType::difference_type; | ||
| 151 | using pointer = TreeType::pointer; | ||
| 152 | using const_pointer = TreeType::const_pointer; | ||
| 153 | using reference = TreeType::reference; | ||
| 154 | using const_reference = TreeType::const_reference; | ||
| 155 | using iterator = TreeType::iterator; | ||
| 156 | using const_iterator = TreeType::const_iterator; | ||
| 157 | |||
| 158 | private: | ||
| 159 | TreeType m_tree{}; | ||
| 160 | KMemoryRegionAllocator& memory_region_allocator; | ||
| 161 | |||
| 162 | public: | ||
| 163 | explicit KMemoryRegionTree(KMemoryRegionAllocator& memory_region_allocator_); | 163 | explicit KMemoryRegionTree(KMemoryRegionAllocator& memory_region_allocator_); |
| 164 | ~KMemoryRegionTree() = default; | ||
| 164 | 165 | ||
| 165 | public: | ||
| 166 | KMemoryRegion* FindModifiable(u64 address) { | 166 | KMemoryRegion* FindModifiable(u64 address) { |
| 167 | if (auto it = this->find(KMemoryRegion(address, address, 0, 0)); it != this->end()) { | 167 | if (auto it = this->find(KMemoryRegion(address, address, 0, 0)); it != this->end()) { |
| 168 | return std::addressof(*it); | 168 | return std::addressof(*it); |
| @@ -241,7 +241,6 @@ public: | |||
| 241 | return GetDerivedRegionExtents(static_cast<KMemoryRegionType>(type_id)); | 241 | return GetDerivedRegionExtents(static_cast<KMemoryRegionType>(type_id)); |
| 242 | } | 242 | } |
| 243 | 243 | ||
| 244 | public: | ||
| 245 | void InsertDirectly(u64 address, u64 last_address, u32 attr = 0, u32 type_id = 0); | 244 | void InsertDirectly(u64 address, u64 last_address, u32 attr = 0, u32 type_id = 0); |
| 246 | bool Insert(u64 address, size_t size, u32 type_id, u32 new_attr = 0, u32 old_attr = 0); | 245 | bool Insert(u64 address, size_t size, u32 type_id, u32 new_attr = 0, u32 old_attr = 0); |
| 247 | 246 | ||
| @@ -252,7 +251,6 @@ public: | |||
| 252 | return this->GetRandomAlignedRegion(size + 2 * guard_size, alignment, type_id) + guard_size; | 251 | return this->GetRandomAlignedRegion(size + 2 * guard_size, alignment, type_id) + guard_size; |
| 253 | } | 252 | } |
| 254 | 253 | ||
| 255 | public: | ||
| 256 | // Iterator accessors. | 254 | // Iterator accessors. |
| 257 | iterator begin() { | 255 | iterator begin() { |
| 258 | return m_tree.begin(); | 256 | return m_tree.begin(); |
| @@ -322,13 +320,21 @@ public: | |||
| 322 | iterator nfind(const_reference ref) const { | 320 | iterator nfind(const_reference ref) const { |
| 323 | return m_tree.nfind(ref); | 321 | return m_tree.nfind(ref); |
| 324 | } | 322 | } |
| 323 | |||
| 324 | private: | ||
| 325 | TreeType m_tree{}; | ||
| 326 | KMemoryRegionAllocator& memory_region_allocator; | ||
| 325 | }; | 327 | }; |
| 326 | 328 | ||
| 327 | class KMemoryRegionAllocator final : NonCopyable { | 329 | class KMemoryRegionAllocator final { |
| 328 | public: | 330 | public: |
| 331 | YUZU_NON_COPYABLE(KMemoryRegionAllocator); | ||
| 332 | YUZU_NON_MOVEABLE(KMemoryRegionAllocator); | ||
| 333 | |||
| 329 | static constexpr size_t MaxMemoryRegions = 200; | 334 | static constexpr size_t MaxMemoryRegions = 200; |
| 330 | 335 | ||
| 331 | constexpr KMemoryRegionAllocator() = default; | 336 | constexpr KMemoryRegionAllocator() = default; |
| 337 | constexpr ~KMemoryRegionAllocator() = default; | ||
| 332 | 338 | ||
| 333 | template <typename... Args> | 339 | template <typename... Args> |
| 334 | KMemoryRegion* Allocate(Args&&... args) { | 340 | KMemoryRegion* Allocate(Args&&... args) { |
diff --git a/src/core/hle/kernel/k_memory_region_type.h b/src/core/hle/kernel/k_memory_region_type.h index a05e66677..0baeddf51 100644 --- a/src/core/hle/kernel/k_memory_region_type.h +++ b/src/core/hle/kernel/k_memory_region_type.h | |||
| @@ -14,7 +14,8 @@ | |||
| 14 | namespace Kernel { | 14 | namespace Kernel { |
| 15 | 15 | ||
| 16 | enum KMemoryRegionType : u32 { | 16 | enum KMemoryRegionType : u32 { |
| 17 | KMemoryRegionAttr_CarveoutProtected = 0x04000000, | 17 | KMemoryRegionAttr_CarveoutProtected = 0x02000000, |
| 18 | KMemoryRegionAttr_Uncached = 0x04000000, | ||
| 18 | KMemoryRegionAttr_DidKernelMap = 0x08000000, | 19 | KMemoryRegionAttr_DidKernelMap = 0x08000000, |
| 19 | KMemoryRegionAttr_ShouldKernelMap = 0x10000000, | 20 | KMemoryRegionAttr_ShouldKernelMap = 0x10000000, |
| 20 | KMemoryRegionAttr_UserReadOnly = 0x20000000, | 21 | KMemoryRegionAttr_UserReadOnly = 0x20000000, |
| @@ -239,6 +240,11 @@ static_assert(KMemoryRegionType_VirtualDramHeapBase.GetValue() == 0x1A); | |||
| 239 | static_assert(KMemoryRegionType_VirtualDramKernelPtHeap.GetValue() == 0x2A); | 240 | static_assert(KMemoryRegionType_VirtualDramKernelPtHeap.GetValue() == 0x2A); |
| 240 | static_assert(KMemoryRegionType_VirtualDramKernelTraceBuffer.GetValue() == 0x4A); | 241 | static_assert(KMemoryRegionType_VirtualDramKernelTraceBuffer.GetValue() == 0x4A); |
| 241 | 242 | ||
| 243 | // UNUSED: .DeriveSparse(2, 2, 0); | ||
| 244 | constexpr auto KMemoryRegionType_VirtualDramUnknownDebug = | ||
| 245 | KMemoryRegionType_Dram.DeriveSparse(2, 2, 1); | ||
| 246 | static_assert(KMemoryRegionType_VirtualDramUnknownDebug.GetValue() == (0x52)); | ||
| 247 | |||
| 242 | constexpr auto KMemoryRegionType_VirtualDramKernelInitPt = | 248 | constexpr auto KMemoryRegionType_VirtualDramKernelInitPt = |
| 243 | KMemoryRegionType_VirtualDramHeapBase.Derive(3, 0); | 249 | KMemoryRegionType_VirtualDramHeapBase.Derive(3, 0); |
| 244 | constexpr auto KMemoryRegionType_VirtualDramPoolManagement = | 250 | constexpr auto KMemoryRegionType_VirtualDramPoolManagement = |
| @@ -330,6 +336,8 @@ constexpr KMemoryRegionType GetTypeForVirtualLinearMapping(u32 type_id) { | |||
| 330 | return KMemoryRegionType_VirtualDramKernelTraceBuffer; | 336 | return KMemoryRegionType_VirtualDramKernelTraceBuffer; |
| 331 | } else if (KMemoryRegionType_DramKernelPtHeap.IsAncestorOf(type_id)) { | 337 | } else if (KMemoryRegionType_DramKernelPtHeap.IsAncestorOf(type_id)) { |
| 332 | return KMemoryRegionType_VirtualDramKernelPtHeap; | 338 | return KMemoryRegionType_VirtualDramKernelPtHeap; |
| 339 | } else if ((type_id | KMemoryRegionAttr_ShouldKernelMap) == type_id) { | ||
| 340 | return KMemoryRegionType_VirtualDramUnknownDebug; | ||
| 333 | } else { | 341 | } else { |
| 334 | return KMemoryRegionType_Dram; | 342 | return KMemoryRegionType_Dram; |
| 335 | } | 343 | } |
diff --git a/src/core/hle/kernel/k_page_buffer.cpp b/src/core/hle/kernel/k_page_buffer.cpp new file mode 100644 index 000000000..f7df4a9a8 --- /dev/null +++ b/src/core/hle/kernel/k_page_buffer.cpp | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | // Copyright 2022 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "common/alignment.h" | ||
| 6 | #include "common/assert.h" | ||
| 7 | #include "core/core.h" | ||
| 8 | #include "core/device_memory.h" | ||
| 9 | #include "core/hle/kernel/k_page_buffer.h" | ||
| 10 | #include "core/hle/kernel/memory_types.h" | ||
| 11 | |||
| 12 | namespace Kernel { | ||
| 13 | |||
| 14 | KPageBuffer* KPageBuffer::FromPhysicalAddress(Core::System& system, PAddr phys_addr) { | ||
| 15 | ASSERT(Common::IsAligned(phys_addr, PageSize)); | ||
| 16 | return reinterpret_cast<KPageBuffer*>(system.DeviceMemory().GetPointer(phys_addr)); | ||
| 17 | } | ||
| 18 | |||
| 19 | } // namespace Kernel | ||
diff --git a/src/core/hle/kernel/k_page_buffer.h b/src/core/hle/kernel/k_page_buffer.h new file mode 100644 index 000000000..6ff3c1568 --- /dev/null +++ b/src/core/hle/kernel/k_page_buffer.h | |||
| @@ -0,0 +1,29 @@ | |||
| 1 | // Copyright 2022 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <array> | ||
| 8 | |||
| 9 | #include "common/common_types.h" | ||
| 10 | #include "core/device_memory.h" | ||
| 11 | #include "core/hle/kernel/memory_types.h" | ||
| 12 | #include "core/hle/kernel/slab_helpers.h" | ||
| 13 | |||
| 14 | namespace Kernel { | ||
| 15 | |||
| 16 | class KPageBuffer final : public KSlabAllocated<KPageBuffer> { | ||
| 17 | public: | ||
| 18 | KPageBuffer() = default; | ||
| 19 | |||
| 20 | static KPageBuffer* FromPhysicalAddress(Core::System& system, PAddr phys_addr); | ||
| 21 | |||
| 22 | private: | ||
| 23 | [[maybe_unused]] alignas(PageSize) std::array<u8, PageSize> m_buffer{}; | ||
| 24 | }; | ||
| 25 | |||
| 26 | static_assert(sizeof(KPageBuffer) == PageSize); | ||
| 27 | static_assert(alignof(KPageBuffer) == PageSize); | ||
| 28 | |||
| 29 | } // namespace Kernel | ||
diff --git a/src/core/hle/kernel/k_page_heap.cpp b/src/core/hle/kernel/k_page_heap.cpp index 29d996d62..97a5890a0 100644 --- a/src/core/hle/kernel/k_page_heap.cpp +++ b/src/core/hle/kernel/k_page_heap.cpp | |||
| @@ -7,35 +7,51 @@ | |||
| 7 | 7 | ||
| 8 | namespace Kernel { | 8 | namespace Kernel { |
| 9 | 9 | ||
| 10 | void KPageHeap::Initialize(VAddr address, std::size_t size, std::size_t metadata_size) { | 10 | void KPageHeap::Initialize(PAddr address, size_t size, VAddr management_address, |
| 11 | // Check our assumptions | 11 | size_t management_size, const size_t* block_shifts, |
| 12 | ASSERT(Common::IsAligned((address), PageSize)); | 12 | size_t num_block_shifts) { |
| 13 | // Check our assumptions. | ||
| 14 | ASSERT(Common::IsAligned(address, PageSize)); | ||
| 13 | ASSERT(Common::IsAligned(size, PageSize)); | 15 | ASSERT(Common::IsAligned(size, PageSize)); |
| 16 | ASSERT(0 < num_block_shifts && num_block_shifts <= NumMemoryBlockPageShifts); | ||
| 17 | const VAddr management_end = management_address + management_size; | ||
| 14 | 18 | ||
| 15 | // Set our members | 19 | // Set our members. |
| 16 | heap_address = address; | 20 | m_heap_address = address; |
| 17 | heap_size = size; | 21 | m_heap_size = size; |
| 18 | 22 | m_num_blocks = num_block_shifts; | |
| 19 | // Setup bitmaps | 23 | |
| 20 | metadata.resize(metadata_size / sizeof(u64)); | 24 | // Setup bitmaps. |
| 21 | u64* cur_bitmap_storage{metadata.data()}; | 25 | m_management_data.resize(management_size / sizeof(u64)); |
| 22 | for (std::size_t i = 0; i < MemoryBlockPageShifts.size(); i++) { | 26 | u64* cur_bitmap_storage{m_management_data.data()}; |
| 23 | const std::size_t cur_block_shift{MemoryBlockPageShifts[i]}; | 27 | for (size_t i = 0; i < num_block_shifts; i++) { |
| 24 | const std::size_t next_block_shift{ | 28 | const size_t cur_block_shift = block_shifts[i]; |
| 25 | (i != MemoryBlockPageShifts.size() - 1) ? MemoryBlockPageShifts[i + 1] : 0}; | 29 | const size_t next_block_shift = (i != num_block_shifts - 1) ? block_shifts[i + 1] : 0; |
| 26 | cur_bitmap_storage = blocks[i].Initialize(heap_address, heap_size, cur_block_shift, | 30 | cur_bitmap_storage = m_blocks[i].Initialize(m_heap_address, m_heap_size, cur_block_shift, |
| 27 | next_block_shift, cur_bitmap_storage); | 31 | next_block_shift, cur_bitmap_storage); |
| 28 | } | 32 | } |
| 33 | |||
| 34 | // Ensure we didn't overextend our bounds. | ||
| 35 | ASSERT(VAddr(cur_bitmap_storage) <= management_end); | ||
| 36 | } | ||
| 37 | |||
| 38 | size_t KPageHeap::GetNumFreePages() const { | ||
| 39 | size_t num_free = 0; | ||
| 40 | |||
| 41 | for (size_t i = 0; i < m_num_blocks; i++) { | ||
| 42 | num_free += m_blocks[i].GetNumFreePages(); | ||
| 43 | } | ||
| 44 | |||
| 45 | return num_free; | ||
| 29 | } | 46 | } |
| 30 | 47 | ||
| 31 | VAddr KPageHeap::AllocateBlock(s32 index, bool random) { | 48 | PAddr KPageHeap::AllocateBlock(s32 index, bool random) { |
| 32 | const std::size_t needed_size{blocks[index].GetSize()}; | 49 | const size_t needed_size = m_blocks[index].GetSize(); |
| 33 | 50 | ||
| 34 | for (s32 i{index}; i < static_cast<s32>(MemoryBlockPageShifts.size()); i++) { | 51 | for (s32 i = index; i < static_cast<s32>(m_num_blocks); i++) { |
| 35 | if (const VAddr addr{blocks[i].PopBlock(random)}; addr) { | 52 | if (const PAddr addr = m_blocks[i].PopBlock(random); addr != 0) { |
| 36 | if (const std::size_t allocated_size{blocks[i].GetSize()}; | 53 | if (const size_t allocated_size = m_blocks[i].GetSize(); allocated_size > needed_size) { |
| 37 | allocated_size > needed_size) { | 54 | this->Free(addr + needed_size, (allocated_size - needed_size) / PageSize); |
| 38 | Free(addr + needed_size, (allocated_size - needed_size) / PageSize); | ||
| 39 | } | 55 | } |
| 40 | return addr; | 56 | return addr; |
| 41 | } | 57 | } |
| @@ -44,34 +60,34 @@ VAddr KPageHeap::AllocateBlock(s32 index, bool random) { | |||
| 44 | return 0; | 60 | return 0; |
| 45 | } | 61 | } |
| 46 | 62 | ||
| 47 | void KPageHeap::FreeBlock(VAddr block, s32 index) { | 63 | void KPageHeap::FreeBlock(PAddr block, s32 index) { |
| 48 | do { | 64 | do { |
| 49 | block = blocks[index++].PushBlock(block); | 65 | block = m_blocks[index++].PushBlock(block); |
| 50 | } while (block != 0); | 66 | } while (block != 0); |
| 51 | } | 67 | } |
| 52 | 68 | ||
| 53 | void KPageHeap::Free(VAddr addr, std::size_t num_pages) { | 69 | void KPageHeap::Free(PAddr addr, size_t num_pages) { |
| 54 | // Freeing no pages is a no-op | 70 | // Freeing no pages is a no-op. |
| 55 | if (num_pages == 0) { | 71 | if (num_pages == 0) { |
| 56 | return; | 72 | return; |
| 57 | } | 73 | } |
| 58 | 74 | ||
| 59 | // Find the largest block size that we can free, and free as many as possible | 75 | // Find the largest block size that we can free, and free as many as possible. |
| 60 | s32 big_index{static_cast<s32>(MemoryBlockPageShifts.size()) - 1}; | 76 | s32 big_index = static_cast<s32>(m_num_blocks) - 1; |
| 61 | const VAddr start{addr}; | 77 | const PAddr start = addr; |
| 62 | const VAddr end{(num_pages * PageSize) + addr}; | 78 | const PAddr end = addr + num_pages * PageSize; |
| 63 | VAddr before_start{start}; | 79 | PAddr before_start = start; |
| 64 | VAddr before_end{start}; | 80 | PAddr before_end = start; |
| 65 | VAddr after_start{end}; | 81 | PAddr after_start = end; |
| 66 | VAddr after_end{end}; | 82 | PAddr after_end = end; |
| 67 | while (big_index >= 0) { | 83 | while (big_index >= 0) { |
| 68 | const std::size_t block_size{blocks[big_index].GetSize()}; | 84 | const size_t block_size = m_blocks[big_index].GetSize(); |
| 69 | const VAddr big_start{Common::AlignUp((start), block_size)}; | 85 | const PAddr big_start = Common::AlignUp(start, block_size); |
| 70 | const VAddr big_end{Common::AlignDown((end), block_size)}; | 86 | const PAddr big_end = Common::AlignDown(end, block_size); |
| 71 | if (big_start < big_end) { | 87 | if (big_start < big_end) { |
| 72 | // Free as many big blocks as we can | 88 | // Free as many big blocks as we can. |
| 73 | for (auto block{big_start}; block < big_end; block += block_size) { | 89 | for (auto block = big_start; block < big_end; block += block_size) { |
| 74 | FreeBlock(block, big_index); | 90 | this->FreeBlock(block, big_index); |
| 75 | } | 91 | } |
| 76 | before_end = big_start; | 92 | before_end = big_start; |
| 77 | after_start = big_end; | 93 | after_start = big_end; |
| @@ -81,31 +97,31 @@ void KPageHeap::Free(VAddr addr, std::size_t num_pages) { | |||
| 81 | } | 97 | } |
| 82 | ASSERT(big_index >= 0); | 98 | ASSERT(big_index >= 0); |
| 83 | 99 | ||
| 84 | // Free space before the big blocks | 100 | // Free space before the big blocks. |
| 85 | for (s32 i{big_index - 1}; i >= 0; i--) { | 101 | for (s32 i = big_index - 1; i >= 0; i--) { |
| 86 | const std::size_t block_size{blocks[i].GetSize()}; | 102 | const size_t block_size = m_blocks[i].GetSize(); |
| 87 | while (before_start + block_size <= before_end) { | 103 | while (before_start + block_size <= before_end) { |
| 88 | before_end -= block_size; | 104 | before_end -= block_size; |
| 89 | FreeBlock(before_end, i); | 105 | this->FreeBlock(before_end, i); |
| 90 | } | 106 | } |
| 91 | } | 107 | } |
| 92 | 108 | ||
| 93 | // Free space after the big blocks | 109 | // Free space after the big blocks. |
| 94 | for (s32 i{big_index - 1}; i >= 0; i--) { | 110 | for (s32 i = big_index - 1; i >= 0; i--) { |
| 95 | const std::size_t block_size{blocks[i].GetSize()}; | 111 | const size_t block_size = m_blocks[i].GetSize(); |
| 96 | while (after_start + block_size <= after_end) { | 112 | while (after_start + block_size <= after_end) { |
| 97 | FreeBlock(after_start, i); | 113 | this->FreeBlock(after_start, i); |
| 98 | after_start += block_size; | 114 | after_start += block_size; |
| 99 | } | 115 | } |
| 100 | } | 116 | } |
| 101 | } | 117 | } |
| 102 | 118 | ||
| 103 | std::size_t KPageHeap::CalculateManagementOverheadSize(std::size_t region_size) { | 119 | size_t KPageHeap::CalculateManagementOverheadSize(size_t region_size, const size_t* block_shifts, |
| 104 | std::size_t overhead_size = 0; | 120 | size_t num_block_shifts) { |
| 105 | for (std::size_t i = 0; i < MemoryBlockPageShifts.size(); i++) { | 121 | size_t overhead_size = 0; |
| 106 | const std::size_t cur_block_shift{MemoryBlockPageShifts[i]}; | 122 | for (size_t i = 0; i < num_block_shifts; i++) { |
| 107 | const std::size_t next_block_shift{ | 123 | const size_t cur_block_shift = block_shifts[i]; |
| 108 | (i != MemoryBlockPageShifts.size() - 1) ? MemoryBlockPageShifts[i + 1] : 0}; | 124 | const size_t next_block_shift = (i != num_block_shifts - 1) ? block_shifts[i + 1] : 0; |
| 109 | overhead_size += KPageHeap::Block::CalculateManagementOverheadSize( | 125 | overhead_size += KPageHeap::Block::CalculateManagementOverheadSize( |
| 110 | region_size, cur_block_shift, next_block_shift); | 126 | region_size, cur_block_shift, next_block_shift); |
| 111 | } | 127 | } |
diff --git a/src/core/hle/kernel/k_page_heap.h b/src/core/hle/kernel/k_page_heap.h index 8d9f30523..60fff766b 100644 --- a/src/core/hle/kernel/k_page_heap.h +++ b/src/core/hle/kernel/k_page_heap.h | |||
| @@ -8,183 +8,210 @@ | |||
| 8 | #include <vector> | 8 | #include <vector> |
| 9 | 9 | ||
| 10 | #include "common/alignment.h" | 10 | #include "common/alignment.h" |
| 11 | #include "common/common_funcs.h" | ||
| 11 | #include "common/common_types.h" | 12 | #include "common/common_types.h" |
| 12 | #include "core/hle/kernel/k_page_bitmap.h" | 13 | #include "core/hle/kernel/k_page_bitmap.h" |
| 13 | #include "core/hle/kernel/memory_types.h" | 14 | #include "core/hle/kernel/memory_types.h" |
| 14 | 15 | ||
| 15 | namespace Kernel { | 16 | namespace Kernel { |
| 16 | 17 | ||
| 17 | class KPageHeap final : NonCopyable { | 18 | class KPageHeap final { |
| 18 | public: | 19 | public: |
| 19 | static constexpr s32 GetAlignedBlockIndex(std::size_t num_pages, std::size_t align_pages) { | 20 | YUZU_NON_COPYABLE(KPageHeap); |
| 20 | const auto target_pages{std::max(num_pages, align_pages)}; | 21 | YUZU_NON_MOVEABLE(KPageHeap); |
| 21 | for (std::size_t i = 0; i < NumMemoryBlockPageShifts; i++) { | 22 | |
| 22 | if (target_pages <= | 23 | KPageHeap() = default; |
| 23 | (static_cast<std::size_t>(1) << MemoryBlockPageShifts[i]) / PageSize) { | 24 | ~KPageHeap() = default; |
| 25 | |||
| 26 | constexpr PAddr GetAddress() const { | ||
| 27 | return m_heap_address; | ||
| 28 | } | ||
| 29 | constexpr size_t GetSize() const { | ||
| 30 | return m_heap_size; | ||
| 31 | } | ||
| 32 | constexpr PAddr GetEndAddress() const { | ||
| 33 | return this->GetAddress() + this->GetSize(); | ||
| 34 | } | ||
| 35 | constexpr size_t GetPageOffset(PAddr block) const { | ||
| 36 | return (block - this->GetAddress()) / PageSize; | ||
| 37 | } | ||
| 38 | constexpr size_t GetPageOffsetToEnd(PAddr block) const { | ||
| 39 | return (this->GetEndAddress() - block) / PageSize; | ||
| 40 | } | ||
| 41 | |||
| 42 | void Initialize(PAddr heap_address, size_t heap_size, VAddr management_address, | ||
| 43 | size_t management_size) { | ||
| 44 | return this->Initialize(heap_address, heap_size, management_address, management_size, | ||
| 45 | MemoryBlockPageShifts.data(), NumMemoryBlockPageShifts); | ||
| 46 | } | ||
| 47 | |||
| 48 | size_t GetFreeSize() const { | ||
| 49 | return this->GetNumFreePages() * PageSize; | ||
| 50 | } | ||
| 51 | |||
| 52 | void SetInitialUsedSize(size_t reserved_size) { | ||
| 53 | // Check that the reserved size is valid. | ||
| 54 | const size_t free_size = this->GetNumFreePages() * PageSize; | ||
| 55 | ASSERT(m_heap_size >= free_size + reserved_size); | ||
| 56 | |||
| 57 | // Set the initial used size. | ||
| 58 | m_initial_used_size = m_heap_size - free_size - reserved_size; | ||
| 59 | } | ||
| 60 | |||
| 61 | PAddr AllocateBlock(s32 index, bool random); | ||
| 62 | void Free(PAddr addr, size_t num_pages); | ||
| 63 | |||
| 64 | static size_t CalculateManagementOverheadSize(size_t region_size) { | ||
| 65 | return CalculateManagementOverheadSize(region_size, MemoryBlockPageShifts.data(), | ||
| 66 | NumMemoryBlockPageShifts); | ||
| 67 | } | ||
| 68 | |||
| 69 | static constexpr s32 GetAlignedBlockIndex(size_t num_pages, size_t align_pages) { | ||
| 70 | const size_t target_pages = std::max(num_pages, align_pages); | ||
| 71 | for (size_t i = 0; i < NumMemoryBlockPageShifts; i++) { | ||
| 72 | if (target_pages <= (size_t(1) << MemoryBlockPageShifts[i]) / PageSize) { | ||
| 24 | return static_cast<s32>(i); | 73 | return static_cast<s32>(i); |
| 25 | } | 74 | } |
| 26 | } | 75 | } |
| 27 | return -1; | 76 | return -1; |
| 28 | } | 77 | } |
| 29 | 78 | ||
| 30 | static constexpr s32 GetBlockIndex(std::size_t num_pages) { | 79 | static constexpr s32 GetBlockIndex(size_t num_pages) { |
| 31 | for (s32 i{static_cast<s32>(NumMemoryBlockPageShifts) - 1}; i >= 0; i--) { | 80 | for (s32 i = static_cast<s32>(NumMemoryBlockPageShifts) - 1; i >= 0; i--) { |
| 32 | if (num_pages >= (static_cast<std::size_t>(1) << MemoryBlockPageShifts[i]) / PageSize) { | 81 | if (num_pages >= (size_t(1) << MemoryBlockPageShifts[i]) / PageSize) { |
| 33 | return i; | 82 | return i; |
| 34 | } | 83 | } |
| 35 | } | 84 | } |
| 36 | return -1; | 85 | return -1; |
| 37 | } | 86 | } |
| 38 | 87 | ||
| 39 | static constexpr std::size_t GetBlockSize(std::size_t index) { | 88 | static constexpr size_t GetBlockSize(size_t index) { |
| 40 | return static_cast<std::size_t>(1) << MemoryBlockPageShifts[index]; | 89 | return size_t(1) << MemoryBlockPageShifts[index]; |
| 41 | } | 90 | } |
| 42 | 91 | ||
| 43 | static constexpr std::size_t GetBlockNumPages(std::size_t index) { | 92 | static constexpr size_t GetBlockNumPages(size_t index) { |
| 44 | return GetBlockSize(index) / PageSize; | 93 | return GetBlockSize(index) / PageSize; |
| 45 | } | 94 | } |
| 46 | 95 | ||
| 47 | private: | 96 | private: |
| 48 | static constexpr std::size_t NumMemoryBlockPageShifts{7}; | 97 | class Block final { |
| 49 | static constexpr std::array<std::size_t, NumMemoryBlockPageShifts> MemoryBlockPageShifts{ | ||
| 50 | 0xC, 0x10, 0x15, 0x16, 0x19, 0x1D, 0x1E, | ||
| 51 | }; | ||
| 52 | |||
| 53 | class Block final : NonCopyable { | ||
| 54 | private: | ||
| 55 | KPageBitmap bitmap; | ||
| 56 | VAddr heap_address{}; | ||
| 57 | uintptr_t end_offset{}; | ||
| 58 | std::size_t block_shift{}; | ||
| 59 | std::size_t next_block_shift{}; | ||
| 60 | |||
| 61 | public: | 98 | public: |
| 99 | YUZU_NON_COPYABLE(Block); | ||
| 100 | YUZU_NON_MOVEABLE(Block); | ||
| 101 | |||
| 62 | Block() = default; | 102 | Block() = default; |
| 103 | ~Block() = default; | ||
| 63 | 104 | ||
| 64 | constexpr std::size_t GetShift() const { | 105 | constexpr size_t GetShift() const { |
| 65 | return block_shift; | 106 | return m_block_shift; |
| 66 | } | 107 | } |
| 67 | constexpr std::size_t GetNextShift() const { | 108 | constexpr size_t GetNextShift() const { |
| 68 | return next_block_shift; | 109 | return m_next_block_shift; |
| 69 | } | 110 | } |
| 70 | constexpr std::size_t GetSize() const { | 111 | constexpr size_t GetSize() const { |
| 71 | return static_cast<std::size_t>(1) << GetShift(); | 112 | return u64(1) << this->GetShift(); |
| 72 | } | 113 | } |
| 73 | constexpr std::size_t GetNumPages() const { | 114 | constexpr size_t GetNumPages() const { |
| 74 | return GetSize() / PageSize; | 115 | return this->GetSize() / PageSize; |
| 75 | } | 116 | } |
| 76 | constexpr std::size_t GetNumFreeBlocks() const { | 117 | constexpr size_t GetNumFreeBlocks() const { |
| 77 | return bitmap.GetNumBits(); | 118 | return m_bitmap.GetNumBits(); |
| 78 | } | 119 | } |
| 79 | constexpr std::size_t GetNumFreePages() const { | 120 | constexpr size_t GetNumFreePages() const { |
| 80 | return GetNumFreeBlocks() * GetNumPages(); | 121 | return this->GetNumFreeBlocks() * this->GetNumPages(); |
| 81 | } | 122 | } |
| 82 | 123 | ||
| 83 | u64* Initialize(VAddr addr, std::size_t size, std::size_t bs, std::size_t nbs, | 124 | u64* Initialize(PAddr addr, size_t size, size_t bs, size_t nbs, u64* bit_storage) { |
| 84 | u64* bit_storage) { | 125 | // Set shifts. |
| 85 | // Set shifts | 126 | m_block_shift = bs; |
| 86 | block_shift = bs; | 127 | m_next_block_shift = nbs; |
| 87 | next_block_shift = nbs; | 128 | |
| 88 | 129 | // Align up the address. | |
| 89 | // Align up the address | 130 | PAddr end = addr + size; |
| 90 | VAddr end{addr + size}; | 131 | const size_t align = (m_next_block_shift != 0) ? (u64(1) << m_next_block_shift) |
| 91 | const auto align{(next_block_shift != 0) ? (1ULL << next_block_shift) | 132 | : (u64(1) << m_block_shift); |
| 92 | : (1ULL << block_shift)}; | 133 | addr = Common::AlignDown(addr, align); |
| 93 | addr = Common::AlignDown((addr), align); | 134 | end = Common::AlignUp(end, align); |
| 94 | end = Common::AlignUp((end), align); | 135 | |
| 95 | 136 | m_heap_address = addr; | |
| 96 | heap_address = addr; | 137 | m_end_offset = (end - addr) / (u64(1) << m_block_shift); |
| 97 | end_offset = (end - addr) / (1ULL << block_shift); | 138 | return m_bitmap.Initialize(bit_storage, m_end_offset); |
| 98 | return bitmap.Initialize(bit_storage, end_offset); | ||
| 99 | } | 139 | } |
| 100 | 140 | ||
| 101 | VAddr PushBlock(VAddr address) { | 141 | PAddr PushBlock(PAddr address) { |
| 102 | // Set the bit for the free block | 142 | // Set the bit for the free block. |
| 103 | std::size_t offset{(address - heap_address) >> GetShift()}; | 143 | size_t offset = (address - m_heap_address) >> this->GetShift(); |
| 104 | bitmap.SetBit(offset); | 144 | m_bitmap.SetBit(offset); |
| 105 | 145 | ||
| 106 | // If we have a next shift, try to clear the blocks below and return the address | 146 | // If we have a next shift, try to clear the blocks below this one and return the new |
| 107 | if (GetNextShift()) { | 147 | // address. |
| 108 | const auto diff{1ULL << (GetNextShift() - GetShift())}; | 148 | if (this->GetNextShift()) { |
| 149 | const size_t diff = u64(1) << (this->GetNextShift() - this->GetShift()); | ||
| 109 | offset = Common::AlignDown(offset, diff); | 150 | offset = Common::AlignDown(offset, diff); |
| 110 | if (bitmap.ClearRange(offset, diff)) { | 151 | if (m_bitmap.ClearRange(offset, diff)) { |
| 111 | return heap_address + (offset << GetShift()); | 152 | return m_heap_address + (offset << this->GetShift()); |
| 112 | } | 153 | } |
| 113 | } | 154 | } |
| 114 | 155 | ||
| 115 | // We couldn't coalesce, or we're already as big as possible | 156 | // We couldn't coalesce, or we're already as big as possible. |
| 116 | return 0; | 157 | return {}; |
| 117 | } | 158 | } |
| 118 | 159 | ||
| 119 | VAddr PopBlock(bool random) { | 160 | PAddr PopBlock(bool random) { |
| 120 | // Find a free block | 161 | // Find a free block. |
| 121 | const s64 soffset{bitmap.FindFreeBlock(random)}; | 162 | s64 soffset = m_bitmap.FindFreeBlock(random); |
| 122 | if (soffset < 0) { | 163 | if (soffset < 0) { |
| 123 | return 0; | 164 | return {}; |
| 124 | } | 165 | } |
| 125 | const auto offset{static_cast<std::size_t>(soffset)}; | 166 | const size_t offset = static_cast<size_t>(soffset); |
| 126 | 167 | ||
| 127 | // Update our tracking and return it | 168 | // Update our tracking and return it. |
| 128 | bitmap.ClearBit(offset); | 169 | m_bitmap.ClearBit(offset); |
| 129 | return heap_address + (offset << GetShift()); | 170 | return m_heap_address + (offset << this->GetShift()); |
| 130 | } | 171 | } |
| 131 | 172 | ||
| 132 | public: | 173 | public: |
| 133 | static constexpr std::size_t CalculateManagementOverheadSize(std::size_t region_size, | 174 | static constexpr size_t CalculateManagementOverheadSize(size_t region_size, |
| 134 | std::size_t cur_block_shift, | 175 | size_t cur_block_shift, |
| 135 | std::size_t next_block_shift) { | 176 | size_t next_block_shift) { |
| 136 | const auto cur_block_size{(1ULL << cur_block_shift)}; | 177 | const size_t cur_block_size = (u64(1) << cur_block_shift); |
| 137 | const auto next_block_size{(1ULL << next_block_shift)}; | 178 | const size_t next_block_size = (u64(1) << next_block_shift); |
| 138 | const auto align{(next_block_shift != 0) ? next_block_size : cur_block_size}; | 179 | const size_t align = (next_block_shift != 0) ? next_block_size : cur_block_size; |
| 139 | return KPageBitmap::CalculateManagementOverheadSize( | 180 | return KPageBitmap::CalculateManagementOverheadSize( |
| 140 | (align * 2 + Common::AlignUp(region_size, align)) / cur_block_size); | 181 | (align * 2 + Common::AlignUp(region_size, align)) / cur_block_size); |
| 141 | } | 182 | } |
| 142 | }; | ||
| 143 | |||
| 144 | public: | ||
| 145 | KPageHeap() = default; | ||
| 146 | |||
| 147 | constexpr VAddr GetAddress() const { | ||
| 148 | return heap_address; | ||
| 149 | } | ||
| 150 | constexpr std::size_t GetSize() const { | ||
| 151 | return heap_size; | ||
| 152 | } | ||
| 153 | constexpr VAddr GetEndAddress() const { | ||
| 154 | return GetAddress() + GetSize(); | ||
| 155 | } | ||
| 156 | constexpr std::size_t GetPageOffset(VAddr block) const { | ||
| 157 | return (block - GetAddress()) / PageSize; | ||
| 158 | } | ||
| 159 | 183 | ||
| 160 | void Initialize(VAddr heap_address, std::size_t heap_size, std::size_t metadata_size); | 184 | private: |
| 161 | VAddr AllocateBlock(s32 index, bool random); | 185 | KPageBitmap m_bitmap; |
| 162 | void Free(VAddr addr, std::size_t num_pages); | 186 | PAddr m_heap_address{}; |
| 163 | 187 | uintptr_t m_end_offset{}; | |
| 164 | void UpdateUsedSize() { | 188 | size_t m_block_shift{}; |
| 165 | used_size = heap_size - (GetNumFreePages() * PageSize); | 189 | size_t m_next_block_shift{}; |
| 166 | } | 190 | }; |
| 167 | |||
| 168 | static std::size_t CalculateManagementOverheadSize(std::size_t region_size); | ||
| 169 | 191 | ||
| 170 | private: | 192 | private: |
| 171 | constexpr std::size_t GetNumFreePages() const { | 193 | void Initialize(PAddr heap_address, size_t heap_size, VAddr management_address, |
| 172 | std::size_t num_free{}; | 194 | size_t management_size, const size_t* block_shifts, size_t num_block_shifts); |
| 195 | size_t GetNumFreePages() const; | ||
| 173 | 196 | ||
| 174 | for (const auto& block : blocks) { | 197 | void FreeBlock(PAddr block, s32 index); |
| 175 | num_free += block.GetNumFreePages(); | ||
| 176 | } | ||
| 177 | 198 | ||
| 178 | return num_free; | 199 | static constexpr size_t NumMemoryBlockPageShifts{7}; |
| 179 | } | 200 | static constexpr std::array<size_t, NumMemoryBlockPageShifts> MemoryBlockPageShifts{ |
| 201 | 0xC, 0x10, 0x15, 0x16, 0x19, 0x1D, 0x1E, | ||
| 202 | }; | ||
| 180 | 203 | ||
| 181 | void FreeBlock(VAddr block, s32 index); | 204 | private: |
| 205 | static size_t CalculateManagementOverheadSize(size_t region_size, const size_t* block_shifts, | ||
| 206 | size_t num_block_shifts); | ||
| 182 | 207 | ||
| 183 | VAddr heap_address{}; | 208 | private: |
| 184 | std::size_t heap_size{}; | 209 | PAddr m_heap_address{}; |
| 185 | std::size_t used_size{}; | 210 | size_t m_heap_size{}; |
| 186 | std::array<Block, NumMemoryBlockPageShifts> blocks{}; | 211 | size_t m_initial_used_size{}; |
| 187 | std::vector<u64> metadata; | 212 | size_t m_num_blocks{}; |
| 213 | std::array<Block, NumMemoryBlockPageShifts> m_blocks{}; | ||
| 214 | std::vector<u64> m_management_data; | ||
| 188 | }; | 215 | }; |
| 189 | 216 | ||
| 190 | } // namespace Kernel | 217 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/k_page_table.cpp b/src/core/hle/kernel/k_page_table.cpp index 2ebbc0819..02d93b12e 100644 --- a/src/core/hle/kernel/k_page_table.cpp +++ b/src/core/hle/kernel/k_page_table.cpp | |||
| @@ -41,27 +41,12 @@ constexpr std::size_t GetAddressSpaceWidthFromType(FileSys::ProgramAddressSpaceT | |||
| 41 | } | 41 | } |
| 42 | } | 42 | } |
| 43 | 43 | ||
| 44 | constexpr u64 GetAddressInRange(const KMemoryInfo& info, VAddr addr) { | ||
| 45 | if (info.GetAddress() < addr) { | ||
| 46 | return addr; | ||
| 47 | } | ||
| 48 | return info.GetAddress(); | ||
| 49 | } | ||
| 50 | |||
| 51 | constexpr std::size_t GetSizeInRange(const KMemoryInfo& info, VAddr start, VAddr end) { | ||
| 52 | std::size_t size{info.GetSize()}; | ||
| 53 | if (info.GetAddress() < start) { | ||
| 54 | size -= start - info.GetAddress(); | ||
| 55 | } | ||
| 56 | if (info.GetEndAddress() > end) { | ||
| 57 | size -= info.GetEndAddress() - end; | ||
| 58 | } | ||
| 59 | return size; | ||
| 60 | } | ||
| 61 | |||
| 62 | } // namespace | 44 | } // namespace |
| 63 | 45 | ||
| 64 | KPageTable::KPageTable(Core::System& system_) : system{system_} {} | 46 | KPageTable::KPageTable(Core::System& system_) |
| 47 | : general_lock{system_.Kernel()}, map_physical_memory_lock{system_.Kernel()}, system{system_} {} | ||
| 48 | |||
| 49 | KPageTable::~KPageTable() = default; | ||
| 65 | 50 | ||
| 66 | ResultCode KPageTable::InitializeForProcess(FileSys::ProgramAddressSpaceType as_type, | 51 | ResultCode KPageTable::InitializeForProcess(FileSys::ProgramAddressSpaceType as_type, |
| 67 | bool enable_aslr, VAddr code_addr, | 52 | bool enable_aslr, VAddr code_addr, |
| @@ -282,96 +267,228 @@ ResultCode KPageTable::MapProcessCode(VAddr addr, std::size_t num_pages, KMemory | |||
| 282 | R_UNLESS(this->CanContain(addr, size, state), ResultInvalidCurrentMemory); | 267 | R_UNLESS(this->CanContain(addr, size, state), ResultInvalidCurrentMemory); |
| 283 | 268 | ||
| 284 | // Lock the table. | 269 | // Lock the table. |
| 285 | std::lock_guard lock{page_table_lock}; | 270 | KScopedLightLock lk(general_lock); |
| 286 | 271 | ||
| 287 | // Verify that the destination memory is unmapped. | 272 | // Verify that the destination memory is unmapped. |
| 288 | R_TRY(this->CheckMemoryState(addr, size, KMemoryState::All, KMemoryState::Free, | 273 | R_TRY(this->CheckMemoryState(addr, size, KMemoryState::All, KMemoryState::Free, |
| 289 | KMemoryPermission::None, KMemoryPermission::None, | 274 | KMemoryPermission::None, KMemoryPermission::None, |
| 290 | KMemoryAttribute::None, KMemoryAttribute::None)); | 275 | KMemoryAttribute::None, KMemoryAttribute::None)); |
| 276 | KPageLinkedList pg; | ||
| 277 | R_TRY(system.Kernel().MemoryManager().AllocateAndOpen( | ||
| 278 | &pg, num_pages, | ||
| 279 | KMemoryManager::EncodeOption(KMemoryManager::Pool::Application, allocation_option))); | ||
| 291 | 280 | ||
| 292 | KPageLinkedList page_linked_list; | 281 | R_TRY(Operate(addr, num_pages, pg, OperationType::MapGroup)); |
| 293 | R_TRY(system.Kernel().MemoryManager().Allocate(page_linked_list, num_pages, memory_pool, | ||
| 294 | allocation_option)); | ||
| 295 | R_TRY(Operate(addr, num_pages, page_linked_list, OperationType::MapGroup)); | ||
| 296 | 282 | ||
| 297 | block_manager->Update(addr, num_pages, state, perm); | 283 | block_manager->Update(addr, num_pages, state, perm); |
| 298 | 284 | ||
| 299 | return ResultSuccess; | 285 | return ResultSuccess; |
| 300 | } | 286 | } |
| 301 | 287 | ||
| 302 | ResultCode KPageTable::MapCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) { | 288 | ResultCode KPageTable::MapCodeMemory(VAddr dst_address, VAddr src_address, std::size_t size) { |
| 303 | std::lock_guard lock{page_table_lock}; | 289 | // Validate the mapping request. |
| 304 | 290 | R_UNLESS(this->CanContain(dst_address, size, KMemoryState::AliasCode), | |
| 305 | const std::size_t num_pages{size / PageSize}; | 291 | ResultInvalidMemoryRegion); |
| 306 | 292 | ||
| 307 | KMemoryState state{}; | 293 | // Lock the table. |
| 308 | KMemoryPermission perm{}; | 294 | KScopedLightLock lk(general_lock); |
| 309 | CASCADE_CODE(CheckMemoryState(&state, &perm, nullptr, nullptr, src_addr, size, | ||
| 310 | KMemoryState::All, KMemoryState::Normal, KMemoryPermission::All, | ||
| 311 | KMemoryPermission::UserReadWrite, KMemoryAttribute::Mask, | ||
| 312 | KMemoryAttribute::None, KMemoryAttribute::IpcAndDeviceMapped)); | ||
| 313 | 295 | ||
| 314 | if (IsRegionMapped(dst_addr, size)) { | 296 | // Verify that the source memory is normal heap. |
| 315 | return ResultInvalidCurrentMemory; | 297 | KMemoryState src_state{}; |
| 316 | } | 298 | KMemoryPermission src_perm{}; |
| 299 | std::size_t num_src_allocator_blocks{}; | ||
| 300 | R_TRY(this->CheckMemoryState(&src_state, &src_perm, nullptr, &num_src_allocator_blocks, | ||
| 301 | src_address, size, KMemoryState::All, KMemoryState::Normal, | ||
| 302 | KMemoryPermission::All, KMemoryPermission::UserReadWrite, | ||
| 303 | KMemoryAttribute::All, KMemoryAttribute::None)); | ||
| 317 | 304 | ||
| 318 | KPageLinkedList page_linked_list; | 305 | // Verify that the destination memory is unmapped. |
| 319 | AddRegionToPages(src_addr, num_pages, page_linked_list); | 306 | std::size_t num_dst_allocator_blocks{}; |
| 307 | R_TRY(this->CheckMemoryState(&num_dst_allocator_blocks, dst_address, size, KMemoryState::All, | ||
| 308 | KMemoryState::Free, KMemoryPermission::None, | ||
| 309 | KMemoryPermission::None, KMemoryAttribute::None, | ||
| 310 | KMemoryAttribute::None)); | ||
| 320 | 311 | ||
| 312 | // Map the code memory. | ||
| 321 | { | 313 | { |
| 322 | auto block_guard = detail::ScopeExit( | 314 | // Determine the number of pages being operated on. |
| 323 | [&] { Operate(src_addr, num_pages, perm, OperationType::ChangePermissions); }); | 315 | const std::size_t num_pages = size / PageSize; |
| 324 | 316 | ||
| 325 | CASCADE_CODE(Operate(src_addr, num_pages, KMemoryPermission::None, | 317 | // Create page groups for the memory being mapped. |
| 326 | OperationType::ChangePermissions)); | 318 | KPageLinkedList pg; |
| 327 | CASCADE_CODE(MapPages(dst_addr, page_linked_list, KMemoryPermission::None)); | 319 | AddRegionToPages(src_address, num_pages, pg); |
| 328 | 320 | ||
| 329 | block_guard.Cancel(); | 321 | // Reprotect the source as kernel-read/not mapped. |
| 330 | } | 322 | const auto new_perm = static_cast<KMemoryPermission>(KMemoryPermission::KernelRead | |
| 323 | KMemoryPermission::NotMapped); | ||
| 324 | R_TRY(Operate(src_address, num_pages, new_perm, OperationType::ChangePermissions)); | ||
| 331 | 325 | ||
| 332 | block_manager->Update(src_addr, num_pages, state, KMemoryPermission::None, | 326 | // Ensure that we unprotect the source pages on failure. |
| 333 | KMemoryAttribute::Locked); | 327 | auto unprot_guard = SCOPE_GUARD({ |
| 334 | block_manager->Update(dst_addr, num_pages, KMemoryState::AliasCode); | 328 | ASSERT(this->Operate(src_address, num_pages, src_perm, OperationType::ChangePermissions) |
| 329 | .IsSuccess()); | ||
| 330 | }); | ||
| 331 | |||
| 332 | // Map the alias pages. | ||
| 333 | R_TRY(MapPages(dst_address, pg, new_perm)); | ||
| 334 | |||
| 335 | // We successfully mapped the alias pages, so we don't need to unprotect the src pages on | ||
| 336 | // failure. | ||
| 337 | unprot_guard.Cancel(); | ||
| 338 | |||
| 339 | // Apply the memory block updates. | ||
| 340 | block_manager->Update(src_address, num_pages, src_state, new_perm, | ||
| 341 | KMemoryAttribute::Locked); | ||
| 342 | block_manager->Update(dst_address, num_pages, KMemoryState::AliasCode, new_perm, | ||
| 343 | KMemoryAttribute::None); | ||
| 344 | } | ||
| 335 | 345 | ||
| 336 | return ResultSuccess; | 346 | return ResultSuccess; |
| 337 | } | 347 | } |
| 338 | 348 | ||
| 339 | ResultCode KPageTable::UnmapCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) { | 349 | ResultCode KPageTable::UnmapCodeMemory(VAddr dst_address, VAddr src_address, std::size_t size) { |
| 340 | std::lock_guard lock{page_table_lock}; | 350 | // Validate the mapping request. |
| 351 | R_UNLESS(this->CanContain(dst_address, size, KMemoryState::AliasCode), | ||
| 352 | ResultInvalidMemoryRegion); | ||
| 353 | |||
| 354 | // Lock the table. | ||
| 355 | KScopedLightLock lk(general_lock); | ||
| 341 | 356 | ||
| 342 | if (!size) { | 357 | // Verify that the source memory is locked normal heap. |
| 343 | return ResultSuccess; | 358 | std::size_t num_src_allocator_blocks{}; |
| 359 | R_TRY(this->CheckMemoryState(std::addressof(num_src_allocator_blocks), src_address, size, | ||
| 360 | KMemoryState::All, KMemoryState::Normal, KMemoryPermission::None, | ||
| 361 | KMemoryPermission::None, KMemoryAttribute::All, | ||
| 362 | KMemoryAttribute::Locked)); | ||
| 363 | |||
| 364 | // Verify that the destination memory is aliasable code. | ||
| 365 | std::size_t num_dst_allocator_blocks{}; | ||
| 366 | R_TRY(this->CheckMemoryStateContiguous( | ||
| 367 | std::addressof(num_dst_allocator_blocks), dst_address, size, KMemoryState::FlagCanCodeAlias, | ||
| 368 | KMemoryState::FlagCanCodeAlias, KMemoryPermission::None, KMemoryPermission::None, | ||
| 369 | KMemoryAttribute::All, KMemoryAttribute::None)); | ||
| 370 | |||
| 371 | // Determine whether any pages being unmapped are code. | ||
| 372 | bool any_code_pages = false; | ||
| 373 | { | ||
| 374 | KMemoryBlockManager::const_iterator it = block_manager->FindIterator(dst_address); | ||
| 375 | while (true) { | ||
| 376 | // Get the memory info. | ||
| 377 | const KMemoryInfo info = it->GetMemoryInfo(); | ||
| 378 | |||
| 379 | // Check if the memory has code flag. | ||
| 380 | if ((info.GetState() & KMemoryState::FlagCode) != KMemoryState::None) { | ||
| 381 | any_code_pages = true; | ||
| 382 | break; | ||
| 383 | } | ||
| 384 | |||
| 385 | // Check if we're done. | ||
| 386 | if (dst_address + size - 1 <= info.GetLastAddress()) { | ||
| 387 | break; | ||
| 388 | } | ||
| 389 | |||
| 390 | // Advance. | ||
| 391 | ++it; | ||
| 392 | } | ||
| 344 | } | 393 | } |
| 345 | 394 | ||
| 346 | const std::size_t num_pages{size / PageSize}; | 395 | // Ensure that we maintain the instruction cache. |
| 396 | bool reprotected_pages = false; | ||
| 397 | SCOPE_EXIT({ | ||
| 398 | if (reprotected_pages && any_code_pages) { | ||
| 399 | system.InvalidateCpuInstructionCacheRange(dst_address, size); | ||
| 400 | } | ||
| 401 | }); | ||
| 347 | 402 | ||
| 348 | CASCADE_CODE(CheckMemoryState(nullptr, nullptr, nullptr, nullptr, src_addr, size, | 403 | // Unmap. |
| 349 | KMemoryState::All, KMemoryState::Normal, KMemoryPermission::None, | 404 | { |
| 350 | KMemoryPermission::None, KMemoryAttribute::Mask, | 405 | // Determine the number of pages being operated on. |
| 351 | KMemoryAttribute::Locked, KMemoryAttribute::IpcAndDeviceMapped)); | 406 | const std::size_t num_pages = size / PageSize; |
| 352 | 407 | ||
| 353 | KMemoryState state{}; | 408 | // Unmap the aliased copy of the pages. |
| 354 | CASCADE_CODE(CheckMemoryState( | 409 | R_TRY(Operate(dst_address, num_pages, KMemoryPermission::None, OperationType::Unmap)); |
| 355 | &state, nullptr, nullptr, nullptr, dst_addr, PageSize, KMemoryState::FlagCanCodeAlias, | ||
| 356 | KMemoryState::FlagCanCodeAlias, KMemoryPermission::None, KMemoryPermission::None, | ||
| 357 | KMemoryAttribute::Mask, KMemoryAttribute::None, KMemoryAttribute::IpcAndDeviceMapped)); | ||
| 358 | CASCADE_CODE(CheckMemoryState(dst_addr, size, KMemoryState::All, state, KMemoryPermission::None, | ||
| 359 | KMemoryPermission::None, KMemoryAttribute::Mask, | ||
| 360 | KMemoryAttribute::None)); | ||
| 361 | CASCADE_CODE(Operate(dst_addr, num_pages, KMemoryPermission::None, OperationType::Unmap)); | ||
| 362 | 410 | ||
| 363 | block_manager->Update(dst_addr, num_pages, KMemoryState::Free); | 411 | // Try to set the permissions for the source pages back to what they should be. |
| 364 | block_manager->Update(src_addr, num_pages, KMemoryState::Normal, | 412 | R_TRY(Operate(src_address, num_pages, KMemoryPermission::UserReadWrite, |
| 365 | KMemoryPermission::UserReadWrite); | 413 | OperationType::ChangePermissions)); |
| 366 | 414 | ||
| 367 | system.InvalidateCpuInstructionCacheRange(dst_addr, size); | 415 | // Apply the memory block updates. |
| 416 | block_manager->Update(dst_address, num_pages, KMemoryState::None); | ||
| 417 | block_manager->Update(src_address, num_pages, KMemoryState::Normal, | ||
| 418 | KMemoryPermission::UserReadWrite); | ||
| 419 | |||
| 420 | // Note that we reprotected pages. | ||
| 421 | reprotected_pages = true; | ||
| 422 | } | ||
| 368 | 423 | ||
| 369 | return ResultSuccess; | 424 | return ResultSuccess; |
| 370 | } | 425 | } |
| 371 | 426 | ||
| 427 | VAddr KPageTable::FindFreeArea(VAddr region_start, std::size_t region_num_pages, | ||
| 428 | std::size_t num_pages, std::size_t alignment, std::size_t offset, | ||
| 429 | std::size_t guard_pages) { | ||
| 430 | VAddr address = 0; | ||
| 431 | |||
| 432 | if (num_pages <= region_num_pages) { | ||
| 433 | if (this->IsAslrEnabled()) { | ||
| 434 | // Try to directly find a free area up to 8 times. | ||
| 435 | for (std::size_t i = 0; i < 8; i++) { | ||
| 436 | const std::size_t random_offset = | ||
| 437 | KSystemControl::GenerateRandomRange( | ||
| 438 | 0, (region_num_pages - num_pages - guard_pages) * PageSize / alignment) * | ||
| 439 | alignment; | ||
| 440 | const VAddr candidate = | ||
| 441 | Common::AlignDown((region_start + random_offset), alignment) + offset; | ||
| 442 | |||
| 443 | KMemoryInfo info = this->QueryInfoImpl(candidate); | ||
| 444 | |||
| 445 | if (info.state != KMemoryState::Free) { | ||
| 446 | continue; | ||
| 447 | } | ||
| 448 | if (region_start > candidate) { | ||
| 449 | continue; | ||
| 450 | } | ||
| 451 | if (info.GetAddress() + guard_pages * PageSize > candidate) { | ||
| 452 | continue; | ||
| 453 | } | ||
| 454 | |||
| 455 | const VAddr candidate_end = candidate + (num_pages + guard_pages) * PageSize - 1; | ||
| 456 | if (candidate_end > info.GetLastAddress()) { | ||
| 457 | continue; | ||
| 458 | } | ||
| 459 | if (candidate_end > region_start + region_num_pages * PageSize - 1) { | ||
| 460 | continue; | ||
| 461 | } | ||
| 462 | |||
| 463 | address = candidate; | ||
| 464 | break; | ||
| 465 | } | ||
| 466 | // Fall back to finding the first free area with a random offset. | ||
| 467 | if (address == 0) { | ||
| 468 | // NOTE: Nintendo does not account for guard pages here. | ||
| 469 | // This may theoretically cause an offset to be chosen that cannot be mapped. We | ||
| 470 | // will account for guard pages. | ||
| 471 | const std::size_t offset_pages = KSystemControl::GenerateRandomRange( | ||
| 472 | 0, region_num_pages - num_pages - guard_pages); | ||
| 473 | address = block_manager->FindFreeArea(region_start + offset_pages * PageSize, | ||
| 474 | region_num_pages - offset_pages, num_pages, | ||
| 475 | alignment, offset, guard_pages); | ||
| 476 | } | ||
| 477 | } | ||
| 478 | |||
| 479 | // Find the first free area. | ||
| 480 | if (address == 0) { | ||
| 481 | address = block_manager->FindFreeArea(region_start, region_num_pages, num_pages, | ||
| 482 | alignment, offset, guard_pages); | ||
| 483 | } | ||
| 484 | } | ||
| 485 | |||
| 486 | return address; | ||
| 487 | } | ||
| 488 | |||
| 372 | ResultCode KPageTable::UnmapProcessMemory(VAddr dst_addr, std::size_t size, | 489 | ResultCode KPageTable::UnmapProcessMemory(VAddr dst_addr, std::size_t size, |
| 373 | KPageTable& src_page_table, VAddr src_addr) { | 490 | KPageTable& src_page_table, VAddr src_addr) { |
| 374 | std::lock_guard lock{page_table_lock}; | 491 | KScopedLightLock lk(general_lock); |
| 375 | 492 | ||
| 376 | const std::size_t num_pages{size / PageSize}; | 493 | const std::size_t num_pages{size / PageSize}; |
| 377 | 494 | ||
| @@ -397,150 +514,482 @@ ResultCode KPageTable::UnmapProcessMemory(VAddr dst_addr, std::size_t size, | |||
| 397 | return ResultSuccess; | 514 | return ResultSuccess; |
| 398 | } | 515 | } |
| 399 | 516 | ||
| 400 | ResultCode KPageTable::MapPhysicalMemory(VAddr addr, std::size_t size) { | 517 | ResultCode KPageTable::MapPhysicalMemory(VAddr address, std::size_t size) { |
| 401 | // Lock the physical memory lock. | 518 | // Lock the physical memory lock. |
| 402 | std::lock_guard phys_lk(map_physical_memory_lock); | 519 | KScopedLightLock map_phys_mem_lk(map_physical_memory_lock); |
| 403 | 520 | ||
| 404 | // Lock the table. | 521 | // Calculate the last address for convenience. |
| 405 | std::lock_guard lock{page_table_lock}; | 522 | const VAddr last_address = address + size - 1; |
| 406 | 523 | ||
| 407 | std::size_t mapped_size{}; | 524 | // Define iteration variables. |
| 408 | const VAddr end_addr{addr + size}; | 525 | VAddr cur_address; |
| 526 | std::size_t mapped_size; | ||
| 409 | 527 | ||
| 410 | block_manager->IterateForRange(addr, end_addr, [&](const KMemoryInfo& info) { | 528 | // The entire mapping process can be retried. |
| 411 | if (info.state != KMemoryState::Free) { | 529 | while (true) { |
| 412 | mapped_size += GetSizeInRange(info, addr, end_addr); | 530 | // Check if the memory is already mapped. |
| 413 | } | 531 | { |
| 414 | }); | 532 | // Lock the table. |
| 415 | 533 | KScopedLightLock lk(general_lock); | |
| 416 | if (mapped_size == size) { | 534 | |
| 417 | return ResultSuccess; | 535 | // Iterate over the memory. |
| 418 | } | 536 | cur_address = address; |
| 537 | mapped_size = 0; | ||
| 538 | |||
| 539 | auto it = block_manager->FindIterator(cur_address); | ||
| 540 | while (true) { | ||
| 541 | // Check that the iterator is valid. | ||
| 542 | ASSERT(it != block_manager->end()); | ||
| 543 | |||
| 544 | // Get the memory info. | ||
| 545 | const KMemoryInfo info = it->GetMemoryInfo(); | ||
| 546 | |||
| 547 | // Check if we're done. | ||
| 548 | if (last_address <= info.GetLastAddress()) { | ||
| 549 | if (info.GetState() != KMemoryState::Free) { | ||
| 550 | mapped_size += (last_address + 1 - cur_address); | ||
| 551 | } | ||
| 552 | break; | ||
| 553 | } | ||
| 554 | |||
| 555 | // Track the memory if it's mapped. | ||
| 556 | if (info.GetState() != KMemoryState::Free) { | ||
| 557 | mapped_size += VAddr(info.GetEndAddress()) - cur_address; | ||
| 558 | } | ||
| 559 | |||
| 560 | // Advance. | ||
| 561 | cur_address = info.GetEndAddress(); | ||
| 562 | ++it; | ||
| 563 | } | ||
| 419 | 564 | ||
| 420 | const std::size_t remaining_size{size - mapped_size}; | 565 | // If the size mapped is the size requested, we've nothing to do. |
| 421 | const std::size_t remaining_pages{remaining_size / PageSize}; | 566 | R_SUCCEED_IF(size == mapped_size); |
| 567 | } | ||
| 422 | 568 | ||
| 423 | // Reserve the memory from the process resource limit. | 569 | // Allocate and map the memory. |
| 424 | KScopedResourceReservation memory_reservation( | 570 | { |
| 425 | system.Kernel().CurrentProcess()->GetResourceLimit(), LimitableResource::PhysicalMemory, | 571 | // Reserve the memory from the process resource limit. |
| 426 | remaining_size); | 572 | KScopedResourceReservation memory_reservation( |
| 427 | if (!memory_reservation.Succeeded()) { | 573 | system.Kernel().CurrentProcess()->GetResourceLimit(), |
| 428 | LOG_ERROR(Kernel, "Could not reserve remaining {:X} bytes", remaining_size); | 574 | LimitableResource::PhysicalMemory, size - mapped_size); |
| 429 | return ResultLimitReached; | 575 | R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached); |
| 576 | |||
| 577 | // Allocate pages for the new memory. | ||
| 578 | KPageLinkedList pg; | ||
| 579 | R_TRY(system.Kernel().MemoryManager().AllocateAndOpenForProcess( | ||
| 580 | &pg, (size - mapped_size) / PageSize, | ||
| 581 | KMemoryManager::EncodeOption(memory_pool, allocation_option), 0, 0)); | ||
| 582 | |||
| 583 | // Map the memory. | ||
| 584 | { | ||
| 585 | // Lock the table. | ||
| 586 | KScopedLightLock lk(general_lock); | ||
| 587 | |||
| 588 | size_t num_allocator_blocks = 0; | ||
| 589 | |||
| 590 | // Verify that nobody has mapped memory since we first checked. | ||
| 591 | { | ||
| 592 | // Iterate over the memory. | ||
| 593 | size_t checked_mapped_size = 0; | ||
| 594 | cur_address = address; | ||
| 595 | |||
| 596 | auto it = block_manager->FindIterator(cur_address); | ||
| 597 | while (true) { | ||
| 598 | // Check that the iterator is valid. | ||
| 599 | ASSERT(it != block_manager->end()); | ||
| 600 | |||
| 601 | // Get the memory info. | ||
| 602 | const KMemoryInfo info = it->GetMemoryInfo(); | ||
| 603 | |||
| 604 | const bool is_free = info.GetState() == KMemoryState::Free; | ||
| 605 | if (is_free) { | ||
| 606 | if (info.GetAddress() < address) { | ||
| 607 | ++num_allocator_blocks; | ||
| 608 | } | ||
| 609 | if (last_address < info.GetLastAddress()) { | ||
| 610 | ++num_allocator_blocks; | ||
| 611 | } | ||
| 612 | } | ||
| 613 | |||
| 614 | // Check if we're done. | ||
| 615 | if (last_address <= info.GetLastAddress()) { | ||
| 616 | if (!is_free) { | ||
| 617 | checked_mapped_size += (last_address + 1 - cur_address); | ||
| 618 | } | ||
| 619 | break; | ||
| 620 | } | ||
| 621 | |||
| 622 | // Track the memory if it's mapped. | ||
| 623 | if (!is_free) { | ||
| 624 | checked_mapped_size += VAddr(info.GetEndAddress()) - cur_address; | ||
| 625 | } | ||
| 626 | |||
| 627 | // Advance. | ||
| 628 | cur_address = info.GetEndAddress(); | ||
| 629 | ++it; | ||
| 630 | } | ||
| 631 | |||
| 632 | // If the size now isn't what it was before, somebody mapped or unmapped | ||
| 633 | // concurrently. If this happened, retry. | ||
| 634 | if (mapped_size != checked_mapped_size) { | ||
| 635 | continue; | ||
| 636 | } | ||
| 637 | } | ||
| 638 | |||
| 639 | // Reset the current tracking address, and make sure we clean up on failure. | ||
| 640 | cur_address = address; | ||
| 641 | auto unmap_guard = detail::ScopeExit([&] { | ||
| 642 | if (cur_address > address) { | ||
| 643 | const VAddr last_unmap_address = cur_address - 1; | ||
| 644 | |||
| 645 | // Iterate, unmapping the pages. | ||
| 646 | cur_address = address; | ||
| 647 | |||
| 648 | auto it = block_manager->FindIterator(cur_address); | ||
| 649 | while (true) { | ||
| 650 | // Check that the iterator is valid. | ||
| 651 | ASSERT(it != block_manager->end()); | ||
| 652 | |||
| 653 | // Get the memory info. | ||
| 654 | const KMemoryInfo info = it->GetMemoryInfo(); | ||
| 655 | |||
| 656 | // If the memory state is free, we mapped it and need to unmap it. | ||
| 657 | if (info.GetState() == KMemoryState::Free) { | ||
| 658 | // Determine the range to unmap. | ||
| 659 | const size_t cur_pages = | ||
| 660 | std::min(VAddr(info.GetEndAddress()) - cur_address, | ||
| 661 | last_unmap_address + 1 - cur_address) / | ||
| 662 | PageSize; | ||
| 663 | |||
| 664 | // Unmap. | ||
| 665 | ASSERT(Operate(cur_address, cur_pages, KMemoryPermission::None, | ||
| 666 | OperationType::Unmap) | ||
| 667 | .IsSuccess()); | ||
| 668 | } | ||
| 669 | |||
| 670 | // Check if we're done. | ||
| 671 | if (last_unmap_address <= info.GetLastAddress()) { | ||
| 672 | break; | ||
| 673 | } | ||
| 674 | |||
| 675 | // Advance. | ||
| 676 | cur_address = info.GetEndAddress(); | ||
| 677 | ++it; | ||
| 678 | } | ||
| 679 | } | ||
| 680 | }); | ||
| 681 | |||
| 682 | // Iterate over the memory. | ||
| 683 | auto pg_it = pg.Nodes().begin(); | ||
| 684 | PAddr pg_phys_addr = pg_it->GetAddress(); | ||
| 685 | size_t pg_pages = pg_it->GetNumPages(); | ||
| 686 | |||
| 687 | auto it = block_manager->FindIterator(cur_address); | ||
| 688 | while (true) { | ||
| 689 | // Check that the iterator is valid. | ||
| 690 | ASSERT(it != block_manager->end()); | ||
| 691 | |||
| 692 | // Get the memory info. | ||
| 693 | const KMemoryInfo info = it->GetMemoryInfo(); | ||
| 694 | |||
| 695 | // If it's unmapped, we need to map it. | ||
| 696 | if (info.GetState() == KMemoryState::Free) { | ||
| 697 | // Determine the range to map. | ||
| 698 | size_t map_pages = std::min(VAddr(info.GetEndAddress()) - cur_address, | ||
| 699 | last_address + 1 - cur_address) / | ||
| 700 | PageSize; | ||
| 701 | |||
| 702 | // While we have pages to map, map them. | ||
| 703 | while (map_pages > 0) { | ||
| 704 | // Check if we're at the end of the physical block. | ||
| 705 | if (pg_pages == 0) { | ||
| 706 | // Ensure there are more pages to map. | ||
| 707 | ASSERT(pg_it != pg.Nodes().end()); | ||
| 708 | |||
| 709 | // Advance our physical block. | ||
| 710 | ++pg_it; | ||
| 711 | pg_phys_addr = pg_it->GetAddress(); | ||
| 712 | pg_pages = pg_it->GetNumPages(); | ||
| 713 | } | ||
| 714 | |||
| 715 | // Map whatever we can. | ||
| 716 | const size_t cur_pages = std::min(pg_pages, map_pages); | ||
| 717 | R_TRY(Operate(cur_address, cur_pages, KMemoryPermission::UserReadWrite, | ||
| 718 | OperationType::Map, pg_phys_addr)); | ||
| 719 | |||
| 720 | // Advance. | ||
| 721 | cur_address += cur_pages * PageSize; | ||
| 722 | map_pages -= cur_pages; | ||
| 723 | |||
| 724 | pg_phys_addr += cur_pages * PageSize; | ||
| 725 | pg_pages -= cur_pages; | ||
| 726 | } | ||
| 727 | } | ||
| 728 | |||
| 729 | // Check if we're done. | ||
| 730 | if (last_address <= info.GetLastAddress()) { | ||
| 731 | break; | ||
| 732 | } | ||
| 733 | |||
| 734 | // Advance. | ||
| 735 | cur_address = info.GetEndAddress(); | ||
| 736 | ++it; | ||
| 737 | } | ||
| 738 | |||
| 739 | // We succeeded, so commit the memory reservation. | ||
| 740 | memory_reservation.Commit(); | ||
| 741 | |||
| 742 | // Increase our tracked mapped size. | ||
| 743 | mapped_physical_memory_size += (size - mapped_size); | ||
| 744 | |||
| 745 | // Update the relevant memory blocks. | ||
| 746 | block_manager->Update(address, size / PageSize, KMemoryState::Free, | ||
| 747 | KMemoryPermission::None, KMemoryAttribute::None, | ||
| 748 | KMemoryState::Normal, KMemoryPermission::UserReadWrite, | ||
| 749 | KMemoryAttribute::None); | ||
| 750 | |||
| 751 | // Cancel our guard. | ||
| 752 | unmap_guard.Cancel(); | ||
| 753 | |||
| 754 | return ResultSuccess; | ||
| 755 | } | ||
| 756 | } | ||
| 430 | } | 757 | } |
| 758 | } | ||
| 431 | 759 | ||
| 432 | KPageLinkedList page_linked_list; | 760 | ResultCode KPageTable::UnmapPhysicalMemory(VAddr address, std::size_t size) { |
| 761 | // Lock the physical memory lock. | ||
| 762 | KScopedLightLock map_phys_mem_lk(map_physical_memory_lock); | ||
| 433 | 763 | ||
| 434 | CASCADE_CODE(system.Kernel().MemoryManager().Allocate(page_linked_list, remaining_pages, | 764 | // Lock the table. |
| 435 | memory_pool, allocation_option)); | 765 | KScopedLightLock lk(general_lock); |
| 436 | 766 | ||
| 437 | // We succeeded, so commit the memory reservation. | 767 | // Calculate the last address for convenience. |
| 438 | memory_reservation.Commit(); | 768 | const VAddr last_address = address + size - 1; |
| 439 | 769 | ||
| 440 | // Map the memory. | 770 | // Define iteration variables. |
| 441 | auto node{page_linked_list.Nodes().begin()}; | 771 | VAddr cur_address = 0; |
| 442 | PAddr map_addr{node->GetAddress()}; | 772 | std::size_t mapped_size = 0; |
| 443 | std::size_t src_num_pages{node->GetNumPages()}; | 773 | std::size_t num_allocator_blocks = 0; |
| 444 | block_manager->IterateForRange(addr, end_addr, [&](const KMemoryInfo& info) { | ||
| 445 | if (info.state != KMemoryState::Free) { | ||
| 446 | return; | ||
| 447 | } | ||
| 448 | 774 | ||
| 449 | std::size_t dst_num_pages{GetSizeInRange(info, addr, end_addr) / PageSize}; | 775 | // Check if the memory is mapped. |
| 450 | VAddr dst_addr{GetAddressInRange(info, addr)}; | 776 | { |
| 777 | // Iterate over the memory. | ||
| 778 | cur_address = address; | ||
| 779 | mapped_size = 0; | ||
| 780 | |||
| 781 | auto it = block_manager->FindIterator(cur_address); | ||
| 782 | while (true) { | ||
| 783 | // Check that the iterator is valid. | ||
| 784 | ASSERT(it != block_manager->end()); | ||
| 785 | |||
| 786 | // Get the memory info. | ||
| 787 | const KMemoryInfo info = it->GetMemoryInfo(); | ||
| 788 | |||
| 789 | // Verify the memory's state. | ||
| 790 | const bool is_normal = info.GetState() == KMemoryState::Normal && | ||
| 791 | info.GetAttribute() == KMemoryAttribute::None; | ||
| 792 | const bool is_free = info.GetState() == KMemoryState::Free; | ||
| 793 | R_UNLESS(is_normal || is_free, ResultInvalidCurrentMemory); | ||
| 794 | |||
| 795 | if (is_normal) { | ||
| 796 | R_UNLESS(info.GetAttribute() == KMemoryAttribute::None, ResultInvalidCurrentMemory); | ||
| 797 | |||
| 798 | if (info.GetAddress() < address) { | ||
| 799 | ++num_allocator_blocks; | ||
| 800 | } | ||
| 801 | if (last_address < info.GetLastAddress()) { | ||
| 802 | ++num_allocator_blocks; | ||
| 803 | } | ||
| 804 | } | ||
| 451 | 805 | ||
| 452 | while (dst_num_pages) { | 806 | // Check if we're done. |
| 453 | if (!src_num_pages) { | 807 | if (last_address <= info.GetLastAddress()) { |
| 454 | node = std::next(node); | 808 | if (is_normal) { |
| 455 | map_addr = node->GetAddress(); | 809 | mapped_size += (last_address + 1 - cur_address); |
| 456 | src_num_pages = node->GetNumPages(); | 810 | } |
| 811 | break; | ||
| 457 | } | 812 | } |
| 458 | 813 | ||
| 459 | const std::size_t num_pages{std::min(src_num_pages, dst_num_pages)}; | 814 | // Track the memory if it's mapped. |
| 460 | Operate(dst_addr, num_pages, KMemoryPermission::UserReadWrite, OperationType::Map, | 815 | if (is_normal) { |
| 461 | map_addr); | 816 | mapped_size += VAddr(info.GetEndAddress()) - cur_address; |
| 817 | } | ||
| 462 | 818 | ||
| 463 | dst_addr += num_pages * PageSize; | 819 | // Advance. |
| 464 | map_addr += num_pages * PageSize; | 820 | cur_address = info.GetEndAddress(); |
| 465 | src_num_pages -= num_pages; | 821 | ++it; |
| 466 | dst_num_pages -= num_pages; | ||
| 467 | } | 822 | } |
| 468 | }); | ||
| 469 | |||
| 470 | mapped_physical_memory_size += remaining_size; | ||
| 471 | 823 | ||
| 472 | const std::size_t num_pages{size / PageSize}; | 824 | // If there's nothing mapped, we've nothing to do. |
| 473 | block_manager->Update(addr, num_pages, KMemoryState::Free, KMemoryPermission::None, | 825 | R_SUCCEED_IF(mapped_size == 0); |
| 474 | KMemoryAttribute::None, KMemoryState::Normal, | 826 | } |
| 475 | KMemoryPermission::UserReadWrite, KMemoryAttribute::None); | ||
| 476 | 827 | ||
| 477 | return ResultSuccess; | 828 | // Make a page group for the unmap region. |
| 478 | } | 829 | KPageLinkedList pg; |
| 830 | { | ||
| 831 | auto& impl = this->PageTableImpl(); | ||
| 832 | |||
| 833 | // Begin traversal. | ||
| 834 | Common::PageTable::TraversalContext context; | ||
| 835 | Common::PageTable::TraversalEntry cur_entry = {.phys_addr = 0, .block_size = 0}; | ||
| 836 | bool cur_valid = false; | ||
| 837 | Common::PageTable::TraversalEntry next_entry; | ||
| 838 | bool next_valid = false; | ||
| 839 | size_t tot_size = 0; | ||
| 840 | |||
| 841 | cur_address = address; | ||
| 842 | next_valid = impl.BeginTraversal(next_entry, context, cur_address); | ||
| 843 | next_entry.block_size = | ||
| 844 | (next_entry.block_size - (next_entry.phys_addr & (next_entry.block_size - 1))); | ||
| 845 | |||
| 846 | // Iterate, building the group. | ||
| 847 | while (true) { | ||
| 848 | if ((!next_valid && !cur_valid) || | ||
| 849 | (next_valid && cur_valid && | ||
| 850 | next_entry.phys_addr == cur_entry.phys_addr + cur_entry.block_size)) { | ||
| 851 | cur_entry.block_size += next_entry.block_size; | ||
| 852 | } else { | ||
| 853 | if (cur_valid) { | ||
| 854 | // ASSERT(IsHeapPhysicalAddress(cur_entry.phys_addr)); | ||
| 855 | R_TRY(pg.AddBlock(cur_entry.phys_addr, cur_entry.block_size / PageSize)); | ||
| 856 | } | ||
| 857 | |||
| 858 | // Update tracking variables. | ||
| 859 | tot_size += cur_entry.block_size; | ||
| 860 | cur_entry = next_entry; | ||
| 861 | cur_valid = next_valid; | ||
| 862 | } | ||
| 479 | 863 | ||
| 480 | ResultCode KPageTable::UnmapPhysicalMemory(VAddr addr, std::size_t size) { | 864 | if (cur_entry.block_size + tot_size >= size) { |
| 481 | std::lock_guard lock{page_table_lock}; | 865 | break; |
| 866 | } | ||
| 482 | 867 | ||
| 483 | const VAddr end_addr{addr + size}; | 868 | next_valid = impl.ContinueTraversal(next_entry, context); |
| 484 | ResultCode result{ResultSuccess}; | 869 | } |
| 485 | std::size_t mapped_size{}; | ||
| 486 | 870 | ||
| 487 | // Verify that the region can be unmapped | 871 | // Add the last block. |
| 488 | block_manager->IterateForRange(addr, end_addr, [&](const KMemoryInfo& info) { | 872 | if (cur_valid) { |
| 489 | if (info.state == KMemoryState::Normal) { | 873 | // ASSERT(IsHeapPhysicalAddress(cur_entry.phys_addr)); |
| 490 | if (info.attribute != KMemoryAttribute::None) { | 874 | R_TRY(pg.AddBlock(cur_entry.phys_addr, (size - tot_size) / PageSize)); |
| 491 | result = ResultInvalidCurrentMemory; | 875 | } |
| 492 | return; | 876 | } |
| 877 | ASSERT(pg.GetNumPages() == mapped_size / PageSize); | ||
| 878 | |||
| 879 | // Reset the current tracking address, and make sure we clean up on failure. | ||
| 880 | cur_address = address; | ||
| 881 | auto remap_guard = detail::ScopeExit([&] { | ||
| 882 | if (cur_address > address) { | ||
| 883 | const VAddr last_map_address = cur_address - 1; | ||
| 884 | cur_address = address; | ||
| 885 | |||
| 886 | // Iterate over the memory we unmapped. | ||
| 887 | auto it = block_manager->FindIterator(cur_address); | ||
| 888 | auto pg_it = pg.Nodes().begin(); | ||
| 889 | PAddr pg_phys_addr = pg_it->GetAddress(); | ||
| 890 | size_t pg_pages = pg_it->GetNumPages(); | ||
| 891 | |||
| 892 | while (true) { | ||
| 893 | // Get the memory info for the pages we unmapped, convert to property. | ||
| 894 | const KMemoryInfo info = it->GetMemoryInfo(); | ||
| 895 | |||
| 896 | // If the memory is normal, we unmapped it and need to re-map it. | ||
| 897 | if (info.GetState() == KMemoryState::Normal) { | ||
| 898 | // Determine the range to map. | ||
| 899 | size_t map_pages = std::min(VAddr(info.GetEndAddress()) - cur_address, | ||
| 900 | last_map_address + 1 - cur_address) / | ||
| 901 | PageSize; | ||
| 902 | |||
| 903 | // While we have pages to map, map them. | ||
| 904 | while (map_pages > 0) { | ||
| 905 | // Check if we're at the end of the physical block. | ||
| 906 | if (pg_pages == 0) { | ||
| 907 | // Ensure there are more pages to map. | ||
| 908 | ASSERT(pg_it != pg.Nodes().end()); | ||
| 909 | |||
| 910 | // Advance our physical block. | ||
| 911 | ++pg_it; | ||
| 912 | pg_phys_addr = pg_it->GetAddress(); | ||
| 913 | pg_pages = pg_it->GetNumPages(); | ||
| 914 | } | ||
| 915 | |||
| 916 | // Map whatever we can. | ||
| 917 | const size_t cur_pages = std::min(pg_pages, map_pages); | ||
| 918 | ASSERT(this->Operate(cur_address, cur_pages, info.GetPermission(), | ||
| 919 | OperationType::Map, pg_phys_addr) == ResultSuccess); | ||
| 920 | |||
| 921 | // Advance. | ||
| 922 | cur_address += cur_pages * PageSize; | ||
| 923 | map_pages -= cur_pages; | ||
| 924 | |||
| 925 | pg_phys_addr += cur_pages * PageSize; | ||
| 926 | pg_pages -= cur_pages; | ||
| 927 | } | ||
| 928 | } | ||
| 929 | |||
| 930 | // Check if we're done. | ||
| 931 | if (last_map_address <= info.GetLastAddress()) { | ||
| 932 | break; | ||
| 933 | } | ||
| 934 | |||
| 935 | // Advance. | ||
| 936 | ++it; | ||
| 493 | } | 937 | } |
| 494 | mapped_size += GetSizeInRange(info, addr, end_addr); | ||
| 495 | } else if (info.state != KMemoryState::Free) { | ||
| 496 | result = ResultInvalidCurrentMemory; | ||
| 497 | } | 938 | } |
| 498 | }); | 939 | }); |
| 499 | 940 | ||
| 500 | if (result.IsError()) { | 941 | // Iterate over the memory, unmapping as we go. |
| 501 | return result; | 942 | auto it = block_manager->FindIterator(cur_address); |
| 502 | } | 943 | while (true) { |
| 944 | // Check that the iterator is valid. | ||
| 945 | ASSERT(it != block_manager->end()); | ||
| 503 | 946 | ||
| 504 | if (!mapped_size) { | 947 | // Get the memory info. |
| 505 | return ResultSuccess; | 948 | const KMemoryInfo info = it->GetMemoryInfo(); |
| 506 | } | ||
| 507 | 949 | ||
| 508 | // Unmap each region within the range | 950 | // If the memory state is normal, we need to unmap it. |
| 509 | KPageLinkedList page_linked_list; | 951 | if (info.GetState() == KMemoryState::Normal) { |
| 510 | block_manager->IterateForRange(addr, end_addr, [&](const KMemoryInfo& info) { | 952 | // Determine the range to unmap. |
| 511 | if (info.state == KMemoryState::Normal) { | 953 | const size_t cur_pages = std::min(VAddr(info.GetEndAddress()) - cur_address, |
| 512 | const std::size_t block_size{GetSizeInRange(info, addr, end_addr)}; | 954 | last_address + 1 - cur_address) / |
| 513 | const std::size_t block_num_pages{block_size / PageSize}; | 955 | PageSize; |
| 514 | const VAddr block_addr{GetAddressInRange(info, addr)}; | 956 | |
| 515 | 957 | // Unmap. | |
| 516 | AddRegionToPages(block_addr, block_size / PageSize, page_linked_list); | 958 | R_TRY(Operate(cur_address, cur_pages, KMemoryPermission::None, OperationType::Unmap)); |
| 517 | |||
| 518 | if (result = Operate(block_addr, block_num_pages, KMemoryPermission::None, | ||
| 519 | OperationType::Unmap); | ||
| 520 | result.IsError()) { | ||
| 521 | return; | ||
| 522 | } | ||
| 523 | } | 959 | } |
| 524 | }); | ||
| 525 | if (result.IsError()) { | ||
| 526 | return result; | ||
| 527 | } | ||
| 528 | 960 | ||
| 529 | const std::size_t num_pages{size / PageSize}; | 961 | // Check if we're done. |
| 530 | system.Kernel().MemoryManager().Free(page_linked_list, num_pages, memory_pool, | 962 | if (last_address <= info.GetLastAddress()) { |
| 531 | allocation_option); | 963 | break; |
| 964 | } | ||
| 532 | 965 | ||
| 533 | block_manager->Update(addr, num_pages, KMemoryState::Free); | 966 | // Advance. |
| 967 | cur_address = info.GetEndAddress(); | ||
| 968 | ++it; | ||
| 969 | } | ||
| 534 | 970 | ||
| 971 | // Release the memory resource. | ||
| 972 | mapped_physical_memory_size -= mapped_size; | ||
| 535 | auto process{system.Kernel().CurrentProcess()}; | 973 | auto process{system.Kernel().CurrentProcess()}; |
| 536 | process->GetResourceLimit()->Release(LimitableResource::PhysicalMemory, mapped_size); | 974 | process->GetResourceLimit()->Release(LimitableResource::PhysicalMemory, mapped_size); |
| 537 | mapped_physical_memory_size -= mapped_size; | 975 | |
| 976 | // Update memory blocks. | ||
| 977 | block_manager->Update(address, size / PageSize, KMemoryState::Free, KMemoryPermission::None, | ||
| 978 | KMemoryAttribute::None); | ||
| 979 | |||
| 980 | // TODO(bunnei): This is a workaround until the next set of changes, where we add reference | ||
| 981 | // counting for mapped pages. Until then, we must manually close the reference to the page | ||
| 982 | // group. | ||
| 983 | system.Kernel().MemoryManager().Close(pg); | ||
| 984 | |||
| 985 | // We succeeded. | ||
| 986 | remap_guard.Cancel(); | ||
| 538 | 987 | ||
| 539 | return ResultSuccess; | 988 | return ResultSuccess; |
| 540 | } | 989 | } |
| 541 | 990 | ||
| 542 | ResultCode KPageTable::MapMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) { | 991 | ResultCode KPageTable::MapMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) { |
| 543 | std::lock_guard lock{page_table_lock}; | 992 | KScopedLightLock lk(general_lock); |
| 544 | 993 | ||
| 545 | KMemoryState src_state{}; | 994 | KMemoryState src_state{}; |
| 546 | CASCADE_CODE(CheckMemoryState( | 995 | CASCADE_CODE(CheckMemoryState( |
| @@ -579,7 +1028,7 @@ ResultCode KPageTable::MapMemory(VAddr dst_addr, VAddr src_addr, std::size_t siz | |||
| 579 | } | 1028 | } |
| 580 | 1029 | ||
| 581 | ResultCode KPageTable::UnmapMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) { | 1030 | ResultCode KPageTable::UnmapMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) { |
| 582 | std::lock_guard lock{page_table_lock}; | 1031 | KScopedLightLock lk(general_lock); |
| 583 | 1032 | ||
| 584 | KMemoryState src_state{}; | 1033 | KMemoryState src_state{}; |
| 585 | CASCADE_CODE(CheckMemoryState( | 1034 | CASCADE_CODE(CheckMemoryState( |
| @@ -622,6 +1071,8 @@ ResultCode KPageTable::UnmapMemory(VAddr dst_addr, VAddr src_addr, std::size_t s | |||
| 622 | 1071 | ||
| 623 | ResultCode KPageTable::MapPages(VAddr addr, const KPageLinkedList& page_linked_list, | 1072 | ResultCode KPageTable::MapPages(VAddr addr, const KPageLinkedList& page_linked_list, |
| 624 | KMemoryPermission perm) { | 1073 | KMemoryPermission perm) { |
| 1074 | ASSERT(this->IsLockedByCurrentThread()); | ||
| 1075 | |||
| 625 | VAddr cur_addr{addr}; | 1076 | VAddr cur_addr{addr}; |
| 626 | 1077 | ||
| 627 | for (const auto& node : page_linked_list.Nodes()) { | 1078 | for (const auto& node : page_linked_list.Nodes()) { |
| @@ -650,7 +1101,7 @@ ResultCode KPageTable::MapPages(VAddr address, KPageLinkedList& page_linked_list | |||
| 650 | R_UNLESS(this->CanContain(address, size, state), ResultInvalidCurrentMemory); | 1101 | R_UNLESS(this->CanContain(address, size, state), ResultInvalidCurrentMemory); |
| 651 | 1102 | ||
| 652 | // Lock the table. | 1103 | // Lock the table. |
| 653 | std::lock_guard lock{page_table_lock}; | 1104 | KScopedLightLock lk(general_lock); |
| 654 | 1105 | ||
| 655 | // Check the memory state. | 1106 | // Check the memory state. |
| 656 | R_TRY(this->CheckMemoryState(address, size, KMemoryState::All, KMemoryState::Free, | 1107 | R_TRY(this->CheckMemoryState(address, size, KMemoryState::All, KMemoryState::Free, |
| @@ -666,13 +1117,54 @@ ResultCode KPageTable::MapPages(VAddr address, KPageLinkedList& page_linked_list | |||
| 666 | return ResultSuccess; | 1117 | return ResultSuccess; |
| 667 | } | 1118 | } |
| 668 | 1119 | ||
| 1120 | ResultCode KPageTable::MapPages(VAddr* out_addr, std::size_t num_pages, std::size_t alignment, | ||
| 1121 | PAddr phys_addr, bool is_pa_valid, VAddr region_start, | ||
| 1122 | std::size_t region_num_pages, KMemoryState state, | ||
| 1123 | KMemoryPermission perm) { | ||
| 1124 | ASSERT(Common::IsAligned(alignment, PageSize) && alignment >= PageSize); | ||
| 1125 | |||
| 1126 | // Ensure this is a valid map request. | ||
| 1127 | R_UNLESS(this->CanContain(region_start, region_num_pages * PageSize, state), | ||
| 1128 | ResultInvalidCurrentMemory); | ||
| 1129 | R_UNLESS(num_pages < region_num_pages, ResultOutOfMemory); | ||
| 1130 | |||
| 1131 | // Lock the table. | ||
| 1132 | KScopedLightLock lk(general_lock); | ||
| 1133 | |||
| 1134 | // Find a random address to map at. | ||
| 1135 | VAddr addr = this->FindFreeArea(region_start, region_num_pages, num_pages, alignment, 0, | ||
| 1136 | this->GetNumGuardPages()); | ||
| 1137 | R_UNLESS(addr != 0, ResultOutOfMemory); | ||
| 1138 | ASSERT(Common::IsAligned(addr, alignment)); | ||
| 1139 | ASSERT(this->CanContain(addr, num_pages * PageSize, state)); | ||
| 1140 | ASSERT(this->CheckMemoryState(addr, num_pages * PageSize, KMemoryState::All, KMemoryState::Free, | ||
| 1141 | KMemoryPermission::None, KMemoryPermission::None, | ||
| 1142 | KMemoryAttribute::None, KMemoryAttribute::None) | ||
| 1143 | .IsSuccess()); | ||
| 1144 | |||
| 1145 | // Perform mapping operation. | ||
| 1146 | if (is_pa_valid) { | ||
| 1147 | R_TRY(this->Operate(addr, num_pages, perm, OperationType::Map, phys_addr)); | ||
| 1148 | } else { | ||
| 1149 | UNIMPLEMENTED(); | ||
| 1150 | } | ||
| 1151 | |||
| 1152 | // Update the blocks. | ||
| 1153 | block_manager->Update(addr, num_pages, state, perm); | ||
| 1154 | |||
| 1155 | // We successfully mapped the pages. | ||
| 1156 | *out_addr = addr; | ||
| 1157 | return ResultSuccess; | ||
| 1158 | } | ||
| 1159 | |||
| 669 | ResultCode KPageTable::UnmapPages(VAddr addr, const KPageLinkedList& page_linked_list) { | 1160 | ResultCode KPageTable::UnmapPages(VAddr addr, const KPageLinkedList& page_linked_list) { |
| 1161 | ASSERT(this->IsLockedByCurrentThread()); | ||
| 1162 | |||
| 670 | VAddr cur_addr{addr}; | 1163 | VAddr cur_addr{addr}; |
| 671 | 1164 | ||
| 672 | for (const auto& node : page_linked_list.Nodes()) { | 1165 | for (const auto& node : page_linked_list.Nodes()) { |
| 673 | const std::size_t num_pages{(addr - cur_addr) / PageSize}; | 1166 | if (const auto result{Operate(cur_addr, node.GetNumPages(), KMemoryPermission::None, |
| 674 | if (const auto result{ | 1167 | OperationType::Unmap)}; |
| 675 | Operate(addr, num_pages, KMemoryPermission::None, OperationType::Unmap)}; | ||
| 676 | result.IsError()) { | 1168 | result.IsError()) { |
| 677 | return result; | 1169 | return result; |
| 678 | } | 1170 | } |
| @@ -691,7 +1183,7 @@ ResultCode KPageTable::UnmapPages(VAddr addr, KPageLinkedList& page_linked_list, | |||
| 691 | R_UNLESS(this->Contains(addr, size), ResultInvalidCurrentMemory); | 1183 | R_UNLESS(this->Contains(addr, size), ResultInvalidCurrentMemory); |
| 692 | 1184 | ||
| 693 | // Lock the table. | 1185 | // Lock the table. |
| 694 | std::lock_guard lock{page_table_lock}; | 1186 | KScopedLightLock lk(general_lock); |
| 695 | 1187 | ||
| 696 | // Check the memory state. | 1188 | // Check the memory state. |
| 697 | R_TRY(this->CheckMemoryState(addr, size, KMemoryState::All, state, KMemoryPermission::None, | 1189 | R_TRY(this->CheckMemoryState(addr, size, KMemoryState::All, state, KMemoryPermission::None, |
| @@ -707,12 +1199,36 @@ ResultCode KPageTable::UnmapPages(VAddr addr, KPageLinkedList& page_linked_list, | |||
| 707 | return ResultSuccess; | 1199 | return ResultSuccess; |
| 708 | } | 1200 | } |
| 709 | 1201 | ||
| 1202 | ResultCode KPageTable::UnmapPages(VAddr address, std::size_t num_pages, KMemoryState state) { | ||
| 1203 | // Check that the unmap is in range. | ||
| 1204 | const std::size_t size = num_pages * PageSize; | ||
| 1205 | R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory); | ||
| 1206 | |||
| 1207 | // Lock the table. | ||
| 1208 | KScopedLightLock lk(general_lock); | ||
| 1209 | |||
| 1210 | // Check the memory state. | ||
| 1211 | std::size_t num_allocator_blocks{}; | ||
| 1212 | R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), address, size, | ||
| 1213 | KMemoryState::All, state, KMemoryPermission::None, | ||
| 1214 | KMemoryPermission::None, KMemoryAttribute::All, | ||
| 1215 | KMemoryAttribute::None)); | ||
| 1216 | |||
| 1217 | // Perform the unmap. | ||
| 1218 | R_TRY(Operate(address, num_pages, KMemoryPermission::None, OperationType::Unmap)); | ||
| 1219 | |||
| 1220 | // Update the blocks. | ||
| 1221 | block_manager->Update(address, num_pages, KMemoryState::Free, KMemoryPermission::None); | ||
| 1222 | |||
| 1223 | return ResultSuccess; | ||
| 1224 | } | ||
| 1225 | |||
| 710 | ResultCode KPageTable::SetProcessMemoryPermission(VAddr addr, std::size_t size, | 1226 | ResultCode KPageTable::SetProcessMemoryPermission(VAddr addr, std::size_t size, |
| 711 | Svc::MemoryPermission svc_perm) { | 1227 | Svc::MemoryPermission svc_perm) { |
| 712 | const size_t num_pages = size / PageSize; | 1228 | const size_t num_pages = size / PageSize; |
| 713 | 1229 | ||
| 714 | // Lock the table. | 1230 | // Lock the table. |
| 715 | std::lock_guard lock{page_table_lock}; | 1231 | KScopedLightLock lk(general_lock); |
| 716 | 1232 | ||
| 717 | // Verify we can change the memory permission. | 1233 | // Verify we can change the memory permission. |
| 718 | KMemoryState old_state; | 1234 | KMemoryState old_state; |
| @@ -766,7 +1282,7 @@ ResultCode KPageTable::SetProcessMemoryPermission(VAddr addr, std::size_t size, | |||
| 766 | } | 1282 | } |
| 767 | 1283 | ||
| 768 | KMemoryInfo KPageTable::QueryInfoImpl(VAddr addr) { | 1284 | KMemoryInfo KPageTable::QueryInfoImpl(VAddr addr) { |
| 769 | std::lock_guard lock{page_table_lock}; | 1285 | KScopedLightLock lk(general_lock); |
| 770 | 1286 | ||
| 771 | return block_manager->FindBlock(addr).GetMemoryInfo(); | 1287 | return block_manager->FindBlock(addr).GetMemoryInfo(); |
| 772 | } | 1288 | } |
| @@ -781,7 +1297,7 @@ KMemoryInfo KPageTable::QueryInfo(VAddr addr) { | |||
| 781 | } | 1297 | } |
| 782 | 1298 | ||
| 783 | ResultCode KPageTable::ReserveTransferMemory(VAddr addr, std::size_t size, KMemoryPermission perm) { | 1299 | ResultCode KPageTable::ReserveTransferMemory(VAddr addr, std::size_t size, KMemoryPermission perm) { |
| 784 | std::lock_guard lock{page_table_lock}; | 1300 | KScopedLightLock lk(general_lock); |
| 785 | 1301 | ||
| 786 | KMemoryState state{}; | 1302 | KMemoryState state{}; |
| 787 | KMemoryAttribute attribute{}; | 1303 | KMemoryAttribute attribute{}; |
| @@ -799,7 +1315,7 @@ ResultCode KPageTable::ReserveTransferMemory(VAddr addr, std::size_t size, KMemo | |||
| 799 | } | 1315 | } |
| 800 | 1316 | ||
| 801 | ResultCode KPageTable::ResetTransferMemory(VAddr addr, std::size_t size) { | 1317 | ResultCode KPageTable::ResetTransferMemory(VAddr addr, std::size_t size) { |
| 802 | std::lock_guard lock{page_table_lock}; | 1318 | KScopedLightLock lk(general_lock); |
| 803 | 1319 | ||
| 804 | KMemoryState state{}; | 1320 | KMemoryState state{}; |
| 805 | 1321 | ||
| @@ -818,7 +1334,7 @@ ResultCode KPageTable::SetMemoryPermission(VAddr addr, std::size_t size, | |||
| 818 | const size_t num_pages = size / PageSize; | 1334 | const size_t num_pages = size / PageSize; |
| 819 | 1335 | ||
| 820 | // Lock the table. | 1336 | // Lock the table. |
| 821 | std::lock_guard lock{page_table_lock}; | 1337 | KScopedLightLock lk(general_lock); |
| 822 | 1338 | ||
| 823 | // Verify we can change the memory permission. | 1339 | // Verify we can change the memory permission. |
| 824 | KMemoryState old_state; | 1340 | KMemoryState old_state; |
| @@ -847,7 +1363,7 @@ ResultCode KPageTable::SetMemoryAttribute(VAddr addr, std::size_t size, u32 mask | |||
| 847 | KMemoryAttribute::SetMask); | 1363 | KMemoryAttribute::SetMask); |
| 848 | 1364 | ||
| 849 | // Lock the table. | 1365 | // Lock the table. |
| 850 | std::lock_guard lock{page_table_lock}; | 1366 | KScopedLightLock lk(general_lock); |
| 851 | 1367 | ||
| 852 | // Verify we can change the memory attribute. | 1368 | // Verify we can change the memory attribute. |
| 853 | KMemoryState old_state; | 1369 | KMemoryState old_state; |
| @@ -878,7 +1394,7 @@ ResultCode KPageTable::SetMemoryAttribute(VAddr addr, std::size_t size, u32 mask | |||
| 878 | 1394 | ||
| 879 | ResultCode KPageTable::SetMaxHeapSize(std::size_t size) { | 1395 | ResultCode KPageTable::SetMaxHeapSize(std::size_t size) { |
| 880 | // Lock the table. | 1396 | // Lock the table. |
| 881 | std::lock_guard lock{page_table_lock}; | 1397 | KScopedLightLock lk(general_lock); |
| 882 | 1398 | ||
| 883 | // Only process page tables are allowed to set heap size. | 1399 | // Only process page tables are allowed to set heap size. |
| 884 | ASSERT(!this->IsKernel()); | 1400 | ASSERT(!this->IsKernel()); |
| @@ -889,15 +1405,15 @@ ResultCode KPageTable::SetMaxHeapSize(std::size_t size) { | |||
| 889 | } | 1405 | } |
| 890 | 1406 | ||
| 891 | ResultCode KPageTable::SetHeapSize(VAddr* out, std::size_t size) { | 1407 | ResultCode KPageTable::SetHeapSize(VAddr* out, std::size_t size) { |
| 892 | // Lock the physical memory lock. | 1408 | // Lock the physical memory mutex. |
| 893 | std::lock_guard phys_lk(map_physical_memory_lock); | 1409 | KScopedLightLock map_phys_mem_lk(map_physical_memory_lock); |
| 894 | 1410 | ||
| 895 | // Try to perform a reduction in heap, instead of an extension. | 1411 | // Try to perform a reduction in heap, instead of an extension. |
| 896 | VAddr cur_address{}; | 1412 | VAddr cur_address{}; |
| 897 | std::size_t allocation_size{}; | 1413 | std::size_t allocation_size{}; |
| 898 | { | 1414 | { |
| 899 | // Lock the table. | 1415 | // Lock the table. |
| 900 | std::lock_guard lk(page_table_lock); | 1416 | KScopedLightLock lk(general_lock); |
| 901 | 1417 | ||
| 902 | // Validate that setting heap size is possible at all. | 1418 | // Validate that setting heap size is possible at all. |
| 903 | R_UNLESS(!is_kernel, ResultOutOfMemory); | 1419 | R_UNLESS(!is_kernel, ResultOutOfMemory); |
| @@ -955,14 +1471,21 @@ ResultCode KPageTable::SetHeapSize(VAddr* out, std::size_t size) { | |||
| 955 | R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached); | 1471 | R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached); |
| 956 | 1472 | ||
| 957 | // Allocate pages for the heap extension. | 1473 | // Allocate pages for the heap extension. |
| 958 | KPageLinkedList page_linked_list; | 1474 | KPageLinkedList pg; |
| 959 | R_TRY(system.Kernel().MemoryManager().Allocate(page_linked_list, allocation_size / PageSize, | 1475 | R_TRY(system.Kernel().MemoryManager().AllocateAndOpen( |
| 960 | memory_pool, allocation_option)); | 1476 | &pg, allocation_size / PageSize, |
| 1477 | KMemoryManager::EncodeOption(memory_pool, allocation_option))); | ||
| 1478 | |||
| 1479 | // Clear all the newly allocated pages. | ||
| 1480 | for (const auto& it : pg.Nodes()) { | ||
| 1481 | std::memset(system.DeviceMemory().GetPointer(it.GetAddress()), heap_fill_value, | ||
| 1482 | it.GetSize()); | ||
| 1483 | } | ||
| 961 | 1484 | ||
| 962 | // Map the pages. | 1485 | // Map the pages. |
| 963 | { | 1486 | { |
| 964 | // Lock the table. | 1487 | // Lock the table. |
| 965 | std::lock_guard lk(page_table_lock); | 1488 | KScopedLightLock lk(general_lock); |
| 966 | 1489 | ||
| 967 | // Ensure that the heap hasn't changed since we began executing. | 1490 | // Ensure that the heap hasn't changed since we began executing. |
| 968 | ASSERT(cur_address == current_heap_end); | 1491 | ASSERT(cur_address == current_heap_end); |
| @@ -976,7 +1499,7 @@ ResultCode KPageTable::SetHeapSize(VAddr* out, std::size_t size) { | |||
| 976 | 1499 | ||
| 977 | // Map the pages. | 1500 | // Map the pages. |
| 978 | const auto num_pages = allocation_size / PageSize; | 1501 | const auto num_pages = allocation_size / PageSize; |
| 979 | R_TRY(Operate(current_heap_end, num_pages, page_linked_list, OperationType::MapGroup)); | 1502 | R_TRY(Operate(current_heap_end, num_pages, pg, OperationType::MapGroup)); |
| 980 | 1503 | ||
| 981 | // Clear all the newly allocated pages. | 1504 | // Clear all the newly allocated pages. |
| 982 | for (std::size_t cur_page = 0; cur_page < num_pages; ++cur_page) { | 1505 | for (std::size_t cur_page = 0; cur_page < num_pages; ++cur_page) { |
| @@ -1004,7 +1527,7 @@ ResultVal<VAddr> KPageTable::AllocateAndMapMemory(std::size_t needed_num_pages, | |||
| 1004 | bool is_map_only, VAddr region_start, | 1527 | bool is_map_only, VAddr region_start, |
| 1005 | std::size_t region_num_pages, KMemoryState state, | 1528 | std::size_t region_num_pages, KMemoryState state, |
| 1006 | KMemoryPermission perm, PAddr map_addr) { | 1529 | KMemoryPermission perm, PAddr map_addr) { |
| 1007 | std::lock_guard lock{page_table_lock}; | 1530 | KScopedLightLock lk(general_lock); |
| 1008 | 1531 | ||
| 1009 | if (!CanContain(region_start, region_num_pages * PageSize, state)) { | 1532 | if (!CanContain(region_start, region_num_pages * PageSize, state)) { |
| 1010 | return ResultInvalidCurrentMemory; | 1533 | return ResultInvalidCurrentMemory; |
| @@ -1024,8 +1547,9 @@ ResultVal<VAddr> KPageTable::AllocateAndMapMemory(std::size_t needed_num_pages, | |||
| 1024 | R_TRY(Operate(addr, needed_num_pages, perm, OperationType::Map, map_addr)); | 1547 | R_TRY(Operate(addr, needed_num_pages, perm, OperationType::Map, map_addr)); |
| 1025 | } else { | 1548 | } else { |
| 1026 | KPageLinkedList page_group; | 1549 | KPageLinkedList page_group; |
| 1027 | R_TRY(system.Kernel().MemoryManager().Allocate(page_group, needed_num_pages, memory_pool, | 1550 | R_TRY(system.Kernel().MemoryManager().AllocateAndOpenForProcess( |
| 1028 | allocation_option)); | 1551 | &page_group, needed_num_pages, |
| 1552 | KMemoryManager::EncodeOption(memory_pool, allocation_option), 0, 0)); | ||
| 1029 | R_TRY(Operate(addr, needed_num_pages, page_group, OperationType::MapGroup)); | 1553 | R_TRY(Operate(addr, needed_num_pages, page_group, OperationType::MapGroup)); |
| 1030 | } | 1554 | } |
| 1031 | 1555 | ||
| @@ -1035,7 +1559,7 @@ ResultVal<VAddr> KPageTable::AllocateAndMapMemory(std::size_t needed_num_pages, | |||
| 1035 | } | 1559 | } |
| 1036 | 1560 | ||
| 1037 | ResultCode KPageTable::LockForDeviceAddressSpace(VAddr addr, std::size_t size) { | 1561 | ResultCode KPageTable::LockForDeviceAddressSpace(VAddr addr, std::size_t size) { |
| 1038 | std::lock_guard lock{page_table_lock}; | 1562 | KScopedLightLock lk(general_lock); |
| 1039 | 1563 | ||
| 1040 | KMemoryPermission perm{}; | 1564 | KMemoryPermission perm{}; |
| 1041 | if (const ResultCode result{CheckMemoryState( | 1565 | if (const ResultCode result{CheckMemoryState( |
| @@ -1058,7 +1582,7 @@ ResultCode KPageTable::LockForDeviceAddressSpace(VAddr addr, std::size_t size) { | |||
| 1058 | } | 1582 | } |
| 1059 | 1583 | ||
| 1060 | ResultCode KPageTable::UnlockForDeviceAddressSpace(VAddr addr, std::size_t size) { | 1584 | ResultCode KPageTable::UnlockForDeviceAddressSpace(VAddr addr, std::size_t size) { |
| 1061 | std::lock_guard lock{page_table_lock}; | 1585 | KScopedLightLock lk(general_lock); |
| 1062 | 1586 | ||
| 1063 | KMemoryPermission perm{}; | 1587 | KMemoryPermission perm{}; |
| 1064 | if (const ResultCode result{CheckMemoryState( | 1588 | if (const ResultCode result{CheckMemoryState( |
| @@ -1081,7 +1605,7 @@ ResultCode KPageTable::UnlockForDeviceAddressSpace(VAddr addr, std::size_t size) | |||
| 1081 | } | 1605 | } |
| 1082 | 1606 | ||
| 1083 | ResultCode KPageTable::LockForCodeMemory(VAddr addr, std::size_t size) { | 1607 | ResultCode KPageTable::LockForCodeMemory(VAddr addr, std::size_t size) { |
| 1084 | std::lock_guard lock{page_table_lock}; | 1608 | KScopedLightLock lk(general_lock); |
| 1085 | 1609 | ||
| 1086 | KMemoryPermission new_perm = KMemoryPermission::NotMapped | KMemoryPermission::KernelReadWrite; | 1610 | KMemoryPermission new_perm = KMemoryPermission::NotMapped | KMemoryPermission::KernelReadWrite; |
| 1087 | 1611 | ||
| @@ -1108,7 +1632,7 @@ ResultCode KPageTable::LockForCodeMemory(VAddr addr, std::size_t size) { | |||
| 1108 | } | 1632 | } |
| 1109 | 1633 | ||
| 1110 | ResultCode KPageTable::UnlockForCodeMemory(VAddr addr, std::size_t size) { | 1634 | ResultCode KPageTable::UnlockForCodeMemory(VAddr addr, std::size_t size) { |
| 1111 | std::lock_guard lock{page_table_lock}; | 1635 | KScopedLightLock lk(general_lock); |
| 1112 | 1636 | ||
| 1113 | KMemoryPermission new_perm = KMemoryPermission::UserReadWrite; | 1637 | KMemoryPermission new_perm = KMemoryPermission::UserReadWrite; |
| 1114 | 1638 | ||
| @@ -1232,7 +1756,7 @@ ResultCode KPageTable::Operate(VAddr addr, std::size_t num_pages, KMemoryPermiss | |||
| 1232 | return ResultSuccess; | 1756 | return ResultSuccess; |
| 1233 | } | 1757 | } |
| 1234 | 1758 | ||
| 1235 | constexpr VAddr KPageTable::GetRegionAddress(KMemoryState state) const { | 1759 | VAddr KPageTable::GetRegionAddress(KMemoryState state) const { |
| 1236 | switch (state) { | 1760 | switch (state) { |
| 1237 | case KMemoryState::Free: | 1761 | case KMemoryState::Free: |
| 1238 | case KMemoryState::Kernel: | 1762 | case KMemoryState::Kernel: |
| @@ -1268,7 +1792,7 @@ constexpr VAddr KPageTable::GetRegionAddress(KMemoryState state) const { | |||
| 1268 | } | 1792 | } |
| 1269 | } | 1793 | } |
| 1270 | 1794 | ||
| 1271 | constexpr std::size_t KPageTable::GetRegionSize(KMemoryState state) const { | 1795 | std::size_t KPageTable::GetRegionSize(KMemoryState state) const { |
| 1272 | switch (state) { | 1796 | switch (state) { |
| 1273 | case KMemoryState::Free: | 1797 | case KMemoryState::Free: |
| 1274 | case KMemoryState::Kernel: | 1798 | case KMemoryState::Kernel: |
diff --git a/src/core/hle/kernel/k_page_table.h b/src/core/hle/kernel/k_page_table.h index 60ae9b9e8..54c6adf8d 100644 --- a/src/core/hle/kernel/k_page_table.h +++ b/src/core/hle/kernel/k_page_table.h | |||
| @@ -5,11 +5,12 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <memory> | 7 | #include <memory> |
| 8 | #include <mutex> | ||
| 9 | 8 | ||
| 9 | #include "common/common_funcs.h" | ||
| 10 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 11 | #include "common/page_table.h" | 11 | #include "common/page_table.h" |
| 12 | #include "core/file_sys/program_metadata.h" | 12 | #include "core/file_sys/program_metadata.h" |
| 13 | #include "core/hle/kernel/k_light_lock.h" | ||
| 13 | #include "core/hle/kernel/k_memory_block.h" | 14 | #include "core/hle/kernel/k_memory_block.h" |
| 14 | #include "core/hle/kernel/k_memory_manager.h" | 15 | #include "core/hle/kernel/k_memory_manager.h" |
| 15 | #include "core/hle/result.h" | 16 | #include "core/hle/result.h" |
| @@ -22,17 +23,21 @@ namespace Kernel { | |||
| 22 | 23 | ||
| 23 | class KMemoryBlockManager; | 24 | class KMemoryBlockManager; |
| 24 | 25 | ||
| 25 | class KPageTable final : NonCopyable { | 26 | class KPageTable final { |
| 26 | public: | 27 | public: |
| 28 | YUZU_NON_COPYABLE(KPageTable); | ||
| 29 | YUZU_NON_MOVEABLE(KPageTable); | ||
| 30 | |||
| 27 | explicit KPageTable(Core::System& system_); | 31 | explicit KPageTable(Core::System& system_); |
| 32 | ~KPageTable(); | ||
| 28 | 33 | ||
| 29 | ResultCode InitializeForProcess(FileSys::ProgramAddressSpaceType as_type, bool enable_aslr, | 34 | ResultCode InitializeForProcess(FileSys::ProgramAddressSpaceType as_type, bool enable_aslr, |
| 30 | VAddr code_addr, std::size_t code_size, | 35 | VAddr code_addr, std::size_t code_size, |
| 31 | KMemoryManager::Pool pool); | 36 | KMemoryManager::Pool pool); |
| 32 | ResultCode MapProcessCode(VAddr addr, std::size_t pages_count, KMemoryState state, | 37 | ResultCode MapProcessCode(VAddr addr, std::size_t pages_count, KMemoryState state, |
| 33 | KMemoryPermission perm); | 38 | KMemoryPermission perm); |
| 34 | ResultCode MapCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size); | 39 | ResultCode MapCodeMemory(VAddr dst_address, VAddr src_address, std::size_t size); |
| 35 | ResultCode UnmapCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size); | 40 | ResultCode UnmapCodeMemory(VAddr dst_address, VAddr src_address, std::size_t size); |
| 36 | ResultCode UnmapProcessMemory(VAddr dst_addr, std::size_t size, KPageTable& src_page_table, | 41 | ResultCode UnmapProcessMemory(VAddr dst_addr, std::size_t size, KPageTable& src_page_table, |
| 37 | VAddr src_addr); | 42 | VAddr src_addr); |
| 38 | ResultCode MapPhysicalMemory(VAddr addr, std::size_t size); | 43 | ResultCode MapPhysicalMemory(VAddr addr, std::size_t size); |
| @@ -41,7 +46,14 @@ public: | |||
| 41 | ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, std::size_t size); | 46 | ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, std::size_t size); |
| 42 | ResultCode MapPages(VAddr addr, KPageLinkedList& page_linked_list, KMemoryState state, | 47 | ResultCode MapPages(VAddr addr, KPageLinkedList& page_linked_list, KMemoryState state, |
| 43 | KMemoryPermission perm); | 48 | KMemoryPermission perm); |
| 49 | ResultCode MapPages(VAddr* out_addr, std::size_t num_pages, std::size_t alignment, | ||
| 50 | PAddr phys_addr, KMemoryState state, KMemoryPermission perm) { | ||
| 51 | return this->MapPages(out_addr, num_pages, alignment, phys_addr, true, | ||
| 52 | this->GetRegionAddress(state), this->GetRegionSize(state) / PageSize, | ||
| 53 | state, perm); | ||
| 54 | } | ||
| 44 | ResultCode UnmapPages(VAddr addr, KPageLinkedList& page_linked_list, KMemoryState state); | 55 | ResultCode UnmapPages(VAddr addr, KPageLinkedList& page_linked_list, KMemoryState state); |
| 56 | ResultCode UnmapPages(VAddr address, std::size_t num_pages, KMemoryState state); | ||
| 45 | ResultCode SetProcessMemoryPermission(VAddr addr, std::size_t size, | 57 | ResultCode SetProcessMemoryPermission(VAddr addr, std::size_t size, |
| 46 | Svc::MemoryPermission svc_perm); | 58 | Svc::MemoryPermission svc_perm); |
| 47 | KMemoryInfo QueryInfo(VAddr addr); | 59 | KMemoryInfo QueryInfo(VAddr addr); |
| @@ -86,6 +98,9 @@ private: | |||
| 86 | ResultCode InitializeMemoryLayout(VAddr start, VAddr end); | 98 | ResultCode InitializeMemoryLayout(VAddr start, VAddr end); |
| 87 | ResultCode MapPages(VAddr addr, const KPageLinkedList& page_linked_list, | 99 | ResultCode MapPages(VAddr addr, const KPageLinkedList& page_linked_list, |
| 88 | KMemoryPermission perm); | 100 | KMemoryPermission perm); |
| 101 | ResultCode MapPages(VAddr* out_addr, std::size_t num_pages, std::size_t alignment, | ||
| 102 | PAddr phys_addr, bool is_pa_valid, VAddr region_start, | ||
| 103 | std::size_t region_num_pages, KMemoryState state, KMemoryPermission perm); | ||
| 89 | ResultCode UnmapPages(VAddr addr, const KPageLinkedList& page_linked_list); | 104 | ResultCode UnmapPages(VAddr addr, const KPageLinkedList& page_linked_list); |
| 90 | bool IsRegionMapped(VAddr address, u64 size); | 105 | bool IsRegionMapped(VAddr address, u64 size); |
| 91 | bool IsRegionContiguous(VAddr addr, u64 size) const; | 106 | bool IsRegionContiguous(VAddr addr, u64 size) const; |
| @@ -97,8 +112,11 @@ private: | |||
| 97 | OperationType operation); | 112 | OperationType operation); |
| 98 | ResultCode Operate(VAddr addr, std::size_t num_pages, KMemoryPermission perm, | 113 | ResultCode Operate(VAddr addr, std::size_t num_pages, KMemoryPermission perm, |
| 99 | OperationType operation, PAddr map_addr = 0); | 114 | OperationType operation, PAddr map_addr = 0); |
| 100 | constexpr VAddr GetRegionAddress(KMemoryState state) const; | 115 | VAddr GetRegionAddress(KMemoryState state) const; |
| 101 | constexpr std::size_t GetRegionSize(KMemoryState state) const; | 116 | std::size_t GetRegionSize(KMemoryState state) const; |
| 117 | |||
| 118 | VAddr FindFreeArea(VAddr region_start, std::size_t region_num_pages, std::size_t num_pages, | ||
| 119 | std::size_t alignment, std::size_t offset, std::size_t guard_pages); | ||
| 102 | 120 | ||
| 103 | ResultCode CheckMemoryStateContiguous(std::size_t* out_blocks_needed, VAddr addr, | 121 | ResultCode CheckMemoryStateContiguous(std::size_t* out_blocks_needed, VAddr addr, |
| 104 | std::size_t size, KMemoryState state_mask, | 122 | std::size_t size, KMemoryState state_mask, |
| @@ -132,7 +150,7 @@ private: | |||
| 132 | return CheckMemoryState(nullptr, nullptr, nullptr, out_blocks_needed, addr, size, | 150 | return CheckMemoryState(nullptr, nullptr, nullptr, out_blocks_needed, addr, size, |
| 133 | state_mask, state, perm_mask, perm, attr_mask, attr, ignore_attr); | 151 | state_mask, state, perm_mask, perm, attr_mask, attr, ignore_attr); |
| 134 | } | 152 | } |
| 135 | ResultCode CheckMemoryState(VAddr addr, size_t size, KMemoryState state_mask, | 153 | ResultCode CheckMemoryState(VAddr addr, std::size_t size, KMemoryState state_mask, |
| 136 | KMemoryState state, KMemoryPermission perm_mask, | 154 | KMemoryState state, KMemoryPermission perm_mask, |
| 137 | KMemoryPermission perm, KMemoryAttribute attr_mask, | 155 | KMemoryPermission perm, KMemoryAttribute attr_mask, |
| 138 | KMemoryAttribute attr, | 156 | KMemoryAttribute attr, |
| @@ -142,11 +160,12 @@ private: | |||
| 142 | } | 160 | } |
| 143 | 161 | ||
| 144 | bool IsLockedByCurrentThread() const { | 162 | bool IsLockedByCurrentThread() const { |
| 145 | return true; | 163 | return general_lock.IsLockedByCurrentThread(); |
| 146 | } | 164 | } |
| 147 | 165 | ||
| 148 | std::recursive_mutex page_table_lock; | 166 | mutable KLightLock general_lock; |
| 149 | std::mutex map_physical_memory_lock; | 167 | mutable KLightLock map_physical_memory_lock; |
| 168 | |||
| 150 | std::unique_ptr<KMemoryBlockManager> block_manager; | 169 | std::unique_ptr<KMemoryBlockManager> block_manager; |
| 151 | 170 | ||
| 152 | public: | 171 | public: |
| @@ -204,8 +223,8 @@ public: | |||
| 204 | constexpr VAddr GetAliasCodeRegionSize() const { | 223 | constexpr VAddr GetAliasCodeRegionSize() const { |
| 205 | return alias_code_region_end - alias_code_region_start; | 224 | return alias_code_region_end - alias_code_region_start; |
| 206 | } | 225 | } |
| 207 | size_t GetNormalMemorySize() { | 226 | std::size_t GetNormalMemorySize() { |
| 208 | std::lock_guard lk(page_table_lock); | 227 | KScopedLightLock lk(general_lock); |
| 209 | return GetHeapSize() + mapped_physical_memory_size; | 228 | return GetHeapSize() + mapped_physical_memory_size; |
| 210 | } | 229 | } |
| 211 | constexpr std::size_t GetAddressSpaceWidth() const { | 230 | constexpr std::size_t GetAddressSpaceWidth() const { |
| @@ -247,7 +266,10 @@ public: | |||
| 247 | constexpr bool IsInsideASLRRegion(VAddr address, std::size_t size) const { | 266 | constexpr bool IsInsideASLRRegion(VAddr address, std::size_t size) const { |
| 248 | return !IsOutsideASLRRegion(address, size); | 267 | return !IsOutsideASLRRegion(address, size); |
| 249 | } | 268 | } |
| 250 | constexpr PAddr GetPhysicalAddr(VAddr addr) { | 269 | constexpr std::size_t GetNumGuardPages() const { |
| 270 | return IsKernel() ? 1 : 4; | ||
| 271 | } | ||
| 272 | PAddr GetPhysicalAddr(VAddr addr) const { | ||
| 251 | const auto backing_addr = page_table_impl.backing_addr[addr >> PageBits]; | 273 | const auto backing_addr = page_table_impl.backing_addr[addr >> PageBits]; |
| 252 | ASSERT(backing_addr); | 274 | ASSERT(backing_addr); |
| 253 | return backing_addr + addr; | 275 | return backing_addr + addr; |
| @@ -268,10 +290,6 @@ private: | |||
| 268 | return is_aslr_enabled; | 290 | return is_aslr_enabled; |
| 269 | } | 291 | } |
| 270 | 292 | ||
| 271 | constexpr std::size_t GetNumGuardPages() const { | ||
| 272 | return IsKernel() ? 1 : 4; | ||
| 273 | } | ||
| 274 | |||
| 275 | constexpr bool ContainsPages(VAddr addr, std::size_t num_pages) const { | 293 | constexpr bool ContainsPages(VAddr addr, std::size_t num_pages) const { |
| 276 | return (address_space_start <= addr) && | 294 | return (address_space_start <= addr) && |
| 277 | (num_pages <= (address_space_end - address_space_start) / PageSize) && | 295 | (num_pages <= (address_space_end - address_space_start) / PageSize) && |
| @@ -303,6 +321,8 @@ private: | |||
| 303 | bool is_kernel{}; | 321 | bool is_kernel{}; |
| 304 | bool is_aslr_enabled{}; | 322 | bool is_aslr_enabled{}; |
| 305 | 323 | ||
| 324 | u32 heap_fill_value{}; | ||
| 325 | |||
| 306 | KMemoryManager::Pool memory_pool{KMemoryManager::Pool::Application}; | 326 | KMemoryManager::Pool memory_pool{KMemoryManager::Pool::Application}; |
| 307 | KMemoryManager::Direction allocation_option{KMemoryManager::Direction::FromFront}; | 327 | KMemoryManager::Direction allocation_option{KMemoryManager::Direction::FromFront}; |
| 308 | 328 | ||
diff --git a/src/core/hle/kernel/k_port.cpp b/src/core/hle/kernel/k_port.cpp index a8ba09c4a..ceb98709f 100644 --- a/src/core/hle/kernel/k_port.cpp +++ b/src/core/hle/kernel/k_port.cpp | |||
| @@ -57,7 +57,12 @@ ResultCode KPort::EnqueueSession(KServerSession* session) { | |||
| 57 | R_UNLESS(state == State::Normal, ResultPortClosed); | 57 | R_UNLESS(state == State::Normal, ResultPortClosed); |
| 58 | 58 | ||
| 59 | server.EnqueueSession(session); | 59 | server.EnqueueSession(session); |
| 60 | server.GetSessionRequestHandler()->ClientConnected(server.AcceptSession()); | 60 | |
| 61 | if (auto session_ptr = server.GetSessionRequestHandler().lock()) { | ||
| 62 | session_ptr->ClientConnected(server.AcceptSession()); | ||
| 63 | } else { | ||
| 64 | UNREACHABLE(); | ||
| 65 | } | ||
| 61 | 66 | ||
| 62 | return ResultSuccess; | 67 | return ResultSuccess; |
| 63 | } | 68 | } |
diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp index 85c506979..b39405496 100644 --- a/src/core/hle/kernel/k_process.cpp +++ b/src/core/hle/kernel/k_process.cpp | |||
| @@ -70,65 +70,12 @@ void SetupMainThread(Core::System& system, KProcess& owner_process, u32 priority | |||
| 70 | } | 70 | } |
| 71 | } // Anonymous namespace | 71 | } // Anonymous namespace |
| 72 | 72 | ||
| 73 | // Represents a page used for thread-local storage. | ||
| 74 | // | ||
| 75 | // Each TLS page contains slots that may be used by processes and threads. | ||
| 76 | // Every process and thread is created with a slot in some arbitrary page | ||
| 77 | // (whichever page happens to have an available slot). | ||
| 78 | class TLSPage { | ||
| 79 | public: | ||
| 80 | static constexpr std::size_t num_slot_entries = | ||
| 81 | Core::Memory::PAGE_SIZE / Core::Memory::TLS_ENTRY_SIZE; | ||
| 82 | |||
| 83 | explicit TLSPage(VAddr address) : base_address{address} {} | ||
| 84 | |||
| 85 | bool HasAvailableSlots() const { | ||
| 86 | return !is_slot_used.all(); | ||
| 87 | } | ||
| 88 | |||
| 89 | VAddr GetBaseAddress() const { | ||
| 90 | return base_address; | ||
| 91 | } | ||
| 92 | |||
| 93 | std::optional<VAddr> ReserveSlot() { | ||
| 94 | for (std::size_t i = 0; i < is_slot_used.size(); i++) { | ||
| 95 | if (is_slot_used[i]) { | ||
| 96 | continue; | ||
| 97 | } | ||
| 98 | |||
| 99 | is_slot_used[i] = true; | ||
| 100 | return base_address + (i * Core::Memory::TLS_ENTRY_SIZE); | ||
| 101 | } | ||
| 102 | |||
| 103 | return std::nullopt; | ||
| 104 | } | ||
| 105 | |||
| 106 | void ReleaseSlot(VAddr address) { | ||
| 107 | // Ensure that all given addresses are consistent with how TLS pages | ||
| 108 | // are intended to be used when releasing slots. | ||
| 109 | ASSERT(IsWithinPage(address)); | ||
| 110 | ASSERT((address % Core::Memory::TLS_ENTRY_SIZE) == 0); | ||
| 111 | |||
| 112 | const std::size_t index = (address - base_address) / Core::Memory::TLS_ENTRY_SIZE; | ||
| 113 | is_slot_used[index] = false; | ||
| 114 | } | ||
| 115 | |||
| 116 | private: | ||
| 117 | bool IsWithinPage(VAddr address) const { | ||
| 118 | return base_address <= address && address < base_address + Core::Memory::PAGE_SIZE; | ||
| 119 | } | ||
| 120 | |||
| 121 | VAddr base_address; | ||
| 122 | std::bitset<num_slot_entries> is_slot_used; | ||
| 123 | }; | ||
| 124 | |||
| 125 | ResultCode KProcess::Initialize(KProcess* process, Core::System& system, std::string process_name, | 73 | ResultCode KProcess::Initialize(KProcess* process, Core::System& system, std::string process_name, |
| 126 | ProcessType type) { | 74 | ProcessType type, KResourceLimit* res_limit) { |
| 127 | auto& kernel = system.Kernel(); | 75 | auto& kernel = system.Kernel(); |
| 128 | 76 | ||
| 129 | process->name = std::move(process_name); | 77 | process->name = std::move(process_name); |
| 130 | 78 | process->resource_limit = res_limit; | |
| 131 | process->resource_limit = kernel.GetSystemResourceLimit(); | ||
| 132 | process->status = ProcessStatus::Created; | 79 | process->status = ProcessStatus::Created; |
| 133 | process->program_id = 0; | 80 | process->program_id = 0; |
| 134 | process->process_id = type == ProcessType::KernelInternal ? kernel.CreateNewKernelProcessID() | 81 | process->process_id = type == ProcessType::KernelInternal ? kernel.CreateNewKernelProcessID() |
| @@ -143,9 +90,6 @@ ResultCode KProcess::Initialize(KProcess* process, Core::System& system, std::st | |||
| 143 | 90 | ||
| 144 | kernel.AppendNewProcess(process); | 91 | kernel.AppendNewProcess(process); |
| 145 | 92 | ||
| 146 | // Open a reference to the resource limit. | ||
| 147 | process->resource_limit->Open(); | ||
| 148 | |||
| 149 | // Clear remaining fields. | 93 | // Clear remaining fields. |
| 150 | process->num_running_threads = 0; | 94 | process->num_running_threads = 0; |
| 151 | process->is_signaled = false; | 95 | process->is_signaled = false; |
| @@ -153,6 +97,9 @@ ResultCode KProcess::Initialize(KProcess* process, Core::System& system, std::st | |||
| 153 | process->is_suspended = false; | 97 | process->is_suspended = false; |
| 154 | process->schedule_count = 0; | 98 | process->schedule_count = 0; |
| 155 | 99 | ||
| 100 | // Open a reference to the resource limit. | ||
| 101 | process->resource_limit->Open(); | ||
| 102 | |||
| 156 | return ResultSuccess; | 103 | return ResultSuccess; |
| 157 | } | 104 | } |
| 158 | 105 | ||
| @@ -405,7 +352,7 @@ ResultCode KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, | |||
| 405 | } | 352 | } |
| 406 | 353 | ||
| 407 | // Create TLS region | 354 | // Create TLS region |
| 408 | tls_region_address = CreateTLSRegion(); | 355 | R_TRY(this->CreateThreadLocalRegion(std::addressof(tls_region_address))); |
| 409 | memory_reservation.Commit(); | 356 | memory_reservation.Commit(); |
| 410 | 357 | ||
| 411 | return handle_table.Initialize(capabilities.GetHandleTableSize()); | 358 | return handle_table.Initialize(capabilities.GetHandleTableSize()); |
| @@ -445,7 +392,7 @@ void KProcess::PrepareForTermination() { | |||
| 445 | 392 | ||
| 446 | stop_threads(kernel.System().GlobalSchedulerContext().GetThreadList()); | 393 | stop_threads(kernel.System().GlobalSchedulerContext().GetThreadList()); |
| 447 | 394 | ||
| 448 | FreeTLSRegion(tls_region_address); | 395 | this->DeleteThreadLocalRegion(tls_region_address); |
| 449 | tls_region_address = 0; | 396 | tls_region_address = 0; |
| 450 | 397 | ||
| 451 | if (resource_limit) { | 398 | if (resource_limit) { |
| @@ -457,9 +404,6 @@ void KProcess::PrepareForTermination() { | |||
| 457 | } | 404 | } |
| 458 | 405 | ||
| 459 | void KProcess::Finalize() { | 406 | void KProcess::Finalize() { |
| 460 | // Finalize the handle table and close any open handles. | ||
| 461 | handle_table.Finalize(); | ||
| 462 | |||
| 463 | // Free all shared memory infos. | 407 | // Free all shared memory infos. |
| 464 | { | 408 | { |
| 465 | auto it = shared_memory_list.begin(); | 409 | auto it = shared_memory_list.begin(); |
| @@ -484,67 +428,110 @@ void KProcess::Finalize() { | |||
| 484 | resource_limit = nullptr; | 428 | resource_limit = nullptr; |
| 485 | } | 429 | } |
| 486 | 430 | ||
| 431 | // Finalize the page table. | ||
| 432 | page_table.reset(); | ||
| 433 | |||
| 487 | // Perform inherited finalization. | 434 | // Perform inherited finalization. |
| 488 | KAutoObjectWithSlabHeapAndContainer<KProcess, KWorkerTask>::Finalize(); | 435 | KAutoObjectWithSlabHeapAndContainer<KProcess, KWorkerTask>::Finalize(); |
| 489 | } | 436 | } |
| 490 | 437 | ||
| 491 | /** | 438 | ResultCode KProcess::CreateThreadLocalRegion(VAddr* out) { |
| 492 | * Attempts to find a TLS page that contains a free slot for | 439 | KThreadLocalPage* tlp = nullptr; |
| 493 | * use by a thread. | 440 | VAddr tlr = 0; |
| 494 | * | ||
| 495 | * @returns If a page with an available slot is found, then an iterator | ||
| 496 | * pointing to the page is returned. Otherwise the end iterator | ||
| 497 | * is returned instead. | ||
| 498 | */ | ||
| 499 | static auto FindTLSPageWithAvailableSlots(std::vector<TLSPage>& tls_pages) { | ||
| 500 | return std::find_if(tls_pages.begin(), tls_pages.end(), | ||
| 501 | [](const auto& page) { return page.HasAvailableSlots(); }); | ||
| 502 | } | ||
| 503 | 441 | ||
| 504 | VAddr KProcess::CreateTLSRegion() { | 442 | // See if we can get a region from a partially used TLP. |
| 505 | KScopedSchedulerLock lock(kernel); | 443 | { |
| 506 | if (auto tls_page_iter{FindTLSPageWithAvailableSlots(tls_pages)}; | 444 | KScopedSchedulerLock sl{kernel}; |
| 507 | tls_page_iter != tls_pages.cend()) { | ||
| 508 | return *tls_page_iter->ReserveSlot(); | ||
| 509 | } | ||
| 510 | 445 | ||
| 511 | Page* const tls_page_ptr{kernel.GetUserSlabHeapPages().Allocate()}; | 446 | if (auto it = partially_used_tlp_tree.begin(); it != partially_used_tlp_tree.end()) { |
| 512 | ASSERT(tls_page_ptr); | 447 | tlr = it->Reserve(); |
| 448 | ASSERT(tlr != 0); | ||
| 513 | 449 | ||
| 514 | const VAddr start{page_table->GetKernelMapRegionStart()}; | 450 | if (it->IsAllUsed()) { |
| 515 | const VAddr size{page_table->GetKernelMapRegionEnd() - start}; | 451 | tlp = std::addressof(*it); |
| 516 | const PAddr tls_map_addr{kernel.System().DeviceMemory().GetPhysicalAddr(tls_page_ptr)}; | 452 | partially_used_tlp_tree.erase(it); |
| 517 | const VAddr tls_page_addr{page_table | 453 | fully_used_tlp_tree.insert(*tlp); |
| 518 | ->AllocateAndMapMemory(1, PageSize, true, start, size / PageSize, | 454 | } |
| 519 | KMemoryState::ThreadLocal, | 455 | |
| 520 | KMemoryPermission::UserReadWrite, | 456 | *out = tlr; |
| 521 | tls_map_addr) | 457 | return ResultSuccess; |
| 522 | .ValueOr(0)}; | 458 | } |
| 459 | } | ||
| 523 | 460 | ||
| 524 | ASSERT(tls_page_addr); | 461 | // Allocate a new page. |
| 462 | tlp = KThreadLocalPage::Allocate(kernel); | ||
| 463 | R_UNLESS(tlp != nullptr, ResultOutOfMemory); | ||
| 464 | auto tlp_guard = SCOPE_GUARD({ KThreadLocalPage::Free(kernel, tlp); }); | ||
| 525 | 465 | ||
| 526 | std::memset(tls_page_ptr, 0, PageSize); | 466 | // Initialize the new page. |
| 527 | tls_pages.emplace_back(tls_page_addr); | 467 | R_TRY(tlp->Initialize(kernel, this)); |
| 528 | 468 | ||
| 529 | const auto reserve_result{tls_pages.back().ReserveSlot()}; | 469 | // Reserve a TLR. |
| 530 | ASSERT(reserve_result.has_value()); | 470 | tlr = tlp->Reserve(); |
| 471 | ASSERT(tlr != 0); | ||
| 531 | 472 | ||
| 532 | return *reserve_result; | 473 | // Insert into our tree. |
| 474 | { | ||
| 475 | KScopedSchedulerLock sl{kernel}; | ||
| 476 | if (tlp->IsAllUsed()) { | ||
| 477 | fully_used_tlp_tree.insert(*tlp); | ||
| 478 | } else { | ||
| 479 | partially_used_tlp_tree.insert(*tlp); | ||
| 480 | } | ||
| 481 | } | ||
| 482 | |||
| 483 | // We succeeded! | ||
| 484 | tlp_guard.Cancel(); | ||
| 485 | *out = tlr; | ||
| 486 | return ResultSuccess; | ||
| 533 | } | 487 | } |
| 534 | 488 | ||
| 535 | void KProcess::FreeTLSRegion(VAddr tls_address) { | 489 | ResultCode KProcess::DeleteThreadLocalRegion(VAddr addr) { |
| 536 | KScopedSchedulerLock lock(kernel); | 490 | KThreadLocalPage* page_to_free = nullptr; |
| 537 | const VAddr aligned_address = Common::AlignDown(tls_address, Core::Memory::PAGE_SIZE); | 491 | |
| 538 | auto iter = | 492 | // Release the region. |
| 539 | std::find_if(tls_pages.begin(), tls_pages.end(), [aligned_address](const auto& page) { | 493 | { |
| 540 | return page.GetBaseAddress() == aligned_address; | 494 | KScopedSchedulerLock sl{kernel}; |
| 541 | }); | 495 | |
| 496 | // Try to find the page in the partially used list. | ||
| 497 | auto it = partially_used_tlp_tree.find_key(Common::AlignDown(addr, PageSize)); | ||
| 498 | if (it == partially_used_tlp_tree.end()) { | ||
| 499 | // If we don't find it, it has to be in the fully used list. | ||
| 500 | it = fully_used_tlp_tree.find_key(Common::AlignDown(addr, PageSize)); | ||
| 501 | R_UNLESS(it != fully_used_tlp_tree.end(), ResultInvalidAddress); | ||
| 502 | |||
| 503 | // Release the region. | ||
| 504 | it->Release(addr); | ||
| 505 | |||
| 506 | // Move the page out of the fully used list. | ||
| 507 | KThreadLocalPage* tlp = std::addressof(*it); | ||
| 508 | fully_used_tlp_tree.erase(it); | ||
| 509 | if (tlp->IsAllFree()) { | ||
| 510 | page_to_free = tlp; | ||
| 511 | } else { | ||
| 512 | partially_used_tlp_tree.insert(*tlp); | ||
| 513 | } | ||
| 514 | } else { | ||
| 515 | // Release the region. | ||
| 516 | it->Release(addr); | ||
| 517 | |||
| 518 | // Handle the all-free case. | ||
| 519 | KThreadLocalPage* tlp = std::addressof(*it); | ||
| 520 | if (tlp->IsAllFree()) { | ||
| 521 | partially_used_tlp_tree.erase(it); | ||
| 522 | page_to_free = tlp; | ||
| 523 | } | ||
| 524 | } | ||
| 525 | } | ||
| 526 | |||
| 527 | // If we should free the page it was in, do so. | ||
| 528 | if (page_to_free != nullptr) { | ||
| 529 | page_to_free->Finalize(); | ||
| 542 | 530 | ||
| 543 | // Something has gone very wrong if we're freeing a region | 531 | KThreadLocalPage::Free(kernel, page_to_free); |
| 544 | // with no actual page available. | 532 | } |
| 545 | ASSERT(iter != tls_pages.cend()); | ||
| 546 | 533 | ||
| 547 | iter->ReleaseSlot(tls_address); | 534 | return ResultSuccess; |
| 548 | } | 535 | } |
| 549 | 536 | ||
| 550 | void KProcess::LoadModule(CodeSet code_set, VAddr base_addr) { | 537 | void KProcess::LoadModule(CodeSet code_set, VAddr base_addr) { |
diff --git a/src/core/hle/kernel/k_process.h b/src/core/hle/kernel/k_process.h index 38b446350..5ed0f2d83 100644 --- a/src/core/hle/kernel/k_process.h +++ b/src/core/hle/kernel/k_process.h | |||
| @@ -15,6 +15,7 @@ | |||
| 15 | #include "core/hle/kernel/k_condition_variable.h" | 15 | #include "core/hle/kernel/k_condition_variable.h" |
| 16 | #include "core/hle/kernel/k_handle_table.h" | 16 | #include "core/hle/kernel/k_handle_table.h" |
| 17 | #include "core/hle/kernel/k_synchronization_object.h" | 17 | #include "core/hle/kernel/k_synchronization_object.h" |
| 18 | #include "core/hle/kernel/k_thread_local_page.h" | ||
| 18 | #include "core/hle/kernel/k_worker_task.h" | 19 | #include "core/hle/kernel/k_worker_task.h" |
| 19 | #include "core/hle/kernel/process_capability.h" | 20 | #include "core/hle/kernel/process_capability.h" |
| 20 | #include "core/hle/kernel/slab_helpers.h" | 21 | #include "core/hle/kernel/slab_helpers.h" |
| @@ -91,7 +92,7 @@ public: | |||
| 91 | static constexpr std::size_t RANDOM_ENTROPY_SIZE = 4; | 92 | static constexpr std::size_t RANDOM_ENTROPY_SIZE = 4; |
| 92 | 93 | ||
| 93 | static ResultCode Initialize(KProcess* process, Core::System& system, std::string process_name, | 94 | static ResultCode Initialize(KProcess* process, Core::System& system, std::string process_name, |
| 94 | ProcessType type); | 95 | ProcessType type, KResourceLimit* res_limit); |
| 95 | 96 | ||
| 96 | /// Gets a reference to the process' page table. | 97 | /// Gets a reference to the process' page table. |
| 97 | KPageTable& PageTable() { | 98 | KPageTable& PageTable() { |
| @@ -362,10 +363,10 @@ public: | |||
| 362 | // Thread-local storage management | 363 | // Thread-local storage management |
| 363 | 364 | ||
| 364 | // Marks the next available region as used and returns the address of the slot. | 365 | // Marks the next available region as used and returns the address of the slot. |
| 365 | [[nodiscard]] VAddr CreateTLSRegion(); | 366 | [[nodiscard]] ResultCode CreateThreadLocalRegion(VAddr* out); |
| 366 | 367 | ||
| 367 | // Frees a used TLS slot identified by the given address | 368 | // Frees a used TLS slot identified by the given address |
| 368 | void FreeTLSRegion(VAddr tls_address); | 369 | ResultCode DeleteThreadLocalRegion(VAddr addr); |
| 369 | 370 | ||
| 370 | private: | 371 | private: |
| 371 | void PinThread(s32 core_id, KThread* thread) { | 372 | void PinThread(s32 core_id, KThread* thread) { |
| @@ -413,13 +414,6 @@ private: | |||
| 413 | /// The ideal CPU core for this process, threads are scheduled on this core by default. | 414 | /// The ideal CPU core for this process, threads are scheduled on this core by default. |
| 414 | u8 ideal_core = 0; | 415 | u8 ideal_core = 0; |
| 415 | 416 | ||
| 416 | /// The Thread Local Storage area is allocated as processes create threads, | ||
| 417 | /// each TLS area is 0x200 bytes, so one page (0x1000) is split up in 8 parts, and each part | ||
| 418 | /// holds the TLS for a specific thread. This vector contains which parts are in use for each | ||
| 419 | /// page as a bitmask. | ||
| 420 | /// This vector will grow as more pages are allocated for new threads. | ||
| 421 | std::vector<TLSPage> tls_pages; | ||
| 422 | |||
| 423 | /// Contains the parsed process capability descriptors. | 417 | /// Contains the parsed process capability descriptors. |
| 424 | ProcessCapabilities capabilities; | 418 | ProcessCapabilities capabilities; |
| 425 | 419 | ||
| @@ -482,6 +476,12 @@ private: | |||
| 482 | KThread* exception_thread{}; | 476 | KThread* exception_thread{}; |
| 483 | 477 | ||
| 484 | KLightLock state_lock; | 478 | KLightLock state_lock; |
| 479 | |||
| 480 | using TLPTree = | ||
| 481 | Common::IntrusiveRedBlackTreeBaseTraits<KThreadLocalPage>::TreeType<KThreadLocalPage>; | ||
| 482 | using TLPIterator = TLPTree::iterator; | ||
| 483 | TLPTree fully_used_tlp_tree; | ||
| 484 | TLPTree partially_used_tlp_tree; | ||
| 485 | }; | 485 | }; |
| 486 | 486 | ||
| 487 | } // namespace Kernel | 487 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/k_resource_limit.cpp b/src/core/hle/kernel/k_resource_limit.cpp index 0c4bba66b..a84977c68 100644 --- a/src/core/hle/kernel/k_resource_limit.cpp +++ b/src/core/hle/kernel/k_resource_limit.cpp | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "common/assert.h" | 5 | #include "common/assert.h" |
| 6 | #include "core/core.h" | ||
| 6 | #include "core/core_timing.h" | 7 | #include "core/core_timing.h" |
| 7 | #include "core/hle/kernel/k_resource_limit.h" | 8 | #include "core/hle/kernel/k_resource_limit.h" |
| 8 | #include "core/hle/kernel/svc_results.h" | 9 | #include "core/hle/kernel/svc_results.h" |
| @@ -151,4 +152,22 @@ void KResourceLimit::Release(LimitableResource which, s64 value, s64 hint) { | |||
| 151 | } | 152 | } |
| 152 | } | 153 | } |
| 153 | 154 | ||
| 155 | KResourceLimit* CreateResourceLimitForProcess(Core::System& system, s64 physical_memory_size) { | ||
| 156 | auto* resource_limit = KResourceLimit::Create(system.Kernel()); | ||
| 157 | resource_limit->Initialize(&system.CoreTiming()); | ||
| 158 | |||
| 159 | // Initialize default resource limit values. | ||
| 160 | // TODO(bunnei): These values are the system defaults, the limits for service processes are | ||
| 161 | // lower. These should use the correct limit values. | ||
| 162 | |||
| 163 | ASSERT(resource_limit->SetLimitValue(LimitableResource::PhysicalMemory, physical_memory_size) | ||
| 164 | .IsSuccess()); | ||
| 165 | ASSERT(resource_limit->SetLimitValue(LimitableResource::Threads, 800).IsSuccess()); | ||
| 166 | ASSERT(resource_limit->SetLimitValue(LimitableResource::Events, 900).IsSuccess()); | ||
| 167 | ASSERT(resource_limit->SetLimitValue(LimitableResource::TransferMemory, 200).IsSuccess()); | ||
| 168 | ASSERT(resource_limit->SetLimitValue(LimitableResource::Sessions, 1133).IsSuccess()); | ||
| 169 | |||
| 170 | return resource_limit; | ||
| 171 | } | ||
| 172 | |||
| 154 | } // namespace Kernel | 173 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/k_resource_limit.h b/src/core/hle/kernel/k_resource_limit.h index fab6005ff..d23d16aa4 100644 --- a/src/core/hle/kernel/k_resource_limit.h +++ b/src/core/hle/kernel/k_resource_limit.h | |||
| @@ -67,4 +67,7 @@ private: | |||
| 67 | KLightConditionVariable cond_var; | 67 | KLightConditionVariable cond_var; |
| 68 | const Core::Timing::CoreTiming* core_timing{}; | 68 | const Core::Timing::CoreTiming* core_timing{}; |
| 69 | }; | 69 | }; |
| 70 | |||
| 71 | KResourceLimit* CreateResourceLimitForProcess(Core::System& system, s64 physical_memory_size); | ||
| 72 | |||
| 70 | } // namespace Kernel | 73 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/k_server_port.h b/src/core/hle/kernel/k_server_port.h index 6302d5e61..2185736be 100644 --- a/src/core/hle/kernel/k_server_port.h +++ b/src/core/hle/kernel/k_server_port.h | |||
| @@ -30,11 +30,11 @@ public: | |||
| 30 | 30 | ||
| 31 | /// Whether or not this server port has an HLE handler available. | 31 | /// Whether or not this server port has an HLE handler available. |
| 32 | bool HasSessionRequestHandler() const { | 32 | bool HasSessionRequestHandler() const { |
| 33 | return session_handler != nullptr; | 33 | return !session_handler.expired(); |
| 34 | } | 34 | } |
| 35 | 35 | ||
| 36 | /// Gets the HLE handler for this port. | 36 | /// Gets the HLE handler for this port. |
| 37 | SessionRequestHandlerPtr GetSessionRequestHandler() const { | 37 | SessionRequestHandlerWeakPtr GetSessionRequestHandler() const { |
| 38 | return session_handler; | 38 | return session_handler; |
| 39 | } | 39 | } |
| 40 | 40 | ||
| @@ -42,7 +42,7 @@ public: | |||
| 42 | * Sets the HLE handler template for the port. ServerSessions crated by connecting to this port | 42 | * Sets the HLE handler template for the port. ServerSessions crated by connecting to this port |
| 43 | * will inherit a reference to this handler. | 43 | * will inherit a reference to this handler. |
| 44 | */ | 44 | */ |
| 45 | void SetSessionHandler(SessionRequestHandlerPtr&& handler) { | 45 | void SetSessionHandler(SessionRequestHandlerWeakPtr&& handler) { |
| 46 | session_handler = std::move(handler); | 46 | session_handler = std::move(handler); |
| 47 | } | 47 | } |
| 48 | 48 | ||
| @@ -66,7 +66,7 @@ private: | |||
| 66 | void CleanupSessions(); | 66 | void CleanupSessions(); |
| 67 | 67 | ||
| 68 | SessionList session_list; | 68 | SessionList session_list; |
| 69 | SessionRequestHandlerPtr session_handler; | 69 | SessionRequestHandlerWeakPtr session_handler; |
| 70 | KPort* parent{}; | 70 | KPort* parent{}; |
| 71 | }; | 71 | }; |
| 72 | 72 | ||
diff --git a/src/core/hle/kernel/k_server_session.cpp b/src/core/hle/kernel/k_server_session.cpp index 4d94eb9cf..30c56ff29 100644 --- a/src/core/hle/kernel/k_server_session.cpp +++ b/src/core/hle/kernel/k_server_session.cpp | |||
| @@ -27,10 +27,7 @@ namespace Kernel { | |||
| 27 | 27 | ||
| 28 | KServerSession::KServerSession(KernelCore& kernel_) : KSynchronizationObject{kernel_} {} | 28 | KServerSession::KServerSession(KernelCore& kernel_) : KSynchronizationObject{kernel_} {} |
| 29 | 29 | ||
| 30 | KServerSession::~KServerSession() { | 30 | KServerSession::~KServerSession() = default; |
| 31 | // Ensure that the global list tracking server sessions does not hold on to a reference. | ||
| 32 | kernel.UnregisterServerSession(this); | ||
| 33 | } | ||
| 34 | 31 | ||
| 35 | void KServerSession::Initialize(KSession* parent_session_, std::string&& name_, | 32 | void KServerSession::Initialize(KSession* parent_session_, std::string&& name_, |
| 36 | std::shared_ptr<SessionRequestManager> manager_) { | 33 | std::shared_ptr<SessionRequestManager> manager_) { |
| @@ -49,6 +46,9 @@ void KServerSession::Destroy() { | |||
| 49 | parent->OnServerClosed(); | 46 | parent->OnServerClosed(); |
| 50 | 47 | ||
| 51 | parent->Close(); | 48 | parent->Close(); |
| 49 | |||
| 50 | // Release host emulation members. | ||
| 51 | manager.reset(); | ||
| 52 | } | 52 | } |
| 53 | 53 | ||
| 54 | void KServerSession::OnClientClosed() { | 54 | void KServerSession::OnClientClosed() { |
| @@ -98,7 +98,12 @@ ResultCode KServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& co | |||
| 98 | UNREACHABLE(); | 98 | UNREACHABLE(); |
| 99 | return ResultSuccess; // Ignore error if asserts are off | 99 | return ResultSuccess; // Ignore error if asserts are off |
| 100 | } | 100 | } |
| 101 | return manager->DomainHandler(object_id - 1)->HandleSyncRequest(*this, context); | 101 | if (auto strong_ptr = manager->DomainHandler(object_id - 1).lock()) { |
| 102 | return strong_ptr->HandleSyncRequest(*this, context); | ||
| 103 | } else { | ||
| 104 | UNREACHABLE(); | ||
| 105 | return ResultSuccess; | ||
| 106 | } | ||
| 102 | 107 | ||
| 103 | case IPC::DomainMessageHeader::CommandType::CloseVirtualHandle: { | 108 | case IPC::DomainMessageHeader::CommandType::CloseVirtualHandle: { |
| 104 | LOG_DEBUG(IPC, "CloseVirtualHandle, object_id=0x{:08X}", object_id); | 109 | LOG_DEBUG(IPC, "CloseVirtualHandle, object_id=0x{:08X}", object_id); |
diff --git a/src/core/hle/kernel/k_slab_heap.h b/src/core/hle/kernel/k_slab_heap.h index 0ad74b0a0..5690cc757 100644 --- a/src/core/hle/kernel/k_slab_heap.h +++ b/src/core/hle/kernel/k_slab_heap.h | |||
| @@ -7,6 +7,7 @@ | |||
| 7 | #include <atomic> | 7 | #include <atomic> |
| 8 | 8 | ||
| 9 | #include "common/assert.h" | 9 | #include "common/assert.h" |
| 10 | #include "common/common_funcs.h" | ||
| 10 | #include "common/common_types.h" | 11 | #include "common/common_types.h" |
| 11 | 12 | ||
| 12 | namespace Kernel { | 13 | namespace Kernel { |
| @@ -15,35 +16,34 @@ class KernelCore; | |||
| 15 | 16 | ||
| 16 | namespace impl { | 17 | namespace impl { |
| 17 | 18 | ||
| 18 | class KSlabHeapImpl final : NonCopyable { | 19 | class KSlabHeapImpl { |
| 20 | YUZU_NON_COPYABLE(KSlabHeapImpl); | ||
| 21 | YUZU_NON_MOVEABLE(KSlabHeapImpl); | ||
| 22 | |||
| 19 | public: | 23 | public: |
| 20 | struct Node { | 24 | struct Node { |
| 21 | Node* next{}; | 25 | Node* next{}; |
| 22 | }; | 26 | }; |
| 23 | 27 | ||
| 28 | public: | ||
| 24 | constexpr KSlabHeapImpl() = default; | 29 | constexpr KSlabHeapImpl() = default; |
| 25 | 30 | ||
| 26 | void Initialize(std::size_t size) { | 31 | void Initialize() { |
| 27 | ASSERT(head == nullptr); | 32 | ASSERT(m_head == nullptr); |
| 28 | obj_size = size; | ||
| 29 | } | ||
| 30 | |||
| 31 | constexpr std::size_t GetObjectSize() const { | ||
| 32 | return obj_size; | ||
| 33 | } | 33 | } |
| 34 | 34 | ||
| 35 | Node* GetHead() const { | 35 | Node* GetHead() const { |
| 36 | return head; | 36 | return m_head; |
| 37 | } | 37 | } |
| 38 | 38 | ||
| 39 | void* Allocate() { | 39 | void* Allocate() { |
| 40 | Node* ret = head.load(); | 40 | Node* ret = m_head.load(); |
| 41 | 41 | ||
| 42 | do { | 42 | do { |
| 43 | if (ret == nullptr) { | 43 | if (ret == nullptr) { |
| 44 | break; | 44 | break; |
| 45 | } | 45 | } |
| 46 | } while (!head.compare_exchange_weak(ret, ret->next)); | 46 | } while (!m_head.compare_exchange_weak(ret, ret->next)); |
| 47 | 47 | ||
| 48 | return ret; | 48 | return ret; |
| 49 | } | 49 | } |
| @@ -51,166 +51,157 @@ public: | |||
| 51 | void Free(void* obj) { | 51 | void Free(void* obj) { |
| 52 | Node* node = static_cast<Node*>(obj); | 52 | Node* node = static_cast<Node*>(obj); |
| 53 | 53 | ||
| 54 | Node* cur_head = head.load(); | 54 | Node* cur_head = m_head.load(); |
| 55 | do { | 55 | do { |
| 56 | node->next = cur_head; | 56 | node->next = cur_head; |
| 57 | } while (!head.compare_exchange_weak(cur_head, node)); | 57 | } while (!m_head.compare_exchange_weak(cur_head, node)); |
| 58 | } | 58 | } |
| 59 | 59 | ||
| 60 | private: | 60 | private: |
| 61 | std::atomic<Node*> head{}; | 61 | std::atomic<Node*> m_head{}; |
| 62 | std::size_t obj_size{}; | ||
| 63 | }; | 62 | }; |
| 64 | 63 | ||
| 65 | } // namespace impl | 64 | } // namespace impl |
| 66 | 65 | ||
| 67 | class KSlabHeapBase : NonCopyable { | 66 | template <bool SupportDynamicExpansion> |
| 67 | class KSlabHeapBase : protected impl::KSlabHeapImpl { | ||
| 68 | YUZU_NON_COPYABLE(KSlabHeapBase); | ||
| 69 | YUZU_NON_MOVEABLE(KSlabHeapBase); | ||
| 70 | |||
| 71 | private: | ||
| 72 | size_t m_obj_size{}; | ||
| 73 | uintptr_t m_peak{}; | ||
| 74 | uintptr_t m_start{}; | ||
| 75 | uintptr_t m_end{}; | ||
| 76 | |||
| 77 | private: | ||
| 78 | void UpdatePeakImpl(uintptr_t obj) { | ||
| 79 | static_assert(std::atomic_ref<uintptr_t>::is_always_lock_free); | ||
| 80 | std::atomic_ref<uintptr_t> peak_ref(m_peak); | ||
| 81 | |||
| 82 | const uintptr_t alloc_peak = obj + this->GetObjectSize(); | ||
| 83 | uintptr_t cur_peak = m_peak; | ||
| 84 | do { | ||
| 85 | if (alloc_peak <= cur_peak) { | ||
| 86 | break; | ||
| 87 | } | ||
| 88 | } while (!peak_ref.compare_exchange_strong(cur_peak, alloc_peak)); | ||
| 89 | } | ||
| 90 | |||
| 68 | public: | 91 | public: |
| 69 | constexpr KSlabHeapBase() = default; | 92 | constexpr KSlabHeapBase() = default; |
| 70 | 93 | ||
| 71 | constexpr bool Contains(uintptr_t addr) const { | 94 | bool Contains(uintptr_t address) const { |
| 72 | return start <= addr && addr < end; | 95 | return m_start <= address && address < m_end; |
| 73 | } | 96 | } |
| 74 | 97 | ||
| 75 | constexpr std::size_t GetSlabHeapSize() const { | 98 | void Initialize(size_t obj_size, void* memory, size_t memory_size) { |
| 76 | return (end - start) / GetObjectSize(); | 99 | // Ensure we don't initialize a slab using null memory. |
| 77 | } | 100 | ASSERT(memory != nullptr); |
| 78 | 101 | ||
| 79 | constexpr std::size_t GetObjectSize() const { | 102 | // Set our object size. |
| 80 | return impl.GetObjectSize(); | 103 | m_obj_size = obj_size; |
| 81 | } | ||
| 82 | 104 | ||
| 83 | constexpr uintptr_t GetSlabHeapAddress() const { | 105 | // Initialize the base allocator. |
| 84 | return start; | 106 | KSlabHeapImpl::Initialize(); |
| 85 | } | 107 | |
| 108 | // Set our tracking variables. | ||
| 109 | const size_t num_obj = (memory_size / obj_size); | ||
| 110 | m_start = reinterpret_cast<uintptr_t>(memory); | ||
| 111 | m_end = m_start + num_obj * obj_size; | ||
| 112 | m_peak = m_start; | ||
| 86 | 113 | ||
| 87 | std::size_t GetObjectIndexImpl(const void* obj) const { | 114 | // Free the objects. |
| 88 | return (reinterpret_cast<uintptr_t>(obj) - start) / GetObjectSize(); | 115 | u8* cur = reinterpret_cast<u8*>(m_end); |
| 116 | |||
| 117 | for (size_t i = 0; i < num_obj; i++) { | ||
| 118 | cur -= obj_size; | ||
| 119 | KSlabHeapImpl::Free(cur); | ||
| 120 | } | ||
| 89 | } | 121 | } |
| 90 | 122 | ||
| 91 | std::size_t GetPeakIndex() const { | 123 | size_t GetSlabHeapSize() const { |
| 92 | return GetObjectIndexImpl(reinterpret_cast<const void*>(peak)); | 124 | return (m_end - m_start) / this->GetObjectSize(); |
| 93 | } | 125 | } |
| 94 | 126 | ||
| 95 | void* AllocateImpl() { | 127 | size_t GetObjectSize() const { |
| 96 | return impl.Allocate(); | 128 | return m_obj_size; |
| 97 | } | 129 | } |
| 98 | 130 | ||
| 99 | void FreeImpl(void* obj) { | 131 | void* Allocate() { |
| 100 | // Don't allow freeing an object that wasn't allocated from this heap | 132 | void* obj = KSlabHeapImpl::Allocate(); |
| 101 | ASSERT(Contains(reinterpret_cast<uintptr_t>(obj))); | ||
| 102 | 133 | ||
| 103 | impl.Free(obj); | 134 | return obj; |
| 104 | } | 135 | } |
| 105 | 136 | ||
| 106 | void InitializeImpl(std::size_t obj_size, void* memory, std::size_t memory_size) { | 137 | void Free(void* obj) { |
| 107 | // Ensure we don't initialize a slab using null memory | 138 | // Don't allow freeing an object that wasn't allocated from this heap. |
| 108 | ASSERT(memory != nullptr); | 139 | const bool contained = this->Contains(reinterpret_cast<uintptr_t>(obj)); |
| 109 | 140 | ASSERT(contained); | |
| 110 | // Initialize the base allocator | 141 | KSlabHeapImpl::Free(obj); |
| 111 | impl.Initialize(obj_size); | 142 | } |
| 112 | 143 | ||
| 113 | // Set our tracking variables | 144 | size_t GetObjectIndex(const void* obj) const { |
| 114 | const std::size_t num_obj = (memory_size / obj_size); | 145 | if constexpr (SupportDynamicExpansion) { |
| 115 | start = reinterpret_cast<uintptr_t>(memory); | 146 | if (!this->Contains(reinterpret_cast<uintptr_t>(obj))) { |
| 116 | end = start + num_obj * obj_size; | 147 | return std::numeric_limits<size_t>::max(); |
| 117 | peak = start; | 148 | } |
| 149 | } | ||
| 118 | 150 | ||
| 119 | // Free the objects | 151 | return (reinterpret_cast<uintptr_t>(obj) - m_start) / this->GetObjectSize(); |
| 120 | u8* cur = reinterpret_cast<u8*>(end); | 152 | } |
| 121 | 153 | ||
| 122 | for (std::size_t i{}; i < num_obj; i++) { | 154 | size_t GetPeakIndex() const { |
| 123 | cur -= obj_size; | 155 | return this->GetObjectIndex(reinterpret_cast<const void*>(m_peak)); |
| 124 | impl.Free(cur); | ||
| 125 | } | ||
| 126 | } | 156 | } |
| 127 | 157 | ||
| 128 | private: | 158 | uintptr_t GetSlabHeapAddress() const { |
| 129 | using Impl = impl::KSlabHeapImpl; | 159 | return m_start; |
| 160 | } | ||
| 130 | 161 | ||
| 131 | Impl impl; | 162 | size_t GetNumRemaining() const { |
| 132 | uintptr_t peak{}; | 163 | // Only calculate the number of remaining objects under debug configuration. |
| 133 | uintptr_t start{}; | 164 | return 0; |
| 134 | uintptr_t end{}; | 165 | } |
| 135 | }; | 166 | }; |
| 136 | 167 | ||
| 137 | template <typename T> | 168 | template <typename T> |
| 138 | class KSlabHeap final : public KSlabHeapBase { | 169 | class KSlabHeap final : public KSlabHeapBase<false> { |
| 139 | public: | 170 | private: |
| 140 | enum class AllocationType { | 171 | using BaseHeap = KSlabHeapBase<false>; |
| 141 | Host, | ||
| 142 | Guest, | ||
| 143 | }; | ||
| 144 | 172 | ||
| 145 | explicit constexpr KSlabHeap(AllocationType allocation_type_ = AllocationType::Host) | 173 | public: |
| 146 | : KSlabHeapBase(), allocation_type{allocation_type_} {} | 174 | constexpr KSlabHeap() = default; |
| 147 | 175 | ||
| 148 | void Initialize(void* memory, std::size_t memory_size) { | 176 | void Initialize(void* memory, size_t memory_size) { |
| 149 | if (allocation_type == AllocationType::Guest) { | 177 | BaseHeap::Initialize(sizeof(T), memory, memory_size); |
| 150 | InitializeImpl(sizeof(T), memory, memory_size); | ||
| 151 | } | ||
| 152 | } | 178 | } |
| 153 | 179 | ||
| 154 | T* Allocate() { | 180 | T* Allocate() { |
| 155 | switch (allocation_type) { | 181 | T* obj = static_cast<T*>(BaseHeap::Allocate()); |
| 156 | case AllocationType::Host: | ||
| 157 | // Fallback for cases where we do not yet support allocating guest memory from the slab | ||
| 158 | // heap, such as for kernel memory regions. | ||
| 159 | return new T; | ||
| 160 | |||
| 161 | case AllocationType::Guest: | ||
| 162 | T* obj = static_cast<T*>(AllocateImpl()); | ||
| 163 | if (obj != nullptr) { | ||
| 164 | new (obj) T(); | ||
| 165 | } | ||
| 166 | return obj; | ||
| 167 | } | ||
| 168 | 182 | ||
| 169 | UNREACHABLE_MSG("Invalid AllocationType {}", allocation_type); | 183 | if (obj != nullptr) [[likely]] { |
| 170 | return nullptr; | 184 | std::construct_at(obj); |
| 185 | } | ||
| 186 | return obj; | ||
| 171 | } | 187 | } |
| 172 | 188 | ||
| 173 | T* AllocateWithKernel(KernelCore& kernel) { | 189 | T* Allocate(KernelCore& kernel) { |
| 174 | switch (allocation_type) { | 190 | T* obj = static_cast<T*>(BaseHeap::Allocate()); |
| 175 | case AllocationType::Host: | ||
| 176 | // Fallback for cases where we do not yet support allocating guest memory from the slab | ||
| 177 | // heap, such as for kernel memory regions. | ||
| 178 | return new T(kernel); | ||
| 179 | 191 | ||
| 180 | case AllocationType::Guest: | 192 | if (obj != nullptr) [[likely]] { |
| 181 | T* obj = static_cast<T*>(AllocateImpl()); | 193 | std::construct_at(obj, kernel); |
| 182 | if (obj != nullptr) { | ||
| 183 | new (obj) T(kernel); | ||
| 184 | } | ||
| 185 | return obj; | ||
| 186 | } | 194 | } |
| 187 | 195 | return obj; | |
| 188 | UNREACHABLE_MSG("Invalid AllocationType {}", allocation_type); | ||
| 189 | return nullptr; | ||
| 190 | } | 196 | } |
| 191 | 197 | ||
| 192 | void Free(T* obj) { | 198 | void Free(T* obj) { |
| 193 | switch (allocation_type) { | 199 | BaseHeap::Free(obj); |
| 194 | case AllocationType::Host: | ||
| 195 | // Fallback for cases where we do not yet support allocating guest memory from the slab | ||
| 196 | // heap, such as for kernel memory regions. | ||
| 197 | delete obj; | ||
| 198 | return; | ||
| 199 | |||
| 200 | case AllocationType::Guest: | ||
| 201 | FreeImpl(obj); | ||
| 202 | return; | ||
| 203 | } | ||
| 204 | |||
| 205 | UNREACHABLE_MSG("Invalid AllocationType {}", allocation_type); | ||
| 206 | } | 200 | } |
| 207 | 201 | ||
| 208 | constexpr std::size_t GetObjectIndex(const T* obj) const { | 202 | size_t GetObjectIndex(const T* obj) const { |
| 209 | return GetObjectIndexImpl(obj); | 203 | return BaseHeap::GetObjectIndex(obj); |
| 210 | } | 204 | } |
| 211 | |||
| 212 | private: | ||
| 213 | const AllocationType allocation_type; | ||
| 214 | }; | 205 | }; |
| 215 | 206 | ||
| 216 | } // namespace Kernel | 207 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp index de3ffe0c7..ba7f72c6b 100644 --- a/src/core/hle/kernel/k_thread.cpp +++ b/src/core/hle/kernel/k_thread.cpp | |||
| @@ -210,7 +210,7 @@ ResultCode KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_s | |||
| 210 | if (owner != nullptr) { | 210 | if (owner != nullptr) { |
| 211 | // Setup the TLS, if needed. | 211 | // Setup the TLS, if needed. |
| 212 | if (type == ThreadType::User) { | 212 | if (type == ThreadType::User) { |
| 213 | tls_address = owner->CreateTLSRegion(); | 213 | R_TRY(owner->CreateThreadLocalRegion(std::addressof(tls_address))); |
| 214 | } | 214 | } |
| 215 | 215 | ||
| 216 | parent = owner; | 216 | parent = owner; |
| @@ -305,7 +305,7 @@ void KThread::Finalize() { | |||
| 305 | 305 | ||
| 306 | // If the thread has a local region, delete it. | 306 | // If the thread has a local region, delete it. |
| 307 | if (tls_address != 0) { | 307 | if (tls_address != 0) { |
| 308 | parent->FreeTLSRegion(tls_address); | 308 | ASSERT(parent->DeleteThreadLocalRegion(tls_address).IsSuccess()); |
| 309 | } | 309 | } |
| 310 | 310 | ||
| 311 | // Release any waiters. | 311 | // Release any waiters. |
| @@ -326,6 +326,9 @@ void KThread::Finalize() { | |||
| 326 | } | 326 | } |
| 327 | } | 327 | } |
| 328 | 328 | ||
| 329 | // Release host emulation members. | ||
| 330 | host_context.reset(); | ||
| 331 | |||
| 329 | // Perform inherited finalization. | 332 | // Perform inherited finalization. |
| 330 | KSynchronizationObject::Finalize(); | 333 | KSynchronizationObject::Finalize(); |
| 331 | } | 334 | } |
diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h index d058db62c..f46db7298 100644 --- a/src/core/hle/kernel/k_thread.h +++ b/src/core/hle/kernel/k_thread.h | |||
| @@ -656,7 +656,7 @@ private: | |||
| 656 | static_assert(sizeof(SyncObjectBuffer::sync_objects) == sizeof(SyncObjectBuffer::handles)); | 656 | static_assert(sizeof(SyncObjectBuffer::sync_objects) == sizeof(SyncObjectBuffer::handles)); |
| 657 | 657 | ||
| 658 | struct ConditionVariableComparator { | 658 | struct ConditionVariableComparator { |
| 659 | struct LightCompareType { | 659 | struct RedBlackKeyType { |
| 660 | u64 cv_key{}; | 660 | u64 cv_key{}; |
| 661 | s32 priority{}; | 661 | s32 priority{}; |
| 662 | 662 | ||
| @@ -672,8 +672,8 @@ private: | |||
| 672 | template <typename T> | 672 | template <typename T> |
| 673 | requires( | 673 | requires( |
| 674 | std::same_as<T, KThread> || | 674 | std::same_as<T, KThread> || |
| 675 | std::same_as<T, LightCompareType>) static constexpr int Compare(const T& lhs, | 675 | std::same_as<T, RedBlackKeyType>) static constexpr int Compare(const T& lhs, |
| 676 | const KThread& rhs) { | 676 | const KThread& rhs) { |
| 677 | const u64 l_key = lhs.GetConditionVariableKey(); | 677 | const u64 l_key = lhs.GetConditionVariableKey(); |
| 678 | const u64 r_key = rhs.GetConditionVariableKey(); | 678 | const u64 r_key = rhs.GetConditionVariableKey(); |
| 679 | 679 | ||
diff --git a/src/core/hle/kernel/k_thread_local_page.cpp b/src/core/hle/kernel/k_thread_local_page.cpp new file mode 100644 index 000000000..17b233fca --- /dev/null +++ b/src/core/hle/kernel/k_thread_local_page.cpp | |||
| @@ -0,0 +1,66 @@ | |||
| 1 | // Copyright 2022 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "common/scope_exit.h" | ||
| 6 | #include "core/core.h" | ||
| 7 | #include "core/hle/kernel/k_memory_block.h" | ||
| 8 | #include "core/hle/kernel/k_page_table.h" | ||
| 9 | #include "core/hle/kernel/k_process.h" | ||
| 10 | #include "core/hle/kernel/k_thread_local_page.h" | ||
| 11 | #include "core/hle/kernel/kernel.h" | ||
| 12 | |||
| 13 | namespace Kernel { | ||
| 14 | |||
| 15 | ResultCode KThreadLocalPage::Initialize(KernelCore& kernel, KProcess* process) { | ||
| 16 | // Set that this process owns us. | ||
| 17 | m_owner = process; | ||
| 18 | m_kernel = &kernel; | ||
| 19 | |||
| 20 | // Allocate a new page. | ||
| 21 | KPageBuffer* page_buf = KPageBuffer::Allocate(kernel); | ||
| 22 | R_UNLESS(page_buf != nullptr, ResultOutOfMemory); | ||
| 23 | auto page_buf_guard = SCOPE_GUARD({ KPageBuffer::Free(kernel, page_buf); }); | ||
| 24 | |||
| 25 | // Map the address in. | ||
| 26 | const auto phys_addr = kernel.System().DeviceMemory().GetPhysicalAddr(page_buf); | ||
| 27 | R_TRY(m_owner->PageTable().MapPages(std::addressof(m_virt_addr), 1, PageSize, phys_addr, | ||
| 28 | KMemoryState::ThreadLocal, | ||
| 29 | KMemoryPermission::UserReadWrite)); | ||
| 30 | |||
| 31 | // We succeeded. | ||
| 32 | page_buf_guard.Cancel(); | ||
| 33 | |||
| 34 | return ResultSuccess; | ||
| 35 | } | ||
| 36 | |||
| 37 | ResultCode KThreadLocalPage::Finalize() { | ||
| 38 | // Get the physical address of the page. | ||
| 39 | const PAddr phys_addr = m_owner->PageTable().GetPhysicalAddr(m_virt_addr); | ||
| 40 | ASSERT(phys_addr); | ||
| 41 | |||
| 42 | // Unmap the page. | ||
| 43 | R_TRY(m_owner->PageTable().UnmapPages(this->GetAddress(), 1, KMemoryState::ThreadLocal)); | ||
| 44 | |||
| 45 | // Free the page. | ||
| 46 | KPageBuffer::Free(*m_kernel, KPageBuffer::FromPhysicalAddress(m_kernel->System(), phys_addr)); | ||
| 47 | |||
| 48 | return ResultSuccess; | ||
| 49 | } | ||
| 50 | |||
| 51 | VAddr KThreadLocalPage::Reserve() { | ||
| 52 | for (size_t i = 0; i < m_is_region_free.size(); i++) { | ||
| 53 | if (m_is_region_free[i]) { | ||
| 54 | m_is_region_free[i] = false; | ||
| 55 | return this->GetRegionAddress(i); | ||
| 56 | } | ||
| 57 | } | ||
| 58 | |||
| 59 | return 0; | ||
| 60 | } | ||
| 61 | |||
| 62 | void KThreadLocalPage::Release(VAddr addr) { | ||
| 63 | m_is_region_free[this->GetRegionIndex(addr)] = true; | ||
| 64 | } | ||
| 65 | |||
| 66 | } // namespace Kernel | ||
diff --git a/src/core/hle/kernel/k_thread_local_page.h b/src/core/hle/kernel/k_thread_local_page.h new file mode 100644 index 000000000..658c67e94 --- /dev/null +++ b/src/core/hle/kernel/k_thread_local_page.h | |||
| @@ -0,0 +1,112 @@ | |||
| 1 | // Copyright 2022 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <algorithm> | ||
| 8 | #include <array> | ||
| 9 | |||
| 10 | #include "common/alignment.h" | ||
| 11 | #include "common/assert.h" | ||
| 12 | #include "common/common_types.h" | ||
| 13 | #include "common/intrusive_red_black_tree.h" | ||
| 14 | #include "core/hle/kernel/k_page_buffer.h" | ||
| 15 | #include "core/hle/kernel/memory_types.h" | ||
| 16 | #include "core/hle/kernel/slab_helpers.h" | ||
| 17 | #include "core/hle/result.h" | ||
| 18 | |||
| 19 | namespace Kernel { | ||
| 20 | |||
| 21 | class KernelCore; | ||
| 22 | class KProcess; | ||
| 23 | |||
| 24 | class KThreadLocalPage final : public Common::IntrusiveRedBlackTreeBaseNode<KThreadLocalPage>, | ||
| 25 | public KSlabAllocated<KThreadLocalPage> { | ||
| 26 | public: | ||
| 27 | static constexpr size_t RegionsPerPage = PageSize / Svc::ThreadLocalRegionSize; | ||
| 28 | static_assert(RegionsPerPage > 0); | ||
| 29 | |||
| 30 | public: | ||
| 31 | constexpr explicit KThreadLocalPage(VAddr addr = {}) : m_virt_addr(addr) { | ||
| 32 | m_is_region_free.fill(true); | ||
| 33 | } | ||
| 34 | |||
| 35 | constexpr VAddr GetAddress() const { | ||
| 36 | return m_virt_addr; | ||
| 37 | } | ||
| 38 | |||
| 39 | ResultCode Initialize(KernelCore& kernel, KProcess* process); | ||
| 40 | ResultCode Finalize(); | ||
| 41 | |||
| 42 | VAddr Reserve(); | ||
| 43 | void Release(VAddr addr); | ||
| 44 | |||
| 45 | bool IsAllUsed() const { | ||
| 46 | return std::ranges::all_of(m_is_region_free.begin(), m_is_region_free.end(), | ||
| 47 | [](bool is_free) { return !is_free; }); | ||
| 48 | } | ||
| 49 | |||
| 50 | bool IsAllFree() const { | ||
| 51 | return std::ranges::all_of(m_is_region_free.begin(), m_is_region_free.end(), | ||
| 52 | [](bool is_free) { return is_free; }); | ||
| 53 | } | ||
| 54 | |||
| 55 | bool IsAnyUsed() const { | ||
| 56 | return !this->IsAllFree(); | ||
| 57 | } | ||
| 58 | |||
| 59 | bool IsAnyFree() const { | ||
| 60 | return !this->IsAllUsed(); | ||
| 61 | } | ||
| 62 | |||
| 63 | public: | ||
| 64 | using RedBlackKeyType = VAddr; | ||
| 65 | |||
| 66 | static constexpr RedBlackKeyType GetRedBlackKey(const RedBlackKeyType& v) { | ||
| 67 | return v; | ||
| 68 | } | ||
| 69 | static constexpr RedBlackKeyType GetRedBlackKey(const KThreadLocalPage& v) { | ||
| 70 | return v.GetAddress(); | ||
| 71 | } | ||
| 72 | |||
| 73 | template <typename T> | ||
| 74 | requires(std::same_as<T, KThreadLocalPage> || | ||
| 75 | std::same_as<T, RedBlackKeyType>) static constexpr int Compare(const T& lhs, | ||
| 76 | const KThreadLocalPage& | ||
| 77 | rhs) { | ||
| 78 | const VAddr lval = GetRedBlackKey(lhs); | ||
| 79 | const VAddr rval = GetRedBlackKey(rhs); | ||
| 80 | |||
| 81 | if (lval < rval) { | ||
| 82 | return -1; | ||
| 83 | } else if (lval == rval) { | ||
| 84 | return 0; | ||
| 85 | } else { | ||
| 86 | return 1; | ||
| 87 | } | ||
| 88 | } | ||
| 89 | |||
| 90 | private: | ||
| 91 | constexpr VAddr GetRegionAddress(size_t i) const { | ||
| 92 | return this->GetAddress() + i * Svc::ThreadLocalRegionSize; | ||
| 93 | } | ||
| 94 | |||
| 95 | constexpr bool Contains(VAddr addr) const { | ||
| 96 | return this->GetAddress() <= addr && addr < this->GetAddress() + PageSize; | ||
| 97 | } | ||
| 98 | |||
| 99 | constexpr size_t GetRegionIndex(VAddr addr) const { | ||
| 100 | ASSERT(Common::IsAligned(addr, Svc::ThreadLocalRegionSize)); | ||
| 101 | ASSERT(this->Contains(addr)); | ||
| 102 | return (addr - this->GetAddress()) / Svc::ThreadLocalRegionSize; | ||
| 103 | } | ||
| 104 | |||
| 105 | private: | ||
| 106 | VAddr m_virt_addr{}; | ||
| 107 | KProcess* m_owner{}; | ||
| 108 | KernelCore* m_kernel{}; | ||
| 109 | std::array<bool, RegionsPerPage> m_is_region_free{}; | ||
| 110 | }; | ||
| 111 | |||
| 112 | } // namespace Kernel | ||
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 49c0714ed..f9828bc43 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp | |||
| @@ -52,7 +52,7 @@ namespace Kernel { | |||
| 52 | 52 | ||
| 53 | struct KernelCore::Impl { | 53 | struct KernelCore::Impl { |
| 54 | explicit Impl(Core::System& system_, KernelCore& kernel_) | 54 | explicit Impl(Core::System& system_, KernelCore& kernel_) |
| 55 | : time_manager{system_}, object_list_container{kernel_}, | 55 | : time_manager{system_}, |
| 56 | service_threads_manager{1, "yuzu:ServiceThreadsManager"}, system{system_} {} | 56 | service_threads_manager{1, "yuzu:ServiceThreadsManager"}, system{system_} {} |
| 57 | 57 | ||
| 58 | void SetMulticore(bool is_multi) { | 58 | void SetMulticore(bool is_multi) { |
| @@ -60,6 +60,7 @@ struct KernelCore::Impl { | |||
| 60 | } | 60 | } |
| 61 | 61 | ||
| 62 | void Initialize(KernelCore& kernel) { | 62 | void Initialize(KernelCore& kernel) { |
| 63 | global_object_list_container = std::make_unique<KAutoObjectWithListContainer>(kernel); | ||
| 63 | global_scheduler_context = std::make_unique<Kernel::GlobalSchedulerContext>(kernel); | 64 | global_scheduler_context = std::make_unique<Kernel::GlobalSchedulerContext>(kernel); |
| 64 | global_handle_table = std::make_unique<Kernel::KHandleTable>(kernel); | 65 | global_handle_table = std::make_unique<Kernel::KHandleTable>(kernel); |
| 65 | global_handle_table->Initialize(KHandleTable::MaxTableSize); | 66 | global_handle_table->Initialize(KHandleTable::MaxTableSize); |
| @@ -70,14 +71,13 @@ struct KernelCore::Impl { | |||
| 70 | 71 | ||
| 71 | // Derive the initial memory layout from the emulated board | 72 | // Derive the initial memory layout from the emulated board |
| 72 | Init::InitializeSlabResourceCounts(kernel); | 73 | Init::InitializeSlabResourceCounts(kernel); |
| 73 | KMemoryLayout memory_layout; | 74 | DeriveInitialMemoryLayout(); |
| 74 | DeriveInitialMemoryLayout(memory_layout); | 75 | Init::InitializeSlabHeaps(system, *memory_layout); |
| 75 | Init::InitializeSlabHeaps(system, memory_layout); | ||
| 76 | 76 | ||
| 77 | // Initialize kernel memory and resources. | 77 | // Initialize kernel memory and resources. |
| 78 | InitializeSystemResourceLimit(kernel, system.CoreTiming(), memory_layout); | 78 | InitializeSystemResourceLimit(kernel, system.CoreTiming()); |
| 79 | InitializeMemoryLayout(memory_layout); | 79 | InitializeMemoryLayout(); |
| 80 | InitializePageSlab(); | 80 | Init::InitializeKPageBufferSlabHeap(system); |
| 81 | InitializeSchedulers(); | 81 | InitializeSchedulers(); |
| 82 | InitializeSuspendThreads(); | 82 | InitializeSuspendThreads(); |
| 83 | InitializePreemption(kernel); | 83 | InitializePreemption(kernel); |
| @@ -108,19 +108,6 @@ struct KernelCore::Impl { | |||
| 108 | for (auto* server_port : server_ports_) { | 108 | for (auto* server_port : server_ports_) { |
| 109 | server_port->Close(); | 109 | server_port->Close(); |
| 110 | } | 110 | } |
| 111 | // Close all open server sessions. | ||
| 112 | std::unordered_set<KServerSession*> server_sessions_; | ||
| 113 | { | ||
| 114 | std::lock_guard lk(server_sessions_lock); | ||
| 115 | server_sessions_ = server_sessions; | ||
| 116 | server_sessions.clear(); | ||
| 117 | } | ||
| 118 | for (auto* server_session : server_sessions_) { | ||
| 119 | server_session->Close(); | ||
| 120 | } | ||
| 121 | |||
| 122 | // Ensure that the object list container is finalized and properly shutdown. | ||
| 123 | object_list_container.Finalize(); | ||
| 124 | 111 | ||
| 125 | // Ensures all service threads gracefully shutdown. | 112 | // Ensures all service threads gracefully shutdown. |
| 126 | ClearServiceThreads(); | 113 | ClearServiceThreads(); |
| @@ -195,11 +182,15 @@ struct KernelCore::Impl { | |||
| 195 | { | 182 | { |
| 196 | std::lock_guard lk(registered_objects_lock); | 183 | std::lock_guard lk(registered_objects_lock); |
| 197 | if (registered_objects.size()) { | 184 | if (registered_objects.size()) { |
| 198 | LOG_WARNING(Kernel, "{} kernel objects were dangling on shutdown!", | 185 | LOG_DEBUG(Kernel, "{} kernel objects were dangling on shutdown!", |
| 199 | registered_objects.size()); | 186 | registered_objects.size()); |
| 200 | registered_objects.clear(); | 187 | registered_objects.clear(); |
| 201 | } | 188 | } |
| 202 | } | 189 | } |
| 190 | |||
| 191 | // Ensure that the object list container is finalized and properly shutdown. | ||
| 192 | global_object_list_container->Finalize(); | ||
| 193 | global_object_list_container.reset(); | ||
| 203 | } | 194 | } |
| 204 | 195 | ||
| 205 | void InitializePhysicalCores() { | 196 | void InitializePhysicalCores() { |
| @@ -219,12 +210,11 @@ struct KernelCore::Impl { | |||
| 219 | 210 | ||
| 220 | // Creates the default system resource limit | 211 | // Creates the default system resource limit |
| 221 | void InitializeSystemResourceLimit(KernelCore& kernel, | 212 | void InitializeSystemResourceLimit(KernelCore& kernel, |
| 222 | const Core::Timing::CoreTiming& core_timing, | 213 | const Core::Timing::CoreTiming& core_timing) { |
| 223 | const KMemoryLayout& memory_layout) { | ||
| 224 | system_resource_limit = KResourceLimit::Create(system.Kernel()); | 214 | system_resource_limit = KResourceLimit::Create(system.Kernel()); |
| 225 | system_resource_limit->Initialize(&core_timing); | 215 | system_resource_limit->Initialize(&core_timing); |
| 226 | 216 | ||
| 227 | const auto [total_size, kernel_size] = memory_layout.GetTotalAndKernelMemorySizes(); | 217 | const auto [total_size, kernel_size] = memory_layout->GetTotalAndKernelMemorySizes(); |
| 228 | 218 | ||
| 229 | // If setting the default system values fails, then something seriously wrong has occurred. | 219 | // If setting the default system values fails, then something seriously wrong has occurred. |
| 230 | ASSERT(system_resource_limit->SetLimitValue(LimitableResource::PhysicalMemory, total_size) | 220 | ASSERT(system_resource_limit->SetLimitValue(LimitableResource::PhysicalMemory, total_size) |
| @@ -240,13 +230,6 @@ struct KernelCore::Impl { | |||
| 240 | constexpr u64 secure_applet_memory_size{4_MiB}; | 230 | constexpr u64 secure_applet_memory_size{4_MiB}; |
| 241 | ASSERT(system_resource_limit->Reserve(LimitableResource::PhysicalMemory, | 231 | ASSERT(system_resource_limit->Reserve(LimitableResource::PhysicalMemory, |
| 242 | secure_applet_memory_size)); | 232 | secure_applet_memory_size)); |
| 243 | |||
| 244 | // This memory seems to be reserved on hardware, but is not reserved/used by yuzu. | ||
| 245 | // Likely Horizon OS reserved memory | ||
| 246 | // TODO(ameerj): Derive the memory rather than hardcode it. | ||
| 247 | constexpr u64 unknown_reserved_memory{0x2f896000}; | ||
| 248 | ASSERT(system_resource_limit->Reserve(LimitableResource::PhysicalMemory, | ||
| 249 | unknown_reserved_memory)); | ||
| 250 | } | 233 | } |
| 251 | 234 | ||
| 252 | void InitializePreemption(KernelCore& kernel) { | 235 | void InitializePreemption(KernelCore& kernel) { |
| @@ -300,15 +283,16 @@ struct KernelCore::Impl { | |||
| 300 | 283 | ||
| 301 | // Gets the dummy KThread for the caller, allocating a new one if this is the first time | 284 | // Gets the dummy KThread for the caller, allocating a new one if this is the first time |
| 302 | KThread* GetHostDummyThread() { | 285 | KThread* GetHostDummyThread() { |
| 303 | auto make_thread = [this]() { | 286 | auto initialize = [this](KThread* thread) { |
| 304 | KThread* thread = KThread::Create(system.Kernel()); | ||
| 305 | ASSERT(KThread::InitializeDummyThread(thread).IsSuccess()); | 287 | ASSERT(KThread::InitializeDummyThread(thread).IsSuccess()); |
| 306 | thread->SetName(fmt::format("DummyThread:{}", GetHostThreadId())); | 288 | thread->SetName(fmt::format("DummyThread:{}", GetHostThreadId())); |
| 307 | return thread; | 289 | return thread; |
| 308 | }; | 290 | }; |
| 309 | 291 | ||
| 310 | thread_local KThread* saved_thread = make_thread(); | 292 | thread_local auto raw_thread = KThread(system.Kernel()); |
| 311 | return saved_thread; | 293 | thread_local auto thread = initialize(&raw_thread); |
| 294 | |||
| 295 | return thread; | ||
| 312 | } | 296 | } |
| 313 | 297 | ||
| 314 | /// Registers a CPU core thread by allocating a host thread ID for it | 298 | /// Registers a CPU core thread by allocating a host thread ID for it |
| @@ -360,16 +344,18 @@ struct KernelCore::Impl { | |||
| 360 | return schedulers[thread_id]->GetCurrentThread(); | 344 | return schedulers[thread_id]->GetCurrentThread(); |
| 361 | } | 345 | } |
| 362 | 346 | ||
| 363 | void DeriveInitialMemoryLayout(KMemoryLayout& memory_layout) { | 347 | void DeriveInitialMemoryLayout() { |
| 348 | memory_layout = std::make_unique<KMemoryLayout>(); | ||
| 349 | |||
| 364 | // Insert the root region for the virtual memory tree, from which all other regions will | 350 | // Insert the root region for the virtual memory tree, from which all other regions will |
| 365 | // derive. | 351 | // derive. |
| 366 | memory_layout.GetVirtualMemoryRegionTree().InsertDirectly( | 352 | memory_layout->GetVirtualMemoryRegionTree().InsertDirectly( |
| 367 | KernelVirtualAddressSpaceBase, | 353 | KernelVirtualAddressSpaceBase, |
| 368 | KernelVirtualAddressSpaceBase + KernelVirtualAddressSpaceSize - 1); | 354 | KernelVirtualAddressSpaceBase + KernelVirtualAddressSpaceSize - 1); |
| 369 | 355 | ||
| 370 | // Insert the root region for the physical memory tree, from which all other regions will | 356 | // Insert the root region for the physical memory tree, from which all other regions will |
| 371 | // derive. | 357 | // derive. |
| 372 | memory_layout.GetPhysicalMemoryRegionTree().InsertDirectly( | 358 | memory_layout->GetPhysicalMemoryRegionTree().InsertDirectly( |
| 373 | KernelPhysicalAddressSpaceBase, | 359 | KernelPhysicalAddressSpaceBase, |
| 374 | KernelPhysicalAddressSpaceBase + KernelPhysicalAddressSpaceSize - 1); | 360 | KernelPhysicalAddressSpaceBase + KernelPhysicalAddressSpaceSize - 1); |
| 375 | 361 | ||
| @@ -386,7 +372,7 @@ struct KernelCore::Impl { | |||
| 386 | if (!(kernel_region_start + KernelRegionSize - 1 <= KernelVirtualAddressSpaceLast)) { | 372 | if (!(kernel_region_start + KernelRegionSize - 1 <= KernelVirtualAddressSpaceLast)) { |
| 387 | kernel_region_size = KernelVirtualAddressSpaceEnd - kernel_region_start; | 373 | kernel_region_size = KernelVirtualAddressSpaceEnd - kernel_region_start; |
| 388 | } | 374 | } |
| 389 | ASSERT(memory_layout.GetVirtualMemoryRegionTree().Insert( | 375 | ASSERT(memory_layout->GetVirtualMemoryRegionTree().Insert( |
| 390 | kernel_region_start, kernel_region_size, KMemoryRegionType_Kernel)); | 376 | kernel_region_start, kernel_region_size, KMemoryRegionType_Kernel)); |
| 391 | 377 | ||
| 392 | // Setup the code region. | 378 | // Setup the code region. |
| @@ -395,11 +381,11 @@ struct KernelCore::Impl { | |||
| 395 | Common::AlignDown(code_start_virt_addr, CodeRegionAlign); | 381 | Common::AlignDown(code_start_virt_addr, CodeRegionAlign); |
| 396 | constexpr VAddr code_region_end = Common::AlignUp(code_end_virt_addr, CodeRegionAlign); | 382 | constexpr VAddr code_region_end = Common::AlignUp(code_end_virt_addr, CodeRegionAlign); |
| 397 | constexpr size_t code_region_size = code_region_end - code_region_start; | 383 | constexpr size_t code_region_size = code_region_end - code_region_start; |
| 398 | ASSERT(memory_layout.GetVirtualMemoryRegionTree().Insert( | 384 | ASSERT(memory_layout->GetVirtualMemoryRegionTree().Insert( |
| 399 | code_region_start, code_region_size, KMemoryRegionType_KernelCode)); | 385 | code_region_start, code_region_size, KMemoryRegionType_KernelCode)); |
| 400 | 386 | ||
| 401 | // Setup board-specific device physical regions. | 387 | // Setup board-specific device physical regions. |
| 402 | Init::SetupDevicePhysicalMemoryRegions(memory_layout); | 388 | Init::SetupDevicePhysicalMemoryRegions(*memory_layout); |
| 403 | 389 | ||
| 404 | // Determine the amount of space needed for the misc region. | 390 | // Determine the amount of space needed for the misc region. |
| 405 | size_t misc_region_needed_size; | 391 | size_t misc_region_needed_size; |
| @@ -408,7 +394,7 @@ struct KernelCore::Impl { | |||
| 408 | misc_region_needed_size = Core::Hardware::NUM_CPU_CORES * (3 * (PageSize + PageSize)); | 394 | misc_region_needed_size = Core::Hardware::NUM_CPU_CORES * (3 * (PageSize + PageSize)); |
| 409 | 395 | ||
| 410 | // Account for each auto-map device. | 396 | // Account for each auto-map device. |
| 411 | for (const auto& region : memory_layout.GetPhysicalMemoryRegionTree()) { | 397 | for (const auto& region : memory_layout->GetPhysicalMemoryRegionTree()) { |
| 412 | if (region.HasTypeAttribute(KMemoryRegionAttr_ShouldKernelMap)) { | 398 | if (region.HasTypeAttribute(KMemoryRegionAttr_ShouldKernelMap)) { |
| 413 | // Check that the region is valid. | 399 | // Check that the region is valid. |
| 414 | ASSERT(region.GetEndAddress() != 0); | 400 | ASSERT(region.GetEndAddress() != 0); |
| @@ -433,22 +419,22 @@ struct KernelCore::Impl { | |||
| 433 | 419 | ||
| 434 | // Setup the misc region. | 420 | // Setup the misc region. |
| 435 | const VAddr misc_region_start = | 421 | const VAddr misc_region_start = |
| 436 | memory_layout.GetVirtualMemoryRegionTree().GetRandomAlignedRegion( | 422 | memory_layout->GetVirtualMemoryRegionTree().GetRandomAlignedRegion( |
| 437 | misc_region_size, MiscRegionAlign, KMemoryRegionType_Kernel); | 423 | misc_region_size, MiscRegionAlign, KMemoryRegionType_Kernel); |
| 438 | ASSERT(memory_layout.GetVirtualMemoryRegionTree().Insert( | 424 | ASSERT(memory_layout->GetVirtualMemoryRegionTree().Insert( |
| 439 | misc_region_start, misc_region_size, KMemoryRegionType_KernelMisc)); | 425 | misc_region_start, misc_region_size, KMemoryRegionType_KernelMisc)); |
| 440 | 426 | ||
| 441 | // Setup the stack region. | 427 | // Setup the stack region. |
| 442 | constexpr size_t StackRegionSize = 14_MiB; | 428 | constexpr size_t StackRegionSize = 14_MiB; |
| 443 | constexpr size_t StackRegionAlign = KernelAslrAlignment; | 429 | constexpr size_t StackRegionAlign = KernelAslrAlignment; |
| 444 | const VAddr stack_region_start = | 430 | const VAddr stack_region_start = |
| 445 | memory_layout.GetVirtualMemoryRegionTree().GetRandomAlignedRegion( | 431 | memory_layout->GetVirtualMemoryRegionTree().GetRandomAlignedRegion( |
| 446 | StackRegionSize, StackRegionAlign, KMemoryRegionType_Kernel); | 432 | StackRegionSize, StackRegionAlign, KMemoryRegionType_Kernel); |
| 447 | ASSERT(memory_layout.GetVirtualMemoryRegionTree().Insert( | 433 | ASSERT(memory_layout->GetVirtualMemoryRegionTree().Insert( |
| 448 | stack_region_start, StackRegionSize, KMemoryRegionType_KernelStack)); | 434 | stack_region_start, StackRegionSize, KMemoryRegionType_KernelStack)); |
| 449 | 435 | ||
| 450 | // Determine the size of the resource region. | 436 | // Determine the size of the resource region. |
| 451 | const size_t resource_region_size = memory_layout.GetResourceRegionSizeForInit(); | 437 | const size_t resource_region_size = memory_layout->GetResourceRegionSizeForInit(); |
| 452 | 438 | ||
| 453 | // Determine the size of the slab region. | 439 | // Determine the size of the slab region. |
| 454 | const size_t slab_region_size = | 440 | const size_t slab_region_size = |
| @@ -465,23 +451,23 @@ struct KernelCore::Impl { | |||
| 465 | Common::AlignUp(code_end_phys_addr + slab_region_size, SlabRegionAlign) - | 451 | Common::AlignUp(code_end_phys_addr + slab_region_size, SlabRegionAlign) - |
| 466 | Common::AlignDown(code_end_phys_addr, SlabRegionAlign); | 452 | Common::AlignDown(code_end_phys_addr, SlabRegionAlign); |
| 467 | const VAddr slab_region_start = | 453 | const VAddr slab_region_start = |
| 468 | memory_layout.GetVirtualMemoryRegionTree().GetRandomAlignedRegion( | 454 | memory_layout->GetVirtualMemoryRegionTree().GetRandomAlignedRegion( |
| 469 | slab_region_needed_size, SlabRegionAlign, KMemoryRegionType_Kernel) + | 455 | slab_region_needed_size, SlabRegionAlign, KMemoryRegionType_Kernel) + |
| 470 | (code_end_phys_addr % SlabRegionAlign); | 456 | (code_end_phys_addr % SlabRegionAlign); |
| 471 | ASSERT(memory_layout.GetVirtualMemoryRegionTree().Insert( | 457 | ASSERT(memory_layout->GetVirtualMemoryRegionTree().Insert( |
| 472 | slab_region_start, slab_region_size, KMemoryRegionType_KernelSlab)); | 458 | slab_region_start, slab_region_size, KMemoryRegionType_KernelSlab)); |
| 473 | 459 | ||
| 474 | // Setup the temp region. | 460 | // Setup the temp region. |
| 475 | constexpr size_t TempRegionSize = 128_MiB; | 461 | constexpr size_t TempRegionSize = 128_MiB; |
| 476 | constexpr size_t TempRegionAlign = KernelAslrAlignment; | 462 | constexpr size_t TempRegionAlign = KernelAslrAlignment; |
| 477 | const VAddr temp_region_start = | 463 | const VAddr temp_region_start = |
| 478 | memory_layout.GetVirtualMemoryRegionTree().GetRandomAlignedRegion( | 464 | memory_layout->GetVirtualMemoryRegionTree().GetRandomAlignedRegion( |
| 479 | TempRegionSize, TempRegionAlign, KMemoryRegionType_Kernel); | 465 | TempRegionSize, TempRegionAlign, KMemoryRegionType_Kernel); |
| 480 | ASSERT(memory_layout.GetVirtualMemoryRegionTree().Insert(temp_region_start, TempRegionSize, | 466 | ASSERT(memory_layout->GetVirtualMemoryRegionTree().Insert(temp_region_start, TempRegionSize, |
| 481 | KMemoryRegionType_KernelTemp)); | 467 | KMemoryRegionType_KernelTemp)); |
| 482 | 468 | ||
| 483 | // Automatically map in devices that have auto-map attributes. | 469 | // Automatically map in devices that have auto-map attributes. |
| 484 | for (auto& region : memory_layout.GetPhysicalMemoryRegionTree()) { | 470 | for (auto& region : memory_layout->GetPhysicalMemoryRegionTree()) { |
| 485 | // We only care about kernel regions. | 471 | // We only care about kernel regions. |
| 486 | if (!region.IsDerivedFrom(KMemoryRegionType_Kernel)) { | 472 | if (!region.IsDerivedFrom(KMemoryRegionType_Kernel)) { |
| 487 | continue; | 473 | continue; |
| @@ -508,21 +494,21 @@ struct KernelCore::Impl { | |||
| 508 | const size_t map_size = | 494 | const size_t map_size = |
| 509 | Common::AlignUp(region.GetEndAddress(), PageSize) - map_phys_addr; | 495 | Common::AlignUp(region.GetEndAddress(), PageSize) - map_phys_addr; |
| 510 | const VAddr map_virt_addr = | 496 | const VAddr map_virt_addr = |
| 511 | memory_layout.GetVirtualMemoryRegionTree().GetRandomAlignedRegionWithGuard( | 497 | memory_layout->GetVirtualMemoryRegionTree().GetRandomAlignedRegionWithGuard( |
| 512 | map_size, PageSize, KMemoryRegionType_KernelMisc, PageSize); | 498 | map_size, PageSize, KMemoryRegionType_KernelMisc, PageSize); |
| 513 | ASSERT(memory_layout.GetVirtualMemoryRegionTree().Insert( | 499 | ASSERT(memory_layout->GetVirtualMemoryRegionTree().Insert( |
| 514 | map_virt_addr, map_size, KMemoryRegionType_KernelMiscMappedDevice)); | 500 | map_virt_addr, map_size, KMemoryRegionType_KernelMiscMappedDevice)); |
| 515 | region.SetPairAddress(map_virt_addr + region.GetAddress() - map_phys_addr); | 501 | region.SetPairAddress(map_virt_addr + region.GetAddress() - map_phys_addr); |
| 516 | } | 502 | } |
| 517 | 503 | ||
| 518 | Init::SetupDramPhysicalMemoryRegions(memory_layout); | 504 | Init::SetupDramPhysicalMemoryRegions(*memory_layout); |
| 519 | 505 | ||
| 520 | // Insert a physical region for the kernel code region. | 506 | // Insert a physical region for the kernel code region. |
| 521 | ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert( | 507 | ASSERT(memory_layout->GetPhysicalMemoryRegionTree().Insert( |
| 522 | code_start_phys_addr, code_region_size, KMemoryRegionType_DramKernelCode)); | 508 | code_start_phys_addr, code_region_size, KMemoryRegionType_DramKernelCode)); |
| 523 | 509 | ||
| 524 | // Insert a physical region for the kernel slab region. | 510 | // Insert a physical region for the kernel slab region. |
| 525 | ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert( | 511 | ASSERT(memory_layout->GetPhysicalMemoryRegionTree().Insert( |
| 526 | slab_start_phys_addr, slab_region_size, KMemoryRegionType_DramKernelSlab)); | 512 | slab_start_phys_addr, slab_region_size, KMemoryRegionType_DramKernelSlab)); |
| 527 | 513 | ||
| 528 | // Determine size available for kernel page table heaps, requiring > 8 MB. | 514 | // Determine size available for kernel page table heaps, requiring > 8 MB. |
| @@ -531,12 +517,12 @@ struct KernelCore::Impl { | |||
| 531 | ASSERT(page_table_heap_size / 4_MiB > 2); | 517 | ASSERT(page_table_heap_size / 4_MiB > 2); |
| 532 | 518 | ||
| 533 | // Insert a physical region for the kernel page table heap region | 519 | // Insert a physical region for the kernel page table heap region |
| 534 | ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert( | 520 | ASSERT(memory_layout->GetPhysicalMemoryRegionTree().Insert( |
| 535 | slab_end_phys_addr, page_table_heap_size, KMemoryRegionType_DramKernelPtHeap)); | 521 | slab_end_phys_addr, page_table_heap_size, KMemoryRegionType_DramKernelPtHeap)); |
| 536 | 522 | ||
| 537 | // All DRAM regions that we haven't tagged by this point will be mapped under the linear | 523 | // All DRAM regions that we haven't tagged by this point will be mapped under the linear |
| 538 | // mapping. Tag them. | 524 | // mapping. Tag them. |
| 539 | for (auto& region : memory_layout.GetPhysicalMemoryRegionTree()) { | 525 | for (auto& region : memory_layout->GetPhysicalMemoryRegionTree()) { |
| 540 | if (region.GetType() == KMemoryRegionType_Dram) { | 526 | if (region.GetType() == KMemoryRegionType_Dram) { |
| 541 | // Check that the region is valid. | 527 | // Check that the region is valid. |
| 542 | ASSERT(region.GetEndAddress() != 0); | 528 | ASSERT(region.GetEndAddress() != 0); |
| @@ -548,7 +534,7 @@ struct KernelCore::Impl { | |||
| 548 | 534 | ||
| 549 | // Get the linear region extents. | 535 | // Get the linear region extents. |
| 550 | const auto linear_extents = | 536 | const auto linear_extents = |
| 551 | memory_layout.GetPhysicalMemoryRegionTree().GetDerivedRegionExtents( | 537 | memory_layout->GetPhysicalMemoryRegionTree().GetDerivedRegionExtents( |
| 552 | KMemoryRegionAttr_LinearMapped); | 538 | KMemoryRegionAttr_LinearMapped); |
| 553 | ASSERT(linear_extents.GetEndAddress() != 0); | 539 | ASSERT(linear_extents.GetEndAddress() != 0); |
| 554 | 540 | ||
| @@ -560,7 +546,7 @@ struct KernelCore::Impl { | |||
| 560 | Common::AlignUp(linear_extents.GetEndAddress(), LinearRegionAlign) - | 546 | Common::AlignUp(linear_extents.GetEndAddress(), LinearRegionAlign) - |
| 561 | aligned_linear_phys_start; | 547 | aligned_linear_phys_start; |
| 562 | const VAddr linear_region_start = | 548 | const VAddr linear_region_start = |
| 563 | memory_layout.GetVirtualMemoryRegionTree().GetRandomAlignedRegionWithGuard( | 549 | memory_layout->GetVirtualMemoryRegionTree().GetRandomAlignedRegionWithGuard( |
| 564 | linear_region_size, LinearRegionAlign, KMemoryRegionType_None, LinearRegionAlign); | 550 | linear_region_size, LinearRegionAlign, KMemoryRegionType_None, LinearRegionAlign); |
| 565 | 551 | ||
| 566 | const u64 linear_region_phys_to_virt_diff = linear_region_start - aligned_linear_phys_start; | 552 | const u64 linear_region_phys_to_virt_diff = linear_region_start - aligned_linear_phys_start; |
| @@ -569,7 +555,7 @@ struct KernelCore::Impl { | |||
| 569 | { | 555 | { |
| 570 | PAddr cur_phys_addr = 0; | 556 | PAddr cur_phys_addr = 0; |
| 571 | u64 cur_size = 0; | 557 | u64 cur_size = 0; |
| 572 | for (auto& region : memory_layout.GetPhysicalMemoryRegionTree()) { | 558 | for (auto& region : memory_layout->GetPhysicalMemoryRegionTree()) { |
| 573 | if (!region.HasTypeAttribute(KMemoryRegionAttr_LinearMapped)) { | 559 | if (!region.HasTypeAttribute(KMemoryRegionAttr_LinearMapped)) { |
| 574 | continue; | 560 | continue; |
| 575 | } | 561 | } |
| @@ -588,55 +574,49 @@ struct KernelCore::Impl { | |||
| 588 | 574 | ||
| 589 | const VAddr region_virt_addr = | 575 | const VAddr region_virt_addr = |
| 590 | region.GetAddress() + linear_region_phys_to_virt_diff; | 576 | region.GetAddress() + linear_region_phys_to_virt_diff; |
| 591 | ASSERT(memory_layout.GetVirtualMemoryRegionTree().Insert( | 577 | ASSERT(memory_layout->GetVirtualMemoryRegionTree().Insert( |
| 592 | region_virt_addr, region.GetSize(), | 578 | region_virt_addr, region.GetSize(), |
| 593 | GetTypeForVirtualLinearMapping(region.GetType()))); | 579 | GetTypeForVirtualLinearMapping(region.GetType()))); |
| 594 | region.SetPairAddress(region_virt_addr); | 580 | region.SetPairAddress(region_virt_addr); |
| 595 | 581 | ||
| 596 | KMemoryRegion* virt_region = | 582 | KMemoryRegion* virt_region = |
| 597 | memory_layout.GetVirtualMemoryRegionTree().FindModifiable(region_virt_addr); | 583 | memory_layout->GetVirtualMemoryRegionTree().FindModifiable(region_virt_addr); |
| 598 | ASSERT(virt_region != nullptr); | 584 | ASSERT(virt_region != nullptr); |
| 599 | virt_region->SetPairAddress(region.GetAddress()); | 585 | virt_region->SetPairAddress(region.GetAddress()); |
| 600 | } | 586 | } |
| 601 | } | 587 | } |
| 602 | 588 | ||
| 603 | // Insert regions for the initial page table region. | 589 | // Insert regions for the initial page table region. |
| 604 | ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert( | 590 | ASSERT(memory_layout->GetPhysicalMemoryRegionTree().Insert( |
| 605 | resource_end_phys_addr, KernelPageTableHeapSize, KMemoryRegionType_DramKernelInitPt)); | 591 | resource_end_phys_addr, KernelPageTableHeapSize, KMemoryRegionType_DramKernelInitPt)); |
| 606 | ASSERT(memory_layout.GetVirtualMemoryRegionTree().Insert( | 592 | ASSERT(memory_layout->GetVirtualMemoryRegionTree().Insert( |
| 607 | resource_end_phys_addr + linear_region_phys_to_virt_diff, KernelPageTableHeapSize, | 593 | resource_end_phys_addr + linear_region_phys_to_virt_diff, KernelPageTableHeapSize, |
| 608 | KMemoryRegionType_VirtualDramKernelInitPt)); | 594 | KMemoryRegionType_VirtualDramKernelInitPt)); |
| 609 | 595 | ||
| 610 | // All linear-mapped DRAM regions that we haven't tagged by this point will be allocated to | 596 | // All linear-mapped DRAM regions that we haven't tagged by this point will be allocated to |
| 611 | // some pool partition. Tag them. | 597 | // some pool partition. Tag them. |
| 612 | for (auto& region : memory_layout.GetPhysicalMemoryRegionTree()) { | 598 | for (auto& region : memory_layout->GetPhysicalMemoryRegionTree()) { |
| 613 | if (region.GetType() == (KMemoryRegionType_Dram | KMemoryRegionAttr_LinearMapped)) { | 599 | if (region.GetType() == (KMemoryRegionType_Dram | KMemoryRegionAttr_LinearMapped)) { |
| 614 | region.SetType(KMemoryRegionType_DramPoolPartition); | 600 | region.SetType(KMemoryRegionType_DramPoolPartition); |
| 615 | } | 601 | } |
| 616 | } | 602 | } |
| 617 | 603 | ||
| 618 | // Setup all other memory regions needed to arrange the pool partitions. | 604 | // Setup all other memory regions needed to arrange the pool partitions. |
| 619 | Init::SetupPoolPartitionMemoryRegions(memory_layout); | 605 | Init::SetupPoolPartitionMemoryRegions(*memory_layout); |
| 620 | 606 | ||
| 621 | // Cache all linear regions in their own trees for faster access, later. | 607 | // Cache all linear regions in their own trees for faster access, later. |
| 622 | memory_layout.InitializeLinearMemoryRegionTrees(aligned_linear_phys_start, | 608 | memory_layout->InitializeLinearMemoryRegionTrees(aligned_linear_phys_start, |
| 623 | linear_region_start); | 609 | linear_region_start); |
| 624 | } | 610 | } |
| 625 | 611 | ||
| 626 | void InitializeMemoryLayout(const KMemoryLayout& memory_layout) { | 612 | void InitializeMemoryLayout() { |
| 627 | const auto system_pool = memory_layout.GetKernelSystemPoolRegionPhysicalExtents(); | 613 | const auto system_pool = memory_layout->GetKernelSystemPoolRegionPhysicalExtents(); |
| 628 | const auto applet_pool = memory_layout.GetKernelAppletPoolRegionPhysicalExtents(); | ||
| 629 | const auto application_pool = memory_layout.GetKernelApplicationPoolRegionPhysicalExtents(); | ||
| 630 | 614 | ||
| 631 | // Initialize memory managers | 615 | // Initialize the memory manager. |
| 632 | memory_manager = std::make_unique<KMemoryManager>(system); | 616 | memory_manager = std::make_unique<KMemoryManager>(system); |
| 633 | memory_manager->InitializeManager(KMemoryManager::Pool::Application, | 617 | const auto& management_region = memory_layout->GetPoolManagementRegion(); |
| 634 | application_pool.GetAddress(), | 618 | ASSERT(management_region.GetEndAddress() != 0); |
| 635 | application_pool.GetEndAddress()); | 619 | memory_manager->Initialize(management_region.GetAddress(), management_region.GetSize()); |
| 636 | memory_manager->InitializeManager(KMemoryManager::Pool::Applet, applet_pool.GetAddress(), | ||
| 637 | applet_pool.GetEndAddress()); | ||
| 638 | memory_manager->InitializeManager(KMemoryManager::Pool::System, system_pool.GetAddress(), | ||
| 639 | system_pool.GetEndAddress()); | ||
| 640 | 620 | ||
| 641 | // Setup memory regions for emulated processes | 621 | // Setup memory regions for emulated processes |
| 642 | // TODO(bunnei): These should not be hardcoded regions initialized within the kernel | 622 | // TODO(bunnei): These should not be hardcoded regions initialized within the kernel |
| @@ -673,22 +653,6 @@ struct KernelCore::Impl { | |||
| 673 | time_phys_addr, time_size, "Time:SharedMemory"); | 653 | time_phys_addr, time_size, "Time:SharedMemory"); |
| 674 | } | 654 | } |
| 675 | 655 | ||
| 676 | void InitializePageSlab() { | ||
| 677 | // Allocate slab heaps | ||
| 678 | user_slab_heap_pages = | ||
| 679 | std::make_unique<KSlabHeap<Page>>(KSlabHeap<Page>::AllocationType::Guest); | ||
| 680 | |||
| 681 | // TODO(ameerj): This should be derived, not hardcoded within the kernel | ||
| 682 | constexpr u64 user_slab_heap_size{0x3de000}; | ||
| 683 | // Reserve slab heaps | ||
| 684 | ASSERT( | ||
| 685 | system_resource_limit->Reserve(LimitableResource::PhysicalMemory, user_slab_heap_size)); | ||
| 686 | // Initialize slab heap | ||
| 687 | user_slab_heap_pages->Initialize( | ||
| 688 | system.DeviceMemory().GetPointer(Core::DramMemoryMap::SlabHeapBase), | ||
| 689 | user_slab_heap_size); | ||
| 690 | } | ||
| 691 | |||
| 692 | KClientPort* CreateNamedServicePort(std::string name) { | 656 | KClientPort* CreateNamedServicePort(std::string name) { |
| 693 | auto search = service_interface_factory.find(name); | 657 | auto search = service_interface_factory.find(name); |
| 694 | if (search == service_interface_factory.end()) { | 658 | if (search == service_interface_factory.end()) { |
| @@ -726,7 +690,6 @@ struct KernelCore::Impl { | |||
| 726 | } | 690 | } |
| 727 | 691 | ||
| 728 | std::mutex server_ports_lock; | 692 | std::mutex server_ports_lock; |
| 729 | std::mutex server_sessions_lock; | ||
| 730 | std::mutex registered_objects_lock; | 693 | std::mutex registered_objects_lock; |
| 731 | std::mutex registered_in_use_objects_lock; | 694 | std::mutex registered_in_use_objects_lock; |
| 732 | 695 | ||
| @@ -750,14 +713,13 @@ struct KernelCore::Impl { | |||
| 750 | // stores all the objects in place. | 713 | // stores all the objects in place. |
| 751 | std::unique_ptr<KHandleTable> global_handle_table; | 714 | std::unique_ptr<KHandleTable> global_handle_table; |
| 752 | 715 | ||
| 753 | KAutoObjectWithListContainer object_list_container; | 716 | std::unique_ptr<KAutoObjectWithListContainer> global_object_list_container; |
| 754 | 717 | ||
| 755 | /// Map of named ports managed by the kernel, which can be retrieved using | 718 | /// Map of named ports managed by the kernel, which can be retrieved using |
| 756 | /// the ConnectToPort SVC. | 719 | /// the ConnectToPort SVC. |
| 757 | std::unordered_map<std::string, ServiceInterfaceFactory> service_interface_factory; | 720 | std::unordered_map<std::string, ServiceInterfaceFactory> service_interface_factory; |
| 758 | NamedPortTable named_ports; | 721 | NamedPortTable named_ports; |
| 759 | std::unordered_set<KServerPort*> server_ports; | 722 | std::unordered_set<KServerPort*> server_ports; |
| 760 | std::unordered_set<KServerSession*> server_sessions; | ||
| 761 | std::unordered_set<KAutoObject*> registered_objects; | 723 | std::unordered_set<KAutoObject*> registered_objects; |
| 762 | std::unordered_set<KAutoObject*> registered_in_use_objects; | 724 | std::unordered_set<KAutoObject*> registered_in_use_objects; |
| 763 | 725 | ||
| @@ -769,7 +731,6 @@ struct KernelCore::Impl { | |||
| 769 | 731 | ||
| 770 | // Kernel memory management | 732 | // Kernel memory management |
| 771 | std::unique_ptr<KMemoryManager> memory_manager; | 733 | std::unique_ptr<KMemoryManager> memory_manager; |
| 772 | std::unique_ptr<KSlabHeap<Page>> user_slab_heap_pages; | ||
| 773 | 734 | ||
| 774 | // Shared memory for services | 735 | // Shared memory for services |
| 775 | Kernel::KSharedMemory* hid_shared_mem{}; | 736 | Kernel::KSharedMemory* hid_shared_mem{}; |
| @@ -777,6 +738,9 @@ struct KernelCore::Impl { | |||
| 777 | Kernel::KSharedMemory* irs_shared_mem{}; | 738 | Kernel::KSharedMemory* irs_shared_mem{}; |
| 778 | Kernel::KSharedMemory* time_shared_mem{}; | 739 | Kernel::KSharedMemory* time_shared_mem{}; |
| 779 | 740 | ||
| 741 | // Memory layout | ||
| 742 | std::unique_ptr<KMemoryLayout> memory_layout; | ||
| 743 | |||
| 780 | // Threads used for services | 744 | // Threads used for services |
| 781 | std::unordered_set<std::shared_ptr<Kernel::ServiceThread>> service_threads; | 745 | std::unordered_set<std::shared_ptr<Kernel::ServiceThread>> service_threads; |
| 782 | Common::ThreadWorker service_threads_manager; | 746 | Common::ThreadWorker service_threads_manager; |
| @@ -925,11 +889,11 @@ const Core::ExclusiveMonitor& KernelCore::GetExclusiveMonitor() const { | |||
| 925 | } | 889 | } |
| 926 | 890 | ||
| 927 | KAutoObjectWithListContainer& KernelCore::ObjectListContainer() { | 891 | KAutoObjectWithListContainer& KernelCore::ObjectListContainer() { |
| 928 | return impl->object_list_container; | 892 | return *impl->global_object_list_container; |
| 929 | } | 893 | } |
| 930 | 894 | ||
| 931 | const KAutoObjectWithListContainer& KernelCore::ObjectListContainer() const { | 895 | const KAutoObjectWithListContainer& KernelCore::ObjectListContainer() const { |
| 932 | return impl->object_list_container; | 896 | return *impl->global_object_list_container; |
| 933 | } | 897 | } |
| 934 | 898 | ||
| 935 | void KernelCore::InvalidateAllInstructionCaches() { | 899 | void KernelCore::InvalidateAllInstructionCaches() { |
| @@ -959,16 +923,6 @@ KClientPort* KernelCore::CreateNamedServicePort(std::string name) { | |||
| 959 | return impl->CreateNamedServicePort(std::move(name)); | 923 | return impl->CreateNamedServicePort(std::move(name)); |
| 960 | } | 924 | } |
| 961 | 925 | ||
| 962 | void KernelCore::RegisterServerSession(KServerSession* server_session) { | ||
| 963 | std::lock_guard lk(impl->server_sessions_lock); | ||
| 964 | impl->server_sessions.insert(server_session); | ||
| 965 | } | ||
| 966 | |||
| 967 | void KernelCore::UnregisterServerSession(KServerSession* server_session) { | ||
| 968 | std::lock_guard lk(impl->server_sessions_lock); | ||
| 969 | impl->server_sessions.erase(server_session); | ||
| 970 | } | ||
| 971 | |||
| 972 | void KernelCore::RegisterKernelObject(KAutoObject* object) { | 926 | void KernelCore::RegisterKernelObject(KAutoObject* object) { |
| 973 | std::lock_guard lk(impl->registered_objects_lock); | 927 | std::lock_guard lk(impl->registered_objects_lock); |
| 974 | impl->registered_objects.insert(object); | 928 | impl->registered_objects.insert(object); |
| @@ -1041,14 +995,6 @@ const KMemoryManager& KernelCore::MemoryManager() const { | |||
| 1041 | return *impl->memory_manager; | 995 | return *impl->memory_manager; |
| 1042 | } | 996 | } |
| 1043 | 997 | ||
| 1044 | KSlabHeap<Page>& KernelCore::GetUserSlabHeapPages() { | ||
| 1045 | return *impl->user_slab_heap_pages; | ||
| 1046 | } | ||
| 1047 | |||
| 1048 | const KSlabHeap<Page>& KernelCore::GetUserSlabHeapPages() const { | ||
| 1049 | return *impl->user_slab_heap_pages; | ||
| 1050 | } | ||
| 1051 | |||
| 1052 | Kernel::KSharedMemory& KernelCore::GetHidSharedMem() { | 998 | Kernel::KSharedMemory& KernelCore::GetHidSharedMem() { |
| 1053 | return *impl->hid_shared_mem; | 999 | return *impl->hid_shared_mem; |
| 1054 | } | 1000 | } |
| @@ -1142,6 +1088,10 @@ const KWorkerTaskManager& KernelCore::WorkerTaskManager() const { | |||
| 1142 | return impl->worker_task_manager; | 1088 | return impl->worker_task_manager; |
| 1143 | } | 1089 | } |
| 1144 | 1090 | ||
| 1091 | const KMemoryLayout& KernelCore::MemoryLayout() const { | ||
| 1092 | return *impl->memory_layout; | ||
| 1093 | } | ||
| 1094 | |||
| 1145 | bool KernelCore::IsPhantomModeForSingleCore() const { | 1095 | bool KernelCore::IsPhantomModeForSingleCore() const { |
| 1146 | return impl->IsPhantomModeForSingleCore(); | 1096 | return impl->IsPhantomModeForSingleCore(); |
| 1147 | } | 1097 | } |
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 0e04fc3bb..7087bbda6 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h | |||
| @@ -41,7 +41,9 @@ class KClientSession; | |||
| 41 | class KEvent; | 41 | class KEvent; |
| 42 | class KHandleTable; | 42 | class KHandleTable; |
| 43 | class KLinkedListNode; | 43 | class KLinkedListNode; |
| 44 | class KMemoryLayout; | ||
| 44 | class KMemoryManager; | 45 | class KMemoryManager; |
| 46 | class KPageBuffer; | ||
| 45 | class KPort; | 47 | class KPort; |
| 46 | class KProcess; | 48 | class KProcess; |
| 47 | class KResourceLimit; | 49 | class KResourceLimit; |
| @@ -51,6 +53,7 @@ class KSession; | |||
| 51 | class KSharedMemory; | 53 | class KSharedMemory; |
| 52 | class KSharedMemoryInfo; | 54 | class KSharedMemoryInfo; |
| 53 | class KThread; | 55 | class KThread; |
| 56 | class KThreadLocalPage; | ||
| 54 | class KTransferMemory; | 57 | class KTransferMemory; |
| 55 | class KWorkerTaskManager; | 58 | class KWorkerTaskManager; |
| 56 | class KWritableEvent; | 59 | class KWritableEvent; |
| @@ -193,14 +196,6 @@ public: | |||
| 193 | /// Opens a port to a service previously registered with RegisterNamedService. | 196 | /// Opens a port to a service previously registered with RegisterNamedService. |
| 194 | KClientPort* CreateNamedServicePort(std::string name); | 197 | KClientPort* CreateNamedServicePort(std::string name); |
| 195 | 198 | ||
| 196 | /// Registers a server session with the gobal emulation state, to be freed on shutdown. This is | ||
| 197 | /// necessary because we do not emulate processes for HLE sessions. | ||
| 198 | void RegisterServerSession(KServerSession* server_session); | ||
| 199 | |||
| 200 | /// Unregisters a server session previously registered with RegisterServerSession when it was | ||
| 201 | /// destroyed during the current emulation session. | ||
| 202 | void UnregisterServerSession(KServerSession* server_session); | ||
| 203 | |||
| 204 | /// Registers all kernel objects with the global emulation state, this is purely for tracking | 199 | /// Registers all kernel objects with the global emulation state, this is purely for tracking |
| 205 | /// leaks after emulation has been shutdown. | 200 | /// leaks after emulation has been shutdown. |
| 206 | void RegisterKernelObject(KAutoObject* object); | 201 | void RegisterKernelObject(KAutoObject* object); |
| @@ -238,12 +233,6 @@ public: | |||
| 238 | /// Gets the virtual memory manager for the kernel. | 233 | /// Gets the virtual memory manager for the kernel. |
| 239 | const KMemoryManager& MemoryManager() const; | 234 | const KMemoryManager& MemoryManager() const; |
| 240 | 235 | ||
| 241 | /// Gets the slab heap allocated for user space pages. | ||
| 242 | KSlabHeap<Page>& GetUserSlabHeapPages(); | ||
| 243 | |||
| 244 | /// Gets the slab heap allocated for user space pages. | ||
| 245 | const KSlabHeap<Page>& GetUserSlabHeapPages() const; | ||
| 246 | |||
| 247 | /// Gets the shared memory object for HID services. | 236 | /// Gets the shared memory object for HID services. |
| 248 | Kernel::KSharedMemory& GetHidSharedMem(); | 237 | Kernel::KSharedMemory& GetHidSharedMem(); |
| 249 | 238 | ||
| @@ -335,6 +324,10 @@ public: | |||
| 335 | return slab_heap_container->writeable_event; | 324 | return slab_heap_container->writeable_event; |
| 336 | } else if constexpr (std::is_same_v<T, KCodeMemory>) { | 325 | } else if constexpr (std::is_same_v<T, KCodeMemory>) { |
| 337 | return slab_heap_container->code_memory; | 326 | return slab_heap_container->code_memory; |
| 327 | } else if constexpr (std::is_same_v<T, KPageBuffer>) { | ||
| 328 | return slab_heap_container->page_buffer; | ||
| 329 | } else if constexpr (std::is_same_v<T, KThreadLocalPage>) { | ||
| 330 | return slab_heap_container->thread_local_page; | ||
| 338 | } | 331 | } |
| 339 | } | 332 | } |
| 340 | 333 | ||
| @@ -350,6 +343,9 @@ public: | |||
| 350 | /// Gets the current worker task manager, used for dispatching KThread/KProcess tasks. | 343 | /// Gets the current worker task manager, used for dispatching KThread/KProcess tasks. |
| 351 | const KWorkerTaskManager& WorkerTaskManager() const; | 344 | const KWorkerTaskManager& WorkerTaskManager() const; |
| 352 | 345 | ||
| 346 | /// Gets the memory layout. | ||
| 347 | const KMemoryLayout& MemoryLayout() const; | ||
| 348 | |||
| 353 | private: | 349 | private: |
| 354 | friend class KProcess; | 350 | friend class KProcess; |
| 355 | friend class KThread; | 351 | friend class KThread; |
| @@ -393,6 +389,8 @@ private: | |||
| 393 | KSlabHeap<KTransferMemory> transfer_memory; | 389 | KSlabHeap<KTransferMemory> transfer_memory; |
| 394 | KSlabHeap<KWritableEvent> writeable_event; | 390 | KSlabHeap<KWritableEvent> writeable_event; |
| 395 | KSlabHeap<KCodeMemory> code_memory; | 391 | KSlabHeap<KCodeMemory> code_memory; |
| 392 | KSlabHeap<KPageBuffer> page_buffer; | ||
| 393 | KSlabHeap<KThreadLocalPage> thread_local_page; | ||
| 396 | }; | 394 | }; |
| 397 | 395 | ||
| 398 | std::unique_ptr<SlabHeapContainer> slab_heap_container; | 396 | std::unique_ptr<SlabHeapContainer> slab_heap_container; |
diff --git a/src/core/hle/kernel/service_thread.cpp b/src/core/hle/kernel/service_thread.cpp index 4eb3a5988..52d25b837 100644 --- a/src/core/hle/kernel/service_thread.cpp +++ b/src/core/hle/kernel/service_thread.cpp | |||
| @@ -49,12 +49,9 @@ ServiceThread::Impl::Impl(KernelCore& kernel, std::size_t num_threads, const std | |||
| 49 | return; | 49 | return; |
| 50 | } | 50 | } |
| 51 | 51 | ||
| 52 | // Allocate a dummy guest thread for this host thread. | ||
| 52 | kernel.RegisterHostThread(); | 53 | kernel.RegisterHostThread(); |
| 53 | 54 | ||
| 54 | // Ensure the dummy thread allocated for this host thread is closed on exit. | ||
| 55 | auto* dummy_thread = kernel.GetCurrentEmuThread(); | ||
| 56 | SCOPE_EXIT({ dummy_thread->Close(); }); | ||
| 57 | |||
| 58 | while (true) { | 55 | while (true) { |
| 59 | std::function<void()> task; | 56 | std::function<void()> task; |
| 60 | 57 | ||
diff --git a/src/core/hle/kernel/slab_helpers.h b/src/core/hle/kernel/slab_helpers.h index f1c11256e..dc1e48fc9 100644 --- a/src/core/hle/kernel/slab_helpers.h +++ b/src/core/hle/kernel/slab_helpers.h | |||
| @@ -59,7 +59,7 @@ class KAutoObjectWithSlabHeapAndContainer : public Base { | |||
| 59 | 59 | ||
| 60 | private: | 60 | private: |
| 61 | static Derived* Allocate(KernelCore& kernel) { | 61 | static Derived* Allocate(KernelCore& kernel) { |
| 62 | return kernel.SlabHeap<Derived>().AllocateWithKernel(kernel); | 62 | return kernel.SlabHeap<Derived>().Allocate(kernel); |
| 63 | } | 63 | } |
| 64 | 64 | ||
| 65 | static void Free(KernelCore& kernel, Derived* obj) { | 65 | static void Free(KernelCore& kernel, Derived* obj) { |
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 4f7aebf3f..839171e85 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp | |||
| @@ -396,7 +396,7 @@ static ResultCode GetProcessId(Core::System& system, u64* out_process_id, Handle | |||
| 396 | // Get the process id. | 396 | // Get the process id. |
| 397 | *out_process_id = process->GetId(); | 397 | *out_process_id = process->GetId(); |
| 398 | 398 | ||
| 399 | return ResultInvalidHandle; | 399 | return ResultSuccess; |
| 400 | } | 400 | } |
| 401 | 401 | ||
| 402 | static ResultCode GetProcessId32(Core::System& system, u32* out_process_id_low, | 402 | static ResultCode GetProcessId32(Core::System& system, u32* out_process_id_low, |
| @@ -645,6 +645,10 @@ static void OutputDebugString(Core::System& system, VAddr address, u64 len) { | |||
| 645 | LOG_DEBUG(Debug_Emulated, "{}", str); | 645 | LOG_DEBUG(Debug_Emulated, "{}", str); |
| 646 | } | 646 | } |
| 647 | 647 | ||
| 648 | static void OutputDebugString32(Core::System& system, u32 address, u32 len) { | ||
| 649 | OutputDebugString(system, address, len); | ||
| 650 | } | ||
| 651 | |||
| 648 | /// Gets system/memory information for the current process | 652 | /// Gets system/memory information for the current process |
| 649 | static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, Handle handle, | 653 | static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, Handle handle, |
| 650 | u64 info_sub_id) { | 654 | u64 info_sub_id) { |
| @@ -1404,7 +1408,7 @@ static ResultCode UnmapProcessMemory(Core::System& system, VAddr dst_address, Ha | |||
| 1404 | } | 1408 | } |
| 1405 | 1409 | ||
| 1406 | static ResultCode CreateCodeMemory(Core::System& system, Handle* out, VAddr address, size_t size) { | 1410 | static ResultCode CreateCodeMemory(Core::System& system, Handle* out, VAddr address, size_t size) { |
| 1407 | LOG_TRACE(Kernel_SVC, "called, handle_out=0x{:X}, address=0x{:X}, size=0x{:X}", | 1411 | LOG_TRACE(Kernel_SVC, "called, handle_out={}, address=0x{:X}, size=0x{:X}", |
| 1408 | static_cast<void*>(out), address, size); | 1412 | static_cast<void*>(out), address, size); |
| 1409 | // Get kernel instance. | 1413 | // Get kernel instance. |
| 1410 | auto& kernel = system.Kernel(); | 1414 | auto& kernel = system.Kernel(); |
| @@ -1438,6 +1442,10 @@ static ResultCode CreateCodeMemory(Core::System& system, Handle* out, VAddr addr | |||
| 1438 | return ResultSuccess; | 1442 | return ResultSuccess; |
| 1439 | } | 1443 | } |
| 1440 | 1444 | ||
| 1445 | static ResultCode CreateCodeMemory32(Core::System& system, Handle* out, u32 address, u32 size) { | ||
| 1446 | return CreateCodeMemory(system, out, address, size); | ||
| 1447 | } | ||
| 1448 | |||
| 1441 | static ResultCode ControlCodeMemory(Core::System& system, Handle code_memory_handle, u32 operation, | 1449 | static ResultCode ControlCodeMemory(Core::System& system, Handle code_memory_handle, u32 operation, |
| 1442 | VAddr address, size_t size, Svc::MemoryPermission perm) { | 1450 | VAddr address, size_t size, Svc::MemoryPermission perm) { |
| 1443 | 1451 | ||
| @@ -1517,6 +1525,12 @@ static ResultCode ControlCodeMemory(Core::System& system, Handle code_memory_han | |||
| 1517 | return ResultSuccess; | 1525 | return ResultSuccess; |
| 1518 | } | 1526 | } |
| 1519 | 1527 | ||
| 1528 | static ResultCode ControlCodeMemory32(Core::System& system, Handle code_memory_handle, | ||
| 1529 | u32 operation, u64 address, u64 size, | ||
| 1530 | Svc::MemoryPermission perm) { | ||
| 1531 | return ControlCodeMemory(system, code_memory_handle, operation, address, size, perm); | ||
| 1532 | } | ||
| 1533 | |||
| 1520 | static ResultCode QueryProcessMemory(Core::System& system, VAddr memory_info_address, | 1534 | static ResultCode QueryProcessMemory(Core::System& system, VAddr memory_info_address, |
| 1521 | VAddr page_info_address, Handle process_handle, | 1535 | VAddr page_info_address, Handle process_handle, |
| 1522 | VAddr address) { | 1536 | VAddr address) { |
| @@ -2318,7 +2332,7 @@ static ResultCode CreateEvent(Core::System& system, Handle* out_write, Handle* o | |||
| 2318 | R_UNLESS(event != nullptr, ResultOutOfResource); | 2332 | R_UNLESS(event != nullptr, ResultOutOfResource); |
| 2319 | 2333 | ||
| 2320 | // Initialize the event. | 2334 | // Initialize the event. |
| 2321 | event->Initialize("CreateEvent"); | 2335 | event->Initialize("CreateEvent", kernel.CurrentProcess()); |
| 2322 | 2336 | ||
| 2323 | // Commit the thread reservation. | 2337 | // Commit the thread reservation. |
| 2324 | event_reservation.Commit(); | 2338 | event_reservation.Commit(); |
| @@ -2559,9 +2573,9 @@ struct FunctionDef { | |||
| 2559 | } // namespace | 2573 | } // namespace |
| 2560 | 2574 | ||
| 2561 | static const FunctionDef SVC_Table_32[] = { | 2575 | static const FunctionDef SVC_Table_32[] = { |
| 2562 | {0x00, nullptr, "Unknown"}, | 2576 | {0x00, nullptr, "Unknown0"}, |
| 2563 | {0x01, SvcWrap32<SetHeapSize32>, "SetHeapSize32"}, | 2577 | {0x01, SvcWrap32<SetHeapSize32>, "SetHeapSize32"}, |
| 2564 | {0x02, nullptr, "Unknown"}, | 2578 | {0x02, nullptr, "SetMemoryPermission32"}, |
| 2565 | {0x03, SvcWrap32<SetMemoryAttribute32>, "SetMemoryAttribute32"}, | 2579 | {0x03, SvcWrap32<SetMemoryAttribute32>, "SetMemoryAttribute32"}, |
| 2566 | {0x04, SvcWrap32<MapMemory32>, "MapMemory32"}, | 2580 | {0x04, SvcWrap32<MapMemory32>, "MapMemory32"}, |
| 2567 | {0x05, SvcWrap32<UnmapMemory32>, "UnmapMemory32"}, | 2581 | {0x05, SvcWrap32<UnmapMemory32>, "UnmapMemory32"}, |
| @@ -2591,97 +2605,97 @@ static const FunctionDef SVC_Table_32[] = { | |||
| 2591 | {0x1d, SvcWrap32<SignalProcessWideKey32>, "SignalProcessWideKey32"}, | 2605 | {0x1d, SvcWrap32<SignalProcessWideKey32>, "SignalProcessWideKey32"}, |
| 2592 | {0x1e, SvcWrap32<GetSystemTick32>, "GetSystemTick32"}, | 2606 | {0x1e, SvcWrap32<GetSystemTick32>, "GetSystemTick32"}, |
| 2593 | {0x1f, SvcWrap32<ConnectToNamedPort32>, "ConnectToNamedPort32"}, | 2607 | {0x1f, SvcWrap32<ConnectToNamedPort32>, "ConnectToNamedPort32"}, |
| 2594 | {0x20, nullptr, "Unknown"}, | 2608 | {0x20, nullptr, "SendSyncRequestLight32"}, |
| 2595 | {0x21, SvcWrap32<SendSyncRequest32>, "SendSyncRequest32"}, | 2609 | {0x21, SvcWrap32<SendSyncRequest32>, "SendSyncRequest32"}, |
| 2596 | {0x22, nullptr, "SendSyncRequestWithUserBuffer32"}, | 2610 | {0x22, nullptr, "SendSyncRequestWithUserBuffer32"}, |
| 2597 | {0x23, nullptr, "Unknown"}, | 2611 | {0x23, nullptr, "SendAsyncRequestWithUserBuffer32"}, |
| 2598 | {0x24, SvcWrap32<GetProcessId32>, "GetProcessId32"}, | 2612 | {0x24, SvcWrap32<GetProcessId32>, "GetProcessId32"}, |
| 2599 | {0x25, SvcWrap32<GetThreadId32>, "GetThreadId32"}, | 2613 | {0x25, SvcWrap32<GetThreadId32>, "GetThreadId32"}, |
| 2600 | {0x26, SvcWrap32<Break32>, "Break32"}, | 2614 | {0x26, SvcWrap32<Break32>, "Break32"}, |
| 2601 | {0x27, nullptr, "OutputDebugString32"}, | 2615 | {0x27, SvcWrap32<OutputDebugString32>, "OutputDebugString32"}, |
| 2602 | {0x28, nullptr, "Unknown"}, | 2616 | {0x28, nullptr, "ReturnFromException32"}, |
| 2603 | {0x29, SvcWrap32<GetInfo32>, "GetInfo32"}, | 2617 | {0x29, SvcWrap32<GetInfo32>, "GetInfo32"}, |
| 2604 | {0x2a, nullptr, "Unknown"}, | 2618 | {0x2a, nullptr, "FlushEntireDataCache32"}, |
| 2605 | {0x2b, nullptr, "Unknown"}, | 2619 | {0x2b, nullptr, "FlushDataCache32"}, |
| 2606 | {0x2c, SvcWrap32<MapPhysicalMemory32>, "MapPhysicalMemory32"}, | 2620 | {0x2c, SvcWrap32<MapPhysicalMemory32>, "MapPhysicalMemory32"}, |
| 2607 | {0x2d, SvcWrap32<UnmapPhysicalMemory32>, "UnmapPhysicalMemory32"}, | 2621 | {0x2d, SvcWrap32<UnmapPhysicalMemory32>, "UnmapPhysicalMemory32"}, |
| 2608 | {0x2e, nullptr, "Unknown"}, | 2622 | {0x2e, nullptr, "GetDebugFutureThreadInfo32"}, |
| 2609 | {0x2f, nullptr, "Unknown"}, | 2623 | {0x2f, nullptr, "GetLastThreadInfo32"}, |
| 2610 | {0x30, nullptr, "Unknown"}, | 2624 | {0x30, nullptr, "GetResourceLimitLimitValue32"}, |
| 2611 | {0x31, nullptr, "Unknown"}, | 2625 | {0x31, nullptr, "GetResourceLimitCurrentValue32"}, |
| 2612 | {0x32, SvcWrap32<SetThreadActivity32>, "SetThreadActivity32"}, | 2626 | {0x32, SvcWrap32<SetThreadActivity32>, "SetThreadActivity32"}, |
| 2613 | {0x33, SvcWrap32<GetThreadContext32>, "GetThreadContext32"}, | 2627 | {0x33, SvcWrap32<GetThreadContext32>, "GetThreadContext32"}, |
| 2614 | {0x34, SvcWrap32<WaitForAddress32>, "WaitForAddress32"}, | 2628 | {0x34, SvcWrap32<WaitForAddress32>, "WaitForAddress32"}, |
| 2615 | {0x35, SvcWrap32<SignalToAddress32>, "SignalToAddress32"}, | 2629 | {0x35, SvcWrap32<SignalToAddress32>, "SignalToAddress32"}, |
| 2616 | {0x36, SvcWrap32<SynchronizePreemptionState>, "SynchronizePreemptionState32"}, | 2630 | {0x36, SvcWrap32<SynchronizePreemptionState>, "SynchronizePreemptionState32"}, |
| 2617 | {0x37, nullptr, "Unknown"}, | 2631 | {0x37, nullptr, "GetResourceLimitPeakValue32"}, |
| 2618 | {0x38, nullptr, "Unknown"}, | 2632 | {0x38, nullptr, "Unknown38"}, |
| 2619 | {0x39, nullptr, "Unknown"}, | 2633 | {0x39, nullptr, "CreateIoPool32"}, |
| 2620 | {0x3a, nullptr, "Unknown"}, | 2634 | {0x3a, nullptr, "CreateIoRegion32"}, |
| 2621 | {0x3b, nullptr, "Unknown"}, | 2635 | {0x3b, nullptr, "Unknown3b"}, |
| 2622 | {0x3c, nullptr, "Unknown"}, | 2636 | {0x3c, nullptr, "KernelDebug32"}, |
| 2623 | {0x3d, nullptr, "Unknown"}, | 2637 | {0x3d, nullptr, "ChangeKernelTraceState32"}, |
| 2624 | {0x3e, nullptr, "Unknown"}, | 2638 | {0x3e, nullptr, "Unknown3e"}, |
| 2625 | {0x3f, nullptr, "Unknown"}, | 2639 | {0x3f, nullptr, "Unknown3f"}, |
| 2626 | {0x40, nullptr, "CreateSession32"}, | 2640 | {0x40, nullptr, "CreateSession32"}, |
| 2627 | {0x41, nullptr, "AcceptSession32"}, | 2641 | {0x41, nullptr, "AcceptSession32"}, |
| 2628 | {0x42, nullptr, "Unknown"}, | 2642 | {0x42, nullptr, "ReplyAndReceiveLight32"}, |
| 2629 | {0x43, nullptr, "ReplyAndReceive32"}, | 2643 | {0x43, nullptr, "ReplyAndReceive32"}, |
| 2630 | {0x44, nullptr, "Unknown"}, | 2644 | {0x44, nullptr, "ReplyAndReceiveWithUserBuffer32"}, |
| 2631 | {0x45, SvcWrap32<CreateEvent32>, "CreateEvent32"}, | 2645 | {0x45, SvcWrap32<CreateEvent32>, "CreateEvent32"}, |
| 2632 | {0x46, nullptr, "Unknown"}, | 2646 | {0x46, nullptr, "MapIoRegion32"}, |
| 2633 | {0x47, nullptr, "Unknown"}, | 2647 | {0x47, nullptr, "UnmapIoRegion32"}, |
| 2634 | {0x48, nullptr, "Unknown"}, | 2648 | {0x48, nullptr, "MapPhysicalMemoryUnsafe32"}, |
| 2635 | {0x49, nullptr, "Unknown"}, | 2649 | {0x49, nullptr, "UnmapPhysicalMemoryUnsafe32"}, |
| 2636 | {0x4a, nullptr, "Unknown"}, | 2650 | {0x4a, nullptr, "SetUnsafeLimit32"}, |
| 2637 | {0x4b, nullptr, "Unknown"}, | 2651 | {0x4b, SvcWrap32<CreateCodeMemory32>, "CreateCodeMemory32"}, |
| 2638 | {0x4c, nullptr, "Unknown"}, | 2652 | {0x4c, SvcWrap32<ControlCodeMemory32>, "ControlCodeMemory32"}, |
| 2639 | {0x4d, nullptr, "Unknown"}, | 2653 | {0x4d, nullptr, "SleepSystem32"}, |
| 2640 | {0x4e, nullptr, "Unknown"}, | 2654 | {0x4e, nullptr, "ReadWriteRegister32"}, |
| 2641 | {0x4f, nullptr, "Unknown"}, | 2655 | {0x4f, nullptr, "SetProcessActivity32"}, |
| 2642 | {0x50, nullptr, "Unknown"}, | 2656 | {0x50, nullptr, "CreateSharedMemory32"}, |
| 2643 | {0x51, nullptr, "Unknown"}, | 2657 | {0x51, nullptr, "MapTransferMemory32"}, |
| 2644 | {0x52, nullptr, "Unknown"}, | 2658 | {0x52, nullptr, "UnmapTransferMemory32"}, |
| 2645 | {0x53, nullptr, "Unknown"}, | 2659 | {0x53, nullptr, "CreateInterruptEvent32"}, |
| 2646 | {0x54, nullptr, "Unknown"}, | 2660 | {0x54, nullptr, "QueryPhysicalAddress32"}, |
| 2647 | {0x55, nullptr, "Unknown"}, | 2661 | {0x55, nullptr, "QueryIoMapping32"}, |
| 2648 | {0x56, nullptr, "Unknown"}, | 2662 | {0x56, nullptr, "CreateDeviceAddressSpace32"}, |
| 2649 | {0x57, nullptr, "Unknown"}, | 2663 | {0x57, nullptr, "AttachDeviceAddressSpace32"}, |
| 2650 | {0x58, nullptr, "Unknown"}, | 2664 | {0x58, nullptr, "DetachDeviceAddressSpace32"}, |
| 2651 | {0x59, nullptr, "Unknown"}, | 2665 | {0x59, nullptr, "MapDeviceAddressSpaceByForce32"}, |
| 2652 | {0x5a, nullptr, "Unknown"}, | 2666 | {0x5a, nullptr, "MapDeviceAddressSpaceAligned32"}, |
| 2653 | {0x5b, nullptr, "Unknown"}, | 2667 | {0x5b, nullptr, "MapDeviceAddressSpace32"}, |
| 2654 | {0x5c, nullptr, "Unknown"}, | 2668 | {0x5c, nullptr, "UnmapDeviceAddressSpace32"}, |
| 2655 | {0x5d, nullptr, "Unknown"}, | 2669 | {0x5d, nullptr, "InvalidateProcessDataCache32"}, |
| 2656 | {0x5e, nullptr, "Unknown"}, | 2670 | {0x5e, nullptr, "StoreProcessDataCache32"}, |
| 2657 | {0x5F, SvcWrap32<FlushProcessDataCache32>, "FlushProcessDataCache32"}, | 2671 | {0x5F, SvcWrap32<FlushProcessDataCache32>, "FlushProcessDataCache32"}, |
| 2658 | {0x60, nullptr, "Unknown"}, | 2672 | {0x60, nullptr, "StoreProcessDataCache32"}, |
| 2659 | {0x61, nullptr, "Unknown"}, | 2673 | {0x61, nullptr, "BreakDebugProcess32"}, |
| 2660 | {0x62, nullptr, "Unknown"}, | 2674 | {0x62, nullptr, "TerminateDebugProcess32"}, |
| 2661 | {0x63, nullptr, "Unknown"}, | 2675 | {0x63, nullptr, "GetDebugEvent32"}, |
| 2662 | {0x64, nullptr, "Unknown"}, | 2676 | {0x64, nullptr, "ContinueDebugEvent32"}, |
| 2663 | {0x65, nullptr, "GetProcessList32"}, | 2677 | {0x65, nullptr, "GetProcessList32"}, |
| 2664 | {0x66, nullptr, "Unknown"}, | 2678 | {0x66, nullptr, "GetThreadList"}, |
| 2665 | {0x67, nullptr, "Unknown"}, | 2679 | {0x67, nullptr, "GetDebugThreadContext32"}, |
| 2666 | {0x68, nullptr, "Unknown"}, | 2680 | {0x68, nullptr, "SetDebugThreadContext32"}, |
| 2667 | {0x69, nullptr, "Unknown"}, | 2681 | {0x69, nullptr, "QueryDebugProcessMemory32"}, |
| 2668 | {0x6A, nullptr, "Unknown"}, | 2682 | {0x6A, nullptr, "ReadDebugProcessMemory32"}, |
| 2669 | {0x6B, nullptr, "Unknown"}, | 2683 | {0x6B, nullptr, "WriteDebugProcessMemory32"}, |
| 2670 | {0x6C, nullptr, "Unknown"}, | 2684 | {0x6C, nullptr, "SetHardwareBreakPoint32"}, |
| 2671 | {0x6D, nullptr, "Unknown"}, | 2685 | {0x6D, nullptr, "GetDebugThreadParam32"}, |
| 2672 | {0x6E, nullptr, "Unknown"}, | 2686 | {0x6E, nullptr, "Unknown6E"}, |
| 2673 | {0x6f, nullptr, "GetSystemInfo32"}, | 2687 | {0x6f, nullptr, "GetSystemInfo32"}, |
| 2674 | {0x70, nullptr, "CreatePort32"}, | 2688 | {0x70, nullptr, "CreatePort32"}, |
| 2675 | {0x71, nullptr, "ManageNamedPort32"}, | 2689 | {0x71, nullptr, "ManageNamedPort32"}, |
| 2676 | {0x72, nullptr, "ConnectToPort32"}, | 2690 | {0x72, nullptr, "ConnectToPort32"}, |
| 2677 | {0x73, nullptr, "SetProcessMemoryPermission32"}, | 2691 | {0x73, nullptr, "SetProcessMemoryPermission32"}, |
| 2678 | {0x74, nullptr, "Unknown"}, | 2692 | {0x74, nullptr, "MapProcessMemory32"}, |
| 2679 | {0x75, nullptr, "Unknown"}, | 2693 | {0x75, nullptr, "UnmapProcessMemory32"}, |
| 2680 | {0x76, nullptr, "Unknown"}, | 2694 | {0x76, nullptr, "QueryProcessMemory32"}, |
| 2681 | {0x77, nullptr, "MapProcessCodeMemory32"}, | 2695 | {0x77, nullptr, "MapProcessCodeMemory32"}, |
| 2682 | {0x78, nullptr, "UnmapProcessCodeMemory32"}, | 2696 | {0x78, nullptr, "UnmapProcessCodeMemory32"}, |
| 2683 | {0x79, nullptr, "Unknown"}, | 2697 | {0x79, nullptr, "CreateProcess32"}, |
| 2684 | {0x7A, nullptr, "Unknown"}, | 2698 | {0x7A, nullptr, "StartProcess32"}, |
| 2685 | {0x7B, nullptr, "TerminateProcess32"}, | 2699 | {0x7B, nullptr, "TerminateProcess32"}, |
| 2686 | {0x7C, nullptr, "GetProcessInfo32"}, | 2700 | {0x7C, nullptr, "GetProcessInfo32"}, |
| 2687 | {0x7D, nullptr, "CreateResourceLimit32"}, | 2701 | {0x7D, nullptr, "CreateResourceLimit32"}, |
| @@ -2754,7 +2768,7 @@ static const FunctionDef SVC_Table_32[] = { | |||
| 2754 | }; | 2768 | }; |
| 2755 | 2769 | ||
| 2756 | static const FunctionDef SVC_Table_64[] = { | 2770 | static const FunctionDef SVC_Table_64[] = { |
| 2757 | {0x00, nullptr, "Unknown"}, | 2771 | {0x00, nullptr, "Unknown0"}, |
| 2758 | {0x01, SvcWrap64<SetHeapSize>, "SetHeapSize"}, | 2772 | {0x01, SvcWrap64<SetHeapSize>, "SetHeapSize"}, |
| 2759 | {0x02, SvcWrap64<SetMemoryPermission>, "SetMemoryPermission"}, | 2773 | {0x02, SvcWrap64<SetMemoryPermission>, "SetMemoryPermission"}, |
| 2760 | {0x03, SvcWrap64<SetMemoryAttribute>, "SetMemoryAttribute"}, | 2774 | {0x03, SvcWrap64<SetMemoryAttribute>, "SetMemoryAttribute"}, |
| @@ -2809,23 +2823,23 @@ static const FunctionDef SVC_Table_64[] = { | |||
| 2809 | {0x34, SvcWrap64<WaitForAddress>, "WaitForAddress"}, | 2823 | {0x34, SvcWrap64<WaitForAddress>, "WaitForAddress"}, |
| 2810 | {0x35, SvcWrap64<SignalToAddress>, "SignalToAddress"}, | 2824 | {0x35, SvcWrap64<SignalToAddress>, "SignalToAddress"}, |
| 2811 | {0x36, SvcWrap64<SynchronizePreemptionState>, "SynchronizePreemptionState"}, | 2825 | {0x36, SvcWrap64<SynchronizePreemptionState>, "SynchronizePreemptionState"}, |
| 2812 | {0x37, nullptr, "Unknown"}, | 2826 | {0x37, nullptr, "GetResourceLimitPeakValue"}, |
| 2813 | {0x38, nullptr, "Unknown"}, | 2827 | {0x38, nullptr, "Unknown38"}, |
| 2814 | {0x39, nullptr, "Unknown"}, | 2828 | {0x39, nullptr, "CreateIoPool"}, |
| 2815 | {0x3A, nullptr, "Unknown"}, | 2829 | {0x3A, nullptr, "CreateIoRegion"}, |
| 2816 | {0x3B, nullptr, "Unknown"}, | 2830 | {0x3B, nullptr, "Unknown3B"}, |
| 2817 | {0x3C, SvcWrap64<KernelDebug>, "KernelDebug"}, | 2831 | {0x3C, SvcWrap64<KernelDebug>, "KernelDebug"}, |
| 2818 | {0x3D, SvcWrap64<ChangeKernelTraceState>, "ChangeKernelTraceState"}, | 2832 | {0x3D, SvcWrap64<ChangeKernelTraceState>, "ChangeKernelTraceState"}, |
| 2819 | {0x3E, nullptr, "Unknown"}, | 2833 | {0x3E, nullptr, "Unknown3e"}, |
| 2820 | {0x3F, nullptr, "Unknown"}, | 2834 | {0x3F, nullptr, "Unknown3f"}, |
| 2821 | {0x40, nullptr, "CreateSession"}, | 2835 | {0x40, nullptr, "CreateSession"}, |
| 2822 | {0x41, nullptr, "AcceptSession"}, | 2836 | {0x41, nullptr, "AcceptSession"}, |
| 2823 | {0x42, nullptr, "ReplyAndReceiveLight"}, | 2837 | {0x42, nullptr, "ReplyAndReceiveLight"}, |
| 2824 | {0x43, nullptr, "ReplyAndReceive"}, | 2838 | {0x43, nullptr, "ReplyAndReceive"}, |
| 2825 | {0x44, nullptr, "ReplyAndReceiveWithUserBuffer"}, | 2839 | {0x44, nullptr, "ReplyAndReceiveWithUserBuffer"}, |
| 2826 | {0x45, SvcWrap64<CreateEvent>, "CreateEvent"}, | 2840 | {0x45, SvcWrap64<CreateEvent>, "CreateEvent"}, |
| 2827 | {0x46, nullptr, "Unknown"}, | 2841 | {0x46, nullptr, "MapIoRegion"}, |
| 2828 | {0x47, nullptr, "Unknown"}, | 2842 | {0x47, nullptr, "UnmapIoRegion"}, |
| 2829 | {0x48, nullptr, "MapPhysicalMemoryUnsafe"}, | 2843 | {0x48, nullptr, "MapPhysicalMemoryUnsafe"}, |
| 2830 | {0x49, nullptr, "UnmapPhysicalMemoryUnsafe"}, | 2844 | {0x49, nullptr, "UnmapPhysicalMemoryUnsafe"}, |
| 2831 | {0x4A, nullptr, "SetUnsafeLimit"}, | 2845 | {0x4A, nullptr, "SetUnsafeLimit"}, |
| @@ -2864,7 +2878,7 @@ static const FunctionDef SVC_Table_64[] = { | |||
| 2864 | {0x6B, nullptr, "WriteDebugProcessMemory"}, | 2878 | {0x6B, nullptr, "WriteDebugProcessMemory"}, |
| 2865 | {0x6C, nullptr, "SetHardwareBreakPoint"}, | 2879 | {0x6C, nullptr, "SetHardwareBreakPoint"}, |
| 2866 | {0x6D, nullptr, "GetDebugThreadParam"}, | 2880 | {0x6D, nullptr, "GetDebugThreadParam"}, |
| 2867 | {0x6E, nullptr, "Unknown"}, | 2881 | {0x6E, nullptr, "Unknown6E"}, |
| 2868 | {0x6F, nullptr, "GetSystemInfo"}, | 2882 | {0x6F, nullptr, "GetSystemInfo"}, |
| 2869 | {0x70, nullptr, "CreatePort"}, | 2883 | {0x70, nullptr, "CreatePort"}, |
| 2870 | {0x71, nullptr, "ManageNamedPort"}, | 2884 | {0x71, nullptr, "ManageNamedPort"}, |
diff --git a/src/core/hle/kernel/svc_types.h b/src/core/hle/kernel/svc_types.h index 365e22e4e..b2e9ec092 100644 --- a/src/core/hle/kernel/svc_types.h +++ b/src/core/hle/kernel/svc_types.h | |||
| @@ -96,4 +96,6 @@ constexpr inline s32 IdealCoreNoUpdate = -3; | |||
| 96 | constexpr inline s32 LowestThreadPriority = 63; | 96 | constexpr inline s32 LowestThreadPriority = 63; |
| 97 | constexpr inline s32 HighestThreadPriority = 0; | 97 | constexpr inline s32 HighestThreadPriority = 0; |
| 98 | 98 | ||
| 99 | constexpr inline size_t ThreadLocalRegionSize = 0x200; | ||
| 100 | |||
| 99 | } // namespace Kernel::Svc | 101 | } // namespace Kernel::Svc |
diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h index a60adfcab..d309f166c 100644 --- a/src/core/hle/kernel/svc_wrap.h +++ b/src/core/hle/kernel/svc_wrap.h | |||
| @@ -669,4 +669,26 @@ void SvcWrap32(Core::System& system) { | |||
| 669 | FuncReturn(system, retval); | 669 | FuncReturn(system, retval); |
| 670 | } | 670 | } |
| 671 | 671 | ||
| 672 | // Used by CreateCodeMemory32 | ||
| 673 | template <ResultCode func(Core::System&, Handle*, u32, u32)> | ||
| 674 | void SvcWrap32(Core::System& system) { | ||
| 675 | Handle handle = 0; | ||
| 676 | |||
| 677 | const u32 retval = func(system, &handle, Param32(system, 1), Param32(system, 2)).raw; | ||
| 678 | |||
| 679 | system.CurrentArmInterface().SetReg(1, handle); | ||
| 680 | FuncReturn(system, retval); | ||
| 681 | } | ||
| 682 | |||
| 683 | // Used by ControlCodeMemory32 | ||
| 684 | template <ResultCode func(Core::System&, Handle, u32, u64, u64, Svc::MemoryPermission)> | ||
| 685 | void SvcWrap32(Core::System& system) { | ||
| 686 | const u32 retval = | ||
| 687 | func(system, Param32(system, 0), Param32(system, 1), Param(system, 2), Param(system, 4), | ||
| 688 | static_cast<Svc::MemoryPermission>(Param32(system, 6))) | ||
| 689 | .raw; | ||
| 690 | |||
| 691 | FuncReturn(system, retval); | ||
| 692 | } | ||
| 693 | |||
| 672 | } // namespace Kernel | 694 | } // namespace Kernel |
diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp index 6e63e057e..e34ef5a78 100644 --- a/src/core/hle/service/acc/acc.cpp +++ b/src/core/hle/service/acc/acc.cpp | |||
| @@ -39,9 +39,9 @@ constexpr ResultCode ERR_FAILED_SAVE_DATA{ErrorModule::Account, 100}; | |||
| 39 | // Thumbnails are hard coded to be at least this size | 39 | // Thumbnails are hard coded to be at least this size |
| 40 | constexpr std::size_t THUMBNAIL_SIZE = 0x24000; | 40 | constexpr std::size_t THUMBNAIL_SIZE = 0x24000; |
| 41 | 41 | ||
| 42 | static std::filesystem::path GetImagePath(Common::UUID uuid) { | 42 | static std::filesystem::path GetImagePath(const Common::UUID& uuid) { |
| 43 | return Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / | 43 | return Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / |
| 44 | fmt::format("system/save/8000000000000010/su/avators/{}.jpg", uuid.FormatSwitch()); | 44 | fmt::format("system/save/8000000000000010/su/avators/{}.jpg", uuid.FormattedString()); |
| 45 | } | 45 | } |
| 46 | 46 | ||
| 47 | static constexpr u32 SanitizeJPEGSize(std::size_t size) { | 47 | static constexpr u32 SanitizeJPEGSize(std::size_t size) { |
| @@ -290,7 +290,7 @@ public: | |||
| 290 | 290 | ||
| 291 | protected: | 291 | protected: |
| 292 | void Get(Kernel::HLERequestContext& ctx) { | 292 | void Get(Kernel::HLERequestContext& ctx) { |
| 293 | LOG_DEBUG(Service_ACC, "called user_id=0x{}", user_id.Format()); | 293 | LOG_DEBUG(Service_ACC, "called user_id=0x{}", user_id.RawString()); |
| 294 | ProfileBase profile_base{}; | 294 | ProfileBase profile_base{}; |
| 295 | ProfileData data{}; | 295 | ProfileData data{}; |
| 296 | if (profile_manager.GetProfileBaseAndData(user_id, profile_base, data)) { | 296 | if (profile_manager.GetProfileBaseAndData(user_id, profile_base, data)) { |
| @@ -300,21 +300,21 @@ protected: | |||
| 300 | rb.PushRaw(profile_base); | 300 | rb.PushRaw(profile_base); |
| 301 | } else { | 301 | } else { |
| 302 | LOG_ERROR(Service_ACC, "Failed to get profile base and data for user=0x{}", | 302 | LOG_ERROR(Service_ACC, "Failed to get profile base and data for user=0x{}", |
| 303 | user_id.Format()); | 303 | user_id.RawString()); |
| 304 | IPC::ResponseBuilder rb{ctx, 2}; | 304 | IPC::ResponseBuilder rb{ctx, 2}; |
| 305 | rb.Push(ResultUnknown); // TODO(ogniK): Get actual error code | 305 | rb.Push(ResultUnknown); // TODO(ogniK): Get actual error code |
| 306 | } | 306 | } |
| 307 | } | 307 | } |
| 308 | 308 | ||
| 309 | void GetBase(Kernel::HLERequestContext& ctx) { | 309 | void GetBase(Kernel::HLERequestContext& ctx) { |
| 310 | LOG_DEBUG(Service_ACC, "called user_id=0x{}", user_id.Format()); | 310 | LOG_DEBUG(Service_ACC, "called user_id=0x{}", user_id.RawString()); |
| 311 | ProfileBase profile_base{}; | 311 | ProfileBase profile_base{}; |
| 312 | if (profile_manager.GetProfileBase(user_id, profile_base)) { | 312 | if (profile_manager.GetProfileBase(user_id, profile_base)) { |
| 313 | IPC::ResponseBuilder rb{ctx, 16}; | 313 | IPC::ResponseBuilder rb{ctx, 16}; |
| 314 | rb.Push(ResultSuccess); | 314 | rb.Push(ResultSuccess); |
| 315 | rb.PushRaw(profile_base); | 315 | rb.PushRaw(profile_base); |
| 316 | } else { | 316 | } else { |
| 317 | LOG_ERROR(Service_ACC, "Failed to get profile base for user=0x{}", user_id.Format()); | 317 | LOG_ERROR(Service_ACC, "Failed to get profile base for user=0x{}", user_id.RawString()); |
| 318 | IPC::ResponseBuilder rb{ctx, 2}; | 318 | IPC::ResponseBuilder rb{ctx, 2}; |
| 319 | rb.Push(ResultUnknown); // TODO(ogniK): Get actual error code | 319 | rb.Push(ResultUnknown); // TODO(ogniK): Get actual error code |
| 320 | } | 320 | } |
| @@ -373,7 +373,7 @@ protected: | |||
| 373 | LOG_DEBUG(Service_ACC, "called, username='{}', timestamp={:016X}, uuid=0x{}", | 373 | LOG_DEBUG(Service_ACC, "called, username='{}', timestamp={:016X}, uuid=0x{}", |
| 374 | Common::StringFromFixedZeroTerminatedBuffer( | 374 | Common::StringFromFixedZeroTerminatedBuffer( |
| 375 | reinterpret_cast<const char*>(base.username.data()), base.username.size()), | 375 | reinterpret_cast<const char*>(base.username.data()), base.username.size()), |
| 376 | base.timestamp, base.user_uuid.Format()); | 376 | base.timestamp, base.user_uuid.RawString()); |
| 377 | 377 | ||
| 378 | if (user_data.size() < sizeof(ProfileData)) { | 378 | if (user_data.size() < sizeof(ProfileData)) { |
| 379 | LOG_ERROR(Service_ACC, "ProfileData buffer too small!"); | 379 | LOG_ERROR(Service_ACC, "ProfileData buffer too small!"); |
| @@ -406,7 +406,7 @@ protected: | |||
| 406 | LOG_DEBUG(Service_ACC, "called, username='{}', timestamp={:016X}, uuid=0x{}", | 406 | LOG_DEBUG(Service_ACC, "called, username='{}', timestamp={:016X}, uuid=0x{}", |
| 407 | Common::StringFromFixedZeroTerminatedBuffer( | 407 | Common::StringFromFixedZeroTerminatedBuffer( |
| 408 | reinterpret_cast<const char*>(base.username.data()), base.username.size()), | 408 | reinterpret_cast<const char*>(base.username.data()), base.username.size()), |
| 409 | base.timestamp, base.user_uuid.Format()); | 409 | base.timestamp, base.user_uuid.RawString()); |
| 410 | 410 | ||
| 411 | if (user_data.size() < sizeof(ProfileData)) { | 411 | if (user_data.size() < sizeof(ProfileData)) { |
| 412 | LOG_ERROR(Service_ACC, "ProfileData buffer too small!"); | 412 | LOG_ERROR(Service_ACC, "ProfileData buffer too small!"); |
| @@ -435,7 +435,7 @@ protected: | |||
| 435 | } | 435 | } |
| 436 | 436 | ||
| 437 | ProfileManager& profile_manager; | 437 | ProfileManager& profile_manager; |
| 438 | Common::UUID user_id{Common::INVALID_UUID}; ///< The user id this profile refers to. | 438 | Common::UUID user_id{}; ///< The user id this profile refers to. |
| 439 | }; | 439 | }; |
| 440 | 440 | ||
| 441 | class IProfile final : public IProfileCommon { | 441 | class IProfile final : public IProfileCommon { |
| @@ -547,7 +547,7 @@ private: | |||
| 547 | 547 | ||
| 548 | IPC::ResponseBuilder rb{ctx, 4}; | 548 | IPC::ResponseBuilder rb{ctx, 4}; |
| 549 | rb.Push(ResultSuccess); | 549 | rb.Push(ResultSuccess); |
| 550 | rb.PushRaw<u64>(user_id.GetNintendoID()); | 550 | rb.PushRaw<u64>(user_id.Hash()); |
| 551 | } | 551 | } |
| 552 | 552 | ||
| 553 | void EnsureIdTokenCacheAsync(Kernel::HLERequestContext& ctx) { | 553 | void EnsureIdTokenCacheAsync(Kernel::HLERequestContext& ctx) { |
| @@ -577,7 +577,7 @@ private: | |||
| 577 | 577 | ||
| 578 | IPC::ResponseBuilder rb{ctx, 4}; | 578 | IPC::ResponseBuilder rb{ctx, 4}; |
| 579 | rb.Push(ResultSuccess); | 579 | rb.Push(ResultSuccess); |
| 580 | rb.PushRaw<u64>(user_id.GetNintendoID()); | 580 | rb.PushRaw<u64>(user_id.Hash()); |
| 581 | } | 581 | } |
| 582 | 582 | ||
| 583 | void StoreOpenContext(Kernel::HLERequestContext& ctx) { | 583 | void StoreOpenContext(Kernel::HLERequestContext& ctx) { |
| @@ -587,7 +587,7 @@ private: | |||
| 587 | } | 587 | } |
| 588 | 588 | ||
| 589 | std::shared_ptr<EnsureTokenIdCacheAsyncInterface> ensure_token_id{}; | 589 | std::shared_ptr<EnsureTokenIdCacheAsyncInterface> ensure_token_id{}; |
| 590 | Common::UUID user_id{Common::INVALID_UUID}; | 590 | Common::UUID user_id{}; |
| 591 | }; | 591 | }; |
| 592 | 592 | ||
| 593 | // 6.0.0+ | 593 | // 6.0.0+ |
| @@ -687,7 +687,7 @@ void Module::Interface::GetUserCount(Kernel::HLERequestContext& ctx) { | |||
| 687 | void Module::Interface::GetUserExistence(Kernel::HLERequestContext& ctx) { | 687 | void Module::Interface::GetUserExistence(Kernel::HLERequestContext& ctx) { |
| 688 | IPC::RequestParser rp{ctx}; | 688 | IPC::RequestParser rp{ctx}; |
| 689 | Common::UUID user_id = rp.PopRaw<Common::UUID>(); | 689 | Common::UUID user_id = rp.PopRaw<Common::UUID>(); |
| 690 | LOG_DEBUG(Service_ACC, "called user_id=0x{}", user_id.Format()); | 690 | LOG_DEBUG(Service_ACC, "called user_id=0x{}", user_id.RawString()); |
| 691 | 691 | ||
| 692 | IPC::ResponseBuilder rb{ctx, 3}; | 692 | IPC::ResponseBuilder rb{ctx, 3}; |
| 693 | rb.Push(ResultSuccess); | 693 | rb.Push(ResultSuccess); |
| @@ -718,7 +718,7 @@ void Module::Interface::GetLastOpenedUser(Kernel::HLERequestContext& ctx) { | |||
| 718 | void Module::Interface::GetProfile(Kernel::HLERequestContext& ctx) { | 718 | void Module::Interface::GetProfile(Kernel::HLERequestContext& ctx) { |
| 719 | IPC::RequestParser rp{ctx}; | 719 | IPC::RequestParser rp{ctx}; |
| 720 | Common::UUID user_id = rp.PopRaw<Common::UUID>(); | 720 | Common::UUID user_id = rp.PopRaw<Common::UUID>(); |
| 721 | LOG_DEBUG(Service_ACC, "called user_id=0x{}", user_id.Format()); | 721 | LOG_DEBUG(Service_ACC, "called user_id=0x{}", user_id.RawString()); |
| 722 | 722 | ||
| 723 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | 723 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; |
| 724 | rb.Push(ResultSuccess); | 724 | rb.Push(ResultSuccess); |
| @@ -833,7 +833,7 @@ void Module::Interface::GetProfileEditor(Kernel::HLERequestContext& ctx) { | |||
| 833 | IPC::RequestParser rp{ctx}; | 833 | IPC::RequestParser rp{ctx}; |
| 834 | Common::UUID user_id = rp.PopRaw<Common::UUID>(); | 834 | Common::UUID user_id = rp.PopRaw<Common::UUID>(); |
| 835 | 835 | ||
| 836 | LOG_DEBUG(Service_ACC, "called, user_id=0x{}", user_id.Format()); | 836 | LOG_DEBUG(Service_ACC, "called, user_id=0x{}", user_id.RawString()); |
| 837 | 837 | ||
| 838 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | 838 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; |
| 839 | rb.Push(ResultSuccess); | 839 | rb.Push(ResultSuccess); |
| @@ -875,7 +875,7 @@ void Module::Interface::StoreSaveDataThumbnailApplication(Kernel::HLERequestCont | |||
| 875 | IPC::RequestParser rp{ctx}; | 875 | IPC::RequestParser rp{ctx}; |
| 876 | const auto uuid = rp.PopRaw<Common::UUID>(); | 876 | const auto uuid = rp.PopRaw<Common::UUID>(); |
| 877 | 877 | ||
| 878 | LOG_WARNING(Service_ACC, "(STUBBED) called, uuid=0x{}", uuid.Format()); | 878 | LOG_WARNING(Service_ACC, "(STUBBED) called, uuid=0x{}", uuid.RawString()); |
| 879 | 879 | ||
| 880 | // TODO(ogniK): Check if application ID is zero on acc initialize. As we don't have a reliable | 880 | // TODO(ogniK): Check if application ID is zero on acc initialize. As we don't have a reliable |
| 881 | // way of confirming things like the TID, we're going to assume a non zero value for the time | 881 | // way of confirming things like the TID, we're going to assume a non zero value for the time |
| @@ -889,7 +889,7 @@ void Module::Interface::StoreSaveDataThumbnailSystem(Kernel::HLERequestContext& | |||
| 889 | const auto uuid = rp.PopRaw<Common::UUID>(); | 889 | const auto uuid = rp.PopRaw<Common::UUID>(); |
| 890 | const auto tid = rp.Pop<u64_le>(); | 890 | const auto tid = rp.Pop<u64_le>(); |
| 891 | 891 | ||
| 892 | LOG_WARNING(Service_ACC, "(STUBBED) called, uuid=0x{}, tid={:016X}", uuid.Format(), tid); | 892 | LOG_WARNING(Service_ACC, "(STUBBED) called, uuid=0x{}, tid={:016X}", uuid.RawString(), tid); |
| 893 | StoreSaveDataThumbnail(ctx, uuid, tid); | 893 | StoreSaveDataThumbnail(ctx, uuid, tid); |
| 894 | } | 894 | } |
| 895 | 895 | ||
| @@ -903,7 +903,7 @@ void Module::Interface::StoreSaveDataThumbnail(Kernel::HLERequestContext& ctx, | |||
| 903 | return; | 903 | return; |
| 904 | } | 904 | } |
| 905 | 905 | ||
| 906 | if (!uuid) { | 906 | if (uuid.IsInvalid()) { |
| 907 | LOG_ERROR(Service_ACC, "User ID is not valid!"); | 907 | LOG_ERROR(Service_ACC, "User ID is not valid!"); |
| 908 | rb.Push(ERR_INVALID_USER_ID); | 908 | rb.Push(ERR_INVALID_USER_ID); |
| 909 | return; | 909 | return; |
| @@ -927,20 +927,20 @@ void Module::Interface::TrySelectUserWithoutInteraction(Kernel::HLERequestContex | |||
| 927 | IPC::ResponseBuilder rb{ctx, 6}; | 927 | IPC::ResponseBuilder rb{ctx, 6}; |
| 928 | if (profile_manager->GetUserCount() != 1) { | 928 | if (profile_manager->GetUserCount() != 1) { |
| 929 | rb.Push(ResultSuccess); | 929 | rb.Push(ResultSuccess); |
| 930 | rb.PushRaw<u128>(Common::INVALID_UUID); | 930 | rb.PushRaw(Common::InvalidUUID); |
| 931 | return; | 931 | return; |
| 932 | } | 932 | } |
| 933 | 933 | ||
| 934 | const auto user_list = profile_manager->GetAllUsers(); | 934 | const auto user_list = profile_manager->GetAllUsers(); |
| 935 | if (std::ranges::all_of(user_list, [](const auto& user) { return user.IsInvalid(); })) { | 935 | if (std::ranges::all_of(user_list, [](const auto& user) { return user.IsInvalid(); })) { |
| 936 | rb.Push(ResultUnknown); // TODO(ogniK): Find the correct error code | 936 | rb.Push(ResultUnknown); // TODO(ogniK): Find the correct error code |
| 937 | rb.PushRaw<u128>(Common::INVALID_UUID); | 937 | rb.PushRaw(Common::InvalidUUID); |
| 938 | return; | 938 | return; |
| 939 | } | 939 | } |
| 940 | 940 | ||
| 941 | // Select the first user we have | 941 | // Select the first user we have |
| 942 | rb.Push(ResultSuccess); | 942 | rb.Push(ResultSuccess); |
| 943 | rb.PushRaw<u128>(profile_manager->GetUser(0)->uuid); | 943 | rb.PushRaw(profile_manager->GetUser(0)->uuid); |
| 944 | } | 944 | } |
| 945 | 945 | ||
| 946 | Module::Interface::Interface(std::shared_ptr<Module> module_, | 946 | Module::Interface::Interface(std::shared_ptr<Module> module_, |
diff --git a/src/core/hle/service/acc/profile_manager.cpp b/src/core/hle/service/acc/profile_manager.cpp index 568303ced..fba847142 100644 --- a/src/core/hle/service/acc/profile_manager.cpp +++ b/src/core/hle/service/acc/profile_manager.cpp | |||
| @@ -19,8 +19,8 @@ namespace FS = Common::FS; | |||
| 19 | using Common::UUID; | 19 | using Common::UUID; |
| 20 | 20 | ||
| 21 | struct UserRaw { | 21 | struct UserRaw { |
| 22 | UUID uuid{Common::INVALID_UUID}; | 22 | UUID uuid{}; |
| 23 | UUID uuid2{Common::INVALID_UUID}; | 23 | UUID uuid2{}; |
| 24 | u64 timestamp{}; | 24 | u64 timestamp{}; |
| 25 | ProfileUsername username{}; | 25 | ProfileUsername username{}; |
| 26 | ProfileData extra_data{}; | 26 | ProfileData extra_data{}; |
| @@ -45,7 +45,7 @@ ProfileManager::ProfileManager() { | |||
| 45 | 45 | ||
| 46 | // Create an user if none are present | 46 | // Create an user if none are present |
| 47 | if (user_count == 0) { | 47 | if (user_count == 0) { |
| 48 | CreateNewUser(UUID::Generate(), "yuzu"); | 48 | CreateNewUser(UUID::MakeRandom(), "yuzu"); |
| 49 | } | 49 | } |
| 50 | 50 | ||
| 51 | auto current = | 51 | auto current = |
| @@ -101,7 +101,7 @@ ResultCode ProfileManager::CreateNewUser(UUID uuid, const ProfileUsername& usern | |||
| 101 | if (user_count == MAX_USERS) { | 101 | if (user_count == MAX_USERS) { |
| 102 | return ERROR_TOO_MANY_USERS; | 102 | return ERROR_TOO_MANY_USERS; |
| 103 | } | 103 | } |
| 104 | if (!uuid) { | 104 | if (uuid.IsInvalid()) { |
| 105 | return ERROR_ARGUMENT_IS_NULL; | 105 | return ERROR_ARGUMENT_IS_NULL; |
| 106 | } | 106 | } |
| 107 | if (username[0] == 0x0) { | 107 | if (username[0] == 0x0) { |
| @@ -145,7 +145,7 @@ std::optional<UUID> ProfileManager::GetUser(std::size_t index) const { | |||
| 145 | 145 | ||
| 146 | /// Returns a users profile index based on their user id. | 146 | /// Returns a users profile index based on their user id. |
| 147 | std::optional<std::size_t> ProfileManager::GetUserIndex(const UUID& uuid) const { | 147 | std::optional<std::size_t> ProfileManager::GetUserIndex(const UUID& uuid) const { |
| 148 | if (!uuid) { | 148 | if (uuid.IsInvalid()) { |
| 149 | return std::nullopt; | 149 | return std::nullopt; |
| 150 | } | 150 | } |
| 151 | 151 | ||
| @@ -250,9 +250,10 @@ UserIDArray ProfileManager::GetOpenUsers() const { | |||
| 250 | std::ranges::transform(profiles, output.begin(), [](const ProfileInfo& p) { | 250 | std::ranges::transform(profiles, output.begin(), [](const ProfileInfo& p) { |
| 251 | if (p.is_open) | 251 | if (p.is_open) |
| 252 | return p.user_uuid; | 252 | return p.user_uuid; |
| 253 | return UUID{Common::INVALID_UUID}; | 253 | return Common::InvalidUUID; |
| 254 | }); | 254 | }); |
| 255 | std::stable_partition(output.begin(), output.end(), [](const UUID& uuid) { return uuid; }); | 255 | std::stable_partition(output.begin(), output.end(), |
| 256 | [](const UUID& uuid) { return uuid.IsValid(); }); | ||
| 256 | return output; | 257 | return output; |
| 257 | } | 258 | } |
| 258 | 259 | ||
| @@ -299,7 +300,7 @@ bool ProfileManager::RemoveUser(UUID uuid) { | |||
| 299 | 300 | ||
| 300 | profiles[*index] = ProfileInfo{}; | 301 | profiles[*index] = ProfileInfo{}; |
| 301 | std::stable_partition(profiles.begin(), profiles.end(), | 302 | std::stable_partition(profiles.begin(), profiles.end(), |
| 302 | [](const ProfileInfo& profile) { return profile.user_uuid; }); | 303 | [](const ProfileInfo& profile) { return profile.user_uuid.IsValid(); }); |
| 303 | return true; | 304 | return true; |
| 304 | } | 305 | } |
| 305 | 306 | ||
| @@ -361,7 +362,7 @@ void ProfileManager::ParseUserSaveFile() { | |||
| 361 | } | 362 | } |
| 362 | 363 | ||
| 363 | std::stable_partition(profiles.begin(), profiles.end(), | 364 | std::stable_partition(profiles.begin(), profiles.end(), |
| 364 | [](const ProfileInfo& profile) { return profile.user_uuid; }); | 365 | [](const ProfileInfo& profile) { return profile.user_uuid.IsValid(); }); |
| 365 | } | 366 | } |
| 366 | 367 | ||
| 367 | void ProfileManager::WriteUserSaveFile() { | 368 | void ProfileManager::WriteUserSaveFile() { |
diff --git a/src/core/hle/service/acc/profile_manager.h b/src/core/hle/service/acc/profile_manager.h index 71b9d5518..17347f7ef 100644 --- a/src/core/hle/service/acc/profile_manager.h +++ b/src/core/hle/service/acc/profile_manager.h | |||
| @@ -35,7 +35,7 @@ static_assert(sizeof(ProfileData) == 0x80, "ProfileData structure has incorrect | |||
| 35 | /// This holds general information about a users profile. This is where we store all the information | 35 | /// This holds general information about a users profile. This is where we store all the information |
| 36 | /// based on a specific user | 36 | /// based on a specific user |
| 37 | struct ProfileInfo { | 37 | struct ProfileInfo { |
| 38 | Common::UUID user_uuid{Common::INVALID_UUID}; | 38 | Common::UUID user_uuid{}; |
| 39 | ProfileUsername username{}; | 39 | ProfileUsername username{}; |
| 40 | u64 creation_time{}; | 40 | u64 creation_time{}; |
| 41 | ProfileData data{}; // TODO(ognik): Work out what this is | 41 | ProfileData data{}; // TODO(ognik): Work out what this is |
| @@ -49,7 +49,7 @@ struct ProfileBase { | |||
| 49 | 49 | ||
| 50 | // Zero out all the fields to make the profile slot considered "Empty" | 50 | // Zero out all the fields to make the profile slot considered "Empty" |
| 51 | void Invalidate() { | 51 | void Invalidate() { |
| 52 | user_uuid.Invalidate(); | 52 | user_uuid = {}; |
| 53 | timestamp = 0; | 53 | timestamp = 0; |
| 54 | username.fill(0); | 54 | username.fill(0); |
| 55 | } | 55 | } |
| @@ -103,7 +103,7 @@ private: | |||
| 103 | 103 | ||
| 104 | std::array<ProfileInfo, MAX_USERS> profiles{}; | 104 | std::array<ProfileInfo, MAX_USERS> profiles{}; |
| 105 | std::size_t user_count{}; | 105 | std::size_t user_count{}; |
| 106 | Common::UUID last_opened_user{Common::INVALID_UUID}; | 106 | Common::UUID last_opened_user{}; |
| 107 | }; | 107 | }; |
| 108 | 108 | ||
| 109 | }; // namespace Service::Account | 109 | }; // namespace Service::Account |
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index e60661fe1..420de3c54 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp | |||
| @@ -55,7 +55,7 @@ constexpr u32 LAUNCH_PARAMETER_ACCOUNT_PRESELECTED_USER_MAGIC = 0xC79497CA; | |||
| 55 | struct LaunchParameterAccountPreselectedUser { | 55 | struct LaunchParameterAccountPreselectedUser { |
| 56 | u32_le magic; | 56 | u32_le magic; |
| 57 | u32_le is_account_selected; | 57 | u32_le is_account_selected; |
| 58 | u128 current_user; | 58 | Common::UUID current_user; |
| 59 | INSERT_PADDING_BYTES(0x70); | 59 | INSERT_PADDING_BYTES(0x70); |
| 60 | }; | 60 | }; |
| 61 | static_assert(sizeof(LaunchParameterAccountPreselectedUser) == 0x88); | 61 | static_assert(sizeof(LaunchParameterAccountPreselectedUser) == 0x88); |
| @@ -618,7 +618,7 @@ void AppletMessageQueue::PushMessage(AppletMessage msg) { | |||
| 618 | AppletMessageQueue::AppletMessage AppletMessageQueue::PopMessage() { | 618 | AppletMessageQueue::AppletMessage AppletMessageQueue::PopMessage() { |
| 619 | if (messages.empty()) { | 619 | if (messages.empty()) { |
| 620 | on_new_message->GetWritableEvent().Clear(); | 620 | on_new_message->GetWritableEvent().Clear(); |
| 621 | return AppletMessage::NoMessage; | 621 | return AppletMessage::None; |
| 622 | } | 622 | } |
| 623 | auto msg = messages.front(); | 623 | auto msg = messages.front(); |
| 624 | messages.pop(); | 624 | messages.pop(); |
| @@ -633,7 +633,7 @@ std::size_t AppletMessageQueue::GetMessageCount() const { | |||
| 633 | } | 633 | } |
| 634 | 634 | ||
| 635 | void AppletMessageQueue::RequestExit() { | 635 | void AppletMessageQueue::RequestExit() { |
| 636 | PushMessage(AppletMessage::ExitRequested); | 636 | PushMessage(AppletMessage::Exit); |
| 637 | } | 637 | } |
| 638 | 638 | ||
| 639 | void AppletMessageQueue::FocusStateChanged() { | 639 | void AppletMessageQueue::FocusStateChanged() { |
| @@ -732,7 +732,7 @@ void ICommonStateGetter::ReceiveMessage(Kernel::HLERequestContext& ctx) { | |||
| 732 | const auto message = msg_queue->PopMessage(); | 732 | const auto message = msg_queue->PopMessage(); |
| 733 | IPC::ResponseBuilder rb{ctx, 3}; | 733 | IPC::ResponseBuilder rb{ctx, 3}; |
| 734 | 734 | ||
| 735 | if (message == AppletMessageQueue::AppletMessage::NoMessage) { | 735 | if (message == AppletMessageQueue::AppletMessage::None) { |
| 736 | LOG_ERROR(Service_AM, "Message queue is empty"); | 736 | LOG_ERROR(Service_AM, "Message queue is empty"); |
| 737 | rb.Push(ERR_NO_MESSAGES); | 737 | rb.Push(ERR_NO_MESSAGES); |
| 738 | rb.PushEnum<AppletMessageQueue::AppletMessage>(message); | 738 | rb.PushEnum<AppletMessageQueue::AppletMessage>(message); |
| @@ -980,7 +980,7 @@ private: | |||
| 980 | LOG_DEBUG(Service_AM, "called"); | 980 | LOG_DEBUG(Service_AM, "called"); |
| 981 | 981 | ||
| 982 | IPC::RequestParser rp{ctx}; | 982 | IPC::RequestParser rp{ctx}; |
| 983 | applet->GetBroker().PushNormalDataFromGame(rp.PopIpcInterface<IStorage>()); | 983 | applet->GetBroker().PushNormalDataFromGame(rp.PopIpcInterface<IStorage>().lock()); |
| 984 | 984 | ||
| 985 | IPC::ResponseBuilder rb{ctx, 2}; | 985 | IPC::ResponseBuilder rb{ctx, 2}; |
| 986 | rb.Push(ResultSuccess); | 986 | rb.Push(ResultSuccess); |
| @@ -1007,7 +1007,7 @@ private: | |||
| 1007 | LOG_DEBUG(Service_AM, "called"); | 1007 | LOG_DEBUG(Service_AM, "called"); |
| 1008 | 1008 | ||
| 1009 | IPC::RequestParser rp{ctx}; | 1009 | IPC::RequestParser rp{ctx}; |
| 1010 | applet->GetBroker().PushInteractiveDataFromGame(rp.PopIpcInterface<IStorage>()); | 1010 | applet->GetBroker().PushInteractiveDataFromGame(rp.PopIpcInterface<IStorage>().lock()); |
| 1011 | 1011 | ||
| 1012 | ASSERT(applet->IsInitialized()); | 1012 | ASSERT(applet->IsInitialized()); |
| 1013 | applet->ExecuteInteractive(); | 1013 | applet->ExecuteInteractive(); |
| @@ -1453,8 +1453,8 @@ void IApplicationFunctions::PopLaunchParameter(Kernel::HLERequestContext& ctx) { | |||
| 1453 | 1453 | ||
| 1454 | Account::ProfileManager profile_manager{}; | 1454 | Account::ProfileManager profile_manager{}; |
| 1455 | const auto uuid = profile_manager.GetUser(static_cast<s32>(Settings::values.current_user)); | 1455 | const auto uuid = profile_manager.GetUser(static_cast<s32>(Settings::values.current_user)); |
| 1456 | ASSERT(uuid); | 1456 | ASSERT(uuid.has_value() && uuid->IsValid()); |
| 1457 | params.current_user = uuid->uuid; | 1457 | params.current_user = *uuid; |
| 1458 | 1458 | ||
| 1459 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | 1459 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; |
| 1460 | 1460 | ||
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h index 2a578aea5..fdd937b82 100644 --- a/src/core/hle/service/am/am.h +++ b/src/core/hle/service/am/am.h | |||
| @@ -22,6 +22,7 @@ class NVFlinger; | |||
| 22 | 22 | ||
| 23 | namespace Service::AM { | 23 | namespace Service::AM { |
| 24 | 24 | ||
| 25 | // This is nn::settings::Language | ||
| 25 | enum SystemLanguage { | 26 | enum SystemLanguage { |
| 26 | Japanese = 0, | 27 | Japanese = 0, |
| 27 | English = 1, // en-US | 28 | English = 1, // en-US |
| @@ -41,16 +42,44 @@ enum SystemLanguage { | |||
| 41 | // 4.0.0+ | 42 | // 4.0.0+ |
| 42 | SimplifiedChinese = 15, | 43 | SimplifiedChinese = 15, |
| 43 | TraditionalChinese = 16, | 44 | TraditionalChinese = 16, |
| 45 | // 10.1.0+ | ||
| 46 | BrazilianPortuguese = 17, | ||
| 44 | }; | 47 | }; |
| 45 | 48 | ||
| 46 | class AppletMessageQueue { | 49 | class AppletMessageQueue { |
| 47 | public: | 50 | public: |
| 51 | // This is nn::am::AppletMessage | ||
| 48 | enum class AppletMessage : u32 { | 52 | enum class AppletMessage : u32 { |
| 49 | NoMessage = 0, | 53 | None = 0, |
| 50 | ExitRequested = 4, | 54 | ChangeIntoForeground = 1, |
| 55 | ChangeIntoBackground = 2, | ||
| 56 | Exit = 4, | ||
| 57 | ApplicationExited = 6, | ||
| 51 | FocusStateChanged = 15, | 58 | FocusStateChanged = 15, |
| 59 | Resume = 16, | ||
| 60 | DetectShortPressingHomeButton = 20, | ||
| 61 | DetectLongPressingHomeButton = 21, | ||
| 62 | DetectShortPressingPowerButton = 22, | ||
| 63 | DetectMiddlePressingPowerButton = 23, | ||
| 64 | DetectLongPressingPowerButton = 24, | ||
| 65 | RequestToPrepareSleep = 25, | ||
| 66 | FinishedSleepSequence = 26, | ||
| 67 | SleepRequiredByHighTemperature = 27, | ||
| 68 | SleepRequiredByLowBattery = 28, | ||
| 69 | AutoPowerDown = 29, | ||
| 52 | OperationModeChanged = 30, | 70 | OperationModeChanged = 30, |
| 53 | PerformanceModeChanged = 31, | 71 | PerformanceModeChanged = 31, |
| 72 | DetectReceivingCecSystemStandby = 32, | ||
| 73 | SdCardRemoved = 33, | ||
| 74 | LaunchApplicationRequested = 50, | ||
| 75 | RequestToDisplay = 51, | ||
| 76 | ShowApplicationLogo = 55, | ||
| 77 | HideApplicationLogo = 56, | ||
| 78 | ForceHideApplicationLogo = 57, | ||
| 79 | FloatingApplicationDetected = 60, | ||
| 80 | DetectShortPressingCaptureButton = 90, | ||
| 81 | AlbumScreenShotTaken = 92, | ||
| 82 | AlbumRecordingSaved = 93, | ||
| 54 | }; | 83 | }; |
| 55 | 84 | ||
| 56 | explicit AppletMessageQueue(Core::System& system); | 85 | explicit AppletMessageQueue(Core::System& system); |
| @@ -179,11 +208,14 @@ public: | |||
| 179 | ~ICommonStateGetter() override; | 208 | ~ICommonStateGetter() override; |
| 180 | 209 | ||
| 181 | private: | 210 | private: |
| 211 | // This is nn::oe::FocusState | ||
| 182 | enum class FocusState : u8 { | 212 | enum class FocusState : u8 { |
| 183 | InFocus = 1, | 213 | InFocus = 1, |
| 184 | NotInFocus = 2, | 214 | NotInFocus = 2, |
| 215 | Background = 3, | ||
| 185 | }; | 216 | }; |
| 186 | 217 | ||
| 218 | // This is nn::oe::OperationMode | ||
| 187 | enum class OperationMode : u8 { | 219 | enum class OperationMode : u8 { |
| 188 | Handheld = 0, | 220 | Handheld = 0, |
| 189 | Docked = 1, | 221 | Docked = 1, |
diff --git a/src/core/hle/service/am/applets/applet_mii.cpp b/src/core/hle/service/am/applets/applet_mii.cpp new file mode 100644 index 000000000..8c4173737 --- /dev/null +++ b/src/core/hle/service/am/applets/applet_mii.cpp | |||
| @@ -0,0 +1,101 @@ | |||
| 1 | // Copyright 2022 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "common/assert.h" | ||
| 6 | #include "common/logging/log.h" | ||
| 7 | #include "core/core.h" | ||
| 8 | #include "core/frontend/applets/mii.h" | ||
| 9 | #include "core/hle/service/am/am.h" | ||
| 10 | #include "core/hle/service/am/applets/applet_mii.h" | ||
| 11 | #include "core/reporter.h" | ||
| 12 | |||
| 13 | namespace Service::AM::Applets { | ||
| 14 | |||
| 15 | Mii::Mii(Core::System& system_, LibraryAppletMode applet_mode_, | ||
| 16 | const Core::Frontend::MiiApplet& frontend_) | ||
| 17 | : Applet{system_, applet_mode_}, frontend{frontend_}, system{system_} {} | ||
| 18 | |||
| 19 | Mii::~Mii() = default; | ||
| 20 | |||
| 21 | void Mii::Initialize() { | ||
| 22 | is_complete = false; | ||
| 23 | |||
| 24 | const auto storage = broker.PopNormalDataToApplet(); | ||
| 25 | ASSERT(storage != nullptr); | ||
| 26 | |||
| 27 | const auto data = storage->GetData(); | ||
| 28 | ASSERT(data.size() == sizeof(MiiAppletInput)); | ||
| 29 | |||
| 30 | std::memcpy(&input_data, data.data(), sizeof(MiiAppletInput)); | ||
| 31 | } | ||
| 32 | |||
| 33 | bool Mii::TransactionComplete() const { | ||
| 34 | return is_complete; | ||
| 35 | } | ||
| 36 | |||
| 37 | ResultCode Mii::GetStatus() const { | ||
| 38 | return ResultSuccess; | ||
| 39 | } | ||
| 40 | |||
| 41 | void Mii::ExecuteInteractive() { | ||
| 42 | UNREACHABLE_MSG("Unexpected interactive applet data!"); | ||
| 43 | } | ||
| 44 | |||
| 45 | void Mii::Execute() { | ||
| 46 | if (is_complete) { | ||
| 47 | return; | ||
| 48 | } | ||
| 49 | |||
| 50 | const auto callback = [this](const Core::Frontend::MiiParameters& parameters) { | ||
| 51 | DisplayCompleted(parameters); | ||
| 52 | }; | ||
| 53 | |||
| 54 | switch (input_data.applet_mode) { | ||
| 55 | case MiiAppletMode::ShowMiiEdit: { | ||
| 56 | Service::Mii::MiiManager manager; | ||
| 57 | Core::Frontend::MiiParameters params{ | ||
| 58 | .is_editable = false, | ||
| 59 | .mii_data = input_data.mii_char_info.mii_data, | ||
| 60 | }; | ||
| 61 | frontend.ShowMii(params, callback); | ||
| 62 | break; | ||
| 63 | } | ||
| 64 | case MiiAppletMode::EditMii: { | ||
| 65 | Service::Mii::MiiManager manager; | ||
| 66 | Core::Frontend::MiiParameters params{ | ||
| 67 | .is_editable = true, | ||
| 68 | .mii_data = input_data.mii_char_info.mii_data, | ||
| 69 | }; | ||
| 70 | frontend.ShowMii(params, callback); | ||
| 71 | break; | ||
| 72 | } | ||
| 73 | case MiiAppletMode::CreateMii: { | ||
| 74 | Service::Mii::MiiManager manager; | ||
| 75 | Core::Frontend::MiiParameters params{ | ||
| 76 | .is_editable = true, | ||
| 77 | .mii_data = manager.BuildDefault(0), | ||
| 78 | }; | ||
| 79 | frontend.ShowMii(params, callback); | ||
| 80 | break; | ||
| 81 | } | ||
| 82 | default: | ||
| 83 | UNIMPLEMENTED_MSG("Unimplemented LibAppletMiiEdit mode={:02X}!", input_data.applet_mode); | ||
| 84 | } | ||
| 85 | } | ||
| 86 | |||
| 87 | void Mii::DisplayCompleted(const Core::Frontend::MiiParameters& parameters) { | ||
| 88 | is_complete = true; | ||
| 89 | |||
| 90 | std::vector<u8> reply(sizeof(AppletOutputForCharInfoEditing)); | ||
| 91 | output_data = { | ||
| 92 | .result = ResultSuccess, | ||
| 93 | .mii_data = parameters.mii_data, | ||
| 94 | }; | ||
| 95 | |||
| 96 | std::memcpy(reply.data(), &output_data, sizeof(AppletOutputForCharInfoEditing)); | ||
| 97 | broker.PushNormalDataFromApplet(std::make_shared<IStorage>(system, std::move(reply))); | ||
| 98 | broker.SignalStateChanged(); | ||
| 99 | } | ||
| 100 | |||
| 101 | } // namespace Service::AM::Applets | ||
diff --git a/src/core/hle/service/am/applets/applet_mii.h b/src/core/hle/service/am/applets/applet_mii.h new file mode 100644 index 000000000..42326bfc2 --- /dev/null +++ b/src/core/hle/service/am/applets/applet_mii.h | |||
| @@ -0,0 +1,90 @@ | |||
| 1 | // Copyright 2022 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <array> | ||
| 8 | |||
| 9 | #include "core/hle/result.h" | ||
| 10 | #include "core/hle/service/am/applets/applets.h" | ||
| 11 | #include "core/hle/service/mii/mii_manager.h" | ||
| 12 | |||
| 13 | namespace Core { | ||
| 14 | class System; | ||
| 15 | } | ||
| 16 | |||
| 17 | namespace Service::AM::Applets { | ||
| 18 | |||
| 19 | // This is nn::mii::AppletMode | ||
| 20 | enum class MiiAppletMode : u32 { | ||
| 21 | ShowMiiEdit = 0, | ||
| 22 | AppendMii = 1, | ||
| 23 | AppendMiiImage = 2, | ||
| 24 | UpdateMiiImage = 3, | ||
| 25 | CreateMii = 4, | ||
| 26 | EditMii = 5, | ||
| 27 | }; | ||
| 28 | |||
| 29 | struct MiiCharInfo { | ||
| 30 | Service::Mii::MiiInfo mii_data{}; | ||
| 31 | INSERT_PADDING_BYTES(0x28); | ||
| 32 | }; | ||
| 33 | static_assert(sizeof(MiiCharInfo) == 0x80, "MiiCharInfo has incorrect size."); | ||
| 34 | |||
| 35 | // This is nn::mii::AppletInput | ||
| 36 | struct MiiAppletInput { | ||
| 37 | s32 version{}; | ||
| 38 | MiiAppletMode applet_mode{}; | ||
| 39 | u32 special_mii_key_code{}; | ||
| 40 | union { | ||
| 41 | std::array<Common::UUID, 8> valid_uuid; | ||
| 42 | MiiCharInfo mii_char_info; | ||
| 43 | }; | ||
| 44 | Common::UUID used_uuid; | ||
| 45 | INSERT_PADDING_BYTES(0x64); | ||
| 46 | }; | ||
| 47 | static_assert(sizeof(MiiAppletInput) == 0x100, "MiiAppletInput has incorrect size."); | ||
| 48 | |||
| 49 | // This is nn::mii::AppletOutput | ||
| 50 | struct MiiAppletOutput { | ||
| 51 | ResultCode result{ResultSuccess}; | ||
| 52 | s32 index{}; | ||
| 53 | INSERT_PADDING_BYTES(0x18); | ||
| 54 | }; | ||
| 55 | static_assert(sizeof(MiiAppletOutput) == 0x20, "MiiAppletOutput has incorrect size."); | ||
| 56 | |||
| 57 | // This is nn::mii::AppletOutputForCharInfoEditing | ||
| 58 | struct AppletOutputForCharInfoEditing { | ||
| 59 | ResultCode result{ResultSuccess}; | ||
| 60 | Service::Mii::MiiInfo mii_data{}; | ||
| 61 | INSERT_PADDING_BYTES(0x24); | ||
| 62 | }; | ||
| 63 | static_assert(sizeof(AppletOutputForCharInfoEditing) == 0x80, | ||
| 64 | "AppletOutputForCharInfoEditing has incorrect size."); | ||
| 65 | |||
| 66 | class Mii final : public Applet { | ||
| 67 | public: | ||
| 68 | explicit Mii(Core::System& system_, LibraryAppletMode applet_mode_, | ||
| 69 | const Core::Frontend::MiiApplet& frontend_); | ||
| 70 | ~Mii() override; | ||
| 71 | |||
| 72 | void Initialize() override; | ||
| 73 | |||
| 74 | bool TransactionComplete() const override; | ||
| 75 | ResultCode GetStatus() const override; | ||
| 76 | void ExecuteInteractive() override; | ||
| 77 | void Execute() override; | ||
| 78 | |||
| 79 | void DisplayCompleted(const Core::Frontend::MiiParameters& parameters); | ||
| 80 | |||
| 81 | private: | ||
| 82 | const Core::Frontend::MiiApplet& frontend; | ||
| 83 | MiiAppletInput input_data{}; | ||
| 84 | AppletOutputForCharInfoEditing output_data{}; | ||
| 85 | |||
| 86 | bool is_complete = false; | ||
| 87 | Core::System& system; | ||
| 88 | }; | ||
| 89 | |||
| 90 | } // namespace Service::AM::Applets | ||
diff --git a/src/core/hle/service/am/applets/applet_profile_select.cpp b/src/core/hle/service/am/applets/applet_profile_select.cpp index a6e891944..82500e121 100644 --- a/src/core/hle/service/am/applets/applet_profile_select.cpp +++ b/src/core/hle/service/am/applets/applet_profile_select.cpp | |||
| @@ -62,11 +62,11 @@ void ProfileSelect::SelectionComplete(std::optional<Common::UUID> uuid) { | |||
| 62 | 62 | ||
| 63 | if (uuid.has_value() && uuid->IsValid()) { | 63 | if (uuid.has_value() && uuid->IsValid()) { |
| 64 | output.result = 0; | 64 | output.result = 0; |
| 65 | output.uuid_selected = uuid->uuid; | 65 | output.uuid_selected = *uuid; |
| 66 | } else { | 66 | } else { |
| 67 | status = ERR_USER_CANCELLED_SELECTION; | 67 | status = ERR_USER_CANCELLED_SELECTION; |
| 68 | output.result = ERR_USER_CANCELLED_SELECTION.raw; | 68 | output.result = ERR_USER_CANCELLED_SELECTION.raw; |
| 69 | output.uuid_selected = Common::INVALID_UUID; | 69 | output.uuid_selected = Common::InvalidUUID; |
| 70 | } | 70 | } |
| 71 | 71 | ||
| 72 | final_data = std::vector<u8>(sizeof(UserSelectionOutput)); | 72 | final_data = std::vector<u8>(sizeof(UserSelectionOutput)); |
diff --git a/src/core/hle/service/am/applets/applet_profile_select.h b/src/core/hle/service/am/applets/applet_profile_select.h index 8fb76e6c4..852e1e0c0 100644 --- a/src/core/hle/service/am/applets/applet_profile_select.h +++ b/src/core/hle/service/am/applets/applet_profile_select.h | |||
| @@ -27,7 +27,7 @@ static_assert(sizeof(UserSelectionConfig) == 0xA0, "UserSelectionConfig has inco | |||
| 27 | 27 | ||
| 28 | struct UserSelectionOutput { | 28 | struct UserSelectionOutput { |
| 29 | u64 result; | 29 | u64 result; |
| 30 | u128 uuid_selected; | 30 | Common::UUID uuid_selected; |
| 31 | }; | 31 | }; |
| 32 | static_assert(sizeof(UserSelectionOutput) == 0x18, "UserSelectionOutput has incorrect size."); | 32 | static_assert(sizeof(UserSelectionOutput) == 0x18, "UserSelectionOutput has incorrect size."); |
| 33 | 33 | ||
diff --git a/src/core/hle/service/am/applets/applets.cpp b/src/core/hle/service/am/applets/applets.cpp index 134ac1ee2..79e62679d 100644 --- a/src/core/hle/service/am/applets/applets.cpp +++ b/src/core/hle/service/am/applets/applets.cpp | |||
| @@ -9,6 +9,7 @@ | |||
| 9 | #include "core/frontend/applets/controller.h" | 9 | #include "core/frontend/applets/controller.h" |
| 10 | #include "core/frontend/applets/error.h" | 10 | #include "core/frontend/applets/error.h" |
| 11 | #include "core/frontend/applets/general_frontend.h" | 11 | #include "core/frontend/applets/general_frontend.h" |
| 12 | #include "core/frontend/applets/mii.h" | ||
| 12 | #include "core/frontend/applets/profile_select.h" | 13 | #include "core/frontend/applets/profile_select.h" |
| 13 | #include "core/frontend/applets/software_keyboard.h" | 14 | #include "core/frontend/applets/software_keyboard.h" |
| 14 | #include "core/frontend/applets/web_browser.h" | 15 | #include "core/frontend/applets/web_browser.h" |
| @@ -19,6 +20,7 @@ | |||
| 19 | #include "core/hle/service/am/applets/applet_controller.h" | 20 | #include "core/hle/service/am/applets/applet_controller.h" |
| 20 | #include "core/hle/service/am/applets/applet_error.h" | 21 | #include "core/hle/service/am/applets/applet_error.h" |
| 21 | #include "core/hle/service/am/applets/applet_general_backend.h" | 22 | #include "core/hle/service/am/applets/applet_general_backend.h" |
| 23 | #include "core/hle/service/am/applets/applet_mii.h" | ||
| 22 | #include "core/hle/service/am/applets/applet_profile_select.h" | 24 | #include "core/hle/service/am/applets/applet_profile_select.h" |
| 23 | #include "core/hle/service/am/applets/applet_software_keyboard.h" | 25 | #include "core/hle/service/am/applets/applet_software_keyboard.h" |
| 24 | #include "core/hle/service/am/applets/applet_web_browser.h" | 26 | #include "core/hle/service/am/applets/applet_web_browser.h" |
| @@ -172,10 +174,11 @@ AppletFrontendSet::AppletFrontendSet() = default; | |||
| 172 | 174 | ||
| 173 | AppletFrontendSet::AppletFrontendSet(ControllerApplet controller_applet, ErrorApplet error_applet, | 175 | AppletFrontendSet::AppletFrontendSet(ControllerApplet controller_applet, ErrorApplet error_applet, |
| 174 | ParentalControlsApplet parental_controls_applet, | 176 | ParentalControlsApplet parental_controls_applet, |
| 175 | PhotoViewer photo_viewer_, ProfileSelect profile_select_, | 177 | MiiApplet mii_applet, PhotoViewer photo_viewer_, |
| 178 | ProfileSelect profile_select_, | ||
| 176 | SoftwareKeyboard software_keyboard_, WebBrowser web_browser_) | 179 | SoftwareKeyboard software_keyboard_, WebBrowser web_browser_) |
| 177 | : controller{std::move(controller_applet)}, error{std::move(error_applet)}, | 180 | : controller{std::move(controller_applet)}, error{std::move(error_applet)}, |
| 178 | parental_controls{std::move(parental_controls_applet)}, | 181 | parental_controls{std::move(parental_controls_applet)}, mii{std::move(mii_applet)}, |
| 179 | photo_viewer{std::move(photo_viewer_)}, profile_select{std::move(profile_select_)}, | 182 | photo_viewer{std::move(photo_viewer_)}, profile_select{std::move(profile_select_)}, |
| 180 | software_keyboard{std::move(software_keyboard_)}, web_browser{std::move(web_browser_)} {} | 183 | software_keyboard{std::move(software_keyboard_)}, web_browser{std::move(web_browser_)} {} |
| 181 | 184 | ||
| @@ -206,6 +209,10 @@ void AppletManager::SetAppletFrontendSet(AppletFrontendSet set) { | |||
| 206 | frontend.parental_controls = std::move(set.parental_controls); | 209 | frontend.parental_controls = std::move(set.parental_controls); |
| 207 | } | 210 | } |
| 208 | 211 | ||
| 212 | if (set.mii != nullptr) { | ||
| 213 | frontend.mii = std::move(set.mii); | ||
| 214 | } | ||
| 215 | |||
| 209 | if (set.photo_viewer != nullptr) { | 216 | if (set.photo_viewer != nullptr) { |
| 210 | frontend.photo_viewer = std::move(set.photo_viewer); | 217 | frontend.photo_viewer = std::move(set.photo_viewer); |
| 211 | } | 218 | } |
| @@ -243,6 +250,10 @@ void AppletManager::SetDefaultAppletsIfMissing() { | |||
| 243 | std::make_unique<Core::Frontend::DefaultParentalControlsApplet>(); | 250 | std::make_unique<Core::Frontend::DefaultParentalControlsApplet>(); |
| 244 | } | 251 | } |
| 245 | 252 | ||
| 253 | if (frontend.mii == nullptr) { | ||
| 254 | frontend.mii = std::make_unique<Core::Frontend::DefaultMiiApplet>(); | ||
| 255 | } | ||
| 256 | |||
| 246 | if (frontend.photo_viewer == nullptr) { | 257 | if (frontend.photo_viewer == nullptr) { |
| 247 | frontend.photo_viewer = std::make_unique<Core::Frontend::DefaultPhotoViewerApplet>(); | 258 | frontend.photo_viewer = std::make_unique<Core::Frontend::DefaultPhotoViewerApplet>(); |
| 248 | } | 259 | } |
| @@ -277,6 +288,8 @@ std::shared_ptr<Applet> AppletManager::GetApplet(AppletId id, LibraryAppletMode | |||
| 277 | return std::make_shared<ProfileSelect>(system, mode, *frontend.profile_select); | 288 | return std::make_shared<ProfileSelect>(system, mode, *frontend.profile_select); |
| 278 | case AppletId::SoftwareKeyboard: | 289 | case AppletId::SoftwareKeyboard: |
| 279 | return std::make_shared<SoftwareKeyboard>(system, mode, *frontend.software_keyboard); | 290 | return std::make_shared<SoftwareKeyboard>(system, mode, *frontend.software_keyboard); |
| 291 | case AppletId::MiiEdit: | ||
| 292 | return std::make_shared<Mii>(system, mode, *frontend.mii); | ||
| 280 | case AppletId::Web: | 293 | case AppletId::Web: |
| 281 | case AppletId::Shop: | 294 | case AppletId::Shop: |
| 282 | case AppletId::OfflineWeb: | 295 | case AppletId::OfflineWeb: |
diff --git a/src/core/hle/service/am/applets/applets.h b/src/core/hle/service/am/applets/applets.h index 15eeb4ee1..0c44aec79 100644 --- a/src/core/hle/service/am/applets/applets.h +++ b/src/core/hle/service/am/applets/applets.h | |||
| @@ -21,6 +21,7 @@ class ControllerApplet; | |||
| 21 | class ECommerceApplet; | 21 | class ECommerceApplet; |
| 22 | class ErrorApplet; | 22 | class ErrorApplet; |
| 23 | class ParentalControlsApplet; | 23 | class ParentalControlsApplet; |
| 24 | class MiiApplet; | ||
| 24 | class PhotoViewerApplet; | 25 | class PhotoViewerApplet; |
| 25 | class ProfileSelectApplet; | 26 | class ProfileSelectApplet; |
| 26 | class SoftwareKeyboardApplet; | 27 | class SoftwareKeyboardApplet; |
| @@ -179,6 +180,7 @@ struct AppletFrontendSet { | |||
| 179 | using ControllerApplet = std::unique_ptr<Core::Frontend::ControllerApplet>; | 180 | using ControllerApplet = std::unique_ptr<Core::Frontend::ControllerApplet>; |
| 180 | using ErrorApplet = std::unique_ptr<Core::Frontend::ErrorApplet>; | 181 | using ErrorApplet = std::unique_ptr<Core::Frontend::ErrorApplet>; |
| 181 | using ParentalControlsApplet = std::unique_ptr<Core::Frontend::ParentalControlsApplet>; | 182 | using ParentalControlsApplet = std::unique_ptr<Core::Frontend::ParentalControlsApplet>; |
| 183 | using MiiApplet = std::unique_ptr<Core::Frontend::MiiApplet>; | ||
| 182 | using PhotoViewer = std::unique_ptr<Core::Frontend::PhotoViewerApplet>; | 184 | using PhotoViewer = std::unique_ptr<Core::Frontend::PhotoViewerApplet>; |
| 183 | using ProfileSelect = std::unique_ptr<Core::Frontend::ProfileSelectApplet>; | 185 | using ProfileSelect = std::unique_ptr<Core::Frontend::ProfileSelectApplet>; |
| 184 | using SoftwareKeyboard = std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet>; | 186 | using SoftwareKeyboard = std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet>; |
| @@ -186,9 +188,9 @@ struct AppletFrontendSet { | |||
| 186 | 188 | ||
| 187 | AppletFrontendSet(); | 189 | AppletFrontendSet(); |
| 188 | AppletFrontendSet(ControllerApplet controller_applet, ErrorApplet error_applet, | 190 | AppletFrontendSet(ControllerApplet controller_applet, ErrorApplet error_applet, |
| 189 | ParentalControlsApplet parental_controls_applet, PhotoViewer photo_viewer_, | 191 | ParentalControlsApplet parental_controls_applet, MiiApplet mii_applet, |
| 190 | ProfileSelect profile_select_, SoftwareKeyboard software_keyboard_, | 192 | PhotoViewer photo_viewer_, ProfileSelect profile_select_, |
| 191 | WebBrowser web_browser_); | 193 | SoftwareKeyboard software_keyboard_, WebBrowser web_browser_); |
| 192 | ~AppletFrontendSet(); | 194 | ~AppletFrontendSet(); |
| 193 | 195 | ||
| 194 | AppletFrontendSet(const AppletFrontendSet&) = delete; | 196 | AppletFrontendSet(const AppletFrontendSet&) = delete; |
| @@ -200,6 +202,7 @@ struct AppletFrontendSet { | |||
| 200 | ControllerApplet controller; | 202 | ControllerApplet controller; |
| 201 | ErrorApplet error; | 203 | ErrorApplet error; |
| 202 | ParentalControlsApplet parental_controls; | 204 | ParentalControlsApplet parental_controls; |
| 205 | MiiApplet mii; | ||
| 203 | PhotoViewer photo_viewer; | 206 | PhotoViewer photo_viewer; |
| 204 | ProfileSelect profile_select; | 207 | ProfileSelect profile_select; |
| 205 | SoftwareKeyboard software_keyboard; | 208 | SoftwareKeyboard software_keyboard; |
diff --git a/src/core/hle/service/apm/apm_controller.cpp b/src/core/hle/service/apm/apm_controller.cpp index 98839fe97..187fef2ad 100644 --- a/src/core/hle/service/apm/apm_controller.cpp +++ b/src/core/hle/service/apm/apm_controller.cpp | |||
| @@ -17,8 +17,8 @@ constexpr auto DEFAULT_PERFORMANCE_CONFIGURATION = PerformanceConfiguration::Con | |||
| 17 | 17 | ||
| 18 | Controller::Controller(Core::Timing::CoreTiming& core_timing_) | 18 | Controller::Controller(Core::Timing::CoreTiming& core_timing_) |
| 19 | : core_timing{core_timing_}, configs{ | 19 | : core_timing{core_timing_}, configs{ |
| 20 | {PerformanceMode::Handheld, DEFAULT_PERFORMANCE_CONFIGURATION}, | 20 | {PerformanceMode::Normal, DEFAULT_PERFORMANCE_CONFIGURATION}, |
| 21 | {PerformanceMode::Docked, DEFAULT_PERFORMANCE_CONFIGURATION}, | 21 | {PerformanceMode::Boost, DEFAULT_PERFORMANCE_CONFIGURATION}, |
| 22 | } {} | 22 | } {} |
| 23 | 23 | ||
| 24 | Controller::~Controller() = default; | 24 | Controller::~Controller() = default; |
| @@ -63,13 +63,13 @@ void Controller::SetFromCpuBoostMode(CpuBoostMode mode) { | |||
| 63 | PerformanceConfiguration::Config15, | 63 | PerformanceConfiguration::Config15, |
| 64 | }}; | 64 | }}; |
| 65 | 65 | ||
| 66 | SetPerformanceConfiguration(PerformanceMode::Docked, | 66 | SetPerformanceConfiguration(PerformanceMode::Boost, |
| 67 | BOOST_MODE_TO_CONFIG_MAP.at(static_cast<u32>(mode))); | 67 | BOOST_MODE_TO_CONFIG_MAP.at(static_cast<u32>(mode))); |
| 68 | } | 68 | } |
| 69 | 69 | ||
| 70 | PerformanceMode Controller::GetCurrentPerformanceMode() const { | 70 | PerformanceMode Controller::GetCurrentPerformanceMode() const { |
| 71 | return Settings::values.use_docked_mode.GetValue() ? PerformanceMode::Docked | 71 | return Settings::values.use_docked_mode.GetValue() ? PerformanceMode::Boost |
| 72 | : PerformanceMode::Handheld; | 72 | : PerformanceMode::Normal; |
| 73 | } | 73 | } |
| 74 | 74 | ||
| 75 | PerformanceConfiguration Controller::GetCurrentPerformanceConfiguration(PerformanceMode mode) { | 75 | PerformanceConfiguration Controller::GetCurrentPerformanceConfiguration(PerformanceMode mode) { |
diff --git a/src/core/hle/service/apm/apm_controller.h b/src/core/hle/service/apm/apm_controller.h index 8d48e0104..d6fbd2c0c 100644 --- a/src/core/hle/service/apm/apm_controller.h +++ b/src/core/hle/service/apm/apm_controller.h | |||
| @@ -32,15 +32,18 @@ enum class PerformanceConfiguration : u32 { | |||
| 32 | Config16 = 0x9222000C, | 32 | Config16 = 0x9222000C, |
| 33 | }; | 33 | }; |
| 34 | 34 | ||
| 35 | // This is nn::oe::CpuBoostMode | ||
| 35 | enum class CpuBoostMode : u32 { | 36 | enum class CpuBoostMode : u32 { |
| 36 | Disabled = 0, | 37 | Normal = 0, // Boost mode disabled |
| 37 | Full = 1, // CPU + GPU -> Config 13, 14, 15, or 16 | 38 | FastLoad = 1, // CPU + GPU -> Config 13, 14, 15, or 16 |
| 38 | Partial = 2, // GPU Only -> Config 15 or 16 | 39 | Partial = 2, // GPU Only -> Config 15 or 16 |
| 39 | }; | 40 | }; |
| 40 | 41 | ||
| 41 | enum class PerformanceMode : u8 { | 42 | // This is nn::oe::PerformanceMode |
| 42 | Handheld = 0, | 43 | enum class PerformanceMode : s32 { |
| 43 | Docked = 1, | 44 | Invalid = -1, |
| 45 | Normal = 0, | ||
| 46 | Boost = 1, | ||
| 44 | }; | 47 | }; |
| 45 | 48 | ||
| 46 | // Class to manage the state and change of the emulated system performance. | 49 | // Class to manage the state and change of the emulated system performance. |
diff --git a/src/core/hle/service/friend/friend.cpp b/src/core/hle/service/friend/friend.cpp index 9f9cea1e0..79cd3acbb 100644 --- a/src/core/hle/service/friend/friend.cpp +++ b/src/core/hle/service/friend/friend.cpp | |||
| @@ -173,7 +173,7 @@ private: | |||
| 173 | const auto uuid = rp.PopRaw<Common::UUID>(); | 173 | const auto uuid = rp.PopRaw<Common::UUID>(); |
| 174 | 174 | ||
| 175 | LOG_WARNING(Service_Friend, "(STUBBED) called, local_play={}, uuid=0x{}", local_play, | 175 | LOG_WARNING(Service_Friend, "(STUBBED) called, local_play={}, uuid=0x{}", local_play, |
| 176 | uuid.Format()); | 176 | uuid.RawString()); |
| 177 | 177 | ||
| 178 | IPC::ResponseBuilder rb{ctx, 2}; | 178 | IPC::ResponseBuilder rb{ctx, 2}; |
| 179 | rb.Push(ResultSuccess); | 179 | rb.Push(ResultSuccess); |
| @@ -186,7 +186,7 @@ private: | |||
| 186 | [[maybe_unused]] const auto filter = rp.PopRaw<SizedFriendFilter>(); | 186 | [[maybe_unused]] const auto filter = rp.PopRaw<SizedFriendFilter>(); |
| 187 | const auto pid = rp.Pop<u64>(); | 187 | const auto pid = rp.Pop<u64>(); |
| 188 | LOG_WARNING(Service_Friend, "(STUBBED) called, offset={}, uuid=0x{}, pid={}", friend_offset, | 188 | LOG_WARNING(Service_Friend, "(STUBBED) called, offset={}, uuid=0x{}, pid={}", friend_offset, |
| 189 | uuid.Format(), pid); | 189 | uuid.RawString(), pid); |
| 190 | 190 | ||
| 191 | IPC::ResponseBuilder rb{ctx, 3}; | 191 | IPC::ResponseBuilder rb{ctx, 3}; |
| 192 | rb.Push(ResultSuccess); | 192 | rb.Push(ResultSuccess); |
| @@ -312,7 +312,7 @@ void Module::Interface::CreateNotificationService(Kernel::HLERequestContext& ctx | |||
| 312 | IPC::RequestParser rp{ctx}; | 312 | IPC::RequestParser rp{ctx}; |
| 313 | auto uuid = rp.PopRaw<Common::UUID>(); | 313 | auto uuid = rp.PopRaw<Common::UUID>(); |
| 314 | 314 | ||
| 315 | LOG_DEBUG(Service_Friend, "called, uuid=0x{}", uuid.Format()); | 315 | LOG_DEBUG(Service_Friend, "called, uuid=0x{}", uuid.RawString()); |
| 316 | 316 | ||
| 317 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | 317 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; |
| 318 | rb.Push(ResultSuccess); | 318 | rb.Push(ResultSuccess); |
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index a2bf7defb..d9202ea6c 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp | |||
| @@ -320,7 +320,7 @@ Hid::Hid(Core::System& system_) | |||
| 320 | {308, nullptr, "SetSevenSixAxisSensorFusionStrength"}, | 320 | {308, nullptr, "SetSevenSixAxisSensorFusionStrength"}, |
| 321 | {309, nullptr, "GetSevenSixAxisSensorFusionStrength"}, | 321 | {309, nullptr, "GetSevenSixAxisSensorFusionStrength"}, |
| 322 | {310, &Hid::ResetSevenSixAxisSensorTimestamp, "ResetSevenSixAxisSensorTimestamp"}, | 322 | {310, &Hid::ResetSevenSixAxisSensorTimestamp, "ResetSevenSixAxisSensorTimestamp"}, |
| 323 | {400, nullptr, "IsUsbFullKeyControllerEnabled"}, | 323 | {400, &Hid::IsUsbFullKeyControllerEnabled, "IsUsbFullKeyControllerEnabled"}, |
| 324 | {401, nullptr, "EnableUsbFullKeyController"}, | 324 | {401, nullptr, "EnableUsbFullKeyController"}, |
| 325 | {402, nullptr, "IsUsbFullKeyControllerConnected"}, | 325 | {402, nullptr, "IsUsbFullKeyControllerConnected"}, |
| 326 | {403, nullptr, "HasBattery"}, | 326 | {403, nullptr, "HasBattery"}, |
| @@ -1673,6 +1673,16 @@ void Hid::ResetSevenSixAxisSensorTimestamp(Kernel::HLERequestContext& ctx) { | |||
| 1673 | rb.Push(ResultSuccess); | 1673 | rb.Push(ResultSuccess); |
| 1674 | } | 1674 | } |
| 1675 | 1675 | ||
| 1676 | void Hid::IsUsbFullKeyControllerEnabled(Kernel::HLERequestContext& ctx) { | ||
| 1677 | IPC::RequestParser rp{ctx}; | ||
| 1678 | |||
| 1679 | LOG_WARNING(Service_HID, "(STUBBED) called"); | ||
| 1680 | |||
| 1681 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 1682 | rb.Push(ResultSuccess); | ||
| 1683 | rb.Push(false); | ||
| 1684 | } | ||
| 1685 | |||
| 1676 | void Hid::SetIsPalmaAllConnectable(Kernel::HLERequestContext& ctx) { | 1686 | void Hid::SetIsPalmaAllConnectable(Kernel::HLERequestContext& ctx) { |
| 1677 | IPC::RequestParser rp{ctx}; | 1687 | IPC::RequestParser rp{ctx}; |
| 1678 | const auto applet_resource_user_id{rp.Pop<u64>()}; | 1688 | const auto applet_resource_user_id{rp.Pop<u64>()}; |
diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h index d290df161..c281081a7 100644 --- a/src/core/hle/service/hid/hid.h +++ b/src/core/hle/service/hid/hid.h | |||
| @@ -159,6 +159,7 @@ private: | |||
| 159 | void InitializeSevenSixAxisSensor(Kernel::HLERequestContext& ctx); | 159 | void InitializeSevenSixAxisSensor(Kernel::HLERequestContext& ctx); |
| 160 | void FinalizeSevenSixAxisSensor(Kernel::HLERequestContext& ctx); | 160 | void FinalizeSevenSixAxisSensor(Kernel::HLERequestContext& ctx); |
| 161 | void ResetSevenSixAxisSensorTimestamp(Kernel::HLERequestContext& ctx); | 161 | void ResetSevenSixAxisSensorTimestamp(Kernel::HLERequestContext& ctx); |
| 162 | void IsUsbFullKeyControllerEnabled(Kernel::HLERequestContext& ctx); | ||
| 162 | void SetIsPalmaAllConnectable(Kernel::HLERequestContext& ctx); | 163 | void SetIsPalmaAllConnectable(Kernel::HLERequestContext& ctx); |
| 163 | void SetPalmaBoostMode(Kernel::HLERequestContext& ctx); | 164 | void SetPalmaBoostMode(Kernel::HLERequestContext& ctx); |
| 164 | void SetNpadCommunicationMode(Kernel::HLERequestContext& ctx); | 165 | void SetNpadCommunicationMode(Kernel::HLERequestContext& ctx); |
diff --git a/src/core/hle/service/kernel_helpers.cpp b/src/core/hle/service/kernel_helpers.cpp index 62f4cdfb2..ff0bbb788 100644 --- a/src/core/hle/service/kernel_helpers.cpp +++ b/src/core/hle/service/kernel_helpers.cpp | |||
| @@ -3,7 +3,9 @@ | |||
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "core/core.h" | 5 | #include "core/core.h" |
| 6 | #include "core/core_timing.h" | ||
| 6 | #include "core/hle/kernel/k_event.h" | 7 | #include "core/hle/kernel/k_event.h" |
| 8 | #include "core/hle/kernel/k_memory_manager.h" | ||
| 7 | #include "core/hle/kernel/k_process.h" | 9 | #include "core/hle/kernel/k_process.h" |
| 8 | #include "core/hle/kernel/k_readable_event.h" | 10 | #include "core/hle/kernel/k_readable_event.h" |
| 9 | #include "core/hle/kernel/k_resource_limit.h" | 11 | #include "core/hle/kernel/k_resource_limit.h" |
| @@ -15,9 +17,11 @@ namespace Service::KernelHelpers { | |||
| 15 | 17 | ||
| 16 | ServiceContext::ServiceContext(Core::System& system_, std::string name_) | 18 | ServiceContext::ServiceContext(Core::System& system_, std::string name_) |
| 17 | : kernel(system_.Kernel()) { | 19 | : kernel(system_.Kernel()) { |
| 20 | // Create the process. | ||
| 18 | process = Kernel::KProcess::Create(kernel); | 21 | process = Kernel::KProcess::Create(kernel); |
| 19 | ASSERT(Kernel::KProcess::Initialize(process, system_, std::move(name_), | 22 | ASSERT(Kernel::KProcess::Initialize(process, system_, std::move(name_), |
| 20 | Kernel::KProcess::ProcessType::Userland) | 23 | Kernel::KProcess::ProcessType::KernelInternal, |
| 24 | kernel.GetSystemResourceLimit()) | ||
| 21 | .IsSuccess()); | 25 | .IsSuccess()); |
| 22 | } | 26 | } |
| 23 | 27 | ||
| @@ -43,7 +47,7 @@ Kernel::KEvent* ServiceContext::CreateEvent(std::string&& name) { | |||
| 43 | } | 47 | } |
| 44 | 48 | ||
| 45 | // Initialize the event. | 49 | // Initialize the event. |
| 46 | event->Initialize(std::move(name)); | 50 | event->Initialize(std::move(name), process); |
| 47 | 51 | ||
| 48 | // Commit the thread reservation. | 52 | // Commit the thread reservation. |
| 49 | event_reservation.Commit(); | 53 | event_reservation.Commit(); |
diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp index 9fc7bb1b1..099276420 100644 --- a/src/core/hle/service/ldr/ldr.cpp +++ b/src/core/hle/service/ldr/ldr.cpp | |||
| @@ -288,7 +288,7 @@ public: | |||
| 288 | } | 288 | } |
| 289 | 289 | ||
| 290 | bool ValidateRegionForMap(Kernel::KPageTable& page_table, VAddr start, std::size_t size) const { | 290 | bool ValidateRegionForMap(Kernel::KPageTable& page_table, VAddr start, std::size_t size) const { |
| 291 | constexpr std::size_t padding_size{4 * Kernel::PageSize}; | 291 | const std::size_t padding_size{page_table.GetNumGuardPages() * Kernel::PageSize}; |
| 292 | const auto start_info{page_table.QueryInfo(start - 1)}; | 292 | const auto start_info{page_table.QueryInfo(start - 1)}; |
| 293 | 293 | ||
| 294 | if (start_info.state != Kernel::KMemoryState::Free) { | 294 | if (start_info.state != Kernel::KMemoryState::Free) { |
| @@ -308,31 +308,69 @@ public: | |||
| 308 | return (start + size + padding_size) <= (end_info.GetAddress() + end_info.GetSize()); | 308 | return (start + size + padding_size) <= (end_info.GetAddress() + end_info.GetSize()); |
| 309 | } | 309 | } |
| 310 | 310 | ||
| 311 | VAddr GetRandomMapRegion(const Kernel::KPageTable& page_table, std::size_t size) const { | 311 | ResultCode GetAvailableMapRegion(Kernel::KPageTable& page_table, u64 size, VAddr& out_addr) { |
| 312 | VAddr addr{}; | 312 | size = Common::AlignUp(size, Kernel::PageSize); |
| 313 | const std::size_t end_pages{(page_table.GetAliasCodeRegionSize() - size) >> | 313 | size += page_table.GetNumGuardPages() * Kernel::PageSize * 4; |
| 314 | Kernel::PageBits}; | 314 | |
| 315 | do { | 315 | const auto is_region_available = [&](VAddr addr) { |
| 316 | addr = page_table.GetAliasCodeRegionStart() + | 316 | const auto end_addr = addr + size; |
| 317 | (Kernel::KSystemControl::GenerateRandomRange(0, end_pages) << Kernel::PageBits); | 317 | while (addr < end_addr) { |
| 318 | } while (!page_table.IsInsideAddressSpace(addr, size) || | 318 | if (system.Memory().IsValidVirtualAddress(addr)) { |
| 319 | page_table.IsInsideHeapRegion(addr, size) || | 319 | return false; |
| 320 | page_table.IsInsideAliasRegion(addr, size)); | 320 | } |
| 321 | return addr; | 321 | |
| 322 | if (!page_table.IsInsideAddressSpace(out_addr, size)) { | ||
| 323 | return false; | ||
| 324 | } | ||
| 325 | |||
| 326 | if (page_table.IsInsideHeapRegion(out_addr, size)) { | ||
| 327 | return false; | ||
| 328 | } | ||
| 329 | |||
| 330 | if (page_table.IsInsideAliasRegion(out_addr, size)) { | ||
| 331 | return false; | ||
| 332 | } | ||
| 333 | |||
| 334 | addr += Kernel::PageSize; | ||
| 335 | } | ||
| 336 | return true; | ||
| 337 | }; | ||
| 338 | |||
| 339 | bool succeeded = false; | ||
| 340 | const auto map_region_end = | ||
| 341 | page_table.GetAliasCodeRegionStart() + page_table.GetAliasCodeRegionSize(); | ||
| 342 | while (current_map_addr < map_region_end) { | ||
| 343 | if (is_region_available(current_map_addr)) { | ||
| 344 | succeeded = true; | ||
| 345 | break; | ||
| 346 | } | ||
| 347 | current_map_addr += 0x100000; | ||
| 348 | } | ||
| 349 | |||
| 350 | if (!succeeded) { | ||
| 351 | UNREACHABLE_MSG("Out of address space!"); | ||
| 352 | return Kernel::ResultOutOfMemory; | ||
| 353 | } | ||
| 354 | |||
| 355 | out_addr = current_map_addr; | ||
| 356 | current_map_addr += size; | ||
| 357 | |||
| 358 | return ResultSuccess; | ||
| 322 | } | 359 | } |
| 323 | 360 | ||
| 324 | ResultVal<VAddr> MapProcessCodeMemory(Kernel::KProcess* process, VAddr baseAddress, | 361 | ResultVal<VAddr> MapProcessCodeMemory(Kernel::KProcess* process, VAddr base_addr, u64 size) { |
| 325 | u64 size) const { | 362 | auto& page_table{process->PageTable()}; |
| 363 | VAddr addr{}; | ||
| 364 | |||
| 326 | for (std::size_t retry = 0; retry < MAXIMUM_MAP_RETRIES; retry++) { | 365 | for (std::size_t retry = 0; retry < MAXIMUM_MAP_RETRIES; retry++) { |
| 327 | auto& page_table{process->PageTable()}; | 366 | R_TRY(GetAvailableMapRegion(page_table, size, addr)); |
| 328 | const VAddr addr{GetRandomMapRegion(page_table, size)}; | ||
| 329 | const ResultCode result{page_table.MapCodeMemory(addr, baseAddress, size)}; | ||
| 330 | 367 | ||
| 368 | const ResultCode result{page_table.MapCodeMemory(addr, base_addr, size)}; | ||
| 331 | if (result == Kernel::ResultInvalidCurrentMemory) { | 369 | if (result == Kernel::ResultInvalidCurrentMemory) { |
| 332 | continue; | 370 | continue; |
| 333 | } | 371 | } |
| 334 | 372 | ||
| 335 | CASCADE_CODE(result); | 373 | R_TRY(result); |
| 336 | 374 | ||
| 337 | if (ValidateRegionForMap(page_table, addr, size)) { | 375 | if (ValidateRegionForMap(page_table, addr, size)) { |
| 338 | return addr; | 376 | return addr; |
| @@ -343,7 +381,7 @@ public: | |||
| 343 | } | 381 | } |
| 344 | 382 | ||
| 345 | ResultVal<VAddr> MapNro(Kernel::KProcess* process, VAddr nro_addr, std::size_t nro_size, | 383 | ResultVal<VAddr> MapNro(Kernel::KProcess* process, VAddr nro_addr, std::size_t nro_size, |
| 346 | VAddr bss_addr, std::size_t bss_size, std::size_t size) const { | 384 | VAddr bss_addr, std::size_t bss_size, std::size_t size) { |
| 347 | for (std::size_t retry = 0; retry < MAXIMUM_MAP_RETRIES; retry++) { | 385 | for (std::size_t retry = 0; retry < MAXIMUM_MAP_RETRIES; retry++) { |
| 348 | auto& page_table{process->PageTable()}; | 386 | auto& page_table{process->PageTable()}; |
| 349 | VAddr addr{}; | 387 | VAddr addr{}; |
| @@ -597,6 +635,7 @@ public: | |||
| 597 | LOG_WARNING(Service_LDR, "(STUBBED) called"); | 635 | LOG_WARNING(Service_LDR, "(STUBBED) called"); |
| 598 | 636 | ||
| 599 | initialized = true; | 637 | initialized = true; |
| 638 | current_map_addr = system.CurrentProcess()->PageTable().GetAliasCodeRegionStart(); | ||
| 600 | 639 | ||
| 601 | IPC::ResponseBuilder rb{ctx, 2}; | 640 | IPC::ResponseBuilder rb{ctx, 2}; |
| 602 | rb.Push(ResultSuccess); | 641 | rb.Push(ResultSuccess); |
| @@ -607,6 +646,7 @@ private: | |||
| 607 | 646 | ||
| 608 | std::map<VAddr, NROInfo> nro; | 647 | std::map<VAddr, NROInfo> nro; |
| 609 | std::map<VAddr, std::vector<SHA256Hash>> nrr; | 648 | std::map<VAddr, std::vector<SHA256Hash>> nrr; |
| 649 | VAddr current_map_addr{}; | ||
| 610 | 650 | ||
| 611 | bool IsValidNROHash(const SHA256Hash& hash) const { | 651 | bool IsValidNROHash(const SHA256Hash& hash) const { |
| 612 | return std::any_of(nrr.begin(), nrr.end(), [&hash](const auto& p) { | 652 | return std::any_of(nrr.begin(), nrr.end(), [&hash](const auto& p) { |
diff --git a/src/core/hle/service/mii/mii_manager.cpp b/src/core/hle/service/mii/mii_manager.cpp index ca4ed35bb..0a57c3cde 100644 --- a/src/core/hle/service/mii/mii_manager.cpp +++ b/src/core/hle/service/mii/mii_manager.cpp | |||
| @@ -118,16 +118,6 @@ u16 GenerateCrc16(const void* data, std::size_t size) { | |||
| 118 | return Common::swap16(static_cast<u16>(crc)); | 118 | return Common::swap16(static_cast<u16>(crc)); |
| 119 | } | 119 | } |
| 120 | 120 | ||
| 121 | Common::UUID GenerateValidUUID() { | ||
| 122 | auto uuid{Common::UUID::Generate()}; | ||
| 123 | |||
| 124 | // Bit 7 must be set, and bit 6 unset for the UUID to be valid | ||
| 125 | uuid.uuid[1] &= 0xFFFFFFFFFFFFFF3FULL; | ||
| 126 | uuid.uuid[1] |= 0x0000000000000080ULL; | ||
| 127 | |||
| 128 | return uuid; | ||
| 129 | } | ||
| 130 | |||
| 131 | template <typename T> | 121 | template <typename T> |
| 132 | T GetRandomValue(T min, T max) { | 122 | T GetRandomValue(T min, T max) { |
| 133 | std::random_device device; | 123 | std::random_device device; |
| @@ -383,7 +373,7 @@ MiiStoreData::MiiStoreData() = default; | |||
| 383 | MiiStoreData::MiiStoreData(const MiiStoreData::Name& name, const MiiStoreBitFields& bit_fields, | 373 | MiiStoreData::MiiStoreData(const MiiStoreData::Name& name, const MiiStoreBitFields& bit_fields, |
| 384 | const Common::UUID& user_id) { | 374 | const Common::UUID& user_id) { |
| 385 | data.name = name; | 375 | data.name = name; |
| 386 | data.uuid = GenerateValidUUID(); | 376 | data.uuid = Common::UUID::MakeRandomRFC4122V4(); |
| 387 | 377 | ||
| 388 | std::memcpy(data.data.data(), &bit_fields, sizeof(MiiStoreBitFields)); | 378 | std::memcpy(data.data.data(), &bit_fields, sizeof(MiiStoreBitFields)); |
| 389 | data_crc = GenerateCrc16(data.data.data(), sizeof(data)); | 379 | data_crc = GenerateCrc16(data.data.data(), sizeof(data)); |
diff --git a/src/core/hle/service/mii/mii_manager.h b/src/core/hle/service/mii/mii_manager.h index 8e048fc56..6999d15b1 100644 --- a/src/core/hle/service/mii/mii_manager.h +++ b/src/core/hle/service/mii/mii_manager.h | |||
| @@ -202,7 +202,7 @@ struct MiiStoreData { | |||
| 202 | static_assert(sizeof(MiiStoreBitFields) == sizeof(data), "data field has incorrect size."); | 202 | static_assert(sizeof(MiiStoreBitFields) == sizeof(data), "data field has incorrect size."); |
| 203 | 203 | ||
| 204 | Name name{}; | 204 | Name name{}; |
| 205 | Common::UUID uuid{Common::INVALID_UUID}; | 205 | Common::UUID uuid{}; |
| 206 | } data; | 206 | } data; |
| 207 | 207 | ||
| 208 | u16 data_crc{}; | 208 | u16 data_crc{}; |
| @@ -326,7 +326,7 @@ public: | |||
| 326 | ResultCode GetIndex(const MiiInfo& info, u32& index); | 326 | ResultCode GetIndex(const MiiInfo& info, u32& index); |
| 327 | 327 | ||
| 328 | private: | 328 | private: |
| 329 | const Common::UUID user_id{Common::INVALID_UUID}; | 329 | const Common::UUID user_id{}; |
| 330 | u64 update_counter{}; | 330 | u64 update_counter{}; |
| 331 | }; | 331 | }; |
| 332 | 332 | ||
diff --git a/src/core/hle/service/mnpp/mnpp_app.cpp b/src/core/hle/service/mnpp/mnpp_app.cpp new file mode 100644 index 000000000..53497612f --- /dev/null +++ b/src/core/hle/service/mnpp/mnpp_app.cpp | |||
| @@ -0,0 +1,45 @@ | |||
| 1 | // Copyright 2022 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "common/logging/log.h" | ||
| 6 | #include "core/hle/ipc_helpers.h" | ||
| 7 | #include "core/hle/service/mnpp/mnpp_app.h" | ||
| 8 | #include "core/hle/service/sm/sm.h" | ||
| 9 | |||
| 10 | namespace Service::MNPP { | ||
| 11 | |||
| 12 | class MNPP_APP final : public ServiceFramework<MNPP_APP> { | ||
| 13 | public: | ||
| 14 | explicit MNPP_APP(Core::System& system_) : ServiceFramework{system_, "mnpp:app"} { | ||
| 15 | // clang-format off | ||
| 16 | static const FunctionInfo functions[] = { | ||
| 17 | {0, &MNPP_APP::Unknown0, "unknown0"}, | ||
| 18 | {1, &MNPP_APP::Unknown1, "unknown1"}, | ||
| 19 | }; | ||
| 20 | // clang-format on | ||
| 21 | |||
| 22 | RegisterHandlers(functions); | ||
| 23 | } | ||
| 24 | |||
| 25 | private: | ||
| 26 | void Unknown0(Kernel::HLERequestContext& ctx) { | ||
| 27 | LOG_WARNING(Service_MNPP, "(STUBBED) called"); | ||
| 28 | |||
| 29 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 30 | rb.Push(ResultSuccess); | ||
| 31 | } | ||
| 32 | |||
| 33 | void Unknown1(Kernel::HLERequestContext& ctx) { | ||
| 34 | LOG_WARNING(Service_MNPP, "(STUBBED) called"); | ||
| 35 | |||
| 36 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 37 | rb.Push(ResultSuccess); | ||
| 38 | } | ||
| 39 | }; | ||
| 40 | |||
| 41 | void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) { | ||
| 42 | std::make_shared<MNPP_APP>(system)->InstallAsService(service_manager); | ||
| 43 | } | ||
| 44 | |||
| 45 | } // namespace Service::MNPP | ||
diff --git a/src/core/hle/service/mnpp/mnpp_app.h b/src/core/hle/service/mnpp/mnpp_app.h new file mode 100644 index 000000000..6bf20b494 --- /dev/null +++ b/src/core/hle/service/mnpp/mnpp_app.h | |||
| @@ -0,0 +1,20 @@ | |||
| 1 | // Copyright 2022 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | namespace Core { | ||
| 8 | class System; | ||
| 9 | } | ||
| 10 | |||
| 11 | namespace Service::SM { | ||
| 12 | class ServiceManager; | ||
| 13 | } | ||
| 14 | |||
| 15 | namespace Service::MNPP { | ||
| 16 | |||
| 17 | /// Registers all MNPP services with the specified service manager. | ||
| 18 | void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system); | ||
| 19 | |||
| 20 | } // namespace Service::MNPP | ||
diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp index 761d0d3c6..513107715 100644 --- a/src/core/hle/service/nfp/nfp.cpp +++ b/src/core/hle/service/nfp/nfp.cpp | |||
| @@ -7,6 +7,9 @@ | |||
| 7 | 7 | ||
| 8 | #include "common/logging/log.h" | 8 | #include "common/logging/log.h" |
| 9 | #include "core/core.h" | 9 | #include "core/core.h" |
| 10 | #include "core/hid/emulated_controller.h" | ||
| 11 | #include "core/hid/hid_core.h" | ||
| 12 | #include "core/hid/hid_types.h" | ||
| 10 | #include "core/hle/ipc_helpers.h" | 13 | #include "core/hle/ipc_helpers.h" |
| 11 | #include "core/hle/kernel/k_event.h" | 14 | #include "core/hle/kernel/k_event.h" |
| 12 | #include "core/hle/service/nfp/nfp.h" | 15 | #include "core/hle/service/nfp/nfp.h" |
| @@ -14,343 +17,790 @@ | |||
| 14 | 17 | ||
| 15 | namespace Service::NFP { | 18 | namespace Service::NFP { |
| 16 | namespace ErrCodes { | 19 | namespace ErrCodes { |
| 17 | constexpr ResultCode ERR_NO_APPLICATION_AREA(ErrorModule::NFP, 152); | 20 | constexpr ResultCode DeviceNotFound(ErrorModule::NFP, 64); |
| 21 | constexpr ResultCode WrongDeviceState(ErrorModule::NFP, 73); | ||
| 22 | constexpr ResultCode ApplicationAreaIsNotInitialized(ErrorModule::NFP, 128); | ||
| 23 | constexpr ResultCode ApplicationAreaExist(ErrorModule::NFP, 168); | ||
| 18 | } // namespace ErrCodes | 24 | } // namespace ErrCodes |
| 19 | 25 | ||
| 20 | Module::Interface::Interface(std::shared_ptr<Module> module_, Core::System& system_, | 26 | constexpr u32 ApplicationAreaSize = 0xD8; |
| 21 | const char* name) | 27 | |
| 22 | : ServiceFramework{system_, name}, module{std::move(module_)}, service_context{system_, | 28 | IUser::IUser(Module::Interface& nfp_interface_, Core::System& system_) |
| 23 | "NFP::IUser"} { | 29 | : ServiceFramework{system_, "NFP::IUser"}, service_context{system_, service_name}, |
| 24 | nfc_tag_load = service_context.CreateEvent("NFP::IUser:NFCTagDetected"); | 30 | nfp_interface{nfp_interface_} { |
| 25 | } | 31 | static const FunctionInfo functions[] = { |
| 26 | 32 | {0, &IUser::Initialize, "Initialize"}, | |
| 27 | Module::Interface::~Interface() { | 33 | {1, &IUser::Finalize, "Finalize"}, |
| 28 | service_context.CloseEvent(nfc_tag_load); | 34 | {2, &IUser::ListDevices, "ListDevices"}, |
| 29 | } | 35 | {3, &IUser::StartDetection, "StartDetection"}, |
| 30 | 36 | {4, &IUser::StopDetection, "StopDetection"}, | |
| 31 | class IUser final : public ServiceFramework<IUser> { | 37 | {5, &IUser::Mount, "Mount"}, |
| 32 | public: | 38 | {6, &IUser::Unmount, "Unmount"}, |
| 33 | explicit IUser(Module::Interface& nfp_interface_, Core::System& system_, | 39 | {7, &IUser::OpenApplicationArea, "OpenApplicationArea"}, |
| 34 | KernelHelpers::ServiceContext& service_context_) | 40 | {8, &IUser::GetApplicationArea, "GetApplicationArea"}, |
| 35 | : ServiceFramework{system_, "NFP::IUser"}, nfp_interface{nfp_interface_}, | 41 | {9, &IUser::SetApplicationArea, "SetApplicationArea"}, |
| 36 | service_context{service_context_} { | 42 | {10, nullptr, "Flush"}, |
| 37 | static const FunctionInfo functions[] = { | 43 | {11, nullptr, "Restore"}, |
| 38 | {0, &IUser::Initialize, "Initialize"}, | 44 | {12, &IUser::CreateApplicationArea, "CreateApplicationArea"}, |
| 39 | {1, &IUser::Finalize, "Finalize"}, | 45 | {13, &IUser::GetTagInfo, "GetTagInfo"}, |
| 40 | {2, &IUser::ListDevices, "ListDevices"}, | 46 | {14, &IUser::GetRegisterInfo, "GetRegisterInfo"}, |
| 41 | {3, &IUser::StartDetection, "StartDetection"}, | 47 | {15, &IUser::GetCommonInfo, "GetCommonInfo"}, |
| 42 | {4, &IUser::StopDetection, "StopDetection"}, | 48 | {16, &IUser::GetModelInfo, "GetModelInfo"}, |
| 43 | {5, &IUser::Mount, "Mount"}, | 49 | {17, &IUser::AttachActivateEvent, "AttachActivateEvent"}, |
| 44 | {6, &IUser::Unmount, "Unmount"}, | 50 | {18, &IUser::AttachDeactivateEvent, "AttachDeactivateEvent"}, |
| 45 | {7, &IUser::OpenApplicationArea, "OpenApplicationArea"}, | 51 | {19, &IUser::GetState, "GetState"}, |
| 46 | {8, &IUser::GetApplicationArea, "GetApplicationArea"}, | 52 | {20, &IUser::GetDeviceState, "GetDeviceState"}, |
| 47 | {9, nullptr, "SetApplicationArea"}, | 53 | {21, &IUser::GetNpadId, "GetNpadId"}, |
| 48 | {10, nullptr, "Flush"}, | 54 | {22, &IUser::GetApplicationAreaSize, "GetApplicationAreaSize"}, |
| 49 | {11, nullptr, "Restore"}, | 55 | {23, &IUser::AttachAvailabilityChangeEvent, "AttachAvailabilityChangeEvent"}, |
| 50 | {12, nullptr, "CreateApplicationArea"}, | 56 | {24, nullptr, "RecreateApplicationArea"}, |
| 51 | {13, &IUser::GetTagInfo, "GetTagInfo"}, | 57 | }; |
| 52 | {14, &IUser::GetRegisterInfo, "GetRegisterInfo"}, | 58 | RegisterHandlers(functions); |
| 53 | {15, &IUser::GetCommonInfo, "GetCommonInfo"}, | ||
| 54 | {16, &IUser::GetModelInfo, "GetModelInfo"}, | ||
| 55 | {17, &IUser::AttachActivateEvent, "AttachActivateEvent"}, | ||
| 56 | {18, &IUser::AttachDeactivateEvent, "AttachDeactivateEvent"}, | ||
| 57 | {19, &IUser::GetState, "GetState"}, | ||
| 58 | {20, &IUser::GetDeviceState, "GetDeviceState"}, | ||
| 59 | {21, &IUser::GetNpadId, "GetNpadId"}, | ||
| 60 | {22, &IUser::GetApplicationAreaSize, "GetApplicationAreaSize"}, | ||
| 61 | {23, &IUser::AttachAvailabilityChangeEvent, "AttachAvailabilityChangeEvent"}, | ||
| 62 | {24, nullptr, "RecreateApplicationArea"}, | ||
| 63 | }; | ||
| 64 | RegisterHandlers(functions); | ||
| 65 | 59 | ||
| 66 | deactivate_event = service_context.CreateEvent("NFP::IUser:DeactivateEvent"); | 60 | availability_change_event = service_context.CreateEvent("IUser:AvailabilityChangeEvent"); |
| 67 | availability_change_event = | 61 | } |
| 68 | service_context.CreateEvent("NFP::IUser:AvailabilityChangeEvent"); | ||
| 69 | } | ||
| 70 | 62 | ||
| 71 | ~IUser() override { | 63 | void IUser::Initialize(Kernel::HLERequestContext& ctx) { |
| 72 | service_context.CloseEvent(deactivate_event); | 64 | LOG_INFO(Service_NFC, "called"); |
| 73 | service_context.CloseEvent(availability_change_event); | ||
| 74 | } | ||
| 75 | 65 | ||
| 76 | private: | 66 | state = State::Initialized; |
| 77 | struct TagInfo { | ||
| 78 | std::array<u8, 10> uuid; | ||
| 79 | u8 uuid_length; // TODO(ogniK): Figure out if this is actual the uuid length or does it | ||
| 80 | // mean something else | ||
| 81 | std::array<u8, 0x15> padding_1; | ||
| 82 | u32_le protocol; | ||
| 83 | u32_le tag_type; | ||
| 84 | std::array<u8, 0x2c> padding_2; | ||
| 85 | }; | ||
| 86 | static_assert(sizeof(TagInfo) == 0x54, "TagInfo is an invalid size"); | ||
| 87 | 67 | ||
| 88 | enum class State : u32 { | 68 | // TODO(german77): Loop through all interfaces |
| 89 | NonInitialized = 0, | 69 | nfp_interface.Initialize(); |
| 90 | Initialized = 1, | ||
| 91 | }; | ||
| 92 | 70 | ||
| 93 | enum class DeviceState : u32 { | 71 | IPC::ResponseBuilder rb{ctx, 2, 0}; |
| 94 | Initialized = 0, | 72 | rb.Push(ResultSuccess); |
| 95 | SearchingForTag = 1, | 73 | } |
| 96 | TagFound = 2, | ||
| 97 | TagRemoved = 3, | ||
| 98 | TagNearby = 4, | ||
| 99 | Unknown5 = 5, | ||
| 100 | Finalized = 6 | ||
| 101 | }; | ||
| 102 | 74 | ||
| 103 | struct CommonInfo { | 75 | void IUser::Finalize(Kernel::HLERequestContext& ctx) { |
| 104 | u16_be last_write_year; | 76 | LOG_INFO(Service_NFP, "called"); |
| 105 | u8 last_write_month; | ||
| 106 | u8 last_write_day; | ||
| 107 | u16_be write_counter; | ||
| 108 | u16_be version; | ||
| 109 | u32_be application_area_size; | ||
| 110 | INSERT_PADDING_BYTES(0x34); | ||
| 111 | }; | ||
| 112 | static_assert(sizeof(CommonInfo) == 0x40, "CommonInfo is an invalid size"); | ||
| 113 | 77 | ||
| 114 | void Initialize(Kernel::HLERequestContext& ctx) { | 78 | state = State::NonInitialized; |
| 115 | LOG_DEBUG(Service_NFC, "called"); | ||
| 116 | 79 | ||
| 117 | IPC::ResponseBuilder rb{ctx, 2, 0}; | 80 | // TODO(german77): Loop through all interfaces |
| 118 | rb.Push(ResultSuccess); | 81 | nfp_interface.Finalize(); |
| 119 | 82 | ||
| 120 | state = State::Initialized; | 83 | IPC::ResponseBuilder rb{ctx, 2}; |
| 121 | } | 84 | rb.Push(ResultSuccess); |
| 85 | } | ||
| 122 | 86 | ||
| 123 | void GetState(Kernel::HLERequestContext& ctx) { | 87 | void IUser::ListDevices(Kernel::HLERequestContext& ctx) { |
| 124 | LOG_DEBUG(Service_NFC, "called"); | 88 | LOG_INFO(Service_NFP, "called"); |
| 125 | 89 | ||
| 126 | IPC::ResponseBuilder rb{ctx, 3, 0}; | 90 | std::vector<u64> devices; |
| 127 | rb.Push(ResultSuccess); | 91 | |
| 128 | rb.PushRaw<u32>(static_cast<u32>(state)); | 92 | // TODO(german77): Loop through all interfaces |
| 93 | devices.push_back(nfp_interface.GetHandle()); | ||
| 94 | |||
| 95 | ctx.WriteBuffer(devices); | ||
| 96 | |||
| 97 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 98 | rb.Push(ResultSuccess); | ||
| 99 | rb.Push(static_cast<s32>(devices.size())); | ||
| 100 | } | ||
| 101 | |||
| 102 | void IUser::StartDetection(Kernel::HLERequestContext& ctx) { | ||
| 103 | IPC::RequestParser rp{ctx}; | ||
| 104 | const auto device_handle{rp.Pop<u64>()}; | ||
| 105 | const auto nfp_protocol{rp.Pop<s32>()}; | ||
| 106 | LOG_INFO(Service_NFP, "called, device_handle={}, nfp_protocol={}", device_handle, nfp_protocol); | ||
| 107 | |||
| 108 | // TODO(german77): Loop through all interfaces | ||
| 109 | if (device_handle == nfp_interface.GetHandle()) { | ||
| 110 | const auto result = nfp_interface.StartDetection(nfp_protocol); | ||
| 111 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 112 | rb.Push(result); | ||
| 113 | return; | ||
| 129 | } | 114 | } |
| 130 | 115 | ||
| 131 | void ListDevices(Kernel::HLERequestContext& ctx) { | 116 | LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); |
| 132 | IPC::RequestParser rp{ctx}; | ||
| 133 | const u32 array_size = rp.Pop<u32>(); | ||
| 134 | LOG_DEBUG(Service_NFP, "called, array_size={}", array_size); | ||
| 135 | 117 | ||
| 136 | ctx.WriteBuffer(device_handle); | 118 | IPC::ResponseBuilder rb{ctx, 2}; |
| 119 | rb.Push(ErrCodes::DeviceNotFound); | ||
| 120 | } | ||
| 137 | 121 | ||
| 138 | IPC::ResponseBuilder rb{ctx, 3}; | 122 | void IUser::StopDetection(Kernel::HLERequestContext& ctx) { |
| 139 | rb.Push(ResultSuccess); | 123 | IPC::RequestParser rp{ctx}; |
| 140 | rb.Push<u32>(1); | 124 | const auto device_handle{rp.Pop<u64>()}; |
| 125 | LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); | ||
| 126 | |||
| 127 | // TODO(german77): Loop through all interfaces | ||
| 128 | if (device_handle == nfp_interface.GetHandle()) { | ||
| 129 | const auto result = nfp_interface.StopDetection(); | ||
| 130 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 131 | rb.Push(result); | ||
| 132 | return; | ||
| 141 | } | 133 | } |
| 142 | 134 | ||
| 143 | void GetNpadId(Kernel::HLERequestContext& ctx) { | 135 | LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); |
| 144 | IPC::RequestParser rp{ctx}; | ||
| 145 | const u64 dev_handle = rp.Pop<u64>(); | ||
| 146 | LOG_DEBUG(Service_NFP, "called, dev_handle=0x{:X}", dev_handle); | ||
| 147 | 136 | ||
| 148 | IPC::ResponseBuilder rb{ctx, 3}; | 137 | IPC::ResponseBuilder rb{ctx, 2}; |
| 149 | rb.Push(ResultSuccess); | 138 | rb.Push(ErrCodes::DeviceNotFound); |
| 150 | rb.Push<u32>(npad_id); | 139 | } |
| 140 | |||
| 141 | void IUser::Mount(Kernel::HLERequestContext& ctx) { | ||
| 142 | IPC::RequestParser rp{ctx}; | ||
| 143 | const auto device_handle{rp.Pop<u64>()}; | ||
| 144 | const auto model_type{rp.PopEnum<ModelType>()}; | ||
| 145 | const auto mount_target{rp.PopEnum<MountTarget>()}; | ||
| 146 | LOG_INFO(Service_NFP, "called, device_handle={}, model_type={}, mount_target={}", device_handle, | ||
| 147 | model_type, mount_target); | ||
| 148 | |||
| 149 | // TODO(german77): Loop through all interfaces | ||
| 150 | if (device_handle == nfp_interface.GetHandle()) { | ||
| 151 | const auto result = nfp_interface.Mount(); | ||
| 152 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 153 | rb.Push(result); | ||
| 154 | return; | ||
| 151 | } | 155 | } |
| 152 | 156 | ||
| 153 | void AttachActivateEvent(Kernel::HLERequestContext& ctx) { | 157 | LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); |
| 154 | IPC::RequestParser rp{ctx}; | ||
| 155 | const u64 dev_handle = rp.Pop<u64>(); | ||
| 156 | LOG_DEBUG(Service_NFP, "called, dev_handle=0x{:X}", dev_handle); | ||
| 157 | 158 | ||
| 158 | IPC::ResponseBuilder rb{ctx, 2, 1}; | 159 | IPC::ResponseBuilder rb{ctx, 2}; |
| 159 | rb.Push(ResultSuccess); | 160 | rb.Push(ErrCodes::DeviceNotFound); |
| 160 | rb.PushCopyObjects(nfp_interface.GetNFCEvent()); | 161 | } |
| 161 | has_attached_handle = true; | 162 | |
| 163 | void IUser::Unmount(Kernel::HLERequestContext& ctx) { | ||
| 164 | IPC::RequestParser rp{ctx}; | ||
| 165 | const auto device_handle{rp.Pop<u64>()}; | ||
| 166 | LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); | ||
| 167 | |||
| 168 | // TODO(german77): Loop through all interfaces | ||
| 169 | if (device_handle == nfp_interface.GetHandle()) { | ||
| 170 | const auto result = nfp_interface.Unmount(); | ||
| 171 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 172 | rb.Push(result); | ||
| 173 | return; | ||
| 162 | } | 174 | } |
| 163 | 175 | ||
| 164 | void AttachDeactivateEvent(Kernel::HLERequestContext& ctx) { | 176 | LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); |
| 165 | IPC::RequestParser rp{ctx}; | ||
| 166 | const u64 dev_handle = rp.Pop<u64>(); | ||
| 167 | LOG_DEBUG(Service_NFP, "called, dev_handle=0x{:X}", dev_handle); | ||
| 168 | 177 | ||
| 169 | IPC::ResponseBuilder rb{ctx, 2, 1}; | 178 | IPC::ResponseBuilder rb{ctx, 2}; |
| 170 | rb.Push(ResultSuccess); | 179 | rb.Push(ErrCodes::DeviceNotFound); |
| 171 | rb.PushCopyObjects(deactivate_event->GetReadableEvent()); | 180 | } |
| 172 | } | 181 | |
| 173 | 182 | void IUser::OpenApplicationArea(Kernel::HLERequestContext& ctx) { | |
| 174 | void StopDetection(Kernel::HLERequestContext& ctx) { | 183 | IPC::RequestParser rp{ctx}; |
| 175 | LOG_DEBUG(Service_NFP, "called"); | 184 | const auto device_handle{rp.Pop<u64>()}; |
| 176 | 185 | const auto access_id{rp.Pop<u32>()}; | |
| 177 | switch (device_state) { | 186 | LOG_WARNING(Service_NFP, "(STUBBED) called, device_handle={}, access_id={}", device_handle, |
| 178 | case DeviceState::TagFound: | 187 | access_id); |
| 179 | case DeviceState::TagNearby: | 188 | |
| 180 | deactivate_event->GetWritableEvent().Signal(); | 189 | // TODO(german77): Loop through all interfaces |
| 181 | device_state = DeviceState::Initialized; | 190 | if (device_handle == nfp_interface.GetHandle()) { |
| 182 | break; | 191 | const auto result = nfp_interface.OpenApplicationArea(access_id); |
| 183 | case DeviceState::SearchingForTag: | ||
| 184 | case DeviceState::TagRemoved: | ||
| 185 | device_state = DeviceState::Initialized; | ||
| 186 | break; | ||
| 187 | default: | ||
| 188 | break; | ||
| 189 | } | ||
| 190 | IPC::ResponseBuilder rb{ctx, 2}; | 192 | IPC::ResponseBuilder rb{ctx, 2}; |
| 191 | rb.Push(ResultSuccess); | 193 | rb.Push(result); |
| 194 | return; | ||
| 192 | } | 195 | } |
| 193 | 196 | ||
| 194 | void GetDeviceState(Kernel::HLERequestContext& ctx) { | 197 | LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); |
| 195 | LOG_DEBUG(Service_NFP, "called"); | 198 | |
| 199 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 200 | rb.Push(ErrCodes::DeviceNotFound); | ||
| 201 | } | ||
| 202 | |||
| 203 | void IUser::GetApplicationArea(Kernel::HLERequestContext& ctx) { | ||
| 204 | IPC::RequestParser rp{ctx}; | ||
| 205 | const auto device_handle{rp.Pop<u64>()}; | ||
| 206 | LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); | ||
| 196 | 207 | ||
| 208 | // TODO(german77): Loop through all interfaces | ||
| 209 | if (device_handle == nfp_interface.GetHandle()) { | ||
| 210 | std::vector<u8> data{}; | ||
| 211 | const auto result = nfp_interface.GetApplicationArea(data); | ||
| 212 | ctx.WriteBuffer(data); | ||
| 197 | IPC::ResponseBuilder rb{ctx, 3}; | 213 | IPC::ResponseBuilder rb{ctx, 3}; |
| 198 | rb.Push(ResultSuccess); | 214 | rb.Push(result); |
| 199 | rb.Push<u32>(static_cast<u32>(device_state)); | 215 | rb.Push(static_cast<u32>(data.size())); |
| 216 | return; | ||
| 200 | } | 217 | } |
| 201 | 218 | ||
| 202 | void StartDetection(Kernel::HLERequestContext& ctx) { | 219 | LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); |
| 203 | LOG_DEBUG(Service_NFP, "called"); | ||
| 204 | 220 | ||
| 205 | if (device_state == DeviceState::Initialized || device_state == DeviceState::TagRemoved) { | 221 | IPC::ResponseBuilder rb{ctx, 2}; |
| 206 | device_state = DeviceState::SearchingForTag; | 222 | rb.Push(ErrCodes::DeviceNotFound); |
| 207 | } | 223 | } |
| 224 | |||
| 225 | void IUser::SetApplicationArea(Kernel::HLERequestContext& ctx) { | ||
| 226 | IPC::RequestParser rp{ctx}; | ||
| 227 | const auto device_handle{rp.Pop<u64>()}; | ||
| 228 | const auto data{ctx.ReadBuffer()}; | ||
| 229 | LOG_WARNING(Service_NFP, "(STUBBED) called, device_handle={}, data_size={}", device_handle, | ||
| 230 | data.size()); | ||
| 231 | |||
| 232 | // TODO(german77): Loop through all interfaces | ||
| 233 | if (device_handle == nfp_interface.GetHandle()) { | ||
| 234 | const auto result = nfp_interface.SetApplicationArea(data); | ||
| 208 | IPC::ResponseBuilder rb{ctx, 2}; | 235 | IPC::ResponseBuilder rb{ctx, 2}; |
| 209 | rb.Push(ResultSuccess); | 236 | rb.Push(result); |
| 237 | return; | ||
| 210 | } | 238 | } |
| 211 | 239 | ||
| 212 | void GetTagInfo(Kernel::HLERequestContext& ctx) { | 240 | LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); |
| 213 | LOG_DEBUG(Service_NFP, "called"); | ||
| 214 | 241 | ||
| 242 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 243 | rb.Push(ErrCodes::DeviceNotFound); | ||
| 244 | } | ||
| 245 | |||
| 246 | void IUser::CreateApplicationArea(Kernel::HLERequestContext& ctx) { | ||
| 247 | IPC::RequestParser rp{ctx}; | ||
| 248 | const auto device_handle{rp.Pop<u64>()}; | ||
| 249 | const auto access_id{rp.Pop<u32>()}; | ||
| 250 | const auto data{ctx.ReadBuffer()}; | ||
| 251 | LOG_WARNING(Service_NFP, "(STUBBED) called, device_handle={}, data_size={}, access_id={}", | ||
| 252 | device_handle, access_id, data.size()); | ||
| 253 | |||
| 254 | // TODO(german77): Loop through all interfaces | ||
| 255 | if (device_handle == nfp_interface.GetHandle()) { | ||
| 256 | const auto result = nfp_interface.CreateApplicationArea(access_id, data); | ||
| 215 | IPC::ResponseBuilder rb{ctx, 2}; | 257 | IPC::ResponseBuilder rb{ctx, 2}; |
| 216 | const auto& amiibo = nfp_interface.GetAmiiboBuffer(); | 258 | rb.Push(result); |
| 217 | const TagInfo tag_info{ | 259 | return; |
| 218 | .uuid = amiibo.uuid, | ||
| 219 | .uuid_length = static_cast<u8>(amiibo.uuid.size()), | ||
| 220 | .padding_1 = {}, | ||
| 221 | .protocol = 1, // TODO(ogniK): Figure out actual values | ||
| 222 | .tag_type = 2, | ||
| 223 | .padding_2 = {}, | ||
| 224 | }; | ||
| 225 | ctx.WriteBuffer(tag_info); | ||
| 226 | rb.Push(ResultSuccess); | ||
| 227 | } | 260 | } |
| 228 | 261 | ||
| 229 | void Mount(Kernel::HLERequestContext& ctx) { | 262 | LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); |
| 230 | LOG_DEBUG(Service_NFP, "called"); | 263 | |
| 264 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 265 | rb.Push(ErrCodes::DeviceNotFound); | ||
| 266 | } | ||
| 267 | |||
| 268 | void IUser::GetTagInfo(Kernel::HLERequestContext& ctx) { | ||
| 269 | IPC::RequestParser rp{ctx}; | ||
| 270 | const auto device_handle{rp.Pop<u64>()}; | ||
| 271 | LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); | ||
| 231 | 272 | ||
| 232 | device_state = DeviceState::TagNearby; | 273 | // TODO(german77): Loop through all interfaces |
| 274 | if (device_handle == nfp_interface.GetHandle()) { | ||
| 275 | TagInfo tag_info{}; | ||
| 276 | const auto result = nfp_interface.GetTagInfo(tag_info); | ||
| 277 | ctx.WriteBuffer(tag_info); | ||
| 233 | IPC::ResponseBuilder rb{ctx, 2}; | 278 | IPC::ResponseBuilder rb{ctx, 2}; |
| 234 | rb.Push(ResultSuccess); | 279 | rb.Push(result); |
| 280 | return; | ||
| 235 | } | 281 | } |
| 236 | 282 | ||
| 237 | void GetModelInfo(Kernel::HLERequestContext& ctx) { | 283 | LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); |
| 238 | LOG_DEBUG(Service_NFP, "called"); | ||
| 239 | 284 | ||
| 285 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 286 | rb.Push(ErrCodes::DeviceNotFound); | ||
| 287 | } | ||
| 288 | |||
| 289 | void IUser::GetRegisterInfo(Kernel::HLERequestContext& ctx) { | ||
| 290 | IPC::RequestParser rp{ctx}; | ||
| 291 | const auto device_handle{rp.Pop<u64>()}; | ||
| 292 | LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); | ||
| 293 | |||
| 294 | // TODO(german77): Loop through all interfaces | ||
| 295 | if (device_handle == nfp_interface.GetHandle()) { | ||
| 296 | RegisterInfo register_info{}; | ||
| 297 | const auto result = nfp_interface.GetRegisterInfo(register_info); | ||
| 298 | ctx.WriteBuffer(register_info); | ||
| 240 | IPC::ResponseBuilder rb{ctx, 2}; | 299 | IPC::ResponseBuilder rb{ctx, 2}; |
| 241 | const auto& amiibo = nfp_interface.GetAmiiboBuffer(); | 300 | rb.Push(result); |
| 242 | ctx.WriteBuffer(amiibo.model_info); | 301 | return; |
| 243 | rb.Push(ResultSuccess); | ||
| 244 | } | 302 | } |
| 245 | 303 | ||
| 246 | void Unmount(Kernel::HLERequestContext& ctx) { | 304 | LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); |
| 247 | LOG_DEBUG(Service_NFP, "called"); | ||
| 248 | 305 | ||
| 249 | device_state = DeviceState::TagFound; | 306 | IPC::ResponseBuilder rb{ctx, 2}; |
| 307 | rb.Push(ErrCodes::DeviceNotFound); | ||
| 308 | } | ||
| 250 | 309 | ||
| 310 | void IUser::GetCommonInfo(Kernel::HLERequestContext& ctx) { | ||
| 311 | IPC::RequestParser rp{ctx}; | ||
| 312 | const auto device_handle{rp.Pop<u64>()}; | ||
| 313 | LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); | ||
| 314 | |||
| 315 | // TODO(german77): Loop through all interfaces | ||
| 316 | if (device_handle == nfp_interface.GetHandle()) { | ||
| 317 | CommonInfo common_info{}; | ||
| 318 | const auto result = nfp_interface.GetCommonInfo(common_info); | ||
| 319 | ctx.WriteBuffer(common_info); | ||
| 251 | IPC::ResponseBuilder rb{ctx, 2}; | 320 | IPC::ResponseBuilder rb{ctx, 2}; |
| 252 | rb.Push(ResultSuccess); | 321 | rb.Push(result); |
| 322 | return; | ||
| 253 | } | 323 | } |
| 254 | 324 | ||
| 255 | void Finalize(Kernel::HLERequestContext& ctx) { | 325 | LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); |
| 256 | LOG_DEBUG(Service_NFP, "called"); | 326 | |
| 327 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 328 | rb.Push(ErrCodes::DeviceNotFound); | ||
| 329 | } | ||
| 257 | 330 | ||
| 258 | device_state = DeviceState::Finalized; | 331 | void IUser::GetModelInfo(Kernel::HLERequestContext& ctx) { |
| 332 | IPC::RequestParser rp{ctx}; | ||
| 333 | const auto device_handle{rp.Pop<u64>()}; | ||
| 334 | LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); | ||
| 259 | 335 | ||
| 336 | // TODO(german77): Loop through all interfaces | ||
| 337 | if (device_handle == nfp_interface.GetHandle()) { | ||
| 338 | ModelInfo model_info{}; | ||
| 339 | const auto result = nfp_interface.GetModelInfo(model_info); | ||
| 340 | ctx.WriteBuffer(model_info); | ||
| 260 | IPC::ResponseBuilder rb{ctx, 2}; | 341 | IPC::ResponseBuilder rb{ctx, 2}; |
| 261 | rb.Push(ResultSuccess); | 342 | rb.Push(result); |
| 343 | return; | ||
| 262 | } | 344 | } |
| 263 | 345 | ||
| 264 | void AttachAvailabilityChangeEvent(Kernel::HLERequestContext& ctx) { | 346 | LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); |
| 265 | LOG_WARNING(Service_NFP, "(STUBBED) called"); | 347 | |
| 348 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 349 | rb.Push(ErrCodes::DeviceNotFound); | ||
| 350 | } | ||
| 266 | 351 | ||
| 352 | void IUser::AttachActivateEvent(Kernel::HLERequestContext& ctx) { | ||
| 353 | IPC::RequestParser rp{ctx}; | ||
| 354 | const auto device_handle{rp.Pop<u64>()}; | ||
| 355 | LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle); | ||
| 356 | |||
| 357 | // TODO(german77): Loop through all interfaces | ||
| 358 | if (device_handle == nfp_interface.GetHandle()) { | ||
| 267 | IPC::ResponseBuilder rb{ctx, 2, 1}; | 359 | IPC::ResponseBuilder rb{ctx, 2, 1}; |
| 268 | rb.Push(ResultSuccess); | 360 | rb.Push(ResultSuccess); |
| 269 | rb.PushCopyObjects(availability_change_event->GetReadableEvent()); | 361 | rb.PushCopyObjects(nfp_interface.GetActivateEvent()); |
| 362 | return; | ||
| 270 | } | 363 | } |
| 271 | 364 | ||
| 272 | void GetRegisterInfo(Kernel::HLERequestContext& ctx) { | 365 | LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); |
| 273 | LOG_WARNING(Service_NFP, "(STUBBED) called"); | ||
| 274 | 366 | ||
| 275 | // TODO(ogniK): Pull Mii and owner data from amiibo | 367 | IPC::ResponseBuilder rb{ctx, 2}; |
| 368 | rb.Push(ErrCodes::DeviceNotFound); | ||
| 369 | } | ||
| 276 | 370 | ||
| 277 | IPC::ResponseBuilder rb{ctx, 2}; | 371 | void IUser::AttachDeactivateEvent(Kernel::HLERequestContext& ctx) { |
| 372 | IPC::RequestParser rp{ctx}; | ||
| 373 | const auto device_handle{rp.Pop<u64>()}; | ||
| 374 | LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle); | ||
| 375 | |||
| 376 | // TODO(german77): Loop through all interfaces | ||
| 377 | if (device_handle == nfp_interface.GetHandle()) { | ||
| 378 | IPC::ResponseBuilder rb{ctx, 2, 1}; | ||
| 278 | rb.Push(ResultSuccess); | 379 | rb.Push(ResultSuccess); |
| 380 | rb.PushCopyObjects(nfp_interface.GetDeactivateEvent()); | ||
| 381 | return; | ||
| 279 | } | 382 | } |
| 280 | 383 | ||
| 281 | void GetCommonInfo(Kernel::HLERequestContext& ctx) { | 384 | LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); |
| 282 | LOG_WARNING(Service_NFP, "(STUBBED) called"); | ||
| 283 | 385 | ||
| 284 | // TODO(ogniK): Pull common information from amiibo | 386 | IPC::ResponseBuilder rb{ctx, 2}; |
| 387 | rb.Push(ErrCodes::DeviceNotFound); | ||
| 388 | } | ||
| 285 | 389 | ||
| 286 | CommonInfo common_info{}; | 390 | void IUser::GetState(Kernel::HLERequestContext& ctx) { |
| 287 | common_info.application_area_size = 0; | 391 | LOG_DEBUG(Service_NFC, "called"); |
| 288 | ctx.WriteBuffer(common_info); | ||
| 289 | 392 | ||
| 290 | IPC::ResponseBuilder rb{ctx, 2}; | 393 | IPC::ResponseBuilder rb{ctx, 3, 0}; |
| 394 | rb.Push(ResultSuccess); | ||
| 395 | rb.PushEnum(state); | ||
| 396 | } | ||
| 397 | |||
| 398 | void IUser::GetDeviceState(Kernel::HLERequestContext& ctx) { | ||
| 399 | IPC::RequestParser rp{ctx}; | ||
| 400 | const auto device_handle{rp.Pop<u64>()}; | ||
| 401 | LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle); | ||
| 402 | |||
| 403 | // TODO(german77): Loop through all interfaces | ||
| 404 | if (device_handle == nfp_interface.GetHandle()) { | ||
| 405 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 291 | rb.Push(ResultSuccess); | 406 | rb.Push(ResultSuccess); |
| 407 | rb.PushEnum(nfp_interface.GetCurrentState()); | ||
| 408 | return; | ||
| 292 | } | 409 | } |
| 293 | 410 | ||
| 294 | void OpenApplicationArea(Kernel::HLERequestContext& ctx) { | 411 | LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); |
| 295 | LOG_WARNING(Service_NFP, "(STUBBED) called"); | ||
| 296 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 297 | rb.Push(ErrCodes::ERR_NO_APPLICATION_AREA); | ||
| 298 | } | ||
| 299 | 412 | ||
| 300 | void GetApplicationAreaSize(Kernel::HLERequestContext& ctx) { | 413 | IPC::ResponseBuilder rb{ctx, 2}; |
| 301 | LOG_WARNING(Service_NFP, "(STUBBED) called"); | 414 | rb.Push(ErrCodes::DeviceNotFound); |
| 302 | // We don't need to worry about this since we can just open the file | 415 | } |
| 416 | |||
| 417 | void IUser::GetNpadId(Kernel::HLERequestContext& ctx) { | ||
| 418 | IPC::RequestParser rp{ctx}; | ||
| 419 | const auto device_handle{rp.Pop<u64>()}; | ||
| 420 | LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle); | ||
| 421 | |||
| 422 | // TODO(german77): Loop through all interfaces | ||
| 423 | if (device_handle == nfp_interface.GetHandle()) { | ||
| 303 | IPC::ResponseBuilder rb{ctx, 3}; | 424 | IPC::ResponseBuilder rb{ctx, 3}; |
| 304 | rb.Push(ResultSuccess); | 425 | rb.Push(ResultSuccess); |
| 305 | rb.PushRaw<u32>(0); // This is from the GetCommonInfo stub | 426 | rb.PushEnum(nfp_interface.GetNpadId()); |
| 427 | return; | ||
| 306 | } | 428 | } |
| 307 | 429 | ||
| 308 | void GetApplicationArea(Kernel::HLERequestContext& ctx) { | 430 | LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); |
| 309 | LOG_WARNING(Service_NFP, "(STUBBED) called"); | 431 | |
| 432 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 433 | rb.Push(ErrCodes::DeviceNotFound); | ||
| 434 | } | ||
| 310 | 435 | ||
| 311 | // TODO(ogniK): Pull application area from amiibo | 436 | void IUser::GetApplicationAreaSize(Kernel::HLERequestContext& ctx) { |
| 437 | IPC::RequestParser rp{ctx}; | ||
| 438 | const auto device_handle{rp.Pop<u64>()}; | ||
| 439 | LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle); | ||
| 312 | 440 | ||
| 441 | // TODO(german77): Loop through all interfaces | ||
| 442 | if (device_handle == nfp_interface.GetHandle()) { | ||
| 313 | IPC::ResponseBuilder rb{ctx, 3}; | 443 | IPC::ResponseBuilder rb{ctx, 3}; |
| 314 | rb.Push(ResultSuccess); | 444 | rb.Push(ResultSuccess); |
| 315 | rb.PushRaw<u32>(0); // This is from the GetCommonInfo stub | 445 | rb.Push(ApplicationAreaSize); |
| 446 | return; | ||
| 316 | } | 447 | } |
| 317 | 448 | ||
| 318 | Module::Interface& nfp_interface; | 449 | LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); |
| 319 | KernelHelpers::ServiceContext& service_context; | 450 | |
| 451 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 452 | rb.Push(ErrCodes::DeviceNotFound); | ||
| 453 | } | ||
| 454 | |||
| 455 | void IUser::AttachAvailabilityChangeEvent(Kernel::HLERequestContext& ctx) { | ||
| 456 | LOG_DEBUG(Service_NFP, "(STUBBED) called"); | ||
| 320 | 457 | ||
| 321 | bool has_attached_handle{}; | 458 | IPC::ResponseBuilder rb{ctx, 2, 1}; |
| 322 | const u64 device_handle{0}; // Npad device 1 | 459 | rb.Push(ResultSuccess); |
| 323 | const u32 npad_id{0}; // Player 1 controller | 460 | rb.PushCopyObjects(availability_change_event->GetReadableEvent()); |
| 324 | State state{State::NonInitialized}; | 461 | } |
| 325 | DeviceState device_state{DeviceState::Initialized}; | 462 | |
| 326 | Kernel::KEvent* deactivate_event; | 463 | Module::Interface::Interface(std::shared_ptr<Module> module_, Core::System& system_, |
| 327 | Kernel::KEvent* availability_change_event; | 464 | const char* name) |
| 328 | }; | 465 | : ServiceFramework{system_, name}, module{std::move(module_)}, |
| 466 | npad_id{Core::HID::NpadIdType::Player1}, service_context{system_, service_name} { | ||
| 467 | activate_event = service_context.CreateEvent("IUser:NFPActivateEvent"); | ||
| 468 | deactivate_event = service_context.CreateEvent("IUser:NFPDeactivateEvent"); | ||
| 469 | } | ||
| 470 | |||
| 471 | Module::Interface::~Interface() = default; | ||
| 329 | 472 | ||
| 330 | void Module::Interface::CreateUserInterface(Kernel::HLERequestContext& ctx) { | 473 | void Module::Interface::CreateUserInterface(Kernel::HLERequestContext& ctx) { |
| 331 | LOG_DEBUG(Service_NFP, "called"); | 474 | LOG_DEBUG(Service_NFP, "called"); |
| 332 | 475 | ||
| 333 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | 476 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; |
| 334 | rb.Push(ResultSuccess); | 477 | rb.Push(ResultSuccess); |
| 335 | rb.PushIpcInterface<IUser>(*this, system, service_context); | 478 | rb.PushIpcInterface<IUser>(*this, system); |
| 336 | } | 479 | } |
| 337 | 480 | ||
| 338 | bool Module::Interface::LoadAmiibo(const std::vector<u8>& buffer) { | 481 | bool Module::Interface::LoadAmiibo(const std::vector<u8>& buffer) { |
| 339 | if (buffer.size() < sizeof(AmiiboFile)) { | 482 | if (device_state != DeviceState::SearchingForTag) { |
| 483 | LOG_ERROR(Service_NFP, "Game is not looking for amiibos, current state {}", device_state); | ||
| 484 | return false; | ||
| 485 | } | ||
| 486 | |||
| 487 | constexpr auto tag_size = sizeof(NTAG215File); | ||
| 488 | constexpr auto tag_size_without_password = sizeof(NTAG215File) - sizeof(NTAG215Password); | ||
| 489 | |||
| 490 | std::vector<u8> amiibo_buffer = buffer; | ||
| 491 | |||
| 492 | if (amiibo_buffer.size() < tag_size_without_password) { | ||
| 493 | LOG_ERROR(Service_NFP, "Wrong file size {}", buffer.size()); | ||
| 494 | return false; | ||
| 495 | } | ||
| 496 | |||
| 497 | // Ensure it has the correct size | ||
| 498 | if (amiibo_buffer.size() != tag_size) { | ||
| 499 | amiibo_buffer.resize(tag_size, 0); | ||
| 500 | } | ||
| 501 | |||
| 502 | LOG_INFO(Service_NFP, "Amiibo detected"); | ||
| 503 | std::memcpy(&tag_data, buffer.data(), tag_size); | ||
| 504 | |||
| 505 | if (!IsAmiiboValid()) { | ||
| 340 | return false; | 506 | return false; |
| 341 | } | 507 | } |
| 342 | 508 | ||
| 343 | std::memcpy(&amiibo, buffer.data(), sizeof(amiibo)); | 509 | // This value can't be dumped from a tag. Generate it |
| 344 | nfc_tag_load->GetWritableEvent().Signal(); | 510 | tag_data.password.PWD = GetTagPassword(tag_data.uuid); |
| 511 | |||
| 512 | device_state = DeviceState::TagFound; | ||
| 513 | activate_event->GetWritableEvent().Signal(); | ||
| 345 | return true; | 514 | return true; |
| 346 | } | 515 | } |
| 347 | 516 | ||
| 348 | Kernel::KReadableEvent& Module::Interface::GetNFCEvent() { | 517 | void Module::Interface::CloseAmiibo() { |
| 349 | return nfc_tag_load->GetReadableEvent(); | 518 | LOG_INFO(Service_NFP, "Remove amiibo"); |
| 519 | device_state = DeviceState::TagRemoved; | ||
| 520 | is_application_area_initialized = false; | ||
| 521 | application_area_id = 0; | ||
| 522 | application_area_data.clear(); | ||
| 523 | deactivate_event->GetWritableEvent().Signal(); | ||
| 524 | } | ||
| 525 | |||
| 526 | bool Module::Interface::IsAmiiboValid() const { | ||
| 527 | const auto& amiibo_data = tag_data.user_memory; | ||
| 528 | LOG_DEBUG(Service_NFP, "uuid_lock=0x{0:x}", tag_data.lock_bytes); | ||
| 529 | LOG_DEBUG(Service_NFP, "compability_container=0x{0:x}", tag_data.compability_container); | ||
| 530 | LOG_DEBUG(Service_NFP, "crypto_init=0x{0:x}", amiibo_data.crypto_init); | ||
| 531 | LOG_DEBUG(Service_NFP, "write_count={}", amiibo_data.write_count); | ||
| 532 | |||
| 533 | LOG_DEBUG(Service_NFP, "character_id=0x{0:x}", amiibo_data.model_info.character_id); | ||
| 534 | LOG_DEBUG(Service_NFP, "character_variant={}", amiibo_data.model_info.character_variant); | ||
| 535 | LOG_DEBUG(Service_NFP, "amiibo_type={}", amiibo_data.model_info.amiibo_type); | ||
| 536 | LOG_DEBUG(Service_NFP, "model_number=0x{0:x}", amiibo_data.model_info.model_number); | ||
| 537 | LOG_DEBUG(Service_NFP, "series={}", amiibo_data.model_info.series); | ||
| 538 | LOG_DEBUG(Service_NFP, "fixed_value=0x{0:x}", amiibo_data.model_info.fixed); | ||
| 539 | |||
| 540 | LOG_DEBUG(Service_NFP, "tag_dynamic_lock=0x{0:x}", tag_data.dynamic_lock); | ||
| 541 | LOG_DEBUG(Service_NFP, "tag_CFG0=0x{0:x}", tag_data.CFG0); | ||
| 542 | LOG_DEBUG(Service_NFP, "tag_CFG1=0x{0:x}", tag_data.CFG1); | ||
| 543 | |||
| 544 | // Check against all know constants on an amiibo binary | ||
| 545 | if (tag_data.lock_bytes != 0xE00F) { | ||
| 546 | return false; | ||
| 547 | } | ||
| 548 | if (tag_data.compability_container != 0xEEFF10F1U) { | ||
| 549 | return false; | ||
| 550 | } | ||
| 551 | if ((amiibo_data.crypto_init & 0xFF) != 0xA5) { | ||
| 552 | return false; | ||
| 553 | } | ||
| 554 | if (amiibo_data.model_info.fixed != 0x02) { | ||
| 555 | return false; | ||
| 556 | } | ||
| 557 | if ((tag_data.dynamic_lock & 0xFFFFFF) != 0x0F0001) { | ||
| 558 | return false; | ||
| 559 | } | ||
| 560 | if (tag_data.CFG0 != 0x04000000U) { | ||
| 561 | return false; | ||
| 562 | } | ||
| 563 | if (tag_data.CFG1 != 0x5F) { | ||
| 564 | return false; | ||
| 565 | } | ||
| 566 | return true; | ||
| 567 | } | ||
| 568 | |||
| 569 | Kernel::KReadableEvent& Module::Interface::GetActivateEvent() const { | ||
| 570 | return activate_event->GetReadableEvent(); | ||
| 571 | } | ||
| 572 | |||
| 573 | Kernel::KReadableEvent& Module::Interface::GetDeactivateEvent() const { | ||
| 574 | return deactivate_event->GetReadableEvent(); | ||
| 575 | } | ||
| 576 | |||
| 577 | void Module::Interface::Initialize() { | ||
| 578 | device_state = DeviceState::Initialized; | ||
| 579 | } | ||
| 580 | |||
| 581 | void Module::Interface::Finalize() { | ||
| 582 | device_state = DeviceState::Unaviable; | ||
| 583 | is_application_area_initialized = false; | ||
| 584 | application_area_id = 0; | ||
| 585 | application_area_data.clear(); | ||
| 586 | } | ||
| 587 | |||
| 588 | ResultCode Module::Interface::StartDetection(s32 protocol_) { | ||
| 589 | auto npad_device = system.HIDCore().GetEmulatedController(npad_id); | ||
| 590 | |||
| 591 | // TODO(german77): Add callback for when nfc data is available | ||
| 592 | |||
| 593 | if (device_state == DeviceState::Initialized || device_state == DeviceState::TagRemoved) { | ||
| 594 | npad_device->SetPollingMode(Common::Input::PollingMode::NFC); | ||
| 595 | device_state = DeviceState::SearchingForTag; | ||
| 596 | protocol = protocol_; | ||
| 597 | return ResultSuccess; | ||
| 598 | } | ||
| 599 | |||
| 600 | LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); | ||
| 601 | return ErrCodes::WrongDeviceState; | ||
| 602 | } | ||
| 603 | |||
| 604 | ResultCode Module::Interface::StopDetection() { | ||
| 605 | auto npad_device = system.HIDCore().GetEmulatedController(npad_id); | ||
| 606 | npad_device->SetPollingMode(Common::Input::PollingMode::Active); | ||
| 607 | |||
| 608 | if (device_state == DeviceState::TagFound || device_state == DeviceState::TagMounted) { | ||
| 609 | CloseAmiibo(); | ||
| 610 | return ResultSuccess; | ||
| 611 | } | ||
| 612 | if (device_state == DeviceState::SearchingForTag || device_state == DeviceState::TagRemoved) { | ||
| 613 | device_state = DeviceState::Initialized; | ||
| 614 | return ResultSuccess; | ||
| 615 | } | ||
| 616 | |||
| 617 | LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); | ||
| 618 | return ErrCodes::WrongDeviceState; | ||
| 619 | } | ||
| 620 | |||
| 621 | ResultCode Module::Interface::Mount() { | ||
| 622 | if (device_state == DeviceState::TagFound) { | ||
| 623 | device_state = DeviceState::TagMounted; | ||
| 624 | return ResultSuccess; | ||
| 625 | } | ||
| 626 | |||
| 627 | LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); | ||
| 628 | return ErrCodes::WrongDeviceState; | ||
| 629 | } | ||
| 630 | |||
| 631 | ResultCode Module::Interface::Unmount() { | ||
| 632 | if (device_state == DeviceState::TagMounted) { | ||
| 633 | is_application_area_initialized = false; | ||
| 634 | application_area_id = 0; | ||
| 635 | application_area_data.clear(); | ||
| 636 | device_state = DeviceState::TagFound; | ||
| 637 | return ResultSuccess; | ||
| 638 | } | ||
| 639 | |||
| 640 | LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); | ||
| 641 | return ErrCodes::WrongDeviceState; | ||
| 642 | } | ||
| 643 | |||
| 644 | ResultCode Module::Interface::GetTagInfo(TagInfo& tag_info) const { | ||
| 645 | if (device_state == DeviceState::TagFound || device_state == DeviceState::TagMounted) { | ||
| 646 | tag_info = { | ||
| 647 | .uuid = tag_data.uuid, | ||
| 648 | .uuid_length = static_cast<u8>(tag_data.uuid.size()), | ||
| 649 | .protocol = protocol, | ||
| 650 | .tag_type = static_cast<u32>(tag_data.user_memory.model_info.amiibo_type), | ||
| 651 | }; | ||
| 652 | return ResultSuccess; | ||
| 653 | } | ||
| 654 | |||
| 655 | LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); | ||
| 656 | return ErrCodes::WrongDeviceState; | ||
| 657 | } | ||
| 658 | |||
| 659 | ResultCode Module::Interface::GetCommonInfo(CommonInfo& common_info) const { | ||
| 660 | if (device_state != DeviceState::TagMounted) { | ||
| 661 | LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); | ||
| 662 | return ErrCodes::WrongDeviceState; | ||
| 663 | } | ||
| 664 | |||
| 665 | // Read this data from the amiibo save file | ||
| 666 | common_info = { | ||
| 667 | .last_write_year = 2022, | ||
| 668 | .last_write_month = 2, | ||
| 669 | .last_write_day = 7, | ||
| 670 | .write_counter = tag_data.user_memory.write_count, | ||
| 671 | .version = 1, | ||
| 672 | .application_area_size = ApplicationAreaSize, | ||
| 673 | }; | ||
| 674 | return ResultSuccess; | ||
| 675 | } | ||
| 676 | |||
| 677 | ResultCode Module::Interface::GetModelInfo(ModelInfo& model_info) const { | ||
| 678 | if (device_state != DeviceState::TagMounted) { | ||
| 679 | LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); | ||
| 680 | return ErrCodes::WrongDeviceState; | ||
| 681 | } | ||
| 682 | |||
| 683 | model_info = tag_data.user_memory.model_info; | ||
| 684 | return ResultSuccess; | ||
| 685 | } | ||
| 686 | |||
| 687 | ResultCode Module::Interface::GetRegisterInfo(RegisterInfo& register_info) const { | ||
| 688 | if (device_state != DeviceState::TagMounted) { | ||
| 689 | LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); | ||
| 690 | return ErrCodes::WrongDeviceState; | ||
| 691 | } | ||
| 692 | |||
| 693 | Service::Mii::MiiManager manager; | ||
| 694 | |||
| 695 | // Read this data from the amiibo save file | ||
| 696 | register_info = { | ||
| 697 | .mii_char_info = manager.BuildDefault(0), | ||
| 698 | .first_write_year = 2022, | ||
| 699 | .first_write_month = 2, | ||
| 700 | .first_write_day = 7, | ||
| 701 | .amiibo_name = {'Y', 'u', 'z', 'u', 'A', 'm', 'i', 'i', 'b', 'o', 0}, | ||
| 702 | .unknown = {}, | ||
| 703 | }; | ||
| 704 | return ResultSuccess; | ||
| 705 | } | ||
| 706 | |||
| 707 | ResultCode Module::Interface::OpenApplicationArea(u32 access_id) { | ||
| 708 | if (device_state != DeviceState::TagMounted) { | ||
| 709 | LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); | ||
| 710 | return ErrCodes::WrongDeviceState; | ||
| 711 | } | ||
| 712 | if (AmiiboApplicationDataExist(access_id)) { | ||
| 713 | application_area_data = LoadAmiiboApplicationData(access_id); | ||
| 714 | application_area_id = access_id; | ||
| 715 | is_application_area_initialized = true; | ||
| 716 | } | ||
| 717 | if (!is_application_area_initialized) { | ||
| 718 | LOG_WARNING(Service_NFP, "Application area is not initialized"); | ||
| 719 | return ErrCodes::ApplicationAreaIsNotInitialized; | ||
| 720 | } | ||
| 721 | return ResultSuccess; | ||
| 722 | } | ||
| 723 | |||
| 724 | ResultCode Module::Interface::GetApplicationArea(std::vector<u8>& data) const { | ||
| 725 | if (device_state != DeviceState::TagMounted) { | ||
| 726 | LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); | ||
| 727 | return ErrCodes::WrongDeviceState; | ||
| 728 | } | ||
| 729 | if (!is_application_area_initialized) { | ||
| 730 | LOG_ERROR(Service_NFP, "Application area is not initialized"); | ||
| 731 | return ErrCodes::ApplicationAreaIsNotInitialized; | ||
| 732 | } | ||
| 733 | |||
| 734 | data = application_area_data; | ||
| 735 | |||
| 736 | return ResultSuccess; | ||
| 737 | } | ||
| 738 | |||
| 739 | ResultCode Module::Interface::SetApplicationArea(const std::vector<u8>& data) { | ||
| 740 | if (device_state != DeviceState::TagMounted) { | ||
| 741 | LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); | ||
| 742 | return ErrCodes::WrongDeviceState; | ||
| 743 | } | ||
| 744 | if (!is_application_area_initialized) { | ||
| 745 | LOG_ERROR(Service_NFP, "Application area is not initialized"); | ||
| 746 | return ErrCodes::ApplicationAreaIsNotInitialized; | ||
| 747 | } | ||
| 748 | application_area_data = data; | ||
| 749 | SaveAmiiboApplicationData(application_area_id, application_area_data); | ||
| 750 | return ResultSuccess; | ||
| 751 | } | ||
| 752 | |||
| 753 | ResultCode Module::Interface::CreateApplicationArea(u32 access_id, const std::vector<u8>& data) { | ||
| 754 | if (device_state != DeviceState::TagMounted) { | ||
| 755 | LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); | ||
| 756 | return ErrCodes::WrongDeviceState; | ||
| 757 | } | ||
| 758 | if (AmiiboApplicationDataExist(access_id)) { | ||
| 759 | LOG_ERROR(Service_NFP, "Application area already exist"); | ||
| 760 | return ErrCodes::ApplicationAreaExist; | ||
| 761 | } | ||
| 762 | application_area_data = data; | ||
| 763 | application_area_id = access_id; | ||
| 764 | SaveAmiiboApplicationData(application_area_id, application_area_data); | ||
| 765 | return ResultSuccess; | ||
| 766 | } | ||
| 767 | |||
| 768 | bool Module::Interface::AmiiboApplicationDataExist(u32 access_id) const { | ||
| 769 | // TODO(german77): Check if file exist | ||
| 770 | return false; | ||
| 771 | } | ||
| 772 | |||
| 773 | std::vector<u8> Module::Interface::LoadAmiiboApplicationData(u32 access_id) const { | ||
| 774 | // TODO(german77): Read file | ||
| 775 | std::vector<u8> data(ApplicationAreaSize); | ||
| 776 | return data; | ||
| 777 | } | ||
| 778 | |||
| 779 | void Module::Interface::SaveAmiiboApplicationData(u32 access_id, | ||
| 780 | const std::vector<u8>& data) const { | ||
| 781 | // TODO(german77): Save file | ||
| 782 | } | ||
| 783 | |||
| 784 | u64 Module::Interface::GetHandle() const { | ||
| 785 | // Generate a handle based of the npad id | ||
| 786 | return static_cast<u64>(npad_id); | ||
| 787 | } | ||
| 788 | |||
| 789 | DeviceState Module::Interface::GetCurrentState() const { | ||
| 790 | return device_state; | ||
| 791 | } | ||
| 792 | |||
| 793 | Core::HID::NpadIdType Module::Interface::GetNpadId() const { | ||
| 794 | return npad_id; | ||
| 350 | } | 795 | } |
| 351 | 796 | ||
| 352 | const Module::Interface::AmiiboFile& Module::Interface::GetAmiiboBuffer() const { | 797 | u32 Module::Interface::GetTagPassword(const TagUuid& uuid) const { |
| 353 | return amiibo; | 798 | // Verifiy that the generated password is correct |
| 799 | u32 password = 0xAA ^ (uuid[1] ^ uuid[3]); | ||
| 800 | password &= (0x55 ^ (uuid[2] ^ uuid[4])) << 8; | ||
| 801 | password &= (0xAA ^ (uuid[3] ^ uuid[5])) << 16; | ||
| 802 | password &= (0x55 ^ (uuid[4] ^ uuid[6])) << 24; | ||
| 803 | return password; | ||
| 354 | } | 804 | } |
| 355 | 805 | ||
| 356 | void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) { | 806 | void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) { |
diff --git a/src/core/hle/service/nfp/nfp.h b/src/core/hle/service/nfp/nfp.h index 95c127efb..022f13b29 100644 --- a/src/core/hle/service/nfp/nfp.h +++ b/src/core/hle/service/nfp/nfp.h | |||
| @@ -7,15 +7,132 @@ | |||
| 7 | #include <array> | 7 | #include <array> |
| 8 | #include <vector> | 8 | #include <vector> |
| 9 | 9 | ||
| 10 | #include "common/common_funcs.h" | ||
| 10 | #include "core/hle/service/kernel_helpers.h" | 11 | #include "core/hle/service/kernel_helpers.h" |
| 12 | #include "core/hle/service/mii/mii_manager.h" | ||
| 11 | #include "core/hle/service/service.h" | 13 | #include "core/hle/service/service.h" |
| 12 | 14 | ||
| 13 | namespace Kernel { | 15 | namespace Kernel { |
| 14 | class KEvent; | 16 | class KEvent; |
| 15 | } | 17 | class KReadableEvent; |
| 18 | } // namespace Kernel | ||
| 19 | |||
| 20 | namespace Core::HID { | ||
| 21 | enum class NpadIdType : u32; | ||
| 22 | } // namespace Core::HID | ||
| 16 | 23 | ||
| 17 | namespace Service::NFP { | 24 | namespace Service::NFP { |
| 18 | 25 | ||
| 26 | enum class ServiceType : u32 { | ||
| 27 | User, | ||
| 28 | Debug, | ||
| 29 | System, | ||
| 30 | }; | ||
| 31 | |||
| 32 | enum class State : u32 { | ||
| 33 | NonInitialized, | ||
| 34 | Initialized, | ||
| 35 | }; | ||
| 36 | |||
| 37 | enum class DeviceState : u32 { | ||
| 38 | Initialized, | ||
| 39 | SearchingForTag, | ||
| 40 | TagFound, | ||
| 41 | TagRemoved, | ||
| 42 | TagMounted, | ||
| 43 | Unaviable, | ||
| 44 | Finalized, | ||
| 45 | }; | ||
| 46 | |||
| 47 | enum class ModelType : u32 { | ||
| 48 | Amiibo, | ||
| 49 | }; | ||
| 50 | |||
| 51 | enum class MountTarget : u32 { | ||
| 52 | Rom, | ||
| 53 | Ram, | ||
| 54 | All, | ||
| 55 | }; | ||
| 56 | |||
| 57 | enum class AmiiboType : u8 { | ||
| 58 | Figure, | ||
| 59 | Card, | ||
| 60 | Yarn, | ||
| 61 | }; | ||
| 62 | |||
| 63 | enum class AmiiboSeries : u8 { | ||
| 64 | SuperSmashBros, | ||
| 65 | SuperMario, | ||
| 66 | ChibiRobo, | ||
| 67 | YoshiWoollyWorld, | ||
| 68 | Splatoon, | ||
| 69 | AnimalCrossing, | ||
| 70 | EightBitMario, | ||
| 71 | Skylanders, | ||
| 72 | Unknown8, | ||
| 73 | TheLegendOfZelda, | ||
| 74 | ShovelKnight, | ||
| 75 | Unknown11, | ||
| 76 | Kiby, | ||
| 77 | Pokemon, | ||
| 78 | MarioSportsSuperstars, | ||
| 79 | MonsterHunter, | ||
| 80 | BoxBoy, | ||
| 81 | Pikmin, | ||
| 82 | FireEmblem, | ||
| 83 | Metroid, | ||
| 84 | Others, | ||
| 85 | MegaMan, | ||
| 86 | Diablo | ||
| 87 | }; | ||
| 88 | |||
| 89 | using TagUuid = std::array<u8, 10>; | ||
| 90 | |||
| 91 | struct TagInfo { | ||
| 92 | TagUuid uuid; | ||
| 93 | u8 uuid_length; | ||
| 94 | INSERT_PADDING_BYTES(0x15); | ||
| 95 | s32 protocol; | ||
| 96 | u32 tag_type; | ||
| 97 | INSERT_PADDING_BYTES(0x30); | ||
| 98 | }; | ||
| 99 | static_assert(sizeof(TagInfo) == 0x58, "TagInfo is an invalid size"); | ||
| 100 | |||
| 101 | struct CommonInfo { | ||
| 102 | u16 last_write_year; | ||
| 103 | u8 last_write_month; | ||
| 104 | u8 last_write_day; | ||
| 105 | u16 write_counter; | ||
| 106 | u16 version; | ||
| 107 | u32 application_area_size; | ||
| 108 | INSERT_PADDING_BYTES(0x34); | ||
| 109 | }; | ||
| 110 | static_assert(sizeof(CommonInfo) == 0x40, "CommonInfo is an invalid size"); | ||
| 111 | |||
| 112 | struct ModelInfo { | ||
| 113 | u16 character_id; | ||
| 114 | u8 character_variant; | ||
| 115 | AmiiboType amiibo_type; | ||
| 116 | u16 model_number; | ||
| 117 | AmiiboSeries series; | ||
| 118 | u8 fixed; // Must be 02 | ||
| 119 | INSERT_PADDING_BYTES(0x4); // Unknown | ||
| 120 | INSERT_PADDING_BYTES(0x20); // Probably a SHA256-(HMAC?) hash | ||
| 121 | INSERT_PADDING_BYTES(0x14); // SHA256-HMAC | ||
| 122 | }; | ||
| 123 | static_assert(sizeof(ModelInfo) == 0x40, "ModelInfo is an invalid size"); | ||
| 124 | |||
| 125 | struct RegisterInfo { | ||
| 126 | Service::Mii::MiiInfo mii_char_info; | ||
| 127 | u16 first_write_year; | ||
| 128 | u8 first_write_month; | ||
| 129 | u8 first_write_day; | ||
| 130 | std::array<u8, 11> amiibo_name; | ||
| 131 | u8 unknown; | ||
| 132 | INSERT_PADDING_BYTES(0x98); | ||
| 133 | }; | ||
| 134 | static_assert(sizeof(RegisterInfo) == 0x100, "RegisterInfo is an invalid size"); | ||
| 135 | |||
| 19 | class Module final { | 136 | class Module final { |
| 20 | public: | 137 | public: |
| 21 | class Interface : public ServiceFramework<Interface> { | 138 | class Interface : public ServiceFramework<Interface> { |
| @@ -24,34 +141,131 @@ public: | |||
| 24 | const char* name); | 141 | const char* name); |
| 25 | ~Interface() override; | 142 | ~Interface() override; |
| 26 | 143 | ||
| 27 | struct ModelInfo { | 144 | struct EncryptedAmiiboFile { |
| 28 | std::array<u8, 0x8> amiibo_identification_block; | 145 | u16 crypto_init; // Must be A5 XX |
| 29 | INSERT_PADDING_BYTES(0x38); | 146 | u16 write_count; // Number of times the amiibo has been written? |
| 147 | INSERT_PADDING_BYTES(0x20); // System crypts | ||
| 148 | INSERT_PADDING_BYTES(0x20); // SHA256-(HMAC?) hash | ||
| 149 | ModelInfo model_info; // This struct is bigger than documentation | ||
| 150 | INSERT_PADDING_BYTES(0xC); // SHA256-HMAC | ||
| 151 | INSERT_PADDING_BYTES(0x114); // section 1 encrypted buffer | ||
| 152 | INSERT_PADDING_BYTES(0x54); // section 2 encrypted buffer | ||
| 153 | }; | ||
| 154 | static_assert(sizeof(EncryptedAmiiboFile) == 0x1F8, "AmiiboFile is an invalid size"); | ||
| 155 | |||
| 156 | struct NTAG215Password { | ||
| 157 | u32 PWD; // Password to allow write access | ||
| 158 | u16 PACK; // Password acknowledge reply | ||
| 159 | u16 RFUI; // Reserved for future use | ||
| 30 | }; | 160 | }; |
| 31 | static_assert(sizeof(ModelInfo) == 0x40, "ModelInfo is an invalid size"); | 161 | static_assert(sizeof(NTAG215Password) == 0x8, "NTAG215Password is an invalid size"); |
| 32 | 162 | ||
| 33 | struct AmiiboFile { | 163 | struct NTAG215File { |
| 34 | std::array<u8, 10> uuid; | 164 | TagUuid uuid; // Unique serial number |
| 35 | INSERT_PADDING_BYTES(0x4a); | 165 | u16 lock_bytes; // Set defined pages as read only |
| 36 | ModelInfo model_info; | 166 | u32 compability_container; // Defines available memory |
| 167 | EncryptedAmiiboFile user_memory; // Writable data | ||
| 168 | u32 dynamic_lock; // Dynamic lock | ||
| 169 | u32 CFG0; // Defines memory protected by password | ||
| 170 | u32 CFG1; // Defines number of verification attempts | ||
| 171 | NTAG215Password password; // Password data | ||
| 37 | }; | 172 | }; |
| 38 | static_assert(sizeof(AmiiboFile) == 0x94, "AmiiboFile is an invalid size"); | 173 | static_assert(sizeof(NTAG215File) == 0x21C, "NTAG215File is an invalid size"); |
| 39 | 174 | ||
| 40 | void CreateUserInterface(Kernel::HLERequestContext& ctx); | 175 | void CreateUserInterface(Kernel::HLERequestContext& ctx); |
| 41 | bool LoadAmiibo(const std::vector<u8>& buffer); | 176 | bool LoadAmiibo(const std::vector<u8>& buffer); |
| 42 | Kernel::KReadableEvent& GetNFCEvent(); | 177 | void CloseAmiibo(); |
| 43 | const AmiiboFile& GetAmiiboBuffer() const; | 178 | |
| 179 | void Initialize(); | ||
| 180 | void Finalize(); | ||
| 181 | |||
| 182 | ResultCode StartDetection(s32 protocol_); | ||
| 183 | ResultCode StopDetection(); | ||
| 184 | ResultCode Mount(); | ||
| 185 | ResultCode Unmount(); | ||
| 186 | |||
| 187 | ResultCode GetTagInfo(TagInfo& tag_info) const; | ||
| 188 | ResultCode GetCommonInfo(CommonInfo& common_info) const; | ||
| 189 | ResultCode GetModelInfo(ModelInfo& model_info) const; | ||
| 190 | ResultCode GetRegisterInfo(RegisterInfo& register_info) const; | ||
| 191 | |||
| 192 | ResultCode OpenApplicationArea(u32 access_id); | ||
| 193 | ResultCode GetApplicationArea(std::vector<u8>& data) const; | ||
| 194 | ResultCode SetApplicationArea(const std::vector<u8>& data); | ||
| 195 | ResultCode CreateApplicationArea(u32 access_id, const std::vector<u8>& data); | ||
| 196 | |||
| 197 | u64 GetHandle() const; | ||
| 198 | DeviceState GetCurrentState() const; | ||
| 199 | Core::HID::NpadIdType GetNpadId() const; | ||
| 200 | |||
| 201 | Kernel::KReadableEvent& GetActivateEvent() const; | ||
| 202 | Kernel::KReadableEvent& GetDeactivateEvent() const; | ||
| 44 | 203 | ||
| 45 | protected: | 204 | protected: |
| 46 | std::shared_ptr<Module> module; | 205 | std::shared_ptr<Module> module; |
| 47 | 206 | ||
| 48 | private: | 207 | private: |
| 208 | /// Validates that the amiibo file is not corrupted | ||
| 209 | bool IsAmiiboValid() const; | ||
| 210 | |||
| 211 | bool AmiiboApplicationDataExist(u32 access_id) const; | ||
| 212 | std::vector<u8> LoadAmiiboApplicationData(u32 access_id) const; | ||
| 213 | void SaveAmiiboApplicationData(u32 access_id, const std::vector<u8>& data) const; | ||
| 214 | |||
| 215 | /// return password needed to allow write access to protected memory | ||
| 216 | u32 GetTagPassword(const TagUuid& uuid) const; | ||
| 217 | |||
| 218 | const Core::HID::NpadIdType npad_id; | ||
| 219 | |||
| 220 | DeviceState device_state{DeviceState::Unaviable}; | ||
| 49 | KernelHelpers::ServiceContext service_context; | 221 | KernelHelpers::ServiceContext service_context; |
| 50 | Kernel::KEvent* nfc_tag_load; | 222 | Kernel::KEvent* activate_event; |
| 51 | AmiiboFile amiibo{}; | 223 | Kernel::KEvent* deactivate_event; |
| 224 | NTAG215File tag_data{}; | ||
| 225 | s32 protocol; | ||
| 226 | bool is_application_area_initialized{}; | ||
| 227 | u32 application_area_id; | ||
| 228 | std::vector<u8> application_area_data; | ||
| 52 | }; | 229 | }; |
| 53 | }; | 230 | }; |
| 54 | 231 | ||
| 232 | class IUser final : public ServiceFramework<IUser> { | ||
| 233 | public: | ||
| 234 | explicit IUser(Module::Interface& nfp_interface_, Core::System& system_); | ||
| 235 | |||
| 236 | private: | ||
| 237 | void Initialize(Kernel::HLERequestContext& ctx); | ||
| 238 | void Finalize(Kernel::HLERequestContext& ctx); | ||
| 239 | void ListDevices(Kernel::HLERequestContext& ctx); | ||
| 240 | void StartDetection(Kernel::HLERequestContext& ctx); | ||
| 241 | void StopDetection(Kernel::HLERequestContext& ctx); | ||
| 242 | void Mount(Kernel::HLERequestContext& ctx); | ||
| 243 | void Unmount(Kernel::HLERequestContext& ctx); | ||
| 244 | void OpenApplicationArea(Kernel::HLERequestContext& ctx); | ||
| 245 | void GetApplicationArea(Kernel::HLERequestContext& ctx); | ||
| 246 | void SetApplicationArea(Kernel::HLERequestContext& ctx); | ||
| 247 | void CreateApplicationArea(Kernel::HLERequestContext& ctx); | ||
| 248 | void GetTagInfo(Kernel::HLERequestContext& ctx); | ||
| 249 | void GetRegisterInfo(Kernel::HLERequestContext& ctx); | ||
| 250 | void GetCommonInfo(Kernel::HLERequestContext& ctx); | ||
| 251 | void GetModelInfo(Kernel::HLERequestContext& ctx); | ||
| 252 | void AttachActivateEvent(Kernel::HLERequestContext& ctx); | ||
| 253 | void AttachDeactivateEvent(Kernel::HLERequestContext& ctx); | ||
| 254 | void GetState(Kernel::HLERequestContext& ctx); | ||
| 255 | void GetDeviceState(Kernel::HLERequestContext& ctx); | ||
| 256 | void GetNpadId(Kernel::HLERequestContext& ctx); | ||
| 257 | void GetApplicationAreaSize(Kernel::HLERequestContext& ctx); | ||
| 258 | void AttachAvailabilityChangeEvent(Kernel::HLERequestContext& ctx); | ||
| 259 | |||
| 260 | KernelHelpers::ServiceContext service_context; | ||
| 261 | |||
| 262 | // TODO(german77): We should have a vector of interfaces | ||
| 263 | Module::Interface& nfp_interface; | ||
| 264 | |||
| 265 | State state{State::NonInitialized}; | ||
| 266 | Kernel::KEvent* availability_change_event; | ||
| 267 | }; | ||
| 268 | |||
| 55 | void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system); | 269 | void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system); |
| 56 | 270 | ||
| 57 | } // namespace Service::NFP | 271 | } // namespace Service::NFP |
diff --git a/src/core/hle/service/ns/pdm_qry.cpp b/src/core/hle/service/ns/pdm_qry.cpp index e2fab5c3f..36ce46353 100644 --- a/src/core/hle/service/ns/pdm_qry.cpp +++ b/src/core/hle/service/ns/pdm_qry.cpp | |||
| @@ -59,7 +59,7 @@ void PDM_QRY::QueryPlayStatisticsByApplicationIdAndUserAccountId(Kernel::HLERequ | |||
| 59 | 59 | ||
| 60 | LOG_WARNING(Service_NS, | 60 | LOG_WARNING(Service_NS, |
| 61 | "(STUBBED) called. unknown={}. application_id=0x{:016X}, user_account_uid=0x{}", | 61 | "(STUBBED) called. unknown={}. application_id=0x{:016X}, user_account_uid=0x{}", |
| 62 | unknown, application_id, user_account_uid.Format()); | 62 | unknown, application_id, user_account_uid.RawString()); |
| 63 | 63 | ||
| 64 | IPC::ResponseBuilder rb{ctx, 12}; | 64 | IPC::ResponseBuilder rb{ctx, 12}; |
| 65 | rb.Push(ResultSuccess); | 65 | rb.Push(ResultSuccess); |
diff --git a/src/core/hle/service/pm/pm.cpp b/src/core/hle/service/pm/pm.cpp index 277abc17a..057666021 100644 --- a/src/core/hle/service/pm/pm.cpp +++ b/src/core/hle/service/pm/pm.cpp | |||
| @@ -91,6 +91,8 @@ public: | |||
| 91 | {4, &DebugMonitor::GetApplicationProcessId, "GetApplicationProcessId"}, | 91 | {4, &DebugMonitor::GetApplicationProcessId, "GetApplicationProcessId"}, |
| 92 | {5, nullptr, "HookToCreateApplicationProgress"}, | 92 | {5, nullptr, "HookToCreateApplicationProgress"}, |
| 93 | {6, nullptr, "ClearHook"}, | 93 | {6, nullptr, "ClearHook"}, |
| 94 | {65000, &DebugMonitor::AtmosphereGetProcessInfo, "AtmosphereGetProcessInfo"}, | ||
| 95 | {65001, nullptr, "AtmosphereGetCurrentLimitInfo"}, | ||
| 94 | }; | 96 | }; |
| 95 | // clang-format on | 97 | // clang-format on |
| 96 | 98 | ||
| @@ -125,6 +127,49 @@ private: | |||
| 125 | GetApplicationPidGeneric(ctx, kernel.GetProcessList()); | 127 | GetApplicationPidGeneric(ctx, kernel.GetProcessList()); |
| 126 | } | 128 | } |
| 127 | 129 | ||
| 130 | void AtmosphereGetProcessInfo(Kernel::HLERequestContext& ctx) { | ||
| 131 | // https://github.com/Atmosphere-NX/Atmosphere/blob/master/stratosphere/pm/source/impl/pm_process_manager.cpp#L614 | ||
| 132 | // This implementation is incomplete; only a handle to the process is returned. | ||
| 133 | IPC::RequestParser rp{ctx}; | ||
| 134 | const auto pid = rp.PopRaw<u64>(); | ||
| 135 | |||
| 136 | LOG_WARNING(Service_PM, "(Partial Implementation) called, pid={:016X}", pid); | ||
| 137 | |||
| 138 | const auto process = SearchProcessList(kernel.GetProcessList(), [pid](const auto& proc) { | ||
| 139 | return proc->GetProcessID() == pid; | ||
| 140 | }); | ||
| 141 | |||
| 142 | if (!process.has_value()) { | ||
| 143 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 144 | rb.Push(ResultProcessNotFound); | ||
| 145 | return; | ||
| 146 | } | ||
| 147 | |||
| 148 | struct ProgramLocation { | ||
| 149 | u64 program_id; | ||
| 150 | u8 storage_id; | ||
| 151 | }; | ||
| 152 | static_assert(sizeof(ProgramLocation) == 0x10, "ProgramLocation has an invalid size"); | ||
| 153 | |||
| 154 | struct OverrideStatus { | ||
| 155 | u64 keys_held; | ||
| 156 | u64 flags; | ||
| 157 | }; | ||
| 158 | static_assert(sizeof(OverrideStatus) == 0x10, "OverrideStatus has an invalid size"); | ||
| 159 | |||
| 160 | OverrideStatus override_status{}; | ||
| 161 | ProgramLocation program_location{ | ||
| 162 | .program_id = (*process)->GetProgramID(), | ||
| 163 | .storage_id = 0, | ||
| 164 | }; | ||
| 165 | |||
| 166 | IPC::ResponseBuilder rb{ctx, 10, 1}; | ||
| 167 | rb.Push(ResultSuccess); | ||
| 168 | rb.PushCopyObjects(*process); | ||
| 169 | rb.PushRaw(program_location); | ||
| 170 | rb.PushRaw(override_status); | ||
| 171 | } | ||
| 172 | |||
| 128 | const Kernel::KernelCore& kernel; | 173 | const Kernel::KernelCore& kernel; |
| 129 | }; | 174 | }; |
| 130 | 175 | ||
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index f54e6fe56..eb1138313 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp | |||
| @@ -39,6 +39,7 @@ | |||
| 39 | #include "core/hle/service/mig/mig.h" | 39 | #include "core/hle/service/mig/mig.h" |
| 40 | #include "core/hle/service/mii/mii.h" | 40 | #include "core/hle/service/mii/mii.h" |
| 41 | #include "core/hle/service/mm/mm_u.h" | 41 | #include "core/hle/service/mm/mm_u.h" |
| 42 | #include "core/hle/service/mnpp/mnpp_app.h" | ||
| 42 | #include "core/hle/service/ncm/ncm.h" | 43 | #include "core/hle/service/ncm/ncm.h" |
| 43 | #include "core/hle/service/nfc/nfc.h" | 44 | #include "core/hle/service/nfc/nfc.h" |
| 44 | #include "core/hle/service/nfp/nfp.h" | 45 | #include "core/hle/service/nfp/nfp.h" |
| @@ -265,6 +266,7 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system | |||
| 265 | Migration::InstallInterfaces(*sm, system); | 266 | Migration::InstallInterfaces(*sm, system); |
| 266 | Mii::InstallInterfaces(*sm, system); | 267 | Mii::InstallInterfaces(*sm, system); |
| 267 | MM::InstallInterfaces(*sm, system); | 268 | MM::InstallInterfaces(*sm, system); |
| 269 | MNPP::InstallInterfaces(*sm, system); | ||
| 268 | NCM::InstallInterfaces(*sm, system); | 270 | NCM::InstallInterfaces(*sm, system); |
| 269 | NFC::InstallInterfaces(*sm, system); | 271 | NFC::InstallInterfaces(*sm, system); |
| 270 | NFP::InstallInterfaces(*sm, system); | 272 | NFP::InstallInterfaces(*sm, system); |
diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp index eaa172595..695a1faa6 100644 --- a/src/core/hle/service/sm/sm.cpp +++ b/src/core/hle/service/sm/sm.cpp | |||
| @@ -81,6 +81,8 @@ ResultVal<Kernel::KPort*> ServiceManager::GetServicePort(const std::string& name | |||
| 81 | } | 81 | } |
| 82 | 82 | ||
| 83 | auto* port = Kernel::KPort::Create(kernel); | 83 | auto* port = Kernel::KPort::Create(kernel); |
| 84 | SCOPE_EXIT({ port->Close(); }); | ||
| 85 | |||
| 84 | port->Initialize(ServerSessionCountMax, false, name); | 86 | port->Initialize(ServerSessionCountMax, false, name); |
| 85 | auto handler = it->second; | 87 | auto handler = it->second; |
| 86 | port->GetServerPort().SetSessionHandler(std::move(handler)); | 88 | port->GetServerPort().SetSessionHandler(std::move(handler)); |
diff --git a/src/core/hle/service/sockets/bsd.cpp b/src/core/hle/service/sockets/bsd.cpp index f83272633..3dbac5a23 100644 --- a/src/core/hle/service/sockets/bsd.cpp +++ b/src/core/hle/service/sockets/bsd.cpp | |||
| @@ -569,9 +569,9 @@ std::pair<s32, Errno> BSD::AcceptImpl(s32 fd, std::vector<u8>& write_buffer) { | |||
| 569 | new_descriptor.socket = std::move(result.socket); | 569 | new_descriptor.socket = std::move(result.socket); |
| 570 | new_descriptor.is_connection_based = descriptor.is_connection_based; | 570 | new_descriptor.is_connection_based = descriptor.is_connection_based; |
| 571 | 571 | ||
| 572 | ASSERT(write_buffer.size() == sizeof(SockAddrIn)); | ||
| 573 | const SockAddrIn guest_addr_in = Translate(result.sockaddr_in); | 572 | const SockAddrIn guest_addr_in = Translate(result.sockaddr_in); |
| 574 | std::memcpy(write_buffer.data(), &guest_addr_in, sizeof(guest_addr_in)); | 573 | const size_t length = std::min(sizeof(guest_addr_in), write_buffer.size()); |
| 574 | std::memcpy(write_buffer.data(), &guest_addr_in, length); | ||
| 575 | 575 | ||
| 576 | return {new_fd, Errno::SUCCESS}; | 576 | return {new_fd, Errno::SUCCESS}; |
| 577 | } | 577 | } |
diff --git a/src/core/hle/service/time/clock_types.h b/src/core/hle/service/time/clock_types.h index 392e16863..d0cacb80c 100644 --- a/src/core/hle/service/time/clock_types.h +++ b/src/core/hle/service/time/clock_types.h | |||
| @@ -36,7 +36,7 @@ struct SteadyClockTimePoint { | |||
| 36 | } | 36 | } |
| 37 | 37 | ||
| 38 | static SteadyClockTimePoint GetRandom() { | 38 | static SteadyClockTimePoint GetRandom() { |
| 39 | return {0, Common::UUID::Generate()}; | 39 | return {0, Common::UUID::MakeRandom()}; |
| 40 | } | 40 | } |
| 41 | }; | 41 | }; |
| 42 | static_assert(sizeof(SteadyClockTimePoint) == 0x18, "SteadyClockTimePoint is incorrect size"); | 42 | static_assert(sizeof(SteadyClockTimePoint) == 0x18, "SteadyClockTimePoint is incorrect size"); |
diff --git a/src/core/hle/service/time/steady_clock_core.h b/src/core/hle/service/time/steady_clock_core.h index d80a2385f..5ee2c0e0a 100644 --- a/src/core/hle/service/time/steady_clock_core.h +++ b/src/core/hle/service/time/steady_clock_core.h | |||
| @@ -49,7 +49,7 @@ public: | |||
| 49 | } | 49 | } |
| 50 | 50 | ||
| 51 | private: | 51 | private: |
| 52 | Common::UUID clock_source_id{Common::UUID::Generate()}; | 52 | Common::UUID clock_source_id{Common::UUID::MakeRandom()}; |
| 53 | bool is_initialized{}; | 53 | bool is_initialized{}; |
| 54 | }; | 54 | }; |
| 55 | 55 | ||
diff --git a/src/core/hle/service/time/time_manager.cpp b/src/core/hle/service/time/time_manager.cpp index c1e4e6cce..00f1ae8cf 100644 --- a/src/core/hle/service/time/time_manager.cpp +++ b/src/core/hle/service/time/time_manager.cpp | |||
| @@ -45,7 +45,7 @@ struct TimeManager::Impl final { | |||
| 45 | time_zone_content_manager{system} { | 45 | time_zone_content_manager{system} { |
| 46 | 46 | ||
| 47 | const auto system_time{Clock::TimeSpanType::FromSeconds(GetExternalRtcValue())}; | 47 | const auto system_time{Clock::TimeSpanType::FromSeconds(GetExternalRtcValue())}; |
| 48 | SetupStandardSteadyClock(system, Common::UUID::Generate(), system_time, {}, {}); | 48 | SetupStandardSteadyClock(system, Common::UUID::MakeRandom(), system_time, {}, {}); |
| 49 | SetupStandardLocalSystemClock(system, {}, system_time.ToSeconds()); | 49 | SetupStandardLocalSystemClock(system, {}, system_time.ToSeconds()); |
| 50 | 50 | ||
| 51 | Clock::SystemClockContext clock_context{}; | 51 | Clock::SystemClockContext clock_context{}; |
diff --git a/src/core/hle/service/vi/display/vi_display.h b/src/core/hle/service/vi/display/vi_display.h index 0979fc421..329f4ba86 100644 --- a/src/core/hle/service/vi/display/vi_display.h +++ b/src/core/hle/service/vi/display/vi_display.h | |||
| @@ -28,10 +28,10 @@ class Layer; | |||
| 28 | 28 | ||
| 29 | /// Represents a single display type | 29 | /// Represents a single display type |
| 30 | class Display { | 30 | class Display { |
| 31 | public: | ||
| 31 | YUZU_NON_COPYABLE(Display); | 32 | YUZU_NON_COPYABLE(Display); |
| 32 | YUZU_NON_MOVEABLE(Display); | 33 | YUZU_NON_MOVEABLE(Display); |
| 33 | 34 | ||
| 34 | public: | ||
| 35 | /// Constructs a display with a given unique ID and name. | 35 | /// Constructs a display with a given unique ID and name. |
| 36 | /// | 36 | /// |
| 37 | /// @param id The unique ID for this display. | 37 | /// @param id The unique ID for this display. |
diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h index 7b1bac3f7..8b6b3b68f 100644 --- a/src/core/loader/loader.h +++ b/src/core/loader/loader.h | |||
| @@ -11,6 +11,7 @@ | |||
| 11 | #include <utility> | 11 | #include <utility> |
| 12 | #include <vector> | 12 | #include <vector> |
| 13 | 13 | ||
| 14 | #include "common/common_funcs.h" | ||
| 14 | #include "common/common_types.h" | 15 | #include "common/common_types.h" |
| 15 | #include "core/file_sys/control_metadata.h" | 16 | #include "core/file_sys/control_metadata.h" |
| 16 | #include "core/file_sys/vfs.h" | 17 | #include "core/file_sys/vfs.h" |
| @@ -139,8 +140,11 @@ std::string GetResultStatusString(ResultStatus status); | |||
| 139 | std::ostream& operator<<(std::ostream& os, ResultStatus status); | 140 | std::ostream& operator<<(std::ostream& os, ResultStatus status); |
| 140 | 141 | ||
| 141 | /// Interface for loading an application | 142 | /// Interface for loading an application |
| 142 | class AppLoader : NonCopyable { | 143 | class AppLoader { |
| 143 | public: | 144 | public: |
| 145 | YUZU_NON_COPYABLE(AppLoader); | ||
| 146 | YUZU_NON_MOVEABLE(AppLoader); | ||
| 147 | |||
| 144 | struct LoadParameters { | 148 | struct LoadParameters { |
| 145 | s32 main_thread_priority; | 149 | s32 main_thread_priority; |
| 146 | u64 main_thread_stack_size; | 150 | u64 main_thread_stack_size; |
diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 88d6ec908..28d30eee2 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp | |||
| @@ -39,8 +39,7 @@ struct Memory::Impl { | |||
| 39 | void MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, PAddr target) { | 39 | void MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, PAddr target) { |
| 40 | ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: {:016X}", size); | 40 | ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: {:016X}", size); |
| 41 | ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: {:016X}", base); | 41 | ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: {:016X}", base); |
| 42 | ASSERT_MSG(target >= DramMemoryMap::Base && target < DramMemoryMap::End, | 42 | ASSERT_MSG(target >= DramMemoryMap::Base, "Out of bounds target: {:016X}", target); |
| 43 | "Out of bounds target: {:016X}", target); | ||
| 44 | MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, target, Common::PageType::Memory); | 43 | MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, target, Common::PageType::Memory); |
| 45 | 44 | ||
| 46 | if (Settings::IsFastmemEnabled()) { | 45 | if (Settings::IsFastmemEnabled()) { |
diff --git a/src/input_common/drivers/gc_adapter.cpp b/src/input_common/drivers/gc_adapter.cpp index 7ab4540a8..155caae42 100644 --- a/src/input_common/drivers/gc_adapter.cpp +++ b/src/input_common/drivers/gc_adapter.cpp | |||
| @@ -248,7 +248,7 @@ bool GCAdapter::Setup() { | |||
| 248 | std::size_t port = 0; | 248 | std::size_t port = 0; |
| 249 | for (GCController& pad : pads) { | 249 | for (GCController& pad : pads) { |
| 250 | pad.identifier = { | 250 | pad.identifier = { |
| 251 | .guid = Common::UUID{Common::INVALID_UUID}, | 251 | .guid = Common::UUID{}, |
| 252 | .port = port++, | 252 | .port = port++, |
| 253 | .pad = 0, | 253 | .pad = 0, |
| 254 | }; | 254 | }; |
diff --git a/src/input_common/drivers/keyboard.cpp b/src/input_common/drivers/keyboard.cpp index 4c1e5bbec..59e3d9cc0 100644 --- a/src/input_common/drivers/keyboard.cpp +++ b/src/input_common/drivers/keyboard.cpp | |||
| @@ -9,17 +9,17 @@ | |||
| 9 | namespace InputCommon { | 9 | namespace InputCommon { |
| 10 | 10 | ||
| 11 | constexpr PadIdentifier key_identifier = { | 11 | constexpr PadIdentifier key_identifier = { |
| 12 | .guid = Common::UUID{Common::INVALID_UUID}, | 12 | .guid = Common::UUID{}, |
| 13 | .port = 0, | 13 | .port = 0, |
| 14 | .pad = 0, | 14 | .pad = 0, |
| 15 | }; | 15 | }; |
| 16 | constexpr PadIdentifier keyboard_key_identifier = { | 16 | constexpr PadIdentifier keyboard_key_identifier = { |
| 17 | .guid = Common::UUID{Common::INVALID_UUID}, | 17 | .guid = Common::UUID{}, |
| 18 | .port = 1, | 18 | .port = 1, |
| 19 | .pad = 0, | 19 | .pad = 0, |
| 20 | }; | 20 | }; |
| 21 | constexpr PadIdentifier keyboard_modifier_identifier = { | 21 | constexpr PadIdentifier keyboard_modifier_identifier = { |
| 22 | .guid = Common::UUID{Common::INVALID_UUID}, | 22 | .guid = Common::UUID{}, |
| 23 | .port = 1, | 23 | .port = 1, |
| 24 | .pad = 1, | 24 | .pad = 1, |
| 25 | }; | 25 | }; |
diff --git a/src/input_common/drivers/mouse.cpp b/src/input_common/drivers/mouse.cpp index d8ae7f0c1..3c9a4e747 100644 --- a/src/input_common/drivers/mouse.cpp +++ b/src/input_common/drivers/mouse.cpp | |||
| @@ -20,7 +20,7 @@ constexpr int motion_wheel_y = 4; | |||
| 20 | constexpr int touch_axis_x = 10; | 20 | constexpr int touch_axis_x = 10; |
| 21 | constexpr int touch_axis_y = 11; | 21 | constexpr int touch_axis_y = 11; |
| 22 | constexpr PadIdentifier identifier = { | 22 | constexpr PadIdentifier identifier = { |
| 23 | .guid = Common::UUID{Common::INVALID_UUID}, | 23 | .guid = Common::UUID{}, |
| 24 | .port = 0, | 24 | .port = 0, |
| 25 | .pad = 0, | 25 | .pad = 0, |
| 26 | }; | 26 | }; |
diff --git a/src/input_common/drivers/sdl_driver.cpp b/src/input_common/drivers/sdl_driver.cpp index 577bf5c31..c17ea305e 100644 --- a/src/input_common/drivers/sdl_driver.cpp +++ b/src/input_common/drivers/sdl_driver.cpp | |||
| @@ -175,23 +175,23 @@ public: | |||
| 175 | return false; | 175 | return false; |
| 176 | } | 176 | } |
| 177 | 177 | ||
| 178 | BatteryLevel GetBatteryLevel() { | 178 | Common::Input::BatteryLevel GetBatteryLevel() { |
| 179 | const auto level = SDL_JoystickCurrentPowerLevel(sdl_joystick.get()); | 179 | const auto level = SDL_JoystickCurrentPowerLevel(sdl_joystick.get()); |
| 180 | switch (level) { | 180 | switch (level) { |
| 181 | case SDL_JOYSTICK_POWER_EMPTY: | 181 | case SDL_JOYSTICK_POWER_EMPTY: |
| 182 | return BatteryLevel::Empty; | 182 | return Common::Input::BatteryLevel::Empty; |
| 183 | case SDL_JOYSTICK_POWER_LOW: | 183 | case SDL_JOYSTICK_POWER_LOW: |
| 184 | return BatteryLevel::Critical; | 184 | return Common::Input::BatteryLevel::Low; |
| 185 | case SDL_JOYSTICK_POWER_MEDIUM: | 185 | case SDL_JOYSTICK_POWER_MEDIUM: |
| 186 | return BatteryLevel::Low; | 186 | return Common::Input::BatteryLevel::Medium; |
| 187 | case SDL_JOYSTICK_POWER_FULL: | 187 | case SDL_JOYSTICK_POWER_FULL: |
| 188 | return BatteryLevel::Medium; | ||
| 189 | case SDL_JOYSTICK_POWER_MAX: | 188 | case SDL_JOYSTICK_POWER_MAX: |
| 190 | return BatteryLevel::Full; | 189 | return Common::Input::BatteryLevel::Full; |
| 191 | case SDL_JOYSTICK_POWER_UNKNOWN: | ||
| 192 | case SDL_JOYSTICK_POWER_WIRED: | 190 | case SDL_JOYSTICK_POWER_WIRED: |
| 191 | return Common::Input::BatteryLevel::Charging; | ||
| 192 | case SDL_JOYSTICK_POWER_UNKNOWN: | ||
| 193 | default: | 193 | default: |
| 194 | return BatteryLevel::Charging; | 194 | return Common::Input::BatteryLevel::None; |
| 195 | } | 195 | } |
| 196 | } | 196 | } |
| 197 | 197 | ||
| @@ -352,6 +352,8 @@ void SDLDriver::HandleGameControllerEvent(const SDL_Event& event) { | |||
| 352 | if (const auto joystick = GetSDLJoystickBySDLID(event.jbutton.which)) { | 352 | if (const auto joystick = GetSDLJoystickBySDLID(event.jbutton.which)) { |
| 353 | const PadIdentifier identifier = joystick->GetPadIdentifier(); | 353 | const PadIdentifier identifier = joystick->GetPadIdentifier(); |
| 354 | SetButton(identifier, event.jbutton.button, true); | 354 | SetButton(identifier, event.jbutton.button, true); |
| 355 | // Battery doesn't trigger an event so just update every button press | ||
| 356 | SetBattery(identifier, joystick->GetBatteryLevel()); | ||
| 355 | } | 357 | } |
| 356 | break; | 358 | break; |
| 357 | } | 359 | } |
| @@ -503,7 +505,7 @@ std::vector<Common::ParamPackage> SDLDriver::GetInputDevices() const { | |||
| 503 | Common::Input::VibrationError SDLDriver::SetRumble( | 505 | Common::Input::VibrationError SDLDriver::SetRumble( |
| 504 | const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) { | 506 | const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) { |
| 505 | const auto joystick = | 507 | const auto joystick = |
| 506 | GetSDLJoystickByGUID(identifier.guid.Format(), static_cast<int>(identifier.port)); | 508 | GetSDLJoystickByGUID(identifier.guid.RawString(), static_cast<int>(identifier.port)); |
| 507 | const auto process_amplitude_exp = [](f32 amplitude, f32 factor) { | 509 | const auto process_amplitude_exp = [](f32 amplitude, f32 factor) { |
| 508 | return (amplitude + std::pow(amplitude, factor)) * 0.5f * 0xFFFF; | 510 | return (amplitude + std::pow(amplitude, factor)) * 0.5f * 0xFFFF; |
| 509 | }; | 511 | }; |
| @@ -600,7 +602,7 @@ Common::ParamPackage SDLDriver::BuildParamPackageForAnalog(PadIdentifier identif | |||
| 600 | Common::ParamPackage params; | 602 | Common::ParamPackage params; |
| 601 | params.Set("engine", GetEngineName()); | 603 | params.Set("engine", GetEngineName()); |
| 602 | params.Set("port", static_cast<int>(identifier.port)); | 604 | params.Set("port", static_cast<int>(identifier.port)); |
| 603 | params.Set("guid", identifier.guid.Format()); | 605 | params.Set("guid", identifier.guid.RawString()); |
| 604 | params.Set("axis_x", axis_x); | 606 | params.Set("axis_x", axis_x); |
| 605 | params.Set("axis_y", axis_y); | 607 | params.Set("axis_y", axis_y); |
| 606 | params.Set("offset_x", offset_x); | 608 | params.Set("offset_x", offset_x); |
| @@ -812,7 +814,7 @@ AnalogMapping SDLDriver::GetAnalogMappingForDevice(const Common::ParamPackage& p | |||
| 812 | PreSetAxis(identifier, binding_left_x.value.axis); | 814 | PreSetAxis(identifier, binding_left_x.value.axis); |
| 813 | PreSetAxis(identifier, binding_left_y.value.axis); | 815 | PreSetAxis(identifier, binding_left_y.value.axis); |
| 814 | const auto left_offset_x = -GetAxis(identifier, binding_left_x.value.axis); | 816 | const auto left_offset_x = -GetAxis(identifier, binding_left_x.value.axis); |
| 815 | const auto left_offset_y = -GetAxis(identifier, binding_left_y.value.axis); | 817 | const auto left_offset_y = GetAxis(identifier, binding_left_y.value.axis); |
| 816 | mapping.insert_or_assign(Settings::NativeAnalog::LStick, | 818 | mapping.insert_or_assign(Settings::NativeAnalog::LStick, |
| 817 | BuildParamPackageForAnalog(identifier, binding_left_x.value.axis, | 819 | BuildParamPackageForAnalog(identifier, binding_left_x.value.axis, |
| 818 | binding_left_y.value.axis, | 820 | binding_left_y.value.axis, |
| @@ -823,7 +825,7 @@ AnalogMapping SDLDriver::GetAnalogMappingForDevice(const Common::ParamPackage& p | |||
| 823 | PreSetAxis(identifier, binding_left_x.value.axis); | 825 | PreSetAxis(identifier, binding_left_x.value.axis); |
| 824 | PreSetAxis(identifier, binding_left_y.value.axis); | 826 | PreSetAxis(identifier, binding_left_y.value.axis); |
| 825 | const auto left_offset_x = -GetAxis(identifier, binding_left_x.value.axis); | 827 | const auto left_offset_x = -GetAxis(identifier, binding_left_x.value.axis); |
| 826 | const auto left_offset_y = -GetAxis(identifier, binding_left_y.value.axis); | 828 | const auto left_offset_y = GetAxis(identifier, binding_left_y.value.axis); |
| 827 | mapping.insert_or_assign(Settings::NativeAnalog::LStick, | 829 | mapping.insert_or_assign(Settings::NativeAnalog::LStick, |
| 828 | BuildParamPackageForAnalog(identifier, binding_left_x.value.axis, | 830 | BuildParamPackageForAnalog(identifier, binding_left_x.value.axis, |
| 829 | binding_left_y.value.axis, | 831 | binding_left_y.value.axis, |
| @@ -838,7 +840,7 @@ AnalogMapping SDLDriver::GetAnalogMappingForDevice(const Common::ParamPackage& p | |||
| 838 | PreSetAxis(identifier, binding_right_x.value.axis); | 840 | PreSetAxis(identifier, binding_right_x.value.axis); |
| 839 | PreSetAxis(identifier, binding_right_y.value.axis); | 841 | PreSetAxis(identifier, binding_right_y.value.axis); |
| 840 | const auto right_offset_x = -GetAxis(identifier, binding_right_x.value.axis); | 842 | const auto right_offset_x = -GetAxis(identifier, binding_right_x.value.axis); |
| 841 | const auto right_offset_y = -GetAxis(identifier, binding_right_y.value.axis); | 843 | const auto right_offset_y = GetAxis(identifier, binding_right_y.value.axis); |
| 842 | mapping.insert_or_assign(Settings::NativeAnalog::RStick, | 844 | mapping.insert_or_assign(Settings::NativeAnalog::RStick, |
| 843 | BuildParamPackageForAnalog(identifier, binding_right_x.value.axis, | 845 | BuildParamPackageForAnalog(identifier, binding_right_x.value.axis, |
| 844 | binding_right_y.value.axis, right_offset_x, | 846 | binding_right_y.value.axis, right_offset_x, |
diff --git a/src/input_common/drivers/touch_screen.cpp b/src/input_common/drivers/touch_screen.cpp index 880781825..30c727df4 100644 --- a/src/input_common/drivers/touch_screen.cpp +++ b/src/input_common/drivers/touch_screen.cpp | |||
| @@ -8,7 +8,7 @@ | |||
| 8 | namespace InputCommon { | 8 | namespace InputCommon { |
| 9 | 9 | ||
| 10 | constexpr PadIdentifier identifier = { | 10 | constexpr PadIdentifier identifier = { |
| 11 | .guid = Common::UUID{Common::INVALID_UUID}, | 11 | .guid = Common::UUID{}, |
| 12 | .port = 0, | 12 | .port = 0, |
| 13 | .pad = 0, | 13 | .pad = 0, |
| 14 | }; | 14 | }; |
diff --git a/src/input_common/drivers/udp_client.cpp b/src/input_common/drivers/udp_client.cpp index 333173e3d..9780ead10 100644 --- a/src/input_common/drivers/udp_client.cpp +++ b/src/input_common/drivers/udp_client.cpp | |||
| @@ -192,22 +192,22 @@ std::size_t UDPClient::GetClientNumber(std::string_view host, u16 port) const { | |||
| 192 | return MAX_UDP_CLIENTS; | 192 | return MAX_UDP_CLIENTS; |
| 193 | } | 193 | } |
| 194 | 194 | ||
| 195 | BatteryLevel UDPClient::GetBatteryLevel(Response::Battery battery) const { | 195 | Common::Input::BatteryLevel UDPClient::GetBatteryLevel(Response::Battery battery) const { |
| 196 | switch (battery) { | 196 | switch (battery) { |
| 197 | case Response::Battery::Dying: | 197 | case Response::Battery::Dying: |
| 198 | return BatteryLevel::Empty; | 198 | return Common::Input::BatteryLevel::Empty; |
| 199 | case Response::Battery::Low: | 199 | case Response::Battery::Low: |
| 200 | return BatteryLevel::Critical; | 200 | return Common::Input::BatteryLevel::Critical; |
| 201 | case Response::Battery::Medium: | 201 | case Response::Battery::Medium: |
| 202 | return BatteryLevel::Low; | 202 | return Common::Input::BatteryLevel::Low; |
| 203 | case Response::Battery::High: | 203 | case Response::Battery::High: |
| 204 | return BatteryLevel::Medium; | 204 | return Common::Input::BatteryLevel::Medium; |
| 205 | case Response::Battery::Full: | 205 | case Response::Battery::Full: |
| 206 | case Response::Battery::Charged: | 206 | case Response::Battery::Charged: |
| 207 | return BatteryLevel::Full; | 207 | return Common::Input::BatteryLevel::Full; |
| 208 | case Response::Battery::Charging: | 208 | case Response::Battery::Charging: |
| 209 | default: | 209 | default: |
| 210 | return BatteryLevel::Charging; | 210 | return Common::Input::BatteryLevel::Charging; |
| 211 | } | 211 | } |
| 212 | } | 212 | } |
| 213 | 213 | ||
| @@ -353,7 +353,7 @@ PadIdentifier UDPClient::GetPadIdentifier(std::size_t pad_index) const { | |||
| 353 | 353 | ||
| 354 | Common::UUID UDPClient::GetHostUUID(const std::string& host) const { | 354 | Common::UUID UDPClient::GetHostUUID(const std::string& host) const { |
| 355 | const auto ip = boost::asio::ip::make_address_v4(host); | 355 | const auto ip = boost::asio::ip::make_address_v4(host); |
| 356 | const auto hex_host = fmt::format("{:06x}", ip.to_uint()); | 356 | const auto hex_host = fmt::format("00000000-0000-0000-0000-0000{:06x}", ip.to_uint()); |
| 357 | return Common::UUID{hex_host}; | 357 | return Common::UUID{hex_host}; |
| 358 | } | 358 | } |
| 359 | 359 | ||
| @@ -385,7 +385,7 @@ std::vector<Common::ParamPackage> UDPClient::GetInputDevices() const { | |||
| 385 | Common::ParamPackage identifier{}; | 385 | Common::ParamPackage identifier{}; |
| 386 | identifier.Set("engine", GetEngineName()); | 386 | identifier.Set("engine", GetEngineName()); |
| 387 | identifier.Set("display", fmt::format("UDP Controller {}", pad_identifier.pad)); | 387 | identifier.Set("display", fmt::format("UDP Controller {}", pad_identifier.pad)); |
| 388 | identifier.Set("guid", pad_identifier.guid.Format()); | 388 | identifier.Set("guid", pad_identifier.guid.RawString()); |
| 389 | identifier.Set("port", static_cast<int>(pad_identifier.port)); | 389 | identifier.Set("port", static_cast<int>(pad_identifier.port)); |
| 390 | identifier.Set("pad", static_cast<int>(pad_identifier.pad)); | 390 | identifier.Set("pad", static_cast<int>(pad_identifier.pad)); |
| 391 | devices.emplace_back(identifier); | 391 | devices.emplace_back(identifier); |
diff --git a/src/input_common/drivers/udp_client.h b/src/input_common/drivers/udp_client.h index e9c178139..c7cc7d846 100644 --- a/src/input_common/drivers/udp_client.h +++ b/src/input_common/drivers/udp_client.h | |||
| @@ -126,7 +126,7 @@ private: | |||
| 126 | struct ClientConnection { | 126 | struct ClientConnection { |
| 127 | ClientConnection(); | 127 | ClientConnection(); |
| 128 | ~ClientConnection(); | 128 | ~ClientConnection(); |
| 129 | Common::UUID uuid{"7F000001"}; | 129 | Common::UUID uuid{"00000000-0000-0000-0000-00007F000001"}; |
| 130 | std::string host{"127.0.0.1"}; | 130 | std::string host{"127.0.0.1"}; |
| 131 | u16 port{26760}; | 131 | u16 port{26760}; |
| 132 | s8 active{-1}; | 132 | s8 active{-1}; |
| @@ -141,7 +141,7 @@ private: | |||
| 141 | std::size_t GetClientNumber(std::string_view host, u16 port) const; | 141 | std::size_t GetClientNumber(std::string_view host, u16 port) const; |
| 142 | 142 | ||
| 143 | // Translates UDP battery level to input engine battery level | 143 | // Translates UDP battery level to input engine battery level |
| 144 | BatteryLevel GetBatteryLevel(Response::Battery battery) const; | 144 | Common::Input::BatteryLevel GetBatteryLevel(Response::Battery battery) const; |
| 145 | 145 | ||
| 146 | void OnVersion(Response::Version); | 146 | void OnVersion(Response::Version); |
| 147 | void OnPortInfo(Response::PortInfo); | 147 | void OnPortInfo(Response::PortInfo); |
diff --git a/src/input_common/helpers/stick_from_buttons.cpp b/src/input_common/helpers/stick_from_buttons.cpp index e23394f5f..31e6f62ab 100644 --- a/src/input_common/helpers/stick_from_buttons.cpp +++ b/src/input_common/helpers/stick_from_buttons.cpp | |||
| @@ -167,12 +167,34 @@ public: | |||
| 167 | } | 167 | } |
| 168 | 168 | ||
| 169 | void UpdateModButtonStatus(const Common::Input::CallbackStatus& button_callback) { | 169 | void UpdateModButtonStatus(const Common::Input::CallbackStatus& button_callback) { |
| 170 | modifier_status = button_callback.button_status.value; | 170 | const auto& new_status = button_callback.button_status; |
| 171 | const bool new_button_value = new_status.inverted ? !new_status.value : new_status.value; | ||
| 172 | modifier_status.toggle = new_status.toggle; | ||
| 173 | |||
| 174 | // Update button status with current | ||
| 175 | if (!modifier_status.toggle) { | ||
| 176 | modifier_status.locked = false; | ||
| 177 | if (modifier_status.value != new_button_value) { | ||
| 178 | modifier_status.value = new_button_value; | ||
| 179 | } | ||
| 180 | } else { | ||
| 181 | // Toggle button and lock status | ||
| 182 | if (new_button_value && !modifier_status.locked) { | ||
| 183 | modifier_status.locked = true; | ||
| 184 | modifier_status.value = !modifier_status.value; | ||
| 185 | } | ||
| 186 | |||
| 187 | // Unlock button ready for next press | ||
| 188 | if (!new_button_value && modifier_status.locked) { | ||
| 189 | modifier_status.locked = false; | ||
| 190 | } | ||
| 191 | } | ||
| 192 | |||
| 171 | UpdateStatus(); | 193 | UpdateStatus(); |
| 172 | } | 194 | } |
| 173 | 195 | ||
| 174 | void UpdateStatus() { | 196 | void UpdateStatus() { |
| 175 | const float coef = modifier_status ? modifier_scale : 1.0f; | 197 | const float coef = modifier_status.value ? modifier_scale : 1.0f; |
| 176 | 198 | ||
| 177 | bool r = right_status; | 199 | bool r = right_status; |
| 178 | bool l = left_status; | 200 | bool l = left_status; |
| @@ -266,7 +288,7 @@ public: | |||
| 266 | if (down_status) { | 288 | if (down_status) { |
| 267 | --y; | 289 | --y; |
| 268 | } | 290 | } |
| 269 | const float coef = modifier_status ? modifier_scale : 1.0f; | 291 | const float coef = modifier_status.value ? modifier_scale : 1.0f; |
| 270 | status.x.raw_value = static_cast<float>(x) * coef * (y == 0 ? 1.0f : SQRT_HALF); | 292 | status.x.raw_value = static_cast<float>(x) * coef * (y == 0 ? 1.0f : SQRT_HALF); |
| 271 | status.y.raw_value = static_cast<float>(y) * coef * (x == 0 ? 1.0f : SQRT_HALF); | 293 | status.y.raw_value = static_cast<float>(y) * coef * (x == 0 ? 1.0f : SQRT_HALF); |
| 272 | return status; | 294 | return status; |
| @@ -287,9 +309,9 @@ private: | |||
| 287 | bool down_status{}; | 309 | bool down_status{}; |
| 288 | bool left_status{}; | 310 | bool left_status{}; |
| 289 | bool right_status{}; | 311 | bool right_status{}; |
| 290 | bool modifier_status{}; | ||
| 291 | float last_x_axis_value{}; | 312 | float last_x_axis_value{}; |
| 292 | float last_y_axis_value{}; | 313 | float last_y_axis_value{}; |
| 314 | Common::Input::ButtonStatus modifier_status{}; | ||
| 293 | const Common::Input::AnalogProperties properties{0.0f, 1.0f, 0.5f, 0.0f, false}; | 315 | const Common::Input::AnalogProperties properties{0.0f, 1.0f, 0.5f, 0.0f, false}; |
| 294 | std::chrono::time_point<std::chrono::steady_clock> last_update; | 316 | std::chrono::time_point<std::chrono::steady_clock> last_update; |
| 295 | }; | 317 | }; |
diff --git a/src/input_common/helpers/touch_from_buttons.cpp b/src/input_common/helpers/touch_from_buttons.cpp index ece1e3b32..f1b57d03a 100644 --- a/src/input_common/helpers/touch_from_buttons.cpp +++ b/src/input_common/helpers/touch_from_buttons.cpp | |||
| @@ -4,7 +4,6 @@ | |||
| 4 | 4 | ||
| 5 | #include <algorithm> | 5 | #include <algorithm> |
| 6 | #include "common/settings.h" | 6 | #include "common/settings.h" |
| 7 | #include "core/frontend/framebuffer_layout.h" | ||
| 8 | #include "input_common/helpers/touch_from_buttons.h" | 7 | #include "input_common/helpers/touch_from_buttons.h" |
| 9 | 8 | ||
| 10 | namespace InputCommon { | 9 | namespace InputCommon { |
diff --git a/src/input_common/input_engine.cpp b/src/input_common/input_engine.cpp index 0508b408d..7adf7e3d7 100644 --- a/src/input_common/input_engine.cpp +++ b/src/input_common/input_engine.cpp | |||
| @@ -70,7 +70,7 @@ void InputEngine::SetAxis(const PadIdentifier& identifier, int axis, f32 value) | |||
| 70 | TriggerOnAxisChange(identifier, axis, value); | 70 | TriggerOnAxisChange(identifier, axis, value); |
| 71 | } | 71 | } |
| 72 | 72 | ||
| 73 | void InputEngine::SetBattery(const PadIdentifier& identifier, BatteryLevel value) { | 73 | void InputEngine::SetBattery(const PadIdentifier& identifier, Common::Input::BatteryLevel value) { |
| 74 | { | 74 | { |
| 75 | std::lock_guard lock{mutex}; | 75 | std::lock_guard lock{mutex}; |
| 76 | ControllerData& controller = controller_list.at(identifier); | 76 | ControllerData& controller = controller_list.at(identifier); |
| @@ -96,7 +96,7 @@ bool InputEngine::GetButton(const PadIdentifier& identifier, int button) const { | |||
| 96 | std::lock_guard lock{mutex}; | 96 | std::lock_guard lock{mutex}; |
| 97 | const auto controller_iter = controller_list.find(identifier); | 97 | const auto controller_iter = controller_list.find(identifier); |
| 98 | if (controller_iter == controller_list.cend()) { | 98 | if (controller_iter == controller_list.cend()) { |
| 99 | LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.Format(), | 99 | LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(), |
| 100 | identifier.pad, identifier.port); | 100 | identifier.pad, identifier.port); |
| 101 | return false; | 101 | return false; |
| 102 | } | 102 | } |
| @@ -113,7 +113,7 @@ bool InputEngine::GetHatButton(const PadIdentifier& identifier, int button, u8 d | |||
| 113 | std::lock_guard lock{mutex}; | 113 | std::lock_guard lock{mutex}; |
| 114 | const auto controller_iter = controller_list.find(identifier); | 114 | const auto controller_iter = controller_list.find(identifier); |
| 115 | if (controller_iter == controller_list.cend()) { | 115 | if (controller_iter == controller_list.cend()) { |
| 116 | LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.Format(), | 116 | LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(), |
| 117 | identifier.pad, identifier.port); | 117 | identifier.pad, identifier.port); |
| 118 | return false; | 118 | return false; |
| 119 | } | 119 | } |
| @@ -130,7 +130,7 @@ f32 InputEngine::GetAxis(const PadIdentifier& identifier, int axis) const { | |||
| 130 | std::lock_guard lock{mutex}; | 130 | std::lock_guard lock{mutex}; |
| 131 | const auto controller_iter = controller_list.find(identifier); | 131 | const auto controller_iter = controller_list.find(identifier); |
| 132 | if (controller_iter == controller_list.cend()) { | 132 | if (controller_iter == controller_list.cend()) { |
| 133 | LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.Format(), | 133 | LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(), |
| 134 | identifier.pad, identifier.port); | 134 | identifier.pad, identifier.port); |
| 135 | return 0.0f; | 135 | return 0.0f; |
| 136 | } | 136 | } |
| @@ -143,13 +143,13 @@ f32 InputEngine::GetAxis(const PadIdentifier& identifier, int axis) const { | |||
| 143 | return axis_iter->second; | 143 | return axis_iter->second; |
| 144 | } | 144 | } |
| 145 | 145 | ||
| 146 | BatteryLevel InputEngine::GetBattery(const PadIdentifier& identifier) const { | 146 | Common::Input::BatteryLevel InputEngine::GetBattery(const PadIdentifier& identifier) const { |
| 147 | std::lock_guard lock{mutex}; | 147 | std::lock_guard lock{mutex}; |
| 148 | const auto controller_iter = controller_list.find(identifier); | 148 | const auto controller_iter = controller_list.find(identifier); |
| 149 | if (controller_iter == controller_list.cend()) { | 149 | if (controller_iter == controller_list.cend()) { |
| 150 | LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.Format(), | 150 | LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(), |
| 151 | identifier.pad, identifier.port); | 151 | identifier.pad, identifier.port); |
| 152 | return BatteryLevel::Charging; | 152 | return Common::Input::BatteryLevel::Charging; |
| 153 | } | 153 | } |
| 154 | const ControllerData& controller = controller_iter->second; | 154 | const ControllerData& controller = controller_iter->second; |
| 155 | return controller.battery; | 155 | return controller.battery; |
| @@ -159,7 +159,7 @@ BasicMotion InputEngine::GetMotion(const PadIdentifier& identifier, int motion) | |||
| 159 | std::lock_guard lock{mutex}; | 159 | std::lock_guard lock{mutex}; |
| 160 | const auto controller_iter = controller_list.find(identifier); | 160 | const auto controller_iter = controller_list.find(identifier); |
| 161 | if (controller_iter == controller_list.cend()) { | 161 | if (controller_iter == controller_list.cend()) { |
| 162 | LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.Format(), | 162 | LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(), |
| 163 | identifier.pad, identifier.port); | 163 | identifier.pad, identifier.port); |
| 164 | return {}; | 164 | return {}; |
| 165 | } | 165 | } |
| @@ -270,7 +270,7 @@ void InputEngine::TriggerOnAxisChange(const PadIdentifier& identifier, int axis, | |||
| 270 | } | 270 | } |
| 271 | 271 | ||
| 272 | void InputEngine::TriggerOnBatteryChange(const PadIdentifier& identifier, | 272 | void InputEngine::TriggerOnBatteryChange(const PadIdentifier& identifier, |
| 273 | [[maybe_unused]] BatteryLevel value) { | 273 | [[maybe_unused]] Common::Input::BatteryLevel value) { |
| 274 | std::lock_guard lock{mutex_callback}; | 274 | std::lock_guard lock{mutex_callback}; |
| 275 | for (const auto& poller_pair : callback_list) { | 275 | for (const auto& poller_pair : callback_list) { |
| 276 | const InputIdentifier& poller = poller_pair.second; | 276 | const InputIdentifier& poller = poller_pair.second; |
diff --git a/src/input_common/input_engine.h b/src/input_common/input_engine.h index fe2faee5a..f44e0799b 100644 --- a/src/input_common/input_engine.h +++ b/src/input_common/input_engine.h | |||
| @@ -16,7 +16,7 @@ | |||
| 16 | 16 | ||
| 17 | // Pad Identifier of data source | 17 | // Pad Identifier of data source |
| 18 | struct PadIdentifier { | 18 | struct PadIdentifier { |
| 19 | Common::UUID guid{Common::INVALID_UUID}; | 19 | Common::UUID guid{}; |
| 20 | std::size_t port{}; | 20 | std::size_t port{}; |
| 21 | std::size_t pad{}; | 21 | std::size_t pad{}; |
| 22 | 22 | ||
| @@ -34,16 +34,6 @@ struct BasicMotion { | |||
| 34 | u64 delta_timestamp{}; | 34 | u64 delta_timestamp{}; |
| 35 | }; | 35 | }; |
| 36 | 36 | ||
| 37 | // Stages of a battery charge | ||
| 38 | enum class BatteryLevel { | ||
| 39 | Empty, | ||
| 40 | Critical, | ||
| 41 | Low, | ||
| 42 | Medium, | ||
| 43 | Full, | ||
| 44 | Charging, | ||
| 45 | }; | ||
| 46 | |||
| 47 | // Types of input that are stored in the engine | 37 | // Types of input that are stored in the engine |
| 48 | enum class EngineInputType { | 38 | enum class EngineInputType { |
| 49 | None, | 39 | None, |
| @@ -59,7 +49,7 @@ namespace std { | |||
| 59 | template <> | 49 | template <> |
| 60 | struct hash<PadIdentifier> { | 50 | struct hash<PadIdentifier> { |
| 61 | size_t operator()(const PadIdentifier& pad_id) const noexcept { | 51 | size_t operator()(const PadIdentifier& pad_id) const noexcept { |
| 62 | u64 hash_value = pad_id.guid.uuid[1] ^ pad_id.guid.uuid[0]; | 52 | u64 hash_value = pad_id.guid.Hash(); |
| 63 | hash_value ^= (static_cast<u64>(pad_id.port) << 32); | 53 | hash_value ^= (static_cast<u64>(pad_id.port) << 32); |
| 64 | hash_value ^= static_cast<u64>(pad_id.pad); | 54 | hash_value ^= static_cast<u64>(pad_id.pad); |
| 65 | return static_cast<size_t>(hash_value); | 55 | return static_cast<size_t>(hash_value); |
| @@ -178,7 +168,7 @@ public: | |||
| 178 | bool GetButton(const PadIdentifier& identifier, int button) const; | 168 | bool GetButton(const PadIdentifier& identifier, int button) const; |
| 179 | bool GetHatButton(const PadIdentifier& identifier, int button, u8 direction) const; | 169 | bool GetHatButton(const PadIdentifier& identifier, int button, u8 direction) const; |
| 180 | f32 GetAxis(const PadIdentifier& identifier, int axis) const; | 170 | f32 GetAxis(const PadIdentifier& identifier, int axis) const; |
| 181 | BatteryLevel GetBattery(const PadIdentifier& identifier) const; | 171 | Common::Input::BatteryLevel GetBattery(const PadIdentifier& identifier) const; |
| 182 | BasicMotion GetMotion(const PadIdentifier& identifier, int motion) const; | 172 | BasicMotion GetMotion(const PadIdentifier& identifier, int motion) const; |
| 183 | 173 | ||
| 184 | int SetCallback(InputIdentifier input_identifier); | 174 | int SetCallback(InputIdentifier input_identifier); |
| @@ -189,7 +179,7 @@ protected: | |||
| 189 | void SetButton(const PadIdentifier& identifier, int button, bool value); | 179 | void SetButton(const PadIdentifier& identifier, int button, bool value); |
| 190 | void SetHatButton(const PadIdentifier& identifier, int button, u8 value); | 180 | void SetHatButton(const PadIdentifier& identifier, int button, u8 value); |
| 191 | void SetAxis(const PadIdentifier& identifier, int axis, f32 value); | 181 | void SetAxis(const PadIdentifier& identifier, int axis, f32 value); |
| 192 | void SetBattery(const PadIdentifier& identifier, BatteryLevel value); | 182 | void SetBattery(const PadIdentifier& identifier, Common::Input::BatteryLevel value); |
| 193 | void SetMotion(const PadIdentifier& identifier, int motion, const BasicMotion& value); | 183 | void SetMotion(const PadIdentifier& identifier, int motion, const BasicMotion& value); |
| 194 | 184 | ||
| 195 | virtual std::string GetHatButtonName([[maybe_unused]] u8 direction_value) const { | 185 | virtual std::string GetHatButtonName([[maybe_unused]] u8 direction_value) const { |
| @@ -202,13 +192,13 @@ private: | |||
| 202 | std::unordered_map<int, u8> hat_buttons; | 192 | std::unordered_map<int, u8> hat_buttons; |
| 203 | std::unordered_map<int, float> axes; | 193 | std::unordered_map<int, float> axes; |
| 204 | std::unordered_map<int, BasicMotion> motions; | 194 | std::unordered_map<int, BasicMotion> motions; |
| 205 | BatteryLevel battery{}; | 195 | Common::Input::BatteryLevel battery{}; |
| 206 | }; | 196 | }; |
| 207 | 197 | ||
| 208 | void TriggerOnButtonChange(const PadIdentifier& identifier, int button, bool value); | 198 | void TriggerOnButtonChange(const PadIdentifier& identifier, int button, bool value); |
| 209 | void TriggerOnHatButtonChange(const PadIdentifier& identifier, int button, u8 value); | 199 | void TriggerOnHatButtonChange(const PadIdentifier& identifier, int button, u8 value); |
| 210 | void TriggerOnAxisChange(const PadIdentifier& identifier, int axis, f32 value); | 200 | void TriggerOnAxisChange(const PadIdentifier& identifier, int axis, f32 value); |
| 211 | void TriggerOnBatteryChange(const PadIdentifier& identifier, BatteryLevel value); | 201 | void TriggerOnBatteryChange(const PadIdentifier& identifier, Common::Input::BatteryLevel value); |
| 212 | void TriggerOnMotionChange(const PadIdentifier& identifier, int motion, | 202 | void TriggerOnMotionChange(const PadIdentifier& identifier, int motion, |
| 213 | const BasicMotion& value); | 203 | const BasicMotion& value); |
| 214 | 204 | ||
diff --git a/src/input_common/input_mapping.cpp b/src/input_common/input_mapping.cpp index a7a6ad8c2..fb78093b8 100644 --- a/src/input_common/input_mapping.cpp +++ b/src/input_common/input_mapping.cpp | |||
| @@ -57,7 +57,7 @@ void MappingFactory::RegisterButton(const MappingData& data) { | |||
| 57 | Common::ParamPackage new_input; | 57 | Common::ParamPackage new_input; |
| 58 | new_input.Set("engine", data.engine); | 58 | new_input.Set("engine", data.engine); |
| 59 | if (data.pad.guid.IsValid()) { | 59 | if (data.pad.guid.IsValid()) { |
| 60 | new_input.Set("guid", data.pad.guid.Format()); | 60 | new_input.Set("guid", data.pad.guid.RawString()); |
| 61 | } | 61 | } |
| 62 | new_input.Set("port", static_cast<int>(data.pad.port)); | 62 | new_input.Set("port", static_cast<int>(data.pad.port)); |
| 63 | new_input.Set("pad", static_cast<int>(data.pad.pad)); | 63 | new_input.Set("pad", static_cast<int>(data.pad.pad)); |
| @@ -93,7 +93,7 @@ void MappingFactory::RegisterStick(const MappingData& data) { | |||
| 93 | Common::ParamPackage new_input; | 93 | Common::ParamPackage new_input; |
| 94 | new_input.Set("engine", data.engine); | 94 | new_input.Set("engine", data.engine); |
| 95 | if (data.pad.guid.IsValid()) { | 95 | if (data.pad.guid.IsValid()) { |
| 96 | new_input.Set("guid", data.pad.guid.Format()); | 96 | new_input.Set("guid", data.pad.guid.RawString()); |
| 97 | } | 97 | } |
| 98 | new_input.Set("port", static_cast<int>(data.pad.port)); | 98 | new_input.Set("port", static_cast<int>(data.pad.port)); |
| 99 | new_input.Set("pad", static_cast<int>(data.pad.pad)); | 99 | new_input.Set("pad", static_cast<int>(data.pad.pad)); |
| @@ -138,7 +138,7 @@ void MappingFactory::RegisterMotion(const MappingData& data) { | |||
| 138 | Common::ParamPackage new_input; | 138 | Common::ParamPackage new_input; |
| 139 | new_input.Set("engine", data.engine); | 139 | new_input.Set("engine", data.engine); |
| 140 | if (data.pad.guid.IsValid()) { | 140 | if (data.pad.guid.IsValid()) { |
| 141 | new_input.Set("guid", data.pad.guid.Format()); | 141 | new_input.Set("guid", data.pad.guid.RawString()); |
| 142 | } | 142 | } |
| 143 | new_input.Set("port", static_cast<int>(data.pad.port)); | 143 | new_input.Set("port", static_cast<int>(data.pad.port)); |
| 144 | new_input.Set("pad", static_cast<int>(data.pad.pad)); | 144 | new_input.Set("pad", static_cast<int>(data.pad.pad)); |
diff --git a/src/input_common/input_poller.cpp b/src/input_common/input_poller.cpp index 2f3c0735a..82b585ff2 100644 --- a/src/input_common/input_poller.cpp +++ b/src/input_common/input_poller.cpp | |||
| @@ -181,7 +181,7 @@ public: | |||
| 181 | .raw_value = input_engine->GetAxis(identifier, axis_y), | 181 | .raw_value = input_engine->GetAxis(identifier, axis_y), |
| 182 | .properties = properties_y, | 182 | .properties = properties_y, |
| 183 | }; | 183 | }; |
| 184 | // This is a workaround too keep compatibility with old yuzu versions. Vertical axis is | 184 | // This is a workaround to keep compatibility with old yuzu versions. Vertical axis is |
| 185 | // inverted on SDL compared to Nintendo | 185 | // inverted on SDL compared to Nintendo |
| 186 | if (invert_axis_y) { | 186 | if (invert_axis_y) { |
| 187 | status.y.raw_value = -status.y.raw_value; | 187 | status.y.raw_value = -status.y.raw_value; |
| @@ -470,7 +470,7 @@ public: | |||
| 470 | } | 470 | } |
| 471 | 471 | ||
| 472 | Common::Input::BatteryStatus GetStatus() const { | 472 | Common::Input::BatteryStatus GetStatus() const { |
| 473 | return static_cast<Common::Input::BatteryLevel>(input_engine->GetBattery(identifier)); | 473 | return input_engine->GetBattery(identifier); |
| 474 | } | 474 | } |
| 475 | 475 | ||
| 476 | void ForceUpdate() override { | 476 | void ForceUpdate() override { |
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_instructions.h b/src/shader_recompiler/backend/glasm/emit_glasm_instructions.h index b48007856..5efbe4e6f 100644 --- a/src/shader_recompiler/backend/glasm/emit_glasm_instructions.h +++ b/src/shader_recompiler/backend/glasm/emit_glasm_instructions.h | |||
| @@ -372,6 +372,8 @@ void EmitSharedAtomicExchange32(EmitContext& ctx, IR::Inst& inst, ScalarU32 poin | |||
| 372 | ScalarU32 value); | 372 | ScalarU32 value); |
| 373 | void EmitSharedAtomicExchange64(EmitContext& ctx, IR::Inst& inst, ScalarU32 pointer_offset, | 373 | void EmitSharedAtomicExchange64(EmitContext& ctx, IR::Inst& inst, ScalarU32 pointer_offset, |
| 374 | Register value); | 374 | Register value); |
| 375 | void EmitSharedAtomicExchange32x2(EmitContext& ctx, IR::Inst& inst, ScalarU32 pointer_offset, | ||
| 376 | Register value); | ||
| 375 | void EmitStorageAtomicIAdd32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | 377 | void EmitStorageAtomicIAdd32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, |
| 376 | ScalarU32 offset, ScalarU32 value); | 378 | ScalarU32 offset, ScalarU32 value); |
| 377 | void EmitStorageAtomicSMin32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | 379 | void EmitStorageAtomicSMin32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, |
| @@ -412,6 +414,24 @@ void EmitStorageAtomicXor64(EmitContext& ctx, IR::Inst& inst, const IR::Value& b | |||
| 412 | ScalarU32 offset, Register value); | 414 | ScalarU32 offset, Register value); |
| 413 | void EmitStorageAtomicExchange64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | 415 | void EmitStorageAtomicExchange64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, |
| 414 | ScalarU32 offset, Register value); | 416 | ScalarU32 offset, Register value); |
| 417 | void EmitStorageAtomicIAdd32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | ||
| 418 | ScalarU32 offset, Register value); | ||
| 419 | void EmitStorageAtomicSMin32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | ||
| 420 | ScalarU32 offset, Register value); | ||
| 421 | void EmitStorageAtomicUMin32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | ||
| 422 | ScalarU32 offset, Register value); | ||
| 423 | void EmitStorageAtomicSMax32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | ||
| 424 | ScalarU32 offset, Register value); | ||
| 425 | void EmitStorageAtomicUMax32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | ||
| 426 | ScalarU32 offset, Register value); | ||
| 427 | void EmitStorageAtomicAnd32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | ||
| 428 | ScalarU32 offset, Register value); | ||
| 429 | void EmitStorageAtomicOr32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | ||
| 430 | ScalarU32 offset, Register value); | ||
| 431 | void EmitStorageAtomicXor32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | ||
| 432 | ScalarU32 offset, Register value); | ||
| 433 | void EmitStorageAtomicExchange32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | ||
| 434 | ScalarU32 offset, Register value); | ||
| 415 | void EmitStorageAtomicAddF32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | 435 | void EmitStorageAtomicAddF32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, |
| 416 | ScalarU32 offset, ScalarF32 value); | 436 | ScalarU32 offset, ScalarF32 value); |
| 417 | void EmitStorageAtomicAddF16x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | 437 | void EmitStorageAtomicAddF16x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, |
| @@ -448,6 +468,17 @@ void EmitGlobalAtomicAnd64(EmitContext& ctx); | |||
| 448 | void EmitGlobalAtomicOr64(EmitContext& ctx); | 468 | void EmitGlobalAtomicOr64(EmitContext& ctx); |
| 449 | void EmitGlobalAtomicXor64(EmitContext& ctx); | 469 | void EmitGlobalAtomicXor64(EmitContext& ctx); |
| 450 | void EmitGlobalAtomicExchange64(EmitContext& ctx); | 470 | void EmitGlobalAtomicExchange64(EmitContext& ctx); |
| 471 | void EmitGlobalAtomicIAdd32x2(EmitContext& ctx); | ||
| 472 | void EmitGlobalAtomicSMin32x2(EmitContext& ctx); | ||
| 473 | void EmitGlobalAtomicUMin32x2(EmitContext& ctx); | ||
| 474 | void EmitGlobalAtomicSMax32x2(EmitContext& ctx); | ||
| 475 | void EmitGlobalAtomicUMax32x2(EmitContext& ctx); | ||
| 476 | void EmitGlobalAtomicInc32x2(EmitContext& ctx); | ||
| 477 | void EmitGlobalAtomicDec32x2(EmitContext& ctx); | ||
| 478 | void EmitGlobalAtomicAnd32x2(EmitContext& ctx); | ||
| 479 | void EmitGlobalAtomicOr32x2(EmitContext& ctx); | ||
| 480 | void EmitGlobalAtomicXor32x2(EmitContext& ctx); | ||
| 481 | void EmitGlobalAtomicExchange32x2(EmitContext& ctx); | ||
| 451 | void EmitGlobalAtomicAddF32(EmitContext& ctx); | 482 | void EmitGlobalAtomicAddF32(EmitContext& ctx); |
| 452 | void EmitGlobalAtomicAddF16x2(EmitContext& ctx); | 483 | void EmitGlobalAtomicAddF16x2(EmitContext& ctx); |
| 453 | void EmitGlobalAtomicAddF32x2(EmitContext& ctx); | 484 | void EmitGlobalAtomicAddF32x2(EmitContext& ctx); |
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_memory.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_memory.cpp index f135b67f5..f0fd94a28 100644 --- a/src/shader_recompiler/backend/glasm/emit_glasm_memory.cpp +++ b/src/shader_recompiler/backend/glasm/emit_glasm_memory.cpp | |||
| @@ -311,6 +311,13 @@ void EmitSharedAtomicExchange64(EmitContext& ctx, IR::Inst& inst, ScalarU32 poin | |||
| 311 | ctx.LongAdd("ATOMS.EXCH.U64 {}.x,{},shared_mem[{}];", inst, value, pointer_offset); | 311 | ctx.LongAdd("ATOMS.EXCH.U64 {}.x,{},shared_mem[{}];", inst, value, pointer_offset); |
| 312 | } | 312 | } |
| 313 | 313 | ||
| 314 | void EmitSharedAtomicExchange32x2([[maybe_unused]] EmitContext& ctx, | ||
| 315 | [[maybe_unused]] IR::Inst& inst, | ||
| 316 | [[maybe_unused]] ScalarU32 pointer_offset, | ||
| 317 | [[maybe_unused]] Register value) { | ||
| 318 | throw NotImplementedException("GLASM instruction"); | ||
| 319 | } | ||
| 320 | |||
| 314 | void EmitStorageAtomicIAdd32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | 321 | void EmitStorageAtomicIAdd32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, |
| 315 | ScalarU32 offset, ScalarU32 value) { | 322 | ScalarU32 offset, ScalarU32 value) { |
| 316 | Atom(ctx, inst, binding, offset, value, "ADD", "U32"); | 323 | Atom(ctx, inst, binding, offset, value, "ADD", "U32"); |
| @@ -411,6 +418,62 @@ void EmitStorageAtomicExchange64(EmitContext& ctx, IR::Inst& inst, const IR::Val | |||
| 411 | Atom(ctx, inst, binding, offset, value, "EXCH", "U64"); | 418 | Atom(ctx, inst, binding, offset, value, "EXCH", "U64"); |
| 412 | } | 419 | } |
| 413 | 420 | ||
| 421 | void EmitStorageAtomicIAdd32x2([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst, | ||
| 422 | [[maybe_unused]] const IR::Value& binding, | ||
| 423 | [[maybe_unused]] ScalarU32 offset, [[maybe_unused]] Register value) { | ||
| 424 | throw NotImplementedException("GLASM instruction"); | ||
| 425 | } | ||
| 426 | |||
| 427 | void EmitStorageAtomicSMin32x2([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst, | ||
| 428 | [[maybe_unused]] const IR::Value& binding, | ||
| 429 | [[maybe_unused]] ScalarU32 offset, [[maybe_unused]] Register value) { | ||
| 430 | throw NotImplementedException("GLASM instruction"); | ||
| 431 | } | ||
| 432 | |||
| 433 | void EmitStorageAtomicUMin32x2([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst, | ||
| 434 | [[maybe_unused]] const IR::Value& binding, | ||
| 435 | [[maybe_unused]] ScalarU32 offset, [[maybe_unused]] Register value) { | ||
| 436 | throw NotImplementedException("GLASM instruction"); | ||
| 437 | } | ||
| 438 | |||
| 439 | void EmitStorageAtomicSMax32x2([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst, | ||
| 440 | [[maybe_unused]] const IR::Value& binding, | ||
| 441 | [[maybe_unused]] ScalarU32 offset, [[maybe_unused]] Register value) { | ||
| 442 | throw NotImplementedException("GLASM instruction"); | ||
| 443 | } | ||
| 444 | |||
| 445 | void EmitStorageAtomicUMax32x2([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst, | ||
| 446 | [[maybe_unused]] const IR::Value& binding, | ||
| 447 | [[maybe_unused]] ScalarU32 offset, [[maybe_unused]] Register value) { | ||
| 448 | throw NotImplementedException("GLASM instruction"); | ||
| 449 | } | ||
| 450 | |||
| 451 | void EmitStorageAtomicAnd32x2([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst, | ||
| 452 | [[maybe_unused]] const IR::Value& binding, | ||
| 453 | [[maybe_unused]] ScalarU32 offset, [[maybe_unused]] Register value) { | ||
| 454 | throw NotImplementedException("GLASM instruction"); | ||
| 455 | } | ||
| 456 | |||
| 457 | void EmitStorageAtomicOr32x2([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst, | ||
| 458 | [[maybe_unused]] const IR::Value& binding, | ||
| 459 | [[maybe_unused]] ScalarU32 offset, [[maybe_unused]] Register value) { | ||
| 460 | throw NotImplementedException("GLASM instruction"); | ||
| 461 | } | ||
| 462 | |||
| 463 | void EmitStorageAtomicXor32x2([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst, | ||
| 464 | [[maybe_unused]] const IR::Value& binding, | ||
| 465 | [[maybe_unused]] ScalarU32 offset, [[maybe_unused]] Register value) { | ||
| 466 | throw NotImplementedException("GLASM instruction"); | ||
| 467 | } | ||
| 468 | |||
| 469 | void EmitStorageAtomicExchange32x2([[maybe_unused]] EmitContext& ctx, | ||
| 470 | [[maybe_unused]] IR::Inst& inst, | ||
| 471 | [[maybe_unused]] const IR::Value& binding, | ||
| 472 | [[maybe_unused]] ScalarU32 offset, | ||
| 473 | [[maybe_unused]] Register value) { | ||
| 474 | throw NotImplementedException("GLASM instruction"); | ||
| 475 | } | ||
| 476 | |||
| 414 | void EmitStorageAtomicAddF32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | 477 | void EmitStorageAtomicAddF32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, |
| 415 | ScalarU32 offset, ScalarF32 value) { | 478 | ScalarU32 offset, ScalarF32 value) { |
| 416 | Atom(ctx, inst, binding, offset, value, "ADD", "F32"); | 479 | Atom(ctx, inst, binding, offset, value, "ADD", "F32"); |
| @@ -537,6 +600,50 @@ void EmitGlobalAtomicExchange64(EmitContext&) { | |||
| 537 | throw NotImplementedException("GLASM instruction"); | 600 | throw NotImplementedException("GLASM instruction"); |
| 538 | } | 601 | } |
| 539 | 602 | ||
| 603 | void EmitGlobalAtomicIAdd32x2(EmitContext&) { | ||
| 604 | throw NotImplementedException("GLASM instruction"); | ||
| 605 | } | ||
| 606 | |||
| 607 | void EmitGlobalAtomicSMin32x2(EmitContext&) { | ||
| 608 | throw NotImplementedException("GLASM instruction"); | ||
| 609 | } | ||
| 610 | |||
| 611 | void EmitGlobalAtomicUMin32x2(EmitContext&) { | ||
| 612 | throw NotImplementedException("GLASM instruction"); | ||
| 613 | } | ||
| 614 | |||
| 615 | void EmitGlobalAtomicSMax32x2(EmitContext&) { | ||
| 616 | throw NotImplementedException("GLASM instruction"); | ||
| 617 | } | ||
| 618 | |||
| 619 | void EmitGlobalAtomicUMax32x2(EmitContext&) { | ||
| 620 | throw NotImplementedException("GLASM instruction"); | ||
| 621 | } | ||
| 622 | |||
| 623 | void EmitGlobalAtomicInc32x2(EmitContext&) { | ||
| 624 | throw NotImplementedException("GLASM instruction"); | ||
| 625 | } | ||
| 626 | |||
| 627 | void EmitGlobalAtomicDec32x2(EmitContext&) { | ||
| 628 | throw NotImplementedException("GLASM instruction"); | ||
| 629 | } | ||
| 630 | |||
| 631 | void EmitGlobalAtomicAnd32x2(EmitContext&) { | ||
| 632 | throw NotImplementedException("GLASM instruction"); | ||
| 633 | } | ||
| 634 | |||
| 635 | void EmitGlobalAtomicOr32x2(EmitContext&) { | ||
| 636 | throw NotImplementedException("GLASM instruction"); | ||
| 637 | } | ||
| 638 | |||
| 639 | void EmitGlobalAtomicXor32x2(EmitContext&) { | ||
| 640 | throw NotImplementedException("GLASM instruction"); | ||
| 641 | } | ||
| 642 | |||
| 643 | void EmitGlobalAtomicExchange32x2(EmitContext&) { | ||
| 644 | throw NotImplementedException("GLASM instruction"); | ||
| 645 | } | ||
| 646 | |||
| 540 | void EmitGlobalAtomicAddF32(EmitContext&) { | 647 | void EmitGlobalAtomicAddF32(EmitContext&) { |
| 541 | throw NotImplementedException("GLASM instruction"); | 648 | throw NotImplementedException("GLASM instruction"); |
| 542 | } | 649 | } |
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_atomic.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_atomic.cpp index dc377b053..a409a7ab3 100644 --- a/src/shader_recompiler/backend/glsl/emit_glsl_atomic.cpp +++ b/src/shader_recompiler/backend/glsl/emit_glsl_atomic.cpp | |||
| @@ -105,6 +105,13 @@ void EmitSharedAtomicExchange64(EmitContext& ctx, IR::Inst& inst, std::string_vi | |||
| 105 | pointer_offset, value, pointer_offset, value); | 105 | pointer_offset, value, pointer_offset, value); |
| 106 | } | 106 | } |
| 107 | 107 | ||
| 108 | void EmitSharedAtomicExchange32x2(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset, | ||
| 109 | std::string_view value) { | ||
| 110 | LOG_WARNING(Shader_GLSL, "Int64 atomics not supported, fallback to non-atomic"); | ||
| 111 | ctx.AddU32x2("{}=uvec2(smem[{}>>2],smem[({}+4)>>2]);", inst, pointer_offset, pointer_offset); | ||
| 112 | ctx.Add("smem[{}>>2]={}.x;smem[({}+4)>>2]={}.y;", pointer_offset, value, pointer_offset, value); | ||
| 113 | } | ||
| 114 | |||
| 108 | void EmitStorageAtomicIAdd32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | 115 | void EmitStorageAtomicIAdd32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, |
| 109 | const IR::Value& offset, std::string_view value) { | 116 | const IR::Value& offset, std::string_view value) { |
| 110 | ctx.AddU32("{}=atomicAdd({}_ssbo{}[{}>>2],{});", inst, ctx.stage_name, binding.U32(), | 117 | ctx.AddU32("{}=atomicAdd({}_ssbo{}[{}>>2],{});", inst, ctx.stage_name, binding.U32(), |
| @@ -265,6 +272,97 @@ void EmitStorageAtomicExchange64(EmitContext& ctx, IR::Inst& inst, const IR::Val | |||
| 265 | ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), value); | 272 | ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), value); |
| 266 | } | 273 | } |
| 267 | 274 | ||
| 275 | void EmitStorageAtomicIAdd32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | ||
| 276 | const IR::Value& offset, std::string_view value) { | ||
| 277 | LOG_WARNING(Shader_GLSL, "Int64 atomics not supported, fallback to non-atomic"); | ||
| 278 | ctx.AddU32x2("{}=uvec2({}_ssbo{}[{}>>2],{}_ssbo{}[({}>>2)+1]);", inst, ctx.stage_name, | ||
| 279 | binding.U32(), ctx.var_alloc.Consume(offset), ctx.stage_name, binding.U32(), | ||
| 280 | ctx.var_alloc.Consume(offset)); | ||
| 281 | ctx.Add("{}_ssbo{}[{}>>2]+={}.x;{}_ssbo{}[({}>>2)+1]+={}.y;", ctx.stage_name, binding.U32(), | ||
| 282 | ctx.var_alloc.Consume(offset), value, ctx.stage_name, binding.U32(), | ||
| 283 | ctx.var_alloc.Consume(offset), value); | ||
| 284 | } | ||
| 285 | |||
| 286 | void EmitStorageAtomicSMin32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | ||
| 287 | const IR::Value& offset, std::string_view value) { | ||
| 288 | LOG_WARNING(Shader_GLSL, "Int64 atomics not supported, fallback to non-atomic"); | ||
| 289 | ctx.AddU32x2("{}=ivec2({}_ssbo{}[{}>>2],{}_ssbo{}[({}>>2)+1]);", inst, ctx.stage_name, | ||
| 290 | binding.U32(), ctx.var_alloc.Consume(offset), ctx.stage_name, binding.U32(), | ||
| 291 | ctx.var_alloc.Consume(offset)); | ||
| 292 | ctx.Add("for(int " | ||
| 293 | "i=0;i<2;++i){{{}_ssbo{}[({}>>2)+i]=uint(min(int({}_ssbo{}[({}>>2)+i]),int({}[i])));}}", | ||
| 294 | ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), ctx.stage_name, | ||
| 295 | binding.U32(), ctx.var_alloc.Consume(offset), value); | ||
| 296 | } | ||
| 297 | |||
| 298 | void EmitStorageAtomicUMin32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | ||
| 299 | const IR::Value& offset, std::string_view value) { | ||
| 300 | LOG_WARNING(Shader_GLSL, "Int64 atomics not supported, fallback to non-atomic"); | ||
| 301 | ctx.AddU32x2("{}=uvec2({}_ssbo{}[{}>>2],{}_ssbo{}[({}>>2)+1]);", inst, ctx.stage_name, | ||
| 302 | binding.U32(), ctx.var_alloc.Consume(offset), ctx.stage_name, binding.U32(), | ||
| 303 | ctx.var_alloc.Consume(offset)); | ||
| 304 | ctx.Add("for(int i=0;i<2;++i){{ " | ||
| 305 | "{}_ssbo{}[({}>>2)+i]=min({}_ssbo{}[({}>>2)+i],{}[i]);}}", | ||
| 306 | ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), ctx.stage_name, | ||
| 307 | binding.U32(), ctx.var_alloc.Consume(offset), value); | ||
| 308 | } | ||
| 309 | |||
| 310 | void EmitStorageAtomicSMax32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | ||
| 311 | const IR::Value& offset, std::string_view value) { | ||
| 312 | LOG_WARNING(Shader_GLSL, "Int64 atomics not supported, fallback to non-atomic"); | ||
| 313 | ctx.AddU32x2("{}=ivec2({}_ssbo{}[{}>>2],{}_ssbo{}[({}>>2)+1]);", inst, ctx.stage_name, | ||
| 314 | binding.U32(), ctx.var_alloc.Consume(offset), ctx.stage_name, binding.U32(), | ||
| 315 | ctx.var_alloc.Consume(offset)); | ||
| 316 | ctx.Add("for(int " | ||
| 317 | "i=0;i<2;++i){{{}_ssbo{}[({}>>2)+i]=uint(max(int({}_ssbo{}[({}>>2)+i]),int({}[i])));}}", | ||
| 318 | ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), ctx.stage_name, | ||
| 319 | binding.U32(), ctx.var_alloc.Consume(offset), value); | ||
| 320 | } | ||
| 321 | |||
| 322 | void EmitStorageAtomicUMax32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | ||
| 323 | const IR::Value& offset, std::string_view value) { | ||
| 324 | LOG_WARNING(Shader_GLSL, "Int64 atomics not supported, fallback to non-atomic"); | ||
| 325 | ctx.AddU32x2("{}=uvec2({}_ssbo{}[{}>>2],{}_ssbo{}[({}>>2)+1]);", inst, ctx.stage_name, | ||
| 326 | binding.U32(), ctx.var_alloc.Consume(offset), ctx.stage_name, binding.U32(), | ||
| 327 | ctx.var_alloc.Consume(offset)); | ||
| 328 | ctx.Add("for(int i=0;i<2;++i){{{}_ssbo{}[({}>>2)+i]=max({}_ssbo{}[({}>>2)+i],{}[i]);}}", | ||
| 329 | ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), ctx.stage_name, | ||
| 330 | binding.U32(), ctx.var_alloc.Consume(offset), value); | ||
| 331 | } | ||
| 332 | |||
| 333 | void EmitStorageAtomicAnd32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | ||
| 334 | const IR::Value& offset, std::string_view value) { | ||
| 335 | LOG_WARNING(Shader_GLSL, "Int64 atomics not supported, fallback to 32x2"); | ||
| 336 | ctx.AddU32x2("{}=uvec2(atomicAnd({}_ssbo{}[{}>>2],{}.x),atomicAnd({}_ssbo{}[({}>>2)+1],{}.y));", | ||
| 337 | inst, ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), value, | ||
| 338 | ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), value); | ||
| 339 | } | ||
| 340 | |||
| 341 | void EmitStorageAtomicOr32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | ||
| 342 | const IR::Value& offset, std::string_view value) { | ||
| 343 | LOG_WARNING(Shader_GLSL, "Int64 atomics not supported, fallback to 32x2"); | ||
| 344 | ctx.AddU32x2("{}=uvec2(atomicOr({}_ssbo{}[{}>>2],{}.x),atomicOr({}_ssbo{}[({}>>2)+1],{}.y));", | ||
| 345 | inst, ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), value, | ||
| 346 | ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), value); | ||
| 347 | } | ||
| 348 | |||
| 349 | void EmitStorageAtomicXor32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | ||
| 350 | const IR::Value& offset, std::string_view value) { | ||
| 351 | LOG_WARNING(Shader_GLSL, "Int64 atomics not supported, fallback to 32x2"); | ||
| 352 | ctx.AddU32x2("{}=uvec2(atomicXor({}_ssbo{}[{}>>2],{}.x),atomicXor({}_ssbo{}[({}>>2)+1],{}.y));", | ||
| 353 | inst, ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), value, | ||
| 354 | ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), value); | ||
| 355 | } | ||
| 356 | |||
| 357 | void EmitStorageAtomicExchange32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | ||
| 358 | const IR::Value& offset, std::string_view value) { | ||
| 359 | LOG_WARNING(Shader_GLSL, "Int64 atomics not supported, fallback to 32x2"); | ||
| 360 | ctx.AddU32x2("{}=uvec2(atomicExchange({}_ssbo{}[{}>>2],{}.x),atomicExchange({}_ssbo{}[({}>>2)+" | ||
| 361 | "1],{}.y));", | ||
| 362 | inst, ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), value, | ||
| 363 | ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), value); | ||
| 364 | } | ||
| 365 | |||
| 268 | void EmitStorageAtomicAddF32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | 366 | void EmitStorageAtomicAddF32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, |
| 269 | const IR::Value& offset, std::string_view value) { | 367 | const IR::Value& offset, std::string_view value) { |
| 270 | SsboCasFunctionF32(ctx, inst, binding, offset, value, "CasFloatAdd"); | 368 | SsboCasFunctionF32(ctx, inst, binding, offset, value, "CasFloatAdd"); |
| @@ -388,6 +486,50 @@ void EmitGlobalAtomicExchange64(EmitContext&) { | |||
| 388 | throw NotImplementedException("GLSL Instrucion"); | 486 | throw NotImplementedException("GLSL Instrucion"); |
| 389 | } | 487 | } |
| 390 | 488 | ||
| 489 | void EmitGlobalAtomicIAdd32x2(EmitContext&) { | ||
| 490 | throw NotImplementedException("GLSL Instrucion"); | ||
| 491 | } | ||
| 492 | |||
| 493 | void EmitGlobalAtomicSMin32x2(EmitContext&) { | ||
| 494 | throw NotImplementedException("GLSL Instrucion"); | ||
| 495 | } | ||
| 496 | |||
| 497 | void EmitGlobalAtomicUMin32x2(EmitContext&) { | ||
| 498 | throw NotImplementedException("GLSL Instrucion"); | ||
| 499 | } | ||
| 500 | |||
| 501 | void EmitGlobalAtomicSMax32x2(EmitContext&) { | ||
| 502 | throw NotImplementedException("GLSL Instrucion"); | ||
| 503 | } | ||
| 504 | |||
| 505 | void EmitGlobalAtomicUMax32x2(EmitContext&) { | ||
| 506 | throw NotImplementedException("GLSL Instrucion"); | ||
| 507 | } | ||
| 508 | |||
| 509 | void EmitGlobalAtomicInc32x2(EmitContext&) { | ||
| 510 | throw NotImplementedException("GLSL Instrucion"); | ||
| 511 | } | ||
| 512 | |||
| 513 | void EmitGlobalAtomicDec32x2(EmitContext&) { | ||
| 514 | throw NotImplementedException("GLSL Instrucion"); | ||
| 515 | } | ||
| 516 | |||
| 517 | void EmitGlobalAtomicAnd32x2(EmitContext&) { | ||
| 518 | throw NotImplementedException("GLSL Instrucion"); | ||
| 519 | } | ||
| 520 | |||
| 521 | void EmitGlobalAtomicOr32x2(EmitContext&) { | ||
| 522 | throw NotImplementedException("GLSL Instrucion"); | ||
| 523 | } | ||
| 524 | |||
| 525 | void EmitGlobalAtomicXor32x2(EmitContext&) { | ||
| 526 | throw NotImplementedException("GLSL Instrucion"); | ||
| 527 | } | ||
| 528 | |||
| 529 | void EmitGlobalAtomicExchange32x2(EmitContext&) { | ||
| 530 | throw NotImplementedException("GLSL Instrucion"); | ||
| 531 | } | ||
| 532 | |||
| 391 | void EmitGlobalAtomicAddF32(EmitContext&) { | 533 | void EmitGlobalAtomicAddF32(EmitContext&) { |
| 392 | throw NotImplementedException("GLSL Instrucion"); | 534 | throw NotImplementedException("GLSL Instrucion"); |
| 393 | } | 535 | } |
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_instructions.h b/src/shader_recompiler/backend/glsl/emit_glsl_instructions.h index 6cabbc717..704baddc9 100644 --- a/src/shader_recompiler/backend/glsl/emit_glsl_instructions.h +++ b/src/shader_recompiler/backend/glsl/emit_glsl_instructions.h | |||
| @@ -442,6 +442,8 @@ void EmitSharedAtomicExchange32(EmitContext& ctx, IR::Inst& inst, std::string_vi | |||
| 442 | std::string_view value); | 442 | std::string_view value); |
| 443 | void EmitSharedAtomicExchange64(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset, | 443 | void EmitSharedAtomicExchange64(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset, |
| 444 | std::string_view value); | 444 | std::string_view value); |
| 445 | void EmitSharedAtomicExchange32x2(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset, | ||
| 446 | std::string_view value); | ||
| 445 | void EmitStorageAtomicIAdd32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | 447 | void EmitStorageAtomicIAdd32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, |
| 446 | const IR::Value& offset, std::string_view value); | 448 | const IR::Value& offset, std::string_view value); |
| 447 | void EmitStorageAtomicSMin32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | 449 | void EmitStorageAtomicSMin32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, |
| @@ -482,6 +484,24 @@ void EmitStorageAtomicXor64(EmitContext& ctx, IR::Inst& inst, const IR::Value& b | |||
| 482 | const IR::Value& offset, std::string_view value); | 484 | const IR::Value& offset, std::string_view value); |
| 483 | void EmitStorageAtomicExchange64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | 485 | void EmitStorageAtomicExchange64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, |
| 484 | const IR::Value& offset, std::string_view value); | 486 | const IR::Value& offset, std::string_view value); |
| 487 | void EmitStorageAtomicIAdd32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | ||
| 488 | const IR::Value& offset, std::string_view value); | ||
| 489 | void EmitStorageAtomicSMin32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | ||
| 490 | const IR::Value& offset, std::string_view value); | ||
| 491 | void EmitStorageAtomicUMin32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | ||
| 492 | const IR::Value& offset, std::string_view value); | ||
| 493 | void EmitStorageAtomicSMax32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | ||
| 494 | const IR::Value& offset, std::string_view value); | ||
| 495 | void EmitStorageAtomicUMax32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | ||
| 496 | const IR::Value& offset, std::string_view value); | ||
| 497 | void EmitStorageAtomicAnd32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | ||
| 498 | const IR::Value& offset, std::string_view value); | ||
| 499 | void EmitStorageAtomicOr32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | ||
| 500 | const IR::Value& offset, std::string_view value); | ||
| 501 | void EmitStorageAtomicXor32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | ||
| 502 | const IR::Value& offset, std::string_view value); | ||
| 503 | void EmitStorageAtomicExchange32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | ||
| 504 | const IR::Value& offset, std::string_view value); | ||
| 485 | void EmitStorageAtomicAddF32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | 505 | void EmitStorageAtomicAddF32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, |
| 486 | const IR::Value& offset, std::string_view value); | 506 | const IR::Value& offset, std::string_view value); |
| 487 | void EmitStorageAtomicAddF16x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | 507 | void EmitStorageAtomicAddF16x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, |
| @@ -518,6 +538,17 @@ void EmitGlobalAtomicAnd64(EmitContext& ctx); | |||
| 518 | void EmitGlobalAtomicOr64(EmitContext& ctx); | 538 | void EmitGlobalAtomicOr64(EmitContext& ctx); |
| 519 | void EmitGlobalAtomicXor64(EmitContext& ctx); | 539 | void EmitGlobalAtomicXor64(EmitContext& ctx); |
| 520 | void EmitGlobalAtomicExchange64(EmitContext& ctx); | 540 | void EmitGlobalAtomicExchange64(EmitContext& ctx); |
| 541 | void EmitGlobalAtomicIAdd32x2(EmitContext& ctx); | ||
| 542 | void EmitGlobalAtomicSMin32x2(EmitContext& ctx); | ||
| 543 | void EmitGlobalAtomicUMin32x2(EmitContext& ctx); | ||
| 544 | void EmitGlobalAtomicSMax32x2(EmitContext& ctx); | ||
| 545 | void EmitGlobalAtomicUMax32x2(EmitContext& ctx); | ||
| 546 | void EmitGlobalAtomicInc32x2(EmitContext& ctx); | ||
| 547 | void EmitGlobalAtomicDec32x2(EmitContext& ctx); | ||
| 548 | void EmitGlobalAtomicAnd32x2(EmitContext& ctx); | ||
| 549 | void EmitGlobalAtomicOr32x2(EmitContext& ctx); | ||
| 550 | void EmitGlobalAtomicXor32x2(EmitContext& ctx); | ||
| 551 | void EmitGlobalAtomicExchange32x2(EmitContext& ctx); | ||
| 521 | void EmitGlobalAtomicAddF32(EmitContext& ctx); | 552 | void EmitGlobalAtomicAddF32(EmitContext& ctx); |
| 522 | void EmitGlobalAtomicAddF16x2(EmitContext& ctx); | 553 | void EmitGlobalAtomicAddF16x2(EmitContext& ctx); |
| 523 | void EmitGlobalAtomicAddF32x2(EmitContext& ctx); | 554 | void EmitGlobalAtomicAddF32x2(EmitContext& ctx); |
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.h b/src/shader_recompiler/backend/spirv/emit_spirv.h index b412957c7..2b360e073 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv.h +++ b/src/shader_recompiler/backend/spirv/emit_spirv.h | |||
| @@ -22,7 +22,7 @@ constexpr u32 NUM_TEXTURE_AND_IMAGE_SCALING_WORDS = | |||
| 22 | struct RescalingLayout { | 22 | struct RescalingLayout { |
| 23 | alignas(16) std::array<u32, NUM_TEXTURE_SCALING_WORDS> rescaling_textures; | 23 | alignas(16) std::array<u32, NUM_TEXTURE_SCALING_WORDS> rescaling_textures; |
| 24 | alignas(16) std::array<u32, NUM_IMAGE_SCALING_WORDS> rescaling_images; | 24 | alignas(16) std::array<u32, NUM_IMAGE_SCALING_WORDS> rescaling_images; |
| 25 | alignas(16) u32 down_factor; | 25 | u32 down_factor; |
| 26 | }; | 26 | }; |
| 27 | constexpr u32 RESCALING_LAYOUT_WORDS_OFFSET = offsetof(RescalingLayout, rescaling_textures); | 27 | constexpr u32 RESCALING_LAYOUT_WORDS_OFFSET = offsetof(RescalingLayout, rescaling_textures); |
| 28 | constexpr u32 RESCALING_LAYOUT_DOWN_FACTOR_OFFSET = offsetof(RescalingLayout, down_factor); | 28 | constexpr u32 RESCALING_LAYOUT_DOWN_FACTOR_OFFSET = offsetof(RescalingLayout, down_factor); |
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp index 46ba52a25..d3cbb14a9 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp | |||
| @@ -82,6 +82,17 @@ Id StorageAtomicU64(EmitContext& ctx, const IR::Value& binding, const IR::Value& | |||
| 82 | ctx.OpStore(pointer, ctx.OpBitcast(ctx.U32[2], result)); | 82 | ctx.OpStore(pointer, ctx.OpBitcast(ctx.U32[2], result)); |
| 83 | return original_value; | 83 | return original_value; |
| 84 | } | 84 | } |
| 85 | |||
| 86 | Id StorageAtomicU32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, Id value, | ||
| 87 | Id (Sirit::Module::*non_atomic_func)(Id, Id, Id)) { | ||
| 88 | LOG_WARNING(Shader_SPIRV, "Int64 atomics not supported, fallback to non-atomic"); | ||
| 89 | const Id pointer{StoragePointer(ctx, ctx.storage_types.U32x2, &StorageDefinitions::U32x2, | ||
| 90 | binding, offset, sizeof(u32[2]))}; | ||
| 91 | const Id original_value{ctx.OpLoad(ctx.U32[2], pointer)}; | ||
| 92 | const Id result{(ctx.*non_atomic_func)(ctx.U32[2], value, original_value)}; | ||
| 93 | ctx.OpStore(pointer, result); | ||
| 94 | return original_value; | ||
| 95 | } | ||
| 85 | } // Anonymous namespace | 96 | } // Anonymous namespace |
| 86 | 97 | ||
| 87 | Id EmitSharedAtomicIAdd32(EmitContext& ctx, Id offset, Id value) { | 98 | Id EmitSharedAtomicIAdd32(EmitContext& ctx, Id offset, Id value) { |
| @@ -141,7 +152,7 @@ Id EmitSharedAtomicExchange64(EmitContext& ctx, Id offset, Id value) { | |||
| 141 | const auto [scope, semantics]{AtomicArgs(ctx)}; | 152 | const auto [scope, semantics]{AtomicArgs(ctx)}; |
| 142 | return ctx.OpAtomicExchange(ctx.U64, pointer, scope, semantics, value); | 153 | return ctx.OpAtomicExchange(ctx.U64, pointer, scope, semantics, value); |
| 143 | } | 154 | } |
| 144 | LOG_ERROR(Shader_SPIRV, "Int64 atomics not supported, fallback to non-atomic"); | 155 | LOG_WARNING(Shader_SPIRV, "Int64 atomics not supported, fallback to non-atomic"); |
| 145 | const Id pointer_1{SharedPointer(ctx, offset, 0)}; | 156 | const Id pointer_1{SharedPointer(ctx, offset, 0)}; |
| 146 | const Id pointer_2{SharedPointer(ctx, offset, 1)}; | 157 | const Id pointer_2{SharedPointer(ctx, offset, 1)}; |
| 147 | const Id value_1{ctx.OpLoad(ctx.U32[1], pointer_1)}; | 158 | const Id value_1{ctx.OpLoad(ctx.U32[1], pointer_1)}; |
| @@ -152,6 +163,18 @@ Id EmitSharedAtomicExchange64(EmitContext& ctx, Id offset, Id value) { | |||
| 152 | return ctx.OpBitcast(ctx.U64, ctx.OpCompositeConstruct(ctx.U32[2], value_1, value_2)); | 163 | return ctx.OpBitcast(ctx.U64, ctx.OpCompositeConstruct(ctx.U32[2], value_1, value_2)); |
| 153 | } | 164 | } |
| 154 | 165 | ||
| 166 | Id EmitSharedAtomicExchange32x2(EmitContext& ctx, Id offset, Id value) { | ||
| 167 | LOG_WARNING(Shader_SPIRV, "Int64 atomics not supported, fallback to non-atomic"); | ||
| 168 | const Id pointer_1{SharedPointer(ctx, offset, 0)}; | ||
| 169 | const Id pointer_2{SharedPointer(ctx, offset, 1)}; | ||
| 170 | const Id value_1{ctx.OpLoad(ctx.U32[1], pointer_1)}; | ||
| 171 | const Id value_2{ctx.OpLoad(ctx.U32[1], pointer_2)}; | ||
| 172 | const Id new_vector{ctx.OpBitcast(ctx.U32[2], value)}; | ||
| 173 | ctx.OpStore(pointer_1, ctx.OpCompositeExtract(ctx.U32[1], new_vector, 0U)); | ||
| 174 | ctx.OpStore(pointer_2, ctx.OpCompositeExtract(ctx.U32[1], new_vector, 1U)); | ||
| 175 | return ctx.OpCompositeConstruct(ctx.U32[2], value_1, value_2); | ||
| 176 | } | ||
| 177 | |||
| 155 | Id EmitStorageAtomicIAdd32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | 178 | Id EmitStorageAtomicIAdd32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, |
| 156 | Id value) { | 179 | Id value) { |
| 157 | return StorageAtomicU32(ctx, binding, offset, value, &Sirit::Module::OpAtomicIAdd); | 180 | return StorageAtomicU32(ctx, binding, offset, value, &Sirit::Module::OpAtomicIAdd); |
| @@ -275,6 +298,56 @@ Id EmitStorageAtomicExchange64(EmitContext& ctx, const IR::Value& binding, const | |||
| 275 | return original; | 298 | return original; |
| 276 | } | 299 | } |
| 277 | 300 | ||
| 301 | Id EmitStorageAtomicIAdd32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 302 | Id value) { | ||
| 303 | return StorageAtomicU32x2(ctx, binding, offset, value, &Sirit::Module::OpIAdd); | ||
| 304 | } | ||
| 305 | |||
| 306 | Id EmitStorageAtomicSMin32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 307 | Id value) { | ||
| 308 | return StorageAtomicU32x2(ctx, binding, offset, value, &Sirit::Module::OpSMin); | ||
| 309 | } | ||
| 310 | |||
| 311 | Id EmitStorageAtomicUMin32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 312 | Id value) { | ||
| 313 | return StorageAtomicU32x2(ctx, binding, offset, value, &Sirit::Module::OpUMin); | ||
| 314 | } | ||
| 315 | |||
| 316 | Id EmitStorageAtomicSMax32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 317 | Id value) { | ||
| 318 | return StorageAtomicU32x2(ctx, binding, offset, value, &Sirit::Module::OpSMax); | ||
| 319 | } | ||
| 320 | |||
| 321 | Id EmitStorageAtomicUMax32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 322 | Id value) { | ||
| 323 | return StorageAtomicU32x2(ctx, binding, offset, value, &Sirit::Module::OpUMax); | ||
| 324 | } | ||
| 325 | |||
| 326 | Id EmitStorageAtomicAnd32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 327 | Id value) { | ||
| 328 | return StorageAtomicU32x2(ctx, binding, offset, value, &Sirit::Module::OpBitwiseAnd); | ||
| 329 | } | ||
| 330 | |||
| 331 | Id EmitStorageAtomicOr32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 332 | Id value) { | ||
| 333 | return StorageAtomicU32x2(ctx, binding, offset, value, &Sirit::Module::OpBitwiseOr); | ||
| 334 | } | ||
| 335 | |||
| 336 | Id EmitStorageAtomicXor32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 337 | Id value) { | ||
| 338 | return StorageAtomicU32x2(ctx, binding, offset, value, &Sirit::Module::OpBitwiseXor); | ||
| 339 | } | ||
| 340 | |||
| 341 | Id EmitStorageAtomicExchange32x2(EmitContext& ctx, const IR::Value& binding, | ||
| 342 | const IR::Value& offset, Id value) { | ||
| 343 | LOG_WARNING(Shader_SPIRV, "Int64 atomics not supported, fallback to non-atomic"); | ||
| 344 | const Id pointer{StoragePointer(ctx, ctx.storage_types.U32x2, &StorageDefinitions::U32x2, | ||
| 345 | binding, offset, sizeof(u32[2]))}; | ||
| 346 | const Id original{ctx.OpLoad(ctx.U32[2], pointer)}; | ||
| 347 | ctx.OpStore(pointer, value); | ||
| 348 | return original; | ||
| 349 | } | ||
| 350 | |||
| 278 | Id EmitStorageAtomicAddF32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | 351 | Id EmitStorageAtomicAddF32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, |
| 279 | Id value) { | 352 | Id value) { |
| 280 | const Id ssbo{ctx.ssbos[binding.U32()].U32}; | 353 | const Id ssbo{ctx.ssbos[binding.U32()].U32}; |
| @@ -418,6 +491,50 @@ Id EmitGlobalAtomicExchange64(EmitContext&) { | |||
| 418 | throw NotImplementedException("SPIR-V Instruction"); | 491 | throw NotImplementedException("SPIR-V Instruction"); |
| 419 | } | 492 | } |
| 420 | 493 | ||
| 494 | Id EmitGlobalAtomicIAdd32x2(EmitContext&) { | ||
| 495 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 496 | } | ||
| 497 | |||
| 498 | Id EmitGlobalAtomicSMin32x2(EmitContext&) { | ||
| 499 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 500 | } | ||
| 501 | |||
| 502 | Id EmitGlobalAtomicUMin32x2(EmitContext&) { | ||
| 503 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 504 | } | ||
| 505 | |||
| 506 | Id EmitGlobalAtomicSMax32x2(EmitContext&) { | ||
| 507 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 508 | } | ||
| 509 | |||
| 510 | Id EmitGlobalAtomicUMax32x2(EmitContext&) { | ||
| 511 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 512 | } | ||
| 513 | |||
| 514 | Id EmitGlobalAtomicInc32x2(EmitContext&) { | ||
| 515 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 516 | } | ||
| 517 | |||
| 518 | Id EmitGlobalAtomicDec32x2(EmitContext&) { | ||
| 519 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 520 | } | ||
| 521 | |||
| 522 | Id EmitGlobalAtomicAnd32x2(EmitContext&) { | ||
| 523 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 524 | } | ||
| 525 | |||
| 526 | Id EmitGlobalAtomicOr32x2(EmitContext&) { | ||
| 527 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 528 | } | ||
| 529 | |||
| 530 | Id EmitGlobalAtomicXor32x2(EmitContext&) { | ||
| 531 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 532 | } | ||
| 533 | |||
| 534 | Id EmitGlobalAtomicExchange32x2(EmitContext&) { | ||
| 535 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 536 | } | ||
| 537 | |||
| 421 | Id EmitGlobalAtomicAddF32(EmitContext&) { | 538 | Id EmitGlobalAtomicAddF32(EmitContext&) { |
| 422 | throw NotImplementedException("SPIR-V Instruction"); | 539 | throw NotImplementedException("SPIR-V Instruction"); |
| 423 | } | 540 | } |
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp index 8ea730c80..80b4bbd27 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp | |||
| @@ -123,34 +123,36 @@ std::optional<OutAttr> OutputAttrPointer(EmitContext& ctx, IR::Attribute attr) { | |||
| 123 | } | 123 | } |
| 124 | 124 | ||
| 125 | Id GetCbuf(EmitContext& ctx, Id result_type, Id UniformDefinitions::*member_ptr, u32 element_size, | 125 | Id GetCbuf(EmitContext& ctx, Id result_type, Id UniformDefinitions::*member_ptr, u32 element_size, |
| 126 | const IR::Value& binding, const IR::Value& offset) { | 126 | const IR::Value& binding, const IR::Value& offset, const Id indirect_func) { |
| 127 | Id buffer_offset; | ||
| 128 | const Id uniform_type{ctx.uniform_types.*member_ptr}; | ||
| 129 | if (offset.IsImmediate()) { | ||
| 130 | // Hardware been proved to read the aligned offset (e.g. LDC.U32 at 6 will read offset 4) | ||
| 131 | const Id imm_offset{ctx.Const(offset.U32() / element_size)}; | ||
| 132 | buffer_offset = imm_offset; | ||
| 133 | } else if (element_size > 1) { | ||
| 134 | const u32 log2_element_size{static_cast<u32>(std::countr_zero(element_size))}; | ||
| 135 | const Id shift{ctx.Const(log2_element_size)}; | ||
| 136 | buffer_offset = ctx.OpShiftRightArithmetic(ctx.U32[1], ctx.Def(offset), shift); | ||
| 137 | } else { | ||
| 138 | buffer_offset = ctx.Def(offset); | ||
| 139 | } | ||
| 127 | if (!binding.IsImmediate()) { | 140 | if (!binding.IsImmediate()) { |
| 128 | throw NotImplementedException("Constant buffer indexing"); | 141 | return ctx.OpFunctionCall(result_type, indirect_func, ctx.Def(binding), buffer_offset); |
| 129 | } | 142 | } |
| 130 | const Id cbuf{ctx.cbufs[binding.U32()].*member_ptr}; | 143 | const Id cbuf{ctx.cbufs[binding.U32()].*member_ptr}; |
| 131 | const Id uniform_type{ctx.uniform_types.*member_ptr}; | 144 | const Id access_chain{ctx.OpAccessChain(uniform_type, cbuf, ctx.u32_zero_value, buffer_offset)}; |
| 132 | if (!offset.IsImmediate()) { | ||
| 133 | Id index{ctx.Def(offset)}; | ||
| 134 | if (element_size > 1) { | ||
| 135 | const u32 log2_element_size{static_cast<u32>(std::countr_zero(element_size))}; | ||
| 136 | const Id shift{ctx.Const(log2_element_size)}; | ||
| 137 | index = ctx.OpShiftRightArithmetic(ctx.U32[1], ctx.Def(offset), shift); | ||
| 138 | } | ||
| 139 | const Id access_chain{ctx.OpAccessChain(uniform_type, cbuf, ctx.u32_zero_value, index)}; | ||
| 140 | return ctx.OpLoad(result_type, access_chain); | ||
| 141 | } | ||
| 142 | // Hardware been proved to read the aligned offset (e.g. LDC.U32 at 6 will read offset 4) | ||
| 143 | const Id imm_offset{ctx.Const(offset.U32() / element_size)}; | ||
| 144 | const Id access_chain{ctx.OpAccessChain(uniform_type, cbuf, ctx.u32_zero_value, imm_offset)}; | ||
| 145 | return ctx.OpLoad(result_type, access_chain); | 145 | return ctx.OpLoad(result_type, access_chain); |
| 146 | } | 146 | } |
| 147 | 147 | ||
| 148 | Id GetCbufU32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) { | 148 | Id GetCbufU32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) { |
| 149 | return GetCbuf(ctx, ctx.U32[1], &UniformDefinitions::U32, sizeof(u32), binding, offset); | 149 | return GetCbuf(ctx, ctx.U32[1], &UniformDefinitions::U32, sizeof(u32), binding, offset, |
| 150 | ctx.load_const_func_u32); | ||
| 150 | } | 151 | } |
| 151 | 152 | ||
| 152 | Id GetCbufU32x4(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) { | 153 | Id GetCbufU32x4(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) { |
| 153 | return GetCbuf(ctx, ctx.U32[4], &UniformDefinitions::U32x4, sizeof(u32[4]), binding, offset); | 154 | return GetCbuf(ctx, ctx.U32[4], &UniformDefinitions::U32x4, sizeof(u32[4]), binding, offset, |
| 155 | ctx.load_const_func_u32x4); | ||
| 154 | } | 156 | } |
| 155 | 157 | ||
| 156 | Id GetCbufElement(EmitContext& ctx, Id vector, const IR::Value& offset, u32 index_offset) { | 158 | Id GetCbufElement(EmitContext& ctx, Id vector, const IR::Value& offset, u32 index_offset) { |
| @@ -201,7 +203,8 @@ void EmitGetIndirectBranchVariable(EmitContext&) { | |||
| 201 | 203 | ||
| 202 | Id EmitGetCbufU8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) { | 204 | Id EmitGetCbufU8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) { |
| 203 | if (ctx.profile.support_descriptor_aliasing && ctx.profile.support_int8) { | 205 | if (ctx.profile.support_descriptor_aliasing && ctx.profile.support_int8) { |
| 204 | const Id load{GetCbuf(ctx, ctx.U8, &UniformDefinitions::U8, sizeof(u8), binding, offset)}; | 206 | const Id load{GetCbuf(ctx, ctx.U8, &UniformDefinitions::U8, sizeof(u8), binding, offset, |
| 207 | ctx.load_const_func_u8)}; | ||
| 205 | return ctx.OpUConvert(ctx.U32[1], load); | 208 | return ctx.OpUConvert(ctx.U32[1], load); |
| 206 | } | 209 | } |
| 207 | Id element{}; | 210 | Id element{}; |
| @@ -217,7 +220,8 @@ Id EmitGetCbufU8(EmitContext& ctx, const IR::Value& binding, const IR::Value& of | |||
| 217 | 220 | ||
| 218 | Id EmitGetCbufS8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) { | 221 | Id EmitGetCbufS8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) { |
| 219 | if (ctx.profile.support_descriptor_aliasing && ctx.profile.support_int8) { | 222 | if (ctx.profile.support_descriptor_aliasing && ctx.profile.support_int8) { |
| 220 | const Id load{GetCbuf(ctx, ctx.S8, &UniformDefinitions::S8, sizeof(s8), binding, offset)}; | 223 | const Id load{GetCbuf(ctx, ctx.S8, &UniformDefinitions::S8, sizeof(s8), binding, offset, |
| 224 | ctx.load_const_func_u8)}; | ||
| 221 | return ctx.OpSConvert(ctx.U32[1], load); | 225 | return ctx.OpSConvert(ctx.U32[1], load); |
| 222 | } | 226 | } |
| 223 | Id element{}; | 227 | Id element{}; |
| @@ -233,8 +237,8 @@ Id EmitGetCbufS8(EmitContext& ctx, const IR::Value& binding, const IR::Value& of | |||
| 233 | 237 | ||
| 234 | Id EmitGetCbufU16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) { | 238 | Id EmitGetCbufU16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) { |
| 235 | if (ctx.profile.support_descriptor_aliasing && ctx.profile.support_int16) { | 239 | if (ctx.profile.support_descriptor_aliasing && ctx.profile.support_int16) { |
| 236 | const Id load{ | 240 | const Id load{GetCbuf(ctx, ctx.U16, &UniformDefinitions::U16, sizeof(u16), binding, offset, |
| 237 | GetCbuf(ctx, ctx.U16, &UniformDefinitions::U16, sizeof(u16), binding, offset)}; | 241 | ctx.load_const_func_u16)}; |
| 238 | return ctx.OpUConvert(ctx.U32[1], load); | 242 | return ctx.OpUConvert(ctx.U32[1], load); |
| 239 | } | 243 | } |
| 240 | Id element{}; | 244 | Id element{}; |
| @@ -250,8 +254,8 @@ Id EmitGetCbufU16(EmitContext& ctx, const IR::Value& binding, const IR::Value& o | |||
| 250 | 254 | ||
| 251 | Id EmitGetCbufS16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) { | 255 | Id EmitGetCbufS16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) { |
| 252 | if (ctx.profile.support_descriptor_aliasing && ctx.profile.support_int16) { | 256 | if (ctx.profile.support_descriptor_aliasing && ctx.profile.support_int16) { |
| 253 | const Id load{ | 257 | const Id load{GetCbuf(ctx, ctx.S16, &UniformDefinitions::S16, sizeof(s16), binding, offset, |
| 254 | GetCbuf(ctx, ctx.S16, &UniformDefinitions::S16, sizeof(s16), binding, offset)}; | 258 | ctx.load_const_func_u16)}; |
| 255 | return ctx.OpSConvert(ctx.U32[1], load); | 259 | return ctx.OpSConvert(ctx.U32[1], load); |
| 256 | } | 260 | } |
| 257 | Id element{}; | 261 | Id element{}; |
| @@ -276,7 +280,8 @@ Id EmitGetCbufU32(EmitContext& ctx, const IR::Value& binding, const IR::Value& o | |||
| 276 | 280 | ||
| 277 | Id EmitGetCbufF32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) { | 281 | Id EmitGetCbufF32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) { |
| 278 | if (ctx.profile.support_descriptor_aliasing) { | 282 | if (ctx.profile.support_descriptor_aliasing) { |
| 279 | return GetCbuf(ctx, ctx.F32[1], &UniformDefinitions::F32, sizeof(f32), binding, offset); | 283 | return GetCbuf(ctx, ctx.F32[1], &UniformDefinitions::F32, sizeof(f32), binding, offset, |
| 284 | ctx.load_const_func_f32); | ||
| 280 | } else { | 285 | } else { |
| 281 | const Id vector{GetCbufU32x4(ctx, binding, offset)}; | 286 | const Id vector{GetCbufU32x4(ctx, binding, offset)}; |
| 282 | return ctx.OpBitcast(ctx.F32[1], GetCbufElement(ctx, vector, offset, 0u)); | 287 | return ctx.OpBitcast(ctx.F32[1], GetCbufElement(ctx, vector, offset, 0u)); |
| @@ -285,8 +290,8 @@ Id EmitGetCbufF32(EmitContext& ctx, const IR::Value& binding, const IR::Value& o | |||
| 285 | 290 | ||
| 286 | Id EmitGetCbufU32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) { | 291 | Id EmitGetCbufU32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) { |
| 287 | if (ctx.profile.support_descriptor_aliasing) { | 292 | if (ctx.profile.support_descriptor_aliasing) { |
| 288 | return GetCbuf(ctx, ctx.U32[2], &UniformDefinitions::U32x2, sizeof(u32[2]), binding, | 293 | return GetCbuf(ctx, ctx.U32[2], &UniformDefinitions::U32x2, sizeof(u32[2]), binding, offset, |
| 289 | offset); | 294 | ctx.load_const_func_u32x2); |
| 290 | } else { | 295 | } else { |
| 291 | const Id vector{GetCbufU32x4(ctx, binding, offset)}; | 296 | const Id vector{GetCbufU32x4(ctx, binding, offset)}; |
| 292 | return ctx.OpCompositeConstruct(ctx.U32[2], GetCbufElement(ctx, vector, offset, 0u), | 297 | return ctx.OpCompositeConstruct(ctx.U32[2], GetCbufElement(ctx, vector, offset, 0u), |
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h index 887112deb..f263b41b0 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h +++ b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h | |||
| @@ -335,6 +335,7 @@ Id EmitSharedAtomicOr32(EmitContext& ctx, Id pointer_offset, Id value); | |||
| 335 | Id EmitSharedAtomicXor32(EmitContext& ctx, Id pointer_offset, Id value); | 335 | Id EmitSharedAtomicXor32(EmitContext& ctx, Id pointer_offset, Id value); |
| 336 | Id EmitSharedAtomicExchange32(EmitContext& ctx, Id pointer_offset, Id value); | 336 | Id EmitSharedAtomicExchange32(EmitContext& ctx, Id pointer_offset, Id value); |
| 337 | Id EmitSharedAtomicExchange64(EmitContext& ctx, Id pointer_offset, Id value); | 337 | Id EmitSharedAtomicExchange64(EmitContext& ctx, Id pointer_offset, Id value); |
| 338 | Id EmitSharedAtomicExchange32x2(EmitContext& ctx, Id pointer_offset, Id value); | ||
| 338 | Id EmitStorageAtomicIAdd32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | 339 | Id EmitStorageAtomicIAdd32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, |
| 339 | Id value); | 340 | Id value); |
| 340 | Id EmitStorageAtomicSMin32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | 341 | Id EmitStorageAtomicSMin32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, |
| @@ -375,6 +376,24 @@ Id EmitStorageAtomicXor64(EmitContext& ctx, const IR::Value& binding, const IR:: | |||
| 375 | Id value); | 376 | Id value); |
| 376 | Id EmitStorageAtomicExchange64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | 377 | Id EmitStorageAtomicExchange64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, |
| 377 | Id value); | 378 | Id value); |
| 379 | Id EmitStorageAtomicIAdd32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 380 | Id value); | ||
| 381 | Id EmitStorageAtomicSMin32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 382 | Id value); | ||
| 383 | Id EmitStorageAtomicUMin32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 384 | Id value); | ||
| 385 | Id EmitStorageAtomicSMax32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 386 | Id value); | ||
| 387 | Id EmitStorageAtomicUMax32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 388 | Id value); | ||
| 389 | Id EmitStorageAtomicAnd32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 390 | Id value); | ||
| 391 | Id EmitStorageAtomicOr32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 392 | Id value); | ||
| 393 | Id EmitStorageAtomicXor32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 394 | Id value); | ||
| 395 | Id EmitStorageAtomicExchange32x2(EmitContext& ctx, const IR::Value& binding, | ||
| 396 | const IR::Value& offset, Id value); | ||
| 378 | Id EmitStorageAtomicAddF32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | 397 | Id EmitStorageAtomicAddF32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, |
| 379 | Id value); | 398 | Id value); |
| 380 | Id EmitStorageAtomicAddF16x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | 399 | Id EmitStorageAtomicAddF16x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, |
| @@ -411,6 +430,17 @@ Id EmitGlobalAtomicAnd64(EmitContext& ctx); | |||
| 411 | Id EmitGlobalAtomicOr64(EmitContext& ctx); | 430 | Id EmitGlobalAtomicOr64(EmitContext& ctx); |
| 412 | Id EmitGlobalAtomicXor64(EmitContext& ctx); | 431 | Id EmitGlobalAtomicXor64(EmitContext& ctx); |
| 413 | Id EmitGlobalAtomicExchange64(EmitContext& ctx); | 432 | Id EmitGlobalAtomicExchange64(EmitContext& ctx); |
| 433 | Id EmitGlobalAtomicIAdd32x2(EmitContext& ctx); | ||
| 434 | Id EmitGlobalAtomicSMin32x2(EmitContext& ctx); | ||
| 435 | Id EmitGlobalAtomicUMin32x2(EmitContext& ctx); | ||
| 436 | Id EmitGlobalAtomicSMax32x2(EmitContext& ctx); | ||
| 437 | Id EmitGlobalAtomicUMax32x2(EmitContext& ctx); | ||
| 438 | Id EmitGlobalAtomicInc32x2(EmitContext& ctx); | ||
| 439 | Id EmitGlobalAtomicDec32x2(EmitContext& ctx); | ||
| 440 | Id EmitGlobalAtomicAnd32x2(EmitContext& ctx); | ||
| 441 | Id EmitGlobalAtomicOr32x2(EmitContext& ctx); | ||
| 442 | Id EmitGlobalAtomicXor32x2(EmitContext& ctx); | ||
| 443 | Id EmitGlobalAtomicExchange32x2(EmitContext& ctx); | ||
| 414 | Id EmitGlobalAtomicAddF32(EmitContext& ctx); | 444 | Id EmitGlobalAtomicAddF32(EmitContext& ctx); |
| 415 | Id EmitGlobalAtomicAddF16x2(EmitContext& ctx); | 445 | Id EmitGlobalAtomicAddF16x2(EmitContext& ctx); |
| 416 | Id EmitGlobalAtomicAddF32x2(EmitContext& ctx); | 446 | Id EmitGlobalAtomicAddF32x2(EmitContext& ctx); |
diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp index cd90c084a..aa5b6c9b7 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp | |||
| @@ -464,6 +464,7 @@ EmitContext::EmitContext(const Profile& profile_, const RuntimeInfo& runtime_inf | |||
| 464 | DefineSharedMemory(program); | 464 | DefineSharedMemory(program); |
| 465 | DefineSharedMemoryFunctions(program); | 465 | DefineSharedMemoryFunctions(program); |
| 466 | DefineConstantBuffers(program.info, uniform_binding); | 466 | DefineConstantBuffers(program.info, uniform_binding); |
| 467 | DefineConstantBufferIndirectFunctions(program.info); | ||
| 467 | DefineStorageBuffers(program.info, storage_binding); | 468 | DefineStorageBuffers(program.info, storage_binding); |
| 468 | DefineTextureBuffers(program.info, texture_binding); | 469 | DefineTextureBuffers(program.info, texture_binding); |
| 469 | DefineImageBuffers(program.info, image_binding); | 470 | DefineImageBuffers(program.info, image_binding); |
| @@ -993,7 +994,7 @@ void EmitContext::DefineConstantBuffers(const Info& info, u32& binding) { | |||
| 993 | } | 994 | } |
| 994 | return; | 995 | return; |
| 995 | } | 996 | } |
| 996 | IR::Type types{info.used_constant_buffer_types}; | 997 | IR::Type types{info.used_constant_buffer_types | info.used_indirect_cbuf_types}; |
| 997 | if (True(types & IR::Type::U8)) { | 998 | if (True(types & IR::Type::U8)) { |
| 998 | if (profile.support_int8) { | 999 | if (profile.support_int8) { |
| 999 | DefineConstBuffers(*this, info, &UniformDefinitions::U8, binding, U8, 'u', sizeof(u8)); | 1000 | DefineConstBuffers(*this, info, &UniformDefinitions::U8, binding, U8, 'u', sizeof(u8)); |
| @@ -1027,6 +1028,62 @@ void EmitContext::DefineConstantBuffers(const Info& info, u32& binding) { | |||
| 1027 | binding += static_cast<u32>(info.constant_buffer_descriptors.size()); | 1028 | binding += static_cast<u32>(info.constant_buffer_descriptors.size()); |
| 1028 | } | 1029 | } |
| 1029 | 1030 | ||
| 1031 | void EmitContext::DefineConstantBufferIndirectFunctions(const Info& info) { | ||
| 1032 | if (!info.uses_cbuf_indirect) { | ||
| 1033 | return; | ||
| 1034 | } | ||
| 1035 | const auto make_accessor{[&](Id buffer_type, Id UniformDefinitions::*member_ptr) { | ||
| 1036 | const Id func_type{TypeFunction(buffer_type, U32[1], U32[1])}; | ||
| 1037 | const Id func{OpFunction(buffer_type, spv::FunctionControlMask::MaskNone, func_type)}; | ||
| 1038 | const Id binding{OpFunctionParameter(U32[1])}; | ||
| 1039 | const Id offset{OpFunctionParameter(U32[1])}; | ||
| 1040 | |||
| 1041 | AddLabel(); | ||
| 1042 | |||
| 1043 | const Id merge_label{OpLabel()}; | ||
| 1044 | const Id uniform_type{uniform_types.*member_ptr}; | ||
| 1045 | |||
| 1046 | std::array<Id, Info::MAX_CBUFS> buf_labels; | ||
| 1047 | std::array<Sirit::Literal, Info::MAX_CBUFS> buf_literals; | ||
| 1048 | for (u32 i = 0; i < Info::MAX_CBUFS; i++) { | ||
| 1049 | buf_labels[i] = OpLabel(); | ||
| 1050 | buf_literals[i] = Sirit::Literal{i}; | ||
| 1051 | } | ||
| 1052 | OpSelectionMerge(merge_label, spv::SelectionControlMask::MaskNone); | ||
| 1053 | OpSwitch(binding, buf_labels[0], buf_literals, buf_labels); | ||
| 1054 | for (u32 i = 0; i < Info::MAX_CBUFS; i++) { | ||
| 1055 | AddLabel(buf_labels[i]); | ||
| 1056 | const Id cbuf{cbufs[i].*member_ptr}; | ||
| 1057 | const Id access_chain{OpAccessChain(uniform_type, cbuf, u32_zero_value, offset)}; | ||
| 1058 | const Id result{OpLoad(buffer_type, access_chain)}; | ||
| 1059 | OpReturnValue(result); | ||
| 1060 | } | ||
| 1061 | AddLabel(merge_label); | ||
| 1062 | OpUnreachable(); | ||
| 1063 | OpFunctionEnd(); | ||
| 1064 | return func; | ||
| 1065 | }}; | ||
| 1066 | IR::Type types{info.used_indirect_cbuf_types}; | ||
| 1067 | if (True(types & IR::Type::U8)) { | ||
| 1068 | load_const_func_u8 = make_accessor(U8, &UniformDefinitions::U8); | ||
| 1069 | } | ||
| 1070 | if (True(types & IR::Type::U16)) { | ||
| 1071 | load_const_func_u16 = make_accessor(U16, &UniformDefinitions::U16); | ||
| 1072 | } | ||
| 1073 | if (True(types & IR::Type::F32)) { | ||
| 1074 | load_const_func_f32 = make_accessor(F32[1], &UniformDefinitions::F32); | ||
| 1075 | } | ||
| 1076 | if (True(types & IR::Type::U32)) { | ||
| 1077 | load_const_func_u32 = make_accessor(U32[1], &UniformDefinitions::U32); | ||
| 1078 | } | ||
| 1079 | if (True(types & IR::Type::U32x2)) { | ||
| 1080 | load_const_func_u32x2 = make_accessor(U32[2], &UniformDefinitions::U32x2); | ||
| 1081 | } | ||
| 1082 | if (True(types & IR::Type::U32x4)) { | ||
| 1083 | load_const_func_u32x4 = make_accessor(U32[4], &UniformDefinitions::U32x4); | ||
| 1084 | } | ||
| 1085 | } | ||
| 1086 | |||
| 1030 | void EmitContext::DefineStorageBuffers(const Info& info, u32& binding) { | 1087 | void EmitContext::DefineStorageBuffers(const Info& info, u32& binding) { |
| 1031 | if (info.storage_buffers_descriptors.empty()) { | 1088 | if (info.storage_buffers_descriptors.empty()) { |
| 1032 | return; | 1089 | return; |
diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.h b/src/shader_recompiler/backend/spirv/spirv_emit_context.h index f87138f7e..906a1dc2c 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.h +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.h | |||
| @@ -294,6 +294,13 @@ public: | |||
| 294 | 294 | ||
| 295 | std::vector<Id> interfaces; | 295 | std::vector<Id> interfaces; |
| 296 | 296 | ||
| 297 | Id load_const_func_u8{}; | ||
| 298 | Id load_const_func_u16{}; | ||
| 299 | Id load_const_func_u32{}; | ||
| 300 | Id load_const_func_f32{}; | ||
| 301 | Id load_const_func_u32x2{}; | ||
| 302 | Id load_const_func_u32x4{}; | ||
| 303 | |||
| 297 | private: | 304 | private: |
| 298 | void DefineCommonTypes(const Info& info); | 305 | void DefineCommonTypes(const Info& info); |
| 299 | void DefineCommonConstants(); | 306 | void DefineCommonConstants(); |
| @@ -302,6 +309,7 @@ private: | |||
| 302 | void DefineSharedMemory(const IR::Program& program); | 309 | void DefineSharedMemory(const IR::Program& program); |
| 303 | void DefineSharedMemoryFunctions(const IR::Program& program); | 310 | void DefineSharedMemoryFunctions(const IR::Program& program); |
| 304 | void DefineConstantBuffers(const Info& info, u32& binding); | 311 | void DefineConstantBuffers(const Info& info, u32& binding); |
| 312 | void DefineConstantBufferIndirectFunctions(const Info& info); | ||
| 305 | void DefineStorageBuffers(const Info& info, u32& binding); | 313 | void DefineStorageBuffers(const Info& info, u32& binding); |
| 306 | void DefineTextureBuffers(const Info& info, u32& binding); | 314 | void DefineTextureBuffers(const Info& info, u32& binding); |
| 307 | void DefineImageBuffers(const Info& info, u32& binding); | 315 | void DefineImageBuffers(const Info& info, u32& binding); |
diff --git a/src/shader_recompiler/frontend/ir/microinstruction.cpp b/src/shader_recompiler/frontend/ir/microinstruction.cpp index 97e2bf6af..631446cf7 100644 --- a/src/shader_recompiler/frontend/ir/microinstruction.cpp +++ b/src/shader_recompiler/frontend/ir/microinstruction.cpp | |||
| @@ -118,6 +118,7 @@ bool Inst::MayHaveSideEffects() const noexcept { | |||
| 118 | case Opcode::SharedAtomicXor32: | 118 | case Opcode::SharedAtomicXor32: |
| 119 | case Opcode::SharedAtomicExchange32: | 119 | case Opcode::SharedAtomicExchange32: |
| 120 | case Opcode::SharedAtomicExchange64: | 120 | case Opcode::SharedAtomicExchange64: |
| 121 | case Opcode::SharedAtomicExchange32x2: | ||
| 121 | case Opcode::GlobalAtomicIAdd32: | 122 | case Opcode::GlobalAtomicIAdd32: |
| 122 | case Opcode::GlobalAtomicSMin32: | 123 | case Opcode::GlobalAtomicSMin32: |
| 123 | case Opcode::GlobalAtomicUMin32: | 124 | case Opcode::GlobalAtomicUMin32: |
| @@ -138,6 +139,15 @@ bool Inst::MayHaveSideEffects() const noexcept { | |||
| 138 | case Opcode::GlobalAtomicOr64: | 139 | case Opcode::GlobalAtomicOr64: |
| 139 | case Opcode::GlobalAtomicXor64: | 140 | case Opcode::GlobalAtomicXor64: |
| 140 | case Opcode::GlobalAtomicExchange64: | 141 | case Opcode::GlobalAtomicExchange64: |
| 142 | case Opcode::GlobalAtomicIAdd32x2: | ||
| 143 | case Opcode::GlobalAtomicSMin32x2: | ||
| 144 | case Opcode::GlobalAtomicUMin32x2: | ||
| 145 | case Opcode::GlobalAtomicSMax32x2: | ||
| 146 | case Opcode::GlobalAtomicUMax32x2: | ||
| 147 | case Opcode::GlobalAtomicAnd32x2: | ||
| 148 | case Opcode::GlobalAtomicOr32x2: | ||
| 149 | case Opcode::GlobalAtomicXor32x2: | ||
| 150 | case Opcode::GlobalAtomicExchange32x2: | ||
| 141 | case Opcode::GlobalAtomicAddF32: | 151 | case Opcode::GlobalAtomicAddF32: |
| 142 | case Opcode::GlobalAtomicAddF16x2: | 152 | case Opcode::GlobalAtomicAddF16x2: |
| 143 | case Opcode::GlobalAtomicAddF32x2: | 153 | case Opcode::GlobalAtomicAddF32x2: |
| @@ -165,6 +175,15 @@ bool Inst::MayHaveSideEffects() const noexcept { | |||
| 165 | case Opcode::StorageAtomicOr64: | 175 | case Opcode::StorageAtomicOr64: |
| 166 | case Opcode::StorageAtomicXor64: | 176 | case Opcode::StorageAtomicXor64: |
| 167 | case Opcode::StorageAtomicExchange64: | 177 | case Opcode::StorageAtomicExchange64: |
| 178 | case Opcode::StorageAtomicIAdd32x2: | ||
| 179 | case Opcode::StorageAtomicSMin32x2: | ||
| 180 | case Opcode::StorageAtomicUMin32x2: | ||
| 181 | case Opcode::StorageAtomicSMax32x2: | ||
| 182 | case Opcode::StorageAtomicUMax32x2: | ||
| 183 | case Opcode::StorageAtomicAnd32x2: | ||
| 184 | case Opcode::StorageAtomicOr32x2: | ||
| 185 | case Opcode::StorageAtomicXor32x2: | ||
| 186 | case Opcode::StorageAtomicExchange32x2: | ||
| 168 | case Opcode::StorageAtomicAddF32: | 187 | case Opcode::StorageAtomicAddF32: |
| 169 | case Opcode::StorageAtomicAddF16x2: | 188 | case Opcode::StorageAtomicAddF16x2: |
| 170 | case Opcode::StorageAtomicAddF32x2: | 189 | case Opcode::StorageAtomicAddF32x2: |
diff --git a/src/shader_recompiler/frontend/ir/opcodes.inc b/src/shader_recompiler/frontend/ir/opcodes.inc index b94ce7406..efb6bfac3 100644 --- a/src/shader_recompiler/frontend/ir/opcodes.inc +++ b/src/shader_recompiler/frontend/ir/opcodes.inc | |||
| @@ -341,6 +341,7 @@ OPCODE(SharedAtomicOr32, U32, U32, | |||
| 341 | OPCODE(SharedAtomicXor32, U32, U32, U32, ) | 341 | OPCODE(SharedAtomicXor32, U32, U32, U32, ) |
| 342 | OPCODE(SharedAtomicExchange32, U32, U32, U32, ) | 342 | OPCODE(SharedAtomicExchange32, U32, U32, U32, ) |
| 343 | OPCODE(SharedAtomicExchange64, U64, U32, U64, ) | 343 | OPCODE(SharedAtomicExchange64, U64, U32, U64, ) |
| 344 | OPCODE(SharedAtomicExchange32x2, U32x2, U32, U32x2, ) | ||
| 344 | 345 | ||
| 345 | OPCODE(GlobalAtomicIAdd32, U32, U64, U32, ) | 346 | OPCODE(GlobalAtomicIAdd32, U32, U64, U32, ) |
| 346 | OPCODE(GlobalAtomicSMin32, U32, U64, U32, ) | 347 | OPCODE(GlobalAtomicSMin32, U32, U64, U32, ) |
| @@ -362,6 +363,15 @@ OPCODE(GlobalAtomicAnd64, U64, U64, | |||
| 362 | OPCODE(GlobalAtomicOr64, U64, U64, U64, ) | 363 | OPCODE(GlobalAtomicOr64, U64, U64, U64, ) |
| 363 | OPCODE(GlobalAtomicXor64, U64, U64, U64, ) | 364 | OPCODE(GlobalAtomicXor64, U64, U64, U64, ) |
| 364 | OPCODE(GlobalAtomicExchange64, U64, U64, U64, ) | 365 | OPCODE(GlobalAtomicExchange64, U64, U64, U64, ) |
| 366 | OPCODE(GlobalAtomicIAdd32x2, U32x2, U32x2, U32x2, ) | ||
| 367 | OPCODE(GlobalAtomicSMin32x2, U32x2, U32x2, U32x2, ) | ||
| 368 | OPCODE(GlobalAtomicUMin32x2, U32x2, U32x2, U32x2, ) | ||
| 369 | OPCODE(GlobalAtomicSMax32x2, U32x2, U32x2, U32x2, ) | ||
| 370 | OPCODE(GlobalAtomicUMax32x2, U32x2, U32x2, U32x2, ) | ||
| 371 | OPCODE(GlobalAtomicAnd32x2, U32x2, U32x2, U32x2, ) | ||
| 372 | OPCODE(GlobalAtomicOr32x2, U32x2, U32x2, U32x2, ) | ||
| 373 | OPCODE(GlobalAtomicXor32x2, U32x2, U32x2, U32x2, ) | ||
| 374 | OPCODE(GlobalAtomicExchange32x2, U32x2, U32x2, U32x2, ) | ||
| 365 | OPCODE(GlobalAtomicAddF32, F32, U64, F32, ) | 375 | OPCODE(GlobalAtomicAddF32, F32, U64, F32, ) |
| 366 | OPCODE(GlobalAtomicAddF16x2, U32, U64, F16x2, ) | 376 | OPCODE(GlobalAtomicAddF16x2, U32, U64, F16x2, ) |
| 367 | OPCODE(GlobalAtomicAddF32x2, U32, U64, F32x2, ) | 377 | OPCODE(GlobalAtomicAddF32x2, U32, U64, F32x2, ) |
| @@ -390,6 +400,15 @@ OPCODE(StorageAtomicAnd64, U64, U32, | |||
| 390 | OPCODE(StorageAtomicOr64, U64, U32, U32, U64, ) | 400 | OPCODE(StorageAtomicOr64, U64, U32, U32, U64, ) |
| 391 | OPCODE(StorageAtomicXor64, U64, U32, U32, U64, ) | 401 | OPCODE(StorageAtomicXor64, U64, U32, U32, U64, ) |
| 392 | OPCODE(StorageAtomicExchange64, U64, U32, U32, U64, ) | 402 | OPCODE(StorageAtomicExchange64, U64, U32, U32, U64, ) |
| 403 | OPCODE(StorageAtomicIAdd32x2, U32x2, U32, U32, U32x2, ) | ||
| 404 | OPCODE(StorageAtomicSMin32x2, U32x2, U32, U32, U32x2, ) | ||
| 405 | OPCODE(StorageAtomicUMin32x2, U32x2, U32, U32, U32x2, ) | ||
| 406 | OPCODE(StorageAtomicSMax32x2, U32x2, U32, U32, U32x2, ) | ||
| 407 | OPCODE(StorageAtomicUMax32x2, U32x2, U32, U32, U32x2, ) | ||
| 408 | OPCODE(StorageAtomicAnd32x2, U32x2, U32, U32, U32x2, ) | ||
| 409 | OPCODE(StorageAtomicOr32x2, U32x2, U32, U32, U32x2, ) | ||
| 410 | OPCODE(StorageAtomicXor32x2, U32x2, U32, U32, U32x2, ) | ||
| 411 | OPCODE(StorageAtomicExchange32x2, U32x2, U32, U32, U32x2, ) | ||
| 393 | OPCODE(StorageAtomicAddF32, F32, U32, U32, F32, ) | 412 | OPCODE(StorageAtomicAddF32, F32, U32, U32, F32, ) |
| 394 | OPCODE(StorageAtomicAddF16x2, U32, U32, U32, F16x2, ) | 413 | OPCODE(StorageAtomicAddF16x2, U32, U32, U32, F16x2, ) |
| 395 | OPCODE(StorageAtomicAddF32x2, U32, U32, U32, F32x2, ) | 414 | OPCODE(StorageAtomicAddF32x2, U32, U32, U32, F32x2, ) |
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/load_constant.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/load_constant.cpp index 2300088e3..8007a4d46 100644 --- a/src/shader_recompiler/frontend/maxwell/translate/impl/load_constant.cpp +++ b/src/shader_recompiler/frontend/maxwell/translate/impl/load_constant.cpp | |||
| @@ -11,10 +11,20 @@ namespace Shader::Maxwell { | |||
| 11 | using namespace LDC; | 11 | using namespace LDC; |
| 12 | namespace { | 12 | namespace { |
| 13 | std::pair<IR::U32, IR::U32> Slot(IR::IREmitter& ir, Mode mode, const IR::U32& imm_index, | 13 | std::pair<IR::U32, IR::U32> Slot(IR::IREmitter& ir, Mode mode, const IR::U32& imm_index, |
| 14 | const IR::U32& reg, const IR::U32& imm) { | 14 | const IR::U32& reg, const IR::U32& imm_offset) { |
| 15 | switch (mode) { | 15 | switch (mode) { |
| 16 | case Mode::Default: | 16 | case Mode::Default: |
| 17 | return {imm_index, ir.IAdd(reg, imm)}; | 17 | return {imm_index, ir.IAdd(reg, imm_offset)}; |
| 18 | case Mode::IS: { | ||
| 19 | // Segmented addressing mode | ||
| 20 | // Ra+imm_offset points into a flat mapping of const buffer | ||
| 21 | // address space | ||
| 22 | const IR::U32 address{ir.IAdd(reg, imm_offset)}; | ||
| 23 | const IR::U32 index{ir.BitFieldExtract(address, ir.Imm32(16), ir.Imm32(16))}; | ||
| 24 | const IR::U32 offset{ir.BitFieldExtract(address, ir.Imm32(0), ir.Imm32(16))}; | ||
| 25 | |||
| 26 | return {ir.IAdd(index, imm_index), offset}; | ||
| 27 | } | ||
| 18 | default: | 28 | default: |
| 19 | break; | 29 | break; |
| 20 | } | 30 | } |
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/logic_operation_three_input.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/logic_operation_three_input.cpp index e0fe47912..f3c7ceb57 100644 --- a/src/shader_recompiler/frontend/maxwell/translate/impl/logic_operation_three_input.cpp +++ b/src/shader_recompiler/frontend/maxwell/translate/impl/logic_operation_three_input.cpp | |||
| @@ -13,59 +13,535 @@ namespace { | |||
| 13 | // Emulate GPU's LOP3.LUT (three-input logic op with 8-bit truth table) | 13 | // Emulate GPU's LOP3.LUT (three-input logic op with 8-bit truth table) |
| 14 | IR::U32 ApplyLUT(IR::IREmitter& ir, const IR::U32& a, const IR::U32& b, const IR::U32& c, | 14 | IR::U32 ApplyLUT(IR::IREmitter& ir, const IR::U32& a, const IR::U32& b, const IR::U32& c, |
| 15 | u64 ttbl) { | 15 | u64 ttbl) { |
| 16 | IR::U32 r{ir.Imm32(0)}; | 16 | switch (ttbl) { |
| 17 | const IR::U32 not_a{ir.BitwiseNot(a)}; | 17 | // generated code, do not edit manually |
| 18 | const IR::U32 not_b{ir.BitwiseNot(b)}; | 18 | case 0: |
| 19 | const IR::U32 not_c{ir.BitwiseNot(c)}; | 19 | return ir.Imm32(0); |
| 20 | if (ttbl & 0x01) { | 20 | case 1: |
| 21 | // r |= ~a & ~b & ~c; | 21 | return ir.BitwiseNot(ir.BitwiseOr(a, ir.BitwiseOr(b, c))); |
| 22 | const auto lhs{ir.BitwiseAnd(not_a, not_b)}; | 22 | case 2: |
| 23 | const auto rhs{ir.BitwiseAnd(lhs, not_c)}; | 23 | return ir.BitwiseAnd(c, ir.BitwiseNot(ir.BitwiseOr(a, b))); |
| 24 | r = ir.BitwiseOr(r, rhs); | 24 | case 3: |
| 25 | return ir.BitwiseNot(ir.BitwiseOr(a, b)); | ||
| 26 | case 4: | ||
| 27 | return ir.BitwiseAnd(b, ir.BitwiseNot(ir.BitwiseOr(a, c))); | ||
| 28 | case 5: | ||
| 29 | return ir.BitwiseNot(ir.BitwiseOr(a, c)); | ||
| 30 | case 6: | ||
| 31 | return ir.BitwiseAnd(ir.BitwiseNot(a), ir.BitwiseXor(b, c)); | ||
| 32 | case 7: | ||
| 33 | return ir.BitwiseNot(ir.BitwiseOr(a, ir.BitwiseAnd(b, c))); | ||
| 34 | case 8: | ||
| 35 | return ir.BitwiseAnd(ir.BitwiseAnd(b, c), ir.BitwiseNot(a)); | ||
| 36 | case 9: | ||
| 37 | return ir.BitwiseNot(ir.BitwiseOr(a, ir.BitwiseXor(b, c))); | ||
| 38 | case 10: | ||
| 39 | return ir.BitwiseAnd(c, ir.BitwiseNot(a)); | ||
| 40 | case 11: | ||
| 41 | return ir.BitwiseAnd(ir.BitwiseNot(a), ir.BitwiseOr(c, ir.BitwiseNot(b))); | ||
| 42 | case 12: | ||
| 43 | return ir.BitwiseAnd(b, ir.BitwiseNot(a)); | ||
| 44 | case 13: | ||
| 45 | return ir.BitwiseAnd(ir.BitwiseNot(a), ir.BitwiseOr(b, ir.BitwiseNot(c))); | ||
| 46 | case 14: | ||
| 47 | return ir.BitwiseAnd(ir.BitwiseNot(a), ir.BitwiseOr(b, c)); | ||
| 48 | case 15: | ||
| 49 | return ir.BitwiseNot(a); | ||
| 50 | case 16: | ||
| 51 | return ir.BitwiseAnd(a, ir.BitwiseNot(ir.BitwiseOr(b, c))); | ||
| 52 | case 17: | ||
| 53 | return ir.BitwiseNot(ir.BitwiseOr(b, c)); | ||
| 54 | case 18: | ||
| 55 | return ir.BitwiseAnd(ir.BitwiseNot(b), ir.BitwiseXor(a, c)); | ||
| 56 | case 19: | ||
| 57 | return ir.BitwiseNot(ir.BitwiseOr(b, ir.BitwiseAnd(a, c))); | ||
| 58 | case 20: | ||
| 59 | return ir.BitwiseAnd(ir.BitwiseNot(c), ir.BitwiseXor(a, b)); | ||
| 60 | case 21: | ||
| 61 | return ir.BitwiseNot(ir.BitwiseOr(c, ir.BitwiseAnd(a, b))); | ||
| 62 | case 22: | ||
| 63 | return ir.BitwiseXor(ir.BitwiseOr(a, b), ir.BitwiseOr(c, ir.BitwiseAnd(a, b))); | ||
| 64 | case 23: | ||
| 65 | return ir.BitwiseXor(ir.BitwiseAnd(ir.BitwiseXor(a, b), ir.BitwiseXor(a, c)), | ||
| 66 | ir.BitwiseNot(a)); | ||
| 67 | case 24: | ||
| 68 | return ir.BitwiseAnd(ir.BitwiseXor(a, b), ir.BitwiseXor(a, c)); | ||
| 69 | case 25: | ||
| 70 | return ir.BitwiseNot(ir.BitwiseOr(ir.BitwiseAnd(a, b), ir.BitwiseXor(b, c))); | ||
| 71 | case 26: | ||
| 72 | return ir.BitwiseAnd(ir.BitwiseOr(c, ir.BitwiseNot(b)), ir.BitwiseXor(a, c)); | ||
| 73 | case 27: | ||
| 74 | return ir.BitwiseXor(ir.BitwiseOr(a, ir.BitwiseNot(c)), ir.BitwiseOr(b, c)); | ||
| 75 | case 28: | ||
| 76 | return ir.BitwiseAnd(ir.BitwiseOr(b, ir.BitwiseNot(c)), ir.BitwiseXor(a, b)); | ||
| 77 | case 29: | ||
| 78 | return ir.BitwiseXor(ir.BitwiseOr(a, ir.BitwiseNot(b)), ir.BitwiseOr(b, c)); | ||
| 79 | case 30: | ||
| 80 | return ir.BitwiseXor(a, ir.BitwiseOr(b, c)); | ||
| 81 | case 31: | ||
| 82 | return ir.BitwiseNot(ir.BitwiseAnd(a, ir.BitwiseOr(b, c))); | ||
| 83 | case 32: | ||
| 84 | return ir.BitwiseAnd(ir.BitwiseAnd(a, c), ir.BitwiseNot(b)); | ||
| 85 | case 33: | ||
| 86 | return ir.BitwiseNot(ir.BitwiseOr(b, ir.BitwiseXor(a, c))); | ||
| 87 | case 34: | ||
| 88 | return ir.BitwiseAnd(c, ir.BitwiseNot(b)); | ||
| 89 | case 35: | ||
| 90 | return ir.BitwiseAnd(ir.BitwiseNot(b), ir.BitwiseOr(c, ir.BitwiseNot(a))); | ||
| 91 | case 36: | ||
| 92 | return ir.BitwiseAnd(ir.BitwiseXor(a, b), ir.BitwiseXor(b, c)); | ||
| 93 | case 37: | ||
| 94 | return ir.BitwiseNot(ir.BitwiseOr(ir.BitwiseAnd(a, b), ir.BitwiseXor(a, c))); | ||
| 95 | case 38: | ||
| 96 | return ir.BitwiseAnd(ir.BitwiseOr(c, ir.BitwiseNot(a)), ir.BitwiseXor(b, c)); | ||
| 97 | case 39: | ||
| 98 | return ir.BitwiseXor(ir.BitwiseOr(a, c), ir.BitwiseOr(b, ir.BitwiseNot(c))); | ||
| 99 | case 40: | ||
| 100 | return ir.BitwiseAnd(c, ir.BitwiseXor(a, b)); | ||
| 101 | case 41: | ||
| 102 | return ir.BitwiseXor(ir.BitwiseOr(a, b), | ||
| 103 | ir.BitwiseOr(ir.BitwiseAnd(a, b), ir.BitwiseNot(c))); | ||
| 104 | case 42: | ||
| 105 | return ir.BitwiseAnd(c, ir.BitwiseNot(ir.BitwiseAnd(a, b))); | ||
| 106 | case 43: | ||
| 107 | return ir.BitwiseXor(ir.BitwiseOr(a, ir.BitwiseNot(c)), | ||
| 108 | ir.BitwiseOr(b, ir.BitwiseXor(a, c))); | ||
| 109 | case 44: | ||
| 110 | return ir.BitwiseAnd(ir.BitwiseOr(b, c), ir.BitwiseXor(a, b)); | ||
| 111 | case 45: | ||
| 112 | return ir.BitwiseXor(a, ir.BitwiseOr(b, ir.BitwiseNot(c))); | ||
| 113 | case 46: | ||
| 114 | return ir.BitwiseXor(ir.BitwiseAnd(a, b), ir.BitwiseOr(b, c)); | ||
| 115 | case 47: | ||
| 116 | return ir.BitwiseOr(ir.BitwiseAnd(c, ir.BitwiseNot(b)), ir.BitwiseNot(a)); | ||
| 117 | case 48: | ||
| 118 | return ir.BitwiseAnd(a, ir.BitwiseNot(b)); | ||
| 119 | case 49: | ||
| 120 | return ir.BitwiseAnd(ir.BitwiseNot(b), ir.BitwiseOr(a, ir.BitwiseNot(c))); | ||
| 121 | case 50: | ||
| 122 | return ir.BitwiseAnd(ir.BitwiseNot(b), ir.BitwiseOr(a, c)); | ||
| 123 | case 51: | ||
| 124 | return ir.BitwiseNot(b); | ||
| 125 | case 52: | ||
| 126 | return ir.BitwiseAnd(ir.BitwiseOr(a, ir.BitwiseNot(c)), ir.BitwiseXor(a, b)); | ||
| 127 | case 53: | ||
| 128 | return ir.BitwiseXor(ir.BitwiseOr(a, c), ir.BitwiseOr(b, ir.BitwiseNot(a))); | ||
| 129 | case 54: | ||
| 130 | return ir.BitwiseXor(b, ir.BitwiseOr(a, c)); | ||
| 131 | case 55: | ||
| 132 | return ir.BitwiseNot(ir.BitwiseAnd(b, ir.BitwiseOr(a, c))); | ||
| 133 | case 56: | ||
| 134 | return ir.BitwiseAnd(ir.BitwiseOr(a, c), ir.BitwiseXor(a, b)); | ||
| 135 | case 57: | ||
| 136 | return ir.BitwiseXor(b, ir.BitwiseOr(a, ir.BitwiseNot(c))); | ||
| 137 | case 58: | ||
| 138 | return ir.BitwiseXor(ir.BitwiseAnd(a, b), ir.BitwiseOr(a, c)); | ||
| 139 | case 59: | ||
| 140 | return ir.BitwiseOr(ir.BitwiseAnd(c, ir.BitwiseNot(a)), ir.BitwiseNot(b)); | ||
| 141 | case 60: | ||
| 142 | return ir.BitwiseXor(a, b); | ||
| 143 | case 61: | ||
| 144 | return ir.BitwiseOr(ir.BitwiseNot(ir.BitwiseOr(a, c)), ir.BitwiseXor(a, b)); | ||
| 145 | case 62: | ||
| 146 | return ir.BitwiseOr(ir.BitwiseAnd(c, ir.BitwiseNot(a)), ir.BitwiseXor(a, b)); | ||
| 147 | case 63: | ||
| 148 | return ir.BitwiseNot(ir.BitwiseAnd(a, b)); | ||
| 149 | case 64: | ||
| 150 | return ir.BitwiseAnd(ir.BitwiseAnd(a, b), ir.BitwiseNot(c)); | ||
| 151 | case 65: | ||
| 152 | return ir.BitwiseNot(ir.BitwiseOr(c, ir.BitwiseXor(a, b))); | ||
| 153 | case 66: | ||
| 154 | return ir.BitwiseAnd(ir.BitwiseXor(a, c), ir.BitwiseXor(b, c)); | ||
| 155 | case 67: | ||
| 156 | return ir.BitwiseNot(ir.BitwiseOr(ir.BitwiseAnd(a, c), ir.BitwiseXor(a, b))); | ||
| 157 | case 68: | ||
| 158 | return ir.BitwiseAnd(b, ir.BitwiseNot(c)); | ||
| 159 | case 69: | ||
| 160 | return ir.BitwiseAnd(ir.BitwiseNot(c), ir.BitwiseOr(b, ir.BitwiseNot(a))); | ||
| 161 | case 70: | ||
| 162 | return ir.BitwiseAnd(ir.BitwiseOr(b, ir.BitwiseNot(a)), ir.BitwiseXor(b, c)); | ||
| 163 | case 71: | ||
| 164 | return ir.BitwiseXor(ir.BitwiseOr(a, b), ir.BitwiseOr(c, ir.BitwiseNot(b))); | ||
| 165 | case 72: | ||
| 166 | return ir.BitwiseAnd(b, ir.BitwiseXor(a, c)); | ||
| 167 | case 73: | ||
| 168 | return ir.BitwiseXor(ir.BitwiseOr(a, c), | ||
| 169 | ir.BitwiseOr(ir.BitwiseAnd(a, c), ir.BitwiseNot(b))); | ||
| 170 | case 74: | ||
| 171 | return ir.BitwiseAnd(ir.BitwiseOr(b, c), ir.BitwiseXor(a, c)); | ||
| 172 | case 75: | ||
| 173 | return ir.BitwiseXor(a, ir.BitwiseOr(c, ir.BitwiseNot(b))); | ||
| 174 | case 76: | ||
| 175 | return ir.BitwiseAnd(b, ir.BitwiseNot(ir.BitwiseAnd(a, c))); | ||
| 176 | case 77: | ||
| 177 | return ir.BitwiseXor(ir.BitwiseOr(a, ir.BitwiseNot(b)), | ||
| 178 | ir.BitwiseOr(c, ir.BitwiseXor(a, b))); | ||
| 179 | case 78: | ||
| 180 | return ir.BitwiseXor(ir.BitwiseAnd(a, c), ir.BitwiseOr(b, c)); | ||
| 181 | case 79: | ||
| 182 | return ir.BitwiseOr(ir.BitwiseAnd(b, ir.BitwiseNot(c)), ir.BitwiseNot(a)); | ||
| 183 | case 80: | ||
| 184 | return ir.BitwiseAnd(a, ir.BitwiseNot(c)); | ||
| 185 | case 81: | ||
| 186 | return ir.BitwiseAnd(ir.BitwiseNot(c), ir.BitwiseOr(a, ir.BitwiseNot(b))); | ||
| 187 | case 82: | ||
| 188 | return ir.BitwiseAnd(ir.BitwiseOr(a, ir.BitwiseNot(b)), ir.BitwiseXor(a, c)); | ||
| 189 | case 83: | ||
| 190 | return ir.BitwiseXor(ir.BitwiseOr(a, b), ir.BitwiseOr(c, ir.BitwiseNot(a))); | ||
| 191 | case 84: | ||
| 192 | return ir.BitwiseAnd(ir.BitwiseNot(c), ir.BitwiseOr(a, b)); | ||
| 193 | case 85: | ||
| 194 | return ir.BitwiseNot(c); | ||
| 195 | case 86: | ||
| 196 | return ir.BitwiseXor(c, ir.BitwiseOr(a, b)); | ||
| 197 | case 87: | ||
| 198 | return ir.BitwiseNot(ir.BitwiseAnd(c, ir.BitwiseOr(a, b))); | ||
| 199 | case 88: | ||
| 200 | return ir.BitwiseAnd(ir.BitwiseOr(a, b), ir.BitwiseXor(a, c)); | ||
| 201 | case 89: | ||
| 202 | return ir.BitwiseXor(c, ir.BitwiseOr(a, ir.BitwiseNot(b))); | ||
| 203 | case 90: | ||
| 204 | return ir.BitwiseXor(a, c); | ||
| 205 | case 91: | ||
| 206 | return ir.BitwiseOr(ir.BitwiseNot(ir.BitwiseOr(a, b)), ir.BitwiseXor(a, c)); | ||
| 207 | case 92: | ||
| 208 | return ir.BitwiseXor(ir.BitwiseAnd(a, c), ir.BitwiseOr(a, b)); | ||
| 209 | case 93: | ||
| 210 | return ir.BitwiseOr(ir.BitwiseAnd(b, ir.BitwiseNot(a)), ir.BitwiseNot(c)); | ||
| 211 | case 94: | ||
| 212 | return ir.BitwiseOr(ir.BitwiseAnd(b, ir.BitwiseNot(a)), ir.BitwiseXor(a, c)); | ||
| 213 | case 95: | ||
| 214 | return ir.BitwiseNot(ir.BitwiseAnd(a, c)); | ||
| 215 | case 96: | ||
| 216 | return ir.BitwiseAnd(a, ir.BitwiseXor(b, c)); | ||
| 217 | case 97: | ||
| 218 | return ir.BitwiseXor(ir.BitwiseOr(b, c), | ||
| 219 | ir.BitwiseOr(ir.BitwiseAnd(b, c), ir.BitwiseNot(a))); | ||
| 220 | case 98: | ||
| 221 | return ir.BitwiseAnd(ir.BitwiseOr(a, c), ir.BitwiseXor(b, c)); | ||
| 222 | case 99: | ||
| 223 | return ir.BitwiseXor(b, ir.BitwiseOr(c, ir.BitwiseNot(a))); | ||
| 224 | case 100: | ||
| 225 | return ir.BitwiseAnd(ir.BitwiseOr(a, b), ir.BitwiseXor(b, c)); | ||
| 226 | case 101: | ||
| 227 | return ir.BitwiseXor(c, ir.BitwiseOr(b, ir.BitwiseNot(a))); | ||
| 228 | case 102: | ||
| 229 | return ir.BitwiseXor(b, c); | ||
| 230 | case 103: | ||
| 231 | return ir.BitwiseOr(ir.BitwiseNot(ir.BitwiseOr(a, b)), ir.BitwiseXor(b, c)); | ||
| 232 | case 104: | ||
| 233 | return ir.BitwiseAnd(ir.BitwiseOr(a, b), ir.BitwiseXor(c, ir.BitwiseAnd(a, b))); | ||
| 234 | case 105: | ||
| 235 | return ir.BitwiseXor(ir.BitwiseNot(a), ir.BitwiseXor(b, c)); | ||
| 236 | case 106: | ||
| 237 | return ir.BitwiseXor(c, ir.BitwiseAnd(a, b)); | ||
| 238 | case 107: | ||
| 239 | return ir.BitwiseXor(ir.BitwiseAnd(c, ir.BitwiseOr(a, b)), | ||
| 240 | ir.BitwiseXor(a, ir.BitwiseNot(b))); | ||
| 241 | case 108: | ||
| 242 | return ir.BitwiseXor(b, ir.BitwiseAnd(a, c)); | ||
| 243 | case 109: | ||
| 244 | return ir.BitwiseXor(ir.BitwiseAnd(b, ir.BitwiseOr(a, c)), | ||
| 245 | ir.BitwiseXor(a, ir.BitwiseNot(c))); | ||
| 246 | case 110: | ||
| 247 | return ir.BitwiseOr(ir.BitwiseAnd(b, ir.BitwiseNot(a)), ir.BitwiseXor(b, c)); | ||
| 248 | case 111: | ||
| 249 | return ir.BitwiseOr(ir.BitwiseNot(a), ir.BitwiseXor(b, c)); | ||
| 250 | case 112: | ||
| 251 | return ir.BitwiseAnd(a, ir.BitwiseNot(ir.BitwiseAnd(b, c))); | ||
| 252 | case 113: | ||
| 253 | return ir.BitwiseXor(ir.BitwiseOr(b, ir.BitwiseNot(a)), | ||
| 254 | ir.BitwiseOr(c, ir.BitwiseXor(a, b))); | ||
| 255 | case 114: | ||
| 256 | return ir.BitwiseXor(ir.BitwiseAnd(b, c), ir.BitwiseOr(a, c)); | ||
| 257 | case 115: | ||
| 258 | return ir.BitwiseOr(ir.BitwiseAnd(a, ir.BitwiseNot(c)), ir.BitwiseNot(b)); | ||
| 259 | case 116: | ||
| 260 | return ir.BitwiseXor(ir.BitwiseAnd(b, c), ir.BitwiseOr(a, b)); | ||
| 261 | case 117: | ||
| 262 | return ir.BitwiseOr(ir.BitwiseAnd(a, ir.BitwiseNot(b)), ir.BitwiseNot(c)); | ||
| 263 | case 118: | ||
| 264 | return ir.BitwiseOr(ir.BitwiseAnd(a, ir.BitwiseNot(b)), ir.BitwiseXor(b, c)); | ||
| 265 | case 119: | ||
| 266 | return ir.BitwiseNot(ir.BitwiseAnd(b, c)); | ||
| 267 | case 120: | ||
| 268 | return ir.BitwiseXor(a, ir.BitwiseAnd(b, c)); | ||
| 269 | case 121: | ||
| 270 | return ir.BitwiseXor(ir.BitwiseAnd(a, ir.BitwiseOr(b, c)), | ||
| 271 | ir.BitwiseXor(b, ir.BitwiseNot(c))); | ||
| 272 | case 122: | ||
| 273 | return ir.BitwiseOr(ir.BitwiseAnd(a, ir.BitwiseNot(b)), ir.BitwiseXor(a, c)); | ||
| 274 | case 123: | ||
| 275 | return ir.BitwiseOr(ir.BitwiseNot(b), ir.BitwiseXor(a, c)); | ||
| 276 | case 124: | ||
| 277 | return ir.BitwiseOr(ir.BitwiseAnd(a, ir.BitwiseNot(c)), ir.BitwiseXor(a, b)); | ||
| 278 | case 125: | ||
| 279 | return ir.BitwiseOr(ir.BitwiseNot(c), ir.BitwiseXor(a, b)); | ||
| 280 | case 126: | ||
| 281 | return ir.BitwiseOr(ir.BitwiseXor(a, b), ir.BitwiseXor(a, c)); | ||
| 282 | case 127: | ||
| 283 | return ir.BitwiseNot(ir.BitwiseAnd(a, ir.BitwiseAnd(b, c))); | ||
| 284 | case 128: | ||
| 285 | return ir.BitwiseAnd(a, ir.BitwiseAnd(b, c)); | ||
| 286 | case 129: | ||
| 287 | return ir.BitwiseNot(ir.BitwiseOr(ir.BitwiseXor(a, b), ir.BitwiseXor(a, c))); | ||
| 288 | case 130: | ||
| 289 | return ir.BitwiseAnd(c, ir.BitwiseXor(a, ir.BitwiseNot(b))); | ||
| 290 | case 131: | ||
| 291 | return ir.BitwiseAnd(ir.BitwiseOr(c, ir.BitwiseNot(a)), ir.BitwiseXor(a, ir.BitwiseNot(b))); | ||
| 292 | case 132: | ||
| 293 | return ir.BitwiseAnd(b, ir.BitwiseXor(a, ir.BitwiseNot(c))); | ||
| 294 | case 133: | ||
| 295 | return ir.BitwiseAnd(ir.BitwiseOr(b, ir.BitwiseNot(a)), ir.BitwiseXor(a, ir.BitwiseNot(c))); | ||
| 296 | case 134: | ||
| 297 | return ir.BitwiseAnd(ir.BitwiseOr(b, c), ir.BitwiseXor(a, ir.BitwiseXor(b, c))); | ||
| 298 | case 135: | ||
| 299 | return ir.BitwiseXor(ir.BitwiseAnd(b, c), ir.BitwiseNot(a)); | ||
| 300 | case 136: | ||
| 301 | return ir.BitwiseAnd(b, c); | ||
| 302 | case 137: | ||
| 303 | return ir.BitwiseAnd(ir.BitwiseOr(b, ir.BitwiseNot(a)), ir.BitwiseXor(b, ir.BitwiseNot(c))); | ||
| 304 | case 138: | ||
| 305 | return ir.BitwiseAnd(c, ir.BitwiseOr(b, ir.BitwiseNot(a))); | ||
| 306 | case 139: | ||
| 307 | return ir.BitwiseOr(ir.BitwiseAnd(b, c), ir.BitwiseNot(ir.BitwiseOr(a, b))); | ||
| 308 | case 140: | ||
| 309 | return ir.BitwiseAnd(b, ir.BitwiseOr(c, ir.BitwiseNot(a))); | ||
| 310 | case 141: | ||
| 311 | return ir.BitwiseOr(ir.BitwiseAnd(b, c), ir.BitwiseNot(ir.BitwiseOr(a, c))); | ||
| 312 | case 142: | ||
| 313 | return ir.BitwiseXor(a, ir.BitwiseOr(ir.BitwiseXor(a, b), ir.BitwiseXor(a, c))); | ||
| 314 | case 143: | ||
| 315 | return ir.BitwiseOr(ir.BitwiseAnd(b, c), ir.BitwiseNot(a)); | ||
| 316 | case 144: | ||
| 317 | return ir.BitwiseAnd(a, ir.BitwiseXor(b, ir.BitwiseNot(c))); | ||
| 318 | case 145: | ||
| 319 | return ir.BitwiseAnd(ir.BitwiseOr(a, ir.BitwiseNot(b)), ir.BitwiseXor(b, ir.BitwiseNot(c))); | ||
| 320 | case 146: | ||
| 321 | return ir.BitwiseAnd(ir.BitwiseOr(a, c), ir.BitwiseXor(a, ir.BitwiseXor(b, c))); | ||
| 322 | case 147: | ||
| 323 | return ir.BitwiseXor(ir.BitwiseAnd(a, c), ir.BitwiseNot(b)); | ||
| 324 | case 148: | ||
| 325 | return ir.BitwiseAnd(ir.BitwiseOr(a, b), ir.BitwiseXor(a, ir.BitwiseXor(b, c))); | ||
| 326 | case 149: | ||
| 327 | return ir.BitwiseXor(ir.BitwiseAnd(a, b), ir.BitwiseNot(c)); | ||
| 328 | case 150: | ||
| 329 | return ir.BitwiseXor(a, ir.BitwiseXor(b, c)); | ||
| 330 | case 151: | ||
| 331 | return ir.BitwiseOr(ir.BitwiseNot(ir.BitwiseOr(a, b)), | ||
| 332 | ir.BitwiseXor(a, ir.BitwiseXor(b, c))); | ||
| 333 | case 152: | ||
| 334 | return ir.BitwiseAnd(ir.BitwiseOr(a, b), ir.BitwiseXor(b, ir.BitwiseNot(c))); | ||
| 335 | case 153: | ||
| 336 | return ir.BitwiseXor(b, ir.BitwiseNot(c)); | ||
| 337 | case 154: | ||
| 338 | return ir.BitwiseXor(c, ir.BitwiseAnd(a, ir.BitwiseNot(b))); | ||
| 339 | case 155: | ||
| 340 | return ir.BitwiseNot(ir.BitwiseAnd(ir.BitwiseOr(a, b), ir.BitwiseXor(b, c))); | ||
| 341 | case 156: | ||
| 342 | return ir.BitwiseXor(b, ir.BitwiseAnd(a, ir.BitwiseNot(c))); | ||
| 343 | case 157: | ||
| 344 | return ir.BitwiseNot(ir.BitwiseAnd(ir.BitwiseOr(a, c), ir.BitwiseXor(b, c))); | ||
| 345 | case 158: | ||
| 346 | return ir.BitwiseOr(ir.BitwiseAnd(b, c), ir.BitwiseXor(a, ir.BitwiseOr(b, c))); | ||
| 347 | case 159: | ||
| 348 | return ir.BitwiseNot(ir.BitwiseAnd(a, ir.BitwiseXor(b, c))); | ||
| 349 | case 160: | ||
| 350 | return ir.BitwiseAnd(a, c); | ||
| 351 | case 161: | ||
| 352 | return ir.BitwiseAnd(ir.BitwiseOr(a, ir.BitwiseNot(b)), ir.BitwiseXor(a, ir.BitwiseNot(c))); | ||
| 353 | case 162: | ||
| 354 | return ir.BitwiseAnd(c, ir.BitwiseOr(a, ir.BitwiseNot(b))); | ||
| 355 | case 163: | ||
| 356 | return ir.BitwiseOr(ir.BitwiseAnd(a, c), ir.BitwiseNot(ir.BitwiseOr(a, b))); | ||
| 357 | case 164: | ||
| 358 | return ir.BitwiseAnd(ir.BitwiseOr(a, b), ir.BitwiseXor(a, ir.BitwiseNot(c))); | ||
| 359 | case 165: | ||
| 360 | return ir.BitwiseXor(a, ir.BitwiseNot(c)); | ||
| 361 | case 166: | ||
| 362 | return ir.BitwiseXor(c, ir.BitwiseAnd(b, ir.BitwiseNot(a))); | ||
| 363 | case 167: | ||
| 364 | return ir.BitwiseNot(ir.BitwiseAnd(ir.BitwiseOr(a, b), ir.BitwiseXor(a, c))); | ||
| 365 | case 168: | ||
| 366 | return ir.BitwiseAnd(c, ir.BitwiseOr(a, b)); | ||
| 367 | case 169: | ||
| 368 | return ir.BitwiseXor(ir.BitwiseNot(c), ir.BitwiseOr(a, b)); | ||
| 369 | case 170: | ||
| 370 | return c; | ||
| 371 | case 171: | ||
| 372 | return ir.BitwiseOr(c, ir.BitwiseNot(ir.BitwiseOr(a, b))); | ||
| 373 | case 172: | ||
| 374 | return ir.BitwiseAnd(ir.BitwiseOr(a, b), ir.BitwiseOr(c, ir.BitwiseNot(a))); | ||
| 375 | case 173: | ||
| 376 | return ir.BitwiseOr(ir.BitwiseAnd(b, c), ir.BitwiseXor(a, ir.BitwiseNot(c))); | ||
| 377 | case 174: | ||
| 378 | return ir.BitwiseOr(c, ir.BitwiseAnd(b, ir.BitwiseNot(a))); | ||
| 379 | case 175: | ||
| 380 | return ir.BitwiseOr(c, ir.BitwiseNot(a)); | ||
| 381 | case 176: | ||
| 382 | return ir.BitwiseAnd(a, ir.BitwiseOr(c, ir.BitwiseNot(b))); | ||
| 383 | case 177: | ||
| 384 | return ir.BitwiseOr(ir.BitwiseAnd(a, c), ir.BitwiseNot(ir.BitwiseOr(b, c))); | ||
| 385 | case 178: | ||
| 386 | return ir.BitwiseXor(b, ir.BitwiseOr(ir.BitwiseXor(a, b), ir.BitwiseXor(a, c))); | ||
| 387 | case 179: | ||
| 388 | return ir.BitwiseOr(ir.BitwiseAnd(a, c), ir.BitwiseNot(b)); | ||
| 389 | case 180: | ||
| 390 | return ir.BitwiseXor(a, ir.BitwiseAnd(b, ir.BitwiseNot(c))); | ||
| 391 | case 181: | ||
| 392 | return ir.BitwiseNot(ir.BitwiseAnd(ir.BitwiseOr(b, c), ir.BitwiseXor(a, c))); | ||
| 393 | case 182: | ||
| 394 | return ir.BitwiseOr(ir.BitwiseAnd(a, c), ir.BitwiseXor(b, ir.BitwiseOr(a, c))); | ||
| 395 | case 183: | ||
| 396 | return ir.BitwiseNot(ir.BitwiseAnd(b, ir.BitwiseXor(a, c))); | ||
| 397 | case 184: | ||
| 398 | return ir.BitwiseAnd(ir.BitwiseOr(a, b), ir.BitwiseOr(c, ir.BitwiseNot(b))); | ||
| 399 | case 185: | ||
| 400 | return ir.BitwiseOr(ir.BitwiseAnd(a, c), ir.BitwiseXor(b, ir.BitwiseNot(c))); | ||
| 401 | case 186: | ||
| 402 | return ir.BitwiseOr(c, ir.BitwiseAnd(a, ir.BitwiseNot(b))); | ||
| 403 | case 187: | ||
| 404 | return ir.BitwiseOr(c, ir.BitwiseNot(b)); | ||
| 405 | case 188: | ||
| 406 | return ir.BitwiseOr(ir.BitwiseAnd(a, c), ir.BitwiseXor(a, b)); | ||
| 407 | case 189: | ||
| 408 | return ir.BitwiseOr(ir.BitwiseXor(a, b), ir.BitwiseXor(a, ir.BitwiseNot(c))); | ||
| 409 | case 190: | ||
| 410 | return ir.BitwiseOr(c, ir.BitwiseXor(a, b)); | ||
| 411 | case 191: | ||
| 412 | return ir.BitwiseOr(c, ir.BitwiseNot(ir.BitwiseAnd(a, b))); | ||
| 413 | case 192: | ||
| 414 | return ir.BitwiseAnd(a, b); | ||
| 415 | case 193: | ||
| 416 | return ir.BitwiseAnd(ir.BitwiseOr(a, ir.BitwiseNot(c)), ir.BitwiseXor(a, ir.BitwiseNot(b))); | ||
| 417 | case 194: | ||
| 418 | return ir.BitwiseAnd(ir.BitwiseOr(a, c), ir.BitwiseXor(a, ir.BitwiseNot(b))); | ||
| 419 | case 195: | ||
| 420 | return ir.BitwiseXor(a, ir.BitwiseNot(b)); | ||
| 421 | case 196: | ||
| 422 | return ir.BitwiseAnd(b, ir.BitwiseOr(a, ir.BitwiseNot(c))); | ||
| 423 | case 197: | ||
| 424 | return ir.BitwiseOr(ir.BitwiseAnd(a, b), ir.BitwiseNot(ir.BitwiseOr(a, c))); | ||
| 425 | case 198: | ||
| 426 | return ir.BitwiseXor(b, ir.BitwiseAnd(c, ir.BitwiseNot(a))); | ||
| 427 | case 199: | ||
| 428 | return ir.BitwiseNot(ir.BitwiseAnd(ir.BitwiseOr(a, c), ir.BitwiseXor(a, b))); | ||
| 429 | case 200: | ||
| 430 | return ir.BitwiseAnd(b, ir.BitwiseOr(a, c)); | ||
| 431 | case 201: | ||
| 432 | return ir.BitwiseXor(ir.BitwiseNot(b), ir.BitwiseOr(a, c)); | ||
| 433 | case 202: | ||
| 434 | return ir.BitwiseAnd(ir.BitwiseOr(a, c), ir.BitwiseOr(b, ir.BitwiseNot(a))); | ||
| 435 | case 203: | ||
| 436 | return ir.BitwiseOr(ir.BitwiseAnd(b, c), ir.BitwiseXor(a, ir.BitwiseNot(b))); | ||
| 437 | case 204: | ||
| 438 | return b; | ||
| 439 | case 205: | ||
| 440 | return ir.BitwiseOr(b, ir.BitwiseNot(ir.BitwiseOr(a, c))); | ||
| 441 | case 206: | ||
| 442 | return ir.BitwiseOr(b, ir.BitwiseAnd(c, ir.BitwiseNot(a))); | ||
| 443 | case 207: | ||
| 444 | return ir.BitwiseOr(b, ir.BitwiseNot(a)); | ||
| 445 | case 208: | ||
| 446 | return ir.BitwiseAnd(a, ir.BitwiseOr(b, ir.BitwiseNot(c))); | ||
| 447 | case 209: | ||
| 448 | return ir.BitwiseOr(ir.BitwiseAnd(a, b), ir.BitwiseNot(ir.BitwiseOr(b, c))); | ||
| 449 | case 210: | ||
| 450 | return ir.BitwiseXor(a, ir.BitwiseAnd(c, ir.BitwiseNot(b))); | ||
| 451 | case 211: | ||
| 452 | return ir.BitwiseNot(ir.BitwiseAnd(ir.BitwiseOr(b, c), ir.BitwiseXor(a, b))); | ||
| 453 | case 212: | ||
| 454 | return ir.BitwiseXor(c, ir.BitwiseOr(ir.BitwiseXor(a, b), ir.BitwiseXor(a, c))); | ||
| 455 | case 213: | ||
| 456 | return ir.BitwiseOr(ir.BitwiseAnd(a, b), ir.BitwiseNot(c)); | ||
| 457 | case 214: | ||
| 458 | return ir.BitwiseOr(ir.BitwiseAnd(a, b), ir.BitwiseXor(c, ir.BitwiseOr(a, b))); | ||
| 459 | case 215: | ||
| 460 | return ir.BitwiseNot(ir.BitwiseAnd(c, ir.BitwiseXor(a, b))); | ||
| 461 | case 216: | ||
| 462 | return ir.BitwiseAnd(ir.BitwiseOr(a, c), ir.BitwiseOr(b, ir.BitwiseNot(c))); | ||
| 463 | case 217: | ||
| 464 | return ir.BitwiseOr(ir.BitwiseAnd(a, b), ir.BitwiseXor(b, ir.BitwiseNot(c))); | ||
| 465 | case 218: | ||
| 466 | return ir.BitwiseOr(ir.BitwiseAnd(a, b), ir.BitwiseXor(a, c)); | ||
| 467 | case 219: | ||
| 468 | return ir.BitwiseOr(ir.BitwiseXor(a, c), ir.BitwiseXor(a, ir.BitwiseNot(b))); | ||
| 469 | case 220: | ||
| 470 | return ir.BitwiseOr(b, ir.BitwiseAnd(a, ir.BitwiseNot(c))); | ||
| 471 | case 221: | ||
| 472 | return ir.BitwiseOr(b, ir.BitwiseNot(c)); | ||
| 473 | case 222: | ||
| 474 | return ir.BitwiseOr(b, ir.BitwiseXor(a, c)); | ||
| 475 | case 223: | ||
| 476 | return ir.BitwiseOr(b, ir.BitwiseNot(ir.BitwiseAnd(a, c))); | ||
| 477 | case 224: | ||
| 478 | return ir.BitwiseAnd(a, ir.BitwiseOr(b, c)); | ||
| 479 | case 225: | ||
| 480 | return ir.BitwiseXor(ir.BitwiseNot(a), ir.BitwiseOr(b, c)); | ||
| 481 | case 226: | ||
| 482 | return ir.BitwiseAnd(ir.BitwiseOr(a, ir.BitwiseNot(b)), ir.BitwiseOr(b, c)); | ||
| 483 | case 227: | ||
| 484 | return ir.BitwiseOr(ir.BitwiseAnd(a, c), ir.BitwiseXor(a, ir.BitwiseNot(b))); | ||
| 485 | case 228: | ||
| 486 | return ir.BitwiseAnd(ir.BitwiseOr(a, ir.BitwiseNot(c)), ir.BitwiseOr(b, c)); | ||
| 487 | case 229: | ||
| 488 | return ir.BitwiseOr(ir.BitwiseAnd(a, b), ir.BitwiseXor(a, ir.BitwiseNot(c))); | ||
| 489 | case 230: | ||
| 490 | return ir.BitwiseOr(ir.BitwiseAnd(a, b), ir.BitwiseXor(b, c)); | ||
| 491 | case 231: | ||
| 492 | return ir.BitwiseOr(ir.BitwiseXor(a, ir.BitwiseNot(b)), ir.BitwiseXor(b, c)); | ||
| 493 | case 232: | ||
| 494 | return ir.BitwiseAnd(ir.BitwiseOr(a, b), ir.BitwiseOr(c, ir.BitwiseAnd(a, b))); | ||
| 495 | case 233: | ||
| 496 | return ir.BitwiseOr(ir.BitwiseAnd(a, b), | ||
| 497 | ir.BitwiseXor(ir.BitwiseNot(c), ir.BitwiseOr(a, b))); | ||
| 498 | case 234: | ||
| 499 | return ir.BitwiseOr(c, ir.BitwiseAnd(a, b)); | ||
| 500 | case 235: | ||
| 501 | return ir.BitwiseOr(c, ir.BitwiseXor(a, ir.BitwiseNot(b))); | ||
| 502 | case 236: | ||
| 503 | return ir.BitwiseOr(b, ir.BitwiseAnd(a, c)); | ||
| 504 | case 237: | ||
| 505 | return ir.BitwiseOr(b, ir.BitwiseXor(a, ir.BitwiseNot(c))); | ||
| 506 | case 238: | ||
| 507 | return ir.BitwiseOr(b, c); | ||
| 508 | case 239: | ||
| 509 | return ir.BitwiseOr(ir.BitwiseNot(a), ir.BitwiseOr(b, c)); | ||
| 510 | case 240: | ||
| 511 | return a; | ||
| 512 | case 241: | ||
| 513 | return ir.BitwiseOr(a, ir.BitwiseNot(ir.BitwiseOr(b, c))); | ||
| 514 | case 242: | ||
| 515 | return ir.BitwiseOr(a, ir.BitwiseAnd(c, ir.BitwiseNot(b))); | ||
| 516 | case 243: | ||
| 517 | return ir.BitwiseOr(a, ir.BitwiseNot(b)); | ||
| 518 | case 244: | ||
| 519 | return ir.BitwiseOr(a, ir.BitwiseAnd(b, ir.BitwiseNot(c))); | ||
| 520 | case 245: | ||
| 521 | return ir.BitwiseOr(a, ir.BitwiseNot(c)); | ||
| 522 | case 246: | ||
| 523 | return ir.BitwiseOr(a, ir.BitwiseXor(b, c)); | ||
| 524 | case 247: | ||
| 525 | return ir.BitwiseOr(a, ir.BitwiseNot(ir.BitwiseAnd(b, c))); | ||
| 526 | case 248: | ||
| 527 | return ir.BitwiseOr(a, ir.BitwiseAnd(b, c)); | ||
| 528 | case 249: | ||
| 529 | return ir.BitwiseOr(a, ir.BitwiseXor(b, ir.BitwiseNot(c))); | ||
| 530 | case 250: | ||
| 531 | return ir.BitwiseOr(a, c); | ||
| 532 | case 251: | ||
| 533 | return ir.BitwiseOr(ir.BitwiseNot(b), ir.BitwiseOr(a, c)); | ||
| 534 | case 252: | ||
| 535 | return ir.BitwiseOr(a, b); | ||
| 536 | case 253: | ||
| 537 | return ir.BitwiseOr(ir.BitwiseNot(c), ir.BitwiseOr(a, b)); | ||
| 538 | case 254: | ||
| 539 | return ir.BitwiseOr(a, ir.BitwiseOr(b, c)); | ||
| 540 | case 255: | ||
| 541 | return ir.Imm32(0xFFFFFFFF); | ||
| 542 | // end of generated code | ||
| 25 | } | 543 | } |
| 26 | if (ttbl & 0x02) { | 544 | throw NotImplementedException("LOP3 with out of range ttbl"); |
| 27 | // r |= ~a & ~b & c; | ||
| 28 | const auto lhs{ir.BitwiseAnd(not_a, not_b)}; | ||
| 29 | const auto rhs{ir.BitwiseAnd(lhs, c)}; | ||
| 30 | r = ir.BitwiseOr(r, rhs); | ||
| 31 | } | ||
| 32 | if (ttbl & 0x04) { | ||
| 33 | // r |= ~a & b & ~c; | ||
| 34 | const auto lhs{ir.BitwiseAnd(not_a, b)}; | ||
| 35 | const auto rhs{ir.BitwiseAnd(lhs, not_c)}; | ||
| 36 | r = ir.BitwiseOr(r, rhs); | ||
| 37 | } | ||
| 38 | if (ttbl & 0x08) { | ||
| 39 | // r |= ~a & b & c; | ||
| 40 | const auto lhs{ir.BitwiseAnd(not_a, b)}; | ||
| 41 | const auto rhs{ir.BitwiseAnd(lhs, c)}; | ||
| 42 | r = ir.BitwiseOr(r, rhs); | ||
| 43 | } | ||
| 44 | if (ttbl & 0x10) { | ||
| 45 | // r |= a & ~b & ~c; | ||
| 46 | const auto lhs{ir.BitwiseAnd(a, not_b)}; | ||
| 47 | const auto rhs{ir.BitwiseAnd(lhs, not_c)}; | ||
| 48 | r = ir.BitwiseOr(r, rhs); | ||
| 49 | } | ||
| 50 | if (ttbl & 0x20) { | ||
| 51 | // r |= a & ~b & c; | ||
| 52 | const auto lhs{ir.BitwiseAnd(a, not_b)}; | ||
| 53 | const auto rhs{ir.BitwiseAnd(lhs, c)}; | ||
| 54 | r = ir.BitwiseOr(r, rhs); | ||
| 55 | } | ||
| 56 | if (ttbl & 0x40) { | ||
| 57 | // r |= a & b & ~c; | ||
| 58 | const auto lhs{ir.BitwiseAnd(a, b)}; | ||
| 59 | const auto rhs{ir.BitwiseAnd(lhs, not_c)}; | ||
| 60 | r = ir.BitwiseOr(r, rhs); | ||
| 61 | } | ||
| 62 | if (ttbl & 0x80) { | ||
| 63 | // r |= a & b & c; | ||
| 64 | const auto lhs{ir.BitwiseAnd(a, b)}; | ||
| 65 | const auto rhs{ir.BitwiseAnd(lhs, c)}; | ||
| 66 | r = ir.BitwiseOr(r, rhs); | ||
| 67 | } | ||
| 68 | return r; | ||
| 69 | } | 545 | } |
| 70 | 546 | ||
| 71 | IR::U32 LOP3(TranslatorVisitor& v, u64 insn, const IR::U32& op_b, const IR::U32& op_c, u64 lut) { | 547 | IR::U32 LOP3(TranslatorVisitor& v, u64 insn, const IR::U32& op_b, const IR::U32& op_c, u64 lut) { |
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/logic_operation_three_input_lut3.py b/src/shader_recompiler/frontend/maxwell/translate/impl/logic_operation_three_input_lut3.py new file mode 100644 index 000000000..8f547c266 --- /dev/null +++ b/src/shader_recompiler/frontend/maxwell/translate/impl/logic_operation_three_input_lut3.py | |||
| @@ -0,0 +1,92 @@ | |||
| 1 | # Copyright © 2022 degasus <markus@selfnet.de> | ||
| 2 | # This work is free. You can redistribute it and/or modify it under the | ||
| 3 | # terms of the Do What The Fuck You Want To Public License, Version 2, | ||
| 4 | # as published by Sam Hocevar. See http://www.wtfpl.net/ for more details. | ||
| 5 | |||
| 6 | from itertools import product | ||
| 7 | |||
| 8 | # The primitive instructions | ||
| 9 | OPS = { | ||
| 10 | 'ir.BitwiseAnd({}, {})' : (2, 1, lambda a,b: a&b), | ||
| 11 | 'ir.BitwiseOr({}, {})' : (2, 1, lambda a,b: a|b), | ||
| 12 | 'ir.BitwiseXor({}, {})' : (2, 1, lambda a,b: a^b), | ||
| 13 | 'ir.BitwiseNot({})' : (1, 0.1, lambda a: (~a) & 255), # Only tiny cost, as this can often inlined in other instructions | ||
| 14 | } | ||
| 15 | |||
| 16 | # Our database of combination of instructions | ||
| 17 | optimized_calls = {} | ||
| 18 | def cmp(lhs, rhs): | ||
| 19 | if lhs is None: # new entry | ||
| 20 | return True | ||
| 21 | if lhs[3] > rhs[3]: # costs | ||
| 22 | return True | ||
| 23 | if lhs[3] < rhs[3]: # costs | ||
| 24 | return False | ||
| 25 | if len(lhs[0]) > len(rhs[0]): # string len | ||
| 26 | return True | ||
| 27 | if len(lhs[0]) < len(rhs[0]): # string len | ||
| 28 | return False | ||
| 29 | if lhs[0] > rhs[0]: # string sorting | ||
| 30 | return True | ||
| 31 | if lhs[0] < rhs[0]: # string sorting | ||
| 32 | return False | ||
| 33 | assert lhs == rhs, "redundant instruction, bug in brute force" | ||
| 34 | return False | ||
| 35 | def register(imm, instruction, count, latency): | ||
| 36 | # Use the sum of instruction count and latency as costs to evaluate which combination is best | ||
| 37 | costs = count + latency | ||
| 38 | |||
| 39 | old = optimized_calls.get(imm, None) | ||
| 40 | new = (instruction, count, latency, costs) | ||
| 41 | |||
| 42 | # Update if new or better | ||
| 43 | if cmp(old, new): | ||
| 44 | optimized_calls[imm] = new | ||
| 45 | return True | ||
| 46 | |||
| 47 | return False | ||
| 48 | |||
| 49 | # Constants: 0, 1 (for free) | ||
| 50 | register(0, 'ir.Imm32(0)', 0, 0) | ||
| 51 | register(255, 'ir.Imm32(0xFFFFFFFF)', 0, 0) | ||
| 52 | |||
| 53 | # Inputs: a, b, c (for free) | ||
| 54 | ta = 0xF0 | ||
| 55 | tb = 0xCC | ||
| 56 | tc = 0xAA | ||
| 57 | inputs = { | ||
| 58 | ta : 'a', | ||
| 59 | tb : 'b', | ||
| 60 | tc : 'c', | ||
| 61 | } | ||
| 62 | for imm, instruction in inputs.items(): | ||
| 63 | register(imm, instruction, 0, 0) | ||
| 64 | register((~imm) & 255, 'ir.BitwiseNot({})'.format(instruction), 0.099, 0.099) # slightly cheaper NEG on inputs | ||
| 65 | |||
| 66 | # Try to combine two values from the db with an instruction. | ||
| 67 | # If it is better than the old method, update it. | ||
| 68 | while True: | ||
| 69 | registered = 0 | ||
| 70 | calls_copy = optimized_calls.copy() | ||
| 71 | for OP, (argc, cost, f) in OPS.items(): | ||
| 72 | for args in product(calls_copy.items(), repeat=argc): | ||
| 73 | # unpack(transponse) the arrays | ||
| 74 | imm = [arg[0] for arg in args] | ||
| 75 | value = [arg[1][0] for arg in args] | ||
| 76 | count = [arg[1][1] for arg in args] | ||
| 77 | latency = [arg[1][2] for arg in args] | ||
| 78 | |||
| 79 | registered += register( | ||
| 80 | f(*imm), | ||
| 81 | OP.format(*value), | ||
| 82 | sum(count) + cost, | ||
| 83 | max(latency) + cost) | ||
| 84 | if registered == 0: | ||
| 85 | # No update at all? So terminate | ||
| 86 | break | ||
| 87 | |||
| 88 | # Hacky output. Please improve me to output valid C++ instead. | ||
| 89 | s = """ case {imm}: | ||
| 90 | return {op};""" | ||
| 91 | for imm in range(256): | ||
| 92 | print(s.format(imm=imm, op=optimized_calls[imm][0])) | ||
diff --git a/src/shader_recompiler/frontend/maxwell/translate_program.cpp b/src/shader_recompiler/frontend/maxwell/translate_program.cpp index 248ad3ced..b22725584 100644 --- a/src/shader_recompiler/frontend/maxwell/translate_program.cpp +++ b/src/shader_recompiler/frontend/maxwell/translate_program.cpp | |||
| @@ -212,11 +212,11 @@ IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Blo | |||
| 212 | } | 212 | } |
| 213 | Optimization::SsaRewritePass(program); | 213 | Optimization::SsaRewritePass(program); |
| 214 | 214 | ||
| 215 | Optimization::ConstantPropagationPass(program); | ||
| 216 | |||
| 215 | Optimization::GlobalMemoryToStorageBufferPass(program); | 217 | Optimization::GlobalMemoryToStorageBufferPass(program); |
| 216 | Optimization::TexturePass(env, program); | 218 | Optimization::TexturePass(env, program); |
| 217 | 219 | ||
| 218 | Optimization::ConstantPropagationPass(program); | ||
| 219 | |||
| 220 | if (Settings::values.resolution_info.active) { | 220 | if (Settings::values.resolution_info.active) { |
| 221 | Optimization::RescalingPass(program); | 221 | Optimization::RescalingPass(program); |
| 222 | } | 222 | } |
diff --git a/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp b/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp index b6a20f904..0b2c60842 100644 --- a/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp +++ b/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp | |||
| @@ -29,6 +29,46 @@ void AddConstantBufferDescriptor(Info& info, u32 index, u32 count) { | |||
| 29 | }); | 29 | }); |
| 30 | } | 30 | } |
| 31 | 31 | ||
| 32 | void AddRegisterIndexedLdc(Info& info) { | ||
| 33 | info.uses_cbuf_indirect = true; | ||
| 34 | |||
| 35 | // The shader can use any possible constant buffer | ||
| 36 | info.constant_buffer_mask = (1 << Info::MAX_CBUFS) - 1; | ||
| 37 | |||
| 38 | auto& cbufs{info.constant_buffer_descriptors}; | ||
| 39 | cbufs.clear(); | ||
| 40 | for (u32 i = 0; i < Info::MAX_CBUFS; i++) { | ||
| 41 | cbufs.push_back(ConstantBufferDescriptor{.index = i, .count = 1}); | ||
| 42 | |||
| 43 | // The shader can use any possible access size | ||
| 44 | info.constant_buffer_used_sizes[i] = 0x10'000; | ||
| 45 | } | ||
| 46 | } | ||
| 47 | |||
| 48 | u32 GetElementSize(IR::Type& used_type, Shader::IR::Opcode opcode) { | ||
| 49 | switch (opcode) { | ||
| 50 | case IR::Opcode::GetCbufU8: | ||
| 51 | case IR::Opcode::GetCbufS8: | ||
| 52 | used_type |= IR::Type::U8; | ||
| 53 | return 1; | ||
| 54 | case IR::Opcode::GetCbufU16: | ||
| 55 | case IR::Opcode::GetCbufS16: | ||
| 56 | used_type |= IR::Type::U16; | ||
| 57 | return 2; | ||
| 58 | case IR::Opcode::GetCbufU32: | ||
| 59 | used_type |= IR::Type::U32; | ||
| 60 | return 4; | ||
| 61 | case IR::Opcode::GetCbufF32: | ||
| 62 | used_type |= IR::Type::F32; | ||
| 63 | return 4; | ||
| 64 | case IR::Opcode::GetCbufU32x2: | ||
| 65 | used_type |= IR::Type::U32x2; | ||
| 66 | return 8; | ||
| 67 | default: | ||
| 68 | throw InvalidArgument("Invalid opcode {}", opcode); | ||
| 69 | } | ||
| 70 | } | ||
| 71 | |||
| 32 | void GetPatch(Info& info, IR::Patch patch) { | 72 | void GetPatch(Info& info, IR::Patch patch) { |
| 33 | if (!IR::IsGeneric(patch)) { | 73 | if (!IR::IsGeneric(patch)) { |
| 34 | throw NotImplementedException("Reading non-generic patch {}", patch); | 74 | throw NotImplementedException("Reading non-generic patch {}", patch); |
| @@ -360,6 +400,15 @@ void VisitUsages(Info& info, IR::Inst& inst) { | |||
| 360 | case IR::Opcode::GlobalAtomicOr64: | 400 | case IR::Opcode::GlobalAtomicOr64: |
| 361 | case IR::Opcode::GlobalAtomicXor64: | 401 | case IR::Opcode::GlobalAtomicXor64: |
| 362 | case IR::Opcode::GlobalAtomicExchange64: | 402 | case IR::Opcode::GlobalAtomicExchange64: |
| 403 | case IR::Opcode::GlobalAtomicIAdd32x2: | ||
| 404 | case IR::Opcode::GlobalAtomicSMin32x2: | ||
| 405 | case IR::Opcode::GlobalAtomicUMin32x2: | ||
| 406 | case IR::Opcode::GlobalAtomicSMax32x2: | ||
| 407 | case IR::Opcode::GlobalAtomicUMax32x2: | ||
| 408 | case IR::Opcode::GlobalAtomicAnd32x2: | ||
| 409 | case IR::Opcode::GlobalAtomicOr32x2: | ||
| 410 | case IR::Opcode::GlobalAtomicXor32x2: | ||
| 411 | case IR::Opcode::GlobalAtomicExchange32x2: | ||
| 363 | case IR::Opcode::GlobalAtomicAddF32: | 412 | case IR::Opcode::GlobalAtomicAddF32: |
| 364 | case IR::Opcode::GlobalAtomicAddF16x2: | 413 | case IR::Opcode::GlobalAtomicAddF16x2: |
| 365 | case IR::Opcode::GlobalAtomicAddF32x2: | 414 | case IR::Opcode::GlobalAtomicAddF32x2: |
| @@ -454,42 +503,18 @@ void VisitUsages(Info& info, IR::Inst& inst) { | |||
| 454 | case IR::Opcode::GetCbufU32x2: { | 503 | case IR::Opcode::GetCbufU32x2: { |
| 455 | const IR::Value index{inst.Arg(0)}; | 504 | const IR::Value index{inst.Arg(0)}; |
| 456 | const IR::Value offset{inst.Arg(1)}; | 505 | const IR::Value offset{inst.Arg(1)}; |
| 457 | if (!index.IsImmediate()) { | 506 | if (index.IsImmediate()) { |
| 458 | throw NotImplementedException("Constant buffer with non-immediate index"); | 507 | AddConstantBufferDescriptor(info, index.U32(), 1); |
| 459 | } | 508 | u32 element_size = GetElementSize(info.used_constant_buffer_types, inst.GetOpcode()); |
| 460 | AddConstantBufferDescriptor(info, index.U32(), 1); | 509 | u32& size{info.constant_buffer_used_sizes[index.U32()]}; |
| 461 | u32 element_size{}; | 510 | if (offset.IsImmediate()) { |
| 462 | switch (inst.GetOpcode()) { | 511 | size = Common::AlignUp(std::max(size, offset.U32() + element_size), 16u); |
| 463 | case IR::Opcode::GetCbufU8: | 512 | } else { |
| 464 | case IR::Opcode::GetCbufS8: | 513 | size = 0x10'000; |
| 465 | info.used_constant_buffer_types |= IR::Type::U8; | 514 | } |
| 466 | element_size = 1; | ||
| 467 | break; | ||
| 468 | case IR::Opcode::GetCbufU16: | ||
| 469 | case IR::Opcode::GetCbufS16: | ||
| 470 | info.used_constant_buffer_types |= IR::Type::U16; | ||
| 471 | element_size = 2; | ||
| 472 | break; | ||
| 473 | case IR::Opcode::GetCbufU32: | ||
| 474 | info.used_constant_buffer_types |= IR::Type::U32; | ||
| 475 | element_size = 4; | ||
| 476 | break; | ||
| 477 | case IR::Opcode::GetCbufF32: | ||
| 478 | info.used_constant_buffer_types |= IR::Type::F32; | ||
| 479 | element_size = 4; | ||
| 480 | break; | ||
| 481 | case IR::Opcode::GetCbufU32x2: | ||
| 482 | info.used_constant_buffer_types |= IR::Type::U32x2; | ||
| 483 | element_size = 8; | ||
| 484 | break; | ||
| 485 | default: | ||
| 486 | break; | ||
| 487 | } | ||
| 488 | u32& size{info.constant_buffer_used_sizes[index.U32()]}; | ||
| 489 | if (offset.IsImmediate()) { | ||
| 490 | size = Common::AlignUp(std::max(size, offset.U32() + element_size), 16u); | ||
| 491 | } else { | 515 | } else { |
| 492 | size = 0x10'000; | 516 | AddRegisterIndexedLdc(info); |
| 517 | GetElementSize(info.used_indirect_cbuf_types, inst.GetOpcode()); | ||
| 493 | } | 518 | } |
| 494 | break; | 519 | break; |
| 495 | } | 520 | } |
| @@ -597,6 +622,15 @@ void VisitUsages(Info& info, IR::Inst& inst) { | |||
| 597 | break; | 622 | break; |
| 598 | case IR::Opcode::LoadStorage64: | 623 | case IR::Opcode::LoadStorage64: |
| 599 | case IR::Opcode::WriteStorage64: | 624 | case IR::Opcode::WriteStorage64: |
| 625 | case IR::Opcode::StorageAtomicIAdd32x2: | ||
| 626 | case IR::Opcode::StorageAtomicSMin32x2: | ||
| 627 | case IR::Opcode::StorageAtomicUMin32x2: | ||
| 628 | case IR::Opcode::StorageAtomicSMax32x2: | ||
| 629 | case IR::Opcode::StorageAtomicUMax32x2: | ||
| 630 | case IR::Opcode::StorageAtomicAnd32x2: | ||
| 631 | case IR::Opcode::StorageAtomicOr32x2: | ||
| 632 | case IR::Opcode::StorageAtomicXor32x2: | ||
| 633 | case IR::Opcode::StorageAtomicExchange32x2: | ||
| 600 | info.used_storage_buffer_types |= IR::Type::U32x2; | 634 | info.used_storage_buffer_types |= IR::Type::U32x2; |
| 601 | break; | 635 | break; |
| 602 | case IR::Opcode::LoadStorage128: | 636 | case IR::Opcode::LoadStorage128: |
diff --git a/src/shader_recompiler/ir_opt/global_memory_to_storage_buffer_pass.cpp b/src/shader_recompiler/ir_opt/global_memory_to_storage_buffer_pass.cpp index 4197b0095..ddf497e32 100644 --- a/src/shader_recompiler/ir_opt/global_memory_to_storage_buffer_pass.cpp +++ b/src/shader_recompiler/ir_opt/global_memory_to_storage_buffer_pass.cpp | |||
| @@ -92,6 +92,15 @@ bool IsGlobalMemory(const IR::Inst& inst) { | |||
| 92 | case IR::Opcode::GlobalAtomicOr64: | 92 | case IR::Opcode::GlobalAtomicOr64: |
| 93 | case IR::Opcode::GlobalAtomicXor64: | 93 | case IR::Opcode::GlobalAtomicXor64: |
| 94 | case IR::Opcode::GlobalAtomicExchange64: | 94 | case IR::Opcode::GlobalAtomicExchange64: |
| 95 | case IR::Opcode::GlobalAtomicIAdd32x2: | ||
| 96 | case IR::Opcode::GlobalAtomicSMin32x2: | ||
| 97 | case IR::Opcode::GlobalAtomicUMin32x2: | ||
| 98 | case IR::Opcode::GlobalAtomicSMax32x2: | ||
| 99 | case IR::Opcode::GlobalAtomicUMax32x2: | ||
| 100 | case IR::Opcode::GlobalAtomicAnd32x2: | ||
| 101 | case IR::Opcode::GlobalAtomicOr32x2: | ||
| 102 | case IR::Opcode::GlobalAtomicXor32x2: | ||
| 103 | case IR::Opcode::GlobalAtomicExchange32x2: | ||
| 95 | case IR::Opcode::GlobalAtomicAddF32: | 104 | case IR::Opcode::GlobalAtomicAddF32: |
| 96 | case IR::Opcode::GlobalAtomicAddF16x2: | 105 | case IR::Opcode::GlobalAtomicAddF16x2: |
| 97 | case IR::Opcode::GlobalAtomicAddF32x2: | 106 | case IR::Opcode::GlobalAtomicAddF32x2: |
| @@ -135,6 +144,15 @@ bool IsGlobalMemoryWrite(const IR::Inst& inst) { | |||
| 135 | case IR::Opcode::GlobalAtomicOr64: | 144 | case IR::Opcode::GlobalAtomicOr64: |
| 136 | case IR::Opcode::GlobalAtomicXor64: | 145 | case IR::Opcode::GlobalAtomicXor64: |
| 137 | case IR::Opcode::GlobalAtomicExchange64: | 146 | case IR::Opcode::GlobalAtomicExchange64: |
| 147 | case IR::Opcode::GlobalAtomicIAdd32x2: | ||
| 148 | case IR::Opcode::GlobalAtomicSMin32x2: | ||
| 149 | case IR::Opcode::GlobalAtomicUMin32x2: | ||
| 150 | case IR::Opcode::GlobalAtomicSMax32x2: | ||
| 151 | case IR::Opcode::GlobalAtomicUMax32x2: | ||
| 152 | case IR::Opcode::GlobalAtomicAnd32x2: | ||
| 153 | case IR::Opcode::GlobalAtomicOr32x2: | ||
| 154 | case IR::Opcode::GlobalAtomicXor32x2: | ||
| 155 | case IR::Opcode::GlobalAtomicExchange32x2: | ||
| 138 | case IR::Opcode::GlobalAtomicAddF32: | 156 | case IR::Opcode::GlobalAtomicAddF32: |
| 139 | case IR::Opcode::GlobalAtomicAddF16x2: | 157 | case IR::Opcode::GlobalAtomicAddF16x2: |
| 140 | case IR::Opcode::GlobalAtomicAddF32x2: | 158 | case IR::Opcode::GlobalAtomicAddF32x2: |
| @@ -199,6 +217,8 @@ IR::Opcode GlobalToStorage(IR::Opcode opcode) { | |||
| 199 | return IR::Opcode::StorageAtomicOr32; | 217 | return IR::Opcode::StorageAtomicOr32; |
| 200 | case IR::Opcode::GlobalAtomicXor32: | 218 | case IR::Opcode::GlobalAtomicXor32: |
| 201 | return IR::Opcode::StorageAtomicXor32; | 219 | return IR::Opcode::StorageAtomicXor32; |
| 220 | case IR::Opcode::GlobalAtomicExchange32: | ||
| 221 | return IR::Opcode::StorageAtomicExchange32; | ||
| 202 | case IR::Opcode::GlobalAtomicIAdd64: | 222 | case IR::Opcode::GlobalAtomicIAdd64: |
| 203 | return IR::Opcode::StorageAtomicIAdd64; | 223 | return IR::Opcode::StorageAtomicIAdd64; |
| 204 | case IR::Opcode::GlobalAtomicSMin64: | 224 | case IR::Opcode::GlobalAtomicSMin64: |
| @@ -215,10 +235,26 @@ IR::Opcode GlobalToStorage(IR::Opcode opcode) { | |||
| 215 | return IR::Opcode::StorageAtomicOr64; | 235 | return IR::Opcode::StorageAtomicOr64; |
| 216 | case IR::Opcode::GlobalAtomicXor64: | 236 | case IR::Opcode::GlobalAtomicXor64: |
| 217 | return IR::Opcode::StorageAtomicXor64; | 237 | return IR::Opcode::StorageAtomicXor64; |
| 218 | case IR::Opcode::GlobalAtomicExchange32: | ||
| 219 | return IR::Opcode::StorageAtomicExchange32; | ||
| 220 | case IR::Opcode::GlobalAtomicExchange64: | 238 | case IR::Opcode::GlobalAtomicExchange64: |
| 221 | return IR::Opcode::StorageAtomicExchange64; | 239 | return IR::Opcode::StorageAtomicExchange64; |
| 240 | case IR::Opcode::GlobalAtomicIAdd32x2: | ||
| 241 | return IR::Opcode::StorageAtomicIAdd32x2; | ||
| 242 | case IR::Opcode::GlobalAtomicSMin32x2: | ||
| 243 | return IR::Opcode::StorageAtomicSMin32x2; | ||
| 244 | case IR::Opcode::GlobalAtomicUMin32x2: | ||
| 245 | return IR::Opcode::StorageAtomicUMin32x2; | ||
| 246 | case IR::Opcode::GlobalAtomicSMax32x2: | ||
| 247 | return IR::Opcode::StorageAtomicSMax32x2; | ||
| 248 | case IR::Opcode::GlobalAtomicUMax32x2: | ||
| 249 | return IR::Opcode::StorageAtomicUMax32x2; | ||
| 250 | case IR::Opcode::GlobalAtomicAnd32x2: | ||
| 251 | return IR::Opcode::StorageAtomicAnd32x2; | ||
| 252 | case IR::Opcode::GlobalAtomicOr32x2: | ||
| 253 | return IR::Opcode::StorageAtomicOr32x2; | ||
| 254 | case IR::Opcode::GlobalAtomicXor32x2: | ||
| 255 | return IR::Opcode::StorageAtomicXor32x2; | ||
| 256 | case IR::Opcode::GlobalAtomicExchange32x2: | ||
| 257 | return IR::Opcode::StorageAtomicExchange32x2; | ||
| 222 | case IR::Opcode::GlobalAtomicAddF32: | 258 | case IR::Opcode::GlobalAtomicAddF32: |
| 223 | return IR::Opcode::StorageAtomicAddF32; | 259 | return IR::Opcode::StorageAtomicAddF32; |
| 224 | case IR::Opcode::GlobalAtomicAddF16x2: | 260 | case IR::Opcode::GlobalAtomicAddF16x2: |
| @@ -298,7 +334,8 @@ std::optional<LowAddrInfo> TrackLowAddress(IR::Inst* inst) { | |||
| 298 | /// Tries to track the storage buffer address used by a global memory instruction | 334 | /// Tries to track the storage buffer address used by a global memory instruction |
| 299 | std::optional<StorageBufferAddr> Track(const IR::Value& value, const Bias* bias) { | 335 | std::optional<StorageBufferAddr> Track(const IR::Value& value, const Bias* bias) { |
| 300 | const auto pred{[bias](const IR::Inst* inst) -> std::optional<StorageBufferAddr> { | 336 | const auto pred{[bias](const IR::Inst* inst) -> std::optional<StorageBufferAddr> { |
| 301 | if (inst->GetOpcode() != IR::Opcode::GetCbufU32) { | 337 | if (inst->GetOpcode() != IR::Opcode::GetCbufU32 && |
| 338 | inst->GetOpcode() != IR::Opcode::GetCbufU32x2) { | ||
| 302 | return std::nullopt; | 339 | return std::nullopt; |
| 303 | } | 340 | } |
| 304 | const IR::Value index{inst->Arg(0)}; | 341 | const IR::Value index{inst->Arg(0)}; |
| @@ -454,6 +491,15 @@ void Replace(IR::Block& block, IR::Inst& inst, const IR::U32& storage_index, | |||
| 454 | case IR::Opcode::GlobalAtomicOr64: | 491 | case IR::Opcode::GlobalAtomicOr64: |
| 455 | case IR::Opcode::GlobalAtomicXor64: | 492 | case IR::Opcode::GlobalAtomicXor64: |
| 456 | case IR::Opcode::GlobalAtomicExchange64: | 493 | case IR::Opcode::GlobalAtomicExchange64: |
| 494 | case IR::Opcode::GlobalAtomicIAdd32x2: | ||
| 495 | case IR::Opcode::GlobalAtomicSMin32x2: | ||
| 496 | case IR::Opcode::GlobalAtomicUMin32x2: | ||
| 497 | case IR::Opcode::GlobalAtomicSMax32x2: | ||
| 498 | case IR::Opcode::GlobalAtomicUMax32x2: | ||
| 499 | case IR::Opcode::GlobalAtomicAnd32x2: | ||
| 500 | case IR::Opcode::GlobalAtomicOr32x2: | ||
| 501 | case IR::Opcode::GlobalAtomicXor32x2: | ||
| 502 | case IR::Opcode::GlobalAtomicExchange32x2: | ||
| 457 | case IR::Opcode::GlobalAtomicAddF32: | 503 | case IR::Opcode::GlobalAtomicAddF32: |
| 458 | case IR::Opcode::GlobalAtomicAddF16x2: | 504 | case IR::Opcode::GlobalAtomicAddF16x2: |
| 459 | case IR::Opcode::GlobalAtomicAddF32x2: | 505 | case IR::Opcode::GlobalAtomicAddF32x2: |
diff --git a/src/shader_recompiler/ir_opt/lower_int64_to_int32.cpp b/src/shader_recompiler/ir_opt/lower_int64_to_int32.cpp index e80d3d1d9..c2654cd9b 100644 --- a/src/shader_recompiler/ir_opt/lower_int64_to_int32.cpp +++ b/src/shader_recompiler/ir_opt/lower_int64_to_int32.cpp | |||
| @@ -199,6 +199,26 @@ void Lower(IR::Block& block, IR::Inst& inst) { | |||
| 199 | return ShiftRightLogical64To32(block, inst); | 199 | return ShiftRightLogical64To32(block, inst); |
| 200 | case IR::Opcode::ShiftRightArithmetic64: | 200 | case IR::Opcode::ShiftRightArithmetic64: |
| 201 | return ShiftRightArithmetic64To32(block, inst); | 201 | return ShiftRightArithmetic64To32(block, inst); |
| 202 | case IR::Opcode::SharedAtomicExchange64: | ||
| 203 | return inst.ReplaceOpcode(IR::Opcode::SharedAtomicExchange32x2); | ||
| 204 | case IR::Opcode::GlobalAtomicIAdd64: | ||
| 205 | return inst.ReplaceOpcode(IR::Opcode::GlobalAtomicIAdd32x2); | ||
| 206 | case IR::Opcode::GlobalAtomicSMin64: | ||
| 207 | return inst.ReplaceOpcode(IR::Opcode::GlobalAtomicSMin32x2); | ||
| 208 | case IR::Opcode::GlobalAtomicUMin64: | ||
| 209 | return inst.ReplaceOpcode(IR::Opcode::GlobalAtomicUMin32x2); | ||
| 210 | case IR::Opcode::GlobalAtomicSMax64: | ||
| 211 | return inst.ReplaceOpcode(IR::Opcode::GlobalAtomicSMax32x2); | ||
| 212 | case IR::Opcode::GlobalAtomicUMax64: | ||
| 213 | return inst.ReplaceOpcode(IR::Opcode::GlobalAtomicUMax32x2); | ||
| 214 | case IR::Opcode::GlobalAtomicAnd64: | ||
| 215 | return inst.ReplaceOpcode(IR::Opcode::GlobalAtomicAnd32x2); | ||
| 216 | case IR::Opcode::GlobalAtomicOr64: | ||
| 217 | return inst.ReplaceOpcode(IR::Opcode::GlobalAtomicOr32x2); | ||
| 218 | case IR::Opcode::GlobalAtomicXor64: | ||
| 219 | return inst.ReplaceOpcode(IR::Opcode::GlobalAtomicXor32x2); | ||
| 220 | case IR::Opcode::GlobalAtomicExchange64: | ||
| 221 | return inst.ReplaceOpcode(IR::Opcode::GlobalAtomicExchange32x2); | ||
| 202 | default: | 222 | default: |
| 203 | break; | 223 | break; |
| 204 | } | 224 | } |
diff --git a/src/shader_recompiler/ir_opt/rescaling_pass.cpp b/src/shader_recompiler/ir_opt/rescaling_pass.cpp index c28500dd1..496d4667e 100644 --- a/src/shader_recompiler/ir_opt/rescaling_pass.cpp +++ b/src/shader_recompiler/ir_opt/rescaling_pass.cpp | |||
| @@ -183,6 +183,31 @@ void ScaleIntegerComposite(IR::IREmitter& ir, IR::Inst& inst, const IR::U1& is_s | |||
| 183 | } | 183 | } |
| 184 | } | 184 | } |
| 185 | 185 | ||
| 186 | void ScaleIntegerOffsetComposite(IR::IREmitter& ir, IR::Inst& inst, const IR::U1& is_scaled, | ||
| 187 | size_t index) { | ||
| 188 | const IR::Value composite{inst.Arg(index)}; | ||
| 189 | if (composite.IsEmpty()) { | ||
| 190 | return; | ||
| 191 | } | ||
| 192 | const auto info{inst.Flags<IR::TextureInstInfo>()}; | ||
| 193 | const IR::U32 x{Scale(ir, is_scaled, IR::U32{ir.CompositeExtract(composite, 0)})}; | ||
| 194 | const IR::U32 y{Scale(ir, is_scaled, IR::U32{ir.CompositeExtract(composite, 1)})}; | ||
| 195 | switch (info.type) { | ||
| 196 | case TextureType::ColorArray2D: | ||
| 197 | case TextureType::Color2D: | ||
| 198 | inst.SetArg(index, ir.CompositeConstruct(x, y)); | ||
| 199 | break; | ||
| 200 | case TextureType::Color1D: | ||
| 201 | case TextureType::ColorArray1D: | ||
| 202 | case TextureType::Color3D: | ||
| 203 | case TextureType::ColorCube: | ||
| 204 | case TextureType::ColorArrayCube: | ||
| 205 | case TextureType::Buffer: | ||
| 206 | // Nothing to patch here | ||
| 207 | break; | ||
| 208 | } | ||
| 209 | } | ||
| 210 | |||
| 186 | void SubScaleCoord(IR::IREmitter& ir, IR::Inst& inst, const IR::U1& is_scaled) { | 211 | void SubScaleCoord(IR::IREmitter& ir, IR::Inst& inst, const IR::U1& is_scaled) { |
| 187 | const auto info{inst.Flags<IR::TextureInstInfo>()}; | 212 | const auto info{inst.Flags<IR::TextureInstInfo>()}; |
| 188 | const IR::Value coord{inst.Arg(1)}; | 213 | const IR::Value coord{inst.Arg(1)}; |
| @@ -220,7 +245,7 @@ void SubScaleImageFetch(IR::Block& block, IR::Inst& inst) { | |||
| 220 | const IR::U1 is_scaled{ir.IsTextureScaled(ir.Imm32(info.descriptor_index))}; | 245 | const IR::U1 is_scaled{ir.IsTextureScaled(ir.Imm32(info.descriptor_index))}; |
| 221 | SubScaleCoord(ir, inst, is_scaled); | 246 | SubScaleCoord(ir, inst, is_scaled); |
| 222 | // Scale ImageFetch offset | 247 | // Scale ImageFetch offset |
| 223 | ScaleIntegerComposite(ir, inst, is_scaled, 2); | 248 | ScaleIntegerOffsetComposite(ir, inst, is_scaled, 2); |
| 224 | } | 249 | } |
| 225 | 250 | ||
| 226 | void SubScaleImageRead(IR::Block& block, IR::Inst& inst) { | 251 | void SubScaleImageRead(IR::Block& block, IR::Inst& inst) { |
| @@ -242,7 +267,7 @@ void PatchImageFetch(IR::Block& block, IR::Inst& inst) { | |||
| 242 | const IR::U1 is_scaled{ir.IsTextureScaled(ir.Imm32(info.descriptor_index))}; | 267 | const IR::U1 is_scaled{ir.IsTextureScaled(ir.Imm32(info.descriptor_index))}; |
| 243 | ScaleIntegerComposite(ir, inst, is_scaled, 1); | 268 | ScaleIntegerComposite(ir, inst, is_scaled, 1); |
| 244 | // Scale ImageFetch offset | 269 | // Scale ImageFetch offset |
| 245 | ScaleIntegerComposite(ir, inst, is_scaled, 2); | 270 | ScaleIntegerOffsetComposite(ir, inst, is_scaled, 2); |
| 246 | } | 271 | } |
| 247 | 272 | ||
| 248 | void PatchImageRead(IR::Block& block, IR::Inst& inst) { | 273 | void PatchImageRead(IR::Block& block, IR::Inst& inst) { |
diff --git a/src/shader_recompiler/shader_info.h b/src/shader_recompiler/shader_info.h index 9f375c30e..9d36bd9eb 100644 --- a/src/shader_recompiler/shader_info.h +++ b/src/shader_recompiler/shader_info.h | |||
| @@ -173,9 +173,11 @@ struct Info { | |||
| 173 | bool uses_atomic_image_u32{}; | 173 | bool uses_atomic_image_u32{}; |
| 174 | bool uses_shadow_lod{}; | 174 | bool uses_shadow_lod{}; |
| 175 | bool uses_rescaling_uniform{}; | 175 | bool uses_rescaling_uniform{}; |
| 176 | bool uses_cbuf_indirect{}; | ||
| 176 | 177 | ||
| 177 | IR::Type used_constant_buffer_types{}; | 178 | IR::Type used_constant_buffer_types{}; |
| 178 | IR::Type used_storage_buffer_types{}; | 179 | IR::Type used_storage_buffer_types{}; |
| 180 | IR::Type used_indirect_cbuf_types{}; | ||
| 179 | 181 | ||
| 180 | u32 constant_buffer_mask{}; | 182 | u32 constant_buffer_mask{}; |
| 181 | std::array<u32, MAX_CBUFS> constant_buffer_used_sizes{}; | 183 | std::array<u32, MAX_CBUFS> constant_buffer_used_sizes{}; |
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp index 5d6d217bb..54a902f56 100644 --- a/src/video_core/engines/maxwell_3d.cpp +++ b/src/video_core/engines/maxwell_3d.cpp | |||
| @@ -7,6 +7,7 @@ | |||
| 7 | #include "common/assert.h" | 7 | #include "common/assert.h" |
| 8 | #include "core/core.h" | 8 | #include "core/core.h" |
| 9 | #include "core/core_timing.h" | 9 | #include "core/core_timing.h" |
| 10 | #include "video_core/dirty_flags.h" | ||
| 10 | #include "video_core/engines/maxwell_3d.h" | 11 | #include "video_core/engines/maxwell_3d.h" |
| 11 | #include "video_core/gpu.h" | 12 | #include "video_core/gpu.h" |
| 12 | #include "video_core/memory_manager.h" | 13 | #include "video_core/memory_manager.h" |
| @@ -195,7 +196,7 @@ void Maxwell3D::ProcessMethodCall(u32 method, u32 argument, u32 nonshadow_argume | |||
| 195 | case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 13: | 196 | case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 13: |
| 196 | case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 14: | 197 | case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 14: |
| 197 | case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 15: | 198 | case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 15: |
| 198 | return StartCBData(method); | 199 | return ProcessCBData(argument); |
| 199 | case MAXWELL3D_REG_INDEX(cb_bind[0]): | 200 | case MAXWELL3D_REG_INDEX(cb_bind[0]): |
| 200 | return ProcessCBBind(0); | 201 | return ProcessCBBind(0); |
| 201 | case MAXWELL3D_REG_INDEX(cb_bind[1]): | 202 | case MAXWELL3D_REG_INDEX(cb_bind[1]): |
| @@ -208,6 +209,14 @@ void Maxwell3D::ProcessMethodCall(u32 method, u32 argument, u32 nonshadow_argume | |||
| 208 | return ProcessCBBind(4); | 209 | return ProcessCBBind(4); |
| 209 | case MAXWELL3D_REG_INDEX(draw.vertex_end_gl): | 210 | case MAXWELL3D_REG_INDEX(draw.vertex_end_gl): |
| 210 | return DrawArrays(); | 211 | return DrawArrays(); |
| 212 | case MAXWELL3D_REG_INDEX(small_index): | ||
| 213 | regs.index_array.count = regs.small_index.count; | ||
| 214 | regs.index_array.first = regs.small_index.first; | ||
| 215 | dirty.flags[VideoCommon::Dirty::IndexBuffer] = true; | ||
| 216 | return DrawArrays(); | ||
| 217 | case MAXWELL3D_REG_INDEX(topology_override): | ||
| 218 | use_topology_override = true; | ||
| 219 | return; | ||
| 211 | case MAXWELL3D_REG_INDEX(clear_buffers): | 220 | case MAXWELL3D_REG_INDEX(clear_buffers): |
| 212 | return ProcessClearBuffers(); | 221 | return ProcessClearBuffers(); |
| 213 | case MAXWELL3D_REG_INDEX(query.query_get): | 222 | case MAXWELL3D_REG_INDEX(query.query_get): |
| @@ -248,14 +257,6 @@ void Maxwell3D::CallMacroMethod(u32 method, const std::vector<u32>& parameters) | |||
| 248 | } | 257 | } |
| 249 | 258 | ||
| 250 | void Maxwell3D::CallMethod(u32 method, u32 method_argument, bool is_last_call) { | 259 | void Maxwell3D::CallMethod(u32 method, u32 method_argument, bool is_last_call) { |
| 251 | if (method == cb_data_state.current) { | ||
| 252 | regs.reg_array[method] = method_argument; | ||
| 253 | ProcessCBData(method_argument); | ||
| 254 | return; | ||
| 255 | } else if (cb_data_state.current != null_cb_data) { | ||
| 256 | FinishCBData(); | ||
| 257 | } | ||
| 258 | |||
| 259 | // It is an error to write to a register other than the current macro's ARG register before it | 260 | // It is an error to write to a register other than the current macro's ARG register before it |
| 260 | // has finished execution. | 261 | // has finished execution. |
| 261 | if (executing_macro != 0) { | 262 | if (executing_macro != 0) { |
| @@ -302,7 +303,7 @@ void Maxwell3D::CallMultiMethod(u32 method, const u32* base_start, u32 amount, | |||
| 302 | case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 13: | 303 | case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 13: |
| 303 | case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 14: | 304 | case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 14: |
| 304 | case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 15: | 305 | case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 15: |
| 305 | ProcessCBMultiData(method, base_start, amount); | 306 | ProcessCBMultiData(base_start, amount); |
| 306 | break; | 307 | break; |
| 307 | default: | 308 | default: |
| 308 | for (std::size_t i = 0; i < amount; i++) { | 309 | for (std::size_t i = 0; i < amount; i++) { |
| @@ -360,6 +361,35 @@ void Maxwell3D::CallMethodFromMME(u32 method, u32 method_argument) { | |||
| 360 | } | 361 | } |
| 361 | } | 362 | } |
| 362 | 363 | ||
| 364 | void Maxwell3D::ProcessTopologyOverride() { | ||
| 365 | using PrimitiveTopology = Maxwell3D::Regs::PrimitiveTopology; | ||
| 366 | using PrimitiveTopologyOverride = Maxwell3D::Regs::PrimitiveTopologyOverride; | ||
| 367 | |||
| 368 | PrimitiveTopology topology{}; | ||
| 369 | |||
| 370 | switch (regs.topology_override) { | ||
| 371 | case PrimitiveTopologyOverride::None: | ||
| 372 | topology = regs.draw.topology; | ||
| 373 | break; | ||
| 374 | case PrimitiveTopologyOverride::Points: | ||
| 375 | topology = PrimitiveTopology::Points; | ||
| 376 | break; | ||
| 377 | case PrimitiveTopologyOverride::Lines: | ||
| 378 | topology = PrimitiveTopology::Lines; | ||
| 379 | break; | ||
| 380 | case PrimitiveTopologyOverride::LineStrip: | ||
| 381 | topology = PrimitiveTopology::LineStrip; | ||
| 382 | break; | ||
| 383 | default: | ||
| 384 | topology = static_cast<PrimitiveTopology>(regs.topology_override); | ||
| 385 | break; | ||
| 386 | } | ||
| 387 | |||
| 388 | if (use_topology_override) { | ||
| 389 | regs.draw.topology.Assign(topology); | ||
| 390 | } | ||
| 391 | } | ||
| 392 | |||
| 363 | void Maxwell3D::FlushMMEInlineDraw() { | 393 | void Maxwell3D::FlushMMEInlineDraw() { |
| 364 | LOG_TRACE(HW_GPU, "called, topology={}, count={}", regs.draw.topology.Value(), | 394 | LOG_TRACE(HW_GPU, "called, topology={}, count={}", regs.draw.topology.Value(), |
| 365 | regs.vertex_buffer.count); | 395 | regs.vertex_buffer.count); |
| @@ -370,6 +400,8 @@ void Maxwell3D::FlushMMEInlineDraw() { | |||
| 370 | ASSERT_MSG(!regs.draw.instance_next || !regs.draw.instance_cont, | 400 | ASSERT_MSG(!regs.draw.instance_next || !regs.draw.instance_cont, |
| 371 | "Illegal combination of instancing parameters"); | 401 | "Illegal combination of instancing parameters"); |
| 372 | 402 | ||
| 403 | ProcessTopologyOverride(); | ||
| 404 | |||
| 373 | const bool is_indexed = mme_draw.current_mode == MMEDrawMode::Indexed; | 405 | const bool is_indexed = mme_draw.current_mode == MMEDrawMode::Indexed; |
| 374 | if (ShouldExecute()) { | 406 | if (ShouldExecute()) { |
| 375 | rasterizer->Draw(is_indexed, true); | 407 | rasterizer->Draw(is_indexed, true); |
| @@ -529,6 +561,8 @@ void Maxwell3D::DrawArrays() { | |||
| 529 | ASSERT_MSG(!regs.draw.instance_next || !regs.draw.instance_cont, | 561 | ASSERT_MSG(!regs.draw.instance_next || !regs.draw.instance_cont, |
| 530 | "Illegal combination of instancing parameters"); | 562 | "Illegal combination of instancing parameters"); |
| 531 | 563 | ||
| 564 | ProcessTopologyOverride(); | ||
| 565 | |||
| 532 | if (regs.draw.instance_next) { | 566 | if (regs.draw.instance_next) { |
| 533 | // Increment the current instance *before* drawing. | 567 | // Increment the current instance *before* drawing. |
| 534 | state.current_instance += 1; | 568 | state.current_instance += 1; |
| @@ -587,46 +621,7 @@ void Maxwell3D::ProcessCBBind(size_t stage_index) { | |||
| 587 | rasterizer->BindGraphicsUniformBuffer(stage_index, bind_data.index, gpu_addr, size); | 621 | rasterizer->BindGraphicsUniformBuffer(stage_index, bind_data.index, gpu_addr, size); |
| 588 | } | 622 | } |
| 589 | 623 | ||
| 590 | void Maxwell3D::ProcessCBData(u32 value) { | 624 | void Maxwell3D::ProcessCBMultiData(const u32* start_base, u32 amount) { |
| 591 | const u32 id = cb_data_state.id; | ||
| 592 | cb_data_state.buffer[id][cb_data_state.counter] = value; | ||
| 593 | // Increment the current buffer position. | ||
| 594 | regs.const_buffer.cb_pos = regs.const_buffer.cb_pos + 4; | ||
| 595 | cb_data_state.counter++; | ||
| 596 | } | ||
| 597 | |||
| 598 | void Maxwell3D::StartCBData(u32 method) { | ||
| 599 | constexpr u32 first_cb_data = MAXWELL3D_REG_INDEX(const_buffer.cb_data); | ||
| 600 | cb_data_state.start_pos = regs.const_buffer.cb_pos; | ||
| 601 | cb_data_state.id = method - first_cb_data; | ||
| 602 | cb_data_state.current = method; | ||
| 603 | cb_data_state.counter = 0; | ||
| 604 | ProcessCBData(regs.const_buffer.cb_data[cb_data_state.id]); | ||
| 605 | } | ||
| 606 | |||
| 607 | void Maxwell3D::ProcessCBMultiData(u32 method, const u32* start_base, u32 amount) { | ||
| 608 | if (cb_data_state.current != method) { | ||
| 609 | if (cb_data_state.current != null_cb_data) { | ||
| 610 | FinishCBData(); | ||
| 611 | } | ||
| 612 | constexpr u32 first_cb_data = MAXWELL3D_REG_INDEX(const_buffer.cb_data); | ||
| 613 | cb_data_state.start_pos = regs.const_buffer.cb_pos; | ||
| 614 | cb_data_state.id = method - first_cb_data; | ||
| 615 | cb_data_state.current = method; | ||
| 616 | cb_data_state.counter = 0; | ||
| 617 | } | ||
| 618 | const std::size_t id = cb_data_state.id; | ||
| 619 | const std::size_t size = amount; | ||
| 620 | std::size_t i = 0; | ||
| 621 | for (; i < size; i++) { | ||
| 622 | cb_data_state.buffer[id][cb_data_state.counter] = start_base[i]; | ||
| 623 | cb_data_state.counter++; | ||
| 624 | } | ||
| 625 | // Increment the current buffer position. | ||
| 626 | regs.const_buffer.cb_pos = regs.const_buffer.cb_pos + 4 * amount; | ||
| 627 | } | ||
| 628 | |||
| 629 | void Maxwell3D::FinishCBData() { | ||
| 630 | // Write the input value to the current const buffer at the current position. | 625 | // Write the input value to the current const buffer at the current position. |
| 631 | const GPUVAddr buffer_address = regs.const_buffer.BufferAddress(); | 626 | const GPUVAddr buffer_address = regs.const_buffer.BufferAddress(); |
| 632 | ASSERT(buffer_address != 0); | 627 | ASSERT(buffer_address != 0); |
| @@ -634,14 +629,16 @@ void Maxwell3D::FinishCBData() { | |||
| 634 | // Don't allow writing past the end of the buffer. | 629 | // Don't allow writing past the end of the buffer. |
| 635 | ASSERT(regs.const_buffer.cb_pos <= regs.const_buffer.cb_size); | 630 | ASSERT(regs.const_buffer.cb_pos <= regs.const_buffer.cb_size); |
| 636 | 631 | ||
| 637 | const GPUVAddr address{buffer_address + cb_data_state.start_pos}; | 632 | const GPUVAddr address{buffer_address + regs.const_buffer.cb_pos}; |
| 638 | const std::size_t size = regs.const_buffer.cb_pos - cb_data_state.start_pos; | 633 | const size_t copy_size = amount * sizeof(u32); |
| 634 | memory_manager.WriteBlock(address, start_base, copy_size); | ||
| 639 | 635 | ||
| 640 | const u32 id = cb_data_state.id; | 636 | // Increment the current buffer position. |
| 641 | memory_manager.WriteBlock(address, cb_data_state.buffer[id].data(), size); | 637 | regs.const_buffer.cb_pos += static_cast<u32>(copy_size); |
| 638 | } | ||
| 642 | 639 | ||
| 643 | cb_data_state.id = null_cb_data; | 640 | void Maxwell3D::ProcessCBData(u32 value) { |
| 644 | cb_data_state.current = null_cb_data; | 641 | ProcessCBMultiData(&value, 1); |
| 645 | } | 642 | } |
| 646 | 643 | ||
| 647 | Texture::TICEntry Maxwell3D::GetTICEntry(u32 tic_index) const { | 644 | Texture::TICEntry Maxwell3D::GetTICEntry(u32 tic_index) const { |
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h index dc9df6c8b..357a74c70 100644 --- a/src/video_core/engines/maxwell_3d.h +++ b/src/video_core/engines/maxwell_3d.h | |||
| @@ -367,6 +367,22 @@ public: | |||
| 367 | Patches = 0xe, | 367 | Patches = 0xe, |
| 368 | }; | 368 | }; |
| 369 | 369 | ||
| 370 | // Constants as from NVC0_3D_UNK1970_D3D | ||
| 371 | // https://gitlab.freedesktop.org/mesa/mesa/-/blob/main/src/gallium/drivers/nouveau/nvc0/nvc0_3d.xml.h#L1598 | ||
| 372 | enum class PrimitiveTopologyOverride : u32 { | ||
| 373 | None = 0x0, | ||
| 374 | Points = 0x1, | ||
| 375 | Lines = 0x2, | ||
| 376 | LineStrip = 0x3, | ||
| 377 | Triangles = 0x4, | ||
| 378 | TriangleStrip = 0x5, | ||
| 379 | LinesAdjacency = 0xa, | ||
| 380 | LineStripAdjacency = 0xb, | ||
| 381 | TrianglesAdjacency = 0xc, | ||
| 382 | TriangleStripAdjacency = 0xd, | ||
| 383 | Patches = 0xe, | ||
| 384 | }; | ||
| 385 | |||
| 370 | enum class IndexFormat : u32 { | 386 | enum class IndexFormat : u32 { |
| 371 | UnsignedByte = 0x0, | 387 | UnsignedByte = 0x0, |
| 372 | UnsignedShort = 0x1, | 388 | UnsignedShort = 0x1, |
| @@ -1200,7 +1216,12 @@ public: | |||
| 1200 | } | 1216 | } |
| 1201 | } index_array; | 1217 | } index_array; |
| 1202 | 1218 | ||
| 1203 | INSERT_PADDING_WORDS_NOINIT(0x7); | 1219 | union { |
| 1220 | BitField<0, 16, u32> first; | ||
| 1221 | BitField<16, 16, u32> count; | ||
| 1222 | } small_index; | ||
| 1223 | |||
| 1224 | INSERT_PADDING_WORDS_NOINIT(0x6); | ||
| 1204 | 1225 | ||
| 1205 | INSERT_PADDING_WORDS_NOINIT(0x1F); | 1226 | INSERT_PADDING_WORDS_NOINIT(0x1F); |
| 1206 | 1227 | ||
| @@ -1244,7 +1265,11 @@ public: | |||
| 1244 | BitField<11, 1, u32> depth_clamp_disabled; | 1265 | BitField<11, 1, u32> depth_clamp_disabled; |
| 1245 | } view_volume_clip_control; | 1266 | } view_volume_clip_control; |
| 1246 | 1267 | ||
| 1247 | INSERT_PADDING_WORDS_NOINIT(0x1F); | 1268 | INSERT_PADDING_WORDS_NOINIT(0xC); |
| 1269 | |||
| 1270 | PrimitiveTopologyOverride topology_override; | ||
| 1271 | |||
| 1272 | INSERT_PADDING_WORDS_NOINIT(0x12); | ||
| 1248 | 1273 | ||
| 1249 | u32 depth_bounds_enable; | 1274 | u32 depth_bounds_enable; |
| 1250 | 1275 | ||
| @@ -1520,10 +1545,8 @@ private: | |||
| 1520 | void ProcessSyncPoint(); | 1545 | void ProcessSyncPoint(); |
| 1521 | 1546 | ||
| 1522 | /// Handles a write to the CB_DATA[i] register. | 1547 | /// Handles a write to the CB_DATA[i] register. |
| 1523 | void StartCBData(u32 method); | ||
| 1524 | void ProcessCBData(u32 value); | 1548 | void ProcessCBData(u32 value); |
| 1525 | void ProcessCBMultiData(u32 method, const u32* start_base, u32 amount); | 1549 | void ProcessCBMultiData(const u32* start_base, u32 amount); |
| 1526 | void FinishCBData(); | ||
| 1527 | 1550 | ||
| 1528 | /// Handles a write to the CB_BIND register. | 1551 | /// Handles a write to the CB_BIND register. |
| 1529 | void ProcessCBBind(size_t stage_index); | 1552 | void ProcessCBBind(size_t stage_index); |
| @@ -1531,6 +1554,9 @@ private: | |||
| 1531 | /// Handles a write to the VERTEX_END_GL register, triggering a draw. | 1554 | /// Handles a write to the VERTEX_END_GL register, triggering a draw. |
| 1532 | void DrawArrays(); | 1555 | void DrawArrays(); |
| 1533 | 1556 | ||
| 1557 | /// Handles use of topology overrides (e.g., to avoid using a topology assigned from a macro) | ||
| 1558 | void ProcessTopologyOverride(); | ||
| 1559 | |||
| 1534 | // Handles a instance drawcall from MME | 1560 | // Handles a instance drawcall from MME |
| 1535 | void StepInstance(MMEDrawMode expected_mode, u32 count); | 1561 | void StepInstance(MMEDrawMode expected_mode, u32 count); |
| 1536 | 1562 | ||
| @@ -1555,20 +1581,10 @@ private: | |||
| 1555 | /// Interpreter for the macro codes uploaded to the GPU. | 1581 | /// Interpreter for the macro codes uploaded to the GPU. |
| 1556 | std::unique_ptr<MacroEngine> macro_engine; | 1582 | std::unique_ptr<MacroEngine> macro_engine; |
| 1557 | 1583 | ||
| 1558 | static constexpr u32 null_cb_data = 0xFFFFFFFF; | ||
| 1559 | struct CBDataState { | ||
| 1560 | static constexpr size_t inline_size = 0x4000; | ||
| 1561 | std::array<std::array<u32, inline_size>, 16> buffer; | ||
| 1562 | u32 current{null_cb_data}; | ||
| 1563 | u32 id{null_cb_data}; | ||
| 1564 | u32 start_pos{}; | ||
| 1565 | u32 counter{}; | ||
| 1566 | }; | ||
| 1567 | CBDataState cb_data_state; | ||
| 1568 | |||
| 1569 | Upload::State upload_state; | 1584 | Upload::State upload_state; |
| 1570 | 1585 | ||
| 1571 | bool execute_on{true}; | 1586 | bool execute_on{true}; |
| 1587 | bool use_topology_override{false}; | ||
| 1572 | }; | 1588 | }; |
| 1573 | 1589 | ||
| 1574 | #define ASSERT_REG_POSITION(field_name, position) \ | 1590 | #define ASSERT_REG_POSITION(field_name, position) \ |
| @@ -1685,6 +1701,7 @@ ASSERT_REG_POSITION(draw, 0x585); | |||
| 1685 | ASSERT_REG_POSITION(primitive_restart, 0x591); | 1701 | ASSERT_REG_POSITION(primitive_restart, 0x591); |
| 1686 | ASSERT_REG_POSITION(provoking_vertex_last, 0x5A1); | 1702 | ASSERT_REG_POSITION(provoking_vertex_last, 0x5A1); |
| 1687 | ASSERT_REG_POSITION(index_array, 0x5F2); | 1703 | ASSERT_REG_POSITION(index_array, 0x5F2); |
| 1704 | ASSERT_REG_POSITION(small_index, 0x5F9); | ||
| 1688 | ASSERT_REG_POSITION(polygon_offset_clamp, 0x61F); | 1705 | ASSERT_REG_POSITION(polygon_offset_clamp, 0x61F); |
| 1689 | ASSERT_REG_POSITION(instanced_arrays, 0x620); | 1706 | ASSERT_REG_POSITION(instanced_arrays, 0x620); |
| 1690 | ASSERT_REG_POSITION(vp_point_size, 0x644); | 1707 | ASSERT_REG_POSITION(vp_point_size, 0x644); |
| @@ -1694,6 +1711,7 @@ ASSERT_REG_POSITION(cull_face, 0x648); | |||
| 1694 | ASSERT_REG_POSITION(pixel_center_integer, 0x649); | 1711 | ASSERT_REG_POSITION(pixel_center_integer, 0x649); |
| 1695 | ASSERT_REG_POSITION(viewport_transform_enabled, 0x64B); | 1712 | ASSERT_REG_POSITION(viewport_transform_enabled, 0x64B); |
| 1696 | ASSERT_REG_POSITION(view_volume_clip_control, 0x64F); | 1713 | ASSERT_REG_POSITION(view_volume_clip_control, 0x64F); |
| 1714 | ASSERT_REG_POSITION(topology_override, 0x65C); | ||
| 1697 | ASSERT_REG_POSITION(depth_bounds_enable, 0x66F); | 1715 | ASSERT_REG_POSITION(depth_bounds_enable, 0x66F); |
| 1698 | ASSERT_REG_POSITION(logic_op, 0x671); | 1716 | ASSERT_REG_POSITION(logic_op, 0x671); |
| 1699 | ASSERT_REG_POSITION(clear_buffers, 0x674); | 1717 | ASSERT_REG_POSITION(clear_buffers, 0x674); |
diff --git a/src/video_core/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp index 67388d980..1fc1358bc 100644 --- a/src/video_core/engines/maxwell_dma.cpp +++ b/src/video_core/engines/maxwell_dma.cpp | |||
| @@ -53,7 +53,6 @@ void MaxwellDMA::Launch() { | |||
| 53 | 53 | ||
| 54 | // TODO(Subv): Perform more research and implement all features of this engine. | 54 | // TODO(Subv): Perform more research and implement all features of this engine. |
| 55 | const LaunchDMA& launch = regs.launch_dma; | 55 | const LaunchDMA& launch = regs.launch_dma; |
| 56 | ASSERT(launch.semaphore_type == LaunchDMA::SemaphoreType::NONE); | ||
| 57 | ASSERT(launch.interrupt_type == LaunchDMA::InterruptType::NONE); | 56 | ASSERT(launch.interrupt_type == LaunchDMA::InterruptType::NONE); |
| 58 | ASSERT(launch.data_transfer_type == LaunchDMA::DataTransferType::NON_PIPELINED); | 57 | ASSERT(launch.data_transfer_type == LaunchDMA::DataTransferType::NON_PIPELINED); |
| 59 | ASSERT(regs.dst_params.origin.x == 0); | 58 | ASSERT(regs.dst_params.origin.x == 0); |
| @@ -79,6 +78,7 @@ void MaxwellDMA::Launch() { | |||
| 79 | CopyPitchToBlockLinear(); | 78 | CopyPitchToBlockLinear(); |
| 80 | } | 79 | } |
| 81 | } | 80 | } |
| 81 | ReleaseSemaphore(); | ||
| 82 | } | 82 | } |
| 83 | 83 | ||
| 84 | void MaxwellDMA::CopyPitchToPitch() { | 84 | void MaxwellDMA::CopyPitchToPitch() { |
| @@ -244,4 +244,22 @@ void MaxwellDMA::FastCopyBlockLinearToPitch() { | |||
| 244 | memory_manager.WriteBlock(regs.offset_out, write_buffer.data(), dst_size); | 244 | memory_manager.WriteBlock(regs.offset_out, write_buffer.data(), dst_size); |
| 245 | } | 245 | } |
| 246 | 246 | ||
| 247 | void MaxwellDMA::ReleaseSemaphore() { | ||
| 248 | const auto type = regs.launch_dma.semaphore_type; | ||
| 249 | const GPUVAddr address = regs.semaphore.address; | ||
| 250 | switch (type) { | ||
| 251 | case LaunchDMA::SemaphoreType::NONE: | ||
| 252 | break; | ||
| 253 | case LaunchDMA::SemaphoreType::RELEASE_ONE_WORD_SEMAPHORE: | ||
| 254 | memory_manager.Write<u32>(address, regs.semaphore.payload); | ||
| 255 | break; | ||
| 256 | case LaunchDMA::SemaphoreType::RELEASE_FOUR_WORD_SEMAPHORE: | ||
| 257 | memory_manager.Write<u64>(address, static_cast<u64>(regs.semaphore.payload)); | ||
| 258 | memory_manager.Write<u64>(address + 8, system.GPU().GetTicks()); | ||
| 259 | break; | ||
| 260 | default: | ||
| 261 | UNREACHABLE_MSG("Unknown semaphore type: {}", static_cast<u32>(type.Value())); | ||
| 262 | } | ||
| 263 | } | ||
| 264 | |||
| 247 | } // namespace Tegra::Engines | 265 | } // namespace Tegra::Engines |
diff --git a/src/video_core/engines/maxwell_dma.h b/src/video_core/engines/maxwell_dma.h index a04514425..2692cac8a 100644 --- a/src/video_core/engines/maxwell_dma.h +++ b/src/video_core/engines/maxwell_dma.h | |||
| @@ -224,6 +224,8 @@ private: | |||
| 224 | 224 | ||
| 225 | void FastCopyBlockLinearToPitch(); | 225 | void FastCopyBlockLinearToPitch(); |
| 226 | 226 | ||
| 227 | void ReleaseSemaphore(); | ||
| 228 | |||
| 227 | Core::System& system; | 229 | Core::System& system; |
| 228 | 230 | ||
| 229 | MemoryManager& memory_manager; | 231 | MemoryManager& memory_manager; |
diff --git a/src/video_core/fence_manager.h b/src/video_core/fence_manager.h index 34dc6c596..f80d62c80 100644 --- a/src/video_core/fence_manager.h +++ b/src/video_core/fence_manager.h | |||
| @@ -8,8 +8,6 @@ | |||
| 8 | #include <queue> | 8 | #include <queue> |
| 9 | 9 | ||
| 10 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 11 | #include "common/settings.h" | ||
| 12 | #include "core/core.h" | ||
| 13 | #include "video_core/delayed_destruction_ring.h" | 11 | #include "video_core/delayed_destruction_ring.h" |
| 14 | #include "video_core/gpu.h" | 12 | #include "video_core/gpu.h" |
| 15 | #include "video_core/memory_manager.h" | 13 | #include "video_core/memory_manager.h" |
diff --git a/src/video_core/host_shaders/CMakeLists.txt b/src/video_core/host_shaders/CMakeLists.txt index fd3e41434..af05d47d1 100644 --- a/src/video_core/host_shaders/CMakeLists.txt +++ b/src/video_core/host_shaders/CMakeLists.txt | |||
| @@ -14,6 +14,7 @@ set(SHADER_FILES | |||
| 14 | convert_d24s8_to_abgr8.frag | 14 | convert_d24s8_to_abgr8.frag |
| 15 | convert_depth_to_float.frag | 15 | convert_depth_to_float.frag |
| 16 | convert_float_to_depth.frag | 16 | convert_float_to_depth.frag |
| 17 | convert_s8d24_to_abgr8.frag | ||
| 17 | full_screen_triangle.vert | 18 | full_screen_triangle.vert |
| 18 | fxaa.frag | 19 | fxaa.frag |
| 19 | fxaa.vert | 20 | fxaa.vert |
diff --git a/src/video_core/host_shaders/convert_s8d24_to_abgr8.frag b/src/video_core/host_shaders/convert_s8d24_to_abgr8.frag new file mode 100644 index 000000000..c8a1683b8 --- /dev/null +++ b/src/video_core/host_shaders/convert_s8d24_to_abgr8.frag | |||
| @@ -0,0 +1,23 @@ | |||
| 1 | // Copyright 2022 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #version 450 | ||
| 6 | |||
| 7 | layout(binding = 0) uniform sampler2D depth_tex; | ||
| 8 | layout(binding = 1) uniform isampler2D stencil_tex; | ||
| 9 | |||
| 10 | layout(location = 0) out vec4 color; | ||
| 11 | |||
| 12 | void main() { | ||
| 13 | ivec2 coord = ivec2(gl_FragCoord.xy); | ||
| 14 | uint depth = uint(textureLod(depth_tex, coord, 0).r * (exp2(24.0) - 1.0f)); | ||
| 15 | uint stencil = uint(textureLod(stencil_tex, coord, 0).r); | ||
| 16 | |||
| 17 | highp uint depth_val = | ||
| 18 | uint(textureLod(depth_tex, coord, 0).r * (exp2(32.0) - 1.0)); | ||
| 19 | lowp uint stencil_val = textureLod(stencil_tex, coord, 0).r; | ||
| 20 | highp uvec4 components = | ||
| 21 | uvec4((uvec3(depth_val) >> uvec3(24u, 16u, 8u)) & 0x000000FFu, stencil_val); | ||
| 22 | color.rgba = vec4(components) / (exp2(8.0) - 1.0); | ||
| 23 | } | ||
diff --git a/src/video_core/query_cache.h b/src/video_core/query_cache.h index 392f82eb7..0173b54d8 100644 --- a/src/video_core/query_cache.h +++ b/src/video_core/query_cache.h | |||
| @@ -18,7 +18,6 @@ | |||
| 18 | 18 | ||
| 19 | #include "common/assert.h" | 19 | #include "common/assert.h" |
| 20 | #include "common/settings.h" | 20 | #include "common/settings.h" |
| 21 | #include "core/core.h" | ||
| 22 | #include "video_core/engines/maxwell_3d.h" | 21 | #include "video_core/engines/maxwell_3d.h" |
| 23 | #include "video_core/gpu.h" | 22 | #include "video_core/gpu.h" |
| 24 | #include "video_core/memory_manager.h" | 23 | #include "video_core/memory_manager.h" |
diff --git a/src/video_core/renderer_base.h b/src/video_core/renderer_base.h index bb204454e..c5f974080 100644 --- a/src/video_core/renderer_base.h +++ b/src/video_core/renderer_base.h | |||
| @@ -5,9 +5,10 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <atomic> | 7 | #include <atomic> |
| 8 | #include <functional> | ||
| 8 | #include <memory> | 9 | #include <memory> |
| 9 | #include <optional> | ||
| 10 | 10 | ||
| 11 | #include "common/common_funcs.h" | ||
| 11 | #include "common/common_types.h" | 12 | #include "common/common_types.h" |
| 12 | #include "core/frontend/emu_window.h" | 13 | #include "core/frontend/emu_window.h" |
| 13 | #include "video_core/gpu.h" | 14 | #include "video_core/gpu.h" |
| @@ -28,8 +29,11 @@ struct RendererSettings { | |||
| 28 | Layout::FramebufferLayout screenshot_framebuffer_layout; | 29 | Layout::FramebufferLayout screenshot_framebuffer_layout; |
| 29 | }; | 30 | }; |
| 30 | 31 | ||
| 31 | class RendererBase : NonCopyable { | 32 | class RendererBase { |
| 32 | public: | 33 | public: |
| 34 | YUZU_NON_COPYABLE(RendererBase); | ||
| 35 | YUZU_NON_MOVEABLE(RendererBase); | ||
| 36 | |||
| 33 | explicit RendererBase(Core::Frontend::EmuWindow& window, | 37 | explicit RendererBase(Core::Frontend::EmuWindow& window, |
| 34 | std::unique_ptr<Core::Frontend::GraphicsContext> context); | 38 | std::unique_ptr<Core::Frontend::GraphicsContext> context); |
| 35 | virtual ~RendererBase(); | 39 | virtual ~RendererBase(); |
diff --git a/src/video_core/renderer_opengl/gl_fence_manager.cpp b/src/video_core/renderer_opengl/gl_fence_manager.cpp index 151290101..293ad7d59 100644 --- a/src/video_core/renderer_opengl/gl_fence_manager.cpp +++ b/src/video_core/renderer_opengl/gl_fence_manager.cpp | |||
| @@ -31,9 +31,8 @@ bool GLInnerFence::IsSignaled() const { | |||
| 31 | return true; | 31 | return true; |
| 32 | } | 32 | } |
| 33 | ASSERT(sync_object.handle != 0); | 33 | ASSERT(sync_object.handle != 0); |
| 34 | GLsizei length; | ||
| 35 | GLint sync_status; | 34 | GLint sync_status; |
| 36 | glGetSynciv(sync_object.handle, GL_SYNC_STATUS, sizeof(GLint), &length, &sync_status); | 35 | glGetSynciv(sync_object.handle, GL_SYNC_STATUS, 1, nullptr, &sync_status); |
| 37 | return sync_status == GL_SIGNALED; | 36 | return sync_status == GL_SIGNALED; |
| 38 | } | 37 | } |
| 39 | 38 | ||
diff --git a/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp index f8495896c..9e6732abd 100644 --- a/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp +++ b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp | |||
| @@ -243,10 +243,6 @@ GraphicsPipeline::GraphicsPipeline( | |||
| 243 | case Settings::ShaderBackend::GLASM: | 243 | case Settings::ShaderBackend::GLASM: |
| 244 | if (!sources[stage].empty()) { | 244 | if (!sources[stage].empty()) { |
| 245 | assembly_programs[stage] = CompileProgram(sources[stage], AssemblyStage(stage)); | 245 | assembly_programs[stage] = CompileProgram(sources[stage], AssemblyStage(stage)); |
| 246 | if (in_parallel) { | ||
| 247 | // Make sure program is built before continuing when building in parallel | ||
| 248 | glGetString(GL_PROGRAM_ERROR_STRING_NV); | ||
| 249 | } | ||
| 250 | } | 246 | } |
| 251 | break; | 247 | break; |
| 252 | case Settings::ShaderBackend::SPIRV: | 248 | case Settings::ShaderBackend::SPIRV: |
| @@ -256,20 +252,18 @@ GraphicsPipeline::GraphicsPipeline( | |||
| 256 | break; | 252 | break; |
| 257 | } | 253 | } |
| 258 | } | 254 | } |
| 259 | if (in_parallel && backend != Settings::ShaderBackend::GLASM) { | 255 | if (in_parallel) { |
| 260 | // Make sure programs have built if we are building shaders in parallel | 256 | std::lock_guard lock{built_mutex}; |
| 261 | for (OGLProgram& program : source_programs) { | 257 | built_fence.Create(); |
| 262 | if (program.handle != 0) { | 258 | // Flush this context to ensure compilation commands and fence are in the GPU pipe. |
| 263 | GLint status{}; | 259 | glFlush(); |
| 264 | glGetProgramiv(program.handle, GL_LINK_STATUS, &status); | 260 | built_condvar.notify_one(); |
| 265 | } | 261 | } else { |
| 266 | } | 262 | is_built = true; |
| 267 | } | 263 | } |
| 268 | if (shader_notify) { | 264 | if (shader_notify) { |
| 269 | shader_notify->MarkShaderComplete(); | 265 | shader_notify->MarkShaderComplete(); |
| 270 | } | 266 | } |
| 271 | is_built = true; | ||
| 272 | built_condvar.notify_one(); | ||
| 273 | }}; | 267 | }}; |
| 274 | if (thread_worker) { | 268 | if (thread_worker) { |
| 275 | thread_worker->QueueWork(std::move(func)); | 269 | thread_worker->QueueWork(std::move(func)); |
| @@ -440,7 +434,7 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) { | |||
| 440 | buffer_cache.UpdateGraphicsBuffers(is_indexed); | 434 | buffer_cache.UpdateGraphicsBuffers(is_indexed); |
| 441 | buffer_cache.BindHostGeometryBuffers(is_indexed); | 435 | buffer_cache.BindHostGeometryBuffers(is_indexed); |
| 442 | 436 | ||
| 443 | if (!is_built.load(std::memory_order::relaxed)) { | 437 | if (!IsBuilt()) { |
| 444 | WaitForBuild(); | 438 | WaitForBuild(); |
| 445 | } | 439 | } |
| 446 | const bool use_assembly{assembly_programs[0].handle != 0}; | 440 | const bool use_assembly{assembly_programs[0].handle != 0}; |
| @@ -585,8 +579,26 @@ void GraphicsPipeline::GenerateTransformFeedbackState() { | |||
| 585 | } | 579 | } |
| 586 | 580 | ||
| 587 | void GraphicsPipeline::WaitForBuild() { | 581 | void GraphicsPipeline::WaitForBuild() { |
| 588 | std::unique_lock lock{built_mutex}; | 582 | if (built_fence.handle == 0) { |
| 589 | built_condvar.wait(lock, [this] { return is_built.load(std::memory_order::relaxed); }); | 583 | std::unique_lock lock{built_mutex}; |
| 584 | built_condvar.wait(lock, [this] { return built_fence.handle != 0; }); | ||
| 585 | } | ||
| 586 | ASSERT(glClientWaitSync(built_fence.handle, 0, GL_TIMEOUT_IGNORED) != GL_WAIT_FAILED); | ||
| 587 | is_built = true; | ||
| 588 | } | ||
| 589 | |||
| 590 | bool GraphicsPipeline::IsBuilt() noexcept { | ||
| 591 | if (is_built) { | ||
| 592 | return true; | ||
| 593 | } | ||
| 594 | if (built_fence.handle == 0) { | ||
| 595 | return false; | ||
| 596 | } | ||
| 597 | // Timeout of zero means this is non-blocking | ||
| 598 | const auto sync_status = glClientWaitSync(built_fence.handle, 0, 0); | ||
| 599 | ASSERT(sync_status != GL_WAIT_FAILED); | ||
| 600 | is_built = sync_status != GL_TIMEOUT_EXPIRED; | ||
| 601 | return is_built; | ||
| 590 | } | 602 | } |
| 591 | 603 | ||
| 592 | } // namespace OpenGL | 604 | } // namespace OpenGL |
diff --git a/src/video_core/renderer_opengl/gl_graphics_pipeline.h b/src/video_core/renderer_opengl/gl_graphics_pipeline.h index 4e28d9a42..311d49f3f 100644 --- a/src/video_core/renderer_opengl/gl_graphics_pipeline.h +++ b/src/video_core/renderer_opengl/gl_graphics_pipeline.h | |||
| @@ -100,9 +100,7 @@ public: | |||
| 100 | return writes_global_memory; | 100 | return writes_global_memory; |
| 101 | } | 101 | } |
| 102 | 102 | ||
| 103 | [[nodiscard]] bool IsBuilt() const noexcept { | 103 | [[nodiscard]] bool IsBuilt() noexcept; |
| 104 | return is_built.load(std::memory_order::relaxed); | ||
| 105 | } | ||
| 106 | 104 | ||
| 107 | template <typename Spec> | 105 | template <typename Spec> |
| 108 | static auto MakeConfigureSpecFunc() { | 106 | static auto MakeConfigureSpecFunc() { |
| @@ -154,7 +152,8 @@ private: | |||
| 154 | 152 | ||
| 155 | std::mutex built_mutex; | 153 | std::mutex built_mutex; |
| 156 | std::condition_variable built_condvar; | 154 | std::condition_variable built_condvar; |
| 157 | std::atomic_bool is_built{false}; | 155 | OGLSync built_fence{}; |
| 156 | bool is_built{false}; | ||
| 158 | }; | 157 | }; |
| 159 | 158 | ||
| 160 | } // namespace OpenGL | 159 | } // namespace OpenGL |
diff --git a/src/video_core/renderer_opengl/gl_resource_manager.h b/src/video_core/renderer_opengl/gl_resource_manager.h index b2d5bfd3b..84e07f8bd 100644 --- a/src/video_core/renderer_opengl/gl_resource_manager.h +++ b/src/video_core/renderer_opengl/gl_resource_manager.h | |||
| @@ -7,12 +7,14 @@ | |||
| 7 | #include <string_view> | 7 | #include <string_view> |
| 8 | #include <utility> | 8 | #include <utility> |
| 9 | #include <glad/glad.h> | 9 | #include <glad/glad.h> |
| 10 | #include "common/common_types.h" | 10 | #include "common/common_funcs.h" |
| 11 | 11 | ||
| 12 | namespace OpenGL { | 12 | namespace OpenGL { |
| 13 | 13 | ||
| 14 | class OGLRenderbuffer : private NonCopyable { | 14 | class OGLRenderbuffer final { |
| 15 | public: | 15 | public: |
| 16 | YUZU_NON_COPYABLE(OGLRenderbuffer); | ||
| 17 | |||
| 16 | OGLRenderbuffer() = default; | 18 | OGLRenderbuffer() = default; |
| 17 | 19 | ||
| 18 | OGLRenderbuffer(OGLRenderbuffer&& o) noexcept : handle(std::exchange(o.handle, 0)) {} | 20 | OGLRenderbuffer(OGLRenderbuffer&& o) noexcept : handle(std::exchange(o.handle, 0)) {} |
| @@ -36,8 +38,10 @@ public: | |||
| 36 | GLuint handle = 0; | 38 | GLuint handle = 0; |
| 37 | }; | 39 | }; |
| 38 | 40 | ||
| 39 | class OGLTexture : private NonCopyable { | 41 | class OGLTexture final { |
| 40 | public: | 42 | public: |
| 43 | YUZU_NON_COPYABLE(OGLTexture); | ||
| 44 | |||
| 41 | OGLTexture() = default; | 45 | OGLTexture() = default; |
| 42 | 46 | ||
| 43 | OGLTexture(OGLTexture&& o) noexcept : handle(std::exchange(o.handle, 0)) {} | 47 | OGLTexture(OGLTexture&& o) noexcept : handle(std::exchange(o.handle, 0)) {} |
| @@ -61,8 +65,10 @@ public: | |||
| 61 | GLuint handle = 0; | 65 | GLuint handle = 0; |
| 62 | }; | 66 | }; |
| 63 | 67 | ||
| 64 | class OGLTextureView : private NonCopyable { | 68 | class OGLTextureView final { |
| 65 | public: | 69 | public: |
| 70 | YUZU_NON_COPYABLE(OGLTextureView); | ||
| 71 | |||
| 66 | OGLTextureView() = default; | 72 | OGLTextureView() = default; |
| 67 | 73 | ||
| 68 | OGLTextureView(OGLTextureView&& o) noexcept : handle(std::exchange(o.handle, 0)) {} | 74 | OGLTextureView(OGLTextureView&& o) noexcept : handle(std::exchange(o.handle, 0)) {} |
| @@ -86,8 +92,10 @@ public: | |||
| 86 | GLuint handle = 0; | 92 | GLuint handle = 0; |
| 87 | }; | 93 | }; |
| 88 | 94 | ||
| 89 | class OGLSampler : private NonCopyable { | 95 | class OGLSampler final { |
| 90 | public: | 96 | public: |
| 97 | YUZU_NON_COPYABLE(OGLSampler); | ||
| 98 | |||
| 91 | OGLSampler() = default; | 99 | OGLSampler() = default; |
| 92 | 100 | ||
| 93 | OGLSampler(OGLSampler&& o) noexcept : handle(std::exchange(o.handle, 0)) {} | 101 | OGLSampler(OGLSampler&& o) noexcept : handle(std::exchange(o.handle, 0)) {} |
| @@ -111,8 +119,10 @@ public: | |||
| 111 | GLuint handle = 0; | 119 | GLuint handle = 0; |
| 112 | }; | 120 | }; |
| 113 | 121 | ||
| 114 | class OGLShader : private NonCopyable { | 122 | class OGLShader final { |
| 115 | public: | 123 | public: |
| 124 | YUZU_NON_COPYABLE(OGLShader); | ||
| 125 | |||
| 116 | OGLShader() = default; | 126 | OGLShader() = default; |
| 117 | 127 | ||
| 118 | OGLShader(OGLShader&& o) noexcept : handle(std::exchange(o.handle, 0)) {} | 128 | OGLShader(OGLShader&& o) noexcept : handle(std::exchange(o.handle, 0)) {} |
| @@ -132,8 +142,10 @@ public: | |||
| 132 | GLuint handle = 0; | 142 | GLuint handle = 0; |
| 133 | }; | 143 | }; |
| 134 | 144 | ||
| 135 | class OGLProgram : private NonCopyable { | 145 | class OGLProgram final { |
| 136 | public: | 146 | public: |
| 147 | YUZU_NON_COPYABLE(OGLProgram); | ||
| 148 | |||
| 137 | OGLProgram() = default; | 149 | OGLProgram() = default; |
| 138 | 150 | ||
| 139 | OGLProgram(OGLProgram&& o) noexcept : handle(std::exchange(o.handle, 0)) {} | 151 | OGLProgram(OGLProgram&& o) noexcept : handle(std::exchange(o.handle, 0)) {} |
| @@ -154,8 +166,10 @@ public: | |||
| 154 | GLuint handle = 0; | 166 | GLuint handle = 0; |
| 155 | }; | 167 | }; |
| 156 | 168 | ||
| 157 | class OGLAssemblyProgram : private NonCopyable { | 169 | class OGLAssemblyProgram final { |
| 158 | public: | 170 | public: |
| 171 | YUZU_NON_COPYABLE(OGLAssemblyProgram); | ||
| 172 | |||
| 159 | OGLAssemblyProgram() = default; | 173 | OGLAssemblyProgram() = default; |
| 160 | 174 | ||
| 161 | OGLAssemblyProgram(OGLAssemblyProgram&& o) noexcept : handle(std::exchange(o.handle, 0)) {} | 175 | OGLAssemblyProgram(OGLAssemblyProgram&& o) noexcept : handle(std::exchange(o.handle, 0)) {} |
| @@ -176,8 +190,10 @@ public: | |||
| 176 | GLuint handle = 0; | 190 | GLuint handle = 0; |
| 177 | }; | 191 | }; |
| 178 | 192 | ||
| 179 | class OGLPipeline : private NonCopyable { | 193 | class OGLPipeline final { |
| 180 | public: | 194 | public: |
| 195 | YUZU_NON_COPYABLE(OGLPipeline); | ||
| 196 | |||
| 181 | OGLPipeline() = default; | 197 | OGLPipeline() = default; |
| 182 | OGLPipeline(OGLPipeline&& o) noexcept : handle{std::exchange<GLuint>(o.handle, 0)} {} | 198 | OGLPipeline(OGLPipeline&& o) noexcept : handle{std::exchange<GLuint>(o.handle, 0)} {} |
| 183 | 199 | ||
| @@ -198,8 +214,10 @@ public: | |||
| 198 | GLuint handle = 0; | 214 | GLuint handle = 0; |
| 199 | }; | 215 | }; |
| 200 | 216 | ||
| 201 | class OGLBuffer : private NonCopyable { | 217 | class OGLBuffer final { |
| 202 | public: | 218 | public: |
| 219 | YUZU_NON_COPYABLE(OGLBuffer); | ||
| 220 | |||
| 203 | OGLBuffer() = default; | 221 | OGLBuffer() = default; |
| 204 | 222 | ||
| 205 | OGLBuffer(OGLBuffer&& o) noexcept : handle(std::exchange(o.handle, 0)) {} | 223 | OGLBuffer(OGLBuffer&& o) noexcept : handle(std::exchange(o.handle, 0)) {} |
| @@ -223,8 +241,10 @@ public: | |||
| 223 | GLuint handle = 0; | 241 | GLuint handle = 0; |
| 224 | }; | 242 | }; |
| 225 | 243 | ||
| 226 | class OGLSync : private NonCopyable { | 244 | class OGLSync final { |
| 227 | public: | 245 | public: |
| 246 | YUZU_NON_COPYABLE(OGLSync); | ||
| 247 | |||
| 228 | OGLSync() = default; | 248 | OGLSync() = default; |
| 229 | 249 | ||
| 230 | OGLSync(OGLSync&& o) noexcept : handle(std::exchange(o.handle, nullptr)) {} | 250 | OGLSync(OGLSync&& o) noexcept : handle(std::exchange(o.handle, nullptr)) {} |
| @@ -247,8 +267,10 @@ public: | |||
| 247 | GLsync handle = 0; | 267 | GLsync handle = 0; |
| 248 | }; | 268 | }; |
| 249 | 269 | ||
| 250 | class OGLFramebuffer : private NonCopyable { | 270 | class OGLFramebuffer final { |
| 251 | public: | 271 | public: |
| 272 | YUZU_NON_COPYABLE(OGLFramebuffer); | ||
| 273 | |||
| 252 | OGLFramebuffer() = default; | 274 | OGLFramebuffer() = default; |
| 253 | 275 | ||
| 254 | OGLFramebuffer(OGLFramebuffer&& o) noexcept : handle(std::exchange(o.handle, 0)) {} | 276 | OGLFramebuffer(OGLFramebuffer&& o) noexcept : handle(std::exchange(o.handle, 0)) {} |
| @@ -272,8 +294,10 @@ public: | |||
| 272 | GLuint handle = 0; | 294 | GLuint handle = 0; |
| 273 | }; | 295 | }; |
| 274 | 296 | ||
| 275 | class OGLQuery : private NonCopyable { | 297 | class OGLQuery final { |
| 276 | public: | 298 | public: |
| 299 | YUZU_NON_COPYABLE(OGLQuery); | ||
| 300 | |||
| 277 | OGLQuery() = default; | 301 | OGLQuery() = default; |
| 278 | 302 | ||
| 279 | OGLQuery(OGLQuery&& o) noexcept : handle(std::exchange(o.handle, 0)) {} | 303 | OGLQuery(OGLQuery&& o) noexcept : handle(std::exchange(o.handle, 0)) {} |
diff --git a/src/video_core/renderer_opengl/gl_state_tracker.h b/src/video_core/renderer_opengl/gl_state_tracker.h index 5864c7c07..550ed6d36 100644 --- a/src/video_core/renderer_opengl/gl_state_tracker.h +++ b/src/video_core/renderer_opengl/gl_state_tracker.h | |||
| @@ -9,7 +9,6 @@ | |||
| 9 | #include <glad/glad.h> | 9 | #include <glad/glad.h> |
| 10 | 10 | ||
| 11 | #include "common/common_types.h" | 11 | #include "common/common_types.h" |
| 12 | #include "core/core.h" | ||
| 13 | #include "video_core/dirty_flags.h" | 12 | #include "video_core/dirty_flags.h" |
| 14 | #include "video_core/engines/maxwell_3d.h" | 13 | #include "video_core/engines/maxwell_3d.h" |
| 15 | 14 | ||
diff --git a/src/video_core/renderer_opengl/maxwell_to_gl.h b/src/video_core/renderer_opengl/maxwell_to_gl.h index daba42ed9..db5bf1d30 100644 --- a/src/video_core/renderer_opengl/maxwell_to_gl.h +++ b/src/video_core/renderer_opengl/maxwell_to_gl.h | |||
| @@ -184,6 +184,8 @@ inline GLenum VertexFormat(Maxwell::VertexAttribute attrib) { | |||
| 184 | case Maxwell::VertexAttribute::Size::Size_32_32_32: | 184 | case Maxwell::VertexAttribute::Size::Size_32_32_32: |
| 185 | case Maxwell::VertexAttribute::Size::Size_32_32_32_32: | 185 | case Maxwell::VertexAttribute::Size::Size_32_32_32_32: |
| 186 | return GL_FLOAT; | 186 | return GL_FLOAT; |
| 187 | case Maxwell::VertexAttribute::Size::Size_11_11_10: | ||
| 188 | return GL_UNSIGNED_INT_10F_11F_11F_REV; | ||
| 187 | default: | 189 | default: |
| 188 | break; | 190 | break; |
| 189 | } | 191 | } |
diff --git a/src/video_core/renderer_vulkan/blit_image.cpp b/src/video_core/renderer_vulkan/blit_image.cpp index 2c3914459..ec03cca38 100644 --- a/src/video_core/renderer_vulkan/blit_image.cpp +++ b/src/video_core/renderer_vulkan/blit_image.cpp | |||
| @@ -9,6 +9,7 @@ | |||
| 9 | #include "video_core/host_shaders/convert_d24s8_to_abgr8_frag_spv.h" | 9 | #include "video_core/host_shaders/convert_d24s8_to_abgr8_frag_spv.h" |
| 10 | #include "video_core/host_shaders/convert_depth_to_float_frag_spv.h" | 10 | #include "video_core/host_shaders/convert_depth_to_float_frag_spv.h" |
| 11 | #include "video_core/host_shaders/convert_float_to_depth_frag_spv.h" | 11 | #include "video_core/host_shaders/convert_float_to_depth_frag_spv.h" |
| 12 | #include "video_core/host_shaders/convert_s8d24_to_abgr8_frag_spv.h" | ||
| 12 | #include "video_core/host_shaders/full_screen_triangle_vert_spv.h" | 13 | #include "video_core/host_shaders/full_screen_triangle_vert_spv.h" |
| 13 | #include "video_core/host_shaders/vulkan_blit_color_float_frag_spv.h" | 14 | #include "video_core/host_shaders/vulkan_blit_color_float_frag_spv.h" |
| 14 | #include "video_core/host_shaders/vulkan_blit_depth_stencil_frag_spv.h" | 15 | #include "video_core/host_shaders/vulkan_blit_depth_stencil_frag_spv.h" |
| @@ -370,6 +371,7 @@ BlitImageHelper::BlitImageHelper(const Device& device_, VKScheduler& scheduler_, | |||
| 370 | convert_float_to_depth_frag(BuildShader(device, CONVERT_FLOAT_TO_DEPTH_FRAG_SPV)), | 371 | convert_float_to_depth_frag(BuildShader(device, CONVERT_FLOAT_TO_DEPTH_FRAG_SPV)), |
| 371 | convert_abgr8_to_d24s8_frag(BuildShader(device, CONVERT_ABGR8_TO_D24S8_FRAG_SPV)), | 372 | convert_abgr8_to_d24s8_frag(BuildShader(device, CONVERT_ABGR8_TO_D24S8_FRAG_SPV)), |
| 372 | convert_d24s8_to_abgr8_frag(BuildShader(device, CONVERT_D24S8_TO_ABGR8_FRAG_SPV)), | 373 | convert_d24s8_to_abgr8_frag(BuildShader(device, CONVERT_D24S8_TO_ABGR8_FRAG_SPV)), |
| 374 | convert_s8d24_to_abgr8_frag(BuildShader(device, CONVERT_S8D24_TO_ABGR8_FRAG_SPV)), | ||
| 373 | linear_sampler(device.GetLogical().CreateSampler(SAMPLER_CREATE_INFO<VK_FILTER_LINEAR>)), | 375 | linear_sampler(device.GetLogical().CreateSampler(SAMPLER_CREATE_INFO<VK_FILTER_LINEAR>)), |
| 374 | nearest_sampler(device.GetLogical().CreateSampler(SAMPLER_CREATE_INFO<VK_FILTER_NEAREST>)) { | 376 | nearest_sampler(device.GetLogical().CreateSampler(SAMPLER_CREATE_INFO<VK_FILTER_NEAREST>)) { |
| 375 | if (device.IsExtShaderStencilExportSupported()) { | 377 | if (device.IsExtShaderStencilExportSupported()) { |
| @@ -474,6 +476,13 @@ void BlitImageHelper::ConvertD24S8ToABGR8(const Framebuffer* dst_framebuffer, | |||
| 474 | ConvertDepthStencil(*convert_d24s8_to_abgr8_pipeline, dst_framebuffer, src_image_view); | 476 | ConvertDepthStencil(*convert_d24s8_to_abgr8_pipeline, dst_framebuffer, src_image_view); |
| 475 | } | 477 | } |
| 476 | 478 | ||
| 479 | void BlitImageHelper::ConvertS8D24ToABGR8(const Framebuffer* dst_framebuffer, | ||
| 480 | ImageView& src_image_view) { | ||
| 481 | ConvertPipelineColorTargetEx(convert_s8d24_to_abgr8_pipeline, dst_framebuffer->RenderPass(), | ||
| 482 | convert_s8d24_to_abgr8_frag); | ||
| 483 | ConvertDepthStencil(*convert_s8d24_to_abgr8_pipeline, dst_framebuffer, src_image_view); | ||
| 484 | } | ||
| 485 | |||
| 477 | void BlitImageHelper::Convert(VkPipeline pipeline, const Framebuffer* dst_framebuffer, | 486 | void BlitImageHelper::Convert(VkPipeline pipeline, const Framebuffer* dst_framebuffer, |
| 478 | const ImageView& src_image_view) { | 487 | const ImageView& src_image_view) { |
| 479 | const VkPipelineLayout layout = *one_texture_pipeline_layout; | 488 | const VkPipelineLayout layout = *one_texture_pipeline_layout; |
diff --git a/src/video_core/renderer_vulkan/blit_image.h b/src/video_core/renderer_vulkan/blit_image.h index 85e7dca5b..1a3944179 100644 --- a/src/video_core/renderer_vulkan/blit_image.h +++ b/src/video_core/renderer_vulkan/blit_image.h | |||
| @@ -56,6 +56,8 @@ public: | |||
| 56 | 56 | ||
| 57 | void ConvertD24S8ToABGR8(const Framebuffer* dst_framebuffer, ImageView& src_image_view); | 57 | void ConvertD24S8ToABGR8(const Framebuffer* dst_framebuffer, ImageView& src_image_view); |
| 58 | 58 | ||
| 59 | void ConvertS8D24ToABGR8(const Framebuffer* dst_framebuffer, ImageView& src_image_view); | ||
| 60 | |||
| 59 | private: | 61 | private: |
| 60 | void Convert(VkPipeline pipeline, const Framebuffer* dst_framebuffer, | 62 | void Convert(VkPipeline pipeline, const Framebuffer* dst_framebuffer, |
| 61 | const ImageView& src_image_view); | 63 | const ImageView& src_image_view); |
| @@ -99,6 +101,7 @@ private: | |||
| 99 | vk::ShaderModule convert_float_to_depth_frag; | 101 | vk::ShaderModule convert_float_to_depth_frag; |
| 100 | vk::ShaderModule convert_abgr8_to_d24s8_frag; | 102 | vk::ShaderModule convert_abgr8_to_d24s8_frag; |
| 101 | vk::ShaderModule convert_d24s8_to_abgr8_frag; | 103 | vk::ShaderModule convert_d24s8_to_abgr8_frag; |
| 104 | vk::ShaderModule convert_s8d24_to_abgr8_frag; | ||
| 102 | vk::Sampler linear_sampler; | 105 | vk::Sampler linear_sampler; |
| 103 | vk::Sampler nearest_sampler; | 106 | vk::Sampler nearest_sampler; |
| 104 | 107 | ||
| @@ -112,6 +115,7 @@ private: | |||
| 112 | vk::Pipeline convert_r16_to_d16_pipeline; | 115 | vk::Pipeline convert_r16_to_d16_pipeline; |
| 113 | vk::Pipeline convert_abgr8_to_d24s8_pipeline; | 116 | vk::Pipeline convert_abgr8_to_d24s8_pipeline; |
| 114 | vk::Pipeline convert_d24s8_to_abgr8_pipeline; | 117 | vk::Pipeline convert_d24s8_to_abgr8_pipeline; |
| 118 | vk::Pipeline convert_s8d24_to_abgr8_pipeline; | ||
| 115 | }; | 119 | }; |
| 116 | 120 | ||
| 117 | } // namespace Vulkan | 121 | } // namespace Vulkan |
diff --git a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp index 751e4792b..1c136c410 100644 --- a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp +++ b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp | |||
| @@ -495,6 +495,8 @@ VkFormat VertexFormat(Maxwell::VertexAttribute::Type type, Maxwell::VertexAttrib | |||
| 495 | return VK_FORMAT_R32G32B32_SFLOAT; | 495 | return VK_FORMAT_R32G32B32_SFLOAT; |
| 496 | case Maxwell::VertexAttribute::Size::Size_32_32_32_32: | 496 | case Maxwell::VertexAttribute::Size::Size_32_32_32_32: |
| 497 | return VK_FORMAT_R32G32B32A32_SFLOAT; | 497 | return VK_FORMAT_R32G32B32A32_SFLOAT; |
| 498 | case Maxwell::VertexAttribute::Size::Size_11_11_10: | ||
| 499 | return VK_FORMAT_B10G11R11_UFLOAT_PACK32; | ||
| 498 | default: | 500 | default: |
| 499 | break; | 501 | break; |
| 500 | } | 502 | } |
diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.cpp b/src/video_core/renderer_vulkan/vk_blit_screen.cpp index c71a1f44d..621a6a071 100644 --- a/src/video_core/renderer_vulkan/vk_blit_screen.cpp +++ b/src/video_core/renderer_vulkan/vk_blit_screen.cpp | |||
| @@ -100,6 +100,8 @@ VkFormat GetFormat(const Tegra::FramebufferConfig& framebuffer) { | |||
| 100 | return VK_FORMAT_A8B8G8R8_UNORM_PACK32; | 100 | return VK_FORMAT_A8B8G8R8_UNORM_PACK32; |
| 101 | case Tegra::FramebufferConfig::PixelFormat::RGB565_UNORM: | 101 | case Tegra::FramebufferConfig::PixelFormat::RGB565_UNORM: |
| 102 | return VK_FORMAT_R5G6B5_UNORM_PACK16; | 102 | return VK_FORMAT_R5G6B5_UNORM_PACK16; |
| 103 | case Tegra::FramebufferConfig::PixelFormat::B8G8R8A8_UNORM: | ||
| 104 | return VK_FORMAT_B8G8R8A8_UNORM; | ||
| 103 | default: | 105 | default: |
| 104 | UNIMPLEMENTED_MSG("Unknown framebuffer pixel format: {}", | 106 | UNIMPLEMENTED_MSG("Unknown framebuffer pixel format: {}", |
| 105 | static_cast<u32>(framebuffer.pixel_format)); | 107 | static_cast<u32>(framebuffer.pixel_format)); |
diff --git a/src/video_core/renderer_vulkan/vk_compute_pass.cpp b/src/video_core/renderer_vulkan/vk_compute_pass.cpp index 3e96c0f60..4d73427b4 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pass.cpp +++ b/src/video_core/renderer_vulkan/vk_compute_pass.cpp | |||
| @@ -2,6 +2,7 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <array> | ||
| 5 | #include <cstring> | 6 | #include <cstring> |
| 6 | #include <memory> | 7 | #include <memory> |
| 7 | #include <optional> | 8 | #include <optional> |
| @@ -292,7 +293,7 @@ std::pair<VkBuffer, VkDeviceSize> QuadIndexedPass::Assemble( | |||
| 292 | .srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT, | 293 | .srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT, |
| 293 | .dstAccessMask = VK_ACCESS_INDEX_READ_BIT, | 294 | .dstAccessMask = VK_ACCESS_INDEX_READ_BIT, |
| 294 | }; | 295 | }; |
| 295 | const std::array push_constants{base_vertex, index_shift}; | 296 | const std::array<u32, 2> push_constants{base_vertex, index_shift}; |
| 296 | const VkDescriptorSet set = descriptor_allocator.Commit(); | 297 | const VkDescriptorSet set = descriptor_allocator.Commit(); |
| 297 | device.GetLogical().UpdateDescriptorSet(set, *descriptor_template, descriptor_data); | 298 | device.GetLogical().UpdateDescriptorSet(set, *descriptor_template, descriptor_data); |
| 298 | cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline); | 299 | cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline); |
diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp index 3bfdf41ba..7d9d4f7ba 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.cpp +++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp | |||
| @@ -140,12 +140,12 @@ bool VKScheduler::UpdateRescaling(bool is_rescaling) { | |||
| 140 | void VKScheduler::WorkerThread(std::stop_token stop_token) { | 140 | void VKScheduler::WorkerThread(std::stop_token stop_token) { |
| 141 | Common::SetCurrentThreadName("yuzu:VulkanWorker"); | 141 | Common::SetCurrentThreadName("yuzu:VulkanWorker"); |
| 142 | do { | 142 | do { |
| 143 | if (work_queue.empty()) { | ||
| 144 | wait_cv.notify_all(); | ||
| 145 | } | ||
| 146 | std::unique_ptr<CommandChunk> work; | 143 | std::unique_ptr<CommandChunk> work; |
| 147 | { | 144 | { |
| 148 | std::unique_lock lock{work_mutex}; | 145 | std::unique_lock lock{work_mutex}; |
| 146 | if (work_queue.empty()) { | ||
| 147 | wait_cv.notify_all(); | ||
| 148 | } | ||
| 149 | work_cv.wait(lock, stop_token, [this] { return !work_queue.empty(); }); | 149 | work_cv.wait(lock, stop_token, [this] { return !work_queue.empty(); }); |
| 150 | if (stop_token.stop_requested()) { | 150 | if (stop_token.stop_requested()) { |
| 151 | continue; | 151 | continue; |
diff --git a/src/video_core/renderer_vulkan/vk_scheduler.h b/src/video_core/renderer_vulkan/vk_scheduler.h index 1b06c9296..e69aa136b 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.h +++ b/src/video_core/renderer_vulkan/vk_scheduler.h | |||
| @@ -146,6 +146,7 @@ private: | |||
| 146 | using FuncType = TypedCommand<T>; | 146 | using FuncType = TypedCommand<T>; |
| 147 | static_assert(sizeof(FuncType) < sizeof(data), "Lambda is too large"); | 147 | static_assert(sizeof(FuncType) < sizeof(data), "Lambda is too large"); |
| 148 | 148 | ||
| 149 | recorded_counts++; | ||
| 149 | command_offset = Common::AlignUp(command_offset, alignof(FuncType)); | 150 | command_offset = Common::AlignUp(command_offset, alignof(FuncType)); |
| 150 | if (command_offset > sizeof(data) - sizeof(FuncType)) { | 151 | if (command_offset > sizeof(data) - sizeof(FuncType)) { |
| 151 | return false; | 152 | return false; |
| @@ -167,7 +168,7 @@ private: | |||
| 167 | } | 168 | } |
| 168 | 169 | ||
| 169 | bool Empty() const { | 170 | bool Empty() const { |
| 170 | return command_offset == 0; | 171 | return recorded_counts == 0; |
| 171 | } | 172 | } |
| 172 | 173 | ||
| 173 | bool HasSubmit() const { | 174 | bool HasSubmit() const { |
| @@ -178,6 +179,7 @@ private: | |||
| 178 | Command* first = nullptr; | 179 | Command* first = nullptr; |
| 179 | Command* last = nullptr; | 180 | Command* last = nullptr; |
| 180 | 181 | ||
| 182 | size_t recorded_counts = 0; | ||
| 181 | size_t command_offset = 0; | 183 | size_t command_offset = 0; |
| 182 | bool submit = false; | 184 | bool submit = false; |
| 183 | alignas(std::max_align_t) std::array<u8, 0x8000> data{}; | 185 | alignas(std::max_align_t) std::array<u8, 0x8000> data{}; |
diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.h b/src/video_core/renderer_vulkan/vk_state_tracker.h index 40a149832..8240c83e1 100644 --- a/src/video_core/renderer_vulkan/vk_state_tracker.h +++ b/src/video_core/renderer_vulkan/vk_state_tracker.h | |||
| @@ -8,7 +8,6 @@ | |||
| 8 | #include <limits> | 8 | #include <limits> |
| 9 | 9 | ||
| 10 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 11 | #include "core/core.h" | ||
| 12 | #include "video_core/dirty_flags.h" | 11 | #include "video_core/dirty_flags.h" |
| 13 | #include "video_core/engines/maxwell_3d.h" | 12 | #include "video_core/engines/maxwell_3d.h" |
| 14 | 13 | ||
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index 0ba56ff1e..8101eb42c 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp | |||
| @@ -554,10 +554,12 @@ void CopyBufferToImage(vk::CommandBuffer cmdbuf, VkBuffer src_buffer, VkImage im | |||
| 554 | }; | 554 | }; |
| 555 | } | 555 | } |
| 556 | 556 | ||
| 557 | [[nodiscard]] bool IsFormatFlipped(PixelFormat format) { | 557 | [[nodiscard]] bool IsFormatFlipped(PixelFormat format, bool emulate_bgr565) { |
| 558 | switch (format) { | 558 | switch (format) { |
| 559 | case PixelFormat::A1B5G5R5_UNORM: | 559 | case PixelFormat::A1B5G5R5_UNORM: |
| 560 | return true; | 560 | return true; |
| 561 | case PixelFormat::B5G6R5_UNORM: | ||
| 562 | return emulate_bgr565; | ||
| 561 | default: | 563 | default: |
| 562 | return false; | 564 | return false; |
| 563 | } | 565 | } |
| @@ -1068,6 +1070,9 @@ void TextureCacheRuntime::ConvertImage(Framebuffer* dst, ImageView& dst_view, Im | |||
| 1068 | if (src_view.format == PixelFormat::S8_UINT_D24_UNORM) { | 1070 | if (src_view.format == PixelFormat::S8_UINT_D24_UNORM) { |
| 1069 | return blit_image_helper.ConvertD24S8ToABGR8(dst, src_view); | 1071 | return blit_image_helper.ConvertD24S8ToABGR8(dst, src_view); |
| 1070 | } | 1072 | } |
| 1073 | if (src_view.format == PixelFormat::D24_UNORM_S8_UINT) { | ||
| 1074 | return blit_image_helper.ConvertS8D24ToABGR8(dst, src_view); | ||
| 1075 | } | ||
| 1071 | break; | 1076 | break; |
| 1072 | case PixelFormat::R32_FLOAT: | 1077 | case PixelFormat::R32_FLOAT: |
| 1073 | if (src_view.format == PixelFormat::D32_FLOAT) { | 1078 | if (src_view.format == PixelFormat::D32_FLOAT) { |
| @@ -1488,7 +1493,7 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewI | |||
| 1488 | }; | 1493 | }; |
| 1489 | if (!info.IsRenderTarget()) { | 1494 | if (!info.IsRenderTarget()) { |
| 1490 | swizzle = info.Swizzle(); | 1495 | swizzle = info.Swizzle(); |
| 1491 | if (IsFormatFlipped(format)) { | 1496 | if (IsFormatFlipped(format, device->MustEmulateBGR565())) { |
| 1492 | std::ranges::transform(swizzle, swizzle.begin(), SwapBlueRed); | 1497 | std::ranges::transform(swizzle, swizzle.begin(), SwapBlueRed); |
| 1493 | } | 1498 | } |
| 1494 | if ((aspect_mask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) != 0) { | 1499 | if ((aspect_mask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) != 0) { |
diff --git a/src/video_core/video_core.cpp b/src/video_core/video_core.cpp index 329bf4def..2f2594585 100644 --- a/src/video_core/video_core.cpp +++ b/src/video_core/video_core.cpp | |||
| @@ -50,6 +50,7 @@ std::unique_ptr<Tegra::GPU> CreateGPU(Core::Frontend::EmuWindow& emu_window, Cor | |||
| 50 | gpu->BindRenderer(std::move(renderer)); | 50 | gpu->BindRenderer(std::move(renderer)); |
| 51 | return gpu; | 51 | return gpu; |
| 52 | } catch (const std::runtime_error& exception) { | 52 | } catch (const std::runtime_error& exception) { |
| 53 | scope.Cancel(); | ||
| 53 | LOG_ERROR(HW_GPU, "Failed to initialize GPU: {}", exception.what()); | 54 | LOG_ERROR(HW_GPU, "Failed to initialize GPU: {}", exception.what()); |
| 54 | return nullptr; | 55 | return nullptr; |
| 55 | } | 56 | } |
diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index 153702c0b..effde73c9 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp | |||
| @@ -39,6 +39,11 @@ constexpr std::array DEPTH16_UNORM_STENCIL8_UINT{ | |||
| 39 | VK_FORMAT_D32_SFLOAT_S8_UINT, | 39 | VK_FORMAT_D32_SFLOAT_S8_UINT, |
| 40 | VK_FORMAT_UNDEFINED, | 40 | VK_FORMAT_UNDEFINED, |
| 41 | }; | 41 | }; |
| 42 | |||
| 43 | constexpr std::array B5G6R5_UNORM_PACK16{ | ||
| 44 | VK_FORMAT_R5G6B5_UNORM_PACK16, | ||
| 45 | VK_FORMAT_UNDEFINED, | ||
| 46 | }; | ||
| 42 | } // namespace Alternatives | 47 | } // namespace Alternatives |
| 43 | 48 | ||
| 44 | enum class NvidiaArchitecture { | 49 | enum class NvidiaArchitecture { |
| @@ -87,6 +92,8 @@ constexpr const VkFormat* GetFormatAlternatives(VkFormat format) { | |||
| 87 | return Alternatives::DEPTH24_UNORM_STENCIL8_UINT.data(); | 92 | return Alternatives::DEPTH24_UNORM_STENCIL8_UINT.data(); |
| 88 | case VK_FORMAT_D16_UNORM_S8_UINT: | 93 | case VK_FORMAT_D16_UNORM_S8_UINT: |
| 89 | return Alternatives::DEPTH16_UNORM_STENCIL8_UINT.data(); | 94 | return Alternatives::DEPTH16_UNORM_STENCIL8_UINT.data(); |
| 95 | case VK_FORMAT_B5G6R5_UNORM_PACK16: | ||
| 96 | return Alternatives::B5G6R5_UNORM_PACK16.data(); | ||
| 90 | default: | 97 | default: |
| 91 | return nullptr; | 98 | return nullptr; |
| 92 | } | 99 | } |
| @@ -224,9 +231,14 @@ std::vector<std::string> GetSupportedExtensions(vk::PhysicalDevice physical) { | |||
| 224 | return supported_extensions; | 231 | return supported_extensions; |
| 225 | } | 232 | } |
| 226 | 233 | ||
| 234 | bool IsExtensionSupported(std::span<const std::string> supported_extensions, | ||
| 235 | std::string_view extension) { | ||
| 236 | return std::ranges::find(supported_extensions, extension) != supported_extensions.end(); | ||
| 237 | } | ||
| 238 | |||
| 227 | NvidiaArchitecture GetNvidiaArchitecture(vk::PhysicalDevice physical, | 239 | NvidiaArchitecture GetNvidiaArchitecture(vk::PhysicalDevice physical, |
| 228 | std::span<const std::string> exts) { | 240 | std::span<const std::string> exts) { |
| 229 | if (std::ranges::find(exts, VK_KHR_FRAGMENT_SHADING_RATE_EXTENSION_NAME) != exts.end()) { | 241 | if (IsExtensionSupported(exts, VK_KHR_FRAGMENT_SHADING_RATE_EXTENSION_NAME)) { |
| 230 | VkPhysicalDeviceFragmentShadingRatePropertiesKHR shading_rate_props{}; | 242 | VkPhysicalDeviceFragmentShadingRatePropertiesKHR shading_rate_props{}; |
| 231 | shading_rate_props.sType = | 243 | shading_rate_props.sType = |
| 232 | VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_PROPERTIES_KHR; | 244 | VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_PROPERTIES_KHR; |
| @@ -239,7 +251,7 @@ NvidiaArchitecture GetNvidiaArchitecture(vk::PhysicalDevice physical, | |||
| 239 | return NvidiaArchitecture::AmpereOrNewer; | 251 | return NvidiaArchitecture::AmpereOrNewer; |
| 240 | } | 252 | } |
| 241 | } | 253 | } |
| 242 | if (std::ranges::find(exts, VK_NV_SHADING_RATE_IMAGE_EXTENSION_NAME) != exts.end()) { | 254 | if (IsExtensionSupported(exts, VK_NV_SHADING_RATE_IMAGE_EXTENSION_NAME)) { |
| 243 | return NvidiaArchitecture::Turing; | 255 | return NvidiaArchitecture::Turing; |
| 244 | } | 256 | } |
| 245 | return NvidiaArchitecture::VoltaOrOlder; | 257 | return NvidiaArchitecture::VoltaOrOlder; |
| @@ -604,7 +616,8 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR | |||
| 604 | break; | 616 | break; |
| 605 | } | 617 | } |
| 606 | } | 618 | } |
| 607 | if (ext_extended_dynamic_state && driver_id == VK_DRIVER_ID_MESA_RADV) { | 619 | const bool is_radv = driver_id == VK_DRIVER_ID_MESA_RADV; |
| 620 | if (ext_extended_dynamic_state && is_radv) { | ||
| 608 | // Mask driver version variant | 621 | // Mask driver version variant |
| 609 | const u32 version = (properties.driverVersion << 3) >> 3; | 622 | const u32 version = (properties.driverVersion << 3) >> 3; |
| 610 | if (version < VK_MAKE_API_VERSION(0, 21, 2, 0)) { | 623 | if (version < VK_MAKE_API_VERSION(0, 21, 2, 0)) { |
| @@ -613,6 +626,17 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR | |||
| 613 | ext_extended_dynamic_state = false; | 626 | ext_extended_dynamic_state = false; |
| 614 | } | 627 | } |
| 615 | } | 628 | } |
| 629 | if (ext_vertex_input_dynamic_state && is_radv) { | ||
| 630 | // TODO(ameerj): Blacklist only offending driver versions | ||
| 631 | // TODO(ameerj): Confirm if RDNA1 is affected | ||
| 632 | const bool is_rdna2 = | ||
| 633 | IsExtensionSupported(supported_extensions, VK_KHR_FRAGMENT_SHADING_RATE_EXTENSION_NAME); | ||
| 634 | if (is_rdna2) { | ||
| 635 | LOG_WARNING(Render_Vulkan, | ||
| 636 | "RADV has broken VK_EXT_vertex_input_dynamic_state on RDNA2 hardware"); | ||
| 637 | ext_vertex_input_dynamic_state = false; | ||
| 638 | } | ||
| 639 | } | ||
| 616 | sets_per_pool = 64; | 640 | sets_per_pool = 64; |
| 617 | 641 | ||
| 618 | const bool is_amd = | 642 | const bool is_amd = |
| @@ -628,7 +652,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR | |||
| 628 | has_broken_cube_compatibility = true; | 652 | has_broken_cube_compatibility = true; |
| 629 | } | 653 | } |
| 630 | } | 654 | } |
| 631 | const bool is_amd_or_radv = is_amd || driver_id == VK_DRIVER_ID_MESA_RADV; | 655 | const bool is_amd_or_radv = is_amd || is_radv; |
| 632 | if (ext_sampler_filter_minmax && is_amd_or_radv) { | 656 | if (ext_sampler_filter_minmax && is_amd_or_radv) { |
| 633 | // Disable ext_sampler_filter_minmax on AMD GCN4 and lower as it is broken. | 657 | // Disable ext_sampler_filter_minmax on AMD GCN4 and lower as it is broken. |
| 634 | if (!is_float16_supported) { | 658 | if (!is_float16_supported) { |
| @@ -639,6 +663,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR | |||
| 639 | } | 663 | } |
| 640 | 664 | ||
| 641 | const bool is_intel_windows = driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS; | 665 | const bool is_intel_windows = driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS; |
| 666 | const bool is_intel_anv = driver_id == VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA; | ||
| 642 | if (ext_vertex_input_dynamic_state && is_intel_windows) { | 667 | if (ext_vertex_input_dynamic_state && is_intel_windows) { |
| 643 | LOG_WARNING(Render_Vulkan, "Blacklisting Intel for VK_EXT_vertex_input_dynamic_state"); | 668 | LOG_WARNING(Render_Vulkan, "Blacklisting Intel for VK_EXT_vertex_input_dynamic_state"); |
| 644 | ext_vertex_input_dynamic_state = false; | 669 | ext_vertex_input_dynamic_state = false; |
| @@ -652,6 +677,10 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR | |||
| 652 | LOG_WARNING(Render_Vulkan, "Intel proprietary drivers do not support MSAA image blits"); | 677 | LOG_WARNING(Render_Vulkan, "Intel proprietary drivers do not support MSAA image blits"); |
| 653 | cant_blit_msaa = true; | 678 | cant_blit_msaa = true; |
| 654 | } | 679 | } |
| 680 | if (is_intel_anv) { | ||
| 681 | LOG_WARNING(Render_Vulkan, "ANV driver does not support native BGR format"); | ||
| 682 | must_emulate_bgr565 = true; | ||
| 683 | } | ||
| 655 | 684 | ||
| 656 | supports_d24_depth = | 685 | supports_d24_depth = |
| 657 | IsFormatSupported(VK_FORMAT_D24_UNORM_S8_UINT, | 686 | IsFormatSupported(VK_FORMAT_D24_UNORM_S8_UINT, |
diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index 37d140ebd..34b1add16 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h | |||
| @@ -354,6 +354,10 @@ public: | |||
| 354 | return cant_blit_msaa; | 354 | return cant_blit_msaa; |
| 355 | } | 355 | } |
| 356 | 356 | ||
| 357 | bool MustEmulateBGR565() const { | ||
| 358 | return must_emulate_bgr565; | ||
| 359 | } | ||
| 360 | |||
| 357 | private: | 361 | private: |
| 358 | /// Checks if the physical device is suitable. | 362 | /// Checks if the physical device is suitable. |
| 359 | void CheckSuitability(bool requires_swapchain) const; | 363 | void CheckSuitability(bool requires_swapchain) const; |
| @@ -448,6 +452,7 @@ private: | |||
| 448 | bool has_nsight_graphics{}; ///< Has Nsight Graphics attached | 452 | bool has_nsight_graphics{}; ///< Has Nsight Graphics attached |
| 449 | bool supports_d24_depth{}; ///< Supports D24 depth buffers. | 453 | bool supports_d24_depth{}; ///< Supports D24 depth buffers. |
| 450 | bool cant_blit_msaa{}; ///< Does not support MSAA<->MSAA blitting. | 454 | bool cant_blit_msaa{}; ///< Does not support MSAA<->MSAA blitting. |
| 455 | bool must_emulate_bgr565{}; ///< Emulates BGR565 by swizzling RGB565 format. | ||
| 451 | 456 | ||
| 452 | // Telemetry parameters | 457 | // Telemetry parameters |
| 453 | std::string vendor_name; ///< Device's driver name. | 458 | std::string vendor_name; ///< Device's driver name. |
diff --git a/src/yuzu/applets/qt_controller.cpp b/src/yuzu/applets/qt_controller.cpp index 4239c17f5..4104928d1 100644 --- a/src/yuzu/applets/qt_controller.cpp +++ b/src/yuzu/applets/qt_controller.cpp | |||
| @@ -257,7 +257,7 @@ void QtControllerSelectorDialog::LoadConfiguration() { | |||
| 257 | } | 257 | } |
| 258 | 258 | ||
| 259 | void QtControllerSelectorDialog::CallConfigureVibrationDialog() { | 259 | void QtControllerSelectorDialog::CallConfigureVibrationDialog() { |
| 260 | ConfigureVibration dialog(this); | 260 | ConfigureVibration dialog(this, system.HIDCore()); |
| 261 | 261 | ||
| 262 | dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint | | 262 | dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint | |
| 263 | Qt::WindowSystemMenuHint); | 263 | Qt::WindowSystemMenuHint); |
diff --git a/src/yuzu/applets/qt_profile_select.cpp b/src/yuzu/applets/qt_profile_select.cpp index 5b32da923..4cd8f7784 100644 --- a/src/yuzu/applets/qt_profile_select.cpp +++ b/src/yuzu/applets/qt_profile_select.cpp | |||
| @@ -23,13 +23,13 @@ QString FormatUserEntryText(const QString& username, Common::UUID uuid) { | |||
| 23 | return QtProfileSelectionDialog::tr( | 23 | return QtProfileSelectionDialog::tr( |
| 24 | "%1\n%2", "%1 is the profile username, %2 is the formatted UUID (e.g. " | 24 | "%1\n%2", "%1 is the profile username, %2 is the formatted UUID (e.g. " |
| 25 | "00112233-4455-6677-8899-AABBCCDDEEFF))") | 25 | "00112233-4455-6677-8899-AABBCCDDEEFF))") |
| 26 | .arg(username, QString::fromStdString(uuid.FormatSwitch())); | 26 | .arg(username, QString::fromStdString(uuid.FormattedString())); |
| 27 | } | 27 | } |
| 28 | 28 | ||
| 29 | QString GetImagePath(Common::UUID uuid) { | 29 | QString GetImagePath(Common::UUID uuid) { |
| 30 | const auto path = | 30 | const auto path = |
| 31 | Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / | 31 | Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / |
| 32 | fmt::format("system/save/8000000000000010/su/avators/{}.jpg", uuid.FormatSwitch()); | 32 | fmt::format("system/save/8000000000000010/su/avators/{}.jpg", uuid.FormattedString()); |
| 33 | return QString::fromStdString(Common::FS::PathToUTF8String(path)); | 33 | return QString::fromStdString(Common::FS::PathToUTF8String(path)); |
| 34 | } | 34 | } |
| 35 | 35 | ||
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 2c8c10c50..4b943c6ba 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp | |||
| @@ -65,23 +65,25 @@ const std::array<int, 2> Config::default_stick_mod = { | |||
| 65 | // This must be in alphabetical order according to action name as it must have the same order as | 65 | // This must be in alphabetical order according to action name as it must have the same order as |
| 66 | // UISetting::values.shortcuts, which is alphabetically ordered. | 66 | // UISetting::values.shortcuts, which is alphabetically ordered. |
| 67 | // clang-format off | 67 | // clang-format off |
| 68 | const std::array<UISettings::Shortcut, 20> Config::default_hotkeys{{ | 68 | const std::array<UISettings::Shortcut, 22> Config::default_hotkeys{{ |
| 69 | {QStringLiteral("Audio Mute/Unmute"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+M"), QStringLiteral("Home+Dpad_Right"), Qt::WindowShortcut}}, | 69 | {QStringLiteral("Audio Mute/Unmute"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+M"), QStringLiteral("Home+Dpad_Right"), Qt::WindowShortcut}}, |
| 70 | {QStringLiteral("Audio Volume Down"), QStringLiteral("Main Window"), {QStringLiteral("-"), QStringLiteral("Home+Dpad_Down"), Qt::ApplicationShortcut}}, | 70 | {QStringLiteral("Audio Volume Down"), QStringLiteral("Main Window"), {QStringLiteral("-"), QStringLiteral("Home+Dpad_Down"), Qt::ApplicationShortcut}}, |
| 71 | {QStringLiteral("Audio Volume Up"), QStringLiteral("Main Window"), {QStringLiteral("+"), QStringLiteral("Home+Dpad_Up"), Qt::ApplicationShortcut}}, | 71 | {QStringLiteral("Audio Volume Up"), QStringLiteral("Main Window"), {QStringLiteral("+"), QStringLiteral("Home+Dpad_Up"), Qt::ApplicationShortcut}}, |
| 72 | {QStringLiteral("Capture Screenshot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+P"), QStringLiteral("Screenshot"), Qt::WidgetWithChildrenShortcut}}, | 72 | {QStringLiteral("Capture Screenshot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+P"), QStringLiteral("Screenshot"), Qt::WidgetWithChildrenShortcut}}, |
| 73 | {QStringLiteral("Change Adapting Filter"), QStringLiteral("Main Window"), {QStringLiteral("F8"), QStringLiteral("Home+L"), Qt::ApplicationShortcut}}, | ||
| 73 | {QStringLiteral("Change Docked Mode"), QStringLiteral("Main Window"), {QStringLiteral("F10"), QStringLiteral("Home+X"), Qt::ApplicationShortcut}}, | 74 | {QStringLiteral("Change Docked Mode"), QStringLiteral("Main Window"), {QStringLiteral("F10"), QStringLiteral("Home+X"), Qt::ApplicationShortcut}}, |
| 75 | {QStringLiteral("Change GPU Accuracy"), QStringLiteral("Main Window"), {QStringLiteral("F9"), QStringLiteral("Home+R"), Qt::ApplicationShortcut}}, | ||
| 74 | {QStringLiteral("Continue/Pause Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F4"), QStringLiteral("Home+Plus"), Qt::WindowShortcut}}, | 76 | {QStringLiteral("Continue/Pause Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F4"), QStringLiteral("Home+Plus"), Qt::WindowShortcut}}, |
| 75 | {QStringLiteral("Exit Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("Esc"), QStringLiteral(""), Qt::WindowShortcut}}, | 77 | {QStringLiteral("Exit Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("Esc"), QStringLiteral(""), Qt::WindowShortcut}}, |
| 76 | {QStringLiteral("Exit yuzu"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Q"), QStringLiteral("Home+Minus"), Qt::WindowShortcut}}, | 78 | {QStringLiteral("Exit yuzu"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Q"), QStringLiteral("Home+Minus"), Qt::WindowShortcut}}, |
| 77 | {QStringLiteral("Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("F11"), QStringLiteral("Home+B"), Qt::WindowShortcut}}, | 79 | {QStringLiteral("Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("F11"), QStringLiteral("Home+B"), Qt::WindowShortcut}}, |
| 78 | {QStringLiteral("Load Amiibo"), QStringLiteral("Main Window"), {QStringLiteral("F2"), QStringLiteral("Home+A"), Qt::WidgetWithChildrenShortcut}}, | ||
| 79 | {QStringLiteral("Load File"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+O"), QStringLiteral(""), Qt::WidgetWithChildrenShortcut}}, | 80 | {QStringLiteral("Load File"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+O"), QStringLiteral(""), Qt::WidgetWithChildrenShortcut}}, |
| 81 | {QStringLiteral("Load/Remove Amiibo"), QStringLiteral("Main Window"), {QStringLiteral("F2"), QStringLiteral("Home+A"), Qt::WidgetWithChildrenShortcut}}, | ||
| 80 | {QStringLiteral("Restart Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F6"), QStringLiteral(""), Qt::WindowShortcut}}, | 82 | {QStringLiteral("Restart Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F6"), QStringLiteral(""), Qt::WindowShortcut}}, |
| 81 | {QStringLiteral("Stop Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F5"), QStringLiteral(""), Qt::WindowShortcut}}, | 83 | {QStringLiteral("Stop Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F5"), QStringLiteral(""), Qt::WindowShortcut}}, |
| 82 | {QStringLiteral("TAS Start/Stop"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F5"), QStringLiteral(""), Qt::ApplicationShortcut}}, | ||
| 83 | {QStringLiteral("TAS Reset"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F6"), QStringLiteral(""), Qt::ApplicationShortcut}}, | ||
| 84 | {QStringLiteral("TAS Record"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F7"), QStringLiteral(""), Qt::ApplicationShortcut}}, | 84 | {QStringLiteral("TAS Record"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F7"), QStringLiteral(""), Qt::ApplicationShortcut}}, |
| 85 | {QStringLiteral("TAS Reset"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F6"), QStringLiteral(""), Qt::ApplicationShortcut}}, | ||
| 86 | {QStringLiteral("TAS Start/Stop"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F5"), QStringLiteral(""), Qt::ApplicationShortcut}}, | ||
| 85 | {QStringLiteral("Toggle Filter Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F"), QStringLiteral(""), Qt::WindowShortcut}}, | 87 | {QStringLiteral("Toggle Filter Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F"), QStringLiteral(""), Qt::WindowShortcut}}, |
| 86 | {QStringLiteral("Toggle Framerate Limit"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+U"), QStringLiteral("Home+Y"), Qt::ApplicationShortcut}}, | 88 | {QStringLiteral("Toggle Framerate Limit"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+U"), QStringLiteral("Home+Y"), Qt::ApplicationShortcut}}, |
| 87 | {QStringLiteral("Toggle Mouse Panning"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F9"), QStringLiteral(""), Qt::ApplicationShortcut}}, | 89 | {QStringLiteral("Toggle Mouse Panning"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F9"), QStringLiteral(""), Qt::ApplicationShortcut}}, |
| @@ -443,6 +445,7 @@ void Config::ReadCoreValues() { | |||
| 443 | qt_config->beginGroup(QStringLiteral("Core")); | 445 | qt_config->beginGroup(QStringLiteral("Core")); |
| 444 | 446 | ||
| 445 | ReadGlobalSetting(Settings::values.use_multi_core); | 447 | ReadGlobalSetting(Settings::values.use_multi_core); |
| 448 | ReadGlobalSetting(Settings::values.use_extended_memory_layout); | ||
| 446 | 449 | ||
| 447 | qt_config->endGroup(); | 450 | qt_config->endGroup(); |
| 448 | } | 451 | } |
| @@ -606,6 +609,7 @@ void Config::ReadCpuValues() { | |||
| 606 | ReadGlobalSetting(Settings::values.cpuopt_unsafe_ignore_standard_fpcr); | 609 | ReadGlobalSetting(Settings::values.cpuopt_unsafe_ignore_standard_fpcr); |
| 607 | ReadGlobalSetting(Settings::values.cpuopt_unsafe_inaccurate_nan); | 610 | ReadGlobalSetting(Settings::values.cpuopt_unsafe_inaccurate_nan); |
| 608 | ReadGlobalSetting(Settings::values.cpuopt_unsafe_fastmem_check); | 611 | ReadGlobalSetting(Settings::values.cpuopt_unsafe_fastmem_check); |
| 612 | ReadGlobalSetting(Settings::values.cpuopt_unsafe_ignore_global_monitor); | ||
| 609 | 613 | ||
| 610 | if (global) { | 614 | if (global) { |
| 611 | ReadBasicSetting(Settings::values.cpu_debug_mode); | 615 | ReadBasicSetting(Settings::values.cpu_debug_mode); |
| @@ -618,6 +622,8 @@ void Config::ReadCpuValues() { | |||
| 618 | ReadBasicSetting(Settings::values.cpuopt_misc_ir); | 622 | ReadBasicSetting(Settings::values.cpuopt_misc_ir); |
| 619 | ReadBasicSetting(Settings::values.cpuopt_reduce_misalign_checks); | 623 | ReadBasicSetting(Settings::values.cpuopt_reduce_misalign_checks); |
| 620 | ReadBasicSetting(Settings::values.cpuopt_fastmem); | 624 | ReadBasicSetting(Settings::values.cpuopt_fastmem); |
| 625 | ReadBasicSetting(Settings::values.cpuopt_fastmem_exclusives); | ||
| 626 | ReadBasicSetting(Settings::values.cpuopt_recompile_exclusives); | ||
| 621 | } | 627 | } |
| 622 | 628 | ||
| 623 | qt_config->endGroup(); | 629 | qt_config->endGroup(); |
| @@ -767,6 +773,7 @@ void Config::ReadUIValues() { | |||
| 767 | ReadBasicSetting(UISettings::values.callout_flags); | 773 | ReadBasicSetting(UISettings::values.callout_flags); |
| 768 | ReadBasicSetting(UISettings::values.show_console); | 774 | ReadBasicSetting(UISettings::values.show_console); |
| 769 | ReadBasicSetting(UISettings::values.pause_when_in_background); | 775 | ReadBasicSetting(UISettings::values.pause_when_in_background); |
| 776 | ReadBasicSetting(UISettings::values.mute_when_in_background); | ||
| 770 | ReadBasicSetting(UISettings::values.hide_mouse); | 777 | ReadBasicSetting(UISettings::values.hide_mouse); |
| 771 | 778 | ||
| 772 | qt_config->endGroup(); | 779 | qt_config->endGroup(); |
| @@ -1016,6 +1023,7 @@ void Config::SaveCoreValues() { | |||
| 1016 | qt_config->beginGroup(QStringLiteral("Core")); | 1023 | qt_config->beginGroup(QStringLiteral("Core")); |
| 1017 | 1024 | ||
| 1018 | WriteGlobalSetting(Settings::values.use_multi_core); | 1025 | WriteGlobalSetting(Settings::values.use_multi_core); |
| 1026 | WriteGlobalSetting(Settings::values.use_extended_memory_layout); | ||
| 1019 | 1027 | ||
| 1020 | qt_config->endGroup(); | 1028 | qt_config->endGroup(); |
| 1021 | } | 1029 | } |
| @@ -1134,6 +1142,7 @@ void Config::SaveCpuValues() { | |||
| 1134 | WriteGlobalSetting(Settings::values.cpuopt_unsafe_ignore_standard_fpcr); | 1142 | WriteGlobalSetting(Settings::values.cpuopt_unsafe_ignore_standard_fpcr); |
| 1135 | WriteGlobalSetting(Settings::values.cpuopt_unsafe_inaccurate_nan); | 1143 | WriteGlobalSetting(Settings::values.cpuopt_unsafe_inaccurate_nan); |
| 1136 | WriteGlobalSetting(Settings::values.cpuopt_unsafe_fastmem_check); | 1144 | WriteGlobalSetting(Settings::values.cpuopt_unsafe_fastmem_check); |
| 1145 | WriteGlobalSetting(Settings::values.cpuopt_unsafe_ignore_global_monitor); | ||
| 1137 | 1146 | ||
| 1138 | if (global) { | 1147 | if (global) { |
| 1139 | WriteBasicSetting(Settings::values.cpu_debug_mode); | 1148 | WriteBasicSetting(Settings::values.cpu_debug_mode); |
| @@ -1146,6 +1155,8 @@ void Config::SaveCpuValues() { | |||
| 1146 | WriteBasicSetting(Settings::values.cpuopt_misc_ir); | 1155 | WriteBasicSetting(Settings::values.cpuopt_misc_ir); |
| 1147 | WriteBasicSetting(Settings::values.cpuopt_reduce_misalign_checks); | 1156 | WriteBasicSetting(Settings::values.cpuopt_reduce_misalign_checks); |
| 1148 | WriteBasicSetting(Settings::values.cpuopt_fastmem); | 1157 | WriteBasicSetting(Settings::values.cpuopt_fastmem); |
| 1158 | WriteBasicSetting(Settings::values.cpuopt_fastmem_exclusives); | ||
| 1159 | WriteBasicSetting(Settings::values.cpuopt_recompile_exclusives); | ||
| 1149 | } | 1160 | } |
| 1150 | 1161 | ||
| 1151 | qt_config->endGroup(); | 1162 | qt_config->endGroup(); |
| @@ -1295,6 +1306,7 @@ void Config::SaveUIValues() { | |||
| 1295 | WriteBasicSetting(UISettings::values.callout_flags); | 1306 | WriteBasicSetting(UISettings::values.callout_flags); |
| 1296 | WriteBasicSetting(UISettings::values.show_console); | 1307 | WriteBasicSetting(UISettings::values.show_console); |
| 1297 | WriteBasicSetting(UISettings::values.pause_when_in_background); | 1308 | WriteBasicSetting(UISettings::values.pause_when_in_background); |
| 1309 | WriteBasicSetting(UISettings::values.mute_when_in_background); | ||
| 1298 | WriteBasicSetting(UISettings::values.hide_mouse); | 1310 | WriteBasicSetting(UISettings::values.hide_mouse); |
| 1299 | 1311 | ||
| 1300 | qt_config->endGroup(); | 1312 | qt_config->endGroup(); |
diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h index 60b20a62f..ae3e36a11 100644 --- a/src/yuzu/configuration/config.h +++ b/src/yuzu/configuration/config.h | |||
| @@ -46,7 +46,7 @@ public: | |||
| 46 | default_mouse_buttons; | 46 | default_mouse_buttons; |
| 47 | static const std::array<int, Settings::NativeKeyboard::NumKeyboardKeys> default_keyboard_keys; | 47 | static const std::array<int, Settings::NativeKeyboard::NumKeyboardKeys> default_keyboard_keys; |
| 48 | static const std::array<int, Settings::NativeKeyboard::NumKeyboardMods> default_keyboard_mods; | 48 | static const std::array<int, Settings::NativeKeyboard::NumKeyboardMods> default_keyboard_mods; |
| 49 | static const std::array<UISettings::Shortcut, 20> default_hotkeys; | 49 | static const std::array<UISettings::Shortcut, 22> default_hotkeys; |
| 50 | 50 | ||
| 51 | static constexpr UISettings::Theme default_theme{ | 51 | static constexpr UISettings::Theme default_theme{ |
| 52 | #ifdef _WIN32 | 52 | #ifdef _WIN32 |
diff --git a/src/yuzu/configuration/configure_cpu.cpp b/src/yuzu/configuration/configure_cpu.cpp index f66cab5d4..bf74ccc7c 100644 --- a/src/yuzu/configuration/configure_cpu.cpp +++ b/src/yuzu/configuration/configure_cpu.cpp | |||
| @@ -36,6 +36,7 @@ void ConfigureCpu::SetConfiguration() { | |||
| 36 | ui->cpuopt_unsafe_ignore_standard_fpcr->setEnabled(runtime_lock); | 36 | ui->cpuopt_unsafe_ignore_standard_fpcr->setEnabled(runtime_lock); |
| 37 | ui->cpuopt_unsafe_inaccurate_nan->setEnabled(runtime_lock); | 37 | ui->cpuopt_unsafe_inaccurate_nan->setEnabled(runtime_lock); |
| 38 | ui->cpuopt_unsafe_fastmem_check->setEnabled(runtime_lock); | 38 | ui->cpuopt_unsafe_fastmem_check->setEnabled(runtime_lock); |
| 39 | ui->cpuopt_unsafe_ignore_global_monitor->setEnabled(runtime_lock); | ||
| 39 | 40 | ||
| 40 | ui->cpuopt_unsafe_unfuse_fma->setChecked(Settings::values.cpuopt_unsafe_unfuse_fma.GetValue()); | 41 | ui->cpuopt_unsafe_unfuse_fma->setChecked(Settings::values.cpuopt_unsafe_unfuse_fma.GetValue()); |
| 41 | ui->cpuopt_unsafe_reduce_fp_error->setChecked( | 42 | ui->cpuopt_unsafe_reduce_fp_error->setChecked( |
| @@ -46,6 +47,8 @@ void ConfigureCpu::SetConfiguration() { | |||
| 46 | Settings::values.cpuopt_unsafe_inaccurate_nan.GetValue()); | 47 | Settings::values.cpuopt_unsafe_inaccurate_nan.GetValue()); |
| 47 | ui->cpuopt_unsafe_fastmem_check->setChecked( | 48 | ui->cpuopt_unsafe_fastmem_check->setChecked( |
| 48 | Settings::values.cpuopt_unsafe_fastmem_check.GetValue()); | 49 | Settings::values.cpuopt_unsafe_fastmem_check.GetValue()); |
| 50 | ui->cpuopt_unsafe_ignore_global_monitor->setChecked( | ||
| 51 | Settings::values.cpuopt_unsafe_ignore_global_monitor.GetValue()); | ||
| 49 | 52 | ||
| 50 | if (Settings::IsConfiguringGlobal()) { | 53 | if (Settings::IsConfiguringGlobal()) { |
| 51 | ui->accuracy->setCurrentIndex(static_cast<int>(Settings::values.cpu_accuracy.GetValue())); | 54 | ui->accuracy->setCurrentIndex(static_cast<int>(Settings::values.cpu_accuracy.GetValue())); |
| @@ -82,6 +85,9 @@ void ConfigureCpu::ApplyConfiguration() { | |||
| 82 | ConfigurationShared::ApplyPerGameSetting(&Settings::values.cpuopt_unsafe_fastmem_check, | 85 | ConfigurationShared::ApplyPerGameSetting(&Settings::values.cpuopt_unsafe_fastmem_check, |
| 83 | ui->cpuopt_unsafe_fastmem_check, | 86 | ui->cpuopt_unsafe_fastmem_check, |
| 84 | cpuopt_unsafe_fastmem_check); | 87 | cpuopt_unsafe_fastmem_check); |
| 88 | ConfigurationShared::ApplyPerGameSetting(&Settings::values.cpuopt_unsafe_ignore_global_monitor, | ||
| 89 | ui->cpuopt_unsafe_ignore_global_monitor, | ||
| 90 | cpuopt_unsafe_ignore_global_monitor); | ||
| 85 | } | 91 | } |
| 86 | 92 | ||
| 87 | void ConfigureCpu::changeEvent(QEvent* event) { | 93 | void ConfigureCpu::changeEvent(QEvent* event) { |
| @@ -120,4 +126,7 @@ void ConfigureCpu::SetupPerGameUI() { | |||
| 120 | ConfigurationShared::SetColoredTristate(ui->cpuopt_unsafe_fastmem_check, | 126 | ConfigurationShared::SetColoredTristate(ui->cpuopt_unsafe_fastmem_check, |
| 121 | Settings::values.cpuopt_unsafe_fastmem_check, | 127 | Settings::values.cpuopt_unsafe_fastmem_check, |
| 122 | cpuopt_unsafe_fastmem_check); | 128 | cpuopt_unsafe_fastmem_check); |
| 129 | ConfigurationShared::SetColoredTristate(ui->cpuopt_unsafe_ignore_global_monitor, | ||
| 130 | Settings::values.cpuopt_unsafe_ignore_global_monitor, | ||
| 131 | cpuopt_unsafe_ignore_global_monitor); | ||
| 123 | } | 132 | } |
diff --git a/src/yuzu/configuration/configure_cpu.h b/src/yuzu/configuration/configure_cpu.h index ed9af0e9f..733e38be4 100644 --- a/src/yuzu/configuration/configure_cpu.h +++ b/src/yuzu/configuration/configure_cpu.h | |||
| @@ -45,6 +45,7 @@ private: | |||
| 45 | ConfigurationShared::CheckState cpuopt_unsafe_ignore_standard_fpcr; | 45 | ConfigurationShared::CheckState cpuopt_unsafe_ignore_standard_fpcr; |
| 46 | ConfigurationShared::CheckState cpuopt_unsafe_inaccurate_nan; | 46 | ConfigurationShared::CheckState cpuopt_unsafe_inaccurate_nan; |
| 47 | ConfigurationShared::CheckState cpuopt_unsafe_fastmem_check; | 47 | ConfigurationShared::CheckState cpuopt_unsafe_fastmem_check; |
| 48 | ConfigurationShared::CheckState cpuopt_unsafe_ignore_global_monitor; | ||
| 48 | 49 | ||
| 49 | const Core::System& system; | 50 | const Core::System& system; |
| 50 | }; | 51 | }; |
diff --git a/src/yuzu/configuration/configure_cpu.ui b/src/yuzu/configuration/configure_cpu.ui index d8064db24..5d80a8c91 100644 --- a/src/yuzu/configuration/configure_cpu.ui +++ b/src/yuzu/configuration/configure_cpu.ui | |||
| @@ -150,6 +150,18 @@ | |||
| 150 | </property> | 150 | </property> |
| 151 | </widget> | 151 | </widget> |
| 152 | </item> | 152 | </item> |
| 153 | <item> | ||
| 154 | <widget class="QCheckBox" name="cpuopt_unsafe_ignore_global_monitor"> | ||
| 155 | <property name="toolTip"> | ||
| 156 | <string> | ||
| 157 | <div>This option improves speed by relying only on the semantics of cmpxchg to ensure safety of exclusive access instructions. Please note this may result in deadlocks and other race conditions.</div> | ||
| 158 | </string> | ||
| 159 | </property> | ||
| 160 | <property name="text"> | ||
| 161 | <string>Ignore global monitor</string> | ||
| 162 | </property> | ||
| 163 | </widget> | ||
| 164 | </item> | ||
| 153 | </layout> | 165 | </layout> |
| 154 | </widget> | 166 | </widget> |
| 155 | </item> | 167 | </item> |
diff --git a/src/yuzu/configuration/configure_cpu_debug.cpp b/src/yuzu/configuration/configure_cpu_debug.cpp index 05a90963d..616a0be75 100644 --- a/src/yuzu/configuration/configure_cpu_debug.cpp +++ b/src/yuzu/configuration/configure_cpu_debug.cpp | |||
| @@ -44,6 +44,12 @@ void ConfigureCpuDebug::SetConfiguration() { | |||
| 44 | Settings::values.cpuopt_reduce_misalign_checks.GetValue()); | 44 | Settings::values.cpuopt_reduce_misalign_checks.GetValue()); |
| 45 | ui->cpuopt_fastmem->setEnabled(runtime_lock); | 45 | ui->cpuopt_fastmem->setEnabled(runtime_lock); |
| 46 | ui->cpuopt_fastmem->setChecked(Settings::values.cpuopt_fastmem.GetValue()); | 46 | ui->cpuopt_fastmem->setChecked(Settings::values.cpuopt_fastmem.GetValue()); |
| 47 | ui->cpuopt_fastmem_exclusives->setEnabled(runtime_lock); | ||
| 48 | ui->cpuopt_fastmem_exclusives->setChecked( | ||
| 49 | Settings::values.cpuopt_fastmem_exclusives.GetValue()); | ||
| 50 | ui->cpuopt_recompile_exclusives->setEnabled(runtime_lock); | ||
| 51 | ui->cpuopt_recompile_exclusives->setChecked( | ||
| 52 | Settings::values.cpuopt_recompile_exclusives.GetValue()); | ||
| 47 | } | 53 | } |
| 48 | 54 | ||
| 49 | void ConfigureCpuDebug::ApplyConfiguration() { | 55 | void ConfigureCpuDebug::ApplyConfiguration() { |
| @@ -56,6 +62,8 @@ void ConfigureCpuDebug::ApplyConfiguration() { | |||
| 56 | Settings::values.cpuopt_misc_ir = ui->cpuopt_misc_ir->isChecked(); | 62 | Settings::values.cpuopt_misc_ir = ui->cpuopt_misc_ir->isChecked(); |
| 57 | Settings::values.cpuopt_reduce_misalign_checks = ui->cpuopt_reduce_misalign_checks->isChecked(); | 63 | Settings::values.cpuopt_reduce_misalign_checks = ui->cpuopt_reduce_misalign_checks->isChecked(); |
| 58 | Settings::values.cpuopt_fastmem = ui->cpuopt_fastmem->isChecked(); | 64 | Settings::values.cpuopt_fastmem = ui->cpuopt_fastmem->isChecked(); |
| 65 | Settings::values.cpuopt_fastmem_exclusives = ui->cpuopt_fastmem_exclusives->isChecked(); | ||
| 66 | Settings::values.cpuopt_recompile_exclusives = ui->cpuopt_recompile_exclusives->isChecked(); | ||
| 59 | } | 67 | } |
| 60 | 68 | ||
| 61 | void ConfigureCpuDebug::changeEvent(QEvent* event) { | 69 | void ConfigureCpuDebug::changeEvent(QEvent* event) { |
diff --git a/src/yuzu/configuration/configure_cpu_debug.ui b/src/yuzu/configuration/configure_cpu_debug.ui index 6e635bb2f..2bc268810 100644 --- a/src/yuzu/configuration/configure_cpu_debug.ui +++ b/src/yuzu/configuration/configure_cpu_debug.ui | |||
| @@ -144,7 +144,34 @@ | |||
| 144 | </string> | 144 | </string> |
| 145 | </property> | 145 | </property> |
| 146 | <property name="text"> | 146 | <property name="text"> |
| 147 | <string>Enable Host MMU Emulation</string> | 147 | <string>Enable Host MMU Emulation (general memory instructions)</string> |
| 148 | </property> | ||
| 149 | </widget> | ||
| 150 | </item> | ||
| 151 | <item> | ||
| 152 | <widget class="QCheckBox" name="cpuopt_fastmem_exclusives"> | ||
| 153 | <property name="toolTip"> | ||
| 154 | <string> | ||
| 155 | <div style="white-space: nowrap">This optimization speeds up exclusive memory accesses by the guest program.</div> | ||
| 156 | <div style="white-space: nowrap">Enabling it causes guest exclusive memory reads/writes to be done directly into memory and make use of Host's MMU.</div> | ||
| 157 | <div style="white-space: nowrap">Disabling this forces all exclusive memory accesses to use Software MMU Emulation.</div> | ||
| 158 | </string> | ||
| 159 | </property> | ||
| 160 | <property name="text"> | ||
| 161 | <string>Enable Host MMU Emulation (exclusive memory instructions)</string> | ||
| 162 | </property> | ||
| 163 | </widget> | ||
| 164 | </item> | ||
| 165 | <item> | ||
| 166 | <widget class="QCheckBox" name="cpuopt_recompile_exclusives"> | ||
| 167 | <property name="toolTip"> | ||
| 168 | <string> | ||
| 169 | <div style="white-space: nowrap">This optimization speeds up exclusive memory accesses by the guest program.</div> | ||
| 170 | <div style="white-space: nowrap">Enabling it reduces the overhead of fastmem failure of exclusive memory accesses.</div> | ||
| 171 | </string> | ||
| 172 | </property> | ||
| 173 | <property name="text"> | ||
| 174 | <string>Enable recompilation of exclusive memory instructions</string> | ||
| 148 | </property> | 175 | </property> |
| 149 | </widget> | 176 | </widget> |
| 150 | </item> | 177 | </item> |
diff --git a/src/yuzu/configuration/configure_general.cpp b/src/yuzu/configuration/configure_general.cpp index 566879317..08d5444ec 100644 --- a/src/yuzu/configuration/configure_general.cpp +++ b/src/yuzu/configuration/configure_general.cpp | |||
| @@ -42,10 +42,14 @@ void ConfigureGeneral::SetConfiguration() { | |||
| 42 | 42 | ||
| 43 | ui->use_multi_core->setEnabled(runtime_lock); | 43 | ui->use_multi_core->setEnabled(runtime_lock); |
| 44 | ui->use_multi_core->setChecked(Settings::values.use_multi_core.GetValue()); | 44 | ui->use_multi_core->setChecked(Settings::values.use_multi_core.GetValue()); |
| 45 | ui->use_extended_memory_layout->setEnabled(runtime_lock); | ||
| 46 | ui->use_extended_memory_layout->setChecked( | ||
| 47 | Settings::values.use_extended_memory_layout.GetValue()); | ||
| 45 | 48 | ||
| 46 | ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing.GetValue()); | 49 | ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing.GetValue()); |
| 47 | ui->toggle_user_on_boot->setChecked(UISettings::values.select_user_on_boot.GetValue()); | 50 | ui->toggle_user_on_boot->setChecked(UISettings::values.select_user_on_boot.GetValue()); |
| 48 | ui->toggle_background_pause->setChecked(UISettings::values.pause_when_in_background.GetValue()); | 51 | ui->toggle_background_pause->setChecked(UISettings::values.pause_when_in_background.GetValue()); |
| 52 | ui->toggle_background_mute->setChecked(UISettings::values.mute_when_in_background.GetValue()); | ||
| 49 | ui->toggle_hide_mouse->setChecked(UISettings::values.hide_mouse.GetValue()); | 53 | ui->toggle_hide_mouse->setChecked(UISettings::values.hide_mouse.GetValue()); |
| 50 | 54 | ||
| 51 | ui->toggle_speed_limit->setChecked(Settings::values.use_speed_limit.GetValue()); | 55 | ui->toggle_speed_limit->setChecked(Settings::values.use_speed_limit.GetValue()); |
| @@ -90,11 +94,15 @@ void ConfigureGeneral::ResetDefaults() { | |||
| 90 | void ConfigureGeneral::ApplyConfiguration() { | 94 | void ConfigureGeneral::ApplyConfiguration() { |
| 91 | ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_multi_core, ui->use_multi_core, | 95 | ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_multi_core, ui->use_multi_core, |
| 92 | use_multi_core); | 96 | use_multi_core); |
| 97 | ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_extended_memory_layout, | ||
| 98 | ui->use_extended_memory_layout, | ||
| 99 | use_extended_memory_layout); | ||
| 93 | 100 | ||
| 94 | if (Settings::IsConfiguringGlobal()) { | 101 | if (Settings::IsConfiguringGlobal()) { |
| 95 | UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked(); | 102 | UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked(); |
| 96 | UISettings::values.select_user_on_boot = ui->toggle_user_on_boot->isChecked(); | 103 | UISettings::values.select_user_on_boot = ui->toggle_user_on_boot->isChecked(); |
| 97 | UISettings::values.pause_when_in_background = ui->toggle_background_pause->isChecked(); | 104 | UISettings::values.pause_when_in_background = ui->toggle_background_pause->isChecked(); |
| 105 | UISettings::values.mute_when_in_background = ui->toggle_background_mute->isChecked(); | ||
| 98 | UISettings::values.hide_mouse = ui->toggle_hide_mouse->isChecked(); | 106 | UISettings::values.hide_mouse = ui->toggle_hide_mouse->isChecked(); |
| 99 | 107 | ||
| 100 | Settings::values.fps_cap.SetValue(ui->fps_cap->value()); | 108 | Settings::values.fps_cap.SetValue(ui->fps_cap->value()); |
| @@ -158,6 +166,9 @@ void ConfigureGeneral::SetupPerGameUI() { | |||
| 158 | Settings::values.use_speed_limit, use_speed_limit); | 166 | Settings::values.use_speed_limit, use_speed_limit); |
| 159 | ConfigurationShared::SetColoredTristate(ui->use_multi_core, Settings::values.use_multi_core, | 167 | ConfigurationShared::SetColoredTristate(ui->use_multi_core, Settings::values.use_multi_core, |
| 160 | use_multi_core); | 168 | use_multi_core); |
| 169 | ConfigurationShared::SetColoredTristate(ui->use_extended_memory_layout, | ||
| 170 | Settings::values.use_extended_memory_layout, | ||
| 171 | use_extended_memory_layout); | ||
| 161 | 172 | ||
| 162 | connect(ui->toggle_speed_limit, &QCheckBox::clicked, ui->speed_limit, [this]() { | 173 | connect(ui->toggle_speed_limit, &QCheckBox::clicked, ui->speed_limit, [this]() { |
| 163 | ui->speed_limit->setEnabled(ui->toggle_speed_limit->isChecked() && | 174 | ui->speed_limit->setEnabled(ui->toggle_speed_limit->isChecked() && |
diff --git a/src/yuzu/configuration/configure_general.h b/src/yuzu/configuration/configure_general.h index 85c1dd4a8..b6f3bb5ed 100644 --- a/src/yuzu/configuration/configure_general.h +++ b/src/yuzu/configuration/configure_general.h | |||
| @@ -48,6 +48,7 @@ private: | |||
| 48 | 48 | ||
| 49 | ConfigurationShared::CheckState use_speed_limit; | 49 | ConfigurationShared::CheckState use_speed_limit; |
| 50 | ConfigurationShared::CheckState use_multi_core; | 50 | ConfigurationShared::CheckState use_multi_core; |
| 51 | ConfigurationShared::CheckState use_extended_memory_layout; | ||
| 51 | 52 | ||
| 52 | const Core::System& system; | 53 | const Core::System& system; |
| 53 | }; | 54 | }; |
diff --git a/src/yuzu/configuration/configure_general.ui b/src/yuzu/configuration/configure_general.ui index 112dc72b3..c6ef2ab70 100644 --- a/src/yuzu/configuration/configure_general.ui +++ b/src/yuzu/configuration/configure_general.ui | |||
| @@ -143,6 +143,13 @@ | |||
| 143 | </widget> | 143 | </widget> |
| 144 | </item> | 144 | </item> |
| 145 | <item> | 145 | <item> |
| 146 | <widget class="QCheckBox" name="use_extended_memory_layout"> | ||
| 147 | <property name="text"> | ||
| 148 | <string>Extended memory layout (6GB DRAM)</string> | ||
| 149 | </property> | ||
| 150 | </widget> | ||
| 151 | </item> | ||
| 152 | <item> | ||
| 146 | <widget class="QCheckBox" name="toggle_check_exit"> | 153 | <widget class="QCheckBox" name="toggle_check_exit"> |
| 147 | <property name="text"> | 154 | <property name="text"> |
| 148 | <string>Confirm exit while emulation is running</string> | 155 | <string>Confirm exit while emulation is running</string> |
| @@ -164,6 +171,13 @@ | |||
| 164 | </widget> | 171 | </widget> |
| 165 | </item> | 172 | </item> |
| 166 | <item> | 173 | <item> |
| 174 | <widget class="QCheckBox" name="toggle_background_mute"> | ||
| 175 | <property name="text"> | ||
| 176 | <string>Mute audio when in background</string> | ||
| 177 | </property> | ||
| 178 | </widget> | ||
| 179 | </item> | ||
| 180 | <item> | ||
| 167 | <widget class="QCheckBox" name="toggle_hide_mouse"> | 181 | <widget class="QCheckBox" name="toggle_hide_mouse"> |
| 168 | <property name="text"> | 182 | <property name="text"> |
| 169 | <string>Hide mouse on inactivity</string> | 183 | <string>Hide mouse on inactivity</string> |
diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp index d53179dbb..7c5776189 100644 --- a/src/yuzu/configuration/configure_input.cpp +++ b/src/yuzu/configuration/configure_input.cpp | |||
| @@ -164,7 +164,7 @@ void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem, | |||
| 164 | }); | 164 | }); |
| 165 | 165 | ||
| 166 | connect(ui->vibrationButton, &QPushButton::clicked, | 166 | connect(ui->vibrationButton, &QPushButton::clicked, |
| 167 | [this] { CallConfigureDialog<ConfigureVibration>(*this); }); | 167 | [this, &hid_core] { CallConfigureDialog<ConfigureVibration>(*this, hid_core); }); |
| 168 | 168 | ||
| 169 | connect(ui->motionButton, &QPushButton::clicked, [this, input_subsystem] { | 169 | connect(ui->motionButton, &QPushButton::clicked, [this, input_subsystem] { |
| 170 | CallConfigureDialog<ConfigureMotionTouch>(*this, input_subsystem); | 170 | CallConfigureDialog<ConfigureMotionTouch>(*this, input_subsystem); |
diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp index cc0534907..0aa4ac3e4 100644 --- a/src/yuzu/configuration/configure_input_player.cpp +++ b/src/yuzu/configuration/configure_input_player.cpp | |||
| @@ -488,6 +488,32 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i | |||
| 488 | emulated_controller->SetStickParam(analog_id, {}); | 488 | emulated_controller->SetStickParam(analog_id, {}); |
| 489 | analog_map_buttons[analog_id][sub_button_id]->setText(tr("[not set]")); | 489 | analog_map_buttons[analog_id][sub_button_id]->setText(tr("[not set]")); |
| 490 | }); | 490 | }); |
| 491 | context_menu.addAction(tr("Center axis"), [&] { | ||
| 492 | const auto stick_value = | ||
| 493 | emulated_controller->GetSticksValues()[analog_id]; | ||
| 494 | const float offset_x = stick_value.x.properties.offset; | ||
| 495 | const float offset_y = stick_value.y.properties.offset; | ||
| 496 | float raw_value_x = stick_value.x.raw_value; | ||
| 497 | float raw_value_y = stick_value.y.raw_value; | ||
| 498 | // See Core::HID::SanitizeStick() to obtain the original raw axis value | ||
| 499 | if (std::abs(offset_x) < 0.5f) { | ||
| 500 | if (raw_value_x > 0) { | ||
| 501 | raw_value_x *= 1 + offset_x; | ||
| 502 | } else { | ||
| 503 | raw_value_x *= 1 - offset_x; | ||
| 504 | } | ||
| 505 | } | ||
| 506 | if (std::abs(offset_x) < 0.5f) { | ||
| 507 | if (raw_value_y > 0) { | ||
| 508 | raw_value_y *= 1 + offset_y; | ||
| 509 | } else { | ||
| 510 | raw_value_y *= 1 - offset_y; | ||
| 511 | } | ||
| 512 | } | ||
| 513 | param.Set("offset_x", -raw_value_x + offset_x); | ||
| 514 | param.Set("offset_y", -raw_value_y + offset_y); | ||
| 515 | emulated_controller->SetStickParam(analog_id, param); | ||
| 516 | }); | ||
| 491 | context_menu.addAction(tr("Invert axis"), [&] { | 517 | context_menu.addAction(tr("Invert axis"), [&] { |
| 492 | if (sub_button_id == 2 || sub_button_id == 3) { | 518 | if (sub_button_id == 2 || sub_button_id == 3) { |
| 493 | const bool invert_value = param.Get("invert_x", "+") == "-"; | 519 | const bool invert_value = param.Get("invert_x", "+") == "-"; |
| @@ -1306,6 +1332,9 @@ void ConfigureInputPlayer::HandleClick( | |||
| 1306 | QPushButton* button, std::size_t button_id, | 1332 | QPushButton* button, std::size_t button_id, |
| 1307 | std::function<void(const Common::ParamPackage&)> new_input_setter, | 1333 | std::function<void(const Common::ParamPackage&)> new_input_setter, |
| 1308 | InputCommon::Polling::InputType type) { | 1334 | InputCommon::Polling::InputType type) { |
| 1335 | if (timeout_timer->isActive()) { | ||
| 1336 | return; | ||
| 1337 | } | ||
| 1309 | if (button == ui->buttonMotionLeft || button == ui->buttonMotionRight) { | 1338 | if (button == ui->buttonMotionLeft || button == ui->buttonMotionRight) { |
| 1310 | button->setText(tr("Shake!")); | 1339 | button->setText(tr("Shake!")); |
| 1311 | } else { | 1340 | } else { |
diff --git a/src/yuzu/configuration/configure_input_player_widget.cpp b/src/yuzu/configuration/configure_input_player_widget.cpp index 6630321cb..fb168b2ca 100644 --- a/src/yuzu/configuration/configure_input_player_widget.cpp +++ b/src/yuzu/configuration/configure_input_player_widget.cpp | |||
| @@ -70,7 +70,6 @@ void PlayerControlPreview::UpdateColors() { | |||
| 70 | colors.slider_arrow = QColor(14, 15, 18); | 70 | colors.slider_arrow = QColor(14, 15, 18); |
| 71 | colors.font2 = QColor(255, 255, 255); | 71 | colors.font2 = QColor(255, 255, 255); |
| 72 | colors.indicator = QColor(170, 238, 255); | 72 | colors.indicator = QColor(170, 238, 255); |
| 73 | colors.indicator2 = QColor(100, 255, 100); | ||
| 74 | colors.deadzone = QColor(204, 136, 136); | 73 | colors.deadzone = QColor(204, 136, 136); |
| 75 | colors.slider_button = colors.button; | 74 | colors.slider_button = colors.button; |
| 76 | } | 75 | } |
| @@ -88,7 +87,6 @@ void PlayerControlPreview::UpdateColors() { | |||
| 88 | colors.slider_arrow = QColor(65, 68, 73); | 87 | colors.slider_arrow = QColor(65, 68, 73); |
| 89 | colors.font2 = QColor(0, 0, 0); | 88 | colors.font2 = QColor(0, 0, 0); |
| 90 | colors.indicator = QColor(0, 0, 200); | 89 | colors.indicator = QColor(0, 0, 200); |
| 91 | colors.indicator2 = QColor(0, 150, 0); | ||
| 92 | colors.deadzone = QColor(170, 0, 0); | 90 | colors.deadzone = QColor(170, 0, 0); |
| 93 | colors.slider_button = QColor(153, 149, 149); | 91 | colors.slider_button = QColor(153, 149, 149); |
| 94 | } | 92 | } |
| @@ -101,6 +99,8 @@ void PlayerControlPreview::UpdateColors() { | |||
| 101 | colors.font = QColor(255, 255, 255); | 99 | colors.font = QColor(255, 255, 255); |
| 102 | colors.led_on = QColor(255, 255, 0); | 100 | colors.led_on = QColor(255, 255, 0); |
| 103 | colors.led_off = QColor(170, 238, 255); | 101 | colors.led_off = QColor(170, 238, 255); |
| 102 | colors.indicator2 = QColor(59, 165, 93); | ||
| 103 | colors.charging = QColor(250, 168, 26); | ||
| 104 | 104 | ||
| 105 | colors.left = colors.primary; | 105 | colors.left = colors.primary; |
| 106 | colors.right = colors.primary; | 106 | colors.right = colors.primary; |
| @@ -357,7 +357,7 @@ void PlayerControlPreview::DrawLeftController(QPainter& p, const QPointF center) | |||
| 357 | DrawCircle(p, center + QPoint(26, 71), 5); | 357 | DrawCircle(p, center + QPoint(26, 71), 5); |
| 358 | 358 | ||
| 359 | // Draw battery | 359 | // Draw battery |
| 360 | DrawBattery(p, center + QPoint(-170, -140), | 360 | DrawBattery(p, center + QPoint(-160, -140), |
| 361 | battery_values[Core::HID::EmulatedDeviceIndex::LeftIndex]); | 361 | battery_values[Core::HID::EmulatedDeviceIndex::LeftIndex]); |
| 362 | } | 362 | } |
| 363 | 363 | ||
| @@ -484,7 +484,7 @@ void PlayerControlPreview::DrawRightController(QPainter& p, const QPointF center | |||
| 484 | DrawSymbol(p, center + QPoint(-26, 66), Symbol::House, 5); | 484 | DrawSymbol(p, center + QPoint(-26, 66), Symbol::House, 5); |
| 485 | 485 | ||
| 486 | // Draw battery | 486 | // Draw battery |
| 487 | DrawBattery(p, center + QPoint(110, -140), | 487 | DrawBattery(p, center + QPoint(120, -140), |
| 488 | battery_values[Core::HID::EmulatedDeviceIndex::RightIndex]); | 488 | battery_values[Core::HID::EmulatedDeviceIndex::RightIndex]); |
| 489 | } | 489 | } |
| 490 | 490 | ||
| @@ -621,9 +621,9 @@ void PlayerControlPreview::DrawDualController(QPainter& p, const QPointF center) | |||
| 621 | DrawSymbol(p, center + QPoint(50, 60), Symbol::House, 4.2f); | 621 | DrawSymbol(p, center + QPoint(50, 60), Symbol::House, 4.2f); |
| 622 | 622 | ||
| 623 | // Draw battery | 623 | // Draw battery |
| 624 | DrawBattery(p, center + QPoint(-100, -160), | 624 | DrawBattery(p, center + QPoint(-200, -10), |
| 625 | battery_values[Core::HID::EmulatedDeviceIndex::LeftIndex]); | 625 | battery_values[Core::HID::EmulatedDeviceIndex::LeftIndex]); |
| 626 | DrawBattery(p, center + QPoint(40, -160), | 626 | DrawBattery(p, center + QPoint(160, -10), |
| 627 | battery_values[Core::HID::EmulatedDeviceIndex::RightIndex]); | 627 | battery_values[Core::HID::EmulatedDeviceIndex::RightIndex]); |
| 628 | } | 628 | } |
| 629 | 629 | ||
| @@ -694,12 +694,12 @@ void PlayerControlPreview::DrawHandheldController(QPainter& p, const QPointF cen | |||
| 694 | 694 | ||
| 695 | // ZL and ZR buttons | 695 | // ZL and ZR buttons |
| 696 | p.setPen(colors.outline); | 696 | p.setPen(colors.outline); |
| 697 | DrawTriggerButton(p, center + QPoint(-210, -130), Direction::Left, button_values[ZL]); | 697 | DrawTriggerButton(p, center + QPoint(-210, -120), Direction::Left, button_values[ZL]); |
| 698 | DrawTriggerButton(p, center + QPoint(210, -130), Direction::Right, button_values[ZR]); | 698 | DrawTriggerButton(p, center + QPoint(210, -120), Direction::Right, button_values[ZR]); |
| 699 | p.setPen(colors.transparent); | 699 | p.setPen(colors.transparent); |
| 700 | p.setBrush(colors.font); | 700 | p.setBrush(colors.font); |
| 701 | DrawSymbol(p, center + QPoint(-210, -130), Symbol::ZL, 1.5f); | 701 | DrawSymbol(p, center + QPoint(-210, -120), Symbol::ZL, 1.5f); |
| 702 | DrawSymbol(p, center + QPoint(210, -130), Symbol::ZR, 1.5f); | 702 | DrawSymbol(p, center + QPoint(210, -120), Symbol::ZR, 1.5f); |
| 703 | 703 | ||
| 704 | // Minus and Plus button | 704 | // Minus and Plus button |
| 705 | p.setPen(colors.outline); | 705 | p.setPen(colors.outline); |
| @@ -725,9 +725,9 @@ void PlayerControlPreview::DrawHandheldController(QPainter& p, const QPointF cen | |||
| 725 | DrawSymbol(p, center + QPoint(161, 37), Symbol::House, 2.75f); | 725 | DrawSymbol(p, center + QPoint(161, 37), Symbol::House, 2.75f); |
| 726 | 726 | ||
| 727 | // Draw battery | 727 | // Draw battery |
| 728 | DrawBattery(p, center + QPoint(-200, 110), | 728 | DrawBattery(p, center + QPoint(-188, 95), |
| 729 | battery_values[Core::HID::EmulatedDeviceIndex::LeftIndex]); | 729 | battery_values[Core::HID::EmulatedDeviceIndex::LeftIndex]); |
| 730 | DrawBattery(p, center + QPoint(130, 110), | 730 | DrawBattery(p, center + QPoint(150, 95), |
| 731 | battery_values[Core::HID::EmulatedDeviceIndex::RightIndex]); | 731 | battery_values[Core::HID::EmulatedDeviceIndex::RightIndex]); |
| 732 | } | 732 | } |
| 733 | 733 | ||
| @@ -781,12 +781,12 @@ void PlayerControlPreview::DrawProController(QPainter& p, const QPointF center) | |||
| 781 | 781 | ||
| 782 | // ZL and ZR buttons | 782 | // ZL and ZR buttons |
| 783 | p.setPen(colors.outline); | 783 | p.setPen(colors.outline); |
| 784 | DrawTriggerButton(p, center + QPoint(-210, -130), Direction::Left, button_values[ZL]); | 784 | DrawTriggerButton(p, center + QPoint(-210, -120), Direction::Left, button_values[ZL]); |
| 785 | DrawTriggerButton(p, center + QPoint(210, -130), Direction::Right, button_values[ZR]); | 785 | DrawTriggerButton(p, center + QPoint(210, -120), Direction::Right, button_values[ZR]); |
| 786 | p.setPen(colors.transparent); | 786 | p.setPen(colors.transparent); |
| 787 | p.setBrush(colors.font); | 787 | p.setBrush(colors.font); |
| 788 | DrawSymbol(p, center + QPoint(-210, -130), Symbol::ZL, 1.5f); | 788 | DrawSymbol(p, center + QPoint(-210, -120), Symbol::ZL, 1.5f); |
| 789 | DrawSymbol(p, center + QPoint(210, -130), Symbol::ZR, 1.5f); | 789 | DrawSymbol(p, center + QPoint(210, -120), Symbol::ZR, 1.5f); |
| 790 | 790 | ||
| 791 | // Minus and Plus buttons | 791 | // Minus and Plus buttons |
| 792 | p.setPen(colors.outline); | 792 | p.setPen(colors.outline); |
| @@ -818,7 +818,7 @@ void PlayerControlPreview::DrawProController(QPainter& p, const QPointF center) | |||
| 818 | DrawSymbol(p, center + QPoint(29, -56), Symbol::House, 3.9f); | 818 | DrawSymbol(p, center + QPoint(29, -56), Symbol::House, 3.9f); |
| 819 | 819 | ||
| 820 | // Draw battery | 820 | // Draw battery |
| 821 | DrawBattery(p, center + QPoint(-30, -160), | 821 | DrawBattery(p, center + QPoint(-20, -160), |
| 822 | battery_values[Core::HID::EmulatedDeviceIndex::LeftIndex]); | 822 | battery_values[Core::HID::EmulatedDeviceIndex::LeftIndex]); |
| 823 | } | 823 | } |
| 824 | 824 | ||
| @@ -875,7 +875,7 @@ void PlayerControlPreview::DrawGCController(QPainter& p, const QPointF center) { | |||
| 875 | DrawCircleButton(p, center + QPoint(0, -44), button_values[Plus], 8); | 875 | DrawCircleButton(p, center + QPoint(0, -44), button_values[Plus], 8); |
| 876 | 876 | ||
| 877 | // Draw battery | 877 | // Draw battery |
| 878 | DrawBattery(p, center + QPoint(-30, -165), | 878 | DrawBattery(p, center + QPoint(-20, 110), |
| 879 | battery_values[Core::HID::EmulatedDeviceIndex::LeftIndex]); | 879 | battery_values[Core::HID::EmulatedDeviceIndex::LeftIndex]); |
| 880 | } | 880 | } |
| 881 | 881 | ||
| @@ -1030,6 +1030,10 @@ constexpr std::array<float, 30 * 2> symbol_c = { | |||
| 1030 | -2.37f, 5.64f, -0.65f, 6.44f, 1.25f, 6.47f, 3.06f, 5.89f, 4.63f, 4.92f, 4.63f, 6.83f, | 1030 | -2.37f, 5.64f, -0.65f, 6.44f, 1.25f, 6.47f, 3.06f, 5.89f, 4.63f, 4.92f, 4.63f, 6.83f, |
| 1031 | }; | 1031 | }; |
| 1032 | 1032 | ||
| 1033 | constexpr std::array<float, 6 * 2> symbol_charging = { | ||
| 1034 | 6.5f, -1.0f, 1.0f, -1.0f, 1.0f, -3.0f, -6.5f, 1.0f, -1.0f, 1.0f, -1.0f, 3.0f, | ||
| 1035 | }; | ||
| 1036 | |||
| 1033 | constexpr std::array<float, 12 * 2> house = { | 1037 | constexpr std::array<float, 12 * 2> house = { |
| 1034 | -1.3f, 0.0f, -0.93f, 0.0f, -0.93f, 1.15f, 0.93f, 1.15f, 0.93f, 0.0f, 1.3f, 0.0f, | 1038 | -1.3f, 0.0f, -0.93f, 0.0f, -0.93f, 1.15f, 0.93f, 1.15f, 0.93f, 0.0f, 1.3f, 0.0f, |
| 1035 | 0.0f, -1.2f, -1.3f, 0.0f, -0.43f, 0.0f, -0.43f, .73f, 0.43f, .73f, 0.43f, 0.0f, | 1039 | 0.0f, -1.2f, -1.3f, 0.0f, -0.43f, 0.0f, -0.43f, .73f, 0.43f, .73f, 0.43f, 0.0f, |
| @@ -2674,36 +2678,43 @@ void PlayerControlPreview::DrawBattery(QPainter& p, QPointF center, | |||
| 2674 | if (battery == Common::Input::BatteryLevel::None) { | 2678 | if (battery == Common::Input::BatteryLevel::None) { |
| 2675 | return; | 2679 | return; |
| 2676 | } | 2680 | } |
| 2677 | p.setPen(colors.outline); | 2681 | // Draw outline |
| 2682 | p.setPen(QPen(colors.button, 5)); | ||
| 2683 | p.setBrush(colors.transparent); | ||
| 2684 | p.drawRoundedRect(center.x(), center.y(), 34, 16, 2, 2); | ||
| 2685 | |||
| 2686 | p.setPen(QPen(colors.button, 3)); | ||
| 2687 | p.drawRect(center.x() + 35, center.y() + 4.5f, 4, 7); | ||
| 2688 | |||
| 2689 | // Draw Battery shape | ||
| 2690 | p.setPen(QPen(colors.indicator2, 3)); | ||
| 2678 | p.setBrush(colors.transparent); | 2691 | p.setBrush(colors.transparent); |
| 2679 | p.drawRect(center.x(), center.y(), 56, 20); | 2692 | p.drawRoundedRect(center.x(), center.y(), 34, 16, 2, 2); |
| 2680 | p.drawRect(center.x() + 56, center.y() + 6, 3, 8); | 2693 | |
| 2681 | p.setBrush(colors.deadzone); | 2694 | p.setPen(QPen(colors.indicator2, 1)); |
| 2695 | p.setBrush(colors.indicator2); | ||
| 2696 | p.drawRect(center.x() + 35, center.y() + 4.5f, 4, 7); | ||
| 2682 | switch (battery) { | 2697 | switch (battery) { |
| 2683 | case Common::Input::BatteryLevel::Charging: | 2698 | case Common::Input::BatteryLevel::Charging: |
| 2684 | p.setBrush(colors.indicator2); | 2699 | p.drawRect(center.x(), center.y(), 34, 16); |
| 2685 | p.drawText(center + QPoint(2, 14), tr("Charging")); | 2700 | p.setPen(colors.slider); |
| 2701 | p.setBrush(colors.charging); | ||
| 2702 | DrawSymbol(p, center + QPointF(17.0f, 8.0f), Symbol::Charging, 2.1f); | ||
| 2686 | break; | 2703 | break; |
| 2687 | case Common::Input::BatteryLevel::Full: | 2704 | case Common::Input::BatteryLevel::Full: |
| 2688 | p.drawRect(center.x() + 42, center.y(), 14, 20); | 2705 | p.drawRect(center.x(), center.y(), 34, 16); |
| 2689 | p.drawRect(center.x() + 28, center.y(), 14, 20); | ||
| 2690 | p.drawRect(center.x() + 14, center.y(), 14, 20); | ||
| 2691 | p.drawRect(center.x(), center.y(), 14, 20); | ||
| 2692 | break; | 2706 | break; |
| 2693 | case Common::Input::BatteryLevel::Medium: | 2707 | case Common::Input::BatteryLevel::Medium: |
| 2694 | p.drawRect(center.x() + 28, center.y(), 14, 20); | 2708 | p.drawRect(center.x(), center.y(), 25, 16); |
| 2695 | p.drawRect(center.x() + 14, center.y(), 14, 20); | ||
| 2696 | p.drawRect(center.x(), center.y(), 14, 20); | ||
| 2697 | break; | 2709 | break; |
| 2698 | case Common::Input::BatteryLevel::Low: | 2710 | case Common::Input::BatteryLevel::Low: |
| 2699 | p.drawRect(center.x() + 14, center.y(), 14, 20); | 2711 | p.drawRect(center.x(), center.y(), 17, 16); |
| 2700 | p.drawRect(center.x(), center.y(), 14, 20); | ||
| 2701 | break; | 2712 | break; |
| 2702 | case Common::Input::BatteryLevel::Critical: | 2713 | case Common::Input::BatteryLevel::Critical: |
| 2703 | p.drawRect(center.x(), center.y(), 14, 20); | 2714 | p.drawRect(center.x(), center.y(), 6, 16); |
| 2704 | break; | 2715 | break; |
| 2705 | case Common::Input::BatteryLevel::Empty: | 2716 | case Common::Input::BatteryLevel::Empty: |
| 2706 | p.drawRect(center.x(), center.y(), 5, 20); | 2717 | p.drawRect(center.x(), center.y(), 3, 16); |
| 2707 | break; | 2718 | break; |
| 2708 | default: | 2719 | default: |
| 2709 | break; | 2720 | break; |
| @@ -2724,6 +2735,7 @@ void PlayerControlPreview::DrawSymbol(QPainter& p, const QPointF center, Symbol | |||
| 2724 | std::array<QPointF, symbol_sl.size() / 2> sl_icon; | 2735 | std::array<QPointF, symbol_sl.size() / 2> sl_icon; |
| 2725 | std::array<QPointF, symbol_zr.size() / 2> zr_icon; | 2736 | std::array<QPointF, symbol_zr.size() / 2> zr_icon; |
| 2726 | std::array<QPointF, symbol_sr.size() / 2> sr_icon; | 2737 | std::array<QPointF, symbol_sr.size() / 2> sr_icon; |
| 2738 | std::array<QPointF, symbol_charging.size() / 2> charging_icon; | ||
| 2727 | switch (symbol) { | 2739 | switch (symbol) { |
| 2728 | case Symbol::House: | 2740 | case Symbol::House: |
| 2729 | for (std::size_t point = 0; point < house.size() / 2; ++point) { | 2741 | for (std::size_t point = 0; point < house.size() / 2; ++point) { |
| @@ -2809,6 +2821,13 @@ void PlayerControlPreview::DrawSymbol(QPainter& p, const QPointF center, Symbol | |||
| 2809 | } | 2821 | } |
| 2810 | p.drawPolygon(sr_icon.data(), static_cast<int>(sr_icon.size())); | 2822 | p.drawPolygon(sr_icon.data(), static_cast<int>(sr_icon.size())); |
| 2811 | break; | 2823 | break; |
| 2824 | case Symbol::Charging: | ||
| 2825 | for (std::size_t point = 0; point < symbol_charging.size() / 2; ++point) { | ||
| 2826 | charging_icon[point] = center + QPointF(symbol_charging[point * 2] * icon_size, | ||
| 2827 | symbol_charging[point * 2 + 1] * icon_size); | ||
| 2828 | } | ||
| 2829 | p.drawPolygon(charging_icon.data(), static_cast<int>(charging_icon.size())); | ||
| 2830 | break; | ||
| 2812 | } | 2831 | } |
| 2813 | } | 2832 | } |
| 2814 | 2833 | ||
diff --git a/src/yuzu/configuration/configure_input_player_widget.h b/src/yuzu/configuration/configure_input_player_widget.h index 4cd5c3be0..3582ef77a 100644 --- a/src/yuzu/configuration/configure_input_player_widget.h +++ b/src/yuzu/configuration/configure_input_player_widget.h | |||
| @@ -72,6 +72,7 @@ private: | |||
| 72 | ZL, | 72 | ZL, |
| 73 | ZR, | 73 | ZR, |
| 74 | SR, | 74 | SR, |
| 75 | Charging, | ||
| 75 | }; | 76 | }; |
| 76 | 77 | ||
| 77 | struct ColorMapping { | 78 | struct ColorMapping { |
| @@ -94,6 +95,7 @@ private: | |||
| 94 | QColor slider_button{}; | 95 | QColor slider_button{}; |
| 95 | QColor slider_arrow{}; | 96 | QColor slider_arrow{}; |
| 96 | QColor deadzone{}; | 97 | QColor deadzone{}; |
| 98 | QColor charging{}; | ||
| 97 | }; | 99 | }; |
| 98 | 100 | ||
| 99 | void UpdateColors(); | 101 | void UpdateColors(); |
diff --git a/src/yuzu/configuration/configure_profile_manager.cpp b/src/yuzu/configuration/configure_profile_manager.cpp index 78b6374c0..d9f6dee4e 100644 --- a/src/yuzu/configuration/configure_profile_manager.cpp +++ b/src/yuzu/configuration/configure_profile_manager.cpp | |||
| @@ -33,10 +33,10 @@ constexpr std::array<u8, 107> backup_jpeg{ | |||
| 33 | 0x01, 0x01, 0x00, 0x00, 0x3f, 0x00, 0xd2, 0xcf, 0x20, 0xff, 0xd9, | 33 | 0x01, 0x01, 0x00, 0x00, 0x3f, 0x00, 0xd2, 0xcf, 0x20, 0xff, 0xd9, |
| 34 | }; | 34 | }; |
| 35 | 35 | ||
| 36 | QString GetImagePath(Common::UUID uuid) { | 36 | QString GetImagePath(const Common::UUID& uuid) { |
| 37 | const auto path = | 37 | const auto path = |
| 38 | Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / | 38 | Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / |
| 39 | fmt::format("system/save/8000000000000010/su/avators/{}.jpg", uuid.FormatSwitch()); | 39 | fmt::format("system/save/8000000000000010/su/avators/{}.jpg", uuid.FormattedString()); |
| 40 | return QString::fromStdString(Common::FS::PathToUTF8String(path)); | 40 | return QString::fromStdString(Common::FS::PathToUTF8String(path)); |
| 41 | } | 41 | } |
| 42 | 42 | ||
| @@ -55,10 +55,10 @@ QString FormatUserEntryText(const QString& username, Common::UUID uuid) { | |||
| 55 | return ConfigureProfileManager::tr("%1\n%2", | 55 | return ConfigureProfileManager::tr("%1\n%2", |
| 56 | "%1 is the profile username, %2 is the formatted UUID (e.g. " | 56 | "%1 is the profile username, %2 is the formatted UUID (e.g. " |
| 57 | "00112233-4455-6677-8899-AABBCCDDEEFF))") | 57 | "00112233-4455-6677-8899-AABBCCDDEEFF))") |
| 58 | .arg(username, QString::fromStdString(uuid.FormatSwitch())); | 58 | .arg(username, QString::fromStdString(uuid.FormattedString())); |
| 59 | } | 59 | } |
| 60 | 60 | ||
| 61 | QPixmap GetIcon(Common::UUID uuid) { | 61 | QPixmap GetIcon(const Common::UUID& uuid) { |
| 62 | QPixmap icon{GetImagePath(uuid)}; | 62 | QPixmap icon{GetImagePath(uuid)}; |
| 63 | 63 | ||
| 64 | if (!icon) { | 64 | if (!icon) { |
| @@ -200,7 +200,7 @@ void ConfigureProfileManager::AddUser() { | |||
| 200 | return; | 200 | return; |
| 201 | } | 201 | } |
| 202 | 202 | ||
| 203 | const auto uuid = Common::UUID::Generate(); | 203 | const auto uuid = Common::UUID::MakeRandom(); |
| 204 | profile_manager->CreateNewUser(uuid, username.toStdString()); | 204 | profile_manager->CreateNewUser(uuid, username.toStdString()); |
| 205 | 205 | ||
| 206 | item_model->appendRow(new QStandardItem{GetIcon(uuid), FormatUserEntryText(username, uuid)}); | 206 | item_model->appendRow(new QStandardItem{GetIcon(uuid), FormatUserEntryText(username, uuid)}); |
diff --git a/src/yuzu/configuration/configure_touch_from_button.cpp b/src/yuzu/configuration/configure_touch_from_button.cpp index bde0a08c4..211a00217 100644 --- a/src/yuzu/configuration/configure_touch_from_button.cpp +++ b/src/yuzu/configuration/configure_touch_from_button.cpp | |||
| @@ -227,6 +227,9 @@ void ConfigureTouchFromButton::RenameMapping() { | |||
| 227 | } | 227 | } |
| 228 | 228 | ||
| 229 | void ConfigureTouchFromButton::GetButtonInput(const int row_index, const bool is_new) { | 229 | void ConfigureTouchFromButton::GetButtonInput(const int row_index, const bool is_new) { |
| 230 | if (timeout_timer->isActive()) { | ||
| 231 | return; | ||
| 232 | } | ||
| 230 | binding_list_model->item(row_index, 0)->setText(tr("[press key]")); | 233 | binding_list_model->item(row_index, 0)->setText(tr("[press key]")); |
| 231 | 234 | ||
| 232 | input_setter = [this, row_index, is_new](const Common::ParamPackage& params, | 235 | input_setter = [this, row_index, is_new](const Common::ParamPackage& params, |
diff --git a/src/yuzu/configuration/configure_vibration.cpp b/src/yuzu/configuration/configure_vibration.cpp index adce04b27..779b6401c 100644 --- a/src/yuzu/configuration/configure_vibration.cpp +++ b/src/yuzu/configuration/configure_vibration.cpp | |||
| @@ -9,11 +9,14 @@ | |||
| 9 | 9 | ||
| 10 | #include "common/param_package.h" | 10 | #include "common/param_package.h" |
| 11 | #include "common/settings.h" | 11 | #include "common/settings.h" |
| 12 | #include "core/hid/emulated_controller.h" | ||
| 13 | #include "core/hid/hid_core.h" | ||
| 14 | #include "core/hid/hid_types.h" | ||
| 12 | #include "ui_configure_vibration.h" | 15 | #include "ui_configure_vibration.h" |
| 13 | #include "yuzu/configuration/configure_vibration.h" | 16 | #include "yuzu/configuration/configure_vibration.h" |
| 14 | 17 | ||
| 15 | ConfigureVibration::ConfigureVibration(QWidget* parent) | 18 | ConfigureVibration::ConfigureVibration(QWidget* parent, Core::HID::HIDCore& hid_core_) |
| 16 | : QDialog(parent), ui(std::make_unique<Ui::ConfigureVibration>()) { | 19 | : QDialog(parent), ui(std::make_unique<Ui::ConfigureVibration>()), hid_core{hid_core_} { |
| 17 | ui->setupUi(this); | 20 | ui->setupUi(this); |
| 18 | 21 | ||
| 19 | vibration_groupboxes = { | 22 | vibration_groupboxes = { |
| @@ -31,6 +34,13 @@ ConfigureVibration::ConfigureVibration(QWidget* parent) | |||
| 31 | const auto& players = Settings::values.players.GetValue(); | 34 | const auto& players = Settings::values.players.GetValue(); |
| 32 | 35 | ||
| 33 | for (std::size_t i = 0; i < NUM_PLAYERS; ++i) { | 36 | for (std::size_t i = 0; i < NUM_PLAYERS; ++i) { |
| 37 | auto controller = hid_core.GetEmulatedControllerByIndex(i); | ||
| 38 | Core::HID::ControllerUpdateCallback engine_callback{ | ||
| 39 | .on_change = [this, | ||
| 40 | i](Core::HID::ControllerTriggerType type) { VibrateController(type, i); }, | ||
| 41 | .is_npad_service = false, | ||
| 42 | }; | ||
| 43 | controller_callback_key[i] = controller->SetCallback(engine_callback); | ||
| 34 | vibration_groupboxes[i]->setChecked(players[i].vibration_enabled); | 44 | vibration_groupboxes[i]->setChecked(players[i].vibration_enabled); |
| 35 | vibration_spinboxes[i]->setValue(players[i].vibration_strength); | 45 | vibration_spinboxes[i]->setValue(players[i].vibration_strength); |
| 36 | } | 46 | } |
| @@ -45,7 +55,14 @@ ConfigureVibration::ConfigureVibration(QWidget* parent) | |||
| 45 | RetranslateUI(); | 55 | RetranslateUI(); |
| 46 | } | 56 | } |
| 47 | 57 | ||
| 48 | ConfigureVibration::~ConfigureVibration() = default; | 58 | ConfigureVibration::~ConfigureVibration() { |
| 59 | StopVibrations(); | ||
| 60 | |||
| 61 | for (std::size_t i = 0; i < NUM_PLAYERS; ++i) { | ||
| 62 | auto controller = hid_core.GetEmulatedControllerByIndex(i); | ||
| 63 | controller->DeleteCallback(controller_callback_key[i]); | ||
| 64 | } | ||
| 65 | }; | ||
| 49 | 66 | ||
| 50 | void ConfigureVibration::ApplyConfiguration() { | 67 | void ConfigureVibration::ApplyConfiguration() { |
| 51 | auto& players = Settings::values.players.GetValue(); | 68 | auto& players = Settings::values.players.GetValue(); |
| @@ -70,3 +87,54 @@ void ConfigureVibration::changeEvent(QEvent* event) { | |||
| 70 | void ConfigureVibration::RetranslateUI() { | 87 | void ConfigureVibration::RetranslateUI() { |
| 71 | ui->retranslateUi(this); | 88 | ui->retranslateUi(this); |
| 72 | } | 89 | } |
| 90 | |||
| 91 | void ConfigureVibration::VibrateController(Core::HID::ControllerTriggerType type, | ||
| 92 | std::size_t player_index) { | ||
| 93 | if (type != Core::HID::ControllerTriggerType::Button) { | ||
| 94 | return; | ||
| 95 | } | ||
| 96 | |||
| 97 | auto& player = Settings::values.players.GetValue()[player_index]; | ||
| 98 | auto controller = hid_core.GetEmulatedControllerByIndex(player_index); | ||
| 99 | const int vibration_strenght = vibration_spinboxes[player_index]->value(); | ||
| 100 | const auto& buttons = controller->GetButtonsValues(); | ||
| 101 | |||
| 102 | bool button_is_pressed = false; | ||
| 103 | for (std::size_t i = 0; i < buttons.size(); ++i) { | ||
| 104 | if (buttons[i].value) { | ||
| 105 | button_is_pressed = true; | ||
| 106 | break; | ||
| 107 | } | ||
| 108 | } | ||
| 109 | |||
| 110 | if (!button_is_pressed) { | ||
| 111 | StopVibrations(); | ||
| 112 | return; | ||
| 113 | } | ||
| 114 | |||
| 115 | const int old_vibration_enabled = player.vibration_enabled; | ||
| 116 | const bool old_vibration_strenght = player.vibration_strength; | ||
| 117 | player.vibration_enabled = true; | ||
| 118 | player.vibration_strength = vibration_strenght; | ||
| 119 | |||
| 120 | const Core::HID::VibrationValue vibration{ | ||
| 121 | .low_amplitude = 1.0f, | ||
| 122 | .low_frequency = 160.0f, | ||
| 123 | .high_amplitude = 1.0f, | ||
| 124 | .high_frequency = 320.0f, | ||
| 125 | }; | ||
| 126 | controller->SetVibration(0, vibration); | ||
| 127 | controller->SetVibration(1, vibration); | ||
| 128 | |||
| 129 | // Restore previous values | ||
| 130 | player.vibration_enabled = old_vibration_enabled; | ||
| 131 | player.vibration_strength = old_vibration_strenght; | ||
| 132 | } | ||
| 133 | |||
| 134 | void ConfigureVibration::StopVibrations() { | ||
| 135 | for (std::size_t i = 0; i < NUM_PLAYERS; ++i) { | ||
| 136 | auto controller = hid_core.GetEmulatedControllerByIndex(i); | ||
| 137 | controller->SetVibration(0, Core::HID::DEFAULT_VIBRATION_VALUE); | ||
| 138 | controller->SetVibration(1, Core::HID::DEFAULT_VIBRATION_VALUE); | ||
| 139 | } | ||
| 140 | } | ||
diff --git a/src/yuzu/configuration/configure_vibration.h b/src/yuzu/configuration/configure_vibration.h index 37bbc2653..50b8195fa 100644 --- a/src/yuzu/configuration/configure_vibration.h +++ b/src/yuzu/configuration/configure_vibration.h | |||
| @@ -15,11 +15,16 @@ namespace Ui { | |||
| 15 | class ConfigureVibration; | 15 | class ConfigureVibration; |
| 16 | } | 16 | } |
| 17 | 17 | ||
| 18 | namespace Core::HID { | ||
| 19 | enum class ControllerTriggerType; | ||
| 20 | class HIDCore; | ||
| 21 | } // namespace Core::HID | ||
| 22 | |||
| 18 | class ConfigureVibration : public QDialog { | 23 | class ConfigureVibration : public QDialog { |
| 19 | Q_OBJECT | 24 | Q_OBJECT |
| 20 | 25 | ||
| 21 | public: | 26 | public: |
| 22 | explicit ConfigureVibration(QWidget* parent); | 27 | explicit ConfigureVibration(QWidget* parent, Core::HID::HIDCore& hid_core_); |
| 23 | ~ConfigureVibration() override; | 28 | ~ConfigureVibration() override; |
| 24 | 29 | ||
| 25 | void ApplyConfiguration(); | 30 | void ApplyConfiguration(); |
| @@ -27,14 +32,21 @@ public: | |||
| 27 | private: | 32 | private: |
| 28 | void changeEvent(QEvent* event) override; | 33 | void changeEvent(QEvent* event) override; |
| 29 | void RetranslateUI(); | 34 | void RetranslateUI(); |
| 35 | void VibrateController(Core::HID::ControllerTriggerType type, std::size_t player_index); | ||
| 36 | void StopVibrations(); | ||
| 30 | 37 | ||
| 31 | std::unique_ptr<Ui::ConfigureVibration> ui; | 38 | std::unique_ptr<Ui::ConfigureVibration> ui; |
| 32 | 39 | ||
| 33 | static constexpr std::size_t NUM_PLAYERS = 8; | 40 | static constexpr std::size_t NUM_PLAYERS = 8; |
| 34 | 41 | ||
| 35 | // Groupboxes encapsulating the vibration strength spinbox. | 42 | /// Groupboxes encapsulating the vibration strength spinbox. |
| 36 | std::array<QGroupBox*, NUM_PLAYERS> vibration_groupboxes; | 43 | std::array<QGroupBox*, NUM_PLAYERS> vibration_groupboxes; |
| 37 | 44 | ||
| 38 | // Spinboxes representing the vibration strength percentage. | 45 | /// Spinboxes representing the vibration strength percentage. |
| 39 | std::array<QSpinBox*, NUM_PLAYERS> vibration_spinboxes; | 46 | std::array<QSpinBox*, NUM_PLAYERS> vibration_spinboxes; |
| 47 | |||
| 48 | /// Callback index to stop the controllers events | ||
| 49 | std::array<int, NUM_PLAYERS> controller_callback_key; | ||
| 50 | |||
| 51 | Core::HID::HIDCore& hid_core; | ||
| 40 | }; | 52 | }; |
diff --git a/src/yuzu/configuration/configure_vibration.ui b/src/yuzu/configuration/configure_vibration.ui index efdf317a9..447a18eb1 100644 --- a/src/yuzu/configuration/configure_vibration.ui +++ b/src/yuzu/configuration/configure_vibration.ui | |||
| @@ -17,6 +17,13 @@ | |||
| 17 | <string notr="true"/> | 17 | <string notr="true"/> |
| 18 | </property> | 18 | </property> |
| 19 | <layout class="QVBoxLayout"> | 19 | <layout class="QVBoxLayout"> |
| 20 | <item row="0" column="0" colspan="4"> | ||
| 21 | <widget class="QLabel" name="label_1"> | ||
| 22 | <property name="text"> | ||
| 23 | <string>Press any controller button to vibrate the controller.</string> | ||
| 24 | </property> | ||
| 25 | </widget> | ||
| 26 | </item> | ||
| 20 | <item> | 27 | <item> |
| 21 | <widget class="QGroupBox" name="vibrationStrengthGroup"> | 28 | <widget class="QGroupBox" name="vibrationStrengthGroup"> |
| 22 | <property name="title"> | 29 | <property name="title"> |
diff --git a/src/yuzu/debugger/console.cpp b/src/yuzu/debugger/console.cpp index f89ea8ea7..4b508b466 100644 --- a/src/yuzu/debugger/console.cpp +++ b/src/yuzu/debugger/console.cpp | |||
| @@ -30,6 +30,7 @@ void ToggleConsole() { | |||
| 30 | freopen_s(&temp, "CONIN$", "r", stdin); | 30 | freopen_s(&temp, "CONIN$", "r", stdin); |
| 31 | freopen_s(&temp, "CONOUT$", "w", stdout); | 31 | freopen_s(&temp, "CONOUT$", "w", stdout); |
| 32 | freopen_s(&temp, "CONOUT$", "w", stderr); | 32 | freopen_s(&temp, "CONOUT$", "w", stderr); |
| 33 | SetConsoleOutputCP(65001); | ||
| 33 | SetColorConsoleBackendEnabled(true); | 34 | SetColorConsoleBackendEnabled(true); |
| 34 | } | 35 | } |
| 35 | } else { | 36 | } else { |
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index d9e689d14..06774768d 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp | |||
| @@ -25,6 +25,7 @@ | |||
| 25 | #include "core/file_sys/vfs_real.h" | 25 | #include "core/file_sys/vfs_real.h" |
| 26 | #include "core/frontend/applets/controller.h" | 26 | #include "core/frontend/applets/controller.h" |
| 27 | #include "core/frontend/applets/general_frontend.h" | 27 | #include "core/frontend/applets/general_frontend.h" |
| 28 | #include "core/frontend/applets/mii.h" | ||
| 28 | #include "core/frontend/applets/software_keyboard.h" | 29 | #include "core/frontend/applets/software_keyboard.h" |
| 29 | #include "core/hid/emulated_controller.h" | 30 | #include "core/hid/emulated_controller.h" |
| 30 | #include "core/hid/hid_core.h" | 31 | #include "core/hid/hid_core.h" |
| @@ -249,9 +250,9 @@ GMainWindow::GMainWindow() | |||
| 249 | #ifdef ARCHITECTURE_x86_64 | 250 | #ifdef ARCHITECTURE_x86_64 |
| 250 | const auto& caps = Common::GetCPUCaps(); | 251 | const auto& caps = Common::GetCPUCaps(); |
| 251 | std::string cpu_string = caps.cpu_string; | 252 | std::string cpu_string = caps.cpu_string; |
| 252 | if (caps.avx || caps.avx2 || caps.avx512) { | 253 | if (caps.avx || caps.avx2 || caps.avx512f) { |
| 253 | cpu_string += " | AVX"; | 254 | cpu_string += " | AVX"; |
| 254 | if (caps.avx512) { | 255 | if (caps.avx512f) { |
| 255 | cpu_string += "512"; | 256 | cpu_string += "512"; |
| 256 | } else if (caps.avx2) { | 257 | } else if (caps.avx2) { |
| 257 | cpu_string += '2'; | 258 | cpu_string += '2'; |
| @@ -806,21 +807,8 @@ void GMainWindow::InitializeWidgets() { | |||
| 806 | filter_status_button = new QPushButton(); | 807 | filter_status_button = new QPushButton(); |
| 807 | filter_status_button->setObjectName(QStringLiteral("TogglableStatusBarButton")); | 808 | filter_status_button->setObjectName(QStringLiteral("TogglableStatusBarButton")); |
| 808 | filter_status_button->setFocusPolicy(Qt::NoFocus); | 809 | filter_status_button->setFocusPolicy(Qt::NoFocus); |
| 809 | connect(filter_status_button, &QPushButton::clicked, [&] { | 810 | connect(filter_status_button, &QPushButton::clicked, this, |
| 810 | auto filter = Settings::values.scaling_filter.GetValue(); | 811 | &GMainWindow::OnToggleAdaptingFilter); |
| 811 | if (filter == Settings::ScalingFilter::LastFilter) { | ||
| 812 | filter = Settings::ScalingFilter::NearestNeighbor; | ||
| 813 | } else { | ||
| 814 | filter = static_cast<Settings::ScalingFilter>(static_cast<u32>(filter) + 1); | ||
| 815 | } | ||
| 816 | if (Settings::values.renderer_backend.GetValue() == Settings::RendererBackend::OpenGL && | ||
| 817 | filter == Settings::ScalingFilter::Fsr) { | ||
| 818 | filter = Settings::ScalingFilter::NearestNeighbor; | ||
| 819 | } | ||
| 820 | Settings::values.scaling_filter.SetValue(filter); | ||
| 821 | filter_status_button->setChecked(true); | ||
| 822 | UpdateFilterText(); | ||
| 823 | }); | ||
| 824 | auto filter = Settings::values.scaling_filter.GetValue(); | 812 | auto filter = Settings::values.scaling_filter.GetValue(); |
| 825 | if (Settings::values.renderer_backend.GetValue() == Settings::RendererBackend::OpenGL && | 813 | if (Settings::values.renderer_backend.GetValue() == Settings::RendererBackend::OpenGL && |
| 826 | filter == Settings::ScalingFilter::Fsr) { | 814 | filter == Settings::ScalingFilter::Fsr) { |
| @@ -835,25 +823,7 @@ void GMainWindow::InitializeWidgets() { | |||
| 835 | dock_status_button = new QPushButton(); | 823 | dock_status_button = new QPushButton(); |
| 836 | dock_status_button->setObjectName(QStringLiteral("TogglableStatusBarButton")); | 824 | dock_status_button->setObjectName(QStringLiteral("TogglableStatusBarButton")); |
| 837 | dock_status_button->setFocusPolicy(Qt::NoFocus); | 825 | dock_status_button->setFocusPolicy(Qt::NoFocus); |
| 838 | connect(dock_status_button, &QPushButton::clicked, [&] { | 826 | connect(dock_status_button, &QPushButton::clicked, this, &GMainWindow::OnToggleDockedMode); |
| 839 | const bool is_docked = Settings::values.use_docked_mode.GetValue(); | ||
| 840 | auto* player_1 = system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1); | ||
| 841 | auto* handheld = system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld); | ||
| 842 | |||
| 843 | if (!is_docked && handheld->IsConnected()) { | ||
| 844 | QMessageBox::warning(this, tr("Invalid config detected"), | ||
| 845 | tr("Handheld controller can't be used on docked mode. Pro " | ||
| 846 | "controller will be selected.")); | ||
| 847 | handheld->Disconnect(); | ||
| 848 | player_1->SetNpadStyleIndex(Core::HID::NpadStyleIndex::ProController); | ||
| 849 | player_1->Connect(); | ||
| 850 | controller_dialog->refreshConfiguration(); | ||
| 851 | } | ||
| 852 | |||
| 853 | Settings::values.use_docked_mode.SetValue(!is_docked); | ||
| 854 | dock_status_button->setChecked(!is_docked); | ||
| 855 | OnDockedModeChanged(is_docked, !is_docked, *system); | ||
| 856 | }); | ||
| 857 | dock_status_button->setText(tr("DOCK")); | 827 | dock_status_button->setText(tr("DOCK")); |
| 858 | dock_status_button->setCheckable(true); | 828 | dock_status_button->setCheckable(true); |
| 859 | dock_status_button->setChecked(Settings::values.use_docked_mode.GetValue()); | 829 | dock_status_button->setChecked(Settings::values.use_docked_mode.GetValue()); |
| @@ -863,22 +833,7 @@ void GMainWindow::InitializeWidgets() { | |||
| 863 | gpu_accuracy_button->setObjectName(QStringLiteral("GPUStatusBarButton")); | 833 | gpu_accuracy_button->setObjectName(QStringLiteral("GPUStatusBarButton")); |
| 864 | gpu_accuracy_button->setCheckable(true); | 834 | gpu_accuracy_button->setCheckable(true); |
| 865 | gpu_accuracy_button->setFocusPolicy(Qt::NoFocus); | 835 | gpu_accuracy_button->setFocusPolicy(Qt::NoFocus); |
| 866 | connect(gpu_accuracy_button, &QPushButton::clicked, [this] { | 836 | connect(gpu_accuracy_button, &QPushButton::clicked, this, &GMainWindow::OnToggleGpuAccuracy); |
| 867 | switch (Settings::values.gpu_accuracy.GetValue()) { | ||
| 868 | case Settings::GPUAccuracy::High: { | ||
| 869 | Settings::values.gpu_accuracy.SetValue(Settings::GPUAccuracy::Normal); | ||
| 870 | break; | ||
| 871 | } | ||
| 872 | case Settings::GPUAccuracy::Normal: | ||
| 873 | case Settings::GPUAccuracy::Extreme: | ||
| 874 | default: { | ||
| 875 | Settings::values.gpu_accuracy.SetValue(Settings::GPUAccuracy::High); | ||
| 876 | } | ||
| 877 | } | ||
| 878 | |||
| 879 | system->ApplySettings(); | ||
| 880 | UpdateGPUAccuracyButton(); | ||
| 881 | }); | ||
| 882 | UpdateGPUAccuracyButton(); | 837 | UpdateGPUAccuracyButton(); |
| 883 | statusBar()->insertPermanentWidget(0, gpu_accuracy_button); | 838 | statusBar()->insertPermanentWidget(0, gpu_accuracy_button); |
| 884 | 839 | ||
| @@ -965,6 +920,7 @@ void GMainWindow::LinkActionShortcut(QAction* action, const QString& action_name | |||
| 965 | static const QString main_window = QStringLiteral("Main Window"); | 920 | static const QString main_window = QStringLiteral("Main Window"); |
| 966 | action->setShortcut(hotkey_registry.GetKeySequence(main_window, action_name)); | 921 | action->setShortcut(hotkey_registry.GetKeySequence(main_window, action_name)); |
| 967 | action->setShortcutContext(hotkey_registry.GetShortcutContext(main_window, action_name)); | 922 | action->setShortcutContext(hotkey_registry.GetShortcutContext(main_window, action_name)); |
| 923 | action->setAutoRepeat(false); | ||
| 968 | 924 | ||
| 969 | this->addAction(action); | 925 | this->addAction(action); |
| 970 | 926 | ||
| @@ -979,7 +935,7 @@ void GMainWindow::InitializeHotkeys() { | |||
| 979 | hotkey_registry.LoadHotkeys(); | 935 | hotkey_registry.LoadHotkeys(); |
| 980 | 936 | ||
| 981 | LinkActionShortcut(ui->action_Load_File, QStringLiteral("Load File")); | 937 | LinkActionShortcut(ui->action_Load_File, QStringLiteral("Load File")); |
| 982 | LinkActionShortcut(ui->action_Load_Amiibo, QStringLiteral("Load Amiibo")); | 938 | LinkActionShortcut(ui->action_Load_Amiibo, QStringLiteral("Load/Remove Amiibo")); |
| 983 | LinkActionShortcut(ui->action_Exit, QStringLiteral("Exit yuzu")); | 939 | LinkActionShortcut(ui->action_Exit, QStringLiteral("Exit yuzu")); |
| 984 | LinkActionShortcut(ui->action_Restart, QStringLiteral("Restart Emulation")); | 940 | LinkActionShortcut(ui->action_Restart, QStringLiteral("Restart Emulation")); |
| 985 | LinkActionShortcut(ui->action_Pause, QStringLiteral("Continue/Pause Emulation")); | 941 | LinkActionShortcut(ui->action_Pause, QStringLiteral("Continue/Pause Emulation")); |
| @@ -1008,12 +964,10 @@ void GMainWindow::InitializeHotkeys() { | |||
| 1008 | ToggleFullscreen(); | 964 | ToggleFullscreen(); |
| 1009 | } | 965 | } |
| 1010 | }); | 966 | }); |
| 1011 | connect_shortcut(QStringLiteral("Change Docked Mode"), [&] { | 967 | connect_shortcut(QStringLiteral("Change Adapting Filter"), |
| 1012 | Settings::values.use_docked_mode.SetValue(!Settings::values.use_docked_mode.GetValue()); | 968 | &GMainWindow::OnToggleAdaptingFilter); |
| 1013 | OnDockedModeChanged(!Settings::values.use_docked_mode.GetValue(), | 969 | connect_shortcut(QStringLiteral("Change Docked Mode"), &GMainWindow::OnToggleDockedMode); |
| 1014 | Settings::values.use_docked_mode.GetValue(), *system); | 970 | connect_shortcut(QStringLiteral("Change GPU Accuracy"), &GMainWindow::OnToggleGpuAccuracy); |
| 1015 | dock_status_button->setChecked(Settings::values.use_docked_mode.GetValue()); | ||
| 1016 | }); | ||
| 1017 | connect_shortcut(QStringLiteral("Audio Mute/Unmute"), | 971 | connect_shortcut(QStringLiteral("Audio Mute/Unmute"), |
| 1018 | [] { Settings::values.audio_muted = !Settings::values.audio_muted; }); | 972 | [] { Settings::values.audio_muted = !Settings::values.audio_muted; }); |
| 1019 | connect_shortcut(QStringLiteral("Audio Volume Down"), [] { | 973 | connect_shortcut(QStringLiteral("Audio Volume Down"), [] { |
| @@ -1051,8 +1005,10 @@ void GMainWindow::SetDefaultUIGeometry() { | |||
| 1051 | } | 1005 | } |
| 1052 | 1006 | ||
| 1053 | void GMainWindow::RestoreUIState() { | 1007 | void GMainWindow::RestoreUIState() { |
| 1008 | setWindowFlags(windowFlags() & ~Qt::FramelessWindowHint); | ||
| 1054 | restoreGeometry(UISettings::values.geometry); | 1009 | restoreGeometry(UISettings::values.geometry); |
| 1055 | restoreState(UISettings::values.state); | 1010 | restoreState(UISettings::values.state); |
| 1011 | render_window->setWindowFlags(render_window->windowFlags() & ~Qt::FramelessWindowHint); | ||
| 1056 | render_window->restoreGeometry(UISettings::values.renderwindow_geometry); | 1012 | render_window->restoreGeometry(UISettings::values.renderwindow_geometry); |
| 1057 | #if MICROPROFILE_ENABLED | 1013 | #if MICROPROFILE_ENABLED |
| 1058 | microProfileDialog->restoreGeometry(UISettings::values.microprofile_geometry); | 1014 | microProfileDialog->restoreGeometry(UISettings::values.microprofile_geometry); |
| @@ -1079,14 +1035,14 @@ void GMainWindow::RestoreUIState() { | |||
| 1079 | } | 1035 | } |
| 1080 | 1036 | ||
| 1081 | void GMainWindow::OnAppFocusStateChanged(Qt::ApplicationState state) { | 1037 | void GMainWindow::OnAppFocusStateChanged(Qt::ApplicationState state) { |
| 1082 | if (!UISettings::values.pause_when_in_background) { | ||
| 1083 | return; | ||
| 1084 | } | ||
| 1085 | if (state != Qt::ApplicationHidden && state != Qt::ApplicationInactive && | 1038 | if (state != Qt::ApplicationHidden && state != Qt::ApplicationInactive && |
| 1086 | state != Qt::ApplicationActive) { | 1039 | state != Qt::ApplicationActive) { |
| 1087 | LOG_DEBUG(Frontend, "ApplicationState unusual flag: {} ", state); | 1040 | LOG_DEBUG(Frontend, "ApplicationState unusual flag: {} ", state); |
| 1088 | } | 1041 | } |
| 1089 | if (emulation_running) { | 1042 | if (!emulation_running) { |
| 1043 | return; | ||
| 1044 | } | ||
| 1045 | if (UISettings::values.pause_when_in_background) { | ||
| 1090 | if (emu_thread->IsRunning() && | 1046 | if (emu_thread->IsRunning() && |
| 1091 | (state & (Qt::ApplicationHidden | Qt::ApplicationInactive))) { | 1047 | (state & (Qt::ApplicationHidden | Qt::ApplicationInactive))) { |
| 1092 | auto_paused = true; | 1048 | auto_paused = true; |
| @@ -1096,6 +1052,16 @@ void GMainWindow::OnAppFocusStateChanged(Qt::ApplicationState state) { | |||
| 1096 | OnStartGame(); | 1052 | OnStartGame(); |
| 1097 | } | 1053 | } |
| 1098 | } | 1054 | } |
| 1055 | if (UISettings::values.mute_when_in_background) { | ||
| 1056 | if (!Settings::values.audio_muted && | ||
| 1057 | (state & (Qt::ApplicationHidden | Qt::ApplicationInactive))) { | ||
| 1058 | Settings::values.audio_muted = true; | ||
| 1059 | auto_muted = true; | ||
| 1060 | } else if (auto_muted && state == Qt::ApplicationActive) { | ||
| 1061 | Settings::values.audio_muted = false; | ||
| 1062 | auto_muted = false; | ||
| 1063 | } | ||
| 1064 | } | ||
| 1099 | } | 1065 | } |
| 1100 | 1066 | ||
| 1101 | void GMainWindow::ConnectWidgetEvents() { | 1067 | void GMainWindow::ConnectWidgetEvents() { |
| @@ -1320,6 +1286,7 @@ bool GMainWindow::LoadROM(const QString& filename, u64 program_id, std::size_t p | |||
| 1320 | std::make_unique<QtControllerSelector>(*this), // Controller Selector | 1286 | std::make_unique<QtControllerSelector>(*this), // Controller Selector |
| 1321 | std::make_unique<QtErrorDisplay>(*this), // Error Display | 1287 | std::make_unique<QtErrorDisplay>(*this), // Error Display |
| 1322 | nullptr, // Parental Controls | 1288 | nullptr, // Parental Controls |
| 1289 | nullptr, // Mii editor | ||
| 1323 | nullptr, // Photo Viewer | 1290 | nullptr, // Photo Viewer |
| 1324 | std::make_unique<QtProfileSelector>(*this), // Profile Selector | 1291 | std::make_unique<QtProfileSelector>(*this), // Profile Selector |
| 1325 | std::make_unique<QtSoftwareKeyboard>(*this), // Software Keyboard | 1292 | std::make_unique<QtSoftwareKeyboard>(*this), // Software Keyboard |
| @@ -1687,7 +1654,7 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target | |||
| 1687 | 1654 | ||
| 1688 | const auto user_save_data_path = FileSys::SaveDataFactory::GetFullPath( | 1655 | const auto user_save_data_path = FileSys::SaveDataFactory::GetFullPath( |
| 1689 | *system, FileSys::SaveDataSpaceId::NandUser, FileSys::SaveDataType::SaveData, | 1656 | *system, FileSys::SaveDataSpaceId::NandUser, FileSys::SaveDataType::SaveData, |
| 1690 | program_id, user_id->uuid, 0); | 1657 | program_id, user_id->AsU128(), 0); |
| 1691 | 1658 | ||
| 1692 | path = Common::FS::ConcatPathSafe(nand_dir, user_save_data_path); | 1659 | path = Common::FS::ConcatPathSafe(nand_dir, user_save_data_path); |
| 1693 | } else { | 1660 | } else { |
| @@ -2865,6 +2832,59 @@ void GMainWindow::OnTasReset() { | |||
| 2865 | input_subsystem->GetTas()->Reset(); | 2832 | input_subsystem->GetTas()->Reset(); |
| 2866 | } | 2833 | } |
| 2867 | 2834 | ||
| 2835 | void GMainWindow::OnToggleDockedMode() { | ||
| 2836 | const bool is_docked = Settings::values.use_docked_mode.GetValue(); | ||
| 2837 | auto* player_1 = system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1); | ||
| 2838 | auto* handheld = system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld); | ||
| 2839 | |||
| 2840 | if (!is_docked && handheld->IsConnected()) { | ||
| 2841 | QMessageBox::warning(this, tr("Invalid config detected"), | ||
| 2842 | tr("Handheld controller can't be used on docked mode. Pro " | ||
| 2843 | "controller will be selected.")); | ||
| 2844 | handheld->Disconnect(); | ||
| 2845 | player_1->SetNpadStyleIndex(Core::HID::NpadStyleIndex::ProController); | ||
| 2846 | player_1->Connect(); | ||
| 2847 | controller_dialog->refreshConfiguration(); | ||
| 2848 | } | ||
| 2849 | |||
| 2850 | Settings::values.use_docked_mode.SetValue(!is_docked); | ||
| 2851 | dock_status_button->setChecked(!is_docked); | ||
| 2852 | OnDockedModeChanged(is_docked, !is_docked, *system); | ||
| 2853 | } | ||
| 2854 | |||
| 2855 | void GMainWindow::OnToggleGpuAccuracy() { | ||
| 2856 | switch (Settings::values.gpu_accuracy.GetValue()) { | ||
| 2857 | case Settings::GPUAccuracy::High: { | ||
| 2858 | Settings::values.gpu_accuracy.SetValue(Settings::GPUAccuracy::Normal); | ||
| 2859 | break; | ||
| 2860 | } | ||
| 2861 | case Settings::GPUAccuracy::Normal: | ||
| 2862 | case Settings::GPUAccuracy::Extreme: | ||
| 2863 | default: { | ||
| 2864 | Settings::values.gpu_accuracy.SetValue(Settings::GPUAccuracy::High); | ||
| 2865 | } | ||
| 2866 | } | ||
| 2867 | |||
| 2868 | system->ApplySettings(); | ||
| 2869 | UpdateGPUAccuracyButton(); | ||
| 2870 | } | ||
| 2871 | |||
| 2872 | void GMainWindow::OnToggleAdaptingFilter() { | ||
| 2873 | auto filter = Settings::values.scaling_filter.GetValue(); | ||
| 2874 | if (filter == Settings::ScalingFilter::LastFilter) { | ||
| 2875 | filter = Settings::ScalingFilter::NearestNeighbor; | ||
| 2876 | } else { | ||
| 2877 | filter = static_cast<Settings::ScalingFilter>(static_cast<u32>(filter) + 1); | ||
| 2878 | } | ||
| 2879 | if (Settings::values.renderer_backend.GetValue() == Settings::RendererBackend::OpenGL && | ||
| 2880 | filter == Settings::ScalingFilter::Fsr) { | ||
| 2881 | filter = Settings::ScalingFilter::NearestNeighbor; | ||
| 2882 | } | ||
| 2883 | Settings::values.scaling_filter.SetValue(filter); | ||
| 2884 | filter_status_button->setChecked(true); | ||
| 2885 | UpdateFilterText(); | ||
| 2886 | } | ||
| 2887 | |||
| 2868 | void GMainWindow::OnConfigurePerGame() { | 2888 | void GMainWindow::OnConfigurePerGame() { |
| 2869 | const u64 title_id = system->GetCurrentProcessProgramID(); | 2889 | const u64 title_id = system->GetCurrentProcessProgramID(); |
| 2870 | OpenPerGameConfiguration(title_id, game_path.toStdString()); | 2890 | OpenPerGameConfiguration(title_id, game_path.toStdString()); |
| @@ -2909,6 +2929,25 @@ void GMainWindow::OnLoadAmiibo() { | |||
| 2909 | return; | 2929 | return; |
| 2910 | } | 2930 | } |
| 2911 | 2931 | ||
| 2932 | Service::SM::ServiceManager& sm = system->ServiceManager(); | ||
| 2933 | auto nfc = sm.GetService<Service::NFP::Module::Interface>("nfp:user"); | ||
| 2934 | if (nfc == nullptr) { | ||
| 2935 | QMessageBox::warning(this, tr("Error"), tr("The current game is not looking for amiibos")); | ||
| 2936 | return; | ||
| 2937 | } | ||
| 2938 | const auto nfc_state = nfc->GetCurrentState(); | ||
| 2939 | if (nfc_state == Service::NFP::DeviceState::TagFound || | ||
| 2940 | nfc_state == Service::NFP::DeviceState::TagMounted) { | ||
| 2941 | nfc->CloseAmiibo(); | ||
| 2942 | QMessageBox::warning(this, tr("Amiibo"), tr("The current amiibo has been removed")); | ||
| 2943 | return; | ||
| 2944 | } | ||
| 2945 | |||
| 2946 | if (nfc_state != Service::NFP::DeviceState::SearchingForTag) { | ||
| 2947 | QMessageBox::warning(this, tr("Error"), tr("The current game is not looking for amiibos")); | ||
| 2948 | return; | ||
| 2949 | } | ||
| 2950 | |||
| 2912 | is_amiibo_file_select_active = true; | 2951 | is_amiibo_file_select_active = true; |
| 2913 | const QString extensions{QStringLiteral("*.bin")}; | 2952 | const QString extensions{QStringLiteral("*.bin")}; |
| 2914 | const QString file_filter = tr("Amiibo File (%1);; All Files (*.*)").arg(extensions); | 2953 | const QString file_filter = tr("Amiibo File (%1);; All Files (*.*)").arg(extensions); |
| @@ -2929,6 +2968,15 @@ void GMainWindow::LoadAmiibo(const QString& filename) { | |||
| 2929 | return; | 2968 | return; |
| 2930 | } | 2969 | } |
| 2931 | 2970 | ||
| 2971 | // Remove amiibo if one is connected | ||
| 2972 | const auto nfc_state = nfc->GetCurrentState(); | ||
| 2973 | if (nfc_state == Service::NFP::DeviceState::TagFound || | ||
| 2974 | nfc_state == Service::NFP::DeviceState::TagMounted) { | ||
| 2975 | nfc->CloseAmiibo(); | ||
| 2976 | QMessageBox::warning(this, tr("Amiibo"), tr("The current amiibo has been removed")); | ||
| 2977 | return; | ||
| 2978 | } | ||
| 2979 | |||
| 2932 | QFile nfc_file{filename}; | 2980 | QFile nfc_file{filename}; |
| 2933 | if (!nfc_file.open(QIODevice::ReadOnly)) { | 2981 | if (!nfc_file.open(QIODevice::ReadOnly)) { |
| 2934 | QMessageBox::warning(this, tr("Error opening Amiibo data file"), | 2982 | QMessageBox::warning(this, tr("Error opening Amiibo data file"), |
diff --git a/src/yuzu/main.h b/src/yuzu/main.h index ca4ab9af5..6a35b9e3d 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h | |||
| @@ -284,6 +284,9 @@ private slots: | |||
| 284 | void OnTasStartStop(); | 284 | void OnTasStartStop(); |
| 285 | void OnTasRecord(); | 285 | void OnTasRecord(); |
| 286 | void OnTasReset(); | 286 | void OnTasReset(); |
| 287 | void OnToggleDockedMode(); | ||
| 288 | void OnToggleGpuAccuracy(); | ||
| 289 | void OnToggleAdaptingFilter(); | ||
| 287 | void OnConfigurePerGame(); | 290 | void OnConfigurePerGame(); |
| 288 | void OnLoadAmiibo(); | 291 | void OnLoadAmiibo(); |
| 289 | void OnOpenYuzuFolder(); | 292 | void OnOpenYuzuFolder(); |
| @@ -369,6 +372,7 @@ private: | |||
| 369 | QString game_path; | 372 | QString game_path; |
| 370 | 373 | ||
| 371 | bool auto_paused = false; | 374 | bool auto_paused = false; |
| 375 | bool auto_muted = false; | ||
| 372 | QTimer mouse_hide_timer; | 376 | QTimer mouse_hide_timer; |
| 373 | 377 | ||
| 374 | // FS | 378 | // FS |
diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui index 5719b2ee4..6ab95b9a5 100644 --- a/src/yuzu/main.ui +++ b/src/yuzu/main.ui | |||
| @@ -266,7 +266,7 @@ | |||
| 266 | <bool>false</bool> | 266 | <bool>false</bool> |
| 267 | </property> | 267 | </property> |
| 268 | <property name="text"> | 268 | <property name="text"> |
| 269 | <string>Load &Amiibo...</string> | 269 | <string>Load/Remove &Amiibo...</string> |
| 270 | </property> | 270 | </property> |
| 271 | </action> | 271 | </action> |
| 272 | <action name="action_Report_Compatibility"> | 272 | <action name="action_Report_Compatibility"> |
diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h index f7298ddad..06e8b46da 100644 --- a/src/yuzu/uisettings.h +++ b/src/yuzu/uisettings.h | |||
| @@ -73,6 +73,7 @@ struct Values { | |||
| 73 | Settings::BasicSetting<bool> confirm_before_closing{true, "confirmClose"}; | 73 | Settings::BasicSetting<bool> confirm_before_closing{true, "confirmClose"}; |
| 74 | Settings::BasicSetting<bool> first_start{true, "firstStart"}; | 74 | Settings::BasicSetting<bool> first_start{true, "firstStart"}; |
| 75 | Settings::BasicSetting<bool> pause_when_in_background{false, "pauseWhenInBackground"}; | 75 | Settings::BasicSetting<bool> pause_when_in_background{false, "pauseWhenInBackground"}; |
| 76 | Settings::BasicSetting<bool> mute_when_in_background{false, "muteWhenInBackground"}; | ||
| 76 | Settings::BasicSetting<bool> hide_mouse{true, "hideInactiveMouse"}; | 77 | Settings::BasicSetting<bool> hide_mouse{true, "hideInactiveMouse"}; |
| 77 | 78 | ||
| 78 | Settings::BasicSetting<bool> select_user_on_boot{false, "select_user_on_boot"}; | 79 | Settings::BasicSetting<bool> select_user_on_boot{false, "select_user_on_boot"}; |
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp index 8e9c7d211..131bc2201 100644 --- a/src/yuzu_cmd/config.cpp +++ b/src/yuzu_cmd/config.cpp | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <memory> | 5 | #include <memory> |
| 6 | #include <optional> | ||
| 6 | #include <sstream> | 7 | #include <sstream> |
| 7 | 8 | ||
| 8 | // Ignore -Wimplicit-fallthrough due to https://github.com/libsdl-org/SDL/issues/4307 | 9 | // Ignore -Wimplicit-fallthrough due to https://github.com/libsdl-org/SDL/issues/4307 |
| @@ -29,11 +30,12 @@ | |||
| 29 | 30 | ||
| 30 | namespace FS = Common::FS; | 31 | namespace FS = Common::FS; |
| 31 | 32 | ||
| 32 | Config::Config() { | 33 | const std::filesystem::path default_config_path = |
| 33 | // TODO: Don't hardcode the path; let the frontend decide where to put the config files. | 34 | FS::GetYuzuPath(FS::YuzuPath::ConfigDir) / "sdl2-config.ini"; |
| 34 | sdl2_config_loc = FS::GetYuzuPath(FS::YuzuPath::ConfigDir) / "sdl2-config.ini"; | ||
| 35 | sdl2_config = std::make_unique<INIReader>(FS::PathToUTF8String(sdl2_config_loc)); | ||
| 36 | 35 | ||
| 36 | Config::Config(std::optional<std::filesystem::path> config_path) | ||
| 37 | : sdl2_config_loc{config_path.value_or(default_config_path)}, | ||
| 38 | sdl2_config{std::make_unique<INIReader>(FS::PathToUTF8String(sdl2_config_loc))} { | ||
| 37 | Reload(); | 39 | Reload(); |
| 38 | } | 40 | } |
| 39 | 41 | ||
| @@ -66,6 +68,11 @@ static const std::array<int, Settings::NativeButton::NumButtons> default_buttons | |||
| 66 | SDL_SCANCODE_M, SDL_SCANCODE_N, SDL_SCANCODE_1, SDL_SCANCODE_2, SDL_SCANCODE_B, | 68 | SDL_SCANCODE_M, SDL_SCANCODE_N, SDL_SCANCODE_1, SDL_SCANCODE_2, SDL_SCANCODE_B, |
| 67 | }; | 69 | }; |
| 68 | 70 | ||
| 71 | static const std::array<int, Settings::NativeMotion::NumMotions> default_motions = { | ||
| 72 | SDL_SCANCODE_7, | ||
| 73 | SDL_SCANCODE_8, | ||
| 74 | }; | ||
| 75 | |||
| 69 | static const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> default_analogs{{ | 76 | static const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> default_analogs{{ |
| 70 | { | 77 | { |
| 71 | SDL_SCANCODE_UP, | 78 | SDL_SCANCODE_UP, |
| @@ -102,27 +109,42 @@ void Config::ReadSetting(const std::string& group, Settings::BasicSetting<Type>& | |||
| 102 | void Config::ReadValues() { | 109 | void Config::ReadValues() { |
| 103 | // Controls | 110 | // Controls |
| 104 | for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) { | 111 | for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) { |
| 112 | auto& player = Settings::values.players.GetValue()[p]; | ||
| 113 | |||
| 105 | const auto group = fmt::format("ControlsP{}", p); | 114 | const auto group = fmt::format("ControlsP{}", p); |
| 106 | for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { | 115 | for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { |
| 107 | std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); | 116 | std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); |
| 108 | Settings::values.players.GetValue()[p].buttons[i] = | 117 | player.buttons[i] = |
| 109 | sdl2_config->Get(group, Settings::NativeButton::mapping[i], default_param); | 118 | sdl2_config->Get(group, Settings::NativeButton::mapping[i], default_param); |
| 110 | if (Settings::values.players.GetValue()[p].buttons[i].empty()) | 119 | if (player.buttons[i].empty()) { |
| 111 | Settings::values.players.GetValue()[p].buttons[i] = default_param; | 120 | player.buttons[i] = default_param; |
| 121 | } | ||
| 112 | } | 122 | } |
| 113 | 123 | ||
| 114 | for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { | 124 | for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { |
| 115 | std::string default_param = InputCommon::GenerateAnalogParamFromKeys( | 125 | std::string default_param = InputCommon::GenerateAnalogParamFromKeys( |
| 116 | default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], | 126 | default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], |
| 117 | default_analogs[i][3], default_analogs[i][4], 0.5f); | 127 | default_analogs[i][3], default_analogs[i][4], 0.5f); |
| 118 | Settings::values.players.GetValue()[p].analogs[i] = | 128 | player.analogs[i] = |
| 119 | sdl2_config->Get(group, Settings::NativeAnalog::mapping[i], default_param); | 129 | sdl2_config->Get(group, Settings::NativeAnalog::mapping[i], default_param); |
| 120 | if (Settings::values.players.GetValue()[p].analogs[i].empty()) | 130 | if (player.analogs[i].empty()) { |
| 121 | Settings::values.players.GetValue()[p].analogs[i] = default_param; | 131 | player.analogs[i] = default_param; |
| 132 | } | ||
| 133 | } | ||
| 134 | |||
| 135 | for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) { | ||
| 136 | const std::string default_param = | ||
| 137 | InputCommon::GenerateKeyboardParam(default_motions[i]); | ||
| 138 | auto& player_motions = player.motions[i]; | ||
| 139 | |||
| 140 | player_motions = | ||
| 141 | sdl2_config->Get(group, Settings::NativeMotion::mapping[i], default_param); | ||
| 142 | if (player_motions.empty()) { | ||
| 143 | player_motions = default_param; | ||
| 144 | } | ||
| 122 | } | 145 | } |
| 123 | 146 | ||
| 124 | Settings::values.players.GetValue()[p].connected = | 147 | player.connected = sdl2_config->GetBoolean(group, "connected", false); |
| 125 | sdl2_config->GetBoolean(group, "connected", false); | ||
| 126 | } | 148 | } |
| 127 | 149 | ||
| 128 | ReadSetting("ControlsGeneral", Settings::values.mouse_enabled); | 150 | ReadSetting("ControlsGeneral", Settings::values.mouse_enabled); |
| @@ -246,6 +268,7 @@ void Config::ReadValues() { | |||
| 246 | 268 | ||
| 247 | // Core | 269 | // Core |
| 248 | ReadSetting("Core", Settings::values.use_multi_core); | 270 | ReadSetting("Core", Settings::values.use_multi_core); |
| 271 | ReadSetting("Core", Settings::values.use_extended_memory_layout); | ||
| 249 | 272 | ||
| 250 | // Cpu | 273 | // Cpu |
| 251 | ReadSetting("Cpu", Settings::values.cpu_accuracy); | 274 | ReadSetting("Cpu", Settings::values.cpu_accuracy); |
| @@ -259,11 +282,14 @@ void Config::ReadValues() { | |||
| 259 | ReadSetting("Cpu", Settings::values.cpuopt_misc_ir); | 282 | ReadSetting("Cpu", Settings::values.cpuopt_misc_ir); |
| 260 | ReadSetting("Cpu", Settings::values.cpuopt_reduce_misalign_checks); | 283 | ReadSetting("Cpu", Settings::values.cpuopt_reduce_misalign_checks); |
| 261 | ReadSetting("Cpu", Settings::values.cpuopt_fastmem); | 284 | ReadSetting("Cpu", Settings::values.cpuopt_fastmem); |
| 285 | ReadSetting("Cpu", Settings::values.cpuopt_fastmem_exclusives); | ||
| 286 | ReadSetting("Cpu", Settings::values.cpuopt_recompile_exclusives); | ||
| 262 | ReadSetting("Cpu", Settings::values.cpuopt_unsafe_unfuse_fma); | 287 | ReadSetting("Cpu", Settings::values.cpuopt_unsafe_unfuse_fma); |
| 263 | ReadSetting("Cpu", Settings::values.cpuopt_unsafe_reduce_fp_error); | 288 | ReadSetting("Cpu", Settings::values.cpuopt_unsafe_reduce_fp_error); |
| 264 | ReadSetting("Cpu", Settings::values.cpuopt_unsafe_ignore_standard_fpcr); | 289 | ReadSetting("Cpu", Settings::values.cpuopt_unsafe_ignore_standard_fpcr); |
| 265 | ReadSetting("Cpu", Settings::values.cpuopt_unsafe_inaccurate_nan); | 290 | ReadSetting("Cpu", Settings::values.cpuopt_unsafe_inaccurate_nan); |
| 266 | ReadSetting("Cpu", Settings::values.cpuopt_unsafe_fastmem_check); | 291 | ReadSetting("Cpu", Settings::values.cpuopt_unsafe_fastmem_check); |
| 292 | ReadSetting("Cpu", Settings::values.cpuopt_unsafe_ignore_global_monitor); | ||
| 267 | 293 | ||
| 268 | // Renderer | 294 | // Renderer |
| 269 | ReadSetting("Renderer", Settings::values.renderer_backend); | 295 | ReadSetting("Renderer", Settings::values.renderer_backend); |
diff --git a/src/yuzu_cmd/config.h b/src/yuzu_cmd/config.h index 1ee932be2..f61ba23ec 100644 --- a/src/yuzu_cmd/config.h +++ b/src/yuzu_cmd/config.h | |||
| @@ -6,6 +6,7 @@ | |||
| 6 | 6 | ||
| 7 | #include <filesystem> | 7 | #include <filesystem> |
| 8 | #include <memory> | 8 | #include <memory> |
| 9 | #include <optional> | ||
| 9 | #include <string> | 10 | #include <string> |
| 10 | 11 | ||
| 11 | #include "common/settings.h" | 12 | #include "common/settings.h" |
| @@ -13,14 +14,14 @@ | |||
| 13 | class INIReader; | 14 | class INIReader; |
| 14 | 15 | ||
| 15 | class Config { | 16 | class Config { |
| 16 | std::unique_ptr<INIReader> sdl2_config; | ||
| 17 | std::filesystem::path sdl2_config_loc; | 17 | std::filesystem::path sdl2_config_loc; |
| 18 | std::unique_ptr<INIReader> sdl2_config; | ||
| 18 | 19 | ||
| 19 | bool LoadINI(const std::string& default_contents = "", bool retry = true); | 20 | bool LoadINI(const std::string& default_contents = "", bool retry = true); |
| 20 | void ReadValues(); | 21 | void ReadValues(); |
| 21 | 22 | ||
| 22 | public: | 23 | public: |
| 23 | Config(); | 24 | explicit Config(std::optional<std::filesystem::path> config_path); |
| 24 | ~Config(); | 25 | ~Config(); |
| 25 | 26 | ||
| 26 | void Reload(); | 27 | void Reload(); |
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h index 6d613bf7a..34782c378 100644 --- a/src/yuzu_cmd/default_ini.h +++ b/src/yuzu_cmd/default_ini.h | |||
| @@ -124,7 +124,11 @@ keyboard_enabled = | |||
| 124 | [Core] | 124 | [Core] |
| 125 | # Whether to use multi-core for CPU emulation | 125 | # Whether to use multi-core for CPU emulation |
| 126 | # 0: Disabled, 1 (default): Enabled | 126 | # 0: Disabled, 1 (default): Enabled |
| 127 | use_multi_core= | 127 | use_multi_core = |
| 128 | |||
| 129 | # Enable extended guest system memory layout (6GB DRAM) | ||
| 130 | # 0 (default): Disabled, 1: Enabled | ||
| 131 | use_extended_memory_layout = | ||
| 128 | 132 | ||
| 129 | [Cpu] | 133 | [Cpu] |
| 130 | # Adjusts various optimizations. | 134 | # Adjusts various optimizations. |
| @@ -174,6 +178,14 @@ cpuopt_reduce_misalign_checks = | |||
| 174 | # 0: Disabled, 1 (default): Enabled | 178 | # 0: Disabled, 1 (default): Enabled |
| 175 | cpuopt_fastmem = | 179 | cpuopt_fastmem = |
| 176 | 180 | ||
| 181 | # Enable Host MMU Emulation for exclusive memory instructions (faster guest memory access) | ||
| 182 | # 0: Disabled, 1 (default): Enabled | ||
| 183 | cpuopt_fastmem_exclusives = | ||
| 184 | |||
| 185 | # Enable fallback on failure of fastmem of exclusive memory instructions (faster guest memory access) | ||
| 186 | # 0: Disabled, 1 (default): Enabled | ||
| 187 | cpuopt_recompile_exclusives = | ||
| 188 | |||
| 177 | # Enable unfuse FMA (improve performance on CPUs without FMA) | 189 | # Enable unfuse FMA (improve performance on CPUs without FMA) |
| 178 | # Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select. | 190 | # Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select. |
| 179 | # 0: Disabled, 1 (default): Enabled | 191 | # 0: Disabled, 1 (default): Enabled |
| @@ -199,6 +211,11 @@ cpuopt_unsafe_inaccurate_nan = | |||
| 199 | # 0: Disabled, 1 (default): Enabled | 211 | # 0: Disabled, 1 (default): Enabled |
| 200 | cpuopt_unsafe_fastmem_check = | 212 | cpuopt_unsafe_fastmem_check = |
| 201 | 213 | ||
| 214 | # Enable faster exclusive instructions | ||
| 215 | # Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select. | ||
| 216 | # 0: Disabled, 1 (default): Enabled | ||
| 217 | cpuopt_unsafe_ignore_global_monitor = | ||
| 218 | |||
| 202 | [Renderer] | 219 | [Renderer] |
| 203 | # Which backend API to use. | 220 | # Which backend API to use. |
| 204 | # 0 (default): OpenGL, 1: Vulkan | 221 | # 0 (default): OpenGL, 1: Vulkan |
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp index b44ea0cc4..f6d563017 100644 --- a/src/yuzu_cmd/yuzu.cpp +++ b/src/yuzu_cmd/yuzu.cpp | |||
| @@ -66,7 +66,8 @@ static void PrintHelp(const char* argv0) { | |||
| 66 | "-f, --fullscreen Start in fullscreen mode\n" | 66 | "-f, --fullscreen Start in fullscreen mode\n" |
| 67 | "-h, --help Display this help and exit\n" | 67 | "-h, --help Display this help and exit\n" |
| 68 | "-v, --version Output version information and exit\n" | 68 | "-v, --version Output version information and exit\n" |
| 69 | "-p, --program Pass following string as arguments to executable\n"; | 69 | "-p, --program Pass following string as arguments to executable\n" |
| 70 | "-c, --config Load the specified configuration file\n"; | ||
| 70 | } | 71 | } |
| 71 | 72 | ||
| 72 | static void PrintVersion() { | 73 | static void PrintVersion() { |
| @@ -78,7 +79,6 @@ int main(int argc, char** argv) { | |||
| 78 | Common::Log::Initialize(); | 79 | Common::Log::Initialize(); |
| 79 | Common::Log::SetColorConsoleBackendEnabled(true); | 80 | Common::Log::SetColorConsoleBackendEnabled(true); |
| 80 | Common::DetachedTasks detached_tasks; | 81 | Common::DetachedTasks detached_tasks; |
| 81 | Config config; | ||
| 82 | 82 | ||
| 83 | int option_index = 0; | 83 | int option_index = 0; |
| 84 | #ifdef _WIN32 | 84 | #ifdef _WIN32 |
| @@ -91,19 +91,24 @@ int main(int argc, char** argv) { | |||
| 91 | } | 91 | } |
| 92 | #endif | 92 | #endif |
| 93 | std::string filepath; | 93 | std::string filepath; |
| 94 | std::optional<std::string> config_path; | ||
| 95 | std::string program_args; | ||
| 94 | 96 | ||
| 95 | bool fullscreen = false; | 97 | bool fullscreen = false; |
| 96 | 98 | ||
| 97 | static struct option long_options[] = { | 99 | static struct option long_options[] = { |
| 100 | // clang-format off | ||
| 98 | {"fullscreen", no_argument, 0, 'f'}, | 101 | {"fullscreen", no_argument, 0, 'f'}, |
| 99 | {"help", no_argument, 0, 'h'}, | 102 | {"help", no_argument, 0, 'h'}, |
| 100 | {"version", no_argument, 0, 'v'}, | 103 | {"version", no_argument, 0, 'v'}, |
| 101 | {"program", optional_argument, 0, 'p'}, | 104 | {"program", optional_argument, 0, 'p'}, |
| 105 | {"config", required_argument, 0, 'c'}, | ||
| 102 | {0, 0, 0, 0}, | 106 | {0, 0, 0, 0}, |
| 107 | // clang-format on | ||
| 103 | }; | 108 | }; |
| 104 | 109 | ||
| 105 | while (optind < argc) { | 110 | while (optind < argc) { |
| 106 | int arg = getopt_long(argc, argv, "g:fhvp::", long_options, &option_index); | 111 | int arg = getopt_long(argc, argv, "g:fhvp::c:", long_options, &option_index); |
| 107 | if (arg != -1) { | 112 | if (arg != -1) { |
| 108 | switch (static_cast<char>(arg)) { | 113 | switch (static_cast<char>(arg)) { |
| 109 | case 'f': | 114 | case 'f': |
| @@ -117,9 +122,12 @@ int main(int argc, char** argv) { | |||
| 117 | PrintVersion(); | 122 | PrintVersion(); |
| 118 | return 0; | 123 | return 0; |
| 119 | case 'p': | 124 | case 'p': |
| 120 | Settings::values.program_args = argv[optind]; | 125 | program_args = argv[optind]; |
| 121 | ++optind; | 126 | ++optind; |
| 122 | break; | 127 | break; |
| 128 | case 'c': | ||
| 129 | config_path = optarg; | ||
| 130 | break; | ||
| 123 | } | 131 | } |
| 124 | } else { | 132 | } else { |
| 125 | #ifdef _WIN32 | 133 | #ifdef _WIN32 |
| @@ -131,6 +139,12 @@ int main(int argc, char** argv) { | |||
| 131 | } | 139 | } |
| 132 | } | 140 | } |
| 133 | 141 | ||
| 142 | Config config{config_path}; | ||
| 143 | |||
| 144 | if (!program_args.empty()) { | ||
| 145 | Settings::values.program_args = program_args; | ||
| 146 | } | ||
| 147 | |||
| 134 | #ifdef _WIN32 | 148 | #ifdef _WIN32 |
| 135 | LocalFree(argv_w); | 149 | LocalFree(argv_w); |
| 136 | #endif | 150 | #endif |